Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions wechat_cli/commands/login_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import click

from ..core.login_status import detect_login_status
from ..output.formatter import output


_EXIT_CODE = {
"logged_in": 0,
"not_running": 10,
"logged_out": 11,
"unknown": 13,
}

_TEXT = {
"logged_in": "已登录",
"not_running": "未运行",
"logged_out": "未登录",
"unknown": "未知",
}


@click.command("login-status")
@click.option("--format", "fmt", default="json", type=click.Choice(["json", "text"]), help="输出格式")
@click.pass_context
def login_status(ctx, fmt):
"""检测本机微信是否已登录"""
status = detect_login_status()
if status not in _EXIT_CODE:
status = "unknown"

if fmt == "json":
output({"status": status}, "json")
else:
output(_TEXT.get(status, "未知"), "text")

ctx.exit(_EXIT_CODE[status])

93 changes: 93 additions & 0 deletions wechat_cli/core/login_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import ctypes
import ctypes.wintypes as wt
import platform
import subprocess


_TRAY_TITLE = "WxTrayIconMessageWindow"
_TRAY_CLASS_MARK = "WxTrayIconMessageWindowClass"
_DEFAULT_PROCESS_WINDOWS = "Weixin.exe"


def detect_login_status():
system = platform.system().lower()
if system != "windows":
return "unknown"

try:
running = _process_exists_windows(_DEFAULT_PROCESS_WINDOWS)
except Exception:
return "unknown"

if not running:
return "not_running"

try:
if _has_tray_window_windows():
return "logged_in"
return "logged_out"
except Exception:
return "unknown"


def _process_exists_windows(image_name):
r = subprocess.run(
["tasklist", "/FI", f"IMAGENAME eq {image_name}", "/FO", "CSV", "/NH"],
capture_output=True,
text=True,
)
out = (r.stdout or "").strip()
if not out:
return False
lower = out.lower()
if "no tasks are running" in lower or "信息:" in out:
return False
for line in out.splitlines():
line = line.strip()
if not line:
continue
parts = line.strip('"').split('","')
if parts and parts[0].lower() == image_name.lower():
return True
return False


def _has_tray_window_windows():
user32 = ctypes.windll.user32

EnumWindows = user32.EnumWindows
EnumWindows.argtypes = [ctypes.WINFUNCTYPE(wt.BOOL, wt.HWND, wt.LPARAM), wt.LPARAM]
EnumWindows.restype = wt.BOOL

GetClassNameW = user32.GetClassNameW
GetClassNameW.argtypes = [wt.HWND, wt.LPWSTR, ctypes.c_int]
GetClassNameW.restype = ctypes.c_int

GetWindowTextW = user32.GetWindowTextW
GetWindowTextW.argtypes = [wt.HWND, wt.LPWSTR, ctypes.c_int]
GetWindowTextW.restype = ctypes.c_int

found = {"ok": False}

@ctypes.WINFUNCTYPE(wt.BOOL, wt.HWND, wt.LPARAM)
def _cb(hwnd, lparam):
class_buf = ctypes.create_unicode_buffer(256)
title_buf = ctypes.create_unicode_buffer(256)

GetClassNameW(hwnd, class_buf, len(class_buf))
GetWindowTextW(hwnd, title_buf, len(title_buf))

title = title_buf.value or ""
if title != _TRAY_TITLE:
return True

cls = class_buf.value or ""
if _TRAY_CLASS_MARK in cls:
found["ok"] = True
return False

return True

EnumWindows(_cb, 0)
return bool(found["ok"])

6 changes: 4 additions & 2 deletions wechat_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ def cli(ctx, config_path):
wechat-cli contacts --query "李" # 搜索联系人
wechat-cli new-messages # 获取增量新消息
"""
# init/version 命令不需要 AppContext
if ctx.invoked_subcommand in ("init", "version"):
# init/version/login-status 命令不需要 AppContext
if ctx.invoked_subcommand in ("init", "version", "login-status"):
return

try:
Expand All @@ -55,6 +55,7 @@ def cli(ctx, config_path):
from .commands.stats import stats
from .commands.unread import unread
from .commands.favorites import favorites
from .commands.login_status import login_status

cli.add_command(init)
cli.add_command(sessions)
Expand All @@ -67,6 +68,7 @@ def cli(ctx, config_path):
cli.add_command(stats)
cli.add_command(unread)
cli.add_command(favorites)
cli.add_command(login_status)


if __name__ == "__main__":
Expand Down