Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8c9784f
Add the channel
DidiDidi129 Aug 17, 2025
6876185
a
DidiDidi129 Aug 17, 2025
ddff91f
a
DidiDidi129 Aug 17, 2025
30e5d38
a
DidiDidi129 Aug 17, 2025
91dff4c
revert
DidiDidi129 Aug 17, 2025
218ab28
a
DidiDidi129 Aug 17, 2025
c5c0b28
f
DidiDidi129 Aug 17, 2025
ffe6679
u
DidiDidi129 Aug 17, 2025
9bfa236
c
DidiDidi129 Aug 17, 2025
c38b27d
k
DidiDidi129 Aug 17, 2025
66dfff3
t
DidiDidi129 Aug 17, 2025
dfef7e3
h
DidiDidi129 Aug 17, 2025
ea8ed27
i
DidiDidi129 Aug 17, 2025
5ec88ea
i
DidiDidi129 Aug 17, 2025
0b18c4e
s
DidiDidi129 Aug 17, 2025
bdba31c
s
DidiDidi129 Aug 17, 2025
c9ccaac
i
DidiDidi129 Aug 17, 2025
f2d6d98
s
DidiDidi129 Aug 17, 2025
908844f
s
DidiDidi129 Aug 17, 2025
0c4c93b
h
DidiDidi129 Aug 17, 2025
f917f82
i
DidiDidi129 Aug 17, 2025
07d2119
Rewrite
DidiDidi129 Aug 17, 2025
53ab414
credits
DidiDidi129 Aug 17, 2025
4e79562
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 17, 2025
9acc5bf
Added the user command
DidiDidi129 Aug 17, 2025
971aed2
more functions
DidiDidi129 Aug 17, 2025
5728fa5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 17, 2025
e5bb138
better cmds
DidiDidi129 Aug 17, 2025
8b763b8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 17, 2025
88cba7f
Revert
DidiDidi129 Aug 17, 2025
7412f9e
idk waht im doing
DidiDidi129 Aug 17, 2025
2dd8af0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 17, 2025
f1526c5
Made the uwus consistant
DidiDidi129 Aug 17, 2025
766ff7a
revert
DidiDidi129 Aug 17, 2025
93e2be2
Credits
DidiDidi129 Aug 17, 2025
b7b9171
uwu > UwU
DidiDidi129 Aug 17, 2025
7162a06
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions uwu/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,5 @@
__red_end_user_data_statement__ = json.load(fp)["end_user_data_statement"]


async def setup(bot: Red) -> None:
"""Load UwU cog."""
cog = UwU()
await bot.add_cog(cog)
async def setup(bot):
await bot.add_cog(UwU(bot))
164 changes: 145 additions & 19 deletions uwu/uwu.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
"""UwU cog for Red-DiscordBot by PhasecoreX."""
"""uwu cog for Red-DiscordBot by PhasecoreX + Didi (The channel and user toggles)."""

# ruff: noqa: S311
import random
from contextlib import suppress
from typing import ClassVar

import discord
from redbot.core import commands
from redbot.core import Config, checks, commands

from .pcx_lib import type_message


class UwU(commands.Cog):
"""UwU."""
"""uwu."""

__author__ = "PhasecoreX"
__version__ = "2.1.1"
__author__ = "PhasecoreX + Didi"
__version__ = "2.5.0"

KAOMOJI_JOY: ClassVar[list[str]] = [
" (\\* ^ ω ^)",
Expand Down Expand Up @@ -49,6 +49,16 @@ class UwU(commands.Cog):
"-.-",
]

def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, identifier=1234567890)
self.config.register_global(uwu_channels={})
# Per-guild user toggles
self.config.register_guild(
user_uwu_toggle={}, admin_override={} # User opt-in # Admin-forced uwu
)
self._webhook_cache: dict[int, discord.Webhook] = {}

