| # -*- coding: utf-8 -*- | |
| from string import ascii_letters, digits | |
| from warnings import filterwarnings | |
| from random import randint, choice | |
| from pprint import pprint | |
| from time import sleep | |
| import webbrowser | |
| import argparse | |
| import requests | |
| import json | |
| import sys | |
| import os | |
| import re | |
| py3 = sys.version_info[0] > 2 | |
| filterwarnings('ignore') | |
| def chunks(l, n): | |
| """Yield successive n-sized chunks from l.""" | |
| for i in range(0, len(l), n): | |
| yield l[i:i+n] | |
| # https://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python/312464#312464 | |
| def natural_sort(l): | |
| convert = lambda text: int(text) if text.isdigit() else text.lower() | |
| alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] | |
| return sorted(l, key=alphanum_key) | |
| # https://stackoverflow.com/questions/4836710/does-python-have-a-built-in-function-for-string-natural-sort/4836734#4836734 | |
| def has_numbers(input_string): | |
| return any(char.isdigit() for char in input_string) | |
| # https://stackoverflow.com/questions/19859282/check-if-a-string-contains-a-number/19859308#19859308 | |
| # god forgive me for my sins | |
| # do not read below this line unless you like to look at shit | |
| def fetch_captcha(): | |
| r = requests.get('https://8ch.net/8chan-captcha/entrypoint.php?mode=get&extra=abcdefghijklmnopqrstuvwxyz&nojs=true') | |
| try: | |
| match = 'CAPTCHA ID: [a-z]*' | |
| captcha_cookie = re.search(match, r.text).group().split(': ')[1] | |
| match = '<image src="data:image/png;base64,[A-Za-z0-9+/=]*">' | |
| with open('captcha_image.png', 'wb') as f: | |
| f.write(re.search(match, r.text).group().split(',')[1].strip('>').strip('"').decode('base64')) | |
| webbrowser.open('captcha_image.png') | |
| if py3: | |
| captcha_text = input('Enter the captcha: ') | |
| else: | |
| captcha_text = raw_input('Enter the captcha: ') | |
| os.remove('captcha_image.png') | |
| data['captcha_cookie'] = captcha_cookie | |
| data['captcha_text'] = captcha_text | |
| except AttributeError: | |
| print('Server error while fetching captcha') | |
| pass | |
| parser = argparse.ArgumentParser(description='Dump a folder to a thread') | |
| parser.add_argument('-f', '--folder', help='Folder to upload images from', type=str, required=True) | |
| parser.add_argument('-u', '--url', help='URL of thread', required=True) | |
| parser.add_argument('-d', '--delay', help='Delay between posting', type=int, required=True) | |
| parser.add_argument('-i', '--images', help='Number of images to post', type=int) | |
| parser.add_argument('-n', '--name', help='Name to post with', type=str, default='') | |
| parser.add_argument('-e', '--email', help='Email to post with', type=str, default='') | |
| parser.add_argument('-p', '--progress', help='Post with dump progress? (default y)', choices=('y', 'n'), default='y') | |
| parser.add_argument('-r', '--random', help='Post with random filenames? (default n)', choices=('y', 'n'), default='n') | |
| parser.add_argument('-l', '--length', help='To be used with -r/--random. Maximum length of random filename (optional, default 15)', type=int, default=15) | |
| args = vars(parser.parse_args()) | |
| # https://stackoverflow.com/questions/9234258/in-python-argparse-is-it-possible-to-have-paired-no-something-something-arg | |
| def make_random_filename(): | |
| splitfilename = l[file].split('.') | |
| extension = splitfilename[len(splitfilename) - 1] | |
| return ''.join(choice(ascii_letters + digits) for _ in range(randint(1, args['length']))) + '.' + extension | |
| delay = args['delay'] | |
| DIRECTORY = args['folder'] | |
| filelist = [] | |
| for file in os.listdir(DIRECTORY): | |
| if os.path.splitext(file)[1].lower() in ('.jpg', '.jpeg', '.png', '.gif', '.webm', '.mp4'): | |
| filelist.append(os.path.join(DIRECTORY, file)) | |
| # I stole this from stackoverflow somewhere, I can't find the link | |
| if len(args['url'].split('/')) == 6: | |
| spliturl = args['url'].split('/') | |
| if has_numbers(spliturl[5]) and '.html' in args['url']: | |
| url = '{}//{}'.format(spliturl[0], spliturl[2]) | |
| board = spliturl[3] | |
| thread = spliturl[5].split('.')[0] | |
| else: | |
| sys.exit('\nBad URL. --u/--url should be the URL of a thread.\nExample: https://8ch.net/b/res/1.html') | |
| else: | |
| sys.exit('\nBad URL. --u/--url should be the URL of a thread.\nExample: https://8ch.net/b/res/1.html') | |
| boardindex = requests.get('{}/{}/index.html'.format(url, board)) | |
| if args['images'] == None: | |
| print('-i/--images not specified. Defaulting to the maximum number of allowed images on this board.') | |
| if boardindex.status_code == 200: | |
| # this is a terrible idea and searching HTML for strings/using regular expressions should never be done | |
| # BUT, since 8chan goes overkill on escaping, this is guaranteed to be the only match | |
| match = '<script type="text/javascript">var configRoot="/";[a-zA-Z0-9;=+()/"?:._ ]*' | |
| maxfiles = int(re.search(match, boardindex.text).group().split(';')[3].split('=')[1]) | |
| if '<script>load_captcha("https://8ch.net/8chan-captcha/entrypoint.php", "abcdefghijklmnopqrstuvwxyz")' in boardindex.text: | |
| captcha = True | |
| else: | |
| captcha = False | |
| else: | |
| sys.exit('Error fetching board index/Unable to retrieve board settings') | |
| else: | |
| if args['images'] >= 1 and args['images'] <= 5: | |
| maxfiles = args['images'] | |
| else: | |
| sys.exit('-i/--images must be between 1 and 5') | |
| filelist = natural_sort(filelist) | |
| total = len(filelist) | |
| chunked_list = list(chunks(filelist, maxfiles)) | |
| data = { | |
| 'board': board, | |
| 'thread': thread, | |
| 'name': args['name'], | |
| 'email': args['email'], | |
| 'subject': '', | |
| 'body': '', | |
| 'embed': '', | |
| 'dx': '', | |
| 'dy': '', | |
| 'dz': '', | |
| 'password': 'ayylmao', | |
| 'json_response': '1', | |
| 'post': 'New Reply', | |
| } | |
| headers = { | |
| 'referer': '', | |
| 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0', | |
| 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', | |
| 'accept-language': 'en-US,en;q=0.5', | |
| 'cache-control': 'max-age=0', | |
| } | |
| headers['referer'] = '{}/{}/'.format(url, board) | |
| def fetch_dnsbl_captcha(): | |
| r = requests.get('https://8ch.net/dnsbls_bypass.php').text | |
| try: | |
| match = "value='[a-z]*" | |
| captcha_cookie = re.search(match, r).group().split("'")[1] | |
| match = '<image src="data:image/png;base64,[A-Za-z0-9+/=]*">' | |
| with open('dnsbl_captcha_image.png', 'wb') as f: | |
| f.write(re.search(match, r).group().split('"')[1].split(',')[1].decode('base64')) | |
| webbrowser.open('dnsbl_captcha_image.png') | |
| if py3: | |
| captcha_text = input('Enter the captcha: ') | |
| else: | |
| captcha_text = raw_input('Enter the captcha: ') | |
| os.remove('dnsbl_captcha_image.png') | |
| dnsbl_data = {} | |
| dnsbl_data['captcha_cookie'] = captcha_cookie | |
| dnsbl_data['captcha_text'] = captcha_text | |
| r = requests.post('{}/dnsbls_bypass.php'.format(url), data=dnsbl_data, headers=headers) | |
| except AttributeError: | |
| print('Server error while fetching captcha') | |
| pass | |
| progress = maxfiles | |
| for chunk in range(len(chunked_list)): | |
| number_of_files = len(chunked_list[chunk]) | |
| status = '' | |
| while 'redirect' not in status: | |
| for x in range(len(chunked_list[chunk])): | |
| l = natural_sort(chunked_list[chunk]) | |
| files = [] | |
| if args['random'] == 'y': | |
| for file in range(len(l)): | |
| if file == 0: | |
| files.append(['file', (make_random_filename(), open(l[file], 'rb'))]) | |
| else: | |
| files.append(['file' + str(file), (make_random_filename(), open(l[file], 'rb'))]) | |
| else: | |
| for file in range(len(l)): | |
| if file == 0: | |
| files.append(['file', open(l[file], 'rb')]) | |
| else: | |
| files.append(['file' + str(file), open(l[file], 'rb')]) | |
| files = tuple(files) | |
| if progress > total: | |
| progress = total | |
| if args['progress'] == 'y': | |
| data['body'] = '{}/{}'.format(progress, total) | |
| else: | |
| data['body'] ='' | |
| if captcha is True: | |
| fetch_captcha() | |
| r = requests.post('{}/post.php'.format(url), data=data, headers=headers, files=files) | |
| status_code = r.status_code | |
| status = r.text | |
| if status_code == 200: | |
| print(status) | |
| else: | |
| print('{} server error'.format(status_code)) | |
| if 'dnsbls_bypass.php' in status: | |
| fetch_dnsbl_captcha() | |
| if 'Failed to resize image' and 'corrupt image' in status or 'XSS' in status: | |
| print('ONE OF THE FOLLOWING IMAGES ARE CORRUPT, AND AS A RESULT, THE FOLLOWING FILES WERE NOT UPLOADED:') | |
| pprint(l) | |
| status = 'redirect' | |
| if 'already exists' in status: | |
| status = 'redirect' | |
| if 'Maximum file size' in status: | |
| print('THE FOLLOWING FILE(S) WERE OVER THE FILE SIZE LIMIT AND WERE NOT POSTED:') | |
| pprint(l) | |
| print('Consider setting -i/--images to a lower number so your files fit under the limit') | |
| status = 'redirect' | |
| del files | |
| print('{}/{} - Waiting {} seconds...').format(progress, total, delay) | |
| sleep(delay) | |
| progress += number_of_files |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment