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

# --------------------------------------------------------------------------- #
#    Isida-NG Jabber Bot (fork of iSida)                                      #
#    Original iSida Copyright (C) 2009-2015 diSabler <dsy@dsy.name>           #
#    Isida-NG Copyright (C) 2026 Luciferus <luciferus@ubunix.pro>             #
#    Project home: https://git.ubunix.pro/Luciferus/isida-ng                  #
# --------------------------------------------------------------------------- #

import re
import os

turn_base = []

# --- ЗАГРУЗКА СЛОВАРЯ ИСКЛЮЧЕНИЙ ---
IGNORE_FILE = data_folder % 'ignore_turn.txt'
_IGNORE_WORDS = set()

# --- ЗАГРУЗКА РУССКИХ БИГРАММ/ТРИГРАММ (КАК PYTHON-КОД) ---
RUGRAMM_FILE = data_folder % 'rugramm.txt'
_RUGRAMM = set()

# --- ПРЕДЛОГИ, АРТИКЛИ, МЕЖДОМЕТИЯ ---
STOP_WORDS = {
    'a', 'an', 'the', 'and', 'or', 'but', 'for', 'nor', 'on', 'at', 'to', 'by', 'in',
    'of', 'off', 'over', 'under', 'up', 'down', 'into', 'through', 'across', 'between',
    'among', 'without', 'within', 'upon', 'toward', 'until', 'since', 'after', 'before',
    'about', 'against', 'along', 'around', 'behind', 'below', 'beneath', 'beside',
    'beyond', 'during', 'except', 'from', 'like', 'near', 'onto', 'opposite', 'outside',
    'per', 'plus', 'round', 'save', 'since', 'than', 'till', 'unto', 'via', 'with',
    'so', 'as', 'than', 'that', 'these', 'those', 'some', 'any', 'no', 'all', 'each',
    'every', 'both', 'neither', 'either', 'then', 'now', 'just', 'only', 'very',
    'too', 'also', 'well', 'oh', 'ah', 'ok', 'okay', 'yes', 'yeah', 'no', 'nope',
    'maybe', 'perhaps', 'probably', 'definitely', 'certainly', 'rain', '*'
}

# --- АНТИ-БИГРАММЫ (жёсткие) ---
ANTI_BIGRAMS = {
    'yfg', 'hbvth', 'pf,sd', 'ftim', 'crbq', 'zpsr', 'кфт', 'вщь', 'цщ', 'квы',
    'gbitim', 'gbit', 'yfghb', 'djn', 'nfr', 'lfkmit', 'ghjcnj', 'sdftim',
    'gthtrk', 'xbnmcz', 'heccrbq', 'zpsr', 'yfghbvth', 'ghb', 'gtht', 'gtrk',
    'xbn', 'cz', 'hecc', 'rbq', 'zps', 'yfg', 'hbv', 'th', 'zpsr', 'pf,sd',
    'zxc', 'qwe', 'asd', 'rty', 'fgh', 'vbn', 'uio', 'jkl', 'xcv', 'bnm',
    'егы', 'шщз', 'щзх', 'зхъ', 'фыв', 'джэ', 'ячс', 'бюё', 'nmcz', 'k.xb',
    'gthtr', 'кфт', 'вщь', 'цщ', 'квы', 'pfrjy', 'xbkcz', 'kbvbn', ',scnh',
    'jltq', 'cndbt', 'pf,', 'sdft', 'im?', 'cvtyb', 'nm&', 'heccr', 'ccrbq',
    'vexf', 'xf.cm', '.cm', 'cj cv', 'tifyy', 'yysv', 'utyf', 'kexituj', ',tp',
    'tot', 'to`', ',kz', 'егктб', 'Utyf-', ',elt', 'j,y', 'fhe;', 'e;bk',
    'ckexf', 'xfqyj?', 'elfkz', 'kznm?'
}

# --- ВСТРОЕННЫЕ СЛОВАРИ (НЕ ЗАВИСЯТ ОТ ВНЕШНИХ ФАЙЛОВ) ---

