Skip to content

Commit 8416764

Browse files
Merge branch 'staging-3.0'
Signed-off-by: lzzy12 <jhashivam2020@gmail.com>
2 parents b8730f7 + d393878 commit 8416764

36 files changed

Lines changed: 1756 additions & 253 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ data*
1010
*.pickle
1111
authorized_chats.txt
1212
log.txt
13+
accounts/*

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "vendor/cmrudl.py"]
2+
path = vendor/cmrudl.py
3+
url = https://github.com/JrMasterModelBuilder/cmrudl.py.git

Dockerfile

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ FROM ubuntu:18.04
22

33
WORKDIR /usr/src/app
44
RUN chmod 777 /usr/src/app
5-
RUN apt -qq update
6-
RUN apt -qq install -y aria2 python3 python3-pip locales
5+
RUN apt-get -qq update
6+
RUN apt-get -qq install -y aria2 python3 python3-pip \
7+
locales python3-lxml \
8+
curl pv jq ffmpeg
79
COPY requirements.txt .
810
RUN pip3 install --no-cache-dir -r requirements.txt
9-
COPY . .
10-
RUN chmod +x aria.sh
1111
RUN locale-gen en_US.UTF-8
1212
ENV LANG en_US.UTF-8
1313
ENV LANGUAGE en_US:en
1414
ENV LC_ALL en_US.UTF-8
15+
COPY . .
16+
COPY netrc /root/.netrc
17+
RUN chmod +x aria.sh
1518

1619
CMD ["bash","start.sh"]

README.md

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ This project is heavily inspired from @out386 's telegram bot which is written i
1212
- Docker support
1313
- Uploading To Team Drives.
1414
- Index Link support
15+
- Service account support
16+
- Mirror all youtube-dl supported links
17+
- Mirror telegram files
1518

1619
# Upcoming features (TODOs):
17-
- Mirror from Telegram files
1820

1921
# How to deploy?
2022
Deploying is pretty much straight forward and is divided into several steps as follows:
@@ -46,25 +48,33 @@ cp config_sample.env config.env
4648
_____REMOVE_THIS_LINE_____=True
4749
```
4850
Fill up rest of the fields. Meaning of each fields are discussed below:
49-
- BOT_TOKEN : The telegram bot token that you get from @BotFather
50-
- GDRIVE_FOLDER_ID : This is the folder ID of the Google Drive Folder to which you want to upload all the mirrors.
51-
- DOWNLOAD_DIR : The path to the local folder where the downloads should be downloaded to
52-
- DOWNLOAD_STATUS_UPDATE_INTERVAL : A short interval of time in seconds after which the Mirror progress message is updated. (I recommend to keep it 5 seconds at least)
53-
- OWNER_ID : The Telegram user ID (not username) of the owner of the bot
54-
- 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
55-
- IS_TEAM_DRIVE : (Optional field) Set to "True" if GDRIVE_FOLDER_ID is from a Team Drive else False or Leave it empty.
56-
- INDEX_URL : (Optional field) Refer to https://github.com/maple3142/GDIndex/ The URL should not have any trailing '/'
57-
51+
- **BOT_TOKEN** : The telegram bot token that you get from @BotFather
52+
- **GDRIVE_FOLDER_ID** : This is the folder ID of the Google Drive Folder to which you want to upload all the mirrors.
53+
- **DOWNLOAD_DIR** : The path to the local folder where the downloads should be downloaded to
54+
- **DOWNLOAD_STATUS_UPDATE_INTERVAL** : A short interval of time in seconds after which the Mirror progress message is updated. (I recommend to keep it 5 seconds at least)
55+
- **OWNER_ID** : The Telegram user ID (not username) of the owner of the bot
56+
- **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
57+
- **IS_TEAM_DRIVE** : (Optional field) Set to "True" if GDRIVE_FOLDER_ID is from a Team Drive else False or Leave it empty.
58+
- **USE_SERVICE_ACCOUNTS**: (Optional field) (Leave empty if unsure) Whether to use service accounts or not. For this to work see "Using service accounts" section below.
59+
- **INDEX_URL** : (Optional field) Refer to https://github.com/maple3142/GDIndex/ The URL should not have any trailing '/'
60+
- **API_KEY** : This is to authenticate to your telegram account for downloading Telegram files. You can get this from https://my.telegram.org DO NOT put this in quotes.
61+
- **API_HASH** : This is to authenticate to your telegram account for downloading Telegram files. You can get this from https://my.telegram.org
62+
- **USER_SESSION_STRING** : Session string generated by running:
63+
```
64+
python3 generate_string_session.py
65+
```
5866
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
5967

6068
## Getting Google OAuth API credential file
6169

62-
- Visit the Google Cloud Console
70+
- Visit the [Google Cloud Console](https://console.developers.google.com/apis/credentials)
6371
- Go to the OAuth Consent tab, fill it, and save.
6472
- Go to the Credentials tab and click Create Credentials -> OAuth Client ID
6573
- Choose Other and Create.
6674
- Use the download button to download your credentials.
6775
- Move that file to the root of mirror-bot, and rename it to credentials.json
76+
- Visit [Google API page](https://console.developers.google.com/apis/library)
77+
- Search for Drive and enable it if it is disabled
6878
- Finally, run the script to generate token file (token.pickle) for Google Drive:
6979
```
7080
pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib
@@ -84,3 +94,38 @@ sudo docker build . -t mirror-bot
8494
```
8595
sudo docker run mirror-bot
8696
```
97+
98+
# Using service accounts for uploading to avoid user rate limit
99+
For Service Account to work, you must set USE_SERVICE_ACCOUNTS="True" in config file or environment variables
100+
Many thanks to [AutoRClone](https://github.com/xyou365/AutoRclone) for the scripts
101+
## Generating service accounts
102+
Step 1. Generate service accounts [What is service account](https://cloud.google.com/iam/docs/service-accounts)
103+
---------------------------------
104+
Let us create only the service accounts that we need.
105+
**Warning:** abuse of this feature is not the aim of autorclone and we do **NOT** recommend that you make a lot of projects, just one project and 100 sa allow you plenty of use, its also possible that overabuse might get your projects banned by google.
106+
107+
```
108+
Note: 1 service account can copy around 750gb a day, 1 project makes 100 service accounts so thats 75tb a day, for most users this should easily suffice.
109+
```
110+
111+
`python3 gen_sa_accounts.py --quick-setup 1 --new-only`
112+
113+
A folder named accounts will be created which will contain keys for the service accounts created
114+
115+
NOTE: If you have created SAs in past from this script, you can also just re download the keys by running:
116+
```
117+
python3 gen_sa_accounts.py --download-keys project_id
118+
```
119+
120+
### Add all the service accounts to the Team Drive or folder
121+
- Run:
122+
```
123+
python3 add_to_team_drive.py -d SharedTeamDriveSrcID
124+
```
125+
126+
# Youtube-dl authentication using .netrc file
127+
For using your premium accounts in youtube-dl, edit the netrc file (in the root directory of this repository) according to following format:
128+
```
129+
machine host login username password my_youtube_password
130+
```
131+
where host is the name of extractor (eg. youtube, twitch). Multiple accounts of different hosts can be added each separated by a new line

add_to_team_drive.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from __future__ import print_function
2+
from google.oauth2.service_account import Credentials
3+
import googleapiclient.discovery, json, progress.bar, glob, sys, argparse, time
4+
from google_auth_oauthlib.flow import InstalledAppFlow
5+
from google.auth.transport.requests import Request
6+
import os, pickle
7+
8+
stt = time.time()
9+
10+
parse = argparse.ArgumentParser(
11+
description='A tool to add service accounts to a shared drive from a folder containing credential files.')
12+
parse.add_argument('--path', '-p', default='accounts',
13+
help='Specify an alternative path to the service accounts folder.')
14+
parse.add_argument('--credentials', '-c', default='./credentials.json',
15+
help='Specify the relative path for the credentials file.')
16+
parse.add_argument('--yes', '-y', default=False, action='store_true', help='Skips the sanity prompt.')
17+
parsereq = parse.add_argument_group('required arguments')
18+
parsereq.add_argument('--drive-id', '-d', help='The ID of the Shared Drive.', required=True)
19+
20+
args = parse.parse_args()
21+
acc_dir = args.path
22+
did = args.drive_id
23+
credentials = glob.glob(args.credentials)
24+
25+
try:
26+
open(credentials[0], 'r')
27+
print('>> Found credentials.')
28+
except IndexError:
29+
print('>> No credentials found.')
30+
sys.exit(0)
31+
32+
if not args.yes:
33+
# input('Make sure the following client id is added to the shared drive as Manager:\n' + json.loads((open(
34+
# credentials[0],'r').read()))['installed']['client_id'])
35+
input('>> Make sure the **Google account** that has generated credentials.json\n is added into your Team Drive '
36+
'(shared drive) as Manager\n>> (Press any key to continue)')
37+
38+
creds = None
39+
if os.path.exists('token_sa.pickle'):
40+
with open('token_sa.pickle', 'rb') as token:
41+
creds = pickle.load(token)
42+
# If there are no (valid) credentials available, let the user log in.
43+
if not creds or not creds.valid:
44+
if creds and creds.expired and creds.refresh_token:
45+
creds.refresh(Request())
46+
else:
47+
flow = InstalledAppFlow.from_client_secrets_file(credentials[0], scopes=[
48+
'https://www.googleapis.com/auth/admin.directory.group',
49+
'https://www.googleapis.com/auth/admin.directory.group.member'
50+
])
51+
# creds = flow.run_local_server(port=0)
52+
creds = flow.run_console()
53+
# Save the credentials for the next run
54+
with open('token_sa.pickle', 'wb') as token:
55+
pickle.dump(creds, token)
56+
57+
drive = googleapiclient.discovery.build("drive", "v3", credentials=creds)
58+
batch = drive.new_batch_http_request()
59+
60+
aa = glob.glob('%s/*.json' % acc_dir)
61+
pbar = progress.bar.Bar("Readying accounts", max=len(aa))
62+
for i in aa:
63+
ce = json.loads(open(i, 'r').read())['client_email']
64+
batch.add(drive.permissions().create(fileId=did, supportsAllDrives=True, body={
65+
"role": "fileOrganizer",
66+
"type": "user",
67+
"emailAddress": ce
68+
}))
69+
pbar.next()
70+
pbar.finish()
71+
print('Adding...')
72+
batch.execute()
73+
74+
print('Complete.')
75+
hours, rem = divmod((time.time() - stt), 3600)
76+
minutes, sec = divmod(rem, 60)
77+
print("Elapsed Time:\n{:0>2}:{:0>2}:{:05.2f}".format(int(hours), int(minutes), sec))

bot/__init__.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import logging
2-
import aria2p
3-
import threading
42
import os
5-
from dotenv import load_dotenv
6-
import telegram.ext as tg
3+
import threading
74
import time
85

6+
import aria2p
7+
import telegram.ext as tg
8+
from dotenv import load_dotenv
9+
import socket
10+
11+
socket.setdefaulttimeout(600)
12+
913
botStartTime = time.time()
1014
if os.path.exists('log.txt'):
1115
with open('log.txt', 'r+') as f:
@@ -69,6 +73,9 @@ def getConfig(name: str):
6973
DOWNLOAD_STATUS_UPDATE_INTERVAL = int(getConfig('DOWNLOAD_STATUS_UPDATE_INTERVAL'))
7074
OWNER_ID = int(getConfig('OWNER_ID'))
7175
AUTO_DELETE_MESSAGE_DURATION = int(getConfig('AUTO_DELETE_MESSAGE_DURATION'))
76+
USER_SESSION_STRING = getConfig('USER_SESSION_STRING')
77+
TELEGRAM_API = getConfig('TELEGRAM_API')
78+
TELEGRAM_HASH = getConfig('TELEGRAM_HASH')
7279
except KeyError as e:
7380
LOGGER.error("One or more env variables missing! Exiting now")
7481
exit(1)
@@ -80,13 +87,22 @@ def getConfig(name: str):
8087
INDEX_URL = None
8188
try:
8289
IS_TEAM_DRIVE = getConfig('IS_TEAM_DRIVE')
83-
if IS_TEAM_DRIVE == 'True' or IS_TEAM_DRIVE == 'true':
90+
if IS_TEAM_DRIVE.lower() == 'true':
8491
IS_TEAM_DRIVE = True
8592
else:
8693
IS_TEAM_DRIVE = False
87-
8894
except KeyError:
8995
IS_TEAM_DRIVE = False
90-
updater = tg.Updater(token=BOT_TOKEN)
96+
97+
try:
98+
USE_SERVICE_ACCOUNTS = getConfig('USE_SERVICE_ACCOUNTS')
99+
if USE_SERVICE_ACCOUNTS.lower() == 'true':
100+
USE_SERVICE_ACCOUNTS = True
101+
else:
102+
USE_SERVICE_ACCOUNTS = False
103+
except KeyError:
104+
USE_SERVICE_ACCOUNTS = False
105+
106+
updater = tg.Updater(token=BOT_TOKEN,use_context=True)
91107
bot = updater.bot
92108
dispatcher = updater.dispatcher

bot/__main__.py

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,76 @@
1+
import shutil
2+
import signal
3+
import pickle
4+
5+
from os import execl, path, remove
6+
from sys import executable
7+
18
from telegram.ext import CommandHandler, run_async
2-
from bot import dispatcher, LOGGER, updater, botStartTime
9+
from bot import dispatcher, updater, botStartTime
310
from bot.helper.ext_utils import fs_utils
4-
from .helper.ext_utils.bot_utils import get_readable_file_size, get_readable_time
5-
import signal
6-
import time
11+
from bot.helper.telegram_helper.bot_commands import BotCommands
712
from bot.helper.telegram_helper.message_utils import *
8-
import shutil
13+
from .helper.ext_utils.bot_utils import get_readable_file_size, get_readable_time
914
from .helper.telegram_helper.filters import CustomFilters
10-
from bot.helper.telegram_helper.bot_commands import BotCommands
11-
from .modules import authorize, list, cancel_mirror, mirror_status, mirror
15+
from .modules import authorize, list, cancel_mirror, mirror_status, mirror, clone, watch
16+
1217

1318
@run_async
14-
def stats(bot,update):
19+
def stats(update, context):
1520
currentTime = get_readable_time((time.time() - botStartTime))
1621
total, used, free = shutil.disk_usage('.')
1722
total = get_readable_file_size(total)
1823
used = get_readable_file_size(used)
1924
free = get_readable_file_size(free)
2025
stats = f'Bot Uptime: {currentTime}\n' \
2126
f'Total disk space: {total}\n' \
22-
f'Used: {used}\n' \
23-
f'Free: {free}'
24-
sendMessage(stats, bot, update)
25-
27+
f'Used: {used}\n' \
28+
f'Free: {free}'
29+
sendMessage(stats, context.bot, update)
2630

2731

2832
@run_async
29-
def start(bot,update):
33+
def start(update, context):
3034
sendMessage("This is a bot which can mirror all your links to Google drive!\n"
31-
"Type /help to get a list of available commands", bot, update)
35+
"Type /help to get a list of available commands", context.bot, update)
3236

3337

3438
@run_async
35-
def ping(bot,update):
39+
def restart(update, context):
40+
restart_message = sendMessage("Restarting, Please wait!", context.bot, update)
41+
# Save restart message object in order to reply to it after restarting
42+
fs_utils.clean_all()
43+
with open('restart.pickle', 'wb') as status:
44+
pickle.dump(restart_message, status)
45+
execl(executable, executable, "-m", "bot")
46+
47+
48+
@run_async
49+
def ping(update, context):
3650
start_time = int(round(time.time() * 1000))
37-
reply = sendMessage("Starting Ping", bot, update)
38-
end_time = int(round(time.time()*1000))
39-
editMessage(f'{end_time - start_time} ms',reply)
51+
reply = sendMessage("Starting Ping", context.bot, update)
52+
end_time = int(round(time.time() * 1000))
53+
editMessage(f'{end_time - start_time} ms', reply)
4054

4155

4256
@run_async
43-
def log(bot,update):
44-
sendLogFile(bot, update)
57+
def log(update, context):
58+
sendLogFile(context.bot, update)
4559

4660

4761
@run_async
48-
def bot_help(bot,update):
62+
def bot_help(update, context):
4963
help_string = f'''
5064
/{BotCommands.HelpCommand}: To get this message
5165
5266
/{BotCommands.MirrorCommand} [download_url][magnet_link]: Start mirroring the link to google drive
5367
5468
/{BotCommands.TarMirrorCommand} [download_url][magnet_link]: start mirroring and upload the archived (.tar) version of the download
5569
70+
/{BotCommands.WatchCommand} [youtube-dl supported link]: Mirror through youtube-dl
71+
72+
/{BotCommands.TarWatchCommand} [youtube-dl supported link]: Mirror through youtube-dl and tar before uploading
73+
5674
/{BotCommands.CancelMirror} : Reply to the message by which the download was initiated and that download will be cancelled
5775
5876
/{BotCommands.StatusCommand}: Shows a status of all the downloads
@@ -66,22 +84,32 @@ def bot_help(bot,update):
6684
/{BotCommands.LogCommand}: Get a log file of the bot. Handy for getting crash reports
6785
6886
'''
69-
sendMessage(help_string, bot, update)
87+
sendMessage(help_string, context.bot, update)
7088

7189

7290
def main():
7391
fs_utils.start_cleanup()
92+
# Check if the bot is restarting
93+
if path.exists('restart.pickle'):
94+
with open('restart.pickle', 'rb') as status:
95+
restart_message = pickle.load(status)
96+
restart_message.edit_text("Restarted Successfully!")
97+
remove('restart.pickle')
98+
7499
start_handler = CommandHandler(BotCommands.StartCommand, start,
75100
filters=CustomFilters.authorized_chat | CustomFilters.authorized_user)
76101
ping_handler = CommandHandler(BotCommands.PingCommand, ping,
77102
filters=CustomFilters.authorized_chat | CustomFilters.authorized_user)
103+
restart_handler = CommandHandler(BotCommands.RestartCommand, restart,
104+
filters=CustomFilters.owner_filter)
78105
help_handler = CommandHandler(BotCommands.HelpCommand,
79106
bot_help, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user)
80107
stats_handler = CommandHandler(BotCommands.StatsCommand,
81-
stats, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user)
108+
stats, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user)
82109
log_handler = CommandHandler(BotCommands.LogCommand, log, filters=CustomFilters.owner_filter)
83110
dispatcher.add_handler(start_handler)
84111
dispatcher.add_handler(ping_handler)
112+
dispatcher.add_handler(restart_handler)
85113
dispatcher.add_handler(help_handler)
86114
dispatcher.add_handler(stats_handler)
87115
dispatcher.add_handler(log_handler)

0 commit comments

Comments
 (0)