index.js 9.45 KB
Newer Older
1 2
var express = require('express');
var app = express();
Killian Kemps's avatar
Killian Kemps committed
3
var bodyParser = require('body-parser');
Killian Kemps's avatar
Killian Kemps committed
4
var sessions = require("client-sessions");
5 6
var http = require('http').Server(app);
var io = require('socket.io')(http);
7
const fs = require('fs');
8
const path = require('path');
9 10 11
var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens

var config = require('./config-vars'); // get our config file
12 13

var login = require("facebook-chat-api");
14
var facebookMessengerService = require('./facebookMessengerService');
15

16 17
app.set('superSecret', config.secret); // secret variable

Killian Kemps's avatar
Killian Kemps committed
18 19 20
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded

21 22 23
// Set web client directory
var client_dir = __dirname + '/dist';

24
// serve the static assets from the client/ directory
25
app.use(express.static(path.join(client_dir)));
26

27
var cookieOptions = {
Killian Kemps's avatar
Killian Kemps committed
28 29 30 31
  cookieName: 'userSession', // cookie name dictates the key name added to the request object
  secret: 'reaaaalsecretblargadeeblargblarg', // should be a large unguessable string
  duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
  activeDuration: 1000 * 60 * 5 // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
32 33 34
};

app.use(sessions(cookieOptions));
Killian Kemps's avatar
Killian Kemps committed
35

36
app.get('/', function(req, res){
37
  res.sendFile(client_dir + '/index.html');
38 39
});

Killian Kemps's avatar
Killian Kemps committed
40
var chatHistory = {};
41
var users = {};
42

43
// XXX Currently dead code
Killian Kemps's avatar
Killian Kemps committed
44 45
function loginFacebookAppstate (email) {
  return new Promise(function (resolve, reject) {
46 47
    console.log('Login from appstate ', email);
    fs.exists(getAppstateName(email), function(exists) {
Killian Kemps's avatar
Killian Kemps committed
48
      if (exists) {
49
        login({appState: JSON.parse(fs.readFileSync(getAppstateName(email), 'utf8'))}, function callback (err, api) {
Killian Kemps's avatar
Killian Kemps committed
50
          if(err) return reject(err);
51
          api.setOptions({selfListen: true});
52 53 54
          users[email] = {
            ID: 0,
            fbID: 0,
55
            fbApi: api
56 57
          };
          resolve(users[email]);
Killian Kemps's avatar
Killian Kemps committed
58 59 60 61 62 63 64 65 66 67
        });
      }
      else {
        console.log('User appstate not found');
        reject('appstate_not_found');
      }
    });
  });
}

Killian Kemps's avatar
Killian Kemps committed
68
function loginFacebookCredentials (email, password) {
69
  return new Promise(function (resolve, reject) {
Killian Kemps's avatar
Killian Kemps committed
70 71 72
    console.log('Login from user credentials');
    login({email: email, password: password}, function callback (err, api) {
      if(err) return reject(err);
73
      fs.writeFileSync(getAppstateName(email), JSON.stringify(api.getAppState()));
74
      api.setOptions({selfListen: true});
75
      users[email] = {
76
        ID:0,
77
        fbID: 0,
78
        fbApi: api
79
      };
80
      resolve(users[email]);
Killian Kemps's avatar
Killian Kemps committed
81 82 83 84
    });
  });
}

85 86 87 88
function getAppstateName(email) {
  return 'appstate-' + email + '.json'
}

89
function startFacebook(decoded, users, socket) {
90
  var currentUser = users[decoded.email];
91

92 93 94 95 96 97 98
  if (currentUser) {
    (function() {
      return new Promise(function (resolve, reject) {
        currentUser.ID = currentUser.fbApi.getCurrentUserID();
        facebookMessengerService.getUserInfo(currentUser.fbApi, currentUser.ID).then(
          function(data) {
            console.log(data);
99
            currentUser.name = data.name;
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
            socket.emit('chat message', 'Logged in as ' + data.name);
            resolve(data);
          });
      });
    })().then(
    function() {
      console.log('Restoring history');
      if (Array.isArray(chatHistory[currentUser.ID])) {
        for (var index in chatHistory[currentUser.ID]) {
          socket.emit('chat message', chatHistory[currentUser.ID][index]);
        }
      }
    }).then(
    function() {
      console.log('Listening for messages...');
      currentUser.fbApi.listen(function callback(err, message) {
        if (err) return console.error(err);
        console.log(message);
        var allInfos = [];
        console.log(Object.keys(facebookMessengerService));
        allInfos.push(facebookMessengerService.getUserInfo(currentUser.fbApi, message.senderID));
        allInfos.push(facebookMessengerService.getThreadInfo(currentUser.ID, currentUser.fbApi, message.threadID));
        Promise.all(allInfos).then(function(data) {
          console.log(data);
          msgToSend = {
            body: message.body,
            conversationID: message.threadID,
127 128 129 130
            timestampDatetime: tsToTsDatetime(message.timestamp),
            attachments: message.attachments,
            senderID: message.senderID,
            senderName: data[0].name,
131
            senderImage: data[0].img,
132
            isSenderUser: message.senderID === currentUser.ID.toString()
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
          }
          socket.emit('chat message', msgToSend);
          if (Array.isArray(chatHistory[currentUser.ID])) {
            chatHistory[currentUser.ID].push(msgToSend);
          }
          else {
            chatHistory[currentUser.ID] = [];
            chatHistory[currentUser.ID].push(msgToSend);
          }
        });
      });
    }
    ).catch(function(err) {
      console.log('An error occured: ', err);
      socket.emit('err', err);
    });
  }
  else {
    socket.emit('need auth');
  }
153 154
}

