aboutsummaryrefslogtreecommitdiff
path: root/bot.py
diff options
context:
space:
mode:
authordavid <[email protected]>2026-10-21 20:16:19 -0300
committerdavid <[email protected]>2026-10-21 20:16:19 -0300
commitdc06343909d2df10c04203ab28bb7fe3c37edcd4 (patch)
tree112d391e54ff027322f0911c7f11f49599a9b32d /bot.py
parentd70458154894f432f45b96fe7139fe31af3f8124 (diff)
downloadfaggot-chan-dc06343909d2df10c04203ab28bb7fe3c37edcd4.tar.gz
faggot-chan-dc06343909d2df10c04203ab28bb7fe3c37edcd4.zip
added inline emojis, and on_edit for tenor and also the bot only saves gifv embeds instead of all images/videos whatever links... i think (i asked claude to do that for me because i couldn't figure it out) if it works as intended it should only save gifs and links ending with .gif
Diffstat (limited to 'bot.py')
-rw-r--r--bot.py44
1 files changed, 34 insertions, 10 deletions
diff --git a/bot.py b/bot.py
index a012827..4a13a57 100644
--- a/bot.py
+++ b/bot.py
@@ -85,7 +85,8 @@ QUOTA_COOLDOWN = 3600 # after gemini says it's out of quota go straight to the
quota_until = 0.0 # unix timestamp
MAX_REACTION_OPTIONS = 25 # per kind (stickers/emojis/media), random sample so the prompt stays small
-REACTION_TAG = re.compile(r'\[(sticker|emoji|media):\s*([^\]]+?)\s*\]', re.I)
+REACTION_TAG = re.compile(r'\[(sticker|reaction|media):\s*([^\]]+?)\s*\]', re.I)
+EMOJI_TAG = re.compile(r'\[emoji:\s*([^\]]+?)\s*\]', re.I) # inline, this one can go anywhere in the message
QUIET_COOLDOWN = 600 # seconds of silence before she's allowed to send a random message (10 mins)
@@ -297,11 +298,11 @@ async def steal_attachments(message):
]
for embed in message.embeds:
- #tenor/klipy links come as gifv embeds with an mp4 in embed.video direct gif links are plain image embeds
+ # tenor/klipy links come as "gifv" embeds with an mp4 in embed.video, direct .gif links are plain image embeds
for part in (embed.image, embed.video):
if part and part.url:
- ext = Path(urlparse(part.url).path).suffix # no extension no preview on discord not worth keeping
- if ext and (embed.type == "gifv" or ext.lower() == ".gif"):
+ ext = Path(urlparse(part.url).path).suffix # no extension, no preview on discord, not worth keeping
+ if ext and (embed.type == "gifv" or ext.lower() == ".gif"):
media.append((part.url, hashlib.sha1(part.url.encode()).hexdigest()[:16] + ext))
for url, name in media:
@@ -456,13 +457,23 @@ def reaction_tool_prompt(guild) -> str:
sections.append(f"{key}:\n" + "\n".join(f"- {n}: {d}" if d else f"- {n}" for n, d in options))
if not sections:
return ""
- return ("\n\nREACTIONS: you can send a sticker, an emoji reaction or a media file instead of (or along with) words, but only when it really fits the moment, most of the time just reply normally. "
- "to do it put ONE tag at the end of your message: [sticker: name], [emoji: name] or [media: name], using exactly one of the names below. "
- "the tag alone is fine if you have nothing to say. never invent names.\n" + "\n".join(sections))
+ emoji_note = ("\n\nyou can also put a server emoji inside the text of your message with [emoji: name], like \"hi [emoji: name] hello\", using a name from the emojis list. "
+ "don't use emojis in every message, most of your messages should have none.") if any(s.startswith("emojis:") for s in sections) else ""
+ return ("\n\nREACTIONS: you can send a sticker, react to the message with an emoji or send a media file instead of (or along with) words, but only when it really fits the moment, most of the time just reply normally. "
+ "to do it put ONE tag at the end of your message: [sticker: name], [reaction: name] or [media: name], using exactly one of the names below ([reaction: name] uses the emoji names). "
+ "the tag alone is fine if you have nothing to say. never invent names.\n" + "\n".join(sections) + emoji_note)
def split_reactions(text: str) -> tuple[str, list]:
return REACTION_TAG.sub("", text).strip(), [(k.lower(), n) for k, n in REACTION_TAG.findall(text)]
+def fill_emojis(text: str, guild) -> str:
+ # [emoji: name] inside a message becomes the real custom emoji, same check as the reactions (has to exist and be available), a made up name just disappears
+ def replace(m):
+ name = m.group(1).strip().strip(":").lower()
+ chosen = next((e for e in guild.emojis if e.available and e.name.lower() == name), None)
+ return str(chosen) if chosen else ""
+ return EMOJI_TAG.sub(replace, text)
+
async def send_reaction(message, kind: str, name: str):
# looks the name up for real, a made up name just does nothing
name = name.strip().strip(":").lower()
@@ -473,7 +484,7 @@ async def send_reaction(message, kind: str, name: str):
await message.channel.send(stickers=[chosen])
log_message(f"[{now_str()}] Faggot-chan sent sticker: {chosen.name}")
print(f"[{now_str()}] Faggot-chan sent sticker: {chosen.name}")
- elif kind == "emoji":
+ elif kind == "reaction":
chosen = next((e for e in message.guild.emojis if e.available and e.name.lower() == name), None)
if chosen:
await message.add_reaction(chosen)
@@ -596,9 +607,10 @@ async def on_message(message):
if not result.strip() and not reactions:
return
+ shown = fill_emojis(result, message.guild) # what gets sent, result keeps the [emoji: x] tags for the log and history
try:
- if result.strip():
- await send_reply(message, result)
+ if shown.strip():
+ await send_reply(message, shown)
print(f"[{now_str()}] {message.author} said: {content}\n[{now_str()}] Faggot-chan said: {result}")
log_message(f"[{now_str()}] Faggot-chan said: {result}")
if str(message.author.id) == settings["moralfag_id"]:
@@ -611,4 +623,16 @@ async def on_message(message):
except Exception as e:
await handle_error(f"[{now_str()}] failed to send reply: {e}", notify_channel=message.channel)
+async def on_message_edit(before, after):
+ # discord builds link embeds (tenor etc) a moment after the message is sent, so message.embeds is still empty in on_message
+ # and the embed shows up here as an edit instead
+ if after.author == client.user or after.author.bot or after.guild is None:
+ return
+ if not after.embeds or before.embeds: # only when embeds just appeared
+ return
+ if after.channel.name not in ["general", "announcements"] or str(after.author.id) in load_blocked():
+ return
+ await steal_attachments(after)
+
client.run(TOKEN)