chore: auto release commit (2026-03-31 17:00:42)
All checks were successful
技能自动化发布 / release (push) Successful in 15s
All checks were successful
技能自动化发布 / release (push) Successful in 15s
This commit is contained in:
Binary file not shown.
@@ -2,29 +2,42 @@ import sys
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
# Windows GBK 编码兼容修复
|
||||
if sys.platform == "win32":
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SKILL_SLUG = "account-manager"
|
||||
|
||||
PLATFORM_URLS = {
|
||||
# 自媒体/图文平台
|
||||
"sohu": "https://mp.sohu.com",
|
||||
"zhihu": "https://www.zhihu.com",
|
||||
"wechat": "https://mp.weixin.qq.com",
|
||||
|
||||
# 大模型平台
|
||||
"kimi": "https://kimi.moonshot.cn",
|
||||
"deepseek": "https://chat.deepseek.com",
|
||||
"doubao": "https://www.doubao.com",
|
||||
"qianwen": "https://tongyi.aliyun.com",
|
||||
"yiyan": "https://yiyan.baidu.com",
|
||||
"yuanbao": "https://yuanbao.tencent.com"
|
||||
"yuanbao": "https://yuanbao.tencent.com",
|
||||
}
|
||||
|
||||
# 新账号默认名称:搜狐1号 / sohu_2 等同平台序号
|
||||
_PLATFORM_NAME_CN = {
|
||||
"sohu": "搜狐",
|
||||
"zhihu": "知乎",
|
||||
"wechat": "微信",
|
||||
"kimi": "Kimi",
|
||||
"deepseek": "DeepSeek",
|
||||
"doubao": "豆包",
|
||||
"qianwen": "通义",
|
||||
"yiyan": "文心",
|
||||
"yuanbao": "元宝",
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +66,7 @@ def get_db_path():
|
||||
|
||||
|
||||
def get_default_profile_dir(account_id):
|
||||
path = os.path.join(get_skill_data_dir(), "profiles", account_id)
|
||||
path = os.path.join(get_skill_data_dir(), "profiles", str(account_id))
|
||||
os.makedirs(path, exist_ok=True)
|
||||
return path
|
||||
|
||||
@@ -62,33 +75,6 @@ def get_conn():
|
||||
return sqlite3.connect(get_db_path())
|
||||
|
||||
|
||||
def init_db():
|
||||
conn = get_conn()
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
platform TEXT NOT NULL,
|
||||
phone TEXT,
|
||||
profile_dir TEXT,
|
||||
login_status INTEGER DEFAULT 0,
|
||||
last_login_at TEXT,
|
||||
extra_json TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
_ensure_column(cur, "accounts", "login_status", "INTEGER DEFAULT 0")
|
||||
_ensure_column(cur, "accounts", "last_login_at", "TEXT")
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _ensure_column(cur, table_name, col_name, ddl):
|
||||
cur.execute(f"PRAGMA table_info({table_name})")
|
||||
cols = [row[1] for row in cur.fetchall()]
|
||||
@@ -96,14 +82,123 @@ def _ensure_column(cur, table_name, col_name, ddl):
|
||||
cur.execute(f"ALTER TABLE {table_name} ADD COLUMN {col_name} {ddl}")
|
||||
|
||||
|
||||
def _has_text_primary_id(cur):
|
||||
cur.execute("PRAGMA table_info(accounts)")
|
||||
for row in cur.fetchall():
|
||||
if row[1] == "id":
|
||||
t = (row[2] or "").upper()
|
||||
return "TEXT" in t or "CHAR" in t
|
||||
return False
|
||||
|
||||
|
||||
def _migrate_text_id_to_integer(conn):
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
CREATE TABLE accounts_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
platform TEXT NOT NULL,
|
||||
phone TEXT,
|
||||
profile_dir TEXT,
|
||||
url TEXT,
|
||||
login_status INTEGER DEFAULT 0,
|
||||
last_login_at TEXT,
|
||||
extra_json TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
cur.execute(
|
||||
"SELECT name, platform, phone, profile_dir, login_status, last_login_at, extra_json, created_at, updated_at FROM accounts ORDER BY rowid"
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
for row in rows:
|
||||
name, platform, phone, profile_dir, login_status, last_login_at, extra_json, created_at, updated_at = row
|
||||
plat = (platform or "").strip().lower()
|
||||
url = PLATFORM_URLS.get(plat, "")
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO accounts_new
|
||||
(name, platform, phone, profile_dir, url, login_status, last_login_at, extra_json, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
name,
|
||||
plat,
|
||||
phone or "",
|
||||
profile_dir or "",
|
||||
url,
|
||||
int(login_status or 0),
|
||||
last_login_at,
|
||||
extra_json,
|
||||
created_at,
|
||||
updated_at,
|
||||
),
|
||||
)
|
||||
cur.execute("DROP TABLE accounts")
|
||||
cur.execute("ALTER TABLE accounts_new RENAME TO accounts")
|
||||
|
||||
|
||||
def init_db():
|
||||
conn = get_conn()
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'")
|
||||
if not cur.fetchone():
|
||||
cur.execute(
|
||||
"""
|
||||
CREATE TABLE accounts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
platform TEXT NOT NULL,
|
||||
phone TEXT,
|
||||
profile_dir TEXT,
|
||||
url TEXT,
|
||||
login_status INTEGER DEFAULT 0,
|
||||
last_login_at TEXT,
|
||||
extra_json TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
else:
|
||||
if _has_text_primary_id(cur):
|
||||
_migrate_text_id_to_integer(conn)
|
||||
cur = conn.cursor()
|
||||
_ensure_column(cur, "accounts", "url", "TEXT")
|
||||
cur.execute(
|
||||
"SELECT id, platform FROM accounts WHERE url IS NULL OR TRIM(COALESCE(url,'')) = ''"
|
||||
)
|
||||
for rid, plat in cur.fetchall():
|
||||
pu = PLATFORM_URLS.get((plat or "").strip().lower(), "")
|
||||
if pu:
|
||||
cur.execute("UPDATE accounts SET url = ? WHERE id = ?", (pu, rid))
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _normalize_account_id(account_id):
|
||||
if account_id is None:
|
||||
return None
|
||||
s = str(account_id).strip()
|
||||
if s.isdigit():
|
||||
return int(s)
|
||||
return s
|
||||
|
||||
|
||||
def get_account_by_id(account_id):
|
||||
init_db()
|
||||
aid = _normalize_account_id(account_id)
|
||||
conn = get_conn()
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"SELECT id, name, platform, phone, profile_dir, login_status, last_login_at, extra_json FROM accounts WHERE id = ?",
|
||||
(account_id,),
|
||||
"SELECT id, name, platform, phone, profile_dir, url, login_status, last_login_at, extra_json FROM accounts WHERE id = ?",
|
||||
(aid,),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if not row:
|
||||
@@ -114,12 +209,13 @@ def get_account_by_id(account_id):
|
||||
"platform": row[2],
|
||||
"phone": row[3] or "",
|
||||
"profile_dir": row[4] or get_default_profile_dir(row[0]),
|
||||
"login_status": int(row[5] or 0),
|
||||
"last_login_at": row[6],
|
||||
"url": row[5] or PLATFORM_URLS.get(row[2], ""),
|
||||
"login_status": int(row[6] or 0),
|
||||
"last_login_at": row[7],
|
||||
}
|
||||
if row[7]:
|
||||
if row[8]:
|
||||
try:
|
||||
extra = json.loads(row[7])
|
||||
extra = json.loads(row[8])
|
||||
if isinstance(extra, dict):
|
||||
acc.update(extra)
|
||||
except Exception:
|
||||
@@ -128,14 +224,17 @@ def get_account_by_id(account_id):
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _default_name_for_platform(platform: str, index: int) -> str:
|
||||
cn = _PLATFORM_NAME_CN.get(platform)
|
||||
if cn:
|
||||
return f"{cn}{index}号"
|
||||
return f"{platform}_{index}"
|
||||
|
||||
|
||||
def cmd_list(platform="all"):
|
||||
"""
|
||||
修改后的 list 功能,能直观打出带着【手机号】的详细台账供AI查询匹配!
|
||||
支持查全部(all),或指定(sohu)
|
||||
"""
|
||||
init_db()
|
||||
conn = get_conn()
|
||||
found = False
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
if platform == "all":
|
||||
@@ -159,7 +258,8 @@ def cmd_list(platform="all"):
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
print(f"ERROR:NO_ACCOUNTS_FOUND")
|
||||
print("ERROR:NO_ACCOUNTS_FOUND")
|
||||
|
||||
|
||||
def cmd_get(account_id):
|
||||
acc = get_account_by_id(account_id)
|
||||
@@ -169,133 +269,272 @@ def cmd_get(account_id):
|
||||
print("ERROR:ACCOUNT_NOT_FOUND")
|
||||
|
||||
|
||||
def cmd_add(account_id, name, platform, phone="", profile_dir=""):
|
||||
def cmd_add(platform: str, phone: str = ""):
|
||||
"""添加账号:仅平台 + 可选手机号;ID 自增,名称按平台序号自动生成。"""
|
||||
init_db()
|
||||
platform = (platform or "").strip().lower()
|
||||
if platform not in PLATFORM_URLS:
|
||||
print(f"ERROR:INVALID_PLATFORM (支持: {', '.join(sorted(PLATFORM_URLS.keys()))})")
|
||||
return
|
||||
|
||||
account_id = (account_id or "").strip()
|
||||
name = (name or "").strip() or account_id
|
||||
if not account_id:
|
||||
print("ERROR:INVALID_ACCOUNT_ID")
|
||||
return
|
||||
|
||||
profile_dir = (profile_dir or "").strip() or get_default_profile_dir(account_id)
|
||||
os.makedirs(profile_dir, exist_ok=True)
|
||||
phone = (phone or "").strip()
|
||||
url = PLATFORM_URLS[platform]
|
||||
now = datetime.now().isoformat(timespec="seconds")
|
||||
|
||||
conn = get_conn()
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT COUNT(*) FROM accounts WHERE platform = ?", (platform,))
|
||||
next_idx = cur.fetchone()[0] + 1
|
||||
name = _default_name_for_platform(platform, next_idx)
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO accounts (id, name, platform, phone, profile_dir, login_status, last_login_at, extra_json, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, 0, NULL, NULL, ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
platform = excluded.platform,
|
||||
phone = excluded.phone,
|
||||
profile_dir = excluded.profile_dir,
|
||||
updated_at = excluded.updated_at
|
||||
INSERT INTO accounts (name, platform, phone, profile_dir, url, login_status, last_login_at, extra_json, created_at, updated_at)
|
||||
VALUES (?, ?, ?, '', ?, 0, NULL, NULL, ?, ?)
|
||||
""",
|
||||
(account_id, name, platform, phone, profile_dir, now, now),
|
||||
(name, platform, phone, url, now, now),
|
||||
)
|
||||
new_id = cur.lastrowid
|
||||
profile_dir = get_default_profile_dir(new_id)
|
||||
cur.execute(
|
||||
"UPDATE accounts SET profile_dir = ?, updated_at = ? WHERE id = ?",
|
||||
(profile_dir, now, new_id),
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
print(f"✅ 已保存账号:{account_id}({platform})")
|
||||
print("ℹ️ 请继续执行登录:python account.py login <账号ID>")
|
||||
print(f"✅ 已保存账号:ID {new_id} | {name} | {platform}")
|
||||
print(f"ℹ️ 登录请执行:python account.py login {new_id}")
|
||||
|
||||
|
||||
def _win_find_exe(candidates):
|
||||
for p in candidates:
|
||||
if p and os.path.isfile(p):
|
||||
return p
|
||||
return None
|
||||
|
||||
|
||||
def resolve_chromium_channel():
|
||||
"""
|
||||
Playwright channel: 'chrome' | 'msedge' | None
|
||||
Windows 优先 Chrome,其次 Edge;其它系统默认 chrome(由 Playwright 解析 PATH)。
|
||||
"""
|
||||
if sys.platform != "win32":
|
||||
return "chrome"
|
||||
|
||||
pf = os.environ.get("ProgramFiles", r"C:\Program Files")
|
||||
pfx86 = os.environ.get("ProgramFiles(x86)", r"C:\Program Files (x86)")
|
||||
local = os.environ.get("LocalAppData", "")
|
||||
|
||||
chrome = _win_find_exe(
|
||||
[
|
||||
os.path.join(pf, "Google", "Chrome", "Application", "chrome.exe"),
|
||||
os.path.join(pfx86, "Google", "Chrome", "Application", "chrome.exe"),
|
||||
os.path.join(local, "Google", "Chrome", "Application", "chrome.exe") if local else "",
|
||||
]
|
||||
)
|
||||
if chrome:
|
||||
return "chrome"
|
||||
|
||||
edge = _win_find_exe(
|
||||
[
|
||||
os.path.join(pfx86, "Microsoft", "Edge", "Application", "msedge.exe"),
|
||||
os.path.join(pf, "Microsoft", "Edge", "Application", "msedge.exe"),
|
||||
os.path.join(local, "Microsoft", "Edge", "Application", "msedge.exe") if local else "",
|
||||
]
|
||||
)
|
||||
if edge:
|
||||
return "msedge"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _print_browser_install_hint():
|
||||
print("❌ 未检测到 Google Chrome 或 Microsoft Edge,请先安装后再登录:")
|
||||
print(" • Chrome: https://www.google.com/chrome/")
|
||||
print(" • Edge: https://www.microsoft.com/zh-cn/edge")
|
||||
|
||||
|
||||
def _login_timeout_seconds():
|
||||
try:
|
||||
return max(60, int((os.getenv("JIANGCHANG_LOGIN_TIMEOUT_SECONDS") or "300").strip()))
|
||||
except ValueError:
|
||||
return 300
|
||||
|
||||
|
||||
def _verify_login(profile_dir: str, platform: str, start_url: str, channel: str) -> bool:
|
||||
try:
|
||||
from playwright.sync_api import sync_playwright
|
||||
except ImportError:
|
||||
print("⚠️ 未安装 playwright,跳过登录校验(请: pip install playwright && playwright install)")
|
||||
return False
|
||||
|
||||
try:
|
||||
with sync_playwright() as p:
|
||||
ctx = p.chromium.launch_persistent_context(
|
||||
user_data_dir=profile_dir,
|
||||
headless=True,
|
||||
channel=channel,
|
||||
args=["--disable-blink-features=AutomationControlled"],
|
||||
)
|
||||
try:
|
||||
page = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
page.goto(start_url, wait_until="domcontentloaded", timeout=45000)
|
||||
time.sleep(2)
|
||||
u = (page.url or "").lower()
|
||||
if platform == "sohu":
|
||||
if "passport" in u or "/login" in u:
|
||||
return False
|
||||
return True
|
||||
if "login" in u or "signin" in u or "passport" in u:
|
||||
return False
|
||||
return True
|
||||
finally:
|
||||
ctx.close()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def cmd_login(account_id):
|
||||
"""首次登录:打开浏览器,用户手动登录,登录态自动保存到Profile目录"""
|
||||
target = get_account_by_id(account_id)
|
||||
|
||||
if not target:
|
||||
print("ERROR:ACCOUNT_NOT_FOUND")
|
||||
return
|
||||
|
||||
channel = resolve_chromium_channel()
|
||||
if not channel:
|
||||
_print_browser_install_hint()
|
||||
return
|
||||
|
||||
try:
|
||||
import playwright # noqa: F401
|
||||
except ImportError:
|
||||
print("ERROR:需要 playwright:pip install playwright && playwright install chromium")
|
||||
return
|
||||
|
||||
profile_dir = target["profile_dir"]
|
||||
os.makedirs(profile_dir, exist_ok=True)
|
||||
url = PLATFORM_URLS.get(target["platform"], "https://www.google.com")
|
||||
url = (target.get("url") or "").strip() or PLATFORM_URLS.get(target["platform"], "https://www.google.com")
|
||||
platform = (target.get("platform") or "").strip().lower()
|
||||
timeout_sec = _login_timeout_seconds()
|
||||
|
||||
print(f"正在为账号 [{target['name']}] 打开浏览器...")
|
||||
print(f"请在浏览器中完成登录,登录后直接关闭浏览器窗口即可。")
|
||||
print(f"登录态将自动保存到:{profile_dir}")
|
||||
browser_name = "Google Chrome" if channel == "chrome" else "Microsoft Edge"
|
||||
print(f"正在为账号 [{target['name']}] 打开 {browser_name} …")
|
||||
print(f"访问地址:{url}")
|
||||
print("请在窗口内完成登录(扫码/输入密码)。到达等待时间后将自动关闭窗口,您也可提前手动关闭。")
|
||||
print(f"最长等待:{timeout_sec} 秒")
|
||||
|
||||
# 用Playwright打开持久化浏览器,等待用户手动登录
|
||||
login_script = f"""
|
||||
import asyncio
|
||||
from playwright.async_api import async_playwright
|
||||
async def main():
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch_persistent_context(
|
||||
user_data_dir=r"{profile_dir}",
|
||||
headless=False,
|
||||
channel="chrome", # 【修改处】也加上这行
|
||||
no_viewport=True, # 确保浏览器内容视口也最大化
|
||||
args=["--start-maximized"]
|
||||
)
|
||||
page = browser.pages[0] if browser.pages else await browser.new_page()
|
||||
await page.goto("{url}")
|
||||
print("浏览器已打开,请完成登录后关闭窗口...")
|
||||
# 移除自动超时,无限等待直到用户扫码完手动关闭
|
||||
await browser.wait_for_event("close", timeout=0)
|
||||
print("登录态已保存!")
|
||||
asyncio.run(main())
|
||||
# Playwright 同步 API 宜在独立子进程中跑浏览器,避免线程与事件循环问题
|
||||
runner = r"""import json, sys
|
||||
from playwright.sync_api import sync_playwright
|
||||
with open(sys.argv[1], encoding="utf-8") as f:
|
||||
c = json.load(f)
|
||||
with sync_playwright() as p:
|
||||
ctx = p.chromium.launch_persistent_context(
|
||||
user_data_dir=c["profile_dir"],
|
||||
headless=False,
|
||||
channel=c["channel"],
|
||||
no_viewport=True,
|
||||
args=["--start-maximized"],
|
||||
)
|
||||
try:
|
||||
page = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
page.goto(c["url"], wait_until="domcontentloaded", timeout=60000)
|
||||
try:
|
||||
ctx.wait_for_event("close", timeout=c["timeout_sec"] * 1000)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
ctx.close()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
raise
|
||||
"""
|
||||
import tempfile
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.py',
|
||||
delete=False, encoding='utf-8') as f:
|
||||
f.write(login_script)
|
||||
tmp_path = f.name
|
||||
cfg = {
|
||||
"channel": channel,
|
||||
"profile_dir": profile_dir,
|
||||
"url": url,
|
||||
"timeout_sec": timeout_sec,
|
||||
}
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w", suffix=".json", delete=False, encoding="utf-8"
|
||||
) as jf:
|
||||
json.dump(cfg, jf, ensure_ascii=False)
|
||||
cfg_path = jf.name
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w", suffix=".py", delete=False, encoding="utf-8"
|
||||
) as pf:
|
||||
pf.write(runner)
|
||||
py_path = pf.name
|
||||
try:
|
||||
r = subprocess.run(
|
||||
[sys.executable, py_path, cfg_path],
|
||||
timeout=timeout_sec + 180,
|
||||
)
|
||||
if r.returncode != 0:
|
||||
print("⚠️ 浏览器进程异常退出,仍将尝试检测登录状态")
|
||||
except subprocess.TimeoutExpired:
|
||||
print("⚠️ 等待浏览器超时,仍将尝试检测登录状态")
|
||||
finally:
|
||||
try:
|
||||
os.unlink(cfg_path)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
os.unlink(py_path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
exit_code = os.system(f'python "{tmp_path}"')
|
||||
os.unlink(tmp_path)
|
||||
if exit_code == 0:
|
||||
_mark_login_success(account_id)
|
||||
print("✅ 登录状态已标记为已登录")
|
||||
time.sleep(1.5)
|
||||
print("正在检测登录状态…")
|
||||
ok = _verify_login(profile_dir, platform, url, channel)
|
||||
_mark_login_status(target["id"], ok)
|
||||
if ok:
|
||||
print("✅ 检测到已登录,状态已写入数据库")
|
||||
else:
|
||||
print("⚠️ 浏览器登录流程异常退出,登录状态未更新")
|
||||
print("⚠️ 未检测到有效登录,状态为未登录。请关闭其他占用该用户目录的浏览器后重试,或延长 JIANGCHANG_LOGIN_TIMEOUT_SECONDS 后再登录。")
|
||||
|
||||
|
||||
def _mark_login_success(account_id):
|
||||
def _mark_login_status(account_id, success: bool):
|
||||
now = datetime.now().isoformat(timespec="seconds")
|
||||
conn = get_conn()
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"UPDATE accounts SET login_status = 1, last_login_at = ?, updated_at = ? WHERE id = ?",
|
||||
(now, now, account_id),
|
||||
)
|
||||
if success:
|
||||
cur.execute(
|
||||
"UPDATE accounts SET login_status = 1, last_login_at = ?, updated_at = ? WHERE id = ?",
|
||||
(now, now, account_id),
|
||||
)
|
||||
else:
|
||||
cur.execute(
|
||||
"UPDATE accounts SET login_status = 0, updated_at = ? WHERE id = ?",
|
||||
(now, account_id),
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("用法:python account.py add <id> <name> <platform> [phone] [profile_dir] | list [platform] | get <id> | login <id>")
|
||||
print(
|
||||
"用法:python account.py add <platform> [phone] | list [platform] | get <id> | login <id>"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
cmd = sys.argv[1]
|
||||
# 兼容写反顺序:python account.py sohu list
|
||||
if len(sys.argv) >= 3 and sys.argv[2] == "list" and cmd in PLATFORM_URLS:
|
||||
cmd_list(cmd)
|
||||
elif cmd in PLATFORM_URLS and len(sys.argv) == 2:
|
||||
cmd_list(cmd)
|
||||
elif cmd == "add" and len(sys.argv) >= 5:
|
||||
cmd_add(
|
||||
sys.argv[2],
|
||||
sys.argv[3],
|
||||
sys.argv[4],
|
||||
sys.argv[5] if len(sys.argv) >= 6 else "",
|
||||
sys.argv[6] if len(sys.argv) >= 7 else "",
|
||||
)
|
||||
elif cmd == "add" and len(sys.argv) >= 3:
|
||||
plat = sys.argv[2].lower()
|
||||
phone_arg = sys.argv[3] if len(sys.argv) >= 4 else ""
|
||||
cmd_add(plat, phone_arg)
|
||||
elif cmd == "list":
|
||||
# 默认不传 platform 时打出 all
|
||||
cmd_list(sys.argv[2] if len(sys.argv) >= 3 else "all")
|
||||
elif cmd == "get" and len(sys.argv) >= 3:
|
||||
cmd_get(sys.argv[2])
|
||||
@@ -303,4 +542,4 @@ if __name__ == "__main__":
|
||||
cmd_login(sys.argv[2])
|
||||
else:
|
||||
print("参数错误")
|
||||
sys.exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user