Skip to content

Commit 94ccdcb

Browse files
committed
Add support for mirror of telegram files
Signed-off-by: lzzy12 <[email protected]> Add a script to generate string session for user Signed-off-by: lzzy12 <[email protected]> Some fix ups Signed-off-by: lzzy12 <[email protected]> Fix telegram download Signed-off-by: lzzy12 <[email protected]>
1 parent 898977d commit 94ccdcb

11 files changed

+170
-11
lines changed

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ Fill up rest of the fields. Meaning of each fields are discussed below:
5454
- AUTO_DELETE_MESSAGE_DURATION : Interval of time (in seconds), after which the bot deletes it's message (and command message) which is expected to be viewed instantly. Note: Set to -1 to never automatically delete messages
5555
- IS_TEAM_DRIVE : (Optional field) Set to "True" if GDRIVE_FOLDER_ID is from a Team Drive else False or Leave it empty.
5656
- INDEX_URL : (Optional field) Refer to https://github.com/maple3142/GDIndex/ The URL should not have any trailing '/'
57-
57+
- USER_SESSION_STRING : Session string generated by running:
58+
```
59+
python3 generate_string_session.py
60+
```
5861
Note: You can limit maximum concurrent downloads by changing the value of MAX_CONCURRENT_DOWNLOADS in aria.sh. By default, it's set to 2
5962

6063
## Getting Google OAuth API credential file

bot/__init__.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
1515
handlers=[logging.FileHandler('log.txt'), logging.StreamHandler()],
16-
level=logging.INFO)
16+
level=logging.WARNING)
1717

1818
load_dotenv('config.env')
1919

@@ -69,6 +69,9 @@ def getConfig(name: str):
6969
DOWNLOAD_STATUS_UPDATE_INTERVAL = int(getConfig('DOWNLOAD_STATUS_UPDATE_INTERVAL'))
7070
OWNER_ID = int(getConfig('OWNER_ID'))
7171
AUTO_DELETE_MESSAGE_DURATION = int(getConfig('AUTO_DELETE_MESSAGE_DURATION'))
72+
USER_SESSION_STRING = getConfig('USER_SESSION_STRING')
73+
TELEGRAM_API = getConfig('TELEGRAM_API')
74+
TELEGRAM_HASH = getConfig('TELEGRAM_HASH')
7275
except KeyError as e:
7376
LOGGER.error("One or more env variables missing! Exiting now")
7477
exit(1)
@@ -88,4 +91,4 @@ def getConfig(name: str):
8891
IS_TEAM_DRIVE = False
8992
updater = tg.Updater(token=BOT_TOKEN)
9093
bot = updater.bot
91-
dispatcher = updater.dispatcher
94+
dispatcher = updater.dispatcher

bot/helper/mirror_utils/download_utils/aria2_download.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
from bot import aria2,download_dict,download_dict_lock
1+
from bot import aria2
22
from bot.helper.ext_utils.bot_utils import *
33
from .download_helper import DownloadHelper
44
from bot.helper.mirror_utils.status_utils.aria_download_status import AriaDownloadStatus
55
from bot.helper.telegram_helper.message_utils import *
66
import threading
77
from aria2p import API
88

9+
910
class AriaDownloadHelper(DownloadHelper):
1011

1112
def __init__(self, listener):

bot/helper/mirror_utils/download_utils/download_helper.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def __init__(self):
1616
self.progress = 0.0
1717
self.progress_string = '0.00%'
1818
self.eta = 0 # Estimated time of download complete
19-
self.eta_string = '0s' # A listener class which have event callbacks
19+
self.eta_string = '0s' # A listener class which have event callbacks
2020
self._resource_lock = threading.Lock()
2121

