mirror of
https://github.com/plantroon/mx-puppet-xmpp.git
synced 2024-12-27 00:51:41 +00:00
get basic message sending working
This commit is contained in:
commit
1cab823bbe
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
config.yaml
|
||||||
|
build
|
||||||
|
skype-registration.yaml
|
||||||
|
*.db
|
4330
package-lock.json
generated
Normal file
4330
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
package.json
Normal file
29
package.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "mx-puppet-skype",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"lint": "tslint --project ./tsconfig.json -t stylish",
|
||||||
|
"start": "npm run-script build && node ./build/index.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "Sorunome",
|
||||||
|
"dependencies": {
|
||||||
|
"command-line-args": "^5.1.1",
|
||||||
|
"command-line-usage": "^5.0.5",
|
||||||
|
"decode-html": "^2.0.0",
|
||||||
|
"escape-html": "^1.0.3",
|
||||||
|
"events": "^3.0.0",
|
||||||
|
"js-yaml": "^3.13.1",
|
||||||
|
"mx-puppet-bridge": "0.0.35-1",
|
||||||
|
"skype-http": "git://github.com/metasonic/skype-http#ea08e617d4f2bd3b16a2de79fe4efb9dbbe489fc",
|
||||||
|
"tslint": "^5.17.0",
|
||||||
|
"typescript": "^3.7.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/mocha": "^7.0.2",
|
||||||
|
"@types/node": "^12.0.8"
|
||||||
|
}
|
||||||
|
}
|
176
src/client.ts
Normal file
176
src/client.ts
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
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 { Log, ExpireSet, IRemoteRoom } from "mx-puppet-bridge";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
import * as skypeHttp from "skype-http";
|
||||||
|
import { Contact as SkypeContact } from "skype-http/dist/lib/types/contact";
|
||||||
|
|
||||||
|
const log = new Log("SkypePuppet:client");
|
||||||
|
|
||||||
|
const ID_TIMEOUT = 60 * 1000;
|
||||||
|
|
||||||
|
export class Client extends EventEmitter {
|
||||||
|
private api: skypeHttp.Api;
|
||||||
|
private handledIds: ExpireSet<string>;
|
||||||
|
private contacts: Map<String, SkypeContact | null> = new Map();
|
||||||
|
private conversations: Map<String, skypeHttp.Conversation | null> = new Map();
|
||||||
|
constructor(
|
||||||
|
private loginUsername: string,
|
||||||
|
private password: string,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.handledIds = new ExpireSet(ID_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get username(): string {
|
||||||
|
return "8:" + this.api.context.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async connect() {
|
||||||
|
this.api = await skypeHttp.connect({
|
||||||
|
credentials: {
|
||||||
|
username: this.loginUsername,
|
||||||
|
password: this.password,
|
||||||
|
},
|
||||||
|
verbose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.api.on("event", (evt: skypeHttp.events.EventMessage) => {
|
||||||
|
if (!evt || !evt.resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resource = evt.resource;
|
||||||
|
if (this.handledIds.has(resource.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.handledIds.add(resource.id);
|
||||||
|
log.debug(`Got new event of type ${resource.type}`);
|
||||||
|
const [type, subtype] = resource.type.split("/");
|
||||||
|
switch (type) {
|
||||||
|
case "Text":
|
||||||
|
this.emit("text", resource);
|
||||||
|
break;
|
||||||
|
case "RichText":
|
||||||
|
if (subtype === "Location") {
|
||||||
|
this.emit("location", resource);
|
||||||
|
} else if (subtype) {
|
||||||
|
this.emit("file", resource);
|
||||||
|
} else {
|
||||||
|
this.emit("richText", resource);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Control":
|
||||||
|
if (subtype === "Typing" || subtype === "ClearTyping") {
|
||||||
|
this.emit("typing", resource, subtype === "Typing");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ThreadActivity":
|
||||||
|
// log.silly(resource);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.api.on("error", (err: Error) => {
|
||||||
|
log.error("An error occured", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.api.listen();
|
||||||
|
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() {
|
||||||
|
await this.api.stopListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getContact(id: string): Promise<SkypeContact | null> {
|
||||||
|
const hasStart = Boolean(id.match(/^\d+:/));
|
||||||
|
const fullId = hasStart ? id : `8:${id}`;
|
||||||
|
if (this.contacts.has(fullId)) {
|
||||||
|
return this.contacts.get(fullId) || null;
|
||||||
|
}
|
||||||
|
if (hasStart) {
|
||||||
|
id = id.substr(id.indexOf(":")+1);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const rawContact = await this.api.getContact(id);
|
||||||
|
const contact: SkypeContact = {
|
||||||
|
personId: rawContact.id.raw,
|
||||||
|
workloads: null,
|
||||||
|
mri: rawContact.id.raw,
|
||||||
|
blocked: false,
|
||||||
|
authorized: true,
|
||||||
|
creationTime: new Date(),
|
||||||
|
displayName: (rawContact.name && rawContact.name.displayName) || rawContact.id.id,
|
||||||
|
displayNameSource: "profile" as any, // tslint:disable-line no-any
|
||||||
|
profile: {
|
||||||
|
avatarUrl: rawContact.avatarUrl || undefined,
|
||||||
|
name: {
|
||||||
|
first: (rawContact.name && rawContact.name).first || undefined,
|
||||||
|
surname: (rawContact.name && rawContact.name).surname || undefined,
|
||||||
|
nickname: (rawContact.name && rawContact.name).nickname || undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.contacts.set(contact.mri, contact || null);
|
||||||
|
return contact || null;
|
||||||
|
} catch (err) {
|
||||||
|
// contact not found
|
||||||
|
this.contacts.set(fullId, null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getConversation(room: IRemoteRoom): Promise<skypeHttp.Conversation | null> {
|
||||||
|
let id = room.roomId;
|
||||||
|
const match = id.match(/^dm-\d+-/);
|
||||||
|
if (match) {
|
||||||
|
const [_, puppetId] = id.split("-");
|
||||||
|
if (Number(puppetId) !== room.puppetId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
id = id.substr(match[0].length);
|
||||||
|
}
|
||||||
|
if (this.conversations.has(id)) {
|
||||||
|
return this.conversations.get(id) || null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const conversation = await this.api.getConversation(id);
|
||||||
|
this.conversations.set(conversation.id, conversation || null);
|
||||||
|
return conversation || null;
|
||||||
|
} catch (err) {
|
||||||
|
// conversation not found
|
||||||
|
this.conversations.set(id, null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sendMessage(conversationId: string, msg: string): Promise<skypeHttp.Api.SendMessageResult> {
|
||||||
|
return await this.api.sendMessage({
|
||||||
|
textContent: msg,
|
||||||
|
}, conversationId);
|
||||||
|
}
|
||||||
|
}
|
125
src/index.ts
Normal file
125
src/index.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
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 {
|
||||||
|
PuppetBridge,
|
||||||
|
IPuppetBridgeRegOpts,
|
||||||
|
Log,
|
||||||
|
IRetData,
|
||||||
|
Util,
|
||||||
|
IProtocolInformation,
|
||||||
|
} from "mx-puppet-bridge";
|
||||||
|
import * as commandLineArgs from "command-line-args";
|
||||||
|
import * as commandLineUsage from "command-line-usage";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as yaml from "js-yaml";
|
||||||
|
import { Skype } from "./skype";
|
||||||
|
import { Client } from "./client";
|
||||||
|
|
||||||
|
const log = new Log("SkypePuppet:index");
|
||||||
|
|
||||||
|
const commandOptions = [
|
||||||
|
{ name: "register", alias: "r", type: Boolean },
|
||||||
|
{ name: "registration-file", alias: "f", type: String },
|
||||||
|
{ name: "config", alias: "c", type: String },
|
||||||
|
{ name: "help", alias: "h", type: Boolean },
|
||||||
|
];
|
||||||
|
const options = Object.assign({
|
||||||
|
"register": false,
|
||||||
|
"registration-file": "skype-registration.yaml",
|
||||||
|
"config": "config.yaml",
|
||||||
|
"help": false,
|
||||||
|
}, commandLineArgs(commandOptions));
|
||||||
|
|
||||||
|
if (options.help) {
|
||||||
|
// tslint:disable-next-line:no-console
|
||||||
|
console.log(commandLineUsage([
|
||||||
|
{
|
||||||
|
header: "Matrix Skype Puppet Bridge",
|
||||||
|
content: "A matrix puppet bridge for skype",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Options",
|
||||||
|
optionList: commandOptions,
|
||||||
|
},
|
||||||
|
]));
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const protocol: IProtocolInformation = {
|
||||||
|
features: {
|
||||||
|
// file: true, // no need for the others as we auto-detect types anyways
|
||||||
|
// presence: true, // we want to be able to send presence
|
||||||
|
globalNamespace: true,
|
||||||
|
},
|
||||||
|
id: "skype",
|
||||||
|
displayname: "Skype",
|
||||||
|
externalUrl: "https://skype.com/",
|
||||||
|
};
|
||||||
|
|
||||||
|
const puppet = new PuppetBridge(options["registration-file"], options.config, protocol);
|
||||||
|
|
||||||
|
if (options.register) {
|
||||||
|
// okay, all we have to do is generate a registration file
|
||||||
|
puppet.readConfig(false);
|
||||||
|
try {
|
||||||
|
puppet.generateRegistration({
|
||||||
|
prefix: "_skypepuppet_",
|
||||||
|
id: "skype-puppet",
|
||||||
|
url: `http://${puppet.Config.bridge.bindAddress}:${puppet.Config.bridge.port}`,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
// tslint:disable-next-line:no-console
|
||||||
|
console.log("Couldn't generate registration file:", err);
|
||||||
|
}
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
await puppet.init();
|
||||||
|
const skype = new Skype(puppet);
|
||||||
|
puppet.on("puppetNew", skype.newPuppet.bind(skype));
|
||||||
|
puppet.on("puppetDelete", skype.deletePuppet.bind(skype));
|
||||||
|
puppet.on("message", skype.handleMatrixMessage.bind(skype));
|
||||||
|
puppet.setCreateUserHook(skype.createUser.bind(skype));
|
||||||
|
puppet.setCreateRoomHook(skype.createRoom.bind(skype));
|
||||||
|
puppet.setGetUserIdsInRoomHook(skype.getUserIdsInRoom.bind(skype));
|
||||||
|
puppet.setGetDataFromStrHook(async (str: string): Promise<IRetData> => {
|
||||||
|
const retData = {
|
||||||
|
success: false,
|
||||||
|
} as IRetData;
|
||||||
|
const [username, password] = str.split(" ");
|
||||||
|
try {
|
||||||
|
const client = new Client(username, password);
|
||||||
|
await client.connect();
|
||||||
|
await client.disconnect();
|
||||||
|
} catch (err) {
|
||||||
|
retData.error = "Username or password wrong";
|
||||||
|
return retData;
|
||||||
|
}
|
||||||
|
retData.success = true;
|
||||||
|
const data: any = {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
};
|
||||||
|
retData.data = data;
|
||||||
|
return retData;
|
||||||
|
});
|
||||||
|
puppet.setBotHeaderMsgHook((): string => {
|
||||||
|
return "Skype Puppet Bridge";
|
||||||
|
});
|
||||||
|
await puppet.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-floating-promises
|
||||||
|
run();
|
277
src/skype.ts
Normal file
277
src/skype.ts
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
/*
|
||||||
|
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 {
|
||||||
|
PuppetBridge, IRemoteUser, IRemoteRoom, IReceiveParams, IMessageEvent, IFileEvent, Log, MessageDeduplicator,
|
||||||
|
} from "mx-puppet-bridge";
|
||||||
|
import { Client } from "./client";
|
||||||
|
import * as skypeHttp from "skype-http";
|
||||||
|
import { Contact as SkypeContact } from "skype-http/dist/lib/types/contact";
|
||||||
|
import * as decodeHtml from "decode-html";
|
||||||
|
import * as escapeHtml from "escape-html";
|
||||||
|
|
||||||
|
const log = new Log("SkypePuppet:skype");
|
||||||
|
|
||||||
|
interface ISkypePuppet {
|
||||||
|
client: Client;
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISkypePuppets {
|
||||||
|
[puppetId: number]: ISkypePuppet;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Skype {
|
||||||
|
private puppets: ISkypePuppets = {};
|
||||||
|
private messageDeduplicator: MessageDeduplicator;
|
||||||
|
constructor(
|
||||||
|
private puppet: PuppetBridge,
|
||||||
|
) {
|
||||||
|
this.messageDeduplicator = new MessageDeduplicator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUserParams(puppetId: number, contact: SkypeContact): IRemoteUser {
|
||||||
|
return {
|
||||||
|
puppetId,
|
||||||
|
userId: contact.mri,
|
||||||
|
name: contact.displayName,
|
||||||
|
avatarUrl: contact.profile ? contact.profile.avatarUrl : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRoomParams(puppetId: number, conversation: skypeHttp.Conversation): IRemoteRoom {
|
||||||
|
const roomType = Number(conversation.id.split(":")[0]);
|
||||||
|
let roomId = conversation.id;
|
||||||
|
const isDirect = roomType === 8;
|
||||||
|
if (isDirect) {
|
||||||
|
return {
|
||||||
|
puppetId,
|
||||||
|
roomId: `dm-${puppetId}-${roomId}`,
|
||||||
|
isDirect: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let avatarUrl: string | null = null;
|
||||||
|
let name: string | null = null;
|
||||||
|
if (conversation.threadProperties) {
|
||||||
|
name = conversation.threadProperties.topic || null;
|
||||||
|
if (name) {
|
||||||
|
name = decodeHtml(name);
|
||||||
|
}
|
||||||
|
const picture = conversation.threadProperties.picture;
|
||||||
|
if (picture && picture.startsWith("URL@")) {
|
||||||
|
avatarUrl = picture.slice("URL@".length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
puppetId,
|
||||||
|
roomId,
|
||||||
|
name,
|
||||||
|
avatarUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSendParams(puppetId: number, resource: skypeHttp.resources.Resource): Promise<IReceiveParams | null> {
|
||||||
|
const roomType = Number(resource.conversation.split(":")[0]);
|
||||||
|
let roomId = resource.conversation;
|
||||||
|
const p = this.puppets[puppetId];
|
||||||
|
const contact = await p.client.getContact(resource.from.raw);
|
||||||
|
const conversation = await p.client.getConversation({
|
||||||
|
puppetId,
|
||||||
|
roomId: resource.conversation,
|
||||||
|
});
|
||||||
|
if (!contact || !conversation) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
user: await this.getUserParams(puppetId, contact),
|
||||||
|
room: await this.getRoomParams(puppetId, conversation),
|
||||||
|
eventId: (resource as any).clientId, // tslint:disable-line no-any
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async startClient(puppetId: number) {
|
||||||
|
const p = this.puppets[puppetId];
|
||||||
|
if (!p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const client = p.client;
|
||||||
|
client.on("text", async (resource: skypeHttp.resources.TextResource) => {
|
||||||
|
try {
|
||||||
|
await this.handleSkypeText(puppetId, resource, false);
|
||||||
|
} catch (err) {
|
||||||
|
log.error("Error while handling text event", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.on("richText", async (resource: skypeHttp.resources.RichTextResource) => {
|
||||||
|
try {
|
||||||
|
await this.handleSkypeText(puppetId, resource, true);
|
||||||
|
} catch (err) {
|
||||||
|
log.error("Error while handling richText event", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.on("location", async (resource: skypeHttp.resources.RichTextLocationResource) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
log.error("Error while handling location event", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.on("file", async (resource: skypeHttp.resources.FileResource) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
log.error("Error while handling file event", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.on("typing", async (resource: skypeHttp.resources.Resource, typing: boolean) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
log.error("Error while handling typing event", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await client.connect();
|
||||||
|
await this.puppet.setUserId(puppetId, client.username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async newPuppet(puppetId: number, data: any) {
|
||||||
|
if (this.puppets[puppetId]) {
|
||||||
|
await this.deletePuppet(puppetId);
|
||||||
|
}
|
||||||
|
const client = new Client(data.username, data.password);
|
||||||
|
this.puppets[puppetId] = {
|
||||||
|
client,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
await this.startClient(puppetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deletePuppet(puppetId: number) {
|
||||||
|
const p = this.puppets[puppetId];
|
||||||
|
if (!p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await p.client.disconnect();
|
||||||
|
delete this.puppets[puppetId];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createUser(remoteUser: IRemoteUser): Promise<IRemoteUser | null> {
|
||||||
|
const p = this.puppets[remoteUser.puppetId];
|
||||||
|
if (!p) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
log.info(`Received create request for user update puppetId=${remoteUser.puppetId} userId=${remoteUser.userId}`);
|
||||||
|
const contact = await p.client.getContact(remoteUser.userId);
|
||||||
|
if (!contact) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.getUserParams(remoteUser.puppetId, contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createRoom(room: IRemoteRoom): Promise<IRemoteRoom | null> {
|
||||||
|
const p = this.puppets[room.puppetId];
|
||||||
|
if (!p) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
log.info(`Received create request for channel update puppetId=${room.puppetId} roomId=${room.roomId}`);
|
||||||
|
const conversation = await p.client.getConversation(room);
|
||||||
|
if (!conversation) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.getRoomParams(room.puppetId, conversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getUserIdsInRoom(room: IRemoteRoom): Promise<Set<string> | null> {
|
||||||
|
const p = this.puppets[room.puppetId];
|
||||||
|
if (!p) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const conversation = await p.client.getConversation(room);
|
||||||
|
if (!conversation) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const users = new Set<string>();
|
||||||
|
if (conversation.members) {
|
||||||
|
for (const member of conversation.members) {
|
||||||
|
users.add(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async handleMatrixMessage(room: IRemoteRoom, data: IMessageEvent) {
|
||||||
|
const p = this.puppets[room.puppetId];
|
||||||
|
if (!p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const conversation = await p.client.getConversation(room);
|
||||||
|
if (!conversation) {
|
||||||
|
log.warn(`Room ${room.roomId} not found!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let msg: string;
|
||||||
|
if (data.formattedBody) {
|
||||||
|
msg = data.formattedBody;
|
||||||
|
} else {
|
||||||
|
msg = escapeHtml(data.body);
|
||||||
|
}
|
||||||
|
const dedupeKey = `${room.puppetId};${room.roomId}`;
|
||||||
|
this.messageDeduplicator.lock(dedupeKey, p.client.username, msg);
|
||||||
|
const ret = await p.client.sendMessage(conversation.id, msg);
|
||||||
|
const eventId = ret && ret.clientMessageId;
|
||||||
|
this.messageDeduplicator.unlock(dedupeKey, p.client.username, eventId);
|
||||||
|
if (eventId) {
|
||||||
|
await this.puppet.eventStore.insert(room.puppetId, data.eventId!, eventId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleSkypeText(
|
||||||
|
puppetId: number,
|
||||||
|
resource: skypeHttp.resources.TextResource | skypeHttp.resources.RichTextResource,
|
||||||
|
rich: boolean,
|
||||||
|
) {
|
||||||
|
const p = this.puppets[puppetId];
|
||||||
|
if (!p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info("Got new skype message");
|
||||||
|
log.silly(resource);
|
||||||
|
const params = await this.getSendParams(puppetId, resource);
|
||||||
|
if (!params) {
|
||||||
|
log.warn("Couldn't generate params");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dedupeKey = `${puppetId};${params.room.roomId}`;
|
||||||
|
if (await this.messageDeduplicator.dedupe(dedupeKey, params.user.userId, params.eventId, resource.content)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!rich) {
|
||||||
|
await this.puppet.sendMessage(params, {
|
||||||
|
body: resource.content,
|
||||||
|
});
|
||||||
|
} else if (resource.native && resource.native.skypeeditedid) {
|
||||||
|
if (resource.content) {
|
||||||
|
await this.puppet.sendEdit(params, resource.native.skypeeditedid, {
|
||||||
|
body: resource.content,
|
||||||
|
formattedBody: resource.content,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.puppet.sendRedact(params, resource.native.skypeeditedid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await this.puppet.sendMessage(params, {
|
||||||
|
body: resource.content,
|
||||||
|
formattedBody: resource.content,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"target": "es2016",
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"inlineSourceMap": true,
|
||||||
|
"outDir": "./build",
|
||||||
|
"types": ["node"],
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"compileOnSave": true,
|
||||||
|
"include": [
|
||||||
|
"src/**/*",
|
||||||
|
]
|
||||||
|
}
|
42
tslint.json
Normal file
42
tslint.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"extends": "tslint:recommended",
|
||||||
|
"rules": {
|
||||||
|
"ordered-imports": false,
|
||||||
|
"no-trailing-whitespace": "error",
|
||||||
|
"max-classes-per-file": {
|
||||||
|
"severity": "warning"
|
||||||
|
},
|
||||||
|
"object-literal-sort-keys": "off",
|
||||||
|
"no-any":{
|
||||||
|
"severity": "warning"
|
||||||
|
},
|
||||||
|
"arrow-return-shorthand": true,
|
||||||
|
"no-magic-numbers": [true, -1, 0, 1, 1000],
|
||||||
|
"prefer-for-of": true,
|
||||||
|
"typedef": {
|
||||||
|
"severity": "warning"
|
||||||
|
},
|
||||||
|
"await-promise": [true],
|
||||||
|
"curly": true,
|
||||||
|
"no-empty": false,
|
||||||
|
"no-invalid-this": true,
|
||||||
|
"no-string-throw": {
|
||||||
|
"severity": "warning"
|
||||||
|
},
|
||||||
|
"no-unused-expression": true,
|
||||||
|
"prefer-const": true,
|
||||||
|
"object-literal-sort-keys": false,
|
||||||
|
"indent": [true, "tabs", 1],
|
||||||
|
"max-file-line-count": {
|
||||||
|
"severity": "warning",
|
||||||
|
"options": [500]
|
||||||
|
},
|
||||||
|
"no-duplicate-imports": true,
|
||||||
|
"array-type": [true, "array"],
|
||||||
|
"promise-function-async": true,
|
||||||
|
"no-bitwise": true,
|
||||||
|
"no-debugger": true,
|
||||||
|
"no-floating-promises": true,
|
||||||
|
"prefer-template": [true, "allow-single-concat"]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user