sopel.modules.adminchannel   F
last analyzed

Complexity

Total Complexity 63

Size/Duplication

Total Lines 353
Duplicated Lines 38.24 %

Importance

Changes 0
Metric Value
wmc 63
eloc 256
dl 135
loc 353
rs 3.36
c 0
b 0
f 0

15 Functions

Rating   Name   Duplication   Size   Complexity  
A default_mask() 0 7 1
B ban() 27 27 6
B unquiet() 26 26 6
B quiet() 26 26 6
B configureHostMask() 0 20 7
A show_mask() 0 8 1
A devoice() 0 15 3
A set_mask() 0 12 1
A op() 15 15 3
B unban() 26 26 6
B kick() 0 25 6
A deop() 15 15 3
A voice() 0 15 3
B topic() 0 33 5
B kickban() 0 34 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like sopel.modules.adminchannel 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
# coding=utf-8
2
"""
3
adminchannel.py - Sopel Channel Admin Module
4
Copyright 2010-2011, Michael Yanovich, Alek Rollyson, and Elsie Powell
5
Copyright © 2012, Elad Alfassa <[email protected]>
6
Licensed under the Eiffel Forum License 2.
7
8
https://sopel.chat
9
"""
10
from __future__ import unicode_literals, absolute_import, print_function, division
11
12
import re
13
14
from sopel import formatting
15
from sopel.module import (
16
    commands, example, priority, OP, HALFOP, require_privilege, require_chanmsg
17
)
18
from sopel.tools import Identifier
19
20
21
def default_mask(trigger):
22
    welcome = formatting.color('Welcome to:', formatting.colors.PURPLE)
23
    chan = formatting.color(trigger.sender, formatting.colors.TEAL)
24
    topic_ = formatting.bold('Topic:')
25
    topic_ = formatting.color('| ' + topic_, formatting.colors.PURPLE)
26
    arg = formatting.color('{}', formatting.colors.GREEN)
27
    return '{} {} {} {}'.format(welcome, chan, topic_, arg)
28
29
30 View Code Duplication
@require_chanmsg
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
31
@require_privilege(OP, 'You are not a channel operator.')
32
@commands('op')
33
def op(bot, trigger):
34
    """
35
    Command to op users in a room. If no nick is given,
36
    Sopel will op the nick who sent the command
37
    """
38
    if bot.channels[trigger.sender].privileges[bot.nick] < OP:
39
        return bot.reply("I'm not a channel operator!")
40
    nick = trigger.group(2)
41
    channel = trigger.sender
42
    if not nick:
43
        nick = trigger.nick
44
    bot.write(['MODE', channel, "+o", nick])
45
46
47 View Code Duplication
@require_chanmsg
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
48
@require_privilege(OP, 'You are not a channel operator.')
49
@commands('deop')
50
def deop(bot, trigger):
51
    """
52
    Command to deop users in a room. If no nick is given,
53
    Sopel will deop the nick who sent the command
54
    """
55
    if bot.channels[trigger.sender].privileges[bot.nick] < OP:
56
        return bot.reply("I'm not a channel operator!")
57
    nick = trigger.group(2)
58
    channel = trigger.sender
59
    if not nick:
60
        nick = trigger.nick
61
    bot.write(['MODE', channel, "-o", nick])
62
63
64
@require_chanmsg
65
@require_privilege(OP, 'You are not a channel operator.')
66
@commands('voice')
67
def voice(bot, trigger):
68
    """
69
    Command to voice users in a room. If no nick is given,
70
    Sopel will voice the nick who sent the command
71
    """
72
    if bot.channels[trigger.sender].privileges[bot.nick] < HALFOP:
73
        return bot.reply("I'm not a channel operator!")
74
    nick = trigger.group(2)
75
    channel = trigger.sender
76
    if not nick:
77
        nick = trigger.nick
78
    bot.write(['MODE', channel, "+v", nick])
79
80
81
@require_chanmsg
82
@require_privilege(OP, 'You are not a channel operator.')
83
@commands('devoice')
84
def devoice(bot, trigger):
85
    """
86
    Command to devoice users in a room. If no nick is given,
87
    Sopel will devoice the nick who sent the command
88
    """
89
    if bot.channels[trigger.sender].privileges[bot.nick] < HALFOP:
90
        return bot.reply("I'm not a channel operator!")
91
    nick = trigger.group(2)
92
    channel = trigger.sender
