| @@ -683,6 +683,9 @@ namespace Discord | |||
| /// <summary> | |||
| /// Downloads all users for this guild if the current list is incomplete. | |||
| /// </summary> | |||
| /// <remarks> | |||
| /// This method downloads all users found within this guild throught the Gateway and caches them. | |||
| /// </remarks> | |||
| /// <returns> | |||
| /// A task that represents the asynchronous download operation. | |||
| /// </returns> | |||
| @@ -57,6 +57,21 @@ namespace Discord | |||
| /// </returns> | |||
| Task UnpinAsync(RequestOptions options = null); | |||
| /// <summary> | |||
| /// Publishes (crossposts) this message. | |||
| /// </summary> | |||
| /// <param name="options">The options to be used when sending the request.</param> | |||
| /// <returns> | |||
| /// A task that represents the asynchronous operation for publishing this message. | |||
| /// </returns> | |||
| /// <remarks> | |||
| /// <note type="warning"> | |||
| /// This call will throw an <see cref="InvalidOperationException"/> if attempted in a non-news channel. | |||
| /// </note> | |||
| /// This method will publish (crosspost) the message. Please note, publishing (crossposting), is only available in news channels. | |||
| /// </remarks> | |||
| Task CrosspostAsync(RequestOptions options = null); | |||
| /// <summary> | |||
| /// Transforms this message's text into a human-readable form by resolving its tags. | |||
| /// </summary> | |||
| @@ -695,6 +695,15 @@ namespace Discord.API | |||
| var ids = new BucketIds(channelId: channelId); | |||
| await SendAsync("POST", () => $"channels/{channelId}/typing", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| public async Task CrosspostAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||
| { | |||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | |||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | |||
| options = RequestOptions.CreateOrClone(options); | |||
| var ids = new BucketIds(channelId: channelId); | |||
| await SendAsync("POST", () => $"channels/{channelId}/messages/{messageId}/crosspost", ids, options: options).ConfigureAwait(false); | |||
| } | |||
| //Channel Permissions | |||
| public async Task ModifyChannelPermissionsAsync(ulong channelId, ulong targetId, ModifyChannelPermissionsParams args, RequestOptions options = null) | |||
| @@ -44,8 +44,10 @@ namespace Discord.Rest | |||
| }; | |||
| return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); | |||
| } | |||
| public static Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) | |||
| => DeleteAsync(msg.Channel.Id, msg.Id, client, options); | |||
| public static async Task DeleteAsync(ulong channelId, ulong msgId, BaseDiscordClient client, | |||
| RequestOptions options) | |||
| { | |||
| @@ -115,6 +117,7 @@ namespace Discord.Rest | |||
| { | |||
| await client.ApiClient.AddPinAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); | |||
| } | |||
| public static async Task UnpinAsync(IMessage msg, BaseDiscordClient client, | |||
| RequestOptions options) | |||
| { | |||
| @@ -240,6 +243,7 @@ namespace Discord.Rest | |||
| return tags.ToImmutable(); | |||
| } | |||
| private static int? FindIndex(IReadOnlyList<ITag> tags, int index) | |||
| { | |||
| int i = 0; | |||
| @@ -253,6 +257,7 @@ namespace Discord.Rest | |||
| return null; //Overlaps tag before this | |||
| return i; | |||
| } | |||
| public static ImmutableArray<ulong> FilterTagsByKey(TagType type, ImmutableArray<ITag> tags) | |||
| { | |||
| return tags | |||
| @@ -260,6 +265,7 @@ namespace Discord.Rest | |||
| .Select(x => x.Key) | |||
| .ToImmutableArray(); | |||
| } | |||
| public static ImmutableArray<T> FilterTagsByValue<T>(TagType type, ImmutableArray<ITag> tags) | |||
| { | |||
| return tags | |||
| @@ -279,5 +285,14 @@ namespace Discord.Rest | |||
| return MessageSource.Bot; | |||
| return MessageSource.User; | |||
| } | |||
| public static Task CrosspostAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) | |||
| => CrosspostAsync(msg.Channel.Id, msg.Id, client, options); | |||
| public static async Task CrosspostAsync(ulong channelId, ulong msgId, BaseDiscordClient client, | |||
| RequestOptions options) | |||
| { | |||
| await client.ApiClient.CrosspostAsync(channelId, msgId, options).ConfigureAwait(false); | |||
| } | |||
| } | |||
| } | |||
| @@ -165,7 +165,7 @@ namespace Discord.Rest | |||
| IReadOnlyCollection<IEmbed> IMessage.Embeds => Embeds; | |||
| /// <inheritdoc /> | |||
| IReadOnlyCollection<ulong> IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); | |||
| /// <inheritdoc /> | |||
| public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _reactions.ToDictionary(x => x.Emote, x => new ReactionMetadata { ReactionCount = x.Count, IsMe = x.Me }); | |||
| @@ -148,6 +148,18 @@ namespace Discord.Rest | |||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | |||
| => MentionUtils.Resolve(this, 0, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | |||
| /// <inheritdoc /> | |||
| /// <exception cref="InvalidOperationException">This operation may only be called on a <see cref="RestNewsChannel"/> channel.</exception> | |||
| public async Task CrosspostAsync(RequestOptions options = null) | |||
| { | |||
| if (!(Channel is RestNewsChannel)) | |||
| { | |||
| throw new InvalidOperationException("Publishing (crossposting) is only valid in news channels."); | |||
| } | |||
| await MessageHelper.CrosspostAsync(this, Discord, options); | |||
| } | |||
| private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")})"; | |||
| } | |||
| } | |||
| @@ -1771,17 +1771,7 @@ namespace Discord.WebSocket | |||
| return guild; | |||
| } | |||
| internal SocketGuild RemoveGuild(ulong id) | |||
| { | |||
| var guild = State.RemoveGuild(id); | |||
| if (guild != null) | |||
| { | |||
| foreach (var _ in guild.Channels) | |||
| State.RemoveChannel(id); | |||
| foreach (var user in guild.Users) | |||
| user.GlobalUser.RemoveRef(this); | |||
| } | |||
| return guild; | |||
| } | |||
| => State.RemoveGuild(id); | |||
| /// <exception cref="InvalidOperationException">Unexpected channel type is created.</exception> | |||
| internal ISocketPrivateChannel AddPrivateChannel(API.Channel model, ClientState state) | |||
| @@ -123,7 +123,7 @@ namespace Discord.WebSocket | |||
| model.Content = text; | |||
| } | |||
| } | |||
| /// <inheritdoc /> | |||
| /// <exception cref="InvalidOperationException">Only the author of a message may modify the message.</exception> | |||
| /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> | |||
| @@ -147,7 +147,19 @@ namespace Discord.WebSocket | |||
| public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, | |||
| TagHandling roleHandling = TagHandling.Name, TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name) | |||
| => MentionUtils.Resolve(this, 0, userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); | |||
| /// <inheritdoc /> | |||
| /// <exception cref="InvalidOperationException">This operation may only be called on a <see cref="SocketNewsChannel"/> channel.</exception> | |||
| public async Task CrosspostAsync(RequestOptions options = null) | |||
| { | |||
| if (!(Channel is SocketNewsChannel)) | |||
| { | |||
| throw new InvalidOperationException("Publishing (crossposting) is only valid in news channels."); | |||
| } | |||
| await MessageHelper.CrosspostAsync(this, Discord, options); | |||
| } | |||
| private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")})"; | |||
| internal new SocketUserMessage Clone() => MemberwiseClone() as SocketUserMessage; | |||
| } | |||
| @@ -44,8 +44,11 @@ namespace Discord.WebSocket | |||
| /// <summary> | |||
| /// Gets mutual guilds shared with this user. | |||
| /// </summary> | |||
| /// <remarks> | |||
| /// This property will only include guilds in the same <see cref="DiscordSocketClient"/>. | |||
| /// </remarks> | |||
| public IReadOnlyCollection<SocketGuild> MutualGuilds | |||
| => Discord.Guilds.Where(g => g.Users.Any(u => u.Id == Id)).ToImmutableArray(); | |||
| => Discord.Guilds.Where(g => g.GetUser(Id) != null).ToImmutableArray(); | |||
| internal SocketUser(DiscordSocketClient discord, ulong id) | |||
| : base(discord, id) | |||
| @@ -33,7 +33,7 @@ namespace Discord.Webhook | |||
| : this(webhookUrl, new DiscordRestConfig()) { } | |||
| // regex pattern to match webhook urls | |||
| private static Regex WebhookUrlRegex = new Regex(@"^.*discordapp\.com\/api\/webhooks\/([\d]+)\/([a-z0-9_-]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); | |||
| private static Regex WebhookUrlRegex = new Regex(@"^.*(discord|discordapp)\.com\/api\/webhooks\/([\d]+)\/([a-z0-9_-]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); | |||
| /// <summary> Creates a new Webhook Discord client. </summary> | |||
| public DiscordWebhookClient(ulong webhookId, string webhookToken, DiscordRestConfig config) | |||
| @@ -132,13 +132,13 @@ namespace Discord.Webhook | |||
| if (match != null) | |||
| { | |||
| // ensure that the first group is a ulong, set the _webhookId | |||
| // 0th group is always the entire match, so start at index 1 | |||
| if (!(match.Groups[1].Success && ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out webhookId))) | |||
| // 0th group is always the entire match, and 1 is the domain; so start at index 2 | |||
| if (!(match.Groups[2].Success && ulong.TryParse(match.Groups[2].Value, NumberStyles.None, CultureInfo.InvariantCulture, out webhookId))) | |||
| throw ex("The webhook Id could not be parsed."); | |||
| if (!match.Groups[2].Success) | |||
| if (!match.Groups[3].Success) | |||
| throw ex("The webhook token could not be parsed."); | |||
| webhookToken = match.Groups[2].Value; | |||
| webhookToken = match.Groups[3].Value; | |||
| } | |||
| else | |||
| throw ex(); | |||