From 04b93c24af8b16f32319df4aa185bbff385db366 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Tue, 4 Jul 2023 09:26:13 -0500 Subject: [PATCH] Refine project structure, add logging, SMTP. --- src/stock_watcher.nim | 107 +++++++++++++++++++++++++- src/stock_watcherpkg/cliconstants.nim | 16 ++++ stock_watcher.nimble | 10 +++ 3 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 src/stock_watcherpkg/cliconstants.nim diff --git a/src/stock_watcher.nim b/src/stock_watcher.nim index 862d40c..7261eeb 100644 --- a/src/stock_watcher.nim +++ b/src/stock_watcher.nim @@ -1,5 +1,106 @@ -# This is just an example to get you started. A typical binary package -# uses this file as the main entry point of the application. +import std/[httpclient, json, logging, smtp, strutils, times] +import docopt, zero_functional +import cliutils/[queue_logger, config] +import stock_watcherpkg/cliconstants + +type + Config = object + cfg: CombinedConfig + apiKey: string + apiRoot: string + notifyAddresses: seq[string] + smtpHostname: string + smtpPassword: string + smtpPort: int + smtpUsername: string + +proc sendMessages( + exitStatus: int, + cfg: Config, + messages: seq[QueuedLogMessage], + dryRun = false + ) {.raises: [].} = + + try: + var subjStatus: string + if dryRun: + subjStatus = "dry run" + elif exitStatus == QuitSuccess: + subjStatus = "success" + else: + subjStatus = "FAILED" + + let dateStr = now().format("yyyy-MM-dd") + let msg = createMessage( + "Stock Watcher ($#) -- $#" % [subjStatus, dateStr], + MESSAGE_TEMPLATE % [ messages.join("\p") ], + cfg.notifyAddresses) + + let smtpConn = newSmtp(useSsl = true) + smtpConn.connect(cfg.smtpHostname, Port cfg.smtpPort) + smtpConn.auth(cfg.smtpUsername, cfg.smtpPassword) + smtpConn.sendmail("operations@probatem.com", cfg.notifyAddresses, $msg) + + except CatchableError: + try: + let ex = getCurrentException() + warn "Failed to send notification email: " & ex.msg + debug ex.getStackTrace + except CatchableError: discard + +proc loadConfig(args: Table[string, Value]): Config = + let filepath = findConfigFile("stock_watcher") + + let cfg = initCombinedConfig(filepath, args) + + result = Config( + cfg: cfg, + apiKey: cfg.getVal("api-key"), + apiRoot: cfg.getVal("api-root"), + notifyAddresses: cfg.getJson("notify-addresses").getElems --> map(it.getStr), + smtpHostname: cfg.getVal("smtp-hostname"), + smtpPassword: cfg.getVal("smtp-password"), + smtpPort: parseInt(cfg.getVal("smtp-port")), + smtpUsername: cfg.getVal("smtp-username")) when isMainModule: - echo("Hello, World!") + var exitStatus = QuitSuccess + var cfg: Config + let args = docopt(USAGE, version = VERSION) + + let queuedLogger = newQueueLogger( + levelThreshold = lvlInfo, + fmtStr = "$levelname -") + + let consoleLogger = newConsoleLogger( + levelThreshold = lvlInfo, + fmtStr = "stock_watcher - $levelname: ") + + try: + if args["--debug"]: + queuedLogger.levelThreshold = lvlDebug + consoleLogger.levelThreshold = lvlDebug + + if args["--help"]: + stderr.writeLine(USAGE) + quit() + + logging.addHandler(consoleLogger) + logging.addHandler(queuedLogger) + + let cfg = loadConfig(args) + let http = newHttpClient() + http.headers = newHttpHeaders({ + "Content-Type": "application/json", + "Authorization": "Bearer " & cfg.apiKey + }) + + except CatchableError: + let ex = getCurrentException() + fatal ex.msg + debug ex.getStackTrace + exitStatus = QuitFailure + + finally: + sendMessages(exitStatus, cfg, queuedLogger.messages, args["--dry-run"]) + quit(exitStatus) diff --git a/src/stock_watcherpkg/cliconstants.nim b/src/stock_watcherpkg/cliconstants.nim new file mode 100644 index 0000000..4ccbaad --- /dev/null +++ b/src/stock_watcherpkg/cliconstants.nim @@ -0,0 +1,16 @@ +const USAGE* = """Usage: + stock_watcher [options] + +Options: + --debug Enable more verbose loggging. + -h, --help Print this usage information. + --version Print the version. +""" +const VERSION* = "0.1.0" + +const MESSAGE_TEMPLATE* = """This is your stock watcher. + +I recommend the following based on what I've seen: + +$# +""" diff --git a/stock_watcher.nimble b/stock_watcher.nimble index 4e8d425..97a8a15 100644 --- a/stock_watcher.nimble +++ b/stock_watcher.nimble @@ -11,3 +11,13 @@ bin = @["stock_watcher"] # Dependencies requires "nim >= 1.6.10" +requires @[ "docopt", "zero_functional" ] + +# dependencies from git.jdb-software.com +requires @[ + "cliutils >= 0.9.0", + "https://git.jdb-software.com/jdb/update-nim-package-version" +] + +task updateVersion, "Update the version of this package.": + exec "update_nim_package_version stock_watcher 'src/stock_watcherpkg/cliconstants.nim'"