func handleMessage(msg tbotapi.Message, api *tbotapi.TelegramBotAPI) {
	typ := msg.Type()
	if typ != tbotapi.TextMessage {
		//ignore non-text messages for now
		return
	}
	text := *msg.Text
	if msg.Chat.IsPrivateChat() {
		fmt.Printf("<-%d, %s,\t%q\n", msg.ID, msg.Chat, text)
	} else {
		fmt.Printf("<-%d, %s(%s),\t%q\n", msg.ID, msg.Chat, msg.From, text)
	}

	if msg.Chat.IsPrivateChat() {
		//always update the list of private chats
		putChat(msg.From.ID, msg.Chat.ID)
	}

	if strings.HasPrefix(text, "/") {
		//command
		cmd := parseCommand(text)
		if cmd == cmdNew {
			game(msg, api)
			return
		}

		groupsLock.RLock()
		if c, ok := groups[msg.Chat.ID]; ok {
			c <- msg
		}
		groupsLock.RUnlock()
	} else {
		if msg.Chat.IsPrivateChat() {
			uid := msg.From.ID
			expectsLock.Lock()
			if expect, ok := expects[uid]; ok {
				switch parseChoice(text) {
				case choiceRock, choicePaper, choiceScissors:
					expect <- parseChoice(text)
					delete(expects, uid)
				default:
					reply(msg, api, "No understand")
				}
			}
			expectsLock.Unlock()
		}
	}
}
func game(msg tbotapi.Message, api *tbotapi.TelegramBotAPI) {
	if hasExpect(msg.From.ID) {
		reply(msg, api, msgAlreadyIngame)
		return
	}

	if msg.Chat.IsPrivateChat() {
		reply(msg, api, "You will play against the bot. "+msgChoose)

		eChan := make(chan choice)
		expectsLock.Lock()
		expects[msg.From.ID] = eChan
		expectsLock.Unlock()

		go func(original tbotapi.Message, api *tbotapi.TelegramBotAPI, expected chan choice) {
			choice := <-eChan

			botChoice := rand.Float64()

			var resp string
			if botChoice < (float64(1) / float64(3)) {
				resp = formatResponse("the bot", "you", choiceRock, choice)
			} else if botChoice < (float64(2) / float64(3)) {
				resp = formatResponse("the bot", "you", choicePaper, choice)
			} else {
				resp = formatResponse("the bot", "you", choiceScissors, choice)
			}

			reply(original, api, resp)
		}(msg, api, eChan)

	} else {
		//group mode

		if !hasChat(msg.From.ID) {
			reply(msg, api, msgNoPrivateChat)
			return
		}

		if hasGroup(msg.Chat.ID) {
			reply(msg, api, "This group already has an open game.")
			return
		}

		messages := make(chan tbotapi.Message)
		groups[msg.Chat.ID] = messages
		reply(msg, api, msgGameOpened)

		go func(original tbotapi.Message, api *tbotapi.TelegramBotAPI, messages chan tbotapi.Message) {
			var p1, p2 chan choice
			var partner tbotapi.User

		loop:
			for {
				msg := <-messages
				if msg.Type() != tbotapi.TextMessage {
					continue
				}
				text := *msg.Text
				switch parseCommand(text) {
				case cmdJoin:
					if msg.From.ID == original.From.ID {
						reply(original, api, msgCreatorAlreadyInGame)
					} else {
						expectsLock.Lock()
						if _, ok := expects[original.From.ID]; ok {
							reply(msg, api, msgAlreadyIngameWillRemainOpen)
							expectsLock.Unlock()
							continue loop
						}

						if _, ok := expects[msg.From.ID]; ok {
							reply(msg, api, msgAlreadyIngameWillRemainOpen)
							expectsLock.Unlock()
							continue loop
						}

						if !hasChat(msg.From.ID) {
							reply(msg, api, msgNoPrivateChat)
							expectsLock.Unlock()
							continue loop
						}

						groupsLock.Lock()
						delete(groups, original.Chat.ID)
						groupsLock.Unlock()
						p1 = make(chan choice)
						expects[original.From.ID] = p1
						p2 = make(chan choice)
						expects[msg.From.ID] = p2
						expectsLock.Unlock()

						p1Chat := chats[original.From.ID]
						p2Chat := chats[msg.From.ID]

						partner = msg.From

						sendTo(p1Chat, api, msgChoose)
						sendTo(p2Chat, api, msgChoose)
						reply(original, api, msgGameStarted)

						break loop
					}
				case cmdAbort:
					if msg.From.ID == original.From.ID {
						groupsLock.Lock()
						delete(groups, original.Chat.ID)
						groupsLock.Unlock()
						reply(original, api, msgGameAborted)
						return
					}
					reply(original, api, msgOnlyCreatorCanAbort)
				}
			}

			// game running

			var choice1, choice2 choice

		nextloop:
			for {
				select {
				case choice1 = <-p1:
					if choice2 != "" {
						break nextloop
					}
				case choice2 = <-p2:
					if choice1 != "" {
						break nextloop
					}
				}
			}

			resp := formatResponse(original.From.String(), partner.String(), choice1, choice2)

			reply(original, api, resp)

		}(msg, api, messages)
	}
}