Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jakbin committed Jul 27, 2023
0 parents commit 772f170
Show file tree
Hide file tree
Showing 10 changed files with 390 additions and 0 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Publish package

on:
push:
paths:
- g_bot/__init__.py

jobs:
deploy:
runs-on: ubuntu-latest
steps:

- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
pip install setuptools wheel twine flake8
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Build and publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python setup.py sdist bdist_wheel
rm dist/*.tar.gz
twine upload dist/*
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build/*
dist/*
*.egg-info/*
__pycache__*
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Jak Bin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# gdrive-bot

Upload files to your gdrive with your gdrive bot

![GitHub Contributors](https://img.shields.io/github/contributors/jakbin/gdrive-bot)
![GitHub commit activity](https://img.shields.io/github/commit-activity/m/jakbin/gdrive-bot)
![GitHub last commit](https://img.shields.io/github/last-commit/jakbin/gdrive-bot)
![Python 3.6](https://img.shields.io/badge/python-3.6-yellow.svg)


## Features
- Progress bar


## Installation

```sh
pip3 install -U gdrive-bot
```

## Usage
```sh
g-bot setup # setup your gdrive credentials
g-bot reset # reset to default your gdrive credentials
g-bot up {file_name} # upload gdrive channel or group
g-bot d {url} # download and upload file to your gdrive
```
1 change: 1 addition & 0 deletions g_bot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "1.0.1"
60 changes: 60 additions & 0 deletions g_bot/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import os
import argparse
import configparser

from g_bot import __version__
from g_bot.main import setup, reset, upload_file, download, config_file

package_name = "g-bot"

config = configparser.ConfigParser()
config.read(config_file)
access_token = config['GDRIVE']['access_token']
folder_id = config["GDRIVE"]['folder_id']
if folder_id == 'xxxxxxxxxxxxx':
folder_id = None

example_uses = '''example:
g-bot setup
g-bot reset
g-bot up {files_name}
g-bot d {url}'''

def main(argv = None):
parser = argparse.ArgumentParser(prog=package_name, description="upload your files to your group or channel", epilog=example_uses, formatter_class=argparse.RawDescriptionHelpFormatter)
subparsers = parser.add_subparsers(dest="command")

setup_parser = subparsers.add_parser("setup", help="setup your gdrivr credentials")

reset_parser = subparsers.add_parser("reset", help="reset to default your gdrivr credentials")

upload_parser = subparsers.add_parser("up", help="upload file to your group or channel")
upload_parser.add_argument("filename", type=str, help="one or more files to upload")

download_parser = subparsers.add_parser("d", help="download and upload file to your group or channel")
download_parser.add_argument("url", type=str, help="url")

parser.add_argument('-v',"--version",
action="store_true",
dest="version",
help="check version of g-bot")

args = parser.parse_args(argv)

if args.command == "setup":
return setup()
elif args.command == "reset":
return reset()
elif args.command == "up":
file_name = os.path.basename(args.filename)
return upload_file(access_token, args.filename, file_name, folder_id)
elif args.command == "d":
file_name = os.path.basename(args.filename)
return download(args.url, access_token, folder_id)
elif args.version:
return print(__version__)
else:
parser.print_help()

if __name__ == '__main__':
raise SystemExit(main())
3 changes: 3 additions & 0 deletions g_bot/config.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[GDRIVE]
access_token = 1234567890xxxxxxx
folder_id = xxxxxxxxxxxxx
197 changes: 197 additions & 0 deletions g_bot/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import os
import sys
import json
import urllib3
import requests
import collections
import configparser
from tqdm import tqdm
from pathlib import Path
from shutil import copy2
import requests_toolbelt
import urllib.parse as urlparse
from requests.exceptions import JSONDecodeError
from requests.exceptions import MissingSchema
from requests import get, ConnectionError, head

urllib3.disable_warnings()

base_dir = os.path.dirname(os.path.realpath(__file__))
config = os.path.join(base_dir, 'config.ini')

home_path = Path.home()
if os.path.isfile(os.path.join(home_path, ".config/g-bot/config.ini")):
config_file = os.path.join(home_path, ".config/g-bot/config.ini")
else:
if not os.path.isdir(os.path.join(home_path, '.config/g-bot')):
os.mkdir(os.path.join(home_path, '.config/g-bot'))
copy2(config,os.path.join(home_path, ".config/g-bot"))
config_file = os.path.join(home_path, ".config/g-bot/config.ini")

config = configparser.ConfigParser()
config.read(config_file)

class ProgressBar(tqdm):
def update_to(self, n: int) -> None:
self.update(n - self.n)

def setup():
print('If you did not want to change anyone, just press enter.')

access_token = input("Enter your GDRIVE api access token : ")
if access_token != '':
config.set('GDRIVE', 'access_token', access_token)

folder_id = input("Enter your channel name or chat id with '-' : ")
if folder_id != '':
config.set('GDRIVE', 'folder_id', folder_id)

with open(config_file, 'w') as configfile:
config.write(configfile)

print("Setup complete!")

def reset():
config.set('GDRIVE', 'folder_id', 'xxxxxxxxxxxxx')
config.set('GDRIVE', 'access_token', '1234567890xxxxxxx')

with open(config_file, 'w') as configfile:
config.write(configfile)

print("Config file has been reset to default!")

def upload_file(access_token:str, filename:str, filedirectory:str, folder_id: str = None):

if folder_id == None:
metadata = {
"name": filename,
"parents": [folder_id]
}
else:
metadata = {
"name": filename,
"parents": [folder_id]
}

session = requests.session()

with open(filedirectory, "rb") as fp:
files = collections.OrderedDict(data=("metadata", json.dumps(metadata), "application/json"), file=fp)
encoder = requests_toolbelt.MultipartEncoder(files)
with ProgressBar(
total=encoder.len,
unit="B",
unit_scale=True,
unit_divisor=1024,
miniters=1,
file=sys.stdout,
) as bar:
monitor = requests_toolbelt.MultipartEncoderMonitor(
encoder, lambda monitor: bar.update_to(monitor.bytes_read)
)

r = session.post(
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&supportsAllDrives=true",
data=monitor,
allow_redirects=False,
json=metadata,
headers={"Authorization": "Bearer " + access_token, "Content-Type": monitor.content_type},
)

try:
resp = r.json()
print(resp)
except JSONDecodeError:
sys.exit(r.text)


def downloader(url:str, file_name:str):
try:
filesize = int(head(url).headers["Content-Length"])
except ConnectionError:
sys.exit("[Error]: No internet")
except MissingSchema as e:
sys.exit(str(e))
except KeyError:
filesize = None

chunk_size = 1024

try:
with get(url, stream=True) as r, open(file_name, "wb") as f, tqdm(
unit="B", # unit string to be displayed.
unit_scale=True, # let tqdm to determine the scale in kilo, mega..etc.
unit_divisor=1024, # is used when unit_scale is true
total=filesize, # the total iteration.
file=sys.stdout, # default goes to stderr, this is the display on console.
desc=file_name # prefix to be displayed on progress bar.
) as progress:
for chunk in r.iter_content(chunk_size=chunk_size):
datasize = f.write(chunk)
progress.update(datasize)
except ConnectionError as e:
print(e)

def filename_from_url(url):
fname = os.path.basename(urlparse.urlparse(url).path)
if len(fname.strip(" \n\t.")) == 0:
return None
return fname

def filename_from_headers(headers):
if type(headers) == str:
headers = headers.splitlines()
if type(headers) == list:
headers = dict([x.split(':', 1) for x in headers])
cdisp = headers.get("Content-Disposition")
if not cdisp:
return None
cdtype = cdisp.split(';')
if len(cdtype) == 1:
return None
if cdtype[0].strip().lower() not in ('inline', 'attachment'):
return None
# several filename params is illegal, but just in case
fnames = [x for x in cdtype[1:] if x.strip().startswith('filename=')]
if len(fnames) > 1:
return None
name = fnames[0].split('=')[1].strip(' \t"')
name = os.path.basename(name)
if not name:
return None
return name

def detect_filename(url=None, headers=None):
names = dict(out='', url='', headers='')
if url:
names["url"] = filename_from_url(url) or ''
if headers:
names["headers"] = filename_from_headers(headers) or ''
return names["out"] or names["headers"] or names["url"]

def download(url:str, access_token:str, folder_id:str = None):
download_path = 'downloads'
if not os.path.isdir(download_path):
os.mkdir(download_path)

filename = detect_filename(url, head(url).headers)

yes = {'yes','y','ye',''}
choice = input(f"Do you want change filename {filename[0:60]} [Y/n]: ").lower()
if choice in yes:
filename = input("Enter new file name with extension: ")

file_path = os.path.join(download_path, filename)

print("Downloading file......")
try:
downloader(url, file_path)
except OSError:
print("File name is too log !")
filename = input("Enter new filename : ")
file_path = os.path.join(download_path, filename)
downloader(url, file_path)

print("\nUploading file......")
filedirectory = f"downloads/{filename}"
upload_file(access_token, filename, filedirectory, folder_id)
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
tqdm
requests
requests-toolbelt
Loading

0 comments on commit 772f170

Please sign in to comment.