grinch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/LICENSE +22 -0
  4. data/README.md +180 -0
  5. data/docs/bot_options.md +454 -0
  6. data/docs/changes.md +541 -0
  7. data/docs/common_mistakes.md +60 -0
  8. data/docs/common_tasks.md +57 -0
  9. data/docs/encodings.md +69 -0
  10. data/docs/events.md +273 -0
  11. data/docs/getting_started.md +184 -0
  12. data/docs/logging.md +90 -0
  13. data/docs/migrating.md +267 -0
  14. data/docs/plugins.md +4 -0
  15. data/docs/readme.md +20 -0
  16. data/examples/basic/autovoice.rb +32 -0
  17. data/examples/basic/google.rb +35 -0
  18. data/examples/basic/hello.rb +15 -0
  19. data/examples/basic/join_part.rb +34 -0
  20. data/examples/basic/memo.rb +39 -0
  21. data/examples/basic/msg.rb +16 -0
  22. data/examples/basic/seen.rb +36 -0
  23. data/examples/basic/urban_dict.rb +35 -0
  24. data/examples/basic/url_shorten.rb +35 -0
  25. data/examples/plugins/autovoice.rb +37 -0
  26. data/examples/plugins/custom_prefix.rb +23 -0
  27. data/examples/plugins/dice_roll.rb +38 -0
  28. data/examples/plugins/google.rb +36 -0
  29. data/examples/plugins/hello.rb +22 -0
  30. data/examples/plugins/hooks.rb +36 -0
  31. data/examples/plugins/join_part.rb +42 -0
  32. data/examples/plugins/lambdas.rb +35 -0
  33. data/examples/plugins/last_nick.rb +24 -0
  34. data/examples/plugins/msg.rb +22 -0
  35. data/examples/plugins/multiple_matches.rb +32 -0
  36. data/examples/plugins/own_events.rb +37 -0
  37. data/examples/plugins/seen.rb +45 -0
  38. data/examples/plugins/timer.rb +22 -0
  39. data/examples/plugins/url_shorten.rb +33 -0
  40. data/lib/cinch.rb +5 -0
  41. data/lib/cinch/ban.rb +50 -0
  42. data/lib/cinch/bot.rb +479 -0
  43. data/lib/cinch/cached_list.rb +19 -0
  44. data/lib/cinch/callback.rb +20 -0
  45. data/lib/cinch/channel.rb +463 -0
  46. data/lib/cinch/channel_list.rb +29 -0
  47. data/lib/cinch/configuration.rb +73 -0
  48. data/lib/cinch/configuration/bot.rb +48 -0
  49. data/lib/cinch/configuration/dcc.rb +16 -0
  50. data/lib/cinch/configuration/plugins.rb +41 -0
  51. data/lib/cinch/configuration/sasl.rb +19 -0
  52. data/lib/cinch/configuration/ssl.rb +19 -0
  53. data/lib/cinch/configuration/timeouts.rb +14 -0
  54. data/lib/cinch/constants.rb +533 -0
  55. data/lib/cinch/dcc.rb +12 -0
  56. data/lib/cinch/dcc/dccable_object.rb +37 -0
  57. data/lib/cinch/dcc/incoming.rb +1 -0
  58. data/lib/cinch/dcc/incoming/send.rb +147 -0
  59. data/lib/cinch/dcc/outgoing.rb +1 -0
  60. data/lib/cinch/dcc/outgoing/send.rb +122 -0
  61. data/lib/cinch/exceptions.rb +46 -0
  62. data/lib/cinch/formatting.rb +125 -0
  63. data/lib/cinch/handler.rb +118 -0
  64. data/lib/cinch/handler_list.rb +90 -0
  65. data/lib/cinch/helpers.rb +231 -0
  66. data/lib/cinch/irc.rb +924 -0
  67. data/lib/cinch/isupport.rb +98 -0
  68. data/lib/cinch/log_filter.rb +21 -0
  69. data/lib/cinch/logger.rb +168 -0
  70. data/lib/cinch/logger/formatted_logger.rb +97 -0
  71. data/lib/cinch/logger/zcbot_logger.rb +22 -0
  72. data/lib/cinch/logger_list.rb +85 -0
  73. data/lib/cinch/mask.rb +69 -0
  74. data/lib/cinch/message.rb +392 -0
  75. data/lib/cinch/message_queue.rb +107 -0
  76. data/lib/cinch/mode_parser.rb +76 -0
  77. data/lib/cinch/network.rb +104 -0
  78. data/lib/cinch/open_ended_queue.rb +26 -0
  79. data/lib/cinch/pattern.rb +65 -0
  80. data/lib/cinch/plugin.rb +515 -0
  81. data/lib/cinch/plugin_list.rb +38 -0
  82. data/lib/cinch/rubyext/float.rb +3 -0
  83. data/lib/cinch/rubyext/module.rb +26 -0
  84. data/lib/cinch/rubyext/string.rb +33 -0
  85. data/lib/cinch/sasl.rb +34 -0
  86. data/lib/cinch/sasl/dh_blowfish.rb +71 -0
  87. data/lib/cinch/sasl/diffie_hellman.rb +47 -0
  88. data/lib/cinch/sasl/mechanism.rb +6 -0
  89. data/lib/cinch/sasl/plain.rb +26 -0
  90. data/lib/cinch/syncable.rb +83 -0
  91. data/lib/cinch/target.rb +199 -0
  92. data/lib/cinch/timer.rb +145 -0
  93. data/lib/cinch/user.rb +488 -0
  94. data/lib/cinch/user_list.rb +87 -0
  95. data/lib/cinch/utilities/deprecation.rb +16 -0
  96. data/lib/cinch/utilities/encoding.rb +37 -0
  97. data/lib/cinch/utilities/kernel.rb +13 -0
  98. data/lib/cinch/version.rb +4 -0
  99. metadata +140 -0
