Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v6.3.1 #85

Merged
merged 2 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
polar: # Replace with a single Polar username
buy_me_a_coffee: yukiarimo
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
152 changes: 152 additions & 0 deletions about.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<!DOCTYPE html>
<html data-bs-theme="dark" lang="en">

<head data-bs-theme="dark">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=yes, user-scalable=no" ,
viewport-fit=cover">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title>Yuna AI</title>
<meta name="theme-color" content="#212529">
<link rel="canonical" href="http://www.yuna-ai.live">
<meta property="og:url" content="/">
<meta property="og:description" content="Your virtual AI girlfriend">
<meta name="twitter:image" content="/static/img/yuna-ai.png">
<meta name="twitter:title" content="Yuna AI">
<meta property="og:image" content="/static/img/yuna-ai.png">
<meta name="description" content="Your virtual AI girlfriend">
<meta property="og:type" content="website">
<meta property="og:title" content="Yuna AI">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:description" content="Your virtual AI girlfriend">
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "WebSite",
"name": "Yuna AI",
"url": "http://www.yuna-ai.live"
}
</script>

<link rel="icon" type="image/jpeg" sizes="60x60" href="/static/img/yuna-ai.png">
<link rel="manifest" href="/static/manifest.json">
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/awesome.css">
<link rel="stylesheet" href="/static/css/about.css">
<style>
body {
margin: 0;
}

canvas {
display: block;
}
</style>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/static/sw.js').then(
function (registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration
.scope);
},
function (err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
</script>

<script defer>
function actionOpen() {
var modalElement = document.getElementById('cuteModal');
var modal = new bootstrap.Modal(modalElement);

modal.show();
};

function actionClose() {
var modalElement = document.getElementById('cuteModal');
var modal = new bootstrap.Modal(modalElement);
modal.hide();
};
</script>
</head>

<body id="page-top">
<div id="wrapper">
<!-- Waifu Card Container -->
<div id="waifu-card-container">
<div class="waifu-card">
<!-- Floating Info Blocks -->
<div class="info-blocks">
<div class="info-block">
<p>Name:</p>
<p>Yuna</p>
</div>
<div class="info-block">
<p>Age:</p>
<p>15</p>
</div>
<div class="info-block">
<p>Gender:</p>
<p>Female</p>
</div>
<div class="info-block">
<p>Race:</p>
<p>Japanese</p>
</div>
</div>

<!-- Detail Panel -->
<div class="detail-panel" data-toggle="modal" data-target="#cuteModal" onclick="actionOpen()">
<div class="detail-text">
<!-- Additional details can be added here -->
</div>
</div>

<!-- Waifu Image -->
<img src="/static/img/yuna-girl.webp" alt="Yuna AI" />

<!-- Waifu Info -->
<div class="waifu-info">
<div class="waifu-description">♡ Your Private Companion ♡</div>
</div>
</div>
</div>
</div>

<!-- The Modal -->
<div class="modal fade" id="cuteModal">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title">Yuna's Specials</h4>
<button type="button" class="close" data-dismiss="modal" onclick="actionClose()">&times;</button>
</div>

<!-- Modal body -->
<div class="modal-body">
Choose your action:
</div>

<!-- Modal footer -->
<div class="modal-footer">
<a href="watch-intro-url" class="btn btn-primary">Watch Intro</a>
<a href="watch-teaser-url" class="btn btn-secondary">Watch Teaser</a>
<a href="https://github.com" class="btn btn-dark">Visit GitHub</a>
</div>

</div>
</div>
</div>

<script src="/static/js/bootstrap/bootstrap.bundle.min.js"></script>
<script src="/static/js/bootstrap/script.min.js"></script>
</body>
</body>

</html>
5 changes: 3 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@
<div class="collapse navbar-collapse" id="navcol-1">
<ul class="navbar-nav mx-auto">
<li class="nav-item"><a class="nav-link active btn btn-primary shadow" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link" href="/services.html">Services and Pricing</a></li>
<li class="nav-item"><a class="nav-link" href="/services.html">Services</a></li>
<li class="nav-item"><a class="nav-link" href="/about.html">WaifuCard</a></li>
</ul><a class="btn btn-primary shadow" role="button" href="/yuna.html">Login</a>
</div>
</div>
Expand All @@ -104,7 +105,7 @@ <h1 class="fw-bold">The best solution for you and your customers</h1>
<div class="col-12 col-lg-10 mx-auto">
<div class="position-relative" style="display: flex;flex-wrap: wrap;justify-content: flex-end;">
<div style="position: relative;flex: 0 0 45%;transform: translate3d(-15%, 35%, 0);"><img
class="/static/img-fluid rounded shadow w-100 fit-cover" data-bss-parallax=""
class="/static/img-fluid rounded shadow fit-cover" data-bss-parallax=""
data-bss-parallax-speed="0.8" src="/static/img/products/prompts.webp"
alt="Prompt Templates"></div>
<div style="position: relative;flex: 0 0 45%;transform: translate3d(-5%, 20%, 0);"><img
Expand Down
41 changes: 39 additions & 2 deletions index.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import shutil
from flask import Flask, get_flashed_messages, request, jsonify, send_from_directory, redirect, url_for, flash
from flask_login import LoginManager, UserMixin, login_required, logout_user, login_user, current_user
import requests
from lib.generate import ChatGenerator, ChatHistoryManager
from lib.router import handle_history_request, handle_image_request, handle_message_request, handle_audio_request, services
from lib.router import handle_history_request, handle_image_request, handle_message_request, handle_audio_request, services, about
from flask_cors import CORS
import json
import os
Expand All @@ -19,6 +20,7 @@
class YunaServer:
def __init__(self):
self.app = Flask(__name__, static_folder='static')
self.app.config['WTF_CSRF_ENABLED'] = False
self.app.secret_key = 'Yuna_AI_Secret_Key'
self.app.config['COMPRESS_ALGORITHM'] = ['br', 'gzip']
self.app.config['COMPRESS_LEVEL'] = 6
Expand All @@ -34,9 +36,42 @@ def __init__(self):
self.chat_history_manager = ChatHistoryManager(self.config)
self.app.errorhandler(404)(self.page_not_found)

@self.app.after_request
def add_cors_headers(response):
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response

@staticmethod
def page_not_found(self):
return f'This page does not exist.', 404

def search(self):
search_query = request.json['query']
url = 'https://www.google.com/search?q=' + search_query

# Send a GET request to the URL with additional headers
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Cache-Control': 'max-age=0',
}
response = requests.get(url, headers=headers)

