image

Я, как и любой другой выпускник, переживаю по поводу экзаменов. От баллов, полученных на ЕГЭ зависит слишком многое, поэтому сейчас трудно думать о чем то другом. Чтобы не обновлять сайт check.ege.edu.ru каждые две минуты, я решил написать расширение, которое будет делать это за меня, а заодно присылать уведомления, в случае, если какой-то из экзаменов проверили.

Задача не очень сложная, но есть один неприятный момент. Сайт, на котором публикуются результаты, требует, чтобы информация о участнике, при каждом закрытии браузера, заполнялась заново. Это очень не удобно, поэтому пришлось немного по-импровизировать. Было замечено, что вся необходимая информация хранится в файлах cookie, поэтому можно просто сохранять и обновлять их когда потребуется, без необходимости вводить данные заново. Таким образом, логика работы расширения такова:


Расширения для браузера пишутся на js, оформляются с помощью html и css. В целом, разработка расширения мало чем отличается от создания сайта. Обычно любое расширение имеет следующий «скелет»:


Manifest.json


В этом файле хранится основная информация: версия, название, описание, подключаемые файлы и тд.

manifest.json
{
  "manifest_version": 2,
  "name": "Результаты ЕГЭ",
  "description": "Расширение следит за обновлениями на сайте check.ege.edu.ru и оповещает о новых результатах",
  "version": "1.0.0",
  "icons": {"128": "icon.png"},
  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  },

  "background": {
    "scripts": ["jquery.js","background.js"], //фоновые страницы
    "persistent": false
// Эта строка включает режим Event Pages, который призван улучшить производительность за счет того, что расширение будет работать только тогда, когда это необходимо. Именно поэтому в дальнейшем будет использоваться alarms вместо setInterval.
  },
  "permissions": [ //разрешения
    "cookies",
    "tabs",
    "alarms",
    "notifications",
    "storage",
    "http://check.ege.edu.ru/*",
    "https://check.ege.edu.ru/*"

  ],
  "web_accessible_resources": [
// Это необходимо для правильной работы оповещений
    "icon.png"
  ]
}


Background.js


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

background.js
chrome.alarms.create("1min", {  // Повторяем код ниже каждую минуту
  delayInMinutes: 1,
  periodInMinutes: 1,
});

chrome.alarms.onAlarm.addListener(function(alarm) {
  if (alarm.name === "1min") {
    chrome.cookies.getAll({"url": 'http://check.ege.edu.ru'}, function(cookie) { 
    if (cookie.length){  
      chrome.storage.local.set({'sCookie': cookie}); 
// Если на сайте есть cookie файлы, то сохраняем их
      checkUpdates(); // и проверяем обновления
    }else{
      chrome.storage.local.get(['sCookie'], function(result) {
        if (!jQuery.isEmptyObject(result)){ 
// Если нет, то загружаем сохраненные ранее
          chrome.cookies.set({
              "url": 'http://check.ege.edu.ru',
              "name": result["sCookie"][0]["name"],
              "value": result["sCookie"][0]["value"]
          }, function(){
            checkUpdates(); // и тоже проверяем обновления
          });
        }
      });
    }
  });
  }
});

function checkUpdates(){
  var myInit = { 
      method: 'GET',
      credentials: 'include'};
  fetch('http://check.ege.edu.ru/api/exam', myInit).then(r => r.text()).then(result => { // Загружаем результаты ЕГЭ
    var examRes = {};
    jQuery.parseJSON(result)['Result']['Exams'].forEach(function(element) {
      examRes[element['Subject']] = element['TestMark']; 
// Сохраняем их в массив examRes
    });
    chrome.storage.local.get(['examRes'], function(result) {
      for (var element in result['examRes']){
        if (result['examRes'][element] != examRes[element]){ 
            showNotification(element, examRes[element]);  
            chrome.storage.local.set({'examRes': examRes});
// Если они не совпадают, с прошлыми данными, то 
// показываем уведомление
// и сохраняем новые данные
        }
      }
    });
  })
}

function showNotification(subName, mark){ 
// показываем уведомление, состоящее их названия предмета и баллов
  chrome.notifications.create('reminder', {
        type: 'basic',
        iconUrl: 'icon.png',
        title: 'Новые результаты!',
        message: subName + ' - ' + mark
     });
}



Popup.html


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


popup.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Результаты ЕГЭ</title>
  <script type="text/javascript" src="jquery.js"></script>
  <script src="popup.js"></script>
  <style>
<!-- Здесь ничего интересного -->
  </style>  
</head>
<body>
  <table border="1" id="mainTable">
  <caption><b>Ваши результаты ЕГЭ</b></caption>
  <tr>
    <th>Предмет</th>
    <th>Тестовый балл</th>
    </tr>
  </table>
