Skip to content

Instantly share code, notes, and snippets.

@Freddo3000
Forked from marceldev89/a3update.py
Last active July 11, 2025 19:35
Show Gist options
  • Select an option

  • Save Freddo3000/a5cd0494f649db75e43611122c9c3f15 to your computer and use it in GitHub Desktop.

Select an option

Save Freddo3000/a5cd0494f649db75e43611122c9c3f15 to your computer and use it in GitHub Desktop.

Revisions

  1. Freddo3000 revised this gist Apr 9, 2020. 1 changed file with 17 additions and 11 deletions.
    28 changes: 17 additions & 11 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -173,30 +173,36 @@ def create_mod_symlinks():


    def copy_keys():
    # Check for broken symlinks
    for key in os.listdir(A3_KEYS_DIR):
    key_path = "{}/{}".format(A3_KEYS_DIR, key)
    if os.path.islink(key_path) and not os.path.exists(key_path):
    print("Removing outdated server key '{}'".format(key))
    os.unlink(key_path)
    # Update/add new key symlinks
    for mod_name, mod_id in MODS.items():
    if mod_name not in SERVER_MODS:
    real_path = "{}/{}".format(A3_WORKSHOP_DIR, mod_id)
    if not os.path.isdir(real_path):
    print("Couldn't copy key for mod '{}', directory doesn't exist.".format(mod_name))
    else:
    dirlist = os.listdir(real_path)
    keys_folder = [x for x in dirlist if re.search(key_regex, x)]
    key_path = os.path.join(A3_KEYS_DIR, key)

    if keys_folder:
    keys_folder = keys_folder[0]
    if os.path.isfile("{}/{}".format(real_path, keys_folder)):
    print("Creating symlink to key for mod '{}' ({})".format(mod_name, key))
    keyDirs = [x for x in dirlist if re.search(key_regex, x)]

    if keyDirs:
    keyDir = keyDirs[0]
    if os.path.isfile("{}/{}".format(real_path, keyDir)):
    # Key is placed in root directory
    key = keyDir
    key_path = os.path.join(A3_KEYS_DIR, key)
    if not os.path.exists(key_path):
    os.symlink(os.path.join(real_path, keys_folder), key_path)
    print("Creating symlink to key for mod '{}' ({})".format(mod_name, key))
    os.symlink(os.path.join(real_path, key), key_path)
    else:
    for key in os.listdir(os.path.join(real_path, keys_folder)):
    real_key_path = os.path.join(real_path, keys_folder, key)
    # Key is in a folder
    for key in os.listdir(os.path.join(real_path, keyDir)):
    real_key_path = os.path.join(real_path, keyDir, key)
    key_path = os.path.join(A3_KEYS_DIR, key)
    if not os.path.exists(key_path):
    print("Creating symlink to key for mod '{}' ({})".format(mod_name, key))
    os.symlink(real_key_path, key_path)
    @@ -382,4 +388,4 @@ def generate_preset():
    log("Generating modpack .html file...")
    generate_preset()

    log("Done!")
    log("Done!")
  2. Freddo3000 revised this gist Nov 7, 2019. 1 changed file with 17 additions and 12 deletions.
    29 changes: 17 additions & 12 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -173,6 +173,11 @@ def create_mod_symlinks():


    def copy_keys():
    for key in os.listdir(A3_KEYS_DIR):
    key_path = "{}/{}".format(A3_KEYS_DIR, key)
    if os.path.islink(key_path) and not os.path.exists(key_path):
    print("Removing outdated server key '{}'".format(key))
    os.unlink(key_path)
    for mod_name, mod_id in MODS.items():
    if mod_name not in SERVER_MODS:
    real_path = "{}/{}".format(A3_WORKSHOP_DIR, mod_id)
    @@ -181,22 +186,22 @@ def copy_keys():
    else:
    dirlist = os.listdir(real_path)
    keys_folder = [x for x in dirlist if re.search(key_regex, x)]
    key_path = os.path.join(A3_KEYS_DIR, key)

    if keys_folder is not None:
    if keys_folder:
    keys_folder = keys_folder[0]
    for key in os.listdir(os.path.join(real_path, keys_folder)):
    key_path = os.path.join(A3_KEYS_DIR, key)
    real_key_path = os.path.join(real_path, keys_folder, key)
    if not os.path.islink(key_path):
    print("Creating symlink to key for mod '{}' ({})".format(mod_name, key))
    os.symlink(real_key_path, key_path)
    if os.path.isfile("{}/{}".format(real_path, keys_folder)):
    print("Creating symlink to key for mod '{}' ({})".format(mod_name, key))
    if not os.path.exists(key_path):
    os.symlink(os.path.join(real_path, keys_folder), key_path)
    else:
    for key in os.listdir(os.path.join(real_path, keys_folder)):
    real_key_path = os.path.join(real_path, keys_folder, key)
    if not os.path.exists(key_path):
    print("Creating symlink to key for mod '{}' ({})".format(mod_name, key))
    os.symlink(real_key_path, key_path)
    else:
    print("!! Couldn't find key folder for mod {} !!".format(mod_name))
    for key in os.listdir(A3_KEYS_DIR):
    key_path = "{}/{}".format(A3_KEYS_DIR, key)
    if os.path.islink(key_path) and not os.path.exists(key_path):
    print("Removing outdated server key '{}'".format(key))
    os.unlink(key_path)


    def generate_preset():
  3. Freddo3000 revised this gist Nov 7, 2019. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -291,7 +291,7 @@ def generate_preset():
    ).format("Modpack", MODPACK_NAME))

    for mod_name, mod_id in MODS.items():
    if not (mod_name in OPTIONAL_MODS and mod_name in SERVER_MODS):
    if not (mod_name in OPTIONAL_MODS or mod_name in SERVER_MODS):
    mod_url = "http://steamcommunity.com/sharedfiles/filedetails/?id={}".format(mod_id)
    response = request.urlopen(mod_url).read()
    response = response.decode("utf-8")
    @@ -323,7 +323,7 @@ def generate_preset():
    match = TITLE_PATTERN.search(response)
    if match:
    mod_title = match.group(1)
    f.write(('<tr data-type="ModContainer">\n'
    f.write(('<tr data-type="OptionalContainer">\n'
    '<td data-type="DisplayName">{}</td>\n'
    '<td>\n'
    '<span class="from-steam">Steam</span>\n'
  4. Freddo3000 revised this gist Nov 7, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion a3update.py
    Original file line number Diff line number Diff line change
    @@ -335,7 +335,7 @@ def generate_preset():
    ).format(mod_title, mod_url, mod_url))
    f.write('</table>\n'
    '</div>\n'
    '<h2 class="list-heading">DLC</h2>'
    '<h2 class="list-heading">DLC</h2>\n'
    '<div class="dlc-list">\n'
    '<table>\n'
    )
  5. Freddo3000 revised this gist Nov 6, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion a3update.py
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    # !/usr/bin/python3
    #!/usr/bin/python3

    # MIT License
    #
  6. Freddo3000 revised this gist Nov 6, 2019. 1 changed file with 203 additions and 30 deletions.
    233 changes: 203 additions & 30 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    #!/usr/bin/python3
    # !/usr/bin/python3

    # MIT License
    #
    @@ -31,8 +31,8 @@
    from datetime import datetime
    from urllib import request

    #region Configuration
    STEAM_CMD = "/home/steam/arma3/steam/steamcmd.sh" # Alternatively "steamcmd" if package is installed
    # region Configuration
    STEAM_CMD = "/home/steam/arma3/steam/steamcmd.sh" # Alternatively "steamcmd" if package is installed
    STEAM_USER = ""
    STEAM_PASS = ""

    @@ -44,17 +44,19 @@
    A3_MODS_DIR = "/home/steam/arma3/mods"
    A3_KEYS_DIR = "/home/steam/arma3/install/keys"

    MODPACK_NAME = "Modpack"
    MODPACK_PATH = "/home/steam/arma3/modpack.html"
    MODS = {
    "@cba_a3": "450814997",
    "@ace3": "463939057",
    "@alive": "620260972",
    "@cup_terrains_core": "583496184",
    "@cup_terrains_maps": "583544987",
    "@cup_weapons": "497660133",
    "@cup_units": "497661914",
    "@cup_vehicles": "541888371",
    "@blastcore": "767380317",
    "@blastcore_server": "855973772"
    "@cba_a3": "450814997",
    "@ace3": "463939057",
    "@alive": "620260972",
    "@cup_terrains_core": "583496184",
    "@cup_terrains_maps": "583544987",
    "@cup_weapons": "497660133",
    "@cup_units": "497661914",
    "@cup_vehicles": "541888371",
    "@blastcore": "767380317",
    "@blastcore_server": "855973772"
    }
    # Only mod names go here, server/optional mods also need to be listed in MODS
    SERVER_MODS = {
    @@ -64,15 +66,21 @@
    "@blastcore"
    }

    PATTERN = re.compile(r"workshopAnnouncement.*?<p id=\"(\d+)\">", re.DOTALL)
    DLC = {
    "Global Mobilization": "1042220"
    }
    UPDATE_PATTERN = re.compile(r"workshopAnnouncement.*?<p id=\"(\d+)\">", re.DOTALL)
    TITLE_PATTERN = re.compile(r"(?<=<div class=\"workshopItemTitle\">)(.*?)(?=<\/div>)", re.DOTALL)
    WORKSHOP_CHANGELOG_URL = "https://steamcommunity.com/sharedfiles/filedetails/changelog"
    #endregion

    #region Functions

    # endregion

    # region Functions
    def log(msg):
    print("")
    print("{{0:=<{}}}".format(len(msg)).format(""))
    print(msg);
    print(msg)
    print("{{0:=<{}}}".format(len(msg)).format(""))


    @@ -82,7 +90,7 @@ def call_steamcmd(params):


    def update_server():
    steam_cmd_params = " +login {} {}".format(STEAM_USER, STEAM_PASS)
    steam_cmd_params = " +login {} {}".format(STEAM_USER, STEAM_PASS)
    steam_cmd_params += " +force_install_dir {}".format(A3_SERVER_DIR)
    steam_cmd_params += " +app_update {} validate".format(A3_SERVER_ID)
    steam_cmd_params += " +quit"
    @@ -94,13 +102,13 @@ def mod_needs_update(mod_id, path):
    if os.path.isdir(path):
    response = request.urlopen("{}/{}".format(WORKSHOP_CHANGELOG_URL, mod_id)).read()
    response = response.decode("utf-8")
    match = PATTERN.search(response)
    match = UPDATE_PATTERN.search(response)

    if match:
    updated_at = datetime.fromtimestamp(int(match.group(1)))
    created_at = datetime.fromtimestamp(os.path.getctime(path))

    return (updated_at >= created_at)
    return updated_at >= created_at

    return False

    @@ -122,10 +130,10 @@ def update_mods():

    # Keep trying until the download actually succeeded
    tries = 0
    while os.path.isdir(path) == False and tries < 10:
    while os.path.isdir(path) is False and tries < 10:
    log("Updating \"{}\" ({}) | {}".format(mod_name, mod_id, tries + 1))

    steam_cmd_params = " +login {} {}".format(STEAM_USER, STEAM_PASS)
    steam_cmd_params = " +login {} {}".format(STEAM_USER, STEAM_PASS)
    steam_cmd_params += " +force_install_dir {}".format(A3_SERVER_DIR)
    steam_cmd_params += " +workshop_download_item {} {} validate".format(
    A3_WORKSHOP_ID,
    @@ -160,10 +168,13 @@ def create_mod_symlinks():
    else:
    print("Mod '{}' does not exist! ({})".format(mod_name, real_path))


    key_regex = re.compile(r'(key).*', re.I)


    def copy_keys():
    for mod_name, mod_id in MODS.items():
    if not mod_name in SERVER_MODS:
    if mod_name not in SERVER_MODS:
    real_path = "{}/{}".format(A3_WORKSHOP_DIR, mod_id)
    if not os.path.isdir(real_path):
    print("Couldn't copy key for mod '{}', directory doesn't exist.".format(mod_name))
    @@ -173,21 +184,180 @@ def copy_keys():

    if keys_folder is not None:
    keys_folder = keys_folder[0]
    for key in os.listdir(os.path.join( real_path, keys_folder )):
    key_path = os.path.join( A3_KEYS_DIR, key )
    real_key_path = os.path.join( real_path, keys_folder, key )
    if not os.path.islink( key_path ):
    for key in os.listdir(os.path.join(real_path, keys_folder)):
    key_path = os.path.join(A3_KEYS_DIR, key)
    real_key_path = os.path.join(real_path, keys_folder, key)
    if not os.path.islink(key_path):
    print("Creating symlink to key for mod '{}' ({})".format(mod_name, key))
    os.symlink( real_key_path, key_path )
    os.symlink(real_key_path, key_path)
    else:
    print("!! Couldn't find key folder for mod {} !!".format(mod_name))
    for key in os.listdir(A3_KEYS_DIR):
    key_path = "{}/{}".format(A3_KEYS_DIR, key)
    if os.path.islink(key_path) and not os.path.exists(key_path):
    print("Removing outdated server key '{}'".format(key))
    os.unlink(key_path)

    #endregion


    def generate_preset():
    f = open(MODPACK_PATH, "w")
    f.write(('<?xml version="1.0" encoding="utf-8"?>\n'
    '<html>\n\n'
    '<!--Created using a3update.py: https://gist.github.com/Freddo3000/a5cd0494f649db75e43611122c9c3f15-->\n'
    '<head>\n'
    '<meta name="arma:Type" content="{}" />\n'
    '<meta name="arma:PresetName" content="{}" />\n'
    '<meta name="generator" content="a3update.py https://gist.github.com/Freddo3000/a5cd0494f649db75e43611122c9c3f15"/>\n'
    ' <title>Arma 3</title>\n'
    '<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" type="text/css" />\n'
    '<style>\n'
    'body {{\n'
    'margin: 0;\n'
    'padding: 0;\n'
    'color: #fff;\n'
    'background: #000;\n'
    '}}\n'
    'body, th, td {{\n'
    'font: 95%/1.3 Roboto, Segoe UI, Tahoma, Arial, Helvetica, sans-serif;\n'
    '}}\n'
    'td {{\n'
    'padding: 3px 30px 3px 0;\n'
    '}}\n'
    'h1 {{\n'
    'padding: 20px 20px 0 20px;\n'
    'color: white;\n'
    'font-weight: 200;\n'
    'font-family: segoe ui;\n'
    'font-size: 3em;\n'
    'margin: 0;\n'
    '}}\n'
    'h2 {{'
    'color: white;'
    'padding: 20px 20px 0 20px;'
    'margin: 0;'
    '}}'
    'em {{\n'
    'font-variant: italic;\n'
    'color:silver;\n'
    '}}\n'
    '.before-list {{\n'
    'padding: 5px 20px 10px 20px;\n'
    '}}\n'
    '.mod-list {{\n'
    'background: #282828;\n'
    'padding: 20px;\n'
    '}}\n'
    '.optional-list {{\n'
    'background: #222222;\n'
    'padding: 20px;\n'
    '}}\n'
    '.dlc-list {{\n'
    'background: #222222;\n'
    'padding: 20px;\n'
    '}}\n'
    '.footer {{\n'
    'padding: 20px;\n'
    'color:gray;\n'
    '}}\n'
    '.whups {{\n'
    'color:gray;\n'
    '}}\n'
    'a {{\n'
    'color: #D18F21;\n'
    'text-decoration: underline;\n'
    '}}\n'
    'a:hover {{\n'
    'color:#F1AF41;\n'
    'text-decoration: none;\n'
    '}}\n'
    '.from-steam {{\n'
    'color: #449EBD;\n'
    '}}\n'
    '.from-local {{\n'
    'color: gray;\n'
    '}}\n'
    ).format("Modpack", MODPACK_NAME))

    f.write(('</style>\n'
    '</head>\n'
    '<body>\n'
    '<h1>Arma 3 - {} <strong>{}</strong></h1>\n'
    '<p class="before-list">\n'
    '<em>Drag this file or link to it to Arma 3 Launcher or open it Mods / Preset / Import.</em>\n'
    '</p>\n'
    '<h2 class="list-heading">Required Mods</h2>'
    '<div class="mod-list">\n'
    '<table>\n'
    ).format("Modpack", MODPACK_NAME))

    for mod_name, mod_id in MODS.items():
    if not (mod_name in OPTIONAL_MODS and mod_name in SERVER_MODS):
    mod_url = "http://steamcommunity.com/sharedfiles/filedetails/?id={}".format(mod_id)
    response = request.urlopen(mod_url).read()
    response = response.decode("utf-8")
    match = TITLE_PATTERN.search(response)
    if match:
    mod_title = match.group(1)
    f.write(('<tr data-type="ModContainer">\n'
    '<td data-type="DisplayName">{}</td>\n'
    '<td>\n'
    '<span class="from-steam">Steam</span>\n'
    '</td>\n'
    '<td>\n'
    '<a href="{}" data-type="Link">{}</a>\n'
    '</td>\n'
    '</tr>\n'
    ).format(mod_title, mod_url, mod_url))
    f.write('</table>\n'
    '</div>\n'
    '<h2 class="list-heading">Optional Mods</h2>'
    '<div class="optional-list">\n'
    '<table>\n'
    )

    for mod_name, mod_id in MODS.items():
    if mod_name in OPTIONAL_MODS:
    mod_url = "http://steamcommunity.com/sharedfiles/filedetails/?id={}".format(mod_id)
    response = request.urlopen(mod_url).read()
    response = response.decode("utf-8")
    match = TITLE_PATTERN.search(response)
    if match:
    mod_title = match.group(1)
    f.write(('<tr data-type="ModContainer">\n'
    '<td data-type="DisplayName">{}</td>\n'
    '<td>\n'
    '<span class="from-steam">Steam</span>\n'
    '</td>\n'
    '<td>\n'
    '<a href="{}" data-type="Link">{}</a>\n'
    '</td>\n'
    '</tr>\n'
    ).format(mod_title, mod_url, mod_url))
    f.write('</table>\n'
    '</div>\n'
    '<h2 class="list-heading">DLC</h2>'
    '<div class="dlc-list">\n'
    '<table>\n'
    )
    for dlc_name, mod_id in DLC.items():
    mod_url = "https://store.steampowered.com/app/{}".format(mod_id)
    f.write(('<tr data-type="DlcContainer">\n'
    '<td data-type="DisplayName">{}</td>\n'
    '<td>\n'
    '<a href="{}" data-type="Link">{}</a>\n'
    '</td>\n'
    '</tr>\n'
    ).format(dlc_name, mod_url, mod_url))
    f.write('</table>\n'
    '</div>\n'
    '<div class="footer">\n'
    '<span>Created using a3update.py by marceldev89; forked by Freddo3000.</span>\n'
    '</div>\n'
    '</body>\n'
    '</html>\n'
    )
    # endregion


    log("Updating A3 server ({})".format(A3_SERVER_ID))
    update_server()
    @@ -204,4 +374,7 @@ def copy_keys():
    log("Copying server keys...")
    copy_keys()

    log("Generating modpack .html file...")
    generate_preset()

    log("Done!")
  7. Freddo3000 revised this gist Nov 6, 2019. 1 changed file with 6 additions and 3 deletions.
    9 changes: 6 additions & 3 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -52,13 +52,16 @@
    "@cup_terrains_maps": "583544987",
    "@cup_weapons": "497660133",
    "@cup_units": "497661914",
    "@cup_vehicles": "541888371"
    "@cup_vehicles": "541888371",
    "@blastcore": "767380317",
    "@blastcore_server": "855973772"
    }
    # Only mod names go here, server/optional mods also need to be listed in MODS
    SERVER_MODS = {

    "@blastcore_server"
    }
    OPTIONAL_MODS = {

    "@blastcore"
    }

    PATTERN = re.compile(r"workshopAnnouncement.*?<p id=\"(\d+)\">", re.DOTALL)
  8. Freddo3000 revised this gist Nov 6, 2019. 1 changed file with 42 additions and 2 deletions.
    44 changes: 42 additions & 2 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -32,7 +32,7 @@
    from urllib import request

    #region Configuration
    STEAM_CMD = "/home/steam/arma3/steam/steamcmd.sh"
    STEAM_CMD = "/home/steam/arma3/steam/steamcmd.sh" # Alternatively "steamcmd" if package is installed
    STEAM_USER = ""
    STEAM_PASS = ""

    @@ -42,6 +42,7 @@

    A3_WORKSHOP_DIR = "{}/steamapps/workshop/content/{}".format(A3_SERVER_DIR, A3_WORKSHOP_ID)
    A3_MODS_DIR = "/home/steam/arma3/mods"
    A3_KEYS_DIR = "/home/steam/arma3/install/keys"

    MODS = {
    "@cba_a3": "450814997",
    @@ -53,6 +54,12 @@
    "@cup_units": "497661914",
    "@cup_vehicles": "541888371"
    }
    SERVER_MODS = {

    }
    OPTIONAL_MODS = {

    }

    PATTERN = re.compile(r"workshopAnnouncement.*?<p id=\"(\d+)\">", re.DOTALL)
    WORKSHOP_CHANGELOG_URL = "https://steamcommunity.com/sharedfiles/filedetails/changelog"
    @@ -149,6 +156,34 @@ def create_mod_symlinks():
    print("Creating symlink '{}'...".format(link_path))
    else:
    print("Mod '{}' does not exist! ({})".format(mod_name, real_path))

    key_regex = re.compile(r'(key).*', re.I)
    def copy_keys():
    for mod_name, mod_id in MODS.items():
    if not mod_name in SERVER_MODS:
    real_path = "{}/{}".format(A3_WORKSHOP_DIR, mod_id)
    if not os.path.isdir(real_path):
    print("Couldn't copy key for mod '{}', directory doesn't exist.".format(mod_name))
    else:
    dirlist = os.listdir(real_path)
    keys_folder = [x for x in dirlist if re.search(key_regex, x)]

    if keys_folder is not None:
    keys_folder = keys_folder[0]
    for key in os.listdir(os.path.join( real_path, keys_folder )):
    key_path = os.path.join( A3_KEYS_DIR, key )
    real_key_path = os.path.join( real_path, keys_folder, key )
    if not os.path.islink( key_path ):
    print("Creating symlink to key for mod '{}' ({})".format(mod_name, key))
    os.symlink( real_key_path, key_path )
    else:
    print("!! Couldn't find key folder for mod {} !!".format(mod_name))
    for key in os.listdir(A3_KEYS_DIR):
    key_path = "{}/{}".format(A3_KEYS_DIR, key)
    if os.path.islink(key_path) and not os.path.exists(key_path):
    print("Removing outdated server key '{}'".format(key))
    os.unlink(key_path)

    #endregion

    log("Updating A3 server ({})".format(A3_SERVER_ID))
    @@ -161,4 +196,9 @@ def create_mod_symlinks():
    lowercase_workshop_dir()

    log("Creating symlinks...")
    create_mod_symlinks()
    create_mod_symlinks()

    log("Copying server keys...")
    copy_keys()

    log("Done!")
  9. @marceldev89 marceldev89 revised this gist Dec 20, 2017. 1 changed file with 23 additions and 0 deletions.
    23 changes: 23 additions & 0 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,27 @@
    #!/usr/bin/python3

    # MIT License
    #
    # Copyright (c) 2017 Marcel de Vries
    #
    # Permission is hereby granted, free of charge, to any person obtaining a copy
    # of this software and associated documentation files (the "Software"), to deal
    # in the Software without restriction, including without limitation the rights
    # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    # copies of the Software, and to permit persons to whom the Software is
    # furnished to do so, subject to the following conditions:
    #
    # The above copyright notice and this permission notice shall be included in all
    # copies or substantial portions of the Software.
    #
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    # SOFTWARE.

    import os
    import os.path
    import re
  10. @marceldev89 marceldev89 revised this gist Dec 20, 2017. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion a3update.py
    Original file line number Diff line number Diff line change
    @@ -37,14 +37,15 @@

    #region Functions
    def log(msg):
    print("")
    print("{{0:=<{}}}".format(len(msg)).format(""))
    print(msg);
    print("{{0:=<{}}}".format(len(msg)).format(""))


    def call_steamcmd(params):
    os.system("{} {}".format(STEAM_CMD, params))
    print("\n\n")
    print("")


    def update_server():
  11. @marceldev89 marceldev89 revised this gist Dec 20, 2017. 1 changed file with 14 additions and 14 deletions.
    28 changes: 14 additions & 14 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -44,7 +44,7 @@ def log(msg):

    def call_steamcmd(params):
    os.system("{} {}".format(STEAM_CMD, params))
    print("")
    print("\n\n")


    def update_server():
    @@ -56,9 +56,9 @@ def update_server():
    call_steamcmd(steam_cmd_params)


    def mod_needs_update(item_id, path):
    def mod_needs_update(mod_id, path):
    if os.path.isdir(path):
    response = request.urlopen("{}/{}".format(WORKSHOP_CHANGELOG_URL, item_id)).read()
    response = request.urlopen("{}/{}".format(WORKSHOP_CHANGELOG_URL, mod_id)).read()
    response = response.decode("utf-8")
    match = PATTERN.search(response)

    @@ -72,30 +72,30 @@ def mod_needs_update(item_id, path):


    def update_mods():
    for key, value in MODS.items():
    path = "{}/{}".format(A3_WORKSHOP_DIR, value)
    for mod_name, mod_id in MODS.items():
    path = "{}/{}".format(A3_WORKSHOP_DIR, mod_id)

    # Check if mod needs to be updated
    if os.path.isdir(path):

    if mod_needs_update(value, path):
    if mod_needs_update(mod_id, path):
    # Delete existing folder so that we can verify whether the
    # download succeeded
    shutil.rmtree(path)
    else:
    print("No update required for \"{}\" ({})... SKIPPING".format(key, value))
    print("No update required for \"{}\" ({})... SKIPPING".format(mod_name, mod_id))
    continue

    # Keep trying until the download actually succeeded
    tries = 0
    while os.path.isdir(path) == False and tries < 10:
    log("Updating {} ({})".format(key, tries + 1))
    log("Updating \"{}\" ({}) | {}".format(mod_name, mod_id, tries + 1))

    steam_cmd_params = " +login {} {}".format(STEAM_USER, STEAM_PASS)
    steam_cmd_params += " +force_install_dir {}".format(A3_SERVER_DIR)
    steam_cmd_params += " +workshop_download_item {} {} validate".format(
    A3_WORKSHOP_ID,
    value
    mod_id
    )
    steam_cmd_params += " +quit"

    @@ -107,24 +107,24 @@ def update_mods():
    tries = tries + 1

    if tries >= 10:
    log("!! Updating {} failed after {} tries !!".format(key, tries))
    log("!! Updating {} failed after {} tries !!".format(mod_name, tries))


    def lowercase_workshop_dir():
    os.system("(cd {} && find . -depth -exec rename -v 's/(.*)\/([^\/]*)/$1\/\L$2/' {{}} \;)".format(A3_WORKSHOP_DIR))


    def create_mod_symlinks():
    for key, value in MODS.items():
    link_path = "{}/{}".format(A3_MODS_DIR, key)
    real_path = "{}/{}".format(A3_WORKSHOP_DIR, value)
    for mod_name, mod_id in MODS.items():
    link_path = "{}/{}".format(A3_MODS_DIR, mod_name)
    real_path = "{}/{}".format(A3_WORKSHOP_DIR, mod_id)

    if os.path.isdir(real_path):
    if not os.path.islink(link_path):
    os.symlink(real_path, link_path)
    print("Creating symlink '{}'...".format(link_path))
    else:
    print("Mod '{}' does not exist! ({})".format(key, real_path))
    print("Mod '{}' does not exist! ({})".format(mod_name, real_path))
    #endregion

    log("Updating A3 server ({})".format(A3_SERVER_ID))
  12. @marceldev89 marceldev89 revised this gist Dec 20, 2017. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -37,9 +37,9 @@

    #region Functions
    def log(msg):
    print("=========")
    print("{{0:=<{}}}".format(len(msg)).format(""))
    print(msg);
    print("=========")
    print("{{0:=<{}}}".format(len(msg)).format(""))


    def call_steamcmd(params):
  13. @marceldev89 marceldev89 revised this gist Dec 20, 2017. 1 changed file with 106 additions and 84 deletions.
    190 changes: 106 additions & 84 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -8,18 +8,19 @@
    from datetime import datetime
    from urllib import request

    steam_cmd = "/home/steam/arma3/steam/steamcmd.sh"
    steam_user = ""
    steam_pass = ""
    #region Configuration
    STEAM_CMD = "/home/steam/arma3/steam/steamcmd.sh"
    STEAM_USER = ""
    STEAM_PASS = ""

    a3_server_id = "233780"
    a3_server_dir = "/home/steam/arma3/install"
    a3_workshop_id = "107410"
    A3_SERVER_ID = "233780"
    A3_SERVER_DIR = "/home/steam/arma3/install"
    A3_WORKSHOP_ID = "107410"

    a3_workshop_dir = "{}/steamapps/workshop/content/{}".format(a3_server_dir, a3_workshop_id)
    a3_mods_dir = "/home/steam/arma3/mods"
    A3_WORKSHOP_DIR = "{}/steamapps/workshop/content/{}".format(A3_SERVER_DIR, A3_WORKSHOP_ID)
    A3_MODS_DIR = "/home/steam/arma3/mods"

    mods = {
    MODS = {
    "@cba_a3": "450814997",
    "@ace3": "463939057",
    "@alive": "620260972",
    @@ -30,89 +31,110 @@
    "@cup_vehicles": "541888371"
    }

    # Update A3 server
    steam_cmd_params = " +login {} {}".format(steam_user, steam_pass)
    steam_cmd_params += " +force_install_dir {}".format(a3_server_dir)
    steam_cmd_params += " +app_update {} validate".format(a3_server_id)
    steam_cmd_params += " +quit"
    PATTERN = re.compile(r"workshopAnnouncement.*?<p id=\"(\d+)\">", re.DOTALL)
    WORKSHOP_CHANGELOG_URL = "https://steamcommunity.com/sharedfiles/filedetails/changelog"
    #endregion

    print("=========")
    print("Updating A3 server ({})".format(a3_server_id))
    print("=========")
    #region Functions
    def log(msg):
    print("=========")
    print(msg);
    print("=========")

    os.system("{} {}".format(steam_cmd, steam_cmd_params))

    time.sleep(5)
    def call_steamcmd(params):
    os.system("{} {}".format(STEAM_CMD, params))
    print("")

    # Update mods
    pattern = re.compile(r"workshopAnnouncement.*?<p id=\"(\d+)\">", re.DOTALL)
    workshop_changelog_url = "https://steamcommunity.com/sharedfiles/filedetails/changelog/{}"

    for key, value in mods.items():
    updated_at = None
    def update_server():
    steam_cmd_params = " +login {} {}".format(STEAM_USER, STEAM_PASS)
    steam_cmd_params += " +force_install_dir {}".format(A3_SERVER_DIR)
    steam_cmd_params += " +app_update {} validate".format(A3_SERVER_ID)
    steam_cmd_params += " +quit"

    response = request.urlopen(workshop_changelog_url.format(value)).read()
    response = response.decode("utf-8")
    match = pattern.search(response)
    call_steamcmd(steam_cmd_params)

    if match:
    updated_at = datetime.fromtimestamp(int(match.group(1)))

    path = "{}/steamapps/workshop/content/{}/{}".format(a3_server_dir, a3_workshop_id, value)

    # Check if mod needs to be updated
    def mod_needs_update(item_id, path):
    if os.path.isdir(path):
    created_at = datetime.fromtimestamp(os.path.getctime(path))
    response = request.urlopen("{}/{}".format(WORKSHOP_CHANGELOG_URL, item_id)).read()
    response = response.decode("utf-8")
    match = PATTERN.search(response)

    if match:
    updated_at = datetime.fromtimestamp(int(match.group(1)))
    created_at = datetime.fromtimestamp(os.path.getctime(path))

    return (updated_at >= created_at)

    return False


    def update_mods():
    for key, value in MODS.items():
    path = "{}/{}".format(A3_WORKSHOP_DIR, value)

    # Check if mod needs to be updated
    if os.path.isdir(path):

    if mod_needs_update(value, path):
    # Delete existing folder so that we can verify whether the
    # download succeeded
    shutil.rmtree(path)
    else:
    print("No update required for \"{}\" ({})... SKIPPING".format(key, value))
    continue

    # Keep trying until the download actually succeeded
    tries = 0
    while os.path.isdir(path) == False and tries < 10:
    log("Updating {} ({})".format(key, tries + 1))

    steam_cmd_params = " +login {} {}".format(STEAM_USER, STEAM_PASS)
    steam_cmd_params += " +force_install_dir {}".format(A3_SERVER_DIR)
    steam_cmd_params += " +workshop_download_item {} {} validate".format(
    A3_WORKSHOP_ID,
    value
    )
    steam_cmd_params += " +quit"

    call_steamcmd(steam_cmd_params)

    if updated_at != None and updated_at >= created_at:
    # Delete existing folder so that we can verify whether the download succeeded
    shutil.rmtree(path)
    # Sleep for a bit so that we can kill the script if needed
    time.sleep(5)

    tries = tries + 1

    if tries >= 10:
    log("!! Updating {} failed after {} tries !!".format(key, tries))


    def lowercase_workshop_dir():
    os.system("(cd {} && find . -depth -exec rename -v 's/(.*)\/([^\/]*)/$1\/\L$2/' {{}} \;)".format(A3_WORKSHOP_DIR))


    def create_mod_symlinks():
    for key, value in MODS.items():
    link_path = "{}/{}".format(A3_MODS_DIR, key)
    real_path = "{}/{}".format(A3_WORKSHOP_DIR, value)

    if os.path.isdir(real_path):
    if not os.path.islink(link_path):
    os.symlink(real_path, link_path)
    print("Creating symlink '{}'...".format(link_path))
    else:
    continue

    # Keep trying until the download actually succeeded
    tries = 0
    while os.path.isdir(path) == False and tries < 10:
    print("")
    print("=========")
    print("Updating {} ({})".format(key, tries + 1))
    print("=========")

    steam_cmd_params = " +login {} {}".format(steam_user, steam_pass)
    steam_cmd_params += " +force_install_dir {}".format(a3_server_dir)
    steam_cmd_params += " +workshop_download_item {} {} validate".format(
    a3_workshop_id,
    value
    )
    steam_cmd_params += " +quit"

    os.system("{} {}".format(steam_cmd, steam_cmd_params))

    # Sleep for a bit so that we can kill the script if needed
    time.sleep(5)

    tries = tries + 1

    if tries >= 10:
    print("Updating {} failed!".format(key))


    print("")
    print("=========")
    print("Converting uppercase files/folders to lowercase...")
    print("=========")
    os.system("find {} -depth -exec rename -v 's/(.*)\/([^\/]*)/$1\/\L$2/' {{}} \;".format(a3_workshop_dir))

    print("=========")
    print("Creating symlinks...")
    print("=========")
    for key, value in mods.items():
    link_path = "{}/{}".format(a3_mods_dir, key)
    real_path = "{}/{}".format(a3_workshop_dir, value)

    if os.path.isdir(real_path):
    if not os.path.islink(link_path):
    os.symlink(real_path, link_path)
    print("Creating symlink '{}'...".format(link_path))
    else:
    print("Mod '{}' does not exist! ({})".format(key, real_path))
    print("Mod '{}' does not exist! ({})".format(key, real_path))
    #endregion

    log("Updating A3 server ({})".format(A3_SERVER_ID))
    update_server()

    log("Updating mods")
    update_mods()

    log("Converting uppercase files/folders to lowercase...")
    lowercase_workshop_dir()

    log("Creating symlinks...")
    create_mod_symlinks()
  14. @marceldev89 marceldev89 revised this gist Dec 20, 2017. No changes.
  15. @marceldev89 marceldev89 revised this gist Dec 20, 2017. 1 changed file with 32 additions and 7 deletions.
    39 changes: 32 additions & 7 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,13 @@
    #!/usr/bin/python3
    import os
    import os.path
    import re
    import shutil
    import time

    from datetime import datetime
    from urllib import request

    steam_cmd = "/home/steam/arma3/steam/steamcmd.sh"
    steam_user = ""
    steam_pass = ""
    @@ -33,25 +37,45 @@
    steam_cmd_params += " +quit"

    print("=========")
    print("Updating A3 server ({})".format(a3_server_id));
    print("Updating A3 server ({})".format(a3_server_id))
    print("=========")

    os.system("{} {}".format(steam_cmd, steam_cmd_params))

    time.sleep(5)

    # Update mods
    pattern = re.compile(r"workshopAnnouncement.*?<p id=\"(\d+)\">", re.DOTALL)
    workshop_changelog_url = "https://steamcommunity.com/sharedfiles/filedetails/changelog/{}"

    for key, value in mods.items():
    updated_at = None

    response = request.urlopen(workshop_changelog_url.format(value)).read()
    response = response.decode("utf-8")
    match = pattern.search(response)

    if match:
    updated_at = datetime.fromtimestamp(int(match.group(1)))

    path = "{}/steamapps/workshop/content/{}/{}".format(a3_server_dir, a3_workshop_id, value)

    # Delete existing folder so that we can verify whether the download succeeded
    # Check if mod needs to be updated
    if os.path.isdir(path):
    shutil.rmtree(path)
    created_at = datetime.fromtimestamp(os.path.getctime(path))

    if updated_at != None and updated_at >= created_at:
    # Delete existing folder so that we can verify whether the download succeeded
    shutil.rmtree(path)
    else:
    continue

    # Keep trying until the download actually succeeded
    tries = 1
    tries = 0
    while os.path.isdir(path) == False and tries < 10:
    print("");
    print("")
    print("=========")
    print("Updating {}".format(key));
    print("Updating {} ({})".format(key, tries + 1))
    print("=========")

    steam_cmd_params = " +login {} {}".format(steam_user, steam_pass)
    @@ -70,7 +94,8 @@
    tries = tries + 1

    if tries >= 10:
    print("Updating {} failed!".format(key));
    print("Updating {} failed!".format(key))


    print("")
    print("=========")
  16. @marceldev89 marceldev89 revised this gist Dec 20, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion a3update.py
    Original file line number Diff line number Diff line change
    @@ -48,7 +48,7 @@

    # Keep trying until the download actually succeeded
    tries = 1
    while os.path.isdir(path) == False or tries >= 10:
    while os.path.isdir(path) == False and tries < 10:
    print("");
    print("=========")
    print("Updating {}".format(key));
  17. @marceldev89 marceldev89 revised this gist Dec 19, 2017. 1 changed file with 8 additions and 2 deletions.
    10 changes: 8 additions & 2 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -47,7 +47,8 @@
    shutil.rmtree(path)

    # Keep trying until the download actually succeeded
    while os.path.isdir(path) == False:
    tries = 1
    while os.path.isdir(path) == False or tries >= 10:
    print("");
    print("=========")
    print("Updating {}".format(key));
    @@ -64,7 +65,12 @@
    os.system("{} {}".format(steam_cmd, steam_cmd_params))

    # Sleep for a bit so that we can kill the script if needed
    time.sleep(0.5)
    time.sleep(5)

    tries = tries + 1

    if tries >= 10:
    print("Updating {} failed!".format(key));

    print("")
    print("=========")
  18. @marceldev89 marceldev89 revised this gist Dec 19, 2017. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -48,6 +48,7 @@

    # Keep trying until the download actually succeeded
    while os.path.isdir(path) == False:
    print("");
    print("=========")
    print("Updating {}".format(key));
    print("=========")
  19. @marceldev89 marceldev89 revised this gist Dec 19, 2017. 1 changed file with 54 additions and 23 deletions.
    77 changes: 54 additions & 23 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,10 @@
    #!/usr/bin/python3
    import os
    import os.path
    import shutil
    import time

    steam_cmd = "/home/steam/arma3/steam/steamcmd.sh"
    steam_cmd_params = ""
    steam_user = ""
    steam_pass = ""

    @@ -15,41 +16,71 @@
    a3_mods_dir = "/home/steam/arma3/mods"

    mods = {
    "@cba_a3": "450814997",
    "@ace3": "463939057",
    "@alive": "620260972",
    "@cup_terrains_core": "583496184",
    "@cup_terrains_maps": "583544987",
    "@cup_weapons": "497660133",
    "@cup_units": "497661914",
    "@cup_vehicles": "541888371"
    "@cba_a3": "450814997",
    "@ace3": "463939057",
    "@alive": "620260972",
    "@cup_terrains_core": "583496184",
    "@cup_terrains_maps": "583544987",
    "@cup_weapons": "497660133",
    "@cup_units": "497661914",
    "@cup_vehicles": "541888371"
    }

    steam_cmd_params += " +login {} {}".format(steam_user, steam_pass)
    # Update A3 server
    steam_cmd_params = " +login {} {}".format(steam_user, steam_pass)
    steam_cmd_params += " +force_install_dir {}".format(a3_server_dir)
    steam_cmd_params += " +app_update {} validate".format(a3_server_id)
    steam_cmd_params += " +quit"

    print("=========")
    print("Updating A3 server ({})".format(a3_server_id));
    print("=========")

    os.system("{} {}".format(steam_cmd, steam_cmd_params))

    # Update mods
    for key, value in mods.items():
    path = "{}/steamapps/workshop/content/{}/{}".format(a3_server_dir, a3_workshop_id, value)

    # Delete existing folder so that we can verify whether the download succeeded
    if os.path.isdir(path):
    shutil.rmtree(path)

    # Keep trying until the download actually succeeded
    while os.path.isdir(path) == False:
    print("=========")
    print("Updating {}".format(key));
    print("=========")

    steam_cmd_params = " +login {} {}".format(steam_user, steam_pass)
    steam_cmd_params += " +force_install_dir {}".format(a3_server_dir)
    steam_cmd_params += " +workshop_download_item {} {} validate".format(
    a3_workshop_id,
    value
    a3_workshop_id,
    value
    )
    steam_cmd_params += " +quit"

    os.system("{} {}".format(steam_cmd, steam_cmd_params))

    # Sleep for a bit so that we can kill the script if needed
    time.sleep(0.5)

    steam_cmd_params += " +quit"
    os.system("{} {}".format(steam_cmd, steam_cmd_params))
    print("")
    print("=========")
    print("Converting uppercase files/folders to lowercase...")
    print("=========")
    os.system("find {} -depth -exec rename -v 's/(.*)\/([^\/]*)/$1\/\L$2/' {{}} \;".format(a3_workshop_dir))

    print("=========")
    print("Creating symlinks...")
    print("=========")
    for key, value in mods.items():
    link_path = "{}/{}".format(a3_mods_dir, key)
    real_path = "{}/{}".format(a3_workshop_dir, value)

    if os.path.isdir(real_path):
    if not os.path.islink(link_path):
    os.symlink(real_path, link_path)
    print("Creating symlink '{}'...".format(link_path))
    else:
    print("Mod '{}' does not exist! ({})".format(key, real_path))
    link_path = "{}/{}".format(a3_mods_dir, key)
    real_path = "{}/{}".format(a3_workshop_dir, value)

    if os.path.isdir(real_path):
    if not os.path.islink(link_path):
    os.symlink(real_path, link_path)
    print("Creating symlink '{}'...".format(link_path))
    else:
    print("Mod '{}' does not exist! ({})".format(key, real_path))
  20. @marceldev89 marceldev89 created this gist Dec 19, 2017.
    55 changes: 55 additions & 0 deletions a3update.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,55 @@
    #!/usr/bin/python3
    import os
    import os.path

    steam_cmd = "/home/steam/arma3/steam/steamcmd.sh"
    steam_cmd_params = ""
    steam_user = ""
    steam_pass = ""

    a3_server_id = "233780"
    a3_server_dir = "/home/steam/arma3/install"
    a3_workshop_id = "107410"

    a3_workshop_dir = "{}/steamapps/workshop/content/{}".format(a3_server_dir, a3_workshop_id)
    a3_mods_dir = "/home/steam/arma3/mods"

    mods = {
    "@cba_a3": "450814997",
    "@ace3": "463939057",
    "@alive": "620260972",
    "@cup_terrains_core": "583496184",
    "@cup_terrains_maps": "583544987",
    "@cup_weapons": "497660133",
    "@cup_units": "497661914",
    "@cup_vehicles": "541888371"
    }

    steam_cmd_params += " +login {} {}".format(steam_user, steam_pass)
    steam_cmd_params += " +force_install_dir {}".format(a3_server_dir)
    steam_cmd_params += " +app_update {} validate".format(a3_server_id)

    for key, value in mods.items():
    steam_cmd_params += " +workshop_download_item {} {} validate".format(
    a3_workshop_id,
    value
    )

    steam_cmd_params += " +quit"
    os.system("{} {}".format(steam_cmd, steam_cmd_params))
    print("")
    print("=========")
    print("Converting uppercase files/folders to lowercase...")
    os.system("find {} -depth -exec rename -v 's/(.*)\/([^\/]*)/$1\/\L$2/' {{}} \;".format(a3_workshop_dir))

    print("Creating symlinks...")
    for key, value in mods.items():
    link_path = "{}/{}".format(a3_mods_dir, key)
    real_path = "{}/{}".format(a3_workshop_dir, value)

    if os.path.isdir(real_path):
    if not os.path.islink(link_path):
    os.symlink(real_path, link_path)
    print("Creating symlink '{}'...".format(link_path))
    else:
    print("Mod '{}' does not exist! ({})".format(key, real_path))