Files
evening_detective_server/api/main.proto
T
2026-10-11 02:23:22 +07:00

2348 lines
68 KiB
Protocol Buffer
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
syntax = "proto3";
// Contract rules:
// - fields inside each message are declared in ascending number order;
// - published field numbers are never renumbered or reused;
// - use ID, <name>ID and URL for abbreviation fields so generated clients
// keep the same naming convention across Go and TypeScript.
package crabs.evening_detective_server;
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "google/api/httpbody.proto";
option go_package = "pkg/proto";
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title : "Документация сервиса \"Вечерний детектив\"";
version: "v0.1.0";
};
security_definitions: {
security: {
key : "BearerAuth";
value: {
type : TYPE_API_KEY;
in : IN_HEADER;
name : "Authorization";
description: "Authentication token, prefixed by Bearer: Bearer <token>";
}
}
}
security: {
security_requirement: {
key : "BearerAuth";
value: {};
}
}
};
service EveningDetectiveServer {
rpc GetTasks(GetTasksReq) returns (GetTasksRsp) {
option (google.api.http) = { get: "/api/tasks" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Задачи";
summary : "Получить общую доску задач";
description: "Доступно разработчикам и администратору.";
};
}
rpc GetTaskAssignees(GetTaskAssigneesReq) returns (GetTaskAssigneesRsp) {
option (google.api.http) = { get: "/api/tasks/assignees" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Задачи";
summary : "Получить разработчиков для назначения";
description: "Доступно разработчикам и администратору.";
};
}
rpc CreateTask(CreateTaskReq) returns (TaskRsp) {
option (google.api.http) = { post: "/api/tasks" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Задачи";
summary : "Создать задачу";
description: "Доступно разработчикам и администратору.";
};
}
rpc UpdateTask(UpdateTaskReq) returns (TaskRsp) {
option (google.api.http) = { put: "/api/tasks/{ID}" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Задачи";
summary : "Редактировать задачу";
description: "Доступно разработчикам и администратору.";
};
}
rpc SetTaskStatus(SetTaskStatusReq) returns (TaskRsp) {
option (google.api.http) = { post: "/api/tasks/{ID}/status" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Задачи";
summary : "Изменить статус задачи";
description: "Доступно разработчикам и администратору.";
};
}
rpc DeleteTask(DeleteTaskReq) returns (DeleteTaskRsp) {
option (google.api.http) = { post: "/api/tasks/{ID}/delete" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Задачи";
summary : "Удалить задачу";
description: "Доступно разработчикам и администратору.";
};
}
rpc GetParticipation(GetParticipationReq) returns (ParticipationRsp) {
option (google.api.http) = { post: "/api/participation/get" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security : {};
tags : "Подтверждение участия";
summary : "Получить состояние участия";
description: "Возвращает команду, игру и текущее решение по персональному токену из письма. Авторизация JWT не требуется. Чтение не меняет статус участия. Токен передаётся в теле запроса. Недействительная ссылка и ошибки обработки возвращаются стандартным gRPC status error.";
};
}
rpc AnswerParticipation(AnswerParticipationReq) returns (ParticipationRsp) {
option (google.api.http) = { post: "/api/participation/answer" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security : {};
tags : "Подтверждение участия";
summary : "Подтвердить или отменить участие";
description: "Принимает окончательный ответ капитана: confirmed или cancelled. Авторизация JWT не требуется; доступ определяется персональным токеном. Первый ответ сохраняется, повтор того же ответа успешен, противоположный ответ отклоняется. Новый ответ принимается только до планового начала игры и пока игра имеет статус draft. Ошибки ссылки, значения answer, закрытого приёма или изменения решения возвращаются стандартным gRPC status error; при уже принятом решении возвращается его текущее состояние.";
};
}
rpc Ping(PingReq) returns (PingRsp) {
option (google.api.http) = {
get: "/api/test/ping"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
summary: "Проверить доступность";
};
}
rpc Echo(EchoReq) returns (EchoRsp) {
option (google.api.http) = {
post: "/api/test/echo"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
summary: "Проверить обработку данных";
};
}
rpc Signup(SignupReq) returns (SignupRsp) {
option (google.api.http) = {
post: "/api/auth/signup"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Регистрация и вход";
summary: "Зарегистрироваться";
};
}
rpc RefreshPassword(RefreshPasswordReq) returns (RefreshPasswordRsp) {
option (google.api.http) = {
post: "/api/auth/refresh-password"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Регистрация и вход";
summary: "Обновить пароль";
};
}
rpc Login(LoginReq) returns (LoginRsp) {
option (google.api.http) = {
post: "/api/auth/login"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Регистрация и вход";
summary: "Войти и получить токена доступа";
};
}
rpc Refresh(RefreshReq) returns (RefreshRsp) {
option (google.api.http) = {
post: "/api/auth/refresh"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Регистрация и вход";
summary: "Обновить токена доступа";
};
}
rpc DeleteAccount(DeleteAccountReq) returns (DeleteAccountRsp) {
option (google.api.http) = {
delete: "/api/auth/delete-account"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Регистрация и вход";
summary : "Удалить учётную запись и персональные данные";
description: "Удаляет учётную запись и персональные данные текущего пользователя (ст. 21 152-ФЗ). Требует заголовок X-Password с паролем учётной записи (подтверждение владельца; пароль не передаётся в URL).";
};
}
rpc GetTerms(GetTermsReq) returns (GetTermsRsp) {
option (google.api.http) = {
get: "/api/terms"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Документы";
summary: "Получить текст Пользовательского соглашения (оферты)";
};
}
rpc GetPrivacy(GetPrivacyReq) returns (GetPrivacyRsp) {
option (google.api.http) = {
get: "/api/privacy"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Документы";
summary: "Получить текст Политики конфиденциальности";
};
}
rpc GetConsent(GetConsentReq) returns (GetConsentRsp) {
option (google.api.http) = {
get: "/api/consent"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Документы";
summary: "Получить текст Согласия на обработку персональных данных";
};
}
rpc GetUsers(GetUsersReq) returns (GetUsersRsp) {
option (google.api.http) = {
get: "/api/users"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Пользователи";
summary: "Получить всех пользователей";
};
}
rpc GetUserByID(GetUserByIDReq) returns (GetUserByIDRsp) {
option (google.api.http) = {
get: "/api/users/{ID}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Пользователи";
summary: "Получить пользователя по id";
};
}
rpc GetMe(GetMeReq) returns (GetMeRsp) {
option (google.api.http) = {
get: "/api/users/me"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Пользователи";
summary: "Получить информацию о себе";
};
}
// Только администратор; сеансы пользователя сохраняются.
// Ошибки: 401, 403, 400, 404, 500.
rpc ChangeUserPassword(ChangeUserPasswordReq) returns (ChangeUserPasswordRsp) {
option (google.api.http) = {
post: "/api/users/{ID}/password"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Пользователи";
summary : "Изменить чужой пароль (только администратор)";
description: "Пароль вводится вручную: минимум 12 символов, максимум 72 байта. Сеансы сохраняются. Ошибки: 401 — нет авторизации, 403 — нет прав, 400 — неверные данные или собственный ID, 404 — пользователь не найден, 500 — ошибка сохранения.";
};
}
rpc AddUserRole(AddUserRoleReq) returns (AddUserRoleRsp) {
option (google.api.http) = {
post: "/api/users/{ID}/role/add"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Пользователи";
summary: "Добавить пользователю роль";
};
}
rpc DeleteUserRole(DeleteUserRoleReq) returns (DeleteUserRoleRsp) {
option (google.api.http) = {
post: "/api/users/{ID}/role/delete"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Пользователи";
summary: "Забрать у пользователя роль";
};
}
rpc GetPermissions(GetPermissionsReq) returns (GetPermissionsRsp) {
option (google.api.http) = {
get: "/api/ui/permissions"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Доступы";
summary: "Получить список доступных элементов интерфейса";
};
}
rpc UploadFile(UploadFileReq) returns (UploadFileRsp) {
option (google.api.http) = {
post: "/api/files/upload"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Файлы";
summary: "Загрузить файл";
};
}
rpc DownloadFile(DownloadFileReq) returns (google.api.HttpBody) {
option (google.api.http) = {
get: "/api/files/{filename}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Файлы";
summary: "Получить файл";
};
}
rpc GetPlaylists(GetPlaylistsReq) returns (GetPlaylistsRsp) {
option (google.api.http) = { get: "/api/playlists" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Музыка";
summary: "Получить плейлисты";
};
}
rpc CreatePlaylist(CreatePlaylistReq) returns (CreatePlaylistRsp) {
option (google.api.http) = { post: "/api/playlists" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Музыка";
summary: "Создать плейлист";
};
}
rpc UpdatePlaylist(UpdatePlaylistReq) returns (UpdatePlaylistRsp) {
option (google.api.http) = { put: "/api/playlists/{ID}" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Музыка";
summary: "Изменить плейлист";
};
}
rpc DeletePlaylist(DeletePlaylistReq) returns (DeletePlaylistRsp) {
option (google.api.http) = { delete: "/api/playlists/{ID}" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Музыка";
summary: "Удалить плейлист";
};
}
rpc UploadPlaylistTrack(UploadPlaylistTrackReq) returns (UploadPlaylistTrackRsp) {
option (google.api.http) = { post: "/api/playlists/{playlistID}/tracks" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Музыка";
summary: "Загрузить MP3 в плейлист";
};
}
rpc DeletePlaylistTrack(DeletePlaylistTrackReq) returns (DeletePlaylistTrackRsp) {
option (google.api.http) = { delete: "/api/playlists/{playlistID}/tracks/{trackID}" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Музыка";
summary: "Удалить MP3 из плейлиста";
};
}
rpc SetScenarioPlaylist(SetScenarioPlaylistReq) returns (SetScenarioPlaylistRsp) {
option (google.api.http) = { put: "/api/scenarios/{ID}/playlist" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Музыка";
summary: "Назначить плейлист сценарию";
};
}
rpc AddScenario(AddScenarioReq) returns (AddScenarioRsp) {
option (google.api.http) = {
post: "/api/scenario"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Создать сценарий";
};
}
rpc GetMyScenarios(GetMyScenariosReq) returns (GetMyScenariosRsp) {
option (google.api.http) = {
get: "/api/my-scenarios"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Получить свои сценарии";
};
}
rpc GetScenariosCatalog(GetScenariosCatalogReq) returns (GetScenariosCatalogRsp) {
option (google.api.http) = {
get: "/api/catalog"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Получить каталог сценариев";
};
}
rpc GetFullScenario(GetScenarioReq) returns (GetScenarioRsp) {
option (google.api.http) = {
get: "/api/scenarios/{ID}/full"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Получить сценарий со всеми деталями по id";
};
}
rpc GetScenario(GetScenarioReq) returns (GetScenarioRsp) {
option (google.api.http) = {
get: "/api/scenarios/{ID}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Получить сценарий по id";
};
}
rpc UpdateScenario(UpdateScenarioReq) returns (UpdateScenarioRsp) {
option (google.api.http) = {
put : "/api/scenarios/{ID}"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Редактировать сценарий";
};
}
rpc PublicScenario(PublicScenarioReq) returns (PublicScenarioRsp) {
option (google.api.http) = {
put : "/api/scenarios/{ID}/public"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Опубликовать сценарий";
};
}
rpc DraftScenario(DraftScenarioReq) returns (DraftScenarioRsp) {
option (google.api.http) = {
put : "/api/scenarios/{ID}/draft"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Снять с публикации сценарий";
};
}
rpc DeleteScenario(DeleteScenarioReq) returns (DeleteScenarioRsp) {
option (google.api.http) = {
delete: "/api/scenarios/{ID}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Удалить сценарий";
};
}
rpc AddScenarioPlace(AddScenarioPlaceReq) returns (AddScenarioPlaceRsp) {
option (google.api.http) = {
post: "/api/scenarios/{ID}/places"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Точки сценария";
summary: "Создать точку сценария";
};
}
rpc UpdateScenarioPlace(UpdateScenarioPlaceReq) returns (UpdateScenarioPlaceRsp) {
option (google.api.http) = {
put : "/api/scenarios/{ID}/places/{code}"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Точки сценария";
summary: "Обновить точку сценария";
};
}
rpc DeleteScenarioPlace(DeleteScenarioPlaceReq) returns (DeleteScenarioPlaceRsp) {
option (google.api.http) = {
delete: "/api/scenarios/{ID}/places/{code}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Точки сценария";
summary: "Удалить точку сценария";
};
}
rpc AddScenarioQuestion(AddScenarioQuestionReq) returns (AddScenarioQuestionRsp) {
option (google.api.http) = {
post: "/api/scenarios/{ID}/questions"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Вопросы сценария";
summary: "Создать вопрос дела";
};
}
rpc UpdateScenarioQuestion(UpdateScenarioQuestionReq) returns (UpdateScenarioQuestionRsp) {
option (google.api.http) = {
put : "/api/scenarios/{ID}/questions/{code}"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Вопросы сценария";
summary: "Обновить вопрос дела";
};
}
rpc DeleteScenarioQuestion(DeleteScenarioQuestionReq) returns (DeleteScenarioQuestionRsp) {
option (google.api.http) = {
delete: "/api/scenarios/{ID}/questions/{code}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Вопросы сценария";
summary: "Удалить вопрос дела";
};
}
// Загрузить файл через UploadFile, затем передать его имя; пустое имя удаляет фон дела.
rpc UpdateScenarioBackground(UpdateScenarioBackgroundReq) returns (UpdateScenarioBackgroundRsp) {
option (google.api.http) = {
put : "/api/scenarios/{ID}/background"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Установить или удалить фон дела";
};
}
rpc UpdateScenarioIntro(UpdateScenarioIntroReq) returns (UpdateScenarioIntroRsp) {
option (google.api.http) = {
put : "/api/scenarios/{ID}/introduction"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Обновить введение сценария";
};
}
// Видео загружается через UploadFile (MP4/WebM). Пустая conclusion удаляет закрывашку.
rpc UpdateScenarioConclusion(UpdateScenarioConclusionReq) returns (UpdateScenarioConclusionRsp) {
option (google.api.http) = {
put : "/api/scenarios/{ID}/conclusion"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Обновить закрывашку сценария (игрокам не передаётся)";
};
}
rpc DownloadScenarioArchive(DownloadScenarioArchiveReq) returns (google.api.HttpBody) {
option (google.api.http) = {
get: "/api/scenarios/{ID}/archive"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Скачать сценарий архивом (со всеми материалами и картинками)";
};
}
rpc UploadScenarioArchive(google.api.HttpBody) returns (UploadScenarioArchiveRsp) {
option (google.api.http) = {
post: "/api/scenarios/archive"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Сценарии";
summary: "Создать сценарий из архива";
};
}
rpc AddGame(AddGameReq) returns (AddGameRsp) {
option (google.api.http) = {
post: "/api/games"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Создать игру";
};
}
rpc GetGames(GetGamesReq) returns (GetGamesRsp) {
option (google.api.http) = {
get: "/api/games"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Получить игры";
};
}
rpc GetGame(GetGameReq) returns (GetGameRsp) {
option (google.api.http) = {
get: "/api/games/{ID}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Получить игру";
};
}
// HTTP uploads are streamed by a dedicated handler, bypassing gRPC message limits.
rpc ImportGame(google.api.HttpBody) returns (GameImportRsp) {
option (google.api.http) = {
post: "/api/games/import"
body: "*"
};
}
rpc StartGameExport(StartGameExportReq) returns (GameExportRsp) {
option (google.api.http) = {
post: "/api/games/{ID}/export"
body: "*"
};
}
rpc GetGameExport(GetGameExportReq) returns (GameExportRsp) {
option (google.api.http) = {get: "/api/games/{ID}/export"};
}
rpc IssueGameTickets(IssueGameTicketsReq) returns (IssueGameTicketsRsp) {
option (google.api.http) = {
post: "/api/games/{gameID}/tickets"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: { security_requirement: {} }
tags : "Билеты";
summary : "Выпустить билеты на игру";
};
}
rpc GetTicket(GetTicketReq) returns (GetTicketRsp) {
option (google.api.http) = {
get: "/api/tickets/{token}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: { security_requirement: {} }
tags : "Билеты";
summary : "Получить данные билета";
};
}
rpc AcceptTicket(AcceptTicketReq) returns (AcceptTicketRsp) {
option (google.api.http) = {
post: "/api/tickets/{token}/accept"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Билеты";
summary: "Принять билет";
};
}
// Создать сессию экрана. Экран получает токен и показывает QR для привязки.
rpc CreateScreenSession(CreateScreenSessionReq) returns (CreateScreenSessionRsp) {
option (google.api.http) = {
post: "/api/screens"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Экраны";
summary: "Создать сессию экрана";
};
}
// Получить состояние экрана по его токену. Вызывается самим экраном.
rpc GetScreenState(GetScreenStateReq) returns (GetScreenStateRsp) {
option (google.api.http) = {
post: "/api/screens/state"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Экраны";
summary: "Получить состояние экрана";
};
}
rpc GetGameScreens(GetGameScreensReq) returns (GetGameScreensRsp) {
option (google.api.http) = {
get: "/api/games/{gameID}/screens"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Получить экраны игры";
};
}
rpc GetGameMusic(GetGameMusicReq) returns (GetGameMusicRsp) {
option (google.api.http) = { get: "/api/games/{gameID}/music" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Музыка";
summary: "Получить музыку игры";
};
}
rpc SetGameMusic(SetGameMusicReq) returns (SetGameMusicRsp) {
option (google.api.http) = { post: "/api/games/{gameID}/music" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Музыка";
summary: "Изменить музыку игры";
};
}
rpc AdvanceScreenMusic(AdvanceScreenMusicReq) returns (AdvanceScreenMusicRsp) {
option (google.api.http) = { post: "/api/screens/music/advance" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: { security_requirement: {} }
tags : "Музыка";
summary : "Перейти к следующему треку экрана";
};
}
// Привязать экран к игре. Требует авторизацию организатора, автора или администратора.
rpc BindScreen(BindScreenReq) returns (BindScreenRsp) {
option (google.api.http) = {
post: "/api/screens/{screenID}/bind"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Привязать экран к игре";
};
}
// Отвязать экран от игры. Требует авторизацию организатора, автора или администратора.
rpc UnbindScreen(UnbindScreenReq) returns (UnbindScreenRsp) {
option (google.api.http) = {
post: "/api/screens/{screenID}/unbind"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Отвязать экран от игры";
};
}
rpc SetScreenSound(SetScreenSoundReq) returns (SetScreenSoundRsp) {
option (google.api.http) = {
post: "/api/screens/{screenID}/sound"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Назначить экран со звуком";
};
}
rpc GetGameMap(GetGameMapReq) returns (GetGameMapRsp) {
option (google.api.http) = { get: "/api/games/{gameID}/map" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Получить карту игры";
};
}
rpc UpdateGameMapItem(UpdateGameMapItemReq) returns (UpdateGameMapItemRsp) {
option (google.api.http) = {
post: "/api/games/{gameID}/map/items/{kind}/{itemID}"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Изменить объект карты игры";
};
}
// Изменить общий режим показа всех экранов игры.
rpc SetScreenMode(SetScreenModeReq) returns (SetScreenModeRsp) {
option (google.api.http) = {
post: "/api/games/{gameID}/screens/mode"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Изменить режим показа экранов";
};
}
rpc SetScreenTimer(SetScreenTimerReq) returns (SetScreenTimerRsp) {
option (google.api.http) = {
post: "/api/games/{gameID}/screens/timer"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Настроить таймер экранов";
};
}
// Отправить общую команду экранам игры.
rpc SendScreenCommand(SendScreenCommandReq) returns (SendScreenCommandRsp) {
option (google.api.http) = {
post: "/api/games/{gameID}/screens/command"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Отправить команду экранам";
};
}
rpc GetScreenPlan(GetScreenPlanReq) returns (GetScreenPlanRsp) {
option (google.api.http) = { get: "/api/games/{gameID}/screen-plan" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Получить план экранов";
};
}
rpc SaveScreenPlan(SaveScreenPlanReq) returns (SaveScreenPlanRsp) {
option (google.api.http) = {
put : "/api/games/{gameID}/screen-plan"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Сохранить план экранов";
};
}
rpc SetScreenPlanStep(SetScreenPlanStepReq) returns (SetScreenPlanStepRsp) {
option (google.api.http) = {
post: "/api/games/{gameID}/screen-plan/step"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Перейти на шаг плана экранов";
};
}
rpc GetScreenResults(GetScreenResultsReq) returns (GetScreenResultsRsp) {
option (google.api.http) = { post: "/api/screens/results" body: "*" };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Экраны";
summary: "Получить результаты для экрана";
};
}
rpc DownloadTeamQR(GetGameReq) returns (google.api.HttpBody) {
option (google.api.http) = {
get: "/api/games/{ID}/teams/qr.pdf"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Скачать PDF с QR-кодами всех команд игры";
};
}
rpc GetGameAnswers(GetGameAnswersReq) returns (GetGameAnswersRsp) {
option (google.api.http) = {
get: "/api/games/{gameID}/answers"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Получить ответы команд на вопросы дела (для оценки организатором)";
};
}
rpc GetGameResults(GetGameResultsReq) returns (GetGameResultsRsp) {
option (google.api.http) = {
get: "/api/games/{gameID}/results"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Получить итоговую таблицу игры (после завершения)";
};
}
rpc CreateGameReview(CreateGameReviewReq) returns (CreateGameReviewRsp) {
option (google.api.http) = {
post: "/api/games/{gameID}/reviews"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: { security_requirement: {} }
tags : "Отзывы"
summary : "Оставить отзыв об игре"
};
}
rpc GetGameReviews(GetGameReviewsReq) returns (GetGameReviewsRsp) {
option (google.api.http) = {
get: "/api/games/{gameID}/reviews"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: { security_requirement: {} }
tags : "Отзывы"
summary : "Получить отзывы об игре"
};
}
rpc GetScenarioReviews(GetScenarioReviewsReq) returns (GetGameReviewsRsp) {
option (google.api.http) = {
get: "/api/scenarios/{scenarioID}/reviews"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: { security_requirement: {} }
tags : "Отзывы"
summary : "Получить отзывы сценария"
};
}
rpc GetGameReviewsForModeration(GetGameReviewsReq) returns (GetGameReviewsRsp) {
option (google.api.http) = {
get: "/api/admin/games/{gameID}/reviews"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Отзывы"
summary: "Получить отзывы игры для модерации"
};
}
rpc GetReviewsForModeration(GetReviewsForModerationReq) returns (GetGameReviewsRsp) {
option (google.api.http) = {
get: "/api/admin/game-reviews"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Отзывы"
summary: "Получить общую очередь отзывов"
};
}
rpc ModerateGameReview(ModerateGameReviewReq) returns (ModerateGameReviewRsp) {
option (google.api.http) = {
put : "/api/admin/game-reviews/{ID}"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Отзывы"
summary: "Модерировать отзыв"
};
}
rpc UpdateGame(UpdateGameReq) returns (UpdateGameRsp) {
option (google.api.http) = {
put : "/api/games/{ID}"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Обновить игру";
};
}
rpc StartGame(StartGameReq) returns (StartGameRsp) {
option (google.api.http) = {
put : "/api/games/{ID}/start"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Начать игру";
};
}
rpc PauseGame(PauseGameReq) returns (PauseGameRsp) {
option (google.api.http) = {
put : "/api/games/{ID}/pause"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Остановить игру";
};
}
rpc PlayGame(PlayGameReq) returns (PlayGameRsp) {
option (google.api.http) = {
put : "/api/games/{ID}/play"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Продолжить игру";
};
}
rpc FinishGame(FinishGameReq) returns (FinishGameRsp) {
option (google.api.http) = {
put : "/api/games/{ID}/finish"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Закончить игру";
};
}
rpc ResetGame(ResetGameReq) returns (ResetGameRsp) {
option (google.api.http) = {
put : "/api/games/{ID}/reset"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Сбросить игру";
};
}
rpc DeleteGame(DeleteGameReq) returns (DeleteGameRsp) {
option (google.api.http) = {
delete: "/api/games/{ID}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Удалить игру";
};
}
rpc AddTeam(AddTeamReq) returns (AddTeamRsp) {
option (google.api.http) = {
post: "/api/teams",
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Команда";
summary: "Создать команду";
};
}
rpc UpdateTeam(UpdateTeamReq) returns (UpdateTeamRsp) {
option (google.api.http) = {
put : "/api/teams/{ID}",
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Команда";
summary: "Обновить команду";
};
}
rpc DeleteTeam(DeleteTeamReq) returns (DeleteTeamRsp) {
option (google.api.http) = {
delete: "/api/teams/{ID}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Команда";
summary: "Удалить команду";
};
}
rpc GiveTeamApplications(GiveTeamApplicationsReq) returns (GiveTeamApplicationsRsp) {
option (google.api.http) = {
post: "/api/teams/{ID}/applications",
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Команда";
summary: "Выдать улику";
};
}
rpc GetTeamStory(GetTeamStoryReq) returns (GetTeamStoryRsp) {
option (google.api.http) = {
get: "/api/teams/{ID}/story"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Ходы";
summary: "Получить ходы";
};
}
rpc AddTeamAction(AddTeamActionReq) returns (AddTeamActionRsp) {
option (google.api.http) = {
post: "/api/teams/{ID}/actions",
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Ходы";
summary: "Сделать ход";
};
}
rpc DeleteLastTeamAction(DeleteLastTeamActionReq) returns (DeleteLastTeamActionRsp) {
option (google.api.http) = {
delete: "/api/teams/{ID}/last-actions"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Ходы";
summary: "Удалить последний ход";
};
}
rpc GetTeamAnswers(GetTeamAnswersReq) returns (GetTeamAnswersRsp) {
option (google.api.http) = {
get: "/api/teams/{ID}/answers"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Ходы";
summary: "Получить ответы команды на вопросы дела";
};
}
rpc SubmitTeamAnswers(SubmitTeamAnswersReq) returns (SubmitTeamAnswersRsp) {
option (google.api.http) = {
post: "/api/teams/{ID}/answers"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {}
}
tags : "Ходы";
summary: "Отправить ответы команды на вопросы дела";
};
}
rpc SetTeamAnswerScore(SetTeamAnswerScoreReq) returns (SetTeamAnswerScoreRsp) {
option (google.api.http) = {
put : "/api/teams/{ID}/answers/{questionCode}/score"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags : "Игры";
summary: "Выставить балл команде за ответ на вопрос дела";
};
}
}
message PingReq {}
message PingRsp {}
message EchoReq {
string text = 1;
}
message EchoRsp {
string text = 1;
}
message SignupReq {
string username = 1;
string email = 2;
bool accept_terms = 3;
bool accept_privacy = 4;
}
message SignupRsp {
}
message RefreshPasswordReq {
string email = 1;
}
message RefreshPasswordRsp {
}
message LoginReq {
string email = 1;
string password = 2;
}
message LoginRsp {
string accessToken = 1;
string refreshToken = 2;
}
message RefreshReq {
string refreshToken = 1;
}
message RefreshRsp {
string accessToken = 1;
string refreshToken = 2;
}
// Пароль подтверждает, что удаляет аккаунт его владелец, а не обладатель
// украденного access-токена (ст. 21 152-ФЗ). Передаётся HTTP-заголовком
// X-Password (пробрасывается матчером в cmd/evening_detective_server/main.go),
// чтобы креденшел не попадал в URL/query-параметры и логи.
message DeleteAccountReq {}
message DeleteAccountRsp {
}
// Документы раздаются по постоянным URL, на которые ссылается сервис при
// фиксации акцептов соглашений (user_agreements) и форма регистрации.
message GetTermsReq {}
message GetTermsRsp {
string text = 1;
}
message GetPrivacyReq {}
message GetPrivacyRsp {
string text = 1;
}
message GetConsentReq {}
message GetConsentRsp {
string text = 1;
}
message GetUsersReq {}
message GetUsersRsp {
repeated User users = 1;
}
message User {
int32 ID = 1;
string username = 2;
string email = 3;
repeated string roles = 4;
bool is_active = 5;
google.protobuf.Timestamp created_at = 6;
}
message GetUserByIDReq {
int32 ID = 1;
}
message GetUserByIDRsp {
User user = 1;
}
message GetMeReq {}
message GetMeRsp {
User user = 1;
}
message ChangeUserPasswordReq {
int32 ID = 1;
string new_password = 2;
}
message ChangeUserPasswordRsp {}
message AddUserRoleReq {
int32 ID = 1;
string role = 2;
}
message AddUserRoleRsp {
}
message DeleteUserRoleReq {
int32 ID = 1;
string role = 2;
}
message DeleteUserRoleRsp {
}
message GetPermissionsReq {}
message GetPermissionsRsp {
repeated string permissions = 1;
// Actual server environment: prod or venue.
string environment = 2;
}
message UploadFileReq {
string filename = 1;
bytes data = 2;
}
message UploadFileRsp {
string filename = 1;
// Тип загруженного файла: image | pdf | audio | video (определяется сервером по
// содержимому). Удобен редактору для предзаполнения file_type улики.
string file_type = 2 [json_name = "file_type"];
}
message DownloadFileReq {
string filename = 1;
}
message PlaylistTrack {
int32 ID = 1;
string name = 2;
string filename = 3;
int32 position = 4;
}
message Playlist {
int32 ID = 1;
string name = 2;
repeated PlaylistTrack tracks = 3;
}
message GetPlaylistsReq {}
message GetPlaylistsRsp {
repeated Playlist playlists = 1;
}
message CreatePlaylistReq {
string name = 1;
}
message CreatePlaylistRsp {
Playlist playlist = 1;
}
message UpdatePlaylistReq {
int32 ID = 1;
string name = 2;
}
message UpdatePlaylistRsp {
Playlist playlist = 1;
}
message DeletePlaylistReq {
int32 ID = 1;
}
message DeletePlaylistRsp {}
message UploadPlaylistTrackReq {
int32 playlistID = 1;
string name = 2;
string filename = 3;
bytes data = 4;
}
message UploadPlaylistTrackRsp {
Playlist playlist = 1;
}
message DeletePlaylistTrackReq {
int32 playlistID = 1;
int32 trackID = 2;
}
message DeletePlaylistTrackRsp {}
message SetScenarioPlaylistReq {
int32 ID = 1;
int32 playlistID = 2;
}
message SetScenarioPlaylistRsp {}
message AddScenarioReq {
string name = 1;
}
message AddScenarioRsp {
int32 ID = 1;
}
message GetMyScenariosReq {}
message GetMyScenariosRsp {
repeated Scenario scenarios = 1;
}
message GetScenariosCatalogReq {}
message GetScenariosCatalogRsp {
repeated Scenario scenarios = 1;
}
message GetScenarioReq {
int32 ID = 1;
}
message GetScenarioRsp {
Scenario scenario = 1;
}
message Scenario {
int32 ID = 1;
string name = 2;
optional string description = 3;
optional string image = 4;
Story story = 5;
string status = 6;
User author = 7;
google.protobuf.Timestamp updated_at = 8;
google.protobuf.Timestamp created_at = 9;
google.protobuf.Timestamp published_at = 10;
double average_rating = 11;
int32 reviews_count = 12;
string difficulty = 13;
optional int32 music_playlist_id = 14 [json_name = "music_playlist_id"];
}
message Story {
repeated Place places = 1;
Introduction introduction = 2;
repeated Question questions = 3;
string background = 4;
Conclusion conclusion = 5; // Только в полном сценарии, игрокам не передаётся.
}
message Conclusion {
string text = 1;
string video = 2;
}
message Introduction {
string text = 1;
string audio = 2;
}
message Place {
string code = 1;
string name = 2;
string text = 3;
string image = 4;
bool hidden = 5;
repeated Application applications = 6;
repeated Door doors = 7;
repeated Key keys = 8;
}
message Application {
string name = 1;
string image = 2;
// Тип файла улики: image | pdf | audio. Значение выводится сервером из
// расширения файла (см. file_storage.FileType); пустое значение возможно
// только для legacy-строк без расширения.
string file_type = 3 [json_name = "file_type"];
}
message Door {
string code = 1;
string name = 2;
repeated Key keys = 3;
}
message Key {
string name = 1;
}
// Вопрос дела - вопрос, на который команда должна ответить в конце игры.
// Правильный ответ (answer) виден только автору сценария и организаторам
// (при оценке ответов команд), команде не передаётся.
message Question {
// Код вопроса - используется при отправке ответов командой
string code = 1;
// Текст вопроса
string text = 2;
// Правильный ответ (справка для организатора при оценке)
string answer = 3;
}
// Ответ команды на вопрос дела
message Answer {
// Код вопроса
string questionCode = 1;
// Текст ответа команды
string answer = 2;
}
// Ответ команды с баллом (для панели организатора)
message AnswerScore {
string questionCode = 1;
string answer = 2;
// Балл, выставленный организатором; отсутствует, если ещё не оценено
optional int32 score = 3;
}
// Ответы одной команды на вопросы дела
message TeamAnswers {
Team team = 1;
repeated AnswerScore answers = 2;
}
message UpdateScenarioReq {
int32 ID = 1;
string name = 2;
string description = 3;
string image = 4;
}
message UpdateScenarioRsp {
}
message PublicScenarioReq {
int32 ID = 1;
}
message PublicScenarioRsp {
}
message DraftScenarioReq {
int32 ID = 1;
}
message DraftScenarioRsp {
}
message DeleteScenarioReq {
int32 ID = 1;
}
message DeleteScenarioRsp {
}
message AddScenarioPlaceReq {
int32 ID = 1;
Place place = 2;
}
message AddScenarioPlaceRsp {
}
message UpdateScenarioPlaceReq {
int32 ID = 1;
string code = 2;
Place place = 3;
}
message UpdateScenarioPlaceRsp {
}
message DeleteScenarioPlaceReq {
int32 ID = 1;
string code = 2;
}
message DeleteScenarioPlaceRsp {
}
message UpdateScenarioConclusionReq {
int32 ID = 1;
Conclusion conclusion = 2;
}
message UpdateScenarioConclusionRsp {
}
message UpdateScenarioIntroReq {
int32 ID = 1;
Introduction introduction = 2;
}
message UpdateScenarioIntroRsp {
}
message AddScenarioQuestionReq {
int32 ID = 1;
Question question = 2;
}
message AddScenarioQuestionRsp {
}
message UpdateScenarioQuestionReq {
int32 ID = 1;
string code = 2;
Question question = 3;
}
message UpdateScenarioQuestionRsp {
}
message DeleteScenarioQuestionReq {
int32 ID = 1;
string code = 2;
}
message DeleteScenarioQuestionRsp {
}
message DownloadScenarioArchiveReq {
int32 ID = 1;
}
message UploadScenarioArchiveRsp {
int32 ID = 1;
}
message AddGameReq {
string name = 1;
string description = 2;
google.protobuf.Timestamp start_at = 3;
int32 scenarioID = 4;
string format = 5;
string location = 6;
}
message AddGameRsp {
int32 ID = 1;
}
message GetGamesReq {}
message GetGamesRsp {
repeated Game games = 1;
}
message Game {
int32 ID = 1;
string name = 2;
string description = 3;
google.protobuf.Timestamp start_at = 4;
string status = 5;
Scenario scenario = 6;
repeated Team teams = 7;
optional google.protobuf.Timestamp started_at = 8;
optional google.protobuf.Timestamp ended_at = 9;
string format = 10;
string location = 11;
GameExport export = 12;
}
// Download ready ZIP through the authenticated streaming HTTP route:
// GET /api/games/{ID}/export/download (not an HttpBody RPC).
message GameExport {
string ID = 1;
string state = 2; // preparing, ready or failed; preparing/ready lock game writes.
string packageID = 3;
int64 size = 4;
string sha256 = 5;
string failure = 6;
}
message GameImportRsp {
int32 gameID = 1;
string packageID = 2;
bool repeated = 3;
}
message StartGameExportReq { int32 ID = 1; }
message GetGameExportReq { int32 ID = 1; }
message GameExportRsp { GameExport export = 1; }
enum ScreenMode {
SCREEN_MODE_UNSPECIFIED = 0;
SCREEN_MODE_COVER = 1;
SCREEN_MODE_INTRO = 2;
SCREEN_MODE_INTRO_DONE = 3;
SCREEN_MODE_CONCLUSION = 4;
SCREEN_MODE_CONCLUSION_DONE = 5;
SCREEN_MODE_RESULTS = 6;
SCREEN_MODE_TIMER = 7;
SCREEN_MODE_TEXT = 8;
}
enum ScreenTimerState {
SCREEN_TIMER_STATE_UNSPECIFIED = 0;
SCREEN_TIMER_STATE_STOPPED = 1;
SCREEN_TIMER_STATE_RUNNING = 2;
SCREEN_TIMER_STATE_PAUSED = 3;
SCREEN_TIMER_STATE_FINISHED = 4;
}
enum ScreenCommand {
SCREEN_COMMAND_UNSPECIFIED = 0;
SCREEN_COMMAND_PLAY = 1;
SCREEN_COMMAND_PAUSE = 2;
SCREEN_COMMAND_RESUME = 3;
SCREEN_COMMAND_STOP = 4;
SCREEN_COMMAND_REPLAY = 5;
SCREEN_COMMAND_SEEK = 6;
SCREEN_COMMAND_VOLUME = 7;
SCREEN_COMMAND_TIMER_START = 8;
SCREEN_COMMAND_TIMER_PAUSE = 9;
SCREEN_COMMAND_TIMER_RESUME = 10;
SCREEN_COMMAND_TIMER_RESET = 11;
}
message ScreenState {
string ID = 1;
string status = 2;
optional int32 gameID = 3;
ScreenMode mode = 4;
bool sound_enabled = 5;
string error = 6;
int64 state_version = 7;
int64 position_ms = 8;
string game_name = 9;
string cover_image = 10;
string intro_audio = 11;
string conclusion_video = 12;
ScreenCommand command = 13;
int32 volume = 14;
string background_image = 15;
string timer_label = 16;
int64 timer_duration_seconds = 17;
int64 timer_remaining_seconds = 18;
ScreenTimerState timer_state = 19;
string text = 20;
MusicState music = 21;
}
message CreateScreenSessionReq {}
message CreateScreenSessionRsp {
string screenID = 1;
string access_token = 2;
}
message GetScreenStateReq {
string screenID = 1;
string accessToken = 2;
}
message GetScreenStateRsp {
ScreenState state = 1;
}
message GetGameScreensReq {
int32 gameID = 1;
}
message GetGameScreensRsp {
repeated ScreenState screens = 1;
}
enum MusicPlaybackState {
MUSIC_PLAYBACK_UNSPECIFIED = 0;
MUSIC_PLAYBACK_STOPPED = 1;
MUSIC_PLAYBACK_PLAYING = 2;
MUSIC_PLAYBACK_PAUSED = 3;
}
message MusicState {
Playlist playlist = 1;
int32 track_index = 2;
int64 position_ms = 3;
int32 volume = 4;
bool loop = 5;
MusicPlaybackState playback = 6;
int64 state_version = 7;
}
message GetGameMusicReq {
int32 gameID = 1;
}
message GetGameMusicRsp {
repeated Playlist playlists = 1;
MusicState state = 2;
int32 scenario_playlist_id = 3;
int32 game_playlist_id = 4;
}
message SetGameMusicReq {
int32 gameID = 1;
int32 playlistID = 2;
int32 track_index = 3;
int64 position_ms = 4;
int32 volume = 5;
bool loop = 6;
MusicPlaybackState playback = 7;
}
message SetGameMusicRsp {
MusicState state = 1;
}
message AdvanceScreenMusicReq {
string screenID = 1;
string accessToken = 2;
}
message AdvanceScreenMusicRsp {
MusicState state = 1;
}
message BindScreenReq {
string screenID = 1;
string accessToken = 2;
int32 gameID = 3;
}
message BindScreenRsp {
ScreenState state = 1;
}
message UnbindScreenReq {
string screenID = 1;
}
message UnbindScreenRsp {
}
message SetScreenSoundReq {
string screenID = 1;
bool soundEnabled = 2;
}
message SetScreenSoundRsp {
}
message MapItem {
string kind = 1;
int32 itemID = 2;
string label = 3;
int32 x = 4;
int32 y = 5;
bool hidden = 6;
string status = 7;
bool sound_enabled = 8;
}
message GetGameMapReq {
int32 gameID = 1;
}
message GetGameMapRsp {
repeated MapItem items = 1;
}
message UpdateGameMapItemReq {
int32 gameID = 1;
string kind = 2;
int32 itemID = 3;
int32 x = 4;
int32 y = 5;
bool hidden = 6;
}
message UpdateGameMapItemRsp {}
message SetScreenModeReq {
int32 gameID = 1;
ScreenMode mode = 2;
}
message SetScreenModeRsp {
}
message SetScreenTimerReq {
int32 gameID = 1;
int64 duration_seconds = 2;
string label = 3;
}
message SetScreenTimerRsp {
}
message SendScreenCommandReq {
int32 gameID = 1;
ScreenCommand command = 2;
int64 position_ms = 3;
int32 volume = 4;
}
message SendScreenCommandRsp {
}
message ScreenPlanStep {
ScreenMode mode = 1;
string text = 2;
string timer_label = 3;
int64 timer_duration_seconds = 4;
}
message ScreenPlan {
repeated ScreenPlanStep steps = 1;
int32 current_step = 2;
}
message GetScreenPlanReq {
int32 gameID = 1;
}
message GetScreenPlanRsp {
ScreenPlan plan = 1;
}
message SaveScreenPlanReq {
int32 gameID = 1;
repeated ScreenPlanStep steps = 2;
int32 current_step = 3;
}
message SaveScreenPlanRsp {
ScreenPlan plan = 1;
}
message SetScreenPlanStepReq {
int32 gameID = 1;
int32 current_step = 2;
}
message SetScreenPlanStepRsp {
ScreenPlan plan = 1;
}
message GetScreenResultsReq {
string screenID = 1;
string accessToken = 2;
}
message GetScreenResultsRsp {
repeated ResultsQuestion questions = 1;
repeated TeamResult teams = 2;
}
message GetParticipationReq {
// Персональный токен из письма: 64 шестнадцатеричных символа. Не является JWT.
string token = 1;
}
message AnswerParticipationReq {
// Персональный токен из письма: 64 шестнадцатеричных символа. Не является JWT.
string token = 1;
// Окончательное решение: confirmed — участвуем, cancelled — отменяем запись.
string answer = 2;
}
message ParticipationRsp {
// Название команды. Данные доступны только по действительному токену.
string team_name = 1;
// Название игры.
string game_name = 2;
// Плановое начало игры, UTC (RFC 3339 в JSON).
google.protobuf.Timestamp start_at = 3;
// Заявленное число участников команды.
int32 participants_count = 4;
// pending — ожидает ответа, confirmed — подтверждено, cancelled — отменено.
string status = 5;
// Новые ответы закрыты: наступило плановое начало или игра вышла из draft.
// Изменение уже принятого решения запрещено независимо от этого поля.
bool closed = 6;
}
message Team {
int32 ID = 1;
string name = 2;
string status = 3;
string password = 4;
int32 actions_count = 5;
repeated Application applications = 6;
string captain_email = 7;
int32 participants_count = 8;
// Статус участия: pending, confirmed или cancelled. Поле панели организатора.
string participation_status = 9;
// Источник решения: captain, automatic либо пустая строка до ответа.
string participation_source = 10;
// Последняя ошибка отправки; пустая строка, если ошибки нет.
string participation_email_error = 11;
// Письмо принято SMTP-сервером; не означает прочтение или доставку во входящие.
bool participation_email_sent = 12;
}
message GetGameReq {
int32 ID = 1;
}
message GetGameRsp {
Game game = 1;
}
message GetGameAnswersReq {
int32 gameID = 1;
}
message GetGameAnswersRsp {
repeated Question questions = 1;
repeated TeamAnswers teams = 2;
}
message GetGameResultsReq {
int32 gameID = 1;
}
// Вопрос в шапке итоговой таблицы: правильный ответ (поле answer сообщения
// Question) командам/таблице не передаётся — он виден только в панели оценки.
message ResultsQuestion {
string code = 1;
string text = 2;
}
// Строка итоговой таблицы: команда, баллы по вопросам, сумма и место.
message TeamResult {
Team team = 1;
repeated AnswerScore answers = 2;
// Сумма баллов по всем вопросам сценария (неоценённые считаются как 0)
int32 total_score = 3;
// Место в итоговой таблице: команды с одинаковой суммой баллов и
// одинаковым числом ходов занимают одно место (следующее пропускается)
int32 place = 4;
// Количество ходов команды (дублирует team.actions_count для удобства
// таблицы; участвует в тай-брейке при равенстве суммы баллов)
int32 actions_count = 5;
}
message GetGameResultsRsp {
repeated ResultsQuestion questions = 1;
repeated TeamResult teams = 2;
}
message GameReview {
int32 ID = 1;
int32 gameID = 2;
int32 teamID = 3;
string team_name = 4;
int32 rating = 5;
string text = 6;
string status = 7;
google.protobuf.Timestamp created_at = 8;
string game_name = 9;
int32 difficulty = 10;
}
message CreateGameReviewReq {
int32 gameID = 1;
int32 teamID = 2;
string password = 3;
int32 rating = 4;
string text = 5;
int32 difficulty = 6;
}
message CreateGameReviewRsp {
GameReview review = 1;
}
message GetGameReviewsReq {
int32 gameID = 1;
}
message GetScenarioReviewsReq {
int32 scenarioID = 1;
}
message GetReviewsForModerationReq {}
message GetGameReviewsRsp {
double average_rating = 1;
int32 reviews_count = 2;
repeated GameReview reviews = 3;
}
message ModerateGameReviewReq {
int32 ID = 1;
string status = 2;
}
message ModerateGameReviewRsp {
GameReview review = 1;
}
message UpdateGameReq {
int32 ID = 1;
string name = 2;
string description = 3;
google.protobuf.Timestamp start_at = 4;
int32 scenarioID = 5;
string format = 6;
string location = 7;
}
message UpdateGameRsp {
}
message StartGameReq {
int32 ID = 1;
}
message StartGameRsp {
}
message PauseGameReq {
int32 ID = 1;
}
message PauseGameRsp {
}
message PlayGameReq {
int32 ID = 1;
}
message PlayGameRsp {
}
message FinishGameReq {
int32 ID = 1;
}
message FinishGameRsp {
}
message ResetGameReq {
int32 ID = 1;
}
message ResetGameRsp {
}
message DeleteGameReq {
int32 ID = 1;
}
message DeleteGameRsp {
}
message AddTeamReq {
int32 gameID = 1;
string name = 2;
string captain_email = 3;
int32 participants_count = 4;
}
message AddTeamRsp {
int32 ID = 1;
string password = 2;
}
message UpdateTeamReq {
int32 ID = 1;
string name = 2;
}
message UpdateTeamRsp {
}
message DeleteTeamReq {
int64 ID = 1;
}
message DeleteTeamRsp {
}
message GiveTeamApplicationsReq {
int64 ID = 1;
string name = 2;
}
message GiveTeamApplicationsRsp {
}
message GetTeamStoryReq {
int64 ID = 1;
string password = 2;
}
message GetTeamStoryRsp {
Story story = 1;
Game game = 2;
repeated TeamAction actions = 3;
}
message TeamAction {
string code = 1;
bool mcp = 2;
}
message AddTeamActionReq {
int64 ID = 1;
string password = 2;
string code = 3;
}
message AddTeamActionRsp {
}
message DeleteLastTeamActionReq {
int64 ID = 1;
}
message DeleteLastTeamActionRsp {
}
message GetTeamAnswersReq {
int64 ID = 1;
string password = 2;
}
message GetTeamAnswersRsp {
repeated Answer answers = 1;
}
message SubmitTeamAnswersReq {
int64 ID = 1;
string password = 2;
repeated Answer answers = 3;
}
message SubmitTeamAnswersRsp {
}
message SetTeamAnswerScoreReq {
int64 ID = 1;
string questionCode = 2;
// Балл; отсутствует — сбросить выставленный балл
optional int32 score = 3;
}
message SetTeamAnswerScoreRsp {
}
message UpdateScenarioBackgroundReq {
int32 ID = 1;
string background = 2;
}
message UpdateScenarioBackgroundRsp {
}
message IssueGameTicketsReq {
int32 gameID = 1;
int32 count = 2;
}
message IssueGameTicketsRsp {
repeated Ticket tickets = 1;
}
message Ticket {
int32 ID = 1;
string URL = 2;
}
message GetTicketReq {
string token = 1;
}
message GetTicketRsp {
TicketPage ticket = 1;
}
message AcceptTicketReq {
string token = 1;
}
message AcceptTicketRsp {
TicketPage ticket = 1;
}
message TicketPage {
int32 ID = 1;
string game_name = 2;
google.protobuf.Timestamp start_at = 3;
string location = 4;
string status = 5;
string game_status = 6;
optional google.protobuf.Timestamp used_at = 7;
string used_by = 8;
}
// Developer tasks use new, in_progress, review and done statuses.
message DeveloperTask {
int32 ID = 1;
string title = 2;
string description = 3;
string status = 4;
// Omitted assignee means the task is unassigned.
optional int32 assigneeID = 5;
string assigneeName = 6;
optional int32 authorID = 7;
// Calendar deadline in YYYY-MM-DD format; empty means no deadline.
string dueDate = 8;
google.protobuf.Timestamp createdAt = 9;
google.protobuf.Timestamp updatedAt = 10;
// Required for updates and deletion to detect concurrent edits.
int32 version = 11;
}
message TaskAssignee {
int32 ID = 1;
string name = 2;
}
message GetTasksReq {}
message GetTasksRsp { repeated DeveloperTask tasks = 1; }
message GetTaskAssigneesReq {}
message GetTaskAssigneesRsp { repeated TaskAssignee assignees = 1; }
message CreateTaskReq {
string title = 1;
string description = 2;
string status = 3;
optional int32 assigneeID = 4;
string dueDate = 5;
}
message UpdateTaskReq {
int32 ID = 1;
string title = 2;
string description = 3;
string status = 4;
optional int32 assigneeID = 5;
string dueDate = 6;
int32 version = 7;
}
message SetTaskStatusReq {
int32 ID = 1;
string status = 2;
int32 version = 3;
}
message DeleteTaskReq {
int32 ID = 1;
int32 version = 2;
}
message TaskRsp { DeveloperTask task = 1; }
message DeleteTaskRsp {}