Leek optimizations

This commit is contained in:
lemonsh 2021-12-30 23:02:12 +00:00
parent b6a32859a1
commit 2cb9db95d1
8 changed files with 117 additions and 159 deletions

8
.gitignore vendored
View file

@ -1,4 +1,4 @@
/target
uberbot_*.toml
uberbot.toml
/Cargo.lock
/target
uberbot_*.toml
uberbot.toml
/Cargo.lock

45
.vscode/launch.json vendored
View file

@ -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}"
}
]
}

View file

@ -3,6 +3,9 @@ name = "uberbot"
version = "0.1.0"
edition = "2021"
[profile.release]
lto = true
[dependencies]
tokio = { version = "1.15", features = ["rt", "macros", "signal"] }
anyhow = "1.0"
@ -17,4 +20,4 @@ toml = "0.5"
serde = "1.0"
arrayvec = "0.7"
rand = "0.8"
async-circe = "0.1.1"
async-circe = { git = "https://git.karx.xyz/circe/async-circe" }

View file

@ -2,7 +2,7 @@
host = "karx.xyz"
port = 6697
username = "uberbot"
channels = ["#main, #no-normies"]
channels = ["#main", "#no-normies"]
mode = "+B"
# Spotify config

View file

@ -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();
for char in target.chars() {
for ch in input.chars() {
if rand::random() {
builder.push(char.to_ascii_uppercase());
builder.try_push(ch.to_ascii_uppercase())?;
} 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();
for char in target.chars() {
builder.push(match char {
for ch in input.chars() {
builder.try_push(match ch {
'a' => '4',
'e' => '3',
'i' => '1',
@ -27,9 +49,52 @@ pub fn leetify(target: &str) -> ArrayString<512> {
's' => '5',
't' => '7',
'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)
}

View file

@ -3,7 +3,6 @@ use htmlescape::decode_html;
use rspotify::clients::BaseClient;
use rspotify::model::PlayableItem;
use rspotify::{model::Id, ClientCredsSpotify, Credentials};
use tracing::debug;
fn calculate_playtime(secs: u64) -> (u64, u64) {
let mut dur_sec = secs;
@ -22,7 +21,7 @@ async fn resolve_spotify(
// if spotify.token.lock().await.unwrap().as_ref().unwrap().is_expired() {
// spotify.request_token().await?;
// }
debug!(
tracing::debug!(
"Resolving Spotify resource '{}' with id '{}'",
resource_type, resource_id
);

View file

@ -1,8 +1,4 @@
use arrayvec::{ArrayString, CapacityError};
use rand::Rng;
use serde_json::Value;
use std::result::Result;
use tracing::debug;
pub async fn get_waifu_pic(category: &str) -> anyhow::Result<Option<String>> {
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()
.await?;
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 url = value["url"].as_str().map(|v| v.to_string());
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(())
}

View file

@ -1,6 +1,6 @@
use async_circe::{commands::Command, Client, Config};
use bots::title::Titlebot;
use bots::weeb;
use bots::{weeb, leek};
use rspotify::Credentials;
use serde::Deserialize;
use std::fs::File;
@ -61,7 +61,7 @@ async fn main() -> anyhow::Result<()> {
.with_env_filter(EnvFilter::from_env("UBERBOT_LOG"))
.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();
file.read_to_string(&mut client_conf)?;
@ -80,7 +80,9 @@ async fn main() -> anyhow::Result<()> {
client_config.port,
client_config.username,
);
tracing::debug!("Creating circe client");
let mut client = Client::new(config).await?;
tracing::debug!("Identifying with IRC");
client.identify().await?;
let state = AppState {
@ -130,6 +132,26 @@ async fn handle_message(state: &mut AppState, command: Command) -> anyhow::Resul
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(
state: &mut AppState,
nick: String,
@ -166,42 +188,13 @@ async fn handle_privmsg(
state.client.privmsg(&channel, response).await?;
}
"mock" => {
let user = match remainder {
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?;
}
execute_leek(state, LeekCommand::Mock, channel, remainder.unwrap_or(&nick)).await?;
}
"leet" => {
let user = match remainder {
Some(u) => match u {
"" => &nick,
_ => u,
},
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?;
}
execute_leek(state, LeekCommand::Leet, channel, remainder.unwrap_or(&nick)).await?;
}
"owo" => {
execute_leek(state, LeekCommand::Owo, channel, remainder.unwrap_or(&nick)).await?;
}
_ => {
state.client.privmsg(&channel, "Unknown command").await?;