2222
def add_download(self, link: str, path):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from .download_helper import DownloadHelper
2+
import threading
3+
import time
4+
from ..status_utils.telegram_download_status import TelegramDownloadStatus
5+
from bot.helper.ext_utils.bot_utils import get_readable_file_size
6+
from bot import LOGGER, bot, download_dict, download_dict_lock, TELEGRAM_API,\
7+
TELEGRAM_HASH, USER_SESSION_STRING
8+
from pyrogram import Client
9+
10+
global_lock = threading.Lock()
11+
GLOBAL_GID = set()
12+
13+
class TelegramDownloadHelper(DownloadHelper):
14+
def __init__(self, listener):
15+
super().__init__()
16+
self.__listener = listener
17+
self.__resource_lock = threading.RLock()
18+
self.__name = ""
19+
self.__gid = ''
20+
self.__start_time = time.time()
21+
self.__user_bot = Client(api_id=TELEGRAM_API,
22+
api_hash=TELEGRAM_HASH,
23+
session_name=USER_SESSION_STRING)
24+
self.__user_bot.start()
25+
26+
@property
27+
def gid(self):
28+
with self.__resource_lock:
29+
return self.__gid
30+
31+
@property
32+
def download_speed(self):
33+
with self.__resource_lock:
34+
return self.downloaded_bytes / (time.time() - self.__start_time)
35+
36+
def __onDownloadStart(self, name, size, file_id):
37+
with download_dict_lock:
38+
download_dict[self.__listener.uid] = TelegramDownloadStatus(self, self.__listener.uid)
39+
with global_lock:
40+
GLOBAL_GID.add(file_id)
41+
with self.__resource_lock:
42+
self.name = name
43+
self.size = size
44+
self.__gid = file_id
45+
self.__listener.onDownloadStarted()
46+
47+
def __onDownloadProgress(self, current, total):
48+
with self.__resource_lock:
49+
self.downloaded_bytes = current
50+
try:
51+
self.progress = current / self.size * 100
52+
except ZeroDivisionError:
53+
return 0
54+
def __onDownloadComplete(self):
55+
56+
self.__listener.onDownloadComplete()
57+
58+
def __download(self, message, path):
59+
self.__user_bot.download_media(message,
60+
progress=self.__onDownloadProgress, file_name=path)
61+
self.__onDownloadComplete()
62+
63+
def add_download(self, message, path):
64+
if message.chat.type == "private":
65+
_message = self.__user_bot.get_messages(bot.get_me().id, message.message_id)
66+
else:
67+
_message = self.__user_bot.get_messages(message.chat.id, message.message_id)
68+
media = _message.document
69+
if media is not None:
70+
with global_lock:
71+
# For avoiding locking the thread lock for long time unnecessarily
72+
download = media.file_id not in GLOBAL_GID
73+
74+
if download:
75+
self.__onDownloadStart(media.file_name, media.file_size, media.file_id)
76+
LOGGER.info(media.file_id)
77+
threading.Thread(target=self.__download, args=(_message, path)).start()
78+
else:
79+
self.__listener.onDownloadError('File already being downloaded!')
80+
else:
81+
self.__listener.onDownloadError('No document in the replied message')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size, get_readable_time
2+
from .status import Status
3+
from bot import DOWNLOAD_DIR
4+
5+
6+
class TelegramDownloadStatus(Status):
7+
def __init__(self, obj, uid):
8+
self.obj = obj
9+
self.uid = uid
10+
11+
def path(self):
12+
return f"{DOWNLOAD_DIR}{self.uid}"
13+
14+
def processed_bytes(self):
15+
return self.obj.downloaded_bytes
16+
17+
def size_raw(self):
18+
return self.obj.size
19+
20+
def size(self):
21+
return get_readable_file_size(self.size_raw())
22+
23+
def status(self):
24+
return MirrorStatus.STATUS_DOWNLOADING
25+
26+
def name(self):
27+
return self.obj.name
28+
29+
def progress_raw(self):
30+
return self.obj.progress
31+
32+
def progress(self):
33+
return f'{round(self.progress_raw(), 2)}%'
34+
35+
def speed_raw(self):
36+
"""
37+
:return: Download speed in Bytes/Seconds
38+
"""
39+
return self.obj.download_speed
40+
41+
def speed(self):
42+
return f'{get_readable_file_size(self.speed_raw())}/s'
43+
44+
def eta(self):
45+
try:
46+
seconds = (self.size_raw() - self.processed_bytes()) / self.speed_raw()
47+
return f'{get_readable_time(seconds)}'
48+
except ZeroDivisionError:
49+
return '-'

