nakiircbot 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []