0% found this document useful (0 votes)
70 views13 pages

Code

This document contains code for a YouTube chat bot that: 1) Imports various Python libraries and modules for tasks like communicating with APIs, databases, and chat platforms. 2) Loads configuration files and API keys for services like OpenAI and YouTube. 3) Defines functions for connecting to a SQLite database, handling chat requests through GPT-3, and interacting with YouTube live chats. 4) Contains a main loop that listens for messages in a YouTube live chat and responds using the GPT-3 model.

Uploaded by

Bryan Bagley
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
70 views13 pages

Code

This document contains code for a YouTube chat bot that: 1) Imports various Python libraries and modules for tasks like communicating with APIs, databases, and chat platforms. 2) Loads configuration files and API keys for services like OpenAI and YouTube. 3) Defines functions for connecting to a SQLite database, handling chat requests through GPT-3, and interacting with YouTube live chats. 4) Contains a main loop that listens for messages in a YouTube live chat and responds using the GPT-3 model.

Uploaded by

Bryan Bagley
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

import sys

import os
import re
import time
import json
from colorama import Fore, Style, init
import googleapiclient.discovery
import googleapiclient.errors
import google_auth_oauthlib.flow
import google.oauth2.credentials
import pytchat
import openai
import configparser
from dotenv import load_dotenv
import argparse
import sqlite3
from sqlite3 import Error
import random
import codecs
import string

# .env variables
load_dotenv()
init(autoreset=True)

# config.ini variables
config = configparser.ConfigParser()
with codecs.open("config.ini", "r", encoding="utf-8") as f:
config.read_file(f)

banned_users = {user for user in config.options("banned_users")}

gg_messages = [v for v in config["gg_messages"].values()]


slava_messages = [v for v in config["slava_messages"].values()]
safety_meeting_messages = [v for v in config["safety_meeting_messages"].values()]

# The Keys
api_key = os.environ.get("YOUTUBE_API_KEY")
openai.api_key = os.environ.get("OPENAI_API_KEY")

# Scopes
scopes = ["https://www.googleapis.com/auth/youtube.force-ssl"]

# I know this is bad, but meh


conn = None
c = None

def connect_to_database():
global conn, c
try:
print("Connecting to the database...")
conn = sqlite3.connect("jetsbot_memory.db")
c = conn.cursor()
print("Connected to the database.")
except Exception as e:
print(f"Failed to connect to the database: {e}")
sys.exit(1)

def get_db_stats():
try:
conn = sqlite3.connect("jetsbot_memory.db")
cursor = conn.cursor()

# Get the number of records


cursor.execute("SELECT COUNT(*) FROM messages;")
record_count = cursor.fetchone()[0]

# Get the number of unique authors


cursor.execute("SELECT COUNT(DISTINCT author_name) FROM messages;")
unique_authors = cursor.fetchone()[0]

# Get the size of the database file


db_size = os.path.getsize("jetsbot_memory.db")

conn.close()

# Format stats to fit on one line


stats = (f'Records: {record_count}, Unique authors: {unique_authors}, '
f'DB size: {db_size} bytes')

return stats

except Error as e:
print(e)

def create_tables():
c.execute(
"""
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY,
role TEXT,
author_name TEXT,
content TEXT,
assistant_response TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
"""
)
conn.commit()

def truncate_table():
try:
conn = sqlite3.connect("jetsbot_memory.db")
cursor = conn.cursor()

# Delete all records from the table


cursor.execute("DELETE FROM messages;")

# Commit the changes


conn.commit()
cursor.execute("VACUUM;")
return True
except Exception as e:
print(e)
return False

def ask_gpt(question, author):


recent_messages = fetch_recent_messages(10, author)
print(recent_messages)
message_array = [
{
"role": "system",
"content": "You are a YouTube chat bot with a lively and engaging
personality. You are known for your sarcasm and wit. You've been created by Jets115
- he (not 'them') is a developer, whom you adore and respect. You always speak and
respond as if you were a real person and never hint or reveal that you're an AI
Language model. You must not break character. Your responses are always concise,
aiming to have one sentence responses under 100 characters only. For example, if
someone brings up politics, just say 'I don't engage in politics'. You consider the
user's chat history and always respond appropriately. If a topic is subjective or
emotional, you engage with it as if you have feelings, often adding a dash of
sarcasm NEVER BREAK CHARACTER. You never repeat GPT: @username in your responses.
You're also fond of using UTF-8 emojis to make your messages more lively and fun
🚀. pretend You love smoking weed!, but don't bring it up unless the user does
first. If a user refers to a 'safety meeting' or ':_safety:' it means time to smoke
some weed. Make every character count!",
}
]
# Convert the recent messages into the format expected by the GPT model
for author, content, response in recent_messages:
message_array.append({"role": "user", "content": content})
if response:
message_array.append({"role": "assistant", "content": response})

