feat: sync AI compose and ChatBot features
This commit is contained in:
parent
35e5d38f4d
commit
b7269b135f
75 changed files with 5426 additions and 123 deletions
149
internal/domain/ai.go
Normal file
149
internal/domain/ai.go
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
package domain
|
||||
|
||||
import "errors"
|
||||
|
||||
const (
|
||||
// AICompose 最大文本长度沿用普通消息正文限制,避免 provider 请求被客户端巨大值打爆。
|
||||
MaxAIComposeTextLength = MaxMessageTextLength
|
||||
// MaxAIComposeEntityCount 限制 TextWithEntities 的实体数量。
|
||||
MaxAIComposeEntityCount = MaxMessageEntityCount
|
||||
// AICompose tone 的 appConfig 默认值与 TDesktop/DrKLO 消费点一致。
|
||||
MaxAIComposeToneTitleLength = 12
|
||||
MaxAIComposeTonePromptLength = 1024
|
||||
AIComposeToneSavedLimitDefault = 5
|
||||
AIComposeToneSavedLimitPremium = 20
|
||||
AIComposeToneExamplesNum = 3
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAIComposeDisabled = errors.New("ai compose disabled")
|
||||
ErrAIComposeInvalid = errors.New("ai compose invalid")
|
||||
ErrAIComposeToneInvalid = errors.New("ai compose tone invalid")
|
||||
ErrAIComposeToneNotFound = errors.New("ai compose tone not found")
|
||||
ErrAIComposeToneLimitExceeded = errors.New("ai compose tone limit exceeded")
|
||||
ErrAIComposeRateLimited = errors.New("ai compose rate limited")
|
||||
ErrAIComposeProviderUnavailable = errors.New("ai compose provider unavailable")
|
||||
ErrAIComposeProviderTimeout = errors.New("ai compose provider timeout")
|
||||
)
|
||||
|
||||
type AIComposeText struct {
|
||||
Text string
|
||||
Entities []MessageEntity
|
||||
}
|
||||
|
||||
func (t AIComposeText) Clone() AIComposeText {
|
||||
out := t
|
||||
out.Entities = append([]MessageEntity(nil), t.Entities...)
|
||||
return out
|
||||
}
|
||||
|
||||
type AIComposeToneRefKind string
|
||||
|
||||
const (
|
||||
AIComposeToneRefDefault AIComposeToneRefKind = "default"
|
||||
AIComposeToneRefID AIComposeToneRefKind = "id"
|
||||
AIComposeToneRefSlug AIComposeToneRefKind = "slug"
|
||||
)
|
||||
|
||||
type AIComposeToneRef struct {
|
||||
Kind AIComposeToneRefKind
|
||||
DefaultTone string
|
||||
ID int64
|
||||
AccessHash int64
|
||||
Slug string
|
||||
}
|
||||
|
||||
func (r AIComposeToneRef) Empty() bool {
|
||||
return r.Kind == "" && r.DefaultTone == "" && r.ID == 0 && r.Slug == ""
|
||||
}
|
||||
|
||||
type AIComposeRequest struct {
|
||||
UserID int64
|
||||
Text AIComposeText
|
||||
Proofread bool
|
||||
Emojify bool
|
||||
TranslateToLang string
|
||||
Tone AIComposeToneRef
|
||||
}
|
||||
|
||||
type AIComposeResult struct {
|
||||
ResultText AIComposeText
|
||||
DiffText *AIComposeText
|
||||
}
|
||||
|
||||
type AITextGenerationRequest struct {
|
||||
UserID int64
|
||||
Text AIComposeText
|
||||
Instruction string
|
||||
}
|
||||
|
||||
type AIComposeTone struct {
|
||||
Default bool
|
||||
Creator bool
|
||||
ID int64
|
||||
AccessHash int64
|
||||
OwnerUserID int64
|
||||
Slug string
|
||||
Title string
|
||||
EmojiID int64
|
||||
Prompt string
|
||||
InstallsCount int
|
||||
AuthorID int64
|
||||
DisplayAuthor bool
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
Saved bool
|
||||
ExampleEnglish *AIComposeToneExample
|
||||
}
|
||||
|
||||
func (t AIComposeTone) Clone() AIComposeTone {
|
||||
out := t
|
||||
if t.ExampleEnglish != nil {
|
||||
ex := t.ExampleEnglish.Clone()
|
||||
out.ExampleEnglish = &ex
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type AIComposeToneExample struct {
|
||||
From AIComposeText
|
||||
To AIComposeText
|
||||
}
|
||||
|
||||
func (e AIComposeToneExample) Clone() AIComposeToneExample {
|
||||
return AIComposeToneExample{
|
||||
From: e.From.Clone(),
|
||||
To: e.To.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
type AIComposeToneInput struct {
|
||||
UserID int64
|
||||
DisplayAuthor bool
|
||||
EmojiID int64
|
||||
Title string
|
||||
Prompt string
|
||||
}
|
||||
|
||||
type AIComposeToneUpdate struct {
|
||||
Ref AIComposeToneRef
|
||||
UserID int64
|
||||
DisplayAuthor *bool
|
||||
EmojiID *int64
|
||||
Title *string
|
||||
Prompt *string
|
||||
}
|
||||
|
||||
type AIComposeTones struct {
|
||||
Hash int64
|
||||
Tones []AIComposeTone
|
||||
}
|
||||
|
||||
func (t AIComposeTones) Clone() AIComposeTones {
|
||||
out := t
|
||||
out.Tones = make([]AIComposeTone, 0, len(t.Tones))
|
||||
for _, tone := range t.Tones {
|
||||
out.Tones = append(out.Tones, tone.Clone())
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
@ -469,6 +469,9 @@ type MessageWebPage struct {
|
|||
Description string `json:"description,omitempty"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Photo *Photo `json:"photo,omitempty"`
|
||||
// ComposeToneEmojiID 映射 webPageAttributeAiComposeTone,用于 TDesktop 渲染
|
||||
// addstyle tone 分享卡片;0 表示普通网页。
|
||||
ComposeToneEmojiID int64 `json:"compose_tone_emoji_id,omitempty"`
|
||||
|
||||
ForceLargeMedia bool `json:"force_large_media,omitempty"`
|
||||
ForceSmallMedia bool `json:"force_small_media,omitempty"`
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ const (
|
|||
MessageEntityEmail MessageEntityType = "email"
|
||||
MessageEntityPhone MessageEntityType = "phone"
|
||||
MessageEntityBankCard MessageEntityType = "bank_card"
|
||||
MessageEntityDiffInsert MessageEntityType = "diff_insert"
|
||||
MessageEntityDiffReplace MessageEntityType = "diff_replace"
|
||||
MessageEntityDiffDelete MessageEntityType = "diff_delete"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -101,6 +104,8 @@ type MessageEntity struct {
|
|||
DocumentID int64
|
||||
// Collapsed 仅 blockquote 使用。
|
||||
Collapsed bool
|
||||
// OldText 仅 AI compose diff_replace 使用;普通消息输入不会接受该实体。
|
||||
OldText string
|
||||
}
|
||||
|
||||
// Message 是账号视角下的一条私聊消息。
|
||||
|
|
@ -113,6 +118,7 @@ type Message struct {
|
|||
From Peer
|
||||
Date int
|
||||
EditDate int
|
||||
HideEdited bool
|
||||
Out bool
|
||||
Silent bool
|
||||
NoForwards bool
|
||||
|
|
@ -405,6 +411,7 @@ type EditMessageRequest struct {
|
|||
Entities []MessageEntity
|
||||
Media *MessageMedia
|
||||
EditDate int
|
||||
HideEdited bool
|
||||
OriginAuthKeyID [8]byte
|
||||
OriginSessionID int64
|
||||
// SetReplyMarkup 置位时替换 reply_markup(ReplyMarkup 为 nil/空 = 清空键盘);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ const (
|
|||
StickersBotUserID int64 = 1063110917
|
||||
// StickersBotAccessHash 固定不变;与 postgres 种子行双写,必须保持一致。
|
||||
StickersBotAccessHash int64 = 5213187021149032991
|
||||
|
||||
// ChatBotUserID 是内置 @ChatBot 账号。它把私聊文本转给 server AI provider 链。
|
||||
ChatBotUserID int64 = 1250000007
|
||||
// ChatBotAccessHash 固定不变;与 postgres 种子行双写,必须保持一致。
|
||||
ChatBotAccessHash int64 = 6332902371644871201
|
||||
)
|
||||
|
||||
// OfficialSystemUser 返回第一阶段内置的官方系统账号。
|
||||
|
|
@ -55,6 +60,19 @@ func StickersBotUser() User {
|
|||
}
|
||||
}
|
||||
|
||||
// ChatBotUser 返回内置 @ChatBot 账号。
|
||||
func ChatBotUser() User {
|
||||
return User{
|
||||
ID: ChatBotUserID,
|
||||
AccessHash: ChatBotAccessHash,
|
||||
FirstName: "ChatBot",
|
||||
Username: "ChatBot",
|
||||
Verified: true,
|
||||
Bot: true,
|
||||
BotInfoVersion: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// SystemUserByID 返回内置系统账号;非系统账号返回 ok=false。
|
||||
// 所有对 777000 的硬编码注入点统一经此函数,新增内置账号只改这里。
|
||||
func SystemUserByID(id int64) (User, bool) {
|
||||
|
|
@ -65,6 +83,8 @@ func SystemUserByID(id int64) (User, bool) {
|
|||
return BotFatherUser(), true
|
||||
case StickersBotUserID:
|
||||
return StickersBotUser(), true
|
||||
case ChatBotUserID:
|
||||
return ChatBotUser(), true
|
||||
}
|
||||
return User{}, false
|
||||
}
|
||||
|
|
@ -76,7 +96,7 @@ func IsSystemUserID(id int64) bool {
|
|||
|
||||
func SystemUserByPhone(phone string) (User, bool) {
|
||||
phone = NormalizePhone(phone)
|
||||
for _, id := range []int64{OfficialSystemUserID, BotFatherUserID, StickersBotUserID} {
|
||||
for _, id := range []int64{OfficialSystemUserID, BotFatherUserID, StickersBotUserID, ChatBotUserID} {
|
||||
u, ok := SystemUserByID(id)
|
||||
if !ok || u.Phone == "" {
|
||||
continue
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue