rirc 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rirc.rb +827 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0439855d19cb4f0b49ab4746b19d30dc9a4dea02
4
+ data.tar.gz: 7b83de5afb207da4e02a5c0ec7b87326cd2c769b
5
+ SHA512:
6
+ metadata.gz: 99bd8b6632cbc7cab8cd144f312e8803cf8efb3ef597b58d58f4392113ef42134539619c86e6e4e277f206b33a64908eb7a39e71f52c823452fb5fcd782b4685
7
+ data.tar.gz: a7080e0d540d0feee76ccdf2ec2b91d0c8a98e5ee43717e6a1a18945fe3bc0f6cb552c8d6f6a32b52e9523af33e7fdf6f5f96b97518474c96d4261047d644010
data/lib/rirc.rb ADDED
@@ -0,0 +1,827 @@
1
+ #! /bin/env ruby
2
+ #
3
+ ############################################################
4
+ # Author: Alice "Duchess" Archer
5
+ # Copyright (c) 2015 under the MIT License
6
+ # see COPYING.md for full copyright
7
+ # Name: rirc
8
+ # Description: IRC framework for IRC bots written in ruby
9
+ ############################################################
10
+
11
+ require 'socket'
12
+ require 'openssl'
13
+
14
+ class IRC_message
15
+ def initialize(command, nick, channel, message, ircmsg)
16
+ @command = command
17
+ @nick = nick
18
+ @channel = channel
19
+ @message = message
20
+ @ircmsg = ircmsg
21
+ end
22
+
23
+ def ircmsg
24
+ return @ircmsg
25
+ end
26
+
27
+ def message
28
+ return @message
29
+ end
30
+
31
+ def nick
32
+ return @nick
33
+ end
34
+
35
+ def command
36
+ return @command
37
+ end
38
+
39
+ def channel
40
+ return @channel
41
+ end
42
+
43
+ def check_regex(type, regex)
44
+
45
+ if type == "command"
46
+ if @command.match(regex) then return true end
47
+ elsif type == "nick"
48
+ if @nick.match(regex) then return true end
49
+ elsif type == "channel"
50
+ if @channel.match(regex) then return true end
51
+ elsif type == "message"
52
+ if @message.match(regex) then return true end
53
+ else
54
+ if @message.match(regex) then return true end
55
+ end
56
+
57
+ return false
58
+ end
59
+
60
+ def message_regex(regex)
61
+ if @message.match(regex) then return true end
62
+
63
+ return false
64
+ end
65
+ end
66
+
67
+ class Pluginf
68
+
69
+ def initialize(regex, name, file_name, help)
70
+ @regexp = Regexp.new(regex.to_s)
71
+ @name = name.to_s
72
+ @file_name = file_name.to_s
73
+ @help = help
74
+ @chan_list = []
75
+ @chan_list.push("any")
76
+ end
77
+
78
+ def initialize(regex, name, file_name, help, chan_list)
79
+ @regexp = Regexp.new(regex.to_s)
80
+ @name = name.to_s
81
+ @file_name = file_name.to_s
82
+ @help = help
83
+ @chan_list = chan_list.map(&:to_s)
84
+ end
85
+
86
+ # default function
87
+ def script(message, nick, chan)
88
+
89
+ end
90
+
91
+ def regex
92
+ return @regexp
93
+ end
94
+
95
+ def chans
96
+ return @chan_list
97
+ end
98
+
99
+ def name
100
+ return @name
101
+ end
102
+
103
+ def file_name
104
+ return @file_name
105
+ end
106
+
107
+ def help
108
+ return @help
109
+ end
110
+
111
+ def cleanup
112
+ return ""
113
+ end
114
+
115
+ def rm_chan(channel)
116
+ @chan_list.delete_if { |a| channel == a }
117
+ end
118
+
119
+ def add_chan(channel)
120
+ if !@chan_list.include? channel then @chan_list.push(channel) end
121
+ end
122
+ end
123
+
124
+ class Plugin_manager
125
+ def initialize(plugin_folder)
126
+ @plugins = []
127
+ @plugin_folder = plugin_folder
128
+ @err_path = "./.errlog"
129
+ end
130
+ =begin
131
+ def initialize(plugin_folder, err_path)
132
+ @plugins = []
133
+ @plugin_folder = plugin_folder
134
+ @err_path = err_path.to_s
135
+ end
136
+ =end
137
+ # returns all the plugins
138
+ def plugins
139
+
140
+ if @plugins.length == 0
141
+ return []
142
+ end
143
+
144
+ return @plugins
145
+ end
146
+
147
+ # search functions
148
+ def get_names
149
+
150
+ if @plugins.length == 0
151
+ return []
152
+ end
153
+
154
+ names = []
155
+
156
+ @plugins.each { |a| names.push(a.name) }
157
+
158
+ return names
159
+ end
160
+
161
+ def get_helps
162
+
163
+ if @plugins.length == 0
164
+ return []
165
+ end
166
+
167
+ names = []
168
+
169
+ @plugins.each { |a| names.push(a.help) }
170
+
171
+ return names
172
+ end
173
+
174
+ def get_files
175
+
176
+ if @plugins.length == 0
177
+ return []
178
+ end
179
+
180
+ names = []
181
+
182
+ @plugins.each { |a| names.push(a.file_name) }
183
+
184
+ return names
185
+ end
186
+
187
+ def get_chans
188
+
189
+ if @plugins.length == 0
190
+ return []
191
+ end
192
+
193
+ names = []
194
+
195
+ @plugins.each { |a| names.push(a.chans) }
196
+
197
+ return names
198
+ end
199
+
200
+ def get_chans(name)
201
+
202
+ if !plugin_loaded(name)
203
+ return nil
204
+ end
205
+
206
+ return get_plugin(name).chans
207
+ end
208
+
209
+ def add_chan(plugin_name, channel)
210
+ if !plugin_loaded(name)
211
+ return "#{plugin_name} is not loaded"
212
+ end
213
+
214
+ get_plugin(name).add_chan(channel)
215
+
216
+ return "#{channel} added to #{plugin_name}"
217
+ end
218
+
219
+ def add_chan_clear_any(plugin_name, channel)
220
+ if !plugin_loaded(name)
221
+ return "#{plugin_name} is not loaded"
222
+ end
223
+
224
+ rm_chan(plugin_name, "any")
225
+ add_chan(plugin_name, channel)
226
+
227
+ return "#{channel} added to #{plugin_name}"
228
+ end
229
+
230
+ def rm_chan(plugin_name, channel)
231
+ if !plugin_loaded(name)
232
+ return "#{plugin_name} is not loaded"
233
+ end
234
+
235
+ get_plugin(name).rm_chan(channel)
236
+
237
+ return "#{channel} removed from #{plugin_name}"
238
+ end
239
+
240
+ def get_regexps
241
+
242
+ if @plugins.length == 0
243
+ return []
244
+ end
245
+
246
+ names = []
247
+
248
+ @plugins.each { |a| names.push(a.regex) }
249
+
250
+ return names
251
+ end
252
+
253
+ def get_plugin(name) # gets a plugin by name or nil if it is not loaded
254
+
255
+ if @plugins.length == 0
256
+ return nil
257
+ end
258
+
259
+ @plugins.each { |a| if a.name == name then return a end }
260
+
261
+ return nil
262
+ end
263
+
264
+ def plugin_help(name) # gets the help for a plugin
265
+
266
+ if @plugins.length == 0
267
+ return nil
268
+ end
269
+
270
+ @plugins.each { |a| if a.name == name then return a.help end }
271
+
272
+ return nil
273
+ end
274
+
275
+ def plugin_file_name(name) # gets the file name for a plugin
276
+
277
+ if @plugins.length == 0
278
+ return nil
279
+ end
280
+
281
+ @plugins.each { |a| if a.name == name then return a.file_name end }
282
+
283
+ return nil
284
+ end
285
+
286
+ def plugin_chans(name) # gets the array of channels for a plugin
287
+
288
+ if @plugins.length == 0
289
+ return nil
290
+ end
291
+
292
+ @plugins.each { |a| if a.name == name then return a.chans end }
293
+
294
+ return nil
295
+ end
296
+
297
+ def plugin_regex(name) # gets the regex for a plugin
298
+
299
+ if @plugins.length == 0
300
+ return nil
301
+ end
302
+
303
+ @plugins.each { |a| if a.name == name then return a.regex end }
304
+
305
+ return nil
306
+ end
307
+
308
+ # check if a plugin is loaded
309
+ def plugin_loaded(name)
310
+
311
+ if @plugins.length == 0
312
+ return false
313
+ end
314
+
315
+ @plugins.each do |a|
316
+ if a.name == name
317
+ return true
318
+ end
319
+ end
320
+
321
+ return false
322
+ end
323
+
324
+ # regex check function
325
+ # this function uses the IRC_message object for message input
326
+ # inputs:
327
+ # - name
328
+ # - IRC_message object
329
+ # - array of admins [can be an empty array]
330
+ # - backlog array [can be an empty array]
331
+ # output: string
332
+ def check_plugin(name, message, admins, backlog) #checks an individual plugin's (by name) regex against message
333
+
334
+ if @plugins.length == 0
335
+ return ""
336
+ end
337
+
338
+ if !plugin_loaded(name)
339
+ return ""
340
+ else
341
+ if message.message.match(get_plugin(name).regex) and (get_plugin(name).chans.include? "any" or get_plugin(name).chans.include? message.channel)
342
+ begin
343
+ return get_plugin(name).script(message, admins, backlog) # plugins use the IRC_message object
344
+ rescue => e
345
+ if File.exist?(@err_path)
346
+ File.write(@err_path, "PLUGIN #{name} FAILED TO RUN", File.size(@err_path), mode: 'a')
347
+ File.write(@err_path, "======================================================", File.size(@err_path), mode: 'a')
348
+ File.write(@err_path, e.to_s, File.size(@err_path), mode: 'a')
349
+ File.write(@err_path, e.backtrace.to_s, File.size(@err_path), mode: 'a')
350
+ File.write(@err_path, "======================================================", File.size(@err_path), mode: 'a')
351
+ end
352
+ return "an error occured for plugin: #{name}"
353
+ end
354
+ end
355
+ end
356
+
357
+ return ""
358
+ end
359
+
360
+ # regex check function that returns responses for all plugins in an array
361
+ # inputs:
362
+ # - IRC_message object
363
+ # - array of admins [can be an empty array]
364
+ # - backlog array [can be an empty array]
365
+ # output: array of strings
366
+ def check_all(message, admins, backlog)
367
+
368
+ if @plugins.length == 0
369
+ return []
370
+ end
371
+
372
+ response = []
373
+
374
+ # this is incredibly inneficient but it makes check_plugin flexible
375
+ @plugins.each { |a| response.push(check_plugin(a.name, message, admins, backlog)) }
376
+
377
+ return response
378
+ end
379
+
380
+ # load
381
+ def plugin_load(name)
382
+
383
+ $LOAD_PATH << "#{@plugin_folder}"
384
+ response = ""
385
+ temp_plugin = nil
386
+ if name.match(/.rb$/)
387
+ begin
388
+ load "#{name}"
389
+ plugin_loader = Loader.new
390
+ temp_plugin = plugin_loader.get_plugin
391
+ if plugin_loaded(temp_plugin.name)
392
+ temp_plugin = nil
393
+ return "Plugin #{name} is already loaded"
394
+ end
395
+ @plugins.push(temp_plugin)
396
+ temp_plugin = nil
397
+ response = "#{name[0..-4]} loaded"
398
+ rescue => e
399
+ response = "cannot load plugin"
400
+ if File.exist?(@err_path)
401
+ File.write(@err_path, "PLUGIN #{name} FAILED TO LOAD", File.size(@err_path), mode: 'a')
402
+ File.write(@err_path, "======================================================", File.size(@err_path), mode: 'a')
403
+ File.write(@err_path, e.to_s, File.size(@err_path), mode: 'a')
404
+ File.write(@err_path, e.backtrace.to_s, File.size(@err_path), mode: 'a')
405
+ File.write(@err_path, "======================================================", File.size(@err_path), mode: 'a')
406
+ end
407
+ end
408
+ else
409
+ begin
410
+ load "#{name}.rb"
411
+ plugin_loader = Loader.new
412
+ temp_plugin = plugin_loader.get_plugin
413
+ if plugin_loaded(temp_plugin.name)
414
+ temp_plugin = nil
415
+ return "Plugin #{name} is already loaded"
416
+ end
417
+ @plugins.push(temp_plugin)
418
+ temp_plugin = nil
419
+ response = "#{name} loaded"
420
+ rescue => e
421
+ response = "cannot load plugin"
422
+ if File.exist?(@err_path)
423
+ File.write(@err_path, "PLUGIN #{name} FAILED TO LOAD", File.size(@err_path), mode: 'a')
424
+ File.write(@err_path, "======================================================", File.size(@err_path), mode: 'a')
425
+ File.write(@err_path, e.to_s, File.size(@err_path), mode: 'a')
426
+ File.write(@err_path, e.backtrace.to_s, File.size(@err_path), mode: 'a')
427
+ File.write(@err_path, "======================================================", File.size(@err_path), mode: 'a')
428
+ end
429
+ end
430
+ end
431
+ $LOAD_PATH << './'
432
+ return response
433
+ end
434
+
435
+ # unload
436
+ def unload(name)
437
+
438
+ if !plugin_loaded(name)
439
+ return "plugin is not loaded"
440
+ end
441
+
442
+ get_plugin(name).cleanup
443
+ @plugins.delete_if { |a| a.name == name }
444
+
445
+ return "plugin #{name} unloaded"
446
+ end
447
+
448
+ # reload
449
+ def reload(name)
450
+
451
+ if !plugin_loaded(name)
452
+ return "plugin is not loaded"
453
+ end
454
+
455
+ temp_file_name = get_plugin(name).file_name
456
+
457
+ unload(name)
458
+ plugin_load(temp_file_name)
459
+
460
+ return "plugin #{name} reloaded"
461
+ end
462
+ end
463
+
464
+ class IRCBot
465
+ def initialize(network, port, nick, user_name, real_name)
466
+ @network = network
467
+ @port = port
468
+ @nick = nick
469
+ @user_name = user_name
470
+ @real_name = real_name
471
+ @socket = nil
472
+ @channels = []
473
+ @admins = []
474
+ @ignore = []
475
+ @hooks = {}
476
+ @backlog = []
477
+ @log_path = "./.log"
478
+ @err_path = "./.errlog"
479
+ end
480
+ =begin
481
+ def initialize(network, port, nick, user_name, real_name, log_path, err_path)
482
+ @network = network
483
+ @port = port
484
+ @nick = nick
485
+ @user_name = user_name
486
+ @real_name = real_name
487
+ @socket = nil
488
+ @channels = []
489
+ @admins = []
490
+ @log_path = log_path
491
+ @err_path = err_path
492
+ @ignore = []
493
+ @hooks = {}
494
+ @backlog = []
495
+ end
496
+ =end
497
+ def backlog
498
+ return @backlog
499
+ end
500
+
501
+ def ignore
502
+ return @ignore
503
+ end
504
+
505
+ def channels
506
+ return @channels
507
+ end
508
+
509
+ def admins
510
+ return @admins
511
+ end
512
+
513
+ def network
514
+
515
+ return @network
516
+ end
517
+
518
+ def port
519
+
520
+ return @port
521
+ end
522
+
523
+ def nick_name
524
+
525
+ return @nick
526
+ end
527
+
528
+ def user_name
529
+
530
+ return @user_name
531
+ end
532
+
533
+ def real_name
534
+
535
+ return @real_name
536
+ end
537
+
538
+ def socket
539
+
540
+ return @socket
541
+ end
542
+
543
+ def say(message)
544
+
545
+ @socket.puts message
546
+ end
547
+
548
+ def join(channel)
549
+
550
+ say "JOIN #{channel}"
551
+ if !@channels.include? channel then @channels.push(channel) end
552
+ end
553
+
554
+ def connect
555
+
556
+ @socket = TCPSocket.open(@network, @port)
557
+ end
558
+
559
+ def connect_ssl
560
+ ssl_context = OpenSSL::SSL::SSLContext.new
561
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
562
+ @socket = OpenSSL::SSL::SSLSocket.new(@socket, ssl_context)
563
+ @socket.sync = true
564
+ @socket.connect
565
+ end
566
+
567
+ def connect_pass(pass)
568
+
569
+ say "PASS #{pass}"
570
+ end
571
+
572
+ def nick(nick)
573
+
574
+ @nick = nick
575
+ say "NICK #{nick}"
576
+ end
577
+
578
+ def privmsg(dest, message)
579
+
580
+ say "PRIVMSG #{dest} :#{message}"
581
+ end
582
+
583
+ def action(dest, message)
584
+
585
+ privmsg(dest, "\01ACTION #{message}\07\01")
586
+ end
587
+
588
+ def notice(dest, message)
589
+
590
+ say "NOTICE #{dest} :#{message}"
591
+ end
592
+
593
+ def ctcp(dest, message)
594
+
595
+ privmsg(dest, "\01VERSION #{message}\07\01")
596
+ end
597
+
598
+ def part(dest, message)
599
+
600
+ @channels.delete_if { |a| dest == a }
601
+ say "PART #{dest} :#{message}"
602
+ end
603
+
604
+ def rm_chan(channel)
605
+ @channels.delete_if { |a| channel == a }
606
+ end
607
+
608
+ def add_chan(channel)
609
+ if !@channels.include? channel then @channels.push(channel) end
610
+ end
611
+
612
+ def quit(message)
613
+
614
+ say "QUIT :#{message}"
615
+ end
616
+
617
+ def names(dest)
618
+
619
+ say "NAMES #{dest}"
620
+ end
621
+
622
+ def identify(nickserv_pass)
623
+ say "PRIVMSG nickserv :identify #{nickserv_pass}"
624
+ end
625
+
626
+ def auth(nickserv_pass)
627
+ say "VERSION"
628
+ say "USER #{@user_name} * * :#{@real_name}"
629
+ nick(@nick)
630
+ if nickserv_pass != "" and nickserv_pass != nil
631
+ identify(nickserv_pass)
632
+ end
633
+ end
634
+
635
+ def read
636
+ if !@socket.eof
637
+
638
+ msg = @socket.gets
639
+
640
+ if msg.match(/^PING :(.*)$/)
641
+ say "PONG #{$~[1]}"
642
+ return "PING"
643
+ end
644
+
645
+ return msg
646
+ else
647
+ return nil
648
+ end
649
+ end
650
+
651
+ def parse(msg)
652
+ message_reg = msg.match(/^(:(?<prefix>\S+) )?(?<command>\S+)( (?!:)(?<params>.+?))?( :(?<trail>.+))?$/)
653
+ nick_n = message_reg[:prefix].to_s.split("!")[0]
654
+ command = message_reg[:command].to_s
655
+ chan = message_reg[:params].to_s
656
+ message = message_reg[:trail].to_s
657
+
658
+ message = message.chomp
659
+
660
+ if chan == @nick then chan = nick_n end
661
+
662
+ ircmsg = IRC_message.new(command, nick_n, chan, message, msg)
663
+
664
+ return ircmsg
665
+ end
666
+
667
+ def add_admin(nick)
668
+ if !@admins.include? nick then @admins.push(nick) end
669
+ end
670
+
671
+ def remove_admin(nick)
672
+ @admins.delete_if { |a| a == nick }
673
+ end
674
+
675
+ def add_ignore(nick)
676
+ @ignore.push(nick)
677
+ end
678
+
679
+ def remove_ignore(nick)
680
+ @ignore.delete_if { |a| a == nick }
681
+ end
682
+
683
+ def on(type, &block)
684
+ type = type.to_s
685
+ @hooks[type] ||= []
686
+ @hooks[type] << block
687
+ end
688
+
689
+ def set_admins(admins_s)
690
+ admins_s.each { |a| self.add_admin(a) }
691
+ end
692
+
693
+ def join_channels(channels_s)
694
+ channels_s.each { |a| self.join(a) }
695
+ end
696
+
697
+ def create_log
698
+ if !File.exist?(@log_path)
699
+ File.open(@log_path, "w+") { |fw| fw.write("Command and Privmsg LOGS") }
700
+ end
701
+
702
+ if !File.exist?(@err_path)
703
+ File.open(@err_path, "w+") { |fw| fw.write("Error LOGS") }
704
+ end
705
+ end
706
+
707
+ def setup(use_ssl, use_pass, pass, nickserv_pass, channels_s)
708
+ self.connect
709
+ if use_ssl then self.connect_ssl end
710
+ if use_pass then self.connect_pass(pass) end
711
+ self.auth(nickserv_pass)
712
+
713
+ self.create_log
714
+
715
+ self.join_channels(channels_s)
716
+
717
+ self.on :message do |msg|
718
+ if msg.nick == msg.channel
719
+ File.write(@log_path, msg.ircmsg, File.size(@log_path), mode: 'a')
720
+ end
721
+
722
+ if !self.nick_name == msg.nick and !self.ignore.include? msg.nick
723
+ @backlog.push(msg)
724
+ end
725
+ end
726
+ end
727
+
728
+ def start!
729
+
730
+ until self.socket.eof? do
731
+ ircmsg = self.read
732
+ msg = self.parse(ircmsg)
733
+
734
+ if ircmsg == "PING" or self.ignore.include?(msg.nick) then next end
735
+
736
+ begin
737
+ hooks = @hooks['command']
738
+ if hooks != nil
739
+ hooks.each { |h| h.call(msg.channel, msg.command) }
740
+ end
741
+ rescue => e
742
+ # do not do anything
743
+ end
744
+
745
+ begin
746
+ hooks = @hooks['message']
747
+ if hooks != nil
748
+ hooks.each { |h| h.call(msg) }
749
+ end
750
+ rescue => e
751
+ # do not do anything
752
+ end
753
+
754
+ begin
755
+ hooks = @hooks['ircmsg']
756
+ if hooks != nil
757
+ hooks.each { |h| h.call(msg.nick, msg.command, msg.channel, msg.message) }
758
+ end
759
+ rescue => e
760
+ # do not do anything
761
+ end
762
+ end
763
+ end
764
+ end
765
+
766
+ class Commands_manager
767
+
768
+ def initialize
769
+ @reg_s = []
770
+ @hook_s = []
771
+ @size = 0
772
+ @log_path = "./.log"
773
+ @err_path = "./.errlog"
774
+ end
775
+
776
+ =begin
777
+ def initialize(log_path, err_path)
778
+ @reg_s = []
779
+ @hook_s = []
780
+ @size = 0
781
+ @log_path = log_path
782
+ @err_path = err_path
783
+ end
784
+ =end
785
+ def on(reg, &block)
786
+ reg = Regexp.new(reg.to_s)
787
+ @reg_s.push(reg)
788
+ @hook_s << block
789
+ @size += 1
790
+ end
791
+
792
+ def check_cmds(ircbot, msg, pluginmgr)
793
+
794
+ if @size == 0
795
+ return
796
+ end
797
+
798
+ 0.upto(@size - 1) do |i|
799
+ if msg.message_regex(@reg_s[i])
800
+ File.write(@log_path, msg, File.size(@log_path), mode: 'a')
801
+ begin
802
+ @hook_s[i].call(ircbot, msg, pluginmgr)
803
+ rescue => e
804
+ if File.exist?(@err_path)
805
+ File.write(@err_path, "COMMAND FAILED TO EXECUTE", File.size(@err_path), mode: 'a')
806
+ File.write(@err_path, "======================================================", File.size(@err_path), mode: 'a')
807
+ File.write(@err_path, e.to_s, File.size(@err_path), mode: 'a')
808
+ File.write(@err_path, e.backtrace.to_s, File.size(@err_path), mode: 'a')
809
+ File.write(@err_path, "======================================================", File.size(@err_path), mode: 'a')
810
+ end
811
+ end
812
+ end
813
+ end
814
+ end
815
+
816
+ def hooks
817
+ return @hook_s
818
+ end
819
+
820
+ def regs
821
+ return @reg_s
822
+ end
823
+
824
+ def size
825
+ return @size
826
+ end
827
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rirc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.3
5
+ platform: ruby
6
+ authors:
7
+ - Alice "Duchess" Archer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-25 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: IRC Framework for Bots
14
+ email: ''
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/rirc.rb
20
+ homepage: https://github.com/The-Duchess/ruby-irc-framework
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.2.3
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Ruby IRC Framework
44
+ test_files: []