package calculon

  1. Overview
  2. Docs

Source file Plugin_history.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
(** {1 History plugin} *)

(** Keep the last [n] lines of history on a chan, and give them to newcomers
    that ask for them *)

(* time, nick, msg *)
type line = { time: float; nick_: string; msg: string }

type t = {
  actions: Plugin.action_callback;
  hist: line Queue.t;
  size: int; (* max size of [hist] *)
  default_len: int; (* default length of history in query *)
}

let on_msg state _ m =
  match Core.privmsg_of_msg m with
  | None -> Lwt.return ()
  | Some { Core.to_; _ } when not (Core.is_chan to_) ->
    Lwt.return () (* ignore private messages *)
  | Some { Core.nick; to_ = _; message } ->
    let time = Unix.gettimeofday () in
    let line = { time; nick_ = nick; msg = message } in
    (* pop oldest item if the queue is full *)
    if Queue.length state.hist >= state.size then ignore (Queue.pop state.hist);
    Queue.push line state.hist;
    Lwt.return ()

(* list of lines in history *)
let reply_history state n : string list =
  assert (n > 0);
  Queue.fold
    (fun acc line ->
      let time =
        Ptime.of_float_s line.time |> Option.get_or "invalid timestamp"
      in
      let line' =
        Printf.sprintf "[%s] %s: %s"
          (Ptime.to_rfc3339 ~space:false time)
          line.nick_ line.msg
      in
      line' :: acc)
    [] state.hist
  |> CCList.take n (* take the last [n] messages *)
  |> List.rev

let cmd_history st : _ =
  Command.make_simple_query_l
    ~descr:
      (Printf.sprintf "give back <n> lines of history in query (max %d)" st.size)
    ~prio:10 ~cmd:"history" (fun _ msg ->
      let msg = String.trim msg in
      if msg = "" then
        Lwt.return @@ reply_history st st.default_len
      else
        Lwt.return
          (* parse the number of lines *)
          (try
             let n = int_of_string msg in
             if n > 0 then
               reply_history st n
             else
               [ Talk.select Talk.Err ]
           with _ -> [ Talk.select Talk.Err ]))

let plugin ?(default_len = 10) ?(n = 150) () =
  Plugin.stateful ~name:"history"
    ~of_json:(fun actions _ ->
      Ok { actions; size = n; default_len; hist = Queue.create () })
    ~to_json:(fun _ -> None)
    ~on_msg:(fun state -> [ on_msg state ])
    ~stop:(fun _ -> Lwt.return ())
    ~commands:(fun st -> [ cmd_history st ])
    ()
OCaml

Innovation. Community. Security.