├── .gitignore ├── Procfile ├── README.md ├── app.json ├── bot └── private.py ├── config.ini ├── main.py ├── requirements.txt └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.session 2 | *.sqlite 3 | static 4 | venv 5 | .idea 6 | *.pyc 7 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 main.py 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archive Bot 2 | 3 | A telegram bot that allows compression of multiple files into a ZIP archive 4 | 5 | 6 | 7 | ## Deploy to Heroku 8 | 9 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/samadii/Archive-bot) 10 | 11 | 12 | ## Deploy locally 13 | 14 | 15 | Clone the repository: 16 | 17 | ``` 18 | git clone https://github.com/samadii/archive-bot.git 19 | ``` 20 | 21 | Install requirements 22 | 23 | ``` 24 | pip3 install -r requirements.txt 25 | ``` 26 | Enter the API_ID and API_HASH and BOT_TOKEN 27 | Example: 28 | ``` 29 | --API_ID >> 1 30 | --API_HASH >> abcdef1234 31 | --BOT_TOKEN >> 1234567890:ABCDEF 32 | ``` 33 | 34 | Run the bot 35 | ``` 36 | python3 main.py 37 | ``` 38 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archiveBot", 3 | "env": { 4 | "BOT_TOKEN": { 5 | "description": "Your bot token, as a string.", 6 | "value": "" 7 | }, 8 | "API_ID": { 9 | "description": "Get this value from https://my.telegram.org", 10 | "value": "" 11 | }, 12 | "API_HASH": { 13 | "description": "Get this value from https://my.telegram.org", 14 | "value": "" 15 | } 16 | }, 17 | "buildpacks": [ 18 | { 19 | "url": "heroku/python" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /bot/private.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters, types 2 | 3 | from zipfile import ZipFile 4 | from os import remove, rmdir, mkdir 5 | 6 | from utils import zip_work, dir_work, up_progress, list_dir, Msg, db_session, User, commit 7 | 8 | 9 | @Client.on_message(filters.command("start")) 10 | def start(_, msg: types.Message): 11 | """ reply start message and add the user to database """ 12 | uid = msg.from_user.id 13 | 14 | with db_session: 15 | if not User.get(uid=uid): 16 | User(uid=uid, status=0) # Initializing the user on database 17 | commit() 18 | 19 | msg.reply(Msg.start(msg)) 20 | 21 | 22 | @Client.on_message(filters.command("zip")) 23 | def start_zip(_, msg: types.Message): 24 | """ starting get files to archive """ 25 | uid = msg.from_user.id 26 | 27 | msg.reply(Msg.zip) 28 | 29 | with db_session: 30 | User.get(uid=uid).status = 1 # change user-status to "INSERT" 31 | commit() 32 | 33 | try: 34 | mkdir(dir_work(uid)) # create static-folder for user 35 | 36 | except FileExistsError: # in case the folder already exist 37 | for file in list_dir(uid): 38 | remove(dir_work(uid) + file) # delete all file from folder 39 | rmdir(dir_work(uid)) # delete folder 40 | mkdir(dir_work(uid)) 41 | 42 | 43 | @Client.on_message(filters.media) 44 | def enter_files(_, msg: types.Message): 45 | """ download files """ 46 | uid = msg.from_user.id 47 | 48 | with db_session: 49 | usr = User.get(uid=uid) 50 | if usr.status == 1: # check if user-status is "INSERT" 51 | 52 | type = msg.document or msg.video or msg.photo or msg.audio 53 | 54 | if type.file_size > 2097152000: 55 | msg.reply(Msg.too_big) 56 | elif len(list_dir(uid)) > 500: 57 | msg.reply(Msg.too_much) 58 | else: 59 | downsts = msg.reply(Msg.downloading, True) # send status-download message 60 | msg.download(dir_work(uid)) 61 | 62 | downsts.delete() # delete status-download message 63 | 64 | else: 65 | msg.reply(Msg.send_zip) # if user-status is not "INSERT" 66 | 67 | 68 | @Client.on_message(filters.command("stopzip")) 69 | def stop_zip(_, msg: types.Message): 70 | """ exit from insert mode and send the archive """ 71 | uid = msg.from_user.id 72 | 73 | if len(msg.command) == 1: 74 | zip_path = zip_work(uid) 75 | else: 76 | zip_path = "static/" + msg.command[1] # costume zip-file name 77 | 78 | with db_session: 79 | usr = User.get(uid=uid) 80 | if usr.status == 1: 81 | usr.status = 0 # change user-status to "NOT-INSERT" 82 | commit() 83 | else: 84 | msg.reply(Msg.send_zip) 85 | return 86 | 87 | stsmsg = msg.reply(Msg.zipping.format(len(list_dir(uid)))) # send status-message "ZIPPING" and count files 88 | 89 | if not list_dir(uid): # if len files is zero 90 | msg.reply(Msg.zero_files) 91 | rmdir(dir_work(uid)) 92 | return 93 | 94 | for file in list_dir(uid): 95 | with ZipFile(zip_path, "a") as zip: 96 | zip.write(f"{dir_work(uid)}/{file}") # add files to zip-archive 97 | remove(f"{dir_work(uid)}{file}") # delete files that added 98 | 99 | stsmsg.edit_text(Msg.uploading) # change status-msg to "UPLOADING" 100 | 101 | try: 102 | msg.reply_document(zip_path, progress=up_progress, # send the zip-archive 103 | progress_args=(stsmsg,)) 104 | except ValueError as e: 105 | msg.reply(Msg.unknow_error.format(str(e))) 106 | 107 | stsmsg.delete() # delete the status-msg 108 | remove(zip_path) # delete the zip-archive 109 | rmdir(dir_work(uid)) # delete the static-folder 110 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [plugins] 2 | root=bot 3 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pyrogram import Client 3 | from os import mkdir 4 | 5 | app_id = int(os.environ.get("API_ID", 12345)) 6 | app_key = os.environ.get('API_HASH') 7 | token = os.environ.get('BOT_TOKEN') 8 | 9 | app = Client("zipBot", app_id, app_key, bot_token=token) 10 | 11 | 12 | if __name__ == '__main__': 13 | 14 | try: 15 | mkdir("static") # create static files folder 16 | except FileExistsError: 17 | pass 18 | 19 | app.run() 20 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | async-lru==1.0.2 2 | pony==0.7.14 3 | pyaes==1.6.1 4 | Pyrogram==1.2.9 5 | PySocks==1.7.1 6 | TgCrypto==1.2.2 7 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from pony.orm import * 2 | from pyrogram.types import Message 3 | from os import listdir 4 | 5 | # ========= DB build ========= 6 | db = Database() 7 | 8 | 9 | class User(db.Entity): 10 | uid = PrimaryKey(int, auto=True) 11 | status = Required(int) # status-user: "INSERT"/"NOT-INSERT" 12 | 13 | 14 | db.bind(provider='sqlite', filename='zipbot.sqlite', create_db=True) 15 | db.generate_mapping(create_tables=True) 16 | 17 | 18 | # ========= helping func ========= 19 | def dir_work(uid: int) -> str: 20 | """ static-user folder """ 21 | return f"static/{uid}/" 22 | 23 | 24 | def zip_work(uid: int) -> str: 25 | """ zip-archive file """ 26 | return f'static/{uid}.zip' 27 | 28 | 29 | def list_dir(uid: int) -> list: 30 | """ items in static-user folder """ 31 | return listdir(dir_work(uid)) 32 | 33 | 34 | def up_progress(current, total, msg: Message): 35 | """ edit status-msg with progress of the uploading """ 36 | msg.edit(f"**Upload progress: {current * 100 / total:.1f}%**") 37 | 38 | 39 | # ========= MSG class ========= 40 | class Msg: 41 | 42 | def start(msg: Message) -> str: 43 | """ return start-message text """ 44 | txt = f"Hey {msg.from_user.mention}!\n" \ 45 | "\nI can compress files in to an archive." \ 46 | "\nJust send /zip, and follow the instructions." 47 | return txt 48 | 49 | zip = "Send the files you want to compress, and at the end send /stopzip after all the files have been downloaded.\n" \ 50 | "\n\nNote: due to upload limit, the total size of the file(s) can be at most 2GB." 51 | too_big = "Note: due to upload limit, the total size of the file(s) can be at most 2GB." 52 | too_much = "Note: the total number of the files can be at most 500" 53 | send_zip = "Send /zip to compress the files" 54 | zipping = "start compressing {} files..." 55 | uploading = "uploading archive..." 56 | unknow_error = "An unknown error occurred" 57 | downloading = "downloading..." 58 | zero_files = "No files were sent" 59 | --------------------------------------------------------------------------------