diff --git a/nyxx.commander/lib/src/CommandContext.dart b/nyxx.commander/lib/src/CommandContext.dart index f7f991caf..9954c364f 100644 --- a/nyxx.commander/lib/src/CommandContext.dart +++ b/nyxx.commander/lib/src/CommandContext.dart @@ -50,9 +50,10 @@ class CommandContext { List? files, bool? tts, AllowedMentions? allowedMentions, - MessageBuilder? builder + MessageBuilder? builder, + ReplyBuilder? replyBuilder }) => channel.sendMessage( - content: content, embed: embed, tts: tts, files: files, builder: builder, allowedMentions: allowedMentions); + content: content, embed: embed, tts: tts, files: files, builder: builder, allowedMentions: allowedMentions, replyBuilder: replyBuilder); /// Reply to messages, then delete it when [duration] expires. /// @@ -61,15 +62,16 @@ class CommandContext { /// await context.replyTemp(content: user.avatarURL()); /// } /// ``` - Future replyTemp(Duration duration, - {dynamic content, - EmbedBuilder? embed, - List? files, - bool? tts, - AllowedMentions? allowedMentions, - MessageBuilder? builder + Future replyTemp(Duration duration, { + dynamic content, + EmbedBuilder? embed, + List? files, + bool? tts, + AllowedMentions? allowedMentions, + MessageBuilder? builder, + ReplyBuilder? replyBuilder }) => channel - .sendMessage(content: content, embed: embed, files: files, tts: tts, builder: builder, allowedMentions: allowedMentions) + .sendMessage(content: content, embed: embed, files: files, tts: tts, builder: builder, allowedMentions: allowedMentions, replyBuilder: replyBuilder) .then((msg) { Timer(duration, () => msg.delete()); return msg; @@ -87,7 +89,8 @@ class CommandContext { List? files, bool? tts, AllowedMentions? allowedMentions, - MessageBuilder? builder + MessageBuilder? builder, + ReplyBuilder? replyBuilder }) => Future.delayed( duration, () => channel.sendMessage( @@ -96,7 +99,8 @@ class CommandContext { files: files, tts: tts, builder: builder, - allowedMentions: allowedMentions)); + allowedMentions: allowedMentions, + replyBuilder: replyBuilder)); /// Awaits for emoji under given [msg] Future awaitEmoji(Message msg) async => diff --git a/nyxx/lib/nyxx.dart b/nyxx/lib/nyxx.dart index c69692f36..eab38a875 100644 --- a/nyxx/lib/nyxx.dart +++ b/nyxx/lib/nyxx.dart @@ -78,6 +78,7 @@ part "src/events/InviteEvents.dart"; part "src/utils/builders/Builder.dart"; +part "src/utils/builders/ReplyBuilder.dart"; part "src/utils/builders/PresenceBuilder.dart"; part "src/utils/builders/AttachmentBuilder.dart"; part "src/utils/builders/PermissionsBuilder.dart"; @@ -148,6 +149,7 @@ part "src/core/message/Attachment.dart"; part "src/core/message/MessageFlags.dart"; part "src/core/message/MessageReference.dart"; part "src/core/message/MessageType.dart"; +part "src/core/message/ReferencedMessage.dart"; part "src/core/application/ClientOAuth2Application.dart"; part "src/core/application/OAuth2Application.dart"; diff --git a/nyxx/lib/src/core/channel/DMChannel.dart b/nyxx/lib/src/core/channel/DMChannel.dart index f77f68e83..5d58c669c 100644 --- a/nyxx/lib/src/core/channel/DMChannel.dart +++ b/nyxx/lib/src/core/channel/DMChannel.dart @@ -61,7 +61,7 @@ class DMChannel extends IChannel implements TextChannel { Future getMessage(Snowflake id) => Future.value(this.messageCache[id]); @override - Future sendMessage({dynamic content, EmbedBuilder? embed, List? files, bool? tts, AllowedMentions? allowedMentions, MessageBuilder? builder}) => - client._httpEndpoints.sendMessage(this.id, content: content, embed: embed, files: files, tts: tts, allowedMentions: allowedMentions, builder: builder); + Future sendMessage({dynamic content, EmbedBuilder? embed, List? files, bool? tts, AllowedMentions? allowedMentions, MessageBuilder? builder, ReplyBuilder? replyBuilder}) => + client._httpEndpoints.sendMessage(this.id, content: content, embed: embed, files: files, tts: tts, allowedMentions: allowedMentions, builder: builder, replyBuilder: replyBuilder); -} \ No newline at end of file +} diff --git a/nyxx/lib/src/core/channel/TextChannel.dart b/nyxx/lib/src/core/channel/TextChannel.dart index 4bd137e46..4daa40120 100644 --- a/nyxx/lib/src/core/channel/TextChannel.dart +++ b/nyxx/lib/src/core/channel/TextChannel.dart @@ -53,7 +53,8 @@ abstract class TextChannel implements IChannel, ISend { List? files, bool? tts, AllowedMentions? allowedMentions, - MessageBuilder? builder + MessageBuilder? builder, + ReplyBuilder? replyBuilder }); /// Bulk removes many messages by its ids. [messages] is list of messages ids to delete. @@ -83,4 +84,4 @@ abstract class TextChannel implements IChannel, ISend { /// Stops a typing loop if one is running. @override void stopTypingLoop(); -} \ No newline at end of file +} diff --git a/nyxx/lib/src/core/channel/guild/TextGuildChannel.dart b/nyxx/lib/src/core/channel/guild/TextGuildChannel.dart index d0b70925d..29562861e 100644 --- a/nyxx/lib/src/core/channel/guild/TextGuildChannel.dart +++ b/nyxx/lib/src/core/channel/guild/TextGuildChannel.dart @@ -82,6 +82,6 @@ class TextGuildChannel extends GuildChannel implements TextChannel { Future getMessage(Snowflake id) => Future.value(this.messageCache[id]); @override - Future sendMessage({dynamic content, EmbedBuilder? embed, List? files, bool? tts, AllowedMentions? allowedMentions, MessageBuilder? builder}) => - client._httpEndpoints.sendMessage(this.id, content: content, embed: embed, files: files, tts: tts, allowedMentions: allowedMentions, builder: builder); -} \ No newline at end of file + Future sendMessage({dynamic content, EmbedBuilder? embed, List? files, bool? tts, AllowedMentions? allowedMentions, MessageBuilder? builder, ReplyBuilder? replyBuilder}) => + client._httpEndpoints.sendMessage(this.id, content: content, embed: embed, files: files, tts: tts, allowedMentions: allowedMentions, builder: builder, replyBuilder: replyBuilder); +} diff --git a/nyxx/lib/src/core/message/ReferencedMessage.dart b/nyxx/lib/src/core/message/ReferencedMessage.dart new file mode 100644 index 000000000..a91e4eeee --- /dev/null +++ b/nyxx/lib/src/core/message/ReferencedMessage.dart @@ -0,0 +1,35 @@ +part of nyxx; + +/// Message wrapper that other message replies to. +/// [message] field can be null of two reasons: backend error or message was deleted. +/// In first case [isBackendFetchError] will be true and [isDeleted] in second case. +class ReferencedMessage { + /// Message object of reply + late final Message? message; + + /// If true the backend couldn't fetch the message + late final bool isBackendFetchError; + + /// If true message was delted + late final bool isDeleted; + + ReferencedMessage._new(Nyxx client, Map raw) { + if (!raw.containsKey(raw["referencedMessage"])) { + this.message = null; + this.isBackendFetchError = true; + this.isDeleted = false; + return; + } + + if (raw["referencedMessage"] == null) { + this.message = null; + this.isBackendFetchError = false; + this.isDeleted = true; + return; + } + + this.message = Message._deserialize(client, raw); + this.isBackendFetchError = false; + this.isDeleted = false; + } +} diff --git a/nyxx/lib/src/core/user/User.dart b/nyxx/lib/src/core/user/User.dart index d41e60c97..f4431bda6 100644 --- a/nyxx/lib/src/core/user/User.dart +++ b/nyxx/lib/src/core/user/User.dart @@ -73,15 +73,20 @@ class User extends SnowflakeEntity with Mentionable, IMessageAuthor implements I client._httpEndpoints.userAvatarURL(this.id, this.avatar, this.discriminator, format: format, size: size); /// Sends a message to user. + @override Future sendMessage( {dynamic content, List? files, EmbedBuilder? embed, bool? tts, AllowedMentions? allowedMentions, - MessageBuilder? builder}) async { + MessageBuilder? builder, + ReplyBuilder? replyBuilder + }) async { final channel = await this.dmChannel; return channel.sendMessage( - content: content, files: files, embed: embed, tts: tts, allowedMentions: allowedMentions, builder: builder); + content: content, files: files, embed: embed, tts: tts, + allowedMentions: allowedMentions, builder: builder, replyBuilder: replyBuilder + ); } -} \ No newline at end of file +} diff --git a/nyxx/lib/src/internal/_HttpEndpoints.dart b/nyxx/lib/src/internal/_HttpEndpoints.dart index b705cbc9d..9edcbc885 100644 --- a/nyxx/lib/src/internal/_HttpEndpoints.dart +++ b/nyxx/lib/src/internal/_HttpEndpoints.dart @@ -122,7 +122,9 @@ abstract class IHttpEndpoints { EmbedBuilder? embed, bool? tts, AllowedMentions? allowedMentions, - MessageBuilder? builder}); + MessageBuilder? builder, + ReplyBuilder? replyBuilder + }); Future fetchMessage(Snowflake channelId, Snowflake messageId); @@ -192,7 +194,7 @@ class _HttpEndpoints implements IHttpEndpoints { this._httpClient = this._client._http; } - Map _initMessage(dynamic content, EmbedBuilder? embed, AllowedMentions? allowedMentions) { + Map _initMessage(dynamic content, EmbedBuilder? embed, AllowedMentions? allowedMentions, ReplyBuilder? replyBuilder) { if (content == null && embed == null) { throw ArgumentError("When sending message both content and embed cannot be null"); } @@ -202,7 +204,8 @@ class _HttpEndpoints implements IHttpEndpoints { return { if (content != null) "content": content.toString(), if (embed != null) "embed": embed._build(), - if (allowedMentions != null) "allowed_mentions": allowedMentions._build() + if (allowedMentions != null) "allowed_mentions": allowedMentions._build(), + if (replyBuilder != null) "message_reference": replyBuilder._build(), }; } @@ -715,17 +718,20 @@ class _HttpEndpoints implements IHttpEndpoints { EmbedBuilder? embed, bool? tts, AllowedMentions? allowedMentions, - MessageBuilder? builder}) async { + MessageBuilder? builder, + ReplyBuilder? replyBuilder + }) async { if (builder != null) { content = builder._content; files = builder.files; embed = builder.embed; tts = builder.tts ?? false; allowedMentions = builder.allowedMentions; + replyBuilder = builder.replyBuilder; } final reqBody = { - ..._initMessage(content, embed, allowedMentions), + ..._initMessage(content, embed, allowedMentions, replyBuilder), if (content != null && tts != null) "tts": tts }; diff --git a/nyxx/lib/src/utils/builders/MessageBuilder.dart b/nyxx/lib/src/utils/builders/MessageBuilder.dart index f40dec7e8..7188a0ad6 100644 --- a/nyxx/lib/src/utils/builders/MessageBuilder.dart +++ b/nyxx/lib/src/utils/builders/MessageBuilder.dart @@ -69,6 +69,9 @@ class MessageBuilder extends MessageEditBuilder { /// List of files to send with message List? files; + /// Allows to create message that replies to another message + ReplyBuilder? replyBuilder; + /// Add attachment void addAttachment(AttachmentBuilder attachment) { if (this.files == null) this.files = []; diff --git a/nyxx/lib/src/utils/builders/ReplyBuilder.dart b/nyxx/lib/src/utils/builders/ReplyBuilder.dart new file mode 100644 index 000000000..376f02b61 --- /dev/null +++ b/nyxx/lib/src/utils/builders/ReplyBuilder.dart @@ -0,0 +1,27 @@ +part of nyxx; + +/// Builder for replying to message +class ReplyBuilder implements Builder { + /// Id of message you reply to + final Snowflake messageId; + + /// Id of channel of message which you reply to + final Snowflake channelId; + + /// Constructs reply builder for given message in channel + ReplyBuilder(this.messageId, this.channelId); + + /// Constructs message reply from given message + factory ReplyBuilder.froMessage(Message message) => + ReplyBuilder(message.id, message.channel.id); + + /// Constructs message reply from cacheable of message and channel + factory ReplyBuilder.fromCacheable(Cacheable messageCacheable, Cacheable channelCacheable) => + ReplyBuilder(messageCacheable.id, channelCacheable.id); + + @override + Map _build() => { + "message_id": this.messageId.id, + "channel_id": this.channelId.id + }; +} diff --git a/nyxx/pubspec.yaml b/nyxx/pubspec.yaml index 566ce89af..d52ac8aba 100644 --- a/nyxx/pubspec.yaml +++ b/nyxx/pubspec.yaml @@ -11,7 +11,7 @@ environment: dependencies: logging: "^1.0.0-dev" - path: "^1.8.0-nullsafety.1" + path: "^1.8.0-nullsafety.2" http: "^0.13.0-nullsafety-dev" dependency_overrides: