게임 서버용 봇. 버튼만으로 게임 역할 받기 / 파티 모집 / 음성 이용시간 통계 / 포인트 상점을 모두 사용할 수 있어요. 명령어를 외울 필요가 없어요.
| 기능 | 설명 |
|---|---|
| 🎮 게임 역할 | 버튼으로 게임 역할을 받거나 뺌. 받으면 해당 게임 모집 알림이 옴 |
| ➕ 게임 추가/삭제 | 관리자가 버튼으로 게임 등록 → 역할 자동 생성. 서버 커스텀 이모지 드롭다운 선택 지원 |
| 📢 파티 모집 | 게임 선택 → 시간/인원/메모 입력 → 모집글 게시 + 역할 멘션 |
| ✋ 참가/취소 | 버튼으로 참가, 본인만 참가 취소 가능, 명단 실시간 갱신 |
| 🔒 마감 | 모집자/관리자가 마감 |
| 🔊 음성방 열기 | 모집글 버튼으로 음성방 자동 생성 (참가자만 입장 가능, 지정 카테고리 아래) |
| 📦 자동 종료 | 음성방 전원 퇴장 시 → 방 삭제 + 모집글을 아카이브 채널로 이동 + 스레드 삭제 |
| 🔊 음성 통계 | 음성채널 이용시간 자동 추적, 내 시간/주간 랭킹 조회 |
| 🪙 포인트 상점 | 음성채널 이용 시 10분당 포인트 자동 적립. 포인트로 역할 구매 |
| 📋 활동 검토 | 매주 음성시간 검토 → 기준 미달 시 DM 경고(2회) → 3회째 강퇴 후보 분류 |
| 🛡️ 안전장치 | 신규원(1주 미만)·면제역할 자동 제외, 강퇴는 관리자 버튼 승인 필수 |
| 🔓 규칙 인증 | 버튼 클릭으로 인증 역할 자동 지급 |
| 📨 익명 건의함 | 버튼 → 모달로 익명 건의 → 관리자 채널 게시. 작성자는 서버 주인만 확인 가능 |
| 🕊️ 잠수 신고 | 활동검토 면제 신청 (3일/1주/2주/1개월 또는 날짜 직접 입력). 관리자 승인식 |
| 🔐 입장 이중보안 | 카카오 오픈채팅 URL을 채널에 안 올리고, 인증 통과자에게만 본인에게만 보이는 메시지로 전달 → 초대코드 정지 회피. 카카오/디스코드 유입 경로 자동 구분. (선택) 운영자 승인제 — 신청 → 운영자가 카톡 입장 확인 후 승인 |
| 🚫 블랙리스트 | 디스코드 고유 ID 기반 차단. 이미 나간 유저도 ID로 등록해 재입장 차단(강퇴/밴 토글, 네이티브 밴 옵션) |
| 👀 관전 모드 | 전용 패널에서 [관전 모드 켜기] → 닉 "관전" 표시 + 모든 파티 음성방 자유 입장(정원 무관). 참가 중이면 자동 탈퇴(모집자는 자동 위임), 음성 퇴장 시 자동 OFF |
| ✏️ 셀프 닉네임 | 서버가 별명 변경을 막아둬도 봇이 대신 변경. old→new 변경 이력 기록·조회 |
| 📌 스티키 공지 | 운영자 공지를 채널 맨 아래에 항상 고정 (새 글이 올라오면 자동 재게시) |
- https://discord.com/developers/applications 접속 → New Application
- 왼쪽 Bot 탭 → Reset Token → 토큰 복사 (이게
DISCORD_TOKEN) - 같은 화면에서 Privileged Gateway Intents 두 개 켜기:
- ✅ Server Members Intent
- ✅ Presence Intent 는 불필요, 끄세요
- 왼쪽 OAuth2 → URL Generator:
- Scopes:
bot,applications.commands - Bot Permissions:
Manage Roles,Manage Channels,Move Members,Kick Members,Send Messages,Embed Links,Read Message History,Use Slash Commands,Manage Nicknames,Manage Server,Manage Messages,Ban Members - 생성된 URL로 봇을 서버에 초대
- Scopes:
⚠️ 봇 역할이 게임 역할·관리 대상 멤버보다 위에 있어야 역할/닉네임 부여가 동작해요.⚠️ Manage Channels+Move Members권한이 있어야 음성방 자동 생성/삭제가 동작해요.⚠️ Kick Members권한은 활동검토 강퇴·블랙리스트 강퇴에 필요해요.⚠️ Manage Nicknames= 관전 표시·셀프 닉네임 변경,Manage Server= 입장 경로(초대코드) 추적,Manage Messages= 스티키 재게시,Ban Members= 블랙리스트 네이티브 밴 옵션. 권한이 없어도 봇은 죽지 않고 안내만 띄워요(graceful). ✅ 이번 기능들은 모두 버튼/패널로 동작해 새 슬래시 명령이 없어요 →SYNC_COMMANDS재배포 불필요.
이 봇은 Supabase(PostgreSQL) 에 데이터를 저장해요. Railway 재배포 시에도 데이터가 보존돼요.
- https://supabase.com → 로그인 → New Project
- 프로젝트 이름·DB 비밀번호 설정 후 생성 (1~2분 소요)
- Supabase 대시보드 → 프로젝트 선택 → Project Settings → Database
- Connection string → Transaction pooler 의 URI 복사
- 형식:
postgresql://postgres.PROJECT_REF:비밀번호@aws-0-ap-northeast-2.pooler.supabase.com:6543/postgres [YOUR-PASSWORD]부분을 DB 생성 시 정한 비밀번호로 교체- 비밀번호를 잊었으면 Reset database password 로 재설정
- 형식:
- 이 문자열 전체를 환경변수
DATABASE_URL에 넣어요
⚠️ 포트6543(Transaction pooler) 를 사용해야 해요. Direct connection(5432)은 봇 재시작 시 연결이 끊길 수 있어요.
Supabase 대시보드 → SQL Editor 에 아래 SQL 전체를 붙여넣고 Run 하세요.
IF NOT EXISTS 구문이라 이미 테이블이 있어도 안전하게 재실행할 수 있어요.
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- 파티 모집 봇 — 전체 스키마 (처음 설치용)
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- ─── 게임 목록 ───────────────────────────────────
CREATE TABLE IF NOT EXISTS games (
guild_id BIGINT NOT NULL,
name TEXT NOT NULL,
emoji TEXT,
role_id BIGINT,
PRIMARY KEY (guild_id, name)
);
-- ─── 파티 모집글 ─────────────────────────────────
CREATE TABLE IF NOT EXISTS recruits (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT NOT NULL,
channel_id BIGINT NOT NULL,
message_id BIGINT,
host_id BIGINT NOT NULL,
game_name TEXT,
play_time TEXT,
max_players INTEGER,
note TEXT,
status TEXT NOT NULL DEFAULT 'open', -- open | closed | archived
voice_channel_id BIGINT,
temp_role_id BIGINT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 방치 모집글 자동정리 기준 (013)
);
-- ─── 파티 참가자 ─────────────────────────────────
CREATE TABLE IF NOT EXISTS participants (
recruit_id BIGINT NOT NULL REFERENCES recruits(id) ON DELETE CASCADE,
user_id BIGINT NOT NULL,
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (recruit_id, user_id)
);
-- ─── 음성채널 현재 세션 (접속 중 추적용) ──────────
CREATE TABLE IF NOT EXISTS voice_sessions (
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
joined_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (guild_id, user_id)
);
-- ─── 음성채널 누적 통계 ──────────────────────────
CREATE TABLE IF NOT EXISTS voice_totals (
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
total_seconds BIGINT NOT NULL DEFAULT 0,
week_seconds BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY (guild_id, user_id)
);
-- ─── 길드별 설정 ─────────────────────────────────
CREATE TABLE IF NOT EXISTS guild_settings (
guild_id BIGINT PRIMARY KEY,
voice_category_id BIGINT, -- 음성방 생성 카테고리
archive_channel_id BIGINT, -- 종료된 모집글 보관 채널
review_log_channel BIGINT, -- 활동검토 결과 로그 채널
exempt_role_id BIGINT, -- 활동검토 면제 역할
min_seconds INTEGER NOT NULL DEFAULT 10800, -- 주간 최소 음성시간(초), 기본 3시간
auto_kick_enabled INTEGER NOT NULL DEFAULT 0, -- 강퇴 승인 모드 on/off
panel_manager_role BIGINT, -- /패널 명령어 허용 역할
verified_role_id BIGINT, -- 규칙 인증 시 지급 역할
last_reviewed_at TIMESTAMPTZ, -- 마지막 활동검토 실행 시각
recruit_post_channel_id BIGINT, -- 모집글 전용 게시 채널
points_per_10min INTEGER NOT NULL DEFAULT 2, -- 10분당 지급 포인트
points_excluded_channel_id BIGINT, -- 포인트 미지급 채널 (잠수채널)
nickname_log_channel_id BIGINT, -- 닉네임 변경 로그 채널 (007)
blacklist_ban_on_join SMALLINT NOT NULL DEFAULT 0, -- 재입장 차단: 0=강퇴 1=밴 (009)
blacklist_notify SMALLINT NOT NULL DEFAULT 1, -- 차단 전 DM 안내 여부 (009)
openchat_url TEXT, -- 카카오 오픈채팅 URL (채널 미게시, ephemeral 전용) (010)
openchat_gate_role_id BIGINT, -- 오픈채팅 게이트 통과 역할 (010)
kakao_invite_code TEXT, -- 카카오 유입용 초대코드 (010)
entry_marker_role_id BIGINT, -- 카카오 유입자 마커 역할 (010)
openchat_approval_required SMALLINT NOT NULL DEFAULT 0, -- 오픈채팅 운영자 승인제 on/off (012)
openchat_request_channel_id BIGINT, -- 오픈채팅 신청을 받을 운영자 채널 (012)
spectator_role_id BIGINT -- 관전자 역할 (자동 생성, 관전 모드용) (013)
);
-- ─── 활동 경고 누적 ──────────────────────────────
CREATE TABLE IF NOT EXISTS activity_warnings (
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
strikes INTEGER NOT NULL DEFAULT 0,
last_review TIMESTAMPTZ,
PRIMARY KEY (guild_id, user_id)
);
-- ─── 익명 건의함 ─────────────────────────────────
CREATE TABLE IF NOT EXISTS suggestions (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT NOT NULL,
author_id BIGINT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
public_msg_id BIGINT
);
-- ─── 잠수 신고 ───────────────────────────────────
CREATE TABLE IF NOT EXISTS leave_notices (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
reason TEXT,
until_date DATE NOT NULL,
status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | rejected | expired
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
reviewed_by BIGINT,
reviewed_at TIMESTAMPTZ
);
-- ─── 포인트 잔액 ─────────────────────────────────
CREATE TABLE IF NOT EXISTS user_points (
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
points BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY (guild_id, user_id)
);
-- ─── 포인트 상점 역할 ─────────────────────────────
CREATE TABLE IF NOT EXISTS shop_roles (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
cost INTEGER NOT NULL CHECK (cost > 0),
label TEXT,
UNIQUE (guild_id, role_id)
);
-- ─── 닉네임 변경 이력 (007) ──────────────────────
CREATE TABLE IF NOT EXISTS nickname_history (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
old_nick TEXT,
new_nick TEXT,
changed_by BIGINT NOT NULL,
changed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- ─── 관전자 (008) ────────────────────────────────
CREATE TABLE IF NOT EXISTS spectators (
recruit_id BIGINT NOT NULL REFERENCES recruits(id) ON DELETE CASCADE,
user_id BIGINT NOT NULL,
original_nick TEXT,
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (recruit_id, user_id)
);
-- ─── 블랙리스트 (009) ────────────────────────────
CREATE TABLE IF NOT EXISTS blacklist (
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
reason TEXT,
added_by BIGINT,
native_ban SMALLINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (guild_id, user_id)
);
-- ─── 멤버 입장 경로 (010) ────────────────────────
CREATE TABLE IF NOT EXISTS member_entry (
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
invite_code TEXT,
route_label TEXT NOT NULL DEFAULT 'unknown', -- discord | kakao | vanity | unknown
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (guild_id, user_id)
);
-- ─── 스티키 공지 (011) ───────────────────────────
CREATE TABLE IF NOT EXISTS sticky_messages (
guild_id BIGINT NOT NULL,
channel_id BIGINT NOT NULL,
title TEXT,
content TEXT NOT NULL,
image_url TEXT,
last_message_id BIGINT,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_by BIGINT,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (guild_id, channel_id)
);
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- 인덱스
-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CREATE INDEX IF NOT EXISTS idx_recruits_guild_status
ON recruits (guild_id, status);
CREATE INDEX IF NOT EXISTS idx_recruits_voice_channel
ON recruits (voice_channel_id)
WHERE voice_channel_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_suggestions_public_msg
ON suggestions (public_msg_id)
WHERE public_msg_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_leave_notices_active
ON leave_notices (guild_id, user_id, status, until_date);
CREATE INDEX IF NOT EXISTS idx_user_points_guild
ON user_points (guild_id);
CREATE INDEX IF NOT EXISTS idx_shop_roles_guild
ON shop_roles (guild_id, cost);
CREATE INDEX IF NOT EXISTS idx_nickname_history_guild_user
ON nickname_history (guild_id, user_id, changed_at DESC);
CREATE INDEX IF NOT EXISTS idx_spectators_recruit ON spectators (recruit_id);
CREATE INDEX IF NOT EXISTS idx_spectators_user ON spectators (user_id);
CREATE INDEX IF NOT EXISTS idx_blacklist_guild ON blacklist (guild_id);
CREATE INDEX IF NOT EXISTS idx_member_entry_route
ON member_entry (guild_id, route_label);
CREATE INDEX IF NOT EXISTS idx_sticky_messages_enabled
ON sticky_messages (guild_id)
WHERE enabled = TRUE;
-- ─── 오픈채팅 입장 신청 (012) ───────────────────
CREATE TABLE IF NOT EXISTS openchat_requests (
id BIGSERIAL PRIMARY KEY,
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | rejected
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
reviewed_by BIGINT,
reviewed_at TIMESTAMPTZ,
review_msg_id BIGINT
);
CREATE INDEX IF NOT EXISTS idx_openchat_requests_pending
ON openchat_requests (guild_id, status);
-- ─── 전역 관전 모드 (013) ────────────────────────
CREATE TABLE IF NOT EXISTS spectator_mode (
guild_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
original_nick TEXT,
started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (guild_id, user_id)
);
-- 참고: 구버전 `spectators` 테이블(008)은 전역 관전 모드로 대체되어 더 이상 사용하지 않습니다.| 테이블 | 역할 |
|---|---|
games |
서버별 게임 목록 (이름·이모지·연결 역할 ID) |
recruits |
파티 모집글 (게임명·인원·시간·상태) |
participants |
모집글별 참가자 목록 |
voice_sessions |
현재 음성채널에 접속 중인 세션 (입장 시각 기록용, 봇 재시작 시 복원) |
voice_totals |
유저별 음성 이용시간 누적 (전체 / 이번 주) |
guild_settings |
서버별 모든 설정값 (채널·역할·검토 기준·포인트 설정 등) |
activity_warnings |
활동검토 경고 누적 횟수 |
suggestions |
익명 건의 내용 (author_id는 서버 주인만 조회 가능) |
leave_notices |
잠수 신고 내역 (기간·사유·승인 상태) |
user_points |
서버별 유저 포인트 잔액 |
shop_roles |
포인트로 구매 가능한 역할과 가격 |
nickname_history |
닉네임 변경 이력 (old→new, 변경자·시각) |
spectators |
(구버전·미사용) 모집별 관전자. 013에서 전역 spectator_mode로 대체 |
spectator_mode |
전역 관전 모드 상태 (켠 사람·원본 닉) |
blacklist |
디스코드 ID 기반 차단 목록 (사유·등록자·네이티브밴 여부) |
member_entry |
멤버 입장 경로 기록 (kakao/discord/vanity/unknown) |
sticky_messages |
채널별 스티키 공지 (내용·현재 메시지 ID·on/off) |
openchat_requests |
오픈채팅 입장 신청 (승인제 ON일 때 운영자 승인 대기열) |
처음 설치라면 2-3의 전체 스키마 SQL만 실행하면 돼요.
이미 운영 중인 봇에 업데이트를 적용할 때는 migrations/ 폴더의 파일을 순서대로 실행하세요.
| 파일 | 적용 내용 |
|---|---|
002_add_suggestions_and_leave_notices.sql |
익명 건의함·잠수 신고 테이블 추가 |
003_indexes_and_idempotency.sql |
성능 인덱스 추가, last_reviewed_at 컬럼 추가 |
004_points_and_shop.sql |
user_points·shop_roles 테이블 추가, points_per_10min 컬럼 추가 |
005_points_afk_channel.sql |
points_excluded_channel_id (잠수채널) 컬럼 추가 |
006_rename_points_per_hour.sql |
points_per_hour → points_per_10min rename, 기존 값 단위 변환 |
007_self_nickname.sql |
nickname_history 테이블 + nickname_log_channel_id 컬럼 |
008_spectators.sql |
spectators 테이블 (관전 기능) |
009_blacklist.sql |
blacklist 테이블 + blacklist_ban_on_join·blacklist_notify 컬럼 |
010_entry_dual_security.sql |
member_entry 테이블 + 오픈채팅/카카오 관련 4개 컬럼 |
011_sticky_messages.sql |
sticky_messages 테이블 (스티키 공지) |
012_openchat_approval.sql |
openchat_requests 테이블 + 오픈채팅 운영자 승인제 컬럼 2개 |
013_spectator_mode.sql |
spectator_mode 테이블 + spectator_role_id·recruits.created_at (전역 관전 모드) |
002~005,007~011은IF NOT EXISTS/ADD COLUMN IF NOT EXISTS구문이라 재실행해도 안전해요.⚠️ 단006은 값 변환(÷6)이 들어 있어 한 번만 실행해야 해요. 다시 실행하면 포인트가 또 나눠져요. (각 마이그레이션은 순서대로 1회씩만 실행하면 됩니다.)
pip install -r requirements.txt
export DISCORD_TOKEN="봇_토큰"
export DATABASE_URL="postgresql://postgres.xxx:비밀번호@...pooler.supabase.com:6543/postgres"
python bot.py봇이 켜지면 채널에서 슬래시 명령어로 패널을 설치하세요:
| 명령어 | 설치되는 패널 |
|---|---|
/패널 종류:파티모집 |
파티 모집 패널 |
/패널 종류:게임역할 |
게임 역할 패널 |
/패널 종류:내활동 |
음성시간·익명건의·잠수신고 패널 |
/패널 종류:관리자 |
서버 설정 관리자 패널 |
/포인트상점 |
포인트 상점 유저 패널 |
/포인트상점설정 |
포인트 상점 관리자 패널 |
/인증패널 |
규칙 인증 버튼 패널 |
슬래시 명령어가 보이지 않을 때: 환경변수
SYNC_COMMANDS=true설정 후 1회 재시작, 동기화 완료 후false로 되돌리세요.
초기 설정 순서:
- 관리자 패널 → ➕ 게임 추가 — 게임 이름 + 이모지 등록
- 관리자 패널 → 채널 설정 — 음성방 카테고리, 아카이브 채널, 모집글 채널 지정
/포인트상점설정→ 🪙 10분당 포인트 — 포인트 적립 비율 설정 (기본 2포인트/10분)/포인트상점설정→ ➕ 역할 추가 — 구매 가능한 역할과 가격 등록
- 이 폴더를 GitHub 저장소로 push
- https://railway.app → New Project → Deploy from GitHub repo
- 저장소 선택
- Variables 탭에서 환경변수 두 개 추가:
DISCORD_TOKEN= 봇 토큰DATABASE_URL= Supabase 연결 문자열
- 자동으로
Procfile의worker: python bot.py가 실행됨
데이터는 Supabase에 있으니 재배포해도 게임 목록·포인트·경고·음성기록이 모두 보존돼요.
discord-party-bot/
├── bot.py # 메인 엔트리, Cog 로드, 영구 View 복원
├── database.py # PostgreSQL(Supabase) 헬퍼 — asyncpg 풀
├── cogs/
│ ├── control_panel.py # 관리자 패널 (채널·역할·활동검토 설정)
│ ├── recruitment.py # 파티 모집 (모달, 참가/취소/마감 버튼)
│ ├── game_roles.py # 게임 추가/삭제, 역할 토글, 커스텀 이모지 선택
│ ├── voice_stats.py # 음성 이용시간 추적 + 랭킹 (잠수채널 제외 처리)
│ ├── point_shop.py # 포인트 상점 (적립·구매·관리자 설정)
│ ├── activity_review.py # 주간 활동검토 자동화
│ ├── verification.py # 규칙 인증 + 오픈채팅 게이트 버튼
│ ├── suggestions.py # 익명 건의함
│ ├── leave_notices.py # 잠수 신고
│ ├── nicknames.py # 셀프 닉네임 변경 + 변경 이력
│ ├── nick_util.py # 닉네임 접두사·복원 공용 헬퍼 (관전/셀프닉 공유)
│ ├── blacklist.py # 블랙리스트 (ID 차단, on_member_join)
│ ├── entry_tracking.py # 입장 경로 구분 + 오픈채팅 게이트 설정
│ └── sticky.py # 스티키 공지 (채널 하단 고정)
├── migrations/
│ ├── 002_add_suggestions_and_leave_notices.sql
│ ├── 003_indexes_and_idempotency.sql
│ ├── 004_points_and_shop.sql
│ ├── 005_points_afk_channel.sql
│ ├── 006_rename_points_per_hour.sql
│ ├── 007_self_nickname.sql
│ ├── 008_spectators.sql
│ ├── 009_blacklist.sql
│ ├── 010_entry_dual_security.sql
│ ├── 011_sticky_messages.sql
│ ├── 012_openchat_approval.sql
│ └── 013_spectator_mode.sql
├── requirements.txt
├── Procfile # Railway 실행 설정 (worker: python bot.py)
├── runtime.txt
└── .env.example
모집 채널을 봇 전용으로 만들려면 채널 설정 → 권한에서:
@everyone→ 메시지 보내기 ❌- 봇 역할 → 메시지 보내기 ✅
관리자 패널·포인트 상점 설정 패널은 관리자만 보이는 채널에 설치하세요.
설정 (관리자 패널 → 📋 활동검토 설정):
- 📑 로그 채널 — 검토 결과를 올릴 관리자 전용 채널 (필수)
- 🕐 기준 시간 — 주간 최소 음성시간 (기본 3시간)
- 🛡️ 면제 역할 — 이 역할 보유자는 검토에서 제외
- 🔁 자동강퇴 모드 — 켜면 강퇴 후보에 승인 버튼이 붙음
동작 흐름:
- 매주 월요일 자동 실행 (또는
▶️ 지금 활동검토 실행) - 자동 제외: 봇 / 면제 역할 / 가입 1주 미만 신규원
- 기준 미달자 → 경고 1회 누적 + DM 발송
- 연속 3회 미달 → 강퇴 후보 분류 (관리자가 드롭다운으로 직접 승인해야 실행)
- 강퇴는 kick 방식 (초대 링크로 재입장 가능, 밴 아님)
- 음성채널에 5분 이상 체류 후 퇴장 시 포인트 자동 적립
- 잠수채널 / Discord AFK 채널 체류 시간은 포인트 미적립
- 잠수채널로 이동 시 이동 전 일반채널 시간만 정산
| 버튼 | 기능 |
|---|---|
| 🪙 10분당 포인트 | 10분 체류당 지급 포인트 설정 (기본 2) |
| ➕ 역할 추가 | 역할 선택 → 가격 입력 → 상점 등록 |
| 🗑️ 역할 제거 | 상점에서 역할 삭제 |
| 📋 현황 보기 | 설정·등록 역할·잠수채널 전체 조회 |
| 🔇 잠수채널 설정 | 포인트 미지급 채널 지정·해제 |
현재는 관리자가 🔄 주간 랭킹 초기화 버튼으로 수동 초기화해요.
매주 자동 초기화를 원하면 discord.ext.tasks 로 스케줄러를 추가할 수 있어요.