成功获取登录后的 ck

This commit is contained in:
yml2213
2026-06-21 23:02:09 +08:00
parent 4dca691890
commit ea8a56ca66
4 changed files with 397 additions and 81 deletions
+5
View File
@@ -0,0 +1,5 @@
{
"username": "用户9006787894",
"cookie": "dy_accounts_main=; dy_auth=e105BJzhxVWQ0tJKUXiiqN8ky11wgfj7ClW%2F8qo%2B1MPv8%2BDGHBq8VnuaJ1MWnm4EZqgVfLkM6PETWtGpLAHvadcMxCn9HWvTfT8%2BSKZR2dmsvReubJmeqBo; wan_auth37wan=4dc825c71d00LrMvodf%2B7ZrhnyMcsREgwQlgd7Ec06k5%2Bm%2FM0tdv0AJJ8gpoNoinSoPHS3vbJO8iK1vqiXfF2hTOLQqJOW3Wzs19DWLwpJpQgV8J%2BF0; LTP0=eyJhbGciOiJtZDUiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOlsicGFzc3BvcnQiXSwiY3QiOjAsImN0aW1lIjoxNzgyMDU0MDA0LCJleHAiOjE3OTc4MjIwMTYsImtleSI6ImR5LWp3dC1tZDUiLCJsdGsiOiJhZDMxOTliZGRhYmM3ZWRmIiwibHRraWQiOjgwNTYxMzgyLCJzdWIiOiJsdCIsInVpZCI6OTg2Mzg1Mjk2fQ.Y2M0YTQ4YjQwZGYwZWI4YWZiZTllNmVjODNiZmJkMGE; last_login_way=nickname; PHPSESSID=ccr09aa2letju5662gdh5aih62; acf_auth=1a34kGp6rHjazzRy4fVzKXX11qCjC9RuQASDGOEfkRbLjsI3A5PzSCXawo5hZvO4g47LF9ruapNiMpf%2BE3u4TplWPMkW4c4eI0pyG05a%2F9dKr%2BOGM0hAEXk; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk4NjM4NTI5NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MDU2MTM4Miwic3RrIjoiNjFhODczZjUwZTg3M2Y5MCIsImV4cCI6MTc4MjY1ODgwNCwiaWF0IjoxNzgyMDU0MDA0LCJrZXkiOiJkeS1qd3QtbWQ1In0.OTFlMjIxOGU4MjZkMjcwMWM4NjQ1NTcwNjNmZWIzMDA; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk4NjM4NTI5NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MDU2MTM4Miwic3RrIjoiNjFhODczZjUwZTg3M2Y5MCIsImV4cCI6MTc4MjY1ODgwNCwiaWF0IjoxNzgyMDU0MDA0LCJrZXkiOiJkeS1qd3QtbWQ1In0.N2E0NmEyOWVlZDE1NTE4NDg0MTFlMmM5OGY1ZGQzZTk; acf_uid=986385296; acf_username=986385296; acf_nickname=%E7%94%A8%E6%88%B79006787894; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F25_; acf_ct=0; acf_ltkid=80561382; acf_biz=1; acf_stk=61a873f50e873f90; acf_devid=d53498d383f449f4c4ea28c4165aa2b4",
"timestamp": 1782054016
}
+178 -73
View File
@@ -2,10 +2,12 @@
import imaplib import imaplib
import email import email
import html
import re import re
import time import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
from email.header import decode_header from email.header import decode_header
from email.utils import parsedate_to_datetime
from typing import Optional from typing import Optional
from loguru import logger from loguru import logger
@@ -20,12 +22,18 @@ class EmailVerifier:
username: str, username: str,
password: str, password: str,
timeout: float = 12, timeout: float = 12,
mailbox: str = "INBOX",
lookback_minutes: int = 10,
max_messages: int = 20,
): ):
self.imap_server = imap_server self.imap_server = imap_server
self.imap_port = imap_port self.imap_port = imap_port
self.username = username self.username = username
self.password = password self.password = password
self.timeout = timeout self.timeout = timeout
self.mailbox = mailbox
self.lookback_minutes = lookback_minutes
self.max_messages = max_messages
self._connection: Optional[imaplib.IMAP4_SSL] = None self._connection: Optional[imaplib.IMAP4_SSL] = None
def connect(self) -> None: def connect(self) -> None:
@@ -52,13 +60,21 @@ class EmailVerifier:
pass pass
self._connection = None self._connection = None
def get_verification_code(self, max_wait: int = 60, interval: int = 2) -> str: def get_verification_code(
self,
max_wait: int = 60,
interval: int = 2,
after_timestamp: Optional[float] = None,
allow_old_seconds: int = 15,
) -> str:
""" """
获取斗鱼验证码 获取斗鱼验证码
Args: Args:
max_wait: 最大等待时间(秒) max_wait: 最大等待时间(秒)
interval: 轮询间隔(秒) interval: 轮询间隔(秒)
after_timestamp: 发起发送验证码请求的时间戳,用于过滤旧邮件
allow_old_seconds: 邮件服务器时间允许向前偏移的秒数
Returns: Returns:
6位验证码 6位验证码
@@ -68,9 +84,12 @@ class EmailVerifier:
self.connect() self.connect()
try: try:
start_time = time.time() deadline = time.monotonic() + max_wait
while time.time() - start_time < max_wait: while time.monotonic() < deadline:
code = self._fetch_latest_code() code = self._fetch_latest_code(
after_timestamp=after_timestamp,
allow_old_seconds=allow_old_seconds,
)
if code: if code:
logger.success(f"获取到验证码: {code}") logger.success(f"获取到验证码: {code}")
return code return code
@@ -82,56 +101,151 @@ class EmailVerifier:
finally: finally:
self.disconnect() self.disconnect()
def _fetch_latest_code(self) -> Optional[str]: def _fetch_latest_code(
self,
after_timestamp: Optional[float] = None,
allow_old_seconds: int = 15,
) -> Optional[str]:
"""从IMAP获取最新验证码""" """从IMAP获取最新验证码"""
try: try:
self._connection.select('INBOX') if not self._connection:
raise RuntimeError("IMAP未连接")
# 搜索最近5分钟的邮件 status, _ = self._connection.select(self.mailbox, readonly=True)
since = (datetime.now() - timedelta(minutes=5)).strftime("%d-%b-%Y") if status != 'OK':
status, messages = self._connection.search( logger.warning(f"选择邮箱目录失败: {self.mailbox}")
None, return None
f'(OR (FROM "douyu") (FROM "斗鱼") (SUBJECT "验证码")) SINCE {since}'
) since = self._build_since_date(after_timestamp, allow_old_seconds)
# IMAP命令只能稳定发送ASCII条件,中文主题/发件人改到本地解析过滤。
status, messages = self._connection.search(None, 'SINCE', since)
if status != 'OK' or not messages[0]: if status != 'OK' or not messages[0]:
return None return None
# 获取最新的一封邮件 message_ids = messages[0].split()
latest_id = messages[0].split()[-1] recent_ids = list(reversed(message_ids[-self.max_messages:]))
status, msg_data = self._connection.fetch(latest_id, '(RFC822)') logger.debug(f"扫描最近 {len(recent_ids)} 封邮件,SINCE {since}")
if status != 'OK': for message_id in recent_ids:
return None msg = self._fetch_message(message_id)
if not msg:
continue
msg = email.message_from_bytes(msg_data[0][1]) if not self._is_recent_enough(msg, after_timestamp, allow_old_seconds):
continue
# 检查是否是斗鱼的邮件
subject = self._decode_subject(msg.get('Subject', '')) subject = self._decode_subject(msg.get('Subject', ''))
if not self._is_douyu_email(subject, msg.get('From', '')): from_addr = msg.get('From', '')
return None
# 提取验证码
body = self._get_email_body(msg) body = self._get_email_body(msg)
if not self._is_douyu_email(subject, from_addr, body):
continue
code = self._extract_verification_code(body) code = self._extract_verification_code(body)
if code: if code:
logger.info(f"从邮件中提取到验证码: {code}") logger.info(f"从邮件中提取到验证码,主题: {subject}")
return code return code
return None
except imaplib.IMAP4.abort as e:
logger.warning(f"IMAP连接中断,准备下轮重连: {e}")
self.disconnect()
self.connect()
return None
except Exception as e: except Exception as e:
logger.error(f"获取邮件失败: {e}") logger.error(f"获取邮件失败: {e}")
return None return None
def _is_douyu_email(self, subject: str, from_addr: str) -> bool: def _build_since_date(
"""判断是否是斗鱼的邮件""" self,
douyu_keywords = ['斗鱼', 'douyu', '验证码', '安全验证'] after_timestamp: Optional[float],
subject_lower = subject.lower() allow_old_seconds: int,
from_lower = from_addr.lower() ) -> str:
"""构造IMAP SINCE日期,月份固定用英文缩写。"""
if after_timestamp:
since_dt = datetime.fromtimestamp(
max(0, after_timestamp - allow_old_seconds)
)
else:
since_dt = datetime.now() - timedelta(minutes=self.lookback_minutes)
return any(keyword in subject_lower or keyword in from_lower return self._format_imap_date(since_dt)
for keyword in douyu_keywords)
def _format_imap_date(self, value: datetime) -> str:
"""格式化IMAP日期,避免系统locale影响月份名称。"""
months = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
]
return f"{value.day:02d}-{months[value.month - 1]}-{value.year}"
def _fetch_message(self, message_id: bytes) -> Optional[email.message.Message]:
"""获取邮件完整内容,使用PEEK避免标记已读。"""
status, msg_data = self._connection.fetch(message_id, '(BODY.PEEK[])')
if status != 'OK':
return None
raw_message = self._join_fetch_payload(msg_data)
if not raw_message:
return None
return email.message_from_bytes(raw_message)
def _join_fetch_payload(self, msg_data) -> bytes:
"""合并IMAP fetch返回中的邮件字节内容。"""
chunks = []
for item in msg_data:
if isinstance(item, tuple) and len(item) >= 2 and isinstance(item[1], bytes):
chunks.append(item[1])
elif isinstance(item, bytes) and item.startswith(b'From '):
chunks.append(item)
return b''.join(chunks)
def _is_recent_enough(
self,
msg: email.message.Message,
after_timestamp: Optional[float],
allow_old_seconds: int,
) -> bool:
"""检查邮件时间是否晚于本次发送验证码请求。"""
if not after_timestamp:
return True
message_time = self._parse_message_time(msg)
if not message_time:
logger.debug("邮件缺少Date头,保守纳入候选")
return True
if message_time.timestamp() < after_timestamp - allow_old_seconds:
subject = self._decode_subject(msg.get('Subject', ''))
logger.debug(f"跳过旧邮件: {subject}")
return False
return True
def _parse_message_time(self, msg: email.message.Message) -> Optional[datetime]:
"""解析邮件Date头。"""
date_header = msg.get('Date')
if not date_header:
return None
try:
return parsedate_to_datetime(date_header)
except (TypeError, ValueError):
return None
def _is_douyu_email(self, subject: str, from_addr: str, body: str = "") -> bool:
"""判断是否是斗鱼的邮件"""
douyu_keywords = ['斗鱼', 'douyu', 'douyutv']
verify_keywords = ['验证码', '安全验证', '登录验证', '动态码', '校验码']
combined = f"{subject}\n{from_addr}\n{body[:500]}".lower()
return (
any(keyword in combined for keyword in douyu_keywords)
or any(keyword in subject for keyword in verify_keywords)
)
def _decode_subject(self, subject: str) -> str: def _decode_subject(self, subject: str) -> str:
"""解码邮件主题""" """解码邮件主题"""
@@ -149,72 +263,63 @@ class EmailVerifier:
def _get_email_body(self, msg: email.message.Message) -> str: def _get_email_body(self, msg: email.message.Message) -> str:
"""获取邮件正文""" """获取邮件正文"""
body = "" parts = []
if msg.is_multipart(): if msg.is_multipart():
for part in msg.walk(): for part in msg.walk():
content_type = part.get_content_type() content_type = part.get_content_type()
if part.get_content_disposition() == 'attachment':
continue
if content_type == 'text/plain' or content_type == 'text/html': if content_type == 'text/plain' or content_type == 'text/html':
decoded = self._decode_part_payload(part)
if decoded:
parts.append(decoded)
else:
decoded = self._decode_part_payload(msg)
if decoded:
parts.append(decoded)
return "\n".join(parts)
def _decode_part_payload(self, part: email.message.Message) -> str:
"""解码邮件片段内容。"""
try: try:
payload = part.get_payload(decode=True) payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8' if payload is None:
body += payload.decode(charset, errors='ignore') raw_payload = part.get_payload()
except: return raw_payload if isinstance(raw_payload, str) else ""
pass
else:
try:
payload = msg.get_payload(decode=True)
charset = msg.get_content_charset() or 'utf-8'
body = payload.decode(charset, errors='ignore')
except:
pass
return body charset = part.get_content_charset() or 'utf-8'
return payload.decode(charset, errors='ignore')
except Exception:
return ""
def _extract_verification_code(self, text: str) -> Optional[str]: def _extract_verification_code(self, text: str) -> Optional[str]:
"""从文本中提取验证码""" """从文本中提取验证码"""
# 清理HTML标签 # 清理HTML标签和实体,方便匹配中文邮件模板。
text = html.unescape(text)
text = re.sub(r'<[^>]+>', ' ', text) text = re.sub(r'<[^>]+>', ' ', text)
text = re.sub(r'\s+', ' ', text)
# 查找6位数字验证码 # 查找6位数字验证码
# 注意:中文冒号和英文冒号需要分别处理 # 注意:中文冒号和英文冒号需要分别处理
patterns = [ patterns = [
r'验证码[:]\s*(\d{6})', # 验证码:123456 或 验证码:123456 r'(?:验证码|校验码|动态码|安全码)\s*(?:是|为|:|)?\s*(\d{6})',
r'验证码\s+(\d{6})', # 验证码 123456 r'(?:您的|你的|本次)?(?:验证码|校验码|动态码|安全码)\s*(?:是|为)?\s*(\d{6})',
r'(\d{6})\s*是您的验证码', # 123456是您的验证码 r'(\d{6})\s*(?:是|为)?(?:您的|你的|本次)?(?:验证码|校验码|动态码|安全码)',
r'您的验证码[是为]\s*[:]\s*(\d{6})', # 您的验证码是:123456 r'(?:verification\s+code|security\s+code|code)\s*(?:is|:|)?\s*(\d{6})',
r'您的验证码[是为]\s*(\d{6})', # 您的验证码是123456 r'\b(\d{6})\b', # 候选邮件已经过滤为斗鱼验证码邮件,最后再兜底匹配6位数字。
r'verification code[:]\s*(\d{6})',
r'code[:]\s*(\d{6})',
r'(\d{6})', # 最后尝试匹配任何6位数字
] ]
for pattern in patterns: for pattern in patterns:
match = re.search(pattern, text, re.IGNORECASE) match = re.search(pattern, text, re.IGNORECASE)
if match: if match:
code = match.group(1) code = match.group(1)
# 验证码有效性检查(简单过滤明显不是验证码的数字)
if not self._is_likely_code(code):
continue
return code return code
return None return None
def _is_likely_code(self, code: str) -> bool:
"""判断是否是有效的验证码"""
# 过滤掉一些明显不是验证码的数字
invalid_patterns = [
r'^(\d)\1{5}$', # 全部相同:111111, 222222
r'^123456$', # 顺序数字
r'^654321$', # 逆序数字
]
for pattern in invalid_patterns:
if re.match(pattern, code):
return False
return True
def get_email_config_for_account(email_address: str) -> dict: def get_email_config_for_account(email_address: str) -> dict:
"""根据邮箱地址自动配置IMAP服务器""" """根据邮箱地址自动配置IMAP服务器"""
+7 -3
View File
@@ -222,11 +222,12 @@ class DouyuLogin:
# 4️⃣ 发送邮箱验证 # 4️⃣ 发送邮箱验证
logger.info("步骤4: 发送邮箱验证...") logger.info("步骤4: 发送邮箱验证...")
email_sent_at = time.time()
self._send_email_verify(remote_code) self._send_email_verify(remote_code)
# 5️⃣ IMAP获取验证码 # 5️⃣ IMAP获取验证码
logger.info("步骤5: 获取邮箱验证码...") logger.info("步骤5: 获取邮箱验证码...")
verify_code = self._get_email_code() verify_code = self._get_email_code(after_timestamp=email_sent_at)
# 6️⃣ 提交验证码 # 6️⃣ 提交验证码
logger.info("步骤6: 提交验证码...") logger.info("步骤6: 提交验证码...")
@@ -455,7 +456,7 @@ class DouyuLogin:
logger.info("验证邮件已发送") logger.info("验证邮件已发送")
def _get_email_code(self) -> str: def _get_email_code(self, after_timestamp: Optional[float] = None) -> str:
"""获取邮箱验证码""" """获取邮箱验证码"""
verifier = EmailVerifier( verifier = EmailVerifier(
imap_server=self.account.email_imap_server, imap_server=self.account.email_imap_server,
@@ -464,7 +465,10 @@ class DouyuLogin:
password=self.account.email_password, password=self.account.email_password,
) )
return verifier.get_verification_code(max_wait=60) return verifier.get_verification_code(
max_wait=60,
after_timestamp=after_timestamp,
)
def _submit_verify_code(self, remote_code: str, verify_code: str) -> str: def _submit_verify_code(self, remote_code: str, verify_code: str) -> str:
""" """
+202
View File
@@ -0,0 +1,202 @@
#!/usr/bin/env python3
"""邮箱IMAP读取测试脚本"""
import argparse
import re
import sys
import time
from datetime import datetime, timedelta
from pathlib import Path
# 添加项目根目录到path
sys.path.insert(0, str(Path(__file__).parent))
from douyu.config import Config
from douyu.email_verifier import EmailVerifier
from utils.logger import setup_logger
def mask_email(email_address: str) -> str:
"""隐藏邮箱中间部分,避免控制台泄露完整账号。"""
if "@" not in email_address:
return email_address
name, domain = email_address.split("@", 1)
if len(name) <= 2:
masked_name = name[0] + "*"
else:
masked_name = name[:2] + "*" * max(2, len(name) - 4) + name[-2:]
return f"{masked_name}@{domain}"
def clean_preview(text: str, max_length: int = 120) -> str:
"""清理正文预览,保持输出紧凑。"""
preview = " ".join(text.split())
preview = re.sub(r"\b\d{6}\b", "******", preview)
if len(preview) <= max_length:
return preview
return preview[:max_length] + "..."
def format_code(code: str, show_code: bool) -> str:
"""格式化验证码输出,默认隐藏大部分数字。"""
if not code:
return "未提取到"
if show_code:
return code
return f"****{code[-2:]}"
def format_message_time(verifier: EmailVerifier, msg) -> str:
"""格式化邮件时间。"""
message_time = verifier._parse_message_time(msg)
if not message_time:
return "未知"
return message_time.strftime("%Y-%m-%d %H:%M:%S %z")
def load_account(config_path: str, index: int):
"""从配置文件加载指定账号。"""
config = Config(config_path)
accounts = config.get_accounts()
if not accounts:
raise ValueError("config.yaml 中没有配置账号")
if index < 0 or index >= len(accounts):
raise IndexError(f"账号索引超出范围: {index},当前共有 {len(accounts)} 个账号")
return accounts[index]
def list_recent_messages(
verifier: EmailVerifier,
lookback_minutes: int,
limit: int,
show_code: bool,
) -> None:
"""列出最近邮件,并测试斗鱼验证码解析。"""
verifier.connect()
try:
status, _ = verifier._connection.select(verifier.mailbox, readonly=True)
if status != "OK":
raise RuntimeError(f"选择邮箱目录失败: {verifier.mailbox}")
since_dt = datetime.now() - timedelta(minutes=lookback_minutes)
since = verifier._format_imap_date(since_dt)
status, messages = verifier._connection.search(None, "SINCE", since)
if status != "OK":
raise RuntimeError(f"搜索邮件失败: status={status}")
message_ids = messages[0].split() if messages and messages[0] else []
if not message_ids:
print(f"最近 {lookback_minutes} 分钟没有邮件")
return
recent_ids = list(reversed(message_ids[-limit:]))
print(f"搜索范围: 最近 {lookback_minutes} 分钟")
print(f"匹配邮件数: {len(message_ids)},展示最新 {len(recent_ids)}\n")
for index, message_id in enumerate(recent_ids, start=1):
msg = verifier._fetch_message(message_id)
if not msg:
print(f"[{index}] 读取失败: id={message_id.decode(errors='ignore')}")
continue
subject = verifier._decode_subject(msg.get("Subject", ""))
from_addr = msg.get("From", "")
body = verifier._get_email_body(msg)
is_douyu = verifier._is_douyu_email(subject, from_addr, body)
code = verifier._extract_verification_code(body) if is_douyu else ""
print(f"[{index}] 邮件ID: {message_id.decode(errors='ignore')}")
print(f" 时间: {format_message_time(verifier, msg)}")
print(f" 发件人: {from_addr}")
print(f" 主题: {subject}")
print(f" 斗鱼邮件: {'' if is_douyu else ''}")
print(f" 验证码: {format_code(code, show_code)}")
print(f" 正文预览: {clean_preview(body)}\n")
finally:
verifier.disconnect()
def wait_verification_code(
account,
max_wait: int,
interval: int,
new_only: bool,
show_code: bool,
) -> None:
"""等待并提取斗鱼验证码,不负责发送验证码邮件。"""
verifier = EmailVerifier(
imap_server=account.email_imap_server,
imap_port=account.email_imap_port,
username=account.email,
password=account.email_password,
)
after_timestamp = time.time() if new_only else None
code = verifier.get_verification_code(
max_wait=max_wait,
interval=interval,
after_timestamp=after_timestamp,
)
print(f"等待验证码结果: {format_code(code, show_code)}")
def parse_args() -> argparse.Namespace:
"""解析命令行参数。"""
parser = argparse.ArgumentParser(description="测试邮箱IMAP是否能读取邮件和验证码")
parser.add_argument("-c", "--config", default="config.yaml", help="配置文件路径")
parser.add_argument("-i", "--index", type=int, default=0, help="账号索引")
parser.add_argument("--lookback-minutes", type=int, default=30, help="读取最近多少分钟邮件")
parser.add_argument("--limit", type=int, default=5, help="展示最近多少封邮件")
parser.add_argument("--wait-code", action="store_true", help="额外等待并提取斗鱼验证码")
parser.add_argument("--new-only", action="store_true", help="等待验证码时只接受脚本启动后的新邮件")
parser.add_argument("--show-code", action="store_true", help="输出完整验证码")
parser.add_argument("--max-wait", type=int, default=60, help="等待验证码最大秒数")
parser.add_argument("--interval", type=int, default=2, help="验证码轮询间隔秒数")
parser.add_argument("--log-level", default="INFO", help="日志级别")
return parser.parse_args()
def main() -> None:
"""脚本入口。"""
args = parse_args()
setup_logger(level=args.log_level)
account = load_account(args.config, args.index)
print("=== 邮箱IMAP读取测试 ===")
print(f"账号索引: {args.index}")
print(f"邮箱账号: {mask_email(account.email)}")
print(f"IMAP服务器: {account.email_imap_server}:{account.email_imap_port}\n")
verifier = EmailVerifier(
imap_server=account.email_imap_server,
imap_port=account.email_imap_port,
username=account.email,
password=account.email_password,
lookback_minutes=args.lookback_minutes,
max_messages=args.limit,
)
list_recent_messages(
verifier=verifier,
lookback_minutes=args.lookback_minutes,
limit=args.limit,
show_code=args.show_code,
)
if args.wait_code:
print("=== 等待斗鱼验证码 ===")
wait_verification_code(
account=account,
max_wait=args.max_wait,
interval=args.interval,
new_only=args.new_only,
show_code=args.show_code,
)
if __name__ == "__main__":
main()