Ejemplo n.º 1
0
def _send_answer_for_voice_input(bot: TeleBot, message: Message, telegram_token: str,
                                 yandex_key: str, botan_key: str) -> None:
    """Finding out answer for voice input.
    First of all - convert voice to text via yandex service,
    then searching correct answer.
    """
    file_info = bot.get_file(message.voice.file_id)
    file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(telegram_token, file_info.file_path))

    try:
        # обращение к нашему новому модулю
        text: str = speech_to_text(file_in_bytes=file.content, key=yandex_key)
        is_answer = _send_text_answer(bot, text, message.chat.id)
        if not botan_track(botan_key, message.chat.id, message, 'Обработка голоса', text=text):
            logging.error('botan problem')
    except SpeechException:
        # Обработка случая, когда распознавание не удалось
        bot.send_message(message.chat.id,
                         'Не удалось распознать Ваш акцент')
        if not botan_track(botan_key, message.chat.id, message, 'Обработка голоса', 'Не распознано', text=""):
            logging.error('botan problem')
    else:
        if not is_answer:
            # Бизнес-логика
            bot.send_message(message.chat.id, 'Вы сказали: "{}"\nЯ не знаю, как на это реагировать :-('.format(text))
            
            if not botan_track(botan_key, message.chat.id, message, 'Обработка голоса', 'Нет ответа',  text=text):
                logging.error('botan problem')
Ejemplo n.º 2
0
def _form_max_size_archive(bot: TeleBot, max_size=40) -> str:
    MAXIMUM_FILE_SIZE = 5 * 1024**2
    max_size *= 1024**2
    cv_users = User.objects.filter(cv_file_id__ne=None)
    cv_users_size = cv_users.count()
    index = 0
    archive_index = 0

    is_continue = True
    while index < cv_users_size:
        current_size = 0
        archive_index += 1
        # make temp directory
        with tempfile.TemporaryDirectory(prefix="ejf_bot_") as temp_dir_path:
            while index < cv_users_size:
                if current_size + MAXIMUM_FILE_SIZE >= max_size:
                    break

                # get current user
                cv_user = cv_users[index]

                # get file from telegram servers
                file_info = bot.get_file(file_id=cv_user.cv_file_id)
                file_name = cv_user.cv_file_name
                file_size = file_info.file_size
                downloaded_file = bot.download_file(file_info.file_path)

                # save file to temp directory
                temp_file_path = os.path.join(temp_dir_path, file_name)
                if os.path.exists(temp_file_path):
                    file_name = f"{file_name.split('.')[0]}{index}.pdf"
                    temp_file_path = os.path.join(temp_dir_path, file_name)
                with open(temp_file_path, "wb") as new_file:
                    new_file.write(downloaded_file)

                # count overall size
                current_size += file_size

                # go to next user
                index += 1

            # make archive
            archive_path = shutil.make_archive(f"CV_database_{archive_index}",
                                               "zip", temp_dir_path)
            yield archive_path
