├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .goreleaser.yml ├── DEVELOPMENT.md ├── Dockerfile.release ├── Dockerfile.ubuntu ├── LICENSE ├── README.md ├── cli └── entry.go ├── cmd └── root.go ├── docker-compose.yml ├── go.mod ├── go.sum └── main.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | github: itzg 3 | custom: 4 | - https://www.buymeacoffee.com/itzg 5 | - https://paypal.me/itzg 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | groups: 9 | updates: 10 | patterns: 11 | - "*" 12 | update-types: 13 | - patch 14 | - minor 15 | - package-ecosystem: "gomod" 16 | directory: "/" 17 | schedule: 18 | interval: "weekly" 19 | groups: 20 | patches: 21 | patterns: 22 | - "*" 23 | update-types: 24 | - patch 25 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "[0-9]+.[0-9]+.[0-9]+" 7 | - "[0-9]+.[0-9]+.[0-9]+-*" 8 | 9 | jobs: 10 | release: 11 | uses: itzg/github-workflows/.github/workflows/go-with-releaser-image.yml@main 12 | with: 13 | go-version: "1.24.1" 14 | secrets: 15 | image-registry-username: ${{ secrets.DOCKERHUB_USERNAME }} 16 | image-registry-password: ${{ secrets.DOCKERHUB_TOKEN }} 17 | scoop-tap-github-token: ${{ secrets.SCOOP_TAP_GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | - README.md 9 | pull_request: 10 | branches: 11 | - master 12 | schedule: 13 | - cron: 0 4 * * SUN 14 | 15 | jobs: 16 | build: 17 | uses: itzg/github-workflows/.github/workflows/go-test.yml@main 18 | with: 19 | go-version: "1.24.1" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /*.idl 3 | *.iml 4 | /dist/ 5 | /vendor/ 6 | /rcon-cli 7 | /rcon-cli.exe 8 | /config.yml -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | project_name: rcon-cli 4 | before: 5 | hooks: 6 | - go mod download 7 | release: 8 | github: 9 | owner: itzg 10 | name: rcon-cli 11 | builds: 12 | - goos: 13 | - linux 14 | - darwin 15 | goarch: 16 | - "386" 17 | - amd64 18 | - arm 19 | - arm64 20 | goarm: 21 | - "6" 22 | - "7" 23 | main: . 24 | binary: rcon-cli 25 | env: 26 | - CGO_ENABLED=0 27 | archives: 28 | - 29 | name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ 30 | .Arm }}{{ end }}' 31 | files: 32 | - LICENSE* 33 | - README* 34 | changelog: 35 | filters: 36 | exclude: 37 | - '^ci:' 38 | - '^docs:' 39 | dockers: 40 | - image_templates: 41 | - "itzg/rcon-cli:{{ .Version }}-amd64" 42 | dockerfile: Dockerfile.release 43 | use: buildx 44 | goos: linux 45 | goarch: amd64 46 | build_flag_templates: 47 | - "--platform=linux/amd64" 48 | - image_templates: 49 | - "itzg/rcon-cli:{{ .Version }}-ubuntu-amd64" 50 | dockerfile: Dockerfile.ubuntu 51 | use: buildx 52 | goos: linux 53 | goarch: amd64 54 | build_flag_templates: 55 | - "--platform=linux/amd64" 56 | - image_templates: 57 | - "itzg/rcon-cli:{{ .Version }}-arm64" 58 | dockerfile: Dockerfile.release 59 | use: buildx 60 | goos: linux 61 | goarch: arm64 62 | build_flag_templates: 63 | - "--platform=linux/arm64" 64 | - image_templates: 65 | - "itzg/rcon-cli:{{ .Version }}-ubuntu-arm64" 66 | dockerfile: Dockerfile.ubuntu 67 | use: buildx 68 | goos: linux 69 | goarch: arm64 70 | build_flag_templates: 71 | - "--platform=linux/arm64" 72 | - image_templates: 73 | - "itzg/rcon-cli:{{ .Version }}-armv7" 74 | dockerfile: Dockerfile.release 75 | use: buildx 76 | goos: linux 77 | goarch: arm 78 | goarm: '7' 79 | build_flag_templates: 80 | - "--platform=linux/arm/v7" 81 | - image_templates: 82 | - "itzg/rcon-cli:{{ .Version }}-ubuntu-armv7" 83 | dockerfile: Dockerfile.ubuntu 84 | use: buildx 85 | goos: linux 86 | goarch: arm 87 | goarm: '7' 88 | build_flag_templates: 89 | - "--platform=linux/arm/v7" 90 | docker_manifests: 91 | - name_template: "itzg/rcon-cli:latest" 92 | image_templates: 93 | - "itzg/rcon-cli:{{ .Version }}-amd64" 94 | - "itzg/rcon-cli:{{ .Version }}-arm64" 95 | - "itzg/rcon-cli:{{ .Version }}-armv7" 96 | - name_template: "itzg/rcon-cli:{{ .Version }}" 97 | image_templates: 98 | - "itzg/rcon-cli:{{ .Version }}-amd64" 99 | - "itzg/rcon-cli:{{ .Version }}-arm64" 100 | - "itzg/rcon-cli:{{ .Version }}-armv7" 101 | - name_template: "itzg/rcon-cli:{{ .Version }}-ubuntu" 102 | image_templates: 103 | - "itzg/rcon-cli:{{ .Version }}-ubuntu-amd64" 104 | - "itzg/rcon-cli:{{ .Version }}-ubuntu-arm64" 105 | - "itzg/rcon-cli:{{ .Version }}-ubuntu-armv7" 106 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Creating a release 2 | 3 | Tag the source repository and push the tag. The CircleCI configuration includes a `release` 4 | workflow that will take care of invoking goreleaser. -------------------------------------------------------------------------------- /Dockerfile.release: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | COPY rcon-cli / 3 | ENTRYPOINT ["/rcon-cli"] 4 | -------------------------------------------------------------------------------- /Dockerfile.ubuntu: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | COPY rcon-cli /usr/bin/rcon-cli 3 | ENTRYPOINT ["/usr/bin/rcon-cli"] 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/itzg/rcon-cli)](https://github.com/itzg/rcon-cli/releases/latest) 2 | [![test](https://github.com/itzg/rcon-cli/actions/workflows/test.yml/badge.svg)](https://github.com/itzg/rcon-cli/actions/workflows/test.yml) 3 | 4 | 5 | A little RCON cli based on james4k's RCON library for golang. 6 | 7 | ## Installation 8 | 9 | 1. Download the appropriate binary for your platform from the [latest releases](https://github.com/itzg/rcon-cli/releases/latest) 10 | 11 | 2. On UNIX-y platforms, set the binary to be executable - done! 12 | 13 | If you [have Go](https://golang.org/dl/) you could also simply `go get github.com/itzg/rcon-cli && rcon-cli -h`. 14 | 15 | ## Usage 16 | 17 | ```text 18 | rcon-cli is a CLI for attaching to an RCON enabled game server, such as Minecraft. 19 | Without any additional arguments, the CLI will start an interactive session with 20 | the RCON server. 21 | 22 | If arguments are passed into the CLI, then the arguments are sent 23 | as a single command (joined by spaces), the response is displayed, 24 | and the CLI will exit. 25 | 26 | Usage: 27 | rcon-cli [flags] [RCON command ...] 28 | 29 | Examples: 30 | 31 | rcon-cli --host mc1 --port 25575 32 | rcon-cli --port 25575 stop 33 | RCON_PORT=25575 rcon-cli stop 34 | 35 | 36 | Flags: 37 | --config string config file (default is $HOME/.rcon-cli.yaml) 38 | --host string RCON server's hostname (default "localhost") 39 | --password string RCON server's password 40 | --port int Server's RCON port (default 27015) 41 | ``` 42 | 43 | ## Configuration 44 | 45 | You can preconfigure rcon-cli to use the arguments you want by default by modifying the file `.rcon-cli.yaml` in your home folder. If you want to use any other file use the argument `--config /path/to/the/config.yaml`. 46 | 47 | Example of a `.rcon-cli.yaml` file: 48 | ```yaml 49 | host: mydomain.com 50 | port: 12345 51 | password: mycustompassword 52 | ``` 53 | 54 | That way executing `rcon-cli` without arguments would connect to `mydomain.com:12345` with the password `mycustompassword` by default. 55 | -------------------------------------------------------------------------------- /cli/entry.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 Rackspace 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS-IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | package cli 18 | 19 | import ( 20 | "errors" 21 | "fmt" 22 | "github.com/james4k/rcon" 23 | "github.com/peterh/liner" 24 | "io" 25 | "log" 26 | "os" 27 | "runtime" 28 | "strings" 29 | ) 30 | 31 | const SectionSign = "§" 32 | const Reset = "\u001B[0m" 33 | 34 | var colors = map[string]string{ 35 | "0": "\u001B[30m", // black 36 | "1": "\u001B[34m", // dark blue 37 | "2": "\u001B[32m", // dark green 38 | "3": "\u001B[36m", // dark aqua 39 | "4": "\u001B[31m", // dark red 40 | "5": "\u001B[35m", // dark purple 41 | "6": "\u001B[33m", // gold 42 | "7": "\u001B[37m", // gray 43 | "8": "\u001B[30m", // dark gray 44 | "9": "\u001B[34m", // blue 45 | "a": "\u001B[32m", // green 46 | "b": "\u001B[32m", // aqua 47 | "c": "\u001B[31m", // red 48 | "d": "\u001B[35m", // light purple 49 | "e": "\u001B[33m", // yellow 50 | "f": "\u001B[37m", // white 51 | "k": "", // random 52 | "m": "\u001B[9m", // strikethrough 53 | "o": "\u001B[3m", // italic 54 | "l": "\u001B[1m", // bold 55 | "n": "\u001B[4m", // underline 56 | "r": Reset, // reset 57 | } 58 | 59 | func Start(hostPort string, password string, out io.Writer) { 60 | remoteConsole, err := rcon.Dial(hostPort, password) 61 | if err != nil { 62 | log.Fatal("Failed to connect to RCON server", err) 63 | } 64 | defer remoteConsole.Close() 65 | 66 | lineEditor := liner.NewLiner() 67 | defer lineEditor.Close() 68 | 69 | for { 70 | cmd, err := lineEditor.Prompt("> ") 71 | 72 | if err != nil { 73 | if errors.Is(err, liner.ErrPromptAborted) { 74 | return 75 | } 76 | 77 | if errors.Is(err, io.EOF) { 78 | return 79 | } 80 | 81 | _, _ = fmt.Fprintln(os.Stderr, "Error reading input:", err) 82 | } 83 | 84 | if cmd == "exit" { 85 | return 86 | } 87 | 88 | reqId, err := remoteConsole.Write(cmd) 89 | if err != nil { 90 | _, _ = fmt.Fprintln(os.Stderr, "Failed to send command:", err.Error()) 91 | continue 92 | } 93 | 94 | resp, respReqId, err := remoteConsole.Read() 95 | if err != nil { 96 | if err == io.EOF { 97 | return 98 | } 99 | _, _ = fmt.Fprintln(os.Stderr, "Failed to read command:", err.Error()) 100 | continue 101 | } 102 | 103 | if reqId != respReqId { 104 | _, _ = fmt.Fprintln(out, "Weird. This response is for another request.") 105 | } 106 | 107 | resp = colorize(resp) 108 | _, _ = fmt.Fprintln(out, resp) 109 | 110 | lineEditor.AppendHistory(cmd) 111 | } 112 | } 113 | 114 | func Execute(hostPort string, password string, out io.Writer, command ...string) { 115 | remoteConsole, err := rcon.Dial(hostPort, password) 116 | if err != nil { 117 | log.Fatal("Failed to connect to RCON server", err) 118 | } 119 | defer remoteConsole.Close() 120 | 121 | preparedCmd := strings.Join(command, " ") 122 | reqId, err := remoteConsole.Write(preparedCmd) 123 | 124 | resp, respReqId, err := remoteConsole.Read() 125 | if err != nil { 126 | if err == io.EOF { 127 | return 128 | } 129 | _, _ = fmt.Fprintln(os.Stderr, "Failed to read command:", err.Error()) 130 | return 131 | } 132 | 133 | if reqId != respReqId { 134 | _, _ = fmt.Fprintln(out, "Weird. This response is for another request.") 135 | } 136 | 137 | resp = colorize(resp) 138 | _, _ = fmt.Fprintln(out, resp) 139 | } 140 | 141 | func colorize(str string) string { 142 | if runtime.GOOS == "windows" { 143 | return str 144 | } 145 | 146 | for code := range colors { 147 | str = strings.ReplaceAll(str, SectionSign+code, colors[code]) 148 | } 149 | 150 | str = strings.ReplaceAll(str, "\n", "\n"+Reset) 151 | 152 | return str 153 | } 154 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 NAME HERE 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmd 16 | 17 | import ( 18 | "fmt" 19 | "log" 20 | "os" 21 | 22 | "github.com/itzg/rcon-cli/cli" 23 | "github.com/spf13/cobra" 24 | "github.com/spf13/viper" 25 | "net" 26 | "strconv" 27 | ) 28 | 29 | var ( 30 | cfgFile string 31 | ) 32 | 33 | // RootCmd represents the base command when called without any subcommands 34 | var RootCmd = &cobra.Command{ 35 | Use: "rcon-cli [flags] [RCON command ...]", 36 | Short: "A CLI for attaching to an RCON enabled game server", 37 | Example: ` 38 | rcon-cli --host mc1 --port 25575 39 | rcon-cli --port 25575 stop 40 | RCON_PORT=25575 rcon-cli stop 41 | `, 42 | Long: ` 43 | rcon-cli is a CLI for attaching to an RCON enabled game server, such as Minecraft. 44 | Without any additional arguments, the CLI will start an interactive session with 45 | the RCON server. 46 | 47 | If arguments are passed into the CLI, then the arguments are sent 48 | as a single command (joined by spaces), the response is displayed, 49 | and the CLI will exit. 50 | `, 51 | Run: func(cmd *cobra.Command, args []string) { 52 | 53 | hostPort := net.JoinHostPort(viper.GetString("host"), strconv.Itoa(viper.GetInt("port"))) 54 | password := viper.GetString("password") 55 | 56 | if len(args) == 0 { 57 | cli.Start(hostPort, password, os.Stdout) 58 | } else { 59 | cli.Execute(hostPort, password, os.Stdout, args...) 60 | } 61 | }, 62 | } 63 | 64 | // Execute adds all child commands to the root command sets flags appropriately. 65 | // This is called by main.main(). It only needs to happen once to the rootCmd. 66 | func Execute() { 67 | if err := RootCmd.Execute(); err != nil { 68 | fmt.Println(err) 69 | os.Exit(-1) 70 | } 71 | } 72 | 73 | func init() { 74 | cobra.OnInitialize(initConfig) 75 | 76 | RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.rcon-cli.yaml)") 77 | RootCmd.PersistentFlags().String("host", "localhost", "RCON server's hostname") 78 | RootCmd.PersistentFlags().String("password", "", "RCON server's password") 79 | RootCmd.PersistentFlags().Int("port", 25575, "Server's RCON port") 80 | err := viper.BindPFlags(RootCmd.PersistentFlags()) 81 | if err != nil { 82 | log.Fatal(err) 83 | } 84 | } 85 | 86 | // initConfig reads in config file and ENV variables if set. 87 | func initConfig() { 88 | if cfgFile != "" { // enable ability to specify config file via flag 89 | viper.SetConfigFile(cfgFile) 90 | } else { 91 | viper.SetConfigName(".rcon-cli") // name of config file (without extension) 92 | viper.AddConfigPath("$HOME") 93 | viper.AddConfigPath("/data") 94 | viper.AddConfigPath("/server") 95 | } 96 | 97 | // This will allow for env vars like RCON_PORT 98 | viper.SetEnvPrefix("rcon") 99 | viper.AutomaticEnv() // read in environment variables that match 100 | 101 | // If a config file is found, read it in. 102 | // Indicates viper.ConfigFileNotFoundError if no config file is provided, which is fine 103 | _ = viper.ReadInConfig() 104 | } 105 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | minecraft-server: 5 | image: itzg/minecraft-server:latest 6 | environment: 7 | EULA: true 8 | TYPE: paper 9 | ENABLE_RCON: true 10 | RCON_PASSWORD: password 11 | ports: 12 | - "127.0.0.1:25575:25575" 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/itzg/rcon-cli 2 | 3 | require ( 4 | github.com/james4k/rcon v0.0.0-20210222224819-34a67ca2b2d6 5 | github.com/peterh/liner v1.2.2 6 | github.com/spf13/cobra v1.9.1 7 | github.com/spf13/viper v1.20.1 8 | ) 9 | 10 | require ( 11 | github.com/fsnotify/fsnotify v1.8.0 // indirect 12 | github.com/go-viper/mapstructure/v2 v2.2.1 // indirect 13 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 14 | github.com/mattn/go-runewidth v0.0.3 // indirect 15 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 16 | github.com/sagikazarmark/locafero v0.7.0 // indirect 17 | github.com/sourcegraph/conc v0.3.0 // indirect 18 | github.com/spf13/afero v1.12.0 // indirect 19 | github.com/spf13/cast v1.7.1 // indirect 20 | github.com/spf13/pflag v1.0.6 // indirect 21 | github.com/subosito/gotenv v1.6.0 // indirect 22 | go.uber.org/atomic v1.9.0 // indirect 23 | go.uber.org/multierr v1.9.0 // indirect 24 | golang.org/x/sys v0.29.0 // indirect 25 | golang.org/x/text v0.21.0 // indirect 26 | gopkg.in/yaml.v3 v3.0.1 // indirect 27 | ) 28 | 29 | go 1.21.0 30 | 31 | toolchain go1.24.1 32 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 6 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 7 | github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= 8 | github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 9 | github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= 10 | github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 11 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 12 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 13 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 14 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 15 | github.com/james4k/rcon v0.0.0-20210222224819-34a67ca2b2d6 h1:SNrbIpIMlIBYe8AQTLfsDJqlXSaEC64CjulMXzR0kS0= 16 | github.com/james4k/rcon v0.0.0-20210222224819-34a67ca2b2d6/go.mod h1:1qNVsDcmNQDsAXYfUuF/Z0rtK5eT8x9D6Pi7S3PjXAg= 17 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 18 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 19 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 20 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 21 | github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= 22 | github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 23 | github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= 24 | github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 25 | github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw= 26 | github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI= 27 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 28 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 29 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 30 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 31 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 32 | github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= 33 | github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= 34 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= 35 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= 36 | github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= 37 | github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= 38 | github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= 39 | github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 40 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 41 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 42 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 43 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 44 | github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= 45 | github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= 46 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 47 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 48 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 49 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 50 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 51 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 52 | go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= 53 | go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 54 | go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= 55 | go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= 56 | golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 57 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 58 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 59 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 60 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 61 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 62 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 63 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 64 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 65 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 66 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 NAME HERE 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import "github.com/itzg/rcon-cli/cmd" 18 | 19 | func main() { 20 | cmd.Execute() 21 | } 22 | --------------------------------------------------------------------------------