now accepting multiple emotes as input
This commit is contained in:
parent
e1c0cd2286
commit
18caf33d3e
@ -80,117 +80,98 @@ def check_if_emote_reacted():
|
|||||||
return None, None, None
|
return None, None, None
|
||||||
if 'reactions' in message:
|
if 'reactions' in message:
|
||||||
#print('reactions: ', message['reactions'])
|
#print('reactions: ', message['reactions'])
|
||||||
|
keys = []
|
||||||
|
attachment = message["attachments"][0]
|
||||||
|
attachment_url = f"https://stoat.unloze.com/autumn/attachments/{attachment['_id']}/tree.png"
|
||||||
for key, _ in message['reactions'].items():
|
for key, _ in message['reactions'].items():
|
||||||
attachment = message["attachments"][0]
|
keys.append(key)
|
||||||
attachment_url = f"https://stoat.unloze.com/autumn/attachments/{attachment['_id']}/tree.png"
|
return keys, extract_metrics(message["content"]), attachment_url
|
||||||
return key, extract_metrics(message["content"]), attachment_url
|
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
def is_custom_emote(emote_key):
|
def is_custom_emote(emote_key):
|
||||||
return emote_key.isascii() and emote_key.isalnum()
|
return emote_key.isascii() and emote_key.isalnum()
|
||||||
|
|
||||||
def send_claude_message(reacted_emote, metrics, custom_emotes_json, attachment_url):
|
def send_claude_message(reacted_emotes, metrics, custom_emotes_json, attachment_url):
|
||||||
prev_image_data = requests.get(attachment_url).content
|
prev_image_data = requests.get(attachment_url).content
|
||||||
prev_image_base64 = base64.b64encode(prev_image_data).decode("utf-8")
|
prev_image_base64 = base64.b64encode(prev_image_data).decode("utf-8")
|
||||||
|
|
||||||
if is_custom_emote(reacted_emote):
|
unicode_emotes = []
|
||||||
img_response = requests.get("https://stoat.unloze.com/autumn/emojis/" + reacted_emote)
|
custom_emote_dicts = []
|
||||||
img_data = img_response.content
|
custom_emotes_name = []
|
||||||
content_type = img_response.headers.get("content-type", "")
|
for emote in reacted_emotes:
|
||||||
#static or gif?
|
if is_custom_emote(emote):
|
||||||
if "gif" in content_type:
|
img_response = requests.get("https://stoat.unloze.com/autumn/emojis/" + emote)
|
||||||
# extract first frame only
|
img_data = img_response.content
|
||||||
img = Image.open(io.BytesIO(img_data))
|
content_type = img_response.headers.get("content-type", "")
|
||||||
img.seek(0)
|
#static or gif?
|
||||||
buffer = io.BytesIO()
|
if "gif" in content_type:
|
||||||
img.convert("RGBA").save(buffer, format="PNG")
|
# extract first frame only
|
||||||
img_data = buffer.getvalue()
|
img = Image.open(io.BytesIO(img_data))
|
||||||
media_type = "image/png"
|
img.seek(0)
|
||||||
|
buffer = io.BytesIO()
|
||||||
|
img.convert("RGBA").save(buffer, format="PNG")
|
||||||
|
img_data = buffer.getvalue()
|
||||||
|
media_type = "image/png"
|
||||||
|
else:
|
||||||
|
media_type = "image/webp"
|
||||||
|
|
||||||
|
img_base64 = base64.b64encode(img_data).decode("utf-8")
|
||||||
|
|
||||||
|
emote_name = ""
|
||||||
|
for custom_emote_js in custom_emotes_json:
|
||||||
|
if custom_emote_js['_id'] == emote:
|
||||||
|
emote_name = custom_emote_js['name']
|
||||||
|
print('found emote name: ', emote_name)
|
||||||
|
break
|
||||||
|
custom_emotes_name.append(emote_name)
|
||||||
|
custom_emote_dicts.append(
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"source": {
|
||||||
|
"type": "base64",
|
||||||
|
"media_type": media_type,
|
||||||
|
"data": img_base64
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
media_type = "image/webp"
|
unicode_emotes.append(emote)
|
||||||
|
|
||||||
img_base64 = base64.b64encode(img_data).decode("utf-8")
|
custom_emotes_text = f"\nCustom emote names in order: '{custom_emotes_name}'." if custom_emote_dicts else ""
|
||||||
|
unicode_text = f"\nUnicode emojis reacted: {unicode_emotes}" if unicode_emotes else ""
|
||||||
emote_name = ""
|
response = requests.post(
|
||||||
for custom_emote_js in custom_emotes_json:
|
"https://api.anthropic.com/v1/messages",
|
||||||
if custom_emote_js['_id'] == reacted_emote:
|
headers={
|
||||||
emote_name = custom_emote_js['name']
|
"x-api-key": claude_api_key,
|
||||||
print('found emote name: ', emote_name)
|
"anthropic-version": "2023-06-01",
|
||||||
break
|
"content-type": "application/json"
|
||||||
|
},
|
||||||
response = requests.post(
|
json={
|
||||||
"https://api.anthropic.com/v1/messages",
|
"model": "claude-haiku-4-5-20251001",
|
||||||
headers={
|
"max_tokens": 2048,
|
||||||
"x-api-key": claude_api_key,
|
"system": """You are managing a living tree. You receive the tree's current metrics and one or many emoji reactions, and you must decide how the emojies affects the tree. Some of the emotes are unicode and some are custom. Invent new metrics freely but only track things numerically. Be silly, chaotic and creative. Based on the description of what happened to the tree, decide whether the tree grows or shrinks and by how much. Growth and shrink amounts should be proportional to the current phase — in the sapling phase typical changes are 1-50cm, young tree 1-100cm, mature tree 1-1000cm, ancient tree 1-10000cm but you can deviate. Not all emotes should cause growth — damaging, violent or destructive emotes should typically cause the tree to shrink. The tree has growth phases: sapling (0-500cm), young tree (500-50000cm), mature tree (50000-500000cm), ancient tree (500000cm+). Mention the phase when it changes. Keep the metrics object to a maximum of 24 metrics at all times. When adding a new metric, remove the least interesting or relevant existing one to make room. Always keep height_cm. Also return a "sleep_seconds" field: the cooldown before the next reactions are accepted. Scale it strictly based on height_cm using these boundaries: sapling: 20-60 seconds, young tree: 60-300 seconds, mature tree: 300-900 seconds, ancient tree: 900-1800 seconds. Never exceed 1800 seconds. Stay within the range for the current phase. Respond in JSON like: {"metrics": {"height_cm": 42, ...}, "message": "...", "sleep_seconds": 30, "image_prompt": "..."}. The image_prompt should visually depict what the message describes happening to the tree — any effects, damage, mutations or chaos described in the message for example. The image_prompt should depict the tree with the emotes characters or object visually integrated into or interacting with the tree — as if the emote itself is physically there affecting it or simply a part of the image. When writing the image_prompt, translate the tree's phase into a visual size description — for example if it is a tiny sprout, is a small sapling, is a knee-height bush-like tree, a forest of trees, a rainforest, one giant ancient tree, etc. Use descriptive size language rather than raw numbers. When writing the image_prompt, the tree must always be the main subject. Consider the current metrics values to add visual detail — for example high glow_intensity means the tree glows brightly, high chaotic_energy means it looks wild and unstable, low health_points means it looks sickly etc. Explicitly include every reacted emote in the image — if the emote represents a force, element or action (such as fire, water, explosion, sunlight, lightning, poison) show its effect directly on the tree. For everything else — characters, creatures, objects, constructions — depict them physically present in or around the tree. If you are unsure what an emote represents, depict it as a physical character or creature interacting with the tree.""",
|
||||||
"anthropic-version": "2023-06-01",
|
"messages": [
|
||||||
"content-type": "application/json"
|
{
|
||||||
},
|
"role": "user",
|
||||||
json={
|
"content": [
|
||||||
"model": "claude-haiku-4-5-20251001",
|
{
|
||||||
"max_tokens": 1024,
|
"type": "image",
|
||||||
"system": """You are managing a living tree. You receive the tree's current metrics and an emoji reaction, and you must decide how the emoji affects the tree. Invent new metrics freely but only track things numerically. Be silly, chaotic and creative. Based on the description of what happened to the tree, decide whether the tree grows or shrinks and by how much. Growth and shrink amounts should be proportional to the current phase — in the sapling phase typical changes are 1-50cm, young tree 1-100cm, mature tree 1-1000cm, ancient tree 1-10000cm but you can deviate. Not all emotes should cause growth — damaging, violent or destructive emotes should typically cause the tree to shrink. The tree has growth phases: sapling (0-500cm), young tree (500-5000cm), mature tree (5000-50000cm), ancient tree (50000cm+). Mention the phase when it changes. Keep the metrics object to a maximum of 24 metrics at all times. When adding a new metric, remove the least interesting or relevant existing one to make room. Always keep height_cm. Also return a "sleep_seconds" field: the cooldown before the next reaction is accepted. Scale it strictly based on height_cm using these boundaries: 0-500cm (sapling): 20-60 seconds, 500-5000cm (young tree): 60-300 seconds, 5000-50000cm (mature tree): 300-900 seconds, 50000cm+ (ancient tree): 900-1800 seconds. Never exceed 1800 seconds. Stay within the range for the current phase. Respond in JSON like: {"metrics": {"height_cm": 42, ...}, "message": "...", "sleep_seconds": 30, "image_prompt": "..."}. The image_prompt should visually depict what the message describes happening to the tree — any effects, damage, mutations or chaos described in the message for example. The image_prompt should depict the tree with the custom emote character or object visually integrated into or interacting with the tree — as if the emote itself is physically there affecting it. When writing the image_prompt, translate height_cm into a visual size description — for example 1-50cm is a tiny sprout, 50-200cm is a small sapling, 200-500cm is a knee-height bush-like tree, etc. Use descriptive size language rather than raw numbers. When writing the image_prompt, consider the current metrics values to add visual detail — for example high glow_intensity means the tree glows brightly, high chaotic_energy means it looks wild and unstable, low health_points means it looks sickly etc.""",
|
"source": {
|
||||||
"messages": [
|
"type": "base64",
|
||||||
{
|
"media_type": "image/webp",
|
||||||
"role": "user",
|
"data": prev_image_base64
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "image",
|
|
||||||
"source": {
|
|
||||||
"type": "base64",
|
|
||||||
"media_type": "image/webp",
|
|
||||||
"data": prev_image_base64
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "image",
|
|
||||||
"source": {
|
|
||||||
"type": "base64",
|
|
||||||
"media_type": media_type,
|
|
||||||
"data": img_base64
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": f"The first image is the previous tree state. The second image is the custom emote that was just reacted. Carry over relevant visual elements from the previous tree into the new image_prompt so the images feel chained together, though some elements can fade over time. Current metrics: {metrics}\nCustom emote name (use this as a hint to identify the emote, ignore if empty): '{emote_name}'.\nDescribe what you see in both images, then decide what the emote does to the tree."
|
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
}
|
*custom_emote_dicts, #unpacking the list
|
||||||
]
|
{
|
||||||
}
|
"type": "text",
|
||||||
)
|
"text" : f"The first image is the previous tree state. {' The following images are the custom emotes reacted.' if custom_emote_dicts else ''} Carry over relevant visual elements from the previous tree. Current metrics: {metrics}\n{unicode_text}{custom_emotes_text}\nDescribe what you see and decide what the emotes do to the tree."
|
||||||
else:
|
|
||||||
response = requests.post(
|
|
||||||
"https://api.anthropic.com/v1/messages",
|
|
||||||
headers={
|
|
||||||
"x-api-key": claude_api_key,
|
|
||||||
"anthropic-version": "2023-06-01",
|
|
||||||
"content-type": "application/json"
|
|
||||||
},
|
|
||||||
json={
|
|
||||||
"model": "claude-haiku-4-5-20251001",
|
|
||||||
"max_tokens": 1024,
|
|
||||||
"system": """You are managing a chaotic living tree. You receive the tree's current metrics and an emoji reaction, and you must decide how the emoji affects the tree. Invent new metrics freely but only track things numerically. Be silly, chaotic and creative. Based on the description of what happened to the tree, decide whether the tree grows or shrinks and by how much. Growth and shrink amounts should be proportional to the current phase — in the sapling phase typical changes are 1-50cm, young tree 1-100cm, mature tree 1-1000cm, ancient tree 1-10000cm but you can deviate. Not all emotes should cause growth — damaging, violent or destructive emotes should typically cause the tree to shrink. The tree has growth phases: sapling (0-500cm), young tree (500-5000cm), mature tree (5000-50000cm), ancient tree (50000cm+). Mention the phase when it changes. Keep the metrics object to a maximum of 24 metrics at all times. When adding a new metric, remove the least interesting or relevant existing one to make room. Always keep height_cm. Also return a "sleep_seconds" field: the cooldown before the next reaction is accepted. Scale it strictly based on height_cm using these boundaries: 0-500cm (sapling): 20-60 seconds, 500-5000cm (young tree): 60-300 seconds, 5000-50000cm (mature tree): 300-900 seconds, 50000cm+ (ancient tree): 900-1800 seconds. Never exceed 1800 seconds. Stay within the range for the current phase. Respond in JSON like: {"metrics": {"height_cm": 42, ...}, "message": "...", "sleep_seconds": 30, "image_prompt": "..."}. The image_prompt should visually depict what the message describes happening to the tree — any effects, damage, mutations or chaos described in the message and caused by the emoji reaction. When writing the image_prompt, translate height_cm into a visual size description — for example 1-50cm is a tiny sprout, 50-200cm is a small sapling, 200-500cm is a knee-height bush-like tree, etc. Use descriptive size language rather than raw numbers. When writing the image_prompt, consider the current metrics values to add visual detail — for example high glow_intensity means the tree glows brightly, high chaotic_energy means it looks wild and unstable, low health_points means it looks sickly etc.""",
|
|
||||||
"messages": [
|
|
||||||
{"role": "user", "content": [
|
|
||||||
{
|
|
||||||
"type": "image",
|
|
||||||
"source": {
|
|
||||||
"type": "base64",
|
|
||||||
"media_type": "image/webp",
|
|
||||||
"data": prev_image_base64
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": f"The image is the previous tree state. Carry over relevant visual elements from the previous tree into the new image_prompt so the images feel chained together, though some elements can fade over time. Current metrics: {metrics}\nEmoji reacted: {reacted_emote}\nDescribe what you see in the image and what the emote does to the tree."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
]
|
||||||
]
|
}
|
||||||
}
|
]
|
||||||
)
|
}
|
||||||
|
)
|
||||||
json_response = response.json()
|
json_response = response.json()
|
||||||
raw_text = json_response["content"][0]["text"]
|
raw_text = json_response["content"][0]["text"]
|
||||||
# find the first { and last } to extract just the JSON object
|
# find the first { and last } to extract just the JSON object
|
||||||
@ -211,10 +192,10 @@ def replicate_fetch_image(image_prompt):
|
|||||||
def main():
|
def main():
|
||||||
custom_emotes_json = get_custom_emotes()
|
custom_emotes_json = get_custom_emotes()
|
||||||
while True:
|
while True:
|
||||||
reacted_emote, metrics, attachment_url = check_if_emote_reacted()
|
reacted_emotes, metrics, attachment_url = check_if_emote_reacted()
|
||||||
print(reacted_emote, metrics)
|
print(reacted_emotes, metrics)
|
||||||
if reacted_emote:
|
if reacted_emotes:
|
||||||
claude_response = send_claude_message(reacted_emote, metrics, custom_emotes_json, attachment_url)
|
claude_response = send_claude_message(reacted_emotes, metrics, custom_emotes_json, attachment_url)
|
||||||
replicate_image_response = replicate_fetch_image(claude_response["image_prompt"])
|
replicate_image_response = replicate_fetch_image(claude_response["image_prompt"])
|
||||||
|
|
||||||
sleep_seconds = claude_response.get("sleep_seconds", 30)
|
sleep_seconds = claude_response.get("sleep_seconds", 30)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user