Архив

Gmail Show-M.-Later

Gmail Show M later

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

Есть более древние варианты в галерее скриптов, Gmail Snooze например, но они позволяют работать только с дневными интервалами и устроены таким образом, что письмо добавленное в 23:00, в 01:00 снова окажется в инбоксе. Поэтому я сначала модифицировал, а потом полностью переписал. В моей реализации, письма живут более правильной жизнью.

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

Касательно объемов данных. Каждому пользователю google выделяется 50мб под ScriptDB, но может быть и больше, если у вас платный аккаунт. Что бы упереться в размер данных, нужно будет очень постараться, поэтому до первой проблемы, решать ее не собираюсь. Тестировал максимум на 20 активных письмах, все прошли полный цикл без ошибок. Наверно, если писем будет больше, то возможен тайм–аут. Если столкнулись с проблемой, пишите, буду исправлять.

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

Буду рад ответить на вопросы.
Буду очень рад, если появится перевод на другие языки.

https://github.com/Rpsl/Show–M–Later


Nginx изменение картинки по рефереру

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

В целом весьма логично, если раньше все боялись за свой трафик, то теперь вопросы трафика мало кого интересуют, поэтому и приоритеты сместились в другую сторону. Хотя я видел ситуацию, когда на одном из рабочих ресурсов, образовался хотлинк канал японского трафика на 250mb/s.

Попробуйте открыть это изображение в новой вкладке (убедитесь что броузер не закешировал)

%раньше тут была картинка%

Рассматривая разные решения, я нашел самый простой способ, делать это с помощью nginx и встроенного модуля ngx_http_referer_module.

test_images.conf

server {
        listen 80;
        server_name images.dev;
        root /www/data/test_images;

        location /test_images {
                valid_referers none blocked server_name images.dev;

                if ($invalid_referer) {
                        rewrite ^(.*)/(.*)$ /images_bad/$2 last;
                }

                rewrite ^(.*)/(.*)$ /images_ok/$2;
        }

        location /images_ok {
                internal;
                alias /www/data/images/test_images/ok;
        }

        location /images_bad {
                internal;
                alias /www/data/images/test_images/bad;
        }
}

Собственно механика очень простая. Мы определяем с помощью "valid_referers" список правильных доменов, можно добавить в него домены поисковиков, например, что бы при поиске картинок пользователю показывалась правильная картинка. Потом мы создаем два внутренних location, которые отвечают за показ правильных и не правильных картинок. В случае не правильного реферера, в конструкции if мы делаем редирект на локейшн с картинками, которые должны отдаваться жуликам, которые ленятся скачивать картинку на свой сервер.

Ну вот, в общем и все. Осталось только позаботиться, что-бы у нас были необходимые картинки.

Тут можно решать разными путями, например, при создание оригинальной картинки создавать ее "обработанный" вариант. Но мне больше нравится ленивая загрузка, которую я, когда-то давно, делал в iCacher, основная идея проста: при попытке запроса "обработанной" картинки, если ее нету, управление передается скрипту, который проверяет ее оригинал и делает ее "обработанную" копию, которая отдается пользователю.

При повторном обращение, запрос не дойдет до скрипта и пользователю отдастся статический файл. Таким образом мы получаем генерацию изображений по требованию. Еще можно добавить крон скрипт, который будет удалять "обработанные" картинки через 30 дней.

Я не уверен, что моя реализация хороший пример и буду рад если вы ее покритикуете. Плюс, я так и не придумал как избавиться от if в данном случае (вариант хранения файлов в images_ok/1/ считаю плохим).


Шутка дня

<!‐‐[if lte IE 7]> <style> * {font-family: "Comic Sans" !important;} </style> <![endif]‐‐>

Почему жилье дорожает

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

Я Вам сейчас очень просто объясню, почему дома подорожали к зарплате - бензин тут ни при чем. Допустим, мы - я, Вы и Хроноскопист летели на самолете через Тихий океан. В пути мы втроем накушались абсента, надебоширили, отломали дверь от туалета, и нас за это выкинули в море через аварийный выход. По счастью, рядом с местом нашего падения обнаружился маленький безымянный полинезийский остров. Выбравшись на берег, мы посовещались, и решили считать его новым государством под названием Соединенные Штаты Абсента (США).