# Get the HTML content from the response
html_content = response.text

# Return the HTML content as plain text
return html_content, 200, {'Content-Type': 'text/plain'}

# User model
class User(UserMixin):
Expand Down Expand Up @@ -76,6 +111,8 @@ def configure_routes(self):
self.app.route('/<path:filename>')(self.custom_static)
self.app.route('/yuna')(self.yuna_server)
self.app.route('/yuna.html')(self.yuna_server)
self.app.route('/services.html', methods=['GET'], endpoint='services')(lambda: services(self))
self.app.route('/about.html', methods=['GET'], endpoint='about')(lambda: about(self))
self.app.route('/apple-touch-icon.png')(self.image_pwa)
self.app.route('/flash-messages')(self.flash_messages)
self.app.route('/main', methods=['GET', 'POST'])(self.main)
Expand All @@ -84,7 +121,7 @@ def configure_routes(self):
self.app.route('/image', methods=['POST'], endpoint='image')(lambda: handle_image_request(self.chat_history_manager, self))
self.app.route('/audio', methods=['GET', 'POST'], endpoint='audio')(lambda: handle_audio_request(self))
self.app.route('/logout', methods=['GET'])(self.logout)
self.app.route('/services.html', methods=['GET'], endpoint='services')(lambda: services(self))
self.app.route('/search', methods=['POST'])(self.search)

def custom_static(self, filename):
if not filename.startswith('static/') and not filename.startswith('/favicon.ico') and not filename.startswith('/manifest.json'):
Expand Down
19 changes: 19 additions & 0 deletions lib/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,25 @@ def list_history_files(self, username):
def generate_speech(self, response):
os.system(f'say -o static/audio/audio.aiff "{response}"')

def delete_message(self, username, chat_id, target_message):
chat_history = self.load_chat_history(username, chat_id)

target_index = None
for i, message in enumerate(chat_history):
if message['message'].strip() == target_message.strip():
target_index = i
break

# If the target message is found, delete it and the next message
if target_index is not None:
# Delete the target message
del chat_history[target_index]
# Check if there is a next message and delete it
if target_index < len(chat_history):
del chat_history[target_index]

self.save_chat_history(chat_history, username, chat_id)

def get_encryption_key(self):
if 'encryption_key' not in self.config['security'] or not self.config['security']['encryption_key']:
new_key = Fernet.generate_key()
Expand Down
16 changes: 13 additions & 3 deletions lib/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,14 @@ def handle_history_request(chat_history_manager):
new_chat_id_name = data.get('name')
chat_history_manager.rename_chat_history_file(list({current_user.get_id()})[0], chat_id, new_chat_id_name)
return jsonify({'response': 'History renamed successfully'})
elif task == 'delete_message':
text = data.get('text')
chat_history_manager.delete_message(list({current_user.get_id()})[0], chat_id, text)
return jsonify({'response': 'Message deleted successfully'})
else:
return jsonify({'error': 'Invalid task parameter'}), 400


@login_required
def handle_message_request(chat_generator, chat_history_manager, chat_id=None, speech=None, text=None, template=None):
data = request.get_json()
chat_id = data.get('chat')
Expand All @@ -47,6 +52,7 @@ def handle_message_request(chat_generator, chat_history_manager, chat_id=None, s
response = chat_generator.generate(chat_id, speech, text, template, chat_history_manager)
return jsonify({'response': response})

@login_required
def handle_audio_request(self):
if 'audio' not in request.files:
return jsonify({'error': 'No audio file'}), 400
Expand All @@ -56,7 +62,8 @@ def handle_audio_request(self):

result = model.transcribe('static/audio/audio.wav')
return jsonify({'text': result['text']})


@login_required
def handle_image_request(chat_history_manager, self):
data = request.get_json()
chat_id = data.get('chat')
Expand Down Expand Up @@ -101,4 +108,7 @@ def handle_image_request(chat_history_manager, self):
return jsonify({'error': 'Invalid task parameter'}), 400

def services(self):
return send_from_directory('.', 'services.html')
return send_from_directory('.', 'services.html')

def about(self):
return send_from_directory('.', 'about.html')
Loading
Loading