<commit_before># coding: UTF-8
"""
Copyright (c) 2008 <NAME>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
"""

from gettext import gettext as _

import gtk
import gobject
import logging
import string
from dbus import UInt64
import time as mtime

import com
import config

import lib.gui_control as gui
from lib import key_dialog
from lib import contrast
from lib import dcc_dialog
from lib import tab as tabs

from helper import color

from com import sushi, parse_from

from typecheck import types

signals = {}
restore_list = []

def setup():
	sushi.g_connect("maki-connected", handle_maki_connect_cb)
	sushi.g_connect("maki-disconnected", handle_maki_disconnect_cb)

"""
One should use this methods to connect to maki's
signals. This API features automatic reconnection
of the registered signals if the connection to
maki was reset.
- connect_signal(<name>,<handler>)
- disconnect_signal(<name>,<handler>)
"""

@types (signal=basestring)
def connect_signal (signal, handler):
	""" connect handler to signal """
	global signals

	if not signals.has_key (signal):
	  	signals[signal] = {}

	if signals[signal].has_key(handler):
		# no doubles
		return

	signals[signal][handler] = sushi.connect_to_signal (signal, handler)

@types (signal=basestring)
def disconnect_signal (signal, handler):
	""" disconnect handler from signal """
	global signals

	try:
		ob = signals[signal][handler]
	except KeyError:
		return
	else:
		ob.remove()
		del signals[signal][handler]

def _connect_signals():

	# Message-Signals
	connect_signal("message", userMessage_cb)
	connect_signal("notice", userNotice_cb)
	connect_signal("action", userAction_cb)
	connect_signal("away_message", userAwayMessage_cb)
	connect_signal("ctcp", userCTCP_cb)
	connect_signal("error", userError_cb)

	# action signals
	connect_signal("part", userPart_cb)
	connect_signal("join", userJoin_cb)
	connect_signal("names", userNames_cb)
	connect_signal("quit", userQuit_cb)
	connect_signal("kick", userKick_cb)
	connect_signal("nick", userNick_cb)
	connect_signal("away", userAway_cb)
	connect_signal("back", userBack_cb)
	connect_signal("mode", userMode_cb)
	connect_signal("oper", userOper_cb)

	# Server-Signals
	connect_signal("connect", serverConnect_cb)
	connect_signal("connected", serverConnected_cb)
	connect_signal("motd", serverMOTD_cb)
	connect_signal("whois", whois_cb) # FIXME: only on demand! like list
	connect_signal("dcc_send", dcc_send_cb)

	# Channel-Signals
	connect_signal("topic", channelTopic_cb)
	connect_signal("banlist", channelBanlist_cb)

	# Maki signals
	connect_signal("shutdown", makiShutdown_cb)

def _restore_signals():
	global restore_list

	for (signal, handler) in restore_list:
		connect_signal(signal, handler)

def handle_maki_disconnect_cb(sushi):
	global signals
	global restore_list

	for signal in signals:
		for handler in signals[signal]:
			signals[signal][handler].remove()
			restore_list.append((signal, handler))

	signals = {}

def handle_maki_connect_cb(sushi):
	""" connect to the important signals of maki
		or, if we've done that before, reconnect
		that signals (and all other registered)
	"""

	self = handle_maki_connect_cb
	try:
		self.init
	except AttributeError:
		self.init = True
	else:
		self.init = False

	if self.init:
		_connect_signals()
	else:
		_restore_signals()

	_add_servers()

@types (server = basestring)
def _setup_server(server):
	tab = gui.tabs.create_server(server)

	gui.tabs.add_tab(None, tab,
		update_shortcuts = config.get_bool("tekka","server_shortcuts"))

	return tab

def _add_servers():
	""" Adds all servers to tekka which are reported by maki. """
	# in case we're reconnecting, clear all stuff
	gui.widgets.get_widget("serverTree").get_model().clear()

	for server in sushi.servers():
		tab = _setup_server(server)
		tab.connected = True
		_add_channels(tab)

	try:
		toSwitch = gui.tabs.get_all_tabs()[1]
	except IndexError:
		return
	else:
		gui.tabs.switch_to_path(toSwitch.path)


