chatikbot.lisp 4.1 KB

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