mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-25 22:17:51 +01:00
Fix Channel ID 0 on Discord Provider
- Add support for 1-many channel mappings - Add handling for MissingAccess and UnknownChannel errors - Added support for Discord threads - Processing error messages now reply
This commit is contained in:
@@ -212,15 +212,17 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
.ToList();
|
||||
}
|
||||
|
||||
var newMappings = results.Select(tuple => new ChannelMapping
|
||||
{
|
||||
IsWatchdogChannel = tuple.Item1.IsWatchdogChannel == true,
|
||||
IsUpdatesChannel = tuple.Item1.IsUpdatesChannel == true,
|
||||
IsAdminChannel = tuple.Item1.IsAdminChannel == true,
|
||||
ProviderChannelId = tuple.Item2.RealId,
|
||||
ProviderId = connectionId,
|
||||
Channel = tuple.Item2,
|
||||
});
|
||||
var newMappings = results.SelectMany(
|
||||
kvp => kvp.Value.Select(
|
||||
channelRepresentation => new ChannelMapping
|
||||
{
|
||||
IsWatchdogChannel = kvp.Key.IsWatchdogChannel == true,
|
||||
IsUpdatesChannel = kvp.Key.IsUpdatesChannel == true,
|
||||
IsAdminChannel = kvp.Key.IsAdminChannel == true,
|
||||
ProviderChannelId = channelRepresentation.RealId,
|
||||
ProviderId = connectionId,
|
||||
Channel = channelRepresentation,
|
||||
}));
|
||||
|
||||
ulong baseId;
|
||||
lock (synchronizationLock)
|
||||
@@ -594,10 +596,11 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
/// </summary>
|
||||
/// <param name="provider">The <see cref="IProvider"/> who recevied <paramref name="message"/>.</param>
|
||||
/// <param name="message">The <see cref="Message"/> to process. If <see langword="null"/>, this indicates the provider reconnected.</param>
|
||||
/// <param name="recursed">If we are called recursively after remapping the provider.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
#pragma warning disable CA1502
|
||||
async Task ProcessMessage(IProvider provider, Message message, CancellationToken cancellationToken)
|
||||
async Task ProcessMessage(IProvider provider, Message message, bool recursed, CancellationToken cancellationToken)
|
||||
#pragma warning restore CA1502
|
||||
{
|
||||
if (!provider.Connected)
|
||||
@@ -617,6 +620,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
var providerChannelId = message.User.Channel.RealId;
|
||||
KeyValuePair<ulong, ChannelMapping>? mappedChannel;
|
||||
long providerId;
|
||||
bool hasChannelZero;
|
||||
lock (providers)
|
||||
{
|
||||
// important, otherwise we could end up processing during shutdown
|
||||
@@ -630,6 +634,18 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
.Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == providerChannelId)
|
||||
.Select(x => (KeyValuePair<ulong, ChannelMapping>?)x)
|
||||
.FirstOrDefault();
|
||||
hasChannelZero = mappedChannels
|
||||
.Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == 0)
|
||||
.Any();
|
||||
}
|
||||
|
||||
if (!recursed && !mappedChannel.HasValue && hasChannelZero)
|
||||
{
|
||||
logger.LogInformation("Receieved message from unmapped channel whose provider contains ID 0. Remapping...");
|
||||
await RemapProvider(provider, cancellationToken);
|
||||
logger.LogTrace("Resume processing original message...");
|
||||
await ProcessMessage(provider, message, true, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.User.Channel.IsPrivateChannel)
|
||||
@@ -646,11 +662,17 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
newId);
|
||||
mappedChannels.Add(newId, new ChannelMapping
|
||||
{
|
||||
IsWatchdogChannel = false,
|
||||
ProviderChannelId = message.User.Channel.RealId,
|
||||
ProviderId = providerId,
|
||||
Channel = message.User.Channel,
|
||||
});
|
||||
|
||||
logger.LogTrace(
|
||||
"Mapping DM {connectionName}:{userId} ({userFriendlyName}) as {newId}",
|
||||
message.User.Channel.ConnectionName,
|
||||
message.User.RealId,
|
||||
message.User.FriendlyName,
|
||||
newId);
|
||||
message.User.Channel.RealId = newId;
|
||||
}
|
||||
else
|
||||
@@ -668,7 +690,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
{
|
||||
message.User.Channel.RealId,
|
||||
},
|
||||
null,
|
||||
message,
|
||||
new MessageContent
|
||||
{
|
||||
Text = "TGS: Processing error, check logs!",
|
||||
@@ -892,7 +914,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
using (LogContext.PushProperty("ChatMessage", messageNumber))
|
||||
try
|
||||
{
|
||||
await ProcessMessage(completedMessageTaskKvp.Key, message, cancellationToken);
|
||||
await ProcessMessage(completedMessageTaskKvp.Key, message, false, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -13,10 +13,12 @@ using Remora.Discord.API.Abstractions.Gateway.Commands;
|
||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.API.Abstractions.Results;
|
||||
using Remora.Discord.API.Objects;
|
||||
using Remora.Discord.Gateway;
|
||||
using Remora.Discord.Gateway.Extensions;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Rest.Results;
|
||||
using Remora.Results;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -48,6 +50,17 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ChannelType"/>s supported by the <see cref="DiscordProvider"/> for mapping.
|
||||
/// </summary>
|
||||
static readonly ChannelType[] SupportedGuildChannelTypes = new[]
|
||||
{
|
||||
ChannelType.GuildText,
|
||||
ChannelType.GuildAnnouncement,
|
||||
ChannelType.PrivateThread,
|
||||
ChannelType.PublicThread,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IAssemblyInformationProvider"/> for the <see cref="DiscordProvider"/>.
|
||||
/// </summary>
|
||||
@@ -103,11 +116,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// </summary>
|
||||
Snowflake currentUserId;
|
||||
|
||||
/// <summary>
|
||||
/// The bot's username at the time of connection.
|
||||
/// </summary>
|
||||
string initialUserName;
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="serviceProvider"/> is being disposed.
|
||||
/// </summary>
|
||||
@@ -263,37 +271,27 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
{
|
||||
if (channelId == 0)
|
||||
{
|
||||
var usersClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
|
||||
var currentGuildsResponse = await usersClient.GetCurrentUserGuildsAsync(ct: cancellationToken);
|
||||
if (!currentGuildsResponse.IsSuccess)
|
||||
{
|
||||
Logger.LogWarning(
|
||||
"Error retrieving current discord guilds: {result}",
|
||||
currentGuildsResponse.LogFormat());
|
||||
return;
|
||||
}
|
||||
|
||||
var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
|
||||
|
||||
var guildsChannelsTasks = currentGuildsResponse.Entity.Select(
|
||||
guild => guildsClient.GetGuildChannelsAsync(guild.ID.Value, cancellationToken));
|
||||
|
||||
await Task.WhenAll(guildsChannelsTasks);
|
||||
|
||||
var unmappedTextChannels = guildsChannelsTasks
|
||||
.Select(task => task.Result)
|
||||
.SelectMany(guildChannels => guildChannels.Entity)
|
||||
.Where(guildChannel => guildChannel.Type == ChannelType.GuildText);
|
||||
|
||||
IEnumerable<IChannel> unmappedTextChannels;
|
||||
var allAccessibleTextChannels = await GetAllAccessibleTextChannels(cancellationToken);
|
||||
lock (mappedChannels)
|
||||
unmappedTextChannels = unmappedTextChannels
|
||||
{
|
||||
unmappedTextChannels = allAccessibleTextChannels
|
||||
.Where(x => !mappedChannels.Contains(x.ID.Value))
|
||||
.ToList();
|
||||
|
||||
var remapRequired = unmappedTextChannels.Any()
|
||||
|| mappedChannels.Any(
|
||||
mappedChannel => !allAccessibleTextChannels.Any(
|
||||
accessibleTextChannel => accessibleTextChannel.ID == new Snowflake(mappedChannel)));
|
||||
|
||||
if (remapRequired)
|
||||
EnqueueMessage(null);
|
||||
}
|
||||
|
||||
// discord API confirmed weak boned: https://stackoverflow.com/a/52462336
|
||||
if (unmappedTextChannels.Any())
|
||||
{
|
||||
Logger.LogTrace("Dispatching to {count} unmapped channels...", unmappedTextChannels.Count());
|
||||
Logger.LogDebug("Dispatching to {count} unmapped channels...", unmappedTextChannels.Count());
|
||||
await Task.WhenAll(
|
||||
unmappedTextChannels.Select(
|
||||
x => SendToChannel(x.ID)));
|
||||
@@ -607,7 +605,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
}
|
||||
|
||||
currentUserId = currentUserResult.Entity.ID;
|
||||
initialUserName = currentUserResult.Entity.Username;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -647,89 +644,133 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task<IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(IEnumerable<Models.ChatChannel> channels, CancellationToken cancellationToken)
|
||||
protected override async Task<Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>> MapChannelsImpl(IEnumerable<Models.ChatChannel> channels, CancellationToken cancellationToken)
|
||||
{
|
||||
if (channels == null)
|
||||
throw new ArgumentNullException(nameof(channels));
|
||||
|
||||
var remapRequired = false;
|
||||
var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
|
||||
|
||||
async Task<Tuple<Models.ChatChannel, ChannelRepresentation>> GetModelChannelFromDBChannel(Models.ChatChannel channelFromDB)
|
||||
async Task<Tuple<Models.ChatChannel, IEnumerable<ChannelRepresentation>>> GetModelChannelFromDBChannel(Models.ChatChannel channelFromDB)
|
||||
{
|
||||
if (!channelFromDB.DiscordChannelId.HasValue)
|
||||
throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
|
||||
|
||||
var channelId = channelFromDB.DiscordChannelId.Value;
|
||||
string connectionName;
|
||||
string friendlyName;
|
||||
if (channelId == 0)
|
||||
var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
|
||||
var discordChannelResponse = await channelsClient.GetChannelAsync(new Snowflake(channelId), cancellationToken);
|
||||
if (!discordChannelResponse.IsSuccess)
|
||||
{
|
||||
connectionName = initialUserName;
|
||||
friendlyName = "(Unmapped accessible channels)";
|
||||
Logger.LogWarning(
|
||||
"Error retrieving discord channel {channelId}: {result}",
|
||||
channelId,
|
||||
discordChannelResponse.LogFormat());
|
||||
|
||||
remapRequired |= !(discordChannelResponse.Error is RestResultError<RestError> restResultError
|
||||
&& (restResultError.Error?.Code == DiscordError.MissingAccess
|
||||
|| restResultError.Error?.Code == DiscordError.UnknownChannel));
|
||||
return null;
|
||||
}
|
||||
else
|
||||
|
||||
var channelType = discordChannelResponse.Entity.Type;
|
||||
if (!SupportedGuildChannelTypes.Contains(channelType))
|
||||
{
|
||||
var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
|
||||
var discordChannelResponse = await channelsClient.GetChannelAsync(new Snowflake(channelId), cancellationToken);
|
||||
if (!discordChannelResponse.IsSuccess)
|
||||
{
|
||||
Logger.LogWarning(
|
||||
"Error retrieving discord channel {channelId}: {result}",
|
||||
channelId,
|
||||
discordChannelResponse.LogFormat());
|
||||
remapRequired = true;
|
||||
return null;
|
||||
}
|
||||
Logger.LogWarning("Cound not map channel {channelId}! Incorrect type: {channelType}", channelId, discordChannelResponse.Entity.Type);
|
||||
return null;
|
||||
}
|
||||
|
||||
var channelType = discordChannelResponse.Entity.Type;
|
||||
if (channelType != ChannelType.GuildText && channelType != ChannelType.GuildAnnouncement)
|
||||
{
|
||||
Logger.LogWarning("Cound not map channel {channelId}! Incorrect type: {channelType}", channelId, discordChannelResponse.Entity.Type);
|
||||
return null;
|
||||
}
|
||||
var guildId = discordChannelResponse.Entity.GuildID.Value;
|
||||
|
||||
friendlyName = discordChannelResponse.Entity.Name.Value;
|
||||
var guildId = discordChannelResponse.Entity.GuildID.Value;
|
||||
|
||||
var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
|
||||
var guildsResponse = await guildsClient.GetGuildAsync(
|
||||
var guildsResponse = await guildsClient.GetGuildAsync(
|
||||
guildId,
|
||||
false,
|
||||
cancellationToken);
|
||||
if (!guildsResponse.IsSuccess)
|
||||
{
|
||||
Logger.LogWarning(
|
||||
"Error retrieving discord guild {guildID}: {result}",
|
||||
guildId,
|
||||
false,
|
||||
cancellationToken);
|
||||
if (!guildsResponse.IsSuccess)
|
||||
{
|
||||
Logger.LogWarning(
|
||||
"Error retrieving discord guild {guildID}: {result}",
|
||||
guildId,
|
||||
guildsResponse.LogFormat());
|
||||
remapRequired = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
connectionName = guildsResponse.Entity.Name;
|
||||
guildsResponse.LogFormat());
|
||||
remapRequired |= true;
|
||||
return null;
|
||||
}
|
||||
|
||||
var connectionName = guildsResponse.Entity.Name;
|
||||
|
||||
var channelModel = new ChannelRepresentation
|
||||
{
|
||||
RealId = channelId,
|
||||
IsAdminChannel = channelFromDB.IsAdminChannel == true,
|
||||
ConnectionName = connectionName,
|
||||
FriendlyName = friendlyName,
|
||||
ConnectionName = guildsResponse.Entity.Name,
|
||||
FriendlyName = discordChannelResponse.Entity.Name.Value,
|
||||
IsPrivateChannel = false,
|
||||
Tag = channelFromDB.Tag,
|
||||
EmbedsSupported = true,
|
||||
};
|
||||
|
||||
Logger.LogTrace("Mapped channel {realId}: {friendlyName}", channelModel.RealId, channelModel.FriendlyName);
|
||||
return Tuple.Create(channelFromDB, channelModel);
|
||||
return Tuple.Create<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
|
||||
channelFromDB,
|
||||
new List<ChannelRepresentation> { channelModel });
|
||||
}
|
||||
|
||||
var tasks = channels
|
||||
.Select(x => GetModelChannelFromDBChannel(x))
|
||||
.Where(x => x.DiscordChannelId != 0)
|
||||
.Select(GetModelChannelFromDBChannel)
|
||||
.ToList();
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
var channelIdZeroModel = channels.FirstOrDefault(x => x.DiscordChannelId == 0);
|
||||
if (channelIdZeroModel != null)
|
||||
{
|
||||
Logger.LogInformation("Mapping ALL additional accessible text channels");
|
||||
var allAccessibleChannels = await GetAllAccessibleTextChannels(cancellationToken);
|
||||
var unmappedTextChannels = allAccessibleChannels
|
||||
.Where(x => !tasks.Any(task => task.Result != null && new Snowflake(task.Result.Item1.DiscordChannelId.Value) == x.ID));
|
||||
|
||||
async Task<Tuple<Models.ChatChannel, IEnumerable<ChannelRepresentation>>> CreateMappingsForUnmappedChannels()
|
||||
{
|
||||
var unmappedTasks =
|
||||
unmappedTextChannels.Select(
|
||||
async unmappedTextChannel =>
|
||||
{
|
||||
var fakeChannelModel = new Models.ChatChannel
|
||||
{
|
||||
DiscordChannelId = unmappedTextChannel.ID.Value,
|
||||
IsAdminChannel = channelIdZeroModel.IsAdminChannel,
|
||||
Tag = channelIdZeroModel.Tag,
|
||||
};
|
||||
|
||||
var tuple = await GetModelChannelFromDBChannel(fakeChannelModel);
|
||||
return tuple?.Item2.First();
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Add catch-all channel
|
||||
unmappedTasks.Add(Task.FromResult(
|
||||
new ChannelRepresentation
|
||||
{
|
||||
IsAdminChannel = channelIdZeroModel.IsAdminChannel.Value,
|
||||
ConnectionName = "(Unknown Discord Guilds)",
|
||||
EmbedsSupported = true,
|
||||
FriendlyName = "(Unknown Discord Channels)",
|
||||
RealId = 0,
|
||||
Tag = channelIdZeroModel.Tag,
|
||||
}));
|
||||
|
||||
await Task.WhenAll(unmappedTasks);
|
||||
return Tuple.Create<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
|
||||
channelIdZeroModel,
|
||||
unmappedTasks.Select(x => x.Result).Where(x => x != null).ToList());
|
||||
}
|
||||
|
||||
var task = CreateMappingsForUnmappedChannels();
|
||||
await task;
|
||||
tasks.Add(task);
|
||||
}
|
||||
|
||||
var enumerator = tasks
|
||||
.Select(x => x.Result)
|
||||
.Where(x => x != null)
|
||||
@@ -738,13 +779,76 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
lock (mappedChannels)
|
||||
{
|
||||
mappedChannels.Clear();
|
||||
mappedChannels.AddRange(enumerator.Select(x => x.Item2.RealId));
|
||||
mappedChannels.AddRange(enumerator.SelectMany(x => x.Item2).Select(x => x.RealId));
|
||||
}
|
||||
|
||||
if (remapRequired)
|
||||
{
|
||||
Logger.LogWarning("Some channels failed to load with unknown errors. We will request that these be remapped, but it may result in communication spam. Please check prior logs and report an issue if this occurs.");
|
||||
EnqueueMessage(null);
|
||||
}
|
||||
|
||||
return enumerator;
|
||||
return new Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(enumerator.Select(x => new KeyValuePair<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(x.Item1, x.Item2)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all text <see cref="IChannel"/>s accessible to and supported by the bot.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in an <see cref="IEnumerable{T}"/> of accessible and compatible <see cref="IChannel"/>s.</returns>
|
||||
async Task<IEnumerable<IChannel>> GetAllAccessibleTextChannels(CancellationToken cancellationToken)
|
||||
{
|
||||
var usersClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
|
||||
var currentGuildsResponse = await usersClient.GetCurrentUserGuildsAsync(ct: cancellationToken);
|
||||
if (!currentGuildsResponse.IsSuccess)
|
||||
{
|
||||
Logger.LogWarning(
|
||||
"Error retrieving current discord guilds: {result}",
|
||||
currentGuildsResponse.LogFormat());
|
||||
return Enumerable.Empty<IChannel>();
|
||||
}
|
||||
|
||||
var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
|
||||
|
||||
async Task<IEnumerable<IChannel>> GetGuildChannels(IPartialGuild guild)
|
||||
{
|
||||
var channelsTask = guildsClient.GetGuildChannelsAsync(guild.ID.Value, cancellationToken);
|
||||
var threads = await guildsClient.ListActiveGuildThreadsAsync(guild.ID.Value, cancellationToken);
|
||||
if (!threads.IsSuccess)
|
||||
Logger.LogWarning(
|
||||
"Error retrieving discord guild threads {guildId} ({guildName}): {result}",
|
||||
guild.ID,
|
||||
guild.Name,
|
||||
threads.LogFormat());
|
||||
|
||||
var channels = await channelsTask;
|
||||
if (!channels.IsSuccess)
|
||||
Logger.LogWarning(
|
||||
"Error retrieving discord guild channels {guildId} ({guildName}): {result}",
|
||||
guild.ID,
|
||||
guild.Name,
|
||||
channels.LogFormat());
|
||||
|
||||
if (!channels.IsSuccess && !threads.IsSuccess)
|
||||
return Enumerable.Empty<IChannel>();
|
||||
|
||||
if (channels.IsSuccess && threads.IsSuccess)
|
||||
return channels.Entity.Concat(threads.Entity.Threads ?? Enumerable.Empty<IChannel>());
|
||||
|
||||
return channels.Entity ?? threads.Entity?.Threads ?? Enumerable.Empty<IChannel>();
|
||||
}
|
||||
|
||||
var guildsChannelsTasks = currentGuildsResponse.Entity
|
||||
.Select(GetGuildChannels)
|
||||
.ToList();
|
||||
|
||||
await Task.WhenAll(guildsChannelsTasks);
|
||||
|
||||
var allAccessibleChannels = guildsChannelsTasks
|
||||
.SelectMany(task => task.Result)
|
||||
.Where(guildChannel => SupportedGuildChannelTypes.Contains(guildChannel.Type));
|
||||
|
||||
return allAccessibleChannels;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -753,7 +857,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// <param name="embed">The <see cref="ChatEmbed"/> to convert.</param>
|
||||
/// <returns>The parameter for sending a single <see cref="IEmbed"/>.</returns>
|
||||
#pragma warning disable CA1502
|
||||
private Optional<IReadOnlyList<IEmbed>> ConvertEmbed(ChatEmbed embed)
|
||||
Optional<IReadOnlyList<IEmbed>> ConvertEmbed(ChatEmbed embed)
|
||||
{
|
||||
if (embed == null)
|
||||
return default;
|
||||
|
||||
@@ -58,8 +58,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// </summary>
|
||||
/// <param name="channels">The <see cref="Api.Models.ChatChannel"/>s to map.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
|
||||
Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken);
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="Dictionary{TKey, TValue}"/> of the <see cref="ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
|
||||
Task<Dictionary<ChatChannel, IEnumerable<ChannelRepresentation>>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to the <see cref="IProvider"/>.
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
|
||||
protected override Task<Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>> MapChannelsImpl(
|
||||
IEnumerable<Models.ChatChannel> channels,
|
||||
CancellationToken cancellationToken)
|
||||
=> Task.Factory.StartNew(
|
||||
@@ -300,37 +300,40 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
else
|
||||
client.RfcJoin(channelToJoin);
|
||||
|
||||
return (IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>)channels
|
||||
.Select(dbChannel =>
|
||||
{
|
||||
var channelName = dbChannel.GetIrcChannelName();
|
||||
ulong? id = null;
|
||||
if (!channelIdMap.Any(y =>
|
||||
return new Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
|
||||
channels
|
||||
.Select(dbChannel =>
|
||||
{
|
||||
if (y.Value != channelName)
|
||||
return false;
|
||||
id = y.Key;
|
||||
return true;
|
||||
}))
|
||||
{
|
||||
id = channelIdCounter++;
|
||||
channelIdMap.Add(id.Value, channelName);
|
||||
}
|
||||
|
||||
return Tuple.Create(
|
||||
dbChannel,
|
||||
new ChannelRepresentation
|
||||
var channelName = dbChannel.GetIrcChannelName();
|
||||
ulong? id = null;
|
||||
if (!channelIdMap.Any(y =>
|
||||
{
|
||||
RealId = id.Value,
|
||||
IsAdminChannel = dbChannel.IsAdminChannel == true,
|
||||
ConnectionName = address,
|
||||
FriendlyName = channelIdMap[id.Value],
|
||||
IsPrivateChannel = false,
|
||||
Tag = dbChannel.Tag,
|
||||
EmbedsSupported = false,
|
||||
});
|
||||
})
|
||||
.ToList();
|
||||
if (y.Value != channelName)
|
||||
return false;
|
||||
id = y.Key;
|
||||
return true;
|
||||
}))
|
||||
{
|
||||
id = channelIdCounter++;
|
||||
channelIdMap.Add(id.Value, channelName);
|
||||
}
|
||||
|
||||
return new KeyValuePair<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
|
||||
dbChannel,
|
||||
new List<ChannelRepresentation>
|
||||
{
|
||||
new ChannelRepresentation
|
||||
{
|
||||
RealId = id.Value,
|
||||
IsAdminChannel = dbChannel.IsAdminChannel == true,
|
||||
ConnectionName = address,
|
||||
FriendlyName = channelIdMap[id.Value],
|
||||
IsPrivateChannel = false,
|
||||
Tag = dbChannel.Tag,
|
||||
EmbedsSupported = false,
|
||||
},
|
||||
});
|
||||
}));
|
||||
}
|
||||
},
|
||||
cancellationToken,
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
public void InitialMappingComplete() => initialConnectionTcs.TrySetResult();
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken)
|
||||
public async Task<Dictionary<ChatChannel, IEnumerable<ChannelRepresentation>>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -198,15 +198,15 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// </summary>
|
||||
/// <param name="channels">The <see cref="ChatChannel"/>s to map.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
|
||||
protected abstract Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="Dictionary{TKey, TValue}"/> of the <see cref="ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
|
||||
protected abstract Task<Dictionary<ChatChannel, IEnumerable<ChannelRepresentation>>> MapChannelsImpl(
|
||||
IEnumerable<ChatChannel> channels,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Queues a <paramref name="message"/> for <see cref="NextMessage(CancellationToken)"/>.
|
||||
/// </summary>
|
||||
/// <param name="message">The <see cref="Message"/> to queue.</param>
|
||||
/// <param name="message">The <see cref="Message"/> to queue. A value of <see langword="null"/> indicates the channel mappings a out of date.</param>
|
||||
protected void EnqueueMessage(Message message)
|
||||
{
|
||||
if (message == null)
|
||||
|
||||
Reference in New Issue
Block a user