Browse Source

Add ratiomaster lib.

master
poka 4 months ago
parent
commit
11bac3f517
9 changed files with 357 additions and 3 deletions
  1. +3
    -3
      crawl.py
  2. +22
    -0
      lib/ratiomaster/README.md
  3. +0
    -0
      lib/ratiomaster/code/__init__.py
  4. +90
    -0
      lib/ratiomaster/code/decoding_bencoded.py
  5. +28
    -0
      lib/ratiomaster/code/pretty.py
  6. +118
    -0
      lib/ratiomaster/code/process_torrent.py
  7. +60
    -0
      lib/ratiomaster/code/torrentclientfactory.py
  8. +34
    -0
      lib/ratiomaster/ratio.py
  9. +2
    -0
      lib/ratiomaster/requirements.txt

+ 3
- 3
crawl.py View File

@ -55,6 +55,7 @@ if ("https://" not in name):
print(colored('No torrent named "' + name + '" on YggTorrent', 'blue'))
sys.exit(1)
else:
name = re.sub(r'\w*--seed\w*', '', name)
research = unquote(name, errors='strict')
# Allow only one torrent downling in same time, and remove oldest torrent if disk size is full.
@ -124,8 +125,7 @@ def downloadTorrent():
shutil.rmtree('data/tmp/torrents', ignore_errors=True)
os.mkdir("data/tmp/torrents")
scraper.download_from_torrent_url(research)
# os.popen(f'cd data/tmp/torrents/ && mv *.torrent {idTorrent}.torrent && mv *.torrent ../../torrents/')
os.popen(f'cd data/tmp/torrents/ && mv *.torrent {idTorrent.strip()}.torrent && mv {idTorrent.strip()}.torrent ../../torrents/')
os.popen(f'cd data/tmp/torrents/ && mv *.torrent ../../torrents/{idTorrent.strip()}.torrent')
# Remove tracker
def removeTracker():
@ -143,7 +143,7 @@ if(scraper.login(login.user, login.passwd)): #Check if user can login
print(colored("Login success", 'green'))
rollingFiles()
downloadTorrent()
removeTracker() if options.rmTracker else None
removeTracker() if options.rmTracker else time.sleep(2); os.replace(f'data/torrents/{idTorrent.strip()}.torrent.added', f'data/meta/{idTorrent.strip()}/{idTorrent.strip()}.torrent')
else:
print(colored("Login failed", 'red'))
sys.exit(1)

+ 22
- 0
lib/ratiomaster/README.md View File

@ -0,0 +1,22 @@
# Ratio.py
Ratio.py is a small command line RatioMaster.Net like in Python3. It fakes upload stats of a torrent.
Current emulators available are:
* Transmission 2.92
## Requirements:
1. Python 3.x
2. pip install -r requirements.txt
## Usage:
```console
foo@bar:~/ratio.py$ python ratio.py -c configuration.json
```
## Configuration example
```js
{
"torrent": "<Torrent file path>",
"upload": "<Upload speed (kB/s)>"
}
```

+ 0
- 0
lib/ratiomaster/code/__init__.py View File


+ 90
- 0
lib/ratiomaster/code/decoding_bencoded.py View File

