Уведомления и календари
Обновлено 27.10.2026
Как узнавать об изменениях в расписании, не спрашивая API каждую минуту. Сверено со стендом d1 23 октября 2026 года; методы этой страницы есть и на production.
Что выбрать
| Задача | Чем решать |
|---|---|
| Расписание в Google Календаре, Apple Calendar, Outlook | Подписка на календарную ленту GET /calendar/schedule.ics |
| Экзамены и зачёты в календаре | GET /session.ics |
| Бот или сервис, который сообщает об изменениях | Опрос GET /schedule/changes раз в несколько минут |
| Полная копия расписания у себя | GET /schedule раз в час-другой, сравнение по updatedAt |
| Push в браузер | Только для самого приложения ВИШ — см. ниже |
Календарная лента
GET /calendar/schedule.ics отдаёт расписание в формате iCalendar с теми же фильтрами, что GET /schedule: group или groupId, teacher или teacherId, scope, dateFrom, dateTo, а ещё lessonIds и subjectIds (только эти занятия или предметы), locale (ru, en, zh) и attachment=1 (отдать файлом).
- Ключ не нужен: это то же расписание, что видит любой посетитель.
- Подпишитесь на адрес — календарь сам перечитывает его и подхватывает правки, отмены и удаления. У каждого занятия постоянный
UID, поэтому изменение обновляет событие, а не добавляет второе. - Время — с часовым поясом (
DTSTART;TZID=Europe/Moscow:…). - Не чаще 60 запросов в минуту с одного адреса.
https://d1.vish-app.ru/api/v1/calendar/schedule.ics?group=ШАД-111
GET /session.ics — то же для сессии: экзамены, зачёты и консультации группы или преподавателя.
Лента изменений
GET /schedule/changes — что недавно изменилось в расписании одной группы (groupId) или преподавателя (teacherId), новое сверху.
period=recent(по умолчанию) — последние изменения;limit— сколько.period=weeks— изменения занятий этой и следующей недели.- У записи:
lessonId,change(created,announced,updated,deleted),subject,room,startAt,endAt,changedFields,changes(поле, было, стало),origin(institute— правка в институте,source— новое расписание университета) иcreatedAt.
Как опрашивать: раз в 5–15 минут, запоминая id или createdAt последней обработанной записи, и обрабатывать только то, что новее. Ответ с period=weeks кэшируется на минуту — чаще спрашивать бессмысленно.
Push в браузере
POST /push/subscriptions и POST /push/unsubscribe подписывают браузер приложения ВИШ на уведомления об изменениях группы или преподавателя; публичный ключ VAPID для подписки — в GET /app-config (push.publicKey). Эти методы рассчитаны на само приложение ВИШ: подписку создаёт его service worker. Своему сайту или боту удобнее лента изменений.
Пример: лента и изменения
Пример скачивает календарную ленту группы, проверяет её формат и уникальность UID, а затем читает изменения этой и следующей недели. Группа — в VISH_GROUP, по умолчанию ШАД-111.
// The group's calendar feed and its recent changes; no key needed.
const API = process.env.VISH_API ?? 'https://d1.vish-app.ru/api/v1'
const GROUP = process.env.VISH_GROUP ?? 'ШАД-111'
const groups = await (await fetch(`${API}/groups?search=${encodeURIComponent(GROUP)}&all=1`)).json()
const group = groups.list.find((item) => item.name === GROUP)
if (!group) throw new Error(`group ${GROUP} not found`)
const feed = await fetch(`${API}/calendar/schedule.ics?group=${encodeURIComponent(GROUP)}`)
const calendar = await feed.text()
if (!feed.ok || !feed.headers.get('content-type')?.startsWith('text/calendar') || !calendar.startsWith('BEGIN:VCALENDAR')) {
throw new Error(`not a calendar: HTTP ${feed.status}`)
}
const uids = [...calendar.matchAll(/^UID:(.+)$/gmu)].map((match) => match[1].trim())
if (new Set(uids).size !== uids.length) throw new Error('duplicate UIDs')
const response = await fetch(`${API}/schedule/changes?groupId=${group.id}&period=weeks`)
if (!response.ok) throw new Error(`changes: HTTP ${response.status}`)
const changes = (await response.json()).list
if (!Array.isArray(changes) || changes.some((item) => !item.lessonId || !item.change || !item.createdAt)) {
throw new Error('unexpected changes answer')
}
console.log(`${GROUP}: ${uids.length} events in the feed, ${changes.length} changes this week and next`)
for (const item of changes.slice(0, 5)) {
console.log(`- ${item.createdAt.slice(0, 16)} ${item.change}: ${item.subject?.ru ?? item.lessonId} (${item.changedFields?.join(', ') ?? ''})`)
}Попробовать
Текущая неделя, сетка звонков и публичный ключ для push:
Попробовать запрос
Ключ нужен только методам с замком. Он живёт только на этой странице: не сохраняется, уходит лишь заголовком этого запроса и забывается, когда вы её покинете. Где взять ключ
Проверено: 2026-10, PWA aa337c45, platform bff65fce.