Skip to content

Feat: Misc Fixes & Recruitment Channel BabySitter #258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Aug 2, 2023
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
627b38b
Fix: Discriminator Use, replace with a `UserNameReference` Method for…
SimplyJpk Jul 25, 2023
9a5843f
Fix: Add a secondary try-catch for empty table generation to give a r…
SimplyJpk Jul 25, 2023
2815978
Fix: Correct my mistaken formatting
SimplyJpk Jul 25, 2023
bd2f4ea
Couple changes to DisplayName use, Track edited messages, inc display…
SimplyJpk Jul 27, 2023
65c2854
Update ModerationService.cs
SimplyJpk Jul 27, 2023
07a83d7
Update ReadMe to include details about docker and optional setup
SimplyJpk Jul 29, 2023
21ca7e0
Further expand some details about bot, LoggingService & DiscordNet Api
SimplyJpk Jul 29, 2023
f14beaf
Add settings to enable/disable UnityHelpService
SimplyJpk Jul 29, 2023
538f644
Feat: Recruitment Service to hopefully better moderate Job seeking
SimplyJpk Jul 29, 2023
79f0b1b
Fix a number of concerns, improve message edit and order of opperations
SimplyJpk Jul 29, 2023
94fe924
Add latest settings to example
SimplyJpk Jul 31, 2023
59240d8
Fix: Hopefully Issue with UserService failing to welcome new users
SimplyJpk Jul 31, 2023
b588e0c
Add Setting to enable/disable ReactRole Service to avoid additional g…
SimplyJpk Aug 1, 2023
335c1e7
Impr: Add Extended Logging Enum for more colours, Positive & LowWarning
SimplyJpk Aug 1, 2023
528f976
Impr: UnityHelpService same log prefix
SimplyJpk Aug 1, 2023
50b5c23
Feat: Utility method for moderators to help identify channel tags
SimplyJpk Aug 1, 2023
900d40b
Fix: Handful of complaints deepsource has about the code
SimplyJpk Aug 1, 2023
0ef997c
Update RecruitService.cs
SimplyJpk Aug 1, 2023
5f2165d
Fix: More deepsource complaints
SimplyJpk Aug 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions DiscordBot/Extensions/UserExtensions.cs
Original file line number Diff line number Diff line change
@@ -17,4 +17,21 @@ public static bool HasRoleGroup(this IUser user, ulong roleId)
{
return user is SocketGuildUser guildUser && guildUser.Roles.Any(x => x.Id == roleId);
}

// Returns the users DisplayName (nickname) if it exists, otherwise returns the username
public static string GetUserPreferredName(this IUser user)
{
var guildUser = user as SocketGuildUser;
return guildUser?.DisplayName ?? user.Username;
}

public static string GetPreferredAndUsername(this IUser user)
{
var guildUser = user as SocketGuildUser;
if (guildUser == null)
return user.Username;
if (guildUser.DisplayName == user.Username)
return guildUser.DisplayName;
return $"{guildUser.DisplayName} (aka {user.Username})";
}
}
47 changes: 40 additions & 7 deletions DiscordBot/Modules/ModerationModule.cs
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@
using DiscordBot.Settings;
using Pathoschild.NaturalTimeParser.Parser;
using DiscordBot.Attributes;
using DiscordBot.Utils;
using Org.BouncyCastle.Asn1.Cms;

namespace DiscordBot.Modules;

@@ -262,13 +264,8 @@ public async Task RulesCommand(IMessageChannel channel, int seconds = 60)
{
//Display rules of this channel for x seconds
var rule = Rules.Channel.First(x => x.Id == 0);
IUserMessage m;
if (rule == null)
m = await ReplyAsync(
"There is no special rule for this channel.\nPlease follow global rules (you can get them by typing `!globalrules`)");
else
m = await ReplyAsync(
$"{rule.Header}{(rule.Content.Length > 0 ? rule.Content : "There is no special rule for this channel.\nPlease follow global rules (you can get them by typing `!globalrules`)")}");
var m = await ReplyAsync(
$"{rule.Header}{(rule.Content.Length > 0 ? rule.Content : "There is no special rule for this channel.\nPlease follow global rules (you can get them by typing `!globalrules`)")}");

var deleteAsync = Context.Message?.DeleteAsync();
if (deleteAsync != null) await deleteAsync;
@@ -421,6 +418,7 @@ public async Task FullSync()
}

#region General Utility Commands

[Command("WelcomeMessageCount")]
[Summary("Returns a count of pending welcome messages.")]
[RequireModerator, HideFromHelp]
@@ -440,6 +438,41 @@ public async Task WelcomeMessageCount()
}
await Context.Message.DeleteAsync();
}

// Command to show the tags available for a specific channel, so the command needs to be run in a channel with tags or specific a channel id to check
[Command("ChannelTags")]
[Summary("Returns a list of tags for the current channel.")]
[RequireModerator, HideFromHelp]
public async Task ChannelTags(ulong channelId)
{
// Get the channel
var channel = await Context.Guild.GetChannelAsync(channelId);

if (channel is not IForumChannel forumChannel)
{
await ReplyAsync($"<#{channelId}> is not a forum channel and has no tags.").DeleteAfterSeconds(seconds: 10);
return;
}

var tags = forumChannel.Tags;
// If there are no tags, say so
if (tags.Count == 0)
{
await ReplyAsync($"<#{channelId}> has no tags.").DeleteAfterSeconds(seconds: 10);
return;
}

// If there are tags, list them in an embed in format of (ID: `id` - Name: `name`)
var embed = new EmbedBuilder()
.WithTitle($"Tags for <#{channelId}>")
.WithDescription(string.Join("\n", tags.Select(tag => $"ID: `{tag.Id}` - Name: `{tag.Name}`")) +
$"\n\n{StringUtil.MessageSelfDestructIn(60)}")
.WithColor(Color.Blue)
.Build();

Context.Message.DeleteAsync();
await ReplyAsync(embed: embed).DeleteAfterSeconds(seconds: 60);
}

#endregion

2 changes: 1 addition & 1 deletion DiscordBot/Modules/UserModule.cs
Original file line number Diff line number Diff line change
@@ -131,7 +131,7 @@ public async Task QuoteMessage(ulong messageId, IMessageChannel channel = null)
.WithFooter(footer =>
{
footer
.WithText($"Quoted by {Context.User.Username}#{Context.User.Discriminator} • From channel {message.Channel.Name}")
.WithText($"Quoted by {Context.User.GetUserPreferredName()} • From channel {message.Channel.Name}")
.WithIconUrl(Context.User.GetAvatarUrl());
})
.WithAuthor(author =>
2 changes: 1 addition & 1 deletion DiscordBot/Modules/UserSlashModule.cs
Original file line number Diff line number Diff line change
@@ -170,7 +170,7 @@ public async Task ModalResponse(ulong id, ReportMessageModal modal)
.WithFooter(footer =>
{
footer
.WithText($"Reported by {Context.User.Username}#{Context.User.Discriminator} • From channel {reportedMessage.Channel.Name}")
.WithText($"Reported by {Context.User.GetPreferredAndUsername()} • From channel {reportedMessage.Channel.Name}")
.WithIconUrl(Context.User.GetAvatarUrl());
})
.WithAuthor(author =>
4 changes: 3 additions & 1 deletion DiscordBot/Program.cs
Original file line number Diff line number Diff line change
@@ -64,10 +64,11 @@ private async Task MainAsync()
?.GetTextChannel(_settings.BotAnnouncementChannel.Id)
?.SendMessageAsync("Bot Started.");

LoggingService.LogToConsole("Bot is connected.", LogSeverity.Info);
LoggingService.LogToConsole("Bot is connected.", ExtendedLogSeverity.Positive);
_isInitialized = true;

_unityHelpService = _services.GetRequiredService<UnityHelpService>();
_services.GetRequiredService<RecruitService>();
}
return Task.CompletedTask;
};
@@ -91,6 +92,7 @@ private IServiceProvider ConfigureServices() =>
.AddSingleton<PublisherService>()
.AddSingleton<FeedService>()
.AddSingleton<UnityHelpService>()
.AddSingleton<RecruitService>()
.AddSingleton<UpdateService>()
.AddSingleton<CurrencyService>()
.AddSingleton<ReactRoleService>()
35 changes: 25 additions & 10 deletions DiscordBot/Services/DatabaseService.cs
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@ namespace DiscordBot.Services;

