| import gradio as gr |
| import random |
| from datetime import datetime, timedelta, timezone |
|
|
| |
| GITHUB_PAGES_URL = "https://nyanpre.github.io/hasu-cp-oracle/" |
|
|
| |
| members = ["かほ", "さや", "こず", "るり", "めぐ", "つづ", "ぎん", "すず", "ひめ", "せら", "いず", "さち", "まい", "あお"] |
|
|
| |
| js_get_share_text = f""" |
| function() {{ |
| const pairRawEl = document.getElementById('pair-raw'); |
| let pairName = ""; |
| |
| if (pairRawEl) {{ |
| pairName = pairRawEl.innerText; |
| }} else {{ |
| const lastPair = localStorage.getItem('lastPairText') || ""; |
| const match = lastPair.match(/>([^<]{{2,}})<\\/div>$/); |
| pairName = match ? match[1] : "default"; |
| }} |
| |
| const shareUrl = "{GITHUB_PAGES_URL}" + encodeURIComponent(pairName) + ".html"; |
| |
| return "私は「" + pairName + "」を信仰しています。\\n\\n蓮ノ空聖書正典:\\n" + shareUrl; |
| }} |
| """ |
| js_share_bluesky = f"function() {{ const text = ({js_get_share_text})(); window.open(`https://bsky.app/intent/compose?text=${{encodeURIComponent(text)}}`, '_blank'); }}" |
| js_share_x = f"function() {{ const text = ({js_get_share_text})(); window.open(`https://twitter.com/intent/tweet?text=${{encodeURIComponent(text)}}`, '_blank'); }}" |
|
|
| |
| def get_personal_daily_oracle(device_id): |
| seed_base = device_id if device_id else "default_fate" |
| jst = timezone(timedelta(hours=9)) |
| today_str = datetime.now(jst).strftime("%Y-%m-%d") |
| random.seed(f"{seed_base}_{today_str}") |
| selected = random.sample(members, 2) |
| random.shuffle(selected) |
| pair_name = f"{selected[0]}{selected[1]}" |
| oracle_html = ( |
| f"<div id='pair-raw' style='display:none;'>{pair_name}</div>" |
| f"<div style='font-size: 38px; font-weight: normal; margin-bottom: 2px;'>本日の神託</div>" |
| f"<div style='font-size: 52px; font-weight: 900; letter-spacing: 2px;'>{pair_name}</div>" |
| ) |
| peace_msg = "これにより、不毛なカップリング論争は終結しました。" |
| return oracle_html, peace_msg, gr.update(visible=True), gr.update(visible=True), gr.update(visible=True) |
|
|
| custom_css = """ |
| .gradio-container { max-width: 600px !important; text-align: center !important; } |
| footer { display: none !important; } |
| .center-content { display: flex !important; flex-direction: column !important; align-items: center !important; padding-top: 45px !important; } |
| h1 { margin-top: 0px !important; margin-bottom: -5px !important; font-size: 32px !important; } |
| #doctrine { font-size: 1.5em !important; line-height: 1.4 !important; font-weight: bold !important; margin-bottom: -5px !important; text-align: center !important; } |
| #oracle-box { |
| color: #000 !important; background: #fff !important; border: 4px solid #000 !important; |
| padding: 25px 10px !important; line-height: 1.1 !important; min-height: 130px !important; |
| display: flex !important; flex-direction: column !important; justify-content: center !important; |
| margin: 0px auto -5px auto !important; |
| } |
| .dark #oracle-box { color: #fff !important; background: #000 !important; border: 4px solid #fff !important; } |
| #peace-msg { font-size: 20px !important; font-weight: bold !important; color: #d63031 !important; margin: 0px auto -5px auto !important; } |
| .action-btn { font-size: 24px !important; font-weight: bold !important; width: 320px !important; border: 2px solid #000 !important; } |
| .dark .action-btn { border: 2px solid #fff !important; } |
| #draw-btn { margin: 0px auto -5px auto !important; height: 75px !important; font-size: 28px !important; } |
| #share-bsky { margin: 0px auto -5px auto !important; height: 60px !important; } |
| #share-x { margin: 0px auto -5px auto !important; height: 60px !important; } |
| """ |
|
|
| |
| with gr.Blocks(title="蓮ノ空聖書正典") as demo: |
| device_id_storage = gr.Textbox(visible=False) |
| demo.load(None, None, device_id_storage, js=""" |
| () => { |
| let id = localStorage.getItem('cp_oracle_device_id'); |
| if(!id) { |
| id = Math.random().toString(36).substring(2, 15); |
| localStorage.setItem('cp_oracle_device_id', id); |
| } |
| return id; |
| } |
| """) |
| with gr.Column(elem_classes="center-content"): |
| gr.Markdown("# ⚖️ 蓮ノ空聖書正典") |
| gr.Markdown("日付が変わるまであなたの思想は<br>統一されます。", elem_id="doctrine") |
| result_display = gr.HTML(elem_id="oracle-box", visible=False) |
| peace_display = gr.Markdown(elem_id="peace-msg") |
| draw_btn = gr.Button("神託を受ける", variant="primary", elem_id="draw-btn", elem_classes="action-btn") |
| share_btn_x = gr.Button("Xで信仰を広める", variant="secondary", elem_id="share-x", elem_classes="action-btn", visible=False) |
| share_btn_bsky = gr.Button("Blueskyで信仰を広める", variant="secondary", elem_id="share-bsky", elem_classes="action-btn", visible=False) |
|
|
| draw_btn.click( |
| fn=get_personal_daily_oracle, |
| inputs=[device_id_storage], |
| outputs=[result_display, peace_display, share_btn_bsky, share_btn_x, result_display], |
| js=""" |
| function(deviceId) { |
| const lastDraw = localStorage.getItem('lastOracleDate'); |
| const lastPair = localStorage.getItem('lastPairText'); |
| const today = new Date().toLocaleDateString('ja-JP'); |
| if (lastDraw === today && lastPair) { |
| alert("本日の神託は既に下されています。\\n明日の更新まで、今の思想を維持しなさい。"); |
| return [deviceId, lastPair, "これにより、不毛なカップリング論争は終結しました。", { "visible": true, "__type__": "update" }, { "visible": true, "__type__": "update" }, { "visible": true, "__type__": "update" }]; |
| } |
| localStorage.setItem('lastOracleDate', today); |
| return [deviceId, null, null, null, null, null]; |
| } |
| """ |
| ).then(fn=None, inputs=[result_display], outputs=None, js=""" |
| function(oracleHtml) { |
| if (oracleHtml && oracleHtml.includes("本日の神託")) { |
| localStorage.setItem('lastPairText', oracleHtml); |
| } |
| } |
| """) |
| share_btn_bsky.click(fn=None, inputs=None, outputs=None, js=js_share_bluesky) |
| share_btn_x.click(fn=None, inputs=None, outputs=None, js=js_share_x) |
|
|
| if __name__ == "__main__": |
| |
| demo.launch(css=custom_css, theme=gr.themes.Monochrome()) |