# Частотные английские слова: если слово есть здесь — это настоящее
# английское слово, и его НЕ надо перекладывать в русскую раскладку.
ENGLISH_WORDS = frozenset("""
the be to of and a in that have i it for not on with he as you do at this but his
by from they we say her she or an will my one all would there their what so up out
if about who get which go me when make can like time no just him know take people
into year your good some could them see other than then now look only come its over
think also back after use two how our work first well way even new want because any
these give day most us was are had has were been being is am
hello world morning evening night today tomorrow yesterday thank thanks please
help sorry answer question test message maybe perhaps probably definitely certainly
hats hat cat dog house home car computer phone internet chat chatroom link email
mail code program programming language python jabber bot user name nick username
password pass login signup sign login account create delete add remove edit change
settings config configuration command commands botowner admin owner moder moderator
help me you him her them us we they you're she's he's it's that's let's don't can't
won't isn't aren't wasn't weren't didn't doesn't could'll should would may might
must need shall ought used ought get go came going goes gone takes took made make
let put keep hold find found look looking see seen show shown tell told say said
speak spoke talk asks asked answer answer ask want wanted like liked love hate
play played playing game keyboard layout letter word words sentence character
number numbers figure figures correct wrong write wrote writing read reading
know known knew remember forget think thinking thought feel feeling think line
work works working job money time hour minute second clock day week month year
season weather rain snow heat cold warm cool sun moon star sky cloud wind
road street city town village country world map street way path route
man woman child children friend friends family mother father sister brother
house home room kitchen bed table chair door window wall floor ground
thing things stuff place places problem problems question questions
whole every almost between near behind beside beyond under above across
through without within about against along around except from into off on to
whether whereas while wherever whichever whoever who whom whose why where when
how then such each own same different other others another whole too two
both few more most many much several enough plenty one two three four five six
seven eight nine ten hundred thousand million billion
apple banana orange lemon berry cherry bread milk water coffee tea juice sugar
salt butter cheese meat fish egg food drink eat drank drunk eats drinking
fast slow quick early late soon far near short long big small little huge tiny
strong weak hard soft easy difficult simple complex important necessary useful
bring bought brought carry cat dog horse bird fish mouse rabbit bear lion wolf
change chance choice choose chose chosen lose lost lose win won beat defeat victory
please plant play point pull push put reach read ready real reason record red
rich ride road roll room round run safe said same save say school score sea
search seat second see seem seen sell send sense serve set seven shall shape
share she shop short should show side sign simple sing sir sister sit six size
skill skin sky sleep slip slow small smile smoke so soft some son song soon
sorry sort sound source south space speak special speed spell spend sport
spring square staff stage stand star start state station stay steel step stick
still stone stop store story straight strange street strong student study
subject such sudden summer sun support sure surface system table take talk
tape teach team tell ten tend term test than thank that their them then there
these they thin thing think third this those though thought thousand three
through throw thus time tip together told tomorrow tonight too took tool top
total touch toward town track trade train travel tree trip trouble true trust
truth try turn twelve twenty twice two type under unit until unto up upon use
usual value various very view voice walk wall want war warm was wash watch water
wave way we wear week well went were west wet what wheel when where which while
white who whole why wide wife wild will win wind window wine wing winter wire
wish with within without woman wonder wood word work world worry would write
wrong wrote yard year yellow yes yet you young your
book read story news article user file folder directory server database table row
field record index tree node graph list store view update insert select delete drop
create alter grant revoke transaction commit rollback cursor execute query result
""".split())

# Русские буквосочетания для проверки русской осмысленности
# (используются, если файл rugramm.txt недоступен или не загрузился)
RU_BIGRAMS_EMBED = frozenset("""
ст но ен то на по ро ов ни ра ко ос ет ер ал ан ом од ел ог лол ат ит ор пр во за
бо ди го до ег ие ия ка ла ль ме мо не ри са се та те ту ус че ши ща ью ья жи ши
ча ща чу щу цы дл со об от зе же жд зн наи пре при суть сть тель чек чик ка бе
ве не пер рес соб ор аб ап ар ас та те сит
""".split())

