Летом проходил конкурс от ВКонтакте на тему «Streaming API Contest». Я решил поучаствовать, но так как нормальной идеи для реализации всех возможностей Streaming API я не нашел, то решил просто выводить записи по указанным правилам.

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

Более подробно здесь.

Сначала думал реализовать все на Node.Js, но потом, чтобы не тратить время на его настройку на VPS сервере, решил использовать PHP.

Всю логику проекта решил отдать клиенту, на сервере же только небольшие настройки по работе с API ВКонтакте и анализатор.

Начнем с интересного — главного:

//все понятно и стандартно
var socket = new WebSocket("wss://streaming.vk.com/stream?key=" + window.key);
var close_connect = ge("close_connect");

/*
* Здесь еще очень много кода
*/

socket.onmessage = function(event) {
  var incomingMessage = event.data;
  var loading = document.getElementById("loading_text");
  var preview = document.getElementById("preview_text");
  var serf = document.getElementById("serf");

  loading.classList.add("none");
  preview.classList.add("none");
  serf.classList.remove("none");
  parser(event.data); //вот здесь начинается вся логика
  console.log(event.data);
};

socket.onclose = function(event) {
  if (event.wasClean) {
    console.warn('Соединение закрыто чисто');
  } else {
    console.warn('Обрыв соединения');
  }
  console.info('Код: ' + event.code + ' причина: ' + event.reason);
  var loading = ge("loading_text");
  if (event.code == 1006) {
    loading.innerHTML = "На этой планете уже есть человек, который сидит на этом сайте.<br/>К сожалению - это не Вы. Повторите попытку позже. <br/> Код ошибки - " + event.code;
  } else {
    loading.innerHTML = "Что-то или кто-то здесь не так... <br/> Код ошибки - " + event.code;
  }
};

socket.onerror = function(error) {};

//close connect
close_connect.addEventListener("click", function() {
  socket.close();
  close_connect.innerHTML = "Соединение закрыто клиентом.";
  ge("analiz_block").classList.remove("none");
}, false);

Как стало понятно из кода выше, на сайте одна сессия для всех пользователей, т.е. пока кто-то уже сидит и парсит правила, другой не получит доступ к сайту.

Здесь есть минус
Пока один человек создает правила и получает новости, другой может видеть эти самые правила и удалять их. Я заметил это поздно, после конкурса, поэтому не исправил.

Далее разберём основную функцию parser(event.data). Я люблю шаблоны, ими легко управлять и с ними легко взаимодействовать.

Много кода
var parser = function(json) {
  var response = JSON.parse(json);
  console.log(response);
  var code = response.code;
  console.log(code);

  if (code != 100) return;

  var tpl_block = document.getElementById("tpl");
  var tpl = tpl_block.innerHTML;
  var main_tpl = tpl_block.innerHTML;
  var content = document.getElementById("main").innerHTML;
  var main = document.getElementById("main");

  var time = response.event['creation_time'];
  var date = new Date(time);
  var type;

  var cnt = ge("cnt");
  var cnt_value = +cnt.innerHTML;
  cnt.innerHTML = cnt_value + 1;

  var creation_time = timestampToDate(response.event['creation_time'] * 1000);

  if (response.event['event_type'] == "post") {
    type = "Публикация";
    count['post'] = ++count['post'];
  } else if (response.event['event_type'] == "comment") { 
    type = "Комментарий";
    count['comment'] = ++count['comment'];
  } else if (response.event['event_type'] == "share") {
    type = "Репост";
    count['share'] = ++count['share'];
  }

  var photo_context;
  if (response.event.attachments) {
    //image 
    if (response.event.attachments[0].type == "photo") {
      photo_context = '<div class="body-img"><img alt="image" src="' + response.event.attachments[0].photo['photo_604'] + '" /></div>';
    } else {
      photo_context = "";
    }
  } else {
    photo_context = "";
  }

  tpl = tpl.split("{event_type}").join(type);
  tpl = tpl.split("{text}").join(response.event['text']);
  tpl = tpl.split("{url}").join(response.event['event_url']);
  tpl = tpl.split("{date}").join(creation_time);
  tpl = tpl.split("{photo}").join(photo_context);
  tpl = tpl.split("{type}").join(response.event['event_type']);
  tpl = tpl.split("{cnt}").join(cnt_value+1);
  tpl = tpl.split("\"").join("'");

  if (filter.top) main.innerHTML = tpl + "" +  content;
  else main.innerHTML = content + "" + tpl;

  //post_id 
  if (response.event['event_type'] != "comment") {
    var post_owner_id = response.event.event_id['post_owner_id'];
    var post_id = response.event.event_id['post_id'];
    var wall_id = post_owner_id + "_" + post_id;
    array_post_id.push(wall_id);
  }

  //limit 
  if (cnt_value + 1 >= +filter.limit) {
    socket.close();
    close_connect.innerHTML = "Достигнут лимит публикаций.";
    ge("analiz_block").classList.remove("none");
  }
}


