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,36 @@
1
+ require "cgi"
2
+ require "ircinch"
3
+ require "nokogiri"
4
+ require "open-uri"
5
+
6
+ class Google
7
+ include Cinch::Plugin
8
+ match(/google (.+)/)
9
+
10
+ def search(query)
11
+ url = "http://www.google.com/search?q=#{CGI.escape(query)}"
12
+ res = Nokogiri.HTML(URI.parse(url).open).at("h3.r")
13
+
14
+ title = res.text
15
+ link = res.at("a")[:href]
16
+ desc = res.at("./following::div").children.first.text
17
+ CGI.unescape_html "#{title} - #{desc} (#{link})"
18
+ rescue
19
+ "No results found"
20
+ end
21
+
22
+ def execute(m, query)
23
+ m.reply(search(query))
24
+ end
25
+ end
26
+
27
+ bot = Cinch::Bot.new do
28
+ configure do |c|
29
+ c.server = "irc.libera.chat"
30
+ c.nick = "MrIrCinch"
31
+ c.channels = ["#ircinch-bots"]
32
+ c.plugins.plugins = [Google]
33
+ end
34
+ end
35
+
36
+ bot.start
@@ -0,0 +1,21 @@
1
+ require "ircinch"
2
+
3
+ class Hello
4
+ include Cinch::Plugin
5
+
6
+ match "hello"
7
+
8
+ def execute(m)
9
+ m.reply "Hello, #{m.user.nick}"
10
+ end
11
+ end
12
+
13
+ bot = Cinch::Bot.new do
14
+ configure do |c|
15
+ c.server = "irc.libera.chat"
16
+ c.channels = ["#ircinch-bots"]
17
+ c.plugins.plugins = [Hello]
18
+ end
19
+ end
20
+
21
+ bot.start
@@ -0,0 +1,34 @@
1
+ require "cinch"
2
+
3
+ class HooksDemo
4
+ include Cinch::Plugin
5
+
6
+ hook :pre, method: :generate_random_number
7
+ def generate_random_number(m)
8
+ # Hooks are called in the same thread as the handler and thus
9
+ # using thread local variables is possible.
10
+ Thread.current[:rand] = Kernel.rand
11
+ end
12
+
13
+ hook :post, method: :cheer
14
+ def cheer(m)
15
+ m.reply "Yay, I successfully ran a command…"
16
+ end
17
+
18
+ match "rand"
19
+ def execute(m)
20
+ m.reply "Random number: " + Thread.current[:rand].to_s
21
+ end
22
+ end
23
+
24
+ bot = Cinch::Bot.new do
25
+ configure do |c|
26
+ c.nick = "ircinch_hooks"
27
+ c.server = "irc.libera.chat"
28
+ c.channels = ["#ircinch-bots"]
29
+ c.verbose = true
30
+ c.plugins.plugins = [HooksDemo]
31
+ end
32
+ end
33
+
34
+ bot.start
@@ -0,0 +1,41 @@
1
+ require "ircinch"
2
+
3
+ class JoinPart
4
+ include Cinch::Plugin
5
+
6
+ match(/join (.+)/, method: :join)
7
+ match(/part(?: (.+))?/, method: :part)
8
+
9
+ def initialize(*args)
10
+ super
11
+
12
+ @admins = ["injekt", "DominikH"]
13
+ end
14
+
15
+ def check_user(user)
16
+ user.refresh # be sure to refresh the data, or someone could steal the nick
17
+ @admins.include?(user.authname)
18
+ end
19
+
20
+ def join(m, channel)
21
+ return unless check_user(m.user)
22
+ Channel(channel).join
23
+ end
24
+
25
+ def part(m, channel)
26
+ return unless check_user(m.user)
27
+ channel ||= m.channel
28
+ Channel(channel).part if channel
29
+ end
30
+ end
31
+
32
+ bot = Cinch::Bot.new do
33
+ configure do |c|
34
+ c.server = "irc.libera.chat"
35
+ c.nick = "IrCinchBot"
36
+ c.channels = ["#ircinch-bots"]
37
+ c.plugins.plugins = [JoinPart]
38
+ end
39
+ end
40
+
41
+ bot.start
@@ -0,0 +1,35 @@
1
+ require "ircinch"
2
+
3
+ class DirectAddressing
4
+ include Cinch::Plugin
5
+
6
+ # Note: the lambda will be executed in the context it has been
7
+ # defined in, in this case the class DirectAddressing (and not an
8
+ # instance of said class).
9
+ #
10
+ # The reason we are using a lambda is that the bot's nick can change
11
+ # and the prefix has to be up to date.
12
+ set :prefix, lambda { |m| Regexp.new("^" + Regexp.escape(m.bot.nick + ": ")) }
13
+
14
+ match "hello", method: :greet
15
+ def greet(m)
16
+ m.reply "Hello to you, too."
17
+ end
18
+
19
+ match "rename", method: :rename
20
+ def rename(m)
21
+ @bot.nick += "_"
22
+ end
23
+ end
24
+
25
+ bot = Cinch::Bot.new do
26
+ configure do |c|
27
+ c.nick = "ircinch_lambda"
28
+ c.server = "irc.libera.chat"
29
+ c.channels = ["#ircinch-bots"]
30
+ c.verbose = true
31
+ c.plugins.plugins = [DirectAddressing]
32
+ end
33
+ end
34
+
35
+ bot.start
@@ -0,0 +1,24 @@
1
+ require "ircinch"
2
+
3
+ class Nickchange
4
+ include Cinch::Plugin
5
+ listen_to :nick
6
+
7
+ def listen(m)
8
+ # This will send a PM to the user who changed their nick and inform
9
+ # them of their old nick.
10
+ m.reply "Your old nick was: #{m.user.last_nick}", true
11
+ end
12
+ end
13
+
14
+ bot = Cinch::Bot.new do
15
+ configure do |c|
16
+ c.nick = "ircinch_nickchange"
17
+ c.server = "irc.libera.chat"
18
+ c.channels = ["#ircinch-bots"]
19
+ c.verbose = true
20
+ c.plugins.plugins = [Nickchange]
21
+ end
22
+ end
23
+
24
+ bot.start
@@ -0,0 +1,21 @@
1
+ require "ircinch"
2
+
3
+ class Messenger
4
+ include Cinch::Plugin
5
+
6
+ match(/msg (.+?) (.+)/)
7
+ def execute(m, receiver, message)
8
+ User(receiver).send(message)
9
+ end
10
+ end
11
+
12
+ bot = Cinch::Bot.new do
13
+ configure do |c|
14
+ c.server = "irc.libera.chat"
15
+ c.nick = "IrCinchBot"
16
+ c.channels = ["#ircinch-bots"]
17
+ c.plugins.plugins = [Messenger]
18
+ end
19
+ end
20
+
21
+ bot.start
@@ -0,0 +1,32 @@
1
+ require "ircinch"
2
+
3
+ class MultiCommands
4
+ include Cinch::Plugin
5
+ match(/command1 (.+)/, method: :command1)
6
+ match(/command2 (.+)/, method: :command2)
7
+ match(/^command3 (.+)/, use_prefix: false)
8
+
9
+ def command1(m, arg)
10
+ m.reply "command1, arg: #{arg}"
11
+ end
12
+
13
+ def command2(m, arg)
14
+ m.reply "command2, arg: #{arg}"
15
+ end
16
+
17
+ def execute(m, arg)
18
+ m.reply "command3, arg: #{arg}"
19
+ end
20
+ end
21
+
22
+ bot = Cinch::Bot.new do
23
+ configure do |c|
24
+ c.nick = "ircinch_multi"
25
+ c.server = "irc.libera.chat"
26
+ c.channels = ["#ircinch-bots"]
27
+ c.verbose = true
28
+ c.plugins.plugins = [MultiCommands]
29
+ end
30
+ end
31
+
32
+ bot.start
@@ -0,0 +1,37 @@
1
+ require "ircinch"
2
+
3
+ class RandomNumberGenerator
4
+ def initialize(bot)
5
+ @bot = bot
6
+ end
7
+
8
+ def start
9
+ loop do
10
+ sleep 5 # pretend that we are waiting for some kind of entropy
11
+ @bot.handlers.dispatch(:random_number, nil, Kernel.rand)
12
+ end
13
+ end
14
+ end
15
+
16
+ class DoSomethingRandom
17
+ include Cinch::Plugin
18
+
19
+ listen_to :random_number
20
+ def listen(m, number)
21
+ Channel("#ircinch-bots").send "I got a random number: #{number}"
22
+ end
23
+ end
24
+
25
+ bot = Cinch::Bot.new do
26
+ configure do |c|
27
+ c.nick = "ircinch_events"
28
+ c.server = "irc.libera.chat"
29
+ c.channels = ["#ircinch-bots"]
30
+ c.verbose = true
31
+ c.plugins.plugins = [DoSomethingRandom]
32
+ end
33
+ end
34
+
35
+ Thread.new { RandomNumberGenerator.new(bot).start }
36
+
37
+ bot.start
@@ -0,0 +1,44 @@
1
+ require "ircinch"
2
+
3
+ class Seen
4
+ class SeenStruct < Struct.new(:who, :where, :what, :time)
5
+ def to_s
6
+ "[#{time.asctime}] #{who} was seen in #{where} saying #{what}"
7
+ end
8
+ end
9
+
10
+ include Cinch::Plugin
11
+ listen_to :channel
12
+ match(/seen (.+)/)
13
+
14
+ def initialize(*args)
15
+ super
16
+ @users = {}
17
+ end
18
+
19
+ def listen(m)
20
+ @users[m.user.nick] = SeenStruct.new(m.user, m.channel, m.message, Time.now)
21
+ end
22
+
23
+ def execute(m, nick)
24
+ if nick == @bot.nick
25
+ m.reply "That's me!"
26
+ elsif nick == m.user.nick
27
+ m.reply "That's you!"
28
+ elsif @users.key?(nick)
29
+ m.reply @users[nick].to_s
30
+ else
31
+ m.reply "I haven't seen #{nick}"
32
+ end
33
+ end
34
+ end
35
+
36
+ bot = Cinch::Bot.new do
37
+ configure do |c|
38
+ c.server = "irc.libera.chat"
39
+ c.channels = ["#ircinch-bots"]
40
+ c.plugins.plugins = [Seen]
41
+ end
42
+ end
43
+
44
+ bot.start
@@ -0,0 +1,22 @@
1
+ require "ircinch"
2
+
3
+ class TimedPlugin
4
+ include Cinch::Plugin
5
+
6
+ timer 5, method: :timed
7
+ def timed
8
+ Channel("#ircinch-bots").send "5 seconds have passed"
9
+ end
10
+ end
11
+
12
+ bot = Cinch::Bot.new do
13
+ configure do |c|
14
+ c.nick = "ircinch_timer"
15
+ c.server = "irc.libera.chat"
16
+ c.channels = ["#ircinch-bots"]
17
+ c.verbose = true
18
+ c.plugins.plugins = [TimedPlugin]
19
+ end
20
+ end
21
+
22
+ bot.start
@@ -0,0 +1,34 @@
1
+ require "cgi"
2
+ require "ircinch"
3
+ require "open-uri"
4
+
5
+ class TinyURL
6
+ include Cinch::Plugin
7
+
8
+ listen_to :channel
9
+
10
+ def shorten(url)
11
+ url = URI.parse("http://tinyurl.com/api-create.php?url=#{CGI.escape(url)}").open
12
+ (url == "Error") ? nil : url
13
+ rescue OpenURI::HTTPError
14
+ nil
15
+ end
16
+
17
+ def listen(m)
18
+ urls = URI.extract(m.message, "http")
19
+ short_urls = urls.map { |url| shorten(url) }.compact
20
+ unless short_urls.empty?
21
+ m.reply short_urls.join(", ")
22
+ end
23
+ end
24
+ end
25
+
26
+ bot = Cinch::Bot.new do
27
+ configure do |c|
28
+ c.server = "irc.libera.chat"
29
+ c.channels = ["#ircinch-bots"]
30
+ c.plugins.plugins = [TinyURL]
31
+ end
32
+ end
33
+
34
+ bot.start
data/ircinch.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/cinch/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "ircinch"
7
+ spec.version = Cinch::VERSION
8
+ spec.authors = ["Matt Sias"]
9
+ spec.email = ["siasmj@uwec.edu"]
10
+
11
+ spec.summary = "An IRC Bot Building Ruby Framework"
12
+ spec.description = "A simple, friendly Ruby DSL for creating IRC bots"
13
+ spec.homepage = "https://github.com/ircinchrb/ircinch"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.7.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/ircinchrb/ircinch"
19
+ spec.metadata["changelog_uri"] = "https://github.com/ircinchrb/ircinch/blob/main/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ # Register runtime dependencies (gems required to run this gem)
33
+ # spec.add_dependency "example-gem", "~> 1.0"
34
+
35
+ # Register development dependencies (gems required to program this gem)
36
+ spec.add_development_dependency "rake", "~> 13.0"
37
+ spec.add_development_dependency "minitest", "~> 5.16"
38
+ spec.add_development_dependency "simplecov", "~> 0.21.2"
39
+ spec.add_development_dependency "standard", "~> 1.18"
40
+
41
+ # For more information and examples about making a new gem, check out our
42
+ # guide at: https://bundler.io/guides/creating_gem.html
43
+ end
data/lib/cinch/ban.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "mask"
4
+
5
+ module Cinch
6
+ # This class represents channel bans.
7
+ class Ban
8
+ # @return [Mask] A {Mask} object for non-extended bans
9
+ # @return [String] A String object for extended bans (see {#extended})
10
+ attr_reader :mask
11
+
12
+ # The user who created the ban. Might be nil on networks that do
13
+ # not strictly follow the RFCs, for example IRCnet in some(?)
14
+ # cases.
15
+ #
16
+ # @return [User, nil] The user who created the ban
17
+ attr_reader :by
18
+
19
+ # @return [Time]
20
+ attr_reader :created_at
21
+
22
+ # @return [Boolean] whether this is an extended ban (as used by for example Freenode)
23
+ attr_reader :extended
24
+
25
+ # @param [String, Mask] mask The mask
26
+ # @param [User, nil] by The user who created the ban.
27
+ # @param [Time] at The time at which the ban was created
28
+ def initialize(mask, by, at)
29
+ @by, @created_at = by, at
30
+ if mask.match?(/^[$~]/)
31
+ @extended = true
32
+ @mask = mask
33
+ else
34
+ @extended = false
35
+ @mask = Mask.from(mask)
36
+ end
37
+ end
38
+
39
+ # @return [Boolean] true if the ban matches `user`
40
+ # @raise [Exceptions::UnsupportedFeature] Cinch does not support
41
+ # Freenode's extended bans
42
+ def match(user)
43
+ raise UnsupportedFeature, "extended bans are not supported yet" if @extended
44
+ @mask =~ user
45
+ end
46
+ alias_method :=~, :match
47
+
48
+ # @return [String]
49
+ def to_s
50
+ @mask.to_s
51
+ end
52
+ end
53
+ end