Files
AskaEth af8591b78a fix(channel,user): owner 车道只认白名单,堵住群主/伪造 role 冒充主人(裁决 06.4.2/06.4.3/06.4.4)
- 适配器只描述平台身份:qq_obv11 的 owner/admin 一律上报 RoleMember,不再上报 RoleOwner
- session 层唯一判主点只认白名单(owner.yaml Accounts ∪ 每渠道 adminIds),nil 档案 fail-closed 拒绝
- 新增「每渠道管理员层」:channels.json ChannelAccount.adminIds[] → runtimeconfig.AdminIDs → OwnerProfile.SetPlatformAdmins(整体替换语义,热重载后移除的 id 立即失效)
- 组合根接线:启动即播种、面板保存先灌白名单再重载适配器(cmd/cyrene/wire_channels.go + 4 条端到端单测)
- 06.4.3 节流与参与度状态按会话独立计数(ConversationKey 三处统一,空会话 GC);06.4.4 未知平台主动发言保守默认 1 条/分钟
2026-10-04 12:07:40 +08:00

74 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package adapter
// Role is the strongly typed identity role the channel layer assigns to a
// message sender. It is derived from channel-level actor metadata (login state,
// account binding, group membership) by the strategy engine, never from prompt
// text. This is a project red line: identity resolution must not rely on
// natural-language hints.
type Role string
const (
// RoleOwner is the bound owner/primary account. 只有渠道层的白名单判主可以赋这个角色
// (owner.yaml 账号白名单 + 每渠道 adminIds[],裁决 06.4.2):适配器**不得**从平台字段
// (如 OneBot 的 sender.role=owner)直接上报它——群主/群管理员都上报 RoleMember。
RoleOwner Role = "owner"
// RoleMember is a non-owner participant (for example a stranger in a group).
RoleMember Role = "member"
// RoleSystem is a system/synthetic sender (platform notice, bot).
RoleSystem Role = "system"
// RoleUnknown is a sender whose identity could not be determined.
RoleUnknown Role = "unknown"
)
// Valid reports whether r is one of the known roles.
func (r Role) Valid() bool {
switch r {
case RoleOwner, RoleMember, RoleSystem, RoleUnknown:
return true
default:
return false
}
}
// Actor is the strongly typed identity metadata attached to a message sender.
// The channel layer derives it from channel telemetry (login session, account
// binding, group membership) rather than from the message's free-text sender
// name. Owner detection and group isolation read this field, not prompt text.
type Actor struct {
// Role is the resolved identity role.
Role Role
// UserID is the stable platform user identifier.
UserID string
// DisplayName is the human-readable sender name, used for display only.
DisplayName string
// IsGroup is true when the actor represents a group/room conversation
// rather than a single user.
IsGroup bool
// GroupID is the group identifier, set when IsGroup is true.
GroupID string
// GroupName is the human-readable group name, used for display only.
GroupName string
}
// IsOwner reports whether the actor is the bound owner account.
func (a Actor) IsOwner() bool { return a.Role == RoleOwner }
// IsGroupChat reports whether the actor represents a group conversation.
func (a Actor) IsGroupChat() bool { return a.IsGroup }
// IsSystem reports whether the actor is a system/synthetic sender.
func (a Actor) IsSystem() bool { return a.Role == RoleSystem }
// IsUnknown reports whether the actor's role could not be determined.
func (a Actor) IsUnknown() bool { return a.Role == RoleUnknown }
// Resolve is a pure helper that infers IsGroup and the group identity from the
// GroupID when the channel layer has not set them explicitly, so adapters can
// populate a minimal Actor and still get correct group flags.
func (a Actor) Resolve() Actor {
if a.GroupID != "" {
a.IsGroup = true
}
return a
}