From 586c921e8b220893bec4e9fbc11a2689db38e476 Mon Sep 17 00:00:00 2001 From: osmarks Date: Fri, 26 Jul 2024 10:56:13 +0100 Subject: [PATCH] Refactor, autogollark --- src/achievement.py | 1 + src/commands.py | 47 ---------------- src/sentience.py | 132 +++++++++++++++++++++++++++++++++++++++++++++ src/util.py | 3 +- 4 files changed, 135 insertions(+), 48 deletions(-) create mode 100644 src/sentience.py diff --git a/src/achievement.py b/src/achievement.py index 517b37d..1e65ae1 100644 --- a/src/achievement.py +++ b/src/achievement.py @@ -22,6 +22,7 @@ achievements = { } async def achieve(bot: commands.Bot, message: discord.Message, achievement): + guild_conf = None if message.guild: guild_conf = await bot.database.execute_fetchone("SELECT achievement_messages FROM guild_config WHERE id = ?", (message.guild.id,)) if guild_conf and guild_conf["achievement_messages"] == 0: return diff --git a/src/commands.py b/src/commands.py index 52243cc..165bb1f 100644 --- a/src/commands.py +++ b/src/commands.py @@ -195,52 +195,5 @@ AutoBotRobot is operated by gollark/osmarks. await ctx.send("\n".join(map(lambda x: f"{x[0]} x{x[1]}", results))) - @commands.command(help="Highly advanced AI Assistant.") - async def ai(self, ctx, *, query=None): - prompt = [] - seen = set() - - def render(dt: datetime): - return f"{dt.hour:02}:{dt.minute:02}" - - async for message in ctx.channel.history(limit=20): - display_name = message.author.display_name - if message.author == self.bot.user: - display_name = util.config["ai"]["own_name"] - content = message.content - if content.startswith(ctx.prefix + "ai"): - content = content.removeprefix(ctx.prefix + "ai").lstrip() - if not content and message.embeds: - content = message.embeds[0].title - elif not content and message.attachments: - content = "[attachments]" - if not content: - continue - if message.author == self.bot.user: - if content in seen: continue - seen.add(content) - prompt.append(f"[{render(message.created_at)}] {display_name}: {content}\n") - if sum(len(x) for x in prompt) > util.config["ai"]["max_len"]: - break - prompt.reverse() - prompt.append(f'[{render(datetime.utcnow())}] {util.config["ai"]["own_name"]}:') - generation = await util.generate(self.session, util.config["ai"]["prompt_start"] + "".join(prompt)) - if generation.strip(): await ctx.send(generation.strip()) - - @commands.command(help="Search meme library.", aliases=["memes"]) - async def meme(self, ctx, *, query=None): - search_many = ctx.invoked_with == "memes" - raw_memes = await util.user_config_lookup(ctx, "enable_raw_memes") == "true" - async with self.session.post(util.config["memetics"]["meme_search_backend"], json={ - "terms": [{"text": query, "weight": 1}], - "k": 4 if search_many else 1 - }) as res: - results = await res.json() - if raw_memes: - o_files = [ discord.File(Path(util.config["memetics"]["memes_local"]) / Path(util.config["memetics"]["meme_base"]) / m[1]) for m in results["matches"] ] - else: - o_files = [ discord.File(Path(util.config["memetics"]["memes_local"]) / util.meme_thumbnail(results, m)) for m in results["matches"] ] - await ctx.send(files=o_files) - def setup(bot): bot.add_cog(GeneralCommands(bot)) diff --git a/src/sentience.py b/src/sentience.py new file mode 100644 index 0000000..84a1b8b --- /dev/null +++ b/src/sentience.py @@ -0,0 +1,132 @@ +import asyncio +import argparse +import random +from numpy.random import default_rng +import re +import aiohttp +import subprocess +import discord.ext.commands as commands +import discord +from datetime import datetime +from pathlib import Path + +import tio +import util + +def render(dt: datetime): + return f"{dt.hour:02}:{dt.minute:02}" + +cleaner = commands.clean_content() +def clean(ctx, text): + return cleaner.convert(ctx, text) + +AUTOGOLLARK_MARKER = "\u200b" +AUTOGOLLARK_GOLLARK = "autogollark" + +class Sentience(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.session = aiohttp.ClientSession() + + async def serialize_history(self, ctx, n=20): + PREFIXES = [ ctx.prefix + "ai", ctx.prefix + "ag", ctx.prefix + "autogollark", ctx.prefix + "gollark" ] + + prompt = [] + seen = set() + async for message in ctx.channel.history(limit=n): + display_name = message.author.display_name + if message.author == self.bot.user: + display_name = util.config["ai"]["own_name"] + content = message.content + for prefix in PREFIXES: + if content.startswith(prefix): + content = content.removeprefix(prefix).lstrip() + if not content and message.embeds: + content = message.embeds[0].title + elif not content and message.attachments: + content = "[attachments]" + if not content: + continue + if message.author == self.bot.user: + if message.content.startswith(AUTOGOLLARK_MARKER): + content = message.content.removeprefix(AUTOGOLLARK_MARKER) + display_name = AUTOGOLLARK_GOLLARK + + if content in seen: continue + seen.add(content) + prompt.append(f"[{render(message.created_at)}] {display_name}: {content}\n") + if sum(len(x) for x in prompt) > util.config["ai"]["max_len"]: + break + prompt.reverse() + return prompt + + @commands.command(help="Highly advanced AI Assistant.") + async def ai(self, ctx, *, query=None): + prompt = await self.serialize_history(ctx) + prompt.append(f'[{render(datetime.utcnow())}] {util.config["ai"]["own_name"]}:') + generation = await util.generate(self.session, util.config["ai"]["prompt_start"] + "".join(prompt)) + if generation.strip(): + await ctx.send(generation.strip()) + + @commands.command(help="Emulated gollark instance.", aliases=["gollark", "ag"]) + async def autogollark(self, ctx): + prompt = await self.serialize_history(ctx, n=50) + prompt.append(f"[{render(datetime.utcnow())}] {AUTOGOLLARK_GOLLARK}:") + conversation = "".join(prompt) + # retrieve gollark data from backend + gollark_chunks = [] + async with self.session.post(util.config["ai"]["autogollark_server"], json={"query": conversation}) as res: + for chunk in (await res.json()): + gollark_chunk = [] + if sum(len(y) for x in gollark_chunks for y in x) > util.config["ai"]["max_gollark_len"]: gollark_chunks.pop(0) + for message in chunk: + dt = datetime.fromisoformat(message["timestamp"]) + line = f"[{render(dt)}] {message['author'] or AUTOGOLLARK_GOLLARK}: {await clean(ctx, message['contents'])}\n" + gollark_chunk.append(line) + + # ugly hack to remove duplicates + ds = [] + for chunk in gollark_chunks: + if line in chunk and line != "---\n": ds.append(chunk) + for d in ds: + print("delete chunk", d) + try: + gollark_chunks.remove(d) + except ValueError: pass + + gollark_chunk.append("---\n") + gollark_chunks.append(gollark_chunk) + + gollark_data = "".join("".join(x) for x in gollark_chunks) + + print(gollark_data + conversation) + + # generate response + generation = await util.generate(self.session, "GOLLARK SAMPLES:\n" + gollark_data + "CONVERSATION:\n" + conversation) + print("output", generation) + if generation.strip(): + await ctx.send(AUTOGOLLARK_MARKER + generation.strip()) + + @commands.Cog.listener("on_message") + async def autogollark_listener(self, message): + if message.channel.id in util.config["ai"]["autogollark_channels"] and not message.content.startswith(AUTOGOLLARK_MARKER): + await self.autogollark(commands.Context(bot=self.bot, message=message, prefix="", view=None)) + + @commands.command(help="Search meme library.", aliases=["memes"]) + async def meme(self, ctx, *, query=None): + search_many = ctx.invoked_with == "memes" + raw_memes = await util.user_config_lookup(ctx, "enable_raw_memes") == "true" + async with self.session.post(util.config["memetics"]["meme_search_backend"], json={ + "terms": [{"text": query, "weight": 1}], + "k": 20 + }) as res: + results = await res.json() + results = results[:(4 if search_many else 1)] + if raw_memes: + o_files = [ discord.File(Path(util.config["memetics"]["memes_local"]) / Path(util.config["memetics"]["meme_base"]) / m[1]) for m in results["matches"] ] + else: + o_files = [ discord.File(Path(util.config["memetics"]["memes_local"]) / util.meme_thumbnail(results, m)) for m in results["matches"] ] + await ctx.send(files=o_files) + +def setup(bot): + bot.add_cog(Sentience(bot)) diff --git a/src/util.py b/src/util.py index d098a59..e56cd2a 100644 --- a/src/util.py +++ b/src/util.py @@ -314,7 +314,8 @@ extensions = ( "userdata", "irc_link", "search", - "esoserver" + "esoserver", + "sentience" ) # https://github.com/SawdustSoftware/simpleflake/blob/master/simpleflake/simpleflake.py