Когда нас выкидывали из самолета, то багажа нам, естественно, не выдали. Поэтому, всех материальных и нематериальных активов у нас - только туалетная дверь, которую Вы таки прихватили с собой. И вообще, несмотря на абсент, Вы у нас оказались самым запасливым - в бумажнике у Вас, совершенно случайно, обнаружилась банкнота в $100. Таким образом, в наших США имеются нефинансовые активы - дверь, и финансовые активы, они же денежная масса - $100. Это все наши сбережения. Поскольку у нас больше вообще ничего нет, то можно сказать и так - у нас есть один материальный актив - дверь, обеспеченный денежной массой в $100. Т. е. наша дверь стоит $100.

Немного протрезвев, мы решаем, что надо как-то обустраиваться. Самый быстрый из нас оказался Хроноскопист. Он тут же объявил, что создает банк и готов взять в рост имеющиеся у населения денежные сбережения под 3% годовых - ну не сидится человеку без дела. Вы отдаете ему $100, и он их записывает в блокнот в статью "Пассивы - Дипазиты". Но я тоже не лаптем щи хлебал - зря я что ли столько времени занимаюсь расследованием экономического мухлежа - я знаю как изъять у Вас и дверь и $100. Я предлагаю Вам взять Ваши $100 в рост под 5% годовых. Вырываю листик из своего блокнота и пишу на нем - "Аблегиция на $100 под 5% годовых". Вы чувствуете, что Вам поперло. Забираете деньги у расстроенного Хроноскописта с дипазита и отдаете их мне в обмен на мою аблегацию.

Я беру Ваши $100 и кладу их на дипозит в банк обратно обрадованного Хроноскописта.
По хорошему, на этом можно было бы и успокоиться и пойти всем заняться делом - пальму потрясти или за моллюсками понырять, снискать себе хлеб насущный, так сказать. Но Вы ж знаете - я неуемный финансовый гений, такие пустяки как кокосы и устрицы меня не интересуют. Помыкавшись по нашему острову - 50 шагов от южного побережья до северного, и 30 с запада на восток, я придумываю гениальную комбинацию. Я подхожу к Вам и предлагаю на пустом месте заработать еще 1% годовых. Взять в банке Хроноскописта кредит под 4%, и купить у меня еще одну аблигацию под 5%. Вторую аблегацию на $100 я тут же выписываю на блокнотном листике, и машу ею у Вас перед носом. Недолго думая, Вы бежите в банк и берете кредит $100 под залог моей первой аблегации на $100. Они там есть - я их туда положил на дипазит. Вы отдаете мне заемные $100 и прячете вторую аблегацию к себе в бумажник - теперь у Вас есть моих аблегаций на $200. А $100 я кладу в банк - теперь у меня там $200 на дипазите. Хроноскопист аж подпрыгивает от радости - кредитный бизнес попер.

Думаете я на этом остановлюсь? Ага, сейчас - я уже выписал Вам третью аблегацию. Бегом в банк за кредитом под залог второй аблегации. Ближе к вечеру, набегавшись по острову с этой сотней баксов и изодрав все листочки из блокнота на аблегации, мы имеем следующую картину. У Вас на $5000 моих аблегаций, а у меня на $5000 дипазитов в банке. Теперь, я чувствую, что пришло время прибрать Вашу дверь к рукам. Я предлагаю купить ее у Вас за $100. Но Вы вредничаете - дверь-то всего одна, и заламываете цену в $1000. Ну, $1000 так $1000 - в конце концов у меня на депозите лежит целых $5000. Я на последнем блокнотном листочке направляю платежное поручение Хроноскописту, перевести $1000 с моего дипазита на Ваш, и забираю Вашу дверь.

Если нашу бухгалтерию отдать американскому экономисту с гарвардским дипломом, он сообщит нам, что наши США располагают $1000 материальных активов в виде двери, и $10000 финансовых активов в виде аблегаций и дипазитов. Т. е. что стоимость нашего совокупного имущества увеличилась за день в 110 раз.

Менее тонкий и образованный человек сказал бы, что мы - три дебила, у нас как была одна дверь и $100, так и осталось, и что только конченные дебилы могли целый день рвать листочки из блокнота, вместо того, чтобы нарвать кокосов. Кто из них прав - решайте сами. Но механизм относительного роста цен на дома именно такой, что в США, что в Японии, что в России.


