Skip to main content

sesion-mental-health-platform

AI-powered mental health practice management platform for psychologists with scheduling, WhatsApp automation, AFIP billing, video consultations, and Claude AI integration

설치로 이동

소스 정보

저장소
reason-machines/devtools-skills
최근 소스 활동
2026년 7월 12일 14:34
감지된 SKILL.md 언어
영어
스타
4
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
sesion-mental-health-platform
description
AI-powered mental health practice management platform for psychologists with scheduling, WhatsApp automation, AFIP billing, video consultations, and Claude AI integration
triggers
["how do I set up the Sesión mental health platform","configure WhatsApp automation for patient appointments","integrate AFIP electronic invoicing in Sesión","set up video consultations with LiveKit","use Claude AI for clinical notes in Sesión","configure MercadoPago payment integration","implement patient journey workflows","troubleshoot Sesión WhatsApp connection"]
# Sesión Mental Health Platform > Skill by [ara.so](https://ara.so) — Devtools Skills collection. Sesión is a comprehensive SaaS platform for psychology clinics and independent practitioners in Argentina. It orchestrates appointment scheduling, automated WhatsApp messaging (via Baileys), AFIP-compliant electronic invoicing, secure video consultations (LiveKit), and AI-powered clinical assistance using Claude Opus 4.6 and Sonnet 4.6. Built with SvelteKit 5, NestJS, Prisma, and TypeScript. ## Installation ### Prerequisites ```bash # Required versions node >= 18.0.0 pnpm >= 8.0.0 postgresql >= 14.0 redis >= 7.0 ``` ### Clone and Setup ```bash git clone https://github.com/fahad-hamid/psique-workflow-clinic.git cd psique-workflow-clinic # Install dependencies pnpm install # Setup environment variables cp .env.example .env ``` ### Environment Configuration ```bash # .env DATABASE_URL="postgresql://user:password@localhost:5432/sesion" REDIS_URL="redis://localhost:6379" # Anthropic AI ANTHROPIC_API_KEY="sk-ant-..." CLAUDE_OPUS_MODEL="claude-opus-4.6" CLAUDE_SONNET_MODEL="claude-sonnet-4.6" # WhatsApp (Baileys) WHATSAPP_SESSION_DIR="./sessions" WHATSAPP_QR_TIMEOUT=60000 # LiveKit Video LIVEKIT_API_KEY="${LIVEKIT_API_KEY}" LIVEKIT_API_SECRET="${LIVEKIT_API_SECRET}" LIVEKIT_WS_URL="wss://your-livekit.cloud.livekit.cloud" # Payment Gateways MERCADOPAGO_ACCESS_TOKEN="${MERCADOPAGO_ACCESS_TOKEN}" STRIPE_SECRET_KEY="${STRIPE_SECRET_KEY}" # AFIP Integration AFIP_CUIT="${AFIP_CUIT}" AFIP_CERT_PATH="./certs/afip.crt" AFIP_KEY_PATH="./certs/afip.key" AFIP_PRODUCTION=false # Application JWT_SECRET="${JWT_SECRET}" APP_URL="http://localhost:5173" API_URL="http://localhost:3000" ``` ### Database Setup ```bash # Run migrations pnpm prisma migrate dev # Seed initial data pnpm prisma db seed ``` ### Start Development Servers ```bash # Frontend (SvelteKit) pnpm dev # Backend (NestJS) pnpm api:dev # Full stack pnpm dev:full ``` ## Architecture Overview ``` sesion/ ├── apps/ │ ├── web/ # SvelteKit 5 frontend │ ├── api/ # NestJS backend │ └── worker/ # Background job processor ├── packages/ │ ├── db/ # Prisma schema & migrations │ ├── ai/ # Claude AI orchestration │ ├── whatsapp/ # Baileys integration │ ├── video/ # LiveKit wrapper │ ├── billing/ # AFIP invoicing │ └── shared/ # Common types & utils └── prisma/ └── schema.prisma ``` ## Core Modules ### 1. Appointment Scheduling #### Create Appointment (NestJS Controller) ```typescript // apps/api/src/appointments/appointments.controller.ts import { Controller, Post, Body, UseGuards } from '@nestjs/common'; import { JwtAuthGuard } from '../auth/jwt-auth.guard'; import { AppointmentsService } from './appointments.service'; import { CreateAppointmentDto } from './dto/create-appointment.dto'; @Controller('appointments') @UseGuards(JwtAuthGuard) export class AppointmentsController { constructor(private readonly appointmentsService: AppointmentsService) {} @Post() async create(@Body() dto: CreateAppointmentDto) { return this.appointmentsService.create(dto); } } ``` #### Appointment Service with Conflict Detection ```typescript // apps/api/src/appointments/appointments.service.ts import { Injectable, ConflictException } from '@nestjs/common'; import { PrismaService } from '@sesion/db'; import { addMinutes, isWithinInterval } from 'date-fns'; @Injectable() export class AppointmentsService { constructor(private prisma: PrismaService) {} async create(dto: CreateAppointmentDto) { // Check for conflicts const conflicts = await this.prisma.appointment.findMany({ where: { practitionerId: dto.practitionerId, status: { not: 'CANCELLED' }, scheduledAt: { gte: dto.scheduledAt, lt: addMinutes(dto.scheduledAt, dto.durationMinutes || 45) } } }); if (conflicts.length > 0) { throw new ConflictException('Time slot already booked'); } // Create appointment const appointment = await this.prisma.appointment.create({ data: { ...dto, status: 'SCHEDULED' }, include: { patient: true, practitioner: true } }); // Trigger WhatsApp confirmation await this.whatsappService.sendConfirmation(appointment); return appointment; } async findUpcoming(practitionerId: string) { return this.prisma.appointment.findMany({ where: { practitionerId, scheduledAt: { gte: new Date() }, status: { in: ['SCHEDULED', 'CONFIRMED'] } }, include: { patient: true }, orderBy: { scheduledAt: 'asc' } }); } } ``` #### Frontend: Appointment Calendar (Svelte 5) ```svelte <!-- apps/web/src/routes/agenda/+page.svelte --> <script lang="ts"> import { onMount } from 'svelte'; import { goto } from '$app/navigation'; let appointments = $state([]); let selectedDate = $state(new Date()); let loading = $state(false); async function loadAppointments() { loading = true; const res = await fetch('/api/appointments/upcoming', { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); appointments = await res.json(); loading = false; } async function createAppointment(event: CustomEvent) { const res = await fetch('/api/appointments', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify(event.detail) }); if (res.ok) { await loadAppointments(); } } onMount(loadAppointments); </script> <div class="agenda-container"> <header> <h1>Agenda</h1> <button onclick={() => goto('/appointments/new')}> Nueva Cita </button> </header> {#if loading} <div class="skeleton"></div> {:else} <div class="appointments-grid"> {#each appointments as apt} <div class="appointment-card" class:urgent={apt.isUrgent}> <time>{new Date(apt.scheduledAt).toLocaleString('es-AR')}</time> <h3>{apt.patient.name}</h3> <span class="type">{apt.type}</span> <span class="status">{apt.status}</span> </div> {/each} </div> {/if} </div> ``` ### 2. WhatsApp Automation (Baileys) #### WhatsApp Connection Service ```typescript // packages/whatsapp/src/whatsapp.service.ts import makeWASocket, { DisconnectReason, useMultiFileAuthState, makeInMemoryStore } from '@whiskeysockets/baileys'; import { Boom } from '@hapi/boom'; import { Injectable, Logger } from '@nestjs/common'; @Injectable() export class WhatsAppService { private sock: any; private readonly logger = new Logger(WhatsAppService.name); async connect() { const { state, saveCreds } = await useMultiFileAuthState( process.env.WHATSAPP_SESSION_DIR || './sessions' ); this.sock = makeWASocket({ auth: state, printQRInTerminal: true }); this.sock.ev.on('creds.update', saveCreds); this.sock.ev.on('connection.update', (update) => { const { connection, lastDisconnect, qr } = update; if (qr) { this.logger.log('QR Code generated, scan with WhatsApp'); } if (connection === 'close') { const shouldReconnect = (lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut; if (shouldReconnect) { this.logger.log('Reconnecting...'); this.connect(); } } else if (connection === 'open') { this.logger.log('WhatsApp connected successfully'); } }); this.sock.ev.on('messages.upsert', async ({ messages }) => { await this.handleIncomingMessage(messages[0]); }); } async sendReminder(appointment: Appointment) { const patientNumber = appointment.patient.phone.replace(/\D/g, '') + '@s.whatsapp.net'; const message = `Hola ${appointment.patient.name}! 👋\n\n` + `Te recordamos tu sesión programada:\n` + `📅 ${format(appointment.scheduledAt, "dd/MM/yyyy 'a las' HH:mm", { locale: es })}\n` + `👨‍⚕️ Con: ${appointment.practitioner.name}\n\n` + `Por favor confirma tu asistencia respondiendo SI o NO.`; await this.sock.sendMessage(patientNumber, { text: message }); await this.prisma.whatsappMessage.create({ data: { appointmentId: appointment.id, to: patientNumber, content: message, type: 'REMINDER', status: 'SENT' } }); } async handleIncomingMessage(message: any) { if (!message.message?.conversation) return; const text = message.message.conversation.toLowerCase(); const from = message.key.remoteJid; // Check for confirmation replies if (['si', 'sí', 'confirmo'].includes(text)) { await this.confirmAppointment(from); } else if (['no', 'cancelar'].includes(text)) { await this.requestCancellation(from); } // Crisis keyword detection if (this.detectCrisisKeywords(text)) { await this.alertPractitioner(from, text); } } private detectCrisisKeywords(text: string): boolean { const crisisKeywords = [ 'suicidio', 'matarme', 'terminar con todo', 'no puedo más', 'crisis', 'emergencia' ]; return crisisKeywords.some(kw => text.includes(kw)); } } ``` #### Automated Reminder Scheduler ```typescript // apps/worker/src/jobs/reminder.job.ts import { Injectable } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; import { PrismaService } from '@sesion/db'; import { WhatsAppService } from '@sesion/whatsapp'; import { subHours } from 'date-fns'; @Injectable() export class ReminderJob { constructor( private prisma: PrismaService, private whatsapp: WhatsAppService ) {} @Cron(CronExpression.EVERY_HOUR) async send24HourReminders() { const tomorrow = new Date(); tomorrow.setHours(tomorrow.getHours() + 24); const appointments = await this.prisma.appointment.findMany({ where: { scheduledAt: { gte: tomorrow, lt: new Date(tomorrow.getTime() + 60 * 60 * 1000) // +1 hour window }, status: 'SCHEDULED', reminderSent24h: false }, include: { patient: true, practitioner: true } }); for (const apt of appointments) { await this.whatsapp.sendReminder(apt); await this.prisma.appointment.update({ where: { id: apt.id }, data: { reminderSent24h: true } }); } } } ``` ### 3. AFIP Electronic Invoicing #### Invoice Generation Service ```typescript // packages/billing/src/afip.service.ts import { Injectable } from '@nestjs/common'; import Afip from '@afipsdk/afip.js'; import { PrismaService } from '@sesion/db'; @Injectable() export class AfipService { private afip: Afip; constructor(private prisma: PrismaService) { this.afip = new Afip({ CUIT: process.env.AFIP_CUIT, cert: process.env.AFIP_CERT_PATH,
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기