Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
ci:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [24.x]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Format check
run: npm run format:check

- name: Type check
run: npm run typecheck

- name: Build
run: npm run build

- name: Docker build test
run: docker compose build

- name: Create test settings.json
run: cp src/settings.json.sample src/settings.json

- name: Docker run test (expect TokenInvalid error)
run: |
# コンテナを起動し、TokenInvalidエラーが出ることを確認
set +e
timeout 15s docker compose up > docker-logs.txt 2>&1
EXIT_CODE=$?
set -e

# ログを表示
echo "=== Docker logs ==="
cat docker-logs.txt
echo "=================="

# コンテナを停止
docker compose down

# TokenInvalidエラーログが出力されているか確認
if grep -q "TokenInvalid" docker-logs.txt; then
echo "✓ Docker container successfully started and reached Discord login"
exit 0
else
echo "✗ Container did not reach expected state"
exit 1
fi
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
dist/
node_modules/
.eslintrc.json
.env
settings.json
settings.json
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
dist
*.log
.git
coverage
build
10 changes: 10 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": false,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"endOfLine": "lf",
"arrowParens": "avoid"
}
45 changes: 45 additions & 0 deletions eslint.config.js
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

別PRにするなら,このPRではこのファイル消しませんか

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const eslint = require('@eslint/js');
const tseslint = require('@typescript-eslint/eslint-plugin');
const tsparser = require('@typescript-eslint/parser');
const prettier = require('eslint-config-prettier');
const globals = require('globals');