RU_VOWELS = frozenset('аеёиоуыэюяАЕЁИОУЫЭЮЯ')
EN_VOWELS = frozenset('aeiouyAEIOUY')

# --- ЗАГРУЗКА СЛОВАРЯ ИГНОРА ---
def _load_ignore_words():
    global _IGNORE_WORDS
    ignore_set = set()
    if os.path.exists(IGNORE_FILE):
        try:
            with open(IGNORE_FILE, 'r', encoding='utf-8') as f:
                for line in f:
                    word = line.strip().lower()
                    if word and not word.startswith('#'):
                        ignore_set.add(word)
        except Exception as e:
            pprint('*** ERROR loading ignore_turn.txt: %s' % str(e), 'red')
    _IGNORE_WORDS = ignore_set
    return ignore_set

_load_ignore_words()

def _reload_ignore():
    return _load_ignore_words()

def _save_ignore_word(word, action='add'):
    if not word:
        return False
    lines = []
    if os.path.exists(IGNORE_FILE):
        try:
            with open(IGNORE_FILE, 'r', encoding='utf-8') as f:
                lines = f.readlines()
        except:
            pass
    word_lower = word.lower()
    if action == 'add':
        for line in lines:
            if line.strip().lower() == word_lower:
                return False
        lines.append(word_lower + '\n')
    else:
        lines = [line for line in lines if line.strip().lower() != word_lower]
    try:
        with open(IGNORE_FILE, 'w', encoding='utf-8') as f:
            f.writelines(lines)
        _reload_ignore()
        return True
    except:
        return False

# --- ЗАГРУЗКА РУССКИХ БИГРАММ (КАК PYTHON-КОД) ---
def _load_rugramm():
    global _RUGRAMM
    rugramm_set = set()
    if os.path.exists(RUGRAMM_FILE):
        try:
            with open(RUGRAMM_FILE, 'r', encoding='utf-8') as f:
                content = f.read()
                # Выполняем содержимое файла как Python-код
                # Ожидаем, что это словарь или множество
                data = eval(content)
                if isinstance(data, dict):
                    # Если это словарь, берём ключи
                    rugramm_set = set(data.keys())
                elif isinstance(data, (set, list, tuple)):
                    rugramm_set = set(data)
                else:
                    pprint('*** ERROR: rugramm.txt must contain a dict or set', 'red')
                    rugramm_set = set()
        except Exception as e:
            pprint('*** ERROR loading rugramm.txt: %s' % str(e), 'red')
            pprint('*** Check the syntax in data/rugramm.txt', 'red')
            rugramm_set = set()
    _RUGRAMM = rugramm_set
    return rugramm_set

_load_rugramm()

def _reload_rugramm():
    return _load_rugramm()

# --------------------------------------------------------------------------- #
# ТАБЛИЦЫ РАСКЛАДОК                                                           #
# --------------------------------------------------------------------------- #

EN_TO_RU = {
    'q': 'й', 'w': 'ц', 'e': 'у', 'r': 'к', 't': 'е', 'y': 'н',
    'u': 'г', 'i': 'ш', 'o': 'щ', 'p': 'з', 'a': 'ф', 's': 'ы',
    'd': 'в', 'f': 'а', 'g': 'п', 'h': 'р', 'j': 'о', 'k': 'л',
    'l': 'д', 'z': 'я', 'x': 'ч', 'c': 'с', 'v': 'м', 'b': 'и',
    'n': 'т', 'm': 'ь', '[': 'х', ']': 'ъ', ';': 'ж', "'": 'э',
    ',': 'б', '.': 'ю', '/': '.', '\\': '/', '~': 'Ё', '{': 'Х',
    '+': 'Ё', ':': 'Ж', '"': 'Э', '<': 'Б', '>': 'Ю', '?': ',',
    '&': '?', '@': '"', '#': '№', '$': ';', '^': ':', '`': 'ё'
}

