Implement initial loading, asking for toking and persisting the configutation.
MODIFY
README.md
README.md
@@ -26,12 +26,16 @@ In order to execute the application, simply run the executable, which lies at
### Configuration
-Cordless doesn't need anything besides your Discord token. In order to retrieve
-your token, simply follow the steps in the graphic below:
+Cordless doesn't need anything besides your Discord token. The application will
+ask you to input the token in case it can't be found.
+
+In order to retrieve your token, simply follow the steps in the graphic below:
![Steps to retrieve discord token - by ripcord](https://cancel.fm/ripcord/static/app_misc/discord_token_howto_en-US.png)
-After retrieving the token, you have to insert it into the `Token` field of
-your configuration file. In order to find the location of the configuration
-file, simply run cordless via your terminal and check the output, as it will
-tell you, where its configuration file lies.
\ No newline at end of file
+After retrieving the token, you have to insert it into the input prompt.
+
+Alternative, you can manually insert it into your configuration file.
+In order to find the location of the configuration file, simply run
+cordless via your terminal and check the output, as it will tell you, where
+its configuration file lies.
\ No newline at end of file

MODIFY
internal/app.go
internal/app.go
package internal
import (
+ "bufio"
"log"
+ "os"
+ "strings"
"github.com/Bios-Marcel/cordless/internal/config"
)
@@ -16,4 +19,37 @@ func Run() {
}
log.Printf("Configuration lies at: %s\n", configDir)
+
+ configuration, configLoadError := config.LoadConfig()
+
+ if configLoadError != nil {
+ log.Fatalf("Error loading configuration file (%s).\n", configLoadError.Error())
+ }
+
+ if configuration.Token == "" {
+ log.Println("The Discord token could not be found, please input your token.")
+ configuration.Token = askForToken()
+ persistError := config.PersistConfig()
+ if persistError != nil {
+ log.Fatalf("Error persisting configuration (%s).\n", persistError.Error())
+ }
+ }
+}
+
+func askForToken() string {
+ reader := bufio.NewReader(os.Stdin)
+ token, inputError := reader.ReadString('\n')
+
+ if inputError != nil {
+ log.Fatalf("Error reading your token (%s).\n", inputError.Error())
+ }
+
+ if token == "" {
+ log.Println("An empty token is not valid, please try again.")
+ return askForToken()
+ }
+
+ token = strings.TrimSuffix(token, "\n")
+
+ return token
}

MODIFY
internal/config/config.go
internal/config/config.go
package config
-import "strings"
+import (
+ "encoding/json"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
var (
//AppName is the name representing the application.
@@ -8,6 +15,8 @@ var (
//AppNameLowercase is the representative name, but lowercase.
//It us used for filepaths and such.
AppNameLowercase = strings.ToLower(AppName)
+
+ currentConfig = Config{}
)
//Config contains all possible configuration for the application.
@@ -17,6 +26,18 @@ type Config struct {
var cachedConfigDir string
+//GetConfigFile returns the absolute path to the configuration file or an error
+//in case of failure.
+func GetConfigFile() (string, error) {
+ configDir, configError := GetConfigDirectory()
+
+ if configError != nil {
+ return "", configError
+ }
+
+ return filepath.Join(configDir, "config.json"), nil
+}
+
//GetConfigDirectory is the parent directory in the os, that contains the
//settings for the application.
func GetConfigDirectory() (string, error) {
@@ -29,6 +50,73 @@ func GetConfigDirectory() (string, error) {
return "", err
}
+ _, statError := os.Stat(directory)
+ if os.IsNotExist(statError) {
+ createDirsError := os.MkdirAll(directory, 0755)
+ if createDirsError != nil {
+ return "", createDirsError
+ }
+ } else if statError != nil {
+ return "", statError
+ }
+
+ //After first retrieval, we will save this, as we needn't redo all that
+ //stuff over and over again.
cachedConfigDir = directory
+
return cachedConfigDir, nil
}
+
+//GetConfig returns the currently loaded configuration.
+func GetConfig() *Config {
+ return &currentConfig
+}
+
+//LoadConfig loads the configuration initially and returns it.
+func LoadConfig() (*Config, error) {
+ configFilePath, configError := GetConfigFile()
+ if configError != nil {
+ return nil, configError
+ }
+
+ configFile, openError := os.Open(configFilePath)
+
+ if os.IsNotExist(openError) {
+ return GetConfig(), nil
+ }
+
+ if openError != nil {
+ return nil, openError
+ }
+
+ defer configFile.Close()
+ decoder := json.NewDecoder(configFile)
+ configLoadError := decoder.Decode(&currentConfig)
+
+ //io.EOF would mean empty, therefore we use defaults.
+ if configLoadError != nil && configLoadError != io.EOF {
+ return nil, configLoadError
+ }
+
+ return GetConfig(), nil
+}
+
+//PersistConfig saves the current configuration onto the filesystem.
+func PersistConfig() error {
+ configFilePath, configError := GetConfigFile()
+ if configError != nil {
+ return configError
+ }
+
+ configAsJSON, jsonError := json.MarshalIndent(&currentConfig, "", " ")
+ if jsonError != nil {
+ return jsonError
+ }
+
+ writeError := ioutil.WriteFile(configFilePath, configAsJSON, 0755)
+ if writeError != nil {
+ return writeError
+ }
+
+ return nil
+}

Implement initial loading, asking for toking and persisting the configutation.