file handling

This commit is contained in:
Sorunome 2020-03-24 21:10:49 +01:00
parent 1cab823bbe
commit 79dc0295cf
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
3 changed files with 202 additions and 14 deletions

View File

@ -11,10 +11,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { Log, ExpireSet, IRemoteRoom } from "mx-puppet-bridge";
import { Log, ExpireSet, IRemoteRoom, Util } 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";
import { NewMediaMessage as SkypeNewMediaMessage } from "skype-http/dist/lib/interfaces/api/api";
const log = new Log("SkypePuppet:client");
@ -168,9 +169,50 @@ export class Client extends EventEmitter {
}
}
public async downloadFile(url: string): Promise<Buffer> {
if (!url.includes("/views/imgpsh_fullsize_anim")) {
url = url + "/views/imgpsh_fullsize_anim";
}
return await Util.DownloadFile(url, {
cookies: this.api.context.cookies,
headers: { Authorization: 'skype_token ' + this.api.context.skypeToken.value },
});
}
public async sendMessage(conversationId: string, msg: string): Promise<skypeHttp.Api.SendMessageResult> {
return await this.api.sendMessage({
textContent: msg,
}, conversationId);
}
public async sendEdit(conversationId: string, messageId: string, msg: string) {
return await this.api.sendEdit({
textContent: msg,
}, conversationId, messageId);
}
public async sendDelete(conversationId: string, messageId: string) {
return await this.api.sendDelete(conversationId, messageId);
}
public async sendAudio(
conversationId: string,
opts: SkypeNewMediaMessage,
): Promise<skypeHttp.Api.SendMessageResult> {
return await this.api.sendAudio(opts, conversationId);
}
public async sendDocument(
conversationId: string,
opts: SkypeNewMediaMessage
): Promise<skypeHttp.Api.SendMessageResult> {
return await this.api.sendDocument(opts, conversationId);
}
public async sendImage(
conversationId: string,
opts: SkypeNewMediaMessage
): Promise<skypeHttp.Api.SendMessageResult> {
return await this.api.sendImage(opts, conversationId);
}
}

View File

