diff --git a/discord.net b/discord.net index e8550fa4..b51408de 160000 --- a/discord.net +++ b/discord.net @@ -1 +1 @@ -Subproject commit e8550fa462f86e0338925547dfe99242bfc1fdcc +Subproject commit b51408def863ee5f4273478efa65eb50e4008487 diff --git a/src/NadekoBot/Attributes/LocalizedAlias.cs b/src/NadekoBot/Attributes/LocalizedAlias.cs new file mode 100644 index 00000000..97f808aa --- /dev/null +++ b/src/NadekoBot/Attributes/LocalizedAlias.cs @@ -0,0 +1,18 @@ +using Discord.Commands; +using NadekoBot.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Attributes +{ + public class LocalizedAliasAttribute : AliasAttribute + { + public LocalizedAliasAttribute([CallerMemberName] string memberName = "") : base(Localization.LoadCommandString(memberName.ToLowerInvariant() + "_text").Split(' ').Skip(1).ToArray()) + { + } + } +} diff --git a/src/NadekoBot/Attributes/LocalizedCommand.cs b/src/NadekoBot/Attributes/LocalizedCommand.cs index fb371a6e..dcb96537 100644 --- a/src/NadekoBot/Attributes/LocalizedCommand.cs +++ b/src/NadekoBot/Attributes/LocalizedCommand.cs @@ -1,12 +1,13 @@ using Discord.Commands; using NadekoBot.Services; +using System.Linq; using System.Runtime.CompilerServices; namespace NadekoBot.Attributes { public class LocalizedCommandAttribute : CommandAttribute { - public LocalizedCommandAttribute([CallerMemberName] string memberName="") : base(Localization.LoadCommandString(memberName.ToLowerInvariant() + "_text")) + public LocalizedCommandAttribute([CallerMemberName] string memberName="") : base(Localization.LoadCommandString(memberName.ToLowerInvariant() + "_text").Split(' ')[0]) { } diff --git a/src/NadekoBot/Attributes/NadekoModule.cs b/src/NadekoBot/Attributes/NadekoModule.cs new file mode 100644 index 00000000..68f7db5f --- /dev/null +++ b/src/NadekoBot/Attributes/NadekoModule.cs @@ -0,0 +1,52 @@ +using Discord.Commands; +using NadekoBot.Services; +using NadekoBot.Services.Database; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Attributes +{ + [System.AttributeUsage(AttributeTargets.Class)] + sealed class NadekoModuleAttribute : ModuleAttribute + { + //modulename / prefix + private static Dictionary modulePrefixes = null; + public static Dictionary ModulePrefixes { + get { + if (modulePrefixes != null) + return modulePrefixes; + + using (var uow = DbHandler.UnitOfWork()) + { + return (modulePrefixes = uow.BotConfig + .GetOrCreate() + .ModulePrefixes + .ToDictionary(p => p.ModuleName, p => p.Prefix)); + } + } + } + + public NadekoModuleAttribute(string moduleName, string defaultPrefix) : base(GetModulePrefix(moduleName) ?? defaultPrefix) + { + AppendSpace = false; + } + + private static string GetModulePrefix(string moduleName) + { + string prefix; + if (ModulePrefixes.TryGetValue(moduleName, out prefix)) + { + Console.WriteLine("Cache hit"); + return prefix; + } + + Console.WriteLine("Cache not hit for " + moduleName); + return null; + } + } +} + diff --git a/src/NadekoBot/Migrations/20160828000228_first.Designer.cs b/src/NadekoBot/Migrations/20160910180231_first.Designer.cs similarity index 59% rename from src/NadekoBot/Migrations/20160828000228_first.Designer.cs rename to src/NadekoBot/Migrations/20160910180231_first.Designer.cs index 43f73276..1097f981 100644 --- a/src/NadekoBot/Migrations/20160828000228_first.Designer.cs +++ b/src/NadekoBot/Migrations/20160910180231_first.Designer.cs @@ -8,7 +8,7 @@ using NadekoBot.Services.Database.Impl; namespace NadekoBot.Migrations { [DbContext(typeof(NadekoSqliteContext))] - [Migration("20160828000228_first")] + [Migration("20160910180231_first")] partial class first { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -39,6 +39,10 @@ namespace NadekoBot.Migrations b.Property("BufferSize"); + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + b.Property("CurrencyName"); b.Property("CurrencyPluralName"); @@ -104,6 +108,39 @@ namespace NadekoBot.Migrations b.ToTable("ClashOfClans"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("InternalTrigger"); + + b.Property("Modifier"); + + b.Property("UnitType"); + + b.HasKey("Id"); + + b.ToTable("ConversionUnits"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => { b.Property("Id") @@ -136,7 +173,31 @@ namespace NadekoBot.Migrations b.HasIndex("BotConfigId"); - b.ToTable("EightBallResponse"); + b.ToTable("EightBallResponses"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("LastStatus"); + + b.Property("Type"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FollowedStream"); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => @@ -160,36 +221,122 @@ namespace NadekoBot.Migrations b.Property("ChannelGreetMessageText"); + b.Property("DefaultMusicVolume"); + b.Property("DeleteMessageOnCommand"); b.Property("DmGreetMessageText"); b.Property("ExclusiveSelfAssignedRoles"); + b.Property("GenerateCurrencyChannelId"); + b.Property("GreetMessageChannelId"); b.Property("GuildId"); + b.Property("LogSettingId"); + b.Property("SendChannelByeMessage"); b.Property("SendChannelGreetMessage"); b.Property("SendDmGreetMessage"); + b.Property("VoicePlusTextEnabled"); + b.HasKey("Id"); b.HasIndex("GuildId") .IsUnique(); + b.HasIndex("LogSettingId"); + b.ToTable("GuildConfigs"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredLogChannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredVoicePresenceCHannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelCreated"); + + b.Property("ChannelDestroyed"); + + b.Property("ChannelId"); + + b.Property("ChannelUpdated"); + + b.Property("IsLogging"); + + b.Property("LogUserPresence"); + + b.Property("LogVoicePresence"); + + b.Property("MessageDeleted"); + + b.Property("MessageReceived"); + + b.Property("MessageUpdated"); + + b.Property("UserBanned"); + + b.Property("UserJoined"); + + b.Property("UserLeft"); + + b.Property("UserPresenceChannelId"); + + b.Property("UserUnbanned"); + + b.Property("UserUpdated"); + + b.Property("VoicePresenceChannelId"); + + b.HasKey("Id"); + + b.ToTable("LogSettings"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => { b.Property("Id") .ValueGeneratedOnAdd(); - b.Property("BotConfigId"); + b.Property("BotConfigId"); b.Property("ModuleName"); @@ -199,7 +346,7 @@ namespace NadekoBot.Migrations b.HasIndex("BotConfigId"); - b.ToTable("ModulePrefix"); + b.ToTable("ModulePrefixes"); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => @@ -256,7 +403,7 @@ namespace NadekoBot.Migrations b.HasIndex("BotConfigId"); - b.ToTable("RaceAnimal"); + b.ToTable("RaceAnimals"); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => @@ -281,6 +428,27 @@ namespace NadekoBot.Migrations b.ToTable("Reminders"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId") + .IsUnique(); + + b.ToTable("Repeaters"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => { b.Property("Id") @@ -298,6 +466,20 @@ namespace NadekoBot.Migrations b.ToTable("SelfAssignableRoles"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.TypingArticle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author"); + + b.Property("Text"); + + b.HasKey("Id"); + + b.ToTable("TypingArticles"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => { b.HasOne("NadekoBot.Services.Database.Models.BotConfig") @@ -320,11 +502,40 @@ namespace NadekoBot.Migrations .HasForeignKey("BotConfigId"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FollowedStreams") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany() + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredVoicePresenceChannelIds") + .HasForeignKey("LogSettingId"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => { - b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + b.HasOne("NadekoBot.Services.Database.Models.BotConfig", "BotConfig") .WithMany("ModulePrefixes") - .HasForeignKey("BotConfigId"); + .HasForeignKey("BotConfigId") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => diff --git a/src/NadekoBot/Migrations/20160828000228_first.cs b/src/NadekoBot/Migrations/20160910180231_first.cs similarity index 56% rename from src/NadekoBot/Migrations/20160828000228_first.cs rename to src/NadekoBot/Migrations/20160910180231_first.cs index 081d2cb9..c24604f5 100644 --- a/src/NadekoBot/Migrations/20160828000228_first.cs +++ b/src/NadekoBot/Migrations/20160910180231_first.cs @@ -15,6 +15,8 @@ namespace NadekoBot.Migrations Id = table.Column(nullable: false) .Annotation("Autoincrement", true), BufferSize = table.Column(nullable: false), + CurrencyGenerationChance = table.Column(nullable: false), + CurrencyGenerationCooldown = table.Column(nullable: false), CurrencyName = table.Column(nullable: true), CurrencyPluralName = table.Column(nullable: true), CurrencySign = table.Column(nullable: true), @@ -47,6 +49,35 @@ namespace NadekoBot.Migrations table.PrimaryKey("PK_ClashOfClans", x => x.Id); }); + migrationBuilder.CreateTable( + name: "ConversionUnits", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + InternalTrigger = table.Column(nullable: true), + Modifier = table.Column(nullable: false), + UnitType = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ConversionUnits", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Currency", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + Amount = table.Column(nullable: false), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Currency", x => x.Id); + }); + migrationBuilder.CreateTable( name: "Donators", columns: table => new @@ -63,31 +94,32 @@ namespace NadekoBot.Migrations }); migrationBuilder.CreateTable( - name: "GuildConfigs", + name: "LogSettings", columns: table => new { Id = table.Column(nullable: false) .Annotation("Autoincrement", true), - AutoAssignRoleId = table.Column(nullable: false), - AutoDeleteByeMessages = table.Column(nullable: false), - AutoDeleteGreetMessages = table.Column(nullable: false), - AutoDeleteGreetMessagesTimer = table.Column(nullable: false), - AutoDeleteSelfAssignedRoleMessages = table.Column(nullable: false), - ByeMessageChannelId = table.Column(nullable: false), - ChannelByeMessageText = table.Column(nullable: true), - ChannelGreetMessageText = table.Column(nullable: true), - DeleteMessageOnCommand = table.Column(nullable: false), - DmGreetMessageText = table.Column(nullable: true), - ExclusiveSelfAssignedRoles = table.Column(nullable: false), - GreetMessageChannelId = table.Column(nullable: false), - GuildId = table.Column(nullable: false), - SendChannelByeMessage = table.Column(nullable: false), - SendChannelGreetMessage = table.Column(nullable: false), - SendDmGreetMessage = table.Column(nullable: false) + ChannelCreated = table.Column(nullable: false), + ChannelDestroyed = table.Column(nullable: false), + ChannelId = table.Column(nullable: false), + ChannelUpdated = table.Column(nullable: false), + IsLogging = table.Column(nullable: false), + LogUserPresence = table.Column(nullable: false), + LogVoicePresence = table.Column(nullable: false), + MessageDeleted = table.Column(nullable: false), + MessageReceived = table.Column(nullable: false), + MessageUpdated = table.Column(nullable: false), + UserBanned = table.Column(nullable: false), + UserJoined = table.Column(nullable: false), + UserLeft = table.Column(nullable: false), + UserPresenceChannelId = table.Column(nullable: false), + UserUnbanned = table.Column(nullable: false), + UserUpdated = table.Column(nullable: false), + VoicePresenceChannelId = table.Column(nullable: false) }, constraints: table => { - table.PrimaryKey("PK_GuildConfigs", x => x.Id); + table.PrimaryKey("PK_LogSettings", x => x.Id); }); migrationBuilder.CreateTable( @@ -125,6 +157,22 @@ namespace NadekoBot.Migrations table.PrimaryKey("PK_Reminders", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Repeaters", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + ChannelId = table.Column(nullable: false), + GuildId = table.Column(nullable: false), + Interval = table.Column(nullable: false), + Message = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Repeaters", x => x.Id); + }); + migrationBuilder.CreateTable( name: "SelfAssignableRoles", columns: table => new @@ -139,6 +187,20 @@ namespace NadekoBot.Migrations table.PrimaryKey("PK_SelfAssignableRoles", x => x.Id); }); + migrationBuilder.CreateTable( + name: "TypingArticles", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + Author = table.Column(nullable: true), + Text = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TypingArticles", x => x.Id); + }); + migrationBuilder.CreateTable( name: "BlacklistItem", columns: table => new @@ -160,7 +222,7 @@ namespace NadekoBot.Migrations }); migrationBuilder.CreateTable( - name: "EightBallResponse", + name: "EightBallResponses", columns: table => new { Id = table.Column(nullable: false) @@ -170,9 +232,9 @@ namespace NadekoBot.Migrations }, constraints: table => { - table.PrimaryKey("PK_EightBallResponse", x => x.Id); + table.PrimaryKey("PK_EightBallResponses", x => x.Id); table.ForeignKey( - name: "FK_EightBallResponse_BotConfig_BotConfigId", + name: "FK_EightBallResponses_BotConfig_BotConfigId", column: x => x.BotConfigId, principalTable: "BotConfig", principalColumn: "Id", @@ -180,24 +242,24 @@ namespace NadekoBot.Migrations }); migrationBuilder.CreateTable( - name: "ModulePrefix", + name: "ModulePrefixes", columns: table => new { Id = table.Column(nullable: false) .Annotation("Autoincrement", true), - BotConfigId = table.Column(nullable: true), + BotConfigId = table.Column(nullable: false), ModuleName = table.Column(nullable: true), Prefix = table.Column(nullable: true) }, constraints: table => { - table.PrimaryKey("PK_ModulePrefix", x => x.Id); + table.PrimaryKey("PK_ModulePrefixes", x => x.Id); table.ForeignKey( - name: "FK_ModulePrefix_BotConfig_BotConfigId", + name: "FK_ModulePrefixes_BotConfig_BotConfigId", column: x => x.BotConfigId, principalTable: "BotConfig", principalColumn: "Id", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( @@ -221,7 +283,7 @@ namespace NadekoBot.Migrations }); migrationBuilder.CreateTable( - name: "RaceAnimal", + name: "RaceAnimals", columns: table => new { Id = table.Column(nullable: false) @@ -232,9 +294,9 @@ namespace NadekoBot.Migrations }, constraints: table => { - table.PrimaryKey("PK_RaceAnimal", x => x.Id); + table.PrimaryKey("PK_RaceAnimals", x => x.Id); table.ForeignKey( - name: "FK_RaceAnimal_BotConfig_BotConfigId", + name: "FK_RaceAnimals_BotConfig_BotConfigId", column: x => x.BotConfigId, principalTable: "BotConfig", principalColumn: "Id", @@ -264,6 +326,108 @@ namespace NadekoBot.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "GuildConfigs", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + AutoAssignRoleId = table.Column(nullable: false), + AutoDeleteByeMessages = table.Column(nullable: false), + AutoDeleteGreetMessages = table.Column(nullable: false), + AutoDeleteGreetMessagesTimer = table.Column(nullable: false), + AutoDeleteSelfAssignedRoleMessages = table.Column(nullable: false), + ByeMessageChannelId = table.Column(nullable: false), + ChannelByeMessageText = table.Column(nullable: true), + ChannelGreetMessageText = table.Column(nullable: true), + DefaultMusicVolume = table.Column(nullable: false), + DeleteMessageOnCommand = table.Column(nullable: false), + DmGreetMessageText = table.Column(nullable: true), + ExclusiveSelfAssignedRoles = table.Column(nullable: false), + GenerateCurrencyChannelId = table.Column(nullable: true), + GreetMessageChannelId = table.Column(nullable: false), + GuildId = table.Column(nullable: false), + LogSettingId = table.Column(nullable: true), + SendChannelByeMessage = table.Column(nullable: false), + SendChannelGreetMessage = table.Column(nullable: false), + SendDmGreetMessage = table.Column(nullable: false), + VoicePlusTextEnabled = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GuildConfigs", x => x.Id); + table.ForeignKey( + name: "FK_GuildConfigs_LogSettings_LogSettingId", + column: x => x.LogSettingId, + principalTable: "LogSettings", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "IgnoredLogChannels", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + ChannelId = table.Column(nullable: false), + LogSettingId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_IgnoredLogChannels", x => x.Id); + table.ForeignKey( + name: "FK_IgnoredLogChannels_LogSettings_LogSettingId", + column: x => x.LogSettingId, + principalTable: "LogSettings", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "IgnoredVoicePresenceCHannels", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + ChannelId = table.Column(nullable: false), + LogSettingId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_IgnoredVoicePresenceCHannels", x => x.Id); + table.ForeignKey( + name: "FK_IgnoredVoicePresenceCHannels_LogSettings_LogSettingId", + column: x => x.LogSettingId, + principalTable: "LogSettings", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "FollowedStream", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Autoincrement", true), + ChannelId = table.Column(nullable: false), + GuildConfigId = table.Column(nullable: true), + GuildId = table.Column(nullable: false), + LastStatus = table.Column(nullable: false), + Type = table.Column(nullable: false), + Username = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_FollowedStream", x => x.Id); + table.ForeignKey( + name: "FK_FollowedStream_GuildConfigs_GuildConfigId", + column: x => x.GuildConfigId, + principalTable: "GuildConfigs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + migrationBuilder.CreateIndex( name: "IX_BlacklistItem_BotConfigId", table: "BlacklistItem", @@ -274,6 +438,12 @@ namespace NadekoBot.Migrations table: "ClashCallers", column: "ClashWarId"); + migrationBuilder.CreateIndex( + name: "IX_Currency_UserId", + table: "Currency", + column: "UserId", + unique: true); + migrationBuilder.CreateIndex( name: "IX_Donators_UserId", table: "Donators", @@ -281,10 +451,15 @@ namespace NadekoBot.Migrations unique: true); migrationBuilder.CreateIndex( - name: "IX_EightBallResponse_BotConfigId", - table: "EightBallResponse", + name: "IX_EightBallResponses_BotConfigId", + table: "EightBallResponses", column: "BotConfigId"); + migrationBuilder.CreateIndex( + name: "IX_FollowedStream_GuildConfigId", + table: "FollowedStream", + column: "GuildConfigId"); + migrationBuilder.CreateIndex( name: "IX_GuildConfigs_GuildId", table: "GuildConfigs", @@ -292,8 +467,23 @@ namespace NadekoBot.Migrations unique: true); migrationBuilder.CreateIndex( - name: "IX_ModulePrefix_BotConfigId", - table: "ModulePrefix", + name: "IX_GuildConfigs_LogSettingId", + table: "GuildConfigs", + column: "LogSettingId"); + + migrationBuilder.CreateIndex( + name: "IX_IgnoredLogChannels_LogSettingId", + table: "IgnoredLogChannels", + column: "LogSettingId"); + + migrationBuilder.CreateIndex( + name: "IX_IgnoredVoicePresenceCHannels_LogSettingId", + table: "IgnoredVoicePresenceCHannels", + column: "LogSettingId"); + + migrationBuilder.CreateIndex( + name: "IX_ModulePrefixes_BotConfigId", + table: "ModulePrefixes", column: "BotConfigId"); migrationBuilder.CreateIndex( @@ -302,10 +492,16 @@ namespace NadekoBot.Migrations column: "BotConfigId"); migrationBuilder.CreateIndex( - name: "IX_RaceAnimal_BotConfigId", - table: "RaceAnimal", + name: "IX_RaceAnimals_BotConfigId", + table: "RaceAnimals", column: "BotConfigId"); + migrationBuilder.CreateIndex( + name: "IX_Repeaters_ChannelId", + table: "Repeaters", + column: "ChannelId", + unique: true); + migrationBuilder.CreateIndex( name: "IX_SelfAssignableRoles_GuildId_RoleId", table: "SelfAssignableRoles", @@ -321,17 +517,29 @@ namespace NadekoBot.Migrations migrationBuilder.DropTable( name: "ClashCallers"); + migrationBuilder.DropTable( + name: "ConversionUnits"); + + migrationBuilder.DropTable( + name: "Currency"); + migrationBuilder.DropTable( name: "Donators"); migrationBuilder.DropTable( - name: "EightBallResponse"); + name: "EightBallResponses"); migrationBuilder.DropTable( - name: "GuildConfigs"); + name: "FollowedStream"); migrationBuilder.DropTable( - name: "ModulePrefix"); + name: "IgnoredLogChannels"); + + migrationBuilder.DropTable( + name: "IgnoredVoicePresenceCHannels"); + + migrationBuilder.DropTable( + name: "ModulePrefixes"); migrationBuilder.DropTable( name: "PlayingStatus"); @@ -340,19 +548,31 @@ namespace NadekoBot.Migrations name: "Quotes"); migrationBuilder.DropTable( - name: "RaceAnimal"); + name: "RaceAnimals"); migrationBuilder.DropTable( name: "Reminders"); + migrationBuilder.DropTable( + name: "Repeaters"); + migrationBuilder.DropTable( name: "SelfAssignableRoles"); + migrationBuilder.DropTable( + name: "TypingArticles"); + migrationBuilder.DropTable( name: "ClashOfClans"); + migrationBuilder.DropTable( + name: "GuildConfigs"); + migrationBuilder.DropTable( name: "BotConfig"); + + migrationBuilder.DropTable( + name: "LogSettings"); } } } diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index b9042c98..92d2ea09 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -38,6 +38,10 @@ namespace NadekoBot.Migrations b.Property("BufferSize"); + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + b.Property("CurrencyName"); b.Property("CurrencyPluralName"); @@ -103,6 +107,39 @@ namespace NadekoBot.Migrations b.ToTable("ClashOfClans"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("InternalTrigger"); + + b.Property("Modifier"); + + b.Property("UnitType"); + + b.HasKey("Id"); + + b.ToTable("ConversionUnits"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => { b.Property("Id") @@ -135,7 +172,31 @@ namespace NadekoBot.Migrations b.HasIndex("BotConfigId"); - b.ToTable("EightBallResponse"); + b.ToTable("EightBallResponses"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("LastStatus"); + + b.Property("Type"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FollowedStream"); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => @@ -159,36 +220,122 @@ namespace NadekoBot.Migrations b.Property("ChannelGreetMessageText"); + b.Property("DefaultMusicVolume"); + b.Property("DeleteMessageOnCommand"); b.Property("DmGreetMessageText"); b.Property("ExclusiveSelfAssignedRoles"); + b.Property("GenerateCurrencyChannelId"); + b.Property("GreetMessageChannelId"); b.Property("GuildId"); + b.Property("LogSettingId"); + b.Property("SendChannelByeMessage"); b.Property("SendChannelGreetMessage"); b.Property("SendDmGreetMessage"); + b.Property("VoicePlusTextEnabled"); + b.HasKey("Id"); b.HasIndex("GuildId") .IsUnique(); + b.HasIndex("LogSettingId"); + b.ToTable("GuildConfigs"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredLogChannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredVoicePresenceCHannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelCreated"); + + b.Property("ChannelDestroyed"); + + b.Property("ChannelId"); + + b.Property("ChannelUpdated"); + + b.Property("IsLogging"); + + b.Property("LogUserPresence"); + + b.Property("LogVoicePresence"); + + b.Property("MessageDeleted"); + + b.Property("MessageReceived"); + + b.Property("MessageUpdated"); + + b.Property("UserBanned"); + + b.Property("UserJoined"); + + b.Property("UserLeft"); + + b.Property("UserPresenceChannelId"); + + b.Property("UserUnbanned"); + + b.Property("UserUpdated"); + + b.Property("VoicePresenceChannelId"); + + b.HasKey("Id"); + + b.ToTable("LogSettings"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => { b.Property("Id") .ValueGeneratedOnAdd(); - b.Property("BotConfigId"); + b.Property("BotConfigId"); b.Property("ModuleName"); @@ -198,7 +345,7 @@ namespace NadekoBot.Migrations b.HasIndex("BotConfigId"); - b.ToTable("ModulePrefix"); + b.ToTable("ModulePrefixes"); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => @@ -255,7 +402,7 @@ namespace NadekoBot.Migrations b.HasIndex("BotConfigId"); - b.ToTable("RaceAnimal"); + b.ToTable("RaceAnimals"); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => @@ -280,6 +427,27 @@ namespace NadekoBot.Migrations b.ToTable("Reminders"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("ChannelId") + .IsUnique(); + + b.ToTable("Repeaters"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => { b.Property("Id") @@ -297,6 +465,20 @@ namespace NadekoBot.Migrations b.ToTable("SelfAssignableRoles"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.TypingArticle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author"); + + b.Property("Text"); + + b.HasKey("Id"); + + b.ToTable("TypingArticles"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => { b.HasOne("NadekoBot.Services.Database.Models.BotConfig") @@ -319,11 +501,40 @@ namespace NadekoBot.Migrations .HasForeignKey("BotConfigId"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FollowedStreams") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany() + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredVoicePresenceChannelIds") + .HasForeignKey("LogSettingId"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => { - b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + b.HasOne("NadekoBot.Services.Database.Models.BotConfig", "BotConfig") .WithMany("ModulePrefixes") - .HasForeignKey("BotConfigId"); + .HasForeignKey("BotConfigId") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index b96ec950..af0d13df 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -14,18 +14,41 @@ using Discord.WebSocket; using NadekoBot.Services.Database; using NadekoBot.Services.Database.Models; -//todo fix delmsgoncmd namespace NadekoBot.Modules.Administration { - [Module(".", AppendSpace = false)] + [NadekoModule("Administration", ".")] public partial class Administration : DiscordModule { public Administration(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) { - + NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler; } + + private async void DelMsgOnCmd_Handler(object sender, CommandExecutedEventArgs e) + { + try + { + var channel = e.Message.Channel as ITextChannel; + if (channel == null) + return; + + bool shouldDelete; + using (var uow = DbHandler.UnitOfWork()) + { + shouldDelete = uow.GuildConfigs.For(channel.Guild.Id).DeleteMessageOnCommand; + } + + if (shouldDelete) + await e.Message.DeleteAsync(); + } + catch (Exception ex) + { + _log.Warn(ex, "Delmsgoncmd errored..."); + } + } + ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Restart(IUserMessage umsg) //{ @@ -36,8 +59,8 @@ namespace NadekoBot.Modules.Administration // System.Diagnostics.Process.Start(System.Reflection.Assembly.GetEntryAssembly().Location); // Environment.Exit(0); //} - - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.Administrator)] public async Task Delmsgoncmd(IUserMessage umsg) @@ -57,7 +80,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("❗`Stopped automatic deletion of successfull command invokations.`"); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task Setrole(IUserMessage umsg, IGuildUser usr, [Remainder] IRole role) @@ -75,7 +98,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task Removerole(IUserMessage umsg, IGuildUser usr, [Remainder] IRole role) @@ -92,7 +115,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task RenameRole(IUserMessage umsg, IRole roleToEdit, string newname) @@ -114,7 +137,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task RemoveAllRoles(IUserMessage umsg, [Remainder] IGuildUser user) @@ -132,7 +155,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task CreateRole(IUserMessage umsg, [Remainder] string roleName = null) @@ -153,7 +176,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task RoleColor(IUserMessage umsg, params string[] args) @@ -191,7 +214,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.BanMembers)] public async Task Ban(IUserMessage umsg, IGuildUser user) @@ -218,7 +241,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.BanMembers)] public async Task Softban(IUserMessage umsg, IGuildUser user, [Remainder] string msg = null) @@ -244,7 +267,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Kick(IUserMessage umsg, IGuildUser user, [Remainder] string msg = null) { @@ -272,7 +295,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.MuteMembers)] public async Task Mute(IUserMessage umsg, params IGuildUser[] users) @@ -295,7 +318,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.MuteMembers)] public async Task Unmute(IUserMessage umsg, params IGuildUser[] users) @@ -318,7 +341,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.DeafenMembers)] public async Task Deafen(IUserMessage umsg, params IGuildUser[] users) @@ -341,7 +364,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.DeafenMembers)] public async Task UnDeafen(IUserMessage umsg, params IGuildUser[] users) @@ -364,7 +387,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageChannels)] public async Task DelVoiChanl(IUserMessage umsg, [Remainder] IVoiceChannel voiceChannel) @@ -373,18 +396,17 @@ namespace NadekoBot.Modules.Administration await umsg.Channel.SendMessageAsync($"Removed channel **{voiceChannel.Name}**.").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageChannels)] public async Task CreatVoiChanl(IUserMessage umsg, [Remainder] string channelName) { var channel = (ITextChannel)umsg.Channel; - //todo actually print info about created channel var ch = await channel.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false); await channel.SendMessageAsync($"Created voice channel **{ch.Name}**, id `{ch.Id}`.").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageChannels)] public async Task DelTxtChanl(IUserMessage umsg, [Remainder] ITextChannel channel) @@ -393,18 +415,17 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync($"Removed text channel **{channel.Name}**, id `{channel.Id}`.").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageChannels)] public async Task CreaTxtChanl(IUserMessage umsg, [Remainder] string channelName) { var channel = (ITextChannel)umsg.Channel; - //todo actually print info about created channel var txtCh = await channel.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false); await channel.SendMessageAsync($"Added text channel **{txtCh.Name}**, id `{txtCh.Id}`.").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageChannels)] public async Task SetTopic(IUserMessage umsg, [Remainder] string topic = null) @@ -415,7 +436,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync(":ok: **New channel topic set.**").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageChannels)] public async Task SetChanlName(IUserMessage umsg, [Remainder] string name) @@ -428,7 +449,7 @@ namespace NadekoBot.Modules.Administration //delets her own messages, no perm required - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Prune(IUserMessage umsg) { @@ -441,7 +462,7 @@ namespace NadekoBot.Modules.Administration } // prune x - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(ChannelPermission.ManageMessages)] public async Task Prune(IUserMessage msg, int count) @@ -460,7 +481,7 @@ namespace NadekoBot.Modules.Administration } //prune @user [x] - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Prune(IUserMessage msg, IGuildUser user, int count = 100) { @@ -470,7 +491,7 @@ namespace NadekoBot.Modules.Administration await msg.Channel.DeleteMessagesAsync(enumerable); } ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Die(IUserMessage umsg) //{ @@ -482,7 +503,7 @@ namespace NadekoBot.Modules.Administration //} ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Setname(IUserMessage umsg, [Remainder] string newName = null) //{ @@ -491,7 +512,7 @@ namespace NadekoBot.Modules.Administration //} ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task NewAvatar(IUserMessage umsg, [Remainder] string img = null) //{ @@ -510,7 +531,7 @@ namespace NadekoBot.Modules.Administration //} ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task SetGame(IUserMessage umsg, [Remainder] string game = null) //{ @@ -522,7 +543,7 @@ namespace NadekoBot.Modules.Administration //} ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Send(IUserMessage umsg, string where, [Remainder] string msg = null) //{ @@ -567,7 +588,7 @@ namespace NadekoBot.Modules.Administration //} ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Announce(IUserMessage umsg, [Remainder] string message) //{ @@ -582,7 +603,7 @@ namespace NadekoBot.Modules.Administration //} ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task SaveChat(IUserMessage umsg, int cnt) //{ @@ -614,7 +635,7 @@ namespace NadekoBot.Modules.Administration //} - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.MentionEveryone)] public async Task MentionRole(IUserMessage umsg, params IRole[] roles) @@ -639,7 +660,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync(send).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Donators(IUserMessage umsg) { @@ -655,7 +676,7 @@ namespace NadekoBot.Modules.Administration } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Donadd(IUserMessage umsg, IUser donator, int amount) { diff --git a/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs b/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs index ff2342cd..1cde1716 100644 --- a/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs @@ -19,26 +19,30 @@ namespace NadekoBot.Modules.Administration public AutoAssignRoleCommands() { var _client = NadekoBot.Client; - _client.UserJoined += async (user) => + _client.UserJoined += (user) => { - GuildConfig conf; - using (var uow = DbHandler.UnitOfWork()) + var t = Task.Run(async () => { - conf = uow.GuildConfigs.For(user.Guild.Id); - } - var aarType = conf.AutoAssignRoleId.GetType(); + GuildConfig conf; + using (var uow = DbHandler.UnitOfWork()) + { + conf = uow.GuildConfigs.For(user.Guild.Id); + } + var aarType = conf.AutoAssignRoleId.GetType(); - if (conf.AutoAssignRoleId == 0) - return; + if (conf.AutoAssignRoleId == 0) + return; - var role = user.Guild.Roles.FirstOrDefault(r => r.Id == conf.AutoAssignRoleId); + var role = user.Guild.Roles.FirstOrDefault(r => r.Id == conf.AutoAssignRoleId); - if (role != null) - await user.AddRolesAsync(role); + if (role != null) + await user.AddRolesAsync(role); + }); + return Task.CompletedTask; }; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task AutoAssignRole(IUserMessage umsg, [Remainder] IRole role = null) diff --git a/src/NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs b/src/NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs index 5c0bd358..36b00e9e 100644 --- a/src/NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs +++ b/src/NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs @@ -1,111 +1,102 @@ -//using Discord; -//using Discord.Commands; -//using NadekoBot.Classes; -//using NadekoBot.Modules.Permissions.Classes; -//using System; -//using System.Collections.Concurrent; -//using System.Collections.Generic; -//using System.Linq; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; -////todo DB -//namespace NadekoBot.Modules.Administration -//{ -// class CrossServerTextChannel : DiscordCommand -// { -// public CrossServerTextChannel(DiscordModule module) : base(module) -// { -// NadekoBot.Client.MessageReceived += async (s, e) => -// { -// try -// { -// if (umsg.Author.Id == NadekoBot.Client.CurrentUser.Id) return; -// foreach (var subscriber in Subscribers) -// { -// var set = subscriber.Value; -// if (!set.Contains(e.Channel)) -// continue; -// foreach (var chan in set.Except(new[] { e.Channel })) -// { -// await chan.SendMessageAsync(GetText(e.Server, e.Channel, umsg.Author, e.Message)).ConfigureAwait(false); -// } -// } -// } -// catch { } -// }; -// NadekoBot.Client.MessageUpdated += async (s, e) => -// { -// try -// { -// if (e.After?.User?.Id == null || e.After.User.Id == NadekoBot.Client.CurrentUser.Id) return; -// foreach (var subscriber in Subscribers) -// { -// var set = subscriber.Value; -// if (!set.Contains(e.Channel)) -// continue; -// foreach (var chan in set.Except(new[] { e.Channel })) -// { -// var msg = chan.Messages -// .FirstOrDefault(m => -// m.RawText == GetText(e.Server, e.Channel, umsg.Author, e.Before)); -// if (msg != default(Message)) -// await msg.Edit(GetText(e.Server, e.Channel, umsg.Author, e.After)).ConfigureAwait(false); -// } -// } +namespace NadekoBot.Modules.Administration +{ + public partial class Administration + { + [Group] + public class CrossServerTextChannel + { + public CrossServerTextChannel() + { + NadekoBot.Client.MessageReceived += (imsg) => + { + var msg = imsg as IUserMessage; + if (msg == null) + return Task.CompletedTask; -// } -// catch { } -// }; -// } + var channel = imsg.Channel as ITextChannel; + if (channel == null) + return Task.CompletedTask; -// private string GetText(Server server, Channel channel, User user, Message message) => -// $"**{server.Name} | {channel.Name}** `{user.Name}`: " + message.RawText; + Task.Run(async () => + { + try + { + if (msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return; + foreach (var subscriber in Subscribers) + { + var set = subscriber.Value; + if (!set.Contains(msg.Channel)) + continue; + foreach (var chan in set.Except(new[] { channel })) + { + await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); + } + } + } + catch { } + }); + return Task.CompletedTask; + }; + } -// public static readonly ConcurrentDictionary> Subscribers = new ConcurrentDictionary>(); + private string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) => + $"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content; -// internal override void Init(CommandGroupBuilder cgb) -// { -// cgb.CreateCommand(Module.Prefix + "scsc") -// .Description("Starts an instance of cross server channel. You will get a token as a DM " + -// $"that other people will use to tune in to the same instance. **Bot Owner Only.** | `{Prefix}scsc`") -// .AddCheck(SimpleCheckers.OwnerOnly()) -// .Do(async e => -// { -// var token = new Random().Next(); -// var set = new HashSet(); -// if (Subscribers.TryAdd(token, set)) -// { -// set.Add(e.Channel); -// await umsg.Author.SendMessageAsync("This is your CSC token:" + token.ToString()).ConfigureAwait(false); -// } -// }); + public static readonly ConcurrentDictionary> Subscribers = new ConcurrentDictionary>(); -// cgb.CreateCommand(Module.Prefix + "jcsc") -// .Description($"Joins current channel to an instance of cross server channel using the token. **Needs Manage Server Permissions.**| `{Prefix}jcsc`") -// .Parameter("token") -// .AddCheck(SimpleCheckers.ManageServer()) -// .Do(async e => -// { -// int token; -// if (!int.TryParse(token, out token)) -// return; -// HashSet set; -// if (!Subscribers.TryGetValue(token, out set)) -// return; -// set.Add(e.Channel); -// await channel.SendMessageAsync(":ok:").ConfigureAwait(false); -// }); + ////todo owner only + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[RequireContext(ContextType.Guild)] + //public async Task Scsc(IUserMessage msg) + //{ + // var channel = (ITextChannel)msg.Channel; + // var token = new NadekoRandom().Next(); + // var set = new HashSet(); + // if (Subscribers.TryAdd(token, set)) + // { + // set.Add(channel); + // await ((IGuildUser)msg.Author).SendMessageAsync("This is your CSC token:" + token.ToString()).ConfigureAwait(false); + // } + //} -// cgb.CreateCommand(Module.Prefix + "lcsc") -// .Description($"Leaves Cross server channel instance from this channel. **Needs Manage Server Permissions.**| `{Prefix}lcsc`") -// .AddCheck(SimpleCheckers.ManageServer()) -// .Do(async e => -// { -// foreach (var subscriber in Subscribers) -// { -// subscriber.Value.Remove(e.Channel); -// } -// await channel.SendMessageAsync(":ok:").ConfigureAwait(false); -// }); -// } -// } -//} + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageGuild)] + public async Task Jcsc(IUserMessage imsg, int token) + { + var channel = (ITextChannel)imsg.Channel; + + HashSet set; + if (!Subscribers.TryGetValue(token, out set)) + return; + set.Add(channel); + await channel.SendMessageAsync(":ok:").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageGuild)] + public async Task Lcsc(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + + foreach (var subscriber in Subscribers) + { + subscriber.Value.Remove(channel); + } + await channel.SendMessageAsync(":ok:").ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 012a580a..6cff7855 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -1,485 +1,668 @@ -//using Discord; -//using Discord.Commands; -//using NadekoBot.Classes; -//using NadekoBot.Extensions; -//using NadekoBot.Modules.Permissions.Classes; -//using System; -//using System.Collections.Concurrent; -//using System.Collections.Generic; -//using System.Linq; -//using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Services; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NLog; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; -////todo DB -////todo Add flags for every event -//namespace NadekoBot.Modules.Administration -//{ -// internal class LogCommand : DiscordCommand -// { -// private string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】"; +namespace NadekoBot.Modules.Administration +{ + public partial class Administration + { + [Group] + public class LogCommands + { + private DiscordSocketClient _client { get; } + private Logger _log { get; } -// private ConcurrentBag> voicePresenceUpdates = new ConcurrentBag>(); + private string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】"; -// public LogCommand(DiscordModule module) : base(module) -// { -// NadekoBot.Client.MessageReceived += MsgRecivd; -// NadekoBot.Client.MessageDeleted += MsgDltd; -// NadekoBot.Client.MessageUpdated += MsgUpdtd; -// NadekoBot.Client.UserUpdated += UsrUpdtd; -// NadekoBot.Client.UserBanned += UsrBanned; -// NadekoBot.Client.UserLeft += UsrLeft; -// NadekoBot.Client.UserJoined += UsrJoined; -// NadekoBot.Client.UserUnbanned += UsrUnbanned; -// NadekoBot.Client.ChannelCreated += ChannelCreated; -// NadekoBot.Client.ChannelDestroyed += ChannelDestroyed; -// NadekoBot.Client.ChannelUpdated += ChannelUpdated; + public ConcurrentDictionary GuildLogSettings { get; } + private ConcurrentDictionary> UserPresenceUpdates { get; } = new ConcurrentDictionary>(); + private Timer t; + private IGoogleApiService _google { get; } -// NadekoBot.Client.MessageReceived += async (s, e) => -// { -// if (e.Channel.IsPrivate || umsg.Author.Id == NadekoBot.Client.CurrentUser.Id) -// return; -// if (!SpecificConfigurations.Default.Of(e.Server.Id).SendPrivateMessageOnMention) return; -// try -// { -// var usr = e.Message.MentionedUsers.FirstOrDefault(u => u != umsg.Author); -// if (usr?.Status != UserStatus.Offline) -// return; -// await channel.SendMessageAsync($"User `{usr.Name}` is offline. PM sent.").ConfigureAwait(false); -// await usr.SendMessageAsync( -// $"User `{umsg.Author.Username}` mentioned you on " + -// $"`{e.Server.Name}` server while you were offline.\n" + -// $"`Message:` {e.Message.Text}").ConfigureAwait(false); -// } -// catch { } -// }; + public LogCommands(DiscordSocketClient client, IGoogleApiService google) + { + _client = client; + _google = google; + _log = LogManager.GetCurrentClassLogger(); -// // start the userpresence queue + using (var uow = DbHandler.UnitOfWork()) + { + GuildLogSettings = new ConcurrentDictionary(uow.GuildConfigs + .GetAll() + .ToDictionary(g => g.GuildId, g => g.LogSetting)); + } -// NadekoBot.OnReady += () => Task.Run(async () => -// { -// while (true) -// { -// var toSend = new Dictionary(); -// //take everything from the queue and merge the messages which are going to the same channel -// KeyValuePair item; -// while (voicePresenceUpdates.TryTake(out item)) -// { -// if (toSend.ContainsKey(item.Key)) -// { -// toSend[item.Key] = toSend[item.Key] + Environment.NewLine + item.Value; -// } -// else -// { -// toSend.Add(item.Key, item.Value); -// } -// } -// //send merged messages to each channel -// foreach (var k in toSend) -// { -// try { await k.Key.SendMessageAsync(Environment.NewLine + k.Value).ConfigureAwait(false); } catch { } -// } + t = new Timer(async (state) => + { + var keys = UserPresenceUpdates.Keys.ToList(); -// await Task.Delay(5000); -// } -// }); -// } + await Task.WhenAll(keys.Select(key => + { + List messages; + if (UserPresenceUpdates.TryRemove(key, out messages)) + try { return key.SendMessageAsync(string.Join(Environment.NewLine, messages)); } catch { } //502/403 + return Task.CompletedTask; + })); + }, null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10)); + -// private async void ChannelUpdated(object sender, ChannelUpdatedEventArgs e) -// { -// try -// { -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// var chId = config.LogServerChannel; -// if (chId == null || config.LogserverIgnoreChannels.Contains(e.After.Id)) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// if (e.Before.Name != e.After.Name) -// await ch.SendMessageAsync($@"`{prettyCurrentTime}` **Channel Name Changed** `#{e.Before.Name}` (*{e.After.Id}*) -// `New:` {e.After.Name}").ConfigureAwait(false); -// else if (e.Before.Topic != e.After.Topic) -// await ch.SendMessageAsync($@"`{prettyCurrentTime}` **Channel Topic Changed** `#{e.After.Name}` (*{e.After.Id}*) -// `Old:` {e.Before.Topic} -// `New:` {e.After.Topic}").ConfigureAwait(false); -// } -// catch { } -// } + _client.MessageReceived += _client_MessageReceived; + _client.MessageUpdated += _client_MessageUpdated; + _client.MessageDeleted += _client_MessageDeleted; + _client.UserBanned += _client_UserBanned; + _client.UserUnbanned += _client_UserUnbanned; + _client.UserJoined += _client_UserJoined; + _client.UserLeft += _client_UserLeft; + _client.UserPresenceUpdated += _client_UserPresenceUpdated; + _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; + _client.UserUpdated += _client_UserUpdated; -// private async void ChannelDestroyed(object sender, ChannelEventArgs e) -// { -// try -// { -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// var chId = config.LogServerChannel; -// if (chId == null || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// await ch.SendMessageAsync($"❗`{prettyCurrentTime}`❗`Channel Deleted:` #{e.Channel.Name} (*{e.Channel.Id}*)").ConfigureAwait(false); -// } -// catch { } -// } + _client.ChannelCreated += _client_ChannelCreated; + _client.ChannelDestroyed += _client_ChannelDestroyed; + _client.ChannelUpdated += _client_ChannelUpdated; + } -// private async void ChannelCreated(object sender, ChannelEventArgs e) -// { -// try -// { -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// var chId = config.LogServerChannel; -// if (chId == null || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// await ch.SendMessageAsync($"`{prettyCurrentTime}`🆕`Channel Created:` #{e.Channel.Mention} (*{e.Channel.Id}*)").ConfigureAwait(false); -// } -// catch { } -// } + private Task _client_UserUpdated(IGuildUser before, IGuildUser after) + { + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(before.Guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.UserUpdated) + return Task.CompletedTask; -// private async void UsrUnbanned(object sender, UserEventArgs e) -// { -// try -// { -// var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; -// if (chId == null) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// await ch.SendMessageAsync($"`{prettyCurrentTime}`♻`User was unbanned:` **{umsg.Author.Username}** ({umsg.Author.Id})").ConfigureAwait(false); -// } -// catch { } -// } + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(before.Guild, logSetting)) == null) + return Task.CompletedTask; -// private async void UsrJoined(object sender, UserEventArgs e) -// { -// try -// { -// var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; -// if (chId == null) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// await ch.SendMessageAsync($"`{prettyCurrentTime}`✅`User joined:` **{umsg.Author.Username}** ({umsg.Author.Id})").ConfigureAwait(false); -// } -// catch { } -// } + var task = Task.Run(async () => + { + string str = $"🕔`{prettyCurrentTime}`"; + if (before.Username != after.Username) + str += $"**Name Changed**👤`{before.Username}#{before.Discriminator}`\n\t\t`New:`{after.ToString()}`"; + else if (before.Nickname != after.Nickname) + str += $"**Nickname Changed**👤`{before.Username}#{before.Discriminator}`\n\t\t`Old:` {before.Nickname}#{before.Discriminator}\n\t\t`New:` {after.Nickname}#{after.Discriminator}"; + else if (before.AvatarUrl != after.AvatarUrl) + str += $"**Avatar Changed**👤`{before.Username}#{before.Discriminator}`\n\t {await _google.ShortenUrl(before.AvatarUrl)} `=>` {await _google.ShortenUrl(after.AvatarUrl)}"; + else if (!before.Roles.SequenceEqual(after.Roles)) + { + if (before.Roles.Count() < after.Roles.Count()) + { + var diffRoles = after.Roles.Where(r => !before.Roles.Contains(r)).Select(r => "`" + r.Name + "`"); + str += $"**User's Roles changed ⚔➕**👤`{before.ToString()}`\n\tNow has {string.Join(", ", diffRoles)} role."; + } + else if (before.Roles.Count() > after.Roles.Count()) + { + var diffRoles = before.Roles.Where(r => !after.Roles.Contains(r)).Select(r => "`" + r.Name + "`"); + str += $"**User's Roles changed ⚔➖**👤`{before.ToString()}`\n\tNo longer has {string.Join(", ", diffRoles)} role."; + } + } + else + return; + await logChannel.SendMessageAsync(str).ConfigureAwait(false); + }); -// private async void UsrLeft(object sender, UserEventArgs e) -// { -// try -// { -// var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; -// if (chId == null) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// await ch.SendMessageAsync($"`{prettyCurrentTime}`❗`User left:` **{umsg.Author.Username}** ({umsg.Author.Id})").ConfigureAwait(false); -// } -// catch { } -// } + return Task.CompletedTask; + } -// private async void UsrBanned(object sender, UserEventArgs e) -// { -// try -// { -// var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; -// if (chId == null) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// await ch.SendMessageAsync($"❗`{prettyCurrentTime}`❌`User banned:` **{umsg.Author.Username}** ({umsg.Author.Id})").ConfigureAwait(false); -// } -// catch { } -// } + private Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter) + { + var before = cbefore as IGuildChannel; + if (before == null) + return Task.CompletedTask; + var after = (IGuildChannel)cafter; -// private async void MsgRecivd(object sender, MessageEventArgs e) -// { -// try -// { -// if (e.Server == null || e.Channel.IsPrivate || umsg.Author.Id == NadekoBot.Client.CurrentUser.Id) -// return; -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// var chId = config.LogServerChannel; -// if (chId == null || e.Channel.Id == chId || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// if (!string.IsNullOrWhiteSpace(e.Message.Text)) -// { -// await ch.SendMessageAsync( -// $@"🕔`{prettyCurrentTime}` **New Message** `#{e.Channel.Name}` -//👤`{umsg.Author?.ToString() ?? ("NULL")}` {e.Message.Text.Unmention()}").ConfigureAwait(false); -// } -// else -// { -// await ch.SendMessageAsync( -// $@"🕔`{prettyCurrentTime}` **File Uploaded** `#{e.Channel.Name}` -//👤`{umsg.Author?.ToString() ?? ("NULL")}` {e.Message.Attachments.FirstOrDefault()?.ProxyUrl}").ConfigureAwait(false); -// } + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(before.Guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.ChannelUpdated) + return Task.CompletedTask; -// } -// catch { } -// } -// private async void MsgDltd(object sender, MessageEventArgs e) -// { -// try -// { -// if (e.Server == null || e.Channel.IsPrivate || umsg.Author?.Id == NadekoBot.Client.CurrentUser.Id) -// return; -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// var chId = config.LogServerChannel; -// if (chId == null || e.Channel.Id == chId || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// if (!string.IsNullOrWhiteSpace(e.Message.Text)) -// { -// await ch.SendMessageAsync( -// $@"🕔`{prettyCurrentTime}` **Message** 🚮 `#{e.Channel.Name}` -//👤`{umsg.Author?.ToString() ?? ("NULL")}` {e.Message.Text.Unmention()}").ConfigureAwait(false); -// } -// else -// { -// await ch.SendMessageAsync( -// $@"🕔`{prettyCurrentTime}` **File Deleted** `#{e.Channel.Name}` -//👤`{umsg.Author?.ToString() ?? ("NULL")}` {e.Message.Attachments.FirstOrDefault()?.ProxyUrl}").ConfigureAwait(false); -// } -// } -// catch { } -// } -// private async void MsgUpdtd(object sender, MessageUpdatedEventArgs e) -// { -// try -// { -// if (e.Server == null || e.Channel.IsPrivate || umsg.Author?.Id == NadekoBot.Client.CurrentUser.Id) -// return; -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// var chId = config.LogServerChannel; -// if (chId == null || e.Channel.Id == chId || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// await ch.SendMessageAsync( -// $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}` -//👤`{umsg.Author?.ToString() ?? ("NULL")}` -// `Old:` {e.Before.Text.Unmention()} -// `New:` {e.After.Text.Unmention()}").ConfigureAwait(false); -// } -// catch { } -// } -// private async void UsrUpdtd(object sender, UserUpdatedEventArgs e) -// { -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// try -// { -// var chId = config.LogPresenceChannel; -// if (chId != null) -// { -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) != null) -// { -// if (e.Before.Status != e.After.Status) -// { -// voicePresenceUpdates.Add(new KeyValuePair(ch, $"`{prettyCurrentTime}`**{e.Before.Name}** is now **{e.After.Status}**.")); -// } -// } -// } -// } -// catch { } + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(before.Guild, logSetting)) == null) + return Task.CompletedTask; -// try -// { -// ulong notifyChBeforeId; -// ulong notifyChAfterId; -// Channel notifyChBefore = null; -// Channel notifyChAfter = null; -// var beforeVch = e.Before.VoiceChannel; -// var afterVch = e.After.VoiceChannel; -// var notifyLeave = false; -// var notifyJoin = false; -// if ((beforeVch != null || afterVch != null) && (beforeVch != afterVch)) // this means we need to notify for sure. -// { -// if (beforeVch != null && config.VoiceChannelLog.TryGetValue(beforeVch.Id, out notifyChBeforeId) && (notifyChBefore = e.Before.Server.TextChannels.FirstOrDefault(tc => tc.Id == notifyChBeforeId)) != null) -// { -// notifyLeave = true; -// } -// if (afterVch != null && config.VoiceChannelLog.TryGetValue(afterVch.Id, out notifyChAfterId) && (notifyChAfter = e.After.Server.TextChannels.FirstOrDefault(tc => tc.Id == notifyChAfterId)) != null) -// { -// notifyJoin = true; -// } -// if ((notifyLeave && notifyJoin) && (notifyChAfter == notifyChBefore)) -// { -// await notifyChAfter.SendMessageAsync($"🎼`{prettyCurrentTime}` {e.Before.Name} moved from **{beforeVch.Mention}** to **{afterVch.Mention}** voice channel.").ConfigureAwait(false); -// } -// else if (notifyJoin) -// { -// await notifyChAfter.SendMessageAsync($"🎼`{prettyCurrentTime}` {e.Before.Name} has joined **{afterVch.Mention}** voice channel.").ConfigureAwait(false); -// } -// else if (notifyLeave) -// { -// await notifyChBefore.SendMessageAsync($"🎼`{prettyCurrentTime}` {e.Before.Name} has left **{beforeVch.Mention}** voice channel.").ConfigureAwait(false); -// } -// } -// } -// catch { } + var task = Task.Run(async () => + { + if (before.Name != after.Name) + await logChannel.SendMessageAsync($@"`{prettyCurrentTime}` **Channel Name Changed** `#{after.Name}` ({after.Id}) + `Old:` {before.Name} + `New:` {after.Name}").ConfigureAwait(false); + else if ((before as ITextChannel).Topic != (after as ITextChannel).Topic) + await logChannel.SendMessageAsync($@"`{prettyCurrentTime}` **Channel Topic Changed** `#{after.Name}` ({after.Id}) + `Old:` {((ITextChannel)before).Topic} + `New:` {((ITextChannel)after).Topic}").ConfigureAwait(false); + }); -// try -// { -// var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; -// if (chId == null) -// return; -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; -// string str = $"🕔`{prettyCurrentTime}`"; -// if (e.Before.Name != e.After.Name) -// str += $"**Name Changed**👤`{e.Before?.ToString()}`\n\t\t`New:`{e.After.ToString()}`"; -// else if (e.Before.Nickname != e.After.Nickname) -// str += $"**Nickname Changed**👤`{e.Before?.ToString()}`\n\t\t`Old:` {e.Before.Nickname}#{e.Before.Discriminator}\n\t\t`New:` {e.After.Nickname}#{e.After.Discriminator}"; -// else if (e.Before.AvatarUrl != e.After.AvatarUrl) -// str += $"**Avatar Changed**👤`{e.Before?.ToString()}`\n\t {await e.Before.AvatarUrl.ShortenUrl()} `=>` {await e.After.AvatarUrl.ShortenUrl()}"; -// else if (!e.Before.Roles.SequenceEqual(e.After.Roles)) -// { -// if (e.Before.Roles.Count() < e.After.Roles.Count()) -// { -// var diffRoles = e.After.Roles.Where(r => !e.Before.Roles.Contains(r)).Select(r => "`" + r.Name + "`"); -// str += $"**User's Roles changed ⚔➕**👤`{e.Before?.ToString()}`\n\tNow has {string.Join(", ", diffRoles)} role."; -// } -// else if (e.Before.Roles.Count() > e.After.Roles.Count()) -// { -// var diffRoles = e.Before.Roles.Where(r => !e.After.Roles.Contains(r)).Select(r => "`" + r.Name + "`"); -// str += $"**User's Roles changed ⚔➖**👤`{e.Before?.ToString()}`\n\tNo longer has {string.Join(", ", diffRoles)} role."; -// } -// else -// { -// Console.WriteLine("SEQUENCE NOT EQUAL BUT NO DIFF ROLES - REPORT TO KWOTH on #NADEKOLOG server"); -// return; -// } + return Task.CompletedTask; + } -// } -// else -// return; -// await ch.SendMessageAsync(str).ConfigureAwait(false); -// } -// catch { } -// } + private Task _client_ChannelDestroyed(IChannel ich) + { + var ch = ich as IGuildChannel; + if (ch == null) + return Task.CompletedTask; -// internal override void Init(CommandGroupBuilder cgb) -// { + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.ChannelDestroyed) + return Task.CompletedTask; -// cgb.CreateCommand(Module.Prefix + "spmom") -// .Description($"Toggles whether mentions of other offline users on your server will send a pm to them. **Needs Manage Server Permissions.**| `{Prefix}spmom`") -// .AddCheck(SimpleCheckers.ManageServer()) -// .Do(async e => -// { -// var specificConfig = SpecificConfigurations.Default.Of(e.Server.Id); -// specificConfig.SendPrivateMessageOnMention = -// !specificConfig.SendPrivateMessageOnMention; -// if (specificConfig.SendPrivateMessageOnMention) -// await channel.SendMessageAsync(":ok: I will send private messages " + -// "to mentioned offline users.").ConfigureAwait(false); -// else -// await channel.SendMessageAsync(":ok: I won't send private messages " + -// "to mentioned offline users anymore.").ConfigureAwait(false); -// }); + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(ch.Guild, logSetting)) == null) + return Task.CompletedTask; -// cgb.CreateCommand(Module.Prefix + "logserver") -// .Description($"Toggles logging in this channel. Logs every message sent/deleted/edited on the server. **Bot Owner Only!** | `{Prefix}logserver`") -// .AddCheck(SimpleCheckers.OwnerOnly()) -// .AddCheck(SimpleCheckers.ManageServer()) -// .Do(async e => -// { -// var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; -// if (chId == null) -// { -// SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel = e.Channel.Id; -// await channel.SendMessageAsync($"❗**I WILL BEGIN LOGGING SERVER ACTIVITY IN THIS CHANNEL**❗").ConfigureAwait(false); -// return; -// } -// Channel ch; -// if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) -// return; + var task = Task.Run(async () => + { + await logChannel.SendMessageAsync($"❗`{prettyCurrentTime}` `{(ch is IVoiceChannel ? "Voice" : "Text")} Channel Deleted:` **#{ch.Name}** ({ch.Id})").ConfigureAwait(false); + }); -// SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel = null; -// await channel.SendMessageAsync($"❗**NO LONGER LOGGING IN {ch.Mention} CHANNEL**❗").ConfigureAwait(false); -// }); + return Task.CompletedTask; + } + private Task _client_ChannelCreated(IChannel ich) + { + var ch = ich as IGuildChannel; + if (ch == null) + return Task.CompletedTask; -// cgb.CreateCommand(Prefix + "logignore") -// .Description($"Toggles whether the {Prefix}logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Bot Owner Only!**| `{Prefix}logignore`") -// .AddCheck(SimpleCheckers.OwnerOnly()) -// .AddCheck(SimpleCheckers.ManageServer()) -// .Do(async e => -// { -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// if (config.LogserverIgnoreChannels.Remove(e.Channel.Id)) -// { -// await channel.SendMessageAsync($"`{Prefix}logserver will stop ignoring this channel.`"); -// } -// else -// { -// config.LogserverIgnoreChannels.Add(e.Channel.Id); -// await channel.SendMessageAsync($"`{Prefix}logserver will ignore this channel.`"); -// } -// }); + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.ChannelCreated) + return Task.CompletedTask; -// cgb.CreateCommand(Module.Prefix + "userpresence") -// .Description($"Starts logging to this channel when someone from the server goes online/offline/idle. **Needs Manage Server Permissions.**| `{Prefix}userpresence`") -// .AddCheck(SimpleCheckers.ManageServer()) -// .Do(async e => -// { -// var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogPresenceChannel; -// if (chId == null) -// { -// SpecificConfigurations.Default.Of(e.Server.Id).LogPresenceChannel = e.Channel.Id; -// await channel.SendMessageAsync($"**User presence notifications enabled.**").ConfigureAwait(false); -// return; -// } -// SpecificConfigurations.Default.Of(e.Server.Id).LogPresenceChannel = null; -// await channel.SendMessageAsync($"**User presence notifications disabled.**").ConfigureAwait(false); -// }); + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(ch.Guild, logSetting)) == null) + return Task.CompletedTask; -// cgb.CreateCommand(Module.Prefix + "voicepresence") -// .Description($"Toggles logging to this channel whenever someone joins or leaves a voice channel you are in right now. **Needs Manage Server Permissions.**| `{Prefix}voicerpresence`") -// .Parameter("all", ParameterType.Optional) -// .AddCheck(SimpleCheckers.ManageServer()) -// .Do(async e => -// { + var task = Task.Run(async () => + { + await logChannel.SendMessageAsync($"`{prettyCurrentTime}`🆕`{(ch is IVoiceChannel ? "Voice" : "Text")} Channel Created:` **#{ch.Name}** ({ch.Id})").ConfigureAwait(false); + }); -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// if (all?.ToLower() == "all") -// { -// foreach (var voiceChannel in e.Server.VoiceChannels) -// { -// config.VoiceChannelLog.TryAdd(voiceChannel.Id, e.Channel.Id); -// } -// await channel.SendMessageAsync("Started logging user presence for **ALL** voice channels!").ConfigureAwait(false); -// return; -// } + return Task.CompletedTask; + } -// if (umsg.Author.VoiceChannel == null) -// { -// await channel.SendMessageAsync("💢 You are not in a voice channel right now. If you are, please rejoin it.").ConfigureAwait(false); -// return; -// } -// ulong throwaway; -// if (!config.VoiceChannelLog.TryRemove(umsg.Author.VoiceChannel.Id, out throwaway)) -// { -// config.VoiceChannelLog.TryAdd(umsg.Author.VoiceChannel.Id, e.Channel.Id); -// await channel.SendMessageAsync($"`Logging user updates for` {umsg.Author.VoiceChannel.Mention} `voice channel.`").ConfigureAwait(false); -// } -// else -// await channel.SendMessageAsync($"`Stopped logging user updates for` {umsg.Author.VoiceChannel.Mention} `voice channel.`").ConfigureAwait(false); -// }); -// } -// } -//} + private Task _client_UserVoiceStateUpdated(IUser iusr, IVoiceState before, IVoiceState after) + { + var usr = iusr as IGuildUser; + if (usr == null) + return Task.CompletedTask; + + var beforeVch = before.VoiceChannel; + var afterVch = after.VoiceChannel; + + if (beforeVch == afterVch) + return Task.CompletedTask; + + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting) + || !logSetting.LogVoicePresence + || logSetting.IgnoredChannels.Any(ic => ic.ChannelId == after.VoiceChannel.Id)) + return Task.CompletedTask; + + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogChannelType.Voice)) == null) + return Task.CompletedTask; + + string str = null; + if (beforeVch?.Guild == afterVch?.Guild) + { + str = $"🎼`{prettyCurrentTime}` {usr.Username} moved from **{beforeVch.Name}** to **{afterVch.Name}** voice channel."; + } + else if (beforeVch == null) + { + str = $"🎼`{prettyCurrentTime}` {usr.Username} has joined **{afterVch.Name}** voice channel."; + } + else if (afterVch == null) + { + str = $"🎼`{prettyCurrentTime}` {usr.Username} has left **{beforeVch.Name}** voice channel."; + } + if(str != null) + UserPresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); + + return Task.CompletedTask; + } + + private Task _client_UserPresenceUpdated(IGuildUser usr, IPresence before, IPresence after) + { + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting) + || !logSetting.LogUserPresence + || before.Status == after.Status) + return Task.CompletedTask; + + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogChannelType.UserPresence)) == null) + return Task.CompletedTask; + string str; + if (before.Status != after.Status) + str = $"`{prettyCurrentTime}`**{usr.Username}** is now **{after.Status}**."; + else + str = $"`{prettyCurrentTime}`**{usr.Username}** is now playing **{after.Status}**."; + + UserPresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); + + return Task.CompletedTask; + } + + private Task _client_UserLeft(IGuildUser usr) + { + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.UserLeft) + return Task.CompletedTask; + + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(usr.Guild, logSetting)) == null) + return Task.CompletedTask; + + var task = Task.Run(async () => + { + await logChannel.SendMessageAsync($"`{prettyCurrentTime}`❗`User left:` **{usr.Username}** ({usr.Id})").ConfigureAwait(false); + }); + + return Task.CompletedTask; + } + + private Task _client_UserJoined(IGuildUser usr) + { + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.UserJoined) + return Task.CompletedTask; + + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(usr.Guild, logSetting)) == null) + return Task.CompletedTask; + + var task = Task.Run(async () => + { + await logChannel.SendMessageAsync($"`{prettyCurrentTime}`❗`User joined:` **{usr.Username}** ({usr.Id})").ConfigureAwait(false); + }); + + return Task.CompletedTask; + } + + private Task _client_UserUnbanned(IUser usr, IGuild guild) + { + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.UserUnbanned) + return Task.CompletedTask; + + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(guild, logSetting)) == null) + return Task.CompletedTask; + + var task = Task.Run(async () => + { + await logChannel.SendMessageAsync($"`{prettyCurrentTime}`♻`User unbanned:` **{usr.Username}** ({usr.Id})").ConfigureAwait(false); + }); + + return Task.CompletedTask; + } + + private Task _client_UserBanned(IUser usr, IGuild guild) + { + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.UserBanned) + return Task.CompletedTask; + + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(guild, logSetting)) == null) + return Task.CompletedTask; + + var task = Task.Run(async () => + { + await logChannel.SendMessageAsync($"❗`{prettyCurrentTime}`❌`User banned:` **{usr.Username}** ({usr.Id})").ConfigureAwait(false); + }); + + return Task.CompletedTask; + } + + private Task _client_MessageDeleted(ulong arg1, Optional imsg) + { + var msg = (imsg.IsSpecified ? imsg.Value : null) as IUserMessage; + if (msg == null || msg.IsAuthor()) + return Task.CompletedTask; + + var channel = msg.Channel as ITextChannel; + if (channel == null) + return Task.CompletedTask; + + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.MessageDeleted) + return Task.CompletedTask; + + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(channel.Guild, logSetting)) == null || logChannel.Id == msg.Id) + return Task.CompletedTask; + + var task = Task.Run(async () => + { + var str = $@"🕔`{prettyCurrentTime}` **Message** 🚮 `#{channel.Name}` +👤`{msg.Author.Username}`: {msg.Resolve(userHandling:UserMentionHandling.NameAndDiscriminator)}"; + if (msg.Attachments.Any()) + str += $"{Environment.NewLine}`Attachements`: {string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))}"; + await logChannel.SendMessageAsync(str).ConfigureAwait(false); + }); + + return Task.CompletedTask; + } + + private Task _client_MessageUpdated(Optional optmsg, IMessage imsg2) + { + var after = imsg2 as IUserMessage; + if (after == null || after.IsAuthor()) + return Task.CompletedTask; + + var before = (optmsg.IsSpecified ? optmsg.Value : null) as IUserMessage; + if (before == null) + return Task.CompletedTask; + + var channel = after.Channel as ITextChannel; + if (channel == null) + return Task.CompletedTask; + + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.MessageUpdated) + return Task.CompletedTask; + + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(channel.Guild, logSetting)) == null || logChannel.Id == after.Channel.Id) + return Task.CompletedTask; + + var task = Task.Run(async () => + { + await logChannel.SendMessageAsync($@"🕔`{prettyCurrentTime}` **Message** 📝 `#{channel.Name}` +👤`{before.Author.Username}` + `Old:` {before.Resolve(userHandling: UserMentionHandling.NameAndDiscriminator)} + `New:` {after.Resolve(userHandling: UserMentionHandling.NameAndDiscriminator)}").ConfigureAwait(false); + }); + + return Task.CompletedTask; + } + + private Task _client_MessageReceived(IMessage imsg) + { + var msg = imsg as IUserMessage; + if (msg == null || msg.IsAuthor()) + return Task.CompletedTask; + + var channel = msg.Channel as ITextChannel; + if (channel == null) + return Task.CompletedTask; + + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out logSetting) + || !logSetting.IsLogging + || !logSetting.MessageReceived) + return Task.CompletedTask; + + ITextChannel logChannel; + if ((logChannel = TryGetLogChannel(channel.Guild, logSetting)) == null || logChannel.Id == imsg.Channel.Id) + return Task.CompletedTask; + + var task = Task.Run(async () => + { + var str = $@"🕔`{prettyCurrentTime}` **New Message** `#{channel.Name}` +👤`{msg.Author.Username}`: {msg.Resolve(userHandling: UserMentionHandling.NameAndDiscriminator)}"; + if (msg.Attachments.Any()) + str += $"{Environment.NewLine}`Attachements`: {string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))}"; + await logChannel.SendMessageAsync(str).ConfigureAwait(false); + }); + + return Task.CompletedTask; + } + + private enum LogChannelType { Text, Voice, UserPresence }; + private ITextChannel TryGetLogChannel(IGuild guild, LogSetting logSetting, LogChannelType logChannelType = LogChannelType.Text) + { + ulong id = 0; + switch (logChannelType) + { + case LogChannelType.Text: + id = logSetting.ChannelId; + break; + case LogChannelType.Voice: + id = logSetting.VoicePresenceChannelId; + break; + case LogChannelType.UserPresence: + id = logSetting.UserPresenceChannelId; + break; + } + var channel = guild.GetTextChannel(id); + + if (channel == null) + using (var uow = DbHandler.UnitOfWork()) + { + var newLogSetting = uow.GuildConfigs.For(guild.Id).LogSetting; + switch (logChannelType) + { + case LogChannelType.Text: + logSetting.IsLogging = false; + break; + case LogChannelType.Voice: + logSetting.LogVoicePresence = false; + break; + case LogChannelType.UserPresence: + logSetting.LogUserPresence = false; + break; + } + GuildLogSettings.AddOrUpdate(guild.Id, newLogSetting, (gid, old) => newLogSetting); + uow.Complete(); + return null; + } + else + return channel; + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task LogServer(IUserMessage msg) + { + var channel = (ITextChannel)msg.Channel; + LogSetting logSetting; + using (var uow = DbHandler.UnitOfWork()) + { + logSetting = uow.GuildConfigs.For(channel.Guild.Id).LogSetting; + GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting); + logSetting.IsLogging = !logSetting.IsLogging; + if (logSetting.IsLogging) + logSetting.ChannelId = channel.Id; + await uow.CompleteAsync(); + } + + if (logSetting.IsLogging) + await channel.SendMessageAsync("`Logging enabled.`").ConfigureAwait(false); + else + await channel.SendMessageAsync("`Logging disabled.`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task LogIgnore(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + int removed; + using (var uow = DbHandler.UnitOfWork()) + { + var config = uow.GuildConfigs.For(channel.Guild.Id); + LogSetting logSetting = GuildLogSettings.GetOrAdd(channel.Guild.Id, (id) => config.LogSetting); + removed = logSetting.IgnoredChannels.RemoveWhere(ilc => ilc.ChannelId == channel.Id); + if (removed == 0) + logSetting.IgnoredChannels.Add(new IgnoredLogChannel { ChannelId = channel.Id }); + config.LogSetting = logSetting; + await uow.CompleteAsync().ConfigureAwait(false); + } + + if (removed == 0) + await channel.SendMessageAsync($"`Logging will now ignore {channel.Name} ({channel.Id}) channel.`").ConfigureAwait(false); + else + await channel.SendMessageAsync($"`Logging will no longer ignore {channel.Name} ({channel.Id}) channel.`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task LogAdd(IUserMessage msg, [Remainder] string eventName) + { + var channel = (ITextChannel)msg.Channel; + //eventName = eventName?.Replace(" ","").ToLowerInvariant(); + + switch (eventName.ToLowerInvariant()) + { + case "messagereceived": + case "messageupdated": + case "messagedeleted": + case "userjoined": + case "userleft": + case "userbanned": + case "userunbanned": + case "channelcreated": + case "channeldestroyed": + case "channelupdated": + using (var uow = DbHandler.UnitOfWork()) + { + var logSetting = uow.GuildConfigs.For(channel.Guild.Id).LogSetting; + GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting); + var prop = logSetting.GetType().GetProperty(eventName); + prop.SetValue(logSetting, true); + await uow.CompleteAsync().ConfigureAwait(false); + } + await channel.SendMessageAsync($"`Now logging {eventName} event.`").ConfigureAwait(false); + break; + default: + await channel.SendMessageAsync($"`Event \"{eventName}\" not found.`").ConfigureAwait(false); + break; + } + } + + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[RequireContext(ContextType.Guild)] + //public async Task LogRemove(IUserMessage msg, string eventName) + //{ + // var channel = (ITextChannel)msg.Channel; + // eventName = eventName.ToLowerInvariant(); + + // switch (eventName) + // { + // case "messagereceived": + // case "messageupdated": + // case "messagedeleted": + // case "userjoined": + // case "userleft": + // case "userbanned": + // case "userunbanned": + // case "channelcreated": + // case "channeldestroyed": + // case "channelupdated": + // using (var uow = DbHandler.UnitOfWork()) + // { + // var config = uow.GuildConfigs.For(channel.Guild.Id); + // LogSetting logSetting = GuildLogSettings.GetOrAdd(channel.Guild.Id, (id) => config.LogSetting); + // logSetting.GetType().GetProperty(eventName).SetValue(logSetting, false); + // config.LogSetting = logSetting; + // await uow.CompleteAsync().ConfigureAwait(false); + // } + // await channel.SendMessageAsync($"`No longer logging {eventName} event.`").ConfigureAwait(false); + // break; + // default: + // await channel.SendMessageAsync($"`Event \"{eventName}\" not found.`").ConfigureAwait(false); + // break; + // } + //} + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task UserPresence(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + bool enabled; + using (var uow = DbHandler.UnitOfWork()) + { + var logSetting = uow.GuildConfigs.For(channel.Guild.Id).LogSetting; + GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting); + enabled = logSetting.LogUserPresence = !logSetting.LogUserPresence; + if(enabled) + logSetting.UserPresenceChannelId = channel.Id; + await uow.CompleteAsync().ConfigureAwait(false); + } + + if (enabled) + await channel.SendMessageAsync($"`Logging user presence updates in {channel.Name} ({channel.Id}) channel.`").ConfigureAwait(false); + else + await channel.SendMessageAsync($"`Stopped logging user presence updates.`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task VoicePresence(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + bool enabled; + using (var uow = DbHandler.UnitOfWork()) + { + var logSetting = uow.GuildConfigs.For(channel.Guild.Id).LogSetting; + GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting); + enabled = logSetting.LogVoicePresence = !logSetting.LogVoicePresence; + if (enabled) + logSetting.VoicePresenceChannelId = channel.Id; + await uow.CompleteAsync().ConfigureAwait(false); + } + + if (enabled) + await channel.SendMessageAsync($"`Logging voice presence updates in {channel.Name} ({channel.Id}) channel.`").ConfigureAwait(false); + else + await channel.SendMessageAsync($"`Stopped logging voice presence updates.`").ConfigureAwait(false); + } + + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[RequireContext(ContextType.Guild)] + //public async Task VoiPresIgnore(IUserMessage imsg, IVoiceChannel voiceChannel) + //{ + // var channel = (ITextChannel)imsg.Channel; + // int removed; + // using (var uow = DbHandler.UnitOfWork()) + // { + // var config = uow.GuildConfigs.For(channel.Guild.Id); + // LogSetting logSetting = GuildLogSettings.GetOrAdd(channel.Guild.Id, (id) => config.LogSetting); + // removed = logSetting.IgnoredVoicePresenceChannelIds.RemoveWhere(ivpc => ivpc.ChannelId == voiceChannel.Id); + // if (removed == 0) + // logSetting.IgnoredVoicePresenceChannelIds.Add(new IgnoredVoicePresenceChannel { ChannelId = voiceChannel.Id }); + // config.LogSetting = logSetting; + // await uow.CompleteAsync().ConfigureAwait(false); + // } + + // if (removed == 0) + // await channel.SendMessageAsync($"`Enabled logging voice presence updates for {voiceChannel.Name} ({voiceChannel.Id}) channel.`").ConfigureAwait(false); + // else + // await channel.SendMessageAsync($"`Disabled logging voice presence updates for {voiceChannel.Name} ({voiceChannel.Id}) channel.`").ConfigureAwait(false); + //} + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs index 520372db..57c5fe9a 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs @@ -1,130 +1,159 @@ using Discord; using Discord.Commands; -using NadekoBot.Classes; -using NadekoBot.Modules.Permissions.Classes; +using Discord.WebSocket; +using NadekoBot.Attributes; +using NadekoBot.Services; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; using System.Threading.Tasks; -using System.Timers; -//todo DB namespace NadekoBot.Modules.Administration { - class MessageRepeater : DiscordCommand + public partial class Administration { - private readonly ConcurrentDictionary repeaters = new ConcurrentDictionary(); - private class Repeater + [Group] + public class RepeatCommands { - [Newtonsoft.Json.JsonIgnore] - public Timer MessageTimer { get; set; } - [Newtonsoft.Json.JsonIgnore] - public Channel RepeatingChannel { get; set; } + public ConcurrentDictionary repeaters; - public ulong RepeatingServerId { get; set; } - public ulong RepeatingChannelId { get; set; } - public Message lastMessage { get; set; } = null; - public string RepeatingMessage { get; set; } - public int Interval { get; set; } - - public Repeater Start() + public class RepeatRunner { - MessageTimer = new Timer { Interval = Interval }; - MessageTimer.Elapsed += async (s, e) => await Invoke(); - return this; - } + private CancellationTokenSource source { get; set; } + private CancellationToken token { get; set; } + public Repeater Repeater { get; } + public ITextChannel Channel { get; } - public async Task Invoke() - { - var ch = RepeatingChannel; - var msg = RepeatingMessage; - if (ch != null && !string.IsNullOrWhiteSpace(msg)) + public RepeatRunner(Repeater repeater, ITextChannel channel = null) { + this.Repeater = repeater; + this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId); + if (Channel == null) + return; + Task.Run(Run); + } + + + private async Task Run() + { + source = new CancellationTokenSource(); + token = source.Token; try { - if (lastMessage != null) - await lastMessage.Delete().ConfigureAwait(false); + while (!token.IsCancellationRequested) + { + await Task.Delay(Repeater.Interval, token).ConfigureAwait(false); + await Channel.SendMessageAsync("🔄 " + Repeater.Message).ConfigureAwait(false); + } } - catch { } - try - { - lastMessage = await ch.SendMessageAsync(msg).ConfigureAwait(false); - } - catch { } + catch (OperationCanceledException) { } + } + + public void Reset() + { + source.Cancel(); + var t = Task.Run(Run); + } + + public void Stop() + { + source.Cancel(); } } - } - internal override void Init(CommandGroupBuilder cgb) - { - cgb.CreateCommand(Module.Prefix + "repeatinvoke") - .Alias(Module.Prefix + "repinv") - .Description($"Immediately shows the repeat message and restarts the timer. **Needs Manage Messages Permissions.**| `{Prefix}repinv`") - .AddCheck(SimpleCheckers.ManageMessages()) - .Do(async e => + public RepeatCommands() + { + using (var uow = DbHandler.UnitOfWork()) { - Repeater rep; - if (!repeaters.TryGetValue(e.Server, out rep)) - { - await channel.SendMessageAsync("`No repeating message found on this server.`"); - return; - } + repeaters = new ConcurrentDictionary(uow.Repeaters.GetAll().Select(r => new RepeatRunner(r)).Where(r => r != null).ToDictionary(r => r.Repeater.ChannelId)); + } + } - await rep.Invoke(); - }); + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageMessages)] + public async Task RepeatInvoke(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; - cgb.CreateCommand(Module.Prefix + "repeat") - .Description("Repeat a message every X minutes. If no parameters are specified, " + - $"repeat is disabled. **Needs Manage Messages Permissions.** |`{Prefix}repeat 5 Hello there`") - .Parameter("minutes", ParameterType.Optional) - .Parameter("msg", ParameterType.Unparsed) - .AddCheck(SimpleCheckers.ManageMessages()) - .Do(async e => + RepeatRunner rep; + if (!repeaters.TryGetValue(channel.Id, out rep)) { - var minutesStr = minutes; - var msg = msg; + await channel.SendMessageAsync("`No repeating message found on this server.`").ConfigureAwait(false); + return; + } + rep.Reset(); + await channel.SendMessageAsync("🔄 " + rep.Repeater.Message).ConfigureAwait(false); + } - // if both null, disable - if (string.IsNullOrWhiteSpace(msg) && string.IsNullOrWhiteSpace(minutesStr)) + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Repeat(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + RepeatRunner rep; + if (repeaters.TryRemove(channel.Id, out rep)) + { + using (var uow = DbHandler.UnitOfWork()) { - - Repeater rep; - if (!repeaters.TryRemove(e.Server, out rep)) - return; - rep.MessageTimer.Stop(); - await channel.SendMessageAsync("Repeating disabled").ConfigureAwait(false); - return; + uow.Repeaters.Remove(rep.Repeater); + await uow.CompleteAsync(); } - int minutes; - if (!int.TryParse(minutesStr, out minutes) || minutes < 1 || minutes > 1440) + rep.Stop(); + await channel.SendMessageAsync("`Stopped repeating a message.`").ConfigureAwait(false); + } + else + await channel.SendMessageAsync("`No message is repeating.`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Repeat(IUserMessage imsg, int minutes, [Remainder] string message) + { + var channel = (ITextChannel)imsg.Channel; + + if (minutes < 1 || minutes > 1500) + return; + + if (string.IsNullOrWhiteSpace(message)) + return; + + RepeatRunner rep; + + rep = repeaters.AddOrUpdate(channel.Id, (cid) => + { + using (var uow = DbHandler.UnitOfWork()) { - await channel.SendMessageAsync("Invalid value").ConfigureAwait(false); - return; - } - - var repeater = repeaters.GetOrAdd( - e.Server, - s => new Repeater + var localRep = new Repeater { - Interval = minutes * 60 * 1000, - RepeatingChannel = e.Channel, - RepeatingChannelId = e.Channel.Id, - RepeatingServerId = e.Server.Id, - }.Start() - ); - - if (!string.IsNullOrWhiteSpace(msg)) - repeater.RepeatingMessage = msg; - - repeater.MessageTimer.Stop(); - repeater.MessageTimer.Start(); - - await channel.SendMessageAsync(String.Format("👌 Repeating `{0}` every " + - "**{1}** minutes on {2} channel.", - repeater.RepeatingMessage, minutes, repeater.RepeatingChannel)) - .ConfigureAwait(false); + ChannelId = channel.Id, + GuildId = channel.Guild.Id, + Interval = TimeSpan.FromMinutes(minutes), + Message = message, + }; + uow.Repeaters.Add(localRep); + uow.Complete(); + return new RepeatRunner(localRep, channel); + } + }, (cid, old) => + { + using (var uow = DbHandler.UnitOfWork()) + { + old.Repeater.Message = message; + old.Repeater.Interval = TimeSpan.FromMinutes(minutes); + uow.Repeaters.Update(old.Repeater); + uow.Complete(); + } + old.Reset(); + return old; }); - } - public MessageRepeater(DiscordModule module) : base(module) { } + await channel.SendMessageAsync($"Repeating \"{rep.Repeater.Message}\" every {rep.Repeater.Interval} minutes").ConfigureAwait(false); + } + } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs index 34e36096..59baf3d4 100644 --- a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs @@ -86,7 +86,7 @@ namespace NadekoBot.Modules.Administration {"%queued%", () => Music.Music.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()} }; - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task RotatePlaying(IUserMessage umsg) { @@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("`Rotating playing status disabled.`"); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task AddPlaying(IUserMessage umsg, [Remainder] string status) { @@ -122,7 +122,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("`Added.`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ListPlaying(IUserMessage umsg) { @@ -144,7 +144,7 @@ namespace NadekoBot.Modules.Administration } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task RemovePlaying(IUserMessage umsg, int index) { diff --git a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs index 16ae221e..c127c439 100644 --- a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs @@ -5,9 +5,9 @@ using NadekoBot.Attributes; using NadekoBot.Extensions; using System; using System.Collections.Concurrent; +using System.Threading; using System.Threading.Tasks; -//todo rewrite to accept msg/sec (for example 1/5 - 1 message every 5 seconds) namespace NadekoBot.Modules.Administration { public partial class Administration @@ -15,58 +15,103 @@ namespace NadekoBot.Modules.Administration [Group] public class RatelimitCommand { - public static ConcurrentDictionary> RatelimitingChannels = new ConcurrentDictionary>(); + public static ConcurrentDictionary RatelimitingChannels = new ConcurrentDictionary(); - private static readonly TimeSpan ratelimitTime = new TimeSpan(0, 0, 0, 5); private DiscordSocketClient _client { get; } + public class Ratelimiter + { + public class RatelimitedUser + { + public ulong UserId { get; set; } + public int MessageCount { get; set; } = 0; + } + + public ulong ChannelId { get; set; } + + public int MaxMessages { get; set; } + public int PerSeconds { get; set; } + + public CancellationTokenSource cancelSource { get; set; } = new CancellationTokenSource(); + + public ConcurrentDictionary Users { get; set; } = new ConcurrentDictionary(); + + public bool CheckUserRatelimit(ulong id) + { + RatelimitedUser usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id }); + if (usr.MessageCount == MaxMessages) + { + return true; + } + else + { + usr.MessageCount++; + var t = Task.Run(async () => { + try + { + await Task.Delay(PerSeconds * 1000, cancelSource.Token); + } + catch (OperationCanceledException) { } + usr.MessageCount--; + }); + return false; + } + + } + } + public RatelimitCommand() { - this._client = NadekoBot.Client; - _client.MessageReceived += async (umsg) => + _client.MessageReceived += (umsg) => { - var usrMsg = umsg as IUserMessage; - var channel = usrMsg.Channel as ITextChannel; - - if (channel == null || await usrMsg.IsAuthor()) - return; - ConcurrentDictionary userTimePair; - if (!RatelimitingChannels.TryGetValue(channel.Id, out userTimePair)) return; - DateTime lastMessageTime; - if (userTimePair.TryGetValue(usrMsg.Author.Id, out lastMessageTime)) + var t = Task.Run(async () => { - if (DateTime.Now - lastMessageTime < ratelimitTime) - { - try - { - await usrMsg.DeleteAsync().ConfigureAwait(false); - } - catch { } + var usrMsg = umsg as IUserMessage; + var channel = usrMsg.Channel as ITextChannel; + + if (channel == null || usrMsg.IsAuthor()) return; - } - } - userTimePair.AddOrUpdate(usrMsg.Author.Id, id => DateTime.Now, (id, dt) => DateTime.Now); + Ratelimiter limiter; + if (!RatelimitingChannels.TryGetValue(channel.Id, out limiter)) + return; + + if (limiter.CheckUserRatelimit(usrMsg.Author.Id)) + await usrMsg.DeleteAsync(); + }); + return Task.CompletedTask; }; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] - public async Task Slowmode(IUserMessage umsg) + public async Task Slowmode(IUserMessage umsg, int msg = 1, int perSec = 5) { var channel = (ITextChannel)umsg.Channel; - ConcurrentDictionary throwaway; + Ratelimiter throwaway; if (RatelimitingChannels.TryRemove(channel.Id, out throwaway)) { - await channel.SendMessageAsync("Slow mode disabled.").ConfigureAwait(false); + throwaway.cancelSource.Cancel(); + await channel.SendMessageAsync("`Slow mode disabled.`").ConfigureAwait(false); return; } - if (RatelimitingChannels.TryAdd(channel.Id, new ConcurrentDictionary())) + + if (msg < 1 || perSec < 1) { - await channel.SendMessageAsync("Slow mode initiated. " + - "Users can't send more than 1 message every 5 seconds.") + await channel.SendMessageAsync("`Invalid parameters.`"); + return; + } + + if (RatelimitingChannels.TryAdd(channel.Id,throwaway = new Ratelimiter() { + ChannelId = channel.Id, + MaxMessages = msg, + PerSeconds = perSec, + })) + { + await channel.SendMessageAsync("`Slow mode initiated.` " + + $"Users can't send more than {throwaway.MaxMessages} message(s) every {throwaway.PerSeconds} second(s).") .ConfigureAwait(false); } } diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index ccb62d6e..8a05ceea 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -//todo DB + namespace NadekoBot.Modules.Administration { public partial class Administration @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Administration public class SelfAssignedRolesCommands { - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task Asar(IUserMessage umsg, [Remainder] IRole role) @@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync(msg.ToString()).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task Rsar(IUserMessage umsg, [Remainder] IRole role) @@ -70,7 +70,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync($":ok:**{role.Name}** has been removed from the list of self-assignable roles").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Lsar(IUserMessage umsg) { @@ -105,7 +105,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync(msg.ToString() + "\n\n" + removeMsg.ToString()).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageRoles)] public async Task Tesar(IUserMessage umsg) @@ -124,7 +124,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("Self assigned roles are now " + exl); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Iam(IUserMessage umsg, [Remainder] IRole role) { @@ -182,7 +182,7 @@ namespace NadekoBot.Modules.Administration } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Iamnot(IUserMessage umsg, [Remainder] IRole role) { diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index fc6052cf..ef6c11a6 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -20,7 +20,7 @@ // this._client = client; // } -// [LocalizedCommand, LocalizedDescription, LocalizedSummary] +// [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] // [RequireContext(ContextType.Guild)] // public async Task Leave(IUserMessage umsg, [Remainder] string guildStr) // { diff --git a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs index 98867f8e..53a5abd5 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs @@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Administration return Task.CompletedTask; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageGuild)] public async Task GreetDel(IUserMessage umsg) @@ -128,7 +128,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("`Automatic deletion of greet messages has been disabled.`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageGuild)] public async Task Greet(IUserMessage umsg) @@ -151,7 +151,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("Greet announcements disabled.").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageGuild)] public async Task GreetMsg(IUserMessage umsg, [Remainder] string text) @@ -180,7 +180,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("Enable greet messsages by typing `.greet`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageGuild)] public async Task GreetDm(IUserMessage umsg) @@ -202,7 +202,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("Greet announcements disabled.").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageGuild)] public async Task GreetDmMsg(IUserMessage umsg, [Remainder] string text) @@ -231,7 +231,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("Enable DM greet messsages by typing `.greetdm`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageGuild)] public async Task Bye(IUserMessage umsg) @@ -254,7 +254,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("Bye announcements disabled.").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageGuild)] public async Task ByeMsg(IUserMessage umsg, [Remainder] string text) @@ -283,7 +283,7 @@ namespace NadekoBot.Modules.Administration await channel.SendMessageAsync("Enable bye messsages by typing `.bye`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] [RequirePermission(GuildPermission.ManageGuild)] public async Task ByeDel(IUserMessage umsg) diff --git a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs deleted file mode 100644 index 7d8bab1a..00000000 --- a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs +++ /dev/null @@ -1,166 +0,0 @@ -//using Discord; -//using Discord.Commands; -//using NadekoBot.Classes; -//using NadekoBot.Extensions; -//using NadekoBot.Modules.Permissions.Classes; -//using System; -//using System.Linq; -//using System.Text.RegularExpressions; -//using System.Threading.Tasks; -//using ChPermOverride = Discord.ChannelPermissionOverrides; - -////todo DB -////todo rewrite -//namespace NadekoBot.Modules.Administration -//{ -// internal class VoicePlusTextCommand : DiscordCommand -// { -// Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); -// public VoicePlusTextCommand(DiscordModule module) : base(module) -// { -// // changing servers may cause bugs -// NadekoBot.Client.UserUpdated += async (sender, e) => -// { -// try -// { -// if (e.Server == null) -// return; -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// if (e.Before.VoiceChannel == e.After.VoiceChannel) return; -// if (!config.VoicePlusTextEnabled) -// return; -// var serverPerms = e.Server.GetUser(NadekoBot.Client.CurrentUser.Id)?.ServerPermissions; -// if (serverPerms == null) -// return; -// if (!serverPerms.Value.ManageChannels || !serverPerms.Value.ManageRoles) -// { - -// try -// { -// await e.Server.Owner.SendMessageAsync( -// "I don't have manage server and/or Manage Channels permission," + -// $" so I cannot run voice+text on **{e.Server.Name}** server.").ConfigureAwait(false); -// } -// catch { } // meh -// config.VoicePlusTextEnabled = false; -// return; -// } - - -// var beforeVch = e.Before.VoiceChannel; -// if (beforeVch != null) -// { -// var textChannel = -// e.Server.FindChannels(GetChannelName(beforeVch.Name), ChannelType.Text).FirstOrDefault(); -// if (textChannel != null) -// await textChannel.AddPermissionsRule(e.Before, -// new ChPermOverride(readMessages: PermValue.Deny, -// sendMessages: PermValue.Deny)).ConfigureAwait(false); -// } -// var afterVch = e.After.VoiceChannel; -// if (afterVch != null && e.Server.AFKChannel != afterVch) -// { -// var textChannel = e.Server.FindChannels( -// GetChannelName(afterVch.Name), -// ChannelType.Text) -// .FirstOrDefault(); -// if (textChannel == null) -// { -// textChannel = (await e.Server.CreateChannel(GetChannelName(afterVch.Name), ChannelType.Text).ConfigureAwait(false)); -// await textChannel.AddPermissionsRule(e.Server.EveryoneRole, -// new ChPermOverride(readMessages: PermValue.Deny, -// sendMessages: PermValue.Deny)).ConfigureAwait(false); -// } -// await textChannel.AddPermissionsRule(e.After, -// new ChPermOverride(readMessages: PermValue.Allow, -// sendMessages: PermValue.Allow)).ConfigureAwait(false); -// } -// } -// catch (Exception ex) -// { -// Console.WriteLine(ex); -// } -// }; -// } - -// private string GetChannelName(string voiceName) => -// channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice"; - -// internal override void Init(CommandGroupBuilder cgb) -// { -// cgb.CreateCommand(Module.Prefix + "cleanv+t") -// .Alias(Module.Prefix + "cv+t") -// .Description($"Deletes all text channels ending in `-voice` for which voicechannels are not found. **Use at your own risk.\nNeeds Manage Roles and Manage Channels Permissions.** | `{Prefix}cleanv+t`") -// .AddCheck(SimpleCheckers.CanManageRoles) -// .AddCheck(SimpleCheckers.ManageChannels()) -// .Do(async e => -// { -// if (!e.Server.CurrentUser.ServerPermissions.ManageChannels) -// { -// await channel.SendMessageAsync("`I have insufficient permission to do that.`"); -// return; -// } - -// var allTxtChannels = e.Server.TextChannels.Where(c => c.Name.EndsWith("-voice")); -// var validTxtChannelNames = e.Server.VoiceChannels.Select(c => GetChannelName(c.Name)); - -// var invalidTxtChannels = allTxtChannels.Where(c => !validTxtChannelNames.Contains(c.Name)); - -// foreach (var c in invalidTxtChannels) -// { -// try -// { -// await c.Delete(); -// } -// catch { } -// await Task.Delay(500); -// } - -// await channel.SendMessageAsync("`Done.`"); -// }); - -// cgb.CreateCommand(Module.Prefix + "voice+text") -// .Alias(Module.Prefix + "v+t") -// .Description("Creates a text channel for each voice channel only users in that voice channel can see." + -// $"If you are server owner, keep in mind you will see them all the time regardless. **Needs Manage Roles and Manage Channels Permissions.**| `{Prefix}voice+text`") -// .AddCheck(SimpleCheckers.ManageChannels()) -// .AddCheck(SimpleCheckers.CanManageRoles) -// .Do(async e => -// { -// try -// { -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// if (config.VoicePlusTextEnabled == true) -// { -// config.VoicePlusTextEnabled = false; -// foreach (var textChannel in e.Server.TextChannels.Where(c => c.Name.EndsWith("-voice"))) -// { -// try -// { -// await textChannel.Delete().ConfigureAwait(false); -// } -// catch -// { -// await channel.SendMessageAsync( -// ":anger: Error: Most likely i don't have permissions to do this.") -// .ConfigureAwait(false); -// return; -// } -// } -// await channel.SendMessageAsync("Successfuly removed voice + text feature.").ConfigureAwait(false); -// return; -// } -// config.VoicePlusTextEnabled = true; -// await channel.SendMessageAsync("Successfuly enabled voice + text feature. " + -// "**Make sure the bot has manage roles and manage channels permissions**") -// .ConfigureAwait(false); - -// } -// catch (Exception ex) -// { -// await channel.SendMessageAsync(ex.ToString()).ConfigureAwait(false); -// } -// }); -// } -// } -//} diff --git a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs new file mode 100644 index 00000000..75f4d649 --- /dev/null +++ b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs @@ -0,0 +1,170 @@ +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Services; +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace NadekoBot.Modules.Administration +{ + public partial class Administration + { + [Group] + public class VoicePlusTextCommands + { + Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); + //guildid/voiceplustextenabled + private ConcurrentDictionary voicePlusTextCache; + public VoicePlusTextCommands() + { + NadekoBot.Client.UserUpdated += UserUpdatedEventHandler; + voicePlusTextCache = new ConcurrentDictionary(); + } + + private Task UserUpdatedEventHandler(IGuildUser before, IGuildUser after) + { + var task = Task.Run(async () => + { + var guild = before.Guild ?? after.Guild; + var botUserPerms = guild.GetCurrentUser().GuildPermissions; + try + { + if (before.VoiceChannel == after.VoiceChannel) return; + + bool isEnabled; + voicePlusTextCache.TryGetValue(guild.Id, out isEnabled); + if (!isEnabled) + return; + + if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles) + { + try + { + await (await guild.GetOwnerAsync()).SendMessageAsync( + "I don't have manage server and/or Manage Channels permission," + + $" so I cannot run voice+text on **{guild.Name}** server.").ConfigureAwait(false); + } + catch { } + using (var uow = DbHandler.UnitOfWork()) + { + uow.GuildConfigs.For(before.Guild.Id).VoicePlusTextEnabled = false; + voicePlusTextCache.TryUpdate(guild.Id, false, true); + } + return; + } + + + var beforeVch = before.VoiceChannel; + if (beforeVch != null) + { + var textChannel = guild.GetTextChannels().Where(t => t.Name == GetChannelName(beforeVch.Name)).FirstOrDefault(); + if (textChannel != null) + await textChannel.AddPermissionOverwriteAsync(before, + new OverwritePermissions(readMessages: PermValue.Deny, + sendMessages: PermValue.Deny)).ConfigureAwait(false); + } + var afterVch = after.VoiceChannel; + if (afterVch != null && guild.AFKChannelId != afterVch.Id) + { + var textChannel = guild.GetTextChannels() + .Where(t => t.Name == GetChannelName(afterVch.Name)) + .FirstOrDefault(); + if (textChannel == null) + { + textChannel = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name)).ConfigureAwait(false)); + await textChannel.AddPermissionOverwriteAsync(guild.EveryoneRole, + new OverwritePermissions(readMessages: PermValue.Deny, + sendMessages: PermValue.Deny)).ConfigureAwait(false); + } + await textChannel.AddPermissionOverwriteAsync(after, + new OverwritePermissions(readMessages: PermValue.Allow, + sendMessages: PermValue.Allow)).ConfigureAwait(false); + } + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + }); + return Task.CompletedTask; + } + + private string GetChannelName(string voiceName) => + channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice"; + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageRoles)] + [RequirePermission(GuildPermission.ManageChannels)] + public async Task VoicePlusText(IUserMessage msg, [Remainder] string arg) + { + var channel = (ITextChannel)msg.Channel; + var guild = channel.Guild; + + var botUser = guild.GetCurrentUser(); + if (!botUser.GuildPermissions.ManageRoles || !botUser.GuildPermissions.ManageChannels) + { + await channel.SendMessageAsync(":anger: `I require manage roles and manage channels permissions to enable this feature.`"); + return; + } + try + { + bool isEnabled; + using (var uow = DbHandler.UnitOfWork()) + { + var conf = uow.GuildConfigs.For(guild.Id); + isEnabled = conf.VoicePlusTextEnabled = !conf.VoicePlusTextEnabled; + } + voicePlusTextCache.AddOrUpdate(guild.Id, isEnabled, (id, val) => isEnabled); + if (isEnabled) + { + foreach (var textChannel in guild.GetTextChannels().Where(c => c.Name.EndsWith("-voice"))) + { + try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { } + } + await channel.SendMessageAsync("Successfuly removed voice + text feature.").ConfigureAwait(false); + return; + } + await channel.SendMessageAsync("Successfuly enabled voice + text feature.").ConfigureAwait(false); + + } + catch (Exception ex) + { + await channel.SendMessageAsync(ex.ToString()).ConfigureAwait(false); + } + } + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageChannels)] + [RequirePermission(GuildPermission.ManageRoles)] + public async Task CleanVPlusT(IUserMessage msg, [Remainder] string arg) + { + var channel = (ITextChannel)msg.Channel; + var guild = channel.Guild; + if (!guild.GetCurrentUser().GuildPermissions.ManageChannels) + { + await channel.SendMessageAsync("`I have insufficient permission to do that.`"); + return; + } + + var allTxtChannels = guild.GetTextChannels().Where(c => c.Name.EndsWith("-voice")); + var validTxtChannelNames = guild.GetVoiceChannels().Select(c => GetChannelName(c.Name)); + + var invalidTxtChannels = allTxtChannels.Where(c => !validTxtChannelNames.Contains(c.Name)); + + foreach (var c in invalidTxtChannels) + { + try { await c.DeleteAsync().ConfigureAwait(false); } catch { } + await Task.Delay(500); + } + + await channel.SendMessageAsync("`Done.`"); + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 0cbc22b8..542946cd 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -12,10 +12,9 @@ using NadekoBot.Services.Database.Models; using System.Linq; using NadekoBot.Services.Database; -//todo DB namespace NadekoBot.Modules.ClashOfClans { - [Module(",", AppendSpace = false)] + [NadekoModule("ClashOfClans", ",")] public class ClashOfClans : DiscordModule { public static ConcurrentDictionary> ClashWars { get; set; } = new ConcurrentDictionary>(); @@ -55,7 +54,7 @@ namespace NadekoBot.Modules.ClashOfClans } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task CreateWar(IUserMessage umsg, int size, [Remainder] string enemyClan = null) { @@ -88,7 +87,7 @@ namespace NadekoBot.Modules.ClashOfClans await channel.SendMessageAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task StartWar(IUserMessage umsg, [Remainder] string number = null) { @@ -116,7 +115,7 @@ namespace NadekoBot.Modules.ClashOfClans SaveWar(war); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ListWar(IUserMessage umsg, [Remainder] string number = null) { @@ -159,7 +158,7 @@ namespace NadekoBot.Modules.ClashOfClans await channel.SendMessageAsync(warsInfo.Item1[warsInfo.Item2].ToPrettyString()).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Claim(IUserMessage umsg, int number, int baseNumber, [Remainder] string other_name = null) { @@ -187,7 +186,7 @@ namespace NadekoBot.Modules.ClashOfClans } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ClaimFinish1(IUserMessage umsg, int number, int baseNumber, [Remainder] string other_name = null) { @@ -195,7 +194,7 @@ namespace NadekoBot.Modules.ClashOfClans await FinishClaim(umsg, number, baseNumber, other_name, 1); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ClaimFinish2(IUserMessage umsg, int number, int baseNumber, [Remainder] string other_name = null) { @@ -203,7 +202,7 @@ namespace NadekoBot.Modules.ClashOfClans await FinishClaim(umsg, number, baseNumber, other_name, 2); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ClaimFinish(IUserMessage umsg, int number, int baseNumber, [Remainder] string other_name = null) { @@ -211,7 +210,7 @@ namespace NadekoBot.Modules.ClashOfClans await FinishClaim(umsg, number, baseNumber, other_name); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task EndWar(IUserMessage umsg, int number) { @@ -232,7 +231,7 @@ namespace NadekoBot.Modules.ClashOfClans warsInfo.Item1.RemoveAt(warsInfo.Item2); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Unclaim(IUserMessage umsg, int number, [Remainder] string otherName = null) { diff --git a/src/NadekoBot/Modules/DiscordModule.cs b/src/NadekoBot/Modules/DiscordModule.cs index 47fdfdf9..f3f23adc 100644 --- a/src/NadekoBot/Modules/DiscordModule.cs +++ b/src/NadekoBot/Modules/DiscordModule.cs @@ -8,10 +8,10 @@ namespace NadekoBot.Modules { public class DiscordModule { - protected ILocalization _l; - protected CommandService _commands; - protected DiscordSocketClient _client; - protected Logger _log; + protected ILocalization _l { get; } + protected CommandService _commands { get; } + protected DiscordSocketClient _client { get; } + protected Logger _log { get; } public DiscordModule(ILocalization loc, CommandService cmds, DiscordSocketClient client) { diff --git a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs index 79f496ec..c511e731 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Gambling { public static ConcurrentDictionary AnimalRaces = new ConcurrentDictionary(); - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Race(IUserMessage umsg) { @@ -32,7 +32,7 @@ namespace NadekoBot.Modules.Gambling await channel.SendMessageAsync("🏁 `Failed starting a race. Another race is probably running.`"); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task JoinRace(IUserMessage umsg, int amount = 0) { @@ -41,17 +41,10 @@ namespace NadekoBot.Modules.Gambling if (amount < 0) amount = 0; - //todo DB - //var userFlowers = Gambling.GetUserFlowers(umsg.Author.Id); + if (amount > 0) + if(!await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)umsg.Author, "BetRace", amount, true).ConfigureAwait(false)) + await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyName}s.").ConfigureAwait(false); - //if (userFlowers < amount) - //{ - // await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); - // return; - //} - - //if (amount > 0) - // await FlowersHandler.RemoveFlowers(umsg.Author, "BetRace", (int)amount, true).ConfigureAwait(false); AnimalRace ar; if (!AnimalRaces.TryGetValue(channel.Guild.Id, out ar)) @@ -67,7 +60,7 @@ namespace NadekoBot.Modules.Gambling private ConcurrentQueue animals { get; } - public bool Fail { get; internal set; } + public bool Fail { get; set; } public List participants = new List(); private ulong serverId; @@ -116,9 +109,9 @@ namespace NadekoBot.Modules.Gambling { await raceChannel.SendMessageAsync("🏁`Race failed to start since there was not enough participants.`"); var p = participants.FirstOrDefault(); - //todo DB - //if (p != null) - // await FlowersHandler.AddFlowersAsync(p.User, "BetRace", p.AmountBet, true).ConfigureAwait(false); + + if (p != null) + await CurrencyHandler.AddCurrencyAsync(p.User, "BetRace", p.AmountBet, true).ConfigureAwait(false); End(); return; } @@ -137,7 +130,7 @@ namespace NadekoBot.Modules.Gambling private async Task StartRace() { - var rng = new Random(); + var rng = new NadekoRandom(); Participant winner = null; IUserMessage msg = null; int place = 1; @@ -150,7 +143,6 @@ namespace NadekoBot.Modules.Gambling //update the state participants.ForEach(p => { - p.Total += 1 + rng.Next(0, 10); if (p.Total > 60) { @@ -191,8 +183,8 @@ namespace NadekoBot.Modules.Gambling if (winner.AmountBet > 0) { var wonAmount = winner.AmountBet * (participants.Count - 1); - //todo DB - //await FlowersHandler.AddFlowersAsync(winner.User, "Won a Race", wonAmount).ConfigureAwait(false); + + await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, false).ConfigureAwait(false); await raceChannel.SendMessageAsync($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false); } else @@ -202,14 +194,15 @@ namespace NadekoBot.Modules.Gambling } - private async Task Client_MessageReceived(IMessage imsg) + private Task Client_MessageReceived(IMessage imsg) { var msg = imsg as IUserMessage; if (msg == null) - return; - if (await msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel) - return; + return Task.CompletedTask; + if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel) + return Task.CompletedTask; messagesSinceGameStarted++; + return Task.CompletedTask; } private async Task CheckForFullGameAsync(CancellationToken cancelToken) diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 9741fa97..d6ecfa5e 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -2,6 +2,7 @@ using Discord; using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; +using NadekoBot.Services; using System; using System.Collections.Generic; using System.Linq; @@ -13,99 +14,100 @@ namespace NadekoBot.Modules.Gambling public partial class Gambling { private Regex dndRegex { get; } = new Regex(@"(?\d+)d(?\d+)", RegexOptions.Compiled); - ////todo drawing - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public Task Roll(IUserMessage umsg, [Remainder] string arg = null) => - // InternalRoll(umsg, arg, true); - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public Task Rolluo(IUserMessage umsg, [Remainder] string arg = null) => - // InternalRoll(umsg, arg, false); + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public Task Roll(IUserMessage umsg, [Remainder] string arg = null) => + publicRoll(umsg, arg, true); - //private async Task InternalRoll(IUserMessage umsg, string arg, bool ordered) { - // var channel = (ITextChannel)umsg.Channel; - // var r = new Random(); - // if (string.IsNullOrWhiteSpace(arg)) - // { - // var gen = r.Next(0, 101); + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public Task Rolluo(IUserMessage umsg, [Remainder] string arg = null) => + publicRoll(umsg, arg, false); + //todo drawing + private async Task publicRoll(IUserMessage umsg, string arg, bool ordered) + { + var channel = (ITextChannel)umsg.Channel; + var r = new NadekoRandom(); + //if (string.IsNullOrWhiteSpace(arg)) + //{ + // var gen = r.Next(0, 101); - // var num1 = gen / 10; - // var num2 = gen % 10; + // var num1 = gen / 10; + // var num2 = gen % 10; - // var imageStream = await new Image[2] { GetDice(num1), GetDice(num2) }.Merge().ToStream(ImageFormat.Png); + // var imageStream = await new Image[2] { GetDice(num1), GetDice(num2) }.Merge().ToStream(ImageFormat.Png); - // await channel.SendFileAsync(imageStream, "dice.png").ConfigureAwait(false); - // return; - // } - // Match m; - // if ((m = dndRegex.Match(arg)).Length != 0) - // { - // int n1; - // int n2; - // if (int.TryParse(m.Groups["n1"].ToString(), out n1) && - // int.TryParse(m.Groups["n2"].ToString(), out n2) && - // n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0) - // { - // var arr = new int[n1]; - // for (int i = 0; i < n1; i++) - // { - // arr[i] = r.Next(1, n2 + 1); - // } - // var elemCnt = 0; - // await channel.SendMessageAsync($"`Rolled {n1} {(n1 == 1 ? "die" : "dice")} 1-{n2}.`\n`Result:` " + string.Join(", ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => elemCnt++ % 2 == 0 ? $"**{x}**" : x.ToString()))).ConfigureAwait(false); - // } - // return; - // } - // try - // { - // var num = int.Parse(e.Args[0]); - // if (num < 1) num = 1; - // if (num > 30) - // { - // await channel.SendMessageAsync("You can roll up to 30 dice at a time.").ConfigureAwait(false); - // num = 30; - // } - // var dices = new List(num); - // var values = new List(num); - // for (var i = 0; i < num; i++) - // { - // var randomNumber = r.Next(1, 7); - // var toInsert = dices.Count; - // if (ordered) - // { - // if (randomNumber == 6 || dices.Count == 0) - // toInsert = 0; - // else if (randomNumber != 1) - // for (var j = 0; j < dices.Count; j++) - // { - // if (values[j] < randomNumber) - // { - // toInsert = j; - // break; - // } - // } - // } - // else - // { - // toInsert = dices.Count; - // } - // dices.Insert(toInsert, GetDice(randomNumber)); - // values.Insert(toInsert, randomNumber); - // } + // await channel.SendFileAsync(imageStream, "dice.png").ConfigureAwait(false); + // return; + //} + Match m; + if ((m = dndRegex.Match(arg)).Length != 0) + { + int n1; + int n2; + if (int.TryParse(m.Groups["n1"].ToString(), out n1) && + int.TryParse(m.Groups["n2"].ToString(), out n2) && + n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0) + { + var arr = new int[n1]; + for (int i = 0; i < n1; i++) + { + arr[i] = r.Next(1, n2 + 1); + } + var elemCnt = 0; + await channel.SendMessageAsync($"`Rolled {n1} {(n1 == 1 ? "die" : "dice")} 1-{n2}.`\n`Result:` " + string.Join(", ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => elemCnt++ % 2 == 0 ? $"**{x}**" : x.ToString()))).ConfigureAwait(false); + } + return; + } + //try + //{ + // var num = int.Parse(e.Args[0]); + // if (num < 1) num = 1; + // if (num > 30) + // { + // await channel.SendMessageAsync("You can roll up to 30 dice at a time.").ConfigureAwait(false); + // num = 30; + // } + // var dices = new List(num); + // var values = new List(num); + // for (var i = 0; i < num; i++) + // { + // var randomNumber = r.Next(1, 7); + // var toInsert = dices.Count; + // if (ordered) + // { + // if (randomNumber == 6 || dices.Count == 0) + // toInsert = 0; + // else if (randomNumber != 1) + // for (var j = 0; j < dices.Count; j++) + // { + // if (values[j] < randomNumber) + // { + // toInsert = j; + // break; + // } + // } + // } + // else + // { + // toInsert = dices.Count; + // } + // dices.Insert(toInsert, GetDice(randomNumber)); + // values.Insert(toInsert, randomNumber); + // } - // var bitmap = dices.Merge(); - // await channel.SendMessageAsync(values.Count + " Dice rolled. Total: **" + values.Sum() + "** Average: **" + (values.Sum() / (1.0f * values.Count)).ToString("N2") + "**").ConfigureAwait(false); - // await channel.SendFileAsync("dice.png", bitmap.ToStream(ImageFormat.Png)).ConfigureAwait(false); - // } - // catch - // { - // await channel.SendMessageAsync("Please enter a number of dice to roll.").ConfigureAwait(false); - // } - //} + // var bitmap = dices.Merge(); + // await channel.SendMessageAsync(values.Count + " Dice rolled. Total: **" + values.Sum() + "** Average: **" + (values.Sum() / (1.0f * values.Count)).ToString("N2") + "**").ConfigureAwait(false); + // await channel.SendFileAsync("dice.png", bitmap.ToStream(ImageFormat.Png)).ConfigureAwait(false); + //} + //catch + //{ + // await channel.SendMessageAsync("Please enter a number of dice to roll.").ConfigureAwait(false); + //} + } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task NRoll(IUserMessage umsg, [Remainder] string range) { @@ -123,11 +125,11 @@ namespace NadekoBot.Modules.Gambling .ToArray(); if (arr[0] > arr[1]) throw new ArgumentException("First argument should be bigger than the second one."); - rolled = new Random().Next(arr[0], arr[1] + 1); + rolled = new NadekoRandom().Next(arr[0], arr[1] + 1); } else { - rolled = new Random().Next(0, int.Parse(range) + 1); + rolled = new NadekoRandom().Next(0, int.Parse(range) + 1); } await channel.SendMessageAsync($"{umsg.Author.Mention} rolled **{rolled}**.").ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs index 099649f6..17c5033b 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs @@ -11,11 +11,11 @@ ////todo drawing //namespace NadekoBot.Modules.Gambling //{ -// internal class DrawCommand : DiscordCommand +// public class DrawCommand : DiscordCommand // { // public DrawCommand(DiscordModule module) : base(module) { } -// internal override void Init(CommandGroupBuilder cgb) +// public override void Init(CommandGroupBuilder cgb) // { // cgb.CreateCommand(Module.Prefix + "draw") // .Description($"Draws a card from the deck.If you supply number [x], she draws up to 5 cards from the deck. | `{Prefix}draw [x]`") diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index 9f30701f..7e5eaa71 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -1,113 +1,93 @@ -//using Discord.Commands; -//using NadekoBot.Classes; -//using NadekoBot.Extensions; -//using System; -//using System.Drawing; -//using System.Threading.Tasks; +using Discord; +using Discord.Commands; -////todo drawing -//namespace NadekoBot.Modules.Gambling -//{ -// internal class FlipCoinCommand : DiscordCommand -// { +//todo drawing +namespace NadekoBot.Modules.Gambling +{ + [Group] + public class FlipCoinCommands + { -// public FlipCoinCommand(DiscordModule module) : base(module) { } - -// internal override void Init(CommandGroupBuilder cgb) -// { -// cgb.CreateCommand(Module.Prefix + "flip") -// .Description($"Flips coin(s) - heads or tails, and shows an image. | `{Prefix}flip` or `{Prefix}flip 3`") -// .Parameter("count", ParameterType.Optional) -// .Do(FlipCoinFunc()); - -// cgb.CreateCommand(Module.Prefix + "betflip") -// .Alias(Prefix+"bf") -// .Description($"Bet to guess will the result be heads or tails. Guessing award you double flowers you've bet. | `{Prefix}bf 5 heads` or `{Prefix}bf 3 t`") -// .Parameter("amount", ParameterType.Required) -// .Parameter("guess", ParameterType.Required) -// .Do(BetFlipCoinFunc()); -// } + public FlipCoinCommands() { } + ////todo drawing + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[RequireContext(ContextType.Guild)] + //public async Task Flip(IUserMessage imsg, int count = 0) + //{ + // var channel = (ITextChannel)imsg.Channel; + // if (count == 0) + // { + // if (rng.Next(0, 2) == 1) + // await channel.SendFileAsync("heads.png", ).ConfigureAwait(false); + // else + // await channel.SendFileAsync("tails.png", ).ConfigureAwait(false); + // return; + // } + // if (result > 10) + // result = 10; + // var imgs = new Image[result]; + // for (var i = 0; i < result; i++) + // { + // imgs[i] = rng.Next(0, 2) == 0 ? + // Properties.Resources.tails : + // Properties.Resources.heads; + // } + // await channel.SendFile($"{result} coins.png", imgs.Merge().ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); + // return; + // await channel.SendMessageAsync("Invalid number").ConfigureAwait(false); + //} -// private readonly Random rng = new Random(); -// public Func BetFlipCoinFunc() => async e => -// { + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[RequireContext(ContextType.Guild)] + //public async Task Betflip(IUserMessage umsg, int amount, string guess) + //{ + // var channel = (ITextChannel)umsg.Channel; + // var guildUser = (IGuildUser)umsg.Author; + // var guessStr = guess.Trim().ToUpperInvariant(); + // if (guessStr != "H" && guessStr != "T" && guessStr != "HEADS" && guessStr != "TAILS") + // return; + + // if (amount < 1) + // return; -// var amountstr = amount.Trim(); + // var userFlowers = Gambling.GetUserFlowers(umsg.Author.Id); -// var guessStr = guess.Trim().ToUpperInvariant(); -// if (guessStr != "H" && guessStr != "T" && guessStr != "HEADS" && guessStr != "TAILS") -// return; + // if (userFlowers < amount) + // { + // await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyName}s. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false); + // return; + // } -// int amount; -// if (!int.TryParse(amountstr, out amount) || amount < 1) -// return; + // await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betflip Gamble", amount, false).ConfigureAwait(false); + // //heads = true + // //tails = false -// var userFlowers = Gambling.GetUserFlowers(umsg.Author.Id); + // var isHeads = guessStr == "HEADS" || guessStr == "H"; + // bool result = false; + // var rng = new NadekoRandom(); + // if (rng.Next(0, 2) == 1) + // { + // await channel.SendFileAsync("heads.png", Properties.Resources.heads.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); + // result = true; + // } + // else + // { + // await channel.SendFileAsync("tails.png", Properties.Resources.tails.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); + // } -// if (userFlowers < amount) -// { -// await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); -// return; -// } + // string str; + // if (isHeads == result) + // { + // str = $"{umsg.Author.Mention}`You guessed it!` You won {amount * 2}{Gambling.CurrencySign}"; + // await CurrencyHandler.AddCurrencyAsync((IGuildUser)umsg.Author, "Betflip Gamble", amount * 2, false).ConfigureAwait(false); -// await FlowersHandler.RemoveFlowers(umsg.Author, "Betflip Gamble", (int)amount, true).ConfigureAwait(false); -// //heads = true -// //tails = false + // } + // else + // str = $"{umsg.Author.Mention}`More luck next time.`"; -// var guess = guessStr == "HEADS" || guessStr == "H"; -// bool result = false; -// if (rng.Next(0, 2) == 1) { -// await e.Channel.SendFile("heads.png", Properties.Resources.heads.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// result = true; -// } -// else { -// await e.Channel.SendFile("tails.png", Properties.Resources.tails.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// } - -// string str; -// if (guess == result) -// { -// str = $"{umsg.Author.Mention}`You guessed it!` You won {amount * 2}{NadekoBot.Config.CurrencySign}"; -// await FlowersHandler.AddFlowersAsync(umsg.Author, "Betflip Gamble", amount * 2, true).ConfigureAwait(false); - -// } -// else -// str = $"{umsg.Author.Mention}`More luck next time.`"; - -// await channel.SendMessageAsync(str).ConfigureAwait(false); -// }; - -// public Func FlipCoinFunc() => async e => -// { - -// if (count == "") -// { -// if (rng.Next(0, 2) == 1) -// await e.Channel.SendFile("heads.png", Properties.Resources.heads.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// else -// await e.Channel.SendFile("tails.png", Properties.Resources.tails.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// } -// else -// { -// int result; -// if (int.TryParse(count, out result)) -// { -// if (result > 10) -// result = 10; -// var imgs = new Image[result]; -// for (var i = 0; i < result; i++) -// { -// imgs[i] = rng.Next(0, 2) == 0 ? -// Properties.Resources.tails : -// Properties.Resources.heads; -// } -// await e.Channel.SendFile($"{result} coins.png", imgs.Merge().ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); -// return; -// } -// await channel.SendMessageAsync("Invalid number").ConfigureAwait(false); -// } -// }; -// } -//} + // await channel.SendMessageAsync(str).ConfigureAwait(false); + //} + } +} diff --git a/src/NadekoBot/Modules/Gambling/Commands/Models/Cards.cs b/src/NadekoBot/Modules/Gambling/Commands/Models/Cards.cs index 5517d5d1..2c2624c6 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/Models/Cards.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/Models/Cards.cs @@ -1,4 +1,6 @@ -using System; +using NadekoBot.Extensions; +using NadekoBot.Services; +using System; using System.Collections.Generic; using System.Linq; @@ -114,7 +116,7 @@ namespace NadekoBot.Modules.Gambling.Models } } } - private Random r = new Random(); + private Random r = new NadekoRandom(); /// /// Take a card from the pool, you either take it from the top if the deck is shuffled, or from a random place if the deck is in the default order. /// @@ -143,7 +145,7 @@ namespace NadekoBot.Modules.Gambling.Models private void Shuffle() { if (cardPool.Count <= 1) return; - var orderedPool = cardPool.OrderBy(x => r.Next()); + var orderedPool = cardPool.Shuffle(); cardPool = cardPool as List ?? orderedPool.ToList(); } public override string ToString() => string.Concat(cardPool.Select(c => c.ToString())) + Environment.NewLine; diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 87a07fab..4585a820 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -9,11 +9,12 @@ using System.Threading.Tasks; using NadekoBot.Services; using Discord.WebSocket; using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using System.Collections.Generic; -//todo DB namespace NadekoBot.Modules.Gambling { - [Module("$", AppendSpace = false)] + [NadekoModule("Gambling", "$")] public partial class Gambling : DiscordModule { public static string CurrencyName { get; set; } @@ -30,10 +31,9 @@ namespace NadekoBot.Modules.Gambling CurrencySign = conf.CurrencySign; CurrencyPluralName = conf.CurrencyPluralName; } - } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Raffle(IUserMessage umsg, [Remainder] IRole role = null) { @@ -43,55 +43,52 @@ namespace NadekoBot.Modules.Gambling var members = role.Members().Where(u => u.Status == UserStatus.Online); var membersArray = members as IUser[] ?? members.ToArray(); - var usr = membersArray[new Random().Next(0, membersArray.Length)]; + var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)]; await channel.SendMessageAsync($"**Raffled user:** {usr.Username} (id: {usr.Id})").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Cash(IUserMessage umsg, [Remainder] IUser user = null) + { + var channel = (ITextChannel)umsg.Channel; + user = user ?? umsg.Author; + long amount; + BotConfig config; + using (var uow = DbHandler.UnitOfWork()) + { + amount = uow.Currency.GetUserCurrency(user.Id); + config = uow.BotConfig.GetOrCreate(); + } + + await channel.SendMessageAsync($"{user.Username} has {amount} {config.CurrencySign}").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Give(IUserMessage umsg, long amount, [Remainder] IGuildUser receiver) + { + var channel = (ITextChannel)umsg.Channel; + if (amount <= 0) + return; + var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)umsg.Author, $"Gift to {receiver.Username} ({receiver.Id}).", amount, true).ConfigureAwait(false); + if (!success) + { + await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyPluralName}.").ConfigureAwait(false); + return; + } + await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {umsg.Author.Username} ({umsg.Author.Id}).", amount, true).ConfigureAwait(false); + await channel.SendMessageAsync($"{umsg.Author.Mention} successfully sent {amount} {Gambling.CurrencyPluralName}s to {receiver.Mention}!").ConfigureAwait(false); } - ////todo DB - //[LocalizedCommand("$$$"), LocalizedDescription("$$$"), LocalizedSummary("$$$")] - //[RequireContext(ContextType.Guild)] - //public async Task Cash(IUserMessage umsg, [Remainder] string arg) - //{ - // var channel = (ITextChannel)umsg.Channel; - - // var usr = e.Message.MentionedUsers.FirstOrDefault() ?? umsg.Author; - // var pts = GetUserFlowers(usr.Id); - // var str = $"{usr.Name} has {pts} {NadekoBot.Config.CurrencySign}"; - // await channel.SendMessageAsync(str).ConfigureAwait(false); - //} - - ////todo DB - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public async Task Give(IUserMessage umsg, long amount, [Remainder] IUser receiver) - //{ - // var channel = (ITextChannel)umsg.Channel; - // if (amount <= 0) - // return; - // var userFlowers = GetUserFlowers(umsg.Author.Id); - - // if (userFlowers < amount) - // { - // await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); - // return; - // } - - // await FlowersHandler.RemoveFlowers(umsg.Author, "Gift", (int)amount, true).ConfigureAwait(false); - // await FlowersHandler.AddFlowersAsync(receiver, "Gift", (int)amount).ConfigureAwait(false); - - // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully sent {amount} {NadekoBot.Config.CurrencyName}s to {receiver.Mention}!").ConfigureAwait(false); - - //} - - ////todo DB ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public Task Award(IUserMessage umsg, long amount, [Remainder] IGuildUser usr) => // Award(umsg, amount, usr.Id); - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Award(IUserMessage umsg, long amount, [Remainder] ulong usrId) //{ @@ -100,19 +97,19 @@ namespace NadekoBot.Modules.Gambling // if (amount <= 0) // return; - // await FlowersHandler.AddFlowersAsync(usrId, $"Awarded by bot owner. ({umsg.Author.Username}/{umsg.Author.Id})", (int)amount).ConfigureAwait(false); + // await CurrencyHandler.AddFlowersAsync(usrId, $"Awarded by bot owner. ({umsg.Author.Username}/{umsg.Author.Id})", (int)amount).ConfigureAwait(false); - // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully awarded {amount} {NadekoBot.Config.CurrencyName}s to <@{usrId}>!").ConfigureAwait(false); + // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully awarded {amount} {Gambling.CurrencyName}s to <@{usrId}>!").ConfigureAwait(false); //} ////todo owner only - ////todo DB - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public Task Take(IUserMessage umsg, long amount, [Remainder] IGuildUser user) => // Take(umsg, amount, user.Id); - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //todo owner only + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Take(IUserMessage umsg, long amount, [Remainder] ulong usrId) //{ @@ -120,76 +117,84 @@ namespace NadekoBot.Modules.Gambling // if (amount <= 0) // return; - // await FlowersHandler.RemoveFlowers(usrId, $"Taken by bot owner.({umsg.Author.Username}/{umsg.Author.Id})", (int)amount).ConfigureAwait(false); + // await CurrencyHandler.RemoveFlowers(usrId, $"Taken by bot owner.({umsg.Author.Username}/{umsg.Author.Id})", (int)amount).ConfigureAwait(false); - // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully took {amount} {NadekoBot.Config.CurrencyName}s from <@{usrId}>!").ConfigureAwait(false); + // await channel.SendMessageAsync($"{umsg.Author.Mention} successfully took {amount} {Gambling.CurrencyName}s from <@{usrId}>!").ConfigureAwait(false); //} - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public async Task BetRoll(IUserMessage umsg, int amount) - //{ - // var channel = (ITextChannel)umsg.Channel; + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task BetRoll(IUserMessage umsg, long amount) + { + var channel = (ITextChannel)umsg.Channel; - // if (amount < 1) - // return; + if (amount < 1) + return; - // var userFlowers = GetUserFlowers(umsg.Author.Id); + var guildUser = (IGuildUser)umsg.Author; - // if (userFlowers < amount) - // { - // await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); - // return; - // } + long userFlowers; + using (var uow = DbHandler.UnitOfWork()) + { + userFlowers = uow.Currency.GetOrCreate(umsg.Author.Id).Amount; + } - // await FlowersHandler.RemoveFlowers(umsg.Author, "Betroll Gamble", (int)amount, true).ConfigureAwait(false); + if (userFlowers < amount) + { + await channel.SendMessageAsync($"{guildUser.Mention} You don't have enough {Gambling.CurrencyPluralName}. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false); + return; + } - // var rng = new Random().Next(0, 101); - // var str = $"{umsg.Author.Mention} `You rolled {rng}.` "; - // if (rng < 67) - // { - // str += "Better luck next time."; - // } - // else if (rng < 90) - // { - // str += $"Congratulations! You won {amount * 2}{NadekoBot.Config.CurrencySign} for rolling above 66"; - // await FlowersHandler.AddFlowersAsync(umsg.Author, "Betroll Gamble", amount * 2, true).ConfigureAwait(false); - // } - // else if (rng < 100) - // { - // str += $"Congratulations! You won {amount * 3}{NadekoBot.Config.CurrencySign} for rolling above 90."; - // await FlowersHandler.AddFlowersAsync(umsg.Author, "Betroll Gamble", amount * 3, true).ConfigureAwait(false); - // } - // else - // { - // str += $"👑 Congratulations! You won {amount * 10}{NadekoBot.Config.CurrencySign} for rolling **100**. 👑"; - // await FlowersHandler.AddFlowersAsync(umsg.Author, "Betroll Gamble", amount * 10, true).ConfigureAwait(false); - // } + await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betroll Gamble", amount, false).ConfigureAwait(false); - // await channel.SendMessageAsync(str).ConfigureAwait(false); - //} + var rng = new NadekoRandom().Next(0, 101); + var str = $"{guildUser.Mention} `You rolled {rng}.` "; + if (rng < 67) + { + str += "More luck next time."; + } + else if (rng < 90) + { + str += $"Congratulations! You won {amount * 2}{Gambling.CurrencySign} for rolling above 66"; + await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 2, false).ConfigureAwait(false); + } + else if (rng < 100) + { + str += $"Congratulations! You won {amount * 3}{Gambling.CurrencySign} for rolling above 90."; + await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 3, false).ConfigureAwait(false); + } + else + { + str += $"👑 Congratulations! You won {amount * 10}{Gambling.CurrencySign} for rolling **100**. 👑"; + await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 10, false).ConfigureAwait(false); + } - ////todo DB -// [LocalizedCommand, LocalizedDescription, LocalizedSummary] -// [RequireContext(ContextType.Guild)] -// public async Task Leaderboard(IUserMessage umsg) -// { -// var channel = (ITextChannel)umsg.Channel; + await channel.SendMessageAsync(str).ConfigureAwait(false); + } -// var richestTemp = DbHandler.Instance.GetTopRichest(); -// var richest = richestTemp as CurrencyState[] ?? richestTemp.ToArray(); -// if (richest.Length == 0) -// return; -// await channel.SendMessageAsync( -// richest.Aggregate(new StringBuilder( -//$@"```xl -//┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓ -//┃ Id ┃ $$$ ┃ -//"), -// (cur, cs) => cur.AppendLine( -//$@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━┫ -//┃{(e.Server.Users.Where(u => u.Id == (ulong)cs.UserId).FirstOrDefault()?.Name.TrimTo(18, true) ?? cs.UserId.ToString()),-20} ┃ {cs.Value,5} ┃") -// ).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━┛```").ConfigureAwait(false); - //} + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Leaderboard(IUserMessage umsg) + { + var channel = (ITextChannel)umsg.Channel; + + IEnumerable richest; + using (var uow = DbHandler.UnitOfWork()) + { + richest = uow.Currency.GetTopRichest(10); + } + if (!richest.Any()) + return; + await channel.SendMessageAsync( + richest.Aggregate(new StringBuilder( +$@"```xl + ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓ + ┃ Id ┃ $$$ ┃ + "), + (cur, cs) => cur.AppendLine( +$@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━┫ + ┃{(channel.Guild.GetUser(cs.UserId)?.Username.TrimTo(18, true) ?? cs.UserId.ToString()),-20} ┃ {cs,5} ┃") + ).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━┛```").ConfigureAwait(false); + } } } diff --git a/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs b/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs index 26408cd2..63e4ca54 100644 --- a/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs @@ -10,9 +10,9 @@ using System.Threading.Tasks; // because i don't want to waste my time on this cancerous command namespace NadekoBot.Modules.Games { - public partial class GamesModule + public partial class Games { - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Leet(IUserMessage umsg, int level, [Remainder] string text = null) { diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 6b144495..53d3c01b 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -1,168 +1,220 @@ -//using Discord; -//using Discord.Commands; -//using NadekoBot.Classes; -//using NadekoBot.Extensions; -//using NadekoBot.Modules.Permissions.Classes; -//using System; -//using System.Collections.Concurrent; -//using System.Collections.Generic; -//using System.IO; -//using System.Linq; -//using System.Security.Cryptography; -//using System.Threading; -//using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Services; +using NadekoBot.Services.Database; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; -////todo DI into partials -////todo DB -//namespace NadekoBot.Modules.Games -//{ -// /// -// /// Flower picking/planting idea is given to me by its -// /// inceptor Violent Crumble from Game Developers League discord server -// /// (he has !cookie and !nom) Thanks a lot Violent! -// /// Check out GDL (its a growing gamedev community): -// /// https://discord.gg/0TYNJfCU4De7YIk8 -// /// -// class PlantPick : DiscordCommand -// { +//todo rewrite +namespace NadekoBot.Modules.Games +{ + public partial class Games + { + /// + /// Flower picking/planting idea is given to me by its + /// inceptor Violent Crumble from Game Developers League discord server + /// (he has !cookie and !nom) Thanks a lot Violent! + /// Check out GDL (its a growing gamedev community): + /// https://discord.gg/0TYNJfCU4De7YIk8 + /// + [Group] + public class PlantPickCommands + { -// private Random rng; -// public PlantPick(DiscordModule module) : base(module) -// { -// NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; -// rng = new Random(); -// } + private Random rng; -// private static readonly ConcurrentDictionary plantpickCooldowns = new ConcurrentDictionary(); + private ConcurrentDictionary generationChannels = new ConcurrentDictionary(); + //channelid/message + private ConcurrentDictionary> plantedFlowers = new ConcurrentDictionary>(); + //channelId/last generation + private ConcurrentDictionary lastGenerations = new ConcurrentDictionary(); -// private async void PotentialFlowerGeneration(object sender, Discord.MessageEventArgs e) -// { -// try -// { -// if (e.Server == null || e.Channel.IsPrivate || e.Message.IsAuthor) -// return; -// var config = Classes.SpecificConfigurations.Default.Of(e.Server.Id); -// var now = DateTime.Now; -// int cd; -// DateTime lastSpawned; -// if (config.GenerateCurrencyChannels.TryGetValue(e.Channel.Id, out cd)) -// if (!plantpickCooldowns.TryGetValue(e.Channel.Id, out lastSpawned) || (lastSpawned + new TimeSpan(0, cd, 0)) < now) -// { -// var rnd = Math.Abs(rng.Next(0,101)); -// if (rnd == 0) -// { -// var msgs = new[] { await e.Channel.SendFile(GetRandomCurrencyImagePath()), await channel.SendMessageAsync($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`") }; -// plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msgs, (u, m) => { m.ForEach(async msgToDelete => { try { await msgToDelete.Delete(); } catch { } }); return msgs; }); -// plantpickCooldowns.AddOrUpdate(e.Channel.Id, now, (i, d) => now); -// } -// } -// } -// catch { } -// } -// //channelid/messageid pair -// ConcurrentDictionary> plantedFlowerChannels = new ConcurrentDictionary>(); + private float chance; + private int cooldown; -// private SemaphoreSlim locker = new SemaphoreSlim(1,1); + public PlantPickCommands() + { + NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; + rng = new NadekoRandom(); -// internal override void Init(CommandGroupBuilder cgb) -// { -// cgb.CreateCommand(Module.Prefix + "pick") -// .Description($"Picks a flower planted in this channel. | `{Prefix}pick`") -// .Do(async e => -// { -// IEnumerable msgs; + using (var uow = DbHandler.UnitOfWork()) + { + var conf = uow.BotConfig.GetOrCreate(); + var x = + generationChannels = new ConcurrentDictionary(uow.GuildConfigs.GetAll() + .Where(c => c.GenerateCurrencyChannelId != null) + .ToDictionary(c => c.GenerateCurrencyChannelId.Value, c => true)); + chance = conf.CurrencyGenerationChance; + cooldown = conf.CurrencyGenerationCooldown; + } + } -// await e.Message.Delete().ConfigureAwait(false); -// if (!plantedFlowerChannels.TryRemove(e.Channel.Id, out msgs)) -// return; + private Task PotentialFlowerGeneration(IMessage imsg) + { + var msg = imsg as IUserMessage; + if (msg == null || msg.IsAuthor()) + return Task.CompletedTask; -// foreach(var msgToDelete in msgs) -// await msgToDelete.Delete().ConfigureAwait(false); + var channel = imsg.Channel as ITextChannel; + if (channel == null) + return Task.CompletedTask; -// await FlowersHandler.AddFlowersAsync(umsg.Author, "Picked a flower.", 1, true).ConfigureAwait(false); -// var msg = await channel.SendMessageAsync($"**{umsg.Author.Username}** picked a {NadekoBot.Config.CurrencyName}!").ConfigureAwait(false); -// ThreadPool.QueueUserWorkItem(async (state) => -// { -// try -// { -// await Task.Delay(10000).ConfigureAwait(false); -// await msg.Delete().ConfigureAwait(false); -// } -// catch { } -// }); -// }); + bool shouldGenerate; + if (!generationChannels.TryGetValue(channel.Id, out shouldGenerate) || !shouldGenerate) + return Task.CompletedTask; -// cgb.CreateCommand(Module.Prefix + "plant") -// .Description($"Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost) | `{Prefix}plant`") -// .Do(async e => -// { -// await locker.WaitAsync().ConfigureAwait(false); -// try -// { -// if (plantedFlowerChannels.ContainsKey(e.Channel.Id)) -// { -// await channel.SendMessageAsync($"There is already a {NadekoBot.Config.CurrencyName} in this channel.").ConfigureAwait(false); -// return; -// } -// var removed = await FlowersHandler.RemoveFlowers(umsg.Author, "Planted a flower.", 1, true).ConfigureAwait(false); -// if (!removed) -// { -// await channel.SendMessageAsync($"You don't have any {NadekoBot.Config.CurrencyName}s.").ConfigureAwait(false); -// return; -// } + var t = Task.Run(async () => + { + var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue); -// var file = GetRandomCurrencyImagePath(); -// Message msg; -// if (file == null) -// msg = await channel.SendMessageAsync(NadekoBot.Config.CurrencySign).ConfigureAwait(false); -// else -// msg = await e.Channel.SendFile(file).ConfigureAwait(false); -// var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.Config.CurrencyName[0]); -// var msg2 = await channel.SendMessageAsync($"Oh how Nice! **{umsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.Config.CurrencyName}. Pick it using {Module.Prefix}pick").ConfigureAwait(false); -// plantedFlowerChannels.TryAdd(e.Channel.Id, new[] { msg, msg2 }); -// } -// finally { locker.Release(); } -// }); + if (DateTime.Now - TimeSpan.FromSeconds(cooldown) < lastGeneration) //recently generated in this channel, don't generate again + return; -// cgb.CreateCommand(Prefix + "gencurrency") -// .Alias(Prefix + "gc") -// .Description($"Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a {NadekoBot.Config.CurrencyName}. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `{Prefix}gc` or `{Prefix}gc 60`") -// .AddCheck(SimpleCheckers.ManageMessages()) -// .Parameter("cd", ParameterType.Unparsed) -// .Do(async e => -// { -// var cdStr = cd; -// int cd = 2; -// if (!int.TryParse(cdStr, out cd) || cd < 0) -// { -// cd = 2; -// } -// var config = SpecificConfigurations.Default.Of(e.Server.Id); -// int throwaway; -// if (config.GenerateCurrencyChannels.TryRemove(e.Channel.Id, out throwaway)) -// { -// await channel.SendMessageAsync("`Currency generation disabled on this channel.`").ConfigureAwait(false); -// } -// else -// { -// if (config.GenerateCurrencyChannels.TryAdd(e.Channel.Id, cd)) -// await channel.SendMessageAsync($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`").ConfigureAwait(false); -// } -// }); -// } + var num = rng.Next(1, 101) + chance * 100; -// private string GetRandomCurrencyImagePath() => -// Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault(); + if (num > 100) + { + lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); + //todo get prefix + try + { + var sent = await channel.SendFileAsync( + GetRandomCurrencyImagePath(), + $"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `>pick`") + .ConfigureAwait(false); + plantedFlowers.AddOrUpdate(channel.Id, new List() { sent }, (id, old) => { old.Add(sent); return old; }); + } + catch { } + + } + }); + return Task.CompletedTask; + } + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Pick(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; -// int GetRandomNumber() -// { -// using (var rg = RandomNumberGenerator.Create()) -// { -// byte[] rno = new byte[4]; -// rg.GetBytes(rno); -// int randomvalue = BitConverter.ToInt32(rno, 0); -// return randomvalue; -// } -// } -// } -//} + if (!channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages) + { + await channel.SendMessageAsync("`I need manage channel permissions in order to process this command.`").ConfigureAwait(false); + return; + } + + List msgs; + + try { await imsg.DeleteAsync().ConfigureAwait(false); } catch { } + if (!plantedFlowers.TryRemove(channel.Id, out msgs)) + return; + + await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); + + await CurrencyHandler.AddCurrencyAsync((IGuildUser)imsg.Author, "Picked flower(s).", msgs.Count, false).ConfigureAwait(false); + var msg = await channel.SendMessageAsync($"**{imsg.Author.Username}** picked {msgs.Count}{Gambling.Gambling.CurrencySign}!").ConfigureAwait(false); + var t = Task.Run(async () => + { + try + { + await Task.Delay(10000).ConfigureAwait(false); + await msg.DeleteAsync().ConfigureAwait(false); + } + catch { } + }); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Plant(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + if (channel == null) + return; + + var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)imsg.Author, "Planted a flower.", 1, false).ConfigureAwait(false); + if (!removed) + { + await channel.SendMessageAsync($"You don't have any {Gambling.Gambling.CurrencyPluralName}.").ConfigureAwait(false); + return; + } + + var file = GetRandomCurrencyImagePath(); + IUserMessage msg; + var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(Gambling.Gambling.CurrencyName[0]); + var msgToSend = $"Oh how Nice! **{imsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {Gambling.Gambling.CurrencyName}. Pick it using >pick"; + if (file == null) + { + msg = await channel.SendMessageAsync(Gambling.Gambling.CurrencySign).ConfigureAwait(false); + } + else + { + //todo add prefix + msg = await channel.SendFileAsync(file, msgToSend).ConfigureAwait(false); + } + plantedFlowers.AddOrUpdate(channel.Id, new List() { msg }, (id, old) => { old.Add(msg); return old; }); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageMessages)] + public async Task Gencurrency(IUserMessage imsg) + { + var channel = imsg.Channel as ITextChannel; + if (channel == null) + return; + + bool enabled; + using (var uow = DbHandler.UnitOfWork()) + { + var guildConfig = uow.GuildConfigs.For(channel.Id); + + if (guildConfig.GenerateCurrencyChannelId == null) + { + guildConfig.GenerateCurrencyChannelId = channel.Id; + generationChannels.TryAdd(channel.Id, true); + enabled = true; + } + else + { + guildConfig.GenerateCurrencyChannelId = null; + bool throwaway; + generationChannels.TryRemove(channel.Id, out throwaway); + enabled = false; + } + await uow.CompleteAsync(); + } + if (enabled) + { + await channel.SendMessageAsync("`Currency generation enabled on this channel.`").ConfigureAwait(false); + } + else + { + await channel.SendMessageAsync($"`Currency generation disabled on this channel.`").ConfigureAwait(false); + } + } + + private string GetRandomCurrencyImagePath() => + Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault(); + + int GetRandomNumber() + { + using (var rg = RandomNumberGenerator.Create()) + { + byte[] rno = new byte[4]; + rg.GetBytes(rno); + int randomvalue = BitConverter.ToInt32(rno, 0); + return randomvalue; + } + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs index 0e579ff9..711873d3 100644 --- a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs @@ -10,12 +10,11 @@ using System.Threading.Tasks; namespace NadekoBot.Modules.Games { - public partial class GamesModule + public partial class Games { - //todo DB in the future public static ConcurrentDictionary ActivePolls = new ConcurrentDictionary(); - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Poll(IUserMessage umsg, [Remainder] string arg = null) { @@ -36,7 +35,7 @@ namespace NadekoBot.Modules.Games } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Pollend(IUserMessage umsg) { @@ -108,26 +107,36 @@ namespace NadekoBot.Modules.Games } } - private async Task Vote(IMessage imsg) + private Task Vote(IMessage imsg) { - var msg = imsg as ISystemMessage; + // has to be a user message + var msg = imsg as IUserMessage; if (msg == null) - return; - try + return Task.CompletedTask; + // channel must be private + IPrivateChannel ch; + if ((ch = msg.Channel as IPrivateChannel) == null) + return Task.CompletedTask; + + // has to be an integer + int vote; + if (!int.TryParse(msg.Content, out vote)) return Task.CompletedTask; + + var t = Task.Run(async () => { - IPrivateChannel ch; - if ((ch = msg.Channel as IPrivateChannel) == null) - return; - int vote; - if (!int.TryParse(msg.Content, out vote)) return; - if (vote < 1 || vote > answers.Length) - return; - if (participants.TryAdd(msg.Author, vote)) + try { - await (ch as ITextChannel).SendMessageAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false); + + if (vote < 1 || vote > answers.Length) + return; + if (participants.TryAdd(msg.Author, vote)) + { + await (ch as ITextChannel).SendMessageAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false); + } } - } - catch { } + catch { } + }); + return Task.CompletedTask; } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs index 875f235c..7817a61a 100644 --- a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs @@ -1,196 +1,197 @@ -//using Discord; -//using Discord.Commands; -//using NadekoBot.Classes; -//using NadekoBot.DataModels; -//using NadekoBot.Extensions; -//using System; -//using System.Collections.Concurrent; -//using System.Collections.Generic; -//using System.Diagnostics; -//using System.Linq; -//using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Services; +using NadekoBot.Services.Database; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; -////todo DB -////todo Rewrite? -//namespace NadekoBot.Modules.Games -//{ -// public static class SentencesProvider -// { -// internal static string GetRandomSentence() -// { -// var data = DbHandler.Instance.GetAllRows(); -// try -// { -// return data.ToList()[new Random().Next(0, data.Count())].Text; -// } -// catch -// { -// return "Failed retrieving data from parse. Owner didn't add any articles to type using `typeadd`."; -// } -// } -// } +namespace NadekoBot.Modules.Games +{ + public partial class Games + { + public class TypingGame + { + public const float WORD_VALUE = 4.5f; + private readonly ITextChannel channel; + public string CurrentSentence; + public bool IsActive; + private readonly Stopwatch sw; + private readonly List finishedUserIds; -// public class TypingGame -// { -// public const float WORD_VALUE = 4.5f; -// private readonly Channel channel; -// public string CurrentSentence; -// public bool IsActive; -// private readonly Stopwatch sw; -// private readonly List finishedUserIds; + public TypingGame(ITextChannel channel) + { + this.channel = channel; + IsActive = false; + sw = new Stopwatch(); + finishedUserIds = new List(); + } -// public TypingGame(Channel channel) -// { -// this.channel = channel; -// IsActive = false; -// sw = new Stopwatch(); -// finishedUserIds = new List(); -// } + public ITextChannel Channel { get; set; } -// public Channel Channell { get; internal set; } + public async Task Stop() + { + if (!IsActive) return false; + NadekoBot.Client.MessageReceived -= AnswerReceived; + finishedUserIds.Clear(); + IsActive = false; + sw.Stop(); + sw.Reset(); + await channel.SendMessageAsync("Typing contest stopped").ConfigureAwait(false); + return true; + } -// internal async Task Stop() -// { -// if (!IsActive) return false; -// NadekoBot.Client.MessageReceived -= AnswerReceived; -// finishedUserIds.Clear(); -// IsActive = false; -// sw.Stop(); -// sw.Reset(); -// await channel.Send("Typing contest stopped").ConfigureAwait(false); -// return true; -// } - -// internal async Task Start() -// { -// while (true) -// { -// if (IsActive) return; // can't start running game -// IsActive = true; -// CurrentSentence = SentencesProvider.GetRandomSentence(); -// var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f); -// await channel.SendMessageAsync($":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false); + public async Task Start() + { + while (true) + { + if (IsActive) return; // can't start running game + IsActive = true; + CurrentSentence = GetRandomSentence(); + var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f); + await channel.SendMessageAsync($":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); -// await msg.Edit("Starting new typing contest in **2**...").ConfigureAwait(false); -// await Task.Delay(1000).ConfigureAwait(false); -// await msg.Edit("Starting new typing contest in **1**...").ConfigureAwait(false); -// await Task.Delay(1000).ConfigureAwait(false); -// await msg.Edit($":book:**{CurrentSentence.Replace(" ", " \x200B")}**:book:").ConfigureAwait(false); -// sw.Start(); -// HandleAnswers(); + var msg = await channel.SendMessageAsync("Starting new typing contest in **3**...").ConfigureAwait(false); + await Task.Delay(1000).ConfigureAwait(false); + 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); + await msg.ModifyAsync(m => m.Content = $":book:**{CurrentSentence.Replace(" ", " \x200B")}**:book:").ConfigureAwait(false); + sw.Start(); + HandleAnswers(); -// while (i > 0) -// { -// await Task.Delay(1000).ConfigureAwait(false); -// i--; -// if (!IsActive) -// return; -// } + while (i > 0) + { + await Task.Delay(1000).ConfigureAwait(false); + i--; + if (!IsActive) + return; + } -// await Stop().ConfigureAwait(false); -// } -// } + await Stop().ConfigureAwait(false); + } + } -// private void HandleAnswers() -// { -// NadekoBot.Client.MessageReceived += AnswerReceived; -// } + public string GetRandomSentence() + { + using (var uow = DbHandler.UnitOfWork()) + { + return uow.TypingArticles.GetRandom()?.Text ?? "No typing articles found. Use `>typeadd` command to add a new article for typing."; + } -// private async void AnswerReceived(object sender, MessageEventArgs e) -// { -// try -// { -// if (e.Channel == null || e.Channel.Id != channel.Id || umsg.Author.Id == NadekoBot.Client.CurrentUser.Id) return; + } -// var guess = e.Message.RawText; + private void HandleAnswers() + { + NadekoBot.Client.MessageReceived += AnswerReceived; + } -// var distance = CurrentSentence.LevenshteinDistance(guess); -// var decision = Judge(distance, guess.Length); -// if (decision && !finishedUserIds.Contains(umsg.Author.Id)) -// { -// finishedUserIds.Add(umsg.Author.Id); -// await channel.Send($"{umsg.Author.Mention} finished in **{sw.Elapsed.Seconds}** seconds with { distance } errors, **{ CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60 }** WPM!").ConfigureAwait(false); -// if (finishedUserIds.Count % 2 == 0) -// { -// await channel.SendMessageAsync($":exclamation: `A lot of people finished, here is the text for those still typing:`\n\n:book:**{CurrentSentence}**:book:").ConfigureAwait(false); -// } -// } -// } -// catch { } -// } + private Task AnswerReceived(IMessage imsg) + { + var msg = imsg as IUserMessage; + if (msg == null) + return Task.CompletedTask; + var t = Task.Run(async () => + { + try + { + if (channel == null || channel.Id != channel.Id || msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return; -// private bool Judge(int errors, int textLength) => errors <= textLength / 25; + var guess = msg.Content; -// } + var distance = CurrentSentence.LevenshteinDistance(guess); + var decision = Judge(distance, guess.Length); + if (decision && !finishedUserIds.Contains(msg.Author.Id)) + { + finishedUserIds.Add(msg.Author.Id); + await channel.SendMessageAsync($"{msg.Author.Mention} finished in **{sw.Elapsed.Seconds}** seconds with { distance } errors, **{ CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60 }** WPM!").ConfigureAwait(false); + if (finishedUserIds.Count % 2 == 0) + { + await channel.SendMessageAsync($":exclamation: `A lot of people finished, here is the text for those still typing:`\n\n:book:**{CurrentSentence}**:book:").ConfigureAwait(false); + } + } + } + catch { } + }); + return Task.CompletedTask; + } -// internal class SpeedTyping : DiscordCommand -// { + private bool Judge(int errors, int textLength) => errors <= textLength / 25; -// public static ConcurrentDictionary RunningContests; + } -// public SpeedTyping(DiscordModule module) : base(module) -// { -// RunningContests = new ConcurrentDictionary(); -// } + [Group] + public class SpeedTypingCommands + { -// public Func DoFunc() => -// async e => -// { -// var game = RunningContests.GetOrAdd(umsg.Author.Server.Id, id => new TypingGame(e.Channel)); + public static ConcurrentDictionary RunningContests; -// if (game.IsActive) -// { -// await channel.SendMessageAsync( -// $"Contest already running in " + -// $"{game.Channell.Mention} channel.") -// .ConfigureAwait(false); -// } -// else -// { -// await game.Start().ConfigureAwait(false); -// } -// }; + public SpeedTypingCommands() + { + RunningContests = new ConcurrentDictionary(); + } -// private Func QuitFunc() => -// async e => -// { -// TypingGame game; -// if (RunningContests.TryRemove(umsg.Author.Server.Id, out game)) -// { -// await game.Stop().ConfigureAwait(false); -// return; -// } -// await channel.SendMessageAsync("No contest to stop on this channel.").ConfigureAwait(false); -// }; + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task TypeStart(IUserMessage msg) + { + var channel = (ITextChannel)msg.Channel; -// internal override void Init(CommandGroupBuilder cgb) -// { -// cgb.CreateCommand(Module.Prefix + "typestart") -// .Description($"Starts a typing contest. | `{Prefix}typestart`") -// .Do(DoFunc()); + var game = RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(channel)); -// cgb.CreateCommand(Module.Prefix + "typestop") -// .Description($"Stops a typing contest on the current channel. | `{Prefix}typestop`") -// .Do(QuitFunc()); + if (game.IsActive) + { + await channel.SendMessageAsync( + $"Contest already running in " + + $"{game.Channel.Mention} channel.") + .ConfigureAwait(false); + } + else + { + await game.Start().ConfigureAwait(false); + } + } -// cgb.CreateCommand(Module.Prefix + "typeadd") -// .Description($"Adds a new article to the typing contest. Owner only. | `{Prefix}typeadd wordswords`") -// .Parameter("text", ParameterType.Unparsed) -// .Do(async e => -// { -// if (!NadekoBot.IsOwner(umsg.Author.Id) || string.IsNullOrWhiteSpace(text)) return; + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task TypeStop(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + TypingGame game; + if (RunningContests.TryRemove(channel.Guild.Id, out game)) + { + await game.Stop().ConfigureAwait(false); + return; + } + await channel.SendMessageAsync("No contest to stop on this channel.").ConfigureAwait(false); + } -// DbHandler.Instance.Connection.Insert(new TypingArticle -// { -// Text = text, -// DateAdded = DateTime.Now -// }); + ////todo owner only + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + //[RequireContext(ContextType.Guild)] + //public async Task Typeadd(IUserMessage imsg, [Remainder] string text) + //{ + // var channel = (ITextChannel)imsg.Channel; -// await channel.SendMessageAsync("Added new article for typing game.").ConfigureAwait(false); -// }); -// } -// } -//} + // using (var uow = DbHandler.UnitOfWork()) + // { + // uow.TypingArticles.Add(new Services.Database.Models.TypingArticle + // { + // Author = imsg.Author.Username, + // Text = text + // }); + // } + + // await channel.SendMessageAsync("Added new article for typing game.").ConfigureAwait(false); + //} + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs index a86f2558..c4e960a6 100644 --- a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs @@ -7,8 +7,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -// todo rewrite? -// todo DB namespace NadekoBot.Modules.Games.Trivia { public class TriviaGame @@ -102,39 +100,42 @@ namespace NadekoBot.Modules.Games.Trivia ShouldStopGame = true; } - private async Task PotentialGuess(IMessage imsg) + private Task PotentialGuess(IMessage imsg) { var umsg = imsg as IUserMessage; if (umsg == null) - return; - - try + return Task.CompletedTask; + var t = Task.Run(async () => { - if (!(umsg.Channel is IGuildChannel && umsg.Channel is ITextChannel)) return; - if ((umsg.Channel as ITextChannel).Guild != guild) return; - if (umsg.Author.Id == (await NadekoBot.Client.GetCurrentUserAsync()).Id) return; - - var guildUser = umsg.Author as IGuildUser; - - var guess = false; - await _guessLock.WaitAsync().ConfigureAwait(false); try { - if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested) + if (!(umsg.Channel is IGuildChannel && umsg.Channel is ITextChannel)) return; + if ((umsg.Channel as ITextChannel).Guild != guild) return; + if (umsg.Author.Id == (await NadekoBot.Client.GetCurrentUserAsync()).Id) return; + + var guildUser = umsg.Author as IGuildUser; + + var guess = false; + await _guessLock.WaitAsync().ConfigureAwait(false); + try { - Users.AddOrUpdate(guildUser, 0, (gu, old) => old++); - guess = true; + if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested) + { + Users.AddOrUpdate(guildUser, 0, (gu, old) => old++); + guess = true; + } } + finally { _guessLock.Release(); } + if (!guess) return; + triviaCancelSource.Cancel(); + await channel.SendMessageAsync($"☑️ {guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); + if (Users[guildUser] != WinRequirement) return; + ShouldStopGame = true; + await channel.SendMessageAsync($":exclamation: We have a winner! It's {guildUser.Mention}.").ConfigureAwait(false); } - finally { _guessLock.Release(); } - if (!guess) return; - triviaCancelSource.Cancel(); - await channel.SendMessageAsync($"☑️ {guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); - if (Users[guildUser] != WinRequirement) return; - ShouldStopGame = true; - await channel.SendMessageAsync($":exclamation: We have a winner! It's {guildUser.Mention}.").ConfigureAwait(false); - } - catch { } + catch { } + }); + return Task.CompletedTask; } public string GetLeaderboard() diff --git a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestionPool.cs b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestionPool.cs index fd21086f..9682bbef 100644 --- a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestionPool.cs +++ b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestionPool.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json.Linq; +using NadekoBot.Services; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; @@ -9,10 +10,9 @@ namespace NadekoBot.Modules.Games.Trivia public class TriviaQuestionPool { public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool(); - //todo DB public HashSet pool = new HashSet(); - private Random rng { get; } = new Random(); + private Random rng { get; } = new NadekoRandom(); static TriviaQuestionPool() { } @@ -28,16 +28,16 @@ namespace NadekoBot.Modules.Games.Trivia return list[rand]; } - internal void Reload() + public void Reload() { - var arr = JArray.Parse(File.ReadAllText("data/questions.json")); + var arr = JArray.Parse(File.ReadAllText("data/triviaquestions.json")); foreach (var item in arr) { var tq = new TriviaQuestion(item["Question"].ToString(), item["Answer"].ToString(), item["Category"]?.ToString()); pool.Add(tq); } - var r = new Random(); + var r = new NadekoRandom(); pool = new HashSet(pool.OrderBy(x => r.Next())); } } diff --git a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs index 13c0adaf..959562a0 100644 --- a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs @@ -10,16 +10,16 @@ using System.Threading.Tasks; //todo Rewrite? Fix trivia not stopping bug namespace NadekoBot.Modules.Games { - public partial class GamesModule + public partial class Games { [Group] public class TriviaCommands { public static ConcurrentDictionary RunningTrivias = new ConcurrentDictionary(); - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] - public async Task Trivia(IUserMessage umsg, string[] args) + public async Task Trivia(IUserMessage umsg, params string[] args) { var channel = (ITextChannel)umsg.Channel; @@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Games await channel.SendMessageAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Tl(IUserMessage umsg) { @@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Games await channel.SendMessageAsync("No trivia is running on this server.").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Tq(IUserMessage umsg) { diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index 4b29e7da..99083ae7 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -12,10 +12,9 @@ using NadekoBot.Services.Database; namespace NadekoBot.Modules.Games { - [Module(">", AppendSpace = false)] + [NadekoModule("Games", ">")] public partial class Games : DiscordModule { - //todo DB private IEnumerable _8BallResponses { get { using (var uow = DbHandler.UnitOfWork()) @@ -28,7 +27,7 @@ namespace NadekoBot.Modules.Games { } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Choose(IUserMessage umsg, [Remainder] string list = null) { @@ -38,11 +37,11 @@ namespace NadekoBot.Modules.Games var listArr = list.Split(';'); if (listArr.Count() < 2) return; - var rng = new Random(); + var rng = new NadekoRandom(); await channel.SendMessageAsync(listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task _8Ball(IUserMessage umsg, [Remainder] string question = null) { @@ -50,12 +49,12 @@ namespace NadekoBot.Modules.Games if (string.IsNullOrWhiteSpace(question)) return; - var rng = new Random(); + var rng = new NadekoRandom(); await channel.SendMessageAsync($@":question: `Question` __**{question}**__ 🎱 `8Ball Answers` __**{_8BallResponses.Shuffle().FirstOrDefault()}**__").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Rps(IUserMessage umsg, string input) { @@ -91,7 +90,7 @@ namespace NadekoBot.Modules.Games default: return; } - var nadekoPick = new Random().Next(0, 3); + var nadekoPick = new NadekoRandom().Next(0, 3); var msg = ""; if (pick == nadekoPick) msg = $"It's a draw! Both picked :{GetRPSPick(pick)}:"; @@ -105,7 +104,7 @@ namespace NadekoBot.Modules.Games await channel.SendMessageAsync(msg).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Linux(IUserMessage umsg, string guhnoo, string loonix) { diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index c62c39c4..070b6930 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -12,7 +12,7 @@ using Discord.WebSocket; namespace NadekoBot.Modules.Help { - [Module("-", AppendSpace = false)] + [NadekoModule("Help", "-")] public partial class Help : DiscordModule { public string HelpString { @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Help { } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Modules(IUserMessage umsg) { @@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Help .ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Commands(IUserMessage umsg, [Remainder] string module = null) { @@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Help module = module?.Trim().ToUpperInvariant(); if (string.IsNullOrWhiteSpace(module)) return; - var cmds = _commands.Commands.Where(c => c.Module.Name.ToUpperInvariant() == module) + var cmds = _commands.Commands.Where(c => c.Module.Name.ToUpperInvariant().StartsWith(module)) .OrderBy(c => c.Text) .AsEnumerable(); var cmdsArray = cmds as Command[] ?? cmds.ToArray(); @@ -55,8 +55,7 @@ namespace NadekoBot.Modules.Help } if (module != "customreactions" && module != "conversations") { - //todo aliases - await channel.SendTableAsync("`List Of Commands:`\n", cmdsArray, el => $"{el.Text,-15}").ConfigureAwait(false); + await channel.SendTableAsync("`List Of Commands:`\n", cmdsArray, el => $"{el.Text,-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false); } else { @@ -65,7 +64,7 @@ namespace NadekoBot.Modules.Help await channel.SendMessageAsync($"`You can type \"-h command_name\" to see the help about that specific command.`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task H(IUserMessage umsg, [Remainder] string comToFind = null) { @@ -77,16 +76,23 @@ namespace NadekoBot.Modules.Help await (await (umsg.Author as IGuildUser).CreateDMChannelAsync()).SendMessageAsync(HelpString).ConfigureAwait(false); return; } - var com = _commands.Commands.FirstOrDefault(c => c.Text.ToLowerInvariant() == comToFind); + var com = _commands.Commands.FirstOrDefault(c => c.Text.ToLowerInvariant() == comToFind || c.Aliases.Select(a=>a.ToLowerInvariant()).Contains(comToFind)); - //todo aliases + if (com == null) + { + await channel.SendMessageAsync("`No command found.`"); + return; + } + var str = $"**__Help for:__ `{com.Text}`**"; + var alias = com.Aliases.Skip(1).FirstOrDefault(); + if (alias != null) + str += $" / `{ alias }`"; if (com != null) - await channel.SendMessageAsync($@"**__Help for:__ `{com.Text}`** -**Desc:** {com.Description} + await channel.SendMessageAsync(str + $@"{Environment.NewLine}**Desc:** {com.Description} **Usage:** {com.Summary}").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Hgit(IUserMessage umsg) { @@ -102,8 +108,7 @@ namespace NadekoBot.Modules.Help helpstr.AppendLine("----------------|--------------|-------"); lastModule = com.Module.Name; } - //todo aliases - helpstr.AppendLine($"`{com.Text}` | {com.Description} | {com.Summary}"); + helpstr.AppendLine($"`{com.Text}` {string.Join(" ", com.Aliases.Skip(1).Select(a=>"`"+a+"`"))} | {com.Description} | {com.Summary}"); } helpstr = helpstr.Replace((await NadekoBot.Client.GetCurrentUserAsync()).Username , "@BotName"); #if DEBUG @@ -113,7 +118,7 @@ namespace NadekoBot.Modules.Help #endif } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Guide(IUserMessage umsg) { @@ -124,7 +129,7 @@ namespace NadekoBot.Modules.Help **Hosting Guides and docs can be found here**: ").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Donate(IUserMessage umsg) { diff --git a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs index 03aaccef..233bca09 100644 --- a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -234,7 +234,7 @@ namespace NadekoBot.Modules.Music.Classes }); } - internal void ClearQueue() + public void ClearQueue() { actionQueue.Enqueue(() => { @@ -256,7 +256,7 @@ namespace NadekoBot.Modules.Music.Classes }); } - internal Task MoveToVoiceChannel(IVoiceChannel voiceChannel) + public Task MoveToVoiceChannel(IVoiceChannel voiceChannel) { if (audioClient?.ConnectionState != ConnectionState.Connected) throw new InvalidOperationException("Can't move while bot is not connected to voice channel."); @@ -264,13 +264,13 @@ namespace NadekoBot.Modules.Music.Classes return PlaybackVoiceChannel.ConnectAsync(); } - internal bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong; + public bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong; - internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist; + public bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist; - internal bool ToggleAutoplay() => this.Autoplay = !this.Autoplay; + public bool ToggleAutoplay() => this.Autoplay = !this.Autoplay; - internal void ThrowIfQueueFull() + public void ThrowIfQueueFull() { if (MaxQueueSize == 0) return; diff --git a/src/NadekoBot/Modules/Music/Classes/Song.cs b/src/NadekoBot/Modules/Music/Classes/Song.cs index f6827d2a..00757f92 100644 --- a/src/NadekoBot/Modules/Music/Classes/Song.cs +++ b/src/NadekoBot/Modules/Music/Classes/Song.cs @@ -16,18 +16,18 @@ namespace NadekoBot.Modules.Music.Classes { public class SongInfo { - public string Provider { get; internal set; } - public MusicType ProviderType { get; internal set; } + public string Provider { get; set; } + public MusicType ProviderType { get; set; } /// /// Will be set only if the providertype is normal /// - public string Query { get; internal set; } - public string Title { get; internal set; } - public string Uri { get; internal set; } + public string Query { get; set; } + public string Title { get; set; } + public string Uri { get; set; } } public class Song { - public StreamState State { get; internal set; } + public StreamState State { get; set; } public string PrettyName => $"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}` `by {QueuerName}`"; public SongInfo SongInfo { get; } @@ -80,11 +80,11 @@ namespace NadekoBot.Modules.Music.Classes return this; } - internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) + public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) { var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString()); - SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo); + SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo, frameBytes * 100); var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false); bytesSent = 0; @@ -118,9 +118,10 @@ namespace NadekoBot.Modules.Music.Classes while (!cancelToken.IsCancellationRequested) { //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); - var read = inStream.Read(buffer, 0, buffer.Length); + var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); //await inStream.CopyToAsync(voiceClient.OutputStream); - _log.Debug("read {0}", read); + if(read < frameBytes) + _log.Debug("read {0}", read); unchecked { bytesSent += (ulong)read; @@ -155,7 +156,7 @@ namespace NadekoBot.Modules.Music.Classes int delayMillis = unchecked(nextTime - Environment.TickCount); if (delayMillis > 0) await Task.Delay(delayMillis, cancelToken).ConfigureAwait(false); - await outStream.WriteAsync(buffer, 0, read); + await outStream.WriteAsync(buffer, 0, read).ConfigureAwait(false); } } finally diff --git a/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs b/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs index a53d7d0b..0979f504 100644 --- a/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs +++ b/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs @@ -1,4 +1,5 @@ using NadekoBot.Extensions; +using NLog; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -17,14 +18,15 @@ namespace NadekoBot.Modules.Music.Classes /// class SongBuffer : Stream { - - public SongBuffer(MusicPlayer musicPlayer, string basename, SongInfo songInfo, int skipTo) + public SongBuffer(MusicPlayer musicPlayer, string basename, SongInfo songInfo, int skipTo, int maxFileSize) { MusicPlayer = musicPlayer; Basename = basename; SongInfo = songInfo; SkipTo = skipTo; + MaxFileSize = maxFileSize; CurrentFileStream = new FileStream(this.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); + _log = LogManager.GetCurrentClassLogger(); } MusicPlayer MusicPlayer; @@ -35,7 +37,7 @@ namespace NadekoBot.Modules.Music.Classes private int SkipTo; - private static int MAX_FILE_SIZE = 2.MiB(); + private int MaxFileSize = 2.MiB(); private long FileNumber = -1; @@ -46,9 +48,10 @@ namespace NadekoBot.Modules.Music.Classes private ulong CurrentBufferSize = 0; private FileStream CurrentFileStream; + private Logger _log; public Task BufferSong(CancellationToken cancelToken) => - Task.Factory.StartNew(async () => + Task.Run(async () => { Process p = null; FileStream outStream = null; @@ -72,7 +75,7 @@ namespace NadekoBot.Modules.Music.Classes while (!p.HasExited) //Also fix low bandwidth { int bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false); - if (currentFileSize >= MAX_FILE_SIZE) + if (currentFileSize >= MaxFileSize) { try { @@ -122,7 +125,7 @@ Check the guides for your platform on how to setup ffmpeg correctly: p.Dispose(); } } - }, TaskCreationOptions.LongRunning); + }); /// /// Return the next file to read, and delete the old one @@ -172,18 +175,7 @@ Check the guides for your platform on how to setup ffmpeg correctly: public override long Length => (long) CurrentBufferSize; - public override long Position - { - get - { - return 0; - } - - set - { - - } - } + public override long Position { get; set; } = 0; public override void Flush() { } @@ -198,6 +190,8 @@ Check the guides for your platform on how to setup ffmpeg correctly: CurrentFileStream = new FileStream(GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); read += CurrentFileStream.Read(buffer, read + offset, count - read); } + if (read < count) + Array.Clear(buffer, read, count - read); } return read; } diff --git a/src/NadekoBot/Modules/Music/Classes/SoundCloud.cs b/src/NadekoBot/Modules/Music/Classes/SoundCloud.cs index c788ed74..c653d5a2 100644 --- a/src/NadekoBot/Modules/Music/Classes/SoundCloud.cs +++ b/src/NadekoBot/Modules/Music/Classes/SoundCloud.cs @@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Music.Classes public bool IsSoundCloudLink(string url) => System.Text.RegularExpressions.Regex.IsMatch(url, "(.*)(soundcloud.com|snd.sc)(.*)"); - internal async Task GetVideoByQueryAsync(string query) + public async Task GetVideoByQueryAsync(string query) { if (string.IsNullOrWhiteSpace(query)) throw new ArgumentNullException(nameof(query)); diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 2bb4d62c..5675eff1 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -13,10 +13,11 @@ using NadekoBot.Extensions; using System.Net.Http; using Newtonsoft.Json.Linq; using System.Collections.Generic; +using NadekoBot.Services.Database; namespace NadekoBot.Modules.Music { - [Module("!!", AppendSpace = false)] + [NadekoModule("ClashOfClans", "!!")] public partial class Music : DiscordModule { public static ConcurrentDictionary MusicPlayers = new ConcurrentDictionary(); @@ -34,46 +35,49 @@ namespace NadekoBot.Modules.Music _google = google; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] - public async Task Next(IUserMessage umsg) + public Task Next(IUserMessage umsg) { var channel = (ITextChannel)umsg.Channel; MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask; if (musicPlayer.PlaybackVoiceChannel == ((IGuildUser)umsg.Author).VoiceChannel) musicPlayer.Next(); + return Task.CompletedTask; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] - public async Task Stop(IUserMessage umsg) + public Task Stop(IUserMessage umsg) { var channel = (ITextChannel)umsg.Channel; MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask; if (((IGuildUser)umsg.Author).VoiceChannel == musicPlayer.PlaybackVoiceChannel) { musicPlayer.Autoplay = false; musicPlayer.Stop(); } + return Task.CompletedTask; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] - public async Task Destroy(IUserMessage umsg) + public Task Destroy(IUserMessage umsg) { var channel = (ITextChannel)umsg.Channel; MusicPlayer musicPlayer; - if (!MusicPlayers.TryRemove(channel.Guild.Id, out musicPlayer)) return; + if (!MusicPlayers.TryRemove(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask; if (((IGuildUser)umsg.Author).VoiceChannel == musicPlayer.PlaybackVoiceChannel) musicPlayer.Destroy(); + return Task.CompletedTask; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Pause(IUserMessage umsg) { @@ -90,7 +94,7 @@ namespace NadekoBot.Modules.Music await channel.SendMessageAsync("🎵`Music Player unpaused.`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Queue(IUserMessage umsg, [Remainder] string query) { @@ -104,7 +108,7 @@ namespace NadekoBot.Modules.Music } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task SoundCloudQueue(IUserMessage umsg, [Remainder] string query) { @@ -118,7 +122,7 @@ namespace NadekoBot.Modules.Music } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ListQueue(IUserMessage umsg, int page = 1) { @@ -151,7 +155,7 @@ namespace NadekoBot.Modules.Music await channel.SendMessageAsync(toSend + string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(15).Select(v => $"`{number++}.` {v.PrettyName}"))).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task NowPlaying(IUserMessage umsg) { @@ -166,7 +170,7 @@ namespace NadekoBot.Modules.Music $"{currentSong.PrettyCurrentTime()}").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Volume(IUserMessage umsg, int val) { @@ -181,51 +185,55 @@ namespace NadekoBot.Modules.Music var volume = musicPlayer.SetVolume(val); await channel.SendMessageAsync($"🎵 `Volume set to {volume}%`").ConfigureAwait(false); } - ////todo DB - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] - //[RequireContext(ContextType.Guild)] - //public async Task Defvol(IUserMessage umsg, [Remainder] int val) - //{ - // var channel = (ITextChannel)umsg.Channel; - // var arg = val; - // float volume; - // if (!float.TryParse(arg, out volume) || volume < 0 || volume > 100) - // { - // await channel.SendMessageAsync("Volume number invalid.").ConfigureAwait(false); - // return; - // } - // var conf = SpecificConfigurations.Default.Of(channel.Guild.Id); - // conf.DefaultMusicVolume = volume / 100; - // await channel.SendMessageAsync($"🎵 `Default volume set to {volume}%`").ConfigureAwait(false); - //} - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] - public async Task Mute(IUserMessage umsg) + public async Task Defvol(IUserMessage umsg, [Remainder] int val) + { + var channel = (ITextChannel)umsg.Channel; + + if (val < 0 || val > 100) + { + await channel.SendMessageAsync("Volume number invalid. Must be between 0 and 100").ConfigureAwait(false); + return; + } + using (var uow = DbHandler.UnitOfWork()) + { + uow.GuildConfigs.For(channel.Guild.Id).DefaultMusicVolume = val / 100.0f; + uow.Complete(); + } + await channel.SendMessageAsync($"🎵 `Default volume set to {val}%`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public Task Mute(IUserMessage umsg) { var channel = (ITextChannel)umsg.Channel; MusicPlayer musicPlayer; if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) - return; + return Task.CompletedTask; if (((IGuildUser)umsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; + return Task.CompletedTask; musicPlayer.SetVolume(0); + return Task.CompletedTask; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] - public async Task Max(IUserMessage umsg) + public Task Max(IUserMessage umsg) { var channel = (ITextChannel)umsg.Channel; MusicPlayer musicPlayer; if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) - return; + return Task.CompletedTask; if (((IGuildUser)umsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; + return Task.CompletedTask; musicPlayer.SetVolume(100); + return Task.CompletedTask; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Shuffle(IUserMessage umsg) { @@ -245,7 +253,7 @@ namespace NadekoBot.Modules.Music await channel.SendMessageAsync("🎵 `Songs shuffled.`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Playlist(IUserMessage umsg, [Remainder] string playlist) { @@ -287,7 +295,7 @@ namespace NadekoBot.Modules.Music await msg.ModifyAsync(m => m.Content = "🎵 `Playlist queue complete.`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task SoundCloudPl(IUserMessage umsg, [Remainder] string pl) { @@ -324,7 +332,7 @@ namespace NadekoBot.Modules.Music } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task LocalPl(IUserMessage umsg, [Remainder] string directory) { @@ -353,7 +361,7 @@ namespace NadekoBot.Modules.Music catch { } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Radio(IUserMessage umsg, string radio_link) { @@ -371,7 +379,7 @@ namespace NadekoBot.Modules.Music } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Local(IUserMessage umsg, [Remainder] string path) { @@ -383,7 +391,7 @@ namespace NadekoBot.Modules.Music } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Move(IUserMessage umsg) { @@ -392,10 +400,10 @@ namespace NadekoBot.Modules.Music var voiceChannel = ((IGuildUser)umsg.Author).VoiceChannel; if (voiceChannel == null || voiceChannel.Guild != channel.Guild || !MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return; - musicPlayer.MoveToVoiceChannel(voiceChannel); + await musicPlayer.MoveToVoiceChannel(voiceChannel); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Remove(IUserMessage umsg, int num) { @@ -414,8 +422,8 @@ namespace NadekoBot.Modules.Music musicPlayer.RemoveSongAt(num - 1); await channel.SendMessageAsync($"🎵**Track {song.PrettyName} at position `#{num}` has been removed.**").ConfigureAwait(false); } - //todo fix - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Remove(IUserMessage umsg, string all) { @@ -430,7 +438,7 @@ namespace NadekoBot.Modules.Music return; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task MoveSong(IUserMessage umsg, [Remainder] string fromto) { @@ -466,7 +474,7 @@ namespace NadekoBot.Modules.Music } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task SetMaxQueue(IUserMessage umsg, uint size) { @@ -480,7 +488,7 @@ namespace NadekoBot.Modules.Music await channel.SendMessageAsync($"🎵 `Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}`"); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ReptCurSong(IUserMessage umsg) { @@ -498,7 +506,7 @@ namespace NadekoBot.Modules.Music .ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task RepeatPl(IUserMessage umsg) { @@ -511,7 +519,7 @@ namespace NadekoBot.Modules.Music } /// - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Save(IUserMessage umsg, [Remainder] string name) //{ @@ -519,7 +527,7 @@ namespace NadekoBot.Modules.Music //} - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Load(IUserMessage umsg, [Remainder] string name) //{ @@ -527,7 +535,7 @@ namespace NadekoBot.Modules.Music //} - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Playlists(IUserMessage umsg, [Remainder] string num) //{ @@ -535,7 +543,7 @@ namespace NadekoBot.Modules.Music //} - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task DeletePlaylist(IUserMessage umsg, [Remainder] string pl) //{ @@ -543,7 +551,7 @@ namespace NadekoBot.Modules.Music //} - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Goto(IUserMessage umsg, int time) { @@ -580,7 +588,7 @@ namespace NadekoBot.Modules.Music await channel.SendMessageAsync($"`Skipped to {minutes}:{seconds}`").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task GetLink(IUserMessage umsg, int index = 0) { @@ -615,7 +623,7 @@ namespace NadekoBot.Modules.Music } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Autoplay(IUserMessage umsg) { @@ -643,8 +651,11 @@ namespace NadekoBot.Modules.Music var musicPlayer = MusicPlayers.GetOrAdd(textCh.Guild.Id, server => { - //todo DB float vol = 1;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume; + using (var uow = DbHandler.UnitOfWork()) + { + vol = uow.GuildConfigs.For(textCh.Guild.Id).DefaultMusicVolume; + } var mp = new MusicPlayer(voiceCh, vol); diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 407f2116..ae9f6c19 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -15,14 +15,14 @@ using NadekoBot.Extensions; namespace NadekoBot.Modules.NSFW { - [Module("~", AppendSpace = false)] + [NadekoModule("NSFW", "~")] public class NSFW : DiscordModule { public NSFW(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) { } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Hentai(IUserMessage umsg, [Remainder] string tag = null) { @@ -41,7 +41,7 @@ namespace NadekoBot.Modules.NSFW await channel.SendMessageAsync(String.Join("\n\n", links)).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Danbooru(IUserMessage umsg, [Remainder] string tag = null) { @@ -55,7 +55,7 @@ namespace NadekoBot.Modules.NSFW await channel.SendMessageAsync(link).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Gelbooru(IUserMessage umsg, [Remainder] string tag = null) { @@ -69,7 +69,7 @@ namespace NadekoBot.Modules.NSFW await channel.SendMessageAsync(link).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Rule34(IUserMessage umsg, [Remainder] string tag = null) { @@ -83,7 +83,7 @@ namespace NadekoBot.Modules.NSFW await channel.SendMessageAsync(link).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task E621(IUserMessage umsg, [Remainder] string tag = null) { @@ -97,7 +97,7 @@ namespace NadekoBot.Modules.NSFW await channel.SendMessageAsync(link).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Cp(IUserMessage umsg) { @@ -106,7 +106,7 @@ namespace NadekoBot.Modules.NSFW await channel.SendMessageAsync("http://i.imgur.com/MZkY1md.jpg").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Boobs(IUserMessage umsg) { @@ -116,7 +116,7 @@ namespace NadekoBot.Modules.NSFW JToken obj; using (var http = new HttpClient()) { - obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new Random().Next(0, 9880) }").ConfigureAwait(false))[0]; + obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new NadekoRandom().Next(0, 9880) }").ConfigureAwait(false))[0]; } await channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false); } @@ -126,7 +126,7 @@ namespace NadekoBot.Modules.NSFW } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Butts(IUserMessage umsg) { @@ -137,7 +137,7 @@ namespace NadekoBot.Modules.NSFW JToken obj; using (var http = new HttpClient()) { - obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new Random().Next(0, 3873) }").ConfigureAwait(false))[0]; + obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new NadekoRandom().Next(0, 3873) }").ConfigureAwait(false))[0]; } await channel.SendMessageAsync($"http://media.obutts.ru/{ obj["preview"].ToString() }").ConfigureAwait(false); } @@ -149,7 +149,7 @@ namespace NadekoBot.Modules.NSFW public static async Task GetDanbooruImageLink(string tag) { - var rng = new Random(); + var rng = new NadekoRandom(); if (tag == "loli") //loli doesn't work for some reason atm tag = "flat_chest"; @@ -181,7 +181,7 @@ namespace NadekoBot.Modules.NSFW if (matches.Count == 0) return null; - var rng = new Random(); + var rng = new NadekoRandom(); var match = matches[rng.Next(0, matches.Count)]; return matches[rng.Next(0, matches.Count)].Groups["url"].Value; } @@ -189,7 +189,7 @@ namespace NadekoBot.Modules.NSFW public static async Task GetRule34ImageLink(string tag) { - var rng = new Random(); + var rng = new NadekoRandom(); var url = $"http://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags={tag.Replace(" ", "_")}"; using (var http = new HttpClient()) @@ -204,7 +204,7 @@ namespace NadekoBot.Modules.NSFW } - internal static async Task GetE621ImageLink(string tags) + public static async Task GetE621ImageLink(string tags) { try { diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index 6418fcdf..31082944 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -15,7 +15,7 @@ namespace NadekoBot.Modules.Games } //todo Dragon should PR this in - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Poke(IUserMessage umsg) { diff --git a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs index 769c989d..dd96cf0e 100644 --- a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs @@ -1,99 +1,132 @@ -//using Discord; -//using Discord.Commands; -//using NadekoBot.Attributes; -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Modules.Searches.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; -//// todo RestSharp -//namespace NadekoBot.Modules.Searches -//{ -// public partial class SearchesModule -// { -// [LocalizedCommand, LocalizedDescription, LocalizedSummary] -// [RequireContext(ContextType.Guild)] -// public async Task Anime(IUserMessage umsg, [Remainder] string query = null) -// { -// var channel = (ITextChannel)umsg.Channel; +namespace NadekoBot.Modules.Searches +{ + public partial class Searches + { + [Group] + public class AnimeSearchCommands + { + private Logger _log; -// if (!(await ValidateQuery(umsg.Channel as ITextChannel, query).ConfigureAwait(false))) return; -// string result; -// try -// { -// result = (await GetAnimeData(query).ConfigureAwait(false)).ToString(); -// } -// catch -// { -// await channel.SendMessageAsync("Failed to find that anime.").ConfigureAwait(false); -// return; -// } + private string anilistToken { get; set; } + private DateTime lastRefresh { get; set; } -// await channel.SendMessageAsync(result.ToString()).ConfigureAwait(false); -// } + public AnimeSearchCommands() + { + _log = LogManager.GetCurrentClassLogger(); + } -// [LocalizedCommand, LocalizedDescription, LocalizedSummary] -// [RequireContext(ContextType.Guild)] -// public async Task Manga(IUserMessage umsg, [Remainder] string query = null) -// { -// var channel = (ITextChannel)umsg.Channel; + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Anime(IUserMessage umsg, [Remainder] string query) + { + var channel = (ITextChannel)umsg.Channel; -// if (!(await ValidateQuery(umsg.Channel as ITextChannel, query).ConfigureAwait(false))) return; -// string result; -// try -// { -// result = (await GetMangaData(query).ConfigureAwait(false)).ToString(); -// } -// catch -// { -// await channel.SendMessageAsync("Failed to find that manga.").ConfigureAwait(false); -// return; -// } -// await channel.SendMessageAsync(result).ConfigureAwait(false); -// } + if (string.IsNullOrWhiteSpace(query)) + return; -// public static async Task GetAnimeData(string query) -// { -// if (string.IsNullOrWhiteSpace(query)) -// throw new ArgumentNullException(nameof(query)); + var result = await GetAnimeData(query).ConfigureAwait(false); -// await RefreshAnilistToken().ConfigureAwait(false); + await channel.SendMessageAsync(result.ToString() ?? "`No anime found.`").ConfigureAwait(false); + } -// var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query); -// var smallContent = ""; -// var cl = new RestSharp.RestClient("http://anilist.co/api"); -// var rq = new RestSharp.RestRequest("/anime/search/" + Uri.EscapeUriString(query)); -// rq.AddParameter("access_token", token); -// smallContent = cl.Execute(rq).Content; -// var smallObj = JArray.Parse(smallContent)[0]; + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task Manga(IUserMessage umsg, [Remainder] string query) + { + var channel = (ITextChannel)umsg.Channel; -// rq = new RestSharp.RestRequest("/anime/" + smallObj["id"]); -// rq.AddParameter("access_token", token); -// var content = cl.Execute(rq).Content; + if (string.IsNullOrWhiteSpace(query)) + return; -// return await Task.Run(() => JsonConvert.DeserializeObject(content)).ConfigureAwait(false); -// } + var result = await GetMangaData(query).ConfigureAwait(false); -// public static async Task GetMangaData(string query) -// { -// if (string.IsNullOrWhiteSpace(query)) -// throw new ArgumentNullException(nameof(query)); + await channel.SendMessageAsync(result.ToString() ?? "`No manga found.`").ConfigureAwait(false); + } -// await RefreshAnilistToken().ConfigureAwait(false); + private async Task GetAnimeData(string query) + { + if (string.IsNullOrWhiteSpace(query)) + throw new ArgumentNullException(nameof(query)); + try + { + await RefreshAnilistToken().ConfigureAwait(false); -// var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query); -// var smallContent = ""; -// var cl = new RestSharp.RestClient("http://anilist.co/api"); -// var rq = new RestSharp.RestRequest("/manga/search/" + Uri.EscapeUriString(query)); -// rq.AddParameter("access_token", token); -// smallContent = cl.Execute(rq).Content; -// var smallObj = JArray.Parse(smallContent)[0]; + var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query); + using (var http = new HttpClient()) + { + var res = await http.GetStringAsync("http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false); + var smallObj = JArray.Parse(res)[0]; + var aniData = await http.GetStringAsync("http://anilist.co/api/anime/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false); -// rq = new RestSharp.RestRequest("/manga/" + smallObj["id"]); -// rq.AddParameter("access_token", token); -// var content = cl.Execute(rq).Content; + return await Task.Run(() => JsonConvert.DeserializeObject(aniData)).ConfigureAwait(false); + } + } + catch (Exception ex) { + _log.Warn(ex, "Failed anime search for {0}", query); + return null; + } + } -// return await Task.Run(() => JsonConvert.DeserializeObject(content)).ConfigureAwait(false); -// } -// } -//} + private async Task RefreshAnilistToken() + { + if (DateTime.Now - lastRefresh > TimeSpan.FromMinutes(29)) + lastRefresh = DateTime.Now; + else + { + return; + } + var headers = new Dictionary { + {"grant_type", "client_credentials"}, + {"client_id", "kwoth-w0ki9"}, + {"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"}, + }; + using (var http = new HttpClient()) + { + http.AddFakeHeaders(); + var formContent = new FormUrlEncodedContent(headers); + var response = await http.PostAsync("http://anilist.co/api/auth/access_token", formContent).ConfigureAwait(false); + var stringContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + anilistToken = JObject.Parse(stringContent)["access_token"].ToString(); + } + + } + + private async Task GetMangaData(string query) + { + if (string.IsNullOrWhiteSpace(query)) + throw new ArgumentNullException(nameof(query)); + try + { + await RefreshAnilistToken().ConfigureAwait(false); + using (var http = new HttpClient()) + { + var res = await http.GetStringAsync("http://anilist.co/api/manga/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false); + var smallObj = JArray.Parse(res)[0]; + var aniData = await http.GetStringAsync("http://anilist.co/api/manga/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false); + + return await Task.Run(() => JsonConvert.DeserializeObject(aniData)).ConfigureAwait(false); + } + } + catch (Exception ex) + { + _log.Warn(ex, "Failed anime search for {0}", query); + return null; + } + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Commands/CalcCommand.cs b/src/NadekoBot/Modules/Searches/Commands/CalcCommand.cs index dd2e7d35..1f9f00dc 100644 --- a/src/NadekoBot/Modules/Searches/Commands/CalcCommand.cs +++ b/src/NadekoBot/Modules/Searches/Commands/CalcCommand.cs @@ -14,7 +14,7 @@ namespace NadekoBot.Modules.Searches [Group] public partial class Searches { - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public static async Task Calculate(IUserMessage msg, [Remainder] string expression) { @@ -41,7 +41,7 @@ namespace NadekoBot.Modules.Searches } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task CalcOps(IUserMessage msg) { diff --git a/src/NadekoBot/Modules/Searches/Commands/ConverterCommands.cs b/src/NadekoBot/Modules/Searches/Commands/ConverterCommands.cs index 52a6d157..63ed2205 100644 --- a/src/NadekoBot/Modules/Searches/Commands/ConverterCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/ConverterCommands.cs @@ -30,7 +30,7 @@ // } -// internal override void Init(CommandGroupBuilder cgb) +// public override void Init(CommandGroupBuilder cgb) // { // cgb.CreateCommand(Module.Prefix + "convert") // .Description($"Convert quantities from>to. | `{Prefix}convert m>km 1000`") diff --git a/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs b/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs index 9f01b70f..4bacbb52 100644 --- a/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs @@ -2,6 +2,7 @@ using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Modules.Searches.Models; +using NadekoBot.Services; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; @@ -19,7 +20,6 @@ namespace NadekoBot.Modules.Searches [Group] public class JokeCommands { - //todo DB private List wowJokes = new List(); private List magicItems; private Logger _log; @@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Searches _log.Warn("data/magicitems.json is missing. Magic items are not loaded."); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Yomama(IUserMessage umsg) { @@ -54,7 +54,7 @@ namespace NadekoBot.Modules.Searches } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Randjoke(IUserMessage umsg) { @@ -66,7 +66,7 @@ namespace NadekoBot.Modules.Searches } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ChuckNorris(IUserMessage umsg) { @@ -78,7 +78,7 @@ namespace NadekoBot.Modules.Searches } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task WowJoke(IUserMessage umsg) { @@ -87,15 +87,15 @@ namespace NadekoBot.Modules.Searches if (!wowJokes.Any()) { } - await channel.SendMessageAsync(wowJokes[new Random().Next(0, wowJokes.Count)].ToString()); + await channel.SendMessageAsync(wowJokes[new NadekoRandom().Next(0, wowJokes.Count)].ToString()); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task MagicItem(IUserMessage umsg) { var channel = (ITextChannel)umsg.Channel; - var rng = new Random(); + var rng = new NadekoRandom(); var item = magicItems[rng.Next(0, magicItems.Count)].ToString(); await channel.SendMessageAsync(item).ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs b/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs index 8e52985a..0dc758e3 100644 --- a/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs @@ -2,6 +2,7 @@ using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; +using NadekoBot.Services; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; @@ -31,7 +32,7 @@ namespace NadekoBot.Modules.Searches "Doesn't matter what you ban really. Enemy will ban your main and you will lose." }; - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Lolban(IUserMessage umsg) { @@ -52,7 +53,7 @@ namespace NadekoBot.Modules.Searches var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList(); var sb = new StringBuilder(); sb.AppendLine($"**Showing {dataList.Count} top banned champions.**"); - sb.AppendLine($"`{trashTalk[new Random().Next(0, trashTalk.Length)]}`"); + sb.AppendLine($"`{trashTalk[new NadekoRandom().Next(0, trashTalk.Length)]}`"); for (var i = 0; i < dataList.Count; i++) { if (i % 2 == 0 && i != 0) @@ -113,7 +114,7 @@ namespace NadekoBot.Modules.Searches // public float StatScore { get; set; } // } -// internal override void Init(CommandGroupBuilder cgb) +// public override void Init(CommandGroupBuilder cgb) // { // cgb.CreateCommand(Module.Prefix + "lolchamp") // .Description($"Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. |`{Prefix}lolchamp Riven` or `{Prefix}lolchamp Annie sup`") diff --git a/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs b/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs index 16995259..43b6fbe4 100644 --- a/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs @@ -15,7 +15,7 @@ namespace NadekoBot.Modules.Searches { public partial class Searches { - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Memelist(IUserMessage umsg) { @@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Searches } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Memegen(IUserMessage umsg, string meme, string topText, string botText) { diff --git a/src/NadekoBot/_Models/JSONModels/AnimeResult.cs b/src/NadekoBot/Modules/Searches/Commands/Models/AnimeResult.cs similarity index 93% rename from src/NadekoBot/_Models/JSONModels/AnimeResult.cs rename to src/NadekoBot/Modules/Searches/Commands/Models/AnimeResult.cs index a092ae03..d2a6152d 100644 --- a/src/NadekoBot/_Models/JSONModels/AnimeResult.cs +++ b/src/NadekoBot/Modules/Searches/Commands/Models/AnimeResult.cs @@ -1,4 +1,4 @@ -namespace NadekoBot.Classes.JSONModels +namespace NadekoBot.Modules.Searches.Models { public class AnimeResult { diff --git a/src/NadekoBot/_Models/JSONModels/MangaResult.cs b/src/NadekoBot/Modules/Searches/Commands/Models/MangaResult.cs similarity index 94% rename from src/NadekoBot/_Models/JSONModels/MangaResult.cs rename to src/NadekoBot/Modules/Searches/Commands/Models/MangaResult.cs index b7cb9f73..f5269017 100644 --- a/src/NadekoBot/_Models/JSONModels/MangaResult.cs +++ b/src/NadekoBot/Modules/Searches/Commands/Models/MangaResult.cs @@ -1,4 +1,4 @@ -namespace NadekoBot.Classes.JSONModels +namespace NadekoBot.Modules.Searches.Models { public class MangaResult { diff --git a/src/NadekoBot/Modules/Searches/Commands/Models/MeasurementUnit.cs b/src/NadekoBot/Modules/Searches/Commands/Models/MeasurementUnit.cs new file mode 100644 index 00000000..ba4d1bb7 --- /dev/null +++ b/src/NadekoBot/Modules/Searches/Commands/Models/MeasurementUnit.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace NadekoBot.Modules.Searches.Commands.Models +{ + public class MeasurementUnit + { + public List Triggers { get; set; } + public string UnitType { get; set; } + public decimal Modifier { get; set; } + } +} diff --git a/src/NadekoBot/Modules/Searches/Commands/Models/Rates.cs b/src/NadekoBot/Modules/Searches/Commands/Models/Rates.cs new file mode 100644 index 00000000..5aed22fa --- /dev/null +++ b/src/NadekoBot/Modules/Searches/Commands/Models/Rates.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace NadekoBot.Modules.Searches.Commands.Models +{ + public class Rates + { + public string Base { get; set; } + public DateTime Date { get; set; } + [JsonProperty("rates")] + public Dictionary ConversionRates { get; set; } + } +} diff --git a/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs b/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs index 6fd23a29..fffe02e2 100644 --- a/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Searches { _log = LogManager.GetCurrentClassLogger(); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Osu(IUserMessage umsg, string usr, string mode) { @@ -58,7 +58,7 @@ namespace NadekoBot.Modules.Searches } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Osub(IUserMessage umsg, [Remainder] string map) { @@ -95,7 +95,7 @@ namespace NadekoBot.Modules.Searches } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Osu5(IUserMessage umsg, string user, [Remainder] string mode) { diff --git a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs index 38e737d2..e090d09f 100644 --- a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs @@ -15,7 +15,6 @@ namespace NadekoBot.Modules.Searches [Group] public class PokemonSearchCommands { - //todo DB private static Dictionary pokemons = new Dictionary(); private static Dictionary pokemonAbilities = new Dictionary(); @@ -39,7 +38,7 @@ namespace NadekoBot.Modules.Searches _log.Warn(PokemonAbilitiesFile + " is missing. Pokemon abilities not loaded."); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Pokemon(IUserMessage umsg, [Remainder] string pokemon = null) { @@ -60,7 +59,7 @@ namespace NadekoBot.Modules.Searches await channel.SendMessageAsync("`No pokemon found.`"); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task PokemonAbility(IUserMessage umsg, [Remainder] string ability = null) { diff --git a/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs b/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs index 92e14b69..b313bf63 100644 --- a/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs @@ -1,348 +1,288 @@ -//using Discord.Commands; -//using NadekoBot.Classes; -//using Newtonsoft.Json.Linq; -//using System; -//using System.Collections.Concurrent; -//using System.Linq; -//using System.Threading.Tasks; -//using Discord; -//using NadekoBot.Services; -//using System.Threading; +using Discord.Commands; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; +using Discord; +using NadekoBot.Services; +using System.Threading; +using NadekoBot.Services.Database; +using System.Collections.Generic; +using NadekoBot.Services.Database.Models; +using System.Net.Http; +using Discord.WebSocket; +using NadekoBot.Attributes; -//todo DB -//namespace NadekoBot.Modules.Searches -//{ -// public partial class Searches -// { -// [Group] -// public class StreamNotificationCommands -// { -// private readonly Timer checkTimer; -// private ConcurrentDictionary> cachedStatuses = new ConcurrentDictionary>(); -// private bool FirstPass { get; set; } = true; +namespace NadekoBot.Modules.Searches +{ + public partial class Searches + { + [Group] + public class StreamNotificationCommands + { + private Timer checkTimer { get; } + private ConcurrentDictionary> cachedStatuses = new ConcurrentDictionary>(); + private bool FirstPass { get; set; } = true; -// public StreamNotifications(DiscordModule module) -// { -// checkTimer = new Timer(async (state) => -// { -// cachedStatuses.Clear(); -// try -// { -// var streams = SpecificConfigurations.Default.AllConfigs.SelectMany(c => c.ObservingStreams); -// if (!streams.Any()) return; -// foreach (var stream in streams) -// { -// Tuple data; -// try -// { -// data = await GetStreamStatus(stream).ConfigureAwait(false); -// } -// catch -// { -// continue; -// } + public StreamNotificationCommands() + { + checkTimer = new Timer(async (state) => + { + cachedStatuses.Clear(); + try + { + IEnumerable streams; + using (var uow = DbHandler.UnitOfWork()) + { + streams = uow.GuildConfigs.GetAllFollowedStreams(); + } + foreach (var stream in streams) + { + Tuple data; + try + { + data = await GetStreamStatus(stream).ConfigureAwait(false); + } + catch + { + continue; + } -// if (data.Item1 != stream.LastStatus) -// { -// stream.LastStatus = data.Item1; -// if (FirstPass) -// continue; -// var server = NadekoBot.Client.GetServer(stream.ServerId); -// var channel = server?.GetChannel(stream.ChannelId); -// if (channel == null) -// continue; -// var msg = $"`{stream.Username}`'s stream is now " + -// $"**{(data.Item1 ? "ONLINE" : "OFFLINE")}** with " + -// $"**{data.Item2}** viewers."; -// if (stream.LastStatus) -// if (stream.Type == StreamNotificationConfig.StreamType.Hitbox) -// msg += $"\n`Here is the Link:`【 http://www.hitbox.tv/{stream.Username}/ 】"; -// else if (stream.Type == StreamNotificationConfig.StreamType.Twitch) -// msg += $"\n`Here is the Link:`【 http://www.twitch.tv/{stream.Username}/ 】"; -// else if (stream.Type == StreamNotificationConfig.StreamType.Beam) -// msg += $"\n`Here is the Link:`【 http://www.beam.pro/{stream.Username}/ 】"; -// else if (stream.Type == StreamNotificationConfig.StreamType.YoutubeGaming) -// msg += $"\n`Here is the Link:`【 not implemented yet - {stream.Username} 】"; -// await channel.SendMessageAsync(msg).ConfigureAwait(false); -// } -// } -// FirstPass = false; -// } -// catch { } -// }, null, TimeSpan.Zero, TimeSpan.FromSeconds(15)); -// } + if (data.Item1 != stream.LastStatus) + { + stream.LastStatus = data.Item1; + if (FirstPass) + continue; + var server = NadekoBot.Client.GetGuild(stream.GuildId); + var channel = server?.GetTextChannel(stream.ChannelId); + if (channel == null) + continue; + var msg = $"`{stream.Username}`'s stream is now " + + $"**{(data.Item1 ? "ONLINE" : "OFFLINE")}** with " + + $"**{data.Item2}** viewers."; + if (stream.LastStatus) + if (stream.Type == FollowedStream.FollowedStreamType.Hitbox) + msg += $"\n`Here is the Link:`【 http://www.hitbox.tv/{stream.Username}/ 】"; + else if (stream.Type == FollowedStream.FollowedStreamType.Twitch) + msg += $"\n`Here is the Link:`【 http://www.twitch.tv/{stream.Username}/ 】"; + else if (stream.Type == FollowedStream.FollowedStreamType.Beam) + msg += $"\n`Here is the Link:`【 http://www.beam.pro/{stream.Username}/ 】"; + //else if (stream.Type == FollowedStream.FollowedStreamType.YoutubeGaming) + // msg += $"\n`Here is the Link:`【 not implemented yet - {stream.Username} 】"; + await channel.SendMessageAsync(msg).ConfigureAwait(false); + } + } + FirstPass = false; + } + catch { } + }, null, TimeSpan.Zero, TimeSpan.FromSeconds(15)); + } -// public StreamNotifications(ILocalization loc, CommandService cmds, IBotConfiguration config, DiscordSocketClient client) : base(loc, cmds, config, client) -// { -// } + private async Task> GetStreamStatus(FollowedStream stream, bool checkCache = true) + { + bool isLive; + string response; + JObject data; + Tuple result; + switch (stream.Type) + { + case FollowedStream.FollowedStreamType.Hitbox: + var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username}"; + if (checkCache && cachedStatuses.TryGetValue(hitboxUrl, out result)) + return result; + using (var http = new HttpClient()) + { + response = await http.GetStringAsync(hitboxUrl).ConfigureAwait(false); + } + data = JObject.Parse(response); + isLive = data["media_is_live"].ToString() == "1"; + result = new Tuple(isLive, data["media_views"].ToString()); + cachedStatuses.TryAdd(hitboxUrl, result); + return result; + case FollowedStream.FollowedStreamType.Twitch: + var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username)}"; + if (checkCache && cachedStatuses.TryGetValue(twitchUrl, out result)) + return result; + using (var http = new HttpClient()) + { + response = await http.GetStringAsync(twitchUrl).ConfigureAwait(false); + } + data = JObject.Parse(response); + isLive = !string.IsNullOrWhiteSpace(data["stream"].ToString()); + result = new Tuple(isLive, isLive ? data["stream"]["viewers"].ToString() : "0"); + cachedStatuses.TryAdd(twitchUrl, result); + return result; + case FollowedStream.FollowedStreamType.Beam: + var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username}"; + if (checkCache && cachedStatuses.TryGetValue(beamUrl, out result)) + return result; + using (var http = new HttpClient()) + { + response = await http.GetStringAsync(beamUrl).ConfigureAwait(false); + } + data = JObject.Parse(response); + isLive = data["online"].ToObject() == true; + result = new Tuple(isLive, data["viewersCurrent"].ToString()); + cachedStatuses.TryAdd(beamUrl, result); + return result; + default: + break; + } + return new Tuple(false, "0"); + } -// private async Task> GetStreamStatus(StreamNotificationConfig stream, bool checkCache = true) -// { -// bool isLive; -// string response; -// JObject data; -// Tuple result; -// switch (stream.Type) -// { -// case StreamNotificationConfig.StreamType.Hitbox: -// var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username}"; -// if (checkCache && cachedStatuses.TryGetValue(hitboxUrl, out result)) -// return result; -// response = await http.GetStringAsync(hitboxUrl).ConfigureAwait(false); -// data = JObject.Parse(response); -// isLive = data["media_is_live"].ToString() == "1"; -// result = new Tuple(isLive, data["media_views"].ToString()); -// cachedStatuses.TryAdd(hitboxUrl, result); -// return result; -// case StreamNotificationConfig.StreamType.Twitch: -// var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username)}"; -// if (checkCache && cachedStatuses.TryGetValue(twitchUrl, out result)) -// return result; -// response = await http.GetStringAsync(twitchUrl).ConfigureAwait(false); -// data = JObject.Parse(response); -// isLive = !string.IsNullOrWhiteSpace(data["stream"].ToString()); -// result = new Tuple(isLive, isLive ? data["stream"]["viewers"].ToString() : "0"); -// cachedStatuses.TryAdd(twitchUrl, result); -// return result; -// case StreamNotificationConfig.StreamType.Beam: -// var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username}"; -// if (checkCache && cachedStatuses.TryGetValue(beamUrl, out result)) -// return result; -// response = await http.GetStringAsync(beamUrl).ConfigureAwait(false); -// data = JObject.Parse(response); -// isLive = data["online"].ToObject() == true; -// result = new Tuple(isLive, data["viewersCurrent"].ToString()); -// cachedStatuses.TryAdd(beamUrl, result); -// return result; -// default: -// break; -// } -// return new Tuple(false, "0"); -// } + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageMessages)] + public async Task Hitbox(IUserMessage msg, [Remainder] string username) => + await TrackStream((ITextChannel)msg.Channel, username, FollowedStream.FollowedStreamType.Hitbox) + .ConfigureAwait(false); -// internal override void Init(CommandGroupBuilder cgb) -// { -// cgb.CreateCommand(Module.Prefix + "hitbox") -// .Alias(Module.Prefix + "hb") -// .Description("Notifies this channel when a certain user starts streaming." + -// $" | `{Prefix}hitbox SomeStreamer`") -// .Parameter("username", ParameterType.Unparsed) -// .AddCheck(SimpleCheckers.ManageServer()) -// .Do(TrackStream(StreamNotificationConfig.StreamType.Hitbox)); + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageMessages)] + public async Task Twitch(IUserMessage msg, [Remainder] string username) => + await TrackStream((ITextChannel)msg.Channel, username, FollowedStream.FollowedStreamType.Twitch) + .ConfigureAwait(false); -// cgb.CreateCommand(Module.Prefix + "twitch") -// .Alias(Module.Prefix + "tw") -// .Description("Notifies this channel when a certain user starts streaming." + -// $" | `{Prefix}twitch SomeStreamer`") -// .AddCheck(SimpleCheckers.ManageServer()) -// .Parameter("username", ParameterType.Unparsed) -// .Do(TrackStream(StreamNotificationConfig.StreamType.Twitch)); + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + [RequirePermission(GuildPermission.ManageMessages)] + public async Task Beam(IUserMessage msg, [Remainder] string username) => + await TrackStream((ITextChannel)msg.Channel, username, FollowedStream.FollowedStreamType.Beam) + .ConfigureAwait(false); -// cgb.CreateCommand(Module.Prefix + "beam") -// .Alias(Module.Prefix + "bm") -// .Description("Notifies this channel when a certain user starts streaming." + -// $" | `{Prefix}beam SomeStreamer`") -// .AddCheck(SimpleCheckers.ManageServer()) -// .Parameter("username", ParameterType.Unparsed) -// .Do(TrackStream(StreamNotificationConfig.StreamType.Beam)); + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task ListStreams(IUserMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; -// cgb.CreateCommand(Module.Prefix + "checkhitbox") -// .Alias(Module.Prefix + "chhb") -// .Description("Checks if a certain user is streaming on the hitbox platform." + -// $" | `{Prefix}chhb SomeStreamer`") -// .Parameter("username", ParameterType.Unparsed) -// .AddCheck(SimpleCheckers.ManageServer()) -// .Do(async e => -// { -// var stream = username?.Trim(); -// if (string.IsNullOrWhiteSpace(stream)) -// return; -// try -// { -// var streamStatus = (await GetStreamStatus(new StreamNotificationConfig -// { -// Username = stream, -// Type = StreamNotificationConfig.StreamType.Hitbox -// })); -// if (streamStatus.Item1) -// { -// await channel.SendMessageAsync($"`Streamer {streamStatus.Item2} is online.`"); -// } -// } -// catch -// { -// await channel.SendMessageAsync("No channel found."); -// } -// }); + IEnumerable streams; + using (var uow = DbHandler.UnitOfWork()) + { + streams = uow.GuildConfigs.For(channel.Guild.Id).FollowedStreams; + } -// cgb.CreateCommand(Module.Prefix + "checktwitch") -// .Alias(Module.Prefix + "chtw") -// .Description("Checks if a certain user is streaming on the twitch platform." + -// $" | `{Prefix}chtw SomeStreamer`") -// .AddCheck(SimpleCheckers.ManageServer()) -// .Parameter("username", ParameterType.Unparsed) -// .Do(async e => -// { -// var stream = username?.Trim(); -// if (string.IsNullOrWhiteSpace(stream)) -// return; -// try -// { -// var streamStatus = (await GetStreamStatus(new StreamNotificationConfig -// { -// Username = stream, -// Type = StreamNotificationConfig.StreamType.Twitch -// })); -// if (streamStatus.Item1) -// { -// await channel.SendMessageAsync($"`Streamer {streamStatus.Item2} is online.`"); -// } -// } -// catch -// { -// await channel.SendMessageAsync("No channel found."); -// } -// }); + if (!streams.Any()) + { + await channel.SendMessageAsync("You are not following any streams on this server.").ConfigureAwait(false); + return; + } -// cgb.CreateCommand(Module.Prefix + "checkbeam") -// .Alias(Module.Prefix + "chbm") -// .Description("Checks if a certain user is streaming on the beam platform." + -// $" | `{Prefix}chbm SomeStreamer`") -// .AddCheck(SimpleCheckers.ManageServer()) -// .Parameter("username", ParameterType.Unparsed) -// .Do(async e => -// { -// var stream = username?.Trim(); -// if (string.IsNullOrWhiteSpace(stream)) -// return; -// try -// { -// var streamStatus = (await GetStreamStatus(new StreamNotificationConfig -// { -// Username = stream, -// Type = StreamNotificationConfig.StreamType.Beam -// })); -// if (streamStatus.Item1) -// { -// await channel.SendMessageAsync($"`Streamer {streamStatus.Item2} is online.`"); -// } -// } -// catch -// { -// await channel.SendMessageAsync("No channel found."); -// } -// }); + var text = string.Join("\n", streams.Select(snc => + { + return $"`{snc.Username}`'s stream on **{channel.Guild.GetTextChannel(snc.ChannelId)?.Name}** channel. 【`{snc.Type.ToString()}`】"; + })); -// cgb.CreateCommand(Module.Prefix + "removestream") -// .Alias(Module.Prefix + "rms") -// .Description("Removes notifications of a certain streamer on this channel." + -// $" | `{Prefix}rms SomeGuy`") -// .AddCheck(SimpleCheckers.ManageServer()) -// .Parameter("username", ParameterType.Unparsed) -// .Do(async e => -// { -// var username = username?.ToLower().Trim(); -// if (string.IsNullOrWhiteSpace(username)) -// return; + await channel.SendMessageAsync($"You are following **{streams.Count()}** streams on this server.\n\n" + text).ConfigureAwait(false); + } -// var config = SpecificConfigurations.Default.Of(e.Server.Id); + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task RemoveStream(IUserMessage msg, [Remainder] string username) + { + var channel = (ITextChannel)msg.Channel; -// var toRemove = config.ObservingStreams -// .FirstOrDefault(snc => snc.ChannelId == e.Channel.Id && -// snc.Username.ToLower().Trim() == username); -// if (toRemove == null) -// { -// await channel.SendMessageAsync(":anger: No such stream.").ConfigureAwait(false); -// return; -// } + username = username.ToUpperInvariant().Trim(); -// config.ObservingStreams.Remove(toRemove); -// await ConfigHandler.SaveConfig().ConfigureAwait(false); -// await channel.SendMessageAsync($":ok: Removed `{toRemovumsg.Authorname}`'s stream from notifications.").ConfigureAwait(false); -// }); + FollowedStream toRemove; + using (var uow = DbHandler.UnitOfWork()) + { + var config = uow.GuildConfigs.For(channel.Guild.Id); + var streams = config.FollowedStreams; + toRemove = streams.Where(fs => fs.ChannelId == channel.Id && fs.Username.ToUpperInvariant() == username).FirstOrDefault(); + if (toRemove != null) + { + config.FollowedStreams = streams.Except(new[] { toRemove }).ToList(); + await uow.CompleteAsync(); + } + } + if (toRemove == null) + { + await channel.SendMessageAsync(":anger: No such stream.").ConfigureAwait(false); + return; + } + await channel.SendMessageAsync($":ok: Removed `{toRemove.Username}`'s stream ({toRemove.Type}) from notifications.").ConfigureAwait(false); + } -// cgb.CreateCommand(Module.Prefix + "liststreams") -// .Alias(Module.Prefix + "ls") -// .Description("Lists all streams you are following on this server." + -// $" | `{Prefix}ls`") -// .Do(async e => -// { + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task CheckStream(IUserMessage imsg, FollowedStream.FollowedStreamType platform, [Remainder] string username) + { + var channel = (ITextChannel)imsg.Channel; -// var config = SpecificConfigurations.Default.Of(e.Server.Id); + var stream = username?.Trim(); + if (string.IsNullOrWhiteSpace(stream)) + return; + try + { + var streamStatus = (await GetStreamStatus(new FollowedStream + { + Username = stream, + Type = platform + })); + if (streamStatus.Item1) + { + await channel.SendMessageAsync($"`Streamer {streamStatus.Item2} is online.`"); + } + } + catch + { + await channel.SendMessageAsync("No channel found."); + } + } -// var streams = config.ObservingStreams.Where(snc => -// snc.ServerId == e.Server.Id); - -// var streamsArray = streams as StreamNotificationConfig[] ?? streams.ToArray(); - -// if (streamsArray.Length == 0) -// { -// await channel.SendMessageAsync("You are not following any streams on this server.").ConfigureAwait(false); -// return; -// } - -// var text = string.Join("\n", streamsArray.Select(snc => -// { -// try -// { -// return $"`{snc.Username}`'s stream on **{e.Server.GetChannel(e.Channel.Id).Name}** channel. 【`{snc.Type.ToString()}`】"; -// } -// catch { } -// return ""; -// })); - -// await channel.SendMessageAsync($"You are following **{streamsArray.Length}** streams on this server.\n\n" + text).ConfigureAwait(false); -// }); -// } - -// private Func TrackStream(StreamNotificationConfig.StreamType type) => -// async e => -// { -// var username = username?.ToLowerInvariant(); -// if (string.IsNullOrWhiteSpace(username)) -// return; - -// var config = SpecificConfigurations.Default.Of(e.Server.Id); - -// var stream = new StreamNotificationConfig -// { -// ServerId = e.Server.Id, -// ChannelId = e.Channel.Id, -// Username = username, -// Type = type, -// }; -// var exists = config.ObservingStreams.Contains(stream); -// if (exists) -// { -// await channel.SendMessageAsync(":anger: I am already notifying that stream on this channel.").ConfigureAwait(false); -// return; -// } -// Tuple data; -// try -// { -// data = await GetStreamStatus(stream).ConfigureAwait(false); -// } -// catch -// { -// await channel.SendMessageAsync(":anger: Stream probably doesn't exist.").ConfigureAwait(false); -// return; -// } -// var msg = $"Stream is currently **{(data.Item1 ? "ONLINE" : "OFFLINE")}** with **{data.Item2}** viewers"; -// if (data.Item1) -// if (type == StreamNotificationConfig.StreamType.Hitbox) -// msg += $"\n`Here is the Link:`【 http://www.hitbox.tv/{stream.Username}/ 】"; -// else if (type == StreamNotificationConfig.StreamType.Twitch) -// msg += $"\n`Here is the Link:`【 http://www.twitch.tv/{stream.Username}/ 】"; -// else if (type == StreamNotificationConfig.StreamType.Beam) -// msg += $"\n`Here is the Link:`【 https://beam.pro/{stream.Username}/ 】"; -// else if (type == StreamNotificationConfig.StreamType.YoutubeGaming) -// msg += $"\n`Here is the Link:` not implemented yet - {stream.Username}"; -// stream.LastStatus = data.Item1; -// if (!exists) -// msg = $":ok: I will notify this channel when status changes.\n{msg}"; -// await channel.SendMessageAsync(msg).ConfigureAwait(false); -// config.ObservingStreams.Add(stream); -// }; -// } -// } -//} \ No newline at end of file + private async Task TrackStream(ITextChannel channel, string username, FollowedStream.FollowedStreamType type) + { + username = username.ToUpperInvariant().Trim(); + var stream = new FollowedStream + { + GuildId = channel.Guild.Id, + ChannelId = channel.Id, + Username = username, + Type = type, + }; + bool exists; + using (var uow = DbHandler.UnitOfWork()) + { + exists = uow.GuildConfigs.For(channel.Guild.Id).FollowedStreams.Where(fs => fs.ChannelId == channel.Id && fs.Username.ToUpperInvariant().Trim() == username).Any(); + } + if (exists) + { + await channel.SendMessageAsync($":anger: I am already following `{username}` ({type}) stream on this channel.").ConfigureAwait(false); + return; + } + Tuple data; + try + { + data = await GetStreamStatus(stream).ConfigureAwait(false); + } + catch + { + await channel.SendMessageAsync(":anger: Stream probably doesn't exist.").ConfigureAwait(false); + return; + } + var msg = $"Stream is currently **{(data.Item1 ? "ONLINE" : "OFFLINE")}** with **{data.Item2}** viewers"; + if (data.Item1) + if (type == FollowedStream.FollowedStreamType.Hitbox) + msg += $"\n`Here is the Link:`【 http://www.hitbox.tv/{stream.Username}/ 】"; + else if (type == FollowedStream.FollowedStreamType.Twitch) + msg += $"\n`Here is the Link:`【 http://www.twitch.tv/{stream.Username}/ 】"; + else if (type == FollowedStream.FollowedStreamType.Beam) + msg += $"\n`Here is the Link:`【 https://beam.pro/{stream.Username}/ 】"; + //else if (type == FollowedStream.FollowedStreamType.YoutubeGaming) + // msg += $"\n`Here is the Link:` not implemented yet - {stream.Username}"; + stream.LastStatus = data.Item1; + using (var uow = DbHandler.UnitOfWork()) + { + uow.GuildConfigs.For(channel.Guild.Id).FollowedStreams.Add(stream); + await uow.CompleteAsync(); + } + msg = $":ok: I will notify this channel when status changes.\n{msg}"; + await channel.SendMessageAsync(msg).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Commands/UnitConversion.cs b/src/NadekoBot/Modules/Searches/Commands/UnitConversion.cs new file mode 100644 index 00000000..64422620 --- /dev/null +++ b/src/NadekoBot/Modules/Searches/Commands/UnitConversion.cs @@ -0,0 +1,201 @@ +using Discord; +using Discord.Commands; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Modules.Searches.Commands.Models; +using NadekoBot.Services; +using NadekoBot.Services.Database.Models; +using Newtonsoft.Json; +using NLog; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace NadekoBot.Modules.Searches +{ + public partial class Searches + { + [Group] + public class UnitConverterCommands + { + private Logger _log; + private static Timer _timer; + public static TimeSpan Span = new TimeSpan(12, 0, 0); + public UnitConverterCommands() + { + _log = LogManager.GetCurrentClassLogger(); + + try + { + using (var uow = DbHandler.UnitOfWork()) + { + //need to do this the first time + if (uow.ConverterUnits.Empty()) + { + var content = JsonConvert.DeserializeObject>(File.ReadAllText("units.json")).Select(u => new ConvertUnit() + { + Modifier = u.Modifier, + UnitType = u.UnitType, + InternalTrigger = string.Join("|", u.Triggers) + }); + + uow.ConverterUnits.AddRange(content.ToArray()); + uow.Complete(); + } + Units = uow.ConverterUnits.GetAll().ToList(); + } + } + catch (Exception e) + { + _log.Warn("Could not load units: " + e.Message); + } + + + + _timer = new Timer(new TimerCallback(UpdateCurrency), null, 0,(int)Span.TotalMilliseconds); + + } + + public void UpdateCurrency(object stateInfo) + { + var currencyRates = UpdateCurrencyRates().Result; + var unitTypeString = "currency"; + using (var uow = DbHandler.UnitOfWork()) + { + var toRemove = Units.Where(u => u.UnitType == unitTypeString); + Units.RemoveAll(u => u.UnitType == unitTypeString); + uow.ConverterUnits.RemoveRange(toRemove.ToArray()); + var baseType = new ConvertUnit() + { + Triggers = new[] { currencyRates.Base }, + Modifier = decimal.One, + UnitType = unitTypeString + }; + uow.ConverterUnits.Add(baseType); + Units.Add(baseType); + var range = currencyRates.ConversionRates.Select(u => new ConvertUnit() + { + InternalTrigger = u.Key, + Modifier = u.Value, + UnitType = unitTypeString + }).ToArray(); + uow.ConverterUnits.AddRange(range); + Units.AddRange(range); + + uow.Complete(); + } + _log.Info("Updated Currency"); + } + + public List Units { get; set; } + + + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task ConvertListE(IUserMessage msg) //extended and bugged list + { + var channel = msg.Channel as IGuildChannel; + + var sb = new StringBuilder("Units that can be used by the converter: \n"); + var res = Units.GroupBy(x => x.UnitType); + foreach (var group in res) + { + sb.AppendLine($"{group.Key}: ```xl"); + foreach (var el in group) + { + sb.Append($" [{string.Join(",", el.Triggers)}] "); + } + sb.AppendLine("```"); + } + await msg.ReplyLong(sb.ToString(), breakOn: new[] { "```xl", "\n" }); + } + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + [RequireContext(ContextType.Guild)] + public async Task ConvertList(IUserMessage msg) + { + var sb = new StringBuilder("Units that can be used by the converter: \n"); + var res = Units.GroupBy(x => x.UnitType); + foreach (var group in res) + { + sb.AppendLine($"{group.Key}: ```xl"); + sb.AppendLine(string.Join(",", group.Select(x => x.Triggers.FirstOrDefault()).OrderBy(x => x))); + sb.AppendLine("```"); + } + await msg.ReplyLong(sb.ToString(), breakOn: new[] { "```xl\n", "\n" }); + } + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] + public async Task Convert(IUserMessage msg, string origin, string target, decimal value) + { + var originUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(origin.ToLowerInvariant())); + var targetUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant())); + if (originUnit == null || targetUnit == null) + { + await msg.Reply(string.Format("Cannot convert {0} to {1}: units not found", origin, target)); + return; + } + if (originUnit.UnitType != targetUnit.UnitType) + { + await msg.Reply(string.Format("Cannot convert {0} to {1}: types of unit are not equal", originUnit.Triggers.First(), targetUnit.Triggers.First())); + return; + } + decimal res; + if (originUnit.Triggers == targetUnit.Triggers) res = value; + else if (originUnit.UnitType == "temperature") + { + //don't really care too much about efficiency, so just convert to Kelvin, then to target + switch (originUnit.Triggers.First().ToUpperInvariant()) + { + case "C": + res = value + (decimal)273.15; //celcius! + break; + case "F": + res = (value + (decimal)459.67) * ((decimal)5 / 9); + break; + default: + res = value; + break; + } + //from Kelvin to target + switch (targetUnit.Triggers.First()) + { + case "C": + res = value - (decimal)273.15; //celcius! + break; + case "F": + res = res * ((decimal)9 / 5) - (decimal)458.67; + break; + default: + break; + } + } + else + { + //I just love currency + if (originUnit.UnitType == "currency") + { + res = (value * targetUnit.Modifier) / originUnit.Modifier; + } + else + res = (value * originUnit.Modifier) / targetUnit.Modifier; + } + res = Math.Round(res, 2); + await msg.Reply(string.Format("{0} {1} is equal to {2} {3}", value, originUnit.Triggers.First(), res, targetUnit.Triggers.First())); + } + } + + public static async Task UpdateCurrencyRates() + { + using (var http = new HttpClient()) + { + var res = await http.GetStringAsync("http://api.fixer.io/latest").ConfigureAwait(false); + return JsonConvert.DeserializeObject(res); + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 99aeb95b..2e987145 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -16,7 +16,7 @@ using NadekoBot.Modules.Searches.IMDB; namespace NadekoBot.Modules.Searches { - [Module("~", AppendSpace = false)] + [NadekoModule("Searches", "~")] public partial class Searches : DiscordModule { private IGoogleApiService _google { get; } @@ -26,7 +26,7 @@ namespace NadekoBot.Modules.Searches _google = youtube; } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Weather(IUserMessage umsg, string city, string country) { @@ -47,7 +47,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 🌄 **Sunrise:** {obj["sunrise"]} 🌇 **Sunset:** {obj["sunset"]}").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Youtube(IUserMessage umsg, [Remainder] string query = null) { @@ -62,7 +62,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 await channel.SendMessageAsync(result).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Imdb(IUserMessage umsg, [Remainder] string query = null) { @@ -86,7 +86,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 await channel.SendMessageAsync(result.ToString()).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task RandomCat(IUserMessage umsg) { @@ -99,7 +99,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task RandomDog(IUserMessage umsg) { @@ -110,7 +110,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task I(IUserMessage umsg, [Remainder] string query = null) { @@ -140,7 +140,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Ir(IUserMessage umsg, [Remainder] string query = null) { @@ -152,7 +152,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 { using (var http = new HttpClient()) { - var rng = new Random(); + var rng = new NadekoRandom(); var reqString = $"https://www.googleapis.com/customsearch/v1?q={Uri.EscapeDataString(query)}&cx=018084019232060951019%3Ahs5piey28-e&num=1&searchType=image&start={ rng.Next(1, 50) }&fields=items%2Flink&key={NadekoBot.Credentials.GoogleApiKey}"; var obj = JObject.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false)); var items = obj["items"] as JArray; @@ -172,7 +172,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Lmgtfy(IUserMessage umsg, [Remainder] string ffs = null) { @@ -186,7 +186,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 .ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Google(IUserMessage umsg, [Remainder] string terms = null) { @@ -200,7 +200,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 .ConfigureAwait(false); } ////todo drawing - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Hearthstone(IUserMessage umsg, [Remainder] string name = null) //{ @@ -245,7 +245,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 // } //} - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Ud(IUserMessage umsg, [Remainder] string query = null) { @@ -279,7 +279,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Hashtag(IUserMessage umsg, [Remainder] string query = null) { @@ -314,7 +314,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Catfact(IUserMessage umsg) { @@ -328,7 +328,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Revav(IUserMessage umsg, [Remainder] string arg = null) { @@ -338,14 +338,14 @@ $@"🌍 **Weather for** 【{obj["target"]}】 if (string.IsNullOrWhiteSpace(usrStr)) return; - var usr = (await channel.Guild.GetUsersAsync()).Where(u => u.Username.ToUpperInvariant() == usrStr).FirstOrDefault(); + var usr = channel.Guild.GetUsers().Where(u => u.Username.ToUpperInvariant() == usrStr).FirstOrDefault(); if (usr == null || string.IsNullOrWhiteSpace(usr.AvatarUrl)) return; await channel.SendMessageAsync($"https://images.google.com/searchbyimage?image_url={usr.AvatarUrl}").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Revimg(IUserMessage umsg, [Remainder] string imageLink = null) { @@ -357,7 +357,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 await channel.SendMessageAsync($"https://images.google.com/searchbyimage?image_url={imageLink}").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Safebooru(IUserMessage umsg, [Remainder] string tag = null) { @@ -371,7 +371,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 await channel.SendMessageAsync(link).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Wiki(IUserMessage umsg, [Remainder] string query = null) { @@ -392,7 +392,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 } ////todo drawing - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task Clr(IUserMessage umsg, [Remainder] string color = null) //{ @@ -417,7 +417,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 // await channel.SendFileAsync("arg1.png", img.ToStream()); //} - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Videocall(IUserMessage umsg, [Remainder] string arg = null) { @@ -428,7 +428,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 var allUsrs = umsg.MentionedUsers.Append(umsg.Author); var allUsrsArray = allUsrs.ToArray(); var str = allUsrsArray.Aggregate("http://appear.in/", (current, usr) => current + Uri.EscapeUriString(usr.Username[0].ToString())); - str += new Random().Next(); + str += new NadekoRandom().Next(); foreach (var usr in allUsrsArray) { await (await (usr as IGuildUser).CreateDMChannelAsync()).SendMessageAsync(str).ConfigureAwait(false); @@ -440,7 +440,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Avatar(IUserMessage umsg, [Remainder] string mention = null) { @@ -457,7 +457,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 public static async Task GetSafebooruImageLink(string tag) { - var rng = new Random(); + var rng = new NadekoRandom(); var url = $"http://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags={tag.Replace(" ", "_")}"; using (var http = new HttpClient()) diff --git a/src/NadekoBot/Modules/Translator/Translator.cs b/src/NadekoBot/Modules/Translator/Translator.cs index 425d9b0c..e974746a 100644 --- a/src/NadekoBot/Modules/Translator/Translator.cs +++ b/src/NadekoBot/Modules/Translator/Translator.cs @@ -9,14 +9,14 @@ using Discord.WebSocket; namespace NadekoBot.Modules.Translator { - [Module("~", AppendSpace = false)] + [NadekoModule("Translator", "~")] public class Translator : DiscordModule { public Translator(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) { } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Translate(IUserMessage umsg, string langs, [Remainder] string text = null) { @@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Translator } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Translangs(IUserMessage umsg) { diff --git a/src/NadekoBot/Modules/Trello/Trello.cs b/src/NadekoBot/Modules/Trello/Trello.cs index 18e1a4d5..fbda6fab 100644 --- a/src/NadekoBot/Modules/Trello/Trello.cs +++ b/src/NadekoBot/Modules/Trello/Trello.cs @@ -11,7 +11,7 @@ ////todo rewrite //namespace NadekoBot.Modules.Trello //{ -// internal class Trello : DiscordModule +// public class Trello : DiscordModule // { // private readonly Timer t = new Timer { Interval = 2000 }; // public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Trello; diff --git a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index 7102addf..7b5a9ef1 100644 --- a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -1,5 +1,6 @@ using Discord; using Discord.Commands; +using Discord.WebSocket; using NadekoBot.Attributes; using NadekoBot.Extensions; using System; @@ -11,7 +12,7 @@ namespace NadekoBot.Modules.Utility { partial class Utility : DiscordModule { - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ServerInfo(IUserMessage msg, string guild = null) { @@ -21,7 +22,7 @@ namespace NadekoBot.Modules.Utility if (guild == null) server = channel.Guild; else - server = (await _client.GetGuildsAsync()).Where(g => g.Name.ToUpperInvariant() == guild.ToUpperInvariant()).FirstOrDefault(); + server = _client.GetGuilds().Where(g => g.Name.ToUpperInvariant() == guild.ToUpperInvariant()).FirstOrDefault(); if (server == null) return; @@ -46,7 +47,7 @@ namespace NadekoBot.Modules.Utility await msg.Reply(sb.ToString()).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ChannelInfo(IUserMessage msg, ITextChannel channel = null) { @@ -62,7 +63,7 @@ namespace NadekoBot.Modules.Utility await msg.Reply(toReturn).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task UserInfo(IUserMessage msg, IGuildUser usr = null) { diff --git a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs index d1206ee6..98f62442 100644 --- a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs @@ -14,7 +14,7 @@ namespace NadekoBot.Modules.Utility { public partial class Utility { - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ShowQuote(IUserMessage umsg, string keyword) { @@ -37,7 +37,7 @@ namespace NadekoBot.Modules.Utility await channel.SendMessageAsync("📣 " + quote.Text); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task AddQuote(IUserMessage umsg, string keyword, [Remainder] string text) { @@ -63,7 +63,7 @@ namespace NadekoBot.Modules.Utility } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task DeleteQuote(IUserMessage umsg, string keyword) { @@ -90,7 +90,7 @@ namespace NadekoBot.Modules.Utility await channel.SendMessageAsync("`Deleted a random quote.`"); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task DelAllQuotes(IUserMessage umsg, string keyword) { diff --git a/src/NadekoBot/Modules/Utility/Commands/Remind.cs b/src/NadekoBot/Modules/Utility/Commands/Remind.cs index d115e921..ba48f1e9 100644 --- a/src/NadekoBot/Modules/Utility/Commands/Remind.cs +++ b/src/NadekoBot/Modules/Utility/Commands/Remind.cs @@ -67,12 +67,7 @@ namespace NadekoBot.Modules.Utility } else { - ch = NadekoBot.Client.GetGuilds() - .Where(g => g.Id == r.ServerId) - .FirstOrDefault() - .GetTextChannels() - .Where(c => c.Id == r.ChannelId) - .FirstOrDefault(); + ch = NadekoBot.Client.GetGuild(r.ServerId)?.GetTextChannel(r.ChannelId); } if (ch == null) return; @@ -97,7 +92,7 @@ namespace NadekoBot.Modules.Utility } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Remind(IUserMessage umsg, string meorchannel, string timeStr, [Remainder] string message) { @@ -190,7 +185,7 @@ namespace NadekoBot.Modules.Utility } ////todo owner only - //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] //[RequireContext(ContextType.Guild)] //public async Task RemindTemplate(IUserMessage umsg, [Remainder] string arg) //{ diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 07499f5a..2464d5c9 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -15,7 +15,7 @@ using Discord.WebSocket; namespace NadekoBot.Modules.Utility { - [Module(".", AppendSpace = false)] + [NadekoModule("Utility", ".")] public partial class Utility : DiscordModule { public Utility(ILocalization loc, CommandService cmds, DiscordSocketClient client) : base(loc, cmds, client) @@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Utility } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task WhosPlaying(IUserMessage umsg, [Remainder] string game = null) { @@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Utility await channel.SendMessageAsync("```xl\n" + string.Join("\n", arr.GroupBy(item => (i++) / 3).Select(ig => string.Concat(ig.Select(el => $"• {el,-35}")))) + "\n```").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task InRole(IUserMessage umsg, [Remainder] string roles = null) { @@ -76,7 +76,7 @@ namespace NadekoBot.Modules.Utility await channel.SendMessageAsync(send).ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task CheckMyPerms(IUserMessage msg) { @@ -93,7 +93,7 @@ namespace NadekoBot.Modules.Utility await msg.Reply(builder.ToString()); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task UserId(IUserMessage msg, IGuildUser target = null) { @@ -101,20 +101,20 @@ namespace NadekoBot.Modules.Utility await msg.Reply($"Id of the user { usr.Username } is { usr.Id })").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] public async Task ChannelId(IUserMessage msg) { await msg.Reply($"This Channel's ID is {msg.Channel.Id}").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ServerId(IUserMessage msg) { await msg.Reply($"This server's ID is {(msg.Channel as ITextChannel).Guild.Id}").ConfigureAwait(false); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Roles(IUserMessage msg, IGuildUser target = null) { @@ -129,7 +129,7 @@ namespace NadekoBot.Modules.Utility } } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task ChannelTopic(IUserMessage umsg) { @@ -142,7 +142,7 @@ namespace NadekoBot.Modules.Utility await channel.SendMessageAsync("`Topic:` " + topic); } - [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [LocalizedCommand, LocalizedDescription, LocalizedSummary, LocalizedAlias] [RequireContext(ContextType.Guild)] public async Task Stats(IUserMessage umsg) { diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 89e37170..38d15dc8 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -2,6 +2,7 @@ using Discord.Commands; using Discord.WebSocket; using NadekoBot.Services; +using NadekoBot.Services.Database; using NadekoBot.Services.Impl; using NLog; using NLog.Config; @@ -11,6 +12,7 @@ using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using NLog.Fluent; namespace NadekoBot { @@ -19,22 +21,26 @@ namespace NadekoBot private Logger _log; public static CommandService Commands { get; private set; } + public static CommandHandler CommandHandler { get; private set; } public static DiscordSocketClient Client { get; private set; } public static Localization Localizer { get; private set; } public static BotCredentials Credentials { get; private set; } - public static GoogleApiService Google { get; set; } + public static GoogleApiService Google { get; private set; } public static StatsService Stats { get; private set; } public async Task RunAsync(string[] args) { SetupLogger(); + _log = LogManager.GetCurrentClassLogger(); + + _log.Info("Starting NadekoBot v" + typeof(NadekoBot).GetTypeInfo().Assembly.GetCustomAttribute().InformationalVersion); //create client Client = new DiscordSocketClient(new DiscordSocketConfig { AudioMode = Discord.Audio.AudioMode.Outgoing, - LargeThreshold = 200, + MessageCacheSize = 10, LogLevel = LogSeverity.Warning, }); @@ -43,8 +49,14 @@ namespace NadekoBot Commands = new CommandService(); Localizer = new Localization(); Google = new GoogleApiService(); - Stats = new StatsService(Client); - _log = LogManager.GetCurrentClassLogger(); + CommandHandler = new CommandHandler(Client, Commands); + Stats = new StatsService(Client, CommandHandler); + + //init db + using (var context = DbHandler.Instance.GetDbContext()) + { + context.EnsureSeedData(); + } //setup DI var depMap = new DependencyMap(); @@ -54,16 +66,16 @@ namespace NadekoBot depMap.Add(Google); //connect - await Client.LoginAsync(TokenType.Bot, Credentials.Token); - await Client.ConnectAsync(); + await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false); + await Client.ConnectAsync().ConfigureAwait(false); + await Client.DownloadAllUsersAsync().ConfigureAwait(false); _log.Info("Connected"); //load commands - await Commands.LoadAssembly(Assembly.GetEntryAssembly(), depMap); - Client.MessageReceived += Client_MessageReceived; + await Commands.LoadAssembly(Assembly.GetEntryAssembly(), depMap).ConfigureAwait(false); - Console.WriteLine(await Stats.Print()); + Console.WriteLine(await Stats.Print().ConfigureAwait(false)); await Task.Delay(-1); } @@ -83,57 +95,10 @@ namespace NadekoBot LogManager.Configuration = logConfig; } - catch (Exception ex) { + catch (Exception ex) + { Console.WriteLine(ex); } } - - private Task Client_MessageReceived(IMessage umsg) - { - var usrMsg = umsg as IUserMessage; - if (usrMsg == null) - return Task.CompletedTask; - var throwaway = Task.Run(async () => - { - var sw = new Stopwatch(); - sw.Start(); - var t = await Commands.Execute(usrMsg, usrMsg.Content); - sw.Stop(); - var channel = (umsg.Channel as ITextChannel); - if (t.IsSuccess) - { - - _log.Info("Command Executed after {4}s\n\t" + - "User: {0}\n\t" + - "Server: {1}\n\t" + - "Channel: {2}\n\t" + - "Message: {3}", - umsg.Author + " [" + umsg.Author.Id + "]", // {0} - (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} - (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), //{2} - umsg.Content, // {3} - sw.Elapsed.TotalSeconds // {4} - ); - } - else if (!t.IsSuccess && t.Error != CommandError.UnknownCommand) - { - _log.Warn("Command Errored after {5}s\n\t" + - "User: {0}\n\t" + - "Server: {1}\n\t" + - "Channel: {2}\n\t" + - "Message: {3}\n\t" + - "Error: {4}", - umsg.Author + " [" + umsg.Author.Id + "]", // {0} - (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} - (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), //{2} - umsg.Content,// {3} - t.ErrorReason, // {4} - sw.Elapsed.TotalSeconds //{5} - ); - } - }); - - return Task.CompletedTask; - } } } diff --git a/src/NadekoBot/Program.cs b/src/NadekoBot/Program.cs index 4d2274c5..81336f9c 100644 --- a/src/NadekoBot/Program.cs +++ b/src/NadekoBot/Program.cs @@ -2,6 +2,7 @@ { public class Program { - public static void Main(string[] args) => new NadekoBot().RunAsync(args).GetAwaiter().GetResult(); + public static void Main(string[] args) => + new NadekoBot().RunAsync(args).GetAwaiter().GetResult(); } } diff --git a/src/NadekoBot/Properties/AssemblyInfo.cs b/src/NadekoBot/Properties/AssemblyInfo.cs index 7f939e99..9a06963a 100644 --- a/src/NadekoBot/Properties/AssemblyInfo.cs +++ b/src/NadekoBot/Properties/AssemblyInfo.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("NadekoBot")] [assembly: AssemblyTrademark("")] +[assembly: AssemblyInformationalVersion("1.0-alpha")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 380f6767..ed63b4ed 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -86,33 +86,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to Try to get 'abalabahaha'. - /// - public static string ab_desc { - get { - return ResourceManager.GetString("ab_desc", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to `@NadekoBot ab`. - /// - public static string ab_summary { - get { - return ResourceManager.GetString("ab_summary", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ab. - /// - public static string ab_text { - get { - return ResourceManager.GetString("ab_text", resourceCulture); - } - } - /// /// Looks up a localized string similar to Add a custom reaction. Guide here: <https://github.com/Kwoth/NadekoBot/wiki/Custom-Reactions> **Bot Owner Only!**. /// @@ -132,7 +105,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to addcustreact. + /// Looks up a localized string similar to addcustreact acr. /// public static string addcustreact_text { get { @@ -159,7 +132,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to addfilterword. + /// Looks up a localized string similar to addfilterword afw. /// public static string addfilterword_text { get { @@ -186,7 +159,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to addplaying. + /// Looks up a localized string similar to addplaying adpl. /// public static string addplaying_text { get { @@ -240,7 +213,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to allchnlcmds. + /// Looks up a localized string similar to allchnlcmds acc. /// public static string allchnlcmds_text { get { @@ -267,7 +240,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to allchnlmdls. + /// Looks up a localized string similar to allchnlmdls acm. /// public static string allchnlmdls_text { get { @@ -321,7 +294,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to allrolecmds. + /// Looks up a localized string similar to allrolecmds acmdcds. /// public static string allrolecmds_text { get { @@ -348,7 +321,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to allrolemdls. + /// Looks up a localized string similar to allrolemdls arm. /// public static string allrolemdls_text { get { @@ -375,7 +348,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to allsrvrcmds. + /// Looks up a localized string similar to allsrvrcmds asc. /// public static string allsrvrcmds_text { get { @@ -402,7 +375,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to allsrvrmdls. + /// Looks up a localized string similar to allsrvrmdls asm. /// public static string allsrvrmdls_text { get { @@ -429,7 +402,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to ani. + /// Looks up a localized string similar to ani anime aq. /// public static string ani_text { get { @@ -537,7 +510,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to autoassignrole. + /// Looks up a localized string similar to autoassignrole aar. /// public static string autoassignrole_text { get { @@ -564,7 +537,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to autoplay. + /// Looks up a localized string similar to autoplay ap. /// public static string autoplay_text { get { @@ -591,7 +564,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to av. + /// Looks up a localized string similar to av avatar. /// public static string avatar_text { get { @@ -645,7 +618,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to ban. + /// Looks up a localized string similar to ban b. /// public static string ban_text { get { @@ -672,7 +645,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to beam. + /// Looks up a localized string similar to beam bm. /// public static string beam_text { get { @@ -699,7 +672,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to betflip. + /// Looks up a localized string similar to betflip bf. /// public static string betflip_text { get { @@ -726,7 +699,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to betroll. + /// Looks up a localized string similar to betroll br. /// public static string betroll_text { get { @@ -807,7 +780,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to butts. + /// Looks up a localized string similar to butts ass butt. /// public static string butts_text { get { @@ -942,7 +915,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to calculate. + /// Looks up a localized string similar to calculate calc. /// public static string calculate_text { get { @@ -977,6 +950,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Check how much NadekoFlowers a person has. (Defaults to yourself). + /// + public static string cash_desc { + get { + return ResourceManager.GetString("cash_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `$$$` or `$$$ @SomeGuy`. + /// + public static string cash_summary { + get { + return ResourceManager.GetString("cash_summary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to cash $$. + /// + public static string cash_text { + get { + return ResourceManager.GetString("cash_text", resourceCulture); + } + } + /// /// Looks up a localized string similar to Shows a random catfact from <http://catfacts-api.appspot.com/api/facts>. /// @@ -1077,7 +1077,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to channelinfo. + /// Looks up a localized string similar to channelinfo cinfo. /// public static string channelinfo_text { get { @@ -1104,7 +1104,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to channeltopic. + /// Looks up a localized string similar to channeltopic ct. /// public static string channeltopic_text { get { @@ -1131,7 +1131,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to checkbeam. + /// Looks up a localized string similar to checkbeam chbm. /// public static string checkbeam_text { get { @@ -1158,7 +1158,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to checkhitbox. + /// Looks up a localized string similar to checkhitbox chhb. /// public static string checkhitbox_text { get { @@ -1212,7 +1212,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to checktwitch. + /// Looks up a localized string similar to checktwitch chtw. /// public static string checktwitch_text { get { @@ -1239,7 +1239,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to chnlcmd. + /// Looks up a localized string similar to chnlcmd cc. /// public static string chnlcmd_text { get { @@ -1266,7 +1266,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to chnlfilterinv. + /// Looks up a localized string similar to chnlfilterinv cfi. /// public static string chnlfilterinv_text { get { @@ -1293,7 +1293,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to chnlfilterwords. + /// Looks up a localized string similar to chnlfilterwords cfw. /// public static string chnlfilterwords_text { get { @@ -1320,7 +1320,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to chnlmdl. + /// Looks up a localized string similar to chnlmdl cm. /// public static string chnlmdl_text { get { @@ -1347,7 +1347,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to chnlperms. + /// Looks up a localized string similar to chnlperms cp. /// public static string chnlperms_text { get { @@ -1374,7 +1374,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to chnlpermscopy. + /// Looks up a localized string similar to chnlpermscopy cpc. /// public static string chnlpermscopy_text { get { @@ -1428,7 +1428,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to chucknorris. + /// Looks up a localized string similar to chucknorris cn. /// public static string chucknorris_text { get { @@ -1455,7 +1455,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to claim. + /// Looks up a localized string similar to claim call c. /// public static string claim_text { get { @@ -1482,7 +1482,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to claimfinish. + /// Looks up a localized string similar to claimfinish cf cf3 claimfinish3. /// public static string claimfinish_text { get { @@ -1509,7 +1509,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to claimfinish1. + /// Looks up a localized string similar to claimfinish1 cf1. /// public static string claimfinish1_text { get { @@ -1536,7 +1536,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to claimfinish2. + /// Looks up a localized string similar to claimfinish2 cf2. /// public static string claimfinish2_text { get { @@ -1590,7 +1590,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to cleanvplust. + /// Looks up a localized string similar to cleanvplust cv+t. /// public static string cleanvplust_text { get { @@ -1617,7 +1617,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to clr. + /// Looks up a localized string similar to clr prune. /// public static string clr_text { get { @@ -1644,7 +1644,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to cmdcooldown. + /// Looks up a localized string similar to cmdcooldown cmdcd. /// public static string cmdcooldown_text { get { @@ -1779,7 +1779,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to createrole. + /// Looks up a localized string similar to createrole cr. /// public static string createrole_text { get { @@ -1806,7 +1806,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to createwar. + /// Looks up a localized string similar to createwar cw. /// public static string createwar_text { get { @@ -1833,7 +1833,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to creatvoichanl. + /// Looks up a localized string similar to creatvoichanl cvch. /// public static string creatvoichanl_text { get { @@ -1860,7 +1860,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to creatxtchanl. + /// Looks up a localized string similar to creatxtchanl ctch. /// public static string creatxtchanl_text { get { @@ -1941,7 +1941,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to deafen. + /// Looks up a localized string similar to deafen deaf. /// public static string deafen_text { get { @@ -1968,7 +1968,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to defvol. + /// Looks up a localized string similar to defvol dv. /// public static string defvol_text { get { @@ -1995,7 +1995,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to delallq. + /// Looks up a localized string similar to delallq daq. /// public static string delallquotes_text { get { @@ -2022,7 +2022,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to delcustreact. + /// Looks up a localized string similar to delcustreact dcr. /// public static string delcustreact_text { get { @@ -2049,7 +2049,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to deleteplaylist. + /// Looks up a localized string similar to deleteplaylist delpls. /// public static string deleteplaylist_text { get { @@ -2076,7 +2076,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to deletequote. + /// Looks up a localized string similar to deletequote delq. /// public static string deletequote_text { get { @@ -2130,7 +2130,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to deltxtchanl. + /// Looks up a localized string similar to deltxtchanl dtch. /// public static string deltxtchanl_text { get { @@ -2157,7 +2157,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to delvoichanl. + /// Looks up a localized string similar to delvoichanl dvch. /// public static string delvoichanl_text { get { @@ -2184,7 +2184,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to destroy. + /// Looks up a localized string similar to destroy d. /// public static string destroy_text { get { @@ -2454,7 +2454,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to editcustreact. + /// Looks up a localized string similar to editcustreact ecr. /// public static string editcustreact_text { get { @@ -2481,7 +2481,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to endwar. + /// Looks up a localized string similar to endwar ew. /// public static string endwar_text { get { @@ -2589,7 +2589,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to gencurrency. + /// Looks up a localized string similar to gencurrency gc. /// public static string gencurrency_text { get { @@ -2616,7 +2616,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to getlink. + /// Looks up a localized string similar to getlink gl. /// public static string getlink_text { get { @@ -2670,7 +2670,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to google. + /// Looks up a localized string similar to google g. /// public static string google_text { get { @@ -2859,7 +2859,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to readme. + /// Looks up a localized string similar to readme guide. /// public static string guide_text { get { @@ -2886,7 +2886,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to h. + /// Looks up a localized string similar to h help. /// public static string h_text { get { @@ -3075,7 +3075,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to hitbox. + /// Looks up a localized string similar to hitbox hb. /// public static string hitbox_text { get { @@ -3156,7 +3156,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to i. + /// Looks up a localized string similar to img i. /// public static string i_text { get { @@ -3210,7 +3210,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to iamnot. + /// Looks up a localized string similar to iamnot iamn. /// public static string iamnot_text { get { @@ -3345,7 +3345,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to joinrace. + /// Looks up a localized string similar to joinrace jr. /// public static string joinrace_text { get { @@ -3372,7 +3372,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to kick. + /// Looks up a localized string similar to kick k. /// public static string kick_text { get { @@ -3426,7 +3426,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to leaderboard. + /// Looks up a localized string similar to leaderboard lb. /// public static string leaderboard_text { get { @@ -3534,7 +3534,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to listallincidents. + /// Looks up a localized string similar to listallincidents lain. /// public static string listallincidents_text { get { @@ -3561,7 +3561,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to listcustreact. + /// Looks up a localized string similar to listcustreact lcr. /// public static string listcustreact_text { get { @@ -3588,7 +3588,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to listincidents. + /// Looks up a localized string similar to listincidents lin. /// public static string listincidents_text { get { @@ -3615,7 +3615,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to listplaying. + /// Looks up a localized string similar to listplaying lipl. /// public static string listplaying_text { get { @@ -3642,7 +3642,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to listqueue. + /// Looks up a localized string similar to listqueue lq. /// public static string listqueue_text { get { @@ -3669,7 +3669,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to lists. + /// Looks up a localized string similar to lists list. /// public static string lists_text { get { @@ -3696,7 +3696,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to liststreams. + /// Looks up a localized string similar to liststreams ls. /// public static string liststreams_text { get { @@ -3723,7 +3723,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to listwar. + /// Looks up a localized string similar to listwar lw. /// public static string listwar_text { get { @@ -3804,7 +3804,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to local. + /// Looks up a localized string similar to local lo. /// public static string local_text { get { @@ -3831,7 +3831,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to localplaylst. + /// Looks up a localized string similar to localplaylst lopl. /// public static string localpl_text { get { @@ -3993,7 +3993,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to lstfilterwords. + /// Looks up a localized string similar to lstfilterwords lfw. /// public static string lstfilterwords_text { get { @@ -4020,7 +4020,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to magicitem. + /// Looks up a localized string similar to magicitem mi. /// public static string magicitem_text { get { @@ -4031,27 +4031,27 @@ namespace NadekoBot.Resources { /// /// Looks up a localized string similar to Queries anilist for a manga and shows the first result.. /// - public static string mang_desc { + public static string manga_desc { get { - return ResourceManager.GetString("mang_desc", resourceCulture); + return ResourceManager.GetString("manga_desc", resourceCulture); } } /// /// Looks up a localized string similar to `~mq Shingeki no kyojin`. /// - public static string mang_summary { + public static string manga_summary { get { - return ResourceManager.GetString("mang_summary", resourceCulture); + return ResourceManager.GetString("manga_summary", resourceCulture); } } /// - /// Looks up a localized string similar to mang. + /// Looks up a localized string similar to manga mang mq. /// - public static string mang_text { + public static string manga_text { get { - return ResourceManager.GetString("mang_text", resourceCulture); + return ResourceManager.GetString("manga_text", resourceCulture); } } @@ -4155,7 +4155,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to mentionrole. + /// Looks up a localized string similar to mentionrole menro. /// public static string mentionrole_text { get { @@ -4209,7 +4209,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to move. + /// Looks up a localized string similar to move mv. /// public static string move_text { get { @@ -4236,7 +4236,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to movelist. + /// Looks up a localized string similar to movelist ml. /// public static string movelist_text { get { @@ -4263,7 +4263,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to movesong. + /// Looks up a localized string similar to movesong ms. /// public static string movesong_text { get { @@ -4290,7 +4290,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to mute. + /// Looks up a localized string similar to mute min. /// public static string mute_text { get { @@ -4317,7 +4317,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to newavatar. + /// Looks up a localized string similar to newavatar setavatar. /// public static string newavatar_text { get { @@ -4344,7 +4344,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to next. + /// Looks up a localized string similar to next n. /// public static string next_text { get { @@ -4371,7 +4371,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to nowplaying. + /// Looks up a localized string similar to nowplaying np. /// public static string nowplaying_text { get { @@ -4506,7 +4506,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to pause. + /// Looks up a localized string similar to pause p. /// public static string pause_text { get { @@ -4533,7 +4533,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to permrole. + /// Looks up a localized string similar to permrole pr. /// public static string permrole_text { get { @@ -4614,7 +4614,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to playlist. + /// Looks up a localized string similar to playlist pl. /// public static string playlist_text { get { @@ -4641,7 +4641,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to playlists. + /// Looks up a localized string similar to playlists pls. /// public static string playlists_text { get { @@ -4668,7 +4668,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to pokemon. + /// Looks up a localized string similar to pokemon poke. /// public static string pokemon_text { get { @@ -4695,7 +4695,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to pokemonability. + /// Looks up a localized string similar to pokemonability pokeab. /// public static string pokemonability_text { get { @@ -4776,7 +4776,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to prune. + /// Looks up a localized string similar to prune clr. /// public static string prune_text { get { @@ -4803,7 +4803,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to queue. + /// Looks up a localized string similar to queue q yq. /// public static string queue_text { get { @@ -4857,7 +4857,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to radio. + /// Looks up a localized string similar to radio ra. /// public static string radio_text { get { @@ -4911,7 +4911,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to randjoke. + /// Looks up a localized string similar to randjoke rj. /// public static string randjoke_text { get { @@ -4938,7 +4938,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to randomcat. + /// Looks up a localized string similar to randomcat meow. /// public static string randomcat_text { get { @@ -4965,7 +4965,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to randomdog. + /// Looks up a localized string similar to randomdog woof. /// public static string randomdog_text { get { @@ -5046,7 +5046,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to remove. + /// Looks up a localized string similar to remove rm. /// public static string remove_text { get { @@ -5073,7 +5073,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to removeallroles. + /// Looks up a localized string similar to removeallroles rar. /// public static string removeallroles_text { get { @@ -5100,7 +5100,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to removeplaying. + /// Looks up a localized string similar to removeplaying rmlp repl. /// public static string removeplaying_text { get { @@ -5127,7 +5127,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to removerole. + /// Looks up a localized string similar to removerole rr. /// public static string removerole_text { get { @@ -5154,7 +5154,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to removestream. + /// Looks up a localized string similar to removestream rms. /// public static string removestream_text { get { @@ -5181,7 +5181,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to renamerole. + /// Looks up a localized string similar to renamerole renr. /// public static string renamerole_text { get { @@ -5235,7 +5235,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to repeatinvoke. + /// Looks up a localized string similar to repeatinvoke repinv. /// public static string repeatinvoke_text { get { @@ -5262,7 +5262,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to rpeatplaylst. + /// Looks up a localized string similar to rpeatplaylst rpl. /// public static string repeatpl_text { get { @@ -5289,7 +5289,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to reptcursong. + /// Looks up a localized string similar to reptcursong rcs. /// public static string reptcursong_text { get { @@ -5361,7 +5361,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to `~revav Image link`. + /// Looks up a localized string similar to `~revimg Image link`. /// public static string revimg_summary { get { @@ -5424,7 +5424,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to rmvfilterword. + /// Looks up a localized string similar to rmvfilterword rw. /// public static string rmvfilterword_text { get { @@ -5451,7 +5451,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to rolecmd. + /// Looks up a localized string similar to rolecmd rc. /// public static string rolecmd_text { get { @@ -5478,7 +5478,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to rolecolor. + /// Looks up a localized string similar to rolecolor rc. /// public static string rolecolor_text { get { @@ -5505,7 +5505,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to rolemdl. + /// Looks up a localized string similar to rolemdl rm. /// public static string rolemdl_text { get { @@ -5532,7 +5532,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to roleperms. + /// Looks up a localized string similar to roleperms rp. /// public static string roleperms_text { get { @@ -5559,7 +5559,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to rolepermscopy. + /// Looks up a localized string similar to rolepermscopy rpc. /// public static string rolepermscopy_text { get { @@ -5667,7 +5667,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to rotateplaying. + /// Looks up a localized string similar to rotateplaying ropl. /// public static string rotateplaying_text { get { @@ -5937,7 +5937,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to serverid. + /// Looks up a localized string similar to serverid sid. /// public static string serverid_text { get { @@ -5964,7 +5964,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to serverinfo. + /// Looks up a localized string similar to serverinfo sinfo. /// public static string serverinfo_text { get { @@ -5991,7 +5991,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to setchanlname. + /// Looks up a localized string similar to setchanlname schn. /// public static string setchanlname_text { get { @@ -6045,7 +6045,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to setmaxqueue. + /// Looks up a localized string similar to setmaxqueue smq. /// public static string setmaxqueue_text { get { @@ -6072,7 +6072,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to setname. + /// Looks up a localized string similar to setname newnm. /// public static string setname_text { get { @@ -6099,7 +6099,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to setrole. + /// Looks up a localized string similar to setrole sr. /// public static string setrole_text { get { @@ -6126,7 +6126,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to settopic. + /// Looks up a localized string similar to settopic st. /// public static string settopic_text { get { @@ -6180,7 +6180,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to showcustreact. + /// Looks up a localized string similar to showcustreact scr. /// public static string showcustreact_text { get { @@ -6234,7 +6234,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to shuffle. + /// Looks up a localized string similar to shuffle sh. /// public static string shuffle_text { get { @@ -6288,7 +6288,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to softban. + /// Looks up a localized string similar to softban sb. /// public static string softban_text { get { @@ -6315,7 +6315,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to soundcloudpl. + /// Looks up a localized string similar to soundcloudpl scpl. /// public static string soundcloudpl_text { get { @@ -6342,7 +6342,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to soundcloudqueue. + /// Looks up a localized string similar to soundcloudqueue sq. /// public static string soundcloudqueue_text { get { @@ -6396,7 +6396,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to srvrcmd. + /// Looks up a localized string similar to srvrcmd sc. /// public static string srvrcmd_text { get { @@ -6423,7 +6423,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to srvrfilterinv. + /// Looks up a localized string similar to srvrfilterinv sfi. /// public static string srvrfilterinv_text { get { @@ -6450,7 +6450,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to srvrfilterwords. + /// Looks up a localized string similar to srvrfilterwords sfw. /// public static string srvrfilterwords_text { get { @@ -6477,7 +6477,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to srvrmdl. + /// Looks up a localized string similar to srvrmdl sm. /// public static string srvrmdl_text { get { @@ -6504,7 +6504,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to srvrperms. + /// Looks up a localized string similar to srvrperms sp. /// public static string srvrperms_text { get { @@ -6531,7 +6531,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to startwar. + /// Looks up a localized string similar to startwar sw. /// public static string startwar_text { get { @@ -6585,7 +6585,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to stop. + /// Looks up a localized string similar to stop s. /// public static string stop_text { get { @@ -6612,7 +6612,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to t. + /// Looks up a localized string similar to trivia t. /// public static string t_text { get { @@ -6666,7 +6666,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to tesar. + /// Looks up a localized string similar to togglexclsar tesar. /// public static string tesar_text { get { @@ -6774,7 +6774,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to translate. + /// Looks up a localized string similar to translate trans. /// public static string translate_text { get { @@ -6801,7 +6801,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to twitch. + /// Looks up a localized string similar to twitch tw. /// public static string twitch_text { get { @@ -6944,33 +6944,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to Searches Urban Dictionary for a word.. - /// - public static string ud_desc { - get { - return ResourceManager.GetString("ud_desc", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to `~ud Pineapple`. - /// - public static string ud_summary { - get { - return ResourceManager.GetString("ud_summary", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ud. - /// - public static string ud_text { - get { - return ResourceManager.GetString("ud_text", resourceCulture); - } - } - /// /// Looks up a localized string similar to Unbinds a bot from the channel and board. **Bot Owner Only!**. /// @@ -7017,7 +6990,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to unclaim. + /// Looks up a localized string similar to unclaim ucall uc. /// public static string unclaim_text { get { @@ -7044,7 +7017,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to undeafen. + /// Looks up a localized string similar to undeafen undef. /// public static string undeafen_text { get { @@ -7106,6 +7079,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Searches Urban Dictionary for a word.. + /// + public static string urbandict_desc { + get { + return ResourceManager.GetString("urbandict_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `~ud Pineapple`. + /// + public static string urbandict_summary { + get { + return ResourceManager.GetString("urbandict_summary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to urbandict ud. + /// + public static string urbandict_text { + get { + return ResourceManager.GetString("urbandict_text", resourceCulture); + } + } + /// /// Looks up a localized string similar to Shows user ID.. /// @@ -7125,7 +7125,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to userid. + /// Looks up a localized string similar to userid uid. /// public static string userid_text { get { @@ -7152,7 +7152,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to userinfo. + /// Looks up a localized string similar to userinfo uinfo. /// public static string userinfo_text { get { @@ -7179,7 +7179,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to userperms. + /// Looks up a localized string similar to userperms up. /// public static string userperms_text { get { @@ -7233,7 +7233,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to usrcmd. + /// Looks up a localized string similar to usrcmd uc. /// public static string usrcmd_text { get { @@ -7260,7 +7260,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to usrmdl. + /// Looks up a localized string similar to usrmdl um. /// public static string usrmdl_text { get { @@ -7287,7 +7287,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to usrpermscopy. + /// Looks up a localized string similar to usrpermscopy upc. /// public static string usrpermscopy_text { get { @@ -7341,7 +7341,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to verbose. + /// Looks up a localized string similar to verbose v. /// public static string verbose_text { get { @@ -7395,7 +7395,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to voiceplustext. + /// Looks up a localized string similar to voice+text v+t. /// public static string voiceplustext_text { get { @@ -7449,7 +7449,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to volume. + /// Looks up a localized string similar to volume vol. /// public static string volume_text { get { @@ -7476,7 +7476,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to we. + /// Looks up a localized string similar to weather we. /// public static string weather_text { get { @@ -7530,7 +7530,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to wiki. + /// Looks up a localized string similar to wikipedia wiki. /// public static string wiki_text { get { @@ -7584,7 +7584,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to yomama. + /// Looks up a localized string similar to yomama ym. /// public static string yomama_text { get { @@ -7611,7 +7611,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to yt. + /// Looks up a localized string similar to youtube yt. /// public static string youtube_text { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index d18d35b8..b126ce83 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - h + h help Either shows a help for a single command, or PMs you help link if no arguments are specified. @@ -271,7 +271,7 @@ `.voicerpresence` - repeatinvoke + repeatinvoke repinv Immediately shows the repeat message and restarts the timer. **Needs Manage Messages Permissions.** @@ -289,7 +289,7 @@ `.repeat 5 Hello there` - rotateplaying + rotateplaying ropl Toggles rotation of playing status of the dynamic strings you specified earlier. **Bot Owner Only!** @@ -298,7 +298,7 @@ `.ropl` - addplaying + addplaying adpl Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %trivia% **Bot Owner Only!** @@ -307,7 +307,7 @@ `.adpl` - listplaying + listplaying lipl Lists all playing statuses with their corresponding number. **Bot Owner Only!** @@ -316,7 +316,7 @@ `.lipl` - removeplaying + removeplaying rmlp repl Removes a playing string on a given number. **Bot Owner Only!** @@ -334,7 +334,7 @@ `.slowmode` - cleanvplust + cleanvplust cv+t Deletes all text channels ending in `-voice` for which voicechannels are not found. **Use at your own risk. Needs Manage Roles and Manage Channels Permissions.** @@ -343,7 +343,7 @@ `.cleanv+t` - voiceplustext + voice+text v+t Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Needs Manage Roles and Manage Channels Permissions.** @@ -406,7 +406,7 @@ `.lsar` - tesar + togglexclsar tesar toggle whether the self-assigned roles should be exclusive @@ -424,7 +424,7 @@ `.iam Gamer` - iamnot + iamnot iamn Removes a role to you that you choose. Role must be on a list of self-assignable roles. @@ -433,7 +433,7 @@ `.iamn Gamer` - addcustreact + addcustreact acr Add a custom reaction. Guide here: <https://github.com/Kwoth/NadekoBot/wiki/Custom-Reactions> **Bot Owner Only!** @@ -442,7 +442,7 @@ `.acr "hello" Hi there %user%` - listcustreact + listcustreact lcr Lists custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. @@ -451,7 +451,7 @@ `.lcr 1` - showcustreact + showcustreact scr Shows all possible responses from a single custom reaction. @@ -460,7 +460,7 @@ `.scr %mention% bb` - editcustreact + editcustreact ecr Edits a custom reaction, arguments are custom reactions name, index to change, and a (multiword) message **Bot Owner Only** @@ -469,7 +469,7 @@ `.ecr "%mention% disguise" 2 Test 123` - delcustreact + delcustreact dcr Deletes a custom reaction with given name (and index). **Bot Owner Only.** @@ -478,7 +478,7 @@ `.dcr index` - autoassignrole + autoassignrole aar Automaticaly assigns a specified role to every user who joins the server. **Needs Manage Roles Permissions.** @@ -496,7 +496,7 @@ `.leave 123123123331` - listincidents + listincidents lin List all UNREAD incidents and flags them as read. **Needs Manage Server Permissions.** @@ -505,7 +505,7 @@ `.lin` - listallincidents + listallincidents lain Sends you a file containing all incidents and flags them as read. **Needs Manage Server Permissions.** @@ -532,7 +532,7 @@ `.restart` - setrole + setrole sr Sets a role for a given user. **Needs Manage Roles Permissions.** @@ -541,7 +541,7 @@ `.sr @User Guest` - removerole + removerole rr Removes a role from a given user. **Needs Manage Roles Permissions.** @@ -550,7 +550,7 @@ `.rr @User Admin` - renamerole + renamerole renr Renames a role. Roles you are renaming must be lower than bot's highest role. **Manage Roles Permissions.** @@ -559,7 +559,7 @@ `.renr "First role" SecondRole` - removeallroles + removeallroles rar Removes all roles from a mentioned user. **Needs Manage Roles Permissions.** @@ -568,7 +568,7 @@ `.rar @User` - createrole + createrole cr Creates a role with a given name. **Needs Manage Roles Permissions.** @@ -577,7 +577,7 @@ `.cr Awesome Role` - rolecolor + rolecolor rc Set a role's color to the hex or 0-255 rgb color value provided. **Needs Manage Roles Permissions.** @@ -586,7 +586,7 @@ `.rc Admin 255 200 100` or `.rc Admin ffba55` - ban + ban b Bans a user by id or name with an optional message. **Needs Ban Permissions.** @@ -595,7 +595,7 @@ `.b "@some Guy" Your behaviour is toxic.` - softban + softban sb Bans and then unbans a user by id or name with an optional message. **Needs Ban Permissions.** @@ -604,7 +604,7 @@ `.sb "@some Guy" Your behaviour is toxic.` - kick + kick k Kicks a mentioned user. **Needs Kick Permissions.** @@ -613,7 +613,7 @@ `.k "@some Guy" Your behaviour is toxic.` - mute + mute min Sets the music volume to 0% @@ -631,7 +631,7 @@ `.unmute "@Someguy"` or `.unmute "@Someguy" "@Someguy"` - deafen + deafen deaf Deafens mentioned user or users. **Needs Deafen Permissions.** @@ -640,7 +640,7 @@ `.deaf "@Someguy"` or `.deaf "@Someguy" "@Someguy"` - undeafen + undeafen undef Undeafens mentioned user or users. **Needs Deafen Permissions.** @@ -649,7 +649,7 @@ `.undef "@Someguy"` or `.undef "@Someguy" "@Someguy"` - delvoichanl + delvoichanl dvch Deletes a voice channel with a given name. **Needs Manage Channel Permissions.** @@ -658,7 +658,7 @@ `.dvch VoiceChannelName` - creatvoichanl + creatvoichanl cvch Creates a new voice channel with a given name. **Needs Manage Channel Permissions.** @@ -667,7 +667,7 @@ `.cvch VoiceChannelName` - deltxtchanl + deltxtchanl dtch Deletes a text channel with a given name. **Needs Manage Channel Permissions.** @@ -676,7 +676,7 @@ `.dtch TextChannelName` - creatxtchanl + creatxtchanl ctch Creates a new text channel with a given name. **Needs Manage Channel Permissions.** @@ -685,7 +685,7 @@ `.ctch TextChannelName` - settopic + settopic st Sets a topic on the current channel. **Needs Manage Channel Permissions.** @@ -694,7 +694,7 @@ `.st My new topic` - setchanlname + setchanlname schn Changed the name of the current channel. **Needs Manage Channel Permissions.** @@ -712,7 +712,7 @@ `.heap` - prune + prune clr `.prune` removes all nadeko's messages in the last 100 messages.`.prune X` removes last X messages from the channel (up to 100)`.prune @Someone` removes all Someone's messages in the last 100 messages.`.prune @Someone X` removes last X 'Someone's' messages in the channel. **Needs Manage Messages Permissions** @@ -730,7 +730,7 @@ `@NadekoBot die` - setname + setname newnm Give the bot a new name. **Bot Owner Only!** @@ -739,7 +739,7 @@ `.newnm BotName` - newavatar + newavatar setavatar Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!** @@ -766,7 +766,7 @@ `.send sid - mentionrole + mentionrole menro Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. @@ -838,7 +838,7 @@ `.remindmsg do something else` - serverinfo + serverinfo sinfo Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. @@ -847,7 +847,7 @@ `.sinfo Some Server` - channelinfo + channelinfo cinfo Shows info about the channel. If no channel is supplied, it defaults to current one. @@ -856,7 +856,7 @@ `.cinfo #some-channel` - userinfo + userinfo uinfo Shows info about the user. If no user is supplied, it defaults a user running the command. @@ -910,7 +910,7 @@ `.dysyd` - userid + userid uid Shows user ID. @@ -928,7 +928,7 @@ `.cid` - serverid + serverid sid Shows current server ID. @@ -946,7 +946,7 @@ `.roles` - channeltopic + channeltopic ct Sends current channel's topic as a message. @@ -955,7 +955,7 @@ `.ct` - chnlfilterinv + chnlfilterinv cfi Enables or disables automatic deleting of invites on the channel.If no channel supplied, it will default to current one. Use ALL to apply to all existing channels at once. @@ -964,7 +964,7 @@ `;cfi enable #general-chat` - srvrfilterinv + srvrfilterinv sfi Enables or disables automatic deleting of invites on the server. @@ -973,7 +973,7 @@ `;sfi disable` - chnlfilterwords + chnlfilterwords cfw Enables or disables automatic deleting of messages containing banned words on the channel.If no channel supplied, it will default to current one. Use ALL to apply to all existing channels at once. @@ -982,7 +982,7 @@ `;cfw enable #general-chat` - addfilterword + addfilterword afw Adds a new word to the list of filtered words @@ -991,7 +991,7 @@ `;afw poop` - rmvfilterword + rmvfilterword rw Removes the word from the list of filtered words @@ -1000,7 +1000,7 @@ `;rw poop` - lstfilterwords + lstfilterwords lfw Shows a list of filtered words @@ -1009,7 +1009,7 @@ `;lfw` - srvrfilterwords + srvrfilterwords sfw Enables or disables automatic deleting of messages containing forbidden words on the server. @@ -1018,7 +1018,7 @@ `;sfw disable` - permrole + permrole pr Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. @@ -1027,7 +1027,7 @@ `;pr role` - rolepermscopy + rolepermscopy rpc Copies BOT PERMISSIONS (not discord permissions) from one role to another. @@ -1036,7 +1036,7 @@ `;rpc Some Role ~ Some other role` - chnlpermscopy + chnlpermscopy cpc Copies BOT PERMISSIONS (not discord permissions) from one channel to another. @@ -1045,7 +1045,7 @@ `;cpc Some Channel ~ Some other channel` - usrpermscopy + usrpermscopy upc Copies BOT PERMISSIONS (not discord permissions) from one role to another. @@ -1054,7 +1054,7 @@ `;upc @SomeUser ~ @SomeOtherUser` - verbose + verbose v Sets whether to show when a command/module is blocked. @@ -1063,7 +1063,7 @@ `;verbose true` - srvrperms + srvrperms sp Shows banned permissions for this server. @@ -1072,7 +1072,7 @@ `;sp` - roleperms + roleperms rp Shows banned permissions for a certain role. No argument means for everyone. @@ -1081,7 +1081,7 @@ `;rp AwesomeRole` - chnlperms + chnlperms cp Shows banned permissions for a certain channel. No argument means for this channel. @@ -1090,7 +1090,7 @@ `;cp #dev` - userperms + userperms up Shows banned permissions for a certain user. No argument means for yourself. @@ -1099,7 +1099,7 @@ `;up Kwoth` - srvrmdl + srvrmdl sm Sets a module's permission at the server level. @@ -1108,7 +1108,7 @@ `;sm "module name" enable` - srvrcmd + srvrcmd sc Sets a command's permission at the server level. @@ -1117,7 +1117,7 @@ `;sc "command name" disable` - rolemdl + rolemdl rm Sets a module's permission at the role level. @@ -1126,7 +1126,7 @@ `;rm "module name" enable MyRole` - rolecmd + rolecmd rc Sets a command's permission at the role level. @@ -1135,7 +1135,7 @@ `;rc "command name" disable MyRole` - chnlmdl + chnlmdl cm Sets a module's permission at the channel level. @@ -1144,7 +1144,7 @@ `;cm "module name" enable SomeChannel` - chnlcmd + chnlcmd cc Sets a command's permission at the channel level. @@ -1153,7 +1153,7 @@ `;cc "command name" enable SomeChannel` - usrmdl + usrmdl um Sets a module's permission at the user level. @@ -1162,7 +1162,7 @@ `;um "module name" enable SomeUsername` - usrcmd + usrcmd uc Sets a command's permission at the user level. @@ -1171,7 +1171,7 @@ `;uc "command name" enable SomeUsername` - allsrvrmdls + allsrvrmdls asm Sets permissions for all modules at the server level. @@ -1180,7 +1180,7 @@ `;asm [enable/disable]` - allsrvrcmds + allsrvrcmds asc Sets permissions for all commands from a certain module at the server level. @@ -1189,7 +1189,7 @@ `;asc "module name" [enable/disable]` - allchnlmdls + allchnlmdls acm Sets permissions for all modules at the channel level. @@ -1198,7 +1198,7 @@ `;acm [enable/disable] SomeChannel` - allchnlcmds + allchnlcmds acc Sets permissions for all commands from a certain module at the channel level. @@ -1207,7 +1207,7 @@ `;acc "module name" [enable/disable] SomeChannel` - allrolemdls + allrolemdls arm Sets permissions for all modules at the role level. @@ -1216,7 +1216,7 @@ `;arm [enable/disable] MyRole` - allrolecmds + allrolecmds acmdcds Sets permissions for all commands from a certain module at the role level. @@ -1270,7 +1270,7 @@ `;sbl [servername/serverid]` - cmdcooldown + cmdcooldown cmdcd Sets a cooldown per user for a command. Set 0 to clear. **Needs Manager Messages Permissions** @@ -1306,7 +1306,7 @@ `... abc` - deletequote + deletequote delq Deletes all quotes with the specified keyword. You have to either be bot owner or the creator of the quote to delete it. @@ -1359,15 +1359,6 @@ `@NadekoBot dump` - - ab - - - Try to get 'abalabahaha' - - - `@NadekoBot ab` - draw @@ -1378,7 +1369,7 @@ `$draw [x]` - shuffle + shuffle sh Shuffles the current playlist. @@ -1396,7 +1387,7 @@ `$flip` or `$flip 3` - betflip + betflip bf Bet to guess will the result be heads or tails. Guessing award you double flowers you've bet. @@ -1441,7 +1432,7 @@ `$race` - joinrace + joinrace jr Joins a new race. You can specify an amount of flowers for betting (optional). You will get YourBet*(participants-1) back if you win. @@ -1486,7 +1477,7 @@ `$take 1 "@someguy"` - betroll + betroll br Bets a certain amount of NadekoFlowers and rolls a dice. Rolling over 66 yields x2 flowers, over 90 - x3 and 100 x10. @@ -1495,7 +1486,7 @@ `$br 5` - leaderboard + leaderboard lb Displays bot currency leaderboard @@ -1504,7 +1495,7 @@ `$lb` - t + trivia t Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. @@ -1594,7 +1585,7 @@ `>plant` - gencurrency + gencurrency gc Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. @@ -1648,7 +1639,7 @@ `>linux Spyware Windows` - next + next n Goes to the next song in the queue. You have to be in the same voice channel as the bot. @@ -1657,7 +1648,7 @@ `!!n` - stop + stop s Stops the music and clears the playlist. Stays in the channel. @@ -1666,7 +1657,7 @@ `!!s` - destroy + destroy d Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) @@ -1675,7 +1666,7 @@ `!!d` - pause + pause p Pauses or Unpauses the song. @@ -1684,7 +1675,7 @@ `!!p` - queue + queue q yq Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. @@ -1693,7 +1684,7 @@ `!!q Dream Of Venice` - soundcloudqueue + soundcloudqueue sq Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. @@ -1702,7 +1693,7 @@ `!!sq Dream Of Venice` - listqueue + listqueue lq Lists 15 currently queued songs per page. Default page is 1. @@ -1711,7 +1702,7 @@ `!!lq` or `!!lq 2` - nowplaying + nowplaying np Shows the song currently playing. @@ -1720,7 +1711,7 @@ `!!np` - volume + volume vol Sets the music volume 0-100% @@ -1729,7 +1720,7 @@ `!!vol 50` - defvol + defvol dv Sets the default music volume when music playback is started (0-100). Persists through restarts. @@ -1756,7 +1747,7 @@ `!!half` - playlist + playlist pl Queues up to 500 songs from a youtube playlist specified by a link, or keywords. @@ -1765,7 +1756,7 @@ `!!pl playlist link or name` - soundcloudpl + soundcloudpl scpl Queue a soundcloud playlist using a link. @@ -1774,7 +1765,7 @@ `!!scpl soundcloudseturl` - localplaylst + localplaylst lopl Queues all songs from a directory. **Bot Owner Only!** @@ -1783,7 +1774,7 @@ `!!lopl C:/music/classical` - radio + radio ra Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: <https://streamable.com/al54>) @@ -1792,7 +1783,7 @@ `!!ra radio link here` - local + local lo Queues a local file by specifying a full path. **Bot Owner Only!** @@ -1801,7 +1792,7 @@ `!!lo C:/music/mysong.mp3` - move + move mv Moves the bot to your voice channel. (works only if music is already playing) @@ -1810,7 +1801,7 @@ `!!mv` - remove + remove rm Remove a song by its # in the queue, or 'all' to remove whole queue. @@ -1819,7 +1810,7 @@ `!!rm 5` - movesong + movesong ms Moves a song from one position to another. @@ -1828,7 +1819,7 @@ `!! ms 5>3` - setmaxqueue + setmaxqueue smq Sets a maximum queue size. Supply 0 or no argument to have no limit. @@ -1846,7 +1837,7 @@ `!!cleanup` - reptcursong + reptcursong rcs Toggles repeat of current song. @@ -1855,7 +1846,7 @@ `!!rcs` - rpeatplaylst + rpeatplaylst rpl Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). @@ -1882,7 +1873,7 @@ `!!load classical-1` - playlists + playlists pls Lists all playlists. Paginated. 20 per page. Default page is 0. @@ -1891,7 +1882,7 @@ `!!pls 1` - deleteplaylist + deleteplaylist delpls Deletes a saved playlist. Only if you made it or if you are the bot owner. @@ -1909,7 +1900,7 @@ `!!goto 30` - getlink + getlink gl Shows a link to the song in the queue by index, or the currently playing song by default. @@ -1918,7 +1909,7 @@ `!!gl` - autoplay + autoplay ap Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) @@ -1945,7 +1936,7 @@ `~lolban` - hitbox + hitbox hb Notifies this channel when a certain user starts streaming. @@ -1954,7 +1945,7 @@ `~hitbox SomeStreamer` - twitch + twitch tw Notifies this channel when a certain user starts streaming. @@ -1963,7 +1954,7 @@ `~twitch SomeStreamer` - beam + beam bm Notifies this channel when a certain user starts streaming. @@ -1972,7 +1963,7 @@ `~beam SomeStreamer` - checkhitbox + checkhitbox chhb Checks if a certain user is streaming on the hitbox platform. @@ -1981,7 +1972,7 @@ `~chhb SomeStreamer` - checktwitch + checktwitch chtw Checks if a certain user is streaming on the twitch platform. @@ -1990,7 +1981,7 @@ `~chtw SomeStreamer` - checkbeam + checkbeam chbm Checks if a certain user is streaming on the beam platform. @@ -1999,7 +1990,7 @@ `~chbm SomeStreamer` - removestream + removestream rms Removes notifications of a certain streamer on this channel. @@ -2008,7 +1999,7 @@ `~rms SomeGuy` - liststreams + liststreams ls Lists all streams you are following on this server. @@ -2044,7 +2035,7 @@ `~wowjoke` - calculate + calculate calc Evaluate a mathematical expression. @@ -2080,7 +2071,7 @@ `~osu top5 Name` - pokemon + pokemon poke Searches for a pokemon. @@ -2089,7 +2080,7 @@ `~poke Sylveon` - pokemonability + pokemonability pokeab Searches for a pokemon ability. @@ -2116,7 +2107,7 @@ `~memegen biw "gets iced coffee" "in the winter"` - we + weather we Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. @@ -2125,7 +2116,7 @@ `~we Moscow RF` - yt + youtube yt Searches youtubes and shows the first result @@ -2134,7 +2125,7 @@ `~yt query` - ani + ani anime aq Queries anilist for an anime and shows the first result. @@ -2151,17 +2142,17 @@ `~imdb Batman vs Superman` - - mang + + manga mang mq - + Queries anilist for a manga and shows the first result. - + `~mq Shingeki no kyojin` - randomcat + randomcat meow Shows a random cat image. @@ -2170,7 +2161,7 @@ `~meow` - randomdog + randomdog woof Shows a random dog image. @@ -2179,7 +2170,7 @@ `~woof` - i + img i Pulls the first image found using a search parameter. Use ~ir for different results. @@ -2206,7 +2197,7 @@ `~lmgtfy query` - google + google g Get a google search link for some terms. @@ -2223,13 +2214,13 @@ `~hs Ysera` - - ud + + urbandict ud - + Searches Urban Dictionary for a word. - + `~ud Pineapple` @@ -2251,7 +2242,7 @@ `~catfact` - yomama + yomama ym Shows a random joke from <http://api.yomomma.info/> @@ -2260,7 +2251,7 @@ `~ym` - randjoke + randjoke rj Shows a random joke from <http://tambal.azurewebsites.net/joke/random> @@ -2269,7 +2260,7 @@ `~rj` - chucknorris + chucknorris cn Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> @@ -2278,7 +2269,7 @@ `~cn` - magicitem + magicitem mi Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> @@ -2302,7 +2293,7 @@ Returns a google reverse image search for an image from a link. - `~revav Image link` + `~revimg Image link` safebooru @@ -2314,7 +2305,7 @@ `~safebooru yuri+kissing` - wiki + wikipedia wiki Gives you back a wikipedia link @@ -2323,7 +2314,7 @@ `~wiki query` - clr + clr prune Shows you what color corresponds to that hex. @@ -2341,7 +2332,7 @@ `~videocall "@SomeGuy"` - av + av avatar Shows a mentioned person's avatar. @@ -2413,7 +2404,7 @@ `~boobs` - butts + butts ass butt Real adult content. @@ -2422,7 +2413,7 @@ `~butts` or `~ass` - createwar + createwar cw Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. @@ -2431,7 +2422,7 @@ `,cw 15 The Enemy Clan` - startwar + startwar sw Starts a war with a given number. @@ -2440,7 +2431,7 @@ `,sw 15` - listwar + listwar lw Shows the active war claims by a number. Shows all wars in a short way if no number is specified. @@ -2449,7 +2440,7 @@ `,lw [war_number] or ,lw` - claim + claim call c Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. @@ -2458,7 +2449,7 @@ `,call [war_number] [base_number] [optional_other_name]` - claimfinish + claimfinish cf cf3 claimfinish3 Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else. @@ -2467,7 +2458,7 @@ `,cf [war_number] [optional_other_name]` - claimfinish2 + claimfinish2 cf2 Finish your claim with 2 stars if you destroyed a base. Optional second argument finishes for someone else. @@ -2476,7 +2467,7 @@ `,cf [war_number] [optional_other_name]` - claimfinish1 + claimfinish1 cf1 Finish your claim with 1 stars if you destroyed a base. Optional second argument finishes for someone else. @@ -2485,7 +2476,7 @@ `,cf [war_number] [optional_other_name]` - unclaim + unclaim ucall uc Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim @@ -2494,7 +2485,7 @@ `,uc [war_number] [optional_other_name]` - endwar + endwar ew Ends the war with a given index. @@ -2512,7 +2503,7 @@ `>attack "vine whip" @someguy` - movelist + movelist ml Lists the moves you are able to use @@ -2548,7 +2539,7 @@ `>settype fire` - translate + translate trans Translates from>to text. From the given language to the destiation language. @@ -2584,7 +2575,7 @@ `trello unbind` - lists + lists list Lists all lists, yo ;) **Bot Owner Only!** @@ -2608,7 +2599,7 @@ `-readme` or `-guide` - readme + readme guide Shows all available operations in .calc command @@ -2626,7 +2617,7 @@ `.delallq` - delallq + delallq daq greetdmmsg @@ -2637,4 +2628,13 @@ Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Needs Manage Server Permissions.** + + Check how much NadekoFlowers a person has. (Defaults to yourself) + + + `$$$` or `$$$ @SomeGuy` + + + cash $$ + \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 72395491..38b66963 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -28,7 +28,7 @@ namespace NadekoBot.Resources { private static global::System.Globalization.CultureInfo resourceCulture; - internal ResponseStrings() { + public ResponseStrings() { } /// diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs new file mode 100644 index 00000000..493571ce --- /dev/null +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -0,0 +1,93 @@ +using Discord.WebSocket; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord; +using NLog; +using System.Diagnostics; +using Discord.Commands; + +namespace NadekoBot.Services +{ + public class CommandHandler + { + private DiscordSocketClient _client; + private CommandService _commandService; + private Logger _log; + + public event EventHandler CommandExecuted = delegate { }; + + public CommandHandler(DiscordSocketClient client, CommandService commandService) + { + _client = client; + _commandService = commandService; + _log = LogManager.GetCurrentClassLogger(); + + _client.MessageReceived += MessageReceivedHandler; + } + + private Task MessageReceivedHandler(IMessage msg) + { + var usrMsg = msg as IUserMessage; + if (usrMsg == null) + return Task.CompletedTask; + var throwaway = Task.Run(async () => + { + var sw = new Stopwatch(); + sw.Start(); + var t = await _commandService.Execute(usrMsg, usrMsg.Content, MultiMatchHandling.Best); + var command = t.Item1; + var result = t.Item2; + sw.Stop(); + var channel = (usrMsg.Channel as ITextChannel); + if (result.IsSuccess) + { + CommandExecuted(this, new CommandExecutedEventArgs(usrMsg, command)); + _log.Info("Command Executed after {4}s\n\t" + + "User: {0}\n\t" + + "Server: {1}\n\t" + + "Channel: {2}\n\t" + + "Message: {3}", + usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0} + (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} + (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} + usrMsg.Content, // {3} + sw.Elapsed.TotalSeconds // {4} + ); + } + else if (!result.IsSuccess && result.Error != CommandError.UnknownCommand) + { + _log.Warn("Command Errored after {5}s\n\t" + + "User: {0}\n\t" + + "Server: {1}\n\t" + + "Channel: {2}\n\t" + + "Message: {3}\n\t" + + "Error: {4}", + usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0} + (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} + (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} + usrMsg.Content,// {3} + result.ErrorReason, // {4} + sw.Elapsed.TotalSeconds // {5} + ); + } + }); + + return Task.CompletedTask; + } + } + + public class CommandExecutedEventArgs + { + public Command Command { get; } + public IUserMessage Message { get; } + + public CommandExecutedEventArgs(IUserMessage msg, Command cmd) + { + Message = msg; + Command = cmd; + } + } +} diff --git a/src/NadekoBot/Services/CurrencyHandler.cs b/src/NadekoBot/Services/CurrencyHandler.cs new file mode 100644 index 00000000..aa7fc918 --- /dev/null +++ b/src/NadekoBot/Services/CurrencyHandler.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord; +using NadekoBot.Services.Database; +using NadekoBot.Extensions; +using NadekoBot.Modules.Gambling; + +namespace NadekoBot.Services +{ + public static class CurrencyHandler + { + public static async Task RemoveCurrencyAsync(IGuildUser author, string reason, long amount, bool sendMessage) + { + if (amount < 0) + throw new ArgumentNullException(nameof(amount)); + + + using (var uow = DbHandler.UnitOfWork()) + { + var success = uow.Currency.TryUpdateState(author.Id, -amount); + if (!success) + return false; + await uow.CompleteAsync(); + } + + if (sendMessage) + try { await author.SendMessageAsync($"`You lost:` {amount} {Gambling.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { } + + return true; + } + + public static async Task AddCurrencyAsync(IGuildUser author, string reason, long amount, bool sendMessage) + { + if (amount < 0) + throw new ArgumentNullException(nameof(amount)); + + + using (var uow = DbHandler.UnitOfWork()) + { + uow.Currency.TryUpdateState(author.Id, amount); + await uow.CompleteAsync(); + } + + if (sendMessage) + await author.SendMessageAsync($"`You received:` {amount} {Gambling.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); + } + } +} diff --git a/src/NadekoBot/Services/Database/IUnitOfWork.cs b/src/NadekoBot/Services/Database/IUnitOfWork.cs index 3a3fb2c2..14719461 100644 --- a/src/NadekoBot/Services/Database/IUnitOfWork.cs +++ b/src/NadekoBot/Services/Database/IUnitOfWork.cs @@ -16,6 +16,8 @@ namespace NadekoBot.Services.Database IReminderRepository Reminders { get; } ISelfAssignedRolesRepository SelfAssignedRoles { get; } IBotConfigRepository BotConfig { get; } + IRepeaterRepository Repeaters { get; } + IUnitConverterRepository ConverterUnits { get; } int Complete(); Task CompleteAsync(); diff --git a/src/NadekoBot/Services/Database/Models/BotConfig.cs b/src/NadekoBot/Services/Database/Models/BotConfig.cs index 6bb02fb3..acb5d199 100644 --- a/src/NadekoBot/Services/Database/Models/BotConfig.cs +++ b/src/NadekoBot/Services/Database/Models/BotConfig.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; @@ -15,21 +16,10 @@ namespace NadekoBot.Services.Database.Models public bool ForwardMessages { get; set; } = true; public bool ForwardToAllOwners { get; set; } = true; - public List ModulePrefixes { get; set; } = new List() - { - new ModulePrefix() { ModuleName="Administration", Prefix="." }, - new ModulePrefix() { ModuleName="Searches", Prefix="~" }, - new ModulePrefix() { ModuleName="NSFW", Prefix="~" }, - new ModulePrefix() { ModuleName="ClashOfClans", Prefix="," }, - new ModulePrefix() { ModuleName="Help", Prefix="-" }, - new ModulePrefix() { ModuleName="Music", Prefix="!!" }, - new ModulePrefix() { ModuleName="Trello", Prefix="trello" }, - new ModulePrefix() { ModuleName="Games", Prefix=">" }, - new ModulePrefix() { ModuleName="Gambling", Prefix="$" }, - new ModulePrefix() { ModuleName="Permissions", Prefix=";" }, - new ModulePrefix() { ModuleName="Pokemon", Prefix=">" }, - new ModulePrefix() { ModuleName="Utility", Prefix="." } - }; + public float CurrencyGenerationChance { get; set; } = 0.02f; + public int CurrencyGenerationCooldown { get; set; } = 10; + + public List ModulePrefixes { get; set; } = new List(); public List RotatingStatusMessages { get; set; } = new List(); @@ -41,43 +31,8 @@ namespace NadekoBot.Services.Database.Models public string CurrencyName { get; set; } = "Nadeko Flower"; public string CurrencyPluralName { get; set; } = "Nadeko Flowers"; - public List EightBallResponses { get; set; } = new List - { - new EightBallResponse() { Text = "Most definitely yes" }, - new EightBallResponse() { Text = "For sure" }, - new EightBallResponse() { Text = "Totally!" }, - new EightBallResponse() { Text = "As I see it, yes" }, - new EightBallResponse() { Text = "My sources say yes" }, - new EightBallResponse() { Text = "Yes" }, - new EightBallResponse() { Text = "Most likely" }, - new EightBallResponse() { Text = "Perhaps" }, - new EightBallResponse() { Text = "Maybe" }, - new EightBallResponse() { Text = "Not sure" }, - new EightBallResponse() { Text = "It is uncertain" }, - new EightBallResponse() { Text = "Ask me again later" }, - new EightBallResponse() { Text = "Don't count on it" }, - new EightBallResponse() { Text = "Probably not" }, - new EightBallResponse() { Text = "Very doubtful" }, - new EightBallResponse() { Text = "Most likely no" }, - new EightBallResponse() { Text = "Nope" }, - new EightBallResponse() { Text = "No" }, - new EightBallResponse() { Text = "My sources say no" }, - new EightBallResponse() { Text = "Dont even think about it" }, - new EightBallResponse() { Text = "Definitely no" }, - new EightBallResponse() { Text = "NO - It may cause disease contraction" } - }; - - public List RaceAnimals { get; set; } = new List - { - new RaceAnimal { Icon = "🐼", Name = "Panda" }, - new RaceAnimal { Icon = "🐻", Name = "Bear" }, - new RaceAnimal { Icon = "🐧", Name = "Pengu" }, - new RaceAnimal { Icon = "🐨", Name = "Koala" }, - new RaceAnimal { Icon = "🐬", Name = "Dolphin" }, - new RaceAnimal { Icon = "🐞", Name = "Ladybird" }, - new RaceAnimal { Icon = "🦀", Name = "Crab" }, - new RaceAnimal { Icon = "🦄", Name = "Unicorn" } - }; + public List EightBallResponses { get; set; } = new List(); + public List RaceAnimals { get; set; } = new List(); } public class PlayingStatus :DbEntity @@ -99,17 +54,59 @@ namespace NadekoBot.Services.Database.Models public class EightBallResponse : DbEntity { public string Text { get; set; } + + public override int GetHashCode() + { + return Text.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (!(obj is EightBallResponse)) + return base.Equals(obj); + + return ((EightBallResponse)obj).Text == Text; + } } public class RaceAnimal : DbEntity { public string Icon { get; set; } public string Name { get; set; } + + public override int GetHashCode() + { + return Icon.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (!(obj is RaceAnimal)) + return base.Equals(obj); + + return ((RaceAnimal)obj).Icon == Icon; + } } public class ModulePrefix : DbEntity { public string ModuleName { get; set; } public string Prefix { get; set; } + + public int BotConfigId { get; set; } = 1; + public BotConfig BotConfig { get; set; } + + public override int GetHashCode() + { + return ModuleName.GetHashCode(); + } + + public override bool Equals(object obj) + { + if(!(obj is ModulePrefix)) + return base.Equals(obj); + + return ((ModulePrefix)obj).ModuleName == ModuleName; + } } } diff --git a/src/NadekoBot/Services/Database/Models/ClashWar.cs b/src/NadekoBot/Services/Database/Models/ClashWar.cs index 2daaf699..77c77994 100644 --- a/src/NadekoBot/Services/Database/Models/ClashWar.cs +++ b/src/NadekoBot/Services/Database/Models/ClashWar.cs @@ -29,7 +29,7 @@ namespace NadekoBot.Services.Database.Models public ulong ChannelId { get; set; } [NotMapped] - public ITextChannel Channel { get; internal set; } + public ITextChannel Channel { get; set; } public List Bases { get; set; } } diff --git a/src/NadekoBot/Services/Database/Models/ConvertUnit.cs b/src/NadekoBot/Services/Database/Models/ConvertUnit.cs new file mode 100644 index 00000000..3a487218 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/ConvertUnit.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class ConvertUnit : DbEntity + { + public ConvertUnit() { } + [NotMapped] + private string[] _triggersValue; + [NotMapped] + public string[] Triggers + { + get + { + return _triggersValue ?? (_triggersValue = InternalTrigger.Split('|')); + } + set + { + _triggersValue = value; + InternalTrigger = string.Join("|", _triggersValue); + } + } + //protected or private? + /// + /// DO NOT CALL THIS + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public string InternalTrigger { get; set; } + public string UnitType { get; set; } + public decimal Modifier { get; set; } + } + +} diff --git a/src/NadekoBot/Services/Database/Models/Currency.cs b/src/NadekoBot/Services/Database/Models/Currency.cs new file mode 100644 index 00000000..5bc8b8d5 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/Currency.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class Currency : DbEntity + { + public ulong UserId { get; set; } + public long Amount { get; set; } + } +} diff --git a/src/NadekoBot/Services/Database/Models/FollowedStream.cs b/src/NadekoBot/Services/Database/Models/FollowedStream.cs new file mode 100644 index 00000000..8bab223e --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/FollowedStream.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class FollowedStream : DbEntity + { + public ulong ChannelId { get; set; } + public string Username { get; set; } + public FollowedStreamType Type { get; set; } + public bool LastStatus { get; set; } + public ulong GuildId { get; set; } + + public enum FollowedStreamType + { + Twitch, Hitbox, Beam + } + } +} diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index 77c1d966..ada21e94 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -28,8 +28,18 @@ namespace NadekoBot.Services.Database.Models public bool SendChannelByeMessage { get; set; } public string ChannelByeMessageText { get; set; } = "%user% has left!"; + public LogSetting LogSetting { get; set; } = new LogSetting(); + //self assignable roles public bool ExclusiveSelfAssignedRoles { get; set; } public bool AutoDeleteSelfAssignedRoleMessages { get; set; } + public float DefaultMusicVolume { get; set; } = 1.0f; + public bool VoicePlusTextEnabled { get; set; } + + //stream notifications + public List FollowedStreams { get; set; } = new List(); + + //currencyGeneration + public ulong? GenerateCurrencyChannelId { get; set; } } } diff --git a/src/NadekoBot/Services/Database/Models/IgnoredLogChannel.cs b/src/NadekoBot/Services/Database/Models/IgnoredLogChannel.cs new file mode 100644 index 00000000..9a6c384f --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/IgnoredLogChannel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class IgnoredLogChannel : DbEntity + { + public LogSetting LogSetting { get; set; } + public ulong ChannelId { get; set; } + } +} diff --git a/src/NadekoBot/Services/Database/Models/LogSetting.cs b/src/NadekoBot/Services/Database/Models/LogSetting.cs new file mode 100644 index 00000000..a02cfcbc --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/LogSetting.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class LogSetting : DbEntity + { + public bool IsLogging { get; set; } + public ulong ChannelId { get; set; } + public HashSet IgnoredChannels { get; set; } + + public bool MessageReceived { get; set; } = true; + public bool MessageUpdated { get; set; } = true; + public bool MessageDeleted { get; set; } = true; + + public bool UserJoined { get; set; } = true; + public bool UserLeft { get; set; } = true; + public bool UserBanned { get; set; } = true; + public bool UserUnbanned { get; set; } = true; + public bool UserUpdated { get; set; } = true; + + public bool ChannelCreated { get; set; } = true; + public bool ChannelDestroyed { get; set; } = true; + public bool ChannelUpdated { get; set; } = true; + + //userpresence + public bool LogUserPresence { get; set; } = false; + public ulong UserPresenceChannelId { get; set; } + + //voicepresence + public bool LogVoicePresence { get; set; } = false; + public ulong VoicePresenceChannelId { get; set; } + public HashSet IgnoredVoicePresenceChannelIds { get; set; } + + } +} diff --git a/src/NadekoBot/Services/Database/Models/Repeater.cs b/src/NadekoBot/Services/Database/Models/Repeater.cs new file mode 100644 index 00000000..ba5552f9 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/Repeater.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class Repeater :DbEntity + { + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + public string Message { get; set; } + public TimeSpan Interval { get; set; } + } +} diff --git a/src/NadekoBot/Services/Database/Models/TypingArticle.cs b/src/NadekoBot/Services/Database/Models/TypingArticle.cs new file mode 100644 index 00000000..508ace03 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/TypingArticle.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class TypingArticle : DbEntity + { + public string Author { get; set; } + public string Text { get; set; } + } +} diff --git a/src/NadekoBot/Services/Database/Models/VoicePresenceChannel.cs b/src/NadekoBot/Services/Database/Models/VoicePresenceChannel.cs new file mode 100644 index 00000000..a6937c4e --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/VoicePresenceChannel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Models +{ + public class IgnoredVoicePresenceChannel : DbEntity + { + public LogSetting LogSetting { get; set; } + public ulong ChannelId { get; set; } + } +} diff --git a/src/NadekoBot/Services/Database/NadekoContext.cs b/src/NadekoBot/Services/Database/NadekoContext.cs index 378c1637..46e42179 100644 --- a/src/NadekoBot/Services/Database/NadekoContext.cs +++ b/src/NadekoBot/Services/Database/NadekoContext.cs @@ -18,6 +18,90 @@ namespace NadekoBot.Services.Database public DbSet Reminders { get; set; } public DbSet SelfAssignableRoles { get; set; } public DbSet BotConfig { get; set; } + public DbSet Repeaters { get; set; } + public DbSet Currency { get; set; } + public DbSet ConversionUnits { get; set; } + public DbSet TypingArticles { get; set; } + + //logging + public DbSet LogSettings { get; set; } + public DbSet IgnoredLogChannels { get; set; } + public DbSet IgnoredVoicePresenceCHannels { get; set; } + + //orphans xD + public DbSet EightBallResponses { get; set; } + public DbSet RaceAnimals { get; set; } + public DbSet ModulePrefixes { get; set; } + + public void EnsureSeedData() + { + if (!BotConfig.Any()) + { + var bc = new BotConfig(); + + bc.ModulePrefixes.AddRange(new HashSet() + { + new ModulePrefix() { ModuleName = "Administration", Prefix = "." }, + new ModulePrefix() { ModuleName = "Searches", Prefix = "~" }, + new ModulePrefix() { ModuleName = "Translator", Prefix = "~" }, + new ModulePrefix() { ModuleName = "NSFW", Prefix = "~" }, + new ModulePrefix() { ModuleName = "ClashOfClans", Prefix = "," }, + new ModulePrefix() { ModuleName = "Help", Prefix = "-" }, + new ModulePrefix() { ModuleName = "Music", Prefix = "!!" }, + new ModulePrefix() { ModuleName = "Trello", Prefix = "trello" }, + new ModulePrefix() { ModuleName = "Games", Prefix = ">" }, + new ModulePrefix() { ModuleName = "Gambling", Prefix = "$" }, + new ModulePrefix() { ModuleName = "Permissions", Prefix = ";" }, + new ModulePrefix() { ModuleName = "Pokemon", Prefix = ">" }, + new ModulePrefix() { ModuleName = "Utility", Prefix = "." } + }); + bc.RaceAnimals.AddRange(new HashSet + { + new RaceAnimal { Icon = "🐼", Name = "Panda" }, + new RaceAnimal { Icon = "🐻", Name = "Bear" }, + new RaceAnimal { Icon = "🐧", Name = "Pengu" }, + new RaceAnimal { Icon = "🐨", Name = "Koala" }, + new RaceAnimal { Icon = "🐬", Name = "Dolphin" }, + new RaceAnimal { Icon = "🐞", Name = "Ladybird" }, + new RaceAnimal { Icon = "🦀", Name = "Crab" }, + new RaceAnimal { Icon = "🦄", Name = "Unicorn" } + }); + bc.EightBallResponses.AddRange(new HashSet + { + new EightBallResponse() { Text = "Most definitely yes" }, + new EightBallResponse() { Text = "For sure" }, + new EightBallResponse() { Text = "Totally!" }, + new EightBallResponse() { Text = "Of course!" }, + new EightBallResponse() { Text = "As I see it, yes" }, + new EightBallResponse() { Text = "My sources say yes" }, + new EightBallResponse() { Text = "Yes" }, + new EightBallResponse() { Text = "Most likely" }, + new EightBallResponse() { Text = "Perhaps" }, + new EightBallResponse() { Text = "Maybe" }, + new EightBallResponse() { Text = "Not sure" }, + new EightBallResponse() { Text = "It is uncertain" }, + new EightBallResponse() { Text = "Ask me again later" }, + new EightBallResponse() { Text = "Don't count on it" }, + new EightBallResponse() { Text = "Probably not" }, + new EightBallResponse() { Text = "Very doubtful" }, + new EightBallResponse() { Text = "Most likely no" }, + new EightBallResponse() { Text = "Nope" }, + new EightBallResponse() { Text = "No" }, + new EightBallResponse() { Text = "My sources say no" }, + new EightBallResponse() { Text = "Dont even think about it" }, + new EightBallResponse() { Text = "Definitely no" }, + new EightBallResponse() { Text = "NO - It may cause disease contraction" } + }); + + BotConfig.Add(bc); + + this.SaveChanges(); + } + if (!TypingArticles.Any()) + { + //todo load default typing articles + } + } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -36,7 +120,7 @@ namespace NadekoBot.Services.Database #endregion - #region Config + #region GuildConfig var configEntity = modelBuilder.Entity(); configEntity @@ -45,6 +129,15 @@ namespace NadekoBot.Services.Database #endregion + #region BotConfig + var botConfigEntity = modelBuilder.Entity(); + //botConfigEntity + // .HasMany(c => c.ModulePrefixes) + // .WithOne(mp => mp.BotConfig) + // .HasForeignKey(mp => mp.BotConfigId); + + #endregion + #region ClashOfClans var callersEntity = modelBuilder.Entity(); @@ -63,6 +156,38 @@ namespace NadekoBot.Services.Database .IsUnique(); #endregion + + #region Repeater + + var repeaterEntity = modelBuilder.Entity(); + + repeaterEntity + .HasIndex(r => r.ChannelId) + .IsUnique(); + + #endregion + + #region Currency + var currencyEntity = modelBuilder.Entity(); + + currencyEntity + .HasIndex(c => c.UserId) + .IsUnique(); + #endregion + + #region LogSettings + + //var logSettingEntity = modelBuilder.Entity(); + + //logSettingEntity + // .HasMany(ls => ls.IgnoredChannels) + // .WithOne(ls => ls.LogSetting) + // .HasPrincipalKey(ls => ls.id; + + //logSettingEntity + // .HasMany(ls => ls.IgnoredVoicePresenceChannelIds) + // .WithOne(ls => ls.LogSetting); + #endregion } protected abstract override void OnConfiguring(DbContextOptionsBuilder optionsBuilder); } diff --git a/src/NadekoBot/Services/Database/Repositories/ICurrencyRepository.cs b/src/NadekoBot/Services/Database/Repositories/ICurrencyRepository.cs new file mode 100644 index 00000000..c6e545df --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/ICurrencyRepository.cs @@ -0,0 +1,17 @@ +using NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Repositories +{ + public interface ICurrencyRepository : IRepository + { + Currency GetOrCreate(ulong userId); + long GetUserCurrency(ulong userId); + bool TryUpdateState(ulong userId, long change); + IEnumerable GetTopRichest(int count); + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs index 70e602f7..82767a80 100644 --- a/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs @@ -11,5 +11,6 @@ namespace NadekoBot.Services.Database.Repositories public interface IGuildConfigRepository : IRepository { GuildConfig For(ulong guildId); + IEnumerable GetAllFollowedStreams(); } } diff --git a/src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs b/src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs new file mode 100644 index 00000000..7bd5c67f --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs @@ -0,0 +1,14 @@ +using NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Repositories +{ + public interface IRepeaterRepository : IRepository + { + + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/ITypingArticlesRepository.cs b/src/NadekoBot/Services/Database/Repositories/ITypingArticlesRepository.cs new file mode 100644 index 00000000..d60dae77 --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/ITypingArticlesRepository.cs @@ -0,0 +1,14 @@ +using NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Repositories +{ + public interface ITypingArticlesRepository : IRepository + { + TypingArticle GetRandom(); + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/IUnitConverterRepository.cs b/src/NadekoBot/Services/Database/Repositories/IUnitConverterRepository.cs new file mode 100644 index 00000000..147f38d8 --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/IUnitConverterRepository.cs @@ -0,0 +1,14 @@ +using NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Database.Repositories +{ + public interface IUnitConverterRepository : IRepository + { + void AddOrUpdate(Func check, ConvertUnit toAdd, Func toUpdate); + bool Empty(); + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/CurrencyRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/CurrencyRepository.cs new file mode 100644 index 00000000..f9bcdbe7 --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/Impl/CurrencyRepository.cs @@ -0,0 +1,60 @@ +using NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace NadekoBot.Services.Database.Repositories.Impl +{ + public class CurrencyRepository : Repository, ICurrencyRepository + { + public CurrencyRepository(DbContext context) : base(context) + { + } + + public Currency GetOrCreate(ulong userId) + { + var cur = _set.FirstOrDefault(c => c.UserId == userId); + + if (cur == null) + { + _set.Add(cur = new Currency() + { + UserId = userId, + Amount = 0 + }); + _context.SaveChanges(); + } + return cur; + } + + public IEnumerable GetTopRichest(int count) => + _set.OrderByDescending(c => c.Amount).Take(count).ToList(); + + public long GetUserCurrency(ulong userId) => + GetOrCreate(userId).Amount; + + public bool TryUpdateState(ulong userId, long change) + { + var cur = GetOrCreate(userId); + + if (change == 0) + return true; + + if (change > 0) + { + cur.Amount += change; + return true; + } + //change is negative + if (cur.Amount + change >= 0) + { + cur.Amount += change; + return true; + } + return false; + } + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs index 13f05404..3ff0b7e6 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs @@ -13,6 +13,14 @@ namespace NadekoBot.Services.Database.Repositories.Impl public GuildConfigRepository(DbContext context) : base(context) { } + + public new IEnumerable GetAll() => + _set.Include(gc => gc.LogSetting) + .ThenInclude(ls => ls.IgnoredChannels) + .Include(gc => gc.LogSetting) + .ThenInclude(ls => ls.IgnoredVoicePresenceChannelIds) + .ToList(); + /// /// Gets and creates if it doesn't exist a config for a guild. /// @@ -20,7 +28,10 @@ namespace NadekoBot.Services.Database.Repositories.Impl /// public GuildConfig For(ulong guildId) { - var config = _set.FirstOrDefault(c => c.GuildId == guildId); + var config = _set.Include(gc => gc.FollowedStreams) + .Include(gc => gc.LogSetting) + .ThenInclude(ls=>ls.IgnoredChannels) + .FirstOrDefault(c => c.GuildId == guildId); if (config == null) { @@ -32,5 +43,10 @@ namespace NadekoBot.Services.Database.Repositories.Impl } return config; } + + public IEnumerable GetAllFollowedStreams() => + _set.Include(gc => gc.FollowedStreams) + .SelectMany(gc => gc.FollowedStreams) + .ToList(); } } diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs index 7207f455..702cfc21 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl public Task GetRandomQuoteByKeywordAsync(ulong guildId, string keyword) { - var rng = new Random(); + var rng = new NadekoRandom(); return _set.Where(q => q.Keyword == keyword).OrderBy(q => rng.Next()).FirstOrDefaultAsync(); } } diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs new file mode 100644 index 00000000..33885856 --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs @@ -0,0 +1,17 @@ +using NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace NadekoBot.Services.Database.Repositories.Impl +{ + public class RepeaterRepository : Repository, IRepeaterRepository + { + public RepeaterRepository(DbContext context) : base(context) + { + } + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/TypingArticlesRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/TypingArticlesRepository.cs new file mode 100644 index 00000000..2b47b729 --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/Impl/TypingArticlesRepository.cs @@ -0,0 +1,25 @@ +using NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace NadekoBot.Services.Database.Repositories.Impl +{ + public class TypingArticlesRepository : Repository, ITypingArticlesRepository + { + private Random _rand = null; + private Random rand => _rand ?? (_rand = new NadekoRandom()); + public TypingArticlesRepository(DbContext context) : base(context) + { + } + + public TypingArticle GetRandom() + { + var skip = (int)(rand.NextDouble() * _set.Count()); + return _set.Skip(skip).FirstOrDefault(); + } + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/UnitCOnverterRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/UnitCOnverterRepository.cs new file mode 100644 index 00000000..846fe524 --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/Impl/UnitCOnverterRepository.cs @@ -0,0 +1,28 @@ +using NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace NadekoBot.Services.Database.Repositories.Impl +{ + public class UnitConverterRepository : Repository, IUnitConverterRepository + { + public UnitConverterRepository(DbContext context) : base(context) + { + } + + public void AddOrUpdate(Func check, ConvertUnit toAdd, Func toUpdate) + { + var existing = _set.FirstOrDefault(check); + if (existing != null) + { + existing = toUpdate.Invoke(existing); + } + else _set.Add(toAdd); + } + + public bool Empty() => !_set.Any(); + } +} diff --git a/src/NadekoBot/Services/Database/UnitOfWork.cs b/src/NadekoBot/Services/Database/UnitOfWork.cs index 5550bc6c..7a4c9520 100644 --- a/src/NadekoBot/Services/Database/UnitOfWork.cs +++ b/src/NadekoBot/Services/Database/UnitOfWork.cs @@ -33,6 +33,17 @@ namespace NadekoBot.Services.Database private IBotConfigRepository _botConfig; public IBotConfigRepository BotConfig => _botConfig ?? (_botConfig = new BotConfigRepository(_context)); + private IRepeaterRepository _repeaters; + public IRepeaterRepository Repeaters => _repeaters ?? (_repeaters = new RepeaterRepository(_context)); + + private ICurrencyRepository _currency; + public ICurrencyRepository Currency => _currency ?? (_currency = new CurrencyRepository(_context)); + private IUnitConverterRepository _conUnits; + public IUnitConverterRepository ConverterUnits => _conUnits ?? (_conUnits = new UnitConverterRepository(_context)); + + private ITypingArticlesRepository _typingArticles; + public ITypingArticlesRepository TypingArticles => _typingArticles ?? (_typingArticles = new TypingArticlesRepository(_context)); + public UnitOfWork(NadekoContext context) { _context = context; diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 9f50971a..3d33bb9f 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -15,28 +15,31 @@ namespace NadekoBot.Services.Impl private int messageCounter; private DiscordSocketClient client; private DateTime started; + private int commandsRan = 0; public string BotVersion => "1.0-alpha"; public string Heap => Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(); - public StatsService(DiscordSocketClient client) + public StatsService(DiscordSocketClient client, CommandHandler cmdHandler) { this.client = client; Reset(); this.client.MessageReceived += _ => Task.FromResult(messageCounter++); + cmdHandler.CommandExecuted += (_, e) => commandsRan++; this.client.Disconnected += _ => Reset(); } public Task Print() => Task.FromResult($@"`Author: Kwoth` `Library: Discord.Net` `Bot Version: {BotVersion}` `Bot id: {(client.GetCurrentUser()).Id}` -`Owners' Ids:` +`Owners' Ids: {string.Join(", ", NadekoBot.Credentials.OwnerIds)}` `Uptime: {GetUptimeString()}` `Servers: {client.GetGuilds().Count} | TextChannels: {client.GetGuilds().SelectMany(g => g.GetChannels().Where(c => c is ITextChannel)).Count()} | VoiceChannels: {client.GetGuilds().SelectMany(g => g.GetChannels().Where(c => c is IVoiceChannel)).Count()}` +`Commands Ran this session: {commandsRan}` `Messages: {messageCounter} ({messageCounter / (double)GetUptime().TotalSeconds:F2}/sec)` `Heap: {Heap} MB`"); public Task Reset() diff --git a/src/NadekoBot/Services/NadekoRandom.cs b/src/NadekoBot/Services/NadekoRandom.cs new file mode 100644 index 00000000..b75e32fe --- /dev/null +++ b/src/NadekoBot/Services/NadekoRandom.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services +{ + public class NadekoRandom : Random + { + RandomNumberGenerator rng; + + public NadekoRandom() : base() + { + rng = RandomNumberGenerator.Create(); + } + + private NadekoRandom(int Seed) : base(Seed) + { + rng = RandomNumberGenerator.Create(); + } + + public override int Next() + { + var bytes = new byte[sizeof(int)]; + rng.GetBytes(bytes); + return Math.Abs(BitConverter.ToInt32(bytes, 0)); + } + + public override int Next(int maxValue) + { + if (maxValue <= 0) + throw new ArgumentOutOfRangeException(); + var bytes = new byte[sizeof(int)]; + rng.GetBytes(bytes); + return Math.Abs(BitConverter.ToInt32(bytes, 0)) % maxValue; + } + + public override int Next(int minValue, int maxValue) + { + if (minValue > maxValue) + throw new ArgumentOutOfRangeException(); + if (minValue == maxValue) + return minValue; + var bytes = new byte[sizeof(int)]; + rng.GetBytes(bytes); + var num = BitConverter.ToInt32(bytes, 0); + var sign = Math.Sign(BitConverter.ToInt32(bytes, 0)); + return (sign * BitConverter.ToInt32(bytes, 0)) % (maxValue - minValue) + minValue; + } + + public override void NextBytes(byte[] buffer) + { + rng.GetBytes(buffer); + } + + protected override double Sample() + { + var bytes = new byte[sizeof(double)]; + rng.GetBytes(bytes); + return Math.Abs(BitConverter.ToDouble(bytes, 0) / double.MaxValue + 1); + } + + public override double NextDouble() + { + var bytes = new byte[sizeof(double)]; + rng.GetBytes(bytes); + return BitConverter.ToDouble(bytes, 0); + } + } +} diff --git a/src/NadekoBot/_Classes/ObservableConcurrentDictionary.cs b/src/NadekoBot/_Classes/ObservableConcurrentDictionary.cs deleted file mode 100644 index cd364328..00000000 --- a/src/NadekoBot/_Classes/ObservableConcurrentDictionary.cs +++ /dev/null @@ -1,204 +0,0 @@ -//-------------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ObservableConcurrentDictionary.cs -// -//-------------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Diagnostics; -using System.Threading; - -namespace System.Collections.Concurrent -{ - /// - /// Provides a thread-safe dictionary for use with data binding. - /// - /// Specifies the type of the keys in this collection. - /// Specifies the type of the values in this collection. - [DebuggerDisplay("Count={Count}")] - public class ObservableConcurrentDictionary : - ICollection>, IDictionary, - INotifyCollectionChanged, INotifyPropertyChanged - { - private readonly SynchronizationContext _context; - private readonly ConcurrentDictionary _dictionary; - - /// - /// Initializes an instance of the ObservableConcurrentDictionary class. - /// - public ObservableConcurrentDictionary() - { - _context = AsyncOperationManager.SynchronizationContext; - _dictionary = new ConcurrentDictionary(); - } - - /// Event raised when the collection changes. - public event NotifyCollectionChangedEventHandler CollectionChanged; - /// Event raised when a property on the collection changes. - public event PropertyChangedEventHandler PropertyChanged; - - /// - /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary. - /// - private void NotifyObserversOfChange() - { - var collectionHandler = CollectionChanged; - var propertyHandler = PropertyChanged; - if (collectionHandler != null || propertyHandler != null) - { - _context.Post(s => - { - if (collectionHandler != null) - { - collectionHandler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - if (propertyHandler != null) - { - propertyHandler(this, new PropertyChangedEventArgs("Count")); - propertyHandler(this, new PropertyChangedEventArgs("Keys")); - propertyHandler(this, new PropertyChangedEventArgs("Values")); - } - }, null); - } - } - - /// Attempts to add an item to the dictionary, notifying observers of any changes. - /// The item to be added. - /// Whether the add was successful. - private bool TryAddWithNotification(KeyValuePair item) - { - return TryAddWithNotification(item.Key, item.Value); - } - - /// Attempts to add an item to the dictionary, notifying observers of any changes. - /// The key of the item to be added. - /// The value of the item to be added. - /// Whether the add was successful. - private bool TryAddWithNotification(TKey key, TValue value) - { - bool result = _dictionary.TryAdd(key, value); - if (result) NotifyObserversOfChange(); - return result; - } - - /// Attempts to remove an item from the dictionary, notifying observers of any changes. - /// The key of the item to be removed. - /// The value of the item removed. - /// Whether the removal was successful. - private bool TryRemoveWithNotification(TKey key, out TValue value) - { - bool result = _dictionary.TryRemove(key, out value); - if (result) NotifyObserversOfChange(); - return result; - } - - /// Attempts to add or update an item in the dictionary, notifying observers of any changes. - /// The key of the item to be updated. - /// The new value to set for the item. - /// Whether the update was successful. - private void UpdateWithNotification(TKey key, TValue value) - { - _dictionary[key] = value; - NotifyObserversOfChange(); - } - - #region ICollection> Members - void ICollection>.Add(KeyValuePair item) - { - TryAddWithNotification(item); - } - - void ICollection>.Clear() - { - ((ICollection>)_dictionary).Clear(); - NotifyObserversOfChange(); - } - - bool ICollection>.Contains(KeyValuePair item) - { - return ((ICollection>)_dictionary).Contains(item); - } - - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) - { - ((ICollection>)_dictionary).CopyTo(array, arrayIndex); - } - - int ICollection>.Count { - get { return ((ICollection>)_dictionary).Count; } - } - - bool ICollection>.IsReadOnly { - get { return ((ICollection>)_dictionary).IsReadOnly; } - } - - bool ICollection>.Remove(KeyValuePair item) - { - TValue temp; - return TryRemoveWithNotification(item.Key, out temp); - } - #endregion - - #region IEnumerable> Members - IEnumerator> IEnumerable>.GetEnumerator() - { - return ((ICollection>)_dictionary).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ((ICollection>)_dictionary).GetEnumerator(); - } - #endregion - - #region IDictionary Members - public void Add(TKey key, TValue value) - { - TryAddWithNotification(key, value); - } - - public bool ContainsKey(TKey key) - { - return _dictionary.ContainsKey(key); - } - - public ICollection Keys { - get { return _dictionary.Keys; } - } - - public bool Remove(TKey key) - { - TValue temp; - return TryRemoveWithNotification(key, out temp); - } - - public bool TryGetValue(TKey key, out TValue value) - { - return _dictionary.TryGetValue(key, out value); - } - - public bool TryAdd(TKey key, TValue value) - { - return TryAddWithNotification(key, value); - } - - public ICollection Values { - get { return _dictionary.Values; } - } - - public TValue this[TKey key] { - get { return _dictionary[key]; } - set { UpdateWithNotification(key, value); } - } - - public bool TryRemove(TKey key, out TValue value) - { - return TryRemoveWithNotification(key, out value); - } - #endregion - } -} \ No newline at end of file diff --git a/src/NadekoBot/_Classes/SearchHelper.cs b/src/NadekoBot/_Classes/SearchHelper.cs deleted file mode 100644 index 8b434786..00000000 --- a/src/NadekoBot/_Classes/SearchHelper.cs +++ /dev/null @@ -1,287 +0,0 @@ -using NadekoBot.Classes.JSONModels; -using NadekoBot.Extensions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Security.Authentication; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace NadekoBot.Classes -{ - public enum RequestHttpMethod - { - Get, - Post - } - - public static class SearchHelper - { - private static DateTime lastRefreshed = DateTime.MinValue; - private static string token { get; set; } = ""; - - public static async Task GetResponseStreamAsync(string url, - IEnumerable> headers = null, RequestHttpMethod method = RequestHttpMethod.Get) - { - if (string.IsNullOrWhiteSpace(url)) - throw new ArgumentNullException(nameof(url)); - var cl = new HttpClient(); - cl.DefaultRequestHeaders.Clear(); - switch (method) - { - case RequestHttpMethod.Get: - if (headers != null) - { - foreach (var header in headers) - { - cl.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value); - } - } - return await cl.GetStreamAsync(url).ConfigureAwait(false); - case RequestHttpMethod.Post: - FormUrlEncodedContent formContent = null; - if (headers != null) - { - formContent = new FormUrlEncodedContent(headers); - } - var message = await cl.PostAsync(url, formContent).ConfigureAwait(false); - return await message.Content.ReadAsStreamAsync().ConfigureAwait(false); - default: - throw new NotImplementedException("That type of request is unsupported."); - } - } - - public static async Task GetResponseStringAsync(string url, - IEnumerable> headers = null, - RequestHttpMethod method = RequestHttpMethod.Get) - { - - using (var streamReader = new StreamReader(await GetResponseStreamAsync(url, headers, method).ConfigureAwait(false))) - { - return await streamReader.ReadToEndAsync().ConfigureAwait(false); - } - } - - public static async Task GetAnimeData(string query) - { - if (string.IsNullOrWhiteSpace(query)) - throw new ArgumentNullException(nameof(query)); - - await RefreshAnilistToken().ConfigureAwait(false); - - var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query); - var smallContent = ""; - var cl = new RestSharp.RestClient("http://anilist.co/api"); - var rq = new RestSharp.RestRequest("/anime/search/" + Uri.EscapeUriString(query)); - rq.AddParameter("access_token", token); - smallContent = cl.Execute(rq).Content; - var smallObj = JArray.Parse(smallContent)[0]; - - rq = new RestSharp.RestRequest("/anime/" + smallObj["id"]); - rq.AddParameter("access_token", token); - var content = cl.Execute(rq).Content; - - return await Task.Run(() => JsonConvert.DeserializeObject(content)).ConfigureAwait(false); - } - - public static async Task GetMangaData(string query) - { - if (string.IsNullOrWhiteSpace(query)) - throw new ArgumentNullException(nameof(query)); - - await RefreshAnilistToken().ConfigureAwait(false); - - var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query); - var smallContent = ""; - var cl = new RestSharp.RestClient("http://anilist.co/api"); - var rq = new RestSharp.RestRequest("/manga/search/" + Uri.EscapeUriString(query)); - rq.AddParameter("access_token", token); - smallContent = cl.Execute(rq).Content; - var smallObj = JArray.Parse(smallContent)[0]; - - rq = new RestSharp.RestRequest("/manga/" + smallObj["id"]); - rq.AddParameter("access_token", token); - var content = cl.Execute(rq).Content; - - return await Task.Run(() => JsonConvert.DeserializeObject(content)).ConfigureAwait(false); - } - - private static async Task RefreshAnilistToken() - { - if (DateTime.Now - lastRefreshed > TimeSpan.FromMinutes(29)) - lastRefreshed = DateTime.Now; - else - { - return; - } - var headers = new Dictionary { - {"grant_type", "client_credentials"}, - {"client_id", "kwoth-w0ki9"}, - {"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"}, - }; - var content = await GetResponseStringAsync( - "http://anilist.co/api/auth/access_token", - headers, - RequestHttpMethod.Post).ConfigureAwait(false); - - token = JObject.Parse(content)["access_token"].ToString(); - } - - public static async Task FindYoutubeUrlByKeywords(string keywords) - { - if (string.IsNullOrWhiteSpace(keywords)) - throw new ArgumentNullException(nameof(keywords), "Query not specified."); - if (keywords.Length > 150) - throw new ArgumentException("Query is too long."); - - //maybe it is already a youtube url, in which case we will just extract the id and prepend it with youtube.com?v= - var match = new Regex("(?:youtu\\.be\\/|v=)(?[\\da-zA-Z\\-_]*)").Match(keywords); - if (match.Length > 1) - { - return $"https://www.youtube.com/watch?v={match.Groups["id"].Value}"; - } - - if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.GoogleAPIKey)) - throw new InvalidCredentialException("Google API Key is missing."); - - var response = await GetResponseStringAsync( - $"https://www.googleapis.com/youtube/v3/search?" + - $"part=snippet&maxResults=1" + - $"&q={Uri.EscapeDataString(keywords)}" + - $"&key={NadekoBot.Credentials.GoogleAPIKey}").ConfigureAwait(false); - JObject obj = JObject.Parse(response); - - var data = JsonConvert.DeserializeObject(response); - - if (data.items.Length > 0) - { - var toReturn = "http://www.youtube.com/watch?v=" + data.items[0].id.videoId.ToString(); - return toReturn; - } - else - return null; - } - - public static async Task> GetRelatedVideoIds(string id, int count = 1) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - var match = new Regex("(?:youtu\\.be\\/|v=)(?[\\da-zA-Z\\-_]*)").Match(id); - if (match.Length > 1) - { - id = match.Groups["id"].Value; - } - var response = await GetResponseStringAsync( - $"https://www.googleapis.com/youtube/v3/search?" + - $"part=snippet&maxResults={count}&type=video" + - $"&relatedToVideoId={id}" + - $"&key={NadekoBot.Credentials.GoogleAPIKey}").ConfigureAwait(false); - JObject obj = JObject.Parse(response); - - var data = JsonConvert.DeserializeObject(response); - - return data.items.Select(v => "http://www.youtube.com/watch?v=" + v.id.videoId); - } - - public static async Task GetPlaylistIdByKeyword(string query) - { - if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.GoogleAPIKey)) - throw new ArgumentNullException(nameof(query)); - var match = new Regex("(?:youtu\\.be\\/|list=)(?[\\da-zA-Z\\-_]*)").Match(query); - if (match.Length > 1) - { - return match.Groups["id"].Value.ToString(); - } - var link = "https://www.googleapis.com/youtube/v3/search?part=snippet" + - "&maxResults=1&type=playlist" + - $"&q={Uri.EscapeDataString(query)}" + - $"&key={NadekoBot.Credentials.GoogleAPIKey}"; - - var response = await GetResponseStringAsync(link).ConfigureAwait(false); - var data = JsonConvert.DeserializeObject(response); - JObject obj = JObject.Parse(response); - - return data.items.Length > 0 ? data.items[0].id.playlistId.ToString() : null; - } - - public static async Task> GetVideoIDs(string playlist, int number = 50) - { - if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.GoogleAPIKey)) - { - throw new ArgumentNullException(nameof(playlist)); - } - if (number < 1) - throw new ArgumentOutOfRangeException(); - - string nextPageToken = null; - - List toReturn = new List(); - - do - { - var toGet = number > 50 ? 50 : number; - number -= toGet; - var link = - $"https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails" + - $"&maxResults={toGet}" + - $"&playlistId={playlist}" + - $"&key={NadekoBot.Credentials.GoogleAPIKey}"; - if (!string.IsNullOrWhiteSpace(nextPageToken)) - link += $"&pageToken={nextPageToken}"; - var response = await GetResponseStringAsync(link).ConfigureAwait(false); - var data = await Task.Run(() => JsonConvert.DeserializeObject(response)).ConfigureAwait(false); - nextPageToken = data.nextPageToken; - toReturn.AddRange(data.items.Select(i => i.contentDetails.videoId)); - } while (number > 0 && !string.IsNullOrWhiteSpace(nextPageToken)); - - return toReturn; - } - - public static async Task ShortenUrl(string url) - { - if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.GoogleAPIKey)) return url; - try - { - var httpWebRequest = - (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/urlshortener/v1/url?key=" + - NadekoBot.Credentials.GoogleAPIKey); - httpWebRequest.ContentType = "application/json"; - httpWebRequest.Method = "POST"; - - using (var streamWriter = new StreamWriter(await httpWebRequest.GetRequestStreamAsync().ConfigureAwait(false))) - { - var json = "{\"longUrl\":\"" + Uri.EscapeDataString(url) + "\"}"; - streamWriter.Write(json); - } - - var httpResponse = (await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) as HttpWebResponse; - var responseStream = httpResponse.GetResponseStream(); - using (var streamReader = new StreamReader(responseStream)) - { - var responseText = await streamReader.ReadToEndAsync().ConfigureAwait(false); - return Regex.Match(responseText, @"""id"": ?""(?.+)""").Groups["id"].Value; - } - } - catch (Exception ex) - { - Console.WriteLine("Shortening of this url failed: " + url); - Console.WriteLine(ex.ToString()); - return url; - } - } - - public static string ShowInPrettyCode(IEnumerable items, Func howToPrint, int cols = 3) - { - var i = 0; - return "```xl\n" + string.Join("\n", items.GroupBy(item => (i++) / cols) - .Select(ig => string.Concat(ig.Select(el => howToPrint(el))))) - + $"\n```"; - } - } -} diff --git a/src/NadekoBot/_Classes/ServerSpecificConfig.cs b/src/NadekoBot/_Classes/ServerSpecificConfig.cs deleted file mode 100644 index 9142e574..00000000 --- a/src/NadekoBot/_Classes/ServerSpecificConfig.cs +++ /dev/null @@ -1,292 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace NadekoBot.Classes -{ - internal class SpecificConfigurations - { - public static SpecificConfigurations Default { get; } = new SpecificConfigurations(); - public static bool Instantiated { get; private set; } - - private const string filePath = "data/ServerSpecificConfigs.json"; - - static SpecificConfigurations() { } - - private SpecificConfigurations() - { - - if (File.Exists(filePath)) - { - try - { - configs = JsonConvert - .DeserializeObject>( - File.ReadAllText(filePath), new JsonSerializerSettings() - { - Error = (s, e) => - { - if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels") - { - e.ErrorContext.Handled = true; - } - } - }); - } - catch (Exception ex) - { - Console.WriteLine($"Deserialization failing: {ex}"); - } - } - if (configs == null) - configs = new ConcurrentDictionary(); - Instantiated = true; - } - - private readonly ConcurrentDictionary configs; - - public IEnumerable AllConfigs => configs.Values; - - public ServerSpecificConfig Of(ulong id) => - configs.GetOrAdd(id, _ => new ServerSpecificConfig()); - - private readonly SemaphoreSlim saveLock = new SemaphoreSlim(1, 1); - - public async Task Save() - { - await saveLock.WaitAsync(); - try - { - File.WriteAllText(filePath, JsonConvert.SerializeObject(configs, Formatting.Indented)); - } - finally - { - saveLock.Release(); - } - } - } - - internal class ServerSpecificConfig : INotifyPropertyChanged - { - [JsonProperty("VoicePlusTextEnabled")] - private bool voicePlusTextEnabled; - [JsonIgnore] - public bool VoicePlusTextEnabled { - get { return voicePlusTextEnabled; } - set { - voicePlusTextEnabled = value; - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - } - } - [JsonProperty("SendPrivateMessageOnMention")] - private bool sendPrivateMessageOnMention; - [JsonIgnore] - public bool SendPrivateMessageOnMention { - get { return sendPrivateMessageOnMention; } - set { - sendPrivateMessageOnMention = value; - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - } - } - - [JsonProperty("LogChannel")] - private ulong? logServerChannel = null; - [JsonIgnore] - public ulong? LogServerChannel { - get { return logServerChannel; } - set { - logServerChannel = value; - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - } - } - - [JsonIgnore] - private ObservableCollection logserverIgnoreChannels; - public ObservableCollection LogserverIgnoreChannels { - get { return logserverIgnoreChannels; } - set { - logserverIgnoreChannels = value; - if (value != null) - logserverIgnoreChannels.CollectionChanged += (s, e) => - { - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - }; - } - } - - [JsonProperty("LogPresenceChannel")] - private ulong? logPresenceChannel = null; - [JsonIgnore] - public ulong? LogPresenceChannel { - get { return logPresenceChannel; } - set { - logPresenceChannel = value; - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - } - } - - [JsonIgnore] - private ObservableConcurrentDictionary voiceChannelLog; - public ObservableConcurrentDictionary VoiceChannelLog { - get { return voiceChannelLog; } - set { - voiceChannelLog = value; - if (value != null) - voiceChannelLog.CollectionChanged += (s, e) => - { - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - }; - } - } - - [JsonIgnore] - private ObservableCollection listOfSelfAssignableRoles; - public ObservableCollection ListOfSelfAssignableRoles { - get { return listOfSelfAssignableRoles; } - set { - listOfSelfAssignableRoles = value; - if (value != null) - listOfSelfAssignableRoles.CollectionChanged += (s, e) => - { - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - }; - } - } - - - - [JsonIgnore] - private ulong autoAssignedRole = 0; - public ulong AutoAssignedRole { - get { return autoAssignedRole; } - set { - autoAssignedRole = value; - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - } - } - - [JsonIgnore] - private ObservableConcurrentDictionary generateCurrencyChannels; - public ObservableConcurrentDictionary GenerateCurrencyChannels { - get { return generateCurrencyChannels; } - set { - generateCurrencyChannels = value; - if (value != null) - generateCurrencyChannels.CollectionChanged += (s, e) => - { - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - }; - } - } - - [JsonIgnore] - private bool autoDeleteMessagesOnCommand = false; - public bool AutoDeleteMessagesOnCommand { - get { return autoDeleteMessagesOnCommand; } - set { - autoDeleteMessagesOnCommand = value; - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - } - } - - [JsonIgnore] - private bool exclusiveSelfAssignedRoles = false; - public bool ExclusiveSelfAssignedRoles - { - get { return exclusiveSelfAssignedRoles; } - set - { - exclusiveSelfAssignedRoles = value; - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - } - } - - - [JsonIgnore] - private ObservableCollection observingStreams; - public ObservableCollection ObservingStreams { - get { return observingStreams; } - set { - observingStreams = value; - if (value != null) - observingStreams.CollectionChanged += (s, e) => - { - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - }; - } - } - - [JsonIgnore] - private float defaultMusicVolume = 1f; - public float DefaultMusicVolume { - get { return defaultMusicVolume; } - set { - defaultMusicVolume = value; - if (!SpecificConfigurations.Instantiated) return; - OnPropertyChanged(); - } - } - - public ServerSpecificConfig() - { - ListOfSelfAssignableRoles = new ObservableCollection(); - ObservingStreams = new ObservableCollection(); - GenerateCurrencyChannels = new ObservableConcurrentDictionary(); - VoiceChannelLog = new ObservableConcurrentDictionary(); - LogserverIgnoreChannels = new ObservableCollection(); - } - - public event PropertyChangedEventHandler PropertyChanged = async delegate { await SpecificConfigurations.Default.Save().ConfigureAwait(false); }; - - private void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } - } - - public class StreamNotificationConfig : IEquatable - { - public string Username { get; set; } - public StreamType Type { get; set; } - public ulong ServerId { get; set; } - public ulong ChannelId { get; set; } - public bool LastStatus { get; set; } - - public enum StreamType - { - Twitch, - Beam, - Hitbox, - YoutubeGaming - } - - public bool Equals(StreamNotificationConfig other) => - this.Username.ToLower().Trim() == other.Username.ToLower().Trim() && - this.Type == other.Type && - this.ServerId == other.ServerId; - - public override int GetHashCode() - { - return (int)ServerId + Username.Length + (int)Type; - } - } -} diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index ecd748a1..e0d37a55 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -33,47 +33,85 @@ namespace NadekoBot.Extensions public static async Task SendFileAsync(this IGuildUser user, Stream fileStream, string fileName, string caption = null, bool isTTS = false) => await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(fileStream, fileName, caption, isTTS).ConfigureAwait(false); - public static async Task Reply(this IUserMessage msg, string content) => + public static async Task Reply(this IUserMessage msg, string content) => await msg.Channel.SendMessageAsync(content).ConfigureAwait(false); - public static Task IsAuthor(this IUserMessage msg) => - Task.FromResult(NadekoBot.Client.GetCurrentUser().Id == msg.Author.Id); + public static bool IsAuthor(this IUserMessage msg) => + NadekoBot.Client.GetCurrentUser().Id == msg.Author.Id; public static IEnumerable Members(this IRole role) => - NadekoBot.Client.GetGuilds().Where(g => g.Id == role.GuildId).FirstOrDefault()?.GetUsers().Where(u => u.Roles.Contains(role)) ?? Enumerable.Empty(); + NadekoBot.Client.GetGuild(role.GuildId)?.GetUsers().Where(u => u.Roles.Contains(role)) ?? Enumerable.Empty(); - public static async Task ReplyLong(this IUserMessage msg, string content, string breakOn = "\n", string addToEnd = "", string addToStart = "") + public static async Task ReplyLong(this IUserMessage msg, string content, string[] breakOn = null, string addToPartialEnd = "", string addToPartialStart = "") { - - if (content.Length < 2000) return new[] { await msg.Channel.SendMessageAsync(content).ConfigureAwait(false) }; + if (content.Length == 0) return null; + var characterLimit = 1750; + if (content.Length < characterLimit) return new[] { await msg.Channel.SendMessageAsync(content).ConfigureAwait(false) }; + if (breakOn == null) breakOn = new[] { "\n", " ", " " }; var list = new List(); - - var temp = Regex.Split(content, breakOn).Select(x => x += breakOn).ToList(); - string toolong; - //while ((toolong = temp.FirstOrDefault(x => x.Length > 2000)) != null) - //{ - // more desperate measures == split on whitespace? - //} - - StringBuilder builder = new StringBuilder(); - //make this less crappy to look at, maybe it's bugged - for (int i = 0; i < temp.Count; i++) + var splitItems = new List(); + foreach (var breaker in breakOn) { - var addition = temp[i]; - //we append - - if (builder.Length == 0 && i != 0) builder.Append(addToStart + addition); - else builder.Append(addition); - - //Check if the next would have room - if (i + 1 >= temp.Count || temp[i + 1].Length + builder.Length + addToEnd.Length > 2000) + if (splitItems.Count == 0) + { + splitItems = Regex.Split(content, $"(?={breaker})").Where(s => !string.IsNullOrWhiteSpace(s)).ToList(); + } + else + { + for (int i = 0; i < splitItems.Count; i++) + { + var temp = splitItems[i]; + if (temp.Length > characterLimit) + { + var splitDeep = Regex.Split(temp, $"(?={breaker})").Where(s => !string.IsNullOrWhiteSpace(s)); + splitItems.RemoveAt(i); + splitItems.InsertRange(i, splitDeep); + } + } + } + if (splitItems.All(s => s.Length < characterLimit)) break; + } + //We remove any entries that are larger than 2000 chars + if (splitItems.Any(s => s.Length >= characterLimit)) + { + splitItems = splitItems.Where(s => s.Length < characterLimit).ToList(); + } + //ensured every item can be sent (if individually) + var firstItem = true; + Queue buildItems = new Queue(splitItems); + StringBuilder builder = new StringBuilder(); + + while (buildItems.Count > 0) + { + if (builder.Length == 0) + { + //first item to add + if (!firstItem) + builder.Append(addToPartialStart); + else + firstItem = false; + builder.Append(buildItems.Dequeue()); + } + else + { + builder.Append(buildItems.Dequeue()); + } + if (buildItems.Count == 0) { - if (i + 1 < temp.Count) builder.Append(addToEnd); list.Add(await msg.Channel.SendMessageAsync(builder.ToString())); builder.Clear(); } + else + { + var peeked = buildItems.Peek(); + if (builder.Length + peeked.Length + addToPartialEnd.Length > characterLimit) + { + builder.Append(addToPartialEnd); + list.Add(await msg.Channel.SendMessageAsync(builder.ToString())); + builder.Clear(); + } + } } - return list.ToArray(); } @@ -227,5 +265,6 @@ namespace NadekoBot.Extensions public static ulong GiB(this ulong value) => value.MiB() * 1024; public static ulong GB(this ulong value) => value.MB() * 1000; + public static string Unmention(this string str) => str.Replace("@", "ම"); } } \ No newline at end of file diff --git a/src/NadekoBot/_Models/DataModels/AnnouncementModel.cs b/src/NadekoBot/_Models/DataModels/AnnouncementModel.cs deleted file mode 100644 index c4103dad..00000000 --- a/src/NadekoBot/_Models/DataModels/AnnouncementModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Newtonsoft.Json; - -namespace NadekoBot.DataModels -{ - internal class Announcement : IDataModel - { - public long ServerId { get; set; } = 0; - public bool Greet { get; set; } = false; - public bool GreetPM { get; set; } = false; - [JsonProperty("greetChannel")] - public long GreetChannelId { get; set; } = 0; - public string GreetText { get; set; } = "Welcome %user%!"; - public bool Bye { get; set; } = false; - public bool ByePM { get; set; } = false; - [JsonProperty("byeChannel")] - public long ByeChannelId { get; set; } = 0; - public string ByeText { get; set; } = "%user% has left the server."; - public bool DeleteGreetMessages { get; set; } = true; - } -} diff --git a/src/NadekoBot/_Models/DataModels/CommandModel.cs b/src/NadekoBot/_Models/DataModels/CommandModel.cs deleted file mode 100644 index 204ec061..00000000 --- a/src/NadekoBot/_Models/DataModels/CommandModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace NadekoBot.DataModels { - internal class Command : IDataModel { - public long UserId { get; set; } - public string UserName { get; set; } - public long ServerId { get; set; } - public string ServerName { get; set; } - public long ChannelId { get; set; } - public string ChannelName { get; set; } - public string CommandName { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/CurrencyStateModel.cs b/src/NadekoBot/_Models/DataModels/CurrencyStateModel.cs deleted file mode 100644 index 9c8b8671..00000000 --- a/src/NadekoBot/_Models/DataModels/CurrencyStateModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NadekoBot.DataModels { - internal class CurrencyState : IDataModel { - public long Value { get; set; } - [SQLite.Unique] - public long UserId { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/CurrencyTransactionModel.cs b/src/NadekoBot/_Models/DataModels/CurrencyTransactionModel.cs deleted file mode 100644 index 51fcccc3..00000000 --- a/src/NadekoBot/_Models/DataModels/CurrencyTransactionModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NadekoBot.DataModels { - internal class CurrencyTransaction : IDataModel { - public string Reason { get; set; } - public int Value { get; set; } - public long UserId { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/Donator.cs b/src/NadekoBot/_Models/DataModels/Donator.cs deleted file mode 100644 index 483898ec..00000000 --- a/src/NadekoBot/_Models/DataModels/Donator.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NadekoBot.DataModels { - internal class Donator : IDataModel { - public long UserId { get; set; } - public string UserName { get; set; } - public int Amount { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/IDataModel.cs b/src/NadekoBot/_Models/DataModels/IDataModel.cs deleted file mode 100644 index 90191474..00000000 --- a/src/NadekoBot/_Models/DataModels/IDataModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using SQLite; -using System; - -namespace NadekoBot.DataModels -{ - internal abstract class IDataModel - { - [PrimaryKey, AutoIncrement] - public int? Id { get; set; } - [Newtonsoft.Json.JsonProperty("createdAt")] - public DateTime DateAdded { get; set; } = DateTime.Now; - public IDataModel() { } - } -} diff --git a/src/NadekoBot/_Models/DataModels/Incident.cs b/src/NadekoBot/_Models/DataModels/Incident.cs deleted file mode 100644 index 2ce2ddd6..00000000 --- a/src/NadekoBot/_Models/DataModels/Incident.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace NadekoBot.DataModels -{ - class Incident : IDataModel - { - public long ServerId { get; set; } - public long ChannelId { get; set; } - public string Text { get; set; } - public bool Read { get; set; } = false; - } -} diff --git a/src/NadekoBot/_Models/DataModels/MusicPlaylist.cs b/src/NadekoBot/_Models/DataModels/MusicPlaylist.cs deleted file mode 100644 index 60973baa..00000000 --- a/src/NadekoBot/_Models/DataModels/MusicPlaylist.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace NadekoBot.DataModels -{ - internal class MusicPlaylist : IDataModel - { - public string Name { get; set; } - public long CreatorId { get; set; } - public string CreatorName { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/PlaylistSongInfo.cs b/src/NadekoBot/_Models/DataModels/PlaylistSongInfo.cs deleted file mode 100644 index 84929781..00000000 --- a/src/NadekoBot/_Models/DataModels/PlaylistSongInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace NadekoBot.DataModels -{ - internal class PlaylistSongInfo : IDataModel - { - public int PlaylistId { get; set; } - public int SongInfoId { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/PokeTypes.cs b/src/NadekoBot/_Models/DataModels/PokeTypes.cs deleted file mode 100644 index d0f538af..00000000 --- a/src/NadekoBot/_Models/DataModels/PokeTypes.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace NadekoBot.DataModels -{ - class UserPokeTypes : IDataModel - { - public long UserId { get; set; } - public string type { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/RequestModel.cs b/src/NadekoBot/_Models/DataModels/RequestModel.cs deleted file mode 100644 index 7b86198f..00000000 --- a/src/NadekoBot/_Models/DataModels/RequestModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace NadekoBot.DataModels { - internal class Request : IDataModel { - public string UserName { get; set; } - public long UserId { get; set; } - public string ServerName { get; set; } - public long ServerId { get; set; } - [Newtonsoft.Json.JsonProperty("Request")] - public string RequestText { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/SongInfo.cs b/src/NadekoBot/_Models/DataModels/SongInfo.cs deleted file mode 100644 index 6fe980ac..00000000 --- a/src/NadekoBot/_Models/DataModels/SongInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using SQLite; - -namespace NadekoBot.DataModels -{ - internal class SongInfo : IDataModel - { - public string Provider { get; internal set; } - public int ProviderType { get; internal set; } - public string Title { get; internal set; } - public string Uri { get; internal set; } - [Unique] - public string Query { get; internal set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/StatsModel.cs b/src/NadekoBot/_Models/DataModels/StatsModel.cs deleted file mode 100644 index 480b569f..00000000 --- a/src/NadekoBot/_Models/DataModels/StatsModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace NadekoBot.DataModels { - internal class Stats : IDataModel { - public int ConnectedServers { get; set; } - public int OnlineUsers { get; set; } - public TimeSpan Uptime { get; set; } - public int RealOnlineUsers { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/TestDataModel.cs b/src/NadekoBot/_Models/DataModels/TestDataModel.cs deleted file mode 100644 index 247743ca..00000000 --- a/src/NadekoBot/_Models/DataModels/TestDataModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace NadekoBot.DataModels -{ - internal class TestDataModel : IDataModel - { - public long TestNumber { get; set; } - public string TestString { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/TypingArticleModel.cs b/src/NadekoBot/_Models/DataModels/TypingArticleModel.cs deleted file mode 100644 index 2557a51c..00000000 --- a/src/NadekoBot/_Models/DataModels/TypingArticleModel.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace NadekoBot.DataModels { - internal class TypingArticle : IDataModel { - public string Text { get; set; } - } -} diff --git a/src/NadekoBot/_Models/DataModels/UserQuoteModel.cs b/src/NadekoBot/_Models/DataModels/UserQuoteModel.cs deleted file mode 100644 index 544252cc..00000000 --- a/src/NadekoBot/_Models/DataModels/UserQuoteModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NadekoBot.DataModels { - internal class UserQuote : IDataModel { - public string UserName { get; set; } - public string Keyword { get; set; } - public string Text { get; set; } - } -} diff --git a/src/NadekoBot/_Models/JSONModels/Configuration.cs b/src/NadekoBot/_Models/JSONModels/Configuration.cs deleted file mode 100644 index 04a4895e..00000000 --- a/src/NadekoBot/_Models/JSONModels/Configuration.cs +++ /dev/null @@ -1,246 +0,0 @@ -using Discord; -using NadekoBot.Extensions; -using Newtonsoft.Json; -using System.Collections.Generic; -using System.IO; -using System.Runtime.Serialization; -using System.Threading; -using System.Threading.Tasks; - -namespace NadekoBot.Classes.JSONModels -{ - public class Configuration - { - [JsonIgnore] - public static readonly Dictionary> DefaultCustomReactions = new Dictionary> - { - {@"\o\", new List() - { "/o/" } }, - {"/o/", new List() - { @"\o\" } }, - {"moveto", new List() { - @"(👉 ͡° ͜ʖ ͡°)👉 %target%" } }, - {"comeatmebro", new List() { - "%target% (ง’̀-‘́)ง" } }, - {"e", new List() { - "%user% did it 😒 🔫", - "%target% did it 😒 🔫" } }, - {"%mention% insult", new List() { - "%target% You are a poop.", - "%target% You're a jerk.", - "%target% I will eat you when I get my powers back." - } }, - {"%mention% praise", new List() - { - "%target% You are cool.", - "%target% You are nice!", - "%target% You did a good job.", - "%target% You did something nice.", - "%target% is awesome!", - "%target% Wow." - } }, - {"%mention% pat", new List() { - "http://i.imgur.com/IiQwK12.gif", - "http://i.imgur.com/JCXj8yD.gif", - "http://i.imgur.com/qqBl2bm.gif", - "http://i.imgur.com/eOJlnwP.gif", - "https://45.media.tumblr.com/229ec0458891c4dcd847545c81e760a5/tumblr_mpfy232F4j1rxrpjzo1_r2_500.gif", - "https://media.giphy.com/media/KZQlfylo73AMU/giphy.gif", - "https://media.giphy.com/media/12hvLuZ7uzvCvK/giphy.gif", - "http://gallery1.anivide.com/_full/65030_1382582341.gif", - "https://49.media.tumblr.com/8e8a099c4eba22abd3ec0f70fd087cce/tumblr_nxovj9oY861ur1mffo1_500.gif ", - } }, - {"%mention% cry", new List() - { - "http://i.imgur.com/Xg3i1Qy.gif", - "http://i.imgur.com/3K8DRrU.gif", - "http://i.imgur.com/k58BcAv.gif", - "http://i.imgur.com/I2fLXwo.gif" - } }, - {"%mention% are you real?", new List() - { - "%user%, I will be soon." - } }, - {"%mention% are you there?", new List() - { - "Yes. :)" - } }, - {"%mention% draw", new List() { - "Sorry, I don't gamble, type $draw for that function." - } }, - {"%mention% bb", new List() - { - "Bye %target%" - } }, - {"%mention% call", new List() { - "Calling %target%" - } }, - {"%mention% disguise", new List() { - "https://cdn.discordapp.com/attachments/140007341880901632/156721710458994690/Cc5mixjUYAADgBs.jpg", - "https://cdn.discordapp.com/attachments/140007341880901632/156721715831898113/hqdefault.jpg", - "https://cdn.discordapp.com/attachments/140007341880901632/156721724430352385/okawari_01_haruka_weird_mask.jpg", - "https://cdn.discordapp.com/attachments/140007341880901632/156721728763068417/mustache-best-girl.png" - - } }, - {"%mention% inv", new List() { - "To invite your bot, click on this link -> " - } }, - { "%mention% threaten", new List() { - "You wanna die, %target%?" - } }, - { "%mention% archer", new List() { - "http://i.imgur.com/Bha9NhL.jpg" - } } - }; - - public bool DontJoinServers { get; set; } = false; - public bool ForwardMessages { get; set; } = true; - public bool ForwardToAllOwners { get; set; } = false; - public bool IsRotatingStatus { get; set; } = false; - public int BufferSize { get; set; } = 4.MiB(); - - public string[] RaceAnimals { get; internal set; } = { - "🐼", - "🐻", - "🐧", - "🐨", - "🐬", - "🐞", - "🦀", - "🦄" }; - - [JsonIgnore] - public List Quotes { get; set; } = new List(); - - [JsonIgnore] - public List PokemonTypes { get; set; } = new List(); - - public string RemindMessageFormat { get; set; } = "❗⏰**I've been told to remind you to '%message%' now by %user%.**⏰❗"; - - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public Dictionary> CustomReactions { get; set; } - - public List RotatingStatuses { get; set; } = new List(); - public CommandPrefixesModel CommandPrefixes { get; set; } = new CommandPrefixesModel(); - public HashSet ServerBlacklist { get; set; } = new HashSet(); - public HashSet ChannelBlacklist { get; set; } = new HashSet(); - - public HashSet UserBlacklist { get; set; } = new HashSet() { - 105309315895693312, - 119174277298782216, - 143515953525817344 - }; - - [OnDeserialized] - internal void OnDeserialized(StreamingContext context) - { - if (CustomReactions == null) - { - CustomReactions = DefaultCustomReactions; - } - } - [OnSerializing] - internal void OnSerializing(StreamingContext context) - { - if (CustomReactions == null) - { - CustomReactions = DefaultCustomReactions; - } - } - - public string[] _8BallResponses { get; set; } = - { - "Most definitely yes", - "For sure", - "As I see it, yes", - "My sources say yes", - "Yes", - "Most likely", - "Perhaps", - "Maybe", - "Not sure", - "It is uncertain", - "Ask me again later", - "Don't count on it", - "Probably not", - "Very doubtful", - "Most likely no", - "Nope", - "No", - "My sources say no", - "Dont even think about it", - "Definitely no", - "NO - It may cause disease contraction" - }; - - public string CurrencySign { get; set; } = "🌸"; - public string CurrencyName { get; set; } = "NadekoFlower"; - public string DMHelpString { get; set; } = "Type `-h` for help."; - public string HelpString { get; set; } = @"You can use `{0}modules` command to see a list of all modules. -You can use `{0}commands ModuleName` -(for example `{0}commands Administration`) to see a list of all of the commands in that module. -For a specific command help, use `{0}h ""Command name""` (for example `-h ""!m q""`) - - -**LIST OF COMMANDS CAN BE FOUND ON THIS LINK** - - - -Nadeko Support Server: "; - } - - public class CommandPrefixesModel - { - public string Administration { get; set; } = "."; - public string Searches { get; set; } = "~"; - public string NSFW { get; set; } = "~"; - public string Conversations { get; set; } = "<@{0}>"; - public string ClashOfClans { get; set; } = ","; - public string Help { get; set; } = "-"; - public string Music { get; set; } = "!!"; - public string Trello { get; set; } = "trello "; - public string Games { get; set; } = ">"; - public string Gambling { get; set; } = "$"; - public string Permissions { get; set; } = ";"; - public string Programming { get; set; } = "%"; - public string Pokemon { get; set; } = ">"; - public string Utility { get; set; } = "."; - } - - public static class ConfigHandler - { - private static readonly SemaphoreSlim configLock = new SemaphoreSlim(1, 1); - public static async Task SaveConfig() - { - await configLock.WaitAsync(); - try - { - File.WriteAllText("data/config.json", JsonConvert.SerializeObject(NadekoBot.Config, Formatting.Indented)); - } - finally - { - configLock.Release(); - } - } - - public static bool IsBlackListed(MessageEventArgs evArgs) => IsUserBlacklisted(evArgs.User.Id) || - (!evArgs.Channel.IsPrivate && - (IsChannelBlacklisted(evArgs.Channel.Id) || IsServerBlacklisted(evArgs.Server.Id))); - - public static bool IsServerBlacklisted(ulong id) => NadekoBot.Config.ServerBlacklist.Contains(id); - - public static bool IsChannelBlacklisted(ulong id) => NadekoBot.Config.ChannelBlacklist.Contains(id); - - public static bool IsUserBlacklisted(ulong id) => NadekoBot.Config.UserBlacklist.Contains(id); - } - - public class Quote - { - public string Author { get; set; } - public string Text { get; set; } - - public override string ToString() => - $"{Text}\n\t*-{Author}*"; - } - -} diff --git a/src/NadekoBot/_Models/JSONModels/LocalizedStrings.cs b/src/NadekoBot/_Models/JSONModels/LocalizedStrings.cs deleted file mode 100644 index 861ebb69..00000000 --- a/src/NadekoBot/_Models/JSONModels/LocalizedStrings.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.IO; - -namespace NadekoBot.Classes.JSONModels { - public class LocalizedStrings { - public string[] Insults { get; set; } = { - " You are a poop.", " You're a jerk.", - " I will eat you when I get my powers back." - }; - - public string[] Praises { get; set; } = { - " You are cool.", - " You are nice!", - " You did a good job.", - " You did something nice.", - " is awesome!", - " Wow." - }; - - public static string[] GetAvailableLocales() { - Directory.CreateDirectory("data/locales"); - return Directory.GetFiles("data/locales"); - } - - //public static void HandleLocalization() { - // var locales = LocalizedStrings.GetAvailableLocales(); - - - // Console.WriteLine("Pick a language:\n" + - // "1. English"); - // for (var i = 0; i < locales.Length; i++) { - // Console.WriteLine((i + 2) + ". " + Path.GetFileNameWithoutExtension(locales[i])); - // } - // File.WriteAllText("data/locales/english.json", JsonConvert.SerializeObject(new LocalizedStrings(), Formatting.Indented)); - // try { - // Console.WriteLine($"Type in a number from {1} to {locales.Length + 1}\n"); - // var input = Console.ReadLine(); - // if (input != "1") - // Locale = LocalizedStrings.LoadLocale(locales[int.Parse(input) - 2]); - // } catch (Exception ex) { - // Console.ForegroundColor = ConsoleColor.Red; - // Console.WriteLine(ex); - // Console.ReadKey(); - // return; - // } - //} - - public static LocalizedStrings LoadLocale(string localeFile) => - Newtonsoft.Json.JsonConvert.DeserializeObject(File.ReadAllText(localeFile)); - } -} diff --git a/src/NadekoBot/_Models/JSONModels/MagicItem.cs b/src/NadekoBot/_Models/JSONModels/MagicItem.cs deleted file mode 100644 index 1ef3db1b..00000000 --- a/src/NadekoBot/_Models/JSONModels/MagicItem.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace NadekoBot.Classes.JSONModels -{ - class MagicItem - { - public string Name { get; set; } - public string Description { get; set; } - public override string ToString() => - $"✨`{Name}`\n\t*{Description}*"; - } -} diff --git a/src/NadekoBot/_Models/JSONModels/PokemonType.cs b/src/NadekoBot/_Models/JSONModels/PokemonType.cs deleted file mode 100644 index 02e50e2f..00000000 --- a/src/NadekoBot/_Models/JSONModels/PokemonType.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace NadekoBot.Classes.JSONModels -{ - public class PokemonType - { - public PokemonType(string n, string i, string[] m, List multi) - { - Name = n; - Icon = i; - Moves = m; - Multipliers = multi; - } - public string Name { get; set; } - public List Multipliers { get; set; } - public string Icon { get; set; } - public string[] Moves { get; set; } - } - public class PokemonMultiplier - { - public PokemonMultiplier(string t, double m) - { - Type = t; - Multiplication = m; - } - public string Type { get; set; } - public double Multiplication { get; set; } - } -} diff --git a/src/NadekoBot/_Models/JSONModels/_JSONModels.cs b/src/NadekoBot/_Models/JSONModels/_JSONModels.cs deleted file mode 100644 index c60d32d8..00000000 --- a/src/NadekoBot/_Models/JSONModels/_JSONModels.cs +++ /dev/null @@ -1,155 +0,0 @@ -// ReSharper disable InconsistentNaming - -using System.Diagnostics; - -namespace NadekoBot.Classes.JSONModels -{ - public class Credentials - { - public string Token { get; set; } = ""; - public string ClientId { get; set; } = "170254782546575360"; - public ulong BotId { get; set; } = 1231231231231; - public ulong[] OwnerIds { get; set; } = { 123123123123, 5675675679845 }; - public string GoogleAPIKey { get; set; } = ""; - public string SoundCloudClientID { get; set; } = ""; - public string MashapeKey { get; set; } = ""; - public string LOLAPIKey { get; set; } = ""; - public string TrelloAppKey { get; set; } = ""; - public string CarbonKey { get; set; } = ""; - public string OsuAPIKey { get; set; } = ""; - } - [DebuggerDisplay("{items[0].id.playlistId}")] - public class YoutubePlaylistSearch - { - public YtPlaylistItem[] items { get; set; } - } - public class YtPlaylistItem - { - public YtPlaylistId id { get; set; } - } - public class YtPlaylistId - { - public string kind { get; set; } - public string playlistId { get; set; } - } - [DebuggerDisplay("{items[0].id.videoId}")] - public class YoutubeVideoSearch - { - public YtVideoItem[] items { get; set; } - } - public class YtVideoItem - { - public YtVideoId id { get; set; } - } - public class YtVideoId - { - public string kind { get; set; } - public string videoId { get; set; } - } - public class PlaylistItemsSearch - { - public string nextPageToken { get; set; } - public PlaylistItem[] items { get; set; } - } - public class PlaylistItem - { - public YtVideoId contentDetails { get; set; } - } - - #region wikpedia example - // { - // "batchcomplete": true, - // "query": { - // "normalized": [ - // { - // "from": "u3fn92fb32f9yb329f32", - // "to": "U3fn92fb32f9yb329f32" - // } - // ], - // "pages": [ - // { - // "ns": 0, - // "title": "U3fn92fb32f9yb329f32", - // "missing": true, - // "contentmodel": "wikitext", - // "pagelanguage": "en", - // "pagelanguagehtmlcode": "en", - // "pagelanguagedir": "ltr", - // "fullurl": "https://en.wikipedia.org/wiki/U3fn92fb32f9yb329f32", - // "editurl": "https://en.wikipedia.org/w/index.php?title=U3fn92fb32f9yb329f32&action=edit", - // "canonicalurl": "https://en.wikipedia.org/wiki/U3fn92fb32f9yb329f32" - // } - // ] - // } - //} - #endregion - - public class WikipediaApiModel - { - public WikipediaQuery Query { get; set; } - } - - public class WikipediaQuery - { - public WikipediaPage[] Pages { get; set; } - } - - public class WikipediaPage - { - public bool Missing { get; set; } = false; - public string FullUrl { get; set; } - } - - public class WoWJoke - { - public string Question { get; set; } - public string Answer { get; set; } - public override string ToString() => $"`{Question}`\n\n**{Answer}**"; - } -} - -//{ -// "kind": "youtube#searchListResponse", -// "etag": "\"kiOs9cZLH2FUp6r6KJ8eyq_LIOk/hCJTmyH_v57mh_MvnUFSTHfjzBs\"", -// "nextPageToken": "CAEQAA", -// "regionCode": "RS", -// "pageInfo": { -// "totalResults": 4603, -// "resultsPerPage": 1 -// }, -// "items": [ -// { -// "kind": "youtube#searchResult", -// "etag": "\"kiOs9cZLH2FUp6r6KJ8eyq_LIOk/iD1S35mk0xOfwTB_8lpPZ9u-Vzc\"", -// "id": { -// "kind": "youtube#playlist", -// "playlistId": "PLs_KC2CCxJVMfOBnIyW5Kbu_GciNiYNAI" -// }, -// "snippet": { -// "publishedAt": "2016-04-14T11:35:29.000Z", -// "channelId": "UCMLwm18Qa20L2L-HGpgC3jQ", -// "title": "Popular Videos - Otorimonogatari & mousou express", -// "description": "", -// "thumbnails": { -// "default": { -// "url": "https://i.ytimg.com/vi/2FeptLky2mU/default.jpg", -// "width": 120, -// "height": 90 -// }, -// "medium": { -// "url": "https://i.ytimg.com/vi/2FeptLky2mU/mqdefault.jpg", -// "width": 320, -// "height": 180 -// }, -// "high": { -// "url": "https://i.ytimg.com/vi/2FeptLky2mU/hqdefault.jpg", -// "width": 480, -// "height": 360 -// } -// }, -// "channelTitle": "Otorimonogatari - Topic", -// "liveBroadcastContent": "none" -// } -// } -// ] -//} \ No newline at end of file diff --git a/src/NadekoBot/_Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/_Modules/CustomReactions/CustomReactions.cs deleted file mode 100644 index 920675b4..00000000 --- a/src/NadekoBot/_Modules/CustomReactions/CustomReactions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Discord.Commands; -using Discord.Modules; -using NadekoBot.Extensions; -using NadekoBot.Modules.Permissions.Classes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; - -namespace NadekoBot.Modules.CustomReactions -{ - internal class CustomReactionsModule : DiscordModule - { - public override string Prefix { get; } = ""; - - private Random rng = new Random(); - - private Dictionary> commandFuncs; - - public CustomReactionsModule() - { - commandFuncs = new Dictionary> - { - {new Regex(@"(?:%rng%|%rng:(\d{1,9})-(\d{1,9})%)"), (e,m) => { - int start, end; - if (m.Groups[1].Success) - { - start = int.Parse(m.Groups[1].Value); - end = int.Parse(m.Groups[2].Value); - return rng.Next(start, end).ToString(); - }else return rng.Next().ToString(); - } }, - {new Regex("%mention%"), (e,m) => NadekoBot.BotMention }, - {new Regex("%user%"), (e,m) => umsg.Author.Mention }, - {new Regex("%target%"), (e,m) => args?.Trim() ?? "" }, - - }; - } - - public override void Install(ModuleManager manager) - { - manager.CreateCommands("", cgb => - { - cgb.AddCheck(PermissionChecker.Instance); - - foreach (var command in NadekoBot.Config.CustomReactions) - { - var commandName = command.Key.Replace("%mention%", NadekoBot.BotMention); - - var c = cgb.CreateCommand(commandName); - if (commandName.Contains(NadekoBot.BotMention)) - c.Alias(commandName.Replace("<@", "<@!")); - c.Description($"Custom reaction. | `{command.Key}`") - .Parameter("args", ParameterType.Unparsed) - .Do(async e => - { - string str = command.Value[rng.Next(0, command.Value.Count())]; - commandFuncs.Keys.ForEach(key => str = key.Replace(str, m => commandFuncs[key](e, m))); - - - await channel.SendMessageAsync(str).ConfigureAwait(false); - }); - } - }); - } - } -} diff --git a/src/NadekoBot/_Modules/DiscordCommand.cs b/src/NadekoBot/_Modules/DiscordCommand.cs deleted file mode 100644 index 05f9a42a..00000000 --- a/src/NadekoBot/_Modules/DiscordCommand.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Discord.Commands; -using NadekoBot.Modules; - -namespace NadekoBot.Classes -{ - /// - /// Base DiscordCommand Class. - /// Inherit this class to create your own command. - /// - public abstract class DiscordCommand - { - - /// - /// Parent module - /// - protected DiscordModule Module { get; } - - /// - /// Parent module's prefix - /// - protected string Prefix => Module.Prefix; - - /// - /// Creates a new instance of discord command, - /// use ": base(module)" in the derived class' - /// constructor to make sure module is assigned - /// - /// Module this command resides in - protected DiscordCommand(DiscordModule module) - { - this.Module = module; - } - - /// - /// Initializes the CommandBuilder with values using CommandGroupBuilder - /// - internal abstract void Init(CommandGroupBuilder cgb); - } -} diff --git a/src/NadekoBot/_Modules/DiscordModule.cs b/src/NadekoBot/_Modules/DiscordModule.cs deleted file mode 100644 index 5e131233..00000000 --- a/src/NadekoBot/_Modules/DiscordModule.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Discord.Modules; -using System.Collections.Generic; -using NadekoBot.Classes; - -namespace NadekoBot.Modules { - public abstract class DiscordModule : IModule { - protected readonly HashSet commands = new HashSet(); - - public abstract string Prefix { get; } - - public abstract void Install(ModuleManager manager); - } -} diff --git a/src/NadekoBot/_Modules/Permissions/Classes/PermissionChecker.cs b/src/NadekoBot/_Modules/Permissions/Classes/PermissionChecker.cs deleted file mode 100644 index cf658aad..00000000 --- a/src/NadekoBot/_Modules/Permissions/Classes/PermissionChecker.cs +++ /dev/null @@ -1,175 +0,0 @@ -using Discord; -using Discord.Commands; -using Discord.Commands.Permissions; -using NadekoBot.Classes.JSONModels; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace NadekoBot.Modules.Permissions.Classes -{ - - internal class PermissionChecker : IPermissionChecker - { - public static PermissionChecker Instance { get; } = new PermissionChecker(); - - //key - sid:command - //value - userid - private ConcurrentDictionary commandCooldowns = new ConcurrentDictionary(); - private HashSet timeBlackList { get; } = new HashSet(); - - static PermissionChecker() { } - private PermissionChecker() - { - Task.Run(async () => - { - while (true) - { - //blacklist is cleared every 1.00 seconds. That is the most time anyone will be blocked - await Task.Delay(1000).ConfigureAwait(false); - timeBlackList.Clear(); - } - }); - } - - public bool CanRun(Command command, User user, Channel channel, out string error) - { - error = String.Empty; - - if (!NadekoBot.Ready) - return false; - - if (channel.IsPrivate || channel.Server == null) - return command.Category == "Help"; - - if (ConfigHandler.IsUserBlacklisted(user.Id) || - (!channel.IsPrivate && - (ConfigHandler.IsServerBlacklisted(channel.Server.Id) || ConfigHandler.IsChannelBlacklisted(channel.Id)))) - { - return false; - } - if (timeBlackList.Contains(user.Id)) - return false; - - if (!channel.IsPrivate && !channel.Server.CurrentUser.GetPermissions(channel).SendMessages) - { - return false; - } - - timeBlackList.Add(user.Id); - - ServerPermissions perms; - PermissionsHandler.PermissionsDict.TryGetValue(user.Server.Id, out perms); - - AddUserCooldown(user.Server.Id, user.Id, command.Text.ToLower()); - if (commandCooldowns.Keys.Contains(user.Server.Id + ":" + command.Text.ToLower())) - { - if (perms?.Verbose == true) - error = $"{user.Mention} You have a cooldown on that command."; - return false; - } - - try - { - //is it a permission command? - // if it is, check if the user has the correct role - // if yes return true, if no return false - if (command.Category == "Permissions") - { - Discord.Role role = null; - try - { - role = PermissionHelper.ValidateRole(user.Server, - PermissionsHandler.GetServerPermissionsRoleName(user.Server)); - } - catch { } - if (user.Server.Owner.Id == user.Id || (role != null && user.HasRole(role))) - return true; - throw new Exception($"You don't have the necessary role (**{(perms?.PermissionsControllerRole ?? "Nadeko")}**) to change permissions."); - } - - var permissionType = PermissionsHandler.GetPermissionBanType(command, user, channel); - - string msg; - - if (permissionType == PermissionsHandler.PermissionBanType.ServerBanModule && - command.Category.ToLower() == "nsfw") - msg = $"**{command.Category}** module has been banned from use on this **server**.\nNSFW module is disabled by default. Server owner can type `;sm nsfw enable` to enable it."; - else - switch (permissionType) - { - case PermissionsHandler.PermissionBanType.None: - return true; - case PermissionsHandler.PermissionBanType.ServerBanCommand: - msg = $"**{command.Text}** command has been banned from use on this **server**."; - break; - case PermissionsHandler.PermissionBanType.ServerBanModule: - msg = $"**{command.Category}** module has been banned from use on this **server**."; - break; - case PermissionsHandler.PermissionBanType.ChannelBanCommand: - msg = $"**{command.Text}** command has been banned from use on this **channel**."; - break; - case PermissionsHandler.PermissionBanType.ChannelBanModule: - msg = $"**{command.Category}** module has been banned from use on this **channel**."; - break; - case PermissionsHandler.PermissionBanType.RoleBanCommand: - msg = $"You do not have a **role** which permits you the usage of **{command.Text}** command."; - break; - case PermissionsHandler.PermissionBanType.RoleBanModule: - msg = $"You do not have a **role** which permits you the usage of **{command.Category}** module."; - break; - case PermissionsHandler.PermissionBanTypumsg.AuthorBanCommand: - msg = $"{user.Mention}, You have been banned from using **{command.Text}** command."; - break; - case PermissionsHandler.PermissionBanTypumsg.AuthorBanModule: - msg = $"{user.Mention}, You have been banned from using **{command.Category}** module."; - break; - default: - return true; - } - if (PermissionsHandler.PermissionsDict[user.Server.Id].Verbose) //if verbose - print errors - error = msg; - return false; - } - catch (Exception ex) - { - Console.WriteLine($"Exception in canrun: {ex}"); - try - { - if (perms != null && perms.Verbose) - //if verbose - print errors - error = ex.Message; - } - catch (Exception ex2) - { - Console.WriteLine($"SERIOUS PERMISSION ERROR {ex2}\n\nUser:{user} Server: {user?.Server?.Name}/{user?.Server?.Id}"); - } - return false; - } - } - - public void AddUserCooldown(ulong serverId, ulong userId, string commandName) - { - commandCooldowns.TryAdd(commandName, userId); - var tosave = serverId + ":" + commandName; - Task.Run(async () => - { - ServerPermissions perms; - PermissionsHandler.PermissionsDict.TryGetValue(serverId, out perms); - int cd; - if (!perms.CommandCooldowns.TryGetValue(commandName, out cd)) - { - return; - } - if (commandCooldowns.TryAdd(tosave, userId)) - { - await Task.Delay(cd * 1000); - ulong throwaway; - commandCooldowns.TryRemove(tosave, out throwaway); - } - - }); - } - } -} diff --git a/src/NadekoBot/_Modules/Permissions/Classes/PermissionHelper.cs b/src/NadekoBot/_Modules/Permissions/Classes/PermissionHelper.cs deleted file mode 100644 index c972953e..00000000 --- a/src/NadekoBot/_Modules/Permissions/Classes/PermissionHelper.cs +++ /dev/null @@ -1,101 +0,0 @@ -using Discord; -using Discord.Commands; -using Discord.Modules; -using System; -using System.Linq; - -namespace NadekoBot.Modules.Permissions.Classes -{ - internal static class PermissionHelper - { - public static bool ValidateBool(string passedArg) - { - if (string.IsNullOrWhiteSpace(passedArg)) - { - throw new ArgumentException("No value supplied! Missing argument"); - } - switch (passedArg.ToLower()) - { - case "1": - case "t": - case "true": - case "enable": - case "enabled": - case "allow": - case "unban": - return true; - case "0": - case "f": - case "false": - case "disable": - case "disabled": - case "disallow": - case "ban": - return false; - default: - throw new ArgumentException("Did not receive a valid boolean value"); - } - } - - internal static string ValidateModule(string mod) - { - if (string.IsNullOrWhiteSpace(mod)) - throw new ArgumentNullException(nameof(mod)); - - foreach (var m in NadekoBot.Client.GetService().Modules) - { - if (m.Name.ToLower().Equals(mod.Trim().ToLower())) - return m.Name; - } - throw new ArgumentException("That module does not exist."); - } - - internal static string ValidateCommand(string commandText) - { - if (string.IsNullOrWhiteSpace(commandText)) - throw new ArgumentNullException(nameof(commandText)); - - var normalizedCmdTxt = commandText.Trim().ToUpperInvariant(); - - foreach (var com in NadekoBot.Client.GetService().AllCommands) - { - if (com.Text.ToUpperInvariant().Equals(normalizedCmdTxt) || com.Aliases.Select(c=>c.ToUpperInvariant()).Contains(normalizedCmdTxt)) - return com.Text; - } - throw new NullReferenceException("That command does not exist."); - } - - internal static Role ValidateRole(Server server, string roleName) - { - if (string.IsNullOrWhiteSpace(roleName)) - throw new ArgumentNullException(nameof(roleName)); - - if (roleName.Trim() == "everyone") - roleName = "@everyone"; - var role = server.FindRoles(roleName.Trim()).FirstOrDefault(); - if (role == null) - throw new NullReferenceException("That role does not exist."); - return role; - } - - internal static Channel ValidateChannel(Server server, string channelName) - { - if (string.IsNullOrWhiteSpace(channelName)) - throw new ArgumentNullException(nameof(channelName)); - var channel = server.FindChannels(channelName.Trim(), ChannelType.Text).FirstOrDefault(); - if (channel == null) - throw new NullReferenceException("That channel does not exist."); - return channel; - } - - internal static User ValidateUser(Server server, string userName) - { - if (string.IsNullOrWhiteSpace(userName)) - throw new ArgumentNullException(nameof(userName)); - var user = server.FindUsers(userName.Trim()).FirstOrDefault(); - if (user == null) - throw new NullReferenceException("That user does not exist."); - return user; - } - } -} diff --git a/src/NadekoBot/_Modules/Permissions/Classes/PermissionsHandler.cs b/src/NadekoBot/_Modules/Permissions/Classes/PermissionsHandler.cs deleted file mode 100644 index 6839b276..00000000 --- a/src/NadekoBot/_Modules/Permissions/Classes/PermissionsHandler.cs +++ /dev/null @@ -1,575 +0,0 @@ -using Discord; -using Discord.Commands; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace NadekoBot.Modules.Permissions.Classes -{ - public static class PermissionsHandler - { - public static ConcurrentDictionary PermissionsDict = - new ConcurrentDictionary(); - - public enum PermissionBanType - { - None, ServerBanCommand, ServerBanModule, - ChannelBanCommand, ChannelBanModule, RoleBanCommand, - RoleBanModule, UserBanCommand, UserBanModule - } - - - public static void Initialize() - { - Console.WriteLine("Reading from the permission files."); - Directory.CreateDirectory("data/permissions"); - foreach (var file in Directory.EnumerateFiles("data/permissions/")) - { - try - { - var strippedFileName = Path.GetFileNameWithoutExtension(file); - if (string.IsNullOrWhiteSpace(strippedFileName)) continue; - var id = ulong.Parse(strippedFileName); - var data = Newtonsoft.Json.JsonConvert.DeserializeObject(File.ReadAllText(file)); - PermissionsDict.TryAdd(id, data); - } - catch { } - } - Console.WriteLine("Permission initialization complete."); - } - - internal static Permissions GetRolePermissionsById(Server server, ulong id) - { - ServerPermissions serverPerms; - if (!PermissionsDict.TryGetValue(server.Id, out serverPerms)) - return null; - - Permissions toReturn; - serverPerms.RolePermissions.TryGetValue(id, out toReturn); - return toReturn; - } - - internal static Permissions GetUserPermissionsById(Server server, ulong id) - { - ServerPermissions serverPerms; - if (!PermissionsDict.TryGetValue(server.Id, out serverPerms)) - return null; - - Permissions toReturn; - serverPerms.UserPermissions.TryGetValue(id, out toReturn); - return toReturn; - } - - internal static Permissions GetChannelPermissionsById(Server server, ulong id) - { - ServerPermissions serverPerms; - if (!PermissionsDict.TryGetValue(server.Id, out serverPerms)) - return null; - - Permissions toReturn; - serverPerms.ChannelPermissions.TryGetValue(id, out toReturn); - return toReturn; - } - - internal static Permissions GetServerPermissions(Server server) - { - ServerPermissions serverPerms; - return !PermissionsDict.TryGetValue(server.Id, out serverPerms) ? null : serverPerms.Permissions; - } - - internal static PermissionBanType GetPermissionBanType(Command command, User user, Channel channel) - { - var server = user.Server; - ServerPermissions serverPerms = PermissionsDict.GetOrAdd(server.Id, id => new ServerPermissions(id, server.Name)); - bool val; - Permissions perm; - //server - if (serverPerms.Permissions.Modules.TryGetValue(command.Category, out val) && val == false) - return PermissionBanType.ServerBanModule; - if (serverPerms.Permissions.Commands.TryGetValue(command.Text, out val) && val == false) - return PermissionBanType.ServerBanCommand; - //channel - if (serverPerms.ChannelPermissions.TryGetValue(channel.Id, out perm) && - perm.Modules.TryGetValue(command.Category, out val) && val == false) - return PermissionBanType.ChannelBanModule; - if (serverPerms.ChannelPermissions.TryGetValue(channel.Id, out perm) && - perm.Commands.TryGetValue(command.Text, out val) && val == false) - return PermissionBanType.ChannelBanCommand; - - //ROLE PART - TWO CASES - // FIRST CASE: - // IF EVERY ROLE USER HAS IS BANNED FROM THE MODULE, - // THAT MEANS USER CANNOT RUN THIS COMMAND - // IF AT LEAST ONE ROLE EXIST THAT IS NOT BANNED, - // USER CAN RUN THE COMMAND - var foundNotBannedRole = false; - foreach (var role in user.Roles) - { - //if every role is banned from using the module -> rolebanmodule - if (serverPerms.RolePermissions.TryGetValue(role.Id, out perm) && - perm.Modules.TryGetValue(command.Category, out val) && val == false) - continue; - foundNotBannedRole = true; - break; - } - if (!foundNotBannedRole) - return PermissionBanType.RoleBanModule; - - // SECOND CASE: - // IF EVERY ROLE USER HAS IS BANNED FROM THE COMMAND, - // THAT MEANS USER CANNOT RUN THAT COMMAND - // IF AT LEAST ONE ROLE EXISTS THAT IS NOT BANNED, - // USER CAN RUN THE COMMAND - foundNotBannedRole = false; - foreach (var role in user.Roles) - { - //if every role is banned from using the module -> rolebanmodule - if (serverPerms.RolePermissions.TryGetValue(role.Id, out perm) && - perm.Commands.TryGetValue(command.Text, out val) && val == false) - continue; - else - { - foundNotBannedRole = true; - break; - } - } - if (!foundNotBannedRole) - return PermissionBanType.RoleBanCommand; - - //user - if (serverPerms.UserPermissions.TryGetValue(user.Id, out perm) && - perm.Modules.TryGetValue(command.Category, out val) && val == false) - return PermissionBanTypumsg.AuthorBanModule; - if (serverPerms.UserPermissions.TryGetValue(user.Id, out perm) && - perm.Commands.TryGetValue(command.Text, out val) && val == false) - return PermissionBanTypumsg.AuthorBanCommand; - - return PermissionBanType.None; - } - - private static Task WriteServerToJson(ServerPermissions serverPerms) => Task.Run(() => - { - string pathToFile = $"data/permissions/{serverPerms.Id}.json"; - File.WriteAllText(pathToFile, - Newtonsoft.Json.JsonConvert.SerializeObject(serverPerms, Newtonsoft.Json.Formatting.Indented)); - }); - - public static Task WriteToJson() => Task.Run(() => - { - Directory.CreateDirectory("data/permissions/"); - foreach (var kvp in PermissionsDict) - { - WriteServerToJson(kvp.Value); - } - }); - - public static string GetServerPermissionsRoleName(Server server) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - return serverPerms.PermissionsControllerRole; - } - - internal static async Task SetPermissionsRole(Server server, string roleName) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - serverPerms.PermissionsControllerRole = roleName; - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - internal static async Task SetVerbosity(Server server, bool val) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - serverPerms.Verbose = val; - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - internal static async Task CopyRolePermissions(Role fromRole, Role toRole) - { - var server = fromRole.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - var from = GetRolePermissionsById(server, fromRole.Id); - if (from == null) - serverPerms.RolePermissions.Add(fromRole.Id, from = new Permissions(fromRole.Name)); - var to = GetRolePermissionsById(server, toRole.Id); - if (to == null) - serverPerms.RolePermissions.Add(toRole.Id, to = new Permissions(toRole.Name)); - - to.CopyFrom(from); - - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - internal static async Task CopyChannelPermissions(Channel fromChannel, Channel toChannel) - { - var server = fromChannel.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - var from = GetChannelPermissionsById(server, fromChannel.Id); - if (from == null) - serverPerms.ChannelPermissions.Add(fromChannel.Id, from = new Permissions(fromChannel.Name)); - var to = GetChannelPermissionsById(server, toChannel.Id); - if (to == null) - serverPerms.ChannelPermissions.Add(toChannel.Id, to = new Permissions(toChannel.Name)); - - to.CopyFrom(from); - - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - internal static async Task CopyUserPermissions(User fromUser, User toUser) - { - var server = fromUser.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - var from = GetUserPermissionsById(server, fromUser.Id); - if (from == null) - serverPerms.UserPermissions.Add(fromUser.Id, from = new Permissions(fromUser.Name)); - var to = GetUserPermissionsById(server, toUser.Id); - if (to == null) - serverPerms.UserPermissions.Add(toUser.Id, to = new Permissions(toUser.Name)); - - to.CopyFrom(from); - - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetServerModulePermission(Server server, string moduleName, bool value) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - var modules = serverPerms.Permissions.Modules; - if (modules.ContainsKey(moduleName)) - modules[moduleName] = value; - else - modules.TryAdd(moduleName, value); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetServerCommandPermission(Server server, string commandName, bool value) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - var commands = serverPerms.Permissions.Commands; - if (commands.ContainsKey(commandName)) - commands[commandName] = value; - else - commands.TryAdd(commandName, value); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetChannelModulePermission(Channel channel, string moduleName, bool value) - { - var server = channel.Server; - - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - if (!serverPerms.ChannelPermissions.ContainsKey(channel.Id)) - serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name)); - - var modules = serverPerms.ChannelPermissions[channel.Id].Modules; - - if (modules.ContainsKey(moduleName)) - modules[moduleName] = value; - else - modules.TryAdd(moduleName, value); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetChannelCommandPermission(Channel channel, string commandName, bool value) - { - var server = channel.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - if (!serverPerms.ChannelPermissions.ContainsKey(channel.Id)) - serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name)); - - var commands = serverPerms.ChannelPermissions[channel.Id].Commands; - - if (commands.ContainsKey(commandName)) - commands[commandName] = value; - else - commands.TryAdd(commandName, value); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetRoleModulePermission(Role role, string moduleName, bool value) - { - var server = role.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - if (!serverPerms.RolePermissions.ContainsKey(role.Id)) - serverPerms.RolePermissions.Add(role.Id, new Permissions(role.Name)); - - var modules = serverPerms.RolePermissions[role.Id].Modules; - - if (modules.ContainsKey(moduleName)) - modules[moduleName] = value; - else - modules.TryAdd(moduleName, value); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetRoleCommandPermission(Role role, string commandName, bool value) - { - var server = role.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - if (!serverPerms.RolePermissions.ContainsKey(role.Id)) - serverPerms.RolePermissions.Add(role.Id, new Permissions(role.Name)); - - var commands = serverPerms.RolePermissions[role.Id].Commands; - - if (commands.ContainsKey(commandName)) - commands[commandName] = value; - else - commands.TryAdd(commandName, value); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetUserModulePermission(User user, string moduleName, bool value) - { - var server = user.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - if (!serverPerms.UserPermissions.ContainsKey(user.Id)) - serverPerms.UserPermissions.Add(user.Id, new Permissions(user.Name)); - - var modules = serverPerms.UserPermissions[user.Id].Modules; - - if (modules.ContainsKey(moduleName)) - modules[moduleName] = value; - else - modules.TryAdd(moduleName, value); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetUserCommandPermission(User user, string commandName, bool value) - { - var server = user.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - if (!serverPerms.UserPermissions.ContainsKey(user.Id)) - serverPerms.UserPermissions.Add(user.Id, new Permissions(user.Name)); - - var commands = serverPerms.UserPermissions[user.Id].Commands; - - if (commands.ContainsKey(commandName)) - commands[commandName] = value; - else - commands.TryAdd(commandName, value); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetServerWordPermission(Server server, bool value) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - serverPerms.Permissions.FilterWords = value; - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetChannelWordPermission(Channel channel, bool value) - { - var server = channel.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - if (!serverPerms.ChannelPermissions.ContainsKey(channel.Id)) - serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name)); - - serverPerms.ChannelPermissions[channel.Id].FilterWords = value; - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetServerFilterInvitesPermission(Server server, bool value) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - serverPerms.Permissions.FilterInvites = value; - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetChannelFilterInvitesPermission(Channel channel, bool value) - { - var server = channel.Server; - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - - if (!serverPerms.ChannelPermissions.ContainsKey(channel.Id)) - serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name)); - - serverPerms.ChannelPermissions[channel.Id].FilterInvites = value; - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task SetCommandCooldown(Server server, string commandName, int value) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - if (value == 0) { - int throwaway; - serverPerms.CommandCooldowns.TryRemove(commandName, out throwaway); - } - else { - serverPerms.CommandCooldowns.AddOrUpdate(commandName, value, (str, v) => value); - } - - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - - public static async Task AddFilteredWord(Server server, string word) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - if (serverPerms.Words.Contains(word)) - throw new InvalidOperationException("That word is already banned."); - serverPerms.Words.Add(word); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - public static async Task RemoveFilteredWord(Server server, string word) - { - var serverPerms = PermissionsDict.GetOrAdd(server.Id, - new ServerPermissions(server.Id, server.Name)); - if (!serverPerms.Words.Contains(word)) - throw new InvalidOperationException("That word is not banned."); - serverPerms.Words.Remove(word); - await WriteServerToJson(serverPerms).ConfigureAwait(false); - } - } - /// - /// Holds a permission list - /// - public class Permissions - { - /// - /// Name of the parent object whose permissions these are - /// - public string Name { get; set; } - /// - /// Module name with allowed/disallowed - /// - public ConcurrentDictionary Modules { get; set; } - /// - /// Command name with allowed/disallowed - /// - public ConcurrentDictionary Commands { get; set; } - /// - /// Should the bot filter invites to other discord servers (and ref links in the future) - /// - public bool FilterInvites { get; set; } - /// - /// Should the bot filter words which are specified in the Words hashset - /// - public bool FilterWords { get; set; } - - public Permissions(string name) - { - Name = name; - Modules = new ConcurrentDictionary(); - Commands = new ConcurrentDictionary(); - FilterInvites = false; - FilterWords = false; - } - - public void CopyFrom(Permissions other) - { - Modules.Clear(); - foreach (var mp in other.Modules) - Modules.AddOrUpdate(mp.Key, mp.Value, (s, b) => mp.Value); - Commands.Clear(); - foreach (var cp in other.Commands) - Commands.AddOrUpdate(cp.Key, cp.Value, (s, b) => cp.Value); - FilterInvites = other.FilterInvites; - FilterWords = other.FilterWords; - } - - public override string ToString() - { - var toReturn = ""; - var bannedModules = Modules.Where(kvp => kvp.Value == false); - var bannedModulesArray = bannedModules as KeyValuePair[] ?? bannedModules.ToArray(); - if (bannedModulesArray.Any()) - { - toReturn += "`Banned Modules:`\n"; - toReturn = bannedModulesArray.Aggregate(toReturn, (current, m) => current + $"\t`[x] {m.Key}`\n"); - } - var bannedCommands = Commands.Where(kvp => kvp.Value == false); - var bannedCommandsArr = bannedCommands as KeyValuePair[] ?? bannedCommands.ToArray(); - if (bannedCommandsArr.Any()) - { - toReturn += "`Banned Commands:`\n"; - toReturn = bannedCommandsArr.Aggregate(toReturn, (current, c) => current + $"\t`[x] {c.Key}`\n"); - } - return toReturn; - } - } - - public class ServerPermissions - { - /// - /// The guy who can edit the permissions - /// - public string PermissionsControllerRole { get; set; } - /// - /// Does it print the error when a restriction occurs - /// - public bool Verbose { get; set; } - /// - /// The id of the thing (user/server/channel) - /// - public ulong Id { get; set; } //a string because of the role name. - /// - /// Permission object bound to the id of something/role name - /// - public Permissions Permissions { get; set; } - /// - /// Banned words, usually profanities, like word "java" - /// - public HashSet Words { get; set; } - - public Dictionary UserPermissions { get; set; } - public Dictionary ChannelPermissions { get; set; } - public Dictionary RolePermissions { get; set; } - /// - /// Dictionary of command names with their respective cooldowns - /// - public ConcurrentDictionary CommandCooldowns { get; set; } - - public ServerPermissions(ulong id, string name) - { - Id = id; - PermissionsControllerRole = "Nadeko"; - Verbose = true; - - Permissions = new Permissions(name); - Permissions.Modules.TryAdd("NSFW", false); - UserPermissions = new Dictionary(); - ChannelPermissions = new Dictionary(); - RolePermissions = new Dictionary(); - CommandCooldowns = new ConcurrentDictionary(); - Words = new HashSet(); - } - } -} \ No newline at end of file diff --git a/src/NadekoBot/_Modules/Permissions/Classes/SimpleCheckers.cs b/src/NadekoBot/_Modules/Permissions/Classes/SimpleCheckers.cs deleted file mode 100644 index 3ab0650d..00000000 --- a/src/NadekoBot/_Modules/Permissions/Classes/SimpleCheckers.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Discord; -using Discord.Commands; -using Discord.Commands.Permissions; -using System; - -namespace NadekoBot.Modules.Permissions.Classes -{ - public static class SimpleCheckers - { - public static ManageRoles CanManageRoles { get; } = new ManageRoles(); - - public static Func OwnerOnly() => - (com, user, ch) => NadekoBot.IsOwner(user.Id); - - public static Func ManageMessages() => - (com, user, ch) => user.ServerPermissions.ManageMessages; - - public static Func ManageChannels() => - (com, user, ch) => user.ServerPermissions.ManageChannels; - - public static Func ManageServer() => - (com, user, ch) => user.ServerPermissions.ManageServer; - - public class ManageRoles : IPermissionChecker - { - public bool CanRun(Command command, User user, Channel channel, out string error) - { - error = string.Empty; - if (user.ServerPermissions.ManageRoles) - return true; - error = "You do not have a permission to manage roles."; - return false; - } - } - } -} diff --git a/src/NadekoBot/_Modules/Permissions/Commands/FilterInvitesCommand.cs b/src/NadekoBot/_Modules/Permissions/Commands/FilterInvitesCommand.cs deleted file mode 100644 index 7b9b1dc1..00000000 --- a/src/NadekoBot/_Modules/Permissions/Commands/FilterInvitesCommand.cs +++ /dev/null @@ -1,117 +0,0 @@ -using Discord; -using Discord.Commands; -using NadekoBot.Classes; -using NadekoBot.Modules.Permissions.Classes; -using System; -using System.Text.RegularExpressions; - -namespace NadekoBot.Modules.Permissions -{ - internal class FilterInvitesCommand : DiscordCommand - { - private readonly Regex filterRegex = new Regex(@"(?:discord(?:\.gg|app\.com\/invite)\/(?([\w]{16}|(?:[\w]+-?){3})))"); - - - public FilterInvitesCommand(DiscordModule module) : base(module) - { - NadekoBot.Client.MessageReceived += async (sender, args) => - { - if (args.Channel.IsPrivate || args.User.Id == NadekoBot.Client.CurrentUser.Id) return; - try - { - Classes.ServerPermissions serverPerms; - if (!IsChannelOrServerFiltering(args.Channel, out serverPerms)) return; - - if (filterRegex.IsMatch(args.Message.RawText)) - { - await args.Message.Delete().ConfigureAwait(false); - IncidentsHandler.Add(args.Server.Id, args.Channel.Id, $"User [{args.User.Name}/{args.User.Id}] posted " + - $"INVITE LINK in [{args.Channel.Name}/{args.Channel.Id}] channel.\n" + - $"`Full message:` {args.Message.Text}"); - if (serverPerms.Verbose) - await args.Channel.SendMessageAsync($"{args.User.Mention} Invite links are not " + - $"allowed on this channel.") - .ConfigureAwait(false); - } - } - catch { } - }; - } - - private static bool IsChannelOrServerFiltering(Channel channel, out Classes.ServerPermissions serverPerms) - { - if (!PermissionsHandler.PermissionsDict.TryGetValue(channel.Server.Id, out serverPerms)) return false; - - if (serverPerms.Permissions.FilterInvites) - return true; - - Classes.Permissions perms; - return serverPerms.ChannelPermissions.TryGetValue(channel.Id, out perms) && perms.FilterInvites; - } - - internal override void Init(CommandGroupBuilder cgb) - { - cgb.CreateCommand(Module.Prefix + "chnlfilterinv") - .Alias(Module.Prefix + "cfi") - .Description("Enables or disables automatic deleting of invites on the channel." + - "If no channel supplied, it will default to current one. Use ALL to apply to all existing channels at once." + - $" | `{Prefix}cfi enable #general-chat`") - .Parameter("bool") - .Parameter("channel", ParameterType.Optional) - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - var chanStr = channel; - - if (chanStr?.ToLowerInvariant().Trim() != "all") - { - - var chan = string.IsNullOrWhiteSpace(chanStr) - ? e.Channel - : PermissionHelper.ValidateChannel(e.Server, chanStr); - await PermissionsHandler.SetChannelFilterInvitesPermission(chan, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for **{chan.Name}** channel.") - .ConfigureAwait(false); - return; - } - //all channels - - foreach (var curChannel in e.Server.TextChannels) - { - await PermissionsHandler.SetChannelFilterInvitesPermission(curChannel, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for **ALL** channels.") - .ConfigureAwait(false); - - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢 Error: {ex.Message}") - .ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Module.Prefix + "srvrfilterinv") - .Alias(Module.Prefix + "sfi") - .Description($"Enables or disables automatic deleting of invites on the server. | `{Prefix}sfi disable`") - .Parameter("bool") - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - await PermissionsHandler.SetServerFilterInvitesPermission(e.Server, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for this server.") - .ConfigureAwait(false); - - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢 Error: {ex.Message}").ConfigureAwait(false); - } - }); - } - } -} diff --git a/src/NadekoBot/_Modules/Permissions/Commands/FilterWordsCommand.cs b/src/NadekoBot/_Modules/Permissions/Commands/FilterWordsCommand.cs deleted file mode 100644 index 781faa93..00000000 --- a/src/NadekoBot/_Modules/Permissions/Commands/FilterWordsCommand.cs +++ /dev/null @@ -1,174 +0,0 @@ -using Discord; -using Discord.Commands; -using NadekoBot.Classes; -using NadekoBot.Modules.Permissions.Classes; -using System; -using System.Linq; - -namespace NadekoBot.Modules.Permissions -{ - internal class FilterWords : DiscordCommand - { - public FilterWords(DiscordModule module) : base(module) - { - NadekoBot.Client.MessageReceived += async (sender, args) => - { - if (args.Channel.IsPrivate || args.User.Id == NadekoBot.Client.CurrentUser.Id) return; - try - { - Classes.ServerPermissions serverPerms; - if (!IsChannelOrServerFiltering(args.Channel, out serverPerms)) return; - - var wordsInMessage = args.Message.RawText.ToLowerInvariant().Split(' '); - if (serverPerms.Words.Any(w => wordsInMessage.Contains(w))) - { - await args.Message.Delete().ConfigureAwait(false); - IncidentsHandler.Add(args.Server.Id, args.Channel.Id, $"User [{args.User.Name}/{args.User.Id}] posted " + - $"BANNED WORD in [{args.Channel.Name}/{args.Channel.Id}] channel.\n" + - $"`Full message:` {args.Message.Text}"); - if (serverPerms.Verbose) - await args.Channel.SendMessageAsync($"{args.User.Mention} One or more of the words you used " + - $"in that sentence are not allowed here.") - .ConfigureAwait(false); - } - } - catch { } - }; - } - - private static bool IsChannelOrServerFiltering(Channel channel, out Classes.ServerPermissions serverPerms) - { - if (!PermissionsHandler.PermissionsDict.TryGetValue(channel.Server.Id, out serverPerms)) return false; - - if (serverPerms.Permissions.FilterWords) - return true; - - Classes.Permissions perms; - return serverPerms.ChannelPermissions.TryGetValue(channel.Id, out perms) && perms.FilterWords; - } - - internal override void Init(CommandGroupBuilder cgb) - { - cgb.CreateCommand(Module.Prefix + "chnlfilterwords") - .Alias(Module.Prefix + "cfw") - .Description("Enables or disables automatic deleting of messages containing banned words on the channel." + - "If no channel supplied, it will default to current one. Use ALL to apply to all existing channels at once." + - $" | `{Prefix}cfw enable #general-chat`") - .Parameter("bool") - .Parameter("channel", ParameterType.Optional) - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - var chanStr = channel?.ToLowerInvariant().Trim(); - - if (chanStr != "all") - { - var chan = string.IsNullOrWhiteSpace(chanStr) - ? e.Channel - : PermissionHelper.ValidateChannel(e.Server, chanStr); - await PermissionsHandler.SetChannelWordPermission(chan, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Word filtering has been **{(state ? "enabled" : "disabled")}** for **{chan.Name}** channel.").ConfigureAwait(false); - return; - } - //all channels - - foreach (var curChannel in e.Server.TextChannels) - { - await PermissionsHandler.SetChannelWordPermission(curChannel, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"Word filtering has been **{(state ? "enabled" : "disabled")}** for **ALL** channels.").ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢 Error: {ex.Message}").ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Module.Prefix + "addfilterword") - .Alias(Module.Prefix + "afw") - .Description("Adds a new word to the list of filtered words" + - $" | `{Prefix}afw poop`") - .Parameter("word", ParameterType.Unparsed) - .Do(async e => - { - try - { - var word = word; - if (string.IsNullOrWhiteSpace(word)) - return; - await PermissionsHandler.AddFilteredWord(e.Server, word.ToLowerInvariant().Trim()).ConfigureAwait(false); - await channel.SendMessageAsync($"Successfully added new filtered word.").ConfigureAwait(false); - - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢 Error: {ex.Message}").ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Module.Prefix + "rmvfilterword") - .Alias(Module.Prefix + "rfw") - .Description("Removes the word from the list of filtered words" + - $" | `{Prefix}rw poop`") - .Parameter("word", ParameterType.Unparsed) - .Do(async e => - { - try - { - var word = word; - if (string.IsNullOrWhiteSpace(word)) - return; - await PermissionsHandler.RemoveFilteredWord(e.Server, word.ToLowerInvariant().Trim()).ConfigureAwait(false); - await channel.SendMessageAsync($"Successfully removed filtered word.").ConfigureAwait(false); - - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢 Error: {ex.Message}").ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Module.Prefix + "lstfilterwords") - .Alias(Module.Prefix + "lfw") - .Description("Shows a list of filtered words" + - $" | `{Prefix}lfw`") - .Do(async e => - { - try - { - Classes.ServerPermissions serverPerms; - if (!PermissionsHandler.PermissionsDict.TryGetValue(e.Server.Id, out serverPerms)) - return; - await channel.SendMessageAsync($"There are `{serverPerms.Words.Count}` filtered words.\n" + - string.Join("\n", serverPerms.Words)).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢 Error: {ex.Message}").ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Module.Prefix + "srvrfilterwords") - .Alias(Module.Prefix + "sfw") - .Description($"Enables or disables automatic deleting of messages containing forbidden words on the server. | `{Prefix}sfw disable`") - .Parameter("bool") - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - await PermissionsHandler.SetServerWordPermission(e.Server, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Word filtering has been **{(state ? "enabled" : "disabled")}** on this server.") - .ConfigureAwait(false); - - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢 Error: {ex.Message}").ConfigureAwait(false); - } - }); - } - } -} diff --git a/src/NadekoBot/_Modules/Permissions/PermissionsModule.cs b/src/NadekoBot/_Modules/Permissions/PermissionsModule.cs deleted file mode 100644 index 69d70eac..00000000 --- a/src/NadekoBot/_Modules/Permissions/PermissionsModule.cs +++ /dev/null @@ -1,834 +0,0 @@ -using Discord.Commands; -using Discord.Modules; -using NadekoBot.Classes; -using NadekoBot.Classes.JSONModels; -using NadekoBot.Extensions; -using NadekoBot.Modules.Games.Commands; -using NadekoBot.Modules.Permissions.Classes; -using NadekoBot.Modules.Permissions.Commands; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace NadekoBot.Modules.Permissions -{ - internal class PermissionModule : DiscordModule - { - public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Permissions; - - public PermissionModule() - { - commands.Add(new FilterInvitesCommand(this)); - commands.Add(new FilterWords(this)); - } - - public override void Install(ModuleManager manager) - { - manager.CreateCommands("", cgb => - { - - cgb.AddCheck(PermissionChecker.Instance); - - commands.ForEach(cmd => cmd.Init(cgb)); - - cgb.CreateCommand(Prefix + "permrole") - .Alias(Prefix + "pr") - .Description($"Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. | `{Prefix}pr role`") - .Parameter("role", ParameterType.Unparsed) - .Do(async e => - { - if (string.IsNullOrWhiteSpace(role)) - { - await channel.SendMessageAsync($"Current permissions role is `{PermissionsHandler.GetServerPermissionsRoleName(e.Server)}`").ConfigureAwait(false); - return; - } - - var arg = role; - Discord.Role role = null; - try - { - role = PermissionHelper.ValidateRole(e.Server, arg); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - await channel.SendMessageAsync($"Role `{arg}` probably doesn't exist. Create the role with that name first.").ConfigureAwait(false); - return; - } - await PermissionsHandler.SetPermissionsRole(e.Server, role.Name).ConfigureAwait(false); - await channel.SendMessageAsync($"Role `{role.Name}` is now required in order to change permissions.").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "rolepermscopy") - .Alias(Prefix + "rpc") - .Description($"Copies BOT PERMISSIONS (not discord permissions) from one role to another. |`{Prefix}rpc Some Role ~ Some other role`") - .Parameter("from_to", ParameterType.Unparsed) - .Do(async e => - { - var arg = from_to?.Trim(); - if (string.IsNullOrWhiteSpace(arg) || !arg.Contains('~')) - return; - var args = arg.Split('~').Select(a => a.Trim()).ToArray(); - if (args.Length > 2) - { - await channel.SendMessageAsync("💢Invalid number of '~'s in the argument.").ConfigureAwait(false); - return; - } - try - { - var fromRole = PermissionHelper.ValidateRole(e.Server, args[0]); - var toRole = PermissionHelper.ValidateRole(e.Server, args[1]); - - await PermissionsHandler.CopyRolePermissions(fromRole, toRole).ConfigureAwait(false); - await channel.SendMessageAsync($"Copied permission settings from **{fromRole.Name}** to **{toRole.Name}**.").ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢{ex.Message}").ConfigureAwait(false); - } - }); - cgb.CreateCommand(Prefix + "chnlpermscopy") - .Alias(Prefix + "cpc") - .Description($"Copies BOT PERMISSIONS (not discord permissions) from one channel to another. |`{Prefix}cpc Some Channel ~ Some other channel`") - .Parameter("from_to", ParameterType.Unparsed) - .Do(async e => - { - var arg = from_to?.Trim(); - if (string.IsNullOrWhiteSpace(arg) || !arg.Contains('~')) - return; - var args = arg.Split('~').Select(a => a.Trim()).ToArray(); - if (args.Length > 2) - { - await channel.SendMessageAsync("💢Invalid number of '~'s in the argument."); - return; - } - try - { - var fromChannel = PermissionHelper.ValidateChannel(e.Server, args[0]); - var toChannel = PermissionHelper.ValidateChannel(e.Server, args[1]); - - await PermissionsHandler.CopyChannelPermissions(fromChannel, toChannel).ConfigureAwait(false); - await channel.SendMessageAsync($"Copied permission settings from **{fromChannel.Name}** to **{toChannel.Name}**.").ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢{ex.Message}"); - } - }); - cgb.CreateCommand(Prefix + "usrpermscopy") - .Alias(Prefix + "upc") - .Description($"Copies BOT PERMISSIONS (not discord permissions) from one role to another. |`{Prefix}upc @SomeUser ~ @SomeOtherUser`") - .Parameter("from_to", ParameterType.Unparsed) - .Do(async e => - { - var arg = from_to?.Trim(); - if (string.IsNullOrWhiteSpace(arg) || !arg.Contains('~')) - return; - var args = arg.Split('~').Select(a => a.Trim()).ToArray(); - if (args.Length > 2) - { - await channel.SendMessageAsync("💢Invalid number of '~'s in the argument.").ConfigureAwait(false); - return; - } - try - { - var fromUser = PermissionHelper.ValidateUser(e.Server, args[0]); - var toUser = PermissionHelper.ValidateUser(e.Server, args[1]); - - await PermissionsHandler.CopyUserPermissions(fromUser, toUser).ConfigureAwait(false); - await channel.SendMessageAsync($"Copied permission settings from **{fromUser.ToString()}**to * *{toUser.ToString()}**.").ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync($"💢{ex.Message}"); - } - }); - - cgb.CreateCommand(Prefix + "verbose") - .Alias(Prefix + "v") - .Description($"Sets whether to show when a command/module is blocked. | `{Prefix}verbose true`") - .Parameter("arg", ParameterType.Required) - .Do(async e => - { - var arg = arg; - var val = PermissionHelper.ValidateBool(arg); - await PermissionsHandler.SetVerbosity(e.Server, val).ConfigureAwait(false); - await channel.SendMessageAsync($"Verbosity set to {val}.").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "srvrperms") - .Alias(Prefix + "sp") - .Description($"Shows banned permissions for this server. | `{Prefix}sp`") - .Do(async e => - { - var perms = PermissionsHandler.GetServerPermissions(e.Server); - if (string.IsNullOrWhiteSpace(perms?.ToString())) - await channel.SendMessageAsync("No permissions set for this server.").ConfigureAwait(false); - await channel.SendMessageAsync(perms.ToString()).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "roleperms") - .Alias(Prefix + "rp") - .Description($"Shows banned permissions for a certain role. No argument means for everyone. | `{Prefix}rp AwesomeRole`") - .Parameter("role", ParameterType.Unparsed) - .Do(async e => - { - var arg = role; - var role = e.Server.EveryoneRole; - if (!string.IsNullOrWhiteSpace(arg)) - try - { - role = PermissionHelper.ValidateRole(e.Server, arg); - } - catch (Exception ex) - { - await channel.SendMessageAsync("💢 Error: " + ex.Message).ConfigureAwait(false); - return; - } - - var perms = PermissionsHandler.GetRolePermissionsById(e.Server, role.Id); - - if (string.IsNullOrWhiteSpace(perms?.ToString())) - await channel.SendMessageAsync($"No permissions set for **{role.Name}** role.").ConfigureAwait(false); - await channel.SendMessageAsync(perms.ToString()).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "chnlperms") - .Alias(Prefix + "cp") - .Description($"Shows banned permissions for a certain channel. No argument means for this channel. | `{Prefix}cp #dev`") - .Parameter("channel", ParameterType.Unparsed) - .Do(async e => - { - var arg = channel; - var channel = e.Channel; - if (!string.IsNullOrWhiteSpace(arg)) - try - { - channel = PermissionHelper.ValidateChannel(e.Server, arg); - } - catch (Exception ex) - { - await channel.SendMessageAsync("💢 Error: " + ex.Message).ConfigureAwait(false); - return; - } - - var perms = PermissionsHandler.GetChannelPermissionsById(e.Server, channel.Id); - if (string.IsNullOrWhiteSpace(perms?.ToString())) - await channel.SendMessageAsync($"No permissions set for **{channel.Name}** channel.").ConfigureAwait(false); - await channel.SendMessageAsync(perms.ToString()).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "userperms") - .Alias(Prefix + "up") - .Description($"Shows banned permissions for a certain user. No argument means for yourself. | `{Prefix}up Kwoth`") - .Parameter("user", ParameterType.Unparsed) - .Do(async e => - { - var user = umsg.Author; - if (!string.IsNullOrWhiteSpace(user)) - try - { - user = PermissionHelper.ValidateUser(e.Server, user); - } - catch (Exception ex) - { - await channel.SendMessageAsync("💢 Error: " + ex.Message).ConfigureAwait(false); - return; - } - - var perms = PermissionsHandler.GetUserPermissionsById(e.Server, user.Id); - if (string.IsNullOrWhiteSpace(perms?.ToString())) - await channel.SendMessageAsync($"No permissions set for user **{user.Name}**.").ConfigureAwait(false); - await channel.SendMessageAsync(perms.ToString()).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "srvrmdl") - .Alias(Prefix + "sm") - .Parameter("module", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Description($"Sets a module's permission at the server level. | `{Prefix}sm \"module name\" enable`") - .Do(async e => - { - try - { - var module = PermissionHelper.ValidateModule(module); - var state = PermissionHelper.ValidateBool(bool); - - await PermissionsHandler.SetServerModulePermission(e.Server, module, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "srvrcmd").Alias(Prefix + "sc") - .Parameter("command", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Description($"Sets a command's permission at the server level. | `{Prefix}sc \"command name\" disable`") - .Do(async e => - { - try - { - var command = PermissionHelper.ValidateCommand(command); - var state = PermissionHelper.ValidateBool(bool); - - await PermissionsHandler.SetServerCommandPermission(e.Server, command, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "rolemdl").Alias(Prefix + "rm") - .Parameter("module", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Parameter("role", ParameterType.Unparsed) - .Description($"Sets a module's permission at the role level. | `{Prefix}rm \"module name\" enable MyRole`") - .Do(async e => - { - try - { - var module = PermissionHelper.ValidateModule(module); - var state = PermissionHelper.ValidateBool(bool); - - if (role?.ToLower() == "all") - { - foreach (var role in e.Server.Roles) - { - await PermissionsHandler.SetRoleModulePermission(role, module, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **ALL** roles.").ConfigureAwait(false); - } - else - { - var role = PermissionHelper.ValidateRole(e.Server, role); - - await PermissionsHandler.SetRoleModulePermission(role, module, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); - } - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "rolecmd").Alias(Prefix + "rc") - .Parameter("command", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Parameter("role", ParameterType.Unparsed) - .Description($"Sets a command's permission at the role level. | `{Prefix}rc \"command name\" disable MyRole`") - .Do(async e => - { - try - { - var command = PermissionHelper.ValidateCommand(command); - var state = PermissionHelper.ValidateBool(bool); - - if (role?.ToLower() == "all") - { - foreach (var role in e.Server.Roles) - { - await PermissionsHandler.SetRoleCommandPermission(role, command, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **ALL** roles.").ConfigureAwait(false); - } - else - { - var role = PermissionHelper.ValidateRole(e.Server, role); - - await PermissionsHandler.SetRoleCommandPermission(role, command, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); - } - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "chnlmdl").Alias(Prefix + "cm") - .Parameter("module", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Parameter("channel", ParameterType.Unparsed) - .Description($"Sets a module's permission at the channel level. | `{Prefix}cm \"module name\" enable SomeChannel`") - .Do(async e => - { - try - { - var module = PermissionHelper.ValidateModule(module); - var state = PermissionHelper.ValidateBool(bool); - var channelArg = channel; - if (channelArg?.ToLower() == "all") - { - foreach (var channel in e.Server.TextChannels) - { - await PermissionsHandler.SetChannelModulePermission(channel, module, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** on **ALL** channels.").ConfigureAwait(false); - } - else if (string.IsNullOrWhiteSpace(channelArg)) - { - await PermissionsHandler.SetChannelModulePermission(e.Channel, module, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{e.Channel.Name}** channel.").ConfigureAwait(false); - } - else - { - var channel = PermissionHelper.ValidateChannel(e.Server, channelArg); - - await PermissionsHandler.SetChannelModulePermission(channel, module, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); - } - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "chnlcmd").Alias(Prefix + "cc") - .Parameter("command", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Parameter("channel", ParameterType.Unparsed) - .Description($"Sets a command's permission at the channel level. | `{Prefix}cc \"command name\" enable SomeChannel`") - .Do(async e => - { - try - { - var command = PermissionHelper.ValidateCommand(command); - var state = PermissionHelper.ValidateBool(bool); - - if (channel?.ToLower() == "all") - { - foreach (var channel in e.Server.TextChannels) - { - await PermissionsHandler.SetChannelCommandPermission(channel, command, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** on **ALL** channels.").ConfigureAwait(false); - } - else - { - var channel = PermissionHelper.ValidateChannel(e.Server, channel); - - await PermissionsHandler.SetChannelCommandPermission(channel, command, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); - } - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "usrmdl").Alias(Prefix + "um") - .Parameter("module", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Parameter("user", ParameterType.Unparsed) - .Description($"Sets a module's permission at the user level. | `{Prefix}um \"module name\" enable SomeUsername`") - .Do(async e => - { - try - { - var module = PermissionHelper.ValidateModule(module); - var state = PermissionHelper.ValidateBool(bool); - var user = PermissionHelper.ValidateUser(e.Server, user); - - await PermissionsHandler.SetUserModulePermission(user, module, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for user **{user.Name}**.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "usrcmd").Alias(Prefix + "uc") - .Parameter("command", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Parameter("user", ParameterType.Unparsed) - .Description($"Sets a command's permission at the user level. | `{Prefix}uc \"command name\" enable SomeUsername`") - .Do(async e => - { - try - { - var command = PermissionHelper.ValidateCommand(command); - var state = PermissionHelper.ValidateBool(bool); - var user = PermissionHelper.ValidateUser(e.Server, user); - - await PermissionsHandler.SetUserCommandPermission(user, command, state).ConfigureAwait(false); - await channel.SendMessageAsync($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for user **{user.Name}**.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "allsrvrmdls").Alias(Prefix + "asm") - .Parameter("bool", ParameterType.Required) - .Description($"Sets permissions for all modules at the server level. | `{Prefix}asm [enable/disable]`") - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - - foreach (var module in NadekoBot.Client.GetService().Modules) - { - await PermissionsHandler.SetServerModulePermission(e.Server, module.Name, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"All modules have been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "allsrvrcmds").Alias(Prefix + "asc") - .Parameter("module", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Description($"Sets permissions for all commands from a certain module at the server level. | `{Prefix}asc \"module name\" [enable/disable]`") - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - var module = PermissionHelper.ValidateModule(module); - - foreach (var command in NadekoBot.Client.GetService().AllCommands.Where(c => c.Category == module)) - { - await PermissionsHandler.SetServerCommandPermission(e.Server, command.Text, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "allchnlmdls").Alias(Prefix + "acm") - .Parameter("bool", ParameterType.Required) - .Parameter("channel", ParameterType.Unparsed) - .Description($"Sets permissions for all modules at the channel level. | `{Prefix}acm [enable/disable] SomeChannel`") - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - var chArg = channel; - var channel = string.IsNullOrWhiteSpace(chArg) ? e.Channel : PermissionHelper.ValidateChannel(e.Server, chArg); - foreach (var module in NadekoBot.Client.GetService().Modules) - { - await PermissionsHandler.SetChannelModulePermission(channel, module.Name, state).ConfigureAwait(false); - } - - await channel.SendMessageAsync($"All modules have been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "allchnlcmds").Alias(Prefix + "acc") - .Parameter("module", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Parameter("channel", ParameterType.Unparsed) - .Description($"Sets permissions for all commands from a certain module at the channel level. | `{Prefix}acc \"module name\" [enable/disable] SomeChannel`") - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - var module = PermissionHelper.ValidateModule(module); - var channel = PermissionHelper.ValidateChannel(e.Server, channel); - foreach (var command in NadekoBot.Client.GetService().AllCommands.Where(c => c.Category == module)) - { - await PermissionsHandler.SetChannelCommandPermission(channel, command.Text, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "allrolemdls").Alias(Prefix + "arm") - .Parameter("bool", ParameterType.Required) - .Parameter("role", ParameterType.Unparsed) - .Description($"Sets permissions for all modules at the role level. | `{Prefix}arm [enable/disable] MyRole`") - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - var role = PermissionHelper.ValidateRole(e.Server, role); - foreach (var module in NadekoBot.Client.GetService().Modules) - { - await PermissionsHandler.SetRoleModulePermission(role, module.Name, state).ConfigureAwait(false); - } - - await channel.SendMessageAsync($"All modules have been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "allrolecmds").Alias(Prefix + "arc") - .Parameter("module", ParameterType.Required) - .Parameter("bool", ParameterType.Required) - .Parameter("role", ParameterType.Unparsed) - .Description($"Sets permissions for all commands from a certain module at the role level. | `{Prefix}arc \"module name\" [enable/disable] MyRole`") - .Do(async e => - { - try - { - var state = PermissionHelper.ValidateBool(bool); - var module = PermissionHelper.ValidateModule(module); - if (role?.ToLower() == "all") - { - foreach (var role in e.Server.Roles) - { - foreach (var command in NadekoBot.Client.GetService().AllCommands.Where(c => c.Category == module)) - { - await PermissionsHandler.SetRoleCommandPermission(role, command.Text, state).ConfigureAwait(false); - } - } - await channel.SendMessageAsync($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **all roles** role.").ConfigureAwait(false); - } - else - { - var role = PermissionHelper.ValidateRole(e.Server, role); - - foreach (var command in NadekoBot.Client.GetService().AllCommands.Where(c => c.Category == module)) - { - await PermissionsHandler.SetRoleCommandPermission(role, command.Text, state).ConfigureAwait(false); - } - await channel.SendMessageAsync($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); - } - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "ubl") - .Description($"Blacklists a mentioned user. | `{Prefix}ubl [user_mention]`") - .Parameter("user", ParameterType.Unparsed) - .AddCheck(SimpleCheckers.OwnerOnly()) - .Do(async e => - { - await Task.Run(async () => - { - if (!e.Message.MentionedUsers.Any()) return; - var usr = e.Message.MentionedUsers.First(); - NadekoBot.Config.UserBlacklist.Add(usr.Id); - await ConfigHandler.SaveConfig().ConfigureAwait(false); - await channel.SendMessageAsync($"`Sucessfully blacklisted user {usr.Name}`").ConfigureAwait(false); - }).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "uubl") - .Description($"Unblacklists a mentioned user. | `{Prefix}uubl [user_mention]`") - .Parameter("user", ParameterType.Unparsed) - .AddCheck(SimpleCheckers.OwnerOnly()) - .Do(async e => - { - await Task.Run(async () => - { - if (!e.Message.MentionedUsers.Any()) return; - var usr = e.Message.MentionedUsers.First(); - if (NadekoBot.Config.UserBlacklist.Contains(usr.Id)) - { - NadekoBot.Config.UserBlacklist.Remove(usr.Id); - await ConfigHandler.SaveConfig().ConfigureAwait(false); - await channel.SendMessageAsync($"`Sucessfully unblacklisted user {usr.Name}`").ConfigureAwait(false); - } - else - { - await channel.SendMessageAsync($"`{usr.Name} was not in blacklist`").ConfigureAwait(false); - } - }).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "cbl") - .Description($"Blacklists a mentioned channel (#general for example). | `{Prefix}cbl #some_channel`") - .Parameter("channel", ParameterType.Unparsed) - .Do(async e => - { - await Task.Run(async () => - { - if (!e.Message.MentionedChannels.Any()) return; - var ch = e.Message.MentionedChannels.First(); - NadekoBot.Config.UserBlacklist.Add(ch.Id); - await ConfigHandler.SaveConfig().ConfigureAwait(false); - await channel.SendMessageAsync($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false); - }).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "cubl") - .Description($"Unblacklists a mentioned channel (#general for example). | `{Prefix}cubl #some_channel`") - .Parameter("channel", ParameterType.Unparsed) - .Do(async e => - { - await Task.Run(async () => - { - if (!e.Message.MentionedChannels.Any()) return; - var ch = e.Message.MentionedChannels.First(); - NadekoBot.Config.UserBlacklist.Remove(ch.Id); - await ConfigHandler.SaveConfig().ConfigureAwait(false); - await channel.SendMessageAsync($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false); - }).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "sbl") - .Description($"Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY** | `{Prefix}sbl [servername/serverid]`") - .Parameter("server", ParameterType.Unparsed) - .AddCheck(SimpleCheckers.OwnerOnly()) - .Do(async e => - { - await Task.Run(async () => - { - var arg = server?.Trim(); - if (string.IsNullOrWhiteSpace(arg)) - return; - var server = NadekoBot.Client.Servers.FirstOrDefault(s => s.Id.ToString() == arg) ?? - NadekoBot.Client.FindServers(arg.Trim()).FirstOrDefault(); - if (server == null) - { - await channel.SendMessageAsync("Cannot find that server").ConfigureAwait(false); - return; - } - var serverId = server.Id; - NadekoBot.Config.ServerBlacklist.Add(serverId); - await ConfigHandler.SaveConfig().ConfigureAwait(false); - //cleanup trivias and typeracing - Modules.Games.Commands.Trivia.TriviaGame trivia; - TriviaCommands.RunningTrivias.TryRemove(serverId, out trivia); - TypingGame typeracer; - SpeedTyping.RunningContests.TryRemove(serverId, out typeracer); - - await channel.SendMessageAsync($"`Sucessfully blacklisted server {server.Name}`").ConfigureAwait(false); - }).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "cmdcooldown") - .Alias(Prefix+ "cmdcd") - .Description($"Sets a cooldown per user for a command. Set 0 to clear. | `{Prefix}cmdcd \"some cmd\" 5`") - .Parameter("command", ParameterType.Required) - .Parameter("secs",ParameterType.Required) - .AddCheck(SimpleCheckers.ManageMessages()) - .Do(async e => - { - try - { - var command = PermissionHelper.ValidateCommand(command); - var secsStr = secs.Trim(); - int secs; - if (!int.TryParse(secsStr, out secs) || secs < 0 || secs > 3600) - throw new ArgumentOutOfRangeException("secs", "Invalid second parameter. (Must be a number between 0 and 3600)"); - - - await PermissionsHandler.SetCommandCooldown(e.Server, command, secs).ConfigureAwait(false); - if(secs == 0) - await channel.SendMessageAsync($"Command **{command}** has no coooldown now.").ConfigureAwait(false); - else - await channel.SendMessageAsync($"Command **{command}** now has a **{secs} {(secs==1 ? "second" : "seconds")}** cooldown.").ConfigureAwait(false); - } - catch (ArgumentException exArg) - { - await channel.SendMessageAsync(exArg.Message).ConfigureAwait(false); - } - catch (Exception ex) - { - await channel.SendMessageAsync("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "allcmdcooldowns") - .Alias(Prefix + "acmdcds") - .Description("Shows a list of all commands and their respective cooldowns. | `{Prefix}acmdcds`") - .Do(async e => - { - ServerPermissions perms; - PermissionsHandler.PermissionsDict.TryGetValue(e.Server.Id, out perms); - if (perms == null) - return; - - if (!perms.CommandCooldowns.Any()) - { - await channel.SendMessageAsync("`No command cooldowns set.`").ConfigureAwait(false); - return; - } - await channel.SendMessageAsync(SearchHelper.ShowInPrettyCode(perms.CommandCooldowns.Select(c=>c.Key+ ": "+c.Value+" secs"),s=>$"{s,-30}",2)).ConfigureAwait(false); - }); - }); - } - } -} diff --git a/src/NadekoBot/units.json b/src/NadekoBot/units.json new file mode 100644 index 00000000..f7fe8cbe --- /dev/null +++ b/src/NadekoBot/units.json @@ -0,0 +1,981 @@ +[ + { + "Triggers": [ + "millimeter", + "millimeters", + "millimeter", + "mm" + ], + "UnitType": "length", + "Modifier": 0.001 + }, + { + "Triggers": [ + "centimeter", + "centimeters", + "centimeter", + "cm" + ], + "UnitType": "length", + "Modifier": 0.01 + }, + { + "Triggers": [ + "decimeter", + "decimeters", + "decimeter", + "dm" + ], + "UnitType": "length", + "Modifier": 0.1 + }, + { + "Triggers": [ + "meter", + "meters", + "meter", + "m" + ], + "UnitType": "length", + "Modifier": 1.0 + }, + { + "Triggers": [ + "kilometer", + "kilometers", + "kilometer", + "km" + ], + "UnitType": "length", + "Modifier": 1000.0 + }, + { + "Triggers": [ + "foot", + "feet", + "foot", + "ft" + ], + "UnitType": "length", + "Modifier": 0.3048 + }, + { + "Triggers": [ + "inch", + "inches", + "inch", + "in" + ], + "UnitType": "length", + "Modifier": 0.0254 + }, + { + "Triggers": [ + "mile", + "miles", + "mile", + "mi" + ], + "UnitType": "length", + "Modifier": 1609.344 + }, + { + "Triggers": [ + "yard", + "yards", + "yard", + "yd" + ], + "UnitType": "length", + "Modifier": 0.9144 + }, + { + "Triggers": [ + "cubic foot", + "cubic feet", + "cubic foot", + "ft3" + ], + "UnitType": "volume", + "Modifier": 0.02831685 + }, + { + "Triggers": [ + "cubic inch", + "cubic inches", + "cubic inch", + "in3" + ], + "UnitType": "volume", + "Modifier": 0.00001638706 + }, + { + "Triggers": [ + "cubic mile", + "cubic miles", + "cubic mile", + "mi3" + ], + "UnitType": "volume", + "Modifier": 4168182000.0 + }, + { + "Triggers": [ + "cubic yard", + "cubic yards", + "cubic yard", + "yd3" + ], + "UnitType": "volume", + "Modifier": 0.7645549 + }, + { + "Triggers": [ + "cup", + "cups", + "cup", + "cup" + ], + "UnitType": "volume", + "Modifier": 0.0002365882 + }, + { + "Triggers": [ + "imperial gallon", + "Imperial gallons", + "Imperial gallon", + "gal" + ], + "UnitType": "volume", + "Modifier": 0.00454609 + }, + { + "Triggers": [ + "us gallon", + "US gallons", + "US gallon", + "gal" + ], + "UnitType": "volume", + "Modifier": 0.003785412 + }, + { + "Triggers": [ + "milliliter", + "milliliters", + "milliliter", + "mL" + ], + "UnitType": "volume", + "Modifier": 0.000001 + }, + { + "Triggers": [ + "liter", + "liters", + "liter", + "L" + ], + "UnitType": "volume", + "Modifier": 0.001 + }, + { + "Triggers": [ + "imperial fluid ounce", + "Imperial fluid ounces", + "Imperial fluid ounce", + "fl oz" + ], + "UnitType": "volume", + "Modifier": 0.00002841306 + }, + { + "Triggers": [ + "us fluid ounce", + "US fluid ounces", + "US fluid ounce", + "fl oz" + ], + "UnitType": "volume", + "Modifier": 0.00002957353 + }, + { + "Triggers": [ + "imperial pint", + "Imperial pints", + "Imperial pint", + "pt" + ], + "UnitType": "volume", + "Modifier": 0.00056826125 + }, + { + "Triggers": [ + "us liquid pint", + "US pints (liquid)", + "US pint (liquid)", + "pt" + ], + "UnitType": "volume", + "Modifier": 0.0004731765 + }, + { + "Triggers": [ + "us dry pint", + "US pints (dry)", + "US pint (dry)", + "pt" + ], + "UnitType": "volume", + "Modifier": 0.0005506105 + }, + { + "Triggers": [ + "imperial quart", + "Imperial quarts", + "Imperial quart", + "qt" + ], + "UnitType": "volume", + "Modifier": 0.00113652297 + }, + { + "Triggers": [ + "us liquid quart", + "US quarts (liquid)", + "US quart (liquid)", + "qt" + ], + "UnitType": "volume", + "Modifier": 0.0009463529 + }, + { + "Triggers": [ + "us dry quart", + "US quarts (dry)", + "US quart (dry)", + "qt" + ], + "UnitType": "volume", + "Modifier": 0.001101221 + }, + { + "Triggers": [ + "tablespoon", + "tablespoons", + "tablespoon", + "tbsp" + ], + "UnitType": "volume", + "Modifier": 0.00001478676 + }, + { + "Triggers": [ + "teaspoon", + "teaspoons", + "teaspoon", + "tspn" + ], + "UnitType": "volume", + "Modifier": 0.000004928922 + }, + { + "Triggers": [ + "milligram", + "milligrams", + "milligram", + "mg" + ], + "UnitType": "weight", + "Modifier": 0.000001 + }, + { + "Triggers": [ + "gram", + "grams", + "gram", + "g" + ], + "UnitType": "weight", + "Modifier": 0.001 + }, + { + "Triggers": [ + "kilogram", + "kilograms", + "kilogram", + "kg" + ], + "UnitType": "weight", + "Modifier": 1.0 + }, + { + "Triggers": [ + "carat", + "carats", + "carat", + "CD" + ], + "UnitType": "weight", + "Modifier": 0.00020 + }, + { + "Triggers": [ + "grain", + "grains", + "grain", + "gr" + ], + "UnitType": "weight", + "Modifier": 0.00006479891 + }, + { + "Triggers": [ + "ounce", + "ounces", + "ounce", + "oz" + ], + "UnitType": "weight", + "Modifier": 0.02834952 + }, + { + "Triggers": [ + "pennyweight", + "pennyweights", + "pennyweight", + "dwt" + ], + "UnitType": "weight", + "Modifier": 0.001555174 + }, + { + "Triggers": [ + "pound", + "pounds", + "pound", + "lb" + ], + "UnitType": "weight", + "Modifier": 0.4535924 + }, + { + "Triggers": [ + "stone", + "stones", + "stone", + "st" + ], + "UnitType": "weight", + "Modifier": 6.35029318 + }, + { + "Triggers": [ + "slug", + "slugs", + "slug", + "slug" + ], + "UnitType": "weight", + "Modifier": 14.59390 + }, + { + "Triggers": [ + "metric ton", + "metric tons", + "metric ton", + "t" + ], + "UnitType": "weight", + "Modifier": 1000.0 + }, + { + "Triggers": [ + "long ton", + "long tons", + "long ton", + "t" + ], + "UnitType": "weight", + "Modifier": 1016.047 + }, + { + "Triggers": [ + "short ton", + "short tons", + "short ton", + "t" + ], + "UnitType": "weight", + "Modifier": 907.1847 + }, + { + "Triggers": [ + "acre", + "acres", + "acre", + "acre" + ], + "UnitType": "area", + "Modifier": 4046.873 + }, + { + "Triggers": [ + "are", + "ares", + "are", + "a" + ], + "UnitType": "area", + "Modifier": 100.0 + }, + { + "Triggers": [ + "hectare", + "hectares", + "hectare", + "ha" + ], + "UnitType": "area", + "Modifier": 10000.0 + }, + { + "Triggers": [ + "square foot", + "square feet", + "square foot", + "ft2" + ], + "UnitType": "area", + "Modifier": 0.09290304 + }, + { + "Triggers": [ + "square meter", + "square meters", + "square meter", + "m2" + ], + "UnitType": "area", + "Modifier": 1.0 + }, + { + "Triggers": [ + "square kilometer", + "square kilometers", + "square kilometer", + "km2" + ], + "UnitType": "area", + "Modifier": 1000000.0 + }, + { + "Triggers": [ + "square inch", + "square inches", + "square inch", + "in2" + ], + "UnitType": "area", + "Modifier": 0.00064516 + }, + { + "Triggers": [ + "square yard", + "square yards", + "square yard", + "yd2" + ], + "UnitType": "area", + "Modifier": 0.8361274 + }, + { + "Triggers": [ + "square mile", + "square miles", + "square mile", + "mi2" + ], + "UnitType": "area", + "Modifier": 2589988.0 + }, + { + "Triggers": [ + "aankadam", + "aankadam", + "aankadam", + "aankadam" + ], + "UnitType": "area", + "Modifier": 6.69 + }, + { + "Triggers": [ + "perch", + "perches", + "perch", + "perch" + ], + "UnitType": "area", + "Modifier": 25.29 + }, + { + "Triggers": [ + "cent", + "cents", + "cent", + "cent" + ], + "UnitType": "area", + "Modifier": 40.47 + }, + { + "Triggers": [ + "chatak", + "chataks", + "chatak", + "chatak" + ], + "UnitType": "area", + "Modifier": 41.81 + }, + { + "Triggers": [ + "kottah", + "kottah (B)", + "kottah (B)", + "kottah (B)" + ], + "UnitType": "area", + "Modifier": 66.89 + }, + { + "Triggers": [ + "guntha", + "guntha", + "guntha", + "guntha" + ], + "UnitType": "area", + "Modifier": 101.17 + }, + { + "Triggers": [ + "ground", + "grounds", + "ground", + "ground" + ], + "UnitType": "area", + "Modifier": 222.97 + }, + { + "Triggers": [ + "marla", + "marla", + "marla", + "marla" + ], + "UnitType": "area", + "Modifier": 501.68 + }, + { + "Triggers": [ + "rood", + "roods", + "rood", + "rood" + ], + "UnitType": "area", + "Modifier": 1011.71 + }, + { + "Triggers": [ + "bigha I", + "bigha I", + "bigha I", + "bigha I" + ], + "UnitType": "area", + "Modifier": 1618.74 + }, + { + "Triggers": [ + "bigha II", + "bigha II", + "bigha II", + "bigha II" + ], + "UnitType": "area", + "Modifier": 2529.29 + }, + { + "Triggers": [ + "kanal", + "kanal", + "kanal", + "kanal" + ], + "UnitType": "area", + "Modifier": 10033.53 + }, + { + "Triggers": [ + "biswa I", + "biswa I", + "biswa I", + "biswa I" + ], + "UnitType": "area", + "Modifier": 32374.85 + }, + { + "Triggers": [ + "biswa II", + "biswa II", + "biswa II", + "biswa II" + ], + "UnitType": "area", + "Modifier": 50585.71 + }, + { + "Triggers": [ + "pascal", + "pascal", + "pascal", + "Pa" + ], + "UnitType": "pressure", + "Modifier": 1.0 + }, + { + "Triggers": [ + "torr", + "torr", + "torr", + "Torr" + ], + "UnitType": "pressure", + "Modifier": 133.3224 + }, + { + "Triggers": [ + "bar", + "bars", + "bar", + "bar" + ], + "UnitType": "pressure", + "Modifier": 100000.0 + }, + { + "Triggers": [ + "millibar", + "millibars", + "millibar", + "mb" + ], + "UnitType": "pressure", + "Modifier": 100.0 + }, + { + "Triggers": [ + "psi", + "psi", + "psi", + "lbf/in2" + ], + "UnitType": "pressure", + "Modifier": 6894.757 + }, + { + "Triggers": [ + "day", + "days", + "day", + "d" + ], + "UnitType": "time", + "Modifier": 86400.0 + }, + { + "Triggers": [ + "hour", + "hours", + "hour", + "h" + ], + "UnitType": "time", + "Modifier": 3600.0 + }, + { + "Triggers": [ + "minute", + "minutes", + "minute", + "min" + ], + "UnitType": "time", + "Modifier": 60.0 + }, + { + "Triggers": [ + "year", + "years", + "year", + "yr" + ], + "UnitType": "time", + "Modifier": 31536000.0 + }, + { + "Triggers": [ + "AUD" + ], + "UnitType": "currency", + "Modifier": 1.4787 + }, + { + "Triggers": [ + "BGN" + ], + "UnitType": "currency", + "Modifier": 1.9558 + }, + { + "Triggers": [ + "BRL" + ], + "UnitType": "currency", + "Modifier": 3.5991 + }, + { + "Triggers": [ + "CAD" + ], + "UnitType": "currency", + "Modifier": 1.4562 + }, + { + "Triggers": [ + "CHF" + ], + "UnitType": "currency", + "Modifier": 1.0951 + }, + { + "Triggers": [ + "CNY" + ], + "UnitType": "currency", + "Modifier": 7.4565 + }, + { + "Triggers": [ + "CZK" + ], + "UnitType": "currency", + "Modifier": 27.025 + }, + { + "Triggers": [ + "DKK" + ], + "UnitType": "currency", + "Modifier": 7.4448 + }, + { + "Triggers": [ + "GBP" + ], + "UnitType": "currency", + "Modifier": 0.8517 + }, + { + "Triggers": [ + "HKD" + ], + "UnitType": "currency", + "Modifier": 8.6631 + }, + { + "Triggers": [ + "HRK" + ], + "UnitType": "currency", + "Modifier": 7.4846 + }, + { + "Triggers": [ + "HUF" + ], + "UnitType": "currency", + "Modifier": 308.97 + }, + { + "Triggers": [ + "IDR" + ], + "UnitType": "currency", + "Modifier": 14814.35 + }, + { + "Triggers": [ + "ILS" + ], + "UnitType": "currency", + "Modifier": 4.2241 + }, + { + "Triggers": [ + "INR" + ], + "UnitType": "currency", + "Modifier": 74.8703 + }, + { + "Triggers": [ + "JPY" + ], + "UnitType": "currency", + "Modifier": 114.27 + }, + { + "Triggers": [ + "KRW" + ], + "UnitType": "currency", + "Modifier": 1244.47 + }, + { + "Triggers": [ + "MXN" + ], + "UnitType": "currency", + "Modifier": 20.7542 + }, + { + "Triggers": [ + "MYR" + ], + "UnitType": "currency", + "Modifier": 4.5205 + }, + { + "Triggers": [ + "NOK" + ], + "UnitType": "currency", + "Modifier": 9.2873 + }, + { + "Triggers": [ + "NZD" + ], + "UnitType": "currency", + "Modifier": 1.5427 + }, + { + "Triggers": [ + "PHP" + ], + "UnitType": "currency", + "Modifier": 51.797 + }, + { + "Triggers": [ + "PLN" + ], + "UnitType": "currency", + "Modifier": 4.3436 + }, + { + "Triggers": [ + "RON" + ], + "UnitType": "currency", + "Modifier": 4.4505 + }, + { + "Triggers": [ + "RUB" + ], + "UnitType": "currency", + "Modifier": 72.4564 + }, + { + "Triggers": [ + "SEK" + ], + "UnitType": "currency", + "Modifier": 9.5008 + }, + { + "Triggers": [ + "SGD" + ], + "UnitType": "currency", + "Modifier": 1.5196 + }, + { + "Triggers": [ + "THB" + ], + "UnitType": "currency", + "Modifier": 38.608 + }, + { + "Triggers": [ + "TRY" + ], + "UnitType": "currency", + "Modifier": 3.2977 + }, + { + "Triggers": [ + "USD" + ], + "UnitType": "currency", + "Modifier": 1.1168 + }, + { + "Triggers": [ + "ZAR" + ], + "UnitType": "currency", + "Modifier": 16.0537 + }, + { + "Triggers": [ + "K", + "kelvin" + ], + "UnitType": "temperature", + "Modifier": 0.00 + }, + { + "Triggers": [ + "F", + "fahrenheit" + ], + "UnitType": "temperature", + "Modifier": 0.00 + }, + { + "Triggers": [ + "C", + "Celcius", + "Centigrade" + ], + "UnitType": "temperature", + "Modifier": 0.00 + }, + { + "Triggers": [ + "EUR" + ], + "UnitType": "currency", + "Modifier": 1.0 + } +] \ No newline at end of file