Skip to content

Instantly share code, notes, and snippets.

@15532th
Last active February 22, 2024 20:23
Show Gist options
  • Save 15532th/be90ce1b9cbd7cf7b2af8fd10609ff19 to your computer and use it in GitHub Desktop.
Save 15532th/be90ce1b9cbd7cf7b2af8fd10609ff19 to your computer and use it in GitHub Desktop.
Update FC2 login cookies
#!/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