Pull to refresh

О том, как я пытался сделать свой агрегатор фриланс-проектов, но он не взлетел

Level of difficultyMedium
Reading time14 min
Views6.7K

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

Зарождение идеи

Фрилансом я занимаюсь давно (примерно с 2013 года), и почти так же давно понял, что раз скорость ответа на проекты — один из главных критериев, который помогает взять заказ обойдя других соискателей, то неплохо было бы создать некий инструмент, который упрощает эту задачу.

Сделать это для личного пользования было тривиально просто и быстро — всего за вечер, в далеком 2013 или 2014 году, я сделал небольшой парсер ленты проектов биржи fl.ru – он просто непрерывно сканировал первую страницу ленты, и выводил сообщения, когда появлялся какой-то новый проект. Кликнув на ссылку, можно было перейти на страницу проекта и быстро ответить на него.

Без сомнения, такие уведомлялки для личного пользования создавали многие. Наверняка за всё время их были написаны сотни, а может и тысячи.

Но я пошел немного дальше — добавил фильтры по словам и разделам (вероятно, это тоже делают многие), и… киллер-фичу — голосовое произношение заголовков проектов. Теперь мне было необязательно постоянно отвлекаться и читать уведомления, в 99% случаев достаточно было услышать заголовок проекта, произнесенный хоть и не самым приятным, но достаточно разборчивым голосом (кажется, он назывался Microsoft Anna) из встроенного в Windows движка tts, и я сразу понимал — стоит ли мчаться к компьютеру и срочно отвечать на проект, или можно забить и продолжать заниматься своими делами.

Получилась достаточно удобная штука, правда у нее выявился интересный побочный психологический эффект. Я начал ждать простых заданий, и делать только их. Зачем мучиться и изучать ТЗ, прикидывая — смогу ли я справиться с проектом, и какие он содержит риски, когда можно подождать еще чуть-чуть и пользуясь преимуществом быстрого ответа, взять простое и понятное задание? Казалось бы, это разумная логика, но в перспективе, возможно это не способствовало творческому развитию и постижению новых горизонтов программирования Как вы уже наверное догадались, на фрилансе я занимался именно программированием. Делал в основном парсеры сайтов, автоматизацию браузера, всевозможные прикладные утилиты, и все остальное что можно было сделать быстро и получить заветные 1000 — 5000 рублей за несколько часов работы.

Естественным образом, такая деятельность быстро надоедает. Решив, что уведомлялка может пригодиться не только мне, я решил превратить ее в общедоступный сервис еще в 2016 году. Но тогда это была не слишком серьезная попытка, да и выглядел конечный продукт сомнительно даже с чисто визуальной точки зрения (сказывалось отсутствие опыта и приверженность безнадежно устаревшему уже тогда WinForms), поэтому речь пойдет не о 2016, а о 2023, в котором я вернулся к этой идее, решив реализовать её на новом качественном уровне, и уже имея некоторый опыт full-stack разработки.

Идея была проста — создать сервис с голосовыми уведомлениями, ну и заодно, через телеграм-бот. Работать должно было на всех ОС - Windows, Mac и Linux. Юзеру достаточно просто держать включенным компьютер и установить громкость динамиков на комфортный уровень.

Технические подробности реализации

Если вам не интересны технические детали внутреннего устройства сервиса, сразу переходите к разделу Маркетинговый Epic Fail

Технологический стек

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

Для бэкенда используется то что я более-менее знаю:

  • VDS с 4 ядрами, 8GB оперативной памяти и диском 80 ГБ;

  • Ubuntu 22.04;

  • NET core 6.0 → 7.0 → 8.0 (миграция в процессе разработки) + C#;

  • MongoDB;

  • SignalR;

  • Nginx;

  • .NET Telegram.Bot

При выборе технологий фронтенда подход аналогичен — использовать то что знаю и с чем уже умею работать. А это — в первую и единственную очередь фреймворк Quasar, основанный на VueJS. Я использовал Quasar 2, основанный на VueJS 3 версии.

Он легко позволяет, поменяв лишь несколько настроек, собрать проект под Web, в виде PWA-приложения, а также создать Electron-приложения под все настольные ОС.

Бэкенд создан при помощи Visual Studio, фронт — Visual Studio Code.

Архитектура сервиса

В целом, архитектура сервиса выглядит примерно так:

Как смог, так и нарисовал...
Как смог, так и нарисовал...

За всю бизнес-логику отвечает приложение ASP.NET WebApi, которое функционирует в виде Linux сервиса.

Непосредственно парсингом фриланс-бирж занимаются отдельные сервисы, которые сканируют в несколько потоков ленты бирж и непрерывно кидают все найденные проекты на localhost в основной сервис, задача которого в данном случае — игнорировать те проекты, которые уже были обнаружены ранее, и обрабатывать новые.

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

Немного о TextToSpeech. Сколько-нибудь пристойных по качеству синтеза автономных решений найти не удалось, все они были не сильно лучше (если вообще лучше) старой доброй Microsoft Анны, поэтому решено было воспользоваться облачным сервисом https://cloud.speechpro.com/service/tts

Понравилось и качество и цена — при сравнимом с решением от Яндекса качестве, цена весьма демократична — немногим более 500 рублей за миллион символов, получается в 3 раза менее затратно. Но документация оставляет желать лучшего, приличной библиотеки под .NET не нашлось, поэтому работу с API пришлось реализовывать самому.

Еще одна проблема — в иные дни speechpro внезапно(!) перестает работать на несколько часов. Странное сочетание высокого качества голосового синтеза с отвратным качеством сервиса... Пришлось подключить и аналог от Яндекса, чтобы пользователи не оставались временами без голосовых уведомлений.

Синтезированные голоса конвертируются из wav (да, с speechpro они приходят исключительно в wav) в mp3, и при помощи WebSocket рассылаются клиентам. Были мысли использовать OPUS или еще какой-нибудь более крутой формат сжатия звука чем mp3, но после серии экспериментов выяснилось, что итоговый результат не столь уж поражает воображение, а у клиентов могут быть проблемы — все браузеры поддерживают mp3, а с остальными форматами — когда как. (Напоминаю — приложения под настольные ОС сделаны на Electron и по сути, представляют собой браузер).

Каждый синтезированный заголовок проекта хранится в базе данных 3 месяца, и удаляется, если по прошествии этого времени он не был повторно использован хотя бы один раз. Это позволяет примерно на 15% сократить запросы к облачным сервисам голосового синтеза, потому что определенные заголовки повторяются довольно часто (ну например, «сделать сайт», «нарисовать логотип» и т.д).

Сам парсинг проектов с бирж осуществляется через халявные прокси, массово получаемые с https://best-proxies.ru/ всего за 500 рублей в месяц. Если не пользоваться прокси, то биржи фриланса могут просто заблокировать IP адрес сервера - нагрузка с него хоть и мизерная, но постоянная.

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

Примерно за 5 дней накапливаются до 150 тысяч прокси, и хотя 90% из них протухают очень быстро, оставшихся хватает для того чтобы успешно парсить биржи. В среднем, от момента появления нового проекта на бирже до уведомления пользователя о нём, проходит около 7 секунд, в редких случаях — до 30 секунд.

Для большого списка прокси нашлось еще одно применение. Изначально я планировал что-то наподобие собственной системы защиты от DDoS атак на уровне приложения (L7). Например, одним только полнотекстовым поиском по всей базе проектов даже школьник легко мог бы положить сервер, и в ночных кошмарах мне уже мерещились коварные конкуренты которые ддосят сайт в тот самый момент на него идет поток новых юзеров с оплаченной рекламы.