93
    if not nick:
94
        nick = trigger.nick
95
    bot.write(['MODE', channel, "-v", nick])
96
97
98
@require_chanmsg
99
@require_privilege(OP, 'You are not a channel operator.')
100
@commands('kick')
101
@priority('high')
102
def kick(bot, trigger):
103
    """Kick a user from the channel."""
104
    if bot.channels[trigger.sender].privileges[bot.nick] < HALFOP:
105
        return bot.reply("I'm not a channel operator!")
106
    text = trigger.group().split()
107
    argc = len(text)
108
    if argc < 2:
109
        return
110
    opt = Identifier(text[1])
111
    nick = opt
112
    channel = trigger.sender
113
    reasonidx = 2
114
    if not opt.is_nick():
115
        if argc < 3:
116
            return
117
        nick = text[2]
118
        channel = opt
119
        reasonidx = 3
120
    reason = ' '.join(text[reasonidx:])
121
    if nick != bot.config.core.nick:
122
        bot.kick(nick, channel, reason)
123
124
125
def configureHostMask(mask):
126
    if mask == '*!*@*':
127
        return mask
128
    if re.match('^[^.@!/]+$', mask) is not None:
129
        return '%s!*@*' % mask
130
    if re.match('^[^@!]+$', mask) is not None:
131
        return '*!*@%s' % mask
132
133
    m = re.match('^([^!@]+)@$', mask)
134
    if m is not None:
135
        return '*!%s@*' % m.group(1)
136
137
    m = re.match('^([^!@]+)@([^@!]+)$', mask)
138
    if m is not None:
139
        return '*!%s@%s' % (m.group(1), m.group(2))
140
141
    m = re.match('^([^!@]+)!(^[!@]+)@?$', mask)
142
    if m is not None:
143
        return '%s!%s@*' % (m.group(1), m.group(2))
144
    return ''
145
146
147 View Code Duplication
@require_chanmsg
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
148
@require_privilege(OP, 'You are not a channel operator.')
149
@commands('ban')
150
@priority('high')
151
def ban(bot, trigger):
152
    """Ban a user from the channel
153
154
    The bot must be a channel operator for this command to work.
155
    """
156
    if bot.channels[trigger.sender].privileges[bot.nick] < HALFOP:
157
        return bot.reply("I'm not a channel operator!")
158
    text = trigger.group().split()
159
    argc = len(text)
160
    if argc < 2:
161
        return
162
    opt = Identifier(text[1])
163
    banmask = opt
164
    channel = trigger.sender
165
    if not opt.is_nick():
166
        if argc < 3:
167
            return
168
        channel = opt
169
        banmask = text[2]
170
    banmask = configureHostMask(banmask)
171
    if banmask == '':
172
        return
173
    bot.write(['MODE', channel, '+b', banmask])
174
175
176 View Code Duplication
@require_chanmsg
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
177
@require_privilege(OP, 'You are not a channel operator.')
178
@commands('unban')
179
def unban(bot, trigger):
180
    """Unban a user from the channel
181
182
    The bot must be a channel operator for this command to work.
183
    """
184
    if bot.channels[trigger.sender].privileges[bot.nick] < HALFOP:
185
        return bot.reply("I'm not a channel operator!")
186
    text = trigger.group().split()
187
    argc = len(text)
188
    if argc < 2:
189
        return
190
    opt = Identifier(text[1])
191
    banmask = opt
192
    channel = trigger.sender
193
    if not opt.is_nick():
194
        if argc < 3:
195
            return
196
        channel = opt
197
        banmask = text[2]
198
    banmask = configureHostMask(banmask)
199
    if banmask == '':
200
        return
201
    bot.write(['MODE', channel, '-b', banmask])
202
203
204 View Code Duplication
@require_chanmsg
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
205
@require_privilege(OP, 'You are not a channel operator.')
206
@commands('quiet')
207
def quiet(bot, trigger):
208
    """Quiet a user
209
210
    The bot must be a channel operator for this command to work.
211
    """
212
    if bot.channels[trigger.sender].privileges[bot.nick] < OP:
213
        return bot.reply("I'm not a channel operator!")
214
    text = trigger.group().split()
215
    argc = len(text)
216
    if argc < 2:
217
        return
218
    opt = Identifier(text[1])
219
    quietmask = opt
220
    channel = trigger.sender
221
    if not opt.is_nick():
222
        if argc < 3:
223
            return
