Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

znc.go 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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. // three cases:
  65. // 1. the user's PMs get played back immediately upon receiving this
  66. // 2. if this is a new connection (from the server's POV), save the information
  67. // and use it to process subsequent joins
  68. // 3. if this is a reattach (from the server's POV), immediately play back
  69. // history for channels that the client is already joined to. In this scenario,
  70. // there are three total attempts to play the history:
  71. // 3.1. During the initial reattach (no-op because the *playback privmsg
  72. // hasn't been received yet, but they negotiated the znc.in/playback
  73. // cap so we know we're going to receive it later)
  74. // 3.2 Upon receiving the *playback privmsg, i.e., now: we should play
  75. // the relevant history lines
  76. // 3.3 When the client sends a subsequent redundant JOIN line for those
  77. // channels; redundant JOIN is a complete no-op so we won't replay twice
  78. config := client.server.Config()
  79. if params[1] == "*" {
  80. items, _ := client.history.Between(after, before, false, config.History.ChathistoryMax)
  81. client.replayPrivmsgHistory(rb, items, true)
  82. } else {
  83. for _, targetName := range strings.Split(targetString, ",") {
  84. if cfTarget, err := CasefoldChannel(targetName); err == nil {
  85. if targets == nil {
  86. targets = make(map[string]bool)
  87. }
  88. targets[cfTarget] = true
  89. }
  90. }
  91. }
  92. rb.session.zncPlaybackTimes = &zncPlaybackTimes{
  93. after: after,
  94. before: before,
  95. targets: targets,
  96. }
  97. for _, channel := range client.Channels() {
  98. channel.autoReplayHistory(client, rb, "")
  99. rb.Flush(true)
  100. }
  101. }