VRChat 한국수어교실(KSL) 공식 스케줄 시스템. 수업 일정을 디스코드에 전 세계 시간대로 자동 표시하여, 한국 학생과 해외 학생이 같은 메시지를 보고 같은 시각을 이해할 수 있게 합니다.
이 프로젝트는 HelpingHandsVR/schedule의 코드를 기반으로 출발했습니다. 훌륭한 토대를 공개해 주신 원저작자 scarletcafe(devon) 님과 Helping Hands 커뮤니티에 깊이 감사드립니다.
This project is built on HelpingHandsVR/schedule by scarletcafe(devon). Our sincere thanks to Devon and the Helping Hands community for making the original work public — the manifest pipeline, the event-lane template design and the
old.jsonformat all originate there.
수업은 아래 세 개 반 중 하나에 속합니다. events.yaml 에서는 키로만 참조합니다.
| 키 | 표시 | 반 이름 | 내용 |
|---|---|---|---|
seed |
🌱 | 씨앗반 (입문) | 지문자, 인사말 등 수어를 처음 접하는 분 |
starlight |
⭐ | 별빛반 (단어) | 단어 위주의 손 모양과 기초 수어 단어 |
moonlight |
🌙 | 달빛반 (문장) | 문장 연결, 비수지 신호(표정), 실전 회화 |
임베드에는 이렇게 표시됩니다:
⭐ [별빛반 - 단어 · Starlight Class - Vocabulary]
수업이 아닌 모임(자유 모임, 문화 교류회 등)은
level을 생략합니다. 학급 라인이 표시되지 않습니다.
진행자 이름 옆 괄호 안에 표시됩니다. events.yaml 의 role 에 키를 적습니다.
| 키 | 표시 | 직책 | English |
|---|---|---|---|
principal |
👑 | 교장선생님 | Principal |
homeroom_teacher |
🏫 | 담임선생님 | Homeroom Teacher |
student_council_president |
🎗️ | 학생회장 | Student Council President |
appreciation_head |
🎬 | 감상부장 | Appreciation Dept Head |
exploration_head |
🧭 | 탐험부장 | Exploration Dept Head |
진행자 · Host: Korea_Yujin (🏫 담임선생님 · Homeroom Teacher)
platforms 와 hand_tracking 으로 지정하며, 학급과는 별도 줄에 표시됩니다.
| 값 | 표시 |
|---|---|
pcvr |
🖥️ PCVR |
quest |
🥽 Quest Standalone |
desktop |
💻 Desktop |
mobile |
📱 Mobile |
hand_tracking: required / recommended / supported |
🖐️ 핸드트래킹 필수 / 권장 / 지원 |
🖥️ PCVR | 🥽 Quest Standalone | 🖐️ 핸드트래킹 권장 · hand tracking recommended
디스코드 동적 타임스탬프(<t:...:f> / <t:...:R>)가 보는 사람의 시간대로 자동
표시되고, 그 아래에 아래 7개 지역을 명시합니다. 서머타임 약어(PST↔PDT, CET↔CEST)는
날짜에 따라 자동으로 바뀝니다.
🇰🇷 KST · 🇯🇵 JST · 🇺🇸 PDT · 🇺🇸 EDT · 🇪🇺 CEST · 🇦🇺 AEST · 🌐 UTC
수정할 파일은 두 개뿐입니다.
| 파일 | 무엇을 담는가 | 언제 고치나 |
|---|---|---|
templates/sign_language_ksl/events.yaml |
수업 목록, 휴강일 | 수업이 바뀔 때마다 |
templates/sign_language_ksl/meta.yaml |
반·직책·시간대·VRChat 링크 | 체계 자체가 바뀔 때 (드묾) |
events.yaml 의 events: 아래에 블록을 하나 더 붙입니다.
- host: Korea_Yujin # 진행자의 VRChat / 디스코드 이름
name: "KSL 별빛반" # 번역이 없을 때 쓰이는 대체 이름
tags: ["class", "vocabulary"]
kind: class # class | event | social | recharge
level: starlight # 🌱 seed · ⭐ starlight · 🌙 moonlight
role: homeroom_teacher # 위 직책 표의 키
title: # 임베드 제목 (한/영)
ko: 별빛반 (단어)
en: Starlight Class (Vocabulary)
description: # 한 줄 설명 (한/영)
ko: 일상에서 가장 많이 쓰는 한국수어 단어를 익힙니다.
en: Everyday Korean Sign Language vocabulary.
platforms: ["pcvr", "quest"]
hand_tracking: recommended
schedule:
basis: "2025-12-03" # ⚠️ 이 수업이 실제로 열린 날짜
day: Wednesday # basis 의 요일과 반드시 일치
hour: 21 # KST 24시간제
minute: 0
duration: 90 # 분 단위
# interval: 14 # 격주라면. 생략하면 매주(7일)
⚠️ basis와day는 반드시 일치해야 합니다. 어긋나면 빌드가 실패하며, 오류 메시지가 실제 요일을 알려 줍니다. 잘못된 시각이 조용히 게시되는 것보다 낫습니다.
⚠️ basis는 "이 수업이 실제로 열린 어느 날" 입니다. 격주 수업이 어느 주에 열리는지 판단하는 기준점이 되므로, 아무 날짜나 적으면 안 됩니다.
수업을 시간표에서 그냥 지우면 학생들은 "봇 오류로 빠진 건지, 공식 휴강인지" 알 수 없습니다. 쉬는 방법은 세 가지이고, 학생들에게 무엇이 보이는지가 다릅니다.
| 방법 | 언제 쓰나 | 학생에게 보이는 것 |
|---|---|---|
① 휴강 공지 — paused + pause_reason |
특정 수업 하나가 쉴 때 (권장) | 🚫 [휴강] |
② 재충전의 날 — closures |
그날 모든 수업이 쉴 때 (공휴일·운영진 휴무) | 🔋 재충전의 날 카드 + 쉬는 수업 목록 |
③ 조용히 숨김 — paused: true 만 |
반 운영을 장기간 중단할 때 | 아무것도 안 보임 |
pause_reason 을 적으면 수업이 시간표에 남아서 휴강으로 표시됩니다.
paused_until 을 적으면 그 날짜가 지난 뒤 자동으로 수업이 재개되므로, 나중에 파일을
다시 고칠 필요가 없습니다.
- host: gom 0703
name: "KSL 별빛반"
# ...
paused: true
pause_reason: # 이게 있으면 시간표에 [휴강]으로 남습니다
ko: 담임선생님 개인 사정
en: Teacher unavailable
paused_until: "2026-10-25" # 선택. 이 날짜까지 쉬고, 다음 수업부터 자동 재개
schedule:
# ... (그대로 둡니다)디스코드에는 이렇게 표시됩니다.
🚫 [휴강 · Cancelled] ~~별빛반 (단어) · 야간반 · Starlight Class (Vocabulary) · Night~~
-# 🕙 10월 18일 오후 10:00 · 진행자 · Host: gom 0703 (🏫 담임선생님 · Homeroom Teacher)
-# 사유 · Reason: 담임선생님 개인 사정 · Teacher unavailable
-# 2026-10-25까지 쉬고, 그다음 수업부터 정상 진행합니다 · Resumes after 2026-10-25
- 휴강된 수업은 원래 시간 자리에 그대로 표시되어, 학생이 "내 수업"을 바로 찾을 수 있습니다. 시간은 보는 사람의 시간대로 표시됩니다.
- 학급·장비·시간대 목록은 생략해 짧게 표시합니다.
- 봇의 참석 신청 / 알림 버튼에는 나오지 않으며, 이미 알림을 신청한 학생에게도 알림 DM이 가지 않습니다.
paused_until없이pause_reason만 쓰면,paused: true를 지울 때까지 매주 휴강으로 표시됩니다. 기간이 정해진 휴강이라면paused_until을 함께 적어 주세요.- 다시 수업을 시작하려면
paused,pause_reason,paused_until세 줄을 지우면 됩니다.
events.yaml 맨 아래 closures: 에 적습니다. 그날은 🔋 전용 카드로 바뀌고, 원래 그날
있던 수업이 취소선으로 나열됩니다. 통합 시간표에서도 해당 수업이 휴강으로 표시됩니다.
closures:
- date: "2026-09-24"
until: "2026-09-26" # 끝날 포함. 생략하면 하루
reason: # 선택. 왜 쉬는지
ko: 추석 연휴
en: Chuseok holiday
note: # 선택. 기본 안내 문구 대신 쓸 말
ko: 이번 주는 재충전의 날입니다. 다음 주에 만나요!
en: Back next week!디스코드에는 이렇게 표시됩니다.
🔋 Thursday (2026-09-24)
재충전의 날 · Recharging Day
-# 추석 연휴
-# Chuseok holiday
-# 이번 주는 재충전의 날입니다. 다음 주에 만나요!
-# Back next week!
-# 🚫 이날 쉬는 수업 · Off this day
-# ~~별빛반 (단어) · Starlight Class (Vocabulary)~~ · 오후 10:00
note를 생략하면 기본 문구 "오늘은 수업을 쉽니다. 푹 쉬어 주세요!" 가 나옵니다. 레인 전체의 기본 문구를 바꾸고 싶다면meta.yaml의localization.labels.recharge_note를 지정하세요.- 날짜는 한국 날짜(KST) 기준입니다.
- 설날·추석은 음력이라 해마다 날짜가 다릅니다. 매년 직접 갱신해 주세요.
paused: true # pause_reason 이 없으면 시간표에서 완전히 사라집니다학생에게는 아무것도 보이지 않습니다. 반을 한동안 운영하지 않아 매주 "휴강"이라고 띄울 필요가 없을 때만 쓰세요. 잠깐 쉬는 것이라면 ①을 쓰는 편이 학생들의 혼선이 없습니다.
pip install -r scripts/requirements.txt
# 디스코드에 보내지 않고 결과만 확인 → output/preview.json
python scripts/build_manifests.py --no-send --preview
# 테스트
python scripts/tests/test_ksl.pymain 에 푸시하면 GitHub Actions가 디스코드 #schedule 메시지를 수정합니다.
시간표는 메시지 하나를 계속 편집하는 방식이라, 자동 실행은 새 메시지를 절대 만들지
않습니다 — 첫 게시나 삭제된 메시지 복구는 Run workflow 에서 allow_create 를 켠
수동 실행으로만 합니다 (DEPLOYMENT.md 1-4).
모든 설정 항목이 주석과 함께 채워진 예시는 docs/examples/ 에 있습니다.
푸시가 없어도 워크플로가 매일 한 번, 월요일엔 두 번 더 스스로 돌아 시간표를 새 주로 넘깁니다. 단, 빌드가 빨간불인 동안에는 자동 갱신도 멈춥니다 — 실패를 오래 두지 마세요. 자세한 내용은 DEPLOYMENT.md 3-3.
Actions탭에서 빨간 실행을 열고Annotations를 봅니다.- YAML 오류라면
힌트 / Hint:줄이 고칠 줄을 알려 줍니다. 오류가 가리키는 줄보다 힌트가 가리키는 줄을 먼저 보세요 — 파서는 실수를 한두 줄 늦게 알아차립니다. - 테스트가 여러 개 한꺼번에 실패해도 원인은 대개 하나입니다. 같은 파일·같은 줄을 가리키고 있다면 그 한 곳만 고치면 됩니다.
- 빌드가 실패하면 디스코드에는 아무것도 게시되지 않습니다. 기존 시간표는 그대로 남아 있으니 서두르지 않아도 됩니다.
자주 나오는 오류와 해결법은 DEPLOYMENT.md 3-2에 표로 정리되어 있습니다.
| 빌드 (GitHub Actions) | 봇 (별도 호스팅, 선택) | |
|---|---|---|
| 실행 | 하루 몇 번 실행 후 종료 | 항상 켜져 있음 |
| 필요한 것 | 웹훅 URL | 봇 토큰 |
| 기능 | 임베드 게시·수정, 동적 타임스탬프, 다국어, 재충전의 날, 링크 버튼 | 위의 전부 + RSVP 버튼, 알림 DM, 디스코드 이벤트 연동 |
디스코드는 웹훅 메시지에 링크 버튼만 허용하기 때문에, 클릭에 응답해야 하는 기능은 살아 있는 봇 프로세스가 필요합니다. 자세한 이유는 docs/KSL_GUIDE.md 0장에 있습니다.
main 브랜치에는 템플릿과 빌드 코드가 있고, deploy 브랜치에는 GitHub Actions가 생성한
매니페스트(output/)가 올라갑니다. VRChat 월드나 외부 페이지는 deploy 쪽을 읽습니다.
- docs/KSL_GUIDE.md — 기능별 단계 가이드, 설정 항목 설명, 문제 해결
- docs/DEPLOYMENT.md — GitHub Secrets, 디스코드 봇 권한/인텐트, 시간표 반영 흐름과 빌드 실패 대처, 매주 자동 갱신 문제, 독립 저장소 전환
- docs/examples/ — 모든 설정 항목이 주석과 함께 채워진 예시 파일
| 원본에서 온 것 | KSL에서 추가한 것 |
|---|---|
이벤트 레인 템플릿 구조 (templates/, schema/) |
동적 타임스탬프 · 타임존 헬퍼 (scripts/timeutil.py, scripts/js/) |
매니페스트 파이프라인과 old.json 형식 (scripts/formats/old.py) |
한/영 이중 표기 · KSL 임베드 (scripts/embeds.py) |
| 주간 시간표 임베드의 원래 레이아웃 | 학급 체계, 직책, 재충전의 날, VRChat 연동 |
| GitHub Actions 배포 구조 | RSVP·알림·디스코드 이벤트 봇 (scripts/bot/) |
원본의 다국어 수어 레인(ASL·BSL·DGS·JSL·LSF)과 서버 통합 레인은 그대로 보존되어 있습니다. 원본이 해당 파일을 갱신하더라도 충돌 없이 반영할 수 있습니다.
⚠️ 원본 저장소에는 LICENSE 파일이 없습니다. 저장소 전체 이력을 확인했으나 라이선스 파일이 추가된 적이 없습니다.라이선스가 명시되지 않은 코드는 기본적으로 저작권자가 모든 권리를 보유합니다. GitHub 이용약관은 GitHub 내부에서의 포크는 허용하지만, 그 외의 재배포나 라이선스 부여는 허용하지 않습니다. 따라서 이 저장소는 아직 자체 LICENSE 파일을 추가하지 않았습니다 — 원저작자만이 기반 코드의 라이선스를 정할 수 있기 때문입니다.
독립 저장소로 공개 운영하기 전에 원저작자(scarletcafe(devon))에게 연락하여 (1) 원본에 라이선스를 추가해 달라고 요청하거나, (2) 이 저장소에 대한 명시적 사용 허락을 받으시기를 권합니다. 절차는 docs/DEPLOYMENT.md 4-4 절에 정리되어 있습니다.
자세한 출처 기록은 NOTICE 파일을 참고하세요.