public class DatabaseService
{
private const string ServiceName = "DatabaseService";

private readonly ILoggingService _logging;
private string ConnectionString { get; }

@@ -39,20 +41,33 @@ public DatabaseService(ILoggingService logging, BotSettings settings)
try
{
var userCount = await _connection.TestConnection();
await _logging.LogAction($"DatabaseService: Connected to database successfully. {userCount} users in database.");
await _logging.LogAction($"{ServiceName}: Connected to database successfully. {userCount} users in database.");
LoggingService.LogToConsole($"{ServiceName}: Connected to database successfully. {userCount} users in database.", ExtendedLogSeverity.Positive);
}
catch (Exception)
catch
{
LoggingService.LogToConsole(
"DatabaseService: Table 'users' does not exist, attempting to generate table.",
LogSeverity.Warning);
c.ExecuteSql(
"CREATE TABLE `users` (`ID` int(11) UNSIGNED NOT NULL, `UserID` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL, `Karma` int(11) UNSIGNED NOT NULL DEFAULT 0, `KarmaWeekly` int(11) UNSIGNED NOT NULL DEFAULT 0, `KarmaMonthly` int(11) UNSIGNED NOT NULL DEFAULT 0, `KarmaYearly` int(11) UNSIGNED NOT NULL DEFAULT 0, `KarmaGiven` int(11) UNSIGNED NOT NULL DEFAULT 0, `Exp` bigint(11) UNSIGNED NOT NULL DEFAULT 0, `Level` int(11) UNSIGNED NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
c.ExecuteSql("ALTER TABLE `users` ADD PRIMARY KEY (`ID`,`UserID`), ADD UNIQUE KEY `UserID` (`UserID`)");
c.ExecuteSql(
"ALTER TABLE `users` MODIFY `ID` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1");
ExtendedLogSeverity.LowWarning);
try
{
c.ExecuteSql(
"CREATE TABLE `users` (`ID` int(11) UNSIGNED NOT NULL, `UserID` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL, `Karma` int(11) UNSIGNED NOT NULL DEFAULT 0, `KarmaWeekly` int(11) UNSIGNED NOT NULL DEFAULT 0, `KarmaMonthly` int(11) UNSIGNED NOT NULL DEFAULT 0, `KarmaYearly` int(11) UNSIGNED NOT NULL DEFAULT 0, `KarmaGiven` int(11) UNSIGNED NOT NULL DEFAULT 0, `Exp` bigint(11) UNSIGNED NOT NULL DEFAULT 0, `Level` int(11) UNSIGNED NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
c.ExecuteSql(
"ALTER TABLE `users` ADD PRIMARY KEY (`ID`,`UserID`), ADD UNIQUE KEY `UserID` (`UserID`)");
c.ExecuteSql(
"ALTER TABLE `users` MODIFY `ID` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1");
}
catch (Exception e)
{
LoggingService.LogToConsole(
$"SQL Exception: Failed to generate table 'users'.\nMessage: {e}",
LogSeverity.Critical);
c.Close();
return;
}
LoggingService.LogToConsole("DatabaseService: Table 'users' generated without errors.",
LogSeverity.Info);
ExtendedLogSeverity.Positive);
c.Close();
}

@@ -134,7 +149,7 @@ public async Task AddNewUser(SocketGuildUser socketUser)
await Query().InsertUser(user);

await _logging.LogAction(
$"User {socketUser.Username}#{socketUser.DiscriminatorValue.ToString()} successfully added to the database.",
$"User {socketUser.GetPreferredAndUsername()} successfully added to the database.",
true,
false);
}
82 changes: 70 additions & 12 deletions DiscordBot/Services/LoggingService.cs
Original file line number Diff line number Diff line change
@@ -5,6 +5,43 @@

namespace DiscordBot.Services.Logging;

#region Extended Log Severity

