diff --git a/ISurvivalBot.sln b/ISurvivalBot.sln new file mode 100644 index 0000000..923daee --- /dev/null +++ b/ISurvivalBot.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31205.134 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ISurvivalBot", "ISurvivalBot\ISurvivalBot.csproj", "{B09CDD35-8951-43C7-93FA-53A5EE6EAF40}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B09CDD35-8951-43C7-93FA-53A5EE6EAF40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B09CDD35-8951-43C7-93FA-53A5EE6EAF40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B09CDD35-8951-43C7-93FA-53A5EE6EAF40}.Debug|x64.ActiveCfg = Debug|x64 + {B09CDD35-8951-43C7-93FA-53A5EE6EAF40}.Debug|x64.Build.0 = Debug|x64 + {B09CDD35-8951-43C7-93FA-53A5EE6EAF40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B09CDD35-8951-43C7-93FA-53A5EE6EAF40}.Release|Any CPU.Build.0 = Release|Any CPU + {B09CDD35-8951-43C7-93FA-53A5EE6EAF40}.Release|x64.ActiveCfg = Release|x64 + {B09CDD35-8951-43C7-93FA-53A5EE6EAF40}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A81E7C66-5FD0-4B8C-A23C-3F518540F05D} + EndGlobalSection +EndGlobal diff --git a/ISurvivalBot/Commands/AudioModule.cs b/ISurvivalBot/Commands/AudioModule.cs new file mode 100644 index 0000000..bf59ed8 --- /dev/null +++ b/ISurvivalBot/Commands/AudioModule.cs @@ -0,0 +1,88 @@ +using Discord; +using Discord.Commands; +using ISurvivalBot.Services; +using ISurvivalBot.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ISurvivalBot.Commands +{ + public class AudioModule : ModuleBase + { + // Scroll down further for the AudioService. + // Like, way down + private readonly AudioService _service; + + // Remember to add an instance of the AudioService + // to your IServiceCollection when you initialize your bot + public AudioModule(AudioService service) + { + _service = service; + } + + // You *MUST* mark these commands with 'RunMode.Async' + // otherwise the bot will not respond until the Task times out. + [Command("joins", RunMode = RunMode.Async)] + [RequireContext(ContextType.Guild, ErrorMessage = "This command is only possible via a public channel.")] + //[RequireBotPermission(ChannelPermission.Speak, ErrorMessage = "I don't have permissions to join a voice chat")] + public async Task JoinCmd() + { + var result = await _service.JoinAudio(Context.Guild, (Context.User as IVoiceState).VoiceChannel); + await Context.Message.AddReactionAsync(result == AudioServiceStatus.Succes ? CommonEmoij.OK : CommonEmoij.NOK); + + /*Emoji custom = null; + foreach(var emoij in Context.Guild.Emotes) + { + Console.WriteLine($"Name: {emoij.Name} Url: {emoij.Url}, Id: {emoij.Id}"); + }*/ + await Context.Message.AddReactionAsync(CommonEmoij.BEREND); + } + + // Remember to add preconditions to your commands, + // this is merely the minimal amount necessary. + // Adding more commands of your own is also encouraged. + [Command("leaves", RunMode = RunMode.Async)] + [RequireContext(ContextType.Guild, ErrorMessage = "This command is only possible via a public channel.")] + public async Task LeaveCmd() + { + var result = await _service.LeaveAudio(Context.Guild, (Context.User as IVoiceState).VoiceChannel); + await Context.Message.AddReactionAsync(result == AudioServiceStatus.Succes ? CommonEmoij.OK : CommonEmoij.NOK); + } + + [Command("plays", RunMode = RunMode.Async)] + [RequireContext(ContextType.Guild, ErrorMessage = "This command is only possible via a public channel.")] + public async Task PlayCmd([Remainder] string song) + { + var result = await _service.PlaySound(Context.Guild, Context.Channel, (Context.User as IVoiceState).VoiceChannel, song); + await Context.Message.AddReactionAsync(result == AudioServiceStatus.Succes ? CommonEmoij.OK : CommonEmoij.NOK); + } + + [Command("stops", RunMode = RunMode.Async)] + [RequireContext(ContextType.Guild, ErrorMessage = "This command is only possible via a public channel.")] + public async Task StopCmd() + { + var result = await _service.StopPlaying(Context.Guild, (Context.User as IVoiceState).VoiceChannel); + await Context.Message.AddReactionAsync(result == AudioServiceStatus.Succes ? CommonEmoij.OK : CommonEmoij.NOK); + } + + [Command("say", RunMode = RunMode.Async)] + [RequireContext(ContextType.Guild, ErrorMessage = "This command is only possible via a public channel.")] + public async Task SayText([Remainder] string text) + { + var result = await _service.SayText(Context.Guild, (Context.User as IVoiceState).VoiceChannel, Context.Channel, text); + await Context.Message.AddReactionAsync(result == AudioServiceStatus.Succes ? CommonEmoij.OK : CommonEmoij.NOK); + } + + [Command("listaudio", RunMode = RunMode.Async)] + public async Task ListAudioFiles() + { + var result = await _service.ListAudio(Context); + await Context.Message.AddReactionAsync(result == AudioServiceStatus.Succes ? CommonEmoij.OK : CommonEmoij.NOK); + } + + } +} diff --git a/ISurvivalBot/Commands/JamaarCommand.cs b/ISurvivalBot/Commands/JamaarCommand.cs new file mode 100644 index 0000000..5fc672d --- /dev/null +++ b/ISurvivalBot/Commands/JamaarCommand.cs @@ -0,0 +1,108 @@ +using Discord; +using Discord.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Commands +{ + public class JamaarCommand : ModuleBase + { + + + public static Random random = new Random(); + public List cards = new List() + { + "Nee, het zal je niet lukken", + "Ja, maar het zal niet makkelijk gaan", + "Ja, maar wees voorbereid op tegenslag", + "Ja, waag het", + "Ja, maar hou zelf het initiatief", + "Ja, het is het begin van een groots avontuur", + "Ja, maar alleen als je je kunt openstellen om te ontvangen", + "Nee, het zal je ongelukkig maken", + "Ja, maar blijf luisteren naar je lichaam", + "Nee, het is de moeite niet waard", + "Nee, je zal van jezelf vervreemden", + "Nee, het is niet je diepste verlangen", + "Ja, vecht ervoor", + "Ja, het zal je genezen", + "Nee, het gaat ten koste van je gezondheid", + "Ja, maar stop met zeuren en piekeren", + "Ja, zelfs als alle voortekenen ongunstig zijn", + "Ja, maar doe het tactvol", + "Ja, het zal je diepgaand veranderen", + "Ja, maar alleen als je je kunt verweren", + "Ja, maar je moet bereid zijn het risico echt te nemen", + "Nee, en trek je terug uit deze situatie", + "Nee, je doet het om je te conformeren", + "Nee, je motitieven zij niet zuiver", + "Nee, het is ondoordracht", + "Nee, het zal in alle opzichten tegenvallen", + "Nee, vergeet het", + "Nee en stop met zoeken naar wat je eigenlijk al hebt", + "Nee, het past niet bij je", + "Ja, maar vergeet niet dat het om de weg gaat, niet om de bestemming", + "Nee, het is een vlucht", + "Nee, wacht op een betere kans", + "Ja, het betekent een nieuwe en frisse start", + "Ja, maar alleen als je niet in goed en kwaad denkt", + "Ja, aanvaard het als een kostbaar geschenk", + "Ja, beslist!", + "Nee, het zal je teveel energie kosten", + "Ja en het zal het beste in jezelf naar boven halen", + "Nee, het zal je in gevaar brengen", + "Ja, maar verbrand je schepen achter je niet", + "Ja, de einige die twijfelt ben jij", + "Ja, je opent een geweldige energiebron", + "Ja, het zal zelfs makkelijker gaan dan je denkt", + "Ja en geniet er van", + "Ja, maar doe het met een glimlach", + "Ja, het is tijd voor een verandering", + "Ja, je bent er klaar voor", + "Ja, het zal je rust geven", + "Ja, het mag", + "Ja, dit is een once-in-a-lifetime mogelijkheid", + "Ja, maar blijf je hart volgen", + "Ja, maar accepteer dat je leven grondig zal veranderen", + "Nee, dit is niet het moment", + "Ja, maar let op de details", + "Ja, het is precies de goede stap op precies het goede moment", + "Ja, maar alleen als je geduld hebt", + "Ja, je bent het waard", + "Ja, maar met mededogen", + "Ja, maar alleen als het open & eerlijk kan", + "Nee, je verdient beter", + "Nee, doe eerst iets anders wat je op dit moment liever wilt", + "Ja, maar alleen als je het conflict niet uit de weg gaat", + "Nee, het is te hoog gegrepen", + "Nee, het is een illusie", + "Ja, maar alleen als je echt op succes bent voorbereid", + "Ja, maar loop niet te hard van stapel", + "Ja, dit is het moment", + "Ja en laat het los", + "Ja, maar accepteer onoplosbare conflicten", + "Ja, je zal er veel van leren", + "Ja, maar alleen als je je eigenbelang opzij kunt zetten", + "Ja, je bloed zal er sneller van stromen", + "Ja, maar houd afstand", + "Nee, je zal er onder bezwijken", + "Nee, zelfs als alle voortekenen gunstig zijn", + "Ja, het zal je gelukkig maken", + "Nee, je zal je vrijheid verliezen", + "Ja, maar vraag hulp" + + }; + + + + [Command("jamaar")] + public async Task Jamaar([Remainder] string text) + { + int answer = random.Next(this.cards.Count); + await Context.Message.ReplyAsync(this.cards[answer]); + } + } +} diff --git a/ISurvivalBot/Commands/MaintenanceCommand.cs b/ISurvivalBot/Commands/MaintenanceCommand.cs new file mode 100644 index 0000000..099970e --- /dev/null +++ b/ISurvivalBot/Commands/MaintenanceCommand.cs @@ -0,0 +1,63 @@ +using Discord; +using Discord.Commands; +using ISurvivalBot.Services; +using ISurvivalBot.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Commands +{ + + + [Group("maintenance")] + public class MaintenanceCommand : ModuleBase + { + + private readonly MaintenanceService _maintenanceService; + private readonly UserService _userService; + + public MaintenanceCommand(MaintenanceService maintenanceservice, UserService userService) + { + this._maintenanceService = maintenanceservice; + this._userService = userService; + } + + + [Command("update", RunMode = RunMode.Async)] + public async Task UpdateCommand() + { + await _maintenanceService.UpdateUser((long)Context.User.Id, Context.User.Username); + } + + + [Command("setadmin", RunMode = RunMode.Async)] + public async Task CreateAdminCommand(string username, bool admin) + { + bool isRequesterAdmin = await _userService.IsAdmin(Context.Message.Author.Username); + if (!isRequesterAdmin) + await Context.Message.AddReactionAsync(CommonEmoij.NOK); + + var mentionUser = Context.Message.MentionedUsers.FirstOrDefault(); + if (mentionUser != null) + username = mentionUser.Username; + + var result = await _userService.SetAdminStatus(username, admin); + await Context.Message.AddReactionAsync(result ? CommonEmoij.OK : CommonEmoij.NOK); + } + + + [Command("isadmin", RunMode = RunMode.Async)] + public async Task IsAdminCommand(string username) + { + var mentionUser = Context.Message.MentionedUsers.FirstOrDefault(); + bool result = await _userService.IsAdmin(mentionUser == null ? username : mentionUser.Username); + await Context.Message.AddReactionAsync(result ? CommonEmoij.OK : CommonEmoij.NOK); + + } + + + } +} diff --git a/ISurvivalBot/Commands/PublicModule.cs b/ISurvivalBot/Commands/PublicModule.cs new file mode 100644 index 0000000..52d8758 --- /dev/null +++ b/ISurvivalBot/Commands/PublicModule.cs @@ -0,0 +1,130 @@ +using Discord; +using Discord.Commands; +using ISurvivalBot.Services; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace ISurvivalBot.Commands +{ + // Modules must be public and inherit from an IModuleBase + public class PublicModule : ModuleBase + { + // Dependency Injection will fill this value in for us + public PictureService PictureService { get; set; } + + [Command("ping")] + [Alias("pong", "hello")] + public async Task PingAsync() + { + await Context.Message.ReplyAsync("pong!"); + } + + + [Command("cat")] + public async Task CatAsync() + { + // Get a stream containing an image of a cat + var stream = await PictureService.GetCatPictureAsync(); + // Streams must be seeked to their beginning before being uploaded! + stream.Seek(0, SeekOrigin.Begin); + await Context.Channel.SendFileAsync(stream, "cat.png"); + } + + [Command("catfact")] + public async Task CatFactAsync() + { + // Get random cat fact + var result = await PictureService.GetCatFact(); + await Context.Message.ReplyAsync(result); + } + + [Command("terry")] + public async Task TerryCommand() + { + Emoji custom = null; + foreach(var emoij in Context.Guild.Emotes) + { + Console.WriteLine($"Name: {emoij.Name} Url: {emoij.Url}, Id: {emoij.Id}"); + } + await Context.Message.AddReactionAsync(Emote.Parse("<:templeos:814952144851042364>")); + } + + + [Command("dog")] + public async Task DogAsync() + { + // Get a stream containing an image of a dog + var stream = await PictureService.GetDogPictureAsync(); + // Streams must be seeked to their beginning before being uploaded! + stream.Seek(0, SeekOrigin.Begin); + await Context.Channel.SendFileAsync(stream, "dog.png"); + } + + [Command("fox")] + public async Task FoxAsync() + { + // Get a stream containing an image of a dog + var stream = await PictureService.GetFoxPictureAsync(); + // Streams must be seeked to their beginning before being uploaded! + stream.Seek(0, SeekOrigin.Begin); + await Context.Channel.SendFileAsync(stream, "fox.png"); + } + + [Command("panda")] + public async Task PandaAsync() + { + // Get a stream containing an image of a dog + var stream = await PictureService.GetPandaPictureAsync(); + // Streams must be seeked to their beginning before being uploaded! + stream.Seek(0, SeekOrigin.Begin); + await Context.Channel.SendFileAsync(stream, "panda.png"); + } + + // Get info on a user, or the user who invoked the command if one is not specified + [Command("userinfo")] + public async Task UserInfoAsync(IUser user = null) + { + user = user ?? Context.User; + + await ReplyAsync(user.ToString()); + } + + + [Command("howto")] + public async Task HowTocommand(string question) + { + question = HttpUtility.UrlEncode(question); + await ReplyAsync($"https://letmegooglethat.com/?q={question}"); + } + + // Ban a user + [Command("ban")] + [RequireContext(ContextType.Guild)] + // make sure the user invoking the command can ban + [RequireUserPermission(GuildPermission.BanMembers, ErrorMessage = "You don't have the permissions to ban a user")] + // make sure the bot itself can ban + [RequireBotPermission(GuildPermission.BanMembers, ErrorMessage = "I don't have the permissions to ban a user")] + public async Task BanUserAsync(IGuildUser user, [Remainder] string reason = null) + { + await user.Guild.AddBanAsync(user, reason: reason); + await ReplyAsync("ok!"); + } + + // [Remainder] takes the rest of the command's arguments as one argument, rather than splitting every space + [Command("echo")] + public Task EchoAsync([Remainder] string text) + // Insert a ZWSP before the text to prevent triggering other bots! + => ReplyAsync('\u200B' + text); + + // 'params' will parse space-separated elements into a list + [Command("list")] + public Task ListAsync(params string[] objects) + => ReplyAsync("You listed: " + string.Join("; ", objects)); + + } +} diff --git a/ISurvivalBot/Commands/QuoteModule.cs b/ISurvivalBot/Commands/QuoteModule.cs new file mode 100644 index 0000000..f63bc2b --- /dev/null +++ b/ISurvivalBot/Commands/QuoteModule.cs @@ -0,0 +1,64 @@ +using Discord; +using Discord.Commands; +using ISurvivalBot.Services; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Commands +{ + + + [Group("quote")] + public class QuoteModule : ModuleBase + { + + + private readonly QuoteService _quoteService; + + //private readonly ILogger _logger; + + + public QuoteModule(/*ILogger logger,*/ QuoteService quoteService) { + //_logger = logger; + _quoteService = quoteService; + } + + + + [Command("add", RunMode = RunMode.Async)] + public async Task AddQuoteCommand([Remainder] string text) + { + await _quoteService.AddQuote(Context.User.Username, Context.Message.Content); + } + + + + [Command("random", RunMode = RunMode.Async)] + public async Task RandomQuoteCommand() + { + var quote = await _quoteService.RandomQuote(); + await Context.Message.ReplyAsync(quote); + } + + [Command("count", RunMode = RunMode.Async)] + public async Task QuoteCountCommand() + { + var quoteCount = await _quoteService.QuoteCount(); + await Context.Message.ReplyAsync($"Number of quotes in database {quoteCount}"); + } + + [Command("usercount", RunMode = RunMode.Async)] + public async Task QuoteCountUsercommand(string user) + { + var quoteCount = await _quoteService.QuoteCountUser(user); + await Context.Message.ReplyAsync($"Number of quotes by user {user}: {quoteCount}"); + } + + + + } +} diff --git a/ISurvivalBot/Commands/SheepCommand.cs b/ISurvivalBot/Commands/SheepCommand.cs new file mode 100644 index 0000000..2d4ebe5 --- /dev/null +++ b/ISurvivalBot/Commands/SheepCommand.cs @@ -0,0 +1,64 @@ +using Discord.Commands; +using ISurvivalBot.Models; +using System; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using System.Linq; + +namespace ISurvivalBot.Commands +{ + public class SheepCommand : ModuleBase + { + + private readonly BotContext _botContext; + //private static uint SheepCount = 0; + + + public SheepCommand(BotContext botContext) + { + this._botContext = botContext; + } + + [Command("meh")] + public async Task MehCommand() + { + int count = await SheepCounter((long)Context.User.Id); + await ReplyAsync($"{Context.User.Username} heeft {count} schaapjes geteld"); + } + + + public async Task SheepCounter(long userId) + { + int count = 0; + var commandItem = await _botContext.CommandCount.AsAsyncEnumerable().Where(cc => cc.Command == "meh" && cc.User.DiscordId == userId).FirstOrDefaultAsync(); + if (commandItem == null) + { + User user = await _botContext.Users.AsAsyncEnumerable().Where(u => u.DiscordId == userId).FirstAsync(); + CommandCount cc = new CommandCount + { + Command = "meh", + Count = 1, + User = user + }; + + await _botContext.CommandCount.AddAsync(cc); + await _botContext.SaveChangesAsync(); + return 1; + } + else + { + + + commandItem.Count++; + count = commandItem.Count; + _botContext.Update(commandItem); + await _botContext.SaveChangesAsync(); + } + + return count; + + } + } +} diff --git a/ISurvivalBot/Commands/ToevalCommand.cs b/ISurvivalBot/Commands/ToevalCommand.cs new file mode 100644 index 0000000..123eab1 --- /dev/null +++ b/ISurvivalBot/Commands/ToevalCommand.cs @@ -0,0 +1,158 @@ +using Discord; +using Discord.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Commands +{ + public class ToevalCommand : ModuleBase + { + public static Random random = new Random(); + public List cards = new List() + { + "Kun jij ook hinken op twee gedachten?", + "Kiezen voor liefde is soms loslaten.", + "MEESTERLIJK", + "Krap zitten maakt flexibel.", + "Welke steunpilaar gebruik jij vandaag?", + "Geef je hersens water tot ze bloeien.", + "Wat wil je niet voelen?", + "Selecteren, wat is dat?", + "Als jij het niet doet, wie doet het dan?", + "Jezelf straffen is \"moordend\".", + "Uitdagingen zijn er om problemen op te lossen.", + "Ga eens langs bij jezelf.", + "Het negatieve heb je ontdekt, nu nog het positieve.", + "Pak de kans om jezelf op te \"lichten\".", + "Licht zit in je ogen.", + "Ken jij de zon achter jouw wolken?", + "Morgen is over een jaar.", + "Kijk eens wat vaker in de spiegel van het niets.", + "Aan alles zit twee kanten, bekijk de andere kant ook eens.", + "Met wat vul jij je rukzak?", + "Mijn leraar wist het ook beter.", + "Wat heb je nodig om jezelf te ontmoeten?", + "Vergeten hoeft niet, vergeven wel.", + "Geloof jij ook dat je meer waard bent?", + "Goed is tweezijdig.", + "De bloemen zijn vandaag voor jou.", + "Tegemoettreden is nog geen ontmoeting.", + "Wanneer ben je in je eigen valkuil gestapt?", + "Is deze overtuiging van jezelf?", + "Je hoeftt niet uniek te worden, je bent het al.", + "Heb jij ook recht op je eigen ontdekking?", + "Ja maar, is nee.", + "Onthaasten, is stilstaan in stromend water.", + "Zijn is de aanvaardig dat je bent.", + "Gekleurd zijn geeft een palet aan mogelijkheden.", + "Dat wat je op je pad tegenkomt, heeft je vast iets te zeggen.", + "Is de ander verantwoordelijk voor jou leven?", + "Ga je nu goed met jezelf om?", + "Klein kan groot zijn.", + "Wij-Zij of Samen?", + "Lekkere brompot!", + "Wie het wil zien hoeft geen bril op te zetten.", + "Diep van binnen, zit daar ook wat?", + "Als leven leren is en leren is leven, houd je ogen dan open!", + "Ben je consument of je eigen criticus?", + "Je kan ook om de berg heen lopen.", + "Maak het onmogelijke mogelijk.", + "Doorzetten of drammen?", + "Drijf eens op de golven mee.", + "Vindt het gesprek op dit moment in je hoofd plaats?", + "Hoezo finish?", + "In welke \"Veilige\" onveiligheid zit ik?", + "Met welke overtuiging zet jij jezelf vast?", + "Stroop je mouwen eens naar beneden.", + "De gekookte kikker had ook niks in de gaten.", + "Genieten is ook leven.", + "Geduld is even noodzakelijk als volharding.", + "Hoor en wederhoor.", + "Angst, is de uitnodiging om te durven leren.", + "Wil je jezelf gelukkig zien?", + "Het weten van nu is niet het weten van toen.", + "Trek je jas maar eens uit!", + "Frustraties koesteren is: zitten in een hogedrukpan.", + "Wat is er mis aan succes?", + "Is veranderen alleen voor anderen?", + "Deden je ouders het ook zo?", + "Eenzaamheid kan je als zaaigoed gebruiken.", + "Wat, waarom en hoe is een drie-eenheid.", + "Deze storm gaat ook weer voorbij.", + "Driemaal links is ook rechts.", + "Als het beeld in beeld komt, verdwijnt een ander beeld.", + "Vandaag even niet.", + "Geweldadigheid kan je inruilen voor weldadig.", + "Knip eens een gaatje in je gedachte", + "Jouw rolmodel, had ook een rolmodel.", + "Ga maar, maar kom ook weer terug.", + "Beter dan best, waar haal je dat?", + "Confronteren is: springen in het diepe.", + "Zoek en jij zal het vinden.", + "Als je denkt het te weten, komt het niet-weten te voorschijn.", // Tot hier toevalstreffers + "Je bent eerlijk tegen mezelf!", + "Je gaat met mij om zoals je wilt dat jij met mij omgaat!", + "Je verandert wat ik anders wil!", + "Je kiest zelf met wie ik om wil gaan!", + "Je bent precies goed zoals ik ben!", + "Je doet het met plezier!", + "Je bent vrij!", + "Je mag een eigen mening hebben!", + "Je mag zeggen: ik weet het niet!", + "Je ben trots op mezelf!", + "Je zeg wat ik voel!", + "Je laat mijn problemen los!", + "Je mag nee zeggen!", + "Je luister naar mezelf door heel stil te zijn!", + "Je kan altijd opnieuw beginnen!", + "Je trekt het geluk naar mij toe!", + "Je bent goed in alles wat je doet!", + "Je bent je eigen beste vriend(in)!", + "Je doet alles met aandacht!", + "We vergeven je!", + "Je bent rijk!", + "Je mag fouten maken!", + "Je bent veilig!", + "Je ontmoet allemaal vrolijke mensen!", + "Je kunt zelf kiezen hoe je reageert in een situatie", + "Je bent dankbaar!", + "Je bent sterk!", + "Je bent mooi van binnen en buiten!", + "We gunnen je het!", + "Je ziet het goede in mensen!", + "Je denkt positief!", + "Je bent tevreden!", + "Je hebt respect voor anderen!", + "Je mag boos zijn!", + "Je mag anders zijn!", + "Je houdt van jezelf", + "Je vertrouwt om ons gevoel!", + "Je bent wijs", + "Je mag om hulp vragen!", + "Je bent een doorzetter!", + "Je zegt duidelijk wat je bedoeld", + "Je kunt het!", + "Je durft het, ook al voel je jezelf verlegen!", + "Je leert jezelf steeds beter kennen!", + "Je zorgt goed voor jezelf!", + "Je twijfelt, dus je kunt beter wachten.", + "Je leert ook van mogelijke dingen.", + "Je kiest je eigen weg.", + "Je mag verdrietig zijn!", + "Je bent zelf verantwoordelijk voor wat je doet.", + "Je neemt de tijd die je nodig hebt.", + "Je komt op voor jezelf." + }; + + + [Command("spreuk")] + public async Task Jamaar(/*[Remainder] string text*/) + { + int answer = random.Next(this.cards.Count); + await Context.Message.ReplyAsync(this.cards[answer]); + } + } +} diff --git a/ISurvivalBot/Commands/UserCommand.cs b/ISurvivalBot/Commands/UserCommand.cs new file mode 100644 index 0000000..57644eb --- /dev/null +++ b/ISurvivalBot/Commands/UserCommand.cs @@ -0,0 +1,85 @@ +using Discord.Commands; +using Discord; +using ISurvivalBot.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ISurvivalBot.Utils; + +namespace ISurvivalBot.Commands +{ + + + [Group("user")] + public class UserCommand : ModuleBase + { + private readonly CommandCountService _commandCountService; + private readonly UserService _userService; + + + public UserCommand(CommandCountService commandCountService, UserService userService) + { + this._userService = userService; + this._commandCountService = commandCountService; + } + + + [Command("countword")] + public async Task CountWord(string username, string commandReq) + { + var mentionUser = Context.Message.MentionedUsers.FirstOrDefault(); + string result = ""; + if (mentionUser != null) + { + result = await _commandCountService.GetCounterByUser(mentionUser.Username, commandReq); + } + else + { + result = await _commandCountService.GetCounterByUser(username, commandReq); + } + await Context.Message.ReplyAsync(result); + + } + + + [Command("isadmin", RunMode = RunMode.Async)] + public async Task IsAdminCommand(string username) + { + var mentionUser = Context.Message.MentionedUsers.FirstOrDefault(); + bool result = await _userService.IsAdmin(mentionUser == null ? username : mentionUser.Username); + await Context.Message.AddReactionAsync(result ? CommonEmoij.OK : CommonEmoij.NOK); + } + + + [Command("setadmin", RunMode = RunMode.Async)] + public async Task CreateAdminCommand(IUser user, bool admin) + { + bool isRequesterAdmin = await _userService.IsAdmin(Context.Message.Author.Username); + if (!isRequesterAdmin) + { + await Context.Message.AddReactionAsync(CommonEmoij.NOK); + return; + } + + var result = await _userService.SetAdminStatus(user.Username, admin); + await Context.Message.AddReactionAsync(result ? CommonEmoij.OK : CommonEmoij.NOK); + } + + [Command("resetwordcount")] + public async Task ResetWordCount(IUser user, string word) + { + bool isRequesterAdmin = await _userService.IsAdmin(Context.Message.Author.Username); + if (!isRequesterAdmin) + { + await Context.Message.AddReactionAsync(CommonEmoij.NOK); + return; + } + + var result = await _commandCountService.ResetWordCount((long)user.Id, word); + await Context.Message.AddReactionAsync(result ? CommonEmoij.OK : CommonEmoij.NOK); + } + + } +} diff --git a/ISurvivalBot/ISurvivalBot.csproj b/ISurvivalBot/ISurvivalBot.csproj new file mode 100644 index 0000000..5bfd2b7 --- /dev/null +++ b/ISurvivalBot/ISurvivalBot.csproj @@ -0,0 +1,26 @@ + + + + Exe + net5.0 + AnyCPU;x64 + + + + + + + + + + + + + + + + Always + + + + diff --git a/ISurvivalBot/Models/BotContext.cs b/ISurvivalBot/Models/BotContext.cs new file mode 100644 index 0000000..08f8caf --- /dev/null +++ b/ISurvivalBot/Models/BotContext.cs @@ -0,0 +1,59 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Models +{ + public class BotContext : DbContext + { + public DbSet Logs { get; set; } + public DbSet CommandCount { get; set; } + public DbSet Quotes { get; set; } + + private ILoggerFactory _loggingFactory; + + public DbSet Users { get; set; } + + + public BotContext(ILoggerFactory loggerFactory) + { + _loggingFactory = loggerFactory; + } + //public DbSet CommandCounters { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder options) + { + + string path = Path.Combine(Environment.CurrentDirectory, "bot.db"); + options.UseSqlite($"Data Source = {path}"); + options.UseLoggerFactory(_loggingFactory); + } + + internal void EnqureCreated() + { + /*foreach (var q in Quotes) + { + this.Quotes.Remove(q); + } + SaveChanges(); + this.Database.EnsureCreated();*/ + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(q => q.Id); + } + + /*public async Task SheepCommandCount() + { + //var count = await CommandCount.Where(cc => cc.Command.Equals("")).AsAsyncEnumerable().ToList(); + + return count.; + }*/ + } +} diff --git a/ISurvivalBot/Models/CommandCount.cs b/ISurvivalBot/Models/CommandCount.cs new file mode 100644 index 0000000..35bbade --- /dev/null +++ b/ISurvivalBot/Models/CommandCount.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Models +{ + public class CommandCount + { + + [Key] + public long CommandCountId { get; set; } + + public string Command { get; set; } + + public int Count { get; set; } + + public User User { get; set; } + } +} diff --git a/ISurvivalBot/Models/Log.cs b/ISurvivalBot/Models/Log.cs new file mode 100644 index 0000000..548b1ce --- /dev/null +++ b/ISurvivalBot/Models/Log.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Models +{ + public class Log + { + public int LogId { get; set; } + public string Message { get; set; } + } +} diff --git a/ISurvivalBot/Models/Quote.cs b/ISurvivalBot/Models/Quote.cs new file mode 100644 index 0000000..b971dbd --- /dev/null +++ b/ISurvivalBot/Models/Quote.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Models +{ + public class Quote + { + + + [Key] + public int Id; + public string AddedByUser{ get; set; } + public DateTime AddedAt { get; set; } + + + + public string QuoteAboutUser { get; set; } + + + [Required] + public string QuoteText { get; set; } + + + //[Required] + //public User User { get; set; } + } +} diff --git a/ISurvivalBot/Models/User.cs b/ISurvivalBot/Models/User.cs new file mode 100644 index 0000000..84da706 --- /dev/null +++ b/ISurvivalBot/Models/User.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Models +{ + public class User + { + [Key] + public long DiscordId { get; set; } + + + public string CurrentUsername { get; set; } + + + public bool IsAdmin { get; set; } + + + } +} diff --git a/ISurvivalBot/Program.cs b/ISurvivalBot/Program.cs new file mode 100644 index 0000000..63172b7 --- /dev/null +++ b/ISurvivalBot/Program.cs @@ -0,0 +1,251 @@ +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using ISurvivalBot.Models; +using ISurvivalBot.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Json; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace ISurvivalBot +{ + class Program + { + public static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult(); + + + private readonly IConfiguration _config; + private DiscordSocketClient _client; + private ILogger _logger; + private ServiceProvider services; + + public Program() + { + // create the configuration + var _builder = new ConfigurationBuilder() + .SetBasePath(AppContext.BaseDirectory) + .AddJsonFile(path: "config.json"); + + // build the configuration and assign to _config + _config = _builder.Build(); + } + + + public async Task MainAsync() + { + // call ConfigureServices to create the ServiceCollection/Provider for passing around the services + using ( services = ConfigureServices()) + { + + + //services.GetService().AddProvider(new ) + _logger = services.GetService().CreateLogger(); + _logger.LogInformation("Example log message"); + // get the client and assign to client + // you get the services via GetRequiredService + var client = services.GetRequiredService(); + _client = client; + client.GuildAvailable += Client_GuildAvailable; + + // setup logging and the ready event + client.Log += LogAsync; + client.Ready += ReadyAsync; + client.MessageReceived += Client_MessageReceived; + client.GuildMemberUpdated += Client_GuildMemberUpdated; + client.UserUpdated += Client_UserUpdated; + services.GetRequiredService().Log += LogAsync; + + // this is where we get the Token value from the configuration file, and start the bot + await client.LoginAsync(TokenType.Bot, _config["Token"]); + await client.StartAsync(); + + // we get the CommandHandler class here and call the InitializeAsync method to start things up for the CommandHandler service + await services.GetRequiredService().InitializeAsync(); + + client.GuildMembersDownloaded += Client_GuildMembersDownloaded; + + await client.DownloadUsersAsync(client.Guilds); + + + //clien + + // client.DownloadUsersAsync() + + //await + + // Update user database + //await updateUsers(client); + + await Task.Delay(-1); + } + } + + private async Task Client_GuildMembersDownloaded(SocketGuild guild) + { + _logger.LogInformation($"Downloaded Guild {guild.Name} becomes available, with id: {guild.Id}"); + + /*var usersList = guild.Users.AsEnumerable().Select(o => + { + Tuple.Create(o.Id, o.Username); + + }).ToList();*/ + + var userList = new List>(); + + foreach (var user in guild.Users) + { + userList.Add(Tuple.Create((long)user.Id, user.Username)); + } + + services.GetRequiredService().UpdateUsers(userList); + + /*foreach (var user in guild.Users) + { + _logger.LogInformation($"User id: {user.Id}, Username: {user.Username}"); + }*/ + } + + private async Task Client_GuildAvailable(SocketGuild guild) + { + _logger.LogInformation($"Guild {guild.Name} becomes available, with id: {guild.Id}"); + + //await guild.DownloadUsersAsync(); + /*await foreach (var lst in guild.GetUsersAsync()) + { + foreach (var user in lst) + { + _logger.LogInformation($"User id: {user.Id}, Username: {user.Username}"); + } + //await services.GetRequiredService().UpdateUser((long)user.Id, user.Username); + }*/ + } + + private async Task updateUsers(DiscordSocketClient client) + { + + var channels = client.Guilds; + // Check if channels are available + if (channels.Count() == 0) + return; + + // Get the users from the first channel + var channel = channels.First(); + await foreach(var lst in channel.GetUsersAsync()) + { + foreach (var user in lst) + { + _logger.LogInformation($"User id: {user.Id}, Username: {user.Username}"); + } + //await services.GetRequiredService().UpdateUser((long)user.Id, user.Username); + } + } + + private async Task Client_UserUpdated(SocketUser old, SocketUser newU) + { + //throw new NotImplementedException(); + _logger.LogInformation($"Old user: {old.Username} new user: {newU.Username}, Status: {newU.Status}"); + } + + private async Task Client_GuildMemberUpdated(SocketGuildUser old, SocketGuildUser newU) + { + //throw new NotImplementedException(); + _logger.LogInformation($"Old user: {old.Username} new user: {newU.Username}, Status: {newU.Status}"); + } + + private async Task Client_MessageReceived(SocketMessage message) + { + + string messageText = message.Content.ToLowerInvariant(); + if (message.Author.Username == "Het Orakel" || message.Content.StartsWith("!")) + return; + if (messageText.Contains("bruh")) + { + int wordCount = wordDuplication(messageText, "bruh"); + var commandCountService = services.GetRequiredService(); + var counter = await commandCountService.CountAndIncrementCommandByUser("bruh", (long)message.Author.Id, wordCount); + await message.Channel.SendMessageAsync($"{message.Author.Username} heeft {counter} keer bruh gezegd!"); + } + else if (messageText.Contains("meh")) + { + int wordCount = wordDuplication(messageText, "meh"); + var commandCountService = services.GetRequiredService(); + var counter = await commandCountService.CountAndIncrementCommandByUser("meh", (long)message.Author.Id, wordCount); + await message.Channel.SendMessageAsync($"{message.Author.Username} heeft {counter} schaapjes geteld!"); + } + } + + private int wordDuplication(string text, string word) + { + string[] words = text.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries); + Dictionary statistics = words + .GroupBy(word => word) + .ToDictionary( + kvp => kvp.Key, // the word itself is the key + kvp => kvp.Count()); // number of occurences is the value + if (statistics.ContainsKey(word)) + return statistics[word]; + return 1; + } + + private Task Log(LogMessage msg) + { + _logger.LogInformation(msg.ToString()); + return Task.CompletedTask; + } + + + private Task LogAsync(LogMessage log) + { + _logger.LogInformation(log.ToString()); + return Task.CompletedTask; + } + + private Task ReadyAsync() + { + _logger.LogInformation($"Connected as -> [{_client.CurrentUser}] :)"); + return Task.CompletedTask; + } + + // this method handles the ServiceCollection creation/configuration, and builds out the service provider we can call on later + private ServiceProvider ConfigureServices() + { + // this returns a ServiceProvider that is used later to call for those services + // we can add types we have access to here, hence adding the new using statement: + // using csharpi.Services; + // the config we build is also added, which comes in handy for setting the command prefix! + return new ServiceCollection() + .AddLogging(opt => + { + opt.AddConsole(); + //opt.AddFilter((category, level) => + // category == DbLoggerCategory.Database.Command.Name + // && level == LogLevel.Information); + }) + .AddSingleton(_config) + .AddSingleton() + //.AddSingleton() + .AddSingleton(new DiscordSocketClient(new DiscordSocketConfig + { + AlwaysDownloadUsers = true + })) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .BuildServiceProvider(); + } + } +} diff --git a/ISurvivalBot/Services/AudioService.cs b/ISurvivalBot/Services/AudioService.cs new file mode 100644 index 0000000..b3fb523 --- /dev/null +++ b/ISurvivalBot/Services/AudioService.cs @@ -0,0 +1,288 @@ +using Discord; +using Discord.Audio; +using Discord.Commands; +using Microsoft.Extensions.Logging; +using Overby.Extensions.AsyncBinaryReaderWriter; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Speech.AudioFormat; +using System.Speech.Synthesis; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ISurvivalBot.Services +{ + + public enum AudioServiceStatus + { + Succes, + Failure + } + + + public class AudioServiceState + { + public ulong VoiceChatId { get; set; } + public IAudioClient AudioClient { get; set; } + + public ConcurrentQueue PlayList { get; set; } = new ConcurrentQueue(); + + public bool IsPlaying { get; set; } = false; + + public CancellationTokenSource? CancellationTokenSource { get; set; } = null; + + public IVoiceChannel VoiceChannel { get; set; } + } + public class AudioService + { + + private readonly ConcurrentDictionary AudioState = new ConcurrentDictionary(); + + private readonly ILogger _logger; + + public AudioService(ILogger logger) => _logger = logger; + + public async Task JoinAudio(IGuild guild, IVoiceChannel voiceChannel) + { + AudioServiceState audioServiceState; + if (AudioState.TryGetValue(guild.Id, out audioServiceState)) { + _logger.LogError($"Guild {guild.Name} is already connected or queue exist"); + return AudioServiceStatus.Failure; + } + if (voiceChannel == null || voiceChannel.Guild.Id != guild.Id) { + return AudioServiceStatus.Failure; + } + + var audioClient = await voiceChannel.ConnectAsync(); + + audioServiceState = new AudioServiceState() + { + AudioClient = audioClient, + VoiceChannel = voiceChannel, + }; + + if (AudioState.TryAdd(guild.Id, audioServiceState)) + { + // If you add a method to log happenings from this service, + // you can uncomment these commented lines to make use of that. + audioClient.Connected += async () => + { + _logger.LogInformation($"Connected to voice channel: {voiceChannel.Name}"); + }; + audioClient.Disconnected += async (Exception ex) => + { + _logger.LogError($"Disconnected from voice channel: {voiceChannel.Name} with exception: {ex.Message}"); + }; + _logger.LogInformation($"Connected to voice channel: {guild.Name}."); + } + + return AudioServiceStatus.Succes; + } + + + public async Task LeaveAudio(IGuild guild, IVoiceChannel voiceChannel) + { + if (voiceChannel == null || voiceChannel.Guild.Id != guild.Id) + { + return AudioServiceStatus.Failure; + } + AudioServiceState audioState = await getAudioState(guild); + if (audioState == null) + { + Console.WriteLine("Not connected"); + return AudioServiceStatus.Failure; + } + if (audioState.IsPlaying) + await StopPlaying(guild, voiceChannel); + _logger.LogInformation($"Disconnected from voice on {guild.Name}."); + await audioState.AudioClient.StopAsync(); + audioState.IsPlaying = false; + + AudioState.Remove(guild.Id, out audioState); + + return AudioServiceStatus.Succes; + } + + + public async Task PlaySound(IGuild guild, IMessageChannel channel, IVoiceChannel voiceChannel, string path) + { + AudioServiceState audioState = await getAudioState(guild); + if (audioState == null || audioState.IsPlaying) + { + Console.WriteLine("Is already playing"); + return AudioServiceStatus.Failure; + } + if (voiceChannel == null || voiceChannel.Guild.Id != guild.Id) + { + return AudioServiceStatus.Failure; + } + + // Your task: Get a full path to the file if the value of 'path' is only a filename. + if (!File.Exists(path)) + { + await channel.SendMessageAsync("File does not exist."); + return AudioServiceStatus.Failure; + } + + + //IAudioClient client; + //if (ConnectedChannels.TryGetValue(guild.Id, out client)) { + + audioState.IsPlaying = true; + await audioState.AudioClient.SetSpeakingAsync(true); // send a speaking indicator + + var psi = new ProcessStartInfo + { + FileName = "ffmpeg", + Arguments = $@"-hide_banner -loglevel panic -i ""{path}"" -ac 2 -f s16le -ar 48000 pipe:1", + RedirectStandardOutput = true, + UseShellExecute = false + }; + + + using (var ffmpeg = Process.Start(psi)) + using (var discord = audioState.AudioClient.CreatePCMStream(AudioApplication.Mixed)) + { + try + { + audioState.CancellationTokenSource = new CancellationTokenSource(); + audioState.CancellationTokenSource.Token.Register(() => ffmpeg.Kill()); + var output = ffmpeg.StandardOutput.BaseStream; + await output.CopyToAsync(discord, audioState.CancellationTokenSource.Token); + } + catch (OperationCanceledException ex) + { + _logger.LogWarning("AudioService: Operation Cancelled"); + } + finally + { + await discord.FlushAsync(); + audioState.IsPlaying = false; + await audioState.AudioClient.SetSpeakingAsync(false); // we're not speaking anymore + ffmpeg.Kill(); + } + } + //} + return AudioServiceStatus.Succes; + + } + + public async Task ListAudio(ICommandContext context) + { + var audioFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.mp3") + .Where(file => new string[] { ".mp3", ".wav" } + .Contains(Path.GetExtension(file))) + .ToList(); + + + string message = $"Available files: " + String.Join(", ", audioFiles.ToArray()); + if (message.Length > 1950) + { + message = message.Substring(0, 1950) + "...."; + } + await context.User.SendMessageAsync(message); + return AudioServiceStatus.Succes; + } + + public async Task StopPlaying(IGuild guild, IVoiceChannel voiceChannel) { + + AudioServiceState audioState = await getAudioState(guild); + if (audioState == null || audioState.IsPlaying) + { + Console.WriteLine("Is already playing"); + return AudioServiceStatus.Failure; + } + if (voiceChannel.Guild.Id != guild.Id) + { + return AudioServiceStatus.Failure; + } + + if (audioState.CancellationTokenSource == null) { + return AudioServiceStatus.Failure; + } + audioState.CancellationTokenSource.Cancel(); + _logger.LogWarning("Cancel token"); + return AudioServiceStatus.Succes; + } + + + private async Task PrintAvailableTTS(SpeechSynthesizer synth) + { + // Output information about all of the installed voices. + Console.WriteLine("Installed voices -"); + foreach (InstalledVoice voice in synth.GetInstalledVoices()) { + VoiceInfo info = voice.VoiceInfo; + string AudioFormats = ""; + foreach (SpeechAudioFormatInfo fmt in info.SupportedAudioFormats) { + AudioFormats += String.Format("{0}\n", + fmt.EncodingFormat.ToString()); + } + + Console.WriteLine(" Name: " + info.Name); + Console.WriteLine(" Culture: " + info.Culture); + Console.WriteLine(" Age: " + info.Age); + Console.WriteLine(" Gender: " + info.Gender); + Console.WriteLine(" Description: " + info.Description); + Console.WriteLine(" ID: " + info.Id); + Console.WriteLine(" Enabled: " + voice.Enabled); + if (info.SupportedAudioFormats.Count != 0) + Console.WriteLine(" Audio formats: " + AudioFormats); + else + Console.WriteLine(" No supported audio formats found"); + + string AdditionalInfo = ""; + foreach (string key in info.AdditionalInfo.Keys) { + AdditionalInfo += String.Format(" {0}: {1}\n", key, info.AdditionalInfo[key]); + } + + Console.WriteLine(" Additional Info - " + AdditionalInfo); + } + } + + + private async Task getAudioState(IGuild guild) + { + AudioServiceState audioServiceState; + if (AudioState.TryGetValue(guild.Id, out audioServiceState)) + { + return audioServiceState; + } + return null; + } + + + + public async Task SayText(IGuild guild, IVoiceChannel voiceChannel, IMessageChannel channel, string text) + { + + AudioServiceState audioState = await getAudioState(guild); + if (audioState == null || audioState.IsPlaying) { + Console.WriteLine("Is already playing"); + return AudioServiceStatus.Failure; + } + if (voiceChannel == null || voiceChannel.Guild.Id != guild.Id) + { + return AudioServiceStatus.Failure; + } + + // Initialize a new instance of the SpeechSynthesizer. + using (SpeechSynthesizer synth = new SpeechSynthesizer()) { + await PrintAvailableTTS(synth); + synth.SetOutputToWaveFile("D:\\output.wav"); + + + synth.Speak(text); + + return await PlaySound(guild, channel, voiceChannel, "D:\\output.wav"); + } + + } + + + } +} diff --git a/ISurvivalBot/Services/CommandCountService.cs b/ISurvivalBot/Services/CommandCountService.cs new file mode 100644 index 0000000..f8c9077 --- /dev/null +++ b/ISurvivalBot/Services/CommandCountService.cs @@ -0,0 +1,85 @@ +using ISurvivalBot.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Services +{ + public class CommandCountService + { + + private readonly BotContext _botContext; + + + public CommandCountService(BotContext botContext) + { + this._botContext = botContext; + } + + + + + public async Task CountAndIncrementCommandByUser(string command, long userId, int wordCount = 1) + { + int count = 0; + var commandItem = await _botContext.CommandCount.AsAsyncEnumerable().Where(cc => cc.Command == command && cc.User.DiscordId == userId).FirstOrDefaultAsync(); + if (commandItem == null) + { + User user = await _botContext.Users.AsAsyncEnumerable().Where(u => u.DiscordId == userId).FirstAsync(); + CommandCount cc = new () + { + Command = command, + Count = wordCount, + User = user + }; + + await _botContext.CommandCount.AddAsync(cc); + await _botContext.SaveChangesAsync(); + return 1; + } + else + { + commandItem.Count+= wordCount; + count = commandItem.Count; + _botContext.Update(commandItem); + await _botContext.SaveChangesAsync(); + } + + return count; + + } + + + public async Task GetCounterByUser(string username, string command) + { + var user = await _botContext.Users.AsAsyncEnumerable().Where(u => u.CurrentUsername == username).FirstOrDefaultAsync(); + if (user == null) + return "Invalid user"; + + var countCommand = await _botContext.CommandCount.AsAsyncEnumerable().Where(cc => cc.Command == command && cc.User == user).FirstOrDefaultAsync(); + if (countCommand == null) + return $"{username}: heeft nog nooit het woord {command} gebruikt."; + + return $"{username}: heeft {countCommand.Count} keer het woord {command} gebruikt."; + } + + public async Task ResetWordCount(long id, string command) + { + var user = await _botContext.Users.AsAsyncEnumerable().Where(u => u.DiscordId == id).FirstOrDefaultAsync(); + if (user == null) + return false; + + var countCommand = await _botContext.CommandCount.AsAsyncEnumerable().Where(cc => cc.Command == command && cc.User == user).FirstOrDefaultAsync(); + if (countCommand == null) + return false; + + countCommand.Count = 0; + _botContext.CommandCount.Update(countCommand); + await _botContext.SaveChangesAsync(); + return true; + + } + } +} diff --git a/ISurvivalBot/Services/CommandHandlingService.cs b/ISurvivalBot/Services/CommandHandlingService.cs new file mode 100644 index 0000000..3cd4bc1 --- /dev/null +++ b/ISurvivalBot/Services/CommandHandlingService.cs @@ -0,0 +1,76 @@ +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Services +{ + public class CommandHandlingService + { + private readonly CommandService _commands; + private readonly DiscordSocketClient _discord; + private readonly IServiceProvider _services; + + public CommandHandlingService(IServiceProvider services) + { + _commands = services.GetRequiredService(); + _discord = services.GetRequiredService(); + _services = services; + + // Hook CommandExecuted to handle post-command-execution logic. + _commands.CommandExecuted += CommandExecutedAsync; + // Hook MessageReceived so we can process each message to see + // if it qualifies as a command. + _discord.MessageReceived += MessageReceivedAsync; + } + + public async Task InitializeAsync() + { + // Register modules that are public and inherit ModuleBase. + await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); + } + + public async Task MessageReceivedAsync(SocketMessage rawMessage) + { + // Ignore system messages, or messages from other bots + if (!(rawMessage is SocketUserMessage message)) return; + if (message.Source != MessageSource.User) return; + + // This value holds the offset where the prefix ends + var argPos = 0; + // Perform prefix check. You may want to replace this with + // (!message.HasCharPrefix('!', ref argPos)) + // for a more traditional command format like !help. + //if (!message.HasMentionPrefix(_discord.CurrentUser, ref argPos)) return; + if (!message.HasCharPrefix('!', ref argPos)) return; + + var context = new SocketCommandContext(_discord, message); + // Perform the execution of the command. In this method, + // the command service will perform precondition and parsing check + // then execute the command if one is matched. + await _commands.ExecuteAsync(context, argPos, _services); + // Note that normally a result will be returned by this format, but here + // we will handle the result in CommandExecutedAsync, + } + + public async Task CommandExecutedAsync(Optional command, ICommandContext context, IResult result) + { + // command is unspecified when there was a search failure (command not found); we don't care about these errors + if (!command.IsSpecified) + return; + + // the command was successful, we don't care about this result, unless we want to log that a command succeeded. + if (result.IsSuccess) + return; + + // the command failed, let's notify the user that something happened. + await context.Channel.SendMessageAsync($"error: {result}"); + } + } +} diff --git a/ISurvivalBot/Services/MaintenanceService.cs b/ISurvivalBot/Services/MaintenanceService.cs new file mode 100644 index 0000000..20d1aed --- /dev/null +++ b/ISurvivalBot/Services/MaintenanceService.cs @@ -0,0 +1,48 @@ +using ISurvivalBot.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Services +{ + public class MaintenanceService + { + public readonly BotContext _botContext; + + + public MaintenanceService(BotContext botContext) + { + this._botContext = botContext; + } + + + public async Task UpdateUsers(List> users) + { + + } + + public async Task UpdateUser(long userId, string username) + { + var user = await _botContext.Users.AsAsyncEnumerable().Where(u => u.DiscordId == userId).FirstOrDefaultAsync(); + if (user == null) + { + User u = new User + { + DiscordId = userId, + CurrentUsername = username, + }; + await _botContext.Users.AddAsync(u); + } + else if (user.CurrentUsername != username) + { + user.CurrentUsername = username; + _botContext.Users.Update(user); + } + await _botContext.SaveChangesAsync(); + + + } + } +} diff --git a/ISurvivalBot/Services/PictureService.cs b/ISurvivalBot/Services/PictureService.cs new file mode 100644 index 0000000..db0a4df --- /dev/null +++ b/ISurvivalBot/Services/PictureService.cs @@ -0,0 +1,69 @@ +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Services +{ + public class PictureService + { + private readonly HttpClient _http; + private readonly IConfiguration _config; + + public PictureService(HttpClient http, IConfiguration config) + { + _http = http; + _config = config; + } + + + public async Task GetCatPictureAsync() + { + var resp = await _http.GetAsync("https://cataas.com/cat"); + return await resp.Content.ReadAsStreamAsync(); + } + + public async Task GetPandaPictureAsync() + { + var resp = await _http.GetAsync("https://redpanda.pics/"); + return await resp.Content.ReadAsStreamAsync(); + } + + + public async Task GetDogPictureAsync() + { + + var resp = await _http.GetAsync($"https://api.thedogapi.com/v1/images/search?api_key={_config["DogKey"]}"); + var responseBody = await resp.Content.ReadAsStringAsync(); + dynamic body = JsonConvert.DeserializeObject(responseBody); + + var resp1 = await _http.GetAsync($"{body[0].url}"); + return await resp1.Content.ReadAsStreamAsync(); + } + + public async Task GetFoxPictureAsync() + { + + var resp = await _http.GetAsync($"https://randomfox.ca/floof/"); + var responseBody = await resp.Content.ReadAsStringAsync(); + dynamic body = JsonConvert.DeserializeObject(responseBody); + + var resp1 = await _http.GetAsync($"{body.image}"); + return await resp1.Content.ReadAsStreamAsync(); + } + + public async Task GetCatFact() + { + var resp = await _http.GetAsync($"https://meowfacts.herokuapp.com/"); + var responseBody = await resp.Content.ReadAsStringAsync(); + dynamic body = JsonConvert.DeserializeObject(responseBody); + var body1 = body.data; + return body1[0]; + } + } +} diff --git a/ISurvivalBot/Services/QuoteService.cs b/ISurvivalBot/Services/QuoteService.cs new file mode 100644 index 0000000..1314aa8 --- /dev/null +++ b/ISurvivalBot/Services/QuoteService.cs @@ -0,0 +1,66 @@ +using ISurvivalBot.Models; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Services +{ + public class QuoteService + { + + // private readonly ILogger _logger; + private readonly BotContext _botContext; + private Random rand = new Random(); + + public QuoteService(/*ILogger logger,*/ BotContext botContext) + { + //this._logger = logger; + this._botContext = botContext; + } + + + public async Task RandomQuote() + { + int quoteCount = _botContext.Quotes.Count(); + if (quoteCount == 0) + return "Error: No quotes in database."; + int toSkip = rand.Next(1, quoteCount); + Quote quote = await _botContext.Quotes.AsAsyncEnumerable().OrderBy(r => Guid.NewGuid()).Skip(toSkip).Take(1).FirstAsync(); + + return $"{quote.QuoteAboutUser}: {quote.QuoteText}"; + } + + public async Task AddQuote(string userAdded, string unparsedText) + { + unparsedText = unparsedText.Replace("!quote add", "").Trim(); + int indexOfDoublePoint = unparsedText.IndexOf(":"); + if (indexOfDoublePoint < 0) + return; + string aboutUser = unparsedText.Substring(0, indexOfDoublePoint); + string quoteText = unparsedText.Substring(indexOfDoublePoint+1, unparsedText.Length- indexOfDoublePoint-1); + Quote quote = new Quote + { + AddedByUser = userAdded, + AddedAt = DateTime.Now, + QuoteText = quoteText, + QuoteAboutUser = aboutUser, + }; + + await _botContext.Quotes.AddAsync(quote); + _botContext.SaveChanges(); + } + + public async Task QuoteCountUser(string user) + { + return await _botContext.Quotes.AsAsyncEnumerable().WhereAwait(c => new ValueTask(c.QuoteAboutUser == user)).CountAsync(); + } + + public async Task QuoteCount() + { + return await _botContext.Quotes.CountAsync(); + } + } +} diff --git a/ISurvivalBot/Services/StateHandlerService.cs b/ISurvivalBot/Services/StateHandlerService.cs new file mode 100644 index 0000000..1526f22 --- /dev/null +++ b/ISurvivalBot/Services/StateHandlerService.cs @@ -0,0 +1,21 @@ +using Discord.Audio; +using Discord.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Services +{ + public class StateHandlerService + { + + public IAudioClient AudioClient { get; set; } + + /*public StateHandlerService(IAudioClient audioClient) + { + this.AudioClient = audioClient; + }*/ + } +} diff --git a/ISurvivalBot/Services/UserService.cs b/ISurvivalBot/Services/UserService.cs new file mode 100644 index 0000000..a7ef33a --- /dev/null +++ b/ISurvivalBot/Services/UserService.cs @@ -0,0 +1,83 @@ +using ISurvivalBot.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Services +{ + public class UserService + { + + private readonly BotContext _botContext; + + + public UserService(BotContext botContext) + { + this._botContext = botContext; + } + + + + public async Task UpdateUser(long userId, string username) + { + var user = await _botContext.Users.AsAsyncEnumerable().Where(u => u.DiscordId == userId).FirstOrDefaultAsync(); + if (user == null) + { + User u = new User + { + DiscordId = userId, + CurrentUsername = username, + }; + await _botContext.Users.AddAsync(u); + } + else if (user.CurrentUsername != username) + { + user.CurrentUsername = username; + _botContext.Users.Update(user); + } + + + } + + public async Task UpdateUsers(List> userList) + { + foreach (var user in userList) + { + await UpdateUser(user.Item1, user.Item2); + } + await _botContext.SaveChangesAsync(); + + } + + public async Task SetAdminStatus(string username, bool admin) + { + var user = await _botContext.Users.AsAsyncEnumerable().Where(u => u.CurrentUsername == username).FirstOrDefaultAsync(); + if (user == null) + return false; + + user.IsAdmin = admin; + _botContext.Users.Update(user); + await _botContext.SaveChangesAsync(); + + return true; + } + + public async Task IsAdmin(string username) + { + var user = await _botContext.Users.AsAsyncEnumerable().Where(u => u.CurrentUsername == username).FirstOrDefaultAsync(); + if (user == null) + return false; + return user.IsAdmin; + } + + public async Task IsAdmin(long userId) + { + var user = await _botContext.Users.AsAsyncEnumerable().Where(u => u.DiscordId == userId).FirstOrDefaultAsync(); + if (user == null) + return false; + return user.IsAdmin; + } + } +} diff --git a/ISurvivalBot/TestModules.cs b/ISurvivalBot/TestModules.cs new file mode 100644 index 0000000..7e700c1 --- /dev/null +++ b/ISurvivalBot/TestModules.cs @@ -0,0 +1,56 @@ +using Discord.Commands; +using Discord.WebSocket; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot +{ + // Create a module with no prefix + /*public class InfoModule : ModuleBase + { + // ~say hello world -> hello world + [Command("say")] + [Summary("Echoes a message.")] + public Task SayAsync([Remainder][Summary("The text to echo")] string echo) + => ReplyAsync(echo); + + // ReplyAsync is a method on ModuleBase + }*/ + + // Create a module with the 'sample' prefix + [Group("sample")] + public class SampleModule : ModuleBase + { + // ~sample square 20 -> 400 + [Command("square")] + [Summary("Squares a number.")] + public async Task SquareAsync( + [Summary("The number to square.")] + int num) + { + // We can also access the channel from the Command Context. + await Context.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}"); + } + + // ~sample userinfo --> foxbot#0282 + // ~sample userinfo @Khionu --> Khionu#8708 + // ~sample userinfo Khionu#8708 --> Khionu#8708 + // ~sample userinfo Khionu --> Khionu#8708 + // ~sample userinfo 96642168176807936 --> Khionu#8708 + // ~sample whois 96642168176807936 --> Khionu#8708 + [Command("userinfo")] + [Summary + ("Returns info about the current user, or the user parameter, if one passed.")] + [Alias("user", "whois")] + public async Task UserInfoAsync( + [Summary("The (optional) user to get info from")] + SocketUser user = null) + { + var userInfo = user ?? Context.Client.CurrentUser; + await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}"); + } + } +} diff --git a/ISurvivalBot/Utils/CommonEmoij.cs b/ISurvivalBot/Utils/CommonEmoij.cs new file mode 100644 index 0000000..9dd4a9a --- /dev/null +++ b/ISurvivalBot/Utils/CommonEmoij.cs @@ -0,0 +1,19 @@ +using Discord; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ISurvivalBot.Utils +{ + public static class CommonEmoij + { + public static Emoji OK = new Emoji("✅"); + public static Emoji NOK = new Emoji("❌"); + + public static Emote BEREND = Emote.Parse("<:berend:821110556667936838>"); + + + } +} diff --git a/ISurvivalBot/config.json b/ISurvivalBot/config.json new file mode 100644 index 0000000..1ecccb0 --- /dev/null +++ b/ISurvivalBot/config.json @@ -0,0 +1,5 @@ +{ + "Token": "ODQ0MTkxMjIzNjQ0NTUzMjU3.YKO0WA.K5ovZkueLnPwAjLJEUtDekv_Ads", + "DogKey": "b73b85ae-698b-40c8-9247-40da77bc51e8", + "Prefix": ";" +} \ No newline at end of file