Ejemplo n.º 1
0
        public BotInstance(IServiceConsumer <ISteamBotCoordinator> coordinator, ILogProvider logProvider,
                           BotCookieManager cookieManager, MqClientProvider mqClientProvider)
        {
            _coordinator      = coordinator;
            _logger           = logProvider.Logger;
            _mqClientProvider = mqClientProvider;
            CookieManager     = cookieManager;

            _callbackManager = new CallbackManager(SteamClient);

            _callbackManager.Subscribe <SteamClient.ConnectedCallback>(OnConnected);
            _callbackManager.Subscribe <SteamClient.DisconnectedCallback>(OnDisconnected);

            _callbackManager.Subscribe <SteamUser.LoggedOnCallback>(OnLoggedOn);
            _callbackManager.Subscribe <SteamUser.UpdateMachineAuthCallback>(OnUpdateMachineAuth);
            _callbackManager.Subscribe <SteamUser.LoginKeyCallback>(OnLoginKeyReceived);

            _callbackManager.Subscribe <SteamFriends.PersonaStateCallback>(OnPersonaStateChanged);
            _callbackManager.Subscribe <SteamFriends.FriendsListCallback>(OnFriendListUpdated);
            _callbackManager.Subscribe <SteamFriends.FriendMsgCallback>(OnFriendMessageReceived);

            _steamUser   = SteamClient.GetHandler <SteamUser>();
            SteamFriends = SteamClient.GetHandler <SteamFriends>();
            SteamApps    = SteamClient.GetHandler <SteamApps>();
        }
Ejemplo n.º 2
0
        public PushHub(ILogProvider logProvider, MqClientProvider mqClientProvider)
        {
            ServiceName = "Keylol.PushHub";

            _logger    = logProvider.Logger;
            _mqChannel = mqClientProvider.CreateModel();
        }
        /// <summary>
        ///     创建 <see cref="SteamBotCoordinator" />
        /// </summary>
        public SteamBotCoordinator(RetryPolicy retryPolicy, MqClientProvider mqClient, OneTimeTokenProvider oneTimeToken)
        {
            _retryPolicy  = retryPolicy;
            _mqChannel    = mqClient.CreateModel();
            _oneTimeToken = oneTimeToken;

            Sessions[SessionId] = this;
            OperationContext.Current.InstanceContext.Closing += OnSessionEnd;
            OnSessionBegin();
        }
Ejemplo n.º 4
0
        /// <summary>
        /// 客户端连接时调用。继承后需要确保 <c>base.OnConnected()</c> 得到调用。
        /// </summary>
        public override async Task OnConnected()
        {
            var mqChannel = Global.Container.GetInstance <MqClientProvider>().CreateModel();

            MqChannels[Context.ConnectionId] = mqChannel;
            var logger    = Global.Container.GetInstance <LogProvider <ReliableHub <TClient> > >().Logger;
            var userId    = Context.User.Identity.GetUserId();
            var queueName = MqClientProvider.ReliableNotificationQueue(userId, GetType().Name);

            mqChannel.QueueDeclare(queueName, true, false, false, null);
            var consumer = new EventingBasicConsumer(mqChannel);

            consumer.Received += (sender, args) =>
            {
                try
                {
                    using (var streamReader = new StreamReader(new MemoryStream(args.Body)))
                    {
                        var serializer   = new JsonSerializer();
                        var notification =
                            serializer.Deserialize <ReliableNotificationDto>(new JsonTextReader(streamReader));
                        typeof(TClient).GetMethod(notification.MethodName)
                        .Invoke(Clients.User(userId), notification.Arguments.Select(a =>
                        {
                            var argumentType = Type.GetType(a.Type);
                            if (argumentType == null)
                            {
                                return(null);
                            }
                            var value = a.Value as JToken;
                            return(value != null ? value.ToObject(argumentType) : a.Value);
                        }).ToArray());
                        mqChannel.BasicAck(args.DeliveryTag, false);
                    }
                }
                catch (Exception e)
                {
                    mqChannel.BasicNack(args.DeliveryTag, false, false);
                    logger.Fatal("Unhandled MQ consumer exception.", e);
                }
            };
            mqChannel.BasicConsume(queueName, false, consumer);
            await base.OnConnected();
        }