Сам шаблон:

<section id="tpl" class="none">
    <div class="template-container block-md_" data-type="{type}" data-num="{cnt}">
         <div class="template-header">
              <div class="template-title" id="tpl_title"><b>{event_type}</b></div>
          </div>
          <div class="template-body">
               <div data-body-text="data-body-text-{cnt}">{text}</div>
                {photo}
           </div>
           <div class="template-footer flex">
                <div>Дата публикации - {date}</div>
                <div>
                     <a href="javascript: return;" class="none button-footer-post button-spam" onclick="spam.addMSG({cnt})">спам</a>
                     <a href="{url}" class="button-footer-post" target="_blank">к публикации</a>
                </div>
            </div>
      </div>
</section>

В коде выше происходит:

1. Парс json с информацией о записях(комментарий, репост или же публикация)
2. Парс шаблона по информации, которую мы получили из json

Один шаблон под три разновидности публикации. Удобно.

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

Анализ кода
var analiz = {
  start: function() {
  	//
    if (count['post'] == 0 && count['comment'] == 0 && count['share'] == 0) {
      alert("Невозможно запустить анализ. Причина - публикации не найдены.");
      return;
    }

    var url = "/vk-competition/VKanaliz.php";
    var loading = ge("loading_sp");
    var button = ge("btn_analiz");
    var analiz_stats = ge("analiz_stats");
    loading.classList.remove("none");
    button.classList.add("none");

    ajax.post({
      url: url,
      data: "post_id=" + array_post_id.join(","),
      callback: function(data) {
      	var resp = JSON.parse(data);
      	if (resp.error) {
      	  alert(resp.error);
      	  return;
      	} else {
      	  var count_likes_all_ = resp.response.count_likes_all;
      	  var count_share_all_ = resp.response.count_share_all;
      	  var count_views_all_ = resp.response.count_views_all;
      	  var analiz_posts = ge("analiz_post");
      	  var analiz_share = ge("analiz_share");
      	  var analiz_comments = ge("analiz_comments");
      	  var analiz_likes = ge("analiz_likes");
      	  var analiz_views = ge("analiz_views");
      	  var analiz_reposts = ge("analiz_reposts");
          var analiz_years = ge("analiz_years_");
          var analiz_sex = ge("analiz_sex");
          var percent_sex_m, percent_sex_w;

          if (resp.response.percent_sex_w == "-") 
            percent_sex_w = 0;
          else 
            percent_sex_w = resp.response.percent_sex_w;

          if (resp.response.percent_sex_w == "-")
            percent_sex_m = 0;
          else
            percent_sex_m = 100 - +percent_sex_w;

          console.log("spam " + resp.response.spam + "%");

      	  //insert data 
          analiz_posts.innerHTML = count['post'];
          analiz_share.innerHTML = count['share'];
          analiz_comments.innerHTML = count['comment'];
          analiz_likes.innerHTML = count_likes_all_;
          analiz_views.innerHTML = "?" + count_views_all_;
          analiz_reposts.innerHTML = count_share_all_;
          analiz_sex.innerHTML = percent_sex_w + "%, " + percent_sex_m + "%";
          analiz_years.innerHTML = resp.response.middle_years;

          //show stats
          loading.classList.add("none");
      	  analiz_stats.classList.remove("none");
      	}
      }
    });
  }
}


VKanaliz.php — файл, который возвращает данные в json. Его содержимое можно посмотреть здесь.

Что получилось в итоге? В итоге вышел небольшой проект, который позволяет анализировать публикацию, ее охват и обратную связь. Уже лучше прежнего.

> Посмотреть пример в живую можно здесь
> Исходный код доступен на GitHub

Всем добра!

Комментарии (0)