2020-02-12 14:16:19 +00:00
using System.Collections.Generic ;
using System.Threading.Tasks ;
using NodaTime ;
namespace PluralKit.Core {
public enum AutoproxyMode
{
Off = 1 ,
Front = 2 ,
Latch = 3 ,
Member = 4
}
public class FullMessage
{
public PKMessage Message ;
public PKMember Member ;
public PKSystem System ;
}
public struct PKMessage
{
public ulong Mid ;
public ulong? Guild ; // null value means "no data" (ie. from before this field being added)
public ulong Channel ;
public ulong Sender ;
public ulong? OriginalMid ;
}
public struct ImportedSwitch
{
public Instant Timestamp ;
public IReadOnlyCollection < PKMember > Members ;
}
public struct SwitchListEntry
{
public ICollection < PKMember > Members ;
public Instant TimespanStart ;
public Instant TimespanEnd ;
}
public struct MemberMessageCount
{
public int Member ;
public int MessageCount ;
}
public struct FrontBreakdown
{
public Dictionary < PKMember , Duration > MemberSwitchDurations ;
public Duration NoFronterDuration ;
public Instant RangeStart ;
public Instant RangeEnd ;
}
public struct SwitchMembersListEntry
{
public int Member ;
public Instant Timestamp ;
}
public struct GuildConfig
{
public ulong Id { get ; set ; }
public ulong? LogChannel { get ; set ; }
public ISet < ulong > LogBlacklist { get ; set ; }
public ISet < ulong > Blacklist { get ; set ; }
}
public class SystemGuildSettings
{
public ulong Guild { get ; set ; }
public bool ProxyEnabled { get ; set ; } = true ;
public AutoproxyMode AutoproxyMode { get ; set ; } = AutoproxyMode . Off ;
public int? AutoproxyMember { get ; set ; }
}
public class MemberGuildSettings
{
public int Member { get ; set ; }
public ulong Guild { get ; set ; }
public string DisplayName { get ; set ; }
2020-02-12 16:42:12 +00:00
public string AvatarUrl { get ; set ; }
2020-02-12 14:16:19 +00:00
}
public interface IDataStore
{
/// <summary>
/// Gets a system by its internal system ID.
/// </summary>
/// <returns>The <see cref="PKSystem"/> with the given internal ID, or null if no system was found.</returns>
Task < PKSystem > GetSystemById ( int systemId ) ;
/// <summary>
/// Gets a system by its user-facing human ID.
/// </summary>
/// <returns>The <see cref="PKSystem"/> with the given human ID, or null if no system was found.</returns>
Task < PKSystem > GetSystemByHid ( string systemHid ) ;
/// <summary>
/// Gets a system by one of its linked Discord account IDs. Multiple IDs can return the same system.
/// </summary>
/// <returns>The <see cref="PKSystem"/> with the given linked account, or null if no system was found.</returns>
Task < PKSystem > GetSystemByAccount ( ulong linkedAccount ) ;
/// <summary>
/// Gets a system by its API token.
/// </summary>
/// <returns>The <see cref="PKSystem"/> with the given API token, or null if no corresponding system was found.</returns>
Task < PKSystem > GetSystemByToken ( string apiToken ) ;
/// <summary>
/// Gets the Discord account IDs linked to a system.
/// </summary>
/// <returns>An enumerable of Discord account IDs linked to this system.</returns>
Task < IEnumerable < ulong > > GetSystemAccounts ( PKSystem system ) ;
/// <summary>
/// Gets the member count of a system.
/// </summary>
/// <param name="includePrivate">Whether the returned count should include private members.</param>
Task < int > GetSystemMemberCount ( PKSystem system , bool includePrivate ) ;
/// <summary>
/// Gets a list of members with proxy tags that conflict with the given tags.
///
/// A set of proxy tags A conflict with proxy tags B if both A's prefix and suffix
/// are a "subset" of B's. In other words, if A's prefix *starts* with B's prefix
/// and A's suffix *ends* with B's suffix, the tag pairs are considered conflicting.
/// </summary>
/// <param name="system">The system to check in.</param>
Task < IEnumerable < PKMember > > GetConflictingProxies ( PKSystem system , ProxyTag tag ) ;
/// <summary>
/// Gets a specific system's guild-specific settings for a given guild.
/// </summary>
Task < SystemGuildSettings > GetSystemGuildSettings ( PKSystem system , ulong guild ) ;
/// <summary>
/// Saves a specific system's guild-specific settings.
/// </summary>
Task SetSystemGuildSettings ( PKSystem system , ulong guild , SystemGuildSettings settings ) ;
/// <summary>
/// Creates a system, auto-generating its corresponding IDs.
/// </summary>
/// <param name="systemName">An optional system name to set. If `null`, will not set a system name.</param>
/// <returns>The created system model.</returns>
Task < PKSystem > CreateSystem ( string systemName ) ;
// TODO: throw exception if account is present (when adding) or account isn't present (when removing)
/// <summary>
/// Links a Discord account to a system.
/// </summary>
/// <exception>Throws an exception (TODO: which?) if the given account is already linked to a system.</exception>
Task AddAccount ( PKSystem system , ulong accountToAdd ) ;
/// <summary>
/// Unlinks a Discord account from a system.
///
/// Will *not* throw if this results in an orphaned system - this is the caller's responsibility to ensure.
/// </summary>
/// <exception>Throws an exception (TODO: which?) if the given account is not linked to the given system.</exception>
Task RemoveAccount ( PKSystem system , ulong accountToRemove ) ;
/// <summary>
/// Saves the information within the given <see cref="PKSystem"/> struct to the data store.
/// </summary>
Task SaveSystem ( PKSystem system ) ;
/// <summary>
/// Deletes the given system from the database.
/// </summary>
/// <para>
/// This will also delete all the system's members, all system switches, and every message that has been proxied
/// by members in the system.
/// </para>
Task DeleteSystem ( PKSystem system ) ;
/// <summary>
/// Gets a system by its internal member ID.
/// </summary>
/// <returns>The <see cref="PKMember"/> with the given internal ID, or null if no member was found.</returns>
Task < PKMember > GetMemberById ( int memberId ) ;
/// <summary>
/// Gets a member by its user-facing human ID.
/// </summary>
/// <returns>The <see cref="PKMember"/> with the given human ID, or null if no member was found.</returns>
Task < PKMember > GetMemberByHid ( string memberHid ) ;
/// <summary>
/// Gets a member by its member name within one system.
/// </summary>
/// <para>
/// Member names are *usually* unique within a system (but not always), whereas member names
/// are almost certainly *not* unique globally - therefore only intra-system lookup is
/// allowed.
/// </para>
/// <returns>The <see cref="PKMember"/> with the given name, or null if no member was found.</returns>
Task < PKMember > GetMemberByName ( PKSystem system , string name ) ;
/// <summary>
/// Gets all members inside a given system.
/// </summary>
/// <returns>An enumerable of <see cref="PKMember"/> structs representing each member in the system, in no particular order.</returns>
IAsyncEnumerable < PKMember > GetSystemMembers ( PKSystem system , bool orderByName = false ) ;
/// <summary>
/// Gets the amount of messages proxied by a given member.
/// </summary>
/// <returns>The message count of the given member.</returns>
Task < ulong > GetMemberMessageCount ( PKMember member ) ;
/// <summary>
/// Collects a breakdown of each member in a system's message count.
/// </summary>
/// <returns>An enumerable of members along with their message counts.</returns>
Task < IEnumerable < MemberMessageCount > > GetMemberMessageCountBulk ( PKSystem system ) ;
/// <summary>
/// Creates a member, auto-generating its corresponding IDs.
/// </summary>
/// <param name="system">The system in which to create the member.</param>
/// <param name="name">The name of the member to create.</param>
/// <returns>The created system model.</returns>
Task < PKMember > CreateMember ( PKSystem system , string name ) ;
/// <summary>
/// Creates multiple members, auto-generating each corresponding ID.
/// </summary>
/// <param name="system">The system to create the member in.</param>
/// <param name="memberNames">A dictionary containing a mapping from an arbitrary key to the member's name.</param>
/// <returns>A dictionary containing the resulting member structs, each mapped to the key given in the argument dictionary.</returns>
Task < Dictionary < string , PKMember > > CreateMembersBulk ( PKSystem system , Dictionary < string , string > memberNames ) ;
/// <summary>
/// Saves the information within the given <see cref="PKMember"/> struct to the data store.
/// </summary>
Task SaveMember ( PKMember member ) ;
/// <summary>
/// Deletes the given member from the database.
/// </summary>
/// <para>
/// This will remove this member from any switches it's involved in, as well as all the messages
/// proxied by this member.
/// </para>
Task DeleteMember ( PKMember member ) ;
/// <summary>
/// Gets a specific member's guild-specific settings for a given guild.
/// </summary>
Task < MemberGuildSettings > GetMemberGuildSettings ( PKMember member , ulong guild ) ;
/// <summary>
/// Saves a specific member's guild-specific settings.
/// </summary>
Task SetMemberGuildSettings ( PKMember member , ulong guild , MemberGuildSettings settings ) ;
/// <summary>
/// Gets a message and its information by its ID.
/// </summary>
/// <param name="id">The message ID to look up. This can be either the ID of the trigger message containing the proxy tags or the resulting proxied webhook message.</param>
/// <returns>An extended message object, containing not only the message data itself but the associated system and member structs.</returns>
Task < FullMessage > GetMessage ( ulong id ) ; // id is both original and trigger, also add return type struct
/// <summary>
/// Saves a posted message to the database.
/// </summary>
/// <param name="senderAccount">The ID of the account that sent the original trigger message.</param>
/// <param name="guildId">The ID of the guild the message was posted to.</param>
/// <param name="channelId">The ID of the channel the message was posted to.</param>
/// <param name="postedMessageId">The ID of the message posted by the webhook.</param>
/// <param name="triggerMessageId">The ID of the original trigger message containing the proxy tags.</param>
/// <param name="proxiedMember">The member (and by extension system) that was proxied.</param>
/// <returns></returns>
Task AddMessage ( ulong senderAccount , ulong guildId , ulong channelId , ulong postedMessageId , ulong triggerMessageId , PKMember proxiedMember ) ;
/// <summary>
/// Deletes a message from the data store.
/// </summary>
/// <param name="postedMessageId">The ID of the webhook message to delete.</param>
Task DeleteMessage ( ulong postedMessageId ) ;
/// <summary>
/// Deletes messages from the data store in bulk.
/// </summary>
/// <param name="postedMessageIds">The IDs of the webhook messages to delete.</param>
Task DeleteMessagesBulk ( IEnumerable < ulong > postedMessageIds ) ;
/// <summary>
/// Gets the most recent message sent by a given account in a given guild.
/// </summary>
/// <returns>The full message object, or null if none was found.</returns>
Task < FullMessage > GetLastMessageInGuild ( ulong account , ulong guild ) ;
/// <summary>
/// Gets switches from a system.
/// </summary>
/// <returns>An enumerable of the *count* latest switches in the system, in latest-first order. May contain fewer elements than requested.</returns>
IAsyncEnumerable < PKSwitch > GetSwitches ( PKSystem system ) ;
/// <summary>
/// Gets the total amount of switches in a given system.
/// </summary>
Task < int > GetSwitchCount ( PKSystem system ) ;
/// <summary>
/// Gets the latest (temporally; closest to now) switch of a given system.
/// </summary>
Task < PKSwitch > GetLatestSwitch ( PKSystem system ) ;
/// <summary>
/// Gets the members a given switch consists of.
/// </summary>
IAsyncEnumerable < PKMember > GetSwitchMembers ( PKSwitch sw ) ;
/// <summary>
/// Gets a list of fronters over a given period of time.
/// </summary>
/// <para>
/// This list is returned as an enumerable of "switch members", each containing a timestamp
/// and a member ID. <seealso cref="GetMemberById"/>
///
/// Switches containing multiple members will be returned as multiple switch members each with the same
/// timestamp, and a change in timestamp should be interpreted as the start of a new switch.
/// </para>
/// <returns>An enumerable of the aforementioned "switch members".</returns>
Task < IEnumerable < SwitchListEntry > > GetPeriodFronters ( PKSystem system , Instant periodStart , Instant periodEnd ) ;
/// <summary>
/// Calculates a breakdown of a system's fronters over a given period, including how long each member has
/// been fronting, and how long *no* member has been fronting.
/// </summary>
/// <para>
/// Switches containing multiple members will count the full switch duration for all members, meaning
/// the total duration may add up to longer than the breakdown period.
/// </para>
/// <param name="system"></param>
/// <param name="periodStart"></param>
/// <param name="periodEnd"></param>
/// <returns></returns>
Task < FrontBreakdown > GetFrontBreakdown ( PKSystem system , Instant periodStart , Instant periodEnd ) ;
/// <summary>
/// Gets the first listed fronter in a system.
/// </summary>
/// <returns>The first fronter, or null if none are registered.</returns>
Task < PKMember > GetFirstFronter ( PKSystem system ) ;
/// <summary>
/// Registers a switch with the given members in the given system.
/// </summary>
/// <exception>Throws an exception (TODO: which?) if any of the members are not in the given system.</exception>
Task AddSwitch ( PKSystem system , IEnumerable < PKMember > switchMembers ) ;
/// <summary>
/// Registers switches in bulk.
/// </summary>
/// <param name="switches">A list of switch structs, each containing a timestamp and a list of members.</param>
/// <exception>Throws an exception (TODO: which?) if any of the given members are not in the given system.</exception>
Task AddSwitchesBulk ( PKSystem system , IEnumerable < ImportedSwitch > switches ) ;
/// <summary>
/// Updates the timestamp of a given switch.
/// </summary>
Task MoveSwitch ( PKSwitch sw , Instant time ) ;
/// <summary>
/// Deletes a given switch from the data store.
/// </summary>
Task DeleteSwitch ( PKSwitch sw ) ;
/// <summary>
/// Deletes all switches in a given system from the data store.
/// </summary>
Task DeleteAllSwitches ( PKSystem system ) ;
/// <summary>
/// Gets the total amount of systems in the data store.
/// </summary>
Task < ulong > GetTotalSystems ( ) ;
/// <summary>
/// Gets the total amount of members in the data store.
/// </summary>
Task < ulong > GetTotalMembers ( ) ;
/// <summary>
/// Gets the total amount of switches in the data store.
/// </summary>
Task < ulong > GetTotalSwitches ( ) ;
/// <summary>
/// Gets the total amount of messages in the data store.
/// </summary>
Task < ulong > GetTotalMessages ( ) ;
/// <summary>
/// Gets the guild configuration struct for a given guild, creating and saving one if none was found.
/// </summary>
/// <returns>The guild's configuration struct.</returns>
Task < GuildConfig > GetOrCreateGuildConfig ( ulong guild ) ;
/// <summary>
/// Saves the given guild configuration struct to the data store.
/// </summary>
Task SaveGuildConfig ( GuildConfig cfg ) ;
}
}