File size: 5,879 Bytes
c4baeb2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | package job
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"sync"
"time"
"pplx2api/config"
"pplx2api/core"
)
const (
// ConfigFileName is the name of the file to store sessions
ConfigFileName = "sessions.json"
)
var (
sessionUpdaterInstance *SessionUpdater
sessionUpdaterOnce sync.Once
)
// SessionConfig represents the structure to be saved to file
type SessionConfig struct {
Sessions []config.SessionInfo `json:"sessions"`
}
// SessionUpdater 管理 Perplexity 会话的定时更新
type SessionUpdater struct {
interval time.Duration
stopChan chan struct{}
isRunning bool
runningLock sync.Mutex
configPath string
}
// NewSessionUpdater 创建一个新的会话更新器
// interval: 更新间隔时间
func GetSessionUpdater(interval time.Duration) *SessionUpdater {
sessionUpdaterOnce.Do(func() {
// 使用当前文件夹下的配置文件
configPath := ConfigFileName
sessionUpdaterInstance = &SessionUpdater{
interval: interval,
stopChan: make(chan struct{}),
isRunning: false,
configPath: configPath,
}
// 初始化时从文件加载会话
sessionUpdaterInstance.loadSessionsFromFile()
})
return sessionUpdaterInstance
}
// loadSessionsFromFile loads sessions from the config file if it exists
func (su *SessionUpdater) loadSessionsFromFile() {
// Check if file exists
if _, err := os.Stat(su.configPath); os.IsNotExist(err) {
log.Println("No sessions config file found, will create on first update")
return
}
// Read the file
data, err := ioutil.ReadFile(su.configPath)
if err != nil {
log.Printf("Failed to read sessions config file: %v", err)
return
}
// Parse the JSON
var sessionConfig SessionConfig
if err := json.Unmarshal(data, &sessionConfig); err != nil {
log.Printf("Failed to parse sessions config file: %v", err)
return
}
// Update the config with loaded sessions
config.ConfigInstance.RwMutex.Lock()
config.ConfigInstance.Sessions = sessionConfig.Sessions
config.ConfigInstance.RwMutex.Unlock()
log.Printf("Loaded %d sessions from config file", len(sessionConfig.Sessions))
}
// saveSessionsToFile saves the current sessions to the config file
func (su *SessionUpdater) saveSessionsToFile() error {
// Get current sessions
config.ConfigInstance.RwMutex.RLock()
sessionsCopy := make([]config.SessionInfo, len(config.ConfigInstance.Sessions))
copy(sessionsCopy, config.ConfigInstance.Sessions)
config.ConfigInstance.RwMutex.RUnlock()
// Create config structure
sessionConfig := SessionConfig{
Sessions: sessionsCopy,
}
// Convert to JSON
data, err := json.MarshalIndent(sessionConfig, "", " ")
if err != nil {
return err
}
// Write to file
err = ioutil.WriteFile(su.configPath, data, 0644)
if err != nil {
return err
}
log.Printf("Saved %d sessions to sessions.json file", len(sessionsCopy))
return nil
}
// Start 启动定时更新任务
func (su *SessionUpdater) Start() {
su.runningLock.Lock()
defer su.runningLock.Unlock()
if su.isRunning {
log.Println("Session updater is already running")
return
}
su.isRunning = true
su.stopChan = make(chan struct{})
go su.runUpdateLoop()
log.Println("Session updater started with interval:", su.interval)
}
// Stop 停止定时更新任务
func (su *SessionUpdater) Stop() {
su.runningLock.Lock()
defer su.runningLock.Unlock()
if !su.isRunning {
log.Println("Session updater is not running")
return
}
close(su.stopChan)
su.isRunning = false
log.Println("Session updater stopped")
}
// runUpdateLoop 运行更新循环
func (su *SessionUpdater) runUpdateLoop() {
ticker := time.NewTicker(su.interval)
defer ticker.Stop()
// 立即执行一次更新
// su.updateAllSessions()
for {
select {
case <-ticker.C:
su.updateAllSessions()
case <-su.stopChan:
log.Println("Update loop terminated")
return
}
}
}
// updateAllSessions 更新所有会话
func (su *SessionUpdater) updateAllSessions() {
log.Println("Starting session update for all sessions...")
// 复制当前会话列表,避免长时间持有锁
config.ConfigInstance.RwMutex.RLock()
sessionsCopy := make([]config.SessionInfo, len(config.ConfigInstance.Sessions))
copy(sessionsCopy, config.ConfigInstance.Sessions)
proxy := config.ConfigInstance.Proxy
config.ConfigInstance.RwMutex.RUnlock()
// 如果没有会话需要更新,直接返回
if len(sessionsCopy) == 0 {
log.Println("No sessions to update")
return
}
// 创建更新后的会话切片
updatedSessions := make([]config.SessionInfo, len(sessionsCopy))
var wg sync.WaitGroup
// 对每个会话执行更新
for i, session := range sessionsCopy {
wg.Add(1)
go func(index int, origSession config.SessionInfo) {
defer wg.Done()
// 创建客户端并更新 cookie
// 写死 model 和 openSearch 参数
client := core.NewClient(origSession.SessionKey, proxy, "claude-3-opus-20240229", false)
newCookie, err := client.GetNewCookie()
if err != nil {
log.Printf("Failed to update session %d: %v", index, err)
// 如果更新失败,保留原始会话
updatedSessions[index] = origSession
return
}
// 创建更新后的会话对象
updatedSessions[index] = config.SessionInfo{
SessionKey: newCookie,
}
}(i, session)
}
// 等待所有更新完成
wg.Wait()
// 一次性替换所有会话
config.ConfigInstance.RwMutex.Lock()
config.ConfigInstance.Sessions = updatedSessions
config.ConfigInstance.RwMutex.Unlock()
log.Printf("All %d sessions have been updated", len(updatedSessions))
// 保存更新后的配置到文件
if err := su.saveSessionsToFile(); err != nil {
log.Printf("Failed to save updated config: %v", err)
}
}
|