bot/helper/telegram_helper/message_utils.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from telegram.update import Update
33
import time
44
from bot import AUTO_DELETE_MESSAGE_DURATION, LOGGER, bot, \
5-
status_reply_dict, status_reply_dict_lock, download_dict_lock, download_dict
5+
status_reply_dict, status_reply_dict_lock
66
from bot.helper.ext_utils.bot_utils import get_readable_message
77
from telegram.error import TimedOut, BadRequest
88
from bot import bot

bot/modules/mirror.py

+15-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from bot.helper.mirror_utils.download_utils import aria2_download
55
from bot.helper.mirror_utils.status_utils.upload_status import UploadStatus
66
from bot.helper.mirror_utils.status_utils.tar_status import TarStatus
7-
from bot import dispatcher, DOWNLOAD_DIR, DOWNLOAD_STATUS_UPDATE_INTERVAL
7+
from bot import dispatcher, DOWNLOAD_DIR, DOWNLOAD_STATUS_UPDATE_INTERVAL, download_dict, download_dict_lock
88
from bot.helper.ext_utils import fs_utils, bot_utils
99
from bot import Interval, INDEX_URL
1010
from bot.helper.telegram_helper.message_utils import *
@@ -14,9 +14,10 @@
1414
import pathlib
1515
import os
1616
from bot.helper.mirror_utils.download_utils.direct_link_generator import direct_link_generator
17+
from bot.helper.mirror_utils.download_utils.telegram_downloader import TelegramDownloadHelper
1718
from bot.helper.ext_utils.exceptions import DirectDownloadLinkException
1819
import requests
19-
20+
import threading
2021

2122
class MirrorListener(listeners.MirrorListeners):
2223
def __init__(self, bot, update, isTar=False, tag=None):
@@ -142,9 +143,19 @@ def _mirror(bot, update, isTar=False):
142143
reply_to = update.message.reply_to_message
143144
if reply_to is not None:
144145
tag = reply_to.from_user.username
146+
document = reply_to.document
145147
if len(link) == 0:
146-
if reply_to.document is not None and reply_to.document.mime_type == "application/x-bittorrent":
147-
link = reply_to.document.get_file().file_path
148+
if document is not None:
149+
if document.file_size <= 20 * 1024 * 1024:
150+
link = document.get_file().file_path
151+
else:
152+
listener = MirrorListener(bot, update, isTar, tag)
153+
tg_downloader = TelegramDownloadHelper(listener)
154+
tg_downloader.add_download(reply_to, f'{DOWNLOAD_DIR}{listener.uid}/')
155+
sendStatusMessage(update, bot)
156+
if len(Interval) == 0:
157+
Interval.append(setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages))
158+
return
148159
else:
149160
tag = None
150161
if not bot_utils.is_url(link) and not bot_utils.is_magnet(link):

config_sample.env

+3
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ DOWNLOAD_STATUS_UPDATE_INTERVAL = 5
1010
AUTO_DELETE_MESSAGE_DURATION = 20
1111
IS_TEAM_DRIVE = ""
1212
INDEX_URL = ""
13+
USER_SESSION_STRING = ""
14+
TELEGRAM_API =
15+
TELEGRAM_HASH = ""

generate_string_session.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from pyrogram import Client
2+
3+
API_KEY = int(input("Enter API KEY: "))
4+
API_HASH = input("Enter API HASH: ")
5+
with Client(':memory:', api_id=API_KEY, api_hash=API_HASH) as app:
6+
print(app.export_session_string())

requirements.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ aria2p>=0.3.0,<0.10.0
77
python-dotenv>=0.10
88
tenacity>=6.0.0
99
python-magic
10-
beautifulsoup4>=4.8.2,<4.8.10
10+
beautifulsoup4>=4.8.2,<4.8.10
11+
Pyrogram>=0.16.0,<0.16.10
12+
TgCrypto>=1.1.1,<1.1.10

0 commit comments

Comments
 (0)