🛠 Настройка окружения – Интеграция Calendar и Things 3 в Obsidian

Мы строим сложные, отказоустойчивые системы на работе, но наше собственное рабочее окружение часто держится на честном слове и силе воли.

Долгое время мой воркфлоу выглядел как лоскутное одеяло. Встречи живут в календаре. Личные и рабочие задачи — в Things 3. Заметки и база знаний — в Obsidian.

Проблема в том, что эти инструменты ничего не знают друг о друге.

Я поймал себя на том, что работаю секретарем у самого себя: вручную копирую названия встреч в заметки, перебиваю задачи из итогов созвона в трекер. Это рутина, которая убивает продуктивность менеджера.

Я решил это исправить. Я захотел, чтобы каждое утро Obsidian сам "ходил" в мой календарь и планировщик, забирал всё важное и раскладывал по полочкам передо мной.

Вот как я настроил эту автоматизацию на macOS.

🛠 Шаг 1. Необходимые плагины

Для этой системы нам понадобятся три плагина из Community Plugins Obsidian:

Templater

«Мозги» системы. Позволяет выполнять JavaScript и системные скрипты (AppleScript) прямо внутри заметки.
Obsidian

Journals

Улучшенная замена стандартным Daily Notes. Позволяет гибко управлять ежедневными записями.
Obsidian

Full Calendar

Для удобного просмотра расписания прямо внутри Obsidian.
Obsidian

📅 Шаг 2. Интеграция Календаря (Outlook)

Здесь мы решаем две задачи: смотреть расписание (визуально) и фиксировать встречи в текстовом журнале.

Чтобы видеть встречи в Obsidian, нам нужна ссылка .ical (или .ics) из Outlook.

  1. Открываем Outlook Web (OWA).
  2. Идем в Settings (шестеренка) -> Calendar -> Shared calendars.
  3. В блоке Publish a calendar выбираем нужный календарь, ставим права "Can view all details" и жмем Publish.
  4. Копируем ICS ссылку https://example.com/calendar.ics
  5. В Obsidian: Настройки Full Calendar → Options → Calendars → Remote → Вставляем ссылку.

Настройки должны выглядеть так:

TBD

✅ Шаг 3. Интеграция Things 3 (Задачи)

Создай файл get_things.js в папке скриптов Templater

function get_things() {
    const { spawnSync } = require('child_process');
    
    const script = `
        tell application "Things3"
            set outputList to {}
            try
                set todayToDos to to dos of list "Today"
                repeat with aToDo in todayToDos
                    set todoName to (name of aToDo as string)
                    set projectName to "Без проекта"
                    
                    if project of aToDo is not missing value then
                        set projectName to (name of project of aToDo as string)
                    end if
                    
                    copy (projectName & "::" & todoName) to end of outputList
                end repeat
            on error errMsg
                return "Ошибка: " & errMsg
            end try
            
            set AppleScript's text item delimiters to "|DELIM|"
            set outputString to outputList as string
            set AppleScript's text item delimiters to ""
            return outputString
        end tell
    `;

    try {
        const child = spawnSync('osascript', [], { input: script, encoding: 'utf-8' });
        if (child.status !== 0 || !child.stdout.trim()) return [];
        return child.stdout.trim().split("|DELIM|");
    } catch (e) {
        console.error("Things3 Export Error:", e);
        return [];
    }
}

module.exports = get_things;

Загрузите плагин в Templater
Obsidian → Settings → Templater → User Scripts Function:

  • Укажите путь до папки со скриптами
  • Нажмите Refresh
  • В итоге в Detected User Scripts должен появиться get_things
    TBD

Шаг 4: Итоговый шаблон Templater

  • Нужно указать путь до папки с шаблонами (_Templates)
  • Включить Trigger Templater on new file creation
    TBD
  • В Journals добавить свой Daily шаблон
    TBD
# Важные задачи дня
<%*
const rawTasks = await tp.user.get_things();
const grouped = {};

rawTasks.forEach(item => {
    const [project, task] = item.split("::");
    if (!grouped[project]) grouped[project] = [];
    grouped[project].push(task);
});

if (Object.keys(grouped).length > 0) {
    for (const project in grouped) {
        tR += `##### ${project}\n`;
        grouped[project].forEach(task => {
            tR += `- [ ] ${task}\n`;
        });
        tR += `\n`;
    }
} else {
    tR += "_Задач на сегодня нет_ ☕️\n";
}
%>

---
# Встречи на сегодня
<%*
const fc = app.plugins.plugins["obsidian-full-calendar"];
const allEvents = await fc?.cache.getAllEvents() || []
if (allEvents.length > 0) {
	const today = window.moment().format("YYYY-MM-DD");
	let events = [];
	try {
		events = await allEvents[0].events.filter(event => {
			return event.event.date === today;
		});
	} catch (err) {
		console.error("Full Calendar Filter Error:", err);
	}
	if (events.length > 0) {
		events.sort((a, b) => {
			const timeA = a.event?.startTime || "";
			const timeB = b.event?.startTime || "";
			return timeA.localeCompare(timeB);
		});
		
		events.forEach(item => {
			// Забираем данные из вложенного объекта 'event'
			const e = item.event; 
			const title = e?.title || "Без названия";
			const start = e?.startTime || "--:--";
			
			tR += `### ${title} – ${start}\n`;
		});
	} else {
		tR += "_На сегодня встреч не найдено._\n";
	}
} else {
	tR += "> [!error] Плагин Full Calendar не активен.\n";
}
%>
# Заметки

  • Настроить шаблон для ежедневных страниц
    TBD

Теперь собираем всё вместе. В настройках плагина Journals укажите путь к этому шаблону.

Что делает шаблон:

  1. Загружает задачи через JS.
  2. Группирует их по проектам (как в интерфейсе Things).
  3. Выводит список встреч.В итоге, при создании Daily Journal будете видеть что-то вроде такого:
    TBD

Почему это работает для продуктивности

  • Снижение когнитивной нагрузки: Вам не нужно переключаться между тремя приложениями, чтобы понять свой план на день.
  • Контроль проектов: Визуальное разделение на проекты в Obsidian помогает держать фокус на рабочих и личных задачах одновременно.
  • Автономность: Скрипты работают локально через AppleScript, не требуя облачных API или сложных авторизаций.