From 149d31e3e7695a6bb54281e2f3c9506f6ac76cf6 Mon Sep 17 00:00:00 2001 From: Jannis Dohm Date: Thu, 1 Apr 2021 05:15:24 +0200 Subject: [PATCH 1/2] first steps of matrix rework css/oi.css - hide cactus-editor (will be replaced with our own) myServer.js - cleanup - added new matrix.js file, removed unused/moved dependencies - added matrix init call - added test page (submitMatrixTest) - added function to create room on creation on new idea - /submitRegister -- now checks if username is allowed (this is also checked on client side, this is just to prevent wrong names on purpose) -- added creation of matrix user and save credentials to db package.json - added dependency to generate random passwords (generate-password) to be used when creating matrix users src/oi/SideLoginRegister.js - restricted usernames to use only A-Za-z0-9 and space src/oi/matrix.js - new file which handels interaction with matrix src/oi/sqliteDv.js - new file which should in the future be used to handle all interaction with the sqlite3 db --- css/oi.css | 4 + myServer.js | 177 +++++++++++------- package.json | 5 +- src/oi/SideLoginRegister.js | 3 +- src/oi/matrix.js | 360 ++++++++++++++++++++++++++++++++++++ src/oi/sqliteDb.js | 70 +++++++ 6 files changed, 549 insertions(+), 70 deletions(-) create mode 100644 src/oi/matrix.js create mode 100644 src/oi/sqliteDb.js diff --git a/css/oi.css b/css/oi.css index 7736ddb47a..d0a7240a25 100644 --- a/css/oi.css +++ b/css/oi.css @@ -156,3 +156,7 @@ input::placeholder { z-index: 1000; outline: none; } + +.cactus-editor { + display: none!important; +} \ No newline at end of file diff --git a/myServer.js b/myServer.js index f4d5859fb7..147a2a17d7 100644 --- a/myServer.js +++ b/myServer.js @@ -7,14 +7,16 @@ const express = require("express"); const fs = require("fs"); const sqlite3 = require("sqlite3").verbose(); const https = require("https"); -var path = require("path"); -var bcrypt = require("bcrypt"); +const path = require("path"); +const bcrypt = require("bcrypt"); const sdk = require("matrix-js-sdk"); -var request = require('request'); +const request = require("request"); const passport = require("passport"); const flash = require("express-flash"); const session = require("express-session"); -const methodOverride = require('method-override'); +const methodOverride = require("method-override"); +const matrix = require("./src/oi/matrix"); +// const db = require("./src/oi/sqliteDb"); const app = express(); const http = express(); @@ -23,12 +25,20 @@ const portSSL = process.argv[3] || 443; //matrix login const client = sdk.createClient("https://matrix.org"); -client.login("m.login.password", {"user": "openidea", "password": process.env.MATRIX_PASSWORD}).then((response) => { +client + .login("m.login.password", { + user: "openidea", + password: process.env.MATRIX_PASSWORD, + }) + .then((response) => { console.log(response.access_token); }); +matrix.loginOpenIdea(); + //usermanagement using passport const initializePassport = require("./passport-config"); +// initializePassport(passport, async db.getUserByEmail); initializePassport(passport, async function getUserByEmail(email) { return new Promise((resolve) => { let sql = `SELECT name, email, pwHash password FROM User WHERE email is ?`; @@ -41,8 +51,7 @@ initializePassport(passport, async function getUserByEmail(email) { if (err) throw err; resolve(rows); }); - } - else resolve(rows); + } else resolve(rows); }); }); }); @@ -91,15 +100,14 @@ app.use((req, res, next) => { app.use(passport.initialize()); app.use(passport.session()); -app.use(methodOverride('_method')); +app.use(methodOverride("_method")); //app start page app.get("/", checkAuthenticated, function (req, res) { - if(req.session.isNew) { - if(req.query.Idea) res.redirect('/?user=new&Idea='+req.query.Idea); - else res.redirect('/?user=new'); - } - else { + if (req.session.isNew) { + if (req.query.Idea) res.redirect("/?user=new&Idea=" + req.query.Idea); + else res.redirect("/?user=new"); + } else { try { console.log(req.user.name + " visited us"); } catch (error) { @@ -109,7 +117,6 @@ app.get("/", checkAuthenticated, function (req, res) { } }); - //respond with topics app.get("/getTopics", function (req, res) { //read all topics from Database @@ -135,10 +142,10 @@ app.get("/getSkills", function (req, res) { }); //respond to getIdeas GET request -app.get("/getIdeas", checkAuthenticated, function(req, res) { +app.get("/getIdeas", checkAuthenticated, function (req, res) { //read all places from Database var idearows; - db.serialize(function() { + db.serialize(function () { let sql = `SELECT lon, lat, IdeaID, upvotes, downvotes FROM v_PlacesVotes ORDER BY lat`; db.all(sql, [], async (err, rows) => { if (err) throw err; @@ -148,16 +155,16 @@ app.get("/getIdeas", checkAuthenticated, function(req, res) { idearows[key].tags = tmp.tags; idearows[key].skills = tmp.skills; idearows[key].user = tmp.user; - }; + } res.json(idearows); }); }); }); function addTagsSkillsUsers(id) { - var idearows = {tags: [], skills: [], user: []}; + var idearows = { tags: [], skills: [], user: [] }; return new Promise((resolve) => { - db.serialize(function() { + db.serialize(function () { let sql = `SELECT Tag FROM Idea_Tags WHERE Idea is ?`; db.all(sql, [id], (err, rows) => { if (err) throw err; @@ -188,17 +195,17 @@ function addTagsSkillsUsers(id) { }); }); }); -}; +} //get svgs -app.get("/svgTest", function(req,res) { +app.get("/svgTest", function (req, res) { let _id = req.query.IdeaID; let _title = ""; db.serialize(function () { let sql = `SELECT ID, title FROM Idea WHERE ID is ?`; db.get(sql, [_id], (err, row) => { if (err) throw err; - if(row) _title = row.title; + if (row) _title = row.title; else _title = "title not found"; // console.log("test query " + _id + " title " + _title); var testSVG = ` @@ -220,12 +227,26 @@ app.get("/svgTest", function(req,res) { ${_title} `; - res.type('svg'); + res.type("svg"); res.send(testSVG); }); }); }); +//test matrix room joining and sending messages +app.get("/submitMatrixTest", async function (req, res, next) { + + //console.log(matrix.create(req.query.message)); + // var matrixU = matrix.createUser("useruser"); + // console.log(matrixU); + + // matrix.createRoom(108); + // console.log(await matrix.sendMessage('useruser', 90, "just a first test", db)); + console.log(await matrix.getLastMessages(90, 10)); + + res.send("test done"); +}); + //respond to getIdea POST request app.post("/getIdea", checkAuthenticated, function (req, res) { if (!req.body.IdeaID) res.json(); @@ -294,7 +315,7 @@ app.post("/submitVote", checkAuthenticatedCancel, function (req, res) { 1: req.body.IdeaID, 2: req.body.upvote, 3: datenow, - 4: req.user.name + 4: req.user.name, }, function (err) { if (err) return console.log(err.message); @@ -330,7 +351,7 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { ); var tags = req.body.tags.split(","); tags.forEach(function (item, index) { - if(item.trim() != ""){ + if (item.trim() != "") { db.run( "INSERT OR IGNORE INTO Tags(Name) VALUES(?1)", { @@ -354,7 +375,7 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { }); var skills = req.body.skills.split(","); skills.forEach(function (item, index) { - if(item.trim() != ""){ + if (item.trim() != "") { db.run( "INSERT OR IGNORE INTO Skills(Name) VALUES(?1)", { @@ -381,7 +402,7 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { { 1: lastID, 2: req.user.name, - 3: 0 + 3: 0, }, function (err) { if (err) return console.log(err.message); @@ -391,17 +412,17 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { /*****************************| | mastodon part start | |*****************************/ - if(req.body.mastodon == "true"){ + if (req.body.mastodon == "true") { console.log("publish on mastodon: " + req.body.mastodon); var tagsBody = ""; - tags.some(function(item, index) { - if(item.trim() != "") { - tagsBody += ("#" + item.trim() + " "); + tags.some(function (item, index) { + if (item.trim() != "") { + tagsBody += "#" + item.trim() + " "; console.log("item: " + index + "tag: " + "#" + item.trim() + " "); } //if 3 tags written stop function - if(index >= 2) return true; + if (index >= 2) return true; else return false; }); @@ -411,54 +432,66 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { + "\n" + "https://openidea.io/?Idea=" + lastID + "\n" + tagsBody; - var jsonBody = {'status': truncate(statusText, 425), - 'visibility': 'unlisted'}// public, unlisted, private, direct - ; + var jsonBody = { + status: truncate(statusText, 425), + visibility: "unlisted", + }; // public, unlisted, private, direct var clientServerOptions = { - url: 'https://botsin.space/api/v1/statuses', + url: "https://botsin.space/api/v1/statuses", body: JSON.stringify(jsonBody), - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', - 'Authorization' : 'Bearer ' + process.env.MASTODON_ACCESS_TOKEN - } + "Content-Type": "application/json", + Authorization: "Bearer " + process.env.MASTODON_ACCESS_TOKEN, + }, }; - request(clientServerOptions, function(error, response, body){ + request(clientServerOptions, function (error, response, body) { // console.log(body); }); - function truncate(str, n){ - return (str.length > n) ? str.substr(0, n-3) + '...' : str; - }; + function truncate(str, n) { + return str.length > n ? str.substr(0, n - 3) + "..." : str; + } } /*****************************| | mastodon part end | |*****************************/ + + /**********************************| + * matrix part start | + |**********************************/ + matrix.createRoom(lastID, req.body.nameText); + /**********************************| + * matrix part end | + |**********************************/ res.json(lastID); } ); }); }); - //support request to matrix room app.post("/submitSupportRequest", function (req, res, next) { client.startClient(); - var testRoomId = "!HTcpkpXWLaaunauYsD:cactus.chat"; + var RoomId = "!HTcpkpXWLaaunauYsD:cactus.chat"; var content = { - "body": "#" + req.body.IdeaID + "\n" + req.body.text + "\n\nsend from: ", - "msgtype": "m.text" + body: "#" + req.body.IdeaID + "\n" + req.body.text + "\n\nsend from: ", + msgtype: "m.text", }; - if(req.user){ + if (req.user) { content.body += req.user.email; + } else { + content.body += "anonymous"; } - else {content.body += "anonymous";} - client.sendEvent(testRoomId, "m.room.message", content, "").then((res) => { + client + .sendEvent(RoomId, "m.room.message", content, "") + .then((res) => { // message sent successfully - }).catch((err) => { + }) + .catch((err) => { console.log(err); }); res.send("Message send to support"); @@ -469,23 +502,23 @@ app.post("/submitSupportRequest", function (req, res, next) { app.post("/submitLogin", function (req, res, next) { passport.authenticate("local", (err, user, info) => { //if authenticate failed respond with info - if(!user) return res.json(info); + if (!user) return res.json(info); else { //start user session - req.logIn(user, function(err) { - if (err) { return next(err); } + req.logIn(user, function (err) { + if (err) { + return next(err); + } return res.json(user.name); }); } - })(req,res,next) - } -); + })(req, res, next); +}); app.delete("/SubmitLogout", function (req, res) { req.logOut(); res.json(); -} - ); +}); //handle register POST request app.post("/submitRegister", async function (req, res) { @@ -494,31 +527,41 @@ app.post("/submitRegister", async function (req, res) { res.json({ email: req.user.email, }); - } catch {} + } catch(e) {} + + //reject username if it uses not allowed character + const regex = /[^A-Za-z0-9 ]+/g; + if(regex.search(req.body.name)) { + res.json({message: "username not allowed"}); + return; + } + console.log(req.body.name); console.log(req.body.email); - console.log(req.body.password); try { const hashedPassword = await bcrypt.hash(req.body.password, 10); console.log(hashedPassword); + const matrixUser = await matrix.createUser(req.body.name); db.serialize(function () { db.run( - "INSERT INTO User(name,email,pwHash) VALUES(?1,?2,?3)", + "INSERT INTO User(name,email,pwHash,matrixname,matrixpw) VALUES(?1,?2,?3,?4,?5)", { 1: req.body.name, 2: req.body.email, 3: hashedPassword, + 4: matrixUser.matrixname, + 5: matrixUser.matrixpw, }, function (err) { if (err) { console.log(err.message); - res.json({"message": err.message}); + res.json({ message: err.message }); } res.json("success"); } ); }); - } catch { + } catch(e) { console.log("error while user registered"); } }); @@ -550,11 +593,11 @@ function checkAuthenticated(req, res, next) { function checkAuthenticatedCancel(req, res, next) { if (req.isAuthenticated()) { - res.append('login', req.user.name); + res.append("login", req.user.name); console.log("checkAuthenticated " + req.user.name); next(); } else { - res.append('login', false); + res.append("login", false); res.json("error"); } } diff --git a/package.json b/package.json index 38a9f436e9..3501fd50ae 100644 --- a/package.json +++ b/package.json @@ -36,15 +36,16 @@ "express": "^4.17.1", "express-flash": "0.0.2", "express-session": "^1.17.1", + "generate-password": "^1.6.0", "material-design-icons": "^3.0.1", "materialize-css": "^1.0.0", "matrix-js-sdk": "^9.9.0", "method-override": "^3.0.0", + "msdf-bmfont-xml": "^2.5.4", "nodemon": "^2.0.7", "passport": "^0.4.1", "passport-local": "^1.0.0", - "sqlite3": "^5.0.2", - "msdf-bmfont-xml": "^2.5.4" + "sqlite3": "^5.0.2" }, "exports": { ".": "./src/og/index.js", diff --git a/src/oi/SideLoginRegister.js b/src/oi/SideLoginRegister.js index 494ce51a04..415b984199 100644 --- a/src/oi/SideLoginRegister.js +++ b/src/oi/SideLoginRegister.js @@ -128,7 +128,8 @@ function showRegister() {
- + +
diff --git a/src/oi/matrix.js b/src/oi/matrix.js new file mode 100644 index 0000000000..67f45bb243 --- /dev/null +++ b/src/oi/matrix.js @@ -0,0 +1,360 @@ +/** + * @fileOverview + * @name matrix.js + * @author Jannis Dohm + * @license MIT + */ +const https = require("https"); +const generator = require('generate-password'); +const crypto = require("crypto"); +const sdk = require("matrix-js-sdk"); +const { exitCode } = require("process"); +const db = require("./sqliteDb"); +const { RoomState } = require("matrix-js-sdk"); + +/* url to the matrix server for crating new users + often uses matrix.example.com + */ +const matrixServer = "https://matrix.dohm.work"; +/* url to append on matrix rooms + often uses example.com + */ +const matrixGroupUrl = "dohm.work"; + +let urldata = { + host: matrixServer, + path: "/_synapse/admin/v1/register", + method: "GET", +}; +let urlRegData = { + nonce: "", + username: "", + displayname: "", + password: "", + mac: "", +}; +let matrix = { + matrixname: "", + matrixpw: "" +}; + +let openideaClient; +let oirooms = []; +const oiroom = {roomId: "", messages: []}; +const oimessage = {displayname: "", datetime: "", message: ""}; + +oimessage.displayname = "testuser"; +oimessage.datetime = "2021-03-31 23:17"; +oimessage.message = "test message"; +oiroom.roomId = "test Room 1"; +oiroom.messages.push(oimessage); + +oimessage.datetime = "2021-03-31 23:19"; +oimessage.message = "test message 2"; +oiroom.messages.push(oimessage); +oirooms.push(oiroom); + +console.log(oirooms); + +function roomAddMessage(room, displayname, datetime, message){ + RoomState.some(r => r.roomId === room) +} + + +/** + * This function creates a matrix account for a given user. + * The username will be transformed to all lowercase and spaces will be replaced with underscores + * for more informations visit: https://github.com/matrix-org/synapse/blob/master/docs/admin_api/register_api.rst + * @param {string} username - wanted username of matrix user + * @returns {object} - matrix object + * @property {string} matrixname - the given matrix username + * @property {string} matrixpw - the automatically created matrix pw + */ +function createUser(username) { + + //generate lowercase only charakters and underscores matrix name! + //make username lowercase + matrix.matrixname = username.toLowerCase(); + //replace spaces with underscores + matrix.matrixname = matrix.matrixname.replace(/\s+/g, '_'); + //delete all non alphanumeric and not underscore (shoudn't be necessary since they are not allowed as usernames anyway) + matrix.matrixname = matrix.matrixname.replace(/\W/g, ''); + // //TODO also add random string if matrixname exists + // if (matrix.matrixname.length < 6) { + // matrix.matrixname += '_' + generator.generate({ + // length: 12, + // numbers: true, + // uppercase: false + // }); + // } + urlRegData.username = matrix.matrixname; + urlRegData.displayname = username; + var password = generator.generate({ + length: 24, + numbers: true + }); + urlRegData.password = password; + matrix.matrixpw = password; + //for debugging and not loosing users in the beginning + console.log("matrixname" + matrix.matrixname); + console.log("pw" + password); + + + function OnResponse(response) { + console.log(`statusCode 1: ${response.statusCode}`); + var data = ""; //This will store the page we're downloading. + response.on("data", function (chunk) { + //Executed whenever a chunk is received. + data += chunk; //Append each chunk to the data variable. + }); + + response.on("end", function () { + urlRegData.nonce = JSON.parse(data).nonce; + urlRegData.mac = generate_mac(urlRegData.nonce, urlRegData.username, urlRegData.password); + + var urlPostData = { + host: "matrix.dohm.work", + path: "/_synapse/admin/v1/register", + port: 443, + method: "POST", + headers: { + "Content-Type": "application/json", + "Content-Length": JSON.stringify(urlRegData).length, + }, + }; + // urlPostData.headers.Content-Length; + // JSON.stringify(urlRegData).length, + + var postreq = https.request(urlPostData, (res) => { + console.log(`statusCode: ${res.statusCode}`); + console.log("res: " + res.statusMessage); + + // res.on("data", (d) => { + // console.log(d); + // }); + }); + + console.log(urlRegData); + postreq.write(JSON.stringify(urlRegData)); + postreq.end(); + }); + } + + console.log(urldata); + https.request(urldata, OnResponse).end(); + return matrix; +} + +function testF(){ + console.log("test"); +} + +/** + * function to generate mac for user creation via shared secret + * @param {number} nonce - nonce which is a one time key to generate the mac. Can be requested from the matrix-synapse server. + * @param {string} user - user to be created + * @param {strin} password - password of user + * @param {boolean} [somebody=false] - wether or not the user should get admin rights + * @returns {number} - mac in HEX + */ +function generate_mac(nonce, user, password, admin=false){ + + var mac = crypto.createHmac("sha1", process.env.DOHMWORK_MATRIX_SS); + + mac.update(nonce.toString()); + mac.update("\x00"); + mac.update(user.toString()); + mac.update("\x00"); + mac.update(password.toString()); + mac.update("\x00"); + if(admin) mac.update("admin"); + else mac.update("notadmin"); + return mac.digest("hex"); +} + +/** + * function to send usermessage to idea room + * @param {string} user - user which sends the message + * @param {number} IdeaID - number of idea hosting the room + * @param {string} message - usermessage + * @param {*} db - opened sqlite database to fetch users matrix data from + * @returns {string} - errormessage or undefined at success + */ +async function sendMessage(user, IdeaID, message, db) { + //get matrixname and matrixpw from database (db) + let matrix = await getMatrixUserByName(user, db); + console.log(matrix); + if(matrix == null || !matrix.matrixname || !matrix.matrixpw) return 'username not found'; + //matrix login + const client = sdk.createClient(matrixServer); + await client + .login("m.login.password", { + user: matrix.matrixname, + password: matrix.matrixpw, + }) + .then((response) => { + // console.log(response.access_token); + }); + await client.startClient(); + + //join room (even if joined already returns the roomID which is needed to write to it) + var roomID + try { + roomID = await client.joinRoom(RoomAlias(IdeaID)); + roomID = roomID.roomId; + } catch (e) { + if(e.errcode == 'M_NOT_FOUND') + { + try { + await createRoom(IdeaID); + roomID = await client.joinRoom(RoomAlias(IdeaID)); + roomID = roomID.roomId; + } + catch (e) {return "room coudn't be joined or created"} + } + } + try { + //create content for message to be send + var content = { + body: message, + msgtype: "m.text", + }; + //matrix send message to room + client + .sendEvent(roomID, "m.room.message", content, "") + .then((res) => { + // message sent successfully + return; + }) + .catch((err) => { + console.log(err); + return err; + }); + } + catch (e) { + return e; + } +}; + +/** + * function to create consistant roomAliases + * @param {number} IdeaID - IdeaID which needs a room + * @param {boolean} [forCreation=false] - if true switches to only local part as return (like Idea_IdeaID) + * @returns {string} - roomAlias looks like this: #Idea_IdeaID:dohm.work + */ +function RoomAlias(IdeaID, forCreation=false){ + var roomAlias = 'Idea_' + IdeaID; + if(!forCreation) roomAlias = '#' + roomAlias + ':' + matrixGroupUrl; + return roomAlias; +} + +/** + * function to get link to comment room of given IdeaID + * @param {number} IdeaID - IdearID of idea of which the url is requested + * @returns {string} - url to join room looks like this: https://matrix.to/#/#Idea_IdeaID:dohm.work + */ +function getJoinUrl(IdeaID){ + let url = 'https://matrix.to/#/' + RoomAlias(IdeaID); + return url; +} + +//TODO +/** + * function to fetch (num) last messages of room + * @param {number} IdeaID - IdeaID to fetch messages from + * @param {number} num - number of fetched messages + * @returns {(string|string[])} - returns num messages of matrix room asocciated with IdeaID + */ +async function getLastMessages(IdeaID, num){ + if(!openideaClient) loginOpenIdea(); + + await openideaClient.startClient({initialSyncLimit: 10}); +// await openideaClient.once('sync', function(state, prevState, res) { +// if(state === 'PREPARED') { +// console.log("prepared"); +// } else { +// console.log(state); +// process.exit(1); +// } +// }); + +var roomID = await openideaClient.joinRoom(RoomAlias(IdeaID)); +// try{ +// var room = await openideaClient.getRoom(roomID.roomId); +// } catch (e) {console.log(e);} + +var iTest = 0; + +await openideaClient.on("Room.timeline", function(event, room, toStartOfTimeline) { + if (event.getType() !== "m.room.message") { + return; // only use messages + } + //if(room.name == RoomAlias(IdeaID)) console.log(room); + if(room.roomId == roomID.roomId) console.log("finaly"); + console.log(iTest++); + console.log(room.roomId); + console.log(event.event.content.body); +}); + +// var scrollback = await openideaClient.scrollback(roomID); +// Object.keys(openideaClient.store.rooms).forEach((roomId) => { +// client.getRoom(roomId).timeline.forEach(t => { +// console.log(t.event); +// }); +// }); + + let messages; + //join room (even if joined already returns the roomID which is needed to write to it) + // var roomID = await openideaClient.joinRoom(RoomAlias(IdeaID)); + // try{ + // openideaClient.getRoom(roomID.roomId).timeline.forEach(t => { + // console.log(t.event); + // }); + + // // messages = openideaClient.scrollback(roomID.roomId, num); + // // console.log(messages); + // } catch (e) {console.log(e);} + + return messages; +} + +//TODO +function loginOpenIdea() { + //return if openideaClint was already set + if (openideaClient) return; + openideaClient = sdk.createClient(matrixServer); + openideaClient + .login("m.login.password", { + user: process.env.MATRIX_DW_USER, + password: process.env.MATRIX_DW_PASSWORD, + }) + .then((response) => { + console.log(response.access_token); + }); +} + + +/** + * function to create comment rooms for ideas + * @param {number} IdeaID - IdeaID for which a room should be created + * @param {string} [forCreation] - Display name of room, if not provided room will be namend 'comments for Idea #IdeaID' + * @returns {Room} - room object + */ +function createRoom(IdeaID, roomName){ + if(!openideaClient) loginOpenIdea(); + roomName = roomName || 'comments for Idea #' + IdeaID; + var options = { + name: roomName, + room_alias_name: RoomAlias(IdeaID, true), + visibility: "public" + }; + + console.log(options); + + return openideaClient.createRoom(options); +} + +//TODO new support request +//TODO move matrix user from myServer to this file + +module.exports = {testF, createUser, sendMessage, getJoinUrl, getLastMessages, createRoom, loginOpenIdea}; diff --git a/src/oi/sqliteDb.js b/src/oi/sqliteDb.js new file mode 100644 index 0000000000..22b15064f0 --- /dev/null +++ b/src/oi/sqliteDb.js @@ -0,0 +1,70 @@ +/** + * @fileOverview + * @name sqliteDb.js + * @author Jannis Dohm + * @license MIT + */ + +const sqlite3 = require("sqlite3").verbose(); + + +// Start sqlite3 database connection +function startDb(){ +let db = new sqlite3.Database("./db/Ideas.db", (err) => { + if (err) { + return console.error(err.message); + } + console.log("Connected to the SQlite database."); + return db; +}); +} + + + + +/** + * function to get matrixname and matrixpw of given user + * @param {string} username - username to fetch matrix data for + * @param {*} db - opened sqlite database to fetch data from + * @returns {Object} - matrix user data + * @property {string} matrixname - matrix username + * @property {string} matrixpw - matrix password + */ +async function getMatrixUserByName(username, db) { + return new Promise((resolve) => { + let sql = `SELECT name, matrixname, matrixpw FROM User WHERE name is ?`; + db.get(sql, [username], (err, rows) => { + if (err) throw err; + console.log(rows); + resolve(rows); + }); + }); +} + +/** + * function to get user object by email or username + * @param {string} email - email of searched user + * @param {boolean} [alsoCheckUserName=true] - if set to true (default) this function will check if the given email was a username and return data for this user. + * @returns {Object} - user object + * @property {string} name - username + * @property {string} email - users email + * @property {string} pwHash - hashed password of the user + */ +async function getUserByEmail(email, alsoCheckUserName=true) { + return new Promise((resolve) => { + let sql = `SELECT name, email, pwHash password FROM User WHERE email is ?`; + db.get(sql, [email], (err, rows) => { + if (err) throw err; + //if no user with this email was found, check if a username with this string exists + if (rows == null && alsoCheckUserName) { + sql = `SELECT name, email, pwHash password FROM User WHERE name is ?`; + db.get(sql, [email], (err, rows) => { + if (err) throw err; + resolve(rows); + }); + } else resolve(rows); + }); + }); +} + +module.exports = {getMatrixUserByName, getUserByEmail, startDb}; \ No newline at end of file From e3c25d71f59751941f61e881680f7b2c9bb46331 Mon Sep 17 00:00:00 2001 From: Jannis Dohm Date: Fri, 2 Apr 2021 06:26:14 +0200 Subject: [PATCH 2/2] reworked matrix handling and comment writing resolves #35 - change guest users in comments to logged in users db/Ideas.db.backup - updated example db to contain matrix data and updated constraints for users media/favicon.png - added for use on platforms etc. where svg isn't supported myServer.js - removed unused dependencies (moved to matrix.js) - now uses matrix.js to handle all matrix related business - new endpoint to handle new comments on ideas - removed createRoom function on idea creation (is handeled by cactus comments for now) - changed regex.search to regex.test (search doesn't exist) src/oi/SideLoginRegister.js - register button now registers new user instead of trying to login src/oi/SideShowOdea.js - if user is not logged in, replaces write comment section with comment heading - new comment input field - sending a report request now closes the report window - renamed _testText to _message src/oi/matrix-wo-cactus.js work on using matrix without cactus chat - this file is abondoned backup and will be deleted in future commits src/oi/matrix.js - further work - now provides functions for: -- user creation -- send messages as logged in user -- login in the openidea user (to send support request) -- send report requests -- generating mac to speak to matrix server api (intern) -- generating room aliases (intern) -- generating link to room (intern) (unused) --- db/Ideas.db.backup | Bin 94208 -> 102400 bytes media/favicon.png | Bin 0 -> 13422 bytes myServer.js | 64 +----- src/oi/SideLoginRegister.js | 2 +- src/oi/SideShowIdea.js | 67 +++++- src/oi/matrix-wo-cactus.js | 157 ++++++++++++++ src/oi/matrix.js | 417 ++++++++++++++---------------------- 7 files changed, 386 insertions(+), 321 deletions(-) create mode 100644 media/favicon.png create mode 100644 src/oi/matrix-wo-cactus.js diff --git a/db/Ideas.db.backup b/db/Ideas.db.backup index d5dadd159ee3e08b7b4a6027bc142e87c8e7dce8..a14bde24b2d903e958a8e9321cd8dca22704099d 100644 GIT binary patch literal 102400 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 delta 5633 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 qUlwYMn4N1qP-bb?4Iy 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 QV*mgE07*qoM6N<$f(K|V*Z=?k literal 0 HcmV?d00001 diff --git a/myServer.js b/myServer.js index 147a2a17d7..5a6772e867 100644 --- a/myServer.js +++ b/myServer.js @@ -9,7 +9,6 @@ const sqlite3 = require("sqlite3").verbose(); const https = require("https"); const path = require("path"); const bcrypt = require("bcrypt"); -const sdk = require("matrix-js-sdk"); const request = require("request"); const passport = require("passport"); const flash = require("express-flash"); @@ -23,17 +22,6 @@ const http = express(); const port = process.argv[2] || 80; const portSSL = process.argv[3] || 443; -//matrix login -const client = sdk.createClient("https://matrix.org"); -client - .login("m.login.password", { - user: "openidea", - password: process.env.MATRIX_PASSWORD, - }) - .then((response) => { - console.log(response.access_token); -}); - matrix.loginOpenIdea(); //usermanagement using passport @@ -233,18 +221,13 @@ app.get("/svgTest", function (req, res) { }); }); -//test matrix room joining and sending messages -app.get("/submitMatrixTest", async function (req, res, next) { - - //console.log(matrix.create(req.query.message)); - // var matrixU = matrix.createUser("useruser"); - // console.log(matrixU); - - // matrix.createRoom(108); - // console.log(await matrix.sendMessage('useruser', 90, "just a first test", db)); - console.log(await matrix.getLastMessages(90, 10)); - - res.send("test done"); +//endpoint for new comments +app.post("/submitComment", checkAuthenticated, async function (req, res, next) { + if(res._headers.login !== "false") + console.log(await matrix.sendMessage(res._headers.login, req.body.IdeaID, req.body.comment, db)); + //elso only if we want to let people comment without login in! + //else console.log(await matrix.sendMessage('guest', req.body.IdeaID, req.body.comment, db)); + res.json({msg: "comment saved"}); }); //respond to getIdea POST request @@ -456,14 +439,6 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { /*****************************| | mastodon part end | |*****************************/ - - /**********************************| - * matrix part start | - |**********************************/ - matrix.createRoom(lastID, req.body.nameText); - /**********************************| - * matrix part end | - |**********************************/ res.json(lastID); } ); @@ -472,28 +447,7 @@ app.post("/submitIdea", checkAuthenticatedCancel, function (req, res) { //support request to matrix room app.post("/submitSupportRequest", function (req, res, next) { - client.startClient(); - - var RoomId = "!HTcpkpXWLaaunauYsD:cactus.chat"; - - var content = { - body: "#" + req.body.IdeaID + "\n" + req.body.text + "\n\nsend from: ", - msgtype: "m.text", - }; - if (req.user) { - content.body += req.user.email; - } else { - content.body += "anonymous"; - } - - client - .sendEvent(RoomId, "m.room.message", content, "") - .then((res) => { - // message sent successfully - }) - .catch((err) => { - console.log(err); - }); + matrix.sendSupportRequest(req); res.send("Message send to support"); }); @@ -531,7 +485,7 @@ app.post("/submitRegister", async function (req, res) { //reject username if it uses not allowed character const regex = /[^A-Za-z0-9 ]+/g; - if(regex.search(req.body.name)) { + if(regex.test(req.body.name)) { res.json({message: "username not allowed"}); return; } diff --git a/src/oi/SideLoginRegister.js b/src/oi/SideLoginRegister.js index 415b984199..52ab426ece 100644 --- a/src/oi/SideLoginRegister.js +++ b/src/oi/SideLoginRegister.js @@ -142,7 +142,7 @@ function showRegister() {
-
diff --git a/src/oi/SideShowIdea.js b/src/oi/SideShowIdea.js index c9989d02f0..9955cb3f0a 100644 --- a/src/oi/SideShowIdea.js +++ b/src/oi/SideShowIdea.js @@ -17,6 +17,8 @@ function show(IdeaID) { document.getElementById("up-btn").onclick = function () {SideLoginRegister.showLogin();}; document.getElementById("down-btn").style.opacity = "0.4"; document.getElementById("down-btn").onclick = function () {SideLoginRegister.showLogin();}; + document.getElementById("commentForm").textContent = "Comments:"; + document.getElementById("commentForm").classList.add("cactus-comment"); } //cactus chat @@ -64,7 +66,23 @@ function show(IdeaID) {
+ +
+
+
+ + + + +
+
+
+
+
+
Vote this idea
@@ -88,6 +106,13 @@ function show(IdeaID) {
`; SidePanel.show(html); + //send comment when hitting enter in comment field + document.getElementById('comment').addEventListener('keydown', (event) => { + if (event.code === 'Enter') { + event.preventDefault(); + SideShowIdea.sendComment(IdeaID); + } + }); var LoginRegister = ` Login @@ -131,19 +156,41 @@ function sendSupportRequest(IdeaID){ xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { alert(this.responseText); - // var obj = JSON.parse(this.responseText); - // if(obj) { - // if(obj == "error") alert("error - are you still logged in?"); - // else { - // myCreateIdea(lon,lat,obj,0); - // SidePanel.hide(); - // } - // } + SidePanel.hide(); }}; - var _testText = document.getElementById("text").value; + var _message = document.getElementById("text").value; xhttp.open("POST", "submitSupportRequest", true); xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhttp.send("text="+_testText+"&IdeaID="+IdeaID); + xhttp.send("text="+_message+"&IdeaID="+IdeaID); } export { sendSupportRequest }; + +function sendComment(IdeaID) { + if(!document.forms.comment_form.comment.checkValidity()) { + return false; + } + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + document.forms.comment_form.comment.value = ""; + + //cactus chat - update + let comsec = document.getElementsByClassName("cactus-container"); + comsec[0].innerHTML = ""; + comsec[0].id = "comment-section"; + comsec[0].classList.remove("cactus-container"); + initComments({ + node: document.getElementById("comment-section"), + defaultHomeserverUrl: "https://matrix.cactus.chat:8448", + serverName: "cactus.chat", + siteName: "openidea.io", + commentSectionId: "Idea#" + IdeaID + }); + //cactus chat end + }}; + xhttp.open("POST", "submitComment", true); + xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + xhttp.send("comment="+document.getElementById("comment").value+"&IdeaID="+IdeaID); +}; +export { sendComment }; \ No newline at end of file diff --git a/src/oi/matrix-wo-cactus.js b/src/oi/matrix-wo-cactus.js new file mode 100644 index 0000000000..a418d8657f --- /dev/null +++ b/src/oi/matrix-wo-cactus.js @@ -0,0 +1,157 @@ + +/** Function not in use - this is done by cactus comments + * function to create comment rooms for ideas + * @param {number} IdeaID - IdeaID for which a room should be created + * @param {string} [forCreation] - Display name of room, if not provided room will be namend 'comments for Idea #IdeaID' + * @returns {Room} - room object + */ +function createRoom(IdeaID, roomName){ + if(!openideaClient) loginOpenIdea(); + roomName = roomName || 'comments for Idea #' + IdeaID; + var options = { + name: roomName, + room_alias_name: RoomAlias(IdeaID, true), + visibility: "public" + }; + + console.log(options); + + return openideaClient.createRoom(options); +} + + +//TODO +/** + * function to fetch (num) last messages of room + * @param {number} IdeaID - IdeaID to fetch messages from + * @param {number} num - number of fetched messages + * @returns {(string|string[])} - returns num messages of matrix room asocciated with IdeaID + */ +async function getLastMessages(IdeaID, num){ + if(!openideaClient) loginOpenIdea(); + + await openideaClient.startClient({initialSyncLimit: 10}); +// await openideaClient.once('sync', function(state, prevState, res) { +// if(state === 'PREPARED') { +// console.log("prepared"); +// } else { +// console.log(state); +// process.exit(1); +// } +// }); + +var roomID = await openideaClient.joinRoom(RoomAlias(IdeaID)); +// try{ +// var room = await openideaClient.getRoom(roomID.roomId); +// } catch (e) {console.log(e);} + +var iTest = 0; + +await openideaClient.on("Room.timeline", function(event, room, toStartOfTimeline) { + if (event.getType() !== "m.room.message") { + return; // only use messages + } + //if(room.name == RoomAlias(IdeaID)) console.log(room); + if(room.roomId == roomID.roomId) console.log("finaly"); + console.log(iTest++); + console.log(room.roomId); + console.log(event.event.content.body); +}); + +// var scrollback = await openideaClient.scrollback(roomID); +// Object.keys(openideaClient.store.rooms).forEach((roomId) => { +// client.getRoom(roomId).timeline.forEach(t => { +// console.log(t.event); +// }); +// }); + + let messages; + //join room (even if joined already returns the roomID which is needed to write to it) + // var roomID = await openideaClient.joinRoom(RoomAlias(IdeaID)); + // try{ + // openideaClient.getRoom(roomID.roomId).timeline.forEach(t => { + // console.log(t.event); + // }); + + // // messages = openideaClient.scrollback(roomID.roomId, num); + // // console.log(messages); + // } catch (e) {console.log(e);} + + return messages; +} + +/** + * function to create consistant roomAliases + * @param {number} IdeaID - IdeaID which needs a room + * @param {boolean} [forCreation=false] - if true switches to only local part as return (like Idea_IdeaID) + * @returns {string} - roomAlias looks like this: #Idea_IdeaID:dohm.work + */ +function RoomAlias(IdeaID, forCreation=false){ + var roomAlias = 'Idea_' + IdeaID; + if(!forCreation) roomAlias = '#' + roomAlias + ':' + matrixGroupUrl; + return roomAlias; +} + +/** + * function to send usermessage to idea room + * @param {string} user - user which sends the message + * @param {number} IdeaID - number of idea hosting the room + * @param {string} message - usermessage + * @param {*} db - opened sqlite database to fetch users matrix data from + * @returns {string} - errormessage or undefined at success + */ +async function sendMessage(user, IdeaID, message, db) { + //get matrixname and matrixpw from database (db) + let matrix = await getMatrixUserByName(user, db); + console.log(matrix); + if(matrix == null || !matrix.matrixname || !matrix.matrixpw) return 'username not found'; + //matrix login + const client = sdk.createClient(matrixServer); + await client + .login("m.login.password", { + user: matrix.matrixname, + password: matrix.matrixpw, + }) + .then((response) => { + // console.log(response.access_token); + }); + await client.startClient(); + + //join room (even if joined already returns the roomID which is needed to write to it) + var roomID + try { + roomID = await client.joinRoom(RoomAlias(IdeaID)); + roomID = roomID.roomId; + } catch (e) { + if(e.errcode == 'M_NOT_FOUND') + { + try { + await createRoom(IdeaID); + roomID = await client.joinRoom(RoomAlias(IdeaID)); + roomID = roomID.roomId; + } + catch (e) {return "room coudn't be joined or created"} + } + } + try { + //create content for message to be send + var content = { + body: message, + msgtype: "m.text", + }; + //matrix send message to room + client + .sendEvent(roomID, "m.room.message", content, "") + .then((res) => { + // message sent successfully + return; + }) + .catch((err) => { + console.log(err); + return err; + }); + } + catch (e) { + return e; + } +}; \ No newline at end of file diff --git a/src/oi/matrix.js b/src/oi/matrix.js index 67f45bb243..51f0094a21 100644 --- a/src/oi/matrix.js +++ b/src/oi/matrix.js @@ -5,61 +5,42 @@ * @license MIT */ const https = require("https"); -const generator = require('generate-password'); +const generator = require("generate-password"); const crypto = require("crypto"); const sdk = require("matrix-js-sdk"); const { exitCode } = require("process"); const db = require("./sqliteDb"); const { RoomState } = require("matrix-js-sdk"); +const { resolve } = require("path"); +const { promiseMapSeries } = require("matrix-js-sdk/lib/utils"); /* url to the matrix server for crating new users often uses matrix.example.com */ -const matrixServer = "https://matrix.dohm.work"; +const matrixServer = "matrix.dohm.work"; /* url to append on matrix rooms often uses example.com */ -const matrixGroupUrl = "dohm.work"; +const matrixGroupUrl = "cactus.chat"; let urldata = { - host: matrixServer, - path: "/_synapse/admin/v1/register", - method: "GET", + host: matrixServer, + path: "/_synapse/admin/v1/register", + method: "GET", }; let urlRegData = { - nonce: "", - username: "", - displayname: "", - password: "", - mac: "", + nonce: "", + username: "", + displayname: "", + password: "", + mac: "", }; let matrix = { - matrixname: "", - matrixpw: "" + matrixname: "", + matrixpw: "", }; let openideaClient; -let oirooms = []; -const oiroom = {roomId: "", messages: []}; -const oimessage = {displayname: "", datetime: "", message: ""}; - -oimessage.displayname = "testuser"; -oimessage.datetime = "2021-03-31 23:17"; -oimessage.message = "test message"; -oiroom.roomId = "test Room 1"; -oiroom.messages.push(oimessage); - -oimessage.datetime = "2021-03-31 23:19"; -oimessage.message = "test message 2"; -oiroom.messages.push(oimessage); -oirooms.push(oiroom); - -console.log(oirooms); - -function roomAddMessage(room, displayname, datetime, message){ - RoomState.some(r => r.roomId === room) -} - /** * This function creates a matrix account for a given user. @@ -67,41 +48,32 @@ function roomAddMessage(room, displayname, datetime, message){ * for more informations visit: https://github.com/matrix-org/synapse/blob/master/docs/admin_api/register_api.rst * @param {string} username - wanted username of matrix user * @returns {object} - matrix object - * @property {string} matrixname - the given matrix username + * @property {string} matrixname - the given matrix username * @property {string} matrixpw - the automatically created matrix pw */ -function createUser(username) { - - //generate lowercase only charakters and underscores matrix name! - //make username lowercase - matrix.matrixname = username.toLowerCase(); - //replace spaces with underscores - matrix.matrixname = matrix.matrixname.replace(/\s+/g, '_'); - //delete all non alphanumeric and not underscore (shoudn't be necessary since they are not allowed as usernames anyway) - matrix.matrixname = matrix.matrixname.replace(/\W/g, ''); - // //TODO also add random string if matrixname exists - // if (matrix.matrixname.length < 6) { - // matrix.matrixname += '_' + generator.generate({ - // length: 12, - // numbers: true, - // uppercase: false - // }); - // } - urlRegData.username = matrix.matrixname; - urlRegData.displayname = username; - var password = generator.generate({ - length: 24, - numbers: true - }); - urlRegData.password = password; - matrix.matrixpw = password; - //for debugging and not loosing users in the beginning - console.log("matrixname" + matrix.matrixname); - console.log("pw" + password); - +async function createUser(username) { + return new Promise((resolve) => { + //generate lowercase only charakters and underscores matrix name! + //make username lowercase + matrix.matrixname = username.toLowerCase(); + //replace spaces with underscores + matrix.matrixname = matrix.matrixname.replace(/\s+/g, "_"); + //delete all non alphanumeric and not underscore (shoudn't be necessary since they are not allowed as usernames anyway) + matrix.matrixname = matrix.matrixname.replace(/\W/g, ""); + urlRegData.username = matrix.matrixname; + urlRegData.displayname = username; + var password = generator.generate({ + length: 24, + numbers: true, + }); + urlRegData.password = password; + matrix.matrixpw = password; + //for debugging and not loosing users in the beginning + console.log("matrixname: \t" + matrix.matrixname); + console.log("pw: \t" + password); function OnResponse(response) { - console.log(`statusCode 1: ${response.statusCode}`); + console.log(`statusCode 1: ${response.statusCode}`); var data = ""; //This will store the page we're downloading. response.on("data", function (chunk) { //Executed whenever a chunk is received. @@ -109,44 +81,44 @@ function createUser(username) { }); response.on("end", function () { - urlRegData.nonce = JSON.parse(data).nonce; - urlRegData.mac = generate_mac(urlRegData.nonce, urlRegData.username, urlRegData.password); - - var urlPostData = { - host: "matrix.dohm.work", - path: "/_synapse/admin/v1/register", - port: 443, - method: "POST", - headers: { - "Content-Type": "application/json", - "Content-Length": JSON.stringify(urlRegData).length, - }, - }; - // urlPostData.headers.Content-Length; - // JSON.stringify(urlRegData).length, - - var postreq = https.request(urlPostData, (res) => { + urlRegData.nonce = JSON.parse(data).nonce; + urlRegData.mac = generate_mac( + urlRegData.nonce, + urlRegData.username, + urlRegData.password + ); + + let urlPostData = { + host: "matrix.dohm.work", + path: "/_synapse/admin/v1/register", + port: 443, + method: "POST", + headers: { + "Content-Type": "application/json", + "Content-Length": JSON.stringify(urlRegData).length, + }, + }; + + let postreq = https.request(urlPostData, (res) => { console.log(`statusCode: ${res.statusCode}`); console.log("res: " + res.statusMessage); + resolve( matrix ); + // res.on("data", (d) => { // console.log(d); // }); }); - console.log(urlRegData); + console.log(urlRegData); postreq.write(JSON.stringify(urlRegData)); postreq.end(); }); } - console.log(urldata); + console.log(urldata); https.request(urldata, OnResponse).end(); - return matrix; -} - -function testF(){ - console.log("test"); +}); } /** @@ -157,19 +129,18 @@ function testF(){ * @param {boolean} [somebody=false] - wether or not the user should get admin rights * @returns {number} - mac in HEX */ -function generate_mac(nonce, user, password, admin=false){ - - var mac = crypto.createHmac("sha1", process.env.DOHMWORK_MATRIX_SS); - - mac.update(nonce.toString()); - mac.update("\x00"); - mac.update(user.toString()); - mac.update("\x00"); - mac.update(password.toString()); - mac.update("\x00"); - if(admin) mac.update("admin"); - else mac.update("notadmin"); - return mac.digest("hex"); +function generate_mac(nonce, user, password, admin = false) { + var mac = crypto.createHmac("sha1", process.env.DOHMWORK_MATRIX_SS); + + mac.update(nonce.toString()); + mac.update("\x00"); + mac.update(user.toString()); + mac.update("\x00"); + mac.update(password.toString()); + mac.update("\x00"); + if (admin) mac.update("admin"); + else mac.update("notadmin"); + return mac.digest("hex"); } /** @@ -177,75 +148,65 @@ function generate_mac(nonce, user, password, admin=false){ * @param {string} user - user which sends the message * @param {number} IdeaID - number of idea hosting the room * @param {string} message - usermessage - * @param {*} db - opened sqlite database to fetch users matrix data from + * @param {*} dbOpened - opened sqlite database to fetch users matrix data from * @returns {string} - errormessage or undefined at success */ -async function sendMessage(user, IdeaID, message, db) { - //get matrixname and matrixpw from database (db) - let matrix = await getMatrixUserByName(user, db); - console.log(matrix); - if(matrix == null || !matrix.matrixname || !matrix.matrixpw) return 'username not found'; - //matrix login - const client = sdk.createClient(matrixServer); - await client - .login("m.login.password", { - user: matrix.matrixname, - password: matrix.matrixpw, - }) - .then((response) => { - // console.log(response.access_token); - }); - await client.startClient(); - - //join room (even if joined already returns the roomID which is needed to write to it) - var roomID - try { - roomID = await client.joinRoom(RoomAlias(IdeaID)); - roomID = roomID.roomId; - } catch (e) { - if(e.errcode == 'M_NOT_FOUND') - { - try { - await createRoom(IdeaID); - roomID = await client.joinRoom(RoomAlias(IdeaID)); - roomID = roomID.roomId; - } - catch (e) {return "room coudn't be joined or created"} - } - } - try { - //create content for message to be send - var content = { - body: message, - msgtype: "m.text", - }; - //matrix send message to room - client - .sendEvent(roomID, "m.room.message", content, "") - .then((res) => { - // message sent successfully - return; - }) - .catch((err) => { - console.log(err); - return err; - }); - } - catch (e) { - return e; - } -}; +async function sendMessage(user, IdeaID, message, dbOpened) { + //get matrixname and matrixpw from database (db) + let matrix = await db.getMatrixUserByName(user, dbOpened); + console.log(matrix); + if (matrix == null || !matrix.matrixname || !matrix.matrixpw) + return "username not found"; + //matrix login + const client = sdk.createClient("https://" + matrixServer); + await client + .login("m.login.password", { + user: matrix.matrixname, + password: matrix.matrixpw, + }) + .then((response) => { + // console.log(response.access_token); + }); + await client.startClient(); + + //join room (even if joined already returns the roomID which is needed to write to it) + var roomID; + try { + roomID = await client.joinRoom(RoomAlias(IdeaID)); + roomID = roomID.roomId; + } catch (e) { + console.log(e); + } + try { + //create content for message to be send + var content = { + body: message, + msgtype: "m.text", + }; + //matrix send message to room + client + .sendEvent(roomID, "m.room.message", content, "") + .then((res) => { + // message sent successfully + return; + }) + .catch((err) => { + console.log(err); + return err; + }); + } catch (e) { + return e; + } +} /** * function to create consistant roomAliases * @param {number} IdeaID - IdeaID which needs a room - * @param {boolean} [forCreation=false] - if true switches to only local part as return (like Idea_IdeaID) - * @returns {string} - roomAlias looks like this: #Idea_IdeaID:dohm.work + * @returns {string} - roomAlias looks like this: #comments_openidea.io_Idea#IdeaId:cactus.chat */ -function RoomAlias(IdeaID, forCreation=false){ - var roomAlias = 'Idea_' + IdeaID; - if(!forCreation) roomAlias = '#' + roomAlias + ':' + matrixGroupUrl; - return roomAlias; +function RoomAlias(IdeaID) { + var roomAlias = "#comments_openidea.io_Idea#" + IdeaID + ":" + matrixGroupUrl; + return roomAlias; } /** @@ -253,108 +214,54 @@ function RoomAlias(IdeaID, forCreation=false){ * @param {number} IdeaID - IdearID of idea of which the url is requested * @returns {string} - url to join room looks like this: https://matrix.to/#/#Idea_IdeaID:dohm.work */ -function getJoinUrl(IdeaID){ - let url = 'https://matrix.to/#/' + RoomAlias(IdeaID); - return url; +function getJoinUrl(IdeaID) { + let url = "https://matrix.to/#/" + RoomAlias(IdeaID); + return url; } -//TODO /** - * function to fetch (num) last messages of room - * @param {number} IdeaID - IdeaID to fetch messages from - * @param {number} num - number of fetched messages - * @returns {(string|string[])} - returns num messages of matrix room asocciated with IdeaID + * function to call on init. + * This function handels the login of the openidea user which is used for reports etc. */ -async function getLastMessages(IdeaID, num){ - if(!openideaClient) loginOpenIdea(); - - await openideaClient.startClient({initialSyncLimit: 10}); -// await openideaClient.once('sync', function(state, prevState, res) { -// if(state === 'PREPARED') { -// console.log("prepared"); -// } else { -// console.log(state); -// process.exit(1); -// } -// }); - -var roomID = await openideaClient.joinRoom(RoomAlias(IdeaID)); -// try{ -// var room = await openideaClient.getRoom(roomID.roomId); -// } catch (e) {console.log(e);} - -var iTest = 0; - -await openideaClient.on("Room.timeline", function(event, room, toStartOfTimeline) { - if (event.getType() !== "m.room.message") { - return; // only use messages - } - //if(room.name == RoomAlias(IdeaID)) console.log(room); - if(room.roomId == roomID.roomId) console.log("finaly"); - console.log(iTest++); - console.log(room.roomId); - console.log(event.event.content.body); -}); - -// var scrollback = await openideaClient.scrollback(roomID); -// Object.keys(openideaClient.store.rooms).forEach((roomId) => { -// client.getRoom(roomId).timeline.forEach(t => { -// console.log(t.event); -// }); -// }); - - let messages; - //join room (even if joined already returns the roomID which is needed to write to it) - // var roomID = await openideaClient.joinRoom(RoomAlias(IdeaID)); - // try{ - // openideaClient.getRoom(roomID.roomId).timeline.forEach(t => { - // console.log(t.event); - // }); - - // // messages = openideaClient.scrollback(roomID.roomId, num); - // // console.log(messages); - // } catch (e) {console.log(e);} - - return messages; -} - -//TODO function loginOpenIdea() { - //return if openideaClint was already set - if (openideaClient) return; - openideaClient = sdk.createClient(matrixServer); - openideaClient - .login("m.login.password", { - user: process.env.MATRIX_DW_USER, - password: process.env.MATRIX_DW_PASSWORD, - }) - .then((response) => { - console.log(response.access_token); - }); + //return if openideaClint was already set + if (openideaClient) return; + openideaClient = sdk.createClient("https://" + matrixServer); + openideaClient + .login("m.login.password", { + user: process.env.MATRIX_DW_USER, + password: process.env.MATRIX_DW_PASSWORD, + }) + .then((response) => { + console.log(response.access_token); + }); } - /** - * function to create comment rooms for ideas - * @param {number} IdeaID - IdeaID for which a room should be created - * @param {string} [forCreation] - Display name of room, if not provided room will be namend 'comments for Idea #IdeaID' - * @returns {Room} - room object + * function to send support message to support chat + * @param {Object} req - req object, this functions reads the body.IdeaID, body.text, req.user */ -function createRoom(IdeaID, roomName){ +function sendSupportRequest(req) { if(!openideaClient) loginOpenIdea(); - roomName = roomName || 'comments for Idea #' + IdeaID; - var options = { - name: roomName, - room_alias_name: RoomAlias(IdeaID, true), - visibility: "public" - }; + openideaClient.startClient(); - console.log(options); + var RoomId = "!HTcpkpXWLaaunauYsD:cactus.chat"; - return openideaClient.createRoom(options); -} + var content = { + body: "#" + req.body.IdeaID + "\n" + req.body.text + "\n\nsend from: ", + msgtype: "m.text", + }; + if (req.user) { + content.body += req.user.email; + } else { + content.body += "anonymous"; + } -//TODO new support request -//TODO move matrix user from myServer to this file + openideaClient + .sendEvent(RoomId, "m.room.message", content, "") + .catch((err) => { + console.log(err); + }); +} -module.exports = {testF, createUser, sendMessage, getJoinUrl, getLastMessages, createRoom, loginOpenIdea}; +module.exports = { createUser, sendMessage, getJoinUrl, loginOpenIdea, sendSupportRequest}; \ No newline at end of file