| Class | Irc::Server |
| In: |
lib/rbot/irc.rb
|
| Parent: | Object |
| hostname | -> | to_s |
| capabilities | [R] | |
| chanmodes | [R] | |
| channels | [R] | |
| hostname | [R] | |
| supports | [R] | |
| usermodes | [R] | |
| users | [R] | |
| version | [R] |
Create a new Server, with all instance variables reset to nil (for scalar variables), empty channel and user lists and @supports initialized to the default values for all known supported features.
# File lib/rbot/irc.rb, line 1563
1563: def initialize
1564: @hostname = @version = @usermodes = @chanmodes = nil
1565:
1566: @channels = ChannelList.new
1567:
1568: @users = UserList.new
1569:
1570: reset_capabilities
1571: end
Returns the Channel with the given name on the server, creating it if necessary. This is a short form for new_channel(str, nil, [], false)
# File lib/rbot/irc.rb, line 1912
1912: def channel(str)
1913: new_channel(str,nil,[],false)
1914: end
TODO Ho
# File lib/rbot/irc.rb, line 1534
1534: def channel_names
1535: @channels.map { |ch| ch.downcase }
1536: end
Clears the server
# File lib/rbot/irc.rb, line 1637
1637: def clear
1638: reset_lists
1639: reset_capabilities
1640: @hostname = @version = @usermodes = @chanmodes = nil
1641: end
Remove User someuser from the list of Users. someuser must be specified with the full Netmask.
# File lib/rbot/irc.rb, line 1993
1993: def delete_user(someuser)
1994: idx = has_user?(someuser)
1995: raise "Tried to remove unmanaged user #{user}" unless idx
1996: have = self.user(someuser)
1997: @channels.each { |ch|
1998: delete_user_from_channel(have, ch)
1999: }
2000: @users.delete_at(idx)
2001: end
Finds all Users on server whose Netmask matches mask
# File lib/rbot/irc.rb, line 2011
2011: def find_users(mask)
2012: nm = new_netmask(mask)
2013: @users.inject(UserList.new) {
2014: |list, user|
2015: if user.user == "*" or user.host == "*"
2016: list << user if user.nick.irc_downcase(casemap) =~ nm.nick.irc_downcase(casemap).to_irc_regexp
2017: else
2018: list << user if user.matches?(nm)
2019: end
2020: list
2021: }
2022: end
# File lib/rbot/irc.rb, line 1543
1543: def inspect
1544: chans, users = [@channels, @users].map {|d|
1545: d.sort { |a, b|
1546: a.downcase <=> b.downcase
1547: }.map { |x|
1548: x.inspect
1549: }
1550: }
1551:
1552: str = self.__to_s__[0..-2]
1553: str << " @hostname=#{hostname}"
1554: str << " @channels=#{chans}"
1555: str << " @users=#{users}"
1556: str << ">"
1557: end
Convert a prefix (@, +, %, …) to the corresponding mode (o, v, h, …). See also prefix_for_mode
# File lib/rbot/irc.rb, line 1618
1618: def mode_for_prefix(pfx)
1619: return @supports[:prefix][:modes][
1620: @supports[:prefix][:prefixes].index(pfx.to_sym)
1621: ]
1622: end
Create a new Channel object bound to the receiver and add it to the list of Channels on the receiver, unless the channel was present already. In this case, the default action is to raise an exception, unless fails is set to false. An exception can also be raised if str is nil or empty, again only if fails is set to true; otherwise, the method just returns nil
# File lib/rbot/irc.rb, line 1835
1835: def new_channel(name, topic=nil, users=[], fails=true)
1836: if name.nil_or_empty?
1837: raise "Tried to look for empty or nil channel name #{name.inspect}" if fails
1838: return nil
1839: end
1840: ex = get_chan(name)
1841: if ex
1842: raise "Channel #{name} already exists on server #{self}" if fails
1843: return ex
1844: else
1845:
1846: prefix = name[0,1]
1847:
1848: # Give a warning if the new Channel goes over some server limits.
1849: #
1850: # FIXME might need to raise an exception
1851: #
1852: warn "#{self} doesn't support channel prefix #{prefix}" unless @supports[:chantypes].include?(prefix)
1853: warn "#{self} doesn't support channel names this long (#{name.length} > #{@supports[:channellen]})" unless name.length <= @supports[:channellen]
1854:
1855: # Next, we check if we hit the limit for channels of type +prefix+
1856: # if the server supports +chanlimit+
1857: #
1858: @supports[:chanlimit].keys.each { |k|
1859: next unless k.include?(prefix)
1860: count = 0
1861: channel_names.each { |n|
1862: count += 1 if k.include?(n[0])
1863: }
1864: # raise IndexError, "Already joined #{count} channels with prefix #{k}" if count == @supports[:chanlimit][k]
1865: warn "Already joined #{count}/#{@supports[:chanlimit][k]} channels with prefix #{k}, we may be going over server limits" if count >= @supports[:chanlimit][k]
1866: }
1867:
1868: # So far, everything is fine. Now create the actual Channel
1869: #
1870: chan = Channel.new(name, topic, users, :server => self)
1871:
1872: # We wade through +prefix+ and +chanmodes+ to create appropriate
1873: # lists and flags for this channel
1874:
1875: @supports[:prefix][:modes].each { |mode|
1876: chan.create_mode(mode, Channel::UserMode)
1877: } if @supports[:prefix][:modes]
1878:
1879: @supports[:chanmodes].each { |k, val|
1880: if val
1881: case k
1882: when :typea
1883: val.each { |mode|
1884: chan.create_mode(mode, Channel::ModeTypeA)
1885: }
1886: when :typeb
1887: val.each { |mode|
1888: chan.create_mode(mode, Channel::ModeTypeB)
1889: }
1890: when :typec
1891: val.each { |mode|
1892: chan.create_mode(mode, Channel::ModeTypeC)
1893: }
1894: when :typed
1895: val.each { |mode|
1896: chan.create_mode(mode, Channel::ModeTypeD)
1897: }
1898: end
1899: end
1900: }
1901:
1902: @channels << chan
1903: # debug "Created channel #{chan.inspect}"
1904: return chan
1905: end
1906: end
Create a new User object bound to the receiver and add it to the list of Users on the receiver, unless the User was present already. In this case, the default action is to raise an exception, unless fails is set to false. An exception can also be raised if str is nil or empty, again only if fails is set to true; otherwise, the method just returns nil
# File lib/rbot/irc.rb, line 1945
1945: def new_user(str, fails=true)
1946: if str.nil_or_empty?
1947: raise "Tried to look for empty or nil user name #{str.inspect}" if fails
1948: return nil
1949: end
1950: tmp = str.to_irc_user(:server => self)
1951: old = get_user(tmp.nick)
1952: # debug "Tmp: #{tmp.inspect}"
1953: # debug "Old: #{old.inspect}"
1954: if old
1955: # debug "User already existed as #{old.inspect}"
1956: if tmp.known?
1957: if old.known?
1958: # debug "Both were known"
1959: # Do not raise an error: things like Freenode change the hostname after identification
1960: warning "User #{tmp.nick} has inconsistent Netmasks! #{self} knows #{old.inspect} but access was tried with #{tmp.inspect}" if old != tmp
1961: raise "User #{tmp} already exists on server #{self}" if fails
1962: end
1963: if old.fullform.downcase != tmp.fullform.downcase
1964: old.replace(tmp)
1965: # debug "Known user now #{old.inspect}"
1966: end
1967: end
1968: return old
1969: else
1970: warn "#{self} doesn't support nicknames this long (#{tmp.nick.length} > #{@supports[:nicklen]})" unless tmp.nick.length <= @supports[:nicklen]
1971: @users << tmp
1972: return @users.last
1973: end
1974: end
This method is used to parse a 005 RPL_ISUPPORT line
See the RPL_ISUPPORT draft
# File lib/rbot/irc.rb, line 1674
1674: def parse_isupport(line)
1675: debug "Parsing ISUPPORT #{line.inspect}"
1676: ar = line.split(' ')
1677: reparse = []
1678: ar.each { |en|
1679: prekey, val = en.split('=', 2)
1680: if prekey =~ /^-(.*)/
1681: key = $1.downcase.to_sym
1682: val = false
1683: else
1684: key = prekey.downcase.to_sym
1685: end
1686: case key
1687: when :casemapping
1688: noval_warn(key, val) {
1689: if val == 'charset'
1690: reparse << "CASEMAPPING=(charset)"
1691: else
1692: # TODO some servers offer non-standard CASEMAPPINGs in the form
1693: # locale.charset[-options], which indicate an extended set of
1694: # allowed characters (mostly for nicks). This might be supported
1695: # with hooks for the unicode core module
1696: @supports[key] = val.to_irc_casemap
1697: end
1698: }
1699: when :chanlimit, :idchan, :maxlist, :targmax
1700: noval_warn(key, val) {
1701: groups = val.split(',')
1702: groups.each { |g|
1703: k, v = g.split(':')
1704: @supports[key][k] = v.to_i || 0
1705: if @supports[key][k] == 0
1706: warn "Deleting #{key} limit of 0 for #{k}"
1707: @supports[key].delete(k)
1708: end
1709: }
1710: }
1711: when :chanmodes
1712: noval_warn(key, val) {
1713: groups = val.split(',')
1714: @supports[key][:typea] = groups[0].scan(/./).map { |x| x.to_sym}
1715: @supports[key][:typeb] = groups[1].scan(/./).map { |x| x.to_sym}
1716: @supports[key][:typec] = groups[2].scan(/./).map { |x| x.to_sym}
1717: @supports[key][:typed] = groups[3].scan(/./).map { |x| x.to_sym}
1718: }
1719: when :channellen, :kicklen, :modes, :topiclen
1720: if val
1721: @supports[key] = val.to_i
1722: else
1723: @supports[key] = nil
1724: end
1725: when :chantypes
1726: @supports[key] = val # can also be nil
1727: when :excepts
1728: val ||= 'e'
1729: @supports[key] = val
1730: when :invex
1731: val ||= 'I'
1732: @supports[key] = val
1733: when :maxchannels
1734: noval_warn(key, val) {
1735: reparse << "CHANLIMIT=(chantypes):#{val} "
1736: }
1737: when :maxtargets
1738: noval_warn(key, val) {
1739: @supports[:targmax]['PRIVMSG'] = val.to_i
1740: @supports[:targmax]['NOTICE'] = val.to_i
1741: }
1742: when :network
1743: noval_warn(key, val) {
1744: @supports[key] = val
1745: }
1746: when :nicklen
1747: noval_warn(key, val) {
1748: @supports[key] = val.to_i
1749: }
1750: when :prefix
1751: if val
1752: val.scan(/\((.*)\)(.*)/) { |m, p|
1753: @supports[key][:modes] = m.scan(/./).map { |x| x.to_sym}
1754: @supports[key][:prefixes] = p.scan(/./).map { |x| x.to_sym}
1755: }
1756: else
1757: @supports[key][:modes] = nil
1758: @supports[key][:prefixes] = nil
1759: end
1760: when :safelist
1761: val_warn(key, val) {
1762: @supports[key] = val.nil? ? true : val
1763: }
1764: when :statusmsg
1765: noval_warn(key, val) {
1766: @supports[key] = val.scan(/./)
1767: }
1768: when :std
1769: noval_warn(key, val) {
1770: @supports[key] = val.split(',')
1771: }
1772: else
1773: @supports[key] = val.nil? ? true : val
1774: end
1775: }
1776: unless reparse.empty?
1777: reparse_str = reparse.join(" ")
1778: reparse_str.gsub!("(chantypes)",@supports[:chantypes])
1779: reparse_str.gsub!("(charset)",@supports[:charset] || 'rfc1459')
1780: parse_isupport(reparse_str)
1781: end
1782: end
This method is used to parse a 004 RPL_MY_INFO line
# File lib/rbot/irc.rb, line 1645
1645: def parse_my_info(line)
1646: ar = line.split(' ')
1647: @hostname = ar[0]
1648: @version = ar[1]
1649: @usermodes = ar[2]
1650: @chanmodes = ar[3]
1651: end
Convert a mode (o, v, h, …) to the corresponding prefix (@, +, %, …). See also mode_for_prefix
# File lib/rbot/irc.rb, line 1610
1610: def prefix_for_mode(mode)
1611: return @supports[:prefix][:prefixes][
1612: @supports[:prefix][:modes].index(mode.to_sym)
1613: ]
1614: end
Resets the server capabilities
# File lib/rbot/irc.rb, line 1575
1575: def reset_capabilities
1576: @supports = {
1577: :casemapping => 'rfc1459'.to_irc_casemap,
1578: :chanlimit => {},
1579: :chanmodes => {
1580: :typea => nil, # Type A: address lists
1581: :typeb => nil, # Type B: needs a parameter
1582: :typec => nil, # Type C: needs a parameter when set
1583: :typed => nil # Type D: must not have a parameter
1584: },
1585: :channellen => 50,
1586: :chantypes => "#&!+",
1587: :excepts => nil,
1588: :idchan => {},
1589: :invex => nil,
1590: :kicklen => nil,
1591: :maxlist => {},
1592: :modes => 3,
1593: :network => nil,
1594: :nicklen => 9,
1595: :prefix => {
1596: :modes => [:o, :v],
1597: :prefixes => ["@""@", :+]
1598: },
1599: :safelist => nil,
1600: :statusmsg => nil,
1601: :std => nil,
1602: :targmax => {},
1603: :topiclen => nil
1604: }
1605: @capabilities = {}
1606: end
TODO Ho
# File lib/rbot/irc.rb, line 1539
1539: def user_nicks
1540: @users.map { |u| u.downcase }
1541: end
# File lib/rbot/irc.rb, line 1653
1653: def noval_warn(key, val, &block)
1654: if val
1655: yield if block_given?
1656: else
1657: warn "No #{key.to_s.upcase} value"
1658: end
1659: end