2020-03-16 18:41:56 +00:00
"use strict" ;
function _typeof ( obj ) { "@babel/helpers - typeof" ; if ( typeof Symbol === "function" && typeof Symbol . iterator === "symbol" ) { _typeof = function _typeof ( obj ) { return typeof obj ; } ; } else { _typeof = function _typeof ( obj ) { return obj && typeof Symbol === "function" && obj . constructor === Symbol && obj !== Symbol . prototype ? "symbol" : typeof obj ; } ; } return _typeof ( obj ) ; }
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports [ "default" ] = void 0 ;
var fs = _interopRequireWildcard ( require ( "fs" ) ) ;
var os = _interopRequireWildcard ( require ( "os" ) ) ;
var path = _interopRequireWildcard ( require ( "path" ) ) ;
var util = _interopRequireWildcard ( require ( "util" ) ) ;
var _nodeLocalstorage = require ( "node-localstorage" ) ;
var matrix = _interopRequireWildcard ( require ( "matrix-js-sdk" ) ) ;
var _logger = _interopRequireDefault ( require ( "./logger" ) ) ;
function _interopRequireDefault ( obj ) { return obj && obj . _ _esModule ? obj : { "default" : obj } ; }
function _getRequireWildcardCache ( ) { if ( typeof WeakMap !== "function" ) return null ; var cache = new WeakMap ( ) ; _getRequireWildcardCache = function _getRequireWildcardCache ( ) { return cache ; } ; return cache ; }
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } if ( obj === null || _typeof ( obj ) !== "object" && typeof obj !== "function" ) { return { "default" : obj } ; } var cache = _getRequireWildcardCache ( ) ; if ( cache && cache . has ( obj ) ) { return cache . get ( obj ) ; } var newObj = { } ; var hasPropertyDescriptor = Object . defineProperty && Object . getOwnPropertyDescriptor ; for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) { var desc = hasPropertyDescriptor ? Object . getOwnPropertyDescriptor ( obj , key ) : null ; if ( desc && ( desc . get || desc . set ) ) { Object . defineProperty ( newObj , key , desc ) ; } else { newObj [ key ] = obj [ key ] ; } } } newObj [ "default" ] = obj ; if ( cache ) { cache . set ( obj , newObj ) ; } return newObj ; }
function _classCallCheck ( instance , Constructor ) { if ( ! ( instance instanceof Constructor ) ) { throw new TypeError ( "Cannot call a class as a function" ) ; } }
function _defineProperties ( target , props ) { for ( var i = 0 ; i < props . length ; i ++ ) { var descriptor = props [ i ] ; descriptor . enumerable = descriptor . enumerable || false ; descriptor . configurable = true ; if ( "value" in descriptor ) descriptor . writable = true ; Object . defineProperty ( target , descriptor . key , descriptor ) ; } }
function _createClass ( Constructor , protoProps , staticProps ) { if ( protoProps ) _defineProperties ( Constructor . prototype , protoProps ) ; if ( staticProps ) _defineProperties ( Constructor , staticProps ) ; return Constructor ; }
global . Olm = require ( "olm" ) ;
var ENCRYPTION _CONFIG = {
algorithm : "m.megolm.v1.aes-sha2"
} ;
var KICK _REASON = "A facilitator has already joined this chat." ;
var BOT _ERROR _MESSAGE = "Something went wrong on our end, please restart the chat and try again." ;
var MAX _RETRIES = 3 ;
var OcrccBot =
/*#__PURE__*/
function ( ) {
function OcrccBot ( ) {
_classCallCheck ( this , OcrccBot ) ;
this . awaitingFacilitator = { } ;
this . client = matrix . createClient ( process . env . MATRIX _SERVER _URL ) ;
this . joinedRooms = [ ] ;
this . activeChatrooms = { } ;
}
_createClass ( OcrccBot , [ {
key : "createLocalStorage" ,
value : function createLocalStorage ( ) {
var storageLoc = "matrix-chatbot-" . concat ( process . env . BOT _USERNAME ) ;
var dir = path . resolve ( path . join ( os . homedir ( ) , ".local-storage" ) ) ;
if ( ! fs . existsSync ( dir ) ) {
fs . mkdirSync ( dir ) ;
}
var localStoragePath = path . resolve ( path . join ( dir , storageLoc ) ) ;
return new _nodeLocalstorage . LocalStorage ( localStoragePath ) ;
}
} , {
key : "sendMessage" ,
value : function sendMessage ( roomId , msgText ) {
var _this = this ;
return this . client . sendTextMessage ( roomId , msgText ) [ "catch" ] ( function ( err ) {
switch ( err [ "name" ] ) {
case "UnknownDeviceError" :
Object . keys ( err . devices ) . forEach ( function ( userId ) {
Object . keys ( err . devices [ userId ] ) . map ( function ( deviceId ) {
_this . client . setDeviceVerified ( userId , deviceId , true ) ;
} ) ;
} ) ;
return _this . sendMessage ( roomId , msgText ) ;
break ;
default :
_logger [ "default" ] . log ( "error" , "ERROR SENDING MESSAGE: " . concat ( err ) ) ;
_this . handleBotCrash ( roomId , err ) ;
break ;
}
} ) ;
}
} , {
key : "inviteUserToRoom" ,
value : function inviteUserToRoom ( client , roomId , member ) {
var _this2 = this ;
var retries = arguments . length > 3 && arguments [ 3 ] !== undefined ? arguments [ 3 ] : 0 ;
_logger [ "default" ] . log ( "info" , "INVITING MEMBER: " + member ) ;
if ( retries > MAX _RETRIES ) {
this . handleBotCrash ( roomId , "Rate limit exceeded for bot account" ) ;
return _logger [ "default" ] . log ( "error" , "RATE LIMIT EXCEEDED AND RETRY LIMIT EXCEEDED" ) ;
}
return client . invite ( roomId , member ) [ "catch" ] ( function ( err ) {
switch ( err [ "name" ] ) {
case "M_LIMIT_EXCEEDED" :
_logger [ "default" ] . log ( "info" , "Rate limit exceeded, retrying." ) ;
var retryCount = retries + 1 ;
var delay = retryCount * 2 * 1000 ;
return setTimeout ( _this2 . inviteUserToRoom , delay , client , roomId , member , retryCount ) ;
break ;
default :
_logger [ "default" ] . log ( "error" , "ERROR INVITING MEMBER: " . concat ( err ) ) ;
_this2 . handleBotCrash ( roomId , err ) ;
break ;
}
} ) ;
}
} , {
key : "kickUserFromRoom" ,
value : function kickUserFromRoom ( client , roomId , member ) {
var _this3 = this ;
var retries = arguments . length > 3 && arguments [ 3 ] !== undefined ? arguments [ 3 ] : 0 ;
_logger [ "default" ] . log ( "info" , "KICKING OUT MEMBER: " + member ) ;
if ( retries > MAX _RETRIES ) {
this . handleBotCrash ( roomId , "Rate limit exceeded for bot account." ) ;
return _logger [ "default" ] . log ( "error" , "RATE LIMIT EXCEEDED AND RETRY LIMIT EXCEEDED" ) ;
}
return client . kick ( roomId , member , KICK _REASON ) [ "catch" ] ( function ( err ) {
switch ( err [ "name" ] ) {
case "M_LIMIT_EXCEEDED" :
_logger [ "default" ] . log ( "info" , "Rate limit exceeded, retrying." ) ;
var retryCount = retries + 1 ;
var delay = retryCount * 2 * 1000 ;
return setTimeout ( _this3 . kickUserFromRoom , delay , client , roomId , member , retryCount ) ;
break ;
default :
_this3 . handleBotCrash ( roomId , err ) ;
_logger [ "default" ] . log ( "error" , "ERROR KICKING OUT MEMBER: " . concat ( err ) ) ;
break ;
}
} ) ;
}
} , {
key : "inviteFacilitators" ,
value : function inviteFacilitators ( roomId ) {
var _this4 = this ;
this . awaitingFacilitator [ roomId ] = true ;
var chatOffline = true ;
this . client . getJoinedRoomMembers ( process . env . FACILITATOR _ROOM _ID ) . then ( function ( members ) {
var onlineMembersCount = 0 ;
Object . keys ( members [ "joined" ] ) . forEach ( function ( member ) {
var user = _this4 . client . getUser ( member ) ;
if ( user . presence === "online" && member !== process . env . BOT _USERID ) {
chatOffline = false ;
_this4 . inviteUserToRoom ( _this4 . client , roomId , member ) ;
}
} ) ;
} ) . then ( function ( ) {
if ( chatOffline ) {
_this4 . sendMessage ( roomId , process . env . CHAT _OFFLINE _MESSAGE ) ;
}
} ) [ "catch" ] ( function ( err ) {
_this4 . handleBotCrash ( roomId , err ) ;
_logger [ "default" ] . log ( "error" , "ERROR GETTING ROOM MEMBERS: " . concat ( err ) ) ;
} ) ;
}
} , {
key : "uninviteFacilitators" ,
value : function uninviteFacilitators ( roomId ) {
var _this5 = this ;
this . awaitingFacilitator [ roomId ] = false ;
this . client . getJoinedRoomMembers ( process . env . FACILITATOR _ROOM _ID ) . then ( function ( allFacilitators ) {
_this5 . client . getJoinedRoomMembers ( roomId ) . then ( function ( roomMembers ) {
var membersIds = Object . keys ( roomMembers [ "joined" ] ) ;
var facilitatorsIds = Object . keys ( allFacilitators [ "joined" ] ) ;
facilitatorsIds . forEach ( function ( f ) {
if ( ! membersIds . includes ( f ) ) {
_this5 . kickUserFromRoom ( _this5 . client , roomId , f ) ;
}
} ) ;
} ) ;
} ) [ "catch" ] ( function ( err ) {
_this5 . handleBotCrash ( roomId , err ) ;
_logger [ "default" ] . log ( "error" , err ) ;
} ) ;
}
} , {
key : "handleBotCrash" ,
value : function handleBotCrash ( roomId , error ) {
if ( roomId ) {
this . sendMessage ( roomId , BOT _ERROR _MESSAGE ) ;
}
this . sendMessage ( process . env . FACILITATOR _ROOM _ID , "The Help Bot ran into an error: " . concat ( error , ". Please verify that the chat service is working." ) ) ;
}
} , {
key : "writeToTranscript" ,
value : function writeToTranscript ( event ) {
try {
var sender = event . getSender ( ) ;
var roomId = event . getRoomId ( ) ;
var content = event . getContent ( ) ;
var date = event . getDate ( ) ;
var time = date . toLocaleTimeString ( "en-GB" , {
timeZone : "America/New_York"
} ) ;
var filepath = this . activeChatrooms [ roomId ] . transcriptFile ;
if ( ! content ) {
return ;
}
var message = "" . concat ( sender , " [" ) . concat ( time , "]: " ) . concat ( content . body , "\n" ) ;
fs . appendFileSync ( filepath , message , "utf8" ) ;
} catch ( err ) {
_logger [ "default" ] . log ( "error" , "ERROR APPENDING TO TRANSCRIPT FILE: " . concat ( err ) ) ;
}
}
} , {
key : "deleteOldDevices" ,
value : function deleteOldDevices ( ) {
var _this6 = this ;
2020-03-18 07:00:43 +00:00
var doDelete = function doDelete ( oldDevices ) {
var auth = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : null ;
var retries = arguments . length > 2 && arguments [ 2 ] !== undefined ? arguments [ 2 ] : 0 ;
if ( retries > MAX _RETRIES ) {
throw new Error ( "Exceeded max retries deleting old devices" ) ;
}
_logger [ "default" ] . log ( "info" , "ATTEMPTING TO DELETE OLD DEVICES: " . concat ( oldDevices ) ) ;
_this6 . client . deleteMultipleDevices ( oldDevices , auth ) . then ( function ( ) {
return _logger [ "default" ] . log ( "info" , "DELETED OLD DEVICES" ) ;
} ) [ "catch" ] ( function ( err ) {
if ( err [ 'errcode' ] === undefined && err [ 'data' ] ) {
var _auth = {
session : err . data . session ,
type : "m.login.password" ,
user : process . env . BOT _USERID ,
identifier : {
type : "m.id.user" ,
user : process . env . BOT _USERID
} ,
password : process . env . BOT _PASSWORD
} ;
doDelete ( oldDevices , _auth ) ;
} else if ( err [ 'errcode' ] === 'M_LIMIT_EXCEEDED' ) {
var retryCount = retries + 1 ;
var delay = err [ 'retry_after_ms' ] ? err [ 'retry_after_ms' ] : retryCount * 1000 ;
_logger [ "default" ] . log ( "error" , "RATE LIMIT EXCEEDED, RETRYING IN " . concat ( delay , " MS" ) ) ;
setTimeout ( function ( ) {
doDelete ( oldDevices , auth , retryCount ) ;
} , delay ) ;
} else {
_logger [ "default" ] . log ( "error" , "ERROR DELETING OLD DEVICES ON RETRY " . concat ( retries , ": " ) . concat ( JSON . stringify ( err ) ) ) ;
doDelete ( oldDevices , auth , retries + 1 ) ;
}
} ) ;
} ;
2020-03-16 18:41:56 +00:00
return this . client . getDevices ( ) . then ( function ( data ) {
var currentDeviceId = _this6 . client . getDeviceId ( ) ;
var allDeviceIds = data . devices . map ( function ( d ) {
return d . device _id ;
} ) ;
var oldDevices = allDeviceIds . filter ( function ( id ) {
return id !== currentDeviceId ;
} ) ;
2020-03-18 07:00:43 +00:00
doDelete ( oldDevices ) ;
} ) [ "catch" ] ( function ( err ) {
_this6 . handleBotCrash ( undefined , err ) ;
2020-03-16 18:41:56 +00:00
2020-03-18 07:00:43 +00:00
_logger [ "default" ] . log ( "error" , "ERROR DELETING OLD DEVICES: " . concat ( JSON . stringify ( err . data ) ) ) ;
2020-03-16 18:41:56 +00:00
} ) ;
2020-03-18 07:00:43 +00:00
} // deleteOldDevices() {
// return this.client.getDevices().then(data => {
// const currentDeviceId = this.client.getDeviceId();
// const allDeviceIds = data.devices.map(d => d.device_id);
// const oldDevices = allDeviceIds.filter(id => id !== currentDeviceId);
// logger.log("info", `DELETING OLD DEVICES: ${oldDevices}`);
// this.client
// .deleteMultipleDevices(oldDevices)
// .catch(err => {
// const auth = {
// session: err.data.session,
// type: "m.login.password",
// user: process.env.BOT_USERID,
// identifier: { type: "m.id.user", user: process.env.BOT_USERID },
// password: process.env.BOT_PASSWORD
// };
// this.client
// .deleteMultipleDevices(oldDevices, auth)
// .then(() => logger.log("info", "DELETED OLD DEVICES"))
// .catch(err =>
// logger.log(
// "error",
// `ERROR DELETING OLD DEVICES: ${JSON.stringify(err.data)}`
// )
// );
// });
// });
// }
2020-03-16 18:41:56 +00:00
} , {
key : "setMembershipListeners" ,
value : function setMembershipListeners ( ) {
var _this7 = this ;
// Automatically accept all room invitations
return this . client . on ( "RoomMember.membership" , function ( event , member ) {
if ( member . membership === "invite" && member . userId === process . env . BOT _USERID && ! _this7 . joinedRooms . includes ( member . roomId ) ) {
_logger [ "default" ] . log ( "info" , "Auto-joining room " + member . roomId ) ;
_this7 . client . joinRoom ( member . roomId ) . then ( function ( room ) {
_this7 . sendMessage ( process . env . FACILITATOR _ROOM _ID , "A support seeker requested a chat (Room ID: " . concat ( member . roomId , ")" ) ) ;
} ) . then ( function ( ) {
return _this7 . inviteFacilitators ( member . roomId ) ;
} ) [ "catch" ] ( function ( err ) {
_logger [ "default" ] . log ( "error" , err ) ;
} ) ;
} // When a facilitator joins a support session, revoke the other invitations
if ( member . membership === "join" && member . userId !== process . env . BOT _USERID && _this7 . awaitingFacilitator [ member . roomId ] ) {
_this7 . activeChatrooms [ member . roomId ] = {
facilitator : member . userId
} ;
_this7 . sendMessage ( member . roomId , "" . concat ( member . name , " has joined the chat." ) ) ;
_this7 . sendMessage ( process . env . FACILITATOR _ROOM _ID , "" . concat ( member . name , " joined the chat (Room ID: " ) . concat ( member . roomId , ")" ) ) ;
_this7 . uninviteFacilitators ( member . roomId ) ;
if ( process . env . CAPTURE _TRANSCRIPTS ) {
var currentDate = new Date ( ) ;
var dateOpts = {
year : "numeric" ,
month : "short" ,
day : "numeric"
} ;
var chatDate = currentDate . toLocaleDateString ( "en-GB" , dateOpts ) ;
var chatTime = currentDate . toLocaleTimeString ( "en-GB" , {
timeZone : "America/New_York"
} ) ;
var filename = "" . concat ( chatDate , " - " ) . concat ( chatTime , " - " ) . concat ( member . roomId , ".txt" ) ;
var filepath = path . resolve ( path . join ( "transcripts" , filename ) ) ;
_this7 . activeChatrooms [ member . roomId ] . transcriptFile = filepath ;
}
}
if ( member . membership === "leave" && member . userId !== process . env . BOT _USERID && _this7 . activeChatrooms [ member . roomId ] && member . userId === _this7 . activeChatrooms [ member . roomId ] . facilitator ) {
_this7 . sendMessage ( member . roomId , "" . concat ( member . name , " has left the chat." ) ) ;
}
} ) ;
}
} , {
key : "setMessageListeners" ,
value : function setMessageListeners ( ) {
var _this8 = this ;
// encrypted messages
this . client . on ( "Event.decrypted" , function ( event , err ) {
if ( err ) {
return _logger [ "default" ] . log ( "error" , "ERROR DECRYPTING EVENT: " . concat ( err ) ) ;
}
if ( event . getType ( ) === "m.room.message" ) {
_this8 . writeToTranscript ( event ) ;
}
} ) ; // unencrypted messages
this . client . on ( "Room.timeline" , function ( event , room , toStartOfTimeline ) {
if ( event . getType ( ) === "m.room.message" && ! _this8 . client . isCryptoEnabled ( ) ) {
if ( event . isEncrypted ( ) ) {
return ;
}
_this8 . writeToTranscript ( event ) ;
}
} ) ;
}
} , {
key : "start" ,
value : function start ( ) {
var _this9 = this ;
var localStorage = this . createLocalStorage ( ) ;
this . client . login ( "m.login.password" , {
user : process . env . BOT _USERNAME ,
password : process . env . BOT _PASSWORD ,
initial _device _display _name : process . env . BOT _DISPLAY _NAME
} ) . then ( function ( data ) {
var accessToken = data . access _token ;
var deviceId = data . device _id ; // create new client with full options
var opts = {
baseUrl : process . env . MATRIX _SERVER _URL ,
accessToken : accessToken ,
userId : process . env . BOT _USERID ,
deviceId : deviceId ,
sessionStore : new matrix . WebStorageSessionStore ( localStorage )
} ;
_this9 . client = matrix . createClient ( opts ) ;
} ) [ "catch" ] ( function ( err ) {
_logger [ "default" ] . log ( "error" , "ERROR WITH LOGIN: " . concat ( err ) ) ;
} ) . then ( function ( ) {
return _this9 . deleteOldDevices ( ) ;
} ) . then ( function ( ) {
return _this9 . client . initCrypto ( ) ;
} ) [ "catch" ] ( function ( err ) {
return _logger [ "default" ] . log ( "error" , "ERROR STARTING CRYPTO: " . concat ( err ) ) ;
} ) . then ( function ( ) {
return _this9 . client . getJoinedRooms ( ) . then ( function ( data ) {
_this9 . joinedRooms = data [ "joined_rooms" ] ;
} ) ;
} ) . then ( function ( ) {
_this9 . setMembershipListeners ( ) ;
if ( process . env . CAPTURE _TRANSCRIPTS ) {
_this9 . setMessageListeners ( ) ;
}
} ) . then ( function ( ) {
return _this9 . client . startClient ( {
initialSyncLimit : 0
} ) ;
} ) [ "catch" ] ( function ( err ) {
_this9 . handleBotCrash ( undefined , err ) ;
_logger [ "default" ] . log ( "error" , "ERROR INITIALIZING CLIENT: " . concat ( err ) ) ;
} ) ;
}
} ] ) ;
return OcrccBot ;
} ( ) ;
var _default = OcrccBot ;
exports [ "default" ] = _default ;