@ -11,7 +11,7 @@
console . log ( "Starting up..." ) ;
import * as sdk from 'matrix-js-sdk' ;
import matrixcs from 'matrix-js-sdk'
import matrixcs from 'matrix-js-sdk' ;
import fs from 'fs' ;
import log4js from 'log4js' ;
import https from 'https' ;
@ -21,20 +21,34 @@ import request from 'request';
import FileType from 'file-type' ;
import express from 'express' ;
import bodyParser from 'body-parser' ;
// Load settings from config.json
const _ _filename = fileURLToPath ( import . meta . url ) ;
const _ _dirname = dirname ( _ _filename ) ;
let rawdata = fs . readFileSync ( _ _dirname + '/config.json' ) ;
let settings = JSON . parse ( rawdata ) ;
console . log ( _ _dirname + "/config.json loaded." ) ;
var logger = log4js . getLogger ( ) ;
logger . level = settings . loglevel ;
logger . info ( "Log initialized." ) ;
var initialsynccomplete = false ;
// Save script start time for ignoring older messages
var boottimestamp = Date . now ( ) ;
var _ _dirname ;
var _ _filename ;
var initialsynccomplete = false ;
// Init logging
var logger = log4js . getLogger ( ) ;
logger . level = "debug" ;
logger . info ( "Log initialized." ) ;
var settings = { } ;
/ * *
* Load settings from config . json
* /
function loadSettingsFile ( ) {
_ _filename = fileURLToPath ( import . meta . url ) ;
_ _dirname = dirname ( _ _filename ) ;
let rawdata = fs . readFileSync ( _ _dirname + '/config.json' ) ;
settings = JSON . parse ( rawdata ) ;
console . log ( _ _dirname + "/config.json loaded." ) ;
logger . level = settings . loglevel ;
}
loadSettingsFile ( ) ;
// https://github.com/matrix-org/matrix-js-sdk/issues/2415#issuecomment-1188812401
matrixcs . request ( request ) ;
@ -465,138 +479,231 @@ function handleHTTPRequest(req, res) {
}
}
const client = sdk . createClient ( { baseUrl : settings . homeserver , userId : settings . matrixuser , accessToken : settings . matrixaccesstoken } ) ;
var httpserver = express ( ) ;
var jsonParser = bodyParser . json ( ) ;
client . startClient ( { initialSyncLimit : 10 } ) ;
logger . info ( "Plugged into the matrix." ) ;
client . once ( 'sync' , function ( state , prevState , res ) {
logger . debug ( "Initial sync complete (" + state + ")" ) ;
initialsynccomplete = true ;
httpserver . post ( "*" , jsonParser , ( req , res ) => {
handleHTTPRequest ( req , res ) ;
} ) ;
httpserver . get ( "*" , ( req , res ) => {
handleHTTPRequest ( req , res ) ;
} ) ;
httpserver . listen ( settings . listenport , ( ) => {
logger . info ( "HTTP server listening on port " + settings . listenport ) ;
} ) ;
logger . info ( "Up and running." ) ;
if ( settings . smsinterval > 0 ) {
setInterval ( checkSMS , settings . smsinterval * 1000 ) ;
checkSMS ( ) ;
// Access token empty
if ( settings . matrixaccesstoken == false ) {
logger . error ( "Matrix access token not set." ) ;
if ( settings . matrixuser == false || settings . matrixpass == false ) {
logger . error ( "Config values for matrixuser and/or matrixpass are not valid." ) ;
process . exit ( 1 ) ;
}
} ) ;
client . on ( "Room.timeline" , function ( event , room ) {
logger . error ( "Attempting to fetch access token for you..." ) ;
try {
if ( ! initialsynccomplete ) {
return ; // ignore anything while we were offline
}
if ( event . getType ( ) !== "m.room.message" ) {
return ; // only use messages
}
if ( client . getUserId ( ) == event . getSender ( ) ) {
return ; // skip own messages to prevent loop
}
if ( event . getTs ( ) < ( Date . now ( ) - 1000 * 60 * 60 * 8 ) ) {
// Ignore old events (8 hrs), they're probably duplicates or something.
logger . warn ( "Ignoring stale Matrix room event [" + event . getId ( ) + "]: older than 8 hours" ) ;
return ;
}
if ( event . getTs ( ) < boottimestamp ) {
logger . warn ( "Ignoring stale Matrix room event [" + event . getId ( ) + "]: event predates start of this program" ) ;
return ;
}
if ( event . getContent ( ) == null || typeof event . getContent ( ) . body == "undefined" || event . getContent ( ) . body == null ) {
// Apparently this can happen?
return ;
request ( {
method : "POST" ,
uri : settings . homeserver + "/_matrix/client/v3/login" ,
json : {
type : "m.login.password" ,
user : settings . matrixuser ,
password : settings . matrixpass
}
} , function ( error , resp , body ) {
if ( ! error && resp . statusCode == 200 ) {
if ( body . access _token ) {
logger . error ( "Got access token for you. Writing it to " + _ _dirname + "/config.json now." ) ;
settings . matrixaccesstoken = body . access _token ;
fs . writeFileSync ( _ _dirname + '/config.json' , JSON . stringify ( settings , null , 4 ) ) ;
logger . error ( "Exiting. Please restart me to load new config file." ) ;
process . exit ( ) ;
}
} else {
logger . error ( "Couldn't get access token. Get it yourself: curl -XPOST -d '{\"type\":\"m.login.password\", \"user\":\"" + settings . matrixuser + "\", \"password\":\"password here\"}' \"https://matrix.netsyms.net/_matrix/client/v3/login\"" ) ;
}
} ) ;
} catch ( ex ) {
logger . error ( "Couldn't get access token. Get it yourself: curl -XPOST -d '{\"type\":\"m.login.password\", \"user\":\"" + settings . matrixuser + "\", \"password\":\"password here\"}' \"https://matrix.netsyms.net/_matrix/client/v3/login\"" ) ;
}
} else {
const client = sdk . createClient ( { baseUrl : settings . homeserver , userId : settings . matrixuser , accessToken : settings . matrixaccesstoken } ) ;
var httpserver = express ( ) ;
var jsonParser = bodyParser . json ( ) ;
client . startClient ( { initialSyncLimit : 10 } ) ;
logger . info ( "Plugged into the matrix." ) ;
client . once ( 'sync' , function ( state , prevState , res ) {
logger . debug ( "Initial sync complete (" + state + ")" ) ;
initialsynccomplete = true ;
httpserver . post ( "*" , jsonParser , ( req , res ) => {
handleHTTPRequest ( req , res ) ;
} ) ;
httpserver . get ( "*" , ( req , res ) => {
handleHTTPRequest ( req , res ) ;
} ) ;
httpserver . listen ( settings . listenport , ( ) => {
logger . info ( "HTTP server listening on port " + settings . listenport ) ;
} ) ;
logger . info ( "Up and running." ) ;
if ( settings . smsinterval > 0 ) {
setInterval ( checkSMS , settings . smsinterval * 1000 ) ;
checkSMS ( ) ;
}
logger . debug ( "Got room message (event ID " + event . getId ( ) + ")" ) ;
if ( event . getContent ( ) . body . toLowerCase ( ) . startsWith ( "!sms" ) ) {
// capture command to start room for new number
const matches = event . getContent ( ) . body . match ( /([1-9]?[0-9]{10})/g ) ;
if ( matches == null ) {
} ) ;
client . on ( "Room.timeline" , function ( event , room ) {
try {
if ( ! initialsynccomplete ) {
return ; // ignore anything while we were offline
}
if ( event . getType ( ) !== "m.room.message" ) {
return ; // only use messages
}
if ( client . getUserId ( ) == event . getSender ( ) ) {
return ; // skip own messages to prevent loop
}
if ( event . getTs ( ) < ( Date . now ( ) - 1000 * 60 * 60 * 8 ) ) {
// Ignore old events (8 hrs), they're probably duplicates or something.
logger . warn ( "Ignoring stale Matrix room event [" + event . getId ( ) + "]: older than 8 hours" ) ;
return ;
}
if ( event . getTs ( ) < boottimestamp ) {
logger . warn ( "Ignoring stale Matrix room event [" + event . getId ( ) + "]: event predates start of this program" ) ;
return ;
}
if ( matches . length == 1 || matches . length == 2 ) {
var tel = matches [ 0 ] ;
var ournumber = settings . smsfrom ;
if ( tel . length == 10 ) {
// make it the full number
tel = "1" + tel ;
if ( event . getContent ( ) == null || typeof event . getContent ( ) . body == "undefined" || event . getContent ( ) . body == null ) {
// Apparently this can happen?
return ;
}
logger . debug ( "Got room message (event ID " + event . getId ( ) + ")" ) ;
if ( event . getContent ( ) . body . toLowerCase ( ) . startsWith ( "!sms" ) ) {
// capture command to start room for new number
const matches = event . getContent ( ) . body . match ( /([1-9]?[0-9]{10})/g ) ;
if ( matches == null ) {
return ;
}
if ( matches . length == 2 ) {
ournumber = matches [ 1 ] ;
if ( ournumber . length == 10 ) {
if ( matches . length == 1 || matches . length == 2 ) {
var tel = matches [ 0 ] ;
var ournumber = settings . smsfrom ;
if ( tel . length == 10 ) {
// make it the full number
ournumber = "1" + ournumber ;
tel = "1" + tel ;
}
if ( matches . length == 2 ) {
ournumber = matches [ 1 ] ;
if ( ournumber . length == 10 ) {
// make it the full number
ournumber = "1" + ournumber ;
}
}
logger . info ( "Got request to start new SMS conversation with " + tel + " using " + ournumber + " from " + event . getSender ( ) + "." ) ;
sendMatrixNotice ( event . getRoomId ( ) , "Starting conversation with " + tel ) ;
createOrJoinSMSRoom ( tel , ournumber , function ( roomid ) {
//client.setRoomTag(roomid, "u.matrix-bridge-voxtelesys-sms", {tel: tel, ournumber: ournumber});
} ) ;
}
logger . info ( "Got request to start new SMS conversation with " + tel + " using " + ournumber + " from " + event . getSender ( ) + "." ) ;
sendMatrixNotice ( event . getRoomId ( ) , "Starting conversation with " + tel ) ;
createOrJoinSMSRoom ( tel , ournumber , function ( roomid ) {
//client.setRoomTag(roomid, "u.matrix-bridge-voxtelesys-sms", {tel: tel, ournumber: ournumber});
} ) ;
return ;
} else if ( event . getContent ( ) . body . toLowerCase ( ) . replace ( /\s/g , "" ) . startsWith ( "!sms" ) ) {
sendMatrixNotice( event . getRoomId ( ) , "Malformed command detected, ignoring." ) ;
sendMatrixNotice ( event . getRoomId ( ) , "Hint: there aren't supposed to be any spaces before or in the \"!sms\" part." ) ;
return ;
}
return ;
} else if ( event . getContent ( ) . body . toLowerCase ( ) . replace ( /\s/g , "" ) . startsWith ( "!sms" ) ) {
sendMatrixNotice ( event . getRoomId ( ) , "Malformed command detected, ignoring." ) ;
sendMatrixNotice ( event . getRoomId ( ) , "Hint: there aren't supposed to be any spaces before or in the \"!sms\" part." ) ;
return ;
}
if ( event . getContent ( ) . body . toLowerCase ( ) . startsWith ( "!fixusers" ) ) {
sendMatrixNotice ( event . getRoomId ( ) , "Inviting missing users across all SMS chats. You may need to run this command several times, there's a server limit to how many invites can be sent at once." ) ;
client . getJoinedRooms ( ) . then ( function ( rooms ) {
var roomlist = rooms . joined _rooms ;
for ( var i = 0 ; i < roomlist . length ; i ++ ) {
( function ( roomid ) {
client . getJoinedRoomMembers ( roomid ) . then ( function ( joined ) {
var members = Object . keys ( joined . joined ) ;
for ( var j = 0 ; j < settings . inviteusers . length ; j ++ ) {
if ( members . indexOf ( settings . inviteusers [ j ] ) == - 1 ) {
logger . info ( "Inviting missing user " + settings . inviteusers [ j ] + " to room " + roomid ) ;
client . invite ( roomid , settings . inviteusers [ j ] ) ;
if ( event . getContent ( ) . body . toLowerCase ( ) . startsWith ( "!fixusers" ) ) {
sendMatrixNotice ( event . getRoomId ( ) , "Inviting missing users across all SMS chats. You may need to run this command several times, there's a server limit to how many invites can be sent at once." ) ;
client . getJoinedRooms ( ) . then ( function ( rooms ) {
var roomlist = rooms . joined _rooms ;
for ( var i = 0 ; i < roomlist . length ; i ++ ) {
( function ( roomid ) {
client . getJoinedRoomMembers ( roomid ) . then ( function ( joined ) {
var members = Object . keys ( joined . joined ) ;
for ( var j = 0 ; j < settings . inviteusers . length ; j ++ ) {
if ( members . indexOf ( settings . inviteusers [ j ] ) == - 1 ) {
logger . info ( "Inviting missing user " + settings . inviteusers [ j ] + " to room " + roomid ) ;
client . invite ( roomid , settings . inviteusers [ j ] ) ;
}
}
}
} ) ;
} ) ( roomlist [ i ] ) ;
}
} ) ;
} ) ;
} ) ( roomlist [ i ] ) ;
}
} ) ;
return ;
} else if ( event . getContent ( ) . body . toLowerCase ( ) . replace ( /\s/g , "" ) . startsWith ( "!fixusers" ) ) {
sendMatrixNotice ( event . getRoomId ( ) , "Malformed command detected, ignoring." ) ;
return ;
}
return ;
} else if ( event . getContent ( ) . body . toLowerCase ( ) . replace ( /\s/g , "" ) . startsWith ( "!fixusers" ) ) {
sendMatrixNotice ( event . getRoomId ( ) , "Malformed command detected, ignoring." ) ;
return ;
}
if ( event . getContent ( ) . body . toLowerCase ( ) . replace ( /\s/g , "" ) . startsWith ( "!" ) ) {
sendMatrixNotice ( event . getRoomId ( ) , "I'm sorry, but my programming forbids me from sending text messages that start with `!`." ) ;
return ;
}
if ( event . getContent ( ) . body . toLowerCase ( ) . replace ( /\s/g , "" ) . startsWith ( "!" ) ) {
sendMatrixNotice ( event . getRoomId ( ) , "I'm sorry, but my programming forbids me from sending text messages that start with `!`." ) ;
return ;
}
var matches = room . name . match ( /SMS_([1-9][0-9]+)(?:_([1-9][0-9]+))?/g ) ;
console . log ( event . getRoomId ( ) ) ;
if ( matches == null || ( matches . length != 1 && matches . length != 2 ) ) {
client . getRoomTags ( event . getRoomId ( ) ) . then ( ( response ) => {
console . log ( response ) ;
if ( typeof response . tags [ "u.matrix-bridge-voxtelesys-sms" ] != "undefined" ) {
var tel = response . tags [ "u.matrix-bridge-voxtelesys-sms" ] . tel ;
var from = response . tags [ "u.matrix-bridge-voxtelesys-sms" ] . ournumber ;
logger . info ( "Got message for " + tel + " from " + event . getSender ( ) + ", relaying to " + from + "." ) ;
var matches = room . name . match ( /SMS_([1-9][0-9]+)(?:_([1-9][0-9]+))?/g ) ;
console . log ( event . getRoomId ( ) ) ;
if ( matches == null || ( matches . length != 1 && matches . length != 2 ) ) {
client . getRoomTags ( event . getRoomId ( ) ) . then ( ( response ) => {
console . log ( response ) ;
if ( typeof response . tags [ "u.matrix-bridge-voxtelesys-sms" ] != "undefined" ) {
var tel = response . tags [ "u.matrix-bridge-voxtelesys-sms" ] . tel ;
var from = response . tags [ "u.matrix-bridge-voxtelesys-sms" ] . ournumber ;
logger . info ( "Got message for " + tel + " from " + event . getSender ( ) + ", relaying to " + from + "." ) ;
switch ( event . getContent ( ) . msgtype ) {
case "m.image" :
case "m.file" :
case "m.video" :
sendMMS (
tel ,
from ,
event . getContent ( ) . url ,
event . getContent ( ) . info . mimetype ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
case "m.text" :
default :
sendSMS (
tel ,
from ,
event . getContent ( ) . body ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
}
} else if ( typeof response . tags [ "com.netsyms.matrix-bridge-voxtelesys.sms" ] != "undefined" ) {
var tel = response . tags [ "com.netsyms.matrix-bridge-voxtelesys.sms" ] . tel ;
var from = response . tags [ "com.netsyms.matrix-bridge-voxtelesys.sms" ] . ournumber ;
client . setRoomTag ( event . getRoomId ( ) , "u.matrix-bridge-voxtelesys-sms" , { tel : tel , ournumber : from , order : 0.5 } ) ;
logger . info ( "Got message for " + tel + " from " + event . getSender ( ) + ", relaying to " + from + "." ) ;
switch ( event . getContent ( ) . msgtype ) {
case "m.image" :
case "m.file" :
case "m.video" :
sendMMS (
tel ,
from ,
event . getContent ( ) . url ,
event . getContent ( ) . info . mimetype ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
case "m.text" :
default :
sendSMS (
tel ,
from ,
event . getContent ( ) . body ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
}
} else {
console . log ( response . tags ) ;
sendMatrixNotice ( room . roomId , "Error: couldn't determine correct number to send SMS from." ) ;
}
} ) ;
} else {
if ( matches . length == 1 ) {
var tel = matches [ 0 ] ;
logger . info ( "Got message for " + tel + " from " + event . getSender ( ) + ", relaying." ) ;
switch ( event . getContent ( ) . msgtype ) {
case "m.image" :
case "m.file" :
case "m.video" :
sendMMS (
tel ,
from ,
event . getContent ( ) . url ,
sendMMS ( tel , settings . smsfrom , event . getContent ( ) . url ,
event . getContent ( ) . info . mimetype ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
@ -604,28 +711,20 @@ client.on("Room.timeline", function (event, room) {
break ;
case "m.text" :
default :
sendSMS (
tel ,
from ,
event . getContent ( ) . body ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
sendSMS ( tel , settings . smsfrom , event . getContent ( ) . body , function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
}
} else if ( typeof response . tags [ "com.netsyms.matrix-bridge-voxtelesys.sms" ] != "undefined" ) {
var tel = response . tags [ "com.netsyms.matrix-bridge-voxtelesys.sms" ] . tel ;
var from = response . tags [ "com.netsyms.matrix-bridge-voxtelesys.sms" ] . ournumber ;
client . setRoomTag ( event . getRoomId ( ) , "u.matrix-bridge-voxtelesys-sms" , { tel : tel , ournumber : from , order : 0.5 } ) ;
} else if ( matches . length == 2 ) {
var tel = matches [ 0 ] ;
var from = matches [ 1 ] ;
logger . info ( "Got message for " + tel + " from " + event . getSender ( ) + ", relaying to " + from + "." ) ;
switch ( event . getContent ( ) . msgtype ) {
case "m.image" :
case "m.file" :
case "m.video" :
sendMMS (
tel ,
from ,
event . getContent ( ) . url ,
sendMMS ( tel , from , event . getContent ( ) . url ,
event . getContent ( ) . info . mimetype ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
@ -633,65 +732,15 @@ client.on("Room.timeline", function (event, room) {
break ;
case "m.text" :
default :
sendSMS (
tel ,
from ,
event . getContent ( ) . body ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
sendSMS ( tel , settings . smsfrom , event . getContent ( ) . body , function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
}
} else {
console . log ( response . tags ) ;
sendMatrixNotice ( room . roomId , "Error: couldn't determine correct number to send SMS from." ) ;
}
} ) ;
} else {
if ( matches . length == 1 ) {
var tel = matches [ 0 ] ;
logger . info ( "Got message for " + tel + " from " + event . getSender ( ) + ", relaying." ) ;
switch ( event . getContent ( ) . msgtype ) {
case "m.image" :
case "m.file" :
case "m.video" :
sendMMS ( tel , settings . smsfrom , event . getContent ( ) . url ,
event . getContent ( ) . info . mimetype ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
case "m.text" :
default :
sendSMS ( tel , settings . smsfrom , event . getContent ( ) . body , function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
}
} else if ( matches . length == 2 ) {
var tel = matches [ 0 ] ;
var from = matches [ 1 ] ;
logger . info ( "Got message for " + tel + " from " + event . getSender ( ) + ", relaying to " + from + "." ) ;
switch ( event . getContent ( ) . msgtype ) {
case "m.image" :
case "m.file" :
case "m.video" :
sendMMS ( tel , from , event . getContent ( ) . url ,
event . getContent ( ) . info . mimetype ,
function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
case "m.text" :
default :
sendSMS ( tel , settings . smsfrom , event . getContent ( ) . body , function ( ) {
client . sendReadReceipt ( event , "m.read" ) ;
} ) ;
break ;
}
}
} catch ( ex ) {
logger . error ( "Error handling incoming event: " + ex ) ;
}
} catch ( ex ) {
logger . error ( "Error handling incoming event: " + ex ) ;
}
} ) ;
} ) ;
}