@@ -0,0 +1,98 @@
1
+ module Cinch
2
+ # This class exposes parsed ISUPPORT information of the IRC network.
3
+ class ISupport < Hash
4
+ @@mappings = {
5
+ %w[PREFIX] => lambda {|v|
6
+ modes, prefixes = v.match(/^\((.+)\)(.+)$/)[1..2]
7
+ h = {}
8
+ modes.split("").each_with_index do |c, i|
9
+ h[c] = prefixes[i]
10
+ end
11
+ h
12
+ },
13
+
14
+ %w[CHANTYPES] => lambda {|v| v.split("")},
15
+ %w[CHANMODES] => lambda {|v|
16
+ h = {}
17
+ h["A"], h["B"], h["C"], h["D"] = v.split(",").map {|l| l.split("")}
18
+ h
19
+ },
20
+
21
+ %w[MODES MAXCHANNELS NICKLEN MAXBANS TOPICLEN
22
+ KICKLEN CHANNELLEN CHIDLEN SILENCE AWAYLEN
23
+ MAXTARGETS WATCH MONITOR] => lambda {|v| v.to_i},
24
+
25
+ %w[CHANLIMIT MAXLIST IDCHAN] => lambda {|v|
26
+ h = {}
27
+ v.split(",").each do |pair|
28
+ args, num = pair.split(":")
29
+ args.split("").each do |arg|
30
+ h[arg] = num.to_i
31
+ end
32
+ end
33
+ h
34
+ },
35
+
36
+ %w[TARGMAX] => lambda {|v|
37
+ h = {}
38
+ v.split(",").each do |pair|
39
+ name, value = pair.split(":")
40
+ h[name] = value.to_i
41
+ end
42
+ h
43
+ },
44
+
45
+ %w[NETWORK] => lambda {|v| v},
46
+ %w[STATUSMSG] => lambda {|v| v.split("")},
47
+ %w[CASEMAPPING] => lambda {|v| v.to_sym},
48
+ %w[ELIST] => lambda {|v| v.split("")},
49
+ # TODO STD
50
+ }
51
+
52
+ def initialize(*args)
53
+ super
54
+ # by setting most numeric values to "Infinity", we let the
55
+ # server truncate messages and lists while at the same time
56
+ # allowing the use of strictness=:strict for servers that don't
57
+ # support ISUPPORT (hopefully none, anyway)
58
+
59
+ self["PREFIX"] = {"o" => "@", "v" => "+"}
60
+ self["CHANTYPES"] = ["#"]
61
+ self["CHANMODES"] = {
62
+ "A" => ["b"],
63
+ "B" => ["k"],
64
+ "C" => ["l"],
65
+ "D" => %w[i m n p s t r]
66
+ }
67
+ self["MODES"] = 1
68
+ self["NICKLEN"] = Float::INFINITY
69
+ self["MAXBANS"] = Float::INFINITY
70
+ self["TOPICLEN"] = Float::INFINITY
71
+ self["KICKLEN"] = Float::INFINITY
72
+ self["CHANNELLEN"] = Float::INFINITY
73
+ self["CHIDLEN"] = 5
74
+ self["AWAYLEN"] = Float::INFINITY
75
+ self["MAXTARGETS"] = 1
76
+ self["MAXCHANNELS"] = Float::INFINITY # deprecated
77
+ self["CHANLIMIT"] = {"#" => Float::INFINITY}
78
+ self["STATUSMSG"] = []
79
+ self["CASEMAPPING"] = :rfc1459
80
+ self["ELIST"] = []
81
+ self["MONITOR"] = 0
82
+ end
83
+
84
+ # @api private
85
+ # @return [void]
86
+ def parse(*options)
87
+ options.each do |option|
88
+ name, value = option.split("=")
89
+ if value
90
+ proc = @@mappings.find {|key, _| key.include?(name)}
91
+ self[name] = (proc && proc[1].call(value)) || value
92
+ else
93
+ self[name] = true
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,21 @@
1
+ module Cinch
2
+ # LogFilter describes an interface for filtering log messages before
3
+ # they're printed.
4
+ #
5
+ # @abstract
6
+ # @since 2.3.0
7
+ class LogFilter
8
+ # filter is called for each log message, except for exceptions. It
9
+ # returns a new string, which is the one that should be printed, or
10
+ # further filtered by other filters. Returning nil will drop the
11
+ # message.
12
+ #
13
+ # @param [String] message The message that is to be logged
14
+ # @param [:debug, :incoming, :outgoing, :info, :warn, :exception,
15
+ # :error, :fatal] event The kind of message
16
+ # @return [String, nil] The modified message, as it should be
17
+ # logged, or nil if the message shouldn't be logged at all
18
+ def filter(message, event)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,168 @@
1
+ module Cinch
2
+ # This is the base logger class from which all loggers have to
3
+ # inherit.
4
+ #
5
+ # @version 2.0.0
6
+ class Logger
7
+ # @private
8
+ LevelOrder = [:debug, :log, :info, :warn, :error, :fatal]
9
+
10
+ # @return [Array<:debug, :log, :info, :warn, :error, :fatal>]
11
+ # The minimum level of events to log
12
+ attr_accessor :level
13
+
14
+ # @return [Mutex]
15
+ # @api private
16
+ attr_reader :mutex
17
+
18
+ # @return [IO]
19
+ # @api private
20
+ attr_reader :output
21
+
22
+ # @param [IO] output The I/O object to write log data to
23
+ def initialize(output, level: :debug)
24
+ @output = output
25
+ @mutex = Mutex.new
26
+ @level = level
27
+ end
28
+
29
+ # Logs a debugging message.
30
+ #
31
+ # @param [String] message
32
+ # @return [void]
33
+ # @version 2.0.0
34
+ def debug(message)
35
+ log(message, :debug)
36
+ end
37
+
38
+ # Logs an error message.
39
+ #
40
+ # @param [String] message
41
+ # @return [void]
42
+ # @since 2.0.0
43
+ def error(message)
44
+ log(message, :error)
45
+ end
46
+
47
+ # Logs a fatal message.
48
+ #
49
+ # @param [String] message
50
+ # @return [void]
51
+ # @since 2.0.0
52
+ def fatal(message)
53
+ log(message, :fatal)
54
+ end
55
+
56
+ # Logs an info message.
57
+ #
58
+ # @param [String] message
59
+ # @return [void]
60
+ # @since 2.0.0
61
+ def info(message)
62
+ log(message, :info)
63
+ end
64
+
65
+ # Logs a warning message.
66
+ #
67
+ # @param [String] message
68
+ # @return [void]
69
+ # @since 2.0.0
70
+ def warn(message)
71
+ log(message, :warn)
72
+ end
73
+
74
+ # Logs an incoming IRC message.
75
+ #
76
+ # @param [String] message
77
+ # @return [void]
78
+ # @since 2.0.0
79
+ def incoming(message)
80
+ log(message, :incoming, :log)
81
+ end
82
+
83
+ # Logs an outgoing IRC message.
84
+ #
85
+ # @param [String] message
86
+ # @return [void]
87
+ # @since 2.0.0
88
+ def outgoing(message)
89
+ log(message, :outgoing, :log)
90
+ end
91
+
92
+ # Logs an exception.
93
+ #
94
+ # @param [Exception] e
95
+ # @return [void]
96
+ # @since 2.0.0
97
+ def exception(e)
98
+ log(e.message, :exception, :error)
99
+ end
100
+
101
+ # Logs a message.
102
+ #
103
+ # @param [String, Array] messages The message(s) to log
104
+ # @param [:debug, :incoming, :outgoing, :info, :warn,
105
+ # :exception, :error, :fatal] event The kind of event that
106
+ # triggered the message
107
+ # @param [:debug, :info, :warn, :error, :fatal] level The level of the message
108
+ # @return [void]
109
+ # @version 2.0.0
110
+ def log(messages, event = :debug, level = event)
111
+ return unless will_log?(level)
112
+ @mutex.synchronize do
113
+ Array(messages).each do |message|
114
+ message = format_general(message)
115
+ message = format_message(message, event)
116
+
117
+ next if message.nil?
118
+ @output.puts message.encode("locale", {:invalid => :replace, :undef => :replace})
119
+ end
120
+ end
121
+ end
122
+
123
+ # @param [:debug, :info, :warn, :error, :fatal] level
124
+ # @return [Boolean] Whether the currently set logging level will
125
+ # allow the passed in level to be logged
126
+ # @since 2.0.0
127
+ def will_log?(level)
128
+ LevelOrder.index(level) >= LevelOrder.index(@level)
129
+ end
130
+
131
+ private
132
+ def format_message(message, level)
133
+ __send__ "format_#{level}", message
134
+ end
135
+
136
+ def format_general(message)
137
+ message
138
+ end
139
+
140
+ def format_debug(message)
141
+ message
142
+ end
143
+
144
+ def format_error(message)
145
+ message
146
+ end
147
+
148
+ def format_info(message)
149
+ message
150
+ end
151
+
152
+ def format_warn(message)
153
+ message
154
+ end
155
+
156
+ def format_incoming(message)
157
+ message
158
+ end
159
+
160
+ def format_outgoing(message)
161
+ message
162
+ end
163
+
164
+ def format_exception(message)
165
+ message
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,97 @@
1
+ require "cinch/logger"
2
+
3
+ module Cinch
4
+ class Logger
5
+ # @version 2.0.0
6
+ class FormattedLogger < Logger
7
+ # @private
8
+ Colors = {
9
+ :reset => "\e[0m",
10
+ :bold => "\e[1m",
11
+ :red => "\e[31m",
12
+ :green => "\e[32m",
13
+ :yellow => "\e[33m",
14
+ :blue => "\e[34m",
15
+ :black => "\e[30m",
16
+ :bg_white => "\e[47m",
17
+ }
18
+
19
+ # (see Logger#exception)
20
+ def exception(e)
21
+ lines = ["#{e.backtrace.first}: #{e.message} (#{e.class})"]
22
+ lines.concat e.backtrace[1..-1].map {|s| "\t" + s}
23
+ log(lines, :exception, :error)
24
+ end
25
+
26
+ private
27
+ def timestamp
28
+ Time.now.strftime("[%Y/%m/%d %H:%M:%S.%L]")
29
+ end
30
+
31
+ # @api private
32
+ # @param [String] text text to colorize
33
+ # @param [Array<Symbol>] codes array of colors to apply
34
+ # @return [String] colorized string
35
+ def colorize(text, *codes)
36
+ return text unless @output.tty?
37
+ codes = Colors.values_at(*codes).join
38
+ text = text.gsub(/#{Regexp.escape(Colors[:reset])}/, Colors[:reset] + codes)
39
+ codes + text + Colors[:reset]
40
+ end
41
+
42
+ def format_general(message)
43
+ message.gsub(/[^[:print:]]/) do |m|
44
+ colorize(m.inspect[1..-2], :bg_white, :black)
45
+ end
46
+ end
47
+
48
+ def format_debug(message)
49
+ "%s %s %s" % [timestamp, colorize("!!", :yellow), message]
50
+ end
51
+
52
+ def format_warn(message)
53
+ format_debug(message)
54
+ end
55
+
56
+ def format_info(message)
57
+ "%s %s %s" % [timestamp, "II", message]
58
+ end
59
+
60
+ def format_incoming(message)
61
+ pre, msg = message.split(" :", 2)
62
+ pre_parts = pre.split(" ")
63
+
64
+ prefix = colorize(">>", :green)
65
+
66
+ if pre_parts.size == 1
67
+ pre_parts[0] = colorize(pre_parts[0], :bold)
68
+ else
69
+ pre_parts[0] = colorize(pre_parts[0], :blue)
70
+ pre_parts[1] = colorize(pre_parts[1], :bold)
71
+ end
72
+
73
+ "%s %s %s %s" % [timestamp,
74
+ prefix,
75
+ pre_parts.join(" "),
76
+ msg ? colorize(":#{msg}", :yellow) : ""]
77
+ end
78
+
79
+ def format_outgoing(message)
80
+ pre, msg = message.split(" :", 2)
81
+ pre_parts = pre.split(" ")
82
+
83
+ prefix = colorize("<<", :red)
84
+ pre_parts[0] = colorize(pre_parts[0], :bold)
85
+
86
+ "%s %s %s %s" % [timestamp,
87
+ prefix,
88
+ pre_parts.join(" "),
89
+ msg ? colorize(":#{msg}", :yellow) : ""]
90
+ end
91
+
92
+ def format_exception(message)
93
+ "%s %s %s" % [timestamp, colorize("!!", :red), message]
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,22 @@
1
+ require "cinch/logger"
2
+ module Cinch
3
+ class Logger
4
+ # This logger logs all incoming messages in the format of zcbot.
5
+ # All other debug output (outgoing messages, exceptions, ...) will
6
+ # silently be dropped. The sole purpose of this logger is to
7
+ # produce logs parseable by pisg (with the zcbot formatter) to
8
+ # create channel statistics..
9
+ class ZcbotLogger < Cinch::Logger
10
+ # (see Logger#log)
11
+ def log(messages, event, level = event)
12
+ return if event != :incoming
13
+ super
14
+ end
15
+
16
+ private
17
+ def format_incoming(message)
18
+ Time.now.strftime("%m/%d/%Y %H:%M:%S ") + message
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,85 @@
1
+ module Cinch
2
+ # This class allows Cinch to use multiple loggers at once. A common
3
+ # use-case would be to log formatted messages to STDERR and a
4
+ # pisg-compatible log to a file.
5
+ #
6
+ # It inherits directly from Array, so adding new loggers is as easy
7
+ # as calling LoggerList#push.
8
+ #
9
+ # @attr_writer level
10
+ # @since 2.0.0
11
+ class LoggerList < Array
12
+ # A list of log filters that will be applied before emitting a log
13
+ # message.
14
+ #
15
+ # @return [Array<LogFilter>]
16
+ # @since 2.3.0
17
+ attr_accessor :filters
18
+ def initialize(*args)
19
+ @filters = []
20
+ super
21
+ end
22
+
23
+ # (see Logger#level=)
24
+ def level=(level)
25
+ each {|l| l.level = level}
26
+ end
27
+
28
+ # (see Logger#log)
29
+ def log(messages, event = :debug, level = event)
30
+ messages = Array(messages).map {|m| filter(m, event)}.compact
31
+ each {|l| l.log(messages, event, level)}
32
+ end
33
+
34
+ # (see Logger#debug)
35
+ def debug(message)
36
+ (m = filter(message, :debug)) && each {|l| l.debug(m)}
37
+ end
38
+
39
+ # (see Logger#error)
40
+ def error(message)
41
+ (m = filter(message, :error)) && each {|l| l.error(m)}
42
+ end
43
+
44
+ # (see Logger#error)
45
+ def fatal(message)
46
+ (m = filter(message, :fatal)) && each {|l| l.fatal(m)}
47
+ end
48
+
49
+ # (see Logger#info)
50
+ def info(message)
51
+ (m = filter(message, :info)) && each {|l| l.info(m)}
52
+ end
53
+
54
+ # (see Logger#warn)
55
+ def warn(message)
56
+ (m = filter(message, :warn)) && each {|l| l.warn(m)}
57
+ end
58
+
59
+ # (see Logger#incoming)
60
+ def incoming(message)
61
+ (m = filter(message, :incoming)) && each {|l| l.incoming(m)}
62
+ end
63
+
64
+ # (see Logger#outgoing)
65
+ def outgoing(message)
66
+ (m = filter(message, :outgoing)) && each {|l| l.outgoing(m)}
67
+ end
68
+
69
+ # (see Logger#exception)
70
+ def exception(e)
71
+ each {|l| l.exception(e)}
72
+ end
73
+
74
+ private
75
+ def filter(m, ev)
76
+ @filters.each do |f|
77
+ m = f.filter(m, ev)
78
+ if m.nil?
79
+ break
80
+ end
81
+ end
82
+ m
83
+ end
84
+ end
85
+ end