features.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. from __future__ import absolute_import
  2. import collections
  3. class FeatureSet(object):
  4. """
  5. An implementation of features as loaded from an ISUPPORT server directive.
  6. Each feature is loaded into an attribute of the same name (but lowercased
  7. to match Python sensibilities).
  8. >>> f = FeatureSet()
  9. >>> f.load(['target', 'PREFIX=(abc)+-/', 'your message sir'])
  10. >>> f.prefix == {'+': 'a', '-': 'b', '/': 'c'}
  11. True
  12. Order of prefix is relevant, so it is retained.
  13. >>> tuple(f.prefix)
  14. ('+', '-', '/')
  15. >>> f.load_feature('CHANMODES=foo,bar,baz')
  16. >>> f.chanmodes
  17. ['foo', 'bar', 'baz']
  18. """
  19. def __init__(self):
  20. self._set_rfc1459_prefixes()
  21. def _set_rfc1459_prefixes(self):
  22. "install standard (RFC1459) prefixes"
  23. self.set('PREFIX', {
  24. '@': 'o',
  25. '+': 'v',
  26. })
  27. def set(self, name, value=True):
  28. "set a feature value"
  29. setattr(self, name.lower(), value)
  30. def remove(self, feature_name):
  31. if feature_name in vars(self):
  32. delattr(self, feature_name)
  33. def load(self, arguments):
  34. "Load the values from the a ServerConnection arguments"
  35. target, features, msg = arguments[:1], arguments[1:-1], arguments[-1:]
  36. list(map(self.load_feature, features))
  37. def load_feature(self, feature):
  38. # negating
  39. if feature[0] == '-':
  40. return self.remove(feature[1:].lower())
  41. name, sep, value = feature.partition('=')
  42. if not sep:
  43. return
  44. if not value:
  45. self.set(name)
  46. return
  47. parser = getattr(self, '_parse_' + name, self._parse_other)
  48. value = parser(value)
  49. self.set(name, value)
  50. @staticmethod
  51. def _parse_PREFIX(value):
  52. "channel user prefixes"
  53. channel_modes, channel_chars = value.split(')')
  54. channel_modes = channel_modes[1:]
  55. return collections.OrderedDict(zip(channel_chars, channel_modes))
  56. @staticmethod
  57. def _parse_CHANMODES(value):
  58. "channel mode letters"
  59. return value.split(',')
  60. @staticmethod
  61. def _parse_TARGMAX(value):
  62. """
  63. >>> res = FeatureSet._parse_TARGMAX('a:3,c:,b:2')
  64. >>> res['a']
  65. 3
  66. """
  67. return dict(string_int_pair(target, ':')
  68. for target in value.split(','))
  69. @staticmethod
  70. def _parse_CHANLIMIT(value):
  71. """
  72. >>> res = FeatureSet._parse_CHANLIMIT('ibe:250,xyz:100')
  73. >>> len(res)
  74. 6
  75. >>> res['x']
  76. 100
  77. >>> res['i'] == res['b'] == res['e'] == 250
  78. True
  79. """
  80. pairs = map(string_int_pair, value.split(','))
  81. return dict(
  82. (target, number)
  83. for target_keys, number in pairs
  84. for target in target_keys
  85. )
  86. _parse_MAXLIST = _parse_CHANLIMIT
  87. @staticmethod
  88. def _parse_other(value):
  89. if value.isdigit():
  90. return int(value)
  91. return value
  92. def string_int_pair(target, sep=':'):
  93. name, value = target.split(sep)
  94. value = int(value) if value else None
  95. return name, value