Skip to content

Commit

Permalink
Merge pull request #273 from Unity-Developer-Community/feat/more-misc…
Browse files Browse the repository at this point in the history
…-fixes

Feature: More misc fixes
  • Loading branch information
Pierre-Demessence authored Jan 21, 2024
2 parents 44b24af + 721e3b4 commit a78a8be
Show file tree
Hide file tree
Showing 21 changed files with 308 additions and 184 deletions.
22 changes: 22 additions & 0 deletions DiscordBot/Attributes/BotCommandChannelAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Discord.Commands;
using DiscordBot.Settings;
using Microsoft.Extensions.DependencyInjection;

namespace DiscordBot.Attributes;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class BotCommandChannelAttribute : PreconditionAttribute
{
public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var settings = services.GetRequiredService<BotSettings>();

if (context.Channel.Id == settings.BotCommandsChannel.Id)
{
return await Task.FromResult(PreconditionResult.FromSuccess());
}

Task task = context.Message.DeleteAfterSeconds(seconds: 10);
return await Task.FromResult(PreconditionResult.FromError($"This command can only be used in <#{settings.BotCommandsChannel.Id.ToString()}>."));
}
}
20 changes: 20 additions & 0 deletions DiscordBot/Attributes/IgnoreBotsAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Discord.Commands;

namespace DiscordBot.Attributes;

/// <summary>
/// Simple attribute, if the command is used by a bot, it escapes early and doesn't run the command.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class IgnoreBotsAttribute : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
if (context.Message.Author.IsBot)
{
return Task.FromResult(PreconditionResult.FromError(string.Empty));
}

return Task.FromResult(PreconditionResult.FromSuccess());
}
}
31 changes: 31 additions & 0 deletions DiscordBot/Attributes/RoleAttributes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Discord.Commands;
using Discord.WebSocket;
using DiscordBot.Settings;
using Microsoft.Extensions.DependencyInjection;

namespace DiscordBot.Attributes;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RequireAdminAttribute : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var user = (SocketGuildUser)context.Message.Author;

if (user.Roles.Any(x => x.Permissions.Administrator)) return Task.FromResult(PreconditionResult.FromSuccess());
return Task.FromResult(PreconditionResult.FromError(user + " attempted to use admin only command!"));
}
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RequireModeratorAttribute : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var user = (SocketGuildUser)context.Message.Author;
var settings = services.GetRequiredService<BotSettings>();

if (user.Roles.Any(x => x.Id == settings.ModeratorRoleId)) return Task.FromResult(PreconditionResult.FromSuccess());
return Task.FromResult(PreconditionResult.FromError(user + " attempted to use a moderator command!"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,7 @@
using DiscordBot.Settings;
using Microsoft.Extensions.DependencyInjection;

namespace DiscordBot;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RequireAdminAttribute : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var user = (SocketGuildUser)context.Message.Author;

if (user.Roles.Any(x => x.Permissions.Administrator)) return Task.FromResult(PreconditionResult.FromSuccess());
return Task.FromResult(PreconditionResult.FromError(user + " attempted to use admin only command!"));
}
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RequireModeratorAttribute : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var user = (SocketGuildUser)context.Message.Author;
var settings = services.GetRequiredService<BotSettings>();

if (user.Roles.Any(x => x.Id == settings.ModeratorRoleId)) return Task.FromResult(PreconditionResult.FromSuccess());
return Task.FromResult(PreconditionResult.FromError(user + " attempted to use a moderator command!"));
}
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class BotChannelOnlyAttribute : PreconditionAttribute
{
public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var settings = services.GetRequiredService<BotSettings>();

if (context.Channel.Id == settings.BotCommandsChannel.Id)
{
return await Task.FromResult(PreconditionResult.FromSuccess());
}

Task task = context.Message.DeleteAfterSeconds(seconds: 10);
return await Task.FromResult(PreconditionResult.FromError($"This command can only be used in <#{settings.BotCommandsChannel.Id.ToString()}>."));
}
}
namespace DiscordBot.Attributes;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RequireThreadAttribute : PreconditionAttribute
Expand Down
14 changes: 14 additions & 0 deletions DiscordBot/Extensions/EmbedBuilderExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ public static EmbedBuilder FooterQuoteBy(this EmbedBuilder builder, IUser reques
return builder;
}

public static EmbedBuilder FooterInChannel(this EmbedBuilder builder, IChannel channel)
{
builder.WithFooter(
$"In channel #{channel.Name}", null);
return builder;
}

public static EmbedBuilder AddAuthor(this EmbedBuilder builder, IUser user, bool includeAvatar = true)
{
builder.WithAuthor(
Expand All @@ -27,5 +34,12 @@ public static EmbedBuilder AddAuthor(this EmbedBuilder builder, IUser user, bool
return builder;
}

public static EmbedBuilder AddAuthorWithAction(this EmbedBuilder builder, IUser user, string action, bool includeAvatar = true)
{
builder.WithAuthor(
$"{user.GetUserPreferredName()} - {action}",
includeAvatar ? user.GetAvatarUrl() : null);
return builder;
}

}
8 changes: 8 additions & 0 deletions DiscordBot/Extensions/UserExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,12 @@ public static string GetPreferredAndUsername(this IUser user)
return guildUser.DisplayName;
return $"{guildUser.DisplayName} (aka {user.Username})";
}