Ejemplo n.º 5
0
        /// <summary>
        /// 对指定用户发出通知,并保证通知送达(如果用户当前不在线则存储通知,下次上线时发送)
        /// </summary>
        /// <param name="userId">用户 ID</param>
        /// <param name="clientActionExpression">通知发送表达式</param>
        /// <typeparam name="THub">Hub 类型</typeparam>
        /// <typeparam name="TClient">Hub Client 类型</typeparam>
        public void ReliableNotify <THub, TClient>(string userId,
                                                   Expression <Action <TClient> > clientActionExpression)
            where THub : ReliableHub <TClient>
            where TClient : class
        {
            var methodCallExpression = (MethodCallExpression)clientActionExpression.Body;
            var hubType = typeof(THub);
            var dto     = new ReliableNotificationDto
            {
                MethodName = methodCallExpression.Method.Name,
                Arguments  = methodCallExpression.Arguments
                             .Select(argument => new ReliableNotificationArgumentDto
                {
                    Type  = $"{argument.Type.FullName}, {argument.Type.Assembly.GetName().Name}",
                    Value = Expression.Lambda(argument).Compile().DynamicInvoke()
                })
                             .ToList()
            };
            var queueName = MqClientProvider.ReliableNotificationQueue(userId, hubType.Name);

            _mqChannel.QueueDeclare(queueName, true, false, false, null);
            _mqChannel.SendMessage(string.Empty, queueName, dto);
        }
Ejemplo n.º 6
0
        public ImageGarage(ILogProvider logProvider, MqClientProvider mqClientProvider,
                           IServiceConsumer <IImageGarageCoordinator> coordinator)
        {
            ServiceName = "Keylol.ImageGarage";

            _logger            = logProvider.Logger;
            _mqChannel         = mqClientProvider.CreateModel();
            _coordinator       = coordinator;
            Config.HtmlEncoder = new HtmlEncoderNone();

            _heartbeatTimer.Elapsed += (sender, args) =>
            {
                try
                {
                    _coordinator.Operations.Ping();
                }
                catch (Exception e)
                {
                    _logger.Warn("Ping failed.", e);
                    _coordinator.Close();
                }
                _heartbeatTimer.Start();
            };
        }
Ejemplo n.º 7
0
        /// <summary>
        ///     启动机器人实例
        /// </summary>
        /// <param name="startWait">是否等待三秒后再启动,默认 <c>false</c></param>
        public void Start(bool startWait = false)
        {
            lock (_startStopLock)
            {
                if (_disposed)
                {
                    _logger.Fatal($"#{SequenceNumber} Try to restart disposed bot.");
                    // throw new InvalidOperationException("Try to restart disposed bot.");
                }

                if (!_callbackPumpStarted)
                {
                    _callbackPumpStarted = true;
                    Task.Run(() =>
                    {
                        _logger.Info($"#{SequenceNumber} Listening callbacks...");
                        while (!_disposed)
                        {
                            _callbackManager.RunWaitAllCallbacks(TimeSpan.FromMilliseconds(10));
                        }
                        _logger.Info($"#{SequenceNumber} Stopped listening callbacks.");
                    });
                }

                try
                {
                    _coordinator.Consume(coordinator => coordinator.UpdateBot(Id, null, false, null));
                }
                catch (Exception e)
                {
                    _logger.Fatal($"#{SequenceNumber} Cannot clear online state before start : {e.Message}");
                    Restart();
                    return;
                }
                if (startWait)
                {
                    _logger.Info($"#{SequenceNumber} Starting in 3 seconds...");
                    Thread.Sleep(TimeSpan.FromSeconds(3));
                }

                var sfhPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data", $"{LogOnDetails.Username}.sfh");
                if (File.Exists(sfhPath))
                {
                    LogOnDetails.SentryFileHash = File.ReadAllBytes(sfhPath);
                    _logger.Info($"#{SequenceNumber} Use sentry file hash from {LogOnDetails.Username}.sfh.");
                }

                if (!_loginPending)
                {
                    LoginSemaphore.WaitOne();
                    _loginPending = true;
                }
                SteamClient.Connect();

                _mqChannel = _mqClientProvider.CreateModel();
                _mqChannel.BasicQos(0, 5, false);
                var queueName = MqClientProvider.SteamBotDelayedActionQueue(Id);
                _mqChannel.QueueDeclare(queueName, true, false, false, null);
                _mqChannel.QueueBind(queueName, MqClientProvider.DelayedMessageExchange, queueName);
                var consumer = new EventingBasicConsumer(_mqChannel);
                consumer.Received += OnDelayedActionReceived;
                _mqChannel.BasicConsume(queueName, false, consumer);
            }
        }
        /// <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);
                    }
                }
            }
        }
Ejemplo n.º 9
0
 /// <summary>
 /// 创建 <see cref="NotificationProvider"/>
 /// </summary>
 /// <param name="mqClientProvider"></param>
 public NotificationProvider(MqClientProvider mqClientProvider)
 {
     _mqChannel = mqClientProvider.CreateModel();
 }