feat: podpora per-user notifikací s Discord, ntfy a Teams (#39)

Uživatelé mohou v nastavení konfigurovat vlastní webhook URL/topic
pro Discord, MS Teams a ntfy, a zvolit události k odběru.
Notifikace jsou odesílány pouze uživatelům se stejnou zvolenou lokalitou.
This commit is contained in:
2026-02-04 17:08:23 +01:00
parent fdd42dc46a
commit f36afe129a
6 changed files with 1824 additions and 12 deletions

View File

@@ -1,6 +1,8 @@
import { useRef } from "react";
import { useEffect, useRef, useState } from "react";
import { Modal, Button, Form } from "react-bootstrap"
import { useSettings, ThemePreference } from "../../context/settings";
import { NotificationSettings, UdalostEnum, getNotificationSettings, updateNotificationSettings } from "../../../../types";
import { useAuth } from "../../context/auth";
type Props = {
isOpen: boolean,
@@ -10,12 +12,56 @@ type Props = {
/** Modální dialog pro uživatelská nastavení. */
export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Props>) {
const auth = useAuth();
const settings = useSettings();
const bankAccountRef = useRef<HTMLInputElement>(null);
const nameRef = useRef<HTMLInputElement>(null);
const hideSoupsRef = useRef<HTMLInputElement>(null);
const themeRef = useRef<HTMLSelectElement>(null);
const ntfyTopicRef = useRef<HTMLInputElement>(null);
const discordWebhookRef = useRef<HTMLInputElement>(null);
const teamsWebhookRef = useRef<HTMLInputElement>(null);
const [notifSettings, setNotifSettings] = useState<NotificationSettings>({});
const [enabledEvents, setEnabledEvents] = useState<UdalostEnum[]>([]);
useEffect(() => {
if (isOpen && auth?.login) {
getNotificationSettings().then(response => {
if (response.data) {
setNotifSettings(response.data);
setEnabledEvents(response.data.enabledEvents ?? []);
}
}).catch(() => {});
}
}, [isOpen, auth?.login]);
const toggleEvent = (event: UdalostEnum) => {
setEnabledEvents(prev =>
prev.includes(event) ? prev.filter(e => e !== event) : [...prev, event]
);
};
const handleSave = async () => {
// Uložení notifikačních nastavení na server
await updateNotificationSettings({
body: {
ntfyTopic: ntfyTopicRef.current?.value || undefined,
discordWebhookUrl: discordWebhookRef.current?.value || undefined,
teamsWebhookUrl: teamsWebhookRef.current?.value || undefined,
enabledEvents,
}
}).catch(() => {});
// Uložení ostatních nastavení (localStorage)
onSave(
bankAccountRef.current?.value,
nameRef.current?.value,
hideSoupsRef.current?.checked,
themeRef.current?.value as ThemePreference,
);
};
return (
<Modal show={isOpen} onHide={onClose} size="lg">
<Modal.Header closeButton>
@@ -51,6 +97,75 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
<hr />
<h4>Notifikace</h4>
<p>
Nastavením notifikací budete dostávat upozornění o událostech (např. "Jdeme na oběd") přímo do vámi zvoleného komunikačního kanálu.
</p>
<Form.Group className="mb-3">
<Form.Label>ntfy téma (topic)</Form.Label>
<Form.Control
ref={ntfyTopicRef}
type="text"
placeholder="moje-tema"
defaultValue={notifSettings.ntfyTopic}
key={notifSettings.ntfyTopic ?? 'ntfy-empty'}
onKeyDown={e => e.stopPropagation()}
/>
<Form.Text className="text-muted">
Téma pro ntfy push notifikace. Nechte prázdné pro vypnutí.
</Form.Text>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Discord webhook URL</Form.Label>
<Form.Control
ref={discordWebhookRef}
type="text"
placeholder="https://discord.com/api/webhooks/..."
defaultValue={notifSettings.discordWebhookUrl}
key={notifSettings.discordWebhookUrl ?? 'discord-empty'}
onKeyDown={e => e.stopPropagation()}
/>
<Form.Text className="text-muted">
URL webhooku Discord kanálu. Nechte prázdné pro vypnutí.
</Form.Text>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>MS Teams webhook URL</Form.Label>
<Form.Control
ref={teamsWebhookRef}
type="text"
placeholder="https://outlook.office.com/webhook/..."
defaultValue={notifSettings.teamsWebhookUrl}
key={notifSettings.teamsWebhookUrl ?? 'teams-empty'}
onKeyDown={e => e.stopPropagation()}
/>
<Form.Text className="text-muted">
URL webhooku MS Teams kanálu. Nechte prázdné pro vypnutí.
</Form.Text>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Události k odběru</Form.Label>
{Object.values(UdalostEnum).map(event => (
<Form.Check
key={event}
id={`notif-event-${event}`}
type="checkbox"
label={event}
checked={enabledEvents.includes(event)}
onChange={() => toggleEvent(event)}
/>
))}
<Form.Text className="text-muted">
Zvolte události, o kterých chcete být notifikováni. Notifikace jsou odesílány pouze uživatelům se stejnou zvolenou lokalitou.
</Form.Text>
</Form.Group>
<hr />
<h4>Bankovní účet</h4>
<p>
Nastavením čísla účtu umožníte automatické generování QR kódů pro úhradu za vámi provedené objednávky v rámci Pizza day.
@@ -88,7 +203,7 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
<Button variant="secondary" onClick={onClose}>
Storno
</Button>
<Button onClick={() => onSave(bankAccountRef.current?.value, nameRef.current?.value, hideSoupsRef.current?.checked, themeRef.current?.value as ThemePreference)}>
<Button onClick={handleSave}>
Uložit
</Button>
</Modal.Footer>

View File

@@ -13,6 +13,7 @@ import foodRoutes, { refreshMetoda } from "./routes/foodRoutes";
import votingRoutes from "./routes/votingRoutes";
import easterEggRoutes from "./routes/easterEggRoutes";
import statsRoutes from "./routes/statsRoutes";
import notificationRoutes from "./routes/notificationRoutes";
const ENVIRONMENT = process.env.NODE_ENV ?? 'production';
dotenv.config({ path: path.resolve(__dirname, `../.env.${ENVIRONMENT}`) });
@@ -146,6 +147,7 @@ app.use("/api/food", foodRoutes);
app.use("/api/voting", votingRoutes);
app.use("/api/easterEggs", easterEggRoutes);
app.use("/api/stats", statsRoutes);
app.use("/api/notifications", notificationRoutes);
app.use('/stats', express.static('public'));
app.use(express.static('public'));

View File

@@ -3,11 +3,56 @@ import dotenv from 'dotenv';
import path from 'path';
import { getClientData, getToday } from "./service";
import { getUsersByLocation, getHumanTime } from "./utils";
import { NotifikaceData, NotifikaceInput } from '../../types';
import { NotifikaceData, NotifikaceInput, NotificationSettings } from '../../types';
import getStorage from "./storage";
const ENVIRONMENT = process.env.NODE_ENV ?? 'production';
dotenv.config({ path: path.resolve(__dirname, `../.env.${ENVIRONMENT}`) });
const storage = getStorage();
const NOTIFICATION_SETTINGS_PREFIX = 'notif';
/** Vrátí klíč pro uložení notifikačních nastavení uživatele. */
function getNotificationSettingsKey(login: string): string {
return `${NOTIFICATION_SETTINGS_PREFIX}_${login}`;
}
/** Vrátí nastavení notifikací pro daného uživatele. */
export async function getNotificationSettings(login: string): Promise<NotificationSettings> {
return await storage.getData<NotificationSettings>(getNotificationSettingsKey(login)) ?? {};
}
/** Uloží nastavení notifikací pro daného uživatele. */
export async function saveNotificationSettings(login: string, settings: NotificationSettings): Promise<NotificationSettings> {
await storage.setData(getNotificationSettingsKey(login), settings);
return settings;
}
/** Odešle ntfy notifikaci na dané téma. */
async function ntfyCallToTopic(topic: string, message: string) {
const url = process.env.NTFY_HOST;
const username = process.env.NTFY_USERNAME;
const password = process.env.NTFY_PASSWD;
if (!url || !username || !password) {
return;
}
const token = Buffer.from(`${username}:${password}`, 'utf8').toString('base64');
try {
const response = await axios({
url: `${url}/${topic}`,
method: 'POST',
data: message,
headers: {
'Authorization': `Basic ${token}`,
'Tag': 'meat_on_bone'
}
});
console.log(response.data);
} catch (error) {
console.error(`Chyba při odesílání ntfy notifikace na topic ${topic}:`, error);
}
}
export const ntfyCall = async (data: NotifikaceInput) => {
const url = process.env.NTFY_HOST
const username = process.env.NTFY_USERNAME;
@@ -87,10 +132,58 @@ export const teamsCall = async (data: NotifikaceInput) => {
}
}
/** Odešle Teams notifikaci na daný webhook URL. */
async function teamsCallToUrl(webhookUrl: string, data: NotifikaceInput) {
const title = data.udalost;
let time = new Date();
time.setTime(time.getTime() + 1000 * 60);
const message = 'Odcházíme v ' + getHumanTime(time) + ', ' + data.user;
const card = {
'@type': 'MessageCard',
'@context': 'http://schema.org/extensions',
'themeColor': "0072C6",
summary: 'Summary description',
sections: [
{
activityTitle: title,
text: message,
},
],
};
try {
await axios.post(webhookUrl, card, {
headers: {
'content-type': 'application/vnd.microsoft.teams.card.o365connector'
},
});
} catch (error) {
console.error(`Chyba při odesílání Teams notifikace:`, error);
}
}
/** Odešle Discord notifikaci na daný webhook URL. */
async function discordCall(webhookUrl: string, data: NotifikaceInput) {
let time = new Date();
time.setTime(time.getTime() + 1000 * 60);
const message = `🍖 **${data.udalost}** — ${data.user} (odchod v ${getHumanTime(time)})`;
try {
await axios.post(webhookUrl, {
content: message,
}, {
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
console.error(`Chyba při odesílání Discord notifikace:`, error);
}
}
/** Zavolá notifikace na všechny konfigurované způsoby notifikace, přetížení proměných na false pro jednotlivé způsoby je vypne*/
export const callNotifikace = async ({ input, teams = true, gotify = false, ntfy = true }: NotifikaceData) => {
const notifications = [];
const notifications: Promise<any>[] = [];
// Globální notifikace (zpětně kompatibilní)
if (ntfy) {
const ntfyPromises = await ntfyCall(input);
if (ntfyPromises) {
@@ -100,20 +193,33 @@ export const callNotifikace = async ({ input, teams = true, gotify = false, ntfy
if (teams) {
const teamsPromises = await teamsCall(input);
if (teamsPromises) {
notifications.push(teamsPromises);
notifications.push(Promise.resolve(teamsPromises));
}
}
// Per-user notifikace: najdeme uživatele se stejnou lokací a odešleme dle jejich nastavení
const clientData = await getClientData(getToday());
const usersToNotify = getUsersByLocation(clientData.choices, input.user);
for (const user of usersToNotify) {
if (user === input.user) continue; // Neposíláme notifikaci spouštějícímu uživateli
const userSettings = await getNotificationSettings(user);
if (!userSettings.enabledEvents?.includes(input.udalost)) continue;
if (userSettings.ntfyTopic) {
notifications.push(ntfyCallToTopic(userSettings.ntfyTopic, `${input.udalost} - spustil: ${input.user}`));
}
if (userSettings.discordWebhookUrl) {
notifications.push(discordCall(userSettings.discordWebhookUrl, input));
}
if (userSettings.teamsWebhookUrl) {
notifications.push(teamsCallToUrl(userSettings.teamsWebhookUrl, input));
}
}
// gotify bych řekl, že už je deprecated
// if (gotify) {
// const gotifyPromises = await gotifyCall(input, gotifyData);
// notifications.push(...gotifyPromises);
// }
try {
const results = await Promise.all(notifications);
return results;
} catch (error) {
console.error("Error in callNotifikace: ", error);
// Handle the error as needed
}
};

View File

@@ -0,0 +1,32 @@
import express, { Request } from "express";
import { getLogin } from "../auth";
import { parseToken } from "../utils";
import { getNotificationSettings, saveNotificationSettings } from "../notifikace";
import { UpdateNotificationSettingsData } from "../../../types";
const router = express.Router();
/** Vrátí nastavení notifikací pro přihlášeného uživatele. */
router.get("/settings", async (req, res, next) => {
const login = getLogin(parseToken(req));
try {
const settings = await getNotificationSettings(login);
res.status(200).json(settings);
} catch (e: any) { next(e) }
});
/** Uloží nastavení notifikací pro přihlášeného uživatele. */
router.post("/settings", async (req: Request<{}, any, UpdateNotificationSettingsData["body"]>, res, next) => {
const login = getLogin(parseToken(req));
try {
const settings = await saveNotificationSettings(login, {
ntfyTopic: req.body.ntfyTopic,
discordWebhookUrl: req.body.discordWebhookUrl,
teamsWebhookUrl: req.body.teamsWebhookUrl,
enabledEvents: req.body.enabledEvents,
});
res.status(200).json(settings);
} catch (e: any) { next(e) }
});
export default router;

527
types/gen/sdk.gen.ts Normal file
View File

@@ -0,0 +1,527 @@
// This file is auto-generated by @hey-api/openapi-ts
import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
import type { LoginData, LoginResponse, GetPizzaQrData, GetPizzaQrResponse, GetDataData, GetDataResponse, AddChoiceData, AddChoiceResponse, RemoveChoiceData, RemoveChoiceResponse, UpdateNoteData, UpdateNoteResponse, RemoveChoicesData, RemoveChoicesResponse, ChangeDepartureTimeData, ChangeDepartureTimeResponse, JdemeObedData, SetBuyerData, CreatePizzaDayData, DeletePizzaDayData, LockPizzaDayData, UnlockPizzaDayData, FinishOrderData, FinishDeliveryData, AddPizzaData, RemovePizzaData, UpdatePizzaDayNoteData, UpdatePizzaFeeData, GetEasterEggData, GetEasterEggResponse, GetEasterEggImageData, GetEasterEggImageResponse, GetStatsData, GetStatsResponse, GetVotesData, GetVotesResponse, UpdateVoteData, GetVotingStatsData, GetVotingStatsResponse, GetNotificationSettingsData, GetNotificationSettingsResponse, UpdateNotificationSettingsData, DismissQrData } from './types.gen';
import { client as _heyApiClient } from './client.gen';
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
/**
* You can provide a client instance returned by `createClient()` instead of
* individual options. This might be also useful if you want to implement a
* custom client.
*/
client?: Client;
/**
* You can pass arbitrary values through the `meta` object. This can be
* used to access values that aren't defined as part of the SDK function.
*/
meta?: Record<string, unknown>;
};
/**
* Přihlášení uživatele
*/
export const login = <ThrowOnError extends boolean = false>(options?: Options<LoginData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<LoginResponse, unknown, ThrowOnError>({
url: '/login',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Získání QR kódu pro platbu za Pizza day
*/
export const getPizzaQr = <ThrowOnError extends boolean = false>(options: Options<GetPizzaQrData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).get<GetPizzaQrResponse, unknown, ThrowOnError>({
url: '/qr',
...options
});
};
/**
* Načtení klientských dat pro aktuální nebo předaný den
*/
export const getData = <ThrowOnError extends boolean = false>(options?: Options<GetDataData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<GetDataResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/data',
...options
});
};
/**
* Přidání či nahrazení volby uživatele pro zvolený den/podnik
*/
export const addChoice = <ThrowOnError extends boolean = false>(options: Options<AddChoiceData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<AddChoiceResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/food/addChoice',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Odstranění jednoho zvoleného jídla uživatele pro zvolený den/podnik
*/
export const removeChoice = <ThrowOnError extends boolean = false>(options: Options<RemoveChoiceData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<RemoveChoiceResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/food/removeChoice',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Nastavení poznámky k volbě uživatele
*/
export const updateNote = <ThrowOnError extends boolean = false>(options: Options<UpdateNoteData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<UpdateNoteResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/food/updateNote',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Odstranění volby uživatele pro zvolený den/podnik, včetně případných jídel
*/
export const removeChoices = <ThrowOnError extends boolean = false>(options: Options<RemoveChoicesData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<RemoveChoicesResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/food/removeChoices',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Úprava preferovaného času odchodu do aktuálně zvoleného podniku.
*/
export const changeDepartureTime = <ThrowOnError extends boolean = false>(options: Options<ChangeDepartureTimeData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<ChangeDepartureTimeResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/food/changeDepartureTime',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Odeslání notifikací "jdeme na oběd" dle konfigurace.
*/
export const jdemeObed = <ThrowOnError extends boolean = false>(options?: Options<JdemeObedData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/food/jdemeObed',
...options
});
};
/**
* Nastavení/odnastavení aktuálně přihlášeného uživatele jako objednatele pro stav "Budu objednávat" pro aktuální den.
*/
export const setBuyer = <ThrowOnError extends boolean = false>(options?: Options<SetBuyerData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/food/updateBuyer',
...options
});
};
/**
* Založení pizza day.
*/
export const createPizzaDay = <ThrowOnError extends boolean = false>(options?: Options<CreatePizzaDayData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/create',
...options
});
};
/**
* Smazání pizza day.
*/
export const deletePizzaDay = <ThrowOnError extends boolean = false>(options?: Options<DeletePizzaDayData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/delete',
...options
});
};
/**
* Uzamkne pizza day. Nebude možné přidávat či odebírat pizzy.
*/
export const lockPizzaDay = <ThrowOnError extends boolean = false>(options?: Options<LockPizzaDayData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/lock',
...options
});
};
/**
* Odemkne pizza day. Bude opět možné přidávat či odebírat pizzy.
*/
export const unlockPizzaDay = <ThrowOnError extends boolean = false>(options?: Options<UnlockPizzaDayData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/unlock',
...options
});
};
/**
* Přepnutí pizza day do stavu "Pizzy objednány". Není možné měnit objednávky, příslušným uživatelům je odeslána notifikace o provedené objednávce.
*/
export const finishOrder = <ThrowOnError extends boolean = false>(options?: Options<FinishOrderData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/finishOrder',
...options
});
};
/**
* Převod pizza day do stavu "Pizzy byly doručeny". Pokud má objednávající nastaveno číslo účtu, je ostatním uživatelům vygenerován a následně zobrazen QR kód pro úhradu jejich objednávky.
*/
export const finishDelivery = <ThrowOnError extends boolean = false>(options: Options<FinishDeliveryData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/finishDelivery',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Přidání pizzy do objednávky.
*/
export const addPizza = <ThrowOnError extends boolean = false>(options: Options<AddPizzaData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/add',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Odstranění pizzy z objednávky.
*/
export const removePizza = <ThrowOnError extends boolean = false>(options: Options<RemovePizzaData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/remove',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Nastavení poznámky k objednávkám pizz přihlášeného uživatele.
*/
export const updatePizzaDayNote = <ThrowOnError extends boolean = false>(options: Options<UpdatePizzaDayNoteData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/updatePizzaDayNote',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Nastavení přirážky/slevy k objednávce pizz uživatele.
*/
export const updatePizzaFee = <ThrowOnError extends boolean = false>(options: Options<UpdatePizzaFeeData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/updatePizzaFee',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Vrátí náhodně metadata jednoho z definovaných easter egg obrázků pro přihlášeného uživatele, nebo nic, pokud žádné definované nemá.
*/
export const getEasterEgg = <ThrowOnError extends boolean = false>(options?: Options<GetEasterEggData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<GetEasterEggResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/easterEggs',
...options
});
};
/**
* Vrátí obrázek konkrétního easter eggu
*/
export const getEasterEggImage = <ThrowOnError extends boolean = false>(options: Options<GetEasterEggImageData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).get<GetEasterEggImageResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/easterEggs/{url}',
...options
});
};
/**
* Vrátí statistiky způsobu stravování pro předaný rozsah dat.
*/
export const getStats = <ThrowOnError extends boolean = false>(options: Options<GetStatsData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).get<GetStatsResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/stats',
...options
});
};
/**
* Vrátí statistiky hlasování o nových funkcích.
*/
export const getVotes = <ThrowOnError extends boolean = false>(options?: Options<GetVotesData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<GetVotesResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/voting/getVotes',
...options
});
};
/**
* Aktualizuje hlasování uživatele o dané funkcionalitě.
*/
export const updateVote = <ThrowOnError extends boolean = false>(options: Options<UpdateVoteData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/voting/updateVote',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Vrátí agregované statistiky hlasování o nových funkcích.
*/
export const getVotingStats = <ThrowOnError extends boolean = false>(options?: Options<GetVotingStatsData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<GetVotingStatsResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/voting/stats',
...options
});
};
/**
* Vrátí nastavení notifikací přihlášeného uživatele.
*/
export const getNotificationSettings = <ThrowOnError extends boolean = false>(options?: Options<GetNotificationSettingsData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<GetNotificationSettingsResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/notifications/settings',
...options
});
};
/**
* Uloží nastavení notifikací přihlášeného uživatele.
*/
export const updateNotificationSettings = <ThrowOnError extends boolean = false>(options: Options<UpdateNotificationSettingsData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<GetNotificationSettingsResponse, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/notifications/settings',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
/**
* Označí QR kód jako uhrazený (zaplacený).
*/
export const dismissQr = <ThrowOnError extends boolean = false>(options: Options<DismissQrData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
security: [
{
scheme: 'bearer',
type: 'http'
}
],
url: '/pizzaDay/dismissQr',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};

1030
types/gen/types.gen.ts Normal file

File diff suppressed because it is too large Load Diff