/// <summary>
        /// 向数据库添加并一条邮政消息并保存
        /// </summary>
        /// <param name="message">消息对象</param>
        public async Task AddAsync([NotNull] Message message)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            _dbContext.Messages.Add(message);
            await _dbContext.SaveChangesAsync();

            await IncreaseUserUnreadMessageCountAsync(message.ReceiverId, 1);

            NotificationProvider.Hub <MessageHub, IMessageHubClient>().User(message.ReceiverId)?
            .OnUnreadCountChanged(await GetUserUnreadMessageCountAsync(message.ReceiverId));
        }
        /// <summary>
        /// 创建 <see cref="PostOfficeMessageList"/>
        /// </summary>
        /// <param name="pageType">邮政页面类型</param>
        /// <param name="currentUserId">当前登录用户 ID</param>
        /// <param name="page">分页页码</param>
        /// <param name="returnPageCount">是否返回总页数</param>
        /// <param name="dbContext"><see cref="KeylolDbContext"/></param>
        /// <param name="cachedData"><see cref="CachedDataProvider"/></param>
        /// <returns>Item1 表示 <see cref="PostOfficeMessageList"/>,Item2 表示总页数</returns>
        public static async Task <Tuple <PostOfficeMessageList, int> > CreateAsync(Type pageType, string currentUserId,
                                                                                   int page, bool returnPageCount, KeylolDbContext dbContext, CachedDataProvider cachedData)
        {
            Expression <Func <Message, bool> > condition;

            if (pageType == typeof(UnreadPage))
            {
                condition = m => m.ReceiverId == currentUserId;
            }
            else if (pageType == typeof(CommentPage))
            {
                condition = m => m.ReceiverId == currentUserId && (int)m.Type >= 100 && (int)m.Type <= 199;
            }
            else if (pageType == typeof(LikePage))
            {
                condition = m => m.ReceiverId == currentUserId && m.Type >= 0 && (int)m.Type <= 99;
            }
            else if (pageType == typeof(SubscriberPage))
            {
                condition = m => m.ReceiverId == currentUserId && (int)m.Type >= 300 && (int)m.Type <= 399;
            }
            else if (pageType == typeof(MissivePage))
            {
                condition = m => m.ReceiverId == currentUserId && (int)m.Type >= 200 && (int)m.Type <= 299;
            }
            else
            {
                throw new ArgumentOutOfRangeException(nameof(pageType));
            }

            var messages = await dbContext.Messages.Include(m => m.Article)
                           .Include(m => m.Article.Author)
                           .Include(m => m.Activity)
                           .Include(m => m.Activity.Author)
                           .Include(m => m.Operator)
                           .Include(m => m.ArticleComment)
                           .Include(m => m.ArticleComment.Article)
                           .Include(m => m.ArticleComment.Article.Author)
                           .Include(m => m.ActivityComment)
                           .Include(m => m.ActivityComment.Activity)
                           .Include(m => m.ActivityComment.Activity.Author)
                           .Where(condition)
                           .OrderByDescending(m => m.Unread)
                           .ThenByDescending(m => m.Sid)
                           .TakePage(page, RecordsPerPage)
                           .ToListAsync();

            var result        = new PostOfficeMessageList(messages.Count);
            var markReadCount = 0;

            foreach (var m in messages)
            {
                var item = new PostOfficeMessage
                {
                    Type       = m.Type,
                    CreateTime = m.CreateTime,
                    Unread     = m.Unread
                };

                if (m.Type.IsMissiveMessage())
                {
                    item.Id = m.Id;
                }
                else
                {
                    item.OperatorIdCode      = m.Operator.IdCode;
                    item.OperatorAvatarImage = m.Operator.AvatarImage;
                    item.OperatorUserName    = m.Operator.UserName;
                }

                if (!string.IsNullOrWhiteSpace(m.Reasons))
                {
                    item.Reasons =
                        m.Reasons.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToList();
                }

                if (m.ArticleId != null)
                {
                    item.ArticleAuthorIdCode = m.Article.Author.IdCode;
                    item.ArticleSidForAuthor = m.Article.SidForAuthor;
                    item.ArticleTitle        = CollapleArticleTitle(m.Article.Title);
                }
                else if (m.ActivityId != null)
                {
                    item.ActivityAuthorIdCode = m.Activity.Author.IdCode;
                    item.ActivitySidForAuthor = m.Activity.SidForAuthor;
                    item.ActivityContent      = CollapseActivityContent(m.Activity);
                }
                else if (m.ArticleCommentId != null)
                {
                    item.CommentContent      = CollapseCommentContent(m.ArticleComment.UnstyledContent);
                    item.CommentSidForParent = m.ArticleComment.SidForArticle;
                    item.ArticleAuthorIdCode = m.ArticleComment.Article.Author.IdCode;
                    item.ArticleSidForAuthor = m.ArticleComment.Article.SidForAuthor;
                    item.ArticleTitle        = CollapleArticleTitle(m.ArticleComment.Article.Title);
                }
                else if (m.ActivityCommentId != null)
                {
                    item.CommentContent       = CollapseCommentContent(m.ActivityComment.Content);
                    item.CommentSidForParent  = m.ActivityComment.SidForActivity;
                    item.ActivityAuthorIdCode = m.ActivityComment.Activity.Author.IdCode;
                    item.ActivitySidForAuthor = m.ActivityComment.Activity.SidForAuthor;
                    item.ActivityContent      = CollapseActivityContent(m.ActivityComment.Activity);
                }

                if (m.Count > 0)
                {
                    item.Count = m.Count;
                }
                if (m.SecondCount > 0)
                {
                    item.SecondCount = m.SecondCount;
                }

                result.Add(item);
                if (m.Unread)
                {
                    m.Unread = false;
                    markReadCount++;
                }
            }
            await dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.ClientWin);

            await cachedData.Messages.IncreaseUserUnreadMessageCountAsync(currentUserId, -markReadCount);

            NotificationProvider.Hub <MessageHub, IMessageHubClient>().User(currentUserId)?
            .OnUnreadCountChanged(await cachedData.Messages.GetUserUnreadMessageCountAsync(currentUserId));
            var pageCount = 1;

            if (returnPageCount)
            {
                var totalCount = await dbContext.Messages.CountAsync(condition);

                pageCount = totalCount > 0 ? (int)Math.Ceiling(totalCount / (double)RecordsPerPage) : 1;
            }
            return(new Tuple <PostOfficeMessageList, int>(result, pageCount));
        }
        /// <summary>
        ///     当机器人收到新的聊天消息时,通过此方法通知协调器
        /// </summary>
        /// <param name="senderSteamId">消息发送人 Steam ID</param>
        /// <param name="botId">机器人 ID</param>
        /// <param name="message">聊天消息内容</param>
        public async Task OnBotNewChatMessage(string senderSteamId, string botId, string message)
        {
            using (var dbContext = new KeylolDbContext())
            {
                var userManager = new KeylolUserManager(dbContext);
                var user        = await userManager.FindBySteamIdAsync(senderSteamId);

                if (user == null)
                {
                    // 非会员,只接受绑定验证码
                    var code  = message.Trim();
                    var token = await dbContext.SteamBindingTokens
                                .SingleOrDefaultAsync(t => t.Code == code && t.SteamId == null);

                    if (token == null)
                    {
                        await Client.SendChatMessage(botId, senderSteamId,
                                                     "你的输入无法被识别,请确认登录验证码的长度和格式。如果需要帮助,请与其乐职员取得联系。");
                    }
                    else
                    {
                        token.BotId   = botId;
                        token.SteamId = senderSteamId;
                        await dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.DatabaseWin);

                        NotificationProvider.Hub <SteamBindingHub, ISteamBindingHubClient>()
                        .Client(token.BrowserConnectionId)?
                        .OnBind(await Client.GetUserProfileName(botId, senderSteamId),
                                await Client.GetUserAvatarHash(botId, senderSteamId));
                        await Client.SendChatMessage(botId, senderSteamId,
                                                     "绑定成功,欢迎加入其乐!今后你可以向机器人发送对话快速登录社区,请勿将机器人从好友列表移除。");

                        await Task.Delay(TimeSpan.FromSeconds(5));

                        await Client.SendChatMessage(botId, senderSteamId,
                                                     "若希望在其乐上获得符合游戏兴趣的据点推荐,请避免将 Steam 资料隐私设置为「仅自己可见」。");
                    }
                }
                else
                {
                    // 已有会员,接受登录验证码和调侃调戏
                    var match = Regex.Match(message, @"^\s*(\d{4})\s*$");
                    if (match.Success)
                    {
                        var code = match.Groups[1].Value;
                        try
                        {
                            var connectionId = await _oneTimeToken.Consume <string>(code, OneTimeTokenPurpose.SteamLogin);

                            var loginToken = await _oneTimeToken.Generate(user.Id, TimeSpan.FromMinutes(1),
                                                                          OneTimeTokenPurpose.UserLogin);

                            NotificationProvider.Hub <SteamLoginHub, ISteamLoginHubClient>()
                            .Client(connectionId).OnLoginOneTimeToken(loginToken, user.UserName, user.AvatarImage);
                            await Client.SendChatMessage(botId, senderSteamId, "欢迎回来,你已成功登录其乐社区。");
                        }
                        catch (Exception)
                        {
                            await Client.SendChatMessage(botId, senderSteamId,
                                                         "你的输入无法被识别,请确认登录验证码的长度和格式。如果需要帮助,请与其乐职员取得联系。");
                        }
                    }
                    else
                    {
                        if (!AutoChatDisabledBots.ContainsKey(botId))
                        {
                            await Client.SendChatMessage(botId, senderSteamId, await AskTulingBotAsync(message, user.Id),
                                                         true);
                        }
                    }
                }
            }
        }
        /// <summary>
        ///     当机器人接收到用户好友请求时,通过此方法通知协调器
        /// </summary>
        /// <param name="userSteamId">用户 Steam ID</param>
        /// <param name="botId">机器人 ID</param>
        public async Task OnBotNewFriendRequest(string userSteamId, string botId)
        {
            await Client.AddFriend(botId, userSteamId); // 先接受请求

            using (var dbContext = new KeylolDbContext())
            {
                var userManager = new KeylolUserManager(dbContext);
                var user        = await userManager.FindBySteamIdAsync(userSteamId);

                if (user == null)
                {
                    // 非会员,在注册时绑定机器人
                    NotificationProvider.Hub <SteamBindingHub, ISteamBindingHubClient>()
                    .Clients(await dbContext.SteamBindingTokens.Where(t => t.BotId == botId)
                             .Select(t => t.BrowserConnectionId)
                             .ToListAsync())?
                    .OnFriend();
                    await Task.Delay(TimeSpan.FromSeconds(3));

                    await Client.SendChatMessage(botId, userSteamId,
                                                 "欢迎使用当前 Steam 账号加入其乐,请输入你在网页上获取的 8 位绑定验证码。");

                    var queueName = MqClientProvider.SteamBotDelayedActionQueue(botId);
                    _mqChannel.QueueDeclare(queueName, true, false, false, null);
                    _mqChannel.SendMessage(MqClientProvider.DelayedMessageExchange,
                                           queueName, new SteamBotDelayedActionDto
                    {
                        Type       = SteamBotDelayedActionType.RemoveFriend,
                        Properties = new
                        {
                            OnlyIfNotKeylolUser = true,
                            Message             = "抱歉,你的会话因超时被强制结束,机器人已将你从好友列表中暂时移除。若要加入其乐,请重新按照网页指示注册账号。",
                            SteamId             = userSteamId
                        }
                    }, 300000);
                }
                else
                {
                    // 现有会员添加机器人为好友
                    var bot = await dbContext.SteamBots.FindAsync(botId);

                    if (user.SteamBotId == null && bot != null && bot.FriendCount < bot.FriendUpperLimit)
                    {
                        // 用户此前删除了机器人好友,重新设定当前机器人为绑定的机器人
                        user.SteamBotId       = botId;
                        user.SteamBindingTime = DateTime.Now;
                        await dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.DatabaseWin);

                        await Task.Delay(TimeSpan.FromSeconds(3));

                        await Client.SendChatMessage(botId, userSteamId,
                                                     "你已成功与其乐机器人再次绑定,请务必不要将其乐机器人从好友列表中移除。");
                    }
                    else
                    {
                        // 用户状态正常但是添加了新的机器人好友,应当移除好友
                        await Task.Delay(TimeSpan.FromSeconds(3));

                        await Client.SendChatMessage(botId, userSteamId,
                                                     "你已绑定另一其乐机器人,当前机器人已拒绝你的好友请求。如有需要,你可以在其乐设置表单中找到你绑定的机器人帐号。");

                        await Client.RemoveFriend(botId, userSteamId);
                    }
                }
            }
        }