</body>
</html>


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

popup.js
chrome.cookies.getAll({"url": 'http://check.ege.edu.ru'}, function(cookie) {
// Смотрим, установлены ли на сайте необходимые cookie
  if (cookie.length){
    chrome.storage.local.set({'sCookie': cookie});
    createTable();
// Если они есть, то сохраняем их и отрисовываем таблицу
  }else{
    chrome.storage.local.get(['sCookie'], function(result) {
// Если нет, то смотрим, нет ли у нас сохраненных ранее cookie файлов
      if (!jQuery.isEmptyObject(result)){
        chrome.cookies.set({
          "url": 'http://check.ege.edu.ru',
          "name": result["sCookie"][0]["name"],
          "value": result["sCookie"][0]["value"]
        }, function(){
// Если есть, то устанавливаем их и отрисовываем таблицу
          createTable();
        });
      }else{
// А если нет, то открываем сайт check.ege.edu.ru в новой вкладке
        chrome.tabs.create({url : "http://check.ege.edu.ru"}); 
      }
    });
  }
});

function createTable(){
  var myInit = { 
      method: 'GET',
      credentials: 'include'};
  fetch('http://check.ege.edu.ru/api/exam', myInit).then(r => r.text()).then(result => {
// Получаем результаты и парсим их
    jQuery.parseJSON(result)['Result']['Exams'].forEach(function(element) {
      if (element['HasResult'] == false){
// Если результата еще нет, то выводим название предмета и надпись "не проверено"
        $("#mainTable").append('<tr><td>'+element['Subject']+'</td><td>Не проверено</td></tr>');
      }else{
// Если есть, то выводим название предмета и баллы
        $("#mainTable").append('<tr><td>'+element['Subject']+'</td><td>'+element['TestMark']+'</td></tr>');
      }
    });
  })
}


Итого, весь код занимает менее 200 строк. Надеюсь, что это расширение пригодится не только мне. В любом случае, во время разработки я получил опыт, который поможет мне в дальнейшем.
Вот ссылка на страницу расширения в chrome store

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


  1. kolu4iy
    09.06.2019 10:07
    +3

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


    1. kireevmp
      09.06.2019 10:36
      +3

      Знаете, и без расширения, мне кажется, на сайт каждую минуту стучится не только миллион, а вообще все школьники, написавшие ЕГЭ.


      1. Chaos_Theory
        09.06.2019 11:42

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


        1. kolu4iy
          09.06.2019 14:19
          +1

          Да, но школьник перестанет стучаться, как только увидит результат, и пойдет отмечать факт, а расширение — продолжит. Кто про него вспомнит?


  1. Koneru
    09.06.2019 10:48

    А почему вы используете Alarm, а не стандартный setInterval?


    1. Taraflex
      09.06.2019 11:25

      setInterval не сработает для фоновых страниц, которые браузеру позволительно выключать («persistent»: false — рекомендуемое состояние для фоновых страниц)

      developer.chrome.com/extensions/background_migration


  1. shok96
    09.06.2019 11:53
    +3

    а почему ресурс для проверки результатов егэ не имеет https?


  1. michael_kotor
    09.06.2019 16:07

    Там же капчу просит ввести. Как вы это обходите?


    1. algotrader2013
      09.06.2019 16:08

      Походу через куки

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


  1. AstorS1
    09.06.2019 22:14

    Я папа одиннадцатиклассницы, и более 12 суток нет результата даже первого экзамена. Завтра последний, волнуемся…


  1. OBIEESupport
    09.06.2019 23:13

    Интересно, уважаемый автор, зачем автоматизировать что-то, что человеку понадобится максимум 9 раз в жизни? Считаем — 4 экзамена в 9 классе, сочинение-допуск + 4 экзамена в 11 классе. И еще, заставка — 97 баллов по физике. У нас на 100 человек физику сдают трое, и как бы ее необходимость сильно преувеличена — в школьном учебнике даже электросхемы в картинках нарисованы.


    1. SbWereWolf
      10.06.2019 07:53

      потренировался человек, в чём беда?


  1. lxsmkv
    10.06.2019 05:37

    Что я хотел узнать из статьи? Как писать расширение для хрома на простом примере.
    Что я узнал из статьи? Кусок программной реализации конкретной задачи.
    Где исходники хотя бы?


    1. TimsTims
      10.06.2019 08:29

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


      1. lxsmkv
        10.06.2019 15:12

        «Делаем расширение для браузера» в заголовке, как бы намекает.


    1. lxsmkv
      10.06.2019 15:08

      Автор добавил недостающий материал. Спасибо.


  1. NickSolver
    10.06.2019 13:07

    Ничего себе какие грамотные школьники пошли)