Civilization VI Turn Notifications

May 14th 2022

Civilization VI has a multiplayer mode that allows users to play at their own pace. All players share a single Cloud Save file which is updated as each player completes their turn. To enhance this game mode Civilization VI has a game option for a Play By Cloud Webhook URL.

civilization-6-webhook

A webhook is commonly a URL that performs some task or function based off optional data sent to the URL. You can read more about webhooks here. Civilization VI sends a JSON payload that looks like the example below to the configured webhook URL.

   
   {
      "Value1":"Game Name", 
      "Value2":"Player Name", 
      "Value3":"Turn Number"
   }
   

Prepare

Before starting, make sure you have a Telegram Bot running.

Create a Telegram Group with all the players of the Play By Cloud game.
You will need the bot’s HTTP API Token, the Telegram chat Id for the group chat, and the Telegram user Ids for the members of the group.

If you don’t already have one, create an Azure account here.

Create an Azure Function App

  1. On your Azure Dashboard, create a new resource.

    azure-create-resource
  2. Use the search function to find the Function App resource

    azure-search-function-app
  3. On the next screen create the function app.

    azure-create-function-app
  4. The next screen will configure your Function App, the steps below describe the available options

    Subscription
    This should be Azure Free, unless you have an existing subscription that you have permission to use.
    Resource Group
    A way to organize common Azure resources, click Create new if you don’t have one already.
    azure-create-function-app-subscription
    Function App Name
    The name for your function app, something like civ6turnbot, this will uniquely identify the function’s URL.
    azure-create-function-app-name
    Publish
    If you want to only publish code, or if you want to publish a whole docker image. This guide will use code. The options below are only available if you select code.
    Runtime Stack
    What language you want to code in, .NET will use C# but JavaScript, Python, and Java are available. PowerShell Core and custom options are also available.
    Region
    This is also available for Docker, select one that’s closest to most of the players in the game.
    azure-create-function-app-publish
    Operating System
    Code can be ran on either Linux or Windows, since we chose .Net we’ll pick Windows.
    azure-create-function-app-operating-system
    Plan
    The plan defines how your app scales and how you pay for the services. Consumption grows the resources allocated to your function as usage requirements increase, and you will be charged based on how much resources you’ve used. More details available here, make sure to review this so you don’t have any surprise charges.
    azure-create-function-app-plan
  5. On the next screen you can adjust hosting related settings, in this case you can define a storage account or use the default generated one as below.

    azure-create-function-app-hosting
  6. The networking tab doesn’t have anything useful for this function app.

  7. The Monitoring tab lets you collect performance and other metrics from the function using Azure Application Insights. Select yes and either create an Application Insights resource or use the default generated one as below.

    azure-create-function-app-monitor
  8. Tags are a name:value way of categorizing resources.

  9. Review the Function App’s configuration on the next page, and click the Create button.

  10. It will take a few minutes for the app to fully deploy. You can take a break here, you’ll know the process is completed when the page looks like below

azure-create-function-app-complete
  1. Click on the Go to resource button to see your new Function App.
azure-function-app-dashboard

Create the HTTP Trigger Function

  1. On the left hand section, find the Functions category.
    azure-function-app-category
  2. Click on the Functions link to go the functions page.
    azure-functions
  3. Click on the + Create button to create a new function
  4. Select Develop in Portal as the development environment. Since this is a relatively simple app we can do everything within the browser and Azure Portal.
    azure-functions-environment
  5. Select HTTP trigger as the template, this will let the app get data from Civilization VI using HTTP.
    azure-functions-template
  6. Give the function a name, such as civ6turnbot and set the authorization level to Function.
    azure-functions-name
  7. On the function page, under the Developer category, click on the Code + Test link to open the code editor.
    azure-functions-code
  8. The default code will respond with a message using the value of the name parameter you send it.
    azure-functions-default-code
  9. You can test this code by using something like Postman. Functions also has a Test/Run tool.
    azure-functions-testrun
  10. The default settings for a test are to use the HTTP POST method, and provide a value of Azure to the name parameter. The body is formatted in JSON.
azure-functions-test
  1. Clicking the Run button will show the response and content from the function.
azure-functions-response

Code the function

Copy the code below, replacing all the existing default code from the function.