#
# Red methods
#
Expand All @@ -66,6 +76,86 @@ async def red_delete_data_for_user(self, *, _requester: str, _user_id: int) -> N
# Command methods
#

@commands.group()
@checks.is_owner()
async def uwuset(self, ctx: commands.Context):
"""Setup uwu channel settings."""

@uwuset.command(name="channel")
async def uwuset_channel(self, ctx: commands.Context, channel: discord.TextChannel):
"""Enable uwu auto-messages for a channel."""
channels = await self.config.uwu_channels()
channels[str(channel.id)] = True
await self.config.uwu_channels.set(channels)
await ctx.send(f"uwu channel set: {channel.mention}")

@uwuset.command(name="remove")
async def uwuset_remove(self, ctx: commands.Context, channel: discord.TextChannel):
"""Disable uwu auto-messages for a channel."""
channels = await self.config.uwu_channels()
channels.pop(str(channel.id), None)
await self.config.uwu_channels.set(channels)
self._webhook_cache.pop(channel.id, None)
await ctx.send(f"uwu channel removed: {channel.mention}")

@commands.group()
async def uwuuser(self, ctx: commands.Context):
"""Toggle per-user uwu webhook in this server."""

@uwuuser.command(name="toggle")
async def uwuuser_toggle(self, ctx: commands.Context):
"""Enable or disable uwu webhook for yourself in this server."""
admin_override = await self.config.guild(ctx.guild).admin_override()
if str(ctx.author.id) in admin_override:
return await ctx.send(
"Admin has forced uwu for you; you cannot disable it."
)

data = await self.config.guild(ctx.guild).user_uwu_toggle()
if str(ctx.author.id) in data:
data.pop(str(ctx.author.id))
await self.config.guild(ctx.guild).user_uwu_toggle.set(data)
await ctx.send("UwU webhook disabled for you in this server.")
else:
data[str(ctx.author.id)] = True
await self.config.guild(ctx.guild).user_uwu_toggle.set(data)
await ctx.send("UwU webhook enabled for you in this server.")

@uwuuser.command(name="list")
@checks.mod()
async def uwuuser_list(self, ctx: commands.Context):
"""List users with per-user uwu enabled or admin-forced."""
data = await self.config.guild(ctx.guild).user_uwu_toggle()
admin_override = await self.config.guild(ctx.guild).admin_override()
if not data and not admin_override:
return await ctx.send("No users have per-user uwu enabled.")
lines = []
for uid in set(list(data.keys()) + list(admin_override.keys())):
member = ctx.guild.get_member(int(uid))
name = member.display_name if member else f"User ID {uid}"
status = "Admin-forced" if uid in admin_override else "User-enabled"
lines.append(f"{name} — {status}")
await ctx.send("\n".join(lines))

@uwuuser.command(name="admin")
@checks.mod()
async def uwuuser_admin(self, ctx: commands.Context, member: discord.Member):
"""Toggle admin-forced uwu for a user."""
admin_override = await self.config.guild(ctx.guild).admin_override()
uid = str(member.id)
if uid in admin_override:
admin_override.pop(uid)
await self.config.guild(ctx.guild).admin_override.set(admin_override)
await ctx.send(f"Admin-forced uwu disabled for {member.display_name}.")
else:
admin_override[uid] = True
# Also remove user opt-in to prevent conflicts
user_toggle = await self.config.guild(ctx.guild).user_uwu_toggle()
user_toggle.pop(uid, None)
await self.config.guild(ctx.guild).user_uwu_toggle.set(user_toggle)
await self.config.guild(ctx.guild).admin_override.set(admin_override)
await ctx.send(f"Admin-forced UwU enabled for {member.display_name}.")

