everybody wants to change the world but noone wants to die
This commit is contained in:
parent
6d81539fc5
commit
e6905b1d0a
6 changed files with 181 additions and 83 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,3 +8,4 @@ memory.json
|
||||||
venv/
|
venv/
|
||||||
output.png
|
output.png
|
||||||
.vscode/
|
.vscode/
|
||||||
|
received_memory.json
|
||||||
|
|
59
bot.py
59
bot.py
|
@ -5,6 +5,8 @@ import time
|
||||||
import random
|
import random
|
||||||
import traceback
|
import traceback
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import shutil
|
||||||
|
|
||||||
from modules.globalvars import *
|
from modules.globalvars import *
|
||||||
from modules.prestartchecks import start_checks
|
from modules.prestartchecks import start_checks
|
||||||
|
@ -35,7 +37,6 @@ from modules.image import gen_image
|
||||||
|
|
||||||
sys.excepthook = handle_exception
|
sys.excepthook = handle_exception
|
||||||
check_for_update() # Check for updates (from modules/version.py)
|
check_for_update() # Check for updates (from modules/version.py)
|
||||||
launched = False
|
|
||||||
|
|
||||||
# Ensure required NLTK resources are available
|
# Ensure required NLTK resources are available
|
||||||
def check_resources():
|
def check_resources():
|
||||||
|
@ -240,10 +241,60 @@ async def talk(ctx, sentence_size: int = 5):
|
||||||
bot.help_command = None
|
bot.help_command = None
|
||||||
|
|
||||||
# Command: Show help information
|
# Command: Show help information
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
@bot.hybrid_command(description=f"{get_translation(LOCALE, 'command_desc_help')}")
|
@bot.hybrid_command(description=f"{get_translation(LOCALE, 'command_desc_help')}")
|
||||||
async def image(ctx):
|
async def image(ctx):
|
||||||
await gen_image()
|
assets_folder = "assets/images"
|
||||||
await send_message(ctx, file=discord.File("output.png"))
|
temp_input = None
|
||||||
|
|
||||||
|
def get_random_asset_image():
|
||||||
|
files = [f for f in os.listdir(assets_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
||||||
|
if not files:
|
||||||
|
return None
|
||||||
|
return os.path.join(assets_folder, random.choice(files))
|
||||||
|
|
||||||
|
if ctx.message.attachments:
|
||||||
|
attachment = ctx.message.attachments[0]
|
||||||
|
if attachment.content_type and attachment.content_type.startswith("image/"):
|
||||||
|
ext = os.path.splitext(attachment.filename)[1]
|
||||||
|
temp_input = f"tempy{ext}"
|
||||||
|
await attachment.save(temp_input)
|
||||||
|
input_path = temp_input
|
||||||
|
else:
|
||||||
|
fallback_image = get_random_asset_image()
|
||||||
|
if fallback_image is None:
|
||||||
|
await ctx.reply("No image available to process.")
|
||||||
|
return
|
||||||
|
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
||||||
|
shutil.copy(fallback_image, temp_input)
|
||||||
|
input_path = temp_input
|
||||||
|
else:
|
||||||
|
fallback_image = get_random_asset_image()
|
||||||
|
if fallback_image is None:
|
||||||
|
await ctx.reply("No image available to process.")
|
||||||
|
return
|
||||||
|
# got lazy here
|
||||||
|
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
||||||
|
shutil.copy(fallback_image, temp_input)
|
||||||
|
input_path = temp_input
|
||||||
|
|
||||||
|
output_path = await gen_image(input_path)
|
||||||
|
|
||||||
|
if output_path is None or not os.path.isfile(output_path):
|
||||||
|
if temp_input and os.path.exists(temp_input):
|
||||||
|
os.remove(temp_input)
|
||||||
|
await ctx.reply("Failed to generate text on the image.")
|
||||||
|
return
|
||||||
|
|
||||||
|
await ctx.send(file=discord.File(output_path))
|
||||||
|
|
||||||
|
if temp_input and os.path.exists(temp_input):
|
||||||
|
os.remove(temp_input)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Remove default help command to use custom help
|
# Remove default help command to use custom help
|
||||||
|
@ -259,7 +310,7 @@ async def help(ctx):
|
||||||
)
|
)
|
||||||
|
|
||||||
command_categories = {
|
command_categories = {
|
||||||
f"{get_translation(LOCALE, 'command_help_categories_general')}": ["mem", "talk", "about", "ping"],
|
f"{get_translation(LOCALE, 'command_help_categories_general')}": ["mem", "talk", "about", "ping", "image"],
|
||||||
f"{get_translation(LOCALE, 'command_help_categories_admin')}": ["stats", "retrain"]
|
f"{get_translation(LOCALE, 'command_help_categories_admin')}": ["stats", "retrain"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
48
cogs/filesharing.py
Normal file
48
cogs/filesharing.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
from modules.globalvars import ownerid
|
||||||
|
class FileSync(commands.Cog):
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
self.mode = None
|
||||||
|
self.peer_id = None
|
||||||
|
self.awaiting_file = False
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def syncfile(self, ctx, mode: str, peer: discord.User):
|
||||||
|
self.mode = mode.lower()
|
||||||
|
self.peer_id = peer.id
|
||||||
|
if ctx.author.id != ownerid:
|
||||||
|
await ctx.send("You don't have permission to execute this command.")
|
||||||
|
return
|
||||||
|
if self.mode == "s":
|
||||||
|
await ctx.send(f"<@{self.peer_id}> FILE_TRANSFER_REQUEST")
|
||||||
|
await ctx.send(file=discord.File("memory.json"))
|
||||||
|
await ctx.send("File sent in this channel.")
|
||||||
|
elif self.mode == "r":
|
||||||
|
await ctx.send("Waiting for incoming file...")
|
||||||
|
self.awaiting_file = True
|
||||||
|
else:
|
||||||
|
await ctx.send("Invalid mode, use 's' or 'r'.")
|
||||||
|
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_message(self, message):
|
||||||
|
if message.author == self.bot.user or not self.awaiting_file:
|
||||||
|
return
|
||||||
|
if message.author.id != self.peer_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
if message.content == "FILE_TRANSFER_REQUEST":
|
||||||
|
print("Ping received. Awaiting file...")
|
||||||
|
if message.attachments:
|
||||||
|
for attachment in message.attachments:
|
||||||
|
if attachment.filename.endswith(".json"):
|
||||||
|
filename = "received_memory.json"
|
||||||
|
await attachment.save(filename)
|
||||||
|
print(f"File saved as {filename}")
|
||||||
|
await message.channel.send("File received and saved.")
|
||||||
|
self.awaiting_file = False
|
||||||
|
|
||||||
|
async def setup(bot):
|
||||||
|
await bot.add_cog(FileSync(bot))
|
|
@ -37,6 +37,7 @@ IGNOREWARNING = False
|
||||||
song = os.getenv("song")
|
song = os.getenv("song")
|
||||||
arch = platform.machine()
|
arch = platform.machine()
|
||||||
slash_commands_enabled = False
|
slash_commands_enabled = False
|
||||||
|
launched = False
|
||||||
latest_version = "0.0.0"
|
latest_version = "0.0.0"
|
||||||
local_version = "1.0.2"
|
local_version = "1.0.3"
|
||||||
os.environ['gooberlocal_version'] = local_version
|
os.environ['gooberlocal_version'] = local_version
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import os
|
import os
|
||||||
import random
|
|
||||||
import re
|
import re
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
from modules.markovmemory import load_markov_model
|
from modules.markovmemory import load_markov_model
|
||||||
|
@ -7,56 +6,42 @@ from modules.sentenceprocessing import improve_sentence_coherence, rephrase_for_
|
||||||
|
|
||||||
generated_sentences = set()
|
generated_sentences = set()
|
||||||
|
|
||||||
async def gen_image(sentence_size=5, max_attempts=10):
|
async def gen_image(input_image_path, sentence_size=5, max_attempts=10):
|
||||||
images_folder = "assets/images"
|
|
||||||
image_files = [f for f in os.listdir(images_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
|
||||||
if not image_files:
|
|
||||||
return
|
|
||||||
|
|
||||||
markov_model = load_markov_model()
|
markov_model = load_markov_model()
|
||||||
if not markov_model:
|
if not markov_model:
|
||||||
return
|
return None
|
||||||
|
if not os.path.isfile(input_image_path):
|
||||||
|
return None
|
||||||
def load_font(size):
|
def load_font(size):
|
||||||
return ImageFont.truetype("assets/fonts/Impact.ttf", size=size)
|
return ImageFont.truetype("assets/fonts/Impact.ttf", size=size)
|
||||||
|
|
||||||
def draw_text_with_outline(draw, text, x, y, font):
|
def draw_text_with_outline(draw, text, x, y, font):
|
||||||
outline_offsets = [(-2, -2), (-2, 2), (2, -2), (2, 2), (0, -2), (0, 2), (-2, 0), (2, 0)]
|
outline_offsets = [(-2, -2), (-2, 2), (2, -2), (2, 2), (0, -2), (0, 2), (-2, 0), (2, 0)]
|
||||||
for ox, oy in outline_offsets:
|
for ox, oy in outline_offsets:
|
||||||
draw.text((x + ox, y + oy), text, font=font, fill="black")
|
draw.text((x + ox, y + oy), text, font=font, fill="black")
|
||||||
draw.text((x, y), text, font=font, fill="white")
|
draw.text((x, y), text, font=font, fill="white")
|
||||||
|
def fits_in_width(text, font, max_width, draw):
|
||||||
def text_height(font, text):
|
|
||||||
bbox = draw.textbbox((0, 0), text, font=font)
|
|
||||||
return bbox[3] - bbox[1]
|
|
||||||
|
|
||||||
def fits_in_width(text, font, max_width):
|
|
||||||
bbox = draw.textbbox((0, 0), text, font=font)
|
bbox = draw.textbbox((0, 0), text, font=font)
|
||||||
text_width = bbox[2] - bbox[0]
|
text_width = bbox[2] - bbox[0]
|
||||||
return text_width <= max_width
|
return text_width <= max_width
|
||||||
|
def split_text_to_fit(text, font, max_width, draw):
|
||||||
def split_text_to_fit(text, font, max_width):
|
|
||||||
words = text.split()
|
words = text.split()
|
||||||
for i in range(len(words), 0, -1):
|
for i in range(len(words), 0, -1):
|
||||||
top_text = " ".join(words[:i])
|
top_text = " ".join(words[:i])
|
||||||
bottom_text = " ".join(words[i:])
|
bottom_text = " ".join(words[i:])
|
||||||
if fits_in_width(top_text, font, max_width) and fits_in_width(bottom_text, font, max_width):
|
if fits_in_width(top_text, font, max_width, draw) and fits_in_width(bottom_text, font, max_width, draw):
|
||||||
return top_text, bottom_text
|
return top_text, bottom_text
|
||||||
midpoint = len(words) // 2
|
midpoint = len(words) // 2
|
||||||
return " ".join(words[:midpoint]), " ".join(words[midpoint:])
|
return " ".join(words[:midpoint]), " ".join(words[midpoint:])
|
||||||
|
|
||||||
coherent_response = "no text generated"
|
|
||||||
attempt = 0
|
attempt = 0
|
||||||
while attempt < max_attempts:
|
while attempt < max_attempts:
|
||||||
chosen_image_path = os.path.join(images_folder, random.choice(image_files))
|
with Image.open(input_image_path).convert("RGBA") as img:
|
||||||
img = Image.open(chosen_image_path).convert("RGBA")
|
|
||||||
draw = ImageDraw.Draw(img)
|
draw = ImageDraw.Draw(img)
|
||||||
width, height = img.size
|
width, height = img.size
|
||||||
|
|
||||||
font_size = int(height / 10)
|
font_size = int(height / 10)
|
||||||
font = load_font(font_size)
|
font = load_font(font_size)
|
||||||
|
|
||||||
# Generate text
|
|
||||||
response = None
|
response = None
|
||||||
for _ in range(20):
|
for _ in range(20):
|
||||||
if sentence_size == 1:
|
if sentence_size == 1:
|
||||||
|
@ -78,35 +63,44 @@ async def gen_image(sentence_size=5, max_attempts=10):
|
||||||
|
|
||||||
cleaned_response = re.sub(r'[^\w\s]', '', response).lower()
|
cleaned_response = re.sub(r'[^\w\s]', '', response).lower()
|
||||||
coherent_response = rephrase_for_coherence(cleaned_response).upper()
|
coherent_response = rephrase_for_coherence(cleaned_response).upper()
|
||||||
|
|
||||||
bbox = draw.textbbox((0, 0), coherent_response, font=font)
|
bbox = draw.textbbox((0, 0), coherent_response, font=font)
|
||||||
text_width = bbox[2] - bbox[0]
|
text_width = bbox[2] - bbox[0]
|
||||||
text_height_px = bbox[3] - bbox[1]
|
text_height_px = bbox[3] - bbox[1]
|
||||||
max_text_height = height // 4
|
max_text_height = height // 4
|
||||||
|
|
||||||
if text_width <= width and text_height_px <= max_text_height:
|
if text_width <= width and text_height_px <= max_text_height:
|
||||||
# Fits on top, draw at y=0 (top)
|
|
||||||
draw_text_with_outline(draw, coherent_response, (width - text_width) / 2, 0, font)
|
draw_text_with_outline(draw, coherent_response, (width - text_width) / 2, 0, font)
|
||||||
img.save("output.png")
|
img.save(input_image_path)
|
||||||
return
|
return input_image_path
|
||||||
top_text, bottom_text = split_text_to_fit(coherent_response, font, width)
|
else:
|
||||||
|
top_text, bottom_text = split_text_to_fit(coherent_response, font, width, draw)
|
||||||
|
|
||||||
top_bbox = draw.textbbox((0, 0), top_text, font=font)
|
top_bbox = draw.textbbox((0, 0), top_text, font=font)
|
||||||
bottom_bbox = draw.textbbox((0, 0), bottom_text, font=font)
|
bottom_bbox = draw.textbbox((0, 0), bottom_text, font=font)
|
||||||
|
|
||||||
top_height = top_bbox[3] - top_bbox[1]
|
top_height = top_bbox[3] - top_bbox[1]
|
||||||
bottom_height = bottom_bbox[3] - bottom_bbox[1]
|
bottom_height = bottom_bbox[3] - bottom_bbox[1]
|
||||||
|
|
||||||
if top_height <= max_text_height and bottom_height <= max_text_height:
|
if top_height <= max_text_height and bottom_height <= max_text_height:
|
||||||
# top text
|
|
||||||
draw_text_with_outline(draw, top_text, (width - (top_bbox[2] - top_bbox[0])) / 2, 0, font)
|
draw_text_with_outline(draw, top_text, (width - (top_bbox[2] - top_bbox[0])) / 2, 0, font)
|
||||||
y_bottom = height - bottom_height - int(height * 0.04)
|
y_bottom = height - bottom_height - int(height * 0.04)
|
||||||
draw_text_with_outline(draw, bottom_text, (width - (bottom_bbox[2] - bottom_bbox[0])) / 2, y_bottom, font)
|
draw_text_with_outline(draw, bottom_text, (width - (bottom_bbox[2] - bottom_bbox[0])) / 2, y_bottom, font)
|
||||||
img.save("output.png")
|
img.save(input_image_path)
|
||||||
return
|
return input_image_path
|
||||||
|
|
||||||
attempt += 1
|
attempt += 1
|
||||||
|
|
||||||
# If all attempts fail, cry.
|
|
||||||
|
with Image.open(input_image_path).convert("RGBA") as img:
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
width, height = img.size
|
||||||
|
font_size = int(height / 10)
|
||||||
|
font = load_font(font_size)
|
||||||
|
|
||||||
truncated = coherent_response[:100]
|
truncated = coherent_response[:100]
|
||||||
bbox = draw.textbbox((0, 0), truncated, font=font)
|
bbox = draw.textbbox((0, 0), truncated, font=font)
|
||||||
text_width = bbox[2] - bbox[0]
|
text_width = bbox[2] - bbox[0]
|
||||||
draw_text_with_outline(draw, truncated, (width - text_width) / 2, 0, font)
|
draw_text_with_outline(draw, truncated, (width - text_width) / 2, 0, font)
|
||||||
img.save("output.png")
|
img.save(input_image_path)
|
||||||
|
return input_image_path
|
||||||
|
|
|
@ -17,6 +17,9 @@ def is_remote_ahead(branch='main', remote='origin'):
|
||||||
|
|
||||||
# Automatically update the local repository if the remote is ahead
|
# Automatically update the local repository if the remote is ahead
|
||||||
def auto_update(branch='main', remote='origin'):
|
def auto_update(branch='main', remote='origin'):
|
||||||
|
if launched == True:
|
||||||
|
print("I've already started! Im not updating...")
|
||||||
|
return
|
||||||
if AUTOUPDATE != "True":
|
if AUTOUPDATE != "True":
|
||||||
pass # Auto-update is disabled
|
pass # Auto-update is disabled
|
||||||
if is_remote_ahead(branch, remote):
|
if is_remote_ahead(branch, remote):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue