nakiircbot 0.0.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 (5) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/lib/nakiircbot.rb +135 -0
  4. data/nakiircbot.gemspec +14 -0
  5. metadata +47 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3fd605975fa07fb8ad61f317912b32c6d9525a46
4
+ data.tar.gz: a6a9a41301909099d2e71d57101232b22c1bbbf1
5
+ SHA512:
6
+ metadata.gz: 46f06a8b677e230df7543238d94cf9321bae53e7dcf9144e74bea95aa030e947e90cc6361672fa7bab9672118f7643b1488c923b39a3ff0425b640ace7e81b06
7
+ data.tar.gz: 0fdd39f22e6bb874c380a368638a463a0a013019888883b65e5d059829234653e49c15e2481c24036a3344563c72e01012f9825cc23d08ff52b220bd5099e0eb
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Victor Maslov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/lib/nakiircbot.rb ADDED
@@ -0,0 +1,135 @@
1
+ module NakiIRCBot
2
+ # @@channels = []
3
+ # class << self
4
+ # attr_accessor :channels
5
+ # end
6
+ def self.start server, port, bot_name, master_name, welcome001, *channels, password: nil, masterword: nil, processors: [], twitch: false
7
+ # @@channels.replace channels.dup
8
+
9
+ abort "matching bot_name and master_name may cause infinite recursion" if bot_name == master_name
10
+ require "base64"
11
+ require "fileutils"
12
+ FileUtils.mkdir_p "logs"
13
+ require "logger"
14
+ original_formatter = Logger::Formatter.new
15
+ logger = Logger.new "logs/txt", "daily",
16
+ progname: bot_name, datetime_format: "%y%m%d %H%M%S",
17
+ formatter: lambda{ |severity, datetime, progname, msg|
18
+ puts "#{datetime.strftime "%H%M%S"} #{severity.to_s[0]} #{progname} #{msg.scrub.inspect[1..-2]}"
19
+ original_formatter.call severity, datetime, progname, Base64.strict_encode64(msg)
20
+ # TODO: maybe encode the whole string for a case of invalid progname?
21
+ }
22
+ logger.level = ENV["LOGLEVEL_#{name}"].to_sym if ENV.include? "LOGLEVEL_#{name}"
23
+ puts "#{name} logger.level = #{logger.level}"
24
+
25
+ # https://en.wikipedia.org/wiki/List_of_Internet_Relay_Chat_commands
26
+ loop do
27
+ logger.info "reconnect"
28
+ require "socket"
29
+ socket = TCPSocket.new server, port
30
+
31
+ # https://stackoverflow.com/a/49476047/322020
32
+ socket_send = lambda do |str|
33
+ logger.info "> #{str}"
34
+ socket.send str + "\n", 0
35
+ end
36
+ socket_send.call "PASS #{password.strip}" if twitch
37
+ socket_send.call "NICK #{bot_name}"
38
+ socket_send.call "USER #{bot_name} #{bot_name} #{bot_name} #{bot_name}" unless twitch
39
+
40
+ queue = []
41
+ prev_socket_time = prev_privmsg_time = Time.now
42
+ loop do
43
+ begin
44
+ addr, msg = queue.shift
45
+ next unless addr && msg
46
+ fail "I should not PRIVMSG myself" if addr == bot_name
47
+ msg.scrub!
48
+ msg.gsub! "\n", " "
49
+ msg.gsub! "\r", " "
50
+ privmsg = "PRIVMSG #{addr} :#{msg}"
51
+ privmsg = "PRIVMSG #{addr} :*flood*" if privmsg.bytes.size > 500 && addr.start_with?("#")
52
+ prev_socket_time = prev_privmsg_time = Time.now
53
+ socket_send.call privmsg
54
+ break
55
+ end until queue.empty? if prev_privmsg_time + 5 < Time.now
56
+
57
+ unless _ = Kernel::select([socket], nil, nil, 1)
58
+ break if Time.now - prev_socket_time > 300
59
+ next
60
+ end
61
+ prev_socket_time = Time.now
62
+ socket_str = _[0][0].gets(chomp: true)
63
+ break unless socket_str
64
+ str = socket_str.force_encoding("utf-8").scrub
65
+ if /\A:\S+ 372 /.match? str # MOTD
66
+ logger.debug "< #{str}"
67
+ elsif /\APING :/.match? str
68
+ logger.debug "< #{str}"
69
+ else
70
+ logger.info "< #{str}"
71
+ end
72
+ break if /\AERROR :Closing Link: /.match? str
73
+
74
+ # if str[/^:\S+ 433 * #{Regexp.escape bot_name} :Nickname is already in use\.$/]
75
+ # socket_send.call "NICK #{bot_name + "_"}"
76
+ # next
77
+ # end
78
+
79
+ # next socket.send("JOIN #{$2}"+"\n"),0 if str[/^:(.+?)!\S+ KICK (\S+) #{Regexp.escape bot_name} /i]
80
+ case str
81
+ when /\A:[a-z.]+ 001 #{Regexp.escape bot_name} :Welcome to the #{Regexp.escape welcome001} #{Regexp.escape bot_name}\z/
82
+ # we join only when we are sure we are on the correct server
83
+ # TODO: maybe abort if the server is wrong?
84
+ next socket_send.call "JOIN #{channels.join ","}"
85
+ when /\A:tmi.twitch.tv 001 #{Regexp.escape bot_name} :Welcome, GLHF!\z/
86
+ socket_send.call "JOIN #{channels.join ","}"
87
+ socket_send.call "CAP REQ :twitch.tv/membership twitch.tv/tags twitch.tv/commands"
88
+ next
89
+ when /\A:NickServ!NickServ@services\. NOTICE #{Regexp.escape bot_name} :This nickname is registered. Please choose a different nickname, or identify via \x02\/msg NickServ identify <password>\x02\.\z/
90
+ abort "no password" unless password
91
+ logger.info "password"
92
+ # next socket.send "PASS #{password.strip}\n", 0
93
+ next socket.send "PRIVMSG NickServ :identify #{bot_name} #{password.strip}\n", 0
94
+ when /\APING :/
95
+ next socket.send "PONG :#{$'}\n", 0 # Quakenet uses timestamp, Freenode and Twitch use server name
96
+ when /\A:([^!]+)!\S+ PRIVMSG #{Regexp.escape bot_name} :\x01VERSION\x01\z/
97
+ next socket_send.call "NOTICE #{$1} :\x01VERSION name 0.0.0\x01"
98
+ # when /^:([^!]+)!\S+ PRIVMSG #{Regexp.escape bot_name} :\001PING (\d+)\001$/
99
+ # socket_send.call "NOTICE",$1,"\001PING #{rand 10000000000}\001"
100
+ # when /^:([^!]+)!\S+ PRIVMSG #{Regexp.escape bot_name} :\001TIME\001$/
101
+ # socket_send.call "NOTICE",$1,"\001TIME 6:06:06, 6 Jun 06\001"
102
+ when /\A#{'\S+ ' if twitch}:(?<who>[^!]+)!\S+ PRIVMSG (?<where>\S+) :(?<what>.+)/
103
+ next( if processors.empty?
104
+ queue.push [master_name, "nothing to reload"]
105
+ else
106
+ processors.each do |processor|
107
+ queue.push [master_name, "reloading #{processor}"]
108
+ load File.absolute_path processor
109
+ end
110
+ end ) if $~.named_captures == {"who"=>master_name, "where"=>bot_name, "what"=>"#{twitch ? "@#{bot_name} " : "#{masterword.strip} "}reload"}
111
+ end
112
+
113
+ begin
114
+ yield str, ->(where, what){ queue.push [where, what] }
115
+ rescue => e
116
+ puts e.full_message
117
+ queue.push [master_name, "yield error: #{e}"]
118
+ end
119
+
120
+ rescue => e
121
+ puts e.full_message
122
+ case e
123
+ when Errno::ECONNRESET, Errno::ECONNABORTED, Errno::ETIMEDOUT, Errno::EPIPE
124
+ sleep 5
125
+ break
126
+ else
127
+ queue.push [master_name, "unhandled error: #{e}"]
128
+ sleep 5
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+ end
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "nakiircbot"
3
+ spec.version = "0.0.0"
4
+ spec.summary = "IRC bot framework"
5
+
6
+ spec.author = "Victor Maslov aka Nakilon"
7
+ spec.email = "nakilon@gmail.com"
8
+ spec.license = "MIT"
9
+ spec.metadata = {"source_code_uri" => "https://github.com/nakilon/nakiircbot"}
10
+
11
+ spec.required_ruby_version = ">=2.5" # for Exception#full_message and block rescue
12
+
13
+ spec.files = %w{ LICENSE nakiircbot.gemspec lib/nakiircbot.rb }
14
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nakiircbot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Victor Maslov aka Nakilon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-05-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: nakilon@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - LICENSE
20
+ - lib/nakiircbot.rb
21
+ - nakiircbot.gemspec
22
+ homepage:
23
+ licenses:
24
+ - MIT
25
+ metadata:
26
+ source_code_uri: https://github.com/nakilon/nakiircbot
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '2.5'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.5.2.3
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: IRC bot framework
47
+ test_files: []