@ -58,8 +58,11 @@ if (options.help) {
const protocol: IProtocolInformation = {
features: {
// file: true, // no need for the others as we auto-detect types anyways
image: true,
audio: true,
file: true,
// presence: true, // we want to be able to send presence
edit: true,
globalNamespace: true,
},
id: "skype",
@ -91,6 +94,11 @@ async function run() {
puppet.on("puppetNew", skype.newPuppet.bind(skype));
puppet.on("puppetDelete", skype.deletePuppet.bind(skype));
puppet.on("message", skype.handleMatrixMessage.bind(skype));
puppet.on("edit", skype.handleMatrixEdit.bind(skype));
puppet.on("redact", skype.handleMatrixRedact.bind(skype));
puppet.on("image", skype.handleMatrixImage.bind(skype));
puppet.on("audio", skype.handleMatrixAudio.bind(skype));
puppet.on("file", skype.handleMatrixFile.bind(skype));
puppet.setCreateUserHook(skype.createUser.bind(skype));
puppet.setCreateRoomHook(skype.createRoom.bind(skype));
puppet.setGetUserIdsInRoomHook(skype.getUserIdsInRoom.bind(skype));

View File

@ -11,11 +11,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {
PuppetBridge, IRemoteUser, IRemoteRoom, IReceiveParams, IMessageEvent, IFileEvent, Log, MessageDeduplicator,
PuppetBridge, IRemoteUser, IRemoteRoom, IReceiveParams, IMessageEvent, IFileEvent, Log, MessageDeduplicator, Util,
ExpireSet,
} 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 { NewMediaMessage as SkypeNewMediaMessage } from "skype-http/dist/lib/interfaces/api/api";
import * as decodeHtml from "decode-html";
import * as escapeHtml from "escape-html";
@ -24,6 +26,7 @@ const log = new Log("SkypePuppet:skype");
interface ISkypePuppet {
client: Client;
data: any;
deletedMessages: ExpireSet<string>;
}
interface ISkypePuppets {
@ -94,7 +97,7 @@ export class Skype {
return {
user: await this.getUserParams(puppetId, contact),
room: await this.getRoomParams(puppetId, conversation),
eventId: (resource as any).clientId, // tslint:disable-line no-any
eventId: (resource as any).clientId || resource.native.clientmessageid || resource.id, // tslint:disable-line no-any
};
}
@ -127,7 +130,7 @@ export class Skype {
});
client.on("file", async (resource: skypeHttp.resources.FileResource) => {
try {
await this.handleSkypeFile(puppetId, resource);
} catch (err) {
log.error("Error while handling file event", err);
}
@ -148,9 +151,11 @@ export class Skype {
await this.deletePuppet(puppetId);
}
const client = new Client(data.username, data.password);
const TWO_MIN = 120000;
this.puppets[puppetId] = {
client,
data,
deletedMessages: new ExpireSet(TWO_MIN),
};
await this.startClient(puppetId);
}
@ -213,6 +218,7 @@ export class Skype {
if (!p) {
return;
}
log.info("Received message from matrix");
const conversation = await p.client.getConversation(room);
if (!conversation) {
log.warn(`Room ${room.roomId} not found!`);
@ -227,10 +233,99 @@ export class Skype {
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);
const dedupeId = ret && ret.clientMessageId;
const eventId = ret && ret.MessageId;
this.messageDeduplicator.unlock(dedupeKey, p.client.username, dedupeId);
if (eventId) {
await this.puppet.eventStore.insert(room.puppetId, data.eventId!, eventId);
await this.puppet.eventSync.insert(room.puppetId, data.eventId!, eventId);
}
}
public async handleMatrixEdit(room: IRemoteRoom, eventId: string, data: IMessageEvent) {
const p = this.puppets[room.puppetId];
if (!p) {
return;
}
log.info("Received edit 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 = data.formattedBody;
} else {
msg = escapeHtml(data.body);
}
const dedupeKey = `${room.puppetId};${room.roomId}`;
this.messageDeduplicator.lock(dedupeKey, p.client.username, msg);
await p.client.sendEdit(conversation.id, eventId, msg);
const newEventId = "";
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) {
const p = this.puppets[room.puppetId];
if (!p) {
return;
}
log.info("Received edit from matrix");
const conversation = await p.client.getConversation(room);
if (!conversation) {
log.warn(`Room ${room.roomId} not found!`);
return;
}
p.deletedMessages.add(eventId);
await p.client.sendDelete(conversation.id, eventId);
}
public async handleMatrixImage(room: IRemoteRoom, data: IFileEvent) {
await this.handleMatrixFile(room, data, "sendImage");
}
public async handleMatrixAudio(room: IRemoteRoom, data: IFileEvent) {
await this.handleMatrixFile(room, data, "sendAudio");
}
public async handleMatrixFile(room: IRemoteRoom, data: IFileEvent, method?: string) {
if (!method) {
method = "sendDocument";
}
const p = this.puppets[room.puppetId];
if (!p) {
return;
}
log.info("Received file from matrix");
const conversation = await p.client.getConversation(room);
if (!conversation) {
log.warn(`Room ${room.roomId} not found!`);
return;
}
const buffer = await Util.DownloadFile(data.url);
const opts: SkypeNewMediaMessage = {
file: buffer,
name: data.filename,
};
if (data.info) {
if (data.info.w) {
opts.width = data.info.w;
}
if (data.info.h) {
opts.height = data.info.h;
}
}
const dedupeKey = `${room.puppetId};${room.roomId}`;
this.messageDeduplicator.lock(dedupeKey, p.client.username, `file:${data.filename}`);
const ret = await p.client[method](conversation.id, opts);
const dedupeId = ret && ret.clientMessageId;
const eventId = ret && ret.MessageId;
this.messageDeduplicator.unlock(dedupeKey, p.client.username, dedupeId);
if (eventId) {
await this.puppet.eventSync.insert(room.puppetId, data.eventId!, eventId);
}
}
@ -250,28 +345,71 @@ export class Skype {
log.warn("Couldn't generate params");
return;
}
let msg = resource.content;
let emote = false;
if (resource.native && resource.native.skypeemoteoffset) {
emote = true;
msg = msg.substr(Number(resource.native.skypeemoteoffset));
}
const dedupeKey = `${puppetId};${params.room.roomId}`;
if (await this.messageDeduplicator.dedupe(dedupeKey, params.user.userId, params.eventId, resource.content)) {
if (await this.messageDeduplicator.dedupe(dedupeKey, params.user.userId, params.eventId, msg)) {
log.silly("normal message dedupe");
return;
}
if (!rich) {
await this.puppet.sendMessage(params, {
body: resource.content,
body: msg,
emote,
});
} else if (resource.native && resource.native.skypeeditedid) {
if (resource.content) {
await this.puppet.sendEdit(params, resource.native.skypeeditedid, {
body: resource.content,
formattedBody: resource.content,
body: msg,
formattedBody: msg,
emote,
});
} else if (p.deletedMessages.has(resource.native.skypeeditedid)) {
log.silly("normal message redact dedupe");
return;
} else {
await this.puppet.sendRedact(params, resource.native.skypeeditedid);
}
} else {
await this.puppet.sendMessage(params, {
body: resource.content,
formattedBody: resource.content,
body: msg,
formattedBody: msg,
emote,
});
}
}
private async handleSkypeFile(puppetId: number, resource: skypeHttp.resources.FileResource) {
const p = this.puppets[puppetId];
if (!p) {
return;
}
log.info("Got new skype file");
log.silly(resource);
const params = await this.getSendParams(puppetId, resource);
if (!params) {
log.warn("Couldn't generate params");
return;
}
const filename = resource.original_file_name;
const dedupeKey = `${puppetId};${params.room.roomId}`;
if (await this.messageDeduplicator.dedupe(dedupeKey, params.user.userId, params.eventId, `file:${filename}`)) {
log.silly("file message dedupe");
return;
}
if (resource.native && resource.native.skypeeditedid && !resource.uri) {
if (p.deletedMessages.has(resource.native.skypeeditedid)) {
log.silly("file message redact dedupe");
return;
}
await this.puppet.sendRedact(params, resource.native.skypeeditedid);
return;
}
const buffer = await p.client.downloadFile(resource.uri);
await this.puppet.sendFileDetect(params, buffer, filename);
}
}