6 Commits
0.2.4 ... 0.3.0

Author SHA1 Message Date
Skylar Sadlier
25c92b787a - Messages used to get ignored if they were over 1 second old. This caused issues with messages being ignored on servers that took longer than a second to respond (*cough* matrix.org *cough*). Now it accepts all new messages. Moving forward any messages that are sent before Node-RED starts or gets deployed will be ignored (so that old messages don't pop up since the cache clears every deploy/node-red restart so old messages look like new ones).
- When the matrix-server node would shut down (from a deployment) it wasn't properly shutting the old instance of the client down. This would cause messages to start duplicating X times (X being however many times you deployed since you last started Node-RED). This is now fixed.
- Upgraded to matrix-js-sdk 15.3.0
- Message processing is now done on the matrix-server node. Before if you had multiple matrix-receive nodes it would output one line per matrix-receive node into the log. Now it only outputs from the matrix-server node. Note that all messages get logged that are sent to rooms the bot is in whereas before it would only log message in rooms the matrix-receive node was in.
- Fixed Shared secret registration failing if user_type was defined.
2022-01-14 12:56:14 -07:00
Skylar Sadlier
0b9c59a7be Fix eventId not being returned from matrix-send-file and matrix-send-message nodes 2021-12-19 00:34:51 -07:00
Skylar Sadlier
9c836c9262 Set version 0.2.6 2021-11-25 00:04:09 -07:00
Skylar Sadlier
9c62a73b15 Merge pull request #35 from Skylar-Tech/dev
Dev
2021-11-25 00:02:01 -07:00
Skylar Sadlier
447b18835c Closes #34 - Fix connection state logic 2021-11-25 00:00:31 -07:00
Skylar Sadlier
2341cbef10 Closes #32 - fixed leaking unencrypted messages during startup 2021-11-24 23:57:56 -07:00
8 changed files with 7838 additions and 2104 deletions

9748
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
{
"name": "node-red-contrib-matrix-chat",
"version": "0.2.4",
"version": "0.3.0",
"description": "Matrix chat server client for Node-RED",
"dependencies": {
"fs-extra": "^9.1.0",
"got": "^11.8.2",
"isomorphic-webcrypto": "^2.3.8",
"matrix-js-sdk": "^12.2.0",
"matrix-js-sdk": "^15.3.0",
"node-localstorage": "^2.2.1",
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
"process": "^0.11.10",

View File

@@ -22,7 +22,7 @@ module.exports = function(RED) {
return;
}
node.server.on("disconnected", function() {
node.server.on("disconnected", function(){
node.status({ fill: "red", shape: "ring", text: "disconnected" });
});
@@ -30,43 +30,12 @@ module.exports = function(RED) {
node.status({ fill: "green", shape: "ring", text: "connected" });
});
node.server.on("Room.timeline", async function(event, room, toStartOfTimeline, data) {
if (toStartOfTimeline) {
return; // ignore paginated results
}
if (!event.getSender() || event.getSender() === node.server.userId) {
return; // ignore our own messages
}
if (!event.getUnsigned() || event.getUnsigned().age > 1000) {
return; // ignore old messages
}
node.server.on("Room.timeline", async function(event, room, toStartOfTimeline, removed, data, msg) {
// if node has a room ID set we only listen on that room
if(node.roomIds.length && node.roomIds.indexOf(room.roomId) === -1) {
return;
}
try {
await node.server.matrixClient.decryptEventIfNeeded(event);
} catch (error) {
node.error(error);
return;
}
let msg = {
encrypted : event.isEncrypted(),
redacted : event.isRedacted(),
content : event.getContent(),
type : (event.getContent()['msgtype'] || event.getType()) || null,
payload : (event.getContent()['body'] || event.getContent()) || null,
userId : event.getSender(),
topic : event.getRoomId(),
eventId : event.getId(),
event : event,
};
node.log("Received" + (msg.encrypted ? ' encrypted' : '') +" timeline event [" + msg.type + "]: (" + room.name + ") " + event.getSender() + " :: " + msg.content.body);
switch(msg.type) {
case 'm.emote':
if(!node.acceptEmotes) return;

View File

@@ -80,7 +80,7 @@ module.exports = function(RED) {
.sendMessage(msg.topic, content)
.then(function(e) {
node.log("File message sent: " + e);
msg.eventId = e.eventId;
msg.eventId = e.event_id;
node.send([msg, null]);
})
.catch(function(e){

View File

@@ -89,6 +89,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed");
node.send([null, msg]);
return;
}
msg.topic = node.roomId || msg.topic;
@@ -118,7 +119,7 @@ module.exports = function(RED) {
node.server.matrixClient.sendMessage(msg.topic, content)
.then(function(e) {
node.log("Message sent: " + msg.payload);
msg.eventId = e.eventId;
msg.eventId = e.event_id;
node.send([msg, null]);
})
.catch(function(e){

View File

@@ -35,6 +35,7 @@ module.exports = function(RED) {
this.enableE2ee = n.enableE2ee || false;
this.e2ee = (this.enableE2ee && this.deviceId);
this.globalAccess = n.global;
this.initializedAt = new Date();
if(!this.credentials.accessToken) {
node.log("Matrix connection failed: missing access token.");
@@ -85,7 +86,6 @@ module.exports = function(RED) {
node.on('close', function(done) {
if(node.matrixClient) {
node.matrixClient.close();
node.matrixClient.stopClient();
node.setConnected(false);
}
@@ -97,8 +97,41 @@ module.exports = function(RED) {
return node.connected;
};
node.matrixClient.on("Room.timeline", async function(event, room, toStartOfTimeline, data) {
node.emit("Room.timeline", event, room, toStartOfTimeline, data);
node.matrixClient.on("Room.timeline", async function(event, room, toStartOfTimeline, removed, data) {
if (toStartOfTimeline) {
return; // ignore paginated results
}
if (!event.getSender() || event.getSender() === node.userId) {
return; // ignore our own messages
}
if (!data || !data.liveEvent) {
return; // ignore old message (we only want live events)
}
if(node.initializedAt > event.getDate()) {
return; // skip events that occurred before our client initialized
}
try {
await node.matrixClient.decryptEventIfNeeded(event);
} catch (error) {
node.error(error);
return;
}
let msg = {
encrypted : event.isEncrypted(),
redacted : event.isRedacted(),
content : event.getContent(),
type : (event.getContent()['msgtype'] || event.getType()) || null,
payload : (event.getContent()['body'] || event.getContent()) || null,
userId : event.getSender(),
topic : event.getRoomId(),
eventId : event.getId(),
event : event,
};
node.log("Received" + (msg.encrypted ? ' encrypted' : '') +" timeline event [" + msg.type + "]: (" + room.name + ") " + event.getSender() + " :: " + msg.content.body + (data.liveEvent ? ' [LIVE]' : '') + (toStartOfTimeline ? ' [PAGINATED]' : ''));
node.emit("Room.timeline", event, room, toStartOfTimeline, removed, data, msg);
});
// node.matrixClient.on("RoomMember.typing", async function(event, member) {
@@ -133,36 +166,70 @@ module.exports = function(RED) {
});
node.matrixClient.on("sync", async function(state, prevState, data) {
switch (state) {
case "ERROR":
node.setConnected(false, function(){
node.error("Connection to Matrix server lost");
});
break;
case "RECONNECTING":
node.setConnected(false, function(){
node.log("Trying to reconnect to matrix server");
});
break;
case "STOPPED":
node.setConnected(false, function(){
node.log("Matrix client stopped");
});
break;
case "SYNCING":
case "PREPARED":
node.setConnected(true, function(){
node.log("Matrix client connected");
});
break;
// case "PREPARED":
// // the client instance is ready to be queried.
// node.log("Matrix server connection ready.");
// node.setConnected(true);
// break;
node.debug("SYNC [STATE=" + state + "] [PREVSTATE=" + prevState + "]");
if(prevState === null && state === "PREPARED" ) {
// Occurs when the initial sync is completed first time.
// This involves setting up filters and obtaining push rules.
node.setConnected(true, function(){
node.log("Matrix client connected");
});
} else if(prevState === null && state === "ERROR") {
// Occurs when the initial sync failed first time.
node.setConnected(false, function(){
node.error("Failed to connect to Matrix server");
});
} else if(prevState === "ERROR" && state === "PREPARED") {
// Occurs when the initial sync succeeds
// after previously failing.
node.setConnected(true, function(){
node.log("Matrix client connected");
});
} else if(prevState === "PREPARED" && state === "SYNCING") {
// Occurs immediately after transitioning to PREPARED.
// Starts listening for live updates rather than catching up.
node.setConnected(true, function(){
node.log("Matrix client connected");
});
} else if(prevState === "SYNCING" && state === "RECONNECTING") {
// Occurs when the live update fails.
node.setConnected(false, function(){
node.error("Connection to Matrix server lost");
});
} else if(prevState === "RECONNECTING" && state === "RECONNECTING") {
// Can occur if the update calls continue to fail,
// but the keepalive calls (to /versions) succeed.
node.setConnected(false, function(){
node.error("Connection to Matrix server lost");
});
} else if(prevState === "RECONNECTING" && state === "ERROR") {
// Occurs when the keepalive call also fails
node.setConnected(false, function(){
node.error("Connection to Matrix server lost");
});
} else if(prevState === "ERROR" && state === "SYNCING") {
// Occurs when the client has performed a
// live update after having previously failed.
node.setConnected(true, function(){
node.log("Matrix client connected");
});
} else if(prevState === "ERROR" && state === "ERROR") {
// Occurs when the client has failed to
// keepalive for a second time or more.
node.setConnected(false, function(){
node.error("Connection to Matrix server lost");
});
} else if(prevState === "SYNCING" && state === "SYNCING") {
// Occurs when the client has performed a live update.
// This is called <i>after</i> processing.
node.setConnected(true, function(){
node.log("Matrix client connected");
});
} else if(state === "STOPPED") {
// Occurs once the client has stopped syncing or
// trying to sync after stopClient has been called.
node.setConnected(false, function(){
node.error("Connection to Matrix server lost");
});
}
});
@@ -189,7 +256,9 @@ module.exports = function(RED) {
node.matrixClient.setGlobalErrorOnUnknownDevices(false);
}
node.log("Connecting to Matrix server...");
await node.matrixClient.startClient({ initialSyncLimit: 8 });
await node.matrixClient.startClient({
initialSyncLimit: 8
});
} catch(error){
node.error(error);
}

View File

@@ -78,7 +78,10 @@
<dt class="optional">msg.payload.user_type
<span class="property-type">string | null</span>
</dt>
<dd> Set the user type. Leave this to null if you don't know what it is for. Check <a href="https://github.com/matrix-org/synapse/blob/master/synapse/api/constants.py">here</a> and look for <code>class UserTypes</code> to figure out what is valid.</dd>
<dd>
Set the user type. Leave this to null if you don't know what it is for. Check <a href="https://github.com/matrix-org/synapse/blob/master/synapse/api/constants.py">here</a> and look for <code>class UserTypes</code> to figure out what is valid.<br>
NOTE: Testing shows that "bot" and "support" do not work for this. Generate a normal user to use as a bot.
</dd>
</dl>
<h3>Outputs</h3>

View File

@@ -62,7 +62,13 @@ module.exports = function(RED) {
.update(utf8.encode(msg.payload.password))
.update("\x00")
.update(msg.payload.admin ? "admin" : "notadmin")
.digest('hex');
if(msg.payload.user_type || null) {
hmac.update("\x00")
.update(msg.payload.user_type);
}
hmac = hmac.digest('hex');
try {
response = await got.post(this.server + '/_synapse/admin/v1/register', {