コード例 #1
0
    def __init__(self):
        self.init_settings()
        self.vk_init()
        self.plugin_init()

        if self.DO_CHAT:
            if self.USE_CHATTER:
                self.chatter = ChatterBot()
            else:
                from chat.chat import chatter
                self.chatter = chatter
コード例 #2
0
ファイル: vbot.py プロジェクト: Mirabbas/VK_hack
    def __init__(self):
        self.WHITELISTED = False

        self.longpoll_values = {}
        self.longpoll_server = ""
        self.longpoll_key = ""
        self.last_ts = 0

        self.vk_init()

        if sys.argv and "-nu" in sys.argv:
            self.plugin_download(f"https://vkbots.github.io/vbot-plugins")
            self.plugin_clear()

            if sys.argv and "-ou" in sys.argv:
                hues.success("Плагины обновлены")
                exit()

        self.plugin_init()

        if settings.CHAT_ENABLE:
            if settings.CHAT_CHATTER:
                self.chatter = ChatterBot()
            else:
                from chat.chat import chatter
                self.chatter = chatter
コード例 #3
0
class Bot(object):
    """Главный класс бота"""
    __slots__ = [
        "PREFIXES", "LOG_MESSAGES", "LOG_COMMANDS", "WHITELISTED",
        "FLOOD_INTERVAL", "USERS", "PROXIES", "SCOPE", "APP_ID",
        "DATABASE_CHARSET", "ONLY_CHAT", "USE_CHATTER", "DO_CHAT",
        "IGNORE_PREFIX", "BLACKLIST_MESSAGE", "WHITELIST_MESSAGE",
        "messages_date", "plugin_system", "cmd_system", "last_ts",
        "scheduled_funcs", "longpoll_server", "longpoll_key", "chatter",
        "longpoll_values", "event_loop", "last_message_id", "vk"
    ]

    def __init__(self):
        self.init_settings()
        self.vk_init()
        self.plugin_init()

        if self.DO_CHAT:
            if self.USE_CHATTER:
                self.chatter = ChatterBot()
            else:
                from chat.chat import chatter
                self.chatter = chatter

    def init_settings(self):
        """Функция инициализации файла настроек и его создания"""
        # Если у нас есть только settings.py.sample
        if isfile('settings.py.sample') and not isfile('settings.py'):
            try:
                shutil.copy('settings.py.sample', 'settings.py')
            except Exception:
                fatal('Я не могу копировать файлы в текущей папке, '
                      'проверьте ваши права на неё!')
            hues.info('Был создан файл settings.py, '
                      'не забудьте добавить данные для авторизации!')
            exit()
        # Если у нас уже есть settings.py
        elif isfile('settings.py'):
            import settings
            try:
                self.WHITELISTED = False
                self.WHITELIST_MESSAGE = settings.WHITELIST_MESSAGE
                self.BLACKLIST_MESSAGE = settings.BLACKLIST_MESSAGE

                self.PREFIXES = settings.PREFIXES

                self.LOG_MESSAGES = settings.LOG_MESSAGES
                self.LOG_COMMANDS = settings.LOG_COMMANDS

                self.APP_ID = settings.APP_ID
                self.SCOPE = settings.SCOPE

                self.FLOOD_INTERVAL = settings.FLOOD_INTERVAL

                self.USERS = settings.USERS
                self.PROXIES = settings.PROXIES

                self.DO_CHAT = settings.DO_CHAT
                self.ONLY_CHAT = settings.ONLY_CHAT
                self.USE_CHATTER = settings.USE_CHATTER
                self.IGNORE_PREFIX = settings.IGNORE_PREFIX

                settings.ADMINS
                settings.BLACKLIST
                settings.WHITELIST

                if not self.USERS:
                    fatal(
                        "Проверьте, что у есть LOGIN и PASSWORD, или же TOKEN в файле settings.py!"
                        "Без них бот работать НЕ СМОЖЕТ.")

            except (ValueError, AttributeError, NameError):
                fatal(
                    'Проверьте содержимое файла settings.py, возможно вы удалили что-то нужное!'
                )
        # Если не нашли ни settings.py, ни settings.py.sample
        else:
            fatal(
                "settings.py и settings.py.sample не найдены, возможно вы их удалили?"
            )

    def vk_init(self):
        hues.warn("Авторизация в ВКонтакте...")
        # Словарь вида ID -> время
        self.messages_date = {}
        self.vk = VkPlus(users_data=self.USERS,
                         proxies=self.PROXIES,
                         bot=self,
                         scope=self.SCOPE,
                         app_id=self.APP_ID)
        if self.vk:
            hues.success("Успешная авторизация")

    def plugin_init(self):
        hues.info("Загрузка плагинов...")

        # Подгружаем плагины
        self.plugin_system = PluginSystem(self.vk, folder=abspath('plugins'))
        self.plugin_system.register_commands()
        # Чтобы плагины могли получить список всех плагинов (костыль)
        self.vk.get_plugins = self.plugin_system.get_plugins

        # Для парсинга команд с пробелом используется
        # обратная сортировка, для того, чтобы самые
        # длинные команды были первыми в списке
        command_names = list(self.plugin_system.commands.keys())
        command_names.sort(key=len, reverse=True)

        from command import CommandSystem
        self.cmd_system = CommandSystem(command_names, self.plugin_system)
        self.scheduled_funcs = self.plugin_system.scheduled_events
        hues.success("Загрузка плагинов завершена")

    async def init_long_polling(self, update=0):
        """Функция для инициализации Long Polling"""
        retries = 5
        for x in range(retries):
            result = await self.vk.method('messages.getLongPollServer',
                                          {'use_ssl': 1})
            if result:
                break

        if not result:
            fatal("Не удалось получить значения Long Poll сервера!")

        try:
            self.last_ts = self.longpoll_values['ts']
            self.longpoll_key = self.longpoll_values['key']
        except AttributeError:
            pass
        except ValueError:
            pass

        if update == 0:
            # Если нам нужно инициализировать с нуля, меняем сервер
            self.longpoll_server = "https://" + result['server']
        if update in (0, 3):
            # Если нам нужно инициализировать с нуля, или ошибка 3
            self.longpoll_key = result['key']
            self.last_ts = result['ts']  # Последний timestamp
        elif update == 2:
            # Если ошибка 2 - то нужен новый ключ
            self.longpoll_key = result['key']

        self.longpoll_values = {
            'act': 'a_check',
            'key': self.longpoll_key,
            'ts': self.last_ts,
            'wait': 25,  # Тайм-аут запроса
            'mode': 10,
            'version': 1
        }

    async def check_event(self, new_event):
        if not new_event:
            return  # На всякий случай

        event_id = new_event.pop(0)

        if event_id != 4:
            return  # Если событие - не новое сообщение

        msg_id, flags, peer_id, ts, subject, text, attaches = new_event

        # Получаем параметры сообщения
        # https://vk.com/dev/using_longpoll_2
        flags = parse_msg_flags(flags)
        # Если сообщение - исходящее
        if flags['outbox']:
            return

        # Если ID находится в чёрном списке
        if await get_or_none(Role, user_id=peer_id, role="blacklisted"):
            if self.BLACKLIST_MESSAGE:
                await self.vk.method("messages.send", {
                    "user_id": peer_id,
                    "message": self.BLACKLIST_MESSAGE
                })

            return

        if self.WHITELISTED and not await get_or_none(
                Role, user_id=peer_id, role="whitelisted"):
            if self.WHITELIST_MESSAGE:
                await self.vk.method("messages.send", {
                    "user_id": peer_id,
                    "message": self.WHITELIST_MESSAGE
                })

            return

        # Тип сообщения - конференция или ЛС?
        try:
            # Пробуем получить ID пользователя, который отправил
            # сообщение в беседе
            user_id = attaches['from']
            peer_id -= 2000000000
            conf = True
        except KeyError:
            # Если ключа from нет - это ЛС
            user_id = peer_id
            conf = False
        user_id = int(user_id)

        cleaned_body = text.replace('<br>', '\n')

        data = MessageEventData(conf, peer_id, user_id, cleaned_body, ts,
                                msg_id, attaches)

        user = await get_or_none(User, uid=user_id)
        if user:
            if ts - user.message_date <= self.FLOOD_INTERVAL:
                user.message_date = ts
                await db.update(user)
                return

            user.message_date = ts

            await db.update(user)
        else:
            user = await db.create(User, uid=user_id)

        await self.check_if_command(data, user)

    async def do_chat(self, msg, user):
        if not self.DO_CHAT:
            return

        if user.chat_data:
            chat_data = json.loads(user.chat_data)
            chat_data.append(normalize(msg.text))
            chat_data = chat_data[::-1]
        else:
            chat_data = [normalize(msg.text)]

        answer = self.chatter.parse_message(chat_data)

        if answer is not None:
            chat_data = chat_data[::-1]
            chat_data.append(normalize(answer))

            user.chat_data = json.dumps(chat_data)

            await db.update(user)

            await msg.answer(answer)

    async def check_if_command(self, data: MessageEventData, user) -> None:
        if self.LOG_MESSAGES:
            who = f"{'конференции' if data.conf else 'ЛС'} {data.peer_id}"
            hues.info(f"Сообщение из {who} > {data.body}")

        msg_obj = Message(self.vk, data, user)

        cmd = Command(msg_obj)

        if not cmd.has_prefix:
            if self.DO_CHAT and self.IGNORE_PREFIX:
                await self.do_chat(msg_obj, user)

            return

        if self.ONLY_CHAT and self.DO_CHAT:
            await self.do_chat(msg_obj, user)

        else:
            result = await self.cmd_system.process_command(msg_obj, cmd)

            if result is False:
                await self.do_chat(msg_obj, user)

    async def run(self, event_loop):
        """Главная функция бота - тут происходит ожидание новых событий (сообщений)"""
        self.event_loop = event_loop  # Нужен для шедулинга функций

        await self.init_long_polling()
        session = aiohttp.ClientSession(loop=event_loop)
        while True:
            try:
                resp = await session.get(self.longpoll_server,
                                         params=self.longpoll_values)
            except (aiohttp.ClientOSError, asyncio.TimeoutError):
                # У меня были такие ошибки на Manjaro 16.10.3 Fringilla
                # ВК почему-то присылал сервер, к которому нельзя подключиться
                hues.warn(
                    'Сервер Long Polling не отвечает, подключаюсь к другому...'
                )
                await self.init_long_polling()
                continue
            """
            Тут не используется resp.json() по простой причине:
            aiohttp будет писать warning'и из-за плохого mimetype
            Неизвестно, почему он у ВК такой - text/javascript; charset=utf-8
            """
            events_text = await resp.text()
            try:
                events = json.loads(events_text)
            except ValueError:
                # В JSON ошибка, или это вовсе не JSON
                continue

            # Проверяем на код ошибки
            failed = events.get('failed')
            if failed:
                err_num = int(failed)
                # Код 1 - Нам нужно обновить timestamp
                if err_num == 1:
                    self.longpoll_values['ts'] = events['ts']
                # Коды 2 и 3 - нужно запросить данные нового
                # Long Polling сервера
                elif err_num in (2, 3):
                    await self.init_long_polling(err_num)
                continue

            # Обновляем время, чтобы не приходили старые события
            self.longpoll_values['ts'] = events['ts']
            for event in events['updates']:
                schedule_coroutine(self.check_event(event))