wocli 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wocli-0.2.0/LICENSE +21 -0
- wocli-0.2.0/PKG-INFO +16 -0
- wocli-0.2.0/README.md +0 -0
- wocli-0.2.0/pyproject.toml +28 -0
- wocli-0.2.0/setup.cfg +4 -0
- wocli-0.2.0/src/wocli/__init__.py +3 -0
- wocli-0.2.0/src/wocli/commands/__init__.py +1 -0
- wocli-0.2.0/src/wocli/commands/badapple.py +51 -0
- wocli-0.2.0/src/wocli/commands/battery.py +124 -0
- wocli-0.2.0/src/wocli/commands/cow.py +58 -0
- wocli-0.2.0/src/wocli/commands/dead.py +41 -0
- wocli-0.2.0/src/wocli/commands/dino.py +229 -0
- wocli-0.2.0/src/wocli/commands/glitch.py +52 -0
- wocli-0.2.0/src/wocli/commands/goodbye.py +40 -0
- wocli-0.2.0/src/wocli/commands/gpa.py +84 -0
- wocli-0.2.0/src/wocli/commands/hash_cmd.py +41 -0
- wocli-0.2.0/src/wocli/commands/ip.py +127 -0
- wocli-0.2.0/src/wocli/commands/lolcat.py +31 -0
- wocli-0.2.0/src/wocli/commands/luck.py +38 -0
- wocli-0.2.0/src/wocli/commands/matrix.py +77 -0
- wocli-0.2.0/src/wocli/commands/path_cmd.py +37 -0
- wocli-0.2.0/src/wocli/commands/port.py +101 -0
- wocli-0.2.0/src/wocli/commands/progress.py +68 -0
- wocli-0.2.0/src/wocli/commands/qr_cmd.py +50 -0
- wocli-0.2.0/src/wocli/commands/regex_cmd.py +39 -0
- wocli-0.2.0/src/wocli/commands/sys_cmd.py +157 -0
- wocli-0.2.0/src/wocli/commands/time_cmd.py +57 -0
- wocli-0.2.0/src/wocli/commands/tree.py +60 -0
- wocli-0.2.0/src/wocli/commands/typing_cmd.py +69 -0
- wocli-0.2.0/src/wocli/commands/wifi_cmd.py +117 -0
- wocli-0.2.0/src/wocli/main.py +129 -0
- wocli-0.2.0/src/wocli.egg-info/PKG-INFO +16 -0
- wocli-0.2.0/src/wocli.egg-info/SOURCES.txt +35 -0
- wocli-0.2.0/src/wocli.egg-info/dependency_links.txt +1 -0
- wocli-0.2.0/src/wocli.egg-info/entry_points.txt +2 -0
- wocli-0.2.0/src/wocli.egg-info/requires.txt +3 -0
- wocli-0.2.0/src/wocli.egg-info/top_level.txt +1 -0
wocli-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wojusensei
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
wocli-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wocli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: 一个为发电而生的何意味终端小工具
|
|
5
|
+
Author: Wojusensei
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: qrcode>=7.4
|
|
14
|
+
Requires-Dist: Pillow>=10.0
|
|
15
|
+
Requires-Dist: requests>=2.31
|
|
16
|
+
Dynamic: license-file
|
wocli-0.2.0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "wocli"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "一个为发电而生的何意味终端小工具"
|
|
9
|
+
authors = [{name = "Wojusensei"}]
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"qrcode>=7.4",
|
|
20
|
+
"Pillow>=10.0",
|
|
21
|
+
"requests>=2.31",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
wocli = "wocli.main:main"
|
|
26
|
+
|
|
27
|
+
[tool.setuptools.package-dir]
|
|
28
|
+
"" = "src"
|
wocli-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""wocli command modules."""
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""wocli badapple - 播放 Bad Apple!! ASCII 动画"""
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
C_WHITE = "\033[97m"
|
|
7
|
+
C_RESET = "\033[0m"
|
|
8
|
+
|
|
9
|
+
FRAME_FILE = os.path.join(os.path.dirname(__file__), "frames.txt")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def load_frames():
|
|
13
|
+
"""加载帧序列,返回帧列表"""
|
|
14
|
+
if not os.path.exists(FRAME_FILE):
|
|
15
|
+
print("\n frames.txt 未找到,请确保下载了 Bad Apple ASCII 帧序列并放到 commands/ 目录。")
|
|
16
|
+
print(" 下载地址: https://raw.githubusercontent.com/kevinjycui/bad-apple/main/frames.txt\n")
|
|
17
|
+
return None
|
|
18
|
+
with open(FRAME_FILE, "r", encoding="utf-8") as f:
|
|
19
|
+
data = f.read()
|
|
20
|
+
# 帧之间用 SPLIT 分隔,每一帧是一段文本
|
|
21
|
+
frames = data.split("\nSPLIT\n")
|
|
22
|
+
return frames
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def run():
|
|
26
|
+
print("\n [ Bad Apple!! ]")
|
|
27
|
+
print(" 正在加载帧序列...")
|
|
28
|
+
frames = load_frames()
|
|
29
|
+
if not frames:
|
|
30
|
+
return
|
|
31
|
+
print(f" 共 {len(frames)} 帧,按 Enter 开始播放...")
|
|
32
|
+
input()
|
|
33
|
+
|
|
34
|
+
# 隐藏光标
|
|
35
|
+
sys.stdout.write("\033[?25l")
|
|
36
|
+
sys.stdout.flush()
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
for frame in frames:
|
|
40
|
+
# 将光标移到终端左上角(不清屏,直接覆盖)
|
|
41
|
+
sys.stdout.write("\033[H")
|
|
42
|
+
sys.stdout.write(C_WHITE + frame + C_RESET)
|
|
43
|
+
sys.stdout.flush()
|
|
44
|
+
time.sleep(1 / 30) # 30fps,可根据机器性能调整
|
|
45
|
+
except KeyboardInterrupt:
|
|
46
|
+
pass
|
|
47
|
+
finally:
|
|
48
|
+
sys.stdout.write("\033[?25h") # 恢复光标
|
|
49
|
+
sys.stdout.write(C_RESET)
|
|
50
|
+
sys.stdout.flush()
|
|
51
|
+
print("\n 播放完毕。")
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""wocli battery - 电池信息与健康度打分"""
|
|
2
|
+
import subprocess
|
|
3
|
+
import platform
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_battery_macos():
|
|
8
|
+
try:
|
|
9
|
+
result = subprocess.run(["pmset", "-g", "batt"], capture_output=True, text=True)
|
|
10
|
+
output = result.stdout
|
|
11
|
+
# 电量
|
|
12
|
+
pct_match = re.search(r"(\d{1,3})%", output)
|
|
13
|
+
percent = int(pct_match.group(1)) if pct_match else -1
|
|
14
|
+
# 充电状态
|
|
15
|
+
status = "未知"
|
|
16
|
+
if "charging" in output:
|
|
17
|
+
status = "充电中"
|
|
18
|
+
elif "discharging" in output:
|
|
19
|
+
status = "使用中"
|
|
20
|
+
elif "charged" in output:
|
|
21
|
+
status = "已充满"
|
|
22
|
+
# 健康度和循环次数
|
|
23
|
+
try:
|
|
24
|
+
health = subprocess.run(
|
|
25
|
+
["system_profiler", "SPPowerDataType"],
|
|
26
|
+
capture_output=True, text=True
|
|
27
|
+
)
|
|
28
|
+
cycles_match = re.search(r"Cycle Count:\s*(\d+)", health.stdout)
|
|
29
|
+
cycles = int(cycles_match.group(1)) if cycles_match else -1
|
|
30
|
+
max_match = re.search(r"Maximum Capacity:\s*(\d+)%", health.stdout)
|
|
31
|
+
max_cap = int(max_match.group(1)) if max_match else 100
|
|
32
|
+
except:
|
|
33
|
+
cycles = -1
|
|
34
|
+
max_cap = 100
|
|
35
|
+
return percent, status, cycles, max_cap
|
|
36
|
+
except:
|
|
37
|
+
return -1, "未知", -1, 100
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_battery_windows():
|
|
41
|
+
try:
|
|
42
|
+
result = subprocess.run(
|
|
43
|
+
["wmic", "path", "Win32_Battery", "get", "EstimatedChargeRemaining,EstimatedRunTime,BatteryStatus"],
|
|
44
|
+
capture_output=True, text=True
|
|
45
|
+
)
|
|
46
|
+
lines = [l.strip() for l in result.stdout.split("\n") if l.strip()]
|
|
47
|
+
if len(lines) < 2:
|
|
48
|
+
return -1, "未知", -1, 100
|
|
49
|
+
parts = lines[1].split()
|
|
50
|
+
percent = int(parts[0]) if parts else -1
|
|
51
|
+
status_map = {"1": "使用中", "2": "充电中", "3": "已充满"}
|
|
52
|
+
status = status_map.get(parts[1], "未知") if len(parts) > 1 else "未知"
|
|
53
|
+
return percent, status, -1, 100
|
|
54
|
+
except:
|
|
55
|
+
return -1, "未知", -1, 100
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_battery_linux():
|
|
59
|
+
try:
|
|
60
|
+
with open("/sys/class/power_supply/BAT0/capacity", "r") as f:
|
|
61
|
+
percent = int(f.read().strip())
|
|
62
|
+
with open("/sys/class/power_supply/BAT0/status", "r") as f:
|
|
63
|
+
raw = f.read().strip()
|
|
64
|
+
status_map = {"Discharging": "使用中", "Charging": "充电中", "Full": "已充满"}
|
|
65
|
+
status = status_map.get(raw, raw)
|
|
66
|
+
return percent, status, -1, 100
|
|
67
|
+
except:
|
|
68
|
+
return -1, "未知", -1, 100
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def score_battery(percent, cycles, max_cap):
|
|
72
|
+
if percent < 0:
|
|
73
|
+
return 0, "无数据"
|
|
74
|
+
charge_score = percent
|
|
75
|
+
if cycles > 0:
|
|
76
|
+
cycle_score = max(0, 100 - cycles * 0.1)
|
|
77
|
+
else:
|
|
78
|
+
cycle_score = 100
|
|
79
|
+
health_score = max_cap
|
|
80
|
+
total = charge_score * 0.3 + cycle_score * 0.3 + health_score * 0.4
|
|
81
|
+
total = min(100, total)
|
|
82
|
+
|
|
83
|
+
if total >= 95:
|
|
84
|
+
grade = "S级 · 古希腊掌管电池的神"
|
|
85
|
+
elif total >= 85:
|
|
86
|
+
grade = "A级 · 还能战三年"
|
|
87
|
+
elif total >= 70:
|
|
88
|
+
grade = "B级 · 电池有点虚了"
|
|
89
|
+
elif total >= 50:
|
|
90
|
+
grade = "C级 · 已经离不开充电器"
|
|
91
|
+
else:
|
|
92
|
+
grade = "D级 · 电池这么老,零百加速只要三十分钟吧"
|
|
93
|
+
|
|
94
|
+
return round(total, 2), grade
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def run():
|
|
98
|
+
system = platform.system()
|
|
99
|
+
if system == "Darwin":
|
|
100
|
+
percent, status, cycles, max_cap = get_battery_macos()
|
|
101
|
+
elif system == "Windows":
|
|
102
|
+
percent, status, cycles, max_cap = get_battery_windows()
|
|
103
|
+
else:
|
|
104
|
+
percent, status, cycles, max_cap = get_battery_linux()
|
|
105
|
+
|
|
106
|
+
if percent < 0:
|
|
107
|
+
print("\n 无法获取电池信息。\n")
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
score_val, grade = score_battery(percent, cycles, max_cap)
|
|
111
|
+
bar_len = 20
|
|
112
|
+
filled = int(bar_len * percent / 100)
|
|
113
|
+
bar = "=" * filled + "-" * (bar_len - filled)
|
|
114
|
+
|
|
115
|
+
print()
|
|
116
|
+
print(f" 当前电量:[{bar}] {percent}%")
|
|
117
|
+
print(f" 充电状态:{status}")
|
|
118
|
+
if cycles >= 0:
|
|
119
|
+
print(f" 循环次数:{cycles} 次")
|
|
120
|
+
if max_cap < 100:
|
|
121
|
+
print(f" 最大容量:{max_cap}%")
|
|
122
|
+
print(f" 健康评分:{score_val:.2f} / 100.00")
|
|
123
|
+
print(f" 评级:{grade}")
|
|
124
|
+
print()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""wocli cow - ASCII牛说你说的话."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
COW = r"""
|
|
7
|
+
\ ^__^
|
|
8
|
+
\ (oo)\_______
|
|
9
|
+
(__)\ )\/\
|
|
10
|
+
||----w |
|
|
11
|
+
|| ||
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def make_bubble(text):
|
|
16
|
+
"""生成对话气泡."""
|
|
17
|
+
lines = []
|
|
18
|
+
words = text.split()
|
|
19
|
+
line = ""
|
|
20
|
+
for word in words:
|
|
21
|
+
if len(line) + len(word) + 1 <= 36:
|
|
22
|
+
line += (" " + word) if line else word
|
|
23
|
+
else:
|
|
24
|
+
lines.append(line)
|
|
25
|
+
line = word
|
|
26
|
+
if line:
|
|
27
|
+
lines.append(line)
|
|
28
|
+
|
|
29
|
+
max_len = max(len(l) for l in lines) if lines else 0
|
|
30
|
+
top = " " + "_" * (max_len + 2)
|
|
31
|
+
bottom = " " + "-" * (max_len + 2)
|
|
32
|
+
|
|
33
|
+
bubble = [top]
|
|
34
|
+
if len(lines) == 1:
|
|
35
|
+
bubble.append(f" < {lines[0]} >")
|
|
36
|
+
else:
|
|
37
|
+
for i, l in enumerate(lines):
|
|
38
|
+
if i == 0:
|
|
39
|
+
bubble.append(f" / {l:<{max_len}} \\")
|
|
40
|
+
elif i == len(lines) - 1:
|
|
41
|
+
bubble.append(f" \\ {l:<{max_len}} /")
|
|
42
|
+
else:
|
|
43
|
+
bubble.append(f" | {l:<{max_len}} |")
|
|
44
|
+
bubble.append(bottom)
|
|
45
|
+
return "\n".join(bubble)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def run():
|
|
49
|
+
"""运行 cow 命令."""
|
|
50
|
+
if len(sys.argv) > 1:
|
|
51
|
+
text = " ".join(sys.argv[1:])
|
|
52
|
+
else:
|
|
53
|
+
text = "哞————(无意味)"
|
|
54
|
+
|
|
55
|
+
print()
|
|
56
|
+
print(make_bubble(text))
|
|
57
|
+
print(COW)
|
|
58
|
+
print()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""wocli dead - 伪装终端死机"""
|
|
2
|
+
import sys
|
|
3
|
+
import time
|
|
4
|
+
import random
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def run():
|
|
8
|
+
print()
|
|
9
|
+
print(" !! WARNING: Kernel panic detected !!")
|
|
10
|
+
time.sleep(1)
|
|
11
|
+
|
|
12
|
+
# 花屏
|
|
13
|
+
chars = "!@#$%^&*()_+-=[]{}|;:',.<>?/~`"
|
|
14
|
+
colors = [31, 32, 33, 34, 35, 36, 91, 92, 93, 94, 95, 96]
|
|
15
|
+
for _ in range(20):
|
|
16
|
+
line = " " + "".join(random.choice(chars) for _ in range(60))
|
|
17
|
+
color = random.choice(colors)
|
|
18
|
+
sys.stdout.write(f"\033[{color}m{line}\033[0m\n")
|
|
19
|
+
sys.stdout.flush()
|
|
20
|
+
time.sleep(0.05)
|
|
21
|
+
|
|
22
|
+
time.sleep(0.5)
|
|
23
|
+
|
|
24
|
+
# 蓝屏
|
|
25
|
+
sys.stdout.write("\033[44m\033[37m")
|
|
26
|
+
sys.stdout.write("\033[2J\033[H")
|
|
27
|
+
print()
|
|
28
|
+
print(" *** SYSTEM ERROR ***")
|
|
29
|
+
print()
|
|
30
|
+
print(" A fatal exception has occurred at 0xDEADBEEF")
|
|
31
|
+
print(" The system has been halted.")
|
|
32
|
+
print()
|
|
33
|
+
print(" Attempting to recover...")
|
|
34
|
+
sys.stdout.flush()
|
|
35
|
+
|
|
36
|
+
time.sleep(3)
|
|
37
|
+
|
|
38
|
+
# 恢复
|
|
39
|
+
sys.stdout.write("\033[0m\033[2J\033[H")
|
|
40
|
+
sys.stdout.write(" Just kidding. Your terminal is fine.\n\n")
|
|
41
|
+
sys.stdout.flush()
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import sys, os, time, random, platform, signal
|
|
2
|
+
|
|
3
|
+
W, H = 70, 20
|
|
4
|
+
GROUND = H - 3
|
|
5
|
+
X0 = 10
|
|
6
|
+
C_ORANGE = "\033[38;5;214m"
|
|
7
|
+
C_GREEN = "\033[32m"
|
|
8
|
+
C_WHITE = "\033[97m"
|
|
9
|
+
C_RESET = "\033[0m"
|
|
10
|
+
|
|
11
|
+
CLAUDE_A = [" ▗ ▗ ", " ▘▘ ", " ▝▝ "]
|
|
12
|
+
CLAUDE_B = [" ▖ ▖ ", " ▝▝ ", " ▘▘ "]
|
|
13
|
+
|
|
14
|
+
# ---------- 跨平台终端控制 ----------
|
|
15
|
+
if platform.system() == "Windows":
|
|
16
|
+
import msvcrt
|
|
17
|
+
def setup_terminal(): pass
|
|
18
|
+
def restore_terminal(): pass
|
|
19
|
+
def get_key():
|
|
20
|
+
if msvcrt.kbhit():
|
|
21
|
+
ch = msvcrt.getch()
|
|
22
|
+
if ch == b'\xe0': return ''
|
|
23
|
+
try:
|
|
24
|
+
return ch.decode('utf-8', errors='ignore')
|
|
25
|
+
except:
|
|
26
|
+
return ''
|
|
27
|
+
return ''
|
|
28
|
+
else:
|
|
29
|
+
import termios, tty, fcntl
|
|
30
|
+
_old_term = None
|
|
31
|
+
_old_flags = None
|
|
32
|
+
_fd = sys.stdin.fileno()
|
|
33
|
+
|
|
34
|
+
def setup_terminal():
|
|
35
|
+
global _old_term, _old_flags
|
|
36
|
+
_old_term = termios.tcgetattr(_fd)
|
|
37
|
+
_old_flags = fcntl.fcntl(_fd, fcntl.F_GETFL)
|
|
38
|
+
tty.setraw(_fd)
|
|
39
|
+
fcntl.fcntl(_fd, fcntl.F_SETFL, _old_flags | os.O_NONBLOCK)
|
|
40
|
+
|
|
41
|
+
def restore_terminal():
|
|
42
|
+
if _old_term is not None:
|
|
43
|
+
termios.tcsetattr(_fd, termios.TCSADRAIN, _old_term)
|
|
44
|
+
if _old_flags is not None:
|
|
45
|
+
fcntl.fcntl(_fd, fcntl.F_SETFL, _old_flags)
|
|
46
|
+
|
|
47
|
+
def get_key():
|
|
48
|
+
try:
|
|
49
|
+
data = os.read(_fd, 1).decode('utf-8', errors='ignore')
|
|
50
|
+
return data
|
|
51
|
+
except BlockingIOError:
|
|
52
|
+
return ''
|
|
53
|
+
|
|
54
|
+
class Game:
|
|
55
|
+
def __init__(self):
|
|
56
|
+
self.score = 0
|
|
57
|
+
self.high = self._load_high()
|
|
58
|
+
self.ground_y = GROUND - len(CLAUDE_A)
|
|
59
|
+
self.y = float(self.ground_y)
|
|
60
|
+
self.vy = 0.0
|
|
61
|
+
self.objs = []
|
|
62
|
+
self.speed = 0.8
|
|
63
|
+
self.frame = 0
|
|
64
|
+
self.over = False
|
|
65
|
+
self._hide_cursor()
|
|
66
|
+
signal.signal(signal.SIGINT, self._stop)
|
|
67
|
+
signal.signal(signal.SIGTERM, self._stop)
|
|
68
|
+
|
|
69
|
+
def _stop(self, *args):
|
|
70
|
+
self.over = True
|
|
71
|
+
|
|
72
|
+
def _hide_cursor(self):
|
|
73
|
+
sys.stdout.write("\033[?25l")
|
|
74
|
+
sys.stdout.flush()
|
|
75
|
+
|
|
76
|
+
def _show_cursor(self):
|
|
77
|
+
sys.stdout.write("\033[?25h")
|
|
78
|
+
sys.stdout.flush()
|
|
79
|
+
|
|
80
|
+
def _load_high(self):
|
|
81
|
+
p = os.path.expanduser("~/.wocli_dino")
|
|
82
|
+
try:
|
|
83
|
+
with open(p) as f:
|
|
84
|
+
return int(f.read())
|
|
85
|
+
except:
|
|
86
|
+
return 0
|
|
87
|
+
|
|
88
|
+
def _save_high(self):
|
|
89
|
+
if self.score > self.high:
|
|
90
|
+
self.high = self.score
|
|
91
|
+
p = os.path.expanduser("~/.wocli_dino")
|
|
92
|
+
try:
|
|
93
|
+
with open(p, "w") as f:
|
|
94
|
+
f.write(str(self.score))
|
|
95
|
+
except:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
def jump(self):
|
|
99
|
+
if self.vy == 0 and self.y >= self.ground_y:
|
|
100
|
+
self.vy = -2.2
|
|
101
|
+
|
|
102
|
+
def update(self):
|
|
103
|
+
if self.over:
|
|
104
|
+
return
|
|
105
|
+
self.frame += 1
|
|
106
|
+
self.score += 1
|
|
107
|
+
if self.score % 600 == 0:
|
|
108
|
+
self.speed = min(2.0, self.speed * 1.05)
|
|
109
|
+
|
|
110
|
+
self.vy += 0.2
|
|
111
|
+
self.y += self.vy
|
|
112
|
+
if self.y >= self.ground_y:
|
|
113
|
+
self.y = self.ground_y
|
|
114
|
+
self.vy = 0
|
|
115
|
+
|
|
116
|
+
# 移除超出屏幕的障碍
|
|
117
|
+
self.objs = [o for o in self.objs if o[0] > -15]
|
|
118
|
+
for o in self.objs:
|
|
119
|
+
o[0] -= self.speed
|
|
120
|
+
|
|
121
|
+
# 生成障碍
|
|
122
|
+
if len(self.objs) < 3 and random.random() < 0.02:
|
|
123
|
+
last_x = max([o[0] for o in self.objs]) if self.objs else W
|
|
124
|
+
if last_x < W - 20:
|
|
125
|
+
if random.random() < 0.65:
|
|
126
|
+
h = random.randint(3, 4)
|
|
127
|
+
w = random.randint(5, 7)
|
|
128
|
+
cactus = []
|
|
129
|
+
for i in range(h):
|
|
130
|
+
line = ""
|
|
131
|
+
for j in range(w):
|
|
132
|
+
if i == 0 and j == w // 2: line += "┃"
|
|
133
|
+
elif i > 0 and (j == 0 or j == w - 1): line += "│"
|
|
134
|
+
elif i == h - 1: line += "━"
|
|
135
|
+
else: line += " " if random.random() < 0.7 else "╻"
|
|
136
|
+
cactus.append(line)
|
|
137
|
+
self.objs.append([W, w, h, GROUND - h, cactus, C_GREEN])
|
|
138
|
+
else:
|
|
139
|
+
bird_y = GROUND - random.randint(4, 7)
|
|
140
|
+
bird = [" ^^ ", " <^^> "]
|
|
141
|
+
self.objs.append([W, 6, 2, bird_y, bird, C_WHITE])
|
|
142
|
+
|
|
143
|
+
# 碰撞检测
|
|
144
|
+
claude_h = len(CLAUDE_A)
|
|
145
|
+
claude_w = max(len(r) for r in CLAUDE_A)
|
|
146
|
+
cx0, cy0 = X0, int(self.y)
|
|
147
|
+
for ox, ow, oh, oy, _, _ in self.objs:
|
|
148
|
+
if (cx0 + claude_w > ox and cx0 < ox + ow and
|
|
149
|
+
cy0 + claude_h > oy and cy0 < oy + oh):
|
|
150
|
+
self.over = True
|
|
151
|
+
self._save_high()
|
|
152
|
+
break
|
|
153
|
+
|
|
154
|
+
def render(self):
|
|
155
|
+
sys.stdout.write("\033[2J\033[H")
|
|
156
|
+
buf = [[" "] * W for _ in range(H)]
|
|
157
|
+
for x in range(W):
|
|
158
|
+
buf[GROUND][x] = "─"
|
|
159
|
+
|
|
160
|
+
frame_char = CLAUDE_A if (self.frame // 3) % 2 == 0 else CLAUDE_B
|
|
161
|
+
for dy, row in enumerate(frame_char):
|
|
162
|
+
y = int(self.y) + dy
|
|
163
|
+
if 0 <= y < H:
|
|
164
|
+
for dx, ch in enumerate(row):
|
|
165
|
+
x = X0 + dx
|
|
166
|
+
if 0 <= x < W and ch != " ":
|
|
167
|
+
buf[y][x] = f"{C_ORANGE}{ch}{C_RESET}"
|
|
168
|
+
|
|
169
|
+
for ox, ow, oh, oy, shape, col in self.objs:
|
|
170
|
+
for sy, row in enumerate(shape):
|
|
171
|
+
y = oy + sy
|
|
172
|
+
if 0 <= y < H:
|
|
173
|
+
for sx, ch in enumerate(row):
|
|
174
|
+
x = ox + sx
|
|
175
|
+
if 0 <= x < W and ch != " ":
|
|
176
|
+
buf[y][x] = f"{col}{ch}{C_RESET}"
|
|
177
|
+
|
|
178
|
+
for row in buf:
|
|
179
|
+
print(" " + "".join(row))
|
|
180
|
+
|
|
181
|
+
score_str = f"Score: {self.score:05d} High: {self.high:05d}"
|
|
182
|
+
hint = "Space:Jump Q:Quit" if not self.over else "GAME OVER R:Restart Q:Quit"
|
|
183
|
+
print(f" {C_ORANGE}{score_str}{C_RESET} {hint}")
|
|
184
|
+
|
|
185
|
+
def input_handle(self):
|
|
186
|
+
k = get_key()
|
|
187
|
+
while k:
|
|
188
|
+
if k == ' ':
|
|
189
|
+
self.jump()
|
|
190
|
+
elif k == 'q':
|
|
191
|
+
self.over = True
|
|
192
|
+
elif k == 'r' and self.over:
|
|
193
|
+
self._save_high()
|
|
194
|
+
self.__init__()
|
|
195
|
+
k = get_key()
|
|
196
|
+
|
|
197
|
+
def run(self):
|
|
198
|
+
try:
|
|
199
|
+
while not self.over:
|
|
200
|
+
self.input_handle()
|
|
201
|
+
self.update()
|
|
202
|
+
self.render()
|
|
203
|
+
time.sleep(0.04)
|
|
204
|
+
self.render()
|
|
205
|
+
time.sleep(1.5)
|
|
206
|
+
finally:
|
|
207
|
+
self._show_cursor()
|
|
208
|
+
self._save_high()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def main():
|
|
212
|
+
print(f"\n {C_ORANGE}╭─ Claude Run ──────────────────────────────╮{C_RESET}")
|
|
213
|
+
print(f" {C_ORANGE}│{C_RESET} 橙色小人跑酷,按任意键开始 {C_ORANGE}│{C_RESET}")
|
|
214
|
+
print(f" {C_ORANGE}╰──────────────────────────────────────────╯{C_RESET}")
|
|
215
|
+
|
|
216
|
+
setup_terminal()
|
|
217
|
+
try:
|
|
218
|
+
# 等待任意键
|
|
219
|
+
while True:
|
|
220
|
+
if get_key():
|
|
221
|
+
break
|
|
222
|
+
time.sleep(0.05)
|
|
223
|
+
g = Game()
|
|
224
|
+
g.run()
|
|
225
|
+
finally:
|
|
226
|
+
restore_terminal()
|
|
227
|
+
|
|
228
|
+
if __name__ == "__main__":
|
|
229
|
+
main()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""wocli glitch - 文字故障风效果."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import random
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def glitch_case(text):
|
|
8
|
+
return "".join(
|
|
9
|
+
c.upper() if random.random() > 0.5 else c.lower()
|
|
10
|
+
for c in text
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def glitch_leet(text):
|
|
15
|
+
mapping = {
|
|
16
|
+
"a": "4", "A": "4",
|
|
17
|
+
"e": "3", "E": "3",
|
|
18
|
+
"i": "1", "I": "1",
|
|
19
|
+
"o": "0", "O": "0",
|
|
20
|
+
"s": "5", "S": "5",
|
|
21
|
+
"t": "7", "T": "7",
|
|
22
|
+
"l": "1", "L": "1",
|
|
23
|
+
}
|
|
24
|
+
return "".join(mapping.get(c, c) for c in text)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def glitch_zalgo(text):
|
|
28
|
+
up = "".join(random.choice("''^`") for _ in range(len(text)))
|
|
29
|
+
down = "".join(random.choice(",.;:") for _ in range(len(text)))
|
|
30
|
+
return up + "\n" + text + "\n" + down
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def glitch_morse(text):
|
|
34
|
+
return " ".join(
|
|
35
|
+
"".join(random.choice([".", "-"]) for _ in range(random.randint(1, 4)))
|
|
36
|
+
for _ in text.split()
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run():
|
|
41
|
+
"""运行 glitch 命令."""
|
|
42
|
+
if len(sys.argv) > 1:
|
|
43
|
+
text = " ".join(sys.argv[1:])
|
|
44
|
+
else:
|
|
45
|
+
text = "Hello World"
|
|
46
|
+
|
|
47
|
+
print()
|
|
48
|
+
print(f" Original : {text}")
|
|
49
|
+
print(f" Glitch 1 : {glitch_case(text)}")
|
|
50
|
+
print(f" Glitch 2 : {glitch_leet(text)}")
|
|
51
|
+
print(f" Glitch 3 : {glitch_zalgo(text)}")
|
|
52
|
+
print()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""wocli goodbye - 花式退出动画."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
import random
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
MESSAGES = [
|
|
9
|
+
"明天又是充满 bug 的一天(目移)",
|
|
10
|
+
"记得 commit 和 push 哦",
|
|
11
|
+
"没人觉得 rust 很香吗",
|
|
12
|
+
"就当是为了我,对他使用force-push吧",
|
|
13
|
+
"Goodbye world!",
|
|
14
|
+
"本次编码结束,bug 数 +1。",
|
|
15
|
+
"代码能跑就行...",
|
|
16
|
+
"感谢使用awa",
|
|
17
|
+
"Ctrl+C 的人生不需要解释",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def run():
|
|
22
|
+
"""运行 goodbye 命令."""
|
|
23
|
+
msg = random.choice(MESSAGES)
|
|
24
|
+
|
|
25
|
+
print()
|
|
26
|
+
print(" System will shut down in 3 seconds...")
|
|
27
|
+
time.sleep(1)
|
|
28
|
+
|
|
29
|
+
width = 30
|
|
30
|
+
for i in range(width + 1):
|
|
31
|
+
bar = "=" * i + ">" + " " * (width - i)
|
|
32
|
+
pct = int(i / width * 100)
|
|
33
|
+
sys.stdout.write(f"\r [{bar}] {pct}%")
|
|
34
|
+
sys.stdout.flush()
|
|
35
|
+
time.sleep(0.08)
|
|
36
|
+
|
|
37
|
+
print()
|
|
38
|
+
print(f" {msg}")
|
|
39
|
+
print(" Bye!")
|
|
40
|
+
print()
|