Я занимаюсь фотографией, и часто у меня возникает потребность в обработке фотоснимка. Сейчас в моде нейронные сети, свертки развертки и даже изменение цветовой палитры снимка и его настроения. Но хотелось ламповой, человеческой обработки. Поэтому я решил создать систему, которая позволит найти ретушеров и обработать снимок с их помощью.
Для ретушеров эта система позволяет потренироваться в обработке снимков определенного жанра, набить руку. В планах — развить данную систему через введение монетизации, таким образом стимулируя приток новых ретушеров.
Сегодня я хочу показать как это было реализовано в виде бота.
Для базы я выбрал вариант развертки приложения в виде бота для телеграмма. Плюс этого решения в том, что не пришлось тратить уйму времени на качественный дизайн. На тему — как завести своего бота, есть множество статей, на хабре это примеры — раз и два.
В качестве языка программирования я выбрал 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.
Также если кому-нибудь интересно — хотелось бы написать несколько программ для телеграм-бота. Буду рад участию.
Если получится получить приглашение, я хочу также написать статью про разработку программы для анализа аффилированности в тендерах, и какие удивительные результаты мы получили от ее запуска.
Автор: весёлый усач