深夜提醒

现在是深夜,建议您注意休息,不要熬夜哦~

🏮 🏮 🏮

新年快乐

祝君万事如意心想事成!

share-image
中
ESC

Three Cards, Three Clicks: I Turned Weekend Venue Booking into a Feishu Bot

English 阅读中文版

Saturday, 9 a.m.: my phone lights up with a message from a bot I wrote myself.

It says: rain tomorrow, eight indoor venues still open for booking—pick one?

I tap once. All three cards get booked in the same minute.

A year ago, this would have eaten ten minutes of my morning, plus a little bit of my patience.

1. The Pain Isn’t “Can’t Book”—It’s “Booking Three Times”

We keep three benefit cards at home, one for each of three people. The platform’s rule is: one card, one set of benefits, and each booking can only use a single cardholder.

So every outing, the whole flow gets multiplied by three:

Open the mini-program → pick a card → pick a venue → pick a date → pick a ticket type → confirm → back to step one.

Three taps, three confirmations, three chances to fail.

What’s worse is the “choosing” itself:

  • Some venues need a coupon to get in, but that ticket type simply isn’t selectable on the booking page;
  • Some venues release weekend inventory in time slots—there in the morning, gone by noon;
  • Some require booking a day ahead, some allow same-day, with cutoffs anywhere from 16:00 to 23:59;
  • For the non-refundable ones, a single wrong tap is real money down the drain.

The platform has no concept of “batch.” It assumes you’re only ever serving one person at a time.

And what I wanted to solve wasn’t “can I book”—it was “can I make the decision just once.”

2. First Teach the Machine to “Calculate,” Then to “Click”

I ruled out UI automation first: the mini-program’s page tree is empty, so there are no controls to grab; and OCR-based tapping is far too brittle—the moment inventory or ticket types shift, it all goes wrong.

What’s genuinely stable is the API underneath. After one round of packet capture, I found that the login state is a single account-level, long-lived token—no WeChat, no phone, no browser required. That gave me a script that leans only on the standard library:

  • pull the three cards and each one’s remaining benefit-package balance;
  • pull the venue list and work out which ones are “points-free” (no extra cost);
  • check real inventory and ticket types for each candidate venue, one by one;
  • sort by “closest to Hangzhou → highest face value → refundable”;
  • place orders for the three cards in sequence; if any one fails, switch to the next available benefit package and retry.

The point isn’t “automating the clicks”—it’s “writing the rules into code”: how many days ahead, what time the cutoff is, how many items you’ve already booked that day, whether this card has already booked the same venue. A person misses one of these checks every single time; a machine doesn’t.

A Little Side Archaeology: IIS 7.5 and ASP.NET MVC 4

The first time I looked at the response headers, I did a double-take:

  • server: Microsoft-IIS/7.5
  • x-aspnet-version: 4.0.30319 and x-aspnetmvc-version: 4.0
  • Set-Cookie: ASP.NET_SessionId=...
  • the date fields come in that old ASP.NET format, /Date(1777305600000)/

A mini-program still selling benefit cards in 2026, running on a backend of IIS 7.5 + ASP.NET MVC 4—roughly the Windows Server 2008 R2 generation. No wonder the API throws the occasional TLS wobble; I had to build four retries into the script for it.

3. Moving the “Choice” into Feishu

The script worked, but firing up a terminal every time wasn’t realistic. So I wired it into Feishu (Lark): one self-built app plus a persistent long connection—no public callback URL needed, the bot dials out on its own.

Every Saturday at 09:00, a scheduled job does three things:

  1. check Sunday’s weather in Hangzhou;
  2. if it’s clear, sort as usual; if it rains, surface indoor options only (skiing, museums, cinema, trampoline parks, climate-controlled indoor parks);
  3. package up to three candidates into a single card, each with a “Book this one” button, plus one “Skip this week.”

Tap “Book this one,” and the bot quietly places one order per card in the background, then updates that same card in place with the outcome: who succeeded, who failed, and why.

The everyday operations are slash commands, too:

/plan [date]        show the choice card
/orders pending bookings, one cancel button each
/cancel <order ID> cancel a single booking
/cancel all cancel all future bookings
/cards how many uses each of the three cards has left
/help help

The /help command and the booking results, inside Feishu

And just like that, “three clicks” became “one choice.”

4. Four Pitfalls I Ran Into

1. One Feishu app can only have a single consumer on its long connection.

An older bot was still running on the machine, using the very same app. So the messages I sent got swallowed by it, and the new bot only ever saw a reaction event. It took stopping, disabling, and cleaning up the old service before things behaved again.

2. Grab the wrong benefit package, and it fails even when you “have uses left.”

The same venue can hang under two packages at once. At first I grabbed the bigger package—the one with the largest “choose-N” quota—but the server treated it as a paid tier and returned “insufficient remaining uses.” I switched to trying packages smallest-to-largest, moving on at the first failure, and the problem vanished.

3. Leave out one layer of JSON nesting, and the card “fake-succeeds.”

The script tucked the result inside a booking field, while the bot read the top level. Reading that field empty, it rendered a card with no information at all as “booked.” Only after I fixed it did the card start showing the date, the amount, and the per-person results.

4. The process ignored SIGTERM, so restarts hung for 90 seconds.

The long-connection library doesn’t react to context cancellation, so systemd could only wait out the timeout and then KILL. I added a safety net: force-quit two seconds after a shutdown signal arrives. Restarts went from 90 seconds to 2.

5. Boundaries

