├── .gitignore ├── Dockerfile ├── README.md ├── TokenGeneration ├── TokenGen.py ├── forPyrogram.py ├── readme.txt └── requirements.txt ├── app.json ├── bot.py ├── docker-compose.yml ├── go-ul_x64 ├── go-ul_x64.exe ├── heroku.yml ├── modules ├── cache.py ├── ddl.py ├── gdrive.py ├── tg.py └── upload.py ├── requirements.txt └── start.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .venv 106 | env/ 107 | venv/ 108 | ENV/ 109 | env.bak/ 110 | venv.bak/ 111 | 112 | # Spyder project settings 113 | .spyderproject 114 | .spyproject 115 | 116 | # Rope project settings 117 | .ropeproject 118 | 119 | # mkdocs documentation 120 | /site 121 | 122 | # mypy 123 | .mypy_cache/ 124 | .dmypy.json 125 | dmypy.json 126 | 127 | # Pyre type checker 128 | .pyre/ 129 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | COPY . . 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | RUN apt-get update -y 5 | RUN apt-get upgrade -y 6 | RUN apt-get -y install python3-pip curl 7 | RUN pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib pycryptodomex pillow pyrogram tgcrypto pycryptodomex python-dotenv 8 | RUN chmod +x start.sh 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MultiUpload 2 | Upload your files to 10+ free hosting services. 3 | #### Supported hosts: 4 | ``` 5 | +----+-------------+--------+ 6 | | # | Host | Limit | 7 | +====+=============+========+ 8 | | 1 | anonfiles | 20 GB | 9 | +----+-------------+--------+ 10 | | 2 | Catbox | 200 MB | 11 | +----+-------------+--------+ 12 | | 3 | file.io | 2 GB | 13 | +----+-------------+--------+ 14 | | 4 | Filemail | 5 GB | 15 | +----+-------------+--------+ 16 | | 5 | Gofile | unlim | 17 | +----+-------------+--------+ 18 | | 6 | KrakenFiles | 1 GB | 19 | +----+-------------+--------+ 20 | | 7 | LetsUpload | 10 GB | 21 | +----+-------------+--------+ 22 | | 8 | MegaUp | 5 GB | 23 | +----+-------------+--------+ 24 | | 9 | MixDrop | unlim | 25 | +----+-------------+--------+ 26 | | 10 | pixeldrain | 10 GB | 27 | +----+-------------+--------+ 28 | | 11 | Racaty | 10 GB | 29 | +----+-------------+--------+ 30 | | 12 | transfer.sh | unlim | 31 | +----+-------------+--------+ 32 | | 13 | Uguu | 128 MB | 33 | +----+-------------+--------+ 34 | | 14 | WeTransfer | 2 GB | 35 | +----+-------------+--------+ 36 | | 15 | workupload | 2 GB | 37 | +----+-------------+--------+ 38 | | 16 | zippyshare | 500 MB | 39 | +----+-------------+--------+ 40 | ``` 41 | #### Setup procedure: 42 | 43 | - Follow [this guide](https://www.iperiusbackup.net/en/how-to-enable-google-drive-api-and-get-client-credentials/), but at the last step instead of selecting application as `web application` use `desktop app` 44 | 45 | 46 | 47 | - Download the json and save it as `credentials.json`. 48 | - `git clone https://github.com/bunnykek/MultiUpload` 49 | - Navigate into the `TokenGeneration` directory and follow the [readme.txt](https://github.com/bunnykek/MultiUpload/blob/main/TokenGeneration/readme.txt) for further procedure. 50 | - Deploy to heroku 51 | #### Heroku Deploy 52 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/bunnykek/MultiUpload) 53 | #### VPS Deploy 54 | - `pip install -r requirements.txt` 55 | - Create a `.env` file with the following variables, 56 | ``` 57 | authorized_list = "[111111111]" 58 | bot_token = "12344556:AAAAAAABBBBBBBBBCCCCCCC" 59 | api_id = "123455" 60 | api_hash = "aaaabbbbbaaaaaddddddd" 61 | default_host_id = "12" 62 | token_json = '{"token": "XXXXXXXXXXXXXXXXXX", "refresh_token": "YYYYYYYYYYYYYYYYYY", "token_uri": "ZZZZZZZZZZZZZZZZZZ", "client_id": "XXXXXXXXXXXXXXXxx", "client_secret": "ZZZZZZZZZZZZZZZZZ", "scopes": ["https://www.googleapis.com/auth/drive.readonly"], "expiry": "2023-06-22T21:35:06.155593Z"}' 63 | ``` 64 | - `py bot.py` 65 | #### Bot commands: 66 | - `/help` - Helps 67 | - `/stats` - Shows the total downloaded cache size. 68 | - `/clear` - Wipes the whole cache. 69 | #### Credits 70 | - [go-upload](https://github.com/Sorrow446/go-upload) by @Sorrow446 71 | ##### Make sure to star my projects if you enjoy. Thanks 72 | -------------------------------------------------------------------------------- /TokenGeneration/TokenGen.py: -------------------------------------------------------------------------------- 1 | #pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib 2 | 3 | import os.path 4 | from google_auth_oauthlib.flow import InstalledAppFlow 5 | 6 | # If modifying these scopes, delete the file token.json. 7 | SCOPES = ['https://www.googleapis.com/auth/drive.readonly'] 8 | 9 | 10 | def main(): 11 | creds = None 12 | if not os.path.exists('token.json'): 13 | flow = InstalledAppFlow.from_client_secrets_file( 14 | 'credentials.json', SCOPES) 15 | creds = flow.run_local_server(port=0) 16 | # Save the credentials for the next run 17 | with open('token.json', 'w') as token: 18 | token.write(creds.to_json()) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /TokenGeneration/forPyrogram.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from pyrogram import Client 3 | 4 | 5 | #get your api_id and api_hash from https://my.telegram.org/apps 6 | api_id = 12345 # your api_id 7 | api_hash = "0123456789abcdef0123456789abcdef" # your api_hash 8 | 9 | 10 | async def main(): 11 | async with Client("my_account", api_id, api_hash) as app: 12 | await app.send_message("me", "Greetings from **Pyrogram**!") 13 | 14 | 15 | asyncio.run(main()) -------------------------------------------------------------------------------- /TokenGeneration/readme.txt: -------------------------------------------------------------------------------- 1 | step 1. Put the credentials.json file in this folder. 2 | step 2. pip install -r requirements.txt (run in cmd/terminal) 3 | step 3. python TokenGen.py (run in cmd/terminal follow the prompted procedure) 4 | step 4. You will see that a "token.json" file has been created. We need its contents. 5 | step 5. open forPyrogram.py file using notepad/IDE and replace the api_id and api_hash with yours and save it. (get it from https://my.telegram.org/apps) 6 | step 6. python forPyrogram.py (run in cmd/terminal) 7 | step 7. Follow the procedure shown by pyrogram, thats it, No need of it further. 8 | 9 | use 'pip3' if 'pip' doesnt work for you 10 | use 'py' or 'python3' if "python" doesn't work for you. 11 | -------------------------------------------------------------------------------- /TokenGeneration/requirements.txt: -------------------------------------------------------------------------------- 1 | google-api-python-client 2 | google-auth-httplib2 3 | google-auth-oauthlib 4 | tgcrypto 5 | pyrogram -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MultiUpload", 3 | "description": "Telegram bot to upload your GDrive/Telegram files to 10+ free hosting services.", 4 | "repository": "https://github.com/bunnykek/MultiUpload", 5 | "logo": "https://imgur.com/UsUog2Z.png", 6 | "keywords": [ 7 | "telegram", 8 | "telegram-bot" 9 | ], 10 | "env": { 11 | "authorized_list": { 12 | "description": "List of authorized groups/users id", 13 | "value": "[12312444324, -121324324235, -121324324235]" 14 | }, 15 | "bot_token": { 16 | "description": "Get your bot's token from https://telegram.me/BotFather ", 17 | "value": "XXXXXXXXXX:YYYYYYYYYYYYYYYYYYYYYYYYY" 18 | }, 19 | "api_id": { 20 | "description": "Get your api_id from https://my.telegram.org/apps", 21 | "value": "12345" 22 | }, 23 | "api_hash": { 24 | "description": "Get your api_hash from https://my.telegram.org/apps", 25 | "value": "0123456789abcdef0123456789abcdef" 26 | }, 27 | "default_host_id": { 28 | "description": "Default host id to use", 29 | "value": "12" 30 | }, 31 | "token_json": { 32 | "description": "token.json file contents", 33 | "value": "{...}" 34 | } 35 | }, 36 | "stack": "container" 37 | } 38 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pyrogram import Client, filters 3 | from pyrogram.types import Message 4 | from modules.gdrive import gdriveDownload 5 | from modules.tg import tgDownload 6 | from modules.ddl import ddlDownload, URLRx 7 | from modules.cache import CacheSize, clearCache 8 | import os 9 | from dotenv import load_dotenv 10 | import re 11 | load_dotenv() 12 | 13 | service_id_rx = re.compile("#(\d{1,2})") 14 | authorized_list = json.loads(os.getenv('authorized_list')) 15 | 16 | app = Client("my_account", api_id=os.getenv('api_id'), 17 | api_hash=os.getenv('api_hash'), bot_token=os.getenv('bot_token')) 18 | 19 | help_message = """**Supported upload hosts:**` 20 | +----+-------------+--------+ 21 | | # | Host | Limit | 22 | +====+=============+========+ 23 | | 1 | anonfiles | 20 GB | 24 | +----+-------------+--------+ 25 | | 2 | Catbox | 200 MB | 26 | +----+-------------+--------+ 27 | | 3 | file.io | 2 GB | 28 | +----+-------------+--------+ 29 | | 4 | Filemail | 5 GB | 30 | +----+-------------+--------+ 31 | | 5 | Gofile | unlim | 32 | +----+-------------+--------+ 33 | | 6 | KrakenFiles | 1 GB | 34 | +----+-------------+--------+ 35 | | 7 | LetsUpload | 10 GB | 36 | +----+-------------+--------+ 37 | | 8 | MegaUp | 5 GB | 38 | +----+-------------+--------+ 39 | | 9 | MixDrop | unlim | 40 | +----+-------------+--------+ 41 | | 10 | pixeldrain | 10 GB | 42 | +----+-------------+--------+ 43 | | 11 | Racaty | 10 GB | 44 | +----+-------------+--------+ 45 | | 12 | transfer.sh | unlim | 46 | +----+-------------+--------+ 47 | | 13 | Uguu | 128 MB | 48 | +----+-------------+--------+ 49 | | 14 | WeTransfer | 2 GB | 50 | +----+-------------+--------+ 51 | | 15 | workupload | 2 GB | 52 | +----+-------------+--------+ 53 | | 16 | zippyshare | 500 MB | 54 | +----+-------------+--------+` 55 | 56 | **Supported links: G-Drive url, TG file, DDL** 57 | 58 | ex: Gdrive to anonfiles: 59 | `/up gdrive-url #1` 60 | 61 | ex: TG file to WeTransfer: 62 | reply to a file with `/up #14` 63 | 64 | **Made by [bunny](https://t.me/pseudoboi) 🧪** 65 | """ 66 | if not os.path.exists('Downloads'): 67 | os.makedirs('Downloads') 68 | 69 | print("Bot started", flush=True) 70 | @app.on_message(filters.text & ~filters.channel) 71 | def echo(client, message: Message): 72 | if not message.chat.id in authorized_list: 73 | message.reply_text('**Unauthorized!**') 74 | return 75 | 76 | if '/help' in message.text: 77 | message.reply(help_message, disable_web_page_preview=True, quote=True) 78 | return 79 | try: 80 | if '/up' in message.text: 81 | serviceID = service_id_rx.search(message.text) 82 | if serviceID: 83 | serviceID = int(serviceID.group(1))-1 84 | if serviceID > 15: 85 | message.reply_text("Invalid Host ID") 86 | return 87 | else: 88 | serviceID = int(os.getenv('default_host_id')) - 1 89 | if 'drive.google' in message.text: 90 | progressMessage = message.reply("Please wait while I download your G-Drive file...") 91 | gdriveDownload(message, serviceID, progressMessage) 92 | elif message.reply_to_message or 't.me' in message.text: 93 | print("Telegram...") 94 | progressMessage = message.reply("Please wait while I download your Telegram file...") 95 | tgDownload(app, message, serviceID, progressMessage) 96 | elif URLRx.search(message.text): 97 | print("URl....") 98 | progressMessage = message.reply("Please wait while I download your link...") 99 | ddlDownload(message, serviceID, progressMessage) 100 | elif '/stats' in message.text: 101 | CacheSize(message) 102 | elif '/clear' in message.text: 103 | clearCache(message) 104 | except Exception as e: 105 | print(e, flush=True) 106 | message.reply(e) 107 | return 108 | app.run() 109 | 110 | 111 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.3" 2 | 3 | services: 4 | app: 5 | build: . 6 | command: bash start.sh 7 | restart: on-failure 8 | -------------------------------------------------------------------------------- /go-ul_x64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bunnykek/MultiUpload/79f5e448983028c1ac23867889a370b359ae439a/go-ul_x64 -------------------------------------------------------------------------------- /go-ul_x64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bunnykek/MultiUpload/79f5e448983028c1ac23867889a370b359ae439a/go-ul_x64.exe -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | run: 5 | worker: ./start.sh -------------------------------------------------------------------------------- /modules/cache.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import subprocess 4 | 5 | 6 | def CacheSize(msg): 7 | print("Getting cache size", flush=True) 8 | output = subprocess.check_output(['du','-sh', 'Downloads']).split()[0].decode('utf-8') 9 | msg.reply_text("Cache size: " + output) 10 | 11 | def clearCache(msg): 12 | print("Clearing cache", flush=True) 13 | #delete all files in Downloads folder using linux command 14 | shutil.rmtree('Downloads') 15 | os.makedirs('Downloads') 16 | #subprocess.call(['rm','-rf','Downloads/*']) 17 | msg.reply_text("Cache cleared") 18 | CacheSize(msg) 19 | -------------------------------------------------------------------------------- /modules/ddl.py: -------------------------------------------------------------------------------- 1 | import os 2 | from modules.upload import upload 3 | import subprocess 4 | import re 5 | 6 | 7 | URLRx = re.compile(r"(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])") 8 | 9 | def ddlDownload(message, serviceID, progressMessage): 10 | print("ddlDownload", flush=True) 11 | ddl = URLRx.search(message.text).group(0) 12 | filename = os.path.basename(ddl).split("?")[0] 13 | print(f"Downloading {filename}", flush=True) 14 | filePath = os.path.join("Downloads", filename) 15 | if not os.path.exists(filePath): 16 | subprocess.run(["curl", "-L", ddl, "-o", filePath]) 17 | upload(filePath, serviceID, message, progressMessage) 18 | -------------------------------------------------------------------------------- /modules/gdrive.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | from pyrogram.types import Message 4 | from modules.upload import upload 5 | from google.oauth2.credentials import Credentials 6 | from googleapiclient.discovery import build 7 | from googleapiclient.http import MediaIoBaseDownload 8 | import os 9 | from dotenv import load_dotenv 10 | load_dotenv() 11 | 12 | SCOPES = ['https://www.googleapis.com/auth/drive.readonly'] 13 | gdriveIDRe = re.compile('([-\w]{25,})') 14 | 15 | creds = Credentials.from_authorized_user_info(json.loads(os.getenv('token_json')), SCOPES) 16 | service = build('drive', 'v3', credentials=creds) 17 | 18 | 19 | def gdriveDownload(message: Message, serviceID: int, progressMessage: Message): 20 | fileid = gdriveIDRe.search(message.text).group(1) 21 | 22 | request = service.files().get_media(fileId=fileid) 23 | 24 | metadata = service.files().get(fileId=fileid, fields='name, size, mimeType', 25 | supportsAllDrives=True).execute() 26 | print(metadata, flush=True) 27 | #check if file exists in python 28 | download_path = os.path.join('Downloads', metadata['name']) 29 | if not os.path.exists(download_path): 30 | fh = open(download_path, "wb") 31 | downloader = MediaIoBaseDownload(fh, request) 32 | done = False 33 | while done is False: 34 | status, done = downloader.next_chunk() 35 | progressMessage.edit_text(f"**{metadata['name']}**\n`Downloading... {int(status.progress() * 100)}%`") 36 | print(f"Download %d%%." % int(status.progress() * 100)) 37 | progressMessage.edit_text(f"Downloaded **{metadata['name']}**") 38 | fh.close() 39 | print("Downloaded", flush=True) 40 | upload(download_path, serviceID, message, progressMessage) 41 | #os.remove(metadata['name']) 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /modules/tg.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from pyrogram import Client 4 | from pyrogram.types import Message 5 | from modules.upload import upload 6 | 7 | tgUrlRx = re.compile("https://t\.me/c/(\d+)/(\d+)") 8 | 9 | # def progress(current, total, progressMessage: Message, fileName: str): 10 | # if int(current * 100 / total) % 10 == 0: 11 | # try: 12 | # progressMessage.edit_text(f"Downloading: `{fileName}`\nProgress: `{current * 100 / total:.1f}%`") 13 | # except: 14 | # pass 15 | # print(f"{current * 100 / total:.1f}%", flush=True) 16 | 17 | def tgDownload(client:Client, msg: Message, serviceID: int, progressMessage: Message): 18 | print("processing TG", flush=True) 19 | result = tgUrlRx.search(msg.text) 20 | if result: 21 | chatid = '-100'+result.group(1) 22 | mid = result.group(2) 23 | print("chatid", chatid) 24 | message = client.get_messages(chatid, int(mid)) 25 | else: 26 | message = msg.reply_to_message 27 | print("Got the message object!") 28 | mediaType = message.media.value 29 | if mediaType == 'video': 30 | media = message.video 31 | mime = message.video.mime_type 32 | elif mediaType == 'audio': 33 | media = message.audio 34 | mime = message.audio.mime_type 35 | elif mediaType == 'document': 36 | media = message.document 37 | mime = message.document.mime_type 38 | print(mime) 39 | else: 40 | print("This media type is not supported", flush=True) 41 | raise Exception("This media type is not supported") 42 | 43 | fileName = media.file_name 44 | size = media.file_size 45 | print(fileName, size, flush=True) 46 | 47 | file_path = os.path.join(os.getcwd(), 'Downloads', fileName) 48 | if not os.path.exists(file_path): 49 | progressMessage.edit(f"Downloading: `{fileName}`") 50 | message.download(file_path) #, progress=progress, progress_args=(progressMessage,fileName)) 51 | upload(file_path, serviceID, msg, progressMessage) 52 | #os.remove(fileName) 53 | print("done", flush=True) 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /modules/upload.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from pyrogram.types import Message 4 | import subprocess 5 | 6 | if os.name == 'nt': 7 | APP = "./go-ul_x64.exe" 8 | else: 9 | APP = "./go-ul_x64" 10 | 11 | SERVICES = ['anonfiles', 'catbox', 'fileio', 'filemail', 'gofile', 'krakenfiles', 'letsupload', 'megaup', 12 | 'mixdrop', 'pixeldrain', 'racaty', 'transfersh', 'uguu', 'wetransfer', 'workupload', 'zippyshare'] 13 | 14 | reply_message = """ 15 | **Name :** `{0}` 16 | **Host :** `{1}` 17 | **URL :** {2}""" 18 | 19 | 20 | def upload(filePath: str, serviceID: int, message: Message, progressMessage: Message): 21 | file_name = os.path.basename(filePath) 22 | print(f"Uploading `{file_name}` to {SERVICES[serviceID]}", flush=True) 23 | progressMessage.edit_text( 24 | f"Uploading to {SERVICES[serviceID]}...\n`{file_name}` ") 25 | 26 | subprocess.Popen([APP, SERVICES[serviceID], 27 | '-f', filePath, '-j', 'response.json']).wait() 28 | 29 | response = json.load(open('response.json')) 30 | 31 | if not response['jobs'][-1]['ok']: 32 | message.reply_text(f"`{response['jobs'][-1]['error_text']}`") 33 | raise Exception(f"{response['jobs'][-1]['error_text']}") 34 | else: 35 | message.reply_text(reply_message.format( 36 | response['jobs'][-1]['filename'], SERVICES[serviceID], response['jobs'][-1]['url']), disable_web_page_preview=True, quote=True) 37 | print(f"Uploaded to {SERVICES[serviceID]}", flush=True) 38 | return 0 39 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | google-api-python-client 2 | google-auth-httplib2 3 | google-auth-oauthlib 4 | pycryptodomex 5 | pillow 6 | pyrogram 7 | tgcrypto 8 | pycryptodomex 9 | python-dotenv -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | chmod +x go-ul_x64; python3 bot.py 2 | --------------------------------------------------------------------------------