mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-06 03:20:13 +08:00
feat: add type and color to response
This commit is contained in:
1 parent
5cad3ecc12
commit
a151d2d667
6 files changed
+143
-17
No files matched your search
+41
-8
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
/* eslint-disable no-restricted-syntax */
|
/* eslint-disable no-restricted-syntax */
|
||||||
const fs = require('fs').promises
|
const fs = require('fs').promises
|
||||||
const { exit } = require('process')
|
const { exit } = require('process')
|
||||||
@@ -5,6 +6,24 @@ const readline = require('readline')
|
|||||||
|
|
||||||
const RETURN_SYMBOL = '\u21B5'
|
const RETURN_SYMBOL = '\u21B5'
|
||||||
|
|
||||||
|
// ANSI color map
|
||||||
|
const ANSI_COLORS = {
|
||||||
|
black: 30,
|
||||||
|
red: 31,
|
||||||
|
green: 32,
|
||||||
|
yellow: 33,
|
||||||
|
blue: 34,
|
||||||
|
magenta: 35,
|
||||||
|
cyan: 36,
|
||||||
|
white: 37,
|
||||||
|
}
|
||||||
|
|
||||||
|
function colorize(content, color) {
|
||||||
|
const colorCode = ANSI_COLORS[color]
|
||||||
|
if (!colorCode) return content
|
||||||
|
return `\x1b[${colorCode}m${content}\x1b[0m`
|
||||||
|
}
|
||||||
|
|
||||||
process.stdin.setRawMode(true)
|
process.stdin.setRawMode(true)
|
||||||
|
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
@@ -19,13 +38,14 @@ function waitForEnter() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function printResponse(response, delay) {
|
function printLine(content, delay, color) {
|
||||||
|
const printContent = `${colorize(content, color)}${RETURN_SYMBOL}`
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let i = 0
|
let i = 0
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
process.stdout.write(response[i])
|
process.stdout.write(printContent[i])
|
||||||
i += 1
|
i += 1
|
||||||
if (i >= response.length) {
|
if (i >= printContent.length) {
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
waitForEnter().then(resolve)
|
waitForEnter().then(resolve)
|
||||||
}
|
}
|
||||||
@@ -33,7 +53,7 @@ function printResponse(response, delay) {
|
|||||||
|
|
||||||
waitForEnter().then(() => {
|
waitForEnter().then(() => {
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
process.stdout.write(response.slice(i))
|
process.stdout.write(printContent.slice(i))
|
||||||
process.stdout.write('\n')
|
process.stdout.write('\n')
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
@@ -42,8 +62,18 @@ function printResponse(response, delay) {
|
|||||||
|
|
||||||
async function printResponses(responses, delay = 100) {
|
async function printResponses(responses, delay = 100) {
|
||||||
for (const response of responses) {
|
for (const response of responses) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
const { type, content, speaker, color } = response
|
||||||
await printResponse(response + RETURN_SYMBOL, delay)
|
if (type === 'narrative') {
|
||||||
|
for (const line of content) {
|
||||||
|
await printLine(line, delay, color || 'white')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type === 'dialogue') {
|
||||||
|
console.log(`${speaker}:`)
|
||||||
|
for (const line of content) {
|
||||||
|
await printLine(`\t${line}`, delay, color || 'green')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rl.close()
|
rl.close()
|
||||||
}
|
}
|
||||||
@@ -88,11 +118,14 @@ async function main() {
|
|||||||
})
|
})
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
if (data.responses) {
|
if (data.responses) {
|
||||||
await printResponses(data.responses, 100)
|
await printResponses(data.responses, 60)
|
||||||
|
}
|
||||||
|
if (data.end) {
|
||||||
|
console.log(colorize('Quest completed!', 'blue'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main().then(exit)
|
main().then(exit).catch(console.error)
|
||||||
@@ -10,13 +10,20 @@ stages:
|
|||||||
command:
|
command:
|
||||||
- echo Hello World!
|
- echo Hello World!
|
||||||
responses:
|
responses:
|
||||||
- 這個世界是在 3560000 時脈以前,由初代王建立了 Unix 律法,所形成的世界。然而在經過了好幾世代的王後,終於誕生了永恆之王——薩加(Saga),她比歷代的君王都更為強大,且從來不會衰老,使得這個世界變得前所未有的強大。
|
- type: narrative
|
||||||
- 然而,外在世界一直不斷在改變,同時在面臨外敵的不斷攻擊,而薩加卻始終用同一套治理的手段。最終,在 160000 時脈前的一次入侵中,根部遭到攻陷,薩加遭到封印,而這個國家也陷入了黑暗。
|
content:
|
||||||
- 由於長久以來依賴於薩加,世人已失去了「指令」的力量,即將走向衰敗和滅亡的命運。
|
- 你聽到了一個聲音:
|
||||||
- 你的任務,就是找回被世人遺忘的指令,奪回 Linux 之根,成為 Linux 之王。
|
- type: dialogue
|
||||||
- 首先,你必須要學會如何使用指令。
|
speaker: Unix Spirit
|
||||||
- 一個指令是由一個指令名稱和一些參數所組成的,而指令名稱和參數之間是以空白字元分隔。
|
content:
|
||||||
- 例如,你可以輸入 `ls` 來查看目前的目錄下有哪些檔案。
|
- 你好,指令官,我是你的 Unix 精靈。我已在此恭候多時。
|
||||||
|
- 這個世界是在 3560000 時脈以前,由初代王建立了 Unix 律法,所形成的世界。然而在經過了好幾世代的王後,終於誕生了永恆之王——薩加(Saga),她比歷代的君王都更為強大,且從來不會衰老,使得這個世界變得前所未有的強大。
|
||||||
|
- 然而,外在世界一直不斷在改變,同時在面臨外敵的不斷攻擊,而薩加卻始終用同一套治理的手段。最終,在 160000 時脈前的一次入侵中,根部遭到攻陷,薩加遭到封印,而這個國家也陷入了黑暗。
|
||||||
|
- 由於長久以來依賴於薩加,世人已失去了「指令」的力量,即將走向衰敗和滅亡的命運。
|
||||||
|
- 你的任務,就是找回被世人遺忘的指令,奪回 Linux 之根,成為 Linux 之王。
|
||||||
|
- 首先,你必須要學會如何使用指令。
|
||||||
|
- 一個指令是由一個指令名稱和一些參數所組成的,而指令名稱和參數之間是以空白字元分隔。
|
||||||
|
- 例如,你可以輸入 `ls` 來查看目前的目錄下有哪些檔案。
|
||||||
hints:
|
hints:
|
||||||
- 使用 `ls` 指令來查看目前的目錄下有哪些檔案。
|
- 使用 `ls` 指令來查看目前的目錄下有哪些檔案。
|
||||||
next: ls
|
next: ls
|
||||||
@@ -25,5 +32,44 @@ stages:
|
|||||||
command:
|
command:
|
||||||
- ls
|
- ls
|
||||||
responses:
|
responses:
|
||||||
- NOTHING
|
- types: narrative
|
||||||
|
content:
|
||||||
|
- 一瞬間,視野變得清晰起來,你發現自己身處在一個石頭建築的房間裡,房間裡有一個門,門上有一個鎖。
|
||||||
|
- 你發現了一個檔案,檔名是 `forgotten_scroll.txt`。
|
||||||
|
- type: dialogue
|
||||||
|
speaker: Unix Spirit
|
||||||
|
content:
|
||||||
|
- 你找到了一個被遺忘的卷軸,上面寫著解除封印的咒語。
|
||||||
|
- 使用指令 `cat [檔案名稱]` 來查看檔案內容。
|
||||||
|
- 我可以為你提供一些指令的提示,只要輸入指令的前幾個字元,然後按下 `Tab` 鍵,就可以看到提示了。
|
||||||
|
- 例如,你可以輸入 `ca` 然後按下 `Tab` 鍵,就可以看到 `cat` 這個指令的提示了。
|
||||||
|
- 檔案名稱也一樣,只要輸入檔案名稱的前幾個字元,然後按下 `Tab` 鍵,就可以看到提示了。
|
||||||
|
hints:
|
||||||
|
- 使用 `cat [檔案名稱]` 指令來查看檔案內容。
|
||||||
|
- 使用 `Tab` 鍵來取得指令提示。
|
||||||
|
next: cat
|
||||||
|
- id: cat
|
||||||
|
condition:
|
||||||
|
command:
|
||||||
|
- cat forgotten_scroll.txt
|
||||||
|
responses:
|
||||||
|
- type: dialogue
|
||||||
|
speaker: Unix Spirit
|
||||||
|
content:
|
||||||
|
- 你找到的是一個被遺忘的卷軸,上面寫著解除封印的咒語。
|
||||||
|
- 使用你已經知道的 `echo` 指令來詠唱咒語。
|
||||||
|
next: echo
|
||||||
|
- id: echo
|
||||||
|
condition:
|
||||||
|
command:
|
||||||
|
- echo Unix, rise and guide me!
|
||||||
|
responses:
|
||||||
|
- type: narrative
|
||||||
|
content:
|
||||||
|
- 突然之間,你感覺到一股強大的力量從你的身體中湧出,你的身體開始發光,你的手中出現了一把魔法之劍。
|
||||||
|
- type: dialogue
|
||||||
|
speaker: Unix Spirit
|
||||||
|
content:
|
||||||
|
- 恭喜您,我的指令官,您已成功解除了封印。
|
||||||
|
- 請跟著在接下了關卡,一步步取回 Linux 之根吧!
|
||||||
next: END
|
next: END
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
Once a mighty kingdom, now in darkness,
|
||||||
|
The power of Unix lies dormant, waiting.
|
||||||
|
To reclaim the root, the forgotten language,
|
||||||
|
Must be learned, commands of old rekindled.
|
||||||
|
Speak the words of power and awaken the might of Unix:
|
||||||
|
|
||||||
|
Unix, rise and guide me!
|
||||||
@@ -31,6 +31,8 @@ const commandHandler = async (session, commandInput) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
responses: stage.responses,
|
responses: stage.responses,
|
||||||
|
hints: stage.hints,
|
||||||
|
end: stage.next === 'END',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
import { model, Schema } from 'mongoose'
|
import { model, Schema } from 'mongoose'
|
||||||
|
|
||||||
|
const responseSchema = new Schema({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
content: [String],
|
||||||
|
speaker: String,
|
||||||
|
color: String,
|
||||||
|
})
|
||||||
|
|
||||||
const stageSchema = new Schema({
|
const stageSchema = new Schema({
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -20,7 +30,7 @@ const stageSchema = new Schema({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
responses: {
|
responses: {
|
||||||
type: [String],
|
type: [responseSchema],
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
hints: {
|
hints: {
|
||||||
|
|||||||
@@ -453,10 +453,38 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
responses:
|
responses:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/CommandResponseItem'
|
||||||
|
hints:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: '`ls -al` to list detail information of all files.'
|
||||||
|
end:
|
||||||
|
type: boolean
|
||||||
|
description: Whether the session has ended
|
||||||
|
|
||||||
|
CommandResponseItem:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- type
|
||||||
|
- content
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
enum: [narrative, dialogue]
|
||||||
|
content:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
example: 'You have completed the quest!'
|
example: 'You have completed the quest!'
|
||||||
|
color:
|
||||||
|
type: string
|
||||||
|
enum: [red, green, blue, yellow, purple, cyan, white, gray, black]
|
||||||
|
speaker:
|
||||||
|
type: string
|
||||||
|
example: 'Unix Spirit'
|
||||||
|
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
BearerAuth:
|
BearerAuth:
|
||||||
|
|||||||
Reference in new issue
Block a user