Skip to content

Commit

Permalink
Initial poll implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Quahu committed May 17, 2024
1 parent c374066 commit 2ae05be
Show file tree
Hide file tree
Showing 37 changed files with 815 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/Disqord.Core/Discord/Limits/Rest/Discord.Limits.Rest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public static class Rest
/// Represents the page size for fetching event users.
/// </summary>
public const int FetchGuildEventUsersPageSize = 100;

/// <summary>
/// Represents the page size for fetching poll answer voters.
/// </summary>
public const int FetchPollAnswerVotersPageSize = 100;
}
}
}
5 changes: 5 additions & 0 deletions src/Disqord.Core/Entities/Core/Message/User/IUserMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,9 @@ public interface IUserMessage : IMessage
/// Gets the stickers sent with this message.
/// </summary>
IReadOnlyList<IMessageSticker> Stickers { get; }

/// <summary>
/// Gets the poll of this message.
/// </summary>
IPoll? Poll { get; }
}
40 changes: 40 additions & 0 deletions src/Disqord.Core/Entities/Core/Message/User/Poll/IPoll.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;

namespace Disqord;

/// <summary>
/// Represents a poll.
/// </summary>
public interface IPoll : IEntity
{
/// <summary>
/// Gets the question of this poll.
/// </summary>
IPollMedia Question { get; }

/// <summary>
/// Gets the answers of this poll.
/// </summary>
IReadOnlyList<IPollAnswer> Answers { get; }

/// <summary>
/// Gets the expiry of this poll.
/// </summary>
DateTimeOffset? Expiry { get; }

/// <summary>
/// Gets whether this poll allows selection of multiple answers.
/// </summary>
bool AllowMultiselect { get; }

/// <summary>
/// Gets the layout type of this poll.
/// </summary>
PollLayoutType LayoutType { get; }

/// <summary>
/// Gets the results of this poll.
/// </summary>
IPollResults? Results { get; }
}
17 changes: 17 additions & 0 deletions src/Disqord.Core/Entities/Core/Message/User/Poll/IPollAnswer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Disqord;