155
function getFBThreadList(decoded, users, socket) {
156
  var currentUser = users[decoded.email];
157

158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
  if (currentUser) {
    (function() {
      return new Promise(function (resolve, reject) {
        currentUser.ID = currentUser.fbApi.getCurrentUserID();
        facebookMessengerService.getThreadList(currentUser.ID, currentUser.fbApi, 10).then(
          function(data) {
            socket.emit('return/threadlist', data);
            resolve(data);
          });
      });
    })().catch(function(err) {
      console.log('An error occured: ', err);
      socket.emit('err', err);
    });
  }
  else {
    socket.emit('need auth');
  }
176 177
}

178
function getFBThreadHistory(decoded, users, socket, threadID) {
179
  var currentUser = users[decoded.email];
180

181 182 183
  if (currentUser) {
    (function() {
      return new Promise(function (resolve, reject) {
184 185
        facebookMessengerService.getThreadHistory(currentUser.fbApi, currentUser.ID, threadID)
        .then(function(enhanced_data) {
186 187
          socket.emit('return/threadHistory', enhanced_data);
          resolve(enhanced_data);
188
        });
189 190 191 192 193 194 195 196 197
      });
    })().catch(function(err) {
      console.log('An error occured: ', err);
      socket.emit('err', err);
    });
  }
  else {
    socket.emit('need auth');
  }
198 199
}

200 201
// Util function to convert timestamp to timestampDatetime
function tsToTsDatetime (timestamp) {
202 203
  console.log('timestamp: ', timestamp);
  var date = new Date(parseInt(timestamp));
204
  function toDoubleDigit(time) {
205
    console.log('time: ', time);
206
    if (time < 10) {
207
      return '0' + time.toString();
208 209
    }
    else {
210
      return time.toString();
211 212
    }
  }
213
  return toDoubleDigit(date.getHours()) + ':' + toDoubleDigit(date.getMinutes());
214 215 216
}


217
io.on('connection', function(socket){
218 219
  socket.on('login', function(credentials){
    loginFacebookCredentials(credentials.email, credentials.password).then(
220
      function(data) {
221 222 223 224 225
        console.log('Finished login in with credentials');
        var user = {
          email: credentials.email
        };
        var token = jwt.sign(user, app.get('superSecret'), {
226
          expiresIn: 86400 // expires in 24 hours
227
        });
228
        startFacebook(user, users, socket);
229 230 231 232 233 234 235
        socket.emit('login ok', token);
      }
    ).catch(function(err) {
      console.log('Error while login with credentials: ', err);
      // If no appstate has been saved before
      socket.emit('login failed', err);
    });
236
  });
237

238 239
  // XXX Currently dead code
  socket.on('start facebook', function(payload){
240 241
    jwt.verify(payload.token, app.get('superSecret'), function(err, decoded) {
      if (err) {
242 243 244
        const message = 'Failed to authenticate token.';
        socket.emit('auth failed', message);
        console.log('auth failed', message);
245
      } else {
246
        console.log('Starting Facebook');
247 248 249
        startFacebook(decoded, users, socket);
      }
    });
250 251
  });

252
  socket.on('get/conversations', function(payload){
253 254
    jwt.verify(payload.token, app.get('superSecret'), function(err, decoded) {
      if (err) {
255 256 257
        const message = 'Failed to authenticate token.';
        socket.emit('auth failed', message);
        console.log('auth failed', message);
258
      } else {
259
        console.log('Getting last conversations..');
260 261 262
        getFBThreadList(decoded, users, socket);
      }
    });
263 264
  });

265
  socket.on('get/conversationHistory', function(payload){
266 267
    jwt.verify(payload.token, app.get('superSecret'), function(err, decoded) {
      if (err) {
268 269 270
        const message = 'Failed to authenticate token.';
        socket.emit('auth failed', message);
        console.log('auth failed', message);
271
      } else {
272 273
        console.log('Getting conversation history..');
        getFBThreadHistory(decoded, users, socket, payload.conversationID);
274 275
      }
    });
276 277
  });

278
  socket.on('chat message', function(payload){
279 280 281
    const token = payload.token;
    const msg = payload.msg;
    const threadID = payload.conversationID;
282 283 284 285 286
    jwt.verify(token, app.get('superSecret'), function(err, decoded) {
      if (err) {
        const message = 'Failed to authenticate token.';
        socket.emit('auth failed', message);
      } else {
287
        var currentUser = users[decoded.email];
288

289
        if (currentUser) {
290 291
          currentUser.fbApi.sendMessage(msg, threadID, function(err, messageInfo){
            if (err) return console.error(err);
292
          });
293 294 295 296
        }
        else {
          socket.emit('need auth');
        }
297 298
      }
    });
299
  });
300

301
});
302

303 304
http.listen(3000, function(){
  console.log('listening on *:3000');
305
});