├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── .netrc ├── .pre-commit-config.yaml ├── Dockerfile ├── LICENSE ├── README.md ├── add_to_team_drive.py ├── aria.bat ├── aria.sh ├── bot ├── __init__.py ├── __main__.py ├── helper │ ├── __init__.py │ ├── ext_utils │ │ ├── __init__.py │ │ ├── bot_utils.py │ │ ├── db_handler.py │ │ ├── exceptions.py │ │ ├── fs_utils.py │ │ └── telegraph_helper.py │ ├── mirror_utils │ │ ├── __init__.py │ │ ├── download_utils │ │ │ ├── __init__.py │ │ │ ├── aria2_download.py │ │ │ ├── direct_link_generator.py │ │ │ ├── direct_link_generator_license.md │ │ │ ├── download_helper.py │ │ │ ├── mega_download.py │ │ │ ├── telegram_downloader.py │ │ │ └── youtube_dl_download_helper.py │ │ ├── status_utils │ │ │ ├── __init__.py │ │ │ ├── aria_download_status.py │ │ │ ├── clone_status.py │ │ │ ├── extract_status.py │ │ │ ├── gdownload_status.py │ │ │ ├── listeners.py │ │ │ ├── mega_status.py │ │ │ ├── split_status.py │ │ │ ├── status.py │ │ │ ├── tar_status.py │ │ │ ├── telegram_download_status.py │ │ │ ├── tg_upload_status.py │ │ │ ├── upload_status.py │ │ │ └── youtube_dl_download_status.py │ │ └── upload_utils │ │ │ ├── __init__.py │ │ │ ├── gdriveTools.py │ │ │ └── pyrogramEngine.py │ └── telegram_helper │ │ ├── __init__.py │ │ ├── bot_commands.py │ │ ├── button_build.py │ │ ├── filters.py │ │ └── message_utils.py └── modules │ ├── __init__.py │ ├── authorize.py │ ├── cancel_mirror.py │ ├── clone.py │ ├── count.py │ ├── delete.py │ ├── leech_settings.py │ ├── list.py │ ├── mirror.py │ ├── mirror_status.py │ ├── speedtest.py │ └── watch.py ├── captain-definition ├── config_sample.env ├── drive_folder ├── driveid.py ├── extract ├── gen_sa_accounts.py ├── generate_drive_token.py ├── heroku.yml ├── heroku_push.sh ├── pextract ├── requirements.txt └── start.sh /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Host On Heroku 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | 7 | build: 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | 13 | - uses: actions/checkout@v2 14 | 15 | - uses: akhileshns/heroku-deploy@v3.12.12 16 | 17 | with: 18 | 19 | region: "us" 20 | 21 | branch: "master" 22 | 23 | usedocker: true 24 | 25 | stack: "container" 26 | 27 | heroku_api_key: ${{secrets.HEROKU_API_KEY}} 28 | heroku_app_name: ${{secrets.HEROKU_APP_NAME}} 29 | heroku_email: ${{secrets.HEROKU_EMAIL}} 30 | 31 | docker_heroku_process_type: worker 32 | env: 33 | HD_CONFIG_FILE_URL: ${{secrets.CONFIG_FILE_URL}} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.env 2 | *auth_token.txt 3 | *.pyc 4 | downloads/* 5 | download/* 6 | data* 7 | .vscode 8 | .idea 9 | *.json 10 | *.pickle 11 | authorized_chats.txt 12 | log.txt 13 | accounts/* 14 | venv/ 15 | .vim/ 16 | drive_folder 17 | drive_folder 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/cmrudl"] 2 | path = vendor/cmrudl 3 | url = https://github.com/JrMasterModelBuilder/cmrudl.py.git 4 | -------------------------------------------------------------------------------- /.netrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harshpreets63/Mirror-Bot/08cd0fc9ecb8460d964e8970cfa8a1268f6faade/.netrc -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.0.1 4 | hooks: 5 | - id: check-ast 6 | - id: check-merge-conflict 7 | - id: detect-private-key 8 | - id: end-of-file-fixer 9 | - id: mixed-line-ending 10 | - id: requirements-txt-fixer 11 | - id: trailing-whitespace 12 | - repo: https://github.com/asottile/pyupgrade 13 | rev: v2.26.0 14 | hooks: 15 | - id: pyupgrade 16 | args: [--py38-plus] 17 | - repo: https://github.com/myint/autoflake 18 | rev: v1.4 19 | hooks: 20 | - id: autoflake 21 | args: 22 | [ 23 | "--in-place", 24 | "--ignore-init-module-imports", 25 | "--remove-unused-variables", 26 | "--remove-all-unused-imports", 27 | "--expand-star-imports", 28 | ] 29 | - repo: https://github.com/pycqa/isort 30 | rev: 5.9.3 31 | hooks: 32 | - id: isort 33 | args: ["--profile", "black", "--filter-files"] 34 | - repo: "https://github.com/psf/black" 35 | rev: 21.9b0 36 | hooks: 37 | - id: black 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM harshpreets63/random:simple 2 | 3 | WORKDIR /usr/src/app 4 | COPY . . 5 | 6 | RUN set -ex \ 7 | && chmod 777 /usr/src/app \ 8 | && cp .netrc /root/.netrc \ 9 | && chmod 600 /usr/src/app/.netrc \ 10 | && cp extract pextract /usr/local/bin \ 11 | && chmod +x aria.sh /usr/local/bin/extract /usr/local/bin/pextract 12 | RUN pip3 install -U pyrogram 13 | CMD ["bash", "start.sh"] 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://t.me/HarshMirrorRepo) 2 | 3 | # Important - Read these points first 4 | 5 | - Original repo is https://github.com/lzzy12/python-aria-mirror-bot 6 | - I have collected some cool features from various repositories and merged them 7 | in one. 8 | - So, credits goes to original repo holder, not to me. I have just collected 9 | them. 10 | - This (or any custom) repo is not supported in official bot support group. 11 | - So if you have any issue then check first that issue is in official repo or 12 | not, You are only allowed to report that issue in bot support group if that 13 | issue is also present in official repo. 14 | 15 | # Features supported: 16 | 17 | - Mirroring direct download links to google drive 18 | - Mirroring Mega.nz links to google drive (In development stage) 19 | - Mirror Telegram files to google drive 20 | - Mirror all youtube-dl supported links 21 | - Mirror Torrent Files and magnet links 22 | - Mirror By Reply 23 | - Custom filename support in direct link, telegram files, YT-DL links 24 | - Extract these filetypes and uploads to google drive 25 | > ZIP, RAR, TAR, 7z, ISO, WIM, CAB, GZIP, BZIP2, APM, ARJ, CHM, CPIO, CramFS, 26 | > DEB, DMG, FAT, HFS, LZH, LZMA, LZMA2, MBR, MSI, MSLZ, NSIS, NTFS, RPM, 27 | > SquashFS, UDF, VHD, XAR, Z. 28 | - Copy files from someone's drive to your drive (using Autorclone) 29 | - Service account support in cloning and uploading 30 | - Download progress 31 | - Upload progress 32 | - Download/upload speeds and ETAs 33 | - Docker support 34 | - Uploading To Team Drives. 35 | - Index Link support 36 | - Shortener support 37 | - View Index Links To Steam Videos Or Music Online (Works With Bhadoo) 38 | - Leech Files To Telegram Supported. 39 | - Clone Status. 40 | - Multi Drive Search. 41 | - SpeedTest. 42 | - Count Drive Files. 43 | - Extract password protected files (It's not hack, you have to enter password 44 | for extracting. LOL) 45 | 46 | - For extracting password protected files, using custom filename and download 47 | from password protected index links see these examples :- 48 | > https://telegra.ph/Magneto-Python-Aria---Custom-Filename-Examples-01-20 49 | 50 | # Multi Search IDs 51 | To use list from multi TD/folder. Run driveid.py in your terminal and follow it. It will generate **drive_folder** file or u can simply create `drive_folder` file in working directory and fill it, check below format: 52 | ``` 53 | MyTdName tdID IndexLink(if available) 54 | MyTdName2 tdID IndexLink(if available) 55 | ``` 56 | Turn On RECURSIVE_SEARCH In Config -RECURSIVE_SEARCH = "True" 57 | 58 | ## Credits :- 59 | 60 | - First of all, full credit goes to 61 | [Shivam Jha aka lzzy12](https://github.com/lzzy12) and 62 | [JaskaranSM aka Zero Cool](https://github.com/jaskaranSM) They build up this 63 | bot from scratch. 64 | - Then a huge thanks to [Sreeraj V R](https://github.com/SVR666) You can 65 | checkout his [repo here](https://github.com/SVR666/LoaderX-Bot) 66 | - Features added from [Sreeraj V R's](https://github.com/SVR666) repo 67 | And Anas [repo here](https://github.com/breakdowns/slam-mirrorbot) Repo (Archived) 68 | - Thanks To Ken For Base Repo 69 | checkout his [repo here](https://github.com/KenHV/Mirror-Bot) 70 | 71 | 72 | ```` 73 | 1. Added Inline Buttons 74 | 2. Added /del command to delete files from drive 75 | 3. /list module will post search result on telegra.ph ``` 76 | ```` 77 | 78 | - Special thanks to [archie](https://github.com/archie9211) for very much useful 79 | feature **Unzipmirror** 80 | 81 | ```` 82 | 1. unzipmirror 83 | 2. Update tracker list dynamically 84 | 3. Fix SSL handsake error ``` 85 | ```` 86 | 87 | # What is this repo about? 88 | 89 | This is a telegram bot writen in python for mirroring files on the internet to 90 | our beloved Google Drive. 91 | 92 | # Inspiration 93 | 94 | This project is heavily inspired from @out386 's telegram bot which is written 95 | in JS. 96 | 97 | ## Deploying on Heroku 98 | - Guide For Deploying on Heroku 99 |
100 | 101 | # Contact Me 102 | - [Telegram](https://T.me/HarshMirrorRepo) 103 | 104 | ## Generate Database 105 |{download.name()}
"
129 | msg += f"\nStatus: {download.status()}"
130 | if download.status() != MirrorStatus.STATUS_ARCHIVING and download.status() != MirrorStatus.STATUS_EXTRACTING and download.status() != MirrorStatus.STATUS_SPLITTING:
131 | msg += f"\n{get_progress_bar_string(download)} {download.progress()}
"
132 | if download.status() == MirrorStatus.STATUS_DOWNLOADING:
133 | msg += f"\nDownloaded: {get_readable_file_size(download.processed_bytes())} of {download.size()}"
134 | elif download.status() == MirrorStatus.STATUS_CLONING:
135 | msg += f"\nCloned: {get_readable_file_size(download.processed_bytes())} of {download.size()}"
136 | else:
137 | msg += f"\nUploaded: {get_readable_file_size(download.processed_bytes())} of {download.size()}"
138 | msg += f"\nSpeed: {download.speed()}" \
139 | f", ETA: {download.eta()} "
140 | # if hasattr(download, 'is_torrent'):
141 | try:
142 | msg += f"\nSeeders: {download.aria_download().num_seeders}" \
143 | f" | Peers: {download.aria_download().connections}"
144 | except:
145 | pass
146 | try:
147 | msg += f"\nSeeders: {download.torrent_info().num_seeds}" \
148 | f" | Leechers: {download.torrent_info().num_leechs}"
149 | except:
150 | pass
151 | msg += f'\nUser: {download.message.from_user.first_name} ➡️{download.message.from_user.id}
'
152 | msg += f"\nTo Stop: /{BotCommands.CancelMirror} {download.gid()}
"
153 | msg += "\n\n"
154 | return msg
155 |
156 | def get_readable_time(seconds: int) -> str:
157 | result = ""
158 | (days, remainder) = divmod(seconds, 86400)
159 | days = int(days)
160 | if days != 0:
161 | result += f"{days}d"
162 | (hours, remainder) = divmod(remainder, 3600)
163 | hours = int(hours)
164 | if hours != 0:
165 | result += f"{hours}h"
166 | (minutes, seconds) = divmod(remainder, 60)
167 | minutes = int(minutes)
168 | if minutes != 0:
169 | result += f"{minutes}m"
170 | seconds = int(seconds)
171 | result += f"{seconds}s"
172 | return result
173 |
174 |
175 | def is_gdrive_link(url: str):
176 | return "drive.google.com" in url
177 |
178 | def is_gdtot_link(url: str):
179 | url = re.match(r'https?://.*\.gdtot\.\S+', url)
180 | return bool(url)
181 |
182 | def is_mega_link(url: str):
183 | return "mega.nz" in url or "mega.co.nz" in url
184 |
185 |
186 | def is_url(url: str):
187 | url = re.findall(URL_REGEX, url)
188 | return bool(url)
189 |
190 |
191 | def is_magnet(url: str):
192 | magnet = re.findall(MAGNET_REGEX, url)
193 | return bool(magnet)
194 |
195 |
196 | def new_thread(fn):
197 | """To use as decorator to make a function call threaded.
198 | Needs import
199 | from threading import Thread"""
200 |
201 | def wrapper(*args, **kwargs):
202 | thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
203 | thread.start()
204 | return thread
205 |
206 | return wrapper
207 |
--------------------------------------------------------------------------------
/bot/helper/ext_utils/db_handler.py:
--------------------------------------------------------------------------------
1 | import psycopg2
2 | from psycopg2 import Error
3 | from bot import AUTHORIZED_CHATS, SUDO_USERS, DB_URI, LOGGER
4 |
5 | class DbManger:
6 | def __init__(self):
7 | self.err = False
8 |
9 | def connect(self):
10 | try:
11 | self.conn = psycopg2.connect(DB_URI)
12 | self.cur = self.conn.cursor()
13 | except psycopg2.DatabaseError as error :
14 | LOGGER.error("Error in dbMang : ", error)
15 | self.err = True
16 |
17 | def disconnect(self):
18 | self.cur.close()
19 | self.conn.close()
20 |
21 | def db_auth(self,chat_id: int):
22 | self.connect()
23 | if self.err:
24 | return "There's some error check log for details"
25 | sql = 'INSERT INTO users VALUES ({});'.format(chat_id)
26 | self.cur.execute(sql)
27 | self.conn.commit()
28 | self.disconnect()
29 | AUTHORIZED_CHATS.add(chat_id)
30 | return 'Authorized successfully'
31 |
32 | def db_unauth(self,chat_id: int):
33 | self.connect()
34 | if self.err:
35 | return "There's some error check log for details"
36 | sql = 'DELETE from users where uid = {};'.format(chat_id)
37 | self.cur.execute(sql)
38 | self.conn.commit()
39 | self.disconnect()
40 | AUTHORIZED_CHATS.remove(chat_id)
41 | return 'Unauthorized successfully'
42 |
43 | def db_addsudo(self,chat_id: int):
44 | self.connect()
45 | if self.err:
46 | return "There's some error check log for details"
47 | if chat_id in AUTHORIZED_CHATS:
48 | sql = 'UPDATE users SET sudo = TRUE where uid = {};'.format(chat_id)
49 | self.cur.execute(sql)
50 | self.conn.commit()
51 | self.disconnect()
52 | SUDO_USERS.add(chat_id)
53 | return 'Successfully promoted as Sudo'
54 | else:
55 | sql = 'INSERT INTO users VALUES ({},TRUE);'.format(chat_id)
56 | self.cur.execute(sql)
57 | self.conn.commit()
58 | self.disconnect()
59 | SUDO_USERS.add(chat_id)
60 | return 'Successfully Authorized and promoted as Sudo'
61 |
62 | def db_rmsudo(self,chat_id: int):
63 | self.connect()
64 | if self.err:
65 | return "There's some error check log for details"
66 | sql = 'UPDATE users SET sudo = FALSE where uid = {};'.format(chat_id)
67 | self.cur.execute(sql)
68 | self.conn.commit()
69 | self.disconnect()
70 | SUDO_USERS.remove(chat_id)
71 | return 'Successfully removed from Sudo'
--------------------------------------------------------------------------------
/bot/helper/ext_utils/exceptions.py:
--------------------------------------------------------------------------------
1 | class DirectDownloadLinkException(Exception):
2 | """Not method found for extracting direct download link from the http link"""
3 |
4 |
5 | class NotSupportedExtractionArchive(Exception):
6 | """The archive format use is trying to extract is not supported"""
7 |
--------------------------------------------------------------------------------
/bot/helper/ext_utils/fs_utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import sys
4 | import pathlib
5 | import tarfile
6 | import magic
7 |
8 | from bot import DOWNLOAD_DIR, LOGGER, aria2
9 | import subprocess
10 | import time
11 |
12 | from PIL import Image
13 | from hachoir.parser import createParser
14 | from hachoir.metadata import extractMetadata
15 | from fsplit.filesplit import Filesplit
16 | from bot import aria2, LOGGER, DOWNLOAD_DIR, TG_SPLIT_SIZE
17 |
18 | VIDEO_SUFFIXES = ("M4V", "MP4", "MOV", "FLV", "WMV", "3GP", "MPG", "WEBM", "MKV", "AVI")
19 |
20 | fs = Filesplit()
21 | from .exceptions import NotSupportedExtractionArchive
22 |
23 |
24 | def clean_download(path: str):
25 | if os.path.exists(path):
26 | LOGGER.info(f"Cleaning download: {path}")
27 | shutil.rmtree(path)
28 |
29 |
30 | def start_cleanup():
31 | try:
32 | shutil.rmtree(DOWNLOAD_DIR)
33 | except FileNotFoundError:
34 | pass
35 |
36 |
37 | def clean_all():
38 | aria2.remove_all(True)
39 | try:
40 | shutil.rmtree(DOWNLOAD_DIR)
41 | except FileNotFoundError:
42 | pass
43 |
44 |
45 | def exit_clean_up(signal, frame):
46 | try:
47 | LOGGER.info(
48 | "Please wait, while we clean up the downloads and stop running downloads"
49 | )
50 | clean_all()
51 | sys.exit(0)
52 | except KeyboardInterrupt:
53 | LOGGER.warning("Force Exiting before the cleanup finishes!")
54 | sys.exit(1)
55 |
56 |
57 | def get_path_size(path):
58 | if os.path.isfile(path):
59 | return os.path.getsize(path)
60 | total_size = 0
61 | for root, dirs, files in os.walk(path):
62 | for f in files:
63 | abs_path = os.path.join(root, f)
64 | total_size += os.path.getsize(abs_path)
65 | return total_size
66 |
67 | def tar(org_path):
68 | tar_path = org_path + ".tar"
69 | path = pathlib.PurePath(org_path)
70 | LOGGER.info(f"Tar: orig_path: {org_path}, tar_path: {tar_path}")
71 | tar = tarfile.open(tar_path, "w")
72 | tar.add(org_path, arcname=path.name)
73 | tar.close()
74 | return tar_path
75 |
76 | def get_base_name(orig_path: str):
77 | if orig_path.endswith(".tar.bz2"):
78 | return orig_path.replace(".tar.bz2", "")
79 | elif orig_path.endswith(".tar.gz"):
80 | return orig_path.replace(".tar.gz", "")
81 | elif orig_path.endswith(".bz2"):
82 | return orig_path.replace(".bz2", "")
83 | elif orig_path.endswith(".gz"):
84 | return orig_path.replace(".gz", "")
85 | elif orig_path.endswith(".tar.xz"):
86 | return orig_path.replace(".tar.xz", "")
87 | elif orig_path.endswith(".tar"):
88 | return orig_path.replace(".tar", "")
89 | elif orig_path.endswith(".tbz2"):
90 | return orig_path.replace("tbz2", "")
91 | elif orig_path.endswith(".tgz"):
92 | return orig_path.replace(".tgz", "")
93 | elif orig_path.endswith(".zip"):
94 | return orig_path.replace(".zip", "")
95 | elif orig_path.endswith(".7z"):
96 | return orig_path.replace(".7z", "")
97 | elif orig_path.endswith(".Z"):
98 | return orig_path.replace(".Z", "")
99 | elif orig_path.endswith(".rar"):
100 | return orig_path.replace(".rar", "")
101 | elif orig_path.endswith(".iso"):
102 | return orig_path.replace(".iso", "")
103 | elif orig_path.endswith(".wim"):
104 | return orig_path.replace(".wim", "")
105 | elif orig_path.endswith(".cab"):
106 | return orig_path.replace(".cab", "")
107 | elif orig_path.endswith(".apm"):
108 | return orig_path.replace(".apm", "")
109 | elif orig_path.endswith(".arj"):
110 | return orig_path.replace(".arj", "")
111 | elif orig_path.endswith(".chm"):
112 | return orig_path.replace(".chm", "")
113 | elif orig_path.endswith(".cpio"):
114 | return orig_path.replace(".cpio", "")
115 | elif orig_path.endswith(".cramfs"):
116 | return orig_path.replace(".cramfs", "")
117 | elif orig_path.endswith(".deb"):
118 | return orig_path.replace(".deb", "")
119 | elif orig_path.endswith(".dmg"):
120 | return orig_path.replace(".dmg", "")
121 | elif orig_path.endswith(".fat"):
122 | return orig_path.replace(".fat", "")
123 | elif orig_path.endswith(".hfs"):
124 | return orig_path.replace(".hfs", "")
125 | elif orig_path.endswith(".lzh"):
126 | return orig_path.replace(".lzh", "")
127 | elif orig_path.endswith(".lzma"):
128 | return orig_path.replace(".lzma", "")
129 | elif orig_path.endswith(".lzma2"):
130 | return orig_path.replace(".lzma2", "")
131 | elif orig_path.endswith(".mbr"):
132 | return orig_path.replace(".mbr", "")
133 | elif orig_path.endswith(".msi"):
134 | return orig_path.replace(".msi", "")
135 | elif orig_path.endswith(".mslz"):
136 | return orig_path.replace(".mslz", "")
137 | elif orig_path.endswith(".nsis"):
138 | return orig_path.replace(".nsis", "")
139 | elif orig_path.endswith(".ntfs"):
140 | return orig_path.replace(".ntfs", "")
141 | elif orig_path.endswith(".rpm"):
142 | return orig_path.replace(".rpm", "")
143 | elif orig_path.endswith(".squashfs"):
144 | return orig_path.replace(".squashfs", "")
145 | elif orig_path.endswith(".udf"):
146 | return orig_path.replace(".udf", "")
147 | elif orig_path.endswith(".vhd"):
148 | return orig_path.replace(".vhd", "")
149 | elif orig_path.endswith(".xar"):
150 | return orig_path.replace(".xar", "")
151 | else:
152 | raise NotSupportedExtractionArchive("File format not supported for extraction")
153 |
154 |
155 | def get_mime_type(file_path):
156 | mime = magic.Magic(mime=True)
157 | mime_type = mime.from_file(file_path)
158 | mime_type = mime_type or "text/plain"
159 | return mime_type
160 | def take_ss(video_file):
161 | des_dir = 'Thumbnails'
162 | if not os.path.exists(des_dir):
163 | os.mkdir(des_dir)
164 | des_dir = os.path.join(des_dir, f"{time.time()}.jpg")
165 | metadata = extractMetadata(createParser(video_file))
166 | duration = metadata.get('duration').seconds if metadata.has("duration") else 5
167 | duration = int(duration) / 2
168 | subprocess.run(["ffmpeg", "-hide_banner", "-loglevel", "error", "-ss", str(duration),
169 | "-i", video_file, "-vframes", "1", des_dir])
170 | if not os.path.lexists(des_dir):
171 | return None
172 |
173 | Image.open(des_dir).convert("RGB").save(des_dir)
174 | img = Image.open(des_dir)
175 | img.resize((480, 320))
176 | img.save(des_dir, "JPEG")
177 | return des_dir
178 |
179 | def split(path, size, file, dirpath, split_size, start_time=0, i=1):
180 | if file.upper().endswith(VIDEO_SUFFIXES):
181 | base_name, extension = os.path.splitext(file)
182 | metadata = extractMetadata(createParser(path))
183 | total_duration = metadata.get('duration').seconds - 8
184 | split_size = split_size - 3000000
185 | while start_time < total_duration:
186 | parted_name = "{}.part{}{}".format(str(base_name), str(i).zfill(3), str(extension))
187 | out_path = os.path.join(dirpath, parted_name)
188 | subprocess.run(["ffmpeg", "-hide_banner", "-loglevel", "error", "-i",
189 | path, "-ss", str(start_time), "-fs", str(split_size),
190 | "-strict", "-2", "-c", "copy", out_path])
191 | out_size = get_path_size(out_path)
192 | if out_size > TG_SPLIT_SIZE:
193 | dif = out_size - TG_SPLIT_SIZE
194 | split_size = split_size - dif + 2000000
195 | os.remove(out_path)
196 | return split(path, size, file, dirpath, split_size, start_time, i)
197 | metadata = extractMetadata(createParser(out_path))
198 | start_time = start_time + metadata.get('duration').seconds - 5
199 | i = i + 1
200 | else:
201 | out_path = os.path.join(dirpath, file + ".")
202 | subprocess.run(["split", "--numeric-suffixes=1", "--suffix-length=3", f"--bytes={split_size}", path, out_path])
--------------------------------------------------------------------------------
/bot/helper/ext_utils/telegraph_helper.py:
--------------------------------------------------------------------------------
1 | # Implement By - @VarnaX-279
2 |
3 | import time
4 | import string
5 | import random
6 | import logging
7 |
8 | from telegraph import Telegraph
9 | from telegraph.exceptions import RetryAfterError
10 |
11 | from bot import LOGGER
12 |
13 |
14 | class TelegraphHelper:
15 | def __init__(self, author_name=None, author_url=None):
16 | self.telegraph = Telegraph()
17 | self.short_name = ''.join(random.SystemRandom().choices(string.ascii_letters, k=8))
18 | self.access_token = None
19 | self.author_name = author_name
20 | self.author_url = author_url
21 | self.create_account()
22 |
23 | def create_account(self):
24 | self.telegraph.create_account(
25 | short_name=self.short_name,
26 | author_name=self.author_name,
27 | author_url=self.author_url
28 | )
29 | self.access_token = self.telegraph.get_access_token()
30 | LOGGER.info(f"Creating TELEGRAPH Account using '{self.short_name}' name")
31 |
32 | def create_page(self, title, content):
33 | try:
34 | return self.telegraph.create_page(
35 | title = title,
36 | author_name=self.author_name,
37 | author_url=self.author_url,
38 | html_content=content
39 | )
40 | except RetryAfterError as st:
41 | LOGGER.warning(f'Telegraph Flood control exceeded. I will sleep for {st.retry_after} seconds.')
42 | time.sleep(st.retry_after)
43 | return self.create_page(title, content)
44 |
45 | def edit_page(self, path, title, content):
46 | try:
47 | return self.telegraph.edit_page(
48 | path = path,
49 | title = title,
50 | author_name=self.author_name,
51 | author_url=self.author_url,
52 | html_content=content
53 | )
54 | except RetryAfterError as st:
55 | LOGGER.warning(f'Telegraph Flood control exceeded. I will sleep for {st.retry_after} seconds.')
56 | time.sleep(st.retry_after)
57 | return self.edit_page(path, title, content)
58 |
59 |
60 | telegraph=TelegraphHelper('Harsh Mirror Repo', 'https://github.com/harshpreets63/Mirror-Bot')
61 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshpreets63/Mirror-Bot/08cd0fc9ecb8460d964e8970cfa8a1268f6faade/bot/helper/mirror_utils/__init__.py
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshpreets63/Mirror-Bot/08cd0fc9ecb8460d964e8970cfa8a1268f6faade/bot/helper/mirror_utils/download_utils/__init__.py
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/aria2_download.py:
--------------------------------------------------------------------------------
1 | import threading
2 | from time import sleep
3 |
4 | from aria2p import API
5 |
6 | from bot import aria2, download_dict_lock
7 | from bot.helper.ext_utils.bot_utils import (
8 | LOGGER,
9 | download_dict,
10 | getDownloadByGid,
11 | is_magnet,
12 | new_thread,
13 | )
14 | from bot.helper.mirror_utils.status_utils.aria_download_status import AriaDownloadStatus
15 | from bot.helper.telegram_helper.message_utils import update_all_messages
16 |
17 | from .download_helper import DownloadHelper
18 |
19 |
20 | class AriaDownloadHelper(DownloadHelper):
21 | def __init__(self):
22 | super().__init__()
23 |
24 | @new_thread
25 | def __onDownloadStarted(self, api, gid):
26 | sleep(1)
27 | LOGGER.info(f"onDownloadStart: {gid}")
28 | download = api.get_download(gid)
29 | self.name = download.name
30 | update_all_messages()
31 |
32 | def __onDownloadComplete(self, api: API, gid):
33 | LOGGER.info(f"onDownloadComplete: {gid}")
34 | dl = getDownloadByGid(gid)
35 | download = api.get_download(gid)
36 | if download.followed_by_ids:
37 | new_gid = download.followed_by_ids[0]
38 | new_download = api.get_download(new_gid)
39 | if dl is None:
40 | dl = getDownloadByGid(new_gid)
41 | with download_dict_lock:
42 | download_dict[dl.uid()] = AriaDownloadStatus(new_gid, dl.getListener())
43 | if new_download.is_torrent:
44 | download_dict[dl.uid()].is_torrent = True
45 | update_all_messages()
46 | LOGGER.info(f"Changed gid from {gid} to {new_gid}")
47 | elif dl:
48 | threading.Thread(target=dl.getListener().onDownloadComplete).start()
49 |
50 | @new_thread
51 | def __onDownloadPause(self, api, gid):
52 | LOGGER.info(f"onDownloadPause: {gid}")
53 | dl = getDownloadByGid(gid)
54 | dl.getListener().onDownloadError("Download stopped by user!")
55 |
56 | @new_thread
57 | def __onDownloadStopped(self, api, gid):
58 | LOGGER.info(f"onDownloadStop: {gid}")
59 | dl = getDownloadByGid(gid)
60 | if dl:
61 | dl.getListener().onDownloadError("Dead Torrent!")
62 |
63 | @new_thread
64 | def __onDownloadError(self, api, gid):
65 | sleep(
66 | 0.5
67 | ) # sleep for split second to ensure proper dl gid update from onDownloadComplete
68 | LOGGER.info(f"onDownloadError: {gid}")
69 | dl = getDownloadByGid(gid)
70 | download = api.get_download(gid)
71 | error = download.error_message
72 | LOGGER.info(f"Download Error: {error}")
73 | if dl:
74 | dl.getListener().onDownloadError(error)
75 |
76 | def start_listener(self):
77 | aria2.listen_to_notifications(
78 | threaded=True,
79 | on_download_start=self.__onDownloadStarted,
80 | on_download_error=self.__onDownloadError,
81 | on_download_pause=self.__onDownloadPause,
82 | on_download_stop=self.__onDownloadStopped,
83 | on_download_complete=self.__onDownloadComplete,
84 | )
85 |
86 | def add_download(self, link: str, path, listener, filename):
87 | if is_magnet(link):
88 | download = aria2.add_magnet(link, {"dir": path, "out": filename})
89 | else:
90 | download = aria2.add_uris([link], {"dir": path, "out": filename})
91 | if download.error_message: # no need to proceed further at this point
92 | listener.onDownloadError(download.error_message)
93 | return
94 | with download_dict_lock:
95 | download_dict[listener.uid] = AriaDownloadStatus(download.gid, listener)
96 | LOGGER.info(f"Started: {download.gid} DIR:{download.dir} ")
97 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/direct_link_generator_license.md:
--------------------------------------------------------------------------------
1 | RAPHIELSCAPE PUBLIC LICENSE
2 | Version 1.c, June 2019
3 |
4 | Copyright (C) 2019 Raphielscape LLC.
5 | Copyright (C) 2019 Devscapes Open Source Holding GmbH.
6 |
7 | Everyone is permitted to copy and distribute verbatim or modified
8 | copies of this license document, and changing it is allowed as long
9 | as the name is changed.
10 |
11 | RAPHIELSCAPE PUBLIC LICENSE
12 | A-1. DEFINITIONS
13 |
14 | 0. “This License” refers to version 1.c of the Raphielscape Public License.
15 |
16 | 1. “Copyright” also means copyright-like laws that apply to other kinds of works.
17 |
18 | 2. “The Work" refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”.
19 | “Licensees” and “recipients” may be individuals or organizations.
20 |
21 | 3. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission,
22 | other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work
23 | or a work “based on” the earlier work.
24 |
25 | 4. Source Form. The “source form” for a work means the preferred form of the work for making modifications to it.
26 | “Object code” means any non-source form of a work.
27 |
28 | The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and
29 | (for an executable work) run the object code and to modify the work, including scripts to control those activities.
30 |
31 | The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
32 | The Corresponding Source for a work in source code form is that same work.
33 |
34 | 5. "The author" refers to "author" of the code, which is the one that made the particular code which exists inside of
35 | the Corresponding Source.
36 |
37 | 6. "Owner" refers to any parties which is made the early form of the Corresponding Source.
38 |
39 | A-2. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
40 |
41 | 0. You must give any other recipients of the Work or Derivative Works a copy of this License; and
42 |
43 | 1. You must cause any modified files to carry prominent notices stating that You changed the files; and
44 |
45 | 2. You must retain, in the Source form of any Derivative Works that You distribute,
46 | this license, all copyright, patent, trademark, authorships and attribution notices
47 | from the Source form of the Work; and
48 |
49 | 3. Respecting the author and owner of works that are distributed in any way.
50 |
51 | You may add Your own copyright statement to Your modifications and may provide
52 | additional or different license terms and conditions for use, reproduction,
53 | or distribution of Your modifications, or for any such Derivative Works as a whole,
54 | provided Your use, reproduction, and distribution of the Work otherwise complies
55 | with the conditions stated in this License.
56 |
57 | B. DISCLAIMER OF WARRANTY
58 |
59 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
60 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
61 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
62 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
63 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
64 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
66 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67 |
68 |
69 | C. REVISED VERSION OF THIS LICENSE
70 |
71 | The Devscapes Open Source Holding GmbH. may publish revised and/or new versions of the
72 | Raphielscape Public License from time to time. Such new versions will be similar in spirit
73 | to the present version, but may differ in detail to address new problems or concerns.
74 |
75 | Each version is given a distinguishing version number. If the Program specifies that a
76 | certain numbered version of the Raphielscape Public License "or any later version" applies to it,
77 | you have the option of following the terms and conditions either of that numbered version or of
78 | any later version published by the Devscapes Open Source Holding GmbH. If the Program does not specify a
79 | version number of the Raphielscape Public License, you may choose any version ever published
80 | by the Devscapes Open Source Holding GmbH.
81 |
82 | END OF LICENSE
83 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/download_helper.py:
--------------------------------------------------------------------------------
1 | # An abstract class which will be inherited by the tool specific classes like aria2_helper or mega_download_helper
2 | import threading
3 |
4 |
5 | class MethodNotImplementedError(NotImplementedError):
6 | def __init__(self):
7 | super(self, "Not implemented method")
8 |
9 |
10 | class DownloadHelper:
11 | def __init__(self):
12 | self.__name = (
13 | "" # Name of the download; empty string if no download has been started
14 | )
15 | self.__size = 0.0 # Size of the download
16 | self.downloaded_bytes = 0.0 # Bytes downloaded
17 | self.speed = 0.0 # Download speed in bytes per second
18 | self.progress = 0.0
19 | self.progress_string = "0.00%"
20 | self.eta = 0 # Estimated time of download complete
21 | self.eta_string = "0s" # A listener class which have event callbacks
22 | self._resource_lock = threading.Lock()
23 |
24 | def add_download(self, link: str, path):
25 | raise MethodNotImplementedError
26 |
27 | def cancel_download(self):
28 | # Returns None if successfully cancelled, else error string
29 | raise MethodNotImplementedError
30 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/mega_download.py:
--------------------------------------------------------------------------------
1 | import threading
2 | from pathlib import Path
3 |
4 | from megasdkrestclient import MegaSdkRestClient, constants
5 |
6 | from bot import LOGGER, download_dict, download_dict_lock
7 | from bot.helper.ext_utils.bot_utils import setInterval
8 |
9 | from ..status_utils.mega_status import MegaDownloadStatus
10 |
11 |
12 | class MegaDownloader:
13 | POLLING_INTERVAL = 2
14 |
15 | def __init__(self, listener):
16 | super().__init__()
17 | self.__listener = listener
18 | self.__name = ""
19 | self.__gid = ""
20 | self.__resource_lock = threading.Lock()
21 | self.__mega_client = MegaSdkRestClient("http://localhost:6090")
22 | self.__periodic = None
23 | self.__downloaded_bytes = 0
24 | self.__progress = 0
25 | self.__size = 0
26 |
27 | @property
28 | def progress(self):
29 | with self.__resource_lock:
30 | return self.__progress
31 |
32 | @property
33 | def downloaded_bytes(self):
34 | with self.__resource_lock:
35 | return self.__downloaded_bytes
36 |
37 | @property
38 | def size(self):
39 | with self.__resource_lock:
40 | return self.__size
41 |
42 | @property
43 | def gid(self):
44 | with self.__resource_lock:
45 | return self.__gid
46 |
47 | @property
48 | def name(self):
49 | with self.__resource_lock:
50 | return self.__name
51 |
52 | @property
53 | def download_speed(self):
54 | if self.gid is not None:
55 | return self.__mega_client.getDownloadInfo(self.gid)["speed"]
56 |
57 | def __onDownloadStart(self, name, size, gid):
58 | self.__periodic = setInterval(self.POLLING_INTERVAL, self.__onInterval)
59 | with download_dict_lock:
60 | download_dict[self.__listener.uid] = MegaDownloadStatus(
61 | self, self.__listener
62 | )
63 | with self.__resource_lock:
64 | self.__name = name
65 | self.__size = size
66 | self.__gid = gid
67 | self.__listener.onDownloadStarted()
68 |
69 | def __onInterval(self):
70 | dlInfo = self.__mega_client.getDownloadInfo(self.gid)
71 | if (
72 | dlInfo["state"]
73 | in [
74 | constants.State.TYPE_STATE_COMPLETED,
75 | constants.State.TYPE_STATE_CANCELED,
76 | constants.State.TYPE_STATE_FAILED,
77 | ]
78 | and self.__periodic is not None
79 | ):
80 | self.__periodic.cancel()
81 | if dlInfo["state"] == constants.State.TYPE_STATE_COMPLETED:
82 | self.__onDownloadComplete()
83 | return
84 | if dlInfo["state"] == constants.State.TYPE_STATE_CANCELED:
85 | self.__onDownloadError("Cancelled by user")
86 | return
87 | if dlInfo["state"] == constants.State.TYPE_STATE_FAILED:
88 | self.__onDownloadError(dlInfo["error_string"])
89 | return
90 | self.__onDownloadProgress(dlInfo["completed_length"], dlInfo["total_length"])
91 |
92 | def __onDownloadProgress(self, current, total):
93 | with self.__resource_lock:
94 | self.__downloaded_bytes = current
95 | try:
96 | self.__progress = current / total * 100
97 | except ZeroDivisionError:
98 | self.__progress = 0
99 |
100 | def __onDownloadError(self, error):
101 | self.__listener.onDownloadError(error)
102 |
103 | def __onDownloadComplete(self):
104 | self.__listener.onDownloadComplete()
105 |
106 | def add_download(self, link, path):
107 | Path(path).mkdir(parents=True, exist_ok=True)
108 | dl = self.__mega_client.addDl(link, path)
109 | gid = dl["gid"]
110 | info = self.__mega_client.getDownloadInfo(gid)
111 | file_name = info["name"]
112 | file_size = info["total_length"]
113 | self.__onDownloadStart(file_name, file_size, gid)
114 | LOGGER.info(f"Started mega download with gid: {gid}")
115 |
116 | def cancel_download(self):
117 | LOGGER.info(f"Cancelling download on user request: {self.gid}")
118 | self.__mega_client.cancelDl(self.gid)
119 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/telegram_downloader.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import threading
3 | import time
4 |
5 | from bot import LOGGER, app, download_dict, download_dict_lock
6 |
7 | from ..status_utils.telegram_download_status import TelegramDownloadStatus
8 | from .download_helper import DownloadHelper
9 |
10 | global_lock = threading.Lock()
11 | GLOBAL_GID = set()
12 |
13 | logging.getLogger("pyrogram").setLevel(logging.WARNING)
14 |
15 |
16 | class TelegramDownloadHelper(DownloadHelper):
17 | def __init__(self, listener):
18 | super().__init__()
19 | self.__listener = listener
20 | self.__resource_lock = threading.RLock()
21 | self.__name = ""
22 | self.__gid = ""
23 | self.__start_time = time.time()
24 | self._bot = app
25 | self.__is_cancelled = False
26 |
27 | @property
28 | def gid(self):
29 | with self.__resource_lock:
30 | return self.__gid
31 |
32 | @property
33 | def download_speed(self):
34 | with self.__resource_lock:
35 | return self.downloaded_bytes / (time.time() - self.__start_time)
36 |
37 | def __onDownloadStart(self, name, size, file_id):
38 | with download_dict_lock:
39 | download_dict[self.__listener.uid] = TelegramDownloadStatus(
40 | self, self.__listener
41 | )
42 | with global_lock:
43 | GLOBAL_GID.add(file_id)
44 | with self.__resource_lock:
45 | self.name = name
46 | self.size = size
47 | self.__gid = file_id
48 | self.__listener.onDownloadStarted()
49 |
50 | def __onDownloadProgress(self, current, total):
51 | if self.__is_cancelled:
52 | self.__onDownloadError("Cancelled by user!")
53 | self._bot.stop_transmission()
54 | return
55 | with self.__resource_lock:
56 | self.downloaded_bytes = current
57 | try:
58 | self.progress = current / self.size * 100
59 | except ZeroDivisionError:
60 | self.progress = 0
61 |
62 | def __onDownloadError(self, error):
63 | with global_lock:
64 | try:
65 | GLOBAL_GID.remove(self.gid)
66 | except KeyError:
67 | pass
68 | self.__listener.onDownloadError(error)
69 |
70 | def __onDownloadComplete(self):
71 | with global_lock:
72 | GLOBAL_GID.remove(self.gid)
73 | self.__listener.onDownloadComplete()
74 |
75 | def __download(self, message, path):
76 | try:
77 | download = self._bot.download_media(message,
78 | progress = self.__onDownloadProgress,
79 | file_name = path
80 | )
81 | except Exception as e:
82 | LOGGER.error(str(e))
83 | return self.__onDownloadError(str(e))
84 | if download is not None:
85 | self.__onDownloadComplete()
86 | elif not self.__is_cancelled:
87 | self.__onDownloadError("Internal error occurred")
88 |
89 | def add_download(self, message, path, filename):
90 | _message = self._bot.get_messages(message.chat.id, reply_to_message_ids=message.message_id)
91 | media = None
92 | media_array = [_message.document, _message.video, _message.audio]
93 | for i in media_array:
94 | if i is not None:
95 | media = i
96 | break
97 | if media is not None:
98 | with global_lock:
99 | # For avoiding locking the thread lock for long time unnecessarily
100 | download = media.file_id not in GLOBAL_GID
101 | if filename == "":
102 | name = media.file_name
103 | else:
104 | name = filename
105 | path = path + name
106 | if download:
107 | self.__onDownloadStart(name, media.file_size, media.file_id)
108 | LOGGER.info(f"Downloading telegram file with id: {media.file_id}")
109 | threading.Thread(target=self.__download, args=(_message, path)).start()
110 | else:
111 | self.__onDownloadError("File already being downloaded!")
112 | else:
113 | self.__onDownloadError("No document in the replied message")
114 |
115 | def cancel_download(self):
116 | LOGGER.info(f"Cancelling download on user request: {self.gid}")
117 | self.__is_cancelled = True
118 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/youtube_dl_download_helper.py:
--------------------------------------------------------------------------------
1 | import random
2 | import string
3 | import time
4 | import logging
5 | import re
6 | import threading
7 |
8 | from .download_helper import DownloadHelper
9 | from yt_dlp import YoutubeDL, DownloadError
10 | from bot import download_dict_lock, download_dict
11 | from bot.helper.telegram_helper.message_utils import sendStatusMessage
12 | from ..status_utils.youtube_dl_download_status import YoutubeDLDownloadStatus
13 |
14 | LOGGER = logging.getLogger(__name__)
15 |
16 |
17 | class MyLogger:
18 | def __init__(self, obj):
19 | self.obj = obj
20 |
21 | def debug(self, msg):
22 | # Hack to fix changing extension
23 | match = re.search(r'.Merger..Merging formats into..(.*?).$', msg) # To mkv
24 | if not match and not self.obj.is_playlist:
25 | match = re.search(r'.ExtractAudio..Destination..(.*?)$', msg) # To mp3
26 | if match and not self.obj.is_playlist:
27 | newname = match.group(1)
28 | newname = newname.split("/")[-1]
29 | self.obj.name = newname
30 |
31 | @staticmethod
32 | def warning(msg):
33 | LOGGER.warning(msg)
34 |
35 | @staticmethod
36 | def error(msg):
37 | if msg != "ERROR: Cancelling...":
38 | LOGGER.error(msg)
39 |
40 |
41 | class YoutubeDLHelper(DownloadHelper):
42 | def __init__(self, listener):
43 | super().__init__()
44 | self.name = ""
45 | self.__start_time = time.time()
46 | self.__listener = listener
47 | self.__gid = ""
48 | self.__download_speed = 0
49 | self.downloaded_bytes = 0
50 | self.size = 0
51 | self.is_playlist = False
52 | self.last_downloaded = 0
53 | self.is_cancelled = False
54 | self.downloading = False
55 | self.__resource_lock = threading.RLock()
56 | self.opts = {'progress_hooks': [self.__onDownloadProgress],
57 | 'logger': MyLogger(self),
58 | 'usenetrc': True,
59 | 'embedsubtitles': True,
60 | 'prefer_ffmpeg': True,
61 | 'cookiefile': 'cookies.txt'}
62 |
63 | @property
64 | def download_speed(self):
65 | with self.__resource_lock:
66 | return self.__download_speed
67 |
68 | @property
69 | def gid(self):
70 | with self.__resource_lock:
71 | return self.__gid
72 |
73 | def __onDownloadProgress(self, d):
74 | self.downloading = True
75 | if self.is_cancelled:
76 | raise ValueError("Cancelling...")
77 | if d['status'] == "finished":
78 | if self.is_playlist:
79 | self.last_downloaded = 0
80 | elif d['status'] == "downloading":
81 | with self.__resource_lock:
82 | self.__download_speed = d['speed']
83 | try:
84 | tbyte = d['total_bytes']
85 | except KeyError:
86 | tbyte = d['total_bytes_estimate']
87 | if self.is_playlist:
88 | downloadedBytes = d['downloaded_bytes']
89 | chunk_size = downloadedBytes - self.last_downloaded
90 | self.last_downloaded = downloadedBytes
91 | self.downloaded_bytes += chunk_size
92 | else:
93 | self.size = tbyte
94 | self.downloaded_bytes = d['downloaded_bytes']
95 | try:
96 | self.progress = (self.downloaded_bytes / self.size) * 100
97 | except ZeroDivisionError:
98 | pass
99 |
100 | def __onDownloadStart(self):
101 | with download_dict_lock:
102 | download_dict[self.__listener.uid] = YoutubeDLDownloadStatus(self, self.__listener)
103 |
104 | def __onDownloadComplete(self):
105 | self.__listener.onDownloadComplete()
106 |
107 | def onDownloadError(self, error):
108 | self.__listener.onDownloadError(error)
109 |
110 | def extractMetaData(self, link, name, get_info=False):
111 |
112 | if get_info:
113 | self.opts['playlist_items'] = '0'
114 | with YoutubeDL(self.opts) as ydl:
115 | try:
116 | result = ydl.extract_info(link, download=False)
117 | if get_info:
118 | return result
119 | realName = ydl.prepare_filename(result)
120 | except DownloadError as e:
121 | if get_info:
122 | raise e
123 | self.onDownloadError(str(e))
124 | return
125 |
126 | if 'entries' in result:
127 | for v in result['entries']:
128 | try:
129 | self.size += v['filesize_approx']
130 | except (KeyError, TypeError):
131 | pass
132 | self.is_playlist = True
133 | if name == "":
134 | self.name = str(realName).split(f" [{result['id']}]")[0]
135 | else:
136 | self.name = name
137 | else:
138 | ext = realName.split('.')[-1]
139 | if name == "":
140 | self.name = str(realName).split(f" [{result['id']}]")[0] + '.' + ext
141 | else:
142 | self.name = f"{name}.{ext}"
143 |
144 | def __download(self, link):
145 | try:
146 | with YoutubeDL(self.opts) as ydl:
147 | ydl.download([link])
148 | if self.is_cancelled:
149 | raise ValueError
150 | self.__onDownloadComplete()
151 | except DownloadError as e:
152 | self.onDownloadError(str(e))
153 | except ValueError:
154 | self.onDownloadError("Download Stopped by User!")
155 |
156 | def add_download(self, link, path, name, qual, playlist):
157 | if playlist == "true":
158 | self.opts['ignoreerrors'] = True
159 | if "hotstar" in link or "sonyliv" in link:
160 | self.opts['geo_bypass_country'] = 'IN'
161 | self.__gid = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=10))
162 | self.__onDownloadStart()
163 | sendStatusMessage(self.__listener.update, self.__listener.bot)
164 | self.opts['format'] = qual
165 | if qual == 'ba/b':
166 | self.opts['postprocessors'] = [{'key': 'FFmpegExtractAudio','preferredcodec': 'mp3','preferredquality': '340'}]
167 | LOGGER.info(f"Downloading with YT-DL: {link}")
168 | self.extractMetaData(link, name)
169 | if self.is_cancelled:
170 | return
171 | if not self.is_playlist:
172 | self.opts['outtmpl'] = f"{path}/{self.name}"
173 | else:
174 | self.opts['outtmpl'] = f"{path}/{self.name}/%(title)s.%(ext)s"
175 | self.__download(link)
176 |
177 | def cancel_download(self):
178 | self.is_cancelled = True
179 | if not self.downloading:
180 | self.onDownloadError("Download Cancelled by User!")
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshpreets63/Mirror-Bot/08cd0fc9ecb8460d964e8970cfa8a1268f6faade/bot/helper/mirror_utils/status_utils/__init__.py
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/aria_download_status.py:
--------------------------------------------------------------------------------
1 | from bot import DOWNLOAD_DIR, LOGGER, aria2
2 | from bot.helper.ext_utils.bot_utils import MirrorStatus
3 |
4 | from .status import Status
5 |
6 |
7 | def get_download(gid):
8 | return aria2.get_download(gid)
9 |
10 |
11 | class AriaDownloadStatus(Status):
12 | def __init__(self, gid, listener):
13 | super().__init__()
14 | self.upload_name = None
15 | self.is_archiving = False
16 | self.__gid = gid
17 | self.__download = get_download(self.__gid)
18 | self.__uid = listener.uid
19 | self.__listener = listener
20 | self.message = listener.message
21 | self.last = None
22 | self.is_waiting = False
23 | self.is_extracting = False
24 |
25 | def __update(self):
26 | self.__download = get_download(self.__gid)
27 | download = self.__download
28 | if download.followed_by_ids:
29 | self.__gid = download.followed_by_ids[0]
30 |
31 | def progress(self):
32 | """
33 | Calculates the progress of the mirror (upload or download)
34 | :return: returns progress in percentage
35 | """
36 | self.__update()
37 | return self.__download.progress_string()
38 |
39 | def size_raw(self):
40 | """
41 | Gets total size of the mirror file/folder
42 | :return: total size of mirror
43 | """
44 | return self.aria_download().total_length
45 |
46 | def processed_bytes(self):
47 | return self.aria_download().completed_length
48 |
49 | def speed(self):
50 | return self.aria_download().download_speed_string()
51 |
52 | def name(self):
53 | return self.aria_download().name
54 |
55 | def path(self):
56 | return f"{DOWNLOAD_DIR}{self.__uid}"
57 |
58 | def size(self):
59 | return self.aria_download().total_length_string()
60 |
61 | def eta(self):
62 | return self.aria_download().eta_string()
63 |
64 | def status(self):
65 | download = self.aria_download()
66 | if download.is_waiting:
67 | return MirrorStatus.STATUS_WAITING
68 | elif download.is_paused:
69 | return MirrorStatus.STATUS_CANCELLED
70 | elif download.has_failed:
71 | return MirrorStatus.STATUS_FAILED
72 | else:
73 | return MirrorStatus.STATUS_DOWNLOADING
74 |
75 | def aria_download(self):
76 | self.__update()
77 | return self.__download
78 |
79 | def download(self):
80 | return self
81 |
82 | def updateName(self, name):
83 | self.__name = name
84 |
85 | def updateGid(self, gid):
86 | self.__gid = gid
87 |
88 | def getListener(self):
89 | return self.__listener
90 |
91 | def uid(self):
92 | return self.__uid
93 |
94 | def gid(self):
95 | self.__update()
96 | return self.__gid
97 |
98 | def cancel_download(self):
99 | LOGGER.info(f"Cancelling Download: {self.name()}")
100 | download = self.aria_download()
101 | if download.is_waiting:
102 | self.__listener.onDownloadError("Cancelled by user")
103 | aria2.remove([download], force=True)
104 | return
105 | if len(download.followed_by_ids) != 0:
106 | downloads = aria2.get_downloads(download.followed_by_ids)
107 | self.__listener.onDownloadError('Download stopped by user!')
108 | aria2.remove(downloads, force=True)
109 | aria2.remove([download], force=True)
110 | return
111 | self.__listener.onDownloadError('Download stopped by user!')
112 | aria2.remove([download], force=True)
113 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/clone_status.py:
--------------------------------------------------------------------------------
1 | # All rights reserved
2 |
3 | from .status import Status
4 | from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size, get_readable_time
5 |
6 |
7 | class CloneStatus(Status):
8 | def __init__(self, obj, size, update, gid):
9 | self.cobj = obj
10 | self.__csize = size
11 | self.message = update.message
12 | self.__cgid = gid
13 |
14 | def processed_bytes(self):
15 | return self.cobj.transferred_size
16 |
17 | def size_raw(self):
18 | return self.__csize
19 |
20 | def size(self):
21 | return get_readable_file_size(self.__csize)
22 |
23 | def status(self):
24 | return MirrorStatus.STATUS_CLONING
25 |
26 | def name(self):
27 | return self.cobj.name
28 |
29 | def gid(self) -> str:
30 | return self.__cgid
31 |
32 | def progress_raw(self):
33 | try:
34 | return self.cobj.transferred_size / self.__csize * 100
35 | except ZeroDivisionError:
36 | return 0
37 |
38 | def progress(self):
39 | return f'{round(self.progress_raw(), 2)}%'
40 |
41 | def speed_raw(self):
42 | """
43 | :return: Download speed in Bytes/Seconds
44 | """
45 | return self.cobj.cspeed()
46 |
47 | def speed(self):
48 | return f'{get_readable_file_size(self.speed_raw())}/s'
49 |
50 | def eta(self):
51 | try:
52 | seconds = (self.__csize - self.cobj.transferred_size) / self.speed_raw()
53 | return f'{get_readable_time(seconds)}'
54 | except ZeroDivisionError:
55 | return '-'
56 |
57 | def download(self):
58 | return self.cobj
59 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/extract_status.py:
--------------------------------------------------------------------------------
1 | from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size
2 |
3 | from .status import Status
4 |
5 |
6 | class ExtractStatus(Status):
7 | def __init__(self, name, path, size):
8 | self.__name = name
9 | self.__path = path
10 | self.__size = size
11 |
12 | # The progress of extract function cannot be tracked. So we just return dummy values.
13 | # If this is possible in future,we should implement it
14 |
15 | def progress(self):
16 | return "0"
17 |
18 | def speed(self):
19 | return "0"
20 |
21 | def name(self):
22 | return self.__name
23 |
24 | def path(self):
25 | return self.__path
26 |
27 | def size(self):
28 | return get_readable_file_size(self.__size)
29 |
30 | def eta(self):
31 | return "0s"
32 |
33 | def status(self):
34 | return MirrorStatus.STATUS_EXTRACTING
35 |
36 | def processed_bytes(self):
37 | return 0
38 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/gdownload_status.py:
--------------------------------------------------------------------------------
1 | from bot import DOWNLOAD_DIR
2 | from bot.helper.ext_utils.bot_utils import (
3 | MirrorStatus,
4 | get_readable_file_size,
5 | get_readable_time,
6 | )
7 |
8 | from .status import Status
9 |
10 |
11 | class DownloadStatus(Status):
12 | def __init__(self, obj, size, listener, gid):
13 | self.dobj = obj
14 | self.__dsize = size
15 | self.uid = listener.uid
16 | self.message = listener.message
17 | self.__dgid = gid
18 |
19 | def path(self):
20 | return f"{DOWNLOAD_DIR}{self.uid}"
21 |
22 | def processed_bytes(self):
23 | return self.dobj.downloaded_bytes
24 |
25 | def size_raw(self):
26 | return self.__dsize
27 |
28 | def size(self):
29 | return get_readable_file_size(self.__dsize)
30 |
31 | def status(self):
32 | return MirrorStatus.STATUS_DOWNLOADING
33 |
34 | def name(self):
35 | return self.dobj.name
36 |
37 | def gid(self) -> str:
38 | return self.__dgid
39 |
40 | def progress_raw(self):
41 | try:
42 | return self.dobj.downloaded_bytes / self.__dsize * 100
43 | except ZeroDivisionError:
44 | return 0
45 |
46 | def progress(self):
47 | return f"{round(self.progress_raw(), 2)}%"
48 |
49 | def speed_raw(self):
50 | """
51 | :return: Download speed in Bytes/Seconds
52 | """
53 | return self.dobj.dspeed()
54 |
55 | def speed(self):
56 | return f"{get_readable_file_size(self.speed_raw())}/s"
57 |
58 | def eta(self):
59 | try:
60 | seconds = (self.__dsize - self.dobj.downloaded_bytes) / self.speed_raw()
61 | return f"{get_readable_time(seconds)}"
62 | except ZeroDivisionError:
63 | return "-"
64 |
65 | def download(self):
66 | return self.dobj
67 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/listeners.py:
--------------------------------------------------------------------------------
1 | class MirrorListeners:
2 | def __init__(self, context, update):
3 | self.bot = context
4 | self.update = update
5 | self.message = update.message
6 | self.uid = self.message.message_id
7 |
8 | def onDownloadStarted(self):
9 | raise NotImplementedError
10 |
11 | def onDownloadProgress(self):
12 | raise NotImplementedError
13 |
14 | def onDownloadComplete(self):
15 | raise NotImplementedError
16 |
17 | def onDownloadError(self, error: str):
18 | raise NotImplementedError
19 |
20 | def onUploadStarted(self):
21 | raise NotImplementedError
22 |
23 | def onUploadProgress(self):
24 | raise NotImplementedError
25 |
26 | def onUploadComplete(self, link: str):
27 | raise NotImplementedError
28 |
29 | def onUploadError(self, error: str):
30 | raise NotImplementedError
31 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/mega_status.py:
--------------------------------------------------------------------------------
1 | from bot import DOWNLOAD_DIR
2 | from bot.helper.ext_utils.bot_utils import (
3 | MirrorStatus,
4 | get_readable_file_size,
5 | get_readable_time,
6 | )
7 |
8 | from .status import Status
9 |
10 |
11 | class MegaDownloadStatus(Status):
12 | def __init__(self, obj, listener):
13 | self.obj = obj
14 | self.uid = listener.uid
15 | self.message = listener.message
16 |
17 | def gid(self):
18 | return self.obj.gid
19 |
20 | def path(self):
21 | return f"{DOWNLOAD_DIR}{self.uid}"
22 |
23 | def processed_bytes(self):
24 | return self.obj.downloaded_bytes
25 |
26 | def size_raw(self):
27 | return self.obj.size
28 |
29 | def size(self):
30 | return get_readable_file_size(self.size_raw())
31 |
32 | def status(self):
33 | return MirrorStatus.STATUS_DOWNLOADING
34 |
35 | def name(self):
36 | return self.obj.name
37 |
38 | def progress_raw(self):
39 | return self.obj.progress
40 |
41 | def progress(self):
42 | return f"{round(self.progress_raw(), 2)}%"
43 |
44 | def speed_raw(self):
45 | """
46 | :return: Download speed in Bytes/Seconds
47 | """
48 | return self.obj.download_speed
49 |
50 | def speed(self):
51 | return f"{get_readable_file_size(self.speed_raw())}/s"
52 |
53 | def eta(self):
54 | try:
55 | seconds = (self.size_raw() - self.processed_bytes()) / self.speed_raw()
56 | return f"{get_readable_time(seconds)}"
57 | except ZeroDivisionError:
58 | return "-"
59 |
60 | def download(self):
61 | return self.obj
62 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/split_status.py:
--------------------------------------------------------------------------------
1 | # Implement By - @anasty17 (https://github.com/SlamDevs/slam-mirrorbot/commit/d888a1e7237f4633c066f7c2bbfba030b83ad616)
2 | # (c) https://github.com/SlamDevs/slam-mirrorbot
3 | # All rights reserved
4 |
5 | from .status import Status
6 | from bot.helper.ext_utils.bot_utils import get_readable_file_size, MirrorStatus
7 |
8 |
9 | class SplitStatus(Status):
10 | def __init__(self, name, path, size):
11 | self.__name = name
12 | self.__path = path
13 | self.__size = size
14 |
15 | # The progress of Tar function cannot be tracked. So we just return dummy values.
16 | # If this is possible in future,we should implement it
17 |
18 | def progress(self):
19 | return '0'
20 |
21 | def speed(self):
22 | return '0'
23 |
24 | def name(self):
25 | return self.__name
26 |
27 | def path(self):
28 | return self.__path
29 |
30 | def size(self):
31 | return get_readable_file_size(self.__size)
32 |
33 | def eta(self):
34 | return '0s'
35 |
36 | def status(self):
37 | return MirrorStatus.STATUS_SPLITTING
38 |
39 | def processed_bytes(self):
40 | return 0
41 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/status.py:
--------------------------------------------------------------------------------
1 | # Generic status class. All other status classes must inherit this class
2 |
3 |
4 | class Status:
5 | def progress(self):
6 | """
7 | Calculates the progress of the mirror (upload or download)
8 | :return: progress in percentage
9 | """
10 | raise NotImplementedError
11 |
12 | def speed(self):
13 | """:return: speed in bytes per second"""
14 | raise NotImplementedError
15 |
16 | def name(self):
17 | """:return name of file/directory being processed"""
18 | raise NotImplementedError
19 |
20 | def path(self):
21 | """:return path of the file/directory"""
22 | raise NotImplementedError
23 |
24 | def size(self):
25 | """:return Size of file folder"""
26 | raise NotImplementedError
27 |
28 | def eta(self):
29 | """:return ETA of the process to complete"""
30 | raise NotImplementedError
31 |
32 | def status(self):
33 | """:return String describing what is the object of this class will be tracking (upload/download/something
34 | else)"""
35 | raise NotImplementedError
36 |
37 | def processed_bytes(self):
38 | """:return The size of file that has been processed (downloaded/uploaded/archived)"""
39 | raise NotImplementedError
40 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/tar_status.py:
--------------------------------------------------------------------------------
1 | from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size
2 |
3 | from .status import Status
4 |
5 |
6 | class TarStatus(Status):
7 | def __init__(self, name, path, size):
8 | self.__name = name
9 | self.__path = path
10 | self.__size = size
11 |
12 | # The progress of zip function cannot be tracked. So we just return dummy values.
13 | # If this is possible in future,we should implement it
14 |
15 | def progress(self):
16 | return "0"
17 |
18 | def speed(self):
19 | return "0"
20 |
21 | def name(self):
22 | return self.__name
23 |
24 | def path(self):
25 | return self.__path
26 |
27 | def size(self):
28 | return get_readable_file_size(self.__size)
29 |
30 | def eta(self):
31 | return "0s"
32 |
33 | def status(self):
34 | return MirrorStatus.STATUS_ARCHIVING
35 |
36 | def processed_bytes(self):
37 | return 0
38 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/telegram_download_status.py:
--------------------------------------------------------------------------------
1 | from bot import DOWNLOAD_DIR
2 | from bot.helper.ext_utils.bot_utils import (
3 | MirrorStatus,
4 | get_readable_file_size,
5 | get_readable_time,
6 | )
7 |
8 | from .status import Status
9 |
10 |
11 | class TelegramDownloadStatus(Status):
12 | def __init__(self, obj, listener):
13 | self.obj = obj
14 | self.uid = listener.uid
15 | self.message = listener.message
16 |
17 | def gid(self):
18 | return self.obj.gid
19 |
20 | def path(self):
21 | return f"{DOWNLOAD_DIR}{self.uid}"
22 |
23 | def processed_bytes(self):
24 | return self.obj.downloaded_bytes
25 |
26 | def size_raw(self):
27 | return self.obj.size
28 |
29 | def size(self):
30 | return get_readable_file_size(self.size_raw())
31 |
32 | def status(self):
33 | return MirrorStatus.STATUS_DOWNLOADING
34 |
35 | def name(self):
36 | return self.obj.name
37 |
38 | def progress_raw(self):
39 | return self.obj.progress
40 |
41 | def progress(self):
42 | return f"{round(self.progress_raw(), 2)}%"
43 |
44 | def speed_raw(self):
45 | """
46 | :return: Download speed in Bytes/Seconds
47 | """
48 | return self.obj.download_speed
49 |
50 | def speed(self):
51 | return f"{get_readable_file_size(self.speed_raw())}/s"
52 |
53 | def eta(self):
54 | try:
55 | seconds = (self.size_raw() - self.processed_bytes()) / self.speed_raw()
56 | return f"{get_readable_time(seconds)}"
57 | except ZeroDivisionError:
58 | return "-"
59 |
60 | def download(self):
61 | return self.obj
62 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/tg_upload_status.py:
--------------------------------------------------------------------------------
1 | # Implement By - @anasty17 (https://github.com/SlamDevs/slam-mirrorbot/commit/d888a1e7237f4633c066f7c2bbfba030b83ad616)
2 | # (c) https://github.com/SlamDevs/slam-mirrorbot
3 | # All rights reserved
4 |
5 | from .status import Status
6 | from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size, get_readable_time
7 | from bot import DOWNLOAD_DIR
8 |
9 |
10 | class TgUploadStatus(Status):
11 | def __init__(self, obj, size, gid, listener):
12 | self.obj = obj
13 | self.__size = size
14 | self.uid = listener.uid
15 | self.message = listener.message
16 | self.__gid = gid
17 |
18 | def path(self):
19 | return f"{DOWNLOAD_DIR}{self.uid}"
20 |
21 | def processed_bytes(self):
22 | return self.obj.uploaded_bytes
23 |
24 | def size_raw(self):
25 | return self.__size
26 |
27 | def size(self):
28 | return get_readable_file_size(self.__size)
29 |
30 | def status(self):
31 | return MirrorStatus.STATUS_UPLOADING
32 |
33 | def name(self):
34 | return self.obj.name
35 |
36 | def progress_raw(self):
37 | try:
38 | return self.obj.uploaded_bytes / self.__size * 100
39 | except ZeroDivisionError:
40 | return 0
41 |
42 | def progress(self):
43 | return f'{round(self.progress_raw(), 2)}%'
44 |
45 | def speed_raw(self):
46 | """
47 | :return: Upload speed in Bytes/Seconds
48 | """
49 | return self.obj.speed()
50 |
51 | def speed(self):
52 | return f'{get_readable_file_size(self.speed_raw())}/s'
53 |
54 | def eta(self):
55 | try:
56 | seconds = (self.__size - self.obj.uploaded_bytes) / self.speed_raw()
57 | return f'{get_readable_time(seconds)}'
58 | except ZeroDivisionError:
59 | return '-'
60 |
61 |
62 | def download(self):
63 | return self.obj
64 |
65 | def gid(self) -> str:
66 | return self.__gid
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/upload_status.py:
--------------------------------------------------------------------------------
1 | from bot import DOWNLOAD_DIR
2 | from bot.helper.ext_utils.bot_utils import (
3 | MirrorStatus,
4 | get_readable_file_size,
5 | get_readable_time,
6 | )
7 |
8 | from .status import Status
9 |
10 |
11 | class UploadStatus(Status):
12 | def __init__(self, obj, size, gid, listener):
13 | self.obj = obj
14 | self.__size = size
15 | self.uid = listener.uid
16 | self.message = listener.message
17 | self.__gid = gid
18 |
19 | def path(self):
20 | return f"{DOWNLOAD_DIR}{self.uid}"
21 |
22 | def processed_bytes(self):
23 | return self.obj.uploaded_bytes
24 |
25 | def size_raw(self):
26 | return self.__size
27 |
28 | def size(self):
29 | return get_readable_file_size(self.__size)
30 |
31 | def status(self):
32 | return MirrorStatus.STATUS_UPLOADING
33 |
34 | def name(self):
35 | return self.obj.name
36 |
37 | def progress_raw(self):
38 | try:
39 | return self.obj.uploaded_bytes / self.__size * 100
40 | except ZeroDivisionError:
41 | return 0
42 |
43 | def progress(self):
44 | return f"{round(self.progress_raw(), 2)}%"
45 |
46 | def speed_raw(self):
47 | """
48 | :return: Upload speed in Bytes/Seconds
49 | """
50 | return self.obj.speed()
51 |
52 | def speed(self):
53 | return f"{get_readable_file_size(self.speed_raw())}/s"
54 |
55 | def eta(self):
56 | try:
57 | seconds = (self.__size - self.obj.uploaded_bytes) / self.speed_raw()
58 | return f"{get_readable_time(seconds)}"
59 | except ZeroDivisionError:
60 | return "-"
61 |
62 | def gid(self) -> str:
63 | return self.__gid
64 |
65 | def download(self):
66 | return self.obj
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/youtube_dl_download_status.py:
--------------------------------------------------------------------------------
1 | from bot import DOWNLOAD_DIR
2 | from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size, get_readable_time
3 | from .status import Status
4 | from bot.helper.ext_utils.fs_utils import get_path_size
5 |
6 | class YoutubeDLDownloadStatus(Status):
7 | def __init__(self, obj, listener):
8 | self.obj = obj
9 | self.uid = listener.uid
10 | self.message = listener.message
11 |
12 | def gid(self):
13 | return self.obj.gid
14 |
15 | def path(self):
16 | return f"{DOWNLOAD_DIR}{self.uid}"
17 |
18 | def processed_bytes(self):
19 | if self.obj.downloaded_bytes != 0:
20 | return self.obj.downloaded_bytes
21 | else:
22 | return get_path_size(f"{DOWNLOAD_DIR}{self.uid}")
23 |
24 | def size_raw(self):
25 | return self.obj.size
26 |
27 | def size(self):
28 | return get_readable_file_size(self.size_raw())
29 |
30 | def status(self):
31 | return MirrorStatus.STATUS_DOWNLOADING
32 |
33 | def name(self):
34 | return self.obj.name
35 |
36 | def progress_raw(self):
37 | return self.obj.progress
38 |
39 | def progress(self):
40 | return f'{round(self.progress_raw(), 2)}%'
41 |
42 | def speed_raw(self):
43 | """
44 | :return: Download speed in Bytes/Seconds
45 | """
46 | return self.obj.download_speed
47 |
48 | def speed(self):
49 | return f'{get_readable_file_size(self.speed_raw())}/s'
50 |
51 | def eta(self):
52 | try:
53 | seconds = (self.size_raw() - self.processed_bytes()) / self.speed_raw()
54 | return f'{get_readable_time(seconds)}'
55 | except:
56 | return '-'
57 |
58 | def download(self):
59 | return self.obj
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/upload_utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshpreets63/Mirror-Bot/08cd0fc9ecb8460d964e8970cfa8a1268f6faade/bot/helper/mirror_utils/upload_utils/__init__.py
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/upload_utils/pyrogramEngine.py:
--------------------------------------------------------------------------------
1 | # Implement By - @anasty17 (https://github.com/SlamDevs/slam-mirrorbot/commit/d888a1e7237f4633c066f7c2bbfba030b83ad616)
2 | # (c) https://github.com/SlamDevs/slam-mirrorbot
3 | # All rights reserved
4 |
5 | import os
6 | import logging
7 | import time
8 |
9 | from pyrogram.errors import FloodWait, RPCError
10 | from hachoir.parser import createParser
11 | from hachoir.metadata import extractMetadata
12 |
13 | from bot import app, DOWNLOAD_DIR, AS_DOCUMENT, AS_DOC_USERS, AS_MEDIA_USERS, LOGS_CHATS
14 | from bot.helper.ext_utils.fs_utils import take_ss
15 |
16 | LOGGER = logging.getLogger(__name__)
17 | logging.getLogger("pyrogram").setLevel(logging.WARNING)
18 |
19 | VIDEO_SUFFIXES = ("MKV", "MP4", "MOV", "WMV", "3GP", "MPG", "WEBM", "AVI", "FLV", "M4V")
20 | AUDIO_SUFFIXES = ("MP3", "M4A", "M4B", "FLAC", "WAV", "AIF", "OGG", "AAC", "DTS", "MID", "AMR", "MKA")
21 | IMAGE_SUFFIXES = ("JPG", "JPX", "PNG", "GIF", "WEBP", "CR2", "TIF", "BMP", "JXR", "PSD", "ICO", "HEIC")
22 |
23 |
24 | class TgUploader:
25 |
26 | def __init__(self, name=None, listener=None):
27 | self.__listener = listener
28 | self.name = name
29 | self.__app = app
30 | self.total_bytes = 0
31 | self.uploaded_bytes = 0
32 | self.last_uploaded = 0
33 | self.start_time = time.time()
34 | self.is_cancelled = False
35 | self.chat_id = listener.message.chat.id
36 | self.message_id = listener.uid
37 | self.user_id = listener.message.from_user.id
38 | self.as_doc = AS_DOCUMENT
39 | self.thumb = f"Thumbnails/{self.user_id}.jpg"
40 | self.sent_msg = self.__app.get_messages(self.chat_id, self.message_id)
41 | self.corrupted = 0
42 |
43 | def upload(self):
44 | msgs_dict = {}
45 | path = f"{DOWNLOAD_DIR}{self.message_id}"
46 | self.user_settings()
47 | for dirpath, subdir, files in sorted(os.walk(path)):
48 | for file in sorted(files):
49 | if self.is_cancelled:
50 | return
51 | up_path = os.path.join(dirpath, file)
52 | fsize = os.path.getsize(up_path)
53 | if fsize == 0:
54 | LOGGER.error(f"{up_path} size is zero, telegram don't upload zero size files")
55 | self.corrupted += 1
56 | continue
57 | self.upload_file(up_path, file, dirpath)
58 | if self.is_cancelled:
59 | return
60 | msgs_dict[file] = self.sent_msg.message_id
61 | self.last_uploaded = 0
62 | if len(msgs_dict) <= self.corrupted:
63 | return self.__listener.onUploadError('Files Corrupted. Check logs')
64 | LOGGER.info(f"Leech Done: {self.name}")
65 | self.__listener.onUploadComplete(self.name, None, msgs_dict, None, self.corrupted)
66 |
67 | def upload_file(self, up_path, file, dirpath):
68 | cap_mono = f"{file}
"
69 | notMedia = False
70 | thumb = self.thumb
71 | try:
72 | if not self.as_doc:
73 | duration = 0
74 | if file.upper().endswith(VIDEO_SUFFIXES):
75 | metadata = extractMetadata(createParser(up_path))
76 | if metadata.has("duration"):
77 | duration = metadata.get("duration").seconds
78 | if thumb is None:
79 | thumb = take_ss(up_path)
80 | if self.is_cancelled:
81 | return
82 | if not file.upper().endswith(("MKV", "MP4")):
83 | file = os.path.splitext(file)[0] + '.mp4'
84 | new_path = os.path.join(dirpath, file)
85 | os.rename(up_path, new_path)
86 | up_path = new_path
87 | self.sent_msg = self.sent_msg.reply_video(video=up_path,
88 | quote=True,
89 | caption=cap_mono,
90 | parse_mode="html",
91 | duration=duration,
92 | width=480,
93 | height=320,
94 | thumb=thumb,
95 | supports_streaming=True,
96 | disable_notification=True,
97 | progress=self.upload_progress)
98 | try:
99 | for i in LOGS_CHATS:
100 | app.send_video(i, video=self.sent_msg.video.file_id, caption=cap_mono)
101 | except Exception as err:
102 | LOGGER.error(f"Failed to forward file to log channel:\n{err}")
103 | if self.thumb is None and thumb is not None and os.path.lexists(thumb):
104 | os.remove(thumb)
105 | elif file.upper().endswith(AUDIO_SUFFIXES):
106 | metadata = extractMetadata(createParser(up_path))
107 | if metadata.has("duration"):
108 | duration = metadata.get('duration').seconds
109 | title = metadata.get("title") if metadata.has("title") else None
110 | artist = metadata.get("artist") if metadata.has("artist") else None
111 | self.sent_msg = self.sent_msg.reply_audio(audio=up_path,
112 | quote=True,
113 | caption=cap_mono,
114 | parse_mode="html",
115 | duration=duration,
116 | performer=artist,
117 | title=title,
118 | thumb=thumb,
119 | disable_notification=True,
120 | progress=self.upload_progress)
121 | try:
122 | for i in LOGS_CHATS:
123 | app.send_audio(i, audio=self.sent_msg.audio.file_id, caption=cap_mono)
124 | except Exception as err:
125 | LOGGER.error(f"Failed to forward file to log channel:\n{err}")
126 | elif file.upper().endswith(IMAGE_SUFFIXES):
127 | self.sent_msg = self.sent_msg.reply_photo(photo=up_path,
128 | quote=True,
129 | caption=cap_mono,
130 | parse_mode="html",
131 | disable_notification=True,
132 | progress=self.upload_progress)
133 | try:
134 | for i in LOGS_CHATS:
135 | app.send_photo(i, photo=self.sent_msg.photo.file_id, caption=cap_mono)
136 | except Exception as err:
137 | LOGGER.error(f"Failed to forward file to log channel:\n{err}")
138 | else:
139 | notMedia = True
140 | if self.as_doc or notMedia:
141 | if file.upper().endswith(VIDEO_SUFFIXES) and thumb is None:
142 | thumb = take_ss(up_path)
143 | if self.is_cancelled:
144 | return
145 | self.sent_msg = self.sent_msg.reply_document(document=up_path,
146 | quote=True,
147 | thumb=thumb,
148 | caption=cap_mono,
149 | parse_mode="html",
150 | disable_notification=True,
151 | progress=self.upload_progress)
152 | try:
153 | for i in LOGS_CHATS:
154 | app.send_document(i, document=self.sent_msg.document.file_id, caption=cap_mono)
155 | except Exception as err:
156 | LOGGER.error(f"Failed to forward file to log channel:\n{err}")
157 | if self.thumb is None and thumb is not None and os.path.lexists(thumb):
158 | os.remove(thumb)
159 | if not self.is_cancelled:
160 | os.remove(up_path)
161 | except FloodWait as f:
162 | LOGGER.info(f)
163 | time.sleep(f.x)
164 | except RPCError as e:
165 | LOGGER.error(f"RPCError: {e} File: {up_path}")
166 | self.corrupted += 1
167 | except Exception as err:
168 | LOGGER.error(f"{err} File: {up_path}")
169 | self.corrupted += 1
170 | def upload_progress(self, current, total):
171 | if self.is_cancelled:
172 | self.__app.stop_transmission()
173 | return
174 | chunk_size = current - self.last_uploaded
175 | self.last_uploaded = current
176 | self.uploaded_bytes += chunk_size
177 |
178 | def user_settings(self):
179 | if self.user_id in AS_DOC_USERS:
180 | self.as_doc = True
181 | elif self.user_id in AS_MEDIA_USERS:
182 | self.as_doc = False
183 | if not os.path.lexists(self.thumb):
184 | self.thumb = None
185 |
186 | def speed(self):
187 | try:
188 | return self.uploaded_bytes / (time.time() - self.start_time)
189 | except ZeroDivisionError:
190 | return 0
191 |
192 | def cancel_download(self):
193 | self.is_cancelled = True
194 | LOGGER.info(f"Cancelling Upload: {self.name}")
195 | self.__listener.onUploadError('your upload has been stopped!')
196 |
--------------------------------------------------------------------------------
/bot/helper/telegram_helper/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshpreets63/Mirror-Bot/08cd0fc9ecb8460d964e8970cfa8a1268f6faade/bot/helper/telegram_helper/__init__.py
--------------------------------------------------------------------------------
/bot/helper/telegram_helper/bot_commands.py:
--------------------------------------------------------------------------------
1 | class _BotCommands:
2 | def __init__(self):
3 | self.StartCommand = "start"
4 | self.MirrorCommand = "mirror"
5 | self.UnzipMirrorCommand = "unzipmirror"
6 | self.ZipMirrorCommand = "zipmirror"
7 | self.TarMirrorCommand = "tarmirror"
8 | self.CancelMirror = "cancel"
9 | self.CancelAllCommand = "cancelall"
10 | self.ListCommand = "list"
11 | self.StatusCommand = "status"
12 | self.AuthorizeCommand = "authorize"
13 | self.UnAuthorizeCommand = "unauthorize"
14 | self.AuthorizedUsersCommand = 'users'
15 | self.AddSudoCommand = 'addsudo'
16 | self.RmSudoCommand = 'rmsudo'
17 | self.PingCommand = "ping"
18 | self.RestartCommand = "restart"
19 | self.StatsCommand = "stats"
20 | self.HelpCommand = "help"
21 | self.LogCommand = "log"
22 | self.CloneCommand = "clone"
23 | self.WatchCommand = "watch"
24 | self.ZipWatchCommand = "zipwatch"
25 | self.TarWatchCommand = "tarwatch"
26 | self.deleteCommand = "del"
27 | self.LeechSetCommand = 'leechset'
28 | self.SetThumbCommand = 'setthumb'
29 | self.LeechCommand = 'leech'
30 | self.TarLeechCommand = 'tarleech'
31 | self.UnzipLeechCommand = 'unzipleech'
32 | self.ZipLeechCommand = 'zipleech'
33 | self.LeechWatchCommand = 'leechwatch'
34 | self.LeechTarWatchCommand = 'leechtarwatch'
35 | self.LeechZipWatchCommand = 'leechzipwatch'
36 | self.CountCommand = "count"
37 | self.SpeedCommand = "speedtest"
38 |
39 | BotCommands = _BotCommands()
40 |
--------------------------------------------------------------------------------
/bot/helper/telegram_helper/button_build.py:
--------------------------------------------------------------------------------
1 | from telegram import InlineKeyboardButton
2 |
3 |
4 | class ButtonMaker:
5 | def __init__(self):
6 | self.button = []
7 |
8 | def buildbutton(self, key, link):
9 | self.button.append(InlineKeyboardButton(text = key, url = link))
10 |
11 | def sbutton(self, key, data):
12 | self.button.append(InlineKeyboardButton(text = key, callback_data = data))
13 |
14 | def build_menu(self, n_cols, footer_buttons=None, header_buttons=None):
15 | menu = [self.button[i:i + n_cols] for i in range(0, len(self.button), n_cols)]
16 | if header_buttons:
17 | menu.insert(0, header_buttons)
18 | if footer_buttons:
19 | menu.append(footer_buttons)
20 | return menu
--------------------------------------------------------------------------------
/bot/helper/telegram_helper/filters.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import MessageFilter
2 | from telegram import Message
3 | from bot import AUTHORIZED_CHATS, SUDO_USERS, OWNER_ID, download_dict, download_dict_lock
4 |
5 |
6 | class CustomFilters:
7 | class _OwnerFilter(MessageFilter):
8 | def filter(self, message):
9 | return bool(message.from_user.id == OWNER_ID)
10 |
11 | owner_filter = _OwnerFilter()
12 |
13 | class _AuthorizedUserFilter(MessageFilter):
14 | def filter(self, message):
15 | id = message.from_user.id
16 | return bool(id in AUTHORIZED_CHATS or id in SUDO_USERS or id == OWNER_ID)
17 |
18 | authorized_user = _AuthorizedUserFilter()
19 |
20 | class _AuthorizedChat(MessageFilter):
21 | def filter(self, message):
22 | return bool(message.chat.id in AUTHORIZED_CHATS)
23 |
24 | authorized_chat = _AuthorizedChat()
25 |
26 | class _SudoUser(MessageFilter):
27 | def filter(self, message):
28 | return bool(message.from_user.id in SUDO_USERS)
29 |
30 | sudo_user = _SudoUser()
31 |
32 | class _MirrorOwner(MessageFilter):
33 | def filter(self, message: Message):
34 | user_id = message.from_user.id
35 | if user_id == OWNER_ID:
36 | return True
37 | args = str(message.text).split(' ')
38 | if len(args) > 1:
39 | # Cancelling by gid
40 | with download_dict_lock:
41 | for message_id, status in download_dict.items():
42 | if status.gid() == args[1] and status.message.from_user.id == user_id:
43 | return True
44 | else:
45 | return False
46 | if not message.reply_to_message and len(args) == 1:
47 | return True
48 | # Cancelling by replying to original mirror message
49 | reply_user = message.reply_to_message.from_user.id
50 | return bool(reply_user == user_id)
51 | mirror_owner_filter = _MirrorOwner()
52 |
--------------------------------------------------------------------------------
/bot/helper/telegram_helper/message_utils.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | import psutil
4 | from telegram import InlineKeyboardMarkup
5 | from telegram.message import Message
6 | from telegram.update import Update
7 |
8 | from bot import (
9 | AUTO_DELETE_MESSAGE_DURATION,
10 | LOGGER,
11 | bot,
12 | download_dict,
13 | download_dict_lock,
14 | status_reply_dict,
15 | status_reply_dict_lock,
16 | )
17 | from bot.helper.ext_utils.bot_utils import (
18 | MirrorStatus,
19 | get_readable_file_size,
20 | get_readable_message,
21 | )
22 |
23 |
24 | def sendMessage(text: str, bot, update: Update):
25 | try:
26 | return bot.send_message(
27 | update.message.chat_id,
28 | reply_to_message_id=update.message.message_id,
29 | text=text,
30 | allow_sending_without_reply=True,
31 | parse_mode="HTMl",
32 | )
33 | except Exception as e:
34 | LOGGER.error(str(e))
35 |
36 |
37 | def sendMarkup(text: str, bot, update: Update, reply_markup: InlineKeyboardMarkup):
38 | try:
39 | return bot.send_message(
40 | update.message.chat_id,
41 | reply_to_message_id=update.message.message_id,
42 | text=text,
43 | allow_sending_without_reply=True,
44 | reply_markup=reply_markup,
45 | parse_mode="HTMl",
46 | )
47 | except Exception as e:
48 | LOGGER.error(str(e))
49 |
50 | def editMessage(text: str, message: Message, reply_markup=None):
51 | try:
52 | bot.edit_message_text(
53 | text=text,
54 | message_id=message.message_id,
55 | chat_id=message.chat.id,
56 | reply_markup=reply_markup,
57 | parse_mode="HTMl",
58 | )
59 | except Exception as e:
60 | LOGGER.error(str(e))
61 |
62 |
63 | def deleteMessage(bot, message: Message):
64 | try:
65 | bot.delete_message(chat_id=message.chat.id, message_id=message.message_id)
66 | except Exception as e:
67 | LOGGER.error(str(e))
68 |
69 |
70 | def sendLogFile(bot, update: Update):
71 | with open("log.txt", "rb") as f:
72 | bot.send_document(
73 | document=f,
74 | filename=f.name,
75 | reply_to_message_id=update.message.message_id,
76 | chat_id=update.message.chat_id,
77 | )
78 |
79 |
80 | def auto_delete_message(bot, cmd_message: Message, bot_message: Message):
81 | if AUTO_DELETE_MESSAGE_DURATION != -1:
82 | time.sleep(AUTO_DELETE_MESSAGE_DURATION)
83 | try:
84 | # Skip if None is passed meaning we don't want to delete bot xor cmd message
85 | deleteMessage(bot, cmd_message)
86 | deleteMessage(bot, bot_message)
87 | except AttributeError:
88 | pass
89 |
90 |
91 | def delete_all_messages():
92 | with status_reply_dict_lock:
93 | for message in list(status_reply_dict.values()):
94 | try:
95 | deleteMessage(bot, message)
96 | del status_reply_dict[message.chat.id]
97 | except Exception as e:
98 | LOGGER.error(str(e))
99 |
100 |
101 | def update_all_messages():
102 | msg = get_readable_message()
103 | msg += (
104 | f"CPU: {psutil.cpu_percent()}%"
105 | f" DISK: {psutil.disk_usage('/').percent}%"
106 | f" RAM: {psutil.virtual_memory().percent}%"
107 | )
108 | with download_dict_lock:
109 | dlspeed_bytes = 0
110 | uldl_bytes = 0
111 | for download in list(download_dict.values()):
112 | speedy = download.speed()
113 | if download.status() == MirrorStatus.STATUS_DOWNLOADING:
114 | if 'K' in speedy:
115 | dlspeed_bytes += float(speedy.split('K')[0]) * 1024
116 | elif 'M' in speedy:
117 | dlspeed_bytes += float(speedy.split('M')[0]) * 1048576
118 | if download.status() == MirrorStatus.STATUS_UPLOADING:
119 | if 'KB/s' in speedy:
120 | uldl_bytes += float(speedy.split('K')[0]) * 1024
121 | elif 'MB/s' in speedy:
122 | uldl_bytes += float(speedy.split('M')[0]) * 1048576
123 | dlspeed = get_readable_file_size(dlspeed_bytes)
124 | ulspeed = get_readable_file_size(uldl_bytes)
125 | msg += f"\nDL:{dlspeed}ps | UL:{ulspeed}/s \n"
126 | with status_reply_dict_lock:
127 | for chat_id in list(status_reply_dict.keys()):
128 | if status_reply_dict[chat_id] and msg != status_reply_dict[chat_id].text:
129 | if len(msg) == 0:
130 | msg = "Starting DL"
131 | try:
132 | editMessage(msg, status_reply_dict[chat_id])
133 | except Exception as e:
134 | LOGGER.error(str(e))
135 | status_reply_dict[chat_id].text = msg
136 |
137 |
138 | def sendStatusMessage(msg, bot):
139 | progress = get_readable_message()
140 | progress += (
141 | f"CPU: {psutil.cpu_percent()}%"
142 | f" DISK: {psutil.disk_usage('/').percent}%"
143 | f" RAM: {psutil.virtual_memory().percent}%"
144 | )
145 | with download_dict_lock:
146 | dlspeed_bytes = 0
147 | uldl_bytes = 0
148 | for download in list(download_dict.values()):
149 | speedy = download.speed()
150 | if download.status() == MirrorStatus.STATUS_DOWNLOADING:
151 | if 'K' in speedy:
152 | dlspeed_bytes += float(speedy.split('K')[0]) * 1024
153 | elif 'M' in speedy:
154 | dlspeed_bytes += float(speedy.split('M')[0]) * 1048576
155 | if download.status() == MirrorStatus.STATUS_UPLOADING:
156 | if 'KB/s' in speedy:
157 | uldl_bytes += float(speedy.split('K')[0]) * 1024
158 | elif 'MB/s' in speedy:
159 | uldl_bytes += float(speedy.split('M')[0]) * 1048576
160 | dlspeed = get_readable_file_size(dlspeed_bytes)
161 | ulspeed = get_readable_file_size(uldl_bytes)
162 | progress += f"\nDL:{dlspeed}ps | UL:{ulspeed}/s \n"
163 | with status_reply_dict_lock:
164 | if msg.message.chat.id in list(status_reply_dict.keys()):
165 | try:
166 | message = status_reply_dict[msg.message.chat.id]
167 | deleteMessage(bot, message)
168 | del status_reply_dict[msg.message.chat.id]
169 | except Exception as e:
170 | LOGGER.error(str(e))
171 | del status_reply_dict[msg.message.chat.id]
172 | pass
173 | if len(progress) == 0:
174 | progress = "Starting DL"
175 | message = sendMessage(progress, bot, msg)
176 | status_reply_dict[msg.message.chat.id] = message
177 |
--------------------------------------------------------------------------------
/bot/modules/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshpreets63/Mirror-Bot/08cd0fc9ecb8460d964e8970cfa8a1268f6faade/bot/modules/__init__.py
--------------------------------------------------------------------------------
/bot/modules/authorize.py:
--------------------------------------------------------------------------------
1 | from bot.helper.telegram_helper.message_utils import sendMessage
2 | from bot import AUTHORIZED_CHATS, SUDO_USERS, dispatcher, DB_URI
3 | from telegram.ext import CommandHandler
4 | from bot.helper.telegram_helper.filters import CustomFilters
5 | from bot.helper.telegram_helper.bot_commands import BotCommands
6 | from bot.helper.ext_utils.db_handler import DbManger
7 |
8 |
9 | def authorize(update, context):
10 | reply_message = None
11 | message_ = None
12 | reply_message = update.message.reply_to_message
13 | message_ = update.message.text.split(' ')
14 | if len(message_) == 2:
15 | user_id = int(message_[1])
16 | if user_id in AUTHORIZED_CHATS:
17 | msg = 'User Already Authorized'
18 | elif DB_URI is not None:
19 | msg = DbManger().db_auth(user_id)
20 | else:
21 | with open('authorized_chats.txt', 'a') as file:
22 | file.write(f'{user_id}\n')
23 | AUTHORIZED_CHATS.add(user_id)
24 | msg = 'User Authorized'
25 | elif reply_message is None:
26 | # Trying to authorize a chat
27 | chat_id = update.effective_chat.id
28 | if chat_id in AUTHORIZED_CHATS:
29 | msg = 'Chat Already Authorized'
30 |
31 | elif DB_URI is not None:
32 | msg = DbManger().db_auth(chat_id)
33 | else:
34 | with open('authorized_chats.txt', 'a') as file:
35 | file.write(f'{chat_id}\n')
36 | AUTHORIZED_CHATS.add(chat_id)
37 | msg = 'Chat Authorized'
38 | else:
39 | # Trying to authorize someone by replying
40 | user_id = reply_message.from_user.id
41 | if user_id in AUTHORIZED_CHATS:
42 | msg = 'User Already Authorized'
43 | elif DB_URI is not None:
44 | msg = DbManger().db_auth(user_id)
45 | else:
46 | with open('authorized_chats.txt', 'a') as file:
47 | file.write(f'{user_id}\n')
48 | AUTHORIZED_CHATS.add(user_id)
49 | msg = 'User Authorized'
50 | sendMessage(msg, context.bot, update)
51 |
52 |
53 | def unauthorize(update, context):
54 | reply_message = None
55 | message_ = None
56 | reply_message = update.message.reply_to_message
57 | message_ = update.message.text.split(' ')
58 | if len(message_) == 2:
59 | user_id = int(message_[1])
60 | if user_id in AUTHORIZED_CHATS:
61 | if DB_URI is not None:
62 | msg = DbManger().db_unauth(user_id)
63 | else:
64 | AUTHORIZED_CHATS.remove(user_id)
65 | msg = 'User Unauthorized'
66 | else:
67 | msg = 'User Already Unauthorized'
68 | elif reply_message is None:
69 | # Trying to unauthorize a chat
70 | chat_id = update.effective_chat.id
71 | if chat_id in AUTHORIZED_CHATS:
72 | if DB_URI is not None:
73 | msg = DbManger().db_unauth(chat_id)
74 | else:
75 | AUTHORIZED_CHATS.remove(chat_id)
76 | msg = 'Chat Unauthorized'
77 | else:
78 | msg = 'Chat Already Unauthorized'
79 | else:
80 | # Trying to authorize someone by replying
81 | user_id = reply_message.from_user.id
82 | if user_id in AUTHORIZED_CHATS:
83 | if DB_URI is not None:
84 | msg = DbManger().db_unauth(user_id)
85 | else:
86 | AUTHORIZED_CHATS.remove(user_id)
87 | msg = 'User Unauthorized'
88 | else:
89 | msg = 'User Already Unauthorized'
90 | with open('authorized_chats.txt', 'a') as file:
91 | file.truncate(0)
92 | for i in AUTHORIZED_CHATS:
93 | file.write(f'{i}\n')
94 | sendMessage(msg, context.bot, update)
95 |
96 |
97 | def addSudo(update, context):
98 | reply_message = None
99 | message_ = None
100 | reply_message = update.message.reply_to_message
101 | message_ = update.message.text.split(' ')
102 | if len(message_) == 2:
103 | user_id = int(message_[1])
104 | if user_id in SUDO_USERS:
105 | msg = 'Already Sudo'
106 | elif DB_URI is not None:
107 | msg = DbManger().db_addsudo(user_id)
108 | else:
109 | with open('sudo_users.txt', 'a') as file:
110 | file.write(f'{user_id}\n')
111 | SUDO_USERS.add(user_id)
112 | msg = 'Promoted as Sudo'
113 | elif reply_message is None:
114 | msg = "Give ID or Reply To message of whom you want to Promote"
115 | else:
116 | # Trying to authorize someone by replying
117 | user_id = reply_message.from_user.id
118 | if user_id in SUDO_USERS:
119 | msg = 'Already Sudo'
120 | elif DB_URI is not None:
121 | msg = DbManger().db_addsudo(user_id)
122 | else:
123 | with open('sudo_users.txt', 'a') as file:
124 | file.write(f'{user_id}\n')
125 | SUDO_USERS.add(user_id)
126 | msg = 'Promoted as Sudo'
127 | sendMessage(msg, context.bot, update)
128 |
129 |
130 | def removeSudo(update, context):
131 | reply_message = None
132 | message_ = None
133 | reply_message = update.message.reply_to_message
134 | message_ = update.message.text.split(' ')
135 | if len(message_) == 2:
136 | user_id = int(message_[1])
137 | if user_id in SUDO_USERS:
138 | if DB_URI is not None:
139 | msg = DbManger().db_rmsudo(user_id)
140 | else:
141 | SUDO_USERS.remove(user_id)
142 | msg = 'Demoted'
143 | else:
144 | msg = 'Not a Sudo'
145 | elif reply_message is None:
146 | msg = "Give ID or Reply To message of whom you want to remove from Sudo"
147 | else:
148 | user_id = reply_message.from_user.id
149 | if user_id in SUDO_USERS:
150 | if DB_URI is not None:
151 | msg = DbManger().db_rmsudo(user_id)
152 | else:
153 | SUDO_USERS.remove(user_id)
154 | msg = 'Demoted'
155 | else:
156 | msg = 'Not a Sudo'
157 | if DB_URI is None:
158 | with open('sudo_users.txt', 'a') as file:
159 | file.truncate(0)
160 | for i in SUDO_USERS:
161 | file.write(f'{i}\n')
162 | sendMessage(msg, context.bot, update)
163 |
164 |
165 | def sendAuthChats(update, context):
166 | user = sudo = ''
167 | user += '\n'.join(str(id) for id in AUTHORIZED_CHATS)
168 | sudo += '\n'.join(str(id) for id in SUDO_USERS)
169 | sendMessage(f'Authorized Chats\n{user}
\nSudo Users\n{sudo}
', context.bot, update)
170 |
171 |
172 | send_auth_handler = CommandHandler(command=BotCommands.AuthorizedUsersCommand, callback=sendAuthChats,
173 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
174 | authorize_handler = CommandHandler(command=BotCommands.AuthorizeCommand, callback=authorize,
175 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
176 | unauthorize_handler = CommandHandler(command=BotCommands.UnAuthorizeCommand, callback=unauthorize,
177 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
178 | addsudo_handler = CommandHandler(command=BotCommands.AddSudoCommand, callback=addSudo,
179 | filters=CustomFilters.owner_filter, run_async=True)
180 | removesudo_handler = CommandHandler(command=BotCommands.RmSudoCommand, callback=removeSudo,
181 | filters=CustomFilters.owner_filter, run_async=True)
182 |
183 | dispatcher.add_handler(send_auth_handler)
184 | dispatcher.add_handler(authorize_handler)
185 | dispatcher.add_handler(unauthorize_handler)
186 | dispatcher.add_handler(addsudo_handler)
187 | dispatcher.add_handler(removesudo_handler)
--------------------------------------------------------------------------------
/bot/modules/cancel_mirror.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler
2 | from bot import download_dict, dispatcher, download_dict_lock, DOWNLOAD_DIR
3 | from bot.helper.ext_utils.fs_utils import clean_download
4 | from bot.helper.telegram_helper.bot_commands import BotCommands
5 | from bot.helper.telegram_helper.filters import CustomFilters
6 | from bot.helper.telegram_helper.message_utils import *
7 |
8 | from time import sleep
9 | from bot.helper.ext_utils.bot_utils import getDownloadByGid, MirrorStatus, getAllDownload
10 |
11 |
12 | def cancel_mirror(update, context):
13 | args = update.message.text.split(" ", maxsplit=1)
14 | mirror_message = None
15 | if len(args) > 1:
16 | gid = args[1]
17 | dl = getDownloadByGid(gid)
18 | if not dl:
19 | sendMessage(f"GID: {gid}
Not Found.", context.bot, update)
20 | return
21 | mirror_message = dl.message
22 | elif update.message.reply_to_message:
23 | mirror_message = update.message.reply_to_message
24 | with download_dict_lock:
25 | keys = list(download_dict.keys())
26 | try:
27 | dl = download_dict[mirror_message.message_id]
28 | except:
29 | pass
30 | if len(args) == 1:
31 | msg = f"Please reply to the /{BotCommands.MirrorCommand}
message which was used to start the download or send /{BotCommands.CancelMirror} GID
to cancel it!"
32 | if mirror_message and mirror_message.message_id not in keys:
33 | if BotCommands.MirrorCommand in mirror_message.text or \
34 | BotCommands.ZipMirrorCommand in mirror_message.text or \
35 | BotCommands.TarMirrorCommand in mirror_message.text or \
36 | BotCommands.UnzipMirrorCommand in mirror_message.text:
37 | msg1 = "Mirror Already Have Been Cancelled"
38 | sendMessage(msg1, context.bot, update)
39 | return
40 | else:
41 | sendMessage(msg, context.bot, update)
42 | return
43 | elif not mirror_message:
44 | sendMessage(msg, context.bot, update)
45 | return
46 | if dl.status() == MirrorStatus.STATUS_ARCHIVING:
47 | sendMessage("Archival in Progress, You Can't Cancel It.", context.bot, update)
48 | elif dl.status() == MirrorStatus.STATUS_EXTRACTING:
49 | sendMessage("Extract in Progress, You Can't Cancel It.", context.bot, update)
50 | elif dl.status() == MirrorStatus.STATUS_SPLITTING:
51 | sendMessage("Split in Progress, You Can't Cancel It.", context.bot, update)
52 | else:
53 | dl.download().cancel_download()
54 | sleep(3) # incase of any error with ondownloaderror listener
55 | clean_download(f'{DOWNLOAD_DIR}{mirror_message.message_id}/')
56 |
57 |
58 | def cancel_all(update, context):
59 | count = 0
60 | gid = 0
61 | while True:
62 | dl = getAllDownload()
63 | if dl:
64 | if dl.gid() != gid:
65 | gid = dl.gid()
66 | dl.download().cancel_download()
67 | count += 1
68 | sleep(0.3)
69 | else:
70 | break
71 | sendMessage(f'{count} Download(s) has been Cancelled!', context.bot, update)
72 |
73 |
74 |
75 | cancel_mirror_handler = CommandHandler(BotCommands.CancelMirror, cancel_mirror,
76 | filters=(CustomFilters.authorized_chat | CustomFilters.authorized_user) & CustomFilters.mirror_owner_filter, run_async=True)
77 | cancel_all_handler = CommandHandler(BotCommands.CancelAllCommand, cancel_all,
78 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
79 | dispatcher.add_handler(cancel_all_handler)
80 | dispatcher.add_handler(cancel_mirror_handler)
81 |
--------------------------------------------------------------------------------
/bot/modules/clone.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler
2 | from telegram import ParseMode
3 | from bot.helper.mirror_utils.upload_utils import gdriveTools
4 | from bot.helper.telegram_helper.message_utils import *
5 | from bot.helper.telegram_helper.filters import CustomFilters
6 | from bot.helper.telegram_helper.bot_commands import BotCommands
7 | from bot.helper.mirror_utils.status_utils.clone_status import CloneStatus
8 | from bot import dispatcher, LOGGER, STOP_DUPLICATE_CLONE, download_dict, download_dict_lock, Interval, DOWNLOAD_STATUS_UPDATE_INTERVAL, CLONE_LIMIT, LOGS_CHATS
9 | from bot.helper.ext_utils.bot_utils import setInterval, check_limit
10 | from bot.helper.ext_utils.bot_utils import get_readable_file_size, is_gdrive_link, is_gdtot_link, new_thread
11 | from bot.helper.mirror_utils.download_utils.direct_link_generator import gdtot
12 | from bot.helper.ext_utils.exceptions import DirectDownloadLinkException
13 | import random
14 | import string
15 |
16 | @new_thread
17 | def cloneNode(update, context):
18 | args = update.message.text.split(" ", maxsplit=1)
19 | reply_to = update.message.reply_to_message
20 | if len(args) > 1:
21 | link = args[1]
22 | elif reply_to is not None:
23 | link = reply_to.text
24 | else:
25 | link = ''
26 | gdtot_link = is_gdtot_link(link)
27 | if gdtot_link:
28 | try:
29 | msg = sendMessage(f"Bypassing GDTOT Link.", context.bot, update)
30 | link = gdtot(link)
31 | deleteMessage(context.bot, msg)
32 | except DirectDownloadLinkException as e:
33 | deleteMessage(context.bot, msg)
34 | return sendMessage(str(e), context.bot, update)
35 | if is_gdrive_link(link):
36 | msg = sendMessage(f"Checking Drive Link...", context.bot, update)
37 | gd = gdriveTools.GoogleDriveHelper()
38 | res, size, name, files = gd.clonehelper(link)
39 | deleteMessage(context.bot, msg)
40 | if res != "":
41 | sendMessage(res, context.bot, update)
42 | return
43 | if STOP_DUPLICATE_CLONE:
44 | LOGGER.info('Checking File/Folder if already in Drive...')
45 | smsg, button = gd.drive_list(name)
46 | if smsg:
47 | msg3 = "File/Folder is already available in Drive.\nHere are the search results:"
48 | sendMarkup(msg3, context.bot, update, button)
49 | return
50 | if CLONE_LIMIT is not None:
51 | result = check_limit(size, CLONE_LIMIT)
52 | if result:
53 | msg2 = f'Failed, Clone limit is {CLONE_LIMIT}.\nYour File/Folder size is {get_readable_file_size(size)}.'
54 | sendMessage(msg2, context.bot, update)
55 | return
56 | if files < 15:
57 | msg = sendMessage(f"Cloning: {link}
", context.bot, update)
58 | result, button = gd.clone(link)
59 | deleteMessage(context.bot, msg)
60 | else:
61 | drive = gdriveTools.GoogleDriveHelper(name)
62 | gid = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=12))
63 | clone_status = CloneStatus(drive, size, update, gid)
64 | with download_dict_lock:
65 | download_dict[update.message.message_id] = clone_status
66 | if len(Interval) == 0:
67 | Interval.append(setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages))
68 | sendStatusMessage(update, context.bot)
69 | result, button = drive.clone(link)
70 | with download_dict_lock:
71 | del download_dict[update.message.message_id]
72 | count = len(download_dict)
73 | try:
74 | if count == 0:
75 | Interval[0].cancel()
76 | del Interval[0]
77 | delete_all_messages()
78 | else:
79 | update_all_messages()
80 | except IndexError:
81 | pass
82 | if update.message.from_user.username:
83 | uname = f'@{update.message.from_user.username}'
84 | else:
85 | uname = f'{update.message.from_user.first_name}'
86 | if uname is not None:
87 | cc = f'\n\ncc: {uname}'
88 | men = f'{uname} '
89 | if button in ["cancelled", ""]:
90 | sendMessage(men + result, context.bot, update)
91 | else:
92 | sendMarkup(result + cc, context.bot, update, button)
93 | if LOGS_CHATS:
94 | try:
95 | for i in LOGS_CHATS:
96 | msg1 = f'File Cloned: {name}
\n'
97 | msg1 += f'Size: {get_readable_file_size(size)}\n'
98 | msg1 += f'By: {uname}\n'
99 | bot.sendMessage(chat_id=i, text=msg1, reply_markup=button, parse_mode=ParseMode.HTML)
100 | except Exception as e:
101 | LOGGER.warning(e)
102 | if gdtot_link:
103 | gd.deletefile(link)
104 | else:
105 | sendMessage('Provide G-Drive Or Gdtot Shareable Link to Clone.', context.bot, update)
106 |
107 | clone_handler = CommandHandler(BotCommands.CloneCommand, cloneNode, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
108 | dispatcher.add_handler(clone_handler)
--------------------------------------------------------------------------------
/bot/modules/count.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler, run_async
2 | from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
3 | from bot.helper.telegram_helper.message_utils import deleteMessage, sendMessage
4 | from bot.helper.telegram_helper.filters import CustomFilters
5 | from bot.helper.telegram_helper.bot_commands import BotCommands
6 | from bot.helper.ext_utils.bot_utils import get_readable_file_size, is_gdrive_link, is_gdtot_link, new_thread
7 | from bot.helper.mirror_utils.download_utils.direct_link_generator import gdtot
8 | from bot.helper.ext_utils.exceptions import DirectDownloadLinkException
9 | from bot import dispatcher
10 |
11 | @new_thread
12 | def countNode(update,context):
13 | args = update.message.text.split(" ",maxsplit=1)
14 | reply_to = update.message.reply_to_message
15 | if len(args) > 1:
16 | link = args[1]
17 | elif reply_to is not None:
18 | link = reply_to.text
19 | else:
20 | link = ''
21 | gdtot_link = is_gdtot_link(link)
22 | if gdtot_link:
23 | try:
24 | msg = sendMessage(f"Bypassing GDTOT Link.", context.bot, update)
25 | link = gdtot(link)
26 | deleteMessage(context.bot, msg)
27 | except DirectDownloadLinkException as e:
28 | deleteMessage(context.bot, msg)
29 | return sendMessage(str(e), context.bot, update)
30 | if is_gdrive_link(link):
31 | msg = sendMessage(f"Counting: {link}
",context.bot,update)
32 | gd = GoogleDriveHelper()
33 | result = gd.count(link)
34 | deleteMessage(context.bot,msg)
35 | sendMessage(result,context.bot,update)
36 | if gdtot_link:
37 | gd.deletefile(link)
38 | else:
39 | sendMessage("Provide G-Drive Or Gdtot Shareable Link to Count.",context.bot,update)
40 |
41 | count_handler = CommandHandler(BotCommands.CountCommand,countNode,filters=CustomFilters.authorized_chat | CustomFilters.authorized_user)
42 | dispatcher.add_handler(count_handler)
43 |
--------------------------------------------------------------------------------
/bot/modules/delete.py:
--------------------------------------------------------------------------------
1 | import threading
2 |
3 | from telegram.ext import CommandHandler
4 |
5 | from bot import LOGGER, dispatcher
6 | from bot.helper.mirror_utils.upload_utils import gdriveTools
7 | from bot.helper.telegram_helper.bot_commands import BotCommands
8 | from bot.helper.telegram_helper.filters import CustomFilters
9 | from bot.helper.telegram_helper.message_utils import auto_delete_message, sendMessage
10 |
11 |
12 | def deletefile(update, context):
13 | msg_args = update.message.text.split(None, 1)
14 | msg = ""
15 | try:
16 | link = msg_args[1]
17 | LOGGER.info(link)
18 | except IndexError:
19 | msg = "send a link along with command"
20 |
21 | if msg == "":
22 | drive = gdriveTools.GoogleDriveHelper()
23 | msg = drive.deletefile(link)
24 | LOGGER.info(f"this is msg : {msg}")
25 | reply_message = sendMessage(msg, context.bot, update)
26 |
27 | threading.Thread(
28 | target=auto_delete_message, args=(context.bot, update.message, reply_message)
29 | ).start()
30 |
31 |
32 | delete_handler = CommandHandler(
33 | command=BotCommands.deleteCommand,
34 | callback=deletefile,
35 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user,
36 | run_async=True,
37 | )
38 | dispatcher.add_handler(delete_handler)
39 |
--------------------------------------------------------------------------------
/bot/modules/leech_settings.py:
--------------------------------------------------------------------------------
1 | # Implement By - @anasty17 (https://github.com/SlamDevs/slam-mirrorbot/commit/d888a1e7237f4633c066f7c2bbfba030b83ad616)
2 | # (c) https://github.com/SlamDevs/slam-mirrorbot
3 | # All rights reserved
4 |
5 | import os
6 | import threading
7 |
8 | from PIL import Image
9 | from telegram.ext import CommandHandler, CallbackQueryHandler
10 | from telegram import InlineKeyboardMarkup
11 |
12 | from bot import AS_DOC_USERS, AS_MEDIA_USERS, dispatcher, AS_DOCUMENT, app, AUTO_DELETE_MESSAGE_DURATION
13 | from bot.helper.telegram_helper.message_utils import sendMessage, sendMarkup, auto_delete_message
14 | from bot.helper.telegram_helper.filters import CustomFilters
15 | from bot.helper.telegram_helper.bot_commands import BotCommands
16 | from bot.helper.telegram_helper import button_build
17 |
18 |
19 | def leechSet(update, context):
20 | user_id = update.message.from_user.id
21 | path = f"Thumbnails/{user_id}.jpg"
22 | msg = f"Leech Type for {user_id} user is "
23 | if (
24 | user_id in AS_DOC_USERS
25 | or user_id not in AS_MEDIA_USERS
26 | and AS_DOCUMENT
27 | ):
28 | msg += "DOCUMENT"
29 | else:
30 | msg += "MEDIA"
31 | msg += "\nCustom Thumbnail "
32 | msg += "exists" if os.path.exists(path) else "not exists"
33 | buttons = button_build.ButtonMaker()
34 | buttons.sbutton("As Document", f"doc {user_id}")
35 | buttons.sbutton("As Media", f"med {user_id}")
36 | buttons.sbutton("Delete Thumbnail", f"thumb {user_id}")
37 | if AUTO_DELETE_MESSAGE_DURATION == -1:
38 | buttons.sbutton("Close", f"closeset {user_id}")
39 | button = InlineKeyboardMarkup(buttons.build_menu(2))
40 | choose_msg = sendMarkup(msg, context.bot, update, button)
41 | threading.Thread(target=auto_delete_message, args=(context.bot, update.message, choose_msg)).start()
42 |
43 | def setLeechType(update, context):
44 | query = update.callback_query
45 | user_id = query.from_user.id
46 | data = query.data
47 | data = data.split(" ")
48 | if user_id != int(data[1]):
49 | query.answer(text="Not Yours!", show_alert=True)
50 | elif data[0] == "doc":
51 | if (
52 | user_id in AS_DOC_USERS
53 | or user_id not in AS_MEDIA_USERS
54 | and AS_DOCUMENT
55 | ):
56 | query.answer(text="Already As Document!", show_alert=True)
57 | elif user_id in AS_MEDIA_USERS:
58 | AS_MEDIA_USERS.remove(user_id)
59 | AS_DOC_USERS.add(user_id)
60 | query.answer(text="Done!", show_alert=True)
61 | else:
62 | AS_DOC_USERS.add(user_id)
63 | query.answer(text="Done!", show_alert=True)
64 | elif data[0] == "med":
65 | if user_id in AS_DOC_USERS:
66 | AS_DOC_USERS.remove(user_id)
67 | AS_MEDIA_USERS.add(user_id)
68 | query.answer(text="Done!", show_alert=True)
69 | elif user_id in AS_MEDIA_USERS or not AS_DOCUMENT:
70 | query.answer(text="Already As Media!", show_alert=True)
71 | else:
72 | AS_MEDIA_USERS.add(user_id)
73 | query.answer(text="Done!", show_alert=True)
74 | elif data[0] == "thumb":
75 | path = f"Thumbnails/{user_id}.jpg"
76 | if os.path.lexists(path):
77 | os.remove(path)
78 | query.answer(text="Done!", show_alert=True)
79 | else:
80 | query.answer(text="No Thumbnail To Delete!", show_alert=True)
81 | elif data[0] == "closeset":
82 | query.message.delete()
83 |
84 | def setThumb(update, context):
85 | user_id = update.message.from_user.id
86 | reply_to = update.message.reply_to_message
87 | if reply_to is not None and reply_to.photo:
88 | path = "Thumbnails"
89 | if not os.path.exists(path):
90 | os.mkdir(path)
91 | photo_msg = app.get_messages(update.message.chat.id, reply_to_message_ids=update.message.message_id)
92 | photo_dir = app.download_media(photo_msg, file_name=path)
93 | des_dir = os.path.join(path, str(user_id) + ".jpg")
94 | # Image.open(photo_dir).convert("RGB").save(photo_dir)
95 | img = Image.open(photo_dir)
96 | img.thumbnail((480, 320))
97 | # img.resize((480, 320))
98 | img.save(des_dir, "JPEG")
99 | os.remove(photo_dir)
100 | sendMessage(f"Custom thumbnail saved for {user_id}
user.", context.bot, update)
101 | else:
102 | sendMessage("Reply to a photo to save custom thumbnail.", context.bot, update)
103 |
104 | leech_set_handler = CommandHandler(BotCommands.LeechSetCommand, leechSet, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
105 | set_thumbnail_handler = CommandHandler(BotCommands.SetThumbCommand, setThumb, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
106 | as_doc_handler = CallbackQueryHandler(setLeechType, pattern="doc", run_async=True)
107 | as_media_handler = CallbackQueryHandler(setLeechType, pattern="med", run_async=True)
108 | del_thumb_handler = CallbackQueryHandler(setLeechType, pattern="thumb", run_async=True)
109 | close_set_handler = CallbackQueryHandler(setLeechType, pattern="closeset", run_async=True)
110 | dispatcher.add_handler(leech_set_handler)
111 | dispatcher.add_handler(as_doc_handler)
112 | dispatcher.add_handler(as_media_handler)
113 | dispatcher.add_handler(close_set_handler)
114 | dispatcher.add_handler(set_thumbnail_handler)
115 | dispatcher.add_handler(del_thumb_handler)
116 |
--------------------------------------------------------------------------------
/bot/modules/list.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler
2 |
3 | from bot import LOGGER, RECURSIVE_SEARCH, dispatcher
4 | from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
5 | from bot.helper.telegram_helper.bot_commands import BotCommands
6 | from bot.helper.telegram_helper.filters import CustomFilters
7 | from bot.helper.telegram_helper.message_utils import editMessage, sendMessage
8 |
9 |
10 | def list_drive(update, context):
11 | try:
12 | search = update.message.text.split(" ", maxsplit=1)[1]
13 | LOGGER.info(f"Searching: {search}")
14 | reply = sendMessage("Searching..... Please wait!", context.bot, update)
15 | gdrive = GoogleDriveHelper(None)
16 | if RECURSIVE_SEARCH:
17 | msg, button = gdrive.uni_drive_list(search)
18 | else:
19 | msg, button = gdrive.drive_list(search)
20 |
21 | if button:
22 | editMessage(msg, reply, button)
23 | else:
24 | editMessage("No result found", reply, button)
25 |
26 | except IndexError:
27 | sendMessage("send a search key along with command", context.bot, update)
28 |
29 |
30 | list_handler = CommandHandler(
31 | BotCommands.ListCommand,
32 | list_drive,
33 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user,
34 | run_async=True,
35 | )
36 | dispatcher.add_handler(list_handler)
--------------------------------------------------------------------------------
/bot/modules/mirror_status.py:
--------------------------------------------------------------------------------
1 | import threading
2 |
3 | from telegram.ext import CommandHandler
4 |
5 | from bot import dispatcher, status_reply_dict, status_reply_dict_lock
6 | from bot.helper.ext_utils.bot_utils import get_readable_message
7 | from bot.helper.telegram_helper.bot_commands import BotCommands
8 | from bot.helper.telegram_helper.filters import CustomFilters
9 | from bot.helper.telegram_helper.message_utils import (
10 | auto_delete_message,
11 | bot,
12 | deleteMessage,
13 | sendMessage,
14 | sendStatusMessage,
15 | )
16 |
17 |
18 | def mirror_status(update, context):
19 | message = get_readable_message()
20 | if len(message) == 0:
21 | message = "No active downloads"
22 | reply_message = sendMessage(message, context.bot, update)
23 | threading.Thread(
24 | target=auto_delete_message, args=(bot, update.message, reply_message)
25 | ).start()
26 | return
27 | index = update.effective_chat.id
28 | with status_reply_dict_lock:
29 | if index in status_reply_dict.keys():
30 | deleteMessage(bot, status_reply_dict[index])
31 | del status_reply_dict[index]
32 | sendStatusMessage(update, context.bot)
33 | deleteMessage(context.bot, update.message)
34 |
35 |
36 | mirror_status_handler = CommandHandler(
37 | BotCommands.StatusCommand,
38 | mirror_status,
39 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user,
40 | run_async=True,
41 | )
42 | dispatcher.add_handler(mirror_status_handler)
43 |
--------------------------------------------------------------------------------
/bot/modules/speedtest.py:
--------------------------------------------------------------------------------
1 | from speedtest import Speedtest
2 | from bot.helper.telegram_helper.filters import CustomFilters
3 | from bot import dispatcher, AUTHORIZED_CHATS
4 | from bot.helper.telegram_helper.bot_commands import BotCommands
5 | from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ParseMode
6 | from telegram.ext import CallbackContext, Filters, CommandHandler
7 |
8 |
9 | def speedtest(update, context):
10 | message = update.effective_message
11 | ed_msg = message.reply_text("Running Speed Test . . . 📈📊")
12 | test = Speedtest()
13 | test.get_best_server()
14 | test.download()
15 | test.upload()
16 | test.results.share()
17 | result = test.results.dict()
18 | path = (result['share'])
19 | string_speed = f'''
20 | 🖥️ Server / Stats of The Machine 🖥️
21 | 💳 Name: {result['server']['name']}
22 | ⛳️ Country: {result['server']['country']}, {result['server']['cc']}
23 |
24 | ✈️ SpeedTest Results 💨
25 | 🔺 Upload: {speed_convert(result['upload'] / 8)}
26 | 🔻 Download: {speed_convert(result['download'] / 8)}
27 | 📶 Ping: {result['ping']} ms
28 | 🏬 ISP: {result['client']['isp']}
29 | '''
30 | ed_msg.delete()
31 | try:
32 | update.effective_message.reply_photo(path, string_speed, parse_mode=ParseMode.HTML)
33 | except:
34 | update.effective_message.reply_text(string_speed, parse_mode=ParseMode.HTML)
35 |
36 | def speed_convert(size):
37 | """Hi human, you can't read bytes?"""
38 | power = 2 ** 10
39 | zero = 0
40 | units = {0: "", 1: "Kb/s", 2: "MB/s", 3: "Gb/s", 4: "Tb/s"}
41 | while size > power:
42 | size /= power
43 | zero += 1
44 | return f"{round(size, 2)} {units[zero]}"
45 |
46 |
47 | SPEED_HANDLER = CommandHandler(BotCommands.SpeedCommand, speedtest,
48 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
49 |
50 | dispatcher.add_handler(SPEED_HANDLER)
--------------------------------------------------------------------------------
/bot/modules/watch.py:
--------------------------------------------------------------------------------
1 | import threading
2 | import re
3 |
4 | from telegram.ext import CommandHandler, CallbackQueryHandler
5 | from telegram import InlineKeyboardMarkup
6 |
7 | from bot import DOWNLOAD_DIR, dispatcher, DOWNLOAD_STATUS_UPDATE_INTERVAL, Interval
8 | from bot.helper.telegram_helper.message_utils import *
9 | from bot.helper.telegram_helper import button_build
10 | from bot.helper.ext_utils.bot_utils import is_url
11 | from bot.helper.ext_utils.bot_utils import setInterval
12 | from bot.helper.ext_utils.bot_utils import get_readable_file_size
13 | from bot.helper.mirror_utils.download_utils.youtube_dl_download_helper import YoutubeDLHelper
14 | from bot.helper.telegram_helper.bot_commands import BotCommands
15 | from bot.helper.telegram_helper.filters import CustomFilters
16 | from .mirror import MirrorListener
17 |
18 | listener_dict = {}
19 |
20 |
21 | def _watch(bot, update, isTar=False, isZip=False, isLeech=False, pswd=None):
22 | mssg = update.message.text
23 | message_args = mssg.split(' ', maxsplit=2)
24 | name_args = mssg.split('|', maxsplit=1)
25 | user_id = update.message.from_user.id
26 | msg_id = update.message.message_id
27 |
28 | try:
29 | link = message_args[1].strip()
30 | if link.startswith("|") or link.startswith("pswd: "):
31 | link = ''
32 | except IndexError:
33 | link = ''
34 | link = re.split(r"pswd:|\|", link)[0]
35 | link = link.strip()
36 |
37 | try:
38 | name = name_args[1]
39 | name = name.split(' pswd: ')[0]
40 | name = name.strip()
41 | except IndexError:
42 | name = ''
43 |
44 | pswdMsg = mssg.split(' pswd: ')
45 | if len(pswdMsg) > 1:
46 | pswd = pswdMsg[1]
47 |
48 | reply_to = update.message.reply_to_message
49 | if reply_to is not None:
50 | link = reply_to.text.strip()
51 |
52 | if not is_url(link):
53 | msg = f"/{BotCommands.WatchCommand} [yt_dl supported link] [quality] |[CustomName] to mirror with youtube_dl.\n\n"
54 | msg += "Note :- Quality and custom name are optional\n\nExample of quality :- audio, 144, 240, 360, 480, 720, 1080, 2160."
55 | msg += "\n\nIf you want to use custom filename, enter it after |"
56 | msg += "\n\nIf you want to add password to zip, enter it after pwsd:"
57 | msg += f"\n\nExample :-\n/{BotCommands.ZipWatchCommand} https://youtu.be/X8Uf3hu0hWY |Raftaar Goat pswd: video123
\n\n"
58 | msg += "This will upload encrypted video zip, whose password will be video123 And Custom name will be Raftaar Goat "
59 | return sendMessage(msg, bot, update)
60 |
61 | listener = MirrorListener(bot, update, isTar, isZip, isLeech=isLeech, pswd=pswd)
62 | listener_dict[msg_id] = listener, user_id, link, name
63 |
64 | buttons = button_build.ButtonMaker()
65 | best_video = "bv*+ba/b"
66 | best_audio = "ba/b"
67 | ydl = YoutubeDLHelper(listener)
68 | try:
69 | result = ydl.extractMetaData(link, name, True)
70 | except Exception as e:
71 | return sendMessage(str(e), bot, update)
72 | if 'entries' in result:
73 | for i in ['144', '240', '360', '480', '720', '1080', '1440', '2160']:
74 | video_format = f"bv*[height<={i}]+ba/b"
75 | buttons.sbutton(str(i), f"qual {msg_id} {video_format} true")
76 | buttons.sbutton("Best Videos", f"qual {msg_id} {best_video} true")
77 | buttons.sbutton("Best Audios", f"qual {msg_id} {best_audio} true")
78 | else:
79 | formats = result.get('formats')
80 |
81 | if formats is not None:
82 | formats_dict = {}
83 |
84 | for frmt in formats:
85 | if not frmt.get('tbr') or not frmt.get('height'):
86 | continue
87 | if frmt.get('fps'):
88 | quality = f"{frmt['height']}p{frmt['fps']}-{frmt['ext']}"
89 | else:
90 | quality = f"{frmt['height']}p-{frmt['ext']}"
91 | if (
92 | quality not in formats_dict
93 | or formats_dict[quality][1] < frmt['tbr']
94 | ):
95 | if frmt.get('filesize'):
96 | size = frmt['filesize']
97 | elif frmt.get('filesize_approx'):
98 | size = frmt['filesize_approx']
99 | else:
100 | size = 0
101 | formats_dict[quality] = [size, frmt['tbr']]
102 |
103 | for forDict in formats_dict:
104 | qual_fps_ext = re.split(r'p|-', forDict, maxsplit=2)
105 | if qual_fps_ext[1] != '':
106 | video_format = f"bv*[height={qual_fps_ext[0]}][fps={qual_fps_ext[1]}][ext={qual_fps_ext[2]}]+ba/b"
107 | else:
108 | video_format = f"bv*[height={qual_fps_ext[0]}][ext={qual_fps_ext[2]}]+ba/b"
109 | buttonName = f"{forDict} ({get_readable_file_size(formats_dict[forDict][0])})"
110 | buttons.sbutton(str(buttonName), f"qual {msg_id} {video_format} f")
111 | buttons.sbutton("Best Video", f"qual {msg_id} {best_video} f")
112 | buttons.sbutton("Best Audio", f"qual {msg_id} {best_audio} f")
113 |
114 | buttons.sbutton("Cancel", f"qual {msg_id} cancel f")
115 | YTBUTTONS = InlineKeyboardMarkup(buttons.build_menu(2))
116 | sendMarkup('Choose video/playlist quality', bot, update, YTBUTTONS)
117 |
118 |
119 | def select_format(update, context):
120 | query = update.callback_query
121 | user_id = query.from_user.id
122 | data = query.data
123 | data = data.split(" ")
124 | task_id = int(data[1])
125 | listener, uid, link, name = listener_dict[task_id]
126 | if user_id != uid:
127 | return query.answer(text="Don't waste your time!", show_alert=True)
128 | elif data[2] != "cancel":
129 | query.answer()
130 | qual = data[2]
131 | playlist = data[3]
132 | ydl = YoutubeDLHelper(listener)
133 | threading.Thread(target=ydl.add_download,args=(link, f'{DOWNLOAD_DIR}{task_id}', name, qual, playlist)).start()
134 | if len(Interval) == 0:
135 | Interval.append(
136 | setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages)
137 | )
138 | del listener_dict[task_id]
139 | query.message.delete()
140 |
141 | def watch(update, context):
142 | _watch(context.bot, update)
143 |
144 | def watchTar(update, context):
145 | _watch(context.bot, update, True)
146 |
147 | def watchZip(update, context):
148 | _watch(context.bot, update, True, True)
149 |
150 | def leechWatch(update, context):
151 | _watch(context.bot, update, isLeech=True)
152 |
153 | def leechWatchZip(update, context):
154 | _watch(context.bot, update, True, True, isLeech=True)
155 |
156 | def leechWatchTar(update, context):
157 | _watch(context.bot, update, True, isLeech=True)
158 |
159 | watch_handler = CommandHandler(BotCommands.WatchCommand, watch,
160 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
161 | zip_watch_handler = CommandHandler(BotCommands.ZipWatchCommand, watchZip,
162 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
163 | tar_watch_handler = CommandHandler(BotCommands.TarWatchCommand, watchTar,
164 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
165 | leech_watch_handler = CommandHandler(BotCommands.LeechWatchCommand, leechWatch,
166 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
167 | leech_zip_watch_handler = CommandHandler(BotCommands.LeechZipWatchCommand, leechWatchZip,
168 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
169 | leech_tar_watch_handler = CommandHandler(BotCommands.LeechTarWatchCommand, leechWatchTar,
170 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
171 | quality_handler = CallbackQueryHandler(select_format, pattern="qual", run_async=True)
172 |
173 | dispatcher.add_handler(watch_handler)
174 | dispatcher.add_handler(zip_watch_handler)
175 | dispatcher.add_handler(tar_watch_handler)
176 | dispatcher.add_handler(leech_watch_handler)
177 | dispatcher.add_handler(leech_zip_watch_handler)
178 | dispatcher.add_handler(leech_tar_watch_handler)
179 | dispatcher.add_handler(quality_handler)
--------------------------------------------------------------------------------
/captain-definition:
--------------------------------------------------------------------------------
1 | {
2 | "schemaVersion": 2,
3 | "dockerfilePath": "./Dockerfile"
4 | }
5 |
--------------------------------------------------------------------------------
/config_sample.env:
--------------------------------------------------------------------------------
1 | #Remove this line before deploying
2 | _____REMOVE_THIS_LINE_____=True
3 |
4 | # ENTER BOT TOKEN (Get your BOT_TOKEN by talking to @botfather)
5 | BOT_TOKEN = ""
6 | GDRIVE_FOLDER_ID = ""
7 | OWNER_ID = ""
8 | DOWNLOAD_DIR = "/home/username/mirror-bot/downloads"
9 | DOWNLOAD_STATUS_UPDATE_INTERVAL = 5
10 | AUTO_DELETE_MESSAGE_DURATION = 20
11 | IS_TEAM_DRIVE = ""
12 | TELEGRAM_API = ""
13 | TELEGRAM_HASH = ""
14 | USE_SERVICE_ACCOUNTS = ""
15 |
16 | # Optional config
17 | ACCOUNTS_ZIP_URL = "" #Enter Direct Links TO Import Service Accounts Directly From Urls Instead Of Adding Files To Repo.( Archive the accounts folder to a zip file.)
18 | TOKEN_PICKLE_URL = "" #Enter Direct Links TO Import Credentials Directly From Urls Instead Of Adding Files To Repo.
19 | AUTHORIZED_CHATS = "" #Separated by space
20 | SUDO_USERS = "" #Separated by space
21 | LOGS_CHATS = "" #Separated by space
22 | DATABASE_URL = ""
23 | IGNORE_PENDING_REQUESTS = ""
24 | INDEX_URL = ""
25 | MEGA_KEY = ""
26 | MEGA_USERNAME = ""
27 | MEGA_PASSWORD = ""
28 | BLOCK_MEGA_LINKS = ""
29 | SHORTENER = ""
30 | SHORTENER_API = ""
31 | STOP_DUPLICATE_CLONE = ""
32 | CLONE_LIMIT = ""
33 | TG_SPLIT_SIZE = "" # leave it empty for max size(2GB)
34 | AS_DOCUMENT = ""
35 | RECURSIVE_SEARCH = "" #T/F And Fill drive_folder File Using Driveid.py Script.
36 | UPTOBOX_TOKEN = ""
37 | # View Link button to open file Index Link in browser instead of direct download link
38 | # You can figure out if it's compatible with your Index code or not, open any video from you Index and check if its URL ends with ?a=view, if yes make it True it will work (Compatible with Bhadoo Drive Index)
39 | VIEW_LINK = ""
40 | # GDTOT COOKIES
41 | PHPSESSID = ""
42 | CRYPT = ""
43 | # Add more buttons (two buttons are already added of file link and index link, you can add extra buttons too, these are optional)
44 | # If you don't know what are below entries, simply leave them, Don't fill anything in them.
45 | BUTTON_THREE_NAME = ""
46 | BUTTON_THREE_URL = ""
47 | BUTTON_FOUR_NAME = ""
48 | BUTTON_FOUR_URL = ""
49 | BUTTON_FIVE_NAME = ""
50 | BUTTON_FIVE_URL = ""
51 |
--------------------------------------------------------------------------------
/drive_folder:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harshpreets63/Mirror-Bot/08cd0fc9ecb8460d964e8970cfa8a1268f6faade/drive_folder
--------------------------------------------------------------------------------
/driveid.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | print("\n\n"\
4 | " Bot can search files recursively, but you have to add the list of drives you want to search.\n"\
5 | " Use the following format: (You can use 'root' in the ID in case you wan to use main drive.)\n"\
6 | " teamdrive NAME --> anything that u likes\n"\
7 | " teamdrive ID --> id of teamdrives in which u likes to search ('root' for main drive)\n"\
8 | " teamdrive INDEX URL --> enter index url for this drive.\n" \
9 | " goto the respective drive and copy the url from address bar\n")
10 | msg = ''
11 | if os.path.exists('drive_folder'):
12 | with open('drive_folder', 'r+') as f:
13 | lines = f.read()
14 | if not re.match(r'^\s*$', lines):
15 | print(lines)
16 | print("\n\n"\
17 | " DO YOU WISH TO KEEP THE ABOVE DETAILS THAT YOU PREVIOUSLY ADDED???? ENTER (y/n)\n"\
18 | " IF NOTHING SHOWS ENTER n")
19 | while (1):
20 | choice = input()
21 | if choice == 'y' or choice == 'Y':
22 | msg = f'{lines}'
23 | break
24 | elif choice == 'n' or choice == 'N':
25 | break
26 | else:
27 | print("\n\n DO YOU WISH TO KEEP THE ABOVE DETAILS ???? y/n <=== this is option ..... OPEN YOUR EYES & READ...")
28 | num = int(input(" How Many Drive/Folder You Likes To Add : "))
29 | count = 1
30 | while count <= num :
31 | print(f"\n > DRIVE - {count}\n")
32 | name = input(" Enter Drive NAME (anything) : ")
33 | id = input(" Enter Drive ID : ")
34 | index = input(" Enter Drive INDEX URL (optional) : ")
35 | if not name or not id:
36 | print("\n\n ERROR : Dont leave the name/id without filling.")
37 | exit(1)
38 | name=name.replace(" ", "_")
39 | if index:
40 | if index[-1] == "/":
41 | index = index[:-1]
42 | else:
43 | index = ''
44 | count+=1
45 | msg += f"{name} {id} {index}\n"
46 | with open('drive_folder', 'w') as file:
47 | file.truncate(0)
48 | file.write(msg)
49 | print("\n\n Done!")
50 |
--------------------------------------------------------------------------------
/extract:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ $# -lt 1 ]; then
4 | echo "Usage: $(basename $0) FILES"
5 | exit 1
6 | fi
7 |
8 | extract() {
9 | arg="$1"
10 | cd "$(dirname "$arg")" || exit
11 | case "$arg" in
12 | *.tar.bz2)
13 | tar xjf "$arg" --one-top-level
14 | local code=$?
15 | ;;
16 | *.tar.gz)
17 | tar xzf "$arg" --one-top-level
18 | local code=$?
19 | ;;
20 | *.bz2)
21 | bunzip2 "$arg"
22 | local code=$?
23 | ;;
24 | *.gz)
25 | gunzip "$arg"
26 | local code=$?
27 | ;;
28 | *.tar)
29 | tar xf "$arg" --one-top-level
30 | local code=$?
31 | ;;
32 | *.tbz2)
33 | (tar xjf "$arg" --one-top-level)
34 | local code=$?
35 | ;;
36 | *.tgz)
37 | tar xzf "$arg" --one-top-level
38 | local code=$?
39 | ;;
40 | *.tar.xz)
41 | a_dir=$(expr "$arg" : '\(.*\).tar.xz')
42 | 7z x "$arg" -o"$a_dir"
43 | local code=$?
44 | ;;
45 | *.zip)
46 | a_dir=$(expr "$arg" : '\(.*\).zip')
47 | 7z x "$arg" -o"$a_dir"
48 | local code=$?
49 | ;;
50 | *.7z)
51 | a_dir=$(expr "$arg" : '\(.*\).7z')
52 | 7z x "$arg" -o"$a_dir"
53 | local code=$?
54 | ;;
55 | *.Z)
56 | uncompress "$arg"
57 | local code=$?
58 | ;;
59 | *.rar)
60 | a_dir=$(expr "$arg" : '\(.*\).rar')
61 | mkdir "$a_dir"
62 | 7z x "$arg" -o"$a_dir"
63 | local code=$?
64 | ;;
65 | *.iso)
66 | a_dir=$(expr "$arg" : '\(.*\).iso')
67 | 7z x "$arg" -o"$a_dir"
68 | local code=$?
69 | ;;
70 | *.wim)
71 | a_dir=$(expr "$arg" : '\(.*\).wim')
72 | 7z x "$arg" -o"$a_dir"
73 | local code=$?
74 | ;;
75 | *.cab)
76 | a_dir=$(expr "$arg" : '\(.*\).cab')
77 | 7z x "$arg" -o"$a_dir"
78 | local code=$?
79 | ;;
80 | *.apm)
81 | a_dir=$(expr "$arg" : '\(.*\).apm')
82 | 7z x "$arg" -o"$a_dir"
83 | local code=$?
84 | ;;
85 | *.arj)
86 | a_dir=$(expr "$arg" : '\(.*\).arj')
87 | 7z x "$arg" -o"$a_dir"
88 | local code=$?
89 | ;;
90 | *.chm)
91 | a_dir=$(expr "$arg" : '\(.*\).chm')
92 | 7z x "$arg" -o"$a_dir"
93 | local code=$?
94 | ;;
95 | *.cpio)
96 | a_dir=$(expr "$arg" : '\(.*\).cpio')
97 | 7z x "$arg" -o"$a_dir"
98 | local code=$?
99 | ;;
100 | *.cramfs)
101 | a_dir=$(expr "$arg" : '\(.*\).cramfs')
102 | 7z x "$arg" -o"$a_dir"
103 | local code=$?
104 | ;;
105 | *.deb)
106 | a_dir=$(expr "$arg" : '\(.*\).deb')
107 | 7z x "$arg" -o"$a_dir"
108 | local code=$?
109 | ;;
110 | *.dmg)
111 | a_dir=$(expr "$arg" : '\(.*\).dmg')
112 | 7z x "$arg" -o"$a_dir"
113 | local code=$?
114 | ;;
115 | *.fat)
116 | a_dir=$(expr "$arg" : '\(.*\).fat')
117 | 7z x "$arg" -o"$a_dir"
118 | local code=$?
119 | ;;
120 | *.hfs)
121 | a_dir=$(expr "$arg" : '\(.*\).hfs')
122 | 7z x "$arg" -o"$a_dir"
123 | local code=$?
124 | ;;
125 | *.lzh)
126 | a_dir=$(expr "$arg" : '\(.*\).lzh')
127 | 7z x "$arg" -o"$a_dir"
128 | local code=$?
129 | ;;
130 | *.lzma)
131 | a_dir=$(expr "$arg" : '\(.*\).lzma')
132 | 7z x "$arg" -o"$a_dir"
133 | local code=$?
134 | ;;
135 | *.lzma2)
136 | a_dir=$(expr "$arg" : '\(.*\).lzma2')
137 | 7z x "$arg" -o"$a_dir"
138 | local code=$?
139 | ;;
140 | *.mbr)
141 | a_dir=$(expr "$arg" : '\(.*\).mbr')
142 | 7z x "$arg" -o"$a_dir"
143 | local code=$?
144 | ;;
145 | *.msi)
146 | a_dir=$(expr "$arg" : '\(.*\).msi')
147 | 7z x "$arg" -o"$a_dir"
148 | local code=$?
149 | ;;
150 | *.mslz)
151 | a_dir=$(expr "$arg" : '\(.*\).mslz')
152 | 7z x "$arg" -o"$a_dir"
153 | local code=$?
154 | ;;
155 | *.nsis)
156 | a_dir=$(expr "$arg" : '\(.*\).nsis')
157 | 7z x "$arg" -o"$a_dir"
158 | local code=$?
159 | ;;
160 | *.ntfs)
161 | a_dir=$(expr "$arg" : '\(.*\).ntfs')
162 | 7z x "$arg" -o"$a_dir"
163 | local code=$?
164 | ;;
165 | *.rpm)
166 | a_dir=$(expr "$arg" : '\(.*\).rpm')
167 | 7z x "$arg" -o"$a_dir"
168 | local code=$?
169 | ;;
170 | *.squashfs)
171 | a_dir=$(expr "$arg" : '\(.*\).squashfs')
172 | 7z x "$arg" -o"$a_dir"
173 | local code=$?
174 | ;;
175 | *.udf)
176 | a_dir=$(expr "$arg" : '\(.*\).udf')
177 | 7z x "$arg" -o"$a_dir"
178 | local code=$?
179 | ;;
180 | *.vhd)
181 | a_dir=$(expr "$arg" : '\(.*\).vhd')
182 | 7z x "$arg" -o"$a_dir"
183 | local code=$?
184 | ;;
185 | *.xar)
186 | a_dir=$(expr "$arg" : '\(.*\).xar')
187 | 7z x "$arg" -o"$a_dir"
188 | local code=$?
189 | ;;
190 | *)
191 | echo "'$arg' cannot be extracted via extract()" 1>&2
192 | exit 1
193 | ;;
194 | esac
195 | cd - || exit $?
196 | exit $code
197 | }
198 |
199 | extract "$1"
200 |
--------------------------------------------------------------------------------
/gen_sa_accounts.py:
--------------------------------------------------------------------------------
1 | import errno
2 | import os
3 | import pickle
4 | import sys
5 | from argparse import ArgumentParser
6 | from base64 import b64decode
7 | from glob import glob
8 | from json import loads
9 | from random import choice
10 | from time import sleep
11 |
12 | from google.auth.transport.requests import Request
13 | from google_auth_oauthlib.flow import InstalledAppFlow
14 | from googleapiclient.discovery import build
15 | from googleapiclient.errors import HttpError
16 |
17 | SCOPES = [
18 | "https://www.googleapis.com/auth/drive",
19 | "https://www.googleapis.com/auth/cloud-platform",
20 | "https://www.googleapis.com/auth/iam",
21 | ]
22 | project_create_ops = []
23 | current_key_dump = []
24 | sleep_time = 30
25 |
26 |
27 | # Create count SAs in project
28 | def _create_accounts(service, project, count):
29 | batch = service.new_batch_http_request(callback=_def_batch_resp)
30 | for _ in range(count):
31 | aid = _generate_id("mfc-")
32 | batch.add(
33 | service.projects()
34 | .serviceAccounts()
35 | .create(
36 | name="projects/" + project,
37 | body={"accountId": aid, "serviceAccount": {"displayName": aid}},
38 | )
39 | )
40 | batch.execute()
41 |
42 |
43 | # Create accounts needed to fill project
44 | def _create_remaining_accounts(iam, project):
45 | print("Creating accounts in %s" % project)
46 | sa_count = len(_list_sas(iam, project))
47 | while sa_count != 100:
48 | _create_accounts(iam, project, 100 - sa_count)
49 | sa_count = len(_list_sas(iam, project))
50 |
51 |
52 | # Generate a random id
53 | def _generate_id(prefix="saf-"):
54 | chars = "-abcdefghijklmnopqrstuvwxyz1234567890"
55 | return prefix + "".join(choice(chars) for _ in range(25)) + choice(chars[1:])
56 |
57 |
58 | # List projects using service
59 | def _get_projects(service):
60 | return [i["projectId"] for i in service.projects().list().execute()["projects"]]
61 |
62 |
63 | # Default batch callback handler
64 | def _def_batch_resp(id, resp, exception):
65 | if exception is not None:
66 | if str(exception).startswith("