@types (server_tab = tabs.TekkaServer)
def _add_channels(server_tab):
	"""
		Adds all channels to tekka wich are reported by maki.
	"""
	channels = sushi.channels(server_tab.name)

	for channel in channels:

		add = False
		nicks, prefixes = sushi.channel_nicks(server_tab.name, channel)

		tab = gui.tabs.search_tab(server_tab.name, channel)

		if not tab:
			tab = gui.tabs.create_channel(server_tab, channel)
			add = True

		tab.nickList.clear()
		tab.nickList.add_nicks(nicks, prefixes)

		tab.topic = sushi.channel_topic(server_tab.name, channel)
		tab.topicsetter = ""

		if gui.tabs.is_active(tab):
			gui.set_topic(gui.markup_escape(tab.topic))
			gui.set_user_count(
				len(tab.nickList),
				tab.nickList.get_operator_count())

		# TODO: handle topic setter
		tab.joined = True
		tab.connected = True

		if add:
			gui.tabs.add_tab(server_tab, tab, update_shortcuts = False)
			gui.print_last_log(server_tab.name, channel)

		topic = sushi.channel_topic(server_tab.name, channel)
		channelTopic_cb(mtime.time(), server_tab.name, "", channel, topic)

	gui.updateServerTreeShortcuts()

def isHighlighted (server_tab, text):
	def has_highlight(text, needle):
		punctuation = string.punctuation + " \n\t"
		ln = len(needle)
		for line in text.split("\n"):
			i = line.find(needle)
			if i >= 0:
				if (line[i-1:i] in punctuation
				and line[ln+i:ln+i+1] in punctuation):
					return True
		return False

	highlightwords = config.get_list("chatting", "highlight_words", [])
	highlightwords.append(server_tab.nick)

	for word in highlightwords:
		if has_highlight(text, word):
			return True
	return False

@types (server = basestring, name = basestring)
def _createTab (server, name):
	""" check if tab exists, create it if not, return the tab """
	server_tab = gui.tabs.search_tab(server)
	tab = gui.tabs.search_tab(server, name)

	if not server_tab:
		raise Exception(
		"No server tab in _createTab(%s, %s)" % (server,name))

	if not tab:
		if name[0] in server_tab.support_chantypes:
			tab = gui.tabs.create_channel(server_tab, name)
		else:
			tab = gui.tabs.create_query(server_tab, name)

		tab.connected = True
		gui.tabs.add_tab(server_tab, tab)
		gui.print_last_log(server, name)

	if tab.name != name:
		# the name of the tab differs from the
		# real nick, correct this.
		tab.name = name

	return tab

def _getPrefix(server, channel, nick):
	tab = gui.tabs.search_tab(server, channel)

	if tab and tab.is_channel():
		return tab.nickList.get_prefix(nick)
	else:
		return ""

@types (tab = tabs.TekkaTab, what = basestring, own = bool)
def _hide_output(tab, what, own = False):
	""" Returns bool.
		Check if the message type determined by "what"
		shall be hidden or not.
		tab should be a TekkaServer, -Channel or -Query
	"""
	if type(tab) == tabs.TekkaChannel:
		cat = "channel_%s_%s" % (
					tab.server.name.lower(),
					tab.name.lower())
	elif type(tab) == tabs.TekkaQuery:
		cat = "query_%s_%s" % (
					tab.server.name.lower(),
					tab.name.lower())
	else:
		return False

	hide = what in config.get_list(cat, "hide", [])
	hideOwn = what in config.get_list(cat, "hide_own", [])

	return ((hide and not own)
		or (own and hideOwn)
		or (hide and own and not hideOwn))

@types (servertab = tabs.TekkaServer, tab = tabs.TekkaTab,
	what = basestring, own = bool)
def _show_output_exclusive(servertab, tab, what, own = False):
	""" Returns bool.
		Determine if the message identified by -what- shall
		be shown in tab -tab- or not.
		-servertab- is not used at the moment.
	"""
	return not _hide_output(tab, what, own = own)

""" Server callbacks """

def serverConnect_cb(time, server):
	"""
		maki is connecting to a server.
	"""
	gui.set_useable(True)

	tab = gui.tabs.search_tab(server)

	if not tab:
		tab = _setup_server(server)

	if tab.connected:
		tab.connected = False

		channels = gui.tabs.get_all_tabs(servers = [server])[1:]

		if channels:
			for channelTab in channels:
				if channelTab.is_channel():
					channelTab.joined=False
				channelTab.connected=False

	gui.serverPrint(time, server, "Connecting...")

	gui.status.set("connecting", "Connecting to %s" % server)

