mirror of
https://github.com/fabianonline/telegram_backup.git
synced 2024-11-22 08:46:15 +00:00
WIP: Lots and lots of rewriting.
This commit is contained in:
parent
2d409352bc
commit
eea08a5559
@ -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
|
try {
|
||||||
if (this.file_auth_key != null) {
|
return AuthKey(FileUtils.readFileToByteArray(file_auth_key))
|
||||||
try {
|
} catch (e: FileNotFoundException) {
|
||||||
return AuthKey(FileUtils.readFileToByteArray(this.file_auth_key))
|
return null
|
||||||
} catch (e: IOException) {
|
|
||||||
if (e !is FileNotFoundException) e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
try {
|
||||||
if (this.file_dc != null) {
|
val infos = FileUtils.readFileToString(this.file_dc).split(":")
|
||||||
try {
|
return DataCenter(infos[0], Integer.parseInt(infos[1]))
|
||||||
val infos = FileUtils.readFileToString(this.file_dc).split(":")
|
} catch (e: FileNotFoundException) {
|
||||||
return DataCenter(infos[0], Integer.parseInt(infos[1]))
|
return null
|
||||||
} catch (e: IOException) {
|
|
||||||
if (e !is FileNotFoundException) e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteAuthKey() {
|
override fun deleteAuthKey() {
|
||||||
if (this.do_save) {
|
try {
|
||||||
try {
|
FileUtils.forceDelete(file_auth_key)
|
||||||
FileUtils.forceDelete(this.file_auth_key)
|
} catch (e: IOException) {
|
||||||
} catch (e: IOException) {
|
logger.warn("Exception in deleteAuthKey(): {}", e)
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteDc() {
|
override fun deleteDc() {
|
||||||
if (this.do_save) {
|
try {
|
||||||
try {
|
FileUtils.forceDelete(file_dc)
|
||||||
FileUtils.forceDelete(this.file_dc)
|
} catch (e: IOException) {
|
||||||
} catch (e: IOException) {
|
logger.warn("Exception in deleteDc(): {}", e)
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,149 +29,136 @@ 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 app: TelegramApp
|
|
||||||
val target_dir: String
|
|
||||||
val file_base: String
|
|
||||||
val phone_number: String
|
|
||||||
val account_file_base: String
|
|
||||||
|
|
||||||
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
|
||||||
|
val client: TelegramClient
|
||||||
|
val user_manager: UserManager
|
||||||
|
val inisettings: IniSettings
|
||||||
|
val database: Database
|
||||||
logger.info("CommandLineController started. App version {}", Config.APP_APPVER)
|
logger.info("CommandLineController started. App version {}", Config.APP_APPVER)
|
||||||
|
|
||||||
printHeader()
|
printHeader()
|
||||||
if (CommandLineOptions.cmd_version) {
|
if (options.cmd_version) {
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
} else if (CommandLineOptions.cmd_help) {
|
} else if (options.cmd_help) {
|
||||||
show_help()
|
show_help()
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
} else if (CommandLineOptions.cmd_license) {
|
} else if (options.cmd_license) {
|
||||||
show_license()
|
show_license()
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup 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)
|
||||||
|
|
||||||
// Setup file_base
|
// Setup file_base
|
||||||
logger.debug("Target dir from Config: {}", Config.TARGET_DIR.anonymize())
|
logger.debug("Target dir from Config: {}", Config.TARGET_DIR.anonymize())
|
||||||
target_dir = CommandLineOptions.val_target ?: Config.TARGET_DIR
|
target_dir = options.val_target ?: Config.TARGET_DIR
|
||||||
logger.debug("Target dir after options: {}", target_dir)
|
logger.debug("Target dir after options: {}", target_dir)
|
||||||
println("Base directory for files: ${target_dir.anonymize()}")
|
println("Base directory for files: ${target_dir.anonymize()}")
|
||||||
|
|
||||||
if (CommandLineOptions.cmd_list_accounts) {
|
if (options.cmd_list_accounts) {
|
||||||
Utils.print_accounts(target_dir)
|
Utils.print_accounts(target_dir)
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CommandLineOptions.cmd_login) {
|
if (options.cmd_login) {
|
||||||
cmd_login(target_dir, CommandLineOptions.val_account)
|
cmd_login(app, target_dir, options.val_account)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.trace("Checking accounts")
|
logger.trace("Checking accounts")
|
||||||
try {
|
phone_number = try { selectAccount(target_dir, options.val_account)
|
||||||
phone_number = selectAccount(target_dir, CommandLineOptions.val_account)
|
|
||||||
} catch(e: AccountNotFoundException) {
|
} catch(e: AccountNotFoundException) {
|
||||||
show_error("The specified account could not be found.")
|
show_error("The specified account could not be found.")
|
||||||
} catch(e: NoAccountsException) {
|
} catch(e: NoAccountsException) {
|
||||||
println("No accounts found. Starting login process...")
|
println("No accounts found. Starting login process...")
|
||||||
cmd_login(target_dir, CommandLineOptions.val_account)
|
cmd_login(app, target_dir, options.val_account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Create a new TelegramApp if the user set his/her own TelegramApp credentials
|
||||||
|
|
||||||
file_base = target_dir + File.separatorChar + account_to_use
|
// 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)
|
||||||
|
|
||||||
account = Account(file_base, account_to_use)
|
|
||||||
|
|
||||||
logger.debug("Initializing TelegramApp")
|
|
||||||
app = TelegramApp(Config.APP_ID, Config.APP_HASH, Config.APP_MODEL, Config.APP_SYSVER, Config.APP_APPVER, Config.APP_LANG)
|
|
||||||
|
|
||||||
logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
|
|
||||||
logger.info("Initializing ApiStorage")
|
logger.info("Initializing ApiStorage")
|
||||||
storage = ApiStorage(account)
|
storage = ApiStorage(file_base)
|
||||||
|
|
||||||
logger.info("Initializing TelegramUpdateHandler")
|
logger.info("Initializing TelegramUpdateHandler")
|
||||||
val handler = TelegramUpdateHandler()
|
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, handler)
|
||||||
|
|
||||||
|
// 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.
|
// Load the ini file.
|
||||||
IniSettings.load()
|
inisettings = IniSettings(file_base)
|
||||||
|
|
||||||
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()
|
if (options.cmd_stats) {
|
||||||
|
cmd_stats(file_base, database)
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
}
|
}
|
||||||
if (CommandLineOptions.val_test != null) {
|
|
||||||
if (CommandLineOptions.val_test == 1) {
|
if (options.val_test != null) {
|
||||||
|
if (options.val_test == 1) {
|
||||||
TestFeatures.test1()
|
TestFeatures.test1()
|
||||||
} else if (CommandLineOptions.val_test == 2) {
|
} else if (options.val_test == 2) {
|
||||||
TestFeatures.test2()
|
TestFeatures.test2()
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Unknown test " + CommandLineOptions.val_test)
|
System.out.println("Unknown test " + options.val_test)
|
||||||
}
|
}
|
||||||
System.exit(1)
|
System.exit(1)
|
||||||
}
|
}
|
||||||
logger.debug("CommandLineOptions.val_export: {}", CommandLineOptions.val_export)
|
|
||||||
if (CommandLineOptions.val_export != null) {
|
val export = options.val_export
|
||||||
if (CommandLineOptions.val_export!!.toLowerCase().equals("html")) {
|
logger.debug("options.val_export: {}", export)
|
||||||
(HTMLExporter()).export()
|
if (export != null) {
|
||||||
|
if (export.toLowerCase().equals("html")) {
|
||||||
|
HTMLExporter().export()
|
||||||
System.exit(0)
|
System.exit(0)
|
||||||
} else {
|
} else {
|
||||||
show_error("Unknown export format.")
|
show_error("Unknown export format '${export}'.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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) {
|
println("You are logged in as ${user_manager.toString().anonymize()}")
|
||||||
|
|
||||||
|
logger.info("Initializing Download Manager")
|
||||||
|
val d = DownloadManager(client, CommandLineDownloadProgress(), database, user_manager, inisettings)
|
||||||
|
|
||||||
|
if (options.cmd_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 = inisettings.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) {
|
||||||
@ -179,7 +166,7 @@ class CommandLineController {
|
|||||||
}
|
}
|
||||||
println()
|
println()
|
||||||
println("Supergroups:")
|
println("Supergroups:")
|
||||||
download = IniSettings.download_supergroups
|
download = inisettings.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) {
|
||||||
@ -188,10 +175,10 @@ 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.val_limit_messages)
|
||||||
d.downloadMessages(CommandLineOptions.val_limit_messages)
|
d.downloadMessages(options.val_limit_messages)
|
||||||
logger.debug("IniSettings.download_media: {}", IniSettings.download_media)
|
logger.debug("IniSettings#download_media: {}", inisettings.download_media)
|
||||||
if (IniSettings.download_media) {
|
if (inisettings.download_media) {
|
||||||
logger.debug("Calling DownloadManager.downloadMedia")
|
logger.debug("Calling DownloadManager.downloadMedia")
|
||||||
d.downloadMedia()
|
d.downloadMedia()
|
||||||
} else {
|
} else {
|
||||||
@ -202,9 +189,9 @@ class CommandLineController {
|
|||||||
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.
|
// If we encountered an exception, we definitely don't want to start the daemon mode now.
|
||||||
CommandLineOptions.cmd_daemon = false
|
options.cmd_daemon = false
|
||||||
} finally {
|
} finally {
|
||||||
if (CommandLineOptions.cmd_daemon) {
|
if (options.cmd_daemon) {
|
||||||
handler.activate()
|
handler.activate()
|
||||||
println("DAEMON mode requested - keeping running.")
|
println("DAEMON mode requested - keeping running.")
|
||||||
} else {
|
} else {
|
||||||
@ -225,79 +212,63 @@ class CommandLineController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun selectAccount(file_base: String, requested_account: String?): String {
|
private fun selectAccount(file_base: String, requested_account: String?): String {
|
||||||
val found_account: String?
|
var found_account: String? = null
|
||||||
val accounts = Utils.getAccounts(file_base)
|
val accounts = Utils.getAccounts(file_base)
|
||||||
if (requested_account != null) {
|
if (requested_account != null) {
|
||||||
logger.debug("Account requested: {}", requested_account.anonymize())
|
logger.debug("Account requested: {}", requested_account.anonymize())
|
||||||
logger.trace("Checking accounts for match.")
|
logger.trace("Checking accounts for match.")
|
||||||
found_account = accounts.find{it == requested_account}
|
found_account = accounts.find{it == requested_account}
|
||||||
|
|
||||||
if (found_account == null) {
|
|
||||||
throw AccountNotFoundException()
|
|
||||||
}
|
|
||||||
} else if (accounts.size == 0) {
|
} else if (accounts.size == 0) {
|
||||||
throw NoAccountsException()
|
throw NoAccountsException()
|
||||||
} else if (accounts.size == 1) {
|
} else if (accounts.size == 1) {
|
||||||
found_account = accounts.firstElement()
|
found_account = accounts.firstElement()
|
||||||
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)
|
System.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (found_account == null) {
|
||||||
|
throw AccountNotFoundException()
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug("accounts.size: {}", accounts.size)
|
logger.debug("accounts.size: {}", accounts.size)
|
||||||
logger.debug("account: {}", found_account.anonymize())
|
logger.debug("account: {}", found_account.anonymize())
|
||||||
return found_account!!
|
return found_account
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cmd_stats() {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cmd_login(target_dir: String, phoneToUse: String?): Nothing {
|
private fun cmd_login(app: TelegramApp, target_dir: String, phoneToUse: String?): Nothing {
|
||||||
val phone: String
|
LoginManager(app, target_dir, phoneToUse).run()
|
||||||
if (phoneToUse == null) {
|
System.exit(0)
|
||||||
println("Please enter your phone number in international format.")
|
throw RuntimeException("Code never reaches this. This exists just to keep the Kotlin compiler happy.")
|
||||||
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() {
|
||||||
@ -323,15 +294,18 @@ class CommandLineController {
|
|||||||
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 AccountNotFoundException() : Exception("Account not found") {}
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
* 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
|
var cmd_console = false
|
||||||
public var cmd_help = false
|
var cmd_help = false
|
||||||
public var cmd_login = false
|
var cmd_login = false
|
||||||
var cmd_debug = false
|
var cmd_debug = false
|
||||||
var cmd_trace = false
|
var cmd_trace = false
|
||||||
var cmd_trace_telegram = false
|
var cmd_trace_telegram = false
|
||||||
@ -34,8 +34,8 @@ internal object CommandLineOptions {
|
|||||||
var val_target: String? = null
|
var val_target: String? = null
|
||||||
var val_export: String? = null
|
var val_export: String? = null
|
||||||
var val_test: Int? = null
|
var val_test: Int? = null
|
||||||
@JvmStatic
|
|
||||||
fun parseOptions(args: Array<String>) {
|
init {
|
||||||
var last_cmd: String? = null
|
var last_cmd: String? = null
|
||||||
loop@ for (arg in args) {
|
loop@ for (arg in args) {
|
||||||
if (last_cmd != null) {
|
if (last_cmd != null) {
|
||||||
|
@ -29,24 +29,30 @@ 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>) {
|
||||||
|
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() {
|
fun setupLogging() {
|
||||||
val logger = LoggerFactory.getLogger(CommandLineRunner::class.java) as Logger
|
|
||||||
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 +71,13 @@ object CommandLineRunner {
|
|||||||
rootLogger.addAppender(appender)
|
rootLogger.addAppender(appender)
|
||||||
rootLogger.setLevel(Level.OFF)
|
rootLogger.setLevel(Level.OFF)
|
||||||
|
|
||||||
if (CommandLineOptions.cmd_trace) {
|
if (options.cmd_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.cmd_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.cmd_trace_telegram) {
|
||||||
(LoggerFactory.getLogger("com.github.badoualy") as Logger).setLevel(Level.TRACE)
|
(LoggerFactory.getLogger("com.github.badoualy") as Logger).setLevel(Level.TRACE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||||
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)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package de.fabianonline.telegram_backup
|
package de.fabianonline.telegram_backup
|
||||||
|
|
||||||
class DbSettings() {
|
class DbSettings(val database: Database) {
|
||||||
private fun fetchValue(name: String): String? = Database.getInstance().fetchSetting(name)
|
private fun fetchValue(name: String): String? = database.fetchSetting(name)
|
||||||
private fun saveValue(name: String, value: String?) = Database.getInstance().saveSetting(name, value)
|
private fun saveValue(name: String, value: String?) = database.saveSetting(name, value)
|
||||||
|
|
||||||
var pts: String?
|
var pts: String?
|
||||||
get() = fetchValue("pts")
|
get() = fetchValue("pts")
|
||||||
|
@ -61,18 +61,9 @@ 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 inisettings: IniSettings) {
|
||||||
internal var user: UserManager? = null
|
|
||||||
internal var db: Database? = null
|
|
||||||
internal var prog: DownloadProgressInterface? = null
|
|
||||||
internal var has_seen_flood_wait_message = false
|
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
|
var completed: Boolean
|
||||||
@ -173,18 +164,14 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (IniSettings.download_channels || IniSettings.download_supergroups) {
|
if (inisettings.download_channels) {
|
||||||
// TODO Add chat title (and other stuff?) to the database
|
println("Checking channels...")
|
||||||
|
for (channel in chats.channels) { if (channel.download) downloadMessagesFromChannel(channel) }
|
||||||
|
}
|
||||||
|
|
||||||
if (IniSettings.download_channels) {
|
if (inisettings.download_supergroups) {
|
||||||
println("Checking channels...")
|
println("Checking supergroups...")
|
||||||
for (channel in chats.channels) { if (channel.download) downloadMessagesFromChannel(channel) }
|
for (supergroup in chats.supergroups) { if (supergroup.download) downloadMessagesFromChannel(supergroup) }
|
||||||
}
|
|
||||||
|
|
||||||
if (IniSettings.download_supergroups) {
|
|
||||||
println("Checking supergroups...")
|
|
||||||
for (supergroup in chats.supergroups) { if (supergroup.download) downloadMessagesFromChannel(supergroup) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +302,7 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
prog!!.onMediaDownloadStart(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)
|
||||||
logger.trace("message {}, {}, {}, {}, {}",
|
logger.trace("message {}, {}, {}, {}, {}",
|
||||||
msg.getId(),
|
msg.getId(),
|
||||||
msg.getMedia().javaClass.getSimpleName().replace("TLMessageMedia", "…"),
|
msg.getMedia().javaClass.getSimpleName().replace("TLMessageMedia", "…"),
|
||||||
@ -366,10 +353,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 (inisettings.whitelist_channels != null) {
|
||||||
download = IniSettings.whitelist_channels!!.contains(tl_channel.getId().toString())
|
download = inisettings.whitelist_channels.contains(tl_channel.getId().toString())
|
||||||
} else if (IniSettings.blacklist_channels != null) {
|
} else if (inisettings.blacklist_channels != null) {
|
||||||
download = !IniSettings.blacklist_channels!!.contains(tl_channel.getId().toString())
|
download = !inisettings.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()) {
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,20 +4,15 @@ import java.io.File
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
|
||||||
object IniSettings {
|
class IniSettings(val file_base: String) {
|
||||||
val logger = LoggerFactory.getLogger(IniSettings::class.java)
|
val logger = LoggerFactory.getLogger(IniSettings::class.java)
|
||||||
var settings = mutableMapOf<String, MutableList<String>>()
|
var settings = mutableMapOf<String, MutableList<String>>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (UserManager.getInstance().user != null) {
|
loadIni(file_base + "config.ini")
|
||||||
loadIni(UserManager.getInstance().fileBase + "config.ini")
|
copySampleIni(file_base + "config.sample.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) {
|
private fun loadIni(filename: String) {
|
||||||
val file = File(filename)
|
val file = File(filename)
|
||||||
logger.trace("Checking ini file {}", filename.anonymize())
|
logger.trace("Checking ini file {}", filename.anonymize())
|
||||||
@ -66,20 +61,12 @@ object IniSettings {
|
|||||||
}
|
}
|
||||||
fun getArray(key: String): List<String> = settings.get(key) ?: listOf<String>()
|
fun getArray(key: String): List<String> = settings.get(key) ?: listOf<String>()
|
||||||
|
|
||||||
val gmaps_key: String
|
val gmaps_key = getString("gmaps_key", default=Config.SECRET_GMAPS)!!
|
||||||
get() = getString("gmaps_key", default=Config.SECRET_GMAPS)!!
|
val pagination = getBoolean("pagination", default=true)
|
||||||
val pagination: Boolean
|
val pagination_size = getInt("pagination_size", default=Config.DEFAULT_PAGINATION)!!
|
||||||
get() = getBoolean("pagination", default=true)
|
val download_media = getBoolean("download_media", default=true)
|
||||||
val pagination_size: Int
|
val download_channels = getBoolean("download_channels", default=false)
|
||||||
get() = getInt("pagination_size", default=Config.DEFAULT_PAGINATION)!!
|
val download_supergroups = getBoolean("download_supergroups", default=false)
|
||||||
val download_media: Boolean
|
val whitelist_channels = getStringList("whitelist_channels")
|
||||||
get() = getBoolean("download_media", default=true)
|
val blacklist_channels = getStringList("blacklist_channels")
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
package de.fabianonline.telegram_backup
|
||||||
|
|
||||||
|
import com.github.badoualy.telegram.tl.api.auth.TLSentCode
|
||||||
|
|
||||||
|
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 +" + user.user!!.getPhone().anonymize() + " 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.") {}
|
@ -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
|
|
||||||
get() = user != null
|
|
||||||
|
|
||||||
val userString: String
|
|
||||||
get() {
|
|
||||||
if (this.user == null) return "Not logged in"
|
|
||||||
val sb = StringBuilder()
|
|
||||||
if (this.user!!.getFirstName() != null) {
|
|
||||||
sb.append(this.user!!.getFirstName())
|
|
||||||
}
|
|
||||||
if (this.user!!.getLastName() != null) {
|
|
||||||
sb.append(" ")
|
|
||||||
sb.append(this.user!!.getLastName())
|
|
||||||
}
|
|
||||||
if (this.user!!.getUsername() != null) {
|
|
||||||
sb.append(" (@")
|
|
||||||
sb.append(this.user!!.getUsername())
|
|
||||||
sb.append(")")
|
|
||||||
}
|
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
val fileBase: String
|
|
||||||
get() = Config.FILE_BASE + File.separatorChar + "+" + this.user!!.getPhone() + File.separatorChar
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.client = c
|
|
||||||
logger.debug("Calling getFullUser")
|
logger.debug("Calling getFullUser")
|
||||||
try {
|
val full_user = client.usersGetFullUser(TLInputUserSelf())
|
||||||
val full_user = this.client.usersGetFullUser(TLInputUserSelf())
|
tl_user = full_user.getUser().getAsUser()
|
||||||
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 toString(): String {
|
||||||
fun sendCodeToPhoneNumber(number: String) {
|
val sb = StringBuilder()
|
||||||
this.phone = number
|
sb.append(tl_user.getFirstName() ?: "")
|
||||||
this.sent_code = this.client.authSendCode(false, number, true)
|
if (tl_user.getLastName() != null) {
|
||||||
}
|
sb.append(" ")
|
||||||
|
sb.append(tl_user.getLastName())
|
||||||
@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
|
|
||||||
}
|
}
|
||||||
|
if (tl_user.getUsername() != null) {
|
||||||
}
|
sb.append(" (@")
|
||||||
|
sb.append(tl_user.getUsername())
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
sb.append(")")
|
||||||
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!!
|
|
||||||
}
|
}
|
||||||
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ import java.util.concurrent.TimeoutException
|
|||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
abstract class AbstractMediaFileManager(protected var message: TLMessage, protected var user: UserManager, protected var client: TelegramClient) {
|
abstract class AbstractMediaFileManager(protected var message: TLMessage, protected var user: UserManager) {
|
||||||
open var isEmpty = false
|
open var isEmpty = false
|
||||||
abstract val size: Int
|
abstract val size: Int
|
||||||
abstract val extension: String
|
abstract val extension: String
|
||||||
|
@ -42,7 +42,7 @@ import java.util.concurrent.TimeoutException
|
|||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
open class DocumentFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : AbstractMediaFileManager(msg, user, client) {
|
open class DocumentFileManager(msg: TLMessage, user: UserManager) : AbstractMediaFileManager(msg, user) {
|
||||||
protected var doc: TLDocument? = null
|
protected var doc: TLDocument? = null
|
||||||
override lateinit var extension: String
|
override lateinit var extension: String
|
||||||
|
|
||||||
|
@ -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): 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)
|
||||||
} else if (media is TLMessageMediaDocument) {
|
} else if (media is TLMessageMediaDocument) {
|
||||||
val d = DocumentFileManager(m, u, c)
|
val d = DocumentFileManager(m, u)
|
||||||
return if (d.isSticker) {
|
return if (d.isSticker) {
|
||||||
StickerFileManager(m, u, c)
|
StickerFileManager(m, u)
|
||||||
} else d
|
} else d
|
||||||
} else if (media is TLMessageMediaGeo) {
|
} else if (media is TLMessageMediaGeo) {
|
||||||
return GeoFileManager(m, u, c)
|
return GeoFileManager(m, u)
|
||||||
} else if (media is TLMessageMediaEmpty) {
|
} else if (media is TLMessageMediaEmpty) {
|
||||||
return UnsupportedFileManager(m, u, c, "empty")
|
return UnsupportedFileManager(m, u, "empty")
|
||||||
} else if (media is TLMessageMediaUnsupported) {
|
} else if (media is TLMessageMediaUnsupported) {
|
||||||
return UnsupportedFileManager(m, u, c, "unsupported")
|
return UnsupportedFileManager(m, u, "unsupported")
|
||||||
} else if (media is TLMessageMediaWebPage) {
|
} else if (media is TLMessageMediaWebPage) {
|
||||||
return UnsupportedFileManager(m, u, c, "webpage")
|
return UnsupportedFileManager(m, u, "webpage")
|
||||||
} else if (media is TLMessageMediaContact) {
|
} else if (media is TLMessageMediaContact) {
|
||||||
return UnsupportedFileManager(m, u, c, "contact")
|
return UnsupportedFileManager(m, u, "contact")
|
||||||
} else if (media is TLMessageMediaVenue) {
|
} else if (media is TLMessageMediaVenue) {
|
||||||
return UnsupportedFileManager(m, u, c, "venue")
|
return UnsupportedFileManager(m, u, "venue")
|
||||||
} else {
|
} else {
|
||||||
AbstractMediaFileManager.throwUnexpectedObjectError(media)
|
AbstractMediaFileManager.throwUnexpectedObjectError(media)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ import java.util.concurrent.TimeoutException
|
|||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
class GeoFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : AbstractMediaFileManager(msg, user, client) {
|
class GeoFileManager(msg: TLMessage, user: UserManager) : AbstractMediaFileManager(msg, user) {
|
||||||
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.
|
||||||
|
@ -42,7 +42,7 @@ import java.util.concurrent.TimeoutException
|
|||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
class PhotoFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : AbstractMediaFileManager(msg, user, client) {
|
class PhotoFileManager(msg: TLMessage, user: UserManager) : AbstractMediaFileManager(msg, user) {
|
||||||
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
|
||||||
|
@ -50,7 +50,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) : DocumentFileManager(msg, user) {
|
||||||
|
|
||||||
override val isSticker = true
|
override val isSticker = true
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ import java.util.concurrent.TimeoutException
|
|||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
class UnsupportedFileManager(msg: TLMessage, user: UserManager, client: TelegramClient, type: String) : AbstractMediaFileManager(msg, user, client) {
|
class UnsupportedFileManager(msg: TLMessage, user: UserManager, type: String) : AbstractMediaFileManager(msg, user) {
|
||||||
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