예제 #1
0
// main runs the routines and collects
func main() {
	tfsApi, err := tfs.NewApi(os.Args[1], os.Args[2], os.Args[3])
	if err != nil {
		trace.Log(err)
		return
	}

	event, err := pullTfsAccount(tfsApi)
	if err != nil {
		trace.Log(err)
		return
	}
	fmt.Println(event)

	recv := make(chan interface{}) // routines send updates here
	quit := make(chan interface{}) // close this to have all routines return

	go pollTfsRooms(tfsApi, 1*time.Second, 60*time.Second, &recv, &quit)
	go pollCommandInterface(&recv, &quit)

	for event := range recv {
		if origin, action, params := tokenizeEvent(event.(string)); origin == "interface" {
			if action == "error" || action == "exit" || action == "logout" || action == "quit" {
				close(quit)
				return
			} else {
				roomParam, message := popToken(params)
				room, err := strconv.Atoi(roomParam)

				if err == nil {
					if action == "join" {
						err = tfsApi.JoinRoom(room)
					} else if action == "leave" {
						err = tfsApi.LeaveRoom(room)
					} else if action == "send" {
						_, err = tfsApi.SendRoomMessage(room, message)
					}
				}

				if err != nil {
					trace.Log(err)
				}
			}
		} else {
			fmt.Println(event)
		}
	}
}
예제 #2
0
// 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
		}
	}
}
예제 #3
0
// 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
		}
	}
}
예제 #4
0
// 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
		}
	}
}