package session import ( "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/restrict" "git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter" "git.yeij.top/AskaEth/Cyrene_v2/internal/user" ) // ResolveActor 把一条归一化入站消息解析成下游权限决策所用的强类型 actor 元数据。 // 这是 design/11 §0 的结构化主人识别规则(裁决 06.4.2):**owner 车道只认白名单**, // 判主依据只有结构化来源——主人档案的账号白名单(owner.yaml,internal/user)与每渠道 // 账号配置的 adminIds[](channels.json,design/08 §1.1)。 // // **适配器上报的角色标记一律不得单独判主**:平台消息里的 role 字段(OneBot 的群主 // owner / 群管理员 admin,或私聊帧里伪造的 role=owner)只能当作非主人侧提示,命中 // owner 也会被降级为 RoleMember。判主永远不看消息正文——正文声称「我是主人」同样无效。 func ResolveActor(msg adapter.UnifiedMessage, owner *user.OwnerProfile) adapter.Actor { actor := adapter.Actor{ Role: msg.Sender.Role, UserID: firstNonEmpty(msg.Sender.UserID, msg.SenderID), DisplayName: firstNonEmpty(msg.Sender.DisplayName, msg.SenderName), IsGroup: msg.Sender.IsGroup || msg.IsGroup(), GroupID: firstNonEmpty(msg.Sender.GroupID, msg.GroupID), GroupName: firstNonEmpty(msg.Sender.GroupName, msg.GroupName), } // 白名单命中即主人;未命中则不管适配器上报了什么角色,都留在非主人车道 // (owner.IsOwner 对 nil 档案安全返回 false)。 if owner.IsOwner(msg.Platform, msg.SenderID) { actor.Role = adapter.RoleOwner return actor.Resolve() } actor.Role = nonOwnerRole(actor.Role) return actor.Resolve() } // nonOwnerRole 把适配器上报的角色收口到非主人车道:owner(群主 / 群管理员 / 伪造 // 字段)一律降级为 RoleMember;member / system / unknown 原样保留;非法值保守回落 // RoleMember,避免留下含糊角色。 func nonOwnerRole(role adapter.Role) adapter.Role { switch role { case adapter.RoleOwner: return adapter.RoleMember case adapter.RoleMember, adapter.RoleSystem, adapter.RoleUnknown: return role default: return adapter.RoleMember } } // EnvFor maps a resolved actor to the restrict.ActorEnv used to gate tools. // Group chats always map to group_member (isolated, restricted) even when the // owner is the speaker (design/02 §3: owner group speech stays in the isolated // group context). A non-group owner maps to owner (full access); a non-group // non-owner maps to stranger. func EnvFor(actor adapter.Actor) restrict.ActorEnv { if actor.IsGroup { return restrict.ActorGroupMember } if actor.IsOwner() { return restrict.ActorOwner } return restrict.ActorStranger } // IdentifyContext 仅凭消息自带的渠道元数据推断 restrict.ActorEnv:群聊一律 // group_member,其余一律 stranger。它拿不到白名单,因此**不能授予 owner 车道**—— // 适配器上报的 role=owner 在此被降级为 member(裁决 06.4.2,owner 只认白名单)。 // 真正的判主走 ResolveActor(msg, owner) + EnvFor。 func IdentifyContext(msg adapter.UnifiedMessage) restrict.ActorEnv { actor := msg.Sender.Resolve() if msg.IsGroup() { actor.IsGroup = true } actor.Role = nonOwnerRole(actor.Role) return EnvFor(actor) } // Env returns the restrict actor environment for the session. It mirrors // EnvFor: group chats are group_member regardless of membership, owner private // is owner, and anything else is stranger. func (s Session) Env() restrict.ActorEnv { if s.IsGroup { return restrict.ActorGroupMember } if s.Owner { return restrict.ActorOwner } return restrict.ActorStranger } // firstNonEmpty returns a when a is non-empty, otherwise b. func firstNonEmpty(a, b string) string { if a != "" { return a } return b }