Tidy up project structure, fix bug with edit logging (#47)

The project structure has been changed because the previous one had
everything in 1 folder. From this PR onwards, the following is true:
- The source code is stored in `src/`
- `*.resx` and `Messages.Designer.cs` is stored in `locale/`
- Documentation is stored on the wiki and in `docs/`
- Miscellaneous files, such as dotfiles, are stored in the root folder
of the repository

This PR additionally fixes an issue that would cause logs of edited
messages to not be syntax highlighted. This happened because the
responder of edited messages was changed to use the universal
`InBlockCode` extension method which did not support syntax highlighting
until this PR

This PR additionally changes CODEOWNERS to be more reliable. Previously,
it would be possible for some PRs to be unable to be approved because
the only person who can approve them is the same person who opened the
PR.

---------

Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
Octol1ttle 2023-07-09 22:36:44 +05:00 committed by GitHub
parent 2dd9f023ef
commit 3eb17b96c5
Signed by: GitHub
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 180 additions and 179 deletions

View file

@ -0,0 +1,90 @@
using System.Globalization;
using Remora.Discord.API.Abstractions.Objects;
namespace Boyfriend.Data;
/// <summary>
/// Stores per-guild settings that can be set by a member
/// with <see cref="DiscordPermission.ManageGuild" /> using the /settings command
/// </summary>
public class GuildConfiguration {
/// <summary>
/// Represents a scheduled event notification receiver.
/// </summary>
/// <remarks>
/// Used to selectively mention guild members when a scheduled event has started or is about to start.
/// </remarks>
public enum NotificationReceiver {
Interested,
Role
}
public static readonly Dictionary<string, CultureInfo> CultureInfoCache = new() {
{ "en", new CultureInfo("en-US") },
{ "ru", new CultureInfo("ru-RU") },
{ "mctaylors-ru", new CultureInfo("tt-RU") }
};
public string Language { get; set; } = "en";
/// <summary>
/// Controls what message should be sent in <see cref="PublicFeedbackChannel" /> when a new member joins the server.
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>No message will be sent if set to "off", "disable" or "disabled".</item>
/// <item><see cref="Messages.DefaultWelcomeMessage" /> will be sent if set to "default" or "reset"</item>
/// </list>
/// </remarks>
/// <seealso cref="GuildMemberAddResponder" />
public string WelcomeMessage { get; set; } = "default";
/// <summary>
/// Controls whether or not the <see cref="Messages.Ready" /> message should be sent
/// in <see cref="PrivateFeedbackChannel" /> on startup.
/// </summary>
/// <seealso cref="GuildCreateResponder" />
public bool ReceiveStartupMessages { get; set; }
public bool RemoveRolesOnMute { get; set; }
/// <summary>
/// Controls whether or not a guild member's roles are returned if he/she leaves and then joins back.
/// </summary>
/// <remarks>Roles will not be returned if the member left the guild because of /ban or /kick.</remarks>
public bool ReturnRolesOnRejoin { get; set; }
public bool AutoStartEvents { get; set; }
/// <summary>
/// Controls what channel should all public messages be sent to.
/// </summary>
public ulong PublicFeedbackChannel { get; set; }
/// <summary>
/// Controls what channel should all private, moderator-only messages be sent to.
/// </summary>
public ulong PrivateFeedbackChannel { get; set; }
public ulong EventNotificationChannel { get; set; }
public ulong DefaultRole { get; set; }
public ulong MuteRole { get; set; }
public ulong EventNotificationRole { get; set; }
/// <summary>
/// Controls what guild members should be mentioned when a scheduled event has started or is about to start.
/// </summary>
/// <seealso cref="NotificationReceiver" />
public List<NotificationReceiver> EventStartedReceivers { get; set; }
= new() { NotificationReceiver.Interested, NotificationReceiver.Role };
/// <summary>
/// Controls the amount of time before a scheduled event to send a reminder in <see cref="EventNotificationChannel" />.
/// </summary>
public TimeSpan EventEarlyNotificationOffset { get; set; } = TimeSpan.Zero;
// Do not convert this to a property, else serialization will be attempted
public CultureInfo GetCulture() {
return CultureInfoCache[Language];
}
}

41
src/Data/GuildData.cs Normal file
View file

@ -0,0 +1,41 @@
using System.Globalization;
using Remora.Rest.Core;
namespace Boyfriend.Data;
/// <summary>
/// Stores information about a guild. This information is not accessible via the Discord API.
/// </summary>
/// <remarks>This information is stored on disk as a JSON file.</remarks>
public class GuildData {
public readonly GuildConfiguration Configuration;
public readonly string ConfigurationPath;
public readonly Dictionary<ulong, MemberData> MemberData;
public readonly string MemberDataPath;
public readonly Dictionary<ulong, ScheduledEventData> ScheduledEvents;
public readonly string ScheduledEventsPath;
public GuildData(
GuildConfiguration configuration, string configurationPath,
Dictionary<ulong, ScheduledEventData> scheduledEvents, string scheduledEventsPath,
Dictionary<ulong, MemberData> memberData, string memberDataPath) {
Configuration = configuration;
ConfigurationPath = configurationPath;
ScheduledEvents = scheduledEvents;
ScheduledEventsPath = scheduledEventsPath;
MemberData = memberData;
MemberDataPath = memberDataPath;
}
public CultureInfo Culture => Configuration.GetCulture();
public MemberData GetMemberData(Snowflake userId) {
if (MemberData.TryGetValue(userId.Value, out var existing)) return existing;
var newData = new MemberData(userId.Value, null);
MemberData.Add(userId.Value, newData);
return newData;
}
}

18
src/Data/MemberData.cs Normal file
View file

@ -0,0 +1,18 @@
using Remora.Rest.Core;
namespace Boyfriend.Data;
/// <summary>
/// Stores information about a member
/// </summary>
public class MemberData {
public MemberData(ulong id, DateTimeOffset? bannedUntil) {
Id = id;
BannedUntil = bannedUntil;
}
public ulong Id { get; }
public DateTimeOffset? BannedUntil { get; set; }
public List<Snowflake> Roles { get; set; } = new();
public List<Reminder> Reminders { get; } = new();
}

9
src/Data/Reminder.cs Normal file
View file

@ -0,0 +1,9 @@
using Remora.Rest.Core;
namespace Boyfriend.Data;
public struct Reminder {
public DateTimeOffset RemindAt;
public string Text;
public Snowflake Channel;
}

View file

@ -0,0 +1,17 @@
using Remora.Discord.API.Abstractions.Objects;
namespace Boyfriend.Data;
/// <summary>
/// Stores information about scheduled events. This information is not provided by the Discord API.
/// </summary>
/// <remarks>This information is stored on disk as a JSON file.</remarks>
public class ScheduledEventData {
public ScheduledEventData(GuildScheduledEventStatus status) {
Status = status;
}
public bool EarlyNotificationSent { get; set; }
public DateTimeOffset? ActualStartTime { get; set; }
public GuildScheduledEventStatus Status { get; set; }
}