// We use DNets built in severity levels, but we add a few more for internal logging.
public enum ExtendedLogSeverity
{
Critical = LogSeverity.Critical,
Error = LogSeverity.Error,
Warning = LogSeverity.Warning,
Info = LogSeverity.Info,
Verbose = LogSeverity.Verbose,
Debug = LogSeverity.Debug,
// Extended levels
Positive = 10, // Positive(info) is green in the console
LowWarning = 11, // LowWarning(warning) is yellow in the console
}

public static class ExtendedLogSeverityExtensions
{
public static LogSeverity ToLogSeverity(this ExtendedLogSeverity severity)
{
return severity switch
{
ExtendedLogSeverity.Positive => LogSeverity.Info,
ExtendedLogSeverity.LowWarning => LogSeverity.Warning,
_ => (LogSeverity)severity
};
}

public static ExtendedLogSeverity ToExtended(this LogSeverity severity)
{
return (ExtendedLogSeverity)severity;
}

}

#endregion // Extended Log Severity

public class LoggingService : ILoggingService
{
private readonly DiscordSocketClient _client;
@@ -45,15 +82,16 @@ public static string ConsistentDateTimeFormat()
// Logs DiscordNet specific messages, this shouldn't be used for normal logging
public static Task DiscordNetLogger(LogMessage message)
{
LoggingService.LogToConsole($"{message.Source} | {message.Message}", message.Severity);
LoggingService.LogToConsole($"{message.Source} | {message.Message}", message.Severity.ToExtended());
return Task.CompletedTask;
}
#region Console Messages
// Logs message to console without changing the colour
public static void LogConsole(string message) {
Console.WriteLine($"[{ConsistentDateTimeFormat()}] {message}");
}
public static void LogToConsole(string message, LogSeverity severity = LogSeverity.Info)

public static void LogToConsole(string message, ExtendedLogSeverity severity = ExtendedLogSeverity.Info)
{
ConsoleColor restoreColour = Console.ForegroundColor;
SetConsoleColour(severity);
@@ -62,39 +100,59 @@ public static void LogToConsole(string message, LogSeverity severity = LogSeveri

Console.ForegroundColor = restoreColour;
}
public static void LogToConsole(string message, LogSeverity severity) => LogToConsole(message, severity.ToExtended());

public static void LogServiceDisabled(string service, string varName)
{
LogToConsole($"Service \"{service}\" is Disabled, {varName} is false in settings.json", ExtendedLogSeverity.Warning);
}

public static void LogServiceEnabled(string service)
{
LogToConsole($"Service \"{service}\" is Enabled", ExtendedLogSeverity.Info);
}

/// <summary>
/// Same behaviour as LogToConsole, however this method is not included in the release build.
/// Good if you need more verbose but obvious logging, but don't want it included in release.
/// </summary>
[Conditional("DEBUG")]
public static void DebugLog(string message, LogSeverity severity = LogSeverity.Info)
public static void DebugLog(string message, ExtendedLogSeverity severity = ExtendedLogSeverity.Info)
{
LogToConsole(message, severity);
}

private static void SetConsoleColour(LogSeverity severity)
[Conditional("DEBUG")]
public static void DebugLog(string message, LogSeverity severity) => DebugLog(message, severity.ToExtended());

private static void SetConsoleColour(ExtendedLogSeverity severity)
{
switch (severity)
{
case LogSeverity.Critical:
case LogSeverity.Error:
case ExtendedLogSeverity.Critical:
case ExtendedLogSeverity.Error:
Console.ForegroundColor = ConsoleColor.Red;
break;
case LogSeverity.Warning:
case ExtendedLogSeverity.Warning:
Console.ForegroundColor = ConsoleColor.Yellow;
break;
case LogSeverity.Info:
case ExtendedLogSeverity.Info:
Console.ForegroundColor = ConsoleColor.White;
break;
case LogSeverity.Verbose:
case LogSeverity.Debug:
case ExtendedLogSeverity.Positive:
Console.ForegroundColor = ConsoleColor.Green;
break;
case ExtendedLogSeverity.LowWarning:
Console.ForegroundColor = ConsoleColor.DarkYellow;
break;
// case ExtendedLogSeverity.Verbose:
// case ExtendedLogSeverity.Debug:
default:
Console.ForegroundColor = ConsoleColor.DarkGray;
break;
}
}
#endregion
}
}

public interface ILoggingService
{
Loading