1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python3
"""Append the Aether (PattNG) section to plain.html: PattNG latest download
(both arm64-v8a flavors with guidance) + 4-step WARP keys flow with his
4 screenshots embedded. Step 3 carries two screenshots in sequence."""
import base64, io, pathlib
from PIL import Image
SITE = pathlib.Path.home() / "workspace/foxyvpn-tutorial-site"
FILES = pathlib.Path.home() / "workspace/user/files"
html = (SITE / "plain.html").read_text(encoding="utf-8")
assert 'id="aether"' not in html, "aether section already present"
new_files = [
("1000045703_4_2cpz.jpg", "لیست کانفیگها در PattNG و علامت مداد کنار کانفیگ Aether"),
("1000045705_5_dw68.jpg", "صفحه ویرایش کانفیگ Aether و گزینه Get new WARP keys در بالا"),
("1000045707_6_f6d8.jpg", "دکمه آبی Get new WARP keys در پایین صفحه"),
("1000045709_7_ewq9.jpg", "ظاهر شدن متن سبز رنگ بعد از گرفتن کلیدهای جدید WARP"),
]
imgs = []
for fname, alt in new_files:
im = Image.open(FILES / fname).convert("RGB")
if im.width > 800:
im = im.resize((800, round(im.height * 800 / im.width)), Image.LANCZOS)
buf = io.BytesIO()
im.save(buf, "JPEG", quality=85, optimize=True)
imgs.append((f"data:image/jpeg;base64,{base64.b64encode(buf.getvalue()).decode()}", alt))
print(fname, "->", len(buf.getvalue()), "bytes")
P69 = "https://github.com/patterniha/PattNG/releases/download/2.3.13-P69"
URL_FDROID = f"{P69}/PattNG_fdroid_2.3.13-P69_arm64-v8a.apk"
URL_NORMAL = f"{P69}/PattNG_2.3.13-P69_arm64-v8a.apk"
SIZE = "۷۲٫۸ مگابایت"
def shot(i):
src, alt = imgs[i]
return f' <div class="shot"><img src="{src}" alt="{alt}"></div>'
def step(num, title, body):
return f""" <div class="step">
<div class="step-header">
<div class="step-number">{num}</div>
<div class="step-title">{title}</div>
</div>
<div class="step-content">
{body}
</div>
</div>
"""
css_extra = """
.part-divider { text-align: center; margin: 64px 0 36px; }
.part-divider .badge {
display: inline-block; background: rgba(124,156,255,0.15); color: var(--accent);
font-size: 0.85rem; font-weight: 500; padding: 6px 14px; border-radius: 999px; margin-bottom: 14px;
}
.part-divider h2 { font-size: 1.45rem; font-weight: 700; color: #fff; line-height: 1.4; margin-bottom: 8px; }
.part-divider p { color: var(--text-muted); font-size: 0.95rem; max-width: 520px; margin: 0 auto; }
.dl-block {
background: rgba(124,156,255,0.05); border: 1px solid var(--border);
border-radius: 12px; padding: 16px; margin-top: 14px;
}
.dl-name { font-weight: 700; color: #fff; margin-bottom: 4px; }
.dl-desc { font-size: 0.9rem; color: var(--text-muted); margin-bottom: 6px; }
.dl-size { display: inline-block; font-size: 0.88rem; color: var(--text-muted); margin-right: 10px; }
.dl-link {
direction: ltr; text-align: left; word-break: break-all; user-select: all;
font-family: 'Courier New', monospace; font-size: 0.78rem; color: #c9d1d9;
background: #0d1117; border: 1px solid #30363d; border-radius: 10px;
padding: 10px 12px; margin-top: 10px; line-height: 1.55;
}
"""
download_card = f""" <div class="step">
<div class="step-header">
<div class="step-number">⬇</div>
<div class="step-title">اول: نصب آخرین نسخهی PattNG</div>
</div>
<div class="step-content">
<p>اول <strong>حتماً و حتماً</strong> آخرین نسخهی PattNG را نصب کنید؛ نسخهای که الان نصب دارید، آخرین نسخه نیست و این کانفیگها با آن درست کار نمیکنند. آخرین نسخه در حال حاضر <span class="highlight">v2.3.13-P69</span> است.</p>
<div class="note warning-note">
<strong>کدام دکمه را بزنم؟</strong> به اسم برنامه روی گوشیتان نگاه کنید: اگر اسمش <strong>PattNG (F-Droid)</strong> است، دکمهی اول را بزنید؛ اگر فقط <strong>PattNG</strong> است، دکمهی دوم را. نسخهی اشتباه مثل یک برنامهی جدا نصب میشود و کانفیگهایتان را نشان نمیدهد.
</div>
<div class="dl-block">
<div class="dl-name">نسخهی F-Droid</div>
<div class="dl-desc">برای کسانی که اسم برنامهشان PattNG (F-Droid) است:</div>
<a class="btn-download" href="{URL_FDROID}" target="_blank" rel="noopener">⬇ دانلود PattNG (F-Droid)</a>
<span class="dl-size">حجم: {SIZE}</span>
<div class="dl-link">{URL_FDROID}</div>
</div>
<div class="dl-block">
<div class="dl-name">نسخهی معمولی</div>
<div class="dl-desc">برای کسانی که اسم برنامهشان فقط PattNG است:</div>
<a class="btn-download" href="{URL_NORMAL}" target="_blank" rel="noopener">⬇ دانلود PattNG معمولی</a>
<span class="dl-size">حجم: {SIZE}</span>
<div class="dl-link">{URL_NORMAL}</div>
</div>
</div>
</div>
"""
s1 = step("۱", "انتخاب کانفیگ Aether", f""" <p>وارد برنامه شوید و یکی از کانفیگهایی را که زیرشان با رنگ نارنجی نوشته شده <span class="highlight">AETHER / MASQUE → PSIPHON</span> انتخاب کنید و علامت <strong>مداد</strong> را بزنید:</p>
{shot(0)}""")
s2 = step("۲", "زدن Get new WARP keys", f""" <p>در صفحهای که باز میشود، بالاترین گزینه یعنی <span class="highlight">Get new WARP keys</span> را بزنید:</p>
{shot(1)}""")
s3 = step("۳", "گرفتن کلیدهای جدید WARP", f""" <p>کمی به پایین بیایید تا به دکمهی آبیرنگ <span class="highlight">Get new WARP keys</span> برسید و روی آن بزنید:</p>
{shot(2)}
<p>بعد کمی صبر کنید تا متن سبزرنگی مثل عکس زیر ظاهر شود؛ یعنی کلیدهای جدید گرفته شدهاند:</p>
{shot(3)}""")
s4 = step("۴", "برگشتن و وصل شدن", """ <p>در آخر، علامت فلش بالای صفحه را بزنید تا به صفحهی اصلی برگردید؛ بعد از برگشتن، روی کانفیگ بزنید تا وصل شوید.</p>
<div class="note warning-note">
<strong>نکته مهم:</strong> این کانفیگها درجا وصل نمیشوند. بعد از زدن دکمهی وصل شدن، کمی صبر کنید تا پیام سبزرنگ ظاهر شود؛ یعنی اتصال برقرار شده است.
</div>""")
section = f"""
<!-- ===== بخش دوم: آموزش وصل شدن به کانفیگهای Aether ===== -->
<div class="part-divider" id="aether">
<div class="badge">بخش دوم</div>
<h2>آموزش وصل شدن به کانفیگهای Aether</h2>
<p>این بخش مخصوص برنامهی PattNG است. اگر کانفیگهای Aether را دارید، برای وصل شدن این مراحل را انجام دهید.</p>
</div>
{download_card}
{s1}
{s2}
{s3}
{s4}
"""
# insert extra CSS before the closing of the media query block's parent: append before </style>
assert "</style>" in html
html = html.replace("</style>", css_extra + " </style>", 1)
# insert section before footer
assert " <footer>" in html
html = html.replace(" <footer>", section + " <footer>", 1)
(SITE / "plain.html").write_text(html, encoding="utf-8")
print("plain.html now:", len(html.encode()), "bytes")