feat(channel/session): 渠道↔会话映射 + 主人识别(actor元数据+白名单,不读消息内容) + 群聊隔离 + 主动推送策略(iLink24h/OneBot限频)
This commit is contained in:
1 parent
3ce9489f8e
commit
7adaf490c7
7 files changed
+1133
No files matched your search
@@ -0,0 +1,80 @@
|
||||
// Package session maps IM channels to internal sessions and resolves the
|
||||
// strongly typed actor identity that gates every downstream permission decision.
|
||||
//
|
||||
// It is the M6 home of three cross-cutting concerns wired together at the
|
||||
// channel layer, per DevDocs/design/11 §0/§2:
|
||||
//
|
||||
// - Channel↔session mapping: a persistent (platform, channel_id) → session_id
|
||||
// map (SQLite table channel_sessions), so the same conversation always maps
|
||||
// to the same internal session across restarts.
|
||||
// - Owner recognition: a structural, never-prompt rule. ResolveActor decides
|
||||
// owner vs group_member vs stranger purely from channel-level actor metadata
|
||||
// matched against the user.OwnerProfile account whitelist. Nothing here reads
|
||||
// message text to infer identity — that is a project red line.
|
||||
// - Proactive push policy: per-platform rules from design/08 §3 (weixin-ilink
|
||||
// 24h window, OneBot burst rate limit, telegram/webhook unrestricted).
|
||||
//
|
||||
// The package is concurrency-safe: the manager guards its cache with an
|
||||
// RWMutex and the rate limiter uses its own mutex, so multiple adapters may
|
||||
// resolve sessions and issue pushes concurrently.
|
||||
package session
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
||||
)
|
||||
|
||||
// Package-level sentinel errors. Each is wrapped with %w at the call site so
|
||||
// callers can use errors.Is.
|
||||
var (
|
||||
// ErrNilDB is returned when SessionManager is built with a nil database.
|
||||
ErrNilDB = errors.New("session: nil database handle")
|
||||
// ErrInvalidChannel is returned when a message has no usable channel id.
|
||||
ErrInvalidChannel = errors.New("session: empty channel id")
|
||||
)
|
||||
|
||||
// sessionIDPrefix distinguishes generated session ids from foreign identifiers.
|
||||
const sessionIDPrefix = "sess-"
|
||||
|
||||
// Session is the resolved channel↔session binding for one inbound message. It
|
||||
// carries the identity metadata (ActorRole, Owner, IsGroup) that the policy
|
||||
// engine and memory layer read, plus the isolation flag MemWritable which says
|
||||
// whether this conversation may write to shared memory.
|
||||
//
|
||||
// The memory rule follows design/02 §3 and design/11 §2: only the owner's
|
||||
// private conversation may read/write memory shared with the Web main session;
|
||||
// group chats and stranger conversations may read memory read-only and may
|
||||
// never persist anything that could leak owner-sensitive information.
|
||||
type Session struct {
|
||||
// Platform is the normalized platform identifier (e.g. "weixin-ilink").
|
||||
Platform string
|
||||
// ChannelID is the stable channel key used for session mapping.
|
||||
ChannelID string
|
||||
// SessionID is the internal session binding the channel maps to.
|
||||
SessionID string
|
||||
// ActorRole is the strongly typed role of the resolved actor.
|
||||
ActorRole adapter.Role
|
||||
// Owner reports whether the actor is the bound owner account.
|
||||
Owner bool
|
||||
// IsGroup reports whether this is a group/room conversation.
|
||||
IsGroup bool
|
||||
// MemWritable reports whether this conversation may write shared memory.
|
||||
// It is true only for the owner's private conversation.
|
||||
MemWritable bool
|
||||
}
|
||||
|
||||
// newSessionID returns a random, collision-resistant session identifier.
|
||||
func newSessionID() string {
|
||||
var b [16]byte
|
||||
if _, err := rand.Read(b[:]); err != nil {
|
||||
// rand.Read should never fail; fall back to a nanosecond-named id so a
|
||||
// session can still be created rather than failing the whole call.
|
||||
return fmt.Sprintf("%s%d", sessionIDPrefix, time.Now().UnixNano())
|
||||
}
|
||||
return sessionIDPrefix + hex.EncodeToString(b[:])
|
||||
}
|
||||
Reference in new issue
Block a user