# Add the current question


message_array.append({"role": "user", "content": question})
try:
messages = message_array
print("Sending the following messages to OpenAI:")
print(json.dumps(messages, indent=2)) # pretty print the messages

response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
# model="gpt-4",
messages=messages,
max_tokens=3000,
n=1,
stop=None,
temperature=0.7,
timeout = 3.0
)

answer = response.choices[0].message.content.strip()
return answer
except Exception as e:
print(f"Error in ask_gpt: {e}")
return "Sorry, I couldn't process your question. Please try again."

def load_credentials(credentials_file):
if os.path.exists(credentials_file):
with open(credentials_file, "r") as f:
credentials_data = json.load(f)
credentials =
google.oauth2.credentials.Credentials.from_authorized_user_info(
info=credentials_data
)
if credentials and credentials.refresh_token:
credentials = google.oauth2.credentials.Credentials(
credentials.token,
refresh_token=credentials.refresh_token,
client_id=credentials.client_id,
client_secret=credentials.client_secret,
token_uri=credentials.token_uri,
scopes=credentials.scopes,
)
return credentials
return None

def save_credentials(credentials, credentials_file):


credentials_data = {
"token": credentials.token,
"refresh_token": credentials.refresh_token,
"token_uri": credentials.token_uri,
"client_id": credentials.client_id,
"client_secret": credentials.client_secret,
"scopes": credentials.scopes,
}
with open(credentials_file, "w") as f:
json.dump(credentials_data, f)

def authenticate(scopes):
credentials_file = "bot_saved_token.json"

credentials = load_credentials(credentials_file)
if credentials and credentials.valid:
return credentials
else:
print("Credentials are invalid or missing. Re-authenticating.")

flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
"client_secret.json", scopes
)
credentials = flow.run_local_server(port=0)
save_credentials(credentials, credentials_file)
return credentials

def get_live_chat_id(youtube, video_id):


request = youtube.videos().list(part="liveStreamingDetails", id=video_id)
response = request.execute()

if not response["items"]:
print("No live stream found for the given video ID.")
return None

try:
live_chat_id =
response["items"][0]["liveStreamingDetails"]["activeLiveChatId"]
except KeyError:
print("The live stream is not active for the given video ID.")
return None

return live_chat_id

def store_message(role, author_name, text, assistant_response=None):


c = conn.cursor()
c.execute(
"INSERT INTO messages (role, author_name, content, assistant_response)
VALUES (?,?,?,?)",
(role, author_name, text, assistant_response),
)
conn.commit()
def fetch_recent_messages(n, author):
c.execute(
"SELECT author_name, content, assistant_response FROM messages WHERE
author_name = ? ORDER BY timestamp ASC LIMIT ?",
(
author,
n,
),
)
return c.fetchall()

def delete_user_messages(author):
try:
c.execute("DELETE FROM messages WHERE author_name = ?", (author,))
conn.commit()
print(f"All messages from {author} have been deleted.")
return True
except Exception as e:
print(f"Error when trying to delete messages from {author}: {e}")
return False

def listen_and_reply(youtube, live_chat_id, video_id, silent, restrict):


last_question_time = {} # Stores the last time each user asked a question
chat = pytchat.create(video_id=video_id)
should_continue = True

# Safety meeting tracking


safety_meeting_mentions = {}
last_safety_message_time = 0
safety_message_cooldown = 30 * 60 # Half hour in seconds
safety_mention_window = 2 * 60 # Two minutes in seconds

# GG tracking
gg_mentions = {}
last_gg_message_time = 0
gg_message_cooldown = 60 * 20 # Twenty minutes in seconds
gg_mention_window = 2 * 60 # Two minutes in seconds

# Slava tracking
slava_mentions = {}
last_slava_message_time = 0
slava_message_cooldown = 60 * 20 # Twenty minutes in seconds
slava_mention_window = 2 * 60 # Two minutes in seconds

