64ac71a8ed
- Collected 9 new messages from dc group and innovation group - Fetched 6 new Feishu docs (黄静雯/陈楚真/韦译/夏莲/莫润麟/张家振) - Downloaded 狱国争霸分析拆解.html file attachment - Saved 6/12 game industry daily report from 韩丹 - Updated .gitignore to exclude temp scripts, images, downloaded files - Added utility scripts (group members extraction, invitation generation)
98 lines
2.9 KiB
Python
98 lines
2.9 KiB
Python
"""批量生成邀请函图片:读取 invitation_config.json 模板 + group_members_with_account.csv"""
|
|
|
|
import csv
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data")
|
|
BG_PATH = os.path.join(DATA_DIR, "bg.png")
|
|
FONT_DIR = DATA_DIR
|
|
CONFIG_PATH = os.path.join(DATA_DIR, "invitation_config.json")
|
|
CSV_PATH = os.path.join(DATA_DIR, "group_members_with_account.csv")
|
|
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "output", "invitations")
|
|
|
|
|
|
def hex_to_rgb(h):
|
|
h = h.lstrip('#')
|
|
return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))
|
|
|
|
|
|
def resolve_font(name):
|
|
local = os.path.join(FONT_DIR, name)
|
|
if os.path.isfile(local):
|
|
return local
|
|
sys_font = os.path.join(r"C:\Windows\Fonts", name)
|
|
if os.path.isfile(sys_font):
|
|
return sys_font
|
|
return os.path.join(FONT_DIR, "WenDaoGeTeSong.ttf")
|
|
|
|
|
|
def generate_image(cfg, name, account, password):
|
|
img = Image.open(BG_PATH).copy()
|
|
draw = ImageDraw.Draw(img)
|
|
font_path = resolve_font(cfg.get("font", "WenDaoGeTeSong.ttf"))
|
|
sw = cfg.get("stroke_width", 3)
|
|
sc = hex_to_rgb(cfg.get("stroke_color", "#000000"))
|
|
|
|
size = cfg.get("size", 30)
|
|
font = ImageFont.truetype(font_path, size)
|
|
color = hex_to_rgb(cfg.get("color", "#ffffff"))
|
|
x = cfg.get("x", 510)
|
|
y = cfg.get("y", 1055)
|
|
|
|
text = cfg.get("text", "")
|
|
text = text.replace("{name}", name).replace("{account}", account).replace("{password}", password)
|
|
|
|
line_height = cfg.get("line_height", 1.5)
|
|
spacing = int(size * (line_height - 1.0))
|
|
|
|
align = cfg.get("align", "center")
|
|
anchor_map = {"left": "lt", "center": "mt", "right": "rt"}
|
|
anchor = anchor_map.get(align, "mt")
|
|
|
|
lines = text.split("\n")
|
|
cur_y = y
|
|
for line in lines:
|
|
draw.text(
|
|
(x, cur_y), line, fill=color, font=font, anchor=anchor,
|
|
stroke_width=sw, stroke_fill=sc
|
|
)
|
|
cur_y += size + spacing
|
|
return img
|
|
|
|
|
|
def main():
|
|
with open(CONFIG_PATH, encoding="utf-8") as f:
|
|
cfg = json.load(f)
|
|
|
|
with open(CSV_PATH, encoding="utf-8") as f:
|
|
rows = list(csv.DictReader(f))
|
|
|
|
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
|
|
|
total = 0
|
|
skipped = 0
|
|
for row in rows:
|
|
name = row.get("姓名", "").strip()
|
|
account = row.get("账号", "").strip()
|
|
password = row.get("密码", "").strip()
|
|
|
|
if not account or not password:
|
|
skipped += 1
|
|
continue
|
|
|
|
img = generate_image(cfg, name, account, password)
|
|
output_path = os.path.join(OUTPUT_DIR, f"invite_{account}.jpg")
|
|
img.convert("RGB").save(output_path, format="JPEG", quality=75)
|
|
total += 1
|
|
|
|
print(f"Done. Generated {total} images, skipped {skipped} (missing account/password).")
|
|
print(f"Output: {OUTPUT_DIR}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|