Ejemplo n.º 3
0
class ColorCodeBot:
    def __init__(self,
                 api_key: str,
                 lang: Mapping[str, str],
                 theme_image_ids: tuple[str],
                 keyboards: Mapping[str, InlineKeyboardMarkup],
                 guesslang_syntaxes: Mapping[str, str],
                 *args: Any,
                 admin_chat_id: Optional[str] = None,
                 db_path: str = str(
                     local.path(__file__).up() / 'user_themes.sqlite'),
                 **kwargs: Any):
        self.lang = lang
        self.theme_image_ids = theme_image_ids
        self.kb = keyboards
        self.guesslang_syntaxes = guesslang_syntaxes
        self.admin_chat_id = admin_chat_id
        self.db_path = db_path
        self.user_themes = KeyValue(key_field=IntegerField(primary_key=True),
                                    value_field=CharField(),
                                    database=APSWDatabase(db_path))
        self.log = mk_logger()
        self.bot = TeleBot(api_key, *args, **kwargs)
        self.register_handlers()
        self.guesser = Guess()

    def register_handlers(self):
        self.welcome = self.bot.message_handler(commands=['start', 'help'])(
            self.welcome)
        self.browse_themes = self.bot.message_handler(
            commands=['theme', 'themes'])(self.browse_themes)
        self.mk_theme_previews = self.bot.message_handler(
            commands=['previews'])(self.mk_theme_previews)
        self.intake_snippet = self.bot.message_handler(
            func=lambda m: m.content_type == 'text')(self.intake_snippet)
        self.recv_photo = self.bot.message_handler(content_types=['photo'])(
            self.recv_photo)
        self.restore_kb = self.bot.callback_query_handler(
            lambda q: yload(q.data)['action'] == 'restore')(self.restore_kb)
        self.set_snippet_filetype = self.bot.callback_query_handler(
            lambda q: yload(q.data)['action'] == 'set ext')(
                self.set_snippet_filetype)
        self.set_theme = self.bot.callback_query_handler(
            lambda q: yload(q.data)['action'] == 'set theme')(self.set_theme)
        self.send_photo_elsewhere = self.bot.inline_handler(
            lambda q: q.query.startswith("img "))(self.send_photo_elsewhere)
        self.switch_from_inline = self.bot.inline_handler(lambda q: True)(
            self.switch_from_inline)

    @retry
    def switch_from_inline(self, inline_query: InlineQuery):
        self.log.msg("receiving inline query",
                     user_id=inline_query.from_user.id,
                     user_first_name=inline_query.from_user.first_name,
                     query=inline_query.query)
        self.bot.answer_inline_query(
            inline_query.id, [],
            switch_pm_text=self.lang['switch to direct'],
            switch_pm_parameter='x')

    @retry
    def welcome(self, message: Message):
        self.log.msg("introducing myself",
                     user_id=message.from_user.id,
                     user_first_name=message.from_user.first_name,
                     chat_id=message.chat.id)
        self.bot.reply_to(
            message,
            self.lang['welcome'],
            reply_markup=ForceReply(
                input_field_placeholder=self.lang['input field placeholder']))

    @retry
    def mk_theme_previews(self, message: Message):
        if not self.admin_chat_id or str(
                message.chat.id) != self.admin_chat_id:
            self.log.msg("naughty preview attempt",
                         user_id=message.from_user.id,
                         user_first_name=message.from_user.first_name,
                         chat_id=message.chat.id,
                         admin_chat_id=self.admin_chat_id)
            return
        sample_code = dedent("""
            # palinDay :: Int -> [ISO Date]
            def palinDay(y):
                '''A possibly empty list containing the palindromic
                   date for the given year, if such a date exists.
                '''
                s = str(y)
                r = s[::-1]
                iso = '-'.join([s, r[0:2], r[2:]])
                try:
                    datetime.strptime(iso, '%Y-%m-%d')
                    return [iso]
                except ValueError:
                    return []
        """)
        for button in chain.from_iterable(self.kb['theme'].keyboard):
            theme = button.text
            html = mk_html(f"# {theme}{sample_code}", 'py', theme)
            with local.tempdir() as folder:
                png_path = mk_png(html, folder=folder)
                send_image(bot=self.bot,
                           chat_id=message.chat.id,
                           png_path=png_path,
                           reply_msg_id=message.message_id)

    @retry
    def browse_themes(self, message: Message):
        self.log.msg("browsing themes",
                     user_id=message.from_user.id,
                     user_first_name=message.from_user.first_name,
                     chat_id=message.chat.id)
        albums = [
            self.theme_image_ids[i:i + 10]
            for i in range(0, len(self.theme_image_ids), 10)
        ]
        for album in albums:
            self.bot.send_media_group(message.chat.id,
                                      map(InputMediaPhoto, album),
                                      reply_to_message_id=message.message_id)
        self.bot.reply_to(message,
                          self.lang['select theme'],
                          reply_markup=self.kb['theme'])

    @retry
    def set_theme(self, cb_query: CallbackQuery):
        data = yload(cb_query.data)
        user = cb_query.message.reply_to_message.from_user
        self.log.msg("setting theme",
                     user_id=user.id,
                     user_first_name=user.first_name,
                     theme=data['theme'],
                     chat_id=cb_query.message.chat.id)
        self.bot.edit_message_reply_markup(cb_query.message.chat.id,
                                           cb_query.message.message_id,
                                           reply_markup=minikb('theme'))
        self.user_themes[user.id] = data['theme']
        self.bot.answer_callback_query(
            cb_query.id,
            text=self.lang['acknowledge theme'].format(data['theme']))
        if self.admin_chat_id:
            with open(self.db_path, 'rb') as doc:
                self.bot.send_document(self.admin_chat_id, doc)

    def guess_ext(self,
                  code: str,
                  probability_min: float = .12) -> Optional[str]:
        syntax, probability = self.guesser.probabilities(code)[0]
        ext = self.guesslang_syntaxes.get(syntax)
        self.log.msg("guessed syntax",
                     probability_min=probability_min,
                     probability=probability,
                     syntax=syntax,
                     ext=ext)
        if probability >= probability_min:
            return ext
        for start, ext in {
                '{': 'json',
                '---\n': 'yaml',
                '[[': 'toml',
                '[': 'ini',
                '<?php': 'php',
                '<': 'xml',
                '-- ': 'lua'
        }.items():
            if code.startswith(start):
                return ext

    @retry
    def intake_snippet(self, message: Message):
        self.log.msg("receiving code",
                     user_id=message.from_user.id,
                     user_first_name=message.from_user.first_name,
                     chat_id=message.chat.id)
        ext = self.guess_ext(message.text)
        if ext:
            kb_msg = self.bot.reply_to(
                message,
                f"{self.lang['query ext']}\n\n{self.lang['guessed syntax'].format(ext)}",
                reply_markup=minikb('syntax', self.lang['syntax picker']),
                parse_mode='Markdown',
                disable_web_page_preview=True)
            self.set_snippet_filetype(cb_query=None,
                                      query_message=kb_msg,
                                      ext=ext)
        else:
            self.bot.reply_to(message,
                              self.lang['query ext'],
                              reply_markup=self.kb['syntax'],
                              parse_mode='Markdown',
                              disable_web_page_preview=True)

    @retry
    def send_photo_elsewhere(self, inline_query: InlineQuery):
        file_id = inline_query.query.split('img ', 1)[-1]
        self.log.msg("creating inline query result",
                     file_id=file_id,
                     file_info=self.bot.get_file(file_id))
        self.bot.answer_inline_query(inline_query.id, [
            InlineQueryResultCachedPhoto(
                id=str(uuid4()), photo_file_id=file_id, title="Send Image")
        ],
                                     is_personal=True)

    @retry
    def restore_kb(self, cb_query: CallbackQuery):
        data = yload(cb_query.data)
        self.bot.edit_message_reply_markup(
            cb_query.message.chat.id,
            cb_query.message.message_id,
            reply_markup=self.kb[data['kb_name']])
        self.bot.answer_callback_query(cb_query.id)

    @retry
    def set_snippet_filetype(self,
                             cb_query: Optional[CallbackQuery] = None,
                             query_message: Optional[Message] = None,
                             ext: Optional[str] = None):
        if cb_query:
            query_message = cb_query.message
            ext = yload(cb_query.data)['ext']
        elif not (query_message and ext):
            raise Exception(
                "Either cb_query or both query_message and ext are required")
        self.log.msg("colorizing code",
                     user_id=query_message.reply_to_message.from_user.id,
                     user_first_name=query_message.reply_to_message.from_user.
                     first_name,
                     syntax=ext,
                     chat_id=query_message.chat.id)
        if cb_query:
            self.bot.edit_message_reply_markup(query_message.chat.id,
                                               query_message.message_id,
                                               reply_markup=minikb(
                                                   'syntax',
                                                   self.lang['syntax picker']))
        snippet = query_message.reply_to_message
        theme = self.user_themes.get(snippet.from_user.id,
                                     'base16/gruvbox-dark-hard')

        html = mk_html(snippet.text, ext, theme)
        send_html(bot=self.bot,
                  chat_id=snippet.chat.id,
                  html=html,
                  reply_msg_id=snippet.message_id)

        with local.tempdir() as folder:
            png_path = mk_png(html, folder=folder)
            did_send = False
            if len(snippet.text.splitlines()) <= 30:
                try:
                    photo_msg = send_image(bot=self.bot,
                                           chat_id=snippet.chat.id,
                                           png_path=png_path,
                                           reply_msg_id=snippet.message_id)
                except ApiException as e:
                    self.log.error("failed to send compressed image",
                                   exc_info=e,
                                   chat_id=snippet.chat.id)
                else:
                    did_send = True
                    kb_to_chat = InlineKeyboardMarkup()
                    kb_to_chat.add(
                        InlineKeyboardButton(
                            self.lang['send to chat'],
                            switch_inline_query=
                            f"img {photo_msg.photo[-1].file_id}"))
                    self.bot.edit_message_reply_markup(photo_msg.chat.id,
                                                       photo_msg.message_id,
                                                       reply_markup=kb_to_chat)
            if not did_send:
                send_image(bot=self.bot,
                           chat_id=snippet.chat.id,
                           png_path=png_path,
                           reply_msg_id=snippet.message_id,
                           compress=False)

        if cb_query:
            self.bot.answer_callback_query(cb_query.id)

    def recv_photo(self, message: Message):
        self.log.msg('received photo',
                     file_id=message.photo[0].file_id,
                     user_id=message.from_user.id,
                     user_first_name=message.from_user.first_name,
                     chat_id=message.chat.id)