Percona Server Configuration Wizard

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

percona

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

# Generated by Percona Configuration Wizard (http://tools.percona.com/) version REL5-20120208
# Configuration name zlo generated for gmail@rpsl.info at 2012-07-05 10:31:23

[mysql]

# CLIENT #
port                           = 3306
socket                         = /srv/mysql/mysql/mysql.sock

[mysqld]

# GENERAL #
user                           = mysql
default_storage_engine         = InnoDB
socket                         = /srv/mysql/mysql/mysql.sock
pid_file                       = /srv/mysql/mysql/mysql.pid

# MyISAM #
key_buffer_size                = 32M
myisam_recover                 = FORCE,BACKUP

# SAFETY #
max_allowed_packet             = 16M
max_connect_errors             = 1000000
skip_name_resolve
sysdate_is_now                 = 1
innodb                         = FORCE

# DATA STORAGE #
datadir                        = /srv/mysql/mysql/

# CACHES AND LIMITS #
tmp_table_size                 = 32M
max_heap_table_size            = 32M
query_cache_type               = 0
query_cache_size               = 0
max_connections                = 500
thread_cache_size              = 50
open_files_limit               = 65535
table_definition_cache         = 1024
table_open_cache               = 256

# INNODB #
innodb_flush_method            = O_DIRECT
innodb_log_files_in_group      = 2
innodb_log_file_size           = 128M
innodb_flush_log_at_trx_commit = 1
innodb_file_per_table          = 1
innodb_buffer_pool_size        = 1456M

# LOGGING #
log_error                      = /srv/mysql/mysql/mysql-error.log
log_queries_not_using_indexes  = 1
slow_query_log                 = 1
slow_query_log_file            = /srv/mysql/mysql/mysql-slow.log

Ну и в качестве бонуса, рекомендую почитать доклад "Optimizing MySQL Configuration" от Percona


Nginx allow POST static files

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

Мое решение лучше тем, что оно не требует поднятия второго сервера на отдельном порту и вообще эстетически красивое.

Кратко о проблеме:

Все знают о том что в Facebook'e есть iframe страницы, но не все знают что Facebook эти страницы запрашивает через POST, а не через GET, как делают все остальные. Поэтому все fb:iframe приложения запрашивая у Nginx'а страницы в ответ будут получать ошибку:

405 "Method Not Allowed"

Вариант который предлагает dotZero выглядит примерно вот так (это я для истории и поисковых запросов оставлю):

upstream fb_static {
    server localhost:8001;
}

server {
    listen       443;
    server_name  _;

    #allow_static_post on;

    error_page 405 =200 @405;

    location @405 {
        proxy_method GET;
        proxy_pass http://fb_static;
    }
}

server {
    listen 8001;
    server_name _;

    location / {
        root "/var/www/fb";
    }
}

Вариант конечно хороший и самое главное рабочий. Но давайте попробуем так:

server {
    listen  80;
    server_name _;

    # Тут мы описываем поведение
    # для нашей кастомной ошибки
    error_page 405 = $uri;

    # Дефолтный location
    location / {
        access_log off;
        root    /srv/path
        try_files $uri $uri/index.html /index.php$is_args$args;
    }
}

Что тут происходит? Мы складываем весь статический контент который будет запрашивать facebook в отдельную директорию, просто, для красоты. Определяем location для этой директории, который будет всегда возвращать код ошибки, например, 599. И определяем поведение для обработки этой ошибки, как попытку отдать данные по этому урлу.

Вот и все, работает. Читайте мануалы.

UPD: вернул конфиг до состояние обработки напрямую 405 error_page, т.к. поведение описанное до этого, не совсем соответствовало реальности. Таким образом, после запроса файла через POST, будет выдана 405 ошибка, которая обработается в error_page 405 = $uri и будет отправлена в location /

То есть либо файл найдется и будет отдан, либо управление будет передано в index.php.


PHP: detect UTF-8

Пару недель назад, мне кто-то доказывал, что mb_detect_encoding, работает в разы быстрее своих надуманных конкурентов. Задача простая, проверить входящую строку на принадлежность к UTF-8. Логика простая, либо UTF-8, либо нет. В повседневной жизни, обычно не приходится выбирать из зоопарка кодировок и, чаще всего, строка либо в utf8, либо в cp1251.

