diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..dd7327e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,231 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directory +AppPackages/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/NadekoBot.sln b/NadekoBot.sln new file mode 100644 index 00000000..9520d5e1 --- /dev/null +++ b/NadekoBot.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot", "NadekoBot\NadekoBot.csproj", "{27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + FullDebug|Any CPU = FullDebug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.FullDebug|Any CPU.Build.0 = Debug|Any CPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/NadekoBot/App.config b/NadekoBot/App.config new file mode 100644 index 00000000..151cc154 --- /dev/null +++ b/NadekoBot/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NadekoBot/Classes/Cards.cs b/NadekoBot/Classes/Cards.cs new file mode 100644 index 00000000..2868e066 --- /dev/null +++ b/NadekoBot/Classes/Cards.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; +using System.Linq; +using System; + +public class Cards +{ + private static Dictionary cardNames = new Dictionary() { + { 1, "Ace" }, + { 2, "Two" }, + { 3, "Three" }, + { 4, "Four" }, + { 5, "Five" }, + { 6, "Six" }, + { 7, "Seven" }, + { 8, "Eight" }, + { 9, "Nine" }, + { 10, "Ten" }, + { 11, "Jack" }, + { 12, "Queen" }, + { 13, "King" }, + }; + + public enum CARD_SUIT + { + Spades = 1, + Hearts = 2, + Diamonds = 3, + Clubs = 4 + } + + public class Card + { + public CARD_SUIT suit; + public int number; + + public string Path + { + get + { + string str = ""; + + if (number <= 10 && number > 1) + { + str += number; + } + else + { + str += GetName().ToLower(); + } + return @"images/cards/" + str + "_of_" + suit.ToString().ToLower() + ".jpg"; + } + } + + public Card(CARD_SUIT s, int card_num) { + this.suit = s; + this.number = card_num; + } + public string GetName() { + return cardNames[number]; + } + + public override string ToString() + { + return cardNames[number] + " Of " + suit; + } + } + + private List cardPool; + public List CardPool + { + get { return cardPool; } + set { cardPool = value; } + } + + /// + /// Creates a new instance of the BlackJackGame, this allows you to create multiple games running at one time. + /// + public Cards() + { + cardPool = new List(52); + RefillPool(); + } + /// + /// Restart the game of blackjack. It will only refill the pool for now. Probably wont be used, unless you want to have only 1 bjg running at one time, + /// then you will restart the same game every time. + /// + public void Restart() + { + // you dont have to uncover what is actually happening anda da hood + RefillPool(); + } + + /// + /// Removes all cards from the pool and refills the pool with all of the possible cards. NOTE: I think this is too expensive. + /// We should probably make it so it copies another premade list with all the cards, or something. + /// + private void RefillPool() + { + cardPool.Clear(); + //foreach suit + for (int j = 1; j < 14; j++) + { + // and number + for (int i = 1; i < 5; i++) + { + //generate a card of that suit and number and add it to the pool + + // the pool will go from ace of spades,hears,diamonds,clubs all the way to the king of spades. hearts, ... + cardPool.Add(new Card((CARD_SUIT)i, j)); + } + } + } + /// + /// Take a card from the pool, you either take it from the top if the deck is shuffled, or from a random place if the deck is in the default order. + /// + /// A card from the pool + public Card DrawACard() + { + if (CardPool.Count > 0) + { + //you can either do this if your deck is not shuffled + Random r = new Random((int)DateTime.Now.Ticks); + int num = r.Next(0, cardPool.Count); + Card c = cardPool[num]; + cardPool.RemoveAt(num); + return c; + + // if you want to shuffle when you fill, then take the first one + /* + Card c = cardPool[0]; + cardPool.RemoveAt(0); + return c; + */ + } + else { + + //for now return null + Restart(); + return null; + } + } + /// + /// Shuffles the deck. Use this if you want to take cards from the top of the deck, instead of randomly. See DrawACard method. + /// + private void Shuffle() { + if (cardPool.Count > 1) + { + Random r = new Random(); + cardPool.OrderBy(x => r.Next()); + } + } + //public override string ToString() => string.Join("", cardPool.Select(c => c.ToString())) + Environment.NewLine; + public override string ToString() => string.Join("", cardPool.Select(c => c.ToString())) + Environment.NewLine; +} + diff --git a/NadekoBot/Classes/Extensions.cs b/NadekoBot/Classes/Extensions.cs new file mode 100644 index 00000000..62fe61b5 --- /dev/null +++ b/NadekoBot/Classes/Extensions.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Security.Cryptography; + +namespace NadekoBot +{ + public static class Extensions + { + public static void Shuffle(this IList list) + { + RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider(); + int n = list.Count; + while (n > 1) + { + byte[] box = new byte[1]; + do provider.GetBytes(box); + while (!(box[0] < n * (Byte.MaxValue / n))); + int k = (box[0] % n); + n--; + T value = list[k]; + list[k] = list[n]; + list[n] = value; + } + } + } +} diff --git a/NadekoBot/Classes/ImageHandler.cs b/NadekoBot/Classes/ImageHandler.cs new file mode 100644 index 00000000..19dd5fb0 --- /dev/null +++ b/NadekoBot/Classes/ImageHandler.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot +{ + class ImageHandler + { + /// + /// Merges Images into 1 Image and returns a bitmap. + /// + /// The Images you want to merge. + /// Merged bitmap + public static Bitmap MergeImages(Image[] images) + { + int width = images.Sum(i => i.Width); + int height = images[0].Height; + Bitmap bitmap = new Bitmap(width, height); + var r = new Random(); + int offsetx = 0; + foreach (var img in images) + { + Bitmap bm = new Bitmap(img); + for (int w = 0; w < img.Width; w++) + { + for (int h = 0; h < img.Height; h++) + { + bitmap.SetPixel(w + offsetx, h, bm.GetPixel(w, h)); + } + } + offsetx += img.Width; + } + return bitmap; + } + } +} diff --git a/NadekoBot/Classes/Trivia.cs b/NadekoBot/Classes/Trivia.cs new file mode 100644 index 00000000..7f27da92 --- /dev/null +++ b/NadekoBot/Classes/Trivia.cs @@ -0,0 +1,315 @@ +using Discord; +using Discord.Commands; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Timers; + +namespace NadekoBot +{ + public class Trivia : DiscordCommand + { + public static Dictionary runningTrivias; + + public Trivia() : base() { + runningTrivias = new Dictionary(); + } + + public static TriviaGame StartNewGame(CommandEventArgs e) { + if (runningTrivias.ContainsKey(e.User.Server.Id)) + return null; + + var tg = new TriviaGame(e, NadekoBot.client); + runningTrivias.Add(e.Server.Id, tg); + + return tg; + } + + public TriviaQuestion GetCurrentQuestion(long serverId) { + return runningTrivias[serverId].currentQuestion; + } + + public override Func DoFunc() + { + return async e => + { + TriviaGame tg; + if ((tg = StartNewGame(e))!=null) + { + await client.SendMessage(e.Channel, "**Trivia game started!** It is bound to this channel. But only 1 game can run per server. \n First player to get to 10 points wins! You have 30 seconds per question.\nUse command [tq] if game was started by accident."); + } + else + await client.SendMessage(e.Channel, "Trivia game is already running on this server. The question is:\n**"+GetCurrentQuestion(e.Server.Id).Question+"**\n[tq quits trivia]\n[@NadekoBot clr clears my messages]"); // TODO type x to be reminded of the question + }; + } + + private Func LbFunc() + { + return async e => + { + if (runningTrivias.ContainsKey(e.Server.Id)) + { + var lb = runningTrivias[e.User.Server.Id].GetLeaderboard(); + await client.SendMessage(e.Channel, lb); + } + else + await client.SendMessage(e.Channel, "Trivia game is not running on this server."); // TODO type x to be reminded of the question + }; + } + + private Func RepeatFunc() + { + return async e => + { + if (runningTrivias.ContainsKey(e.Server.Id)) + { + var lb = runningTrivias[e.User.Server.Id].GetLeaderboard(); + await client.SendMessage(e.Channel, lb); + } + else + await client.SendMessage(e.Channel, "Trivia game is not running on this server."); // TODO type x to be reminded of the question + }; + } + + public override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand("t") + .Description("Starts a game of trivia. Questions suck and repeat a lot atm.") + .Alias("-t") + .Do(DoFunc()); + + cgb.CreateCommand("tl") + .Description("Shows a current trivia leaderboard.") + .Alias("-tl") + .Alias("tlb") + .Alias("-tlb") + .Do(LbFunc()); + + cgb.CreateCommand("tq") + .Description("Quits current trivia after current question.") + .Alias("-tq") + .Do(QuitFunc()); + } + + private Func QuitFunc() + { + return async e => + { + if (runningTrivias.ContainsKey(e.Server.Id) && runningTrivias[e.Server.Id].ChannelId ==e.Channel.Id) + { + await client.SendMessage(e.Channel, "Trivia will stop after this question. Run [**@NadekoBot clr**] to remove this bot's messages from the channel."); + runningTrivias[e.Server.Id].StopGame(); + } + else await client.SendMessage(e.Channel, "No trivias are running on this channel."); + }; + } + + internal static void FinishGame(TriviaGame triviaGame) + { + runningTrivias.Remove(runningTrivias.Where(kvp => kvp.Value == triviaGame).First().Key); + } + } + + public class TriviaGame { + + private DiscordClient client; + private long _serverId; + private long _channellId; + + public long ChannelId + { + get + { + return _channellId; + } + } + + private Dictionary users; + + public List oldQuestions; + + public TriviaQuestion currentQuestion = null; + + private bool active = false; + + private Timer timeout; + private bool isQuit = false; + + public TriviaGame(CommandEventArgs starter, DiscordClient client) { + this.users = new Dictionary(); + this.client = client; + this._serverId = starter.Server.Id; + this._channellId= starter.Channel.Id; + + oldQuestions = new List(); + client.MessageReceived += PotentialGuess; + + timeout = new Timer(); + timeout.Interval = 30000; + timeout.Elapsed += (s, e) => { TimeUp(); }; + + LoadNextRound(); + } + + private async void PotentialGuess(object sender, MessageEventArgs e) + { + if (e.Server.Id != _serverId || !active) + return; + + if (e.Message.Text.ToLower() == currentQuestion.Answer.ToLower()) + { + active = false; //start pause between rounds + timeout.Enabled = false; + + if (!users.ContainsKey(e.User.Id)) + users.Add(e.User.Id, 1); + else + { + users[e.User.Id]++; + } + await client.SendMessage(e.Channel, Mention.User(e.User) + " Guessed it!\n The answer was: **" + currentQuestion.Answer + "**"); + + if (users[e.User.Id] >= 10) { + await client.SendMessage(e.Channel, " We have a winner! It's " + Mention.User(e.User)+"\n"+GetLeaderboard()+"\n To start a new game type '@NadekoBot t'"); + FinishGame(); + return; + } + + //if it still didnt return, we can safely start another round :D + LoadNextRound(); + } + } + + public void StopGame() { + isQuit = true; + } + + private void LoadNextRound() + { + Channel ch = client.GetChannel(_channellId); + + + if(currentQuestion!=null) + oldQuestions.Add(currentQuestion.Question); + + currentQuestion = TriviaQuestionsPool.Instance.GetRandomQuestion(oldQuestions); + + if (currentQuestion == null || isQuit) + { + client.SendMessage(ch, "Trivia bot stopping. :\\\n" + GetLeaderboard()); + FinishGame(); + return; + } + Timer t = new Timer(); + t.Interval = 2500; + t.Enabled = true; + t.Elapsed += async (s, ev) => { + active = true; + await client.SendMessage(ch, "QUESTION\n**" + currentQuestion.Question + " **"); + t.Enabled = false; + timeout.Enabled = true;//starting countdown of the next question + }; + return; + } + + private async void TimeUp() { + await client.SendMessage(client.GetChannel(_channellId), "**Time's up.**\nCorrect answer was: **" + currentQuestion.Answer+"**\n**[tq quits trivia][tl shows leaderboard][@NadekoBot clr clears my messages]**"); + LoadNextRound(); + } + + public void FinishGame() { + isQuit = true; + active = false; + client.MessageReceived -= PotentialGuess; + timeout.Enabled = false; + timeout.Dispose(); + Trivia.FinishGame(this); + } + + public string GetLeaderboard() { + if (users.Count == 0) + return ""; + + + string str = "**Leaderboard:**\n-----------\n"; + + if(users.Count>1) + users.OrderBy(kvp => kvp.Value); + + foreach (var KeyValuePair in users) + { + str += "**" + client.GetUser(client.GetServer(_serverId), KeyValuePair.Key).Name + "** has " +KeyValuePair.Value + (KeyValuePair.Value == 1 ? "point." : "points.") + Environment.NewLine; + } + + return str; + } + } + + public class TriviaQuestion { + public string Question; + public string Answer; + public TriviaQuestion(string q, string a) { + this.Question = q; + this.Answer = a; + } + + public override string ToString() + { + return this.Question; + } + } + + public class TriviaQuestionsPool { + private static TriviaQuestionsPool instance = null; + + public static TriviaQuestionsPool Instance + { + get { + if (instance == null) + instance = new TriviaQuestionsPool(); + return instance; + } + private set { instance = value; } + } + + public List pool; + + private Random _r; + + public TriviaQuestionsPool() { + _r = new Random(); + pool = new List(); + var httpClient = new System.Net.Http.HttpClient(); + + + var r = httpClient.GetAsync("http://jservice.io/api/clues?category=19").Result; + string str = r.Content.ReadAsStringAsync().Result; + dynamic obj = JArray.Parse(str); + + foreach (var item in obj) + { + pool.Add(new TriviaQuestion((string)item.question,(string)item.answer)); + } + } + + + public TriviaQuestion GetRandomQuestion(List exclude) { + if (pool.Count == 0) + return null; + + TriviaQuestion tq = pool[_r.Next(0, pool.Count)]; + if (exclude.Count > 0 && exclude.Count < pool.Count) + { + while (exclude.Contains(tq.Question)) + { + tq = pool[_r.Next(0, pool.Count)]; + } + } + return tq; + } + } +} diff --git a/NadekoBot/Classes/_JSONModels.cs b/NadekoBot/Classes/_JSONModels.cs new file mode 100644 index 00000000..dd94dec9 --- /dev/null +++ b/NadekoBot/Classes/_JSONModels.cs @@ -0,0 +1,56 @@ +namespace NadekoBot +{ + public class Credentials + { + public string Username; + public string Password; + public string BotMention; + public string GoogleAPIKey; + public long OwnerID; + public bool Crawl; + public string ParseID; + public string ParseKey; + } + + class AnimeResult + { + public int id; + public string airing_status; + public string title_english; + public int total_episodes; + public string description; + public string image_url_lge; + + public override string ToString() + { + return "`Title:` **" + title_english + + "**\n`Status:` " + airing_status + + "\n`Episodes:` " + total_episodes + + "\n`Link:` http://anilist.co/anime/" + id + + "\n`Synopsis:` " + description.Substring(0, description.Length > 500 ? 500 : description.Length) + "..." + + "\n`img:` " + image_url_lge; + } + } + + class MangaResult + { + public int id; + public string publishing_status; + public string image_url_lge; + public string title_english; + public int total_chapters; + public int total_volumes; + public string description; + + public override string ToString() + { + return "`Title:` **" + title_english + + "**\n`Status:` " + publishing_status + + "\n`Chapters:` " + total_chapters + + "\n`Volumes:` " + total_volumes + + "\n`Link:` http://anilist.co/manga/" + id + + "\n`Synopsis:` " + description.Substring(0, description.Length > 500 ? 500 : description.Length) + "..." + + "\n`img:` " + image_url_lge; + } + } +} \ No newline at end of file diff --git a/NadekoBot/Commands/CopyCommand.cs b/NadekoBot/Commands/CopyCommand.cs new file mode 100644 index 00000000..60c8e6fc --- /dev/null +++ b/NadekoBot/Commands/CopyCommand.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; + +namespace NadekoBot +{ + class CopyCommand : DiscordCommand + { + private List CopiedUsers; + + public CopyCommand() : base() + { + CopiedUsers = new List(); + client.MessageReceived += Client_MessageReceived; + } + + private async void Client_MessageReceived(object sender, Discord.MessageEventArgs e) + { + if (CopiedUsers.Contains(e.User.Id)) { + await client.SendMessage(e.Channel, e.Message.Text); + } + } + + public override Func DoFunc() + { + return async e => + { + if (CopiedUsers.Contains(e.User.Id)) return; + + CopiedUsers.Add(e.User.Id); + await client.SendMessage(e.Channel, " I'll start copying you now."); + return; + }; + } + + public override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand("copyme") + .Alias("cm") + .Description("Nadeko starts copying everything you say. Disable with cs") + .Do(DoFunc()); + + cgb.CreateCommand("cs") + .Alias("copystop") + .Description("Nadeko stops copying you") + .Do(StopCopy()); + } + + private Func StopCopy() + { + return async e => + { + if (!CopiedUsers.Contains(e.User.Id)) return; + + CopiedUsers.Remove(e.User.Id); + await client.SendMessage(e.Channel, " I wont copy anymore."); + return; + }; + } + } +} diff --git a/NadekoBot/Commands/DiceRollCommand.cs b/NadekoBot/Commands/DiceRollCommand.cs new file mode 100644 index 00000000..325a4a67 --- /dev/null +++ b/NadekoBot/Commands/DiceRollCommand.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; +using Discord.Modules; +using Discord; +using System.Drawing; + +namespace NadekoBot +{ + class DiceRollCommand : DiscordCommand + { + public DiceRollCommand() : base() { } + + public override Func DoFunc() + { + Random r = new Random(); + return async e => { + if (e.Args[0] == "") + { + int num1 = r.Next(0, 10); + int num2 = r.Next(0, 10); + + Console.WriteLine(num1+"|"+num2); + + Image[] images; + + 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) }; + } + + Bitmap bitmap = ImageHandler.MergeImages(images); + bitmap.Save("dices.png"); + await client.SendFile(e.Channel, "dices.png"); + return; + } + else + { + try + { + int num = int.Parse(e.Args[0]); + if (num < 1) num = 1; + if (num > 30) + { + await client.SendMessage(e.Channel, "You can roll up to 30 dies at a time."); + num = 30; + } + List dices = new List(num); + List values = new List(num); + for (int i = 0; i < num; i++) + { + int randomNumber = r.Next(1, 7); + int toInsert = dices.Count; + if (randomNumber == 6 || dices.Count==0) + toInsert = 0; + else if (randomNumber!=1) + for (int j = 0; j < dices.Count; j++) { + if (values[j] < randomNumber) + { + toInsert = j; + break; + } + } + dices.Insert(toInsert, GetDice(randomNumber)); + values.Insert(toInsert, randomNumber); + } + + Bitmap bitmap = ImageHandler.MergeImages(dices.ToArray()); + bitmap.Save("dices.png"); + await client.SendMessage(e.Channel, values.Count + " Dies rolled. Total: **"+values.Sum()+"** Average: **"+(values.Sum()/(1.0f*values.Count)).ToString("N2")+"**"); + await client.SendFile(e.Channel, "dices.png"); + } + catch (Exception) + { + await client.SendMessage(e.Channel, "Please enter a number of dices to roll."); + return; + } + } + }; + } + + private Image GetDice(int num) { + return Image.FromFile("images/dice/"+num+".png"); + } + + public override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand("$roll") + .Description("Rolls 2 dice from 0-10. If you supply a number [x] it rolls up to 30 normal dice.\nUsage: $roll [x]") + .Parameter("num", ParameterType.Optional) + .Do(DoFunc()); + } + } +} \ No newline at end of file diff --git a/NadekoBot/Commands/DiscordCommand.cs b/NadekoBot/Commands/DiscordCommand.cs new file mode 100644 index 00000000..3fecd6b8 --- /dev/null +++ b/NadekoBot/Commands/DiscordCommand.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; + +namespace NadekoBot +{ + /// + /// Base DiscordCommand Class. + /// Inherit this class to create your own command. + /// + public abstract class DiscordCommand + { + /// + /// Client at the moment of creating this object + /// + public DiscordClient client { get; set; } + + /// + /// Constructor of the base class + /// + /// CommandBuilder which will be modified + protected DiscordCommand() + { + client = NadekoBot.client; + } + /// + /// Function containing the behaviour of the command. + /// + /// Client who will handle the message sending, etc, if any. + /// + public abstract Func DoFunc(); + + /// + /// Initializes the CommandBuilder with values + /// + public abstract void Init(CommandGroupBuilder cgb); + } +} diff --git a/NadekoBot/Commands/DrawCommand.cs b/NadekoBot/Commands/DrawCommand.cs new file mode 100644 index 00000000..a5764730 --- /dev/null +++ b/NadekoBot/Commands/DrawCommand.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; +using Discord; +using System.Drawing; + +namespace NadekoBot +{ + class DrawCommand : DiscordCommand + { + private Cards cards = null; + + public DrawCommand() : base() { } + + public override Func DoFunc() { + return async (e) => + { + if (cards == null) + { + cards = new Cards(); + await client.SendMessage(e.Channel, "Shuffling cards..."); + } + + try + { + int num = int.Parse(e.Args[0]); + if (num > 5) + { + num = 5; + } + else if (num < 1) + { + num = 1; + } + Image[] images = new Image[num]; + for (int i = 0; i < num; i++) + { + if (cards.CardPool.Count == 0) + { + await client.SendMessage(e.Channel, "No more cards in a deck...\nGetting a new deck...\nShuffling cards..."); + } + images[i] = Image.FromFile(cards.DrawACard().Path); + } + Bitmap bitmap = ImageHandler.MergeImages(images); + bitmap.Save("cards.png"); + await client.SendFile(e.Channel, "cards.png"); + } + catch (Exception) { + Console.WriteLine("Error drawing (a) card(s)"); + } + }; + } + + public override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand("$draw") + .Description("Draws a card from the deck.If you supply number [x], she draws up to 5 cards from the deck.\nUsage: $draw [x]") + .Parameter("count", ParameterType.Multiple) + .Do(DoFunc()); + } + + } +} diff --git a/NadekoBot/Commands/FlipCoinCommand.cs b/NadekoBot/Commands/FlipCoinCommand.cs new file mode 100644 index 00000000..60d00bbf --- /dev/null +++ b/NadekoBot/Commands/FlipCoinCommand.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Discord.Commands; + +namespace NadekoBot +{ + class FlipCoinCommand : DiscordCommand + { + + private Random _r; + public FlipCoinCommand() : base() + { + _r = new Random(); + } + + public override Func DoFunc() + { + return async e => { + int num = _r.Next(0, 2); + if (num == 1) + { + await client.SendFile(e.Channel, @"images/coins/heads.png"); + } + else + { + await client.SendFile(e.Channel, @"images/coins/tails.png"); + } + }; + } + + public override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand("$flip") + .Description("Flips a coin, heads or tails, and shows an image of it.") + .Do(DoFunc()); + } + } +} diff --git a/NadekoBot/Modules/Conversations.cs b/NadekoBot/Modules/Conversations.cs new file mode 100644 index 00000000..e1b6777d --- /dev/null +++ b/NadekoBot/Modules/Conversations.cs @@ -0,0 +1,585 @@ +using Discord; +using Discord.Commands; +using Discord.Modules; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Timers; +using System.Threading.Tasks; +using System.Diagnostics; +using System.Drawing; +using Newtonsoft.Json.Linq; +using System.IO; +using System.Text; + +namespace NadekoBot.Modules +{ + class Conversations : DiscordModule + { + private string firestr = "🔥 ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้ 🔥"; + public Conversations() : base() + { + commands.Add(new CopyCommand()); + } + + private CommandBuilder CreateCommand(CommandGroupBuilder cbg, string txt) + { + CommandBuilder cb = cbg.CreateCommand(txt); + return AliasCommand(cb, txt); + } + + private CommandBuilder AliasCommand(CommandBuilder cb, string txt) + { + return cb.Alias(new string[] { "," + txt, "-" + txt }); + } + + public override void Install(ModuleManager manager) + { + Random rng = new Random(); + manager.CreateCommands("", cgb => + { + var client = manager.Client; + + cgb.CreateCommand("\\o\\") + .Description("Nadeko replies with /o/") + .Do(async e => + { + await client.SendMessage(e.Channel, Mention.User(e.User) + "/o/"); + }); + + cgb.CreateCommand("/o/") + .Description("Nadeko replies with \\o\\") + .Do(async e => + { + await client.SendMessage(e.Channel, Mention.User(e.User) + "\\o\\"); + }); + + }); + + manager.CreateCommands(NadekoBot.botMention, cgb => + { + var client = manager.Client; + + commands.ForEach(cmd => cmd.Init(cgb)); + + CreateCommand(cgb, "do you love me") + .Description("Replies with positive answer only to the bot owner.") + .Do(async e => + { + if (e.User.Id == NadekoBot.OwnerID) + await client.SendMessage(e.Channel, Mention.User(e.User) + ", Of course I do, my Master."); + else + await client.SendMessage(e.Channel, Mention.User(e.User) + ", Don't be silly."); + }); + + CreateCommand(cgb, "die") + .Description("Works only for the owner. Shuts the bot down.") + .Do(async e => + { + if (e.User.Id == NadekoBot.OwnerID) + { + Timer t = new Timer(); + t.Interval = 2000; + t.Elapsed += (s, ev) => { Environment.Exit(0); }; + t.Start(); + await client.SendMessage(e.Channel, Mention.User(e.User) + ", Yes, my love."); + } + else + await client.SendMessage(e.Channel, Mention.User(e.User) + ", No."); + }); + + CreateCommand(cgb, "how are you") + .Description("Replies positive only if bot owner is online.") + .Do(async e => + { + if (e.User.Id == NadekoBot.OwnerID) + { + await client.SendMessage(e.Channel, Mention.User(e.User) + " I am great as long as you are here."); + } + else + { + var kw = client.GetUser(e.Server, NadekoBot.OwnerID); + if (kw != null && kw.Status == UserStatus.Online) + { + await client.SendMessage(e.Channel, Mention.User(e.User) + " I am great as long as " + Mention.User(kw) + " is with me."); + } + else + { + await client.SendMessage(e.Channel, Mention.User(e.User) + " I am sad. My Master is not with me."); + } + } + }); + + CreateCommand(cgb, "insult") + .Parameter("mention", ParameterType.Required) + .Description("Only works for owner. Insults @X person.\nUsage: @NadekoBot insult @X.") + .Do(async e => + { + List insults = new List { " you are a poop.", " you jerk.", " i will eat you when i get my powers back." }; + Random r = new Random(); + var u = e.Message.MentionedUsers.Last(); + if (u.Id == NadekoBot.OwnerID) + { + await client.SendMessage(e.Channel, "I would never insult my master <3"); + } + else if (e.User.Id == NadekoBot.OwnerID) + { + await client.SendMessage(e.Channel, Mention.User(u) + insults[r.Next(0, insults.Count)]); + } + else + { + await client.SendMessage(e.Channel, Mention.User(e.User) + " Eww, why would i do that for you ?!"); + } + }); + + CreateCommand(cgb, "praise") + .Description("Only works for owner. Praises @X person.\nUsage: @NadekoBot insult @X.") + .Parameter("mention", ParameterType.Required) + .Do(async e => + { + List praises = new List { " You are cool.", " You are nice... But don't get any wrong ideas.", " You did a good job." }; + Random r = new Random(); + var u = e.Message.MentionedUsers.First(); + if (e.User.Id == NadekoBot.OwnerID) + { + if (u.Id != NadekoBot.OwnerID) + await client.SendMessage(e.Channel, Mention.User(u) + praises[r.Next(0, praises.Count)]); + else + { + await client.SendMessage(e.Channel, Mention.User(u) + " No need, you know I love you <3"); + } + } + else + { + if (u.Id == NadekoBot.OwnerID) + { + await client.SendMessage(e.Channel, Mention.User(e.User) + " I don't need your permission to praise my beloved Master <3"); + } + else + { + await client.SendMessage(e.Channel, Mention.User(e.User) + " Yeah... No."); + } + } + }); + + CreateCommand(cgb, "are you real") + .Description("Useless.") + .Do(async e => + { + await client.SendMessage(e.Channel, Mention.User(e.User) + " I will be soon."); + }); + + cgb.CreateCommand("are you there") + .Description("Checks if nadeko is operational.") + .Alias(new string[] { "!", "?", "??", "???", "!!", "!!!" }) + .Do(SayYes()); + + CreateCommand(cgb, "draw") + .Description("Nadeko instructs you to type $draw. Gambling functions start with $") + .Do(async e => + { + await client.SendMessage(e.Channel, "Sorry i dont gamble, type $draw for that function."); + }); + + CreateCommand(cgb, "uptime") + .Description("Shows how long is Nadeko running for.") + .Do(async e => + { + var time = (DateTime.Now - Process.GetCurrentProcess().StartTime); + string str = "I am online for " + time.Days + " days, " + time.Hours + " hours, and " + time.Minutes + " minutes."; + await client.SendMessage(e.Channel, str); + }); + + CreateCommand(cgb, "fire") + .Description("Shows a unicode fire message. Optional parameter [x] tells her how many times to repeat the fire.\nUsage: @NadekoBot fire [x]") + .Parameter("times", ParameterType.Optional) + .Do(async e => + { + int count = 0; + if (e.Args?.Length > 0) + int.TryParse(e.Args[0], out count); + + if (count < 1) + count = 1; + else if (count > 12) + count = 12; + string str = ""; + for (int i = 0; i < count; i++) + { + str += firestr; + } + await client.SendMessage(e.Channel, str); + }); + + CreateCommand(cgb, "rip") + .Description("Shows a grave image.Optional parameter [@X] instructs her to put X's name on the grave.\nUsage: @NadekoBot rip [@X]") + .Parameter("all", ParameterType.Unparsed) + .Do(async e => + { + + if (e.Message.MentionedUsers.Count() == 1) + { + await client.SendFile(e.Channel, @"images\rip.png"); + } + else + { + foreach (User u in e.Message.MentionedUsers) + { + if (u.Name == "NadekoBot") continue; + RipName(u.Name); + await client.SendFile(e.Channel, @"images\ripnew.png"); + } + } + }); + + cgb.CreateCommand("j") + .Description("Joins a server using a code. Obsolete, since nadeko will autojoin any valid code in chat.") + .Parameter("id", ParameterType.Required) + .Do(async e => + { + try + { + await client.AcceptInvite(client.GetInvite(e.Args[0]).Result); + await client.SendMessage(e.Channel, "I got in!"); + } + catch (Exception) + { + await client.SendMessage(e.Channel, "Invalid code."); + } + }); + + cgb.CreateCommand("i") + .Description("Pulls a first image using a search parameter.\nUsage: @NadekoBot img Multiword_search_parameter") + .Alias("img") + .Parameter("all", ParameterType.Unparsed) + .Do(async e => + { + var httpClient = new System.Net.Http.HttpClient(); + string str = e.Args[0]; + + var r = httpClient.GetAsync("http://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=" + Uri.EscapeDataString(str) + "&start=0").Result; + + dynamic obj = JObject.Parse(r.Content.ReadAsStringAsync().Result); + if (obj.responseData.results.Count == 0) + { + await client.SendMessage(e.Channel, "No results found for that keyword :\\"); + return; + } + string s = Searches.ShortenUrl(obj.responseData.results[0].url.ToString()); + await client.SendMessage(e.Channel, s); + }); + + cgb.CreateCommand("ir") + .Description("Pulls a random image using a search parameter.\nUsage: @NadekoBot img Multiword_search_parameter") + .Alias("imgrandom") + .Parameter("all", ParameterType.Unparsed) + .Do(async e => + { + + var httpClient = new System.Net.Http.HttpClient(); + string str = e.Args[0]; + var r = httpClient.GetAsync("http://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=" + Uri.EscapeDataString(str) + "&start=" + rng.Next(0, 30)).Result; + dynamic obj = JObject.Parse(r.Content.ReadAsStringAsync().Result); + if (obj.responseData.results.Count == 0) + { + await client.SendMessage(e.Channel, "No results found for that keyword :\\"); + return; + } + int rnd = rng.Next(0, obj.responseData.results.Count); + string s = Searches.ShortenUrl(obj.responseData.results[rnd].url.ToString()); + await client.SendMessage(e.Channel, s); + }); + + + AliasCommand(CreateCommand(cgb, "save"), "s") + .Description("Saves something for the owner in a file.") + .Parameter("all", ParameterType.Unparsed) + .Do(async e => + { + if (e.User.Id == NadekoBot.OwnerID) + { + string m = ""; + try + { + FileStream f = File.OpenWrite("saves.txt"); + m = e.Args[0]; + byte[] b = Encoding.ASCII.GetBytes(m + "\n"); + f.Seek(f.Length, SeekOrigin.Begin); + f.Write(b, 0, b.Length); + f.Close(); + } + catch (Exception) + { + await client.SendMessage(e.Channel, "Error saving. Sorry :("); + } + if (m.Length > 0) + await client.SendMessage(e.Channel, "I saved this for you: " + Environment.NewLine + "```" + m + "```"); + else + await client.SendMessage(e.Channel, "No point in saving empty message..."); + } + else await client.SendMessage(e.Channel, "Not for you, only my Master <3"); + }); + + CreateCommand(cgb, "ls") + .Description("Shows all saved items.") + .Do(async e => + { + FileStream f = File.OpenRead("saves.txt"); + if (f.Length == 0) + { + await client.SendMessage(e.Channel, "Saves are empty."); + return; + } + byte[] b = new byte[f.Length / sizeof(byte)]; + f.Read(b, 0, b.Length); + f.Close(); + string str = Encoding.ASCII.GetString(b); + await client.SendPrivateMessage(e.User, "```" + (str.Length < 1950 ? str : str.Substring(0, 1950)) + "```"); + }); + + CreateCommand(cgb, "cs") + .Description("Deletes all saves") + .Do(async e => + { + File.Delete("saves.txt"); + await client.SendMessage(e.Channel, "Cleared all saves."); + }); + + CreateCommand(cgb, "bb") + .Description("Says bye to someone.\nUsage: @NadekoBot bb @X") + .Parameter("ppl", ParameterType.Unparsed) + .Do(async e => + { + string str = "Bye"; + foreach (var u in e.Message.MentionedUsers) + { + str += " " + Mention.User(u); + } + await client.SendMessage(e.Channel, str); + }); + + AliasCommand(CreateCommand(cgb, "req"), "request") + .Description("Requests a feature for nadeko.\nUsage: @NadekoBot req Mutliword_feature_request") + .Parameter("all", ParameterType.Unparsed) + .Do(async e => + { + string str = e.Args[0]; + + try + { + StatsCollector.SaveRequest(e, str); + } + catch (Exception) + { + await client.SendMessage(e.Channel, "Something went wrong."); + return; + } + await client.SendMessage(e.Channel, "Thank you for your request."); + }); + + CreateCommand(cgb, "lr") + .Description("PMs the user all current nadeko requests.") + .Do(async e => + { + string str = StatsCollector.GetRequests(); + if (str.Trim().Length > 110) + await client.SendPrivateMessage(e.User, str); + else + await client.SendPrivateMessage(e.User, "No requests atm."); + }); + + CreateCommand(cgb, "dr") + .Description("Deletes a request. Only owner is able to do this.") + .Parameter("reqNumber", ParameterType.Required) + .Do(async e => + { + if (e.User.Id == NadekoBot.OwnerID) + { + try + { + if (StatsCollector.DeleteRequest(int.Parse(e.Args[0]))) + { + await client.SendMessage(e.Channel, Mention.User(e.User) + " Request deleted."); + } + else + { + await client.SendMessage(e.Channel, "No request on that number."); + } + } + catch + { + await client.SendMessage(e.Channel, "Error deleting request, probably NaN error."); + } + } + else await client.SendMessage(e.Channel, "You don't have permission to do that."); + }); + + CreateCommand(cgb, "rr") + .Description("Resolves a request. Only owner is able to do this.") + .Parameter("reqNumber", ParameterType.Required) + .Do(async e => + { + if (e.User.Id == NadekoBot.OwnerID) + { + try + { + var sc = StatsCollector.ResolveRequest(int.Parse(e.Args[0])); + if (sc != null) + { + await client.SendMessage(e.Channel, Mention.User(e.User) + " Request resolved, notice sent."); + await client.SendPrivateMessage(client.GetUser(client.GetServer(sc.ServerId), sc.Id), "**This request of yours has been resolved:**\n" + sc.Text); + } + else + { + await client.SendMessage(e.Channel, "No request on that number."); + } + } + catch + { + await client.SendMessage(e.Channel, "Error resolving request, probably NaN error."); + } + } + else await client.SendMessage(e.Channel, "You don't have permission to do that."); + }); + + CreateCommand(cgb, "clr") + .Description("Clears some of nadeko's messages from the current channel.") + .Do(async e => + { + try + { + if (e.Channel.Messages.Count() < 50) + { + await client.DownloadMessages(e.Channel, 100); + } + + await client.DeleteMessages(e.Channel.Messages.Where(msg => msg.User.Id == client.CurrentUser.Id)); + + } + catch (Exception) + { + await client.SendMessage(e.Channel, "I cant do it :("); + } + }); + + CreateCommand(cgb, "call") + .Description("Useless. Writes calling @X to chat.\nUsage: @NadekoBot call @X ") + .Parameter("who", ParameterType.Required) + .Do(async e => + { + await client.SendMessage(e.Channel, "Calling " + e.Args[0] + "..."); + }); + + cgb.CreateCommand("cid") + .Description("Shows current channel id") + .Do(async e => + { + await client.SendMessage(e.Channel, "This channel's id is " + e.Channel.Id); + }); + + cgb.CreateCommand("sid") + .Description("Shows current server id") + .Do(async e => + { + await client.SendMessage(e.Channel, "This server's id is " + e.Server.Id); + }); + CreateCommand(cgb, "hide") + .Description("Hides nadeko in plain sight!11!!") + .Do(async e => + { + try + { + using (MemoryStream ms = new MemoryStream()) + using (Image img = Image.FromFile("images/hidden.png")) + { + img.Save(ms, System.Drawing.Imaging.ImageFormat.Png); + + await client.EditProfile("", null, null, null, ms, ImageType.Png); + } + } + catch (Exception ex) + { + StatsCollector.DEBUG_LOG(ex.ToString()); + } + }); + + CreateCommand(cgb, "unhide") + .Description("Hides nadeko in plain sight!11!!") + .Do(async e => + { + try + { + using (MemoryStream ms = new MemoryStream()) + using (Image img = Image.FromFile("images/nadeko.jpg")) + { + img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); + + await client.EditProfile("", null, null, null,ms, ImageType.Jpeg); + } + } + catch (Exception ex) + { + StatsCollector.DEBUG_LOG(ex.ToString()); + } + }); + CreateCommand(cgb, "stats") + .Description("Shows some basic stats for nadeko") + .Do(async e => + { + int serverCount = client.AllServers.Count(); + int uniqueUserCount = client.AllUsers.Count(); + var time = (DateTime.Now - Process.GetCurrentProcess().StartTime); + string uptime = " " + time.Days + " days, " + time.Hours + " hours, and " + time.Minutes + " minutes."; + + await client.SendMessage(e.Channel, String.Format("```Servers: {0}\nUnique Users: {1}\nUptime: {2}\nMy id is: {3}```", serverCount, uniqueUserCount, uptime, client.CurrentUserId)); + }); + + + //TODO add eval + /* + cgb.CreateCommand(">") + .Parameter("code", ParameterType.Unparsed) + .Do(async e => + { + if (e.Message.User.Id == NadekoBot.OwnerId) + { + var result = await CSharpScript.EvaluateAsync(e.Args[0]); + await client.SendMessage(e.Channel, result?.ToString() ?? "null"); + return; + } + });*/ + }); + } + + public void RipName(string name) + { + Bitmap bm = new Bitmap(Image.FromFile(@"images\rip.png")); + + int offset = name.Length * 5; + + int fontSize = 20; + + if (name.Length > 10) + { + fontSize -= (name.Length - 10) / 2; + } + + //TODO use measure string + Graphics g = Graphics.FromImage(bm); + g.DrawString(name, new Font("Comic Sans MS", fontSize, FontStyle.Bold), Brushes.Black, 100 - offset, 200); + g.DrawString("? - " + DateTime.Now.Year, new Font("Consolas", 12, FontStyle.Bold), Brushes.Black, 80, 235); + g.Flush(); + g.Dispose(); + + bm.Save(@"images\ripnew.png"); + } + + private Func SayYes() + { + return async e => + { + await NadekoBot.client.SendMessage(e.Channel, "Yes. :)"); + }; + } + } +} diff --git a/NadekoBot/Modules/DiscordModule.cs b/NadekoBot/Modules/DiscordModule.cs new file mode 100644 index 00000000..2ad0d20d --- /dev/null +++ b/NadekoBot/Modules/DiscordModule.cs @@ -0,0 +1,20 @@ +using Discord.Modules; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Modules +{ + abstract class DiscordModule : IModule + { + public List commands; + + protected DiscordModule() { + commands = new List(); + } + + public abstract void Install(ModuleManager manager); + } +} diff --git a/NadekoBot/Modules/Gambling.cs b/NadekoBot/Modules/Gambling.cs new file mode 100644 index 00000000..2565ecc3 --- /dev/null +++ b/NadekoBot/Modules/Gambling.cs @@ -0,0 +1,23 @@ +using Discord.Commands; +using Discord.Modules; + +namespace NadekoBot.Modules +{ + class Gambling : DiscordModule + { + + public Gambling() { + commands.Add(new DrawCommand()); + commands.Add(new FlipCoinCommand()); + commands.Add(new DiceRollCommand()); + } + + public override void Install(ModuleManager manager) + { + manager.CreateCommands("", cgb => + { + commands.ForEach(com => com.Init(cgb)); + }); + } + } +} diff --git a/NadekoBot/Modules/Games.cs b/NadekoBot/Modules/Games.cs new file mode 100644 index 00000000..cbe17bc2 --- /dev/null +++ b/NadekoBot/Modules/Games.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Modules; + +namespace NadekoBot.Modules +{ + class Games : DiscordModule + { + public Games() : base() { + commands.Add(new Trivia()); + } + + public override void Install(ModuleManager manager) + { + manager.CreateCommands("", cgb => + { + commands.ForEach(cmd => cmd.Init(cgb)); + }); + } + } +} diff --git a/NadekoBot/Modules/Music.cs b/NadekoBot/Modules/Music.cs new file mode 100644 index 00000000..82fa1bfe --- /dev/null +++ b/NadekoBot/Modules/Music.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Modules; +using Discord.Commands; +using System.IO; +using Discord; +using Discord.Audio; +using System.Collections.Concurrent; +using VideoLibrary; +using System.Threading; +using System.Diagnostics; + +namespace NadekoBot.Modules +{ + class Music : DiscordModule + { + private static bool exit = true; + + public static bool NextSong = false; + public static IDiscordVoiceClient Voice; + public static Channel VoiceChannel; + public static bool Pause = false; + public static List SongQueue = new List(); + + public static YouTubeVideo CurrentSong; + + public static bool Exit + { + get { return exit; } + set { exit = value;} // if i set this to true, break the song and exit the main loop + } + + public Music() : base() { + //commands.Add(new PlayMusic()); + } + + //m r,radio - init + //m n,next - next in que + //m p,pause - pauses, call again to unpause + //m yq [key_words] - queue from yt by keywords + //m s,stop - stop + //m sh - shuffle songs + //m pl - current playlist + + + public override void Install(ModuleManager manager) + { + var client = NadekoBot.client; + manager.CreateCommands("!m", cgb => + { + //queue all more complex commands + commands.ForEach(cmd => cmd.Init(cgb)); + + cgb.CreateCommand("n") + .Alias("next") + .Description("Goes to the next song in the queue.") + .Do(e => + { + if (Voice != null && Exit == false) + { + NextSong = true; + } + }); + + cgb.CreateCommand("s") + .Alias("stop") + .Description("Completely stops the music and unbinds the bot from the channel.") + .Do(e => + { + if (Voice != null && Exit == false) + { + Exit = true; + SongQueue = new List(); + } + }); + + cgb.CreateCommand("p") + .Alias("pause") + .Description("Pauses the song") + .Do(async e => + { + if (Voice != null && Exit == false && CurrentSong != null) + { + Pause = !Pause; + if (Pause) + { + await client.SendMessage(e.Channel, "Pausing. Run the command again to resume."); + } + else + { + await client.SendMessage(e.Channel, "Resuming..."); + } + } + }); + + cgb.CreateCommand("q") + .Alias("yq") + .Description("Queue a song using a multi/single word name.\nUsage: `!m q Dream Of Venice`") + .Parameter("Query", ParameterType.Unparsed) + .Do(async e => + { + var youtube = YouTube.Default; + var video = youtube.GetAllVideos(Searches.FindYoutubeUrlByKeywords(e.Args[0])) + .Where(v => v.AdaptiveKind == AdaptiveKind.Audio) + .OrderByDescending(v => v.AudioBitrate).FirstOrDefault(); + + if (video?.Uri != "" && video.Uri != null) + { + SongQueue.Add(video); + await client.SendMessage(e.Channel, "**Queued** " + video.FullName); + } + }); + + cgb.CreateCommand("lq") + .Alias("ls").Alias("lp") + .Description("Lists up to 10 currently queued songs.") + .Do(async e => + { + await client.SendMessage(e.Channel, SongQueue.Count + " videos currently queued."); + await client.SendMessage(e.Channel, string.Join("\n", SongQueue.Select(v => v.FullName).Take(10))); + }); + + cgb.CreateCommand("sh") + .Description("Shuffles the current playlist.") + .Do(async e => + { + if (SongQueue.Count < 2) + { + await client.SendMessage(e.Channel, "Not enough songs in order to perform the shuffle."); + return; + } + + SongQueue.Shuffle(); + await client.SendMessage(e.Channel, "Songs shuffled!"); + }); + + cgb.CreateCommand("radio") + .Alias("music") + .Description("Binds to a voice and text channel in order to play music.") + .Parameter("ChannelName", ParameterType.Unparsed) + .Do(async e => + { + if (Voice != null) return; + VoiceChannel = client.FindChannels(e.Server, e.GetArg("ChannelName").Trim(), ChannelType.Voice).FirstOrDefault(); + Voice = await client.JoinVoiceServer(VoiceChannel); + Exit = false; + NextSong = false; + Pause = false; + try + { + while (true) + { + if (Exit) break; + if (SongQueue.Count == 0 || Pause) { Thread.Sleep(100); continue; } + if (!LoadNextSong()) break; + + await Task.Run(async () => + { + if (Exit) + { + Voice = null; + Exit = false; + await client.SendMessage(e.Channel, "Exiting..."); + return; + } + int blockSize = 1920; + byte[] buffer = new byte[1920]; + //float multiplier = 1.0f / 48000 / 2; + + var msg = await client.SendMessage(e.Channel, "Playing " + Music.CurrentSong.FullName + " [00:00]"); + int counter = 0; + int byteCount; + using (var stream = GetAudioFileStream(Music.CurrentSong.Uri)) + { + while ((byteCount = stream.Read(buffer, 0, blockSize)) > 0) + { + Voice.SendVoicePCM(buffer, byteCount); + counter += blockSize; + if (NextSong) + { + NextSong = false; + break; + } + if (Exit) + { + Exit = false; + return; + } + while (Pause) Thread.Sleep(100); + } + } + }); + } + await Voice.WaitVoice(); + } + catch (Exception ex) { Console.WriteLine(ex.ToString()); } + await client.LeaveVoiceServer(VoiceChannel.Server); + Voice = null; + VoiceChannel = null; + }); + }); + } + + private Stream GetAudioFileStream(string file) + { + Process p = Process.Start(new ProcessStartInfo() + { + FileName = "ffmpeg", + Arguments = "-i \"" + Uri.EscapeUriString(file) + "\" -f s16le -ar 48000 -af volume=1 -ac 1 pipe:1 ", + UseShellExecute = false, + RedirectStandardOutput = true + }); + return p.StandardOutput.BaseStream; + } + + private bool LoadNextSong() + { + if (SongQueue.Count == 0) { + CurrentSong = null; + return false; + } + CurrentSong = SongQueue[0]; + SongQueue.RemoveAt(0); + return true; + } + } +} diff --git a/NadekoBot/Modules/Searches.cs b/NadekoBot/Modules/Searches.cs new file mode 100644 index 00000000..6d41dc2b --- /dev/null +++ b/NadekoBot/Modules/Searches.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Modules; +using System.Net; +using System.IO; +using Newtonsoft.Json.Linq; +using System.Text.RegularExpressions; +using System.Reflection; +using System.Xml; +using Newtonsoft.Json; +using System.Net.Http; + +namespace NadekoBot.Modules +{ + class Searches : DiscordModule + { + public Searches() : base() + { + + } + + public override void Install(ModuleManager manager) + { + var client = NadekoBot.client; + + manager.CreateCommands("",cgb => + { + cgb.CreateCommand("~av") + .Parameter("mention", Discord.Commands.ParameterType.Required) + .Do(async e => + { + if (e.Message.MentionedUsers.Count() == 0) { + await client.SendMessage(e.Channel, "You need to mention a person"); + return; + } + string av = e.Message.MentionedUsers.First().AvatarUrl; + await client.SendMessage(e.Channel, ShortenUrl("http://www.discordapp.com/api/" + av)); + + }); + cgb.CreateCommand("~yt") + .Parameter("query",Discord.Commands.ParameterType.Unparsed) + .Description("Queries youtubes and embeds the first result") + .Do(async e => + { + if (!(await ValidateQuery(e.Channel, e.GetArg("query")))) return; + + var str = ShortenUrl(FindYoutubeUrlByKeywords(e.GetArg("query"))); + if (str == null || str.Trim().Length < 5) + { + await client.SendMessage(e.Channel, "Query failed"); + return; + } + await client.SendMessage(e.Channel, str); + }); + + cgb.CreateCommand("~ani") + .Alias("~anime").Alias("~aq") + .Parameter("query", Discord.Commands.ParameterType.Unparsed) + .Description("Queries anilist for an anime and shows the first result.") + .Do(async e => + { + if (!(await ValidateQuery(e.Channel, e.GetArg("query")))) return; + + var result = GetAnimeQueryResultLink(e.GetArg("query")); + if (result == null) { + await client.SendMessage(e.Channel, "Failed to find that anime."); + return; + } + + await client.SendMessage(e.Channel,result.ToString()); + }); + + cgb.CreateCommand("~mang") + .Alias("~manga").Alias("~mq") + .Parameter("query", Discord.Commands.ParameterType.Unparsed) + .Description("Queries anilist for a manga and shows the first result.") + .Do(async e => + { + if (!(await ValidateQuery(e.Channel, e.GetArg("query")))) return; + + var result = GetMangaQueryResultLink(e.GetArg("query")); + if (result == null) + { + await client.SendMessage(e.Channel, "Failed to find that anime."); + return; + } + await client.SendMessage(e.Channel, result.ToString()); + }); + }); + } + + private string token = ""; + private AnimeResult GetAnimeQueryResultLink(string query) + { + try + { + var cl = new RestSharp.RestClient("https://anilist.co/api"); + var rq = new RestSharp.RestRequest("/auth/access_token", RestSharp.Method.POST); + + RefreshToken(); + + rq = new RestSharp.RestRequest("/anime/search/" + Uri.EscapeUriString(query)); + rq.AddParameter("access_token", token); + + var smallObj = JArray.Parse(cl.Execute(rq).Content)[0]; + + rq = new RestSharp.RestRequest("anime/" + smallObj["id"]); + rq.AddParameter("access_token", token); + return JsonConvert.DeserializeObject(cl.Execute(rq).Content); + } + catch (Exception) + { + return null; + } + } + + private MangaResult GetMangaQueryResultLink(string query) + { + try + { + RefreshToken(); + + var cl = new RestSharp.RestClient("https://anilist.co/api"); + var rq = new RestSharp.RestRequest("/auth/access_token", RestSharp.Method.POST); + rq = new RestSharp.RestRequest("/manga/search/"+Uri.EscapeUriString(query)); + rq.AddParameter("access_token", token); + + var smallObj = JArray.Parse(cl.Execute(rq).Content)[0]; + + rq = new RestSharp.RestRequest("manga/" + smallObj["id"]); + rq.AddParameter("access_token", token); + return JsonConvert.DeserializeObject (cl.Execute(rq).Content); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + return null; + } + } + + private void RefreshToken() + { + var cl = new RestSharp.RestClient("https://anilist.co/api"); + var rq = new RestSharp.RestRequest("/auth/access_token", RestSharp.Method.POST); + rq.AddParameter("grant_type", "client_credentials"); + rq.AddParameter("client_id", "kwoth-w0ki9"); + rq.AddParameter("client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"); + token = JObject.Parse(cl.Execute(rq).Content)["access_token"].ToString(); + } + + private static async Task ValidateQuery(Discord.Channel ch,string query) { + if (query == null || query.Trim().Length == 0) + { + await NadekoBot.client.SendMessage(ch, "Please specify search parameters."); + return false; + } + return true; + } + + public static string FindYoutubeUrlByKeywords(string v) + { + WebRequest wr = WebRequest.Create("https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=" + Uri.EscapeDataString(v) + "&key=" + NadekoBot.GoogleAPIKey); + + var sr = new StreamReader(wr.GetResponse().GetResponseStream()); + + dynamic obj = JObject.Parse(sr.ReadToEnd()); + return "http://www.youtube.com/watch?v=" + obj.items[0].id.videoId.ToString(); + } + + public static string ShortenUrl(string url) + { + var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/urlshortener/v1/url?key=" + NadekoBot.GoogleAPIKey); + httpWebRequest.ContentType = "application/json"; + httpWebRequest.Method = "POST"; + + using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) + { + string json = "{\"longUrl\":\"" + url + "\"}"; + streamWriter.Write(json); + } + try + { + var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); + using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) + { + var responseText = streamReader.ReadToEnd(); + string MATCH_PATTERN = @"""id"": ?""(?.+)"""; + return Regex.Match(responseText, MATCH_PATTERN).Groups["id"].Value; + } + } + catch (Exception ex) { Console.WriteLine(ex.ToString()); return ""; } + } + } +} diff --git a/NadekoBot/NadekoBot.cs b/NadekoBot/NadekoBot.cs new file mode 100644 index 00000000..ef7c106a --- /dev/null +++ b/NadekoBot/NadekoBot.cs @@ -0,0 +1,151 @@ +using Discord; +using System; +using System.IO; +using Newtonsoft.Json; +using Discord.Commands; +using Discord.Modules; +using System.Text.RegularExpressions; +using Parse; +using NadekoBot.Modules; +using System.Timers; + +namespace NadekoBot +{ + class NadekoBot + { + public static DiscordClient client; + public static StatsCollector sc; + public static string botMention; + public static string GoogleAPIKey; + public static long OwnerID; + + static void Main(string[] args) + { + //load credentials from credentials.json + Credentials c; + try + { + c = JsonConvert.DeserializeObject(File.ReadAllText("credentials.json")); + botMention = c.BotMention; + GoogleAPIKey = c.GoogleAPIKey; + OwnerID = c.OwnerID; + } + catch (Exception) + { + Console.WriteLine("Failed to load stuff from credentials.json, RTFM"); + Console.ReadKey(); + return; + } + + //init parse + if (c.ParseKey != null && c.ParseID != null) + ParseClient.Initialize(c.ParseID,c.ParseKey); + + //create new discord client + client = new DiscordClient(new DiscordClientConfig() { + VoiceMode = DiscordVoiceMode.Outgoing, + }); + + //create a command service + var commandService = new CommandService(new CommandServiceConfig + { + CommandChar = null, + HelpMode = HelpMode.Disable + }); + + //monitor commands for logging + sc = new StatsCollector(commandService); + + //add command service + var commands = client.AddService(commandService); + + //help command + commands.CreateCommand("-h") + .Alias(new string[]{"-help",NadekoBot.botMention+" help", NadekoBot.botMention+" h"}) + .Description("Help command") + .Do(async e => + { + string helpstr = ""; + foreach (var com in client.Commands().AllCommands) { + helpstr += "&###**#" + com.Category + "#**\n"; + helpstr += PrintCommandHelp(com); + } + while (helpstr.Length > 2000) { + var curstr = helpstr.Substring(0, 2000); + await client.SendPrivateMessage(e.User, curstr.Substring(0,curstr.LastIndexOf("&"))); + helpstr = curstr.Substring(curstr.LastIndexOf("&")) + helpstr.Substring(2000); + } + await client.SendPrivateMessage(e.User, helpstr); + }); + + //create module service + var modules = client.AddService(new ModuleService()); + + //install modules + modules.Install(new Conversations(), "Conversation", FilterType.Unrestricted); + modules.Install(new Gambling(), "Gambling", FilterType.Unrestricted); + modules.Install(new Games(), "Games", FilterType.Unrestricted); + modules.Install(new Music(), "Music", FilterType.Unrestricted); + modules.Install(new Searches(), "Searches", FilterType.Unrestricted); + + commands.CommandError += Commands_CommandError; + + //run the bot + client.Run(async () => + { + Console.WriteLine("Trying to connect..."); + try + { + await client.Connect(c.Username, c.Password); + Console.WriteLine("Connected!"); + } + catch (Exception ex) { + Console.WriteLine(ex.ToString()); + } + }); + Console.WriteLine("Exiting..."); + Console.ReadKey(); + } + + private static void Commands_CommandError(object sender, CommandErrorEventArgs e) + { + if (e.Command != null) + client.SendMessage(e.Channel, Mention.User(e.User) + " Command failed. See help (-h)."); + } + + private static string PrintCommandHelp(Command com) + { + var str = "`" + com.Text + "`\n"; + foreach (var a in com.Aliases) + str += "`" + a + "`\n"; + str += "Description: " + com.Description + "\n"; + return str; + } + /* removed + private static void Crawl() + { + Timer t = new Timer(); + t.Interval = 5000; // start crawling after 5 seconds + t.Elapsed += (s, e) => { + var wc = new WebCrawler.WebCrawler(); + WebCrawler.WebCrawler.OnFoundInvite += inv => { TryJoin(inv); }; + t.Stop(); + }; + t.Start(); + } + */ + + private static async void TryJoin(string code) + { + try + { + await NadekoBot.client.AcceptInvite(await NadekoBot.client.GetInvite(code)); + File.AppendAllText("invites.txt", code + "\n"); + } + catch (Exception) + { + StatsCollector.DEBUG_LOG("Failed to join " + code); + } + } + } +} \ No newline at end of file diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj new file mode 100644 index 00000000..f3f22964 --- /dev/null +++ b/NadekoBot/NadekoBot.csproj @@ -0,0 +1,117 @@ + + + + + Debug + AnyCPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46} + Exe + Properties + NadekoBot + NadekoBot + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + E:\Ostalo\Discord.Net\src\Discord.Net.Modules.Net45\bin\Debug\Discord.Net.dll + + + E:\Ostalo\Discord.Net\src\Discord.Net.Modules.Net45\bin\Debug\Discord.Net.Commands.dll + + + E:\Ostalo\Discord.Net\src\Discord.Net.Modules.Net45\bin\Debug\Discord.Net.Modules.dll + + + ..\packages\VideoLibrary.1.3.1\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\libvideo.dll + True + + + ..\packages\Newtonsoft.Json.8.0.1-beta3\lib\net45\Newtonsoft.Json.dll + True + + + ..\packages\Parse.1.6.2\lib\net45\Parse.dll + True + + + ..\packages\Parse.1.6.2\lib\net45\Parse.NetFx45.dll + True + + + ..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll + True + + + ..\packages\RestSharp.105.2.3\lib\net452\RestSharp.dll + True + + + + + + + + + + + + ..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NadekoBot/Properties/AssemblyInfo.cs b/NadekoBot/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..7b8bf2aa --- /dev/null +++ b/NadekoBot/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NadekoBot")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NadekoBot")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("27a886f5-cdda-4f4a-81ee-6dafcce9de46")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NadekoBot/StatsCollector.cs b/NadekoBot/StatsCollector.cs new file mode 100644 index 00000000..0885466d --- /dev/null +++ b/NadekoBot/StatsCollector.cs @@ -0,0 +1,207 @@ +using Discord; +using Discord.Commands; +using Discord.Modules; +using Parse; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Timers; + +namespace NadekoBot +{ + public class StatsCollector + { + + private CommandService _service; + + string lastMention = ""; + string lastMessage = "No messages."; + int commandsRan = 0; + string dataLastSent = "Data last sent at: NEVER"; + + List messages = new List(); + + public StatsCollector(CommandService service) + { + this._service = service; + + _service.RanCommand += StatsCollector_RanCommand; + NadekoBot.client.MessageReceived += Client_MessageReceived; + /* + Timer t = new Timer(); + + t.Interval = 5000; + + t.Elapsed += (s,e) => + { + FillConsole(); + }; + + t.Start(); + */ + + StartCollecting(); + } + + private void FillConsole() { + Console.Clear(); + + var time = (DateTime.Now - Process.GetCurrentProcess().StartTime); + string str = "Online for " + time.Days + "d, " + time.Hours + "h, " + time.Minutes + "m, "+time.Seconds+"s."; + + Console.SetCursorPosition(0, 0); + Console.Write(str); + + Console.SetCursorPosition(0, 1); + Console.Write(dataLastSent); + + Console.SetCursorPosition(0, 2); + Console.Write("Commands ran since start: " +commandsRan); + + Console.SetCursorPosition(0, 3); + Console.Write(lastMention); + + Console.SetCursorPosition(0, 4); + Console.WriteLine(lastMessage); + } + + private void Client_MessageReceived(object sender, MessageEventArgs e) + { + + lastMessage = "[" + e.User.Name + "] on [" + e.Server.Name + "] server, channel: [" + e.Channel.Name + "] \n" + "Body: " + e.Message.Text + " "; + + if (e.Message.MentionedUsers.Where(u => u.Id == NadekoBot.OwnerID).Count() > 0) + { + lastMention = "You were last mentioned in '" + e.Server.Name + "' server, channel '" + e.Channel.Name + "', by " + e.User.Name; + } + + } + + private async void TryJoin(MessageEventArgs e, string code) { + try + { + await NadekoBot.client.AcceptInvite(await NadekoBot.client.GetInvite(code)); + await NadekoBot.client.SendMessage(e.Channel, Mention.User(e.User) + " I joined it, thanks :)"); + DEBUG_LOG("Sucessfuly joined server with code " + code); + DEBUG_LOG("Here is a link for you: discord.gg/" + code); + } + catch (Exception ex) { + DEBUG_LOG("Failed to join " + code); + DEBUG_LOG("Reason: " + ex.ToString()); + } + } + + public static void DEBUG_LOG(string text) { + NadekoBot.client.SendMessage(NadekoBot.client.GetChannel(119365591852122112), text); + } + + private void StartCollecting() { + Timer t = new Timer(); + t.Interval = 3600000; + t.Enabled = true; + t.Elapsed += (s, e) => + { + var obj = new ParseObject("Stats"); + dataLastSent = "Data last sent at: "+DateTime.Now.Hour+":"+DateTime.Now.Minute; + obj["OnlineUsers"] = NadekoBot.client.AllUsers.Count(); + obj["ConnectedServers"] = NadekoBot.client.AllServers.Count(); + + obj.SaveAsync(); + }; + Console.WriteLine("Server stats sent."); + } + + public static void SaveRequest(CommandEventArgs e, string text) { + + var obj = new ParseObject("Requests"); + + obj["ServerId"] = e.Server.Id; + obj["ServerName"] = e.Server.Name; + obj["UserId"] = e.User.Id; + obj["UserName"] = e.User.Name; + obj["Request"] = text; + + obj.SaveAsync(); + } + + public static string GetRequests() { + var task = ParseObject.GetQuery("Requests") + .FindAsync().Result; + + string str = "Here are all current requests for NadekoBot:\n\n"; + int i = 1; + foreach (var reqObj in task) + { + + str += (i++) + ". by **" + reqObj["UserName"] +"** from **" + reqObj["ServerName"] + "** at "+ reqObj.CreatedAt.Value.ToLocalTime() + "\n"; + str+= "**"+reqObj["Request"]+"**\n----------\n"; + } + return str+"\n__Type [@NadekoBot clr] to clear all of my messages.__"; + } + + public static bool DeleteRequest(int requestNumber) { + var task = ParseObject.GetQuery("Requests") + .FindAsync().Result; + int i = 1; + foreach (var reqObj in task) + { + if (i == requestNumber) + { + reqObj.DeleteAsync(); + return true; + } + i++; + } + return false; + } + /// + /// Resolves a request with a number and returns that users id. + /// + /// RequestObject of the request. Null if none + public static ResolveRequestObject ResolveRequest(int requestNumber) { + var task = ParseObject.GetQuery("Requests") + .FindAsync().Result; + int i = 1; + foreach (var reqObj in task) + { + if (i == requestNumber) { + var txt = reqObj.Get("Request"); + var id = reqObj.Get("UserId"); + var sid = reqObj.Get("ServerId"); + reqObj.DeleteAsync(); + return new ResolveRequestObject { Id = id, Text = txt, ServerId=sid }; + } + i++; + } + return null; + } + + public class ResolveRequestObject { + public long Id; + public long ServerId; + public string Text; + } + + private void StatsCollector_RanCommand(object sender, CommandEventArgs e) + { + commandsRan++; + var obj = new ParseObject("CommandsRan"); + + obj["ServerId"] = e.Server.Id; + obj["ServerName"] = e.Server.Name; + + obj["ChannelId"] = e.Channel.Id; + obj["ChannelName"] = e.Channel.Name; + + obj["UserId"] = e.User.Id; + obj["UserName"] = e.User.Name; + + obj["CommandName"] = e.Command.Text; + obj.SaveAsync(); + } + } +} diff --git a/NadekoBot/packages.config b/NadekoBot/packages.config new file mode 100644 index 00000000..edde9f6f --- /dev/null +++ b/NadekoBot/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/dlls/Discord.Net.Commands.dll b/dlls/Discord.Net.Commands.dll new file mode 100644 index 00000000..87935ad0 Binary files /dev/null and b/dlls/Discord.Net.Commands.dll differ diff --git a/dlls/Discord.Net.Modules.dll b/dlls/Discord.Net.Modules.dll new file mode 100644 index 00000000..e442d232 Binary files /dev/null and b/dlls/Discord.Net.Modules.dll differ diff --git a/dlls/Discord.Net.dll b/dlls/Discord.Net.dll new file mode 100644 index 00000000..08dc588a Binary files /dev/null and b/dlls/Discord.Net.dll differ diff --git a/dlls/lib/libsodium.dll b/dlls/lib/libsodium.dll new file mode 100644 index 00000000..a9ab5078 Binary files /dev/null and b/dlls/lib/libsodium.dll differ diff --git a/dlls/lib/opus.dll b/dlls/lib/opus.dll new file mode 100644 index 00000000..a9eec802 Binary files /dev/null and b/dlls/lib/opus.dll differ diff --git a/dlls/websocket-sharp.dll b/dlls/websocket-sharp.dll new file mode 100644 index 00000000..19b6cc74 Binary files /dev/null and b/dlls/websocket-sharp.dll differ