Event.matches()   F
last analyzed

Complexity

Conditions 19

Size

Total Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 19
c 1
b 0
f 1
dl 0
loc 59
rs 3.1913

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like Event.matches() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import re
2
class Event:
3
  """
4
  Allows event type definition. The definition accepts a regex.
5
  Every event can be triggered by specific lines, messages, message_id or users.
6
  Eventually (see time_event branch for proof-of-concept implementation) time-sensitive events will be triggerable as well.
7
8
  Each line received by the bot is passed to each module in the modules_list. If the module determines the line matches what the event cares about,
9
  the event calls each of its subscribers itself, which contains all the information the module needs to respond appropriately.
10
11
  To use:
12
    e = Event("__my_type__")
13
    e.define("some_regex")
14
    bot.register_event(e, calling_module)
15
  """
16
  def __init__(self, _type):
17
    """
18
    Define your own type here. Make sure if you're making a broad event (all messages, for example) you use a sane type, as other modules that care about this kind of event can subscribe to it.
19
    
20
    Args:
21
    _type: string. like "__youtube__" or "__weather__". Underscores are a convention.
22
    """
23
    self._type = _type
24
    self.subscribers = [] # this is a list of subscribers to notify
25
    self.user = ""
26
    self.definition = ""
27
    self.msg_definition = ""
28
    self.user_definition = ""
29
    self.channel = ""
30
    self.line = ""
31
    self.msg = ""
32
    self.verb = ""
33
    self.mode = ""
34
    self.is_pm = False
35
    self.message_id = -1
36
    
37
  def subscribe(self, e):
38
    """
39
    Append passed-in event to our list of subscribing modules.
40
41
    Args:
42
    e: event.
43
    """
44
    self.subscribers.append(e)
45
46
  def define(self, definition=None, msg_definition=None, user_definition=None, message_id=None, mode=None):
47
    """
48
    Define ourself by general line (definition), msg_definition (what someone says in a channel or PM), user_definition (the user who said the thing), or message_id (like 376 for MOTD or 422 for no MOTD)
49
    Currently, an event is defined by only one type of definition. If one were to remove the returns after each self. set, an event could be defined and triggered by any of several definitions.
50
51
    Args:
52
    definition: string. regex allowed.
53
    msg_definition: string. regex allowed. this is what someone would say in a channel. like "hello, pybot".
54
    user_definition: string. the user that said the thing. like 'hlmtre' or 'BoneKin'.
55
    message_id: the numerical ID of low-level IRC protocol stuff. 376, for example, tells clients 'hey, this is the MOTD.'
56
    """
57
    if definition is not None:
58
      self.definition = definition
59
    if msg_definition is not None:
60
      self.msg_definition = msg_definition
61
    if user_definition is not None:
62
      self.user_definition = user_definition
63
    if message_id is not None:
64
      self.message_id = message_id
65
    if mode is not None:
66
      self.mode = mode
67
68
    return
69
70
71
  def matches(self, line):
72
    """
73
    Fills out the event object per line, and returns True or False if the line matches one of our definitions.
74
    Args:
75
    line: string. The entire incoming line.
76
77
    Return:
78
    boolean; True or False.
79
    """
80
    # perhaps TODO
81
    # first try very simply
82
    if len(self.definition) and self.definition in line:
83
      return True
84
    # grab message id. not always present
85
    try:
86
      temp = line.split(":")[1].split(" ")[1]
87
    except IndexError:
88
      pass
89
90
    if len(self.mode):
91
      try:
92
        split_line = line.split()
93
        temp_verb = split_line[1] # first nick, then verb
94
        if self.mode == temp_verb.strip():
95
          return True
96
      except IndexError:
97
        pass
98
99
    try:
100
      message_id = int(temp)
101
    except (ValueError, UnboundLocalError):
102
      message_id = 0
103
104
    try:
105
      msg = line.split(":",2)[2]
106
    except IndexError:
107
      return
108
109
    if len(self.msg_definition):
110
      if re.search(self.msg_definition, msg):
111
        return True
112
113
    if len(self.definition):
114
      if re.search(self.definition, line):
115
        return True
116
117
    if len(self.user_definition):
118
      if len(line) and "PRIVMSG" in line > 0:
119
        line_array = line.split()
120
        user_and_mask = line_array[0][1:]
121
        user = user_and_mask.split("!")[0]
122
        if self.user_definition == user:
123
          return True
124
125
    if type(self.message_id) is int:
126
      if self.message_id == message_id:
127
        return True
128
129
    return False
130
131
  def notifySubscribers(self, line):
132
    """
133
    Fills out the object with all necessary information, then notifies subscribers with itself (an event with all the line information parsed out) as an argument.
134
    Args:
135
    line: string
136
    
137
    """
138
    self.line = line
139
    if line == "null":
140
      for s in self.subscribers:
141
        s.handle(self)
142
      return
143
    self.user = line.split(":")[1].rsplit("!")[0] # nick is first thing on line
144
    if "JOIN" in line or "QUIT" in line:
145
      self.user = line.split("!")[0].replace(":","")
146
    try:
147
      temp = line.split(":")[1].split(" ")[1]
148
    except IndexError:
149
      pass
150
151
    try:
152
      self.msg = line.split(":",2)[2]
153
    except IndexError:
154
      self.msg = ""
155
156
    l = line.split()
157
    self.channel = ""
158
    self.verb = ""
159
    ind = 0
160
    privmsg_index = 0
161
    for e in l:
162
      ind+=1
163
      if e == "PRIVMSG":
164
        privmsg_index = ind
165
      if e.startswith("#"):
166
        self.channel = e
167
        break
168
    for v in l:
169
      if v.strip() in ["JOIN","PART","QUIT","NICK","KICK","PRIVMSG","TOPIC", "NOTICE", "PING", "PONG", "MODE"]:
170
        self.verb = v
171
        break
172
    # channel is unset if it does not begin with #
173
    if self.verb == "PRIVMSG" and not len(self.channel):
174
      self.is_pm = True
175
    for s in self.subscribers:
176
      try:
177
        s.handle(self)
178
      except AttributeError:
179
        pass
180