mirror of
				https://github.com/fabianonline/telegram_backup.git
				synced 2025-10-31 15:49:22 +00:00 
			
		
		
		
	Added a simple 51converter to convert messages with api layer 51 to JSON.
This commit is contained in:
		| @@ -11,7 +11,7 @@ repositories { | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
| 	compile('com.github.badoualy:kotlogram:666a81ef9d6707f117a3fecc2d21c91d51c7d075') { | ||||
| 	compile('com.github.badoualy:kotlogram:0.0.6') { | ||||
| 		exclude module: 'slf4j-simple' | ||||
| 	} | ||||
| 	compile 'org.xerial:sqlite-jdbc:3.16.1' | ||||
|   | ||||
| @@ -1,140 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.TelegramApiStorage; | ||||
| import com.github.badoualy.telegram.mtproto.model.DataCenter; | ||||
| import com.github.badoualy.telegram.mtproto.auth.AuthKey; | ||||
| import com.github.badoualy.telegram.mtproto.model.MTSession; | ||||
|  | ||||
| import org.apache.commons.io.FileUtils; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.IOException; | ||||
|  | ||||
| class ApiStorage implements TelegramApiStorage { | ||||
| 	private String prefix = null; | ||||
| 	private boolean do_save = false; | ||||
| 	private AuthKey auth_key = null; | ||||
| 	private DataCenter dc = null; | ||||
| 	private File file_auth_key = null; | ||||
| 	private File file_dc = null; | ||||
| 	 | ||||
| 	public ApiStorage(String prefix) { | ||||
| 		this.setPrefix(prefix); | ||||
| 	} | ||||
| 	 | ||||
| 	public void setPrefix(String prefix) { | ||||
| 		this.prefix = prefix; | ||||
| 		this.do_save = (this.prefix!=null); | ||||
| 		if (this.do_save) { | ||||
| 			String base = Config.FILE_BASE + | ||||
| 				File.separatorChar + | ||||
| 				this.prefix + | ||||
| 				File.separatorChar; | ||||
| 			this.file_auth_key = new File(base + Config.FILE_NAME_AUTH_KEY); | ||||
| 			this.file_dc = new File(base + Config.FILE_NAME_DC); | ||||
| 			this._saveAuthKey(); | ||||
| 			this._saveDc(); | ||||
| 		} else { | ||||
| 			this.file_auth_key = null; | ||||
| 			this.file_dc = null; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public void saveAuthKey(AuthKey authKey) { | ||||
| 		this.auth_key = authKey; | ||||
| 		this._saveAuthKey(); | ||||
| 	} | ||||
| 	 | ||||
| 	private void _saveAuthKey() { | ||||
| 		if (this.do_save && this.auth_key!=null) { | ||||
| 			try { | ||||
| 				FileUtils.writeByteArrayToFile(this.file_auth_key, this.auth_key.getKey()); | ||||
| 			} catch (IOException e) { | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public AuthKey loadAuthKey() { | ||||
| 		if (this.auth_key != null) return this.auth_key; | ||||
| 		if (this.file_auth_key != null) { | ||||
| 			try { | ||||
| 				return new AuthKey(FileUtils.readFileToByteArray(this.file_auth_key)); | ||||
| 			} catch (IOException e) { | ||||
| 				if (!(e instanceof FileNotFoundException)) e.printStackTrace(); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		return null; | ||||
| 	} | ||||
| 	 | ||||
| 	public void saveDc(DataCenter dc) { | ||||
| 		this.dc = dc; | ||||
| 		this._saveDc(); | ||||
| 	} | ||||
| 	 | ||||
| 	private void _saveDc() { | ||||
| 		if (this.do_save && this.dc != null) { | ||||
| 			try { | ||||
| 				FileUtils.write(this.file_dc, this.dc.toString()); | ||||
| 			} catch (IOException e) { | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public DataCenter loadDc() { | ||||
| 		if (this.dc != null) return this.dc; | ||||
| 		if (this.file_dc != null) { | ||||
| 			try { | ||||
| 				String[] infos = FileUtils.readFileToString(this.file_dc).split(":"); | ||||
| 				return new DataCenter(infos[0], Integer.parseInt(infos[1])); | ||||
| 			} catch (IOException e) { | ||||
| 				if (!(e instanceof FileNotFoundException)) e.printStackTrace(); | ||||
| 			} | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
| 	 | ||||
| 	public void deleteAuthKey() { | ||||
| 		if (this.do_save) { | ||||
| 			try { | ||||
| 				FileUtils.forceDelete(this.file_auth_key); | ||||
| 			} catch (IOException e) { | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public void deleteDc() { | ||||
| 		if (this.do_save) { | ||||
| 			try { | ||||
| 				FileUtils.forceDelete(this.file_dc); | ||||
| 			} catch (IOException e) { | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public void saveSession(MTSession session) { | ||||
| 	} | ||||
| 	 | ||||
| 	public MTSession loadSession() { return null; } | ||||
| } | ||||
| @@ -16,10 +16,6 @@ | ||||
|  | ||||
| package de.fabianonline.telegram_backup; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.TelegramUpdateHandler; | ||||
| import de.fabianonline.telegram_backup.exporter.HTMLExporter; | ||||
| import de.fabianonline.telegram_backup.models.Message; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.Kotlogram; | ||||
| import com.github.badoualy.telegram.api.TelegramApp; | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| @@ -38,7 +34,6 @@ import org.slf4j.Logger; | ||||
|  | ||||
| public class CommandLineController { | ||||
| 	private static Logger logger = LoggerFactory.getLogger(CommandLineController.class); | ||||
| 	private ApiStorage storage; | ||||
| 	public TelegramApp app; | ||||
| 	 | ||||
| 	public CommandLineController() { | ||||
| @@ -70,103 +65,29 @@ public class CommandLineController { | ||||
| 		 | ||||
| 		logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login); | ||||
|  | ||||
| 		logger.info("Initializing ApiStorage"); | ||||
| 		storage = new ApiStorage(account); | ||||
| 		logger.info("Initializing TelegramUpdateHandler"); | ||||
| 		TelegramUpdateHandler handler = new TelegramUpdateHandler(); | ||||
| 		logger.info("Creating Client"); | ||||
| 		TelegramClient client = Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, handler); | ||||
| 		TelegramClient client = null; //Kotlogram.getDefaultClient(app, storage, Kotlogram.PROD_DC4, handler); | ||||
| 		 | ||||
| 		try { | ||||
| 			logger.info("Initializing UserManager"); | ||||
| 			UserManager.init(client); | ||||
| 			UserManager.init(account); | ||||
| 			Database.init(client); | ||||
| 			 | ||||
| 			UserManager user = UserManager.getInstance(); | ||||
| 			// do stuff | ||||
| 			Database.getInstance().jsonify(); | ||||
| 			 | ||||
| 			if (!CommandLineOptions.cmd_login && !user.isLoggedIn()) { | ||||
| 				System.out.println("Your authorization data is invalid or missing. You will have to login with Telegram again."); | ||||
| 				CommandLineOptions.cmd_login = true; | ||||
| 			} | ||||
| 			if (account!=null && user.isLoggedIn()) { | ||||
| 				if (!account.equals("+" + user.getUser().getPhone())) { | ||||
| 					logger.error("Account: {}, user.getUser().getPhone(): +{}", Utils.anonymize(account), Utils.anonymize(user.getUser().getPhone())); | ||||
| 					throw new RuntimeException("Account / User mismatch"); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			if (CommandLineOptions.cmd_stats) { | ||||
| 				cmd_stats(); | ||||
| 				System.exit(0); | ||||
| 			} | ||||
| 			 | ||||
| 			logger.debug("CommandLineOptions.val_export: {}", CommandLineOptions.val_export); | ||||
| 			if (CommandLineOptions.val_export != null) { | ||||
| 				if (CommandLineOptions.val_export.toLowerCase().equals("html")) { | ||||
| 					(new HTMLExporter()).export(); | ||||
| 					System.exit(0); | ||||
| 				} else { | ||||
| 					show_error("Unknown export format."); | ||||
| 				} | ||||
| 			} | ||||
| 		 | ||||
| 			logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login); | ||||
| 			if (CommandLineOptions.cmd_login) { | ||||
| 				cmd_login(account); | ||||
| 				System.exit(0); | ||||
| 			} | ||||
| 			 | ||||
| 			if (user.isLoggedIn()) { | ||||
| 				System.out.println("You are logged in as " + Utils.anonymize(user.getUserString())); | ||||
| 			} else { | ||||
| 				System.out.println("You are not logged in."); | ||||
| 				System.exit(1); | ||||
| 			} | ||||
| 			 | ||||
| 			logger.info("Initializing Download Manager"); | ||||
| 			 | ||||
| 			if (CommandLineOptions.val_test != null) { | ||||
| 				if (CommandLineOptions.val_test == 1) { | ||||
| 					TestFeatures.test1(); | ||||
| 				} else if (CommandLineOptions.val_test == 2) { | ||||
| 					TestFeatures.test2(user, client); | ||||
| 				} else if (CommandLineOptions.val_test == 3) { | ||||
| 					logger.debug(Message.get(39925).getMessage()); | ||||
| 				} else { | ||||
| 					System.out.println("Unknown test " + CommandLineOptions.val_test); | ||||
| 				} | ||||
| 				System.exit(1); | ||||
| 			} | ||||
| 			 | ||||
| 			DownloadManager d = new DownloadManager(client, new 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 { | ||||
| 				System.out.println("Skipping media download because --no-media is set."); | ||||
| 			} | ||||
| 		} catch (Exception e) { | ||||
| 			e.printStackTrace(); | ||||
| 			logger.error("Exception caught!", e); | ||||
| 		} finally { | ||||
| 			if (CommandLineOptions.cmd_daemon) { | ||||
| 				handler.activate(); | ||||
| 				System.out.println("DAEMON mode requested - keeping running."); | ||||
| 			} else { | ||||
| 				client.close(); | ||||
| 			System.out.println(); | ||||
| 			System.out.println("----- EXIT -----"); | ||||
| 			System.exit(0); | ||||
| 		} | ||||
| 	} | ||||
| 	} | ||||
| 	 | ||||
| 	private void printHeader() { | ||||
| 		System.out.println("Telegram_Backup version " + Config.APP_APPVER + ", Copyright (C) 2016, 2017 Fabian Schlenz"); | ||||
| 		System.out.println("Telegram_Backup 51convert version " + Config.APP_APPVER + ", Copyright (C) 2016, 2017 Fabian Schlenz"); | ||||
| 		System.out.println(); | ||||
| 		System.out.println("Telegram_Backup comes with ABSOLUTELY NO WARRANTY. This is free software, and you are"); | ||||
| 		System.out.println("welcome to redistribute it under certain conditions; run it with '--license' for details."); | ||||
| @@ -221,18 +142,6 @@ public class CommandLineController { | ||||
| 		return account; | ||||
| 	} | ||||
| 	 | ||||
| 	private void cmd_stats() { | ||||
| 		HashMap<String, Integer> map = new HashMap<String, Integer>(); | ||||
| 		map.put("count.accounts", Utils.getAccounts().size()); | ||||
| 		map.put("count.messages", Database.getInstance().getMessageCount()); | ||||
| 		map.put("messages.top_id", Database.getInstance().getTopMessageID()); | ||||
| 		for(Map.Entry<String, Integer> pair : Database.getInstance().getMessageMediaTypesWithCount().entrySet()) { | ||||
| 			map.put(pair.getKey(), pair.getValue()); | ||||
| 		} | ||||
| 		 | ||||
| 		System.out.println(map.toString()); | ||||
| 	} | ||||
| 	 | ||||
| 	private void cmd_login(String phone) throws RpcErrorException, IOException { | ||||
| 		UserManager user = UserManager.getInstance(); | ||||
| 		if (phone==null) { | ||||
| @@ -251,7 +160,6 @@ public class CommandLineController { | ||||
| 			String pw = getPassword(); | ||||
| 			user.verifyPassword(pw); | ||||
| 		} | ||||
| 		storage.setPrefix("+" + user.getUser().getPhone()); | ||||
| 		 | ||||
| 		System.out.println("Everything seems fine. Please run this tool again with '--account +" + Utils.anonymize(user.getUser().getPhone()) + " to use this account."); | ||||
| 	} | ||||
| @@ -275,22 +183,6 @@ public class CommandLineController { | ||||
| 	 | ||||
| 	private void show_help() { | ||||
| 		System.out.println("Valid options are:"); | ||||
| 		System.out.println("  -h, --help                   Shows this help."); | ||||
| 		System.out.println("  -a, --account <x>            Use account <x>."); | ||||
| 		System.out.println("  -l, --login                  Login to an existing telegram account."); | ||||
| 		System.out.println("      --debug                  Shows some debug information."); | ||||
| 		System.out.println("      --trace                  Shows lots of debug information. Overrides --debug."); | ||||
| 		System.out.println("      --trace-telegram         Shows lots of debug messages from the library used to access Telegram."); | ||||
| 		System.out.println("  -A, --list-accounts          List all existing accounts "); | ||||
| 		System.out.println("      --limit-messages <x>     Downloads at most the most recent <x> messages."); | ||||
| 		System.out.println("      --no-media               Do not download media files."); | ||||
| 		System.out.println("  -t, --target <x>             Target directory for the files."); | ||||
| 		System.out.println("  -e, --export <format>        Export the database. Valid formats are:"); | ||||
| 		System.out.println("                               html - Creates HTML files."); | ||||
| 		System.out.println("      --license                Displays the license of this program."); | ||||
| 		System.out.println("  -d, --daemon                 Keep running and automatically save new messages."); | ||||
| 		System.out.println("      --anonymize              (Try to) Remove all sensitive information from output. Useful for requesting support."); | ||||
| 		System.out.println("      --stats                  Print some usage statistics."); | ||||
| 	} | ||||
| 	 | ||||
| 	private void list_accounts() { | ||||
|   | ||||
| @@ -1,58 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.DownloadProgressInterface; | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager; | ||||
|  | ||||
| class CommandLineDownloadProgress implements DownloadProgressInterface { | ||||
| 	private int mediaCount = 0; | ||||
| 	private int i = 0; | ||||
| 	 | ||||
| 	public void onMessageDownloadStart(int count) { i=0; System.out.println("Downloading " + count + " messages."); } | ||||
| 	public void onMessageDownloaded(int number) { i+=number; System.out.print("..." + i); } | ||||
| 	public void onMessageDownloadFinished() { System.out.println(" done."); } | ||||
| 	 | ||||
| 	public void onMediaDownloadStart(int count) { | ||||
| 		i = 0; | ||||
| 		mediaCount = count; | ||||
| 		System.out.println("Checking and downloading media."); | ||||
| 		System.out.println("Legend:"); | ||||
| 		System.out.println("'V' - Video         'P' - Photo         'D' - Document"); | ||||
| 		System.out.println("'S' - Sticker       'A' - Audio         'G' - Geolocation"); | ||||
| 		System.out.println("'.' - Previously downloaded file        'e' - Empty file"); | ||||
| 		System.out.println("' ' - Ignored media type (weblinks or contacts, for example)"); | ||||
| 		System.out.println("'x' - File skipped because of timeout errors"); | ||||
| 		System.out.println("" + count + " Files to check / download");     | ||||
| 	} | ||||
| 	 | ||||
| 	public void onMediaDownloaded(AbstractMediaFileManager fm) { | ||||
| 		show(fm.getLetter().toUpperCase()); | ||||
| 	} | ||||
| 	 | ||||
| 	public void onMediaDownloadedEmpty() { show("e"); } | ||||
| 	public void onMediaAlreadyPresent(AbstractMediaFileManager fm) { | ||||
| 		show("."); | ||||
| 	} | ||||
| 	public void onMediaSkipped() { show("x"); } | ||||
| 	 | ||||
| 	public void onMediaDownloadFinished() { showNewLine(); System.out.println("Done."); } | ||||
| 	 | ||||
| 	private void show(String letter) { System.out.print(letter); i++; if (i % 100 == 0) showNewLine();} | ||||
| 	private void showNewLine() { System.out.println(" - " + i + "/" + mediaCount); } | ||||
| } | ||||
| 	 | ||||
| @@ -75,18 +75,6 @@ public class CommandLineRunner { | ||||
| 	} | ||||
| 	 | ||||
| 	public static boolean checkVersion() { | ||||
| 		Version v = Utils.getNewestVersion(); | ||||
| 		if (v!=null && v.isNewer) { | ||||
| 			System.out.println("A newer version is vailable!"); | ||||
| 			System.out.println("You are using: " + Config.APP_APPVER); | ||||
| 			System.out.println("Available:     " + v.version); | ||||
| 			System.out.println("Get it here:   " + v.url); | ||||
| 			System.out.println(); | ||||
| 			System.out.println("Changes in this version:"); | ||||
| 			System.out.println(v.body); | ||||
| 			System.out.println(); | ||||
| 			return false; | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.slf4j.Logger; | ||||
| import com.google.gson.Gson; | ||||
| import com.github.badoualy.telegram.api.Kotlogram; | ||||
|  | ||||
| import java.sql.Connection; | ||||
| import java.sql.DriverManager; | ||||
| @@ -45,9 +46,6 @@ import java.nio.file.StandardCopyOption; | ||||
| import java.nio.file.FileAlreadyExistsException; | ||||
| import java.text.SimpleDateFormat; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager; | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory; | ||||
|  | ||||
| public class Database { | ||||
| 	private Connection conn; | ||||
| 	private Statement stmt; | ||||
| @@ -77,10 +75,6 @@ public class Database { | ||||
| 			CommandLineController.show_error("Could not connect to SQLITE database."); | ||||
| 		} | ||||
| 		 | ||||
| 		// Run updates | ||||
| 		DatabaseUpdates updates = new DatabaseUpdates(conn, this); | ||||
| 		updates.doUpdates(); | ||||
| 		 | ||||
| 		System.out.println("Database is ready."); | ||||
| 	} | ||||
| 	 | ||||
| @@ -93,537 +87,6 @@ public class Database { | ||||
| 		return instance; | ||||
| 	} | ||||
| 	 | ||||
| 	public void backupDatabase(int currentVersion) { | ||||
| 		String filename = String.format(Config.FILE_NAME_DB_BACKUP, currentVersion); | ||||
| 		System.out.println("  Creating a backup of your database as " + filename); | ||||
| 		try { | ||||
| 			String src = user_manager.getFileBase() + Config.FILE_NAME_DB; | ||||
| 			String dst = user_manager.getFileBase() + filename; | ||||
| 			logger.debug("Copying {} to {}", src, dst); | ||||
| 			Files.copy( | ||||
| 				new File(src).toPath(), | ||||
| 				new File(dst).toPath()); | ||||
| 		} catch (FileAlreadyExistsException e) { | ||||
| 			logger.warn("Backup already exists:", e); | ||||
| 		} catch (IOException e) { | ||||
| 			e.printStackTrace(); | ||||
| 			throw new RuntimeException("Could not create backup."); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public int getTopMessageID() { | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT MAX(id) FROM messages"); | ||||
| 			rs.next(); | ||||
| 			return rs.getInt(1); | ||||
| 		} catch (SQLException e) { | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public void logRun(int start_id, int end_id, int count) { | ||||
| 		try { | ||||
| 			PreparedStatement ps = conn.prepareStatement("INSERT INTO runs "+ | ||||
| 				"(time,        start_id, end_id, count_missing) "+ | ||||
| 				"VALUES "+ | ||||
| 				"(DateTime('now'),    ?,        ?,      ?            )"); | ||||
| 			ps.setInt(1, start_id); | ||||
| 			ps.setInt(2, end_id); | ||||
| 			ps.setInt(3, count); | ||||
| 			ps.execute(); | ||||
| 		} catch (SQLException e) {} | ||||
| 	} | ||||
| 	 | ||||
| 	public int getMessageCount() { | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM messages"); | ||||
| 			rs.next(); | ||||
| 			return rs.getInt(1); | ||||
| 		} catch (SQLException e) { | ||||
| 			throw new RuntimeException("Could not get count of messages."); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public LinkedList<Integer> getMissingIDs() { | ||||
| 		try { | ||||
| 			LinkedList<Integer> missing = new LinkedList<Integer>(); | ||||
| 			int max = getTopMessageID(); | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT id FROM messages ORDER BY id"); | ||||
| 			rs.next(); | ||||
| 			int id=rs.getInt(1); | ||||
| 			for (int i=1; i<=max; i++) { | ||||
| 				if (i==id) { | ||||
| 					rs.next(); | ||||
| 					if (rs.isClosed()) { | ||||
| 						id = Integer.MAX_VALUE; | ||||
| 					} else { | ||||
| 						id=rs.getInt(1); | ||||
| 					} | ||||
| 				} else if (i<id) { | ||||
| 					missing.add(i); | ||||
| 				} | ||||
| 			} | ||||
| 			return missing; | ||||
| 		} catch(SQLException e) { | ||||
| 			e.printStackTrace(); | ||||
| 			throw new RuntimeException("Could not get list of ids."); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public synchronized void saveMessages(TLVector<TLAbsMessage> all, Integer api_layer, Gson gson) { | ||||
| 		try { | ||||
| 				//"(id, dialog_id, from_id, from_type, text, time, has_media, data, sticker, type) " + | ||||
| 				//"VALUES " + | ||||
| 				//"(?,  ?,         ?,       ?,         ?,    ?,    ?,         ?,    ?,       ?)"); | ||||
| 			String columns = | ||||
| 				"(id, message_type, dialog_id, chat_id, sender_id, fwd_from_id, text, time, has_media, media_type, media_file, media_size, data, api_layer, json) "+ | ||||
| 				"VALUES " + | ||||
| 				"(?,  ?,            ?,         ?,       ?,         ?,           ?,    ?,    ?,         ?,          ?,          ?,          ?,    ?,         ?)"; | ||||
| 				//1   2             3          4        5          6            7     8     9          10          11          12          13    14         15 | ||||
| 			PreparedStatement ps = conn.prepareStatement("INSERT OR REPLACE INTO messages " + columns); | ||||
| 			PreparedStatement ps_insert_or_ignore = conn.prepareStatement("INSERT OR IGNORE INTO messages " + columns); | ||||
|  | ||||
| 			for (TLAbsMessage abs : all) { | ||||
| 				if (abs instanceof TLMessage) { | ||||
| 					TLMessage msg = (TLMessage) abs; | ||||
| 					ps.setInt(1, msg.getId()); | ||||
| 					ps.setString(2, "message"); | ||||
| 					TLAbsPeer peer = msg.getToId(); | ||||
| 					if (peer instanceof TLPeerChat) { | ||||
| 						ps.setNull(3, Types.INTEGER); | ||||
| 						ps.setInt(4, ((TLPeerChat)peer).getChatId()); | ||||
| 					} else if (peer instanceof TLPeerUser) { | ||||
| 						int id = ((TLPeerUser)peer).getUserId(); | ||||
| 						if (id==this.user_manager.getUser().getId()) { | ||||
| 							id = msg.getFromId(); | ||||
| 						} | ||||
| 						ps.setInt(3, id); | ||||
| 						ps.setNull(4, Types.INTEGER); | ||||
| 					} else { | ||||
| 						throw new RuntimeException("Unexpected Peer type: " + peer.getClass().getName()); | ||||
| 					} | ||||
| 					ps.setInt(5, msg.getFromId()); | ||||
| 					 | ||||
| 					if (msg.getFwdFrom() != null && msg.getFwdFrom().getFromId() != null) { | ||||
| 						ps.setInt(6, msg.getFwdFrom().getFromId()); | ||||
| 					} else { | ||||
| 						ps.setNull(6, Types.INTEGER); | ||||
| 					} | ||||
|  | ||||
| 					String text = msg.getMessage(); | ||||
| 					if ((text==null || text.equals("")) && msg.getMedia()!=null) { | ||||
| 						if (msg.getMedia() instanceof TLMessageMediaDocument) { | ||||
| 							text = ((TLMessageMediaDocument)msg.getMedia()).getCaption(); | ||||
| 						} else if (msg.getMedia() instanceof TLMessageMediaPhoto) { | ||||
| 							text = ((TLMessageMediaPhoto)msg.getMedia()).getCaption(); | ||||
| 						} | ||||
| 					} | ||||
| 					ps.setString(7, text); | ||||
| 					ps.setString(8, ""+msg.getDate()); | ||||
| 					AbstractMediaFileManager f = FileManagerFactory.getFileManager(msg, user_manager, client); | ||||
| 					if (f==null) { | ||||
| 						ps.setNull(9, Types.BOOLEAN); | ||||
| 						ps.setNull(10, Types.VARCHAR); | ||||
| 						ps.setNull(11, Types.VARCHAR); | ||||
| 						ps.setNull(12, Types.INTEGER); | ||||
| 					} else { | ||||
| 						ps.setBoolean(9, true); | ||||
| 						ps.setString(10, f.getName()); | ||||
| 						ps.setString(11, f.getTargetFilename()); | ||||
| 						ps.setInt(12, f.getSize()); | ||||
| 					} | ||||
| 					ByteArrayOutputStream stream = new ByteArrayOutputStream(); | ||||
| 					msg.serializeBody(stream); | ||||
| 					ps.setBytes(13, stream.toByteArray()); | ||||
| 					ps.setInt(14, api_layer); | ||||
| 					ps.setString(15, gson.toJson(msg)); | ||||
| 					ps.addBatch(); | ||||
| 				} else if (abs instanceof TLMessageService) { | ||||
| 					ps_insert_or_ignore.setInt(1, abs.getId()); | ||||
| 					ps_insert_or_ignore.setString(2, "service_message"); | ||||
| 					ps_insert_or_ignore.setNull(3, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(4, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(5, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(6, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(7, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setNull(8, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(9, Types.BOOLEAN); | ||||
| 					ps_insert_or_ignore.setNull(10, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setNull(11, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setNull(12, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(13, Types.BLOB); | ||||
| 					ps_insert_or_ignore.setInt(14, api_layer); | ||||
| 					ps_insert_or_ignore.setString(15, gson.toJson((TLMessageService)abs)); | ||||
| 					ps_insert_or_ignore.addBatch(); | ||||
| 				} else if (abs instanceof TLMessageEmpty) { | ||||
| 					ps_insert_or_ignore.setInt(1, abs.getId()); | ||||
| 					ps_insert_or_ignore.setString(2, "empty_message"); | ||||
| 					ps_insert_or_ignore.setNull(3, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(4, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(5, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(6, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(7, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setNull(8, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(9, Types.BOOLEAN); | ||||
| 					ps_insert_or_ignore.setNull(10, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setNull(11, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setNull(12, Types.INTEGER); | ||||
| 					ps_insert_or_ignore.setNull(13, Types.BLOB); | ||||
| 					ps_insert_or_ignore.setInt(14, api_layer); | ||||
| 					ps_insert_or_ignore.setString(15, gson.toJson((TLMessageEmpty)abs)); | ||||
| 					ps_insert_or_ignore.addBatch(); | ||||
| 				} else { | ||||
| 					throw new RuntimeException("Unexpected Message type: " + abs.getClass().getName()); | ||||
| 				} | ||||
| 			} | ||||
| 			conn.setAutoCommit(false); | ||||
| 			ps.executeBatch(); | ||||
| 			ps.clearBatch(); | ||||
| 			ps_insert_or_ignore.executeBatch(); | ||||
| 			ps_insert_or_ignore.clearBatch(); | ||||
| 			conn.commit(); | ||||
| 			conn.setAutoCommit(true); | ||||
| 		} catch (Exception e) { | ||||
| 			e.printStackTrace(); | ||||
| 			throw new RuntimeException("Exception shown above happened."); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public synchronized void saveChats(TLVector<TLAbsChat> all, Gson gson) { | ||||
| 		try { | ||||
| 			PreparedStatement ps_insert_or_replace = conn.prepareStatement( | ||||
| 				"INSERT OR REPLACE INTO chats " + | ||||
| 					"(id, name, type, json) "+ | ||||
| 					"VALUES " + | ||||
| 					"(?,  ?,    ?,    ?)"); | ||||
| 			PreparedStatement ps_insert_or_ignore = conn.prepareStatement( | ||||
| 				"INSERT OR IGNORE INTO chats " + | ||||
| 					"(id, name, type, json) "+ | ||||
| 					"VALUES " + | ||||
| 					"(?,  ?,    ?,    ?)"); | ||||
|  | ||||
| 			for(TLAbsChat abs : all) { | ||||
| 				ps_insert_or_replace.setInt(1, abs.getId()); | ||||
| 				ps_insert_or_ignore.setInt(1, abs.getId()); | ||||
| 				if (abs instanceof TLChatEmpty) { | ||||
| 					ps_insert_or_ignore.setNull(2, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setString(3, "empty_chat"); | ||||
| 					ps_insert_or_ignore.setString(4, gson.toJson((TLChatEmpty)abs)); | ||||
| 					ps_insert_or_ignore.addBatch(); | ||||
| 				} else if (abs instanceof TLChatForbidden) { | ||||
| 					ps_insert_or_replace.setString(2, ((TLChatForbidden)abs).getTitle()); | ||||
| 					ps_insert_or_replace.setString(3, "chat"); | ||||
| 					ps_insert_or_replace.setString(4, gson.toJson((TLChatForbidden)abs)); | ||||
| 					ps_insert_or_replace.addBatch(); | ||||
| 				} else if (abs instanceof TLChannelForbidden) { | ||||
| 					ps_insert_or_replace.setString(2, ((TLChannelForbidden)abs).getTitle()); | ||||
| 					ps_insert_or_replace.setString(3, "channel"); | ||||
| 					ps_insert_or_replace.setString(4, gson.toJson((TLChannelForbidden)abs)); | ||||
| 					ps_insert_or_replace.addBatch(); | ||||
| 				} else if (abs instanceof TLChat) { | ||||
| 					ps_insert_or_replace.setString(2, ((TLChat) abs).getTitle()); | ||||
| 					ps_insert_or_replace.setString(3, "chat"); | ||||
| 					ps_insert_or_replace.setString(4, gson.toJson((TLChat) abs)); | ||||
| 					ps_insert_or_replace.addBatch(); | ||||
| 				} else if (abs instanceof TLChannel) { | ||||
| 					ps_insert_or_replace.setString(2, ((TLChannel)abs).getTitle()); | ||||
| 					ps_insert_or_replace.setString(3, "channel"); | ||||
| 					ps_insert_or_replace.setString(4, gson.toJson((TLChannel)abs)); | ||||
| 					ps_insert_or_replace.addBatch(); | ||||
| 				} else { | ||||
| 					throw new RuntimeException("Unexpected " + abs.getClass().getName()); | ||||
| 				} | ||||
| 			} | ||||
| 			conn.setAutoCommit(false); | ||||
| 			ps_insert_or_ignore.executeBatch(); | ||||
| 			ps_insert_or_ignore.clearBatch(); | ||||
| 			ps_insert_or_replace.executeBatch(); | ||||
| 			ps_insert_or_replace.clearBatch(); | ||||
| 			conn.commit(); | ||||
| 			conn.setAutoCommit(true); | ||||
| 		} catch (Exception e) { | ||||
| 			e.printStackTrace(); | ||||
| 			throw new RuntimeException("Exception shown above happened."); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public synchronized void saveUsers(TLVector<TLAbsUser> all, Gson gson) { | ||||
| 		try { | ||||
| 			PreparedStatement ps_insert_or_replace = conn.prepareStatement( | ||||
| 				"INSERT OR REPLACE INTO users " + | ||||
| 					"(id, first_name, last_name, username, type, phone, json) " + | ||||
| 					"VALUES " + | ||||
| 					"(?,  ?,          ?,         ?,        ?,    ?,     ?)"); | ||||
| 			PreparedStatement ps_insert_or_ignore = conn.prepareStatement( | ||||
| 				"INSERT OR IGNORE INTO users " + | ||||
| 					"(id, first_name, last_name, username, type, phone, json) " + | ||||
| 					"VALUES " + | ||||
| 					"(?,  ?,          ?,         ?,        ?,    ?,     ?)"); | ||||
| 			for (TLAbsUser abs : all) { | ||||
| 				if (abs instanceof TLUser) { | ||||
| 					TLUser user = (TLUser)abs; | ||||
| 					ps_insert_or_replace.setInt(1, user.getId()); | ||||
| 					ps_insert_or_replace.setString(2, user.getFirstName()); | ||||
| 					ps_insert_or_replace.setString(3, user.getLastName()); | ||||
| 					ps_insert_or_replace.setString(4, user.getUsername()); | ||||
| 					ps_insert_or_replace.setString(5, "user"); | ||||
| 					ps_insert_or_replace.setString(6, user.getPhone()); | ||||
| 					ps_insert_or_replace.setString(7, gson.toJson(user)); | ||||
| 					ps_insert_or_replace.addBatch(); | ||||
| 				} else if (abs instanceof TLUserEmpty) { | ||||
| 					ps_insert_or_ignore.setInt(1, abs.getId()); | ||||
| 					ps_insert_or_ignore.setNull(2, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setNull(3, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setNull(4, Types.VARCHAR); | ||||
| 					ps_insert_or_ignore.setString(5, "empty_user"); | ||||
| 					ps_insert_or_ignore.setNull(6, Types.VARCHAR); | ||||
| 					ps_insert_or_replace.setString(7, gson.toJson((TLUserEmpty)abs)); | ||||
| 					ps_insert_or_ignore.addBatch(); | ||||
| 				} else { | ||||
| 					throw new RuntimeException("Unexpected " + abs.getClass().getName()); | ||||
| 				} | ||||
| 			} | ||||
| 			conn.setAutoCommit(false); | ||||
| 			ps_insert_or_ignore.executeBatch(); | ||||
| 			ps_insert_or_ignore.clearBatch(); | ||||
| 			ps_insert_or_replace.executeBatch(); | ||||
| 			ps_insert_or_replace.clearBatch(); | ||||
| 			conn.commit(); | ||||
| 			conn.setAutoCommit(true); | ||||
| 		} catch (Exception e) { | ||||
| 			e.printStackTrace(); | ||||
| 			throw new RuntimeException("Exception shown above happened."); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public LinkedList<TLMessage> getMessagesWithMedia() { | ||||
| 		try { | ||||
| 			LinkedList<TLMessage> list = new LinkedList<TLMessage>(); | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT data FROM messages WHERE has_media=1"); | ||||
| 			while (rs.next()) { | ||||
| 				list.add(bytesToTLMessage(rs.getBytes(1))); | ||||
| 			} | ||||
| 			rs.close(); | ||||
| 			return list; | ||||
| 		} catch (Exception e) { | ||||
| 			e.printStackTrace(); | ||||
| 			throw new RuntimeException("Exception occured. See above."); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public String queryString(String query) { | ||||
| 		String result = null; | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery(query); | ||||
| 			rs.next(); | ||||
| 			result = rs.getString(1); | ||||
| 			rs.close(); | ||||
| 		} catch (Exception e) { | ||||
| 			logger.warn("Exception happened in queryString:", e); | ||||
| 		} | ||||
| 		return result; | ||||
| 	} | ||||
| 	 | ||||
| 	public int getMessagesFromUserCount() { | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM messages WHERE sender_id=" + user_manager.getUser().getId()); | ||||
| 			rs.next(); | ||||
| 			return rs.getInt(1); | ||||
| 		} catch (SQLException e) { | ||||
| 			throw new RuntimeException(e); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public LinkedList<Integer> getIdsFromQuery(String query) { | ||||
| 		try { | ||||
| 			LinkedList<Integer> list = new LinkedList<Integer>(); | ||||
| 			ResultSet rs = stmt.executeQuery(query); | ||||
| 			while(rs.next()) { list.add(rs.getInt(1)); } | ||||
| 			rs.close(); | ||||
| 			return list; | ||||
| 		} catch (SQLException e) { throw new RuntimeException(e); } | ||||
| 	} | ||||
| 	 | ||||
| 	public HashMap<String, Integer> getMessageTypesWithCount() { | ||||
| 		return getMessageTypesWithCount(new GlobalChat()); | ||||
| 	}		 | ||||
| 	 | ||||
| 	public HashMap<String, Integer> getMessageTypesWithCount(AbstractChat c) { | ||||
| 		HashMap<String, Integer> map = new HashMap<String, Integer>(); | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT message_type, COUNT(id) FROM messages WHERE " + c.getQuery() + " GROUP BY message_type"); | ||||
| 			while (rs.next()) { | ||||
| 				map.put("count.messages.type." + rs.getString(1), rs.getInt(2)); | ||||
| 			} | ||||
| 			return map; | ||||
| 		} catch (Exception e) { throw new RuntimeException(e); } | ||||
| 	} | ||||
| 	 | ||||
| 	public HashMap<String, Integer> getMessageMediaTypesWithCount() { | ||||
| 		return getMessageMediaTypesWithCount(new GlobalChat()); | ||||
| 	} | ||||
| 	 | ||||
| 	public HashMap<String, Integer> getMessageMediaTypesWithCount(AbstractChat c) { | ||||
| 		HashMap<String, Integer> map = new HashMap<String, Integer>(); | ||||
| 		try { | ||||
| 			int count = 0; | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT media_type, COUNT(id) FROM messages WHERE " + c.getQuery() + " GROUP BY media_type"); | ||||
| 			while (rs.next()) { | ||||
| 				String s = rs.getString(1); | ||||
| 				if (s==null) { | ||||
| 					s="null"; | ||||
| 				} else { | ||||
| 					count += rs.getInt(2); | ||||
| 				} | ||||
| 				map.put("count.messages.media_type." + s, rs.getInt(2)); | ||||
| 			} | ||||
| 			map.put("count.messages.media_type.any", count); | ||||
| 			return map; | ||||
| 		} catch (Exception e) { throw new RuntimeException(e); } | ||||
| 	} | ||||
| 	 | ||||
| 	public HashMap<String, Object> getMessageAuthorsWithCount() { | ||||
| 		return getMessageAuthorsWithCount(new GlobalChat()); | ||||
| 	} | ||||
| 	 | ||||
| 	public HashMap<String, Object> getMessageAuthorsWithCount(AbstractChat c) { | ||||
| 		HashMap<String, Object> map = new HashMap<String, Object>(); | ||||
| 		HashMap<User, Integer> user_map = new HashMap<User, Integer>(); | ||||
| 		int count_others = 0; | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT users.id, users.first_name, users.last_name, users.username, COUNT(messages.id) "+ | ||||
| 				"FROM messages, users WHERE users.id=messages.sender_id AND " + c.getQuery() + " GROUP BY sender_id"); | ||||
| 			while (rs.next()) { | ||||
| 				User u = new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4)); | ||||
| 				if (u.isMe) { | ||||
| 					map.put("authors.count.me", rs.getInt(5)); | ||||
| 				} else { | ||||
| 					user_map.put(u, rs.getInt(5)); | ||||
| 					count_others += rs.getInt(5); | ||||
| 				} | ||||
| 			} | ||||
| 			map.put("authors.count.others", count_others); | ||||
| 			map.put("authors.all", user_map.entrySet()); | ||||
| 			return map; | ||||
| 		} catch (Exception e) { throw new RuntimeException(e); } | ||||
| 	} | ||||
| 	 | ||||
| 	public int[][] getMessageTimesMatrix() { | ||||
| 		return getMessageTimesMatrix(new GlobalChat()); | ||||
| 	} | ||||
|  | ||||
| 	public int[][] getMessageTimesMatrix(AbstractChat c) { | ||||
| 		int result[][] = new int[7][24]; | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT STRFTIME('%w', time, 'unixepoch') as DAY, " + | ||||
| 				"STRFTIME('%H', time, 'unixepoch') AS hour, " + | ||||
| 				"COUNT(id) FROM messages WHERE " + c.getQuery() + " GROUP BY hour, day " + | ||||
| 				"ORDER BY hour, day"); | ||||
| 			while (rs.next()) { | ||||
| 				result[rs.getInt(1) == 0 ? 6 : rs.getInt(1)-1][rs.getInt(2)] = rs.getInt(3); | ||||
| 			} | ||||
| 			return result; | ||||
| 		} catch (Exception e) { throw new RuntimeException(e); } | ||||
| 	} | ||||
|  | ||||
| 	public String getEncoding() { | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("PRAGMA encoding"); | ||||
| 			rs.next(); | ||||
| 			return rs.getString(1); | ||||
| 		} catch (SQLException e) { | ||||
| 			logger.debug("SQLException: {}", e); | ||||
| 			return "unknown"; | ||||
| 		} | ||||
| 	} | ||||
| 			 | ||||
| 	 | ||||
| 	public LinkedList<Chat> getListOfChatsForExport() { | ||||
| 		LinkedList<Chat> list = new LinkedList<Chat>(); | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT chats.id, chats.name, COUNT(messages.id) as c "+ | ||||
| 				"FROM chats, messages WHERE messages.chat_id IS NOT NULL AND messages.chat_id=chats.id "+ | ||||
| 				"GROUP BY chats.id ORDER BY c DESC"); | ||||
| 			while (rs.next()) { | ||||
| 				list.add(new Chat(rs.getInt(1), rs.getString(2), rs.getInt(3))); | ||||
| 			} | ||||
| 			rs.close(); | ||||
| 			return list; | ||||
| 		} catch (Exception e) { | ||||
| 			e.printStackTrace(); | ||||
| 			throw new RuntimeException("Exception above!"); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	public LinkedList<Dialog> getListOfDialogsForExport() { | ||||
| 		LinkedList<Dialog> list = new LinkedList<Dialog>(); | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery( | ||||
| 				"SELECT users.id, first_name, last_name, username, COUNT(messages.id) as c " + | ||||
| 				"FROM users, messages WHERE messages.dialog_id IS NOT NULL AND messages.dialog_id=users.id " + | ||||
| 				"GROUP BY users.id ORDER BY c DESC"); | ||||
| 			while (rs.next()) { | ||||
| 				list.add(new Dialog(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getInt(5))); | ||||
| 			} | ||||
| 			rs.close(); | ||||
| 			return list; | ||||
| 		} catch (Exception e) { | ||||
| 			e.printStackTrace(); | ||||
| 			throw new RuntimeException("Exception above!"); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public LinkedList<HashMap<String, Object>> getMessagesForExport(AbstractChat c) { | ||||
| 		try { | ||||
| 			 | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT messages.id as message_id, text, time*1000 as time, has_media, " + | ||||
| 				"media_type, media_file, media_size, users.first_name as user_first_name, users.last_name as user_last_name, " + | ||||
| 				"users.username as user_username, users.id as user_id, " + | ||||
| 				"users_fwd.first_name as user_fwd_first_name, users_fwd.last_name as user_fwd_last_name, users_fwd.username as user_fwd_username " + | ||||
| 				"FROM messages, users LEFT JOIN users AS users_fwd ON users_fwd.id=fwd_from_id WHERE " + | ||||
| 				"users.id=messages.sender_id AND " + c.getQuery() + " " + | ||||
| 				"ORDER BY messages.id"); | ||||
| 			SimpleDateFormat format_time = new SimpleDateFormat("HH:mm:ss"); | ||||
| 			SimpleDateFormat format_date = new SimpleDateFormat("d MMM yy"); | ||||
| 			ResultSetMetaData meta = rs.getMetaData(); | ||||
| 			int columns = meta.getColumnCount(); | ||||
| 			LinkedList<HashMap<String, Object>> list = new LinkedList<HashMap<String, Object>>(); | ||||
| 			 | ||||
| 			Integer count=0; | ||||
| 			String old_date = null; | ||||
| 			Integer old_user = null; | ||||
| 			while (rs.next()) { | ||||
| 				HashMap<String, Object> h = new HashMap<String, Object>(columns); | ||||
| 				for (int i=1; i<=columns; i++) { | ||||
| 					h.put(meta.getColumnName(i), rs.getObject(i)); | ||||
| 				} | ||||
| 				// Additional values to make up for Mustache's inability to format dates | ||||
| 				Date d = rs.getTime("time"); | ||||
| 				String date = format_date.format(d); | ||||
| 				h.put("formatted_time", format_time.format(d)); | ||||
| 				h.put("formatted_date", date); | ||||
| 				if (rs.getString("media_type")!=null) { | ||||
| 					h.put("media_" + rs.getString("media_type"), true); | ||||
| 				} | ||||
| 				h.put("from_me", rs.getInt("user_id")==user_manager.getUser().getId()); | ||||
| 				h.put("is_new_date", !date.equals(old_date)); | ||||
| 				h.put("odd_even", (count%2==0) ? "even" : "odd"); | ||||
| 				h.put("same_user", old_user!=null && rs.getInt("user_id")==old_user); | ||||
| 				old_user = rs.getInt("user_id"); | ||||
| 				old_date = date; | ||||
| 				 | ||||
| 				list.add(h); | ||||
| 				count++; | ||||
| 			} | ||||
| 			rs.close(); | ||||
| 			return list; | ||||
| 		} catch(Exception e) { | ||||
| 			e.printStackTrace(); | ||||
| 			throw new RuntimeException("Exception above!"); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public static TLMessage bytesToTLMessage(byte[] b) { | ||||
| 		try { | ||||
| 			if (b==null) return null; | ||||
| @@ -637,6 +100,27 @@ public class Database { | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public void jsonify() { | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT id, data FROM messages WHERE api_layer=51"); | ||||
| 			PreparedStatement ps = conn.prepareStatement("UPDATE messages SET json=? WHERE id=?"); | ||||
| 			Gson gson = Utils.getGson(); | ||||
| 			while(rs.next()) { | ||||
| 				TLMessage msg = bytesToTLMessage(rs.getBytes(2)); | ||||
| 				ps.setInt(2, rs.getInt(1)); | ||||
| 				ps.setString(1, gson.toJson(msg)); | ||||
| 				ps.addBatch(); | ||||
| 			} | ||||
| 			rs.close(); | ||||
| 			conn.setAutoCommit(false); | ||||
| 			ps.executeBatch(); | ||||
| 			conn.commit(); | ||||
| 			conn.setAutoCommit(true); | ||||
| 		} catch (Exception e) { | ||||
| 			throw new RuntimeException(e); | ||||
| 		} | ||||
| 	}																					   | ||||
| 	 | ||||
| 		 | ||||
| 	 | ||||
| 	 | ||||
|   | ||||
| @@ -1,332 +0,0 @@ | ||||
| package de.fabianonline.telegram_backup; | ||||
|  | ||||
| import java.util.HashMap; | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.LinkedList; | ||||
| import java.sql.Connection; | ||||
| import java.sql.SQLException; | ||||
| import java.sql.Statement; | ||||
| import java.sql.Types; | ||||
| import java.sql.ResultSet; | ||||
| import java.sql.PreparedStatement; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.slf4j.Logger; | ||||
| import com.google.gson.Gson; | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory; | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager; | ||||
| import com.github.badoualy.telegram.api.Kotlogram; | ||||
|  | ||||
| public class DatabaseUpdates { | ||||
| 	protected Connection conn; | ||||
| 	protected Database db; | ||||
| 	private static final Logger logger = LoggerFactory.getLogger(DatabaseUpdates.class); | ||||
| 	private static LinkedList<DatabaseUpdate> updates = new LinkedList<DatabaseUpdate>(); | ||||
| 	 | ||||
| 	public DatabaseUpdates(Connection conn, Database db) { | ||||
| 		this.conn = conn; | ||||
| 		this.db = db; | ||||
| 		logger.debug("Registering Database Updates..."); | ||||
| 		register(new DB_Update_1(conn, db)); | ||||
| 		register(new DB_Update_2(conn, db)); | ||||
| 		register(new DB_Update_3(conn, db)); | ||||
| 		register(new DB_Update_4(conn, db)); | ||||
| 		register(new DB_Update_5(conn, db)); | ||||
| 		register(new DB_Update_6(conn, db)); | ||||
| 		register(new DB_Update_7(conn, db)); | ||||
| 		register(new DB_Update_8(conn, db)); | ||||
| 	} | ||||
| 	 | ||||
| 	public void doUpdates() { | ||||
| 		try { | ||||
| 			Statement stmt = conn.createStatement(); | ||||
| 			ResultSet rs; | ||||
| 			logger.debug("DatabaseUpdate.doUpdates running"); | ||||
|  | ||||
| 			logger.debug("Getting current database version"); | ||||
| 			int version; | ||||
| 			logger.debug("Checking if table database_versions exists"); | ||||
| 			rs = stmt.executeQuery("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='database_versions'"); | ||||
| 			rs.next(); | ||||
| 			if (rs.getInt(1)==0) { | ||||
| 				logger.debug("Table does not exist"); | ||||
| 				version = 0; | ||||
| 			} else { | ||||
| 				logger.debug("Table exists. Checking max version"); | ||||
| 				rs.close(); | ||||
| 				rs = stmt.executeQuery("SELECT MAX(version) FROM database_versions"); | ||||
| 				rs.next(); | ||||
| 				version = rs.getInt(1); | ||||
| 			} | ||||
| 			rs.close(); | ||||
| 			logger.debug("version: {}", version); | ||||
| 			System.out.println("Database version: " + version); | ||||
| 			logger.debug("Max available database version is {}", getMaxPossibleVersion()); | ||||
| 			 | ||||
| 			if (version < getMaxPossibleVersion()) { | ||||
| 				logger.debug("Update is necessary. {} => {}.", version, getMaxPossibleVersion()); | ||||
| 				boolean backup = false; | ||||
| 				for (int i=version+1; i<=getMaxPossibleVersion(); i++) { | ||||
| 					if (getUpdateToVersion(i).needsBackup()) { | ||||
| 						logger.debug("Update to version {} needs a backup", i); | ||||
| 						backup=true; | ||||
| 					} | ||||
| 				} | ||||
| 				if (backup) { | ||||
| 					if (version > 0) { | ||||
| 						logger.debug("Performing backup"); | ||||
| 						db.backupDatabase(version); | ||||
| 					} else { | ||||
| 						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"); | ||||
| 				try { | ||||
| 					for (int i=version+1; i<=getMaxPossibleVersion(); i++) { | ||||
| 						getUpdateToVersion(i).doUpdate(); | ||||
| 					} | ||||
| 				} catch (SQLException e) { throw new RuntimeException(e); } | ||||
| 			} else { | ||||
| 				logger.debug("No update necessary."); | ||||
| 			} | ||||
| 			 | ||||
| 		} catch (SQLException e) { | ||||
| 			throw new RuntimeException(e); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	private DatabaseUpdate getUpdateToVersion(int i) { return updates.get(i-1); } | ||||
| 	 | ||||
| 	private int getMaxPossibleVersion() { | ||||
| 		return updates.size(); | ||||
| 	} | ||||
| 	 | ||||
| 	private void register(DatabaseUpdate d) { | ||||
| 		logger.debug("Registering {} as update to version {}", d.getClass().getName(), d.getVersion()); | ||||
| 		if (d.getVersion() != updates.size()+1) { | ||||
| 			throw new RuntimeException("Tried to register DB update to version " + d.getVersion() + ", but would need update to version " + (updates.size()+1)); | ||||
| 		} | ||||
| 		updates.add(d); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| abstract class DatabaseUpdate { | ||||
| 	protected Connection conn; | ||||
| 	protected Statement stmt; | ||||
| 	protected Database db; | ||||
| 	protected static final Logger logger = LoggerFactory.getLogger(DatabaseUpdate.class); | ||||
| 	public DatabaseUpdate(Connection conn, Database db) { | ||||
| 		this.conn = conn; | ||||
| 		try { | ||||
| 			stmt = conn.createStatement(); | ||||
| 		} catch (SQLException e) { throw new RuntimeException(e); } | ||||
| 		this.db = db; | ||||
| 	 | ||||
| 	} | ||||
| 	public void doUpdate() throws SQLException { | ||||
| 		logger.debug("Applying update to version {}", getVersion()); | ||||
| 		System.out.println("  Updating to version " + getVersion() + "..."); | ||||
| 		_doUpdate(); | ||||
| 		logger.debug("Saving current database version to the db"); | ||||
| 		stmt.executeUpdate("INSERT INTO database_versions (version) VALUES (" + getVersion() + ")"); | ||||
| 	} | ||||
| 	 | ||||
| 	protected abstract void _doUpdate() throws SQLException; | ||||
| 	public abstract int getVersion(); | ||||
| 	public boolean needsBackup() { return false; } | ||||
| } | ||||
|  | ||||
| class DB_Update_1 extends DatabaseUpdate { | ||||
| 	public int getVersion() { return 1; } | ||||
| 	public DB_Update_1(Connection conn, Database db) { super(conn, db); } | ||||
| 	 | ||||
| 	protected void _doUpdate() throws SQLException { | ||||
| 		stmt.executeUpdate("CREATE TABLE messages (" | ||||
| 			+ "id INTEGER PRIMARY KEY ASC, " | ||||
| 			+ "dialog_id INTEGER, " | ||||
| 			+ "to_id INTEGER, " | ||||
| 			+ "from_id INTEGER, " | ||||
| 			+ "from_type TEXT, " | ||||
| 			+ "text TEXT, " | ||||
| 			+ "time TEXT, " | ||||
| 			+ "has_media BOOLEAN, " | ||||
| 			+ "sticker TEXT, " | ||||
| 			+ "data BLOB," | ||||
| 			+ "type TEXT)"); | ||||
| 		stmt.executeUpdate("CREATE TABLE dialogs (" | ||||
| 			+ "id INTEGER PRIMARY KEY ASC, " | ||||
| 			+ "name TEXT, " | ||||
| 			+ "type TEXT)"); | ||||
| 		stmt.executeUpdate("CREATE TABLE people (" | ||||
| 			+ "id INTEGER PRIMARY KEY ASC, " | ||||
| 			+ "first_name TEXT, " | ||||
| 			+ "last_name TEXT, " | ||||
| 			+ "username TEXT, " | ||||
| 			+ "type TEXT)"); | ||||
| 		stmt.executeUpdate("CREATE TABLE database_versions (" | ||||
| 			+ "version INTEGER)");		 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class DB_Update_2 extends DatabaseUpdate { | ||||
| 	public int getVersion() { return 2; } | ||||
| 	public DB_Update_2(Connection conn, Database db) { super(conn, db); } | ||||
| 	 | ||||
| 	protected void _doUpdate() throws SQLException { | ||||
| 		stmt.executeUpdate("ALTER TABLE people RENAME TO 'users'"); | ||||
| 		stmt.executeUpdate("ALTER TABLE users ADD COLUMN phone TEXT"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class DB_Update_3 extends DatabaseUpdate { | ||||
| 	public int getVersion() { return 3; } | ||||
| 	public DB_Update_3(Connection conn, Database db) { super(conn, db); } | ||||
| 	 | ||||
| 	protected void _doUpdate() throws SQLException { | ||||
| 		stmt.executeUpdate("ALTER TABLE dialogs RENAME TO 'chats'"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class DB_Update_4 extends DatabaseUpdate { | ||||
| 	public int getVersion() { return 4; } | ||||
| 	public DB_Update_4(Connection conn, Database db) { super(conn, db); } | ||||
| 	 | ||||
| 	protected void _doUpdate() throws SQLException { | ||||
| 		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("DROP TABLE messages"); | ||||
| 		stmt.executeUpdate("ALTER TABLE messages_new RENAME TO 'messages'"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class DB_Update_5 extends DatabaseUpdate { | ||||
| 	public int getVersion() { return 5; } | ||||
| 	public DB_Update_5(Connection conn, Database db) { super(conn, db); } | ||||
|  | ||||
| 	protected void _doUpdate() throws SQLException { | ||||
| 		stmt.executeUpdate("CREATE TABLE runs (id INTEGER PRIMARY KEY ASC, time INTEGER, start_id INTEGER, end_id INTEGER, count_missing INTEGER)"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class DB_Update_6 extends DatabaseUpdate { | ||||
| 	public int getVersion() { return 6; } | ||||
| 	public DB_Update_6(Connection conn, Database db) { super(conn, db); } | ||||
| 	public boolean needsBackup() { return true; } | ||||
| 	 | ||||
| 	protected void _doUpdate() throws SQLException { | ||||
| 		stmt.executeUpdate( | ||||
| 			"CREATE TABLE messages_new (\n" + | ||||
| 			"    id INTEGER PRIMARY KEY ASC,\n" + | ||||
| 			"    message_type TEXT,\n" + | ||||
| 			"    dialog_id INTEGER,\n" + | ||||
| 			"    chat_id INTEGER,\n" + | ||||
| 			"    sender_id INTEGER,\n" + | ||||
| 			"    fwd_from_id INTEGER,\n" + | ||||
| 			"    text TEXT,\n" + | ||||
| 			"    time INTEGER,\n" + | ||||
| 			"    has_media BOOLEAN,\n" + | ||||
| 			"    media_type TEXT,\n" + | ||||
| 			"    media_file TEXT,\n" + | ||||
| 			"    media_size INTEGER,\n" + | ||||
| 			"    media_json TEXT,\n" + | ||||
| 			"    markup_json TEXT,\n" + | ||||
| 			"    data BLOB)"); | ||||
| 		LinkedHashMap<String, String> mappings = new LinkedHashMap<String, String>(); | ||||
| 		mappings.put("id", "id"); | ||||
| 		mappings.put("message_type", "type"); | ||||
| 		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("sender_id", "from_id"); | ||||
| 		mappings.put("text", "text"); | ||||
| 		mappings.put("time", "time"); | ||||
| 		mappings.put("has_media", "has_media"); | ||||
| 		mappings.put("data", "data"); | ||||
| 		StringBuilder query = new StringBuilder("INSERT INTO messages_new\n("); | ||||
| 		boolean first; | ||||
| 		first = true; | ||||
| 		for(String s : mappings.keySet()) { | ||||
| 			if (!first) query.append(", "); | ||||
| 			query.append(s); | ||||
| 			first = false; | ||||
| 		} | ||||
| 		query.append(")\nSELECT \n"); | ||||
| 		first = true; | ||||
| 		for (String s : mappings.values()) { | ||||
| 			if (!first) query.append(", "); | ||||
| 			query.append(s); | ||||
| 			first = false; | ||||
| 		} | ||||
| 		query.append("\nFROM messages"); | ||||
| 		stmt.executeUpdate(query.toString()); | ||||
| 		 | ||||
| 		System.out.println("    Updating the data (this might take some time)..."); | ||||
| 		ResultSet rs = stmt.executeQuery("SELECT id, data FROM messages_new"); | ||||
| 		PreparedStatement ps = conn.prepareStatement("UPDATE messages_new SET fwd_from_id=?, media_type=?, media_file=?, media_size=? WHERE id=?"); | ||||
| 		while (rs.next()) { | ||||
| 			ps.setInt(5, rs.getInt(1)); | ||||
| 			TLMessage msg = db.bytesToTLMessage(rs.getBytes(2)); | ||||
| 			if (msg==null || msg.getFwdFrom()==null) { | ||||
| 				ps.setNull(1, Types.INTEGER); | ||||
| 			} else { | ||||
| 				ps.setInt(1, msg.getFwdFrom().getFromId()); | ||||
| 			} | ||||
| 			AbstractMediaFileManager f = FileManagerFactory.getFileManager(msg, db.user_manager, db.client); | ||||
| 			if (f==null) { | ||||
| 				ps.setNull(2, Types.VARCHAR); | ||||
| 				ps.setNull(3, Types.VARCHAR); | ||||
| 				ps.setNull(4, Types.INTEGER); | ||||
| 			} else { | ||||
| 				ps.setString(2, f.getName()); | ||||
| 				ps.setString(3, f.getTargetFilename()); | ||||
| 				ps.setInt(4, f.getSize()); | ||||
| 			} | ||||
| 			ps.addBatch(); | ||||
| 		} | ||||
| 		rs.close(); | ||||
| 		conn.setAutoCommit(false); | ||||
| 		ps.executeBatch(); | ||||
| 		conn.commit(); | ||||
| 		conn.setAutoCommit(true); | ||||
| 		stmt.executeUpdate("DROP TABLE messages"); | ||||
| 		stmt.executeUpdate("ALTER TABLE messages_new RENAME TO messages"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class DB_Update_7 extends DatabaseUpdate { | ||||
| 	public int getVersion() { return 7; } | ||||
| 	public boolean needsBackup() { return true; } | ||||
| 	public DB_Update_7(Connection conn, Database db) { super(conn, db); } | ||||
| 	 | ||||
| 	protected void _doUpdate() throws SQLException { | ||||
| 		stmt.executeUpdate("ALTER TABLE messages ADD COLUMN api_layer INTEGER"); | ||||
| 		 | ||||
| 		stmt.executeUpdate("UPDATE messages SET api_layer=51"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class DB_Update_8 extends DatabaseUpdate { | ||||
| 	public int getVersion() { return 8; } | ||||
| 	public DB_Update_8(Connection conn, Database db) { super(conn, db); } | ||||
| 	 | ||||
| 	protected void _doUpdate() throws SQLException { | ||||
| 		stmt.executeUpdate("ALTER TABLE messages ADD COLUMN json TEXT"); | ||||
| 		stmt.executeUpdate("ALTER TABLE chats ADD COLUMN json TEXT"); | ||||
| 		stmt.executeUpdate("ALTER TABLE users ADD COLUMN json TEXT"); | ||||
| 		 | ||||
| 		ResultSet rs = stmt.executeQuery("SELECT id, data FROM messages WHERE api_layer=" + Kotlogram.API_LAYER); | ||||
| 		PreparedStatement ps = conn.prepareStatement("UPDATE messages SET json=? WHERE id=?"); | ||||
| 		Gson gson = Utils.getGson(); | ||||
| 		while(rs.next()) { | ||||
| 			TLMessage msg = db.bytesToTLMessage(rs.getBytes(2)); | ||||
| 			ps.setInt(2, rs.getInt(1)); | ||||
| 			ps.setString(1, gson.toJson(msg)); | ||||
| 			ps.addBatch(); | ||||
| 		} | ||||
| 		rs.close(); | ||||
| 		conn.setAutoCommit(false); | ||||
| 		ps.executeBatch(); | ||||
| 		conn.commit(); | ||||
| 		conn.setAutoCommit(true); | ||||
| 	} | ||||
| } | ||||
| @@ -1,409 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.StickerConverter; | ||||
| import de.fabianonline.telegram_backup.DownloadProgressInterface; | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory; | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import com.github.badoualy.telegram.api.Kotlogram; | ||||
| import com.github.badoualy.telegram.tl.core.TLIntVector; | ||||
| import com.github.badoualy.telegram.tl.core.TLObject; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.tl.api.upload.TLFile; | ||||
| import com.github.badoualy.telegram.tl.exception.RpcErrorException; | ||||
| import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.slf4j.Logger; | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.GsonBuilder; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.Random; | ||||
| import java.net.URL; | ||||
| import java.util.concurrent.TimeoutException; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.StandardCopyOption; | ||||
|  | ||||
| import org.apache.commons.io.FileUtils; | ||||
|  | ||||
| public class DownloadManager { | ||||
| 	UserManager user; | ||||
| 	TelegramClient client; | ||||
| 	Database db; | ||||
| 	DownloadProgressInterface prog = null; | ||||
| 	static TelegramClient download_client; | ||||
| 	static boolean last_download_succeeded = true; | ||||
| 	static final Logger logger = LoggerFactory.getLogger(DownloadManager.class); | ||||
| 	 | ||||
| 	public DownloadManager(TelegramClient c, DownloadProgressInterface p) { | ||||
| 		this.user = UserManager.getInstance(); | ||||
| 		this.client = c; | ||||
| 		this.prog = p; | ||||
| 		this.db = Database.getInstance(); | ||||
| 	} | ||||
| 	 | ||||
| 	public void downloadMessages(Integer limit) throws RpcErrorException, IOException { | ||||
| 		boolean completed = true; | ||||
| 		do { | ||||
| 			completed = true; | ||||
| 			try { | ||||
| 				_downloadMessages(limit); | ||||
| 			} catch (RpcErrorException e) { | ||||
| 				if (e.getCode()==420) { // FLOOD_WAIT | ||||
| 					completed = false; | ||||
| 					Utils.obeyFloodWaitException(e); | ||||
| 				} else { | ||||
| 					throw e; | ||||
| 				} | ||||
| 			} catch (TimeoutException e) { | ||||
| 				completed = false; | ||||
| 				System.out.println(""); | ||||
| 				System.out.println("Telegram took too long to respond to our request."); | ||||
| 				System.out.println("I'm going to wait a minute and then try again."); | ||||
| 				try { TimeUnit.MINUTES.sleep(1); } catch(InterruptedException e2) {} | ||||
| 				System.out.println(""); | ||||
| 			} | ||||
| 		} while (!completed); | ||||
| 	} | ||||
| 	 | ||||
| 	public void _downloadMessages(Integer limit) throws RpcErrorException, IOException, TimeoutException { | ||||
| 		logger.info("This is _downloadMessages with limit {}", limit); | ||||
| 		int dialog_limit = 100; | ||||
| 		logger.info("Downloading the last {} dialogs", dialog_limit); | ||||
| 		System.out.println("Downloading most recent dialogs... "); | ||||
| 		int max_message_id = 0; | ||||
| 		TLAbsDialogs dialogs = client.messagesGetDialogs( | ||||
| 			0, | ||||
| 			0, | ||||
| 			new TLInputPeerEmpty(), | ||||
| 			dialog_limit); | ||||
| 		logger.debug("Got {} dialogs", dialogs.getDialogs().size()); | ||||
| 		for (TLDialog d : dialogs.getDialogs()) { | ||||
| 			if (d.getTopMessage() > max_message_id && ! (d.getPeer() instanceof TLPeerChannel)) { | ||||
| 				logger.trace("Updating top message id: {} => {}. Dialog type: {}", max_message_id, d.getTopMessage(), d.getPeer().getClass().getName()); | ||||
| 				max_message_id = d.getTopMessage(); | ||||
| 			} | ||||
| 		} | ||||
| 		System.out.println("Top message ID is " + max_message_id); | ||||
| 		int max_database_id = db.getTopMessageID(); | ||||
| 		System.out.println("Top message ID in database is " + max_database_id); | ||||
| 		if (limit != null) { | ||||
| 			System.out.println("Limit is set to " + 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); | ||||
| 		} | ||||
| 		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."); | ||||
| 			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); | ||||
| 			logger.debug("new max_database_id: {}", max_database_id); | ||||
| 		} | ||||
|  | ||||
| 		if (max_database_id == max_message_id) { | ||||
| 			System.out.println("No new messages to download."); | ||||
| 		} else if (max_database_id > max_message_id) { | ||||
| 			throw new 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 { | ||||
| 			int start_id = max_database_id + 1; | ||||
| 			int end_id = max_message_id; | ||||
| 			 | ||||
| 			List<Integer> ids = makeIdList(start_id, end_id); | ||||
| 			downloadMessages(ids); | ||||
| 		} | ||||
| 		 | ||||
| 		logger.info("Searching for missing messages in the db"); | ||||
| 		int count_missing = 0; | ||||
| 		System.out.println("Checking message database for completeness..."); | ||||
| 		int db_count = db.getMessageCount(); | ||||
| 		int db_max = db.getTopMessageID(); | ||||
| 		logger.debug("db_count: {}", db_count); | ||||
| 		logger.debug("db_max: {}", db_max); | ||||
| 		 | ||||
| 		if (db_count != db_max) { | ||||
| 			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."); | ||||
| 			} else { | ||||
| 				LinkedList<Integer> all_missing_ids = db.getMissingIDs(); | ||||
| 				LinkedList<Integer> downloadable_missing_ids = new LinkedList<Integer>(); | ||||
| 				for (Integer id : all_missing_ids) { | ||||
| 					if (id > max_message_id - 1000000) downloadable_missing_ids.add(id); | ||||
| 				} | ||||
| 				count_missing = all_missing_ids.size(); | ||||
| 				System.out.println("" + all_missing_ids.size() + " messages are missing in your Database."); | ||||
| 				System.out.println("I can (and will) download " + downloadable_missing_ids.size() + " of them."); | ||||
| 				 | ||||
| 				downloadMessages(downloadable_missing_ids); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		logger.info("Logging this run"); | ||||
| 		db.logRun(Math.min(max_database_id + 1, max_message_id), max_message_id, count_missing); | ||||
| 	} | ||||
| 	 | ||||
| 	private void downloadMessages(List<Integer> ids) throws RpcErrorException, IOException { | ||||
| 		prog.onMessageDownloadStart(ids.size()); | ||||
| 		boolean has_seen_flood_wait_message = false; | ||||
| 		 | ||||
| 		logger.debug("Entering download loop"); | ||||
| 		while (ids.size()>0) { | ||||
| 			logger.trace("Loop"); | ||||
| 			TLIntVector vector = new TLIntVector(); | ||||
| 			int download_count = Config.GET_MESSAGES_BATCH_SIZE; | ||||
| 			logger.trace("download_count: {}", download_count); | ||||
| 			for (int i=0; i<download_count; i++) { | ||||
| 				if (ids.size()==0) break; | ||||
| 				vector.add(ids.remove(0)); | ||||
| 			} | ||||
| 			logger.trace("vector.size(): {}", vector.size()); | ||||
| 			logger.trace("ids.size(): {}", ids.size()); | ||||
| 			 | ||||
| 			TLAbsMessages response; | ||||
| 			int tries = 0; | ||||
| 			while(true) { | ||||
| 				logger.trace("Trying getMessages(), tries={}", tries); | ||||
| 				if (tries>=5) { | ||||
| 					CommandLineController.show_error("Couldn't getMessages after 5 tries. Quitting."); | ||||
| 				} | ||||
| 				tries++; | ||||
| 				try { | ||||
| 					response = client.messagesGetMessages(vector); | ||||
| 					break; | ||||
| 				} catch (RpcErrorException e) { | ||||
| 					if (e.getCode()==420) { // FLOOD_WAIT | ||||
| 						Utils.obeyFloodWaitException(e, has_seen_flood_wait_message); | ||||
| 						has_seen_flood_wait_message = true; | ||||
| 					} else { | ||||
| 						throw e; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			logger.trace("response.getMessages().size(): {}", response.getMessages().size()); | ||||
| 			if (response.getMessages().size() != vector.size()) { | ||||
| 				CommandLineController.show_error("Requested " + vector.size() + " messages, but got " + response.getMessages().size() + ". That is unexpected. Quitting."); | ||||
| 			} | ||||
| 			 | ||||
| 			//ObjectMapper om = new ObjectMapper(); | ||||
| 			//String json = om.writerWithDefaultPrettyPrinter().writeValueAsString(response.getMessages().get(1)); | ||||
| 			Gson gson = Utils.getGson(); | ||||
| 			 | ||||
| 			prog.onMessageDownloaded(response.getMessages().size()); | ||||
| 			db.saveMessages(response.getMessages(), Kotlogram.API_LAYER, gson); | ||||
| 			db.saveChats(response.getChats(), gson); | ||||
| 			db.saveUsers(response.getUsers(), gson); | ||||
| 			logger.trace("Sleeping"); | ||||
| 			try { | ||||
| 				TimeUnit.MILLISECONDS.sleep(Config.DELAY_AFTER_GET_MESSAGES); | ||||
| 			} catch (InterruptedException e) {} | ||||
| 		} | ||||
| 		logger.debug("Finished."); | ||||
| 		 | ||||
| 		prog.onMessageDownloadFinished(); | ||||
| 	} | ||||
| 	 | ||||
| 	public void downloadMedia() throws RpcErrorException, IOException { | ||||
| 		download_client = client.getDownloaderClient(); | ||||
| 		boolean completed = true; | ||||
| 		do { | ||||
| 			completed = true; | ||||
| 			try { | ||||
| 				_downloadMedia(); | ||||
| 			} catch (RpcErrorException e) { | ||||
| 				if (e.getTag().startsWith("420: FLOOD_WAIT_")) { | ||||
| 					completed = false; | ||||
| 					Utils.obeyFloodWaitException(e); | ||||
| 				} else { | ||||
| 					throw e; | ||||
| 				} | ||||
| 			} /*catch (TimeoutException e) { | ||||
| 				completed = false; | ||||
| 				System.out.println(""); | ||||
| 				System.out.println("Telegram took too long to respond to our request."); | ||||
| 				System.out.println("I'm going to wait a minute and then try again."); | ||||
| 				logger.warn("TimeoutException caught", e); | ||||
| 				try { TimeUnit.MINUTES.sleep(1); } catch(InterruptedException e2) {} | ||||
| 				System.out.println(""); | ||||
| 			}*/ | ||||
| 		} while (!completed); | ||||
| 	} | ||||
| 	 | ||||
| 	private void _downloadMedia() throws RpcErrorException, IOException { | ||||
| 		logger.info("This is _downloadMedia"); | ||||
| 		logger.info("Checking if there are messages in the DB with a too old API layer"); | ||||
| 		LinkedList<Integer> ids = db.getIdsFromQuery("SELECT id FROM messages WHERE has_media=1 AND api_layer<" + Kotlogram.API_LAYER); | ||||
| 		if (ids.size()>0) { | ||||
| 			System.out.println("You have " + ids.size() + " messages in your db that need an update. Doing that now."); | ||||
| 			logger.debug("Found {} messages", ids.size()); | ||||
| 			downloadMessages(ids); | ||||
| 		} | ||||
| 		 | ||||
| 		LinkedList<TLMessage> messages = this.db.getMessagesWithMedia(); | ||||
| 		logger.debug("Database returned {} messages with media", messages.size()); | ||||
| 		prog.onMediaDownloadStart(messages.size()); | ||||
| 		for (TLMessage msg : messages) { | ||||
| 			AbstractMediaFileManager m = FileManagerFactory.getFileManager(msg, user, client); | ||||
| 			logger.trace("message {}, {}, {}, {}, {}", | ||||
| 				msg.getId(), | ||||
| 				msg.getMedia().getClass().getSimpleName().replace("TLMessageMedia", "…"), | ||||
| 				m.getClass().getSimpleName(), | ||||
| 				m.isEmpty() ? "empty" : "non-empty", | ||||
| 				m.isDownloaded() ? "downloaded" : "not downloaded"); | ||||
| 			if (m.isEmpty()) { | ||||
| 				prog.onMediaDownloadedEmpty(); | ||||
| 			} else if (m.isDownloaded()) { | ||||
| 				prog.onMediaAlreadyPresent(m); | ||||
| 			} else { | ||||
| 				try { | ||||
| 					m.download(); | ||||
| 					prog.onMediaDownloaded(m); | ||||
| 				} catch (TimeoutException e) { | ||||
| 					// do nothing - skip this file | ||||
| 					prog.onMediaSkipped(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		prog.onMediaDownloadFinished(); | ||||
| 	} | ||||
| 	 | ||||
| 	private List<Integer> makeIdList(int start, int end) { | ||||
| 		LinkedList<Integer> a = new LinkedList<Integer>(); | ||||
| 		for (int i=start; i<=end; i++) a.add(i); | ||||
| 		return a;  | ||||
| 	} | ||||
| 	 | ||||
| 	public static void downloadFile(TelegramClient client, String targetFilename, int size, int dcId, long volumeId, int localId, long secret) throws RpcErrorException, IOException, TimeoutException { | ||||
| 		TLInputFileLocation loc = new TLInputFileLocation(volumeId, localId, secret); | ||||
| 		downloadFileFromDc(client, targetFilename, loc, dcId, size); | ||||
| 	} | ||||
| 	 | ||||
| 	public static void downloadFile(TelegramClient client, String targetFilename, int size, int dcId, long id, long accessHash) throws RpcErrorException, IOException, TimeoutException { | ||||
| 		TLInputDocumentFileLocation loc = new TLInputDocumentFileLocation(id, accessHash); | ||||
| 		downloadFileFromDc(client, targetFilename, loc, dcId, size); | ||||
| 	} | ||||
| 	 | ||||
| 	private static boolean downloadFileFromDc(TelegramClient client, String target, TLAbsInputFileLocation loc, Integer dcID, int size) throws RpcErrorException, IOException, TimeoutException { | ||||
| 		FileOutputStream fos = null; | ||||
| 		try { | ||||
| 			String temp_filename = target + ".downloading"; | ||||
| 			logger.debug("Downloading file {}", target); | ||||
| 			logger.trace("Temporary filename: {}", temp_filename); | ||||
| 			 | ||||
| 			int offset = 0; | ||||
| 			if (new File(temp_filename).isFile()) { | ||||
| 				logger.info("Temporary filename already exists; continuing this file"); | ||||
| 				offset = (int)new File(temp_filename).length(); | ||||
| 				if (offset >= size) { | ||||
| 					logger.warn("Temporary file size is >= the target size. Assuming corrupt file & deleting it"); | ||||
| 					new File(temp_filename).delete(); | ||||
| 					offset = 0; | ||||
| 				} | ||||
| 			} | ||||
| 			logger.trace("offset before the loop is {}", offset); | ||||
| 			fos = new FileOutputStream(temp_filename, true); | ||||
| 			TLFile response = null; | ||||
| 			boolean try_again; | ||||
| 			do { | ||||
| 				try_again = false; | ||||
| 				int block_size = size; | ||||
| 				logger.trace("offset: {} block_size: {} size: {}", offset, block_size, size); | ||||
| 				TLRequestUploadGetFile req = new TLRequestUploadGetFile(loc, offset, block_size); | ||||
| 				try { | ||||
| 					if (dcID==null) { | ||||
| 						response = (TLFile) download_client.executeRpcQuery(req); | ||||
| 					} else { | ||||
| 						response = (TLFile) download_client.executeRpcQuery(req, dcID); | ||||
| 					} | ||||
| 				} catch (RpcErrorException e) { | ||||
| 					if (e.getTag().startsWith("420: FLOOD_WAIT_")) { | ||||
| 						try_again = true; | ||||
| 						Utils.obeyFloodWaitException(e); | ||||
| 					} else { | ||||
| 						throw e; | ||||
| 					} | ||||
| 				} | ||||
| 				 | ||||
| 				offset += response.getBytes().getData().length; | ||||
| 				logger.trace("response: {} total size: {}", response.getBytes().getData().length, offset); | ||||
| 				 | ||||
| 				fos.write(response.getBytes().getData()); | ||||
| 				fos.flush(); | ||||
| 				try { TimeUnit.MILLISECONDS.sleep(Config.DELAY_AFTER_GET_FILE); } catch(InterruptedException e) {} | ||||
| 			} while(offset < size && (response.getBytes().getData().length>0 || try_again)); | ||||
| 			fos.close(); | ||||
| 			if (offset < size) { | ||||
| 				System.out.println("Requested file " + target + " with " + size + " bytes, but got only " + offset + " bytes."); | ||||
| 				new File(temp_filename).delete(); | ||||
| 				System.exit(1); | ||||
| 			} | ||||
| 			logger.trace("Renaming {} to {}", temp_filename, target); | ||||
| 			int rename_tries = 0; | ||||
| 			IOException last_exception = null; | ||||
| 			while (rename_tries <= Config.RENAMING_MAX_TRIES) { | ||||
| 				rename_tries++; | ||||
| 				try { | ||||
| 					Files.move(new File(temp_filename).toPath(), new File(target).toPath(), StandardCopyOption.REPLACE_EXISTING); | ||||
| 					last_exception = null; | ||||
| 					break; | ||||
| 				} catch (IOException e) { | ||||
| 					logger.debug("Exception during move. rename_tries: {}. Exception: {}", rename_tries, e); | ||||
| 					last_exception = e; | ||||
| 					try { TimeUnit.MILLISECONDS.sleep(Config.RENAMING_DELAY); } catch (InterruptedException e2) {} | ||||
| 				} | ||||
| 			} | ||||
| 			if (last_exception != null) { | ||||
| 				throw last_exception; | ||||
| 			} | ||||
| 			last_download_succeeded = true; | ||||
| 			return true; | ||||
| 		} catch (java.io.IOException ex) { | ||||
| 			if (fos!=null) fos.close(); | ||||
| 			System.out.println("IOException happened while downloading " + target); | ||||
| 			throw ex; | ||||
| 		} catch (RpcErrorException ex) { | ||||
| 			if (fos!=null) fos.close(); | ||||
| 			if (ex.getCode()==500) { | ||||
| 				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."); | ||||
| 					throw ex; | ||||
| 				} | ||||
| 				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."); | ||||
| 				logger.warn(ex.toString()); | ||||
| 				return false; | ||||
| 			} | ||||
| 			System.out.println("RpcErrorException happened while downloading " + target); | ||||
| 			throw ex; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public static boolean downloadExternalFile(String target, String url) throws IOException { | ||||
| 		FileUtils.copyURLToFile(new URL(url), new File(target), 5000, 5000); | ||||
| 		return true; | ||||
| 	} | ||||
| } | ||||
| @@ -1,32 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager; | ||||
|  | ||||
| public interface DownloadProgressInterface { | ||||
| 	public void onMessageDownloadStart(int count); | ||||
| 	public void onMessageDownloaded(int number); | ||||
| 	public void onMessageDownloadFinished(); | ||||
| 	 | ||||
| 	public void onMediaDownloadStart(int count); | ||||
| 	public void onMediaDownloaded(AbstractMediaFileManager a); | ||||
| 	public void onMediaDownloadedEmpty(); | ||||
| 	public void onMediaSkipped(); | ||||
| 	public void onMediaAlreadyPresent(AbstractMediaFileManager a); | ||||
| 	public void onMediaDownloadFinished(); | ||||
| } | ||||
| @@ -1,159 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.UpdateCallback; | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import com.github.badoualy.telegram.api.Kotlogram; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.tl.core.TLVector; | ||||
|  | ||||
| import com.google.gson.Gson; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.Utils; | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager; | ||||
| import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory; | ||||
|  | ||||
| class TelegramUpdateHandler implements UpdateCallback { | ||||
| 	private UserManager user = null; | ||||
| 	private Database db = null; | ||||
| 	public boolean debug = false; | ||||
| 	private Gson gson = Utils.getGson(); | ||||
| 	 | ||||
| 	public void activate() { this.user = UserManager.getInstance(); this.db = Database.getInstance();} | ||||
| 	 | ||||
| 	public void onUpdates(TelegramClient c, TLUpdates u) { | ||||
| 		if (db==null) return; | ||||
| 		if (debug) System.out.println("onUpdates - " + u.getUpdates().size() + " Updates, " + u.getUsers().size() + " Users, " + u.getChats().size() + " Chats"); | ||||
| 		for(TLAbsUpdate update : u.getUpdates()) { | ||||
| 			processUpdate(update, c); | ||||
| 			if (debug) System.out.println("  " + update.getClass().getName()); | ||||
| 		} | ||||
| 		db.saveUsers(u.getUsers(), gson); | ||||
| 		db.saveChats(u.getChats(), gson); | ||||
| 	} | ||||
| 	 | ||||
| 	public void onUpdatesCombined(TelegramClient c, TLUpdatesCombined u) { | ||||
| 		if (db==null) return; | ||||
| 		if (debug) System.out.println("onUpdatesCombined"); | ||||
| 		for(TLAbsUpdate update : u.getUpdates()) { | ||||
| 			processUpdate(update, c); | ||||
| 		} | ||||
| 		db.saveUsers(u.getUsers(), gson); | ||||
| 		db.saveChats(u.getChats(), gson); | ||||
| 	} | ||||
| 		 | ||||
| 	public void onUpdateShort(TelegramClient c, TLUpdateShort u) { | ||||
| 		if (db==null) return; | ||||
| 		if (debug) System.out.println("onUpdateShort"); | ||||
| 		processUpdate(u.getUpdate(), c); | ||||
| 		if (debug) System.out.println("  " + u.getUpdate().getClass().getName()); | ||||
| 	} | ||||
| 	 | ||||
| 	public void onShortChatMessage(TelegramClient c, TLUpdateShortChatMessage m) { | ||||
| 		if (db==null) return; | ||||
| 		if (debug) System.out.println("onShortChatMessage - " + m.getMessage()); | ||||
| 		TLMessage msg = new TLMessage( | ||||
| 			m.getOut(), | ||||
| 			m.getMentioned(), | ||||
| 			m.getMediaUnread(), | ||||
| 			m.getSilent(), | ||||
| 			false, | ||||
| 			m.getId(), | ||||
| 			m.getFromId(), | ||||
| 			new TLPeerChat(m.getChatId()), | ||||
| 			m.getFwdFrom(), | ||||
| 			m.getViaBotId(), | ||||
| 			m.getReplyToMsgId(), | ||||
| 			m.getDate(), | ||||
| 			m.getMessage(), | ||||
| 			null, | ||||
| 			null, | ||||
| 			m.getEntities(), | ||||
| 			null, | ||||
| 			null); | ||||
| 		TLVector<TLAbsMessage> vector = new TLVector<TLAbsMessage>(TLAbsMessage.class); | ||||
| 		vector.add(msg); | ||||
| 		db.saveMessages(vector, Kotlogram.API_LAYER, gson); | ||||
| 		System.out.print('.'); | ||||
| 	} | ||||
| 	 | ||||
| 	public void onShortMessage(TelegramClient c, TLUpdateShortMessage m) { | ||||
| 		if (db==null) return; | ||||
| 		if (debug) System.out.println("onShortMessage - " + m.getOut() + " - " + m.getUserId() + " - " + m.getMessage()); | ||||
| 		int from_id, to_id; | ||||
| 		if (m.getOut()==true) { | ||||
| 			from_id = user.getUser().getId(); | ||||
| 			to_id = m.getUserId(); | ||||
| 		} else { | ||||
| 			to_id = user.getUser().getId(); | ||||
| 			from_id = m.getUserId(); | ||||
| 		} | ||||
| 		TLMessage msg = new TLMessage( | ||||
| 			m.getOut(), | ||||
| 			m.getMentioned(), | ||||
| 			m.getMediaUnread(), | ||||
| 			m.getSilent(), | ||||
| 			false, | ||||
| 			m.getId(), | ||||
| 			from_id, | ||||
| 			new TLPeerUser(to_id), | ||||
| 			m.getFwdFrom(), | ||||
| 			m.getViaBotId(), | ||||
| 			m.getReplyToMsgId(), | ||||
| 			m.getDate(), | ||||
| 			m.getMessage(), | ||||
| 			null, | ||||
| 			null, | ||||
| 			m.getEntities(), | ||||
| 			null, | ||||
| 			null); | ||||
| 		TLVector<TLAbsMessage> vector = new TLVector<TLAbsMessage>(TLAbsMessage.class); | ||||
| 		vector.add(msg); | ||||
| 		db.saveMessages(vector, Kotlogram.API_LAYER, gson); | ||||
| 		System.out.print('.'); | ||||
| 	} | ||||
| 	 | ||||
| 	public void onShortSentMessage(TelegramClient c, TLUpdateShortSentMessage m) { if (db==null) return; System.out.println("onShortSentMessage"); } | ||||
| 	public void onUpdateTooLong(TelegramClient c) { if (db==null) return; System.out.println("onUpdateTooLong"); } | ||||
| 	 | ||||
| 	private void processUpdate(TLAbsUpdate update, TelegramClient client) { | ||||
| 		if (update instanceof TLUpdateNewMessage) { | ||||
| 			TLAbsMessage abs_msg = ((TLUpdateNewMessage)update).getMessage(); | ||||
| 			TLVector<TLAbsMessage> vector = new TLVector<TLAbsMessage>(TLAbsMessage.class); | ||||
| 			vector.add(abs_msg); | ||||
| 			db.saveMessages(vector, Kotlogram.API_LAYER, gson); | ||||
| 			System.out.print('.'); | ||||
| 			if (abs_msg instanceof TLMessage) { | ||||
| 				AbstractMediaFileManager fm = FileManagerFactory.getFileManager((TLMessage)abs_msg, user, client); | ||||
| 				if (fm != null && !fm.isEmpty() && !fm.isDownloaded()) { | ||||
| 					try { | ||||
| 						fm.download(); | ||||
| 					} catch (Exception e) { | ||||
| 						System.out.println("We got an exception while downloading media, but we're going to ignore it."); | ||||
| 						System.out.println("Here it is anyway:"); | ||||
| 						e.printStackTrace(); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			// ignore everything else... | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,64 +0,0 @@ | ||||
| package de.fabianonline.telegram_backup; | ||||
|  | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import java.sql.Connection; | ||||
| import java.sql.DriverManager; | ||||
| import java.sql.Statement; | ||||
| import java.sql.SQLException; | ||||
| import java.sql.ResultSet; | ||||
| import java.io.IOException; | ||||
| import java.nio.charset.Charset; | ||||
|  | ||||
| class TestFeatures { | ||||
| 	public static void test1() { | ||||
| 		// Tests entries in a cache4.db in the current working directory for compatibility | ||||
| 		try { | ||||
| 			Class.forName("org.sqlite.JDBC"); | ||||
| 		} catch(ClassNotFoundException e) { | ||||
| 			CommandLineController.show_error("Could not load jdbc-sqlite class."); | ||||
| 		} | ||||
|  | ||||
| 		String path = "jdbc:sqlite:cache4.db"; | ||||
|  | ||||
| 		Connection conn = null; | ||||
| 		Statement stmt = null; | ||||
| 		 | ||||
| 		try { | ||||
| 			conn = DriverManager.getConnection(path); | ||||
| 			stmt = conn.createStatement(); | ||||
| 		} catch (SQLException e) { | ||||
| 			CommandLineController.show_error("Could not connect to SQLITE database."); | ||||
| 		} | ||||
| 		 | ||||
| 		int unsupported_constructor = 0; | ||||
| 		int success = 0; | ||||
| 		 | ||||
| 		try { | ||||
| 			ResultSet rs = stmt.executeQuery("SELECT data FROM messages"); | ||||
| 			while (rs.next()) { | ||||
| 				try { | ||||
| 					TLApiContext.getInstance().deserializeMessage(rs.getBytes(1)); | ||||
| 				} catch (com.github.badoualy.telegram.tl.exception.UnsupportedConstructorException e) { | ||||
| 					unsupported_constructor++; | ||||
| 				} catch (IOException e) { | ||||
| 					System.out.println("IOException: " + e); | ||||
| 				} | ||||
| 				success++; | ||||
| 			} | ||||
| 		} catch (SQLException e) { | ||||
| 			System.out.println("SQL exception: " + e); | ||||
| 		} | ||||
| 		 | ||||
| 		System.out.println("Success:                 " + success); | ||||
| 		System.out.println("Unsupported constructor: " + unsupported_constructor); | ||||
| 	} | ||||
| 	 | ||||
| 	public static void test2(UserManager user, TelegramClient client) { | ||||
| 		// Prints system.encoding and default charset | ||||
| 		System.out.println("Default Charset:   " + Charset.defaultCharset()); | ||||
| 		System.out.println("file.encoding:     " + System.getProperty("file.encoding")); | ||||
| 		Database db = Database.getInstance(); | ||||
| 		System.out.println("Database encoding: " + db.getEncoding()); | ||||
| 	} | ||||
| } | ||||
| @@ -45,20 +45,12 @@ public class UserManager { | ||||
| 	private static Logger logger = LoggerFactory.getLogger(UserManager.class); | ||||
| 	private static UserManager instance = null; | ||||
| 	 | ||||
| 	public static void init(TelegramClient c) throws IOException { | ||||
| 		instance = new UserManager(c); | ||||
| 	public static void init(String phone) throws IOException { | ||||
| 		instance = new UserManager(phone); | ||||
| 	} | ||||
| 	 | ||||
| 	private UserManager(TelegramClient c) throws IOException { | ||||
| 		this.client = c; | ||||
| 		logger.debug("Calling getFullUser"); | ||||
| 		try { | ||||
| 			TLUserFull full_user = this.client.usersGetFullUser(new TLInputUserSelf()); | ||||
| 			this.user = full_user.getUser().getAsUser(); | ||||
| 		} catch (RpcErrorException e) { | ||||
| 			// This may happen. Ignoring it. | ||||
| 			logger.debug("Ignoring exception:", e); | ||||
| 		} | ||||
| 	private UserManager(String phone) throws IOException { | ||||
| 		this.phone = phone; | ||||
| 	} | ||||
| 	 | ||||
| 	public static UserManager getInstance() { | ||||
| @@ -69,19 +61,9 @@ public class UserManager { | ||||
| 	public boolean isLoggedIn() { return user!=null; } | ||||
| 	 | ||||
| 	public void sendCodeToPhoneNumber(String number) throws RpcErrorException, IOException { | ||||
| 		this.phone = number; | ||||
| 		this.sent_code = this.client.authSendCode(false, this.phone, true); | ||||
| 	} | ||||
| 	 | ||||
| 	public void verifyCode(String code) throws RpcErrorException, IOException { | ||||
| 		this.code = code; | ||||
| 		try { | ||||
| 			this.auth = client.authSignIn(phone, this.sent_code.getPhoneCodeHash(), this.code); | ||||
| 			this.user = auth.getUser().getAsUser(); | ||||
| 		} catch (RpcErrorException e) { | ||||
| 			if (e.getCode()!=401 || !e.getTag().equals("SESSION_PASSWORD_NEEDED")) throw e; | ||||
| 			this.password_needed = true; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public boolean isPasswordNeeded() { return this.password_needed; } | ||||
| @@ -126,6 +108,6 @@ public class UserManager { | ||||
| 	public TLUser getUser() { return this.user; } | ||||
| 	 | ||||
| 	public String getFileBase() { | ||||
| 		return Config.FILE_BASE + File.separatorChar + "+" + this.user.getPhone() + File.separatorChar; | ||||
| 		return Config.FILE_BASE + File.separatorChar + this.phone + File.separatorChar; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -48,130 +48,6 @@ public class Utils { | ||||
| 		return accounts; | ||||
| 	} | ||||
| 	 | ||||
| 	static void obeyFloodWaitException(RpcErrorException e) throws RpcErrorException { | ||||
| 		obeyFloodWaitException(e, false); | ||||
| 	} | ||||
| 	 | ||||
| 	static void obeyFloodWaitException(RpcErrorException e, boolean silent) throws RpcErrorException { | ||||
| 		if (e==null || e.getCode()!=420) return; | ||||
| 		 | ||||
| 		int delay = e.getTagInteger(); | ||||
| 		if(!silent) { | ||||
| 			System.out.println(""); | ||||
| 			System.out.println( | ||||
| 				"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" + | ||||
| 				"asked us to wait for " + delay + " seconds.\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" + | ||||
| 				"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" + | ||||
| 				"the fact that Telegram won't talk to me until then."); | ||||
| 			System.out.println(""); | ||||
| 		} | ||||
| 		try { TimeUnit.SECONDS.sleep(delay + 1); } catch(InterruptedException e2) {} | ||||
| 	} | ||||
| 	 | ||||
| 	static Version getNewestVersion() { | ||||
| 		try { | ||||
| 			String data_url = "https://api.github.com/repos/fabianonline/telegram_backup/releases"; | ||||
| 			logger.debug("Requesting current release info from {}", data_url); | ||||
| 			String json = IOUtils.toString(new URL(data_url)); | ||||
| 			JsonParser parser = new JsonParser(); | ||||
| 			JsonElement root_elm = parser.parse(json); | ||||
| 			if (root_elm.isJsonArray()) { | ||||
| 				JsonArray root = root_elm.getAsJsonArray(); | ||||
| 				JsonObject newest_version = null; | ||||
| 				for (JsonElement e : root) if (e.isJsonObject()) { | ||||
| 					JsonObject version = e.getAsJsonObject(); | ||||
| 					if (version.getAsJsonPrimitive("prerelease").getAsBoolean() == false) { | ||||
| 						newest_version = version; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				if (newest_version == null) return null; | ||||
| 				String new_v = newest_version.getAsJsonPrimitive("tag_name").getAsString(); | ||||
| 				logger.debug("Found current release version {}", new_v); | ||||
| 				String cur_v = Config.APP_APPVER; | ||||
| 				 | ||||
| 				int result = compareVersions(cur_v, new_v); | ||||
| 				 | ||||
| 				return new Version(new_v, newest_version.getAsJsonPrimitive("html_url").getAsString(), newest_version.getAsJsonPrimitive("body").getAsString(), result == VERSION_2_NEWER); | ||||
| 			} | ||||
| 			return null; | ||||
| 		} catch(Exception e) { | ||||
| 			return null; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public static int compareVersions(String v1, String v2) { | ||||
| 		logger.debug("Comparing versions {} and {}.", v1, v2); | ||||
| 		if (v1.equals(v2)) return VERSIONS_EQUAL; | ||||
| 				 | ||||
| 		String[] v1_p = v1.split("-", 2); | ||||
| 		String[] v2_p = v2.split("-", 2); | ||||
| 		 | ||||
| 		logger.trace("Parts to compare without suffixes: {} and {}.", v1_p[0], v2_p[0]); | ||||
| 		 | ||||
| 		String[] v1_p2 = v1_p[0].split("\\."); | ||||
| 		String[] v2_p2 = v2_p[0].split("\\."); | ||||
| 		 | ||||
| 		logger.trace("Length of the parts without suffixes: {} and {}.", v1_p2.length, v2_p2.length); | ||||
| 		 | ||||
| 		int i; | ||||
| 		for (i=0; i<v1_p2.length && i<v2_p2.length; i++) { | ||||
| 			int i_1 = Integer.parseInt(v1_p2[i]); | ||||
| 			int i_2 = Integer.parseInt(v2_p2[i]); | ||||
| 			logger.trace("Comparing parts: {} and {}.", i_1, i_2); | ||||
| 			if (i_1 > i_2) { | ||||
| 				logger.debug("v1 is newer"); | ||||
| 				return VERSION_1_NEWER; | ||||
| 			} else if (i_2 > i_1) { | ||||
| 				logger.debug("v2 is newer"); | ||||
| 				return VERSION_2_NEWER; | ||||
| 			} | ||||
| 		} | ||||
| 		logger.trace("At least one of the versions has run out of parts."); | ||||
| 		if (v1_p2.length > v2_p2.length) { | ||||
| 			logger.debug("v1 is longer, so it is newer"); | ||||
| 			return VERSION_1_NEWER; | ||||
| 		} else if (v2_p2.length > v1_p2.length) { | ||||
| 			logger.debug("v2 is longer, so it is newer"); | ||||
| 			return VERSION_2_NEWER; | ||||
| 		} | ||||
| 		 | ||||
| 		// startsWith | ||||
| 		if (v1_p.length>1 && v2_p.length==1) { | ||||
| 			logger.debug("v1 has a suffix, v2 not."); | ||||
| 			if (v1_p[1].startsWith("pre")) { | ||||
| 				logger.debug("v1 is a pre version, so v1 is newer"); | ||||
| 				return VERSION_2_NEWER; | ||||
| 			} else { | ||||
| 				return VERSION_1_NEWER; | ||||
| 			} | ||||
| 		} else if (v1_p.length==1 && v2_p.length>1) { | ||||
| 			logger.debug("v1 has no suffix, but v2 has"); | ||||
| 			if (v2_p[1].startsWith("pre")) { | ||||
| 				logger.debug("v2 is a pre version, so v1 is better"); | ||||
| 				return VERSION_1_NEWER; | ||||
| 			} else { | ||||
| 				return VERSION_2_NEWER; | ||||
| 			} | ||||
| 		} else if (v1_p.length>1 && v2_p.length>1) { | ||||
| 			logger.debug("Both have a suffix"); | ||||
| 			if (v1_p[1].startsWith("pre") && !v2_p[1].startsWith("pre")) { | ||||
| 				logger.debug("v1 is a 'pre' version, v2 not."); | ||||
| 				return VERSION_2_NEWER; | ||||
| 			} else if (!v1_p[1].startsWith("pre") && v2_p[1].startsWith("pre")) { | ||||
| 				logger.debug("v2 is a 'pre' version, v2 not."); | ||||
| 				return VERSION_1_NEWER; | ||||
| 			} | ||||
| 			return VERSIONS_EQUAL; | ||||
| 		} | ||||
| 		logger.debug("We couldn't find a real difference, so we're assuming the versions are equal-ish."); | ||||
| 		return VERSIONS_EQUAL; | ||||
| 	} | ||||
| 	 | ||||
| 	public static String anonymize(String str) { | ||||
| 		if (!CommandLineOptions.cmd_anonymize) return str; | ||||
| 		return str.replaceAll("[0-9]", "1").replaceAll("[A-Z]", "A").replaceAll("[a-z]", "a") + " (ANONYMIZED)"; | ||||
|   | ||||
| @@ -1,182 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup.exporter; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.Utils; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.PrintWriter; | ||||
| import java.io.OutputStreamWriter; | ||||
| import java.io.FileOutputStream; | ||||
| import java.nio.charset.Charset; | ||||
| import java.io.FileWriter; | ||||
| import java.io.IOException; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.net.URL; | ||||
| import org.apache.commons.io.FileUtils; | ||||
| import java.util.LinkedList; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
|  | ||||
| import com.github.mustachejava.DefaultMustacheFactory; | ||||
| import com.github.mustachejava.Mustache; | ||||
| import com.github.mustachejava.MustacheFactory; | ||||
|  | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| public class HTMLExporter { | ||||
| 	private static Logger logger = LoggerFactory.getLogger(HTMLExporter.class); | ||||
| 	 | ||||
| 	public void export() throws IOException { | ||||
| 		try { | ||||
| 			UserManager user = UserManager.getInstance(); | ||||
| 			Database db = Database.getInstance(); | ||||
| 			 | ||||
| 			// Create base dir | ||||
| 			logger.debug("Creating base dir"); | ||||
| 			String base = user.getFileBase() + "files" + File.separatorChar; | ||||
| 			new File(base).mkdirs(); | ||||
| 			new File(base + "dialogs").mkdirs(); | ||||
| 			 | ||||
| 			logger.debug("Fetching dialogs"); | ||||
| 			LinkedList<Database.Dialog> dialogs = db.getListOfDialogsForExport(); | ||||
| 			logger.trace("Got {} dialogs", dialogs.size()); | ||||
| 			logger.debug("Fetching chats"); | ||||
| 			LinkedList<Database.Chat> chats = db.getListOfChatsForExport(); | ||||
| 			logger.trace("Got {} chats", chats.size()); | ||||
| 			 | ||||
| 			logger.debug("Generating index.html"); | ||||
| 			HashMap<String, Object> scope = new HashMap<String, Object>(); | ||||
| 			scope.put("user", user); | ||||
| 			scope.put("dialogs", dialogs); | ||||
| 			scope.put("chats", chats); | ||||
| 			 | ||||
| 			// Collect stats data | ||||
| 			scope.put("count.chats", chats.size()); | ||||
| 			scope.put("count.dialogs", dialogs.size()); | ||||
| 			 | ||||
| 			int count_messages_chats = 0; | ||||
| 			int count_messages_dialogs = 0; | ||||
| 			for (Database.Chat   c : chats)   count_messages_chats   += c.count; | ||||
| 			for (Database.Dialog d : dialogs) count_messages_dialogs += d.count; | ||||
| 			 | ||||
| 			scope.put("count.messages", count_messages_chats + count_messages_dialogs); | ||||
| 			scope.put("count.messages.chats", count_messages_chats); | ||||
| 			scope.put("count.messages.dialogs", count_messages_dialogs); | ||||
| 			 | ||||
| 			scope.put("count.messages.from_me", db.getMessagesFromUserCount()); | ||||
|  | ||||
| 			scope.put("heatmap_data", intArrayToString(db.getMessageTimesMatrix())); | ||||
| 			 | ||||
| 			scope.putAll(db.getMessageAuthorsWithCount()); | ||||
| 			scope.putAll(db.getMessageTypesWithCount()); | ||||
| 			scope.putAll(db.getMessageMediaTypesWithCount()); | ||||
| 			 | ||||
| 			MustacheFactory mf = new DefaultMustacheFactory(); | ||||
| 			Mustache mustache = mf.compile("templates/html/index.mustache"); | ||||
| 			OutputStreamWriter w = getWriter(base + "index.html"); | ||||
| 			mustache.execute(w, scope); | ||||
| 			w.close(); | ||||
| 			 | ||||
| 			mustache = mf.compile("templates/html/chat.mustache"); | ||||
| 			 | ||||
| 			int i=0; | ||||
| 			logger.debug("Generating {} dialog pages", dialogs.size()); | ||||
| 			for (Database.Dialog d : dialogs) { | ||||
| 				i++; | ||||
| 				logger.trace("Dialog {}/{}: {}", i, dialogs.size(), Utils.anonymize(""+d.id)); | ||||
| 				LinkedList<HashMap<String, Object>> messages = db.getMessagesForExport(d); | ||||
| 				scope.clear(); | ||||
| 				scope.put("user", user); | ||||
| 				scope.put("dialog", d); | ||||
| 				scope.put("messages", messages); | ||||
| 				 | ||||
| 				scope.putAll(db.getMessageAuthorsWithCount(d)); | ||||
| 				scope.put("heatmap_data", intArrayToString(db.getMessageTimesMatrix(d))); | ||||
| 				scope.putAll(db.getMessageTypesWithCount(d)); | ||||
| 				scope.putAll(db.getMessageMediaTypesWithCount(d)); | ||||
| 				 | ||||
| 				w = getWriter(base + "dialogs" + File.separatorChar + "user_" + d.id + ".html"); | ||||
| 				mustache.execute(w, scope); | ||||
| 				w.close(); | ||||
| 			} | ||||
| 			 | ||||
| 			i=0; | ||||
| 			logger.debug("Generating {} chat pages", chats.size()); | ||||
| 			for (Database.Chat c : chats) { | ||||
| 				i++; | ||||
| 				logger.trace("Chat {}/{}: {}", i, chats.size(), Utils.anonymize(""+c.id)); | ||||
| 				LinkedList<HashMap<String, Object>> messages = db.getMessagesForExport(c); | ||||
| 				scope.clear(); | ||||
| 				scope.put("user", user); | ||||
| 				scope.put("chat", c); | ||||
| 				scope.put("messages", messages); | ||||
| 				 | ||||
| 				scope.putAll(db.getMessageAuthorsWithCount(c)); | ||||
| 				scope.put("heatmap_data", intArrayToString(db.getMessageTimesMatrix(c))); | ||||
| 				scope.putAll(db.getMessageTypesWithCount(c)); | ||||
| 				scope.putAll(db.getMessageMediaTypesWithCount(c)); | ||||
| 				 | ||||
| 				w = getWriter(base + "dialogs" + File.separatorChar + "chat_" + c.id + ".html"); | ||||
| 				mustache.execute(w, scope); | ||||
| 				w.close(); | ||||
| 			} | ||||
| 			 | ||||
| 			logger.debug("Generating additional files"); | ||||
| 			// Copy CSS | ||||
| 			URL cssFile = getClass().getResource("/templates/html/style.css"); | ||||
| 			File dest = new File(base + "style.css"); | ||||
| 			FileUtils.copyURLToFile(cssFile, dest); | ||||
| 			logger.debug("Done exporting."); | ||||
| 		} catch (IOException e) { | ||||
| 			e.printStackTrace(); | ||||
| 			logger.error("Caught an exception!", e); | ||||
| 			throw e; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	private OutputStreamWriter getWriter(String filename) throws FileNotFoundException { | ||||
| 		logger.trace("Creating writer for file {}", Utils.anonymize(filename)); | ||||
| 		return new OutputStreamWriter(new FileOutputStream(filename), Charset.forName("UTF-8").newEncoder()); | ||||
| 	} | ||||
|  | ||||
| 	private String intArrayToString(int[][] data) { | ||||
| 		StringBuilder sb = new StringBuilder(); | ||||
| 		sb.append("["); | ||||
| 		for (int x=0; x<data.length; x++) { | ||||
| 			for (int y=0; y<data[x].length; y++) { | ||||
| 				if (x>0 || y>0) sb.append(","); | ||||
| 				sb.append("[" + x + "," + y + "," + data[x][y] + "]"); | ||||
| 			} | ||||
| 		} | ||||
| 		sb.append("]"); | ||||
| 		return sb.toString(); | ||||
| 	} | ||||
| 	 | ||||
| 	private String mapToString(Map<String, Integer> map) { | ||||
| 		StringBuilder sb = new StringBuilder("["); | ||||
| 		for (Map.Entry<String, Integer> entry : map.entrySet()) { | ||||
| 			sb.append("['" + entry.getKey() + "', " + entry.getValue() + "],"); | ||||
| 		} | ||||
| 		sb.append("]"); | ||||
| 		return sb.toString(); | ||||
| 	} | ||||
| } | ||||
| @@ -1,86 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup.mediafilemanager; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.StickerConverter; | ||||
| import de.fabianonline.telegram_backup.DownloadProgressInterface; | ||||
| import de.fabianonline.telegram_backup.Config; | ||||
| import de.fabianonline.telegram_backup.DownloadManager; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import com.github.badoualy.telegram.tl.core.TLIntVector; | ||||
| import com.github.badoualy.telegram.tl.core.TLObject; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.tl.api.upload.TLFile; | ||||
| import com.github.badoualy.telegram.tl.exception.RpcErrorException; | ||||
| import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedList; | ||||
| import java.net.URL; | ||||
| import java.util.concurrent.TimeoutException; | ||||
|  | ||||
| import org.apache.commons.io.FileUtils; | ||||
|  | ||||
| public abstract class AbstractMediaFileManager { | ||||
| 	protected UserManager user; | ||||
| 	protected TLMessage message; | ||||
| 	protected TelegramClient client; | ||||
| 	protected boolean isEmpty = false; | ||||
| 	 | ||||
| 	public AbstractMediaFileManager(TLMessage msg, UserManager user, TelegramClient client) {this.user = user; this.message = msg; this.client = client;}; | ||||
| 	public abstract int getSize(); | ||||
| 	public abstract String getExtension(); | ||||
| 	public boolean isEmpty() { return isEmpty; } | ||||
| 	public boolean isDownloaded() { return new File(getTargetPathAndFilename()).isFile(); } | ||||
| 	public boolean isDownloading() { return new File(getTargetPathAndFilename() + ".downloading").isFile(); } | ||||
| 	public abstract void download() throws RpcErrorException, IOException, TimeoutException; | ||||
| 	public static void throwUnexpectedObjectError(Object o) { | ||||
| 		throw new RuntimeException("Unexpected " + o.getClass().getName()); | ||||
| 	} | ||||
| 	public String getTargetPath() { | ||||
| 		String path = user.getFileBase() + Config.FILE_FILES_BASE + File.separatorChar; | ||||
| 		new File(path).mkdirs(); | ||||
| 		return path; | ||||
| 	} | ||||
| 	public String getTargetFilename() { return "" + message.getId() + "." + getExtension(); } | ||||
| 	public String getTargetPathAndFilename() { return getTargetPath() + getTargetFilename(); } | ||||
| 	 | ||||
| 	protected String extensionFromMimetype(String mime) { | ||||
| 		switch(mime) { | ||||
| 			case "text/plain": return "txt"; | ||||
| 		} | ||||
| 		 | ||||
| 		int i = mime.lastIndexOf('/'); | ||||
| 		String ext = mime.substring(i+1).toLowerCase(); | ||||
| 		 | ||||
| 		if (ext=="unknown") return "dat"; | ||||
| 		 | ||||
| 		return ext; | ||||
| 	} | ||||
| 	 | ||||
| 	public abstract String getLetter(); | ||||
| 	public abstract String getName(); | ||||
| 	public abstract String getDescription(); | ||||
| } | ||||
| @@ -1,112 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup.mediafilemanager; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.StickerConverter; | ||||
| import de.fabianonline.telegram_backup.DownloadProgressInterface; | ||||
| import de.fabianonline.telegram_backup.DownloadManager; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import com.github.badoualy.telegram.tl.core.TLIntVector; | ||||
| import com.github.badoualy.telegram.tl.core.TLObject; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.tl.api.upload.TLFile; | ||||
| import com.github.badoualy.telegram.tl.exception.RpcErrorException; | ||||
| import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedList; | ||||
| import java.net.URL; | ||||
| import java.util.concurrent.TimeoutException; | ||||
|  | ||||
| import org.apache.commons.io.FileUtils; | ||||
|  | ||||
| public class DocumentFileManager extends AbstractMediaFileManager { | ||||
| 	protected TLDocument doc; | ||||
| 	private String extension = null; | ||||
| 	 | ||||
| 	public DocumentFileManager(TLMessage msg, UserManager user, TelegramClient client) { | ||||
| 		super(msg, user, client); | ||||
| 		TLAbsDocument d = ((TLMessageMediaDocument)msg.getMedia()).getDocument(); | ||||
| 		if (d instanceof TLDocument) { | ||||
| 			this.doc = (TLDocument)d; | ||||
| 		} else if (d instanceof TLDocumentEmpty) { | ||||
| 			this.isEmpty = true; | ||||
| 		} else { | ||||
| 			throwUnexpectedObjectError(d); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public boolean isSticker() { | ||||
| 		TLDocumentAttributeSticker sticker = null; | ||||
| 		if (this.isEmpty || doc==null) return false; | ||||
| 		if (doc.getAttributes() != null) for(TLAbsDocumentAttribute attr : doc.getAttributes()) { | ||||
| 			if (attr instanceof TLDocumentAttributeSticker) { | ||||
| 				sticker = (TLDocumentAttributeSticker)attr; | ||||
| 			} | ||||
| 		} | ||||
| 		return sticker!=null; | ||||
| 	} | ||||
| 	 | ||||
| 	public int getSize() { | ||||
| 		if (doc != null) return doc.getSize(); | ||||
| 		return 0; | ||||
| 	 } | ||||
| 	 | ||||
| 	public String getExtension() { | ||||
| 		if (extension != null) return extension; | ||||
| 		if (doc == null) return "empty"; | ||||
| 		String ext = null; | ||||
| 		String original_filename = null; | ||||
| 		if (doc.getAttributes() != null) for(TLAbsDocumentAttribute attr : doc.getAttributes()) { | ||||
| 			if (attr instanceof TLDocumentAttributeFilename) { | ||||
| 				original_filename = ((TLDocumentAttributeFilename)attr).getFileName(); | ||||
| 			} | ||||
| 		} | ||||
| 		if (original_filename != null) { | ||||
| 			int i = original_filename.lastIndexOf('.'); | ||||
| 			if (i>0) ext = original_filename.substring(i+1); | ||||
| 			 | ||||
| 		} | ||||
| 		if (ext==null) { | ||||
| 			ext = extensionFromMimetype(doc.getMimeType()); | ||||
| 		} | ||||
| 		 | ||||
| 		// Sometimes, extensions contain a trailing double quote. Remove this. Fixes #12. | ||||
| 		ext = ext.replace("\"", ""); | ||||
| 		 | ||||
| 		this.extension = ext; | ||||
| 		return ext; | ||||
| 	} | ||||
| 	 | ||||
| 	public void download() throws RpcErrorException, IOException, TimeoutException { | ||||
| 		if (doc!=null) { | ||||
| 			DownloadManager.downloadFile(client, getTargetPathAndFilename(), getSize(), doc.getDcId(), doc.getId(), doc.getAccessHash()); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public String getLetter() { return "d"; } | ||||
| 	public String getName() { return "document"; } | ||||
| 	public String getDescription() { return "Document"; } | ||||
| } | ||||
| @@ -1,75 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup.mediafilemanager; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.StickerConverter; | ||||
| import de.fabianonline.telegram_backup.DownloadProgressInterface; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import com.github.badoualy.telegram.tl.core.TLIntVector; | ||||
| import com.github.badoualy.telegram.tl.core.TLObject; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.tl.api.upload.TLFile; | ||||
| import com.github.badoualy.telegram.tl.exception.RpcErrorException; | ||||
| import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedList; | ||||
| import java.net.URL; | ||||
| import java.util.concurrent.TimeoutException; | ||||
|  | ||||
| import org.apache.commons.io.FileUtils; | ||||
|  | ||||
| public class FileManagerFactory { | ||||
| 	public static AbstractMediaFileManager getFileManager(TLMessage m, UserManager u, TelegramClient c) { | ||||
| 		if (m==null) return null; | ||||
| 		TLAbsMessageMedia media = m.getMedia(); | ||||
| 		if (media==null) return null; | ||||
| 		 | ||||
| 		if (media instanceof TLMessageMediaPhoto) { | ||||
| 			return new PhotoFileManager(m, u, c); | ||||
| 		} else if (media instanceof TLMessageMediaDocument) { | ||||
| 			DocumentFileManager d = new DocumentFileManager(m, u, c); | ||||
| 			if (d.isSticker()) { | ||||
| 				return new StickerFileManager(m, u, c); | ||||
| 			} | ||||
| 			return d; | ||||
| 		} else if (media instanceof TLMessageMediaGeo) { | ||||
| 			return new GeoFileManager(m, u, c); | ||||
| 		} else if (media instanceof TLMessageMediaEmpty) { | ||||
| 			return new UnsupportedFileManager(m, u, c, "empty"); | ||||
| 		} else if (media instanceof TLMessageMediaUnsupported) { | ||||
| 			return new UnsupportedFileManager(m, u, c, "unsupported"); | ||||
| 		} else if (media instanceof TLMessageMediaWebPage) { | ||||
| 			return new UnsupportedFileManager(m, u, c, "webpage"); | ||||
| 		} else if (media instanceof TLMessageMediaContact) { | ||||
| 			return new UnsupportedFileManager(m, u, c, "contact"); | ||||
| 		} else if (media instanceof TLMessageMediaVenue) { | ||||
| 			return new UnsupportedFileManager(m, u, c, "venue"); | ||||
| 		} else { | ||||
| 			AbstractMediaFileManager.throwUnexpectedObjectError(media); | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
| } | ||||
| @@ -1,82 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup.mediafilemanager; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.StickerConverter; | ||||
| import de.fabianonline.telegram_backup.DownloadProgressInterface; | ||||
| import de.fabianonline.telegram_backup.DownloadManager; | ||||
| import de.fabianonline.telegram_backup.Config; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import com.github.badoualy.telegram.tl.core.TLIntVector; | ||||
| import com.github.badoualy.telegram.tl.core.TLObject; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.tl.api.upload.TLFile; | ||||
| import com.github.badoualy.telegram.tl.exception.RpcErrorException; | ||||
| import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedList; | ||||
| import java.net.URL; | ||||
| import java.util.concurrent.TimeoutException; | ||||
|  | ||||
| import org.apache.commons.io.FileUtils; | ||||
|  | ||||
| public class GeoFileManager extends AbstractMediaFileManager { | ||||
| 	protected TLGeoPoint geo; | ||||
| 	 | ||||
| 	public GeoFileManager(TLMessage msg, UserManager user, TelegramClient client) { | ||||
| 		super(msg, user, client); | ||||
| 		TLAbsGeoPoint g = ((TLMessageMediaGeo)msg.getMedia()).getGeo(); | ||||
| 		if (g instanceof TLGeoPoint) { | ||||
| 			this.geo = (TLGeoPoint) g; | ||||
| 		} else if (g instanceof TLGeoPointEmpty) { | ||||
| 			this.isEmpty = true; | ||||
| 		} else { | ||||
| 			throwUnexpectedObjectError(g); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public int getSize() { | ||||
| 		File f = new File(getTargetPathAndFilename()); | ||||
| 		if (f.isFile()) return (int)f.length(); | ||||
| 		 | ||||
| 		// We don't know the size, so we just guess. | ||||
| 		return 100000; | ||||
| 	} | ||||
| 	 | ||||
| 	public String getExtension() { return "png"; } | ||||
| 	 | ||||
| 	public void download() throws IOException { | ||||
| 		String url = "https://maps.googleapis.com/maps/api/staticmap?" + | ||||
| 			"center=" + geo.getLat() + "," + geo.getLong() + "&" + | ||||
| 			"zoom=14&size=300x150&scale=2&format=png&" + | ||||
| 			"key=" + Config.SECRET_GMAPS; | ||||
| 		DownloadManager.downloadExternalFile(getTargetPathAndFilename(), url); | ||||
| 	} | ||||
|  | ||||
| 	public String getLetter() { return "g"; } | ||||
| 	public String getName() { return "geo"; }	 | ||||
| 	public String getDescription() { return "Geolocation"; } | ||||
| } | ||||
| @@ -1,88 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup.mediafilemanager; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.StickerConverter; | ||||
| import de.fabianonline.telegram_backup.DownloadProgressInterface; | ||||
| import de.fabianonline.telegram_backup.DownloadManager; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import com.github.badoualy.telegram.tl.core.TLIntVector; | ||||
| import com.github.badoualy.telegram.tl.core.TLObject; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.tl.api.upload.TLFile; | ||||
| import com.github.badoualy.telegram.tl.exception.RpcErrorException; | ||||
| import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedList; | ||||
| import java.net.URL; | ||||
| import java.util.concurrent.TimeoutException; | ||||
|  | ||||
| import org.apache.commons.io.FileUtils; | ||||
|  | ||||
| public class PhotoFileManager extends AbstractMediaFileManager { | ||||
| 	private TLPhoto photo; | ||||
| 	private TLPhotoSize size = null; | ||||
| 	public PhotoFileManager(TLMessage msg, UserManager user, TelegramClient client) { | ||||
| 		super(msg, user, client); | ||||
| 		TLAbsPhoto p = ((TLMessageMediaPhoto)msg.getMedia()).getPhoto(); | ||||
| 		if (p instanceof TLPhoto) { | ||||
| 			this.photo = (TLPhoto)p; | ||||
| 			 | ||||
| 			TLPhotoSize biggest = null; | ||||
| 			for (TLAbsPhotoSize s : photo.getSizes()) if (s instanceof TLPhotoSize) { | ||||
| 				TLPhotoSize size = (TLPhotoSize) s; | ||||
| 				if (biggest == null || (size.getW()>biggest.getW() && size.getH()>biggest.getH())) { | ||||
| 					biggest = size; | ||||
| 				} | ||||
| 			} | ||||
| 			if (biggest==null) { | ||||
| 				throw new RuntimeException("Could not find a size for a photo."); | ||||
| 			} | ||||
| 			this.size = biggest; | ||||
| 		} else if (p instanceof TLPhotoEmpty) { | ||||
| 			this.isEmpty = true; | ||||
| 		} else { | ||||
| 			throwUnexpectedObjectError(p); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public int getSize() { | ||||
| 		if (size!=null) return size.getSize(); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	 | ||||
| 	public String getExtension() { return "jpg"; } | ||||
| 	 | ||||
| 	public void download() throws RpcErrorException, IOException, TimeoutException { | ||||
| 		if (isEmpty) return; | ||||
| 		TLFileLocation loc = (TLFileLocation) size.getLocation(); | ||||
| 		DownloadManager.downloadFile(client, getTargetPathAndFilename(), getSize(), loc.getDcId(), loc.getVolumeId(), loc.getLocalId(), loc.getSecret()); | ||||
| 	} | ||||
| 	 | ||||
| 	public String getLetter() { return "p"; } | ||||
| 	public String getName() { return "photo"; } | ||||
| 	public String getDescription() { return "Photo"; } | ||||
| } | ||||
| @@ -1,108 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup.mediafilemanager; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.StickerConverter; | ||||
| import de.fabianonline.telegram_backup.DownloadProgressInterface; | ||||
| import de.fabianonline.telegram_backup.DownloadManager; | ||||
| import de.fabianonline.telegram_backup.Config; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import com.github.badoualy.telegram.tl.core.TLIntVector; | ||||
| import com.github.badoualy.telegram.tl.core.TLObject; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.tl.api.upload.TLFile; | ||||
| import com.github.badoualy.telegram.tl.exception.RpcErrorException; | ||||
| import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile; | ||||
|  | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.StandardCopyOption; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedList; | ||||
| import java.net.URL; | ||||
| import java.util.concurrent.TimeoutException; | ||||
|  | ||||
| import org.apache.commons.io.FileUtils; | ||||
|  | ||||
| public class StickerFileManager extends DocumentFileManager { | ||||
| 	private static Logger logger = LoggerFactory.getLogger(StickerFileManager.class); | ||||
| 	 | ||||
| 	public StickerFileManager(TLMessage msg, UserManager user, TelegramClient client) { | ||||
| 		super(msg, user, client); | ||||
| 	} | ||||
| 	 | ||||
| 	public boolean isSticker() { return true; } | ||||
| 	 | ||||
| 	private String getFilenameBase() { | ||||
| 		TLDocumentAttributeSticker sticker = null; | ||||
| 		for(TLAbsDocumentAttribute attr : doc.getAttributes()) { | ||||
| 			if (attr instanceof TLDocumentAttributeSticker) { | ||||
| 				sticker = (TLDocumentAttributeSticker)attr; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		StringBuilder file = new StringBuilder(); | ||||
| 		if (sticker.getStickerset() instanceof TLInputStickerSetShortName) { | ||||
| 			file.append(((TLInputStickerSetShortName)sticker.getStickerset()).getShortName()); | ||||
| 		} else if (sticker.getStickerset() instanceof TLInputStickerSetID) { | ||||
| 			file.append(((TLInputStickerSetID)sticker.getStickerset()).getId()); | ||||
| 		} | ||||
| 		file.append("_"); | ||||
| 		file.append(sticker.getAlt().hashCode()); | ||||
| 		return file.toString(); | ||||
| 	} | ||||
| 	 | ||||
| 	public String getTargetFilename() { | ||||
| 		return getFilenameBase() + "." + getExtension(); | ||||
| 	} | ||||
| 	 | ||||
| 	public String getTargetPath() { | ||||
| 		String path = user.getFileBase() + Config.FILE_FILES_BASE + File.separatorChar + Config.FILE_STICKER_BASE + File.separatorChar; | ||||
| 		new File(path).mkdirs(); | ||||
| 		return path; | ||||
| 	} | ||||
| 	 | ||||
| 	public void download() throws RpcErrorException, IOException, TimeoutException { | ||||
| 		String old_file = Config.FILE_BASE + File.separatorChar + Config.FILE_STICKER_BASE + File.separatorChar + getTargetFilename(); | ||||
| 		 | ||||
| 		logger.trace("Old filename exists: {}", new File(old_file).exists()); | ||||
| 		 | ||||
| 		if (new File(old_file).exists()) { | ||||
| 			Files.copy(Paths.get(old_file), Paths.get(getTargetPathAndFilename()), StandardCopyOption.REPLACE_EXISTING); | ||||
| 			return; | ||||
| 		} | ||||
| 		super.download(); | ||||
| 	}	 | ||||
| 	 | ||||
| 	public String getExtension() { return "webp"; } | ||||
| 	 | ||||
| 	public String getLetter() { return "s"; } | ||||
| 	public String getName() { return "sticker"; } | ||||
| 	public String getDescription() { return "Sticker"; } | ||||
| } | ||||
| @@ -1,72 +0,0 @@ | ||||
| /* Telegram_Backup | ||||
|  * Copyright (C) 2016 Fabian Schlenz | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ | ||||
|  | ||||
| package de.fabianonline.telegram_backup.mediafilemanager; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.UserManager; | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import de.fabianonline.telegram_backup.StickerConverter; | ||||
| import de.fabianonline.telegram_backup.DownloadProgressInterface; | ||||
| import de.fabianonline.telegram_backup.DownloadManager; | ||||
| import de.fabianonline.telegram_backup.Config; | ||||
|  | ||||
| import com.github.badoualy.telegram.api.TelegramClient; | ||||
| import com.github.badoualy.telegram.tl.core.TLIntVector; | ||||
| import com.github.badoualy.telegram.tl.core.TLObject; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsMessages; | ||||
| import com.github.badoualy.telegram.tl.api.messages.TLAbsDialogs; | ||||
| import com.github.badoualy.telegram.tl.api.*; | ||||
| import com.github.badoualy.telegram.tl.api.upload.TLFile; | ||||
| import com.github.badoualy.telegram.tl.exception.RpcErrorException; | ||||
| import com.github.badoualy.telegram.tl.api.request.TLRequestUploadGetFile; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.LinkedList; | ||||
| import java.net.URL; | ||||
| import java.util.concurrent.TimeoutException; | ||||
|  | ||||
| import org.apache.commons.io.FileUtils; | ||||
|  | ||||
| public class UnsupportedFileManager extends AbstractMediaFileManager { | ||||
| 	String type = null; | ||||
| 	public UnsupportedFileManager(TLMessage msg, UserManager user, TelegramClient client, String type) { | ||||
| 		super(msg, user, client); | ||||
| 		this.type = type; | ||||
| 	} | ||||
|  | ||||
| 	public String getTargetFilename() { | ||||
| 		return ""; | ||||
| 	} | ||||
| 	 | ||||
| 	public String getTargetPath() { | ||||
| 		return ""; | ||||
| 	}	 | ||||
| 	 | ||||
| 	public String getExtension() { return ""; } | ||||
| 	 | ||||
| 	public int getSize() { return 0; } | ||||
| 	 | ||||
| 	public boolean isEmpty() { return false; } | ||||
| 	public void download() {} | ||||
| 	public boolean isDownloaded() { return false; } | ||||
| 	 | ||||
| 	public String getLetter() { return " "; } | ||||
| 	public String getName() { return type; } | ||||
| 	public String getDescription() { return "Unsupported / non-downloadable Media"; } | ||||
| } | ||||
| @@ -1,25 +0,0 @@ | ||||
| package de.fabianonline.telegram_backup.models; | ||||
|  | ||||
| import de.fabianonline.telegram_backup.Database; | ||||
| import com.google.gson.JsonParser; | ||||
| import com.google.gson.JsonObject; | ||||
|  | ||||
| public class Message { | ||||
| 	protected static String tableName = "messages"; | ||||
| 	private JsonObject json; | ||||
| 	private String message = null; | ||||
| 	 | ||||
| 	public Message(String json) { | ||||
| 		this.json = new JsonParser().parse(json).getAsJsonObject(); | ||||
| 	} | ||||
| 	 | ||||
| 	public static Message get(int id) { | ||||
| 		String json = Database.getInstance().queryString("SELECT json FROM " + tableName + " WHERE id=" + id); | ||||
| 		return new Message(json); | ||||
| 	} | ||||
| 	 | ||||
| 	public String getMessage() { | ||||
| 		if (message != null) return message; | ||||
| 		return message = json.getAsJsonPrimitive("message").getAsString(); | ||||
| 	} | ||||
| } | ||||
| @@ -1,4 +0,0 @@ | ||||
| class Peer { | ||||
| 	int userId; | ||||
| 	String _constructor; | ||||
| } | ||||
| @@ -1,17 +0,0 @@ | ||||
| import static org.junit.Assert.*; | ||||
| import org.junit.Test; | ||||
| import de.fabianonline.telegram_backup.Utils; | ||||
|  | ||||
| public class CompareVersionsTest { | ||||
| 	@Test | ||||
| 	public void tests() { | ||||
| 		assertEquals(Utils.compareVersions("1.0.3", "1.0.4"), Utils.VERSION_2_NEWER); | ||||
| 		assertEquals(Utils.compareVersions("1.0.4", "1.0.3"), Utils.VERSION_1_NEWER); | ||||
| 		assertEquals(Utils.compareVersions("1.0.4", "1.0.4.1"), Utils.VERSION_2_NEWER); | ||||
| 		assertEquals(Utils.compareVersions("1.0.4", "1.0.4-pre.1"), Utils.VERSION_1_NEWER); | ||||
| 		assertEquals(Utils.compareVersions("1.0.4", "1.0.4"), Utils.VERSIONS_EQUAL); | ||||
| 		assertEquals(Utils.compareVersions("1.0.4-pre.2", "1.0.4-pre.1"), Utils.VERSIONS_EQUAL); | ||||
| 		assertEquals(Utils.compareVersions("1.0.4", "1.0.4-abcdef-dirty"), Utils.VERSION_2_NEWER); | ||||
| 		assertEquals(Utils.compareVersions("1.0.5", "1.0.5-test.1"), Utils.VERSION_2_NEWER); | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user