#!/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 html.entities
import http.client as httplib
import logging
import operator
import os
import math
import random
import re
import base64
import socket
import string
import sys
import time
import urllib
import urllib.request as urllib2
import urllib.error

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 = 'TERM' in os.environ
    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):
    fp = open(filename)
    data = fp.read()
    fp.close()
    return data

def writefile(filename, data):
    if isinstance(data, (bytes, bytearray)):
        data = data.decode('utf-8', 'replace')
    fp = open(filename, 'w', encoding='utf-8')
    fp.write(data)
    fp.close()

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):
    # Check session config first (temporary, not persisted)
    if room in session_config and item in session_config[room]:
        return session_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

# Session-only config storage (not persisted to database, reset on restart)
session_config = {}

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 put_session_config(room,item,value):
    """Set a session-only config value (not persisted to database)"""
    if room not in session_config:
        session_config[room] = {}
    if value in [True,False,None]: value = str(value)
    session_config[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)
        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 conference in current_join and current_join[conference] != id: break
    if conference in current_join:
        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', encoding='utf-8')
    fl.write(paste_he)
    fl.write(paste_body)
    paste_ender = ['','</div></div></body></html>'][_html_paste]
    fl.write(paste_ender)
    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 tjid in ddos_ignore:
        iq_ddos_requests,iq_ddos_limit = GT('ddos_iq_requests'),GT('ddos_iq_limit')
        nick = getResourse(room)
        qry = str(iq.getTag(name='query'))
        if tjid in ddos_iq: 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 jjid in ddos_ignore: 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 = base64.b64decode(re.sub('%[0-9A-F]{2}', unproc, raw_data.split('base64,',1)[1]))
            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 error_code in presence_error: 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")').decode('utf-8')
        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")').decode('utf-8')
        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")').decode('utf-8')
        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")').decode('utf-8')
        except: id_ext,hash_error = get_eval_item(mess,'getTag("c",namespace=xmpp.NS_CAPS).getAttr("ext")'),True
        try:
            id_bmver = mess.getAttr('ver').decode('utf-8')
            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")').decode('utf-8')
        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")').decode('utf-8')
        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")').decode('utf-8')
        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.decode('utf-8'))

        current_hash = base64.b64encode(hashlib.md5(hash_body.encode('utf-8')).digest()).decode('utf-8')
        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 _CL in locales: return _CL
    try:
        loc = users_locale[jid]
        if loc in locales: 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).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 = base64.b64encode(hashlib.sha1(capsHash.encode('utf-8')).digest()).decode('utf-8').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 = base64.b64decode(get_value_from_array2(data,'PHOTO.BINVAL'))
                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): execfile(configname)
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():
        class InterfaceError(Exception):
            pass

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')
execfile(pl_folder % 'main.py')
pprint('*** Loading AI plugin','white')
execfile(pl_folder % 'ai.py')

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')
        execfile(pl_folder % pl)
        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).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).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')
                execfile(pl_folder % 'main.py')
                pprint('*** Reload AI plugin','white')
                execfile(pl_folder % 'ai.py')
                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')
                        execfile(pl_folder % pl)
                        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!
