diff --git a/Discord.Addons.Interactive/Criteria/EnsureFromUserCriterion.cs b/Discord.Addons.Interactive/Criteria/EnsureFromUserCriterion.cs index 5e77acd..a612de3 100644 --- a/Discord.Addons.Interactive/Criteria/EnsureFromUserCriterion.cs +++ b/Discord.Addons.Interactive/Criteria/EnsureFromUserCriterion.cs @@ -17,7 +17,7 @@ public EnsureFromUserCriterion(ulong id) public Task JudgeAsync(SocketCommandContext sourceContext, IMessage parameter) { - bool ok = _id == parameter.Author.Id; + var ok = _id == parameter.Author.Id; return Task.FromResult(ok); } } diff --git a/Discord.Addons.Interactive/InteractiveBase.cs b/Discord.Addons.Interactive/InteractiveBase.cs index 6d58c7a..1c3c0f9 100644 --- a/Discord.Addons.Interactive/InteractiveBase.cs +++ b/Discord.Addons.Interactive/InteractiveBase.cs @@ -15,8 +15,7 @@ public abstract class InteractiveBase : InteractiveBase { } - public abstract class InteractiveBase : ModuleBase - where T : SocketCommandContext + public abstract class InteractiveBase : ModuleBase where T : SocketCommandContext { public InteractiveService Interactive { get; set; } @@ -31,26 +30,17 @@ public Task ReplyAndDeleteAsync(string content, bool isTTS = false public Task InlineReactionReplyAsync(ReactionCallbackData data, bool fromSourceUser = true) => Interactive.SendMessageWithReactionCallbacksAsync(Context, data, fromSourceUser); - - public Task PagedReplyAsync(IEnumerable pages, bool fromSourceUser = true) - { - var pager = new PaginatedMessage - { - Pages = pages - }; - return PagedReplyAsync(pager, fromSourceUser); - } - - public Task PagedReplyAsync(PaginatedMessage pager, bool fromSourceUser = true) + + public Task PagedReplyAsync(PaginatedMessage pager, ReactionList reactions, bool fromSourceUser = true) { var criterion = new Criteria(); if (fromSourceUser) criterion.AddCriterion(new EnsureReactionFromSourceUserCriterion()); - return PagedReplyAsync(pager, criterion); + return PagedReplyAsync(pager, reactions, criterion); } - public Task PagedReplyAsync(PaginatedMessage pager, ICriterion criterion) - => Interactive.SendPaginatedMessageAsync(Context, pager, criterion); + public Task PagedReplyAsync(PaginatedMessage pager, ReactionList reactions, ICriterion criterion) + => Interactive.SendPaginatedMessageAsync(Context, pager, reactions, criterion); public RuntimeResult Ok(string reason = null) => new OkResult(reason); } diff --git a/Discord.Addons.Interactive/InteractiveService.cs b/Discord.Addons.Interactive/InteractiveService.cs index 5c52967..741ebc6 100644 --- a/Discord.Addons.Interactive/InteractiveService.cs +++ b/Discord.Addons.Interactive/InteractiveService.cs @@ -18,7 +18,7 @@ public class InteractiveService : IDisposable private readonly Dictionary _callbacks; private readonly TimeSpan _defaultTimeout; - // helpers to allow DI containers to resolve without a custom factory + // Helpers to allow DI containers to resolve without a custom factory public InteractiveService(DiscordSocketClient discord, InteractiveServiceConfig config = null) : this((BaseSocketClient)discord, config) { } @@ -43,10 +43,13 @@ public Task NextMessageAsync(SocketCommandContext context, CancellationToken token = default) { var criterion = new Criteria(); + if (fromSourceUser) criterion.AddCriterion(new EnsureSourceUserCriterion()); + if (inSourceChannel) criterion.AddCriterion(new EnsureSourceChannelCriterion()); + return NextMessageAsync(context, criterion, timeout, token); } @@ -80,8 +83,7 @@ async Task Handler(SocketMessage message) if (task == trigger) return await trigger.ConfigureAwait(false); - else - return null; + return null; } public async Task ReplyAndDeleteAsync(SocketCommandContext context, @@ -97,23 +99,10 @@ public async Task ReplyAndDeleteAsync(SocketCommandContext context .ConfigureAwait(false); return message; } - - /// - /// Sends a message with reaction callbacks - /// - /// - /// The context. - /// - /// - /// The callbacks. - /// - /// - /// The from source user. - /// - /// - /// The . - /// - public async Task SendMessageWithReactionCallbacksAsync(SocketCommandContext context, ReactionCallbackData reactionCallbackData, bool fromSourceUser = true) + + public async Task SendMessageWithReactionCallbacksAsync(SocketCommandContext context, + ReactionCallbackData reactionCallbackData, + bool fromSourceUser = true) { var criterion = new Criteria(); if (fromSourceUser) @@ -128,10 +117,11 @@ public async Task SendMessageWithReactionCallbacksAsync(SocketComm public async Task SendPaginatedMessageAsync(SocketCommandContext context, PaginatedMessage pager, + ReactionList reactions, ICriterion criterion = null) { var callback = new PaginatedMessageCallback(this, context, pager, criterion); - await callback.DisplayAsync().ConfigureAwait(false); + await callback.DisplayAsync(reactions).ConfigureAwait(false); return callback.Message; } diff --git a/Discord.Addons.Interactive/Paginator/PaginatedAppearanceOptions.cs b/Discord.Addons.Interactive/Paginator/PaginatedAppearanceOptions.cs index 3d98a11..3454104 100644 --- a/Discord.Addons.Interactive/Paginator/PaginatedAppearanceOptions.cs +++ b/Discord.Addons.Interactive/Paginator/PaginatedAppearanceOptions.cs @@ -4,26 +4,37 @@ namespace Discord.Addons.Interactive.Paginator { public class PaginatedAppearanceOptions { - public static PaginatedAppearanceOptions Default = new PaginatedAppearanceOptions(); + public IEmote First { get; set; } = new Emoji("⏮"); + + public IEmote Back { get; set; } = new Emoji("◀"); + + public IEmote Next { get; set; } = new Emoji("▶"); + + public IEmote Last { get; set; } = new Emoji("⏭"); + + public IEmote Stop { get; set; } = new Emoji("⏹"); + + public IEmote Jump { get; set; } = new Emoji("🔢"); + + public IEmote Info { get; set; } = new Emoji("ℹ"); - public IEmote First = new Emoji("⏮"); - public IEmote Back = new Emoji("◀"); - public IEmote Next = new Emoji("▶"); - public IEmote Last = new Emoji("⏭"); - public IEmote Stop = new Emoji("⏹"); - public IEmote Jump = new Emoji("🔢"); - public IEmote Info = new Emoji("ℹ"); + public string FooterFormat { get; set; } = "Page {0}/{1}"; - public string FooterFormat = "Page {0}/{1}"; - public string InformationText = "This is a paginator. React with the respective icons to change page."; + public string InformationTitle { get; set; } = "Paginator Information"; + + public string InformationText { get; set; } = "This funny-looking embed is called a **Paginator**. It's just like a book. You can flip through the pages using the arrows, enter the number of the page you want to navigate to, or close the it entirely."; - public JumpDisplayOptions JumpDisplayOptions = JumpDisplayOptions.WithManageMessages; - public bool DisplayInformationIcon = true; + public Color InformationColor { get; set; } = new Color(252, 166, 205); + + public JumpDisplayOptions JumpDisplayOptions { get; set; } = JumpDisplayOptions.WithManageMessages; + + public bool DisplayInformationIcon { get; set; } = true; + + public TimeSpan? Timeout { get; set; } = null; + + public TimeSpan InfoTimeout { get; set; } = TimeSpan.FromSeconds(10); - public TimeSpan? Timeout = null; - public TimeSpan InfoTimeout = TimeSpan.FromSeconds(30); - - public int FieldsPerPage = 6; + public static PaginatedAppearanceOptions Default { get; set; } = new PaginatedAppearanceOptions(); } public enum JumpDisplayOptions diff --git a/Discord.Addons.Interactive/Paginator/PaginatedMessage.cs b/Discord.Addons.Interactive/Paginator/PaginatedMessage.cs index 3299c50..a7cfbcb 100644 --- a/Discord.Addons.Interactive/Paginator/PaginatedMessage.cs +++ b/Discord.Addons.Interactive/Paginator/PaginatedMessage.cs @@ -1,40 +1,63 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Discord.Addons.Interactive.Paginator { public class PaginatedMessage { - /// - /// Pages contains a collection of elements to page over in the embed. It is expected - /// that a string-like object is used in this collection, as objects will be converted - /// to a displayable string only through their generic ToString method, with the - /// exception of EmbedFieldBuilders. - /// - /// If this collection is of EmbedFieldBuilder, then the pages will be displayed in - /// batches of , and the - /// embed's description will be populated with the field. - /// - public IEnumerable Pages { get; set; } - - /// - /// Content sets the content of the message, displayed above the embed. This may remain empty. - /// - public string Content { get; set; } = ""; - - /// - /// Author sets the property directly. - /// + public IEnumerable Pages { get; set; } = new List(); + + public string Content { get; set; } = string.Empty; + public EmbedAuthorBuilder Author { get; set; } = null; public Color Color { get; set; } = Color.Default; - public string Title { get; set; } = ""; + public string Title { get; set; } = null; + + public string Url { get; set; } = null; + + public string Description { get; set; } = string.Empty; - /// - /// AlternateDescription will be used as the description of the pager only when - /// is a collection of . - /// - public string AlternateDescription { get; set; } = ""; + public string ImageUrl { get; set; } = null; + public string ThumbnailUrl { get; set; } = null; + + public List Fields { get; set; } = new List(); + + public EmbedFooterBuilder FooterOverride { get; set; } = null; + + public DateTimeOffset? TimeStamp { get; set; } = null; public PaginatedAppearanceOptions Options { get; set; } = PaginatedAppearanceOptions.Default; } + + public class EmbedPage + { + public string AlternateAuthorTitle { get; set; } + + public string AlteranteAuthorIcon { get; set; } + + public bool DisplayTotalFieldsCount { get; set; } = false; + + public string TotalFieldsMessage { get; set; } = null; + + public double TotalFieldsCountConstant { get; set; } = 1; + + public string Title { get; set; } + + public string Url { get; set; } = null; + + public string Description { get; set; } + + public string ImageUrl { get; set; } + + public string ThumbnailUrl { get; set; } = null; + + public List Fields { get; set; } = new List(); + + public EmbedFooterBuilder FooterOverride { get; set; } = null; + + public DateTimeOffset? TimeStamp { get; set; } = null; + + public Color? Color { get; set; } = null; + } } \ No newline at end of file diff --git a/Discord.Addons.Interactive/Paginator/PaginatedMessageCallback.cs b/Discord.Addons.Interactive/Paginator/PaginatedMessageCallback.cs index d18c0eb..7c879a0 100644 --- a/Discord.Addons.Interactive/Paginator/PaginatedMessageCallback.cs +++ b/Discord.Addons.Interactive/Paginator/PaginatedMessageCallback.cs @@ -1,32 +1,31 @@ using Discord.Addons.Interactive.Callbacks; using Discord.Addons.Interactive.Criteria; +using Discord.Addons.Interactive.Paginator; using Discord.Commands; +using Discord.Rest; using Discord.WebSocket; using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Discord.Addons.Interactive.Paginator +namespace Discord.Addons.Interactive { public class PaginatedMessageCallback : IReactionCallback { - public SocketCommandContext Context { get; } - public InteractiveService Interactive { get; } - public IUserMessage Message { get; private set; } - public RunMode RunMode => RunMode.Sync; - public ICriterion Criterion { get; } public TimeSpan? Timeout => Options.Timeout; - private readonly PaginatedMessage _pager; - private PaginatedAppearanceOptions Options => _pager.Options; + private readonly int _pages; - private int _page = 1; - public PaginatedMessageCallback(InteractiveService interactive, + private readonly PaginatedMessage _pager; + + private int _currentPage = 1; + + public PaginatedMessageCallback( + InteractiveService interactive, SocketCommandContext sourceContext, PaginatedMessage pager, ICriterion criterion = null) @@ -36,70 +35,93 @@ public PaginatedMessageCallback(InteractiveService interactive, Criterion = criterion ?? new EmptyCriterion(); _pager = pager; _pages = _pager.Pages.Count(); - - if (_pager.Pages is IEnumerable) - _pages = ((_pager.Pages.Count() - 1) / Options.FieldsPerPage) + 1; } - public async Task DisplayAsync() + public SocketCommandContext Context { get; } + + public InteractiveService Interactive { get; } + + public ICriterion Criterion { get; } + + public IUserMessage Message { get; private set; } + + public async Task DisplayAsync(ReactionList reactionList) { var embed = BuildEmbed(); var message = await Context.Channel.SendMessageAsync(_pager.Content, embed: embed).ConfigureAwait(false); + Message = message; Interactive.AddReactionCallback(message, this); - // Reactions take a while to add, don't wait for them + _ = Task.Run(async () => { - await message.AddReactionAsync(Options.First); - await message.AddReactionAsync(Options.Back); - await message.AddReactionAsync(Options.Next); - await message.AddReactionAsync(Options.Last); + if (reactionList.First) + await message.AddReactionAsync(Options.First); + + if (reactionList.Backward) + await message.AddReactionAsync(Options.Back); + + if (reactionList.Forward) + await message.AddReactionAsync(Options.Next); - var manageMessages = (Context.Channel is IGuildChannel guildChannel) && ((IGuildUser)Context.User).GetPermissions(guildChannel).ManageMessages; + if (reactionList.Last) + await message.AddReactionAsync(Options.Last); - if (Options.JumpDisplayOptions == JumpDisplayOptions.Always - || (Options.JumpDisplayOptions == JumpDisplayOptions.WithManageMessages && manageMessages)) + var manageMessages = Context.Channel is IGuildChannel guildChannel && + (Context.User as IGuildUser).GetPermissions(guildChannel).ManageMessages; + + if (reactionList.Jump + && Options.JumpDisplayOptions == JumpDisplayOptions.Always + || Options.JumpDisplayOptions == JumpDisplayOptions.WithManageMessages && manageMessages) await message.AddReactionAsync(Options.Jump); - await message.AddReactionAsync(Options.Stop); + if (reactionList.Trash) + await message.AddReactionAsync(Options.Stop); - if (Options.DisplayInformationIcon) + if (reactionList.Info && Options.DisplayInformationIcon) await message.AddReactionAsync(Options.Info); }); - // TODO: (Next major version) timeouts need to be handled at the service-level! + if (Timeout.HasValue) + DisplayTimeout(message, Message); + } + + public void DisplayTimeout(RestUserMessage m1, IUserMessage m2) + { if (Timeout.HasValue) { _ = Task.Delay(Timeout.Value).ContinueWith(_ => { - Interactive.RemoveReactionCallback(message); - _ = Message.DeleteAsync(); + Interactive.RemoveReactionCallback(m1); + m2.RemoveAllReactionsAsync(); }); } } - + public async Task HandleCallbackAsync(SocketReaction reaction) { var emote = reaction.Emote; if (emote.Equals(Options.First)) - _page = 1; + { + _currentPage = 1; + } else if (emote.Equals(Options.Next)) { - if (_page >= _pages) - return false; - ++_page; + if (_currentPage >= _pages) return false; + ++_currentPage; } else if (emote.Equals(Options.Back)) { - if (_page <= 1) - return false; - --_page; + if (_currentPage <= 1) return false; + --_currentPage; } else if (emote.Equals(Options.Last)) - _page = _pages; + { + _currentPage = _pages; + } else if (emote.Equals(Options.Stop)) { - await Message.DeleteAsync().ConfigureAwait(false); + await Message.RemoveAllReactionsAsync().ConfigureAwait(false); return true; } else if (emote.Equals(Options.Jump)) @@ -110,7 +132,9 @@ public async Task HandleCallbackAsync(SocketReaction reaction) .AddCriterion(new EnsureSourceChannelCriterion()) .AddCriterion(new EnsureFromUserCriterion(reaction.UserId)) .AddCriterion(new EnsureIsIntegerCriterion()); + var response = await Interactive.NextMessageAsync(Context, criteria, TimeSpan.FromSeconds(15)); + var request = int.Parse(response.Content); if (request < 1 || request > _pages) { @@ -118,45 +142,75 @@ public async Task HandleCallbackAsync(SocketReaction reaction) await Interactive.ReplyAndDeleteAsync(Context, Options.Stop.Name); return; } - _page = request; + + _currentPage = request; _ = response.DeleteAsync().ConfigureAwait(false); await RenderAsync().ConfigureAwait(false); }); } else if (emote.Equals(Options.Info)) { - await Interactive.ReplyAndDeleteAsync(Context, Options.InformationText, timeout: Options.InfoTimeout); + await Interactive.ReplyAndDeleteAsync(Context, null, embed: + new EmbedBuilder() + .WithTitle(Options.InformationTitle) + .WithDescription(Options.InformationText) + .WithColor(Options.InformationColor) + .Build(), + timeout: Options.InfoTimeout); + return false; } + _ = Message.RemoveReactionAsync(reaction.Emote, reaction.User.Value); await RenderAsync().ConfigureAwait(false); return false; } - - protected virtual Embed BuildEmbed() + + protected Embed BuildEmbed() { - var builder = new EmbedBuilder() - .WithAuthor(_pager.Author) - .WithColor(_pager.Color) - .WithFooter(f => f.Text = string.Format(Options.FooterFormat, _page, _pages)) - .WithTitle(_pager.Title); - if (_pager.Pages is IEnumerable efb) + var current = _pager.Pages.ElementAt(_currentPage - 1); + var builder = new EmbedBuilder { - builder.Fields = efb.Skip((_page - 1) * Options.FieldsPerPage).Take(Options.FieldsPerPage).ToList(); - builder.Description = _pager.AlternateDescription; + Title = current.Title ?? _pager.Title, + Url = current.Url ?? _pager.Url, + Description = current.Description ?? _pager.Description, + ImageUrl = current.ImageUrl ?? _pager.ImageUrl, + Color = current.Color ?? _pager.Color, + Fields = current.Fields ?? _pager.Fields, + Footer = current.FooterOverride ?? _pager.FooterOverride ?? new EmbedFooterBuilder + { + Text = string.Format(Options.FooterFormat, _currentPage, _pages) + }, + ThumbnailUrl = current.ThumbnailUrl ?? _pager.ThumbnailUrl, + Timestamp = current.TimeStamp ?? _pager.TimeStamp + }; + + if (current.DisplayTotalFieldsCount) + { + builder + .WithAuthor(author => + { + author.Name = $"{current.AlternateAuthorTitle}\nPage {_currentPage}/{_pages} ({Math.Round(_pager.Pages.Sum(x => x.Fields.Count) * current.TotalFieldsCountConstant)} {current.TotalFieldsMessage})"; + author.IconUrl = current.AlteranteAuthorIcon; + }); } else { - builder.Description = _pager.Pages.ElementAt(_page - 1).ToString(); + builder + .WithAuthor(author => + { + author.Name = $"{current.AlternateAuthorTitle}\nPage {_currentPage}/{_pages}"; + author.IconUrl = current.AlteranteAuthorIcon; + }); } return builder.Build(); } - - private async Task RenderAsync() + + private Task RenderAsync() { var embed = BuildEmbed(); - await Message.ModifyAsync(m => m.Embed = embed).ConfigureAwait(false); + return Message.ModifyAsync(m => m.Embed = embed); } } } \ No newline at end of file diff --git a/Discord.Addons.Interactive/Paginator/ReactionList.cs b/Discord.Addons.Interactive/Paginator/ReactionList.cs new file mode 100644 index 0000000..ef5120a --- /dev/null +++ b/Discord.Addons.Interactive/Paginator/ReactionList.cs @@ -0,0 +1,13 @@ +namespace Discord.Addons.Interactive.Paginator +{ + public class ReactionList + { + public bool First { get; set; } = true; + public bool Last { get; set; } = true; + public bool Forward { get; set; } = true; + public bool Backward { get; set; } = true; + public bool Jump { get; set; } = true; + public bool Trash { get; set; } = true; + public bool Info { get; set; } = true; + } +} \ No newline at end of file diff --git a/SampleApp/Modules/SampleModule.cs b/SampleApp/Modules/SampleModule.cs index fb4bd49..87a796a 100644 --- a/SampleApp/Modules/SampleModule.cs +++ b/SampleApp/Modules/SampleModule.cs @@ -3,13 +3,15 @@ using Discord.Addons.Interactive.InlineReaction; using Discord.Commands; using System; +using System.Collections.Generic; using System.Threading.Tasks; +using Discord.Addons.Interactive.Paginator; namespace SampleApp.Modules { public class SampleModule : InteractiveBase { - // DeleteAfterAsync will send a message and asynchronously delete it after the timeout has popped + // DeleteAfterAsync will send a message and asynchronously delete it after the timeout has popped. // This method will not block. [Command("delete")] public async Task Test_DeleteAfterAsync() @@ -19,41 +21,83 @@ public async Task Test_DeleteAfterAsync() } // NextMessageAsync will wait for the next message to come in over the gateway, given certain criteria - // By default, this will be limited to messages from the source user in the source channel + // By default, this will be limited to messages from the source user in the source channel. // This method will block the gateway, so it should be ran in async mode. [Command("next", RunMode = RunMode.Async)] public async Task Test_NextMessageAsync() { await ReplyAsync("What is 2+2?"); + var response = await NextMessageAsync(); if (response != null) + { await ReplyAsync($"You replied: {response.Content}"); + } else + { await ReplyAsync("You did not reply before the timeout"); + } } // PagedReplyAsync will send a paginated message to the channel - // You can customize the paginator by creating a PaginatedMessage object - // You can customize the criteria for the paginator as well, which defaults to restricting to the source user + // You can customize the paginator by creating a EmbedPage objects, which each represent a single page. There + // are lots of options for you to explore. + // You can customize the criteria for the paginator as well, which defaults to restricting to the source user. // This method will not block. [Command("paginator")] public async Task Test_Paginator() { - var pages = new[] { "Page 1", "Page 2", "Page 3", "aaaaaa", "Page 5" }; - await PagedReplyAsync(pages); + // Use an object initializer ideally. This code is written the way it is for demonstration purposes. + var pages = new List(); + + var p1 = new EmbedPage + { + Title = "First Page", + Description = "Interesting Information", + }; + + var p2 = new EmbedPage + { + Title = "Second Page", + AlternateAuthorTitle = Context.User.Username, + AlteranteAuthorIcon = Context.User.GetAvatarUrl() + }; + + var p3 = new EmbedPage + { + ImageUrl = "https://img2.gelbooru.com/samples/a5/9c/sample_a59c7a3eefe67b062ea37825ce6cea83.jpg", + }; + + pages.Add(p1); + pages.Add(p2); + pages.Add(p3); + + var options = new PaginatedAppearanceOptions + { + InformationText = "This fancy embed is called a Paginator", + Timeout = TimeSpan.FromSeconds(30), + }; + + var pagedEmbed = new PaginatedMessage + { + Pages = pages, + Options = options, + FooterOverride = new EmbedFooterBuilder().WithText("Nice Footer") + }; + + await PagedReplyAsync(pagedEmbed, new ReactionList()); } // InlineReactionReplyAsync will send a message and adds reactions on it. // Once an user adds a reaction, the callback is fired. - // If callback was successful next callback is not handled + // If callback was successful next callback is not handled. // Unsuccessful callback is a reaction that did not have a callback. [Command("reaction")] public async Task Test_ReactionReply() { await InlineReactionReplyAsync(new ReactionCallbackData("text", null, false, false) .WithCallback(new Emoji("👍"), (c, r) => c.Channel.SendMessageAsync($"{r.User.Value.Mention} replied with 👍")) - .WithCallback(new Emoji("👎"), (c, r) => c.Channel.SendMessageAsync($"{r.User.Value.Mention} replied with 👎")) - ); + .WithCallback(new Emoji("👎"), (c, r) => c.Channel.SendMessageAsync($"{r.User.Value.Mention} replied with 👎"))); } } } \ No newline at end of file diff --git a/SampleApp/Program.cs b/SampleApp/Program.cs index 92f6399..5308dae 100644 --- a/SampleApp/Program.cs +++ b/SampleApp/Program.cs @@ -5,25 +5,30 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.IO; -using System.Reflection; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; namespace SampleApp { - internal class Program + public class Program { - private static void Main(string[] args) - => new Program().MainAsync().GetAwaiter().GetResult(); - - private DiscordSocketClient _client; - private CommandService _commands; - private IServiceProvider _services; - - public async Task MainAsync() + private static DiscordSocketClient _client; + private static CommandService _commands; + private static IServiceProvider _services; + private static IConfigurationRoot _configuration; + + public static async Task Main(string[] args) { - var token = File.ReadAllText("token.ignore"); + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json"); - _client = new DiscordSocketClient(); + _configuration = builder.Build(); + + _services = ConfigureServices(); + + _client = _services.GetRequiredService(); + _commands = _services.GetRequiredService(); _client.Log += log => { @@ -31,32 +36,46 @@ public async Task MainAsync() return Task.CompletedTask; }; + var token = _configuration["Token"]; + await _client.LoginAsync(TokenType.Bot, token); await _client.StartAsync(); - - _services = new ServiceCollection() - .AddSingleton(_client) - .AddSingleton() - .BuildServiceProvider(); - - _commands = new CommandService(); - await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); - + + await _commands.AddModulesAsync(typeof(Program).Assembly, _services); + _client.MessageReceived += HandleCommandAsync; await Task.Delay(-1); } - public async Task HandleCommandAsync(SocketMessage m) + private static IServiceProvider ConfigureServices() { - if (!(m is SocketUserMessage msg)) return; - if (msg.Author.IsBot) return; + var client = new DiscordSocketClient(); + var commands = new CommandService(); - var argPos = 0; - if (!(msg.HasStringPrefix("i~>", ref argPos))) return; + return new ServiceCollection() + .AddSingleton(client) + .AddSingleton(commands) + .AddSingleton(_configuration) + .AddSingleton() + .BuildServiceProvider(); + } + + private static async Task HandleCommandAsync(SocketMessage socketMessage) + { + if (!(socketMessage is SocketUserMessage message) + || !(message.Author is IGuildUser guildUser) + || guildUser.IsBot) + { + return; + } - var context = new SocketCommandContext(_client, msg); - await _commands.ExecuteAsync(context, argPos, _services); + var argPos = 0; + if (message.HasStringPrefix("!!", ref argPos)) + { + var context = new SocketCommandContext(_client, message); + await _commands.ExecuteAsync(context, argPos, _services); + } } } } \ No newline at end of file diff --git a/SampleApp/SampleApp.csproj b/SampleApp/SampleApp.csproj index 420f6f7..061b496 100644 --- a/SampleApp/SampleApp.csproj +++ b/SampleApp/SampleApp.csproj @@ -8,6 +8,9 @@ + + + @@ -15,4 +18,10 @@ + + + PreserveNewest + + + \ No newline at end of file diff --git a/SampleApp/appsettings.json b/SampleApp/appsettings.json new file mode 100644 index 0000000..45d94ce --- /dev/null +++ b/SampleApp/appsettings.json @@ -0,0 +1,3 @@ +{ + "Token": "" +} \ No newline at end of file