diff --git a/config.example.json b/config.example.json index ee30060..83af2be 100644 --- a/config.example.json +++ b/config.example.json @@ -2,7 +2,7 @@ "homeserver": "https://matrix.org", "mediaurlpath": "https://matrix.org/_matrix/media/r0/download/{{server-name}}/{{media-id}}", "matrixdomain": "matrix.org", - "matrixuser": "smsbot", + "matrixuser": "@smsbot:matrix.org", "matrixpass": "hunter2", "matrixaccesstoken": "", "inviteusers": ["@annoyme:matrix.org"], @@ -11,7 +11,6 @@ "smsonlyto": ["14061234567"], "listenport": 8069, "publicurl": "http://localhost:8069/", - "googleverifiedsms": false, "loglevel": "debug", "smsinterval": 15 } diff --git a/main.js b/main.js index 054f498..347eeaf 100644 --- a/main.js +++ b/main.js @@ -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); - } -}); \ No newline at end of file + }); +} \ No newline at end of file