func main() {
	amqpConn, err := amqp.Dial(config.AmqpURI)
	fatalError(err, "couldn't connect to mq server")
	defer amqpConn.Close()

	amqpChan, err := amqpConn.Channel()
	fatalError(err, "couldn't create mq channel")
	defer amqpChan.Close()

	amqpQueue, err := amqpChan.QueueDeclare("", false, true, true, false, nil)
	fatalError(err, "couldn't create mq queue")

	err = amqpChan.QueueBind(amqpQueue.Name, "*.message", config.AmqpExchange, false, nil)
	fatalError(err, "couldn't bind mq queue")

	amqpMessages, err := amqpChan.Consume(amqpQueue.Name, "", true, true, false, false, nil)
	fatalError(err, "couldn't consume mq messages")

	api := slack.New(config.SlackToken)
	rtm := api.NewRTM()
	go rtm.ManageConnection()

	for {
		select {
		case ev := <-rtm.IncomingEvents:
			if msg, ok := ev.Data.(*slack.MessageEvent); ok {
				if msg.SubType == "bot_message" || msg.Channel != config.SlackChannelID {
					break
				}

				user, err := rtm.GetUserInfo(msg.User)
				if err != nil {
					break
				}

				bridgeMsg := BridgeMessage{
					Sender:  user.Name,
					Content: flattenSlackMessage(rtm, msg.Text),
				}

				jsonData, err := json.Marshal(bridgeMsg)
				fatalError(err, "couldn't serialize bridge message")

				amqpMsg := amqp.Publishing{
					Body:         jsonData,
					DeliveryMode: amqp.Persistent,
				}

				fmt.Printf("[<] %s: %s\n", bridgeMsg.Sender, bridgeMsg.Content)

				err = amqpChan.Publish(config.AmqpExchange, "slack.message", false, false, amqpMsg)
				fatalError(err, "couldn't publish mq message")
			}
		case ev := <-amqpMessages:
			if ev.RoutingKey == "slack.message" {
				break
			}

			bridgeMsg := map[string]interface{}{}
			err = json.Unmarshal(ev.Body, &bridgeMsg)
			fatalError(err, "couldn't deserialize bridge message")

			slackMsg := slack.PostMessageParameters{
				Username: bridgeMsg["Sender"].(string),
				Text:     bridgeMsg["Content"].(string),
			}

			if senderAvatar, ok := bridgeMsg["SenderAvatar"]; ok {
				slackMsg.IconURL = senderAvatar.(string)
			}

			for _, interop := range config.BridgeInterop {
				isInteropMessage := true

				for key, value := range interop.MessageMatch {
					if !reflect.DeepEqual(bridgeMsg[key], value) {
						isInteropMessage = false
						break
					}
				}

				if isInteropMessage {
					regex := regexp.MustCompile(interop.MessageRegex)
					match := regex.FindAllStringSubmatch(slackMsg.Text, -1)
					if len(match) == 1 && len(match[0]) == 3 {
						slackMsg.Username = match[0][1]
						slackMsg.Text = match[0][2]
						slackMsg.IconURL = interop.AvatarURLs[slackMsg.Username]
					}
				}
			}

			fmt.Printf("[>] %s: %s\n", slackMsg.Username, slackMsg.Text)

			_, _, err = rtm.PostMessage(config.SlackChannelID, slackMsg.Text, slackMsg)
			fatalError(err, "couldn't post slack message")
		}
	}
}