mirror of
https://github.com/fabianonline/telegram_backup.git
synced 2024-11-22 16:56:16 +00:00
Merge branch 'feature-rewrite'
This commit is contained in:
commit
1ff540977e
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,4 +1,4 @@
|
|||||||
#Thu Oct 06 11:24:39 CEST 2016
|
#Fri Mar 23 06:07:28 CET 2018
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package de.fabianonline.telegram_backup
|
||||||
|
|
||||||
|
class Account(val file_base: String, val phone_number: String) {
|
||||||
|
|
||||||
|
}
|
@ -22,120 +22,64 @@ import com.github.badoualy.telegram.mtproto.auth.AuthKey
|
|||||||
import com.github.badoualy.telegram.mtproto.model.MTSession
|
import com.github.badoualy.telegram.mtproto.model.MTSession
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.slf4j.Logger
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
internal class ApiStorage(prefix: String?) : TelegramApiStorage {
|
internal class ApiStorage(val base_dir: String) : TelegramApiStorage {
|
||||||
private var prefix: String? = null
|
var auth_key: AuthKey? = null
|
||||||
private var do_save = false
|
var dc: DataCenter? = null
|
||||||
private var auth_key: AuthKey? = null
|
val file_auth_key: File
|
||||||
private var dc: DataCenter? = null
|
val file_dc: File
|
||||||
private var file_auth_key: File? = null
|
val logger = LoggerFactory.getLogger(ApiStorage::class.java)
|
||||||
private var file_dc: File? = null
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.setPrefix(prefix)
|
file_auth_key = File(base_dir + Config.FILE_NAME_AUTH_KEY)
|
||||||
}
|
file_dc = File(base_dir + Config.FILE_NAME_DC)
|
||||||
|
|
||||||
fun setPrefix(prefix: String?) {
|
|
||||||
this.prefix = prefix
|
|
||||||
this.do_save = this.prefix != null
|
|
||||||
if (this.do_save) {
|
|
||||||
val base = Config.FILE_BASE +
|
|
||||||
File.separatorChar +
|
|
||||||
this.prefix +
|
|
||||||
File.separatorChar
|
|
||||||
this.file_auth_key = File(base + Config.FILE_NAME_AUTH_KEY)
|
|
||||||
this.file_dc = File(base + Config.FILE_NAME_DC)
|
|
||||||
this._saveAuthKey()
|
|
||||||
this._saveDc()
|
|
||||||
} else {
|
|
||||||
this.file_auth_key = null
|
|
||||||
this.file_dc = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveAuthKey(authKey: AuthKey) {
|
override fun saveAuthKey(authKey: AuthKey) {
|
||||||
this.auth_key = authKey
|
FileUtils.writeByteArrayToFile(file_auth_key, authKey.key)
|
||||||
this._saveAuthKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun _saveAuthKey() {
|
|
||||||
if (this.do_save && this.auth_key != null) {
|
|
||||||
try {
|
|
||||||
FileUtils.writeByteArrayToFile(this.file_auth_key, this.auth_key!!.key)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadAuthKey(): AuthKey? {
|
override fun loadAuthKey(): AuthKey? {
|
||||||
if (this.auth_key != null) return this.auth_key
|
|
||||||
if (this.file_auth_key != null) {
|
|
||||||
try {
|
try {
|
||||||
return AuthKey(FileUtils.readFileToByteArray(this.file_auth_key))
|
return AuthKey(FileUtils.readFileToByteArray(file_auth_key))
|
||||||
} catch (e: IOException) {
|
} catch (e: FileNotFoundException) {
|
||||||
if (e !is FileNotFoundException) e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun saveDc(dataCenter: DataCenter) {
|
override fun saveDc(dataCenter: DataCenter) {
|
||||||
this.dc = dataCenter
|
FileUtils.write(file_dc, dataCenter.toString())
|
||||||
this._saveDc()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun _saveDc() {
|
|
||||||
if (this.do_save && this.dc != null) {
|
|
||||||
try {
|
|
||||||
FileUtils.write(this.file_dc, this.dc!!.toString())
|
|
||||||
} catch (e: IOException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadDc(): DataCenter? {
|
override fun loadDc(): DataCenter? {
|
||||||
if (this.dc != null) return this.dc
|
|
||||||
if (this.file_dc != null) {
|
|
||||||
try {
|
try {
|
||||||
val infos = FileUtils.readFileToString(this.file_dc).split(":")
|
val infos = FileUtils.readFileToString(this.file_dc).split(":")
|
||||||
return DataCenter(infos[0], Integer.parseInt(infos[1]))
|
return DataCenter(infos[0], Integer.parseInt(infos[1]))
|
||||||
} catch (e: IOException) {
|
} catch (e: FileNotFoundException) {
|
||||||
if (e !is FileNotFoundException) e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun deleteAuthKey() {
|
override fun deleteAuthKey() {
|
||||||
if (this.do_save) {
|
|
||||||
try {
|
try {
|
||||||
FileUtils.forceDelete(this.file_auth_key)
|
FileUtils.forceDelete(file_auth_key)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
logger.warn("Exception in deleteAuthKey(): {}", e)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteDc() {
|
override fun deleteDc() {
|
||||||
if (this.do_save) {
|
|
||||||
try {
|
try {
|
||||||
FileUtils.forceDelete(this.file_dc)
|
FileUtils.forceDelete(file_dc)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
logger.warn("Exception in deleteDc(): {}", e)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,121 +29,127 @@ import java.util.HashMap
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
|
||||||
class CommandLineController {
|
class CommandLineController(val options: CommandLineOptions) {
|
||||||
private val storage: ApiStorage
|
val logger = LoggerFactory.getLogger(CommandLineController::class.java)
|
||||||
var app: TelegramApp
|
|
||||||
|
|
||||||
private fun getLine(): String {
|
|
||||||
if (System.console() != null) {
|
|
||||||
return System.console().readLine("> ")
|
|
||||||
} else {
|
|
||||||
print("> ")
|
|
||||||
return Scanner(System.`in`).nextLine()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPassword(): String {
|
|
||||||
if (System.console() != null) {
|
|
||||||
return String(System.console().readPassword("> "))
|
|
||||||
} else {
|
|
||||||
return getLine()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
val storage: ApiStorage
|
||||||
|
val app: TelegramApp
|
||||||
|
val target_dir: String
|
||||||
|
val file_base: String
|
||||||
|
val phone_number: String
|
||||||
|
val handler: TelegramUpdateHandler
|
||||||
|
var client: TelegramClient
|
||||||
|
val user_manager: UserManager
|
||||||
|
val settings: Settings
|
||||||
|
val database: Database
|
||||||
logger.info("CommandLineController started. App version {}", Config.APP_APPVER)
|
logger.info("CommandLineController started. App version {}", Config.APP_APPVER)
|
||||||
|
|
||||||
this.printHeader()
|
printHeader()
|
||||||
if (CommandLineOptions.cmd_version) {
|
if (options.isSet("version")) {
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
} else if (CommandLineOptions.cmd_help) {
|
} else if (options.isSet("help")) {
|
||||||
this.show_help()
|
show_help()
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
} else if (CommandLineOptions.cmd_license) {
|
} else if (options.isSet("license")) {
|
||||||
CommandLineController.show_license()
|
show_license()
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
this.setupFileBase()
|
|
||||||
if (CommandLineOptions.cmd_list_accounts) {
|
|
||||||
this.list_accounts()
|
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup TelegramApp
|
||||||
logger.debug("Initializing TelegramApp")
|
logger.debug("Initializing TelegramApp")
|
||||||
app = TelegramApp(Config.APP_ID, Config.APP_HASH, Config.APP_MODEL, Config.APP_SYSVER, Config.APP_APPVER, Config.APP_LANG)
|
app = TelegramApp(Config.APP_ID, Config.APP_HASH, Config.APP_MODEL, Config.APP_SYSVER, Config.APP_APPVER, Config.APP_LANG)
|
||||||
|
|
||||||
|
// Setup file_base
|
||||||
|
logger.debug("Target dir from Config: {}", Config.TARGET_DIR.anonymize())
|
||||||
|
target_dir = options.get("target") ?: Config.TARGET_DIR
|
||||||
|
logger.debug("Target dir after options: {}", target_dir)
|
||||||
|
println("Base directory for files: ${target_dir.anonymize()}")
|
||||||
|
|
||||||
|
if (options.isSet("list_accounts")) {
|
||||||
|
Utils.print_accounts(target_dir)
|
||||||
|
System.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.isSet("login")) {
|
||||||
|
cmd_login(app, target_dir, options.get("account"))
|
||||||
|
}
|
||||||
|
|
||||||
logger.trace("Checking accounts")
|
logger.trace("Checking accounts")
|
||||||
val account = this.selectAccount()
|
phone_number = try { selectAccount(target_dir, options.get("account"))
|
||||||
logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
|
} catch(e: AccountNotFoundException) {
|
||||||
|
show_error("The specified account could not be found.")
|
||||||
|
} catch(e: NoAccountsException) {
|
||||||
|
println("No accounts found. Starting login process...")
|
||||||
|
cmd_login(app, target_dir, options.get("account"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Create a new TelegramApp if the user set his/her own TelegramApp credentials
|
||||||
|
|
||||||
|
// At this point we can assume that the selected user account ("phone_number") exists.
|
||||||
|
// So we can create some objects:
|
||||||
|
file_base = build_file_base(target_dir, phone_number)
|
||||||
|
|
||||||
logger.info("Initializing ApiStorage")
|
logger.info("Initializing ApiStorage")
|
||||||
storage = ApiStorage(account)
|
storage = ApiStorage(file_base)
|
||||||
logger.info("Initializing TelegramUpdateHandler")
|
|
||||||
val handler = TelegramUpdateHandler()
|
|
||||||
logger.info("Creating Client")
|
logger.info("Creating Client")
|
||||||
val client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, handler)
|
client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, null)
|
||||||
|
|
||||||
|
// From now on we have a new catch-all-block that will terminate it's TelegramClient when an exception happens.
|
||||||
try {
|
try {
|
||||||
logger.info("Initializing UserManager")
|
logger.info("Initializing UserManager")
|
||||||
UserManager.init(client)
|
user_manager = UserManager(client)
|
||||||
val user = UserManager.getInstance()
|
|
||||||
if (!CommandLineOptions.cmd_login && !user.loggedIn) {
|
// TODO
|
||||||
|
/*if (!options.cmd_login && !user.loggedIn) {
|
||||||
println("Your authorization data is invalid or missing. You will have to login with Telegram again.")
|
println("Your authorization data is invalid or missing. You will have to login with Telegram again.")
|
||||||
CommandLineOptions.cmd_login = true
|
options.cmd_login = true
|
||||||
}
|
}*/
|
||||||
if (account != null && user.loggedIn) {
|
|
||||||
if (account != "+" + user.user!!.getPhone()) {
|
if (phone_number != user_manager.phone) {
|
||||||
logger.error("Account: {}, user.user!!.getPhone(): +{}", account.anonymize(), user.user!!.getPhone().anonymize())
|
logger.error("phone_number: {}, user_manager.phone: {}", phone_number.anonymize(), user_manager.phone.anonymize())
|
||||||
throw RuntimeException("Account / User mismatch")
|
show_error("Account / User mismatch")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the ini file.
|
|
||||||
IniSettings.load()
|
|
||||||
|
|
||||||
logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
|
|
||||||
if (CommandLineOptions.cmd_login) {
|
|
||||||
cmd_login(CommandLineOptions.val_account)
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
// If we reach this point, we can assume that there is an account and a database can be loaded / created.
|
// If we reach this point, we can assume that there is an account and a database can be loaded / created.
|
||||||
Database.init(client)
|
database = Database(file_base, user_manager)
|
||||||
if (CommandLineOptions.cmd_stats) {
|
|
||||||
cmd_stats()
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
if (CommandLineOptions.val_test != null) {
|
|
||||||
if (CommandLineOptions.val_test == 1) {
|
|
||||||
TestFeatures.test1()
|
|
||||||
} else if (CommandLineOptions.val_test == 2) {
|
|
||||||
TestFeatures.test2()
|
|
||||||
} else {
|
|
||||||
System.out.println("Unknown test " + CommandLineOptions.val_test)
|
|
||||||
}
|
|
||||||
System.exit(1)
|
|
||||||
}
|
|
||||||
logger.debug("CommandLineOptions.val_export: {}", CommandLineOptions.val_export)
|
|
||||||
if (CommandLineOptions.val_export != null) {
|
|
||||||
if (CommandLineOptions.val_export!!.toLowerCase().equals("html")) {
|
|
||||||
(HTMLExporter()).export()
|
|
||||||
System.exit(0)
|
|
||||||
} else {
|
|
||||||
show_error("Unknown export format.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (user.loggedIn) {
|
|
||||||
System.out.println("You are logged in as ${user.userString.anonymize()}")
|
|
||||||
} else {
|
|
||||||
println("You are not logged in.")
|
|
||||||
System.exit(1)
|
|
||||||
}
|
|
||||||
logger.info("Initializing Download Manager")
|
|
||||||
val d = DownloadManager(client, CommandLineDownloadProgress())
|
|
||||||
|
|
||||||
if (CommandLineOptions.cmd_list_channels) {
|
// Load the settings and stuff.
|
||||||
|
settings = Settings(file_base, database, options)
|
||||||
|
|
||||||
|
if (options.isSet("stats")) {
|
||||||
|
cmd_stats(file_base, database)
|
||||||
|
System.exit(0)
|
||||||
|
} else if (options.isSet("settings")) {
|
||||||
|
settings.print()
|
||||||
|
System.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
val export = options.get("export")
|
||||||
|
logger.debug("options.val_export: {}", export)
|
||||||
|
if (export != null) {
|
||||||
|
if (export.toLowerCase() == "html") {
|
||||||
|
HTMLExporter(database, user_manager, settings=settings, file_base=file_base).export()
|
||||||
|
System.exit(0)
|
||||||
|
} else {
|
||||||
|
show_error("Unknown export format '${export}'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("You are logged in as ${user_manager.toString().anonymize()}")
|
||||||
|
|
||||||
|
logger.info("Initializing Download Manager")
|
||||||
|
val d = DownloadManager(client, CommandLineDownloadProgress(), database, user_manager, settings, file_base)
|
||||||
|
|
||||||
|
if (options.isSet("list_channels")) {
|
||||||
val chats = d.getChats()
|
val chats = d.getChats()
|
||||||
val print_header = {download: Boolean -> println("%-15s %-40s %s".format("ID", "Title", if (download) "Download" else "")); println("-".repeat(65)) }
|
val print_header = {download: Boolean -> println("%-15s %-40s %s".format("ID", "Title", if (download) "Download" else "")); println("-".repeat(65)) }
|
||||||
val format = {c: DownloadManager.Channel, download: Boolean -> "%-15s %-40s %s".format(c.id.toString().anonymize(), c.title.anonymize(), if (download) (if(c.download) "YES" else "no") else "")}
|
val format = {c: DownloadManager.Channel, download: Boolean -> "%-15s %-40s %s".format(c.id.toString().anonymize(), c.title.anonymize(), if (download) (if(c.download) "YES" else "no") else "")}
|
||||||
var download: Boolean
|
var download: Boolean
|
||||||
|
|
||||||
println("Channels:")
|
println("Channels:")
|
||||||
download = IniSettings.download_channels
|
download = settings.download_channels
|
||||||
if (!download) println("Download of channels is disabled - see download_channels in config.ini")
|
if (!download) println("Download of channels is disabled - see download_channels in config.ini")
|
||||||
print_header(download)
|
print_header(download)
|
||||||
for (c in chats.channels) {
|
for (c in chats.channels) {
|
||||||
@ -151,7 +157,7 @@ class CommandLineController {
|
|||||||
}
|
}
|
||||||
println()
|
println()
|
||||||
println("Supergroups:")
|
println("Supergroups:")
|
||||||
download = IniSettings.download_supergroups
|
download = settings.download_supergroups
|
||||||
if (!download) println("Download of supergroups is disabled - see download_supergroups in config.ini")
|
if (!download) println("Download of supergroups is disabled - see download_supergroups in config.ini")
|
||||||
print_header(download)
|
print_header(download)
|
||||||
for (c in chats.supergroups) {
|
for (c in chats.supergroups) {
|
||||||
@ -160,33 +166,35 @@ class CommandLineController {
|
|||||||
System.exit(0)
|
System.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Calling DownloadManager.downloadMessages with limit {}", CommandLineOptions.val_limit_messages)
|
logger.debug("Calling DownloadManager.downloadMessages with limit {}", options.get("limit_messages"))
|
||||||
d.downloadMessages(CommandLineOptions.val_limit_messages)
|
d.downloadMessages(options.get("limit_messages")?.toInt())
|
||||||
logger.debug("IniSettings.download_media: {}", IniSettings.download_media)
|
logger.debug("IniSettings#download_media: {}", settings.download_media)
|
||||||
if (IniSettings.download_media) {
|
if (settings.download_media) {
|
||||||
logger.debug("Calling DownloadManager.downloadMedia")
|
logger.debug("Calling DownloadManager.downloadMedia")
|
||||||
d.downloadMedia()
|
d.downloadMedia()
|
||||||
} else {
|
} else {
|
||||||
println("Skipping media download because download_media is set to false.")
|
println("Skipping media download because download_media is set to false.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.isSet("daemon")) {
|
||||||
|
logger.info("Initializing TelegramUpdateHandler")
|
||||||
|
handler = TelegramUpdateHandler(user_manager, database, file_base, settings)
|
||||||
|
client.close()
|
||||||
|
logger.info("Creating new client")
|
||||||
|
client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, handler)
|
||||||
|
println("DAEMON mode requested - keeping running.")
|
||||||
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
println("An error occured!")
|
println("An error occurred!")
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
logger.error("Exception caught!", e)
|
logger.error("Exception caught!", e)
|
||||||
// If we encountered an exception, we definitely don't want to start the daemon mode now.
|
|
||||||
CommandLineOptions.cmd_daemon = false
|
|
||||||
} finally {
|
} finally {
|
||||||
if (CommandLineOptions.cmd_daemon) {
|
|
||||||
handler.activate()
|
|
||||||
println("DAEMON mode requested - keeping running.")
|
|
||||||
} else {
|
|
||||||
client.close()
|
client.close()
|
||||||
println()
|
println()
|
||||||
println("----- EXIT -----")
|
println("----- EXIT -----")
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun printHeader() {
|
private fun printHeader() {
|
||||||
System.out.println("Telegram_Backup version " + Config.APP_APPVER + ", Copyright (C) 2016, 2017 Fabian Schlenz")
|
System.out.println("Telegram_Backup version " + Config.APP_APPVER + ", Copyright (C) 2016, 2017 Fabian Schlenz")
|
||||||
@ -196,150 +204,102 @@ class CommandLineController {
|
|||||||
println()
|
println()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupFileBase() {
|
private fun selectAccount(file_base: String, requested_account: String?): String {
|
||||||
logger.debug("Target dir at startup: {}", Config.FILE_BASE.anonymize())
|
var found_account: String?
|
||||||
if (CommandLineOptions.val_target != null) {
|
val accounts = Utils.getAccounts(file_base)
|
||||||
Config.FILE_BASE = CommandLineOptions.val_target!!
|
if (requested_account != null) {
|
||||||
}
|
logger.debug("Account requested: {}", requested_account.anonymize())
|
||||||
logger.debug("Target dir after options: {}", Config.FILE_BASE.anonymize())
|
|
||||||
System.out.println("Base directory for files: " + Config.FILE_BASE.anonymize())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun selectAccount(): String? {
|
|
||||||
var account = "none"
|
|
||||||
val accounts = Utils.getAccounts()
|
|
||||||
if (CommandLineOptions.cmd_login) {
|
|
||||||
logger.debug("Login requested, doing nothing.")
|
|
||||||
// do nothing
|
|
||||||
} else if (CommandLineOptions.val_account != null) {
|
|
||||||
logger.debug("Account requested: {}", CommandLineOptions.val_account!!.anonymize())
|
|
||||||
logger.trace("Checking accounts for match.")
|
logger.trace("Checking accounts for match.")
|
||||||
var found = false
|
found_account = accounts.find{it == requested_account}
|
||||||
for (acc in accounts) {
|
|
||||||
logger.trace("Checking {}", acc.anonymize())
|
|
||||||
if (acc == CommandLineOptions.val_account) {
|
|
||||||
found = true
|
|
||||||
logger.trace("Matches.")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
show_error("Couldn't find account '" + CommandLineOptions.val_account!!.anonymize() + "'. Maybe you want to use '--login' first?")
|
|
||||||
}
|
|
||||||
account = CommandLineOptions.val_account!!
|
|
||||||
} else if (accounts.size == 0) {
|
} else if (accounts.size == 0) {
|
||||||
println("No accounts found. Starting login process...")
|
throw NoAccountsException()
|
||||||
CommandLineOptions.cmd_login = true
|
|
||||||
return null
|
|
||||||
} else if (accounts.size == 1) {
|
} else if (accounts.size == 1) {
|
||||||
account = accounts.firstElement()
|
found_account = accounts.firstElement()
|
||||||
System.out.println("Using only available account: " + account.anonymize())
|
println("Using only available account: " + found_account.anonymize())
|
||||||
} else {
|
} else {
|
||||||
show_error(("You didn't specify which account to use.\n" +
|
show_error(("You have more than one account but didn't specify which one to use.\n" +
|
||||||
"Use '--account <x>' to use account <x>.\n" +
|
"Use '--account <x>' to use account <x>.\n" +
|
||||||
"Use '--list-accounts' to see all available accounts."))
|
"Use '--list-accounts' to see all available accounts."))
|
||||||
System.exit(1)
|
|
||||||
}
|
|
||||||
logger.debug("accounts.size: {}", accounts.size)
|
|
||||||
logger.debug("account: {}", account.anonymize())
|
|
||||||
return account
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cmd_stats() {
|
if (found_account == null) {
|
||||||
|
throw AccountNotFoundException()
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("accounts.size: {}", accounts.size)
|
||||||
|
logger.debug("account: {}", found_account.anonymize())
|
||||||
|
return found_account
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cmd_stats(file_base: String, db: Database) {
|
||||||
println()
|
println()
|
||||||
println("Stats:")
|
println("Stats:")
|
||||||
val format = "%40s: %d%n"
|
val format = "%40s: %d%n"
|
||||||
System.out.format(format, "Number of accounts", Utils.getAccounts().size)
|
System.out.format(format, "Number of accounts", Utils.getAccounts(file_base).size)
|
||||||
System.out.format(format, "Number of messages", Database.getInstance().getMessageCount())
|
System.out.format(format, "Number of messages", db.getMessageCount())
|
||||||
System.out.format(format, "Number of chats", Database.getInstance().getChatCount())
|
System.out.format(format, "Number of chats", db.getChatCount())
|
||||||
System.out.format(format, "Number of users", Database.getInstance().getUserCount())
|
System.out.format(format, "Number of users", db.getUserCount())
|
||||||
System.out.format(format, "Top message ID", Database.getInstance().getTopMessageID())
|
System.out.format(format, "Top message ID", db.getTopMessageID())
|
||||||
println()
|
println()
|
||||||
println("Media Types:")
|
println("Media Types:")
|
||||||
for ((key, value) in Database.getInstance().getMessageMediaTypesWithCount()) {
|
for ((key, value) in db.getMessageMediaTypesWithCount()) {
|
||||||
System.out.format(format, key, value)
|
System.out.format(format, key, value)
|
||||||
}
|
}
|
||||||
println()
|
println()
|
||||||
println("Api layers of messages:")
|
println("Api layers of messages:")
|
||||||
for ((key, value) in Database.getInstance().getMessageApiLayerWithCount()) {
|
for ((key, value) in db.getMessageApiLayerWithCount()) {
|
||||||
System.out.format(format, key, value)
|
System.out.format(format, key, value)
|
||||||
}
|
}
|
||||||
println()
|
println()
|
||||||
println("Message source types:")
|
println("Message source types:")
|
||||||
for ((key, value) in Database.getInstance().getMessageSourceTypeWithCount()) {
|
for ((key, value) in db.getMessageSourceTypeWithCount()) {
|
||||||
System.out.format(format, key, value)
|
System.out.format(format, key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
private fun cmd_login(app: TelegramApp, target_dir: String, phoneToUse: String?): Nothing {
|
||||||
private fun cmd_login(phoneToUse: String?) {
|
LoginManager(app, target_dir, phoneToUse).run()
|
||||||
val user = UserManager.getInstance()
|
System.exit(0)
|
||||||
val phone: String
|
throw RuntimeException("Code never reaches this. This exists just to keep the Kotlin compiler happy.")
|
||||||
if (phoneToUse == null) {
|
|
||||||
println("Please enter your phone number in international format.")
|
|
||||||
println("Example: +4917077651234")
|
|
||||||
phone = getLine()
|
|
||||||
} else {
|
|
||||||
phone = phoneToUse
|
|
||||||
}
|
|
||||||
user.sendCodeToPhoneNumber(phone)
|
|
||||||
println("Telegram sent you a code. Please enter it here.")
|
|
||||||
val code = getLine()
|
|
||||||
user.verifyCode(code)
|
|
||||||
if (user.isPasswordNeeded) {
|
|
||||||
println("We also need your account password. Please enter it now. It should not be printed, so it's okay if you see nothing while typing it.")
|
|
||||||
val pw = getPassword()
|
|
||||||
user.verifyPassword(pw)
|
|
||||||
}
|
|
||||||
storage.setPrefix("+" + user.user!!.getPhone())
|
|
||||||
System.out.println("Everything seems fine. Please run this tool again with '--account +" + user.user!!.getPhone().anonymize() + " to use this account.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun show_help() {
|
private fun show_help() {
|
||||||
println("Valid options are:")
|
println("Valid options are:")
|
||||||
println(" -h, --help Shows this help.")
|
println(" --help Shows this help.")
|
||||||
println(" -a, --account <x> Use account <x>.")
|
println(" --account <x> Use account <x>.")
|
||||||
println(" -l, --login Login to an existing telegram account.")
|
println(" --login Login to an existing telegram account.")
|
||||||
println(" --debug Shows some debug information.")
|
println(" --debug Shows some debug information.")
|
||||||
println(" --trace Shows lots of debug information. Overrides --debug.")
|
println(" --trace Shows lots of debug information. Overrides --debug.")
|
||||||
println(" --trace-telegram Shows lots of debug messages from the library used to access Telegram.")
|
println(" --trace-telegram Shows lots of debug messages from the library used to access Telegram.")
|
||||||
println(" -A, --list-accounts List all existing accounts ")
|
println(" --list-accounts List all existing accounts ")
|
||||||
println(" --limit-messages <x> Downloads at most the most recent <x> messages.")
|
println(" --limit-messages <x> Downloads at most the most recent <x> messages.")
|
||||||
println(" -t, --target <x> Target directory for the files.")
|
println(" --target <x> Target directory for the files.")
|
||||||
println(" -e, --export <format> Export the database. Valid formats are:")
|
println(" --export <format> Export the database. Valid formats are:")
|
||||||
println(" html - Creates HTML files.")
|
println(" html - Creates HTML files.")
|
||||||
println(" --license Displays the license of this program.")
|
println(" --license Displays the license of this program.")
|
||||||
println(" -d, --daemon Keep running after the backup and automatically save new messages.")
|
println(" --daemon Keep running after the backup and automatically save new messages.")
|
||||||
println(" --anonymize (Try to) Remove all sensitive information from output. Useful for requesting support.")
|
println(" --anonymize (Try to) Remove all sensitive information from output. Useful for requesting support.")
|
||||||
println(" --stats Print some usage statistics.")
|
println(" --stats Print some usage statistics.")
|
||||||
println(" --list-channels Lists all channels together with their ID")
|
println(" --list-channels Lists all channels together with their ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun list_accounts() {
|
|
||||||
println("List of available accounts:")
|
|
||||||
val accounts = Utils.getAccounts()
|
|
||||||
if (accounts.size > 0) {
|
|
||||||
for (str in accounts) {
|
|
||||||
System.out.println(" " + str.anonymize())
|
|
||||||
}
|
|
||||||
println("Use '--account <x>' to use one of those accounts.")
|
|
||||||
} else {
|
|
||||||
println("NO ACCOUNTS FOUND")
|
|
||||||
println("Use '--login' to login to a telegram account.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(CommandLineController::class.java)
|
private val logger = LoggerFactory.getLogger(CommandLineController::class.java)
|
||||||
|
|
||||||
public fun show_error(error: String) {
|
public fun show_error(error: String): Nothing {
|
||||||
logger.error(error)
|
logger.error(error)
|
||||||
println("ERROR: " + error)
|
println("ERROR: " + error)
|
||||||
System.exit(1)
|
System.exit(1)
|
||||||
|
throw RuntimeException("Code never reaches this. This exists just to keep the Kotlin compiler happy.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun show_license() {
|
fun show_license() {
|
||||||
println("TODO: Print the GPL.")
|
println("TODO: Print the GPL.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun build_file_base(target_dir: String, account_to_use: String) = target_dir + File.separatorChar + account_to_use + File.separatorChar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AccountNotFoundException() : Exception("Account not found") {}
|
||||||
|
class NoAccountsException() : Exception("No accounts found") {}
|
||||||
}
|
}
|
||||||
|
@ -15,83 +15,52 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
package de.fabianonline.telegram_backup
|
package de.fabianonline.telegram_backup
|
||||||
|
|
||||||
internal object CommandLineOptions {
|
class CommandLineOptions(args: Array<String>) {
|
||||||
public var cmd_console = false
|
private val values = mutableMapOf<String, String>()
|
||||||
public var cmd_help = false
|
var last_key: String? = null
|
||||||
public var cmd_login = false
|
val substitutions = mapOf("-t" to "--target")
|
||||||
var cmd_debug = false
|
|
||||||
var cmd_trace = false
|
init {
|
||||||
var cmd_trace_telegram = false
|
val list = args.toMutableList()
|
||||||
var cmd_list_accounts = false
|
|
||||||
var cmd_version = false
|
while (list.isNotEmpty()) {
|
||||||
var cmd_license = false
|
|
||||||
var cmd_daemon = false
|
var current_arg = list.removeAt(0)
|
||||||
var cmd_anonymize = false
|
if (!current_arg.startsWith("-")) throw RuntimeException("Unexpected unnamed parameter ${current_arg}")
|
||||||
var cmd_stats = false
|
|
||||||
var cmd_list_channels = false
|
var next_arg: String? = null
|
||||||
var val_account: String? = null
|
|
||||||
var val_limit_messages: Int? = null
|
if (current_arg.contains("=")) {
|
||||||
var val_target: String? = null
|
val parts = current_arg.split("=", limit=2)
|
||||||
var val_export: String? = null
|
current_arg = parts[0]
|
||||||
var val_test: Int? = null
|
next_arg = parts[1]
|
||||||
@JvmStatic
|
} else if (list.isNotEmpty() && !list[0].startsWith("--")) {
|
||||||
fun parseOptions(args: Array<String>) {
|
next_arg = list.removeAt(0)
|
||||||
var last_cmd: String? = null
|
|
||||||
loop@ for (arg in args) {
|
|
||||||
if (last_cmd != null) {
|
|
||||||
when (last_cmd) {
|
|
||||||
"--account" -> val_account = arg
|
|
||||||
"--limit-messages" -> val_limit_messages = Integer.parseInt(arg)
|
|
||||||
"--target" -> val_target = arg
|
|
||||||
"--export" -> val_export = arg
|
|
||||||
"--test" -> val_test = Integer.parseInt(arg)
|
|
||||||
}
|
}
|
||||||
last_cmd = null
|
|
||||||
continue
|
if (!current_arg.startsWith("--") && current_arg.startsWith("-")) {
|
||||||
|
val replacement = substitutions.get(current_arg)
|
||||||
|
if (replacement == null) throw RuntimeException("Unknown short parameter ${current_arg}")
|
||||||
|
current_arg = replacement
|
||||||
}
|
}
|
||||||
when (arg) {
|
|
||||||
"-a", "--account" -> {
|
current_arg = current_arg.substring(2)
|
||||||
last_cmd = "--account"
|
|
||||||
continue@loop
|
if (next_arg == null) {
|
||||||
|
// current_arg seems to be a boolean value
|
||||||
|
values.put(current_arg, "true")
|
||||||
|
if (current_arg.startsWith("no-")) {
|
||||||
|
current_arg = current_arg.substring(3)
|
||||||
|
values.put(current_arg, "false")
|
||||||
}
|
}
|
||||||
"-h", "--help" -> cmd_help = true
|
} else {
|
||||||
"-l", "--login" -> cmd_login = true
|
// current_arg has the value next_arg
|
||||||
"--debug" -> cmd_debug = true
|
values.put(current_arg, next_arg)
|
||||||
"--trace" -> cmd_trace = true
|
|
||||||
"--trace-telegram" -> cmd_trace_telegram = true
|
|
||||||
"-A", "--list-accounts" -> cmd_list_accounts = true
|
|
||||||
"--limit-messages" -> {
|
|
||||||
last_cmd = arg
|
|
||||||
continue@loop
|
|
||||||
}
|
|
||||||
"--console" -> cmd_console = true
|
|
||||||
"-t", "--target" -> {
|
|
||||||
last_cmd = "--target"
|
|
||||||
continue@loop
|
|
||||||
}
|
|
||||||
"-V", "--version" -> cmd_version = true
|
|
||||||
"-e", "--export" -> {
|
|
||||||
last_cmd = "--export"
|
|
||||||
continue@loop
|
|
||||||
}
|
|
||||||
"--pagination" -> {
|
|
||||||
last_cmd = "--pagination"
|
|
||||||
continue@loop
|
|
||||||
}
|
|
||||||
"--license" -> cmd_license = true
|
|
||||||
"-d", "--daemon" -> cmd_daemon = true
|
|
||||||
"--test" -> {
|
|
||||||
last_cmd = "--test"
|
|
||||||
continue@loop
|
|
||||||
}
|
|
||||||
"--anonymize" -> cmd_anonymize = true
|
|
||||||
"--stats" -> cmd_stats = true
|
|
||||||
"--list-channels" -> cmd_list_channels = true
|
|
||||||
else -> throw RuntimeException("Unknown command " + arg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (last_cmd != null) {
|
println(values)
|
||||||
CommandLineController.show_error("Command $last_cmd had no parameter set.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator fun get(name: String): String? = values[name]
|
||||||
|
fun isSet(name: String): Boolean = values[name]=="true"
|
||||||
}
|
}
|
||||||
|
@ -29,24 +29,35 @@ import ch.qos.logback.core.ConsoleAppender
|
|||||||
import ch.qos.logback.classic.Level
|
import ch.qos.logback.classic.Level
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
CommandLineOptions.parseOptions(args)
|
val clr = CommandLineRunner(args)
|
||||||
|
|
||||||
CommandLineRunner.setupLogging()
|
clr.setupLogging()
|
||||||
CommandLineRunner.checkVersion()
|
clr.checkVersion()
|
||||||
|
clr.run()
|
||||||
|
|
||||||
|
|
||||||
if (true || CommandLineOptions.cmd_console) {
|
|
||||||
// Always use the console for now.
|
|
||||||
CommandLineController()
|
|
||||||
} else {
|
|
||||||
GUIController()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object CommandLineRunner {
|
class CommandLineRunner(args: Array<String>) {
|
||||||
fun setupLogging() {
|
|
||||||
val logger = LoggerFactory.getLogger(CommandLineRunner::class.java) as Logger
|
val logger = LoggerFactory.getLogger(CommandLineRunner::class.java) as Logger
|
||||||
|
val options = CommandLineOptions(args)
|
||||||
|
|
||||||
|
fun run() {
|
||||||
|
// Always use the console for now.
|
||||||
|
try {
|
||||||
|
CommandLineController(options)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
println("An error occured!")
|
||||||
|
e.printStackTrace()
|
||||||
|
logger.error("Exception caught!", e)
|
||||||
|
System.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setupLogging() {
|
||||||
|
if (options.isSet("anonymize")) {
|
||||||
|
Utils.anonymize = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
logger.trace("Setting up Loggers...")
|
logger.trace("Setting up Loggers...")
|
||||||
val rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME) as Logger
|
val rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME) as Logger
|
||||||
val rootContext = rootLogger.getLoggerContext()
|
val rootContext = rootLogger.getLoggerContext()
|
||||||
@ -65,13 +76,13 @@ object CommandLineRunner {
|
|||||||
rootLogger.addAppender(appender)
|
rootLogger.addAppender(appender)
|
||||||
rootLogger.setLevel(Level.OFF)
|
rootLogger.setLevel(Level.OFF)
|
||||||
|
|
||||||
if (CommandLineOptions.cmd_trace) {
|
if (options.isSet("trace")) {
|
||||||
(LoggerFactory.getLogger("de.fabianonline.telegram_backup") as Logger).setLevel(Level.TRACE)
|
(LoggerFactory.getLogger("de.fabianonline.telegram_backup") as Logger).setLevel(Level.TRACE)
|
||||||
} else if (CommandLineOptions.cmd_debug) {
|
} else if (options.isSet("debug")) {
|
||||||
(LoggerFactory.getLogger("de.fabianonline.telegram_backup") as Logger).setLevel(Level.DEBUG)
|
(LoggerFactory.getLogger("de.fabianonline.telegram_backup") as Logger).setLevel(Level.DEBUG)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CommandLineOptions.cmd_trace_telegram) {
|
if (options.isSet("trace_telegram")) {
|
||||||
(LoggerFactory.getLogger("com.github.badoualy") as Logger).setLevel(Level.TRACE)
|
(LoggerFactory.getLogger("com.github.badoualy") as Logger).setLevel(Level.TRACE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ object Config {
|
|||||||
val APP_APPVER: String
|
val APP_APPVER: String
|
||||||
val APP_LANG = "en"
|
val APP_LANG = "en"
|
||||||
|
|
||||||
var FILE_BASE = System.getProperty("user.home") + File.separatorChar + ".telegram_backup"
|
var TARGET_DIR = System.getProperty("user.home") + File.separatorChar + ".telegram_backup"
|
||||||
val FILE_NAME_AUTH_KEY = "auth.dat"
|
val FILE_NAME_AUTH_KEY = "auth.dat"
|
||||||
val FILE_NAME_DC = "dc.dat"
|
val FILE_NAME_DC = "dc.dat"
|
||||||
val FILE_NAME_DB = "database.sqlite"
|
val FILE_NAME_DB = "database.sqlite"
|
||||||
|
@ -21,7 +21,6 @@ import com.github.badoualy.telegram.tl.core.TLVector
|
|||||||
import com.github.badoualy.telegram.api.TelegramClient
|
import com.github.badoualy.telegram.api.TelegramClient
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.sql.DriverManager
|
import java.sql.DriverManager
|
||||||
import java.sql.Statement
|
import java.sql.Statement
|
||||||
@ -47,34 +46,45 @@ import java.text.SimpleDateFormat
|
|||||||
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
|
||||||
|
|
||||||
class Database private constructor(var client: TelegramClient) {
|
class Database constructor(val file_base: String, val user_manager: UserManager) {
|
||||||
private var conn: Connection? = null
|
val conn: Connection
|
||||||
private var stmt: Statement? = null
|
val stmt: Statement
|
||||||
var user_manager: UserManager
|
val logger = LoggerFactory.getLogger(Database::class.java)
|
||||||
|
|
||||||
fun getTopMessageID(): Int {
|
init {
|
||||||
|
println("Opening database...")
|
||||||
try {
|
try {
|
||||||
val rs = stmt!!.executeQuery("SELECT MAX(message_id) FROM messages WHERE source_type IN ('group', 'dialog')")
|
Class.forName("org.sqlite.JDBC")
|
||||||
rs.next()
|
} catch (e: ClassNotFoundException) {
|
||||||
val result = rs.getInt(1)
|
throw RuntimeException("Could not load jdbc-sqlite class.")
|
||||||
rs.close()
|
}
|
||||||
return result
|
|
||||||
|
val path = "jdbc:sqlite:${file_base}${Config.FILE_NAME_DB}"
|
||||||
|
|
||||||
|
try {
|
||||||
|
conn = DriverManager.getConnection(path)!!
|
||||||
|
stmt = conn.createStatement()
|
||||||
} catch (e: SQLException) {
|
} catch (e: SQLException) {
|
||||||
return 0
|
throw RuntimeException("Could not connect to SQLITE database.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run updates
|
||||||
|
val updates = DatabaseUpdates(conn, this)
|
||||||
|
updates.doUpdates()
|
||||||
|
|
||||||
|
println("Database is ready.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getTopMessageID(): Int = queryInt("SELECT MAX(message_id) FROM messages WHERE source_type IN ('group', 'dialog')")
|
||||||
fun getMessageCount(): Int = queryInt("SELECT COUNT(*) FROM messages")
|
fun getMessageCount(): Int = queryInt("SELECT COUNT(*) FROM messages")
|
||||||
fun getChatCount(): Int = queryInt("SELECT COUNT(*) FROM chats")
|
fun getChatCount(): Int = queryInt("SELECT COUNT(*) FROM chats")
|
||||||
fun getUserCount(): Int = queryInt("SELECT COUNT(*) FROM users")
|
fun getUserCount(): Int = queryInt("SELECT COUNT(*) FROM users")
|
||||||
|
|
||||||
val missingIDs: LinkedList<Int>
|
fun getMissingIDs(): LinkedList<Int> {
|
||||||
get() {
|
|
||||||
try {
|
try {
|
||||||
val missing = LinkedList<Int>()
|
val missing = LinkedList<Int>()
|
||||||
val max = getTopMessageID()
|
val max = getTopMessageID()
|
||||||
val rs = stmt!!.executeQuery("SELECT message_id FROM messages WHERE source_type IN ('group', 'dialog') ORDER BY id")
|
val rs = stmt.executeQuery("SELECT message_id FROM messages WHERE source_type IN ('group', 'dialog') ORDER BY id")
|
||||||
rs.next()
|
rs.next()
|
||||||
var id = rs.getInt(1)
|
var id = rs.getInt(1)
|
||||||
for (i in 1..max) {
|
for (i in 1..max) {
|
||||||
@ -95,7 +105,6 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
throw RuntimeException("Could not get list of ids.")
|
throw RuntimeException("Could not get list of ids.")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessagesWithMediaCount() = queryInt("SELECT COUNT(*) FROM messages WHERE has_media=1")
|
fun getMessagesWithMediaCount() = queryInt("SELECT COUNT(*) FROM messages WHERE has_media=1")
|
||||||
@ -105,7 +114,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
val list = LinkedList<TLMessage?>()
|
val list = LinkedList<TLMessage?>()
|
||||||
var query = "SELECT data FROM messages WHERE has_media=1 ORDER BY id"
|
var query = "SELECT data FROM messages WHERE has_media=1 ORDER BY id"
|
||||||
if (limit > 0) query += " LIMIT ${limit} OFFSET ${offset}"
|
if (limit > 0) query += " LIMIT ${limit} OFFSET ${offset}"
|
||||||
val rs = stmt!!.executeQuery(query)
|
val rs = stmt.executeQuery(query)
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
list.add(bytesToTLMessage(rs.getBytes(1)))
|
list.add(bytesToTLMessage(rs.getBytes(1)))
|
||||||
}
|
}
|
||||||
@ -118,7 +127,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessagesFromUserCount() = queryInt("SELECT COUNT(*) FROM messages WHERE sender_id=" + user_manager.user!!.getId())
|
fun getMessagesFromUserCount() = queryInt("SELECT COUNT(*) FROM messages WHERE sender_id=" + user_manager.id)
|
||||||
|
|
||||||
fun getMessageTypesWithCount(): HashMap<String, Int> = getMessageTypesWithCount(GlobalChat())
|
fun getMessageTypesWithCount(): HashMap<String, Int> = getMessageTypesWithCount(GlobalChat())
|
||||||
|
|
||||||
@ -127,7 +136,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
fun getMessageSourceTypeWithCount(): HashMap<String, Int> {
|
fun getMessageSourceTypeWithCount(): HashMap<String, Int> {
|
||||||
val map = HashMap<String, Int>()
|
val map = HashMap<String, Int>()
|
||||||
try {
|
try {
|
||||||
val rs = stmt!!.executeQuery("SELECT COUNT(id), source_type FROM messages GROUP BY source_type ORDER BY source_type")
|
val rs = stmt.executeQuery("SELECT COUNT(id), source_type FROM messages GROUP BY source_type ORDER BY source_type")
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
val source_type = rs.getString(2) ?: "null"
|
val source_type = rs.getString(2) ?: "null"
|
||||||
map.put("count.messages.source_type.${source_type}", rs.getInt(1))
|
map.put("count.messages.source_type.${source_type}", rs.getInt(1))
|
||||||
@ -142,7 +151,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
fun getMessageApiLayerWithCount(): HashMap<String, Int> {
|
fun getMessageApiLayerWithCount(): HashMap<String, Int> {
|
||||||
val map = HashMap<String, Int>()
|
val map = HashMap<String, Int>()
|
||||||
try {
|
try {
|
||||||
val rs = stmt!!.executeQuery("SELECT COUNT(id), api_layer FROM messages GROUP BY api_layer ORDER BY api_layer")
|
val rs = stmt.executeQuery("SELECT COUNT(id), api_layer FROM messages GROUP BY api_layer ORDER BY api_layer")
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
var layer = rs.getInt(2)
|
var layer = rs.getInt(2)
|
||||||
map.put("count.messages.api_layer.$layer", rs.getInt(1))
|
map.put("count.messages.api_layer.$layer", rs.getInt(1))
|
||||||
@ -158,25 +167,12 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
|
|
||||||
fun getMessageTimesMatrix(): Array<IntArray> = getMessageTimesMatrix(GlobalChat())
|
fun getMessageTimesMatrix(): Array<IntArray> = getMessageTimesMatrix(GlobalChat())
|
||||||
|
|
||||||
fun getEncoding(): String {
|
fun getEncoding(): String = queryString("PRAGMA encoding")
|
||||||
try {
|
|
||||||
val rs = stmt!!.executeQuery("PRAGMA encoding")
|
|
||||||
rs.next()
|
|
||||||
val result = rs.getString(1)
|
|
||||||
rs.close()
|
|
||||||
return result
|
|
||||||
} catch (e: SQLException) {
|
|
||||||
logger.debug("SQLException: {}", e)
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun getListOfChatsForExport(): LinkedList<Chat> {
|
fun getListOfChatsForExport(): LinkedList<Chat> {
|
||||||
val list = LinkedList<Chat>()
|
val list = LinkedList<Chat>()
|
||||||
try {
|
|
||||||
val rs = stmt!!.executeQuery("SELECT chats.id, chats.name, COUNT(messages.id) as c " +
|
val rs = stmt.executeQuery("SELECT chats.id, chats.name, COUNT(messages.id) as c " +
|
||||||
"FROM chats, messages WHERE messages.source_type IN('group', 'supergroup', 'channel') AND messages.source_id=chats.id " +
|
"FROM chats, messages WHERE messages.source_type IN('group', 'supergroup', 'channel') AND messages.source_id=chats.id " +
|
||||||
"GROUP BY chats.id ORDER BY c DESC")
|
"GROUP BY chats.id ORDER BY c DESC")
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
@ -184,18 +180,12 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
}
|
}
|
||||||
rs.close()
|
rs.close()
|
||||||
return list
|
return list
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw RuntimeException("Exception above!")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getListOfDialogsForExport(): LinkedList<Dialog> {
|
fun getListOfDialogsForExport(): LinkedList<Dialog> {
|
||||||
val list = LinkedList<Dialog>()
|
val list = LinkedList<Dialog>()
|
||||||
try {
|
val rs = stmt.executeQuery(
|
||||||
val rs = stmt!!.executeQuery(
|
|
||||||
"SELECT users.id, first_name, last_name, username, COUNT(messages.id) as c " +
|
"SELECT users.id, first_name, last_name, username, COUNT(messages.id) as c " +
|
||||||
"FROM users, messages WHERE messages.source_type='dialog' AND messages.source_id=users.id " +
|
"FROM users, messages WHERE messages.source_type='dialog' AND messages.source_id=users.id " +
|
||||||
"GROUP BY users.id ORDER BY c DESC")
|
"GROUP BY users.id ORDER BY c DESC")
|
||||||
@ -204,44 +194,14 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
}
|
}
|
||||||
rs.close()
|
rs.close()
|
||||||
return list
|
return list
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw RuntimeException("Exception above!")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
this.user_manager = UserManager.getInstance()
|
|
||||||
System.out.println("Opening database...")
|
|
||||||
try {
|
|
||||||
Class.forName("org.sqlite.JDBC")
|
|
||||||
} catch (e: ClassNotFoundException) {
|
|
||||||
CommandLineController.show_error("Could not load jdbc-sqlite class.")
|
|
||||||
}
|
|
||||||
|
|
||||||
val path = "jdbc:sqlite:${user_manager.fileBase}${Config.FILE_NAME_DB}"
|
|
||||||
|
|
||||||
try {
|
|
||||||
conn = DriverManager.getConnection(path)
|
|
||||||
stmt = conn!!.createStatement()
|
|
||||||
} catch (e: SQLException) {
|
|
||||||
CommandLineController.show_error("Could not connect to SQLITE database.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run updates
|
|
||||||
val updates = DatabaseUpdates(conn!!, this)
|
|
||||||
updates.doUpdates()
|
|
||||||
|
|
||||||
System.out.println("Database is ready.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun backupDatabase(currentVersion: Int) {
|
fun backupDatabase(currentVersion: Int) {
|
||||||
val filename = String.format(Config.FILE_NAME_DB_BACKUP, currentVersion)
|
val filename = String.format(Config.FILE_NAME_DB_BACKUP, currentVersion)
|
||||||
System.out.println(" Creating a backup of your database as " + filename)
|
System.out.println(" Creating a backup of your database as " + filename)
|
||||||
try {
|
try {
|
||||||
val src = user_manager.fileBase + Config.FILE_NAME_DB
|
val src = file_base + Config.FILE_NAME_DB
|
||||||
val dst = user_manager.fileBase + filename
|
val dst = file_base + filename
|
||||||
logger.debug("Copying {} to {}", src.anonymize(), dst.anonymize())
|
logger.debug("Copying {} to {}", src.anonymize(), dst.anonymize())
|
||||||
Files.copy(
|
Files.copy(
|
||||||
File(src).toPath(),
|
File(src).toPath(),
|
||||||
@ -258,8 +218,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
fun getTopMessageIDForChannel(id: Int): Int = queryInt("SELECT MAX(message_id) FROM messages WHERE source_id=$id AND source_type IN('channel', 'supergroup')")
|
fun getTopMessageIDForChannel(id: Int): Int = queryInt("SELECT MAX(message_id) FROM messages WHERE source_id=$id AND source_type IN('channel', 'supergroup')")
|
||||||
|
|
||||||
fun logRun(start_id: Int, end_id: Int, count: Int) {
|
fun logRun(start_id: Int, end_id: Int, count: Int) {
|
||||||
try {
|
val ps = conn.prepareStatement("INSERT INTO runs " +
|
||||||
val ps = conn!!.prepareStatement("INSERT INTO runs " +
|
|
||||||
"(time, start_id, end_id, count_missing) " +
|
"(time, start_id, end_id, count_missing) " +
|
||||||
"VALUES " +
|
"VALUES " +
|
||||||
"(DateTime('now'), ?, ?, ? )")
|
"(DateTime('now'), ?, ?, ? )")
|
||||||
@ -268,27 +227,36 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
ps.setInt(3, count)
|
ps.setInt(3, count)
|
||||||
ps.execute()
|
ps.execute()
|
||||||
ps.close()
|
ps.close()
|
||||||
} catch (e: SQLException) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun queryInt(query: String): Int {
|
fun queryInt(query: String): Int {
|
||||||
try {
|
val rs = stmt.executeQuery(query)
|
||||||
val rs = stmt!!.executeQuery(query)
|
|
||||||
rs.next()
|
rs.next()
|
||||||
val result = rs.getInt(1)
|
val result = rs.getInt(1)
|
||||||
rs.close()
|
rs.close()
|
||||||
return result
|
return result
|
||||||
} catch (e: SQLException) {
|
|
||||||
throw RuntimeException("Could not get count of messages.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun queryString(query: String): String {
|
||||||
|
val rs = stmt.executeQuery(query)
|
||||||
|
rs.next()
|
||||||
|
val result = rs.getString(1)
|
||||||
|
rs.close()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queryStringMap(query: String): Map<String, String> {
|
||||||
|
val map = mutableMapOf<String, String>()
|
||||||
|
val rs = stmt.executeQuery(query)
|
||||||
|
while(rs.next()) {
|
||||||
|
map.put(rs.getString(1), rs.getString(2))
|
||||||
|
}
|
||||||
|
rs.close()
|
||||||
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun saveMessages(all: TLVector<TLAbsMessage>, api_layer: Int, source_type: MessageSource = MessageSource.NORMAL) {
|
fun saveMessages(all: TLVector<TLAbsMessage>, api_layer: Int, source_type: MessageSource = MessageSource.NORMAL, settings: Settings?) {
|
||||||
try {
|
|
||||||
//"(id, dialog_id, from_id, from_type, text, time, has_media, data, sticker, type) " +
|
//"(id, dialog_id, from_id, from_type, text, time, has_media, data, sticker, type) " +
|
||||||
//"VALUES " +
|
//"VALUES " +
|
||||||
//"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
//"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
@ -296,8 +264,8 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
"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
|
||||||
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)
|
||||||
|
|
||||||
for (msg in all) {
|
for (msg in all) {
|
||||||
if (msg is TLMessage) {
|
if (msg is TLMessage) {
|
||||||
@ -309,7 +277,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
ps.setInt(4, peer.getChatId())
|
ps.setInt(4, peer.getChatId())
|
||||||
} else if (peer is TLPeerUser) {
|
} else if (peer is TLPeerUser) {
|
||||||
var id = peer.getUserId()
|
var id = peer.getUserId()
|
||||||
if (id == this.user_manager.user!!.getId()) {
|
if (id == user_manager.id) {
|
||||||
id = msg.getFromId()
|
id = msg.getFromId()
|
||||||
}
|
}
|
||||||
ps.setString(3, "dialog")
|
ps.setString(3, "dialog")
|
||||||
@ -350,7 +318,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
}
|
}
|
||||||
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, client)
|
val f = FileManagerFactory.getFileManager(msg, user_manager, 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)
|
||||||
@ -377,7 +345,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
ps.setInt(4, peer.getChatId())
|
ps.setInt(4, peer.getChatId())
|
||||||
} else if (peer is TLPeerUser) {
|
} else if (peer is TLPeerUser) {
|
||||||
var id = peer.getUserId()
|
var id = peer.getUserId()
|
||||||
if (id == this.user_manager.user!!.getId()) {
|
if (id == user_manager.id) {
|
||||||
id = msg.getFromId()
|
id = msg.getFromId()
|
||||||
}
|
}
|
||||||
ps.setString(3, "dialog")
|
ps.setString(3, "dialog")
|
||||||
@ -425,32 +393,26 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
throw RuntimeException("Unexpected Message type: " + msg.javaClass)
|
throw RuntimeException("Unexpected Message type: " + msg.javaClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn!!.setAutoCommit(false)
|
conn.setAutoCommit(false)
|
||||||
ps.executeBatch()
|
ps.executeBatch()
|
||||||
ps.clearBatch()
|
ps.clearBatch()
|
||||||
ps_insert_or_ignore.executeBatch()
|
ps_insert_or_ignore.executeBatch()
|
||||||
ps_insert_or_ignore.clearBatch()
|
ps_insert_or_ignore.clearBatch()
|
||||||
conn!!.commit()
|
conn.commit()
|
||||||
conn!!.setAutoCommit(true)
|
conn.setAutoCommit(true)
|
||||||
|
|
||||||
ps.close()
|
ps.close()
|
||||||
ps_insert_or_ignore.close()
|
ps_insert_or_ignore.close()
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw RuntimeException("Exception shown above happened.")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun saveChats(all: TLVector<TLAbsChat>) {
|
fun saveChats(all: TLVector<TLAbsChat>) {
|
||||||
try {
|
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) " +
|
||||||
"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) " +
|
||||||
"VALUES " +
|
"VALUES " +
|
||||||
@ -483,32 +445,26 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
throw RuntimeException("Unexpected " + abs.javaClass)
|
throw RuntimeException("Unexpected " + abs.javaClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn!!.setAutoCommit(false)
|
conn.setAutoCommit(false)
|
||||||
ps_insert_or_ignore.executeBatch()
|
ps_insert_or_ignore.executeBatch()
|
||||||
ps_insert_or_ignore.clearBatch()
|
ps_insert_or_ignore.clearBatch()
|
||||||
ps_insert_or_replace.executeBatch()
|
ps_insert_or_replace.executeBatch()
|
||||||
ps_insert_or_replace.clearBatch()
|
ps_insert_or_replace.clearBatch()
|
||||||
conn!!.commit()
|
conn.commit()
|
||||||
conn!!.setAutoCommit(true)
|
conn.setAutoCommit(true)
|
||||||
|
|
||||||
ps_insert_or_ignore.close()
|
ps_insert_or_ignore.close()
|
||||||
ps_insert_or_replace.close()
|
ps_insert_or_replace.close()
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw RuntimeException("Exception shown above happened.")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun saveUsers(all: TLVector<TLAbsUser>) {
|
fun saveUsers(all: TLVector<TLAbsUser>) {
|
||||||
try {
|
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) " +
|
||||||
"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) " +
|
||||||
"VALUES " +
|
"VALUES " +
|
||||||
@ -535,30 +491,22 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
throw RuntimeException("Unexpected " + abs.javaClass)
|
throw RuntimeException("Unexpected " + abs.javaClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn!!.setAutoCommit(false)
|
conn.setAutoCommit(false)
|
||||||
ps_insert_or_ignore.executeBatch()
|
ps_insert_or_ignore.executeBatch()
|
||||||
ps_insert_or_ignore.clearBatch()
|
ps_insert_or_ignore.clearBatch()
|
||||||
ps_insert_or_replace.executeBatch()
|
ps_insert_or_replace.executeBatch()
|
||||||
ps_insert_or_replace.clearBatch()
|
ps_insert_or_replace.clearBatch()
|
||||||
conn!!.commit()
|
conn.commit()
|
||||||
conn!!.setAutoCommit(true)
|
conn.setAutoCommit(true)
|
||||||
|
|
||||||
ps_insert_or_ignore.close()
|
ps_insert_or_ignore.close()
|
||||||
ps_insert_or_replace.close()
|
ps_insert_or_replace.close()
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw RuntimeException("Exception shown above happened.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchSetting(key: String): String? {
|
fun fetchSettings() = queryStringMap("SELECT key, value FROM settings")
|
||||||
val rs = stmt!!.executeQuery("SELECT value FROM settings WHERE key='${key}'")
|
|
||||||
rs.next()
|
|
||||||
return rs.getString(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveSetting(key: String, value: String?) {
|
fun saveSetting(key: String, value: String?) {
|
||||||
val ps = conn!!.prepareStatement("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)")
|
val ps = conn.prepareStatement("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)")
|
||||||
ps.setString(1, key)
|
ps.setString(1, key)
|
||||||
if (value==null) {
|
if (value==null) {
|
||||||
ps.setNull(2, Types.VARCHAR)
|
ps.setNull(2, Types.VARCHAR)
|
||||||
@ -566,59 +514,41 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
ps.setString(2, value)
|
ps.setString(2, value)
|
||||||
}
|
}
|
||||||
ps.execute()
|
ps.execute()
|
||||||
|
ps.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIdsFromQuery(query: String): LinkedList<Int> {
|
fun getIdsFromQuery(query: String): LinkedList<Int> {
|
||||||
try {
|
|
||||||
val list = LinkedList<Int>()
|
val list = LinkedList<Int>()
|
||||||
val rs = stmt!!.executeQuery(query)
|
val rs = stmt.executeQuery(query)
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
list.add(rs.getInt(1))
|
list.add(rs.getInt(1))
|
||||||
}
|
}
|
||||||
rs.close()
|
rs.close()
|
||||||
return list
|
return list
|
||||||
} catch (e: SQLException) {
|
|
||||||
throw RuntimeException(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessageTypesWithCount(c: AbstractChat): HashMap<String, Int> {
|
fun getMessageTypesWithCount(c: AbstractChat): HashMap<String, Int> {
|
||||||
val map = HashMap<String, Int>()
|
val map = HashMap<String, Int>()
|
||||||
try {
|
val rs = stmt.executeQuery("SELECT message_type, COUNT(message_id) FROM messages WHERE " + c.query + " GROUP BY message_type")
|
||||||
val rs = stmt!!.executeQuery("SELECT message_type, COUNT(message_id) FROM messages WHERE " + c.query + " GROUP BY message_type")
|
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
map.put("count.messages.type." + rs.getString(1), rs.getInt(2))
|
map.put("count.messages.type." + rs.getString(1), rs.getInt(2))
|
||||||
}
|
}
|
||||||
rs.close()
|
rs.close()
|
||||||
return map
|
return map
|
||||||
} catch (e: Exception) {
|
|
||||||
throw RuntimeException(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessageMediaTypesWithCount(c: AbstractChat): HashMap<String, Int> {
|
fun getMessageMediaTypesWithCount(c: AbstractChat): HashMap<String, Int> {
|
||||||
val map = HashMap<String, Int>()
|
val map = HashMap<String, Int>()
|
||||||
try {
|
|
||||||
var count = 0
|
var count = 0
|
||||||
val rs = stmt!!.executeQuery("SELECT media_type, COUNT(message_id) FROM messages WHERE " + c.query + " GROUP BY media_type")
|
val rs = stmt.executeQuery("SELECT media_type, COUNT(message_id) FROM messages WHERE " + c.query + " GROUP BY media_type")
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
var s = rs.getString(1)
|
var type = rs.getString(1) ?: "null"
|
||||||
if (s == null) {
|
if (type != "null") count += rs.getInt(2)
|
||||||
s = "null"
|
map.put("count.messages.media_type.${type}", rs.getInt(2))
|
||||||
} else {
|
|
||||||
count += rs.getInt(2)
|
|
||||||
}
|
|
||||||
map.put("count.messages.media_type.$s", rs.getInt(2))
|
|
||||||
}
|
}
|
||||||
map.put("count.messages.media_type.any", count)
|
map.put("count.messages.media_type.any", count)
|
||||||
rs.close()
|
rs.close()
|
||||||
return map
|
return map
|
||||||
} catch (e: Exception) {
|
|
||||||
throw RuntimeException(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessageAuthorsWithCount(c: AbstractChat): HashMap<String, Any> {
|
fun getMessageAuthorsWithCount(c: AbstractChat): HashMap<String, Any> {
|
||||||
@ -628,8 +558,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
// Set a default value for 'me' to fix the charts for channels - cause I
|
// Set a default value for 'me' to fix the charts for channels - cause I
|
||||||
// possibly didn't send any messages there.
|
// possibly didn't send any messages there.
|
||||||
map.put("authors.count.me", 0)
|
map.put("authors.count.me", 0)
|
||||||
try {
|
val rs = stmt.executeQuery("SELECT users.id, users.first_name, users.last_name, users.username, COUNT(messages.id) " +
|
||||||
val rs = stmt!!.executeQuery("SELECT users.id, users.first_name, users.last_name, users.username, COUNT(messages.id) " +
|
|
||||||
"FROM messages " +
|
"FROM messages " +
|
||||||
"LEFT JOIN users ON users.id=messages.sender_id " +
|
"LEFT JOIN users ON users.id=messages.sender_id " +
|
||||||
"WHERE " + c.query + " GROUP BY sender_id")
|
"WHERE " + c.query + " GROUP BY sender_id")
|
||||||
@ -655,23 +584,13 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
map.put("authors.all", all_data)
|
map.put("authors.all", all_data)
|
||||||
rs.close()
|
rs.close()
|
||||||
return map
|
return map
|
||||||
} catch (e: Exception) {
|
|
||||||
throw RuntimeException(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessageCountForExport(c: AbstractChat): Int {
|
fun getMessageCountForExport(c: AbstractChat): Int = queryInt("SELECT COUNT(*) FROM messages WHERE " + c.query)
|
||||||
val rs = stmt!!.executeQuery("SELECT COUNT(*) FROM messages WHERE " + c.query);
|
|
||||||
rs.next()
|
|
||||||
val result = rs.getInt(1)
|
|
||||||
rs.close()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getMessageTimesMatrix(c: AbstractChat): Array<IntArray> {
|
fun getMessageTimesMatrix(c: AbstractChat): Array<IntArray> {
|
||||||
val result = Array(7) { IntArray(24) }
|
val result = Array(7) { IntArray(24) }
|
||||||
try {
|
val rs = stmt.executeQuery("SELECT STRFTIME('%w', time, 'unixepoch') as DAY, " +
|
||||||
val rs = stmt!!.executeQuery("SELECT STRFTIME('%w', time, 'unixepoch') as DAY, " +
|
|
||||||
"STRFTIME('%H', time, 'unixepoch') AS hour, " +
|
"STRFTIME('%H', time, 'unixepoch') AS hour, " +
|
||||||
"COUNT(id) FROM messages WHERE " + c.query + " GROUP BY hour, day " +
|
"COUNT(id) FROM messages WHERE " + c.query + " GROUP BY hour, day " +
|
||||||
"ORDER BY hour, day")
|
"ORDER BY hour, day")
|
||||||
@ -680,14 +599,9 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
}
|
}
|
||||||
rs.close()
|
rs.close()
|
||||||
return result
|
return result
|
||||||
} catch (e: Exception) {
|
|
||||||
throw RuntimeException(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessagesForExport(c: AbstractChat, limit: Int=-1, offset: Int=0): LinkedList<HashMap<String, Any>> {
|
fun getMessagesForExport(c: AbstractChat, limit: Int=-1, offset: Int=0): LinkedList<HashMap<String, Any>> {
|
||||||
try {
|
|
||||||
var query = "SELECT messages.message_id as message_id, text, time*1000 as time, has_media, " +
|
var query = "SELECT messages.message_id as message_id, text, time*1000 as time, has_media, " +
|
||||||
"media_type, media_file, media_size, users.first_name as user_first_name, users.last_name as user_last_name, " +
|
"media_type, media_file, media_size, users.first_name as user_first_name, users.last_name as user_last_name, " +
|
||||||
"users.username as user_username, users.id as user_id, " +
|
"users.username as user_username, users.id as user_id, " +
|
||||||
@ -702,7 +616,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
query = query + " LIMIT ${limit} OFFSET ${offset}"
|
query = query + " LIMIT ${limit} OFFSET ${offset}"
|
||||||
}
|
}
|
||||||
|
|
||||||
val rs = stmt!!.executeQuery(query)
|
val rs = stmt.executeQuery(query)
|
||||||
|
|
||||||
val format_time = SimpleDateFormat("HH:mm:ss")
|
val format_time = SimpleDateFormat("HH:mm:ss")
|
||||||
val format_date = SimpleDateFormat("d MMM yy")
|
val format_date = SimpleDateFormat("d MMM yy")
|
||||||
@ -726,7 +640,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
if (rs.getString("media_type") != null) {
|
if (rs.getString("media_type") != null) {
|
||||||
h.put("media_" + rs.getString("media_type"), true)
|
h.put("media_" + rs.getString("media_type"), true)
|
||||||
}
|
}
|
||||||
h.put("from_me", rs.getInt("user_id") == user_manager.user!!.getId())
|
h.put("from_me", rs.getInt("user_id") == user_manager.id)
|
||||||
h.put("is_new_date", !date.equals(old_date))
|
h.put("is_new_date", !date.equals(old_date))
|
||||||
h.put("odd_even", if (count % 2 == 0) "even" else "odd")
|
h.put("odd_even", if (count % 2 == 0) "even" else "odd")
|
||||||
h.put("same_user", old_user != 0 && rs.getInt("user_id") == old_user)
|
h.put("same_user", old_user != 0 && rs.getInt("user_id") == old_user)
|
||||||
@ -738,11 +652,6 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
}
|
}
|
||||||
rs.close()
|
rs.close()
|
||||||
return list
|
return list
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw RuntimeException("Exception above!")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -752,21 +661,13 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner class Dialog(var id: Int, var first_name: String?, var last_name: String?, var username: String?, var count: Int?) : AbstractChat() {
|
inner class Dialog(var id: Int, var first_name: String?, var last_name: String?, var username: String?, var count: Int?) : AbstractChat() {
|
||||||
|
override val query = "source_type='dialog' AND source_id=" + id
|
||||||
override val query: String
|
override val type = "dialog"
|
||||||
get() = "source_type='dialog' AND source_id=" + id
|
|
||||||
|
|
||||||
override val type: String
|
|
||||||
get() = "dialog"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class Chat(var id: Int, var name: String?, var count: Int?) : AbstractChat() {
|
inner class Chat(var id: Int, var name: String?, var count: Int?) : AbstractChat() {
|
||||||
|
override val query = "source_type IN('group', 'supergroup', 'channel') AND source_id=" + id
|
||||||
override val query: String
|
override val type = "chat"
|
||||||
get() = "source_type IN('group', 'supergroup', 'channel') AND source_id=" + id
|
|
||||||
|
|
||||||
override val type: String
|
|
||||||
get() = "chat"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class User(id: Int, first_name: String?, last_name: String?) {
|
inner class User(id: Int, first_name: String?, last_name: String?) {
|
||||||
@ -774,7 +675,7 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
var isMe: Boolean = false
|
var isMe: Boolean = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
isMe = id == user_manager.user!!.getId()
|
isMe = id == user_manager.id
|
||||||
val s = StringBuilder()
|
val s = StringBuilder()
|
||||||
if (first_name != null) s.append(first_name + " ")
|
if (first_name != null) s.append(first_name + " ")
|
||||||
if (last_name != null) s.append(last_name)
|
if (last_name != null) s.append(last_name)
|
||||||
@ -783,26 +684,11 @@ class Database private constructor(var client: TelegramClient) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner class GlobalChat : AbstractChat() {
|
inner class GlobalChat : AbstractChat() {
|
||||||
override val query: String
|
override val query = "1=1"
|
||||||
get() = "1=1"
|
override val type = "GlobalChat"
|
||||||
|
|
||||||
override val type: String
|
|
||||||
get() = "GlobalChat"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(Database::class.java)
|
|
||||||
internal var instance: Database? = null
|
|
||||||
|
|
||||||
fun init(c: TelegramClient) {
|
|
||||||
instance = Database(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getInstance(): Database {
|
|
||||||
if (instance == null) throw RuntimeException("Database is not initialized but getInstance() was called.")
|
|
||||||
return instance!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bytesToTLMessage(b: ByteArray?): TLMessage? {
|
fun bytesToTLMessage(b: ByteArray?): TLMessage? {
|
||||||
try {
|
try {
|
||||||
if (b == null) return null
|
if (b == null) return null
|
||||||
|
@ -313,7 +313,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.client)
|
val f = FileManagerFactory.getFileManager(msg, db.user_manager, 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)
|
||||||
@ -431,7 +431,7 @@ internal class DB_Update_9(conn: Connection, db: Database) : DatabaseUpdate(conn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
rs.close()
|
rs.close()
|
||||||
db.saveMessages(messages, api_layer=53, source_type=MessageSource.SUPERGROUP)
|
db.saveMessages(messages, api_layer=53, source_type=MessageSource.SUPERGROUP, settings=null)
|
||||||
execute("DELETE FROM messages WHERE id IN (" + messages_to_delete.joinToString() + ")")
|
execute("DELETE FROM messages WHERE id IN (" + messages_to_delete.joinToString() + ")")
|
||||||
print(".")
|
print(".")
|
||||||
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
package de.fabianonline.telegram_backup
|
|
||||||
|
|
||||||
class DbSettings() {
|
|
||||||
private fun fetchValue(name: String): String? = Database.getInstance().fetchSetting(name)
|
|
||||||
private fun saveValue(name: String, value: String?) = Database.getInstance().saveSetting(name, value)
|
|
||||||
|
|
||||||
var pts: String?
|
|
||||||
get() = fetchValue("pts")
|
|
||||||
set(x: String?) = saveValue("pts", x)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -18,7 +18,6 @@ package de.fabianonline.telegram_backup
|
|||||||
|
|
||||||
import de.fabianonline.telegram_backup.UserManager
|
import de.fabianonline.telegram_backup.UserManager
|
||||||
import de.fabianonline.telegram_backup.Database
|
import de.fabianonline.telegram_backup.Database
|
||||||
import de.fabianonline.telegram_backup.StickerConverter
|
|
||||||
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
||||||
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
|
||||||
@ -61,55 +60,21 @@ enum class MessageSource(val descr: String) {
|
|||||||
SUPERGROUP("supergroup")
|
SUPERGROUP("supergroup")
|
||||||
}
|
}
|
||||||
|
|
||||||
class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressInterface) {
|
class DownloadManager(val client: TelegramClient, val prog: DownloadProgressInterface, val db: Database, val user_manager: UserManager, val settings: Settings, val file_base: String) {
|
||||||
internal var user: UserManager? = null
|
|
||||||
internal var db: Database? = null
|
|
||||||
internal var prog: DownloadProgressInterface? = null
|
|
||||||
internal var has_seen_flood_wait_message = false
|
|
||||||
|
|
||||||
init {
|
|
||||||
this.user = UserManager.getInstance()
|
|
||||||
this.prog = p
|
|
||||||
this.db = Database.getInstance()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
@Throws(RpcErrorException::class, IOException::class)
|
||||||
fun downloadMessages(limit: Int?) {
|
fun downloadMessages(limit: Int?) {
|
||||||
var completed: Boolean
|
logger.info("This is downloadMessages with limit {}", limit)
|
||||||
do {
|
|
||||||
completed = true
|
|
||||||
try {
|
|
||||||
_downloadMessages(limit)
|
|
||||||
} catch (e: RpcErrorException) {
|
|
||||||
if (e.getCode() == 420) { // FLOOD_WAIT
|
|
||||||
completed = false
|
|
||||||
Utils.obeyFloodWaitException(e)
|
|
||||||
} else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
} catch (e: TimeoutException) {
|
|
||||||
completed = false
|
|
||||||
System.out.println("")
|
|
||||||
System.out.println("Telegram took too long to respond to our request.")
|
|
||||||
System.out.println("I'm going to wait a minute and then try again.")
|
|
||||||
try {
|
|
||||||
TimeUnit.MINUTES.sleep(1)
|
|
||||||
} catch (e2: InterruptedException) {
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("")
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (!completed)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
|
||||||
fun _downloadMessages(limit: Int?) {
|
|
||||||
logger.info("This is _downloadMessages with limit {}", limit)
|
|
||||||
logger.info("Downloading the last dialogs")
|
logger.info("Downloading the last dialogs")
|
||||||
System.out.println("Downloading most recent dialogs... ")
|
System.out.println("Downloading most recent dialogs... ")
|
||||||
var max_message_id = 0
|
var max_message_id = 0
|
||||||
val chats = getChats()
|
var result: ChatList? = null
|
||||||
|
|
||||||
|
Utils.obeyFloodWait() {
|
||||||
|
result = getChats()
|
||||||
|
}
|
||||||
|
|
||||||
|
val chats = result!!
|
||||||
|
|
||||||
logger.debug("Got {} dialogs, {} supergoups, {} channels", chats.dialogs.size, chats.supergroups.size, chats.channels.size)
|
logger.debug("Got {} dialogs, {} supergoups, {} channels", chats.dialogs.size, chats.supergroups.size, chats.channels.size)
|
||||||
|
|
||||||
for (d in chats.dialogs) {
|
for (d in chats.dialogs) {
|
||||||
@ -119,7 +84,7 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("Top message ID is " + max_message_id)
|
System.out.println("Top message ID is " + max_message_id)
|
||||||
var max_database_id = db!!.getTopMessageID()
|
var max_database_id = db.getTopMessageID()
|
||||||
System.out.println("Top message ID in database is " + max_database_id)
|
System.out.println("Top message ID in database is " + max_database_id)
|
||||||
if (limit != null) {
|
if (limit != null) {
|
||||||
System.out.println("Limit is set to " + limit)
|
System.out.println("Limit is set to " + limit)
|
||||||
@ -136,7 +101,7 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
if (max_database_id == max_message_id) {
|
if (max_database_id == max_message_id) {
|
||||||
System.out.println("No new messages to download.")
|
System.out.println("No new messages to download.")
|
||||||
} else if (max_database_id > max_message_id) {
|
} else if (max_database_id > max_message_id) {
|
||||||
throw RuntimeException("max_database_id is bigger then max_message_id. This shouldn't happen. But the telegram api nonetheless does that sometimes. Just ignore this error, wait a few seconds and then try again.")
|
throw RuntimeException("max_database_id is bigger than max_message_id. This shouldn't happen. But the telegram api nonetheless does that sometimes. Just ignore this error, wait a few seconds and then try again.")
|
||||||
} else {
|
} else {
|
||||||
val start_id = max_database_id + 1
|
val start_id = max_database_id + 1
|
||||||
val end_id = max_message_id
|
val end_id = max_message_id
|
||||||
@ -147,8 +112,8 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
|
|
||||||
logger.info("Searching for missing messages in the db")
|
logger.info("Searching for missing messages in the db")
|
||||||
System.out.println("Checking message database for completeness...")
|
System.out.println("Checking message database for completeness...")
|
||||||
val db_count = db!!.getMessageCount()
|
val db_count = db.getMessageCount()
|
||||||
val db_max = db!!.getTopMessageID()
|
val db_max = db.getTopMessageID()
|
||||||
logger.debug("db_count: {}", db_count)
|
logger.debug("db_count: {}", db_count)
|
||||||
logger.debug("db_max: {}", db_max)
|
logger.debug("db_max: {}", db_max)
|
||||||
|
|
||||||
@ -173,24 +138,20 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (IniSettings.download_channels || IniSettings.download_supergroups) {
|
if (settings.download_channels) {
|
||||||
// TODO Add chat title (and other stuff?) to the database
|
|
||||||
|
|
||||||
if (IniSettings.download_channels) {
|
|
||||||
println("Checking channels...")
|
println("Checking channels...")
|
||||||
for (channel in chats.channels) { if (channel.download) downloadMessagesFromChannel(channel) }
|
for (channel in chats.channels) { if (channel.download) downloadMessagesFromChannel(channel) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IniSettings.download_supergroups) {
|
if (settings.download_supergroups) {
|
||||||
println("Checking supergroups...")
|
println("Checking supergroups...")
|
||||||
for (supergroup in chats.supergroups) { if (supergroup.download) downloadMessagesFromChannel(supergroup) }
|
for (supergroup in chats.supergroups) { if (supergroup.download) downloadMessagesFromChannel(supergroup) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun downloadMessagesFromChannel(channel: Channel) {
|
private fun downloadMessagesFromChannel(channel: Channel) {
|
||||||
val obj = channel.obj
|
val obj = channel.obj
|
||||||
val max_known_id = db!!.getTopMessageIDForChannel(channel.id)
|
val max_known_id = db.getTopMessageIDForChannel(channel.id)
|
||||||
if (obj.getTopMessage() > max_known_id) {
|
if (obj.getTopMessage() > max_known_id) {
|
||||||
val ids = makeIdList(max_known_id + 1, obj.getTopMessage())
|
val ids = makeIdList(max_known_id + 1, obj.getTopMessage())
|
||||||
var channel_name = channel.title
|
var channel_name = channel.title
|
||||||
@ -210,7 +171,7 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
} else {
|
} else {
|
||||||
"${source_type.descr} $source_name"
|
"${source_type.descr} $source_name"
|
||||||
}
|
}
|
||||||
prog!!.onMessageDownloadStart(ids.size, source_string)
|
prog.onMessageDownloadStart(ids.size, source_string)
|
||||||
|
|
||||||
logger.debug("Entering download loop")
|
logger.debug("Entering download loop")
|
||||||
while (ids.size > 0) {
|
while (ids.size > 0) {
|
||||||
@ -225,105 +186,62 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
logger.trace("vector.size(): {}", vector.size)
|
logger.trace("vector.size(): {}", vector.size)
|
||||||
logger.trace("ids.size(): {}", ids.size)
|
logger.trace("ids.size(): {}", ids.size)
|
||||||
|
|
||||||
var response: TLAbsMessages
|
var resp: TLAbsMessages? = null
|
||||||
var tries = 0
|
try {
|
||||||
while (true) {
|
Utils.obeyFloodWait(max_tries=5) {
|
||||||
logger.trace("Trying getMessages(), tries={}", tries)
|
if (channel == null) {
|
||||||
if (tries >= 5) {
|
resp = client.messagesGetMessages(vector)
|
||||||
|
} else {
|
||||||
|
resp = client.channelsGetMessages(channel, vector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: MaxTriesExceededException) {
|
||||||
CommandLineController.show_error("Couldn't getMessages after 5 tries. Quitting.")
|
CommandLineController.show_error("Couldn't getMessages after 5 tries. Quitting.")
|
||||||
}
|
}
|
||||||
tries++
|
val response = resp!!
|
||||||
try {
|
|
||||||
if (channel == null) {
|
|
||||||
response = client!!.messagesGetMessages(vector)
|
|
||||||
} else {
|
|
||||||
response = client!!.channelsGetMessages(channel, vector)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
} catch (e: RpcErrorException) {
|
|
||||||
if (e.getCode() == 420) { // FLOOD_WAIT
|
|
||||||
Utils.obeyFloodWaitException(e, has_seen_flood_wait_message)
|
|
||||||
has_seen_flood_wait_message = true
|
|
||||||
} else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
logger.trace("response.getMessages().size(): {}", response.getMessages().size)
|
logger.trace("response.getMessages().size(): {}", response.getMessages().size)
|
||||||
if (response.getMessages().size != vector.size) {
|
if (response.getMessages().size != vector.size) {
|
||||||
CommandLineController.show_error("Requested ${vector.size} messages, but got ${response.getMessages().size}. That is unexpected. Quitting.")
|
CommandLineController.show_error("Requested ${vector.size} messages, but got ${response.getMessages().size}. That is unexpected. Quitting.")
|
||||||
}
|
}
|
||||||
|
|
||||||
prog!!.onMessageDownloaded(response.getMessages().size)
|
prog.onMessageDownloaded(response.getMessages().size)
|
||||||
db!!.saveMessages(response.getMessages(), Kotlogram.API_LAYER, source_type=source_type)
|
db.saveMessages(response.getMessages(), Kotlogram.API_LAYER, source_type=source_type, settings=settings)
|
||||||
db!!.saveChats(response.getChats())
|
db.saveChats(response.getChats())
|
||||||
db!!.saveUsers(response.getUsers())
|
db.saveUsers(response.getUsers())
|
||||||
logger.trace("Sleeping")
|
logger.trace("Sleeping")
|
||||||
try {
|
try { TimeUnit.MILLISECONDS.sleep(Config.DELAY_AFTER_GET_MESSAGES) } catch (e: InterruptedException) { }
|
||||||
TimeUnit.MILLISECONDS.sleep(Config.DELAY_AFTER_GET_MESSAGES)
|
|
||||||
} catch (e: InterruptedException) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
logger.debug("Finished.")
|
logger.debug("Finished.")
|
||||||
|
|
||||||
prog!!.onMessageDownloadFinished()
|
prog.onMessageDownloadFinished()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
@Throws(RpcErrorException::class, IOException::class)
|
||||||
fun downloadMedia() {
|
fun downloadMedia() {
|
||||||
download_client = client!!.getDownloaderClient()
|
download_client = client.getDownloaderClient()
|
||||||
var completed: Boolean
|
|
||||||
do {
|
|
||||||
completed = true
|
|
||||||
try {
|
|
||||||
_downloadMedia()
|
|
||||||
} catch (e: RpcErrorException) {
|
|
||||||
if (e.getCode() == 420) { // FLOOD_WAIT
|
|
||||||
completed = false
|
|
||||||
Utils.obeyFloodWaitException(e)
|
|
||||||
} else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*catch (TimeoutException e) {
|
|
||||||
completed = false;
|
|
||||||
System.out.println("");
|
|
||||||
System.out.println("Telegram took too long to respond to our request.");
|
|
||||||
System.out.println("I'm going to wait a minute and then try again.");
|
|
||||||
logger.warn("TimeoutException caught", e);
|
|
||||||
try { TimeUnit.MINUTES.sleep(1); } catch(InterruptedException e2) {}
|
|
||||||
System.out.println("");
|
|
||||||
}*/
|
|
||||||
} while (!completed)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
|
||||||
private fun _downloadMedia() {
|
|
||||||
logger.info("This is _downloadMedia")
|
logger.info("This is _downloadMedia")
|
||||||
logger.info("Checking if there are messages in the DB with a too old API layer")
|
logger.info("Checking if there are messages in the DB with a too old API layer")
|
||||||
val ids = db!!.getIdsFromQuery("SELECT id FROM messages WHERE has_media=1 AND api_layer<" + Kotlogram.API_LAYER)
|
val ids = db.getIdsFromQuery("SELECT id FROM messages WHERE has_media=1 AND api_layer<" + Kotlogram.API_LAYER)
|
||||||
if (ids.size > 0) {
|
if (ids.size > 0) {
|
||||||
System.out.println("You have ${ids.size} messages in your db that need an update. Doing that now.")
|
System.out.println("You have ${ids.size} messages in your db that need an update. Doing that now.")
|
||||||
logger.debug("Found {} messages", ids.size)
|
logger.debug("Found {} messages", ids.size)
|
||||||
downloadMessages(ids, null, source_type=MessageSource.NORMAL)
|
downloadMessages(ids, null, source_type=MessageSource.NORMAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val message_count = db.getMessagesWithMediaCount()
|
||||||
|
prog.onMediaDownloadStart(message_count)
|
||||||
var offset = 0
|
var offset = 0
|
||||||
val limit = 1000
|
val limit = 1000
|
||||||
val message_count = this.db!!.getMessagesWithMediaCount()
|
|
||||||
prog!!.onMediaDownloadStart(message_count)
|
|
||||||
while (true) {
|
while (true) {
|
||||||
logger.debug("Querying messages with media, limit={}, offset={}", limit, offset)
|
logger.debug("Querying messages with media, limit={}, offset={}", limit, offset)
|
||||||
val messages = this.db!!.getMessagesWithMedia(limit=limit, offset=offset)
|
val messages = db.getMessagesWithMedia(limit, offset)
|
||||||
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)
|
||||||
|
prog.onMediaDownloadStart(messages.size)
|
||||||
for (msg in messages) {
|
for (msg in messages) {
|
||||||
if (msg == null) continue
|
if (msg == null) continue
|
||||||
val m = FileManagerFactory.getFileManager(msg, user!!, client!!)
|
val m = FileManagerFactory.getFileManager(msg, user_manager, file_base, settings=settings)
|
||||||
logger.trace("message {}, {}, {}, {}, {}",
|
logger.trace("message {}, {}, {}, {}, {}",
|
||||||
msg.getId(),
|
msg.getId(),
|
||||||
msg.getMedia().javaClass.getSimpleName().replace("TLMessageMedia", "…"),
|
msg.getMedia().javaClass.getSimpleName().replace("TLMessageMedia", "…"),
|
||||||
@ -331,27 +249,27 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
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 (IniSettings.max_file_age!=null && (System.currentTimeMillis() / 1000) - msg.date > IniSettings.max_file_age * 24 * 60 * 60) {
|
} else if (settings.max_file_age>0 && (System.currentTimeMillis() / 1000) - msg.date > settings.max_file_age * 24 * 60 * 60) {
|
||||||
prog!!.onMediaTooOld()
|
prog.onMediaTooOld()
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
val result = m.download()
|
val result = m.download()
|
||||||
if (result) {
|
if (result) {
|
||||||
prog!!.onMediaDownloaded(m)
|
prog.onMediaDownloaded(m)
|
||||||
} else {
|
} else {
|
||||||
prog!!.onMediaSkipped()
|
prog.onMediaSkipped()
|
||||||
}
|
}
|
||||||
} catch (e: TimeoutException) {
|
} catch (e: TimeoutException) {
|
||||||
// do nothing - skip this file
|
// do nothing - skip this file
|
||||||
prog!!.onMediaSkipped()
|
prog.onMediaSkipped()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prog!!.onMediaDownloadFinished()
|
prog.onMediaDownloadFinished()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeIdList(start: Int, end: Int): MutableList<Int> {
|
private fun makeIdList(start: Int, end: Int): MutableList<Int> {
|
||||||
@ -363,7 +281,7 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
fun getChats(): ChatList {
|
fun getChats(): ChatList {
|
||||||
val cl = ChatList()
|
val cl = ChatList()
|
||||||
logger.trace("Calling messagesGetDialogs")
|
logger.trace("Calling messagesGetDialogs")
|
||||||
val dialogs = client!!.messagesGetDialogs(0, 0, TLInputPeerEmpty(), 100)
|
val dialogs = client.messagesGetDialogs(0, 0, TLInputPeerEmpty(), 100)
|
||||||
logger.trace("Got {} dialogs back", dialogs.getDialogs().size)
|
logger.trace("Got {} dialogs back", dialogs.getDialogs().size)
|
||||||
|
|
||||||
// Add dialogs
|
// Add dialogs
|
||||||
@ -376,10 +294,10 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
if (tl_peer_channel == null) continue
|
if (tl_peer_channel == null) continue
|
||||||
|
|
||||||
var download = true
|
var download = true
|
||||||
if (IniSettings.whitelist_channels != null) {
|
if (settings.whitelist_channels.isNotEmpty()) {
|
||||||
download = IniSettings.whitelist_channels!!.contains(tl_channel.getId().toString())
|
download = settings.whitelist_channels.contains(tl_channel.getId().toString())
|
||||||
} else if (IniSettings.blacklist_channels != null) {
|
} else if (settings.blacklist_channels.isNotEmpty()) {
|
||||||
download = !IniSettings.blacklist_channels!!.contains(tl_channel.getId().toString())
|
download = !settings.blacklist_channels.contains(tl_channel.getId().toString())
|
||||||
}
|
}
|
||||||
val channel = Channel(id=tl_channel.getId(), access_hash=tl_channel.getAccessHash(), title=tl_channel.getTitle(), obj=tl_peer_channel, download=download)
|
val channel = Channel(id=tl_channel.getId(), access_hash=tl_channel.getAccessHash(), title=tl_channel.getTitle(), obj=tl_peer_channel, download=download)
|
||||||
if (tl_channel.getMegagroup()) {
|
if (tl_channel.getMegagroup()) {
|
||||||
@ -440,38 +358,32 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
}
|
}
|
||||||
logger.trace("offset before the loop is {}", offset)
|
logger.trace("offset before the loop is {}", offset)
|
||||||
fos = FileOutputStream(temp_filename, true)
|
fos = FileOutputStream(temp_filename, true)
|
||||||
var response: TLFile? = null
|
|
||||||
var try_again: Boolean
|
|
||||||
do {
|
do {
|
||||||
try_again = false
|
|
||||||
logger.trace("offset: {} block_size: {} size: {}", offset, size, size)
|
logger.trace("offset: {} block_size: {} size: {}", offset, size, size)
|
||||||
val req = TLRequestUploadGetFile(loc, offset, size)
|
val req = TLRequestUploadGetFile(loc, offset, size)
|
||||||
|
var resp: TLFile? = null
|
||||||
try {
|
try {
|
||||||
response = download_client!!.executeRpcQuery(req, dcID) as TLFile
|
Utils.obeyFloodWait() {
|
||||||
|
resp = download_client!!.executeRpcQuery(req, dcID) as TLFile
|
||||||
|
}
|
||||||
} catch (e: RpcErrorException) {
|
} catch (e: RpcErrorException) {
|
||||||
if (e.getCode() == 420) { // FLOOD_WAIT
|
if (e.getCode() == 400) {
|
||||||
try_again = true
|
// Somehow this file is broken. No idea why. Let's skip it for now.
|
||||||
Utils.obeyFloodWaitException(e)
|
|
||||||
continue // response is null since we didn't actually receive any data. Skip the rest of this iteration and try again.
|
|
||||||
} else if (e.getCode() == 400) {
|
|
||||||
//Somehow this file is broken. No idea why. Let's skip it for now
|
|
||||||
return false
|
return false
|
||||||
} else {
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
val response = resp!!
|
||||||
|
|
||||||
offset += response.getBytes().getData().size
|
offset += response.getBytes().getData().size
|
||||||
logger.trace("response: {} total size: {}", response.getBytes().getData().size, offset)
|
logger.trace("response: {} total size: {}", response.getBytes().getData().size, offset)
|
||||||
|
|
||||||
fos.write(response.getBytes().getData())
|
fos.write(response.getBytes().getData())
|
||||||
fos.flush()
|
fos.flush()
|
||||||
try {
|
try { TimeUnit.MILLISECONDS.sleep(Config.DELAY_AFTER_GET_FILE) } catch (e: InterruptedException) { }
|
||||||
TimeUnit.MILLISECONDS.sleep(Config.DELAY_AFTER_GET_FILE)
|
|
||||||
} catch (e: InterruptedException) {
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (offset < size && (try_again || response!!.getBytes().getData().size > 0))
|
} while (offset < size && response.getBytes().getData().size > 0)
|
||||||
fos.close()
|
fos.close()
|
||||||
if (offset < size) {
|
if (offset < size) {
|
||||||
System.out.println("Requested file $target with $size bytes, but got only $offset bytes.")
|
System.out.println("Requested file $target with $size bytes, but got only $offset bytes.")
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
/* Telegram_Backup
|
|
||||||
* Copyright (C) 2016 Fabian Schlenz
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
package de.fabianonline.telegram_backup
|
|
||||||
|
|
||||||
import javax.swing.*
|
|
||||||
import javax.swing.event.ListSelectionEvent
|
|
||||||
import javax.swing.event.ListSelectionListener
|
|
||||||
import java.awt.*
|
|
||||||
import java.awt.event.ActionEvent
|
|
||||||
import java.awt.event.ActionListener
|
|
||||||
import java.util.Vector
|
|
||||||
|
|
||||||
class GUIController() {
|
|
||||||
init {
|
|
||||||
showAccountChooserDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showAccountChooserDialog() {
|
|
||||||
val accountChooser = JDialog()
|
|
||||||
accountChooser.setTitle("Choose account")
|
|
||||||
accountChooser.setSize(400, 200)
|
|
||||||
val vert = JPanel()
|
|
||||||
vert.setLayout(BorderLayout())
|
|
||||||
vert.add(JLabel("Please select the account to use or create a new one."), BorderLayout.NORTH)
|
|
||||||
val accounts = Utils.getAccounts()
|
|
||||||
val list = JList<String>(accounts)
|
|
||||||
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
|
||||||
vert.add(list, BorderLayout.CENTER)
|
|
||||||
val bottom = JPanel(GridLayout(1, 2))
|
|
||||||
val btnAddAccount = JButton("Add account")
|
|
||||||
bottom.add(btnAddAccount)
|
|
||||||
val btnLogin = JButton("Login")
|
|
||||||
btnLogin.setEnabled(false)
|
|
||||||
bottom.add(btnLogin)
|
|
||||||
vert.add(bottom, BorderLayout.SOUTH)
|
|
||||||
accountChooser.add(vert)
|
|
||||||
accountChooser.setVisible(true)
|
|
||||||
accountChooser.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addAccountDialog() {
|
|
||||||
val loginDialog = JDialog()
|
|
||||||
loginDialog.setTitle("Add an account")
|
|
||||||
loginDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
|
||||||
|
|
||||||
val sections = JPanel()
|
|
||||||
sections.setLayout(BoxLayout(sections, BoxLayout.Y_AXIS))
|
|
||||||
|
|
||||||
val top = JPanel()
|
|
||||||
top.setLayout(BoxLayout(top, BoxLayout.Y_AXIS))
|
|
||||||
top.add(JLabel("Please enter your phone number in international format:"))
|
|
||||||
top.add(JTextField("+49123773212"))
|
|
||||||
|
|
||||||
sections.add(top)
|
|
||||||
sections.add(Box.createVerticalStrut(5))
|
|
||||||
sections.add(JSeparator(SwingConstants.HORIZONTAL))
|
|
||||||
|
|
||||||
val middle = JPanel()
|
|
||||||
middle.setLayout(BoxLayout(middle, BoxLayout.Y_AXIS))
|
|
||||||
middle.add(JLabel("Telegram sent you a code. Enter it here:"))
|
|
||||||
middle.add(JTextField())
|
|
||||||
middle.setEnabled(false)
|
|
||||||
|
|
||||||
sections.add(middle)
|
|
||||||
sections.add(Box.createVerticalStrut(5))
|
|
||||||
sections.add(JSeparator(SwingConstants.HORIZONTAL))
|
|
||||||
|
|
||||||
loginDialog.add(sections)
|
|
||||||
loginDialog.setVisible(true)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
package de.fabianonline.telegram_backup
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.slf4j.Logger
|
|
||||||
|
|
||||||
object IniSettings {
|
|
||||||
val logger = LoggerFactory.getLogger(IniSettings::class.java)
|
|
||||||
var settings = mutableMapOf<String, MutableList<String>>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (UserManager.getInstance().user != null) {
|
|
||||||
loadIni(UserManager.getInstance().fileBase + "config.ini")
|
|
||||||
copySampleIni(UserManager.getInstance().fileBase + "config.sample.ini")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dummy function that can be called to force this object to run its init-code.
|
|
||||||
fun load() { }
|
|
||||||
|
|
||||||
private fun loadIni(filename: String) {
|
|
||||||
val file = File(filename)
|
|
||||||
logger.trace("Checking ini file {}", filename.anonymize())
|
|
||||||
if (!file.exists()) return
|
|
||||||
logger.debug("Loading ini file {}", filename.anonymize())
|
|
||||||
file.forEachLine { parseLine(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseLine(original_line: String) {
|
|
||||||
logger.trace("Parsing line: {}", original_line)
|
|
||||||
var line = original_line.trim().replaceAfter("#", "").removeSuffix("#")
|
|
||||||
logger.trace("After cleaning: {}", line)
|
|
||||||
if (line == "") return
|
|
||||||
val parts: List<String> = line.split("=", limit=2).map{it.trim()}
|
|
||||||
|
|
||||||
if (parts.size < 2) throw RuntimeException("Invalid config setting: $line")
|
|
||||||
|
|
||||||
val (key, value) = parts
|
|
||||||
if (value=="") {
|
|
||||||
settings.remove(key)
|
|
||||||
} else {
|
|
||||||
var map = settings.get(key)
|
|
||||||
if (map == null) {
|
|
||||||
map = mutableListOf<String>()
|
|
||||||
settings.put(key, map)
|
|
||||||
}
|
|
||||||
map.add(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun copySampleIni(filename: String) {
|
|
||||||
val stream = Config::class.java.getResourceAsStream("/config.sample.ini")
|
|
||||||
File(filename).outputStream().use { stream.copyTo(it) }
|
|
||||||
stream.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun println() = println(settings)
|
|
||||||
|
|
||||||
fun getString(key: String, default: String? = null): String? = settings.get(key)?.last() ?: default
|
|
||||||
fun getStringList(key: String): List<String>? = settings.get(key)
|
|
||||||
fun getInt(key: String, default: Int? = null): Int? = try { settings.get(key)?.last()?.toInt() } catch (e: NumberFormatException) { null } ?: default
|
|
||||||
fun getBoolean(key: String, default: Boolean = false): Boolean {
|
|
||||||
val value = settings.get(key)?.last()
|
|
||||||
if (value==null) return default
|
|
||||||
return value=="true"
|
|
||||||
}
|
|
||||||
fun getArray(key: String): List<String> = settings.get(key) ?: listOf<String>()
|
|
||||||
|
|
||||||
val gmaps_key: String
|
|
||||||
get() = getString("gmaps_key", default=Config.SECRET_GMAPS)!!
|
|
||||||
val pagination: Boolean
|
|
||||||
get() = getBoolean("pagination", default=true)
|
|
||||||
val pagination_size: Int
|
|
||||||
get() = getInt("pagination_size", default=Config.DEFAULT_PAGINATION)!!
|
|
||||||
val download_media: Boolean
|
|
||||||
get() = getBoolean("download_media", default=true)
|
|
||||||
val download_channels: Boolean
|
|
||||||
get() = getBoolean("download_channels", default=false)
|
|
||||||
val download_supergroups: Boolean
|
|
||||||
get() = getBoolean("download_supergroups", default=false)
|
|
||||||
val whitelist_channels: List<String>?
|
|
||||||
get() = getStringList("whitelist_channels")
|
|
||||||
val blacklist_channels: List<String>?
|
|
||||||
get() = getStringList("blacklist_channels")
|
|
||||||
val max_file_age = getInt("max_file_age")
|
|
||||||
}
|
|
@ -0,0 +1,96 @@
|
|||||||
|
package de.fabianonline.telegram_backup
|
||||||
|
|
||||||
|
import com.github.badoualy.telegram.api.Kotlogram
|
||||||
|
import com.github.badoualy.telegram.api.TelegramApp
|
||||||
|
import com.github.badoualy.telegram.api.TelegramClient
|
||||||
|
import com.github.badoualy.telegram.tl.api.TLUser
|
||||||
|
import com.github.badoualy.telegram.tl.api.account.TLPassword
|
||||||
|
import com.github.badoualy.telegram.tl.api.auth.TLSentCode
|
||||||
|
import com.github.badoualy.telegram.tl.core.TLBytes
|
||||||
|
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class LoginManager(val app: TelegramApp, val target_dir: String, val phoneToUse: String?) {
|
||||||
|
fun run() {
|
||||||
|
var phone: String
|
||||||
|
|
||||||
|
if (phoneToUse == null) {
|
||||||
|
println("Please enter your phone number in international format.")
|
||||||
|
println("Example: +4917077651234")
|
||||||
|
phone = getLine()
|
||||||
|
} else {
|
||||||
|
phone = phoneToUse
|
||||||
|
}
|
||||||
|
|
||||||
|
val file_base = CommandLineController.build_file_base(target_dir, phone)
|
||||||
|
|
||||||
|
// We now have an account, so we can create an ApiStorage and TelegramClient.
|
||||||
|
val storage = ApiStorage(file_base)
|
||||||
|
val client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, null)
|
||||||
|
|
||||||
|
val sent_code = send_code_to_phone_number(client, phone)
|
||||||
|
println("Telegram sent you a code. Please enter it here.")
|
||||||
|
val code = getLine()
|
||||||
|
|
||||||
|
try {
|
||||||
|
verify_code(client, phone, sent_code, code)
|
||||||
|
} catch(e: PasswordNeededException) {
|
||||||
|
println("We also need your account password. Please enter it now. It should not be printed, so it's okay if you see nothing while typing it.")
|
||||||
|
val pw = getPassword()
|
||||||
|
verify_password(client, pw)
|
||||||
|
}
|
||||||
|
System.out.println("Everything seems fine. Please run this tool again with '--account ${phone} to use this account.")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun send_code_to_phone_number(client: TelegramClient, phone: String): TLSentCode {
|
||||||
|
return client.authSendCode(false, phone, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verify_code(client: TelegramClient, phone: String, sent_code: TLSentCode, code: String): TLUser {
|
||||||
|
try {
|
||||||
|
val auth = client.authSignIn(phone, sent_code.getPhoneCodeHash(), code)
|
||||||
|
return auth.getUser().getAsUser()
|
||||||
|
} catch (e: RpcErrorException) {
|
||||||
|
if (e.getCode() == 401 && e.getTag()=="SESSION_PASSWORD_NEEDED") {
|
||||||
|
throw PasswordNeededException()
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verify_password(client: TelegramClient, password: String): TLUser {
|
||||||
|
val pw = password.toByteArray(charset = Charsets.UTF_8)
|
||||||
|
val salt = (client.accountGetPassword() as TLPassword).getCurrentSalt().getData()
|
||||||
|
val md = MessageDigest.getInstance("SHA-256")
|
||||||
|
|
||||||
|
val salted = ByteArray(2 * salt.size + pw.size)
|
||||||
|
System.arraycopy(salt, 0, salted, 0, salt.size)
|
||||||
|
System.arraycopy(pw, 0, salted, salt.size, pw.size)
|
||||||
|
System.arraycopy(salt, 0, salted, salt.size + pw.size, salt.size)
|
||||||
|
val hash = md.digest(salted)
|
||||||
|
val auth = client.authCheckPassword(TLBytes(hash))
|
||||||
|
return auth.getUser().getAsUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getLine(): String {
|
||||||
|
if (System.console() != null) {
|
||||||
|
return System.console().readLine("> ")
|
||||||
|
} else {
|
||||||
|
print("> ")
|
||||||
|
return Scanner(System.`in`).nextLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPassword(): String {
|
||||||
|
if (System.console() != null) {
|
||||||
|
return String(System.console().readPassword("> "))
|
||||||
|
} else {
|
||||||
|
return getLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PasswordNeededException: Exception("A password is needed to be able to login to this account.") {}
|
135
src/main/kotlin/de/fabianonline/telegram_backup/Settings.kt
Normal file
135
src/main/kotlin/de/fabianonline/telegram_backup/Settings.kt
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package de.fabianonline.telegram_backup
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.util.LinkedList
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
class Settings(val file_base: String, val database: Database, val cli_settings: CommandLineOptions) {
|
||||||
|
val logger = LoggerFactory.getLogger(Settings::class.java)
|
||||||
|
|
||||||
|
private val db_settings: Map<String, String>
|
||||||
|
|
||||||
|
val ini_settings: Map<String, List<String>>
|
||||||
|
|
||||||
|
init {
|
||||||
|
db_settings = database.fetchSettings()
|
||||||
|
ini_settings = load_ini("config.ini")
|
||||||
|
copy_sample_ini("config.sample.ini")
|
||||||
|
}
|
||||||
|
// Merging CLI and INI settings
|
||||||
|
|
||||||
|
val sf = SettingsFactory(ini_settings, cli_settings)
|
||||||
|
val gmaps_key = sf.getString("gmaps_key", default=Config.SECRET_GMAPS, secret=true)
|
||||||
|
val pagination = sf.getBoolean("pagination", default=true)
|
||||||
|
val pagination_size = sf.getInt("pagination_size", default=Config.DEFAULT_PAGINATION)
|
||||||
|
val download_media = sf.getBoolean("download_media", default=true)
|
||||||
|
val download_channels = sf.getBoolean("download_channels", default=false)
|
||||||
|
val download_supergroups = sf.getBoolean("download_supergroups", default=false)
|
||||||
|
val whitelist_channels = sf.getStringList("whitelist_channels", default=LinkedList<String>())
|
||||||
|
val blacklist_channels = sf.getStringList("blacklist_channels", default=LinkedList<String>())
|
||||||
|
val max_file_age = sf.getInt("max_file_age", default=-1)
|
||||||
|
|
||||||
|
private fun get_setting_list(name: String): List<String>? {
|
||||||
|
return ini_settings[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun load_ini(filename: String): Map<String, List<String>> {
|
||||||
|
val map = mutableMapOf<String, MutableList<String>>()
|
||||||
|
val file = File(file_base + filename)
|
||||||
|
logger.trace("Checking ini file {}", filename.anonymize())
|
||||||
|
if (!file.exists()) return map
|
||||||
|
logger.debug("Loading ini file {}", filename.anonymize())
|
||||||
|
file.forEachLine { parseLine(it, map) }
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseLine(original_line: String, map: MutableMap<String, MutableList<String>>) {
|
||||||
|
logger.trace("Parsing line: {}", original_line)
|
||||||
|
var line = original_line.trim().replaceAfter("#", "").removeSuffix("#")
|
||||||
|
logger.trace("After cleaning: {}", line)
|
||||||
|
if (line == "") return
|
||||||
|
val parts: List<String> = line.split("=", limit=2).map{it.trim()}
|
||||||
|
|
||||||
|
if (parts.size < 2) throw RuntimeException("Invalid config setting: $line")
|
||||||
|
|
||||||
|
val (key, value) = parts
|
||||||
|
if (value=="") {
|
||||||
|
map.remove(key)
|
||||||
|
} else {
|
||||||
|
var list = map.get(key)
|
||||||
|
if (list == null) {
|
||||||
|
list = mutableListOf<String>()
|
||||||
|
map.put(key, list)
|
||||||
|
}
|
||||||
|
list.add(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copy_sample_ini(filename: String) {
|
||||||
|
val stream = Config::class.java.getResourceAsStream("/config.sample.ini")
|
||||||
|
File(filename).outputStream().use { stream.copyTo(it) }
|
||||||
|
stream.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun print() {
|
||||||
|
println()
|
||||||
|
Setting.all_settings.forEach { it.print() }
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsFactory(val ini: Map<String, List<String>>, val cli: CommandLineOptions) {
|
||||||
|
fun getInt(name: String, default: Int, secret: Boolean = false) = getSetting(name, listOf(default.toString()), secret).get().toInt()
|
||||||
|
fun getBoolean(name: String, default: Boolean, secret: Boolean = false) = getSetting(name, listOf(default.toString()), secret).get().toBoolean()
|
||||||
|
fun getString(name: String, default: String, secret: Boolean = false) = getSetting(name, listOf(default), secret).get()
|
||||||
|
fun getStringList(name: String, default: List<String>, secret: Boolean = false) = getSetting(name, default, secret).getList()
|
||||||
|
|
||||||
|
fun getSetting(name: String, default: List<String>, secret: Boolean) = Setting(ini, cli, name, default, secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Setting(val ini: Map<String, List<String>>, val cli: CommandLineOptions, val name: String, val default: List<String>, val secret: Boolean) {
|
||||||
|
val values: List<String>
|
||||||
|
val source: SettingSource
|
||||||
|
val logger = LoggerFactory.getLogger(Setting::class.java)
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (getCli(name) != null) {
|
||||||
|
values = listOf(getCli(name)!!)
|
||||||
|
source = SettingSource.CLI
|
||||||
|
} else if (getIni(name) != null) {
|
||||||
|
values = getIni(name)!!
|
||||||
|
source = SettingSource.INI
|
||||||
|
} else {
|
||||||
|
values = default
|
||||||
|
source = SettingSource.DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Setting ${name} loaded. Source: ${source}. Value: ${values.toString().anonymize()}")
|
||||||
|
|
||||||
|
all_settings.add(this)
|
||||||
|
}
|
||||||
|
fun get(): String = values.last()
|
||||||
|
fun getList(): List<String> = values
|
||||||
|
|
||||||
|
fun getIni(name: String): List<String>? {
|
||||||
|
return ini[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCli(name: String): String? {
|
||||||
|
return cli.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun print() {
|
||||||
|
println("%-25s %-10s %s".format(name, source, (if (secret && source==SettingSource.DEFAULT) "[REDACTED]" else values)))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val all_settings = LinkedList<Setting>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SettingSource {
|
||||||
|
INI,
|
||||||
|
CLI,
|
||||||
|
DEFAULT
|
||||||
|
}
|
@ -1,52 +0,0 @@
|
|||||||
/* Telegram_Backup
|
|
||||||
* Copyright (C) 2016 Fabian Schlenz
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
package de.fabianonline.telegram_backup
|
|
||||||
|
|
||||||
import com.github.badoualy.telegram.tl.api.*
|
|
||||||
import java.lang.StringBuilder
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
object StickerConverter {
|
|
||||||
fun makeFilenameWithPath(attr: TLDocumentAttributeSticker): String {
|
|
||||||
val file = StringBuilder()
|
|
||||||
file.append(makePath())
|
|
||||||
file.append(makeFilename(attr))
|
|
||||||
return file.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun makeFilename(attr: TLDocumentAttributeSticker): String {
|
|
||||||
val file = StringBuilder()
|
|
||||||
if (attr.getStickerset() is TLInputStickerSetShortName) {
|
|
||||||
file.append((attr.getStickerset() as TLInputStickerSetShortName).getShortName())
|
|
||||||
} else if (attr.getStickerset() is TLInputStickerSetID) {
|
|
||||||
file.append((attr.getStickerset() as TLInputStickerSetID).getId())
|
|
||||||
}
|
|
||||||
file.append("_")
|
|
||||||
file.append(attr.getAlt().hashCode())
|
|
||||||
file.append(".webp")
|
|
||||||
return file.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun makePath(): String {
|
|
||||||
val path = Config.FILE_BASE +
|
|
||||||
File.separatorChar +
|
|
||||||
Config.FILE_STICKER_BASE +
|
|
||||||
File.separatorChar
|
|
||||||
File(path).mkdirs()
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
}
|
|
@ -26,48 +26,39 @@ import de.fabianonline.telegram_backup.Database
|
|||||||
import de.fabianonline.telegram_backup.UserManager
|
import de.fabianonline.telegram_backup.UserManager
|
||||||
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
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
internal class TelegramUpdateHandler : UpdateCallback {
|
internal class TelegramUpdateHandler(val user_manager: UserManager, val db: Database, val file_base: String, val settings: Settings) : UpdateCallback {
|
||||||
private var user: UserManager? = null
|
val logger = LoggerFactory.getLogger(TelegramUpdateHandler::class.java)
|
||||||
private var db: Database? = null
|
|
||||||
var debug = false
|
|
||||||
|
|
||||||
fun activate() {
|
|
||||||
this.user = UserManager.getInstance()
|
|
||||||
this.db = Database.getInstance()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUpdates(client: TelegramClient, updates: TLUpdates) {
|
override fun onUpdates(client: TelegramClient, updates: TLUpdates) {
|
||||||
if (db == null) return
|
|
||||||
if (debug) System.out.println("onUpdates - " + updates.getUpdates().size + " Updates, " + updates.getUsers().size + " Users, " + updates.getChats().size + " Chats")
|
logger.debug("onUpdates - " + updates.getUpdates().size + " Updates, " + updates.getUsers().size + " Users, " + updates.getChats().size + " Chats")
|
||||||
for (update in updates.getUpdates()) {
|
for (update in updates.getUpdates()) {
|
||||||
processUpdate(update, client)
|
processUpdate(update)
|
||||||
if (debug) System.out.println(" " + update.javaClass.getName())
|
logger.debug(" " + update.javaClass.getName())
|
||||||
}
|
}
|
||||||
db!!.saveUsers(updates.getUsers())
|
db.saveUsers(updates.getUsers())
|
||||||
db!!.saveChats(updates.getChats())
|
db.saveChats(updates.getChats())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpdatesCombined(client: TelegramClient, updates: TLUpdatesCombined) {
|
override fun onUpdatesCombined(client: TelegramClient, updates: TLUpdatesCombined) {
|
||||||
if (db == null) return
|
logger.debug("onUpdatesCombined")
|
||||||
if (debug) System.out.println("onUpdatesCombined")
|
|
||||||
for (update in updates.getUpdates()) {
|
for (update in updates.getUpdates()) {
|
||||||
processUpdate(update, client)
|
processUpdate(update)
|
||||||
}
|
}
|
||||||
db!!.saveUsers(updates.getUsers())
|
db.saveUsers(updates.getUsers())
|
||||||
db!!.saveChats(updates.getChats())
|
db.saveChats(updates.getChats())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpdateShort(client: TelegramClient, update: TLUpdateShort) {
|
override fun onUpdateShort(client: TelegramClient, update: TLUpdateShort) {
|
||||||
if (db == null) return
|
logger.debug("onUpdateShort")
|
||||||
if (debug) System.out.println("onUpdateShort")
|
processUpdate(update.getUpdate())
|
||||||
processUpdate(update.getUpdate(), client)
|
logger.debug(" " + update.getUpdate().javaClass.getName())
|
||||||
if (debug) System.out.println(" " + update.getUpdate().javaClass.getName())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShortChatMessage(client: TelegramClient, message: TLUpdateShortChatMessage) {
|
override fun onShortChatMessage(client: TelegramClient, message: TLUpdateShortChatMessage) {
|
||||||
if (db == null) return
|
logger.debug("onShortChatMessage - " + message.getMessage())
|
||||||
if (debug) System.out.println("onShortChatMessage - " + message.getMessage())
|
|
||||||
val msg = TLMessage(
|
val msg = TLMessage(
|
||||||
message.getOut(),
|
message.getOut(),
|
||||||
message.getMentioned(),
|
message.getMentioned(),
|
||||||
@ -85,21 +76,20 @@ internal class TelegramUpdateHandler : UpdateCallback {
|
|||||||
message.getEntities(), null, null)
|
message.getEntities(), null, null)
|
||||||
val vector = TLVector<TLAbsMessage>(TLAbsMessage::class.java)
|
val vector = TLVector<TLAbsMessage>(TLAbsMessage::class.java)
|
||||||
vector.add(msg)
|
vector.add(msg)
|
||||||
db!!.saveMessages(vector, Kotlogram.API_LAYER)
|
db.saveMessages(vector, Kotlogram.API_LAYER, settings=settings)
|
||||||
System.out.print('.')
|
System.out.print('.')
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShortMessage(client: TelegramClient, message: TLUpdateShortMessage) {
|
override fun onShortMessage(client: TelegramClient, message: TLUpdateShortMessage) {
|
||||||
val m = message
|
val m = message
|
||||||
if (db == null) return
|
logger.debug("onShortMessage - " + m.getOut() + " - " + m.getUserId() + " - " + m.getMessage())
|
||||||
if (debug) System.out.println("onShortMessage - " + m.getOut() + " - " + m.getUserId() + " - " + m.getMessage())
|
|
||||||
val from_id: Int
|
val from_id: Int
|
||||||
val to_id: Int
|
val to_id: Int
|
||||||
if (m.getOut() == true) {
|
if (m.getOut() == true) {
|
||||||
from_id = user!!.user!!.getId()
|
from_id = user_manager.id
|
||||||
to_id = m.getUserId()
|
to_id = m.getUserId()
|
||||||
} else {
|
} else {
|
||||||
to_id = user!!.user!!.getId()
|
to_id = user_manager.id
|
||||||
from_id = m.getUserId()
|
from_id = m.getUserId()
|
||||||
}
|
}
|
||||||
val msg = TLMessage(
|
val msg = TLMessage(
|
||||||
@ -119,29 +109,27 @@ internal class TelegramUpdateHandler : UpdateCallback {
|
|||||||
m.getEntities(), null, null)
|
m.getEntities(), null, null)
|
||||||
val vector = TLVector<TLAbsMessage>(TLAbsMessage::class.java)
|
val vector = TLVector<TLAbsMessage>(TLAbsMessage::class.java)
|
||||||
vector.add(msg)
|
vector.add(msg)
|
||||||
db!!.saveMessages(vector, Kotlogram.API_LAYER)
|
db.saveMessages(vector, Kotlogram.API_LAYER, settings=settings)
|
||||||
System.out.print('.')
|
System.out.print('.')
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShortSentMessage(client: TelegramClient, message: TLUpdateShortSentMessage) {
|
override fun onShortSentMessage(client: TelegramClient, message: TLUpdateShortSentMessage) {
|
||||||
if (db == null) return
|
logger.debug("onShortSentMessage")
|
||||||
System.out.println("onShortSentMessage")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpdateTooLong(client: TelegramClient) {
|
override fun onUpdateTooLong(client: TelegramClient) {
|
||||||
if (db == null) return
|
logger.debug("onUpdateTooLong")
|
||||||
System.out.println("onUpdateTooLong")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processUpdate(update: TLAbsUpdate, client: TelegramClient) {
|
private fun processUpdate(update: TLAbsUpdate) {
|
||||||
if (update is TLUpdateNewMessage) {
|
if (update is TLUpdateNewMessage) {
|
||||||
val abs_msg = update.getMessage()
|
val abs_msg = update.getMessage()
|
||||||
val vector = TLVector<TLAbsMessage>(TLAbsMessage::class.java)
|
val vector = TLVector<TLAbsMessage>(TLAbsMessage::class.java)
|
||||||
vector.add(abs_msg)
|
vector.add(abs_msg)
|
||||||
db!!.saveMessages(vector, Kotlogram.API_LAYER)
|
db.saveMessages(vector, Kotlogram.API_LAYER, settings=settings)
|
||||||
System.out.print('.')
|
System.out.print('.')
|
||||||
if (abs_msg is TLMessage && IniSettings.download_media==true) {
|
if (abs_msg is TLMessage && settings.download_media==true) {
|
||||||
val fm = FileManagerFactory.getFileManager(abs_msg, user!!, client)
|
val fm = FileManagerFactory.getFileManager(abs_msg, user_manager, file_base, settings)
|
||||||
if (fm != null && !fm.isEmpty && !fm.downloaded) {
|
if (fm != null && !fm.isEmpty && !fm.downloaded) {
|
||||||
try {
|
try {
|
||||||
fm.download()
|
fm.download()
|
||||||
|
@ -10,7 +10,7 @@ import java.sql.ResultSet
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
internal object TestFeatures {
|
internal class TestFeatures(val db: Database) {
|
||||||
fun test1() {
|
fun test1() {
|
||||||
// Tests entries in a cache4.db in the current working directory for compatibility
|
// Tests entries in a cache4.db in the current working directory for compatibility
|
||||||
try {
|
try {
|
||||||
@ -24,18 +24,13 @@ internal object TestFeatures {
|
|||||||
var conn: Connection
|
var conn: Connection
|
||||||
var stmt: Statement? = null
|
var stmt: Statement? = null
|
||||||
|
|
||||||
try {
|
|
||||||
conn = DriverManager.getConnection(path)
|
conn = DriverManager.getConnection(path)
|
||||||
stmt = conn.createStatement()
|
stmt = conn.createStatement()
|
||||||
} catch (e: SQLException) {
|
|
||||||
CommandLineController.show_error("Could not connect to SQLITE database.")
|
|
||||||
}
|
|
||||||
|
|
||||||
var unsupported_constructor = 0
|
var unsupported_constructor = 0
|
||||||
var success = 0
|
var success = 0
|
||||||
|
|
||||||
try {
|
val rs = stmt.executeQuery("SELECT data FROM messages")
|
||||||
val rs = stmt!!.executeQuery("SELECT data FROM messages")
|
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
try {
|
try {
|
||||||
TLApiContext.getInstance().deserializeMessage(rs.getBytes(1))
|
TLApiContext.getInstance().deserializeMessage(rs.getBytes(1))
|
||||||
@ -44,12 +39,8 @@ internal object TestFeatures {
|
|||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
System.out.println("IOException: " + e)
|
System.out.println("IOException: " + e)
|
||||||
}
|
}
|
||||||
|
|
||||||
success++
|
success++
|
||||||
}
|
}
|
||||||
} catch (e: SQLException) {
|
|
||||||
System.out.println("SQL exception: " + e)
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Success: " + success)
|
System.out.println("Success: " + success)
|
||||||
System.out.println("Unsupported constructor: " + unsupported_constructor)
|
System.out.println("Unsupported constructor: " + unsupported_constructor)
|
||||||
@ -59,7 +50,6 @@ internal object TestFeatures {
|
|||||||
// Prints system.encoding and default charset
|
// Prints system.encoding and default charset
|
||||||
System.out.println("Default Charset: " + Charset.defaultCharset())
|
System.out.println("Default Charset: " + Charset.defaultCharset())
|
||||||
System.out.println("file.encoding: " + System.getProperty("file.encoding"))
|
System.out.println("file.encoding: " + System.getProperty("file.encoding"))
|
||||||
val db = Database.getInstance()
|
|
||||||
System.out.println("Database encoding: " + db.getEncoding())
|
System.out.println("Database encoding: " + db.getEncoding())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,107 +34,34 @@ import java.io.File
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
|
||||||
class UserManager @Throws(IOException::class)
|
class UserManager(val client: TelegramClient) {
|
||||||
private constructor(c: TelegramClient) {
|
val tl_user: TLUser
|
||||||
var user: TLUser? = null
|
val logger = LoggerFactory.getLogger(UserManager::class.java)
|
||||||
var phone: String? = null
|
val phone: String
|
||||||
private var code: String? = null
|
get() = "+" + tl_user.getPhone()
|
||||||
private val client: TelegramClient
|
val id: Int
|
||||||
private var sent_code: TLSentCode? = null
|
get() = tl_user.getId()
|
||||||
private var auth: TLAuthorization? = null
|
|
||||||
var isPasswordNeeded = false
|
|
||||||
private set
|
|
||||||
|
|
||||||
val loggedIn: Boolean
|
init {
|
||||||
get() = user != null
|
logger.debug("Calling getFullUser")
|
||||||
|
val full_user = client.usersGetFullUser(TLInputUserSelf())
|
||||||
|
tl_user = full_user.getUser().getAsUser()
|
||||||
|
}
|
||||||
|
|
||||||
val userString: String
|
override fun toString(): String {
|
||||||
get() {
|
|
||||||
if (this.user == null) return "Not logged in"
|
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
if (this.user!!.getFirstName() != null) {
|
sb.append(tl_user.getFirstName() ?: "")
|
||||||
sb.append(this.user!!.getFirstName())
|
if (tl_user.getLastName() != null) {
|
||||||
}
|
|
||||||
if (this.user!!.getLastName() != null) {
|
|
||||||
sb.append(" ")
|
sb.append(" ")
|
||||||
sb.append(this.user!!.getLastName())
|
sb.append(tl_user.getLastName())
|
||||||
}
|
}
|
||||||
if (this.user!!.getUsername() != null) {
|
if (tl_user.getUsername() != null) {
|
||||||
sb.append(" (@")
|
sb.append(" (@")
|
||||||
sb.append(this.user!!.getUsername())
|
sb.append(tl_user.getUsername())
|
||||||
sb.append(")")
|
sb.append(")")
|
||||||
}
|
}
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
val fileBase: String
|
|
||||||
get() = Config.FILE_BASE + File.separatorChar + "+" + this.user!!.getPhone() + File.separatorChar
|
|
||||||
|
|
||||||
init {
|
|
||||||
this.client = c
|
|
||||||
logger.debug("Calling getFullUser")
|
|
||||||
try {
|
|
||||||
val full_user = this.client.usersGetFullUser(TLInputUserSelf())
|
|
||||||
this.user = full_user.getUser().getAsUser()
|
|
||||||
} catch (e: RpcErrorException) {
|
|
||||||
// This may happen. Ignoring it.
|
|
||||||
logger.debug("Ignoring exception:", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
|
||||||
fun sendCodeToPhoneNumber(number: String) {
|
|
||||||
this.phone = number
|
|
||||||
this.sent_code = this.client.authSendCode(false, number, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
|
||||||
fun verifyCode(code: String) {
|
|
||||||
this.code = code
|
|
||||||
try {
|
|
||||||
this.auth = client.authSignIn(phone, this.sent_code!!.getPhoneCodeHash(), this.code)
|
|
||||||
this.user = auth!!.getUser().getAsUser()
|
|
||||||
} catch (e: RpcErrorException) {
|
|
||||||
if (e.getCode() != 401 || !e.getTag().equals("SESSION_PASSWORD_NEEDED")) throw e
|
|
||||||
this.isPasswordNeeded = true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
|
||||||
fun verifyPassword(pw: String) {
|
|
||||||
val password = pw.toByteArray(charset = Charsets.UTF_8)
|
|
||||||
val salt = (client.accountGetPassword() as TLPassword).getCurrentSalt().getData()
|
|
||||||
var md: MessageDigest
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("SHA-256")
|
|
||||||
} catch (e: NoSuchAlgorithmException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val salted = ByteArray(2 * salt.size + password.size)
|
|
||||||
System.arraycopy(salt, 0, salted, 0, salt.size)
|
|
||||||
System.arraycopy(password, 0, salted, salt.size, password.size)
|
|
||||||
System.arraycopy(salt, 0, salted, salt.size + password.size, salt.size)
|
|
||||||
val hash = md.digest(salted)
|
|
||||||
auth = client.authCheckPassword(TLBytes(hash))
|
|
||||||
this.user = auth!!.getUser().getAsUser()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(UserManager::class.java)
|
|
||||||
internal var instance: UserManager? = null
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun init(c: TelegramClient) {
|
|
||||||
instance = UserManager(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getInstance(): UserManager {
|
|
||||||
if (instance == null) throw RuntimeException("UserManager is not yet initialized.")
|
|
||||||
return instance!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -20,6 +20,7 @@ import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
|||||||
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 com.google.gson.*
|
import com.google.gson.*
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
@ -32,11 +33,29 @@ object Utils {
|
|||||||
@JvmField public val VERSION_1_NEWER = 1
|
@JvmField public val VERSION_1_NEWER = 1
|
||||||
@JvmField public val VERSION_2_NEWER = 2
|
@JvmField public val VERSION_2_NEWER = 2
|
||||||
|
|
||||||
|
var hasSeenFloodWaitMessage = false
|
||||||
|
|
||||||
|
var anonymize = false
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(Utils::class.java) as Logger
|
private val logger = LoggerFactory.getLogger(Utils::class.java) as Logger
|
||||||
|
|
||||||
fun getAccounts(): Vector<String> {
|
fun print_accounts(file_base: String) {
|
||||||
|
println("List of available accounts:")
|
||||||
|
val accounts = getAccounts(file_base)
|
||||||
|
if (accounts.size > 0) {
|
||||||
|
for (str in accounts) {
|
||||||
|
println(" " + str.anonymize())
|
||||||
|
}
|
||||||
|
println("Use '--account <x>' to use one of those accounts.")
|
||||||
|
} else {
|
||||||
|
println("NO ACCOUNTS FOUND")
|
||||||
|
println("Use '--login' to login to a telegram account.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAccounts(file_base: String): Vector<String> {
|
||||||
val accounts = Vector<String>()
|
val accounts = Vector<String>()
|
||||||
val folder = File(Config.FILE_BASE)
|
val folder = File(file_base)
|
||||||
val files = folder.listFiles()
|
val files = folder.listFiles()
|
||||||
if (files != null)
|
if (files != null)
|
||||||
for (f in files) {
|
for (f in files) {
|
||||||
@ -81,29 +100,47 @@ object Utils {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class)
|
fun obeyFloodWait(max_tries: Int = -1, method: () -> Unit) {
|
||||||
@JvmOverloads internal fun obeyFloodWaitException(e: RpcErrorException?, silent: Boolean = false) {
|
var tries = 0
|
||||||
if (e == null || e.getCode() != 420) return
|
while (true) {
|
||||||
|
tries++
|
||||||
|
if (max_tries>0 && tries>max_tries) throw MaxTriesExceededException()
|
||||||
|
logger.trace("This is try ${tries}.")
|
||||||
|
try {
|
||||||
|
method.invoke()
|
||||||
|
// If we reach this, the method has returned successfully -> we are done
|
||||||
|
return
|
||||||
|
} catch(e: RpcErrorException) {
|
||||||
|
// If we got something else than a FLOOD_WAIT error, we just rethrow it
|
||||||
|
if (e.getCode() != 420) throw e
|
||||||
|
|
||||||
val delay: Long = e.getTagInteger()!!.toLong()
|
val delay = e.getTagInteger()!!.toLong()
|
||||||
if (!silent) {
|
|
||||||
System.out.println("")
|
if (!hasSeenFloodWaitMessage) {
|
||||||
System.out.println(
|
println(
|
||||||
|
"\n" +
|
||||||
"Telegram complained about us (okay, me) making too many requests in too short time by\n" +
|
"Telegram complained about us (okay, me) making too many requests in too short time by\n" +
|
||||||
"sending us \"" + e.getTag() + "\" as an error. So we now have to wait a bit. Telegram\n" +
|
"sending us \"${e.getTag()}\" as an error. So we now have to wait a bit. Telegram\n" +
|
||||||
"asked us to wait for " + delay + " seconds.\n" +
|
"asked us to wait for ${delay} seconds.\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"So I'm going to do just that for now. If you don't want to wait, you can quit by pressing\n" +
|
"So I'm going to do just that for now. If you don't want to wait, you can quit by pressing\n" +
|
||||||
"Ctrl+C. You can restart me at any time and I will just continue to download your\n" +
|
"Ctrl+C. You can restart me at any time and I will just continue to download your\n" +
|
||||||
"messages and media. But be advised that just restarting me is not going to change\n" +
|
"messages and media. But be advised that just restarting me is not going to change\n" +
|
||||||
"the fact that Telegram won't talk to me until then.")
|
"the fact that Telegram won't talk to me until then." +
|
||||||
System.out.println("")
|
"\n")
|
||||||
}
|
|
||||||
try {
|
|
||||||
TimeUnit.SECONDS.sleep(delay + 1)
|
|
||||||
} catch (e2: InterruptedException) {
|
|
||||||
}
|
}
|
||||||
|
hasSeenFloodWaitMessage = true
|
||||||
|
|
||||||
|
try { TimeUnit.SECONDS.sleep(delay + 1) } catch (e: InterruptedException) { }
|
||||||
|
} catch (e: TimeoutException) {
|
||||||
|
println(
|
||||||
|
"\n" +
|
||||||
|
"Telegram took too long to respond to our request.\n" +
|
||||||
|
"I'm going to wait a minute and then try again." +
|
||||||
|
"\n")
|
||||||
|
try { TimeUnit.MINUTES.sleep(1) } catch (e: InterruptedException) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@ -179,8 +216,10 @@ object Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun String.anonymize(): String {
|
fun String.anonymize(): String {
|
||||||
return if (!CommandLineOptions.cmd_anonymize) this else this.replace(Regex("[0-9]"), "1").replace(Regex("[A-Z]"), "A").replace(Regex("[a-z]"), "a") + " (ANONYMIZED)"
|
return if (!Utils.anonymize) this else this.replace(Regex("[0-9]"), "1").replace(Regex("[A-Z]"), "A").replace(Regex("[a-z]"), "a") + " (ANONYMIZED)"
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
class MaxTriesExceededException(): RuntimeException("Max tries exceeded") {}
|
||||||
|
@ -37,18 +37,15 @@ import de.fabianonline.telegram_backup.*
|
|||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
class HTMLExporter {
|
class HTMLExporter(val db: Database, val user: UserManager, val settings: Settings, val file_base: String) {
|
||||||
val db = Database.getInstance()
|
|
||||||
val user = UserManager.getInstance()
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun export() {
|
fun export() {
|
||||||
try {
|
try {
|
||||||
val pagination = if (IniSettings.pagination) IniSettings.pagination_size else -1
|
val pagination = if (settings.pagination) settings.pagination_size else -1
|
||||||
|
|
||||||
// Create base dir
|
// Create base dir
|
||||||
logger.debug("Creating base dir")
|
logger.debug("Creating base dir")
|
||||||
val base = user.fileBase + "files" + File.separatorChar
|
val base = file_base + "files" + File.separatorChar
|
||||||
File(base).mkdirs()
|
File(base).mkdirs()
|
||||||
File(base + "dialogs").mkdirs()
|
File(base + "dialogs").mkdirs()
|
||||||
|
|
||||||
|
@ -17,33 +17,17 @@
|
|||||||
package de.fabianonline.telegram_backup.mediafilemanager
|
package de.fabianonline.telegram_backup.mediafilemanager
|
||||||
|
|
||||||
import de.fabianonline.telegram_backup.UserManager
|
import de.fabianonline.telegram_backup.UserManager
|
||||||
import de.fabianonline.telegram_backup.Database
|
|
||||||
import de.fabianonline.telegram_backup.StickerConverter
|
|
||||||
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
|
||||||
import de.fabianonline.telegram_backup.Config
|
import de.fabianonline.telegram_backup.Config
|
||||||
import de.fabianonline.telegram_backup.DownloadManager
|
|
||||||
|
|
||||||
import com.github.badoualy.telegram.api.TelegramClient
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLIntVector
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLObject
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs
|
|
||||||
import com.github.badoualy.telegram.tl.api.*
|
import com.github.badoualy.telegram.tl.api.*
|
||||||
import com.github.badoualy.telegram.tl.api.upload.TLFile
|
|
||||||
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
||||||
import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile
|
import de.fabianonline.telegram_backup.Settings
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.LinkedList
|
|
||||||
import java.net.URL
|
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
abstract class AbstractMediaFileManager(protected var message: TLMessage, protected var user: UserManager, val file_base: String) {
|
||||||
|
|
||||||
abstract class AbstractMediaFileManager(protected var message: TLMessage, protected var user: UserManager, protected var client: TelegramClient) {
|
|
||||||
open var isEmpty = false
|
open var isEmpty = false
|
||||||
abstract val size: Int
|
abstract val size: Int
|
||||||
abstract val extension: String
|
abstract val extension: String
|
||||||
@ -56,7 +40,7 @@ abstract class AbstractMediaFileManager(protected var message: TLMessage, protec
|
|||||||
|
|
||||||
open val targetPath: String
|
open val targetPath: String
|
||||||
get() {
|
get() {
|
||||||
val path = user.fileBase + Config.FILE_FILES_BASE + File.separatorChar
|
val path = file_base + Config.FILE_FILES_BASE + File.separatorChar
|
||||||
File(path).mkdirs()
|
File(path).mkdirs()
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
@ -17,32 +17,15 @@
|
|||||||
package de.fabianonline.telegram_backup.mediafilemanager
|
package de.fabianonline.telegram_backup.mediafilemanager
|
||||||
|
|
||||||
import de.fabianonline.telegram_backup.UserManager
|
import de.fabianonline.telegram_backup.UserManager
|
||||||
import de.fabianonline.telegram_backup.Database
|
|
||||||
import de.fabianonline.telegram_backup.StickerConverter
|
|
||||||
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
|
||||||
import de.fabianonline.telegram_backup.DownloadManager
|
import de.fabianonline.telegram_backup.DownloadManager
|
||||||
|
|
||||||
import com.github.badoualy.telegram.api.TelegramClient
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLIntVector
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLObject
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs
|
|
||||||
import com.github.badoualy.telegram.tl.api.*
|
import com.github.badoualy.telegram.tl.api.*
|
||||||
import com.github.badoualy.telegram.tl.api.upload.TLFile
|
|
||||||
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
||||||
import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile
|
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.LinkedList
|
|
||||||
import java.net.URL
|
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
open class DocumentFileManager(msg: TLMessage, user: UserManager, file_base: String) : AbstractMediaFileManager(msg, user, file_base) {
|
||||||
|
|
||||||
open class DocumentFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : AbstractMediaFileManager(msg, user, client) {
|
|
||||||
protected var doc: TLDocument? = null
|
protected var doc: TLDocument? = null
|
||||||
override lateinit var extension: String
|
override lateinit var extension: String
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ package de.fabianonline.telegram_backup.mediafilemanager
|
|||||||
|
|
||||||
import de.fabianonline.telegram_backup.UserManager
|
import de.fabianonline.telegram_backup.UserManager
|
||||||
import de.fabianonline.telegram_backup.Database
|
import de.fabianonline.telegram_backup.Database
|
||||||
import de.fabianonline.telegram_backup.StickerConverter
|
|
||||||
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
||||||
|
|
||||||
import com.github.badoualy.telegram.api.TelegramClient
|
import com.github.badoualy.telegram.api.TelegramClient
|
||||||
@ -30,6 +29,7 @@ import com.github.badoualy.telegram.tl.api.*
|
|||||||
import com.github.badoualy.telegram.tl.api.upload.TLFile
|
import com.github.badoualy.telegram.tl.api.upload.TLFile
|
||||||
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
||||||
import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile
|
import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile
|
||||||
|
import de.fabianonline.telegram_backup.Settings
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -42,29 +42,29 @@ import java.util.concurrent.TimeoutException
|
|||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
object FileManagerFactory {
|
object FileManagerFactory {
|
||||||
fun getFileManager(m: TLMessage?, u: UserManager, c: TelegramClient): AbstractMediaFileManager? {
|
fun getFileManager(m: TLMessage?, u: UserManager, file_base: String, settings: Settings?): AbstractMediaFileManager? {
|
||||||
if (m == null) return null
|
if (m == null) return null
|
||||||
val media = m.getMedia() ?: return null
|
val media = m.getMedia() ?: return null
|
||||||
|
|
||||||
if (media is TLMessageMediaPhoto) {
|
if (media is TLMessageMediaPhoto) {
|
||||||
return PhotoFileManager(m, u, c)
|
return PhotoFileManager(m, u, file_base)
|
||||||
} else if (media is TLMessageMediaDocument) {
|
} else if (media is TLMessageMediaDocument) {
|
||||||
val d = DocumentFileManager(m, u, c)
|
val d = DocumentFileManager(m, u, file_base)
|
||||||
return if (d.isSticker) {
|
return if (d.isSticker) {
|
||||||
StickerFileManager(m, u, c)
|
StickerFileManager(m, u, file_base)
|
||||||
} else d
|
} else d
|
||||||
} else if (media is TLMessageMediaGeo) {
|
} else if (media is TLMessageMediaGeo) {
|
||||||
return GeoFileManager(m, u, c)
|
return GeoFileManager(m, u, file_base, settings)
|
||||||
} else if (media is TLMessageMediaEmpty) {
|
} else if (media is TLMessageMediaEmpty) {
|
||||||
return UnsupportedFileManager(m, u, c, "empty")
|
return UnsupportedFileManager(m, u, file_base, "empty")
|
||||||
} else if (media is TLMessageMediaUnsupported) {
|
} else if (media is TLMessageMediaUnsupported) {
|
||||||
return UnsupportedFileManager(m, u, c, "unsupported")
|
return UnsupportedFileManager(m, u, file_base, "unsupported")
|
||||||
} else if (media is TLMessageMediaWebPage) {
|
} else if (media is TLMessageMediaWebPage) {
|
||||||
return UnsupportedFileManager(m, u, c, "webpage")
|
return UnsupportedFileManager(m, u, file_base, "webpage")
|
||||||
} else if (media is TLMessageMediaContact) {
|
} else if (media is TLMessageMediaContact) {
|
||||||
return UnsupportedFileManager(m, u, c, "contact")
|
return UnsupportedFileManager(m, u, file_base, "contact")
|
||||||
} else if (media is TLMessageMediaVenue) {
|
} else if (media is TLMessageMediaVenue) {
|
||||||
return UnsupportedFileManager(m, u, c, "venue")
|
return UnsupportedFileManager(m, u, file_base, "venue")
|
||||||
} else {
|
} else {
|
||||||
AbstractMediaFileManager.throwUnexpectedObjectError(media)
|
AbstractMediaFileManager.throwUnexpectedObjectError(media)
|
||||||
}
|
}
|
||||||
|
@ -17,33 +17,16 @@
|
|||||||
package de.fabianonline.telegram_backup.mediafilemanager
|
package de.fabianonline.telegram_backup.mediafilemanager
|
||||||
|
|
||||||
import de.fabianonline.telegram_backup.UserManager
|
import de.fabianonline.telegram_backup.UserManager
|
||||||
import de.fabianonline.telegram_backup.Database
|
|
||||||
import de.fabianonline.telegram_backup.StickerConverter
|
|
||||||
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
|
||||||
import de.fabianonline.telegram_backup.DownloadManager
|
import de.fabianonline.telegram_backup.DownloadManager
|
||||||
import de.fabianonline.telegram_backup.IniSettings
|
|
||||||
|
|
||||||
import com.github.badoualy.telegram.api.TelegramClient
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLIntVector
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLObject
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs
|
|
||||||
import com.github.badoualy.telegram.tl.api.*
|
import com.github.badoualy.telegram.tl.api.*
|
||||||
import com.github.badoualy.telegram.tl.api.upload.TLFile
|
import de.fabianonline.telegram_backup.Config
|
||||||
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
import de.fabianonline.telegram_backup.Settings
|
||||||
import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile
|
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.LinkedList
|
|
||||||
import java.net.URL
|
|
||||||
import java.util.concurrent.TimeoutException
|
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
class GeoFileManager(msg: TLMessage, user: UserManager, file_base: String, val settings: Settings?) : AbstractMediaFileManager(msg, user, file_base) {
|
||||||
|
|
||||||
class GeoFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : AbstractMediaFileManager(msg, user, client) {
|
|
||||||
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.
|
||||||
@ -77,7 +60,7 @@ class GeoFileManager(msg: TLMessage, user: UserManager, client: TelegramClient)
|
|||||||
"center=${geo.getLat()},${geo.getLong()}&" +
|
"center=${geo.getLat()},${geo.getLong()}&" +
|
||||||
"markers=color:red|${geo.getLat()},${geo.getLong()}&" +
|
"markers=color:red|${geo.getLat()},${geo.getLong()}&" +
|
||||||
"zoom=14&size=300x150&scale=2&format=png&" +
|
"zoom=14&size=300x150&scale=2&format=png&" +
|
||||||
"key=" + IniSettings.gmaps_key
|
"key=" + (settings?.gmaps_key ?: Config.SECRET_GMAPS)
|
||||||
return DownloadManager.downloadExternalFile(targetPathAndFilename, url)
|
return DownloadManager.downloadExternalFile(targetPathAndFilename, url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,32 +17,15 @@
|
|||||||
package de.fabianonline.telegram_backup.mediafilemanager
|
package de.fabianonline.telegram_backup.mediafilemanager
|
||||||
|
|
||||||
import de.fabianonline.telegram_backup.UserManager
|
import de.fabianonline.telegram_backup.UserManager
|
||||||
import de.fabianonline.telegram_backup.Database
|
|
||||||
import de.fabianonline.telegram_backup.StickerConverter
|
|
||||||
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
|
||||||
import de.fabianonline.telegram_backup.DownloadManager
|
import de.fabianonline.telegram_backup.DownloadManager
|
||||||
|
|
||||||
import com.github.badoualy.telegram.api.TelegramClient
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLIntVector
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLObject
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs
|
|
||||||
import com.github.badoualy.telegram.tl.api.*
|
import com.github.badoualy.telegram.tl.api.*
|
||||||
import com.github.badoualy.telegram.tl.api.upload.TLFile
|
|
||||||
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
||||||
import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile
|
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.LinkedList
|
|
||||||
import java.net.URL
|
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
class PhotoFileManager(msg: TLMessage, user: UserManager, file_base: String) : AbstractMediaFileManager(msg, user, file_base) {
|
||||||
|
|
||||||
class PhotoFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : AbstractMediaFileManager(msg, user, client) {
|
|
||||||
private lateinit var photo: TLPhoto
|
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
|
||||||
|
@ -18,7 +18,6 @@ package de.fabianonline.telegram_backup.mediafilemanager
|
|||||||
|
|
||||||
import de.fabianonline.telegram_backup.UserManager
|
import de.fabianonline.telegram_backup.UserManager
|
||||||
import de.fabianonline.telegram_backup.Database
|
import de.fabianonline.telegram_backup.Database
|
||||||
import de.fabianonline.telegram_backup.StickerConverter
|
|
||||||
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
||||||
import de.fabianonline.telegram_backup.DownloadManager
|
import de.fabianonline.telegram_backup.DownloadManager
|
||||||
import de.fabianonline.telegram_backup.Config
|
import de.fabianonline.telegram_backup.Config
|
||||||
@ -50,7 +49,7 @@ import java.util.concurrent.TimeoutException
|
|||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
class StickerFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : DocumentFileManager(msg, user, client) {
|
class StickerFileManager(msg: TLMessage, user: UserManager, file_base: String) : DocumentFileManager(msg, user, file_base) {
|
||||||
|
|
||||||
override val isSticker = true
|
override val isSticker = true
|
||||||
|
|
||||||
@ -80,7 +79,7 @@ class StickerFileManager(msg: TLMessage, user: UserManager, client: TelegramClie
|
|||||||
|
|
||||||
override val targetPath: String
|
override val targetPath: String
|
||||||
get() {
|
get() {
|
||||||
val path = user.fileBase + Config.FILE_FILES_BASE + File.separatorChar + Config.FILE_STICKER_BASE + File.separatorChar
|
val path = file_base + Config.FILE_FILES_BASE + File.separatorChar + Config.FILE_STICKER_BASE + File.separatorChar
|
||||||
File(path).mkdirs()
|
File(path).mkdirs()
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
@ -94,19 +93,6 @@ class StickerFileManager(msg: TLMessage, user: UserManager, client: TelegramClie
|
|||||||
override val description: String
|
override val description: String
|
||||||
get() = "Sticker"
|
get() = "Sticker"
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
|
||||||
override fun download(): Boolean {
|
|
||||||
val old_file = Config.FILE_BASE + File.separatorChar + Config.FILE_STICKER_BASE + File.separatorChar + targetFilename
|
|
||||||
|
|
||||||
logger.trace("Old filename exists: {}", File(old_file).exists())
|
|
||||||
|
|
||||||
if (File(old_file).exists()) {
|
|
||||||
Files.copy(Paths.get(old_file), Paths.get(targetPathAndFilename), StandardCopyOption.REPLACE_EXISTING)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return super.download()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(StickerFileManager::class.java)
|
private val logger = LoggerFactory.getLogger(StickerFileManager::class.java)
|
||||||
}
|
}
|
||||||
|
@ -17,33 +17,10 @@
|
|||||||
package de.fabianonline.telegram_backup.mediafilemanager
|
package de.fabianonline.telegram_backup.mediafilemanager
|
||||||
|
|
||||||
import de.fabianonline.telegram_backup.UserManager
|
import de.fabianonline.telegram_backup.UserManager
|
||||||
import de.fabianonline.telegram_backup.Database
|
|
||||||
import de.fabianonline.telegram_backup.StickerConverter
|
|
||||||
import de.fabianonline.telegram_backup.DownloadProgressInterface
|
|
||||||
import de.fabianonline.telegram_backup.DownloadManager
|
|
||||||
import de.fabianonline.telegram_backup.Config
|
|
||||||
|
|
||||||
import com.github.badoualy.telegram.api.TelegramClient
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLIntVector
|
|
||||||
import com.github.badoualy.telegram.tl.core.TLObject
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages
|
|
||||||
import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs
|
|
||||||
import com.github.badoualy.telegram.tl.api.*
|
import com.github.badoualy.telegram.tl.api.*
|
||||||
import com.github.badoualy.telegram.tl.api.upload.TLFile
|
|
||||||
import com.github.badoualy.telegram.tl.exception.RpcErrorException
|
|
||||||
import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile
|
|
||||||
|
|
||||||
import java.io.IOException
|
class UnsupportedFileManager(msg: TLMessage, user: UserManager, type: String, file_base: String) : AbstractMediaFileManager(msg, user, file_base) {
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.LinkedList
|
|
||||||
import java.net.URL
|
|
||||||
import java.util.concurrent.TimeoutException
|
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
|
||||||
|
|
||||||
class UnsupportedFileManager(msg: TLMessage, user: UserManager, client: TelegramClient, type: String) : AbstractMediaFileManager(msg, user, client) {
|
|
||||||
override var name = type
|
override var name = type
|
||||||
override val targetFilename = ""
|
override val targetFilename = ""
|
||||||
override val targetPath = ""
|
override val targetPath = ""
|
||||||
|
Loading…
Reference in New Issue
Block a user