Conversation.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. import {Interfaces} from "./Interfaces";
  2. import ConversationStateInterface = Interfaces.ConversationState;
  3. export module Conversations {
  4. import ClientInterface = Interfaces.ClientInterface;
  5. import ConversationInterface = Interfaces.ConversationInterface;
  6. export class NullManager implements Interfaces.ConversationsManagerInterface {
  7. get(id: string): Promise<Interfaces.ConversationInterface> {
  8. return undefined;
  9. }
  10. add(conversation: Interfaces.ConversationInterface) {
  11. }
  12. remove(id: string) {
  13. }
  14. create(initiator: Interfaces.ClientInterface, recipient: Interfaces.ClientInterface): Promise<Interfaces.ConversationInterface> {
  15. return undefined;
  16. }
  17. }
  18. export class DefaultManager implements Interfaces.ConversationsManagerInterface {
  19. private apiConnector: Interfaces.ApiConnectorInterface;
  20. private logger: Interfaces.LoggerInterface;
  21. private clientsManager: Interfaces.ClientsManagerInterface;
  22. private conversations: { [id: string]: Interfaces.ConversationInterface } = {};
  23. constructor(apiConnector: Interfaces.ApiConnectorInterface, clientsManager: Interfaces.ClientsManagerInterface, logger: Interfaces.LoggerInterface) {
  24. this.apiConnector = apiConnector;
  25. this.clientsManager = clientsManager;
  26. this.logger = logger;
  27. }
  28. get(id: string): Promise<ConversationInterface> {
  29. this.logger.debug(`Get conversation with ID=${id}`);
  30. return new Promise((resolve, reject) => {
  31. if (this.conversations.hasOwnProperty(id)) {
  32. this.logger.debug(`Conversation with ID=${id} is already in list`);
  33. resolve(this.conversations[id]);
  34. } else {
  35. this.logger.debug(`Load conversation with ID=${id} by API call`);
  36. this.apiConnector.execute('/conversations', {action: 'get', id: id})
  37. .then((response: string) => {
  38. let data = JSON.parse(response);
  39. Promise.all([this.clientsManager.get(data.initiatorId), this.clientsManager.get(data.recipientId)])
  40. .then((values: [ClientInterface, ClientInterface]) => {
  41. let initiator = values[0];
  42. let recipient = values[1];
  43. let conversation = new Conversation(this.apiConnector, initiator, recipient, this.logger, data.id);
  44. conversation.duration = parseInt(data.duration);
  45. if (data.isStarted == 1 && data.isFinished == 0) {
  46. conversation.state = Interfaces.ConversationState.RUNNING();
  47. }
  48. this.add(conversation);
  49. resolve(this.conversations[id]);
  50. }).catch(reject);
  51. }).catch(reject);
  52. }
  53. });
  54. }
  55. create(initiator: Interfaces.ClientInterface, recipient: Interfaces.ClientInterface): Promise<Interfaces.ConversationInterface> {
  56. this.logger.debug(`Create new conversation`);
  57. return new Promise((resolve, reject) => {
  58. return this.apiConnector.execute('/conversations', {action: 'create'})
  59. .then(response => {
  60. let data = JSON.parse(response);
  61. return this.apiConnector.execute('/conversations', {
  62. action: 'init',
  63. id: data.id,
  64. peers: [initiator.id, recipient.id]
  65. });
  66. })
  67. .then(response => {
  68. let data = JSON.parse(response);
  69. let conversation = new Conversation(this.apiConnector, initiator, recipient, this.logger, data.id);
  70. this.add(conversation);
  71. resolve(conversation);
  72. })
  73. .catch(reject);
  74. });
  75. }
  76. add(conversation: Interfaces.ConversationInterface) {
  77. this.logger.debug(`Add conversation with ID=${conversation.id}`);
  78. this.conversations[conversation.id] = conversation;
  79. }
  80. remove(id: string) {
  81. this.logger.debug(`Delete conversation with ID=${id}`);
  82. delete this.conversations[id];
  83. }
  84. }
  85. export class Conversation implements Interfaces.ConversationInterface {
  86. private interval = null;
  87. private syncInterval = null;
  88. private _id: string;
  89. duration: number = 0;
  90. private conversationState: (value?: Interfaces.ConversationState) => ConversationStateInterface;
  91. private apiConnector: Interfaces.ApiConnectorInterface;
  92. private initiator: Interfaces.ClientInterface;
  93. private recipient: Interfaces.ClientInterface;
  94. private logger: Interfaces.LoggerInterface;
  95. constructor(apiConnector: Interfaces.ApiConnectorInterface, initiator: Interfaces.ClientInterface, recipient: Interfaces.ClientInterface, logger: Interfaces.LoggerInterface, id: string) {
  96. this.apiConnector = apiConnector;
  97. this.initiator = initiator;
  98. this.recipient = recipient;
  99. this.logger = logger;
  100. this._id = id;
  101. let state = Interfaces.ConversationState.INIT();
  102. this.conversationState = (value?: ConversationStateInterface) => {
  103. if (value !== undefined)
  104. state = value;
  105. return state;
  106. };
  107. this.state = ConversationStateInterface.INIT();
  108. }
  109. getInitiator(): Interfaces.ClientInterface {
  110. return this.initiator;
  111. }
  112. getRecipient(): Interfaces.ClientInterface {
  113. return this.recipient;
  114. }
  115. get id(): string {
  116. return this._id;
  117. }
  118. get state() {
  119. return this.conversationState();
  120. }
  121. set state(state: Interfaces.ConversationState) {
  122. if (!this.state.isEqualsTo(state)) {
  123. if (state.isEqualsTo(ConversationStateInterface.INIT())) {
  124. }
  125. if (state.isEqualsTo(ConversationStateInterface.RUNNING())) {
  126. this.setRunning();
  127. }
  128. if (state.isEqualsTo(ConversationStateInterface.PAUSED())) {
  129. this.setPaused();
  130. }
  131. if (state.isEqualsTo(ConversationStateInterface.STOPPED())) {
  132. this.setStopped();
  133. }
  134. this.conversationState(state);
  135. } else {
  136. this.logger.debug(`Conversation state already set to "${state.toString()}"`);
  137. }
  138. }
  139. private setRunning() {
  140. this.logger.debug(`Set conversation with ID=${this.id} running`);
  141. if (this.interval === null) {
  142. let lastIntervalTick = Date.now();
  143. this.interval = setInterval(() => {
  144. let now = Date.now();
  145. let interval = (now - lastIntervalTick) / 1000;
  146. this.duration += interval;
  147. lastIntervalTick = now;
  148. this.initiator.payedTime -= Math.round(interval * this.recipient.coefficient);
  149. this.recipient.payedTime += Math.round(interval * this.recipient.coefficient);
  150. if (this.initiator.payedTime < 0) {
  151. this.logger.debug(`User with ID=${this.initiator.id} is ran out of money`);
  152. this.state = ConversationStateInterface.STOPPED();
  153. }
  154. }, 1000);
  155. }
  156. if (this.syncInterval === null) {
  157. this.syncInterval = setInterval(() => {
  158. this.sync();
  159. }, 5000);
  160. }
  161. this.apiConnector.execute('/conversations', {id: this.id, action: 'start'});
  162. }
  163. private sync() {
  164. this.initiator.send('chat-sync-timer', {
  165. conversationId: this.id,
  166. duration: Math.round(this.duration)
  167. });
  168. this.recipient.send('chat-sync-timer', {
  169. conversationId: this.id,
  170. duration: Math.round(this.duration)
  171. });
  172. this.apiConnector.execute('/conversations', {
  173. id: this.id,
  174. action: 'duration',
  175. duration: Math.round(this.duration)
  176. });
  177. this.apiConnector.execute('/user', {
  178. action: 'updatetime',
  179. id: this.initiator.id,
  180. value: Math.round(this.initiator.payedTime)
  181. });
  182. this.apiConnector.execute('/user', {
  183. action: 'updatetime',
  184. id: this.recipient.id,
  185. value: Math.round(this.recipient.payedTime)
  186. });
  187. }
  188. private setPaused() {
  189. this.logger.debug(`Set conversation with ID=${this.id} paused`);
  190. clearInterval(this.interval);
  191. this.interval = null;
  192. clearInterval(this.syncInterval);
  193. this.syncInterval = null;
  194. this.initiator.send('chat-conversation-pause', {id: this.id});
  195. this.recipient.send('chat-conversation-pause', {id: this.id});
  196. }
  197. private setStopped() {
  198. this.logger.debug(`Set conversation with ID=${this.id} stopped`);
  199. clearInterval(this.interval);
  200. this.interval = null;
  201. clearInterval(this.syncInterval);
  202. this.syncInterval = null;
  203. delete this.initiator.conversations[this.id];
  204. delete this.recipient.conversations[this.id];
  205. this.apiConnector.execute('/conversations', {id: this.id, action: 'stop', duration: Math.round(this.duration)});
  206. this.apiConnector.execute('/user', {
  207. action: 'updatetime',
  208. id: this.initiator.id,
  209. value: this.initiator.payedTime
  210. });
  211. this.apiConnector.execute('/user', {
  212. action: 'updatetime',
  213. id: this.recipient.id,
  214. value: this.recipient.payedTime
  215. });
  216. this.initiator.send('chat-conversation-stop', {id: this.id});
  217. this.recipient.send('chat-conversation-stop', {id: this.id});
  218. }
  219. private getPeer(id): Interfaces.ClientInterface {
  220. let peers = [this.initiator, this.recipient];
  221. for (let i in peers) {
  222. if (peers.hasOwnProperty(i) && parseInt(peers[i].id) === parseInt(id)) {
  223. return peers[i];
  224. }
  225. }
  226. }
  227. newMessage(message, from, to) {
  228. this.logger.debug(`New message to conversation with ID=${this.id}`);
  229. if (this.state.isEqualsTo(Interfaces.ConversationState.RUNNING())) {
  230. this.apiConnector.execute('/messages', {
  231. action: 'send',
  232. conversationId: this.id,
  233. senderId: from,
  234. recipientId: to,
  235. text: message
  236. }).then(response => {
  237. let data = JSON.parse(response);
  238. if (data.isSent) {
  239. let sender = this.getPeer(data.senderId);
  240. let recipient = this.getPeer(data.recipientId);
  241. let messageData = {
  242. message: data.text,
  243. conversationId: this.id,
  244. sender: {
  245. name: sender.name,
  246. photo: sender.photo
  247. },
  248. recipient: {
  249. name: recipient.name,
  250. photo: recipient.photo
  251. }
  252. };
  253. this.initiator.sendMessage(messageData);
  254. this.recipient.sendMessage(messageData);
  255. }
  256. }).catch(this.logger.error);
  257. }
  258. }
  259. }
  260. }