From 6249e0a8c5254e45cf79e3e53824e63e54e18233 Mon Sep 17 00:00:00 2001 From: Uko Kokņevičs Date: Sat, 18 Oct 2025 10:47:50 +0300 Subject: Make config be a global special variable --- src/config.lisp | 97 +++++++++++++++++++++++++++++++++++++--------------- src/inline-bots.lisp | 7 ++-- src/main.lisp | 38 ++++++++++---------- src/state.lisp | 17 ++++----- 4 files changed, 103 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/config.lisp b/src/config.lisp index 55575bb..03ded98 100644 --- a/src/config.lisp +++ b/src/config.lisp @@ -1,33 +1,76 @@ ;; SPDX-License-Identifier: EUPL-1.2 ;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs (defpackage :ukkoclot/config - (:use :c2cl :ukkoclot/hash-tables) - (:documentation - "Stuff for loading the configuration of the bot") + (:documentation "Stuff for loading the configuration of the bot") + (:nicknames :conf) + (:use :c2cl :iterate) (:export - :config-load :config-merge - :config-p - :config-bot-name :config-bot-token :config-db-path :config-dev-group :config-owner)) + #:*config* + #:config + #:make-config + #:config-p + #:copy-config + #:load-config + #:print-default + #:bot-name + #:bot-token + #:db-path + #:dev-group + #:owner)) (in-package :ukkoclot/config) -(defmacro defconfig (&rest slots-and-types) - "Macro to make the config struct creation easier." - `(defstruct config - ,@(loop for (name type) on slots-and-types by #'cddr - collect `(,(intern (symbol-name name)) (error "No value given for ~A" ,name) :type ,type :read-only t)))) - -(defconfig - :bot-name string - :bot-token string - :db-path string - :dev-group integer - :owner integer) - -(defun config-load (filename) - "Load the config from the given `filename'. All entries must be specified." - (apply #'make-config (with-open-file (f filename) (read f)))) - -(defun config-merge (config filename) - "Merge the current config with new entries from `filename'." - (loop for (name value) on (with-open-file (f filename) (read f)) by #'cddr do - (setf (slot-value config (intern (symbol-name name) :ukkoclot/config)) value))) +(defstruct config + (bot-name "Ukko's Clot" :type string) + (bot-token "123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi" :type string) + (db-path #P"./data.db" :type (or pathname string)) + (dev-group -1001234567890 :type integer) + (owner 12345678 :type integer)) + +(defvar *config* (make-config) + "Bot's configuration") + +(defun bot-name (&optional (config *config*)) + "Get the desired name for the bot" + (config-bot-name config)) + +(defun bot-token (&optional (config *config*)) + "Get the API token for the bot" + (config-bot-token config)) + +(defun db-path (&optional (config *config*)) + "Get the path to the bot's database" + (config-db-path config)) + +(defun dev-group (&optional (config *config*)) + "Get the ID of the dev/testing group" + (config-dev-group config)) + +(defun owner (&optional (config *config*)) + "Get the ID of the bot's owner" + (config-owner config)) + +(defun load-config (filename &optional (config *config*)) + "Load config from the given `filename'." + (prog1 config + (let ((data (with-open-file (f filename) (read f)))) + (iter + (for (kw-name value) on data by #'cddr) + (let ((name (intern (symbol-name kw-name) :ukkoclot/config))) + (setf (slot-value config name) value)))))) + +(defun serialize (config) + "Serializes the config to a plist." + (iter + (for slot in (class-direct-slots (class-of config))) + (appending + (let* ((name (slot-definition-name slot)) + (kw-name (intern (symbol-name name) :keyword))) + (list kw-name (slot-value config name)))))) + +(defun print-default (filename) + "Prints the default config to the given `filename'." + (with-open-file (f filename :direction :output :if-exists :supersede) + (format f ";; lint:suppress in-package spdx-license-identifier~%") + (format f ";; Copy this file to config.lisp and modify it there~%") + (let ((data (serialize (make-config)))) + (format f "~<(~;~@{~(~W~) ~W~^ ~_~}~;)~:>~%" data)))) diff --git a/src/inline-bots.lisp b/src/inline-bots.lisp index 6001cb2..47dd81b 100644 --- a/src/inline-bots.lisp +++ b/src/inline-bots.lisp @@ -2,11 +2,12 @@ ;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs (defpackage :ukkoclot/inline-bots (:documentation "This package deals with removing unwanted inline bot usage") - (:use :c2cl :ukkoclot/config :ukkoclot/tg) + (:use :c2cl :ukkoclot/tg) (:import-from :com.dieggsy.f-string :enable-f-strings) + (:import-from :conf) (:import-from :log) (:import-from :ukkoclot/tg :send-message :try-delete-message) - (:import-from :ukkoclot/state :bot-config :bot-db) + (:import-from :ukkoclot/state :bot-db) (:local-nicknames (:db :ukkoclot/db)) (:export :blacklist-inline-bot :on-inline-bot :whitelist-inline-bot)) (in-package :ukkoclot/inline-bots) @@ -41,7 +42,7 @@ Its messages will no longer be deleted." :callback-data #f"bbl:{(user-id via)}"))) (send-message bot - :chat-id (config-dev-group (bot-config bot)) + :chat-id (conf:dev-group) :text #f"Deleted a message sent via inline bot @{(user-username via)} {(user-id via)}" :parse-mode html :reply-markup (make-inline-keyboard-markup diff --git a/src/main.lisp b/src/main.lisp index 5d3cf76..be17168 100644 --- a/src/main.lisp +++ b/src/main.lisp @@ -2,15 +2,16 @@ ;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs (defpackage :ukkoclot/main (:nicknames :ukkoclot) - (:use :c2cl :iterate :ukkoclot/config :ukkoclot/inline-bots :ukkoclot/tg) + (:use :c2cl :iterate :ukkoclot/inline-bots :ukkoclot/tg) (:import-from :alexandria :when-let) (:import-from :com.dieggsy.f-string :enable-f-strings) + (:import-from :conf) (:import-from :log) (:import-from :serapeum :drop) (:import-from :str) (:import-from :ukkoclot/db :with-db) (:import-from :ukkoclot/serializing :fixup-value) - (:import-from :ukkoclot/state :make-bot :bot-config :bot-power-on) + (:import-from :ukkoclot/state :make-bot :bot-power-on) (:import-from :ukkoclot/strings :escape-xml :is-tg-whitespace :is-tg-whitespace-str) (:local-nicknames (:jzon :com.inuoe.jzon)) @@ -31,25 +32,26 @@ (defun main () (log:config :debug) (unwind-protect - (let ((config (config-load #P"config.default.lisp"))) - (config-merge config #P"config.lisp") - (log:info "Starting up ~A" (config-bot-name config)) - (main-with-config config) + (progn + (conf:print-default #P"config.default.lisp") + (conf:load-config #P"config.lisp") + (log:info "Starting up ~A" (conf:bot-name)) + (main-with-config) nil) (log:info "Quitting!"))) -(defun main-with-config (config) +(defun main-with-config () (unwind-protect - (with-db (db (config-db-path config)) - (let ((bot (make-bot config db))) + (with-db (db (conf:db-path)) + (let ((bot (make-bot db))) ;; TODO: Catch fatal errors & report them - (wrapped-main bot config))) + (wrapped-main bot))) (log:info "We're done!"))) -(defun wrapped-main (bot config) +(defun wrapped-main (bot) (when *in-prod* - (send-message bot :chat-id (config-dev-group config) :text "Initializing...")) - (set-my-name bot :name (config-bot-name config)) + (send-message bot :chat-id (conf:dev-group) :text "Initializing...")) + (set-my-name bot :name (conf:bot-name)) (let ((gup-offset 0)) (loop while (bot-power-on bot) do (let ((updates (get-updates bot :timeout 60 :offset gup-offset))) @@ -65,14 +67,14 @@ (setf gup-offset (1+ (update-update-id update))))))) ;; One last getUpdates to make sure offset is stored on server (get-updates bot :timeout 0 :limit 1 :offset gup-offset)) - (send-message bot :chat-id (config-dev-group config) :text "Shutting down...")) + (send-message bot :chat-id (conf:dev-group) :text "Shutting down...")) (defun on-callback-query (bot cb) (let ((data (callback-query-data cb))) (cond ((and data (str:starts-with-p "bbl:" data :ignore-case nil) (= (user-id (callback-query-from cb)) - (config-owner (bot-config bot)))) + (conf:owner))) (let ((bot-id (read-from-string data t nil :start 4))) (blacklist-inline-bot bot bot-id)) (when-let (msg (callback-query-message cb)) @@ -85,7 +87,7 @@ ((and data (str:starts-with-p "bwl:" data :ignore-case nil) (= (user-id (callback-query-from cb)) - (config-owner (bot-config bot)))) + (conf:owner))) (let ((bot-id (read-from-string data t nil :start 4))) (whitelist-inline-bot bot bot-id)) (when-let (msg (callback-query-message cb)) @@ -269,7 +271,7 @@ ((and (equal simple-cmd "shutdown") (message-from msg) - (= (user-id (message-from msg)) (config-owner (bot-config bot)))) + (= (user-id (message-from msg)) (conf:owner))) (setf (bot-power-on bot) nil) (reply-message bot msg "Initialising shutdown..." :allow-sending-without-reply t))))) @@ -280,6 +282,6 @@ (log:error "While handling ~A: ~A" evt err) (let ((msg #f"{(escape-xml-obj err)} while handling{;~%}
{(escape-xml-obj evt)}
")) (send-message bot - :chat-id (config-dev-group (bot-config bot)) + :chat-id (conf:dev-group) :text msg :parse-mode html))) diff --git a/src/state.lisp b/src/state.lisp index 98e2048..597f0a8 100644 --- a/src/state.lisp +++ b/src/state.lisp @@ -1,13 +1,15 @@ ;; SPDX-License-Identifier: EUPL-1.2 ;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs (defpackage :ukkoclot/state - (:use :c2cl :ukkoclot/config) + (:documentation "Holds the global state") + (:use :c2cl) + (:import-from :com.dieggsy.f-string :enable-f-strings) + (:import-from :conf :bot-token) (:export #:bot #:make-bot #:bot-p #:copy-bot - #:bot-config #:bot-db #:bot-base-uri #:bot-power-on @@ -15,16 +17,15 @@ #:bot-id%)) (in-package :ukkoclot/state) +(enable-f-strings) + (defstruct (bot (:constructor make-bot%)) - (config (error "No value given for config") :read-only t) (db (error "No value given for DB") :read-only t) (base-uri (error "No value given for base-uri") :read-only t) (power-on t :type boolean) (username% nil :type (or string null)) (id% nil :type (or integer null))) -(defun make-bot (config db) - (let ((base-uri (concatenate 'string - "https://api.telegram.org/bot" - (config-bot-token config) "/"))) - (make-bot% :config config :db db :base-uri base-uri))) +(defun make-bot (db) + (let ((base-uri #f"https://api.telegram.org/bot{(bot-token)}/")) + (make-bot% :db db :base-uri base-uri))) -- cgit v1.2.3