1
0

db.lisp 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. (in-package #:chatikbot)
  2. (defvar *db-name* "db.sqlite" "SQLite database name")
  3. (defun db-path ()
  4. (merge-pathnames *db-name*
  5. (asdf:component-pathname
  6. (asdf:find-system '#:chatikbot))))
  7. (defmacro with-db ((db) &body body)
  8. `(sqlite:with-open-database (,db (db-path) :busy-timeout 10)
  9. ,@body))
  10. (defun db-init ()
  11. (sqlite:with-open-database (db (db-path))
  12. ;; Finance
  13. (sqlite:execute-non-query db "create table if not exists finance (ts, usd, eur, gbp, brent)")
  14. (sqlite:execute-non-query db "create index if not exists fin_ts_ids on finance (ts)")
  15. ;; Foursquare
  16. (sqlite:execute-non-query db "create table if not exists fsq_chat_users (chat_id, user_id)")
  17. (sqlite:execute-non-query db "create index if not exists fsq_chat_users_chat_idx on fsq_chat_users (chat_id)")
  18. (sqlite:execute-non-query db "create index if not exists fsq_chat_users_user_idx on fsq_chat_users (user_id)")
  19. (sqlite:execute-non-query db "create table if not exists fsq_seen (id, created_at)")
  20. (sqlite:execute-non-query db "create index if not exists fsq_seen_idx on fsq_seen (id)")))
  21. ;; Finance
  22. (defun db-add-finance (ts usd eur gbp brent)
  23. (sqlite:with-open-database (db (db-path) :busy-timeout 10)
  24. (sqlite:execute-non-query db "insert into finance (ts, usd, eur, gbp, brent) values (?, ?, ?, ?, ?)"
  25. ts usd eur gbp brent)))
  26. (defun db-get-last-finance ()
  27. (sqlite:with-open-database (db (db-path) :busy-timeout 10)
  28. (sqlite:execute-one-row-m-v db "select ts, usd, eur, gbp, brent from finance order by ts desc limit 1")))
  29. (defun %finance-alist (statement)
  30. (let ((names (sqlite:statement-column-names statement))
  31. (map '(("usd" . "USD/RUB") ("eur" . "EUR/RUB") ("gbp" . "GBP/RUB") ("brent" . "Brent"))))
  32. (cons (sqlite:statement-column-value statement 0)
  33. (loop
  34. for i from 1 below (length names)
  35. for col in (rest names)
  36. collect (cons (aget col map) (sqlite:statement-column-value statement i))))))
  37. (defun db-get-series (after-ts &optional usd eur gbp brent (avg 60))
  38. (let ((sql (format nil
  39. "select ts/~a*~a~:[~;,avg(usd) as usd~]~:[~;,avg(eur) as eur~]~:[~;,avg(gbp) as gbp~]~:[~;,avg(brent) as brent~] from finance where ts >= ? group by ts/~a order by ts"
  40. avg avg usd eur gbp brent avg)))
  41. (sqlite:with-open-database (db (db-path) :busy-timeout 10)
  42. (loop
  43. with statement = (sqlite:prepare-statement db sql)
  44. initially (sqlite:bind-parameter statement 1 (local-time:timestamp-to-unix after-ts))
  45. while (sqlite:step-statement statement)
  46. collect (%finance-alist statement)
  47. finally (sqlite:finalize-statement statement)))))
  48. ;; Foursquare
  49. (defun db-fsq-get-chat-users (chat-id)
  50. (flatten (with-db (db)
  51. (sqlite:execute-to-list db "select user_id from fsq_chat_users where chat_id = ?" chat-id))))
  52. (defun db-fsq-get-user-chats (user-id)
  53. (flatten (with-db (db)
  54. (sqlite:execute-to-list db "select chat_id from fsq_chat_users where user_id = ?" user-id))))
  55. (defun db-fsq-set-chat-users (chat-id users)
  56. (with-db (db)
  57. (sqlite:with-transaction db
  58. (sqlite:execute-non-query db "delete from fsq_chat_users where chat_id = ?" chat-id)
  59. (dolist (user-id users)
  60. (sqlite:execute-non-query db "insert into fsq_chat_users (chat_id, user_id) values (?, ?)" chat-id user-id)))))
  61. (defun db-fsq-add-seen (id created-at)
  62. (with-db (db)
  63. (sqlite:execute-non-query db "insert into fsq_seen (id, created_at) values (?, ?)" id created-at)))
  64. (defun db-fsq-has-seen (id)
  65. (with-db (db)
  66. (sqlite:execute-single db "select id from fsq_seen where id = ? limit 1" id)))
  67. (defun db-fsq-last-created ()
  68. (with-db (db)
  69. (sqlite:execute-single db "select created_at from fsq_seen order by created_at desc limit 1")))