#!/usr/bin/python
# -*- coding: utf-8 -*-

# --------------------------------------------------------------------------- #
#                                                                             #
#    iSida Jabber Bot                                                         #
#    Copyright (C) diSabler <dsy@dsy.name>                                    #
#                                                                             #
#    This program is free software: you can redistribute it and/or modify     #
#    it under the terms of the GNU General Public License as published by     #
#    the Free Software Foundation, either version 3 of the License, or        #
#    (at your option) any later version.                                      #
#                                                                             #
#    This program is distributed in the hope that it will be useful,          #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
#    GNU General Public License for more details.                             #
#                                                                             #
#    You should have received a copy of the GNU General Public License        #
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.    #
#                                                                             #
# --------------------------------------------------------------------------- #

from __future__ import with_statement

import calendar
import crontab
import datetime
import gc
import json
import hashlib
import htmlentitydefs
import httplib
import logging
import operator
import os
import math
import random
import re
import socket
import string
import sys
import time
import urllib
import urllib2

import chardet
import xmpp

global execute, prefix, comms, hashlib, trace

def sqlite3_split_part(txt, spltr, cnt):
	if txt: return (txt.split(spltr) + [''] * (cnt-1))[cnt-1]
	else: return txt

sqlite3_row_number_last_x = 0
def sqlite3_row_number():
	global sqlite3_row_number_last_x
	sqlite3_row_number_last_x += 1
	return sqlite3_row_number_last_x

def cur_execute_sqlite3(*params):
	conn = sqlite3.connect(sqlite_base)	
	if 'split_part' in list(params)[0]: conn.create_function('split_part', 3, sqlite3_split_part)
	if 'row_number' in list(params)[0]:
		global sqlite3_row_number_last_x
		sqlite3_row_number_last_x = 0
		conn.create_function('row_number', 0, sqlite3_row_number)
	cur = conn.cursor()
	par = True
	try:
		params = list(params)
		params[0] = params[0].replace('%s','?').replace(' ilike ',' like ').replace(' update ',' `update` ').replace(' repeat ',' `repeat` ').replace(' option ',' `option` ').replace(' count ',' `count` ').replace(' match ',' `match` ')
		params = tuple(params)
		cur.execute(*params)
		prm = params[0].split()[0].lower()
		if prm in ['update','insert','delete','create','drop','alter']: conn.commit()
	except Exception as par:
		par = None
		conn.rollback()
		if halt_on_exception: raise
	conn.commit()
	conn.close()
	return par
	
def cur_execute_mysql(*params):
	conn = mysqldb.connect(database=base_name, user=base_user, host=base_host, password=base_pass, port=base_port)
	cur = conn.cursor()
	par = True
	try:
		params = list(params)
		params[0] = params[0].replace(' ilike ',' like ').replace(' update ',' `update` ').replace(' repeat ',' `repeat` ').replace(' option ',' `option` ').replace(' count ',' `count` ').replace(' match ',' `match` ').replace('split_part(','substring_index(')
		params = tuple(params)
		cur.execute(*params)
		prm = params[0].split()[0].lower()
		if prm in ['update','insert','delete','create','drop','alter']: conn.commit()
	except Exception as par:
		par = None
		conn.rollback()
		if halt_on_exception: raise
	conn.commit()
	conn.close()
	return par

def cur_execute(*params):
	if base_type == 'sqlite3': return cur_execute_sqlite3(*params)
	elif base_type == 'mysql': return cur_execute_mysql(*params)
	global conn
	cur = conn.cursor()
	if base_type == 'pgsql': psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, cur)
	par = True
	try:
		cur.execute(*params)
		prm = params[0].split()[0].lower()
		if prm in ['update','insert','delete','create','drop','alter']: conn.commit()
	except Exception as par:
		if database_debug:
			try: par = str(par)
			except: par = str(par)
			pprint(par,'red')
		else: par = None
		conn.rollback()
		if halt_on_exception: raise
	cur.close()
	return par

def cur_execute_fetchone_sqlite3(*params):
	conn = sqlite3.connect(sqlite_base)
	if 'split_part' in list(params)[0]: conn.create_function('split_part', 3, sqlite3_split_part)
	if 'row_number' in list(params)[0]:
		global sqlite3_row_number_last_x
		sqlite3_row_number_last_x = 0
		conn.create_function('row_number', 0, sqlite3_row_number)
	try: cur = conn.cursor()
	except: return None
	par = None
	try:
		params = list(params)
		params[0] = params[0].replace('%s','?').replace(' ilike ',' like ').replace(' update ',' `update` ').replace(' repeat ',' `repeat` ').replace(' option ',' `option` ').replace(' count ',' `count` ').replace(' match ',' `match` ')
		params = tuple(params)
		cur.execute(*params)
		try: par = cur.fetchone()
		except Exception as par:
			par = None
			if halt_on_exception: raise
	except Exception as par:
		par = None
		conn.rollback()
	conn.close()
	return par

def cur_execute_fetchone_mysql(*params):
	conn = mysqldb.connect(database=base_name, user=base_user, host=base_host, password=base_pass, port=base_port)
	try: cur = conn.cursor()
	except: return None
	par = None
	try:
		params = list(params)
		params[0] = params[0].replace(' ilike ',' like ').replace(' update ',' `update` ').replace(' repeat ',' `repeat` ').replace(' option ',' `option` ').replace(' count ',' `count` ').replace(' match ',' `match` ').replace('split_part(','substring_index(')
		params = tuple(params)
		cur.execute(*params)
		try: par = cur.fetchone()
		except Exception as par:
			par = None
			if halt_on_exception: raise
	except Exception as par:
		par = None
		conn.rollback()
	conn.close()
	return par

def cur_execute_fetchone(*params):
	if base_type == 'sqlite3': return cur_execute_fetchone_sqlite3(*params)
	elif base_type == 'mysql': return cur_execute_fetchone_mysql(*params)
	global conn
	try: cur = conn.cursor()
	except: return None
	if base_type == 'pgsql': psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, cur)
	par = None
	try:
		cur.execute(*params)
		try: par = cur.fetchone()
		except Exception as par:
			if database_debug:
				try: par = str(par)
				except: par = str(par)
			else: par = None
			if halt_on_exception: raise
	except Exception as par:
		if database_debug:
			try: par = str(par)
			except: par = str(par)
			pprint(par,'red')
			if halt_on_exception: raise
		else: par = None
		conn.rollback()
	cur.close()
	return par

def cur_execute_fetchall_sqlite3(*params):
	conn = sqlite3.connect(sqlite_base)
	if 'split_part' in list(params)[0]: conn.create_function('split_part', 3, sqlite3_split_part)
	if 'row_number' in list(params)[0]:
		global sqlite3_row_number_last_x
		sqlite3_row_number_last_x = 0
		conn.create_function('row_number', 0, sqlite3_row_number)
	try: cur = conn.cursor()
	except: return None
	par = None
	try:
		params = list(params)
		params[0] = params[0].replace('%s','?').replace(' ilike ',' like ').replace(' update ',' `update` ').replace(' repeat ',' `repeat` ').replace(' option ',' `option` ').replace(' count ',' `count` ').replace(' match ',' `match` ')
		params = tuple(params)
		cur.execute(*params)
		try: par = cur.fetchall()
		except Exception as par:
			par = None
			if halt_on_exception: raise
	except Exception as par:
		par = None
		conn.rollback()
		if halt_on_exception: raise
	conn.close()
	return par

def cur_execute_fetchall_mysql(*params):
	conn = mysqldb.connect(database=base_name, user=base_user, host=base_host, password=base_pass, port=base_port)
	try: cur = conn.cursor()
	except: return None
	par = None
	try:
		params = list(params)
		params[0] = params[0].replace(' ilike ',' like ').replace(' update ',' `update` ').replace(' repeat ',' `repeat` ').replace(' option ',' `option` ').replace(' count ',' `count` ').replace(' match ',' `match` ').replace('split_part(','substring_index(')
		params = tuple(params)
		cur.execute(*params)
		try: par = cur.fetchall()
		except Exception as par:
			par = None
			if halt_on_exception: raise
	except Exception as par:
		par = None
		conn.rollback()
		if halt_on_exception: raise
	conn.close()
	return par

def cur_execute_fetchall(*params):
	if base_type == 'sqlite3': return cur_execute_fetchall_sqlite3(*params)
	elif base_type == 'mysql': return cur_execute_fetchall_mysql(*params)
	global conn
	try: cur = conn.cursor()
	except: return None
	if base_type == 'pgsql': psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, cur)
	par = None
	try:
		cur.execute(*params)
		try: par = cur.fetchall()
		except Exception as par:
			if database_debug:
				try: par = str(par)
				except: par = str(par)
			else: par = None
			if halt_on_exception: raise
	except Exception as par:
		if database_debug:
			try: par = str(par)
			except: par = str(par)
			pprint(par,'red')
		else: par = None
		conn.rollback()
		if halt_on_exception: raise
	cur.close()
	return par

def get_color(c):
	color = os.environ.__contains__('TERM')
	colors = {'clear':'[0m','blue':'[34m','red':'[31m','magenta':'[35m','green':'[32m','cyan':'[36m','brown':'[33m','light_gray':'[37m','black':'[30m','bright_blue':'[34;1m','bright_red':'[31;1m','purple':'[35;1m','bright_green':'[32;1m','bright_cyan':'[36;1m','yellow':'[33;1m','dark_gray':'[30;1m','white':'[37;1m'}
	return ['','\x1b%s' % colors[c]][color]

def get_color_win32(c):
	colors = {'clear':7,'blue':1,'red':4,'magenta':5,'green':2,'cyan':3,'brown':6,'light_gray':7,'black':0,'bright_blue':9,'bright_red':12,'purple':13,'bright_green':10,'bright_cyan':11,'yellow':14,'dark_gray':8,'white':15}
	return colors[c]

def thr(func,param,name):
	global th_cnt, thread_error_count, sema
	th_cnt += 1
	try:
		if thread_type:
			with sema:
				tmp_th = KThread(group=None,target=log_execute,name='%s_%s' % (th_cnt,name),args=(func,param))
				tmp_th.start()
		else: thread.start_new_thread(log_execute,(func,param))
	except SystemExit: pass
	except Exception as SM:
		try: SM = str(SM)
		except: SM = str(SM)
		if 'thread' in SM.lower(): thread_error_count += 1
		else: logging.exception(' [%s] %s' % (timeadd(tuple(time.localtime())),str(func)))
		if thread_type:
			try: tmp_th.kill()
			except: pass
		if halt_on_exception: raise

def log_execute(proc, params):
	try: proc(*params)
	except SystemExit: pass
	except: logging.exception(' [%s] %s' % (timeadd(tuple(time.localtime())),str(proc)))

def sender(item):
	global message_out, presence_out, iq_out, unknown_out, last_stanza, messages_log
	last_stanza = str(item)
	if last_stanza[:2] == '<m':
		message_out += 1
		id = get_id()
		item.setAttr('id',id)
		messages_log[id] = item
	elif last_stanza[:2] == '<p': presence_out += 1
	elif last_stanza[:2] == '<i': iq_out += 1
	else: unknown_out += 1
	if time_nolimit: time.sleep(time_nolimit)
	try: cl.send(item)
	except Exception as SM:
		pprint(last_stanza,'red')
		pprint(SM,'red')
		if halt_on_exception: raise

def readfile(filename):
	with open(filename, 'rb') as fp:
		data = fp.read()
	return data

def writefile(filename, data):
	with open(filename, 'w') as fp:
		fp.write(data)

def getFile(filename,default):
	if os.path.isfile(filename):
		try: filebody = eval(readfile(filename))
		except:
			if os.path.isfile(back_file % filename.split('/')[-1]):
				while True:
					try:
						filebody = eval(readfile(back_file % filename.split('/')[-1]))
						break
					except: pass
			else:
				filebody = default
				writefile(filename,str(default))
	else:
		filebody = default
		writefile(filename,str(default))
	writefile(back_file % filename.split('/')[-1],str(filebody))
	return filebody

