1
0

utils.lisp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. (in-package #:chatikbot)
  2. (defvar *backoff-start* 1 "Initial back-off")
  3. (defvar *backoff-max* 64 "Maximum back-off delay")
  4. (defun loop-with-error-backoff (func)
  5. (let ((backoff *backoff-start*))
  6. (loop
  7. do
  8. (handler-case
  9. (progn
  10. (funcall func)
  11. (setf backoff *backoff-start*))
  12. (error (e)
  13. (log:error e)
  14. (log:info "Backing off for" backoff)
  15. (sleep backoff)
  16. (setf backoff (min *backoff-max*
  17. (* 2 backoff))))
  18. (bordeaux-threads:timeout (e)
  19. (log:error e)
  20. (log:info "Backing off for" backoff)
  21. (sleep backoff)
  22. (setf backoff (min *backoff-max*
  23. (* 2 backoff))))))))
  24. (defun replace-all (string part replacement &key (test #'char=))
  25. "Returns a new string in which all the occurences of the part
  26. is replaced with replacement."
  27. (with-output-to-string (out)
  28. (loop with part-length = (length part)
  29. for old-pos = 0 then (+ pos part-length)
  30. for pos = (search part string
  31. :start2 old-pos
  32. :test test)
  33. do (write-string string out
  34. :start old-pos
  35. :end (or pos (length string)))
  36. when pos do (write-string replacement out)
  37. while pos)))
  38. (defmacro aget (key alist)
  39. `(cdr (assoc ,key ,alist :test #'equal)))
  40. (defun mappend (fn &rest lists)
  41. "Apply fn to each element of lists and append the results."
  42. (apply #'append (apply #'mapcar fn lists)))
  43. (defun random-elt (choices)
  44. "Choose an element from a list at random."
  45. (elt choices (random (length choices))))
  46. (defun flatten (the-list)
  47. "Append together elements (or lists) in the list."
  48. (mappend #'(lambda (x) (if (listp x) (flatten x) (list x))) the-list))
  49. (defun make-circular (items)
  50. "Make items list circular"
  51. (setf (cdr (last items)) items))
  52. (defmacro push-circular (obj circ)
  53. "Move circ list and set head to obj"
  54. `(progn
  55. (pop ,circ)
  56. (setf (car ,circ) ,obj)))
  57. (defmacro peek-circular (circ)
  58. "Get head of circular list"
  59. `(car ,circ))
  60. (defmacro pop-circular (circ)
  61. "Get head of circular list"
  62. `(pop ,circ))
  63. (defun flat-circular (circ)
  64. "Flattens circular list"
  65. (do ((cur (cdr circ) (cdr cur))
  66. (head circ)
  67. result)
  68. ((eq head cur)
  69. (nreverse (push (car cur) result)))
  70. (push (car cur) result)))
  71. ;; Fix bug in local-time (following symlinks in /usr/share/zoneinfo/
  72. ;; leads to bad cutoff)
  73. (in-package #:local-time)
  74. (defun reread-timezone-repository (&key (timezone-repository *default-timezone-repository-path*))
  75. (check-type timezone-repository (or pathname string))
  76. (multiple-value-bind (valid? error)
  77. (ignore-errors
  78. (truename timezone-repository)
  79. t)
  80. (unless valid?
  81. (error "REREAD-TIMEZONE-REPOSITORY was called with invalid PROJECT-DIRECTORY (~A). The error is ~A."
  82. timezone-repository error)))
  83. (let* ((root-directory timezone-repository)
  84. (cutoff-position (length (princ-to-string root-directory))))
  85. (flet ((visitor (file)
  86. (handler-case
  87. (let* ((full-name (subseq (princ-to-string file) cutoff-position))
  88. (name (pathname-name file))
  89. (timezone (%realize-timezone (make-timezone :path file :name name))))
  90. (setf (gethash full-name *location-name->timezone*) timezone)
  91. (map nil (lambda (subzone)
  92. (push timezone (gethash (subzone-abbrev subzone)
  93. *abbreviated-subzone-name->timezone-list*)))
  94. (timezone-subzones timezone)))
  95. (invalid-timezone-file () nil))))
  96. (setf *location-name->timezone* (make-hash-table :test 'equal))
  97. (setf *abbreviated-subzone-name->timezone-list* (make-hash-table :test 'equal))
  98. (cl-fad:walk-directory root-directory #'visitor :directories nil :follow-symlinks nil
  99. :test (lambda (file)
  100. (not (find "Etc" (pathname-directory file) :test #'string=))))
  101. (cl-fad:walk-directory (merge-pathnames "Etc/" root-directory) #'visitor :directories nil))))
  102. (local-time:reread-timezone-repository :timezone-repository "/usr/share/zoneinfo/")