Защиту от атак на уровни L3-L4 обещал на себя взять хостер (VDSina, если кому интересно), не знаю насколько такая защита действенна, но в любом случае, от L7 это бы не спасло, поэтому пришлось потратить целый месяц на продумывание разных алгоритмов блокировки IP, с которых идет ненормальный трафик. Но как ни мудри, а от распределенной атаки это все равно бы не помогло.

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

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

Теоретически можно было воспользоваться Сloudflare, но при этом появлялась проблема с почтовым сервером — обратную DNS-запись в таком случае указывать нельзя, это лишает смысла использование Cloudflare для сокрытия IP сервера. А без обратной записи сильно повышается вероятность попадания писем в спам. Мне советовали воспользоваться одним из сервисов рассылки писем, но лимиты на бесплатных тарифах (как мне казалось) были слишком скромны, поэтому для почты используется Postfix, вроде бы письма приходят нормально.

Еще один внешний API, который я использовал, это конечно же (куда же без него), платежный агрегатор, потому что для использования функционала голосовых уведомлений с каждого пользователя планировалось снимать 10 российских рублей в день.

Скрытый текст

При регистрации ИП, укажите не свой постоянный мобильный номер, а какой-нибудь такой, который можно выключить и забыть о нём. Иначе впереди вас будет ожидать по крайней мере год, в течение которого вы вынуждены будете ежедневно принимать тонны звонков с назойливыми предложениями от банков открыть счет для бизнеса. Потому что номер, который вы укажете, им обязательно продадут.

При разработке бэкенда 80% времени ушло на инфраструктурный код — самопальный WAF, админку, слой доступа к данным, логгирование и тд, и лишь 20% - непосредственно на основную логику.

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

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

База данных

Наверное, MongoDB отлично подходит для разработки чего-то подобного. К счастью, транзакции тут почти не используются, потому что они, кажется, до сих пор являются слабым местом MongoDB. Не то чтобы их вообще не было, но работать с ними неудобно, и кроме того, они ведут себя, так сказать… не всегда очевидным способом, но это тема для отдельного разговора.

Еще при работе с MongoDB уходит уйма времени на то чтобы разобраться — как выполнять довольно сложную логику вставки, удаления, обновления, инкремента/декремента, вставку значений во вложенные коллекции. В условиях многопоточности (а Web естественным образом ее предполагает) эти операции необходимо делать атомарно на уровне коллекции для предотвращения перехода базы в не консистентное состояние при параллельном доступе.

Поэтому приходилось городить подобные конструкции:

public async Task<bool> UpdateFilterDeleteStrongStopAsync(string userId, string filterId, string tag)
{
    var filter = Builders<DbUserFilter>.Filter.Eq(x => x.UserId, userId)
        & Builders<DbUserFilter>.Filter.ElemMatch(x => x.Filters, Builders<Filter>.Filter.Eq(x => x.FilterId, filterId));

    var update = Builders<DbUserFilter>.Update.Pull(x => x.Filters.FirstMatchingElement().StrongStops, tag);

    var r = await collection.UpdateOneAsync(filter, update);
    return r.ModifiedCount > 0;
}

А порой, и такие анти-паттерны (логика в строках), когда гугление StackOverflow не давало более элегантного решения за разумное время:

public async Task<bool> UpdateFilterSiteIsEnabledAsync(string userId, string filterId, string domain, bool on)
{
    var r = await collection.UpdateOneAsync(x => x.UserId == userId,
        Builders<DbUserFilter>.Update.Set("Filters.$[f].SitesSectionsFilters.$[s].IsEnabled", on),
        new UpdateOptions
        {
            ArrayFilters = new List<ArrayFilterDefinition>
            {
                new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("f.FilterId", filterId)),
                new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("s.Domain", domain))
            }
        }
    );
    return r.ModifiedCount > 0;
}

SignalR

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

Frontend

С фронтeндом всё довольно просто — это просто SPA-сайт, написанный на VueJS, точнее - на Quasar, который основан на VueJS.

