Pull to refresh

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

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

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

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

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

В качестве языка программирования я выбрал 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.

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

Если получится получить приглашение, я хочу также написать статью про разработку программы для анализа аффилированности в тендерах, и какие удивительные результаты мы получили от ее запуска.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.