diff --git a/.gitignore b/.gitignore index da881a0e..ff2c4dd5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ obj/ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs -**/Bin/Debug/** **/Bin/Release/ **/Bin/PRIVATE/ !**/Bin/Debug/opus.dll @@ -19,9 +18,11 @@ obj/ !**/Bin/Debug/Nito.AsyncEx.dll !**/Bin/Debug/WebSocket4Net.dll !**/Bin/Debug/sqlite3.dll -!**/Bin/Debug/data/* -!**/Bin/Debug/data/ !**/Bin/Debug/credentials_example.json +NadekoBot/bin/debug/*.* +NadekoBot/bin/debug/data/permissions +NadekoBot/bin/debug/data/incidents +!NadekoBot/bin/Debug/data/currency_images/* Tests/bin # NuGet Packages diff --git a/NadekoBot/App.config b/NadekoBot/App.config index 151cc154..75d0347a 100644 --- a/NadekoBot/App.config +++ b/NadekoBot/App.config @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/NadekoBot/Classes/BombermanGame.cs b/NadekoBot/Classes/BombermanGame.cs new file mode 100644 index 00000000..5b801e30 --- /dev/null +++ b/NadekoBot/Classes/BombermanGame.cs @@ -0,0 +1,8 @@ +namespace NadekoBot.Classes +{ + class BombermanGame + { + public ulong ChannelId { get; internal set; } + public bool Ended { get; internal set; } + } +} diff --git a/NadekoBot/Classes/DBHandler.cs b/NadekoBot/Classes/DBHandler.cs index fd54899f..c6488a27 100644 --- a/NadekoBot/Classes/DBHandler.cs +++ b/NadekoBot/Classes/DBHandler.cs @@ -1,19 +1,23 @@ -using System.Collections.Generic; -using System.Linq; +using NadekoBot.Classes._DataModels; using SQLite; -using NadekoBot.Classes._DataModels; using System; +using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; -namespace NadekoBot.Classes { - internal class DbHandler { +namespace NadekoBot.Classes +{ + internal class DbHandler + { public static DbHandler Instance { get; } = new DbHandler(); private string FilePath { get; } = "data/nadekobot.sqlite"; static DbHandler() { } - public DbHandler() { - using (var conn = new SQLiteConnection(FilePath)) { + public DbHandler() + { + using (var conn = new SQLiteConnection(FilePath)) + { conn.CreateTable(); conn.CreateTable(); conn.CreateTable(); @@ -23,44 +27,83 @@ namespace NadekoBot.Classes { conn.CreateTable(); conn.CreateTable(); conn.CreateTable(); - conn.CreateTable(); + conn.CreateTable(); conn.CreateTable(); + conn.CreateTable(); conn.Execute(Queries.TransactionTriggerQuery); } } - internal void InsertData(T o) where T : IDataModel { - using (var conn = new SQLiteConnection(FilePath)) { + internal T FindOne(Expression> p) where T : IDataModel, new() + { + using (var conn = new SQLiteConnection(FilePath)) + { + return conn.Table().Where(p).FirstOrDefault(); + } + } + + internal void DeleteAll() where T : IDataModel + { + using (var conn = new SQLiteConnection(FilePath)) + { + conn.DeleteAll(); + } + } + + internal void DeleteWhere(Expression> p) where T : IDataModel, new() + { + using (var conn = new SQLiteConnection(FilePath)) + { + var id = conn.Table().Where(p).FirstOrDefault()?.Id; + if (id.HasValue) + conn.Delete(id); + } + } + + internal void InsertData(T o) where T : IDataModel + { + using (var conn = new SQLiteConnection(FilePath)) + { conn.Insert(o, typeof(T)); } } - internal void InsertMany(T objects) where T : IEnumerable { - using (var conn = new SQLiteConnection(FilePath)) { + internal void InsertMany(T objects) where T : IEnumerable + { + using (var conn = new SQLiteConnection(FilePath)) + { conn.InsertAll(objects); } } - internal void UpdateData(T o) where T : IDataModel { - using (var conn = new SQLiteConnection(FilePath)) { + internal void UpdateData(T o) where T : IDataModel + { + using (var conn = new SQLiteConnection(FilePath)) + { conn.Update(o, typeof(T)); } } - internal HashSet GetAllRows() where T : IDataModel, new() { - using (var conn = new SQLiteConnection(FilePath)) { + internal HashSet GetAllRows() where T : IDataModel, new() + { + using (var conn = new SQLiteConnection(FilePath)) + { return new HashSet(conn.Table()); } } - internal CurrencyState GetStateByUserId(long id) { - using (var conn = new SQLiteConnection(FilePath)) { + internal CurrencyState GetStateByUserId(long id) + { + using (var conn = new SQLiteConnection(FilePath)) + { return conn.Table().Where(x => x.UserId == id).FirstOrDefault(); } } - internal T Delete(int id) where T : IDataModel, new() { - using (var conn = new SQLiteConnection(FilePath)) { + internal T Delete(int id) where T : IDataModel, new() + { + using (var conn = new SQLiteConnection(FilePath)) + { var found = conn.Find(id); if (found != null) conn.Delete(found.Id); @@ -71,8 +114,10 @@ namespace NadekoBot.Classes { /// /// Updates an existing object or creates a new one /// - internal void Save(T o) where T : IDataModel, new() { - using (var conn = new SQLiteConnection(FilePath)) { + internal void Save(T o) where T : IDataModel, new() + { + using (var conn = new SQLiteConnection(FilePath)) + { var found = conn.Find(o.Id); if (found == null) conn.Insert(o, typeof(T)); @@ -81,8 +126,10 @@ namespace NadekoBot.Classes { } } - internal T GetRandom(Expression> p) where T : IDataModel, new() { - using (var conn = new SQLiteConnection(FilePath)) { + internal T GetRandom(Expression> p) where T : IDataModel, new() + { + using (var conn = new SQLiteConnection(FilePath)) + { var r = new Random(); return conn.Table().Where(p).ToList().OrderBy(x => r.Next()).FirstOrDefault(); } @@ -90,13 +137,14 @@ namespace NadekoBot.Classes { } } -public static class Queries { +public static class Queries +{ public static string TransactionTriggerQuery = @" CREATE TRIGGER IF NOT EXISTS OnTransactionAdded AFTER INSERT ON CurrencyTransaction BEGIN INSERT OR REPLACE INTO CurrencyState (Id, UserId, Value, DateAdded) - VALUES (COALESCE((SELECT Id from CurrencyState where UserId = NEW.UserId),(SELECT COALESCE(MAX(Id),0)+1 from CurrencyState)), + VALUES (COALESCE((SELECT Id from CurrencyState where UserId = NEW.UserId),(SELECT COALESCE(MAX(Id),0)+1 from CurrencyState)), NEW.UserId, COALESCE((SELECT Value+New.Value FROM CurrencyState Where UserId = NEW.UserId),NEW.Value), NEW.DateAdded); diff --git a/NadekoBot/Classes/FlowersHandler.cs b/NadekoBot/Classes/FlowersHandler.cs index c40dd83e..d696c0b1 100644 --- a/NadekoBot/Classes/FlowersHandler.cs +++ b/NadekoBot/Classes/FlowersHandler.cs @@ -4,7 +4,7 @@ namespace NadekoBot.Classes { internal static class FlowersHandler { - public static async Task AddFlowersAsync(Discord.User u, string reason, int amount) + public static async Task AddFlowersAsync(Discord.User u, string reason, int amount, bool silent = false) { if (amount <= 0) return; @@ -17,6 +17,10 @@ namespace NadekoBot.Classes Value = amount, }); }); + + if (silent) + return; + var flows = ""; for (var i = 0; i < amount; i++) { @@ -25,19 +29,23 @@ namespace NadekoBot.Classes await u.SendMessage("👑Congratulations!👑\nYou received: " + flows); } - public static async Task RemoveFlowersAsync(Discord.User u, string reason, int amount) + public static bool RemoveFlowers(Discord.User u, string reason, int amount) { if (amount <= 0) - return; - await Task.Run(() => + return false; + var uid = (long)u.Id; + var state = DbHandler.Instance.FindOne<_DataModels.CurrencyState>(cs => cs.UserId == uid); + + if (state.Value < amount) + return false; + + DbHandler.Instance.InsertData(new _DataModels.CurrencyTransaction { - DbHandler.Instance.InsertData(new _DataModels.CurrencyTransaction - { - Reason = reason, - UserId = (long)u.Id, - Value = -amount, - }); + Reason = reason, + UserId = (long)u.Id, + Value = -amount, }); + return true; } } } diff --git a/NadekoBot/Classes/JSONModels/Configuration.cs b/NadekoBot/Classes/JSONModels/Configuration.cs index a528cc37..1bfcd92d 100644 --- a/NadekoBot/Classes/JSONModels/Configuration.cs +++ b/NadekoBot/Classes/JSONModels/Configuration.cs @@ -94,7 +94,7 @@ namespace NadekoBot.Classes.JSONModels public string Gambling { get; set; } = "$"; public string Permissions { get; set; } = ";"; public string Programming { get; set; } = "%"; - public string Pokemon { get; set; } = "poke"; + public string Pokemon { get; set; } = ">"; } public static class ConfigHandler diff --git a/NadekoBot/Classes/Music/MusicControls.cs b/NadekoBot/Classes/Music/MusicControls.cs index cf24af88..f83ac3e8 100644 --- a/NadekoBot/Classes/Music/MusicControls.cs +++ b/NadekoBot/Classes/Music/MusicControls.cs @@ -5,15 +5,18 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -namespace NadekoBot.Classes.Music { +namespace NadekoBot.Classes.Music +{ - public enum MusicType { + public enum MusicType + { Radio, Normal, Local } - public enum StreamState { + public enum StreamState + { Resolving, Queued, Buffering, //not using it atm @@ -21,7 +24,8 @@ namespace NadekoBot.Classes.Music { Completed } - public class MusicPlayer { + public class MusicPlayer + { public static int MaximumPlaylistSize => 50; private IAudioClient audioClient { get; set; } @@ -44,8 +48,11 @@ namespace NadekoBot.Classes.Music { public Channel PlaybackVoiceChannel { get; private set; } private bool Destroyed { get; set; } = false; + public bool RepeatSong { get; private set; } = false; + public bool RepeatPlaylist { get; private set; } = false; - public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume) { + public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume) + { if (startingVoiceChannel == null) throw new ArgumentNullException(nameof(startingVoiceChannel)); if (startingVoiceChannel.Type != ChannelType.Voice) @@ -56,27 +63,42 @@ namespace NadekoBot.Classes.Music { SongCancelSource = new CancellationTokenSource(); cancelToken = SongCancelSource.Token; - Task.Run(async () => { - while (!Destroyed) { - try { - if(audioClient?.State != ConnectionState.Connected) + Task.Run(async () => + { + while (!Destroyed) + { + try + { + if (audioClient?.State != ConnectionState.Connected) audioClient = await PlaybackVoiceChannel.JoinAudio(); - } catch { + } + catch + { await Task.Delay(1000); continue; } CurrentSong = GetNextSong(); var curSong = CurrentSong; - if (curSong != null) { - try { + if (curSong != null) + { + try + { OnStarted(this, curSong); await curSong.Play(audioClient, cancelToken); - } catch (OperationCanceledException) { + } + catch (OperationCanceledException) + { Console.WriteLine("Song canceled"); - } catch (Exception ex) { + } + catch (Exception ex) + { Console.WriteLine($"Exception in PlaySong: {ex}"); } OnCompleted(this, curSong); + if (RepeatSong) + playlist.Insert(0, curSong); + else if (RepeatPlaylist) + playlist.Insert(playlist.Count, curSong); SongCancelSource = new CancellationTokenSource(); cancelToken = SongCancelSource.Token; } @@ -85,17 +107,22 @@ namespace NadekoBot.Classes.Music { }); } - public void Next() { - lock (playlistLock) { - if (!SongCancelSource.IsCancellationRequested) { + public void Next() + { + lock (playlistLock) + { + if (!SongCancelSource.IsCancellationRequested) + { Paused = false; SongCancelSource.Cancel(); } } } - public void Stop() { - lock (playlistLock) { + public void Stop() + { + lock (playlistLock) + { playlist.Clear(); CurrentSong = null; if (!SongCancelSource.IsCancellationRequested) @@ -105,13 +132,16 @@ namespace NadekoBot.Classes.Music { public void TogglePause() => Paused = !Paused; - public void Shuffle() { - lock (playlistLock) { + public void Shuffle() + { + lock (playlistLock) + { playlist.Shuffle(); } } - public int SetVolume(int volume) { + public int SetVolume(int volume) + { if (volume < 0) volume = 0; if (volume > 150) @@ -121,8 +151,10 @@ namespace NadekoBot.Classes.Music { return volume; } - private Song GetNextSong() { - lock (playlistLock) { + private Song GetNextSong() + { + lock (playlistLock) + { if (playlist.Count == 0) return null; var toReturn = playlist[0]; @@ -131,45 +163,56 @@ namespace NadekoBot.Classes.Music { } } - public void AddSong(Song s) { + public void AddSong(Song s) + { if (s == null) throw new ArgumentNullException(nameof(s)); - lock (playlistLock) { + lock (playlistLock) + { playlist.Add(s); } } - public void RemoveSong(Song s) { + public void RemoveSong(Song s) + { if (s == null) throw new ArgumentNullException(nameof(s)); - lock (playlistLock) { + lock (playlistLock) + { playlist.Remove(s); } } - public void RemoveSongAt(int index) { - lock (playlistLock) { + public void RemoveSongAt(int index) + { + lock (playlistLock) + { if (index < 0 || index >= playlist.Count) throw new ArgumentException("Invalid index"); playlist.RemoveAt(index); } } - internal Task MoveToVoiceChannel(Channel voiceChannel) { + internal Task MoveToVoiceChannel(Channel voiceChannel) + { if (audioClient?.State != ConnectionState.Connected) throw new InvalidOperationException("Can't move while bot is not connected to voice channel."); PlaybackVoiceChannel = voiceChannel; return PlaybackVoiceChannel.JoinAudio(); } - internal void ClearQueue() { - lock (playlistLock) { + internal void ClearQueue() + { + lock (playlistLock) + { playlist.Clear(); } } - public void Destroy() { - lock (playlistLock) { + public void Destroy() + { + lock (playlistLock) + { playlist.Clear(); Destroyed = true; CurrentSong = null; @@ -178,5 +221,9 @@ namespace NadekoBot.Classes.Music { audioClient.Disconnect(); } } + + internal bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong; + + internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist; } } diff --git a/NadekoBot/Classes/NadekoStats.cs b/NadekoBot/Classes/NadekoStats.cs index e7d7a8fd..b8d98e0f 100644 --- a/NadekoBot/Classes/NadekoStats.cs +++ b/NadekoBot/Classes/NadekoStats.cs @@ -1,16 +1,19 @@ using Discord; using Discord.Commands; +using NadekoBot.Extensions; +using NadekoBot.Modules; +using NadekoBot.Modules.Administration.Commands; using System; using System.Diagnostics; using System.Linq; -using NadekoBot.Extensions; -using System.Threading.Tasks; using System.Reflection; +using System.Threading.Tasks; using System.Timers; -using NadekoBot.Modules; -namespace NadekoBot { - public class NadekoStats { +namespace NadekoBot +{ + public class NadekoStats + { public static NadekoStats Instance { get; } = new NadekoStats(); public string BotVersion => $"{Assembly.GetExecutingAssembly().GetName().Name} v{Assembly.GetExecutingAssembly().GetName().Version}"; @@ -23,11 +26,12 @@ namespace NadekoBot { public int TextChannelsCount { get; private set; } = 0; public int VoiceChannelsCount { get; private set; } = 0; - private readonly Timer commandLogTimer = new Timer() {Interval = 10000}; + private readonly Timer commandLogTimer = new Timer() { Interval = 10000 }; static NadekoStats() { } - private NadekoStats() { + private NadekoStats() + { var commandService = NadekoBot.Client.GetService(); statsStopwatch.Start(); @@ -43,52 +47,66 @@ namespace NadekoBot { TextChannelsCount = channelsArray.Count(c => c.Type == ChannelType.Text); VoiceChannelsCount = channelsArray.Count() - TextChannelsCount; - NadekoBot.Client.JoinedServer += (s, e) => { - try { + NadekoBot.Client.JoinedServer += (s, e) => + { + try + { ServerCount++; TextChannelsCount += e.Server.TextChannels.Count(); VoiceChannelsCount += e.Server.VoiceChannels.Count(); - } catch { } + } + catch { } }; - NadekoBot.Client.LeftServer += (s, e) => { - try { + NadekoBot.Client.LeftServer += (s, e) => + { + try + { ServerCount--; TextChannelsCount -= e.Server.TextChannels.Count(); VoiceChannelsCount -= e.Server.VoiceChannels.Count(); - } catch { } + } + catch { } }; - NadekoBot.Client.ChannelCreated += (s, e) => { - try { + NadekoBot.Client.ChannelCreated += (s, e) => + { + try + { if (e.Channel.IsPrivate) return; if (e.Channel.Type == ChannelType.Text) TextChannelsCount++; else if (e.Channel.Type == ChannelType.Voice) VoiceChannelsCount++; - } catch { } + } + catch { } }; - NadekoBot.Client.ChannelDestroyed += (s, e) => { - try { + NadekoBot.Client.ChannelDestroyed += (s, e) => + { + try + { if (e.Channel.IsPrivate) return; if (e.Channel.Type == ChannelType.Text) VoiceChannelsCount++; else if (e.Channel.Type == ChannelType.Voice) VoiceChannelsCount--; - } catch { } + } + catch { } }; } public TimeSpan GetUptime() => DateTime.Now - Process.GetCurrentProcess().StartTime; - public string GetUptimeString() { + public string GetUptimeString() + { var time = (DateTime.Now - Process.GetCurrentProcess().StartTime); return time.Days + " days, " + time.Hours + " hours, and " + time.Minutes + " minutes."; } public Task LoadStats() => - Task.Run(() => { + Task.Run(() => + { var songs = Music.MusicPlayers.Count(mp => mp.Value.CurrentSong != null); var sb = new System.Text.StringBuilder(); sb.AppendLine("`Author: Kwoth` `Library: Discord.Net`"); @@ -102,7 +120,7 @@ namespace NadekoBot { sb.AppendLine($" | VoiceChannels: {VoiceChannelsCount}`"); sb.AppendLine($"`Commands Ran this session: {commandsRan}`"); sb.AppendLine($"`Message queue size: {NadekoBot.Client.MessageQueue.Count}`"); - sb.Append($"`Greeted {Commands.ServerGreetCommand.Greeted} times.`"); + sb.Append($"`Greeted {ServerGreetCommand.Greeted} times.`"); sb.AppendLine($" `| Playing {songs} songs, ".SnPl(songs) + $"{Music.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count)} queued.`"); sb.AppendLine($"`Heap: {Heap(false)}`"); @@ -111,7 +129,8 @@ namespace NadekoBot { public string Heap(bool pass = true) => Math.Round((double)GC.GetTotalMemory(pass) / 1.MiB(), 2).ToString(); - public async Task GetStats() { + public async Task GetStats() + { if (statsStopwatch.Elapsed.Seconds < 4 && !string.IsNullOrWhiteSpace(statsCache)) return statsCache; await LoadStats(); @@ -119,35 +138,45 @@ namespace NadekoBot { return statsCache; } - private async Task StartCollecting() { - while (true) { + private async Task StartCollecting() + { + while (true) + { await Task.Delay(new TimeSpan(0, 30, 0)); - try { + try + { var onlineUsers = await Task.Run(() => NadekoBot.Client.Servers.Sum(x => x.Users.Count())); var realOnlineUsers = await Task.Run(() => NadekoBot.Client.Servers .Sum(x => x.Users.Count(u => u.Status == UserStatus.Online))); var connectedServers = NadekoBot.Client.Servers.Count(); - Classes.DbHandler.Instance.InsertData(new Classes._DataModels.Stats { + Classes.DbHandler.Instance.InsertData(new Classes._DataModels.Stats + { OnlineUsers = onlineUsers, RealOnlineUsers = realOnlineUsers, Uptime = GetUptime(), ConnectedServers = connectedServers, DateAdded = DateTime.Now }); - } catch { + } + catch + { Console.WriteLine("DB Exception in stats collecting."); break; } } } - private async void StatsCollector_RanCommand(object sender, CommandEventArgs e) { + private async void StatsCollector_RanCommand(object sender, CommandEventArgs e) + { Console.WriteLine($">>Command {e.Command.Text}"); - await Task.Run(() => { - try { + await Task.Run(() => + { + try + { commandsRan++; - Classes.DbHandler.Instance.InsertData(new Classes._DataModels.Command { + Classes.DbHandler.Instance.InsertData(new Classes._DataModels.Command + { ServerId = (long)e.Server.Id, ServerName = e.Server.Name, ChannelId = (long)e.Channel.Id, @@ -157,7 +186,9 @@ namespace NadekoBot { CommandName = e.Command.Text, DateAdded = DateTime.Now }); - } catch { + } + catch + { Console.WriteLine("Error in ran command DB write."); } }); diff --git a/NadekoBot/Classes/ServerSpecificConfig.cs b/NadekoBot/Classes/ServerSpecificConfig.cs index 01a0deb3..a7615b56 100644 --- a/NadekoBot/Classes/ServerSpecificConfig.cs +++ b/NadekoBot/Classes/ServerSpecificConfig.cs @@ -1,15 +1,16 @@ -using System; +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 NadekoBot.Classes.JSONModels; -using Newtonsoft.Json; -namespace NadekoBot.Classes { - internal class SpecificConfigurations { +namespace NadekoBot.Classes +{ + internal class SpecificConfigurations + { public static SpecificConfigurations Default { get; } = new SpecificConfigurations(); public static bool Instantiated { get; private set; } @@ -17,14 +18,19 @@ namespace NadekoBot.Classes { static SpecificConfigurations() { } - private SpecificConfigurations() { + private SpecificConfigurations() + { - if (File.Exists(filePath)) { - try { + if (File.Exists(filePath)) + { + try + { configs = JsonConvert .DeserializeObject>( File.ReadAllText(filePath)); - } catch (Exception ex) { + } + catch (Exception ex) + { Console.WriteLine($"Deserialization failing: {ex}"); } } @@ -42,14 +48,17 @@ namespace NadekoBot.Classes { private readonly object saveLock = new object(); - public void Save() { - lock (saveLock) { + public void Save() + { + lock (saveLock) + { File.WriteAllText(filePath, JsonConvert.SerializeObject(configs, Formatting.Indented)); } } } - internal class ServerSpecificConfig : INotifyPropertyChanged { + internal class ServerSpecificConfig : INotifyPropertyChanged + { [JsonProperty("VoicePlusTextEnabled")] private bool voicePlusTextEnabled; [JsonIgnore] @@ -78,7 +87,8 @@ namespace NadekoBot.Classes { set { listOfSelfAssignableRoles = value; if (value != null) - listOfSelfAssignableRoles.CollectionChanged += (s, e) => { + listOfSelfAssignableRoles.CollectionChanged += (s, e) => + { if (!SpecificConfigurations.Instantiated) return; OnPropertyChanged(); }; @@ -92,34 +102,39 @@ namespace NadekoBot.Classes { set { observingStreams = value; if (value != null) - observingStreams.CollectionChanged += (s, e) => { + observingStreams.CollectionChanged += (s, e) => + { if (!SpecificConfigurations.Instantiated) return; OnPropertyChanged(); }; } } - public ServerSpecificConfig() { + public ServerSpecificConfig() + { ListOfSelfAssignableRoles = new ObservableCollection(); ObservingStreams = new ObservableCollection(); } public event PropertyChangedEventHandler PropertyChanged = delegate { SpecificConfigurations.Default.Save(); }; - private void OnPropertyChanged([CallerMemberName] string propertyName = null) { + private void OnPropertyChanged([CallerMemberName] string propertyName = null) + { Console.WriteLine("property changed"); PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } - public class StreamNotificationConfig : IEquatable { + 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 { + public enum StreamType + { Twitch, Beam, Hitbox, @@ -131,7 +146,8 @@ namespace NadekoBot.Classes { this.Type == other.Type && this.ServerId == other.ServerId; - public override int GetHashCode() { + public override int GetHashCode() + { return (int)((int)ServerId + Username.Length + (int)Type); } } diff --git a/NadekoBot/Classes/_DataModels/PokeTypes.cs b/NadekoBot/Classes/_DataModels/PokeTypes.cs index 3661540a..d4cceac2 100644 --- a/NadekoBot/Classes/_DataModels/PokeTypes.cs +++ b/NadekoBot/Classes/_DataModels/PokeTypes.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace NadekoBot.Classes._DataModels { - class userPokeTypes : IDataModel + class UserPokeTypes : IDataModel { public long UserId { get; set; } public int type { get; set; } diff --git a/NadekoBot/Classes/_DataModels/Reminder.cs b/NadekoBot/Classes/_DataModels/Reminder.cs new file mode 100644 index 00000000..6be3d0a7 --- /dev/null +++ b/NadekoBot/Classes/_DataModels/Reminder.cs @@ -0,0 +1,14 @@ +using System; + +namespace NadekoBot.Classes._DataModels +{ + class Reminder : IDataModel + { + public DateTime When { get; set; } + public long ChannelId { get; set; } + public long ServerId { get; set; } + public long UserId { get; set; } + public string Message { get; set; } + public bool IsPrivate { get; set; } + } +} diff --git a/NadekoBot/Commands/Bomberman.cs b/NadekoBot/Commands/Bomberman.cs new file mode 100644 index 00000000..44d460e5 --- /dev/null +++ b/NadekoBot/Commands/Bomberman.cs @@ -0,0 +1,56 @@ +using Discord; +using Discord.Commands; +using NadekoBot.Classes; +using NadekoBot.Modules; +using System; + +namespace NadekoBot.Commands +{ + class Bomberman : DiscordCommand + { + public Bomberman(DiscordModule module) : base(module) + { + NadekoBot.Client.MessageReceived += async (s, e) => + { + if (e.Channel.Id != bombGame.ChannelId) return; + + var text = e.Message.Text; + await e.Message.Delete(); + HandleBombermanCommand(e.User, text); + }; + } + + private void HandleBombermanCommand(User user, string text) + { + throw new NotImplementedException(); + } + + //only one bomberman game can run at any one time + public static BombermanGame bombGame = null; + private readonly object locker = new object(); + internal override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand($"{Module.Prefix}bmb") + .Description("Creates a bomberman game for this channel or join existing one." + + " If you are 4th player - Game will start. After game starts " + + " everything written in the channel will be autodeleted and treated as a bomberman command." + + " only one bomberman game can run at any one time per bot. Game will run at 1FPS." + + " You must have manage messages permissions in order to create the game.") + .Do(e => + { + lock (locker) + { + if (bombGame == null || bombGame.Ended) + { + if (!e.User.ServerPermissions.ManageMessages || + !e.Server.GetUser(NadekoBot.Client.CurrentUser.Id).ServerPermissions.ManageMessages) + { + e.Channel.SendMessage("Both you and Nadeko need manage messages permissions to start a new bomberman game.").Wait(); + } + + } + } + }); + } + } +} diff --git a/NadekoBot/Commands/FilterWordsCommand.cs b/NadekoBot/Commands/FilterWordsCommand.cs index 48a5e802..5c140238 100644 --- a/NadekoBot/Commands/FilterWordsCommand.cs +++ b/NadekoBot/Commands/FilterWordsCommand.cs @@ -1,27 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Discord; +using Discord; using Discord.Commands; using NadekoBot.Classes; using NadekoBot.Classes.Permissions; using NadekoBot.Modules; +using System; +using System.Linq; using ServerPermissions = NadekoBot.Classes.Permissions.ServerPermissions; -namespace NadekoBot.Commands { - internal class FilterWords : DiscordCommand { - public FilterWords(DiscordModule module) : base(module) { - NadekoBot.Client.MessageReceived += async (sender, args) => { +namespace NadekoBot.Commands +{ + 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 { + try + { ServerPermissions serverPerms; if (!IsChannelOrServerFiltering(args.Channel, out serverPerms)) return; var wordsInMessage = args.Message.RawText.ToLowerInvariant().Split(' '); - if (serverPerms.Words.Any(w => wordsInMessage.Contains(w))) { + if (serverPerms.Words.Any(w => wordsInMessage.Contains(w))) + { await args.Message.Delete(); IncidentsHandler.Add(args.Server.Id, $"User [{args.User.Name}/{args.User.Id}] posted " + $"BANNED WORD in [{args.Channel.Name}/{args.Channel.Id}] channel. " + @@ -30,11 +32,13 @@ namespace NadekoBot.Commands { await args.Channel.SendMessage($"{args.User.Mention} One or more of the words you used " + $"in that sentence are not allowed here."); } - } catch { } + } + catch { } }; } - private static bool IsChannelOrServerFiltering(Channel channel, out ServerPermissions serverPerms) { + private static bool IsChannelOrServerFiltering(Channel channel, out ServerPermissions serverPerms) + { if (!PermissionsHandler.PermissionsDict.TryGetValue(channel.Server.Id, out serverPerms)) return false; if (serverPerms.Permissions.FilterWords) @@ -44,7 +48,8 @@ namespace NadekoBot.Commands { return serverPerms.ChannelPermissions.TryGetValue(channel.Id, out perms) && perms.FilterWords; } - internal override void Init(CommandGroupBuilder cgb) { + internal override void Init(CommandGroupBuilder cgb) + { cgb.CreateCommand(Module.Prefix + "cfw") .Alias(Module.Prefix + "channelfilterwords") .Description("Enables or disables automatic deleting of messages containing banned words on the channel." + @@ -52,12 +57,15 @@ namespace NadekoBot.Commands { "\n**Usage**: ;cfi enable #general-chat") .Parameter("bool") .Parameter("channel", ParameterType.Optional) - .Do(async e => { - try { + .Do(async e => + { + try + { var state = PermissionHelper.ValidateBool(e.GetArg("bool")); var chanStr = e.GetArg("channel")?.ToLowerInvariant().Trim(); - if (chanStr != "all") { + if (chanStr != "all") + { var chan = string.IsNullOrWhiteSpace(chanStr) ? e.Channel : PermissionHelper.ValidateChannel(e.Server, chanStr); @@ -67,11 +75,14 @@ namespace NadekoBot.Commands { } //all channels - foreach (var curChannel in e.Server.TextChannels) { + foreach (var curChannel in e.Server.TextChannels) + { PermissionsHandler.SetChannelWordPermission(curChannel, state); } await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** for **ALL** channels."); - } catch (Exception ex) { + } + catch (Exception ex) + { await e.Channel.SendMessage($"💢 Error: {ex.Message}"); } }); @@ -81,15 +92,19 @@ namespace NadekoBot.Commands { .Description("Adds a new word to the list of filtered words" + "\n**Usage**: ;aw poop") .Parameter("word", ParameterType.Unparsed) - .Do(async e => { - try { + .Do(async e => + { + try + { var word = e.GetArg("word"); if (string.IsNullOrWhiteSpace(word)) return; PermissionsHandler.AddFilteredWord(e.Server, word.ToLowerInvariant().Trim()); await e.Channel.SendMessage($"Successfully added new filtered word."); - } catch (Exception ex) { + } + catch (Exception ex) + { await e.Channel.SendMessage($"💢 Error: {ex.Message}"); } }); @@ -99,15 +114,19 @@ namespace NadekoBot.Commands { .Description("Removes the word from the list of filtered words" + "\n**Usage**: ;rw poop") .Parameter("word", ParameterType.Unparsed) - .Do(async e => { - try { + .Do(async e => + { + try + { var word = e.GetArg("word"); if (string.IsNullOrWhiteSpace(word)) return; PermissionsHandler.RemoveFilteredWord(e.Server, word.ToLowerInvariant().Trim()); await e.Channel.SendMessage($"Successfully removed filtered word."); - } catch (Exception ex) { + } + catch (Exception ex) + { await e.Channel.SendMessage($"💢 Error: {ex.Message}"); } }); @@ -116,14 +135,18 @@ namespace NadekoBot.Commands { .Alias(Module.Prefix + "listfilteredwords") .Description("Shows a list of filtered words" + "\n**Usage**: ;lfw") - .Do(async e => { - try { + .Do(async e => + { + try + { ServerPermissions serverPerms; if (!PermissionsHandler.PermissionsDict.TryGetValue(e.Server.Id, out serverPerms)) return; await e.Channel.SendMessage($"There are `{serverPerms.Words.Count}` filtered words.\n" + string.Join("\n", serverPerms.Words)); - } catch (Exception ex) { + } + catch (Exception ex) + { await e.Channel.SendMessage($"💢 Error: {ex.Message}"); } }); @@ -132,13 +155,17 @@ namespace NadekoBot.Commands { .Alias(Module.Prefix + "serverfilterwords") .Description("Enables or disables automatic deleting of messages containing forbidden words on the server.\n**Usage**: ;sfi disable") .Parameter("bool") - .Do(async e => { - try { + .Do(async e => + { + try + { var state = PermissionHelper.ValidateBool(e.GetArg("bool")); PermissionsHandler.SetServerWordPermission(e.Server, state); await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** on this server."); - } catch (Exception ex) { + } + catch (Exception ex) + { await e.Channel.SendMessage($"💢 Error: {ex.Message}"); } }); diff --git a/NadekoBot/Commands/PlantPick.cs b/NadekoBot/Commands/PlantPick.cs new file mode 100644 index 00000000..8d688911 --- /dev/null +++ b/NadekoBot/Commands/PlantPick.cs @@ -0,0 +1,85 @@ +using Discord; +using Discord.Commands; +using NadekoBot.Classes; +using NadekoBot.Modules; +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace NadekoBot.Commands +{ + /// + /// 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 + { + public PlantPick(DiscordModule module) : base(module) + { + + } + + //channelid/messageid pair + ConcurrentDictionary plantedFlowerChannels = new ConcurrentDictionary(); + + private object locker = new object(); + + internal override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand(Module.Prefix + "pick") + .Description("Picks a flower planted in this channel.") + .Do(async e => + { + Message msg; + + await e.Message.Delete(); + if (!plantedFlowerChannels.TryRemove(e.Channel.Id, out msg)) + return; + + await msg.Delete(); + await FlowersHandler.AddFlowersAsync(e.User, "Picked a flower.", 1, true); + msg = await e.Channel.SendMessage($"**{e.User.Name}** picked a {NadekoBot.Config.CurrencyName}!"); + await Task.Delay(10000); + await msg.Delete(); + }); + + 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)") + .Do(async e => + { + lock (locker) + { + if (plantedFlowerChannels.ContainsKey(e.Channel.Id)) + { + e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel."); + return; + } + var removed = FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1); + if (!removed) + { + e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").Wait(); + return; + } + + var rng = new Random(); + var file = Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault(); + Message msg; + if (file == null) + msg = e.Channel.SendMessage("🌸").Result; + else + msg = e.Channel.SendFile(file).Result; + plantedFlowerChannels.TryAdd(e.Channel.Id, msg); + } + + var msg2 = await e.Channel.SendMessage($"Oh how Nice! **{e.User.Name}** planted a flower. Pick it using {Module.Prefix}pick"); + await Task.Delay(20000); + await msg2.Delete(); + }); + } + } +} diff --git a/NadekoBot/Commands/StreamNotifications.cs b/NadekoBot/Commands/StreamNotifications.cs index f7a980d4..60afd8d8 100644 --- a/NadekoBot/Commands/StreamNotifications.cs +++ b/NadekoBot/Commands/StreamNotifications.cs @@ -5,6 +5,7 @@ using NadekoBot.Classes.Permissions; using NadekoBot.Modules; using Newtonsoft.Json.Linq; using System; +using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; using System.Timers; @@ -18,11 +19,15 @@ namespace NadekoBot.Commands { Interval = new TimeSpan(0, 0, 15).TotalMilliseconds, }; + + private ConcurrentDictionary> cachedStatuses = new ConcurrentDictionary>(); + public StreamNotifications(DiscordModule module) : base(module) { checkTimer.Elapsed += async (s, e) => { + cachedStatuses.Clear(); try { var streams = SpecificConfigurations.Default.AllConfigs.SelectMany(c => c.ObservingStreams); @@ -74,23 +79,39 @@ namespace NadekoBot.Commands bool isLive; string response; JObject data; + Tuple result; switch (stream.Type) { case StreamNotificationConfig.StreamType.Hitbox: - response = await SearchHelper.GetResponseStringAsync($"https://api.hitbox.tv/media/status/{stream.Username}"); + var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username}"; + if (cachedStatuses.TryGetValue(hitboxUrl, out result)) + return result; + response = await SearchHelper.GetResponseStringAsync(hitboxUrl); data = JObject.Parse(response); isLive = data["media_is_live"].ToString() == "1"; - return new Tuple(isLive, data["media_views"].ToString()); + result = new Tuple(isLive, data["media_views"].ToString()); + cachedStatuses.TryAdd(hitboxUrl, result); + return result; case StreamNotificationConfig.StreamType.Twitch: - response = await SearchHelper.GetResponseStringAsync($"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username)}"); + var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username)}"; + if (cachedStatuses.TryGetValue(twitchUrl, out result)) + return result; + response = await SearchHelper.GetResponseStringAsync(twitchUrl); data = JObject.Parse(response); isLive = !string.IsNullOrWhiteSpace(data["stream"].ToString()); - return new Tuple(isLive, isLive ? data["stream"]["viewers"].ToString() : "0"); + result = new Tuple(isLive, isLive ? data["stream"]["viewers"].ToString() : "0"); + cachedStatuses.TryAdd(twitchUrl, result); + return result; case StreamNotificationConfig.StreamType.Beam: - response = await SearchHelper.GetResponseStringAsync($"https://beam.pro/api/v1/channels/{stream.Username}"); + var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username}"; + if (cachedStatuses.TryGetValue(beamUrl, out result)) + return result; + response = await SearchHelper.GetResponseStringAsync(beamUrl); data = JObject.Parse(response); isLive = data["online"].ToObject() == true; - return new Tuple(isLive, data["viewersCurrent"].ToString()); + result = new Tuple(isLive, data["viewersCurrent"].ToString()); + cachedStatuses.TryAdd(beamUrl, result); + return result; default: break; } diff --git a/NadekoBot/Modules/Administration.cs b/NadekoBot/Modules/Administration/AdministrationModule.cs similarity index 99% rename from NadekoBot/Modules/Administration.cs rename to NadekoBot/Modules/Administration/AdministrationModule.cs index 80b5c0d2..b2508fb2 100644 --- a/NadekoBot/Modules/Administration.cs +++ b/NadekoBot/Modules/Administration/AdministrationModule.cs @@ -4,19 +4,19 @@ using Discord.Modules; using NadekoBot.Classes; using NadekoBot.Classes._DataModels; using NadekoBot.Classes.Permissions; -using NadekoBot.Commands; using NadekoBot.Extensions; +using NadekoBot.Modules.Administration.Commands; using Newtonsoft.Json.Linq; using System; using System.IO; using System.Linq; using System.Threading.Tasks; -namespace NadekoBot.Modules +namespace NadekoBot.Modules.Administration { - internal class Administration : DiscordModule + internal class AdministrationModule : DiscordModule { - public Administration() + public AdministrationModule() { commands.Add(new ServerGreetCommand(this)); commands.Add(new LogCommand(this)); @@ -26,6 +26,7 @@ namespace NadekoBot.Modules commands.Add(new VoicePlusTextCommand(this)); commands.Add(new CrossServerTextChannel(this)); commands.Add(new SelfAssignedRolesCommand(this)); + commands.Add(new Remind(this)); } public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Administration; @@ -227,6 +228,7 @@ namespace NadekoBot.Modules try { await e.Server.Ban(usr); + await e.Channel.SendMessage("Banned user " + usr.Name + " Id: " + usr.Id); } catch @@ -456,6 +458,7 @@ namespace NadekoBot.Modules cgb.CreateCommand(Prefix + "st").Alias(Prefix + "settopic") .Alias(Prefix + "topic") .Description("Sets a topic on the current channel.") + .AddCheck(SimpleCheckers.ManageChannels()) .Parameter("topic", ParameterType.Unparsed) .Do(async e => { diff --git a/NadekoBot/Commands/CrossServerTextChannel.cs b/NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs similarity index 74% rename from NadekoBot/Commands/CrossServerTextChannel.cs rename to NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs index 4def85f9..b11dce4d 100644 --- a/NadekoBot/Commands/CrossServerTextChannel.cs +++ b/NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs @@ -1,36 +1,48 @@ -using System; +using Discord; +using Discord.Commands; +using NadekoBot.Classes.Permissions; +using NadekoBot.Commands; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using Discord; -using Discord.Commands; -using NadekoBot.Classes.Permissions; -using NadekoBot.Modules; -namespace NadekoBot.Commands { - class CrossServerTextChannel : DiscordCommand { - public CrossServerTextChannel(DiscordModule module) : base(module) { - NadekoBot.Client.MessageReceived += async (s, e) => { - try { +namespace NadekoBot.Modules.Administration.Commands +{ + class CrossServerTextChannel : DiscordCommand + { + public CrossServerTextChannel(DiscordModule module) : base(module) + { + NadekoBot.Client.MessageReceived += async (s, e) => + { + try + { if (e.User.Id == NadekoBot.Client.CurrentUser.Id) return; - foreach (var subscriber in Subscribers) { + foreach (var subscriber in Subscribers) + { var set = subscriber.Value; if (!set.Contains(e.Channel)) continue; - foreach (var chan in set.Except(new[] { e.Channel })) { + foreach (var chan in set.Except(new[] { e.Channel })) + { await chan.SendMessage(GetText(e.Server, e.Channel, e.User, e.Message)); } } - } catch { } + } + catch { } }; - NadekoBot.Client.MessageUpdated += async (s, e) => { - try { + 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) { + foreach (var subscriber in Subscribers) + { var set = subscriber.Value; if (!set.Contains(e.Channel)) continue; - foreach (var chan in set.Except(new[] { e.Channel })) { + foreach (var chan in set.Except(new[] { e.Channel })) + { var msg = chan.Messages .FirstOrDefault(m => m.RawText == GetText(e.Server, e.Channel, e.User, e.Before)); @@ -39,7 +51,8 @@ namespace NadekoBot.Commands { } } - } catch { } + } + catch { } }; } @@ -48,15 +61,18 @@ namespace NadekoBot.Commands { public static readonly ConcurrentDictionary> Subscribers = new ConcurrentDictionary>(); - internal override void Init(CommandGroupBuilder cgb) { + 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") .AddCheck(SimpleCheckers.OwnerOnly()) - .Do(async e => { + .Do(async e => + { var token = new Random().Next(); var set = new HashSet(); - if (Subscribers.TryAdd(token, set)) { + if (Subscribers.TryAdd(token, set)) + { set.Add(e.Channel); await e.User.SendMessage("This is your CSC token:" + token.ToString()); } @@ -66,7 +82,8 @@ namespace NadekoBot.Commands { .Description("Joins current channel to an instance of cross server channel using the token.") .Parameter("token") .AddCheck(SimpleCheckers.ManageServer()) - .Do(async e => { + .Do(async e => + { int token; if (!int.TryParse(e.GetArg("token"), out token)) return; @@ -80,8 +97,10 @@ namespace NadekoBot.Commands { cgb.CreateCommand(Module.Prefix + "lcsc") .Description("Leaves Cross server channel instance from this channel") .AddCheck(SimpleCheckers.ManageServer()) - .Do(async e => { - foreach (var subscriber in Subscribers) { + .Do(async e => + { + foreach (var subscriber in Subscribers) + { subscriber.Value.Remove(e.Channel); } await e.Channel.SendMessage(":ok:"); diff --git a/NadekoBot/Commands/LogCommand.cs b/NadekoBot/Modules/Administration/Commands/LogCommand.cs similarity index 99% rename from NadekoBot/Commands/LogCommand.cs rename to NadekoBot/Modules/Administration/Commands/LogCommand.cs index 3b5783ea..8febca20 100644 --- a/NadekoBot/Commands/LogCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -2,13 +2,13 @@ using Discord.Commands; using NadekoBot.Classes; using NadekoBot.Classes.Permissions; -using NadekoBot.Modules; +using NadekoBot.Commands; using System; using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; -namespace NadekoBot.Commands +namespace NadekoBot.Modules.Administration.Commands { internal class LogCommand : DiscordCommand { diff --git a/NadekoBot/Commands/MessageRepeater.cs b/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs similarity index 80% rename from NadekoBot/Commands/MessageRepeater.cs rename to NadekoBot/Modules/Administration/Commands/MessageRepeater.cs index 9ef5d5bd..c742ead5 100644 --- a/NadekoBot/Commands/MessageRepeater.cs +++ b/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs @@ -1,15 +1,18 @@ -using System; -using System.Timers; -using System.Collections.Concurrent; -using Discord; -using NadekoBot.Classes.Permissions; +using Discord; using Discord.Commands; -using NadekoBot.Modules; +using NadekoBot.Classes.Permissions; +using NadekoBot.Commands; +using System; +using System.Collections.Concurrent; +using System.Timers; -namespace NadekoBot.Commands { - class MessageRepeater : DiscordCommand { +namespace NadekoBot.Modules.Administration.Commands +{ + class MessageRepeater : DiscordCommand + { private readonly ConcurrentDictionary repeaters = new ConcurrentDictionary(); - private class Repeater { + private class Repeater + { [Newtonsoft.Json.JsonIgnore] public Timer MessageTimer { get; set; } [Newtonsoft.Json.JsonIgnore] @@ -20,21 +23,27 @@ namespace NadekoBot.Commands { public string RepeatingMessage { get; set; } public int Interval { get; set; } - public Repeater Start() { + public Repeater Start() + { MessageTimer = new Timer { Interval = Interval }; - MessageTimer.Elapsed += async (s, e) => { + MessageTimer.Elapsed += async (s, e) => + { var ch = RepeatingChannel; var msg = RepeatingMessage; - if (ch != null && !string.IsNullOrWhiteSpace(msg)) { - try { + if (ch != null && !string.IsNullOrWhiteSpace(msg)) + { + try + { await ch.SendMessage(msg); - } catch { } + } + catch { } } }; return this; } } - internal override void Init(CommandGroupBuilder cgb) { + internal override void Init(CommandGroupBuilder cgb) + { cgb.CreateCommand(Module.Prefix + "repeat") .Description("Repeat a message every X minutes. If no parameters are specified, " + @@ -42,12 +51,14 @@ namespace NadekoBot.Commands { .Parameter("minutes", ParameterType.Optional) .Parameter("msg", ParameterType.Unparsed) .AddCheck(SimpleCheckers.ManageMessages()) - .Do(async e => { + .Do(async e => + { var minutesStr = e.GetArg("minutes"); var msg = e.GetArg("msg"); // if both null, disable - if (string.IsNullOrWhiteSpace(msg) && string.IsNullOrWhiteSpace(minutesStr)) { + if (string.IsNullOrWhiteSpace(msg) && string.IsNullOrWhiteSpace(minutesStr)) + { await e.Channel.SendMessage("Repeating disabled"); Repeater rep; if (repeaters.TryGetValue(e.Server, out rep)) @@ -55,14 +66,16 @@ namespace NadekoBot.Commands { return; } int minutes; - if (!int.TryParse(minutesStr, out minutes) || minutes < 1 || minutes > 720) { + if (!int.TryParse(minutesStr, out minutes) || minutes < 1 || minutes > 720) + { await e.Channel.SendMessage("Invalid value"); return; } var repeater = repeaters.GetOrAdd( e.Server, - s => new Repeater { + s => new Repeater + { Interval = minutes * 60 * 1000, RepeatingChannel = e.Channel, RepeatingChannelId = e.Channel.Id, @@ -82,6 +95,6 @@ namespace NadekoBot.Commands { }); } - public MessageRepeater(DiscordModule module) : base(module) {} + public MessageRepeater(DiscordModule module) : base(module) { } } } diff --git a/NadekoBot/Commands/PlayingRotate.cs b/NadekoBot/Modules/Administration/Commands/PlayingRotate.cs similarity index 85% rename from NadekoBot/Commands/PlayingRotate.cs rename to NadekoBot/Modules/Administration/Commands/PlayingRotate.cs index f2ed46e9..314ed2ea 100644 --- a/NadekoBot/Commands/PlayingRotate.cs +++ b/NadekoBot/Modules/Administration/Commands/PlayingRotate.cs @@ -1,15 +1,17 @@ -using System; +using Discord.Commands; +using NadekoBot.Classes.JSONModels; +using NadekoBot.Commands; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Discord.Commands; using System.Timers; -using NadekoBot.Classes.JSONModels; -using NadekoBot.Modules; -namespace NadekoBot.Commands { - internal class PlayingRotate : DiscordCommand { +namespace NadekoBot.Modules.Administration.Commands +{ + internal class PlayingRotate : DiscordCommand + { private static readonly Timer timer = new Timer(12000); public static Dictionary> PlayingPlaceholders { get; } = @@ -34,16 +36,21 @@ namespace NadekoBot.Commands { private readonly object playingPlaceholderLock = new object(); - public PlayingRotate(DiscordModule module) : base(module) { + public PlayingRotate(DiscordModule module) : base(module) + { var i = -1; - timer.Elapsed += (s, e) => { - try { + timer.Elapsed += (s, e) => + { + try + { i++; var status = ""; - lock (playingPlaceholderLock) { + lock (playingPlaceholderLock) + { if (PlayingPlaceholders.Count == 0) return; - if (i >= PlayingPlaceholders.Count) { + if (i >= PlayingPlaceholders.Count) + { i = -1; return; } @@ -54,14 +61,17 @@ namespace NadekoBot.Commands { if (string.IsNullOrWhiteSpace(status)) return; Task.Run(() => { NadekoBot.Client.SetGame(status); }); - } catch { } + } + catch { } }; timer.Enabled = NadekoBot.Config.IsRotatingStatus; } - public Func DoFunc() => async e => { - lock (playingPlaceholderLock) { + public Func DoFunc() => async e => + { + lock (playingPlaceholderLock) + { if (timer.Enabled) timer.Stop(); else @@ -72,7 +82,8 @@ namespace NadekoBot.Commands { await e.Channel.SendMessage($"❗`Rotating playing status has been {(timer.Enabled ? "enabled" : "disabled")}.`"); }; - internal override void Init(CommandGroupBuilder cgb) { + internal override void Init(CommandGroupBuilder cgb) + { cgb.CreateCommand(Module.Prefix + "rotateplaying") .Alias(Module.Prefix + "ropl") .Description("Toggles rotation of playing status of the dynamic strings you specified earlier.") @@ -85,11 +96,13 @@ namespace NadekoBot.Commands { "Supported placeholders: " + string.Join(", ", PlayingPlaceholders.Keys)) .Parameter("text", ParameterType.Unparsed) .AddCheck(Classes.Permissions.SimpleCheckers.OwnerOnly()) - .Do(async e => { + .Do(async e => + { var arg = e.GetArg("text"); if (string.IsNullOrWhiteSpace(arg)) return; - lock (playingPlaceholderLock) { + lock (playingPlaceholderLock) + { NadekoBot.Config.RotatingStatuses.Add(arg); ConfigHandler.SaveConfig(); } @@ -100,12 +113,14 @@ namespace NadekoBot.Commands { .Alias(Module.Prefix + "lipl") .Description("Lists all playing statuses with their corresponding number.") .AddCheck(Classes.Permissions.SimpleCheckers.OwnerOnly()) - .Do(async e => { + .Do(async e => + { if (NadekoBot.Config.RotatingStatuses.Count == 0) await e.Channel.SendMessage("`There are no playing strings. " + "Add some with .addplaying [text] command.`"); var sb = new StringBuilder(); - for (var i = 0; i < NadekoBot.Config.RotatingStatuses.Count; i++) { + for (var i = 0; i < NadekoBot.Config.RotatingStatuses.Count; i++) + { sb.AppendLine($"`{i + 1}.` {NadekoBot.Config.RotatingStatuses[i]}"); } await e.Channel.SendMessage(sb.ToString()); @@ -116,11 +131,13 @@ namespace NadekoBot.Commands { .Description("Removes a playing string on a given number.") .Parameter("number", ParameterType.Required) .AddCheck(Classes.Permissions.SimpleCheckers.OwnerOnly()) - .Do(async e => { + .Do(async e => + { var arg = e.GetArg("number"); int num; string str; - lock (playingPlaceholderLock) { + lock (playingPlaceholderLock) + { if (!int.TryParse(arg.Trim(), out num) || num <= 0 || num > NadekoBot.Config.RotatingStatuses.Count) return; str = NadekoBot.Config.RotatingStatuses[num - 1]; diff --git a/NadekoBot/Commands/RatelimitCommand.cs b/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs similarity index 78% rename from NadekoBot/Commands/RatelimitCommand.cs rename to NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs index 133878f1..61736edb 100644 --- a/NadekoBot/Commands/RatelimitCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs @@ -1,28 +1,36 @@ -using System; -using System.Collections.Concurrent; using Discord.Commands; using NadekoBot.Classes.Permissions; -using NadekoBot.Modules; +using NadekoBot.Commands; +using System; +using System.Collections.Concurrent; -namespace NadekoBot.Commands { - internal class RatelimitCommand : DiscordCommand { +namespace NadekoBot.Modules.Administration.Commands +{ + internal class RatelimitCommand : DiscordCommand + { public static ConcurrentDictionary> RatelimitingChannels = new ConcurrentDictionary>(); private static readonly TimeSpan ratelimitTime = new TimeSpan(0, 0, 0, 5); - public RatelimitCommand(DiscordModule module) : base(module) { - NadekoBot.Client.MessageReceived += async (s, e) => { + public RatelimitCommand(DiscordModule module) : base(module) + { + NadekoBot.Client.MessageReceived += async (s, e) => + { if (e.Channel.IsPrivate || e.User.Id == NadekoBot.Client.CurrentUser.Id) return; ConcurrentDictionary userTimePair; if (!RatelimitingChannels.TryGetValue(e.Channel.Id, out userTimePair)) return; DateTime lastMessageTime; - if (userTimePair.TryGetValue(e.User.Id, out lastMessageTime)) { - if (DateTime.Now - lastMessageTime < ratelimitTime) { - try { + if (userTimePair.TryGetValue(e.User.Id, out lastMessageTime)) + { + if (DateTime.Now - lastMessageTime < ratelimitTime) + { + try + { await e.Message.Delete(); - } catch { } + } + catch { } return; } } @@ -30,23 +38,27 @@ namespace NadekoBot.Commands { }; } - internal override void Init(CommandGroupBuilder cgb) { + internal override void Init(CommandGroupBuilder cgb) + { cgb.CreateCommand(Module.Prefix + "slowmode") .Description("Toggles slow mode. When ON, users will be able to send only 1 message every 5 seconds.") .Parameter("minutes", ParameterType.Optional) .AddCheck(SimpleCheckers.ManageMessages()) - .Do(async e => { + .Do(async e => + { //var minutesStr = e.GetArg("minutes"); //if (string.IsNullOrWhiteSpace(minutesStr)) { // RatelimitingChannels.Remove(e.Channel.Id); // return; //} ConcurrentDictionary throwaway; - if (RatelimitingChannels.TryRemove(e.Channel.Id, out throwaway)) { + if (RatelimitingChannels.TryRemove(e.Channel.Id, out throwaway)) + { await e.Channel.SendMessage("Slow mode disabled."); return; } - if (RatelimitingChannels.TryAdd(e.Channel.Id, new ConcurrentDictionary())) { + if (RatelimitingChannels.TryAdd(e.Channel.Id, new ConcurrentDictionary())) + { await e.Channel.SendMessage("Slow mode initiated. " + "Users can't send more than 1 message every 5 seconds."); } diff --git a/NadekoBot/Modules/Administration/Commands/Remind.cs b/NadekoBot/Modules/Administration/Commands/Remind.cs new file mode 100644 index 00000000..6b4374a5 --- /dev/null +++ b/NadekoBot/Modules/Administration/Commands/Remind.cs @@ -0,0 +1,166 @@ +using Discord; +using Discord.Commands; +using NadekoBot.Classes; +using NadekoBot.Classes._DataModels; +using NadekoBot.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Timers; + +namespace NadekoBot.Modules.Administration.Commands +{ + class Remind : DiscordCommand + { + + Regex regex = new Regex(@"^(?:(?\d)mo)?(?:(?\d)w)?(?:(?\d{1,2})d)?(?:(?\d{1,2})h)?(?:(?\d{1,2})m)?$", + RegexOptions.Compiled | RegexOptions.Multiline); + + List reminders = new List(); + + public Remind(DiscordModule module) : base(module) + { + var remList = DbHandler.Instance.GetAllRows(); + + Console.WriteLine(string.Join("\n-", remList.Select(r => r.When.ToString()))); + reminders = remList.Select(StartNewReminder).ToList(); + } + + private Timer StartNewReminder(Reminder r) + { + var now = DateTime.Now; + var twoMins = new TimeSpan(0, 2, 0); + TimeSpan time = (r.When - now) < twoMins + ? twoMins //if the time is less than 2 minutes, + : r.When - now; //it will send the message 2 minutes after start + //To account for high bot startup times + + var t = new Timer(time.TotalMilliseconds); + t.Elapsed += async (s, e) => + { + try + { + Channel ch; + if (r.IsPrivate) + { + ch = NadekoBot.Client.PrivateChannels.FirstOrDefault(c => (long)c.Id == r.ChannelId); + if (ch == null) + ch = await NadekoBot.Client.CreatePrivateChannel((ulong)r.ChannelId); + } + else + ch = NadekoBot.Client.GetServer((ulong)r.ServerId)?.GetChannel((ulong)r.ChannelId); + + if (ch == null) + return; + + await ch.SendMessage($"❗⏰**I've been told to remind you to '{r.Message}' now by <@{r.UserId}>.**⏰❗"); + } + catch (Exception ex) + { + Console.WriteLine($"Timer error! {ex}"); + } + finally + { + DbHandler.Instance.Delete(r.Id); + t.Stop(); + t.Dispose(); + } + }; + t.Start(); + return t; + } + + internal override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand(Module.Prefix + "remind") + .Parameter("meorchannel", ParameterType.Required) + .Parameter("time", ParameterType.Required) + .Parameter("message", ParameterType.Unparsed) + .Do(async e => + { + var meorchStr = e.GetArg("meorchannel").ToUpperInvariant(); + Channel ch; + bool isPrivate = false; + if (meorchStr == "ME") + { + isPrivate = true; + ch = await e.User.CreatePMChannel(); + } + else if (meorchStr == "HERE") + { + ch = e.Channel; + } + else { + ch = e.Server.FindChannels(meorchStr).FirstOrDefault(); + } + + if (ch == null) + { + await e.Channel.SendMessage($"{e.User.Mention} Something went wrong (channel cannot be found) ;("); + return; + } + + var timeStr = e.GetArg("time"); + + var m = regex.Match(timeStr); + + if (m.Length == 0) + { + await e.Channel.SendMessage("Not a valid time format blablabla"); + return; + } + + string output = ""; + var namesAndValues = new Dictionary(); + + foreach (var groupName in regex.GetGroupNames()) + { + if (groupName == "0") continue; + int value = 0; + int.TryParse(m.Groups[groupName].Value, out value); + + if (string.IsNullOrEmpty(m.Groups[groupName].Value)) + { + namesAndValues[groupName] = 0; + continue; + } + else if (value < 1 || + (groupName == "months" && value > 1) || + (groupName == "weeks" && value > 4) || + (groupName == "days" && value >= 7) || + (groupName == "hours" && value > 23) || + (groupName == "minutes" && value > 59)) + { + await e.Channel.SendMessage($"Invalid {groupName} value."); + return; + } + else + namesAndValues[groupName] = value; + output += m.Groups[groupName].Value + " " + groupName + " "; + } + var time = DateTime.Now + new TimeSpan(30 * namesAndValues["months"] + + 7 * namesAndValues["weeks"] + + namesAndValues["days"], + namesAndValues["hours"], + namesAndValues["minutes"], + 0); + + var rem = new Reminder + { + ChannelId = (long)ch.Id, + IsPrivate = isPrivate, + When = time, + Message = e.GetArg("message"), + UserId = (long)e.User.Id, + ServerId = (long)e.Server.Id + }; + DbHandler.Instance.InsertData(rem); + + reminders.Add(StartNewReminder(rem)); + + await e.Channel.SendMessage($"⏰ I will remind \"{ch.Name}\" to \"{e.GetArg("message").ToString()}\" in {output}. ({time:d.M.yyyy.} at {time:HH:m})"); + }); + } + } +} diff --git a/NadekoBot/Commands/SelfAssignedRolesCommand.cs b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs similarity index 82% rename from NadekoBot/Commands/SelfAssignedRolesCommand.cs rename to NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index ac87225a..40a31f13 100644 --- a/NadekoBot/Commands/SelfAssignedRolesCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -1,29 +1,35 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Discord.Commands; +using Discord.Commands; using NadekoBot.Classes; using NadekoBot.Classes.Permissions; -using NadekoBot.Modules; +using NadekoBot.Commands; +using System.Collections.Generic; +using System.Linq; +using System.Text; -namespace NadekoBot.Commands { - internal class SelfAssignedRolesCommand : DiscordCommand { +namespace NadekoBot.Modules.Administration.Commands +{ + internal class SelfAssignedRolesCommand : DiscordCommand + { public SelfAssignedRolesCommand(DiscordModule module) : base(module) { } - internal override void Init(CommandGroupBuilder cgb) { + internal override void Init(CommandGroupBuilder cgb) + { cgb.CreateCommand(".asar") .Description("Adds a role, or list of roles separated by whitespace" + "(use quotations for multiword roles) to the list of self-assignable roles.\n**Usage**: .asar Gamer") .Parameter("roles", ParameterType.Multiple) .AddCheck(SimpleCheckers.CanManageRoles) - .Do(async e => { + .Do(async e => + { var config = SpecificConfigurations.Default.Of(e.Server.Id); var msg = new StringBuilder(); - foreach (var arg in e.Args) { + foreach (var arg in e.Args) + { var role = e.Server.FindRoles(arg.Trim()).FirstOrDefault(); if (role == null) msg.AppendLine($":anger:Role **{arg}** not found."); else { - if (config.ListOfSelfAssignableRoles.Contains(role.Id)) { + if (config.ListOfSelfAssignableRoles.Contains(role.Id)) + { msg.AppendLine($":anger:Role **{role.Name}** is already in the list."); continue; } @@ -38,17 +44,20 @@ namespace NadekoBot.Commands { .Description("Removes a specified role from the list of self-assignable roles.") .Parameter("role", ParameterType.Unparsed) .AddCheck(SimpleCheckers.CanManageRoles) - .Do(async e => { + .Do(async e => + { var roleName = e.GetArg("role")?.Trim(); if (string.IsNullOrWhiteSpace(roleName)) return; var role = e.Server.FindRoles(roleName).FirstOrDefault(); - if (role == null) { + if (role == null) + { await e.Channel.SendMessage(":anger:That role does not exist."); return; } var config = SpecificConfigurations.Default.Of(e.Server.Id); - if (!config.ListOfSelfAssignableRoles.Contains(role.Id)) { + if (!config.ListOfSelfAssignableRoles.Contains(role.Id)) + { await e.Channel.SendMessage(":anger:That role is not self-assignable."); return; } @@ -59,20 +68,25 @@ namespace NadekoBot.Commands { cgb.CreateCommand(".lsar") .Description("Lits all self-assignable roles.") .Parameter("roles", ParameterType.Multiple) - .Do(async e => { + .Do(async e => + { var config = SpecificConfigurations.Default.Of(e.Server.Id); var msg = new StringBuilder($"There are `{config.ListOfSelfAssignableRoles.Count}` self assignable roles:\n"); var toRemove = new HashSet(); - foreach (var roleId in config.ListOfSelfAssignableRoles) { + foreach (var roleId in config.ListOfSelfAssignableRoles) + { var role = e.Server.GetRole(roleId); - if (role == null) { + if (role == null) + { msg.Append($"`{roleId} not found. Cleaned up.`, "); toRemove.Add(roleId); - } else { + } + else { msg.Append($"**{role.Name}**, "); } } - foreach (var id in toRemove) { + foreach (var id in toRemove) + { config.ListOfSelfAssignableRoles.Remove(id); } await e.Channel.SendMessage(msg.ToString()); @@ -83,21 +97,25 @@ namespace NadekoBot.Commands { "Role must be on a list of self-assignable roles." + "\n**Usage**: .iam Gamer") .Parameter("role", ParameterType.Unparsed) - .Do(async e => { + .Do(async e => + { var roleName = e.GetArg("role")?.Trim(); if (string.IsNullOrWhiteSpace(roleName)) return; var role = e.Server.FindRoles(roleName).FirstOrDefault(); - if (role == null) { + if (role == null) + { await e.Channel.SendMessage(":anger:That role does not exist."); return; } var config = SpecificConfigurations.Default.Of(e.Server.Id); - if (!config.ListOfSelfAssignableRoles.Contains(role.Id)) { + if (!config.ListOfSelfAssignableRoles.Contains(role.Id)) + { await e.Channel.SendMessage(":anger:That role is not self-assignable."); return; } - if (e.User.HasRole(role)) { + if (e.User.HasRole(role)) + { await e.Channel.SendMessage($":anger:You already have {role.Name} role."); return; } @@ -111,21 +129,25 @@ namespace NadekoBot.Commands { "Role must be on a list of self-assignable roles." + "\n**Usage**: .iamn Gamer") .Parameter("role", ParameterType.Unparsed) - .Do(async e => { + .Do(async e => + { var roleName = e.GetArg("role")?.Trim(); if (string.IsNullOrWhiteSpace(roleName)) return; var role = e.Server.FindRoles(roleName).FirstOrDefault(); - if (role == null) { + if (role == null) + { await e.Channel.SendMessage(":anger:That role does not exist."); return; } var config = SpecificConfigurations.Default.Of(e.Server.Id); - if (!config.ListOfSelfAssignableRoles.Contains(role.Id)) { + if (!config.ListOfSelfAssignableRoles.Contains(role.Id)) + { await e.Channel.SendMessage(":anger:That role is not self-assignable."); return; } - if (!e.User.HasRole(role)) { + if (!e.User.HasRole(role)) + { await e.Channel.SendMessage($":anger:You don't have {role.Name} role."); return; } diff --git a/NadekoBot/Commands/ServerGreetCommand.cs b/NadekoBot/Modules/Administration/Commands/ServerGreetCommand.cs similarity index 89% rename from NadekoBot/Commands/ServerGreetCommand.cs rename to NadekoBot/Modules/Administration/Commands/ServerGreetCommand.cs index c404aef0..7af7a72b 100644 --- a/NadekoBot/Commands/ServerGreetCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/ServerGreetCommand.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using Discord; using Discord.Commands; -using System.Collections.Concurrent; +using NadekoBot.Commands; using NadekoBot.Extensions; -using Discord; -using NadekoBot.Modules; +using System.Collections.Concurrent; +using System.Linq; /* Voltana's legacy public class AsyncLazy : Lazy> @@ -21,14 +18,17 @@ public class AsyncLazy : Lazy> } */ -namespace NadekoBot.Commands { - internal class ServerGreetCommand : DiscordCommand { +namespace NadekoBot.Modules.Administration.Commands +{ + internal class ServerGreetCommand : DiscordCommand + { public static ConcurrentDictionary AnnouncementsDictionary; public static long Greeted = 0; - public ServerGreetCommand(DiscordModule module) : base(module) { + public ServerGreetCommand(DiscordModule module) : base(module) + { AnnouncementsDictionary = new ConcurrentDictionary(); NadekoBot.Client.UserJoined += UserJoined; @@ -41,8 +41,10 @@ namespace NadekoBot.Commands { AnnouncementsDictionary.TryAdd((ulong)obj.ServerId, new AnnounceControls(obj)); } - private async void UserLeft(object sender, UserEventArgs e) { - try { + private async void UserLeft(object sender, UserEventArgs e) + { + try + { if (!AnnouncementsDictionary.ContainsKey(e.Server.Id) || !AnnouncementsDictionary[e.Server.Id].Bye) return; @@ -52,9 +54,11 @@ namespace NadekoBot.Commands { if (string.IsNullOrEmpty(msg)) return; - if (controls.ByePM) { + if (controls.ByePM) + { Greeted++; - try { + try + { await e.User.SendMessage($"`Farewell Message From {e.Server?.Name}`\n" + msg); } catch { } @@ -68,8 +72,10 @@ namespace NadekoBot.Commands { catch { } } - private async void UserJoined(object sender, Discord.UserEventArgs e) { - try { + private async void UserJoined(object sender, Discord.UserEventArgs e) + { + try + { if (!AnnouncementsDictionary.ContainsKey(e.Server.Id) || !AnnouncementsDictionary[e.Server.Id].Greet) return; @@ -79,7 +85,8 @@ namespace NadekoBot.Commands { var msg = controls.GreetText.Replace("%user%", e.User.Mention).Trim(); if (string.IsNullOrEmpty(msg)) return; - if (controls.GreetPM) { + if (controls.GreetPM) + { Greeted++; await e.User.SendMessage($"`Welcome Message From {e.Server.Name}`\n" + msg); } @@ -92,7 +99,8 @@ namespace NadekoBot.Commands { catch { } } - public class AnnounceControls { + public class AnnounceControls + { private Classes._DataModels.Announcement _model { get; } public bool Greet { @@ -139,28 +147,36 @@ namespace NadekoBot.Commands { set { _model.ServerId = (long)value; } } - public AnnounceControls(Classes._DataModels.Announcement model) { + public AnnounceControls(Classes._DataModels.Announcement model) + { this._model = model; } - public AnnounceControls(ulong serverId) { + public AnnounceControls(ulong serverId) + { this._model = new Classes._DataModels.Announcement(); ServerId = serverId; } - internal bool ToggleBye(ulong id) { - if (Bye) { + internal bool ToggleBye(ulong id) + { + if (Bye) + { return Bye = false; - } else { + } + else { ByeChannel = id; return Bye = true; } } - internal bool ToggleGreet(ulong id) { - if (Greet) { + internal bool ToggleGreet(ulong id) + { + if (Greet) + { return Greet = false; - } else { + } + else { GreetChannel = id; return Greet = true; } @@ -168,16 +184,19 @@ namespace NadekoBot.Commands { internal bool ToggleGreetPM() => GreetPM = !GreetPM; internal bool ToggleByePM() => ByePM = !ByePM; - private void Save() { + private void Save() + { Classes.DbHandler.Instance.Save(_model); } } - internal override void Init(CommandGroupBuilder cgb) { + internal override void Init(CommandGroupBuilder cgb) + { cgb.CreateCommand(Module.Prefix + "greet") .Description("Enables or Disables anouncements on the current channel when someone joins the server.") - .Do(async e => { + .Do(async e => + { if (!e.User.ServerPermissions.ManageServer) return; if (!AnnouncementsDictionary.ContainsKey(e.Server.Id)) AnnouncementsDictionary.TryAdd(e.Server.Id, new AnnounceControls(e.Server.Id)); @@ -193,7 +212,8 @@ namespace NadekoBot.Commands { cgb.CreateCommand(Module.Prefix + "greetmsg") .Description("Sets a new announce message. Type %user% if you want to mention the new member.\n**Usage**: .greetmsg Welcome to the server, %user%.") .Parameter("msg", ParameterType.Unparsed) - .Do(async e => { + .Do(async e => + { if (!e.User.ServerPermissions.ManageServer) return; if (e.GetArg("msg") == null) return; if (!AnnouncementsDictionary.ContainsKey(e.Server.Id)) @@ -207,7 +227,8 @@ namespace NadekoBot.Commands { cgb.CreateCommand(Module.Prefix + "bye") .Description("Enables or Disables anouncements on the current channel when someone leaves the server.") - .Do(async e => { + .Do(async e => + { if (!e.User.ServerPermissions.ManageServer) return; if (!AnnouncementsDictionary.ContainsKey(e.Server.Id)) AnnouncementsDictionary.TryAdd(e.Server.Id, new AnnounceControls(e.Server.Id)); @@ -223,7 +244,8 @@ namespace NadekoBot.Commands { cgb.CreateCommand(Module.Prefix + "byemsg") .Description("Sets a new announce leave message. Type %user% if you want to mention the new member.\n**Usage**: .byemsg %user% has left the server.") .Parameter("msg", ParameterType.Unparsed) - .Do(async e => { + .Do(async e => + { if (!e.User.ServerPermissions.ManageServer) return; if (e.GetArg("msg") == null) return; if (!AnnouncementsDictionary.ContainsKey(e.Server.Id)) @@ -237,7 +259,8 @@ namespace NadekoBot.Commands { cgb.CreateCommand(Module.Prefix + "byepm") .Description("Toggles whether the good bye messages will be sent in a PM or in the text channel.") - .Do(async e => { + .Do(async e => + { if (!e.User.ServerPermissions.ManageServer) return; if (!AnnouncementsDictionary.ContainsKey(e.Server.Id)) AnnouncementsDictionary.TryAdd(e.Server.Id, new AnnounceControls(e.Server.Id)); @@ -253,7 +276,8 @@ namespace NadekoBot.Commands { cgb.CreateCommand(Module.Prefix + "greetpm") .Description("Toggles whether the greet messages will be sent in a PM or in the text channel.") - .Do(async e => { + .Do(async e => + { if (!e.User.ServerPermissions.ManageServer) return; if (!AnnouncementsDictionary.ContainsKey(e.Server.Id)) AnnouncementsDictionary.TryAdd(e.Server.Id, new AnnounceControls(e.Server.Id)); diff --git a/NadekoBot/Commands/VoiceNotificationCommand.cs b/NadekoBot/Modules/Administration/Commands/VoiceNotificationCommand.cs similarity index 75% rename from NadekoBot/Commands/VoiceNotificationCommand.cs rename to NadekoBot/Modules/Administration/Commands/VoiceNotificationCommand.cs index ecf15957..e6bd0529 100644 --- a/NadekoBot/Commands/VoiceNotificationCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/VoiceNotificationCommand.cs @@ -1,34 +1,40 @@ -using System; +using Discord; +using Discord.Commands; +using NadekoBot.Commands; +using System; +using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; -using Discord.Commands; -using System.Collections.Concurrent; -using Discord; -using NadekoBot.Modules; -namespace NadekoBot.Commands { - internal class VoiceNotificationCommand : DiscordCommand { +namespace NadekoBot.Modules.Administration.Commands +{ + internal class VoiceNotificationCommand : DiscordCommand + { //voicechannel/text channel private readonly ConcurrentDictionary subscribers = new ConcurrentDictionary(); - public Func DoFunc() => async e => { + public Func DoFunc() => async e => + { var arg = e.GetArg("voice_name"); if (string.IsNullOrWhiteSpace("voice_name")) return; var voiceChannel = e.Server.FindChannels(arg, ChannelType.Voice).FirstOrDefault(); if (voiceChannel == null) return; - if (subscribers.ContainsKey(voiceChannel)) { + if (subscribers.ContainsKey(voiceChannel)) + { await e.Channel.SendMessage("`Voice channel notifications disabled.`"); return; } - if (subscribers.TryAdd(voiceChannel, e.Channel)) { + if (subscribers.TryAdd(voiceChannel, e.Channel)) + { await e.Channel.SendMessage("`Voice channel notifications enabled.`"); } }; - internal override void Init(CommandGroupBuilder cgb) { + internal override void Init(CommandGroupBuilder cgb) + { cgb.CreateCommand(Module.Prefix + "voicenotif") .Description("Enables notifications on who joined/left the voice channel.\n**Usage**:.voicenotif Karaoke club") .Parameter("voice_name", ParameterType.Unparsed) diff --git a/NadekoBot/Commands/VoicePlusTextCommand.cs b/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs similarity index 79% rename from NadekoBot/Commands/VoicePlusTextCommand.cs rename to NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs index 76e33b0f..78f88844 100644 --- a/NadekoBot/Commands/VoicePlusTextCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs @@ -1,20 +1,24 @@ -using System; -using System.Linq; -using System.Runtime.CompilerServices; -using Discord; +using Discord; using Discord.Commands; using NadekoBot.Classes; using NadekoBot.Classes.Permissions; -using NadekoBot.Modules; +using NadekoBot.Commands; +using System; +using System.Linq; using ChPermOverride = Discord.ChannelPermissionOverrides; -namespace NadekoBot.Commands { - internal class VoicePlusTextCommand : DiscordCommand { +namespace NadekoBot.Modules.Administration.Commands +{ + internal class VoicePlusTextCommand : DiscordCommand + { - public VoicePlusTextCommand(DiscordModule module) : base(module) { + public VoicePlusTextCommand(DiscordModule module) : base(module) + { // changing servers may cause bugs - NadekoBot.Client.UserUpdated += async (sender, e) => { - try { + NadekoBot.Client.UserUpdated += async (sender, e) => + { + try + { if (e.Server == null) return; var config = SpecificConfigurations.Default.Of(e.Server.Id); @@ -24,20 +28,24 @@ namespace NadekoBot.Commands { var serverPerms = e.Server.GetUser(NadekoBot.Client.CurrentUser.Id)?.ServerPermissions; if (serverPerms == null) return; - if (!serverPerms.Value.ManageChannels || !serverPerms.Value.ManageRoles) { + if (!serverPerms.Value.ManageChannels || !serverPerms.Value.ManageRoles) + { - try { + try + { await e.Server.Owner.SendMessage( "I don't have manage server and/or Manage Channels permission," + $" so I cannot run voice+text on **{e.Server.Name}** server."); - } catch { } // meh + } + catch { } // meh config.VoicePlusTextEnabled = false; return; } var beforeVch = e.Before.VoiceChannel; - if (beforeVch != null) { + if (beforeVch != null) + { var textChannel = e.Server.FindChannels(GetChannelName(beforeVch.Name), ChannelType.Text).FirstOrDefault(); if (textChannel != null) @@ -46,12 +54,14 @@ namespace NadekoBot.Commands { sendMessages: PermValue.Deny)); } var afterVch = e.After.VoiceChannel; - if (afterVch != null) { + if (afterVch != null) + { var textChannel = e.Server.FindChannels( GetChannelName(afterVch.Name), ChannelType.Text) .FirstOrDefault(); - if (textChannel == null) { + if (textChannel == null) + { textChannel = (await e.Server.CreateChannel(GetChannelName(afterVch.Name), ChannelType.Text)); await textChannel.AddPermissionsRule(e.Server.EveryoneRole, new ChPermOverride(readMessages: PermValue.Deny, @@ -61,7 +71,9 @@ namespace NadekoBot.Commands { new ChPermOverride(readMessages: PermValue.Allow, sendMessages: PermValue.Allow)); } - } catch (Exception ex) { + } + catch (Exception ex) + { Console.WriteLine(ex); } }; @@ -70,22 +82,30 @@ namespace NadekoBot.Commands { private string GetChannelName(string voiceName) => voiceName.Replace(" ", "-").Trim() + "-voice"; - internal override void Init(CommandGroupBuilder cgb) { + internal override void Init(CommandGroupBuilder cgb) + { cgb.CreateCommand(Module.Prefix + "v+t") .Alias(Module.Prefix + "voice+text") .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.") .AddCheck(SimpleCheckers.ManageChannels()) .AddCheck(SimpleCheckers.CanManageRoles) - .Do(async e => { - try { + .Do(async e => + { + try + { var config = SpecificConfigurations.Default.Of(e.Server.Id); - if (config.VoicePlusTextEnabled == true) { + if (config.VoicePlusTextEnabled == true) + { config.VoicePlusTextEnabled = false; - foreach (var textChannel in e.Server.TextChannels.Where(c => c.Name.EndsWith("-voice"))) { - try { + foreach (var textChannel in e.Server.TextChannels.Where(c => c.Name.EndsWith("-voice"))) + { + try + { await textChannel.Delete(); - } catch { + } + catch + { await e.Channel.SendMessage( ":anger: Error: Most likely i don't have permissions to do this."); @@ -99,7 +119,9 @@ namespace NadekoBot.Commands { await e.Channel.SendMessage("Successfuly enabled voice + text feature. " + "**Make sure the bot has manage roles and manage channels permissions**"); - } catch (Exception ex) { + } + catch (Exception ex) + { await e.Channel.SendMessage(ex.ToString()); } }); diff --git a/NadekoBot/Modules/ClashOfClans.cs b/NadekoBot/Modules/ClashOfClans.cs index 17264553..2bc9ab42 100644 --- a/NadekoBot/Modules/ClashOfClans.cs +++ b/NadekoBot/Modules/ClashOfClans.cs @@ -1,16 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Discord.Commands; -using System.Collections.Concurrent; -using System.Linq; -using System.Threading; +using Discord.Commands; using Discord.Modules; using NadekoBot.Classes.ClashOfClans; using NadekoBot.Modules; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; -namespace NadekoBot.Commands { +namespace NadekoBot.Commands +{ internal class ClashOfClans : DiscordModule { public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.ClashOfClans; @@ -19,8 +17,10 @@ namespace NadekoBot.Commands { private readonly object writeLock = new object(); - public override void Install(ModuleManager manager) { - manager.CreateCommands("", cgb => { + public override void Install(ModuleManager manager) + { + manager.CreateCommands("", cgb => + { cgb.CreateCommand(Prefix + "createwar") .Alias(Prefix + "cw") @@ -28,38 +28,48 @@ namespace NadekoBot.Commands { $"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name.\n**Usage**:{Prefix}cw 15 The Enemy Clan") .Parameter("size") .Parameter("enemy_clan", ParameterType.Unparsed) - .Do(async e => { + .Do(async e => + { if (!e.User.ServerPermissions.ManageChannels) return; List wars; - if (!ClashWars.TryGetValue(e.Server.Id, out wars)) { + if (!ClashWars.TryGetValue(e.Server.Id, out wars)) + { wars = new List(); if (!ClashWars.TryAdd(e.Server.Id, wars)) return; } var enemyClan = e.GetArg("enemy_clan"); - if (string.IsNullOrWhiteSpace(enemyClan)) { + if (string.IsNullOrWhiteSpace(enemyClan)) + { return; } int size; - if (!int.TryParse(e.GetArg("size"), out size) || size < 10 || size > 50 || size % 5 != 0) { + if (!int.TryParse(e.GetArg("size"), out size) || size < 10 || size > 50 || size % 5 != 0) + { await e.Channel.SendMessage("💢🔰 Not a Valid war size"); return; } var cw = new ClashWar(enemyClan, size, e); //cw.Start(); wars.Add(cw); - cw.OnUserTimeExpired += async (u) => { - try { + cw.OnUserTimeExpired += async (u) => + { + try + { await e.Channel.SendMessage( $"❗🔰**Claim from @{u} for a war against {cw.ShortPrint()} has expired.**"); - } catch { } + } + catch { } }; - cw.OnWarEnded += async () => { - try { + cw.OnWarEnded += async () => + { + try + { await e.Channel.SendMessage($"❗🔰**War against {cw.ShortPrint()} ended.**"); - } catch { } + } + catch { } }; await e.Channel.SendMessage($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**"); //war with the index X started. @@ -69,18 +79,23 @@ namespace NadekoBot.Commands { .Alias(Prefix + "startwar") .Description("Starts a war with a given number.") .Parameter("number", ParameterType.Required) - .Do(async e => { + .Do(async e => + { var warsInfo = GetInfo(e); - if (warsInfo == null) { + if (warsInfo == null) + { await e.Channel.SendMessage("💢🔰 **That war does not exist.**"); return; } var war = warsInfo.Item1[warsInfo.Item2]; - try { + try + { var startTask = war.Start(); await e.Channel.SendMessage($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**"); await startTask; - } catch { + } + catch + { await e.Channel.SendMessage($"🔰**WAR AGAINST {war.ShortPrint()} IS ALREADY STARTED**"); } }); @@ -89,13 +104,16 @@ namespace NadekoBot.Commands { .Alias(Prefix + "lw") .Description($"Shows the active war claims by a number. Shows all wars in a short way if no number is specified.\n**Usage**: {Prefix}lw [war_number] or {Prefix}lw") .Parameter("number", ParameterType.Optional) - .Do(async e => { + .Do(async e => + { // if number is null, print all wars in a short way - if (string.IsNullOrWhiteSpace(e.GetArg("number"))) { + if (string.IsNullOrWhiteSpace(e.GetArg("number"))) + { //check if there are any wars List wars = null; ClashWars.TryGetValue(e.Server.Id, out wars); - if (wars == null || wars.Count == 0) { + if (wars == null || wars.Count == 0) + { await e.Channel.SendMessage("🔰 **No active wars.**"); return; } @@ -103,7 +121,8 @@ namespace NadekoBot.Commands { var sb = new StringBuilder(); sb.AppendLine("🔰 **LIST OF ACTIVE WARS**"); sb.AppendLine("**-------------------------**"); - for (var i = 0; i < wars.Count; i++) { + for (var i = 0; i < wars.Count; i++) + { sb.AppendLine($"**#{i + 1}.** `Enemy:` **{wars[i].EnemyClan}**"); sb.AppendLine($"\t\t`Size:` **{wars[i].Size} v {wars[i].Size}**"); sb.AppendLine("**-------------------------**"); @@ -113,7 +132,8 @@ namespace NadekoBot.Commands { } //if number is not null, print the war needed var warsInfo = GetInfo(e); - if (warsInfo == null) { + if (warsInfo == null) + { await e.Channel.SendMessage("💢🔰 **That war does not exist.**"); return; } @@ -127,14 +147,17 @@ namespace NadekoBot.Commands { .Parameter("number") .Parameter("baseNumber") .Parameter("other_name", ParameterType.Unparsed) - .Do(async e => { + .Do(async e => + { var warsInfo = GetInfo(e); - if (warsInfo == null || warsInfo.Item1.Count == 0) { + if (warsInfo == null || warsInfo.Item1.Count == 0) + { await e.Channel.SendMessage("💢🔰 **That war does not exist.**"); return; } int baseNum; - if (!int.TryParse(e.GetArg("baseNumber"), out baseNum)) { + if (!int.TryParse(e.GetArg("baseNumber"), out baseNum)) + { await e.Channel.SendMessage("💢🔰 **Invalid base number.**"); return; } @@ -142,11 +165,14 @@ namespace NadekoBot.Commands { string.IsNullOrWhiteSpace(e.GetArg("other_name")) ? e.User.Name : e.GetArg("other_name"); - try { + try + { var war = warsInfo.Item1[warsInfo.Item2]; war.Call(usr, baseNum - 1); await e.Channel.SendMessage($"🔰**{usr}** claimed a base #{baseNum} for a war against {war.ShortPrint()}"); - } catch (Exception ex) { + } + catch (Exception ex) + { await e.Channel.SendMessage($"💢🔰 {ex.Message}"); } }); @@ -156,9 +182,11 @@ namespace NadekoBot.Commands { .Description($"Finish your claim if you destroyed a base. Optional second argument finishes for someone else.\n**Usage**: {Prefix}cf [war_number] [optional_other_name]") .Parameter("number", ParameterType.Required) .Parameter("other_name", ParameterType.Unparsed) - .Do(async e => { + .Do(async e => + { var warInfo = GetInfo(e); - if (warInfo == null || warInfo.Item1.Count == 0) { + if (warInfo == null || warInfo.Item1.Count == 0) + { await e.Channel.SendMessage("💢🔰 **That war does not exist.**"); return; } @@ -168,10 +196,13 @@ namespace NadekoBot.Commands { e.GetArg("other_name"); var war = warInfo.Item1[warInfo.Item2]; - try { + try + { var baseNum = war.FinishClaim(usr); await e.Channel.SendMessage($"❗🔰{e.User.Mention} **DESTROYED** a base #{baseNum + 1} in a war against {war.ShortPrint()}"); - } catch (Exception ex) { + } + catch (Exception ex) + { await e.Channel.SendMessage($"💢🔰 {ex.Message}"); } }); @@ -182,9 +213,11 @@ namespace NadekoBot.Commands { .Description($"Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim\n**Usage**: {Prefix}uc [war_number] [optional_other_name]") .Parameter("number", ParameterType.Required) .Parameter("other_name", ParameterType.Unparsed) - .Do(async e => { + .Do(async e => + { var warsInfo = GetInfo(e); - if (warsInfo == null || warsInfo.Item1.Count == 0) { + if (warsInfo == null || warsInfo.Item1.Count == 0) + { await e.Channel.SendMessage("💢🔰 **That war does not exist.**"); return; } @@ -192,11 +225,14 @@ namespace NadekoBot.Commands { string.IsNullOrWhiteSpace(e.GetArg("other_name")) ? e.User.Name : e.GetArg("other_name"); - try { + try + { var war = warsInfo.Item1[warsInfo.Item2]; var baseNumber = war.Uncall(usr); await e.Channel.SendMessage($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}"); - } catch (Exception ex) { + } + catch (Exception ex) + { await e.Channel.SendMessage($"💢🔰 {ex.Message}"); } }); @@ -205,9 +241,11 @@ namespace NadekoBot.Commands { .Alias(Prefix + "ew") .Description($"Ends the war with a given index.\n**Usage**:{Prefix}ew [war_number]") .Parameter("number") - .Do(async e => { + .Do(async e => + { var warsInfo = GetInfo(e); - if (warsInfo == null) { + if (warsInfo == null) + { await e.Channel.SendMessage("💢🔰 That war does not exist."); return; } @@ -219,18 +257,21 @@ namespace NadekoBot.Commands { }); } - private static Tuple, int> GetInfo(CommandEventArgs e) { + private static Tuple, int> GetInfo(CommandEventArgs e) + { //check if there are any wars List wars = null; ClashWars.TryGetValue(e.Server.Id, out wars); - if (wars == null || wars.Count == 0) { + if (wars == null || wars.Count == 0) + { return null; } // get the number of the war int num; if (string.IsNullOrWhiteSpace(e.GetArg("number"))) num = 0; - else if (!int.TryParse(e.GetArg("number"), out num) || num > wars.Count) { + else if (!int.TryParse(e.GetArg("number"), out num) || num > wars.Count) + { return null; } num -= 1; diff --git a/NadekoBot/Modules/Conversations.cs b/NadekoBot/Modules/Conversations.cs index c3f8ecf1..2ad59ee1 100644 --- a/NadekoBot/Modules/Conversations.cs +++ b/NadekoBot/Modules/Conversations.cs @@ -159,6 +159,7 @@ namespace NadekoBot.Modules }); cgb.CreateCommand("how are you") + .Alias("how are you?") .Description("Replies positive only if bot owner is online.") .Do(async e => { diff --git a/NadekoBot/Modules/Gambling/DiceRollCommand.cs b/NadekoBot/Modules/Gambling/DiceRollCommand.cs index 20e3dfbe..1719bd0e 100644 --- a/NadekoBot/Modules/Gambling/DiceRollCommand.cs +++ b/NadekoBot/Modules/Gambling/DiceRollCommand.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Gambling internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "roll") - .Description("Rolls 2 dice from 0-10. If you supply a number [x] it rolls up to 30 normal dice.\n**Usage**: $roll [x]") + .Description("Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice.\n**Usage**: $roll [x]") .Parameter("num", ParameterType.Optional) .Do(Roll0to10Func()); cgb.CreateCommand(Module.Prefix + "nroll") @@ -28,7 +28,13 @@ namespace NadekoBot.Modules.Gambling .Do(Roll0to5Func()); } - private Image GetDice(int num) => Properties.Resources.ResourceManager.GetObject("_" + num) as Image; + private Image GetDice(int num) => num != 10 + ? Properties.Resources.ResourceManager.GetObject("_" + num) as Image + : new[] + { + (Properties.Resources.ResourceManager.GetObject("_" + 1) as Image), + (Properties.Resources.ResourceManager.GetObject("_" + 0) as Image), + }.Merge(); private Func Roll0to10Func() { @@ -37,30 +43,24 @@ namespace NadekoBot.Modules.Gambling { if (e.Args[0] == "") { - var num1 = r.Next(0, 10); - var num2 = r.Next(0, 10); + var gen = r.Next(0, 101); - Image[] images; + var num1 = gen / 10; + var num2 = gen % 10; - if (num1 == 0 && num2 == 0 && r.Next(0, 2) == 1) - { - images = new Image[3] { GetDice(1), GetDice(0), GetDice(0) }; - } - else { - images = new Image[2] { GetDice(num1), GetDice(num2) }; - } + var imageStream = new Image[2] { GetDice(num1), GetDice(num2) }.Merge().ToStream(ImageFormat.Png); - var bitmap = images.Merge(); - await e.Channel.SendFile("dice.png", bitmap.ToStream(ImageFormat.Png)); + await e.Channel.SendFile("dice.png", imageStream); } - else { + else + { try { var num = int.Parse(e.Args[0]); if (num < 1) num = 1; if (num > 30) { - await e.Channel.SendMessage("You can roll up to 30 dies at a time."); + await e.Channel.SendMessage("You can roll up to 30 dice at a time."); num = 30; } var dices = new List(num); @@ -113,7 +113,8 @@ namespace NadekoBot.Modules.Gambling throw new ArgumentException("First argument should be bigger than the second one."); rolled = new Random().Next(arr[0], arr[1] + 1); } - else { + else + { rolled = new Random().Next(0, int.Parse(e.GetArg("range")) + 1); } diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index ee1661d3..8fe25f16 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -33,9 +33,11 @@ namespace NadekoBot.Modules.Gambling .Description("Prints a name and ID of a random user from the online list from the (optional) role.") .Parameter("role", ParameterType.Optional) .Do(RaffleFunc()); + cgb.CreateCommand(Prefix + "$$") .Description(string.Format("Check how much {0}s you have.", NadekoBot.Config.CurrencyName)) .Do(NadekoFlowerCheckFunc()); + cgb.CreateCommand(Prefix + "give") .Description(string.Format("Give someone a certain amount of {0}s", NadekoBot.Config.CurrencyName)) .Parameter("amount", ParameterType.Required) @@ -61,12 +63,56 @@ namespace NadekoBot.Modules.Gambling return; } - await FlowersHandler.RemoveFlowersAsync(e.User, "Gift", (int)amount); + FlowersHandler.RemoveFlowers(e.User, "Gift", (int)amount); await FlowersHandler.AddFlowersAsync(mentionedUser, "Gift", (int)amount); await e.Channel.SendMessage($"{e.User.Mention} successfully sent {amount} {NadekoBot.Config.CurrencyName}s to {mentionedUser.Mention}!"); }); + + cgb.CreateCommand(Prefix + "award") + .Description("Gives someone a certain amount of flowers. **Owner only!**") + .AddCheck(Classes.Permissions.SimpleCheckers.OwnerOnly()) + .Parameter("amount", ParameterType.Required) + .Parameter("receiver", ParameterType.Unparsed) + .Do(async e => + { + var amountStr = e.GetArg("amount")?.Trim(); + long amount; + if (!long.TryParse(amountStr, out amount) || amount < 0) + return; + + var mentionedUser = e.Message.MentionedUsers.FirstOrDefault(u => + u.Id != NadekoBot.Client.CurrentUser.Id); + if (mentionedUser == null) + return; + + await FlowersHandler.AddFlowersAsync(mentionedUser, $"Awarded by bot owner. ({e.User.Name}/{e.User.Id})", (int)amount); + + await e.Channel.SendMessage($"{e.User.Mention} successfully awarded {amount} {NadekoBot.Config.CurrencyName}s to {mentionedUser.Mention}!"); + }); + + cgb.CreateCommand(Prefix + "take") + .Description("Takes a certain amount of flowers from someone. **Owner only!**") + .AddCheck(Classes.Permissions.SimpleCheckers.OwnerOnly()) + .Parameter("amount", ParameterType.Required) + .Parameter("rektperson", ParameterType.Unparsed) + .Do(async e => + { + var amountStr = e.GetArg("amount")?.Trim(); + long amount; + if (!long.TryParse(amountStr, out amount) || amount < 0) + return; + + var mentionedUser = e.Message.MentionedUsers.FirstOrDefault(u => + u.Id != NadekoBot.Client.CurrentUser.Id); + if (mentionedUser == null) + return; + + FlowersHandler.RemoveFlowers(mentionedUser, $"Taken by bot owner.({e.User.Name}/{e.User.Id})", (int)amount); + + await e.Channel.SendMessage($"{e.User.Mention} successfully took {amount} {NadekoBot.Config.CurrencyName}s from {mentionedUser.Mention}!"); + }); }); } diff --git a/NadekoBot/Modules/Games.cs b/NadekoBot/Modules/Games.cs index 9321859a..6d2e39c7 100644 --- a/NadekoBot/Modules/Games.cs +++ b/NadekoBot/Modules/Games.cs @@ -1,28 +1,32 @@ -using System; -using System.Linq; +using Discord.Commands; using Discord.Modules; using NadekoBot.Commands; -using Newtonsoft.Json.Linq; -using System.IO; -using Discord.Commands; using NadekoBot.Extensions; +using System; +using System.Linq; -namespace NadekoBot.Modules { - internal class Games : DiscordModule { +namespace NadekoBot.Modules +{ + internal class Games : DiscordModule + { private readonly Random rng = new Random(); - public Games() { + public Games() + { commands.Add(new Trivia(this)); commands.Add(new SpeedTyping(this)); commands.Add(new PollCommand(this)); + commands.Add(new PlantPick(this)); //commands.Add(new BetrayGame(this)); - + } public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Games; - public override void Install(ModuleManager manager) { - manager.CreateCommands("", cgb => { + public override void Install(ModuleManager manager) + { + manager.CreateCommands("", cgb => + { cgb.AddCheck(Classes.Permissions.PermissionChecker.Instance); @@ -30,8 +34,9 @@ namespace NadekoBot.Modules { cgb.CreateCommand(Prefix + "choose") .Description("Chooses a thing from a list of things\n**Usage**: >choose Get up;Sleep;Sleep more") - .Parameter("list", Discord.Commands.ParameterType.Unparsed) - .Do(async e => { + .Parameter("list", ParameterType.Unparsed) + .Do(async e => + { var arg = e.GetArg("list"); if (string.IsNullOrWhiteSpace(arg)) return; @@ -43,24 +48,29 @@ namespace NadekoBot.Modules { cgb.CreateCommand(Prefix + "8ball") .Description("Ask the 8ball a yes/no question.") - .Parameter("question", Discord.Commands.ParameterType.Unparsed) - .Do(async e => { + .Parameter("question", ParameterType.Unparsed) + .Do(async e => + { var question = e.GetArg("question"); if (string.IsNullOrWhiteSpace(question)) return; - try { + try + { await e.Channel.SendMessage( $":question: **Question**: `{question}` \n🎱 **8Ball Answers**: `{NadekoBot.Config._8BallResponses[rng.Next(0, NadekoBot.Config._8BallResponses.Length)]}`"); - } catch { } + } + catch { } }); cgb.CreateCommand(Prefix + "rps") - .Description("Play a game of rocket paperclip scissors with nadkeo.\n**Usage**: >rps scissors") + .Description("Play a game of rocket paperclip scissors with Nadeko.\n**Usage**: >rps scissors") .Parameter("input", ParameterType.Required) - .Do(async e => { + .Do(async e => + { var input = e.GetArg("input").Trim(); int pick; - switch (input) { + switch (input) + { case "r": case "rock": case "rocket": @@ -96,7 +106,8 @@ namespace NadekoBot.Modules { .Description("Prints a customizable Linux interjection") .Parameter("gnu", ParameterType.Required) .Parameter("linux", ParameterType.Required) - .Do(async e => { + .Do(async e => + { var guhnoo = e.Args[0]; var loonix = e.Args[1]; @@ -112,7 +123,8 @@ There really is a {loonix}, and these people are using it, but it is just a part }); } - private string GetRPSPick(int i) { + private string GetRPSPick(int i) + { if (i == 0) return "rocket"; else if (i == 1) diff --git a/NadekoBot/Modules/Music.cs b/NadekoBot/Modules/Music.cs index 32c9f1c9..7b50a3a2 100644 --- a/NadekoBot/Modules/Music.cs +++ b/NadekoBot/Modules/Music.cs @@ -70,6 +70,7 @@ namespace NadekoBot.Modules cgb.CreateCommand("n") .Alias("next") + .Alias("skip") .Description("Goes to the next song in the queue.") .Do(e => { @@ -138,7 +139,15 @@ namespace NadekoBot.Modules await e.Channel.SendMessage("🎵 No active music player."); return; } - var toSend = "🎵 **" + musicPlayer.Playlist.Count + "** `tracks currently queued.` "; + var currentSong = musicPlayer.CurrentSong; + if (currentSong == null) + return; + var toSend = $"🎵`Now Playing` {currentSong.PrettyName} " + $"{currentSong.PrettyCurrentTime()}\n"; + if (musicPlayer.RepeatSong) + toSend += "🔂"; + else if (musicPlayer.RepeatPlaylist) + toSend += "🔁"; + toSend += $" **{musicPlayer.Playlist.Count}** `tracks currently queued.` "; if (musicPlayer.Playlist.Count >= MusicPlayer.MaximumPlaylistSize) toSend += "**Song queue is full!**\n"; else @@ -156,9 +165,10 @@ namespace NadekoBot.Modules if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) return; var currentSong = musicPlayer.CurrentSong; - if (currentSong != null) - await e.Channel.SendMessage($"🎵`Now Playing` {currentSong.PrettyName} " + - $"{currentSong.PrettyCurrentTime()}"); + if (currentSong == null) + return; + await e.Channel.SendMessage($"🎵`Now Playing` {currentSong.PrettyName} " + + $"{currentSong.PrettyCurrentTime()}"); }); cgb.CreateCommand("vol") @@ -303,7 +313,7 @@ namespace NadekoBot.Modules }); cgb.CreateCommand("radio").Alias("ra") - .Description("Queues a direct radio stream from a link.") + .Description("Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf") .Parameter("radio_link", ParameterType.Required) .Do(async e => { @@ -385,6 +395,34 @@ namespace NadekoBot.Modules } }); + cgb.CreateCommand("rcs") + .Alias("repeatcurrentsong") + .Description("Toggles repeat of current song.") + .Do(async e => + { + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) + return; + var currentSong = musicPlayer.CurrentSong; + if (currentSong == null) + return; + var currentValue = musicPlayer.ToggleRepeatSong(); + await e.Channel.SendMessage(currentValue ? + $"🎵🔂`Repeating track:`{currentSong.PrettyName}" : + $"🎵🔂`Current track repeat stopped.`"); + }); + + cgb.CreateCommand("rpl") + .Alias("repeatplaylist") + .Description("Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue).") + .Do(async e => + { + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) + return; + var currentValue = musicPlayer.ToggleRepeatPlaylist(); + await e.Channel.SendMessage($"🎵🔁`Repeat playlist {(currentValue ? "enabled" : "disabled")}`"); + }); //cgb.CreateCommand("debug") // .Description("Does something magical. **BOT OWNER ONLY**") // .AddCheck(Classes.Permissions.SimpleCheckers.OwnerOnly()) diff --git a/NadekoBot/Modules/Pokemon/DefaultMoves.cs b/NadekoBot/Modules/Pokemon/DefaultMoves.cs index 0072f6bf..4cdba343 100644 --- a/NadekoBot/Modules/Pokemon/DefaultMoves.cs +++ b/NadekoBot/Modules/Pokemon/DefaultMoves.cs @@ -79,7 +79,11 @@ namespace NadekoBot.Modules.Pokemon {"bullet punch",16}, {"metal burst",16}, {"gear grind",16}, - {"magnet bomb",16} + {"magnet bomb",16}, + {"fairy wind",17}, + {"draining kiss",17}, + {"dazzling gleam",17}, + {"play rough",17} }; diff --git a/NadekoBot/Modules/Pokemon/PokeStats.cs b/NadekoBot/Modules/Pokemon/PokeStats.cs index 0d9c9d34..b9795043 100644 --- a/NadekoBot/Modules/Pokemon/PokeStats.cs +++ b/NadekoBot/Modules/Pokemon/PokeStats.cs @@ -1,15 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; namespace NadekoBot.Modules.Pokemon { class PokeStats { //Health left - public int HP { get; set; } = 500; + public int Hp { get; set; } = 500; + public int MaxHp { get; } = 500; //Amount of moves made since last time attacked public int MovesMade { get; set; } = 0; //Last people attacked diff --git a/NadekoBot/Modules/Pokemon/PokemonModule.cs b/NadekoBot/Modules/Pokemon/PokemonModule.cs index ac29c79c..ed181485 100644 --- a/NadekoBot/Modules/Pokemon/PokemonModule.cs +++ b/NadekoBot/Modules/Pokemon/PokemonModule.cs @@ -1,378 +1,318 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Discord.Commands; using Discord.Modules; -using Discord.Commands; -using NadekoBot.Commands; using NadekoBot.Classes; -using NadekoBot.Extensions; using NadekoBot.Classes._DataModels; using NadekoBot.Classes.Permissions; -using System.Collections.Concurrent; +using NadekoBot.Extensions; using NadekoBot.Modules.Pokemon.PokeTypes; using NadekoBot.Modules.Pokemon.PokeTypes.Extensions; - +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; namespace NadekoBot.Modules.Pokemon { - - class PokemonGame : DiscordModule + class PokemonModule : DiscordModule { public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Pokemon; - public readonly int BaseHealth = 500; - //private Dictionary stats = new Dictionary(); + private ConcurrentDictionary Stats = new ConcurrentDictionary(); - - public PokemonGame() + public PokemonModule() { - //Something? - } - public override void Install(ModuleManager manager) - { - manager.CreateCommands("", cgb => - { - cgb.AddCheck(Classes.Permissions.PermissionChecker.Instance); - - commands.ForEach(cmd => cmd.Init(cgb)); - - cgb.CreateCommand(Prefix + "attack") - .Description("Attacks a target with the given move") - .Parameter("move", ParameterType.Required) - .Parameter("target", ParameterType.Unparsed) - .Do(async e => + DbHandler.Instance.DeleteAll(); + DbHandler.Instance.InsertMany( + DefaultMoves.DefaultMovesList.Select(move => new PokeMoves { - var move = e.GetArg("move"); - var target = e.Server.FindUsers(e.GetArg("target")).FirstOrDefault(); - if (target == null) - { - await e.Channel.SendMessage("No such person."); - return; - } - // Checking stats first, then move - //Set up the userstats - PokeStats userStats; - userStats = Stats.GetOrAdd(e.User.Id, defaultStats()); - - //Check if able to move - //User not able if HP < 0, has made more than 4 attacks - if (userStats.HP < 0) - { - await e.Channel.SendMessage($"{e.User.Mention} has fainted and was not able to move!"); - return; - } - if (userStats.MovesMade >= 5) - { - await e.Channel.SendMessage($"{e.User.Mention} has used too many moves in a row and was not able to move!"); - return; - } - if (userStats.LastAttacked.Contains(target.Id)) - { - await e.Channel.SendMessage($"{e.User.Mention} can't attack again without retaliation!"); - return; - } - //get target stats - PokeStats targetStats; - targetStats = Stats.GetOrAdd(target.Id, defaultStats()); - - //If target's HP is below 0, no use attacking - if (targetStats.HP <= 0) - { - await e.Channel.SendMessage($"{target.Mention} has already fainted!"); - return; - } - - //Check whether move can be used - IPokeType userType = getPokeType(e.User.Id); - - var enabledMoves = userType.GetMoves(); - if (!enabledMoves.Contains(move.ToLowerInvariant())) - { - await e.Channel.SendMessage($"{e.User.Mention} was not able to use **{move}**, use {Prefix}listmoves to see moves you can use"); - return; - } - - //get target type - IPokeType targetType = getPokeType(target.Id); - //generate damage - int damage = getDamage(userType, targetType); - //apply damage to target - targetStats.HP -= damage; - - var response = $"{e.User.Mention} used **{move}**{userType.GetImage()} on {target.Mention}{targetType.GetImage()} for **{damage}** damage"; - - //Damage type - if (damage < 40) - { - response += "\nIt's not effective.."; - } - else if (damage > 60) - { - response += "\nIt's super effective!"; - } - else - { - response += "\nIt's somewhat effective"; - } - - //check fainted - - if (targetStats.HP <= 0) - { - response += $"\n**{target.Name}** has fainted!"; - } - else - { - response += $"\n**{target.Name}** has {targetStats.HP} HP remaining"; - } - - //update other stats - userStats.LastAttacked.Add(target.Id); - userStats.MovesMade++; - targetStats.MovesMade = 0; - if (targetStats.LastAttacked.Contains(e.User.Id)) - { - targetStats.LastAttacked.Remove(e.User.Id); - } - - //update dictionary - //This can stay the same right? - Stats[e.User.Id] = userStats; - Stats[target.Id] = targetStats; - - await e.Channel.SendMessage(response); - }); - - cgb.CreateCommand(Prefix + "listmoves") - .Description("Lists the moves you are able to use") - .Do(async e => - { - var userType = getPokeType(e.User.Id); - List movesList = userType.GetMoves(); - var str = "**Moves:**"; - foreach (string m in movesList) - { - str += $"\n{userType.GetImage()}{m}"; - } - await e.Channel.SendMessage(str); - }); - - cgb.CreateCommand(Prefix + "addmove") - .Description($"Adds move given to database.\n**Usage**: {Prefix}addmove flame fire") - .Parameter("movename", ParameterType.Required) - .Parameter("movetype", ParameterType.Required) - .Do(async e => - { - //Implement NadekoFlowers???? - string newMove = e.GetArg("movename").ToLowerInvariant(); - var newType = PokemonTypesMain.stringToPokeType(e.GetArg("movetype").ToUpperInvariant()); - int typeNum = newType.GetNum(); - var db = DbHandler.Instance.GetAllRows().Select(x => x.move); - if (db.Contains(newMove)) - { - await e.Channel.SendMessage($"{newMove} already exists"); - return; - } - await Task.Run(() => - { - DbHandler.Instance.InsertData(new Classes._DataModels.PokeMoves - { - move = newMove, - type = typeNum - }); - }); - await e.Channel.SendMessage($"Added {newType.GetImage()}{newMove}"); - }); - - cgb.CreateCommand(Prefix + "heal") - .Description($"Heals someone. Revives those that fainted. Costs a NadekoFlower \n**Usage**:{Prefix}revive @someone") - .Parameter("target", ParameterType.Required) - .Do(async e => - { - var usr = e.Server.FindUsers(e.GetArg("target")).FirstOrDefault(); - if (usr == null) - { - await e.Channel.SendMessage("No such person."); - return; - } - if (Stats.ContainsKey(usr.Id)) - { - - var targetStats = Stats[usr.Id]; - int HP = targetStats.HP; - if (targetStats.HP == BaseHealth) - { - await e.Channel.SendMessage($"{usr.Name} already has full HP!"); - return; - } - //Payment~ - var amount = 1; - var pts = Classes.DbHandler.Instance.GetStateByUserId((long)e.User.Id)?.Value ?? 0; - if (pts < amount) - { - await e.Channel.SendMessage($"{e.User.Mention} you don't have enough NadekoFlowers! \nYou still need {amount - pts} to be able to do this!"); - return; - } - var up = (usr.Id == e.User.Id) ? "yourself" : usr.Name; - await FlowersHandler.RemoveFlowersAsync(e.User, $"heal {up}", amount); - //healing - targetStats.HP = BaseHealth; - if (HP < 0) - { - //Could heal only for half HP? - Stats[usr.Id].HP = (BaseHealth / 2); - await e.Channel.SendMessage($"{e.User.Name} revived {usr.Name} for 🌸"); - return; - } - await e.Channel.SendMessage($"{e.User.Name} healed {usr.Name} for {BaseHealth - HP} HP with a 🌸"); - return; - } - else - { - await e.Channel.SendMessage($"{usr.Name} already has full HP!"); - } - }); - - cgb.CreateCommand(Prefix + "type") - .Description($"Get the poketype of the target.\n**Usage**: {Prefix}type @someone") - .Parameter("target", ParameterType.Required) - .Do(async e => - { - var usr = e.Server.FindUsers(e.GetArg("target")).FirstOrDefault(); - if (usr == null) - { - await e.Channel.SendMessage("No such person."); - return; - } - var pType = getPokeType(usr.Id); - await e.Channel.SendMessage($"Type of {usr.Name} is **{pType.GetName().ToLowerInvariant()}**{pType.GetImage()}"); - - }); - - cgb.CreateCommand(Prefix + "setdefaultmoves") - .Description($"Sets the moves DB to the default state and returns them all **OWNER ONLY**") - .AddCheck(SimpleCheckers.OwnerOnly()) - .Do(async e => - { - //clear DB - var db = DbHandler.Instance.GetAllRows(); - foreach (PokeMoves p in db) - { - DbHandler.Instance.Delete(p.Id); - } - - foreach (var entry in DefaultMoves.DefaultMovesList) - { - DbHandler.Instance.InsertData(new Classes._DataModels.PokeMoves - { - move = entry.Key, - type = entry.Value - }); - } - - var str = "**Reset moves to default**.\n**Moves:**"; - //could sort, but meh - var dbMoves = DbHandler.Instance.GetAllRows(); - foreach (PokeMoves m in dbMoves) - { - var t = PokemonTypesMain.IntToPokeType(m.type); - - str += $"\n{t.GetImage()}{m.move}"; - } - - await e.Channel.SendMessage(str); - - }); - - cgb.CreateCommand(Prefix + "settype") - .Description($"Set your poketype. Costs a NadekoFlower.\n**Usage**: {Prefix}settype fire") - .Parameter("targetType", ParameterType.Required) - .Do(async e => - { - var targetTypeString = e.GetArg("targetType"); - var targetType = PokemonTypesMain.stringToPokeType(targetTypeString.ToUpperInvariant()); - if (targetType == null) - { - await e.Channel.SendMessage("Invalid type specified. Type must be one of:\nNORMAL, FIRE, WATER, ELECTRIC, GRASS, ICE, FIGHTING, POISON, GROUND, FLYING, PSYCHIC, BUG, ROCK, GHOST, DRAGON, DARK, STEEL"); - return; - } - if (targetType == getPokeType(e.User.Id)) - { - await e.Channel.SendMessage($"Your type is already {targetType.GetName().ToLowerInvariant()}{targetType.GetImage()}"); - return; - } - - //Payment~ - var amount = 1; - var pts = Classes.DbHandler.Instance.GetStateByUserId((long)e.User.Id)?.Value ?? 0; - if (pts < amount) - { - await e.Channel.SendMessage($"{e.User.Mention} you don't have enough NadekoFlowers! \nYou still need {amount - pts} to be able to do this!"); - return; - } - await FlowersHandler.RemoveFlowersAsync(e.User, $"set usertype to {targetTypeString}", amount); - //Actually changing the type here - var preTypes = DbHandler.Instance.GetAllRows(); - Dictionary Dict = preTypes.ToDictionary(x => x.UserId, y => y.Id); - if (Dict.ContainsKey((long)e.User.Id)) - { - //delete previous type - DbHandler.Instance.Delete(Dict[(long)e.User.Id]); - } - - DbHandler.Instance.InsertData(new Classes._DataModels.userPokeTypes - { - UserId = (long)e.User.Id, - type = targetType.GetNum() - }); - - //Now for the response - - await e.Channel.SendMessage($"Set type of {e.User.Mention} to {targetTypeString}{targetType.GetImage()} for a 🌸"); - }); - }); + move = move.Key, + type = move.Value + })); } - - - - private int getDamage(IPokeType usertype, IPokeType targetType) + private int GetDamage(PokeType usertype, PokeType targetType) { - Random rng = new Random(); + var rng = new Random(); int damage = rng.Next(40, 60); - double multiplier = 1; - multiplier = usertype.GetMagnifier(targetType); + var multiplier = usertype.Multiplier(targetType); damage = (int)(damage * multiplier); return damage; } - private IPokeType getPokeType(ulong id) + private PokeType GetPokeType(ulong id) { - var db = DbHandler.Instance.GetAllRows(); + var db = DbHandler.Instance.GetAllRows(); Dictionary setTypes = db.ToDictionary(x => x.UserId, y => y.type); if (setTypes.ContainsKey((long)id)) { return PokemonTypesMain.IntToPokeType(setTypes[(long)id]); } - int remainder = (int)id % 16; + int remainder = Math.Abs((int)(id % 18)); return PokemonTypesMain.IntToPokeType(remainder); - - } - private PokeStats defaultStats() + public override void Install(ModuleManager manager) { - PokeStats s = new PokeStats(); - s.HP = BaseHealth; - return s; + manager.CreateCommands("", cgb => + { + cgb.AddCheck(PermissionChecker.Instance); + + commands.ForEach(cmd => cmd.Init(cgb)); + + cgb.CreateCommand(Prefix + "attack") + .Description("Attacks a target with the given move") + .Parameter("move", ParameterType.Required) + .Parameter("target", ParameterType.Unparsed) + .Do(async e => + { + var move = e.GetArg("move"); + var targetStr = e.GetArg("target")?.Trim(); + if (string.IsNullOrWhiteSpace(targetStr)) + return; + var target = e.Server.FindUsers(targetStr).FirstOrDefault(); + if (target == null) + { + await e.Channel.SendMessage("No such person."); + return; + } + else if (target == e.User) + { + await e.Channel.SendMessage("You can't attack yourself."); + return; + } + // Checking stats first, then move + //Set up the userstats + PokeStats userStats; + userStats = Stats.GetOrAdd(e.User.Id, new PokeStats()); + + //Check if able to move + //User not able if HP < 0, has made more than 4 attacks + if (userStats.Hp < 0) + { + await e.Channel.SendMessage($"{e.User.Mention} has fainted and was not able to move!"); + return; + } + if (userStats.MovesMade >= 5) + { + await e.Channel.SendMessage($"{e.User.Mention} has used too many moves in a row and was not able to move!"); + return; + } + if (userStats.LastAttacked.Contains(target.Id)) + { + await e.Channel.SendMessage($"{e.User.Mention} can't attack again without retaliation!"); + return; + } + //get target stats + PokeStats targetStats; + targetStats = Stats.GetOrAdd(target.Id, new PokeStats()); + + //If target's HP is below 0, no use attacking + if (targetStats.Hp <= 0) + { + await e.Channel.SendMessage($"{target.Mention} has already fainted!"); + return; + } + + //Check whether move can be used + PokeType userType = GetPokeType(e.User.Id); + + var enabledMoves = userType.GetMoves(); + if (!enabledMoves.Contains(move.ToLowerInvariant())) + { + await e.Channel.SendMessage($"{e.User.Mention} was not able to use **{move}**, use {Prefix}listmoves to see moves you can use"); + return; + } + + //get target type + PokeType targetType = GetPokeType(target.Id); + //generate damage + int damage = GetDamage(userType, targetType); + //apply damage to target + targetStats.Hp -= damage; + + var response = $"{e.User.Mention} used **{move}**{userType.Image} on {target.Mention}{targetType.Image} for **{damage}** damage"; + + //Damage type + if (damage < 40) + { + response += "\nIt's not effective.."; + } + else if (damage > 60) + { + response += "\nIt's super effective!"; + } + else + { + response += "\nIt's somewhat effective"; + } + + //check fainted + + if (targetStats.Hp <= 0) + { + response += $"\n**{target.Name}** has fainted!"; + } + else + { + response += $"\n**{target.Name}** has {targetStats.Hp} HP remaining"; + } + + //update other stats + userStats.LastAttacked.Add(target.Id); + userStats.MovesMade++; + targetStats.MovesMade = 0; + if (targetStats.LastAttacked.Contains(e.User.Id)) + { + targetStats.LastAttacked.Remove(e.User.Id); + } + + //update dictionary + //This can stay the same right? + Stats[e.User.Id] = userStats; + Stats[target.Id] = targetStats; + + await e.Channel.SendMessage(response); + }); + + cgb.CreateCommand(Prefix + "ml") + .Alias("movelist") + .Description("Lists the moves you are able to use") + .Do(async e => + { + var userType = GetPokeType(e.User.Id); + List movesList = userType.GetMoves(); + var str = $"**Moves for `{userType.Name}` type.**"; + foreach (string m in movesList) + { + str += $"\n{userType.Image}{m}"; + } + await e.Channel.SendMessage(str); + }); + + cgb.CreateCommand(Prefix + "heal") + .Description($"Heals someone. Revives those that fainted. Costs a NadekoFlower \n**Usage**:{Prefix}revive @someone") + .Parameter("target", ParameterType.Unparsed) + .Do(async e => + { + var targetStr = e.GetArg("target")?.Trim(); + if (string.IsNullOrWhiteSpace(targetStr)) + return; + var usr = e.Server.FindUsers(targetStr).FirstOrDefault(); + if (usr == null) + { + await e.Channel.SendMessage("No such person."); + return; + } + if (Stats.ContainsKey(usr.Id)) + { + + var targetStats = Stats[usr.Id]; + int HP = targetStats.Hp; + if (targetStats.Hp == targetStats.MaxHp) + { + await e.Channel.SendMessage($"{usr.Name} already has full HP!"); + return; + } + //Payment~ + var amount = 1; + var pts = Classes.DbHandler.Instance.GetStateByUserId((long)e.User.Id)?.Value ?? 0; + if (pts < amount) + { + await e.Channel.SendMessage($"{e.User.Mention} you don't have enough {NadekoBot.Config.CurrencyName}s! \nYou still need {amount - pts} to be able to do this!"); + return; + } + var target = (usr.Id == e.User.Id) ? "yourself" : usr.Name; + FlowersHandler.RemoveFlowers(e.User, $"Poke-Heal {target}", amount); + //healing + targetStats.Hp = targetStats.MaxHp; + if (HP < 0) + { + //Could heal only for half HP? + Stats[usr.Id].Hp = (targetStats.MaxHp / 2); + await e.Channel.SendMessage($"{e.User.Name} revived {usr.Name} with one {NadekoBot.Config.CurrencySign}"); + return; + } + await e.Channel.SendMessage($"{e.User.Name} healed {usr.Name} for {targetStats.MaxHp - HP} HP with a 🌸"); + return; + } + else + { + await e.Channel.SendMessage($"{usr.Name} already has full HP!"); + } + }); + + cgb.CreateCommand(Prefix + "type") + .Description($"Get the poketype of the target.\n**Usage**: {Prefix}type @someone") + .Parameter("target", ParameterType.Unparsed) + .Do(async e => + { + var usrStr = e.GetArg("target")?.Trim(); + if (string.IsNullOrWhiteSpace(usrStr)) + return; + var usr = e.Server.FindUsers(usrStr).FirstOrDefault(); + if (usr == null) + { + await e.Channel.SendMessage("No such person."); + return; + } + var pType = GetPokeType(usr.Id); + await e.Channel.SendMessage($"Type of {usr.Name} is **{pType.Name.ToLowerInvariant()}**{pType.Image}"); + + }); + + cgb.CreateCommand(Prefix + "settype") + .Description($"Set your poketype. Costs a {NadekoBot.Config.CurrencyName}.\n**Usage**: {Prefix}settype fire") + .Parameter("targetType", ParameterType.Unparsed) + .Do(async e => + { + var targetTypeStr = e.GetArg("targetType")?.ToUpperInvariant(); + if (string.IsNullOrWhiteSpace(targetTypeStr)) + return; + var targetType = PokemonTypesMain.stringToPokeType(targetTypeStr); + if (targetType == null) + { + await e.Channel.SendMessage("Invalid type specified. Type must be one of:\nNORMAL, FIRE, WATER, ELECTRIC, GRASS, ICE, FIGHTING, POISON, GROUND, FLYING, PSYCHIC, BUG, ROCK, GHOST, DRAGON, DARK, STEEL"); + return; + } + if (targetType == GetPokeType(e.User.Id)) + { + await e.Channel.SendMessage($"Your type is already {targetType.Name.ToLowerInvariant()}{targetType.Image}"); + return; + } + + //Payment~ + var amount = 1; + var pts = DbHandler.Instance.GetStateByUserId((long)e.User.Id)?.Value ?? 0; + if (pts < amount) + { + await e.Channel.SendMessage($"{e.User.Mention} you don't have enough {NadekoBot.Config.CurrencyName}s! \nYou still need {amount - pts} to be able to do this!"); + return; + } + FlowersHandler.RemoveFlowers(e.User, $"set usertype to {targetTypeStr}", amount); + //Actually changing the type here + var preTypes = DbHandler.Instance.GetAllRows(); + Dictionary Dict = preTypes.ToDictionary(x => x.UserId, y => y.Id); + if (Dict.ContainsKey((long)e.User.Id)) + { + //delete previous type + DbHandler.Instance.Delete(Dict[(long)e.User.Id]); + } + + DbHandler.Instance.InsertData(new UserPokeTypes + { + UserId = (long)e.User.Id, + type = targetType.Num + }); + + //Now for the response + + await e.Channel.SendMessage($"Set type of {e.User.Mention} to {targetTypeStr}{targetType.Image} for a {NadekoBot.Config.CurrencySign}"); + }); + }); } - - } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/BugType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/BugType.cs index 2df84631..00da7d7c 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/BugType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/BugType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class BugType : IPokeType + class BugType : PokeType { static readonly string name = "BUG"; public static int numType = 11; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIRE": return 0.5; @@ -24,8 +18,10 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes case "FIGHTING": return 0.5; case "POISON": return 0.5; case "FLYING": return 0.5; + case "GHOST": return 0.5; case "PSYCHIC": return 2; case "ROCK": return 0.5; + case "FAIRY": return 0.5; case "DARK": return 2; case "STEEL": return 0.5; default: return 1; @@ -33,24 +29,10 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - + public string Name => name; + public string Image => "🐛"; - public string GetName() - { - return name; - } - - - - public string GetImage() - { - return "🐛"; - } - - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/DarkType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/DarkType.cs index 54f69e2d..2b9287ed 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/DarkType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/DarkType.cs @@ -1,52 +1,32 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class DarkType : IPokeType + class DarkType : PokeType { static readonly string name = "DARK"; public static int numType = 15; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIGHTING": return 0.5; case "PSYCHIC": return 2; case "GHOST": return 2; case "DARK": return 0.5; - case "STEEL": return 0.5; + case "FAIRY": return 0.5; default: return 1; } } List moves = new List(); - + public string Name => name; + public string Image => "🕶"; - public string GetName() - { - return name; - } - - - - public string GetImage() - { - return "🕶"; - } - - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/Dragontype.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/Dragontype.cs index e7168ffc..ae448af9 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/Dragontype.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/Dragontype.cs @@ -1,49 +1,35 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class DragonType : IPokeType + class DragonType : PokeType { static readonly string name = "DRAGON"; public static int numType = 14; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "DRAGON": return 2; case "STEEL": return 0.5; + case "FAIRY": return 0; default: return 1; } } List moves = new List(); - - public string GetName() - { - return name; - } - + public string Name => name; - public string GetImage() - { - return "🐉"; - } - public int GetNum() - { - return numType; - } + + public string Image => "🐉"; + + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/ElectricType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/ElectricType.cs index 3cb77723..9e44767e 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/ElectricType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/ElectricType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class ElectricType : IPokeType + class ElectricType : PokeType { static readonly string name = "ELECTRIC"; public static int numType = 3; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "WATER": return 2; @@ -30,23 +24,14 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - - public string GetName() - { - return name; - } - - public string GetImage() - { - return "⚡️"; - } + public string Name => name; - public int GetNum() - { - return numType; - } + + public string Image => "⚡️"; + + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/FairyType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/FairyType.cs new file mode 100644 index 00000000..17c40b93 --- /dev/null +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/FairyType.cs @@ -0,0 +1,33 @@ +using NadekoBot.Modules.Pokemon.PokeTypes; +using System.Collections.Generic; + +namespace NadekoBot.Modules.Pokemon.PokemonTypes +{ + class FairyType : PokeType + { + static readonly string name = "FAIRY"; + public static int numType = 17; + + public double Multiplier(PokeType target) + { + switch (target.Name) + { + + case "FIGHTING": return 2; + case "FIRE": return 0.5; + case "DARK": return 0.5; + case "POISON": return 0.5; + case "STEEL": return 2; + case "DRAGON": return 2; + default: return 1; + } + } + List moves = new List(); + + public string Name => name; + + public string Image => "💫"; + + public int Num => numType; + } +} diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/FightingType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/FightingType.cs index 3d4603ed..a972595f 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/FightingType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/FightingType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class FightingType : IPokeType + class FightingType : PokeType { static readonly string name = "FIGHTING"; public static int numType = 6; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "NORMAL": return 2; @@ -29,28 +23,20 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes case "GHOST": return 0; case "DARK": return 2; case "STEEL": return 2; + case "FAIRY": return 0.5; default: return 1; } } List moves = new List(); - - public string GetName() - { - return name; - } - - public string GetImage() - { - return "✊"; - } + public string Name => name; - public int GetNum() - { - return numType; - } + + public string Image => "✊"; + + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/FireType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/FireType.cs index 6995799a..4504f7c7 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/FireType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/FireType.cs @@ -1,24 +1,18 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class FireType : IPokeType + class FireType : PokeType { static readonly string name = "FIRE"; public static int numType = 1; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIRE": return 0.5; @@ -33,25 +27,10 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } } List moves = new List(); + public string Name => name; - + public string Image => "🔥"; - - public string GetName() - { - return name; - } - - - - public string GetImage() - { - return "🔥"; - } - - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/FlyingType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/FlyingType.cs index 8f26875b..afd65972 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/FlyingType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/FlyingType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class FlyingType : IPokeType + class FlyingType : PokeType { static readonly string name = "FLYING"; public static int numType = 9; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "ELECTRIC": return 0.5; @@ -30,24 +24,15 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - - public string GetName() - { - return name; - } - + public string Name => name; - public string GetImage() - { - return "☁"; - } - public int GetNum() - { - return numType; - } + + public string Image => "☁"; + + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/GhostType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/GhostType.cs index 83c35504..f902c403 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/GhostType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/GhostType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class GhostType : IPokeType + class GhostType : PokeType { static readonly string name = "GHOST"; public static int numType = 13; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "NORMAL": return 0; @@ -29,24 +23,15 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - - public string GetName() - { - return name; - } - + public string Name => name; - public string GetImage() - { - return "👻"; - } - public int GetNum() - { - return numType; - } + + public string Image => "👻"; + + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/GrassType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/GrassType.cs index 1fc34dd6..f861c719 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/GrassType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/GrassType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class GrassType : IPokeType + class GrassType : PokeType { static readonly string name = "GRASS"; public static int numType = 4; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIRE": return 0.5; @@ -32,23 +26,14 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - - public string GetName() - { - return name; - } - - public string GetImage() - { - return "🌿"; - } + public string Name => name; - public int GetNum() - { - return numType; - } + + public string Image => "🌿"; + + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/GroundType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/GroundType.cs index 9795aa77..a09d398c 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/GroundType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/GroundType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class GroundType : IPokeType + class GroundType : PokeType { static readonly string name = "GROUND"; public static int numType = 8; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIRE": return 2; @@ -32,24 +26,10 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - + public string Name => name; + public string Image => "🗻"; - public string GetName() - { - return name; - } - - - - public string GetImage() - { - return "🗻"; - } - - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/IPokeTypeExtensions.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/IPokeTypeExtensions.cs index 08d354cf..1b81790e 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/IPokeTypeExtensions.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/IPokeTypeExtensions.cs @@ -5,23 +5,23 @@ using System.Collections.Generic; namespace NadekoBot.Modules.Pokemon.PokeTypes.Extensions { public static class IPokeTypeExtensions + { + public static List GetMoves(this PokeType poketype) { - public static List GetMoves(this IPokeType poketype) + var db = DbHandler.Instance.GetAllRows(); + List moves = new List(); + foreach (PokeMoves p in db) { - var db = DbHandler.Instance.GetAllRows(); - List moves = new List(); - foreach (PokeMoves p in db) + if (p.type == poketype.Num) { - if (p.type == poketype.GetNum()) + if (!moves.Contains(p.move)) { - if (!moves.Contains(p.move)) - { - moves.Add(p.move); - } + moves.Add(p.move); } } - return moves; } + return moves; } - } + +} diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/IceType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/IceType.cs index 1d1156ad..2afc203d 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/IceType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/IceType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class IceType : IPokeType + class IceType : PokeType { static readonly string name = "ICE"; public static int numType = 5; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIRE": return 0.5; @@ -32,23 +26,10 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - + public string Name => name; + public string Image => "❄"; - public string GetName() - { - return name; - } - - - public string GetImage() - { - return "❄"; - } - - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/NormalType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/NormalType.cs index 3052b4e1..1554ddbe 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/NormalType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/NormalType.cs @@ -1,26 +1,18 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; -using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class NormalType : IPokeType + class NormalType : PokeType { static readonly string name = "NORMAL"; public static int type_num = 0; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { - case "ROCK": return 0.5; case "GHOST": return 0; case "STEEL": return 0.5; @@ -29,24 +21,10 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - + public string Name => name; + public string Image => "⭕️"; - public string GetName() - { - return name; - } - - - - public string GetImage() - { - return "⭕️"; - } - - public int GetNum() - { - return type_num; - } + public int Num => type_num; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/PoisonType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/PoisonType.cs index 9c30dfae..ab1a0e3e 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/PoisonType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/PoisonType.cs @@ -1,23 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; -using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class PoisonType : IPokeType + class PoisonType : PokeType { static readonly string name = "POISON"; public static int numType = 7; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "GRASS": return 2; @@ -26,28 +19,16 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes case "ROCK": return 0.5; case "GHOST": return 0.5; case "STEEL": return 0; + case "FAIRY": return 2; default: return 1; } } List moves = new List(); - + public string Name => name; + public string Image => "☠"; - public string GetName() - { - return name; - } - - - public string GetImage() - { - return "☠"; - } - - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/PokeType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/PokeType.cs index 6c47e466..efde0905 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/PokeType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/PokeType.cs @@ -1,28 +1,24 @@ -using System.Collections.Generic; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; -using NadekoBot.Modules.Pokemon.PokemonTypes; +using NadekoBot.Modules.Pokemon.PokemonTypes; +using System.Collections.Generic; namespace NadekoBot.Modules.Pokemon.PokeTypes { - - public interface IPokeType + public interface PokeType { - - string GetImage(); - string GetName(); - int GetNum(); - double GetMagnifier(IPokeType target); + string Image { get; } + string Name { get; } + int Num { get; } + double Multiplier(PokeType target); } public class PokemonTypesMain { - - public static IPokeType stringToPokeType(string newType) + + public static PokeType stringToPokeType(string newType) { - foreach (IPokeType t in TypeList) + foreach (PokeType t in TypeList) { - if (t.GetName() == newType) + if (t.Name == newType) { return t; } @@ -30,26 +26,8 @@ namespace NadekoBot.Modules.Pokemon.PokeTypes return null; } - //public static List getMoves(int numType) - //{ - // var db = DbHandler.Instance.GetAllRows(); - // List moves = new List(); - // foreach (PokeMoves p in db) - // { - // if (p.type == numType) - // { - // if (!moves.Contains(p.move)) - // { - // moves.Add(p.move); - // } - // } - // } - // return moves; - //} - - //These classes can use all methods (except getMoves) - public static List TypeList = new List() + public static List TypeList = new List() { new NormalType(), new FireType(), @@ -67,14 +45,15 @@ namespace NadekoBot.Modules.Pokemon.PokeTypes new GhostType(), new DragonType(), new DarkType(), - new SteelType() + new SteelType(), + new FairyType() }; - public static IPokeType IntToPokeType(int id) + public static PokeType IntToPokeType(int id) { - foreach (IPokeType t in TypeList) + foreach (PokeType t in TypeList) { - if (t.GetNum() == id) + if (t.Num == id) { return t; } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/PsychicType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/PsychicType.cs index 1972c197..e675091f 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/PsychicType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/PsychicType.cs @@ -1,23 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; -using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class PsychicType : IPokeType + class PsychicType : PokeType { static readonly string name = "PSYCHIC"; public static int numType = 10; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIGHTING": return 2; @@ -30,24 +23,13 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - - public string GetName() - { - return name; - } - + public string Name => name; - public string GetImage() - { - return "💫"; - } + public string Image => "🔮"; - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/RockType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/RockType.cs index 76b7ae44..eafd1803 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/RockType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/RockType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class RockType : IPokeType + class RockType : PokeType { static readonly string name = "ROCK"; public static int numType = 12; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIRE": return 2; @@ -31,24 +25,10 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - + public string Name => name; + public string Image => "💎"; - public string GetName() - { - return name; - } - - - - public string GetImage() - { - return "💎"; - } - - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/SteelType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/SteelType.cs index ff8b554b..73427039 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/SteelType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/SteelType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class SteelType : IPokeType + class SteelType : PokeType { static readonly string name = "STEEL"; - public static int numType = -1; + public static int numType = 16; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIRE": return 0.5; @@ -30,23 +24,10 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - + public string Name => name; + public string Image => "🔩"; - public string GetName() - { - return name; - } - - - public string GetImage() - { - return "🔩"; - } - - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Pokemon/PokemonTypes/WaterType.cs b/NadekoBot/Modules/Pokemon/PokemonTypes/WaterType.cs index e5cfeaaa..2b6c49d8 100644 --- a/NadekoBot/Modules/Pokemon/PokemonTypes/WaterType.cs +++ b/NadekoBot/Modules/Pokemon/PokemonTypes/WaterType.cs @@ -1,22 +1,16 @@ -using System; +using NadekoBot.Modules.Pokemon.PokeTypes; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NadekoBot.Modules.Pokemon; -using NadekoBot.Classes; -using NadekoBot.Classes._DataModels; using NadekoBot.Modules.Pokemon.PokeTypes; using NadekoBot.Modules.Pokemon.PokeTypes; namespace NadekoBot.Modules.Pokemon.PokemonTypes { - class WaterType : IPokeType + class WaterType : PokeType { static readonly string name = "WATER"; public static int numType = 2; - public double GetMagnifier(IPokeType target) + public double Multiplier(PokeType target) { - switch (target.GetName()) + switch (target.Name) { case "FIRE": return 2; @@ -30,23 +24,10 @@ namespace NadekoBot.Modules.Pokemon.PokemonTypes } List moves = new List(); - + public string Name => name; + public string Image => "💦"; - public string GetName() - { - return name; - } - - - public string GetImage() - { - return "💦"; - } - - public int GetNum() - { - return numType; - } + public int Num => numType; } } diff --git a/NadekoBot/Modules/Searches.cs b/NadekoBot/Modules/Searches.cs index fe5b158d..8e29d878 100644 --- a/NadekoBot/Modules/Searches.cs +++ b/NadekoBot/Modules/Searches.cs @@ -317,9 +317,62 @@ $@"🌍 **Weather for** 【{obj["target"]}】 .Description("Shows a random quote.") .Do(async e => { - await - e.Channel.SendMessage( - NadekoBot.Config.Quotes[new Random().Next(0, NadekoBot.Config.Quotes.Count)].ToString()); + var quote = NadekoBot.Config.Quotes[rng.Next(0, NadekoBot.Config.Quotes.Count)].ToString(); + await e.Channel.SendMessage(quote); + }); + + cgb.CreateCommand(Prefix + "catfact") + .Description("Shows a random catfact from ") + .Do(async e => + { + var response = await SearchHelper.GetResponseStringAsync("http://catfacts-api.appspot.com/api/facts"); + if (response == null) + return; + await e.Channel.SendMessage($"🐈 `{JObject.Parse(response)["facts"][0].ToString()}`"); + }); + + cgb.CreateCommand(Prefix + "yomama") + .Alias(Prefix + "ym") + .Description("Shows a random joke from ") + .Do(async e => + { + var response = await SearchHelper.GetResponseStringAsync("http://api.yomomma.info/"); + await e.Channel.SendMessage("`" + JObject.Parse(response)["joke"].ToString() + "` 😆"); + }); + + cgb.CreateCommand(Prefix + "randjoke") + .Alias(Prefix + "rj") + .Description("Shows a random joke from ") + .Do(async e => + { + var response = await SearchHelper.GetResponseStringAsync("http://tambal.azurewebsites.net/joke/random"); + await e.Channel.SendMessage("`" + JObject.Parse(response)["joke"].ToString() + "` 😆"); + }); + + cgb.CreateCommand(Prefix + "chucknorris") + .Alias(Prefix + "cn") + .Description("Shows a random chucknorris joke from ") + .Do(async e => + { + var response = await SearchHelper.GetResponseStringAsync("http://api.icndb.com/jokes/random/"); + await e.Channel.SendMessage("`" + JObject.Parse(response)["value"]["joke"].ToString() + "` 😆"); + }); + + cgb.CreateCommand(Prefix + "revav") + .Description("Returns a google reverse image search for someone's avatar.") + .Parameter("user", ParameterType.Unparsed) + .Do(async e => + { + var usrStr = e.GetArg("user")?.Trim(); + + if (string.IsNullOrWhiteSpace(usrStr)) + return; + + var usr = e.Server.FindUsers(usrStr).FirstOrDefault(); + + if (usr == null || string.IsNullOrWhiteSpace(usr.AvatarUrl)) + return; + await e.Channel.SendMessage($"https://images.google.com/searchbyimage?image_url={usr.AvatarUrl}"); }); }); } diff --git a/NadekoBot/NadekoBot.cs b/NadekoBot/NadekoBot.cs index d4f60c86..2fbb48f4 100644 --- a/NadekoBot/NadekoBot.cs +++ b/NadekoBot/NadekoBot.cs @@ -5,8 +5,10 @@ using Discord.Modules; using NadekoBot.Classes.JSONModels; using NadekoBot.Commands; using NadekoBot.Modules; +using NadekoBot.Modules.Administration; using NadekoBot.Modules.Gambling; using NadekoBot.Modules.Pokemon; +using NadekoBot.Modules.Translator; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -14,13 +16,12 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; -using NadekoBot.Modules.Translator; namespace NadekoBot { public class NadekoBot { - public static DiscordClient Client; + public static DiscordClient Client { get; private set; } public static Credentials Creds { get; set; } public static Configuration Config { get; set; } public static LocalizedStrings Locale { get; set; } = new LocalizedStrings(); @@ -110,7 +111,7 @@ namespace NadekoBot Client = new DiscordClient(new DiscordConfigBuilder() { MessageCacheSize = 10, - ConnectionTimeout = 60000, + ConnectionTimeout = 120000, LogLevel = LogSeverity.Warning, LogHandler = (s, e) => Console.WriteLine($"Severity: {e.Severity}" + @@ -157,7 +158,7 @@ namespace NadekoBot })); //install modules - modules.Add(new Administration(), "Administration", ModuleFilter.None); + modules.Add(new AdministrationModule(), "Administration", ModuleFilter.None); modules.Add(new Help(), "Help", ModuleFilter.None); modules.Add(new PermissionModule(), "Permissions", ModuleFilter.None); modules.Add(new Conversations(), "Conversations", ModuleFilter.None); @@ -167,7 +168,7 @@ namespace NadekoBot modules.Add(new Searches(), "Searches", ModuleFilter.None); modules.Add(new NSFW(), "NSFW", ModuleFilter.None); modules.Add(new ClashOfClans(), "ClashOfClans", ModuleFilter.None); - modules.Add(new PokemonGame(), "Pokegame", ModuleFilter.None); + modules.Add(new PokemonModule(), "Pokegame", ModuleFilter.None); modules.Add(new TranslatorModule(), "Translator", ModuleFilter.None); if (!string.IsNullOrWhiteSpace(Creds.TrelloAppKey)) modules.Add(new Trello(), "Trello", ModuleFilter.None); diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index 858557ba..1a56b634 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -9,7 +9,7 @@ Properties NadekoBot NadekoBot - v4.5.2 + v4.6 512 true false @@ -32,6 +32,7 @@ true + AnyCPU @@ -145,27 +146,30 @@ + - - + + + + - + - - + + - + @@ -175,9 +179,9 @@ - - - + + + @@ -187,9 +191,10 @@ - + + diff --git a/NadekoBot/bin/Debug/data/config_example.json b/NadekoBot/bin/Debug/data/config_example.json index 0f4507e1..b5d31bc6 100644 --- a/NadekoBot/bin/Debug/data/config_example.json +++ b/NadekoBot/bin/Debug/data/config_example.json @@ -16,7 +16,7 @@ "Gambling": "$", "Permissions": ";", "Programming": "%", - "Pokemon": "poke" + "Pokemon": ">" }, "ServerBlacklist": [], "ChannelBlacklist": [], diff --git a/NadekoBot/bin/Debug/data/currency_images/img1.jpg b/NadekoBot/bin/Debug/data/currency_images/img1.jpg new file mode 100644 index 00000000..4a34ba97 Binary files /dev/null and b/NadekoBot/bin/Debug/data/currency_images/img1.jpg differ diff --git a/NadekoBot/bin/Debug/data/currency_images/img2.jpg b/NadekoBot/bin/Debug/data/currency_images/img2.jpg new file mode 100644 index 00000000..5697f8bb Binary files /dev/null and b/NadekoBot/bin/Debug/data/currency_images/img2.jpg differ diff --git a/NadekoBot/bin/Debug/data/currency_images/img3.jpg b/NadekoBot/bin/Debug/data/currency_images/img3.jpg new file mode 100644 index 00000000..e0019b09 Binary files /dev/null and b/NadekoBot/bin/Debug/data/currency_images/img3.jpg differ diff --git a/NadekoBot/bin/Debug/data/lol/champions/AurelionSol.png b/NadekoBot/bin/Debug/data/lol/champions/AurelionSol.png new file mode 100644 index 00000000..25ed0c66 Binary files /dev/null and b/NadekoBot/bin/Debug/data/lol/champions/AurelionSol.png differ diff --git a/NadekoBot/packages.config b/NadekoBot/packages.config index ec861bf0..19d0d51d 100644 --- a/NadekoBot/packages.config +++ b/NadekoBot/packages.config @@ -9,7 +9,7 @@ - + diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index d83b8b1a..0ddc6cc5 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -8,7 +8,7 @@ Properties Tests Tests - v4.5.2 + v4.6 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 diff --git a/commandlist.md b/commandlist.md index 681c48ba..3c6b0eaf 100644 --- a/commandlist.md +++ b/commandlist.md @@ -2,7 +2,7 @@ ######You can donate on paypal: `nadekodiscordbot@gmail.com` or Bitcoin `17MZz1JAqME39akMLrVT4XBPffQJ2n1EPa` #NadekoBot List Of Commands -Version: `NadekoBot v0.9.5930.23184` +Version: `NadekoBot v0.9.5933.23628` ### Administration Command and aliases | Description | Usage ----------------|--------------|------- @@ -31,6 +31,7 @@ Command and aliases | Description | Usage `.lsar` | Lits all self-assignable roles. `.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | .iam Gamer `.iamn`, `.iamnot` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | .iamn Gamer +`.remind` | `.sr`, `.setrole` | Sets a role for a given user. | .sr @User Guest `.rr`, `.removerole` | Removes a role from a given user. | .rr @User Admin `.r`, `.role`, `.cr` | Creates a role with a given name. | .r Awesome Role @@ -46,7 +47,7 @@ Command and aliases | Description | Usage `.vch`, `.cvch` | Creates a new voice channel with a given name. `.rch`, `.rtch` | Removes a text channel with a given name. `.ch`, `.tch` | Creates a new text channel with a given name. -`.st`, `.settopic` | Sets a topic on the current channel. +`.st`, `.settopic`, `.topic` | Sets a topic on the current channel. `.uid`, `.userid` | Shows user ID. `.cid`, `.channelid` | Shows current channel ID. `.sid`, `.serverid` | Shows current server ID. @@ -70,7 +71,6 @@ Command and aliases | Description | Usage `.unstuck` | Clears the message queue. **Owner Only!** `.donators` | List of lovely people who donated to keep this project alive. `.adddon`, `.donadd` | Add a donator to the database. -`.topic` | Sets current channel's topic. `.videocall` | Creates a private appear.in video call link for you and other mentioned people. The link is sent to mentioned people via a private message. ### Help @@ -138,7 +138,7 @@ Command and aliases | Description | Usage `@BotName uptime` | Shows how long Nadeko has been running for. `@BotName die` | Works only for the owner. Shuts the bot down. `@BotName do you love me` | Replies with positive answer only to the bot owner. -`@BotName how are you` | Replies positive only if bot owner is online. +`@BotName how are you`, `@BotName how are you?` | Replies positive only if bot owner is online. `@BotName insult` | Insults @X person. | @NadekoBot insult @X. `@BotName praise` | Praises @X person. | @NadekoBot praise @X. `@BotName pat` | Pat someone ^_^ @@ -169,8 +169,10 @@ Command and aliases | Description | Usage `$roll` | Rolls 2 dice from 0-10. If you supply a number [x] it rolls up to 30 normal dice. | $roll [x] `$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` `$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. -`$$$` | Check how many NadekoFlowers you have. -`$give` | Give someone a certain amount of flowers +`$$$` | Check how much NadekoFlowers you have. +`$give` | Give someone a certain amount of NadekoFlowers +`$award` | Gives someone a certain amount of flowers. **Owner only!** +`$take` | Takes a certain amount of flowers from someone. **Owner only!** ### Games Command and aliases | Description | Usage @@ -185,15 +187,13 @@ Command and aliases | Description | Usage `>pollend` | Stops active poll on this server and prints the results in this channel. `>choose` | Chooses a thing from a list of things | >choose Get up;Sleep;Sleep more `>8ball` | Ask the 8ball a yes/no question. -`>attack` | Attack a person. Supported attacks: 'splash', 'strike', 'burn', 'surge'. | >attack strike @User -`>poketype` | Gets the users element type. Use this to do more damage with strike! -`>rps` | Play a game of rocket paperclip scissors with nadkeo. | >rps scissors +`>rps` | Play a game of rocket paperclip scissors with Nadeko. | >rps scissors `>linux` | Prints a customizable Linux interjection ### Music Command and aliases | Description | Usage ----------------|--------------|------- -`!m n`, `!m next` | Goes to the next song in the queue. +`!m n`, `!m next`, `!m skip` | Goes to the next song in the queue. `!m s`, `!m stop` | Stops the music and clears the playlist. Stays in the channel. `!m d`, `!m destroy` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) `!m p`, `!m pause` | Pauses or Unpauses the song. @@ -209,7 +209,7 @@ Command and aliases | Description | Usage `!m setgame` | Sets the game of the bot to the number of songs playing. **Owner only** `!m pl` | Queues up to 25 songs from a youtube playlist specified by a link, or keywords. `!m lopl` | Queues up to 50 songs from a directory. **Owner Only!** -`!m radio`, `!m ra` | Queues a direct radio stream from a link. +`!m radio`, `!m ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf `!m lo` | Queues a local file by specifying a full path. **Owner Only!** `!m mv` | Moves the bot to your voice channel. (works only if music is already playing) `!m rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. @@ -222,6 +222,7 @@ Command and aliases | Description | Usage `~lolban` | Shows top 6 banned champions ordered by ban rate. Ban these champions and you will be Plat 5 in no time. `~hitbox`, `~hb` | Notifies this channel when a certain user starts streaming. | ~hitbox SomeStreamer `~twitch`, `~tw` | Notifies this channel when a certain user starts streaming. | ~twitch SomeStreamer +`~beam`, `~bm` | Notifies this channel when a certain user starts streaming. | ~beam SomeStreamer `~removestream`, `~rms` | Removes notifications of a certain streamer on this channel. | ~rms SomeGuy `~liststreams`, `~ls` | Lists all streams you are following on this server. | ~ls `~we` | Shows weather data for a specified city and a country BOTH ARE REQUIRED. Weather api is very random if you make a mistake. @@ -238,12 +239,11 @@ Command and aliases | Description | Usage `~ud` | Searches Urban Dictionary for a word. | ~ud Pineapple `~#` | Searches Tagdef.com for a hashtag. | ~# ff `~quote` | Shows a random quote. - -### Translator -Command and aliases | Description | Usage -----------------|--------------|------- -`~trans` | Translates from>to text. From the given language to the destiation language. -`~translangs` | List the valid languages for translation. +`~catfact` | Shows a random catfact from +`~yomama`, `~ym` | Shows a random joke from +`~randjoke`, `~rj` | Shows a random joke from +`~chucknorris`, `~cn` | Shows a random chucknorris joke from +`~revav` | Returns a google reverse image search for someone's avatar. ### NSFW Command and aliases | Description | Usage @@ -267,6 +267,21 @@ Command and aliases | Description | Usage `,unclaim`, `,uncall`, `,uc` | Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim | ,uc [war_number] [optional_other_name] `,endwar`, `,ew` | Ends the war with a given index. | ,ew [war_number] +### Pokegame +Command and aliases | Description | Usage +----------------|--------------|------- +`>attack` | Attacks a target with the given move +`>ml`, `movelist` | Lists the moves you are able to use +`>heal` | Heals someone. Revives those that fainted. Costs a NadekoFlower | >revive @someone +`>type` | Get the poketype of the target. | >type @someone +`>settype` | Set your poketype. Costs a NadekoFlower. | >settype fire + +### Translator +Command and aliases | Description | Usage +----------------|--------------|------- +`~trans` | Translates from>to text. From the given language to the destiation language. +`~translangs` | List the valid languages for translation. + ### Trello Command and aliases | Description | Usage ----------------|--------------|-------