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>(); }
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(); }
/// <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(); }
/// <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); }
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(); }; }
/// <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); } } } }
/// <summary> /// 创建 <see cref="NotificationProvider"/> /// </summary> /// <param name="mqClientProvider"></param> public NotificationProvider(MqClientProvider mqClientProvider) { _mqChannel = mqClientProvider.CreateModel(); }