The code ingests the JSON data from Civilization VI and sends a Telegram BOT API call with a message informing the next player its their turn. A Dictionary called players stores a list of players with the first parameter as their username in Civilization VI or Steam, and the second parameter as a markdown tag to create a custom @ mention in telegram with the associated player’s Telegram Id. Using the telegram Id allows tagging players who might not have a Telegram username. You can find a user’s Telegram Id using the the instructions here.

   
      #r "Newtonsoft.Json"

      using Microsoft.AspNetCore.Mvc;
      using Microsoft.Extensions.Primitives;
      using System.Net;
      using System.Net.Http;
      using System.Net.Http.Headers;
      using System.Text;
      using Newtonsoft.Json;

      public static async Task Run(HttpRequest req, ILogger log)
      {
         log.LogInformation("C# HTTP trigger function processed a request.");
         
         Dictionary players = new Dictionary();
         players.Add("Player 1", "[Player 1 Name](tg://user?id=99999999)");
         players.Add("Player 2", "[Player 2 Name](tg://user?id=11111111)");
         players.Add("Player 3", "[Player 3 Name](tg://user?id=22222222)");
         

         string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
         dynamic data = JsonConvert.DeserializeObject(requestBody);
         string name = data.value2;
         string message = "Hey " + players[name] + "! Its your turn on `" + data?.value1 +"`";

         var results = await SendTelegramMessage(message);
         log.LogInformation(String.Format("{0}", results));

         string responseMessage = String.Format("{0}", results);

         return new OkObjectResult(responseMessage);
      }

      public static async Task SendTelegramMessage(string text)
      {
         using (var client = new HttpClient())
         {

            Dictionary dictionary = new Dictionary();
            dictionary.Add("chat_id", "CHAT_ID");
            dictionary.Add("text", text);
            dictionary.Add("parse_mode", "Markdown");

            string json = JsonConvert.SerializeObject(dictionary);
            var requestData = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await client.PostAsync(String.Format("https://api.telegram.org/botID:AUTH/sendMessage"), requestData);
            var result = await response.Content.ReadAsStringAsync();

            return result;
         }
      }
   

Connect the Telegram Bot

  1. If you completed the perquisites you should have a;
    1. Telegram Bot API Token
    2. a Telegram group Id
    3. a list of Telegram user Ids.
  2. In the code above, go to line 40 and replace CHAT_ID with the ID of the group or user you want to send messages to. Ideally this will be a group Id that starts with a - and looks like -210987654
  3. You can also use a regular chat ID, which is a positive integer, therefore doesn’t start with a -.
  4. Go to line 47 and change botID:AUTH to the bot API Token which starts with bot and looks like 123456789:jbd78sadvbdy63d37gda37bd8. Within the code it should look like bot123456789:jbd78sadvbdy63d37gda37bd8
  5. For testing purposes you can leave the players Dict with sample data and replace it once you’ve verified the function is working with Telegram.

Test the Function

  1. Click on Test/Run to open the testing panel.
  2. In the Input section, add the JSON below to the Body.
   
      {
         "value1": "Azure",
         "value2": "Player 2",
         "value3": "3"
      }
   
azure-functions-first-test
  1. You should receive a message from your bot in Telegram.
    telegram-first-test
  2. You can see additional details about your call to the Telegram Bot API in the output tab of the Test/Run panel.
    azure-functions-first-test-output

Setup the players

  1. The Dictionary players on line 17 holds information regarding the players.
   
      Dictionary players = new Dictionary();
      players.Add("Player 1", "[Player 1 Name](tg://user?id=99999999)");
      players.Add("Player 2", "[Player 2 Name](tg://user?id=11111111)");
      players.Add("Player 3", "[Player 3 Name](tg://user?id=22222222)");
   
  1. You need three pieces of information to add a player

    Player 1
    The username sent by the Civilization VI.
    Player 1 Name
    The name you want to appear in telegram, this can be anything you want.
    user?id=99999999
    The Telegram user Id for the player.
  2. The Civilization VI username can either be set in the game options, or the store/service username such as a Steam Username.

civilization-6-webhook
  1. You can get the Telegram user Id by following the instructions here.

Get the Webhook URL

  1. Return to the Function’s Overview page.
    azure-functions-overview
  2. Click the Get Function Url button to get the URL for the webhook.
    azure-functions-url
  3. Go to the Civilization VI Game Options screen and paste the URL into the Play By Cloud Webhook URL input.
    civilization-6-webhook-azure
  4. Make sure to set the Frequency to Every Turn so the webhook is called for everyone’s turn, instead of only you.
  5. Start a new Play By Cloud game and watch as you get tagged custom Telegram messages on players’ turns.

Notes

Only one player needs to have the Play By Cloud Webhook URL setup, and the frequency set to every turn. If all players have the URL setup, you will receive multiple messages per turn in Telegram. Any changes to the game settings requires restarting the Play By Cloud game, so make sure to test everything out before starting a real game.

The code in the Function App can be modified to provide additional functions such as reminders when someone hasn’t played for some set time. Utilizing other Azure resources such as a database creates almost endless possibilities to what can be done.

This post is written by Gouthaman Raveendran, licensed under CC BY-NC 4.0.