@ -0,0 +1,90 @@
import re
import logging
import binascii
class bencoding():
def __init__(self):
self.decimal_match = re.compile('\d')
self.data = b''
self.dict = {}
def get_dict(self, key):
if key not in self.dict:
return ''
start = self.dict[key][0]
end = self.dict[key][1]
return self.data[start:end]
def get_item(self, chunks):
item = chunks[self.i]
self.i += 1
if not type(item) == str:
item = bytes([item])
try:
item = item.decode('utf-8')
except:
item = '\\x{}'.format(binascii.hexlify(item))
return item
def decoding_byte_string(self, chunks, item):
# logging.debug('decoding string')
num = ''
while self.decimal_match.search(item):
num += item
item = self.get_item(chunks)
line = ''
for i in range(int(num)):
line += self.get_item(chunks)
return line
def decoding_integer(self, chunks):
# logging.debug('decoding integer')
item = self.get_item(chunks)
num = ''
while item != 'e':
num += item
item = self.get_item(chunks)
return int(num)
def decoding_list(self, chunks):
# logging.debug('decoding list')
item = self.get_item(chunks)
list = []
while item != 'e':
self.i -= 1
list.append(self._dechunk(chunks))
item = self.get_item(chunks)
return list
def decoding_dictionnary(self, chunks):
# logging.debug('decoding dictionnary')
item = self.get_item(chunks)
hash = {}
while item != 'e':
self.i -= 1
key = self._dechunk(chunks)
start = self.i
hash[key] = self._dechunk(chunks)
end = self.i
self.dict[key] = (start, end)
item = self.get_item(chunks)
return hash
def _dechunk(self, chunks):
item = self.get_item(chunks)
if item == 'd':
return self.decoding_dictionnary(chunks)
elif item == 'l':
return self.decoding_list(chunks)
elif item == 'i':
return self.decoding_integer(chunks)
elif self.decimal_match.search(item):
return self.decoding_byte_string(chunks, item)
raise "Invalid input!"
def bdecode(self, data):
self.data = data
chunks = list(self.data)
self.i = 0
root = self._dechunk(chunks)
return root

+ 28
- 0
lib/ratiomaster/code/pretty.py View File

@ -0,0 +1,28 @@
import requests
from pprint import pformat
def get_headers(headers):
res = ''
for k, v in headers.items():
res += '{}: {}\n'.format(k, v)
return res
def pretty_GET(url, headers, params):
req = requests.Request('GET', url, headers=headers, params=params)
s = requests.Session()
prepared = s.prepare_request(req)
p = '-----START-----\n'
p +=('{} {}\n{}'.format(prepared.method, prepared.url,
get_headers(prepared.headers),
)
)
if prepared.body:
pi += prepared.body
p += '------END------'
return p
def pretty_data(data):
return pformat(data)

+ 118
- 0
lib/ratiomaster/code/process_torrent.py View File

@ -0,0 +1,118 @@
from code.decoding_bencoded import bencoding
from code.torrentclientfactory import Transmission292
from code.pretty import pretty_data, pretty_GET
from hashlib import sha1
from urllib.parse import quote_plus
import requests
import logging
import random
from tqdm import tqdm
from time import sleep
from struct import unpack
logging.basicConfig(level=logging.DEBUG)
class process_torrent():
def __init__(self, configuration):
self.configuration = configuration
self.open_torrent()
self.torrentclient = Transmission292(self.tracker_info_hash())
def open_torrent(self):
torrent_file = self.configuration['torrent']
with open(torrent_file, 'rb') as tf:
data = tf.read()
self.b_enc = bencoding()
self.metainfo = self.b_enc.bdecode(data)
self.info = self.metainfo['info']
if 'length' not in self.info:
self.info['length'] = 0
for file in self.info['files']:
self.info['length'] += file['length']
print(pretty_data(self.info['files']))
def tracker_info_hash(self):
raw_info = self.b_enc.get_dict('info')
hash_factory = sha1()
hash_factory.update(raw_info)
hashed = hash_factory.hexdigest()
sha = bytearray.fromhex(hashed)
return str(quote_plus(sha))
def send_request(self, params, headers):
url = self.metainfo['announce']
print(pretty_GET(url, headers, params))
while True:
try:
r = requests.get(url, params=params, headers=headers)
except requests.exceptions.ConnectionError as e:
sleep(1)
continue
break
return r.content
def tracker_start_request(self):
tc = self.torrentclient
headers = tc.get_headers()
params = tc.get_query(uploaded=0,
downloaded=0,
event='started')
print('----------- First Command to Tracker --------')
content = self.send_request(params, headers)
self.tracker_response_parser(content)
def tracker_response_parser(self, tr_response):
b_enc = bencoding()
response = b_enc.bdecode(tr_response)
print('----------- Received Tracker Response --------')
print(pretty_data(response))
raw_peers = b_enc.get_dict('peers')
i = 0
peers = []
while i<len(raw_peers)-6:
peer = raw_peers[i:i+6]
i+=6
unpacked_ip = unpack('BBBB', peer[0:4])
ip = ".".join(str(i) for i in unpacked_ip)
unpacked_port = unpack('!H', peer[4:6])
port = unpacked_port[0]
peers.append((ip, port))
self.interval = response['interval']
def wait(self):
pbar = tqdm(total=self.interval)
print('sleep: {}'.format(self.interval))
t = 0
while t < self.interval:
t += 1
pbar.update(1)
sleep(1)
pbar.close()
def tracker_process(self):
while True:
self.tracker_start_request()
print('----------- Sending Command to Tracker --------')
# get upload
min_up = self.interval-(self.interval*0.1)
max_up = self.interval
randomize_upload = random.randint(min_up, max_up)
uploaded = int(self.configuration['upload'])*1000*randomize_upload
# get download
downloaded = 0
tc = self.torrentclient
headers = tc.get_headers()
params = tc.get_query(uploaded=uploaded,
downloaded=downloaded,
event='stopped')
content = self.send_request(params, headers)
self.tracker_response_parser(content)
self.wait()

