diff --git a/com.jeffser.Alpaca.json b/com.jeffser.Alpaca.json index 585d523..fcc367f 100644 --- a/com.jeffser.Alpaca.json +++ b/com.jeffser.Alpaca.json @@ -99,6 +99,20 @@ } ] }, + { + "name": "python3-html2text", + "buildsystem": "simple", + "build-commands": [ + "pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"html2text\" --no-build-isolation" + ], + "sources": [ + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/1a/43/e1d53588561e533212117750ee79ad0ba02a41f52a08c1df3396bd466c05/html2text-2024.2.26.tar.gz", + "sha256": "05f8e367d15aaabc96415376776cdd11afd5127a77fce6e36afc60c563ca2c32" + } + ] + }, { "name": "ollama", "buildsystem": "simple", diff --git a/src/dialogs.py b/src/dialogs.py index 9bd3fdc..898c34e 100644 --- a/src/dialogs.py +++ b/src/dialogs.py @@ -3,6 +3,8 @@ from gi.repository import Adw, Gtk, Gdk, GLib, GtkSource, Gio, GdkPixbuf import os from pytube import YouTube +from html2text import html2text +from . import connection_handler # CLEAR CHAT | WORKS @@ -312,3 +314,35 @@ def youtube_caption(self, video_url): callback = lambda dialog, task, video_url = video_url, caption_drop_down = caption_drop_down: youtube_caption_response(self, dialog, task, video_url, caption_drop_down) ) +# Website extraction | + +def attach_website_response(self, dialog, task, url): + if dialog.choose_finish(task) == "accept": + html = connection_handler.simple_get(url)['text'] + md = html2text(html) + buffer = self.message_text_view.get_buffer() + textview_text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False).replace(url, "") + buffer.delete(buffer.get_start_iter(), buffer.get_end_iter()) + buffer.insert(buffer.get_start_iter(), textview_text, len(textview_text)) + if not os.path.exists('/tmp/alpaca/websites/'): + os.makedirs('/tmp/alpaca/websites/') + md_name = self.generate_numbered_name('website.md', os.listdir('/tmp/alpaca/websites')) + file_path = os.path.join('/tmp/alpaca/websites/', md_name) + with open(file_path, 'w+') as f: + f.write('{}\n\n{}'.format(url, md)) + self.attach_file(file_path, 'website') + +def attach_website(self, url): + dialog = Adw.AlertDialog( + heading=_("Attach Website (Experimental)"), + body=_("Are you sure you want to attach\n'{}'?").format(url), + close_response="cancel" + ) + dialog.add_response("cancel", _("Cancel")) + dialog.add_response("accept", _("Accept")) + dialog.set_response_appearance("accept", Adw.ResponseAppearance.SUGGESTED) + dialog.choose( + parent = self, + cancellable = None, + callback = lambda dialog, task, url=url: attach_website_response(self, dialog, task, url) + ) diff --git a/src/window.py b/src/window.py index 85d99a9..7a965b8 100644 --- a/src/window.py +++ b/src/window.py @@ -196,7 +196,7 @@ class AlpacaWindow(Adw.ApplicationWindow): self.save_history() self.show_toast("info", 6, self.main_overlay) - if self.bot_message: return + if self.bot_message or self.get_focus() not in (self.message_text_view, self.send_button): return if not self.message_text_view.get_buffer().get_text(self.message_text_view.get_buffer().get_start_iter(), self.message_text_view.get_buffer().get_end_iter(), False): return current_chat_row = self.chat_list_box.get_selected_row() self.chat_list_box.unselect_all() @@ -514,6 +514,8 @@ class AlpacaWindow(Adw.ApplicationWindow): if file_type == 'youtube': self.file_preview_dialog.set_title(content.split('\n')[0]) self.file_preview_open_button.set_name(content.split('\n')[2]) + elif file_type == 'website': + self.file_preview_open_button.set_name(content.split('\n')[0]) else: self.file_preview_dialog.set_title(os.path.basename(file_path)) self.file_preview_open_button.set_name(file_path) @@ -545,7 +547,7 @@ class AlpacaWindow(Adw.ApplicationWindow): Generate a title following these rules: - The title should be based on the prompt at the end - Keep it in the same language as the prompt - - The title needs to be less than 4 words + - The title needs to be less than 30 characters - Use only alphanumeric characters and spaces - Just write the title, NOTHING ELSE @@ -558,6 +560,7 @@ Generate a title following these rules: if 'images' in message: data["images"] = message['images'] response = connection_handler.simple_post(f"{connection_handler.url}/api/generate", data=json.dumps(data)) new_chat_name = json.loads(response['text'])["response"].lstrip().rstrip().replace('"', '').replace("'", "").title() + new_chat_name = new_chat_name[:30] + (new_chat_name[30:] and '...') self.rename_chat(label_element.get_name(), new_chat_name, label_element) def show_message(self, msg:str, bot:bool, footer:str=None, images:list=None, files:dict=None, id:str=None): @@ -660,7 +663,12 @@ Generate a title following these rules: for name, file_type in files.items(): button_content = Adw.ButtonContent( label=name, - icon_name="play-symbolic" if file_type=='youtube' else "document-text-symbolic" + icon_name={ + "plain_text": "document-text-symbolic", + "pdf": "document-text-symbolic", + "youtube": "play-symbolic", + "website": "globe-symbolic" + }[file_type] ) button = Gtk.Button( vexpand=False, @@ -671,7 +679,6 @@ Generate a title following these rules: child=button_content ) file_path = os.path.join(self.data_dir, "chats", "{selected_chat}", id, name) - print(file_path) button.connect("clicked", lambda button, file_path=file_path, file_type=file_type: self.preview_file(file_path, file_type)) file_container.append(button) message_box.append(file_scroller) @@ -1308,7 +1315,7 @@ Generate a title following these rules: return base64.b64encode(image_data).decode("utf-8") except Exception as e: self.show_toast("error", 5, self.main_overlay) - elif file_type == 'plain_text' or file_type == 'youtube': + elif file_type == 'plain_text' or file_type == 'youtube' or file_type == 'website': with open(file_path, 'r') as f: return f.read() elif file_type == 'pdf': @@ -1334,7 +1341,8 @@ Generate a title following these rules: "image": "image-x-generic-symbolic", "plain_text": "document-text-symbolic", "pdf": "document-text-symbolic", - "youtube": "play-symbolic" + "youtube": "play-symbolic", + "website": "globe-symbolic" }[file_type] ) button = Gtk.Button( @@ -1372,11 +1380,20 @@ Generate a title following these rules: youtube_regex = re.compile( r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/' r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})') + url_regex = re.compile( + r'http[s]?://' + r'(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|' + r'(?:%[0-9a-fA-F][0-9a-fA-F]))+' + r'(?:\\:[0-9]{1,5})?' + r'(?:/[^\\s]*)?' + ) if youtube_regex.match(text): try: dialogs.youtube_caption(self, text) except Exception as e: self.show_toast("error", 10, self.main_overlay) + elif url_regex.match(text): + dialogs.attach_website(self, text) except Exception as e: 'huh' def cb_image_received(self, clipboard, result):