diff --git a/package-lock.json b/package-lock.json index b11499d8..f2c26b81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1652,6 +1652,11 @@ "object-assign": "4.1.1" } }, + "file-type": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-7.3.0.tgz", + "integrity": "sha1-GIaXYOEMNsBBbVMINoT8/A1Z3zo=" + }, "finalhandler": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.3.6.tgz", diff --git a/package.json b/package.json index 4c00fa0a..107a6cb7 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "colors": "1.1.x", "commander": "2.12.x", "escape-html": "1.0.x", + "file-type": "^7.3.0", "marked": "0.3.x", "matrix-appservice-bridge": "1.x.x", "matrix-js-sdk": "0.x.x", diff --git a/src/portal.js b/src/portal.js index 097e0871..f4867c39 100644 --- a/src/portal.js +++ b/src/portal.js @@ -64,8 +64,36 @@ class Portal { return true } + + async copyPhotoSize(telegramPOV, sender, photo) { + const size = photo.sizes.slice(-1)[0] + const uploaded = await this.copyFile(telegramPOV, sender, size.location, photo.id) + uploaded.info.h = size.h + uploaded.info.w = size.w + uploaded.info.size = size.size + uploaded.info.orientation = 0 + return uploaded + } + + async copyFile(telegramPOV, sender, location, id) { + console.log(JSON.stringify(location, "", " ")) + id = id || location.id + const file = await telegramPOV.getFile(location) + const uploaded = await sender.intent.getClient().uploadContent({ + stream: file.buffer, + name: `${id}.${file.extension}`, + type: file.mimetype, + }, { rawResponse: false }) + uploaded.matrixtype = file.matrixtype + uploaded.info = { + mimetype: file.mimetype, + size: location.size, + } + return uploaded + } + async updateAvatar(telegramPOV, chat) { - if (!chat.photo) { + if (!chat.photo || this.peer.type === "user") { return false } @@ -80,12 +108,11 @@ class Portal { const file = await telegramPOV.getFile(photo) const name = `${photo.volume_id}_${photo.local_id}.${file.extension}` - const uploaded = await this.app.botIntent.getClient() - .uploadContent({ - stream: Buffer.from(file.bytes), - name, - type: file.mimetype, - }, { rawResponse: false }) + const uploaded = await this.app.botIntent.getClient().uploadContent({ + stream: file.buffer, + name, + type: file.mimetype, + }, { rawResponse: false }) this.avatarURL = uploaded.content_uri this.photo = { @@ -113,6 +140,17 @@ class Portal { if (evt.text.length > 0) { sender.sendText(this.roomID, evt.text) } + if (evt.photo) { + const photo = await this.copyPhoto(evt.source, sender, evt.photo) + photo.name = evt.caption || "Photo" + sender.sendFile(this.roomID, photo) + } else if (evt.document) { + const file = await this.copyFile(evt.source, sender, evt.document) + file.name = evt.caption || "File upload" + sender.sendFile(this.roomID, file) + } else if (evt.geo) { + sender.sendLocation(this.roomID, evt.geo) + } } async handleMatrixEvent(sender, evt) { diff --git a/src/telegram-puppet.js b/src/telegram-puppet.js index fc0c7a2e..0daf0bdb 100644 --- a/src/telegram-puppet.js +++ b/src/telegram-puppet.js @@ -14,25 +14,56 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . const telegram = require("telegram-mtproto") +const fileType = require("file-type") const pkg = require("../package.json") const TelegramPeer = require("./telegram-peer") - /* * Mapping from Telegram file types to MIME types and extensions. */ -const META_FROM_FILETYPE = { - "storage.fileGif": { - mimetype: "image/gif", - extension: "gif", - }, - "storage.fileJpeg": { - mimetype: "image/jpeg", - extension: "jpeg", - }, - "storage.filePng": { - mimetype: "image/png", - extension: "png", - }, +function metaFromFileType(type) { + const extension = type.substr("storage.file".length).toLowerCase() + let fileClass, mimetype, matrixtype + /*eslint no-fallthrough: "off"*/ + switch (type) { + case "storage.fileGif": + case "storage.fileJpeg": + case "storage.filePng": + case "storage.fileWebp": + fileClass = "image" + break + case "storage.fileMov": + mimetype = "quicktime" + case "storage.fileMp4": + fileClass = "video" + break + case "storage.fileMp3": + mimetype = "mpeg" + fileClass = "audio" + break + case "storage.filePartial": + throw new Error("Partial files should be completed before fetching their type.") + case "storage.fileUnknown": + fileClass = "application" + mimetype = "octet-stream" + matrixtype = "m.file" + break + default: + return undefined + } + mimetype = `${fileClass}/${mimetype || extension}` + matrixtype = matrixtype || `m.${fileClass}` + return { mimetype, extension, matrixtype } +} + +function matrixFromMime(mime) { + if (mime.startsWith("audio/")) { + return "m.audio" + } else if (mime.startsWith("video/")) { + return "m.video" + } else if (mime.startsWith("image/")) { + return "m.image" + } + return "m.file" } /** @@ -278,6 +309,18 @@ class TelegramPuppet { to, source: this, text: update.message, + photo: update.media && update.media._ === "messageMediaPhoto" + ? update.media.photo + : undefined, + document: update.media && update.media._ === "messageMediaDocument" + ? update.media.document + : undefined, + geo: update.media && update.media._ === "messageMediaGeo" + ? update.media.geo + : undefined, + caption: update.media ? + update.media.caption + : undefined, }) } @@ -352,17 +395,41 @@ class TelegramPuppet { } async getFile(location) { - location = Object.assign({}, location, { _: "inputFileLocation" }) - delete location.dc_id + if (location.volume_id && location.local_id) { + location = { + _: "inputFileLocation", + volume_id: location.volume_id, + local_id: location.local_id, + secret: location.secret, + } + } else if (location.id && location.access_hash) { + location = { + _: "inputDocumentFileLocation", + id: location.id, + access_hash: location.access_hash, + } + } else { + throw new Error("Unrecognized file location type.") + } const file = await this.client("upload.getFile", { location, offset: 0, + // Max download size: 100mb limit: 100 * 1024 * 1024, }) - const meta = META_FROM_FILETYPE[file.type._] - if (meta) { - file.mimetype = meta.mimetype - file.extension = meta.extension + file.buffer = Buffer.from(file.bytes) + if (file.type._ === "storage.filePartial") { + const { mime, ext } = fileType(file.buffer) + file.mimetype = mime + file.extension = ext + file.matrixtype = matrixFromMime(mime) + } else { + const meta = metaFromFileType(file.type._) + if (meta) { + file.mimetype = meta.mimetype + file.extension = meta.extension + file.matrixtype = meta.matrixtype + } } return file } diff --git a/src/telegram-user.js b/src/telegram-user.js index 5dad0e6d..b039ee71 100644 --- a/src/telegram-user.js +++ b/src/telegram-user.js @@ -138,12 +138,20 @@ class TelegramUser { return this.intent.sendText(roomID, text) } - sendImage(roomID, opts) { + sendFile(roomID, file) { return this.intent.sendMessage(roomID, { - msgtype: "m.image", - url: opts.content_uri, - body: opts.name, - info: opts.info, + msgtype: file.matrixtype || "m.file", + url: file.content_uri, + body: file.name || "Uploaded file", + info: file.info, + }) + } + + sendLocation(roomID, { long = 0.0, lat = 0.0, body = "Location" } = {}) { + return this.intent.sendMessage(roomID, { + msgtype: "m.location", + geo_uri: `geo:${lat},${long}`, + body, }) }