def serverConnected_cb(time, server):
	"""
		maki connected successfuly to a server.
	"""
	tab = gui.tabs.search_tab(server)

	if not tab:
		tab = _setup_server(server)

	tab.connected = True

	# iterate over tabs, set the connected flag to queries
	for query in [tab for tab in gui.tabs.get_all_tabs(
	servers = [server])[1:] if tab.is_query()]:
		query.connected = True

	gui.serverPrint(time, server, "Connected.")

def serverMOTD_cb(time, server, message, first_time = {}):
	""" Server is sending a MOTD.
		Channes are joined 3s after the end of the
		MOTD so at the end of the MOTD, make sure
		that the prefixes and chantypes are read
		correctly.
	"""
	if not first_time.has_key(server):
		tab = gui.tabs.search_tab(server)
		if not tab:
			tab = _setup_server(server)
		else:
			gui.tabs.update_server(tab)

		gui.status.unset("connecting")
		tab.connected = True
		first_time[server] = tab

	if not message:
		# get the prefixes for the server to make
		# sure they are correct
		tab = first_time[server]
		tab.support_prefix = sushi.support_prefix(server)
		tab.support_chantypes = sushi.support_chantypes(server)
		del first_time[server]

	else:
		gui.serverPrint(time, server, gui.escape(message))

""" Callbacks for channel interaction """

def channelTopic_cb(time, server, from_str, channel, topic):
	"""
		The topic was set on server "server" in channel "channel" by
		user "nick" to "topic".
		Apply this!
	"""
	nick = parse_from(from_str)[0]
	serverTab, channelTab = gui.tabs.search_tabs(server, channel)

	if not channelTab:
		raise Exception("Channel %s does not exist but "
						"emits topic signal." % channel)

	channelTab.topic = topic
	channelTab.topicsetter = nick

	if channelTab == gui.tabs.get_current_tab():
		gui.set_topic(gui.markup_escape(topic))

	if not nick:
		# just reporting the topic.
		message = _(u"• Topic for %(channel)s: %(topic)s") % {
			"channel": channel,
			"topic": gui.escape(topic) }
		gui.channelPrint(time, server, channel, message, "action")

	else:
		if nick == serverTab.nick:
			message = _(u"• You changed the topic to %(topic)s.")
		else:
			message = _(u"• %(nick)s changed the topic to %(topic)s.")

		gui.channelPrint(
			time, server, channel,
			message % {
				"nick": nick,
				"topic": gui.escape(topic) },
			"action")

def channelBanlist_cb(time, server, channel, mask, who, when):
	"""
		ban list signal.
	"""
	if not mask and not who and when == -1:
		gui.channelPrint(
			time, server, channel,
			"End of banlist.", "action")
	else:
		timestring = mtime.strftime(
			"%Y-%m-%d %H:%M:%S",
			mtime.localtime(when))

		gui.channelPrint(
			time,
			server,
			channel,
			"%s by %s on %s" % (
				gui.escape(mask),
				gui.escape(who),
				gui.escape(timestring)),
			"action")

""" Callbacks of maki signals """

def makiShutdown_cb(time):
	gui.myPrint("Maki is shut down!")
	gui.set_useable(False)

""" Callbacks for users """

def userAway_cb(time, server):
	"""
		maki says that we are away.
	"""
	tab = gui.tabs.search_tab(server)

	if tab:
		tab.away = "-- Not implemented yet --"


def userBack_cb(time, server):
	"""
		maki says that we are back from away being.
	"""
	tab = gui.tabs.search_tab(server)

	if tab:
		tab.away = ""

def userAwayMessage_cb(timestamp, server, nick, message):
	"""
		The user is away and the server gives us the message he left
		for us to see why he is away and probably when he's back again.
	"""
	# TODO: print this one time only..
	gui.channelPrint(
		timestamp,
		server,
		nick,
		_(u"• %(nick)s is away (%(message)s).") % {
			"nick": nick,
			"message": gui.escape(message)},
		"action")
<commit_msg>Hide away message if it's printed once<commit_after># coding: UTF-8
"""
Copyright (c) 2008 <NAME>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
"""

from gettext import gettext as _

import gtk
import gobject
import logging
import string
from dbus import UInt64
import time as mtime

import com
import config

import lib.gui_control as gui
from lib import key_dialog
from lib import contrast
from lib import dcc_dialog
from lib import tab as tabs