+ 60
- 0
lib/ratiomaster/code/torrentclientfactory.py View File

@ -0,0 +1,60 @@
import random
import string
class Transmission292():
def __init__(self, info_hash):
self.name = "Transmission 2.92 (14714)"
parameters = {}
# urlencoded 20-byte SHA1 hash of the value of the info key from the Metainfo file
parameters['info_hash'] = info_hash
# urlencoded 20-byte string used as a unique ID for the client
parameters["peer_id"] = self.generate_peer_id()
# The port number that the client is listening on
parameters["port"] = random.randint(1025, 65535)
# Number of peers that the client would like to receive from the tracker
parameters["numwant"] = 80
# An additional identification that is not shared with any other peers
parameters["key"] = self.generate_key()
# Setting this to 1 indicates that the client accepts a compact response
parameters["compact"] = 0
# Setting this to 1 indicates that the client accepts crypto
parameters["supportcrypto"] = 1
self.parameters = parameters
def get_headers(self):
headers = {}
headers['User-Agent'] = 'Transmission/2.92'
headers['Accept'] = '*/*'
headers['Accept-Encoding'] = 'Accept-Encoding: gzip;q=1.0, deflate, identity'
return headers
def get_query(self, uploaded, downloaded, left=0, event=None):
# The total amount uploaded (since the client sent the 'started' event)
self.parameters["uploaded"] = uploaded
# The total amount downloaded (since the client sent the 'started' event)
self.parameters["downloaded"] = downloaded
# The number of bytes this client still has to download
self.parameters["left"] = left
# If specified, must be one of started, completed, stopped
if event:
self.parameters["event"] = event
params = '&'.join('{}={}'.format(k, v)
for k, v in self.parameters.items())
return params
def id_generator(self, chars, size):
id = ''
for _ in range(size):
id += random.choice(chars)
return id
def generate_peer_id(self):
chars = string.ascii_lowercase + string.digits
rand_id = self.id_generator(chars, 12)
peer_id = "-TR2920-" + rand_id
return peer_id
def generate_key(self):
chars = 'ABCDEF' + string.digits
key = self.id_generator(chars, 8)
return key

+ 34
- 0
lib/ratiomaster/ratio.py View File

@ -0,0 +1,34 @@
from code.process_torrent import process_torrent
import argparse
import json
import sys
def parse_args():
"""Create the arguments"""
parser = argparse.ArgumentParser('\nratio.py -c <configuration-file.json>')
parser.add_argument("-c", "--configuration", help="Configuration file")
return parser.parse_args()
def load_configuration(configuration_file):
with open(configuration_file) as f:
configuration = json.load(f)
if 'torrent' not in configuration:
return None
return configuration
if __name__ == "__main__":
args = parse_args()
if args.configuration:
configuration = load_configuration(args.configuration)
else:
sys.exit()
if not configuration:
sys.exit()
to = process_torrent(configuration)
to.tracker_process()

+ 2
- 0
lib/ratiomaster/requirements.txt View File

@ -0,0 +1,2 @@
requests
tqdm

Loading…
Cancel
Save