Skip to content

Commit

Permalink
Merge pull request #391 from tktcorporation/fix/239/gif
Browse files Browse the repository at this point in the history
Correction of reading errors for GIF emojis, and Support for reading aloud stickers
  • Loading branch information
tktcorporation committed Nov 26, 2023
2 parents 69f0bc3 + b2622d7 commit f060459
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 66 deletions.
82 changes: 41 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,49 @@
# discord-tts-bot
# discord-tts-bot: Your Voice Channel Assistant

[![Test](https://github.com/tktcorporation/discord-tts-bot/actions/workflows/test.yml/badge.svg)](https://github.com/tktcorporation/discord-tts-bot/actions/workflows/test.yml)
[![codecov](https://codecov.io/gh/tktcorporation/discord-tts-bot/branch/master/graph/badge.svg?token=HB6NMTENNZ)](https://codecov.io/gh/tktcorporation/discord-tts-bot)

## Prerequirements
## Introduction
Welcome to `discord-tts-bot`! This innovative Discord bot uses text-to-speech (TTS) technology to bring your voice channels to life. Whether you're hosting a game night, a study group, or just hanging out, `discord-tts-bot` adds an extra layer of interaction to your Discord server.

- Docker, docker-compose
- AWS Account
- Discord Bot Token
## Key Features
- **High Precision Text-to-Speech:** Utilizes AWS Polly for high accuracy in reading both Kanji and English text, providing a natural voice experience across diverse language environments.
- **Text-to-Speech in Voice Channels:** Simply type, and the bot speaks in your voice channel.
- **Easy to Set Up:** A few steps and your bot is ready on your server.

## Get Started
## Getting Started

### Env Vars
optional
1. `cp -p .envrc.sample .envrc` and set variables.
1. Install [direnv](https://github.com/direnv/direnv).
1. `direnv allow`
### Prerequisites
Before you begin, make sure you have:
- Docker and docker-compose installed.
- An AWS account.
- A Discord bot token.

### Run
### Quick Setup
To set up your `discord-tts-bot`, configure the following environment variables:

1. Invite your bot to your server.
1. `docker-compose up`
1. Type `/` and select `join` command.
1. The bot talks in your voice chat.
1. **`DISCORD_TOKEN`**: Obtain this token from Discord to authenticate and run your bot. It's essential for bot operation on your server.
2. **AWS Credentials** (for text-to-speech functionality using AWS Polly):
- **`AWS_ACCESS_KEY_ID`**
- **`AWS_SECRET_ACCESS_KEY`**
- **`AWS_REGION`**: Set these to integrate AWS Polly for high-quality text-to-speech conversion.
3. **`DISCORD_CMD_PREFIX`**: This is the prefix used for calling the bot commands in Discord. It can be any character or sequence of your choice.

### Develop
### Running the Bot
1. Invite the bot to your Discord server.
2. Start the bot with `docker-compose run app /bin/bash -c "make run"`.
3. In Discord, type `/join` to have the bot join a voice channel.
4. Start typing in discord chat and hear your messages read aloud!

1. `docker-compose run app /bin/bash`
### Development and Contributions
- **Development:** Use `docker-compose run app /bin/bash` for a development environment.
- **Testing:** Run `make test` to execute tests.
- **Linting and Formatting:** Keep your code clean with `make watch`.
- **Package Installation:** Use `make install` to install development packages.

#### Test

```bash
make test
```

#### Linter, Formatter

```bash
make watch
```

## Deploying to Heroku

### Prerequirements

- Heroku Account
## Deployment
- Deploy on Heroku with easy steps detailed in the deployment section.
- Follow the release and deployment guide for smooth updates.

### Release & Deploy

Expand All @@ -57,12 +56,13 @@ make watch
1. Create Pull Request
1. Merge Pull Request

## To invite sample bot
https://discord.com/api/oauth2/authorize?client_id=798137406946934784&permissions=2184261184&scope=bot
## Additional License Information

## LICENCE
MIT
### Sound File Attribution
- **`sounds/shabeko_dayo.wav`**: This specific sound file was generated using CoeFontStudio.

### file
## Try it Out!
Try out my [sample bot](https://discord.com/api/oauth2/authorize?client_id=798137406946934784&permissions=2184261184&scope=bot) and see `discord-tts-bot` in action!

sounds/shabeko_dayo.wav is generayted by CoeFontStudio.
## License
This project is proudly licensed under the MIT License. Check out the LICENSE file for more details.
14 changes: 7 additions & 7 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ services:
# rust
- /cargo-cache:/usr/local/cargo/registry:cached
tty: true
heroku:
# run `apk add docker` before running heroku commands
image: sue445/heroku-cli
working_dir: /workspace
volumes:
- .:/workspace:cached
- /var/run/docker.sock:/var/run/docker.sock:rw
# heroku:
# # run `apk add docker` before running heroku commands
# image: sue445/heroku-cli
# working_dir: /workspace
# volumes:
# - .:/workspace:cached
# - /var/run/docker.sock:/var/run/docker.sock:rw
2 changes: 1 addition & 1 deletion src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod usecase;
pub use usecase::message_commands;
pub use usecase::services::check_msg;

pub use usecase::slash_commands;
2 changes: 1 addition & 1 deletion src/commands/usecase/message_commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::services::{self};

mod help;
pub use help::{COMMANDS_GROUP, COMMANDS_GROUP_OPTIONS};
pub use help::COMMANDS_GROUP;

mod bgm;
mod clear;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/usecase/slash_commands/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl SlashCommand for Play {
let url_option = command
.data
.options
.get(0)
.first()
.expect("url option is required")
.resolved
.clone()
Expand Down
2 changes: 1 addition & 1 deletion src/commands/usecase/slash_commands/select_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl SlashCommand for SelectChannel {
let channel_id_option = command
.data
.options
.get(0)
.first()
.expect("channel option is required")
.resolved
.clone()
Expand Down
2 changes: 1 addition & 1 deletion src/handler/model/voice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl Speaker for Voice {
async fn get_input_from_local<P: AsRef<OsStr>>(file_path: P) -> Input {
ffmpeg(file_path)
.await
.expect("This might fail: handle this error!")
.expect("This might fail if ffmpeg is not installed")
}

async fn play_input(
Expand Down
19 changes: 17 additions & 2 deletions src/handler/usecase/text_to_speech/text_to_speech_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use regex::Regex;

impl Message {
pub fn to_speech_message(&self, _options: SpeechOptions) -> SpeechMessage {
let content = self.get_content();
// urlはそのまま読まない
let str = if self.msg.content.contains("http") {
let str = if content.contains("http") {
"url".to_string()
} else {
self.msg.content.clone()
content
};
// convert discord styled string for speech
let converted = convert_discord_string(&str);
Expand All @@ -24,6 +25,7 @@ enum DiscordStringType {
Channel,
Role,
Emoji,
Animoji,
Mention,
}
impl DiscordStringType {
Expand All @@ -32,6 +34,7 @@ impl DiscordStringType {
DiscordStringType::Channel => Regex::new(r"<#[0-9]+?>").unwrap(),
DiscordStringType::Role => Regex::new(r"<@&[0-9]+?>").unwrap(),
DiscordStringType::Emoji => Regex::new(r"<:(.+?):[0-9]+?>").unwrap(),
DiscordStringType::Animoji => Regex::new(r"<a:(.+?):[0-9]+?>").unwrap(),
DiscordStringType::Mention => Regex::new(r"<@[0-9]+?>").unwrap(),
}
}
Expand All @@ -40,6 +43,7 @@ impl DiscordStringType {
DiscordStringType::Channel => ConvertType::Empty,
DiscordStringType::Role => ConvertType::Empty,
DiscordStringType::Emoji => ConvertType::MatchString,
DiscordStringType::Animoji => ConvertType::MatchString,
DiscordStringType::Mention => ConvertType::Empty,
}
}
Expand All @@ -56,6 +60,10 @@ impl DiscordStringType {
if type_.to_regex().is_match(s) {
return Some(type_);
}
let type_ = DiscordStringType::Animoji;
if type_.to_regex().is_match(s) {
return Some(type_);
}
let type_ = DiscordStringType::Mention;
if type_.to_regex().is_match(s) {
return Some(type_);
Expand Down Expand Up @@ -132,6 +140,13 @@ mod tests {
assert_eq!("butter", result);
}

#[test]
fn test_remove_animoji_string() {
let str = "<a:sanma:872873394570424340>";
let result = convert_discord_string(str);
assert_eq!("sanma", result);
}

#[test]
fn test_remove_double_emoji_string() {
let content = "<:butter:872873394570424340>さんま<:sanma:872873394570424340>";
Expand Down
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ mod tests {

#[tokio::test]
async fn check_env_exists() {
env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
env::var("DISCORD_CMD_PREFIX").expect("Expected a prefix in the environment");
env::var("DISCORD_TOKEN").expect("Expected a DISCORD_TOKEN in the environment");
env::var("DISCORD_CMD_PREFIX").expect("Expected a DISCORD_CMD_PREFIX in the environment");
}
}
66 changes: 57 additions & 9 deletions src/model/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ impl Message {

false
}
pub fn get_content(&self) -> String {
if !self.msg.sticker_items.is_empty() {
// ステッカーの名前をすべて結合して返す
self.msg
.sticker_items
.iter()
.map(|sticker| sticker.name.clone())
.collect::<Vec<String>>()
.join(" ")
} else {
// 通常のメッセージの内容を返す
self.msg.content.clone()
}
}
}

#[cfg(test)]
Expand All @@ -43,20 +57,20 @@ mod tests {

#[test]
fn test_is_command_msg() {
let message = message_factory("a", false);
let message = message_factory("a", false, false);
assert!(!message.is_command());
}

#[test]
fn test_is_command_msg_and() {
let message = message_factory("hogehoege&sa", false);
let message = message_factory("hogehoege&sa", false, false);
assert!(!message.is_command());
}

#[test]
fn test_is_command_msg_cmd_pref() {
let content = &(env::var("DISCORD_CMD_PREFIX").unwrap() + " hogehoge")[..];
let message = message_factory(content, true);
let message = message_factory(content, true, false);
assert!(message.is_command());
}
}
Expand All @@ -67,34 +81,57 @@ mod tests {

#[test]
fn test_is_from_bot_msg() {
let message = message_factory("a", true);
let message = message_factory("a", true, false);
assert!(message.is_from_bot());
}

#[test]
fn test_is_from_bot_msg_and() {
let message = message_factory("hogehoege&sa", true);
let message = message_factory("hogehoege&sa", true, false);
assert!(message.is_from_bot());
}

#[test]
fn test_is_from_bot_msg_cmd_pref() {
let content = &(env::var("DISCORD_CMD_PREFIX").unwrap() + " hogehoge")[..];
let message = message_factory(content, false);
let message = message_factory(content, false, false);
assert!(!message.is_from_bot());
}
}

#[cfg(test)]
mod get_content_tests {
use super::*;

#[test]
fn test_get_content_msg() {
let message = message_factory("a", false, false);
assert_eq!("a", message.get_content());
}

#[test]
fn test_get_content_msg_and() {
let message = message_factory("hogehoege&sa", false, false);
assert_eq!("hogehoege&sa", message.get_content());
}

#[test]
fn test_get_content_msg_sticker() {
let message = message_factory("a", false, true);
assert_eq!("hoge", message.get_content());
}
}

use regex::Regex;

#[test]
fn test_factory() {
let m = message_factory("message", true);
let m = message_factory("message", true, false);
assert!(m.is_from_bot());
assert_eq!("message", m.msg.content);
}

fn message_factory(content: &str, from_bot: bool) -> Message {
fn message_factory(content: &str, from_bot: bool, is_sticker: bool) -> Message {
let message_json = r#"{
"id":881482961801842698,
"attachments":[],
Expand Down Expand Up @@ -137,7 +174,7 @@ mod tests {
"application":null,
"message_reference":null,
"flags":0,
"stickers":[],
"sticker_items":[STICKER],
"referenced_message":null
}"#;
let re_content = Regex::new(r"\[CONTENT\]").unwrap();
Expand All @@ -146,6 +183,17 @@ mod tests {
let result = re_from_bot
.replace(&result, if from_bot { "true" } else { "false" })
.to_string();
let re_sticker = Regex::new(r"\[STICKER\]").unwrap();
let result = re_sticker
.replace(
&result,
if is_sticker {
r#"[{"id":1137185632217747466,"name":"hoge","format_type":1}]"#
} else {
"[]"
},
)
.to_string();
let m: SerenityMessage = serde_json::from_str(&result[..]).unwrap();
Message::new(m)
}
Expand Down

0 comments on commit f060459

Please sign in to comment.