Mhanndalorian Bot

Welcome to the Mhanndalorian Bot, a Discord bot for the mobile game Star Wars Galaxy of Heroes. Now offering the Mhanndalorian API for developers.

The Mhanndalorian API is a special API able to provide read only access to game data that requires authentication to get. The use of this API is offered through the Patreon subscription tiers listed below and requires an approval process.

Developer API Access Membership


$1
per month
MUST APPLY FOR
Developer API Access Membership will allow you access to the following features:
  • The SWGOH Registry, a collective database of player's SWGOH allycodes and their Discord user IDs.
  • Retrieve authenticated game data for your accounts and your end users accounts1 for the following data.
    • Territory War Battle Logs
    • Territory War Leaderboards
    • Territory Battles Battle Logs
    • Territory Battles Leaderboards
    • Active Raids
    • Player Inventory
    • Grand Arena Championships
If you are interested in this subscription tier please contact one of the following people in the
Mhanndalorian Bot Discord
Bot Mhann#2253

1Access to end user accounts is only upon approval of your intended usage based on a thorough review of your tool during the application process.

Getting Started

Getting access to the api

  1. In your Patreon account go to Settings and link your Discord account. This will make sure you gain access to all Subscriber benefits in Discord.
  2. Join the Mhanndalorian Bot Discord server and contact one of the individuals listed in the membership tier.
  3. Upon approval of your usage you will be instructed how to purchase the subscription.
  4. Install Mhanndalorian Bot into the support Discord for your tool. All the default permissions are necessary for the bot to function properly.
  5. After installing Mhanndalorian Bot, you will need to run the following commands in the discord channel: /register and enter your ally code. You must verify your account following the instructions from the bot.
  6. Once registered, your api key will be generated and sent to you when completed.

Using the api

All users must register with the Mhanndalorian Bot and run the /eaconnect command in the Discord channel for the bot to explicitly allow access to the authenticated game data for their verified ally code. In order to access the endpoints, you will need to form an HTTP request as shown below. All data will be returned to you in JSON format. There are two options for generating API calls. Option #1 is simpler to setup, but is less secure as your API key is transmitted in the header. Option #2 requires additional setup, but is very secure as it uses an HMAC signature and your API key is not transmitted. Below, there are additional details regarding how to generate the HMAC signature along with various code examples.

Generating the call

Request Method: POST