RU_TO_EN = {v: k for k, v in EN_TO_RU.items()}

# --------------------------------------------------------------------------- #
# ПРОВЕРКА НА ОСМЫСЛЕННОСТЬ (БИГРАММЫ + АНТИ)                                 #
# --------------------------------------------------------------------------- #

# Английские биграммы (постоянные)
ENGLISH_COMMON = {
    'th', 'he', 'in', 'en', 'an', 're', 'er', 'on', 'at', 'nd', 'st', 'or', 'nt',
    'ea', 'ti', 'is', 'ou', 'ar', 'es', 'te', 'of', 'it', 'al', 'ri', 'ha', 've',
    'ed', 'se', 'me', 'de', 'le', 'co', 'ma', 'li', 'ca', 'be', 'ne', 'ra', 'ro',
    've', 'el', 'me', 'ta', 'te', 'ic', 'al', 'om', 'ut', 'ur', 'et', 'em', 'ol',
    'tion', 'ing', 'ent', 'ion', 'tio', 'and', 'her', 'for', 'you', 'tha', 'ter',
    'con', 'com', 'dis', 'ex', 'im', 'in', 'non', 'pre', 'pro', 're', 'un',
    'able', 'ible', 'tion', 'sion', 'ment', 'ness', 'ful', 'ous', 'ive', 'al',
    'ic', 'cal', 'ary', 'ory', 'ery', 'ism', 'ist', 'ize', 'ise', 'ing', 'ed',
    'er', 'or', 'ar', 'ly', 'ty', 'y', 'es', 'ies', 'ied', 'ing', 'ly', 'ful'
}

def _is_meaningful_word(word):
    if len(word) < 3:
        return True

    word_lower = word.lower()

    # Проверяем анти-биграммы (жёсткие)
    for anti in ANTI_BIGRAMS:
        if anti in word_lower:
            return False

    # Проверяем русские биграммы (из файла)
    for gram in _RUGRAMM:
        if gram in word_lower:
            return True

    # Проверяем английские биграммы
    bigrams = [word_lower[i:i+2] for i in range(len(word_lower)-1)]
    for bg in bigrams:
        if bg in ENGLISH_COMMON:
            return True

    return False

# --------------------------------------------------------------------------- #
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ                                                     #
# --------------------------------------------------------------------------- #

def _is_command(word):
    if word.startswith(('.', '/', '#', '@')):
        return True
    if re.match(r'(svn|http[s]?|ftp)://|magnet:\?', word, re.S|re.I|re.U):
        return True
    return False

def _is_abbreviation(word):
    if len(word) >= 2 and word.isupper() and word.isalpha():
        return True
    return False

def _should_skip(word):
    if len(word) < 2:
        return True
    if _is_command(word) or _is_abbreviation(word):
        return True
    if word.lower() in _IGNORE_WORDS:
        return True
    if word.lower() in STOP_WORDS:
        return True
    return False

def fix_layout(text, direction='auto'):
    if not text:
        return text
    if direction == 'auto':
        en_count = sum(1 for c in text if c.lower() in EN_TO_RU)
        ru_count = sum(1 for c in text if c in RU_TO_EN)
        direction = 'en2ru' if en_count >= ru_count else 'ru2en'

    result = []
    for char in text:
        if direction == 'en2ru':
            if char.lower() in EN_TO_RU:
                replacement = EN_TO_RU[char.lower()]
                if char.isupper():
                    replacement = replacement.upper()
                result.append(replacement)
            else:
                result.append(char)
        else:
            if char in RU_TO_EN:
                replacement = RU_TO_EN[char]
                if char.isupper():
                    replacement = replacement.upper()
                result.append(replacement)
            else:
                result.append(char)
    return ''.join(result)

# --- ВЫДЕЛЕНИЕ РАСКЛАДКИ: ПОСИМВОЛЬНОЕ/ПОСЛОВНОЕ РЕШЕНИЕ (ДЛЯ АВТОТУРНА) ---