def get_config(room,item):
	setup = cur_execute_fetchone('select value from config_conf where room=%s and option = %s',(room,item))
	try:
		if setup[0] in ['True','true']: return True
		elif setup[0] in ['False','false','None','none']: return False
		else: return setup[0]
	except:
		try: return config_prefs[item][3]
		except: return None

def get_config_int(room,item):
	setup = cur_execute_fetchone('select value from config_conf where room=%s and option = %s',(room,item))
	try: return int(setup[0])
	except: return int(config_prefs[item][3])

def put_config(room,item,value):
	if value in [True,False,None]: value = str(value)
	setup = cur_execute_fetchone('select value from config_conf where room=%s and option = %s',(room,item))
	if setup: cur_execute('update config_conf set value=%s where room=%s and option = %s', (value,room,item))
	else: cur_execute('insert into config_conf values (%s,%s,%s)', (room,item,value))

def GT(item):
	try:
		gt_result = cur_execute_fetchone('select value from config_owner where option = %s;',(item,))[0]
		if gt_result in ['true','false','none']: gt_result = gt_result.capitalize()
	except:
		try: gt_result = owner_prefs[item][2]
		except: gt_result = None
	try: return eval(gt_result)
	except: return gt_result

def PT(item,value):
	if value in [True,False,None] or isinstance(value,type([])): value = str(value)
	setup = cur_execute_fetchone('select value from config_owner where option = %s;',(item,))
	if setup: cur_execute('update config_owner set value=%s where option = %s', (value,item))
	else: cur_execute('insert into config_owner values (%s,%s)', (item,value))

def get_subtag(body,tag):
	T = re.findall('%s=\"(.*?)\"' % tag,body,re.S)
	if T: return T[0]
	else: return ''

def get_tag(body,tag):
	T = re.findall('<%s.*?>(.*?)</%s>' % (tag,tag),body,re.S)
	if T: return T[0]
	else: return ''

def get_tag_full(body,tag):
	T = re.findall('(<%s[^>]*?>|</%s>)' % (tag,tag),body,re.S)
	if T and len(T)==1: return T[0]
	elif len(T) >= 2 and T[0][1:len(tag)+1] == T[1][-len(tag)-1:-1]:
		T1 = re.findall('(%s.*?%s)' % (re.escape(T[0]),re.escape(T[1])),body,re.S)
		if T1: return T1[0]
		else: return ''
	elif len(T): return T[0]
	else: return ''

def get_tag_item(body,tag,item):
	body = get_tag_full(body,tag)
	return get_subtag(body,item)

def parser(t):
	try: return ''.join([['?',l][l<='~'] for l in str(t)])
	except:
		fp = open(slog_folder % 'critical_exception_%s.txt' % int(time.time()), 'wb')
		fp.write(t)
		fp.close()

def remove_sub_space(t): return ''.join([['?',l][l>=' ' or l in '\t\r\n'] for l in str(t)])

def smart_encode(text,enc):
	tx,splitter = '','|'
	while splitter in text: splitter += '|'
	ttext = text.replace('</','<%s/' % splitter).split(splitter)
	for tmp in ttext:
		try: tx += str(tmp,enc)
		except: pass
	return tx

def timeadd(lt): return '%02d.%02d.%02d %02d:%02d:%02d' % (lt[2],lt[1],lt[0],lt[3],lt[4],lt[5])

def onlytimeadd(lt): return '%02d:%02d:%02d' % (lt[3],lt[4],lt[5])

def pprint(*text):
	global last_logs_store
	c,wc,win_color = '','',''
	if len(text) > 1:
		if is_win32: win_color = get_color_win32(text[1])
		else: c,wc = get_color(text[1]),get_color('clear')
	elif is_win32: win_color = get_color_win32('clear')
	text = text[0]
	lt = tuple(time.localtime())
	zz = '%s[%s]%s %s%s' % (wc,onlytimeadd(lt),c,text,wc)
	last_logs_store = ['[%s] %s' % (onlytimeadd(lt),text)] + last_logs_store[:last_logs_size]
	if debug_console:
		if is_win32 and win_color:
			ctypes.windll.Kernel32.SetConsoleTextAttribute(win_console_color, get_color_win32('clear'))
			print(zz.split(' ',1)[0],)
			ctypes.windll.Kernel32.SetConsoleTextAttribute(win_console_color, win_color)
			try: print(zz.split(' ',1)[1])
			except: print(parser(zz.split(' ',1)[1]))
			ctypes.windll.Kernel32.SetConsoleTextAttribute(win_console_color, get_color_win32('clear'))
		else:
			try: print(zz)
			except: print(parser(zz))
	if CommandsLog:
		fname = slog_folder % '%02d%02d%02d.txt' % (lt[0],lt[1],lt[2])
		fbody = '%s|%s\n' % (onlytimeadd(lt),text.replace('\n','\r'))
		fl = open(fname, 'a')
		fl.write(fbody.encode('utf-8'))
		fl.close()

def send_presence_all(sm):
	pr=xmpp.Presence(typ='unavailable')
	pr.setStatus(sm)
	sender(pr)
	time.sleep(2)

def errorHandler(text):
	draw_warning(text)
	sys.exit('exit')

def get_joke(text):

	def joke_blond(text):

		def blond_text(text):
			b = ''
			cnt = random.randint(0,1)
			for tmp in text:
				if cnt: b += tmp.upper()
				else: b += tmp.lower()
				cnt = not cnt
			return b

		text = text.split(' ')
		new_text = []
		for t in text:
			if t == '/me' or [True for s in ['http://','https://','ftp://','svn://','xmpp:','git://'] if t.startswith(s)]: new_text.append(t)
			else: new_text.append(blond_text(t))
		return ' '.join(new_text)

	def joke_upyachka(text):
		upch = [u'пыщь!!!111адын',u'ололололо!!11',u'ГОЛАКТЕКО ОПАСНОСТЕ!!11',u'ТЕЛОИД!!',u'ЖАЖА1!',u'ОНОТОЛЕЙ!!!!!',u'ПОТС ЗОХВАЧЕН11!!!',
				u'ПыЩЩЩЩЩЩЩЩЩь!!!!!!!1111',u'ПыЩЩЩЩЩЩЩЩЩь11111адинадин1адин']
		return '%s %s' % (text.strip(),random.choice(upch))

	def no_joke(text): return text

	jokes = [joke_blond,no_joke,joke_upyachka]
	return random.choice(jokes)(text)

def msg_validator(t): return ''.join([[l,'?'][l<' ' and l not in '\n\t\r' or l>u'\uff00'] for l in str(t)])

def message_exclude_update():
	global messages_excl
	excl = GT('exclude_messages').replace('\r','').replace('\t','').split('\n')
	messages_excl = []
	for c in excl:
		if '#' not in c and len(c): messages_excl.append(c)

def message_validate(item):
	if messages_excl:
		for c in messages_excl:
			cn = re.findall(c,' %s ' % item,re.S|re.I|re.U)
			for tmp in cn: item = item.replace(tmp,[GT('censor_text')*len(tmp),GT('censor_text')][len(GT('censor_text'))>1])
	return item

def send_msg(mtype, mjid, mnick, mmessage):
	global between_msg_last,time_limit
	if mmessage:
		mmessage = message_validate(mmessage)
		mnick = message_validate(mnick)
		# 1st april joke :)
		if time.localtime()[1:3] == (4,1) and GT('1st_april_joke'): mmessage = get_joke(mmessage)
		no_send = True
		if len(mmessage) > msg_limit:
			cnt = 0
			maxcnt = int(len(mmessage)/msg_limit) + 1
			mmsg = mmessage
			while len(mmsg) > msg_limit and not game_over:
				tmsg = u'[%s/%s] %s[…]' % (cnt+1,maxcnt,mmsg[:msg_limit])
				cnt += 1
				sender(xmpp.Message('%s/%s' % (mjid,mnick), tmsg, 'chat'))
				mmsg = mmsg[msg_limit:]
				time.sleep(time_limit)
			tmsg = '[%s/%s] %s' % (cnt+1,maxcnt,mmsg)
			sender(xmpp.Message('%s/%s' % (mjid,mnick), tmsg, 'chat'))
			if mtype == 'chat': no_send = None
			else: mmessage = mmessage[:msg_limit] + u'[…]'
		if no_send:
			if mtype == 'groupchat' and mnick != '': mmessage = '%s: %s' % (mnick,mmessage)
			else: mjid += '/' + mnick
			while mmessage[-1:] in ['\n','\t','\r',' ']: mmessage = mmessage[:-1]
			mmessage = msg_validator(mmessage)
			if len(mmessage): sender(xmpp.Message(mjid, mmessage, mtype))

