mirror of
https://github.com/plantroon/mx-puppet-xmpp.git
synced 2024-12-28 09:31:40 +00:00
add message parsers
This commit is contained in:
parent
19020890e1
commit
416df718bd
13
package-lock.json
generated
13
package-lock.json
generated
@ -2123,6 +2123,11 @@
|
|||||||
"type-fest": "^0.8.0"
|
"type-fest": "^0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"he": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0="
|
||||||
|
},
|
||||||
"htmlencode": {
|
"htmlencode": {
|
||||||
"version": "0.0.4",
|
"version": "0.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/htmlencode/-/htmlencode-0.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/htmlencode/-/htmlencode-0.0.4.tgz",
|
||||||
@ -2858,6 +2863,14 @@
|
|||||||
"semver": "^5.4.1"
|
"semver": "^5.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-html-parser": {
|
||||||
|
"version": "1.2.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-1.2.13.tgz",
|
||||||
|
"integrity": "sha512-RTG5oDk3nh1oXQGYtQQa9w0v9LjltgzzO1tv3cpXzb9M/UkpFIZ5v4osx3OYAcnWx/ETBXjxs2cgtD9fhn6Bjw==",
|
||||||
|
"requires": {
|
||||||
|
"he": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"noop-logger": {
|
"noop-logger": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"events": "^3.0.0",
|
"events": "^3.0.0",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"mx-puppet-bridge": "0.0.35-1",
|
"mx-puppet-bridge": "0.0.35-1",
|
||||||
|
"node-html-parser": "^1.2.13",
|
||||||
"skype-http": "git://github.com/Sorunome/skype-http#10555125f46307bbff93a8c4779889f4100669d2",
|
"skype-http": "git://github.com/Sorunome/skype-http#10555125f46307bbff93a8c4779889f4100669d2",
|
||||||
"tslint": "^5.17.0",
|
"tslint": "^5.17.0",
|
||||||
"typescript": "^3.7.4"
|
"typescript": "^3.7.4"
|
||||||
|
@ -45,9 +45,11 @@ export class Client extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async connect() {
|
public async connect() {
|
||||||
if (this.state) {
|
let connectedWithAuth = false;
|
||||||
|
if (this.state && false) {
|
||||||
try {
|
try {
|
||||||
this.api = await skypeHttp.connect({ state: this.state, verbose: true });
|
this.api = await skypeHttp.connect({ state: this.state, verbose: true });
|
||||||
|
connectedWithAuth = true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.api = await skypeHttp.connect({
|
this.api = await skypeHttp.connect({
|
||||||
credentials: {
|
credentials: {
|
||||||
@ -56,6 +58,7 @@ export class Client extends EventEmitter {
|
|||||||
},
|
},
|
||||||
verbose: true,
|
verbose: true,
|
||||||
});
|
});
|
||||||
|
connectedWithAuth = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.api = await skypeHttp.connect({
|
this.api = await skypeHttp.connect({
|
||||||
@ -65,6 +68,7 @@ export class Client extends EventEmitter {
|
|||||||
},
|
},
|
||||||
verbose: true,
|
verbose: true,
|
||||||
});
|
});
|
||||||
|
connectedWithAuth = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.api.on("event", (evt: skypeHttp.events.EventMessage) => {
|
this.api.on("event", (evt: skypeHttp.events.EventMessage) => {
|
||||||
@ -109,21 +113,17 @@ export class Client extends EventEmitter {
|
|||||||
this.emit("error", err);
|
this.emit("error", err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const contacts = await this.api.getContacts();
|
||||||
|
for (const contact of contacts) {
|
||||||
|
this.contacts.set(contact.mri, contact);
|
||||||
|
}
|
||||||
|
const conversations = await this.api.getConversations();
|
||||||
|
for (const conversation of conversations) {
|
||||||
|
this.conversations.set(conversation.id, conversation);
|
||||||
|
}
|
||||||
|
|
||||||
await this.api.listen();
|
await this.api.listen();
|
||||||
await this.api.setStatus("Online");
|
await this.api.setStatus("Online");
|
||||||
|
|
||||||
try {
|
|
||||||
const contacts = await this.api.getContacts();
|
|
||||||
for (const contact of contacts) {
|
|
||||||
this.contacts.set(contact.mri, contact);
|
|
||||||
}
|
|
||||||
const conversations = await this.api.getConversations();
|
|
||||||
for (const conversation of conversations) {
|
|
||||||
this.conversations.set(conversation.id, conversation);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
log.error(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async disconnect() {
|
public async disconnect() {
|
||||||
|
66
src/matrixmessageparser.ts
Normal file
66
src/matrixmessageparser.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 mx-puppet-skype
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Parser from "node-html-parser";
|
||||||
|
import * as escapeHtml from "escape-html";
|
||||||
|
|
||||||
|
export class MatrixMessageParser {
|
||||||
|
public parse(msg: string): string {
|
||||||
|
const nodes = Parser.parse(`<wrap>${msg}</wrap>`, {
|
||||||
|
lowerCaseTagName: true,
|
||||||
|
pre: true,
|
||||||
|
});
|
||||||
|
return this.walkNode(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private walkChildNodes(node: Parser.Node): string {
|
||||||
|
return node.childNodes.map((node) => this.walkNode(node)).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private escape(s: string): string {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private walkNode(node: Parser.Node): string {
|
||||||
|
if (node.nodeType === Parser.NodeType.TEXT_NODE) {
|
||||||
|
return this.escape((node as Parser.TextNode).text);
|
||||||
|
} else if (node.nodeType === Parser.NodeType.ELEMENT_NODE) {
|
||||||
|
const nodeHtml = node as Parser.HTMLElement;
|
||||||
|
switch (nodeHtml.tagName) {
|
||||||
|
case "em":
|
||||||
|
case "i":
|
||||||
|
return `<i raw_pre="_" raw_post="_">${this.walkChildNodes(nodeHtml)}</i>`;
|
||||||
|
case "strong":
|
||||||
|
case "b":
|
||||||
|
return `<b raw_pre="*" raw_post="*">${this.walkChildNodes(nodeHtml)}</b>`;
|
||||||
|
case "del":
|
||||||
|
return `<s raw_pre="~" raw_post="~">${this.walkChildNodes(nodeHtml)}</s>`;
|
||||||
|
case "code":
|
||||||
|
return `<pre raw_pre="{code}" raw_post="{code}">${this.walkChildNodes(nodeHtml)}</pre>`;
|
||||||
|
case "a": {
|
||||||
|
const href = nodeHtml.attributes.href;
|
||||||
|
const inner = this.walkChildNodes(nodeHtml);
|
||||||
|
return `<a href="${escapeHtml(href)}">${inner}</a>`;
|
||||||
|
}
|
||||||
|
case "wrap":
|
||||||
|
return this.walkChildNodes(nodeHtml);
|
||||||
|
default:
|
||||||
|
if (!nodeHtml.tagName) {
|
||||||
|
return this.walkChildNodes(nodeHtml);
|
||||||
|
}
|
||||||
|
return `<${nodeHtml.tagName}>${this.walkChildNodes(nodeHtml)}</${nodeHtml.tagName}>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
45
src/skype.ts
45
src/skype.ts
@ -10,6 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PuppetBridge, IRemoteUser, IRemoteRoom, IReceiveParams, IMessageEvent, IFileEvent, Log, MessageDeduplicator, Util,
|
PuppetBridge, IRemoteUser, IRemoteRoom, IReceiveParams, IMessageEvent, IFileEvent, Log, MessageDeduplicator, Util,
|
||||||
ExpireSet, IRetList,
|
ExpireSet, IRetList,
|
||||||
@ -20,6 +21,8 @@ import { Contact as SkypeContact } from "skype-http/dist/lib/types/contact";
|
|||||||
import { NewMediaMessage as SkypeNewMediaMessage } from "skype-http/dist/lib/interfaces/api/api";
|
import { NewMediaMessage as SkypeNewMediaMessage } from "skype-http/dist/lib/interfaces/api/api";
|
||||||
import * as decodeHtml from "decode-html";
|
import * as decodeHtml from "decode-html";
|
||||||
import * as escapeHtml from "escape-html";
|
import * as escapeHtml from "escape-html";
|
||||||
|
import { MatrixMessageParser } from "./matrixmessageparser";
|
||||||
|
import { SkypeMessageParser } from "./skypemessageparser";
|
||||||
|
|
||||||
const log = new Log("SkypePuppet:skype");
|
const log = new Log("SkypePuppet:skype");
|
||||||
|
|
||||||
@ -38,10 +41,14 @@ interface ISkypePuppets {
|
|||||||
export class Skype {
|
export class Skype {
|
||||||
private puppets: ISkypePuppets = {};
|
private puppets: ISkypePuppets = {};
|
||||||
private messageDeduplicator: MessageDeduplicator;
|
private messageDeduplicator: MessageDeduplicator;
|
||||||
|
private matrixMessageParser: MatrixMessageParser;
|
||||||
|
private skypeMessageParser: SkypeMessageParser;
|
||||||
constructor(
|
constructor(
|
||||||
private puppet: PuppetBridge,
|
private puppet: PuppetBridge,
|
||||||
) {
|
) {
|
||||||
this.messageDeduplicator = new MessageDeduplicator();
|
this.messageDeduplicator = new MessageDeduplicator();
|
||||||
|
this.matrixMessageParser = new MatrixMessageParser();
|
||||||
|
this.skypeMessageParser = new SkypeMessageParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUserParams(puppetId: number, contact: SkypeContact): IRemoteUser {
|
public getUserParams(puppetId: number, contact: SkypeContact): IRemoteUser {
|
||||||
@ -160,8 +167,8 @@ export class Skype {
|
|||||||
});
|
});
|
||||||
const MINUTE = 60000;
|
const MINUTE = 60000;
|
||||||
client.on("error", async (err: Error) => {
|
client.on("error", async (err: Error) => {
|
||||||
await this.puppet.sendStatusMessage(puppetId, "Error:" + err);
|
await this.puppet.sendStatusMessage(puppetId, "Error: " + err);
|
||||||
await this.puppet.sendStatusMessage(puppetId, "Reconnecting in a minute... " + err.message);
|
await this.puppet.sendStatusMessage(puppetId, "Reconnecting in a minute... ");
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await this.stopClient(puppetId);
|
await this.stopClient(puppetId);
|
||||||
await this.startClient(puppetId);
|
await this.startClient(puppetId);
|
||||||
@ -175,7 +182,7 @@ export class Skype {
|
|||||||
await this.puppet.sendStatusMessage(puppetId, "connected");
|
await this.puppet.sendStatusMessage(puppetId, "connected");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error("Failed to connect", err);
|
log.error("Failed to connect", err);
|
||||||
await this.puppet.sendStatusMessage(puppetId, "Failed to connect, reconnecting in a minute... " + err.message);
|
await this.puppet.sendStatusMessage(puppetId, "Failed to connect, reconnecting in a minute... " + err);
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await this.startClient(puppetId);
|
await this.startClient(puppetId);
|
||||||
}, MINUTE);
|
}, MINUTE);
|
||||||
@ -310,7 +317,7 @@ export class Skype {
|
|||||||
}
|
}
|
||||||
let msg: string;
|
let msg: string;
|
||||||
if (data.formattedBody) {
|
if (data.formattedBody) {
|
||||||
msg = data.formattedBody;
|
msg = this.matrixMessageParser.parse(data.formattedBody);
|
||||||
} else {
|
} else {
|
||||||
msg = escapeHtml(data.body);
|
msg = escapeHtml(data.body);
|
||||||
}
|
}
|
||||||
@ -338,7 +345,7 @@ export class Skype {
|
|||||||
}
|
}
|
||||||
let msg: string;
|
let msg: string;
|
||||||
if (data.formattedBody) {
|
if (data.formattedBody) {
|
||||||
msg = data.formattedBody;
|
msg = this.matrixMessageParser.parse(data.formattedBody);
|
||||||
} else {
|
} else {
|
||||||
msg = escapeHtml(data.body);
|
msg = escapeHtml(data.body);
|
||||||
}
|
}
|
||||||
@ -440,18 +447,20 @@ export class Skype {
|
|||||||
log.silly("normal message dedupe");
|
log.silly("normal message dedupe");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!rich) {
|
let sendMsg: IMessageEvent;
|
||||||
await this.puppet.sendMessage(params, {
|
if (rich) {
|
||||||
|
sendMsg = this.skypeMessageParser.parse(msg);
|
||||||
|
} else {
|
||||||
|
sendMsg = {
|
||||||
body: msg,
|
body: msg,
|
||||||
emote,
|
};
|
||||||
});
|
}
|
||||||
} else if (resource.native && resource.native.skypeeditedid) {
|
if (emote) {
|
||||||
|
sendMsg.emote = true;
|
||||||
|
}
|
||||||
|
if (resource.native && resource.native.skypeeditedid) {
|
||||||
if (resource.content) {
|
if (resource.content) {
|
||||||
await this.puppet.sendEdit(params, resource.native.skypeeditedid, {
|
await this.puppet.sendEdit(params, resource.native.skypeeditedid, sendMsg);
|
||||||
body: msg,
|
|
||||||
formattedBody: msg,
|
|
||||||
emote,
|
|
||||||
});
|
|
||||||
} else if (p.deletedMessages.has(resource.native.skypeeditedid)) {
|
} else if (p.deletedMessages.has(resource.native.skypeeditedid)) {
|
||||||
log.silly("normal message redact dedupe");
|
log.silly("normal message redact dedupe");
|
||||||
return;
|
return;
|
||||||
@ -459,11 +468,7 @@ export class Skype {
|
|||||||
await this.puppet.sendRedact(params, resource.native.skypeeditedid);
|
await this.puppet.sendRedact(params, resource.native.skypeeditedid);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.puppet.sendMessage(params, {
|
await this.puppet.sendMessage(params, sendMsg);
|
||||||
body: msg,
|
|
||||||
formattedBody: msg,
|
|
||||||
emote,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
100
src/skypemessageparser.ts
Normal file
100
src/skypemessageparser.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 mx-puppet-skype
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Parser from "node-html-parser";
|
||||||
|
import * as decodeHtml from "decode-html";
|
||||||
|
import * as escapeHtml from "escape-html";
|
||||||
|
import { IMessageEvent } from "mx-puppet-bridge";
|
||||||
|
|
||||||
|
export class SkypeMessageParser {
|
||||||
|
public parse(msg: string): IMessageEvent {
|
||||||
|
const nodes = Parser.parse(`<wrap>${msg}</wrap>`, {
|
||||||
|
lowerCaseTagName: true,
|
||||||
|
pre: true,
|
||||||
|
});
|
||||||
|
return this.walkNode(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private walkChildNodes(node: Parser.Node): IMessageEvent {
|
||||||
|
return node.childNodes.map((node) => this.walkNode(node)).reduce((acc, curr) => {
|
||||||
|
return {
|
||||||
|
body: acc.body + curr.body,
|
||||||
|
formattedBody: acc.formattedBody! + curr.formattedBody!,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private escape(s: string): IMessageEvent {
|
||||||
|
return {
|
||||||
|
body: decodeHtml(s),
|
||||||
|
formattedBody: s.replace("\n", "<br>"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private walkNode(node: Parser.Node): IMessageEvent {
|
||||||
|
if (node.nodeType === Parser.NodeType.TEXT_NODE) {
|
||||||
|
return this.escape((node as Parser.TextNode).text);
|
||||||
|
} else if (node.nodeType === Parser.NodeType.ELEMENT_NODE) {
|
||||||
|
const nodeHtml = node as Parser.HTMLElement;
|
||||||
|
switch (nodeHtml.tagName) {
|
||||||
|
case "i": {
|
||||||
|
const child = this.walkChildNodes(nodeHtml);
|
||||||
|
return {
|
||||||
|
body: `_${child.body}_`,
|
||||||
|
formattedBody: `<em>${child.formattedBody}</em>`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "b": {
|
||||||
|
const child = this.walkChildNodes(nodeHtml);
|
||||||
|
return {
|
||||||
|
body: `*${child.body}*`,
|
||||||
|
formattedBody: `<strong>${child.formattedBody}</strong>`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "s": {
|
||||||
|
const child = this.walkChildNodes(nodeHtml);
|
||||||
|
return {
|
||||||
|
body: `~${child.body}~`,
|
||||||
|
formattedBody: `<del>${child.formattedBody}</del>`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "pre": {
|
||||||
|
const child = this.walkChildNodes(nodeHtml);
|
||||||
|
return {
|
||||||
|
body: `{code}${child.body}{code}`,
|
||||||
|
formattedBody: `<code>${child.formattedBody}</code>`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "a": {
|
||||||
|
const href = nodeHtml.attributes.href;
|
||||||
|
const child = this.walkChildNodes(nodeHtml);
|
||||||
|
return {
|
||||||
|
body: child.body === href ? href : `[${child.body}](${href})`,
|
||||||
|
formattedBody: `<a href="${escapeHtml(href)}">${child.formattedBody}</a>`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "e_m":
|
||||||
|
return {
|
||||||
|
body: "",
|
||||||
|
formattedBody: "",
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return this.walkChildNodes(nodeHtml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
body: "",
|
||||||
|
formattedBody: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user