Ejemplo n.º 4
0
class TelegramBot:
    def __init__(self):
        try:
            self._user_bot = TeleBot(Settings.UserBot.token)
            self._admin_bot = TeleBot(Settings.AdminBot.token)
            self._log = Logger(self._admin_bot, self._user_bot,
                               Settings.logger_name, Settings.AdminBot.id,
                               Settings.path_to_logs)
            self._statuses = Status(self._log)
            self._processing = Processing(self._log)
            self._networks = Networks(self._log,
                                      Settings.Networks.path_to_models)
            self._deepdream = DeepDream(self._log, self._user_bot,
                                        self._admin_bot, Settings.Photo.path,
                                        self._send_mail)
            self._colorize = Colorize(self._log, self._user_bot,
                                      self._admin_bot, Settings.Photo.path,
                                      self._send_mail)
            self._init_handlers()
            self._log.admin_info("Bot is running!!")
            self._log.info("Bot is running!!")
            self.start()
        except SystemExit:
            pass
        except BaseException as e:
            self._processing.stop_all()
            self._log.fatal("Bot is not running!!", e=e.args)

    def start(self):
        self._user_bot.polling(none_stop=True, interval=1)

    # local functions
    def _random_string(self, n):
        a = string.ascii_letters + string.digits
        return ''.join([random.choice(a) for i in range(n)])

    # web functions
    def _send_mail(self, mess, first_photo, second_photo, msg):
        try:
            file_path_one = Settings.Photo.path + first_photo
            file_path_two = Settings.Photo.path + second_photo
            msg_root = MIMEMultipart('related')
            msg_root[
                'Subject'] = mess.from_user.first_name + " " + mess.from_user.last_name
            msg_root['From'] = Settings.Mail.username
            msg_root['To'] = Settings.Mail.username
            msg_root.preamble = 'This is a multi-part message in MIME format.'

            msg_alternative = MIMEMultipart('alternative')
            msg_root.attach(msg_alternative)

            msg_text = MIMEText(
                '<i>' + msg +
                '</i><br><img src="cid:image1"><img src="cid:image2"><br>',
                'html')
            msg_alternative.attach(msg_text)

            fp = open(file_path_one, 'rb')
            msg_image_one = MIMEImage(fp.read())
            fp.close()
            msg_image_one.add_header('Content-ID', '<image1>')
            msg_root.attach(msg_image_one)

            fp = open(file_path_two, 'rb')
            msg_image_two = MIMEImage(fp.read())
            fp.close()
            msg_image_two.add_header('Content-ID', '<image2>')
            msg_root.attach(msg_image_two)

            smtp = smtplib.SMTP('smtp.mail.ru:587')
            smtp.ehlo()
            smtp.starttls()
            smtp.login(Settings.Mail.username, Settings.Mail.password)
            smtp.sendmail(Settings.Mail.username, Settings.Mail.username,
                          msg_root.as_string())
            smtp.quit()
        except BaseException as e:
            self._log.warning("Mail sending error [" + mess.from_user.id + "]")

    def _download_photo(self, message):
        try:
            photo_name = self._random_string(8)
            photo_id = message.photo[-1].file_id
            photo_image = self._user_bot.download_file(
                self._user_bot.get_file(photo_id).file_path)
            with open(Settings.Photo.path + photo_name + ".jpg",
                      'wb') as imgfile:
                imgfile.write(photo_image)
            return photo_name
        except BaseException as e:
            self._log.error(
                "Can`t download photo [" + message.from_user.id + "]",
                message.from_user.id, e.args)

    # handlers
    def _text_mess(self, message):
        self._user_bot.send_message(message.from_user.id, message.text)

    def _photo_mess(self, message):
        try:
            if self._statuses[message.from_user.id].get_status() == 0:
                self._user_bot.send_message(message.from_user.id,
                                            "I don't know what to do 😕")
            else:
                photo_name = self._download_photo(message)
                if self._statuses[message.from_user.id].get_status() == 32:
                    self._colorize_function(message, photo_name)
                elif self._statuses[message.from_user.id].get_status() == 23:
                    self._deepdream_function(message, photo_name)
        except BaseException as e:
            self._log.error("Something went wrong with photo",
                            message.from_user.id, e.args)

    # handlers commands
    def _deepdream_command(self, message):
        try:
            keyboard = types.InlineKeyboardMarkup()
            for net in self._networks.get_parameterized_keys():
                callback_button = types.InlineKeyboardButton(
                    text=net, callback_data="deepdream_net_" + net)
                keyboard.add(callback_button)
            self._user_bot.send_message(message.chat.id,
                                        "Please, choose a neural network",
                                        reply_markup=keyboard)
            self._statuses[message.chat.id].set_status(21)
        except BaseException as e:
            self._log.error("DeepDream command error ", message.from_user.id,
                            e.args)

    def _colorize_command(self, message):
        try:
            keyboard = types.InlineKeyboardMarkup()
            for net in self._networks.get_standart_keys():
                callback_button = types.InlineKeyboardButton(
                    text=net, callback_data="colorize_net_" + net)
                keyboard.add(callback_button)
            self._user_bot.send_message(message.chat.id,
                                        "Please, choose a neural network",
                                        reply_markup=keyboard)
            self._statuses[message.chat.id].set_status(31)
        except BaseException as e:
            self._log.error("Colorize command error ", message.from_user.id,
                            e.args)

    def _stop_command(self):
        self._processing.stop_all()
        self._log.warning("Bot is stopped!")
        self._log.admin_info("Bot is stopped!")
        #self._user_bot.stop_polling()

    def _stop_process_command(self, i):
        self._processing.stop_process(i)

    def _stop_all_command(self):
        self._processing.stop_all()

    # callbacks
    def _deepdream_callback(self, call):
        try:
            if call.data[:14] == "deepdream_net_":
                if self._statuses[call.message.chat.id].get_status() == 21:
                    self._statuses[call.message.chat.id].set_net(
                        call.data[14:])
                    self._user_bot.edit_message_text(
                        chat_id=call.message.chat.id,
                        message_id=call.message.message_id,
                        text=call.data[14:])
                    layers = self._networks.get_parameterized_net(
                        self._statuses[
                            call.message.chat.id].get_net()).get_layers()

                    keyboard = types.InlineKeyboardMarkup()
                    for layer in layers:
                        self._log.debug(layer)
                        callback_button = types.InlineKeyboardButton(
                            text=layer,
                            callback_data="deepdream_layer_" + layer)
                        keyboard.add(callback_button)
                    self._user_bot.send_message(call.message.chat.id,
                                                "Please, choose layer level",
                                                reply_markup=keyboard)
                    self._statuses[call.message.chat.id].set_status(22)
            elif call.data[:16] == "deepdream_layer_":
                if self._statuses[call.message.chat.id].get_status() == 22:
                    self._statuses[call.message.chat.id].set_layer(
                        str(call.data[16:]).replace(".", "/"))
                    self._user_bot.edit_message_text(
                        chat_id=call.message.chat.id,
                        message_id=call.message.message_id,
                        text=call.data[16:])
                    self._user_bot.send_message(call.message.chat.id,
                                                "Send your photo, please")
                    self._statuses[call.message.chat.id].set_status(23)
        except BaseException as e:
            self._log.error("Something went wrong with callback",
                            call.message.from_user.id, e.args)

    def _colorize_callback(self, call):
        try:
            if call.data[:13] == "colorize_net_":
                if self._statuses[call.message.chat.id].get_status() == 31:
                    self._statuses[call.message.chat.id].set_net(
                        call.data[13:])
                    self._user_bot.edit_message_text(
                        chat_id=call.message.chat.id,
                        message_id=call.message.message_id,
                        text=call.data[13:])
                    self._user_bot.send_message(call.message.chat.id,
                                                "Send your photo, please")
                    self._statuses[call.message.chat.id].set_status(32)
        except BaseException as e:
            self._log.error("Something went wrong with callback",
                            call.message.from_user.id, e.args)

    # functional
    def _deepdream_function(self, message, photo_name):
        try:
            args = (message, photo_name + ".jpg",
                    self._networks.get_parameterized_net(self._statuses[
                        message.from_user.id].get_net()).get_model(),
                    self._statuses[message.from_user.id].get_layer())
            self._processing.new_process(self._deepdream.start_deep_dream,
                                         args, "DeepDream",
                                         message.from_user.id)
            self._user_bot.send_message(message.from_user.id,
                                        "I'm working on it")
            self._statuses[message.from_user.id].set_status(0)
        except BaseException as e:
            self._log.error("Something went wrong with deepdream starting",
                            message.from_user.id, e.args)

    def _colorize_function(self, message, photo_name):
        try:
            args = (message, photo_name + ".jpg",
                    self._networks.get_standart_net(self._statuses[
                        message.from_user.id].get_net()).get_model())
            self._processing.new_process(self._colorize.start_colorize, args,
                                         "Colorize", message.from_user.id)
            self._user_bot.send_message(message.from_user.id,
                                        "I'm working on it")
            self._statuses[message.from_user.id].set_status(0)
        except BaseException as e:
            self._log.error("Something went wrong with colorize starting",
                            message.from_user.id, e.args)

    def _weather_function(self, message):
        pass

    # init handlers
    def _init_handlers(self):
        # admin
        @self._user_bot.message_handler(commands=['stopbot'])
        def handle_command(message):
            if message.from_user.id == Settings.AdminBot.id:
                self._stop_command()

        @self._user_bot.message_handler(commands=['allprocessesstop'])
        def handle_command(message):
            if message.from_user.id == Settings.AdminBot.id:
                self._stop_all_command()

        @self._user_bot.message_handler(commands=['processstop'])
        def handle_command(message):
            if message.from_user.id == Settings.AdminBot.id:
                self._stop_process_command(int(message.text.split()[1]))

        # user
        @self._user_bot.message_handler(commands=['weather'])
        def handle_command(message):
            self._weather_function(message)

        @self._user_bot.message_handler(commands=['colorize'])
        def handle_command(message):
            self._colorize_command(message)

        @self._user_bot.message_handler(commands=['deepdream'])
        def handle_command(message):
            self._deepdream_command(message)

        @self._user_bot.message_handler(content_types=['text'])
        def handle_text(message):
            self._text_mess(message)

        @self._user_bot.message_handler(content_types=['photo'])
        def handle_photo(message):
            self._photo_mess(message)

        @self._user_bot.callback_query_handler(func=lambda call: True)
        def callback_inline(call):
            if call.data[:9] == "deepdream":
                self._deepdream_callback(call)
            elif call.data[:8] == "colorize":
                self._colorize_callback(call)