Сделал простой синтетический тест. Прогнал определение через функцию 10000 раз. Посмотрел кто быстрее.

Вариант 1, который мне нравится:

$s = microtime(true);

for( $i=0; $i<10000; $i++ )
{
    $string = 'sdf;gklaj;skdjf;sdkfls;dfjk';

    if( preg_match('//u', $string ) )
    {

    }
}

$e = microtime(true);

echo "\n\n" . $e-$s;

Средний результат выполнения скрипта на моей машине ~0.020 сек. Сравнивал я его с более правильным решением, которое выглядит так:

$s = microtime(true);

for( $i=0; $i<10000; $i++ )
{
    $string = 'sdf;gklaj;skdjf;sdkfls;dfjk';

    mb_detect_encoding( $string, array('UTF-8'), true );
}

$e = microtime(true);

echo "\n\n" . $e-$s;

Средний результат выполнения скрипта на моей машине ~0.050 сек. После этих тестов, решил прогнать на больших объемах данных, сгенерировал текст размером 10к байт и пропустил через функции. Для preg_match время выполнения составило ~0.120 сек, для mb_detect_encoding ~0.7 сек. Из этих изысканий можно сделать вывод, что определение через preg_match работает в разы быстрее чем стандартные механизмы, но, вероятно, проигрывает в количестве доступых фич.

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


Про webmoney

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

webmoney

Есть у меня виртуалка, живет уже много лет, в ней крутится только Win XP и кипер класик. Апдейты там еще какие–то ставятся хз. Кроме кипера и Google Chrome в ней вообще ничего нету используется раз в месяц для вывода денег из sape и транзита этих денег на оплату мобильника, инета, серверов.

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

Через пару дней собрался силами и прошел этот квест снова, далее неделя ожидания и куча ебанутых писем на почту о статусе моей заявки "Костя из саппорта проверил ваши данные", "Дима из саппорта подтвердил ваши данные", "Ваша заявка передана из отдела саппорта в отдел заявок" — Нахуй мне это знать??? Мне важно что заявка получена вами и что на нее пришел ответ. Спустя неделю получил новый файл ключей, залогинился, снова, блядь, активируйте вашу систему, ссылка отправленна на почту.

Потом я уже почитал этот пост, решил сделать себе из кипер классик — кипер мини, енум, клиент для мака, полтора часа попыток понять что это и нахуй оно вообще нужно, 2000 пройденых каптч, 50 полученых смс и ачивка "Вебмани в МакОС" получена.

Спасибо. Охуеть. Сдохните.


Молоток.ру и забота о пользователях

В последнее время я достаточно щепетильно отношусь к своему почтовому ящику. Я начал это делать, после того как понял, что мне в день приходит более сотни левых писем о всяких акциях, скидках, конкурсах и прочие дайджест рассылки. Я отписался от 90% сервисов которые не представляют для меня интереса и жизнь вроде наладилась, дышать стало свободнее. Только остаются некоторые уебищные сервисы, отписаться от которых - дикий квест. Молоток.ру один из таких. Не помню по какому роковому стечению обстоятельств лет 8 назад я случайно завел там аккаунт, но точно помню, что с тех пор я его больше не посещал, а вот письма со всякой херней они продолжают слать мне до сих пор.

У вас кстати есть карточка от много.ру? Я ее случайно лет 10 назад завел. Прошло лет 9 как я ее потерял, но письма они продолжают слать, у них видимо не хватает мозгов додумать, что аккаунты которые не подавали активности последние 10 лет можно считать не валидными.

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

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

trololo

Главной мыслью этой заметки является обращение к веб мастерам. Пожалуйста, делайте то, что нужно пользователям, а не то, чем вы занимаетесь.


Nginx detect mobile browser

Регулярка для Nginx

set $mobile_rewrite do_not_perform;

if ($http_user_agent ~* "android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge|maemo|midp|mmp|netfront|opera m(ob|in)i|palm(os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino") {
  set $mobile_rewrite perform;
}

if ($http_user_agent ~* "^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-||_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac(|\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-)") {
  set $mobile_rewrite perform;
}

if ($mobile_rewrite = perform) {
  rewrite ^ http://m.site.com/ redirect;
  break;
}

Ну и собственно вот тут собранные подобные рецепты для различных языков и сервисов.