/ *
* Copyright 2020 - 2022 Netsyms Technologies .
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
* /
/ *
* Matrix really , really , really needs better SDK docs .
* /
console . log ( "Starting up..." ) ;
import * as sdk from 'matrix-js-sdk' ;
import matrixcs from 'matrix-js-sdk' ;
import fs from 'fs' ;
import log4js from 'log4js' ;
import https from 'https' ;
import { fileURLToPath } from 'url' ;
import { dirname } from 'path' ;
import request from 'request' ;
import FileType from 'file-type' ;
import express from 'express' ;
import bodyParser from 'body-parser' ;
import fetch from 'node-fetch' ;
// Save script start time for ignoring older messages
var boottimestamp = Date . now ( ) ;
var client ;
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 ) ;
/ * *
* From https : //github.com/stevekinney/node-phone-formatter
* @ param { string } phoneNumber
* @ param { string } formatString
* @ returns { string }
* /
function formatPhoneNumber ( phoneNumber , formatString ) {
phoneNumber = phoneNumber . replace (
/^[\+\d{1,3}\-\s]*\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/ ,
"$1$2$3"
) ;
for ( var i = 0 , l = phoneNumber . length ; i < l ; i ++ ) {
formatString = formatString . replace ( "N" , phoneNumber [ i ] ) ;
}
return formatString ;
}
function checkSMS ( ) {
logger . debug ( "Checking SMS" ) ;
const options = {
hostname : 'smsapi.voxtelesys.net' ,
port : 443 ,
path : '/api/v1/msgs/inbound?unread_only=true' ,
method : 'GET' ,
headers : {
"Authorization" : "Bearer " + settings . smsapikey
}
} ;
const req = https . request ( options , res => {
res . on ( 'data' , d => {
try {
var json = JSON . parse ( d ) ;
var messages = json . results ;
if ( messages . length == 0 ) {
logger . debug ( "No new SMS messages." ) ;
}
for ( var i = 0 ; i < messages . length ; i ++ ) {
let msg = messages [ i ] ;
if ( settings . smsonlyto . length > 0 && settings . smsonlyto . indexOf ( msg . to ) != - 1 ) {
logger . info ( "Received SMS from " + msg . from + " for " + msg . to + ": " + msg . body ) ;
createOrJoinSMSRoom ( msg . from , msg . to , function ( roomid ) {
sendMatrix ( roomid , msg . body , msg . media ) ;
} ) ;
} else {
logger . info ( "Received SMS from " + msg . from + " for " + msg . to + ", ignoring based on smsonlyto list." ) ;
}
}
} catch ( ex ) {
logger . error ( ex ) ;
}
} ) ;
} ) ;
req . on ( 'error' , error => {
logger . warn ( error ) ;
} ) ;
req . end ( ) ;
}
/ * *
* Connect to the server and get the room ID and stuff
* @ param { type } alias
* @ returns { undefined }
* /
async function getRoomIDFromAlias ( alias ) {
var info = await client . getRoomIdForAlias ( alias ) ;
if ( typeof info . room _id != "undefined" ) {
return info ;
}
var response = await fetch ( settings . homeserver + "/_matrix/client/v3/directory/room/" + encodeURIComponent ( alias ) ) ;
if ( response . status != 200 ) {
throw new Error ( "Fetch returned invalid status code " + response . status ) ;
}
var json = await response . json ( ) ;
return json ;
}
/ * *
* Join or create + join a room with alias # SMS _ { tel } . If already joined , do nothing .
* @ param { string } tel
* @ param { string } ournumber the phone number Matrix messages will be sent from for this room
* @ param { function } callback function with the room ID as an argument .
* @ returns { undefined }
* /
async function createOrJoinSMSRoom ( tel , ournumber , callback ) {
var roomName = "#SMS_" + tel + "_" + ournumber + ":" + settings . matrixdomain ;
logger . debug ( "Checking if room " + roomName + " exists." ) ;
try {
var res = await getRoomIDFromAlias ( roomName ) ;
logger . debug ( JSON . stringify ( res ) ) ;
logger . debug ( "Room " + roomName + " exists!" ) ;
var inRoom = false ;
if ( ! res . error ) {
var rooms = ( await client . getJoinedRooms ( ) ) . joined _rooms ;
for ( var i = 0 ; i < rooms . length ; i ++ ) {
if ( rooms [ i ] == res . room _id ) {
inRoom = true ;
break ;
}
}
}
if ( inRoom ) {
// we're already in the room, do nothing
logger . debug ( "Room " + roomName + " already joined." ) ;
client . setRoomTag ( res . room _id , "u.matrix-bridge-voxtelesys-sms" , { tel : tel , ournumber : ournumber , order : 0.5 } ) ;
callback ( res . room _id ) ;
} else {
// not in the room, join it
client . joinRoom ( res . room _id ) . then ( ( room ) => {
logger . debug ( "Room " + roomName + " joined." ) ;
client . setRoomTag ( room . room _id , "u.matrix-bridge-voxtelesys-sms" , { tel : tel , ournumber : ournumber , order : 0.5 } ) ;
callback ( res . room _id ) ;
} ) ;
}
return ;
} catch ( err ) {
// room doesn't exist, create it
logger . debug ( err ) ;
logger . debug ( "Room " + roomName + " does not exist. Creating it now." ) ;
var userPowerLevels = { } ;
for ( var i = 0 ; i < settings . inviteusers . length ; i ++ ) {
userPowerLevels [ settings . inviteusers [ i ] ] = 50 ;
}
userPowerLevels [ settings [ "matrixuser" ] ] = 100 ;
try {
var room = await client . createRoom ( {
room _alias _name : "SMS_" + tel + "_" + ournumber ,
preset : "trusted_private_chat" ,
visibility : "private" ,
invite : settings . inviteusers ,
power _level _content _override : {
"events" : {
"m.room.name" : 50 ,
"m.room.power_levels" : 50 ,
"m.room.canonical_alias" : 100
} ,
"events_default" : 0 ,
"invite" : 50 ,
"kick" : 50 ,
"notifications" : {
"room" : 50
} ,
"redact" : 50 ,
"state_default" : 50 ,
"users" : userPowerLevels ,
"users_default" : 50
} ,
is _direct : true ,
name : formatPhoneNumber ( tel , "(NNN) NNN-NNNN" ) ,
topic : "SMS conversation with " + formatPhoneNumber ( tel , "(NNN) NNN-NNNN" ) + " (using " + formatPhoneNumber ( ournumber , "(NNN) NNN-NNNN" ) + ")"
} ) ;
logger . debug ( "Room" + roomName + " created with ID " + room . room _id ) ;
// The first message or two we send doesn't go through unless we do this.
// It just spits out "Error sending event M_FORBIDDEN: Unknown room" instead.
createOrJoinSMSRoom ( tel , ournumber , callback ) ;
} catch ( errr ) {
logger . error ( "Could not create " + roomName + "." ) ;
logger . error ( errr ) ;
}
}
}
function getAndUploadFile ( url , callback ) {
logger . info ( "Downloading MMS media " + url ) ;
// download
request ( { url , encoding : null } , ( err , resp , buffer ) => {
FileType . fromBuffer ( buffer ) . then ( function ( mimeobj ) {
logger . debug ( mimeobj ) ;
if ( typeof mimeobj == "undefined" ) {
logger . error ( "Undefined media mimetype, not uploading to Matrix." ) ;
return ;
}
// upload
logger . info ( "Uploading MMS media to Matrix " + url ) ;
client . uploadContent ( buffer , {
onlyContentUri : true ,
rawResponse : false ,
type : mimeobj . mime
} ) . then ( ( res ) => {
if ( typeof callback == "function" ) {
callback ( res , mimeobj . mime ) ;
logger . info ( "Media URI: " + res ) ;
}
} ) . catch ( ( err ) => {
if ( typeof callback == "function" ) {
callback ( false ) ;
}
if ( err . data . error == "Unknown room" ) {
return ;
}
logger . error ( err ) ;
} ) ;
} ) ;
} ) ;
}
/ * *
* Send a message to a Matrix room .
* @ param { string } room the room to post the message in .
* @ param { string } body message content .
* @ param { array } media Array of media URLs to download via HTTP ( s ) and send to Matrix .
* @ returns { undefined }
* /
async function sendMatrix ( room , body , media ) {
var roomid = room ;
if ( room . startsWith ( "#" ) ) {
try {
roomid = ( await getRoomIDFromAlias ( ) ) . room _id ;
logger . info ( "Translated alias " + room + " to room ID " + roomid ) ;
} catch ( err ) {
logger . error ( err ) ;
}
}
if ( Array . isArray ( media ) ) {
for ( var i = 0 ; i < media . length ; i ++ ) {
getAndUploadFile ( media [ i ] , async function ( uri , mimetype ) {
if ( mimetype == "image/jpg" || mimetype == "image/jpeg" || mimetype == "image/png" || mimetype == "image/gif" ) {
var content = {
body : "Image" ,
msgtype : "m.image" ,
url : uri ,
info : {
mimetype : mimetype
}
} ;
} else {
var content = {
body : "File" ,
msgtype : "m.file" ,
url : uri ,
info : {
mimetype : mimetype
}
} ;
}
try {
await client . sendEvent ( roomid , "m.room.message" , content , "" ) ;
return true ;
} catch ( err ) {
if ( err . data . error == "Unknown room" ) {
return ;
}
logger . error ( err ) ;
}
} ) ;
}
}
if ( body != "" ) {
var content = {
body : body ,
msgtype : "m.text"
} ;
try {
await client . sendEvent ( roomid , "m.room.message" , content , "" ) ;
return true ;
} catch ( err ) {
if ( err . data . error == "Unknown room" ) {
return false ;
}
logger . error ( err ) ;
}
} else {
return true ;
}
}
function sendMatrixNotice ( roomid , body , callback ) {
var content = {
body : body ,
msgtype : "m.notice"
} ;
client . sendEvent ( roomid , "m.room.message" , content , "" ) . then ( ( res ) => {
if ( typeof callback == "function" ) {
callback ( true ) ;
}
} ) . catch ( ( err ) => {
if ( typeof callback == "function" ) {
callback ( false ) ;
}
if ( err . data . error == "Unknown room" ) {
return ;
}
logger . error ( err ) ;
} ) ;
}
/ * *
* Send a SMS to a phone number .
* @ param { string } number
* @ param { string } body message content .
* @ param { function | undefined } callback passes true when successful , false on failure .
* @ returns { undefined }
* /
function sendSMS ( number , from , body , callback ) {
if ( settings . googleverifiedsms ) {
// Use Google Verified SMS to add business branding to SMS message
}
logger . info ( "Sending SMS to " + number + " from " + from ) ;
var data = {
to : [ number ] ,
from : from ,
body : body
} ;
const jsondata = JSON . stringify ( data ) ;
const options = {
hostname : 'smsapi.voxtelesys.net' ,
port : 443 ,
path : '/api/v1/sms' ,
method : 'POST' ,
headers : {
"Authorization" : "Bearer " + settings . smsapikey ,
"Content-Type" : "application/json" ,
"Accept" : "application/json"
}
}
const req = https . request ( options , res => {
res . on ( 'data' , d => {
logger . debug ( d . toString ( 'utf8' ) ) ;
} ) ;
} ) ;
req . on ( 'error' , error => {
logger . error ( error ) ;
callback ( false ) ;
} ) ;
req . write ( jsondata ) ;
req . end ( ) ;
if ( typeof callback == "function" ) {
callback ( true ) ;
}
}
/ * *
* Send a SMS to a phone number .
* @ param { string } number
* @ param { string } body message content .
* @ param { function | undefined } callback passes true when successful , false on failure .
* @ returns { undefined }
* /
function sendMMS ( number , from , mediauri , mimetype , callback ) {
logger . info ( "Sending MMS to " + number + " from " + from ) ;
var urichunks = mediauri . split ( "/" ) ; // should result in something like [ "mxc:", "", "matrix.org", "mediaidhere90473473" ]
if ( urichunks . length < 4 ) {
logger . error ( "Invalid media uri" ) ;
if ( typeof callback == "function" ) {
callback ( false ) ;
}
return ;
}
var httpmediaurl = settings . mediaurlpath ;
httpmediaurl = httpmediaurl . replace ( "{{server-name}}" , urichunks [ 2 ] ) ;
httpmediaurl = httpmediaurl . replace ( "{{media-id}}" , urichunks [ 3 ] ) ;
var body = "" ;
var media = [ httpmediaurl ] ;
switch ( mimetype ) {
case "image/jpeg" :
case "image/png" :
case "image/gif" :
case "image/bmp" :
// These are likely to work, so don't include the URL
body = "" ;
break ;
default :
// Send link to content too
body = httpmediaurl ;
//media = [];
}
var data = {
to : [ number ] ,
from : from ,
body : body ,
media : media
} ;
const jsondata = JSON . stringify ( data ) ;
const options = {
hostname : 'smsapi.voxtelesys.net' ,
port : 443 ,
path : '/api/v1/sms' ,
method : 'POST' ,
headers : {
"Authorization" : "Bearer " + settings . smsapikey ,
"Content-Type" : "application/json" ,
"Accept" : "application/json"
}
}
const req = https . request ( options , res => {
res . on ( 'data' , d => {
logger . debug ( d . toString ( 'utf8' ) ) ;
} ) ;
} ) ;
req . on ( 'error' , error => {
logger . error ( error ) ;
if ( typeof callback == "function" ) {
callback ( false ) ;
}
} ) ;
req . write ( jsondata ) ;
req . end ( ) ;
if ( typeof callback == "function" ) {
callback ( true ) ;
}
}
function handleHTTPRequest ( req , res ) {
try {
logger . info ( "Got HTTP request: " + req . url ) ;
if ( req . url == "/webhook" ) {
try {
var msg = req . body ;
if ( msg . type == "mo" ) {
if ( settings . smsonlyto . length > 0 && settings . smsonlyto . indexOf ( msg . to ) != - 1 ) {
logger . info ( "Received SMS from " + msg . from + " for " + msg . to + ": " + msg . body ) ;
createOrJoinSMSRoom ( msg . from , msg . to , function ( roomid ) {
logger . info ( "Sending to room " + roomid ) ;
sendMatrix ( roomid , msg . body , msg . media ) ;
} ) ;
res . sendStatus ( 204 ) ;
res . end ( ) ;
} else {
logger . info ( "Received SMS from " + msg . from + " for " + msg . to + ", ignoring based on smsonlyto list." ) ;
res . sendStatus ( 403 ) ;
res . end ( ) ;
}
} else {
res . sendStatus ( 403 ) ;
res . end ( ) ;
}
} catch ( ex ) {
logger . error ( "Decoding webhook body: " + ex ) ;
logger . error ( req . body ) ;
res . sendStatus ( 500 ) ;
res . end ( ) ;
}
} else {
try {
res . sendStatus ( 404 ) ;
res . end ( ) ;
} catch ( err ) {
logger . error ( err ) ;
}
}
} catch ( exx ) {
logger . error ( exx ) ;
}
}
// 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 ) ;
}
logger . error ( "Attempting to fetch access token for you..." ) ;
try {
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 {
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 ( ) ;
}
} ) ;
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 ( 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 == 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 ( 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});
} ) ;
}
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 ] ) ;
}
}
} ) ;
} ) ( roomlist [ i ] ) ;
}
} ) ;
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 ;
}
var matches = room . name . match ( /SMS_([1-9][0-9]+)(?:_([1-9][0-9]+))?/g ) ;
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 , 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 ) ;
}
} ) ;
}