node-red-contrib-matrix-chat/src/matrix-send-message.js
Skylar Sadlier 351679ad77 - Add image thumbnail generation support for file upload node (and improved logging) #102
- Fix Received images missing thumbnail kills Node Red #65
- Trim rooms provided in receive node's Room ID config value
2024-06-13 20:21:20 -06:00

205 lines
7.3 KiB
JavaScript

const {RelationType} = require("matrix-js-sdk");
module.exports = function(RED) {
function MatrixSendImage(n) {
RED.nodes.createNode(this, n);
var node = this;
this.name = n.name;
this.server = RED.nodes.getNode(n.server);
this.roomId = n.roomId;
this.messageType = n.messageType;
this.messageFormat = n.messageFormat;
this.replaceMessage = n.replaceMessage;
this.message = n.message;
this.threadReplyType = n.threadReplyType || null;
this.threadReplyValue = n.threadReplyValue || null;
// taken from https://github.com/matrix-org/synapse/blob/master/synapse/push/mailer.py
this.allowedTags = [
"font", // custom to matrix for IRC-style font coloring
"del", // for markdown
// deliberately no h1/h2 to stop people shouting.
"h3",
"h4",
"h5",
"h6",
"blockquote",
"p",
"a",
"ul",
"ol",
"nl",
"li",
"b",
"i",
"u",
"strong",
"em",
"strike",
"code",
"hr",
"br",
"div",
"table",
"thead",
"caption",
"tbody",
"tr",
"th",
"td",
"pre",
];
if (!node.server) {
node.warn("No configuration node");
return;
}
node.server.register(node);
node.status({ fill: "red", shape: "ring", text: "disconnected" });
node.server.on("disconnected", function(){
node.status({ fill: "red", shape: "ring", text: "disconnected" });
});
node.server.on("connected", function() {
node.status({ fill: "green", shape: "ring", text: "connected" });
});
node.on("input", function (msg) {
function getToValue(msg, type, property) {
let value = property;
if (type === "msg") {
value = RED.util.getMessageProperty(msg, property);
} else if ((type === 'flow') || (type === 'global')) {
try {
value = RED.util.evaluateNodeProperty(property, type, node, msg);
} catch(e2) {
throw new Error("Invalid value evaluation");
}
} else if(type === "bool") {
value = (property === 'true');
} else if(type === "num") {
value = Number(property);
}
return value;
}
let msgType = node.messageType,
msgFormat = node.messageFormat,
threadReply = getToValue(msg, node.threadReplyType, node.threadReplyValue);
if (!node.server || !node.server.matrixClient) {
node.warn("No matrix server selected");
return;
}
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
return;
}
msg.topic = node.roomId || msg.topic;
if(!msg.topic) {
node.warn("Room must be specified in msg.topic or in configuration");
return;
}
let payload = n.message || msg.payload;
if(!payload) {
node.error('msg.payload must be defined or the message configured on the node.', msg);
return;
}
let content = null;
if(typeof payload === 'object') {
content = payload;
} else {
if(msgType === 'msg.type') {
if(!msg.type) {
node.error("msg.type type is set to be passed in via msg.type but was not defined", msg);
return;
}
msgType = msg.type;
}
if(msgFormat === 'msg.format') {
if(!msg.format) {
node.error("Message format is set to be passed in via msg.format but was not defined", msg);
return;
}
msgFormat = msg.format;
}
content = {
msgtype: msgType,
body: payload.toString()
};
if(msgFormat === 'html') {
content.format = "org.matrix.custom.html";
content.formatted_body =
(typeof msg.formatted_payload !== 'undefined' && msg.formatted_payload)
? msg.formatted_payload.toString()
: payload.toString();
}
if((node.replaceMessage || msg.replace) && msg.eventId) {
content['m.new_content'] = {
msgtype: content.msgtype,
body: content.body
};
if('format' in content) {
content['m.new_content']['format'] = content['format'];
}
if('formatted_body' in content) {
content['m.new_content']['formatted_body'] = content['formatted_body'];
}
content['m.relates_to'] = {
rel_type: RelationType.Replace,
event_id: msg.eventId
};
content['body'] = ' * ' + content['body'];
} else if(threadReply) {
// if incoming message is a reply to a thread we fetch the thread parent from the m.relates_to property
// otherwise fallback to msg.eventId
let threadParent = (msg?.content?.['m.relates_to']?.rel_type === RelationType.Thread ? msg?.content?.['m.relates_to']?.event_id : null) || msg.eventId;
if(threadParent) {
content["m.relates_to"] = {
"rel_type": RelationType.Thread,
"event_id": threadParent,
"is_falling_back": true,
};
if(msg.eventId !== threadParent) {
content["m.relates_to"]["m.in_reply_to"] = {
"event_id": msg.eventId
};
}
}
}
}
node.server.matrixClient.sendMessage(msg.topic, content)
.then(function(e) {
node.log("Message sent: " + payload);
msg.eventId = e.event_id;
node.log(JSON.stringify(e));
node.send([msg, null]);
})
.catch(function(e){
node.error("Error sending message: " + e, {});
msg.error = e;
node.send([null, msg]);
});
});
node.on("close", function() {
node.server.deregister(node);
});
}
RED.nodes.registerType("matrix-send-message", MatrixSendImage);
}