Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
Settings
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Mattia Rizzolo
Settings
Commits
feb7f63f
Commit
feb7f63f
authored
Jan 13, 2014
by
Mattia Rizzolo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update chanserver-mod to the latest version
parent
fb2433fa
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
455 additions
and
520 deletions
+455
-520
xchat2/chanserv-mod.py
xchat2/chanserv-mod.py
+455
-520
No files found.
xchat2/chanserv-mod.py
View file @
feb7f63f
# Simple chanserv helper script for Xchat
# Simple chanserv helper script for Xchat
# (c) 2006,2007 Dennis Kaarsemaker
# (c) 2006-2012 Dennis Kaarsemaker
#
# Latest version can be found on http://github.com/seveas/chanserv.py
#
#
# Latest version can be found on http://www.kaarsemaker.net/software/
#
# This script is free software; you can redistribute it and/or
# This script is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# modify it under the terms of the GNU General Public License
# version 3, as published by the Free Software Foundation.
# version 3, as published by the Free Software Foundation.
#
#
# This program is distributed in the hope that it will be useful,
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License along
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Usage instructions:
# Usage instructions:
# Place in ~/.xchat2 for it to be autoloaded
# - Place in ~/.xchat2 for it to be autoloaded
# - Use /py load chanserv.py if you already started X-chat
# - Connect to freenode, if not connected (other networks will not work)
#
#
# It adds one command to xchat: /cs
# It adds one command to xchat: /cs
# /cs understands the following arguments
# /cs understands the following arguments
# o or op - Let chanserv op you/others (/cs op, /cs op somenick)
#
# To give/take ops/voice:
#
# o or op - Let chanserv op you/others (/cs op, /cs op nick)
# v or voice - Let chanserv give you/others voice
# v or voice - Let chanserv give you/others voice
# d or deop - Let chanserv deop you/others (/cs deop, /cs deop somenick)
# d or deop - Let chanserv deop you/others (/cs deop, /cs deop nick)
# dv or devoice - Let chanserv decoice you/others (/cs devoice, /cs devoice somenick)
# dv or devoice - Let chanserv decoice you/others (/cs devoice, /cs devoice nick)
# k or kick - Op, kick, deop (/cs kick somenick [comment])
#
# b or ban - Op, ban, deop (/cs ban somenick)
# To op yourself, perform an action, and deop:
# f or forward - Ban a user with a forward (/cs forward nick chan)
#
# n or nameban - GECOS ban (/cs nameban nick)
# k or kick - Kick a user, possibly with comment (/cs kick nick [comment])
# m or mute - Op, mute, deop (/cs mute somenick)
# b or ban - Ban a user (/cs ban [-nihar] nick)
# kb or kickban - Op, kickban, deop (/cs kb somenick [comment])
# kb or kickban - Kick and ban a user (/cs ban [-nihar] nick)
# kf or kickforward - Same as kb, but with a forward (/cs kf somenick channel [comment])
# f or forward - Ban a user with a forward (/cs forward [-nihar] nick chan)
# kn or kicknameban - Same as kb, but with a GECOS ban (/cs kn somenick [comment])
# kf or kickforward - Kickban a user with a forward (/cs forward [-nihar] nick chan)
# l or lart - A combination of kick, ban, nameban, ident ban and nick ban.
# m or mute - Mute a user (/cs mute [-nihar] nick)
# u or unban - Op, unban, deop (/cs u somenick)
# l or lart - A combination of kick and ban on all fields
# t or topic - Op, set topic, deop (/cs t New topic here)
# u or unban - Remove all bans for a user (/cs u nick)
# m or mode - Op, change channel mode, deop (/cs mode modes here)
# t or topic - Set channel topic (/cs t New topic here)
# i or invite - With nick as argument: op, invite, deop. Without nick: invite to a channel
# m or mode - Change channel mode (/cs mode modes here)
# bans - Show bans that apply to someone without removing them (/cs bans nick_or_mask)
# i or invite - Invite yourself or someone else (/cs invite [nick])
# bans - Show bans that apply to someone without removing them (/cs bans nick)
#
#
# * For (kick)ban and mute, it will use the ip-address or hostname instead of
# * Bans, forwards and mute take an extra optional argument that specifies
# the nickname, unless you give a complete mask as argument. This works even
# what should be banned: nickname, ident, host, account and/or realname.
# after a person left by using /whowas. /whowas generally works up to a few
# /cs ban -nah nick -- Ban nick, account and host
# hours after someone left.
# /cs forward -nihra nick #somewhere -- Forward all
#
# * These commands also take an extra argument to specify when bans/mutes
# should be lifted automatically.
# /cs ban -t600 nick -- Ban nick for 10 minutes
# /cs ban -nah -t3600 -- Ban nick, account and hostname for an hour
#
#
# * Unban will remove all bans matching the nick or mask you give as argument
# * Unban will remove all bans matching the nick or mask you give as argument
# (* and ? wildcards work)
# (* and ? wildcards work)
# * It won't actually kick, but use the /remove command
# * It won't actually kick, but use the /remove command
# * Script is made to work on Freenode, may need changes to work on other
# networks
# * The -n argument before any of the commands (eg: /cs -n m foo) will make you
# stay opped after the act
#
# Apart from the /cs command it also adds automatic rejoining magic. When you
# are /remove'd from a channel, it will automatically rejoin (X-chat already can
# do that for you if kicked). When attempting to (re)join a channel which is
# invite-only, has a key set or where you are banned, the script will poke
# chanserv to let you in and will automatically let you in if ChanServ helps
#
#
# Changelog of latest versions
# The following additional features are implemented
# 1.0: - Use xchat.get_info for getting the xchat dir
# - Autorejoin for /remove
# - If a nick in /cs u starts with 2 asterisks, a non-looked up nameban
# - Auto-unmute when muted
# removal will be tried. This was the last item in the todo, so this is
# - Auto-unban via chanserv
# 1.0
# - Auto-invite via chanserv
# 1.0.1: - Add voice/devoice
# - Auto-getkey via chanserv
# 1.0.2: - Fix complete mask detection
# - Fixed downloader
# 1.0.3 - Added /cs bans -- determine which bans apply to someone without removing them
# 1.0.4 - Update download link and allow a comment in /cs lart
# 1.0.5 - Don't require a comment in /cs lart
__module_name__
=
"chanserv"
__module_name__
=
"chanserv"
__module_version__
=
"
1.0.5
"
__module_version__
=
"
2.2.4
"
__module_description__
=
"Chanserv helper"
__module_description__
=
"Chanserv helper"
import
collections
import
xchat
import
xchat
import
time
import
time
import
re
import
re
import
os
# Event queue
# Event queue
pending
=
[]
pending
=
[]
# /whois cache
# /whois cache
users
=
{}
users
=
{}
# /mode =bd 'cache'
# /mode bq 'cache'
bans
=
{}
bans
=
collections
.
defaultdict
(
list
)
_bans
=
{}
quiets
=
collections
.
defaultdict
(
list
)
# channel modes
collecting_bans
=
[]
modes
=
{}
abbreviations
=
{
'kick'
:
'k'
,
'ban'
:
'b'
,
'kickban'
:
'kb'
,
'forward'
:
'f'
,
intercept_akick
=
False
'kickforward'
:
'kf'
,
'mute'
:
'm'
,
'topic'
:
't'
,
'unban'
:
'u'
,
'mode'
:
'm'
,
'invite'
:
'i'
,
'op'
:
'o'
,
'deop'
:
'd'
,
'lart'
:
'l'
,
KICK
,
BAN
,
MUTE
,
KICKBAN
,
UNBAN
,
TOPIC
,
MODE
,
NAMEBAN
,
KICKNAMEBAN
,
LART
,
INVITE
,
BANS
,
XBAN
=
range
(
13
)
'voice'
:
'v'
,
'devoice'
:
'dv'
,
'bans'
:
'bans'
}
expansions
=
dict
([
x
[::
-
1
]
for
x
in
abbreviations
.
items
()])
simple_commands
=
[
'op'
,
'deop'
,
'voice'
,
'devoice'
]
kick_commands
=
[
'kick'
,
'kickforward'
,
'kickban'
,
'lart'
]
forward_commands
=
[
'kickforward'
,
'forward'
]
ban_commands
=
[
'ban'
,
'forward'
,
'mute'
,
'lart'
,
'kickban'
,
'kickforward'
]
simple_commands
+=
[
abbreviations
[
x
]
for
x
in
simple_commands
]
kick_commands
+=
[
abbreviations
[
x
]
for
x
in
kick_commands
]
ban_commands
+=
[
abbreviations
[
x
]
for
x
in
ban_commands
]
forward_commands
+=
[
abbreviations
[
x
]
for
x
in
forward_commands
]
all_commands
=
abbreviations
.
keys
()
+
abbreviations
.
values
()
ban_sentinel
=
'!'
debug
=
os
.
path
.
exists
(
os
.
path
.
join
(
xchat
.
get_info
(
'xchatdir'
),
'chanserv.py-debug'
))
# Main /cs command
def
cs
(
word
,
word_eol
,
userdata
):
def
cs
(
word
,
word_eol
,
userdata
):
chan
=
xchat
.
get_info
(
'channel'
)
"""Main command dispatcher"""
me
=
xchat
.
get_info
(
'nick'
)
ctx
=
xchat
.
get_context
()
deop
=
True
if
len
(
word
)
==
1
:
if
len
(
word
)
==
1
:
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
command
=
word
[
1
].
lower
()
if
command
not
in
all_commands
:
return
xchat
.
EAT_NONE
args
=
dict
(
enumerate
(
word_eol
[
2
:]))
me
=
xchat
.
get_info
(
'nick'
)
action
=
Action
(
channel
=
xchat
.
get_info
(
'channel'
),
me
=
me
,
context
=
xchat
.
get_context
())
# The simple ones: op/voice
if
command
in
simple_commands
:
action
.
target
=
args
.
get
(
0
,
me
)
action
.
deop
=
(
action
.
target
!=
me
)
action
.
needs_op
=
False
command
=
expansions
.
get
(
command
,
command
)
action
.
actions
.
append
(
'chanserv %s %%(channel)s %%(target_nick)s'
%
command
)
return
action
.
schedule
()
# Usage check
if
len
(
word
)
<
3
:
if
command
in
all_commands
:
xchat
.
emit_print
(
"Server Error"
,
"Not enough arguments for %s"
%
command
)
return
xchat
.
EAT_ALL
return
xchat
.
EAT_NONE
if
command
in
(
't'
,
'topic'
):
action
.
actions
.
append
(
'chanserv TOPIC %%(channel)s %s'
%
args
[
0
])
action
.
needs_op
=
False
return
action
.
schedule
()
if
command
in
(
'm'
,
'mode'
)
and
args
[
0
][
0
]
in
'+=-'
:
action
.
actions
.
append
(
'MODE %%(channel)s %s'
%
args
[
0
])
return
action
.
schedule
()
if
command
in
(
'i'
,
'invite'
):
target
=
args
[
0
]
if
target
.
startswith
(
'#'
):
action
.
needs_op
=
False
action
.
actions
.
append
(
'chanserv INVITE %s'
%
target
)
else
:
if
target
.
lower
()
in
[
x
.
nick
.
lower
()
for
x
in
action
.
context
.
get_list
(
'users'
)]:
xchat
.
emit_print
(
"Server Error"
,
"%s is already in %s"
%
(
target
,
action
.
channel
))
return
xchat
.
EAT_ALL
action
.
actions
.
append
(
'INVITE %s %%(channel)s'
%
target
)
return
action
.
schedule
()
if
word
[
1
]
==
'-n'
:
# Kick/ban/forward/mute handling
deop
=
False
if
len
(
word
)
<
4
and
command
in
forward_commands
:
word
.
remove
(
'-n'
)
xchat
.
emit_print
(
"Server Error"
,
"Not enough arguments for %s"
%
command
)
for
w
in
word_eol
:
if
w
.
strip
().
startswith
(
'-n'
):
word_eol
.
remove
(
w
)
break
comm
=
word
[
1
].
lower
()
if
comm
in
[
'o'
,
'op'
]:
word_eol
.
append
(
''
)
if
me
in
word_eol
[
2
]
or
word_eol
[
2
]
==
''
:
for
p
in
pending
:
if
p
.
channel
==
chan
:
p
.
deop
=
False
xchat
.
command
(
'chanserv OP %s %s'
%
(
chan
,
word_eol
[
2
]))
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
if
comm
in
[
'd'
,
'deop'
]:
# Command dispatch
if
len
(
word
)
<
3
:
word
.
append
(
me
)
# Check for -nihra argument
if
me
in
word
[
2
:]:
if
command
in
ban_commands
:
for
p
in
pending
:
args_start
=
3
if
p
.
channel
==
chan
:
while
args
[
0
].
startswith
(
'-'
):
p
.
deop
=
True
if
args
[
0
].
startswith
(
'-t'
):
xchat
.
command
(
'chanserv OP %s %s'
%
(
chan
,
' '
.
join
(
map
(
lambda
x
:
'-'
+
x
,
word
[
2
:]))))
try
:
return
xchat
.
EAT_ALL
action
.
timer
=
int
(
args
[
0
][
2
:].
split
(
None
,
1
)[
0
])
except
ValueError
:
pass
else
:
action
.
bans
=
args
[
0
][
1
:].
split
(
None
,
1
)[
0
]
args
=
dict
(
enumerate
(
word_eol
[
args_start
:]))
args_start
+=
1
if
command
in
(
'lart'
,
'l'
):
action
.
bans
=
'nihra'
if
comm
in
[
'v'
,
'voice'
]:
# Set target
word_eol
.
append
(
''
)
action
.
target
=
args
[
0
].
split
(
None
,
1
)[
0
]
xchat
.
command
(
'chanserv VOICE %s %s'
%
(
chan
,
word_eol
[
2
]))
return
xchat
.
EAT_ALL
if
comm
in
[
'dv'
,
'devoice'
]:
if
not
valid_nickname
(
action
.
target
)
and
not
valid_mask
(
action
.
target
):
if
len
(
word
)
<
3
:
word
.
append
(
me
)
xchat
.
emit_print
(
"Server Error"
,
"Invalid target: %s"
%
action
.
target
)
xchat
.
command
(
'chanserv VOICE %s %s'
%
(
chan
,
' '
.
join
(
map
(
lambda
x
:
'-'
+
x
,
word
[
2
:]))))
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
if
comm
in
[
'k'
,
'kick'
]:
if
action
.
bans
and
not
valid_nickname
(
action
.
target
):
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
xchat
.
emit_print
(
"Server Error"
,
"Ban types and lart can only be used with nicks, not with complete masks"
)
if
len
(
word
)
<
4
:
word_eol
.
append
(
''
)
if
word
[
2
].
lower
()
not
in
[
x
.
nick
.
lower
()
for
x
in
ctx
.
get_list
(
'users'
)]:
xchat
.
emit_print
(
"Server Error"
,
"%s is not in %s"
%
(
word
[
2
],
ctx
.
get_info
(
'channel'
)))
return
xchat
.
EAT_ALL
schedule
(
Action
(
ctx
,
KICK
,
word
[
2
],
word_eol
[
3
]),
deop
)
return
xchat
.
EAT_ALL
if
comm
in
[
'b'
,
'ban'
]:
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
schedule
(
Action
(
ctx
,
BAN
,
word
[
2
]),
deop
)
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
if
comm
in
[
'x'
,
'xban'
]:
if
valid_mask
(
action
.
target
):
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
action
.
bans
=
'f'
schedule
(
Action
(
ctx
,
XBAN
,
word
[
2
]),
deop
)
return
xchat
.
EAT_ALL
if
comm
in
[
'n'
,
'nameban'
]:
if
not
action
.
bans
:
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
action
.
bans
=
'h'
schedule
(
Action
(
ctx
,
NAMEBAN
,
word
[
2
]),
deop
)
return
xchat
.
EAT_ALL
if
comm
in
[
'f'
,
'forward'
]:
# Find forward channel
if
len
(
word
)
<
4
:
return
xchat
.
EAT_ALL
if
command
in
forward_commands
:
if
word
[
3
][
0
]
!=
'#'
:
action
.
forward_to
=
'$'
+
args
[
1
].
split
(
None
,
1
)[
0
]
# Kludge
xchat
.
emit_print
(
"Server Error"
,
"You can only forward to a channel"
);
if
not
valid_channel
(
action
.
forward_to
[
1
:]):
xchat
.
emit_print
(
"Server Error"
,
"Invalid channel: %s"
%
action
.
forward_to
[
1
:])
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
schedule
(
Action
(
ctx
,
BAN
,
word
[
2
],
forward_channel
=
word
[
3
]),
deop
)
return
xchat
.
EAT_ALL
# Check if target is there and schedule kick
if
command
in
kick_commands
:
if
comm
in
[
'm'
,
'mute'
]:
if
action
.
target
.
lower
()
not
in
[
x
.
nick
.
lower
()
for
x
in
action
.
context
.
get_list
(
'users'
)]:
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
xchat
.
emit_print
(
"Server Error"
,
"%s is not in %s"
%
(
action
.
target
,
action
.
channel
))
if
word
[
2
][
0
]
not
in
"+-="
:
schedule
(
Action
(
ctx
,
MUTE
,
word
[
2
]),
deop
)
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
action
.
reason
=
args
.
get
(
1
,
'Goodbye'
)
action
.
actions
.
append
(
'remove %(channel)s %(target_nick)s :%(reason)s'
)
if
comm
in
[
'kb'
,
'kickban'
]:
if
command
in
(
'm'
,
'mute'
):
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
action
.
banmode
=
'q'
if
len
(
word
)
<
4
:
word_eol
.
append
(
''
)
schedule
(
Action
(
ctx
,
KICKBAN
,
word
[
2
],
word_eol
[
3
]),
deop
)
return
xchat
.
EAT_ALL
if
comm
in
[
'kn'
,
'kicknameban'
]:
if
command
in
ban_commands
:
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
action
.
do_ban
=
True
if
len
(
word
)
<
4
:
word_eol
.
append
(
''
)
if
'n'
in
action
.
bans
:
action
.
actions
.
append
(
'mode %(channel)s +%(banmode)s %(target_nick)s!*@*%(forward_to)s'
)
schedule
(
Action
(
ctx
,
KICKNAMEBAN
,
word
[
2
],
word_eol
[
3
]),
deop
)
if
'i'
in
action
.
bans
:
action
.
actions
.
append
(
'mode %(channel)s +%(banmode)s *!%(target_ident)s@*%(forward_to)s'
)
return
xchat
.
EAT_ALL
if
'h'
in
action
.
bans
:
action
.
actions
.
append
(
'mode %(channel)s +%(banmode)s *!*@%(target_host)s%(forward_to)s'
)
if
'r'
in
action
.
bans
:
action
.
actions
.
append
(
'mode %(channel)s +%(banmode)s $r:%(target_name_bannable)s%(forward_to)s'
)
if
'a'
in
action
.
bans
:
action
.
actions
.
append
(
'mode %(channel)s +%(banmode)s $a:%(target_account)s%(forward_to)s'
)
if
'f'
in
action
.
bans
:
action
.
actions
.
append
(
'mode %(channel)s +%(banmode)s %(target)s%(forward_to)s'
)
if
comm
in
[
'kf'
,
'kickforward'
]:
if
command
in
(
'u'
,
'unban'
):
if
len
(
word
)
<
4
:
return
xchat
.
EAT_ALL
action
.
do_unban
=
True
if
word
[
3
][
0
]
!=
'#'
:
xchat
.
emit_print
(
"Server Error"
,
"You can only forward to a channel"
);
if
command
==
'bans'
:
action
.
do_bans
=
True
action
.
needs_op
=
False
return
action
.
schedule
()
xchat
.
hook_command
(
'cs'
,
cs
,
"For help with /cs, please read the comments in the script"
)
class
Action
(
object
):
"""A list of actions to do, and information needed for them"""
def
__init__
(
self
,
channel
,
me
,
context
):
self
.
channel
=
channel
self
.
me
=
me
self
.
context
=
context
self
.
stamp
=
time
.
time
()
# Defaults
self
.
deop
=
True
self
.
needs_op
=
True
self
.
do_ban
=
self
.
do_unban
=
self
.
do_bans
=
False
self
.
banmode
=
'b'
self
.
reason
=
''
self
.
bans
=
''
self
.
actions
=
[]
self
.
resolved
=
True
self
.
target
=
''
self
.
forward_to
=
''
self
.
timer
=
0
def
__str__
(
self
):
ctx
=
{
'channel'
:
self
.
channel
,
'target'
:
self
.
target
}
if
hasattr
(
self
,
'target_ident'
):
ctx
[
'target'
]
=
'%s!%s@%s (r: %s a: %s)'
%
(
self
.
target_nick
,
self
.
target_ident
,
self
.
target_host
,
self
.
target_name
,
self
.
target_account
)
ctx
[
'actions'
]
=
' | '
.
join
(
self
.
actions
)
return
"C: %(channel)s T: %(target)s A: %(actions)s"
%
ctx
def
schedule
(
self
,
update_stamp
=
False
):
"""Request information and add ourselves to the queue"""
if
debug
:
xchat
.
emit_print
(
'Server Text'
,
"Scheduling "
+
str
(
self
))
if
update_stamp
:
self
.
stamp
=
time
.
time
()
pending
.
append
(
self
)
# Am I opped?
self
.
am_op
=
False
for
user
in
self
.
context
.
get_list
(
'users'
):
if
user
.
nick
==
self
.
me
and
user
.
prefix
==
'@'
:
self
.
am_op
=
True
self
.
deop
=
False
if
self
.
needs_op
and
not
self
.
am_op
:
self
.
context
.
command
(
"chanserv op %s"
%
self
.
channel
)
# Find needed information
if
(
'a'
in
self
.
bans
or
'r'
in
self
.
bans
)
and
valid_mask
(
self
.
target
)
and
not
self
.
target
.
startswith
(
'$'
):
xchat
.
emit_print
(
'Server Error'
,
"Invalid argument %s for account/realname ban"
%
self
.
target
)
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
if
len
(
word
)
<
5
:
word_eol
.
append
(
''
)
if
self
.
do_ban
or
self
.
do_unban
or
self
.
do_bans
:
schedule
(
Action
(
ctx
,
KICKBAN
,
word
[
2
],
word_eol
[
4
],
word
[
3
]),
deop
)
self
.
resolve_nick
()
return
xchat
.
EAT_ALL
if
comm
in
[
'u'
,
'unban'
]:
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
if
word
[
2
].
startswith
(
'**'
):
schedule
(
Action
(
ctx
,
UNBAN
,
word_eol
[
2
]),
deop
)
else
:
else
:
schedule
(
Action
(
ctx
,
UNBAN
,
word
[
2
]),
deop
)
self
.
target_nick
=
self
.
target
return
xchat
.
EAT_ALL
if
comm
in
[
'l'
,
'lart'
]:
if
self
.
do_unban
or
self
.
do_bans
:
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
self
.
fetch_bans
()
if
len
(
word_eol
)
<
4
:
word_eol
.
append
(
''
)
schedule
(
Action
(
ctx
,
LART
,
word
[
2
],
word_eol
[
3
]),
deop
)
return
xchat
.
EAT_ALL
if
comm
in
[
't'
,
'topic'
]:
run_pending
()
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
schedule
(
Action
(
ctx
,
TOPIC
,
word_eol
[
2
]),
deop
)
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
if
comm
in
[
'm'
,
'mode'
]:
def
resolve_nick
(
self
,
request
=
True
):
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
"""Try to find nickname, ident and host"""
schedule
(
Action
(
ctx
,
MODE
,
word_eol
[
2
]),
deop
)
self
.
target_nick
=
None
return
xchat
.
EAT_ALL
self
.
target_ident
=
None
self
.
target_host
=
None
self
.
target_name
=
None
self
.
target_account
=
None
self
.
resolved
=
False
if
valid_mask
(
self
.
target
):
if
self
.
target
.
startswith
(
'$a:'
):
self
.
target_account
=
self
.
target
[
3
:]
elif
self
.
target
.
startswith
(
'$r:'
):
self
.
target_name
=
self
.
target
[
3
:]
else
:
self
.
target_nick
,
self
.
target_mask
,
self
.
target_host
=
re
.
split
(
'[!@]'
,
self
.
target
)
self
.
resolved
=
True
return
if
comm
in
[
'i'
,
'invite'
]:
self
.
target_nick
=
self
.
target
.
lower
()
if
len
(
word
)
<
3
:
return
xchat
.
EAT_ALL
if
self
.
target_nick
in
users
:
if
word
[
2
][
0
]
==
'#'
:
if
users
[
self
.
target_nick
].
time
<
time
.
time
()
-
10
:
xchat
.
command
(
'chanserv INVITE %s'
%
(
word
[
2
]))
del
users
[
self
.
target_nick
]
if
request
:
self
.
context
.
command
(
'whois %s'
%
self
.
target_nick
)
else
:
self
.
target_ident
=
users
[
self
.
target_nick
].
ident
self
.
target_host
=
users
[
self
.
target_nick
].
host
self
.
target_name
=
users
[
self
.
target_nick
].
name
self
.
target_name_bannable
=
re
.
sub
(
'[^a-zA-Z0-9]'
,
'?'
,
self
.
target_name
)
self
.
target_account
=
users
[
self
.
target_nick
].
account
self
.
resolved
=
True
if
'gateway/'
in
self
.
target_host
and
self
.
bans
==
'h'
and
self
.
do_ban
:
# For gateway/* users, default to ident ban
self
.
actions
.
append
(
'mode %(channel)s +%(banmode)s *!%(target_ident)s@gateway/*%(forward_to)s'
)
self
.
actions
.
remove
(
'mode %(channel)s +%(banmode)s *!*@%(target_host)s%(forward_to)s'
)
else
:
else
:
if
word
[
2
].
lower
()
in
[
x
.
nick
.
lower
()
for
x
in
ctx
.
get_list
(
'users'
)]:
if
request
:
xchat
.
emit_print
(
"Server Error"
,
"%s is already in %s"
%
(
word
[
2
],
ctx
.
get_info
(
'channel'
)))
self
.
context
.
command
(
'whois %s'
%
self
.
target_nick
)
return
xchat
.
EAT_ALL
schedule
(
Action
(
ctx
,
INVITE
,
word_eol
[
2
]),
deop
)
return
xchat
.
EAT_ALL
if
comm
==
'bans'
:
def
fetch_bans
(
self
):
if
len
(
word
)
<
3
:
word
.
append
(
me
)
"""Read bans for a channel"""
schedule
(
Action
(
ctx
,
BANS
,
word
[
2
]),
deop
)
bans
[
self
.
channel
]
=
[]
return
xchat
.
EAT_ALL
quiets
[
self
.
channel
]
=
[]
collecting_bans
.
append
(
self
.
channel
)
if
comm
in
[
'update'
]:
self
.
context
.
command
(
"mode %s +bq"
%
self
.
channel
)
import
thread
thread
.
start_new_thread
(
download
,
tuple
([]))
return
xchat
.
EAT_ALL
# /cs is an alias for chanserv too, so don't eat anything if we're not able
# to fulfill the request
return
xchat
.
EAT_NONE
xchat
.
hook_command
(
'cs'
,
cs
,
"For help with /cs, please read the comments in the script"
)
# Action class, quite powerful and extendable
class
Action
:
def
__init__
(
self
,
ctx
,
typ
,
arg
,
comment
=
''
,
forward_channel
=
''
):
self
.
ctx
=
ctx
self
.
typ
=
typ
self
.
arg
=
arg
self
.
nick
=
arg
.
lower
()
self
.
comment
=
comment
self
.
forward_channel
=
forward_channel
self
.
completemask
=
False
self
.
realname
=
''
self
.
mask
=
None
if
typ
in
[
MUTE
,
BAN
,
XBAN
,
KICKBAN
,
UNBAN
,
BANS
]:
if
self
.
nick
.
startswith
(
'**'
):
self
.
realname
=
self
.
nick
[
2
:]
self
.
nick
=
''
self
.
mask
=
(
''
,
''
)
if
'!'
in
self
.
nick
and
'@'
in
self
.
nick
and
self
.
nick
.
find
(
'!'
)
<
self
.
nick
.
find
(
'@'
):
self
.
nick
,
self
.
mask
=
self
.
nick
.
split
(
'!'
,
1
)
self
.
completemask
=
True
if
'@'
in
self
.
mask
:
self
.
mask
=
list
(
self
.
mask
.
split
(
'@'
,
1
))
self
.
channel
=
ctx
.
get_info
(
'channel'
)
self
.
stamp
=
time
.
time
()
def
run
(
self
):
def
run
(
self
):
# Now perform actions
"""Perform our actions"""
if
self
.
typ
==
TOPIC
:
if
debug
:
self
.
ctx
.
command
(
"TOPIC %s"
%
self
.
arg
)
xchat
.
emit_print
(
'Server Text'
,
"Running "
+
str
(
self
)
)
kwargs
=
dict
(
self
.
__dict__
.
items
())
if
self
.
typ
==
MODE
:
self
.
ctx
.
command
(
"MODE %s %s"
%
(
self
.
channel
,
self
.
arg
))
if
self
.
do_bans
:
xchat
.
emit_print
(
'Server Text'
,
"Bans matching %s!%s@%s (r:%s, a:%s)"
%
if
self
.
typ
==
INVITE
:
(
self
.
target_nick
,
self
.
target_ident
,
self
.
target_host
,
self
.
target_name
,
self
.
target_account
))
self
.
ctx
.
command
(
"INVITE %s"
%
(
self
.
arg
))
if
self
.
do_unban
or
self
.
do_bans
:
if
self
.
typ
==
UNBAN
:
for
b
in
bans
[
self
.
channel
]:
for
b
in
bans
[
self
.
channel
]:
if
self
.
match
(
b
):
if
self
.
match
(
b
):
if
'!'
in
b
and
'@'
in
b
:
if
self
.
do_bans
:
self
.
ctx
.
command
(
"MODE %s -b %s"
%
(
self
.
channel
,
b
)
)
xchat
.
emit_print
(
'Server Text'
,
b
)
else
:
else
:
self
.
ctx
.
command
(
"MODE %s -d :%s"
%
(
self
.
channel
,
b
))
self
.
actions
.
append
(
'mode %s -b %s'
%
(
self
.
channel
,
b
))
if
self
.
typ
==
BANS
:
for
b
in
quiets
[
self
.
channel
]:
xchat
.
emit_print
(
'Server Text'
,
"Bans matching %s!%s@%s (%s)"
%
(
self
.
nick
,
self
.
mask
[
0
],
self
.
mask
[
1
],
self
.
realname
))
for
b
in
bans
[
self
.
channel
]:
if
self
.
match
(
b
):
if
self
.
match
(
b
):
xchat
.
emit_print
(
'Server Text'
,
b
)
if
self
.
do_bans
:
xchat
.
emit_print
(
'Server Text'
,
b
+
' (quiet)'
)
if
self
.
typ
in
[
KICK
,
KICKBAN
,
KICKNAMEBAN
,
LART
]:
else
:
self
.
ctx
.
command
(
"REMOVE %s %s :%s"
%
(
self
.
channel
,
self
.
nick
,
self
.
comment
))
self
.
actions
.
append
(
'mode %s -q %s'
%
(
self
.
channel
,
b
))
if
self
.
typ
in
[
BAN
,
KICKBAN
,
MUTE
,
LART
]:
# Perform all registered actions
mode
=
'b'
for
action
in
self
.
actions
:
if
self
.
forward_channel
:
if
'%(target_account)s'
in
action
and
not
self
.
target_account
:
self
.
mask
[
1
]
+=
'!'
+
self
.
forward_channel
xchat
.
emit_print
(
'Server Text'
,
"Can't do an account ban for %s, not identified"
%
self
.
target_nick
)
if
self
.
typ
==
MUTE
:
continue
mode
=
'q'
self
.
context
.
command
(
action
%
kwargs
)
if
self
.
completemask
:
self
.
ctx
.
command
(
"MODE %s +%s %s!%s@%s"
%
(
self
.
channel
,
mode
,
self
.
nick
,
self
.
mask
[
0
],
self
.
mask
[
1
]))
self
.
done
()
else
:
self
.
ctx
.
command
(
"MODE %s +%s *!*@%s"
%
(
self
.
channel
,
mode
,
self
.
mask
[
1
]))
def
done
(
self
):
"""Finaliazation and cleanup"""
if
self
.
typ
==
XBAN
:
# Done!
mode
=
'b'
if
debug
:
self
.
ctx
.
command
(
"MODE %s +%s $x:*%s*"
%
(
self
.
channel
,
mode
,
self
.
nick
))
xchat
.
emit_print
(
'Server Text'
,
"Done "
+
str
(
self
))
pending
.
remove
(
self
)
# Deop?
if
not
self
.
am_op
or
not
self
.
needs_op
:
return
if
self
.
typ
in
[
NAMEBAN
,
KICKNAMEBAN
,
LART
]:
for
p
in
pending
:
self
.
ctx
.
command
(
"MODE %s +d %s"
%
(
self
.
channel
,
self
.
realname
.
replace
(
' '
,
'?'
)))
if
p
.
channel
==
self
.
channel
and
p
.
needs_op
or
not
p
.
deop
:
self
.
deop
=
False
break
if
self
.
typ
==
LART
:
if
self
.
deop
:
# Still todo: ident ban and nick ban
self
.
context
.
command
(
"chanserv deop %s"
%
self
.
channel
)
self
.
ctx
.
command
(
"MODE %s +b %s!*@*"
%
(
self
.
channel
,
self
.
nick
))
self
.
ctx
.
command
(
"MODE %s +b *!%s@*"
%
(
self
.
channel
,
self
.
mask
[
0
]))
# Schedule removal?
if
self
.
timer
:
action
=
Action
(
self
.
channel
,
self
.
me
,
self
.
context
)
action
.
deop
=
self
.
deop
action
.
actions
=
[
x
.
replace
(
'+'
,
'-'
,
1
)
for
x
in
self
.
actions
]
action
.
target
=
action
.
target_nick
=
self
.
target_nick
action
.
target_ident
=
self
.
target_ident
action
.
target_host
=
self
.
target_host
action
.
target_name
=
self
.
target_name
action
.
target_name_bannable
=
self
.
target_name_bannable
action
.
target_account
=
self
.
target_account
action
.
resolved
=
True
action
.
banmode
=
self
.
banmode
action
.
needs_op
=
True
xchat
.
hook_timer
(
self
.
timer
*
1000
,
lambda
act
:
act
.
schedule
(
update_stamp
=
True
)
and
False
,
action
)
def
match
(
self
,
ban
):
def
match
(
self
,
ban
):
if
'!'
in
ban
and
'@'
in
ban
:
# Not 100% reliable but it'll do
"""Does a ban match this action"""
try
:
if
ban
.
startswith
(
'$r:'
)
and
self
.
target_name
:
nick
,
host
=
ban
.
split
(
'!'
)[:
2
]
# Trim !#foo channel forward
return
ban2re
(
ban
[
3
:]).
match
(
self
.
target_name
)
ident
,
host
=
host
.
split
(
'@'
)
elif
ban
.
startswith
(
'$a:'
)
and
self
.
target_account
:
except
:
return
ban2re
(
ban
[
3
:]).
match
(
self
.
target_account
)
# If this happens, the ban is invalid and we should remove it anyway
else
:
return
True
if
'#'
in
ban
:
if
nick
[
0
]
==
'%'
:
ban
=
ban
[:
ban
.
find
(
'$#'
)]
nick
=
nick
[
1
:]
return
ban2re
(
ban
).
match
(
'%s!%s@%s'
%
(
self
.
target_nick
,
self
.
target_ident
,
self
.
target_host
))
for
mtch
,
me
in
[(
nick
,
self
.
nick
),
(
ident
,
self
.
mask
[
0
]),
(
host
,
self
.
mask
[
1
])]:
mtch
=
'^%s$'
%
re
.
escape
(
mtch
).
replace
(
r'\
*
','
.
*
').replace(r'
\
?
','
.
')
if not re.match(mtch,me,re.I):
return False
return True
mtch = '
^%
s
$
' % re.escape(ban).replace(r'
\
*
','
.
*
').replace(r'
\
?
','
.
')
if not re.match(mtch,self.realname,re.I):
return False
return True
def n2a(self,request=False):
if self.nick in users:
if users[self.nick][0]:
if users[self.nick][3] > time.time() - 10:
self.mask = list(users[self.nick][0:2])
self.realname = users[self.nick][2]
return
if request:
self.ctx.command('
whois
%
s
' % self.nick)
self.mask = None
def schedule(event, deop):
# Add event to the pending queue and make sure all neccessary commands are
# issued. Don'
t
op
if
not
sure
the
nick
is
there
pending
.
append
(
event
)
# Am I op?
for
user
in
event
.
ctx
.
get_list
(
'users'
):
if
user
.
nick
==
event
.
ctx
.
get_info
(
'nick'
)
and
user
.
prefix
==
'@'
:
event
.
am_op
=
True
break
else
:
event
.
am_op
=
False
# Deop afterwards?
event
.
deop
=
deop
if
event
.
deop
:
event
.
deop
=
not
event
.
am_op
for
p
in
pending
:
if
p
.
channel
==
event
.
channel
and
p
.
deop
:
event
.
deop
=
True
# Do I know the nick
if
event
.
typ
in
(
BAN
,
XBAN
,
KICKBAN
,
MUTE
,
UNBAN
,
BANS
)
and
not
event
.
mask
:
event
.
n2a
(
request
=
True
)
if
event
.
typ
in
(
NAMEBAN
,
KICKNAMEBAN
)
and
not
event
.
realname
:
event
.
n2a
(
request
=
True
)
if
event
.
typ
==
LART
and
(
not
event
.
mask
or
not
event
.
realname
):
event
.
n2a
(
request
=
True
)
# Do I have all bans
if
event
.
typ
in
[
UNBAN
,
BANS
]
and
event
.
channel
not
in
bans
:
_bans
[
event
.
channel
]
=
[]
event
.
ctx
.
command
(
"MODE %s =bd"
%
event
.
channel
)
# Do I have all modes
if
event
.
typ
in
[
TOPIC
,
MODE
]
and
event
.
channel
not
in
modes
:
event
.
ctx
.
command
(
"MODE %s"
%
event
.
channel
)
run_pending
()
def
run_pending
(
just_opped
=
None
):
def
run_pending
(
just_opped
=
None
):
"""Check all actions and run them if all information is there"""
now
=
time
.
time
()
for
p
in
pending
:
for
p
in
pending
:
if
p
.
channel
==
just_opped
:
p
.
am_op
=
True
if
p
.
target_nick
in
users
and
not
p
.
resolved
:
p
.
resolve_nick
(
request
=
False
)
# Timeout?
# Timeout?
if
p
.
stamp
<
time
.
time
()
-
10
:
if
p
.
stamp
<
now
-
10
:
if
p
.
deop
and
len
([
x
for
x
in
pending
if
x
.
channel
==
p
.
channel
])
==
0
:
p
.
done
()
p
.
ctx
.
command
(
'chanserv OP %s -%s'
%
(
p
.
channel
,
p
.
ctx
.
get_info
(
'nick'
)))
pending
.
remove
(
p
)
continue
continue
if
p
.
channel
==
just_opped
:
can_run
=
not
(
p
.
channel
in
collecting_bans
and
(
p
.
do_unban
or
p
.
do_bans
))
p
.
am_op
=
True
if
can_run
and
p
.
resolved
and
(
p
.
am_op
or
not
p
.
needs_op
):
if
p
.
typ
in
(
BAN
,
XBAN
,
KICKBAN
,
MUTE
,
UNBAN
,
LART
,
BANS
)
and
not
p
.
mask
:
p
.
run
()
p
.
n2a
()
if
p
.
typ
in
(
NAMEBAN
,
KICKNAMEBAN
,
LART
)
and
not
p
.
realname
:
# Helper functions
p
.
n2a
()
def
ban2re
(
data
):
return
re
.
compile
(
'^'
+
re
.
escape
(
data
).
replace
(
r'\
*
','
.
*
').replace(r'
\
?
','
.
') + '
$
')
# Run!
# Mode check here! TODO
_valid_nickname = re.compile(r'
^
[
-
a
-
zA
-
Z0
-
9
\
[
\
]{}
`
|
_
^
\\
]{
0
,
30
}
$
')
if
p
.
typ
==
MODE
and
p
.
channel
in
modes
:
valid_nickname = lambda data: _valid_nickname.match(data)
if
not
modes
[
p
.
channel
].
would_change
([
x
for
x
in
p
.
arg
.
split
()
if
x
]):
_valid_channel = re.compile(r'
^
[
#~].*') # OK, this is cheating
pending
.
remove
(
p
)
valid_channel
=
lambda
data
:
_valid_channel
.
match
(
data
)
return
_valid_mask
=
re
.
compile
(
r'^([-a-zA-Z0-9\
[
\]{}`|_^\\*?]{0,30}!.*?@.*?|\
$[
ar]:.*)$'
)
valid_mask
=
lambda
data
:
_valid_mask
.
match
(
data
)
if
(
p
.
typ
in
(
BAN
,
XBAN
,
KICKBAN
,
MUTE
)
and
p
.
mask
)
or
\
(
p
.
typ
in
(
NAMEBAN
,
KICKNAMEBAN
)
and
p
.
realname
)
or
\
# Data processing
(
p
.
typ
in
(
UNBAN
,
BANS
)
and
p
.
channel
in
bans
and
p
.
mask
)
or
\
(
p
.
typ
in
(
MODE
,
TOPIC
)
and
p
.
channel
in
modes
)
or
\
(
p
.
typ
==
LART
and
p
.
realname
and
p
.
mask
)
or
\
p
.
typ
in
(
KICK
,
INVITE
):
if
p
.
am_op
or
(
p
.
typ
==
TOPIC
and
't'
not
in
modes
[
p
.
channel
].
modeset
)
or
(
p
.
typ
==
BANS
):
p
.
run
()
pending
.
remove
(
p
)
if
p
.
typ
in
(
UNBAN
,
BANS
)
and
len
([
x
for
x
in
pending
if
x
.
channel
==
p
.
channel
and
x
.
typ
in
(
UNBAN
,
BANS
)])
==
0
:
bans
.
pop
(
p
.
channel
)
if
p
.
deop
and
len
([
x
for
x
in
pending
if
x
.
channel
==
p
.
channel
])
==
0
:
p
.
ctx
.
command
(
'chanserv OP %s -%s'
%
(
p
.
channel
,
p
.
ctx
.
get_info
(
'nick'
)))
else
:
p
.
ctx
.
command
(
'chanserv OP %s %s'
%
(
p
.
channel
,
p
.
ctx
.
get_info
(
'nick'
)))
# Run commands after chanserv ops
def
do_mode
(
word
,
word_eol
,
userdata
):
def
do_mode
(
word
,
word_eol
,
userdata
):
"""Run pending actions when chanserv opped us"""
ctx
=
xchat
.
get_context
()
ctx
=
xchat
.
get_context
()
if
'chanserv!'
in
word
[
0
].
lower
()
and
'+o'
in
word
[
3
]
and
ctx
.
get_info
(
'nick'
)
in
word
:
if
'chanserv!'
in
word
[
0
].
lower
()
and
'+o'
in
word
[
3
]
and
ctx
.
get_info
(
'nick'
)
in
word
:
run_pending
(
just_opped
=
ctx
.
get_info
(
'channel'
))
run_pending
(
just_opped
=
ctx
.
get_info
(
'channel'
))
xchat
.
hook_server
(
'MODE'
,
do_mode
)
xchat
.
hook_server
(
'MODE'
,
do_mode
)
# Run commands after /whois returns data
class
User
(
object
):
def
__init__
(
self
,
nick
,
ident
,
host
,
name
):
self
.
nick
=
nick
;
self
.
ident
=
ident
;
self
.
host
=
host
;
self
.
name
=
name
self
.
account
=
None
self
.
time
=
time
.
time
()
def
do_whois
(
word
,
word_eol
,
userdata
):
def
do_whois
(
word
,
word_eol
,
userdata
):
users
[
word
[
3
].
lower
()]
=
(
word
[
4
],
word
[
5
],
word_eol
[
7
][
1
:],
time
.
time
())
"""Store whois replies in global cache"""
run_pending
()
nick
=
word
[
3
].
lower
()
if
word
[
1
]
==
'330'
:
users
[
nick
].
account
=
word
[
4
]
else
:
users
[
nick
]
=
User
(
nick
,
word
[
4
],
word
[
5
],
word_eol
[
7
][
1
:])
xchat
.
hook_server
(
'311'
,
do_whois
)
xchat
.
hook_server
(
'311'
,
do_whois
)
xchat
.
hook_server
(
'330'
,
do_whois
)
xchat
.
hook_server
(
'314'
,
do_whois
)
# This actually is a /whowas reply
xchat
.
hook_server
(
'314'
,
do_whois
)
# This actually is a /whowas reply
# Do /whowas is /whois fails
def
do_missing
(
word
,
word_eol
,
userdata
):
def
do_missing
(
word
,
word_eol
,
userdata
):
"""Fall back to whowas if whois fails"""
for
p
in
pending
:
for
p
in
pending
:
if
p
.
nick
==
word
[
3
]:
if
p
.
target
==
word
[
3
]:
p
.
c
tx
.
command
(
'whowas %s'
%
word
[
3
])
p
.
c
ontext
.
command
(
'whowas %s'
%
word
[
3
])
break
break
xchat
.
hook_server
(
'401'
,
do_missing
)
xchat
.
hook_server
(
'401'
,
do_missing
)
# Display an error if /whowas also fails
def
do_endwas
(
word
,
word_eol
,
userdata
):
def
do_endwas
(
word
,
word_eol
,
userdata
):
"""Display error if nickname cannot be resolved"""
for
p
in
pending
:
for
p
in
pending
:
if
p
.
nick
==
word
[
3
]:
if
p
.
target
==
word
[
3
]:
xchat
.
emit_print
(
"Server Error"
,
"%s could not be found"
%
p
.
nick
)
xchat
.
emit_print
(
"Server Error"
,
"%s could not be found"
%
p
.
target
)
pending
.
remove
(
p
)
pending
.
remove
(
p
)
xchat
.
hook_server
(
'406'
,
do_endwas
)
xchat
.
hook_server
(
'406'
,
do_endwas
)
# Add ban data tot cache (reply of /mode =b)
def
endofwhois
(
word
,
word_eol
,
userdata
):
"""Process the queue after nickname resolution"""
run_pending
()
xchat
.
hook_server
(
'318'
,
endofwhois
)
xchat
.
hook_server
(
'369'
,
endofwhois
)
xchat
.
hook_server
(
'482'
,
lambda
word
,
word_eol
,
userdata
:
xchat
.
emit_print
(
'Server Error'
,
'%s in %s'
%
(
word_eol
[
4
][
1
:],
word
[
3
])))
def
do_ban
(
word
,
word_eol
,
userdata
):
def
do_ban
(
word
,
word_eol
,
userdata
):
if
word
[
3
]
in
_bans
:
"""Process banlists"""
_bans
[
word
[
3
]].
append
(
word
[
4
])
channel
,
ban
=
word
[
3
:
5
]
if
channel
in
collecting_bans
:
bans
[
channel
].
append
(
ban
)
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
return
xchat
.
EAT_NONE
xchat
.
hook_server
(
'367'
,
do_ban
)
xchat
.
hook_server
(
'367'
,
do_ban
)
# Run commands after all bans are shown
def
do_quiet
(
word
,
word_eol
,
userdata
):
# It does mode =bd, so 2 368 have to be received before any action is taken
"""Process banlists"""
MARKER
=
'@@@@@@'
# This is invalid as ban, so is safe, the +b bans come first
channel
,
ban
=
word
[
3
],
word
[
5
]
MARKER2
=
'!!!!!!'
if
channel
in
collecting_bans
:
quiets
[
channel
].
append
(
ban
)
return
xchat
.
EAT_ALL
return
xchat
.
EAT_NONE
xchat
.
hook_server
(
'728'
,
do_quiet
)
def
do_endban
(
word
,
word_eol
,
userdata
):
def
do_endban
(
word
,
word_eol
,
userdata
):
if
word
[
3
]
in
_bans
:
"""Process end-of-ban markers"""
if
MARKER
in
_bans
[
word
[
3
]]:
channel
=
word
[
3
]
_bans
[
word
[
3
]].
remove
(
MARKER
)
if
channel
in
collecting_bans
:
bans
[
word
[
3
]]
=
_bans
[
word
[
3
]]
del
(
_bans
[
word
[
3
]])
run_pending
()
else
:
_bans
[
word
[
3
]].
append
(
MARKER
)
return
xchat
.
EAT_ALL
return
xchat
.
EAT_ALL
return
xchat
.
EAT_NONE
xchat
.
hook_server
(
'368'
,
do_endban
)
xchat
.
hook_server
(
'368'
,
do_endban
)
# Autorejoin on /remove and /kick
def
do_endquiet
(
word
,
word_eol
,
userdata
):
"""Process end-of-quiet markers"""
channel
=
word
[
3
]
if
channel
in
collecting_bans
:
collecting_bans
.
remove
(
channel
)
run_pending
()
return
xchat
.
EAT_ALL
return
xchat
.
EAT_NONE
xchat
.
hook_server
(
'729'
,
do_endquiet
)
# Turn on autorejoin
xchat
.
command
(
'SET -quiet irc_auto_rejoin ON'
)
xchat
.
command
(
'SET -quiet irc_auto_rejoin ON'
)
def
rejoin
(
word
,
word_eol
,
userdata
):
def
rejoin
(
word
,
word_eol
,
userdata
):
if
word
[
0
][
1
:
word
[
0
].
find
(
'!'
)]
==
xchat
.
get_info
(
'nick'
)
and
word
[
3
][
1
:].
lower
()
==
'requested'
:
"""Rejoin when /remove'd"""
if
word
[
0
][
1
:
word
[
0
].
find
(
'!'
)]
==
xchat
.
get_info
(
'nick'
)
and
len
(
word
)
>
3
and
word
[
3
][
1
:].
lower
()
==
'requested'
:
xchat
.
command
(
'join %s'
%
word
[
2
])
xchat
.
command
(
'join %s'
%
word
[
2
])
xchat
.
hook_server
(
'PART'
,
rejoin
)
xchat
.
hook_server
(
'PART'
,
rejoin
)
# Try to convince chanserv to let me in
# Unban when muted
def
letmein
(
word
,
word_eol
,
userdata
):
xchat
.
hook_server
(
'404'
,
lambda
word
,
word_eol
,
userdata
:
xchat
.
command
(
'quote cs unban %s'
%
word
[
3
]))
if
word
[
1
]
==
'473'
:
xchat
.
command
(
'quote cs invite %s'
%
word
[
3
])
elif
word
[
1
]
==
'474'
:
xchat
.
command
(
'quote cs unban %s'
%
word
[
3
])
elif
word
[
1
]
==
'475'
:
xchat
.
command
(
'quote cs getkey %s'
%
word
[
3
])
xchat
.
hook_server
(
'473'
,
letmein
)
# +i
xchat
.
hook_server
(
'474'
,
letmein
)
# +b
xchat
.
hook_server
(
'475'
,
letmein
)
# +k
def
unmute
(
word
,
word_eol
,
userdata
):
xchat
.
command
(
'cs unban %s'
%
xchat
.
get_info
(
'nick'
))
xchat
.
hook_server
(
'404'
,
unmute
)
class
ModeSet
:
def
__init__
(
self
,
raw_modes
=
[],
modeset
=
{},
new
=
False
):
self
.
modeset
=
dict
(
modeset
)
# Always copy
if
raw_modes
:
self
.
merge
(
raw_modes
)
self
.
new
=
new
def
merge
(
self
,
raw_modes
):
newmodes
=
raw_modes
[
0
];
args
=
raw_modes
[
1
:]
num_plusmin
=
newmodes
.
count
(
'+'
)
+
newmodes
.
count
(
'-'
)
do_set
=
True
if
len
(
args
)
>
len
(
newmodes
)
-
num_plusmin
or
newmodes
[
0
]
not
in
'+-'
:
xchat
.
emit_print
(
'Server Error'
,
'Woah mitzy, be careful with your modes :)'
)
else
:
for
i
in
range
(
len
(
newmodes
)):
m
=
newmodes
[
i
]
if
m
==
'+'
:
do_set
=
True
;
num_plusmin
-=
1
elif
m
==
'-'
:
do_set
=
False
;
num_plusmin
-=
1
elif
m
.
isalnum
():
if
m
in
'vohbdqa'
:
continue
argp
=
len
(
newmodes
)
-
i
-
len
(
args
)
-
num_plusmin
if
argp
==
0
:
self
.
modeset
[
m
]
=
args
[
0
]
args
=
args
[
1
:]
else
:
if
do_set
:
self
.
modeset
[
m
]
=
True
elif
m
in
self
.
modeset
:
self
.
modeset
.
pop
(
m
)
else
:
xchat
.
emit_print
(
'Server Error'
,
'Whoah mitzy, be careful with your modes :)'
)
def
would_change
(
self
,
raw_modes
):
# Always return true for operator/voice etc... changes
for
m
in
raw_modes
[
0
]:
if
m
in
'vohbdqa'
:
return
True
return
str
(
ModeSet
(
raw_modes
,
self
.
modeset
))
!=
str
(
self
)
def
__str__
(
self
):
# Convince chanserv to let me in when key/unban/invite is needed
return
'{'
+
','
.
join
([
'%s:%s'
%
(
x
,
self
.
modeset
[
x
])
for
x
in
sorted
(
self
.
modeset
.
keys
())])
+
'}'
xchat
.
hook_server
(
'471'
,
lambda
word
,
word_eol
,
userdata
:
xchat
.
command
(
'quote cs invite %s'
%
word
[
3
]))
# 471 = limit reached
xchat
.
hook_server
(
'473'
,
lambda
word
,
word_eol
,
userdata
:
xchat
.
command
(
'quote cs invite %s'
%
word
[
3
]))
xchat
.
hook_server
(
'474'
,
lambda
word
,
word_eol
,
userdata
:
xchat
.
command
(
'quote cs unban %s'
%
word
[
3
]))
xchat
.
hook_server
(
'475'
,
lambda
word
,
word_eol
,
userdata
:
xchat
.
command
(
'quote cs getkey %s'
%
word
[
3
]))
def
do_mode2
(
word
,
word_eol
,
userdata
):
def
on_invite
(
word
,
word_eol
,
userdata
):
if
userdata
:
"""Autojoin when chanserv invites us"""
ret
=
xchat
.
EAT_NONE
c
=
word
[
3
]
if
c
[
0
]
!=
'#'
:
return
if
c
not
in
modes
:
ret
=
xchat
.
EAT_ALL
modes
[
c
]
=
ModeSet
(
word
[
4
:],
new
=
c
not
in
modes
)
run_pending
()
return
ret
else
:
# Let's be lazy here
c
=
word
[
2
]
if
c
[
0
]
!=
'#'
:
return
if
c
not
in
modes
:
xchat
.
command
(
"MODE %s"
%
c
)
else
:
modes
[
c
].
merge
(
word
[
3
:])
xchat
.
hook_server
(
'324'
,
do_mode2
,
True
)
xchat
.
hook_server
(
'MODE'
,
do_mode2
,
False
)
def
do_time
(
word
,
word_eol
,
userdata
):
if
word
[
3
]
in
modes
and
modes
[
word
[
3
]].
new
:
modes
[
word
[
3
]].
new
=
False
return
xchat
.
EAT_ALL
xchat
.
hook_server
(
'329'
,
do_time
,
True
)
def
joincb
(
word
,
word_eol
,
userdata
):
modes
[
word
[
1
]]
=
ModeSet
()
xchat
.
hook_command
(
'join'
,
joincb
,
priority
=
xchat
.
PRI_HIGHEST
)
# Did chanserv let me in? - This function is now misnamed as it's used for akick as well
def
join
(
word
,
word_eol
,
userdata
):
global
intercept_akick
if
word
[
0
]
==
':ChanServ!ChanServ@services.'
:
if
word
[
0
]
==
':ChanServ!ChanServ@services.'
:
if
word
[
1
]
==
'INVITE'
:
xchat
.
command
(
'JOIN %s'
%
word
[
-
1
][
1
:])
xchat
.
command
(
'join %s'
%
word
[
-
1
][
1
:])
if
'have been cleared'
in
word_eol
[
0
]:
xchat
.
command
(
'JOIN %s'
%
word
[
-
1
])
xchat
.
hook_server
(
'INVITE'
,
on_invite
)
if
'key is'
in
word_eol
[
0
]:
xchat
.
command
(
'JOIN %s %s'
%
(
word
[
4
][
2
:
-
2
],
word
[
-
1
][
2
:
-
2
]))
# Work around xchat stupidness by always writing chanserv notices to the
def
on_notice
(
word
,
word_eol
,
userdata
):
# current context
"""Autojoin when chanserv unbans us or sent us a key"""
# Intercept akick lists if needed
if
word
[
0
]
!=
':ChanServ!ChanServ@services.'
:
if
intercept_akick
:
return
if
'AutoRemove'
in
word_eol
[
0
]
or
'Num Hostmask'
in
word_eol
[
0
]
or
'--- --------'
in
word_eol
[
0
]:
if
'Unbanned'
in
word_eol
[
0
]:
return
xchat
.
EAT_ALL
xchat
.
command
(
'JOIN %s'
%
word
[
6
].
strip
()[
1
:
-
1
])
if
'-- End of list --'
in
word_eol
[
0
]:
if
'key is'
in
word_eol
[
0
]:
intercept_akick
=
False
xchat
.
command
(
'JOIN %s %s'
%
(
word
[
4
][
1
:
-
1
],
word
[
-
1
]))
# FIXME Remove marker
xchat
.
hook_server
(
'NOTICE'
,
on_notice
)
run_pending
()
else
:
# FIXME Parse hostmask and add to bans
pass
if
word
[
1
]
==
'NOTICE'
:
xchat
.
emit_print
(
"Notice"
,
'ChanServ'
,
word_eol
[
3
][
2
:])
return
xchat
.
EAT_ALL
xchat
.
hook_server
(
'NOTICE'
,
join
)
xchat
.
hook_server
(
'INVITE'
,
join
)
def
download
():
import
urllib2
,
os
xchat
.
emit_print
(
'Server Text'
,
'Trying to download chanserv.py from kaarsemaker.net'
)
try
:
fd
=
open
(
os
.
path
.
join
(
xchat
.
get_info
(
'xchatdir'
),
'chanserv.py'
))
old_cs
=
fd
.
read
()
fd
.
close
()
fd
=
urllib2
.
urlopen
(
'http://media.kaarsemaker.net/chanserv.py'
)
new_cs
=
fd
.
read
()
fd
.
close
()
if
old_cs
==
new_cs
:
xchat
.
emit_print
(
'Server Text'
,
'No new version of chanserv.py is available'
)
return
# Basic sanity check
if
'Seveas'
in
new_cs
and
'chanserv.py'
in
new_cs
and
'xchat.hook_server'
in
new_cs
:
fd2
=
open
(
os
.
path
.
join
(
xchat
.
get_info
(
'xchatdir'
),
'chanserv.py'
),
'w'
)
fd2
.
write
(
new_cs
)
fd2
.
close
()
xchat
.
emit_print
(
'Server Text'
,
'chanserv.py updated -- reload with /py reload chanserv.py'
)
else
:
xchat
.
emit_print
(
'Server Error'
,
'Downloading chanserv.py failed - downloaded file not correct'
)
except
:
xchat
.
emit_print
(
'Server Error'
,
'Failed to update chanserv.py'
)
# Spam!
xchat
.
emit_print
(
'Server Text'
,
"Loaded %s %s by Seveas <dennis@kaarsemaker.net>"
%
(
__module_description__
,
__module_version__
))
xchat
.
emit_print
(
'Server Text'
,
"Loaded %s %s by Seveas <dennis@kaarsemaker.net>"
%
(
__module_description__
,
__module_version__
))
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment