/** * Microsoft 365: zalozeni schuzky v kalendari. * * Sluzba: https://services.csbot.cz/apps/microsoft-365-service * Endpoint: POST /users/{user_id}/calendar/events * * Casova zona se posila vzdy vyslovne. Graph bez ni bere cas jako UTC, takze * schuzka domluvena na desatou by v letnim case vysla na dvanactou - a nikdo * by nevedel proc. */ export const manifest = { id: 'microsoft365.create-event', name: 'Vytvořit schůzku', description: 'Založí schůzku v kalendáři zvoleného uživatele a pozve účastníky.', inputs: [ { id: 'mailbox', label: 'Kalendář uživatele', type: 'string', required: true, hint: 'E-mail nebo ID uživatele, jehož kalendář se použije.', }, { id: 'subject', label: 'Název schůzky', type: 'string', required: true }, { id: 'start', label: 'Začátek', type: 'date', required: true, hint: 'Datum a čas začátku.', }, { id: 'end', label: 'Konec', type: 'date', required: true, hint: 'Musí být po začátku.', }, { id: 'timeZone', label: 'Časová zóna', type: 'string', required: false, default: 'Central Europe Standard Time', hint: 'Název zóny podle Windows, jak ho očekává Graph.', }, { id: 'attendees', label: 'Účastníci', type: 'string', required: false, hint: 'E-maily oddělené čárkou.', }, { id: 'location', label: 'Místo', type: 'string', required: false }, { id: 'body', label: 'Popis', type: 'string', required: false, multiline: true, }, { id: 'onlineMeeting', label: 'Přidat schůzku v Teams', type: 'boolean', required: false, default: false, }, ], outputs: [ { id: 'eventId', label: 'ID schůzky', type: 'string', required: true }, { id: 'webLink', label: 'Odkaz na schůzku', type: 'string', required: false }, { id: 'joinUrl', label: 'Odkaz do Teams', type: 'string', required: false }, ], timeoutMs: 30000, }; export async function run(inputs, ctx) { const { pick, text, need } = ctx.util; if (Date.parse(inputs.end) <= Date.parse(inputs.start)) { ctx.fail('Konec schůzky musí být po jejím začátku.'); } // Graph chce cas bez zonoveho posunu a zonu zvlast. ISO z validace parametru // konci na Z, takze se posun odrizne a zona se posle v `timeZone`. const local = (value) => value.replace(/(\.\d+)?Z$/, ''); const payload = { subject: inputs.subject, start: { dateTime: local(inputs.start), timeZone: inputs.timeZone }, end: { dateTime: local(inputs.end), timeZone: inputs.timeZone }, is_online_meeting: inputs.onlineMeeting, }; if (inputs.body) payload.body = inputs.body; if (inputs.location) payload.location = inputs.location; if (inputs.onlineMeeting) payload.online_meeting_provider = 'teamsForBusiness'; const attendees = String(inputs.attendees ?? '') .split(/[,;]/) .map((item) => item.trim()) .filter((item) => item !== '') .map((address) => ({ address })); if (attendees.length > 0) payload.attendees = attendees; const { body } = await ctx.http.post( `/users/${encodeURIComponent(inputs.mailbox)}/calendar/events`, payload, ); return { eventId: need(text(pick(body, 'id')), 'ID schůzky'), webLink: text(pick(body, 'webLink')), joinUrl: text(pick(body, 'onlineMeetingUrl') ?? pick(pick(body, 'onlineMeeting'), 'joinUrl')), }; }