def _en_readable(word):
    """Настоящее английское слово: v словаре, либо с гласными и
    типичными английскими биграммами."""
    w = word.lower()
    if len(w) < 2 or any('\u0400' <= c <= '\u04ff' for c in w):
        return False
    if w in ENGLISH_WORDS:
        return True
    vowels = sum(1 for c in w if c in 'aeiouy')
    if vowels < 1:
        return False
    bigrams = [w[i:i+2] for i in range(len(w) - 1)]
    if not bigrams:
        return False
    hits = sum(1 for b in bigrams if b in ENGLISH_COMMON)
    return float(hits) / len(bigrams) >= 0.5

def _ru_readable(word):
    """Похоже на настоящее русское слово: есть гласные и
    привычные русские буквосочетания (анти-биграммы запрещены)."""
    w = word.lower()
    if len(w) < 2 or any('a' <= c <= 'z' for c in w):
        return False
    for anti in ANTI_BIGRAMS:
        if anti in w:
            return False
    vowels = sum(1 for c in w if c in 'аеёиоуыэюя')
    if vowels < 1:
        return False
    for gram in _RUGRAMM:
        if gram in w:
            return True
    for gram in RU_BIGRAMS_EMBED:
        if gram in w:
            return True
    return len(w) <= 3

def _convert_letters(word, direction):
    """Меняет раскладку ТОЛЬКО для букв, все остальные символы
    (пунктуацию, цифры) оставляет на своих местах."""
    result = []
    for char in word:
        if direction == 'en2ru':
            if 'a' <= char.lower() <= 'z':
                replacement = EN_TO_RU.get(char.lower(), char)
                if char.isupper():
                    replacement = replacement.upper()
                result.append(replacement)
            else:
                result.append(char)
        else:
            if '\u0400' <= char <= '\u04ff':
                replacement = RU_TO_EN.get(char, char)
                if char.isupper():
                    replacement = replacement.upper()
                result.append(replacement)
            else:
                result.append(char)
    return ''.join(result)

def _convert_with_layout_punct(word, direction):
    """Перекладывает слово в заданную раскладку. Буквы перекладываются
    как обычно; ВНУТРЕННИЕ (между буквами) '.', ',' и '\'' — это тоже
    клавиши русской раскладки (ю, б, э), поэтому они тоже перекладываются.
    Ведущая/замыкающая пунктуация (',', '.', '!', '?' и т.п.) сохраняется."""
    if direction == 'en2ru':
        # '.' и ',' между буквами — это 'ю' и 'б'; '\'' между буквами — 'э'
        word = re.sub(r'(?<=[A-Za-z])[.,\']+(?=[A-Za-z])',
                      lambda m: ''.join(EN_TO_RU.get(c, c) for c in m.group(0)),
                      word)
    else:
        word = re.sub(r'(?<=[\u0400-\u04ff])[.,\']+(?=[\u0400-\u04ff])',
                      lambda m: ''.join(RU_TO_EN.get(c, c) for c in m.group(0)),
                      word)
    return _convert_letters(word, direction)

def _fix_layout_word(word):
    """Возвращает исправленное слово (с сохранением пунктуации) или None,
    если слово менять не нужно."""
    if not word or not word.strip():
        return None

    # Пропускаем URL и команды целиком
    if _is_command(word) or re.match(r'^(?:https?|ftp|svn)://|magnet:\?|www\.', word, re.I):
        return None

    # Пропускаем домены и слова с точкой (jabber.ru, xmpp.ru, 1.2.3, file.txt, ...)
    if '.' in word:
        return None


    # Извлекаем ТОЛЬКО буквы и проверяем раскладку по ним
    letters = re.findall(r'[A-Za-z\u0400-\u04ff]', word)
    if len(letters) < 2:
        return None
    core = ''.join(letters)
    if core.isupper():
        return None  # аббревиатура (LOL, USA, ...)
    low_core = core.lower()
    if low_core in _IGNORE_WORDS or low_core in STOP_WORDS:
        return None

    has_latin = any('a' <= c.lower() <= 'z' for c in core)
    has_cyr = any('\u0400' <= c <= '\u04ff' for c in core)
    if has_latin and has_cyr:
        return None  # смешанное слово — оставляем как есть

    if has_latin:
        # латиница: могла быть напечатана на русской раскладке
        ru_core = _convert_letters(core, 'en2ru')
        if _ru_readable(ru_core) and not _en_readable(core):
            return _convert_with_layout_punct(word, 'en2ru')
    elif has_cyr:
        # кириллица: могла быть напечатана на английской раскладке
        en_core = _convert_letters(core, 'ru2en')
        if _en_readable(en_core) and not _ru_readable(core):
            return _convert_with_layout_punct(word, 'ru2en')
    return None