Esempio n. 5
0
        private static void RegisterServices()
        {
            Container.Options.DefaultScopedLifestyle = new OwinRequestLifestyle();

            // log4net
            SetupLogger();
            Container.RegisterConditional(typeof(ILogProvider),
                                          c => typeof(LogProvider <>).MakeGenericType(c.Consumer?.ImplementationType ?? typeof(Startup)),
                                          Lifestyle.Singleton,
                                          c => true);

            // RabbitMQ IConnection
            Container.RegisterSingleton <MqClientProvider>();

            // RabbitMQ IModel
            Container.RegisterPerOwinRequest(() => Container.GetInstance <MqClientProvider>().CreateModel());

            // StackExchange.Redis
            Container.RegisterSingleton <RedisProvider>();

            // Geetest
            Container.RegisterSingleton <GeetestProvider>();

            // OWIN Context Provider
            Container.RegisterSingleton <OwinContextProvider>();

            // Keylol DbContext
            Container.RegisterPerOwinRequest(() =>
            {
                var context = new KeylolDbContext();
#if DEBUG
                context.Database.Log = s => { NotificationProvider.Hub <LogHub, ILogHubClient>().All.OnWrite(s); };
#endif
                return(context);
            });

            // Keylol User Manager
            Container.RegisterPerOwinRequest <KeylolUserManager>();

            // Keylol Role Manager
            Container.RegisterPerOwinRequest <KeylolRoleManager>();

            // Coupon
            Container.RegisterPerOwinRequest <CouponProvider>();

            // Statistics
            Container.RegisterPerOwinRequest <CachedDataProvider>();

            // One-time Token
            Container.RegisterSingleton <OneTimeTokenProvider>();

            // Transient Fault Handling Retry Policy
            Container.RegisterSingleton <RetryPolicy>(() =>
            {
                // 首次失败立即重试,之后重试每次增加 2 秒间隔
                var strategy = new Incremental(3, TimeSpan.Zero, TimeSpan.FromSeconds(2));
                return(new RetryPolicy <SoapFaultWebServiceTransientErrorDetectionStrategy>(strategy));
            });

            // Notification
            Container.RegisterSingleton <NotificationProvider>();

            // HttpConfiguration / Web API Controllers
            var httpConfiguration = new HttpConfiguration();
            Container.RegisterWebApiControllers(httpConfiguration);
            httpConfiguration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(Container);
            Container.RegisterSingleton(() => httpConfiguration);

            Container.Verify();
        }
Esempio n. 6
0
 /// <summary>
 /// Subclasses of <see cref="T:log4net.Appender.AppenderSkeleton" /> should implement this method
 /// to perform actual logging.
 /// </summary>
 /// <param name="loggingEvent">The event to append.</param>
 /// <remarks>
 /// <para>
 /// A subclass must implement this method to perform
 /// logging of the <paramref name="loggingEvent" />.
 /// </para>
 /// <para>This method will be called by <see cref="M:DoAppend(LoggingEvent)" />
 /// if all the conditions listed for that method are met.
 /// </para>
 /// <para>
 /// To restrict the logging of events in the appender
 /// override the <see cref="M:PreAppendCheck()" /> method.
 /// </para>
 /// </remarks>
 protected override void Append(LoggingEvent loggingEvent)
 {
     NotificationProvider.Hub <LogHub, ILogHubClient>()
     .All.OnWrite(
         $"[{loggingEvent.Level}] {loggingEvent.TimeStamp} {loggingEvent.LoggerName} - {loggingEvent.RenderedMessage}");
 }