├── Licence.md ├── Readme.md └── factorio-mod-updater /Licence.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © 2016 Andrew J. Stevens, Esq. 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | This will automatically update all mods in a Factorio installation. It is intended to be used on a headless server on Linux and run by a cron job, but works on any distro of Factorio. 3 | 4 | After using the very nice [factorio-updater](https://github.com/narc0tiq/factorio-updater) script I found that my local Factorio started to have a lot of mod updates that the server didn't. Copying them manually is a bit of a hassle. Remembering to check everyday would require me to be sober occasionally. Who's got time for that? 5 | 6 | ## Use ## 7 | You'll need ruby installed. Any reasonably recent version will do nicely. 8 | 9 | ### Ubuntu/Debian ### 10 | ``` 11 | sudo apt install ruby -y 12 | ``` 13 | 14 | ### Fedora/CentOS ### 15 | ``` 16 | sudo yum install ruby -y 17 | ``` 18 | 19 | Get a copy of this script on your server: 20 | ``` 21 | sudo mkdir /opt/factorio-mod-updater 22 | sudo chown `whoami` /opt/factorio-mod-updater 23 | git clone https://github.com/astevens/factorio-mod-updater /opt/factorio-mod-updater 24 | ``` 25 | 26 | This was deliberately written to use no dependencies or libraries so that it can just be copied to your server. 27 | 28 | --- 29 | 30 | ### Config ### 31 | If you setup your server to math the defaults in [factorio-init](https://github.com/Bisa/factorio-init) (in /opt/factorio) then you're almost done! 32 | 33 | You probably already have your username and token configured in your server-settings.json. 34 | 35 | Make sure you have a mods directory: 36 | ``` 37 | mkdir -p /opt/factorio/mods 38 | ``` 39 | 40 | And copy a mod-list.json in there (macOS): 41 | ``` 42 | scp ~/Library/Application\ Support/factorio/mods/mod-list.json factorio@supersexyserver:/opt/factorio/mods/ 43 | ``` 44 | 45 | On Windows it's location is: 46 | ``` 47 | %APPDATA%/Factorio/mods/mod-list.json 48 | ``` 49 | 50 | ### Run it ### 51 | Just execute it to use the default paths and user config: 52 | ``` 53 | /opt/factorio-mod-updater/factorio-mod-updater 54 | ``` 55 | 56 | and you will see it check your mods: 57 | ``` 58 | Checking AddAssemblerBatteries, bobtechsave, Power Armor MK3, ZRecycling 59 | Connecting to mods.factorio.com 60 | AddAssemblerBatteries by DRY411S 0.14.2 61 | Current 62 | bobtechsave by Bobingabout 0.14.1 63 | Current 64 | Power Armor MK3 by jimmy_1283 0.0.4 65 | Current 66 | ZRecycling by DRY411S 0.14.1 67 | ``` 68 | 69 | Mods do not need to be installed or any particular version. The latest version of anything in your mod-list.json will be installed and any old versions will be removed. 70 | 71 | --- 72 | 73 | If you play Factorio you probably like automation. Let's assume you have [factorio-init](https://github.com/Bisa/factorio-init) and [factorio-update](https://github.com/narc0tiq/factorio-updater) installed and configured... 74 | ``` 75 | crontab -e 76 | ``` 77 | 78 | ``` 79 | 0 10 * * * /opt/factorio-init/factorio stop; /opt/factorio-init/factorio update; /opt/factorio-mod-updater/factorio-mod-updater; /opt/factorio-init/factorio start 80 | ``` 81 | 82 | This will stop your server, update the factorio version, update all mods, and start your server at 10:00 am - my server is running in GMT, so this is early in the morning local time. 83 | 84 | Now sit back and enjoy. Fresh mods make you 16% more attractive to the opposite sex. 85 | 86 | --- 87 | 88 | ### Detailed config ### 89 | Paths and user details can be overridden if needed. 90 | 91 | ``` 92 | Usage: factorio-mod-updater [options] 93 | -s, --server-settings PATH location of server-settings.json for username and token 94 | -u, --username USERNAME factorio.com username instead of server-settings.json 95 | -t, --token TOKEN factorio.com token instead of server-settings.json 96 | --mod-host SERVER server to query for mod data and downloads 97 | -p, --path PATH directory to Factorio 98 | -m, --mods-path PATH location of mods dir containing mod-list.json 99 | -h, --help Prints this help 100 | ``` 101 | 102 | If you have a different factorio location or special name for your server-config you can feed it the path: 103 | ``` 104 | factorio-mod-updater -p /var/srv/factorio -s ~/factorio/happyfunconfig.json 105 | ``` 106 | 107 | server-settings.json is only used for username and token. You can skip it if you specify them on the command line: 108 | ``` 109 | factorio-mod-updater -u CoolAccountName -t 123GIBBERISH 110 | ``` 111 | 112 | --- 113 | 114 | ### Future ideas ### 115 | - install and remove mods 116 | - match mods and versions of remote server 117 | - some kind of integration with factorio-init scripts 118 | - save settings to a config file? 119 | - use a mod/auth config from a remote url? 120 | - figure out how to scp/echo mod-list from Windows easily 121 | 122 | --- 123 | 124 | Pull requests and bug reports are always welcome! 125 | 126 | Thanks to [factorio-init](https://github.com/Bisa/factorio-init) and [factorio-update](https://github.com/narc0tiq/factorio-updater)! 127 | -------------------------------------------------------------------------------- /factorio-mod-updater: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'optparse' 3 | require 'open-uri' 4 | require 'json' 5 | 6 | class ModService 7 | def initialize(username, token, server = "mods.factorio.com") 8 | @username = username 9 | @token = token 10 | @server_base_uri = URI("https://#{server}") 11 | end 12 | 13 | def mod_info(mod_names) 14 | mods_uri = URI.join(@server_base_uri, "/api/mods") 15 | mods_uri.query = URI.encode_www_form :page_size => 'max', :namelist => mod_names 16 | puts "Connecting to #{mods_uri.host}" 17 | JSON.parse(open(mods_uri).read)['results'] 18 | end 19 | 20 | def download(mod_path) 21 | mod_uri = URI.join(@server_base_uri, mod_path) 22 | mod_uri.query = URI.encode_www_form :username => @username, :token => @token 23 | 24 | temp_file = Tempfile.open(File.basename(mod_uri.path)) 25 | IO.copy_stream(open(mod_uri), temp_file) 26 | temp_file 27 | end 28 | end 29 | 30 | options = {} 31 | OptionParser.new do |opts| 32 | opts.on("-s", "--server-settings PATH", "location of server-settings.json for username and token") do |o| 33 | options[:server_settings] = File.expand_path(o) 34 | end 35 | opts.on("-u", "--username USERNAME", "factorio.com username instead of server-settings.json") do |o| 36 | options[:username] = o 37 | end 38 | opts.on("-t", "--token TOKEN", "factorio.com token instead of server-settings.json") do |o| 39 | options[:token] = o 40 | end 41 | opts.on("--mod-host SERVER", "server to query for mod data and downloads") do |o| 42 | options[:mod_host] = o 43 | end 44 | opts.on("-p", "--path PATH", "directory to Factorio") do |o| 45 | options[:base_path] = File.expand_path(o) 46 | end 47 | opts.on("-m", "--mods-path PATH", "location of mods dir containing mod-list.json") do |o| 48 | options[:mods_path] = File.expand_path(o) 49 | end 50 | opts.on("-h", "--help", "Prints this help") do 51 | puts opts 52 | exit 53 | end 54 | end.parse! 55 | 56 | def defaults(options) 57 | options[:base_path] ||= File.join('/', 'opt', 'factorio') 58 | options[:mod_host] ||= 'mods.factorio.com' 59 | options[:mods_path] ||= File.join(options[:base_path], 'mods') 60 | options[:server_settings] ||= File.join(options[:base_path], 'data', 'server-settings.json') 61 | 62 | if [options[:username], options[:token]].all?(&:nil?) 63 | server_data = JSON.parse(File.read(options[:server_settings])) 64 | options.merge!(:username => server_data['username'], :token => server_data['token']) 65 | end 66 | options 67 | end 68 | 69 | config = defaults(options) 70 | local_mod_data = JSON.parse(File.read(File.join(config[:mods_path], "mod-list.json"))) 71 | mod_names = local_mod_data['mods'].map{|m| m['name']} - ['base'] 72 | 73 | puts "Checking #{mod_names.join(', ')}" 74 | 75 | mod_service = ModService.new(config[:username], config[:token], config[:mod_host]) 76 | mod_service.mod_info(mod_names).each do |mod_data| 77 | current_release = mod_data['releases'].max_by{|r| Gem::Version.new r['version']} 78 | puts "#{mod_data['name']} by #{mod_data['owner']} #{current_release['version']}" 79 | mod_file = File.join(config[:mods_path], current_release['file_name']) 80 | if File.exist? mod_file 81 | puts "\tCurrent" 82 | else 83 | puts "\tInstalling" 84 | delete_us = Dir.glob(File.join(config[:mods_path], "#{mod_data['name']}_*.zip")) 85 | temp_file = mod_service.download(current_release['download_url']) 86 | FileUtils.cp(temp_file, mod_file) 87 | FileUtils.rm([temp_file, *delete_us]) 88 | end 89 | end 90 | --------------------------------------------------------------------------------