Сам по себе Quasar обладает отличной библиотекой визуальных компонентов в стиле Material Design, и в данном случае этой библиотеки оказалось достаточно. Хотя по предыдущему опыту мне известно, что эти компоненты хоть и достаточно качественные, но слишком простые, и зачастую их функционала не хватает для чего-то серьезного, например нет хорошего слайдера изображений. Однако, повторюсь, тут этого и не требовалось.

В сущности, для меня программирование фронта — это рутина вперемешку с попытками сделать что-то такое, что выглядит не слишком уродливо, и описывать эту рутину смысла нет.

Расскажу лишь о принципиальных проблемах, с которыми пришлось столкнуться.

Поскольку фронт это просто сайт, то можно было и оставить всё в рамках браузера, если бы не 2 обстоятельства:

  1. Для постоянного использования держать постоянно вкладку браузера неудобно — её можно случайно закрыть.

  2. Все без исключения современные браузеры обладают нежелательным в нашем случае поведением — они не дают воспроизводить звук, до тех пор пока пользователь что-нибудь не сделает (хотя бы кликнет мышью в любом месте) на сайте. Вряд ли разумно заставлять пользователя что-то делать каждый раз после захода на сайт. Даже с учетом того что ему надо делать это лишь один раз в начале «рабочей смены». Он может просто забыть об этом, и пропустит голосовые уведомления.

К счастью, Quasar позволяет легко и непринужденно превратить SPA в PWA, почти не переписывая никакой код.

PWA-приложение уже не нуждается в предварительных действиях для воспроизведения звука, и всё бы было хорошо, если бы не один сценарий…

Дело в том, что установить PWA можно не только лишь с любого браузера. Можно через Chrome или Edge (если речь про Windows), а с Firefox нельзя.

Между тем, Firefox по умолчанию у кого-то все еще стоит, например у меня. Не сильно страшно один раз зайти через хром, чтобы поставить PWA-приложение, но после этого все ссылки с этого приложения будут самым подлым образом открываться именно в том браузере через который оно и было установлено, а не в браузере по умолчанию! А ведь мы постоянно должны переходить по ссылкам на проекты, и намного приятнее когда ссылка открывается в привычном браузере.

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

Что же, остается один путь — скомпилировать проект в виде Electron приложения. Quasar легко позволяет сделать и это, и в коде опять почти ничего не надо менять.

Правда, сделать это пришлось под все ОС. Зато теперь с сайта можно скачать Electron-приложение, которое является точной копией этого самого сайта. Дешево и сердито.

На виртуалке вроде работает и на Маке и на Линуксе… Но не уверен что так будет у всех. Но поскольку физических и финансовых возможностей устраивать масштабное тестирование не было, я решил поскорее перейти к самому интересному — начать рекламу приложения.

Итак, переходим к грустной части нашего повествования.

Маркетинговый Epic Fail

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

Скрытый текст

К слову сказать, нынешние владельцы одной из известных бирж фриланса (fl.ru) никак не могут понять этого очевидного обстоятельства, и предпринимают какие-то абсурдные шаги по «борьбе с демпингом» и т.д., в результате чего фрилансеры, а следом и заказчики, просто бегут от них на другие биржи, которые более адекватно пытаются приспособиться к текущим реалиям.

Да и идея сама по себе давно устарела, что-то такое было разумно делать лет 10 назад, сейчас это уже не сильно актуально, даже с учетом оригинального (такого больше ни у кого нет, честно, я проверял) подхода с голосовыми уведомлениями.

И всё же, предварительно я выяснил что в моменте в рунете на крупных биржах существует по крайней мере 20 тысяч активных фрилансеров, которые являются моей целевой аудиторией — они мониторят наиболее крупные биржи фриланса на появление подходящей работы и для них важно быстро отвечать на проекты.

