Бот — сводник для фотографов и ретушеров

в 2:34, , рубрики: python, sqlite, метки: , , ,

Я занимаюсь фотографией, и часто у меня возникает потребность в обработке фотоснимка. Сейчас в моде нейронные сети, свертки развертки и даже изменение цветовой палитры снимка и его настроения. Но хотелось ламповой, человеческой обработки. Поэтому я решил создать систему, которая позволит найти ретушеров и обработать снимок с их помощью.

Для ретушеров эта система позволяет потренироваться в обработке снимков определенного жанра, набить руку. В планах — развить данную систему через введение монетизации, таким образом стимулируя приток новых ретушеров.

Сегодня я хочу показать как это было реализовано в виде бота.

Для базы я выбрал вариант развертки приложения в виде бота для телеграмма. Плюс этого решения в том, что не пришлось тратить уйму времени на качественный дизайн. На тему — как завести своего бота, есть множество статей, на хабре это примеры — раз и два.

В качестве языка программирования я выбрал python 2.7, с выбором его была целая история. Раньше я занимался программированием на C для научных проектов. Немного писал на LabView, но никогда не пробовал современных языков. Потом начал работать в банке, в стек C#, MSSQL, JavaScript. И пришло осознание, что есть ООП и функциональное программирование. Теперь же, когда есть из компьютера только мак, долго выбирал на чем писать. Питон оказался именно той серебряной пулей для обучения и разработки в свободное время.

Для взаимодействия с API телеграмма использовался фреймворк python-telegram-bot. Решение не писать на чистом API принял после того, как увидел вот такие красивые конструкции в примерах кода:

updater = Updater("TOKEN")

    # Get the dispatcher to register handlers
    dp = updater.dispatcher

    # Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler('start', start)],

        states={
            GENDER: [RegexHandler('^(Boy|Girl|Other)$', gender)],

            PHOTO: [MessageHandler(Filters.photo, photo),
                    CommandHandler('skip', skip_photo)],

            LOCATION: [MessageHandler(Filters.location, location),
                       CommandHandler('skip', skip_location)],

            BIO: [MessageHandler(Filters.text, bio)]
        },

        fallbacks=[CommandHandler('cancel', cancel)]
    )

    dp.add_handler(conv_handler)

Главной особенностью моего бота я видел взаимодействие пользователей и сохранение таких показателей, как взаимный рейтинг и рейтинги ретушеров/фотографов. В качестве хранения была выбрана реляционная ДБ SQLite. В ее пользу была простота установки, удобный (знакомый) синтаксис, работа из файла. Единственный минус, который я видел для себя, — слабая производительность, но в моем случае если бы все уперлось в нее, это была бы победа. Итак, стек такой — Python 2.7, Telegram-bot, SQLite.

Первоначально задумка была писать так, как в коде из примера — в концепции stage Conversation. Однако проблемы возникли на этапе переключений стадий. В итоге я решил использовать хранение стадии для каждого пользователя внутри базы SQL.

Реализовано это было внутри класса:


class StageHandler:
	"""Данный клас выполняет роль обработки стадии 
	для пользователя """
	@staticmethod
	def get_user_data(user_data_var):
		if 'current_user' in user_data_var:
        	current_user = user_data_var['current_user']
    	else:
	        DbWorker.user_create(
	        	user_data_var['current_user_user'].id, 
	        	user_data_var['current_user_user'].first_name)
	        current_user = user.first_name
	        user_data_var['current_user'] = current_user

	    if 'current_stage' in user_data_var:
	        user_current_stage = user_data_var['current_stage']
	    else:
	        user_current_stage = DbWorker.user_get_current_stage(
	        	user_data_var['current_user_user'].id)
	        user_data_var['current_stage'] = user_current_stage


	    if 'current_role' in user_data_var:
            user_current_role = user_data_var['current_role']
        else:
            user_current_role = DbWorker.user_get_current_role(
            	user_data_var['current_user_user'].id)
            user_data_var['current_role'] = user_current_role


        if 'photo_genre' in user_data_var:
	        photo_genre = user_data_var['photo_genre']
	    else:
	        photo_genre = DbWorker.user_get_current_genre(
	        	user_data_var['current_user_user'].id)
	        user_data_var['photo_genre'] = photo_genre

		return user_data_var

