コード例 #1
0
ファイル: DiscordLinkUtility.cs プロジェクト: ItsKaa/Ditto
        public async Task Add(
            [Help("channel", "The guild channel of this server.")] ITextChannel textChannel,
            [Help("linkChannelId", "The ID of the channel from the other server that you wish to link to the selected 'channel'.", "example: 334120131626621412")] ulong linkChannelId,
            [Help("date", "The optional date-time for the first post to synchronise.", "example: \"2020-01-01 10:00 AM\"")] DateTime?fromDate = null)
        {
            if (!Permissions.IsBotOwner(Context))
            {
                await Context.ApplyResultReaction(CommandResult.FailedUserPermission).ConfigureAwait(false);

                return;
            }

            var linkChannel = _discordClient.GetChannel(linkChannelId) as ITextChannel;

            if (textChannel == null || linkChannel == null)
            {
                await Context.ApplyResultReaction(CommandResult.FailedBotPermission).ConfigureAwait(false);

                return;
            }

            var link = await LinkUtility.TryAddLinkAsync(LinkType.Discord, textChannel, linkChannel.Id.ToString(), fromDate).ConfigureAwait(false);

            Log.Debug($"Added link {link.Id}: {linkChannelId} -> {textChannel.Id}");
            await Context.ApplyResultReaction(link == null?CommandResult.Failed : CommandResult.Success).ConfigureAwait(false);
        }
