feat(channel): owner 车道结构化参与闸与可调旋钮(乙 = C)
主人 2026-10-13 裁决「乙 C」= 默认按甲(群里不对她说就不开口)+ 可调旋钮。 - inbound.go: owner 不再无条件短路参与闸(去掉 `!actor.IsOwner()` 门), 主人群消息改走 shouldRespond 的 owner 车道分支;主人私聊恒回。 - ownerlane.go(新): owner 车道结构化闸 + 判据 `msg.AddressedToMe || (msg.SelfID == "" && len(msg.Mentions) > 0)` (未接线渠道退回旧判据,保护 kook/webhook 的群 @);指向她本人必回(硬保留); 非指向消息按每群旋钮——默认只进上下文不回(registerSilenced 落隔离会话)。 「少说点」类关键词落成结构化降频配置,带 source 日志。 - channelbridge.go: Options.OwnerLaneFor/OwnerLaneSet 读写接缝 + Bridge 状态。 - engage.go: 冷避词表并入「少说点」(与既有「少说两句」同义)。 - runtimeconfig/channels_ownerlane.go(新): owner_lane.json 落盘面 (默认档 + 每群覆盖,可读可改,面板可写)。 - wire_channels.go: 注入 owner_lane.json 存储($DATA_DIR/config/)。 - 测试:不对她说就不回(进上下文零回复)/ @她必回(即使降频)/ 未接线退回 旧判据 / 降频生效且可恢复 + 频率闸 / 旋钮读写接缝 / 归属不变量闭合 / 陌生人私聊与主人私聊回归。变异自证:恢复 owner 短路 ⇒ 5 条用例红。
This commit is contained in:
1 parent
3f266c26b2
commit
537a8b7988
11 files changed
+917
-21
No files matched your search
@@ -139,6 +139,14 @@ type Options struct {
|
||||
// Now 是安静时段判定所用的墙上时钟源。为 nil 时用 time.Now,
|
||||
// 使依赖时间的行为在测试中可注入。
|
||||
Now func() time.Time
|
||||
// OwnerLaneFor 是 owner 车道结构化旋钮(design/18 §11「乙 = C」)的**读取**接缝:
|
||||
// 按 (platform, groupID) 返回该群生效的参与策略,返回 nil 表示没有覆盖
|
||||
// (桥用自己的默认档:不对她说就不开口)。为 nil 时全部走默认档 + 桥内内存覆盖。
|
||||
OwnerLaneFor func(platform, groupID string) *OwnerLanePolicy
|
||||
// OwnerLaneSet 是旋钮的**落盘**接缝:主人在群里说「少说点」这类诉求被结构化后,
|
||||
// 桥通过它把新策略写进外部配置(面板可读可改的那一份)。为 nil 时只更新桥内内存
|
||||
// 覆盖(进程重启即回到默认档)。
|
||||
OwnerLaneSet func(platform, groupID string, p OwnerLanePolicy) error
|
||||
// Factory 依据某平台生效的账号配置重建其适配器
|
||||
// (channels.json → 环境变量回退)。非 nil 时桥接器支持热重载;
|
||||
// 为 nil 时桥接器仅支持启动期装配(Reload 返回 ErrNilFactory)。
|
||||
@@ -250,6 +258,19 @@ type Bridge struct {
|
||||
engageMu sync.Mutex
|
||||
engageGroups map[string]*engageGroupState
|
||||
|
||||
// ownerLane 是 owner 车道结构化旋钮(design/18 §11)的**内存覆盖表**,
|
||||
// 键 = platform|groupID。ownerLaneFor 从外部配置读取(组合根注入的 owner_lane.json),
|
||||
// ownerLaneSet 把改动回写落盘;ownerLaneLast 记录每条 owner 车道回复的时间戳,供
|
||||
// 「多频繁」闸使用。四者都由 ownerLaneMu 保护。
|
||||
ownerLane map[string]OwnerLanePolicy
|
||||
ownerLaneFor func(platform, groupID string) *OwnerLanePolicy
|
||||
ownerLaneSet func(platform, groupID string, p OwnerLanePolicy) error
|
||||
ownerLaneLast map[string]time.Time
|
||||
ownerLaneMu sync.Mutex
|
||||
// ownerLaneLog 是 owner 闸门/旋钮的子功能日志器(source=services/channelbridge/ownerlane),
|
||||
// 使「这条为什么没回」与「旋钮被改成什么」在日志里可追溯。
|
||||
ownerLaneLog *slog.Logger
|
||||
|
||||
mu sync.RWMutex
|
||||
adapters map[string]adapter.PlatformAdapter
|
||||
connected map[string]bool
|
||||
@@ -356,6 +377,11 @@ func New(opts Options) (*Bridge, error) {
|
||||
engageRand: engageRand,
|
||||
bubbleGapFn: bubbleGap,
|
||||
engageGroups: make(map[string]*engageGroupState),
|
||||
ownerLane: make(map[string]OwnerLanePolicy),
|
||||
ownerLaneFor: opts.OwnerLaneFor,
|
||||
ownerLaneSet: opts.OwnerLaneSet,
|
||||
ownerLaneLast: make(map[string]time.Time),
|
||||
ownerLaneLog: logger.WithSource(log, ownerLaneLogSource),
|
||||
adapters: make(map[string]adapter.PlatformAdapter),
|
||||
connected: make(map[string]bool),
|
||||
readCancels: make(map[string]context.CancelFunc),
|
||||
|
||||
Reference in new issue
Block a user