Spaces:
Running
Running
File size: 6,013 Bytes
e6131ae a5eb6b9 e6131ae 5803228 e6131ae | 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 141 142 143 144 145 146 147 148 149 150 151 152 | #!/usr/bin/env python3
"""
Set up LINE Rich Menu for Stock Predictor bot.
Run once: python setup_rich_menu.py
Layout (2 columns x 2 rows = 4 buttons):
[๐ ๆฅ่ก็ฅจ] [๐ ้ๅ็ถฒ็ซ]
[๐ ้ ๆธฌ] [โ ่ชชๆ]
"""
import os, json, sys, urllib.request, urllib.error
TOKEN = os.environ.get("LINE_CHANNEL_ACCESS_TOKEN") or ""
SITE_URL = os.environ.get("STOCK_SITE_URL", "https://dennischan0909-dockerspace.hf.space")
if not TOKEN:
# Try loading from .env in same dir
env_file = os.path.join(os.path.dirname(__file__), ".env")
if os.path.exists(env_file):
for line in open(env_file):
line = line.strip()
if line.startswith("LINE_CHANNEL_ACCESS_TOKEN="):
TOKEN = line.split("=", 1)[1].strip().strip('"').strip("'")
if line.startswith("STOCK_SITE_URL="):
SITE_URL = line.split("=", 1)[1].strip().strip('"').strip("'")
if not TOKEN:
print("ERROR: LINE_CHANNEL_ACCESS_TOKEN not set")
sys.exit(1)
HEADERS = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
def api(method: str, path: str, body=None):
url = f"https://api.line.me{path}"
data = json.dumps(body).encode() if body else None
req = urllib.request.Request(url, data=data, headers=HEADERS, method=method)
try:
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
except urllib.error.HTTPError as e:
err = e.read().decode()
print(f" HTTP {e.code}: {err}")
raise
# โโ 1. Delete existing menus โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
print("Deleting existing rich menus...")
try:
menus = api("GET", "/v2/bot/richmenu/list")
for m in menus.get("richmenus", []):
api("DELETE", f"/v2/bot/richmenu/{m['richMenuId']}")
print(f" Deleted {m['richMenuId']}")
except Exception as e:
print(f" (skip) {e}")
# โโ 2. Create new menu โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
W, H = 2500, 843 # LINE recommended size for 2-row menu
HALF_W = W // 2
menu_body = {
"size": {"width": W, "height": H},
"selected": True,
"name": "Stock Menu",
"chatBarText": "่ก็ฅจ้ธๅฎ",
"areas": [
# Row 1 left โ ๆฅ่ก็ฅจ
{
"bounds": {"x": 0, "y": 0, "width": HALF_W, "height": H // 2},
"action": {"type": "message", "text": "ๆฅ่ก็ฅจ"},
},
# Row 1 right โ ้ๅ็ถฒ็ซ
{
"bounds": {"x": HALF_W, "y": 0, "width": HALF_W, "height": H // 2},
"action": {"type": "uri", "uri": SITE_URL},
},
# Row 2 left โ ้ ๆธฌ
{
"bounds": {"x": 0, "y": H // 2, "width": HALF_W, "height": H // 2},
"action": {"type": "message", "text": "้ ๆธฌ"},
},
# Row 2 right โ ่ชชๆ
{
"bounds": {"x": HALF_W, "y": H // 2, "width": HALF_W, "height": H // 2},
"action": {"type": "message", "text": "่ชชๆ"},
},
],
}
print("Creating rich menu...")
result = api("POST", "/v2/bot/richmenu", menu_body)
menu_id = result["richMenuId"]
print(f" Created: {menu_id}")
# โโ 3. Upload image โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
print("Uploading menu image...")
img_path = os.path.join(os.path.dirname(__file__), "rich_menu.png")
if not os.path.exists(img_path):
# Generate a simple placeholder image with Pillow if available
try:
from PIL import Image, ImageDraw, ImageFont
img = Image.new("RGB", (W, H), "#1a1a2e")
draw = ImageDraw.Draw(img)
labels = [
("๐ ๆฅ่ก็ฅจ", HALF_W // 2, H // 4),
("๐ ้ๅ็ถฒ็ซ", HALF_W + HALF_W // 2, H // 4),
("๐ ้ ๆธฌ", HALF_W // 2, H * 3 // 4),
("โ ่ชชๆ", HALF_W + HALF_W // 2, H * 3 // 4),
]
# Grid lines
draw.line([(HALF_W, 0), (HALF_W, H)], fill="#444", width=4)
draw.line([(0, H // 2), (W, H // 2)], fill="#444", width=4)
# Labels
try:
font = ImageFont.truetype("arial.ttf", 80)
except Exception:
font = ImageFont.load_default()
for text, x, y in labels:
draw.text((x, y), text, fill="white", font=font, anchor="mm")
img.save(img_path)
print(f" Generated placeholder image: {img_path}")
except ImportError:
print(" Pillow not installed โ skipping auto-image generation")
print(f" Please place a 2500ร843 PNG at: {img_path}")
print(" Then re-run this script (it will skip menu creation and only upload image + set default).")
# Set default anyway so menu structure is ready
api("POST", f"/v2/bot/richmenu/{menu_id}/default")
print(f" Set as default (image pending). Menu ID: {menu_id}")
sys.exit(0)
with open(img_path, "rb") as f:
img_data = f.read()
img_req = urllib.request.Request(
f"https://api-data.line.me/v2/bot/richmenu/{menu_id}/content",
data=img_data,
headers={**HEADERS, "Content-Type": "image/png"},
method="POST",
)
try:
with urllib.request.urlopen(img_req) as r:
print(f" Image uploaded: {r.status}")
except urllib.error.HTTPError as e:
print(f" Image upload failed: {e.code} {e.read().decode()}")
# โโ 4. Set as default โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
print("Setting as default menu...")
api("POST", f"/v2/bot/user/all/richmenu/{menu_id}")
print(f"Done! Menu ID: {menu_id}")
print(f"Site URL: {SITE_URL}")
|