/// <summary>
/// Represents a poll answer.
/// </summary>
public interface IPollAnswer : IEntity
{
/// <summary>
/// Gets the ID of the poll answer. This is used to match <see cref="IPollAnswerCount"/> based on the ID.
/// </summary>
int Id { get; }

/// <summary>
/// Gets the media of this poll answer.
/// </summary>
IPollMedia Media { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Disqord;

/// <summary>
/// Represents the vote count of a poll answer.
/// </summary>
public interface IPollAnswerCount : IEntity
{
/// <summary>
/// Gets the ID of the poll answer.
/// </summary>
int AnswerId { get; }

/// <summary>
/// Gets the amount of users that selected the poll answer.
/// </summary>
int Count { get; }

/// <summary>
/// Gets whether the bot has selected the poll answer.
/// </summary>
bool HasOwnVote { get; }
}
17 changes: 17 additions & 0 deletions src/Disqord.Core/Entities/Core/Message/User/Poll/IPollMedia.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Disqord;

/// <summary>
/// Represents poll media.
/// </summary>
public interface IPollMedia : IEntity
{
/// <summary>
/// Gets the text of this poll media.
/// </summary>
string? Text { get; }

/// <summary>
/// Gets the emoji of this poll media.
/// </summary>
IEmoji? Emoji { get; }
}
19 changes: 19 additions & 0 deletions src/Disqord.Core/Entities/Core/Message/User/Poll/IPollResults.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Collections.Generic;

namespace Disqord;

/// <summary>
/// Represents the results of a poll.
/// </summary>
public interface IPollResults : IEntity
{
/// <summary>
/// Gets whether the votes have been precisely counted.
/// </summary>
bool IsFinalized { get; }

/// <summary>
/// Gets the vote counts of each answer.
/// </summary>
IReadOnlyList<IPollAnswerCount> AnswerCounts { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,11 @@ public static TMessage WithStickerIds<TMessage>(this TMessage message, params Sn
{
return message.WithStickerIds(stickerIds as IEnumerable<Snowflake>);
}

public static TMessage WithPoll<TMessage>(this TMessage message, LocalPoll poll)
where TMessage : LocalMessageBase
{
message.Poll = poll;
return message;
}
}
6 changes: 6 additions & 0 deletions src/Disqord.Core/Entities/Local/Message/LocalMessageBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public abstract class LocalMessageBase : ILocalConstruct<LocalMessageBase>
/// </summary>
public Optional<IList<Snowflake>> StickerIds { get; set; }

/// <summary>
/// Gets or sets the poll of this message.
/// </summary>
public Optional<LocalPoll> Poll { get; set; }

/// <summary>
/// Instantiates a new <see cref="LocalMessageBase"/>.
/// </summary>
Expand All @@ -71,6 +76,7 @@ protected LocalMessageBase(LocalMessageBase other)
Attachments = other.Attachments.DeepClone();
Components = other.Components.DeepClone();
StickerIds = other.StickerIds.Clone();
Poll = other.Poll.Clone();
}

public abstract LocalMessageBase Clone();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.ComponentModel;

namespace Disqord;

[EditorBrowsable(EditorBrowsableState.Never)]
public static class LocalPollAnswerExtensions
{
public static TPollAnswer WithMedia<TPollAnswer>(this TPollAnswer answer, LocalPollMedia media)
where TPollAnswer : LocalPollAnswer
{
answer.Media = media;
return answer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Collections.Generic;
using System.ComponentModel;
using Qommon;

namespace Disqord;

[EditorBrowsable(EditorBrowsableState.Never)]
public static class LocalPollExtensions
{
public static TPoll WithQuestion<TPoll>(this TPoll poll, string text)
where TPoll : LocalPoll
{
var media = new LocalPollMedia()
{
Text = text
};

return poll.WithQuestion(media);
}

public static TPoll WithQuestion<TPoll>(this TPoll poll, LocalPollMedia question)
where TPoll : LocalPoll
{
poll.Question = question;
return poll;
}

public static TPoll AddAnswer<TPoll>(this TPoll poll, string text, LocalEmoji? emoji = null)
where TPoll : LocalPoll
{
var media = new LocalPollMedia
{
Text = text,
Emoji = Optional.FromNullable(emoji)
};

var answer = new LocalPollAnswer()
{
Media = media
};

return poll.AddAnswer(answer);
}

public static TPoll AddAnswer<TPoll>(this TPoll poll, LocalPollAnswer answer)
where TPoll : LocalPoll
{
if (poll.Answers.Add(answer, out var list))
poll.Answers = new(list);

return poll;
}

public static TPoll WithAnswers<TPoll>(this TPoll poll, IEnumerable<LocalPollAnswer> answers)
where TPoll : LocalPoll
{
Guard.IsNotNull(answers);

if (poll.Answers.With(answers, out var list))
poll.Answers = new(list);

return poll;
}

public static TPoll WithAnswers<TPoll>(this TPoll poll, params LocalPollAnswer[] answers)
where TPoll : LocalPoll
{
return poll.WithAnswers(answers as IEnumerable<LocalPollAnswer>);
}

public static TPoll WithDuration<TPoll>(this TPoll poll, int duration)
where TPoll : LocalPoll
{
poll.Duration = duration;
return poll;
}

public static TPoll WithAllowMultiselect<TPoll>(this TPoll poll, bool allowMultiselect = true)
where TPoll : LocalPoll
{
poll.AllowMultiselect = allowMultiselect;
return poll;
}

public static TPoll WithLayoutType<TPoll>(this TPoll poll, PollLayoutType layoutType)
where TPoll : LocalPoll
{
poll.LayoutType = layoutType;
return poll;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.ComponentModel;

namespace Disqord;

[EditorBrowsable(EditorBrowsableState.Never)]
public static class LocalPollMediaExtensions
{
public static TPollMedia WithText<TPollMedia>(this TPollMedia media, string text)
where TPollMedia : LocalPollMedia
{
media.Text = text;
return media;
}

public static TPollMedia WithEmoji<TPollMedia>(this TPollMedia media, LocalEmoji emoji)
where TPollMedia : LocalPollMedia
{
media.Emoji = emoji;
return media;
}
}
90 changes: 90 additions & 0 deletions src/Disqord.Core/Entities/Local/Message/Poll/LocalPoll.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Collections.Generic;
using System.Linq;
using Disqord.Models;
using Qommon;

namespace Disqord;

public class LocalPoll : ILocalConstruct<LocalPoll>, IJsonConvertible<PollJsonModel>
{
/// <summary>
/// Gets or sets the question of this poll.
/// </summary>
public Optional<LocalPollMedia> Question { get; set; }

/// <summary>
/// Gets or sets the answers of this poll.
/// </summary>
public Optional<IList<LocalPollAnswer>> Answers { get; set; }

/// <summary>
/// Gets or sets the duration in hours of this poll.
/// </summary>
public Optional<int> Duration { get; set; }

/// <summary>
/// Gets or sets whether this poll allows selection of multiple answers.
/// </summary>
public Optional<bool> AllowMultiselect { get; set; }

/// <summary>
/// Gets or sets the layout type of this poll.
/// </summary>
public Optional<PollLayoutType> LayoutType { get; set; }

/// <summary>
/// Instantiates a new <see cref="LocalPoll"/>.
/// </summary>
public LocalPoll()
{ }

/// <summary>
/// Instantiates a new <see cref="LocalPoll"/> with the properties copied from another instance.
/// </summary>
/// <param name="other"> The other instance to copy properties from. </param>
protected LocalPoll(LocalPoll other)
{
Question = other.Question.Clone();
Answers = other.Answers.DeepClone();
Duration = other.Duration;
AllowMultiselect = other.AllowMultiselect;
LayoutType = other.LayoutType;
}

/// <inheritdoc/>
public virtual LocalPoll Clone()
{
return new(this);
}

/// <inheritdoc/>
public virtual PollJsonModel ToModel()
{
OptionalGuard.HasValue(Question);
OptionalGuard.HasValue(Answers);

return new PollJsonModel
{
Question = Question.Value.ToModel(),
Answers = Answers.Value.Select(answer => answer.ToModel()).ToArray(),
Duration = Duration,
AllowMultiselect = AllowMultiselect.GetValueOrDefault(),
LayoutType = LayoutType.GetValueOrDefault(PollLayoutType.Default)
};
}

/// <summary>
/// Converts the specified poll to a <see cref="LocalPoll"/>.
/// </summary>
/// <param name="poll"> The poll to convert. </param>
/// <returns>
/// The output <see cref="LocalPoll"/>.
/// </returns>
public static LocalPoll CreateFrom(IPoll poll)
{
var localPoll = new LocalPoll
{ };

return localPoll;
}
}
Loading

0 comments on commit 2ae05be

Please sign in to comment.