224
        quietmask = text[2]
225
        channel = opt
226
    quietmask = configureHostMask(quietmask)
227
    if quietmask == '':
228
        return
229
    bot.write(['MODE', channel, '+q', quietmask])
230
231
232 View Code Duplication
@require_chanmsg
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
233
@require_privilege(OP, 'You are not a channel operator.')
234
@commands('unquiet')
235
def unquiet(bot, trigger):
236
    """Unquiet a user
237
238
    The bot must be a channel operator for this command to work.
239
    """
240
    if bot.channels[trigger.sender].privileges[bot.nick] < OP:
241
        return bot.reply("I'm not a channel operator!")
242
    text = trigger.group().split()
243
    argc = len(text)
244
    if argc < 2:
245
        return
246
    opt = Identifier(text[1])
247
    quietmask = opt
248
    channel = trigger.sender
249
    if not opt.is_nick():
250
        if argc < 3:
251
            return
252
        quietmask = text[2]
253
        channel = opt
254
    quietmask = configureHostMask(quietmask)
255
    if quietmask == '':
256
        return
257
    bot.write(['MODE', channel, '-q', quietmask])
258
259
260
@require_chanmsg
261
@require_privilege(OP, 'You are not a channel operator.')
262
@commands('kickban', 'kb')
263
@example('.kickban [#chan] user1 user!*@* get out of here')
264
@priority('high')
265
def kickban(bot, trigger):
266
    """Kick and ban a user from the channel
267
268
    The bot must be a channel operator for this command to work.
269
    """
270
    if bot.channels[trigger.sender].privileges[bot.nick] < HALFOP:
271
        return bot.reply("I'm not a channel operator!")
272
    text = trigger.group().split()
273
    argc = len(text)
274
    if argc < 4:
275
        return
276
    opt = Identifier(text[1])
277
    nick = opt
278
    mask = text[2]
279
    channel = trigger.sender
280
    reasonidx = 3
281
    if not opt.is_nick():
282
        if argc < 5:
283
            return
284
        channel = opt
285
        nick = text[2]
286
        mask = text[3]
287
        reasonidx = 4
288
    reason = ' '.join(text[reasonidx:])
289
    mask = configureHostMask(mask)
290
    if mask == '':
291
        return
292
    bot.write(['MODE', channel, '+b', mask])
293
    bot.kick(nick, channel, reason)
294
295
296
@require_chanmsg
297
@require_privilege(OP, 'You are not a channel operator.')
298
@commands('topic')
299
def topic(bot, trigger):
300
    """Change the channel topic
301
302
    The bot must be a channel operator for this command to work.
303
    """
304
    if bot.channels[trigger.sender].privileges[bot.nick] < HALFOP:
305
        return bot.reply("I'm not a channel operator!")
306
    if not trigger.group(2):
307
        return
308
    channel = trigger.sender.lower()
309
310
    narg = 1
311
    mask = None
312
    mask = bot.db.get_channel_value(channel, 'topic_mask')
313
    mask = mask or default_mask(trigger)
314
    mask = mask.replace('%s', '{}')
315
    narg = len(re.findall('{}', mask))
316
317
    top = trigger.group(2)
318
    args = []
319
    if top:
320
        args = top.split('~', narg)
321
322
    if len(args) != narg:
323
        message = "Not enough arguments. You gave {}, it requires {}.".format(
324
            len(args), narg)
325
        return bot.say(message)
326
    topic = mask.format(*args)
327
328
    bot.write(('TOPIC', channel + ' :' + topic))
329
330
331
@require_chanmsg
332
@require_privilege(OP, 'You are not a channel operator.')
333
@commands('tmask')
334
def set_mask(bot, trigger):
335
    """Set the topic mask to use for the current channel
336
337
    Within the topic mask, {} is used to allow substituting in chunks of text.
338
339
    This mask is used when running the 'topic' command.
340
    """
341
    bot.db.set_channel_value(trigger.sender, 'topic_mask', trigger.group(2))
342
    bot.say("Gotcha, " + trigger.nick)
343
344
345
@require_chanmsg
346
@require_privilege(OP, 'You are not a channel operator.')
347
@commands('showmask')
348
def show_mask(bot, trigger):
349
    """Show the topic mask for the current channel."""
350
    mask = bot.db.get_channel_value(trigger.sender, 'topic_mask')
351
    mask = mask or default_mask(trigger)
352
    bot.say(mask)
353