diff options
Diffstat (limited to 'src/main.py')
-rw-r--r-- | src/main.py | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..8997ab5 --- /dev/null +++ b/src/main.py @@ -0,0 +1,338 @@ +# main.py +# +# Copyright 2024 Lucas Fryzek +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# SPDX-License-Identifier: GPL-3.0-or-later + +import sys +import os +import gi + +gi.require_version('Gtk', '4.0') +gi.require_version('Adw', '1') + +from gi.repository import Gtk, Gio, Adw +from .window import WeegtkWindow + +from .network import Network +from weegtk import protocol +from weegtk import config +from .chat import WeegtkChat +from .preferences import WeegtkPreferences + +class WeegtkApplication(Adw.Application): + """The main application singleton class.""" + + def __init__(self): + super().__init__(application_id='com.fryzekconcepts.weegtk', + flags=Gio.ApplicationFlags.DEFAULT_FLAGS) + self.create_action('quit', lambda *_: self.quit(), ['<primary>q']) + self.create_action('about', self.on_about_action) + self.create_action('preferences', self.on_preferences_action) + + self.network = Network() + self.network.connect("status_changed", self._network_status_changed) + self.network.connect("message_from_weechat", self._network_weechat_msg) + + conf = config.read() + #self.network.connect_weechat("localhost", "9001", False, relay_pw, None, "") + self.network.connect_weechat_ssh(conf["ssh"]["host"], + conf["ssh"]["port"], + conf["ssh"]["username"], + conf["ssh"]["key"], + conf["relay"]["hostname"], + conf["relay"]["port"], + conf["relay"]["password"]) + + self.buffers = [] + + def _network_status_changed(self, source_object, status, extra): + """Called when the network status has changed.""" + # TODO handle this + #if self.config.getboolean('look', 'statusbar'): + # self.statusBar().showMessage(status) + #self.network.debug_print(0, '', status, forcecolor='#0000AA') + #self.network_status_set(status) + print(f"Status is {status}") + + def _network_weechat_msg(self, source_object, message): + """Called when a message is received from WeeChat.""" + #print(f"Message {message}") + try: + proto = protocol.Protocol() + message = proto.decode(message.get_data()) + # TODO figure this out + #if message.uncompressed: + # self.network.debug_print( + # 0, '==>', + # 'message uncompressed (%d bytes):\n%s' + # % (message.size_uncompressed, + # protocol.hex_and_ascii(message.uncompressed, 20)), + # forcecolor='#008800') + #self.network.debug_print(0, '', 'Message: %s' % message) + #print(f"parsed message is f{message}") + self.parse_message(message) + except: + print('Error while decoding message from WeeChat:\n%s' + % traceback.format_exc()) + self.net.disconnect_weechat() + + def do_activate(self): + """Called when the application is activated. + + We raise the application's main window, creating it if + necessary. + """ + win = self.props.active_window + if not win: + win = WeegtkWindow(application=self) + win.present() + + def on_about_action(self, *args): + """Callback for the app.about action.""" + about = Adw.AboutDialog(application_name='weegtk', + application_icon='com.fryzekconcepts.weegtk', + developer_name='Lucas Fryzek', + version='0.1.0', + developers=['Lucas Fryzek <lucas.fryzek@fryzekconcepts.com>'], + copyright='© 2024 Lucas Fryzek') + # Translators: Replace "translator-credits" with your name/username, and optionally an email or URL. + about.set_translator_credits(_('translator-credits')) + about.add_credit_section("QWeechat", ["Sébastien Helleu <flashcode@flashtux.org>"]) + about.present(self.props.active_window) + + def on_preferences_action(self, widget, _): + """Callback for the app.preferences action.""" + pref = WeegtkPreferences() + pref.present(self.props.active_window) + + def create_action(self, name, callback, shortcuts=None): + """Add an application action. + + Args: + name: the name of the action + callback: the function to be called when the action is + activated + shortcuts: an optional list of accelerators + """ + action = Gio.SimpleAction.new(name, None) + action.connect("activate", callback) + self.add_action(action) + if shortcuts: + self.set_accels_for_action(f"app.{name}", shortcuts) + + def _parse_handshake(self, message): + """Parse a WeeChat message with handshake response.""" + for obj in message.objects: + if obj.objtype != 'htb': + continue + self.network.init_with_handshake(obj.value) + break + + def _parse_listbuffers(self, message): + """Parse a WeeChat message with list of buffers.""" + # TODO handle gui for this + for obj in message.objects: + if obj.objtype != 'hda' or obj.value['path'][-1] != 'buffer': + continue + #self.list_buffers.clear() + #while self.stacked_buffers.count() > 0: + # buf = self.stacked_buffers.widget(0) + # self.stacked_buffers.removeWidget(buf) + self.buffers = [] + for item in obj.value['items']: + buf = self.create_buffer(item) + self.insert_buffer(len(self.buffers), buf) + #self.list_buffers.setCurrentRow(0) + #self.buffers[0].widget.input.setFocus() + + def _parse_line(self, message): + """Parse a WeeChat message with a buffer line.""" + # TODO implement text for chats + for obj in message.objects: + lines = [] + if obj.objtype != 'hda' or obj.value['path'][-1] != 'line_data': + continue + for item in obj.value['items']: + if message.msgid == 'listlines': + ptrbuf = item['__path'][0] + else: + ptrbuf = item['buffer'] + index = [i for i, b in enumerate(self.buffers) + if b.pointer() == ptrbuf] + if index: + lines.append( + (index[0], + (item['date'], item['prefix'], + item['message'])) + ) + if message.msgid == 'listlines': + lines.reverse() + for line in lines: + #print(line) + self.buffers[line[0]].display(*line[1]) + + def _parse_nicklist(self, message): + """Parse a WeeChat message with a buffer nicklist.""" + # TODO implement nicklist for chat + # buffer_refresh = {} + # for obj in message.objects: + # if obj.objtype != 'hda' or \ + # obj.value['path'][-1] != 'nicklist_item': + # continue + # group = '__root' + # for item in obj.value['items']: + # index = [i for i, b in enumerate(self.buffers) + # if b.pointer() == item['__path'][0]] + # if index: + # if not index[0] in buffer_refresh: + # self.buffers[index[0]].nicklist = {} + # buffer_refresh[index[0]] = True + # if item['group']: + # group = item['name'] + # self.buffers[index[0]].nicklist_add_item( + # group, item['group'], item['prefix'], item['name'], + # item['visible']) + # for index in buffer_refresh: + # self.buffers[index].nicklist_refresh() + + def _parse_nicklist_diff(self, message): + """Parse a WeeChat message with a buffer nicklist diff.""" + buffer_refresh = {} + for obj in message.objects: + if obj.objtype != 'hda' or \ + obj.value['path'][-1] != 'nicklist_item': + continue + group = '__root' + for item in obj.value['items']: + index = [i for i, b in enumerate(self.buffers) + if b.pointer() == item['__path'][0]] + if not index: + continue + buffer_refresh[index[0]] = True + if item['_diff'] == ord('^'): + group = item['name'] + elif item['_diff'] == ord('+'): + self.buffers[index[0]].nicklist_add_item( + group, item['group'], item['prefix'], item['name'], + item['visible']) + elif item['_diff'] == ord('-'): + self.buffers[index[0]].nicklist_remove_item( + group, item['group'], item['name']) + elif item['_diff'] == ord('*'): + self.buffers[index[0]].nicklist_update_item( + group, item['group'], item['prefix'], item['name'], + item['visible']) + for index in buffer_refresh: + self.buffers[index].nicklist_refresh() + + def _parse_buffer_opened(self, message): + """Parse a WeeChat message with a new buffer (opened).""" + for obj in message.objects: + if obj.objtype != 'hda' or obj.value['path'][-1] != 'buffer': + continue + for item in obj.value['items']: + buf = self.create_buffer(item) + index = self.find_buffer_index_for_insert(item['next_buffer']) + self.insert_buffer(index, buf) + + def _parse_buffer(self, message): + """Parse a WeeChat message with a buffer event + (anything except a new buffer). + """ + for obj in message.objects: + if obj.objtype != 'hda' or obj.value['path'][-1] != 'buffer': + continue + for item in obj.value['items']: + index = [i for i, b in enumerate(self.buffers) + if b.pointer() == item['__path'][0]] + if not index: + continue + index = index[0] + if message.msgid == '_buffer_type_changed': + self.buffers[index].data['type'] = item['type'] + elif message.msgid in ('_buffer_moved', '_buffer_merged', + '_buffer_unmerged'): + buf = self.buffers[index] + buf.data['number'] = item['number'] + self.remove_buffer(index) + index2 = self.find_buffer_index_for_insert( + item['next_buffer']) + self.insert_buffer(index2, buf) + elif message.msgid == '_buffer_renamed': + self.buffers[index].data['full_name'] = item['full_name'] + self.buffers[index].data['short_name'] = item['short_name'] + elif message.msgid == '_buffer_title_changed': + self.buffers[index].data['title'] = item['title'] + self.buffers[index].update_title() + elif message.msgid == '_buffer_cleared': + self.buffers[index].widget.chat.clear() + elif message.msgid.startswith('_buffer_localvar_'): + self.buffers[index].data['local_variables'] = \ + item['local_variables'] + self.buffers[index].update_prompt() + elif message.msgid == '_buffer_closing': + self.remove_buffer(index) + + def parse_message(self, message): + """Parse a WeeChat message.""" + if message.msgid.startswith('debug'): + self.network.debug_print(0, '', '(debug message, ignored)') + elif message.msgid == 'handshake': + self._parse_handshake(message) + elif message.msgid == 'listbuffers': + self._parse_listbuffers(message) + elif message.msgid in ('listlines', '_buffer_line_added'): + self._parse_line(message) + elif message.msgid in ('_nicklist', 'nicklist'): + self._parse_nicklist(message) + elif message.msgid == '_nicklist_diff': + self._parse_nicklist_diff(message) + elif message.msgid == '_buffer_opened': + self._parse_buffer_opened(message) + elif message.msgid.startswith('_buffer_'): + self._parse_buffer(message) + elif message.msgid == '_upgrade': + self.network.desync_weechat() + elif message.msgid == '_upgrade_ended': + self.network.sync_weechat() + else: + print(f"Unknown message with id {message.msgid}") + + def create_buffer(self, item): + """Create a new buffer.""" + buf = WeegtkChat(data=item) + buf.connect("buffer_input", self.buffer_input) + return buf + + def insert_buffer(self, index, buf): + """Insert a buffer in list.""" + self.buffers.insert(index, buf) + buf_name = buf.data['short_name'] + + # Only make chats visible on main screen + if buf.is_chat(): + self.props.active_window.stack.add_titled(buf, name=buf_name, title=buf_name) + + def buffer_input(self, source_object, full_name, text): + if self.network.is_connected(): + message = f"input {full_name} {text}\n" + self.network.send_to_weechat(message) + +def main(version): + """The application's entry point.""" + app = WeegtkApplication() + return app.run(sys.argv) |