HEADERS (option #1 - less secure):
    Content-Type: application/json
    Accept-Encoding: br,gzip,deflate
    api-key: your-api-key
    x-discord-id: Your Discord Id *

HEADERS (option #2 - very secure):
    Content-Type: application/json
    Accept-Encoding: br,gzip,deflate
    x-timestamp: Unix timestamp in milliseconds
    Authorization: HMAC signature
    x-discord-id: Your Discord Id *

BODY (All endpoints except /api/guild):
  { "payload": { 
      "allyCode": "561963325" //Ally code you are looking up
      "enums": boolean
    } 
  }
        
BODY (/api/guild endpoint):
  { "payload": { 
      "guildId": "B2-VYdu3SEevuO3NTCW52Q" //Guild being looked up
      "enums": boolean
    } 
  }
        
*x-discord-id header field is required if:

Endpoints

All endpoints are documented below. Clicking an endpoint will show a sample response. Some field values have been replaced with "removed" but are available in an actual response. Requests will be sent as a POST to the following url, adding the desired endpoint to the end of it:

https://mhanndalorianbot.work/api

Authenticated Endpoints (breaks connection):
/activeraid
Fetches data about the current raid.
/events
Fetches data about current and upcoming events in game.
/gac
Fetches Grand Arena Championship map data of all squads placed on defense, active match data, and event leaderboard data.
/inventory
Fetches inventory including unequipped mods, currencies, materials, and gear.
/leaderboard
Fetches data about current squad arena and fleet arena leaderboard.
/squadfetch
Fetches all saved squad and fleet loadouts from your account.
/tb
Fetches Territory Battle data including platoons and leaderboards.
/tbleaderboardhistory
Fetches historical leaderboard data for the last 4 TBs.
/tblogs
Fetches TB log data (Details about every action that happens in TB).
/tw
Fetches Territory War data.
/twleaderboard
Fetches Territory War leaderboard data for the guild including banners earned for attack, defend, total and rogue actions.
/twlogs
Fetches Territory War log data including attacks, deployments, holds, and preloads.

Non-Authenticated Endpoints (Does not break connection):
/database
Used to interact with the SWGOH registry. This endpoint works a bit different than other endpoints. See documentation here.
/guild
Fetches guild data including members (with brief details), recentRaidResult, recentTerritoryWarResult, profile, and guild reset data.
/player
Fetches player data including name, playerId, allyCode, guildId, guildName, lastActivityTime, profileStat, playerRating, rosterUnits, datacrons, and more.

HMAC Details

To prevent tampering with messages, replay attacks, unauthorized access, and ensure API key security, an implementation of hash message authentication code (HMAC) is provided. When using the API with an HMAC signaure, the api-key field is removed from the headers and the Authorization and x-timestamp fields are included. The Authorization field contains the HMAC signature and the x-timestamp field is the Unix epoch time in milliseconds.

The SHA256 hash algorithm is used, when creating the HMAC signature. The HMAC should add these attributes of the message in this order:
The hex digest of this process should be included as the Signature in the Authorization header. Below are some examples of how to generate the HMAC in various languages along with some sample data to check your HMAC algorithm against. Thank you to Mar Trepodi, Kidori, and Jordan for helping to write these examples along with testing the sample data.

const crypto = require('crypto'); function generateHMAC(apiKey, method, payload, endpoint) {
// Generate Unix epoch time in milliseconds const timestamp = Date.now(); // Create a base HMAC object using SHA256 algorithm const hmac = crypto.createHmac('sha256', apiKey); // Add the request timestamp to the HMAC object hmac.update(timestamp.toString()); // Add the HTTP method (in upper case) to the HMAC object hmac.update(method.toUpperCase()); // Add the API endpoint URI (in lower case) to the HMAC object hmac.update(endpoint.toLowerCase()); // Create a serialized string from the payload JSON const payloadString = JSON.stringify(payload); // Generate MD5 hash of the payload string const payloadHash = crypto
.createHash('md5') .update(payloadString) .digest('hex');
// Add the payload hash to the HMAC object hmac.update(payloadHash); // Calculate the HMAC signature const HMACSignature = hmac.digest('hex'); // Return the HMAC signature and timestamp return {x-timestamp: timestamp, Authorization: HMACSignature};
}
import time import hashlib import hmac def hmac_sign(api_key: str, method: str, endpoint: str, payload: dict) -> dict[str, str]:
"""HMAC signing function for Mhanndalorian Bot APIs.
Args:
api_key: The API key assigned by Mhanndalorian Bot method: The HTTP method of the request endpoint: The API endpoint URI payload: Dictionary containing the payload of the HTTP request
Returns:
Dictionary containing the 'x-timestamp' and 'Authorization' headers for the signed request
""" # Get the current time in milliseconds req_time = str(int(time.time() * 1000)) # Create a base HMAC object using SHA256 algorithm hmac_obj = hmac.new(key=api_key.encode(), digestmod=hashlib.sha256) # Add the request timestamp to the HMAC object hmac_obj.update(req_time.encode()) # Add the HTTP method (in upper case) to the HMAC object hmac_obj.update(method.upper().encode()) # Add the API endpoint URI to the HMAC object hmac_obj.update(endpoint.lower().encode()) # Create a serialized string from the payload JSON/dictionary object payload_str = dumps(payload, separators=(',', ':')) # Generate MD5 hash of the payload string payload_hash = hashlib.md5(payload_str.encode()).hexdigest() # Add the payload MD5 hash to the HMAC object hmac_obj.update(payload_hash.encode()) # Return the HTTP headers that must be included with the HMAC signed request return {"x-timestamp": req_time, "Authorization": hmac_obj.hexdigest()}
private Map<String, Object> generateHMAC(String apiKey, String method, Object payload, String endpoint) throws NoSuchAlgorithmException, JsonProcessingException {
// Generate Unix epoch time in milliseconds long timestamp = Instant.now().toEpochMilli(); // Create a base HMAC object using SHA256 algorithm SecretKeySpec secretKeySpec = new SecretKeySpec(apiKey.getBytes(), "HmacSHA256"); Mac mac = Mac.getInstance(secretKeySpec.getAlgorithm()); mac.init(secretKeySpec); // Add the request timestamp to the HMAC object mac.update(String.valueOf(timestamp).getBytes()); // Add the HTTP method (in upper case) to the HMAC object mac.update(method.toUpperCase().getBytes()); // Add the API endpoint URI (in lower case) to the HMAC object mac.update(endpoint.toLowerCase().getBytes()); // Create a serialized string from the payload JSON ObjectMapper objectMapper = new ObjectMapper(); String payloadString = objectMapper.writeValueAsString(payload); // Generate MD5 hash of the payload string MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(payloadString.getBytes()); byte[] payloadHash = digest.digest(); // Add the payload hash to the HMAC object mac.update(payloadHash); // Calculate the HMAC signature String HMACSignature = new String(mac.doFinal()); // Return the HMAC signature and timestamp return Map.of(
"x-timestamp", timestamp, "Authorization", HMACSignature
);
}
public class HMACSigner {
/// <summary> /// HMAC signing function for Mhanndalorian Bot APIs. /// /// Args: /// api_key: The API key assigned by Mhanndalorian Bot /// method: The HTTP method of the request /// endpoint: The API endpoint URI /// payload: Dictionary containing the payload of the HTTP request /// /// Returns: /// Dictionary containing the 'x-timestamp' and 'Authorization' headers for the signed request /// </summary> public static Dictionary hmac_sign(string api_key, string method, string endpoint, Dictionary payload) {
// Get the current time in milliseconds string req_time = ((long)(DateTimeOffset.Now.ToUnixTimeMilliseconds())).ToString(); // Create a base HMAC object using SHA256 algorithm with the API key as key // In C#, we'll use HMACSHA256 which computes the HMAC in one shot. // We'll simulate sequential updates by concatenating all parts into one message. // Add the request timestamp to the message string message = req_time; // Add the HTTP method (in upper case) to the message message += method.ToUpperInvariant(); // Add the API endpoint URI (in lower case) to the message message += endpoint.ToLowerInvariant(); // Create a serialized string from the payload JSON/dictionary object using minimal separators // In System.Text.Json the default is a minified JSON string. string payload_str = JsonSerializer.Serialize(payload); // Generate MD5 hash of the payload string using (MD5 md5 = MD5.Create()) {
byte[] payloadBytes = Encoding.UTF8.GetBytes(payload_str); byte[] md5HashBytes = md5.ComputeHash(payloadBytes); // Convert MD5 hash bytes to hexadecimal string (lowercase) StringBuilder sb = new StringBuilder(); foreach (byte b in md5HashBytes) {
sb.Append(b.ToString("x2"));
} string payload_hash = sb.ToString(); // Add the payload MD5 hash to the message message += payload_hash;
} // Compute the HMAC SHA256 signature using the concatenated message byte[] keyBytes = Encoding.UTF8.GetBytes(api_key); byte[] messageBytes = Encoding.UTF8.GetBytes(message); string authorization; using (HMACSHA256 hmac = new HMACSHA256(keyBytes)) {
byte[] hashBytes = hmac.ComputeHash(messageBytes); // Convert HMAC hash bytes to hexadecimal string (lowercase) StringBuilder sb = new StringBuilder(); foreach (byte b in hashBytes) {
sb.Append(b.ToString("x2"));
} authorization = sb.ToString();
} // Return the HTTP headers that must be included with the HMAC signed request return new Dictionary {
{ "x-timestamp", req_time }, { "Authorization", authorization }
};
}
}

Sample Data

apiKey = "abcd12345" method = "POST" payload = {"payload":{"allyCode":"123456789","enums":false}} timeStamp = "1738865880757" endpoint = "/api/inventory" Initial HMAC object with API Key: efa02b80c4d31b6d5621edaf6e37499258559eedb5c8d69ec3d92fad72e6cb43 HMAC after adding timestamp: e0015967bd324826d9b4346e0ad2081d69deec9929d3d4be901fd9403fd71545 HMAC after adding HTTP method: 6862b62175c30875eb9fab095a4ac8e03a39d195de1675512182f2e1fc380259 HMAC after adding endpoint: 522cad1b98db532ba26ce9562d4c2bd54b3ba88c2fcaa483400a345e24e2b1f6 Payload MD5 Hash: e96991d11ca4a5aea5a33dd922654969 Final HMAC signature: e630ee795f5e5f479bc12a1a9c3eb8f5183102a91a16f7ff94b5f4a30aca0836