|
|
@@ -0,0 +1,246 @@
|
|
|
+/**
|
|
|
+ * Created by lcdee on 19.04.2017.
|
|
|
+ */
|
|
|
+
|
|
|
+import {Client} from "./Client";
|
|
|
+import * as SocketIO from "socket.io";
|
|
|
+
|
|
|
+export class App {
|
|
|
+ private clients: ClientsContainer = new ClientsContainer(this);
|
|
|
+ public conversations: { [index: string]: Conversation } = {};
|
|
|
+ private staticMappings = {};
|
|
|
+
|
|
|
+ constructor(config: AppConfigInterface) {
|
|
|
+ let fs = require('fs');
|
|
|
+ if (config.host === undefined) {
|
|
|
+ config.host = '127.0.0.1';
|
|
|
+ }
|
|
|
+ let httpServer = require('http').createServer((request, response) => {
|
|
|
+ //res.setHeader('Access-Control-Allow-Origin', 'galapsy.ru galapsy.com');
|
|
|
+ }).listen(config.port, config.host);
|
|
|
+ let socketIO = require('socket.io')(httpServer);
|
|
|
+ socketIO.on('connection', (socket) => {
|
|
|
+ socket.on('userConnect', (data) => {
|
|
|
+ this.addClient(socket, data);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ console.log(`Server started at ${config.host}:${config.port}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ getClient(id: string): Client {
|
|
|
+ try {
|
|
|
+ return this.clients.get(id);
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e.message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ addClient(socket: SocketIO.Socket, data: {} = {}) {
|
|
|
+ this.clients.add(socket, data);
|
|
|
+ }
|
|
|
+
|
|
|
+ removeClient(id: string) {
|
|
|
+ try {
|
|
|
+ this.clients.remove(id);
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e.message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ addConversation(conversation: Conversation) {
|
|
|
+ this.conversations[conversation.id] = conversation;
|
|
|
+ }
|
|
|
+
|
|
|
+ removeConversation(id: string) {
|
|
|
+ delete this.conversations[id];
|
|
|
+ }
|
|
|
+
|
|
|
+ apiCall(path: string, data: any, success: (data?: string) => void = () => {
|
|
|
+ }, fail: (err?: string) => void = () => {
|
|
|
+ }) {
|
|
|
+ let client = require('http').request({
|
|
|
+ host: 'galapsy.ru',
|
|
|
+ port: 80,
|
|
|
+ path: '/api' + path,
|
|
|
+ method: 'POST'
|
|
|
+ }, (response) => {
|
|
|
+ let responseMessage = '';
|
|
|
+ response.on('data', (chunk) => {
|
|
|
+ responseMessage += chunk;
|
|
|
+ }).on('end', () => {
|
|
|
+ success(responseMessage);
|
|
|
+ }).on('error', (err) => {
|
|
|
+ fail(err);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ client.write(JSON.stringify(data));
|
|
|
+ client.end();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export interface MessagingClientData {
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ photo: string;
|
|
|
+ role: string;
|
|
|
+ payedTime: string;
|
|
|
+ timeToPay: number;
|
|
|
+ coefficient: string;
|
|
|
+}
|
|
|
+
|
|
|
+class ClientsContainer {
|
|
|
+ private app: App;
|
|
|
+ private clients: { [index: string]: Client } = {};
|
|
|
+
|
|
|
+ constructor(app: App) {
|
|
|
+ this.app = app;
|
|
|
+ }
|
|
|
+
|
|
|
+ add(socket: SocketIO.Socket, data) {
|
|
|
+ if (!this.clients.hasOwnProperty(data.id)) {
|
|
|
+ this.app.apiCall('/user', {id: data.id, action: 'getinfo'}, response => {
|
|
|
+ let data = JSON.parse(response);
|
|
|
+ this.clients[data.id] = new Client(this.app, data);
|
|
|
+ this.clients[data.id].addSocket(socket);
|
|
|
+ this.app.apiCall('/user', {id: data.id, action: 'setstatus', value: true});
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.clients[data.id].addSocket(socket);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ get(id: string) {
|
|
|
+ if (this.clients !== undefined && this.clients[id] !== undefined) {
|
|
|
+ return this.clients[id];
|
|
|
+ }
|
|
|
+ throw new Error(`Client ${id} not found`);
|
|
|
+ }
|
|
|
+
|
|
|
+ remove(id: string) {
|
|
|
+ if (this.clients !== undefined && this.clients[id] !== undefined) {
|
|
|
+ delete this.clients[id];
|
|
|
+ } else {
|
|
|
+ throw new Error(`Client ${id} not found`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+export class Conversation {
|
|
|
+ private app: App;
|
|
|
+ private interval;
|
|
|
+ private syncInterval;
|
|
|
+
|
|
|
+ id: string;
|
|
|
+ initiator: Client;
|
|
|
+ recipient: Client;
|
|
|
+
|
|
|
+ duration: number = 0;
|
|
|
+
|
|
|
+ conversationState: (value?: number) => number;
|
|
|
+
|
|
|
+ static readonly STATE_INIT = 0;
|
|
|
+ static readonly STATE_RUNNING = 1;
|
|
|
+ static readonly STATE_STOPPED = 2;
|
|
|
+ static readonly STATE_PAUSED = 3;
|
|
|
+
|
|
|
+ constructor(app: App) {
|
|
|
+ let state;
|
|
|
+ this.app = app;
|
|
|
+ this.conversationState = (value?: number) => {
|
|
|
+ if (value !== undefined)
|
|
|
+ state = value;
|
|
|
+ return state;
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ init(success: (response) => void, fail: (err) => void) {
|
|
|
+ this.app.apiCall('/conversations', {'action': 'create'}, success, fail);
|
|
|
+ }
|
|
|
+
|
|
|
+ set state(value: number) {
|
|
|
+ if ([Conversation.STATE_INIT, Conversation.STATE_RUNNING, Conversation.STATE_PAUSED, Conversation.STATE_STOPPED].indexOf(value) === -1) {
|
|
|
+ throw new Error(`State ${value} not in list of possible values.`);
|
|
|
+ }
|
|
|
+ this.conversationState(value);
|
|
|
+ if (value === Conversation.STATE_INIT) {
|
|
|
+ this.app.conversations[this.id] = this;
|
|
|
+ this.initiator.conversations[this.id] = this;
|
|
|
+ this.recipient.conversations[this.id] = this;
|
|
|
+ }
|
|
|
+ if (value === Conversation.STATE_RUNNING) {
|
|
|
+ let lastIntervalTick = Date.now();
|
|
|
+ this.interval = setInterval(() => {
|
|
|
+ let now = Date.now();
|
|
|
+ let interval = (now - lastIntervalTick) / 1000;
|
|
|
+ this.duration += interval;
|
|
|
+ lastIntervalTick = now;
|
|
|
+ this.initiator.payedTime -= Math.ceil(interval * this.recipient.coeficient);
|
|
|
+ this.recipient.payedTime += Math.ceil(interval * this.recipient.coeficient);
|
|
|
+ if (this.initiator.payedTime < 0) {
|
|
|
+ this.state = Conversation.STATE_STOPPED;
|
|
|
+ }
|
|
|
+ }, 1000);
|
|
|
+ this.syncInterval = setInterval(() => {
|
|
|
+ this.initiator.send('chat-sync-timer', {
|
|
|
+ conversationId: this.id,
|
|
|
+ duration: this.duration
|
|
|
+ });
|
|
|
+ this.recipient.send('chat-sync-timer', {
|
|
|
+ conversationId: this.id,
|
|
|
+ duration: this.duration
|
|
|
+ });
|
|
|
+ this.app.apiCall('/conversations', {
|
|
|
+ id: this.id,
|
|
|
+ action: 'duration',
|
|
|
+ duration: this.duration
|
|
|
+ });
|
|
|
+ this.app.apiCall('/user', {
|
|
|
+ action: 'updatetime',
|
|
|
+ id: this.initiator.id,
|
|
|
+ value: this.initiator.payedTime
|
|
|
+ });
|
|
|
+ this.app.apiCall('/user', {
|
|
|
+ action: 'updatetime',
|
|
|
+ id: this.recipient.id,
|
|
|
+ value: this.recipient.payedTime
|
|
|
+ });
|
|
|
+ }, 5000);
|
|
|
+ this.app.apiCall('/conversations', {id: this.id, action: 'start'});
|
|
|
+ }
|
|
|
+ if (value === Conversation.STATE_STOPPED || value === Conversation.STATE_PAUSED) {
|
|
|
+ clearInterval(this.interval);
|
|
|
+ clearInterval(this.syncInterval);
|
|
|
+ }
|
|
|
+ if (value === Conversation.STATE_STOPPED) {
|
|
|
+ delete this.initiator.conversations[this.id];
|
|
|
+ delete this.recipient.conversations[this.id];
|
|
|
+ delete this.app.conversations[this.id];
|
|
|
+ this.app.apiCall('/conversations', {id: this.id, action: 'stop', duration: this.duration});
|
|
|
+ this.app.apiCall('/user', {
|
|
|
+ action: 'updatetime',
|
|
|
+ id: this.initiator.id,
|
|
|
+ value: this.initiator.payedTime
|
|
|
+ });
|
|
|
+ this.app.apiCall('/user', {
|
|
|
+ action: 'updatetime', id:
|
|
|
+ this.recipient.id,
|
|
|
+ value: this.recipient.payedTime
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ get state() {
|
|
|
+ return this.conversationState();
|
|
|
+ }
|
|
|
+
|
|
|
+ newMessage(message) {
|
|
|
+ this.initiator.sendMessage({message: message, duration: this.duration});
|
|
|
+ this.recipient.sendMessage({message: message, duration: this.duration});
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export interface AppConfigInterface {
|
|
|
+ host?: string;
|
|
|
+ port: number;
|
|
|
+}
|