Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added ISUPPORT

  • Loading branch information...
commit 8313cc9a069c0d2259f1cafe2f0cdba7c352db5b 1 parent 4618ea9
Tobias Bühlmann authored
View
175 lib/ponder/isupport.rb
@@ -0,0 +1,175 @@
+module Ponder
+ class ISupport < Hash
+ def initialize
+ super
+
+ # Defaults:
+ self['CASEMAPPING'] = 'rfc1459'
+ self['CHANLIMIT'] = {}
+ self['CHANMODES'] = {
+ 'A' => ['b'],
+ 'B' => ['k'],
+ 'C' => ['l'],
+ 'D' => %w(i m n p s t r)
+ }
+ self['CHANNELLEN'] = 200
+ self['CHANTYPES'] = ['#', '&']
+ self['EXCEPTS'] = false
+ self['IDCHAN'] = {}
+ self['INVEX'] = false
+ self['KICKLEN'] = Float::INFINITY
+ self['MAXLIST'] = {}
+ self['MODES'] = 3
+ self['NETWORK'] = ''
+ self['NICKLEN'] = Float::INFINITY
+ self['PREFIX'] = {'o' => '@', 'v' => '+'}
+ self['SAFELIST'] = false
+ self['STATUSMSG'] = false
+ self['STD'] = false
+ self['TARGMAX'] = {}
+ self['TOPICLEN'] = Float::INFINITY
+ end
+
+ def parse(message)
+ patterns = message.split(' ')
+ patterns.delete_at(0)
+ patterns.each do |pattern|
+ return self if pattern.start_with?(':')
+ key = pattern.scan(/\w+/).first
+ method_name = "set_#{key.downcase}"
+ begin
+ if respond_to?(method_name, true)
+ send method_name, pattern
+ else
+ set_different key, pattern
+ end
+ rescue
+ end
+ end
+ end
+
+ private
+
+ def set_casemapping(pattern)
+ self['CASEMAPPING'] = pattern.split('=')[1]
+ end
+
+ def set_chanlimit(pattern)
+ value = pattern.split('=')[1]
+ value.split(',').each do |prefixes_and_limit|
+ prefixes, limit = prefixes_and_limit.split(':')
+ limit = limit.nil? ? Float::INFINITY : limit.to_i
+ prefixes.split('').each do |prefix|
+ self['CHANLIMIT'][prefix] = limit
+ end
+ end
+ end
+
+ def set_chanmodes(pattern)
+ value = pattern.split('=')[1]
+ modes_per_type = value.split(',').map { |modes| modes.split('') }
+ ('A'..'D').each_with_index do |type, index|
+ self['CHANMODES'][type] = modes_per_type[index]
+ end
+ end
+
+ def set_channellen(pattern)
+ self['CHANNELLEN'] = pattern.split('=')[1].to_i
+ end
+
+ def set_chantypes(pattern)
+ self['CHANTYPES'] = pattern.split('=')[1].split('')
+ end
+
+ def set_excepts(pattern)
+ mode_char = pattern.split('=')[1]
+ self['EXCEPTS'] = mode_char.nil? ? true : mode_char
+ end
+
+ def set_idchan(pattern)
+ value = pattern.split('=')[1]
+ value.split(',').each do |prefix_and_number|
+ prefix, number = prefix_and_number.split(':')
+ self['IDCHAN'][prefix] = number.to_i
+ end
+ end
+
+ def set_invex(pattern)
+ mode_char = pattern.split('=')[1]
+ self['INVEX'] = mode_char.nil? ? true : mode_char
+ end
+
+ def set_kicklen(pattern)
+ self['KICKLEN'] = pattern.split('=')[1].to_i
+ end
+
+ def set_maxlist(pattern)
+ value = pattern.split('=')[1]
+ value.split(',').each do |prefixes_and_maximum|
+ prefixes, maximum = prefixes_and_maximum.split(':')
+ prefixes.split('').each do |prefix|
+ self['MAXLIST'][prefix] = maximum.to_i
+ end
+ end
+ end
+
+ def set_modes(pattern)
+ mode_char = pattern.split('=')[1]
+ self['MODES'] = mode_char.nil? ? Float::INFINITY : mode_char.to_i
+ end
+
+ def set_network(pattern)
+ self['NETWORK'] = pattern.split('=')[1]
+ end
+
+ def set_nicklen(pattern)
+ self['NICKLEN'] = pattern.split('=')[1].to_i
+ end
+
+ def set_prefix(pattern)
+ modes, prefixes = pattern.scan(/\((.+)\)(.+)/).flatten
+ modes = modes.split('')
+ prefixes = prefixes.split('')
+ modes.zip(prefixes).each do |pair|
+ self['PREFIX'][pair.first] = pair.last
+ end
+ end
+
+ def set_safelist(pattern)
+ self['SAFELIST'] = true
+ end
+
+ def set_statusmsg(pattern)
+ self['STATUSMSG'] = pattern.split('=')[1].split('')
+ end
+
+ def set_std(pattern)
+ self['STD'] = pattern.split('=')[1].split(',')
+ end
+
+ def set_targmax(pattern)
+ targets = pattern.split('=')[1].split(',')
+ targets.each do |target_with_maximum|
+ target, maximum = target_with_maximum.split(':')
+ maximum = maximum.nil? ? Float::INFINITY : maximum.to_i
+ self['TARGMAX'][target] = maximum
+ end
+ end
+
+ def set_topiclen(pattern)
+ self['TOPICLEN'] = pattern.split('=')[1].to_i
+ end
+
+ def set_different(key, pattern)
+ if pattern.include? '='
+ if pattern.include? ','
+ self[key] = pattern.split('=')[1].split(',')
+ else
+ self[key] = pattern.split('=')[1]
+ end
+ else
+ self[key] = true
+ end
+ end
+ end
+end
View
178 lib/ponder/isupport_ostruct.rb
@@ -0,0 +1,178 @@
+require 'ostruct'
+
+module Ponder
+ class ISupport < OpenStruct
+ def initialize
+ super
+
+ # Defaults:
+ self.casemapping = 'rfc1459'
+ self.chanlimit = {}
+ self.chanmodes = {
+ 'A' => ['b'],
+ 'B' => ['k'],
+ 'C' => ['l'],
+ 'D' => %w(i m n p s t r)
+ }
+ self.channellen = 200
+ self.chantypes = ['#', '&']
+ self.excepts = false
+ self.idchan = {}
+ self.invex = false
+ self.kicklen = Float::INFINITY
+ self.maxlist = {}
+ self.modes = 3
+ self.network = ''
+ self.nicklen = Float::INFINITY
+ self.prefix = {'o' => '@', 'v' => '+'}
+ self.safelist = false
+ self.statusmsg = false
+ self.std = false
+ self.targmax = {}
+ self.topiclen = Float::INFINITY
+ end
+
+ def parse(message)
+ patterns = message.split(' ')
+ patterns.delete_at(0)
+ patterns.each do |pattern|
+ return self if pattern.start_with?(':')
+ key = pattern.scan(/\w+/).first.downcase
+ begin
+ method_name = "set_#{key}"
+ if respond_to?(method_name, true)
+ send method_name, pattern
+ else
+ set_different key, pattern
+ end
+ rescue
+ puts 'Error!' # TODO: Something useful. Warning?
+ end
+ end
+ end
+
+ private
+
+ def set_casemapping(pattern)
+ self.casemapping = pattern.split('=')[1]
+ end
+
+ def set_chanlimit(pattern)
+ value = pattern.split('=')[1]
+ value.split(',').each do |prefixes_and_limit|
+ prefixes, limit = prefixes_and_limit.split(':')
+ limit = limit.nil? ? Float::INFINITY : limit.to_i
+ prefixes.split('').each do |prefix|
+ self.chanlimit[prefix] = limit
+ end
+ end
+ end
+
+ def set_chanmodes(pattern)
+ value = pattern.split('=')[1]
+ modes_per_type = value.split(',').map { |modes| modes.split('') }
+ ('A'..'D').each_with_index do |type, index|
+ self.chanmodes[type] = modes_per_type[index]
+ end
+ end
+
+ def set_channellen(pattern)
+ self.channellen = pattern.split('=')[1].to_i
+ end
+
+ def set_chantypes(pattern)
+ self.chantypes = pattern.split('=')[1].split('')
+ end
+
+ def set_excepts(pattern)
+ mode_char = pattern.split('=')[1]
+ self.excepts = mode_char.nil? ? true : mode_char
+ end
+
+ def set_idchan(pattern)
+ value = pattern.split('=')[1]
+ value.split(',').each do |prefix_and_number|
+ prefix, number = prefix_and_number.split(':')
+ self.idchan[prefix] = number.to_i
+ end
+ end
+
+ def set_invex(pattern)
+ mode_char = pattern.split('=')[1]
+ self.invex = mode_char.nil? ? true : mode_char
+ end
+
+ def set_kicklen(pattern)
+ self.kicklen = pattern.split('=')[1].to_i
+ end
+
+ def set_maxlist(pattern)
+ value = pattern.split('=')[1]
+ value.split(',').each do |prefixes_and_maximum|
+ prefixes, maximum = prefixes_and_maximum.split(':')
+ prefixes.split('').each do |prefix|
+ self.maxlist[prefix] = maximum.to_i
+ end
+ end
+ end
+
+ def set_modes(pattern)
+ mode_char = pattern.split('=')[1]
+ self.modes = mode_char.nil? ? Float::INFINITY : mode_char.to_i
+ end
+
+ def set_network(pattern)
+ self.network = pattern.split('=')[1]
+ end
+
+ def set_nicklen(pattern)
+ self.nicklen = pattern.split('=')[1].to_i
+ end
+
+ def set_prefix(pattern)
+ modes, prefixes = pattern.scan(/\((.+)\)(.+)/).flatten
+ modes = modes.split('')
+ prefixes = prefixes.split('')
+ modes.zip(prefixes).each do |pair|
+ self.prefix[pair.first] = pair.last
+ end
+ end
+
+ def set_safelist(pattern)
+ self.safelist = true
+ end
+
+ def set_statusmsg(pattern)
+ self.statusmsg = pattern.split('=')[1].split('')
+ end
+
+ def set_std(pattern)
+ self.std = pattern.split('=')[1].split(',')
+ end
+
+ def set_targmax(pattern)
+ targets = pattern.split('=')[1].split(',')
+ targets.each do |target_with_maximum|
+ target, maximum = target_with_maximum.split(':')
+ maximum = maximum.nil? ? Float::INFINITY : maximum.to_i
+ self.targmax[target] = maximum
+ end
+ end
+
+ def set_topiclen(pattern)
+ self.topiclen = pattern.split('=')[1].to_i
+ end
+
+ def set_different(key, pattern)
+ if pattern.include? '='
+ if pattern.include? ','
+ send "#{key}=", pattern.split('=')[1].split(',')
+ else
+ send "#{key}=", pattern.split('=')[1]
+ end
+ else
+ send "#{key}=", true
+ end
+ end
+ end
+end
View
10 lib/ponder/thaum.rb
@@ -8,6 +8,7 @@
require 'ponder/connection'
require 'ponder/filter'
require 'ponder/irc'
+require 'ponder/isupport'
require 'ponder/logger/twoflogger'
require 'ponder/logger/blind_io'
require 'ponder/message_parser'
@@ -17,7 +18,7 @@ class Thaum
include IRC
include AsyncIRC::Delegate
- attr_reader :config, :callbacks
+ attr_reader :config, :callbacks, :isupport
attr_accessor :connected, :logger, :console_logger, :deferrables
def initialize(&block)
@@ -67,6 +68,9 @@ def initialize(&block)
# user callbacks
@callbacks = Hash.new { |hash, key| hash[key] = [] }
+ # setting up isuport
+ @isupport = ISupport.new
+
# standard callbacks for PING, VERSION, TIME and Nickname is already in use
on :query, /^\001PING \d+\001$/ do |event_data|
time = event_data[:message].scan(/\d+/)[0]
@@ -81,6 +85,10 @@ def initialize(&block)
notice event_data[:nick], "\001TIME #{Time.now.strftime('%a %b %d %H:%M:%S %Y')}\001"
end
+ on 005 do |event_data|
+ @isupport.parse event_data[:params]
+ end
+
# before and after filter
@before_filters = Hash.new { |hash, key| hash[key] = [] }
@after_filters = Hash.new { |hash, key| hash[key] = [] }
View
1  spec/irc_spec.rb
@@ -133,4 +133,3 @@
@ponder.ban('#mended_drum', 'foo!bar@baz')
end
end
-
View
267 spec/isupport_spec.rb
@@ -0,0 +1,267 @@
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+
+require 'spec_helper'
+require 'ponder/isupport'
+
+describe Ponder::ISupport do
+ before(:each) do
+ @isupport = Ponder::ISupport.new
+ end
+
+ describe 'CASEMAPPING' do
+ it 'sets CASEMAPPING correctly' do
+ @isupport.parse('bot_nick CASEMAPPING=foobar')
+ @isupport['CASEMAPPING'].should eq('foobar')
+ end
+ end
+
+ describe 'CHANLIMIT' do
+ it 'sets CHANLIMIT correctly having one argument' do
+ @isupport.parse('bot_nick CHANLIMIT=#:120')
+ @isupport['CHANLIMIT'].should eq({'#' => 120})
+ end
+
+ it 'sets CHANLIMIT correctly having many argument' do
+ @isupport.parse('bot_nick CHANLIMIT=#+:10,&')
+ @isupport['CHANLIMIT'].should eq({'#' => 10, '+' => 10, '&' => Float::INFINITY})
+ end
+ end
+
+ describe 'CHANMODES' do
+ it 'sets CHANMODES correctly' do
+ @isupport.parse('bot_nick CHANMODES=eIbq,k,flj,CFLMPQcgimnprstz')
+ @isupport['CHANMODES'].should eq({
+ 'A' => %w(e I b q),
+ 'B' => %w(k),
+ 'C' => %w(f l j),
+ 'D' => %w(C F L M P Q c g i m n p r s t z)
+ })
+ end
+ end
+
+ describe 'CHANNELLEN' do
+ it 'sets CHANNELLEN correctly' do
+ @isupport.parse('bot_nick CHANNELLEN=50')
+ @isupport['CHANNELLEN'].should eq(50)
+ end
+ end
+
+ describe 'CHANTYPES' do
+ it 'sets CHANTYPES correctly having one type' do
+ @isupport.parse('bot_nick CHANTYPES=#')
+ @isupport['CHANTYPES'].should eq(['#'])
+ end
+
+ it 'sets CHANTYPES correctly having many types' do
+ @isupport.parse('bot_nick CHANTYPES=+#&')
+ @isupport['CHANTYPES'].should eq(%w(+ # &))
+ end
+ end
+
+ describe 'EXCEPTS' do
+ it 'sets EXCEPTS correctly with mode_char' do
+ @isupport.parse('bot_nick EXCEPTS')
+ @isupport['EXCEPTS'].should be_true
+ end
+
+ it 'sets EXCEPTS correctly without mode_char' do
+ @isupport.parse('bot_nick EXCEPTS=e')
+ @isupport['EXCEPTS'].should eq('e')
+ end
+ end
+
+ describe 'IDCHAN' do
+ it 'sets IDCHAN correctly having one argument' do
+ @isupport.parse('bot_nick IDCHAN=!:5')
+ @isupport['IDCHAN'].should eq({'!' => 5})
+ end
+
+ it 'sets IDCHAN correctly having many arguments' do
+ @isupport.parse('bot_nick IDCHAN=!:5,?:4')
+ @isupport['IDCHAN'].should eq({'!' => 5, '?' => 4})
+ end
+ end
+
+ describe 'INVEX' do
+ it 'sets INVEX correctly having no argument' do
+ @isupport.parse('bot_nick INVEX')
+ @isupport['INVEX'].should be_true
+ end
+
+ it 'sets IDCHAN correctly having one argument' do
+ @isupport.parse('bot_nick INVEX=a')
+ @isupport['INVEX'].should eq('a')
+ end
+ end
+
+ describe 'KICKLEN' do
+ it 'sets KICKLEN correctly' do
+ @isupport.parse('bot_nick KICKLEN=100')
+ @isupport['KICKLEN'].should eq(100)
+ end
+ end
+
+ describe 'MAXLIST' do
+ it 'sets MAXLIST correctly having one argument' do
+ @isupport.parse('bot_nick MAXLIST=b:25')
+ @isupport['MAXLIST'].should eq({'b' => 25})
+ end
+
+ it 'sets MAXLIST correctly having many arguments' do
+ @isupport.parse('bot_nick MAXLIST=b:25,eI:50')
+ @isupport['MAXLIST'].should eq({'b' => 25, 'e' => 50, 'I' => 50})
+ end
+ end
+
+ describe 'MODES' do
+ it 'sets MODES correctly having no argument' do
+ @isupport.parse('bot_nick MODES')
+ @isupport['MODES'].should eq(Float::INFINITY)
+ end
+
+ it 'sets MODES correctly having one argument' do
+ @isupport.parse('bot_nick MODES=5')
+ @isupport['MODES'].should eq(5)
+ end
+ end
+
+ describe 'NETWORK' do
+ it 'sets NETWORK correctly' do
+ @isupport.parse('bot_nick NETWORK=freenode')
+ @isupport['NETWORK'].should eq('freenode')
+ end
+ end
+
+ describe 'NICKLEN' do
+ it 'sets NICKLEN correctly' do
+ @isupport.parse('bot_nick NICKLEN=9')
+ @isupport['NICKLEN'].should eq(9)
+ end
+ end
+
+ describe 'PREFIX' do
+ it 'sets PREFIX correctly' do
+ @isupport.parse('bot_nick PREFIX=(ohv)@%+')
+ @isupport['PREFIX'].should eq({'o' => '@', 'h' => '%', 'v' => '+'})
+ end
+ end
+
+ describe 'SAFELIST' do
+ it 'sets SAFELIST correctly' do
+ @isupport.parse('bot_nick SAFELIST')
+ @isupport['SAFELIST'].should be_true
+ end
+ end
+
+ describe 'STATUSMSG' do
+ it 'sets STATUSMSG correctly having one argument' do
+ @isupport.parse('bot_nick STATUSMSG=+')
+ @isupport['STATUSMSG'].should eq(['+'])
+ end
+
+ it 'sets STATUSMSG correctly having many arguments' do
+ @isupport.parse('bot_nick STATUSMSG=@+')
+ @isupport['STATUSMSG'].should eq(['@', '+'])
+ end
+ end
+
+ describe 'STD' do
+ it 'sets STD correctly having one argument' do
+ @isupport.parse('bot_nick STD=foo')
+ @isupport['STD'].should eq(['foo'])
+ end
+
+ it 'sets STD correctly having many arguments' do
+ @isupport.parse('bot_nick STD=foo,bar,baz')
+ @isupport['STD'].should eq(['foo', 'bar', 'baz'])
+ end
+ end
+
+ describe 'TARGMAX' do
+ it 'sets TARGMAX correctly having limits' do
+ @isupport.parse('bot_nick TARGMAX=NAMES:1,LIST:2,KICK:3')
+ @isupport['TARGMAX'].should eq({'NAMES' => 1, 'LIST' => 2, 'KICK' => 3})
+ end
+
+ it 'sets TARGMAX correctly having limits and no limits' do
+ @isupport.parse('bot_nick TARGMAX=NAMES:1,LIST:2,KICK:3,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR:')
+ @isupport['TARGMAX'].should eq({
+ 'NAMES' => 1,
+ 'LIST' => 2,
+ 'KICK' => 3,
+ 'WHOIS' => 1,
+ 'PRIVMSG' => 4,
+ 'NOTICE' => 4,
+ 'ACCEPT' => Float::INFINITY,
+ 'MONITOR' => Float::INFINITY
+ })
+ end
+ end
+
+ describe 'TOPICLEN' do
+ it 'sets TOPICLEN correctly' do
+ @isupport.parse('bot_nick TOPICLEN=250')
+ @isupport['TOPICLEN'].should eq(250)
+ end
+ end
+
+ describe 'Different' do
+ it 'sets non mentioned keys correclty aswell' do
+ @isupport.parse('bot_nick AWAYLEN=160')
+ @isupport.parse('bot_nick CNOTICE')
+ @isupport.parse('bot_nick EXTBAN=$,arx')
+ @isupport['AWAYLEN'].should eq('160')
+ @isupport['CNOTICE'].should be_true
+ @isupport['EXTBAN'].should eq(['$', 'arx'])
+ end
+ end
+
+ describe 'Several arguments at once' do
+ it 'sets several arguments at once correcty' do
+ @isupport.parse('bot_nick CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQcgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=freenode KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server')
+ @isupport.parse('bot_nick CASEMAPPING=strict CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server')
+ @isupport.parse('bot_nick EXTBAN=$,arx WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server')
+ @isupport['CHANTYPES'].should eq(['#'])
+ @isupport['EXCEPTS'].should be_true
+ @isupport['INVEX'].should be_true
+ @isupport['CHANMODES'].should eq({
+ 'A' => %w(e I b q),
+ 'B' => %w(k),
+ 'C' => %w(f l j),
+ 'D' => %w(C F L M P Q c g i m n p r s t z)
+ })
+ @isupport['CHANLIMIT'].should eq({'#' => 120})
+ @isupport['MAXLIST'].should eq({'b' => 100, 'q' => 100, 'e' => 100, 'I' => 100})
+ @isupport['MODES'].should eq(4)
+ @isupport['KNOCK'].should be_true
+ @isupport['STATUSMSG'].should eq(['@', '+'])
+ @isupport['CALLERID'].should eq('g')
+ @isupport['CASEMAPPING'].should eq('strict')
+ @isupport['CHARSET'].should eq('ascii')
+ @isupport['NICKLEN'].should eq(16)
+ @isupport['CHANNELLEN'].should eq(50)
+ @isupport['TOPICLEN'].should eq(390)
+ @isupport['ETRACE'].should be_true
+ @isupport['CPRIVMSG'].should be_true
+ @isupport['CNOTICE'].should be_true
+ @isupport['DEAF'].should eq('D')
+ @isupport['MONITOR'].should eq('100')
+ @isupport['FNC'].should be_true
+ @isupport['TARGMAX'].should eq({
+ 'NAMES' => 1,
+ 'LIST' => 1,
+ 'KICK' => 1,
+ 'WHOIS' => 1,
+ 'PRIVMSG' => 4,
+ 'NOTICE' => 4,
+ 'ACCEPT' => Float::INFINITY,
+ 'MONITOR' => Float::INFINITY
+ })
+ @isupport['EXTBAN'].should eq(['$', 'arx'])
+ @isupport['WHOX'].should be_true
+ @isupport['CLIENTVER'].should eq('3.0')
+ @isupport['SAFELIST'].should be_true
+ @isupport['ELIST'].should eq('CTU')
+ end
+ end
+end
View
1  spec/spec_helper.rb
@@ -3,4 +3,3 @@
require 'rubygems'
require 'rspec'
-
Please sign in to comment.
Something went wrong with that request. Please try again.