public static string GetUserLoggingString(this IUser user)
{
var guildUser = user as SocketGuildUser;
if (guildUser == null)
return $"{user.Username} `{user.Id}`";
return $"{guildUser.GetPreferredAndUsername()} `{guildUser.Id}`";
}
}
1 change: 1 addition & 0 deletions DiscordBot/Modules/EmbedModule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net;
using System.Text;
using Discord.Commands;
using DiscordBot.Attributes;
using Newtonsoft.Json;

// ReSharper disable all UnusedMember.Local
Expand Down
1 change: 1 addition & 0 deletions DiscordBot/Modules/TicketModule.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Discord.Commands;
using DiscordBot.Attributes;
using DiscordBot.Services;
using DiscordBot.Settings;

Expand Down
15 changes: 15 additions & 0 deletions DiscordBot/Modules/UnityHelp/UnityHelpModule.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Discord.Commands;
using Discord.WebSocket;
using DiscordBot.Attributes;
using DiscordBot.Services;
using DiscordBot.Settings;

Expand All @@ -25,6 +26,20 @@ public async Task ResolveAsync()
await Context.Message.DeleteAsync();
await HelpService.OnUserRequestChannelClose(Context.User, Context.Channel as SocketThreadChannel);
}

[Command("pending-questions")]
[Summary("Moderation only command, announces the number of pending questions in the help channel.")]
[RequireModerator, HideFromHelp, IgnoreBots]
public async Task PendingQuestionsAsync()
{
if (!BotSettings.UnityHelpBabySitterEnabled)
{
await ReplyAsync("UnityHelp Service currently disabled.").DeleteAfterSeconds(15);
return;
}
var trackedQuestionCount = HelpService.GetTrackedQuestionCount();
await ReplyAsync($"There are {trackedQuestionCount} pending questions in the help channel.");
}

#region Utility

Expand Down
44 changes: 20 additions & 24 deletions DiscordBot/Modules/UserModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ public async Task UserCompleted(string message)
await Context.Message.DeleteAsync();
}

[Group("Role"), BotChannelOnly]
[Group("Role"), BotCommandChannel]
public class RoleModule : ModuleBase
{
public BotSettings Settings { get; set; }
Expand Down Expand Up @@ -640,7 +640,7 @@ public async Task CoinFlip()

#region Publisher

[Command("PInfo"), BotChannelOnly, Priority(11)]
[Command("PInfo"), BotCommandChannel, Priority(11)]
[Summary("Information on how to get publisher role.")]
[Alias("publisherinfo")]
public async Task PublisherInfo()
Expand All @@ -656,7 +656,7 @@ public async Task PublisherInfo()
await Context.Message.DeleteAfterSeconds(seconds: 2);
}

[Command("Publisher"), BotChannelOnly, HideFromHelp]
[Command("Publisher"), BotCommandChannel, HideFromHelp]
[Summary("Get the Asset-Publisher role by verifying who you are. Syntax: !publisher publisherID")]
public async Task Publisher(uint publisherId)
{
Expand All @@ -676,7 +676,7 @@ public async Task Publisher(uint publisherId)
await Context.Message.DeleteAfterSeconds(seconds: 1);
}

