ircinch 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/.standard.yml +3 -0
  3. data/CHANGELOG.md +298 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/Gemfile +5 -0
  6. data/LICENSE.txt +23 -0
  7. data/README.md +195 -0
  8. data/Rakefile +14 -0
  9. data/docs/bot_options.md +454 -0
  10. data/docs/changes.md +541 -0
  11. data/docs/common_mistakes.md +60 -0
  12. data/docs/common_tasks.md +57 -0
  13. data/docs/encodings.md +69 -0
  14. data/docs/events.md +273 -0
  15. data/docs/getting_started.md +184 -0
  16. data/docs/logging.md +90 -0
  17. data/docs/migrating.md +267 -0
  18. data/docs/plugins.md +4 -0
  19. data/docs/readme.md +20 -0
  20. data/examples/basic/autovoice.rb +32 -0
  21. data/examples/basic/google.rb +35 -0
  22. data/examples/basic/hello.rb +14 -0
  23. data/examples/basic/join_part.rb +35 -0
  24. data/examples/basic/memo.rb +39 -0
  25. data/examples/basic/msg.rb +15 -0
  26. data/examples/basic/seen.rb +37 -0
  27. data/examples/basic/urban_dict.rb +36 -0
  28. data/examples/basic/url_shorten.rb +36 -0
  29. data/examples/plugins/autovoice.rb +37 -0
  30. data/examples/plugins/custom_prefix.rb +22 -0
  31. data/examples/plugins/dice_roll.rb +38 -0
  32. data/examples/plugins/google.rb +36 -0
  33. data/examples/plugins/hello.rb +21 -0
  34. data/examples/plugins/hooks.rb +34 -0
  35. data/examples/plugins/join_part.rb +41 -0
  36. data/examples/plugins/lambdas.rb +35 -0
  37. data/examples/plugins/last_nick.rb +24 -0
  38. data/examples/plugins/msg.rb +21 -0
  39. data/examples/plugins/multiple_matches.rb +32 -0
  40. data/examples/plugins/own_events.rb +37 -0
  41. data/examples/plugins/seen.rb +44 -0
  42. data/examples/plugins/timer.rb +22 -0
  43. data/examples/plugins/url_shorten.rb +34 -0
  44. data/ircinch.gemspec +43 -0
  45. data/lib/cinch/ban.rb +53 -0
  46. data/lib/cinch/bot.rb +476 -0
  47. data/lib/cinch/cached_list.rb +21 -0
  48. data/lib/cinch/callback.rb +22 -0
  49. data/lib/cinch/channel.rb +465 -0
  50. data/lib/cinch/channel_list.rb +31 -0
  51. data/lib/cinch/configuration/bot.rb +50 -0
  52. data/lib/cinch/configuration/dcc.rb +18 -0
  53. data/lib/cinch/configuration/plugins.rb +43 -0
  54. data/lib/cinch/configuration/sasl.rb +21 -0
  55. data/lib/cinch/configuration/ssl.rb +21 -0
  56. data/lib/cinch/configuration/timeouts.rb +16 -0
  57. data/lib/cinch/configuration.rb +75 -0
  58. data/lib/cinch/constants.rb +535 -0
  59. data/lib/cinch/dcc/dccable_object.rb +39 -0
  60. data/lib/cinch/dcc/incoming/send.rb +149 -0
  61. data/lib/cinch/dcc/incoming.rb +3 -0
  62. data/lib/cinch/dcc/outgoing/send.rb +123 -0
  63. data/lib/cinch/dcc/outgoing.rb +3 -0
  64. data/lib/cinch/dcc.rb +14 -0
  65. data/lib/cinch/exceptions.rb +48 -0
  66. data/lib/cinch/formatting.rb +127 -0
  67. data/lib/cinch/handler.rb +120 -0
  68. data/lib/cinch/handler_list.rb +92 -0
  69. data/lib/cinch/helpers.rb +230 -0
  70. data/lib/cinch/i_support.rb +100 -0
  71. data/lib/cinch/irc.rb +924 -0
  72. data/lib/cinch/log_filter.rb +23 -0
  73. data/lib/cinch/logger/formatted_logger.rb +100 -0
  74. data/lib/cinch/logger/zcbot_logger.rb +26 -0
  75. data/lib/cinch/logger.rb +171 -0
  76. data/lib/cinch/logger_list.rb +88 -0
  77. data/lib/cinch/mask.rb +69 -0
  78. data/lib/cinch/message.rb +397 -0
  79. data/lib/cinch/message_queue.rb +104 -0
  80. data/lib/cinch/mode_parser.rb +78 -0
  81. data/lib/cinch/network.rb +106 -0
  82. data/lib/cinch/open_ended_queue.rb +26 -0
  83. data/lib/cinch/pattern.rb +66 -0
  84. data/lib/cinch/plugin.rb +517 -0
  85. data/lib/cinch/plugin_list.rb +40 -0
  86. data/lib/cinch/rubyext/float.rb +5 -0
  87. data/lib/cinch/rubyext/module.rb +28 -0
  88. data/lib/cinch/rubyext/string.rb +35 -0
  89. data/lib/cinch/sasl/dh_blowfish.rb +73 -0
  90. data/lib/cinch/sasl/diffie_hellman.rb +50 -0
  91. data/lib/cinch/sasl/mechanism.rb +8 -0
  92. data/lib/cinch/sasl/plain.rb +29 -0
  93. data/lib/cinch/sasl.rb +36 -0
  94. data/lib/cinch/syncable.rb +83 -0
  95. data/lib/cinch/target.rb +199 -0
  96. data/lib/cinch/timer.rb +147 -0
  97. data/lib/cinch/user.rb +489 -0
  98. data/lib/cinch/user_list.rb +89 -0
  99. data/lib/cinch/utilities/deprecation.rb +18 -0
  100. data/lib/cinch/utilities/encoding.rb +39 -0
  101. data/lib/cinch/utilities/kernel.rb +15 -0
  102. data/lib/cinch/version.rb +6 -0
  103. data/lib/cinch.rb +7 -0
  104. data/lib/ircinch.rb +7 -0
  105. metadata +205 -0
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cinch
4
+ # LogFilter describes an interface for filtering log messages before
5
+ # they're printed.
6
+ #
7
+ # @abstract
8
+ # @since 2.3.0
9
+ class LogFilter
10
+ # filter is called for each log message, except for exceptions. It
11
+ # returns a new string, which is the one that should be printed, or
12
+ # further filtered by other filters. Returning nil will drop the
13
+ # message.
14
+ #
15
+ # @param [String] message The message that is to be logged
16
+ # @param [:debug, :incoming, :outgoing, :info, :warn, :exception,
17
+ # :error, :fatal] event The kind of message
18
+ # @return [String, nil] The modified message, as it should be
19
+ # logged, or nil if the message shouldn't be logged at all
20
+ def filter(message, event)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../logger"
4
+
5
+ module Cinch
6
+ class Logger
7
+ # @version 2.0.0
8
+ class FormattedLogger < Logger
9
+ # @private
10
+ COLORS = {
11
+ reset: "\e[0m",
12
+ bold: "\e[1m",
13
+ red: "\e[31m",
14
+ green: "\e[32m",
15
+ yellow: "\e[33m",
16
+ blue: "\e[34m",
17
+ black: "\e[30m",
18
+ bg_white: "\e[47m"
19
+ }
20
+
21
+ # (see Logger#exception)
22
+ def exception(e)
23
+ lines = ["#{e.backtrace.first}: #{e.message} (#{e.class})"]
24
+ lines.concat e.backtrace[1..].map { |s| "\t" + s }
25
+ log(lines, :exception, :error)
26
+ end
27
+
28
+ private
29
+
30
+ def timestamp
31
+ Time.now.strftime("[%Y/%m/%d %H:%M:%S.%L]")
32
+ end
33
+
34
+ # @api private
35
+ # @param [String] text text to colorize
36
+ # @param [Array<Symbol>] codes array of colors to apply
37
+ # @return [String] colorized string
38
+ def colorize(text, *codes)
39
+ return text unless @output.tty?
40
+ codes = COLORS.values_at(*codes).join
41
+ text = text.gsub(/#{Regexp.escape(COLORS[:reset])}/, COLORS[:reset] + codes)
42
+ codes + text + COLORS[:reset]
43
+ end
44
+
45
+ def format_general(message)
46
+ message.gsub(/[^[:print:]]/) do |m|
47
+ colorize(m.inspect[1..-2], :bg_white, :black)
48
+ end
49
+ end
50
+
51
+ def format_debug(message)
52
+ "%s %s %s" % [timestamp, colorize("!!", :yellow), message]
53
+ end
54
+
55
+ def format_warn(message)
56
+ format_debug(message)
57
+ end
58
+
59
+ def format_info(message)
60
+ "%s %s %s" % [timestamp, "II", message]
61
+ end
62
+
63
+ def format_incoming(message)
64
+ pre, msg = message.split(" :", 2)
65
+ pre_parts = pre.split(" ")
66
+
67
+ prefix = colorize(">>", :green)
68
+
69
+ if pre_parts.size == 1
70
+ pre_parts[0] = colorize(pre_parts[0], :bold)
71
+ else
72
+ pre_parts[0] = colorize(pre_parts[0], :blue)
73
+ pre_parts[1] = colorize(pre_parts[1], :bold)
74
+ end
75
+
76
+ "%s %s %s %s" % [timestamp,
77
+ prefix,
78
+ pre_parts.join(" "),
79
+ msg ? colorize(":#{msg}", :yellow) : ""]
80
+ end
81
+
82
+ def format_outgoing(message)
83
+ pre, msg = message.split(" :", 2)
84
+ pre_parts = pre.split(" ")
85
+
86
+ prefix = colorize("<<", :red)
87
+ pre_parts[0] = colorize(pre_parts[0], :bold)
88
+
89
+ "%s %s %s %s" % [timestamp,
90
+ prefix,
91
+ pre_parts.join(" "),
92
+ msg ? colorize(":#{msg}", :yellow) : ""]
93
+ end
94
+
95
+ def format_exception(message)
96
+ "%s %s %s" % [timestamp, colorize("!!", :red), message]
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../logger"
4
+
5
+ module Cinch
6
+ class Logger
7
+ # This logger logs all incoming messages in the format of zcbot.
8
+ # All other debug output (outgoing messages, exceptions, ...) will
9
+ # silently be dropped. The sole purpose of this logger is to
10
+ # produce logs parseable by pisg (with the zcbot formatter) to
11
+ # create channel statistics..
12
+ class ZcbotLogger < Cinch::Logger
13
+ # (see Logger#log)
14
+ def log(messages, event, level = event)
15
+ return if event != :incoming
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ def format_incoming(message)
22
+ Time.now.strftime("%m/%d/%Y %H:%M:%S ") + message
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cinch
4
+ # This is the base logger class from which all loggers have to
5
+ # inherit.
6
+ #
7
+ # @version 2.0.0
8
+ class Logger
9
+ # @private
10
+ LEVEL_ORDER = [:debug, :log, :info, :warn, :error, :fatal]
11
+
12
+ # @return [Array<:debug, :log, :info, :warn, :error, :fatal>]
13
+ # The minimum level of events to log
14
+ attr_accessor :level
15
+
16
+ # @return [Mutex]
17
+ # @api private
18
+ attr_reader :mutex
19
+
20
+ # @return [IO]
21
+ # @api private
22
+ attr_reader :output
23
+
24
+ # @param [IO] output The I/O object to write log data to
25
+ def initialize(output, level: :debug)
26
+ @output = output
27
+ @mutex = Mutex.new
28
+ @level = level
29
+ end
30
+
31
+ # Logs a debugging message.
32
+ #
33
+ # @param [String] message
34
+ # @return [void]
35
+ # @version 2.0.0
36
+ def debug(message)
37
+ log(message, :debug)
38
+ end
39
+
40
+ # Logs an error message.
41
+ #
42
+ # @param [String] message
43
+ # @return [void]
44
+ # @since 2.0.0
45
+ def error(message)
46
+ log(message, :error)
47
+ end
48
+
49
+ # Logs a fatal message.
50
+ #
51
+ # @param [String] message
52
+ # @return [void]
53
+ # @since 2.0.0
54
+ def fatal(message)
55
+ log(message, :fatal)
56
+ end
57
+
58
+ # Logs an info message.
59
+ #
60
+ # @param [String] message
61
+ # @return [void]
62
+ # @since 2.0.0
63
+ def info(message)
64
+ log(message, :info)
65
+ end
66
+
67
+ # Logs a warning message.
68
+ #
69
+ # @param [String] message
70
+ # @return [void]
71
+ # @since 2.0.0
72
+ def warn(message)
73
+ log(message, :warn)
74
+ end
75
+
76
+ # Logs an incoming IRC message.
77
+ #
78
+ # @param [String] message
79
+ # @return [void]
80
+ # @since 2.0.0
81
+ def incoming(message)
82
+ log(message, :incoming, :log)
83
+ end
84
+
85
+ # Logs an outgoing IRC message.
86
+ #
87
+ # @param [String] message
88
+ # @return [void]
89
+ # @since 2.0.0
90
+ def outgoing(message)
91
+ log(message, :outgoing, :log)
92
+ end
93
+
94
+ # Logs an exception.
95
+ #
96
+ # @param [Exception] e
97
+ # @return [void]
98
+ # @since 2.0.0
99
+ def exception(e)
100
+ log(e.message, :exception, :error)
101
+ end
102
+
103
+ # Logs a message.
104
+ #
105
+ # @param [String, Array] messages The message(s) to log
106
+ # @param [:debug, :incoming, :outgoing, :info, :warn,
107
+ # :exception, :error, :fatal] event The kind of event that
108
+ # triggered the message
109
+ # @param [:debug, :info, :warn, :error, :fatal] level The level of the message
110
+ # @return [void]
111
+ # @version 2.0.0
112
+ def log(messages, event = :debug, level = event)
113
+ return unless will_log?(level)
114
+ @mutex.synchronize do
115
+ Array(messages).each do |message|
116
+ message = format_general(message)
117
+ message = format_message(message, event)
118
+
119
+ next if message.nil?
120
+ @output.puts message.encode("locale", invalid: :replace, undef: :replace)
121
+ end
122
+ end
123
+ end
124
+
125
+ # @param [:debug, :info, :warn, :error, :fatal] level
126
+ # @return [Boolean] Whether the currently set logging level will
127
+ # allow the passed in level to be logged
128
+ # @since 2.0.0
129
+ def will_log?(level)
130
+ LEVEL_ORDER.index(level) >= LEVEL_ORDER.index(@level)
131
+ end
132
+
133
+ private
134
+
135
+ def format_message(message, level)
136
+ __send__ "format_#{level}", message
137
+ end
138
+
139
+ def format_general(message)
140
+ message
141
+ end
142
+
143
+ def format_debug(message)
144
+ message
145
+ end
146
+
147
+ def format_error(message)
148
+ message
149
+ end
150
+
151
+ def format_info(message)
152
+ message
153
+ end
154
+
155
+ def format_warn(message)
156
+ message
157
+ end
158
+
159
+ def format_incoming(message)
160
+ message
161
+ end
162
+
163
+ def format_outgoing(message)
164
+ message
165
+ end
166
+
167
+ def format_exception(message)
168
+ message
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cinch
4
+ # This class allows Cinch to use multiple loggers at once. A common
5
+ # use-case would be to log formatted messages to STDERR and a
6
+ # pisg-compatible log to a file.
7
+ #
8
+ # It inherits directly from Array, so adding new loggers is as easy
9
+ # as calling LoggerList#push.
10
+ #
11
+ # @attr_writer level
12
+ # @since 2.0.0
13
+ class LoggerList < Array
14
+ # A list of log filters that will be applied before emitting a log
15
+ # message.
16
+ #
17
+ # @return [Array<LogFilter>]
18
+ # @since 2.3.0
19
+ attr_accessor :filters
20
+ def initialize(*args)
21
+ @filters = []
22
+ super
23
+ end
24
+
25
+ # (see Logger#level=)
26
+ def level=(level)
27
+ each { |l| l.level = level }
28
+ end
29
+
30
+ # (see Logger#log)
31
+ def log(messages, event = :debug, level = event)
32
+ messages = Array(messages).map { |m| filter(m, event) }.compact
33
+ each { |l| l.log(messages, event, level) }
34
+ end
35
+
36
+ # (see Logger#debug)
37
+ def debug(message)
38
+ (m = filter(message, :debug)) && each { |l| l.debug(m) }
39
+ end
40
+
41
+ # (see Logger#error)
42
+ def error(message)
43
+ (m = filter(message, :error)) && each { |l| l.error(m) }
44
+ end
45
+
46
+ # (see Logger#error)
47
+ def fatal(message)
48
+ (m = filter(message, :fatal)) && each { |l| l.fatal(m) }
49
+ end
50
+
51
+ # (see Logger#info)
52
+ def info(message)
53
+ (m = filter(message, :info)) && each { |l| l.info(m) }
54
+ end
55
+
56
+ # (see Logger#warn)
57
+ def warn(message)
58
+ (m = filter(message, :warn)) && each { |l| l.warn(m) }
59
+ end
60
+
61
+ # (see Logger#incoming)
62
+ def incoming(message)
63
+ (m = filter(message, :incoming)) && each { |l| l.incoming(m) }
64
+ end
65
+
66
+ # (see Logger#outgoing)
67
+ def outgoing(message)
68
+ (m = filter(message, :outgoing)) && each { |l| l.outgoing(m) }
69
+ end
70
+
71
+ # (see Logger#exception)
72
+ def exception(e)
73
+ each { |l| l.exception(e) }
74
+ end
75
+
76
+ private
77
+
78
+ def filter(m, ev)
79
+ @filters.each do |f|
80
+ m = f.filter(m, ev)
81
+ if m.nil?
82
+ break
83
+ end
84
+ end
85
+ m
86
+ end
87
+ end
88
+ end
data/lib/cinch/mask.rb ADDED
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cinch
4
+ # This class represents masks, which are primarily used for bans.
5
+ class Mask
6
+ # @return [String]
7
+ attr_reader :nick
8
+ # @return [String]
9
+ attr_reader :user
10
+ # @return [String]
11
+ attr_reader :host
12
+ # @return [String]
13
+ attr_reader :mask
14
+
15
+ # @version 1.1.2
16
+ # @param [String] mask
17
+ def initialize(mask)
18
+ @mask = mask
19
+ @nick, @user, @host = mask.match(/(.+)!(.+)@(.+)/)[1..]
20
+ @regexp = Regexp.new("^" + Regexp.escape(mask).gsub("\\*", ".*").gsub("\\?", ".?") + "$")
21
+ end
22
+
23
+ # @return [Boolean]
24
+ # @since 1.1.0
25
+ def ==(other)
26
+ other.respond_to?(:mask) && other.mask == @mask
27
+ end
28
+
29
+ # @return [Boolean]
30
+ # @since 1.1.0
31
+ def eql?(other)
32
+ other.is_a?(self.class) && self == other
33
+ end
34
+
35
+ # @return [Fixnum]
36
+ def hash
37
+ @mask.hash
38
+ end
39
+
40
+ # @param [Mask, String, #mask] target
41
+ # @return [Boolean]
42
+ # @version 1.1.2
43
+ def match(target)
44
+ self.class.from(target).mask =~ @regexp
45
+
46
+ # TODO support CIDR (freenode)
47
+ end
48
+ alias_method :=~, :match
49
+
50
+ # @return [String]
51
+ def to_s
52
+ @mask.dup
53
+ end
54
+
55
+ # @param [String, #mask] target
56
+ # @return [target] if already a Mask
57
+ # @return [Mask]
58
+ # @version 2.0.0
59
+ def self.from(target)
60
+ return target if target.is_a?(self)
61
+
62
+ if target.respond_to?(:mask)
63
+ target.mask
64
+ else
65
+ Mask.new(target.to_s)
66
+ end
67
+ end
68
+ end
69
+ end