1
0
mirror of https://github.com/fabianonline/telegram_backup.git synced 2024-11-22 16:56:16 +00:00

Merge branch 'feature-json2'

This commit is contained in:
Fabian Schlenz 2018-04-20 06:11:11 +02:00
commit 3c68e6d814
13 changed files with 273 additions and 132 deletions

View File

@ -38,7 +38,8 @@ dependencies {
compile 'com.github.spullara.mustache.java:compiler:0.9.5' compile 'com.github.spullara.mustache.java:compiler:0.9.5'
compile 'org.slf4j:slf4j-api:1.7.21' compile 'org.slf4j:slf4j-api:1.7.21'
compile 'ch.qos.logback:logback-classic:1.1.7' compile 'ch.qos.logback:logback-classic:1.1.7'
compile 'com.google.code.gson:gson:2.5' compile 'com.google.code.gson:gson:2.8.0'
compile 'com.github.salomonbrys.kotson:kotson:2.5.0'
compile 'com.github.kittinunf.fuel:fuel:1.12.0' compile 'com.github.kittinunf.fuel:fuel:1.12.0'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'

View File

@ -16,6 +16,7 @@
package de.fabianonline.telegram_backup package de.fabianonline.telegram_backup
import com.github.badoualy.telegram.api.Kotlogram
import com.github.badoualy.telegram.tl.api.* import com.github.badoualy.telegram.tl.api.*
import com.github.badoualy.telegram.tl.core.TLVector import com.github.badoualy.telegram.tl.core.TLVector
import com.github.badoualy.telegram.api.TelegramClient import com.github.badoualy.telegram.api.TelegramClient
@ -42,6 +43,8 @@ import java.nio.file.Files
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
import java.nio.file.FileAlreadyExistsException import java.nio.file.FileAlreadyExistsException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import com.google.gson.*
import com.github.salomonbrys.kotson.*
import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory
@ -109,14 +112,16 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
fun getMessagesWithMediaCount() = queryInt("SELECT COUNT(*) FROM messages WHERE has_media=1") fun getMessagesWithMediaCount() = queryInt("SELECT COUNT(*) FROM messages WHERE has_media=1")
fun getMessagesWithMedia(limit: Int = 0, offset: Int = 0): LinkedList<TLMessage?> { fun getMessagesWithMedia(limit: Int = 0, offset: Int = 0): LinkedList<Pair<Int, JsonObject>> {
try { try {
val list = LinkedList<TLMessage?>() val list = LinkedList<Pair<Int, JsonObject>>()
var query = "SELECT data FROM messages WHERE has_media=1 ORDER BY id" var query = "SELECT id, json FROM messages WHERE has_media=1 AND json IS NOT NULL ORDER BY id"
if (limit > 0) query += " LIMIT ${limit} OFFSET ${offset}" if (limit > 0) query += " LIMIT ${limit} OFFSET ${offset}"
val rs = executeQuery(query) val rs = executeQuery(query)
val parser = JsonParser()
while (rs.next()) { while (rs.next()) {
list.add(bytesToTLMessage(rs.getBytes(1))) val obj = parser.parse(rs.getString(2)).obj
list.add(Pair<Int, JsonObject>(rs.getInt(1), obj))
} }
rs.close() rs.close()
return list return list
@ -266,13 +271,10 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
@Synchronized @Synchronized
fun saveMessages(all: TLVector<TLAbsMessage>, api_layer: Int, source_type: MessageSource = MessageSource.NORMAL, settings: Settings?) { fun saveMessages(all: TLVector<TLAbsMessage>, api_layer: Int, source_type: MessageSource = MessageSource.NORMAL, settings: Settings?) {
//"(id, dialog_id, from_id, from_type, text, time, has_media, data, sticker, type) " + val columns = "(message_id, message_type, source_type, source_id, sender_id, fwd_from_id, text, time, has_media, media_type, media_file, media_size, data, api_layer, json) " +
//"VALUES " +
//"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
val columns = "(message_id, message_type, source_type, source_id, sender_id, fwd_from_id, text, time, has_media, media_type, media_file, media_size, data, api_layer) " +
"VALUES " + "VALUES " +
"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
//1 2 3 4 5 6 7 8 9 10 11 12 13 14 //1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
val ps = conn.prepareStatement("INSERT OR REPLACE INTO messages " + columns) val ps = conn.prepareStatement("INSERT OR REPLACE INTO messages " + columns)
val ps_insert_or_ignore = conn.prepareStatement("INSERT OR IGNORE INTO messages " + columns) val ps_insert_or_ignore = conn.prepareStatement("INSERT OR IGNORE INTO messages " + columns)
@ -327,7 +329,7 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
} }
ps.setString(7, text) ps.setString(7, text)
ps.setString(8, "" + msg.getDate()) ps.setString(8, "" + msg.getDate())
val f = FileManagerFactory.getFileManager(msg, user_manager, file_base, settings) val f = FileManagerFactory.getFileManager(msg, file_base, settings)
if (f == null) { if (f == null) {
ps.setNull(9, Types.BOOLEAN) ps.setNull(9, Types.BOOLEAN)
ps.setNull(10, Types.VARCHAR) ps.setNull(10, Types.VARCHAR)
@ -343,6 +345,7 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
msg.serializeBody(stream) msg.serializeBody(stream)
ps.setBytes(13, stream.toByteArray()) ps.setBytes(13, stream.toByteArray())
ps.setInt(14, api_layer) ps.setInt(14, api_layer)
ps.setString(15, msg.toJson())
ps.addBatch() ps.addBatch()
} else if (msg is TLMessageService) { } else if (msg is TLMessageService) {
ps_insert_or_ignore.setInt(1, msg.getId()) ps_insert_or_ignore.setInt(1, msg.getId())
@ -381,6 +384,7 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
ps_insert_or_ignore.setNull(12, Types.INTEGER) ps_insert_or_ignore.setNull(12, Types.INTEGER)
ps_insert_or_ignore.setNull(13, Types.BLOB) ps_insert_or_ignore.setNull(13, Types.BLOB)
ps_insert_or_ignore.setInt(14, api_layer) ps_insert_or_ignore.setInt(14, api_layer)
ps_insert_or_ignore.setString(15, msg.toJson())
ps_insert_or_ignore.addBatch() ps_insert_or_ignore.addBatch()
} else if (msg is TLMessageEmpty) { } else if (msg is TLMessageEmpty) {
ps_insert_or_ignore.setInt(1, msg.getId()) ps_insert_or_ignore.setInt(1, msg.getId())
@ -397,6 +401,7 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
ps_insert_or_ignore.setNull(12, Types.INTEGER) ps_insert_or_ignore.setNull(12, Types.INTEGER)
ps_insert_or_ignore.setNull(13, Types.BLOB) ps_insert_or_ignore.setNull(13, Types.BLOB)
ps_insert_or_ignore.setInt(14, api_layer) ps_insert_or_ignore.setInt(14, api_layer)
ps_insert_or_ignore.setNull(15, Types.VARCHAR)
ps_insert_or_ignore.addBatch() ps_insert_or_ignore.addBatch()
} else { } else {
throw RuntimeException("Unexpected Message type: " + msg.javaClass) throw RuntimeException("Unexpected Message type: " + msg.javaClass)
@ -418,18 +423,23 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
fun saveChats(all: TLVector<TLAbsChat>) { fun saveChats(all: TLVector<TLAbsChat>) {
val ps_insert_or_replace = conn.prepareStatement( val ps_insert_or_replace = conn.prepareStatement(
"INSERT OR REPLACE INTO chats " + "INSERT OR REPLACE INTO chats " +
"(id, name, type) " + "(id, name, type, json, api_layer) " +
"VALUES " + "VALUES " +
"(?, ?, ?)") "(?, ?, ?, ?, ?)")
val ps_insert_or_ignore = conn.prepareStatement( val ps_insert_or_ignore = conn.prepareStatement(
"INSERT OR IGNORE INTO chats " + "INSERT OR IGNORE INTO chats " +
"(id, name, type) " + "(id, name, type, json, api_layer) " +
"VALUES " + "VALUES " +
"(?, ?, ?)") "(?, ?, ?, ?, ?)")
for (abs in all) { for (abs in all) {
ps_insert_or_replace.setInt(1, abs.getId()) ps_insert_or_replace.setInt(1, abs.getId())
ps_insert_or_ignore.setInt(1, abs.getId()) ps_insert_or_ignore.setInt(1, abs.getId())
val json = abs.toJson()
ps_insert_or_replace.setString(4, json)
ps_insert_or_ignore.setString(4, json)
ps_insert_or_replace.setInt(5, Kotlogram.API_LAYER)
ps_insert_or_ignore.setInt(5, Kotlogram.API_LAYER)
if (abs is TLChatEmpty) { if (abs is TLChatEmpty) {
ps_insert_or_ignore.setNull(2, Types.VARCHAR) ps_insert_or_ignore.setNull(2, Types.VARCHAR)
ps_insert_or_ignore.setString(3, "empty_chat") ps_insert_or_ignore.setString(3, "empty_chat")
@ -470,14 +480,14 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
fun saveUsers(all: TLVector<TLAbsUser>) { fun saveUsers(all: TLVector<TLAbsUser>) {
val ps_insert_or_replace = conn.prepareStatement( val ps_insert_or_replace = conn.prepareStatement(
"INSERT OR REPLACE INTO users " + "INSERT OR REPLACE INTO users " +
"(id, first_name, last_name, username, type, phone) " + "(id, first_name, last_name, username, type, phone, json, api_layer) " +
"VALUES " + "VALUES " +
"(?, ?, ?, ?, ?, ?)") "(?, ?, ?, ?, ?, ?, ?, ?)")
val ps_insert_or_ignore = conn.prepareStatement( val ps_insert_or_ignore = conn.prepareStatement(
"INSERT OR IGNORE INTO users " + "INSERT OR IGNORE INTO users " +
"(id, first_name, last_name, username, type, phone) " + "(id, first_name, last_name, username, type, phone, json, api_layer) " +
"VALUES " + "VALUES " +
"(?, ?, ?, ?, ?, ?)") "(?, ?, ?, ?, ?, ?, ?, ?)")
for (abs in all) { for (abs in all) {
if (abs is TLUser) { if (abs is TLUser) {
val user = abs val user = abs
@ -487,6 +497,8 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
ps_insert_or_replace.setString(4, user.getUsername()) ps_insert_or_replace.setString(4, user.getUsername())
ps_insert_or_replace.setString(5, "user") ps_insert_or_replace.setString(5, "user")
ps_insert_or_replace.setString(6, user.getPhone()) ps_insert_or_replace.setString(6, user.getPhone())
ps_insert_or_replace.setString(7, user.toJson())
ps_insert_or_replace.setInt(8, Kotlogram.API_LAYER)
ps_insert_or_replace.addBatch() ps_insert_or_replace.addBatch()
} else if (abs is TLUserEmpty) { } else if (abs is TLUserEmpty) {
ps_insert_or_ignore.setInt(1, abs.getId()) ps_insert_or_ignore.setInt(1, abs.getId())
@ -495,6 +507,8 @@ class Database constructor(val file_base: String, val user_manager: UserManager)
ps_insert_or_ignore.setNull(4, Types.VARCHAR) ps_insert_or_ignore.setNull(4, Types.VARCHAR)
ps_insert_or_ignore.setString(5, "empty_user") ps_insert_or_ignore.setString(5, "empty_user")
ps_insert_or_ignore.setNull(6, Types.VARCHAR) ps_insert_or_ignore.setNull(6, Types.VARCHAR)
ps_insert_or_ignore.setNull(7, Types.VARCHAR)
ps_insert_or_ignore.setInt(8, Kotlogram.API_LAYER)
ps_insert_or_ignore.addBatch() ps_insert_or_ignore.addBatch()
} else { } else {
throw RuntimeException("Unexpected " + abs.javaClass) throw RuntimeException("Unexpected " + abs.javaClass)

View File

@ -15,6 +15,8 @@ import org.slf4j.LoggerFactory
import org.slf4j.Logger import org.slf4j.Logger
import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory
import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
import com.github.salomonbrys.kotson.*
import com.google.gson.*
class DatabaseUpdates(protected var conn: Connection, protected var db: Database) { class DatabaseUpdates(protected var conn: Connection, protected var db: Database) {
@ -33,6 +35,7 @@ class DatabaseUpdates(protected var conn: Connection, protected var db: Database
register(DB_Update_8(conn, db)) register(DB_Update_8(conn, db))
register(DB_Update_9(conn, db)) register(DB_Update_9(conn, db))
register(DB_Update_10(conn, db)) register(DB_Update_10(conn, db))
register(DB_Update_11(conn, db))
} }
fun doUpdates() { fun doUpdates() {
@ -313,7 +316,7 @@ internal class DB_Update_6(conn: Connection, db: Database) : DatabaseUpdate(conn
} else { } else {
ps.setInt(1, msg.getFwdFrom().getFromId()) ps.setInt(1, msg.getFwdFrom().getFromId())
} }
val f = FileManagerFactory.getFileManager(msg, db.user_manager, db.file_base, settings = null) val f = FileManagerFactory.getFileManager(msg, db.file_base, settings = null)
if (f == null) { if (f == null) {
ps.setNull(2, Types.VARCHAR) ps.setNull(2, Types.VARCHAR)
ps.setNull(3, Types.VARCHAR) ps.setNull(3, Types.VARCHAR)
@ -451,3 +454,46 @@ internal class DB_Update_10(conn: Connection, db: Database) : DatabaseUpdate(con
execute("CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)") execute("CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)")
} }
} }
internal class DB_Update_11(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
override val version = 11
val logger = LoggerFactory.getLogger(DB_Update_11::class.java)
override fun _doUpdate() {
execute("ALTER TABLE messages ADD COLUMN json TEXT NULL")
execute("ALTER TABLE chats ADD COLUMN json TEXT NULL")
execute("ALTER TABLE chats ADD COLUMN api_layer INTEGER NULL")
execute("ALTER TABLE users ADD COLUMN json TEXT NULL")
execute("ALTER TABLE users ADD COLUMN api_layer INTEGER NULL")
val limit = 5000
var offset = 0
var i: Int
val ps = conn.prepareStatement("UPDATE messages SET json=? WHERE id=?")
println(" Updating messages to add their JSON representation to the database. This might take a few moments...")
print(" ")
do {
i = 0
logger.debug("Querying with limit $limit, offset is now $offset")
val rs = db.executeQuery("SELECT id, data FROM messages WHERE json IS NULL AND api_layer=53 LIMIT $limit")
while (rs.next()) {
i++
val id = rs.getInt(1)
val msg = Database.bytesToTLMessage(rs.getBytes(2))
val json = if (msg==null) Gson().toJson(null) else msg.toJson()
ps.setString(1, json)
ps.setInt(2, id)
ps.addBatch()
}
rs.close()
conn.setAutoCommit(false)
ps.executeBatch()
ps.clearBatch()
conn.commit()
conn.setAutoCommit(true)
offset += limit
print(".")
} while (i >= limit)
println()
ps.close()
}
}

View File

@ -36,6 +36,7 @@ import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.slf4j.Logger import org.slf4j.Logger
import com.google.gson.Gson import com.google.gson.Gson
import com.github.salomonbrys.kotson.*
import com.github.kittinunf.fuel.Fuel import com.github.kittinunf.fuel.Fuel
import com.github.kittinunf.result.Result import com.github.kittinunf.result.Result
import com.github.kittinunf.result.getAs import com.github.kittinunf.result.getAs
@ -242,20 +243,21 @@ class DownloadManager(val client: TelegramClient, val prog: DownloadProgressInte
if (messages.size == 0) break if (messages.size == 0) break
offset += limit offset += limit
logger.debug("Database returned {} messages with media", messages.size) logger.debug("Database returned {} messages with media", messages.size)
for (msg in messages) { for (pair in messages) {
if (msg == null) continue val id = pair.first
val m = FileManagerFactory.getFileManager(msg, user_manager, file_base, settings=settings) val json = pair.second
try {
val m = FileManagerFactory.getFileManager(json, file_base, settings=settings)!!
logger.trace("message {}, {}, {}, {}, {}", logger.trace("message {}, {}, {}, {}, {}",
msg.getId(), id,
msg.getMedia().javaClass.getSimpleName().replace("TLMessageMedia", ""), m.javaClass.getSimpleName(),
m!!.javaClass.getSimpleName(),
if (m.isEmpty) "empty" else "non-empty", if (m.isEmpty) "empty" else "non-empty",
if (m.downloaded) "downloaded" else "not downloaded") if (m.downloaded) "downloaded" else "not downloaded")
if (m.isEmpty) { if (m.isEmpty) {
prog.onMediaDownloadedEmpty() prog.onMediaDownloadedEmpty()
} else if (m.downloaded) { } else if (m.downloaded) {
prog.onMediaAlreadyPresent(m) prog.onMediaAlreadyPresent(m)
} else if (settings.max_file_age>0 && (System.currentTimeMillis() / 1000) - msg.date > settings.max_file_age * 24 * 60 * 60) { } else if (settings.max_file_age>0 && (System.currentTimeMillis() / 1000) - json["date"].int > settings.max_file_age * 24 * 60 * 60) {
prog.onMediaSkipped() prog.onMediaSkipped()
} else if (settings.max_file_size>0 && settings.max_file_size*1024*1024 > m.size) { } else if (settings.max_file_size>0 && settings.max_file_size*1024*1024 > m.size) {
prog.onMediaSkipped() prog.onMediaSkipped()
@ -272,6 +274,10 @@ class DownloadManager(val client: TelegramClient, val prog: DownloadProgressInte
prog.onMediaFailed() prog.onMediaFailed()
} }
} }
} catch (e: IllegalStateException) {
println(json.toPrettyJson())
throw e
}
} }
} }
prog.onMediaDownloadFinished() prog.onMediaDownloadFinished()

View File

@ -129,7 +129,7 @@ internal class TelegramUpdateHandler(val user_manager: UserManager, val db: Data
db.saveMessages(vector, Kotlogram.API_LAYER, settings=settings) db.saveMessages(vector, Kotlogram.API_LAYER, settings=settings)
System.out.print('.') System.out.print('.')
if (abs_msg is TLMessage && settings.download_media==true) { if (abs_msg is TLMessage && settings.download_media==true) {
val fm = FileManagerFactory.getFileManager(abs_msg, user_manager, file_base, settings) val fm = FileManagerFactory.getFileManager(abs_msg, file_base, settings)
if (fm != null && !fm.isEmpty && !fm.downloaded) { if (fm != null && !fm.isEmpty && !fm.downloaded) {
try { try {
fm.download() fm.download()

View File

@ -17,11 +17,16 @@
package de.fabianonline.telegram_backup package de.fabianonline.telegram_backup
import com.github.badoualy.telegram.tl.exception.RpcErrorException import com.github.badoualy.telegram.tl.exception.RpcErrorException
import com.github.badoualy.telegram.tl.api.TLAbsMessage
import com.github.badoualy.telegram.tl.api.TLAbsUser
import com.github.badoualy.telegram.tl.api.TLAbsChat
import com.github.badoualy.telegram.api.Kotlogram
import java.io.File import java.io.File
import java.util.Vector import java.util.Vector
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException import java.util.concurrent.TimeoutException
import com.google.gson.* import com.google.gson.*
import com.github.salomonbrys.kotson.*
import java.net.URL import java.net.URL
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import de.fabianonline.telegram_backup.Version import de.fabianonline.telegram_backup.Version
@ -222,4 +227,42 @@ fun String.anonymize(): String {
fun Any.toJson(): String = Gson().toJson(this) fun Any.toJson(): String = Gson().toJson(this)
fun Any.toPrettyJson(): String = GsonBuilder().setPrettyPrinting().create().toJson(this) fun Any.toPrettyJson(): String = GsonBuilder().setPrettyPrinting().create().toJson(this)
fun JsonObject.isA(name: String): Boolean = this.contains("_constructor") && this["_constructor"].string.startsWith(name + "#")
fun JsonElement.isA(name: String): Boolean = this.obj.isA(name)
class MaxTriesExceededException(): RuntimeException("Max tries exceeded") {} class MaxTriesExceededException(): RuntimeException("Max tries exceeded") {}
fun TLAbsMessage.toJson(): String {
val json = Gson().toJsonTree(this).obj
cleanUpMessageJson(json)
json["api_layer"] = Kotlogram.API_LAYER
return json.toString()
}
fun TLAbsChat.toJson(): String {
val json = Gson().toJsonTree(this).obj
json["api_layer"] = Kotlogram.API_LAYER
return json.toString()
}
fun TLAbsUser.toJson(): String {
val json = Gson().toJsonTree(this).obj
json["api_layer"] = Kotlogram.API_LAYER
return json.toString()
}
fun cleanUpMessageJson(json : JsonElement) {
if (json.isJsonArray) {
json.array.forEach {cleanUpMessageJson(it)}
return
} else if (!json.isJsonObject) {
return
}
if (json.obj.has("bytes")) {
json.obj -= "bytes"
return
}
json.obj.forEach {_: String, elm: JsonElement ->
if (elm.isJsonObject || elm.isJsonArray) cleanUpMessageJson(elm)
}
}

View File

@ -26,14 +26,17 @@ import de.fabianonline.telegram_backup.Settings
import java.io.IOException import java.io.IOException
import java.io.File import java.io.File
import java.util.concurrent.TimeoutException import java.util.concurrent.TimeoutException
import com.google.gson.*
import com.github.salomonbrys.kotson.*
import de.fabianonline.telegram_backup.*
abstract class AbstractMediaFileManager(protected var message: TLMessage, protected var user: UserManager, val file_base: String) { abstract class AbstractMediaFileManager(private var json: JsonObject, val file_base: String) {
open var isEmpty = false open var isEmpty = false
abstract val size: Int abstract val size: Int
abstract val extension: String abstract val extension: String
open val downloaded: Boolean open val downloaded: Boolean
get() = File(targetPathAndFilename).isFile() get() = !isEmpty && File(targetPathAndFilename).isFile()
val downloading: Boolean val downloading: Boolean
get() = File("${targetPathAndFilename}.downloading").isFile() get() = File("${targetPathAndFilename}.downloading").isFile()
@ -47,10 +50,10 @@ abstract class AbstractMediaFileManager(protected var message: TLMessage, protec
open val targetFilename: String open val targetFilename: String
get() { get() {
val message_id = message.getId() val message_id = json["id"].int
var to = message.getToId() var to = json["toId"].obj
if (to is TLPeerChannel) { if (to.isA("peerChannel")) {
val channel_id = to.getChannelId() val channel_id = to["channelId"].int
return "channel_${channel_id}_${message_id}.$extension" return "channel_${channel_id}_${message_id}.$extension"
} else return "${message_id}.$extension" } else return "${message_id}.$extension"
} }
@ -77,8 +80,8 @@ abstract class AbstractMediaFileManager(protected var message: TLMessage, protec
} }
companion object { companion object {
fun throwUnexpectedObjectError(o: Any) { fun throwUnexpectedObjectError(constructor: String) {
throw RuntimeException("Unexpected " + o.javaClass.getName()) throw RuntimeException("Unexpected ${constructor}")
} }
} }
} }

View File

@ -24,44 +24,38 @@ import com.github.badoualy.telegram.tl.exception.RpcErrorException
import java.io.IOException import java.io.IOException
import java.util.concurrent.TimeoutException import java.util.concurrent.TimeoutException
import com.google.gson.*
import com.github.salomonbrys.kotson.*
import de.fabianonline.telegram_backup.*
open class DocumentFileManager(msg: TLMessage, user: UserManager, file_base: String) : AbstractMediaFileManager(msg, user, file_base) { open class DocumentFileManager(message: JsonObject, file_base: String) : AbstractMediaFileManager(message, file_base) {
protected var doc: TLDocument? = null //protected var doc: TLDocument? = null
override lateinit var extension: String override lateinit var extension: String
open val isSticker: Boolean open val isSticker: Boolean
get() { get() = json.get("attributes")?.array?.any{it.obj.isA("documentAttributeSticker")} ?: false
if (this.isEmpty || doc == null) return false
return doc!!.getAttributes()?.filter { it is TLDocumentAttributeSticker }?.isNotEmpty() ?: false
}
override val size: Int override val size: Int
get() = if (doc != null) doc!!.getSize() else 0 get() = json["size"].int
open override val letter: String = "d" open override val letter: String = "d"
open override val name: String = "document" open override val name: String = "document"
open override val description: String = "Document" open override val description: String = "Document"
private val json = message["media"]["document"].obj
init { init {
val d = (msg.getMedia() as TLMessageMediaDocument).getDocument()
if (d is TLDocument) {
this.doc = d
} else if (d is TLDocumentEmpty) {
this.isEmpty = true
} else {
throwUnexpectedObjectError(d)
}
extension = processExtension() extension = processExtension()
} }
private fun processExtension(): String { private fun processExtension(): String {
if (doc == null) return "empty" //if (doc == null) return "empty"
var ext: String? = null var ext: String? = null
var original_filename: String? = null var original_filename: String? = null
if (doc!!.getAttributes() != null) if (json.contains("attributes"))
for (attr in doc!!.getAttributes()) { for (attr in json["attributes"].array) {
if (attr is TLDocumentAttributeFilename) { if (attr.obj["_constructor"].string.startsWith("documentAttributeFilename")) {
original_filename = attr.getFileName() original_filename = attr.obj["fileName"].string
} }
} }
if (original_filename != null) { if (original_filename != null) {
@ -70,7 +64,7 @@ open class DocumentFileManager(msg: TLMessage, user: UserManager, file_base: Str
} }
if (ext == null) { if (ext == null) {
ext = extensionFromMimetype(doc!!.getMimeType()) ext = extensionFromMimetype(json["mimeType"].string)
} }
// Sometimes, extensions contain a trailing double quote. Remove this. Fixes #12. // Sometimes, extensions contain a trailing double quote. Remove this. Fixes #12.
@ -81,9 +75,7 @@ open class DocumentFileManager(msg: TLMessage, user: UserManager, file_base: Str
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class) @Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
override fun download(): Boolean { override fun download(): Boolean {
if (doc != null) { DownloadManager.downloadFile(targetPathAndFilename, size, json["dcId"].int, json["id"].long, json["accessHash"].long)
DownloadManager.downloadFile(targetPathAndFilename, size, doc!!.getDcId(), doc!!.getId(), doc!!.getAccessHash())
}
return true return true
} }
} }

View File

@ -36,38 +36,55 @@ import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.util.ArrayList import java.util.ArrayList
import java.util.LinkedList import java.util.LinkedList
import java.util.NoSuchElementException
import java.net.URL import java.net.URL
import java.util.concurrent.TimeoutException import java.util.concurrent.TimeoutException
import com.google.gson.*
import com.github.salomonbrys.kotson.*
import de.fabianonline.telegram_backup.*
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
object FileManagerFactory { object FileManagerFactory {
fun getFileManager(m: TLMessage?, u: UserManager, file_base: String, settings: Settings?): AbstractMediaFileManager? { fun getFileManager(m: TLMessage?, file_base: String, settings: Settings?): AbstractMediaFileManager? {
if (m == null) return null if (m == null) return null
val media = m.getMedia() ?: return null val json = Gson().toJsonTree(m).obj
return getFileManager(json, file_base, settings)
}
if (media is TLMessageMediaPhoto) { fun getFileManager(message: JsonObject?, file_base: String, settings: Settings?): AbstractMediaFileManager? {
return PhotoFileManager(m, u, file_base) if (message == null) return null
} else if (media is TLMessageMediaDocument) { try {
val d = DocumentFileManager(m, u, file_base) val media = message.get("media")?.obj ?: return null
return if (d.isSticker) {
StickerFileManager(m, u, file_base) if (media.isA("messageMediaPhoto")) {
} else d return PhotoFileManager(message, file_base)
} else if (media is TLMessageMediaGeo) { } else if (media.isA("messageMediaDocument")) {
return GeoFileManager(m, u, file_base, settings) val d = DocumentFileManager(message, file_base)
} else if (media is TLMessageMediaEmpty) { return if (d.isSticker) StickerFileManager(message, file_base) else d
return UnsupportedFileManager(m, u, file_base, "empty") } else if (media.isA("messageMediaGeo")) {
} else if (media is TLMessageMediaUnsupported) { return GeoFileManager(message, file_base, settings)
return UnsupportedFileManager(m, u, file_base, "unsupported") } else if (media.isA("messageMediaEmpty")) {
} else if (media is TLMessageMediaWebPage) { return UnsupportedFileManager(message, file_base, "empty")
return UnsupportedFileManager(m, u, file_base, "webpage") } else if (media.isA("messageMediaUnsupported")) {
} else if (media is TLMessageMediaContact) { return UnsupportedFileManager(message, file_base, "unsupported")
return UnsupportedFileManager(m, u, file_base, "contact") } else if (media.isA("messageMediaWebPage")) {
} else if (media is TLMessageMediaVenue) { return UnsupportedFileManager(message, file_base, "webpage")
return UnsupportedFileManager(m, u, file_base, "venue") } else if (media.isA("messageMediaContact")) {
} else { return UnsupportedFileManager(message, file_base, "contact")
AbstractMediaFileManager.throwUnexpectedObjectError(media) } else if (media.isA("messageMediaVenue")) {
return UnsupportedFileManager(message, file_base, "venue")
} else {
AbstractMediaFileManager.throwUnexpectedObjectError(media["_constructor"].string)
}
} catch (e: IllegalStateException) {
println(message.toPrettyJson())
throw e
} catch (e: NoSuchElementException) {
println(message.toPrettyJson())
throw e
} }
return null return null
} }
} }

View File

@ -25,9 +25,12 @@ import de.fabianonline.telegram_backup.Settings
import java.io.IOException import java.io.IOException
import java.io.File import java.io.File
import com.google.gson.*
import com.github.salomonbrys.kotson.*
import de.fabianonline.telegram_backup.Utils
class GeoFileManager(msg: TLMessage, user: UserManager, file_base: String, val settings: Settings?) : AbstractMediaFileManager(msg, user, file_base) { class GeoFileManager(message: JsonObject, file_base: String, val settings: Settings?) : AbstractMediaFileManager(message, file_base) {
protected lateinit var geo: TLGeoPoint //protected lateinit var geo: TLGeoPoint
// We don't know the size, so we just guess. // We don't know the size, so we just guess.
override val size: Int override val size: Int
@ -43,7 +46,10 @@ class GeoFileManager(msg: TLMessage, user: UserManager, file_base: String, val s
override val name = "geo" override val name = "geo"
override val description = "Geolocation" override val description = "Geolocation"
val json = message["media"]["geo"].obj
init { init {
/*
val g = (msg.getMedia() as TLMessageMediaGeo).getGeo() val g = (msg.getMedia() as TLMessageMediaGeo).getGeo()
if (g is TLGeoPoint) { if (g is TLGeoPoint) {
this.geo = g this.geo = g
@ -51,16 +57,16 @@ class GeoFileManager(msg: TLMessage, user: UserManager, file_base: String, val s
this.isEmpty = true this.isEmpty = true
} else { } else {
throwUnexpectedObjectError(g) throwUnexpectedObjectError(g)
} }*/
} }
@Throws(IOException::class) @Throws(IOException::class)
override fun download(): Boolean { override fun download(): Boolean {
val url = "https://maps.googleapis.com/maps/api/staticmap?" + val url = "https://maps.googleapis.com/maps/api/staticmap?" +
"center=${geo.getLat()},${geo.getLong()}&" + "center=${json["lat"].float},${json["_long"].float}&" +
"markers=color:red|${geo.getLat()},${geo.getLong()}&" + "markers=color:red|${json["lat"].float},${json["_long"].float}&" +
"zoom=14&size=300x150&scale=2&format=png&" + "zoom=14&size=300x150&scale=2&format=png&" +
"key=" + (settings?.gmaps_key ?: Config.SECRET_GMAPS) "key=" + (settings?.gmaps_key)
return DownloadManager.downloadExternalFile(targetPathAndFilename, url) return DownloadManager.downloadExternalFile(targetPathAndFilename, url)
} }
} }

View File

@ -25,45 +25,63 @@ import com.github.badoualy.telegram.tl.exception.RpcErrorException
import java.io.IOException import java.io.IOException
import java.util.concurrent.TimeoutException import java.util.concurrent.TimeoutException
class PhotoFileManager(msg: TLMessage, user: UserManager, file_base: String) : AbstractMediaFileManager(msg, user, file_base) { import com.google.gson.*
private lateinit var photo: TLPhoto import com.github.salomonbrys.kotson.*
import de.fabianonline.telegram_backup.*
class PhotoFileManager(message: JsonObject, file_base: String) : AbstractMediaFileManager(message, file_base) {
//private lateinit var photo: TLPhoto
override var size = 0 override var size = 0
private lateinit var photo_size: TLPhotoSize //private lateinit var photo_size: TLPhotoSize
override val extension = "jpg" override val extension = "jpg"
override val letter = "p" override val letter = "p"
override val name = "photo" override val name = "photo"
override val description = "Photo" override val description = "Photo"
init { val biggestSize: JsonObject
val p = (msg.getMedia() as TLMessageMediaPhoto).getPhoto() var biggestSizeW = 0
if (p is TLPhoto) { var biggestSizeH = 0
this.photo = p
var biggest: TLPhotoSize? = null val json = message["media"]["photo"].obj
for (s in photo.getSizes()) override var isEmpty = json.isA("photoEmpty")
if (s is TLPhotoSize) {
if (biggest == null || s.getW() > biggest.getW() && s.getH() > biggest.getH()) { init {
biggest = s /*val p = (msg.getMedia() as TLMessageMediaPhoto).getPhoto()*/
} if (!isEmpty) {
var bsTemp: JsonObject? = null
for (elm in json["sizes"].array) {
val s = elm.obj
if (!s.isA("photoSize")) continue
if (bsTemp == null || (s["w"].int > biggestSizeW && s["h"].int > biggestSizeH)) {
bsTemp = s
biggestSizeW = s["w"].int
biggestSizeH = s["h"].int
size = s["size"].int // file size
} }
if (biggest == null) {
throw RuntimeException("Could not find a size for a photo.")
} }
this.photo_size = biggest
this.size = biggest.getSize() if (bsTemp == null) throw RuntimeException("Could not find a size for a photo.")
} else if (p is TLPhotoEmpty) { biggestSize = bsTemp
} else {
biggestSize = JsonObject()
}
/*} else if (p is TLPhotoEmpty) {
this.isEmpty = true this.isEmpty = true
} else { } else {
throwUnexpectedObjectError(p) throwUnexpectedObjectError(p)
} }*/
} }
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class) @Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
override fun download(): Boolean { override fun download(): Boolean {
if (isEmpty) return true /*if (isEmpty) return true*/
val loc = photo_size.getLocation() as TLFileLocation //val loc = photo_size.getLocation() as TLFileLocation
DownloadManager.downloadFile(targetPathAndFilename, size, loc.getDcId(), loc.getVolumeId(), loc.getLocalId(), loc.getSecret())
val loc = biggestSize["location"].obj
DownloadManager.downloadFile(targetPathAndFilename, size, loc["dcId"].int, loc["volumeId"].long, loc["localId"].int, loc["secret"].long)
return true return true
} }
} }

View File

@ -49,29 +49,23 @@ import java.util.concurrent.TimeoutException
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
class StickerFileManager(msg: TLMessage, user: UserManager, file_base: String) : DocumentFileManager(msg, user, file_base) { import com.google.gson.*
import com.github.salomonbrys.kotson.*
import de.fabianonline.telegram_backup.*
class StickerFileManager(message: JsonObject, file_base: String) : DocumentFileManager(message, file_base) {
override val isSticker = true override val isSticker = true
val json = message["media"]["document"].obj
val sticker = json["attributes"].array.first{it.obj.isA("documentAttributeSticker")}.obj
override var isEmpty = sticker["stickerset"].obj.isA("inputStickerSetEmpty")
private val filenameBase: String private val filenameBase: String
get() { get() {
var sticker: TLDocumentAttributeSticker? = null val set = sticker["stickerset"].obj.get("shortName").nullString ?: sticker["stickerset"].obj.get("id").string
for (attr in doc!!.getAttributes()) { val hash = sticker["alt"].string.hashCode()
if (attr is TLDocumentAttributeSticker) { return "${set}_${hash}"
sticker = attr
}
}
val file = StringBuilder()
val set = sticker!!.getStickerset()
if (set is TLInputStickerSetShortName) {
file.append(set.getShortName())
} else if (set is TLInputStickerSetID) {
file.append(set.getId())
}
file.append("_")
file.append(sticker.getAlt().hashCode())
return file.toString()
} }
override val targetFilename: String override val targetFilename: String

View File

@ -19,8 +19,9 @@ package de.fabianonline.telegram_backup.mediafilemanager
import de.fabianonline.telegram_backup.UserManager import de.fabianonline.telegram_backup.UserManager
import com.github.badoualy.telegram.tl.api.* import com.github.badoualy.telegram.tl.api.*
import com.google.gson.JsonObject
class UnsupportedFileManager(msg: TLMessage, user: UserManager, type: String, file_base: String) : AbstractMediaFileManager(msg, user, file_base) { class UnsupportedFileManager(json: JsonObject, file_base: String, type: String) : AbstractMediaFileManager(json, file_base) {
override var name = type override var name = type
override val targetFilename = "" override val targetFilename = ""
override val targetPath = "" override val targetPath = ""