-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7d11867
commit 864c480
Showing
7 changed files
with
427 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using Discord; | ||
using Discord.Interactions; | ||
using Librarian.Services; | ||
|
||
namespace Librarian.Commands; | ||
|
||
/// <summary> | ||
/// Represents a class which implements the <c>Bookmark</c> context menu. | ||
/// </summary> | ||
internal sealed class BookmarkCommand : InteractionModuleBase<SocketInteractionContext> | ||
{ | ||
private readonly BookmarkService _bookmarkService; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="BookmarkCommand" /> class. | ||
/// </summary> | ||
/// <param name="bookmarkService">The bookmark service.</param> | ||
public BookmarkCommand(BookmarkService bookmarkService) | ||
{ | ||
_bookmarkService = bookmarkService; | ||
} | ||
|
||
[MessageCommand("Bookmark")] | ||
[RequireContext(ContextType.Guild)] | ||
public async Task BookmarkAsync(IMessage message) | ||
{ | ||
if (message is not IUserMessage userMessage) | ||
{ | ||
return; | ||
} | ||
|
||
var member = (IGuildUser)Context.User; | ||
IUserMessage? bookmark = await _bookmarkService.CreateBookmarkAsync(member, userMessage).ConfigureAwait(false); | ||
if (bookmark is null) | ||
{ | ||
await RespondAsync("Bookmark failed. Please make sure you have DMs enabled.", ephemeral: true).ConfigureAwait(false); | ||
return; | ||
} | ||
|
||
var response = $"Bookmark created. [Check your DMs]({bookmark.GetJumpUrl()}) to view your bookmarks."; | ||
await RespondAsync(response, ephemeral: true).ConfigureAwait(false); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
using System.Text; | ||
using Discord; | ||
using Discord.Interactions; | ||
using Discord.WebSocket; | ||
using Humanizer; | ||
using Librarian.Services; | ||
|
||
namespace Librarian.Commands; | ||
|
||
/// <summary> | ||
/// Represents a class which implements the <c>info</c> command. | ||
/// </summary> | ||
internal sealed class InfoCommand : InteractionModuleBase<SocketInteractionContext> | ||
{ | ||
private readonly BotService _botService; | ||
private readonly DiscordSocketClient _discordClient; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="InfoCommand" /> class. | ||
/// </summary> | ||
/// <param name="botService">The bot service.</param> | ||
/// <param name="discordClient"></param> | ||
public InfoCommand(BotService botService, DiscordSocketClient discordClient) | ||
{ | ||
_botService = botService; | ||
_discordClient = discordClient; | ||
} | ||
|
||
[SlashCommand("info", "Displays information about the bot.")] | ||
[RequireContext(ContextType.Guild)] | ||
public async Task InfoAsync() | ||
{ | ||
SocketGuildUser member = Context.Guild.GetUser(_discordClient.CurrentUser.Id); | ||
string botVerison = _botService.Version; | ||
|
||
SocketRole? highestRole = member.Roles.Where(r => r.Color != Color.Default).MaxBy(r => r.Position); | ||
|
||
var embed = new EmbedBuilder(); | ||
embed.WithAuthor(member); | ||
embed.WithColor(highestRole?.Color ?? Color.Default); | ||
embed.WithThumbnailUrl(member.GetAvatarUrl()); | ||
embed.WithTitle($"Librarian v{botVerison}"); | ||
embed.AddField("Ping", $"{_discordClient.Latency} ms", true); | ||
embed.AddField("Uptime", (DateTimeOffset.UtcNow - _botService.StartedAt).Humanize(), true); | ||
embed.AddField("View Source", "[View on GitHub](https://github.com/BrackeysBot/Librarian)", true); | ||
|
||
var builder = new StringBuilder(); | ||
builder.AppendLine($"Librarian: {botVerison}"); | ||
builder.AppendLine($"Discord.Net: {_botService.DiscordNetVersion}"); | ||
builder.AppendLine($"CLR: {Environment.Version.ToString(3)}"); | ||
builder.AppendLine($"Host: {Environment.OSVersion}"); | ||
|
||
embed.AddField("Version", $"```\n{builder}\n```"); | ||
|
||
await RespondAsync(embed: embed.Build(), ephemeral: true).ConfigureAwait(false); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,41 @@ | ||
// See https://aka.ms/new-console-template for more information | ||
using Discord; | ||
using Discord.Interactions; | ||
using Discord.WebSocket; | ||
using Librarian.Services; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Hosting; | ||
using Microsoft.Extensions.Logging; | ||
using Serilog; | ||
using X10D.Hosting.DependencyInjection; | ||
|
||
Console.WriteLine("Hello, World!"); | ||
Directory.CreateDirectory("data"); | ||
|
||
Log.Logger = new LoggerConfiguration() | ||
.WriteTo.Console() | ||
.WriteTo.File("logs/latest.log", rollingInterval: RollingInterval.Day) | ||
#if DEBUG | ||
.MinimumLevel.Debug() | ||
#endif | ||
.CreateLogger(); | ||
|
||
|
||
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); | ||
builder.Configuration.AddJsonFile("data/config.json", true, true); | ||
builder.Logging.ClearProviders(); | ||
builder.Logging.AddSerilog(); | ||
|
||
builder.Services.AddSingleton<DiscordSocketClient>(); | ||
builder.Services.AddSingleton<InteractionService>(); | ||
builder.Services.AddSingleton(new DiscordSocketConfig | ||
{ | ||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMessages | GatewayIntents.MessageContent | ||
}); | ||
|
||
builder.Services.AddHostedSingleton<BookmarkService>(); | ||
builder.Services.AddHostedSingleton<BookmarkEmoteService>(); | ||
|
||
builder.Services.AddHostedSingleton<BotService>(); | ||
|
||
IHost app = builder.Build(); | ||
await app.RunAsync(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using Discord; | ||
using Discord.WebSocket; | ||
using Microsoft.Extensions.Hosting; | ||
|
||
namespace Librarian.Services; | ||
|
||
/// <summary> | ||
/// Represents a service which listens for the bookmark emote. | ||
/// </summary> | ||
internal sealed class BookmarkEmoteService : BackgroundService | ||
{ | ||
private readonly DiscordSocketClient _discordClient; | ||
private readonly BookmarkService _bookmarkService; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="BookmarkDeletionService" /> class. | ||
/// </summary> | ||
/// <param name="discordClient">The Discord client.</param> | ||
/// <param name="bookmarkService">The bookmark service.</param> | ||
public BookmarkEmoteService(DiscordSocketClient discordClient, BookmarkService bookmarkService) | ||
{ | ||
_discordClient = discordClient; | ||
_bookmarkService = bookmarkService; | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override Task ExecuteAsync(CancellationToken stoppingToken) | ||
{ | ||
_discordClient.ReactionAdded += OnBookmarkAdded; | ||
return Task.CompletedTask; | ||
} | ||
|
||
private async Task OnBookmarkAdded(Cacheable<IUserMessage, ulong> message, | ||
Cacheable<IMessageChannel, ulong> channel, | ||
SocketReaction reaction) | ||
{ | ||
var theChannel = (ITextChannel)(channel.HasValue ? channel.Value : reaction.Channel); | ||
var theMessage = (IUserMessage)(message.HasValue | ||
? message.Value | ||
: await theChannel.GetMessageAsync(message.Id).ConfigureAwait(false) | ||
); | ||
|
||
if (theMessage.Channel is not ITextChannel) return; // ignore DMs | ||
if (reaction.Emote.Name != "🔖") return; | ||
|
||
IUser user = reaction.User.Value; | ||
|
||
await theMessage.RemoveReactionAsync(reaction.Emote, user).ConfigureAwait(false); | ||
await _bookmarkService.CreateBookmarkAsync((IGuildUser)user, theMessage).ConfigureAwait(false); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using Discord; | ||
using Discord.WebSocket; | ||
using Microsoft.Extensions.Hosting; | ||
|
||
namespace Librarian.Services; | ||
|
||
internal sealed class BookmarkService : BackgroundService | ||
{ | ||
private readonly DiscordSocketClient _discordClient; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="BookmarkService" /> class. | ||
/// </summary> | ||
/// <param name="discordClient">The Discord client.</param> | ||
public BookmarkService(DiscordSocketClient discordClient) | ||
{ | ||
_discordClient = discordClient; | ||
} | ||
|
||
/// <summary> | ||
/// Creates a bookmark for the specified message. | ||
/// </summary> | ||
/// <param name="user">The user who bookmarked the message.</param> | ||
/// <param name="message">The message to bookmark.</param> | ||
/// <returns>The bookmark sent to the user.</returns> | ||
/// <exception cref="ArgumentNullException"> | ||
/// <paramref name="user" /> or <paramref name="message" /> is <see langword="null" />. | ||
/// </exception> | ||
public async Task<IUserMessage?> CreateBookmarkAsync(IGuildUser user, IUserMessage message) | ||
{ | ||
if (user is null) throw new ArgumentNullException(nameof(user)); | ||
if (message is null) throw new ArgumentNullException(nameof(message)); | ||
|
||
IDMChannel dmChannel; | ||
try | ||
{ | ||
dmChannel = await user.CreateDMChannelAsync(); | ||
} | ||
catch | ||
{ | ||
return null; | ||
} | ||
|
||
IGuild guild = user.Guild; | ||
|
||
var embed = new EmbedBuilder(); | ||
embed.WithTitle("🔖 Bookmarked Message"); | ||
embed.WithDescription($"You bookmarked a message in **{guild.Name}**."); | ||
embed.WithColor(Color.Green); | ||
embed.WithTimestamp(DateTimeOffset.UtcNow); | ||
embed.WithThumbnailUrl(guild.IconUrl); | ||
embed.AddField("Author", message.Author.Mention, true); | ||
embed.AddField("Sent", $"<t:{message.Timestamp.ToUnixTimeSeconds()}:R>", true); | ||
embed.AddField("Channel", MentionUtils.MentionChannel(message.Channel.Id), true); | ||
|
||
var jumpButton = new ButtonBuilder(); | ||
jumpButton.WithLabel("Jump to Message"); | ||
jumpButton.WithStyle(ButtonStyle.Link); | ||
jumpButton.WithUrl(message.GetJumpUrl()); | ||
|
||
var deleteButton = new ButtonBuilder(); | ||
deleteButton.WithLabel("Delete Bookmark"); | ||
deleteButton.WithStyle(ButtonStyle.Danger); | ||
deleteButton.WithCustomId("delete_bookmark"); | ||
deleteButton.WithEmote(new Emoji("🗑️")); | ||
|
||
var components = new ComponentBuilder(); | ||
components.WithButton(jumpButton); | ||
components.WithButton(deleteButton); | ||
|
||
return await dmChannel.SendMessageAsync(embed: embed.Build(), components: components.Build()).ConfigureAwait(false); | ||
} | ||
|
||
protected override Task ExecuteAsync(CancellationToken stoppingToken) | ||
{ | ||
_discordClient.ButtonExecuted += OnDeleteBookmark; | ||
return Task.CompletedTask; | ||
} | ||
|
||
private async Task OnDeleteBookmark(SocketMessageComponent component) | ||
{ | ||
if (component.Data.CustomId != "delete_bookmark") | ||
{ | ||
return; | ||
} | ||
|
||
await component.Message.DeleteAsync().ConfigureAwait(false); | ||
await component.RespondAsync("Bookmark deleted!", ephemeral: true).ConfigureAwait(false); | ||
} | ||
} |
Oops, something went wrong.