feat(backthink): 事件驱动主动消息绕过PushGate——IoT等事件该发就发(heartbeat仍受60min节流)
- trigger.go: 新增 TriggerKind(TriggerHeartbeat/TriggerEvent) + Trigger.Kind 标记触发来源 - push.go: PushRequest.TriggerKind 分流——event 跳过 gate.Allow/RecordPush(该发就发),heartbeat 仍受 60min≤1/冷却/分类限频;PushResult.Bypassed 标记 + detail=event_bypass 区分 - backthink.go: think() 传 TriggerKind + 过同事件去重门 - event_dedup.go(新): 同事件(Source+Summary)去重防刷,无 60min 节流;不同事件/心跳后重置链 - 测试: event 连续 3 次全推 bypass / heartbeat 第2次 rate_limited / event 不消耗心跳限 / 同事件去重 - build/vet/test 全绿 + windows/darwin 交叉编译通过
This commit is contained in:
1 parent
3e4efa0e41
commit
d09b25071d
6 files changed
+266
-22
No files matched your search
@@ -7,6 +7,33 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/iot"
|
||||
)
|
||||
|
||||
// TriggerKind classifies what woke a thinking pass, so the push path can apply the
|
||||
// right policy: a heartbeat is gate-governed (60min≤1 / cooldown / per-flavor), an
|
||||
// event is a real signal the owner wants to hear about and bypasses the rate gate.
|
||||
type TriggerKind int
|
||||
|
||||
const (
|
||||
// TriggerHeartbeat is the periodic autonomous reflection. Its proactive push is
|
||||
// throttled by the PushGate so the owner is never spammed by empty reflections.
|
||||
TriggerHeartbeat TriggerKind = iota
|
||||
// TriggerEvent is a real event (IoT device change, reminder, owner-relevant
|
||||
// system event). Its push bypasses the PushGate — "该发就发" — subject only to
|
||||
// same-event dedup.
|
||||
TriggerEvent
|
||||
)
|
||||
|
||||
// String returns a stable label for logs and records.
|
||||
func (k TriggerKind) String() string {
|
||||
switch k {
|
||||
case TriggerEvent:
|
||||
return "event"
|
||||
case TriggerHeartbeat:
|
||||
return "heartbeat"
|
||||
default:
|
||||
return fmt.Sprintf("triggerkind(%d)", int(k))
|
||||
}
|
||||
}
|
||||
|
||||
// Bus subjects the service subscribes to for event-triggered thinking. They are
|
||||
// the contract defined by design/12 §1: an IoT state change, a reminder firing,
|
||||
// or a generic system event.
|
||||
@@ -34,17 +61,21 @@ type Trigger struct {
|
||||
Source string
|
||||
Summary string
|
||||
Import Importance
|
||||
// Kind records whether this pass was the periodic heartbeat or a real event, so
|
||||
// the push path can bypass the rate gate for a genuine event (owner mandate).
|
||||
Kind TriggerKind
|
||||
}
|
||||
|
||||
// heartbeatTrigger is the periodic, low-importance trigger. Low importance means
|
||||
// the pass rarely interrupts the user, honouring "默认不发送 chat 类型消息".
|
||||
func heartbeatTrigger() Trigger {
|
||||
return Trigger{Source: "heartbeat", Summary: "periodic autonomous reflection", Import: ImportanceLow}
|
||||
return Trigger{Source: "heartbeat", Summary: "periodic autonomous reflection", Import: ImportanceLow, Kind: TriggerHeartbeat}
|
||||
}
|
||||
|
||||
// triggerForEvent maps a bus event to a Trigger. An IoT device change is treated
|
||||
// as normal importance, a reminder firing as high importance, and a generic
|
||||
// system event as normal importance.
|
||||
// system event as normal importance. Every event is tagged TriggerEvent so its
|
||||
// proactive push bypasses the heartbeat PushGate.
|
||||
func triggerForEvent(e bus.Event) Trigger {
|
||||
switch e.Subject {
|
||||
case iot.DeviceUpdateSubject:
|
||||
@@ -53,13 +84,14 @@ func triggerForEvent(e bus.Event) Trigger {
|
||||
Source: e.Subject,
|
||||
Summary: fmt.Sprintf("device %s (%s) %s", d.Device.ID, d.Device.Type, d.Action),
|
||||
Import: ImportanceNormal,
|
||||
Kind: TriggerEvent,
|
||||
}
|
||||
}
|
||||
return Trigger{Source: e.Subject, Summary: e.Subject, Import: ImportanceNormal}
|
||||
return Trigger{Source: e.Subject, Summary: e.Subject, Import: ImportanceNormal, Kind: TriggerEvent}
|
||||
case SubjectReminderTrigger:
|
||||
return Trigger{Source: e.Subject, Summary: e.Subject, Import: ImportanceHigh}
|
||||
return Trigger{Source: e.Subject, Summary: e.Subject, Import: ImportanceHigh, Kind: TriggerEvent}
|
||||
case SubjectSystemEvent:
|
||||
tr := Trigger{Source: e.Subject, Summary: e.Subject, Import: ImportanceNormal}
|
||||
tr := Trigger{Source: e.Subject, Summary: e.Subject, Import: ImportanceNormal, Kind: TriggerEvent}
|
||||
if sy, ok := e.Payload.(SystemEvent); ok {
|
||||
if sy.Summary != "" {
|
||||
tr.Summary = sy.Summary
|
||||
@@ -68,6 +100,6 @@ func triggerForEvent(e bus.Event) Trigger {
|
||||
}
|
||||
return tr
|
||||
default:
|
||||
return Trigger{Source: e.Subject, Summary: e.Subject, Import: ImportanceNormal}
|
||||
return Trigger{Source: e.Subject, Summary: e.Subject, Import: ImportanceNormal, Kind: TriggerEvent}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user