Spaces:
Paused
Paused
| 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) | |
| } | |
| } | |