mirror of
https://github.com/plantroon/mx-puppet-xmpp.git
synced 2024-11-14 23:41:40 +00:00
implement quotes
This commit is contained in:
parent
9054d7a18f
commit
3f161b27cb
6
package-lock.json
generated
6
package-lock.json
generated
@ -98,9 +98,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sorunome/skype-http": {
|
"@sorunome/skype-http": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@sorunome/skype-http/-/skype-http-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sorunome/skype-http/-/skype-http-1.5.2.tgz",
|
||||||
"integrity": "sha512-dJ0fMeTmtzTUWbXN3+SCth8C9XElhrEkzbp454xyo0vXYyoPMbIUYuWeEas1hyeYjaqI9PdgTOe5xI9y+qr9/g==",
|
"integrity": "sha512-NAxvVVHIi7G/9v5RbB3vkNqbgy2CB+puE6ZvwX1ha4+PCP5MTy5KZRfrhOpIVyTLmTEXS50USl/ixAek5RwLlA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"async-file": "^2.0.2",
|
"async-file": "^2.0.2",
|
||||||
"big-integer": "^1.6.26",
|
"big-integer": "^1.6.26",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"author": "Sorunome",
|
"author": "Sorunome",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sorunome/skype-http": "^1.5.1",
|
"@sorunome/skype-http": "^1.5.2",
|
||||||
"cheerio": "^1.0.0-rc.3",
|
"cheerio": "^1.0.0-rc.3",
|
||||||
"command-line-args": "^5.1.1",
|
"command-line-args": "^5.1.1",
|
||||||
"command-line-usage": "^5.0.5",
|
"command-line-usage": "^5.0.5",
|
||||||
|
@ -62,6 +62,7 @@ const protocol: IProtocolInformation = {
|
|||||||
audio: true,
|
audio: true,
|
||||||
file: true,
|
file: true,
|
||||||
edit: true,
|
edit: true,
|
||||||
|
reply: true,
|
||||||
globalNamespace: true,
|
globalNamespace: true,
|
||||||
},
|
},
|
||||||
id: "skype",
|
id: "skype",
|
||||||
@ -94,6 +95,7 @@ async function run() {
|
|||||||
puppet.on("puppetDelete", skype.deletePuppet.bind(skype));
|
puppet.on("puppetDelete", skype.deletePuppet.bind(skype));
|
||||||
puppet.on("message", skype.handleMatrixMessage.bind(skype));
|
puppet.on("message", skype.handleMatrixMessage.bind(skype));
|
||||||
puppet.on("edit", skype.handleMatrixEdit.bind(skype));
|
puppet.on("edit", skype.handleMatrixEdit.bind(skype));
|
||||||
|
puppet.on("reply", skype.handleMatrixReply.bind(skype));
|
||||||
puppet.on("redact", skype.handleMatrixRedact.bind(skype));
|
puppet.on("redact", skype.handleMatrixRedact.bind(skype));
|
||||||
puppet.on("image", skype.handleMatrixImage.bind(skype));
|
puppet.on("image", skype.handleMatrixImage.bind(skype));
|
||||||
puppet.on("audio", skype.handleMatrixAudio.bind(skype));
|
puppet.on("audio", skype.handleMatrixAudio.bind(skype));
|
||||||
|
@ -52,8 +52,12 @@ export class MatrixMessageParser {
|
|||||||
const inner = this.walkChildNodes(nodeHtml);
|
const inner = this.walkChildNodes(nodeHtml);
|
||||||
return `<a href="${escapeHtml(href)}">${inner}</a>`;
|
return `<a href="${escapeHtml(href)}">${inner}</a>`;
|
||||||
}
|
}
|
||||||
|
case "blockquote":
|
||||||
|
return `<quote>${this.walkChildNodes(nodeHtml)}</quote>`;
|
||||||
case "wrap":
|
case "wrap":
|
||||||
return this.walkChildNodes(nodeHtml);
|
return this.walkChildNodes(nodeHtml);
|
||||||
|
case "mx-reply": // disgard replies
|
||||||
|
return "";
|
||||||
default:
|
default:
|
||||||
if (!nodeHtml.tagName) {
|
if (!nodeHtml.tagName) {
|
||||||
return this.walkChildNodes(nodeHtml);
|
return this.walkChildNodes(nodeHtml);
|
||||||
|
70
src/skype.ts
70
src/skype.ts
@ -409,6 +409,63 @@ export class Skype {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async handleMatrixReply(room: IRemoteRoom, eventId: string, data: IMessageEvent) {
|
||||||
|
const p = this.puppets[room.puppetId];
|
||||||
|
if (!p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info("Received reply from matrix");
|
||||||
|
const conversation = await p.client.getConversation(room);
|
||||||
|
if (!conversation) {
|
||||||
|
log.warn(`Room ${room.roomId} not found!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let msg: string;
|
||||||
|
if (data.formattedBody) {
|
||||||
|
msg = this.matrixMessageParser.parse(data.formattedBody);
|
||||||
|
} else {
|
||||||
|
msg = escapeHtml(data.body);
|
||||||
|
}
|
||||||
|
// now prepend the reply
|
||||||
|
const author = escapeHtml(p.client.username.substr(p.client.username.indexOf(":") + 1));
|
||||||
|
const ownContact = await p.client.getContact(p.client.username);
|
||||||
|
const authorname = escapeHtml(ownContact ? ownContact.displayName : p.client.username);
|
||||||
|
const conversationId = escapeHtml(conversation.id);
|
||||||
|
const timestamp = Math.round(Number(eventId) / 1000).toString();
|
||||||
|
const origEventId = (await this.puppet.eventSync.getMatrix(room.puppetId, eventId))[0];
|
||||||
|
let contents = "blah";
|
||||||
|
if (origEventId) {
|
||||||
|
const [realOrigEventId, roomId] = origEventId.split(";");
|
||||||
|
try {
|
||||||
|
const client = (await this.puppet.roomSync.getRoomOp(roomId)) || this.puppet.botIntent.underlyingClient;
|
||||||
|
const evt = await client.getEvent(roomId, realOrigEventId);
|
||||||
|
if (evt && evt.content && typeof evt.content.body === "string") {
|
||||||
|
if (evt.content.formatted_body) {
|
||||||
|
contents = this.matrixMessageParser.parse(evt.content.formatted_body);
|
||||||
|
} else {
|
||||||
|
contents = escapeHtml(evt.content.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
log.verbose("Event not found", err.body || err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const quote = `<quote author="${author}" authorname="${authorname}" timestamp="${timestamp}" ` +
|
||||||
|
`conversation="${conversationId}" messageid="${escapeHtml(eventId)}">` +
|
||||||
|
`<legacyquote>[${timestamp}] ${authorname}: </legacyquote>${contents}<legacyquote>
|
||||||
|
|
||||||
|
<<< </legacyquote></quote>`;
|
||||||
|
msg = quote + msg;
|
||||||
|
const dedupeKey = `${room.puppetId};${room.roomId}`;
|
||||||
|
this.messageDeduplicator.lock(dedupeKey, p.client.username, msg);
|
||||||
|
const ret = await p.client.sendMessage(conversation.id, msg);
|
||||||
|
const newEventId = ret && ret.MessageId;
|
||||||
|
this.messageDeduplicator.unlock(dedupeKey, p.client.username, newEventId);
|
||||||
|
if (newEventId) {
|
||||||
|
await this.puppet.eventSync.insert(room.puppetId, data.eventId!, newEventId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async handleMatrixRedact(room: IRemoteRoom, eventId: string) {
|
public async handleMatrixRedact(room: IRemoteRoom, eventId: string) {
|
||||||
const p = this.puppets[room.puppetId];
|
const p = this.puppets[room.puppetId];
|
||||||
if (!p) {
|
if (!p) {
|
||||||
@ -514,6 +571,17 @@ export class Skype {
|
|||||||
log.silly("normal message dedupe");
|
log.silly("normal message dedupe");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (rich && msg.trim().startsWith("<quote")) {
|
||||||
|
// okay, we might have a reply...
|
||||||
|
const $ = cheerio.load(msg);
|
||||||
|
const quote = $("quote");
|
||||||
|
const messageid = quote.attr("messageid");
|
||||||
|
if (messageid) {
|
||||||
|
const sendQuoteMsg = this.skypeMessageParser.parse(msg, { noQuotes: true });
|
||||||
|
await this.puppet.sendReply(params, messageid, sendQuoteMsg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
let sendMsg: IMessageEvent;
|
let sendMsg: IMessageEvent;
|
||||||
if (rich) {
|
if (rich) {
|
||||||
sendMsg = this.skypeMessageParser.parse(msg);
|
sendMsg = this.skypeMessageParser.parse(msg);
|
||||||
@ -557,7 +625,7 @@ export class Skype {
|
|||||||
}
|
}
|
||||||
let sendMsg: IMessageEvent;
|
let sendMsg: IMessageEvent;
|
||||||
if (rich) {
|
if (rich) {
|
||||||
sendMsg = this.skypeMessageParser.parse(msg);
|
sendMsg = this.skypeMessageParser.parse(msg, { noQuotes: msg.trim().startsWith("<quote") });
|
||||||
} else {
|
} else {
|
||||||
sendMsg = {
|
sendMsg = {
|
||||||
body: msg,
|
body: msg,
|
||||||
|
@ -17,23 +17,30 @@ import * as escapeHtml from "escape-html";
|
|||||||
import { IMessageEvent } from "mx-puppet-bridge";
|
import { IMessageEvent } from "mx-puppet-bridge";
|
||||||
import * as emoji from "node-emoji";
|
import * as emoji from "node-emoji";
|
||||||
|
|
||||||
|
interface ISkypeMessageParserOpts {
|
||||||
|
noQuotes?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class SkypeMessageParser {
|
export class SkypeMessageParser {
|
||||||
public parse(msg: string): IMessageEvent {
|
public parse(msg: string, opts: ISkypeMessageParserOpts = {}): IMessageEvent {
|
||||||
|
opts = Object.assign({
|
||||||
|
noQuotes: false,
|
||||||
|
}, opts);
|
||||||
const nodes = Parser.parse(`<wrap>${msg}</wrap>`, {
|
const nodes = Parser.parse(`<wrap>${msg}</wrap>`, {
|
||||||
lowerCaseTagName: true,
|
lowerCaseTagName: true,
|
||||||
pre: true,
|
pre: true,
|
||||||
});
|
});
|
||||||
return this.walkNode(nodes);
|
return this.walkNode(nodes, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private walkChildNodes(node: Parser.Node): IMessageEvent {
|
private walkChildNodes(node: Parser.Node, opts: ISkypeMessageParserOpts): IMessageEvent {
|
||||||
if (!node.childNodes.length) {
|
if (!node.childNodes.length) {
|
||||||
return {
|
return {
|
||||||
body: "",
|
body: "",
|
||||||
formattedBody: "",
|
formattedBody: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return node.childNodes.map((n) => this.walkNode(n)).reduce((acc, curr) => {
|
return node.childNodes.map((n) => this.walkNode(n, opts)).reduce((acc, curr) => {
|
||||||
return {
|
return {
|
||||||
body: acc.body + curr.body,
|
body: acc.body + curr.body,
|
||||||
formattedBody: acc.formattedBody! + curr.formattedBody!,
|
formattedBody: acc.formattedBody! + curr.formattedBody!,
|
||||||
@ -48,35 +55,35 @@ export class SkypeMessageParser {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private walkNode(node: Parser.Node): IMessageEvent {
|
private walkNode(node: Parser.Node, opts: ISkypeMessageParserOpts): IMessageEvent {
|
||||||
if (node.nodeType === Parser.NodeType.TEXT_NODE) {
|
if (node.nodeType === Parser.NodeType.TEXT_NODE) {
|
||||||
return this.escape((node as Parser.TextNode).text);
|
return this.escape((node as Parser.TextNode).text);
|
||||||
} else if (node.nodeType === Parser.NodeType.ELEMENT_NODE) {
|
} else if (node.nodeType === Parser.NodeType.ELEMENT_NODE) {
|
||||||
const nodeHtml = node as Parser.HTMLElement;
|
const nodeHtml = node as Parser.HTMLElement;
|
||||||
switch (nodeHtml.tagName) {
|
switch (nodeHtml.tagName) {
|
||||||
case "i": {
|
case "i": {
|
||||||
const child = this.walkChildNodes(nodeHtml);
|
const child = this.walkChildNodes(nodeHtml, opts);
|
||||||
return {
|
return {
|
||||||
body: `_${child.body}_`,
|
body: `_${child.body}_`,
|
||||||
formattedBody: `<em>${child.formattedBody}</em>`,
|
formattedBody: `<em>${child.formattedBody}</em>`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "b": {
|
case "b": {
|
||||||
const child = this.walkChildNodes(nodeHtml);
|
const child = this.walkChildNodes(nodeHtml, opts);
|
||||||
return {
|
return {
|
||||||
body: `*${child.body}*`,
|
body: `*${child.body}*`,
|
||||||
formattedBody: `<strong>${child.formattedBody}</strong>`,
|
formattedBody: `<strong>${child.formattedBody}</strong>`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "s": {
|
case "s": {
|
||||||
const child = this.walkChildNodes(nodeHtml);
|
const child = this.walkChildNodes(nodeHtml, opts);
|
||||||
return {
|
return {
|
||||||
body: `~${child.body}~`,
|
body: `~${child.body}~`,
|
||||||
formattedBody: `<del>${child.formattedBody}</del>`,
|
formattedBody: `<del>${child.formattedBody}</del>`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "pre": {
|
case "pre": {
|
||||||
const child = this.walkChildNodes(nodeHtml);
|
const child = this.walkChildNodes(nodeHtml, opts);
|
||||||
return {
|
return {
|
||||||
body: `{code}${child.body}{code}`,
|
body: `{code}${child.body}{code}`,
|
||||||
formattedBody: `<code>${child.formattedBody}</code>`,
|
formattedBody: `<code>${child.formattedBody}</code>`,
|
||||||
@ -84,12 +91,25 @@ export class SkypeMessageParser {
|
|||||||
}
|
}
|
||||||
case "a": {
|
case "a": {
|
||||||
const href = nodeHtml.attributes.href;
|
const href = nodeHtml.attributes.href;
|
||||||
const child = this.walkChildNodes(nodeHtml);
|
const child = this.walkChildNodes(nodeHtml, opts);
|
||||||
return {
|
return {
|
||||||
body: child.body === href ? href : `[${child.body}](${href})`,
|
body: child.body === href ? href : `[${child.body}](${href})`,
|
||||||
formattedBody: `<a href="${escapeHtml(href)}">${child.formattedBody}</a>`,
|
formattedBody: `<a href="${escapeHtml(href)}">${child.formattedBody}</a>`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case "quote": {
|
||||||
|
if (opts.noQuotes) {
|
||||||
|
return {
|
||||||
|
body: "",
|
||||||
|
formattedBody: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const child = this.walkChildNodes(nodeHtml, opts);
|
||||||
|
return {
|
||||||
|
body: `> ${child.body}\n`,
|
||||||
|
formattedBody: `<blockquote>${child.formattedBody}<br> - ${nodeHtml.attributes.authorname}</blockquote>`,
|
||||||
|
};
|
||||||
|
}
|
||||||
case "ss": {
|
case "ss": {
|
||||||
// skype emoji
|
// skype emoji
|
||||||
const type = nodeHtml.attributes.type;
|
const type = nodeHtml.attributes.type;
|
||||||
@ -164,14 +184,14 @@ export class SkypeMessageParser {
|
|||||||
formattedBody: `(${escapeHtml(type)})`,
|
formattedBody: `(${escapeHtml(type)})`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "e_m":
|
case "e_m": // empty edit tag
|
||||||
// empty edit tag
|
case "legacyquote": // empty legacy quote tag
|
||||||
return {
|
return {
|
||||||
body: "",
|
body: "",
|
||||||
formattedBody: "",
|
formattedBody: "",
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return this.walkChildNodes(nodeHtml);
|
return this.walkChildNodes(nodeHtml, opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
Loading…
Reference in New Issue
Block a user