PageRenderTime 59ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/JabbR/Hubs/Chat.cs

https://bitbucket.org/v_jli/jean06129jabbrnewest
C# | 1180 lines | 859 code | 223 blank | 98 comment | 62 complexity | 9435033bf9371ebbe82fcef07315294f MD5 | raw file
Possible License(s): MIT
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using JabbR.Commands;
  7. using JabbR.ContentProviders.Core;
  8. using JabbR.Infrastructure;
  9. using JabbR.Models;
  10. using JabbR.Services;
  11. using JabbR.ViewModels;
  12. using Microsoft.AspNet.SignalR;
  13. using Newtonsoft.Json;
  14. namespace JabbR
  15. {
  16. [AuthorizeClaim(JabbRClaimTypes.Identifier)]
  17. public class Chat : Hub, INotificationService
  18. {
  19. private static readonly TimeSpan _disconnectThreshold = TimeSpan.FromSeconds(10);
  20. private readonly IJabbrRepository _repository;
  21. private readonly IChatService _service;
  22. private readonly ICache _cache;
  23. private readonly ContentProviderProcessor _resourceProcessor;
  24. private readonly ILogger _logger;
  25. public Chat(ContentProviderProcessor resourceProcessor,
  26. IChatService service,
  27. IJabbrRepository repository,
  28. ICache cache,
  29. ILogger logger)
  30. {
  31. _resourceProcessor = resourceProcessor;
  32. _service = service;
  33. _repository = repository;
  34. _cache = cache;
  35. _logger = logger;
  36. }
  37. private string UserAgent
  38. {
  39. get
  40. {
  41. if (Context.Headers != null)
  42. {
  43. return Context.Headers["User-Agent"];
  44. }
  45. return null;
  46. }
  47. }
  48. private bool OutOfSync
  49. {
  50. get
  51. {
  52. string version = Context.QueryString["version"];
  53. if (String.IsNullOrEmpty(version))
  54. {
  55. return true;
  56. }
  57. return new Version(version) != Constants.JabbRVersion;
  58. }
  59. }
  60. public override Task OnConnected()
  61. {
  62. _logger.Log("OnConnected({0})", Context.ConnectionId);
  63. CheckStatus();
  64. return base.OnConnected();
  65. }
  66. public void Join()
  67. {
  68. Join(reconnecting: false);
  69. }
  70. public void Join(bool reconnecting)
  71. {
  72. // Get the client state
  73. var userId = Context.User.GetUserId();
  74. // Try to get the user from the client state
  75. ChatUser user = _repository.GetUserById(userId);
  76. if (reconnecting)
  77. {
  78. _logger.Log("{0}:{1} connected after dropping connection.", user.Name, Context.ConnectionId);
  79. // If the user was marked as offline then mark them inactive
  80. if (user.Status == (int)UserStatus.Offline)
  81. {
  82. user.Status = (int)UserStatus.Inactive;
  83. _repository.CommitChanges();
  84. }
  85. // Ensure the client is re-added
  86. _service.AddClient(user, Context.ConnectionId, UserAgent);
  87. }
  88. else
  89. {
  90. _logger.Log("{0}:{1} connected.", user.Name, Context.ConnectionId);
  91. // Update some user values
  92. _service.UpdateActivity(user, Context.ConnectionId, UserAgent);
  93. _repository.CommitChanges();
  94. }
  95. ClientState clientState = GetClientState();
  96. OnUserInitialize(clientState, user, reconnecting);
  97. }
  98. private void CheckStatus()
  99. {
  100. if (OutOfSync)
  101. {
  102. Clients.Caller.outOfSync();
  103. }
  104. }
  105. private void OnUserInitialize(ClientState clientState, ChatUser user, bool reconnecting)
  106. {
  107. // Update the active room on the client (only if it's still a valid room)
  108. if (user.Rooms.Any(room => room.Name.Equals(clientState.ActiveRoom, StringComparison.OrdinalIgnoreCase)))
  109. {
  110. // Update the active room on the client (only if it's still a valid room)
  111. Clients.Caller.activeRoom = clientState.ActiveRoom;
  112. }
  113. LogOn(user, Context.ConnectionId, reconnecting);
  114. }
  115. public bool Send(string content, string roomName)
  116. {
  117. var message = new ClientMessage
  118. {
  119. Content = content,
  120. Room = roomName
  121. };
  122. return Send(message);
  123. }
  124. public bool Send(ClientMessage clientMessage)
  125. {
  126. CheckStatus();
  127. // See if this is a valid command (starts with /)
  128. if (TryHandleCommand(clientMessage.Content, clientMessage.Room))
  129. {
  130. return true;
  131. }
  132. var userId = Context.User.GetUserId();
  133. ChatUser user = _repository.VerifyUserId(userId);
  134. ChatRoom room = _repository.VerifyUserRoom(_cache, user, clientMessage.Room);
  135. if (room == null || (room.Private && !user.AllowedRooms.Contains(room)))
  136. {
  137. return false;
  138. }
  139. // REVIEW: Is it better to use _repository.VerifyRoom(message.Room, mustBeOpen: false)
  140. // here?
  141. if (room.Closed)
  142. {
  143. throw new InvalidOperationException(String.Format("You cannot post messages to '{0}'. The room is closed.", clientMessage.Room));
  144. }
  145. // Update activity *after* ensuring the user, this forces them to be active
  146. UpdateActivity(user, room);
  147. // Create a true unique id and save the message to the db
  148. string id = Guid.NewGuid().ToString("d");
  149. ChatMessage chatMessage = _service.AddMessage(user, room, id, clientMessage.Content);
  150. _repository.CommitChanges();
  151. var messageViewModel = new MessageViewModel(chatMessage);
  152. if (clientMessage.Id == null)
  153. {
  154. // If the client didn't generate an id for the message then just
  155. // send it to everyone. The assumption is that the client has some ui
  156. // that it wanted to update immediately showing the message and
  157. // then when the actual message is roundtripped it would "solidify it".
  158. Clients.Group(room.Name).addMessage(messageViewModel, room.Name);
  159. }
  160. else
  161. {
  162. // If the client did set an id then we need to give everyone the real id first
  163. Clients.OthersInGroup(room.Name).addMessage(messageViewModel, room.Name);
  164. // Now tell the caller to replace the message
  165. Clients.Caller.replaceMessage(clientMessage.Id, messageViewModel, room.Name);
  166. }
  167. // Add mentions
  168. AddMentions(chatMessage);
  169. var urls = UrlExtractor.ExtractUrls(chatMessage.Content);
  170. if (urls.Count > 0)
  171. {
  172. _resourceProcessor.ProcessUrls(urls, Clients, room.Name, chatMessage.Id);
  173. }
  174. return true;
  175. }
  176. private void AddMentions(ChatMessage message)
  177. {
  178. var mentionedUsers = new List<ChatUser>();
  179. foreach (var userName in MentionExtractor.ExtractMentions(message.Content))
  180. {
  181. ChatUser mentionedUser = _repository.GetUserByName(userName);
  182. // Don't create a mention if
  183. // 1. If the mentioned user doesn't exist.
  184. // 2. If you mention yourself
  185. // 3. If you're mentioned in a private room that you don't have access to
  186. if (mentionedUser == null ||
  187. mentionedUser == message.User ||
  188. (message.Room.Private && !mentionedUser.AllowedRooms.Contains(message.Room)))
  189. {
  190. continue;
  191. }
  192. // mark as read if ALL of the following
  193. // 1. user is not offline
  194. // 2. user is not AFK
  195. // 3. user has been active within the last 10 minutes
  196. // 4. user is currently in the room
  197. bool markAsRead = mentionedUser.Status != (int)UserStatus.Offline
  198. && !mentionedUser.IsAfk
  199. && (DateTimeOffset.UtcNow - mentionedUser.LastActivity) < TimeSpan.FromMinutes(10)
  200. && _repository.IsUserInRoom(_cache, mentionedUser, message.Room);
  201. _service.AddNotification(mentionedUser, message, message.Room, markAsRead);
  202. mentionedUsers.Add(mentionedUser);
  203. }
  204. if (mentionedUsers.Count > 0)
  205. {
  206. _repository.CommitChanges();
  207. }
  208. foreach (var user in mentionedUsers)
  209. {
  210. UpdateUnreadMentions(user);
  211. }
  212. }
  213. private void UpdateUnreadMentions(ChatUser mentionedUser)
  214. {
  215. var unread = _repository.GetUnreadNotificationsCount(mentionedUser);
  216. foreach (var client in mentionedUser.ConnectedClients)
  217. {
  218. Clients.Client(client.Id).updateUnreadNotifications(unread);
  219. }
  220. }
  221. public UserViewModel GetUserInfo()
  222. {
  223. var userId = Context.User.GetUserId();
  224. ChatUser user = _repository.VerifyUserId(userId);
  225. return new UserViewModel(user);
  226. }
  227. public override Task OnReconnected()
  228. {
  229. _logger.Log("OnReconnected({0})", Context.ConnectionId);
  230. var userId = Context.User.GetUserId();
  231. ChatUser user = _repository.VerifyUserId(userId);
  232. if (user == null)
  233. {
  234. _logger.Log("Reconnect failed user {0}:{1} doesn't exist.", userId, Context.ConnectionId);
  235. return TaskAsyncHelper.Empty;
  236. }
  237. // Make sure this client is being tracked
  238. _service.AddClient(user, Context.ConnectionId, UserAgent);
  239. var currentStatus = (UserStatus)user.Status;
  240. if (currentStatus == UserStatus.Offline)
  241. {
  242. _logger.Log("{0}:{1} reconnected after temporary network problem and marked offline.", user.Name, Context.ConnectionId);
  243. // Mark the user as inactive
  244. user.Status = (int)UserStatus.Inactive;
  245. _repository.CommitChanges();
  246. // If the user was offline that means they are not in the user list so we need to tell
  247. // everyone the user is really in the room
  248. var userViewModel = new UserViewModel(user);
  249. foreach (var room in user.Rooms)
  250. {
  251. var isOwner = user.OwnedRooms.Contains(room);
  252. // Tell the people in this room that you've joined
  253. Clients.Group(room.Name).addUser(userViewModel, room.Name, isOwner);
  254. }
  255. }
  256. else
  257. {
  258. _logger.Log("{0}:{1} reconnected after temporary network problem.", user.Name, Context.ConnectionId);
  259. }
  260. CheckStatus();
  261. return base.OnReconnected();
  262. }
  263. public override Task OnDisconnected()
  264. {
  265. _logger.Log("OnDisconnected({0})", Context.ConnectionId);
  266. DisconnectClient(Context.ConnectionId, useThreshold: true);
  267. return base.OnDisconnected();
  268. }
  269. public object GetCommands()
  270. {
  271. return CommandManager.GetCommandsMetaData();
  272. }
  273. public object GetShortcuts()
  274. {
  275. return new[] {
  276. new { Name = "Tab or Shift + Tab", Category = "shortcut", Description = "Go to the next open room tab or Go to the previous open room tab." },
  277. new { Name = "Alt + L", Category = "shortcut", Description = "Go to the Lobby."},
  278. new { Name = "Alt + Number", Category = "shortcut", Description = "Go to specific Tab."}
  279. };
  280. }
  281. public IEnumerable<LobbyRoomViewModel> GetRooms()
  282. {
  283. string userId = Context.User.GetUserId();
  284. ChatUser user = _repository.VerifyUserId(userId);
  285. var rooms = _repository.GetAllowedRooms(user).Select(r => new LobbyRoomViewModel
  286. {
  287. Name = r.Name,
  288. Count = r.Users.Count(u => u.Status != (int)UserStatus.Offline),
  289. Private = r.Private,
  290. Closed = r.Closed,
  291. Topic = r.Topic
  292. }).ToList();
  293. return rooms;
  294. }
  295. public IEnumerable<MessageViewModel> GetPreviousMessages(string messageId)
  296. {
  297. var previousMessages = (from m in _repository.GetPreviousMessages(messageId)
  298. orderby m.When descending
  299. select m).Take(100);
  300. return previousMessages.AsEnumerable()
  301. .Reverse()
  302. .Select(m => new MessageViewModel(m));
  303. }
  304. public RoomViewModel GetRoomInfo(string roomName)
  305. {
  306. if (String.IsNullOrEmpty(roomName))
  307. {
  308. return null;
  309. }
  310. string userId = Context.User.GetUserId();
  311. ChatUser user = _repository.VerifyUserId(userId);
  312. ChatRoom room = _repository.GetRoomByName(roomName);
  313. if (room == null || (room.Private && !user.AllowedRooms.Contains(room)))
  314. {
  315. return null;
  316. }
  317. var recentMessages = (from m in _repository.GetMessagesByRoom(room)
  318. orderby m.When descending
  319. select m).Take(50).ToList();
  320. // Reverse them since we want to get them in chronological order
  321. recentMessages.Reverse();
  322. // Get online users through the repository
  323. IEnumerable<ChatUser> onlineUsers = _repository.GetOnlineUsers(room).ToList();
  324. return new RoomViewModel
  325. {
  326. Name = room.Name,
  327. Users = from u in onlineUsers
  328. select new UserViewModel(u),
  329. Owners = from u in room.Owners.Online()
  330. select u.Name,
  331. RecentMessages = recentMessages.Select(m => new MessageViewModel(m)),
  332. Topic = room.Topic ?? "",
  333. Welcome = room.Welcome ?? "",
  334. Closed = room.Closed
  335. };
  336. }
  337. public void PostNotification(ClientNotification notification)
  338. {
  339. PostNotification(notification, executeContentProviders: true);
  340. }
  341. public void PostNotification(ClientNotification notification, bool executeContentProviders)
  342. {
  343. string userId = Context.User.GetUserId();
  344. ChatUser user = _repository.GetUserById(userId);
  345. ChatRoom room = _repository.VerifyUserRoom(_cache, user, notification.Room);
  346. // User must be an owner
  347. if (room == null ||
  348. !room.Owners.Contains(user) ||
  349. (room.Private && !user.AllowedRooms.Contains(room)))
  350. {
  351. throw new InvalidOperationException("You're not allowed to post a notification");
  352. }
  353. var chatMessage = new ChatMessage
  354. {
  355. Id = Guid.NewGuid().ToString("d"),
  356. Content = notification.Content,
  357. User = user,
  358. Room = room,
  359. HtmlEncoded = false,
  360. ImageUrl = notification.ImageUrl,
  361. Source = notification.Source,
  362. When = DateTimeOffset.UtcNow,
  363. MessageType = (int)MessageType.Notification
  364. };
  365. _repository.Add(chatMessage);
  366. _repository.CommitChanges();
  367. Clients.Group(room.Name).addMessage(new MessageViewModel(chatMessage), room.Name);
  368. if (executeContentProviders)
  369. {
  370. var urls = UrlExtractor.ExtractUrls(chatMessage.Content);
  371. if (urls.Count > 0)
  372. {
  373. _resourceProcessor.ProcessUrls(urls, Clients, room.Name, chatMessage.Id);
  374. }
  375. }
  376. }
  377. public void Typing(string roomName)
  378. {
  379. string userId = Context.User.GetUserId();
  380. ChatUser user = _repository.GetUserById(userId);
  381. ChatRoom room = _repository.VerifyUserRoom(_cache, user, roomName);
  382. if (room == null || (room.Private && !user.AllowedRooms.Contains(room)))
  383. {
  384. return;
  385. }
  386. UpdateActivity(user, room);
  387. var userViewModel = new UserViewModel(user);
  388. Clients.Group(room.Name).setTyping(userViewModel, room.Name);
  389. }
  390. public void UpdateActivity()
  391. {
  392. string userId = Context.User.GetUserId();
  393. ChatUser user = _repository.GetUserById(userId);
  394. foreach (var room in user.Rooms)
  395. {
  396. UpdateActivity(user, room);
  397. }
  398. CheckStatus();
  399. }
  400. private void LogOn(ChatUser user, string clientId, bool reconnecting)
  401. {
  402. if (!reconnecting)
  403. {
  404. // Update the client state
  405. Clients.Caller.id = user.Id;
  406. Clients.Caller.name = user.Name;
  407. Clients.Caller.hash = user.Hash;
  408. Clients.Caller.unreadNotifications = user.Notifications.Count(n => !n.Read);
  409. }
  410. var rooms = new List<RoomViewModel>();
  411. var privateRooms = new List<LobbyRoomViewModel>();
  412. var userViewModel = new UserViewModel(user);
  413. var ownedRooms = user.OwnedRooms.Select(r => r.Key);
  414. foreach (var room in user.Rooms)
  415. {
  416. var isOwner = ownedRooms.Contains(room.Key);
  417. // Tell the people in this room that you've joined
  418. Clients.Group(room.Name).addUser(userViewModel, room.Name, isOwner);
  419. // Add the caller to the group so they receive messages
  420. Groups.Add(clientId, room.Name);
  421. if (!reconnecting)
  422. {
  423. // Add to the list of room names
  424. rooms.Add(new RoomViewModel
  425. {
  426. Name = room.Name,
  427. Private = room.Private,
  428. Closed = room.Closed
  429. });
  430. }
  431. }
  432. if (!reconnecting)
  433. {
  434. foreach (var r in user.AllowedRooms)
  435. {
  436. privateRooms.Add(new LobbyRoomViewModel
  437. {
  438. Name = r.Name,
  439. Count = _repository.GetOnlineUsers(r).Count(),
  440. Private = r.Private,
  441. Closed = r.Closed,
  442. Topic = r.Topic
  443. });
  444. }
  445. // Initialize the chat with the rooms the user is in
  446. Clients.Caller.logOn(rooms, privateRooms);
  447. }
  448. }
  449. private void UpdateActivity(ChatUser user, ChatRoom room)
  450. {
  451. UpdateActivity(user);
  452. OnUpdateActivity(user, room);
  453. }
  454. private void UpdateActivity(ChatUser user)
  455. {
  456. _service.UpdateActivity(user, Context.ConnectionId, UserAgent);
  457. _repository.CommitChanges();
  458. }
  459. private bool TryHandleCommand(string command, string room)
  460. {
  461. string clientId = Context.ConnectionId;
  462. string userId = Context.User.GetUserId();
  463. var commandManager = new CommandManager(clientId, UserAgent, userId, room, _service, _repository, _cache, this);
  464. return commandManager.TryHandleCommand(command);
  465. }
  466. private void DisconnectClient(string clientId, bool useThreshold = false)
  467. {
  468. string userId = _service.DisconnectClient(clientId);
  469. if (String.IsNullOrEmpty(userId))
  470. {
  471. _logger.Log("Failed to disconnect {0}. No user found", clientId);
  472. return;
  473. }
  474. if (useThreshold)
  475. {
  476. Thread.Sleep(_disconnectThreshold);
  477. }
  478. // Query for the user to get the updated status
  479. ChatUser user = _repository.GetUserById(userId);
  480. // There's no associated user for this client id
  481. if (user == null)
  482. {
  483. _logger.Log("Failed to disconnect {0}:{1}. No user found", userId, clientId);
  484. return;
  485. }
  486. _repository.Reload(user);
  487. _logger.Log("{0}:{1} disconnected", user.Name, Context.ConnectionId);
  488. // The user will be marked as offline if all clients leave
  489. if (user.Status == (int)UserStatus.Offline)
  490. {
  491. _logger.Log("Marking {0} offline", user.Name);
  492. foreach (var room in user.Rooms)
  493. {
  494. var userViewModel = new UserViewModel(user);
  495. Clients.OthersInGroup(room.Name).leave(userViewModel, room.Name);
  496. }
  497. }
  498. }
  499. private void OnUpdateActivity(ChatUser user, ChatRoom room)
  500. {
  501. var userViewModel = new UserViewModel(user);
  502. Clients.Group(room.Name).updateActivity(userViewModel, room.Name);
  503. }
  504. private void LeaveRoom(ChatUser user, ChatRoom room)
  505. {
  506. var userViewModel = new UserViewModel(user);
  507. Clients.Group(room.Name).leave(userViewModel, room.Name);
  508. foreach (var client in user.ConnectedClients)
  509. {
  510. Groups.Remove(client.Id, room.Name);
  511. }
  512. OnRoomChanged(room);
  513. }
  514. void INotificationService.LogOn(ChatUser user, string clientId)
  515. {
  516. LogOn(user, clientId, reconnecting: true);
  517. }
  518. void INotificationService.ChangePassword()
  519. {
  520. Clients.Caller.changePassword();
  521. }
  522. void INotificationService.SetPassword()
  523. {
  524. Clients.Caller.setPassword();
  525. }
  526. void INotificationService.KickUser(ChatUser targetUser, ChatRoom room)
  527. {
  528. foreach (var client in targetUser.ConnectedClients)
  529. {
  530. // Kick the user from this room
  531. Clients.Client(client.Id).kick(room.Name);
  532. // Remove the user from this the room group so he doesn't get the leave message
  533. Groups.Remove(client.Id, room.Name);
  534. }
  535. // Tell the room the user left
  536. LeaveRoom(targetUser, room);
  537. }
  538. void INotificationService.OnUserCreated(ChatUser user)
  539. {
  540. // Set some client state
  541. Clients.Caller.name = user.Name;
  542. Clients.Caller.id = user.Id;
  543. Clients.Caller.hash = user.Hash;
  544. // Tell the client a user was created
  545. Clients.Caller.userCreated();
  546. }
  547. void INotificationService.JoinRoom(ChatUser user, ChatRoom room)
  548. {
  549. var userViewModel = new UserViewModel(user);
  550. var roomViewModel = new RoomViewModel
  551. {
  552. Name = room.Name,
  553. Private = room.Private,
  554. Welcome = room.Welcome ?? "",
  555. Closed = room.Closed
  556. };
  557. var isOwner = user.OwnedRooms.Contains(room);
  558. // Tell all clients to join this room
  559. foreach (var client in user.ConnectedClients)
  560. {
  561. Clients.Client(client.Id).joinRoom(roomViewModel);
  562. }
  563. // Tell the people in this room that you've joined
  564. Clients.Group(room.Name).addUser(userViewModel, room.Name, isOwner);
  565. // Notify users of the room count change
  566. OnRoomChanged(room);
  567. foreach (var client in user.ConnectedClients)
  568. {
  569. Groups.Add(client.Id, room.Name);
  570. }
  571. }
  572. void INotificationService.AllowUser(ChatUser targetUser, ChatRoom targetRoom)
  573. {
  574. foreach (var client in targetUser.ConnectedClients)
  575. {
  576. // Tell this client it's an owner
  577. Clients.Client(client.Id).allowUser(targetRoom.Name);
  578. }
  579. // Tell the calling client the granting permission into the room was successful
  580. Clients.Caller.userAllowed(targetUser.Name, targetRoom.Name);
  581. }
  582. void INotificationService.UnallowUser(ChatUser targetUser, ChatRoom targetRoom)
  583. {
  584. // Kick the user from the room when they are unallowed
  585. ((INotificationService)this).KickUser(targetUser, targetRoom);
  586. foreach (var client in targetUser.ConnectedClients)
  587. {
  588. // Tell this client it's an owner
  589. Clients.Client(client.Id).unallowUser(targetRoom.Name);
  590. }
  591. // Tell the calling client the granting permission into the room was successful
  592. Clients.Caller.userUnallowed(targetUser.Name, targetRoom.Name);
  593. }
  594. void INotificationService.AddOwner(ChatUser targetUser, ChatRoom targetRoom)
  595. {
  596. foreach (var client in targetUser.ConnectedClients)
  597. {
  598. // Tell this client it's an owner
  599. Clients.Client(client.Id).makeOwner(targetRoom.Name);
  600. }
  601. var userViewModel = new UserViewModel(targetUser);
  602. // If the target user is in the target room.
  603. // Tell everyone in the target room that a new owner was added
  604. if (_repository.IsUserInRoom(_cache, targetUser, targetRoom))
  605. {
  606. Clients.Group(targetRoom.Name).addOwner(userViewModel, targetRoom.Name);
  607. }
  608. // Tell the calling client the granting of ownership was successful
  609. Clients.Caller.ownerMade(targetUser.Name, targetRoom.Name);
  610. }
  611. void INotificationService.RemoveOwner(ChatUser targetUser, ChatRoom targetRoom)
  612. {
  613. foreach (var client in targetUser.ConnectedClients)
  614. {
  615. // Tell this client it's no longer an owner
  616. Clients.Client(client.Id).demoteOwner(targetRoom.Name);
  617. }
  618. var userViewModel = new UserViewModel(targetUser);
  619. // If the target user is in the target room.
  620. // Tell everyone in the target room that the owner was removed
  621. if (_repository.IsUserInRoom(_cache, targetUser, targetRoom))
  622. {
  623. Clients.Group(targetRoom.Name).removeOwner(userViewModel, targetRoom.Name);
  624. }
  625. // Tell the calling client the removal of ownership was successful
  626. Clients.Caller.ownerRemoved(targetUser.Name, targetRoom.Name);
  627. }
  628. void INotificationService.ChangeGravatar(ChatUser user)
  629. {
  630. Clients.Caller.hash = user.Hash;
  631. // Update the calling client
  632. foreach (var client in user.ConnectedClients)
  633. {
  634. Clients.Client(client.Id).gravatarChanged();
  635. }
  636. // Create the view model
  637. var userViewModel = new UserViewModel(user);
  638. // Tell all users in rooms to change the gravatar
  639. foreach (var room in user.Rooms)
  640. {
  641. Clients.Group(room.Name).changeGravatar(userViewModel, room.Name);
  642. }
  643. }
  644. void INotificationService.OnSelfMessage(ChatRoom room, ChatUser user, string content)
  645. {
  646. Clients.Group(room.Name).sendMeMessage(user.Name, content, room.Name);
  647. }
  648. void INotificationService.SendPrivateMessage(ChatUser fromUser, ChatUser toUser, string messageText)
  649. {
  650. // Send a message to the sender and the sendee
  651. foreach (var client in fromUser.ConnectedClients)
  652. {
  653. Clients.Client(client.Id).sendPrivateMessage(fromUser.Name, toUser.Name, messageText);
  654. }
  655. foreach (var client in toUser.ConnectedClients)
  656. {
  657. Clients.Client(client.Id).sendPrivateMessage(fromUser.Name, toUser.Name, messageText);
  658. }
  659. }
  660. void INotificationService.PostNotification(ChatRoom room, ChatUser user, string message)
  661. {
  662. foreach (var client in user.ConnectedClients)
  663. {
  664. Clients.Client(client.Id).postNotification(message, room.Name);
  665. }
  666. }
  667. void INotificationService.ListRooms(ChatUser user)
  668. {
  669. string userId = Context.User.GetUserId();
  670. var userModel = new UserViewModel(user);
  671. Clients.Caller.showUsersRoomList(userModel, user.Rooms.Allowed(userId).Select(r => r.Name));
  672. }
  673. void INotificationService.ListUsers()
  674. {
  675. var users = _repository.Users.Online().Select(s => s.Name).OrderBy(s => s);
  676. Clients.Caller.listUsers(users);
  677. }
  678. void INotificationService.ListUsers(IEnumerable<ChatUser> users)
  679. {
  680. Clients.Caller.listUsers(users.Select(s => s.Name));
  681. }
  682. void INotificationService.ListUsers(ChatRoom room, IEnumerable<string> names)
  683. {
  684. Clients.Caller.showUsersInRoom(room.Name, names);
  685. }
  686. void INotificationService.ListAllowedUsers(ChatRoom room)
  687. {
  688. Clients.Caller.listAllowedUsers(room.Name, room.Private, room.AllowedUsers.Select(s => s.Name));
  689. }
  690. void INotificationService.LockRoom(ChatUser targetUser, ChatRoom room)
  691. {
  692. var userViewModel = new UserViewModel(targetUser);
  693. // Tell the room it's locked
  694. Clients.All.lockRoom(userViewModel, room.Name);
  695. // Tell the caller the room was successfully locked
  696. Clients.Caller.roomLocked(room.Name);
  697. // Notify people of the change
  698. OnRoomChanged(room);
  699. }
  700. void INotificationService.CloseRoom(IEnumerable<ChatUser> users, ChatRoom room)
  701. {
  702. // notify all members of room that it is now closed
  703. foreach (var user in users)
  704. {
  705. foreach (var client in user.ConnectedClients)
  706. {
  707. Clients.Client(client.Id).roomClosed(room.Name);
  708. }
  709. }
  710. }
  711. void INotificationService.UnCloseRoom(IEnumerable<ChatUser> users, ChatRoom room)
  712. {
  713. // notify all members of room that it is now re-opened
  714. foreach (var user in users)
  715. {
  716. foreach (var client in user.ConnectedClients)
  717. {
  718. Clients.Client(client.Id).roomUnClosed(room.Name);
  719. }
  720. }
  721. }
  722. void INotificationService.LogOut(ChatUser user, string clientId)
  723. {
  724. foreach (var client in user.ConnectedClients)
  725. {
  726. DisconnectClient(client.Id);
  727. Clients.Client(client.Id).logOut();
  728. }
  729. }
  730. void INotificationService.ShowUserInfo(ChatUser user)
  731. {
  732. string userId = Context.User.GetUserId();
  733. Clients.Caller.showUserInfo(new
  734. {
  735. Name = user.Name,
  736. OwnedRooms = user.OwnedRooms
  737. .Allowed(userId)
  738. .Where(r => !r.Closed)
  739. .Select(r => r.Name),
  740. Status = ((UserStatus)user.Status).ToString(),
  741. LastActivity = user.LastActivity,
  742. IsAfk = user.IsAfk,
  743. AfkNote = user.AfkNote,
  744. Note = user.Note,
  745. Hash = user.Hash,
  746. Rooms = user.Rooms.Allowed(userId).Select(r => r.Name)
  747. });
  748. }
  749. void INotificationService.ShowHelp()
  750. {
  751. Clients.Caller.showCommands();
  752. }
  753. void INotificationService.Invite(ChatUser user, ChatUser targetUser, ChatRoom targetRoom)
  754. {
  755. // Send the invite message to the sendee
  756. foreach (var client in targetUser.ConnectedClients)
  757. {
  758. Clients.Client(client.Id).sendInvite(user.Name, targetUser.Name, targetRoom.Name);
  759. }
  760. // Send the invite notification to the sender
  761. foreach (var client in user.ConnectedClients)
  762. {
  763. Clients.Client(client.Id).sendInvite(user.Name, targetUser.Name, targetRoom.Name);
  764. }
  765. }
  766. void INotificationService.NugeUser(ChatUser user, ChatUser targetUser)
  767. {
  768. // Send a nudge message to the sender and the sendee
  769. foreach (var client in targetUser.ConnectedClients)
  770. {
  771. Clients.Client(client.Id).nudge(user.Name, targetUser.Name);
  772. }
  773. foreach (var client in user.ConnectedClients)
  774. {
  775. Clients.Client(client.Id).sendPrivateMessage(user.Name, targetUser.Name, "nudged " + targetUser.Name);
  776. }
  777. }
  778. void INotificationService.NudgeRoom(ChatRoom room, ChatUser user)
  779. {
  780. Clients.Group(room.Name).nudge(user.Name);
  781. }
  782. void INotificationService.LeaveRoom(ChatUser user, ChatRoom room)
  783. {
  784. LeaveRoom(user, room);
  785. }
  786. void INotificationService.OnUserNameChanged(ChatUser user, string oldUserName, string newUserName)
  787. {
  788. // Create the view model
  789. var userViewModel = new UserViewModel(user);
  790. // Tell the user's connected clients that the name changed
  791. foreach (var client in user.ConnectedClients)
  792. {
  793. Clients.Client(client.Id).userNameChanged(userViewModel);
  794. }
  795. // Notify all users in the rooms
  796. foreach (var room in user.Rooms)
  797. {
  798. Clients.Group(room.Name).changeUserName(oldUserName, userViewModel, room.Name);
  799. }
  800. }
  801. void INotificationService.ChangeNote(ChatUser user)
  802. {
  803. bool isNoteCleared = user.Note == null;
  804. // Update the calling client
  805. foreach (var client in user.ConnectedClients)
  806. {
  807. Clients.Client(client.Id).noteChanged(user.IsAfk, isNoteCleared);
  808. }
  809. // Create the view model
  810. var userViewModel = new UserViewModel(user);
  811. // Tell all users in rooms to change the note
  812. foreach (var room in user.Rooms)
  813. {
  814. Clients.Group(room.Name).changeNote(userViewModel, room.Name);
  815. }
  816. }
  817. void INotificationService.ChangeFlag(ChatUser user)
  818. {
  819. bool isFlagCleared = String.IsNullOrWhiteSpace(user.Flag);
  820. // Create the view model
  821. var userViewModel = new UserViewModel(user);
  822. // Update the calling client
  823. foreach (var client in user.ConnectedClients)
  824. {
  825. Clients.Client(client.Id).flagChanged(isFlagCleared, userViewModel.Country);
  826. }
  827. // Tell all users in rooms to change the flag
  828. foreach (var room in user.Rooms)
  829. {
  830. Clients.Group(room.Name).changeFlag(userViewModel, room.Name);
  831. }
  832. }
  833. void INotificationService.ChangeTopic(ChatUser user, ChatRoom room)
  834. {
  835. bool isTopicCleared = String.IsNullOrWhiteSpace(room.Topic);
  836. var parsedTopic = room.Topic ?? "";
  837. Clients.Group(room.Name).topicChanged(room.Name, isTopicCleared, parsedTopic, user.Name);
  838. // Create the view model
  839. var roomViewModel = new RoomViewModel
  840. {
  841. Name = room.Name,
  842. Topic = parsedTopic,
  843. Closed = room.Closed
  844. };
  845. Clients.Group(room.Name).changeTopic(roomViewModel);
  846. }
  847. void INotificationService.ChangeWelcome(ChatUser user, ChatRoom room)
  848. {
  849. bool isWelcomeCleared = String.IsNullOrWhiteSpace(room.Welcome);
  850. var parsedWelcome = room.Welcome ?? "";
  851. foreach (var client in user.ConnectedClients)
  852. {
  853. Clients.Client(client.Id).welcomeChanged(isWelcomeCleared, parsedWelcome);
  854. }
  855. }
  856. void INotificationService.AddAdmin(ChatUser targetUser)
  857. {
  858. foreach (var client in targetUser.ConnectedClients)
  859. {
  860. // Tell this client it's an owner
  861. Clients.Client(client.Id).makeAdmin();
  862. }
  863. var userViewModel = new UserViewModel(targetUser);
  864. // Tell all users in rooms to change the admin status
  865. foreach (var room in targetUser.Rooms)
  866. {
  867. Clients.Group(room.Name).addAdmin(userViewModel, room.Name);
  868. }
  869. // Tell the calling client the granting of admin status was successful
  870. Clients.Caller.adminMade(targetUser.Name);
  871. }
  872. void INotificationService.RemoveAdmin(ChatUser targetUser)
  873. {
  874. foreach (var client in targetUser.ConnectedClients)
  875. {
  876. // Tell this client it's no longer an owner
  877. Clients.Client(client.Id).demoteAdmin();
  878. }
  879. var userViewModel = new UserViewModel(targetUser);
  880. // Tell all users in rooms to change the admin status
  881. foreach (var room in targetUser.Rooms)
  882. {
  883. Clients.Group(room.Name).removeAdmin(userViewModel, room.Name);
  884. }
  885. // Tell the calling client the removal of admin status was successful
  886. Clients.Caller.adminRemoved(targetUser.Name);
  887. }
  888. void INotificationService.BroadcastMessage(ChatUser user, string messageText)
  889. {
  890. // Tell all users in all rooms about this message
  891. foreach (var room in _repository.Rooms)
  892. {
  893. Clients.Group(room.Name).broadcastMessage(messageText, room.Name);
  894. }
  895. }
  896. void INotificationService.ForceUpdate()
  897. {
  898. Clients.All.forceUpdate();
  899. }
  900. private void OnRoomChanged(ChatRoom room)
  901. {
  902. var roomViewModel = new RoomViewModel
  903. {
  904. Name = room.Name,
  905. Private = room.Private,
  906. Closed = room.Closed
  907. };
  908. // Update the room count
  909. Clients.All.updateRoomCount(roomViewModel, _repository.GetOnlineUsers(room).Count());
  910. }
  911. private ClientState GetClientState()
  912. {
  913. // New client state
  914. var jabbrState = GetCookieValue("jabbr.state");
  915. ClientState clientState = null;
  916. if (String.IsNullOrEmpty(jabbrState))
  917. {
  918. clientState = new ClientState();
  919. }
  920. else
  921. {
  922. clientState = JsonConvert.DeserializeObject<ClientState>(jabbrState);
  923. }
  924. return clientState;
  925. }
  926. private string GetCookieValue(string key)
  927. {
  928. Cookie cookie;
  929. Context.RequestCookies.TryGetValue(key, out cookie);
  930. string value = cookie != null ? cookie.Value : null;
  931. return value != null ? Uri.UnescapeDataString(value) : null;
  932. }
  933. void INotificationService.BanUser(ChatUser targetUser)
  934. {
  935. var rooms = targetUser.Rooms.Select(x => x.Name);
  936. foreach (var room in rooms)
  937. {
  938. foreach (var client in targetUser.ConnectedClients)
  939. {
  940. // Kick the user from this room
  941. Clients.Client(client.Id).kick(room);
  942. // Remove the user from this the room group so he doesn't get the leave message
  943. Groups.Remove(client.Id, room);
  944. }
  945. }
  946. Clients.Client(targetUser.ConnectedClients.First().Id).logOut(rooms);
  947. }
  948. protected override void Dispose(bool disposing)
  949. {
  950. if (disposing)
  951. {
  952. _repository.Dispose();
  953. }
  954. base.Dispose(disposing);
  955. }
  956. }
  957. }