def _turn_layout_words(text):
    """Поместный разбор: перекладывает только слова, явно набранные
    не в той раскладке. Возвращает None, если менять нечего."""
    parts = []
    changed = False
    for piece in re.split(r'(\s+)', text):
        if not piece.strip():
            parts.append(piece)
            continue
        fixed = _fix_layout_word(piece)
        if fixed is not None:
            parts.append(fixed)
            changed = True
        else:
            parts.append(piece)
    return ''.join(parts) if changed else None

# --------------------------------------------------------------------------- #
# ОСНОВНАЯ ЛОГИКА ПЕРЕКЛЮЧЕНИЯ                                                #
# --------------------------------------------------------------------------- #

def turner_raw(text, jid, nick):
    global turn_base
    to_turn = text

    if not text:
        for tmp in turn_base:
            if tmp[0] == jid and tmp[1] == nick:
                turn_base.remove(tmp)
                to_turn = tmp[2]
                break

    if not to_turn:
        return None

    msg = ''
    if to_turn[:3] == '/me':
        msg = '*%s' % nick
        to_turn = to_turn[3:]
    elif ': ' in to_turn and to_turn.split(': ', 1)[0] not in ['http', 'https', 'ftp']:
        parts = to_turn.split(': ', 1)
        msg = '%s:' % parts[0]
        to_turn = parts[1]

    words = re.findall(r'\S+', to_turn)
    result_parts = []

    for word in words:
        if _should_skip(word):
            result_parts.append(word)
            continue

        ru_letters = all(c in RU_TO_EN for c in word)
        en_letters = all(c.lower() in EN_TO_RU for c in word)

        if not ru_letters and not en_letters:
            result_parts.append(word)
            continue

        if ru_letters:
            switched = fix_layout(word, 'ru2en')
        else:
            switched = fix_layout(word, 'en2ru')

        if _is_meaningful_word(switched):
            result_parts.append(switched)
        else:
            result_parts.append(word)

    result = ' '.join(result_parts)

    if msg:
        result = '%s %s' % (msg, result)

    result = result.strip()

    if get_config(getRoom(jid), 'censor'):
        result = to_censore(result, jid)

    return result

# --------------------------------------------------------------------------- #
# КОМАНДЫ НАСТРОЙКИ                                                           #
# --------------------------------------------------------------------------- #

def turn_reload(type, jid, nick, text):
    if get_level(jid, nick)[0] < 9:
        send_msg(type, jid, nick, L('You need bot owner access level.', '%s/%s' % (jid, nick)))
        return
    ignore_count = len(_reload_ignore())
    rugramm_count = len(_reload_rugramm())
    msg = L('Ignore list reloaded (%s words). Russian grams reloaded (%s items).', '%s/%s' % (jid, nick)) % (ignore_count, rugramm_count)
    send_msg(type, jid, nick, msg)

def turner(type, jid, nick, text):
    if not text and type != 'groupchat':
        send_msg(type, jid, nick, L('Not allowed in private!', '%s/%s' % (jid, nick)))
        return
    to_turn = turner_raw(text, jid, nick)
    if to_turn:
        send_msg(type, jid, ['', nick][type == 'groupchat'], to_turn)
    else:
        send_msg(type, jid, nick, L('What?', '%s/%s' % (jid, nick)))