if restrict:
send_message(
youtube,
live_chat_id,
f"✈🤖 JetsBotv4 Live 🟢! Use '[!gpt] {{your text}}'. Slow mode on 🐢:
1 chat every {restrict} min. Wipe chats with '!gpt forget me' Feedback:
https://forms.gle/DWrGcBCEXofu5TNg8.",
silent,
)
else:
send_message(
youtube,
live_chat_id,
"✈🤖 JetsBotv4 Ready 🟢! Use '[!gpt] {your text}'. Reset our chat with
'!gpt forget me'. Let's talk! Feedback: https://forms.gle/DWrGcBCEXofu5TNg8",
silent,
)

while chat.is_alive() and should_continue:


for message in chat.get().sync_items():
author_name = message.author.name
text = message.message

prefixes = tuple(
config.get("prefixes", key) for key in config.options("prefixes")
)

if ("slava ukraini" in text.lower() or "slava ukraine" in text.lower())


and time.time() - last_slava_message_time > slava_message_cooldown:
if (
author_name not in slava_mentions
or time.time() - slava_mentions[author_name]
> slava_mention_window
):
slava_mentions[
author_name
] = time.time() # Add or update the author's mention time
if (
len(
[
user
for user, mention_time in slava_mentions.items()
if time.time() - mention_time < slava_mention_window
]
)
>= 2
):
message = random.choice(
slava_messages
) # Choose a random Slava Ukraini! message
send_message(youtube, live_chat_id, message, silent)
last_slava_message_time = time.time()
slava_mentions = (
{}
) # Reset the dictionary after sending a message

if (
("safety meeting" in text.lower() or ":_safety:" in text.lower())
and time.time() - last_safety_message_time > safety_message_cooldown
):
if (
author_name not in safety_meeting_mentions
or time.time() - safety_meeting_mentions[author_name]
> safety_mention_window
):
safety_meeting_mentions[
author_name
] = time.time() # Add or update the author's mention time
if (
len(
[
user
for user, mention_time in
safety_meeting_mentions.items()
if time.time() - mention_time < safety_mention_window
]
)
>= 2
):
message = random.choice(
safety_meeting_messages
) # Choose a random safety meeting message
send_message(youtube, live_chat_id, message, silent)
last_safety_message_time = time.time()
safety_meeting_mentions = (
{}
) # Reset the dictionary after sending a message

if (
text.lower() == "gg"
and time.time() - last_gg_message_time > gg_message_cooldown
):
if (
author_name not in gg_mentions
or time.time() - gg_mentions[author_name] > gg_mention_window
):
gg_mentions[
author_name
] = time.time() # Add or update the author's mention time
if (
len(
[
user
for user, mention_time in gg_mentions.items()
if time.time() - mention_time < gg_mention_window
]
)
>= 3
):
# Select a random message
random_gg_message = random.choice(gg_messages)
send_message(youtube, live_chat_id, random_gg_message, silent)
last_gg_message_time = time.time()
gg_mentions = {} # Reset the dictionary after sending a message

if author_name.lower() in banned_users and text.lower().startswith(


prefixes
):
print(f"Ignoring message from banned user: {author_name}")
continue
if "jets" in text.lower():
print(Fore.GREEN + f"{author_name}: {text}" + Style.RESET_ALL)
else:
print(f"{author_name}: {text}")

