fc33b3eb68
* docs: transfer AstrBotDevs/AstrBot-docs to AstrBotDevs/AstrBot * refactor: reorder imports and improve type hints in sync_docs_to_wiki.py and upload_doc_images_to_r2.py * feat: add GitHub Actions workflow to sync wiki with documentation Co-authored-by: Soulter <37870767+Soulter@users.noreply.github.com> Co-authored-by: anka-afk <110004162+anka-afk@users.noreply.github.com> Co-authored-by: zouyonghe <62183434+zouyonghe@users.noreply.github.com> Co-authored-by: shuiping233 <49360196+shuiping233@users.noreply.github.com> Co-authored-by: LIghtJUNction <106986785+LIghtJUNction@users.noreply.github.com> Co-authored-by: Sjshi763 <179909421+Sjshi763@users.noreply.github.com> Co-authored-by: xiewoc <70128845+xiewoc@users.noreply.github.com> Co-authored-by: QingFeng-awa <151742581+QingFeng-awa@users.noreply.github.com> Co-authored-by: PaloMiku <96452465+PaloMiku@users.noreply.github.com> Co-authored-by: shangxueink <138397030+shangxueink@users.noreply.github.com> Co-authored-by: IGCrystal-A <244300990+IGCrystal-A@users.noreply.github.com> Co-authored-by: RC-CHN <67079377+RC-CHN@users.noreply.github.com> Co-authored-by: MC090610 <113341105+MC090610@users.noreply.github.com> Co-authored-by: Waterwzy <196913419+Waterwzy@users.noreply.github.com> Co-authored-by: Lanhuace-Wan <186303160+Lanhuace-Wan@users.noreply.github.com> Co-authored-by: LiAlH4qwq <61769640+LiAlH4qwq@users.noreply.github.com> Co-authored-by: HSOS6 <209910899+HSOS6@users.noreply.github.com> Co-authored-by: th-dd <162813557+th-dd@users.noreply.github.com> Co-authored-by: miaoxutao123 <81676466+miaoxutao123@users.noreply.github.com> Co-authored-by: nuomicici <143102889+nuomicici@users.noreply.github.com> Co-authored-by: nasyt233 <210103278+nasyt233@users.noreply.github.com> Co-authored-by: jlugjb <7426462+jlugjb@users.noreply.github.com> Co-authored-by: Raven95676 <176760093+Raven95676@users.noreply.github.com> Co-authored-by: Futureppo <180109455+Futureppo@users.noreply.github.com> Co-authored-by: MliKiowa <61873808+MliKiowa@users.noreply.github.com> Co-authored-by: Fridemn <150212937+Fridemn@users.noreply.github.com> Co-authored-by: BakaCookie520 <138355736+BakaCookie520@users.noreply.github.com> Co-authored-by: YumeYuka <125112916+YumeYuka@users.noreply.github.com> Co-authored-by: xming521 <32786500+xming521@users.noreply.github.com> Co-authored-by: ywh555hhh <121592812+ywh555hhh@users.noreply.github.com> Co-authored-by: stevessr <89645372+stevessr@users.noreply.github.com> Co-authored-by: roeseth <41995115+roeseth@users.noreply.github.com> Co-authored-by: ikun-1145141 <265925499+ikun-1145141@users.noreply.github.com> Co-authored-by: evpeople <54983536+evpeople@users.noreply.github.com> Co-authored-by: Yue-bin <60509781+Yue-bin@users.noreply.github.com> Co-authored-by: W1ndys <109416673+W1ndys@users.noreply.github.com> Co-authored-by: TheFurina <218887821+TheFurina@users.noreply.github.com> Co-authored-by: Seayon <12275933+Seayon@users.noreply.github.com> Co-authored-by: OnlyblackTea <38585636+OnlyblackTea@users.noreply.github.com> Co-authored-by: ocetars <74854972+ocetars@users.noreply.github.com> Co-authored-by: railgun19457 <117180744+railgun19457@users.noreply.github.com> Co-authored-by: JunieXD <107397009+JunieXD@users.noreply.github.com> Co-authored-by: advent259141 <197440256+advent259141@users.noreply.github.com> Co-authored-by: Doge2077 <91442300+Doge2077@users.noreply.github.com> Co-authored-by: Bocity <23430545+Bocity@users.noreply.github.com> Co-authored-by: Aurora-xk <192227833+Aurora-xk@users.noreply.github.com>
114 lines
4.9 KiB
Markdown
114 lines
4.9 KiB
Markdown
|
|
# Session Control
|
|
|
|
> v3.4.36 and above
|
|
|
|
Why do we need session control? Consider a Chinese idiom chain game plugin where a user or group needs to have multiple conversations with the bot rather than a one-time command. This is when session control becomes necessary.
|
|
|
|
```txt
|
|
User: /idiom-chain
|
|
Bot: Please send an idiom
|
|
User: One horse takes the lead (一马当先)
|
|
Bot: Foresight (先见之明)
|
|
User: Keen observation (明察秋毫)
|
|
...
|
|
```
|
|
|
|
AstrBot provides out-of-the-box session control functionality:
|
|
|
|
Import:
|
|
|
|
```py
|
|
import astrbot.api.message_components as Comp
|
|
from astrbot.core.utils.session_waiter import (
|
|
session_waiter,
|
|
SessionController,
|
|
)
|
|
```
|
|
|
|
Code within the handler can be written as follows:
|
|
|
|
```python
|
|
from astrbot.api.event import filter, AstrMessageEvent
|
|
|
|
@filter.command("idiom-chain")
|
|
async def handle_empty_mention(self, event: AstrMessageEvent):
|
|
"""Idiom chain game implementation"""
|
|
try:
|
|
yield event.plain_result("Please send an idiom~")
|
|
|
|
# How to use the session controller
|
|
@session_waiter(timeout=60, record_history_chains=False) # Register a session controller with a 60-second timeout, without recording message history
|
|
async def empty_mention_waiter(controller: SessionController, event: AstrMessageEvent):
|
|
idiom = event.message_str # The idiom sent by the user, e.g., "one horse takes the lead"
|
|
|
|
if idiom == "exit": # If the user wants to exit the idiom chain game by typing "exit"
|
|
await event.send(event.plain_result("Exited the idiom chain game~"))
|
|
controller.stop() # Stop the session controller, which will end immediately.
|
|
return
|
|
|
|
if len(idiom) != 4: # If the user's input is not a 4-character idiom
|
|
await event.send(event.plain_result("The idiom must be four characters~")) # Send a reply, cannot use yield
|
|
return
|
|
# Exit the current method without executing subsequent logic, but the session is not interrupted; subsequent user input will still enter the current session
|
|
|
|
# ...
|
|
message_result = event.make_result()
|
|
message_result.chain = [Comp.Plain("Foresight")] # import astrbot.api.message_components as Comp
|
|
await event.send(message_result) # Send a reply, cannot use yield
|
|
|
|
controller.keep(timeout=60, reset_timeout=True) # Reset timeout to 60s. If not reset, it will continue the previous timeout countdown.
|
|
|
|
# controller.stop() # Stop the session controller, which will end immediately.
|
|
# If history chains are recorded, you can retrieve them via controller.get_history_chains()
|
|
|
|
try:
|
|
await empty_mention_waiter(event)
|
|
except TimeoutError as _: # When timeout occurs, the session controller will raise TimeoutError
|
|
yield event.plain_result("You timed out!")
|
|
except Exception as e:
|
|
yield event.plain_result("An error occurred, please contact the administrator: " + str(e))
|
|
finally:
|
|
event.stop_event()
|
|
except Exception as e:
|
|
logger.error("handle_empty_mention error: " + str(e))
|
|
```
|
|
|
|
Once the session controller is activated, messages subsequently sent by that sender will first be processed by the `empty_mention_waiter` function you defined above, until the session controller is stopped or times out.
|
|
|
|
## SessionController
|
|
|
|
Used by developers to control whether a session should end, and to retrieve message history chains.
|
|
|
|
- keep(): Keep this session alive
|
|
- timeout (float): Required. Session timeout duration.
|
|
- reset_timeout (bool): When set to True, it resets the timeout; timeout must be > 0, if <= 0 the session ends immediately. When set to False, it maintains the original timeout; new timeout = remaining timeout + timeout (can be < 0)
|
|
- stop(): End this session
|
|
- get_history_chains() -> List[List[Comp.BaseMessageComponent]]: Retrieve message history chains
|
|
|
|
## Custom Session ID Filter
|
|
|
|
By default, the AstrBot session controller uses `sender_id` (the sender's ID) as the identifier for distinguishing different sessions. If you want to treat an entire group as one session, you need to customize the session ID filter.
|
|
|
|
```py
|
|
import astrbot.api.message_components as Comp
|
|
from astrbot.core.utils.session_waiter import (
|
|
session_waiter,
|
|
SessionFilter,
|
|
SessionController,
|
|
)
|
|
|
|
# Using the handler from above
|
|
# ...
|
|
class CustomFilter(SessionFilter):
|
|
def filter(self, event: AstrMessageEvent) -> str:
|
|
return event.get_group_id() if event.get_group_id() else event.unified_msg_origin
|
|
|
|
await empty_mention_waiter(event, session_filter=CustomFilter()) # Pass in session_filter here
|
|
# ...
|
|
```
|
|
|
|
After this setup, when a user in a group sends a message, the session controller will treat the entire group as one session, and messages from other users in the group will also be considered part of the same session.
|
|
|
|
You can even use this feature to enable team-based activities within groups!
|