@commands.command(aliases=["owo"])
async def uwu(self, ctx: commands.Context, *, text: str | None = None) -> None:
"""Uwuize the replied to message, previous message, or your own text."""
Expand All @@ -79,7 +169,6 @@ async def uwu(self, ctx: commands.Context, *, text: str | None = None) -> None:
text = (await ctx.fetch_message(message_id)).content
if not text:
messages = [message async for message in ctx.channel.history(limit=2)]
# [0] is the command, [1] is the message before the command
text = messages[1].content or "I can't translate that!"
await type_message(
ctx.channel,
Expand All @@ -89,6 +178,53 @@ async def uwu(self, ctx: commands.Context, *, text: str | None = None) -> None:
),
)

#
# Event listener for auto-uwu channels
#

@commands.Cog.listener()
async def on_message(self, message: discord.Message):
if message.author.bot or not message.guild:
return

uwu_channels = await self.config.uwu_channels()
user_toggle = await self.config.guild(message.guild).user_uwu_toggle()
admin_override = await self.config.guild(message.guild).admin_override()

is_enabled = (
str(message.channel.id) in uwu_channels
or str(message.author.id) in user_toggle
or str(message.author.id) in admin_override
)

if not is_enabled:
return

uwu_content = self.uwuize_string(message.content)

# Delete original message
with suppress(discord.Forbidden, discord.NotFound, discord.HTTPException):
await message.delete()

# Get or create webhook from cache
webhook = self._webhook_cache.get(message.channel.id)
if not webhook:
webhooks = await message.channel.webhooks()
for wh in webhooks:
if wh.name == "uwu Bot":
webhook = wh
break
if not webhook:
webhook = await message.channel.create_webhook(name="uwu Bot")
self._webhook_cache[message.channel.id] = webhook

await webhook.send(
uwu_content,
username=message.author.display_name,
avatar_url=message.author.display_avatar.url,
allowed_mentions=discord.AllowedMentions.none(),
)

#
# Public methods
#
Expand All @@ -110,18 +246,13 @@ def uwuize_string(self, string: str) -> str:
return converted

def uwuize_word(self, word: str) -> str:
"""Uwuize and return a word.

Thank you to the following for inspiration:
https://github.com/senguyen1011/UwUinator
"""
"""Uwuize and return a word."""
word = word.lower()
uwu = word.rstrip(".?!,")
punctuations = word[len(uwu) :]
final_punctuation = punctuations[-1] if punctuations else ""
extra_punctuation = punctuations[:-1] if punctuations else ""

# Process punctuation
if final_punctuation == "." and not random.randint(0, 3):
final_punctuation = random.choice(self.KAOMOJI_JOY)
if final_punctuation == "?" and not random.randint(0, 2):
Expand All @@ -133,7 +264,6 @@ def uwuize_word(self, word: str) -> str:
if final_punctuation and not random.randint(0, 4):
final_punctuation = random.choice(self.KAOMOJI_SPARKLES)

# Full word exceptions
if uwu in ("you're", "youre"):
uwu = "ur"
elif uwu == "fuck":
Expand All @@ -152,17 +282,15 @@ def uwuize_word(self, word: str) -> str:
uwu = "b-butt"
elif uwu in ("dad", "father"):
uwu = "daddy"
# Normal word conversion
else:
# Protect specific word endings from changes
protected = ""
if uwu.endswith(("le", "ll", "er", "re")):
protected = uwu[-2:]
uwu = uwu[:-2]
elif uwu.endswith(("les", "lls", "ers", "res")):
protected = uwu[-3:]
uwu = uwu[:-3]
# l -> w, r -> w, n<vowel> -> ny<vowel>, ove -> uv

uwu = (
uwu.replace("l", "w")
.replace("r", "w")
Expand All @@ -175,14 +303,12 @@ def uwuize_word(self, word: str) -> str:
+ protected
)

# Add occasional stutter
if (
len(uwu) > 2 # noqa: PLR2004
len(uwu) > 2
and uwu[0].isalpha()
and "-" not in uwu
and not random.randint(0, 6)
):
uwu = f"{uwu[0]}-{uwu}"

# Add back punctuations and return
return uwu + extra_punctuation + final_punctuation