コード例 #2
0
ファイル: DiscordLinkUtility.cs プロジェクト: ItsKaa/Ditto
        static DiscordLinkUtility()
        {
            LastUpdate    = new ConcurrentDictionary <int, DateTime>();
            _reconnecting = false;
            _initialLogin = true;

            _discordClient = new DiscordClientEx(new DiscordSocketConfig()
            {
                MessageCacheSize  = Ditto.Settings.Cache.AmountOfCachedMessages,
                LogLevel          = LogSeverity.Warning,
                ConnectionTimeout = (int)(Ditto.Settings.Timeout * 60),
                HandlerTimeout    = (int)(Ditto.Settings.Timeout * 60),
                DefaultRetryMode  = RetryMode.AlwaysRetry,
            });

            var loginAction = new Func <Task>(async() =>
            {
                // Cancel out when already reconnecting
                if (_reconnecting)
                {
                    return;
                }

                if (!_initialLogin)
                {
                    Log.Info("Reconnecting discord slave user...");
                }

                // Attempt to continuously reconnect.
                _reconnecting    = true;
                _connected       = false;
                int retryAttempt = 0;
                while (true)
                {
                    try
                    {
                        await _discordClient.LoginAsync(0, Ditto.Settings.Credentials.UserSlaveToken, true);
                        await _discordClient.StartAsync().ConfigureAwait(false);
                        _reconnecting = false;
                        break;
                    }
                    catch
                    {
                        if (_initialLogin)
                        {
                            _initialLogin = false;
                            Log.Error("Slave user could not connect at first login, will not retry further.");
                            break;
                        }
                        else
                        {
                            _initialLogin = false;
                            Log.Warn($"Slave user could not connect ({++retryAttempt})");
                            await Task.Delay(500).ConfigureAwait(false);
                        }
                    }
                }
            });


            _discordClient.Connected += () =>
            {
                _connected = true;
                Log.Info("Slave user connected!");
                return(Task.CompletedTask);
            };

            _discordClient.Disconnected += (ex) =>
            {
                if (!_initialLogin)
                {
                    Log.Warn($"Slave user has been disconnected.");
                    Task.Run(() => loginAction());
                }
                else
                {
                    _initialLogin = false;
                    _reconnecting = false;
                    Log.Error("Slave user could not connect at first login, will not retry further.");
                    return(_discordClient.StopAsync());
                }

                return(Task.CompletedTask);
            };

            _discordClient.LoggedOut += () =>
            {
                if (!_initialLogin)
                {
                    Log.Warn($"Slave user got logged out.");
                    Task.Run(() => loginAction());
                }
                return(Task.CompletedTask);
            };

            Task.Run(async() =>
            {
                await loginAction().ConfigureAwait(false);
            });

            LinkUtility.TryAddHandler(LinkType.Discord, async(link, channel, cancellationToken) =>
            {
                var messageIds = new List <string>();

                // Only pull discord channel feeds every 2 minutes for each individual channel.
                var lastUpdateTime = LastUpdate.GetOrAdd(link.Id, DateTime.MinValue);
                if (!_connected || (DateTime.UtcNow - lastUpdateTime).TotalSeconds < 120)
                {
                    return(messageIds);
                }

                if (link != null)
                {
                    if (ulong.TryParse(link.Value, out ulong linkChannelId))
                    {
                        if (_discordClient.GetChannel(linkChannelId) is ITextChannel linkChannel)
                        {
                            // Retrieve the latest messages in bulk from the targeted channel.
                            var messages        = new List <IMessage>();
                            ulong lastMessageId = ulong.MaxValue;
                            while (true)
                            {
                                if (cancellationToken.IsCancellationRequested)
                                {
                                    return(messageIds);
                                }

                                var messagesChunk = Enumerable.Empty <IMessage>();
                                if (lastMessageId != ulong.MaxValue)
                                {
                                    messagesChunk = (await linkChannel.GetMessagesAsync(lastMessageId, Direction.Before, 100, CacheMode.AllowDownload).ToListAsync().ConfigureAwait(false))
                                                    .SelectMany(m => m)
                                                    .Where(m => m.CreatedAt.UtcDateTime > link.Date)
                                                    .Where(m => null == link.Links.FirstOrDefault(l => l.Identity == m.Id.ToString()));
                                }
                                else
                                {
                                    messagesChunk = (await linkChannel.GetMessagesAsync(100, CacheMode.AllowDownload).ToListAsync().ConfigureAwait(false))
                                                    .SelectMany(m => m)
                                                    .Where(m => m.CreatedAt.UtcDateTime > link.Date)
                                                    .Where(m => null == link.Links.FirstOrDefault(l => l.Identity == m.Id.ToString()));
                                }
                                messages.AddRange(messagesChunk);
                                lastMessageId = messagesChunk.LastOrDefault()?.Id ?? ulong.MaxValue;

                                // Cancel when message count is less than the maximum.
                                if (messagesChunk.Count() < 100)
                                {
                                    break;
                                }
                            }

                            // Update link date-time.
                            var lastMessageDate    = DateTime.MinValue;
                            var funcUpdateLinkDate = new Func <Task>(async() =>
                            {
                                if (lastMessageDate > link.Date)
                                {
                                    link.Date = lastMessageDate;
                                    await Ditto.Database.WriteAsync(uow =>
                                    {
                                        uow.Links.Update(link);
                                    }).ConfigureAwait(false);

                                    await Task.Delay(10).ConfigureAwait(false);
                                }
                            });


                            // Attempt to post the messages in sync with the created date.
                            try
                            {
                                var guildUsers = new List <IGuildUser>();
                                foreach (var message in messages.OrderBy(m => m.CreatedAt))
                                {
                                    int retryCount = 0;
                                    while (retryCount < 10)
                                    {
                                        // Cancel out where needed
                                        if (cancellationToken.IsCancellationRequested)
                                        {
                                            await funcUpdateLinkDate().ConfigureAwait(false);
                                            return(messageIds);
                                        }

                                        // Attempt to send the message.
                                        try
                                        {
                                            var authorGuildUser = guildUsers.FirstOrDefault(x => x.Id == message.Author.Id);
                                            if (authorGuildUser == null)
                                            {
                                                try
                                                {
                                                    authorGuildUser = await linkChannel.Guild.GetUserAsync(message.Author.Id).ConfigureAwait(false);
                                                    if (authorGuildUser != null)
                                                    {
                                                        guildUsers.Add(authorGuildUser);
                                                    }
                                                }
                                                catch { }
                                            }

                                            var dateUtc      = message.CreatedAt.UtcDateTime;
                                            var embedBuilder = new EmbedBuilder().WithAuthor(new EmbedAuthorBuilder()
                                                                                             .WithIconUrl(message.Author.GetAvatarUrl())
                                                                                             .WithName(authorGuildUser?.Nickname ?? message.Author?.Username)
                                                                                             )
                                                               //.WithTitle(message.Channel.Name)
                                                               .WithDescription(message.Content)
                                                               .WithFooter($"{dateUtc:dddd, MMMM} {dateUtc.Day.Ordinal()} {dateUtc:yyyy} at {dateUtc:HH:mm} UTC")
                                                               .WithDiscordLinkColour(channel.Guild)
                                            ;

                                            if (message.Attachments.Count > 0)
                                            {
                                                embedBuilder.WithImageUrl(message.Attachments.FirstOrDefault().Url);
                                            }

                                            var postedMessage = await channel.SendMessageAsync(embed: embedBuilder.Build(), options: new RequestOptions()
                                            {
                                                RetryMode = RetryMode.AlwaysFail
                                            }).ConfigureAwait(false);
                                            if (postedMessage != null)
                                            {
                                                // Do not add the message to the messageIds, we do not use the link_items database for this.
                                                //messageIds.Add(message.Id.ToString());
                                                lastMessageDate = message.CreatedAt.UtcDateTime;
                                            }

                                            // OK, cancel out.
                                            break;
                                        }
                                        catch (Exception ex)
                                        {
                                            // Update the link date just in case.
                                            await funcUpdateLinkDate().ConfigureAwait(false);

                                            // Cancel out where needed
                                            if (cancellationToken.IsCancellationRequested)
                                            {
                                                return(messageIds);
                                            }

                                            // Attempt to retry sending the message
                                            if (!await LinkUtility.SendRetryLinkMessageAsync(link.Type, retryCount++, ex is Discord.Net.RateLimitedException ? null : ex))
                                            {
                                                return(messageIds);
                                            }
                                        }
                                    }
                                }
                            }
                            finally
                            {
                                // Update the link date time.
                                await funcUpdateLinkDate().ConfigureAwait(false);
                            }
                        }
                    }
                }

                LastUpdate.TryUpdate(link.Id, DateTime.UtcNow, lastUpdateTime);
                return(messageIds);
            });
        }