module.exports = [
eslint.configs.recommended,
{
files: ['src/**/*.ts'],
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 2021,
sourceType: 'module',
project: './tsconfig.json',
},
globals: {
...globals.node,
NodeJS: 'readonly',
},
},
plugins: {
'@typescript-eslint': tseslint,
},
rules: {
...tseslint.configs['flat/recommended-type-checked'].rules,
'@typescript-eslint/no-explicit-any': 'warn',
'no-unused-vars': 'off', // TypeScript ESLintのno-unused-varsを使うため無効化
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'@typescript-eslint/no-non-null-assertion': 'off',
'no-console': 'off',
},
},
{
ignores: ['dist/**', 'node_modules/**', '*.js', '!eslint.config.js'],
},
prettier,
];
28 changes: 24 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"start": "node dist/main.js",
"format": "prettier --write 'src'"
"fmt": "prettier --write \"src/**/*.ts\"",
"format": "prettier --write \"src/**/*.ts\"",
"format:check": "prettier --check \"src/**/*.ts\"",
"typecheck": "tsc --noEmit"
},
"engines": {
"node": "24.x"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"devDependencies": {
"@eslint/js": "^9.4.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

別PRにするなら devDependencies の更新はしなくていいんじゃないすか

"@typescript-eslint/eslint-plugin": "^8.46.4",
"@typescript-eslint/parser": "^8.46.4",
"eslint": "^9.4.0",
Expand All @@ -24,6 +28,7 @@
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-n": "^17.6.0",
"eslint-plugin-promise": "^7.2.1",
"globals": "^16.5.0",
"prettier": "^3.3.2",
"ts-node": "^10.9.2",
"ts-node-dev": "^2.0.0",
Expand Down
6 changes: 4 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ try {
} catch (err) {
console.error(
"設定ファイル settings.json の読み込みまたはパースに失敗しました。\n" +
"エラー内容: " + err + "\n" +
"settings.json が存在しない場合は settings.json.sample をコピーして作成してください。"
"エラー内容: " +
err +
"\n" +
"settings.json が存在しない場合は settings.json.sample をコピーして作成してください。"
);
process.exit(1);
}
Expand Down
18 changes: 10 additions & 8 deletions src/discord/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export const commands = [
.setName("borrow")
.setDescription("鍵を借りる(オプション:リマインダー開始時間を分で指定)")
.addIntegerOption(option =>
option.setName("delay-minutes")
option
.setName("delay-minutes")
.setDescription("指定分後にリマインダー開始(指定なしはデフォルト)")
.setRequired(false)
.setMinValue(0)
Expand All @@ -27,7 +28,8 @@ export const commands = [
.setName("reminder-time")
.setDescription("リマインダー送信時間を設定(分)")
.addIntegerOption(option =>
option.setName("minutes")
option
.setName("minutes")
.setDescription("リマインダー送信までの時間(分)")
.setRequired(true)
.setMinValue(1)
Expand All @@ -38,14 +40,16 @@ export const commands = [
.setName("check-time")
.setDescription("定時チェックの時刻を設定")
.addIntegerOption(option =>
option.setName("hour")
option
.setName("hour")
.setDescription("時(0-23)")
.setRequired(true)
.setMinValue(0)
.setMaxValue(23)
)
.addIntegerOption(option =>
option.setName("minute")
option
.setName("minute")
.setDescription("分(0-59)")
.setRequired(true)
.setMinValue(0)
Expand All @@ -60,8 +64,6 @@ export const commands = [
.setName("owner")
.setDescription("鍵の持ち主を変更")
.addUserOption(option =>
option.setName("user")
.setDescription("新しい持ち主")
.setRequired(true)
)
option.setName("user").setDescription("新しい持ち主").setRequired(true)
),
].map(command => command.toJSON());
32 changes: 21 additions & 11 deletions src/discord/discordUI.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import { Key, OperKey, Presence } from "../types";
import { modeConsole } from "../config";
import { borrowKey, openKey, closeKey, returnKey } from "../services/keyOperations";
import {
borrowKey,
openKey,
closeKey,
returnKey,
} from "../services/keyOperations";

// ボタンを定義
// 「借りる」ボタン - 緑色(成功)スタイル
Expand Down Expand Up @@ -33,7 +38,9 @@ export const returnButton = new ButtonBuilder()
* @param isReminderEnabled - リマインダーが有効かどうか
* @returns 適切な色とラベルのボタン
*/
export const createReminderToggleButton = (isReminderEnabled: boolean): ButtonBuilder => {
export const createReminderToggleButton = (
isReminderEnabled: boolean
): ButtonBuilder => {
// ラベルはリマインダーに統一
const label = "リマインダー";
// 現在の状態を色で表示: ON時は Success(緑)、OFF時は Secondary(灰色)
Expand All @@ -56,19 +63,16 @@ export const mapLabel: Map<Key, string> = new Map([
// 各状態で表示すべきボタンを管理
export const mapButtons: Map<Key, ActionRowBuilder<ButtonBuilder>> = new Map([
// 返却済み状態: 「借りる」ボタンのみ表示
[
"RETURN",
new ActionRowBuilder<ButtonBuilder>().addComponents(borrowButton),
],
["RETURN", new ActionRowBuilder<ButtonBuilder>().addComponents(borrowButton)],
// 開けた状態: 「閉める」ボタンのみ表示
["OPEN", new ActionRowBuilder<ButtonBuilder>().addComponents(closeButton)],
// 閉めた状態: 「返す」と「開ける」ボタンを表示(リマインダーボタンは動的に追加)
[
"CLOSE",
!modeConsole
? new ActionRowBuilder<ButtonBuilder>()
.addComponents(returnButton)
.addComponents(openButton)
.addComponents(returnButton)
.addComponents(openButton)
: new ActionRowBuilder<ButtonBuilder>().addComponents(returnButton),
],
]);
Expand All @@ -89,13 +93,19 @@ export const mapOpers: Map<string, OperKey> = new Map([
* @param isReminderEnabled - リマインダーが有効かどうか
* @returns ボタンセット
*/
export const getButtons = (keyStatus: Key, isReminderEnabled: boolean): ActionRowBuilder<ButtonBuilder> => {
export const getButtons = (
keyStatus: Key,
isReminderEnabled: boolean
): ActionRowBuilder<ButtonBuilder> => {
const reminderButton = createReminderToggleButton(isReminderEnabled);

if (keyStatus === "CLOSE") {
// 閉めた状態: 「返す」「開ける」「リマインダー」を表示
return new ActionRowBuilder<ButtonBuilder>()
.addComponents(returnButton, openButton, reminderButton);
return new ActionRowBuilder<ButtonBuilder>().addComponents(
returnButton,
openButton,
reminderButton
);
}

const buttons = mapButtons.get(keyStatus);
Expand Down
Loading