diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ac5e8d..688637f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,43 +8,29 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 name: Checkout - - uses: docker/metadata-action@v3 + - uses: docker/metadata-action@v4 id: meta with: images: ghcr.io/mii443/ncb-tts-r2 tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} - - uses: docker/login-action@v1 + - uses: docker/login-action@v2 with: registry: ghcr.io username: mii443 password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - with: - platforms: linux/amd64,linux/arm64 + uses: docker/setup-buildx-action@v2 - - name: Cache Docker layers - uses: actions/cache@v4 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - - uses: docker/build-push-action@v2 + - uses: docker/build-push-action@v4 with: context: . push: true platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max - - name: Move cache - run: | - rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Cargo.toml b/Cargo.toml index 050fe74..dae0969 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ncb-tts-r2" -version = "1.10.1" +version = "1.11.2" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,7 +9,7 @@ edition = "2021" serde_json = "1.0" serde = "1.0" toml = "0.8.19" -gcp_auth = "0.12.3" +gcp_auth = "0.5.0" reqwest = { version = "0.12.9", features = ["json"] } base64 = "0.22.1" async-trait = "0.1.57" diff --git a/Dockerfile b/Dockerfile index 92a4a12..8811900 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM lukemathwalker/cargo-chef:latest-rust-1.82 AS chef -WORKDIR app +WORKDIR /app FROM chef AS planner COPY . . @@ -7,13 +7,39 @@ RUN cargo chef prepare --recipe-path recipe.json FROM chef AS builder COPY --from=planner /app/recipe.json recipe.json -RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg libssl-dev pkg-config libopus-dev gcc && apt-get -y clean +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + ffmpeg \ + libssl-dev \ + pkg-config \ + libopus-dev \ + gcc && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* RUN cargo chef cook --release --recipe-path recipe.json COPY . . RUN cargo build --release FROM ubuntu:22.04 AS runtime WORKDIR /ncb-tts-r2 -RUN apt-get update && apt-get install -y --no-install-recommends openssl ca-certificates ffmpeg libssl-dev libopus-dev && apt-get -y clean -COPY --from=builder /app/target/release/ncb-tts-r2 /usr/local/bin + +# 非rootユーザーの作成 +RUN groupadd -r appgroup && useradd -r -g appgroup appuser + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + openssl \ + ca-certificates \ + ffmpeg \ + libssl-dev \ + libopus-dev && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=builder /app/target/release/ncb-tts-r2 /usr/local/bin/ncb-tts-r2 +RUN chmod +x /usr/local/bin/ncb-tts-r2 + +# 非rootユーザーに切り替え +USER appuser + ENTRYPOINT ["/usr/local/bin/ncb-tts-r2"] diff --git a/docker-compose.yml b/docker-compose.yml index 1bc32b9..a6e246f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: '3' services: ncb-tts-r2: container_name: ncb-tts-r2 - image: ghcr.io/mii443/ncb-tts-r2:1.10.1 + image: ghcr.io/mii443/ncb-tts-r2:1.11.2 environment: - NCB_TOKEN=YOUR_BOT_TOKEN - NCB_APP_ID=YOUR_BOT_ID diff --git a/src/database/database.rs b/src/database/database.rs index c3bbba9..452c934 100644 --- a/src/database/database.rs +++ b/src/database/database.rs @@ -98,6 +98,8 @@ impl Database { let config = ServerConfig { dictionary: Dictionary::new(), autostart_channel_id: None, + voice_state_announce: Some(true), + read_username: Some(true), }; self.set_server_config(server_id, config).await diff --git a/src/database/server_config.rs b/src/database/server_config.rs index fd718c9..592fc81 100644 --- a/src/database/server_config.rs +++ b/src/database/server_config.rs @@ -10,4 +10,6 @@ pub struct DictionaryOnlyServerConfig { pub struct ServerConfig { pub dictionary: Dictionary, pub autostart_channel_id: Option, + pub voice_state_announce: Option, + pub read_username: Option, } diff --git a/src/event_handler.rs b/src/event_handler.rs index e986ca2..fcd9816 100644 --- a/src/event_handler.rs +++ b/src/event_handler.rs @@ -122,6 +122,93 @@ impl EventHandler for Handler { } if let Some(message_component) = interaction.message_component() { match &*message_component.data.custom_id { + "TTS_CONFIG_SERVER_SET_VOICE_STATE_ANNOUNCE" => { + let data_read = ctx.data.read().await; + let mut config = { + let database = data_read + .get::() + .expect("Cannot get DatabaseClientData") + .clone(); + + database + .get_server_config_or_default(message_component.guild_id.unwrap().get()) + .await + .unwrap() + .unwrap() + }; + + config.voice_state_announce = + Some(!config.voice_state_announce.unwrap_or(true)); + let state = config.voice_state_announce.unwrap_or(true); + + { + let database = data_read + .get::() + .expect("Cannot get DatabaseClientData") + .clone(); + + database + .set_server_config(message_component.guild_id.unwrap().get(), config) + .await + .unwrap(); + } + + message_component + .create_response( + &ctx.http, + CreateInteractionResponse::UpdateMessage( + CreateInteractionResponseMessage::new().content(format!( + "入退出アナウンス通知を{}へ切り替えました。", + if state { "`有効`" } else { "`無効`" } + )), + ), + ) + .await + .unwrap(); + } + "TTS_CONFIG_SERVER_SET_READ_USERNAME" => { + let data_read = ctx.data.read().await; + let mut config = { + let database = data_read + .get::() + .expect("Cannot get DatabaseClientData") + .clone(); + + database + .get_server_config_or_default(message_component.guild_id.unwrap().get()) + .await + .unwrap() + .unwrap() + }; + + config.read_username = Some(!config.read_username.unwrap_or(true)); + let state = config.read_username.unwrap_or(true); + + { + let database = data_read + .get::() + .expect("Cannot get DatabaseClientData") + .clone(); + + database + .set_server_config(message_component.guild_id.unwrap().get(), config) + .await + .unwrap(); + } + + message_component + .create_response( + &ctx.http, + CreateInteractionResponse::UpdateMessage( + CreateInteractionResponseMessage::new().content(format!( + "ユーザー名読み上げを{}へ切り替えました。", + if state { "`有効`" } else { "`無効`" } + )), + ), + ) + .await + .unwrap(); + } "TTS_CONFIG_SERVER_REMOVE_DICTIONARY_MENU" => { let i = usize::from_str_radix( &match message_component.data.kind { @@ -399,14 +486,51 @@ impl EventHandler for Handler { CreateInteractionResponse::UpdateMessage( CreateInteractionResponseMessage::new() .content("自動参加チャンネル設定") - .components(vec![CreateActionRow::SelectMenu( - CreateSelectMenu::new( - "SET_AUTOSTART_CHANNEL", - CreateSelectMenuKind::String { options }, + .components(vec![ + CreateActionRow::SelectMenu( + CreateSelectMenu::new( + "SET_AUTOSTART_CHANNEL", + CreateSelectMenuKind::String { options }, + ) + .min_values(0) + .max_values(1), + ), + CreateActionRow::Buttons(vec![CreateButton::new( + "TTS_CONFIG_SERVER_BACK", ) - .min_values(0) - .max_values(1), - )]), + .label("← サーバー設定に戻る") + .style(ButtonStyle::Secondary)]), + ]), + ), + ) + .await + .unwrap(); + } + "TTS_CONFIG_SERVER_BACK" => { + message_component + .create_response( + &ctx.http, + CreateInteractionResponse::UpdateMessage( + CreateInteractionResponseMessage::new() + .content("サーバー設定") + .components(vec![CreateActionRow::Buttons(vec![ + CreateButton::new("TTS_CONFIG_SERVER_DICTIONARY") + .label("辞書管理") + .style(ButtonStyle::Primary), + CreateButton::new( + "TTS_CONFIG_SERVER_SET_AUTOSTART_CHANNEL", + ) + .label("自動参加チャンネル") + .style(ButtonStyle::Primary), + CreateButton::new( + "TTS_CONFIG_SERVER_SET_VOICE_STATE_ANNOUNCE", + ) + .label("入退出アナウンス通知切り替え") + .style(ButtonStyle::Primary), + CreateButton::new("TTS_CONFIG_SERVER_SET_READ_USERNAME") + .label("ユーザー名読み上げ切り替え") + .style(ButtonStyle::Primary), + ])]), ), ) .await @@ -420,32 +544,64 @@ impl EventHandler for Handler { CreateInteractionResponseMessage::new() .content("サーバー設定") .components(vec![CreateActionRow::Buttons(vec![ - CreateButton::new( - "TTS_CONFIG_SERVER_ADD_DICTIONARY_BUTTON", - ) - .label("辞書を追加") - .style(ButtonStyle::Primary), - CreateButton::new( - "TTS_CONFIG_SERVER_REMOVE_DICTIONARY_BUTTON", - ) - .label("辞書を削除") - .style(ButtonStyle::Danger), - CreateButton::new( - "TTS_CONFIG_SERVER_SHOW_DICTIONARY_BUTTON", - ) - .label("辞書一覧") - .style(ButtonStyle::Primary), + CreateButton::new("TTS_CONFIG_SERVER_DICTIONARY") + .label("辞書管理") + .style(ButtonStyle::Primary), CreateButton::new( "TTS_CONFIG_SERVER_SET_AUTOSTART_CHANNEL", ) .label("自動参加チャンネル") .style(ButtonStyle::Primary), + CreateButton::new( + "TTS_CONFIG_SERVER_SET_VOICE_STATE_ANNOUNCE", + ) + .label("入退出アナウンス通知切り替え") + .style(ButtonStyle::Primary), + CreateButton::new("TTS_CONFIG_SERVER_SET_READ_USERNAME") + .label("ユーザー名読み上げ切り替え") + .style(ButtonStyle::Primary), ])]), ), ) .await .unwrap(); } + "TTS_CONFIG_SERVER_DICTIONARY" => { + message_component + .create_response( + &ctx.http, + CreateInteractionResponse::UpdateMessage( + CreateInteractionResponseMessage::new() + .content("辞書管理") + .components(vec![ + CreateActionRow::Buttons(vec![ + CreateButton::new( + "TTS_CONFIG_SERVER_ADD_DICTIONARY_BUTTON", + ) + .label("辞書を追加") + .style(ButtonStyle::Primary), + CreateButton::new( + "TTS_CONFIG_SERVER_REMOVE_DICTIONARY_BUTTON", + ) + .label("辞書を削除") + .style(ButtonStyle::Danger), + CreateButton::new( + "TTS_CONFIG_SERVER_SHOW_DICTIONARY_BUTTON", + ) + .label("辞書一覧") + .style(ButtonStyle::Primary), + ]), + CreateActionRow::Buttons(vec![CreateButton::new( + "TTS_CONFIG_SERVER_BACK", + ) + .label("← サーバー設定に戻る") + .style(ButtonStyle::Secondary)]), + ]), + ), + ) + .await + .unwrap(); + } _ => {} } match message_component.data.kind { diff --git a/src/events/voice_state_update.rs b/src/events/voice_state_update.rs index d20e1b1..0b3b04a 100644 --- a/src/events/voice_state_update.rs +++ b/src/events/voice_state_update.rs @@ -48,6 +48,10 @@ pub async fn voice_state_update(ctx: Context, old: Option, new: Voic .unwrap() }; + if !config.voice_state_announce.unwrap_or(true) { + return; + } + { let mut storage = storage_lock.write().await; if !storage.contains_key(&guild_id) { diff --git a/src/implement/message.rs b/src/implement/message.rs index 6e93d1f..398b4df 100644 --- a/src/implement/message.rs +++ b/src/implement/message.rs @@ -57,7 +57,11 @@ impl TTSMessage for Message { } else { self.author.read_name() }; - format!("{}さんの発言{}", name, text) + if config.read_username.unwrap_or(true) { + format!("{}さんの発言{}", name, text) + } else { + format!("{}", text) + } } } else { let member = self.member.clone(); @@ -71,7 +75,12 @@ impl TTSMessage for Message { } else { self.author.read_name() }; - format!("{}さんの発言{}", name, text) + + if config.read_username.unwrap_or(true) { + format!("{}さんの発言{}", name, text) + } else { + format!("{}", text) + } }; if self.attachments.len() > 0 {