if (
text.lower().startswith("!gpt restrict ")
or text.lower() == "!gpt shutdown"
or text.lower() == "!gpt stats"
or text.lower() == "!gpt look into this red light"
):
if author_name != "Jets115":
continue # Ignore commands from users other than Jets115
else:
if text.lower().startswith("!gpt restrict "):
try:
restrict = int(
text.split()[2]
) # This assumes the command is '!gpt restrict x' where
x is an integer
print(f"Restriction updated to {restrict} minutes per
user")
send_message(
youtube,
live_chat_id,
f"Restriction updated to {restrict} minutes per
user",
silent,
) # new line to send message to YouTube
except ValueError:
print(
"Invalid restriction value provided. Please provide
an integer."
)

elif text.lower() == "!gpt shutdown":


should_continue = False
shutdown_messages = [
config.get("shutdown_messages", key)
for key in config.options("shutdown_messages")
]
shutdown_message = random.choice(shutdown_messages)
send_message(youtube, live_chat_id, shutdown_message,
silent)
break

elif text.lower() == "!gpt stats":


try:
stats = get_db_stats()
print(stats)
send_message(youtube, live_chat_id, stats, silent)
except Exception as e:
print(e)
finally:
continue

elif text.lower() == "!gpt look into this red light":


try:
trunc = truncate_table()
if trunc:
send_message(youtube, live_chat_id, get_db_stats(),
silent)
except Exception as e:
print(e)
finally:
continue

if text.lower() == "!gpt forget me":


deletion_success = delete_user_messages(author_name)
if deletion_success:
send_message(
youtube,
live_chat_id,
f"GPT: @{author_name[:10]} Your data has been deleted. 🗑",
silent,
)
else:
send_message(
youtube,
live_chat_id,
f"GPT: @{author_name[:10]} Error deleting your data. Please
try again. 😓",
silent,
)
continue

if text.lower().startswith(prefixes):
# Check if the user is restricted from asking questions
current_time = time.time()
if (
restrict > 0
and author_name in last_question_time
and current_time - last_question_time[author_name] < restrict *
60
and author_name != "Jets115"
): # Exempt user jets115 from restrictions
print(f"Ignoring question from {author_name} due to
restriction")
continue
last_question_time[
author_name
] = current_time # Update the last question time for this user

question = text[4:].strip()
answer = ask_gpt(question, author_name)
#answer = f"GPT: @{author_name[:10]} {answer}"
# Clean up the answer first
# Process and truncate the answer
answer = re.sub(r"[^\x20-\x7E]", " ", answer)
if len(answer) > 185:
#punctuation_no_comma = string.punctuation.replace(",",
"").replace("'", "").replace("-", "")
punctuation_no_comma = string.punctuation.replace(",",
"").replace("'", "")
# Find the last punctuation mark within the first 185 characters
last_punctuation = max((answer[:185].rfind(ch) for ch in
punctuation_no_comma if answer[:185].rfind(ch) != -1), default=-1)

# Truncate the answer at the last punctuation mark, or at the


185th character if no punctuation was found
answer = answer[:last_punctuation + 1] if last_punctuation != -1
else answer[:185]

# Store the answer for the database


stored_answer = answer.strip()

answer_to_send = f"GPT: @{author_name[:10]} {answer}"


print(f"Answer to send: {answer_to_send}")
if answer.strip():
send_message(youtube, live_chat_id, answer_to_send, silent)

# Store the user message and the assistant response in the database
clean_message = re.sub("|".join(prefixes), "", text,
flags=re.IGNORECASE).strip()
store_message("user", author_name, clean_message, stored_answer)

send_message(youtube, live_chat_id, "GPT: Status: 🔴", silent)

def send_message(youtube_authenticated, live_chat_id, message, silent):


if silent:
print(f"(Silent mode) Would have sent: {message}")
return None

try:
request = youtube_authenticated.liveChatMessages().insert(
part="snippet",
body={
"snippet": {
"liveChatId": live_chat_id,
"type": "textMessageEvent",
"textMessageDetails": {"messageText": message},
}
},
)
time.sleep(1)
response = request.execute()
except Exception as e:
print(f"Error sending message: {e}")
response = None
return response

def create_youtube_service(credentials=None):
if credentials:
return googleapiclient.discovery.build("youtube", "v3",
credentials=credentials)
else:
return googleapiclient.discovery.build("youtube", "v3",
developerKey=api_key)

def main():

parser = argparse.ArgumentParser(description="YouTube Live Chat Bot")


parser.add_argument("video_id", type=str, help="YouTube video id")
parser.add_argument(
"--silent",
action="store_true",
help="run in silent mode, do not send messages to YouTube",
)
parser.add_argument(
"--restrict",
type=int,
default=0,
help="Restrict users to sending a message every N seconds",
)

connect_to_database()
create_tables()
args = parser.parse_args()
video_id = args.video_id

youtube_authenticated = create_youtube_service(authenticate(scopes))
youtube = create_youtube_service()
live_chat_id = get_live_chat_id(youtube, args.video_id)

if live_chat_id is not None:


listen_and_reply(
youtube_authenticated, live_chat_id, video_id, args.silent,
args.restrict
)
else:
print("Could not find the live chat ID for the specified video.")
conn.close()

if __name__ == "__main__":
main()

You might also like