-
Notifications
You must be signed in to change notification settings - Fork 1
/
Config.scala
160 lines (125 loc) · 6.4 KB
/
Config.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package tgtg
import cats.data.NonEmptyList
import cats.syntax.all.*
import com.comcast.ip4s.{Host, *}
import com.monovore.decline.{Argument, Opts, Visibility}
import cron4s.{Cron, CronExpr}
import org.legogroup.woof.LogLevel
import sttp.model.Uri
import tgtg.notification.NotifyConfig
import scala.concurrent.duration.*
object UserId extends NewType[String]
type UserId = UserId.Type
object ApiToken extends NewType[String]
type ApiToken = ApiToken.Type
case class TgtgConfig(refreshToken: ApiToken, userId: UserId)
case class RedisConfig(host: Host)
case class ServerConfig(
intervals: Option[NonEmptyList[FiniteDuration]],
crons: Option[NonEmptyList[CronExpr]],
isServer: Boolean
)
object Email extends NewType[String]
type Email = Email.Type
trait BaseConfig:
def log: LogLevel
case class AuthConfig(email: Option[Email], log: LogLevel) extends BaseConfig
case class Config(
tgtg: TgtgConfig,
notification: NotifyConfig,
cronitor: Option[ApiToken],
redis: Option[RedisConfig],
server: ServerConfig,
log: LogLevel
) extends BaseConfig
object Config:
import allOpts.*
val auth =
Opts.subcommand("auth", "Authenticate with TooGoodToGo to retrieve your credentials (user-id and refresh-token).")(
(userEmail, log).mapN(AuthConfig.apply)
)
val opts = (tgtg, notification, cronitor, redis, server, log).mapN(Config.apply)
private object allOpts:
private given Argument[Uri] = Argument.from("url")(
Uri
.parse(_)
.ensureOr(s => show"Url $s must have a scheme (http:// or https://)")(_.scheme.isDefined)
.ensureOr(s => show"Url $s must have a host")(_.host.isDefined)
.ensureOr(s => show"Url $s must not be relative")(_.isAbsolute)
.toValidatedNel
)
private given Argument[Host] =
Argument.from("host")(str => Host.fromString(str).toRight(show"Invalid host $str").toValidatedNel)
private given Argument[LogLevel] = Argument.from("level")(_.toLowerCase match
case "debug" => LogLevel.Debug.validNel
case "info" => LogLevel.Info.validNel
case "warn" => LogLevel.Warn.validNel
case "error" => LogLevel.Error.validNel
case other => show"Invalid log level $other. Should be one of 'debug', 'info', 'warn' or 'error'".invalidNel
)
private given Argument[ApiToken] = Argument.from("token")(ApiToken(_).validNel)
private given Argument[Email] = Argument.from("email")(Email(_).validNel)
private given Argument[UserId] = Argument.from("user_id")(UserId(_).validNel)
private given Argument[CronExpr] = Argument.from("cron")(c =>
Cron
.parse(c)
.leftMap(e => show"Invalid cron '$c': $e")
.toValidatedNel
)
private val refreshHelp =
"Refresh token for TooGoodToGo. Get it using the `tgtg auth` command."
private val refreshToken =
Opts.option[ApiToken]("refresh-token", refreshHelp, "r") orElse
Opts.env[ApiToken]("TGTG_REFRESH_TOKEN", refreshHelp)
private val userIdHelp =
"User id for TooGoodToGo. Get it using the `tgtg auth` command."
val userId = Opts.option[UserId]("user-id", userIdHelp, "u") orElse Opts.env[UserId]("TGTG_USER_ID", userIdHelp)
private val gotifyHelp = "Gotify token for notifications (optional)."
private val gotifyToken =
Opts.option[ApiToken]("gotify-token", gotifyHelp) orElse Opts.env[ApiToken]("GOTIFY_TOKEN", gotifyHelp)
private val gotifyUrlHelp = "Gotify server URL to send notifications to."
private val gotifyUrl =
Opts.option[Uri]("gotify-url", gotifyUrlHelp) orElse Opts.env[Uri]("GOTIFY_URL", gotifyUrlHelp)
private val gotify = (gotifyToken, gotifyUrl).mapN(NotifyConfig.Gotify.apply)
private val pushbulletHelp = "Pushbullet token for notifications."
private val pushbullet =
(Opts.option[ApiToken]("pushbullet-token", pushbulletHelp) orElse
Opts.env[ApiToken]("PUSHBULLET_TOKEN", pushbulletHelp))
.map(NotifyConfig.Pushbullet.apply)
private val pushoverTokenHelp = "Pushover token for notifications."
private val pushoverToken =
Opts.option[ApiToken]("pushover-token", pushoverTokenHelp) orElse
Opts.env[ApiToken]("PUSHOVER_TOKEN", pushoverTokenHelp)
private val pushoverUserHelp = "Pushover user ID for notifications."
private val pushoverUser =
Opts.option[UserId]("pushover-user", pushoverUserHelp) orElse Opts.env[UserId]("PUSHOVER_USER", pushoverUserHelp)
private val pushover = (pushoverToken, pushoverUser).mapN(NotifyConfig.Pushover.apply)
private val webhookHelp = "Webhook URL to send notifications to."
private val webhook = (Opts.option[Uri]("webhook-url", webhookHelp) orElse
Opts.env[Uri]("WEBHOOK_URL", webhookHelp)).map(NotifyConfig.Webhook.apply)
val notification: Opts[NotifyConfig] = List(gotify, pushbullet, pushover, webhook).reduce(_ orElse _)
private val cronitorHelp = "Cronitor token for monitoring (optional)."
val cronitor = (Opts.option[ApiToken]("cronitor-token", cronitorHelp) orElse
Opts.env[ApiToken]("CRONITOR_TOKEN", cronitorHelp)).orNone
val tgtg = (refreshToken, userId).mapN(TgtgConfig.apply)
private val redisHostHelp =
"Specify the Redis host for storing authentication tokens and notification history cache. If not set a local cache.json file will be used instead."
private val redisHost =
Opts.option[Host]("redis-host", redisHostHelp, "R") orElse
Opts.env[Host]("REDIS_HOST", redisHostHelp)
val redis = redisHost.map(RedisConfig.apply).orNone
val log = (
Opts.flag("verbose", "Enable verbose logging for more detailed output.", "v").as(LogLevel.Trace) orElse
Opts.flag("quiet", "Suppress logging output (only log errors).", "q").as(LogLevel.Error) orElse
Opts.env[LogLevel]("LOG_LEVEL", "Set the log level (debug, info, warn, error).", "log_level")
).withDefault(LogLevel.Info)
private val intervals =
Opts.options[FiniteDuration]("interval", "Time interval between checks for available boxes.", "i").orNone
private val crons =
Opts.options[CronExpr]("cron", "Cron expression for when to check for available boxes.", "c").orNone
private val isServer =
Opts.flag("server", "DEPRECATED. Use --interval or --cron options", "s", Visibility.Partial).orFalse
val server = (intervals, crons, isServer).mapN(ServerConfig.apply)
val userEmail = Opts.option[Email]("user-email", "Email to use for authentication (optional).").orNone
end allOpts
end Config