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