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