from helper import color

from com import sushi, parse_from

from typecheck import types

signals = {}
restore_list = []

def setup():
	sushi.g_connect("maki-connected", handle_maki_connect_cb)
	sushi.g_connect("maki-disconnected", handle_maki_disconnect_cb)

"""
One should use this methods to connect to maki's
signals. This API features automatic reconnection
of the registered signals if the connection to
maki was reset.
- connect_signal(<name>,<handler>)
- disconnect_signal(<name>,<handler>)
"""

@types (signal=basestring)
def connect_signal (signal, handler):
	""" connect handler to signal """
	global signals

	if not signals.has_key (signal):
	  	signals[signal] = {}

	if signals[signal].has_key(handler):
		# no doubles
		return

	signals[signal][handler] = sushi.connect_to_signal (signal, handler)

@types (signal=basestring)
def disconnect_signal (signal, handler):
	""" disconnect handler from signal """
	global signals

	try:
		ob = signals[signal][handler]
	except KeyError:
		return
	else:
		ob.remove()
		del signals[signal][handler]

def _connect_signals():

	# Message-Signals
	connect_signal("message", userMessage_cb)
	connect_signal("notice", userNotice_cb)
	connect_signal("action", userAction_cb)
	connect_signal("away_message", userAwayMessage_cb)
	connect_signal("ctcp", userCTCP_cb)
	connect_signal("error", userError_cb)

	# action signals
	connect_signal("part", userPart_cb)
	connect_signal("join", userJoin_cb)
	connect_signal("names", userNames_cb)
	connect_signal("quit", userQuit_cb)
	connect_signal("kick", userKick_cb)
	connect_signal("nick", userNick_cb)
	connect_signal("away", userAway_cb)
	connect_signal("back", userBack_cb)
	connect_signal("mode", userMode_cb)
	connect_signal("oper", userOper_cb)

	# Server-Signals
	connect_signal("connect", serverConnect_cb)
	connect_signal("connected", serverConnected_cb)
	connect_signal("motd", serverMOTD_cb)
	connect_signal("whois", whois_cb) # FIXME: only on demand! like list
	connect_signal("dcc_send", dcc_send_cb)

	# Channel-Signals
	connect_signal("topic", channelTopic_cb)
	connect_signal("banlist", channelBanlist_cb)

	# Maki signals
	connect_signal("shutdown", makiShutdown_cb)

def _restore_signals():
	global restore_list

	for (signal, handler) in restore_list:
		connect_signal(signal, handler)

def handle_maki_disconnect_cb(sushi):
	global signals
	global restore_list

	for signal in signals:
		for handler in signals[signal]:
			signals[signal][handler].remove()
			restore_list.append((signal, handler))

	signals = {}

def handle_maki_connect_cb(sushi):
	""" connect to the important signals of maki
		or, if we've done that before, reconnect
		that signals (and all other registered)
	"""

	self = handle_maki_connect_cb
	try:
		self.init
	except AttributeError:
		self.init = True
	else:
		self.init = False

	if self.init:
		_connect_signals()
	else:
		_restore_signals()

	_add_servers()

@types (server = basestring)
def _setup_server(server):
	tab = gui.tabs.create_server(server)

	gui.tabs.add_tab(None, tab,
		update_shortcuts = config.get_bool("tekka","server_shortcuts"))

	return tab

def _add_servers():
	""" Adds all servers to tekka which are reported by maki. """
	# in case we're reconnecting, clear all stuff
	gui.widgets.get_widget("serverTree").get_model().clear()

	for server in sushi.servers():
		tab = _setup_server(server)
		tab.connected = True
		_add_channels(tab)

	try:
		toSwitch = gui.tabs.get_all_tabs()[1]
	except IndexError:
		return
	else:
		gui.tabs.switch_to_path(toSwitch.path)


