Client for "server push messages"

Published on February 07, 2019

Client for "server push messages"

    Continuation of the publication "Server push messages"

    When developing the client side, there was no goal to repeat and make the design like the others.
    On the other hand, it is difficult to change and make a new arrangement of areas in the desktop version. The contact area on the left, the message on the right, the input area of ​​the new message below, to make better and more convenient is not so easy. In many modern solutions, design and design elements are the same as mobile programs. I think this is why the message input field cannot be expanded, two or more lines can be made.



    The picture shows a broken line separator, when you hover, it becomes yellow-black-striped. By moving the separator, you can adjust the height of the message field.

    Similarly, you can change the width of the contact list and the list of messages.

    Sending messages is animated, there was no animation when sending messages in other programs.

    For the background of the contact list, a gradient of gray and pink is used, a similar combination can be found in the sky as a glow.

    Users online - orange color of the name, connected rooms (groups) of black. Non-online users and disabled groups are gray. You can distinguish a room from a user in the contact list by a star name, the names of rooms begin with ✯



    Users can create new rooms (groups), add users from groups. Also, it is possible to change rooms if the editing user has an administrator flag in the list of room users.



    It is possible to send files, image files are sent immediately in the message and displayed in the message. Video files are not sent immediately, only the first frame is sent, you need to start the video to download. The remaining files are similarly downloaded only after clicking on the file in the message. The maximum file size, as well as the size of the part of the file into which large files are divided, is configured in push0k admin. To send a file, you can use the “send attachment” button, or you can simply drag the file into the message window. Similarly, the sent file from the message can be dragged into the explorer or finder folder in Mac OS. In other words, drag & drop is implemented for files.



    Messages remove html tags. But there is a markdown similar to markdown.

    * fat *
    ~Italics ~
    _ underlined _

    You can send links, but the link should be a separate message.

    Each message has buttons:

    “” quoting a message
    ➦ sending a message



    A button with a lock is implemented to securely connect to the server. The logic is similar to browsers - an insecure certificate, expired, self-signed or from a different domain name, the lock is open - the connection is insecure. The button with the lock opens the certificate information.



    All server users are visible to each other, that is, each user's contacts are other users. Users can rename their contacts. It is possible to block notifications of new messages from the contact, you can also completely block messages from the contact.



    The messages do not send emoticons. No video and audio connection. There is no possibility of displaying the user's screen. In the future, some of the above must be implemented.

    Download push0k client:

    windows
    Mac OS

    Application made on electron using vue.js. The application is free, but unlike the server described in the first part of the article, I don’t plan to open the source code.

    Connection example


    Connection consists of three parts:

    1. Connection setup.

      For a secure connection, this is handshake messaging.
    2. Authorization

      When authorizing, in addition to the login, the hash from the password hash + connection id is transmitted.

      In the authorization message, the computer parameters of the OS type, OS version, processor, memory, computer name are transmitted. These reference data are necessary for statistics and understanding how the type of OS, processor and memory affects the connection speed, data exchange, etc.

      The first versions of the program were made and worked from 1c. Usually, not one 1s database can be run on the same computer, but several, for example, ZUP and PSU from an accountant. If the solution is used to support bookkeeping, it is important to understand from which database the accountant’s message was sent. The authorization also transfers the database data.
    3. Sync data.

      After receiving a successful authorization message, a data request message with a date is sent to the server. The largest date of the last message or last change of a user or room (group). By the transmitted date, new messages or modified users, rooms (groups) are determined. The date can be empty, then all messages are received by users and rooms, this can be the first time you connect.

      After receiving the synchronization data, a confirmation message is received to the server, as well as the calculated time of “establishing a connection”, “authorization”, and “data synchronization”.

    Connect socket.io to html page.


    For node.js

    • open console
    • go to the project folder command `cd / yourCatalog`
    • execute the installation command `npm install socket.io`

    Example code node.js:

    const io = require('socket.io-client');
    const crypto = require('crypto');
    const os = require('os');
    const cpusarray = os.cpus();
    let actiontime = 0;
    let contime = 0;
    let auftime = 0;
    let datasintime = 0;
    let socket;
    let lastdatesync = new Date(0).toISOString();
    let usernumber = '+7 (999) 777-77-77';
    let pw = 'somePassword';
    let baseref = process.cwd();
    let basename = 'push0k client';
    let baseid = crypto
      .createHash('md5')
      .update(appdirectory)
      .digest('hex');
    baseid =
      baseid.substring(0, 8) + '-' + 
      baseid.substring(8, 12) + '-' + 
      baseid.substring(12, 16) + '-' + 
      baseid.substring(16, 20) + '-' + 
      baseid.substring(20, 32);
    let basever = '19.02';
    let clientid = crypto
      .createHash('md5')
      .update(os.hostname())
      .digest('hex');
    clientid =
      clientid.substring(0, 8) + '-' +
      clientid.substring(8, 12) + '-' +
      clientid.substring(12, 16) + '-' +
      clientid.substring(16, 20) + '-' +
      clientid.substring(20, 32);
    let syncdata = '';
    let syncdatasize = 0;
    function sha256(p) {
      const hash = crypto.createHash('sha256');
      hash.update(p);
      return '' + hash.digest('hex');
    }
    function connect() {
      socket = io('http://yourServer.com:6789', { transports: ['websocket'], timeout: 5000 });
      socket.connect();
      socket.on('connect', onconnect);
      socket.on('message', onmessage);
      actiontime = new Date().getTime();
    }
    function onconnect() {
      contime = new Date().getTime() - actiontime;
      usernumber = usernumber.replace(/\D/g, '');
      socket
        .binary(false)
        .emit(
          'message',
          '{"event":"auf","user":"' +
            usernumber +
            '","password":"' +
            sha256(pw + socket.id) +
            '","roomsjoin":true,"basename":"' +
            basename +
            '","basever":"' +
            basever +
            '","baseid":"' +
            baseid +
            '","baseref":"' +
            encodeURIComponent(baseref) +
            '","osversion":"' +
            encodeURIComponent(os.release()) +
            '","appversion":"18.08","clientid":"' +
            clientid +
            '","infappview":"","ram":"' +
            os.totalmem() / 1024 / 1024 +
            '","proc":"' +
            encodeURIComponent(cpusarray[0].model) +
            '","ostype":"' +
            encodeURIComponent(os.type() + ' ' + os.arch()) +
            '","compname":"' +
            encodeURIComponent(os.hostname()) +
            '"}'
    	);
      // Настройки передачи файлов с сервера
      let filetranfer = msgdata.filetranfer;
      let filemaxsize = msgdata.filemaxsize;
      let filepartsize = msgdata.filepartsize;	
      actiontime = new Date().getTime();
    }
    function onmessage(msg) {
      let msgdata;
      let mestime = new Date().getTime();
      if (typeof msg === 'string') {
        try {
          msgdata = JSON.parse(msg);
        } catch (err) {
          this.message = err.toString();
          return;
        }
      } else if (typeof msg === 'object') {
        msgdata = msg;
      }
      if (msgdata.event === 'connected') {
        auftime = mestime - actiontime;
        socket
          .binary(false)
          .emit(
            'message',
            '{"event":"getData","userid":"' +
              msgdata.userid +
              '","id":"' +
              msgdata.id +
              '","baseid":"' +
              baseid +
              '","clientid":"' +
              clientid +
              '","lastdatesinc":"' +
              lastdatesync +
              '"}'
          );
        if (msgdata.setpass === 'true') {
          // временный пароль должен быть изменен пользователем
          // в модальном диалоге без возможности отказа
          // openDialogSetNewPassword();
        }
        actiontime = new Date().getTime();
      } else if (msgdata.event === 'datasync') {
        syncdata += msgdata.data;
        syncdatasize += Buffer.byteLength(msg, 'utf8');
        if (msgdata.dataPart < msgdata.partsCount) {
          return;
        }
        datasintime = mestime - actiontime;
        socket
          .binary(false)
          .emit(
            'message',
            '{"event":"dataConfirm","userid":"' +
              msgdata.userid +
              '","dataid":"' +
              msgdata.dataid +
              '","baseid":"' +
              baseid +
              '","contime":' +
              contime +
              ',"auftime":' +
              auftime +
              ',"datasintime":' +
              datasintime +
              ',"datesync":"' +
              msgdata.datesync +
              '","datasize":' +
              syncdatasize +
              '}'
          );
        contime = 0;
        datasintime = 0;
        auftime = 0;
        syncdatasize = 0;
        let datasync = JSON.parse(Buffer.from(syncdata, 'base64').toString('utf8'));
        syncdata = '';
        // обработка полученных данных с сервера
        // datasync.Users массив пользователей
        // datasync.Rooms массив комнат
        // datasync.Cons массив подключений пользователей
        // datasync.joinedRooms массив подключенных комнат
        // datasync.Mess массив сообщений
      }
    }