You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

znc.go 2.9KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // Copyright (c) 2019 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "time"
  9. )
  10. type zncCommandHandler func(client *Client, command string, params []string, rb *ResponseBuffer)
  11. var zncHandlers = map[string]zncCommandHandler{
  12. "*playback": zncPlaybackHandler,
  13. }
  14. func zncPrivmsgHandler(client *Client, command string, privmsg string, rb *ResponseBuffer) {
  15. zncModuleHandler(client, command, strings.Fields(privmsg), rb)
  16. }
  17. func zncModuleHandler(client *Client, command string, params []string, rb *ResponseBuffer) {
  18. command = strings.ToLower(command)
  19. if subHandler, ok := zncHandlers[command]; ok {
  20. subHandler(client, command, params, rb)
  21. } else {
  22. nick := rb.target.Nick()
  23. rb.Add(nil, client.server.name, "NOTICE", nick, fmt.Sprintf(client.t("Oragono does not emulate the ZNC module %s"), command))
  24. rb.Add(nil, "*status!znc@znc.in", "NOTICE", nick, fmt.Sprintf(client.t("No such module [%s]"), command))
  25. }
  26. }
  27. // "number of seconds (floating point for millisecond precision) elapsed since January 1, 1970"
  28. func zncWireTimeToTime(str string) (result time.Time) {
  29. var secondsPortion, fracPortion string
  30. dot := strings.IndexByte(str, '.')
  31. if dot == -1 {
  32. secondsPortion = str
  33. } else {
  34. secondsPortion = str[:dot]
  35. fracPortion = str[dot:]
  36. }
  37. seconds, _ := strconv.ParseInt(secondsPortion, 10, 64)
  38. fraction, _ := strconv.ParseFloat(fracPortion, 64)
  39. return time.Unix(seconds, int64(fraction*1000000000))
  40. }
  41. type zncPlaybackTimes struct {
  42. after time.Time
  43. before time.Time
  44. targets map[string]bool // nil for "*" (everything), otherwise the channel names
  45. }
  46. // https://wiki.znc.in/Playback
  47. // PRIVMSG *playback :play <target> [lower_bound] [upper_bound]
  48. // e.g., PRIVMSG *playback :play * 1558374442
  49. func zncPlaybackHandler(client *Client, command string, params []string, rb *ResponseBuffer) {
  50. if len(params) < 2 {
  51. return
  52. } else if strings.ToLower(params[0]) != "play" {
  53. return
  54. }
  55. targetString := params[1]
  56. var after, before time.Time
  57. if 2 < len(params) {
  58. after = zncWireTimeToTime(params[2])
  59. }
  60. if 3 < len(params) {
  61. before = zncWireTimeToTime(params[3])
  62. }
  63. var targets map[string]bool
  64. // OK: the user's PMs get played back immediately on receiving this,
  65. // then we save the timestamps in the session to handle replay on future channel joins
  66. config := client.server.Config()
  67. if params[1] == "*" {
  68. items, _ := client.history.Between(after, before, false, config.History.ChathistoryMax)
  69. client.replayPrivmsgHistory(rb, items, true)
  70. } else {
  71. for _, targetName := range strings.Split(targetString, ",") {
  72. if cfTarget, err := CasefoldChannel(targetName); err == nil {
  73. if targets == nil {
  74. targets = make(map[string]bool)
  75. }
  76. targets[cfTarget] = true
  77. }
  78. }
  79. }
  80. rb.session.zncPlaybackTimes = &zncPlaybackTimes{
  81. after: after,
  82. before: before,
  83. targets: targets,
  84. }
  85. }