@types (server_tab = tabs.TekkaServer)
def _add_channels(server_tab):
	"""
		Adds all channels to tekka wich are reported by maki.
	"""
	channels = sushi.channels(server_tab.name)

	for channel in channels:

		add = False
		nicks, prefixes = sushi.channel_nicks(server_tab.name, channel)

		tab = gui.tabs.search_tab(server_tab.name, channel)

		if not tab:
			tab = gui.tabs.create_channel(server_tab, channel)
			add = True

		tab.nickList.clear()
		tab.nickList.add_nicks(nicks, prefixes)

		tab.topic = sushi.channel_topic(server_tab.name, channel)
		tab.topicsetter = ""

		if gui.tabs.is_active(tab):
			gui.set_topic(gui.markup_escape(tab.topic))
			gui.set_user_count(
				len(tab.nickList),
				tab.nickList.get_operator_count())

		# TODO: handle topic setter
		tab.joined = True
		tab.connected = True

		if add:
			gui.tabs.add_tab(server_tab, tab, update_shortcuts = False)
			gui.print_last_log(server_tab.name, channel)

		topic = sushi.channel_topic(server_tab.name, channel)
		channelTopic_cb(mtime.time(), server_tab.name, "", channel, topic)

	gui.updateServerTreeShortcuts()

def isHighlighted (server_tab, text):
	def has_highlight(text, needle):
		punctuation = string.punctuation + " \n\t"
		ln = len(needle)
		for line in text.split("\n"):
			i = line.find(needle)
			if i >= 0:
				if (line[i-1:i] in punctuation
				and line[ln+i:ln+i+1] in punctuation):
					return True
		return False

	highlightwords = config.get_list("chatting", "highlight_words", [])
	highlightwords.append(server_tab.nick)

	for word in highlightwords:
		if has_highlight(text, word):
			return True
	return False

@types (server = basestring, name = basestring)
def _createTab (server, name):
	""" check if tab exists, create it if not, return the tab """
	server_tab = gui.tabs.search_tab(server)
	tab = gui.tabs.search_tab(server, name)

	if not server_tab:
		raise Exception(
		"No server tab in _createTab(%s, %s)" % (server,name))

	if not tab:
		if name[0] in server_tab.support_chantypes:
			tab = gui.tabs.create_channel(server_tab, name)
		else:
			tab = gui.tabs.create_query(server_tab, name)

		tab.connected = True
		gui.tabs.add_tab(server_tab, tab)
		gui.print_last_log(server, name)

	if tab.name != name:
		# the name of the tab differs from the
		# real nick, correct this.
		tab.name = name

	return tab

def _getPrefix(server, channel, nick):
	tab = gui.tabs.search_tab(server, channel)

	if tab and tab.is_channel():
		return tab.nickList.get_prefix(nick)
	else:
		return ""

@types (tab = tabs.TekkaTab, what = basestring, own = bool)
def _hide_output(tab, what, own = False):
	""" Returns bool.
		Check if the message type determined by "what"
		shall be hidden or not.
		tab should be a TekkaServer, -Channel or -Query
	"""
	if type(tab) == tabs.TekkaChannel:
		cat = "channel_%s_%s" % (
					tab.server.name.lower(),
					tab.name.lower())
	elif type(tab) == tabs.TekkaQuery:
		cat = "query_%s_%s" % (
					tab.server.name.lower(),
					tab.name.lower())
	else:
		return False

	hide = what in config.get_list(cat, "hide", [])
	hideOwn = what in config.get_list(cat, "hide_own", [])

	return ((hide and not own)
		or (own and hideOwn)
		or (hide and own and not hideOwn))

@types (servertab = tabs.TekkaServer, tab = tabs.TekkaTab,
	what = basestring, own = bool)
def _show_output_exclusive(servertab, tab, what, own = False):
	""" Returns bool.
		Determine if the message identified by -what- shall
		be shown in tab -tab- or not.
		-servertab- is not used at the moment.
	"""
	return not _hide_output(tab, what, own = own)

""" Server callbacks """

def serverConnect_cb(time, server):
	"""
		maki is connecting to a server.
	"""
	gui.set_useable(True)

	tab = gui.tabs.search_tab(server)

	if not tab:
		tab = _setup_server(server)

	if tab.connected:
		tab.connected = False

		channels = gui.tabs.get_all_tabs(servers = [server])[1:]

		if channels:
			for channelTab in channels:
				if channelTab.is_channel():
					channelTab.joined=False
				channelTab.connected=False

	gui.serverPrint(time, server, "Connecting...")

	gui.status.set("connecting", "Connecting to %s" % server)

def serverConnected_cb(time, server):
	"""
		maki connected successfuly to a server.
	"""
	tab = gui.tabs.search_tab(server)

	if not tab:
		tab = _setup_server(server)

	tab.connected = True

	# iterate over tabs, set the connected flag to queries
	for query in [tab for tab in gui.tabs.get_all_tabs(
	servers = [server])[1:] if tab.is_query()]:
		query.connected = True

	gui.serverPrint(time, server, "Connected.")

