Leek optimizations
This commit is contained in:
parent
b6a32859a1
commit
2cb9db95d1
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,4 +1,4 @@
|
||||||
/target
|
/target
|
||||||
uberbot_*.toml
|
uberbot_*.toml
|
||||||
uberbot.toml
|
uberbot.toml
|
||||||
/Cargo.lock
|
/Cargo.lock
|
||||||
|
|
45
.vscode/launch.json
vendored
45
.vscode/launch.json
vendored
|
@ -1,45 +0,0 @@
|
||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Debug executable 'uberbot'",
|
|
||||||
"cargo": {
|
|
||||||
"args": [
|
|
||||||
"build",
|
|
||||||
"--bin=uberbot",
|
|
||||||
"--package=uberbot"
|
|
||||||
],
|
|
||||||
"filter": {
|
|
||||||
"name": "uberbot",
|
|
||||||
"kind": "bin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"args": [],
|
|
||||||
"cwd": "${workspaceFolder}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Debug unit tests in executable 'uberbot'",
|
|
||||||
"cargo": {
|
|
||||||
"args": [
|
|
||||||
"test",
|
|
||||||
"--no-run",
|
|
||||||
"--bin=uberbot",
|
|
||||||
"--package=uberbot"
|
|
||||||
],
|
|
||||||
"filter": {
|
|
||||||
"name": "uberbot",
|
|
||||||
"kind": "bin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"args": [],
|
|
||||||
"cwd": "${workspaceFolder}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -3,6 +3,9 @@ name = "uberbot"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.15", features = ["rt", "macros", "signal"] }
|
tokio = { version = "1.15", features = ["rt", "macros", "signal"] }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
@ -17,4 +20,4 @@ toml = "0.5"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
arrayvec = "0.7"
|
arrayvec = "0.7"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
async-circe = "0.1.1"
|
async-circe = { git = "https://git.karx.xyz/circe/async-circe" }
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
host = "karx.xyz"
|
host = "karx.xyz"
|
||||||
port = 6697
|
port = 6697
|
||||||
username = "uberbot"
|
username = "uberbot"
|
||||||
channels = ["#main, #no-normies"]
|
channels = ["#main", "#no-normies"]
|
||||||
mode = "+B"
|
mode = "+B"
|
||||||
|
|
||||||
# Spotify config
|
# Spotify config
|
||||||
|
|
|
@ -1,24 +1,46 @@
|
||||||
use arrayvec::ArrayString;
|
use arrayvec::{ArrayString, CapacityError};
|
||||||
|
use rand::Rng;
|
||||||
|
use std::{error::Error, fmt::{Debug, Display}};
|
||||||
|
|
||||||
pub fn mock(target: &str) -> ArrayString<512> {
|
#[derive(Debug)]
|
||||||
|
pub struct LeekCapacityError(CapacityError);
|
||||||
|
|
||||||
|
impl Display for LeekCapacityError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for LeekCapacityError {}
|
||||||
|
|
||||||
|
impl<T> From<CapacityError<T>> for LeekCapacityError {
|
||||||
|
fn from(e: CapacityError<T>) -> Self {
|
||||||
|
Self { 0: e.simplify() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type LeekResult = Result<ArrayString<512>, LeekCapacityError>;
|
||||||
|
|
||||||
|
pub fn mock(input: &str) -> LeekResult {
|
||||||
let mut builder = ArrayString::<512>::new();
|
let mut builder = ArrayString::<512>::new();
|
||||||
|
|
||||||
for char in target.chars() {
|
for ch in input.chars() {
|
||||||
if rand::random() {
|
if rand::random() {
|
||||||
builder.push(char.to_ascii_uppercase());
|
builder.try_push(ch.to_ascii_uppercase())?;
|
||||||
} else {
|
} else {
|
||||||
builder.push(char.to_ascii_lowercase());
|
builder.try_push(ch.to_ascii_lowercase())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
Ok(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn leetify(target: &str) -> ArrayString<512> {
|
pub fn leetify(input: &str) -> LeekResult {
|
||||||
let mut builder = ArrayString::<512>::new();
|
let mut builder = ArrayString::<512>::new();
|
||||||
|
|
||||||
for char in target.chars() {
|
for ch in input.chars() {
|
||||||
builder.push(match char {
|
builder.try_push(match ch {
|
||||||
'a' => '4',
|
'a' => '4',
|
||||||
'e' => '3',
|
'e' => '3',
|
||||||
'i' => '1',
|
'i' => '1',
|
||||||
|
@ -27,9 +49,52 @@ pub fn leetify(target: &str) -> ArrayString<512> {
|
||||||
's' => '5',
|
's' => '5',
|
||||||
't' => '7',
|
't' => '7',
|
||||||
'b' => '8',
|
'b' => '8',
|
||||||
_ => char,
|
_ => ch,
|
||||||
});
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
Ok(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn owoify(input: &str) -> LeekResult {
|
||||||
|
let mut builder: ArrayString<512> = ArrayString::from(input)?;
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut last_char = '\0';
|
||||||
|
for byte in input.bytes() {
|
||||||
|
let mut ch = char::from(byte);
|
||||||
|
if !ch.is_ascii() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// owoify character
|
||||||
|
ch = match ch.to_ascii_lowercase() {
|
||||||
|
'r' | 'l' => 'w',
|
||||||
|
_ => ch,
|
||||||
|
};
|
||||||
|
// stutter (e.g. "o-ohayou gozaimasu!")
|
||||||
|
if last_char == ' ' && rng.gen_bool(0.2) {
|
||||||
|
builder.try_push(ch)?;
|
||||||
|
builder.try_push('-')?;
|
||||||
|
}
|
||||||
|
match ch {
|
||||||
|
// nya-ify
|
||||||
|
'a' | 'e' | 'i' | 'o' | 'u' if last_char == 'n' => {
|
||||||
|
builder.try_push('y')?;
|
||||||
|
}
|
||||||
|
// textmoji
|
||||||
|
'.' => {
|
||||||
|
builder.try_push_str(match rng.gen_range(0..6) {
|
||||||
|
1 => " OwO",
|
||||||
|
2 => " :3",
|
||||||
|
3 => " >w<",
|
||||||
|
4 => " >_<",
|
||||||
|
5 => " ^•ﻌ•^",
|
||||||
|
_ => " ^^",
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
builder.try_push(ch)?;
|
||||||
|
last_char = ch;
|
||||||
|
}
|
||||||
|
Ok(builder)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ use htmlescape::decode_html;
|
||||||
use rspotify::clients::BaseClient;
|
use rspotify::clients::BaseClient;
|
||||||
use rspotify::model::PlayableItem;
|
use rspotify::model::PlayableItem;
|
||||||
use rspotify::{model::Id, ClientCredsSpotify, Credentials};
|
use rspotify::{model::Id, ClientCredsSpotify, Credentials};
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
fn calculate_playtime(secs: u64) -> (u64, u64) {
|
fn calculate_playtime(secs: u64) -> (u64, u64) {
|
||||||
let mut dur_sec = secs;
|
let mut dur_sec = secs;
|
||||||
|
@ -22,7 +21,7 @@ async fn resolve_spotify(
|
||||||
// if spotify.token.lock().await.unwrap().as_ref().unwrap().is_expired() {
|
// if spotify.token.lock().await.unwrap().as_ref().unwrap().is_expired() {
|
||||||
// spotify.request_token().await?;
|
// spotify.request_token().await?;
|
||||||
// }
|
// }
|
||||||
debug!(
|
tracing::debug!(
|
||||||
"Resolving Spotify resource '{}' with id '{}'",
|
"Resolving Spotify resource '{}' with id '{}'",
|
||||||
resource_type, resource_id
|
resource_type, resource_id
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
use arrayvec::{ArrayString, CapacityError};
|
|
||||||
use rand::Rng;
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::result::Result;
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
pub async fn get_waifu_pic(category: &str) -> anyhow::Result<Option<String>> {
|
pub async fn get_waifu_pic(category: &str) -> anyhow::Result<Option<String>> {
|
||||||
let api_resp = reqwest::get(format!("https://api.waifu.pics/sfw/{}", category))
|
let api_resp = reqwest::get(format!("https://api.waifu.pics/sfw/{}", category))
|
||||||
|
@ -10,62 +6,9 @@ pub async fn get_waifu_pic(category: &str) -> anyhow::Result<Option<String>> {
|
||||||
.text()
|
.text()
|
||||||
.await?;
|
.await?;
|
||||||
let api_resp = api_resp.trim();
|
let api_resp = api_resp.trim();
|
||||||
debug!("API response: {}", api_resp);
|
tracing::debug!("API response: {}", api_resp);
|
||||||
let value: Value = serde_json::from_str(&api_resp)?;
|
let value: Value = serde_json::from_str(&api_resp)?;
|
||||||
let url = value["url"].as_str().map(|v| v.to_string());
|
let url = value["url"].as_str().map(|v| v.to_string());
|
||||||
Ok(url)
|
Ok(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OwoCapacityError(CapacityError);
|
|
||||||
|
|
||||||
impl<T> From<CapacityError<T>> for OwoCapacityError {
|
|
||||||
fn from(e: CapacityError<T>) -> Self {
|
|
||||||
Self { 0: e.simplify() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn owoify_out_of_place(
|
|
||||||
input: &str,
|
|
||||||
output: &mut ArrayString<512>,
|
|
||||||
) -> Result<(), OwoCapacityError> {
|
|
||||||
let input: ArrayString<512> = ArrayString::from(input)?;
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let mut last_char = '\0';
|
|
||||||
for byte in input.bytes() {
|
|
||||||
let mut ch = char::from(byte);
|
|
||||||
if !ch.is_ascii() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// owoify character
|
|
||||||
ch = match ch.to_ascii_lowercase() {
|
|
||||||
'r' | 'l' => 'w',
|
|
||||||
_ => ch,
|
|
||||||
};
|
|
||||||
// stutter (e.g. "o-ohayou gozaimasu!")
|
|
||||||
if last_char == ' ' && rng.gen_bool(0.2) {
|
|
||||||
output.try_push(ch)?;
|
|
||||||
output.try_push('-')?;
|
|
||||||
}
|
|
||||||
match ch {
|
|
||||||
// nya-ify
|
|
||||||
'a' | 'e' | 'i' | 'o' | 'u' if last_char == 'n' => {
|
|
||||||
output.try_push('y')?;
|
|
||||||
}
|
|
||||||
// textmoji
|
|
||||||
'.' => {
|
|
||||||
output.try_push_str(match rng.gen_range(0..6) {
|
|
||||||
1 => " OwO",
|
|
||||||
2 => " :3",
|
|
||||||
3 => " >w<",
|
|
||||||
4 => " >_<",
|
|
||||||
5 => " ^•ﻌ•^",
|
|
||||||
_ => " ^^",
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
output.try_push(ch)?;
|
|
||||||
last_char = ch;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
65
src/main.rs
65
src/main.rs
|
@ -1,6 +1,6 @@
|
||||||
use async_circe::{commands::Command, Client, Config};
|
use async_circe::{commands::Command, Client, Config};
|
||||||
use bots::title::Titlebot;
|
use bots::title::Titlebot;
|
||||||
use bots::weeb;
|
use bots::{weeb, leek};
|
||||||
use rspotify::Credentials;
|
use rspotify::Credentials;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
@ -61,7 +61,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.with_env_filter(EnvFilter::from_env("UBERBOT_LOG"))
|
.with_env_filter(EnvFilter::from_env("UBERBOT_LOG"))
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let mut file = File::open("uberbot.toml")?;
|
let mut file = File::open(env::var("UBERBOT_CONFIG").unwrap_or_else(|_| "uberbot.toml".to_string()))?;
|
||||||
let mut client_conf = String::new();
|
let mut client_conf = String::new();
|
||||||
file.read_to_string(&mut client_conf)?;
|
file.read_to_string(&mut client_conf)?;
|
||||||
|
|
||||||
|
@ -80,7 +80,9 @@ async fn main() -> anyhow::Result<()> {
|
||||||
client_config.port,
|
client_config.port,
|
||||||
client_config.username,
|
client_config.username,
|
||||||
);
|
);
|
||||||
|
tracing::debug!("Creating circe client");
|
||||||
let mut client = Client::new(config).await?;
|
let mut client = Client::new(config).await?;
|
||||||
|
tracing::debug!("Identifying with IRC");
|
||||||
client.identify().await?;
|
client.identify().await?;
|
||||||
|
|
||||||
let state = AppState {
|
let state = AppState {
|
||||||
|
@ -130,6 +132,26 @@ async fn handle_message(state: &mut AppState, command: Command) -> anyhow::Resul
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum LeekCommand {
|
||||||
|
Owo, Leet, Mock
|
||||||
|
}
|
||||||
|
async fn execute_leek(state: &mut AppState, cmd: LeekCommand, channel: &str, nick: &str) -> anyhow::Result<()> {
|
||||||
|
match state.last_msgs.get(nick) {
|
||||||
|
Some(msg) => {
|
||||||
|
let output = match cmd {
|
||||||
|
LeekCommand::Owo => leek::owoify(msg)?,
|
||||||
|
LeekCommand::Leet => leek::leetify(msg)?,
|
||||||
|
LeekCommand::Mock => leek::mock(msg)?
|
||||||
|
};
|
||||||
|
state.client.privmsg(channel, &output).await?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
state.client.privmsg(channel, "No last messages found.").await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_privmsg(
|
async fn handle_privmsg(
|
||||||
state: &mut AppState,
|
state: &mut AppState,
|
||||||
nick: String,
|
nick: String,
|
||||||
|
@ -166,42 +188,13 @@ async fn handle_privmsg(
|
||||||
state.client.privmsg(&channel, response).await?;
|
state.client.privmsg(&channel, response).await?;
|
||||||
}
|
}
|
||||||
"mock" => {
|
"mock" => {
|
||||||
let user = match remainder {
|
execute_leek(state, LeekCommand::Mock, channel, remainder.unwrap_or(&nick)).await?;
|
||||||
Some(u) => match u {
|
|
||||||
"" => &nick,
|
|
||||||
_ => u,
|
|
||||||
},
|
|
||||||
None => &nick,
|
|
||||||
}
|
|
||||||
.trim();
|
|
||||||
if let Some(prev_msg) = state.last_msgs.get(user) {
|
|
||||||
let resp = bots::leek::mock(prev_msg);
|
|
||||||
state.client.privmsg(&channel, &resp).await?;
|
|
||||||
} else {
|
|
||||||
state
|
|
||||||
.client
|
|
||||||
.privmsg(&channel, "No previous messages to mock!")
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"leet" => {
|
"leet" => {
|
||||||
let user = match remainder {
|
execute_leek(state, LeekCommand::Leet, channel, remainder.unwrap_or(&nick)).await?;
|
||||||
Some(u) => match u {
|
}
|
||||||
"" => &nick,
|
"owo" => {
|
||||||
_ => u,
|
execute_leek(state, LeekCommand::Owo, channel, remainder.unwrap_or(&nick)).await?;
|
||||||
},
|
|
||||||
None => &nick,
|
|
||||||
}
|
|
||||||
.trim();
|
|
||||||
if let Some(prev_msg) = state.last_msgs.get(user) {
|
|
||||||
let resp = bots::leek::leetify(prev_msg);
|
|
||||||
state.client.privmsg(&channel, &resp).await?;
|
|
||||||
} else {
|
|
||||||
state
|
|
||||||
.client
|
|
||||||
.privmsg(&channel, "No previous messages to leetify!")
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
state.client.privmsg(&channel, "Unknown command").await?;
|
state.client.privmsg(&channel, "Unknown command").await?;
|
||||||
|
|
Loading…
Reference in a new issue