
Я, как и любой другой выпускник, переживаю по поводу экзаменов. От баллов, полученных на ЕГЭ зависит слишком многое, поэтому сейчас трудно думать о чем то другом. Чтобы не обновлять сайт 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