This thing does exactly three jobs: query, sort, order. It doesn’t route around the platform’s inventory or rules, and it doesn’t make the “where to go” call for anyone.

A few constraints I set for myself:

  • by default, only book refundable venues; non-refundable ones just get listed as a heads-up, never auto-ordered;
  • if the same card already has an order for the same venue on the same day, skip it—no double-booking;
  • at most 6 items per card per day; stop at the cap;
  • don’t retry a failed order to death—if switching packages also fails, throw the error and hand it to a human;
  • the token lives only on this machine, permission 0600, and never touches the logs or the chat history.

6. In Closing

What automation actually saves isn’t money—it’s “those ten minutes on a weekend morning”: no more opening the mini-program, no more squinting at inventory three times over, no more worrying you’ll mistap a non-refundable ticket.

The machine’s job is to fold up the repetitive motions; the human’s job is to decide where this weekend goes.

If you hand the machine even the “where to go,” that’s not automation—that’s giving up.

文章作者:阿文
文章链接: https://www.awen.me/post/2b471be3.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿文的博客!

评论

0 条评论
😀😃😄 😁😅😂 🤣😊😇 🙂🙃😉 😌😍🥰 😘😗😙 😚😋😛 😝😜🤪 🤨🧐🤓 😎🥸🤩 🥳😏😒 😞😔😟 😕🙁☹️ 😣😖😫 😩🥺😢 😭😤😠 😡🤬🤯 😳🥵🥶 😱😨😰 😥😓🤗 🤔🤭🤫 🤥😶😐 😑😬🙄 😯😦😧 😮😲🥱 😴🤤😪 😵🤐🥴 🤢🤮🤧 😷🤒🤕 🤑🤠😈 👿👹👺 🤡💩👻 💀☠️👽 👾🤖🎃 😺😸😹 😻😼😽 🙀😿😾 👍👎👏 🙌👐🤲 🤝🤜🤛 ✌️🤞🤟 🤘👌🤏 👈👉👆 👇☝️✋ 🤚🖐️🖖 👋🤙💪 🦾🖕✍️ 🙏💅🤳 💯💢💥 💫💦💨 🕳️💣💬 👁️‍🗨️🗨️🗯️ 💭💤❤️ 🧡💛💚 💙💜🖤 🤍🤎💔 ❣️💕💞 💓💗💖 💘💝💟 ☮️✝️☪️ 🕉️☸️✡️ 🔯🕎☯️ ☦️🛐⛎ ♈♉♊ ♋♌♍ ♎♏♐ ♑♒♓ 🆔⚛️🉑 ☢️☣️📴 📳🈶🈚 🈸🈺🈷️ ✴️🆚💮 🉐㊙️㊗️ 🈴🈵🈹 🈲🅰️🅱️ 🆎🆑🅾️ 🆘❌⭕ 🛑⛔📛 🚫💯💢 ♨️🚷🚯 🚳🚱🔞 📵🚭❗ ❕❓❔ ‼️⁉️🔅 🔆〽️⚠️ 🚸🔱⚜️ 🔰♻️✅ 🈯💹❇️ ✳️❎🌐 💠Ⓜ️🌀 💤🏧🚾 ♿🅿️🈳 🈂🛂🛃 🛄🛅🛗 🚀🛸🚁 🚉🚆🚅 ✈️🛫🛬 🛩️💺🛰️
加载中...

留言反馈

😀😃😄 😁😅😂 🤣😊😇 🙂🙃😉 😌😍🥰 😘😗😙 😚😋😛 😝😜🤪 🤨🧐🤓 😎🥸🤩 🥳😏😒 😞😔😟 😕🙁☹️ 😣😖😫 😩🥺😢 😭😤😠 😡🤬🤯 😳🥵🥶 😱😨😰 😥😓🤗 🤔🤭🤫 🤥😶😐 😑😬🙄 😯😦😧 😮😲🥱 😴🤤😪 😵🤐🥴 🤢🤮🤧 😷🤒🤕 🤑🤠😈 👿👹👺 🤡💩👻 💀☠️👽 👾🤖🎃 😺😸😹 😻😼😽 🙀😿😾 👍👎👏 🙌👐🤲 🤝🤜🤛 ✌️🤞🤟 🤘👌🤏 👈👉👆 👇☝️✋ 🤚🖐️🖖 👋🤙💪 🦾🖕✍️ 🙏💅🤳 💯💢💥 💫💦💨 🕳️💣💬 👁️‍🗨️🗨️🗯️ 💭💤❤️ 🧡💛💚 💙💜🖤 🤍🤎💔 ❣️💕💞 💓💗💖 💘💝💟 ☮️✝️☪️ 🕉️☸️✡️ 🔯🕎☯️ ☦️🛐⛎ ♈♉♊ ♋♌♍ ♎♏♐ ♑♒♓ 🆔⚛️🉑 ☢️☣️📴 📳🈶🈚 🈸🈺🈷️ ✴️🆚💮 🉐㊙️㊗️ 🈴🈵🈹 🈲🅰️🅱️ 🆎🆑🅾️ 🆘❌⭕ 🛑⛔📛 🚫💯💢 ♨️🚷🚯 🚳🚱🔞 📵🚭❗ ❕❓❔ ‼️⁉️🔅 🔆〽️⚠️ 🚸🔱⚜️ 🔰♻️✅ 🈯💹❇️ ✳️❎🌐 💠Ⓜ️🌀 💤🏧🚾 ♿🅿️🈳 🈂🛂🛃 🛄🛅🛗 🚀🛸🚁 🚉🚆🚅 ✈️🛫🛬 🛩️💺🛰️