def os_version_disco():
	iSys = sys.platform
	iOs = os.name
	isidaPyVer = sys.version.split()[0]
	if iOs == 'posix':
		osInfo = os.uname()
		isidaOs = osInfo[0]
		isidaOsVer = '%s-%s/PYv%s' % (osInfo[2].split('-',1)[0],osInfo[4],isidaPyVer)
	elif iSys == 'win32':
		def get_registry_value(key, subkey, value):
			import _winreg
			key = getattr(_winreg, key)
			handle = _winreg.OpenKey(key, subkey)
			(value, type) = _winreg.QueryValueEx(handle, value)
			return value
		def get(key): return get_registry_value("HKEY_LOCAL_MACHINE", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",key)
		osInfo = get("ProductName")
		buildInfo = get("CurrentBuildNumber")
		try:
			spInfo = get("CSDVersion")
			isidaOs = osInfo
			isidaOsVer = 'SP:%s/BLD:%s/PYv%s' % (spInfo,buildInfo,isidaPyVer)
		except:
			isidaOs = 'Windows'
			isidaOsVer = '%s/BLD:%s/PYv%s' % (osInfo.replace('Windows','').strip(),buildInfo,isidaPyVer)
	else: isidaOs = isidaOsVer = 'unknown'
	return isidaOs, isidaOsVer

def os_version():
	iSys = sys.platform
	iOs = os.name
	isidaPyVer = '%s [%s]' % (sys.version.split(' (')[0],sys.version.split(')')[0].split(', ')[1])
	if iOs == 'posix':
		osInfo = os.uname()
		isidaOs = '%s %s-%s / Python %s' % (osInfo[0],osInfo[2],osInfo[4],isidaPyVer)
	elif iSys == 'win32':
		def get_registry_value(key, subkey, value):
			import _winreg
			key = getattr(_winreg, key)
			handle = _winreg.OpenKey(key, subkey)
			(value, type) = _winreg.QueryValueEx(handle, value)
			return value
		def get(key): return get_registry_value("HKEY_LOCAL_MACHINE", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",key)
		osInfo = ' '.join(get("ProductName").split()[:3])
		buildInfo = get("CurrentBuildNumber")
		try:
			spInfo = get("CSDVersion")
			isidaOs = '%s %s [%s] / Python %s' % (osInfo,spInfo,buildInfo,isidaPyVer)
		except: isidaOs = '%s [%s] / Python %s' % (osInfo,buildInfo,isidaPyVer)
	else: isidaOs = 'unknown'
	return isidaOs

def caps_and_send(tmp):
	tmp.setTag('x', namespace=xmpp.NS_VCARD_UPDATE)
	tmp.getTag('x', namespace=xmpp.NS_VCARD_UPDATE).setTagData('photo',photo_hash)
	tmp.setTag('c', namespace=xmpp.NS_CAPS, attrs={'node':capsNode,'ver':capsHash,'hash':'sha-1'})
	sender(tmp)

def join(conference,passwd):
	global pres_answer,cycles_used,cycles_unused,current_join
	id = get_id()
	current_join[conference] = id
	if Settings['status'] == 'online': j = xmpp.Node('presence', {'id': id, 'to': conference}, payload = [xmpp.Node('status', {},[Settings['message']]),\
																										  xmpp.Node('priority', {},[Settings['priority']])])
	else: j = xmpp.Node('presence', {'id': id, 'to': conference}, payload = [xmpp.Node('show', {},[Settings['status']]),\
																		xmpp.Node('status', {},[Settings['message']]),\
																		xmpp.Node('priority', {},[Settings['priority']])])
	j.setTag('x', namespace=xmpp.NS_MUC).addChild('history', {'maxchars':'0', 'maxstanzas':'0'})
	j.getTag('x').setTagData('password', passwd)
	caps_and_send(j)
	answered, Error, join_timeout = None, None, 3
	if is_start: join_timeout_delay = 0.3
	else: join_timeout_delay = 1
	while not answered and join_timeout >= 0 and not game_over:
		if is_start:
			cyc = cl.Process(1)
			if str(cyc) == 'None': cycles_unused += 1
			elif int(str(cyc)): cycles_used += 1
			else: cycles_unused += 1
		else: time.sleep(join_timeout_delay)
		join_timeout -= join_timeout_delay
		for tmp in pres_answer:
			if tmp[0]==id:
				Error = tmp[1]
				pres_answer.remove(tmp)
				answered = True
				break
		if current_join.__contains__(conference) and current_join[conference] != id: break
	if current_join.__contains__(conference):
		if current_join[conference] != id: Error = {'CAPTCHA':current_join[conference],'ID':id}
		current_join.pop(conference)
	return Error

def leave(conference, sm):
	j = xmpp.Presence(conference, 'unavailable', status=sm)
	sender(j)

def muc_filter_action(act,jid,room,reason):
	if act in ['visitor','kick']:
		nick = get_nick_by_jid(room,getRoom(jid))
		if nick and act=='visitor': sender(xmpp.Node('iq',{'id': get_id(), 'type': 'set', 'to':room},payload = [xmpp.Node('query', {'xmlns': xmpp.NS_MUC_ADMIN},[xmpp.Node('item',{'role':'visitor', 'nick':nick},[xmpp.Node('reason',{},reason)])])]))
		elif nick and act=='kick': sender(xmpp.Node('iq',{'id': get_id(), 'type': 'set', 'to':room},payload = [xmpp.Node('query', {'xmlns': xmpp.NS_MUC_ADMIN},[xmpp.Node('item',{'role':'none', 'nick':nick},[xmpp.Node('reason',{},reason)])])]))
		elif not nick and act=='visitor': sender(xmpp.Node('iq',{'id': get_id(), 'type': 'set', 'to':room},payload = [xmpp.Node('query', {'xmlns': xmpp.NS_MUC_ADMIN},[xmpp.Node('item',{'role':'visitor', 'jid':jid},[xmpp.Node('reason',{},reason)])])]))
		elif not nick and act=='kick': sender(xmpp.Node('iq',{'id': get_id(), 'type': 'set', 'to':room},payload = [xmpp.Node('query', {'xmlns': xmpp.NS_MUC_ADMIN},[xmpp.Node('item',{'role':'none', 'jid':jid},[xmpp.Node('reason',{},reason)])])]))
	elif act=='ban': sender(xmpp.Node('iq',{'id': get_id(), 'type': 'set', 'to':room},payload = [xmpp.Node('query', {'xmlns': xmpp.NS_MUC_ADMIN},[xmpp.Node('item',{'affiliation':'outcast', 'jid':jid},[xmpp.Node('reason',{},reason)])])]))
	return None

def paste_text(text,room,jid):
	return paste_text_raw(text,room,jid,True)

def paste_text_muc(text,room,jid):
	return paste_text_raw(text,room,jid,False)

def paste_text_raw(text,room,jid,need_replace):
	nick = get_nick_by_jid_res(room,jid)
	_html_paste = GT('html_paste_enable')
	if _html_paste:
		if need_replace: text = html_escape(text).replace(' ','&nbsp;')
		else: text = html_escape(text)
		nick = html_escape(nick)
	paste_header = ['','<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><link href="%s" rel="stylesheet" type="text/css" /><title>\n' % paste_css_path][_html_paste]
	url = '%s%s' % (str(hex(int(time.time()*100)))[2:-1],['.txt','.html'][_html_paste])
	lt = tuple(time.localtime())
	ott = onlytimeadd(tuple(time.localtime()))
	paste_body = ['%s','<p><span class="paste">%s</span></p>\n'][_html_paste] % (text)
	lht = '%s [%s] - %02d/%02d/%02d %02d:%02d:%02d' % (nick,room,lt[0],lt[1],lt[2],lt[3],lt[4],lt[5])
	paste_he = ['%s\t\thttp://isida.dsy.name\n\n' % lht,'%s%s</title></head><body><div class="main"><div class="top"><div class="heart"><a href="http://isida.dsy.name">http://isida.dsy.name</a></div><div class="conference">%s</div></div><div class="container">\n' % (paste_header,lht,lht)][_html_paste]
	fl = open(pastepath+url, 'a')
	fl.write(paste_he.encode('utf-8'))
	fl.write(paste_body.encode('utf-8'))
	paste_ender = ['','</div></div></body></html>'][_html_paste]
	fl.write(paste_ender.encode('utf-8'))
	fl.close()
	return pasteurl+url

def disp_time(*t):
	if len(t) == 2: rn = t[1]
	else: rn = ''
	t = t[0]
	lt=tuple(time.localtime(t))
	return '%02d:%02d:%02d, %02d.%s\'%s, %s' % (lt[3],lt[4],lt[5],lt[2],L(wmonth[lt[1]-1],rn).replace('_',''),lt[0],L(wday[lt[6]],rn))

def nice_time(*ttim):
	if len(ttim) == 2: rn = ttim[1]
	else: rn = ''
	ttim = ttim[0]	
	gt=tuple(time.gmtime(ttim))
	lt=tuple(time.localtime(ttim))
	timeofset = (datetime.datetime(*lt[:6])-datetime.datetime(*gt[:6])).seconds / 3600.0
	if timeofset < 0: t_gmt = 'GMT%s' % int(timeofset)
	else: t_gmt = 'GMT+%s' % int(timeofset)
	if timeofset%1: t_gmt += ':%02d' % int((timeofset%1*60/100) * 100)
	t_utc='%s%02d%02dT%02d:%02d:%02d' % gt[:6]
	t_display = '%02d:%02d:%02d, %02d.%s\'%s, %s, ' % (lt[3],lt[4],lt[5],lt[2],L(wmonth[lt[1]-1],rn).replace('_',''),lt[0],L(wday[lt[6]],rn))
	#t_tz = time.tzname[time.localtime()[8]]
	#enc = chardet.detect(t_tz)['encoding']
	#if t_tz == None: body = ''
	#if enc == None or enc == '' or enc.lower() == 'unicode': enc = 'utf-8'
	#t_tz = str(t_tz,enc)
	#t_display += '%s, %s' % (t_tz,t_gmt)
	#return t_utc,t_tz,t_display
	t_display += t_gmt
	return t_utc,t_gmt,t_display

def get_eval_item(mess,string):
	try:
		result = eval('mess.%s' % string)
		if result: return result.encode('utf-8')
	except: pass
	return ''

def match_for_raw(original,regexp,gr):
	orig_drop = orig_reg = re.findall(regexp, original.replace('\n',' '), re.S|re.I|re.U)
	while '' in orig_drop: orig_drop.remove('')
	orig_join = ''.join(orig_reg)
	if orig_join:
		#orig_split = original.split()
		#match_percent = 100.0 / len(original) * len(orig_join)
		#match_percent = 100.0 / len(orig_drop) * len(orig_split)
		match_percent = 100.0 / len(orig_drop) * sum([len(tmp) <= 2 for tmp in orig_drop])

		raw_percent = get_config(gr,'muc_filter_raw_percent')
		if raw_percent.isdigit(): raw_percent = int(raw_percent)
		else: raw_percent = int(config_prefs['muc_filter_raw_percent'][3])
		return ['',orig_join][match_percent >= raw_percent]
	else: return ''

def caps_matcher(c_caps,c_list):
	result = False
	for t in c_list:
		r = int(t[0] == '*')*2+int(t[-1] == '*')
		if r == 3 and t[1:-1].lower() in c_caps.lower(): return True
		elif r == 2 and c_caps.endswith(t[1:]): return True
		elif r == 1 and c_caps.startswith(t[:-1]): return True
		elif c_caps == t: return True
	return result

def iqCB(sess,iq):
	global timeofset, iq_in, iq_request, last_msg_base, last_msg_time_base, ddos_ignore, ddos_iq, user_hash, server_hash, server_hash_list
	global disco_excl, message_excl, users_locale
	iq_in += 1
	id = iq.getID()
	if id == None: return None
	room = str(iq.getFrom())
	if cur_execute_fetchone('select * from bot_owner where jid=%s',(getRoom(room),)): towh = selfjid
	else: towh = '%s/%s' % (getRoom(room),get_nick_by_jid_res(getRoom(room), selfjid))
	query = iq.getTag('query')
	was_request = id in iq_request
	al,tjid = get_level(getRoom(room),getResourse(room))
	acclvl = al >= 7 and GT('iq_disco_enable')
	nnj,tjid = False,getRoom(tjid)
	if room == selfjid: nnj = True
	else:
		for tmp in megabase:
			if '%s/%s' % tuple(tmp[0:2]) == room:
				nnj = True
				break

	c_lang = iq.getAttr('xml:lang')
	if c_lang: users_locale[room] = c_lang[:2].replace('uk','ua')

	if getServer(Settings['jid']) == room: nnj = True

	if iq.getType()=='error' and was_request:
		try: er_name = get_tag(str(iq),'error').replace('<','').split()[0]
		except: er_name = 'Unknown error!'
		iq_async(id,time.time(),er_name,'error')

	elif iq.getType()=='result' and was_request:
		try: nspace = query.getNamespace()
		except: nspace = 'None'
		if nspace == xmpp.NS_MUC_ADMIN: iq_async(id,nspace,time.time(),iq)
		elif nspace == xmpp.NS_MUC_OWNER: iq_async(id,nspace,time.time(),iq)
		elif nspace == xmpp.NS_VERSION:
			ver_client = str(query.getTagData(tag='name'))
			ver_version = str(query.getTagData(tag='version'))
			ver_os = str(query.getTagData(tag='os'))
			t = cur_execute_fetchone('select jid from versions where room=%s and jid=%s and client=%s and version=%s and os=%s',(getRoom(room),tjid,ver_client,ver_version,ver_os))
			if not t: cur_execute('insert into versions values (%s,%s,%s,%s,%s,%s)',(getRoom(room),tjid,ver_client,ver_version,ver_os,int(time.time())))
			iq_async(id,nspace,time.time(),'%s %s // %s' % (ver_client,ver_version,ver_os))
		elif nspace == xmpp.NS_TIME: iq_async(id,nspace,time.time(),query.getTagData(tag='display'),query.getTagData(tag='utc'),query.getTagData(tag='tz'))
		elif iq.getTag('time',namespace=xmpp.NS_URN_TIME): iq_async(id,xmpp.NS_URN_TIME,time.time(),iq.getTag('time').getTagData(tag='utc'),iq.getTag('time').getTagData(tag='tzo'))
		elif iq.getTag('vCard',namespace=xmpp.NS_VCARD): iq_async(id,xmpp.NS_VCARD,time.time(),str(iq),iq)
		else: iq_async(id,nspace,time.time(),str(iq),iq)

	elif iq.getType()=='get' and nnj and not ddos_ignore.__contains__(tjid):
		iq_ddos_requests,iq_ddos_limit = GT('ddos_iq_requests'),GT('ddos_iq_limit')
		nick = getResourse(room)
		qry = str(iq.getTag(name='query'))
		if ddos_iq.__contains__(tjid): time_tuple = [time.time()] + ddos_iq[tjid][:iq_ddos_requests-1]
		else: time_tuple = [time.time()]
		ddos_iq[tjid] = time_tuple
		if len(time_tuple) == iq_ddos_requests and (time_tuple[0]-time_tuple[-1]) < iq_ddos_limit:
			ddos_ignore[tjid] = [getRoom(room),nick,time.time()+GT('ddos_limit')[al]]
			pprint('!!! IQ-DDOS Detect: %s %s [%s] %s' % (al, room, tjid, qry),'bright_red')
			return
		for t in [tmp[2] for tmp in giq_hook if tmp[1] == 'get']:
			to_send = t(iq,id,room,acclvl,query,towh,al)
			if to_send:
				if to_send != True: sender(to_send)
				raise xmpp.NodeProcessed
	elif iq.getType()=='set':
		for t in [tmp[2] for tmp in giq_hook if tmp[1] == 'set']:
			to_send = t(iq,id,room,acclvl,query,towh,al)
			if to_send:
				if to_send != True: sender(to_send)
				raise xmpp.NodeProcessed

def iq_async_clean():
	global iq_reques
	while not game_over:
		to = GT('timeout')
		while to > 0 and not game_over:
			to -= 1
			time.sleep(1)
		if len(iq_request):
			for tmp in iq_request.keys():
				if iq_request[tmp][0] + GT('timeout') < time.time(): iq_request.pop(tmp)
				break

def presence_async_clean():
	global pres_answer
	while not game_over:
		to = GT('timeout')
		while to > 0 and not game_over:
			to -= 1
			time.sleep(1)
		if len(pres_answer):
			tm = []
			for tmp in pres_answer:
				if tmp[2] + GT('timeout') > time.time(): tm.append(tmp)
			pres_answer = tm

def iq_async(*answ):
	global iq_request
	req = iq_request.pop(answ[0])
	try: er_code = answ[3]
	except: er_code = None
	if er_code == 'error': is_answ = (answ[1]-req[0],(answ[2],'error'))
	elif req[3] == xmpp.NS_URN_PING or req[3] == answ[1]: is_answ = (answ[2]-req[0],answ[3:])
	elif req[3] == xmpp.NS_VCARD and answ[1] == 'None':
		answ[4].setTag('vCard',namespace=xmpp.NS_VCARD)
		is_answ = (answ[2]-req[0],answ[3:])
	else:
		pprint('!!! Got a fake iq answer. Request: %s. Answer: %s. Raw: %s' % (req[3],answ[1],answ[3]),'red')
		is_answ = (answ[2]-req[0],('%s %s' % (L('Error!'),L('Got a fake iq answer!')),))
	req[2].append(is_answ)
	thr(req[1],(tuple(req[2])),'iq_async_%s' % answ[0])

def remove_ignore():
	global ddos_ignore
	while not game_over:
		if len(ddos_ignore):
			tt = time.time()
			for tmp in ddos_ignore.keys():
				if tt > ddos_ignore[tmp][2]:
					try:
						ddos_ignore.pop(tmp)
						pprint('!!! DDOS: Jid %s is removed from ignore!' % tmp,'red')
					except: pprint('!!! DDOS: Unable find jid %s in ignore list. Perhaps it\'s removed by bot\'s owner!' % tmp,'red')
		time.sleep(10)

def com_parser(access_mode, nowname, type, room, nick, text, jid):
	global last_command, ddos_ignore
	jjid = getRoom(jid)
	if ddos_ignore.__contains__(jjid): return
	if last_command[1:7] == [nowname, type, room, nick, text, jid] and time.time() < last_command[7]+GT('ddos_diff')[access_mode]:
		ddos_ignore[jjid] = [room,nick,time.time()+GT('ddos_limit')[access_mode]]
		pprint('!!! DDOS Detect: %s %s/%s %s %s' % (access_mode, room, nick, jid, text),'bright_red')
		send_msg(type, room, nick, L('Warning! Exceeded the limit of sending the same commands. You to ignore for %s.','%s/%s'%(room,nick)) % un_unix(GT('ddos_limit')[access_mode],'%s/%s'%(room,nick)))
		return None
	no_comm = True
	cof = cur_execute_fetchall('select * from commonoff;')#!!!
	for parse in comms:
		if access_mode >= parse[0] and nick != nowname:
			not_offed = True
			if access_mode != 9 or ignore_owner:
				for co in cof:
					if co[0]==room and co[1]==text.lower()[:len(co[1])]:
						not_offed = None
						break
			p1l = parse[1].lower()
			if not_offed and (text.lower() == p1l or text[:len(parse[1])+1].lower() in ['%s '%p1l,'%s\n'%p1l]):
				pprint('%s %s/%s [%s] %s' % (jid,room,nick,access_mode,text),'bright_cyan')
				no_comm = None
				if not parse[3]: thr(parse[2],(type, room, nick, parse[4:]),parse[1])
				elif parse[3] == 1: thr(parse[2],(type, room, nick),parse[1])
				elif parse[3] == 2: thr(parse[2],(type, room, nick, text[len(parse[1])+1:]),parse[1])
				last_command = [access_mode, nowname, type, room, nick, text, jid, time.time()]
				break
	return no_comm

def messageCB(sess,mess):
	global lfrom, lto, lastserver, lastnick, comms, message_in, no_comm, last_hash, current_join
	message_in += 1
	type=str(mess.getType())
	room=str(mess.getFrom().getStripped())
	allow_execute = True
	if getRoom(Settings['jid']) == getRoom(room):
		allow_execute = False
		msg = 'Warning! Self-message detected!'
		pprint('!!! %s Stanza:\n%s' % (msg,str(mess)))
		own = cur_execute_fetchall('select * from bot_owner;')
		if own:
			for ajid in own: send_msg('chat', ajid[0], '', msg)
	text=str(mess.getBody())
	id = mess.getID()
	try: was_send = messages_log.pop(id)
	except: was_send = None
	if was_send and type == 'error' and mess.getTag('error',attrs={'code':'500','type':'wait'}):
		time.sleep(time_limit)
		sender(was_send)
		return

	if current_join and room in [getRoom(t) for t in current_join.keys()]:
		try:
			tt = {}
			for t in mess.getTag('captcha',attrs={'xmlns':'urn:xmpp:captcha'}).getTag('x',attrs={'xmlns':'jabber:x:data'}).getTags('field'):
				tt[t.getAttrs()['var']] = t.getTagData('value')
			if current_join[tt['from']] == tt['sid']:
				pprint('*** Captcha: %s' % text)
				current_join[tt['from']] = text
			else: current_join.pop(tt['from'])
		except:
			for t in current_join.keys():
				if room == getRoom(t):
					current_join.pop(t)
					break
	try:
		code = mess.getTag('x',namespace=xmpp.NS_MUC_USER).getTagAttr('status','code')
		if code in msg_status_codes:
			append_message_to_log(room,'','',type,msg_status_codes[code])
			return
	except: pass
	if (text == 'None' or text == '') and not mess.getSubject(): return
	if mess.getTimestamp() != None: return
	nick=mess.getFrom().getResource()
	if nick != None: nick = str(nick)
	towh=str(mess.getTo().getStripped())
	lprefix = get_local_prefix(room)
	ft = back_text = text
	rn = '%s/%s' % (room,nick)
	access_mode,jid = get_level(room,nick)
	if not allow_execute: access_mode = -1
	nowname = get_xnick(room)
	if '@' not in jid and (jid == 'None' or jid.startswith('j2j.')) and is_owner(room): access_mode = 9
	if type == 'groupchat' and nick != '' and access_mode >= 0 and jid not in ['None',Settings['jid']]: talk_count(room,jid,nick,text)
	if nick != '' and nick != None and nick != nowname and len(text)>1 and text != 'None' and text != to_censore(text,room) and access_mode >= 0 and get_config(getRoom(room),'censor'):
		cens_text = L('Censored!',rn)
		lvl = get_level(room,nick)[0]
		if lvl >= 5 and get_config(getRoom(room),'censor_warning'): send_msg(type,room,nick,cens_text)
		elif lvl == 4 and get_config(getRoom(room),'censor_action_member') != 'off':
			act = get_config(getRoom(room),'censor_action_member')
			muc_filter_action(act,jid,room,cens_text)
		elif lvl < 4 and get_config(getRoom(room),'censor_action_non_member') != 'off':
			act = get_config(getRoom(room),'censor_action_non_member')
			muc_filter_action(act,jid,room,cens_text)
	no_comm = True
	if text != 'None' and text and access_mode >= 0 and not mess.getSubject():
		no_comm = True
		is_par = False
		if text.startswith('%s: ' % nowname) or text.startswith('%s, ' % nowname):
			text = text[len(nowname)+2:]
			is_par = True
		btext = text
		if text.startswith(lprefix):
			text = text[len(lprefix):]
			is_par = True
		if type == 'chat': is_par = True
		if is_par: no_comm = com_parser(access_mode, nowname, type, room, nick, text, jid)
		if no_comm:
			btl = btext.lower()
			if base_type == 'mysql': alias = cur_execute_fetchone("select match ,cmd from alias where (room=%s or room=%s) and ( match =%s or %s like concat( match ,' %%')) order by room desc",(room,'*',btl,btl))
			else: alias = cur_execute_fetchone("select match ,cmd from alias where (room=%s or room=%s) and ( match =%s or %s ilike match ||' %%') order by room desc",(room,'*',btl,btl))
			if alias:
				pprint('%s %s/%s [%s|alias] %s' % (jid,room,nick,access_mode,text),'bright_cyan')
				argz = btext[len(alias[0])+1:]
				if not argz:
					ppr = alias[1].replace('%*', '').replace('%{reduce}*', '').replace('%{reduceall}*', '').replace('%{unused}*', '')
					ppr = re.sub('\%\{nick2jid\}[0-9\*]','',ppr,flags=re.S|re.I|re.U)
					cpar = re.findall('%([0-9]+)', ppr, re.S)
					if len(cpar):
						for tmp in cpar:
							try: ppr = ppr.replace('%'+tmp,'')
							except: pass
				else:
					if '%' not in alias[1]: ppr = '%s %%*' % alias[1]
					else: ppr = alias[1]
					ppr = ppr.replace('%*', argz).replace('%{reduce}*', argz.strip()).replace('%{reduceall}*', reduce_spaces_all(argz))
					argz = argz.split()
					argzbk = list(argz)
					n2j = re.findall('\%\{nick2jid\}([0-9])',ppr,flags=re.S|re.I|re.U)
					if n2j:
						for t in n2j:
							it = int(t)
							try: curr_nick = get_jid_by_nick(room, argz[it])
							except: curr_nick = ''
							ppr = re.sub('\%%\{nick2jid\}%s' % t,curr_nick,ppr,flags=re.S|re.I|re.U)
							argzbk = argzbk[:it]+argzbk[it+1:]
					n2j = re.findall('\%\{nick2jid\}(\*)',ppr,flags=re.S|re.I|re.U)
					if n2j:
						curr_nick = get_jid_by_nick(room, ' '.join(argz))
						ppr = re.sub('\%\{nick2jid\}[0-9\*]',curr_nick,ppr,flags=re.S|re.I|re.U)
						argz = []
					if '%' in ppr:
						cpar = re.findall('%([0-9]+)', ppr, re.S)
						if cpar:
							for tmp in cpar:
								try:
									it = int(tmp)
									ppr = ppr.replace('%'+tmp,argz[it])
									argzbk = argzbk[:it]+argzbk[it+1:]
								except: pass
							ppr = ppr.replace('%{unused}*',' '.join(argzbk))

				if len(ppr) == ppr.count(' '): ppr = ''
				no_comm = com_parser(access_mode, nowname, type, room, nick, ppr, jid)

	thr(msg_afterwork,(mess,room,jid,nick,type,back_text,no_comm,access_mode,nowname),'msg_afterwork')

def msg_afterwork(mess,room,jid,nick,type,back_text,no_comm,access_mode,nowname):
	global topics
	not_alowed_flood = False
	subj = str(mess.getSubject())
	text = back_text
	if subj != 'None':
		if '\n' in subj: subj = '\n%s'  % subj
		subj = L('*** Topic: %s','%s/%s'%(room,nick)) % subj
		if len(text) and text != 'None': subj = text.replace(': ',':\n',1) if '\n' in text else text
		topics[room],nick = subj,''
		text = subj
		not_alowed_flood, no_comm = True, False

	# Fetch and store xhtml images!
	if type == 'groupchat' and get_config(room,'paste_xhtml_images'):
		try:
			raw_data = mess.getTag('html',attrs={'xmlns':'http://jabber.org/protocol/xhtml-im'}).getTag('body').getTagAttr('img','src')
			ext = re.findall('^data:image/(.*?);',raw_data[:32])[0]
			def unproc(t): return chr(int(t.group(0)[1:],16))
			img = re.sub('%[0-9A-F]{2}', unproc, raw_data.split('base64,',1)[1]).decode('base64')
			filename = '%s.%s' % (hex(int(time.time()))[2:],ext)
			fp = open(pastepath + filename, 'wb')
			fp.write(img)
			fp.close()
			send_msg(type, room, '', L('Fetched xhtml image: %s%s','%s/%s'%(room,nick)) % (pasteurl,filename))
			pprint('*** Fetched xhtml image from %s/%s [%s], size %s, file %s' % (room,nick,jid,len(img),pastepath + filename),'cyan')
		except: pass

	for tmp in gmessage: not_alowed_flood = tmp(room,jid,nick,type,text) or not_alowed_flood
	if no_comm:
		for tmp in gactmessage: not_alowed_flood = not_alowed_flood or tmp(room,jid,nick,type,text)
	if not not_alowed_flood and no_comm and text not in ['None','',' '] and not mess.getSubject():
		if room != selfjid: is_flood = get_config(getRoom(room),'flood') not in ['off',False]
		else: is_flood = None
		if selfjid != jid and access_mode >= 0 and (back_text[:len(nowname)+2] == nowname+': ' or back_text[:len(nowname)+2] == nowname+', ' or type == 'chat') and is_flood:
			pprint('Send msg human: %s/%s [%s] <<< %s' % (room,nick,type,text),'dark_gray')
			if len(back_text) > 128: send_msg_human(type, room, nick, L('Too many letters!','%s/%s'%(room,nick)), 'msg_human')
			else:
				if back_text[:len(nowname)] == nowname: back_text = back_text[len(nowname)+2:]
				try:
					text = getAnswer(type, room, nick, back_text)
					if text: send_msg_human(type, room, nick, text, 'msg_human')
				except: pass

def send_msg_human(type, room, nick, text, th):
	if nick: rn = '%s/%s' % (room,nick)
	else: rn = room
	pprint('Send msg human: %s [%s] >>> %s' % (rn,type,text),'dark_gray')
	thr(send_msg_hmn,(type, room, nick, text),th)

def send_msg_hmn(type, room, nick, text):
	time.sleep(len(text)/4.0+random.randint(0,3))
	send_msg(type, room, nick, text)

def to_censore(text,room):
	ccn = []
	custom_replace = {}
	if get_config(getRoom(room),'censor_custom'):
		custom_censor = get_config(getRoom(room),'censor_custom_rules').replace('\r','').replace('\t','').split('\n')
		for c in custom_censor:
			if c.count('#') <= 1: cc=c.split('#',1)
			else:
				cc=c.split('#')
				if cc[-1] == cc[-2] == '': cc = ['#'.join(cc[:-2]),'#']
				else: cc = ['#'.join(cc[:-1]),cc[-1]]
			if cc[0]:
				try:
					_ = re.compile(cc[0],re.S|re.I|re.U)
					ccn.append(cc[0])
					if len(cc) > 1: custom_replace[cc[0]] = reduce_spaces_all(cc[1])
				except: pass
	wca = None
	def_replacer = GT('censor_text')
	for c in censor+ccn:
		cn = re.findall(c,' %s ' % text,re.S|re.I|re.U)
		for tmp in cn:
			t = ''.join(tmp)
			try: replacer = custom_replace[c]
			except: replacer = def_replacer
			text,wca = text.replace(t,[replacer*len(t),replacer][len(replacer)>1]),True
	if wca: text = text.strip(' ')
	return text

def check_hash_actions():
	global hash_actions_list
	if hash_actions_list:
		for tmp in hash_actions_list.keys():
			if time.time() > hash_actions_list[tmp][1]:
				hal = hash_actions_list.pop(tmp)
				if hal[0] == L('whitelist'): put_config(tmp,'muc_filter_whitelist',False)
				elif hal[0] == L('lock by hash'):
					hashes_list = reduce_spaces_all(get_config(tmp,'muc_filter_deny_hash_list').replace(',',' ').replace(';',' ').replace('|',' ')).split()
					if hal[2] in hashes_list:
						hashes_list.remove(hal[2])
						put_config(tmp,'muc_filter_deny_hash_list',' '.join(hashes_list))
					if not hashes_list: put_config(tmp,'muc_filter_deny_hash',False)
				pprint('Removed hash action %s in %s' % (hal[0],tmp),'cyan')

def presenceCB(sess,mess):
	global megabase, pres_answer, cu_age, presence_in, hashes, last_hash, users_locale
	presence_in += 1
	room=str(mess.getFrom().getStripped())
	nick=str(mess.getFrom().getResource())
	text=str(mess.getStatus())
	type=str(mess.getType())
	affiliation=str(mess.getAffiliation())
	role=str(mess.getRole())
	jid=str(mess.getJid())
	mss = str(mess)
	bad_presence = mss.count('<x xmlns=\"http://jabber') > 1 and mss.count(' affiliation=\"') > 1 and mss.count(' role=\"') > 1
	priority=str(mess.getPriority())
	show=str(mess.getShow())
	reason=str(mess.getReason())
	status=str(mess.getStatusCode())
	try: chg_nick = [None,esc_max2(mess.getTag('x',namespace=xmpp.NS_MUC_USER).getTagAttr('item','nick'))][status == '303']
	except: chg_nick = None
	actor=str(mess.getActor())
	to=str(mess.getTo())
	id = mess.getID()
	tt = int(time.time())
	if type=='error':
		try: error_code = mess.getTagAttr('error','code')
		except: error_code = None
		try: error_type = mess.getTagAttr('error','type')
		except: error_type = None
		try:
			error_text = mess.getTag('error').getTagData(tag='text')
			if not error_text: raise
		except:
			try: error_text = get_tag(str(mess.getTag('error')),'error')
			except: error_text = L('Unknown error!')
		if not error_text and presence_error.__contains__(error_code): error_text = presence_error[error_code]
		if error_code:
			if error_type: error_msg = '%s [%s]' % (error_code,error_type)
			else: error_msg = error_code
			if error_text: error_msg = '%s - %s' % (error_msg,error_text)
		else:
			if error_type: error_msg = '%s [%s]' % (error_type.capitalize(),error_text)
			else: error_msg = error_text
		pres_answer.append((id,error_msg,tt))
		return
	elif id != None: pres_answer.append((id,None,tt))
	if jid == 'None': jid = get_level(room,nick)[1]

	# Get Caps
	if '%s|%s' % (role,affiliation) in levl:
		id_ver = id_lang = id_photo = id_avatar = 'error!'
		hash_error = False
		try: id_node = get_eval_item(mess,'getTag("c",namespace=xmpp.NS_CAPS).getAttr("node")')
		except: id_node,hash_error = get_eval_item(mess,'getTag("c",namespace=xmpp.NS_CAPS).getAttr("node")'),True
		try: id_ver = get_eval_item(mess,'getTag("c",namespace=xmpp.NS_CAPS).getAttr("ver")')
		except: id_ver,hash_error = get_eval_item(mess,'getTag("c",namespace=xmpp.NS_CAPS).getAttr("ver")'),True
		try: id_hash = get_eval_item(mess,'getTag("c",namespace=xmpp.NS_CAPS).getAttr("hash")')
		except: id_hash,hash_error = get_eval_item(mess,'getTag("c",namespace=xmpp.NS_CAPS).getAttr("hash")'),True
		try: id_ext = get_eval_item(mess,'getTag("c",namespace=xmpp.NS_CAPS).getAttr("ext")')
		except: id_ext,hash_error = get_eval_item(mess,'getTag("c",namespace=xmpp.NS_CAPS).getAttr("ext")'),True
		try:
			id_bmver = mess.getAttr('ver')
			if id_bmver or id_bmver == '': id_bmver = '%s_' % id_bmver
			else: id_bmver = ''
		except: id_bmver = ''
		if id_ext and id_hash: id_hash = '%s|%s' % (id_hash,id_ext)
		elif id_ext: id_hash = id_ext
		if id_hash: capses['%s/%s' % (room,nick)] = '%s\n%s [%s]\n%s' % (id_node,id_ver,id_hash,id_bmver)
		else: capses['%s/%s' % (room,nick)] = '%s\n%s\n%s' % (id_node,id_ver,id_bmver)

	# Hash control
	if '%s|%s' % (role,affiliation) in levl and levl['%s|%s' % (role,affiliation)] <= 3:
		try: id_lang = get_eval_item(mess,'getAttr("xml:lang")')
		except: id_lang,hash_error = get_eval_item(mess,'getAttr("xml:lang")'),True
		try: id_photo = get_eval_item(mess,'getTag("x",namespace=xmpp.NS_VCARD_UPDATE).getTagData("photo")')
		except: id_photo,hash_error = get_eval_item(mess,'getTag("x",namespace=xmpp.NS_VCARD_UPDATE).getTagData("photo")'),True
		try: id_avatar = get_eval_item(mess,'getTag("x",namespace=xmpp.NS_AVATAR).getTagData("hash")')
		except: id_avatar,hash_error = get_eval_item(mess,'getTag("x",namespace=xmpp.NS_AVATAR).getTagData("hash")'),True

		hash_body = '<'.join([str(tmp) for tmp in (id_avatar,id_photo,id_ver,id_bmver,id_lang)]) + '<<'
		if hash_error:
			writefile(slog_folder % 'bad_stanza_%s.txt' % int(time.time()),str(mess).encode('utf-8'))
			hash_body = parser(hash_body)

		current_hash = hashlib.md5(hash_body.encode('utf-8')).digest().encode('base64').replace('\n','')
		hashes['%s/%s' % (room,nick)] = current_hash
		if type != 'unavailable' and not get_config(room,'muc_filter_whitelist') and get_config(room,'muc_filter_hash'):
			try: lh = last_hash[room]
			except: lh = [current_hash,[]]
			try:
				hash_ev = get_config_int(room,'muc_filter_hash_events')
				if not 3 <= hash_ev <= 1000: raise
			except:
				hash_ev = config_prefs['muc_filter_hash_events'][3]
				put_config(room,'muc_filter_hash_events',hash_ev)
			try:
				hash_tm = get_config_int(room,'muc_filter_hash_time')
				if not 3 <= hash_tm <= 1000: raise
			except:
				hash_tm = config_prefs['muc_filter_hash_time'][3]
				put_config(room,'muc_filter_hash_time',hash_tm)
			if current_hash == lh[0]:
				if lh[1]: lh[1] = [time.time()] + lh[1][:hash_ev-1]
				else: lh[1] = [time.time()]
				lh[0] = current_hash
			else: lh = [current_hash,[]]
			if lh[1] and len(lh[1]) == hash_ev and lh[1][0]-lh[1][-1] <= hash_tm:
				pprint('Hash high action: %s %s/%s' % (current_hash,room,nick),'red')
				h_action = get_config(room,'muc_filter_hash_action')
				try:
					h_action_time = get_config_int(room,'muc_filter_hash_action_time')
					if not 3 <= h_action_time <= 86400: raise
				except:
					h_action_time = config_prefs['muc_filter_hash_action_time'][3]
					put_config(room,'muc_filter_hash_action_time',h_action_time)
				hash_actions_list[room] = [h_action,h_action_time+time.time(),current_hash]
				if h_action == L('whitelist'): put_config(room,'muc_filter_whitelist',True)
				elif h_action == L('lock by hash'):
					put_config(room,'muc_filter_deny_hash',True)
					hashes_list = reduce_spaces_all(get_config(room,'muc_filter_deny_hash_list').replace(',',' ').replace(';',' ').replace('|',' ')).split()
					if not current_hash in hashes_list:
						hashes_list.append(current_hash)
						put_config(room,'muc_filter_deny_hash_list',' '.join(hashes_list))
				h_action_current = get_config(room,'muc_filter_hash_action_current')
				if h_action_current != L('off'):
					pprint('MUC-Filter initial flush by hash: %s %s' % (room,jid),'brown')
					muc_filter_action(h_action_current,jid,room,L('Deny by hash lock!'))
					for tmp in hashes.keys():
						tmp_room,tmp_nick = tmp.split('/',1)
						if tmp_room == room and hashes[tmp] == current_hash:
							tmp_access,tmp_jid = get_level(room,tmp_nick)
							if tmp_access <= 3 and tmp_jid != 'None':
								in_base = cur_execute_fetchall('select sum(sum) from (select sum(age) from age where jid=%s and room=%s union all select %s-time from age where jid=%s and room=%s and status=0) as sum_age;',(getRoom(tmp_jid),room,int(time.time()),getRoom(tmp_jid),room))
								if not in_base: nmute = True
								else:
									newbie_time = get_config(room,'muc_filter_newbie_time')
									if newbie_time.isdigit(): newbie_time = int(newbie_time)
									else: newbie_time = 60
									if in_base[0] < newbie_time: nmute = True
									else: nmute = False
								if nmute:
									pprint('MUC-Filter flush by hash: %s %s' % (room,tmp_jid),'brown')
									muc_filter_action(h_action_current,tmp_jid,room,L('Deny by hash lock!'))
				lh = [current_hash,[]]
			last_hash[room] = lh

	if bad_presence: send_msg('groupchat', room, '', L('/me detect bad stanza from %s') % nick)
	nowname = get_xnick(room.lower())
	not_found,exit_type,exit_message = 0,'',''
	if type=='unavailable':
		try: users_locale.pop('%s/%s' % (room,nick))
		except: pass
		if status=='307': exit_type,exit_message = L('kicked'),reason
		elif status=='321': exit_type,exit_message = L('kicked'),L('revoke member affiliation in members-only room')
		elif status=='301': exit_type,exit_message = L('banned'),reason
		elif status=='303': exit_type,exit_message = L('change nick to %s') % chg_nick,''
		else: exit_type,exit_message = L('leave'),text
		if exit_message == 'None': exit_message = ''
		try: exit_message += '\r' + acl_ver_tmp['%s/%s' % (room,nick)]
		except: pass
		if nick != '':
			for mmb in megabase:
				if mmb[0]==room and mmb[1]==nick:
					megabase.remove(mmb)
					break
			if to == selfjid and status in ['307','301'] and cur_execute_fetchone('select room from conference where room=%s',('%s/%s' % (room,nick),)):
				cur_execute('delete from conference where room ilike %s;', ('%s/%%'%getRoom(room),))
				pprint('*** bot was %s %s %s' % (['banned in','kicked from'][status=='307'],room,exit_message),'red')
				if GT('kick_ban_notify'):
					ntf_list = GT('kick_ban_notify_jid').replace(',',' ').replace('|',' ').replace(';',' ')
					while '  ' in ntf_list: ntf_list = ntf_list.replace('  ',' ')
					ntf_list = ntf_list.split()
					if len(ntf_list):
						ntf_msg = [L('banned in'),L('kicked from')][status == '307']
						ntf_msg = L('Bot was %s %s with reason: %s') % (ntf_msg,room,exit_message)
						for tmp in ntf_list: send_msg('chat', tmp, '', ntf_msg)
	else:
		if reason: exit_message = reason
		c_lang = mess.getAttr('xml:lang')
		if c_lang and nick: users_locale['%s/%s' % (room,nick)] = c_lang[:2].replace('uk','ua')
		if nick != '':
			for mmb in megabase:
				if mmb[0]==room and mmb[1]==nick:
					megabase.remove(mmb)
					megabase.append([room, nick, role, affiliation, jid])
					if role != mmb[2] or affiliation != mmb[3]: not_found = 1
					else: not_found = 2
					break
			if not not_found: megabase.append([room, nick, role, affiliation, jid])
	if jid == 'None': jid, jid2 = '<temporary>%s' % nick, 'None'
	else: jid2, jid = jid, getRoom(jid.lower())
	for tmp in gpresence: thr(tmp,(room,jid2,nick,type,(text, role, affiliation, exit_type, exit_message, show, priority, not_found, chg_nick)),'presence_afterwork')
	al = get_level(getRoom(room),nick)[0]
	if type == 'subscribe':
		if al == 9:
			caps_and_send(xmpp.Presence(room, 'subscribed'))
			caps_and_send(xmpp.Presence(room, 'subscribe'))
			pprint('Subscribtion accepted %s' % room,'light_gray')
		else:
			caps_and_send(xmpp.Presence(room, 'unsubscribed'))
			pprint('Subscribtion rejected %s' % room,'red')
	elif type == 'unsubscribed':
		if al == 9:
			caps_and_send(xmpp.Presence(room, 'unsubscribe'))
			caps_and_send(xmpp.Presence(room, 'unsubscribed'))
			pprint('Unsubscribtion accepted %s' % room,'light_gray')
		else: pprint('Unsubscribtion rejected %s' % room,'red')
	if reason in ['','None',None] and nick != '' and (nick != 'None' or (text != 'None' and len(text)>1)) and nick != nowname and al >= 0 and get_config(getRoom(room),'censor'):
		nt = '%s %s' % (nick,text)
		if nt != to_censore(nt,room):
			cens_text = L('Censored!','%s/%s'%(room,nick))
			if al >= 5 and get_config(getRoom(room),'censor_warning'): send_msg('groupchat',room,nick,cens_text)
			elif al == 4 and get_config(getRoom(room),'censor_action_member') != 'off':
				act = get_config(getRoom(room),'censor_action_member')
				muc_filter_action(act,jid2,getRoom(room),cens_text)
			elif al < 4 and get_config(getRoom(room),'censor_action_non_member') != 'off':
				act = get_config(getRoom(room),'censor_action_non_member')
				muc_filter_action(act,jid2,getRoom(room),cens_text)
	ab = cur_execute_fetchone('select * from age where room=%s and jid=%s and nick=%s',(room, jid, nick))
	ttext = '%s\n%s\n%s\n%s\n%s' % (role,affiliation,priority,show,text)
	if ab:
		if type=='unavailable': cur_execute('update age set time=%s, age=%s, status=%s, type=%s, message=%s where room=%s and jid=%s and nick=%s', (tt,ab[4]+(tt-ab[3]),1,exit_type,exit_message,room, jid, nick))
		else:
			if ab[5]: cur_execute('update age set time=%s, status=%s, message=%s where room=%s and jid=%s and nick=%s', (tt,0,ttext,room, jid, nick))
			else: cur_execute('update age set status=%s, message=%s where room=%s and jid=%s and nick=%s', (0,ttext,room, jid, nick))
	else: cur_execute('insert into age values (%s,%s,%s,%s,%s,%s,%s,%s,%s)', (room,nick,jid,tt,0,0,'',ttext,nick.lower()))

	if not cur_execute_fetchone('select time from first_join where room=%s and jid=%s;',(room,jid)):
		cur_execute('insert into first_join values (%s,%s,%s);', (room,jid,tt))

def onoff_no_tr(msg):
	if msg in [None,False,0,'0']: return 'off'
	elif msg in [True,1,'1']: return 'on'
	else: return msg

def onoff(*msg):
	if len(msg) == 1: return L(onoff_no_tr(msg[0]))
	else: return L(onoff_no_tr(msg[0]),msg[1])

def getName(jid):
	jid = str(jid)
	if jid == 'None': return jid
	if '@' not in jid: return ''
	return jid[:jid.find('@')].lower()

def getServer(jid):
	jid = str(jid)
	if '/' not in jid: jid += '/'
	if jid == 'None': return jid
	return jid[jid.find('@')+1:jid.find('/')].lower()

def getResourse(jid):
	jid = str(jid)
	if jid == 'None': return jid
	try: return jid.split('/')[1]
	except: return ''

def getRoom(jid):
	jid = str(jid)
	if jid == 'None': return jid
	if '@' in jid: return '%s@%s' % (getName(jid),getServer(jid))
	else: return getServer(jid)

def now_schedule():
	while not game_over:
		to = GT('schedule_time')
		while to > 0 and not game_over:
			time.sleep(2)
			to -= 2
		if not game_over:
			for tmp in gtimer:
				if not game_over: thr(tmp,(),('time_thread_%s' % tmp).split(' ',2)[1])

def check_rss():
	global rss_processed
	if rss_processed: return
	l_hl = int(time.time())
	feedbase = cur_execute_fetchall('select * from feed order by time;')
	if feedbase:
		for fd in feedbase:
			ltime = fd[1]
			timetype = ltime[-1:].lower()
			if not timetype in ('h','m'): timetype = 'h'
			try: ofset = int(ltime[:-1])
			except: ofset = 4
			if timetype == 'h': ofset *= 3600
			elif timetype == 'm': ofset *= 60
			try: ll_hl = int(fd[3])
			except: ll_hl = 0
			in_room = cur_execute_fetchone('select room from conference where room ilike %s',('%s/%%'%fd[4],))
			if ofset < 600: ofset = 600
			if in_room and ll_hl + ofset <= l_hl:
				rss_processed = True
				pprint('check rss: %s in %s' % (fd[0],fd[4]),'green')
				rss('groupchat', fd[4], 'RSS', 'new %s 10 %s silent' % (fd[0],fd[2]))
				break
	rss_processed = False

def talk_count(room,jid,nick,text):
	jid = getRoom(jid)
	ab = cur_execute_fetchone('select * from talkers where room=%s and jid=%s',(room,jid))
	wtext = len(reduce_spaces_all(text).split(' '))
	if ab: cur_execute('update talkers set nick=%s, words=%s, frases=%s where room=%s and jid=%s', (nick,ab[3]+wtext,ab[4]+1,room,jid))
	else: cur_execute('insert into talkers values (%s,%s,%s,%s,%s)', (room, jid, nick, wtext, 1))

def flush_stats():
	pprint('Executed threads: %s | Error(s): %s' % (th_cnt,thread_error_count),'bright_blue')
	pprint('Message in %s | out %s' % (message_in,message_out),'bright_blue')
	pprint('Presence in %s | out %s' % (presence_in,presence_out),'bright_blue')
	pprint('Iq in %s | out %s' % (iq_in,iq_out),'bright_blue')
	pprint('Unknown out %s' % unknown_out,'bright_blue')
	pprint('Cycles used %s | unused %s' % (cycles_used,cycles_unused),'bright_blue')
	if is_win32: ctypes.windll.Kernel32.SetConsoleTextAttribute(win_console_color, get_color_win32('clear'))

def disconnecter():
	global bot_exit_type, game_over
	pprint('--- Restart by disconnect handler! ---','bright_blue')
	pprint('--- Last stanza ---','bright_blue')
	pprint(last_stanza,'bright_blue')
	pprint('-'*19,'blue')
	game_over, bot_exit_type = True, 'restart'
	time.sleep(2)

def get_L_(jid):
	if jid:
		_CL = get_config(getRoom(jid),'set_default_locale')
		if _CL != 'off' and locales.__contains__(_CL): return _CL
	try:
		loc = users_locale[jid]
		if locales.__contains__(loc): return loc
		else: return CURRENT_LOCALE
	except: return CURRENT_LOCALE

def L(*par):
	if len(par) == 2: text,jid = par
	else: text,jid = par[0],''
	if not len(text): return text
	loc = get_L_(jid)
	try: return locales[loc][text]
	except: return text
	
def get_id():
	global id_count
	id_count += 1
	return 'request_%s' % id_count

def draw_warning(wt):
	wt = '!!! Warning! %s !!!' % wt
	pprint('!'*len(wt),'bright_red')
	pprint(wt,'bright_red')
	pprint('!'*len(wt),'bright_red')

def get_bot_version():
	if os.path.isfile(ver_file):
		bvers = readfile(ver_file).replace('\n','').replace('\r','').replace('\t','').replace(' ','')
		bV = '%s.%s-%s' % (botVersionDef,bvers,base_type)
		if bvers[:-1] == 'M': draw_warning('Launched bot\'s modification!')
	else: bV = '%s-%s' % (botVersionDef, base_type)
	return bV

def update_locale():
	global CURRENT_LOCALE
	locales = {}
	llist = ['en'] + [tmp[:-4] for tmp in os.listdir(loc_folder[:-6]) if tmp[-4:]=='.txt']
	CL = cur_execute_fetchone('select value from config_owner where option = %s', ('bot_locale',))
	if CL: CURRENT_LOCALE = CL[0]
	for t in llist:
		locales[t] = {}
		lf = loc_folder % t
		if os.path.isfile(lf):
			lf = readfile(lf).decode('UTF').replace('\r','').split('\n')
			for c in lf:
				if ('#' not in c[:3]) and len(c) and '\t' in c: locales[t][c.split('\t',1)[0].replace('\\n','\n').replace('\\t','\t')] = c.split('\t',1)[1].replace('\\n','\n').replace('\\t','\t')
	return locales,CURRENT_LOCALE

def init_hash():
	id_category = 'client'
	id_type = 'bot'
	id_lang = CURRENT_LOCALE
	id_name = 'iSida'
	capsHash = '%s<' % '/'.join([t.replace('/','//') for t in [id_category,id_type,id_lang,id_name]])
	bot_features.sort()
	capsHash += ''.join(['%s<' % t for t in bot_features])
	capsHash += '%s<' % xmpp.NS_SOFTWAREINFO
	tmp = ['%s<%s' % (t,bot_softwareinfo[t]) for t in bot_softwareinfo.keys()]
	tmp.sort()
	capsHash += ''.join(['%s<' % t for t in tmp])
	capsHash = hashlib.sha1(capsHash.encode('utf-8')).digest().encode('base64').replace('\n','')
	return id_category,id_type,id_lang,id_name,capsHash

def get_repo():
	dirs = os.listdir('.')+os.listdir('../')
	if '.svn' in dirs: return 'svn'
	elif '.git' in dirs: return 'git'
	else: return 'unknown'

def get_value_from_array2(a,v):
	for t in a:
		if t[0] == v: return t[1]
	return None

def get_array_from_array2(a1,a2):
	a_res = []
	for t in a1:
		if t[0] in a2: a_res.append(t)
	return a_res

def self_vcard():
	global iq_request
	iqid = get_id()
	i = xmpp.Node('iq', {'id': iqid, 'type': 'get', 'to':getRoom(selfjid)}, payload = [xmpp.Node('vCard', {'xmlns': xmpp.NS_VCARD},[])])
	iq_request[iqid]=(time.time(),self_vcard_async,[],xmpp.NS_VCARD)
	sender(i)

def self_vcard_async(is_answ):
	global photo_hash
	try: vc = is_answ[1][1].getTag('vCard',namespace=xmpp.NS_VCARD)
	except: return
	if not vc or str(vc) == '<vCard xmlns="vcard-temp" />': return
	else:
		data = []
		for t in vc.getChildren():
			if t.getChildren():
				cm = []
				for r in t.getChildren():
					if r.getData(): cm.append(('%s.%s' % (t.getName(),r.getName()),str(r.getData())))
				data += cm
			elif t.getData(): data.append((t.getName(),t.getData()))
		if data:
			try:
				photo_data = get_value_from_array2(data,'PHOTO.BINVAL').decode('base64')
				photo_hash = hashlib.sha1(photo_data).hexdigest()
				PT('photo_hash',photo_hash)
			except: pass

# --------------------- Иницилизация переменных ----------------------

nmbrs = ['0','1','2','3','4','5','6','7','8','9','.']
ul = slog_folder % 'update.log'				# лог последнего обновление
halt_on_exception = False					# остановка на ошибках
debug_xmpppy = False				# отладка xmpppy
debug_console = False				# отладка действий бота
CommandsLog = None					# логгирование команд
prefix = '_'						# префикс комманд
msg_limit = 1000					# лимит размера сообщений
botName = 'iSida'					# название бота
botVersionDef = u'4.0'				# версия бота
disco_config_node = 'http://isida.dsy.name/config'
pres_answer = []					# результаты посылки презенсов
iq_request = {}						# iq запросы
th_cnt = 0							# счётчик тредов
thread_error_count = 0				# счётчик ошибок тредов
bot_exit_type = None				# причина завершения бота
last_stream = []					# очередь станз к отправке
last_command = []					# последняя исполненная ботом команда
messages_log = {}					# лог отправленных сообщений
thread_type = True					# тип тредов
time_limit = 0.5					# максимальная задержка между посылкой станз с одинаковым типом в groupchat
time_nolimit = 0.1					# задержка между посылкой станз с разными типами
message_in,message_out = 0,0		# статистика сообщений
iq_in,iq_out = 0,0					# статистика iq запросов
presence_in,presence_out = 0,0		# статистика презенсов
unknown_out = 0						# статистика ошибочных отправок
cycles_used,cycles_unused = 0,0		# статистика циклов
id_count = 0						# номер запроса
megabase = []						# главная временная база с полной информацией из презенсов
ignore_owner = None					# исполнять отключенные команды для владельца бота
configname = 'settings/config.py'	# конфиг бота
topics = {}							# временное хранение топиков
last_msg_base = {}					# последние сообщения
last_msg_time_base = {}				# время между последними сообщениями последние сообщения
no_comm = True
muc_rejoins = {}
muc_statuses = {}
muc_repeat = {}
last_stanza = ''					# последняя станза, посланная ботом
ENABLE_TLS = True					# принудительное отключение TLS
ENABLE_SASL = True					# включение SASL
base_timeout = 20					# таймаут на доступ ко всем базам
between_msg_last = {}				# время последнего сообщения
last_sender_activity = time.time()	# время последней отправки
hashes = {}							# хеши презенсов
capses = {}							# капсы клиентов
last_hash = {}						# хеш последнего события
hash_actions_list = {}				# список действий на хеш-события
smile_folder = '.smiles'			# папка со смайлами в чатлогах
smile_descriptor = 'icondef.xml'	# дескриптор смайлов
last_logs_store = []				# последние записи в логах
last_logs_size = 20					# максимальное количество последних записей в логах
ddos_ignore = {}					# данные при подозрении на ddos
ddos_iq = {}
CURRENT_LOCALE = 'en'
user_hash = {}
server_hash = {}
server_hash_list = {}
newbie_msg = {}
mute_msg = {}
messages_excl = []
rss_processed = False
database_debug = False
current_join = {}
default_censor_set = 1				# номер набора правил для цензора
users_locale = {}
base_type = 'pgsql'     # тип базы: pgsql, mysql, sqlite3
base_name = 'isidabot'  # название базы
base_user = 'isidabot'  # пользователь базы
base_host = 'localhost' # хост базы
base_port = '5432'		# порт для подключения
base_charset = 'utf8'   # кодировка
bot_features = [xmpp.NS_DISCO_INFO,xmpp.NS_DISCO_ITEMS,xmpp.NS_COMMANDS,disco_config_node,xmpp.NS_PING,xmpp.NS_URN_TIME,xmpp.NS_X_OOB,xmpp.NS_MUC_FILTER,
				xmpp.NS_VERSION,xmpp.NS_TIME,xmpp.NS_MUC,xmpp.NS_LAST,xmpp.NS_DATA,xmpp.NS_MUC_ROOMS]

gc.enable()

gt=tuple(time.gmtime())
lt=tuple(time.localtime())
if lt[0:3] == gt[0:3]: timeofset = int(lt[3])-int(gt[3])
elif lt[0:3] > gt[0:3]: timeofset = int(lt[3])-int(gt[3]) + 24
else: timeofset = int(gt[3])-int(lt[3]) + 24
is_win32 = sys.platform == 'win32'

if is_win32:
	import ctypes
	ctypes.windll.Kernel32.GetStdHandle.restype = ctypes.c_ulong
	win_console_color = ctypes.windll.Kernel32.GetStdHandle(ctypes.c_ulong(0xfffffff5))

if os.path.isfile(configname): exec(open(configname).read())
else: errorHandler('%s is missed.' % configname)

if base_type == 'pgsql':
	import psycopg2
	import psycopg2.extensions
elif base_type == 'mysql':
	import mysql.connector as mysqldb
elif base_type == 'sqlite3':
	import sqlite3
else: errorHandler('Unknown database backend!')

if base_type in ['mysql','sqlite3']:
	class psycopg2():
		def InterfaceError(): return None

if thread_type:
	import threading
	sema = threading.BoundedSemaphore(value=30)
	#try: threading.stack_size(65536)
	#except ValueError: pass

	class KThread(threading.Thread):
		def __init__(self, *args, **keywords):
			threading.Thread.__init__(self, *args, **keywords)
			self.killed = False

		def start(self):
			self.__run_backup = self.run
			self.run = self.__run
			threading.Thread.start(self)

		def __run(self):
			sys.settrace(self.globaltrace)
			self.__run_backup()
			self.run = self.__run_backup

		def globaltrace(self, frame, why, arg):
			if why == 'call': return self.localtrace
			else: return None

		def localtrace(self, frame, why, arg):
			if self.killed:
				if why == 'line': raise SystemExit()
			return self.localtrace

		def kill(self): self.killed = True

	def garbage_collector():
		gc.collect()
		garbage_collector_timer = threading.Timer(3600,garbage_collector)
		garbage_collector_timer.start()

	garbage_collector()

else: import thread

botVersion = get_bot_version()
try: tmp = botOs
except: botOs = os_version()

tmp_os,tmp_ver = os_version_disco()
bot_softwareinfo = {'software':botName,'software_version':'%s-%s-%s' % (botVersionDef,get_repo().replace('unknown','none'),base_type),'os':tmp_os,'os_version':tmp_ver}

sm_f = os.path.join(public_log % smile_folder)
if os.path.isdir(sm_f):
	smiles_dirs_case = [sd for sd in os.listdir(sm_f) if sd[0] != '.' and os.path.isfile(os.path.join(sm_f,sd,smile_descriptor))]
	smiles_dirs = [sd.lower() for sd in smiles_dirs_case]
else: smiles_dirs, smiles_dirs_case = [], []

logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)	# включение логгирования
capsNode = 'http://v4.isida.dsy.name'
god = SuperAdmin.lower()

pprint('-'*50,'blue')
if os.path.basename(sys.argv[0]) != 'isida.py': errorHandler('Ugly launch detect! Read wiki!')

if base_type == 'pgsql': conn = psycopg2.connect(database=base_name, user=base_user, host=base_host, password=base_pass, port=base_port)
elif base_type == 'mysql': pass
elif base_type == 'sqlite3': pass
else: errorHandler('Can\'t connect to `%s` base type!' % base_type)

own = cur_execute_fetchall('select * from bot_owner;')
if not own: cur_execute('insert into bot_owner values (%s)',(SuperAdmin.lower(),))

pprint('*** Loading localization','white')
locales,CURRENT_LOCALE = update_locale()

pprint('*** Init caps hash','white')
id_category,id_type,id_lang,id_name,capsHash = init_hash()

pl_folder	= 'plugins/%s'

gpresence	= []
gmessage	= []
gactmessage	= []
giq_hook    = []

pprint('*** Loading Main plugin','white')
exec(open(pl_folder % 'main.py').read())
pprint('*** Loading AI plugin','white')
exec(open(pl_folder % 'ai.py').read())

GTIMER_DEF  = [check_rss,check_hash_actions,clean_user_and_server_hash]
pliname		= data_folder % 'ignored.txt'
gtimer		= list(GTIMER_DEF)
pprint('*** Loading other plugins','white')

pl,pl_ignore,plugins = os.listdir(pl_folder % ''),getFile(pliname,[]),[]
for tmp in pl:
	if tmp.endswith('.py') and not tmp.startswith('.') and tmp not in ['main.py','ai.py']: plugins.append(tmp)
plugins.sort()

for pl in plugins:
	if pl in pl_ignore: pprint('Ignore plugin: %s' % pl,'red')
	else:
		presence_control,message_control,message_act_control,iq_control,timer,execute,iq_hook = [],[],[],[],[],[],[]
		pprint('Append plugin: %s' % pl,'cyan')
		exec(open(pl_folder % pl).read())
		for cm in execute: comms.append((cm[0],cm[1],cm[2],cm[3],'%s\r%s' % (pl[:-3],cm[4]))+cm[5:])
		for tmr in timer: gtimer.append(tmr)
		for tmp in presence_control: gpresence.append(tmp)
		for tmp in message_control: gmessage.append(tmp)
		for tmp in message_act_control: gactmessage.append(tmp)
		for tmp in iq_hook: giq_hook.append(tmp)
		giq_hook.sort()

if os.path.isfile(starttime_file):
	try: starttime = int(readfile(starttime_file))
	except: starttime = readfile(starttime_file)
else: starttime = int(time.time())
sesstime = int(time.time())
cu_age = []
close_age_null()
try:
	confbase = cur_execute_fetchall('select * from conference;')
	if confbase: confbase.sort()
	else: raise
except:
	c_room,c_passwd = '%s/%s' % (defaultConf.lower(),Settings['nickname']),''
	confbase = [(c_room,c_passwd)]
	cur_execute('insert into conference (room,passwd) values (%s,%s);',(c_room,c_passwd))

censor = []

if os.path.isfile(cens):
	cf = readfile(cens).decode('UTF').replace('\r','').split('# censor ')[default_censor_set].split('\n')
	for c in cf:
		if '#' not in c and len(c): censor.append(c)

if os.path.isfile(custom_cens):
	cf = readfile(cens).decode('UTF').replace('\r','').split('\n')
	for c in cf:
		if '#' not in c and len(c): censor.append(c)

pprint('*'*50,'blue')
pprint('*** Name: %s' % botName,'yellow')
pprint('*** Version: %s' % botVersion,'yellow')
pprint('*** OS: %s ' % botOs,'yellow')
pprint('*'*50,'blue')
pprint('*** (c) 2oo9-%s Disabler Production Lab.' % str(time.localtime()[0]).replace('0','o'),'bright_cyan')

if float(sys.version[:3]) < 2.7: errorHandler('Required Python >= 2.7')

lastnick = Settings['nickname']
jid = xmpp.JID(Settings['jid'])
if getResourse(jid) in ['None','']: jid = xmpp.JID(Settings['jid'].split('/')[0]+'/my owner is stupid and can not complete the configuration')
selfjid = jid
pprint('JID: %s' % str(jid),'light_gray')

message_exclude_update()
photo_hash = GT('photo_hash')
if not photo_hash: photo_hash = ''

try:
	try:
		Server = (':'.join(server.split(':')[:-1]),server.split(':')[-1])
		Port = int(Server[1])
		pprint('Trying to connect to %s' % server,'yellow')
	except: Server,Port = None,5222
	if debug_xmpppy: cl = xmpp.Client(jid.getDomain(),Port,ENABLE_TLS=ENABLE_TLS)
	else: cl = xmpp.Client(jid.getDomain(),Port,debug=[],ENABLE_TLS=ENABLE_TLS)
	try:
		Proxy = proxy
		pprint('Using proxy %s' % Proxy['host'],'green')
	except NameError: Proxy = None
	try:
		Secure = secure
		pprint('Tryins secured connection','cyan')
	except NameError: Secure = None
	con_stat = cl.connect(Server,Proxy,Secure,ENABLE_TLS=ENABLE_TLS)
	if con_stat: pprint('Connected as %s' % con_stat.upper(),'yellow')
	else: raise
except:
	pprint('No connection. Restart in %s sec.' % GT('reboot_time'),'red')
	time.sleep(GT('reboot_time'))
	sys.exit('restart')
auth_type = cl.auth(jid.getNode(), Settings['password'], jid.getResource(),sasl=ENABLE_SASL)
if auth_type: pprint('Authenticated via %s' % auth_type.upper(),'yellow')
else:
	pprint('Authentication error!','red')
	sys.exit('exit')
pprint('Registration Handlers','yellow')
cl.RegisterHandler('message',messageCB)
cl.RegisterHandler('iq',iqCB)
cl.RegisterHandler('presence',presenceCB)
cl.RegisterDisconnectHandler(disconnecter)
cl.UnregisterDisconnectHandler(cl.DisconnectHandler)
if GT('show_loading_by_status'):
	if GT('show_loading_by_status_show') == 'online': caps_and_send(xmpp.Presence(status=GT('show_loading_by_status_message'), priority=Settings['priority']))
	else: caps_and_send(xmpp.Presence(show=GT('show_loading_by_status_show'), status=GT('show_loading_by_status_message'), priority=Settings['priority']))
else:
	if Settings['status'] == 'online': caps_and_send(xmpp.Presence(status=Settings['message'], priority=Settings['priority']))
	else: caps_and_send(xmpp.Presence(show=Settings['status'], status=Settings['message'], priority=Settings['priority']))

pprint('Update self vcard','yellow')
self_vcard()
time.sleep(1)
pprint('Wait conference','yellow')
time.sleep(0.5)
game_over = None
thr(remove_ignore,(),'ddos_remove')
cb = []
is_start = True
lastserver = getServer(confbase[-1][0].lower())
join_percent, join_pers_add = 0, 100.0/len(confbase)

for tocon in confbase:
	if tocon[1]: pprint('->- %s | pass: %s' % tocon,'green')
	else: pprint('->- %s' % tocon[0],'green')
	if GT('show_loading_by_status_percent'):
		join_percent += join_pers_add
		join_status = '%s %s%%' % (GT('show_loading_by_status_message'),int(join_percent))
		if GT('show_loading_by_status'):
			if GT('show_loading_by_status_room'): join_status = '%s [%s]' % (join_status,tocon[0])
			if GT('show_loading_by_status_show') == 'online': caps_and_send(xmpp.Presence(status=join_status, priority=Settings['priority']))
			else: caps_and_send(xmpp.Presence(show=GT('show_loading_by_status_show'), status=join_status, priority=Settings['priority']))
	baseArg = str(tocon[0])
	if '/' not in baseArg: baseArg += '/%s' % str(Settings['nickname'])
	zz = join(baseArg, tocon[1])
	while str(zz)[:3] == '409' and not game_over:
		time.sleep(1)
		baseArg += '_'
		zz = join(baseArg, tocon[1])
	if zz:
		pprint('-!- Error "%s" while join in to %s' % (zz,baseArg),'red')
		if GT('show_loading_by_status'):
			if GT('show_loading_by_status_room'): join_status = L('Error while join in to %s - %s') % (tocon[0],zz)
			if GT('show_loading_by_status_show') == 'online': caps_and_send(xmpp.Presence(status=join_status, priority=Settings['priority']))
			else: caps_and_send(xmpp.Presence(show=GT('show_loading_by_status_show'), status=join_status, priority=Settings['priority']))
	else: pprint('-<- %s' % baseArg,'bright_green')
	if game_over: break
is_start = plugins_reload = None
pprint('Joined','white')

thr(now_schedule,(),'schedule')
thr(iq_async_clean,(),'async_iq_clean')
thr(presence_async_clean,(),'async_presence_clean')
try: thr(bomb_random,(),'bomb_random')
except: pass

if GT('show_loading_by_status'):
	if Settings['status'] == 'online': caps_and_send(xmpp.Presence(status=Settings['message'], priority=Settings['priority']))
	else: caps_and_send(xmpp.Presence(show=Settings['status'], status=Settings['message'], priority=Settings['priority']))

while 1:
	try:
		while not game_over:
			cyc = cl.Process(1)
			if str(cyc) == 'None': cycles_unused += 1
			elif int(str(cyc)): cycles_used += 1
			else: cycles_unused += 1
			if plugins_reload:
				from isida import update as isida_update
				isida_update(get_repo())
				botVersion = get_bot_version()
				bot_softwareinfo['software_version'] = botVersion
				pprint('*** Reload localization','white')
				locales,CURRENT_LOCALE = update_locale()
				pprint('*** Reinit caps hash','white')
				self_vcard()
				id_category,id_type,id_lang,id_name,capsHash = init_hash()
				gtimer,gpresence,gmessage,gactmessage,giq_hook = list(GTIMER_DEF),[],[],[],[]
				pprint('*** Reload Main plugin','white')
				exec(open(pl_folder % 'main.py').read())
				pprint('*** Reload AI plugin','white')
				exec(open(pl_folder % 'ai.py').read())
				pprint('*** Reload other plugins','white')
				pl,pl_ignore,plugins = os.listdir(pl_folder % ''),getFile(pliname,[]),[]
				for tmp in pl:
					if tmp.endswith('.py') and not tmp.startswith('.') and tmp not in ['main.py','ai.py']: plugins.append(tmp)
				plugins.sort()
				for pl in plugins:
					if pl in pl_ignore: pprint('Ignore plugin: %s' % pl,'red')
					else:
						presence_control,message_control,message_act_control,iq_control,timer,execute,iq_hook = [],[],[],[],[],[],[]
						pprint('Append plugin: %s' % pl,'cyan')
						exec(open(pl_folder % pl).read())
						for cm in execute: comms.append((cm[0],cm[1],cm[2],cm[3],'%s\r%s' % (pl[:-3],cm[4]))+cm[5:])
						for tmr in timer: gtimer.append(tmr)
						for tmp in presence_control: gpresence.append(tmp)
						for tmp in message_control: gmessage.append(tmp)
						for tmp in message_act_control: gactmessage.append(tmp)
						for tmp in iq_hook: giq_hook.append(tmp)
						giq_hook.sort()
				pprint('*** Soft update finished! Plugins loaded: %s. Commands: %s' % (len(plugins)+1,len(comms)),'white')
				plugins_reload = None
		atempt_to_shutdown(False)
		sys.exit(bot_exit_type)

	except KeyboardInterrupt: atempt_to_shutdown_with_reason(L('Shutdown by CTRL+C...'),0,'exit',False)

	except xmpp.SystemShutdown: atempt_to_shutdown_with_reason(L('System Shutdown. Trying to restart in %s sec.') % GT('reboot_time'),GT('reboot_time'),'restart',False)

	except psycopg2.InterfaceError: atempt_to_shutdown_with_reason(L('Interface error! Trying to restart in %s sec.') % int(GT('reboot_time')/4),int(GT('reboot_time')/4),'restart',False)

	except Exception as SM:
		try: SM = str(SM)
		except: SM = str(SM)
		pprint('*** Error *** %s ***' % SM,'red')
		logging.exception(' [%s] ' % timeadd(tuple(time.localtime())))
		if 'parsing finished' in SM.lower() or 'database is locked' in SM.lower(): atempt_to_shutdown_with_reason(L('Critical error! Trying to restart in %s sec.') % int(GT('reboot_time')/4),GT('reboot_time')/4,'restart',True)
		if halt_on_exception: raise

# The end is near!
