├── LICENSE ├── README.org └── backup.lua /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Samim Pezeshki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * telegram-cli-backup 2 | A simple and dirty lua script to backup Telegram messages into a CSV file or a sqlite database. 3 | * How 4 | Change first two lines of the script and set your contact name and message count. Then send the lua script to telegram-cli. If using CSV type, only lua is needed, if writing to a sqlite database, sqlite3 library should be installed (via luarocks for example). 5 | #+BEGIN_SRC sh 6 | $ telegram-cli -s backup.lua 7 | #+END_SRC 8 | * Requirements 9 | - [[https://github.com/vysheng/tg][telegram-cli]] 10 | - lua 11 | 12 | And if writing to a sqlite database: 13 | - Lua lsqlite3 (can be installed by =$ luarocks install lsqlite3=) 14 | -------------------------------------------------------------------------------- /backup.lua: -------------------------------------------------------------------------------- 1 | MODE = 'history' -- 'history' or 'users' 2 | CONTACT_NAME = 'Raff' -- It can be a part of contacts name 3 | MESSAGE_COUNT = 500 4 | TYPE = 'csv' -- 'csv' or 'sqlite' 5 | DATABASE_FILE = 'db.sqlite3' 6 | CSV_FILE_CONTACTS = 'contacts.csv' 7 | CSV_FILE_MESSAGES = 'messages.csv' 8 | 9 | function writeCSV(path, data, sep) 10 | sep = sep or ',' 11 | local file = assert(io.open(path, "a")) 12 | for i=1,#data do 13 | for j=1,#data[i] do 14 | if j>1 then file:write(sep) end 15 | if data[i][j] == nil then 16 | file:write("") 17 | else 18 | file:write(data[i][j]) 19 | end 20 | end 21 | file:write('\n') 22 | end 23 | file:close() 24 | end 25 | 26 | function history_cb(extra, success, history) 27 | if success then 28 | for _,m in pairs(history) do 29 | if not m.service then -- Ignore Telegram service messages 30 | local out = m.out and 1 or 0 -- Cast boolean to integer 31 | if m.text == nil then -- No nil value 32 | m.text = '' 33 | end 34 | 35 | local fromName = 'DELETED_ACCOUNT' 36 | if (m.from ~= nil) then 37 | fromName = m.from.print_name 38 | end 39 | local toName = '' 40 | if (m.to ~= nil) then 41 | toName = m.to.print_name 42 | end 43 | 44 | if TYPE == 'csv' then 45 | if (m.media == nil or m.media == '') then 46 | writeCSV(CSV_FILE_MESSAGES, {{fromName, toName, m.text, out, m.date, m.id,'','',''}}) 47 | elseif m.media.type == 'webpage' and m.media.url ~= nil then 48 | writeCSV(CSV_FILE_MESSAGES, {{fromName, toName, m.text, out, m.date, m.id,'1',m.media.type,m.media.url}}) 49 | else 50 | writeCSV(CSV_FILE_MESSAGES, {{fromName, toName, m.text, out, m.date, m.id,'1',m.media.type,''}}) 51 | end 52 | elseif TYPE == 'sqlite' then 53 | local sql = [[ 54 | INSERT INTO messages 55 | (`from`, `to`, `text`, `out`, `date`, `message_id`, `media`, `media_type`,`url`) 56 | VALUES ( 57 | ']] .. db:escape(fromName) .. [[', 58 | ']] .. db:escape(toName) .. [[', 59 | ']] .. db:escape(m.text) .. [[', 60 | ']] .. out .. [[', 61 | ']] .. m.date .. [[', 62 | ']] .. m.id .. [[', 63 | ]] 64 | 65 | if (m.media == nil or m.media == '') then 66 | sql = sql .. "NULL, NULL, NULL)" 67 | elseif m.media.type == 'webpage' and m.media.url ~= nil then 68 | sql = sql .. "'1','".. m.media.type .. "', '" .. m.media.url .. "')" 69 | else 70 | sql = sql .. "'1','".. m.media.type .. "', NULL)" 71 | end 72 | 73 | print(m.id) 74 | --require 'pl.pretty'.dump(m) 75 | -- print(sql) 76 | res = db:execute(sql) 77 | -- print(heh) 78 | end 79 | end 80 | end 81 | print("done") 82 | end 83 | end 84 | 85 | function user_cb(extra, success, user) 86 | if success and user.print_name ~= 'deleted' then 87 | if user.phone == nil then 88 | user.phone = '' 89 | end 90 | if user.username == nil then 91 | user.username = '' 92 | end 93 | 94 | if TYPE == 'csv' then 95 | writeCSV(CSV_FILE_CONTACTS, {{user.id, user.print_name, user.username, user.phone}}) 96 | elseif TYPE == 'sqlite' then 97 | local sql = [[ 98 | INSERT INTO users 99 | (`id`, `name`, `username`, `phone`) 100 | VALUES ( 101 | ']] .. user.id .. [[', 102 | ']] .. db:escape(user.print_name) .. [[', 103 | ']] .. user.username .. [[', 104 | ']] .. user.phone .. [[' 105 | ); 106 | ]] 107 | -- print(sql) 108 | res = db:execute(sql) 109 | -- print(res) 110 | end 111 | end 112 | end 113 | 114 | function dialogs_cb(extra, success, dialog) 115 | if success then 116 | for _,d in pairs(dialog) do 117 | v = d.peer 118 | print(v.print_name) 119 | if v.print_name ~= nil and string.find(v.print_name, CONTACT_NAME) then 120 | print(v.print_name) 121 | if (v.type == 'user') then 122 | user_info(v.print_name, user_cb, history_extra) 123 | else 124 | chat_info(v.print_name, user_cb, history_extra) 125 | end 126 | if MODE == 'history' then 127 | get_history(v.print_name, MESSAGE_COUNT, history_cb, history_extra) 128 | end 129 | end 130 | end 131 | end 132 | end 133 | 134 | function on_binlog_replay_end () 135 | if TYPE == 'csv' then 136 | data = {{'id', 'name', 'username', 'phone'}} 137 | writeCSV(CSV_FILE_CONTACTS, data) 138 | data = {{'id', 'from', 'to', 'text', 'out', 'date', 'message_id', 'media', 'media_type', 'url'}} 139 | writeCSV(CSV_FILE_MESSAGES, data) 140 | elseif TYPE == 'sqlite' then 141 | sqlite3 = require("luasql.sqlite3") 142 | --db = sqlite3.open(DATABASE_FILE) 143 | driver = sqlite3.sqlite3() 144 | db = driver:connect(DATABASE_FILE) 145 | 146 | res = db:execute[[ 147 | CREATE TABLE messages ( 148 | `id` INTEGER PRIMARY KEY, 149 | `from` TEXT, 150 | `to` TEXT, 151 | `text` TEXT, 152 | `out` INTEGER, 153 | `date` INTEGER, 154 | `message_id` INTEGER, 155 | `media` TEXT, 156 | `media_type` TEXT, 157 | `url` TEXT 158 | ); 159 | ]] 160 | 161 | res = db:execute[[ 162 | CREATE TABLE users ( 163 | `id` INTEGER PRIMARY KEY, 164 | `name` TEXT, 165 | `username` TEXT, 166 | `phone` TEXT 167 | ); 168 | ]] 169 | else 170 | error("Please choose type csv or sqlite") 171 | end 172 | res = get_dialog_list(dialogs_cb, contacts_extra) 173 | end 174 | 175 | function on_msg_receive (msg) 176 | end 177 | 178 | function on_our_id (id) 179 | end 180 | 181 | function on_secret_chat_created (peer) 182 | end 183 | 184 | function on_user_update (user) 185 | end 186 | 187 | function on_chat_update (user) 188 | end 189 | 190 | function on_get_difference_end () 191 | end 192 | --------------------------------------------------------------------------------