Add image/audio/file/location bridging. Fixes #9

This commit is contained in:
Tulir Asokan
2017-11-25 23:57:11 +02:00
parent 1caba73147
commit adeb271c1b
5 changed files with 151 additions and 32 deletions
+5
View File
@@ -1652,6 +1652,11 @@
"object-assign": "4.1.1" "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": { "finalhandler": {
"version": "0.3.6", "version": "0.3.6",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.3.6.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.3.6.tgz",
+1
View File
@@ -13,6 +13,7 @@
"colors": "1.1.x", "colors": "1.1.x",
"commander": "2.12.x", "commander": "2.12.x",
"escape-html": "1.0.x", "escape-html": "1.0.x",
"file-type": "^7.3.0",
"marked": "0.3.x", "marked": "0.3.x",
"matrix-appservice-bridge": "1.x.x", "matrix-appservice-bridge": "1.x.x",
"matrix-js-sdk": "0.x.x", "matrix-js-sdk": "0.x.x",
+45 -7
View File
@@ -64,8 +64,36 @@ class Portal {
return true 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) { async updateAvatar(telegramPOV, chat) {
if (!chat.photo) { if (!chat.photo || this.peer.type === "user") {
return false return false
} }
@@ -80,12 +108,11 @@ class Portal {
const file = await telegramPOV.getFile(photo) const file = await telegramPOV.getFile(photo)
const name = `${photo.volume_id}_${photo.local_id}.${file.extension}` const name = `${photo.volume_id}_${photo.local_id}.${file.extension}`
const uploaded = await this.app.botIntent.getClient() const uploaded = await this.app.botIntent.getClient().uploadContent({
.uploadContent({ stream: file.buffer,
stream: Buffer.from(file.bytes), name,
name, type: file.mimetype,
type: file.mimetype, }, { rawResponse: false })
}, { rawResponse: false })
this.avatarURL = uploaded.content_uri this.avatarURL = uploaded.content_uri
this.photo = { this.photo = {
@@ -113,6 +140,17 @@ class Portal {
if (evt.text.length > 0) { if (evt.text.length > 0) {
sender.sendText(this.roomID, evt.text) 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) { async handleMatrixEvent(sender, evt) {
+87 -20
View File
@@ -14,25 +14,56 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
const telegram = require("telegram-mtproto") const telegram = require("telegram-mtproto")
const fileType = require("file-type")
const pkg = require("../package.json") const pkg = require("../package.json")
const TelegramPeer = require("./telegram-peer") const TelegramPeer = require("./telegram-peer")
/* /*
* Mapping from Telegram file types to MIME types and extensions. * Mapping from Telegram file types to MIME types and extensions.
*/ */
const META_FROM_FILETYPE = { function metaFromFileType(type) {
"storage.fileGif": { const extension = type.substr("storage.file".length).toLowerCase()
mimetype: "image/gif", let fileClass, mimetype, matrixtype
extension: "gif", /*eslint no-fallthrough: "off"*/
}, switch (type) {
"storage.fileJpeg": { case "storage.fileGif":
mimetype: "image/jpeg", case "storage.fileJpeg":
extension: "jpeg", case "storage.filePng":
}, case "storage.fileWebp":
"storage.filePng": { fileClass = "image"
mimetype: "image/png", break
extension: "png", 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, to,
source: this, source: this,
text: update.message, 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) { async getFile(location) {
location = Object.assign({}, location, { _: "inputFileLocation" }) if (location.volume_id && location.local_id) {
delete location.dc_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", { const file = await this.client("upload.getFile", {
location, location,
offset: 0, offset: 0,
// Max download size: 100mb
limit: 100 * 1024 * 1024, limit: 100 * 1024 * 1024,
}) })
const meta = META_FROM_FILETYPE[file.type._] file.buffer = Buffer.from(file.bytes)
if (meta) { if (file.type._ === "storage.filePartial") {
file.mimetype = meta.mimetype const { mime, ext } = fileType(file.buffer)
file.extension = meta.extension 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 return file
} }
+13 -5
View File
@@ -138,12 +138,20 @@ class TelegramUser {
return this.intent.sendText(roomID, text) return this.intent.sendText(roomID, text)
} }
sendImage(roomID, opts) { sendFile(roomID, file) {
return this.intent.sendMessage(roomID, { return this.intent.sendMessage(roomID, {
msgtype: "m.image", msgtype: file.matrixtype || "m.file",
url: opts.content_uri, url: file.content_uri,
body: opts.name, body: file.name || "Uploaded file",
info: opts.info, 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,
}) })
} }