# This Is A Telegram Bot Written In Python For Mirroring Files On The Internet To Our Beloved Google Drive.
Here Are Some Things To Get You Started.👇 ## 👉[All The Feature Of This Bot Or What This Bot Can Do For You.](https://github.com/iamLiquidX/MirrorX/wiki/Feature-Or-What-This-Bot-Can-Do) ## 👉[How To Deploy](https://github.com/iamLiquidX/MirrorX/wiki/How-To-Deploy) ## 👉[Commands To Use The Bot](https://github.com/iamLiquidX/MirrorX/wiki/Commands-To-Use-This-Bot) ## 👉[Modification Guide](https://github.com/iamLiquidX/MirrorX/wiki/Modification) For The Most Recent Changes, Please Check The Changelog.👇 ## 👉[Changelog](https://github.com/iamLiquidX/MirrorX/wiki/Changelog) # Credits 👇 1. [Shivam Jha aka lzzy12](https://github.com/lzzy12) & [JaskaranSM aka Zero Cool](https://github.com/jaskaranSM) - They Built This Bot From Scratch. 2. [Sreeraj V R](https://github.com/SVR666)- Added Inline Button, Added Support For Deleting File/Folders From GDrive, Search Results On Telegra.ph. 3. [Archie](https://github.com/archie9211) - Added Support For Extraction Of Archives, Fixed SSL Handshake Error, Update Trackers Dynamically. 4. [Magneto](https://github.com/magneto261290) - Added Alot Of Customization, Support For Custom File Names, Support For Password Protected Archives, Quality Selection Option In YTDL And Much More. 5. [KenHV](https://github.com/KenHV) - Many Fixes And Imporovements. 6. [Anos](https://github.com/destiny6520) - Modification/Customization Guide. 7. [Viswanath](https://github.com/nenokkadine) - Fixes & Improvements, Dockerfile Clean Up, DHT Support In Aria. 8. [breakdowns](https://github.com/breakdowns) - Source Code For Count,Zip/Unzip GDrive Links & Fembed. ================================================ FILE: bin/MirrorX ================================================ #!/bin/bash pip3 install -U MirrorX MirrorXBot ================================================ FILE: bin/extract ================================================ #!/bin/bash if [ $# -lt 1 ]; then echo "Usage: $(basename $0) FILES" exit 1 fi extract() { arg="$1" cd "$(dirname "$arg")" || exit case "$arg" in *.tar.bz2) tar xjf "$arg" --one-top-level local code=$? ;; *.tar.gz) tar xzf "$arg" --one-top-level local code=$? ;; *.bz2) bunzip2 "$arg" local code=$? ;; *.gz) gunzip "$arg" local code=$? ;; *.tar) tar xf "$arg" --one-top-level local code=$? ;; *.tbz2) (tar xjf "$arg" --one-top-level) local code=$? ;; *.tgz) tar xzf "$arg" --one-top-level local code=$? ;; *.zip) a_dir=$(expr "$arg" : '\(.*\).zip') 7z x "$arg" -o"$a_dir" local code=$? ;; *.7z) a_dir=$(expr "$arg" : '\(.*\).7z') 7z x "$arg" -o"$a_dir" local code=$? ;; *.Z) uncompress "$arg" local code=$? ;; *.rar) a_dir=$(expr "$arg" : '\(.*\).rar') mkdir "$a_dir" 7z x "$arg" -o"$a_dir" local code=$? ;; *.iso) a_dir=$(expr "$arg" : '\(.*\).iso') 7z x "$arg" -o"$a_dir" local code=$? ;; *.wim) a_dir=$(expr "$arg" : '\(.*\).wim') 7z x "$arg" -o"$a_dir" local code=$? ;; *.cab) a_dir=$(expr "$arg" : '\(.*\).cab') 7z x "$arg" -o"$a_dir" local code=$? ;; *.apm) a_dir=$(expr "$arg" : '\(.*\).apm') 7z x "$arg" -o"$a_dir" local code=$? ;; *.arj) a_dir=$(expr "$arg" : '\(.*\).arj') 7z x "$arg" -o"$a_dir" local code=$? ;; *.chm) a_dir=$(expr "$arg" : '\(.*\).chm') 7z x "$arg" -o"$a_dir" local code=$? ;; *.cpio) a_dir=$(expr "$arg" : '\(.*\).cpio') 7z x "$arg" -o"$a_dir" local code=$? ;; *.cramfs) a_dir=$(expr "$arg" : '\(.*\).cramfs') 7z x "$arg" -o"$a_dir" local code=$? ;; *.deb) a_dir=$(expr "$arg" : '\(.*\).deb') 7z x "$arg" -o"$a_dir" local code=$? ;; *.dmg) a_dir=$(expr "$arg" : '\(.*\).dmg') 7z x "$arg" -o"$a_dir" local code=$? ;; *.fat) a_dir=$(expr "$arg" : '\(.*\).fat') 7z x "$arg" -o"$a_dir" local code=$? ;; *.hfs) a_dir=$(expr "$arg" : '\(.*\).hfs') 7z x "$arg" -o"$a_dir" local code=$? ;; *.lzh) a_dir=$(expr "$arg" : '\(.*\).lzh') 7z x "$arg" -o"$a_dir" local code=$? ;; *.lzma) a_dir=$(expr "$arg" : '\(.*\).lzma') 7z x "$arg" -o"$a_dir" local code=$? ;; *.lzma2) a_dir=$(expr "$arg" : '\(.*\).lzma2') 7z x "$arg" -o"$a_dir" local code=$? ;; *.mbr) a_dir=$(expr "$arg" : '\(.*\).mbr') 7z x "$arg" -o"$a_dir" local code=$? ;; *.msi) a_dir=$(expr "$arg" : '\(.*\).msi') 7z x "$arg" -o"$a_dir" local code=$? ;; *.mslz) a_dir=$(expr "$arg" : '\(.*\).mslz') 7z x "$arg" -o"$a_dir" local code=$? ;; *.nsis) a_dir=$(expr "$arg" : '\(.*\).nsis') 7z x "$arg" -o"$a_dir" local code=$? ;; *.ntfs) a_dir=$(expr "$arg" : '\(.*\).ntfs') 7z x "$arg" -o"$a_dir" local code=$? ;; *.rpm) a_dir=$(expr "$arg" : '\(.*\).rpm') 7z x "$arg" -o"$a_dir" local code=$? ;; *.squashfs) a_dir=$(expr "$arg" : '\(.*\).squashfs') 7z x "$arg" -o"$a_dir" local code=$? ;; *.udf) a_dir=$(expr "$arg" : '\(.*\).udf') 7z x "$arg" -o"$a_dir" local code=$? ;; *.vhd) a_dir=$(expr "$arg" : '\(.*\).vhd') 7z x "$arg" -o"$a_dir" local code=$? ;; *.xar) a_dir=$(expr "$arg" : '\(.*\).xar') 7z x "$arg" -o"$a_dir" local code=$? ;; *) echo "'$arg' cannot be extracted via extract()" 1>&2 exit 1 ;; esac cd - || exit $? exit $code } extract "$1" ================================================ FILE: bin/pextract ================================================ #!/bin/bash if [ $# -lt 1 ]; then echo "Usage: $(basename $0) FILES" exit 1 fi extract() { arg="$1" pswd="$2" cd "$(dirname "$arg")" || exit case "$arg" in *.tar.bz2) tar xjf "$arg" --one-top-level local code=$? ;; *.tar.gz) tar xzf "$arg" --one-top-level local code=$? ;; *.bz2) bunzip2 "$arg" local code=$? ;; *.gz) gunzip "$arg" local code=$? ;; *.tar) tar xf "$arg" --one-top-level local code=$? ;; *.tbz2) (tar xjf "$arg" --one-top-level) local code=$? ;; *.tgz) tar xzf "$arg" --one-top-level local code=$? ;; *.zip) a_dir=$(expr "$arg" : '\(.*\).zip') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.7z) a_dir=$(expr "$arg" : '\(.*\).7z') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.Z) uncompress "$arg" local code=$? ;; *.rar) a_dir=$(expr "$arg" : '\(.*\).rar') mkdir "$a_dir" 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.iso) a_dir=$(expr "$arg" : '\(.*\).iso') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.wim) a_dir=$(expr "$arg" : '\(.*\).wim') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.cab) a_dir=$(expr "$arg" : '\(.*\).cab') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.apm) a_dir=$(expr "$arg" : '\(.*\).apm') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.arj) a_dir=$(expr "$arg" : '\(.*\).arj') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.chm) a_dir=$(expr "$arg" : '\(.*\).chm') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.cpio) a_dir=$(expr "$arg" : '\(.*\).cpio') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.cramfs) a_dir=$(expr "$arg" : '\(.*\).cramfs') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.deb) a_dir=$(expr "$arg" : '\(.*\).deb') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.dmg) a_dir=$(expr "$arg" : '\(.*\).dmg') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.fat) a_dir=$(expr "$arg" : '\(.*\).fat') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.hfs) a_dir=$(expr "$arg" : '\(.*\).hfs') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.lzh) a_dir=$(expr "$arg" : '\(.*\).lzh') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.lzma) a_dir=$(expr "$arg" : '\(.*\).lzma') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.lzma2) a_dir=$(expr "$arg" : '\(.*\).lzma2') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.mbr) a_dir=$(expr "$arg" : '\(.*\).mbr') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.msi) a_dir=$(expr "$arg" : '\(.*\).msi') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.mslz) a_dir=$(expr "$arg" : '\(.*\).mslz') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.nsis) a_dir=$(expr "$arg" : '\(.*\).nsis') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.ntfs) a_dir=$(expr "$arg" : '\(.*\).ntfs') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.rpm) a_dir=$(expr "$arg" : '\(.*\).rpm') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.squashfs) a_dir=$(expr "$arg" : '\(.*\).squashfs') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.udf) a_dir=$(expr "$arg" : '\(.*\).udf') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.vhd) a_dir=$(expr "$arg" : '\(.*\).vhd') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *.xar) a_dir=$(expr "$arg" : '\(.*\).xar') 7z x "$arg" -o"$a_dir" -p"$pswd" local code=$? ;; *) echo "'$arg' cannot be extracted via extract()" 1>&2 exit 1 ;; esac cd - || exit $? exit $code } extract "$1" "$2" ================================================ FILE: bot/__init__.py ================================================ import logging import os import threading import time import random import string import subprocess import pkgutil import pathlib import sys import aria2p import requests import telegram.ext as tg from dotenv import load_dotenv from pyrogram import Client from telegraph import Telegraph from megasdkrestclient import MegaSdkRestClient, errors as mega_err import socket import faulthandler faulthandler.enable() socket.setdefaulttimeout(600) botStartTime = time.time() if os.path.exists('log.txt'): with open('log.txt', 'r+') as f: f.truncate(0) logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[logging.FileHandler('log.txt'), logging.StreamHandler()], level=logging.INFO) load_dotenv('config.env') Interval = [] def getConfig(name: str): return os.environ[name] LOGGER = logging.getLogger(__name__) try: if bool(getConfig('_____REMOVE_THIS_LINE_____')): logging.error('The README.md file there to be read! Exiting now!') exit() except KeyError: pass CWD = os.getcwd() ariaconfig = pkgutil.get_data("bot", "data/aria.conf").decode() dhtfile = pkgutil.get_data("bot", "data/dht.dat") dht6file = pkgutil.get_data("bot", "data/dht6.dat") with open("dht.dat", "wb+") as dht: dht.write(dhtfile) with open("dht6.dat", "wb+") as dht6: dht6.write(dhtfile) ariaconfig = ariaconfig.replace("/currentwd", str(CWD)) try: max_dl = getConfig("MAX_CONCURRENT_DOWNLOADS") except KeyError: max_dl = "4" tracker_list = requests.get("https://raw.githubusercontent.com/XIU2/TrackersListCollection/master/all_aria2.txt").text ariaconfig += f"\nmax-concurrent-downloads={max_dl}\nbt-tracker={tracker_list}" with open("aria.conf", "w+") as ariaconf: ariaconf.write(ariaconfig) ARIA_CHILD_PROC = None try: ARIA_CHILD_PROC = subprocess.Popen(["aria2c", f"--conf-path={CWD}/aria.conf"]) except FileNotFoundError: LOGGER.error("Please install Aria2c, Exiting..") sys.exit(0) except OSError: LOGGER.error("Aria2c Binary might have got damaged, Please Check and reinstall..") sys.exit(0) time.sleep(1) aria2 = aria2p.API( aria2p.Client( host="http://localhost", port=6800, secret="", ) ) DOWNLOAD_DIR = None BOT_TOKEN = None download_dict_lock = threading.Lock() status_reply_dict_lock = threading.Lock() # Key: update.effective_chat.id # Value: telegram.Message status_reply_dict = {} # Key: update.message.message_id # Value: An object of Status download_dict = {} # Stores list of users and chats the bot is authorized to use in AUTHORIZED_CHATS = set() if os.path.exists('authorized_chats.txt'): with open('authorized_chats.txt', 'r+') as f: lines = f.readlines() for line in lines: # LOGGER.info(line.split()) AUTHORIZED_CHATS.add(int(line.split()[0])) try: achats = getConfig('AUTHORIZED_CHATS') achats = achats.split(" ") for chats in achats: AUTHORIZED_CHATS.add(int(chats)) except: pass try: BOT_TOKEN = getConfig('BOT_TOKEN') parent_id = getConfig('GDRIVE_FOLDER_ID') DOWNLOAD_DIR = getConfig('DOWNLOAD_DIR') if not DOWNLOAD_DIR.endswith("/"): DOWNLOAD_DIR = DOWNLOAD_DIR + '/' if not os.path.exists(DOWNLOAD_DIR): os.makedirs(DOWNLOAD_DIR, 0o777) DOWNLOAD_STATUS_UPDATE_INTERVAL = int(getConfig('DOWNLOAD_STATUS_UPDATE_INTERVAL')) OWNER_ID = int(getConfig('OWNER_ID')) AUTO_DELETE_MESSAGE_DURATION = int(getConfig('AUTO_DELETE_MESSAGE_DURATION')) TELEGRAM_API = getConfig('TELEGRAM_API') TELEGRAM_HASH = getConfig('TELEGRAM_HASH') except KeyError as e: LOGGER.error("One or more env variables missing! Exiting now") sys.exit() # exit() LOGGER.info("Generating USER_SESSION_STRING") app = Client(':memory:', api_id=int(TELEGRAM_API), api_hash=TELEGRAM_HASH, bot_token=BOT_TOKEN) #Generate Telegraph Token sname = ''.join(random.SystemRandom().choices(string.ascii_letters, k=8)) LOGGER.info("Generating Telegraph Token using '" + sname + "' name") telegraph = Telegraph() telegraph.create_account(short_name=sname) telegraph_token = telegraph.get_access_token() LOGGER.info("Telegraph Token Generated: '" + telegraph_token + "'") try: UPTOBOX_TOKEN = getConfig('UPTOBOX_TOKEN') except KeyError: logging.warning('UPTOBOX_TOKEN not provided!') UPTOBOX_TOKEN = None try: MEGA_KEY = getConfig('MEGA_KEY') except KeyError: MEGA_KEY = None LOGGER.info('MEGA API KEY NOT AVAILABLE') MEGA_CHILD_PROC = None if MEGA_KEY is not None: try: MEGA_CHILD_PROC = subprocess.Popen(["megasdkrest", "--apikey", MEGA_KEY]) except FileNotFoundError: LOGGER.error("Please install Megasdkrest Binary, Exiting..") sys.exit(0) except OSError: LOGGER.error("Megasdkrest Binary might have got damaged, Please Check ..") sys.exit(0) time.sleep(3) mega_client = MegaSdkRestClient('http://localhost:6090') try: MEGA_USERNAME = getConfig('MEGA_USERNAME') MEGA_PASSWORD = getConfig('MEGA_PASSWORD') if len(MEGA_USERNAME) > 0 and len(MEGA_PASSWORD) > 0: try: mega_client.login(MEGA_USERNAME, MEGA_PASSWORD) except mega_err.MegaSdkRestClientException as e: logging.error(e.message['message']) exit(0) else: LOGGER.info("Mega API KEY provided but credentials not provided. Starting mega in anonymous mode!") MEGA_USERNAME = None MEGA_PASSWORD = None except KeyError: LOGGER.info("Mega API KEY provided but credentials not provided. Starting mega in anonymous mode!") MEGA_USERNAME = None MEGA_PASSWORD = None else: MEGA_USERNAME = None MEGA_PASSWORD = None try: INDEX_URL = getConfig('INDEX_URL') if len(INDEX_URL) == 0: INDEX_URL = None except KeyError: INDEX_URL = None try: BUTTON_THREE_NAME = getConfig('BUTTON_THREE_NAME') BUTTON_THREE_URL = getConfig('BUTTON_THREE_URL') if len(BUTTON_THREE_NAME) == 0 or len(BUTTON_THREE_URL) == 0: raise KeyError except KeyError: BUTTON_THREE_NAME = None BUTTON_THREE_URL = None try: BUTTON_FOUR_NAME = getConfig('BUTTON_FOUR_NAME') BUTTON_FOUR_URL = getConfig('BUTTON_FOUR_URL') if len(BUTTON_FOUR_NAME) == 0 or len(BUTTON_FOUR_URL) == 0: raise KeyError except KeyError: BUTTON_FOUR_NAME = None BUTTON_FOUR_URL = None try: BUTTON_FIVE_NAME = getConfig('BUTTON_FIVE_NAME') BUTTON_FIVE_URL = getConfig('BUTTON_FIVE_URL') if len(BUTTON_FIVE_NAME) == 0 or len(BUTTON_FIVE_URL) == 0: raise KeyError except KeyError: BUTTON_FIVE_NAME = None BUTTON_FIVE_URL = None try: STOP_DUPLICATE_MIRROR = getConfig('STOP_DUPLICATE_MIRROR') if STOP_DUPLICATE_MIRROR.lower() == 'true': STOP_DUPLICATE_MIRROR = True else: STOP_DUPLICATE_MIRROR = False except KeyError: STOP_DUPLICATE_MIRROR = False try: IS_TEAM_DRIVE = getConfig('IS_TEAM_DRIVE') if IS_TEAM_DRIVE.lower() == 'true': IS_TEAM_DRIVE = True else: IS_TEAM_DRIVE = False except KeyError: IS_TEAM_DRIVE = False try: USE_SERVICE_ACCOUNTS = getConfig('USE_SERVICE_ACCOUNTS') if USE_SERVICE_ACCOUNTS.lower() == 'true': USE_SERVICE_ACCOUNTS = True else: USE_SERVICE_ACCOUNTS = False except KeyError: USE_SERVICE_ACCOUNTS = False try: BLOCK_MEGA_LINKS = getConfig('BLOCK_MEGA_LINKS') if BLOCK_MEGA_LINKS.lower() == 'true': BLOCK_MEGA_LINKS = True else: BLOCK_MEGA_LINKS = False except KeyError: BLOCK_MEGA_LINKS = False try: SHORTENER = getConfig('SHORTENER') SHORTENER_API = getConfig('SHORTENER_API') if len(SHORTENER) == 0 or len(SHORTENER_API) == 0: raise KeyError except KeyError: SHORTENER = None SHORTENER_API = None updater = tg.Updater(token=BOT_TOKEN) bot = updater.bot dispatcher = updater.dispatcher ================================================ FILE: bot/__main__.py ================================================ import os import shutil, psutil import signal from sys import executable import time from telegram.ext import CommandHandler from bot import bot, dispatcher, updater, botStartTime from bot.helper.ext_utils import fs_utils from bot.helper.telegram_helper.bot_commands import BotCommands from bot.helper.telegram_helper.message_utils import * from .helper.ext_utils.bot_utils import get_readable_file_size, get_readable_time from .helper.telegram_helper.filters import CustomFilters from .modules import authorize, list, cancel_mirror, mirror_status, mirror, clone, watch, delete, speedtest, count from pyrogram import idle from bot import app def stats(update, context): currentTime = get_readable_time(time.time() - botStartTime) total, used, free = shutil.disk_usage('.') total = get_readable_file_size(total) used = get_readable_file_size(used) free = get_readable_file_size(free) sent = get_readable_file_size(psutil.net_io_counters().bytes_sent) recv = get_readable_file_size(psutil.net_io_counters().bytes_recv) cpuUsage = psutil.cpu_percent(interval=0.5) memory = psutil.virtual_memory().percent disk = psutil.disk_usage('/').percent stats = f'Bot Uptime:- {currentTime}\n' \ f'Total Disk Space:- {total}\n' \ f'Used:- {used} ' \ f'Free:- {free}\n\n' \ f'Data Usage\nUp:- {sent}\n' \ f'Down:- {recv}\n\n' \ f'CPU: {cpuUsage}% ' \ f'RAM: {memory}% ' \ f'Disk: {disk}%' sendMessage(stats, context.bot, update) def start(update, context): start_string = f''' This is a bot which can mirror all your links to Google drive! Type /{BotCommands.HelpCommand} to get a list of available commands ''' sendMessage(start_string, context.bot, update) def restart(update, context): restart_message = sendMessage("Restarting, Please wait!", context.bot, update) # Save restart message ID and chat ID in order to edit it after restarting with open(".restartmsg", "w") as f: f.truncate(0) f.write(f"{restart_message.chat.id}\n{restart_message.message_id}\n") fs_utils.clean_all() os.execl(executable, executable, "-m", "bot") def ping(update, context): start_time = int(round(time.time() * 1000)) reply = sendMessage("Starting Ping", context.bot, update) end_time = int(round(time.time() * 1000)) editMessage(f'{end_time - start_time} ms', reply) def log(update, context): sendLogFile(context.bot, update) def bot_help(update, context): help_string = f''' /{BotCommands.HelpCommand}: To get this message /{BotCommands.MirrorCommand} [download_url][magnet_link]: Start mirroring the link to google drive.\n /{BotCommands.UnzipMirrorCommand} [download_url][magnet_link] : starts mirroring and if downloaded file is any archive , extracts it to google drive /{BotCommands.CountCommand}: Count files/folders of G-Drive Links /{BotCommands.TarMirrorCommand} [download_url][magnet_link]: start mirroring and upload the archived (.tar) version of the download /{BotCommands.WatchCommand} [youtube-dl supported link]: Mirror through youtube-dl. Click /{BotCommands.WatchCommand} for more help. /{BotCommands.TarWatchCommand} [youtube-dl supported link]: Mirror through youtube-dl and tar before uploading /{BotCommands.CancelMirror} : Reply to the message by which the download was initiated and that download will be cancelled /{BotCommands.StatusCommand}: Shows a status of all the downloads /{BotCommands.ListCommand} [search term]: Searches the search term in the Google drive, if found replies with the link /{BotCommands.StatsCommand}: Show Stats of the machine the bot is hosted on /{BotCommands.AuthorizeCommand}: Authorize a chat or a user to use the bot (Can only be invoked by owner of the bot) /{BotCommands.LogCommand}: Get a log file of the bot. Handy for getting crash reports /{BotCommands.SpeedCommand} : Check Internet Speed Of The Host ''' sendMessage(help_string, context.bot, update) def main(): fs_utils.start_cleanup() # Check if the bot is restarting if os.path.isfile(".restartmsg"): with open(".restartmsg") as f: chat_id, msg_id = map(int, f) bot.edit_message_text("Restarted successfully!", chat_id, msg_id) os.remove(".restartmsg") start_handler = CommandHandler(BotCommands.StartCommand, start, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True) ping_handler = CommandHandler(BotCommands.PingCommand, ping, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True) restart_handler = CommandHandler(BotCommands.RestartCommand, restart, filters=CustomFilters.owner_filter| CustomFilters.authorized_user, run_async=True) help_handler = CommandHandler(BotCommands.HelpCommand, bot_help, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True) stats_handler = CommandHandler(BotCommands.StatsCommand, stats, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True) log_handler = CommandHandler(BotCommands.LogCommand, log, filters=CustomFilters.owner_filter, run_async=True) dispatcher.add_handler(start_handler) dispatcher.add_handler(ping_handler) dispatcher.add_handler(restart_handler) dispatcher.add_handler(help_handler) dispatcher.add_handler(stats_handler) dispatcher.add_handler(log_handler) updater.start_polling() LOGGER.info("Bot Started!") signal.signal(signal.SIGINT, fs_utils.exit_clean_up) app.start() main() idle() ================================================ FILE: bot/helper/__init__.py ================================================ ================================================ FILE: bot/helper/ext_utils/__init__.py ================================================ ================================================ FILE: bot/helper/ext_utils/bot_utils.py ================================================ import logging import re import threading import time from bot.helper.telegram_helper.bot_commands import BotCommands from bot import download_dict, download_dict_lock LOGGER = logging.getLogger(__name__) MAGNET_REGEX = r"magnet:\?xt=urn:btih:[a-zA-Z0-9]*" URL_REGEX = r"(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+" class MirrorStatus: STATUS_UPLOADING = "Uploading" STATUS_DOWNLOADING = "Downloading" STATUS_WAITING = "Queued" STATUS_FAILED = "Failed.Cleaning download" STATUS_CANCELLED = "Cancelled " STATUS_ARCHIVING = "Archiving" STATUS_EXTRACTING = "Extracting" PROGRESS_MAX_SIZE = 100 // 8 PROGRESS_INCOMPLETE = ['█', '█', '█', '█', '█', '█', '█'] SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] class setInterval: def __init__(self, interval, action): self.interval = interval self.action = action self.stopEvent = threading.Event() thread = threading.Thread(target=self.__setInterval) thread.start() def __setInterval(self): nextTime = time.time() + self.interval while not self.stopEvent.wait(nextTime - time.time()): nextTime += self.interval self.action() def cancel(self): self.stopEvent.set() def get_readable_file_size(size_in_bytes) -> str: if size_in_bytes is None: return '0B' index = 0 while size_in_bytes >= 1024: size_in_bytes /= 1024 index += 1 try: return f'{round(size_in_bytes, 2)}{SIZE_UNITS[index]}' except IndexError: return 'File too large' def getDownloadByGid(gid): with download_dict_lock: for dl in download_dict.values(): status = dl.status() if status != MirrorStatus.STATUS_UPLOADING and status != MirrorStatus.STATUS_ARCHIVING \ and status != MirrorStatus.STATUS_EXTRACTING: if dl.gid() == gid: return dl return None def get_progress_bar_string(status): completed = status.processed_bytes() / 8 total = status.size_raw() / 8 if total == 0: p = 0 else: p = round(completed * 100 / total) p = min(max(p, 0), 100) cFull = p // 8 cPart = p % 8 - 1 p_str = '█' * cFull if cPart >= 0: p_str += PROGRESS_INCOMPLETE[cPart] p_str += '░' * (PROGRESS_MAX_SIZE - cFull) p_str = f"[{p_str}]" return p_str def get_readable_message(): with download_dict_lock: msg = "" for download in list(download_dict.values()): msg += f"Name:-{download.name()}"
msg += f"\nStatus:- {download.status()}"
if download.status() != MirrorStatus.STATUS_ARCHIVING and download.status() != MirrorStatus.STATUS_EXTRACTING:
msg += f"\n{get_progress_bar_string(download)} {download.progress()}"
if download.status() == MirrorStatus.STATUS_DOWNLOADING:
msg += f"\nDownloaded:- {get_readable_file_size(download.processed_bytes())} of {download.size()}"
else:
msg += f"\nUploaded:- {get_readable_file_size(download.processed_bytes())} of {download.size()}"
msg += f"\nSpeed:- {download.speed()}, \nETA:- {download.eta()} "
# if hasattr(download, 'is_torrent'):
try:
msg += f"\nInfo: Seeders:- {download.aria_download().num_seeders}" \
f" & Peers:- {download.aria_download().connections}"
except:
pass
if download.status() == MirrorStatus.STATUS_DOWNLOADING:
msg += f"\nTo Stop:- /{BotCommands.CancelMirror} {download.gid()}"
msg += "\n\n"
return msg
def get_readable_time(seconds: int) -> str:
result = ''
(days, remainder) = divmod(seconds, 86400)
days = int(days)
if days != 0:
result += f'{days}d'
(hours, remainder) = divmod(remainder, 3600)
hours = int(hours)
if hours != 0:
result += f'{hours}h'
(minutes, seconds) = divmod(remainder, 60)
minutes = int(minutes)
if minutes != 0:
result += f'{minutes}m'
seconds = int(seconds)
result += f'{seconds}s'
return result
def is_url(url: str):
url = re.findall(URL_REGEX, url)
if url:
return True
return False
def is_magnet(url: str):
magnet = re.findall(MAGNET_REGEX, url)
if magnet:
return True
return False
def is_gdrive_link(url: str):
return "drive.google.com" in url
def is_mega_link(url: str):
return "mega.nz" in url
def get_mega_link_type(url: str):
if "folder" in url:
return "folder"
elif "file" in url:
return "file"
elif "/#F!" in url:
return "folder"
return "file"
def new_thread(fn):
"""To use as decorator to make a function call threaded.
Needs import
from threading import Thread"""
def wrapper(*args, **kwargs):
thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
thread.start()
return thread
return wrapper
================================================
FILE: bot/helper/ext_utils/exceptions.py
================================================
class DirectDownloadLinkException(Exception):
"""Not method found for extracting direct download link from the http link"""
pass
class NotSupportedExtractionArchive(Exception):
"""The archive format use is trying to extract is not supported"""
pass
================================================
FILE: bot/helper/ext_utils/fs_utils.py
================================================
import sys
from bot import aria2, LOGGER, DOWNLOAD_DIR, ARIA_CHILD_PROC, MEGA_CHILD_PROC
import shutil
import os
import pathlib
import magic
import tarfile
from .exceptions import NotSupportedExtractionArchive
def clean_download(path: str):
if os.path.exists(path):
LOGGER.info(f"Cleaning download: {path}")
shutil.rmtree(path)
def start_cleanup():
try:
shutil.rmtree(DOWNLOAD_DIR)
except FileNotFoundError:
pass
def clean_all():
aria2.remove_all(True)
try:
shutil.rmtree(DOWNLOAD_DIR)
except FileNotFoundError:
pass
def exit_clean_up(signal, frame):
try:
LOGGER.info("Please wait, while we clean up the downloads and stop running downloads")
clean_all()
ARIA_CHILD_PROC.kill()
MEGA_CHILD_PROC.kill()
sys.exit(0)
except KeyboardInterrupt:
LOGGER.warning("Force Exiting before the cleanup finishes!")
ARIA_CHILD_PROC.kill()
MEGA_CHILD_PROC.kill()
sys.exit(1)
def get_path_size(path):
if os.path.isfile(path):
return os.path.getsize(path)
total_size = 0
for root, dirs, files in os.walk(path):
for f in files:
abs_path = os.path.join(root, f)
total_size += os.path.getsize(abs_path)
return total_size
def tar(org_path):
tar_path = org_path + ".tar"
path = pathlib.PurePath(org_path)
LOGGER.info(f'Tar: orig_path: {org_path}, tar_path: {tar_path}')
tar = tarfile.open(tar_path, "w")
tar.add(org_path, arcname=path.name)
tar.close()
return tar_path
def get_base_name(orig_path: str):
if orig_path.endswith(".tar.bz2"):
return orig_path.replace(".tar.bz2", "")
elif orig_path.endswith(".tar.gz"):
return orig_path.replace(".tar.gz", "")
elif orig_path.endswith(".bz2"):
return orig_path.replace(".bz2", "")
elif orig_path.endswith(".gz"):
return orig_path.replace(".gz", "")
elif orig_path.endswith(".tar"):
return orig_path.replace(".tar", "")
elif orig_path.endswith(".tbz2"):
return orig_path.replace("tbz2", "")
elif orig_path.endswith(".tgz"):
return orig_path.replace(".tgz", "")
elif orig_path.endswith(".zip"):
return orig_path.replace(".zip", "")
elif orig_path.endswith(".7z"):
return orig_path.replace(".7z", "")
elif orig_path.endswith(".Z"):
return orig_path.replace(".Z", "")
elif orig_path.endswith(".rar"):
return orig_path.replace(".rar", "")
elif orig_path.endswith(".iso"):
return orig_path.replace(".iso", "")
elif orig_path.endswith(".wim"):
return orig_path.replace(".wim", "")
elif orig_path.endswith(".cab"):
return orig_path.replace(".cab", "")
elif orig_path.endswith(".apm"):
return orig_path.replace(".apm", "")
elif orig_path.endswith(".arj"):
return orig_path.replace(".arj", "")
elif orig_path.endswith(".chm"):
return orig_path.replace(".chm", "")
elif orig_path.endswith(".cpio"):
return orig_path.replace(".cpio", "")
elif orig_path.endswith(".cramfs"):
return orig_path.replace(".cramfs", "")
elif orig_path.endswith(".deb"):
return orig_path.replace(".deb", "")
elif orig_path.endswith(".dmg"):
return orig_path.replace(".dmg", "")
elif orig_path.endswith(".fat"):
return orig_path.replace(".fat", "")
elif orig_path.endswith(".hfs"):
return orig_path.replace(".hfs", "")
elif orig_path.endswith(".lzh"):
return orig_path.replace(".lzh", "")
elif orig_path.endswith(".lzma"):
return orig_path.replace(".lzma", "")
elif orig_path.endswith(".lzma2"):
return orig_path.replace(".lzma2", "")
elif orig_path.endswith(".mbr"):
return orig_path.replace(".mbr", "")
elif orig_path.endswith(".msi"):
return orig_path.replace(".msi", "")
elif orig_path.endswith(".mslz"):
return orig_path.replace(".mslz", "")
elif orig_path.endswith(".nsis"):
return orig_path.replace(".nsis", "")
elif orig_path.endswith(".ntfs"):
return orig_path.replace(".ntfs", "")
elif orig_path.endswith(".rpm"):
return orig_path.replace(".rpm", "")
elif orig_path.endswith(".squashfs"):
return orig_path.replace(".squashfs", "")
elif orig_path.endswith(".udf"):
return orig_path.replace(".udf", "")
elif orig_path.endswith(".vhd"):
return orig_path.replace(".vhd", "")
elif orig_path.endswith(".xar"):
return orig_path.replace(".xar", "")
else:
raise NotSupportedExtractionArchive('File format not supported for extraction')
def get_mime_type(file_path):
mime = magic.Magic(mime=True)
mime_type = mime.from_file(file_path)
mime_type = mime_type if mime_type else "text/plain"
return mime_type
================================================
FILE: bot/helper/mirror_utils/__init__.py
================================================
================================================
FILE: bot/helper/mirror_utils/download_utils/__init__.py
================================================
================================================
FILE: bot/helper/mirror_utils/download_utils/aria2_download.py
================================================
from bot import aria2, download_dict_lock, STOP_DUPLICATE_MIRROR
from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
from bot.helper.ext_utils.bot_utils import *
from .download_helper import DownloadHelper
from bot.helper.mirror_utils.status_utils.aria_download_status import AriaDownloadStatus
from bot.helper.telegram_helper.message_utils import *
import threading
from aria2p import API
from time import sleep
class AriaDownloadHelper(DownloadHelper):
def __init__(self):
super().__init__()
@new_thread
def __onDownloadStarted(self, api, gid):
sleep(1)
LOGGER.info(f"onDownloadStart: {gid}")
dl = getDownloadByGid(gid)
download = api.get_download(gid)
self.name = download.name
sname = download.name
if STOP_DUPLICATE_MIRROR:
if dl.getListener().isTar == True:
sname = sname + ".tar"
if dl.getListener().extract == True:
smsg = None
else:
gdrive = GoogleDriveHelper(None)
smsg, button = gdrive.drive_list(sname)
if smsg:
dl.getListener().onDownloadError(f'File is already available in drive.This download has been stopped.\n\n')
sendMarkup("Here are the search results:", dl.getListener().bot, dl.getListener().update, button)
aria2.remove([download])
return
update_all_messages()
def __onDownloadComplete(self, api: API, gid):
LOGGER.info(f"onDownloadComplete: {gid}")
dl = getDownloadByGid(gid)
download = api.get_download(gid)
if download.followed_by_ids:
new_gid = download.followed_by_ids[0]
new_download = api.get_download(new_gid)
with download_dict_lock:
download_dict[dl.uid()] = AriaDownloadStatus(new_gid, dl.getListener())
if new_download.is_torrent:
download_dict[dl.uid()].is_torrent = True
update_all_messages()
LOGGER.info(f'Changed gid from {gid} to {new_gid}')
else:
if dl: threading.Thread(target=dl.getListener().onDownloadComplete).start()
@new_thread
def __onDownloadPause(self, api, gid):
LOGGER.info(f"onDownloadPause: {gid}")
dl = getDownloadByGid(gid)
dl.getListener().onDownloadError('Download stopped by user!')
@new_thread
def __onDownloadStopped(self, api, gid):
LOGGER.info(f"onDownloadStop: {gid}")
dl = getDownloadByGid(gid)
if dl: dl.getListener().onDownloadError('Your torrent has no seeds.Download stopped automatically.')
@new_thread
def __onDownloadError(self, api, gid):
sleep(0.5) #sleep for split second to ensure proper dl gid update from onDownloadComplete
LOGGER.info(f"onDownloadError: {gid}")
dl = getDownloadByGid(gid)
download = api.get_download(gid)
error = download.error_message
LOGGER.info(f"Download Error: {error}")
if dl: dl.getListener().onDownloadError(error)
def start_listener(self):
aria2.listen_to_notifications(threaded=True, on_download_start=self.__onDownloadStarted,
on_download_error=self.__onDownloadError,
on_download_pause=self.__onDownloadPause,
on_download_stop=self.__onDownloadStopped,
on_download_complete=self.__onDownloadComplete)
def add_download(self, link: str, path, listener, filename):
if is_magnet(link):
download = aria2.add_magnet(link, {'dir': path, 'out': filename})
else:
download = aria2.add_uris([link], {'dir': path, 'out': filename})
if download.error_message: #no need to proceed further at this point
listener.onDownloadError(download.error_message)
return
with download_dict_lock:
download_dict[listener.uid] = AriaDownloadStatus(download.gid,listener)
LOGGER.info(f"Started: {download.gid} DIR:{download.dir} ")
================================================
FILE: bot/helper/mirror_utils/download_utils/direct_link_generator.py
================================================
# Copyright (C) 2019 The Raphielscape Company LLC.
#
# Licensed under the Raphielscape Public License, Version 1.c (the "License");
# you may not use this file except in compliance with the License.
#
""" Helper Module containing various sites direct links generators. This module is copied and modified as per need
from https://github.com/AvinashReddy3108/PaperplaneExtended . I hereby take no credit of the following code other
than the modifications. See https://github.com/AvinashReddy3108/PaperplaneExtended/commits/master/userbot/modules/direct_links.py
for original authorship. """
import json
import re
import math
import urllib.parse
from os import popen
from random import choice
from urllib.parse import urlparse
import lk21
import requests
import logging
from bot import UPTOBOX_TOKEN
from bs4 import BeautifulSoup
from lk21.extractors.bypasser import Bypass
from base64 import standard_b64encode
from js2py import EvalJs
from bot.helper.ext_utils.exceptions import DirectDownloadLinkException
def direct_link_generator(link: str):
""" direct links generator """
if not link:
raise DirectDownloadLinkException("`No links found!`")
elif 'zippyshare.com' in link:
return zippy_share(link)
elif 'yadi.sk' in link:
return yandex_disk(link)
elif 'cloud.mail.ru' in link:
return cm_ru(link)
elif 'mediafire.com' in link:
return mediafire(link)
elif 'uptobox.com' in link:
return uptobox(link)
elif 'osdn.net' in link:
return osdn(link)
elif 'github.com' in link:
return github(link)
elif 'fembed.com' in link:
return fembed(link)
elif 'femax20.com' in link:
return fembed(link)
elif 'feurl.com' in link:
return fembed(link)
else:
raise DirectDownloadLinkException(f'No Direct link function found for {link}')
def zippy_share(url: str) -> str:
link = re.findall("https:/.(.*?).zippyshare", url)[0]
response_content = (requests.get(url)).content
bs_obj = BeautifulSoup(response_content, "lxml")
try:
js_script = bs_obj.find("div", {"class": "center",}).find_all(
"script"
)[1]
except:
js_script = bs_obj.find("div", {"class": "right",}).find_all(
"script"
)[0]
js_content = re.findall(r'\.href.=."/(.*?)";', str(js_script))
js_content = 'var x = "/' + js_content[0] + '"'
evaljs = EvalJs()
setattr(evaljs, "x", None)
evaljs.execute(js_content)
js_content = getattr(evaljs, "x")
return f"https://{link}.zippyshare.com{js_content}"
def yandex_disk(url: str) -> str:
""" Yandex.Disk direct links generator
Based on https://github.com/wldhx/yadisk-direct"""
try:
link = re.findall(r'\bhttps?://.*yadi\.sk\S+', url)[0]
except IndexError:
reply = "`No Yandex.Disk links found`\n"
return reply
api = 'https://cloud-api.yandex.net/v1/disk/public/resources/download?public_key={}'
try:
dl_url = requests.get(api.format(link)).json()['href']
return dl_url
except KeyError:
raise DirectDownloadLinkException("`Error: File not found / Download limit reached`\n")
def cm_ru(url: str) -> str:
""" cloud.mail.ru direct links generator
Using https://github.com/JrMasterModelBuilder/cmrudl.py"""
reply = ''
try:
link = re.findall(r'\bhttps?://.*cloud\.mail\.ru\S+', url)[0]
except IndexError:
raise DirectDownloadLinkException("`No cloud.mail.ru links found`\n")
command = f'vendor/cmrudl.py/cmrudl -s {link}'
result = popen(command).read()
result = result.splitlines()[-1]
try:
data = json.loads(result)
except json.decoder.JSONDecodeError:
raise DirectDownloadLinkException("`Error: Can't extract the link`\n")
dl_url = data['download']
return dl_url
def mediafire(url: str) -> str:
""" MediaFire direct links generator """
try:
link = re.findall(r'\bhttps?://.*mediafire\.com\S+', url)[0]
except IndexError:
raise DirectDownloadLinkException("`No MediaFire links found`\n")
page = BeautifulSoup(requests.get(link).content, 'lxml')
info = page.find('a', {'aria-label': 'Download file'})
dl_url = info.get('href')
return dl_url
def uptobox(url: str) -> str:
try:
link = re.findall(r'\bhttps?://.*uptobox\.com\S+', url)[0]
except IndexError:
raise DirectDownloadLinkException("`No Uptobox links found`\n")
if UPTOBOX_TOKEN is None:
logging.error('UPTOBOX_TOKEN not provided!')
else:
check = 'https://uptobox.com/api/user/me?token=%s' % (UPTOBOX_TOKEN)
request = requests.get(check)
info = request.json()
premium = info["data"]["premium"]
try:
link = re.findall(r'\bhttp?://.*uptobox\.com/dl\S+', url)[0]
logging.info('Uptobox direct link')
dl_url = url
except:
if premium == 1:
file_id = re.findall(r'\bhttps?://.*uptobox\.com/(\w+)', url)[0]
file_link = 'https://uptobox.com/api/link?token=%s&file_code=%s' % (UPTOBOX_TOKEN, file_id)
req = requests.get(file_link)
result = req.json()
dl_url = result['data']['dlLink']
else:
file_id = re.findall(r'\bhttps?://.*uptobox\.com/(\w+)', url)[0]
file_link = 'https://uptobox.com/api/link?token=%s&file_code=%s' % (UPTOBOX_TOKEN, file_id)
req = requests.get(file_link)
result = req.json()
waiting_time = result["data"]["waiting"] + 1
waiting_token = result["data"]["waitingToken"]
_countdown(waiting_time)
file_link = 'https://uptobox.com/api/link?token=%s&file_code=%s&waitingToken=%s' % (UPTOBOX_TOKEN, file_id, waiting_token)
req = requests.get(file_link)
result = req.json()
dl_url = result['data']['dlLink']
return dl_url
def osdn(url: str) -> str:
""" OSDN direct links generator """
osdn_link = 'https://osdn.net'
try:
link = re.findall(r'\bhttps?://.*osdn\.net\S+', url)[0]
except IndexError:
raise DirectDownloadLinkException("`No OSDN links found`\n")
page = BeautifulSoup(
requests.get(link, allow_redirects=True).content, 'lxml')
info = page.find('a', {'class': 'mirror_link'})
link = urllib.parse.unquote(osdn_link + info['href'])
mirrors = page.find('form', {'id': 'mirror-select-form'}).findAll('tr')
urls = []
for data in mirrors[1:]:
mirror = data.find('input')['value']
urls.append(re.sub(r'm=(.*)&f', f'm={mirror}&f', link))
return urls[0]
def github(url: str) -> str:
""" GitHub direct links generator """
try:
re.findall(r'\bhttps?://.*github\.com.*releases\S+', url)[0]
except IndexError:
raise DirectDownloadLinkException("`No GitHub Releases links found`\n")
download = requests.get(url, stream=True, allow_redirects=False)
try:
dl_url = download.headers["location"]
return dl_url
except KeyError:
raise DirectDownloadLinkException("`Error: Can't extract the link`\n")
def useragent():
"""
useragent random setter
"""
useragents = BeautifulSoup(
requests.get(
'https://developers.whatismybrowser.com/'
'useragents/explore/operating_system_name/android/').content,
'lxml').findAll('td', {'class': 'useragent'})
user_agent = choice(useragents)
return user_agent.text
def fembed(link: str) -> str:
""" Fembed direct link generator
Based on https://github.com/breakdowns/slam-mirrorbot """
bypasser = lk21.Bypass()
dl_url=bypasser.bypass_fembed(link)
lst_link = []
count = len(dl_url)
for i in dl_url:
lst_link.append(dl_url[i])
return lst_link[count-1]
================================================
FILE: bot/helper/mirror_utils/download_utils/direct_link_generator_license.md
================================================
RAPHIELSCAPE PUBLIC LICENSE
Version 1.c, June 2019
Copyright (C) 2019 Raphielscape LLC.
Copyright (C) 2019 Devscapes Open Source Holding GmbH.
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
RAPHIELSCAPE PUBLIC LICENSE
A-1. DEFINITIONS
0. “This License” refers to version 1.c of the Raphielscape Public License.
1. “Copyright” also means copyright-like laws that apply to other kinds of works.
2. “The Work" refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”.
“Licensees” and “recipients” may be individuals or organizations.
3. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission,
other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work
or a work “based on” the earlier work.
4. Source Form. The “source form” for a work means the preferred form of the work for making modifications to it.
“Object code” means any non-source form of a work.
The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and
(for an executable work) run the object code and to modify the work, including scripts to control those activities.
The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
The Corresponding Source for a work in source code form is that same work.
5. "The author" refers to "author" of the code, which is the one that made the particular code which exists inside of
the Corresponding Source.
6. "Owner" refers to any parties which is made the early form of the Corresponding Source.
A-2. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You must give any other recipients of the Work or Derivative Works a copy of this License; and
1. You must cause any modified files to carry prominent notices stating that You changed the files; and
2. You must retain, in the Source form of any Derivative Works that You distribute,
this license, all copyright, patent, trademark, authorships and attribution notices
from the Source form of the Work; and
3. Respecting the author and owner of works that are distributed in any way.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction,
or distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
B. DISCLAIMER OF WARRANTY
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
C. REVISED VERSION OF THIS LICENSE
The Devscapes Open Source Holding GmbH. may publish revised and/or new versions of the
Raphielscape Public License from time to time. Such new versions will be similar in spirit
to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies that a
certain numbered version of the Raphielscape Public License "or any later version" applies to it,
you have the option of following the terms and conditions either of that numbered version or of
any later version published by the Devscapes Open Source Holding GmbH. If the Program does not specify a
version number of the Raphielscape Public License, you may choose any version ever published
by the Devscapes Open Source Holding GmbH.
END OF LICENSE
================================================
FILE: bot/helper/mirror_utils/download_utils/download_helper.py
================================================
# An abstract class which will be inherited by the tool specific classes like aria2_helper or mega_download_helper
import threading
class MethodNotImplementedError(NotImplementedError):
def __init__(self):
super(self, 'Not implemented method')
class DownloadHelper:
def __init__(self):
self.__name = '' # Name of the download; empty string if no download has been started
self.__size = 0.0 # Size of the download
self.downloaded_bytes = 0.0 # Bytes downloaded
self.speed = 0.0 # Download speed in bytes per second
self.progress = 0.0
self.progress_string = '0.00%'
self.eta = 0 # Estimated time of download complete
self.eta_string = '0s' # A listener class which have event callbacks
self._resource_lock = threading.Lock()
def add_download(self, link: str, path):
raise MethodNotImplementedError
def cancel_download(self):
# Returns None if successfully cancelled, else error string
raise MethodNotImplementedError
================================================
FILE: bot/helper/mirror_utils/download_utils/mega_download.py
================================================
import threading
from bot import LOGGER, download_dict, download_dict_lock
from .download_helper import DownloadHelper
from ..status_utils.mega_status import MegaDownloadStatus
from megasdkrestclient import MegaSdkRestClient, constants
from bot.helper.ext_utils.bot_utils import setInterval
from pathlib import Path
class MegaDownloader:
POLLING_INTERVAL = 2
def __init__(self, listener):
super().__init__()
self.__listener = listener
self.__name = ""
self.__gid = ''
self.__resource_lock = threading.Lock()
self.__mega_client = MegaSdkRestClient('http://localhost:6090')
self.__periodic = None
self.__downloaded_bytes = 0
self.__progress = 0
self.__size = 0
@property
def progress(self):
with self.__resource_lock:
return self.__progress
@property
def downloaded_bytes(self):
with self.__resource_lock:
return self.__downloaded_bytes
@property
def size(self):
with self.__resource_lock:
return self.__size
@property
def gid(self):
with self.__resource_lock:
return self.__gid
@property
def name(self):
with self.__resource_lock:
return self.__name
@property
def download_speed(self):
if self.gid is not None:
return self.__mega_client.getDownloadInfo(self.gid)['speed']
def __onDownloadStart(self, name, size, gid):
self.__periodic = setInterval(self.POLLING_INTERVAL, self.__onInterval)
with download_dict_lock:
download_dict[self.__listener.uid] = MegaDownloadStatus(self, self.__listener)
with self.__resource_lock:
self.__name = name
self.__size = size
self.__gid = gid
self.__listener.onDownloadStarted()
def __onInterval(self):
dlInfo = self.__mega_client.getDownloadInfo(self.gid)
if (dlInfo['state'] == constants.State.TYPE_STATE_COMPLETED or dlInfo[
'state'] == constants.State.TYPE_STATE_CANCELED or dlInfo[
'state'] == constants.State.TYPE_STATE_FAILED) and self.__periodic is not None:
self.__periodic.cancel()
if dlInfo['state'] == constants.State.TYPE_STATE_COMPLETED:
self.__onDownloadComplete()
return
if dlInfo['state'] == constants.State.TYPE_STATE_CANCELED:
self.__onDownloadError('Cancelled by user')
return
if dlInfo['state'] == constants.State.TYPE_STATE_FAILED:
self.__onDownloadError(dlInfo['error_string'])
return
self.__onDownloadProgress(dlInfo['completed_length'], dlInfo['total_length'])
def __onDownloadProgress(self, current, total):
with self.__resource_lock:
self.__downloaded_bytes = current
try:
self.__progress = current / total * 100
except ZeroDivisionError:
self.__progress = 0
def __onDownloadError(self, error):
self.__listener.onDownloadError(error)
def __onDownloadComplete(self):
self.__listener.onDownloadComplete()
def add_download(self, link, path):
Path(path).mkdir(parents=True, exist_ok=True)
dl = self.__mega_client.addDl(link, path)
gid = dl['gid']
info = self.__mega_client.getDownloadInfo(gid)
file_name = info['name']
file_size = info['total_length']
self.__onDownloadStart(file_name, file_size, gid)
LOGGER.info(f'Started mega download with gid: {gid}')
def cancel_download(self):
LOGGER.info(f'Cancelling download on user request: {self.gid}')
self.__mega_client.cancelDl(self.gid)
================================================
FILE: bot/helper/mirror_utils/download_utils/telegram_downloader.py
================================================
import logging
import threading
import time
from bot import LOGGER, download_dict, download_dict_lock, app
from .download_helper import DownloadHelper
from ..status_utils.telegram_download_status import TelegramDownloadStatus
global_lock = threading.Lock()
GLOBAL_GID = set()
logging.getLogger("pyrogram").setLevel(logging.WARNING)
class TelegramDownloadHelper(DownloadHelper):
def __init__(self, listener):
super().__init__()
self.__listener = listener
self.__resource_lock = threading.RLock()
self.__name = ""
self.__gid = ''
self.__start_time = time.time()
self.__user_bot = app
self.__is_cancelled = False
@property
def gid(self):
with self.__resource_lock:
return self.__gid
@property
def download_speed(self):
with self.__resource_lock:
return self.downloaded_bytes / (time.time() - self.__start_time)
def __onDownloadStart(self, name, size, file_id):
with download_dict_lock:
download_dict[self.__listener.uid] = TelegramDownloadStatus(self, self.__listener)
with global_lock:
GLOBAL_GID.add(file_id)
with self.__resource_lock:
self.name = name
self.size = size
self.__gid = file_id
self.__listener.onDownloadStarted()
def __onDownloadProgress(self, current, total):
if self.__is_cancelled:
self.__onDownloadError('Cancelled by user!')
self.__user_bot.stop_transmission()
return
with self.__resource_lock:
self.downloaded_bytes = current
try:
self.progress = current / self.size * 100
except ZeroDivisionError:
self.progress = 0
def __onDownloadError(self, error):
with global_lock:
try:
GLOBAL_GID.remove(self.gid)
except KeyError:
pass
self.__listener.onDownloadError(error)
def __onDownloadComplete(self):
with global_lock:
GLOBAL_GID.remove(self.gid)
self.__listener.onDownloadComplete()
def __download(self, message, path):
download = self.__user_bot.download_media(message,
progress=self.__onDownloadProgress, file_name=path)
if download is not None:
self.__onDownloadComplete()
else:
if not self.__is_cancelled:
self.__onDownloadError('Internal error occurred')
def add_download(self, message, path, filename):
_message = self.__user_bot.get_messages(message.chat.id, message.message_id)
media = None
media_array = [_message.document, _message.video, _message.audio]
for i in media_array:
if i is not None:
media = i
break
if media is not None:
with global_lock:
# For avoiding locking the thread lock for long time unnecessarily
download = media.file_id not in GLOBAL_GID
if filename == "":
name = media.file_name
else:
name = filename
path = path + name
if download:
self.__onDownloadStart(name, media.file_size, media.file_id)
LOGGER.info(f'Downloading telegram file with id: {media.file_id}')
threading.Thread(target=self.__download, args=(_message, path)).start()
else:
self.__onDownloadError('File already being downloaded!')
else:
self.__onDownloadError('No document in the replied message')
def cancel_download(self):
LOGGER.info(f'Cancelling download on user request: {self.gid}')
self.__is_cancelled = True
================================================
FILE: bot/helper/mirror_utils/download_utils/youtube_dl_download_helper.py
================================================
from .download_helper import DownloadHelper
import time
from yt_dlp import YoutubeDL, DownloadError
from bot import download_dict_lock, download_dict
from ..status_utils.youtube_dl_download_status import YoutubeDLDownloadStatus
import logging
import re
import threading
LOGGER = logging.getLogger(__name__)
class MyLogger:
def __init__(self, obj):
self.obj = obj
def debug(self, msg):
LOGGER.debug(msg)
# Hack to fix changing changing extension
match = re.search(r'.ffmpeg..Merging formats into..(.*?).$', msg)
if match and not self.obj.is_playlist:
newname = match.group(1)
newname = newname.split("/")
newname = newname[-1]
self.obj.name = newname
@staticmethod
def warning(msg):
LOGGER.warning(msg)
@staticmethod
def error(msg):
LOGGER.error(msg)
class YoutubeDLHelper(DownloadHelper):
def __init__(self, listener):
super().__init__()
self.name = ""
self.__start_time = time.time()
self.__listener = listener
self.__gid = ""
self.opts = {
'progress_hooks': [self.__onDownloadProgress],
'logger': MyLogger(self),
'usenetrc': True
}
self.__download_speed = 0
self.download_speed_readable = ''
self.downloaded_bytes = 0
self.size = 0
self.is_playlist = False
self.last_downloaded = 0
self.is_cancelled = False
self.vid_id = ''
self.__resource_lock = threading.RLock()
@property
def download_speed(self):
with self.__resource_lock:
return self.__download_speed
@property
def gid(self):
with self.__resource_lock:
return self.__gid
def __onDownloadProgress(self, d):
if self.is_cancelled:
raise ValueError("Cancelling Download..")
if d['status'] == "finished":
if self.is_playlist:
self.last_downloaded = 0
elif d['status'] == "downloading":
with self.__resource_lock:
self.__download_speed = d['speed']
try:
tbyte = d['total_bytes']
except KeyError:
tbyte = d['total_bytes_estimate']
if self.is_playlist:
progress = d['downloaded_bytes'] / tbyte
chunk_size = d['downloaded_bytes'] - self.last_downloaded
self.last_downloaded = tbyte * progress
self.downloaded_bytes += chunk_size
try:
self.progress = (self.downloaded_bytes / self.size) * 100
except ZeroDivisionError:
pass
else:
self.download_speed_readable = d['_speed_str']
self.downloaded_bytes = d['downloaded_bytes']
def __onDownloadStart(self):
with download_dict_lock:
download_dict[self.__listener.uid] = YoutubeDLDownloadStatus(self, self.__listener)
def __onDownloadComplete(self):
self.__listener.onDownloadComplete()
def onDownloadError(self, error):
self.__listener.onDownloadError(error)
def extractMetaData(self, link, qual, name):
if "hotstar" in link or "sonyliv" in link:
self.opts['geo_bypass_country'] = 'IN'
with YoutubeDL(self.opts) as ydl:
try:
result = ydl.extract_info(link, download=False)
if name == "":
name = ydl.prepare_filename(result)
else:
name = name
# noobway hack for changing extension after converting to mp3
if qual == "audio":
name = name.replace(".mp4", ".mp3").replace(".webm", ".mp3")
except DownloadError as e:
self.onDownloadError(str(e))
return
if result.get('direct'):
return None
if 'entries' in result:
video = result['entries'][0]
for v in result['entries']:
if v and v.get('filesize'):
self.size += float(v['filesize'])
# For playlists, ydl.prepare-filename returns the following format: {meta.get("name")}\nSize:- {get_readable_file_size(self.transferred_size)}'
durl = self.__G_DRIVE_DIR_BASE_DOWNLOAD_URL.format(dir_id)
buttons = button_build.ButtonMaker()
if SHORTENER is not None and SHORTENER_API is not None:
surl = requests.get(f'https://{SHORTENER}/api?api={SHORTENER_API}&url={durl}&format=text').text
buttons.buildbutton("Drive Link", surl)
else:
buttons.buildbutton("Drive Link", durl)
if INDEX_URL is not None:
url_path = requests.utils.quote(f'{meta.get("name")}')
url = f'{INDEX_URL}/{url_path}/'
if SHORTENER is not None and SHORTENER_API is not None:
siurl = requests.get(f'https://{SHORTENER}/api?api={SHORTENER_API}&url={url}&format=text').text
buttons.buildbutton("Index Link", siurl)
else:
buttons.buildbutton("Index Link", url)
if BUTTON_THREE_NAME is not None and BUTTON_THREE_URL is not None:
buttons.buildbutton(f"{BUTTON_THREE_NAME}", f"{BUTTON_THREE_URL}")
if BUTTON_FOUR_NAME is not None and BUTTON_FOUR_URL is not None:
buttons.buildbutton(f"{BUTTON_FOUR_NAME}", f"{BUTTON_FOUR_URL}")
if BUTTON_FIVE_NAME is not None and BUTTON_FIVE_URL is not None:
buttons.buildbutton(f"{BUTTON_FIVE_NAME}", f"{BUTTON_FIVE_URL}")
else:
file = self.copyFile(meta.get('id'), parent_id)
msg += f'Name:- {file.get("name")}'
durl = self.__G_DRIVE_BASE_DOWNLOAD_URL.format(file.get("id"))
buttons = button_build.ButtonMaker()
if SHORTENER is not None and SHORTENER_API is not None:
surl = requests.get(f'https://{SHORTENER}/api?api={SHORTENER_API}&url={durl}&format=text').text
buttons.buildbutton("Drive Link", surl)
else:
buttons.buildbutton("Drive Link", durl)
try:
msg += f'\nSize:- {get_readable_file_size(int(meta.get("size")))}'
except TypeError:
pass
if INDEX_URL is not None:
url_path = requests.utils.quote(f'{file.get("name")}')
url = f'{INDEX_URL}/{url_path}'
if SHORTENER is not None and SHORTENER_API is not None:
siurl = requests.get(f'https://{SHORTENER}/api?api={SHORTENER_API}&url={url}&format=text').text
buttons.buildbutton("Index Link", siurl)
else:
buttons.buildbutton("Index Link", url)
if BUTTON_THREE_NAME is not None and BUTTON_THREE_URL is not None:
buttons.buildbutton(f"{BUTTON_THREE_NAME}", f"{BUTTON_THREE_URL}")
if BUTTON_FOUR_NAME is not None and BUTTON_FOUR_URL is not None:
buttons.buildbutton(f"{BUTTON_FOUR_NAME}", f"{BUTTON_FOUR_URL}")
if BUTTON_FIVE_NAME is not None and BUTTON_FIVE_URL is not None:
buttons.buildbutton(f"{BUTTON_FIVE_NAME}", f"{BUTTON_FIVE_URL}")
except Exception as err:
if isinstance(err, RetryError):
LOGGER.info(f"Total Attempts:- {err.last_attempt.attempt_number}")
err = err.last_attempt.exception()
err = str(err).replace('>', '').replace('<', '')
LOGGER.error(err)
return err, ""
return msg, InlineKeyboardMarkup(buttons.build_menu(2))
def cloneFolder(self, name, local_path, folder_id, parent_id):
LOGGER.info(f"Syncing: {local_path}")
files = self.getFilesByFolderId(folder_id)
new_id = None
if len(files) == 0:
return parent_id
for file in files:
if file.get('mimeType') == self.__G_DRIVE_DIR_MIME_TYPE:
file_path = os.path.join(local_path, file.get('name'))
current_dir_id = self.create_directory(file.get('name'), parent_id)
new_id = self.cloneFolder(file.get('name'), file_path, file.get('id'), current_dir_id)
else:
try:
self.transferred_size += int(file.get('size'))
except TypeError:
pass
try:
self.copyFile(file.get('id'), parent_id)
new_id = parent_id
except Exception as e:
if isinstance(e, RetryError):
LOGGER.info(f"Total Attempts: {e.last_attempt.attempt_number}")
err = e.last_attempt.exception()
else:
err = e
LOGGER.error(err)
return new_id
@retry(wait=wait_exponential(multiplier=2, min=3, max=6), stop=stop_after_attempt(5),
retry=retry_if_exception_type(HttpError), before=before_log(LOGGER, logging.DEBUG))
def create_directory(self, directory_name, parent_id):
file_metadata = {
"name": directory_name,
"mimeType": self.__G_DRIVE_DIR_MIME_TYPE
}
if parent_id is not None:
file_metadata["parents"] = [parent_id]
file = self.__service.files().create(supportsTeamDrives=True, body=file_metadata).execute()
file_id = file.get("id")
if not IS_TEAM_DRIVE:
self.__set_permission(file_id)
LOGGER.info("Created Google-Drive Folder:\nName: {}\nID: {} ".format(file.get("name"), file_id))
return file_id
def upload_dir(self, input_directory, parent_id):
list_dirs = os.listdir(input_directory)
if len(list_dirs) == 0:
return parent_id
new_id = None
for item in list_dirs:
current_file_name = os.path.join(input_directory, item)
if self.is_cancelled:
return None
if os.path.isdir(current_file_name):
current_dir_id = self.create_directory(item, parent_id)
new_id = self.upload_dir(current_file_name, current_dir_id)
else:
mime_type = get_mime_type(current_file_name)
file_name = current_file_name.split("/")[-1]
# current_file_name will have the full path
self.upload_file(current_file_name, file_name, mime_type, parent_id)
new_id = parent_id
return new_id
def authorize(self):
# Get credentials
credentials = None
if not USE_SERVICE_ACCOUNTS:
if os.path.exists(self.__G_DRIVE_TOKEN_FILE):
with open(self.__G_DRIVE_TOKEN_FILE, 'rb') as f:
credentials = pickle.load(f)
if credentials is None or not credentials.valid:
if credentials and credentials.expired and credentials.refresh_token:
credentials.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', self.__OAUTH_SCOPE)
LOGGER.info(flow)
credentials = flow.run_console(port=0)
# Save the credentials for the next run
with open(self.__G_DRIVE_TOKEN_FILE, 'wb') as token:
pickle.dump(credentials, token)
else:
LOGGER.info(f"Authorizing with {SERVICE_ACCOUNT_INDEX}.json service account")
credentials = service_account.Credentials.from_service_account_file(
f'accounts/{SERVICE_ACCOUNT_INDEX}.json',
scopes=self.__OAUTH_SCOPE)
return build('drive', 'v3', credentials=credentials, cache_discovery=False)
def edit_telegraph(self):
nxt_page = 1
prev_page = 0
for content in self.telegraph_content :
if nxt_page == 1 :
content += f'Next'
nxt_page += 1
else :
if prev_page <= self.num_of_path:
content += f'Prev'
prev_page += 1
if nxt_page < self.num_of_path:
content += f' | Next'
nxt_page += 1
Telegraph(access_token=telegraph_token).edit_page(path = self.path[prev_page],
title = 'MirrorX Search',
author_name='MirrorX',
author_url='https://github.com/iamLiquidX',
html_content=content)
return
def escapes(self, str):
chars = ['\\', "'", '"', r'\a', r'\b', r'\f', r'\n', r'\r', r'\t']
for char in chars:
str = str.replace(char, '\\'+char)
return str
def drive_list(self, fileName):
msg = ""
fileName = self.escapes(str(fileName))
# Create Search Query for API request.
query = f"'{parent_id}' in parents and (name contains '{fileName}')"
response = self.__service.files().list(supportsTeamDrives=True,
includeTeamDriveItems=True,
q=query,
spaces='drive',
pageSize=200,
fields='files(id, name, mimeType, size)',
orderBy='modifiedTime desc').execute()
content_count = 0
if response["files"]:
msg += f'{file.get('name')}
(folder){file.get('name')}
({get_readable_file_size(int(file.get('size')))}){name}'
msg += f'\nSize:- {get_readable_file_size(self.total_bytes)}'
msg += f"\nType:- Folder"
msg += f"\nSubFolders:- {self.total_folders}"
msg += f"\nFiles:- {self.total_files}"
else:
msg += f'Name:- {name}'
try:
typee = drive_file['mimeType']
except:
typee = 'File'
try:
self.total_files += 1
self.gDrive_file(**drive_file)
msg += f'\nSize:- {get_readable_file_size(self.total_bytes)}'
msg += f"\nType:- {typee}"
msg += f"\nFiles:- {self.total_files}"
except TypeError:
pass
except Exception as err:
if isinstance(err, RetryError):
LOGGER.info(f"Total Attempts: {err.last_attempt.attempt_number}")
err = err.last_attempt.exception()
err = str(err).replace('>', '').replace('<', '')
LOGGER.error(err)
return err
return msg
def gDrive_file(self, **kwargs):
try:
size = int(kwargs['size'])
except:
size = 0
self.total_bytes += size
def gDrive_directory(self, **kwargs) -> None:
files = self.getFilesByFolderId(kwargs['id'])
if len(files) == 0:
return
for file_ in files:
if file_['mimeType'] == self.__G_DRIVE_DIR_MIME_TYPE:
self.total_folders += 1
self.gDrive_directory(**file_)
else:
self.total_files += 1
self.gDrive_file(**file_)
def clonehelper(self, link):
try:
file_id = self.getIdFromUrl(link)
except (KeyError,IndexError):
msg = "Google drive ID could not be found in the provided link"
return msg, "", ""
LOGGER.info(f"File ID: {file_id}")
try:
drive_file = self.__service.files().get(fileId=file_id, fields="id, name, mimeType, size",
supportsTeamDrives=True).execute()
name = drive_file['name']
LOGGER.info(f"Checking: {name}")
if drive_file['mimeType'] == self.__G_DRIVE_DIR_MIME_TYPE:
self.gDrive_directory(**drive_file)
else:
try:
self.total_files += 1
self.gDrive_file(**drive_file)
except TypeError:
pass
clonesize = self.total_bytes
except Exception as err:
if isinstance(err, RetryError):
LOGGER.info(f"Total Attempts: {err.last_attempt.attempt_number}")
err = err.last_attempt.exception()
err = str(err).replace('>', '').replace('<', '')
LOGGER.error(err)
if "File not found" in str(err):
msg = "File not found."
else:
msg = f"Error.\n{err}"
return msg, "", ""
return "", clonesize, name
def download(self, link):
self.is_downloading = True
file_id = self.getIdFromUrl(link)
if USE_SERVICE_ACCOUNTS:
self.service_account_count = len(os.listdir("accounts"))
self.start_time = time.time()
self.updater = setInterval(self.update_interval, self._on_download_progress)
try:
meta = self.getFileMetadata(file_id)
path = f"{DOWNLOAD_DIR}{self.__listener.uid}/"
if meta.get("mimeType") == self.__G_DRIVE_DIR_MIME_TYPE:
self.download_folder(file_id, path, meta.get('name'))
else:
os.makedirs(path)
self.download_file(file_id, path, meta.get('name'), meta.get('mimeType'))
except Exception as err:
if isinstance(err, RetryError):
LOGGER.info(f"Total Attempts: {err.last_attempt.attempt_number}")
err = err.last_attempt.exception()
err = str(err).replace('>', '').replace('<', '')
LOGGER.error(err)
self.is_cancelled = True
self.__listener.onDownloadError(err)
return
finally:
self.updater.cancel()
if self.is_cancelled:
return
self.__listener.onDownloadComplete()
def download_folder(self, folder_id, path, folder_name):
if not os.path.exists(path + folder_name):
os.makedirs(path + folder_name)
path += folder_name + '/'
result = []
page_token = None
while True:
files = self.__service.files().list(
supportsTeamDrives=True,
includeTeamDriveItems=True,
q=f"'{folder_id}' in parents",
fields='nextPageToken, files(id, name, mimeType, size, shortcutDetails)',
pageToken=page_token,
pageSize=1000).execute()
result.extend(files['files'])
page_token = files.get("nextPageToken")
if not page_token:
break
result = sorted(result, key=lambda k: k['name'])
for item in result:
file_id = item['id']
filename = item['name']
mime_type = item['mimeType']
shortcut_details = item.get('shortcutDetails', None)
if shortcut_details != None:
file_id = shortcut_details['targetId']
mime_type = shortcut_details['targetMimeType']
if mime_type == 'application/vnd.google-apps.folder':
self.download_folder(file_id, path, filename)
elif not os.path.isfile(path + filename):
self.download_file(file_id, path, filename, mime_type)
if self.is_cancelled:
break
return
def download_file(self, file_id, path, filename, mime_type):
request = self.__service.files().get_media(fileId=file_id)
fh = io.FileIO('{}{}'.format(path, filename), 'wb')
downloader = MediaIoBaseDownload(fh, request, chunksize = 65 * 1024 * 1024)
done = False
while done is False:
if self.is_cancelled:
fh.close()
break
return
try:
self.dstatus, done = downloader.next_chunk()
except HttpError as err:
if err.resp.get('content-type', '').startswith('application/json'):
reason = json.loads(err.content).get('error').get('errors')[0].get('reason')
if reason == 'userRateLimitExceeded' or reason == 'dailyLimitExceeded':
if USE_SERVICE_ACCOUNTS:
if not self.switchServiceAccount():
raise err
LOGGER.info(f"Got: {reason}, Trying Again...")
return self.download_file(file_id, path, filename, mime_type)
else:
raise err
else:
raise err
self._file_downloaded_bytes = 0
def _on_download_progress(self):
if self.dstatus is not None:
chunk_size = self.dstatus.total_size * self.dstatus.progress() - self._file_downloaded_bytes
self._file_downloaded_bytes = self.dstatus.total_size * self.dstatus.progress()
self.downloaded_bytes += chunk_size
self.dtotal_time += self.update_interval
def cancel_download(self):
self.is_cancelled = True
self.__listener.onDownloadError('Download stopped by user!')
================================================
FILE: bot/helper/telegram_helper/__init__.py
================================================
================================================
FILE: bot/helper/telegram_helper/bot_commands.py
================================================
import os
def getCommand(name: str, command: str):
try:
if len(os.environ[name]) == 0:
raise KeyError
return os.environ[name]
except KeyError:
return command
class _BotCommands:
def __init__(self):
self.StartCommand = getCommand('START_COMMAND', 'start')
self.MirrorCommand = getCommand('MIRROR_COMMAND', 'mirror')
self.UnzipMirrorCommand = getCommand('UNZIP_COMMAND', 'unzipmirror')
self.TarMirrorCommand = getCommand('TAR_COMMAND', 'tarmirror')
self.CancelMirror = getCommand('CANCEL_COMMAND', 'cancel')
self.CancelAllCommand = getCommand('CANCELALL_COMMAND', 'cancelall')
self.ListCommand = getCommand('LIST_COMMAND', 'list')
self.SpeedCommand = getCommand('SPEED_COMMAND', 'speedtest')
self.CountCommand = getCommand('COUNT_COMMAND', 'count')
self.StatusCommand = getCommand('STATUS_COMMAND', 'status')
self.AuthorizeCommand = getCommand('AUTH_COMMAND', 'authorize')
self.UnAuthorizeCommand = getCommand('UNAUTH_COMMAND', 'unauthorize')
self.PingCommand = getCommand('PING_COMMAND', 'ping')
self.RestartCommand = getCommand('RESTART_COMMAND', 'restart')
self.StatsCommand = getCommand('STATS_COMMAND', 'stats')
self.HelpCommand = getCommand('HELP_COMMAND', 'help')
self.LogCommand = getCommand('LOG_COMMAND', 'log')
self.CloneCommand = getCommand('CLONE_COMMAND', 'clone')
self.WatchCommand = getCommand('WATCH_COMMAND', 'watch')
self.TarWatchCommand = getCommand('TARWATCH_COMMAND', 'tarwatch')
self.deleteCommand = getCommand('DELETE_COMMAND', 'del')
BotCommands = _BotCommands()
================================================
FILE: bot/helper/telegram_helper/button_build.py
================================================
from telegram import InlineKeyboardButton
class ButtonMaker:
def __init__(self):
self.button = []
def buildbutton(self, key, link):
self.button.append(InlineKeyboardButton(text = key, url = link))
def build_menu(self, n_cols, footer_buttons=None, header_buttons=None):
menu = [self.button[i:i + n_cols] for i in range(0, len(self.button), n_cols)]
if header_buttons:
menu.insert(0, header_buttons)
if footer_buttons:
menu.append(footer_buttons)
return menu
================================================
FILE: bot/helper/telegram_helper/filters.py
================================================
from telegram.ext import MessageFilter
from telegram import Message
from bot import AUTHORIZED_CHATS, OWNER_ID, download_dict, download_dict_lock
class CustomFilters:
class _OwnerFilter(MessageFilter):
def filter(self, message):
return bool(message.from_user.id == OWNER_ID)
owner_filter = _OwnerFilter()
class _AuthorizedUserFilter(MessageFilter):
def filter(self, message):
id = message.from_user.id
return bool(id in AUTHORIZED_CHATS or id == OWNER_ID)
authorized_user = _AuthorizedUserFilter()
class _AuthorizedChat(MessageFilter):
def filter(self, message):
return bool(message.chat.id in AUTHORIZED_CHATS)
authorized_chat = _AuthorizedChat()
class _MirrorOwner(MessageFilter):
def filter(self, message: Message):
user_id = message.from_user.id
if user_id == OWNER_ID:
return True
args = str(message.text).split(' ')
if len(args) > 1:
# Cancelling by gid
with download_dict_lock:
for message_id, status in download_dict.items():
if status.gid() == args[1] and status.message.from_user.id == user_id:
return True
else:
return False
# Cancelling by replying to original mirror message
reply_user = message.reply_to_message.from_user.id
return bool(reply_user == user_id)
mirror_owner_filter = _MirrorOwner()
================================================
FILE: bot/helper/telegram_helper/message_utils.py
================================================
from telegram import InlineKeyboardMarkup
from telegram.message import Message
from telegram.update import Update
import time
import psutil
from bot import AUTO_DELETE_MESSAGE_DURATION, LOGGER, bot, \
status_reply_dict, status_reply_dict_lock, download_dict, download_dict_lock
from bot.helper.ext_utils.bot_utils import get_readable_message, get_readable_file_size, MirrorStatus
from telegram.error import TimedOut, BadRequest
def sendMessage(text: str, bot, update: Update):
try:
return bot.send_message(update.message.chat_id,
reply_to_message_id=update.message.message_id,
text=text, parse_mode='HTMl')
except Exception as e:
LOGGER.error(str(e))
def sendMarkup(text: str, bot, update: Update, reply_markup: InlineKeyboardMarkup):
try:
return bot.send_message(update.message.chat_id,
reply_to_message_id=update.message.message_id,
text=text, reply_markup=reply_markup, parse_mode='HTMl')
except Exception as e:
LOGGER.error(str(e))
def editMessage(text: str, message: Message, reply_markup=None):
try:
bot.edit_message_text(text=text, message_id=message.message_id,
chat_id=message.chat.id,reply_markup=reply_markup,
parse_mode='HTMl')
except Exception as e:
LOGGER.error(str(e))
def deleteMessage(bot, message: Message):
try:
bot.delete_message(chat_id=message.chat.id,
message_id=message.message_id)
except Exception as e:
LOGGER.error(str(e))
def sendLogFile(bot, update: Update):
with open('log.txt', 'rb') as f:
bot.send_document(document=f, filename=f.name,
reply_to_message_id=update.message.message_id,
chat_id=update.message.chat_id)
def auto_delete_message(bot, cmd_message: Message, bot_message: Message):
if AUTO_DELETE_MESSAGE_DURATION != -1:
time.sleep(AUTO_DELETE_MESSAGE_DURATION)
try:
# Skip if None is passed meaning we don't want to delete bot xor cmd message
deleteMessage(bot, cmd_message)
deleteMessage(bot, bot_message)
except AttributeError:
pass
def delete_all_messages():
with status_reply_dict_lock:
for message in list(status_reply_dict.values()):
try:
deleteMessage(bot, message)
del status_reply_dict[message.chat.id]
except Exception as e:
LOGGER.error(str(e))
def update_all_messages():
msg = get_readable_message()
msg += f"CPU:- {psutil.cpu_percent()}%" \
f" DISK:- {psutil.disk_usage('/').percent}%" \
f" RAM:- {psutil.virtual_memory().percent}%"
with download_dict_lock:
dlspeed_bytes = 0
uldl_bytes = 0
for download in list(download_dict.values()):
speedy = download.speed()
if download.status() == MirrorStatus.STATUS_DOWNLOADING:
if 'KiB/s' in speedy:
dlspeed_bytes += float(speedy.split('K')[0]) * 1024
elif 'MiB/s' in speedy:
dlspeed_bytes += float(speedy.split('M')[0]) * 1048576
if download.status() == MirrorStatus.STATUS_UPLOADING:
if 'KB/s' in speedy:
uldl_bytes += float(speedy.split('K')[0]) * 1024
elif 'MB/s' in speedy:
uldl_bytes += float(speedy.split('M')[0]) * 1048576
dlspeed = get_readable_file_size(dlspeed_bytes)
ulspeed = get_readable_file_size(uldl_bytes)
msg += f"\nDL:{dlspeed}ps | UL:{ulspeed}ps \n"
with status_reply_dict_lock:
for chat_id in list(status_reply_dict.keys()):
if status_reply_dict[chat_id] and msg != status_reply_dict[chat_id].text:
if len(msg) == 0:
msg = "Starting DL"
try:
editMessage(msg, status_reply_dict[chat_id])
except Exception as e:
LOGGER.error(str(e))
status_reply_dict[chat_id].text = msg
def sendStatusMessage(msg, bot):
progress = get_readable_message()
progress += f"CPU: {psutil.cpu_percent()}%" \
f" DISK: {psutil.disk_usage('/').percent}%" \
f" RAM: {psutil.virtual_memory().percent}%"
with download_dict_lock:
dlspeed_bytes = 0
uldl_bytes = 0
for download in list(download_dict.values()):
speedy = download.speed()
if download.status() == MirrorStatus.STATUS_DOWNLOADING:
if 'KiB/s' in speedy:
dlspeed_bytes += float(speedy.split('K')[0]) * 1024
elif 'MiB/s' in speedy:
dlspeed_bytes += float(speedy.split('M')[0]) * 1048576
if download.status() == MirrorStatus.STATUS_UPLOADING:
if 'KB/s' in speedy:
uldl_bytes += float(speedy.split('K')[0]) * 1024
elif 'MB/s' in speedy:
uldl_bytes += float(speedy.split('M')[0]) * 1048576
dlspeed = get_readable_file_size(dlspeed_bytes)
ulspeed = get_readable_file_size(uldl_bytes)
progress += f"\nDL:{dlspeed}ps | UL:{ulspeed}ps \n"
with status_reply_dict_lock:
if msg.message.chat.id in list(status_reply_dict.keys()):
try:
message = status_reply_dict[msg.message.chat.id]
deleteMessage(bot, message)
del status_reply_dict[msg.message.chat.id]
except Exception as e:
LOGGER.error(str(e))
del status_reply_dict[msg.message.chat.id]
if len(progress) == 0:
progress = "Starting DL"
message = sendMessage(progress, bot, msg)
status_reply_dict[msg.message.chat.id] = message
================================================
FILE: bot/modules/__init__.py
================================================
================================================
FILE: bot/modules/authorize.py
================================================
from bot.helper.telegram_helper.message_utils import sendMessage
from bot import AUTHORIZED_CHATS, dispatcher
from telegram.ext import CommandHandler
from bot.helper.telegram_helper.filters import CustomFilters
from telegram.ext import Filters
from telegram import Update
from bot.helper.telegram_helper.bot_commands import BotCommands
def authorize(update,context):
reply_message = update.message.reply_to_message
msg = ''
with open('authorized_chats.txt', 'a') as file:
if reply_message is None:
# Trying to authorize a chat
chat_id = update.effective_chat.id
if chat_id not in AUTHORIZED_CHATS:
file.write(f'{chat_id}\n')
AUTHORIZED_CHATS.add(chat_id)
msg = 'Chat authorized'
else:
msg = 'Already authorized chat'
else:
# Trying to authorize someone in specific
user_id = reply_message.from_user.id
if user_id not in AUTHORIZED_CHATS:
file.write(f'{user_id}\n')
AUTHORIZED_CHATS.add(user_id)
msg = 'Person Authorized to use the bot!'
else:
msg = 'Person already authorized'
sendMessage(msg, context.bot, update)
def unauthorize(update,context):
reply_message = update.message.reply_to_message
if reply_message is None:
# Trying to unauthorize a chat
chat_id = update.effective_chat.id
if chat_id in AUTHORIZED_CHATS:
AUTHORIZED_CHATS.remove(chat_id)
msg = 'Chat unauthorized'
else:
msg = 'Already unauthorized chat'
else:
# Trying to authorize someone in specific
user_id = reply_message.from_user.id
if user_id in AUTHORIZED_CHATS:
AUTHORIZED_CHATS.remove(user_id)
msg = 'Person unauthorized to use the bot!'
else:
msg = 'Person already unauthorized!'
with open('authorized_chats.txt', 'a') as file:
file.truncate(0)
for i in AUTHORIZED_CHATS:
file.write(f'{i}\n')
sendMessage(msg, context.bot, update)
authorize_handler = CommandHandler(command=BotCommands.AuthorizeCommand, callback=authorize,
filters=CustomFilters.owner_filter & Filters.group, run_async=True)
unauthorize_handler = CommandHandler(command=BotCommands.UnAuthorizeCommand, callback=unauthorize,
filters=CustomFilters.owner_filter & Filters.group, run_async=True)
dispatcher.add_handler(authorize_handler)
dispatcher.add_handler(unauthorize_handler)
================================================
FILE: bot/modules/cancel_mirror.py
================================================
from telegram.ext import CommandHandler
from bot import download_dict, dispatcher, download_dict_lock, DOWNLOAD_DIR
from bot.helper.ext_utils.fs_utils import clean_download
from bot.helper.telegram_helper.bot_commands import BotCommands
from bot.helper.telegram_helper.filters import CustomFilters
from bot.helper.telegram_helper.message_utils import *
from time import sleep
from bot.helper.ext_utils.bot_utils import getDownloadByGid, MirrorStatus
def cancel_mirror(update, context):
args = update.message.text.split(" ", maxsplit=1)
mirror_message = None
if len(args) > 1:
gid = args[1]
dl = getDownloadByGid(gid)
if not dl:
sendMessage(f"GID: {gid} not found.", context.bot, update)
return
with download_dict_lock:
keys = list(download_dict.keys())
mirror_message = dl.message
elif update.message.reply_to_message:
mirror_message = update.message.reply_to_message
with download_dict_lock:
keys = list(download_dict.keys())
dl = download_dict[mirror_message.message_id]
if len(args) == 1:
if mirror_message is None or mirror_message.message_id not in keys:
if BotCommands.MirrorCommand in mirror_message.text or \
BotCommands.TarMirrorCommand in mirror_message.text:
msg = "Mirror already have been cancelled"
sendMessage(msg, context.bot, update)
return
else:
msg = "Please reply to the /mirror message which was used to start the download or /cancel gid to cancel it!"
sendMessage(msg, context.bot, update)
return
if dl.status() == "Uploading":
sendMessage("Upload in Progress, Don't Cancel it.", context.bot, update)
return
elif dl.status() == "Archiving":
sendMessage("Archival in Progress, Don't Cancel it.", context.bot, update)
return
else:
dl.download().cancel_download()
sleep(1) # Wait a Second For Aria2 To free Resources.
clean_download(f'{DOWNLOAD_DIR}{mirror_message.message_id}/')
def cancel_all(update, context):
with download_dict_lock:
count = 0
for dlDetails in list(download_dict.values()):
if dlDetails.status() == MirrorStatus.STATUS_DOWNLOADING \
or dlDetails.status() == MirrorStatus.STATUS_WAITING:
dlDetails.download().cancel_download()
count += 1
delete_all_messages()
sendMessage(f'Cancelled {count} downloads!', context.bot, update)
cancel_mirror_handler = CommandHandler(BotCommands.CancelMirror, cancel_mirror,
filters=(CustomFilters.authorized_chat | CustomFilters.authorized_user) & CustomFilters.mirror_owner_filter, run_async=True)
cancel_all_handler = CommandHandler(BotCommands.CancelAllCommand, cancel_all,
filters=CustomFilters.owner_filter, run_async=True)
dispatcher.add_handler(cancel_all_handler)
dispatcher.add_handler(cancel_mirror_handler)
================================================
FILE: bot/modules/clone.py
================================================
from telegram.ext import CommandHandler
from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
from bot.helper.telegram_helper.message_utils import *
from bot.helper.telegram_helper.filters import CustomFilters
from bot.helper.telegram_helper.bot_commands import BotCommands
from bot.helper.ext_utils.bot_utils import new_thread
from bot import dispatcher
def cloneNode(update,context):
args = update.message.text.split(" ",maxsplit=1)
if len(args) > 1:
link = args[1]
msg = sendMessage(f"Cloning: {link}",context.bot,update)
gd = GoogleDriveHelper()
result, button = gd.clone(link)
deleteMessage(context.bot,msg)
if button == "":
sendMessage(result,context.bot,update)
else:
if update.message.from_user.username:
uname = f'@{update.message.from_user.username}'
else:
uname = f'{update.message.from_user.first_name}'
if uname is not None:
cc = f'\n\nReq. By: {uname}'
sendMarkup(result + cc, context.bot, update, button)
else:
sendMessage("Provide G-Drive Shareable Link to Clone.",context.bot,update)
clone_handler = CommandHandler(BotCommands.CloneCommand,cloneNode,filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
dispatcher.add_handler(clone_handler)
================================================
FILE: bot/modules/count.py
================================================
import bot
from telegram.ext import CommandHandler
from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
from bot.helper.telegram_helper.message_utils import deleteMessage, sendMessage
from bot.helper.telegram_helper.filters import CustomFilters
from bot.helper.telegram_helper.bot_commands import BotCommands
from bot import dispatcher, AUTHORIZED_CHATS
def countNode(update,context):
args = update.message.text.split(" ",maxsplit=1)
if len(args) > 1:
link = args[1]
msg = sendMessage(f"Counting: {link}",context.bot,update)
gd = GoogleDriveHelper()
result = gd.count(link)
deleteMessage(context.bot,msg)
if update.message.from_user.username:
uname = f'@{update.message.from_user.username}'
else:
uname = f'{update.message.from_user.first_name}'
if uname is not None:
cc = f'\n\nReq. By: {uname}'
sendMessage(result + cc, context.bot, update)
else:
sendMessage("Provide G-Drive Shareable Link to Count.",context.bot,update)
count_handler = CommandHandler(BotCommands.CountCommand,countNode,filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
dispatcher.add_handler(count_handler)
================================================
FILE: bot/modules/delete.py
================================================
from telegram.ext import CommandHandler
import threading
from telegram import Update
from bot import dispatcher, LOGGER
from bot.helper.telegram_helper.message_utils import auto_delete_message, sendMessage
from bot.helper.telegram_helper.filters import CustomFilters
from bot.helper.telegram_helper.bot_commands import BotCommands
from bot.helper.mirror_utils.upload_utils import gdriveTools
def deletefile(update, context):
msg_args = update.message.text.split(None, 1)
msg = ''
try:
link = msg_args[1]
LOGGER.info(msg_args[1])
except IndexError:
msg = 'send a link along with command'
if msg == '' :
drive = gdriveTools.GoogleDriveHelper()
msg = drive.deletefile(link)
LOGGER.info(f"this is msg : {msg}")
reply_message = sendMessage(msg, context.bot, update)
threading.Thread(target=auto_delete_message, args=(context.bot, update.message, reply_message)).start()
delete_handler = CommandHandler(command=BotCommands.deleteCommand, callback=deletefile,
filters=CustomFilters.owner_filter, run_async=True)
dispatcher.add_handler(delete_handler)
================================================
FILE: bot/modules/list.py
================================================
from telegram.ext import CommandHandler
from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
from bot import LOGGER, dispatcher
from bot.helper.telegram_helper.message_utils import sendMessage, sendMarkup, editMessage
from bot.helper.telegram_helper.filters import CustomFilters
from bot.helper.telegram_helper.bot_commands import BotCommands
def list_drive(update,context):
try:
search = update.message.text.split(' ',maxsplit=1)[1]
LOGGER.info(f"Searching: {search}")
reply = sendMessage('Searching..... Please wait!', context.bot, update)
gdrive = GoogleDriveHelper(None)
msg, button = gdrive.drive_list(search)
if button:
editMessage(msg, reply, button)
else:
editMessage('No result found', reply, button)
except IndexError:
sendMessage('send a search key along with command', context.bot, update)
except AttributeError:
pass
list_handler = CommandHandler(BotCommands.ListCommand, list_drive,filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
dispatcher.add_handler(list_handler)
================================================
FILE: bot/modules/mirror.py
================================================
import requests
from telegram.ext import CommandHandler
from telegram import InlineKeyboardMarkup
from bot import Interval, INDEX_URL, BUTTON_THREE_NAME, BUTTON_THREE_URL, BUTTON_FOUR_NAME, BUTTON_FOUR_URL, BUTTON_FIVE_NAME, BUTTON_FIVE_URL, BLOCK_MEGA_LINKS, MEGA_KEY
from bot import dispatcher, DOWNLOAD_DIR, DOWNLOAD_STATUS_UPDATE_INTERVAL, download_dict, download_dict_lock, SHORTENER, SHORTENER_API
from bot.helper.ext_utils import fs_utils, bot_utils
from bot.helper.ext_utils.bot_utils import setInterval
from bot.helper.ext_utils.exceptions import DirectDownloadLinkException, NotSupportedExtractionArchive
from bot.helper.mirror_utils.download_utils.aria2_download import AriaDownloadHelper
from bot.helper.mirror_utils.download_utils.mega_download import MegaDownloader
from bot.helper.mirror_utils.download_utils.direct_link_generator import direct_link_generator
from bot.helper.mirror_utils.download_utils.telegram_downloader import TelegramDownloadHelper
from bot.helper.mirror_utils.status_utils import listeners
from bot.helper.mirror_utils.status_utils.extract_status import ExtractStatus
from bot.helper.mirror_utils.status_utils.tar_status import TarStatus
from bot.helper.mirror_utils.status_utils.upload_status import UploadStatus
from bot.helper.mirror_utils.status_utils.gdownload_status import DownloadStatus
from bot.helper.mirror_utils.upload_utils import gdriveTools
from bot.helper.telegram_helper.bot_commands import BotCommands
from bot.helper.telegram_helper.filters import CustomFilters
from bot.helper.telegram_helper.message_utils import *
from bot.helper.telegram_helper import button_build
import urllib
import pathlib
import os
import subprocess
import threading
import re
import random
import string
ariaDlManager = AriaDownloadHelper()
ariaDlManager.start_listener()
class MirrorListener(listeners.MirrorListeners):
def __init__(self, bot, update, pswd, isTar=False, tag=None, extract=False):
super().__init__(bot, update)
self.isTar = isTar
self.tag = tag
self.extract = extract
self.pswd = pswd
def onDownloadStarted(self):
pass
def onDownloadProgress(self):
# We are handling this on our own!
pass
def clean(self):
try:
Interval[0].cancel()
del Interval[0]
delete_all_messages()
except IndexError:
pass
def onDownloadComplete(self):
with download_dict_lock:
LOGGER.info(f"Download completed: {download_dict[self.uid].name()}")
download = download_dict[self.uid]
name = download.name()
size = download.size_raw()
if name is None: # when pyrogram's media.file_name is of NoneType
name = os.listdir(f'{DOWNLOAD_DIR}{self.uid}')[0]
m_path = f'{DOWNLOAD_DIR}{self.uid}/{name}'
if self.isTar:
download.is_archiving = True
try:
with download_dict_lock:
download_dict[self.uid] = TarStatus(name, m_path, size)
path = fs_utils.tar(m_path)
except FileNotFoundError:
LOGGER.info('File to archive not found!')
self.onUploadError('Internal error occurred!!')
return
elif self.extract:
download.is_extracting = True
try:
path = fs_utils.get_base_name(m_path)
LOGGER.info(
f"Extracting : {name} "
)
with download_dict_lock:
download_dict[self.uid] = ExtractStatus(name, m_path, size)
pswd = self.pswd
if pswd is not None:
archive_result = subprocess.run(["pextract", m_path, pswd])
else:
archive_result = subprocess.run(["extract", m_path])
if archive_result.returncode == 0:
threading.Thread(target=os.remove, args=(m_path,)).start()
LOGGER.info(f"Deleting archive : {m_path}")
else:
LOGGER.warning('Unable to extract archive! Uploading anyway')
path = f'{DOWNLOAD_DIR}{self.uid}/{name}'
LOGGER.info(
f'got path : {path}'
)
except NotSupportedExtractionArchive:
LOGGER.info("Not any valid archive, uploading file as it is.")
path = f'{DOWNLOAD_DIR}{self.uid}/{name}'
else:
path = f'{DOWNLOAD_DIR}{self.uid}/{name}'
up_name = pathlib.PurePath(path).name
if up_name == "None":
up_name = "".join(os.listdir(f'{DOWNLOAD_DIR}{self.uid}/'))
up_path = f'{DOWNLOAD_DIR}{self.uid}/{up_name}'
LOGGER.info(f"Upload Name : {up_name}")
drive = gdriveTools.GoogleDriveHelper(up_name, self)
size = fs_utils.get_path_size(up_path)
upload_status = UploadStatus(drive, size, self)
with download_dict_lock:
download_dict[self.uid] = upload_status
update_all_messages()
drive.upload(up_name)
def onDownloadError(self, error):
error = error.replace('<', ' ')
error = error.replace('>', ' ')
LOGGER.info(self.update.effective_chat.id)
with download_dict_lock:
try:
download = download_dict[self.uid]
del download_dict[self.uid]
LOGGER.info(f"Deleting folder: {download.path()}")
fs_utils.clean_download(download.path())
LOGGER.info(str(download_dict))
except Exception as e:
LOGGER.error(str(e))
pass
count = len(download_dict)
if self.message.from_user.username:
uname = f"@{self.message.from_user.username}"
else:
uname = f'{self.message.from_user.first_name}'
msg = f"{uname} your download has been stopped due to: {error}"
sendMessage(msg, self.bot, self.update)
if count == 0:
self.clean()
else:
update_all_messages()
def onUploadStarted(self):
pass
def onUploadProgress(self):
pass
def onUploadComplete(self, link: str, size):
with download_dict_lock:
msg = f'Name:- {download_dict[self.uid].name()}\nSize:- {size}'
buttons = button_build.ButtonMaker()
if SHORTENER is not None and SHORTENER_API is not None:
surl = requests.get(f'https://{SHORTENER}/api?api={SHORTENER_API}&url={link}&format=text').text
buttons.buildbutton("Drive Link", surl)
else:
buttons.buildbutton("Drive Link", link)
LOGGER.info(f'Done Uploading {download_dict[self.uid].name()}')
if INDEX_URL is not None:
url_path = requests.utils.quote(f'{download_dict[self.uid].name()}')
share_url = f'{INDEX_URL}/{url_path}'
if os.path.isdir(f'{DOWNLOAD_DIR}/{self.uid}/{download_dict[self.uid].name()}'):
share_url += '/'
if SHORTENER is not None and SHORTENER_API is not None:
siurl = requests.get(f'https://{SHORTENER}/api?api={SHORTENER_API}&url={share_url}&format=text').text
buttons.buildbutton("Index Link", siurl)
else:
buttons.buildbutton("Index Link", share_url)
if BUTTON_THREE_NAME is not None and BUTTON_THREE_URL is not None:
buttons.buildbutton(f"{BUTTON_THREE_NAME}", f"{BUTTON_THREE_URL}")
if BUTTON_FOUR_NAME is not None and BUTTON_FOUR_URL is not None:
buttons.buildbutton(f"{BUTTON_FOUR_NAME}", f"{BUTTON_FOUR_URL}")
if BUTTON_FIVE_NAME is not None and BUTTON_FIVE_URL is not None:
buttons.buildbutton(f"{BUTTON_FIVE_NAME}", f"{BUTTON_FIVE_URL}")
if self.message.from_user.username:
uname = f"@{self.message.from_user.username}"
else:
uname = f'{self.message.from_user.first_name}'
if uname is not None:
msg += f'\n\nReq. By:- {uname}'
try:
fs_utils.clean_download(download_dict[self.uid].path())
except FileNotFoundError:
pass
del download_dict[self.uid]
count = len(download_dict)
sendMarkup(msg, self.bot, self.update, InlineKeyboardMarkup(buttons.build_menu(2)))
if count == 0:
self.clean()
else:
update_all_messages()
def onUploadError(self, error):
e_str = error.replace('<', '').replace('>', '')
with download_dict_lock:
try:
fs_utils.clean_download(download_dict[self.uid].path())
except FileNotFoundError:
pass
del download_dict[self.message.message_id]
count = len(download_dict)
sendMessage(e_str, self.bot, self.update)
if count == 0:
self.clean()
else:
update_all_messages()
def _mirror(bot, update, isTar=False, extract=False):
mesg = update.message.text.split('\n')
message_args = mesg[0].split(' ')
name_args = mesg[0].split('|')
try:
link = message_args[1]
print(link)
if link.startswith("|") or link.startswith("pswd: "):
link = ''
except IndexError:
link = ''
try:
name = name_args[1]
name = name.strip()
if name.startswith("pswd: "):
name = ''
except IndexError:
name = ''
try:
ussr = urllib.parse.quote(mesg[1], safe='')
pssw = urllib.parse.quote(mesg[2], safe='')
except:
ussr = ''
pssw = ''
if ussr != '' and pssw != '':
link = link.split("://", maxsplit=1)
link = f'{link[0]}://{ussr}:{pssw}@{link[1]}'
pswd = re.search('(?<=pswd: )(.*)', update.message.text)
if pswd is not None:
pswd = pswd.groups()
pswd = " ".join(pswd)
LOGGER.info(link)
link = link.strip()
reply_to = update.message.reply_to_message
if reply_to is not None:
file = None
tag = reply_to.from_user.username
media_array = [reply_to.document, reply_to.video, reply_to.audio]
for i in media_array:
if i is not None:
file = i
break
if not bot_utils.is_url(link) and not bot_utils.is_magnet(link) or len(link) == 0:
if file is not None:
if file.mime_type != "application/x-bittorrent":
listener = MirrorListener(bot, update, pswd, isTar, tag, extract)
tg_downloader = TelegramDownloadHelper(listener)
tg_downloader.add_download(reply_to, f'{DOWNLOAD_DIR}{listener.uid}/', name)
sendStatusMessage(update, bot)
if len(Interval) == 0:
Interval.append(setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages))
return
else:
link = file.get_file().file_path
else:
tag = None
if not bot_utils.is_url(link) and not bot_utils.is_magnet(link):
sendMessage('No download source provided', bot, update)
return
try:
link = direct_link_generator(link)
except DirectDownloadLinkException as e:
LOGGER.info(f'{link}: {e}')
listener = MirrorListener(bot, update, pswd, isTar, tag, extract)
if bot_utils.is_gdrive_link(link):
if not isTar and not extract:
sendMessage(f"Use /{BotCommands.CloneCommand} To Copy File/Folder", bot, update)
return
res, size, name = gdriveTools.GoogleDriveHelper().clonehelper(link)
if res != "":
sendMessage(res, bot, update)
return
LOGGER.info(f"Download Name : {name}")
drive = gdriveTools.GoogleDriveHelper(name, listener)
gid = ''.join(random.SystemRandom().choices(string.ascii_letters + string.digits, k=12))
download_status = DownloadStatus(drive, size, listener, gid)
with download_dict_lock:
download_dict[listener.uid] = download_status
if len(Interval) == 0:
Interval.append(setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages))
sendStatusMessage(update, bot)
drive.download(link)
elif bot_utils.is_mega_link(link) and MEGA_KEY is not None:
if BLOCK_MEGA_LINKS:
sendMessage("Mega Links Are Blocked.", bot, update)
else:
mega_dl = MegaDownloader(listener)
mega_dl.add_download(link, f'{DOWNLOAD_DIR}{listener.uid}/')
sendStatusMessage(update, bot)
else:
ariaDlManager.add_download(link, f'{DOWNLOAD_DIR}{listener.uid}/', listener, name)
sendStatusMessage(update, bot)
if len(Interval) == 0:
Interval.append(setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages))
def mirror(update, context):
_mirror(context.bot, update)
def tar_mirror(update, context):
_mirror(context.bot, update, True)
def unzip_mirror(update, context):
_mirror(context.bot, update, extract=True)
mirror_handler = CommandHandler(BotCommands.MirrorCommand, mirror,
filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
tar_mirror_handler = CommandHandler(BotCommands.TarMirrorCommand, tar_mirror,
filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
unzip_mirror_handler = CommandHandler(BotCommands.UnzipMirrorCommand, unzip_mirror,
filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
dispatcher.add_handler(mirror_handler)
dispatcher.add_handler(tar_mirror_handler)
dispatcher.add_handler(unzip_mirror_handler)
================================================
FILE: bot/modules/mirror_status.py
================================================
from telegram.ext import CommandHandler
from bot import dispatcher, status_reply_dict, DOWNLOAD_STATUS_UPDATE_INTERVAL, status_reply_dict_lock
from bot.helper.telegram_helper.message_utils import *
from time import sleep
from bot.helper.ext_utils.bot_utils import get_readable_message
from telegram.error import BadRequest
from bot.helper.telegram_helper.filters import CustomFilters
from bot.helper.telegram_helper.bot_commands import BotCommands
import threading
def mirror_status(update,context):
message = get_readable_message()
if len(message) == 0:
message = "No active downloads"
reply_message = sendMessage(message, context.bot, update)
threading.Thread(target=auto_delete_message, args=(bot, update.message, reply_message)).start()
return
index = update.effective_chat.id
with status_reply_dict_lock:
if index in status_reply_dict.keys():
deleteMessage(bot, status_reply_dict[index])
del status_reply_dict[index]
sendStatusMessage(update,context.bot)
deleteMessage(context.bot,update.message)
mirror_status_handler = CommandHandler(BotCommands.StatusCommand, mirror_status,
filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
dispatcher.add_handler(mirror_status_handler)
================================================
FILE: bot/modules/speedtest.py
================================================
import speedtest
import bot
from bot.helper.telegram_helper.filters import CustomFilters
from bot import dispatcher, AUTHORIZED_CHATS
from bot.helper.telegram_helper.bot_commands import BotCommands
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ParseMode
from telegram.ext import CallbackContext, Filters, CommandHandler
def speedtst(update, context):
message = update.effective_message
ed_msg = message.reply_text("Running Speed Test . . . 💨")
test = speedtest.Speedtest()
test.get_best_server()
test.download()
test.upload()
test.results.share()
result = test.results.dict()
context.bot.editMessageText(
"🔻 Download Speed : "
f"{speed_convert(result['download'])}\n"
"🔺 Upload Speed : "
f"{speed_convert(result['upload'])}\n"
"📶 Ping : "
f"{result['ping']}\n"
"🏬 ISP : "
f"{result['client']['isp']}",
update.effective_chat.id,
ed_msg.message_id,
)
def speed_convert(size):
"""Hi human, you can't read bytes?"""
power = 2 ** 10
zero = 0
units = {0: "", 1: "Kb/s", 2: "Mb/s", 3: "Gb/s", 4: "Tb/s"}
while size > power:
size /= power
zero += 1
return f"{round(size, 2)} {units[zero]}"
SPEED_HANDLER = CommandHandler(BotCommands.SpeedCommand, speedtst,
filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
dispatcher.add_handler(SPEED_HANDLER)
================================================
FILE: bot/modules/watch.py
================================================
from telegram.ext import CommandHandler
from telegram import Bot, Update
from bot import Interval, DOWNLOAD_DIR, DOWNLOAD_STATUS_UPDATE_INTERVAL, dispatcher, LOGGER
from bot.helper.ext_utils.bot_utils import setInterval
from bot.helper.telegram_helper.message_utils import update_all_messages, sendMessage, sendStatusMessage
from .mirror import MirrorListener
from bot.helper.mirror_utils.download_utils.youtube_dl_download_helper import YoutubeDLHelper
from bot.helper.telegram_helper.bot_commands import BotCommands
from bot.helper.telegram_helper.filters import CustomFilters
import threading
def _watch(bot: Bot, update, isTar=False):
mssg = update.message.text
message_args = mssg.split(' ')
name_args = mssg.split('|')
try:
link = message_args[1]
except IndexError:
msg = f"/{BotCommands.WatchCommand} [yt_dl supported link] [quality] |[CustomName] to mirror with youtube_dl.\n\n"
msg += "Note :- Quality and custom name are optional\n\nExample of quality :- audio, 144, 240, 360, 480, 720, 1080, 2160."
msg += "\n\nIf you want to use custom filename, plz enter it after |"
msg += f"\n\nExample :-\n/{BotCommands.WatchCommand} https://youtu.be/ocX2FN1nguA 720 |My video bro\n\n"
msg += "This file will be downloaded in 720p quality and it's name will be My video bro"
sendMessage(msg, bot, update)
return
try:
if "|" in mssg:
mssg = mssg.split("|")
qual = mssg[0].split(" ")[2]
if qual == "":
raise IndexError
else:
qual = message_args[2]
if qual != "audio":
qual = f'bestvideo[height<={qual}]+bestaudio/best[height<={qual}]'
except IndexError:
qual = "bestvideo+bestaudio/best"
try:
name = name_args[1]
except IndexError:
name = ""
reply_to = update.message.reply_to_message
if reply_to is not None:
tag = reply_to.from_user.username
else:
tag = None
pswd = ""
listener = MirrorListener(bot, update, pswd, isTar, tag)
ydl = YoutubeDLHelper(listener)
threading.Thread(target=ydl.add_download,args=(link, f'{DOWNLOAD_DIR}{listener.uid}', qual, name)).start()
sendStatusMessage(update, bot)
if len(Interval) == 0:
Interval.append(setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages))
def watchTar(update, context):
_watch(context.bot, update, True)
def watch(update, context):
_watch(context.bot, update)
mirror_handler = CommandHandler(BotCommands.WatchCommand, watch,
filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
tar_mirror_handler = CommandHandler(BotCommands.TarWatchCommand, watchTar,
filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
dispatcher.add_handler(mirror_handler)
dispatcher.add_handler(tar_mirror_handler)
================================================
FILE: config_sample.env
================================================
#Remove this line before deploying
_____REMOVE_THIS_LINE_____=True
## Telegram
BOT_TOKEN = "" # ENTER BOT TOKEN (Get your BOT_TOKEN by talking to @botfather)
OWNER_ID =
DOWNLOAD_STATUS_UPDATE_INTERVAL = 5
AUTO_DELETE_MESSAGE_DURATION = 20
TELEGRAM_API =
TELEGRAM_HASH = ""
AUTHORIZED_CHATS = "" #Separated by space
MAX_CONCURRENT_DOWNLOADS = 4
## Drive
GDRIVE_FOLDER_ID = ""
IS_TEAM_DRIVE = ""
INDEX_URL = ""
DOWNLOAD_DIR = "/app/downloads" # Dont change this if you are using Docker compose
USE_SERVICE_ACCOUNTS = ""
## Optional config
UPTOBOX_TOKEN = ""
MEGA_KEY = "" # Get this from https://mega.nz/sdk
MEGA_USERNAME = ""
MEGA_PASSWORD = ""
BLOCK_MEGA_LINKS = ""
STOP_DUPLICATE_MIRROR = ""
SHORTENER = ""
SHORTENER_API = ""
# Add more buttons (two buttons are already added of file link and index link, you can add extra buttons too, these are optional)
# If you don't know what are below entries, simply leave them, Don't fill anything in them.
BUTTON_THREE_NAME = ""
BUTTON_THREE_URL = ""
BUTTON_FOUR_NAME = ""
BUTTON_FOUR_URL = ""
BUTTON_FIVE_NAME = ""
BUTTON_FIVE_URL = ""
# Commands Customization (Completely Optional)
# If you want to change
START_COMMAND = ""
MIRROR_COMMAND = ""
UNZIP_COMMAND = ""
TAR_COMMAND = ""
CANCEL_COMMAND = ""
CANCELALL_COMMAND = ""
LIST_COMMAND = ""
SPEED_COMMAND = ""
COUNT_COMMAND = ""
STATUS_COMMAND = ""
AUTH_COMMAND = ""
UNAUTH_COMMAND = ""
PING_COMMAND = ""
RESTART_COMMAND = ""
STATS_COMMAND = ""
HELP_COMMAND = ""
LOG_COMMAND = ""
CLONE_COMMAND = ""
WATCH_COMMAND = ""
TARWATCH_COMMAND = ""
DELETE_COMMAND = ""
================================================
FILE: docker-compose.yml
================================================
version: "3.9"
services:
mirrorx:
image: ghcr.io/iamliquidx/mirrorx:latest
container_name: mirrorx
env_file:
# Should be filled up and should be kept in the same directory
- config.env
volumes:
# Make a folder in the host OS and keep SA(accounts folder) or token there
# syntax "host OS path":"where it should be mounted inside container"
- /home/someuser/mirrorx/accounts:/app/accounts
# for Service Accounts ex: /home/someuser/mirrorx/accounts:/app/accounts
# for token and credential.json
# - /home/someuser/mirrorx/token.pickle:/app/token.pickle
# - /home/someuser/mirrorx/credential.json:/app/credentials.json
# Incase you want to add a netrc file to youtube-dl
# - /home/someuser/mirrorx/netrc:/root/.netrc
# Dont edit the Container Directory (i.e Right Side one) as the bot runs in the same directory
restart: unless-stopped
# Restart policy, if container exits due to some error it will restart again
================================================
FILE: netrc
================================================
================================================
FILE: requirements.txt
================================================
requests==2.26.0
appdirs==1.4.4
aria2p==0.10.4
progress==1.6
psutil==5.8.0
python-telegram-bot==13.7
google-api-python-client==2.11.0
google-auth-httplib2==0.1.0
google-auth-oauthlib==0.4.5
js2py==0.71
python-dotenv==0.19.0
tenacity==8.0.1
python-magic==0.4.24
beautifulsoup4==4.9.3
Pyrogram
TgCrypto
yt-dlp
lxml==4.6.3
telegraph==1.4.1
speedtest-cli==2.1.3
messages==0.5.0
pybase64==1.1.4
lk21==1.6.0
megasdkrestclient
================================================
FILE: setup.py
================================================
from setuptools import setup, find_packages
import pathlib
CWD = pathlib.Path(__file__).parent
README = (CWD / "README.md").read_text()
setup(
name='MirrorX',
version='6.0.13',
packages=find_packages(),
long_description=README,
long_description_content_type="text/markdown",
url='https://github.com/iamliquidx/mirrorx',
license='GPL3.0',
author='',
author_email='',
include_package_data=True,
description='Telegram Mirror Bot',
platforms="any",
install_requires=[
"requests==2.26.0",
"appdirs==1.4.4",
"aria2p==0.10.4",
"progress==1.6",
"psutil==5.8.0",
"python-telegram-bot==13.7",
"google-api-python-client==2.11.0",
"google-auth-httplib2==0.1.0",
"google-auth-oauthlib==0.4.5",
"js2py==0.71",
"python-dotenv==0.19.0",
"tenacity==8.0.1",
"python-magic==0.4.24",
"beautifulsoup4==4.9.3",
"Pyrogram",
"TgCrypto",
"yt-dlp",
"lxml==4.6.3",
"telegraph==1.4.1",
"speedtest-cli==2.1.3",
"messages==0.5.0",
"pybase64==1.1.4",
"lk21==1.6.0",
"megasdkrestclient"
],
classifiers=[
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"License :: OSI Approved :: GNU General Public License (GPL)",
"Operating System :: POSIX :: Linux",
"Development Status :: 5 - Production/Stable"
],
python_requires=">=3.7",
entry_points={
"console_scripts":[
"MirrorXBot = bot.__main__:main"
]
},
package_data={
"": ["data/*.dat", "data/aria.conf"],
},
scripts=['bin/extract', 'bin/pextract', 'bin/MirrorX'],
)
================================================
FILE: utils/add_to_team_drive.py
================================================
from google.oauth2.service_account import Credentials
import googleapiclient.discovery, json, progress.bar, glob, sys, argparse, time
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
import os, pickle
stt = time.time()
parse = argparse.ArgumentParser(
description='A tool to add service accounts to a shared drive from a folder containing credential files.')
parse.add_argument('--path', '-p', default='accounts',
help='Specify an alternative path to the service accounts folder.')
parse.add_argument('--credentials', '-c', default='./credentials.json',
help='Specify the relative path for the credentials file.')
parse.add_argument('--yes', '-y', default=False, action='store_true', help='Skips the sanity prompt.')
parsereq = parse.add_argument_group('required arguments')
parsereq.add_argument('--drive-id', '-d', help='The ID of the Shared Drive.', required=True)
args = parse.parse_args()
acc_dir = args.path
did = args.drive_id
credentials = glob.glob(args.credentials)
try:
open(credentials[0])
print('>> Found credentials.')
except IndexError:
print('>> No credentials found.')
sys.exit(0)
if not args.yes:
# input('Make sure the following client id is added to the shared drive as Manager:\n' + json.loads((open(
# credentials[0],'r').read()))['installed']['client_id'])
input('>> Make sure the **Google account** that has generated credentials.json\n is added into your Team Drive '
'(shared drive) as Manager\n>> (Press any key to continue)')
creds = None
if os.path.exists('token_sa.pickle'):
with open('token_sa.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(credentials[0], scopes=[
'https://www.googleapis.com/auth/admin.directory.group',
'https://www.googleapis.com/auth/admin.directory.group.member'
])
# creds = flow.run_local_server(port=0)
creds = flow.run_console()
# Save the credentials for the next run
with open('token_sa.pickle', 'wb') as token:
pickle.dump(creds, token)
drive = googleapiclient.discovery.build("drive", "v3", credentials=creds)
batch = drive.new_batch_http_request()
aa = glob.glob('%s/*.json' % acc_dir)
pbar = progress.bar.Bar("Readying accounts", max=len(aa))
for i in aa:
ce = json.loads(open(i).read())['client_email']
batch.add(drive.permissions().create(fileId=did, supportsAllDrives=True, body={
"role": "fileOrganizer",
"type": "user",
"emailAddress": ce
}))
pbar.next()
pbar.finish()
print('Adding...')
batch.execute()
print('Complete.')
hours, rem = divmod((time.time() - stt), 3600)
minutes, sec = divmod(rem, 60)
print("Elapsed Time:\n{:0>2}:{:0>2}:{:05.2f}".format(int(hours), int(minutes), sec))
================================================
FILE: utils/gen_sa_accounts.py
================================================
import errno
import os
import pickle
import sys
from argparse import ArgumentParser
from base64 import b64decode
from glob import glob
from json import loads
from random import choice
from time import sleep
from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/iam']
project_create_ops = []
current_key_dump = []
sleep_time = 30
# Create count SAs in project
def _create_accounts(service, project, count):
batch = service.new_batch_http_request(callback=_def_batch_resp)
for i in range(count):
aid = _generate_id('mfc-')
batch.add(service.projects().serviceAccounts().create(name='projects/' + project, body={'accountId': aid,
'serviceAccount': {
'displayName': aid}}))
batch.execute()
# Create accounts needed to fill project
def _create_remaining_accounts(iam, project):
print('Creating accounts in %s' % project)
sa_count = len(_list_sas(iam, project))
while sa_count != 100:
_create_accounts(iam, project, 100 - sa_count)
sa_count = len(_list_sas(iam, project))
# Generate a random id
def _generate_id(prefix='saf-'):
chars = '-abcdefghijklmnopqrstuvwxyz1234567890'
return prefix + ''.join(choice(chars) for _ in range(25)) + choice(chars[1:])
# List projects using service
def _get_projects(service):
return [i['projectId'] for i in service.projects().list().execute()['projects']]
# Default batch callback handler
def _def_batch_resp(id, resp, exception):
if exception is not None:
if str(exception).startswith('