def serverMOTD_cb(time, server, message, first_time = {}):
	""" Server is sending a MOTD.
		Channes are joined 3s after the end of the
		MOTD so at the end of the MOTD, make sure
		that the prefixes and chantypes are read
		correctly.
	"""
	if not first_time.has_key(server):
		tab = gui.tabs.search_tab(server)
		if not tab:
			tab = _setup_server(server)
		else:
			gui.tabs.update_server(tab)

		gui.status.unset("connecting")
		tab.connected = True
		first_time[server] = tab

	if not message:
		# get the prefixes for the server to make
		# sure they are correct
		tab = first_time[server]
		tab.support_prefix = sushi.support_prefix(server)
		tab.support_chantypes = sushi.support_chantypes(server)
		del first_time[server]

	else:
		gui.serverPrint(time, server, gui.escape(message))

""" Callbacks for channel interaction """

def channelTopic_cb(time, server, from_str, channel, topic):
	"""
		The topic was set on server "server" in channel "channel" by
		user "nick" to "topic".
		Apply this!
	"""
	nick = parse_from(from_str)[0]
	serverTab, channelTab = gui.tabs.search_tabs(server, channel)

	if not channelTab:
		raise Exception("Channel %s does not exist but "
						"emits topic signal." % channel)

	channelTab.topic = topic
	channelTab.topicsetter = nick

	if channelTab == gui.tabs.get_current_tab():
		gui.set_topic(gui.markup_escape(topic))

	if not nick:
		# just reporting the topic.
		message = _(u"• Topic for %(channel)s: %(topic)s") % {
			"channel": channel,
			"topic": gui.escape(topic) }
		gui.channelPrint(time, server, channel, message, "action")

	else:
		if nick == serverTab.nick:
			message = _(u"• You changed the topic to %(topic)s.")
		else:
			message = _(u"• %(nick)s changed the topic to %(topic)s.")

		gui.channelPrint(
			time, server, channel,
			message % {
				"nick": nick,
				"topic": gui.escape(topic) },
			"action")

def channelBanlist_cb(time, server, channel, mask, who, when):
	"""
		ban list signal.
	"""
	if not mask and not who and when == -1:
		gui.channelPrint(
			time, server, channel,
			"End of banlist.", "action")
	else:
		timestring = mtime.strftime(
			"%Y-%m-%d %H:%M:%S",
			mtime.localtime(when))

		gui.channelPrint(
			time,
			server,
			channel,
			"%s by %s on %s" % (
				gui.escape(mask),
				gui.escape(who),
				gui.escape(timestring)),
			"action")

""" Callbacks of maki signals """

def makiShutdown_cb(time):
	gui.myPrint("Maki is shut down!")
	gui.set_useable(False)

""" Callbacks for users """

def userAway_cb(time, server):
	"""
		maki says that we are away.
	"""
	tab = gui.tabs.search_tab(server)

	if tab:
		tab.away = "-- Not implemented yet --"


def userBack_cb(time, server):
	"""
		maki says that we are back from away being.
	"""
	tab = gui.tabs.search_tab(server)

	if tab:
		tab.away = ""

def userAwayMessage_cb(timestamp, server, nick, message):
	"""
		The user is away and the server gives us the message he left
		for us to see why he is away and probably when he's back again.
	"""
	tab = gui.tabs.get_current_tab()


	# XXX:  you can still write /msg <nick> and get an away message
	# XXX:: in the query window. This would be a more complex fix.
	try:
		tab.printed_away_message
	except AttributeError:
		print_it = True
	else:
		print_it = not tab.printed_away_message

	if print_it:
		gui.channelPrint(
			timestamp,
			server,
			nick,
			_(u"• %(nick)s is away (%(message)s).") % {
				"nick": nick,
				"message": gui.escape(message)},
			"action")


		if tab and tab.name == nick:
			tab.printed_away_message = True

{'fraction_non_alphanumeric': 0.11210230100692982, 'fraction_numerical': 0.0014548795895708104, 'mean_word_length': 4.363449691991787, 'pattern_counts': {'":': 12, '<': 14, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4683363', 'n_tokens_mistral': 9586, 'n_tokens_neox': 8749, 'n_words': 2601}