DevConsole: New IRC Client interface
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
sugardir = $(pkgdatadir)/services/console/lib/purk/scripts
|
||||
|
||||
sugar_PYTHON = \
|
||||
alias.py \
|
||||
chaninfo.py \
|
||||
clicks.py \
|
||||
completion.py \
|
||||
console.py \
|
||||
history.py \
|
||||
ignore.py \
|
||||
irc_script.py \
|
||||
keys.py \
|
||||
theme.py \
|
||||
timeout.py \
|
||||
ui_script.py
|
||||
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
from conf import conf
|
||||
|
||||
aliases = conf.get("aliases",{
|
||||
'op':'"mode "+window.id+" +"+"o"*len(args)+" "+" ".join(args)',
|
||||
'deop':'"mode "+window.id+" -"+"o"*len(args)+" "+" ".join(args)',
|
||||
'voice':'"mode "+window.id+" +"+"v"*len(args)+" "+" ".join(args)',
|
||||
'devoice':'"mode "+window.id+" -"+"v"*len(args)+" "+" ".join(args)',
|
||||
'umode':'"mode "+network.me+" "+" ".join(args)',
|
||||
'clear':'window.output.clear()',
|
||||
})
|
||||
|
||||
class CommandHandler:
|
||||
__slots__ = ["command"]
|
||||
def __init__(self, command):
|
||||
self.command = command
|
||||
def __call__(self, e):
|
||||
loc = sys.modules.copy()
|
||||
loc.update(e.__dict__)
|
||||
result = eval(self.command,loc)
|
||||
if isinstance(result,basestring):
|
||||
core.events.run(result,e.window,e.network)
|
||||
|
||||
for name in aliases:
|
||||
globals()['onCommand'+name.capitalize()] = CommandHandler(aliases[name])
|
||||
|
||||
def onCommandAlias(e):
|
||||
if e.args and 'r' in e.switches:
|
||||
name = e.args[0].lower()
|
||||
command = aliases[name]
|
||||
del aliases[name]
|
||||
conf['aliases'] = aliases
|
||||
e.window.write("* Deleted alias %s%s (was %s)" % (conf.get('command-prefix','/'),name,command))
|
||||
core.events.load(__name__,reloading=True)
|
||||
elif 'l' in e.switches:
|
||||
e.window.write("* Current aliases:")
|
||||
for i in aliases:
|
||||
e.window.write("* %s%s: %s" % (conf.get('command-prefix','/'),i,aliases[i]))
|
||||
elif len(e.args) >= 2:
|
||||
name = e.args[0].lower()
|
||||
command = ' '.join(e.args[1:])
|
||||
aliases[name] = command
|
||||
conf['aliases'] = aliases
|
||||
e.window.write("* Created an alias %s%s to %s" % (conf.get('command-prefix','/'),name,command))
|
||||
core.events.reload(__name__)
|
||||
elif len(e.args) == 1:
|
||||
name = e.args[0].lower()
|
||||
if name in aliases:
|
||||
e.window.write("* %s%s is an alias to %s" % (conf.get('command-prefix','/'),name,aliases[name]))
|
||||
else:
|
||||
e.window.write("* There is no alias %s%s" % (conf.get('command-prefix','/'),name))
|
||||
else:
|
||||
e.window.write(
|
||||
"""Usage:
|
||||
/alias \x02name\x02 \x02expression\x02 to create or replace an alias
|
||||
/alias \x02name\x02 to look at an alias
|
||||
/alias -r \x02name\x02 to remove an alias
|
||||
/alias -l to see a list of aliases""")
|
||||
@@ -0,0 +1,320 @@
|
||||
import windows
|
||||
|
||||
def _justprefix(network, channel, nick):
|
||||
fr, to = network.isupport["PREFIX"][1:].split(")")
|
||||
|
||||
for mode, prefix in zip(fr, to):
|
||||
if mode in channel.nicks.get(nick, ''):
|
||||
return prefix
|
||||
|
||||
return ''
|
||||
|
||||
def prefix(network, channelname, nick):
|
||||
channel = getchan(network, channelname)
|
||||
|
||||
if channel:
|
||||
nick = '%s%s' % (_justprefix(network, channel, nick), nick)
|
||||
|
||||
return nick
|
||||
|
||||
def escape(string):
|
||||
for escapes in (('&','&'), ('<','<'), ('>','>')):
|
||||
string = string.replace(*escapes)
|
||||
return string
|
||||
|
||||
def sortkey(network, channelname, nick):
|
||||
chanmodes, dummy = network.isupport["PREFIX"][1:].split(")")
|
||||
nickmodes = mode(network, channelname, nick)
|
||||
|
||||
return '%s%s' % (''.join(str(int(mode not in nickmodes)) for mode in chanmodes), network.norm_case(nick))
|
||||
|
||||
def nicklist_add(network, channel, nick):
|
||||
window = windows.get(windows.ChannelWindow, network, channel.name, core)
|
||||
#window = core.window
|
||||
if window:
|
||||
window.nicklist.append(nick, escape(prefix(network, channel.name, nick)), sortkey(network, channel.name, nick))
|
||||
|
||||
def nicklist_del(network, channel, nick):
|
||||
window = windows.get(windows.ChannelWindow, network, channel.name, core)
|
||||
#window = core.window
|
||||
if window:
|
||||
try:
|
||||
window.nicklist.remove(nick)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def setupListRightClick(e):
|
||||
if isinstance(e.window, windows.ChannelWindow):
|
||||
#if isinstance(core.window, windows.ChannelWindow):
|
||||
#if e.data[0] in e.window.network.isupport["PREFIX"].split(")")[1]:
|
||||
if e.data[0] in core.window.network.isupport["PREFIX"].split(")")[1]:
|
||||
e.nick = e.data[1:]
|
||||
else:
|
||||
e.nick = e.data
|
||||
|
||||
def setupSocketConnect(e):
|
||||
e.network.channels = {}
|
||||
|
||||
def setdownDisconnect(e):
|
||||
e.network.channels = {}
|
||||
|
||||
class Channel(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.nicks = {}
|
||||
self.normal_nicks = {} # mapping of normal nicks to actual nicks
|
||||
self.getting_names = False #are we between lines in a /names reply?
|
||||
self.mode = ''
|
||||
self.special_mode = {} #for limits, keys, and anything similar
|
||||
self.topic = ''
|
||||
self.got_mode = False #did we get at least one mode reply?
|
||||
self.got_names = False #did we get at least one names reply?
|
||||
|
||||
def getchan(network, channel):
|
||||
return hasattr(network, 'channels') and network.channels.get(network.norm_case(channel))
|
||||
|
||||
#return a list of channels you're on on the given network
|
||||
def channels(network):
|
||||
if not hasattr(network, 'channels'):
|
||||
network.channels = {}
|
||||
|
||||
return list(network.channels)
|
||||
|
||||
#return True if you're on the channel
|
||||
def ischan(network, channel):
|
||||
return bool(getchan(network, channel))
|
||||
|
||||
#return True if the nick is on the channel
|
||||
def ison(network, channel, nickname):
|
||||
channel = getchan(network, channel)
|
||||
return channel and network.norm_case(nickname) in channel.normal_nicks
|
||||
|
||||
#return a list of nicks on the given channel
|
||||
def nicks(network, channel):
|
||||
channel = getchan(network, channel)
|
||||
|
||||
if channel:
|
||||
return channel.nicks
|
||||
else:
|
||||
return {}
|
||||
|
||||
#return the mode on the given channel
|
||||
def mode(network, channel, nickname=''):
|
||||
channel = getchan(network, channel)
|
||||
|
||||
if channel:
|
||||
if nickname:
|
||||
realnick = channel.normal_nicks.get(network.norm_case(nickname))
|
||||
if realnick:
|
||||
return channel.nicks[realnick]
|
||||
|
||||
else:
|
||||
result = channel.mode
|
||||
for m in channel.mode:
|
||||
if m in channel.special_mode:
|
||||
result += ' '+channel.special_mode[m]
|
||||
return result
|
||||
|
||||
return ''
|
||||
|
||||
#return the topic on the given channel
|
||||
def topic(network, channel):
|
||||
channel = getchan(network, channel)
|
||||
|
||||
if channel:
|
||||
return channel.topic
|
||||
else:
|
||||
return ''
|
||||
|
||||
def setupJoin(e):
|
||||
print e
|
||||
if e.source == e.network.me:
|
||||
e.network.channels[e.network.norm_case(e.target)] = Channel(e.target)
|
||||
e.network.raw('MODE '+e.target)
|
||||
|
||||
#if we wanted to be paranoid, we'd account for not being on the channel
|
||||
channel = getchan(e.network,e.target)
|
||||
channel.nicks[e.source] = ''
|
||||
channel.normal_nicks[e.network.norm_case(e.source)] = e.source
|
||||
|
||||
if e.source == e.network.me:
|
||||
#If the channel window already existed, and we're joining, then we
|
||||
#didn't clear out the nicklist when we left. That means we have to clear
|
||||
#it out now.
|
||||
window = windows.get(windows.ChannelWindow, e.network, e.target, core)
|
||||
#window = core.window
|
||||
#print core
|
||||
if window:
|
||||
window.nicklist.clear()
|
||||
|
||||
nicklist_add(e.network, channel, e.source)
|
||||
|
||||
def setdownPart(e):
|
||||
if e.source == e.network.me:
|
||||
del e.network.channels[e.network.norm_case(e.target)]
|
||||
else:
|
||||
channel = getchan(e.network,e.target)
|
||||
nicklist_del(e.network, channel, e.source)
|
||||
del channel.nicks[e.source]
|
||||
del channel.normal_nicks[e.network.norm_case(e.source)]
|
||||
|
||||
def setdownKick(e):
|
||||
if e.target == e.network.me:
|
||||
del e.network.channels[e.network.norm_case(e.channel)]
|
||||
else:
|
||||
channel = getchan(e.network,e.channel)
|
||||
nicklist_del(e.network, channel, e.target)
|
||||
del channel.nicks[e.target]
|
||||
del channel.normal_nicks[e.network.norm_case(e.target)]
|
||||
|
||||
def setdownQuit(e):
|
||||
#if paranoid: check if e.source is me
|
||||
for channame in channels(e.network):
|
||||
channel = getchan(e.network,channame)
|
||||
if e.source in channel.nicks:
|
||||
nicklist_del(e.network, channel, e.source)
|
||||
del channel.nicks[e.source]
|
||||
del channel.normal_nicks[e.network.norm_case(e.source)]
|
||||
|
||||
def setupMode(e):
|
||||
channel = getchan(e.network,e.channel)
|
||||
if channel:
|
||||
user_modes = e.network.isupport['PREFIX'].split(')')[0][1:]
|
||||
|
||||
(list_modes,
|
||||
always_parm_modes,
|
||||
set_parm_modes,
|
||||
normal_modes) = e.network.isupport['CHANMODES'].split(',')
|
||||
|
||||
list_modes += user_modes
|
||||
|
||||
mode_on = True #are we reading a + section or a - section?
|
||||
params = e.text.split(' ')
|
||||
|
||||
for char in params.pop(0):
|
||||
if char == '+':
|
||||
mode_on = True
|
||||
|
||||
elif char == '-':
|
||||
mode_on = False
|
||||
|
||||
else:
|
||||
if char in user_modes:
|
||||
#these are modes like op and voice
|
||||
nickname = params.pop(0)
|
||||
nicklist_del(e.network, channel, nickname)
|
||||
if mode_on:
|
||||
channel.nicks[nickname] += char
|
||||
else:
|
||||
channel.nicks[nickname] = channel.nicks[nickname].replace(char, '')
|
||||
nicklist_add(e.network, channel, nickname)
|
||||
|
||||
elif char in list_modes:
|
||||
#things like ban/unban
|
||||
#FIXME: We don't keep track of those lists here, but we know
|
||||
# when they're changed and how. Scriptors should be able to
|
||||
# take advantage of this
|
||||
params.pop(0)
|
||||
|
||||
elif char in always_parm_modes:
|
||||
#these always have a parameter
|
||||
param = params.pop(0)
|
||||
|
||||
if mode_on:
|
||||
channel.special_mode[char] = param
|
||||
else:
|
||||
#account for unsetting modes that aren't set
|
||||
channel.special_mode.pop(char, None)
|
||||
|
||||
elif char in set_parm_modes:
|
||||
#these have a parameter only if they're being set
|
||||
if mode_on:
|
||||
channel.special_mode[char] = params.pop(0)
|
||||
else:
|
||||
#account for unsetting modes that aren't set
|
||||
channel.special_mode.pop(char, None)
|
||||
|
||||
if char not in list_modes:
|
||||
if mode_on:
|
||||
channel.mode = channel.mode.replace(char, '')+char
|
||||
else:
|
||||
channel.mode = channel.mode.replace(char, '')
|
||||
|
||||
def setdownNick(e):
|
||||
for channame in channels(e.network):
|
||||
channel = getchan(e.network,channame)
|
||||
if e.source in channel.nicks:
|
||||
nicklist_del(e.network, channel, e.source)
|
||||
del channel.normal_nicks[e.network.norm_case(e.source)]
|
||||
channel.nicks[e.target] = channel.nicks[e.source]
|
||||
del channel.nicks[e.source]
|
||||
channel.normal_nicks[e.network.norm_case(e.target)] = e.target
|
||||
nicklist_add(e.network, channel, e.target)
|
||||
|
||||
def setupTopic(e):
|
||||
channel = getchan(e.network, e.target)
|
||||
if channel:
|
||||
channel.topic = e.text
|
||||
|
||||
def setupRaw(e):
|
||||
if e.msg[1] == '353': #names reply
|
||||
channel = getchan(e.network,e.msg[4])
|
||||
if channel:
|
||||
if not channel.getting_names:
|
||||
channel.nicks.clear()
|
||||
channel.normal_nicks.clear()
|
||||
channel.getting_names = True
|
||||
if not channel.got_names:
|
||||
e.quiet = True
|
||||
for nickname in e.msg[5].split(' '):
|
||||
if nickname:
|
||||
if not nickname[0].isalpha() and nickname[0] in e.network.prefixes:
|
||||
n = nickname[1:]
|
||||
channel.nicks[n] = e.network.prefixes[nickname[0]]
|
||||
channel.normal_nicks[e.network.norm_case(n)] = n
|
||||
else:
|
||||
channel.nicks[nickname] = ''
|
||||
channel.normal_nicks[e.network.norm_case(nickname)] = nickname
|
||||
|
||||
elif e.msg[1] == '366': #end of names reply
|
||||
channel = getchan(e.network,e.msg[3])
|
||||
if channel:
|
||||
if not channel.got_names:
|
||||
e.quiet = True
|
||||
channel.got_names = True
|
||||
channel.getting_names = False
|
||||
|
||||
window = windows.get(windows.ChannelWindow, e.network, e.msg[3], core)
|
||||
if window:
|
||||
window.nicklist.replace(
|
||||
(nick, escape(prefix(e.network, channel.name, nick)), sortkey(e.network, channel.name, nick)) for nick in channel.nicks
|
||||
)
|
||||
|
||||
elif e.msg[1] == '324': #channel mode is
|
||||
channel = getchan(e.network,e.msg[3])
|
||||
if channel:
|
||||
if not channel.got_mode:
|
||||
e.quiet = True
|
||||
channel.got_mode = True
|
||||
mode = e.msg[4]
|
||||
params = e.msg[:4:-1]
|
||||
list_modes, always_parm_modes, set_parm_modes, normal_modes = \
|
||||
e.network.isupport['CHANMODES'].split(',')
|
||||
parm_modes = always_parm_modes + set_parm_modes
|
||||
channel.mode = e.msg[4]
|
||||
channel.special_mode.clear()
|
||||
for char in channel.mode:
|
||||
if char in parm_modes:
|
||||
channel.special_mode[char] = params.pop()
|
||||
|
||||
elif e.msg[1] == '331': #no topic
|
||||
channel = getchan(e.network,e.msg[3])
|
||||
if channel:
|
||||
channel.topic = ''
|
||||
|
||||
elif e.msg[1] == '332': #channel topic is
|
||||
channel = getchan(e.network,e.msg[3])
|
||||
if channel:
|
||||
channel.topic = e.text
|
||||
|
||||
#core.events.load(__name__)
|
||||
@@ -0,0 +1,146 @@
|
||||
import ui
|
||||
import windows
|
||||
import chaninfo
|
||||
from conf import conf
|
||||
|
||||
def set_target(e):
|
||||
target_l = e.target.lstrip('@+%.(<')
|
||||
e._target_fr = e.target_fr + len(e.target) - len(target_l)
|
||||
|
||||
target_r = e.target.rstrip('>:,')
|
||||
e._target_to = e.target_to - len(e.target) + len(target_r)
|
||||
|
||||
if target_r.endswith(')'):
|
||||
e._target = e.text[e._target_fr:e._target_to]
|
||||
open_parens = e._target.count('(') - e._target.count(')')
|
||||
while open_parens < 0 and e.text[e._target_to-1] == ')':
|
||||
e._target_to -= 1
|
||||
open_parens += 1
|
||||
|
||||
e._target = e.text[e._target_fr:e._target_to]
|
||||
|
||||
def is_nick(e):
|
||||
return isinstance(e.window, windows.ChannelWindow) and \
|
||||
chaninfo.ison(e.window.network, e.window.id, e._target)
|
||||
|
||||
def is_url(e):
|
||||
def starts(prefix, mindots=1):
|
||||
def prefix_url(target):
|
||||
return target.startswith(prefix) and target.count('.') >= mindots
|
||||
|
||||
return prefix_url
|
||||
|
||||
to_check = [starts(*x) for x in [
|
||||
('http://', 1),
|
||||
('https://', 1),
|
||||
('ftp://', 1),
|
||||
('www', 2),
|
||||
]]
|
||||
|
||||
for check_url in to_check:
|
||||
if check_url(e._target):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_chan(e):
|
||||
# click on a #channel
|
||||
return e.window.network and e._target and \
|
||||
e._target[0] in e.window.network.isupport.get('CHANTYPES', '&#$+')
|
||||
|
||||
def get_autojoin_list(network):
|
||||
perform = conf.get('networks',{}).get(network.name,{}).get('perform',())
|
||||
channels = set()
|
||||
for line in perform:
|
||||
if line.startswith('join ') and ' ' not in line[5:]:
|
||||
channels.update(line[5:].split(','))
|
||||
return channels
|
||||
|
||||
def add_autojoin(network, channel):
|
||||
if 'networks' not in conf:
|
||||
conf['networks'] = {}
|
||||
if network.name not in conf['networks']:
|
||||
conf['networks'][network.name] = {'server': network.server}
|
||||
conf['start_networks'] = conf.get('start_networks',[]) + [network.name]
|
||||
if 'perform' in conf['networks'][network.name]:
|
||||
perform = conf['networks'][network.name]['perform']
|
||||
else:
|
||||
perform = conf['networks'][network.name]['perform'] = []
|
||||
|
||||
for n, line in enumerate(perform):
|
||||
if line.startswith('join ') and ' ' not in line[5:]:
|
||||
perform[n] = "%s,%s" % (line, channel)
|
||||
break
|
||||
else:
|
||||
perform.append('join %s' % channel)
|
||||
|
||||
def make_nick_menu(e, target):
|
||||
def query():
|
||||
core.events.run('query %s' % target, e.window, e.window.network)
|
||||
|
||||
def whois():
|
||||
core.events.run('whois %s' % target, e.window, e.window.network)
|
||||
|
||||
e.menu += [
|
||||
('Query', query),
|
||||
('Whois', whois),
|
||||
(),
|
||||
]
|
||||
|
||||
def onHover(e):
|
||||
set_target(e)
|
||||
|
||||
for is_check in (is_nick, is_url, is_chan):
|
||||
if is_check(e):
|
||||
e.tolink.add((e._target_fr, e._target_to))
|
||||
break
|
||||
|
||||
def onClick(e):
|
||||
set_target(e)
|
||||
|
||||
if is_nick(e):
|
||||
core.events.run('query %s' % e._target, e.window, e.window.network)
|
||||
|
||||
# url of the form http://xxx.xxx or www.xxx.xxx
|
||||
elif is_url(e):
|
||||
if e._target.startswith('www'):
|
||||
e._target = 'http://%s' % e._target
|
||||
ui.open_file(e._target)
|
||||
|
||||
# click on a #channel
|
||||
elif is_chan(e):
|
||||
if not chaninfo.ischan(e.window.network, e._target):
|
||||
e.window.network.join(e._target)
|
||||
window = windows.get(windows.ChannelWindow, e.window.network, e._target)
|
||||
if window:
|
||||
window.activate()
|
||||
|
||||
def onRightClick(e):
|
||||
set_target(e)
|
||||
|
||||
# nick on this channel
|
||||
if is_nick(e):
|
||||
make_nick_menu(e, e._target)
|
||||
|
||||
elif is_url(e):
|
||||
if e._target.startswith('www'):
|
||||
e._target = 'http://%s' % e._target
|
||||
|
||||
def copy_to():
|
||||
# copy to clipboard
|
||||
ui.set_clipboard(e._target)
|
||||
|
||||
e.menu += [('Copy', copy_to)]
|
||||
|
||||
elif is_chan(e):
|
||||
e.channel = e._target
|
||||
e.network = e.window.network
|
||||
core.events.trigger('ChannelMenu', e)
|
||||
|
||||
def onListRightClick(e):
|
||||
if isinstance(e.window, windows.ChannelWindow):
|
||||
make_nick_menu(e, e.nick)
|
||||
|
||||
def onListDoubleClick(e):
|
||||
if isinstance(e.window, windows.ChannelWindow):
|
||||
core.events.run("query %s" % e.target, e.window, e.window.network)
|
||||
@@ -0,0 +1,135 @@
|
||||
import windows
|
||||
import chaninfo
|
||||
from conf import conf
|
||||
|
||||
def channel_completer(window, left, right, text):
|
||||
if isinstance(window, windows.ChannelWindow):
|
||||
yield window.id
|
||||
|
||||
for w in windows.get_with(wclass=windows.ChannelWindow, network=window.network):
|
||||
if w is not window:
|
||||
yield w.id
|
||||
|
||||
for w in windows.get_with(wclass=windows.ChannelWindow):
|
||||
if w.network is not window.network:
|
||||
yield w.id
|
||||
|
||||
# normal server commands
|
||||
srv_commands = ('ping', 'join', 'part', 'mode', 'server', 'kick',
|
||||
'quit', 'nick', 'privmsg', 'notice', 'topic')
|
||||
|
||||
def command_completer(window, left, right, text):
|
||||
for c in srv_commands:
|
||||
yield '/%s' % c
|
||||
|
||||
if 'CMDS' in window.network.isupport:
|
||||
for c in window.network.isupport['CMDS'].split(','):
|
||||
yield '/%s' % c.lower()
|
||||
|
||||
for c in core.events.all_events:
|
||||
if c.startswith('Command') and c != 'Command':
|
||||
yield '/%s' % c[7:].lower()
|
||||
|
||||
def nick_completer(window, left, right, text):
|
||||
if type(window) == windows.QueryWindow:
|
||||
yield window.id
|
||||
|
||||
recent_speakers = getattr(window, 'recent_speakers', ())
|
||||
|
||||
for nick in recent_speakers:
|
||||
if chaninfo.ison(window.network, window.id, nick):
|
||||
yield nick
|
||||
|
||||
for nick in chaninfo.nicks(window.network, window.id):
|
||||
if nick not in recent_speakers:
|
||||
yield nick
|
||||
|
||||
def script_completer(window, left, right, text):
|
||||
return core.events.loaded.iterkeys()
|
||||
|
||||
def network_completer(window, left, right, text):
|
||||
return conf.get('networks', {}).iterkeys()
|
||||
|
||||
def get_completer_for(window):
|
||||
input = window.input
|
||||
|
||||
left, right = input.text[:input.cursor], input.text[input.cursor:]
|
||||
|
||||
text = left.split(' ')[-1]
|
||||
|
||||
while True:
|
||||
suffix = ''
|
||||
if text and text[0] in window.network.isupport.get('CHANTYPES', '#&+'):
|
||||
candidates = channel_completer(window, left, right, text)
|
||||
|
||||
elif input.text.startswith('/reload '):
|
||||
candidates = script_completer(window, left, right, text)
|
||||
|
||||
elif input.text.startswith('/edit '):
|
||||
candidates = script_completer(window, left, right, text)
|
||||
|
||||
elif input.text.startswith('/server '):
|
||||
candidates = network_completer(window, left, right, text)
|
||||
|
||||
elif text.startswith('/'):
|
||||
candidates = command_completer(window, left, right, text)
|
||||
suffix = ' '
|
||||
|
||||
else:
|
||||
candidates = nick_completer(window, left, right, text)
|
||||
|
||||
if left == text:
|
||||
suffix = ': '
|
||||
else:
|
||||
suffix = ' '
|
||||
|
||||
if text:
|
||||
before = left[:-len(text)]
|
||||
else:
|
||||
before = left
|
||||
|
||||
insert_text = '%s%s%s%s' % (before, '%s', suffix, right)
|
||||
cursor_pos = len(before + suffix)
|
||||
|
||||
original = window.input.text, window.input.cursor
|
||||
|
||||
for cand in candidates:
|
||||
if cand.lower().startswith(text.lower()):
|
||||
window.input.text, window.input.cursor = insert_text % cand, cursor_pos + len(cand)
|
||||
yield None
|
||||
|
||||
window.input.text, window.input.cursor = original
|
||||
yield None
|
||||
|
||||
# generator--use recent_completer.next() to continue cycling through whatever
|
||||
recent_completer = None
|
||||
|
||||
def onKeyPress(e):
|
||||
global recent_completer
|
||||
|
||||
if e.key == 'Tab':
|
||||
if not recent_completer:
|
||||
recent_completer = get_completer_for(e.window)
|
||||
|
||||
recent_completer.next()
|
||||
|
||||
else:
|
||||
recent_completer = None
|
||||
|
||||
def onActive(e):
|
||||
global recent_completer
|
||||
|
||||
recent_completer = None
|
||||
|
||||
def onText(e):
|
||||
if chaninfo.ischan(e.network, e.target):
|
||||
if not hasattr(e.window, 'recent_speakers'):
|
||||
e.window.recent_speakers = []
|
||||
|
||||
for nick in e.window.recent_speakers:
|
||||
if nick == e.source or not chaninfo.ison(e.network, e.target, nick):
|
||||
e.window.recent_speakers.remove(nick)
|
||||
|
||||
e.window.recent_speakers.insert(0, e.source)
|
||||
|
||||
onAction = onText
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
import sys
|
||||
import traceback
|
||||
import windows
|
||||
from conf import conf
|
||||
|
||||
class ConsoleWriter:
|
||||
__slots__ = ['window']
|
||||
def __init__(self, window):
|
||||
self.window = window
|
||||
def write(self, text):
|
||||
try:
|
||||
self.window.write(text, line_ending='')
|
||||
except:
|
||||
self.window.write(traceback.format_exc())
|
||||
|
||||
class ConsoleWindow(windows.SimpleWindow):
|
||||
def __init__(self, network, id):
|
||||
windows.SimpleWindow.__init__(self, network, id)
|
||||
|
||||
writer = ConsoleWriter(self)
|
||||
|
||||
sys.stdout = writer
|
||||
sys.stderr = writer
|
||||
|
||||
self.globals = {'window': self}
|
||||
self.locals = {}
|
||||
|
||||
#this prevents problems (and updates an open console window) on reload
|
||||
#window = None
|
||||
#for window in manager:
|
||||
# if type(window).__name__ == "ConsoleWindow":
|
||||
# window.mutate(ConsoleWindow, window.network, window.id)
|
||||
#del window
|
||||
|
||||
def onClose(e):
|
||||
if isinstance(e.window, ConsoleWindow):
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stderr = sys.__stderr__
|
||||
|
||||
def onCommandConsole(e):
|
||||
windows.new(ConsoleWindow, None, "console").activate()
|
||||
|
||||
def onCommandSay(e):
|
||||
if isinstance(e.window, ConsoleWindow):
|
||||
import pydoc #fix nonresponsive help() command
|
||||
old_pager, pydoc.pager = pydoc.pager, pydoc.plainpager
|
||||
e.window.globals.update(sys.modules)
|
||||
text = ' '.join(e.args)
|
||||
try:
|
||||
e.window.write(">>> %s" % text)
|
||||
result = eval(text, e.window.globals, e.window.locals)
|
||||
if result is not None:
|
||||
e.window.write(repr(result))
|
||||
e.window.globals['_'] = result
|
||||
except SyntaxError:
|
||||
try:
|
||||
exec text in e.window.globals, e.window.locals
|
||||
except:
|
||||
traceback.print_exc()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
pydoc.pager = old_pager
|
||||
else:
|
||||
raise core.events.CommandError("There's no one here to speak to.")
|
||||
|
||||
def onStart(e):
|
||||
if conf.get('start-console'):
|
||||
windows.new(ConsoleWindow, None, "console")
|
||||
@@ -0,0 +1,45 @@
|
||||
def onKeyPress(e):
|
||||
if not hasattr(e.window, 'history'):
|
||||
e.window.history = [], -1
|
||||
|
||||
if e.key in ('Up', 'Down'):
|
||||
h, i = e.window.history
|
||||
|
||||
if i == -1 and e.window.input.text:
|
||||
h.insert(0, e.window.input.text)
|
||||
i = 0
|
||||
|
||||
if e.key == 'Up':
|
||||
i += 1
|
||||
|
||||
if i < len(h):
|
||||
e.window.history = h, i
|
||||
|
||||
e.window.input.text = h[i]
|
||||
e.window.input.cursor = -1
|
||||
|
||||
else: # e.key == 'Up'
|
||||
i -= 1
|
||||
|
||||
if i > -1:
|
||||
e.window.history = h, i
|
||||
|
||||
e.window.input.text = h[i]
|
||||
e.window.input.cursor = -1
|
||||
|
||||
elif i == -1:
|
||||
e.window.history = h, i
|
||||
|
||||
e.window.input.text = ''
|
||||
e.window.input.cursor = -1
|
||||
|
||||
def onInput(e):
|
||||
if not hasattr(e.window, 'history'):
|
||||
e.window.history = [], -1
|
||||
|
||||
if e.text:
|
||||
h, i = e.window.history
|
||||
|
||||
h.insert(0, e.text)
|
||||
|
||||
e.window.history = h, -1
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
from conf import conf
|
||||
import irc
|
||||
|
||||
def preRaw(e):
|
||||
if e.msg[1] in ('PRIVMSG','NOTICE'):
|
||||
address = e.network.norm_case('%s!%s' % (e.source, e.address))
|
||||
for mask in conf.get('ignore_masks',()):
|
||||
if irc.match_glob(address, e.network.norm_case(mask)):
|
||||
core.events.halt()
|
||||
|
||||
def onCommandIgnore(e):
|
||||
if 'ignore_masks' not in conf:
|
||||
conf['ignore_masks'] = []
|
||||
if 'l' in e.switches:
|
||||
for i in conf['ignore_masks']:
|
||||
e.window.write('* %s' % i)
|
||||
elif 'c' in e.switches:
|
||||
del conf['ignore_masks']
|
||||
e.window.write('* Cleared the ignore list.')
|
||||
elif e.args:
|
||||
if '!' in e.args[0] or '*' in e.args[0] or '?' in e.args[0]:
|
||||
mask = e.args[0]
|
||||
else:
|
||||
mask = '%s!*' % e.args[0]
|
||||
if 'r' in e.switches:
|
||||
if mask in conf['ignore_masks']:
|
||||
conf['ignore_masks'].remove(mask)
|
||||
e.window.write('* Removed %s from the ignore list' % e.args[0])
|
||||
else:
|
||||
raise core.events.CommandError("Couldn't find %s in the ignore list" % e.args[0])
|
||||
else:
|
||||
if mask in conf['ignore_masks']:
|
||||
e.window.write('* %s is already ignored' % e.args[0])
|
||||
else:
|
||||
conf['ignore_masks'].append(mask)
|
||||
e.window.write('* Ignoring messages from %s' % e.args[0])
|
||||
else:
|
||||
e.window.write(
|
||||
"""Usage:
|
||||
/ignore \x02nick/mask\x02 to ignore a nickname or mask
|
||||
/ignore -r \x02nick/mask\x02 to stop ignoring a nickname or mask
|
||||
/ignore -l to view the ignore list
|
||||
/ignore -c to clear the ignore list""")
|
||||
@@ -0,0 +1,588 @@
|
||||
import time
|
||||
|
||||
from conf import conf
|
||||
import ui
|
||||
import windows
|
||||
import irc
|
||||
|
||||
COMMAND_PREFIX = conf.get('command_prefix', '/')
|
||||
|
||||
NICK_SUFFIX = r"`_-\|0123456789"
|
||||
|
||||
_hextochr = dict(('%02x' % i, chr(i)) for i in range(256))
|
||||
def unquote(url, rurl=""):
|
||||
|
||||
while '%' in url:
|
||||
url, char = url.rsplit('%', 1)
|
||||
|
||||
chars = char[:2].lower()
|
||||
|
||||
if chars in _hextochr:
|
||||
rurl = '%s%s%s' % (_hextochr[chars], char[2:], rurl)
|
||||
else:
|
||||
rurl = "%s%s%s" % ('%', char, rurl)
|
||||
|
||||
return url + rurl
|
||||
|
||||
#for getting a list of alternative nicks to try on a network
|
||||
def _nick_generator(network):
|
||||
for nick in network.nicks[1:]:
|
||||
yield nick
|
||||
if network._nick_error:
|
||||
nick = 'ircperson'
|
||||
else:
|
||||
nick = network.nicks[0]
|
||||
import itertools
|
||||
for i in itertools.count(1):
|
||||
for j in xrange(len(NICK_SUFFIX)**i):
|
||||
suffix = ''.join(NICK_SUFFIX[(j/(len(NICK_SUFFIX)**x))%len(NICK_SUFFIX)] for x in xrange(i))
|
||||
if network._nick_max_length:
|
||||
yield nick[0:network._nick_max_length-i]+suffix
|
||||
else:
|
||||
yield nick+suffix
|
||||
|
||||
def setdownRaw(e):
|
||||
if not e.done:
|
||||
if not e.network.got_nick:
|
||||
if e.msg[1] in ('432','433','436','437'): #nickname unavailable
|
||||
failednick = e.msg[3]
|
||||
nicks = list(e.network.nicks)
|
||||
|
||||
if hasattr(e.network,'_nick_generator'):
|
||||
if len(failednick) < len(e.network._next_nick):
|
||||
e.network._nick_max_length = len(failednick)
|
||||
e.network._next_nick = e.network._nick_generator.next()
|
||||
e.network.raw('NICK %s' % e.network._next_nick)
|
||||
e.network._nick_error |= (e.msg[1] == '432')
|
||||
else:
|
||||
e.network._nick_error = (e.msg[1] == '432')
|
||||
if len(failednick) < len(e.network.nicks[0]):
|
||||
e.network._nick_max_length = len(failednick)
|
||||
else:
|
||||
e.network._nick_max_length = 0
|
||||
e.network._nick_generator = _nick_generator(e.network)
|
||||
e.network._next_nick = e.network._nick_generator.next()
|
||||
e.network.raw('NICK %s' % e.network._next_nick)
|
||||
|
||||
elif e.msg[1] == '431': #no nickname given--this shouldn't happen
|
||||
pass
|
||||
|
||||
elif e.msg[1] == '001':
|
||||
e.network.got_nick = True
|
||||
if e.network.me != e.msg[2]:
|
||||
core.events.trigger(
|
||||
'Nick', network=e.network, window=e.window,
|
||||
source=e.network.me, target=e.msg[2], address='',
|
||||
text=e.msg[2]
|
||||
)
|
||||
e.network.me = e.msg[2]
|
||||
if hasattr(e.network,'_nick_generator'):
|
||||
del e.network._nick_generator, e.network._nick_max_length, e.network._next_nick
|
||||
|
||||
if e.msg[1] == "PING":
|
||||
e.network.raw("PONG :%s" % e.msg[-1])
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] == "JOIN":
|
||||
e.channel = e.target
|
||||
e.requested = e.network.norm_case(e.channel) in e.network.requested_joins
|
||||
core.events.trigger("Join", e)
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] == "PART":
|
||||
e.channel = e.target
|
||||
e.requested = e.network.norm_case(e.channel) in e.network.requested_parts
|
||||
e.text = ' '.join(e.msg[3:])
|
||||
core.events.trigger("Part", e)
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] in "MODE":
|
||||
e.channel = e.target
|
||||
e.text = ' '.join(e.msg[3:])
|
||||
core.events.trigger("Mode", e)
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] == "QUIT":
|
||||
core.events.trigger('Quit', e)
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] == "KICK":
|
||||
e.channel = e.msg[2]
|
||||
e.target = e.msg[3]
|
||||
core.events.trigger('Kick', e)
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] == "NICK":
|
||||
core.events.trigger('Nick', e)
|
||||
if e.network.me == e.source:
|
||||
e.network.me = e.target
|
||||
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] == "PRIVMSG":
|
||||
core.events.trigger('Text', e)
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] == "NOTICE":
|
||||
core.events.trigger('Notice', e)
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] == "TOPIC":
|
||||
core.events.trigger('Topic', e)
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] in ("376", "422"): #RPL_ENDOFMOTD
|
||||
if e.network.status == irc.INITIALIZING:
|
||||
e.network.status = irc.CONNECTED
|
||||
core.events.trigger('Connect', e)
|
||||
e.done = True
|
||||
|
||||
elif e.msg[1] == "470": #forwarded from channel X to channel Y
|
||||
if e.network.norm_case(e.msg[3]) in e.network.requested_joins:
|
||||
e.network.requested_joins.discard(e.network.norm_case(e.msg[3]))
|
||||
e.network.requested_joins.add(e.network.norm_case(e.msg[4]))
|
||||
|
||||
elif e.msg[1] == "005": #RPL_ISUPPORT
|
||||
for arg in e.msg[3:]:
|
||||
if ' ' not in arg: #ignore "are supported by this server"
|
||||
if '=' in arg:
|
||||
name, value = arg.split('=', 1)
|
||||
if value.isdigit():
|
||||
value = int(value)
|
||||
else:
|
||||
name, value = arg, ''
|
||||
|
||||
#Workaround for broken servers (bahamut on EnterTheGame)
|
||||
if name == 'PREFIX' and value[0] != '(':
|
||||
continue
|
||||
|
||||
#in theory, we're supposed to replace \xHH with the
|
||||
# corresponding ascii character, but I don't think anyone
|
||||
# really does this
|
||||
e.network.isupport[name] = value
|
||||
|
||||
if name == 'PREFIX':
|
||||
new_prefixes = {}
|
||||
modes, prefixes = value[1:].split(')')
|
||||
for mode, prefix in zip(modes, prefixes):
|
||||
new_prefixes[mode] = prefix
|
||||
new_prefixes[prefix] = mode
|
||||
e.network.prefixes = new_prefixes
|
||||
|
||||
def setupSocketConnect(e):
|
||||
e.network.got_nick = False
|
||||
e.network.isupport = {
|
||||
'NETWORK': e.network.server,
|
||||
'PREFIX': '(ohv)@%+',
|
||||
'CHANMODES': 'b,k,l,imnpstr',
|
||||
}
|
||||
e.network.prefixes = {'o':'@', 'h':'%', 'v':'+', '@':'o', '%':'h', '+':'v'}
|
||||
e.network.connect_timestamp = time.time()
|
||||
e.network.requested_joins.clear()
|
||||
e.network.requested_parts.clear()
|
||||
e.network.on_channels.clear()
|
||||
if hasattr(e.network,'_nick_generator'):
|
||||
del e.network._nick_generator, e.network._nick_max_length, e.network._next_nick
|
||||
if not e.done:
|
||||
#this needs to be tested--anyone have a server that uses PASS?
|
||||
if e.network.password:
|
||||
e.network.raw("PASS :%s" % e.network.password)
|
||||
e.network.raw("NICK %s" % e.network.nicks[0])
|
||||
e.network.raw("USER %s %s %s :%s" %
|
||||
(e.network.username, "8", "*", e.network.fullname))
|
||||
#per rfc2812 these are username, user mode flags, unused, realname
|
||||
|
||||
#e.network.me = None
|
||||
e.done = True
|
||||
|
||||
def onDisconnect(e):
|
||||
if hasattr(e.network,'_reconnect_source'):
|
||||
e.network._reconnect_source.unregister()
|
||||
del e.network._reconnect_source
|
||||
if hasattr(e.network,'connect_timestamp'):
|
||||
if e.error and conf.get('autoreconnect',True):
|
||||
delay = time.time() - e.network.connect_timestamp > 30 and 30 or 120
|
||||
def do_reconnect():
|
||||
if not e.network.status:
|
||||
server(network=e.network)
|
||||
def do_announce_reconnect():
|
||||
if not e.network.status:
|
||||
windows.get_default(e.network).write("* Will reconnect in %s seconds.." % delay)
|
||||
e.network._reconnect_source = ui.register_timer(delay*1000,do_reconnect)
|
||||
e.network._reconnect_source = ui.register_idle(do_announce_reconnect)
|
||||
|
||||
def onCloseNetwork(e):
|
||||
e.network.quit()
|
||||
if hasattr(e.network,'_reconnect_source'):
|
||||
e.network._reconnect_source.unregister()
|
||||
del e.network._reconnect_source
|
||||
|
||||
def setdownDisconnect(e):
|
||||
if hasattr(e.network,'connect_timestamp'):
|
||||
del e.network.connect_timestamp
|
||||
|
||||
def setupInput(e):
|
||||
if not e.done:
|
||||
if e.text.startswith(COMMAND_PREFIX) and not e.ctrl:
|
||||
command = e.text[len(COMMAND_PREFIX):]
|
||||
else:
|
||||
command = 'say - %s' % e.text
|
||||
|
||||
core.events.run(command, e.window, e.network)
|
||||
|
||||
e.done = True
|
||||
|
||||
def onCommandSay(e):
|
||||
if isinstance(e.window, windows.ChannelWindow) or isinstance(e.window, windows.QueryWindow):
|
||||
e.network.msg(e.window.id, ' '.join(e.args))
|
||||
else:
|
||||
raise core.events.CommandError("There's no one here to speak to.")
|
||||
|
||||
def onCommandMsg(e):
|
||||
e.network.msg(e.args[0], ' '.join(e.args[1:]))
|
||||
|
||||
def onCommandNotice(e):
|
||||
e.network.notice(e.args[0], ' '.join(e.args[1:]))
|
||||
|
||||
def onCommandQuery(e):
|
||||
windows.new(windows.QueryWindow, e.network, e.args[0], core).activate()
|
||||
if len(e.args) > 1:
|
||||
message = ' '.join(e.args[1:])
|
||||
if message: #this is false if you do "/query nickname "
|
||||
e.network.msg(e.args[0], ' '.join(e.args[1:]))
|
||||
|
||||
def setupJoin(e):
|
||||
if e.source == e.network.me:
|
||||
chan = e.network.norm_case(e.channel)
|
||||
e.network.on_channels.add(chan)
|
||||
e.network.requested_joins.discard(chan)
|
||||
|
||||
def setdownPart(e):
|
||||
if e.source == e.network.me:
|
||||
chan = e.network.norm_case(e.channel)
|
||||
e.network.on_channels.discard(chan)
|
||||
e.network.requested_parts.discard(chan)
|
||||
|
||||
def setdownKick(e):
|
||||
if e.target == e.network.me:
|
||||
chan = e.network.norm_case(e.channel)
|
||||
e.network.on_channels.discard(chan)
|
||||
|
||||
def ischan(network, channel):
|
||||
return network.norm_case(channel) in network.on_channels
|
||||
|
||||
# make /nick work offline
|
||||
def change_nick(network, nick):
|
||||
if not network.status:
|
||||
core.events.trigger(
|
||||
'Nick',
|
||||
network=network, window=windows.get_default(network),
|
||||
source=network.me, target=nick, address='', text=nick
|
||||
)
|
||||
network.nicks[0] = nick
|
||||
network.me = nick
|
||||
else:
|
||||
network.raw('NICK :%s' % nick)
|
||||
|
||||
def onCommandNick(e):
|
||||
default_nick = irc.default_nicks()[0]
|
||||
if 't' not in e.switches and e.network.me == default_nick:
|
||||
conf['nick'] = e.args[0]
|
||||
import conf as _conf
|
||||
_conf.save()
|
||||
for network in set(w.network for w in core.manager):
|
||||
if network.me == default_nick:
|
||||
change_nick(network, e.args[0])
|
||||
else:
|
||||
change_nick(e.network, e.args[0])
|
||||
|
||||
def setdownNick(e):
|
||||
if e.source != e.network.me:
|
||||
window = windows.get(windows.QueryWindow, e.network, e.source)
|
||||
if window:
|
||||
window.id = e.target
|
||||
|
||||
# make /quit always disconnect us
|
||||
def onCommandQuit(e):
|
||||
if e.network.status:
|
||||
e.network.quit(' '.join(e.args))
|
||||
else:
|
||||
raise core.events.CommandError("We're not connected to a network.")
|
||||
|
||||
def onCommandRaw(e):
|
||||
if e.network.status >= irc.INITIALIZING:
|
||||
e.network.raw(' '.join(e.args))
|
||||
else:
|
||||
raise core.events.CommandError("We're not connected to a network.")
|
||||
|
||||
onCommandQuote = onCommandRaw
|
||||
|
||||
def onCommandJoin(e):
|
||||
if e.args:
|
||||
if e.network.status >= irc.INITIALIZING:
|
||||
e.network.join(' '.join(e.args), requested = 'n' not in e.switches)
|
||||
else:
|
||||
raise core.events.CommandError("We're not connected.")
|
||||
elif isinstance(e.window, windows.ChannelWindow):
|
||||
e.window.network.join(e.window.id, requested = 'n' not in e.switches)
|
||||
else:
|
||||
raise core.events.CommandError("You must supply a channel.")
|
||||
|
||||
def onCommandPart(e):
|
||||
if e.args:
|
||||
if e.network.status >= irc.INITIALIZING:
|
||||
e.network.part(' '.join(e.args), requested = 'n' not in e.switches)
|
||||
else:
|
||||
raise core.events.CommandError("We're not connected.")
|
||||
elif isinstance(e.window, windows.ChannelWindow):
|
||||
e.window.network.part(e.window.id, requested = 'n' not in e.switches)
|
||||
else:
|
||||
raise core.events.CommandError("You must supply a channel.")
|
||||
|
||||
def onCommandHop(e):
|
||||
if e.args:
|
||||
if e.network.status >= irc.INITIALIZING:
|
||||
e.network.part(e.args[0], requested = False)
|
||||
e.network.join(' '.join(e.args), requested = False)
|
||||
else:
|
||||
raise core.events.CommandError("We're not connected.")
|
||||
elif isinstance(e.window, windows.ChannelWindow):
|
||||
e.window.network.part(e.window.id, requested = False)
|
||||
e.window.network.join(e.window.id, requested = False)
|
||||
else:
|
||||
raise core.events.CommandError("You must supply a channel.")
|
||||
|
||||
#this should be used whereever a new irc.Network may need to be created
|
||||
def server(server=None,port=6667,network=None,connect=True):
|
||||
network_info = {}
|
||||
|
||||
if server:
|
||||
network_info["name"] = server
|
||||
network_info["server"] = server
|
||||
if port:
|
||||
network_info["port"] = port
|
||||
get_network_info(server, network_info)
|
||||
|
||||
if not network:
|
||||
network = irc.Network(**network_info)
|
||||
windows.new(windows.StatusWindow, network, "status").activate()
|
||||
else:
|
||||
if "server" in network_info:
|
||||
network.name = network_info['name']
|
||||
network.server = network_info['server']
|
||||
if not network.status:
|
||||
#window = windows.get_default(network)
|
||||
window = core.window
|
||||
if window:
|
||||
window.update()
|
||||
if "port" in network_info:
|
||||
network.port = network_info["port"]
|
||||
|
||||
if network.status:
|
||||
network.quit()
|
||||
if connect:
|
||||
network.connect()
|
||||
core.window.write("* Connecting to %s on port %s" % (network.server, network.port))
|
||||
#windows.get_default(network).write(
|
||||
# "* Connecting to %s on port %s" % (network.server, network.port)
|
||||
# )
|
||||
|
||||
return network
|
||||
|
||||
def onCommandServer(e):
|
||||
host = port = None
|
||||
|
||||
if e.args:
|
||||
host = e.args[0]
|
||||
|
||||
if ':' in host:
|
||||
host, port = host.rsplit(':', 1)
|
||||
port = int(port)
|
||||
|
||||
elif len(e.args) > 1:
|
||||
port = int(e.args[1])
|
||||
|
||||
else:
|
||||
port = 6667
|
||||
|
||||
if 'm' in e.switches:
|
||||
network = None
|
||||
else:
|
||||
network = e.network
|
||||
|
||||
server(server=host, port=port, network=network, connect='o' not in e.switches)
|
||||
|
||||
#see http://www.w3.org/Addressing/draft-mirashi-url-irc-01.txt
|
||||
def onCommandIrcurl(e):
|
||||
url = e.args[0]
|
||||
|
||||
if url.startswith('irc://'):
|
||||
url = url[6:]
|
||||
|
||||
if not url.startswith('/'):
|
||||
host, target = url.rsplit('/',1)
|
||||
if ':' in host:
|
||||
host, port = host.rsplit(':',1)
|
||||
else:
|
||||
port = 6667
|
||||
else:
|
||||
host = None
|
||||
port = 6667
|
||||
target = url
|
||||
|
||||
if host:
|
||||
if e.network and e.network.server == host:
|
||||
network = e.network
|
||||
else:
|
||||
for w in list(windows.manager):
|
||||
if w.network and w.network.server == host:
|
||||
network = w.network
|
||||
break
|
||||
else:
|
||||
for w in list(windows.manager):
|
||||
if w.network and w.network.server == 'irc.default.org':
|
||||
network = server(host,port,w.network)
|
||||
break
|
||||
else:
|
||||
network = server(host,port)
|
||||
|
||||
if ',' in target:
|
||||
target, modifiers = target.split(',',1)
|
||||
action = ''
|
||||
else:
|
||||
target = unquote(target)
|
||||
if target[0] not in '#&+':
|
||||
target = '#'+target
|
||||
action = 'join %s' % target
|
||||
|
||||
if network.status == irc.CONNECTED:
|
||||
core.events.run(action, windows.get_default(network), network)
|
||||
else:
|
||||
if not hasattr(network,'temp_perform'):
|
||||
network.temp_perform = [action]
|
||||
else:
|
||||
network.temp_perform.append(action)
|
||||
|
||||
#commands that we need to add a : to but otherwise can send unchanged
|
||||
#the dictionary contains the number of arguments we take without adding the :
|
||||
trailing = {
|
||||
'away':0,
|
||||
'cnotice':2,
|
||||
'cprivmsg':2,
|
||||
'kick':2,
|
||||
'kill':1,
|
||||
'part':1,
|
||||
'squery':1,
|
||||
'squit':1,
|
||||
'topic':1,
|
||||
'wallops':0,
|
||||
}
|
||||
|
||||
needschan = {
|
||||
'topic':0,
|
||||
'invite':1,
|
||||
'kick':0,
|
||||
# 'mode':0, #this is commonly used for channels, but can apply to users
|
||||
# 'names':0, #with no parameters, this is supposed to give a list of all users; we may be able to safely ignore that.
|
||||
}
|
||||
|
||||
def setupCommand(e):
|
||||
if not e.done:
|
||||
if e.name in needschan and isinstance(e.window, windows.ChannelWindow):
|
||||
valid_chan_prefixes = e.network.isupport.get('CHANTYPES', '#&+')
|
||||
chan_pos = needschan[e.name]
|
||||
|
||||
if len(e.args) > chan_pos:
|
||||
if not e.args[chan_pos] or e.args[chan_pos][0] not in valid_chan_prefixes:
|
||||
e.args.insert(chan_pos, e.window.id)
|
||||
else:
|
||||
e.args.append(e.window.id)
|
||||
|
||||
if e.name in trailing:
|
||||
trailing_pos = trailing[e.name]
|
||||
|
||||
if len(e.args) > trailing_pos:
|
||||
e.args[trailing_pos] = ':%s' % e.args[trailing_pos]
|
||||
|
||||
e.text = '%s %s' % (e.name, ' '.join(e.args))
|
||||
|
||||
def setdownCommand(e):
|
||||
if not e.done and e.network.status >= irc.INITIALIZING:
|
||||
e.network.raw(e.text)
|
||||
e.done = True
|
||||
|
||||
def get_network_info(name, network_info):
|
||||
conf_info = conf.get('networks', {}).get(name)
|
||||
|
||||
if conf_info:
|
||||
network_info['server'] = name
|
||||
network_info.update(conf_info)
|
||||
|
||||
def onStart(e):
|
||||
for network in conf.get('start_networks', []):
|
||||
server(server=network)
|
||||
|
||||
def onConnect(e):
|
||||
network_info = conf.get('networks', {}).get(e.network.name, {})
|
||||
|
||||
for command in network_info.get('perform', []):
|
||||
while command.startswith(COMMAND_PREFIX):
|
||||
command = command[len(COMMAND_PREFIX):]
|
||||
core.events.run(command, e.window, e.network)
|
||||
|
||||
tojoin = ','.join(network_info.get('join', []))
|
||||
if tojoin:
|
||||
core.events.run('join -n %s' % tojoin, e.window, e.network)
|
||||
|
||||
if hasattr(e.network,'temp_perform'):
|
||||
for command in e.network.temp_perform:
|
||||
core.events.run(command, e.window, e.network)
|
||||
del e.network.temp_perform
|
||||
|
||||
def isautojoin(network, channel):
|
||||
try:
|
||||
joinlist = conf['networks'][network.name]['join']
|
||||
except KeyError:
|
||||
return False
|
||||
normchannel = network.norm_case(channel)
|
||||
for chan in joinlist:
|
||||
if normchannel == network.norm_case(chan):
|
||||
return True
|
||||
return False
|
||||
|
||||
def setautojoin(network, channel):
|
||||
if 'networks' not in conf:
|
||||
conf['networks'] = networks = {}
|
||||
else:
|
||||
networks = conf['networks']
|
||||
if network.name not in networks:
|
||||
networks[network.name] = network_settings = {}
|
||||
if 'start_networks' not in conf:
|
||||
conf['start_networks'] = []
|
||||
conf['start_networks'].append(network.name)
|
||||
else:
|
||||
network_settings = networks[network.name]
|
||||
|
||||
if 'join' not in network_settings:
|
||||
network_settings['join'] = [channel]
|
||||
else:
|
||||
network_settings['join'].append(channel)
|
||||
|
||||
def unsetautojoin(network, channel):
|
||||
try:
|
||||
joinlist = conf['networks'][network.name]['join']
|
||||
except KeyError:
|
||||
return False
|
||||
normchannel = network.norm_case(channel)
|
||||
for i, chan in enumerate(joinlist[:]):
|
||||
if normchannel == network.norm_case(chan):
|
||||
joinlist.pop(i)
|
||||
|
||||
def onChannelMenu(e):
|
||||
def toggle_join():
|
||||
if isautojoin(e.network, e.channel):
|
||||
unsetautojoin(e.network, e.channel)
|
||||
else:
|
||||
setautojoin(e.network, e.channel)
|
||||
|
||||
e.menu.append(('Autojoin', isautojoin(e.network, e.channel), toggle_join))
|
||||
@@ -0,0 +1,70 @@
|
||||
import windows
|
||||
import widgets
|
||||
import irc
|
||||
|
||||
shortcuts = {
|
||||
'^b': '\x02',
|
||||
'^u': '\x1F',
|
||||
'^r': '\x16',
|
||||
'^k': '\x03',
|
||||
'^l': '\x04',
|
||||
'^o': '\x0F',
|
||||
}
|
||||
|
||||
def onKeyPress(e):
|
||||
if e.key in shortcuts:
|
||||
e.window.input.insert(shortcuts[e.key])
|
||||
|
||||
elif e.key == '!c':
|
||||
e.window.output.copy()
|
||||
|
||||
elif e.key == 'Page_Up':
|
||||
e.window.output.y = e.window.output.y - e.window.output.height / 2
|
||||
|
||||
elif e.key == 'Page_Down':
|
||||
e.window.output.y = e.window.output.y + e.window.output.height / 2
|
||||
|
||||
elif e.key == '^Home':
|
||||
e.window.output.y = 0
|
||||
|
||||
elif e.key == '^End':
|
||||
e.window.output.y = e.window.output.ymax
|
||||
|
||||
elif e.key in ('^Page_Up', '^Page_Down'):
|
||||
winlist = list(windows.manager)
|
||||
index = winlist.index(e.window) + ((e.key == '^Page_Down') and 1 or -1)
|
||||
if 0 <= index < len(winlist):
|
||||
winlist[index].activate()
|
||||
|
||||
elif e.key == '!a':
|
||||
winlist = list(windows.manager)
|
||||
winlist = winlist[winlist.index(e.window):]+winlist
|
||||
w = [w for w in winlist if widgets.HILIT in w.activity]
|
||||
|
||||
if not w:
|
||||
w = [w for w in winlist if widgets.TEXT in w.activity]
|
||||
|
||||
if w:
|
||||
windows.manager.set_active(w[0])
|
||||
|
||||
# tabbed browsing
|
||||
elif e.key == '^t':
|
||||
windows.new(windows.StatusWindow, irc.Network(), 'status').activate()
|
||||
|
||||
elif e.key == '^w':
|
||||
windows.manager.get_active().close()
|
||||
|
||||
elif e.key == '^f':
|
||||
window = windows.manager.get_active()
|
||||
|
||||
find = widgets.FindBox(window)
|
||||
|
||||
window.pack_start(find, expand=False)
|
||||
|
||||
find.textbox.grab_focus()
|
||||
|
||||
elif len(e.key) == 2 and e.key.startswith('!') and e.key[1].isdigit():
|
||||
n = int(e.key[1])
|
||||
if n and n <= len(core.manager):
|
||||
list(core.manager)[n-1].activate()
|
||||
#else e.key == "!0"
|
||||
@@ -0,0 +1,366 @@
|
||||
import time
|
||||
|
||||
import windows
|
||||
import widgets
|
||||
import chaninfo
|
||||
|
||||
from conf import conf
|
||||
|
||||
textareas = {}
|
||||
if 'font' in conf:
|
||||
textareas['font'] = conf['font']
|
||||
if 'bg_color' in conf:
|
||||
textareas['bg'] = conf['bg_color']
|
||||
if 'fg_color' in conf:
|
||||
textareas['fg'] = conf['fg_color']
|
||||
|
||||
widgets.set_style("view", textareas)
|
||||
widgets.set_style("nicklist", textareas)
|
||||
|
||||
#copied pretty directly from something that was probably copied from wine sources
|
||||
def RGBtoHSL(r, g, b):
|
||||
maxval = max(r, g, b)
|
||||
minval = min(r, g, b)
|
||||
|
||||
luminosity = ((maxval + minval) * 240 + 255) // 510
|
||||
|
||||
if maxval == minval:
|
||||
saturation = 0
|
||||
hue = 160
|
||||
else:
|
||||
delta = maxval - minval
|
||||
|
||||
if luminosity <= 120:
|
||||
saturation = ((maxval+minval)//2 + delta*240) // (maxval + minval)
|
||||
else:
|
||||
saturation = ((150-maxval-minval)//2 + delta*240) // (150-maxval-minval)
|
||||
|
||||
#sigh..
|
||||
rnorm = (delta//2 + maxval*40 - r*40)//delta
|
||||
gnorm = (delta//2 + maxval*40 - g*40)//delta
|
||||
bnorm = (delta//2 + maxval*40 - b*40)//delta
|
||||
|
||||
if r == maxval:
|
||||
hue = bnorm-gnorm
|
||||
elif g == maxval:
|
||||
hue = 80+rnorm-bnorm
|
||||
else:
|
||||
hue = 160+gnorm-rnorm
|
||||
hue = hue % 240
|
||||
return hue, saturation, luminosity
|
||||
|
||||
#copied from the same place
|
||||
def huetoRGB(hue, mid1, mid2):
|
||||
hue = hue % 240
|
||||
|
||||
if hue > 160:
|
||||
return mid1
|
||||
elif hue > 120:
|
||||
hue = 160 - hue
|
||||
elif hue > 40:
|
||||
return mid2
|
||||
return ((hue * (mid2 - mid1) + 20) // 40) + mid1
|
||||
|
||||
#this too
|
||||
def HSLtoRGB(hue, saturation, luminosity):
|
||||
if saturation != 0:
|
||||
if luminosity > 120:
|
||||
mid2 = saturation + luminosity - (saturation * luminosity + 120)//240
|
||||
else:
|
||||
mid2 = ((saturation + 240) * luminosity + 120)//240
|
||||
|
||||
mid1 = luminosity * 2 - mid2
|
||||
|
||||
return tuple((huetoRGB(hue+x, mid1, mid2) * 255 + 120) // 240 for x in (80,0,-80))
|
||||
else:
|
||||
value = luminosity * 255 // 240
|
||||
return value, value, value
|
||||
|
||||
def gethashcolor(string):
|
||||
h = hash(string)
|
||||
rgb = HSLtoRGB(h%241, 100-h//241%61, 90)
|
||||
return "%02x%02x%02x" % rgb
|
||||
|
||||
#take an event e and trigger the highlight event if necessary
|
||||
def hilight_text(e):
|
||||
if not hasattr(e, 'Highlight'):
|
||||
e.Highlight = []
|
||||
core.events.trigger('Highlight', e)
|
||||
|
||||
#hilight own nick
|
||||
def onHighlight(e):
|
||||
lowertext = e.text.lower()
|
||||
for word in conf.get('highlight_words', []) + [e.network.me] + e.network.nicks:
|
||||
lowerword = word.lower()
|
||||
pos = lowertext.find(lowerword, 0)
|
||||
while pos != -1:
|
||||
e.Highlight.append((pos, pos+len(word)))
|
||||
pos = lowertext.find(lowerword, pos+1)
|
||||
|
||||
def prefix(e):
|
||||
return time.strftime(conf.get('timestamp', ''))
|
||||
|
||||
def getsourcecolor(e):
|
||||
address = getattr(e, "address", "")
|
||||
if address:
|
||||
if e.network.me == e.source:
|
||||
e.network._my_address = address
|
||||
elif e.network.me == e.source:
|
||||
address = getattr(e.network, "_my_address", "")
|
||||
if '@' in address:
|
||||
address = address.split('@')[1]
|
||||
if not address:
|
||||
address = e.source
|
||||
return "\x04%s" % gethashcolor(address)
|
||||
|
||||
def format_source(e):
|
||||
highlight = getattr(e, "Highlight", "") and '\x02' or ''
|
||||
return "%s\x04%s%s" % (highlight, getsourcecolor(e), e.source)
|
||||
|
||||
def format_info_source(e):
|
||||
if e.source == e.network.me:
|
||||
return "\x04%sYou" % (getsourcecolor(e))
|
||||
else:
|
||||
return "\x04%s%s" % (getsourcecolor(e), e.source)
|
||||
|
||||
def address(e):
|
||||
#if e.source != e.network.me:
|
||||
# return "%s " % info_in_brackets(e.address)
|
||||
#else:
|
||||
# return ""
|
||||
return ""
|
||||
|
||||
def text(e):
|
||||
if e.text:
|
||||
#return " %s" % info_in_brackets(e.text)
|
||||
return ": \x0F%s" % e.text
|
||||
else:
|
||||
return ""
|
||||
|
||||
def info_in_brackets(text):
|
||||
return "(\x044881b6%s\x0F)" % text
|
||||
|
||||
def pretty_time(secs):
|
||||
times = (
|
||||
#("years", "year", 31556952),
|
||||
("weeks", "week", 604800),
|
||||
("days", "day", 86400),
|
||||
("hours", "hour", 3600),
|
||||
("minutes", "minute", 60),
|
||||
("seconds", "second", 1),
|
||||
)
|
||||
if secs == 0:
|
||||
return "0 seconds"
|
||||
result = ""
|
||||
for plural, singular, amount in times:
|
||||
n, secs = divmod(secs, amount)
|
||||
if n == 1:
|
||||
result = result + " %s %s" % (n, singular)
|
||||
elif n:
|
||||
result = result + " %s %s" % (n, plural)
|
||||
return result[1:]
|
||||
|
||||
def onText(e):
|
||||
hilight_text(e)
|
||||
color = getsourcecolor(e)
|
||||
to_write = prefix(e)
|
||||
if e.network.me == e.target: # this is a pm
|
||||
if e.window.id == e.network.norm_case(e.source):
|
||||
to_write += "\x02<\x0F%s\x0F\x02>\x0F " % (format_source(e))
|
||||
else:
|
||||
to_write += "\x02*\x0F%s\x0F\x02*\x0F " % (format_source(e))
|
||||
else:
|
||||
if e.window.id == e.network.norm_case(e.target):
|
||||
to_write += "\x02<\x0F%s\x0F\x02>\x0F " % (format_source(e))
|
||||
else:
|
||||
to_write += "\x02<\x0F%s:%s\x0F\x02>\x0F " % (format_source(e), e.target)
|
||||
to_write += e.text
|
||||
|
||||
if e.Highlight:
|
||||
e.window.write(to_write, widgets.HILIT)
|
||||
else:
|
||||
e.window.write(to_write, widgets.TEXT)
|
||||
|
||||
def onOwnText(e):
|
||||
color = getsourcecolor(e)
|
||||
to_write = prefix(e)
|
||||
if e.window.id == e.network.norm_case(e.target):
|
||||
to_write += "\x02<\x0F%s\x0F\x02>\x0F %s" % (format_source(e), e.text)
|
||||
else:
|
||||
to_write += "%s->\x0F \x02*\x0F%s\x0F\x02*\x0F %s" % (color, e.target, e.text)
|
||||
|
||||
e.window.write(to_write)
|
||||
|
||||
def onAction(e):
|
||||
hilight_text(e)
|
||||
color = color = getsourcecolor(e)
|
||||
to_write = "%s\x02*\x0F %s\x0F %s" % (prefix(e), format_source(e), e.text)
|
||||
|
||||
if e.Highlight:
|
||||
e.window.write(to_write, widgets.HILIT)
|
||||
else:
|
||||
e.window.write(to_write, widgets.TEXT)
|
||||
|
||||
def onOwnAction(e):
|
||||
color = getsourcecolor(e)
|
||||
to_write = "%s\x02*\x0F %s\x0F %s" % (prefix(e), format_source(e), e.text)
|
||||
|
||||
e.window.write(to_write)
|
||||
|
||||
def onNotice(e):
|
||||
hilight_text(e)
|
||||
color = getsourcecolor(e)
|
||||
to_write = prefix(e)
|
||||
if e.network.me == e.target: # this is a pm
|
||||
to_write += "\x02-\x0F%s\x0F\x02-\x0F " % (format_source(e))
|
||||
else:
|
||||
to_write += "\x02-\x0F%s:%s\x0F\x02-\x0F " % (format_source(e), e.target)
|
||||
to_write += e.text
|
||||
|
||||
e.window.write(to_write, (e.Highlight and widgets.HILIT) or widgets.TEXT)
|
||||
|
||||
def onOwnNotice(e):
|
||||
color = getsourcecolor(e)
|
||||
to_write = "%s-> \x02-\x02%s\x0F\x02-\x0F %s" % (prefix(e), e.target, e.text)
|
||||
|
||||
e.window.write(to_write)
|
||||
|
||||
def onCtcp(e):
|
||||
color = getsourcecolor(e)
|
||||
to_write = "%s\x02[\x02%s\x0F\x02]\x0F %s" % (prefix(e), format_source(e), e.text)
|
||||
|
||||
if not e.quiet:
|
||||
e.window.write(to_write)
|
||||
|
||||
def onCtcpReply(e):
|
||||
color = getsourcecolor(e)
|
||||
to_write = "%s%s--- %s reply from %s:\x0F %s" % (prefix(e), color, e.name.capitalize(), format_source(e), ' '.join(e.args))
|
||||
|
||||
window = windows.manager.get_active()
|
||||
if window.network != e.network:
|
||||
window = windows.get_default(e.network)
|
||||
window.write(to_write, widgets.TEXT)
|
||||
|
||||
def onJoin(e):
|
||||
if e.source == e.network.me:
|
||||
to_write = "%s%s %sjoin %s" % (prefix(e), format_info_source(e), address(e), e.target)
|
||||
else:
|
||||
to_write = "%s%s %sjoins %s" % (prefix(e), format_info_source(e), address(e), e.target)
|
||||
|
||||
e.window.write(to_write)
|
||||
|
||||
def onPart(e):
|
||||
if e.source == e.network.me:
|
||||
to_write = "%s%s leave %s%s" % (prefix(e), format_info_source(e), e.target, text(e))
|
||||
else:
|
||||
to_write = "%s%s leaves %s%s" % (prefix(e), format_info_source(e), e.target, text(e))
|
||||
|
||||
e.window.write(to_write)
|
||||
|
||||
def onKick(e):
|
||||
if e.source == e.network.me:
|
||||
to_write = "%s%s kick %s%s" % (prefix(e), format_info_source(e), e.target, text(e))
|
||||
else:
|
||||
to_write = "%s%s kicks %s%s" % (prefix(e), format_info_source(e), e.target, text(e))
|
||||
|
||||
e.window.write(to_write, (e.target == e.network.me and widgets.HILIT) or widgets.EVENT)
|
||||
|
||||
def onMode(e):
|
||||
if e.source == e.network.me:
|
||||
to_write = "%s%s set mode:\x0F %s" % (prefix(e), format_info_source(e), e.text)
|
||||
else:
|
||||
to_write = "%s%s sets mode:\x0F %s" % (prefix(e), format_info_source(e), e.text)
|
||||
|
||||
e.window.write(to_write)
|
||||
|
||||
def onQuit(e):
|
||||
to_write = "%s%s leaves%s" % (prefix(e), format_info_source(e), text(e))
|
||||
|
||||
for channame in chaninfo.channels(e.network):
|
||||
if chaninfo.ison(e.network, channame, e.source):
|
||||
window = windows.get(windows.ChannelWindow, e.network, channame, core)
|
||||
if window:
|
||||
window.write(to_write)
|
||||
|
||||
def onNick(e):
|
||||
color = getsourcecolor(e)
|
||||
if e.source == e.network.me:
|
||||
to_write = "%s%sYou are now known as %s" % (prefix(e), color, e.target)
|
||||
else:
|
||||
to_write = "%s%s%s is now known as %s" % (prefix(e), color, e.source, e.target)
|
||||
|
||||
if e.source == e.network.me:
|
||||
for window in windows.get_with(core.manager, network=e.network):
|
||||
window.write(to_write)
|
||||
else:
|
||||
for channame in chaninfo.channels(e.network):
|
||||
if chaninfo.ison(e.network,channame,e.source):
|
||||
window = windows.get(windows.ChannelWindow, e.network, channame)
|
||||
if window:
|
||||
window.write(to_write)
|
||||
|
||||
def onTopic(e):
|
||||
if e.source == e.network.me:
|
||||
to_write = "%s%s set topic:\x0F %s" % (prefix(e), format_info_source(e), e.text)
|
||||
else:
|
||||
to_write = "%s%s sets topic:\x0F %s" % (prefix(e), format_info_source(e), e.text)
|
||||
|
||||
e.window.write(to_write)
|
||||
|
||||
def onRaw(e):
|
||||
if not e.quiet:
|
||||
if e.msg[1].isdigit():
|
||||
if e.msg[1] == '332':
|
||||
window = windows.get(windows.ChannelWindow, e.network, e.msg[3], core) or e.window
|
||||
window.write(
|
||||
"%sTopic on %s is: %s" %
|
||||
(prefix(e), e.msg[3], e.text)
|
||||
)
|
||||
|
||||
elif e.msg[1] == '333':
|
||||
window = windows.get(windows.ChannelWindow, e.network, e.msg[3], core) or e.window
|
||||
window.write(
|
||||
"%sTopic on %s set by %s at time %s" %
|
||||
(prefix(e), e.msg[3], e.msg[4], time.ctime(int(e.msg[5])))
|
||||
)
|
||||
|
||||
elif e.msg[1] == '329': #RPL_CREATIONTIME
|
||||
pass
|
||||
|
||||
elif e.msg[1] == '311': #RPL_WHOISUSER
|
||||
e.window.write("* %s is %s@%s * %s" % (e.msg[3], e.msg[4], e.msg[5], e.msg[7]))
|
||||
|
||||
elif e.msg[1] == '312': #RPL_WHOISSERVER
|
||||
e.window.write("* %s on %s (%s)" % (e.msg[3], e.msg[4], e.msg[5]))
|
||||
|
||||
elif e.msg[1] == '317': #RPL_WHOISIDLE
|
||||
e.window.write("* %s has been idle for %s" % (e.msg[3], pretty_time(int(e.msg[4]))))
|
||||
if e.msg[5].isdigit():
|
||||
e.window.write("* %s signed on %s" % (e.msg[3], time.ctime(int(e.msg[5]))))
|
||||
|
||||
elif e.msg[1] == '319': #RPL_WHOISCHANNELS
|
||||
e.window.write("* %s on channels: %s" % (e.msg[3], e.msg[4]))
|
||||
|
||||
elif e.msg[1] == '330': #RPL_WHOISACCOUNT
|
||||
#this appears to conflict with another raw, so if there's anything weird about it,
|
||||
# we fall back on the default
|
||||
if len(e.msg) == 6 and not e.msg[4].isdigit() and not e.msg[5].isdigit():
|
||||
e.window.write("* %s %s %s" % (e.msg[3], e.msg[5], e.msg[4]))
|
||||
else:
|
||||
e.window.write("* %s" % ' '.join(e.msg[3:]))
|
||||
|
||||
else:
|
||||
e.window.write("* %s" % ' '.join(e.msg[3:]))
|
||||
elif e.msg[1] == 'ERROR':
|
||||
e.window.write("Error: %s" % e.text)
|
||||
|
||||
def onDisconnect(e):
|
||||
to_write = '%s* Disconnected' % prefix(e)
|
||||
if e.error:
|
||||
to_write += ' (%s)' % e.error
|
||||
|
||||
for window in windows.get_with(network=e.network):
|
||||
if isinstance(window, windows.StatusWindow):
|
||||
window.write(to_write, widgets.TEXT)
|
||||
else:
|
||||
window.write(to_write, widgets.EVENT)
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
import time
|
||||
|
||||
import ui
|
||||
from conf import conf
|
||||
|
||||
def setupRaw(e):
|
||||
e.network._message_timeout = False
|
||||
|
||||
def onSocketConnect(e):
|
||||
timeout = conf.get("server_traffic_timeout", 120)*1000
|
||||
e.network._message_timeout = False
|
||||
if timeout:
|
||||
e.network._message_timeout_source = ui.register_timer(timeout, check_timeout, e.network)
|
||||
else:
|
||||
e.network._message_timeout_source = None
|
||||
|
||||
def check_timeout(network):
|
||||
if network._message_timeout:
|
||||
network.raw("PING %s" % network.me)
|
||||
timeout = conf.get("server_death_timeout", 240)*1000
|
||||
network._message_timeout_source = ui.register_timer(timeout, check_death_timeout, network)
|
||||
return False
|
||||
else:
|
||||
network._message_timeout = True
|
||||
return True # call this function again
|
||||
|
||||
def check_death_timeout(network):
|
||||
if network._message_timeout:
|
||||
network.raw("QUIT :Server missing, presumed dead")
|
||||
network.disconnect(error="The server seems to have stopped talking to us")
|
||||
else:
|
||||
network._message_timeout = False
|
||||
timeout = conf.get("server_traffic_timeout", 120)*1000
|
||||
if timeout:
|
||||
network._message_timeout_source = ui.register_timer(timeout, check_timeout, network)
|
||||
else:
|
||||
network._message_timeout_source = None
|
||||
|
||||
def onDisconnect(e):
|
||||
try:
|
||||
if e.network._message_timeout_source:
|
||||
e.network._message_timeout_source.unregister()
|
||||
e.network._message_timeout_source = None
|
||||
except AttributeError:
|
||||
pass
|
||||
@@ -0,0 +1,132 @@
|
||||
import irc
|
||||
import ui
|
||||
import windows
|
||||
import irc_script
|
||||
from conf import conf
|
||||
|
||||
# FIXME: meh still might want rid of these, I'm not sure yet
|
||||
|
||||
def onActive(e):
|
||||
e.window.activity = None
|
||||
|
||||
ui.register_idle(windows.manager.set_title)
|
||||
|
||||
def setupNick(e):
|
||||
if e.source == e.network.me:
|
||||
for w in windows.get_with(core.manager, network=e.network):
|
||||
try:
|
||||
w.nick_label.update(e.target)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def onExit(e):
|
||||
for n in set(w.network for w in windows.manager):
|
||||
if n:
|
||||
n.quit()
|
||||
|
||||
def setupJoin(e):
|
||||
if e.source == e.network.me:
|
||||
window = windows.get(windows.StatusWindow, e.network, 'status', core)
|
||||
|
||||
if window and not conf.get('status'):
|
||||
window.mutate(windows.ChannelWindow, e.network, e.target)
|
||||
else:
|
||||
window = windows.new(windows.ChannelWindow, e.network, e.target, core)
|
||||
|
||||
if e.requested:
|
||||
window.activate()
|
||||
|
||||
e.window = windows.get(windows.ChannelWindow, e.network, e.target, core) or e.window
|
||||
|
||||
def setupText(e):
|
||||
if e.target == e.network.me:
|
||||
e.window = windows.new(windows.QueryWindow, e.network, e.source, core)
|
||||
else:
|
||||
e.window = \
|
||||
windows.get(windows.ChannelWindow, e.network, e.target, core) or \
|
||||
windows.get(windows.QueryWindow, e.network, e.source, core) or \
|
||||
e.window
|
||||
|
||||
setupAction = setupText
|
||||
|
||||
def setupNotice(e):
|
||||
if e.target != e.network.me:
|
||||
e.window = \
|
||||
windows.get(windows.ChannelWindow, e.network, e.target, core) or e.window
|
||||
|
||||
def setupOwnText(e):
|
||||
e.window = \
|
||||
windows.get(windows.ChannelWindow, e.network, e.target, core) or \
|
||||
windows.get(windows.QueryWindow, e.network, e.target, core) or \
|
||||
e.window
|
||||
|
||||
setupOwnAction = setupOwnText
|
||||
|
||||
def setdownPart(e):
|
||||
if e.source == e.network.me:
|
||||
window = windows.get(windows.ChannelWindow, e.network, e.target, core)
|
||||
|
||||
if window:
|
||||
cwindows = list(windows.get_with(
|
||||
network=window.network,
|
||||
wclass=windows.ChannelWindow
|
||||
))
|
||||
|
||||
if len(cwindows) == 1 and not list(windows.get_with(network=window.network, wclass=windows.StatusWindow)):
|
||||
window.mutate(windows.StatusWindow, e.network, 'status')
|
||||
if e.requested:
|
||||
window.activate()
|
||||
elif e.requested:
|
||||
window.close()
|
||||
|
||||
def onClose(e):
|
||||
nwindows = list(windows.get_with(core.manager, network=e.window.network))
|
||||
|
||||
if isinstance(e.window, windows.ChannelWindow):
|
||||
cwindows = list(windows.get_with(core.manager,
|
||||
network=e.window.network,
|
||||
wclass=windows.ChannelWindow
|
||||
))
|
||||
|
||||
#if we only have one window for the network, don't bother to part as
|
||||
# we'll soon be quitting anyway
|
||||
if len(nwindows) != 1 and irc_script.ischan(e.window.network, e.window.id):
|
||||
e.window.network.part(e.window.id)
|
||||
|
||||
if len(nwindows) == 1:
|
||||
core.events.trigger("CloseNetwork", window=e.window, network=e.window.network)
|
||||
|
||||
elif isinstance(e.window, windows.StatusWindow) and conf.get('status'):
|
||||
core.events.trigger("CloseNetwork", window=e.window, network=e.window.network)
|
||||
for window in nwindows:
|
||||
if window != e.window:
|
||||
window.close()
|
||||
|
||||
if len(core.manager) == 1:
|
||||
windows.new(windows.StatusWindow, irc.Network(), "status", core)
|
||||
|
||||
def onConnecting(e):
|
||||
return
|
||||
window = windows.get_default(e.network)
|
||||
if window:
|
||||
window.update()
|
||||
|
||||
onDisconnect = onConnecting
|
||||
|
||||
def setupPart(e):
|
||||
e.window = windows.get(windows.ChannelWindow, e.network, e.target, core) or e.window
|
||||
|
||||
setupTopic = setupPart
|
||||
|
||||
def setupKick(e):
|
||||
e.window = windows.get(windows.ChannelWindow, e.network, e.channel, core) or e.window
|
||||
|
||||
def setupMode(e):
|
||||
if e.target != e.network.me:
|
||||
e.window = windows.get(windows.ChannelWindow, e.network, e.target, core) or e.window
|
||||
|
||||
def onWindowMenu(e):
|
||||
if isinstance(e.window, windows.ChannelWindow):
|
||||
e.channel = e.window.id
|
||||
e.network = e.window.network
|
||||
core.events.trigger('ChannelMenu', e)
|
||||
Reference in New Issue
Block a user