inventwithdean
commited on
Commit
·
c48e85d
1
Parent(s):
49d8086
add realtime yt chat support via pytchat
Browse files- __pycache__/audience.cpython-310.pyc +0 -0
- __pycache__/guard.cpython-310.pyc +0 -0
- __pycache__/host.cpython-310.pyc +0 -0
- __pycache__/image_api.cpython-310.pyc +0 -0
- __pycache__/prompts.cpython-310.pyc +0 -0
- __pycache__/randomname.cpython-310.pyc +0 -0
- __pycache__/timing.cpython-310.pyc +0 -0
- __pycache__/tv_crew.cpython-310.pyc +0 -0
- __pycache__/validity.cpython-310.pyc +0 -0
- __pycache__/yt_chat.cpython-310.pyc +0 -0
- yt_chat.py +65 -101
__pycache__/audience.cpython-310.pyc
ADDED
|
Binary file (2.05 kB). View file
|
|
|
__pycache__/guard.cpython-310.pyc
ADDED
|
Binary file (1.13 kB). View file
|
|
|
__pycache__/host.cpython-310.pyc
ADDED
|
Binary file (2.48 kB). View file
|
|
|
__pycache__/image_api.cpython-310.pyc
ADDED
|
Binary file (834 Bytes). View file
|
|
|
__pycache__/prompts.cpython-310.pyc
ADDED
|
Binary file (1.35 kB). View file
|
|
|
__pycache__/randomname.cpython-310.pyc
ADDED
|
Binary file (625 Bytes). View file
|
|
|
__pycache__/timing.cpython-310.pyc
ADDED
|
Binary file (1.51 kB). View file
|
|
|
__pycache__/tv_crew.cpython-310.pyc
ADDED
|
Binary file (2 kB). View file
|
|
|
__pycache__/validity.cpython-310.pyc
ADDED
|
Binary file (380 Bytes). View file
|
|
|
__pycache__/yt_chat.cpython-310.pyc
ADDED
|
Binary file (3.41 kB). View file
|
|
|
yt_chat.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
| 1 |
import os
|
|
|
|
| 2 |
|
| 3 |
# from dotenv import load_dotenv
|
| 4 |
from guard import Guardian
|
| 5 |
from tv_crew import TVCrew
|
| 6 |
import requests
|
| 7 |
import time
|
| 8 |
-
import googleapiclient.discovery
|
| 9 |
|
| 10 |
# load_dotenv()
|
| 11 |
unreal_orchestrator_url = os.getenv("ORCHESTRATOR_URL")
|
|
@@ -13,7 +13,8 @@ api_key_unreal = os.getenv("API_KEY_UNREAL")
|
|
| 13 |
|
| 14 |
API_KEY = os.getenv("YOUTUBE_API_KEY")
|
| 15 |
VIDEO_ID = "xBVJIJQg1FY"
|
| 16 |
-
POLL_INTERVAL =
|
|
|
|
| 17 |
|
| 18 |
|
| 19 |
class StreamChatHost:
|
|
@@ -31,116 +32,79 @@ Keep your responses short, like two or three sentences, and be witty."""
|
|
| 31 |
|
| 32 |
self.messages = [self.system_message]
|
| 33 |
self.headers = {"Content-Type": "application/json", "x-api-key": api_key_unreal}
|
| 34 |
-
self.youtube = googleapiclient.discovery.build(
|
| 35 |
-
"youtube", "v3", developerKey=API_KEY
|
| 36 |
-
)
|
| 37 |
-
self.chat_id = self.get_live_chat_id(self.youtube, VIDEO_ID)
|
| 38 |
-
self.next_page_token = None
|
| 39 |
|
| 40 |
def chat_interaction_loop(self):
|
| 41 |
"""Checks if the Host is in lobby, then pulls chat messages from the Live Stream. The Host then replies to the message."""
|
| 42 |
-
while
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
)
|
| 47 |
-
in_lobby = request.json().get("in_lobby", False)
|
| 48 |
-
if not in_lobby:
|
| 49 |
-
continue
|
| 50 |
-
if len(self.messages) > 20:
|
| 51 |
-
self.messages = [self.system_message]
|
| 52 |
-
self.tv_crew.clear_context()
|
| 53 |
-
try:
|
| 54 |
-
request = self.youtube.liveChatMessages().list(
|
| 55 |
-
liveChatId=self.chat_id,
|
| 56 |
-
part="snippet,authorDetails",
|
| 57 |
-
maxResults=20,
|
| 58 |
-
pageToken=self.next_page_token,
|
| 59 |
)
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
for item in response.get("items", []):
|
| 63 |
-
author_name = item["authorDetails"]["displayName"]
|
| 64 |
-
message_text = item["snippet"]["displayMessage"]
|
| 65 |
-
user_messages.append(f"{author_name}: {message_text}")
|
| 66 |
-
|
| 67 |
-
first_fetch = True if self.next_page_token is None else False
|
| 68 |
-
self.next_page_token = response.get("nextPageToken")
|
| 69 |
-
if first_fetch:
|
| 70 |
-
continue
|
| 71 |
-
if len(user_messages) == 0:
|
| 72 |
continue
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
user_message = "[YouTube Chat]\n"
|
| 78 |
-
for msg in user_messages:
|
| 79 |
-
if len(msg) <= 500:
|
| 80 |
-
user_message += f"{msg}\n"
|
| 81 |
-
is_safe, _ = self.guardian.moderate_message(user_message)
|
| 82 |
|
| 83 |
-
|
| 84 |
-
|
|
|
|
|
|
|
| 85 |
|
| 86 |
-
self.tv_crew.add_context(user_message)
|
| 87 |
-
image_base64, caption = self.tv_crew.suggest_image()
|
| 88 |
-
if image_base64:
|
| 89 |
-
is_safe, _ = self.guardian.moderate_message(caption)
|
| 90 |
if not is_safe:
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
else:
|
| 94 |
-
user_message += f"[Crew Updated Television to: {caption}]\n"
|
| 95 |
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
|
|
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
response = requests.post(
|
| 119 |
-
url=f"{unreal_orchestrator_url}/
|
| 120 |
-
json=
|
| 121 |
headers=self.headers,
|
| 122 |
)
|
| 123 |
-
|
| 124 |
-
payload = {"text": host_response, "is_host": True}
|
| 125 |
-
response = requests.post(
|
| 126 |
-
url=f"{unreal_orchestrator_url}/tts",
|
| 127 |
-
json=payload,
|
| 128 |
-
headers=self.headers,
|
| 129 |
-
)
|
| 130 |
-
|
| 131 |
-
def get_live_chat_id(self, youtube, video_id):
|
| 132 |
-
try:
|
| 133 |
-
request = youtube.videos().list(part="liveStreamingDetails", id=video_id)
|
| 134 |
-
response = request.execute()
|
| 135 |
-
if not response["items"]:
|
| 136 |
-
print("No video found with the given ID.")
|
| 137 |
-
return None
|
| 138 |
-
|
| 139 |
-
details = response["items"][0]["liveStreamingDetails"]
|
| 140 |
-
if not details:
|
| 141 |
-
print("No live streaming details found for this video.")
|
| 142 |
-
return None
|
| 143 |
-
return details.get("activeLiveChatId", None)
|
| 144 |
-
except Exception as e:
|
| 145 |
-
print(f"An error occurred: {e}")
|
| 146 |
-
return None
|
|
|
|
| 1 |
import os
|
| 2 |
+
import pytchat
|
| 3 |
|
| 4 |
# from dotenv import load_dotenv
|
| 5 |
from guard import Guardian
|
| 6 |
from tv_crew import TVCrew
|
| 7 |
import requests
|
| 8 |
import time
|
|
|
|
| 9 |
|
| 10 |
# load_dotenv()
|
| 11 |
unreal_orchestrator_url = os.getenv("ORCHESTRATOR_URL")
|
|
|
|
| 13 |
|
| 14 |
API_KEY = os.getenv("YOUTUBE_API_KEY")
|
| 15 |
VIDEO_ID = "xBVJIJQg1FY"
|
| 16 |
+
POLL_INTERVAL = 3 # seconds
|
| 17 |
+
chat = pytchat.create(video_id=VIDEO_ID)
|
| 18 |
|
| 19 |
|
| 20 |
class StreamChatHost:
|
|
|
|
| 32 |
|
| 33 |
self.messages = [self.system_message]
|
| 34 |
self.headers = {"Content-Type": "application/json", "x-api-key": api_key_unreal}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
def chat_interaction_loop(self):
|
| 37 |
"""Checks if the Host is in lobby, then pulls chat messages from the Live Stream. The Host then replies to the message."""
|
| 38 |
+
while chat.is_alive():
|
| 39 |
+
for c in chat.get().sync_items():
|
| 40 |
+
request = requests.get(
|
| 41 |
+
f"{unreal_orchestrator_url}/status", headers=self.headers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
)
|
| 43 |
+
in_lobby = request.json().get("in_lobby", False)
|
| 44 |
+
if not in_lobby:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
continue
|
| 46 |
+
if len(self.messages) > 20:
|
| 47 |
+
self.messages = [self.system_message]
|
| 48 |
+
self.tv_crew.clear_context()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
+
user_message = f"{c.author.name}]- {c.message}"
|
| 51 |
+
# print(user_message)
|
| 52 |
+
# print("Checking message safety...")
|
| 53 |
+
is_safe, _ = self.guardian.moderate_message(user_message)
|
| 54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
if not is_safe:
|
| 56 |
+
# print("Message flagged by Guardian, skipping...")
|
| 57 |
+
continue
|
|
|
|
|
|
|
| 58 |
|
| 59 |
+
self.tv_crew.add_context(user_message)
|
| 60 |
+
# print("TV Crew suggesting image...")
|
| 61 |
+
image_base64, caption = self.tv_crew.suggest_image()
|
| 62 |
+
if image_base64:
|
| 63 |
+
# print("Checking image caption safety...")
|
| 64 |
+
is_safe, _ = self.guardian.moderate_message(caption)
|
| 65 |
+
if not is_safe:
|
| 66 |
+
# print("Image caption flagged by Guardian, skipping image...")
|
| 67 |
+
image_base64 = None
|
| 68 |
+
caption = None
|
| 69 |
+
else:
|
| 70 |
+
user_message += f"[Crew Updated Television to: {caption}]\n"
|
| 71 |
+
|
| 72 |
+
self.messages.append({"role": "user", "content": user_message})
|
| 73 |
+
try:
|
| 74 |
+
# print("Generating host response...")
|
| 75 |
+
response = self.client.responses.create(
|
| 76 |
+
model=self.model, input=self.messages
|
| 77 |
+
)
|
| 78 |
+
host_response = response.output_text
|
| 79 |
+
self.messages.append(
|
| 80 |
+
{"role": "assistant", "content": host_response}
|
| 81 |
+
)
|
| 82 |
+
except Exception as e:
|
| 83 |
+
# print(f"Error generating host response: {e}")
|
| 84 |
+
continue
|
| 85 |
|
| 86 |
+
self.tv_crew.add_context(f"Host: {host_response}\n")
|
| 87 |
+
# print(f"Host Response: {host_response}")
|
| 88 |
+
# Just to make sure, that we're in the lobby
|
| 89 |
+
request = requests.get(
|
| 90 |
+
f"{unreal_orchestrator_url}/status", headers=self.headers
|
| 91 |
+
)
|
| 92 |
+
in_lobby = request.json().get("in_lobby", False)
|
| 93 |
+
if not in_lobby:
|
| 94 |
+
continue
|
| 95 |
|
| 96 |
+
if image_base64:
|
| 97 |
+
# print("Sending Image Unreal Orchestrator...")
|
| 98 |
+
payload_tv = {"base64": image_base64}
|
| 99 |
+
response = requests.post(
|
| 100 |
+
url=f"{unreal_orchestrator_url}/television",
|
| 101 |
+
json=payload_tv,
|
| 102 |
+
headers=self.headers,
|
| 103 |
+
)
|
| 104 |
+
# print("Sending Host response to Unreal Orchestrator...")
|
| 105 |
+
payload = {"text": host_response, "is_host": True}
|
| 106 |
response = requests.post(
|
| 107 |
+
url=f"{unreal_orchestrator_url}/tts",
|
| 108 |
+
json=payload,
|
| 109 |
headers=self.headers,
|
| 110 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|