Если бы удалось привлечь из этого числа хотя бы 1000 фрилансеров (5% от существующей целевой аудитории), и половина из них платила бы 10 р в день (ну или все платили бы 10 р через день), то 150 т.р. в месяц было бы отличным вознаграждением за мои труды.

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

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

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

Основной площадка, на которой я собирался рекламировать сервис, должен был стать телеграм, точнее, телеграм-боты.

Расчет тут был такой… Поскольку мы имеем дело с фрилансерами, наверняка многие их них пользуются какими-то ботами, да хотя бы теми что предоставляют услуги по доступу к нейронным сетями. Вот на них я и ориентировался.

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

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

Конечно, сначала надо было провести эксперименты, потому что выкидывать 100 тысяч рублей в непонятную авантюру было рискованно.

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

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

Ни один из этих клиентов в дальнейшем не принес мне ни копейки.

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

Следующая попытка была - воспользоваться Яндекс Директом. Возможно, что-то было сделано неправильно, но потратив 2000 рублей, я получил 1600 посетителей за сутки и 0 регистраций. Поэтому дальнейшие исследования в области Яндекс Директа я счел бессмысленными.

Может быть есть смысл размещать статьи в блогах? Ок, разместив несколько статей на VC, я получил 5 зарегистрированных пользователей, ни один из них также не заплатил ни копейки. Хотя парочка постоянно пользуется до сих пор бесплатным функционалом уведомления через бота.

Другие платформы для блоггинга дали вообще нулевой результат.

Потом были попытки рекламировать сервис совсем уж дурацкими способами — например через socpublic.com. Эффект — аналогичен Яндекс Директу, хоть и в 10 раз дешевле. Наверное, таким образом можно устроить кому-нибудь дешевую ддос-атаку, но точно не получить ни одного пользователя.

Наконец, я раздобыл сотню телеграмм-аккаунтов фрилансеров и заказал прямую рассылку фрилансеру-спамеру за 1000 рублей. Уже не корысти ради, а сугубо ради того чтобы узнать — это вообще кому-то кроме меня надо? Итоговый отчет спамера: 60 прочитанных сообщений. Мой итог: 4 регистрации, и опять никто не заинтересовался функционалом голосовых уведомлений, то есть никто не заплатил.

На этом, пожалуй, можно было закончить и сделать выводы, хоть и неутешительные, но полезные на будущее.

Вывод № 1:

Как утверждал старина Кант, только эксперимент является критерием истины, этим и отличается научный метод познания от неуёмного фантазирования, называемого метафизикой.

Вывод № 2 (более практичный):

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

Даже мой сервис — хороший и нужный, я же пользуюсь им сам и плачу по 2000 рублей в месяц за сервер, а значит, 100%, им бы мог пользоваться кто-то еще, если бы только знал о его существовании. Но в этом-то и проблема. Никто не знает. И не существует достаточно дешевых способов достучаться до целевой аудитории. Даже если можно достучаться, не факт что удастся объяснить, и показать — как и чем это может быть полезно. Этот вопрос требует основательной проработки, и этим должны заниматься профессионалы за хорошую плату, но если с клиента ты получаешь… всего 300 рублей в месяц (на самом деле 0, хаха), то любые вложения в таких профессионалов будут приводить лишь к прямому убытку.

Вывод №3 (максимально конкретный):

Если надо сэкономить на рекламе — следует делать SEO-оптимизированные сайты. SPA тут точно не годится.

Вывод № 4 (личный):

Я - лузер.

Заключение

Ну что же, если кто-то дочитал до конца этой печальной истории, то ссылочка на сервис вот: https://lancetracker.com Не сочтите за рекламу, ну нельзя же так долго рассказывать и в конце концов, так и не показать.

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

Но кажется, я уже примерно знаю ответ на этот вопрос...

Tags:
Hubs:
Total votes 15: ↑13 and ↓2+13
Comments17

Articles