Skip to content

Commit 8b53b52

Browse files
Add random chance to uwuify eval responses
1 parent 090e1ae commit 8b53b52

File tree

2 files changed

+177
-3
lines changed

2 files changed

+177
-3
lines changed

bot/exts/smart_eval/_cog.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from bot.bot import SirRobin
1010
from bot.exts.smart_eval._smart_eval_rules import DEFAULT_RESPONSES, RULES
11+
from bot.utils.uwu import uwuify
1112

1213
DONATION_LEVELS = {
1314
# Number of donations: (response time, intelligence level)
@@ -160,13 +161,16 @@ async def smart_eval(self, ctx: commands.Context, *, code: str) -> None:
160161
matching_responses.append(response)
161162
if not matching_responses:
162163
matching_responses = DEFAULT_RESPONSES
163-
final_response = random.choice(matching_responses)
164+
165+
selected_response = random.choice(matching_responses)
166+
if random.randint(1,5) == 5:
167+
selected_response = uwuify(selected_response)
164168

165169
async with ctx.typing():
166170
await asyncio.sleep(response_time)
167171

168-
if len(final_response) <= 1000:
169-
await ctx.reply(final_response)
172+
if len(selected_response) <= 1000:
173+
await ctx.reply(selected_response)
170174
else:
171175
await ctx.reply(
172176
"There's definitely something wrong but I'm just not sure how to put it concisely into words."

bot/utils/uwu.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# This file was copied from https://github.com/python-discord/sir-lancebot
2+
# and modified.
3+
4+
import random
5+
import re
6+
import typing as t
7+
from dataclasses import dataclass
8+
from functools import partial
9+
10+
from bot.bot import SirRobin
11+
12+
WORD_REPLACE = {
13+
"small": "smol",
14+
"cute": "kawaii~",
15+
"fluff": "floof",
16+
"love": "luv",
17+
"stupid": "baka",
18+
"idiot": "baka",
19+
"what": "nani",
20+
"meow": "nya~",
21+
"roar": "rawrr~",
22+
}
23+
24+
EMOJIS = [
25+
"rawr x3",
26+
"OwO",
27+
"UwU",
28+
"o.O",
29+
"-.-",
30+
">w<",
31+
"σωσ",
32+
"òωó",
33+
"ʘwʘ",
34+
":3",
35+
"XD",
36+
"nyaa~~",
37+
"mya",
38+
">_<",
39+
"rawr",
40+
"uwu",
41+
"^^",
42+
"^^;;",
43+
]
44+
45+
EMOJI_REPLACE = {
46+
"😐": ":cat:",
47+
"😢": ":crying_cat_face:",
48+
"😍": ":heart_eyes_cat:",
49+
"😂": ":joy_cat:",
50+
"😗": ":kissing_cat:",
51+
"😠": ":pouting_cat:",
52+
"😱": ":scream_cat:",
53+
"😆": ":smile_cat:",
54+
"🙂": ":smiley_cat:",
55+
"😀": ":smiley_cat:",
56+
"😏": ":smirk_cat:",
57+
"🥺": ":pleading_face::point_right::point_left:"
58+
}
59+
REGEX_WORD_REPLACE = re.compile(r"(?<!w)[lr](?!w)")
60+
61+
REGEX_PUNCTUATION = re.compile(r"[.!?\r\n\t]")
62+
63+
REGEX_STUTTER = re.compile(r"(\s)([a-zA-Z])")
64+
SUBSTITUTE_STUTTER = r"\g<1>\g<2>-\g<2>"
65+
66+
REGEX_NYA = re.compile(r"n([aeou][^aeiou])")
67+
SUBSTITUTE_NYA = r"ny\1"
68+
69+
REGEX_EMOJI = re.compile(r"<(a)?:(\w+?):(\d{15,21}?)>", re.ASCII)
70+
71+
72+
@dataclass(frozen=True, eq=True)
73+
class Emoji:
74+
"""Data class for an Emoji."""
75+
76+
name: str
77+
uid: int
78+
animated: bool = False
79+
80+
def __str__(self):
81+
anim_bit = "a" if self.animated else ""
82+
return f"<{anim_bit}:{self.name}:{self.uid}>"
83+
84+
def can_display(self, bot: SirRobin) -> bool:
85+
"""Determines if a bot is in a server with the emoji."""
86+
return bot.get_emoji(self.uid) is not None
87+
88+
@classmethod
89+
def from_match(cls, match: tuple[str, str, str]) -> t.Optional["Emoji"]:
90+
"""Creates an Emoji from a regex match tuple."""
91+
if not match or len(match) != 3 or not match[2].isdecimal():
92+
return None
93+
return cls(match[1], int(match[2]), match[0] == "a")
94+
95+
96+
97+
98+
def _word_replace(input_string: str) -> str:
99+
"""Replaces words that are keys in the word replacement hash to the values specified."""
100+
for word, replacement in WORD_REPLACE.items():
101+
input_string = input_string.replace(word, replacement)
102+
return input_string
103+
104+
def _char_replace(input_string: str) -> str:
105+
"""Replace certain characters with 'w'."""
106+
return REGEX_WORD_REPLACE.sub("w", input_string)
107+
108+
def _stutter(strength: float, input_string: str) -> str:
109+
"""Adds stuttering to a string."""
110+
return REGEX_STUTTER.sub(partial(_stutter_replace, strength=strength), input_string, 0)
111+
112+
def _stutter_replace(match: re.Match, strength: float = 0.0) -> str:
113+
"""Replaces a single character with a stuttered character."""
114+
match_string = match.group()
115+
if random.random() < strength:
116+
return f"{match_string}-{match_string[-1]}" # Stutter the last character
117+
return match_string
118+
119+
def _nyaify(input_string: str) -> str:
120+
"""Nyaifies a string by adding a 'y' between an 'n' and a vowel."""
121+
return REGEX_NYA.sub(SUBSTITUTE_NYA, input_string, 0)
122+
123+
def _emoji(strength: float, input_string: str) -> str:
124+
"""Replaces some punctuation with emoticons."""
125+
return REGEX_PUNCTUATION.sub(partial(_emoji_replace, strength=strength), input_string, 0)
126+
127+
def _emoji_replace(match: re.Match, strength: float = 0.0) -> str:
128+
"""Replaces a punctuation character with an emoticon."""
129+
match_string = match.group()
130+
if random.random() < strength:
131+
return f" {random.choice(EMOJIS)} "
132+
return match_string
133+
134+
def _ext_emoji_replace(input_string: str) -> str:
135+
"""Replaces any emoji the bot cannot send in input_text with a random emoticons."""
136+
groups = REGEX_EMOJI.findall(input_string)
137+
emojis = {Emoji.from_match(match) for match in groups}
138+
# Replace with random emoticon if unable to display
139+
emojis_map = {
140+
re.escape(str(e)): random.choice(EMOJIS)
141+
for e in emojis if e and not e.can_display(SirRobin)
142+
}
143+
if emojis_map:
144+
# Pattern for all emoji markdowns to be replaced
145+
emojis_re = re.compile("|".join(emojis_map.keys()))
146+
# Replace matches with random emoticon
147+
return emojis_re.sub(
148+
lambda m: emojis_map[re.escape(m.group())],
149+
input_string
150+
)
151+
# Return original if no replacement
152+
return input_string
153+
154+
def _uwu_emojis(input_string: str) -> str:
155+
"""Replaces certain emojis with better emojis."""
156+
for old, new in EMOJI_REPLACE.items():
157+
input_string = input_string.replace(old, new)
158+
return input_string
159+
160+
def uwuify(input_string: str, *, stutter_strength: float = 0.2, emoji_strength: float = 0.1) -> str:
161+
"""Takes a string and returns an uwuified version of it."""
162+
input_string = input_string.lower()
163+
input_string = _word_replace(input_string)
164+
input_string = _nyaify(input_string)
165+
input_string = _char_replace(input_string)
166+
input_string = _stutter(stutter_strength, input_string)
167+
input_string = _emoji(emoji_strength, input_string)
168+
input_string = _ext_emoji_replace(input_string)
169+
input_string = _uwu_emojis(input_string)
170+
return input_string

0 commit comments

Comments
 (0)