В итоге приложение выполняет следующие команды, ответы на которые выбираются в зависимости от вашей роли и стадии обработки.

Start — описывает проект, регистрирует пользователя.
Retush — начинает ретушь либо помогает продолжить уже сделанные шаги.
Switch_role — команда для изменения роли в системе.
Set_rating — данная команда позволяет поставить рейтинг как фотографу, так и ретушеру.
Cancel — откатывает текущую обработку.
Status — показывает текущий статус бота.

Такое малое количество команд было сделано для удобства использования.

Взаимодействие между пользователями было реализовано через возможность запускать Job для пользователя. Job — это такой таймер, который по каждому его тику выполняет заданный код. В боте это позволило проверять готовность загруженной фотографии, и для ретушеров проверять, появится ли фотография выбранного жанра. Реализация в коде выглядит следующим образом.


update.message.reply_text('Прекрасный выбор, сейчас мы выберем самое лучшее фото '
                                        'и вы сможете начать с ним работать')
            job_check_send_retusher = Job(
                                        check_photo_for_retusher,
                                        10.0,
                                        context={'retusher_id' : user.id,
                                        'select_genre' : update.message.text
                                        })                   

            job_queue.put(job_check_send_retusher)

def check_photo_for_retusher(bot, job):
    job_data = job.context
    retusher_id = -1
    select_genre = u'Жанр не выбран'
    if 'retusher_id' in job_data:
        retusher_id = job.context['retusher_id']
    if 'select_genre' in job_data:
        select_genre = job.context['select_genre']
    request_row = DbWorker.request_get_not_complete_photo(select_genre)
    photograph_photo_file_id, request_id, photograph_user_id = request_row
    if request_id >= 0:
        if select_genre == u'Жанр не выбран':
            job.schedule_removal()
            reply_keyboard = [['Портрет', 'Пейзаж', 'Макро', 'Другое']]
            bot.sendMessage(chat_id=retusher_id, text='no request with your genre, select another genre',
                    reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
        else:
            photo_file_name = 'photos/{file_name}.jpg'.format(file_name = photograph_photo_file_id)
            bot.sendPhoto(chat_id = retusher_id, photo = open(photo_file_name, 'rb'))
            DbWorker.request_set_to_retusher(request_id, retusher_id)
            DbWorker.user_update_stage(retusher_id, 3)
            job.schedule_removal()
    else:
        job.interval += 10.0
        if job.interval > 80.0:
            job.schedule_removal()
            reply_keyboard = [['Портрет', 'Пейзаж', 'Макро', 'Другое']]
            bot.sendMessage(chat_id=retusher_id, text='no request with your genre, select another genre',
                    reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
        else:
            bot.sendMessage(chat_id=retusher_id, text='no request with your genre = {select_genre}'.
                format(select_genre = select_genre))
    return

По данному проекту есть еще много идей. В том числе — перенести все текстовые послания в SQL, добавить возможность смотреть коллаж своих прошлых фотографий с рейтингом, смотреть рейтинг общий для фотографов и ретушеров, настроить минимальный рейтинг для выбора ретушера и фотографа. В данный момент вы можете попробовать бота по нику SolverBot.

Также если кому-нибудь интересно — хотелось бы написать несколько программ для телеграм-бота. Буду рад участию.

Если получится получить приглашение, я хочу также написать статью про разработку программы для анализа аффилированности в тендерах, и какие удивительные результаты мы получили от ее запуска.

Автор: весёлый усач

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js