Instantly share code, notes, and snippets.
Last active
February 22, 2024 20:23
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save 15532th/be90ce1b9cbd7cf7b2af8fd10609ff19 to your computer and use it in GitHub Desktop.
Update FC2 login cookies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import logging | |
import re | |
import urllib.request | |
from http import cookiejar | |
from pathlib import Path | |
from typing import Optional | |
from urllib.error import HTTPError, URLError | |
DESCRIPTION = '''Rotate fc2.com cookies. | |
Load (possibly expired) cookies from a file, try to log in and write updated values back on success. | |
The file must be in Netscape format, using either UTF8 without BOM or a plain ASCII encoding. | |
For update process to work, it must contain cookies from all fc2.com subdomains, | |
collected in a logged in state with "Keep me logged in" checkbox enabled. | |
''' | |
class CookiesLoadError(Exception): | |
"""Raised if loading cookies from file failed""" | |
class CookiesStoreError(Exception): | |
"""Raised if writing cookies to file failed""" | |
class NetworkRequestError(Exception): | |
"""Raised if network request failed or remote server responded with error""" | |
class LoginError(Exception): | |
"""Raised if login process didn't proceed as expected""" | |
def set_loggers(log_level=logging.INFO): | |
log_format = '[%(asctime)s] %(message)s' | |
datefmt = '%Y/%m/%d %H:%M:%S' | |
logging.basicConfig(level=log_level, format=log_format, datefmt=datefmt) | |
def load_cookies(path: Path) -> cookiejar.MozillaCookieJar: | |
"""load cookies from a text file in Netscape format""" | |
cookie_jar = cookiejar.MozillaCookieJar(path) | |
try: | |
cookie_jar.load(ignore_discard=True, ignore_expires=True) | |
return cookie_jar | |
except FileNotFoundError as e: | |
raise CookiesLoadError(f'failed to load cookies from "{path}": file not found') from e | |
except (cookiejar.LoadError, OSError) as e: | |
raise CookiesLoadError(f'failed to load cookies from "{path}": {e}') from e | |
def store_cookies(cookie_jar: cookiejar.MozillaCookieJar, path: Path) -> None: | |
"""store cookies as a text file in Netscape format""" | |
if path.is_dir(): | |
raise CookiesStoreError(f'failed to write cookies to "{path}": directory with this name already exists') | |
try: | |
cookie_jar.save(path, ignore_discard=True, ignore_expires=True) | |
except OSError as e: | |
raise CookiesStoreError(f'failed to write cookies to "{path}": {e}') from e | |
def prepare_opener(cookies: cookiejar.CookieJar) -> urllib.request.OpenerDirector: | |
cookie_handler = urllib.request.HTTPCookieProcessor(cookiejar=cookies) | |
opener = urllib.request.build_opener(cookie_handler) | |
return opener | |
def make_request(opener: urllib.request.OpenerDirector, url: str) -> str: | |
"""Make GET request to the url using opener, | |
return page text decoded as UTF8 or None on error""" | |
try: | |
with opener.open(url, timeout=60) as response: | |
data = response.read() | |
text = data.decode() | |
return text | |
except UnicodeDecodeError as e: | |
raise NetworkRequestError(f'error getting {url}: failed to decode response page: {e}') from e | |
except HTTPError as e: | |
raise NetworkRequestError(f'error getting {url}: got response {e.status} {e.reason}') from e | |
except URLError as e: | |
raise NetworkRequestError(f'error getting {url}: {e.reason}') from e | |
except TimeoutError as e: | |
raise NetworkRequestError(f'error getting {url}: request timed out') from e | |
def find_first(pattern: str, text: str) -> Optional[str]: | |
match = re.findall(pattern, text) | |
if not match: | |
return None | |
return match[0] | |
def find_member_login_url(login_page: str) -> str: | |
re_member_login_url = r'href="(http://live\.fc2\.com/member_login/\?uid=[^"]+)"' | |
login_url = find_first(re_member_login_url, login_page) | |
if login_url is None: | |
raise LoginError('member login url not found.\nEnsure cookies file was prepared while logged in with the "Keep me logged in" checkbox checked') | |
return login_url | |
def log_in(cookies: cookiejar.MozillaCookieJar) -> cookiejar.MozillaCookieJar: | |
opener = prepare_opener(cookies) | |
login_url = 'https://live.fc2.com/login' | |
login_page = make_request(opener, login_url) | |
member_login_url = find_member_login_url(login_page) | |
live_page = make_request(opener, member_login_url) | |
if live_page.find('https://live.fc2.com/member_logout') == -1: | |
raise LoginError('no logout url on live page') | |
re_username = r'<span class="m-hder01_uName">(.*?)</span>' | |
name = find_first(re_username, live_page) | |
if name is None: | |
logging.info('logged in without error, but no username was found on the page') | |
else: | |
logging.info(f'successfully logged in as {name}') | |
return cookies | |
def rotate_cookies(input_file: Path, output_file: Path): | |
jar = load_cookies(input_file) | |
logging.info(f'successfully loaded cookies from {input_file}') | |
updated_jar = log_in(jar) | |
store_cookies(updated_jar, output_file) | |
logging.info(f'successfully stored updated cookies in {output_file}') | |
def main(): | |
parser = argparse.ArgumentParser(description=DESCRIPTION) | |
parser.add_argument("cookies", nargs='?', type=Path, help="file with cookies to rotate", default=Path('cookies.txt')) | |
parser.add_argument("--output", "-o", type=Path, help="output file. Overwrites source file if not specified", default=None) | |
args = parser.parse_args() | |
set_loggers() | |
input_file = args.cookies | |
output_file = args.output or args.cookies | |
try: | |
rotate_cookies(input_file, output_file) | |
except (CookiesLoadError, CookiesStoreError) as e: | |
logging.warning(e) | |
except (NetworkRequestError, LoginError) as e: | |
logging.warning(e) | |
logging.warning('updating cookies failed, aborting') | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment