Files
dygou_go/internal/app/app_accounts.go
T
2026-08-08 19:30:23 +08:00

202 lines
6.5 KiB
Go

package app
import (
"fmt"
"strings"
"douyu_go/internal/logger"
"douyu_go/internal/types"
)
// ImportAccounts 将剪贴板中的账号行追加到账号文件。每行格式必须为
// 账号名----密码----Cookie,账号名不能与已有账号重复。
func (a *App) ImportAccounts(content string) types.AccountMutationResult {
entries, err := parseAccountImport(content)
if err != nil {
return types.AccountMutationResult{Error: err.Error()}
}
a.accountsMu.Lock()
defer a.accountsMu.Unlock()
accountFile := a.scheduler.AccountFilePath()
lines, err := parseAccountFile(accountFile)
if err != nil {
return types.AccountMutationResult{Error: "读取账号文件失败: " + err.Error()}
}
existingNames := make(map[string]struct{}, len(lines))
for _, line := range lines {
if line.Name != "" {
existingNames[line.Name] = struct{}{}
}
}
for _, entry := range entries {
if _, exists := existingNames[entry.Name]; exists {
return types.AccountMutationResult{Error: fmt.Sprintf("账号 %q 已存在", entry.Name)}
}
existingNames[entry.Name] = struct{}{}
lines = append(lines, entry)
}
if err := a.saveAccountLines(accountFile, lines); err != nil {
return types.AccountMutationResult{Error: err.Error()}
}
logger.LogEvent("账号", "success", "已从剪贴板导入 %d 个账号", len(entries))
return types.AccountMutationResult{Success: true, Count: len(entries)}
}
// UpdateAccount 更新账号名或 Cookie。newCookie 留空时保留文件中的原 Cookie。
func (a *App) UpdateAccount(originalName, newName, newCookie string) types.AccountMutationResult {
originalName = strings.TrimSpace(originalName)
newName = strings.TrimSpace(newName)
newCookie = strings.TrimSpace(newCookie)
if originalName == "" || newName == "" {
return types.AccountMutationResult{Error: "账号名不能为空"}
}
if invalidAccountField(newName) || strings.ContainsAny(newCookie, "\r\n") {
return types.AccountMutationResult{Error: "账号名或 Cookie 包含无效字符"}
}
a.accountsMu.Lock()
defer a.accountsMu.Unlock()
accountFile := a.scheduler.AccountFilePath()
lines, err := parseAccountFile(accountFile)
if err != nil {
return types.AccountMutationResult{Error: "读取账号文件失败: " + err.Error()}
}
targetIndex := -1
nameCount := 0
for i, line := range lines {
if line.Name == originalName {
targetIndex = i
nameCount++
}
if line.Name == newName && newName != originalName {
return types.AccountMutationResult{Error: fmt.Sprintf("账号 %q 已存在", newName)}
}
}
if targetIndex < 0 {
return types.AccountMutationResult{Error: "未找到要编辑的账号"}
}
if nameCount > 1 {
return types.AccountMutationResult{Error: "账号名重复,请先删除重复账号后再编辑"}
}
parts := strings.SplitN(strings.TrimSpace(lines[targetIndex].Raw), "----", 3)
if len(parts) != 3 {
return types.AccountMutationResult{Error: "原账号格式不正确,无法保留密码段"}
}
oldCookie := strings.TrimSpace(parts[2])
if newCookie == "" {
newCookie = oldCookie
}
if newCookie == "" {
return types.AccountMutationResult{Error: "Cookie 不能为空"}
}
lines[targetIndex] = accountLine{
Raw: newName + "----" + strings.TrimSpace(parts[1]) + "----" + newCookie,
Name: newName,
}
if err := a.saveAccountLines(accountFile, lines); err != nil {
return types.AccountMutationResult{Error: err.Error()}
}
logger.LogEvent("账号", "success", "账号已更新: %s", newName)
return types.AccountMutationResult{Success: true, Count: 1}
}
// DeleteAccounts 从账号文件中删除指定账号,可一次删除多个账号。
func (a *App) DeleteAccounts(accountNames []string) types.AccountMutationResult {
nameSet := make(map[string]struct{}, len(accountNames))
for _, name := range accountNames {
if trimmed := strings.TrimSpace(name); trimmed != "" {
nameSet[trimmed] = struct{}{}
}
}
if len(nameSet) == 0 {
return types.AccountMutationResult{Error: "请选择要删除的账号"}
}
a.accountsMu.Lock()
defer a.accountsMu.Unlock()
accountFile := a.scheduler.AccountFilePath()
lines, err := parseAccountFile(accountFile)
if err != nil {
return types.AccountMutationResult{Error: "读取账号文件失败: " + err.Error()}
}
kept := make([]accountLine, 0, len(lines))
deleted := 0
for _, line := range lines {
if _, remove := nameSet[line.Name]; remove && !line.Comment {
deleted++
continue
}
kept = append(kept, line)
}
if deleted == 0 {
return types.AccountMutationResult{Error: "未找到要删除的账号"}
}
if err := a.saveAccountLines(accountFile, kept); err != nil {
return types.AccountMutationResult{Error: err.Error()}
}
logger.LogEvent("账号", "success", "已删除 %d 个账号", deleted)
return types.AccountMutationResult{Success: true, Count: deleted}
}
func parseAccountImport(content string) ([]accountLine, error) {
content = strings.ReplaceAll(content, "\r\n", "\n")
content = strings.ReplaceAll(content, "\r", "\n")
entries := make([]accountLine, 0)
names := make(map[string]struct{})
for index, raw := range strings.Split(content, "\n") {
trimmed := strings.TrimSpace(raw)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
parts := strings.SplitN(trimmed, "----", 3)
if len(parts) != 3 {
return nil, fmt.Errorf("第 %d 行格式不正确,应为 账号名----密码----Cookie", index+1)
}
name := strings.TrimSpace(parts[0])
password := strings.TrimSpace(parts[1])
cookie := strings.TrimSpace(parts[2])
if name == "" || cookie == "" || invalidAccountField(name) || strings.ContainsAny(cookie, "\r\n") {
return nil, fmt.Errorf("第 %d 行账号名或 Cookie 无效", index+1)
}
if _, exists := names[name]; exists {
return nil, fmt.Errorf("剪贴板中账号 %q 重复", name)
}
names[name] = struct{}{}
entries = append(entries, accountLine{Raw: name + "----" + password + "----" + cookie, Name: name})
}
if len(entries) == 0 {
return nil, fmt.Errorf("未识别到可导入的账号")
}
return entries, nil
}
func invalidAccountField(value string) bool {
return strings.ContainsAny(value, "\r\n") || strings.Contains(value, "----")
}
func (a *App) saveAccountLines(accountFile string, lines []accountLine) error {
rawLines := make([]string, 0, len(lines))
for _, line := range lines {
rawLines = append(rawLines, line.Raw)
}
content := strings.Join(rawLines, "\r\n")
if content != "" {
content += "\r\n"
}
if _, err := writeFileWithBackup(accountFile, []byte(content)); err != nil {
return fmt.Errorf("更新账号文件失败: %w", err)
}
a.scheduler.LoadAccounts()
return nil
}