             radiantTowers = _GetTowers(match.tower_status_radiant).join(", ") or "None"
             direTowers = _GetTowers(match.tower_status_dire).join(", ") or "None"
 
-            msg.send "Match ID #{match.match_id} is a #{DOTA_LOBBIES[match.lobby_type].toLowerCase()} game that took place #{date}. The #{victor} won the game in #{duration} minutes. First blood was drawn #{firstBlood} into the game."
+            res.send "Match ID #{match.match_id} is a #{DOTA_LOBBIES[match.lobby_type].toLowerCase()} game that took place #{date}. The #{victor} won the game in #{duration} minutes. First blood was drawn #{firstBlood} into the game."
 
             # Generate map image and graphs?
-            msg.send "Radiant towers remaining: #{radiantTowers} | Dire towers remaining: #{direTowers}"
-            msg.send "Dotabuff: http://www.dotabuff.com/matches/#{match.match_id}"
+            res.send "Radiant towers remaining: #{radiantTowers} | Dire towers remaining: #{direTowers}"
+            res.send "Dotabuff: http://www.dotabuff.com/matches/#{match.match_id}"
 
             additionalInfo = (steamID, type = "Steam ID") ->
                 communityID = _GetCommunityID steamID
                 target = _GetPlayer communityID, match.players
                 unless target
-                    msg.reply "The #{type} you have entered (\"#{genericID}\") was not found in Match ID #{match.match_id}."
+                    res.reply "The #{type} you have entered (\"#{genericID}\") was not found in Match ID #{match.match_id}."
                     return
                 faction = _GetFaction target.player_slot
                 hero = DOTA_HEROES[target.hero_id] or "No hero"
-                msg.reply "#{faction} - #{hero} (Lvl #{target.level}) | KDA: #{target.kills}/#{target.deaths}/#{target.assists} | LH: #{target.last_hits} | GPM: #{target.gold_per_min} | XPM: #{target.xp_per_min} | HD: #{target.hero_damage} | TD: #{target.tower_damage} | TGE: #{target.gold_per_min*duration}"
+                res.reply "#{faction} - #{hero} (Lvl #{target.level}) | KDA: #{target.kills}/#{target.deaths}/#{target.assists} | LH: #{target.last_hits} | GPM: #{target.gold_per_min} | XPM: #{target.xp_per_min} | HD: #{target.hero_damage} | TD: #{target.tower_damage} | TGE: #{target.gold_per_min*duration}"
                 # Future (TODO): Team fight contribution? Overall contribution algo.
 
             if genericID?
                 if _IsSteamID genericID
                     additionalInfo steamID
                 else
-                    GetSteamID msg, genericID, (steamID) ->
+                    GetSteamID res, genericID, (steamID) ->
                         additionalInfo steamID, "profile URL"
#
# Steam API
#
GetSteamResult = (msg, endpoint, params = {}, handler, version = 1) ->
    params.key = STEAM_API_KEY

    msg.http("#{STEAM_API_URL}/#{endpoint}/v#{version}/")
        .query(params)
        .get() (err, res, body) ->
            if err or res.statusCode isnt 200
                err = "Bad request (invalid Steam web API key)" if res.statusCode is 400
                msg.reply "An error occurred while attempting to process your request. Please try again later."
                log = if msg.logger? then msg.logger else msg.robot.logger
                return log.error err
            handler JSON.parse(body)

#
# User API
#
GetSteamID = (msg, customURL, callback) ->
    # Retrieve from cache
    return callback steamID for steamID, user of _GetSteamData() when user.url is customURL

    # Retrieve from API
    GetSteamResult msg, "ISteamUser/ResolveVanityURL", vanityurl: customURL, (object) ->
        if object.response.success is 42
            msg.reply "The custom URL you have entered (\"#{customURL}\") does not exist."
            return

        # Cache results

#
# Dota API
#
GetHeroes = (res, callback) ->
    GetSteamResult res, "IEconDOTA2_570/GetHeroes", language: "en", (object) ->
        mappedHeroes = {}
        for hero in object.result.heroes
            mappedHeroes[hero.id] = hero.localized_name
        callback mappedHeroes

GetMatchDetails = (res, matchID, callback) ->
    GetSteamResult res, "IDOTA2Match_570/GetMatchDetails", match_id: matchID, (object) ->
        callback object.result

GetMatchHistory = (res, genericID, callback) ->
    history = (steamID, type = "Steam ID") ->
        params =
            account_id: steamID
            matches_requested: DOTA_MAX_RESULTS
        GetSteamResult res, "IDOTA2Match_570/GetMatchHistory", params, (object) ->
            if object.result.status is 15
                res.reply "The user has disabled the \"Expose Public Match Data\" option."
                return
            else if object.result.num_results is 0
                res.reply "No game matches were found for the #{type}: #{genericID}."
            callback steamID, object.result

    # Steam ID
