1- import getpass
21import os
32import re
43import warnings
54from http .cookiejar import LWPCookieJar
6- from typing import List , Optional , Tuple , Union
5+ from typing import List , Optional , Union
6+ from http .cookiejar import Cookie
77
88import requests
99from bs4 import BeautifulSoup
@@ -61,10 +61,62 @@ def __call__(cls, *args, **kwargs):
6161 return cls ._instances [cls ]
6262
6363
64- def default_credential_supplier () -> Tuple [str , str ]:
65- username = input ('AtCoder username: ' )
66- password = getpass .getpass ('AtCoder password: ' )
67- return username , password
64+ def show_cookie_instructions ():
65+ """Show instructions for obtaining REVEL_SESSION cookie"""
66+ print ("\n [English]" )
67+ print ("=== How to get AtCoder REVEL_SESSION cookie ===" )
68+ print ("1. Log in to AtCoder using your browser" )
69+ print (" https://atcoder.jp/login" )
70+ print ()
71+ print ("2. Open Developer Tools by pressing F12" )
72+ print ()
73+ print ("3. Follow these steps to get the REVEL_SESSION cookie:" )
74+ print (" - Click the 'Application' tab (or 'Storage' tab in Firefox)" )
75+ print (" - Select 'Cookies' → 'https://atcoder.jp' from the left sidebar" )
76+ print (" - Find 'REVEL_SESSION' and copy its 'Value'" )
77+ print ()
78+ print ("4. Paste the REVEL_SESSION value below" )
79+ print ("=" * 48 )
80+ print ()
81+ print ("[日本語/Japanese]" )
82+ print ("=== AtCoder REVEL_SESSION クッキーの取得方法 ===" )
83+ print ("1. ブラウザでAtCoderにログインしてください" )
84+ print (" https://atcoder.jp/login" )
85+ print ()
86+ print ("2. F12キーを押して開発者ツールを開いてください" )
87+ print ()
88+ print ("3. 以下の手順でREVEL_SESSIONクッキーを取得してください:" )
89+ print (" - 「Application」タブ(Firefoxの場合は「Storage」タブ)をクリック" )
90+ print (" - 左側から「Cookies」→「https://atcoder.jp」を選択" )
91+ print (" - 「REVEL_SESSION」の「Value」列の値をコピー" )
92+ print ()
93+ print ("4. 下記にREVEL_SESSIONの値を貼り付けてください" )
94+ print ("=" * 48 )
95+
96+
97+ def default_cookie_supplier () -> Cookie :
98+ """Get REVEL_SESSION cookie from user input and return Cookie object"""
99+ show_cookie_instructions ()
100+ cookie_value = input ('\n REVEL_SESSION cookie value: ' ).strip ()
101+ return Cookie (
102+ version = 0 ,
103+ name = 'REVEL_SESSION' ,
104+ value = cookie_value ,
105+ port = None ,
106+ port_specified = False ,
107+ domain = 'atcoder.jp' ,
108+ domain_specified = True ,
109+ domain_initial_dot = False ,
110+ path = '/' ,
111+ path_specified = True ,
112+ secure = True ,
113+ expires = None ,
114+ discard = True ,
115+ comment = None ,
116+ comment_url = None ,
117+ rest = {},
118+ rfc2109 = False
119+ )
68120
69121
70122class AtCoderClient (metaclass = Singleton ):
@@ -75,40 +127,39 @@ def __init__(self):
75127 def check_logging_in (self ):
76128 private_url = "https://atcoder.jp/home"
77129 resp = self ._request (private_url )
78- return resp .text .find ("Sign In" ) == - 1
130+ # consider logged in if settings link exists (only shown when logged in)
131+ return 'href="/settings"' in resp .text
79132
80133 def login (self ,
81- credential_supplier = None ,
134+ cookie_supplier = None ,
82135 use_local_session_cache = True ,
83136 save_session_cache = True ):
84137
85- if credential_supplier is None :
86- credential_supplier = default_credential_supplier
87-
88138 if use_local_session_cache :
89- load_cookie_to (self ._session )
90- if self .check_logging_in ():
91- logger .info (
92- "Successfully Logged in using the previous session cache." )
93- logger .info (
94- "If you'd like to invalidate the cache, delete {}." .format (default_cookie_path ))
95-
96- return
97-
98- username , password = credential_supplier ()
99-
100- soup = BeautifulSoup (self ._session .get (
101- "https://atcoder.jp/login" ).text , "html.parser" )
102- token = soup .find_all ("form" )[1 ].find (
103- "input" , type = "hidden" ).get ("value" )
104- resp = self ._request ("https://atcoder.jp/login" , data = {
105- 'username' : username ,
106- "password" : password ,
107- "csrf_token" : token
108- }, method = 'POST' )
109-
110- if resp .text .find ("パスワードを忘れた方はこちら" ) != - 1 or resp .text .find ("Forgot your password" ) != - 1 :
111- raise LoginError
139+ session_cache_exists = load_cookie_to (self ._session )
140+ if session_cache_exists :
141+ if self .check_logging_in ():
142+ logger .info (
143+ "Successfully Logged in using the previous session cache." )
144+ logger .info (
145+ "If you'd like to invalidate the cache, delete {}." .format (default_cookie_path ))
146+ return
147+ else :
148+ logger .warn (
149+ "Failed to login with the session cache. The session cache is invalid, or has been expired. Trying to login without cache." )
150+
151+ if cookie_supplier is None :
152+ cookie_supplier = default_cookie_supplier
153+
154+ cookie = cookie_supplier ()
155+ self ._session .cookies .set_cookie (cookie )
156+
157+ # Verify the cookie is valid
158+ if not self .check_logging_in ():
159+ raise LoginError (
160+ "Login attempt failed. REVEL_SESSION cookie could be invalid or expired." )
161+ else :
162+ logger .info ("Successfully logged in using REVEL_SESSION cookie." )
112163
113164 if use_local_session_cache and save_session_cache :
114165 save_cookie (self ._session )
@@ -159,7 +210,8 @@ def download_all_contests(self) -> List[Contest]:
159210 contest_ids = sorted (contest_ids )
160211 return [Contest (contest_id ) for contest_id in contest_ids ]
161212
162- def submit_source_code (self , contest : Contest , problem : Problem , lang : Union [str , Language ], source : str ) -> Submission :
213+ def submit_source_code (self , contest : Contest , problem : Problem , lang : Union [str , Language ],
214+ source : str ) -> Submission :
163215 if isinstance (lang , str ):
164216 warnings .warn (
165217 "Parameter lang as a str object is deprecated. "
0 commit comments