175 lines
6.3 KiB
Python
175 lines
6.3 KiB
Python
import asyncio
|
|
import aiohttp
|
|
import os
|
|
import sys
|
|
import json
|
|
from typing import Optional, Dict, Any
|
|
from dotenv import load_dotenv
|
|
import urllib.parse
|
|
import re
|
|
|
|
load_dotenv()
|
|
|
|
SKETCHERS_UNITED_SESSION = os.environ.get('SKETCHERS_UNITED_SESSION')
|
|
REMEMBER_WEB = os.environ.get('REMEMBER_WEB')
|
|
TARGET_URL = 'https://sketchersunited.org/chats'
|
|
API_PASSWORD = os.environ.get('API_PASSWORD')
|
|
XSRF_TOKEN = os.environ.get('XSRF_TOKEN')
|
|
|
|
def get_session_cookie_header() -> Optional[Dict[str, str]]:
|
|
if not SKETCHERS_UNITED_SESSION or not REMEMBER_WEB:
|
|
print("Missing required session or remember_web environment variables.", file=sys.stderr)
|
|
return None
|
|
|
|
cookies_to_send = []
|
|
cookies_to_send.append(f"sketchers_united_session={SKETCHERS_UNITED_SESSION}")
|
|
cookies_to_send.append(f"remember_web_59ba36addc2b2f9401580f014c7f58ea4e30989d={REMEMBER_WEB}")
|
|
|
|
if XSRF_TOKEN:
|
|
cookies_to_send.append(f"XSRF-TOKEN={XSRF_TOKEN}")
|
|
|
|
cookie_header = "; ".join(cookies_to_send)
|
|
|
|
return {
|
|
'Cookie': cookie_header,
|
|
'Accept': 'application/json',
|
|
'User-Agent': 'usernames chat checking bot',
|
|
'Content-Type': 'application/json',
|
|
}
|
|
|
|
def get_post_headers(xsrf_token: str) -> Dict[str, str]:
|
|
return {
|
|
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0',
|
|
'Accept': 'application/json, text/plain, */*',
|
|
'Accept-Language': 'en-US,en;q=0.5',
|
|
'X-XSRF-TOKEN': xsrf_token,
|
|
'Sec-GPC': '1',
|
|
'Sec-Fetch-Dest': 'empty',
|
|
'Sec-Fetch-Mode': 'cors',
|
|
'Sec-Fetch-Site': 'same-origin',
|
|
'Priority': 'u=0',
|
|
}
|
|
|
|
async def make_new_json_request(user_id: int):
|
|
url = f'http://localhost/api/token/{user_id}'
|
|
data = {
|
|
'password': API_PASSWORD
|
|
}
|
|
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
}
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
async with session.post(url, json=data, headers=headers) as response:
|
|
response_text = await response.text()
|
|
if(len(response_text) < 14):
|
|
return response_text
|
|
else:
|
|
return False
|
|
|
|
async def post_chat_update(session: aiohttp.ClientSession, chat_object: Dict[str, Any], xsrf_token: str):
|
|
pattern = r'/(\d+)/'
|
|
match = re.search(pattern, chat_object.get('icon_url'))
|
|
|
|
token = await make_new_json_request(match.group(1))
|
|
form_data = aiohttp.FormData()
|
|
if(token):
|
|
form_data.add_field('text', f"""You've signed up for the Secret Sketchers event, your link is http://localhost/profile/{token} - it is private and should only be used by you.
|
|
\nDon't want to be in it anymore? Go to http://localhost/withdraw/{token} - note that withdrawing is permanent and you cannot re-enter the event.""")
|
|
else:
|
|
form_data.add_field('text', f"""Something went wrong, Sorry I will fix it sooner or later... maybe""")
|
|
|
|
headers = get_post_headers(xsrf_token)
|
|
|
|
try:
|
|
cookie_headers = get_session_cookie_header()
|
|
if cookie_headers:
|
|
headers['Cookie'] = cookie_headers['Cookie']
|
|
|
|
async with session.post(
|
|
f"{TARGET_URL}/{chat_object.get('id')}/messages",
|
|
data=form_data,
|
|
headers=headers
|
|
) as response:
|
|
if response.status not in [200, 201]:
|
|
text = await response.text()
|
|
print(f" Status: {response.status}. Response: {text}", file=sys.stderr)
|
|
except aiohttp.ClientError as e:
|
|
print(f"-> POST ERROR: Client error during update for chat ID {chat_object.get('id', 'N/A')}: {e}", file=sys.stderr)
|
|
except Exception as e:
|
|
print(f"-> POST ERROR: Unexpected error during update for chat ID {chat_object.get('id', 'N/A')}: {e}", file=sys.stderr)
|
|
|
|
|
|
|
|
async def process_chats():
|
|
global XSRF_TOKEN
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
headers = get_session_cookie_header()
|
|
if not headers:
|
|
return
|
|
|
|
async with session.get(TARGET_URL, headers=headers) as response:
|
|
if response.status != 200:
|
|
print(f"failed: {response.status}", file=sys.stderr)
|
|
return
|
|
|
|
try:
|
|
chat_data_array = await response.json()
|
|
if not isinstance(chat_data_array, list):
|
|
print("not json", file=sys.stderr)
|
|
return
|
|
|
|
except aiohttp.ContentTypeError:
|
|
print("also not json", file=sys.stderr)
|
|
return
|
|
|
|
fresh_xsrf_token = None
|
|
for cookie in session.cookie_jar:
|
|
if cookie.key == "XSRF-TOKEN":
|
|
fresh_xsrf_token = urllib.parse.unquote(cookie.value)
|
|
break
|
|
|
|
XSRF_TOKEN = fresh_xsrf_token
|
|
|
|
for index, chat_object in enumerate(chat_data_array):
|
|
if(chat_object.get('type') == 'private'):
|
|
seen_at_value = chat_object.get('seen_at')
|
|
is_unread = False
|
|
|
|
try:
|
|
if isinstance(seen_at_value, dict):
|
|
is_unread = True
|
|
# print(f"Chat {index}: Status: DICT (New Chat) -> Unread: True")
|
|
else:
|
|
latest_created_at = chat_object.get('latest', {}).get('created_at', 0)
|
|
seen_at_int = int(seen_at_value) if seen_at_value is not None else 0
|
|
|
|
is_unread = seen_at_int < latest_created_at
|
|
# print(f"Chat {index}: Seen at ({seen_at_int}) < Latest message ({latest_created_at}) -> Unread: {is_unread}")
|
|
|
|
except (AttributeError, TypeError, ValueError) as e:
|
|
is_unread = False
|
|
|
|
if is_unread:
|
|
await post_chat_update(session, chat_object, fresh_xsrf_token)
|
|
|
|
|
|
async def main_loop():
|
|
while True:
|
|
try:
|
|
await process_chats()
|
|
await asyncio.sleep(10)
|
|
|
|
except asyncio.CancelledError:
|
|
print("cancelled normally (ok knowing my luck the error is gonna be this one every single time even tho it never actually is an error like this!) cute puppy: Hiiii")
|
|
break
|
|
except Exception as e:
|
|
print(f"error {e}")
|
|
await asyncio.sleep(10)
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main_loop())
|