mirror of
https://github.com/fabianonline/telegram_backup.git
synced 2024-11-22 16:56:16 +00:00
Kotlin: Reformatted code.
This commit is contained in:
parent
7067f98943
commit
bb180f95be
@ -28,120 +28,120 @@ import java.io.FileNotFoundException
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
internal class ApiStorage(prefix: String?) : TelegramApiStorage {
|
internal class ApiStorage(prefix: String?) : TelegramApiStorage {
|
||||||
private var prefix: String? = null
|
private var prefix: String? = null
|
||||||
private var do_save = false
|
private var do_save = false
|
||||||
private var auth_key: AuthKey? = null
|
private var auth_key: AuthKey? = null
|
||||||
private var dc: DataCenter? = null
|
private var dc: DataCenter? = null
|
||||||
private var file_auth_key: File? = null
|
private var file_auth_key: File? = null
|
||||||
private var file_dc: File? = null
|
private var file_dc: File? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.setPrefix(prefix)
|
this.setPrefix(prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setPrefix(prefix: String?) {
|
fun setPrefix(prefix: String?) {
|
||||||
this.prefix = prefix
|
this.prefix = prefix
|
||||||
this.do_save = this.prefix != null
|
this.do_save = this.prefix != null
|
||||||
if (this.do_save) {
|
if (this.do_save) {
|
||||||
val base = Config.FILE_BASE +
|
val base = Config.FILE_BASE +
|
||||||
File.separatorChar +
|
File.separatorChar +
|
||||||
this.prefix +
|
this.prefix +
|
||||||
File.separatorChar
|
File.separatorChar
|
||||||
this.file_auth_key = File(base + Config.FILE_NAME_AUTH_KEY)
|
this.file_auth_key = File(base + Config.FILE_NAME_AUTH_KEY)
|
||||||
this.file_dc = File(base + Config.FILE_NAME_DC)
|
this.file_dc = File(base + Config.FILE_NAME_DC)
|
||||||
this._saveAuthKey()
|
this._saveAuthKey()
|
||||||
this._saveDc()
|
this._saveDc()
|
||||||
} else {
|
} else {
|
||||||
this.file_auth_key = null
|
this.file_auth_key = null
|
||||||
this.file_dc = null
|
this.file_dc = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveAuthKey(authKey: AuthKey) {
|
override fun saveAuthKey(authKey: AuthKey) {
|
||||||
this.auth_key = authKey
|
this.auth_key = authKey
|
||||||
this._saveAuthKey()
|
this._saveAuthKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun _saveAuthKey() {
|
private fun _saveAuthKey() {
|
||||||
if (this.do_save && this.auth_key != null) {
|
if (this.do_save && this.auth_key != null) {
|
||||||
try {
|
try {
|
||||||
FileUtils.writeByteArrayToFile(this.file_auth_key, this.auth_key!!.key)
|
FileUtils.writeByteArrayToFile(this.file_auth_key, this.auth_key!!.key)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadAuthKey(): AuthKey? {
|
override fun loadAuthKey(): AuthKey? {
|
||||||
if (this.auth_key != null) return this.auth_key
|
if (this.auth_key != null) return this.auth_key
|
||||||
if (this.file_auth_key != null) {
|
if (this.file_auth_key != null) {
|
||||||
try {
|
try {
|
||||||
return AuthKey(FileUtils.readFileToByteArray(this.file_auth_key))
|
return AuthKey(FileUtils.readFileToByteArray(this.file_auth_key))
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
if (e !is FileNotFoundException) e.printStackTrace()
|
if (e !is FileNotFoundException) e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveDc(dataCenter: DataCenter) {
|
override fun saveDc(dataCenter: DataCenter) {
|
||||||
this.dc = dataCenter
|
this.dc = dataCenter
|
||||||
this._saveDc()
|
this._saveDc()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun _saveDc() {
|
private fun _saveDc() {
|
||||||
if (this.do_save && this.dc != null) {
|
if (this.do_save && this.dc != null) {
|
||||||
try {
|
try {
|
||||||
FileUtils.write(this.file_dc, this.dc!!.toString())
|
FileUtils.write(this.file_dc, this.dc!!.toString())
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadDc(): DataCenter? {
|
override fun loadDc(): DataCenter? {
|
||||||
if (this.dc != null) return this.dc
|
if (this.dc != null) return this.dc
|
||||||
if (this.file_dc != null) {
|
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: IOException) {
|
||||||
if (e !is FileNotFoundException) e.printStackTrace()
|
if (e !is FileNotFoundException) e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteAuthKey() {
|
override fun deleteAuthKey() {
|
||||||
if (this.do_save) {
|
if (this.do_save) {
|
||||||
try {
|
try {
|
||||||
FileUtils.forceDelete(this.file_auth_key)
|
FileUtils.forceDelete(this.file_auth_key)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteDc() {
|
override fun deleteDc() {
|
||||||
if (this.do_save) {
|
if (this.do_save) {
|
||||||
try {
|
try {
|
||||||
FileUtils.forceDelete(this.file_dc)
|
FileUtils.forceDelete(this.file_dc)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveSession(session: MTSession?) {}
|
override fun saveSession(session: MTSession?) {}
|
||||||
|
|
||||||
override fun loadSession(): MTSession? {
|
override fun loadSession(): MTSession? {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
package de.fabianonline.telegram_backup
|
package de.fabianonline.telegram_backup
|
||||||
|
|
||||||
import de.fabianonline.telegram_backup.TelegramUpdateHandler
|
import de.fabianonline.telegram_backup.TelegramUpdateHandler
|
||||||
import de.fabianonline.telegram_backup.exporter.HTMLExporter
|
import de.fabianonline.telegram_backup.exporter.HTMLExporter
|
||||||
import com.github.badoualy.telegram.api.Kotlogram
|
import com.github.badoualy.telegram.api.Kotlogram
|
||||||
@ -27,327 +28,282 @@ import java.util.Vector
|
|||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
|
||||||
class CommandLineController {
|
class CommandLineController {
|
||||||
private val storage:ApiStorage
|
private val storage: ApiStorage
|
||||||
var app:TelegramApp
|
var app: TelegramApp
|
||||||
|
|
||||||
private fun getLine():String {
|
private fun getLine(): String {
|
||||||
if (System.console() != null) {
|
if (System.console() != null) {
|
||||||
return System.console().readLine("> ")
|
return System.console().readLine("> ")
|
||||||
} else {
|
} else {
|
||||||
print("> ")
|
print("> ")
|
||||||
return Scanner(System.`in`).nextLine()
|
return Scanner(System.`in`).nextLine()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPassword():String {
|
|
||||||
if (System.console() != null) {
|
|
||||||
return String(System.console().readPassword("> "))
|
|
||||||
} else {
|
|
||||||
return getLine()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init{
|
|
||||||
logger.info("CommandLineController started. App version {}", Config.APP_APPVER)
|
|
||||||
this.printHeader()
|
|
||||||
if (CommandLineOptions.cmd_version)
|
|
||||||
{
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
else if (CommandLineOptions.cmd_help)
|
|
||||||
{
|
|
||||||
this.show_help()
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
else if (CommandLineOptions.cmd_license)
|
|
||||||
{
|
|
||||||
CommandLineController.show_license()
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
this.setupFileBase()
|
|
||||||
if (CommandLineOptions.cmd_list_accounts)
|
|
||||||
{
|
|
||||||
this.list_accounts()
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
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.trace("Checking accounts")
|
|
||||||
val account = this.selectAccount()
|
|
||||||
logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
|
|
||||||
logger.info("Initializing ApiStorage")
|
|
||||||
storage = ApiStorage(account)
|
|
||||||
logger.info("Initializing TelegramUpdateHandler")
|
|
||||||
val handler = TelegramUpdateHandler()
|
|
||||||
logger.info("Creating Client")
|
|
||||||
val client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, handler)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
logger.info("Initializing UserManager")
|
|
||||||
UserManager.init(client)
|
|
||||||
val user = UserManager.getInstance()
|
|
||||||
if (!CommandLineOptions.cmd_login && !user.loggedIn)
|
|
||||||
{
|
|
||||||
println("Your authorization data is invalid or missing. You will have to login with Telegram again.")
|
|
||||||
CommandLineOptions.cmd_login = true
|
|
||||||
}
|
|
||||||
if (account != null && user.loggedIn)
|
|
||||||
{
|
|
||||||
if (account != "+" + user.user!!.getPhone())
|
|
||||||
{
|
|
||||||
logger.error("Account: {}, user.user!!.getPhone(): +{}", Utils.anonymize(account), Utils.anonymize(user.user!!.getPhone()))
|
|
||||||
throw RuntimeException("Account / User mismatch")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
|
|
||||||
if (CommandLineOptions.cmd_login)
|
|
||||||
{
|
|
||||||
cmd_login(account)
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
// If we reach this point, we can assume that there is an account and a database can be loaded / created.
|
|
||||||
Database.init(client)
|
|
||||||
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 " + Utils.anonymize(user.userString))
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
println("You are not logged in.")
|
|
||||||
System.exit(1)
|
|
||||||
}
|
|
||||||
logger.info("Initializing Download Manager")
|
|
||||||
val d = DownloadManager(client, CommandLineDownloadProgress())
|
|
||||||
logger.debug("Calling DownloadManager.downloadMessages with limit {}", CommandLineOptions.val_limit_messages)
|
|
||||||
d.downloadMessages(CommandLineOptions.val_limit_messages)
|
|
||||||
logger.debug("CommandLineOptions.cmd_no_media: {}", CommandLineOptions.cmd_no_media)
|
|
||||||
if (!CommandLineOptions.cmd_no_media)
|
|
||||||
{
|
|
||||||
logger.debug("Calling DownloadManager.downloadMedia")
|
|
||||||
d.downloadMedia()
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
println("Skipping media download because --no-media is set.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e:Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
logger.error("Exception caught!", e)
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (CommandLineOptions.cmd_daemon)
|
|
||||||
{
|
|
||||||
handler.activate()
|
|
||||||
println("DAEMON mode requested - keeping running.")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
client.close()
|
|
||||||
println()
|
|
||||||
println("----- EXIT -----")
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun printHeader() {
|
|
||||||
System.out.println("Telegram_Backup version " + Config.APP_APPVER + ", Copyright (C) 2016, 2017 Fabian Schlenz")
|
|
||||||
println()
|
|
||||||
println("Telegram_Backup comes with ABSOLUTELY NO WARRANTY. This is free software, and you are")
|
|
||||||
println("welcome to redistribute it under certain conditions; run it with '--license' for details.")
|
|
||||||
println()
|
|
||||||
}
|
|
||||||
private fun setupFileBase() {
|
|
||||||
logger.debug("Target dir at startup: {}", Utils.anonymize(Config.FILE_BASE))
|
|
||||||
if (CommandLineOptions.val_target != null)
|
|
||||||
{
|
|
||||||
Config.FILE_BASE = CommandLineOptions.val_target!!
|
|
||||||
}
|
|
||||||
logger.debug("Target dir after options: {}", Utils.anonymize(Config.FILE_BASE))
|
|
||||||
System.out.println("Base directory for files: " + Utils.anonymize(Config.FILE_BASE))
|
|
||||||
}
|
|
||||||
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: {}", Utils.anonymize(CommandLineOptions.val_account!!))
|
|
||||||
logger.trace("Checking accounts for match.")
|
|
||||||
var found = false
|
|
||||||
for (acc in accounts)
|
|
||||||
{
|
|
||||||
logger.trace("Checking {}", Utils.anonymize(acc))
|
|
||||||
if (acc == CommandLineOptions.val_account)
|
|
||||||
{
|
|
||||||
found = true
|
|
||||||
logger.trace("Matches.")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
show_error("Couldn't find account '" + Utils.anonymize(CommandLineOptions.val_account!!) + "'. Maybe you want to use '--login' first?")
|
|
||||||
}
|
|
||||||
account = CommandLineOptions.val_account!!
|
|
||||||
}
|
|
||||||
else if (accounts.size == 0)
|
|
||||||
{
|
|
||||||
println("No accounts found. Starting login process...")
|
|
||||||
CommandLineOptions.cmd_login = true
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
else if (accounts.size == 1)
|
|
||||||
{
|
|
||||||
account = accounts.firstElement()
|
|
||||||
System.out.println("Using only available account: " + Utils.anonymize(account))
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
show_error(("You didn't specify which account to use.\n" +
|
|
||||||
"Use '--account <x>' to use account <x>.\n" +
|
|
||||||
"Use '--list-accounts' to see all available accounts."))
|
|
||||||
System.exit(1)
|
|
||||||
}
|
|
||||||
logger.debug("accounts.size: {}", accounts.size)
|
|
||||||
logger.debug("account: {}", Utils.anonymize(account))
|
|
||||||
return account
|
|
||||||
}
|
|
||||||
private fun cmd_stats() {
|
|
||||||
println()
|
|
||||||
println("Stats:")
|
|
||||||
val format = "%40s: %d%n"
|
|
||||||
System.out.format(format, "Number of accounts", Utils.getAccounts().size)
|
|
||||||
System.out.format(format, "Number of messages", Database.getInstance().getMessageCount())
|
|
||||||
System.out.format(format, "Number of chats", Database.getInstance().getChatCount())
|
|
||||||
System.out.format(format, "Number of users", Database.getInstance().getUserCount())
|
|
||||||
System.out.format(format, "Top message ID", Database.getInstance().getTopMessageID())
|
|
||||||
println()
|
|
||||||
println("Media Types:")
|
|
||||||
for ((key, value) in Database.getInstance().getMessageMediaTypesWithCount())
|
|
||||||
{
|
|
||||||
System.out.format(format, key, value)
|
|
||||||
}
|
|
||||||
println()
|
|
||||||
println("Api layers of messages:")
|
|
||||||
for ((key, value) in Database.getInstance().getMessageApiLayerWithCount())
|
|
||||||
{
|
|
||||||
System.out.format(format, key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
|
||||||
private fun cmd_login(phoneToUse:String?) {
|
|
||||||
val user = UserManager.getInstance()
|
|
||||||
val phone: String
|
|
||||||
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 +" + Utils.anonymize(user.user!!.getPhone()) + " to use this account.")
|
|
||||||
}
|
|
||||||
private fun show_help() {
|
|
||||||
println("Valid options are:")
|
|
||||||
println(" -h, --help Shows this help.")
|
|
||||||
println(" -a, --account <x> Use account <x>.")
|
|
||||||
println(" -l, --login Login to an existing telegram account.")
|
|
||||||
println(" --debug Shows some debug information.")
|
|
||||||
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(" -A, --list-accounts List all existing accounts ")
|
|
||||||
println(" --limit-messages <x> Downloads at most the most recent <x> messages.")
|
|
||||||
println(" --no-media Do not download media files.")
|
|
||||||
println(" -t, --target <x> Target directory for the files.")
|
|
||||||
println(" -e, --export <format> Export the database. Valid formats are:")
|
|
||||||
println(" html - Creates HTML files.")
|
|
||||||
println(" --license Displays the license of this program.")
|
|
||||||
println(" -d, --daemon Keep running and automatically save new messages.")
|
|
||||||
println(" --anonymize (Try to) Remove all sensitive information from output. Useful for requesting support.")
|
|
||||||
println(" --stats Print some usage statistics.")
|
|
||||||
println(" --with-channels Backup channels as well.")
|
|
||||||
println(" --with-supergroups Backup supergroups as well.")
|
|
||||||
}
|
|
||||||
private fun list_accounts() {
|
|
||||||
println("List of available accounts:")
|
|
||||||
val accounts = Utils.getAccounts()
|
|
||||||
if (accounts.size > 0)
|
|
||||||
{
|
|
||||||
for (str in accounts)
|
|
||||||
{
|
|
||||||
System.out.println(" " + Utils.anonymize(str))
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
private val logger = LoggerFactory.getLogger(CommandLineController::class.java)
|
|
||||||
|
|
||||||
public fun show_error(error:String) {
|
private fun getPassword(): String {
|
||||||
logger.error(error)
|
if (System.console() != null) {
|
||||||
println("ERROR: " + error)
|
return String(System.console().readPassword("> "))
|
||||||
System.exit(1)
|
} else {
|
||||||
}
|
return getLine()
|
||||||
fun show_license() {
|
}
|
||||||
println("TODO: Print the GPL.")
|
}
|
||||||
}
|
|
||||||
}
|
init {
|
||||||
|
logger.info("CommandLineController started. App version {}", Config.APP_APPVER)
|
||||||
|
this.printHeader()
|
||||||
|
if (CommandLineOptions.cmd_version) {
|
||||||
|
System.exit(0)
|
||||||
|
} else if (CommandLineOptions.cmd_help) {
|
||||||
|
this.show_help()
|
||||||
|
System.exit(0)
|
||||||
|
} else if (CommandLineOptions.cmd_license) {
|
||||||
|
CommandLineController.show_license()
|
||||||
|
System.exit(0)
|
||||||
|
}
|
||||||
|
this.setupFileBase()
|
||||||
|
if (CommandLineOptions.cmd_list_accounts) {
|
||||||
|
this.list_accounts()
|
||||||
|
System.exit(0)
|
||||||
|
}
|
||||||
|
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.trace("Checking accounts")
|
||||||
|
val account = this.selectAccount()
|
||||||
|
logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
|
||||||
|
logger.info("Initializing ApiStorage")
|
||||||
|
storage = ApiStorage(account)
|
||||||
|
logger.info("Initializing TelegramUpdateHandler")
|
||||||
|
val handler = TelegramUpdateHandler()
|
||||||
|
logger.info("Creating Client")
|
||||||
|
val client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, handler)
|
||||||
|
try {
|
||||||
|
logger.info("Initializing UserManager")
|
||||||
|
UserManager.init(client)
|
||||||
|
val user = UserManager.getInstance()
|
||||||
|
if (!CommandLineOptions.cmd_login && !user.loggedIn) {
|
||||||
|
println("Your authorization data is invalid or missing. You will have to login with Telegram again.")
|
||||||
|
CommandLineOptions.cmd_login = true
|
||||||
|
}
|
||||||
|
if (account != null && user.loggedIn) {
|
||||||
|
if (account != "+" + user.user!!.getPhone()) {
|
||||||
|
logger.error("Account: {}, user.user!!.getPhone(): +{}", Utils.anonymize(account), Utils.anonymize(user.user!!.getPhone()))
|
||||||
|
throw RuntimeException("Account / User mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
|
||||||
|
if (CommandLineOptions.cmd_login) {
|
||||||
|
cmd_login(account)
|
||||||
|
System.exit(0)
|
||||||
|
}
|
||||||
|
// If we reach this point, we can assume that there is an account and a database can be loaded / created.
|
||||||
|
Database.init(client)
|
||||||
|
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 " + Utils.anonymize(user.userString))
|
||||||
|
} else {
|
||||||
|
println("You are not logged in.")
|
||||||
|
System.exit(1)
|
||||||
|
}
|
||||||
|
logger.info("Initializing Download Manager")
|
||||||
|
val d = DownloadManager(client, CommandLineDownloadProgress())
|
||||||
|
logger.debug("Calling DownloadManager.downloadMessages with limit {}", CommandLineOptions.val_limit_messages)
|
||||||
|
d.downloadMessages(CommandLineOptions.val_limit_messages)
|
||||||
|
logger.debug("CommandLineOptions.cmd_no_media: {}", CommandLineOptions.cmd_no_media)
|
||||||
|
if (!CommandLineOptions.cmd_no_media) {
|
||||||
|
logger.debug("Calling DownloadManager.downloadMedia")
|
||||||
|
d.downloadMedia()
|
||||||
|
} else {
|
||||||
|
println("Skipping media download because --no-media is set.")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
logger.error("Exception caught!", e)
|
||||||
|
} finally {
|
||||||
|
if (CommandLineOptions.cmd_daemon) {
|
||||||
|
handler.activate()
|
||||||
|
println("DAEMON mode requested - keeping running.")
|
||||||
|
} else {
|
||||||
|
client.close()
|
||||||
|
println()
|
||||||
|
println("----- EXIT -----")
|
||||||
|
System.exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun printHeader() {
|
||||||
|
System.out.println("Telegram_Backup version " + Config.APP_APPVER + ", Copyright (C) 2016, 2017 Fabian Schlenz")
|
||||||
|
println()
|
||||||
|
println("Telegram_Backup comes with ABSOLUTELY NO WARRANTY. This is free software, and you are")
|
||||||
|
println("welcome to redistribute it under certain conditions; run it with '--license' for details.")
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupFileBase() {
|
||||||
|
logger.debug("Target dir at startup: {}", Utils.anonymize(Config.FILE_BASE))
|
||||||
|
if (CommandLineOptions.val_target != null) {
|
||||||
|
Config.FILE_BASE = CommandLineOptions.val_target!!
|
||||||
|
}
|
||||||
|
logger.debug("Target dir after options: {}", Utils.anonymize(Config.FILE_BASE))
|
||||||
|
System.out.println("Base directory for files: " + Utils.anonymize(Config.FILE_BASE))
|
||||||
|
}
|
||||||
|
|
||||||
|
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: {}", Utils.anonymize(CommandLineOptions.val_account!!))
|
||||||
|
logger.trace("Checking accounts for match.")
|
||||||
|
var found = false
|
||||||
|
for (acc in accounts) {
|
||||||
|
logger.trace("Checking {}", Utils.anonymize(acc))
|
||||||
|
if (acc == CommandLineOptions.val_account) {
|
||||||
|
found = true
|
||||||
|
logger.trace("Matches.")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
show_error("Couldn't find account '" + Utils.anonymize(CommandLineOptions.val_account!!) + "'. Maybe you want to use '--login' first?")
|
||||||
|
}
|
||||||
|
account = CommandLineOptions.val_account!!
|
||||||
|
} else if (accounts.size == 0) {
|
||||||
|
println("No accounts found. Starting login process...")
|
||||||
|
CommandLineOptions.cmd_login = true
|
||||||
|
return null
|
||||||
|
} else if (accounts.size == 1) {
|
||||||
|
account = accounts.firstElement()
|
||||||
|
System.out.println("Using only available account: " + Utils.anonymize(account))
|
||||||
|
} else {
|
||||||
|
show_error(("You didn't specify which account to use.\n" +
|
||||||
|
"Use '--account <x>' to use account <x>.\n" +
|
||||||
|
"Use '--list-accounts' to see all available accounts."))
|
||||||
|
System.exit(1)
|
||||||
|
}
|
||||||
|
logger.debug("accounts.size: {}", accounts.size)
|
||||||
|
logger.debug("account: {}", Utils.anonymize(account))
|
||||||
|
return account
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cmd_stats() {
|
||||||
|
println()
|
||||||
|
println("Stats:")
|
||||||
|
val format = "%40s: %d%n"
|
||||||
|
System.out.format(format, "Number of accounts", Utils.getAccounts().size)
|
||||||
|
System.out.format(format, "Number of messages", Database.getInstance().getMessageCount())
|
||||||
|
System.out.format(format, "Number of chats", Database.getInstance().getChatCount())
|
||||||
|
System.out.format(format, "Number of users", Database.getInstance().getUserCount())
|
||||||
|
System.out.format(format, "Top message ID", Database.getInstance().getTopMessageID())
|
||||||
|
println()
|
||||||
|
println("Media Types:")
|
||||||
|
for ((key, value) in Database.getInstance().getMessageMediaTypesWithCount()) {
|
||||||
|
System.out.format(format, key, value)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
println("Api layers of messages:")
|
||||||
|
for ((key, value) in Database.getInstance().getMessageApiLayerWithCount()) {
|
||||||
|
System.out.format(format, key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(RpcErrorException::class, IOException::class)
|
||||||
|
private fun cmd_login(phoneToUse: String?) {
|
||||||
|
val user = UserManager.getInstance()
|
||||||
|
val phone: String
|
||||||
|
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 +" + Utils.anonymize(user.user!!.getPhone()) + " to use this account.")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun show_help() {
|
||||||
|
println("Valid options are:")
|
||||||
|
println(" -h, --help Shows this help.")
|
||||||
|
println(" -a, --account <x> Use account <x>.")
|
||||||
|
println(" -l, --login Login to an existing telegram account.")
|
||||||
|
println(" --debug Shows some debug information.")
|
||||||
|
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(" -A, --list-accounts List all existing accounts ")
|
||||||
|
println(" --limit-messages <x> Downloads at most the most recent <x> messages.")
|
||||||
|
println(" --no-media Do not download media files.")
|
||||||
|
println(" -t, --target <x> Target directory for the files.")
|
||||||
|
println(" -e, --export <format> Export the database. Valid formats are:")
|
||||||
|
println(" html - Creates HTML files.")
|
||||||
|
println(" --license Displays the license of this program.")
|
||||||
|
println(" -d, --daemon Keep running and automatically save new messages.")
|
||||||
|
println(" --anonymize (Try to) Remove all sensitive information from output. Useful for requesting support.")
|
||||||
|
println(" --stats Print some usage statistics.")
|
||||||
|
println(" --with-channels Backup channels as well.")
|
||||||
|
println(" --with-supergroups Backup supergroups as well.")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun list_accounts() {
|
||||||
|
println("List of available accounts:")
|
||||||
|
val accounts = Utils.getAccounts()
|
||||||
|
if (accounts.size > 0) {
|
||||||
|
for (str in accounts) {
|
||||||
|
System.out.println(" " + Utils.anonymize(str))
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
private val logger = LoggerFactory.getLogger(CommandLineController::class.java)
|
||||||
|
|
||||||
|
public fun show_error(error: String) {
|
||||||
|
logger.error(error)
|
||||||
|
println("ERROR: " + error)
|
||||||
|
System.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun show_license() {
|
||||||
|
println("TODO: Print the GPL.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,68 +20,68 @@ import de.fabianonline.telegram_backup.DownloadProgressInterface
|
|||||||
import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
|
import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
|
||||||
|
|
||||||
internal class CommandLineDownloadProgress : DownloadProgressInterface {
|
internal class CommandLineDownloadProgress : DownloadProgressInterface {
|
||||||
private var mediaCount = 0
|
private var mediaCount = 0
|
||||||
private var i = 0
|
private var i = 0
|
||||||
|
|
||||||
override fun onMessageDownloadStart(count: Int, source: String?) {
|
override fun onMessageDownloadStart(count: Int, source: String?) {
|
||||||
i = 0
|
i = 0
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
System.out.println("Downloading $count messages.")
|
System.out.println("Downloading $count messages.")
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Downloading " + count + " messages from " + Utils.anonymize(source))
|
System.out.println("Downloading " + count + " messages from " + Utils.anonymize(source))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMessageDownloaded(number: Int) {
|
override fun onMessageDownloaded(number: Int) {
|
||||||
i += number
|
i += number
|
||||||
print("..." + i)
|
print("..." + i)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMessageDownloadFinished() {
|
override fun onMessageDownloadFinished() {
|
||||||
println(" done.")
|
println(" done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaDownloadStart(count: Int) {
|
override fun onMediaDownloadStart(count: Int) {
|
||||||
i = 0
|
i = 0
|
||||||
mediaCount = count
|
mediaCount = count
|
||||||
println("Checking and downloading media.")
|
println("Checking and downloading media.")
|
||||||
println("Legend:")
|
println("Legend:")
|
||||||
println("'V' - Video 'P' - Photo 'D' - Document")
|
println("'V' - Video 'P' - Photo 'D' - Document")
|
||||||
println("'S' - Sticker 'A' - Audio 'G' - Geolocation")
|
println("'S' - Sticker 'A' - Audio 'G' - Geolocation")
|
||||||
println("'.' - Previously downloaded file 'e' - Empty file")
|
println("'.' - Previously downloaded file 'e' - Empty file")
|
||||||
println("' ' - Ignored media type (weblinks or contacts, for example)")
|
println("' ' - Ignored media type (weblinks or contacts, for example)")
|
||||||
println("'x' - File skipped because of timeout errors")
|
println("'x' - File skipped because of timeout errors")
|
||||||
println("" + count + " Files to check / download")
|
println("" + count + " Files to check / download")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaDownloaded(file_manager: AbstractMediaFileManager) {
|
override fun onMediaDownloaded(file_manager: AbstractMediaFileManager) {
|
||||||
show(file_manager.letter.toUpperCase())
|
show(file_manager.letter.toUpperCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaDownloadedEmpty() {
|
override fun onMediaDownloadedEmpty() {
|
||||||
show("e")
|
show("e")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaAlreadyPresent(file_manager: AbstractMediaFileManager) {
|
override fun onMediaAlreadyPresent(file_manager: AbstractMediaFileManager) {
|
||||||
show(".")
|
show(".")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaSkipped() {
|
override fun onMediaSkipped() {
|
||||||
show("x")
|
show("x")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaDownloadFinished() {
|
override fun onMediaDownloadFinished() {
|
||||||
showNewLine()
|
showNewLine()
|
||||||
println("Done.")
|
println("Done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun show(letter: String) {
|
private fun show(letter: String) {
|
||||||
print(letter)
|
print(letter)
|
||||||
i++
|
i++
|
||||||
if (i % 100 == 0) showNewLine()
|
if (i % 100 == 0) showNewLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNewLine() {
|
private fun showNewLine() {
|
||||||
println(" - $i/$mediaCount")
|
println(" - $i/$mediaCount")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,85 +14,84 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
package de.fabianonline.telegram_backup
|
package de.fabianonline.telegram_backup
|
||||||
|
|
||||||
internal object CommandLineOptions {
|
internal object CommandLineOptions {
|
||||||
public var cmd_console = false
|
public var cmd_console = false
|
||||||
public var cmd_help = false
|
public var cmd_help = false
|
||||||
public var cmd_login = false
|
public 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
|
||||||
var cmd_list_accounts = false
|
var cmd_list_accounts = false
|
||||||
var cmd_version = false
|
var cmd_version = false
|
||||||
var cmd_license = false
|
var cmd_license = false
|
||||||
var cmd_daemon = false
|
var cmd_daemon = false
|
||||||
var cmd_no_media = false
|
var cmd_no_media = false
|
||||||
var cmd_anonymize = false
|
var cmd_anonymize = false
|
||||||
var cmd_stats = false
|
var cmd_stats = false
|
||||||
var cmd_channels = false
|
var cmd_channels = false
|
||||||
var cmd_supergroups = false
|
var cmd_supergroups = false
|
||||||
var val_account:String? = null
|
var val_account: String? = null
|
||||||
var val_limit_messages:Int? = null
|
var val_limit_messages: Int? = null
|
||||||
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>) {
|
@JvmStatic
|
||||||
var last_cmd:String? = null
|
fun parseOptions(args: Array<String>) {
|
||||||
loop@ for (arg in args)
|
var last_cmd: String? = null
|
||||||
{
|
loop@ for (arg in args) {
|
||||||
if (last_cmd != null)
|
if (last_cmd != null) {
|
||||||
{
|
when (last_cmd) {
|
||||||
when (last_cmd) {
|
"--account" -> val_account = arg
|
||||||
"--account" -> val_account = arg
|
"--limit-messages" -> val_limit_messages = Integer.parseInt(arg)
|
||||||
"--limit-messages" -> val_limit_messages = Integer.parseInt(arg)
|
"--target" -> val_target = arg
|
||||||
"--target" -> val_target = arg
|
"--export" -> val_export = arg
|
||||||
"--export" -> val_export = arg
|
"--test" -> val_test = Integer.parseInt(arg)
|
||||||
"--test" -> val_test = Integer.parseInt(arg)
|
}
|
||||||
}
|
last_cmd = null
|
||||||
last_cmd = null
|
continue
|
||||||
continue
|
}
|
||||||
}
|
when (arg) {
|
||||||
when (arg) {
|
"-a", "--account" -> {
|
||||||
"-a", "--account" -> {
|
last_cmd = "--account"
|
||||||
last_cmd = "--account"
|
continue@loop
|
||||||
continue@loop
|
}
|
||||||
}
|
"-h", "--help" -> cmd_help = true
|
||||||
"-h", "--help" -> cmd_help = true
|
"-l", "--login" -> cmd_login = true
|
||||||
"-l", "--login" -> cmd_login = true
|
"--debug" -> cmd_debug = true
|
||||||
"--debug" -> cmd_debug = true
|
"--trace" -> cmd_trace = true
|
||||||
"--trace" -> cmd_trace = true
|
"--trace-telegram" -> cmd_trace_telegram = true
|
||||||
"--trace-telegram" -> cmd_trace_telegram = true
|
"-A", "--list-accounts" -> cmd_list_accounts = true
|
||||||
"-A", "--list-accounts" -> cmd_list_accounts = true
|
"--limit-messages" -> {
|
||||||
"--limit-messages" -> {
|
last_cmd = arg
|
||||||
last_cmd = arg
|
continue@loop
|
||||||
continue@loop
|
}
|
||||||
}
|
"--console" -> cmd_console = true
|
||||||
"--console" -> cmd_console = true
|
"-t", "--target" -> {
|
||||||
"-t", "--target" -> {
|
last_cmd = "--target"
|
||||||
last_cmd = "--target"
|
continue@loop
|
||||||
continue@loop
|
}
|
||||||
}
|
"-V", "--version" -> cmd_version = true
|
||||||
"-V", "--version" -> cmd_version = true
|
"-e", "--export" -> {
|
||||||
"-e", "--export" -> {
|
last_cmd = "--export"
|
||||||
last_cmd = "--export"
|
continue@loop
|
||||||
continue@loop
|
}
|
||||||
}
|
"--license" -> cmd_license = true
|
||||||
"--license" -> cmd_license = true
|
"-d", "--daemon" -> cmd_daemon = true
|
||||||
"-d", "--daemon" -> cmd_daemon = true
|
"--no-media" -> cmd_no_media = true
|
||||||
"--no-media" -> cmd_no_media = true
|
"--test" -> {
|
||||||
"--test" -> {
|
last_cmd = "--test"
|
||||||
last_cmd = "--test"
|
continue@loop
|
||||||
continue@loop
|
}
|
||||||
}
|
"--anonymize" -> cmd_anonymize = true
|
||||||
"--anonymize" -> cmd_anonymize = true
|
"--stats" -> cmd_stats = true
|
||||||
"--stats" -> cmd_stats = true
|
"--with-channels" -> cmd_channels = true
|
||||||
"--with-channels" -> cmd_channels = true
|
"--with-supergroups" -> cmd_supergroups = true
|
||||||
"--with-supergroups" -> cmd_supergroups = true
|
else -> throw RuntimeException("Unknown command " + arg)
|
||||||
else -> throw RuntimeException("Unknown command " + arg)
|
}
|
||||||
}
|
}
|
||||||
}
|
if (last_cmd != null) {
|
||||||
if (last_cmd != null)
|
CommandLineController.show_error("Command $last_cmd had no parameter set.")
|
||||||
{
|
}
|
||||||
CommandLineController.show_error("Command $last_cmd had no parameter set.")
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,67 +27,67 @@ import ch.qos.logback.classic.spi.ILoggingEvent
|
|||||||
import ch.qos.logback.core.ConsoleAppender
|
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)
|
CommandLineOptions.parseOptions(args)
|
||||||
|
|
||||||
CommandLineRunner.setupLogging()
|
CommandLineRunner.setupLogging()
|
||||||
CommandLineRunner.checkVersion()
|
CommandLineRunner.checkVersion()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (true || CommandLineOptions.cmd_console) {
|
if (true || CommandLineOptions.cmd_console) {
|
||||||
// Always use the console for now.
|
// Always use the console for now.
|
||||||
CommandLineController()
|
CommandLineController()
|
||||||
} else {
|
} else {
|
||||||
GUIController()
|
GUIController()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object CommandLineRunner {
|
object CommandLineRunner {
|
||||||
fun setupLogging() {
|
fun setupLogging() {
|
||||||
val logger = LoggerFactory.getLogger(CommandLineRunner::class.java) as Logger
|
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()
|
||||||
rootContext.reset()
|
rootContext.reset()
|
||||||
|
|
||||||
val encoder = PatternLayoutEncoder()
|
val encoder = PatternLayoutEncoder()
|
||||||
encoder.setContext(rootContext)
|
encoder.setContext(rootContext)
|
||||||
encoder.setPattern("%d{HH:mm:ss.SSS} %-5level %-35.-35(%logger{0}.%method): %message%n")
|
encoder.setPattern("%d{HH:mm:ss.SSS} %-5level %-35.-35(%logger{0}.%method): %message%n")
|
||||||
encoder.start()
|
encoder.start()
|
||||||
|
|
||||||
val appender = ConsoleAppender<ILoggingEvent>()
|
val appender = ConsoleAppender<ILoggingEvent>()
|
||||||
appender.setContext(rootContext)
|
appender.setContext(rootContext)
|
||||||
appender.setEncoder(encoder)
|
appender.setEncoder(encoder)
|
||||||
appender.start()
|
appender.start()
|
||||||
|
|
||||||
rootLogger.addAppender(appender)
|
rootLogger.addAppender(appender)
|
||||||
rootLogger.setLevel(Level.OFF)
|
rootLogger.setLevel(Level.OFF)
|
||||||
|
|
||||||
if (CommandLineOptions.cmd_trace) {
|
if (CommandLineOptions.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 (CommandLineOptions.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 (CommandLineOptions.cmd_trace_telegram) {
|
||||||
(LoggerFactory.getLogger("com.github.badoualy") as Logger).setLevel(Level.TRACE)
|
(LoggerFactory.getLogger("com.github.badoualy") as Logger).setLevel(Level.TRACE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkVersion(): Boolean {
|
fun checkVersion(): Boolean {
|
||||||
val v = Utils.getNewestVersion()
|
val v = Utils.getNewestVersion()
|
||||||
if (v != null && v.isNewer) {
|
if (v != null && v.isNewer) {
|
||||||
System.out.println("A newer version is vailable!")
|
System.out.println("A newer version is vailable!")
|
||||||
System.out.println("You are using: " + Config.APP_APPVER)
|
System.out.println("You are using: " + Config.APP_APPVER)
|
||||||
System.out.println("Available: " + v.version)
|
System.out.println("Available: " + v.version)
|
||||||
System.out.println("Get it here: " + v.url)
|
System.out.println("Get it here: " + v.url)
|
||||||
System.out.println()
|
System.out.println()
|
||||||
System.out.println("Changes in this version:")
|
System.out.println("Changes in this version:")
|
||||||
System.out.println(v.body)
|
System.out.println(v.body)
|
||||||
System.out.println()
|
System.out.println()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,39 +22,39 @@ import java.io.FileInputStream
|
|||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
object Config {
|
object Config {
|
||||||
val APP_ID = 32860
|
val APP_ID = 32860
|
||||||
val APP_HASH = "16e4ff955cd0adfc058f95ca564f562d"
|
val APP_HASH = "16e4ff955cd0adfc058f95ca564f562d"
|
||||||
val APP_MODEL = "Desktop"
|
val APP_MODEL = "Desktop"
|
||||||
val APP_SYSVER = "1.0"
|
val APP_SYSVER = "1.0"
|
||||||
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 FILE_BASE = 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"
|
||||||
val FILE_NAME_DB_BACKUP = "database.version_%d.backup.sqlite"
|
val FILE_NAME_DB_BACKUP = "database.version_%d.backup.sqlite"
|
||||||
val FILE_FILES_BASE = "files"
|
val FILE_FILES_BASE = "files"
|
||||||
val FILE_STICKER_BASE = "stickers"
|
val FILE_STICKER_BASE = "stickers"
|
||||||
|
|
||||||
var DELAY_AFTER_GET_MESSAGES: Long = 400
|
var DELAY_AFTER_GET_MESSAGES: Long = 400
|
||||||
var DELAY_AFTER_GET_FILE: Long = 100
|
var DELAY_AFTER_GET_FILE: Long = 100
|
||||||
var GET_MESSAGES_BATCH_SIZE = 200
|
var GET_MESSAGES_BATCH_SIZE = 200
|
||||||
|
|
||||||
var RENAMING_MAX_TRIES = 5
|
var RENAMING_MAX_TRIES = 5
|
||||||
var RENAMING_DELAY: Long = 1000
|
var RENAMING_DELAY: Long = 1000
|
||||||
|
|
||||||
val SECRET_GMAPS = "AIzaSyBEtUDhCQKEH6i2Mn1GAiQ9M_tLN0vxHIs"
|
val SECRET_GMAPS = "AIzaSyBEtUDhCQKEH6i2Mn1GAiQ9M_tLN0vxHIs"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val p = Properties()
|
val p = Properties()
|
||||||
try {
|
try {
|
||||||
p.load(Config::class.java.getResourceAsStream("/build.properties"))
|
p.load(Config::class.java.getResourceAsStream("/build.properties"))
|
||||||
APP_APPVER = p.getProperty("version")
|
APP_APPVER = p.getProperty("version")
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,355 +17,355 @@ import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
|
|||||||
|
|
||||||
class DatabaseUpdates(protected var conn: Connection, protected var db: Database) {
|
class DatabaseUpdates(protected var conn: Connection, protected var db: Database) {
|
||||||
|
|
||||||
private val maxPossibleVersion: Int
|
private val maxPossibleVersion: Int
|
||||||
get() = updates.size
|
get() = updates.size
|
||||||
|
|
||||||
init {
|
init {
|
||||||
logger.debug("Registering Database Updates...")
|
logger.debug("Registering Database Updates...")
|
||||||
register(DB_Update_1(conn, db))
|
register(DB_Update_1(conn, db))
|
||||||
register(DB_Update_2(conn, db))
|
register(DB_Update_2(conn, db))
|
||||||
register(DB_Update_3(conn, db))
|
register(DB_Update_3(conn, db))
|
||||||
register(DB_Update_4(conn, db))
|
register(DB_Update_4(conn, db))
|
||||||
register(DB_Update_5(conn, db))
|
register(DB_Update_5(conn, db))
|
||||||
register(DB_Update_6(conn, db))
|
register(DB_Update_6(conn, db))
|
||||||
register(DB_Update_7(conn, db))
|
register(DB_Update_7(conn, db))
|
||||||
register(DB_Update_8(conn, db))
|
register(DB_Update_8(conn, db))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doUpdates() {
|
fun doUpdates() {
|
||||||
try {
|
try {
|
||||||
val stmt = conn.createStatement()
|
val stmt = conn.createStatement()
|
||||||
var rs: ResultSet
|
var rs: ResultSet
|
||||||
logger.debug("DatabaseUpdate.doUpdates running")
|
logger.debug("DatabaseUpdate.doUpdates running")
|
||||||
|
|
||||||
logger.debug("Getting current database version")
|
logger.debug("Getting current database version")
|
||||||
val version: Int
|
val version: Int
|
||||||
logger.debug("Checking if table database_versions exists")
|
logger.debug("Checking if table database_versions exists")
|
||||||
rs = stmt.executeQuery("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='database_versions'")
|
rs = stmt.executeQuery("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='database_versions'")
|
||||||
rs.next()
|
rs.next()
|
||||||
if (rs.getInt(1) == 0) {
|
if (rs.getInt(1) == 0) {
|
||||||
logger.debug("Table does not exist")
|
logger.debug("Table does not exist")
|
||||||
version = 0
|
version = 0
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Table exists. Checking max version")
|
logger.debug("Table exists. Checking max version")
|
||||||
rs.close()
|
rs.close()
|
||||||
rs = stmt.executeQuery("SELECT MAX(version) FROM database_versions")
|
rs = stmt.executeQuery("SELECT MAX(version) FROM database_versions")
|
||||||
rs.next()
|
rs.next()
|
||||||
version = rs.getInt(1)
|
version = rs.getInt(1)
|
||||||
}
|
}
|
||||||
rs.close()
|
rs.close()
|
||||||
logger.debug("version: {}", version)
|
logger.debug("version: {}", version)
|
||||||
System.out.println("Database version: " + version)
|
System.out.println("Database version: " + version)
|
||||||
logger.debug("Max available database version is {}", maxPossibleVersion)
|
logger.debug("Max available database version is {}", maxPossibleVersion)
|
||||||
|
|
||||||
if (version < maxPossibleVersion) {
|
if (version < maxPossibleVersion) {
|
||||||
logger.debug("Update is necessary. {} => {}.", version, maxPossibleVersion)
|
logger.debug("Update is necessary. {} => {}.", version, maxPossibleVersion)
|
||||||
var backup = false
|
var backup = false
|
||||||
for (i in version + 1..maxPossibleVersion) {
|
for (i in version + 1..maxPossibleVersion) {
|
||||||
if (getUpdateToVersion(i).needsBackup) {
|
if (getUpdateToVersion(i).needsBackup) {
|
||||||
logger.debug("Update to version {} needs a backup", i)
|
logger.debug("Update to version {} needs a backup", i)
|
||||||
backup = true
|
backup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (backup) {
|
if (backup) {
|
||||||
if (version > 0) {
|
if (version > 0) {
|
||||||
logger.debug("Performing backup")
|
logger.debug("Performing backup")
|
||||||
db.backupDatabase(version)
|
db.backupDatabase(version)
|
||||||
} else {
|
} else {
|
||||||
logger.debug("NOT performing a backup, because we are creating a fresh database and don't need a backup of that.")
|
logger.debug("NOT performing a backup, because we are creating a fresh database and don't need a backup of that.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Applying updates")
|
logger.debug("Applying updates")
|
||||||
try {
|
try {
|
||||||
for (i in version + 1..maxPossibleVersion) {
|
for (i in version + 1..maxPossibleVersion) {
|
||||||
getUpdateToVersion(i).doUpdate()
|
getUpdateToVersion(i).doUpdate()
|
||||||
}
|
}
|
||||||
} catch (e: SQLException) {
|
} catch (e: SQLException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
logger.debug("No update necessary.")
|
logger.debug("No update necessary.")
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: SQLException) {
|
} catch (e: SQLException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getUpdateToVersion(i: Int): DatabaseUpdate {
|
private fun getUpdateToVersion(i: Int): DatabaseUpdate {
|
||||||
return updates.get(i - 1)
|
return updates.get(i - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun register(d: DatabaseUpdate) {
|
private fun register(d: DatabaseUpdate) {
|
||||||
logger.debug("Registering {} as update to version {}", d.javaClass, d.version)
|
logger.debug("Registering {} as update to version {}", d.javaClass, d.version)
|
||||||
if (d.version != updates.size + 1) {
|
if (d.version != updates.size + 1) {
|
||||||
throw RuntimeException("Tried to register DB update to version ${d.version}, but would need update to version ${updates.size + 1}")
|
throw RuntimeException("Tried to register DB update to version ${d.version}, but would need update to version ${updates.size + 1}")
|
||||||
}
|
}
|
||||||
updates.add(d)
|
updates.add(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(DatabaseUpdates::class.java)
|
private val logger = LoggerFactory.getLogger(DatabaseUpdates::class.java)
|
||||||
private val updates = LinkedList<DatabaseUpdate>()
|
private val updates = LinkedList<DatabaseUpdate>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class DatabaseUpdate(protected var conn: Connection, protected var db: Database) {
|
internal abstract class DatabaseUpdate(protected var conn: Connection, protected var db: Database) {
|
||||||
protected var stmt: Statement
|
protected var stmt: Statement
|
||||||
abstract val version: Int
|
abstract val version: Int
|
||||||
|
|
||||||
init {
|
init {
|
||||||
try {
|
try {
|
||||||
stmt = conn.createStatement()
|
stmt = conn.createStatement()
|
||||||
} catch (e: SQLException) {
|
} catch (e: SQLException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
fun doUpdate() {
|
fun doUpdate() {
|
||||||
logger.debug("Applying update to version {}", version)
|
logger.debug("Applying update to version {}", version)
|
||||||
System.out.println(" Updating to version $version...")
|
System.out.println(" Updating to version $version...")
|
||||||
_doUpdate()
|
_doUpdate()
|
||||||
logger.debug("Saving current database version to the db")
|
logger.debug("Saving current database version to the db")
|
||||||
stmt.executeUpdate("INSERT INTO database_versions (version) VALUES ($version)")
|
stmt.executeUpdate("INSERT INTO database_versions (version) VALUES ($version)")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
protected abstract fun _doUpdate()
|
protected abstract fun _doUpdate()
|
||||||
|
|
||||||
open val needsBackup = false
|
open val needsBackup = false
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
protected fun execute(sql: String) {
|
protected fun execute(sql: String) {
|
||||||
logger.debug("Executing: {}", sql)
|
logger.debug("Executing: {}", sql)
|
||||||
stmt.executeUpdate(sql)
|
stmt.executeUpdate(sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
protected val logger = LoggerFactory.getLogger(DatabaseUpdate::class.java)
|
protected val logger = LoggerFactory.getLogger(DatabaseUpdate::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DB_Update_1(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
internal class DB_Update_1(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
||||||
override val version: Int
|
override val version: Int
|
||||||
get() = 1
|
get() = 1
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
override fun _doUpdate() {
|
override fun _doUpdate() {
|
||||||
stmt.executeUpdate("CREATE TABLE messages ("
|
stmt.executeUpdate("CREATE TABLE messages ("
|
||||||
+ "id INTEGER PRIMARY KEY ASC, "
|
+ "id INTEGER PRIMARY KEY ASC, "
|
||||||
+ "dialog_id INTEGER, "
|
+ "dialog_id INTEGER, "
|
||||||
+ "to_id INTEGER, "
|
+ "to_id INTEGER, "
|
||||||
+ "from_id INTEGER, "
|
+ "from_id INTEGER, "
|
||||||
+ "from_type TEXT, "
|
+ "from_type TEXT, "
|
||||||
+ "text TEXT, "
|
+ "text TEXT, "
|
||||||
+ "time TEXT, "
|
+ "time TEXT, "
|
||||||
+ "has_media BOOLEAN, "
|
+ "has_media BOOLEAN, "
|
||||||
+ "sticker TEXT, "
|
+ "sticker TEXT, "
|
||||||
+ "data BLOB,"
|
+ "data BLOB,"
|
||||||
+ "type TEXT)")
|
+ "type TEXT)")
|
||||||
stmt.executeUpdate("CREATE TABLE dialogs ("
|
stmt.executeUpdate("CREATE TABLE dialogs ("
|
||||||
+ "id INTEGER PRIMARY KEY ASC, "
|
+ "id INTEGER PRIMARY KEY ASC, "
|
||||||
+ "name TEXT, "
|
+ "name TEXT, "
|
||||||
+ "type TEXT)")
|
+ "type TEXT)")
|
||||||
stmt.executeUpdate("CREATE TABLE people ("
|
stmt.executeUpdate("CREATE TABLE people ("
|
||||||
+ "id INTEGER PRIMARY KEY ASC, "
|
+ "id INTEGER PRIMARY KEY ASC, "
|
||||||
+ "first_name TEXT, "
|
+ "first_name TEXT, "
|
||||||
+ "last_name TEXT, "
|
+ "last_name TEXT, "
|
||||||
+ "username TEXT, "
|
+ "username TEXT, "
|
||||||
+ "type TEXT)")
|
+ "type TEXT)")
|
||||||
stmt.executeUpdate("CREATE TABLE database_versions (" + "version INTEGER)")
|
stmt.executeUpdate("CREATE TABLE database_versions (" + "version INTEGER)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DB_Update_2(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
internal class DB_Update_2(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
||||||
override val version: Int
|
override val version: Int
|
||||||
get() = 2
|
get() = 2
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
override fun _doUpdate() {
|
override fun _doUpdate() {
|
||||||
stmt.executeUpdate("ALTER TABLE people RENAME TO 'users'")
|
stmt.executeUpdate("ALTER TABLE people RENAME TO 'users'")
|
||||||
stmt.executeUpdate("ALTER TABLE users ADD COLUMN phone TEXT")
|
stmt.executeUpdate("ALTER TABLE users ADD COLUMN phone TEXT")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DB_Update_3(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
internal class DB_Update_3(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
||||||
override val version: Int
|
override val version: Int
|
||||||
get() = 3
|
get() = 3
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
override fun _doUpdate() {
|
override fun _doUpdate() {
|
||||||
stmt.executeUpdate("ALTER TABLE dialogs RENAME TO 'chats'")
|
stmt.executeUpdate("ALTER TABLE dialogs RENAME TO 'chats'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DB_Update_4(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
internal class DB_Update_4(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
||||||
override val version: Int
|
override val version: Int
|
||||||
get() = 4
|
get() = 4
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
override fun _doUpdate() {
|
override fun _doUpdate() {
|
||||||
stmt.executeUpdate("CREATE TABLE messages_new (id INTEGER PRIMARY KEY ASC, dialog_id INTEGER, to_id INTEGER, from_id INTEGER, from_type TEXT, text TEXT, time INTEGER, has_media BOOLEAN, sticker TEXT, data BLOB, type TEXT);")
|
stmt.executeUpdate("CREATE TABLE messages_new (id INTEGER PRIMARY KEY ASC, dialog_id INTEGER, to_id INTEGER, from_id INTEGER, from_type TEXT, text TEXT, time INTEGER, has_media BOOLEAN, sticker TEXT, data BLOB, type TEXT);")
|
||||||
stmt.executeUpdate("INSERT INTO messages_new SELECT * FROM messages")
|
stmt.executeUpdate("INSERT INTO messages_new SELECT * FROM messages")
|
||||||
stmt.executeUpdate("DROP TABLE messages")
|
stmt.executeUpdate("DROP TABLE messages")
|
||||||
stmt.executeUpdate("ALTER TABLE messages_new RENAME TO 'messages'")
|
stmt.executeUpdate("ALTER TABLE messages_new RENAME TO 'messages'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DB_Update_5(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
internal class DB_Update_5(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
||||||
override val version: Int
|
override val version: Int
|
||||||
get() = 5
|
get() = 5
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
override fun _doUpdate() {
|
override fun _doUpdate() {
|
||||||
stmt.executeUpdate("CREATE TABLE runs (id INTEGER PRIMARY KEY ASC, time INTEGER, start_id INTEGER, end_id INTEGER, count_missing INTEGER)")
|
stmt.executeUpdate("CREATE TABLE runs (id INTEGER PRIMARY KEY ASC, time INTEGER, start_id INTEGER, end_id INTEGER, count_missing INTEGER)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DB_Update_6(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
internal class DB_Update_6(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
||||||
override val version: Int
|
override val version: Int
|
||||||
get() = 6
|
get() = 6
|
||||||
|
|
||||||
override val needsBackup = true
|
override val needsBackup = true
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
override fun _doUpdate() {
|
override fun _doUpdate() {
|
||||||
stmt.executeUpdate(
|
stmt.executeUpdate(
|
||||||
"CREATE TABLE messages_new (\n" +
|
"CREATE TABLE messages_new (\n" +
|
||||||
" id INTEGER PRIMARY KEY ASC,\n" +
|
" id INTEGER PRIMARY KEY ASC,\n" +
|
||||||
" message_type TEXT,\n" +
|
" message_type TEXT,\n" +
|
||||||
" dialog_id INTEGER,\n" +
|
" dialog_id INTEGER,\n" +
|
||||||
" chat_id INTEGER,\n" +
|
" chat_id INTEGER,\n" +
|
||||||
" sender_id INTEGER,\n" +
|
" sender_id INTEGER,\n" +
|
||||||
" fwd_from_id INTEGER,\n" +
|
" fwd_from_id INTEGER,\n" +
|
||||||
" text TEXT,\n" +
|
" text TEXT,\n" +
|
||||||
" time INTEGER,\n" +
|
" time INTEGER,\n" +
|
||||||
" has_media BOOLEAN,\n" +
|
" has_media BOOLEAN,\n" +
|
||||||
" media_type TEXT,\n" +
|
" media_type TEXT,\n" +
|
||||||
" media_file TEXT,\n" +
|
" media_file TEXT,\n" +
|
||||||
" media_size INTEGER,\n" +
|
" media_size INTEGER,\n" +
|
||||||
" media_json TEXT,\n" +
|
" media_json TEXT,\n" +
|
||||||
" markup_json TEXT,\n" +
|
" markup_json TEXT,\n" +
|
||||||
" data BLOB)")
|
" data BLOB)")
|
||||||
val mappings = LinkedHashMap<String, String>()
|
val mappings = LinkedHashMap<String, String>()
|
||||||
mappings.put("id", "id")
|
mappings.put("id", "id")
|
||||||
mappings.put("message_type", "type")
|
mappings.put("message_type", "type")
|
||||||
mappings.put("dialog_id", "CASE from_type WHEN 'user' THEN dialog_id ELSE NULL END")
|
mappings.put("dialog_id", "CASE from_type WHEN 'user' THEN dialog_id ELSE NULL END")
|
||||||
mappings.put("chat_id", "CASE from_type WHEN 'chat' THEN dialog_id ELSE NULL END")
|
mappings.put("chat_id", "CASE from_type WHEN 'chat' THEN dialog_id ELSE NULL END")
|
||||||
mappings.put("sender_id", "from_id")
|
mappings.put("sender_id", "from_id")
|
||||||
mappings.put("text", "text")
|
mappings.put("text", "text")
|
||||||
mappings.put("time", "time")
|
mappings.put("time", "time")
|
||||||
mappings.put("has_media", "has_media")
|
mappings.put("has_media", "has_media")
|
||||||
mappings.put("data", "data")
|
mappings.put("data", "data")
|
||||||
val query = StringBuilder("INSERT INTO messages_new\n(")
|
val query = StringBuilder("INSERT INTO messages_new\n(")
|
||||||
var first: Boolean
|
var first: Boolean
|
||||||
first = true
|
first = true
|
||||||
for (s in mappings.keys) {
|
for (s in mappings.keys) {
|
||||||
if (!first) query.append(", ")
|
if (!first) query.append(", ")
|
||||||
query.append(s)
|
query.append(s)
|
||||||
first = false
|
first = false
|
||||||
}
|
}
|
||||||
query.append(")\nSELECT \n")
|
query.append(")\nSELECT \n")
|
||||||
first = true
|
first = true
|
||||||
for (s in mappings.values) {
|
for (s in mappings.values) {
|
||||||
if (!first) query.append(", ")
|
if (!first) query.append(", ")
|
||||||
query.append(s)
|
query.append(s)
|
||||||
first = false
|
first = false
|
||||||
}
|
}
|
||||||
query.append("\nFROM messages")
|
query.append("\nFROM messages")
|
||||||
stmt.executeUpdate(query.toString())
|
stmt.executeUpdate(query.toString())
|
||||||
|
|
||||||
System.out.println(" Updating the data (this might take some time)...")
|
System.out.println(" Updating the data (this might take some time)...")
|
||||||
val rs = stmt.executeQuery("SELECT id, data FROM messages_new")
|
val rs = stmt.executeQuery("SELECT id, data FROM messages_new")
|
||||||
val ps = conn.prepareStatement("UPDATE messages_new SET fwd_from_id=?, media_type=?, media_file=?, media_size=? WHERE id=?")
|
val ps = conn.prepareStatement("UPDATE messages_new SET fwd_from_id=?, media_type=?, media_file=?, media_size=? WHERE id=?")
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
ps.setInt(5, rs.getInt(1))
|
ps.setInt(5, rs.getInt(1))
|
||||||
val msg = Database.bytesToTLMessage(rs.getBytes(2))
|
val msg = Database.bytesToTLMessage(rs.getBytes(2))
|
||||||
if (msg == null || msg.getFwdFrom() == null) {
|
if (msg == null || msg.getFwdFrom() == null) {
|
||||||
ps.setNull(1, Types.INTEGER)
|
ps.setNull(1, Types.INTEGER)
|
||||||
} 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.client)
|
||||||
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)
|
||||||
ps.setNull(4, Types.INTEGER)
|
ps.setNull(4, Types.INTEGER)
|
||||||
} else {
|
} else {
|
||||||
ps.setString(2, f.name)
|
ps.setString(2, f.name)
|
||||||
ps.setString(3, f.targetFilename)
|
ps.setString(3, f.targetFilename)
|
||||||
ps.setInt(4, f.size)
|
ps.setInt(4, f.size)
|
||||||
}
|
}
|
||||||
ps.addBatch()
|
ps.addBatch()
|
||||||
}
|
}
|
||||||
rs.close()
|
rs.close()
|
||||||
conn.setAutoCommit(false)
|
conn.setAutoCommit(false)
|
||||||
ps.executeBatch()
|
ps.executeBatch()
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.setAutoCommit(true)
|
conn.setAutoCommit(true)
|
||||||
stmt.executeUpdate("DROP TABLE messages")
|
stmt.executeUpdate("DROP TABLE messages")
|
||||||
stmt.executeUpdate("ALTER TABLE messages_new RENAME TO messages")
|
stmt.executeUpdate("ALTER TABLE messages_new RENAME TO messages")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DB_Update_7(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
internal class DB_Update_7(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
||||||
override val version: Int
|
override val version: Int
|
||||||
get() = 7
|
get() = 7
|
||||||
|
|
||||||
override val needsBackup = true
|
override val needsBackup = true
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
override fun _doUpdate() {
|
override fun _doUpdate() {
|
||||||
stmt.executeUpdate("ALTER TABLE messages ADD COLUMN api_layer INTEGER")
|
stmt.executeUpdate("ALTER TABLE messages ADD COLUMN api_layer INTEGER")
|
||||||
|
|
||||||
stmt.executeUpdate("UPDATE messages SET api_layer=51")
|
stmt.executeUpdate("UPDATE messages SET api_layer=51")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DB_Update_8(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
internal class DB_Update_8(conn: Connection, db: Database) : DatabaseUpdate(conn, db) {
|
||||||
override val version: Int
|
override val version: Int
|
||||||
get() = 8
|
get() = 8
|
||||||
|
|
||||||
override val needsBackup = true
|
override val needsBackup = true
|
||||||
|
|
||||||
@Throws(SQLException::class)
|
@Throws(SQLException::class)
|
||||||
override fun _doUpdate() {
|
override fun _doUpdate() {
|
||||||
execute("ALTER TABLE messages ADD COLUMN source_type TEXT")
|
execute("ALTER TABLE messages ADD COLUMN source_type TEXT")
|
||||||
execute("ALTER TABLE messages ADD COLUMN source_id INTEGER")
|
execute("ALTER TABLE messages ADD COLUMN source_id INTEGER")
|
||||||
execute("update messages set source_type='dialog', source_id=dialog_id where dialog_id is not null")
|
execute("update messages set source_type='dialog', source_id=dialog_id where dialog_id is not null")
|
||||||
execute("update messages set source_type='group', source_id=chat_id where chat_id is not null")
|
execute("update messages set source_type='group', source_id=chat_id where chat_id is not null")
|
||||||
|
|
||||||
execute("CREATE TABLE messages_new (" +
|
execute("CREATE TABLE messages_new (" +
|
||||||
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||||
"message_id INTEGER," +
|
"message_id INTEGER," +
|
||||||
"message_type TEXT," +
|
"message_type TEXT," +
|
||||||
"source_type TEXT," +
|
"source_type TEXT," +
|
||||||
"source_id INTEGER," +
|
"source_id INTEGER," +
|
||||||
"sender_id INTEGER," +
|
"sender_id INTEGER," +
|
||||||
"fwd_from_id INTEGER," +
|
"fwd_from_id INTEGER," +
|
||||||
"text TEXT," +
|
"text TEXT," +
|
||||||
"time INTEGER," +
|
"time INTEGER," +
|
||||||
"has_media BOOLEAN," +
|
"has_media BOOLEAN," +
|
||||||
"media_type TEXT," +
|
"media_type TEXT," +
|
||||||
"media_file TEXT," +
|
"media_file TEXT," +
|
||||||
"media_size INTEGER," +
|
"media_size INTEGER," +
|
||||||
"media_json TEXT," +
|
"media_json TEXT," +
|
||||||
"markup_json TEXT," +
|
"markup_json TEXT," +
|
||||||
"data BLOB," +
|
"data BLOB," +
|
||||||
"api_layer INTEGER)")
|
"api_layer INTEGER)")
|
||||||
execute("INSERT INTO messages_new" +
|
execute("INSERT INTO messages_new" +
|
||||||
"(message_id, message_type, source_type, source_id, sender_id, fwd_from_id, text, time, has_media, media_type," +
|
"(message_id, message_type, source_type, source_id, sender_id, fwd_from_id, text, time, has_media, media_type," +
|
||||||
"media_file, media_size, media_json, markup_json, data, api_layer)" +
|
"media_file, media_size, media_json, markup_json, data, api_layer)" +
|
||||||
"SELECT " +
|
"SELECT " +
|
||||||
"id, message_type, source_type, source_id, sender_id, fwd_from_id, text, time, has_media, media_type," +
|
"id, message_type, source_type, source_id, sender_id, fwd_from_id, text, time, has_media, media_type," +
|
||||||
"media_file, media_size, media_json, markup_json, data, api_layer FROM messages")
|
"media_file, media_size, media_json, markup_json, data, api_layer FROM messages")
|
||||||
execute("DROP TABLE messages")
|
execute("DROP TABLE messages")
|
||||||
execute("ALTER TABLE messages_new RENAME TO 'messages'")
|
execute("ALTER TABLE messages_new RENAME TO 'messages'")
|
||||||
execute("CREATE UNIQUE INDEX unique_messages ON messages (source_type, source_id, message_id)")
|
execute("CREATE UNIQUE INDEX unique_messages ON messages (source_type, source_id, message_id)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,102 +53,102 @@ import java.nio.file.StandardCopyOption
|
|||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressInterface) {
|
class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressInterface) {
|
||||||
internal var user: UserManager? = null
|
internal var user: UserManager? = null
|
||||||
internal var db: Database? = null
|
internal var db: Database? = null
|
||||||
internal var prog: DownloadProgressInterface? = null
|
internal var prog: DownloadProgressInterface? = null
|
||||||
internal var has_seen_flood_wait_message = false
|
internal var has_seen_flood_wait_message = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.user = UserManager.getInstance()
|
this.user = UserManager.getInstance()
|
||||||
this.prog = p
|
this.prog = p
|
||||||
this.db = Database.getInstance()
|
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
|
||||||
do {
|
do {
|
||||||
completed = true
|
completed = true
|
||||||
try {
|
try {
|
||||||
_downloadMessages(limit)
|
_downloadMessages(limit)
|
||||||
} catch (e: RpcErrorException) {
|
} catch (e: RpcErrorException) {
|
||||||
if (e.getCode() == 420) { // FLOOD_WAIT
|
if (e.getCode() == 420) { // FLOOD_WAIT
|
||||||
completed = false
|
completed = false
|
||||||
Utils.obeyFloodWaitException(e)
|
Utils.obeyFloodWaitException(e)
|
||||||
} else {
|
} else {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
} catch (e: TimeoutException) {
|
} catch (e: TimeoutException) {
|
||||||
completed = false
|
completed = false
|
||||||
System.out.println("")
|
System.out.println("")
|
||||||
System.out.println("Telegram took too long to respond to our request.")
|
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.")
|
System.out.println("I'm going to wait a minute and then try again.")
|
||||||
try {
|
try {
|
||||||
TimeUnit.MINUTES.sleep(1)
|
TimeUnit.MINUTES.sleep(1)
|
||||||
} catch (e2: InterruptedException) {
|
} catch (e2: InterruptedException) {
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("")
|
System.out.println("")
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (!completed)
|
} while (!completed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
||||||
fun _downloadMessages(limit: Int?) {
|
fun _downloadMessages(limit: Int?) {
|
||||||
logger.info("This is _downloadMessages with limit {}", limit)
|
logger.info("This is _downloadMessages with limit {}", limit)
|
||||||
val dialog_limit = 100
|
val dialog_limit = 100
|
||||||
logger.info("Downloading the last {} dialogs", dialog_limit)
|
logger.info("Downloading the last {} dialogs", dialog_limit)
|
||||||
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 dialogs = client!!.messagesGetDialogs(
|
val dialogs = client!!.messagesGetDialogs(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
TLInputPeerEmpty(),
|
TLInputPeerEmpty(),
|
||||||
dialog_limit)
|
dialog_limit)
|
||||||
logger.debug("Got {} dialogs", dialogs.getDialogs().size)
|
logger.debug("Got {} dialogs", dialogs.getDialogs().size)
|
||||||
|
|
||||||
for (d in dialogs.getDialogs()) {
|
for (d in dialogs.getDialogs()) {
|
||||||
if (d.getTopMessage() > max_message_id && d.getPeer() !is TLPeerChannel) {
|
if (d.getTopMessage() > max_message_id && d.getPeer() !is TLPeerChannel) {
|
||||||
logger.trace("Updating top message id: {} => {}. Dialog type: {}", max_message_id, d.getTopMessage(), d.getPeer().javaClass)
|
logger.trace("Updating top message id: {} => {}. Dialog type: {}", max_message_id, d.getTopMessage(), d.getPeer().javaClass)
|
||||||
max_message_id = d.getTopMessage()
|
max_message_id = d.getTopMessage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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)
|
||||||
max_database_id = Math.max(max_database_id, max_message_id - limit)
|
max_database_id = Math.max(max_database_id, max_message_id - limit)
|
||||||
System.out.println("New top message id 'in database' is " + max_database_id)
|
System.out.println("New top message id 'in database' is " + max_database_id)
|
||||||
}
|
}
|
||||||
if (max_message_id - max_database_id > 1000000) {
|
if (max_message_id - max_database_id > 1000000) {
|
||||||
System.out.println("Would have to load more than 1 million messages which is not supported by telegram. Capping the list.")
|
System.out.println("Would have to load more than 1 million messages which is not supported by telegram. Capping the list.")
|
||||||
logger.debug("max_message_id={}, max_database_id={}, difference={}", max_message_id, max_database_id, max_message_id - max_database_id)
|
logger.debug("max_message_id={}, max_database_id={}, difference={}", max_message_id, max_database_id, max_message_id - max_database_id)
|
||||||
max_database_id = Math.max(0, max_message_id - 1000000)
|
max_database_id = Math.max(0, max_message_id - 1000000)
|
||||||
logger.debug("new max_database_id: {}", max_database_id)
|
logger.debug("new max_database_id: {}", max_database_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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.")
|
||||||
} 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
|
||||||
|
|
||||||
val ids = makeIdList(start_id, end_id)
|
val ids = makeIdList(start_id, end_id)
|
||||||
downloadMessages(ids, null, null)
|
downloadMessages(ids, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
/*if (db_count != db_max) {
|
/*if (db_count != db_max) {
|
||||||
if (limit != null) {
|
if (limit != null) {
|
||||||
System.out.println("You are missing messages in your database. But since you're using '--limit-messages', I won't download these now.");
|
System.out.println("You are missing messages in your database. But since you're using '--limit-messages', I won't download these now.");
|
||||||
} else {
|
} else {
|
||||||
@ -169,138 +169,138 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (CommandLineOptions.cmd_channels || CommandLineOptions.cmd_supergroups) {
|
if (CommandLineOptions.cmd_channels || CommandLineOptions.cmd_supergroups) {
|
||||||
System.out.println("Processing channels and/or supergroups...")
|
System.out.println("Processing channels and/or supergroups...")
|
||||||
System.out.println("Please note that only channels/supergroups in the last 100 active chats are processed.")
|
System.out.println("Please note that only channels/supergroups in the last 100 active chats are processed.")
|
||||||
|
|
||||||
val channel_access_hashes = HashMap<Int, Long>()
|
val channel_access_hashes = HashMap<Int, Long>()
|
||||||
val channel_names = HashMap<Int, String>()
|
val channel_names = HashMap<Int, String>()
|
||||||
val channels = LinkedList<Int>()
|
val channels = LinkedList<Int>()
|
||||||
val supergroups = LinkedList<Int>()
|
val supergroups = LinkedList<Int>()
|
||||||
|
|
||||||
// TODO Add chat title (and other stuff?) to the database
|
// TODO Add chat title (and other stuff?) to the database
|
||||||
for (c in dialogs.getChats()) {
|
for (c in dialogs.getChats()) {
|
||||||
if (c is TLChannel) {
|
if (c is TLChannel) {
|
||||||
channel_access_hashes.put(c.getId(), c.getAccessHash())
|
channel_access_hashes.put(c.getId(), c.getAccessHash())
|
||||||
channel_names.put(c.getId(), c.getTitle())
|
channel_names.put(c.getId(), c.getTitle())
|
||||||
if (c.getMegagroup()) {
|
if (c.getMegagroup()) {
|
||||||
supergroups.add(c.getId())
|
supergroups.add(c.getId())
|
||||||
} else {
|
} else {
|
||||||
channels.add(c.getId())
|
channels.add(c.getId())
|
||||||
}
|
}
|
||||||
// Channel: TLChannel
|
// Channel: TLChannel
|
||||||
// Supergroup: getMegagroup()==true
|
// Supergroup: getMegagroup()==true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (d in dialogs.getDialogs()) {
|
for (d in dialogs.getDialogs()) {
|
||||||
if (d.getPeer() is TLPeerChannel) {
|
if (d.getPeer() is TLPeerChannel) {
|
||||||
val channel_id = (d.getPeer() as TLPeerChannel).getChannelId()
|
val channel_id = (d.getPeer() as TLPeerChannel).getChannelId()
|
||||||
|
|
||||||
// If this is a channel and we don't want to download channels OR
|
// If this is a channel and we don't want to download channels OR
|
||||||
// it is a supergroups and we don't want to download supergroups, then
|
// it is a supergroups and we don't want to download supergroups, then
|
||||||
if (channels.contains(channel_id) && !CommandLineOptions.cmd_channels || supergroups.contains(channel_id) && !CommandLineOptions.cmd_supergroups) {
|
if (channels.contains(channel_id) && !CommandLineOptions.cmd_channels || supergroups.contains(channel_id) && !CommandLineOptions.cmd_supergroups) {
|
||||||
// Skip this chat.
|
// Skip this chat.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val max_known_id = db!!.getTopMessageIDForChannel(channel_id)
|
val max_known_id = db!!.getTopMessageIDForChannel(channel_id)
|
||||||
if (d.getTopMessage() > max_known_id) {
|
if (d.getTopMessage() > max_known_id) {
|
||||||
val ids = makeIdList(max_known_id + 1, d.getTopMessage())
|
val ids = makeIdList(max_known_id + 1, d.getTopMessage())
|
||||||
val access_hash = channel_access_hashes.get(channel_id) ?: throw RuntimeException("AccessHash for Channel missing.")
|
val access_hash = channel_access_hashes.get(channel_id) ?: throw RuntimeException("AccessHash for Channel missing.")
|
||||||
var channel_name = channel_names.get(channel_id)
|
var channel_name = channel_names.get(channel_id)
|
||||||
if (channel_name == null) {
|
if (channel_name == null) {
|
||||||
channel_name = "?"
|
channel_name = "?"
|
||||||
}
|
}
|
||||||
val channel = TLInputChannel(channel_id, access_hash)
|
val channel = TLInputChannel(channel_id, access_hash)
|
||||||
downloadMessages(ids, channel, "channel $channel_name")
|
downloadMessages(ids, channel, "channel $channel_name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
@Throws(RpcErrorException::class, IOException::class)
|
||||||
private fun downloadMessages(ids: MutableList<Int>, channel: TLInputChannel?, source_string: String?) {
|
private fun downloadMessages(ids: MutableList<Int>, channel: TLInputChannel?, source_string: String?) {
|
||||||
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) {
|
||||||
logger.trace("Loop")
|
logger.trace("Loop")
|
||||||
val vector = TLIntVector()
|
val vector = TLIntVector()
|
||||||
val download_count = Config.GET_MESSAGES_BATCH_SIZE
|
val download_count = Config.GET_MESSAGES_BATCH_SIZE
|
||||||
logger.trace("download_count: {}", download_count)
|
logger.trace("download_count: {}", download_count)
|
||||||
for (i in 0 until download_count) {
|
for (i in 0 until download_count) {
|
||||||
if (ids.size == 0) break
|
if (ids.size == 0) break
|
||||||
vector.add(ids.removeAt(0))
|
vector.add(ids.removeAt(0))
|
||||||
}
|
}
|
||||||
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 response: TLAbsMessages
|
||||||
var tries = 0
|
var tries = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
logger.trace("Trying getMessages(), tries={}", tries)
|
logger.trace("Trying getMessages(), tries={}", tries)
|
||||||
if (tries >= 5) {
|
if (tries >= 5) {
|
||||||
CommandLineController.show_error("Couldn't getMessages after 5 tries. Quitting.")
|
CommandLineController.show_error("Couldn't getMessages after 5 tries. Quitting.")
|
||||||
}
|
}
|
||||||
tries++
|
tries++
|
||||||
try {
|
try {
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
response = client!!.messagesGetMessages(vector)
|
response = client!!.messagesGetMessages(vector)
|
||||||
} else {
|
} else {
|
||||||
response = client!!.channelsGetMessages(channel, vector)
|
response = client!!.channelsGetMessages(channel, vector)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
} catch (e: RpcErrorException) {
|
} catch (e: RpcErrorException) {
|
||||||
if (e.getCode() == 420) { // FLOOD_WAIT
|
if (e.getCode() == 420) { // FLOOD_WAIT
|
||||||
Utils.obeyFloodWaitException(e, has_seen_flood_wait_message)
|
Utils.obeyFloodWaitException(e, has_seen_flood_wait_message)
|
||||||
has_seen_flood_wait_message = true
|
has_seen_flood_wait_message = true
|
||||||
} else {
|
} else {
|
||||||
throw e
|
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)
|
db!!.saveMessages(response.getMessages(), Kotlogram.API_LAYER)
|
||||||
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)
|
TimeUnit.MILLISECONDS.sleep(Config.DELAY_AFTER_GET_MESSAGES)
|
||||||
} catch (e: InterruptedException) {
|
} 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
|
var completed: Boolean
|
||||||
do {
|
do {
|
||||||
completed = true
|
completed = true
|
||||||
try {
|
try {
|
||||||
_downloadMedia()
|
_downloadMedia()
|
||||||
} catch (e: RpcErrorException) {
|
} catch (e: RpcErrorException) {
|
||||||
if (e.getTag().startsWith("420: FLOOD_WAIT_")) {
|
if (e.getTag().startsWith("420: FLOOD_WAIT_")) {
|
||||||
completed = false
|
completed = false
|
||||||
Utils.obeyFloodWaitException(e)
|
Utils.obeyFloodWaitException(e)
|
||||||
} else {
|
} else {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*catch (TimeoutException e) {
|
/*catch (TimeoutException e) {
|
||||||
completed = false;
|
completed = false;
|
||||||
System.out.println("");
|
System.out.println("");
|
||||||
System.out.println("Telegram took too long to respond to our request.");
|
System.out.println("Telegram took too long to respond to our request.");
|
||||||
@ -309,181 +309,181 @@ class DownloadManager(internal var client: TelegramClient?, p: DownloadProgressI
|
|||||||
try { TimeUnit.MINUTES.sleep(1); } catch(InterruptedException e2) {}
|
try { TimeUnit.MINUTES.sleep(1); } catch(InterruptedException e2) {}
|
||||||
System.out.println("");
|
System.out.println("");
|
||||||
}*/
|
}*/
|
||||||
} while (!completed)
|
} while (!completed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
@Throws(RpcErrorException::class, IOException::class)
|
||||||
private fun _downloadMedia() {
|
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, null)
|
downloadMessages(ids, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val messages = this.db!!.getMessagesWithMedia()
|
val messages = this.db!!.getMessagesWithMedia()
|
||||||
logger.debug("Database returned {} messages with media", messages.size)
|
logger.debug("Database returned {} messages with media", messages.size)
|
||||||
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!!, client!!)
|
||||||
logger.trace("message {}, {}, {}, {}, {}",
|
logger.trace("message {}, {}, {}, {}, {}",
|
||||||
msg.getId(),
|
msg.getId(),
|
||||||
msg.getMedia().javaClass.getSimpleName().replace("TLMessageMedia", "…"),
|
msg.getMedia().javaClass.getSimpleName().replace("TLMessageMedia", "…"),
|
||||||
m!!.javaClass.getSimpleName(),
|
m!!.javaClass.getSimpleName(),
|
||||||
if (m.isEmpty) "empty" else "non-empty",
|
if (m.isEmpty) "empty" else "non-empty",
|
||||||
if (m.downloaded) "downloaded" else "not downloaded")
|
if (m.downloaded) "downloaded" else "not downloaded")
|
||||||
if (m.isEmpty) {
|
if (m.isEmpty) {
|
||||||
prog!!.onMediaDownloadedEmpty()
|
prog!!.onMediaDownloadedEmpty()
|
||||||
} else if (m.downloaded) {
|
} else if (m.downloaded) {
|
||||||
prog!!.onMediaAlreadyPresent(m)
|
prog!!.onMediaAlreadyPresent(m)
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
m.download()
|
m.download()
|
||||||
prog!!.onMediaDownloaded(m)
|
prog!!.onMediaDownloaded(m)
|
||||||
} 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> {
|
||||||
val a = LinkedList<Int>()
|
val a = LinkedList<Int>()
|
||||||
for (i in start..end) a.add(i)
|
for (i in start..end) a.add(i)
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
internal var download_client: TelegramClient? = null
|
internal var download_client: TelegramClient? = null
|
||||||
internal var last_download_succeeded = true
|
internal var last_download_succeeded = true
|
||||||
internal val logger = LoggerFactory.getLogger(DownloadManager::class.java)
|
internal val logger = LoggerFactory.getLogger(DownloadManager::class.java)
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
||||||
fun downloadFile(targetFilename: String, size: Int, dcId: Int, volumeId: Long, localId: Int, secret: Long) {
|
fun downloadFile(targetFilename: String, size: Int, dcId: Int, volumeId: Long, localId: Int, secret: Long) {
|
||||||
val loc = TLInputFileLocation(volumeId, localId, secret)
|
val loc = TLInputFileLocation(volumeId, localId, secret)
|
||||||
downloadFileFromDc(targetFilename, loc, dcId, size)
|
downloadFileFromDc(targetFilename, loc, dcId, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
||||||
fun downloadFile(targetFilename: String, size: Int, dcId: Int, id: Long, accessHash: Long) {
|
fun downloadFile(targetFilename: String, size: Int, dcId: Int, id: Long, accessHash: Long) {
|
||||||
val loc = TLInputDocumentFileLocation(id, accessHash)
|
val loc = TLInputDocumentFileLocation(id, accessHash)
|
||||||
downloadFileFromDc(targetFilename, loc, dcId, size)
|
downloadFileFromDc(targetFilename, loc, dcId, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
||||||
private fun downloadFileFromDc(target: String, loc: TLAbsInputFileLocation, dcID: Int, size: Int): Boolean {
|
private fun downloadFileFromDc(target: String, loc: TLAbsInputFileLocation, dcID: Int, size: Int): Boolean {
|
||||||
var fos: FileOutputStream? = null
|
var fos: FileOutputStream? = null
|
||||||
try {
|
try {
|
||||||
val temp_filename = target + ".downloading"
|
val temp_filename = target + ".downloading"
|
||||||
logger.debug("Downloading file {}", target)
|
logger.debug("Downloading file {}", target)
|
||||||
logger.trace("Temporary filename: {}", temp_filename)
|
logger.trace("Temporary filename: {}", temp_filename)
|
||||||
|
|
||||||
var offset = 0
|
var offset = 0
|
||||||
if (File(temp_filename).isFile()) {
|
if (File(temp_filename).isFile()) {
|
||||||
logger.info("Temporary filename already exists; continuing this file")
|
logger.info("Temporary filename already exists; continuing this file")
|
||||||
offset = File(temp_filename).length().toInt()
|
offset = File(temp_filename).length().toInt()
|
||||||
if (offset >= size) {
|
if (offset >= size) {
|
||||||
logger.warn("Temporary file size is >= the target size. Assuming corrupt file & deleting it")
|
logger.warn("Temporary file size is >= the target size. Assuming corrupt file & deleting it")
|
||||||
File(temp_filename).delete()
|
File(temp_filename).delete()
|
||||||
offset = 0
|
offset = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 response: TLFile? = null
|
||||||
var try_again: Boolean
|
var try_again: Boolean
|
||||||
do {
|
do {
|
||||||
try_again = false
|
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)
|
||||||
try {
|
try {
|
||||||
response = download_client!!.executeRpcQuery(req, dcID) as TLFile
|
response = download_client!!.executeRpcQuery(req, dcID) as TLFile
|
||||||
} catch (e: RpcErrorException) {
|
} catch (e: RpcErrorException) {
|
||||||
if (e.getTag().startsWith("420: FLOOD_WAIT_")) {
|
if (e.getTag().startsWith("420: FLOOD_WAIT_")) {
|
||||||
try_again = true
|
try_again = true
|
||||||
Utils.obeyFloodWaitException(e)
|
Utils.obeyFloodWaitException(e)
|
||||||
} else if (e.getCode() == 400) {
|
} else if (e.getCode() == 400) {
|
||||||
//Somehow this file is broken. No idea why. Let's skip it for now
|
//Somehow this file is broken. No idea why. Let's skip it for now
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
TimeUnit.MILLISECONDS.sleep(Config.DELAY_AFTER_GET_FILE)
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (offset < size && (response!!.getBytes().getData().size > 0 || try_again))
|
} while (offset < size && (response!!.getBytes().getData().size > 0 || try_again))
|
||||||
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.")
|
||||||
File(temp_filename).delete()
|
File(temp_filename).delete()
|
||||||
System.exit(1)
|
System.exit(1)
|
||||||
}
|
}
|
||||||
logger.trace("Renaming {} to {}", temp_filename, target)
|
logger.trace("Renaming {} to {}", temp_filename, target)
|
||||||
var rename_tries = 0
|
var rename_tries = 0
|
||||||
var last_exception: IOException? = null
|
var last_exception: IOException? = null
|
||||||
while (rename_tries <= Config.RENAMING_MAX_TRIES) {
|
while (rename_tries <= Config.RENAMING_MAX_TRIES) {
|
||||||
rename_tries++
|
rename_tries++
|
||||||
try {
|
try {
|
||||||
Files.move(File(temp_filename).toPath(), File(target).toPath(), StandardCopyOption.REPLACE_EXISTING)
|
Files.move(File(temp_filename).toPath(), File(target).toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||||
last_exception = null
|
last_exception = null
|
||||||
break
|
break
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
logger.debug("Exception during move. rename_tries: {}. Exception: {}", rename_tries, e)
|
logger.debug("Exception during move. rename_tries: {}. Exception: {}", rename_tries, e)
|
||||||
last_exception = e
|
last_exception = e
|
||||||
try {
|
try {
|
||||||
TimeUnit.MILLISECONDS.sleep(Config.RENAMING_DELAY)
|
TimeUnit.MILLISECONDS.sleep(Config.RENAMING_DELAY)
|
||||||
} catch (e2: InterruptedException) {
|
} catch (e2: InterruptedException) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (last_exception != null) {
|
if (last_exception != null) {
|
||||||
throw last_exception
|
throw last_exception
|
||||||
}
|
}
|
||||||
last_download_succeeded = true
|
last_download_succeeded = true
|
||||||
return true
|
return true
|
||||||
} catch (ex: java.io.IOException) {
|
} catch (ex: java.io.IOException) {
|
||||||
if (fos != null) fos.close()
|
if (fos != null) fos.close()
|
||||||
System.out.println("IOException happened while downloading " + target)
|
System.out.println("IOException happened while downloading " + target)
|
||||||
throw ex
|
throw ex
|
||||||
} catch (ex: RpcErrorException) {
|
} catch (ex: RpcErrorException) {
|
||||||
if (fos != null) fos.close()
|
if (fos != null) fos.close()
|
||||||
if (ex.getCode() == 500) {
|
if (ex.getCode() == 500) {
|
||||||
if (!last_download_succeeded) {
|
if (!last_download_succeeded) {
|
||||||
System.out.println("Got an Internal Server Error from Telegram. Since the file downloaded before also happened to get this error, we will stop downloading now. Please try again later.")
|
System.out.println("Got an Internal Server Error from Telegram. Since the file downloaded before also happened to get this error, we will stop downloading now. Please try again later.")
|
||||||
throw ex
|
throw ex
|
||||||
}
|
}
|
||||||
last_download_succeeded = false
|
last_download_succeeded = false
|
||||||
System.out.println("Got an Internal Server Error from Telegram. Skipping this file for now. Next run of telegram_backup will continue to download this file.")
|
System.out.println("Got an Internal Server Error from Telegram. Skipping this file for now. Next run of telegram_backup will continue to download this file.")
|
||||||
logger.warn(ex.toString())
|
logger.warn(ex.toString())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
System.out.println("RpcErrorException happened while downloading " + target)
|
System.out.println("RpcErrorException happened while downloading " + target)
|
||||||
throw ex
|
throw ex
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun downloadExternalFile(target: String, url: String): Boolean {
|
fun downloadExternalFile(target: String, url: String): Boolean {
|
||||||
FileUtils.copyURLToFile(URL(url), File(target), 5000, 5000)
|
FileUtils.copyURLToFile(URL(url), File(target), 5000, 5000)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,14 +19,14 @@ package de.fabianonline.telegram_backup
|
|||||||
import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
|
import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
|
||||||
|
|
||||||
interface DownloadProgressInterface {
|
interface DownloadProgressInterface {
|
||||||
fun onMessageDownloadStart(count: Int, source: String?)
|
fun onMessageDownloadStart(count: Int, source: String?)
|
||||||
fun onMessageDownloaded(number: Int)
|
fun onMessageDownloaded(number: Int)
|
||||||
fun onMessageDownloadFinished()
|
fun onMessageDownloadFinished()
|
||||||
|
|
||||||
fun onMediaDownloadStart(count: Int)
|
fun onMediaDownloadStart(count: Int)
|
||||||
fun onMediaDownloaded(file_manager: AbstractMediaFileManager)
|
fun onMediaDownloaded(file_manager: AbstractMediaFileManager)
|
||||||
fun onMediaDownloadedEmpty()
|
fun onMediaDownloadedEmpty()
|
||||||
fun onMediaSkipped()
|
fun onMediaSkipped()
|
||||||
fun onMediaAlreadyPresent(file_manager: AbstractMediaFileManager)
|
fun onMediaAlreadyPresent(file_manager: AbstractMediaFileManager)
|
||||||
fun onMediaDownloadFinished()
|
fun onMediaDownloadFinished()
|
||||||
}
|
}
|
||||||
|
@ -25,61 +25,61 @@ import java.awt.event.ActionListener
|
|||||||
import java.util.Vector
|
import java.util.Vector
|
||||||
|
|
||||||
class GUIController() {
|
class GUIController() {
|
||||||
init {
|
init {
|
||||||
showAccountChooserDialog()
|
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() {
|
private fun showAccountChooserDialog() {
|
||||||
val loginDialog = JDialog()
|
val accountChooser = JDialog()
|
||||||
loginDialog.setTitle("Add an account")
|
accountChooser.setTitle("Choose account")
|
||||||
loginDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
val sections = JPanel()
|
private fun addAccountDialog() {
|
||||||
sections.setLayout(BoxLayout(sections, BoxLayout.Y_AXIS))
|
val loginDialog = JDialog()
|
||||||
|
loginDialog.setTitle("Add an account")
|
||||||
|
loginDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
||||||
|
|
||||||
val top = JPanel()
|
val sections = JPanel()
|
||||||
top.setLayout(BoxLayout(top, BoxLayout.Y_AXIS))
|
sections.setLayout(BoxLayout(sections, BoxLayout.Y_AXIS))
|
||||||
top.add(JLabel("Please enter your phone number in international format:"))
|
|
||||||
top.add(JTextField("+49123773212"))
|
|
||||||
|
|
||||||
sections.add(top)
|
val top = JPanel()
|
||||||
sections.add(Box.createVerticalStrut(5))
|
top.setLayout(BoxLayout(top, BoxLayout.Y_AXIS))
|
||||||
sections.add(JSeparator(SwingConstants.HORIZONTAL))
|
top.add(JLabel("Please enter your phone number in international format:"))
|
||||||
|
top.add(JTextField("+49123773212"))
|
||||||
|
|
||||||
val middle = JPanel()
|
sections.add(top)
|
||||||
middle.setLayout(BoxLayout(middle, BoxLayout.Y_AXIS))
|
sections.add(Box.createVerticalStrut(5))
|
||||||
middle.add(JLabel("Telegram sent you a code. Enter it here:"))
|
sections.add(JSeparator(SwingConstants.HORIZONTAL))
|
||||||
middle.add(JTextField())
|
|
||||||
middle.setEnabled(false)
|
|
||||||
|
|
||||||
sections.add(middle)
|
val middle = JPanel()
|
||||||
sections.add(Box.createVerticalStrut(5))
|
middle.setLayout(BoxLayout(middle, BoxLayout.Y_AXIS))
|
||||||
sections.add(JSeparator(SwingConstants.HORIZONTAL))
|
middle.add(JLabel("Telegram sent you a code. Enter it here:"))
|
||||||
|
middle.add(JTextField())
|
||||||
|
middle.setEnabled(false)
|
||||||
|
|
||||||
loginDialog.add(sections)
|
sections.add(middle)
|
||||||
loginDialog.setVisible(true)
|
sections.add(Box.createVerticalStrut(5))
|
||||||
}
|
sections.add(JSeparator(SwingConstants.HORIZONTAL))
|
||||||
|
|
||||||
|
loginDialog.add(sections)
|
||||||
|
loginDialog.setVisible(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,32 +21,32 @@ import java.lang.StringBuilder
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object StickerConverter {
|
object StickerConverter {
|
||||||
fun makeFilenameWithPath(attr: TLDocumentAttributeSticker): String {
|
fun makeFilenameWithPath(attr: TLDocumentAttributeSticker): String {
|
||||||
val file = StringBuilder()
|
val file = StringBuilder()
|
||||||
file.append(makePath())
|
file.append(makePath())
|
||||||
file.append(makeFilename(attr))
|
file.append(makeFilename(attr))
|
||||||
return file.toString()
|
return file.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeFilename(attr: TLDocumentAttributeSticker): String {
|
fun makeFilename(attr: TLDocumentAttributeSticker): String {
|
||||||
val file = StringBuilder()
|
val file = StringBuilder()
|
||||||
if (attr.getStickerset() is TLInputStickerSetShortName) {
|
if (attr.getStickerset() is TLInputStickerSetShortName) {
|
||||||
file.append((attr.getStickerset() as TLInputStickerSetShortName).getShortName())
|
file.append((attr.getStickerset() as TLInputStickerSetShortName).getShortName())
|
||||||
} else if (attr.getStickerset() is TLInputStickerSetID) {
|
} else if (attr.getStickerset() is TLInputStickerSetID) {
|
||||||
file.append((attr.getStickerset() as TLInputStickerSetID).getId())
|
file.append((attr.getStickerset() as TLInputStickerSetID).getId())
|
||||||
}
|
}
|
||||||
file.append("_")
|
file.append("_")
|
||||||
file.append(attr.getAlt().hashCode())
|
file.append(attr.getAlt().hashCode())
|
||||||
file.append(".webp")
|
file.append(".webp")
|
||||||
return file.toString()
|
return file.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makePath(): String {
|
fun makePath(): String {
|
||||||
val path = Config.FILE_BASE +
|
val path = Config.FILE_BASE +
|
||||||
File.separatorChar +
|
File.separatorChar +
|
||||||
Config.FILE_STICKER_BASE +
|
Config.FILE_STICKER_BASE +
|
||||||
File.separatorChar
|
File.separatorChar
|
||||||
File(path).mkdirs()
|
File(path).mkdirs()
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,20 +27,20 @@ import java.io.InputStream
|
|||||||
import com.github.badoualy.telegram.tl.StreamUtils.readTLObject
|
import com.github.badoualy.telegram.tl.StreamUtils.readTLObject
|
||||||
|
|
||||||
class TLRequestAccountGetPasswordWithCurrentSalt : TLMethod<TLPassword>() {
|
class TLRequestAccountGetPasswordWithCurrentSalt : TLMethod<TLPassword>() {
|
||||||
private val _constructor = "account.getPassword#548a30f5"
|
private val _constructor = "account.getPassword#548a30f5"
|
||||||
override fun getConstructorId(): Int = CONSTRUCTOR_ID
|
override fun getConstructorId(): Int = CONSTRUCTOR_ID
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun deserializeResponse(stream: InputStream, context: TLContext): TLPassword {
|
override fun deserializeResponse(stream: InputStream, context: TLContext): TLPassword {
|
||||||
val response = (readTLObject(stream, context) ?: throw IOException("Unable to parse response")) as? TLPassword ?: throw IOException("Incorrect response type, expected getClass().getCanonicalName(), found response.getClass().getCanonicalName()")
|
val response = (readTLObject(stream, context) ?: throw IOException("Unable to parse response")) as? TLPassword ?: throw IOException("Incorrect response type, expected getClass().getCanonicalName(), found response.getClass().getCanonicalName()")
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return _constructor
|
return _constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val CONSTRUCTOR_ID = 0x548a30f5
|
val CONSTRUCTOR_ID = 0x548a30f5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,133 +28,133 @@ import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
|
|||||||
import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory
|
import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory
|
||||||
|
|
||||||
internal class TelegramUpdateHandler : UpdateCallback {
|
internal class TelegramUpdateHandler : UpdateCallback {
|
||||||
private var user: UserManager? = null
|
private var user: UserManager? = null
|
||||||
private var db: Database? = null
|
private var db: Database? = null
|
||||||
var debug = false
|
var debug = false
|
||||||
|
|
||||||
fun activate() {
|
fun activate() {
|
||||||
this.user = UserManager.getInstance()
|
this.user = UserManager.getInstance()
|
||||||
this.db = Database.getInstance()
|
this.db = Database.getInstance()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpdates(client: TelegramClient, updates: TLUpdates) {
|
override fun onUpdates(client: TelegramClient, updates: TLUpdates) {
|
||||||
if (db == null) return
|
if (db == null) return
|
||||||
if (debug) System.out.println("onUpdates - " + updates.getUpdates().size + " Updates, " + updates.getUsers().size + " Users, " + updates.getChats().size + " Chats")
|
if (debug) System.out.println("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, client)
|
||||||
if (debug) System.out.println(" " + update.javaClass.getName())
|
if (debug) System.out.println(" " + 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
|
if (db == null) return
|
||||||
if (debug) System.out.println("onUpdatesCombined")
|
if (debug) System.out.println("onUpdatesCombined")
|
||||||
for (update in updates.getUpdates()) {
|
for (update in updates.getUpdates()) {
|
||||||
processUpdate(update, client)
|
processUpdate(update, client)
|
||||||
}
|
}
|
||||||
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
|
if (db == null) return
|
||||||
if (debug) System.out.println("onUpdateShort")
|
if (debug) System.out.println("onUpdateShort")
|
||||||
processUpdate(update.getUpdate(), client)
|
processUpdate(update.getUpdate(), client)
|
||||||
if (debug) System.out.println(" " + 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
|
if (db == null) return
|
||||||
if (debug) System.out.println("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(),
|
||||||
message.getMediaUnread(),
|
message.getMediaUnread(),
|
||||||
message.getSilent(),
|
message.getSilent(),
|
||||||
false,
|
false,
|
||||||
message.getId(),
|
message.getId(),
|
||||||
message.getFromId(),
|
message.getFromId(),
|
||||||
TLPeerChat(message.getChatId()),
|
TLPeerChat(message.getChatId()),
|
||||||
message.getFwdFrom(),
|
message.getFwdFrom(),
|
||||||
message.getViaBotId(),
|
message.getViaBotId(),
|
||||||
message.getReplyToMsgId(),
|
message.getReplyToMsgId(),
|
||||||
message.getDate(),
|
message.getDate(),
|
||||||
message.getMessage(), null, null,
|
message.getMessage(), null, null,
|
||||||
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)
|
||||||
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
|
if (db == null) return
|
||||||
if (debug) System.out.println("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!!.user!!.getId()
|
||||||
to_id = m.getUserId()
|
to_id = m.getUserId()
|
||||||
} else {
|
} else {
|
||||||
to_id = user!!.user!!.getId()
|
to_id = user!!.user!!.getId()
|
||||||
from_id = m.getUserId()
|
from_id = m.getUserId()
|
||||||
}
|
}
|
||||||
val msg = TLMessage(
|
val msg = TLMessage(
|
||||||
m.getOut(),
|
m.getOut(),
|
||||||
m.getMentioned(),
|
m.getMentioned(),
|
||||||
m.getMediaUnread(),
|
m.getMediaUnread(),
|
||||||
m.getSilent(),
|
m.getSilent(),
|
||||||
false,
|
false,
|
||||||
m.getId(),
|
m.getId(),
|
||||||
from_id,
|
from_id,
|
||||||
TLPeerUser(to_id),
|
TLPeerUser(to_id),
|
||||||
m.getFwdFrom(),
|
m.getFwdFrom(),
|
||||||
m.getViaBotId(),
|
m.getViaBotId(),
|
||||||
m.getReplyToMsgId(),
|
m.getReplyToMsgId(),
|
||||||
m.getDate(),
|
m.getDate(),
|
||||||
m.getMessage(), null, null,
|
m.getMessage(), null, null,
|
||||||
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)
|
||||||
System.out.print('.')
|
System.out.print('.')
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShortSentMessage(client: TelegramClient, message: TLUpdateShortSentMessage) {
|
override fun onShortSentMessage(client: TelegramClient, message: TLUpdateShortSentMessage) {
|
||||||
if (db == null) return
|
if (db == null) return
|
||||||
System.out.println("onShortSentMessage")
|
System.out.println("onShortSentMessage")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpdateTooLong(client: TelegramClient) {
|
override fun onUpdateTooLong(client: TelegramClient) {
|
||||||
if (db == null) return
|
if (db == null) return
|
||||||
System.out.println("onUpdateTooLong")
|
System.out.println("onUpdateTooLong")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processUpdate(update: TLAbsUpdate, client: TelegramClient) {
|
private fun processUpdate(update: TLAbsUpdate, client: TelegramClient) {
|
||||||
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)
|
||||||
System.out.print('.')
|
System.out.print('.')
|
||||||
if (abs_msg is TLMessage) {
|
if (abs_msg is TLMessage) {
|
||||||
val fm = FileManagerFactory.getFileManager(abs_msg, user!!, client)
|
val fm = FileManagerFactory.getFileManager(abs_msg, user!!, client)
|
||||||
if (fm != null && !fm.isEmpty && !fm.downloaded) {
|
if (fm != null && !fm.isEmpty && !fm.downloaded) {
|
||||||
try {
|
try {
|
||||||
fm.download()
|
fm.download()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
System.out.println("We got an exception while downloading media, but we're going to ignore it.")
|
System.out.println("We got an exception while downloading media, but we're going to ignore it.")
|
||||||
System.out.println("Here it is anyway:")
|
System.out.println("Here it is anyway:")
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ignore everything else...
|
// ignore everything else...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,55 +11,55 @@ import java.io.IOException
|
|||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
internal object TestFeatures {
|
internal object TestFeatures {
|
||||||
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 {
|
||||||
Class.forName("org.sqlite.JDBC")
|
Class.forName("org.sqlite.JDBC")
|
||||||
} catch (e: ClassNotFoundException) {
|
} catch (e: ClassNotFoundException) {
|
||||||
CommandLineController.show_error("Could not load jdbc-sqlite class.")
|
CommandLineController.show_error("Could not load jdbc-sqlite class.")
|
||||||
}
|
}
|
||||||
|
|
||||||
val path = "jdbc:sqlite:cache4.db"
|
val path = "jdbc:sqlite:cache4.db"
|
||||||
|
|
||||||
var conn: Connection
|
var conn: Connection
|
||||||
var stmt: Statement? = null
|
var stmt: Statement? = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
conn = DriverManager.getConnection(path)
|
conn = DriverManager.getConnection(path)
|
||||||
stmt = conn.createStatement()
|
stmt = conn.createStatement()
|
||||||
} catch (e: SQLException) {
|
} catch (e: SQLException) {
|
||||||
CommandLineController.show_error("Could not connect to SQLITE database.")
|
CommandLineController.show_error("Could not connect to SQLITE database.")
|
||||||
}
|
}
|
||||||
|
|
||||||
var unsupported_constructor = 0
|
var unsupported_constructor = 0
|
||||||
var success = 0
|
var success = 0
|
||||||
|
|
||||||
try {
|
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))
|
||||||
} catch (e: com.github.badoualy.telegram.tl.exception.UnsupportedConstructorException) {
|
} catch (e: com.github.badoualy.telegram.tl.exception.UnsupportedConstructorException) {
|
||||||
unsupported_constructor++
|
unsupported_constructor++
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
System.out.println("IOException: " + e)
|
System.out.println("IOException: " + e)
|
||||||
}
|
}
|
||||||
|
|
||||||
success++
|
success++
|
||||||
}
|
}
|
||||||
} catch (e: SQLException) {
|
} catch (e: SQLException) {
|
||||||
System.out.println("SQL exception: " + e)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test2() {
|
fun test2() {
|
||||||
// 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()
|
val db = Database.getInstance()
|
||||||
System.out.println("Database encoding: " + db.getEncoding())
|
System.out.println("Database encoding: " + db.getEncoding())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,105 +36,105 @@ import org.slf4j.Logger
|
|||||||
|
|
||||||
class UserManager @Throws(IOException::class)
|
class UserManager @Throws(IOException::class)
|
||||||
private constructor(c: TelegramClient) {
|
private constructor(c: TelegramClient) {
|
||||||
var user: TLUser? = null
|
var user: TLUser? = null
|
||||||
var phone: String? = null
|
var phone: String? = null
|
||||||
private var code: String? = null
|
private var code: String? = null
|
||||||
private val client: TelegramClient
|
private val client: TelegramClient
|
||||||
private var sent_code: TLSentCode? = null
|
private var sent_code: TLSentCode? = null
|
||||||
private var auth: TLAuthorization? = null
|
private var auth: TLAuthorization? = null
|
||||||
var isPasswordNeeded = false
|
var isPasswordNeeded = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
val loggedIn: Boolean
|
val loggedIn: Boolean
|
||||||
get() = user != null
|
get() = user != null
|
||||||
|
|
||||||
val userString: String
|
val userString: String
|
||||||
get() {
|
get() {
|
||||||
if (this.user == null) return "Not logged in"
|
if (this.user == null) return "Not logged in"
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
if (this.user!!.getFirstName() != null) {
|
if (this.user!!.getFirstName() != null) {
|
||||||
sb.append(this.user!!.getFirstName())
|
sb.append(this.user!!.getFirstName())
|
||||||
}
|
}
|
||||||
if (this.user!!.getLastName() != null) {
|
if (this.user!!.getLastName() != null) {
|
||||||
sb.append(" ")
|
sb.append(" ")
|
||||||
sb.append(this.user!!.getLastName())
|
sb.append(this.user!!.getLastName())
|
||||||
}
|
}
|
||||||
if (this.user!!.getUsername() != null) {
|
if (this.user!!.getUsername() != null) {
|
||||||
sb.append(" (@")
|
sb.append(" (@")
|
||||||
sb.append(this.user!!.getUsername())
|
sb.append(this.user!!.getUsername())
|
||||||
sb.append(")")
|
sb.append(")")
|
||||||
}
|
}
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
val fileBase: String
|
val fileBase: String
|
||||||
get() = Config.FILE_BASE + File.separatorChar + "+" + this.user!!.getPhone() + File.separatorChar
|
get() = Config.FILE_BASE + File.separatorChar + "+" + this.user!!.getPhone() + File.separatorChar
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.client = c
|
this.client = c
|
||||||
logger.debug("Calling getFullUser")
|
logger.debug("Calling getFullUser")
|
||||||
try {
|
try {
|
||||||
val full_user = this.client.usersGetFullUser(TLInputUserSelf())
|
val full_user = this.client.usersGetFullUser(TLInputUserSelf())
|
||||||
this.user = full_user.getUser().getAsUser()
|
this.user = full_user.getUser().getAsUser()
|
||||||
} catch (e: RpcErrorException) {
|
} catch (e: RpcErrorException) {
|
||||||
// This may happen. Ignoring it.
|
// This may happen. Ignoring it.
|
||||||
logger.debug("Ignoring exception:", e)
|
logger.debug("Ignoring exception:", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
@Throws(RpcErrorException::class, IOException::class)
|
||||||
fun sendCodeToPhoneNumber(number: String) {
|
fun sendCodeToPhoneNumber(number: String) {
|
||||||
this.phone = number
|
this.phone = number
|
||||||
this.sent_code = this.client.authSendCode(false, number, true)
|
this.sent_code = this.client.authSendCode(false, number, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
@Throws(RpcErrorException::class, IOException::class)
|
||||||
fun verifyCode(code: String) {
|
fun verifyCode(code: String) {
|
||||||
this.code = code
|
this.code = code
|
||||||
try {
|
try {
|
||||||
this.auth = client.authSignIn(phone, this.sent_code!!.getPhoneCodeHash(), this.code)
|
this.auth = client.authSignIn(phone, this.sent_code!!.getPhoneCodeHash(), this.code)
|
||||||
this.user = auth!!.getUser().getAsUser()
|
this.user = auth!!.getUser().getAsUser()
|
||||||
} catch (e: RpcErrorException) {
|
} catch (e: RpcErrorException) {
|
||||||
if (e.getCode() != 401 || !e.getTag().equals("SESSION_PASSWORD_NEEDED")) throw e
|
if (e.getCode() != 401 || !e.getTag().equals("SESSION_PASSWORD_NEEDED")) throw e
|
||||||
this.isPasswordNeeded = true
|
this.isPasswordNeeded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class)
|
@Throws(RpcErrorException::class, IOException::class)
|
||||||
fun verifyPassword(pw: String) {
|
fun verifyPassword(pw: String) {
|
||||||
val password = pw.toByteArray(charset=Charsets.UTF_8)
|
val password = pw.toByteArray(charset = Charsets.UTF_8)
|
||||||
val salt = (client.accountGetPassword() as TLPassword).getCurrentSalt().getData()
|
val salt = (client.accountGetPassword() as TLPassword).getCurrentSalt().getData()
|
||||||
var md: MessageDigest
|
var md: MessageDigest
|
||||||
try {
|
try {
|
||||||
md = MessageDigest.getInstance("SHA-256")
|
md = MessageDigest.getInstance("SHA-256")
|
||||||
} catch (e: NoSuchAlgorithmException) {
|
} catch (e: NoSuchAlgorithmException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val salted = ByteArray(2 * salt.size + password.size)
|
val salted = ByteArray(2 * salt.size + password.size)
|
||||||
System.arraycopy(salt, 0, salted, 0, salt.size)
|
System.arraycopy(salt, 0, salted, 0, salt.size)
|
||||||
System.arraycopy(password, 0, salted, salt.size, password.size)
|
System.arraycopy(password, 0, salted, salt.size, password.size)
|
||||||
System.arraycopy(salt, 0, salted, salt.size + password.size, salt.size)
|
System.arraycopy(salt, 0, salted, salt.size + password.size, salt.size)
|
||||||
val hash = md.digest(salted)
|
val hash = md.digest(salted)
|
||||||
auth = client.authCheckPassword(TLBytes(hash))
|
auth = client.authCheckPassword(TLBytes(hash))
|
||||||
this.user = auth!!.getUser().getAsUser()
|
this.user = auth!!.getUser().getAsUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(UserManager::class.java)
|
private val logger = LoggerFactory.getLogger(UserManager::class.java)
|
||||||
internal var instance: UserManager? = null
|
internal var instance: UserManager? = null
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun init(c: TelegramClient) {
|
fun init(c: TelegramClient) {
|
||||||
instance = UserManager(c)
|
instance = UserManager(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInstance(): UserManager {
|
fun getInstance(): UserManager {
|
||||||
if (instance == null) throw RuntimeException("UserManager is not yet initialized.")
|
if (instance == null) throw RuntimeException("UserManager is not yet initialized.")
|
||||||
return instance!!
|
return instance!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,156 +28,156 @@ import org.slf4j.Logger
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
object Utils {
|
object Utils {
|
||||||
@JvmField public val VERSIONS_EQUAL = 0
|
@JvmField public val VERSIONS_EQUAL = 0
|
||||||
@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
|
||||||
|
|
||||||
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 getAccounts(): Vector<String> {
|
||||||
val accounts = Vector<String>()
|
val accounts = Vector<String>()
|
||||||
val folder = File(Config.FILE_BASE)
|
val folder = File(Config.FILE_BASE)
|
||||||
val files = folder.listFiles()
|
val files = folder.listFiles()
|
||||||
if (files != null)
|
if (files != null)
|
||||||
for (f in files) {
|
for (f in files) {
|
||||||
if (f.isDirectory() && f.getName().startsWith("+")) {
|
if (f.isDirectory() && f.getName().startsWith("+")) {
|
||||||
accounts.add(f.getName())
|
accounts.add(f.getName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return accounts
|
return accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNewestVersion(): Version? {
|
fun getNewestVersion(): Version? {
|
||||||
try {
|
try {
|
||||||
val data_url = "https://api.github.com/repos/fabianonline/telegram_backup/releases"
|
val data_url = "https://api.github.com/repos/fabianonline/telegram_backup/releases"
|
||||||
logger.debug("Requesting current release info from {}", data_url)
|
logger.debug("Requesting current release info from {}", data_url)
|
||||||
val json = IOUtils.toString(URL(data_url))
|
val json = IOUtils.toString(URL(data_url))
|
||||||
val parser = JsonParser()
|
val parser = JsonParser()
|
||||||
val root_elm = parser.parse(json)
|
val root_elm = parser.parse(json)
|
||||||
if (root_elm.isJsonArray()) {
|
if (root_elm.isJsonArray()) {
|
||||||
val root = root_elm.getAsJsonArray()
|
val root = root_elm.getAsJsonArray()
|
||||||
var newest_version: JsonObject? = null
|
var newest_version: JsonObject? = null
|
||||||
for (e in root)
|
for (e in root)
|
||||||
if (e.isJsonObject()) {
|
if (e.isJsonObject()) {
|
||||||
val version = e.getAsJsonObject()
|
val version = e.getAsJsonObject()
|
||||||
if (version.getAsJsonPrimitive("prerelease").getAsBoolean() == false) {
|
if (version.getAsJsonPrimitive("prerelease").getAsBoolean() == false) {
|
||||||
newest_version = version
|
newest_version = version
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newest_version == null) return null
|
if (newest_version == null) return null
|
||||||
val new_v = newest_version.getAsJsonPrimitive("tag_name").getAsString()
|
val new_v = newest_version.getAsJsonPrimitive("tag_name").getAsString()
|
||||||
logger.debug("Found current release version {}", new_v)
|
logger.debug("Found current release version {}", new_v)
|
||||||
val cur_v = Config.APP_APPVER
|
val cur_v = Config.APP_APPVER
|
||||||
|
|
||||||
val result = compareVersions(cur_v, new_v)
|
val result = compareVersions(cur_v, new_v)
|
||||||
|
|
||||||
return Version(new_v, newest_version.getAsJsonPrimitive("html_url").getAsString(), newest_version.getAsJsonPrimitive("body").getAsString(), result == VERSION_2_NEWER)
|
return Version(new_v, newest_version.getAsJsonPrimitive("html_url").getAsString(), newest_version.getAsJsonPrimitive("body").getAsString(), result == VERSION_2_NEWER)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class)
|
@Throws(RpcErrorException::class)
|
||||||
@JvmOverloads internal fun obeyFloodWaitException(e: RpcErrorException?, silent: Boolean = false) {
|
@JvmOverloads internal fun obeyFloodWaitException(e: RpcErrorException?, silent: Boolean = false) {
|
||||||
if (e == null || e.getCode() != 420) return
|
if (e == null || e.getCode() != 420) return
|
||||||
|
|
||||||
val delay: Long = e.getTagInteger()!!.toLong()
|
val delay: Long = e.getTagInteger()!!.toLong()
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
System.out.println("")
|
System.out.println("")
|
||||||
System.out.println(
|
System.out.println(
|
||||||
"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("")
|
System.out.println("")
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
TimeUnit.SECONDS.sleep(delay + 1)
|
TimeUnit.SECONDS.sleep(delay + 1)
|
||||||
} catch (e2: InterruptedException) {
|
} catch (e2: InterruptedException) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun compareVersions(v1: String, v2: String): Int {
|
fun compareVersions(v1: String, v2: String): Int {
|
||||||
logger.debug("Comparing versions {} and {}.", v1, v2)
|
logger.debug("Comparing versions {} and {}.", v1, v2)
|
||||||
if (v1.equals(v2)) return VERSIONS_EQUAL
|
if (v1.equals(v2)) return VERSIONS_EQUAL
|
||||||
|
|
||||||
val v1_p = v1.split("-", limit=2)
|
val v1_p = v1.split("-", limit = 2)
|
||||||
val v2_p = v2.split("-", limit=2)
|
val v2_p = v2.split("-", limit = 2)
|
||||||
|
|
||||||
logger.trace("Parts to compare without suffixes: {} and {}.", v1_p[0], v2_p[0])
|
logger.trace("Parts to compare without suffixes: {} and {}.", v1_p[0], v2_p[0])
|
||||||
|
|
||||||
val v1_p2 = v1_p[0].split(".")
|
val v1_p2 = v1_p[0].split(".")
|
||||||
val v2_p2 = v2_p[0].split(".")
|
val v2_p2 = v2_p[0].split(".")
|
||||||
|
|
||||||
logger.trace("Length of the parts without suffixes: {} and {}.", v1_p2.size, v2_p2.size)
|
logger.trace("Length of the parts without suffixes: {} and {}.", v1_p2.size, v2_p2.size)
|
||||||
|
|
||||||
var i: Int
|
var i: Int
|
||||||
i = 0
|
i = 0
|
||||||
while (i < v1_p2.size && i < v2_p2.size) {
|
while (i < v1_p2.size && i < v2_p2.size) {
|
||||||
val i_1 = Integer.parseInt(v1_p2[i])
|
val i_1 = Integer.parseInt(v1_p2[i])
|
||||||
val i_2 = Integer.parseInt(v2_p2[i])
|
val i_2 = Integer.parseInt(v2_p2[i])
|
||||||
logger.trace("Comparing parts: {} and {}.", i_1, i_2)
|
logger.trace("Comparing parts: {} and {}.", i_1, i_2)
|
||||||
if (i_1 > i_2) {
|
if (i_1 > i_2) {
|
||||||
logger.debug("v1 is newer")
|
logger.debug("v1 is newer")
|
||||||
return VERSION_1_NEWER
|
return VERSION_1_NEWER
|
||||||
} else if (i_2 > i_1) {
|
} else if (i_2 > i_1) {
|
||||||
logger.debug("v2 is newer")
|
logger.debug("v2 is newer")
|
||||||
return VERSION_2_NEWER
|
return VERSION_2_NEWER
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
logger.trace("At least one of the versions has run out of parts.")
|
logger.trace("At least one of the versions has run out of parts.")
|
||||||
if (v1_p2.size > v2_p2.size) {
|
if (v1_p2.size > v2_p2.size) {
|
||||||
logger.debug("v1 is longer, so it is newer")
|
logger.debug("v1 is longer, so it is newer")
|
||||||
return VERSION_1_NEWER
|
return VERSION_1_NEWER
|
||||||
} else if (v2_p2.size > v1_p2.size) {
|
} else if (v2_p2.size > v1_p2.size) {
|
||||||
logger.debug("v2 is longer, so it is newer")
|
logger.debug("v2 is longer, so it is newer")
|
||||||
return VERSION_2_NEWER
|
return VERSION_2_NEWER
|
||||||
}
|
}
|
||||||
|
|
||||||
// startsWith
|
// startsWith
|
||||||
if (v1_p.size > 1 && v2_p.size == 1) {
|
if (v1_p.size > 1 && v2_p.size == 1) {
|
||||||
logger.debug("v1 has a suffix, v2 not.")
|
logger.debug("v1 has a suffix, v2 not.")
|
||||||
if (v1_p[1].startsWith("pre")) {
|
if (v1_p[1].startsWith("pre")) {
|
||||||
logger.debug("v1 is a pre version, so v1 is newer")
|
logger.debug("v1 is a pre version, so v1 is newer")
|
||||||
return VERSION_2_NEWER
|
return VERSION_2_NEWER
|
||||||
} else {
|
} else {
|
||||||
return VERSION_1_NEWER
|
return VERSION_1_NEWER
|
||||||
}
|
}
|
||||||
} else if (v1_p.size == 1 && v2_p.size > 1) {
|
} else if (v1_p.size == 1 && v2_p.size > 1) {
|
||||||
logger.debug("v1 has no suffix, but v2 has")
|
logger.debug("v1 has no suffix, but v2 has")
|
||||||
if (v2_p[1].startsWith("pre")) {
|
if (v2_p[1].startsWith("pre")) {
|
||||||
logger.debug("v2 is a pre version, so v1 is better")
|
logger.debug("v2 is a pre version, so v1 is better")
|
||||||
return VERSION_1_NEWER
|
return VERSION_1_NEWER
|
||||||
} else {
|
} else {
|
||||||
return VERSION_2_NEWER
|
return VERSION_2_NEWER
|
||||||
}
|
}
|
||||||
} else if (v1_p.size > 1 && v2_p.size > 1) {
|
} else if (v1_p.size > 1 && v2_p.size > 1) {
|
||||||
logger.debug("Both have a suffix")
|
logger.debug("Both have a suffix")
|
||||||
if (v1_p[1].startsWith("pre") && !v2_p[1].startsWith("pre")) {
|
if (v1_p[1].startsWith("pre") && !v2_p[1].startsWith("pre")) {
|
||||||
logger.debug("v1 is a 'pre' version, v2 not.")
|
logger.debug("v1 is a 'pre' version, v2 not.")
|
||||||
return VERSION_2_NEWER
|
return VERSION_2_NEWER
|
||||||
} else if (!v1_p[1].startsWith("pre") && v2_p[1].startsWith("pre")) {
|
} else if (!v1_p[1].startsWith("pre") && v2_p[1].startsWith("pre")) {
|
||||||
logger.debug("v2 is a 'pre' version, v2 not.")
|
logger.debug("v2 is a 'pre' version, v2 not.")
|
||||||
return VERSION_1_NEWER
|
return VERSION_1_NEWER
|
||||||
}
|
}
|
||||||
return VERSIONS_EQUAL
|
return VERSIONS_EQUAL
|
||||||
}
|
}
|
||||||
logger.debug("We couldn't find a real difference, so we're assuming the versions are equal-ish.")
|
logger.debug("We couldn't find a real difference, so we're assuming the versions are equal-ish.")
|
||||||
return VERSIONS_EQUAL
|
return VERSIONS_EQUAL
|
||||||
}
|
}
|
||||||
|
|
||||||
fun anonymize(str: String): String {
|
fun anonymize(str: String): String {
|
||||||
return if (!CommandLineOptions.cmd_anonymize) str else str.replace("[0-9]".toRegex(), "1").replace("[A-Z]".toRegex(), "A").replace("[a-z]".toRegex(), "a") + " (ANONYMIZED)"
|
return if (!CommandLineOptions.cmd_anonymize) str else str.replace("[0-9]".toRegex(), "1").replace("[A-Z]".toRegex(), "A").replace("[a-z]".toRegex(), "a") + " (ANONYMIZED)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,145 +42,145 @@ import org.slf4j.LoggerFactory
|
|||||||
|
|
||||||
class HTMLExporter {
|
class HTMLExporter {
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun export() {
|
fun export() {
|
||||||
try {
|
try {
|
||||||
val user = UserManager.getInstance()
|
val user = UserManager.getInstance()
|
||||||
val db = Database.getInstance()
|
val db = Database.getInstance()
|
||||||
|
|
||||||
// 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 = user.fileBase + "files" + File.separatorChar
|
||||||
File(base).mkdirs()
|
File(base).mkdirs()
|
||||||
File(base + "dialogs").mkdirs()
|
File(base + "dialogs").mkdirs()
|
||||||
|
|
||||||
logger.debug("Fetching dialogs")
|
logger.debug("Fetching dialogs")
|
||||||
val dialogs = db.getListOfDialogsForExport()
|
val dialogs = db.getListOfDialogsForExport()
|
||||||
logger.trace("Got {} dialogs", dialogs.size)
|
logger.trace("Got {} dialogs", dialogs.size)
|
||||||
logger.debug("Fetching chats")
|
logger.debug("Fetching chats")
|
||||||
val chats = db.getListOfChatsForExport()
|
val chats = db.getListOfChatsForExport()
|
||||||
logger.trace("Got {} chats", chats.size)
|
logger.trace("Got {} chats", chats.size)
|
||||||
|
|
||||||
logger.debug("Generating index.html")
|
logger.debug("Generating index.html")
|
||||||
val scope = HashMap<String, Any>()
|
val scope = HashMap<String, Any>()
|
||||||
scope.put("user", user)
|
scope.put("user", user)
|
||||||
scope.put("dialogs", dialogs)
|
scope.put("dialogs", dialogs)
|
||||||
scope.put("chats", chats)
|
scope.put("chats", chats)
|
||||||
|
|
||||||
// Collect stats data
|
// Collect stats data
|
||||||
scope.put("count.chats", chats.size)
|
scope.put("count.chats", chats.size)
|
||||||
scope.put("count.dialogs", dialogs.size)
|
scope.put("count.dialogs", dialogs.size)
|
||||||
|
|
||||||
var count_messages_chats = 0
|
var count_messages_chats = 0
|
||||||
var count_messages_dialogs = 0
|
var count_messages_dialogs = 0
|
||||||
for (c in chats) count_messages_chats += c.count ?: 0
|
for (c in chats) count_messages_chats += c.count ?: 0
|
||||||
for (d in dialogs) count_messages_dialogs += d.count ?: 0
|
for (d in dialogs) count_messages_dialogs += d.count ?: 0
|
||||||
|
|
||||||
scope.put("count.messages", count_messages_chats + count_messages_dialogs)
|
scope.put("count.messages", count_messages_chats + count_messages_dialogs)
|
||||||
scope.put("count.messages.chats", count_messages_chats)
|
scope.put("count.messages.chats", count_messages_chats)
|
||||||
scope.put("count.messages.dialogs", count_messages_dialogs)
|
scope.put("count.messages.dialogs", count_messages_dialogs)
|
||||||
|
|
||||||
scope.put("count.messages.from_me", db.getMessagesFromUserCount())
|
scope.put("count.messages.from_me", db.getMessagesFromUserCount())
|
||||||
|
|
||||||
scope.put("heatmap_data", intArrayToString(db.getMessageTimesMatrix()))
|
scope.put("heatmap_data", intArrayToString(db.getMessageTimesMatrix()))
|
||||||
|
|
||||||
scope.putAll(db.getMessageAuthorsWithCount())
|
scope.putAll(db.getMessageAuthorsWithCount())
|
||||||
scope.putAll(db.getMessageTypesWithCount())
|
scope.putAll(db.getMessageTypesWithCount())
|
||||||
scope.putAll(db.getMessageMediaTypesWithCount())
|
scope.putAll(db.getMessageMediaTypesWithCount())
|
||||||
|
|
||||||
val mf = DefaultMustacheFactory()
|
val mf = DefaultMustacheFactory()
|
||||||
var mustache = mf.compile("templates/html/index.mustache")
|
var mustache = mf.compile("templates/html/index.mustache")
|
||||||
var w = getWriter(base + "index.html")
|
var w = getWriter(base + "index.html")
|
||||||
mustache.execute(w, scope)
|
mustache.execute(w, scope)
|
||||||
w.close()
|
w.close()
|
||||||
|
|
||||||
mustache = mf.compile("templates/html/chat.mustache")
|
mustache = mf.compile("templates/html/chat.mustache")
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
logger.debug("Generating {} dialog pages", dialogs.size)
|
logger.debug("Generating {} dialog pages", dialogs.size)
|
||||||
for (d in dialogs) {
|
for (d in dialogs) {
|
||||||
i++
|
i++
|
||||||
logger.trace("Dialog {}/{}: {}", i, dialogs.size, Utils.anonymize("" + d.id))
|
logger.trace("Dialog {}/{}: {}", i, dialogs.size, Utils.anonymize("" + d.id))
|
||||||
val messages = db.getMessagesForExport(d)
|
val messages = db.getMessagesForExport(d)
|
||||||
scope.clear()
|
scope.clear()
|
||||||
scope.put("user", user)
|
scope.put("user", user)
|
||||||
scope.put("dialog", d)
|
scope.put("dialog", d)
|
||||||
scope.put("messages", messages)
|
scope.put("messages", messages)
|
||||||
|
|
||||||
scope.putAll(db.getMessageAuthorsWithCount(d))
|
scope.putAll(db.getMessageAuthorsWithCount(d))
|
||||||
scope.put("heatmap_data", intArrayToString(db.getMessageTimesMatrix(d)))
|
scope.put("heatmap_data", intArrayToString(db.getMessageTimesMatrix(d)))
|
||||||
scope.putAll(db.getMessageTypesWithCount(d))
|
scope.putAll(db.getMessageTypesWithCount(d))
|
||||||
scope.putAll(db.getMessageMediaTypesWithCount(d))
|
scope.putAll(db.getMessageMediaTypesWithCount(d))
|
||||||
|
|
||||||
w = getWriter(base + "dialogs" + File.separatorChar + "user_" + d.id + ".html")
|
w = getWriter(base + "dialogs" + File.separatorChar + "user_" + d.id + ".html")
|
||||||
mustache.execute(w, scope)
|
mustache.execute(w, scope)
|
||||||
w.close()
|
w.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
logger.debug("Generating {} chat pages", chats.size)
|
logger.debug("Generating {} chat pages", chats.size)
|
||||||
for (c in chats) {
|
for (c in chats) {
|
||||||
i++
|
i++
|
||||||
logger.trace("Chat {}/{}: {}", i, chats.size, Utils.anonymize("" + c.id))
|
logger.trace("Chat {}/{}: {}", i, chats.size, Utils.anonymize("" + c.id))
|
||||||
val messages = db.getMessagesForExport(c)
|
val messages = db.getMessagesForExport(c)
|
||||||
scope.clear()
|
scope.clear()
|
||||||
scope.put("user", user)
|
scope.put("user", user)
|
||||||
scope.put("chat", c)
|
scope.put("chat", c)
|
||||||
scope.put("messages", messages)
|
scope.put("messages", messages)
|
||||||
|
|
||||||
scope.putAll(db.getMessageAuthorsWithCount(c))
|
scope.putAll(db.getMessageAuthorsWithCount(c))
|
||||||
scope.put("heatmap_data", intArrayToString(db.getMessageTimesMatrix(c)))
|
scope.put("heatmap_data", intArrayToString(db.getMessageTimesMatrix(c)))
|
||||||
scope.putAll(db.getMessageTypesWithCount(c))
|
scope.putAll(db.getMessageTypesWithCount(c))
|
||||||
scope.putAll(db.getMessageMediaTypesWithCount(c))
|
scope.putAll(db.getMessageMediaTypesWithCount(c))
|
||||||
|
|
||||||
w = getWriter(base + "dialogs" + File.separatorChar + "chat_" + c.id + ".html")
|
w = getWriter(base + "dialogs" + File.separatorChar + "chat_" + c.id + ".html")
|
||||||
mustache.execute(w, scope)
|
mustache.execute(w, scope)
|
||||||
w.close()
|
w.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Generating additional files")
|
logger.debug("Generating additional files")
|
||||||
// Copy CSS
|
// Copy CSS
|
||||||
val cssFile = javaClass.getResource("/templates/html/style.css")
|
val cssFile = javaClass.getResource("/templates/html/style.css")
|
||||||
val dest = File(base + "style.css")
|
val dest = File(base + "style.css")
|
||||||
FileUtils.copyURLToFile(cssFile, dest)
|
FileUtils.copyURLToFile(cssFile, dest)
|
||||||
logger.debug("Done exporting.")
|
logger.debug("Done exporting.")
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
logger.error("Caught an exception!", e)
|
logger.error("Caught an exception!", e)
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(FileNotFoundException::class)
|
@Throws(FileNotFoundException::class)
|
||||||
private fun getWriter(filename: String): OutputStreamWriter {
|
private fun getWriter(filename: String): OutputStreamWriter {
|
||||||
logger.trace("Creating writer for file {}", Utils.anonymize(filename))
|
logger.trace("Creating writer for file {}", Utils.anonymize(filename))
|
||||||
return OutputStreamWriter(FileOutputStream(filename), Charset.forName("UTF-8").newEncoder())
|
return OutputStreamWriter(FileOutputStream(filename), Charset.forName("UTF-8").newEncoder())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun intArrayToString(data: Array<IntArray>): String {
|
private fun intArrayToString(data: Array<IntArray>): String {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
sb.append("[")
|
sb.append("[")
|
||||||
for (x in data.indices) {
|
for (x in data.indices) {
|
||||||
for (y in 0 until data[x].size) {
|
for (y in 0 until data[x].size) {
|
||||||
if (x > 0 || y > 0) sb.append(",")
|
if (x > 0 || y > 0) sb.append(",")
|
||||||
sb.append("[" + x + "," + y + "," + data[x][y] + "]")
|
sb.append("[" + x + "," + y + "," + data[x][y] + "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.append("]")
|
sb.append("]")
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mapToString(map: Map<String, Int>): String {
|
private fun mapToString(map: Map<String, Int>): String {
|
||||||
val sb = StringBuilder("[")
|
val sb = StringBuilder("[")
|
||||||
for ((key, value) in map) {
|
for ((key, value) in map) {
|
||||||
sb.append("['$key', $value],")
|
sb.append("['$key', $value],")
|
||||||
}
|
}
|
||||||
sb.append("]")
|
sb.append("]")
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(HTMLExporter::class.java)
|
private val logger = LoggerFactory.getLogger(HTMLExporter::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,25 +44,25 @@ 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, 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
|
||||||
|
|
||||||
open val downloaded: Boolean
|
open val downloaded: Boolean
|
||||||
get() = File(targetPathAndFilename).isFile()
|
get() = File(targetPathAndFilename).isFile()
|
||||||
|
|
||||||
val downloading: Boolean
|
val downloading: Boolean
|
||||||
get() = File("${targetPathAndFilename}.downloading").isFile()
|
get() = File("${targetPathAndFilename}.downloading").isFile()
|
||||||
|
|
||||||
open val targetPath: String
|
open val targetPath: String
|
||||||
get() {
|
get() {
|
||||||
val path = user.fileBase + Config.FILE_FILES_BASE + File.separatorChar
|
val path = user.fileBase + Config.FILE_FILES_BASE + File.separatorChar
|
||||||
File(path).mkdirs()
|
File(path).mkdirs()
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
open val targetFilename: String
|
open val targetFilename: String
|
||||||
get() {
|
get() {
|
||||||
val message_id = message.getId()
|
val message_id = message.getId()
|
||||||
var to = message.getToId()
|
var to = message.getToId()
|
||||||
if (to is TLPeerChannel) {
|
if (to is TLPeerChannel) {
|
||||||
@ -71,30 +71,30 @@ abstract class AbstractMediaFileManager(protected var message: TLMessage, protec
|
|||||||
} else return "${message_id}.$extension"
|
} else return "${message_id}.$extension"
|
||||||
}
|
}
|
||||||
|
|
||||||
open val targetPathAndFilename: String
|
open val targetPathAndFilename: String
|
||||||
get() = targetPath + targetFilename
|
get() = targetPath + targetFilename
|
||||||
|
|
||||||
abstract val letter: String
|
abstract val letter: String
|
||||||
abstract val name: String
|
abstract val name: String
|
||||||
abstract val description: String
|
abstract val description: String
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
||||||
abstract fun download()
|
abstract fun download()
|
||||||
|
|
||||||
protected fun extensionFromMimetype(mime: String): String {
|
protected fun extensionFromMimetype(mime: String): String {
|
||||||
when (mime) {
|
when (mime) {
|
||||||
"text/plain" -> return "txt"
|
"text/plain" -> return "txt"
|
||||||
}
|
}
|
||||||
|
|
||||||
val i = mime.lastIndexOf('/')
|
val i = mime.lastIndexOf('/')
|
||||||
val ext = mime.substring(i + 1).toLowerCase()
|
val ext = mime.substring(i + 1).toLowerCase()
|
||||||
|
|
||||||
return if (ext === "unknown") "dat" else ext
|
return if (ext === "unknown") "dat" else ext
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun throwUnexpectedObjectError(o: Any) {
|
fun throwUnexpectedObjectError(o: Any) {
|
||||||
throw RuntimeException("Unexpected " + o.javaClass.getName())
|
throw RuntimeException("Unexpected " + o.javaClass.getName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,63 +43,63 @@ 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, 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
|
||||||
|
|
||||||
open val isSticker: Boolean
|
open val isSticker: Boolean
|
||||||
get() {
|
get() {
|
||||||
if (this.isEmpty || doc == null) return false
|
if (this.isEmpty || doc == null) return false
|
||||||
return doc!!.getAttributes()?.filter{it is TLDocumentAttributeSticker}?.isNotEmpty() ?: false
|
return doc!!.getAttributes()?.filter { it is TLDocumentAttributeSticker }?.isNotEmpty() ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
override val size: Int
|
override val size: Int
|
||||||
get() = if (doc != null) doc!!.getSize() else 0
|
get() = if (doc != null) doc!!.getSize() else 0
|
||||||
|
|
||||||
open override val letter: String = "d"
|
open override val letter: String = "d"
|
||||||
open override val name: String = "document"
|
open override val name: String = "document"
|
||||||
open override val description: String = "Document"
|
open override val description: String = "Document"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val d = (msg.getMedia() as TLMessageMediaDocument).getDocument()
|
val d = (msg.getMedia() as TLMessageMediaDocument).getDocument()
|
||||||
if (d is TLDocument) {
|
if (d is TLDocument) {
|
||||||
this.doc = d
|
this.doc = d
|
||||||
} else if (d is TLDocumentEmpty) {
|
} else if (d is TLDocumentEmpty) {
|
||||||
this.isEmpty = true
|
this.isEmpty = true
|
||||||
} else {
|
} else {
|
||||||
throwUnexpectedObjectError(d)
|
throwUnexpectedObjectError(d)
|
||||||
}
|
}
|
||||||
extension = processExtension()
|
extension = processExtension()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processExtension(): String {
|
private fun processExtension(): String {
|
||||||
if (doc == null) return "empty"
|
if (doc == null) return "empty"
|
||||||
var ext: String? = null
|
var ext: String? = null
|
||||||
var original_filename: String? = null
|
var original_filename: String? = null
|
||||||
if (doc!!.getAttributes() != null)
|
if (doc!!.getAttributes() != null)
|
||||||
for (attr in doc!!.getAttributes()) {
|
for (attr in doc!!.getAttributes()) {
|
||||||
if (attr is TLDocumentAttributeFilename) {
|
if (attr is TLDocumentAttributeFilename) {
|
||||||
original_filename = attr.getFileName()
|
original_filename = attr.getFileName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (original_filename != null) {
|
if (original_filename != null) {
|
||||||
val i = original_filename.lastIndexOf('.')
|
val i = original_filename.lastIndexOf('.')
|
||||||
if (i > 0) ext = original_filename.substring(i + 1)
|
if (i > 0) ext = original_filename.substring(i + 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
if (ext == null) {
|
if (ext == null) {
|
||||||
ext = extensionFromMimetype(doc!!.getMimeType())
|
ext = extensionFromMimetype(doc!!.getMimeType())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sometimes, extensions contain a trailing double quote. Remove this. Fixes #12.
|
// Sometimes, extensions contain a trailing double quote. Remove this. Fixes #12.
|
||||||
ext = ext.replace("\"", "")
|
ext = ext.replace("\"", "")
|
||||||
|
|
||||||
return ext
|
return ext
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
||||||
override fun download() {
|
override fun download() {
|
||||||
if (doc != null) {
|
if (doc != null) {
|
||||||
DownloadManager.downloadFile(targetPathAndFilename, size, doc!!.getDcId(), doc!!.getId(), doc!!.getAccessHash())
|
DownloadManager.downloadFile(targetPathAndFilename, size, doc!!.getDcId(), doc!!.getId(), doc!!.getAccessHash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,32 +42,32 @@ 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, c: TelegramClient): 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, c)
|
||||||
} else if (media is TLMessageMediaDocument) {
|
} else if (media is TLMessageMediaDocument) {
|
||||||
val d = DocumentFileManager(m, u, c)
|
val d = DocumentFileManager(m, u, c)
|
||||||
return if (d.isSticker) {
|
return if (d.isSticker) {
|
||||||
StickerFileManager(m, u, c)
|
StickerFileManager(m, u, c)
|
||||||
} else d
|
} else d
|
||||||
} else if (media is TLMessageMediaGeo) {
|
} else if (media is TLMessageMediaGeo) {
|
||||||
return GeoFileManager(m, u, c)
|
return GeoFileManager(m, u, c)
|
||||||
} else if (media is TLMessageMediaEmpty) {
|
} else if (media is TLMessageMediaEmpty) {
|
||||||
return UnsupportedFileManager(m, u, c, "empty")
|
return UnsupportedFileManager(m, u, c, "empty")
|
||||||
} else if (media is TLMessageMediaUnsupported) {
|
} else if (media is TLMessageMediaUnsupported) {
|
||||||
return UnsupportedFileManager(m, u, c, "unsupported")
|
return UnsupportedFileManager(m, u, c, "unsupported")
|
||||||
} else if (media is TLMessageMediaWebPage) {
|
} else if (media is TLMessageMediaWebPage) {
|
||||||
return UnsupportedFileManager(m, u, c, "webpage")
|
return UnsupportedFileManager(m, u, c, "webpage")
|
||||||
} else if (media is TLMessageMediaContact) {
|
} else if (media is TLMessageMediaContact) {
|
||||||
return UnsupportedFileManager(m, u, c, "contact")
|
return UnsupportedFileManager(m, u, c, "contact")
|
||||||
} else if (media is TLMessageMediaVenue) {
|
} else if (media is TLMessageMediaVenue) {
|
||||||
return UnsupportedFileManager(m, u, c, "venue")
|
return UnsupportedFileManager(m, u, c, "venue")
|
||||||
} else {
|
} else {
|
||||||
AbstractMediaFileManager.throwUnexpectedObjectError(media)
|
AbstractMediaFileManager.throwUnexpectedObjectError(media)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,39 +44,39 @@ 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, 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.
|
||||||
override val size: Int
|
override val size: Int
|
||||||
get() {
|
get() {
|
||||||
val f = File(targetPathAndFilename)
|
val f = File(targetPathAndFilename)
|
||||||
return if (f.isFile()) f.length().toInt() else 100000
|
return if (f.isFile()) f.length().toInt() else 100000
|
||||||
}
|
}
|
||||||
|
|
||||||
override val extension: String
|
override val extension: String
|
||||||
get() = "png"
|
get() = "png"
|
||||||
|
|
||||||
override val letter = "g"
|
override val letter = "g"
|
||||||
override val name = "geo"
|
override val name = "geo"
|
||||||
override val description = "Geolocation"
|
override val description = "Geolocation"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val g = (msg.getMedia() as TLMessageMediaGeo).getGeo()
|
val g = (msg.getMedia() as TLMessageMediaGeo).getGeo()
|
||||||
if (g is TLGeoPoint) {
|
if (g is TLGeoPoint) {
|
||||||
this.geo = g
|
this.geo = g
|
||||||
} else if (g is TLGeoPointEmpty) {
|
} else if (g is TLGeoPointEmpty) {
|
||||||
this.isEmpty = true
|
this.isEmpty = true
|
||||||
} else {
|
} else {
|
||||||
throwUnexpectedObjectError(g)
|
throwUnexpectedObjectError(g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun download() {
|
override fun download() {
|
||||||
val url = "https://maps.googleapis.com/maps/api/staticmap?" +
|
val url = "https://maps.googleapis.com/maps/api/staticmap?" +
|
||||||
"center=" + geo.getLat() + "," + geo.getLong() + "&" +
|
"center=" + geo.getLat() + "," + geo.getLong() + "&" +
|
||||||
"zoom=14&size=300x150&scale=2&format=png&" +
|
"zoom=14&size=300x150&scale=2&format=png&" +
|
||||||
"key=" + Config.SECRET_GMAPS
|
"key=" + Config.SECRET_GMAPS
|
||||||
DownloadManager.downloadExternalFile(targetPathAndFilename, url)
|
DownloadManager.downloadExternalFile(targetPathAndFilename, url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,43 +43,43 @@ 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, 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
|
||||||
|
|
||||||
override val extension = "jpg"
|
override val extension = "jpg"
|
||||||
override val letter = "p"
|
override val letter = "p"
|
||||||
override val name = "photo"
|
override val name = "photo"
|
||||||
override val description = "Photo"
|
override val description = "Photo"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val p = (msg.getMedia() as TLMessageMediaPhoto).getPhoto()
|
val p = (msg.getMedia() as TLMessageMediaPhoto).getPhoto()
|
||||||
if (p is TLPhoto) {
|
if (p is TLPhoto) {
|
||||||
this.photo = p
|
this.photo = p
|
||||||
|
|
||||||
var biggest: TLPhotoSize? = null
|
var biggest: TLPhotoSize? = null
|
||||||
for (s in photo.getSizes())
|
for (s in photo.getSizes())
|
||||||
if (s is TLPhotoSize) {
|
if (s is TLPhotoSize) {
|
||||||
if (biggest == null || s.getW() > biggest.getW() && s.getH() > biggest.getH()) {
|
if (biggest == null || s.getW() > biggest.getW() && s.getH() > biggest.getH()) {
|
||||||
biggest = s
|
biggest = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (biggest == null) {
|
if (biggest == null) {
|
||||||
throw RuntimeException("Could not find a size for a photo.")
|
throw RuntimeException("Could not find a size for a photo.")
|
||||||
}
|
}
|
||||||
this.photo_size = biggest
|
this.photo_size = biggest
|
||||||
this.size = biggest.getSize()
|
this.size = biggest.getSize()
|
||||||
} else if (p is TLPhotoEmpty) {
|
} else if (p is TLPhotoEmpty) {
|
||||||
this.isEmpty = true
|
this.isEmpty = true
|
||||||
} else {
|
} else {
|
||||||
throwUnexpectedObjectError(p)
|
throwUnexpectedObjectError(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
||||||
override fun download() {
|
override fun download() {
|
||||||
if (isEmpty) return
|
if (isEmpty) return
|
||||||
val loc = photo_size.getLocation() as TLFileLocation
|
val loc = photo_size.getLocation() as TLFileLocation
|
||||||
DownloadManager.downloadFile(targetPathAndFilename, size, loc.getDcId(), loc.getVolumeId(), loc.getLocalId(), loc.getSecret())
|
DownloadManager.downloadFile(targetPathAndFilename, size, loc.getDcId(), loc.getVolumeId(), loc.getLocalId(), loc.getSecret())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,62 +52,62 @@ import org.apache.commons.io.FileUtils
|
|||||||
|
|
||||||
class StickerFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : DocumentFileManager(msg, user, client) {
|
class StickerFileManager(msg: TLMessage, user: UserManager, client: TelegramClient) : DocumentFileManager(msg, user, client) {
|
||||||
|
|
||||||
override val isSticker = true
|
override val isSticker = true
|
||||||
|
|
||||||
private val filenameBase: String
|
private val filenameBase: String
|
||||||
get() {
|
get() {
|
||||||
var sticker: TLDocumentAttributeSticker? = null
|
var sticker: TLDocumentAttributeSticker? = null
|
||||||
for (attr in doc!!.getAttributes()) {
|
for (attr in doc!!.getAttributes()) {
|
||||||
if (attr is TLDocumentAttributeSticker) {
|
if (attr is TLDocumentAttributeSticker) {
|
||||||
sticker = attr
|
sticker = attr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val file = StringBuilder()
|
val file = StringBuilder()
|
||||||
val set = sticker!!.getStickerset()
|
val set = sticker!!.getStickerset()
|
||||||
if (set is TLInputStickerSetShortName) {
|
if (set is TLInputStickerSetShortName) {
|
||||||
file.append(set.getShortName())
|
file.append(set.getShortName())
|
||||||
} else if (set is TLInputStickerSetID) {
|
} else if (set is TLInputStickerSetID) {
|
||||||
file.append(set.getId())
|
file.append(set.getId())
|
||||||
}
|
}
|
||||||
file.append("_")
|
file.append("_")
|
||||||
file.append(sticker.getAlt().hashCode())
|
file.append(sticker.getAlt().hashCode())
|
||||||
return file.toString()
|
return file.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val targetFilename: String
|
override val targetFilename: String
|
||||||
get() = filenameBase + "." + extension
|
get() = filenameBase + "." + extension
|
||||||
|
|
||||||
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 = user.fileBase + Config.FILE_FILES_BASE + File.separatorChar + Config.FILE_STICKER_BASE + File.separatorChar
|
||||||
File(path).mkdirs()
|
File(path).mkdirs()
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
override var extension = "webp"
|
override var extension = "webp"
|
||||||
|
|
||||||
override val letter: String
|
override val letter: String
|
||||||
get() = "s"
|
get() = "s"
|
||||||
override val name: String
|
override val name: String
|
||||||
get() = "sticker"
|
get() = "sticker"
|
||||||
override val description: String
|
override val description: String
|
||||||
get() = "Sticker"
|
get() = "Sticker"
|
||||||
|
|
||||||
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
@Throws(RpcErrorException::class, IOException::class, TimeoutException::class)
|
||||||
override fun download() {
|
override fun download() {
|
||||||
val old_file = Config.FILE_BASE + File.separatorChar + Config.FILE_STICKER_BASE + File.separatorChar + targetFilename
|
val old_file = Config.FILE_BASE + File.separatorChar + Config.FILE_STICKER_BASE + File.separatorChar + targetFilename
|
||||||
|
|
||||||
logger.trace("Old filename exists: {}", File(old_file).exists())
|
logger.trace("Old filename exists: {}", File(old_file).exists())
|
||||||
|
|
||||||
if (File(old_file).exists()) {
|
if (File(old_file).exists()) {
|
||||||
Files.copy(Paths.get(old_file), Paths.get(targetPathAndFilename), StandardCopyOption.REPLACE_EXISTING)
|
Files.copy(Paths.get(old_file), Paths.get(targetPathAndFilename), StandardCopyOption.REPLACE_EXISTING)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
super.download()
|
super.download()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(StickerFileManager::class.java)
|
private val logger = LoggerFactory.getLogger(StickerFileManager::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,15 +44,15 @@ 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, 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 = ""
|
||||||
override val extension = ""
|
override val extension = ""
|
||||||
override val size = 0
|
override val size = 0
|
||||||
override var isEmpty = false
|
override var isEmpty = false
|
||||||
override val downloaded = false
|
override val downloaded = false
|
||||||
override val letter = " "
|
override val letter = " "
|
||||||
override val description = "Unsupported / non-downloadable Media"
|
override val description = "Unsupported / non-downloadable Media"
|
||||||
|
|
||||||
override fun download() {}
|
override fun download() {}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user