[Command("Verify"), BotChannelOnly, HideFromHelp]
[Command("Verify"), BotCommandChannel, HideFromHelp]
[Summary("Verify a publisher with the code received by email. Syntax : !verify publisherId code")]
public async Task VerifyPackage(uint packageId, string code)
{
Expand Down Expand Up @@ -1014,13 +1014,9 @@ private int ParseNumber(string s)
public async Task Birthday()
{
// URL to cell C15/"Next birthday" cell from Corn's google sheet
var nextBirthday =
"https://docs.google.com/spreadsheets/d/10iGiKcrBl1fjoBNTzdtjEVYEgOfTveRXdI5cybRTnj4/gviz/tq?tqx=out:html&range=C15:C15";
var doc = new HtmlWeb().Load(nextBirthday);

// XPath to the table row
var row = doc.DocumentNode.SelectSingleNode("/html/body/table/tr[2]/td");
var tableText = WebUtility.HtmlDecode(row.InnerText);
const string nextBirthday = "https://docs.google.com/spreadsheets/d/10iGiKcrBl1fjoBNTzdtjEVYEgOfTveRXdI5cybRTnj4/gviz/tq?tqx=out:html&range=C15:C15";

var tableText = await WebUtil.GetHtmlNodeInnerText(nextBirthday, "/html/body/table/tr[2]/td");
var message = $"**{tableText}**";

await ReplyAsync(message).DeleteAfterTime(minutes: 3);
Expand All @@ -1034,29 +1030,29 @@ public async Task Birthday(IUser user)
{
var searchName = user.Username;
// URL to columns B to D of Corn's google sheet
var birthdayTable =
"https://docs.google.com/spreadsheets/d/10iGiKcrBl1fjoBNTzdtjEVYEgOfTveRXdI5cybRTnj4/gviz/tq?tqx=out:html&gid=318080247&range=B:D";
var doc = new HtmlWeb().Load(birthdayTable);
const string birthdayTable = "https://docs.google.com/spreadsheets/d/10iGiKcrBl1fjoBNTzdtjEVYEgOfTveRXdI5cybRTnj4/gviz/tq?tqx=out:html&gid=318080247&range=B:D";
var relevantNodes = await WebUtil.GetHtmlNodes(birthdayTable, "/html/body/table/tr");

var birthdate = default(DateTime);

HtmlNode matchedNode = null;
var matchedLength = int.MaxValue;

// XPath to each table row
foreach (var row in doc.DocumentNode.SelectNodes("/html/body/table/tr"))
foreach (var row in relevantNodes)
{
// XPath to the name column (C)
var nameNode = row.SelectSingleNode("td[2]");
var name = nameNode.InnerText;
if (name.ToLower().Contains(searchName.ToLower()))
// Check for a "Closer" match
if (name.Length < matchedLength)
{
matchedNode = row;
matchedLength = name.Length;
// Nothing will match "Better" so we may as well break out
if (name.Length == searchName.Length) break;
}

if (!name.ToLower().Contains(searchName.ToLower()) || name.Length >= matchedLength)
continue;

// Check for a "Closer" match
matchedNode = row;
matchedLength = name.Length;
// Nothing will match "Better" so we may as well break out
if (name.Length == searchName.Length) break;
}

if (matchedNode != null)
Expand Down
12 changes: 8 additions & 4 deletions DiscordBot/Modules/Weather/WeatherModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ private async Task<EmbedBuilder> TemperatureEmbed(string city, string replaceCit
[Command("Temperature"), HideFromHelp]
[Summary("Attempts to provide the temperature of the user provided.")]
[Alias("temp"), Priority(20)]
public async Task Temperature(IUser user)
public async Task Temperature(IUser user = null)
{
user ??= Context.User;
if (!await DoesUserHaveDefaultCity(user))
return;

Expand Down Expand Up @@ -133,8 +134,9 @@ private async Task<EmbedBuilder> WeatherEmbed(string city, string replaceCityWit

[Command("Weather"), HideFromHelp, Priority(20)]
[Summary("Attempts to provide the weather of the user provided.")]
public async Task CurentWeather(IUser user)
public async Task CurentWeather(IUser user = null)
{
user ??= Context.User;
if (!await DoesUserHaveDefaultCity(user))
return;

Expand Down Expand Up @@ -209,8 +211,9 @@ private async Task<EmbedBuilder> PollutionEmbed(string city, string replaceCityW

[Command("Pollution"), HideFromHelp, Priority(21)]
[Summary("Attempts to provide the pollution conditions of the user provided.")]
public async Task Pollution(IUser user)
public async Task Pollution(IUser user = null)
{
user ??= Context.User;
if (!await DoesUserHaveDefaultCity(user))
return;

Expand Down Expand Up @@ -256,8 +259,9 @@ private async Task<EmbedBuilder> TimeEmbed(string city, string replaceCityWith =

[Command("Time"), HideFromHelp, Priority(22)]
[Summary("Attempts to provide the time of the user provided.")]
public async Task Time(IUser user)
public async Task Time(IUser user = null)
{
user ??= Context.User;
if (!await DoesUserHaveDefaultCity(user))
return;

Expand Down
4 changes: 4 additions & 0 deletions DiscordBot/Program.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics;
using Discord.Commands;
using Discord.Interactions;
using Discord.WebSocket;
Expand Down Expand Up @@ -70,6 +71,8 @@ private async Task MainAsync()

_unityHelpService = _services.GetRequiredService<UnityHelpService>();
_recruitService = _services.GetRequiredService<RecruitService>();
_services.GetRequiredService<IntroductionWatcherService>();

return Task.CompletedTask;
};

Expand All @@ -88,6 +91,7 @@ private IServiceProvider ConfigureServices() =>
.AddSingleton<ILoggingService, LoggingService>()
.AddSingleton<DatabaseService>()
.AddSingleton<UserService>()
.AddSingleton<IntroductionWatcherService>()
.AddSingleton<ModerationService>()
.AddSingleton<PublisherService>()
.AddSingleton<FeedService>()
Expand Down
4 changes: 4 additions & 0 deletions DiscordBot/Services/CommandHandlingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ private async Task HandleCommand(SocketMessage messageParam)
if (result is PreconditionGroupResult groupResult)
{
resultString = groupResult.PreconditionResults.First().ErrorReason;

// Pre-condition doesn't have a reason, we don't respond.
if (resultString == string.Empty)
return;
}
await context.Channel.SendMessageAsync(resultString).DeleteAfterSeconds(10);
}
Expand Down
Loading

0 comments on commit a78a8be

Please sign in to comment.