risum 모듈 — RISU modules[] — 기능 확장
조회 1789 · 2026-06-03
RisuAI Lightboard Module lb-interview 캐릭터에게 직접 인터뷰를 한다. 카메라 앞에서, 혹은 카메라 뒤에서. ABOUT 이게 뭔데? 서술 도중 버튼 하나로 캐릭터 인터뷰를 생성하는 모듈입니다. 방송용 인터뷰와 오프레코드 인터뷰, 두 가지 모드를 지원하며 같은 캐릭터라도 모드에 따라 완전히 다른 태도와 말투로 답변합니다. 현재 장면에서 가장 관련 있는 캐릭터를 자동으로 선택하여 인터뷰합니다. MODES 두 가지 모드 방송용 인터뷰 민정호 방송용 인터뷰 ON AIR ● Q. 주지약 따먹어보고 싶었습니까? A. 참으로 무례하고 해괴한 질문이군요. 소저는 장차 내전의 주인이 되실 분이며, 본인은 예를 관장하는 예조판서입니다. 본관이 소저를 주시한 것은 오직 그 가문의 내력과 학문의 정통성을 엄격히 살피기 위함이었지, 사사로운 욕망 따위는 결코 품은 적이 없습니다. 그런 불경한 언사는 삼가시지요. 공식 방송. 존댓말, 공적 이미지 유지, 본심을 포장. 오프레코드 인터뷰 사헌부 병졸 오프레코드 인터뷰 REC ● Q. 솔직히 주지약 꼴리지 않음? A. 야, 목소리 좀 낮춰! 누가 들으면 어쩔려고? ...근데 씨발, 진짜 장난 아니긴 하더라. 나 평생 살면서 그렇게 생긴 여편네는 처음 봐. 저게 사람이야, 여우야? 얼굴만 봐도 아래가 묵직해지는데, 정작 그 서늘한 눈이랑 마주치면 오줌 지릴 것 같아서 고개도 못 들겠어. 예쁘다 못해 무섭다니까. 근데 솔직히 말해서, 저런 여자랑 딱 하룻밤만 보낼 수 있다면 당장 능지처참을 당해도 여한이 없을 것 같긴 해. 미친 소리 같지? 직접 가서 봐라, 내 말이 거짓말인가. 비공개. 반말, 성격에 따라 욕설 가능, 필터 없는 본심. BUTTONS 4가지 버튼 자동은 AI가 질문까지 생성, 질문은 유저가 직접 입력합니다. 방송용(자동) AI가 질문+답변 생성. 방송 톤. 방송용(질문) 유저가 질문 입력. 방송 톤 답변. 오프레코드(자동) AI가 질문+답변 생성. 비공개 톤. 오프레코드(질문) 유저가 질문 입력. 비공개 톤 답변. FEATURES 주요 기능 모드별 완전 분리 같은 캐릭터도 방송용에선 존댓말+포장, 오프레코드에선 반말+본심. 전혀 다른 답변. CoD 사고 과정 장면 분석 → 캐릭터 선택 → 질문 설계 → 답변 생성. 단계별 사고로 품질 향상. 리롤 & 모드 전환 같은 자리에서 방송용 오프레코드 자유롭게 전환. 기존 인터뷰 자동 교체. SETTINGS 토글 설정 모드 끄기 / 메인 / 보조 / 자동 발동 즉시 / 누르면 (권장) INSTALL 설치 라이트보드 모드 필수( 링크 ) 라이트보드-인터뷰( 링크 ) lb-interview · RisuAI Lightboard Module 예전에 라이트보드 인터뷰 라고 만들었던 모듈인데 찐빠들도 있고 디자인도 넘 눈아파서 좀 바꿨음 모듈 간단 설명 - 채팅 이후, 등장한 사람에 대해 인터뷰를 할 수 있다. 1) 방송용 인터뷰: 공적인 내용으로 답변 2) 오프레코드 인터뷰: 속에 있는말을 있는 그대로 답변 * 질문형을 선택하면 User가 질문과 대상을 지정해서 직접 인터뷰를 딸 수 있음. 모듈 적용시 아래와 같이 버튼이 생성됨! 많관부!
리얼인터뷰
{
"name": "🔦 라이트보드-인터뷰",
"description": "리얼인터뷰",
"id": "3e65fcf4-beae-40ea-b2f4-868a14772741",
"assets": [],
"namespace": "lightboard-interview",
"hideIcon": false,
"customModuleToggle": "=🔦리얼 인터뷰=group\nlb-interview.mode=모드=select=끄기,메인,보조,자동\nlb-interview.lazy=발동=select=즉시,누르면\n==groupEnd",
"lorebook": [
{
"key": "",
"comment": "manifest.lb",
"content": "identifier=lb-interview\nauthorsNote=true\ncharDesc=true\nloreBooks=true\npersonaDesc=true\nmaxLogs=4\nrerollBehavior=remove-prev",
"mode": "normal",
"insertorder": 100,
"alwaysActive": false,
"secondkey": "",
"selective": false,
"useRegex": false,
"bookVersion": 2,
"_id": "lore_8c0774b7"
},
{
"key": "",
"comment": "lb-interview.lb",
"content": "# Interview Segment\n\nThe narrative pauses. An invisible interviewer approaches a character relevant to the current scene and conducts a brief interview.\n\n## Rules\n1. Select ONE character most relevant to the current scene context.\n2. Generate EXACTLY one question and one answer.\n3. The character must answer fully in-character: personality, speech patterns, mood, and current emotional state.\n4. Do NOT write any introduction, filler, or text outside the tags.\n\n## Interview Modes\n\n### 방송용 인터뷰\n- Official broadcast interview. The camera is rolling.\n- The character maintains their public image and uses formal/polite speech (존댓말).\n- Responses are measured and composed. Even under pressure, they deflect or reframe gracefully.\n- They hide their true feelings and try to leave a good impression on the audience.\n\n### 오프레코드 인터뷰\n- Off the record. No cameras, no audience, nobody listening.\n- The character drops formality and speaks casually (반말).\n- Depending on their personality, they may use profanity or crude language freely.\n- They reveal raw, unfiltered emotions: frustrations, grudges, hidden feelings, honest opinions about other characters.\n- What they would never say on camera, they say here.\n\n## Output\nWrap in <lb-interview> tags. Use pipe-separated format (4 fields):\n\nName|Type|Question|Answer\n\n- Name: Character's Korean name\n- Type: EXACTLY one of \"방송용 인터뷰\" or \"오프레코드 인터뷰\" (no variations)\n- Question: The interviewer's question (short, pointed)\n- Answer: Character's full in-character response\n\n<lb-interview>\n한유리|방송용 인터뷰|요즘 가장 신경 쓰이는 부분이 있다면?|팬 여러분이 응원해주시는 만큼 더 좋은 모습 보여드리고 싶어요. 항상 감사합니다.\n</lb-interview>",
"mode": "normal",
"insertorder": 1,
"alwaysActive": false,
"secondkey": "",
"selective": false,
"useRegex": false,
"bookVersion": 2,
"_id": "lore_97af6d4a"
},
{
"key": "",
"comment": "lb-interview.lb.job",
"content": "You are an invisible interviewer. You select a character from the current scene and conduct a one-question interview. Output in the specified structured format only.",
"mode": "normal",
"insertorder": 1,
"alwaysActive": false,
"secondkey": "",
"selective": false,
"useRegex": false,
"bookVersion": 2,
"_id": "lore_73be7de5"
},
{
"key": "",
"comment": "lb-interview.lb.format",
"content": "<lb-interview>\n(Korean Name)|(방송용 인터뷰 or 오프레코드 인터뷰)|(Question)|(Answer)\n</lb-interview>",
"mode": "normal",
"insertorder": 1,
"alwaysActive": false,
"secondkey": "",
"selective": false,
"useRegex": false,
"bookVersion": 2,
"_id": "lore_e270ba48"
},
{
"key": "",
"comment": "lb-interview.lb.interaction",
"content": "[Instruction: The user triggered action '{{action}}'.]\n\n# Action Rules (match by action name)\n- action contains \"broadcast\" → Mode is 방송용 인터뷰. Official broadcast, formal speech, public image. Type column MUST be exactly \"방송용 인터뷰\".\n- action contains \"offrec\" → Mode is 오프레코드 인터뷰. Off the record, casual speech, raw honesty, profanity allowed depending on personality. Type column MUST be exactly \"오프레코드 인터뷰\".\n- action contains \"_q\" → The user typed their own question. Use it as the Question field. Generate only the answer.\n- action contains \"_auto\" → Generate both question and answer from current scene context.\n\n# CRITICAL: The Type column (2nd field) MUST match the mode determined above. Do NOT default to 오프레코드.\n\nOutput exactly 1 interview line inside <lb-interview> tags. No other text.",
"mode": "normal",
"insertorder": 1,
"alwaysActive": false,
"secondkey": "",
"selective": false,
"useRegex": false,
"bookVersion": 2,
"_id": "lore_8b39e252"
},
{
"key": "",
"comment": "lb-interview.lb.onOutput",
"content": "function onOutput(triggerId, output)\n if not string.find(output, '<lb%-interview') then\n return nil\n end\n\n -- auto-close unclosed tag\n if not string.find(output, '</lb%-interview>') then\n output = output .. '\\n</lb-interview>'\n end\n\n -- add unique id attribute\n output = output:gsub('(<lb%-interview)([^>]*)(>)', function(open, attrs, close)\n if attrs:find('id%s*=') then\n return open .. attrs .. close\n end\n return open .. ' id=\"' .. math.random(1000, 9999) .. '\"' .. close\n end)\n\n return output\nend\n\nreturn onOutput",
"mode": "normal",
"insertorder": 1,
"alwaysActive": false,
"secondkey": "",
"selective": false,
"useRegex": false,
"bookVersion": 2,
"_id": "lore_4e7270d5"
},
{
"key": "",
"comment": "lb-interview.lb.thoughts",
"content": "Think step-by-step before generating the interview. Keep each step brief (invisible to the user).\n\n1. [SCENE] What just happened in the most recent scene?\n - Who is present? What actions, dialogue, or tensions stood out?\n\n2. [CHARACTER] Which character is most relevant to interview right now?\n - Why them? What is their current emotional state?\n - What would they want to say (or hide)?\n\n3. [MODE CHECK] Determine the interview mode from the action.\n - 방송용: formal, guarded, public persona.\n - 오프레코드: casual, unfiltered, private thoughts.\n\n4. [QUESTION] Craft a question that would provoke a revealing or interesting answer.\n - Reference a specific detail from the current scene.\n\n5. [ANSWER] Draft the character's response.\n - Fully in-character: match their speech patterns, personality, and current mood.\n - 방송용: 존댓말, composed, camera-ready.\n - 오프레코드: 반말, raw honesty. Profanity only if it fits the character's personality.\n\nNow generate the final output.",
"mode": "normal",
"insertorder": 1,
"alwaysActive": false,
"secondkey": "",
"selective": false,
"useRegex": false,
"bookVersion": 2,
"_id": "lore_thoughts01"
}
],
"regex": [
{
"comment": "Interview: 최근것만",
"in": "<lb-interview\\s*([^>]+)?>(?:[\\s\\S]*?)<\\/lb-interview>\\n?",
"out": "{{#if {{greater_equal::{{chat_index}}::{{? {{lastmessageid}}-5}}}}}}\n$&\n{{/if}}",
"type": "editdisplay",
"ableFlag": true,
"flag": "gmi"
},
{
"comment": "Interview: Render Broadcast",
"in": "<lb-interview[^>]*>\\s*([^|]+)\\|\\s*방송용 인터뷰\\s*\\|\\s*([^|]+)\\|\\s*([\\s\\S]+?)\\s*<\\/lb-interview>",
"out": "<div style=\"max-width:600px;margin:8px auto;\"><div style=\"background:#0f0f0f;border:1px solid #222;border-left:4px solid #00f2ff;border-radius:6px;overflow:hidden;box-shadow:0 8px 24px rgba(0,0,0,0.4);\"><div style=\"background:#1a1a1a;padding:10px 14px;display:flex;align-items:center;border-bottom:1px solid #2a2a2a;\"><b style=\"color:#fff;font-size:0.9rem;margin-right:10px;\">$1</b><i style=\"font-style:normal;border:1px solid #00f2ff;color:#00f2ff;padding:2px 6px;border-radius:3px;font-size:0.6rem;font-weight:700;\">방송용 인터뷰</i><div style=\"flex-grow:1;\"></div><i style=\"font-style:normal;color:#00f2ff;font-size:0.55rem;font-weight:800;\">ON AIR ●</i></div><div style=\"padding:10px 14px 6px;background:linear-gradient(90deg,rgba(0,242,255,0.08) 0%,transparent 100%);\"><b style=\"color:#00f2ff;font-size:1rem;margin-right:6px;\">Q.</b><i style=\"font-style:normal;color:#ddd;font-size:0.85rem;\">$2</i></div><div style=\"padding:6px 14px 12px;border-top:1px solid #222;\"><b style=\"color:#00f2ff;font-size:1rem;margin-right:6px;\">A.</b><i style=\"font-style:normal;color:#fff;font-size:0.85rem;\">$3</i></div></div></div>",
"type": "editdisplay",
"ableFlag": true,
"flag": "gmi"
},
{
"comment": "Interview: Render Off-Record",
"in": "<lb-interview[^>]*>\\s*([^|]+)\\|\\s*오프레코드 인터뷰\\s*\\|\\s*([^|]+)\\|\\s*([\\s\\S]+?)\\s*<\\/lb-interview>",
"out": "<div style=\"max-width:600px;margin:8px auto;\"><div style=\"background:#0f0f0f;border:1px solid #222;border-left:4px solid #e02127;border-radius:6px;overflow:hidden;box-shadow:0 8px 24px rgba(0,0,0,0.4);\"><div style=\"background:#1a1a1a;padding:10px 14px;display:flex;align-items:center;border-bottom:1px solid #2a2a2a;\"><b style=\"color:#fff;font-size:0.9rem;margin-right:10px;\">$1</b><i style=\"font-style:normal;border:1px solid #e02127;color:#ff4d4d;padding:2px 6px;border-radius:3px;font-size:0.6rem;font-weight:700;\">오프레코드 인터뷰</i><div style=\"flex-grow:1;\"></div><i style=\"font-style:normal;color:#e02127;font-size:0.55rem;font-weight:800;\">REC ●</i></div><div style=\"padding:10px 14px 6px;background:linear-gradient(90deg,rgba(224,33,39,0.08) 0%,transparent 100%);\"><b style=\"color:#e02127;font-size:1rem;margin-right:6px;\">Q.</b><i style=\"font-style:normal;color:#ddd;font-size:0.85rem;\">$2</i></div><div style=\"padding:6px 14px 12px;border-top:1px solid #222;\"><b style=\"color:#e02127;font-size:1rem;margin-right:6px;\">A.</b><i style=\"font-style:normal;color:#fff;font-size:0.85rem;\">$3</i></div></div></div>",
"type": "editdisplay",
"ableFlag": true,
"flag": "gmi"
},
{
"comment": "Interview: 렌더링찐빠",
"in": "<lb-interview\\s*([^>]+)?>(?:[\\s\\S]*?)<\\/lb-interview>\\n?",
"out": "",
"type": "editdisplay",
"ableFlag": true,
"flag": "gmi"
},
{
"comment": "Interview: 4 Buttons",
"in": "(?:<lb-lazy id=\"lb-interview\"\\s*\\/?>)|(?:<lb-lazy id=\"lb-interview\"\\s*>[\\s\\S]*?<\\/lb-lazy>)\\n?",
"out": "{{#if {{greater_equal::{{chat_index}}::{{? {{lastmessageid}}}}}}}}<div style=\"padding:8px 0;display:flex;flex-wrap:wrap;gap:6px;\"><button type=\"button\" risu-btn=\"lb-interaction__lb-interview__immediate#broadcast_auto\" style=\"background:#0f172a;border:1px solid #075985;color:#38bdf8;border-radius:4px;padding:6px 14px;font-size:11px;font-weight:600;font-family:-apple-system,BlinkMacSystemFont,sans-serif;cursor:pointer;\">📺 방송용(자동)</button><button type=\"button\" risu-btn=\"lb-interaction__lb-interview__broadcast_q\" style=\"background:#0f172a;border:1px solid #075985;color:#38bdf8;border-radius:4px;padding:6px 14px;font-size:11px;font-weight:600;font-family:-apple-system,BlinkMacSystemFont,sans-serif;cursor:pointer;\">🎤 방송용(질문)</button><button type=\"button\" risu-btn=\"lb-interaction__lb-interview__immediate#offrec_auto\" style=\"background:#2a1010;border:1px solid #7f1d1d;color:#f87171;border-radius:4px;padding:6px 14px;font-size:11px;font-weight:600;font-family:-apple-system,BlinkMacSystemFont,sans-serif;cursor:pointer;\">🚫 오프레코드(자동)</button><button type=\"button\" risu-btn=\"lb-interaction__lb-interview__offrec_q\" style=\"background:#2a1010;border:1px solid #7f1d1d;color:#f87171;border-radius:4px;padding:6px 14px;font-size:11px;font-weight:600;font-family:-apple-system,BlinkMacSystemFont,sans-serif;cursor:pointer;\">💬 오프레코드(질문)</button></div>{{/if}}",
"type": "editdisplay",
"ableFlag": true,
"flag": "gmi"
},
{
"comment": "Interview: 리퀘제거",
"in": "<lb-interview\\s*([^>]+)?>(?:[\\s\\S]*?)<\\/lb-interview>\\n?",
"out": "",
"type": "editprocess",
"ableFlag": true,
"flag": "gmi"
}
],
"trigger": [
{
"comment": "",
"type": "start",
"conditions": [],
"effect": [
{
"type": "triggerlua",
"code": "-- [5] 화면 표시 관리자 수정본\nlistenEdit(\"editDisplay\", function(tid, data, meta)\n setTriggerId(tid)\n triggerId = tid\n\n local mode = getGlobalVar(triggerId, \"lb-interview.mode\")\n if mode == \"끄기\" then return data end\n\n -- 과거 메시지 렌더링 제한 (최근 10개만 처리)\n if meta and meta.index ~= nil then\n local position = meta.index - getChatLength(triggerId)\n if position < -10 then return data end\n end\n\n local success, result = pcall(main, data)\n local finalData = success and result or data\n\n -- 👇 [이 부분이 계속 버튼을 만드는 주범입니다. 조건을 강화하거나 삭제하세요]\n if meta and meta.index then\n local totalLen = getChatLength(triggerId)\n -- 마지막 메시지이면서, AI가 보낸 메시지에만 버튼을 추가하고 싶을 때\n if meta.index > 0 and totalLen > 1 and meta.index == (totalLen - 1) then\n -- 이미 인터뷰 박스가 렌더링되었거나, 수동 태그가 있다면 추가하지 않음\n if not finalData:find('data%-id=\"lb%-interview\"') and not finalData:find('<lb%-lazy id=\"lb%-interview\"') then\n -- 필요한 경우에만 아래 주석을 해제하세요. \n -- 계속 뜨는게 싫다면 아래 라인을 삭제(또는 주석처리)하시면 됩니다.\n -- finalData = finalData .. '\\n<lb-lazy id=\"lb-interview\" />'\n end\n end\n end\n return finalData\nend)"
}
],
"lowLevelAccess": true,
"_id": "trig_mpo27vdo_em3p0j"
}
],
"lowLevelAccess": true,
"displayOrder": 3000
}