2005-01-19 14:14:38 +01:00
###
2005-01-19 14:33:05 +01:00
# Copyright (c) 2002-2005, Jeremiah Fincher
2005-01-19 14:14:38 +01:00
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import re
2014-01-17 20:38:59 +01:00
import sys
2005-01-19 14:14:38 +01:00
import fnmatch
import supybot . conf as conf
2013-01-20 00:09:37 +01:00
import supybot . gpg as gpg
2005-01-19 14:14:38 +01:00
import supybot . utils as utils
import supybot . ircdb as ircdb
from supybot . commands import *
import supybot . ircutils as ircutils
import supybot . callbacks as callbacks
2010-10-16 13:51:27 +02:00
from supybot . i18n import PluginInternationalization , internationalizeDocstring
_ = PluginInternationalization ( ' User ' )
2005-01-19 14:14:38 +01:00
2005-02-09 08:04:04 +01:00
class User ( callbacks . Plugin ) :
2014-11-30 21:07:41 +01:00
""" Provides commands for dealing with users, such as registration and
2014-12-20 10:45:17 +01:00
authentication to the bot . This is a core Supybot plugin that should
2014-11-30 21:07:41 +01:00
not be removed ! """
2005-01-19 14:14:38 +01:00
def _checkNotChannel ( self , irc , msg , password = ' ' ) :
2005-01-29 22:24:17 +01:00
if password and irc . isChannel ( msg . args [ 0 ] ) :
2014-01-20 15:43:55 +01:00
raise callbacks . Error ( conf . supybot . replies . requiresPrivacy ( ) )
2005-01-19 14:14:38 +01:00
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def list ( self , irc , msg , args , optlist , glob ) :
""" [--capability=<capability>] [<glob>]
Returns the valid registered usernames matching < glob > . If < glob > is
not given , returns all registered usernames .
"""
predicates = [ ]
for ( option , arg ) in optlist :
if option == ' capability ' :
2013-04-27 10:10:11 +02:00
if arg in conf . supybot . capabilities . private ( ) :
try :
u = ircdb . users . getUser ( msg . prefix )
if not u . _checkCapability ( ' admin ' ) :
raise KeyError
except KeyError :
# Note that it may be raised by checkCapability too.
irc . error ( _ ( ' This is a private capability. Only admins '
' can see who has it. ' ) , Raise = True )
2005-01-19 14:14:38 +01:00
def p ( u , cap = arg ) :
try :
return u . _checkCapability ( cap )
except KeyError :
return False
predicates . append ( p )
if glob :
r = re . compile ( fnmatch . translate ( glob ) , re . I )
def p ( u ) :
return r . match ( u . name ) is not None
predicates . append ( p )
users = [ ]
2015-08-08 22:20:14 +02:00
for u in ircdb . users . values ( ) :
2005-01-19 14:14:38 +01:00
for predicate in predicates :
if not predicate ( u ) :
break
else :
users . append ( u . name )
if users :
2005-02-15 14:57:57 +01:00
utils . sortBy ( str . lower , users )
2005-01-31 15:52:27 +01:00
irc . reply ( format ( ' % L ' , users ) )
2005-01-19 14:14:38 +01:00
else :
if predicates :
2010-10-16 13:51:27 +02:00
irc . reply ( _ ( ' There are no matching registered users. ' ) )
2005-01-19 14:14:38 +01:00
else :
2010-10-16 13:51:27 +02:00
irc . reply ( _ ( ' There are no registered users. ' ) )
2005-01-19 14:14:38 +01:00
list = wrap ( list , [ getopts ( { ' capability ' : ' capability ' } ) ,
additional ( ' glob ' ) ] )
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def register ( self , irc , msg , args , name , password ) :
""" <name> <password>
Registers < name > with the given password < password > and the current
hostmask of the person registering . You shouldn ' t register twice; if
you ' re not recognized as a user but you ' ve already registered , use the
2005-05-27 18:37:22 +02:00
hostmask add command to add another hostmask to your already - registered
2005-01-19 14:14:38 +01:00
user , or use the identify command to identify just for a session .
This command ( and all other commands that include a password ) must be
sent to the bot privately , not in a channel .
"""
addHostmask = True
try :
ircdb . users . getUserId ( name )
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' That name is already assigned to someone. ' ) ,
Raise = True )
2005-01-19 14:14:38 +01:00
except KeyError :
pass
if ircutils . isUserHostmask ( name ) :
2010-10-16 13:51:27 +02:00
irc . errorInvalid ( _ ( ' username ' ) , name ,
_ ( ' Hostmasks are not valid usernames. ' ) ,
Raise = True )
2005-01-19 14:14:38 +01:00
try :
u = ircdb . users . getUser ( msg . prefix )
if u . _checkCapability ( ' owner ' ) :
addHostmask = False
else :
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' Your hostmask is already registered to %s ' ) %
u . name )
2005-01-19 14:14:38 +01:00
return
except KeyError :
pass
user = ircdb . users . newUser ( )
user . name = name
user . setPassword ( password )
if addHostmask :
user . addHostmask ( msg . prefix )
ircdb . users . setUser ( user )
irc . replySuccess ( )
register = wrap ( register , [ ' private ' , ' something ' , ' something ' ] )
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def unregister ( self , irc , msg , args , user , password ) :
""" <name> [<password>]
Unregisters < name > from the user database . If the user giving this
command is an owner user , the password is not necessary .
"""
try :
caller = ircdb . users . getUser ( msg . prefix )
isOwner = caller . _checkCapability ( ' owner ' )
except KeyError :
caller = None
isOwner = False
if not conf . supybot . databases . users . allowUnregistration ( ) :
if not caller or not isOwner :
self . log . warning ( ' %s tried to unregister user %s . ' ,
msg . prefix , user . name )
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' This command has been disabled. You \' ll have to '
' ask the owner of this bot to unregister your '
' user. ' ) , Raise = True )
2005-01-19 14:14:38 +01:00
if isOwner or user . checkPassword ( password ) :
ircdb . users . delUser ( user . id )
irc . replySuccess ( )
else :
irc . error ( conf . supybot . replies . incorrectAuthentication ( ) )
unregister = wrap ( unregister , [ ' private ' , ' otherUser ' ,
additional ( ' anything ' ) ] )
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def changename ( self , irc , msg , args , user , newname , password ) :
""" <name> <new name> [<password>]
Changes your current user database name to the new name given .
< password > is only necessary if the user isn ' t recognized by hostmask.
2010-08-29 16:49:13 +02:00
This message must be sent to the bot privately ( not on a channel ) since
it may contain a password .
2005-01-19 14:14:38 +01:00
"""
try :
id = ircdb . users . getUserId ( newname )
2010-10-16 13:51:27 +02:00
irc . error ( format ( _ ( ' % q is already registered. ' ) , newname ) )
2005-01-19 14:14:38 +01:00
return
except KeyError :
pass
if user . checkHostmask ( msg . prefix ) or user . checkPassword ( password ) :
user . name = newname
ircdb . users . setUser ( user )
irc . replySuccess ( )
changename = wrap ( changename , [ ' private ' , ' otherUser ' , ' something ' ,
additional ( ' something ' , ' ' ) ] )
2005-04-15 14:16:37 +02:00
class set ( callbacks . Commands ) :
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-04-15 14:16:37 +02:00
def password ( self , irc , msg , args , user , password , newpassword ) :
2010-08-29 17:10:54 +02:00
""" [<name>] <old password> <new password>
2005-04-15 14:16:37 +02:00
Sets the new password for the user specified by < name > to < new
password > . Obviously this message must be sent to the bot
privately ( not in a channel ) . If the requesting user is an owner
user ( and the user whose password is being changed isn ' t that same
owner user ) , then < old password > needn ' t be correct.
"""
2009-04-01 20:33:19 +02:00
try :
u = ircdb . users . getUser ( msg . prefix )
except KeyError :
u = None
2010-08-29 17:10:54 +02:00
if user is None :
if u is None :
irc . errorNotRegistered ( Raise = True )
user = u
2005-04-15 14:16:37 +02:00
if user . checkPassword ( password ) or \
2009-04-01 20:33:19 +02:00
( u and u . _checkCapability ( ' owner ' ) and not u == user ) :
2005-04-15 14:16:37 +02:00
user . setPassword ( newpassword )
ircdb . users . setUser ( user )
irc . replySuccess ( )
else :
irc . error ( conf . supybot . replies . incorrectAuthentication ( ) )
2010-08-29 17:10:54 +02:00
password = wrap ( password , [ ' private ' , optional ( ' otherUser ' ) ,
' something ' , ' something ' ] )
2005-04-15 14:16:37 +02:00
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-04-15 14:16:37 +02:00
def secure ( self , irc , msg , args , user , password , value ) :
""" <password> [<True|False>]
Sets the secure flag on the user of the person sending the message .
Requires that the person ' s hostmask be in the list of hostmasks for
that user in addition to the password being correct . When the
2014-07-13 17:23:11 +02:00
secure flag is set , the user * must * identify before they can be
2005-04-15 14:16:37 +02:00
recognized . If a specific True / False value is not given , it
inverts the current value .
"""
if value is None :
value = not user . secure
if user . checkPassword ( password ) and \
user . checkHostmask ( msg . prefix , useAuth = False ) :
user . secure = value
ircdb . users . setUser ( user )
2010-10-16 13:51:27 +02:00
irc . reply ( _ ( ' Secure flag set to %s ' ) % value )
2005-04-15 14:16:37 +02:00
else :
irc . error ( conf . supybot . replies . incorrectAuthentication ( ) )
secure = wrap ( secure , [ ' private ' , ' user ' , ' something ' ,
additional ( ' boolean ' ) ] )
2005-01-19 14:14:38 +01:00
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def username ( self , irc , msg , args , hostmask ) :
""" <hostmask|nick>
Returns the username of the user specified by < hostmask > or < nick > if
the user is registered .
"""
if ircutils . isNick ( hostmask ) :
try :
hostmask = irc . state . nickToHostmask ( hostmask )
except KeyError :
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' I haven \' t seen %s . ' ) % hostmask , Raise = True )
2005-01-19 14:14:38 +01:00
try :
user = ircdb . users . getUser ( hostmask )
irc . reply ( user . name )
except KeyError :
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' I don \' t know who that is. ' ) )
2005-01-19 14:14:38 +01:00
username = wrap ( username , [ first ( ' nick ' , ' hostmask ' ) ] )
2005-04-15 01:41:18 +02:00
class hostmask ( callbacks . Commands ) :
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-04-15 01:41:18 +02:00
def hostmask ( self , irc , msg , args , nick ) :
""" [<nick>]
2005-01-19 14:14:38 +01:00
2005-04-15 01:41:18 +02:00
Returns the hostmask of < nick > . If < nick > isn ' t given, return the
hostmask of the person giving the command .
"""
if not nick :
nick = msg . nick
irc . reply ( irc . state . nickToHostmask ( nick ) )
hostmask = wrap ( hostmask , [ additional ( ' seenNick ' ) ] )
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-04-15 01:41:18 +02:00
def list ( self , irc , msg , args , name ) :
""" [<name>]
Returns the hostmasks of the user specified by < name > ; if < name >
isn ' t specified, returns the hostmasks of the user calling the
command .
"""
def getHostmasks ( user ) :
2014-01-21 10:57:38 +01:00
hostmasks = list ( map ( repr , user . hostmasks ) )
2005-06-01 08:20:29 +02:00
if hostmasks :
hostmasks . sort ( )
return format ( ' % L ' , hostmasks )
else :
2010-10-16 13:51:27 +02:00
return format ( _ ( ' %s has no registered hostmasks. ' ) ,
user . name )
2005-04-15 01:41:18 +02:00
try :
user = ircdb . users . getUser ( msg . prefix )
if name :
if name != user . name and \
not ircdb . checkCapability ( msg . prefix , ' owner ' ) :
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' You may only retrieve your own '
2011-06-25 13:06:06 +02:00
' hostmasks. ' ) , Raise = True )
2005-04-15 01:41:18 +02:00
else :
try :
user = ircdb . users . getUser ( name )
irc . reply ( getHostmasks ( user ) )
except KeyError :
irc . errorNoUser ( )
else :
irc . reply ( getHostmasks ( user ) )
except KeyError :
irc . errorNotRegistered ( )
list = wrap ( list , [ ' private ' , additional ( ' something ' ) ] )
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-04-15 01:41:18 +02:00
def add ( self , irc , msg , args , user , hostmask , password ) :
""" [<name>] [<hostmask>] [<password>]
Adds the hostmask < hostmask > to the user specified by < name > . The
< password > may only be required if the user is not recognized by
2009-04-08 22:55:40 +02:00
hostmask . < password > is also not required if an owner user is
giving the command on behalf of some other user . If < hostmask > is
not given , it defaults to your current hostmask . If < name > is not
given , it defaults to your currently identified name . This message
must be sent to the bot privately ( not on a channel ) since it may
contain a password .
2005-04-15 01:41:18 +02:00
"""
if not hostmask :
hostmask = msg . prefix
if not ircutils . isUserHostmask ( hostmask ) :
2010-10-16 13:51:27 +02:00
irc . errorInvalid ( _ ( ' hostmask ' ) , hostmask ,
_ ( ' Make sure your hostmask includes a nick, '
2005-04-15 01:41:18 +02:00
' then an exclamation point (!), then a user, '
' then an at symbol (@), then a host. Feel '
' free to use wildcards (* and ?, which work '
' just like they do on the command line) in '
2010-10-16 13:51:27 +02:00
' any of these parts. ' ) ,
2005-04-15 01:41:18 +02:00
Raise = True )
try :
otherId = ircdb . users . getUserId ( hostmask )
if otherId != user . id :
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' That hostmask is already registered. ' ) ,
2005-01-19 14:14:38 +01:00
Raise = True )
2005-04-15 01:41:18 +02:00
except KeyError :
pass
if not user . checkPassword ( password ) and \
not user . checkHostmask ( msg . prefix ) :
try :
u = ircdb . users . getUser ( msg . prefix )
except KeyError :
irc . error ( conf . supybot . replies . incorrectAuthentication ( ) ,
Raise = True )
if not u . _checkCapability ( ' owner ' ) :
irc . error ( conf . supybot . replies . incorrectAuthentication ( ) ,
Raise = True )
try :
user . addHostmask ( hostmask )
2014-01-20 15:49:15 +01:00
except ValueError as e :
2005-04-15 01:41:18 +02:00
irc . error ( str ( e ) , Raise = True )
try :
ircdb . users . setUser ( user )
2010-08-29 17:24:54 +02:00
except ircdb . DuplicateHostmask :
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' That hostmask is already registered. ' ) ,
Raise = True )
2014-03-05 14:14:36 +01:00
except ValueError as e :
irc . error ( str ( e ) , Raise = True )
2005-04-15 01:41:18 +02:00
irc . replySuccess ( )
2009-04-08 22:55:40 +02:00
add = wrap ( add , [ ' private ' , first ( ' otherUser ' , ' user ' ) ,
optional ( ' something ' ) , additional ( ' something ' , ' ' ) ] )
2005-04-15 01:41:18 +02:00
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-04-15 01:41:18 +02:00
def remove ( self , irc , msg , args , user , hostmask , password ) :
2013-08-10 16:01:25 +02:00
""" [<name>] [<hostmask>] [<password>]
2005-04-15 01:41:18 +02:00
Removes the hostmask < hostmask > from the record of the user
specified by < name > . If the hostmask given is ' all ' then all
hostmasks will be removed . The < password > may only be required if
2013-03-08 20:34:31 +01:00
the user is not recognized by their hostmask . This message must be
2009-04-08 22:55:40 +02:00
sent to the bot privately ( not on a channel ) since it may contain a
2013-08-10 16:01:25 +02:00
password . If < hostmask > is
not given , it defaults to your current hostmask . If < name > is not
2014-03-22 14:28:09 +01:00
given , it defaults to your currently identified name .
2005-04-15 01:41:18 +02:00
"""
2013-08-10 16:01:25 +02:00
if not hostmask :
hostmask = msg . prefix
2005-04-15 01:41:18 +02:00
if not user . checkPassword ( password ) and \
not user . checkHostmask ( msg . prefix ) :
u = ircdb . users . getUser ( msg . prefix )
if not u . _checkCapability ( ' owner ' ) :
irc . error ( conf . supybot . replies . incorrectAuthentication ( ) )
return
try :
s = ' '
if hostmask == ' all ' :
user . hostmasks . clear ( )
2010-10-16 13:51:27 +02:00
s = _ ( ' All hostmasks removed. ' )
2005-01-19 14:14:38 +01:00
else :
2005-04-15 01:41:18 +02:00
user . removeHostmask ( hostmask )
except KeyError :
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' There was no such hostmask. ' ) )
2005-04-15 01:41:18 +02:00
return
ircdb . users . setUser ( user )
irc . replySuccess ( s )
2013-08-10 16:01:25 +02:00
remove = wrap ( remove , [ ' private ' , first ( ' otherUser ' , ' user ' ) ,
optional ( ' something ' ) , additional ( ' something ' , ' ' ) ] )
2005-01-19 14:14:38 +01:00
2014-03-25 15:29:32 +01:00
def callCommand ( self , command , irc , msg , * args , * * kwargs ) :
if command [ 0 ] != ' gpg ' or \
( gpg . available and self . registryValue ( ' gpg.enable ' ) ) :
return super ( User , self ) \
. callCommand ( command , irc , msg , * args , * * kwargs )
else :
irc . error ( _ ( ' GPG features are not enabled. ' ) )
2013-01-20 00:09:37 +01:00
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def capabilities ( self , irc , msg , args , user ) :
""" [<name>]
Returns the capabilities of the user specified by < name > ; if < name >
2008-09-11 17:56:58 +02:00
isn ' t specified, returns the capabilities of the user calling the
command .
2005-01-19 14:14:38 +01:00
"""
2005-02-19 01:23:34 +01:00
try :
u = ircdb . users . getUser ( msg . prefix )
except KeyError :
irc . errorNotRegistered ( )
else :
2011-08-07 11:59:19 +02:00
if u == user or u . _checkCapability ( ' admin ' ) :
2005-02-19 01:23:34 +01:00
irc . reply ( ' [ %s ] ' % ' ; ' . join ( user . capabilities ) , private = True )
else :
irc . error ( conf . supybot . replies . incorrectAuthentication ( ) ,
Raise = True )
2005-01-19 14:14:38 +01:00
capabilities = wrap ( capabilities , [ first ( ' otherUser ' , ' user ' ) ] )
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def identify ( self , irc , msg , args , user , password ) :
""" <name> <password>
Identifies the user as < name > . This command ( and all other
commands that include a password ) must be sent to the bot privately ,
not in a channel .
"""
if user . checkPassword ( password ) :
try :
user . addAuth ( msg . prefix )
2005-03-27 11:59:44 +02:00
ircdb . users . setUser ( user , flush = False )
2005-01-19 14:14:38 +01:00
irc . replySuccess ( )
except ValueError :
2010-10-16 13:51:27 +02:00
irc . error ( _ ( ' Your secure flag is true and your hostmask '
' doesn \' t match any of your known hostmasks. ' ) )
2005-01-19 14:14:38 +01:00
else :
self . log . warning ( ' Failed identification attempt by %s (password '
' did not match for %s ). ' , msg . prefix , user . name )
irc . error ( conf . supybot . replies . incorrectAuthentication ( ) )
identify = wrap ( identify , [ ' private ' , ' otherUser ' , ' something ' ] )
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def unidentify ( self , irc , msg , args , user ) :
""" takes no arguments
Un - identifies you . Note that this may not result in the desired
effect of causing the bot not to recognize you anymore , since you may
have added hostmasks to your user that can cause the bot to continue to
recognize you .
"""
user . clearAuth ( )
ircdb . users . setUser ( user )
2010-10-30 11:23:56 +02:00
irc . replySuccess ( _ ( ' If you remain recognized after giving this command, '
2005-01-19 14:14:38 +01:00
' you \' re being recognized by hostmask, rather than '
' by password. You must remove whatever hostmask is '
' causing you to be recognized in order not to be '
2010-10-30 11:23:56 +02:00
' recognized. ' ) )
2005-01-19 14:14:38 +01:00
unidentify = wrap ( unidentify , [ ' user ' ] )
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def whoami ( self , irc , msg , args ) :
""" takes no arguments
Returns the name of the user calling the command .
"""
try :
user = ircdb . users . getUser ( msg . prefix )
irc . reply ( user . name )
except KeyError :
2014-08-09 17:35:10 +02:00
irc . reply ( _ ( ' I don \' t recognize you. You can message me either of these two commands: " user identify <username> <password> " to log in or " user register <username> <password> " to register. ' ) )
2005-01-19 14:14:38 +01:00
whoami = wrap ( whoami )
2010-10-16 13:51:27 +02:00
@internationalizeDocstring
2005-01-19 14:14:38 +01:00
def stats ( self , irc , msg , args ) :
""" takes no arguments
Returns some statistics on the user database .
"""
users = 0
owners = 0
admins = 0
hostmasks = 0
2015-08-08 22:20:14 +02:00
for user in ircdb . users . values ( ) :
2005-01-19 14:14:38 +01:00
users + = 1
hostmasks + = len ( user . hostmasks )
try :
if user . _checkCapability ( ' owner ' ) :
owners + = 1
elif user . _checkCapability ( ' admin ' ) :
admins + = 1
except KeyError :
pass
2010-10-16 13:51:27 +02:00
irc . reply ( format ( _ ( ' I have %s registered users '
2005-01-29 22:24:17 +01:00
' with %s registered hostmasks; '
2010-10-16 13:51:27 +02:00
' % n and % n. ' ) ,
2005-01-29 22:24:17 +01:00
users , hostmasks ,
( owners , ' owner ' ) , ( admins , ' admin ' ) ) )
2005-01-19 14:14:38 +01:00
stats = wrap ( stats )
Class = User
2006-02-11 16:52:51 +01:00
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
2005-01-19 14:14:38 +01:00