// routine to poll message information for a given room at variable intervals func pollTfsRoomMessages(room *tfs.Room, tfsApi *tfs.Api, min time.Duration, max time.Duration, send *chan interface{}, recv *chan interface{}) { delay := min messageMap := make(map[int]*tfs.RoomMessage) messageLast := room.LastActivity timer := time.NewTimer(delay) defer timer.Stop() for { select { case <-timer.C: delay = delay * 2 if delay > max { delay = max } messages, err := tfsApi.GetRoomMessages(room, messageLast) if err != nil { trace.Log(err) *send <- fmt.Sprintf("messages error %d %s", room.Id, err) return } // new for _, message := range messages.Value { if _, ok := messageMap[message.Id]; ok { continue } json, err := json.Marshal(message) if err != nil { trace.Log(err) continue } *send <- fmt.Sprintf("messages new %d %s %d %s", message.PostedRoomId, message.PostedBy.Id, message.Id, json) messageMap[message.Id] = message messageLast = message.PostedTime delay = min } timer.Reset(delay) case <-*recv: // quit return } } }
// routine to poll user information for a given room at variable intervals func pollTfsRoomUsers(room *tfs.Room, tfsApi *tfs.Api, min time.Duration, max time.Duration, send *chan interface{}, recv *chan interface{}) { delay := min userMap := make(map[string]*tfs.RoomUser) timer := time.NewTimer(delay) defer timer.Stop() for { select { case <-timer.C: delay = delay * 2 if delay > max { delay = max } users, err := tfsApi.GetRoomUsers(room) if err != nil { trace.Log(err) *send <- fmt.Sprintf("users error %d %s", room.Id, err) return } // added newUserMap := make(map[string]*tfs.RoomUser) for _, user := range users.Value { newUserMap[user.User.Id] = user if _, ok := userMap[user.User.Id]; ok { continue } json, err := json.Marshal(user) if err != nil { trace.Log(err) continue } *send <- fmt.Sprintf("users add %d %s %s", user.RoomId, user.User.Id, json) userMap[user.User.Id] = user delay = min } // removed for _, user := range userMap { if _, ok := newUserMap[user.User.Id]; ok { continue } *send <- fmt.Sprintf("users remove %d %s", user.RoomId, user.User.Id) delete(userMap, user.User.Id) delay = min } // changed for id, user := range newUserMap { if newUserMap[id].IsOnline != userMap[id].IsOnline { json, err := json.Marshal(*user) if err != nil { trace.Log(err) continue } userMap[id] = newUserMap[id] *send <- fmt.Sprintf("users change %d %s %s", user.RoomId, user.User.Id, json) delay = min } } timer.Reset(delay) case <-*recv: // quit return } } }
// routine to poll room information at variable intervals func pollTfsRooms(tfsApi *tfs.Api, min time.Duration, max time.Duration, send *chan interface{}, recv *chan interface{}) { delay := min roomRecv := make(chan interface{}) // routines started here we'll be proxied roomQuit := make(map[int]*chan interface{}) // we'll close this to have routines we started return roomMap := make(map[int]*tfs.Room) // defer close(roomRecv) // TODO: will cause a panic if network errors cause this to be closed before other routines quit, but will block a routine if we don't timer := time.NewTimer(delay) defer timer.Stop() for { select { case <-timer.C: // tick tock delay = delay * 2 if delay > max { delay = max } rooms, err := tfsApi.GetRooms() if err != nil { trace.Log(err) for _, quit := range roomQuit { // clean up on error close(*quit) // routines should return on this being closed } *send <- fmt.Sprintf("rooms error %s", err) return } // added newRoomMap := make(map[int]*tfs.Room) for _, room := range rooms.Value { newRoomMap[room.Id] = room if _, ok := roomMap[room.Id]; ok { continue } json, err := json.Marshal(room) if err != nil { trace.Log(err) continue } *send <- fmt.Sprintf("rooms add %d %s", room.Id, json) roomMap[room.Id] = room q := make(chan interface{}) roomQuit[room.Id] = &q // http://stackoverflow.com/questions/25601802/why-does-inline-instantiation-of-variable-requires-explicitly-taking-the-address go pollTfsRoomUsers(room, tfsApi, min, max/2, &roomRecv, roomQuit[room.Id]) go pollTfsRoomMessages(room, tfsApi, min, max/4, &roomRecv, roomQuit[room.Id]) delay = min } // removed for _, room := range roomMap { if _, ok := newRoomMap[room.Id]; ok { continue } close(*roomQuit[room.Id]) delete(roomMap, room.Id) delete(roomQuit, room.Id) *send <- fmt.Sprintf("rooms remove %d", room.Id) delay = min } timer.Reset(delay) case event := <-roomRecv: // relay send or clean up from a routine that errored _, action, params := tokenizeEvent(event.(string)) if action == "error" { // routine error room, _ := strconv.Atoi(params) quit, ok := roomQuit[room] if ok { // clean up routines close(*quit) delete(roomMap, room) delete(roomQuit, room) *send <- fmt.Sprintf("rooms remove %d", room) } } else { *send <- event // relay } case <-*recv: // we need to quit for _, quit := range roomQuit { // clean up routines close(*quit) // routines should return on this being closed } return } } }