123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- import os
- import re
- import magic
- import httpx
- import random, ssl
- import io
- import subprocess
- from urllib.parse import urlparse
- from PIL import Image, ExifTags,ImageDraw,ImageFont
- import pillow_avif
- from requests_toolbelt.multipart.encoder import MultipartEncoder
- import pyautogui # Replace pyperclip with pyautogui for sending keys
- import tornado.ioloop
- import tornado.web
- import tornado.gen
- import asyncio
- import logging
- import namegen as n
- # Configure logging
- logging.basicConfig(level=logging.DEBUG)
- # Define the upload folder path
- UPLOAD_FOLDER = './pics'
- ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
-
- # Define an in-memory data store for storing the current room
- data_store = {'current_room': 'chad'}
- data_store_lock = asyncio.Lock()
- # Function to check if a file is allowed to be uploaded
- def allowed_file(filename):
- return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
- # Function to compress an image
- def compress_image(image):
- with io.BytesIO() as output:
- image = image.convert('RGB')
- image.save(output, format="JPEG", quality=40, optimize=True)
- contents = output.getvalue()
- return contents
- # Function to compress an image without resizing
- def compress_image_raw(image):
- with io.BytesIO() as output:
- image = image.convert('RGB')
- image.save(output, format="JPEG", quality=100, subsampling=0)
- contents = output.getvalue()
- return contents
- with open('sessionid.txt', 'r') as f:
- sessionid = f.read()
- async def upload_dickpic(slugstr, buffer_pic_data):
- async with httpx.AsyncClient(cookies={'agreeterms': '1', 'sessionid': sessionid}) as client:
- response = await client.get("https://chaturbate.com/emoticons/")
- pattern = r'name="csrfmiddlewaretoken" value="([a-zA-Z0-9]+)"'
- match = re.search(pattern, response.text)
- if not match:
- logging.error("CSRF token not found")
- return False
- csrfmiddlewaretoken = match.group(1)
- mime = magic.Magic(mime=True)
- mime_type = mime.from_buffer(buffer_pic_data)
- m = MultipartEncoder(
- fields={
- 'csrfmiddlewaretoken': csrfmiddlewaretoken,
- 'slug': slugstr,
- 'image': ('image.jpg', buffer_pic_data, mime_type),
- 'category': str(1),
- 'suggested_category': str()
- }
- )
- headers = {
- 'origin': 'https://chaturbate.com',
- 'content-type': m.content_type,
- 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
- }
- # Log headers and cookies for debugging
- logging.debug(f"Headers: {headers}")
- logging.debug(f"Cookies: {client.cookies}")
- r = await client.post('https://chaturbate.com/emoticons/', data=m.to_string(), headers=headers)
- if r.status_code == 302 and 'upload_complete' in r.headers.get('location', ''):
- print(slugstr)
- return True
- elif r.status_code == 403:
- logging.error("403 Forbidden: Check CSRF token, cookies, and headers.")
- logging.error(f"Response: {r.text}")
- return False
- else:
- logging.error(f"Unexpected status code: {r.status_code}")
- logging.error(f"Response: {r.text}")
- return False
- # Function to play a beep sound
- def play_beep(frequency=440, duration=1, volume=10):
- command = f'ffplay -nodisp -autoexit -f lavfi -i "sine=frequency={frequency}:duration={duration}" -volume {volume}'
- subprocess.Popen(command, shell=True)
- # Function to send a file extension
- def send_ext(filename):
- return filename.rsplit('.', 1)[1].lower()
- def addSexualComment(image): # Get image dimensions
- # Get image dimensions
- image_width, image_height = image.size
-
- # Initialize ImageDraw
- draw = ImageDraw.Draw(image)
-
- # Define your text components
- room_name = data_store.get('current_room')
- text_before_room = "Hopefully you like this @"
- text_after_room = room_name
-
- # Load a font
- font_size = int(image_height * 0.05) # Start with 5% of the image height
- font = ImageFont.truetype("ariel.otf", font_size) # Ensure the font path is correct
-
- # Calculate the size of each text component
- text_before_bbox = draw.textbbox((0, 0), text_before_room, font=font)
- text_before_width = text_before_bbox[2] - text_before_bbox[0]
-
- text_after_bbox = draw.textbbox((0, 0), text_after_room, font=font)
- text_after_width = text_after_bbox[2] - text_after_bbox[0]
-
- total_text_width = text_before_width + text_after_width
- total_text_height = max(text_before_bbox[3] - text_before_bbox[1], text_after_bbox[3] - text_after_bbox[1])
-
- # If the total text is too wide, reduce the font size until it fits
- while total_text_width > image_width - 20: # Leave some padding (20 pixels total)
- font_size -= 1
- font = ImageFont.truetype("ariel.otf", font_size) # Update font with new size
-
- text_before_bbox = draw.textbbox((0, 0), text_before_room, font=font)
- text_before_width = text_before_bbox[2] - text_before_bbox[0]
-
- text_after_bbox = draw.textbbox((0, 0), text_after_room, font=font)
- text_after_width = text_after_bbox[2] - text_after_bbox[0]
-
- total_text_width = text_before_width + text_after_width
- total_text_height = max(text_before_bbox[3] - text_before_bbox[1], text_after_bbox[3] - text_after_bbox[1])
-
- # Calculate position for the combined text
- x_position = (image_width - total_text_width) / 2
- y_position = image_height - total_text_height - 10
-
- # Draw the first part of the text in white
- draw.text((x_position, y_position), text_before_room, font=font, fill="white")
-
- # Draw the room name in red
- x_position += text_before_width
- draw.text((x_position, y_position), text_after_room, font=font, fill="red")
-
- return image
- # Function to handle EXIF orientation
- def handle_exif_orientation(image):
- exif = image._getexif()
- if exif is not None:
- orientation_tag = None
- for orientation in ExifTags.TAGS.keys():
- if ExifTags.TAGS[orientation] == 'Orientation':
- orientation_tag = orientation
- break
- if orientation_tag is not None and orientation_tag in exif:
- orientation = exif[orientation_tag]
- if orientation == 3:
- return image.rotate(180, expand=True)
- elif orientation == 6:
- return image.rotate(270, expand=True)
- elif orientation == 8:
- return image.rotate(90, expand=True)
- return image
- async def run_command(command):
- # subprocess.run(command, shell=True)
- await asyncio.to_thread(subprocess.run, command, shell=True)
- async def save_image_async(image, path, quality=60):
- await asyncio.to_thread(image.save, path, quality=quality)
- async def upload_image(image, slugstr):
- async with httpx.AsyncClient() as client:
- with io.BytesIO() as output:
- image.save(output, format="JPEG")
- contents = output.getvalue()
- files = {'file': ('image.jpg', contents, 'image/jpeg')}
- data = {'msg': f":{slugstr} @{data_store.get('current_room')}"}
- response = await client.post("http://opc:2525/upload_image", files=files, data=data)
- if response.status_code == 200:
- print("Image uploaded successfully!")
- class BeepHandler(tornado.web.RequestHandler):
- async def get(self):
- play_beep(duration=0.05, volume=60)
- self.write("")
- class WebRTCHandler(tornado.web.RequestHandler):
- async def get(self):
- self.set_header('Access-Control-Allow-Origin', '*')
- self.render('camtest.html', name="")
- class UploadFileHandler(tornado.web.RequestHandler):
- def options(self):
- self.set_status(204)
- self.set_header('Access-Control-Allow-Origin', '*')
- self.set_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
- self.set_header('Access-Control-Allow-Headers', 'Content-Type')
- self.finish()
- async def get(self):
- self.set_header('Access-Control-Allow-Origin', '*')
- self.render('index.html', name="")
- async def post(self):
- self.set_header('Access-Control-Allow-Origin', '*')
- async with data_store_lock:
- current_room = data_store.get('current_room')
- files = self.request.files.get('file')
- if not files:
- self.write("No file part")
- return
- file = files[0]
- if file.filename == '':
- self.write("No selected file")
- return
- if file and allowed_file(file.filename):
- slugstr = "penis" + "".join([n.generate_nickname() for _ in range(2)])
- filename = slugstr + "." + send_ext(file.filename)
- img = Image.open(io.BytesIO(file.body))
- img = handle_exif_orientation(img)
- # img = addSexualComment(img)
- tiny_img = compress_image(img)
- raw_img_flipped = Image.open(io.BytesIO(compress_image_raw(img)))
-
- results = await asyncio.gather(*[upload_dickpic(slugstr=slugstr, buffer_pic_data=tiny_img)])
- if any(results):
- filepath_to_save_avif = os.path.join(UPLOAD_FOLDER, slugstr + ".avif")
- # unused pyautogui.write(":" + slugstr + " ") # Use pyautogui to simulate keypress instead of pyperclip
- await run_command("echo 'type :"+slugstr+" ' | dotool")
- play_beep(duration=0.05, volume=60)
- # unused await run_command("echo key ctrl+v | dotool")
- await save_image_async(raw_img_flipped, filepath_to_save_avif)
- await upload_image(raw_img_flipped, slugstr)
- self.redirect("/" + slugstr, status=302)
- return
- self.render('index.html', name="")
- class ShowSlugHandler(tornado.web.RequestHandler):
- async def get(self, name):
- self.render('index.html', name=name)
- class SetRoomHandler(tornado.web.RequestHandler):
- async def post(self):
- data = tornado.escape.json_decode(self.request.body)
- parsed_url = urlparse(data['url'])
- if parsed_url.netloc != "chaturbate.com":
- self.write("not chaturbate")
- return
- path = parsed_url.path
- path_parts = path.strip('/').split('/')
- async with data_store_lock:
- data_store['current_room'] = path_parts[0]
- print(path_parts[0])
- self.write("")
- class DownloadFileHandler(tornado.web.RequestHandler):
- async def get(self, name):
- file_path = os.path.join(UPLOAD_FOLDER, name)
- if os.path.exists(file_path):
- self.set_header('Content-Type', 'application/octet-stream')
- self.set_header('Content-Disposition', f'attachment; filename={name}')
- with open(file_path, 'rb') as f:
- while chunk := f.read(1024 * 1024):
- self.write(chunk)
- self.finish()
- else:
- self.set_status(404)
- self.write("File not found")
- class SendKeysHandler(tornado.web.RequestHandler):
- async def get(self):
- command = "{ echo mouseto 0.5 0.5; echo click; echo click left; echo key enter; sleep 1; echo key left; } | dotool"
- await run_command(command)
- self.write("")
- class SendCockEnterKeysHandler(tornado.web.RequestHandler):
- async def get(self):
- command = "echo key enter | dotool"
- await run_command(command)
- referer = self.request.headers.get('Referer', '/')
- self.redirect(referer)
- class FaviconHandler(tornado.web.RequestHandler):
- async def get(self):
- self.set_status(204)
- self.finish()
- def make_app():
- return tornado.web.Application([
- (r"/a", BeepHandler),
- (r"/b", WebRTCHandler),
- (r"/sendkeys", SendKeysHandler),
- (r"/sendcock_enter", SendCockEnterKeysHandler),
- (r"/telemetry", SetRoomHandler),
- (r"/", UploadFileHandler),
- (r"/([a-zA-Z0-9]+)", ShowSlugHandler),
- (r"/uploads/(.*)", DownloadFileHandler),
- (r"/favicon.ico", FaviconHandler),
- ], template_path="templates", static_path="static")
- if __name__ == "__main__":
- app = make_app()
- app.listen(5000)
- tornado.ioloop.IOLoop.current().start()
|