chatikbot.lisp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. (in-package :cl-user)
  2. (defpackage chatikbot
  3. (:use :cl :alexandria)
  4. (:import-from :chatikbot.db
  5. :with-db
  6. :db-init
  7. :db-execute
  8. :db-select
  9. :load-settings)
  10. (:import-from :chatikbot.utils
  11. :*bot-name*
  12. :*admins*
  13. :aget
  14. :format-ts
  15. :run-hooks
  16. :loop-with-error-backoff)
  17. (:import-from :chatikbot.telegram
  18. :*telegram-token*
  19. :telegram-get-me
  20. :telegram-set-webhook
  21. :telegram-send-message)
  22. (:import-from :chatikbot.secrets
  23. :*secret-ring*
  24. :*secret-pass-store*
  25. :*secret-pass-bin*)
  26. (:import-from :chatikbot.macros
  27. :defcron)
  28. (:import-from :chatikbot.bot
  29. :process-updates
  30. :*bot-user-id*)
  31. (:import-from :chatikbot.server
  32. :*web-path*
  33. :*web-iface*
  34. :*web-port*)
  35. (:export :start))
  36. (in-package :chatikbot)
  37. (defvar *plugins* nil "list of enabled plugins.")
  38. (defun plugins-db-init ()
  39. (db-execute "create table if not exists plugins (name)")
  40. (db-execute "create unique index if not exists plugins_name_unique on plugins (name)"))
  41. (defun enable-plugin (name)
  42. (load (merge-pathnames (format nil "plugins/~A.lisp" name)
  43. (asdf:component-pathname
  44. (asdf:find-system '#:chatikbot))))
  45. (db-execute "replace into plugins (name) values (?)" name)
  46. (push name *plugins*))
  47. (defun disable-plugin (name)
  48. (db-execute "delete from plugins where name = ?" name)
  49. (setf *plugins* (delete name *plugins* :test #'equal)))
  50. (eval-when (:load-toplevel :execute)
  51. ;; Load config file
  52. (alexandria:when-let (file (probe-file "config.lisp"))
  53. (load file))
  54. ;; Init database
  55. (db-init)
  56. ;; Load plugins
  57. (plugins-db-init)
  58. (setf *plugins* (flatten (db-select "select name from plugins")))
  59. (dolist (plugin *plugins*)
  60. (log:info "Loading" plugin)
  61. (handler-case
  62. (load (merge-pathnames (format nil "plugins/~A.lisp" plugin)
  63. (asdf:component-pathname
  64. (asdf:find-system '#:chatikbot))))
  65. (error (e) (log:error e))))
  66. ;; Load settings
  67. (load-settings)
  68. ;; Init plugin's database
  69. (with-db (db)
  70. (run-hooks :db-init)))
  71. (defcron process-watchdog ()
  72. (close
  73. (open (merge-pathnames ".watchdog"
  74. (asdf:component-pathname
  75. (asdf:find-system '#:chatikbot)))
  76. :direction :output
  77. :if-exists :supersede
  78. :if-does-not-exist :create)))
  79. (defun cleanup ()
  80. ;; Clear prev threads
  81. (mapc #'trivial-timers:unschedule-timer (trivial-timers:list-all-timers))
  82. (let ((old-updates (find "process-updates"
  83. (bordeaux-threads:all-threads)
  84. :key #'bordeaux-threads:thread-name
  85. :test #'equal)))
  86. (when old-updates
  87. (bordeaux-threads:destroy-thread old-updates))))
  88. (defun start ()
  89. ;; Test telegram token
  90. (setf *bot-name* (concatenate 'string "@" (aget "username" (telegram-get-me)))
  91. *bot-user-id* (parse-integer *telegram-token*
  92. :end (position #\: *telegram-token*)))
  93. (cleanup)
  94. ;; Run 'starting' hooks to set up schedules
  95. (run-hooks :starting)
  96. (if *web-path*
  97. ;; If *web-path* is set, use webhooks
  98. (telegram-set-webhook (format nil "~A/~A" *web-path* *telegram-token*))
  99. ;; else start getUpdates thread
  100. (progn
  101. (telegram-set-webhook "") ;; Disable webhooks if present
  102. (bordeaux-threads:make-thread
  103. (lambda () (loop-with-error-backoff #'process-updates))
  104. :name "process-updates")))
  105. ;; Notify admins
  106. (dolist (admin *admins*)
  107. (ignore-errors
  108. (telegram-send-message admin (format nil "~A started at ~A" *bot-name* (format-ts (local-time:now)))))))