def turn_ignore(type, jid, nick, text):
    if get_level(jid, nick)[0] < 9:
        send_msg(type, jid, nick, L('You need bot owner access level.', '%s/%s' % (jid, nick)))
        return

    parts = text.strip().split(' ', 1)
    if len(parts) != 2:
        send_msg(type, jid, nick, L('Usage: turn_ignore add|del <word>', '%s/%s' % (jid, nick)))
        return

    action, word = parts[0].lower(), parts[1].strip()
    if not word:
        send_msg(type, jid, nick, L('Word cannot be empty.', '%s/%s' % (jid, nick)))
        return

    if action not in ['add', 'del']:
        send_msg(type, jid, nick, L('Usage: turn_ignore add|del <word>', '%s/%s' % (jid, nick)))
        return

    if _save_ignore_word(word, action):
        if action == 'add':
            msg = L('Word "%s" added to ignore list.', '%s/%s' % (jid, nick)) % word
        else:
            msg = L('Word "%s" removed from ignore list.', '%s/%s' % (jid, nick)) % word
    else:
        if action == 'add':
            msg = L('Word "%s" already in ignore list.', '%s/%s' % (jid, nick)) % word
        else:
            msg = L('Word "%s" not found in ignore list.', '%s/%s' % (jid, nick)) % word

    send_msg(type, jid, nick, msg)

def append_to_turner(room, jid, nick, type, text):
    global turn_base
    for tmp in turn_base:
        if tmp[0] == room and tmp[1] == nick:
            try:
                turn_base.remove(tmp)
            except:
                pass
            break
    turn_base.append((room, nick, text))

def remove_from_turner(room, jid, nick, type, text):
    global turn_base
    if type == 'unavailable':
        for tmp in turn_base:
            if tmp[0] == room and tmp[1] == nick:
                try:
                    turn_base.remove(tmp)
                except:
                    pass
                break

def autoturn(room, jid, nick, type, text):
    if not (get_config(room, 'autoturn') and type == 'groupchat'):
        return
    if cur_execute_fetchone('select * from commonoff where room=%s and cmd=%s', (room, 'turn')):
        return

    nowname = get_xnick(room)
    if nick == nowname:
        return

    text = (text or '').strip()
    if not text:
        return

    # 1. Отбрасываем обращения к боту: "Bot, <text>" / "Bot: <text>"
    text = re.sub('^%s[,:]\s' % re.escape(nowname), '', text)
    text = text.strip()
    if not text:
        return

    # 2. Полностью пропускаем /me
    if text.lower().startswith('/me'):
        return

    # 3. Полностью пропускаем команды (.turn, !ping, #topic, ...)
    first = re.split(r'\s+', text, 1)[0]
    if first.startswith(('.', '/', '!', '#', '@')):
        return

    # 4. Полностью пропускаем ссылки
    if re.match(r'^(?:https?|ftp|svn)://|magnet:\?|www\.', text, re.I):
        return

    # 5. Пошаговый разбор (переводим только слова, очевидно набранные не в той раскладке):
    converted = _turn_layout_words(text)
    if converted and converted != text:
        pprint('Autoturn text: %s/%s [%s] %s > %s' % (room, nick, jid, text, converted), 'dark_gray')
        send_msg(type, room, '', converted)
        return True

# --------------------------------------------------------------------------- #
# РЕГИСТРАЦИЯ                                                                #
# --------------------------------------------------------------------------- #

global execute, message_control, presence_control, message_act_control

message_control = [append_to_turner]
presence_control = [remove_from_turner]
message_act_control = [autoturn]

execute = [
    (3, 'turn', turner, 2, 'Turn text from one layout to another.\nturn <text> - switch layout for text\nturn - switch layout for last message'),
    (9, 'turn_reload', turn_reload, 2, 'Reload ignore list from data/ignore_turn.txt and rugramm.txt'),
    (9, 'turn_ignore', turn_ignore, 2, 'Add or remove word from ignore list.\nturn_ignore add|del <word>'),
]