From 2de4eea35744f7e6ef6aa82b6beba35a3cae38da Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 26 Jan 2018 23:21:46 +0300 Subject: [PATCH 001/217] initial commit - rewriting. ngc wrapper added, project structure updated --- tests/tests.py | 18 +- toxygen/app.py | 263 +++++ toxygen/av/__init__.py | 0 toxygen/av/call.py | 58 ++ toxygen/{ => av}/calls.py | 66 +- toxygen/{ => av}/screen_sharing.py | 0 toxygen/bootstrap/__init__.py | 0 toxygen/{ => bootstrap}/bootstrap.py | 2 +- toxygen/{ => bootstrap}/nodes.json | 0 toxygen/callbacks.py | 12 +- toxygen/contacts/__init__.py | 0 toxygen/{ => contacts}/basecontact.py | 10 +- toxygen/{ => contacts}/contact.py | 7 +- toxygen/{ => contacts}/friend.py | 4 +- toxygen/{ => contacts}/group_chat.py | 6 +- toxygen/{ => contacts}/profile.py | 37 +- toxygen/db/__init__.py | 0 toxygen/{ => db}/history.py | 30 +- toxygen/file_transfers.py | 8 +- toxygen/login.py | 0 toxygen/main.py | 434 +------- toxygen/messenger/__init__.py | 0 toxygen/{ => messenger}/messages.py | 0 toxygen/network/__init__.py | 0 toxygen/{ => network}/tox_dns.py | 2 +- toxygen/plugin_support/__init__.py | 0 .../{ => plugin_support}/plugin_support.py | 4 +- .../{smileys.py => smileys_and_stickers.py} | 0 toxygen/threads.py | 62 ++ toxygen/toxcore_enums_and_consts.py | 220 ---- toxygen/tray.py | 83 ++ toxygen/ui/__init__.py | 0 toxygen/{ => ui}/avwidgets.py | 6 +- toxygen/{ => ui}/items_factory.py | 3 +- toxygen/{ => ui}/list_items.py | 12 +- toxygen/{ => ui}/loginscreen.py | 3 +- toxygen/{ => ui}/mainscreen.py | 13 +- toxygen/{ => ui}/mainscreen_widgets.py | 6 +- toxygen/{ => ui}/menu.py | 14 +- toxygen/{ => ui}/passwordscreen.py | 2 +- toxygen/{ => ui}/widgets.py | 0 toxygen/updater/__init__.py | 0 toxygen/{ => updater}/updater.py | 2 +- toxygen/user_data/__init__.py | 0 toxygen/user_data/profile_manager.py | 70 ++ toxygen/{ => user_data}/settings.py | 82 +- toxygen/{ => user_data}/toxes.py | 10 +- toxygen/util/__init__.py | 0 toxygen/{ => util}/util.py | 2 +- toxygen/wrapper/__init__.py | 0 toxygen/{ => wrapper}/libtox.py | 0 toxygen/{ => wrapper}/tox.py | 874 ++++++++++++++-- toxygen/{ => wrapper}/toxav.py | 4 +- toxygen/{ => wrapper}/toxav_enums.py | 0 toxygen/wrapper/toxcore_enums_and_consts.py | 944 ++++++++++++++++++ toxygen/{ => wrapper}/toxencryptsave.py | 4 +- .../toxencryptsave_enums_and_consts.py | 0 57 files changed, 2420 insertions(+), 957 deletions(-) create mode 100644 toxygen/app.py create mode 100644 toxygen/av/__init__.py create mode 100644 toxygen/av/call.py rename toxygen/{ => av}/calls.py (85%) rename toxygen/{ => av}/screen_sharing.py (100%) create mode 100644 toxygen/bootstrap/__init__.py rename toxygen/{ => bootstrap}/bootstrap.py (98%) rename toxygen/{ => bootstrap}/nodes.json (100%) create mode 100644 toxygen/contacts/__init__.py rename toxygen/{ => contacts}/basecontact.py (92%) rename toxygen/{ => contacts}/contact.py (99%) rename toxygen/{ => contacts}/friend.py (97%) rename toxygen/{ => contacts}/group_chat.py (94%) rename toxygen/{ => contacts}/profile.py (98%) create mode 100644 toxygen/db/__init__.py rename toxygen/{ => db}/history.py (88%) create mode 100644 toxygen/login.py create mode 100644 toxygen/messenger/__init__.py rename toxygen/{ => messenger}/messages.py (100%) create mode 100644 toxygen/network/__init__.py rename toxygen/{ => network}/tox_dns.py (98%) create mode 100644 toxygen/plugin_support/__init__.py rename toxygen/{ => plugin_support}/plugin_support.py (99%) rename toxygen/{smileys.py => smileys_and_stickers.py} (100%) create mode 100644 toxygen/threads.py delete mode 100644 toxygen/toxcore_enums_and_consts.py create mode 100644 toxygen/tray.py create mode 100644 toxygen/ui/__init__.py rename toxygen/{ => ui}/avwidgets.py (98%) rename toxygen/{ => ui}/items_factory.py (97%) rename toxygen/{ => ui}/list_items.py (99%) rename toxygen/{ => ui}/loginscreen.py (98%) rename toxygen/{ => ui}/mainscreen.py (99%) rename toxygen/{ => ui}/mainscreen_widgets.py (99%) rename toxygen/{ => ui}/menu.py (99%) rename toxygen/{ => ui}/passwordscreen.py (99%) rename toxygen/{ => ui}/widgets.py (100%) create mode 100644 toxygen/updater/__init__.py rename toxygen/{ => updater}/updater.py (99%) create mode 100644 toxygen/user_data/__init__.py create mode 100644 toxygen/user_data/profile_manager.py rename toxygen/{ => user_data}/settings.py (72%) rename toxygen/{ => user_data}/toxes.py (77%) create mode 100644 toxygen/util/__init__.py rename toxygen/{ => util}/util.py (98%) create mode 100644 toxygen/wrapper/__init__.py rename toxygen/{ => wrapper}/libtox.py (100%) rename toxygen/{ => wrapper}/tox.py (68%) rename toxygen/{ => wrapper}/toxav.py (99%) rename toxygen/{ => wrapper}/toxav_enums.py (100%) create mode 100644 toxygen/wrapper/toxcore_enums_and_consts.py rename toxygen/{ => wrapper}/toxencryptsave.py (97%) rename toxygen/{ => wrapper}/toxencryptsave_enums_and_consts.py (100%) diff --git a/tests/tests.py b/tests/tests.py index 27618af..cad1d1c 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,9 +1,9 @@ -from toxygen.profile import * -from toxygen.tox_dns import tox_dns -from toxygen.history import History +from contacts.profile import * +from network.tox_dns import tox_dns +from db.history import History from toxygen.smileys import SmileyLoader -from toxygen.messages import * -import toxygen.toxes as encr +from messenger.messages import * +import user_data.toxes as encr import toxygen.util as util import time @@ -23,15 +23,15 @@ class TestTox: assert tox.self_get_status_message() == str(status_message, 'utf-8') -class TestProfileHelper: +class TestProfileManager: def test_creation(self): file_name, path = 'test.tox', os.path.dirname(os.path.realpath(__file__)) + '/' data = b'test' with open(path + file_name, 'wb') as fl: fl.write(data) - ph = ProfileHelper(path, file_name[:4]) - assert ProfileHelper.get_path() == path + ph = ProfileManager(path, file_name[:4]) + assert ProfileManager.get_path() == path assert ph.open_profile() == data assert os.path.exists(path + 'avatars/') @@ -81,7 +81,7 @@ def create_singletons(): Settings._instance = Settings.get_default_settings() if not os.path.exists(folder): os.makedirs(folder) - ProfileHelper(folder, 'test') + ProfileManager(folder, 'test') def create_friend(name, status_message, number, tox_id): diff --git a/toxygen/app.py b/toxygen/app.py new file mode 100644 index 0000000..1dec36f --- /dev/null +++ b/toxygen/app.py @@ -0,0 +1,263 @@ +class App: + + def __init__(self, path_or_uri=None): + self.tox = self.ms = self.init = self.app = self.tray = self.mainloop = self.avloop = None + if path_or_uri is None: + self.uri = self.path = None + elif path_or_uri.startswith('tox:'): + self.path = None + self.uri = path_or_uri[4:] + else: + self.path = path_or_uri + self.uri = None + + def enter_pass(self, data): + """ + Show password screen + """ + tmp = [data] + p = PasswordScreen(toxes.ToxES.get_instance(), tmp) + p.show() + self.app.lastWindowClosed.connect(self.app.quit) + self.app.exec_() + if tmp[0] == data: + raise SystemExit() + else: + return tmp[0] + + def main(self): + """ + Main function of app. loads login screen if needed and starts main screen + """ + app = QtWidgets.QApplication(sys.argv) + app.setWindowIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) + self.app = app + + if platform.system() == 'Linux': + QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) + + with open(curr_directory() + '/styles/dark_style.qss') as fl: + style = fl.read() + app.setStyleSheet(style) + + encrypt_save = toxes.ToxES() + + if self.path is not None: + path = os.path.dirname(self.path) + '/' + name = os.path.basename(self.path)[:-4] + data = ProfileManager(path, name).open_profile() + if encrypt_save.is_data_encrypted(data): + data = self.enter_pass(data) + settings = Settings(name) + self.tox = profile.tox_factory(data, settings) + else: + auto_profile = Settings.get_auto_profile() + if not auto_profile[0]: + # show login screen if default profile not found + current_locale = QtCore.QLocale() + curr_lang = current_locale.languageToString(current_locale.language()) + langs = Settings.supported_languages() + if curr_lang in langs: + lang_path = langs[curr_lang] + translator = QtCore.QTranslator() + translator.load(curr_directory() + '/translations/' + lang_path) + app.installTranslator(translator) + app.translator = translator + ls = LoginScreen() + ls.setWindowIconText("Toxygen") + profiles = ProfileManager.find_profiles() + ls.update_select(map(lambda x: x[1], profiles)) + _login = self.Login(profiles) + ls.update_on_close(_login.login_screen_close) + ls.show() + app.exec_() + if not _login.t: + return + elif _login.t == 1: # create new profile + _login.name = _login.name.strip() + name = _login.name if _login.name else 'toxygen_user' + pr = map(lambda x: x[1], ProfileManager.find_profiles()) + if name in list(pr): + msgBox = QtWidgets.QMessageBox() + msgBox.setWindowTitle( + QtWidgets.QApplication.translate("MainWindow", "Error")) + text = (QtWidgets.QApplication.translate("MainWindow", + 'Profile with this name already exists')) + msgBox.setText(text) + msgBox.exec_() + return + self.tox = profile.tox_factory() + self.tox.self_set_name(bytes(_login.name, 'utf-8') if _login.name else b'Toxygen User') + self.tox.self_set_status_message(b'Toxing on Toxygen') + reply = QtWidgets.QMessageBox.question(None, + 'Profile {}'.format(name), + QtWidgets.QApplication.translate("login", + 'Do you want to set profile password?'), + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + set_pass = SetProfilePasswordScreen(encrypt_save) + set_pass.show() + self.app.lastWindowClosed.connect(self.app.quit) + self.app.exec_() + reply = QtWidgets.QMessageBox.question(None, + 'Profile {}'.format(name), + QtWidgets.QApplication.translate("login", + 'Do you want to save profile in default folder? If no, profile will be saved in program folder'), + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + path = Settings.get_default_path() + else: + path = curr_directory() + '/' + try: + ProfileManager(path, name).save_profile(self.tox.get_savedata()) + except Exception as ex: + print(str(ex)) + log('Profile creation exception: ' + str(ex)) + msgBox = QtWidgets.QMessageBox() + msgBox.setText(QtWidgets.QApplication.translate("login", + 'Profile saving error! Does Toxygen have permission to write to this directory?')) + msgBox.exec_() + return + path = Settings.get_default_path() + settings = Settings(name) + if curr_lang in langs: + settings['language'] = curr_lang + settings.save() + else: # load existing profile + path, name = _login.get_data() + if _login.default: + Settings.set_auto_profile(path, name) + data = ProfileManager(path, name).open_profile() + if encrypt_save.is_data_encrypted(data): + data = self.enter_pass(data) + settings = Settings(name) + self.tox = profile.tox_factory(data, settings) + else: + path, name = auto_profile + data = ProfileManager(path, name).open_profile() + if encrypt_save.is_data_encrypted(data): + data = self.enter_pass(data) + settings = Settings(name) + self.tox = profile.tox_factory(data, settings) + + if Settings.is_active_profile(path, name): # profile is in use + reply = QtWidgets.QMessageBox.question(None, + 'Profile {}'.format(name), + QtWidgets.QApplication.translate("login", 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?'), + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply != QtWidgets.QMessageBox.Yes: + return + else: + settings.set_active_profile() + + # application color scheme + for theme in settings.built_in_themes().keys(): + if settings['theme'] == theme: + with open(curr_directory() + settings.built_in_themes()[theme]) as fl: + style = fl.read() + app.setStyleSheet(style) + + lang = Settings.supported_languages()[settings['language']] + translator = QtCore.QTranslator() + translator.load(curr_directory() + '/translations/' + lang) + app.installTranslator(translator) + app.translator = translator + + # tray icon + + + self.ms.show() + + updating = False + if settings['update'] and updater.updater_available() and updater.connection_available(): # auto update + version = updater.check_for_updates() + if version is not None: + if settings['update'] == 2: + updater.download(version) + updating = True + else: + reply = QtWidgets.QMessageBox.question(None, + 'Toxygen', + QtWidgets.QApplication.translate("login", + 'Update for Toxygen was found. Download and install it?'), + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + updater.download(version) + updating = True + + if updating: + data = self.tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + settings.close() + del self.tox + return + + plugin_helper = PluginLoader(self.tox, settings) # plugin support + plugin_helper.load() + + start() + # init thread + self.init = self.InitThread(self.tox, self.ms, self.tray) + self.init.start() + + # starting threads for tox iterate and toxav iterate + self.mainloop = self.ToxIterateThread(self.tox) + self.mainloop.start() + self.avloop = self.ToxAVIterateThread(self.tox.AV) + self.avloop.start() + + if self.uri is not None: + self.ms.add_contact(self.uri) + + app.lastWindowClosed.connect(app.quit) + app.exec_() + + self.init.stop = True + self.mainloop.stop = True + self.avloop.stop = True + plugin_helper.stop() + stop() + self.mainloop.wait() + self.init.wait() + self.avloop.wait() + self.tray.hide() + data = self.tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + settings.close() + del self.tox + + def reset(self): + """ + Create new tox instance (new network settings) + :return: tox instance + """ + self.mainloop.stop = True + self.init.stop = True + self.avloop.stop = True + self.mainloop.wait() + self.init.wait() + self.avloop.wait() + data = self.tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + del self.tox + # create new tox instance + self.tox = profile.tox_factory(data, Settings.get_instance()) + # init thread + self.init = self.InitThread(self.tox, self.ms, self.tray) + self.init.start() + + # starting threads for tox iterate and toxav iterate + self.mainloop = self.ToxIterateThread(self.tox) + self.mainloop.start() + + self.avloop = self.ToxAVIterateThread(self.tox.AV) + self.avloop.start() + + plugin_helper = PluginLoader.get_instance() + plugin_helper.set_tox(self.tox) + + return self.tox \ No newline at end of file diff --git a/toxygen/av/__init__.py b/toxygen/av/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/av/call.py b/toxygen/av/call.py new file mode 100644 index 0000000..c43e264 --- /dev/null +++ b/toxygen/av/call.py @@ -0,0 +1,58 @@ + + +class Call: + + def __init__(self, out_audio, out_video, in_audio=False, in_video=False): + self._in_audio = in_audio + self._in_video = in_video + self._out_audio = out_audio + self._out_video = out_video + self._is_active = False + + def get_is_active(self): + return self._is_active + + def set_is_active(self, value): + self._is_active = value + + is_active = property(get_is_active, set_is_active) + + # ----------------------------------------------------------------------------------------------------------------- + # Audio + # ----------------------------------------------------------------------------------------------------------------- + + def get_in_audio(self): + return self._in_audio + + def set_in_audio(self, value): + self._in_audio = value + + in_audio = property(get_in_audio, set_in_audio) + + def get_out_audio(self): + return self._out_audio + + def set_out_audio(self, value): + self._out_audio = value + + out_audio = property(get_out_audio, set_out_audio) + + # ----------------------------------------------------------------------------------------------------------------- + # Video + # ----------------------------------------------------------------------------------------------------------------- + + def get_in_video(self): + return self._in_video + + def set_in_video(self, value): + self._in_video = value + + in_video = property(get_in_video, set_in_video) + + def get_out_video(self): + return self._out_video + + def set_out_video(self, value): + self._in_video = value + + out_video = property(get_out_video, set_out_video) diff --git a/toxygen/calls.py b/toxygen/av/calls.py similarity index 85% rename from toxygen/calls.py rename to toxygen/av/calls.py index 6477c03..20684b2 100644 --- a/toxygen/calls.py +++ b/toxygen/av/calls.py @@ -1,73 +1,17 @@ import pyaudio import time import threading -import settings -from toxav_enums import * +from user_data import settings +from wrapper.toxav_enums import * import cv2 import itertools import numpy as np -import screen_sharing +from av import screen_sharing + + # TODO: play sound until outgoing call will be started or cancelled -class Call: - - def __init__(self, out_audio, out_video, in_audio=False, in_video=False): - self._in_audio = in_audio - self._in_video = in_video - self._out_audio = out_audio - self._out_video = out_video - self._is_active = False - - def get_is_active(self): - return self._is_active - - def set_is_active(self, value): - self._is_active = value - - is_active = property(get_is_active, set_is_active) - - # ----------------------------------------------------------------------------------------------------------------- - # Audio - # ----------------------------------------------------------------------------------------------------------------- - - def get_in_audio(self): - return self._in_audio - - def set_in_audio(self, value): - self._in_audio = value - - in_audio = property(get_in_audio, set_in_audio) - - def get_out_audio(self): - return self._out_audio - - def set_out_audio(self, value): - self._out_audio = value - - out_audio = property(get_out_audio, set_out_audio) - - # ----------------------------------------------------------------------------------------------------------------- - # Video - # ----------------------------------------------------------------------------------------------------------------- - - def get_in_video(self): - return self._in_video - - def set_in_video(self, value): - self._in_video = value - - in_video = property(get_in_video, set_in_video) - - def get_out_video(self): - return self._out_video - - def set_out_video(self, value): - self._in_video = value - - out_video = property(get_out_video, set_out_video) - - class AV: def __init__(self, toxav): diff --git a/toxygen/screen_sharing.py b/toxygen/av/screen_sharing.py similarity index 100% rename from toxygen/screen_sharing.py rename to toxygen/av/screen_sharing.py diff --git a/toxygen/bootstrap/__init__.py b/toxygen/bootstrap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/bootstrap.py b/toxygen/bootstrap/bootstrap.py similarity index 98% rename from toxygen/bootstrap.py rename to toxygen/bootstrap/bootstrap.py index 0589940..6d97f25 100644 --- a/toxygen/bootstrap.py +++ b/toxygen/bootstrap/bootstrap.py @@ -1,7 +1,7 @@ import random import urllib.request from util import log, curr_directory -import settings +from user_data import settings from PyQt5 import QtNetwork, QtCore import json diff --git a/toxygen/nodes.json b/toxygen/bootstrap/nodes.json similarity index 100% rename from toxygen/nodes.json rename to toxygen/bootstrap/nodes.json diff --git a/toxygen/callbacks.py b/toxygen/callbacks.py index b59d17c..ba2994d 100644 --- a/toxygen/callbacks.py +++ b/toxygen/callbacks.py @@ -1,10 +1,10 @@ -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5 import QtGui from notifications import * -from settings import Settings -from profile import Profile -from toxcore_enums_and_consts import * -from toxav_enums import * -from tox import bin_to_string +from user_data.settings import Settings +from contacts.profile import Profile +from wrapper.toxcore_enums_and_consts import * +from wrapper.toxav_enums import * +from wrapper.tox import bin_to_string from plugin_support import PluginLoader import queue import threading diff --git a/toxygen/contacts/__init__.py b/toxygen/contacts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/basecontact.py b/toxygen/contacts/basecontact.py similarity index 92% rename from toxygen/basecontact.py rename to toxygen/contacts/basecontact.py index e1243a4..6e2d55a 100644 --- a/toxygen/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -1,6 +1,6 @@ -from settings import * +from user_data.settings import * from PyQt5 import QtCore, QtGui -from toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE +from wrapper.toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE class BaseContact: @@ -81,7 +81,7 @@ class BaseContact: """ Tries to load avatar of contact or uses default avatar """ - prefix = ProfileHelper.get_path() + 'avatars/' + prefix = ProfileManager.get_path() + 'avatars/' avatar_path = prefix + '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image avatar_path = curr_directory() + '/images/avatar.png' @@ -92,13 +92,13 @@ class BaseContact: self._widget.avatar_label.repaint() def reset_avatar(self): - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if os.path.isfile(avatar_path): os.remove(avatar_path) self.load_avatar() def set_avatar(self, avatar): - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) with open(avatar_path, 'wb') as f: f.write(avatar) self.load_avatar() diff --git a/toxygen/contact.py b/toxygen/contacts/contact.py similarity index 99% rename from toxygen/contact.py rename to toxygen/contacts/contact.py index 9f27a1d..ad491fd 100644 --- a/toxygen/contact.py +++ b/toxygen/contacts/contact.py @@ -1,8 +1,7 @@ -from PyQt5 import QtCore, QtGui -from history import * -import basecontact +from db.history import * +from contacts import basecontact import util -from messages import * +from messenger.messages import * import file_transfers as ft import re diff --git a/toxygen/friend.py b/toxygen/contacts/friend.py similarity index 97% rename from toxygen/friend.py rename to toxygen/contacts/friend.py index d912708..b80032a 100644 --- a/toxygen/friend.py +++ b/toxygen/contacts/friend.py @@ -1,5 +1,5 @@ -import contact -from messages import * +from contacts import contact +from messenger.messages import * import os diff --git a/toxygen/group_chat.py b/toxygen/contacts/group_chat.py similarity index 94% rename from toxygen/group_chat.py rename to toxygen/contacts/group_chat.py index f7921a1..05faaa9 100644 --- a/toxygen/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -1,9 +1,11 @@ -import contact +from contacts import contact import util from PyQt5 import QtGui, QtCore -import toxcore_enums_and_consts as constants +from wrapper import toxcore_enums_and_consts as constants +# TODO: ngc + class GroupChat(contact.Contact): def __init__(self, name, status_message, widget, tox, group_number): diff --git a/toxygen/profile.py b/toxygen/contacts/profile.py similarity index 98% rename from toxygen/profile.py rename to toxygen/contacts/profile.py index 204419a..f74d134 100644 --- a/toxygen/profile.py +++ b/toxygen/contacts/profile.py @@ -1,22 +1,21 @@ -from list_items import * -from PyQt5 import QtGui, QtWidgets -from friend import * -from settings import * -from toxcore_enums_and_consts import * +from ui.list_items import * +from PyQt5 import QtWidgets +from contacts.friend import * +from user_data.settings import * +from wrapper.toxcore_enums_and_consts import * from ctypes import * from util import log, Singleton, curr_directory -from tox_dns import tox_dns -from history import * +from network.tox_dns import tox_dns +from db.history import * from file_transfers import * import time -import calls -import avwidgets +from av import calls import plugin_support -import basecontact -import items_factory +from contacts import basecontact +from ui import items_factory, avwidgets import cv2 import threading -from group_chat import * +from contacts.group_chat import * import re @@ -283,7 +282,7 @@ class Profile(basecontact.BaseContact, Singleton): if friend.tox_id is None: avatar_path = curr_directory() + '/images/group.png' else: - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path): # load default image avatar_path = curr_directory() + '/images/avatar.png' os.chdir(os.path.dirname(avatar_path)) @@ -752,7 +751,7 @@ class Profile(basecontact.BaseContact, Singleton): else: self.set_active(0) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) def add_friend(self, tox_id): """ @@ -785,7 +784,7 @@ class Profile(basecontact.BaseContact, Singleton): num = self._tox.friend_by_public_key(tox_id) self.delete_friend(num) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) except: # not in friend list pass @@ -801,7 +800,7 @@ class Profile(basecontact.BaseContact, Singleton): if add_to_friend_list: self.add_friend(tox_id) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) # ----------------------------------------------------------------------------------------------------------------- # Friend requests @@ -837,7 +836,7 @@ class Profile(basecontact.BaseContact, Singleton): friend = Friend(message_getter, result, tox_id, '', item, tox_id) self._contacts.append(friend) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) return True except Exception as ex: # wrong data log('Friend request failed with ' + str(ex)) @@ -857,7 +856,7 @@ class Profile(basecontact.BaseContact, Singleton): if reply == QtWidgets.QMessageBox.Yes: # accepted self.add_friend(tox_id) data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) + ProfileManager.get_instance().save_profile(data) except Exception as ex: # something is wrong log('Accept friend request failed! ' + str(ex)) @@ -1180,7 +1179,7 @@ class Profile(basecontact.BaseContact, Singleton): """ :param friend_number: number of friend who should get new avatar """ - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path): # reset image avatar_path = None sa = SendAvatar(avatar_path, self._tox, friend_number) diff --git a/toxygen/db/__init__.py b/toxygen/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/history.py b/toxygen/db/history.py similarity index 88% rename from toxygen/history.py rename to toxygen/db/history.py index 586981a..eea7457 100644 --- a/toxygen/history.py +++ b/toxygen/db/history.py @@ -1,8 +1,8 @@ from sqlite3 import connect -import settings +from user_data import settings from os import chdir import os.path -from toxes import ToxES +from user_data.toxes import ToxES PAGE_SIZE = 42 @@ -17,13 +17,15 @@ MESSAGE_OWNER = { 'NOT_SENT': 2 } +# TODO: unique message id and ngc support + class History: def __init__(self, name): self._name = name - chdir(settings.ProfileHelper.get_path()) - path = settings.ProfileHelper.get_path() + self._name + '.hstr' + chdir(settings.ProfileManager.get_path()) + path = settings.ProfileManager.get_path() + self._name + '.hstr' if os.path.exists(path): decr = ToxES.get_instance() try: @@ -45,7 +47,7 @@ class History: def save(self): encr = ToxES.get_instance() if encr.has_password(): - path = settings.ProfileHelper.get_path() + self._name + '.hstr' + path = settings.ProfileManager.get_path() + self._name + '.hstr' with open(path, 'rb') as fin: data = fin.read() data = encr.pass_encrypt(bytes(data)) @@ -53,7 +55,7 @@ class History: fout.write(data) def export(self, directory): - path = settings.ProfileHelper.get_path() + self._name + '.hstr' + path = settings.ProfileManager.get_path() + self._name + '.hstr' new_path = directory + self._name + '.hstr' with open(path, 'rb') as fin: data = fin.read() @@ -64,7 +66,7 @@ class History: fout.write(data) def add_friend_to_db(self, tox_id): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -84,7 +86,7 @@ class History: db.close() def delete_friend_from_db(self, tox_id): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -98,7 +100,7 @@ class History: db.close() def friend_exists_in_db(self, tox_id): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) cursor = db.cursor() cursor.execute('SELECT 0 FROM friends WHERE tox_id=?', (tox_id, )) @@ -107,7 +109,7 @@ class History: return result is not None def save_messages_to_db(self, tox_id, messages_iter): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -121,7 +123,7 @@ class History: db.close() def update_messages(self, tox_id, unsent_time): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -136,7 +138,7 @@ class History: def delete_message(self, tox_id, time): start, end = str(time - 0.01), str(time + 0.01) - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -150,7 +152,7 @@ class History: db.close() def delete_messages(self, tox_id): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: cursor = db.cursor() @@ -174,7 +176,7 @@ class History: self._db = self._cursor = None def connect(self): - chdir(settings.ProfileHelper.get_path()) + chdir(settings.ProfileManager.get_path()) self._db = connect(self._name + '.hstr', timeout=TIMEOUT) self._cursor = self._db.cursor() self._cursor.execute('SELECT message, owner, unix_time, message_type FROM id' + self._tox_id + diff --git a/toxygen/file_transfers.py b/toxygen/file_transfers.py index 7e0b193..6c65809 100644 --- a/toxygen/file_transfers.py +++ b/toxygen/file_transfers.py @@ -1,9 +1,9 @@ -from toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL +from wrapper.toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL from os.path import basename, getsize, exists, dirname from os import remove, rename, chdir from time import time, sleep -from tox import Tox -import settings +from wrapper.tox import Tox +from user_data import settings from PyQt5 import QtCore @@ -305,7 +305,7 @@ class ReceiveAvatar(ReceiveTransfer): MAX_AVATAR_SIZE = 512 * 1024 def __init__(self, tox, friend_number, size, file_number): - path = settings.ProfileHelper.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) + path = settings.ProfileManager.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) super(ReceiveAvatar, self).__init__(path + '.tmp', tox, friend_number, size, file_number) if size > self.MAX_AVATAR_SIZE: self.send_control(TOX_FILE_CONTROL['CANCEL']) diff --git a/toxygen/login.py b/toxygen/login.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/main.py b/toxygen/main.py index d630bb6..4e91c50 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,434 +1,11 @@ import sys -from loginscreen import LoginScreen -import profile -from settings import * -from PyQt5 import QtCore, QtGui, QtWidgets -from bootstrap import generate_nodes, download_nodes_list -from mainscreen import MainWindow +from user_data.settings import * from callbacks import init_callbacks, stop, start from util import curr_directory, program_version, remove -import styles.style # reqired for styles loading -import platform -import toxes -from passwordscreen import PasswordScreen, UnlockAppScreen, SetProfilePasswordScreen -from plugin_support import PluginLoader import updater +import argparse -class Toxygen: - - def __init__(self, path_or_uri=None): - super(Toxygen, self).__init__() - self.tox = self.ms = self.init = self.app = self.tray = self.mainloop = self.avloop = None - if path_or_uri is None: - self.uri = self.path = None - elif path_or_uri.startswith('tox:'): - self.path = None - self.uri = path_or_uri[4:] - else: - self.path = path_or_uri - self.uri = None - - def enter_pass(self, data): - """ - Show password screen - """ - tmp = [data] - p = PasswordScreen(toxes.ToxES.get_instance(), tmp) - p.show() - self.app.lastWindowClosed.connect(self.app.quit) - self.app.exec_() - if tmp[0] == data: - raise SystemExit() - else: - return tmp[0] - - def main(self): - """ - Main function of app. loads login screen if needed and starts main screen - """ - app = QtWidgets.QApplication(sys.argv) - app.setWindowIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) - self.app = app - - if platform.system() == 'Linux': - QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) - - with open(curr_directory() + '/styles/dark_style.qss') as fl: - style = fl.read() - app.setStyleSheet(style) - - encrypt_save = toxes.ToxES() - - if self.path is not None: - path = os.path.dirname(self.path) + '/' - name = os.path.basename(self.path)[:-4] - data = ProfileHelper(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) - else: - auto_profile = Settings.get_auto_profile() - if not auto_profile[0]: - # show login screen if default profile not found - current_locale = QtCore.QLocale() - curr_lang = current_locale.languageToString(current_locale.language()) - langs = Settings.supported_languages() - if curr_lang in langs: - lang_path = langs[curr_lang] - translator = QtCore.QTranslator() - translator.load(curr_directory() + '/translations/' + lang_path) - app.installTranslator(translator) - app.translator = translator - ls = LoginScreen() - ls.setWindowIconText("Toxygen") - profiles = ProfileHelper.find_profiles() - ls.update_select(map(lambda x: x[1], profiles)) - _login = self.Login(profiles) - ls.update_on_close(_login.login_screen_close) - ls.show() - app.exec_() - if not _login.t: - return - elif _login.t == 1: # create new profile - _login.name = _login.name.strip() - name = _login.name if _login.name else 'toxygen_user' - pr = map(lambda x: x[1], ProfileHelper.find_profiles()) - if name in list(pr): - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("MainWindow", "Error")) - text = (QtWidgets.QApplication.translate("MainWindow", - 'Profile with this name already exists')) - msgBox.setText(text) - msgBox.exec_() - return - self.tox = profile.tox_factory() - self.tox.self_set_name(bytes(_login.name, 'utf-8') if _login.name else b'Toxygen User') - self.tox.self_set_status_message(b'Toxing on Toxygen') - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", - 'Do you want to set profile password?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - set_pass = SetProfilePasswordScreen(encrypt_save) - set_pass.show() - self.app.lastWindowClosed.connect(self.app.quit) - self.app.exec_() - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", - 'Do you want to save profile in default folder? If no, profile will be saved in program folder'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - path = Settings.get_default_path() - else: - path = curr_directory() + '/' - try: - ProfileHelper(path, name).save_profile(self.tox.get_savedata()) - except Exception as ex: - print(str(ex)) - log('Profile creation exception: ' + str(ex)) - msgBox = QtWidgets.QMessageBox() - msgBox.setText(QtWidgets.QApplication.translate("login", - 'Profile saving error! Does Toxygen have permission to write to this directory?')) - msgBox.exec_() - return - path = Settings.get_default_path() - settings = Settings(name) - if curr_lang in langs: - settings['language'] = curr_lang - settings.save() - else: # load existing profile - path, name = _login.get_data() - if _login.default: - Settings.set_auto_profile(path, name) - data = ProfileHelper(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) - else: - path, name = auto_profile - data = ProfileHelper(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) - - if Settings.is_active_profile(path, name): # profile is in use - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply != QtWidgets.QMessageBox.Yes: - return - else: - settings.set_active_profile() - - # application color scheme - for theme in settings.built_in_themes().keys(): - if settings['theme'] == theme: - with open(curr_directory() + settings.built_in_themes()[theme]) as fl: - style = fl.read() - app.setStyleSheet(style) - - lang = Settings.supported_languages()[settings['language']] - translator = QtCore.QTranslator() - translator.load(curr_directory() + '/translations/' + lang) - app.installTranslator(translator) - app.translator = translator - - # tray icon - self.tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) - self.tray.setObjectName('tray') - - self.ms = MainWindow(self.tox, self.reset, self.tray) - app.aboutToQuit.connect(self.ms.close_window) - - class Menu(QtWidgets.QMenu): - - def newStatus(self, status): - if not Settings.get_instance().locked: - profile.Profile.get_instance().set_status(status) - self.aboutToShowHandler() - self.hide() - - def aboutToShowHandler(self): - status = profile.Profile.get_instance().status - act = self.act - if status is None or Settings.get_instance().locked: - self.actions()[1].setVisible(False) - else: - self.actions()[1].setVisible(True) - act.actions()[0].setChecked(False) - act.actions()[1].setChecked(False) - act.actions()[2].setChecked(False) - act.actions()[status].setChecked(True) - self.actions()[2].setVisible(not Settings.get_instance().locked) - - def languageChange(self, *args, **kwargs): - self.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) - self.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Set status')) - self.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Exit')) - self.act.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Online')) - self.act.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Away')) - self.act.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Busy')) - - m = Menu() - show = m.addAction(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) - sub = m.addMenu(QtWidgets.QApplication.translate('tray', 'Set status')) - onl = sub.addAction(QtWidgets.QApplication.translate('tray', 'Online')) - away = sub.addAction(QtWidgets.QApplication.translate('tray', 'Away')) - busy = sub.addAction(QtWidgets.QApplication.translate('tray', 'Busy')) - onl.setCheckable(True) - away.setCheckable(True) - busy.setCheckable(True) - m.act = sub - exit = m.addAction(QtWidgets.QApplication.translate('tray', 'Exit')) - - def show_window(): - s = Settings.get_instance() - - def show(): - if not self.ms.isActiveWindow(): - self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) - self.ms.activateWindow() - self.ms.show() - if not s.locked: - show() - else: - def correct_pass(): - show() - s.locked = False - s.unlockScreen = False - if not s.unlockScreen: - s.unlockScreen = True - self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass) - self.p.show() - - def tray_activated(reason): - if reason == QtWidgets.QSystemTrayIcon.DoubleClick: - show_window() - - def close_app(): - if not Settings.get_instance().locked: - settings.closing = True - self.ms.close() - - show.triggered.connect(show_window) - exit.triggered.connect(close_app) - m.aboutToShow.connect(lambda: m.aboutToShowHandler()) - onl.triggered.connect(lambda: m.newStatus(0)) - away.triggered.connect(lambda: m.newStatus(1)) - busy.triggered.connect(lambda: m.newStatus(2)) - - self.tray.setContextMenu(m) - self.tray.show() - self.tray.activated.connect(tray_activated) - - self.ms.show() - - updating = False - if settings['update'] and updater.updater_available() and updater.connection_available(): # auto update - version = updater.check_for_updates() - if version is not None: - if settings['update'] == 2: - updater.download(version) - updating = True - else: - reply = QtWidgets.QMessageBox.question(None, - 'Toxygen', - QtWidgets.QApplication.translate("login", - 'Update for Toxygen was found. Download and install it?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - updater.download(version) - updating = True - - if updating: - data = self.tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - settings.close() - del self.tox - return - - plugin_helper = PluginLoader(self.tox, settings) # plugin support - plugin_helper.load() - - start() - # init thread - self.init = self.InitThread(self.tox, self.ms, self.tray) - self.init.start() - - # starting threads for tox iterate and toxav iterate - self.mainloop = self.ToxIterateThread(self.tox) - self.mainloop.start() - self.avloop = self.ToxAVIterateThread(self.tox.AV) - self.avloop.start() - - if self.uri is not None: - self.ms.add_contact(self.uri) - - app.lastWindowClosed.connect(app.quit) - app.exec_() - - self.init.stop = True - self.mainloop.stop = True - self.avloop.stop = True - plugin_helper.stop() - stop() - self.mainloop.wait() - self.init.wait() - self.avloop.wait() - self.tray.hide() - data = self.tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - settings.close() - del self.tox - - def reset(self): - """ - Create new tox instance (new network settings) - :return: tox instance - """ - self.mainloop.stop = True - self.init.stop = True - self.avloop.stop = True - self.mainloop.wait() - self.init.wait() - self.avloop.wait() - data = self.tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - del self.tox - # create new tox instance - self.tox = profile.tox_factory(data, Settings.get_instance()) - # init thread - self.init = self.InitThread(self.tox, self.ms, self.tray) - self.init.start() - - # starting threads for tox iterate and toxav iterate - self.mainloop = self.ToxIterateThread(self.tox) - self.mainloop.start() - - self.avloop = self.ToxAVIterateThread(self.tox.AV) - self.avloop.start() - - plugin_helper = PluginLoader.get_instance() - plugin_helper.set_tox(self.tox) - - return self.tox - - # ----------------------------------------------------------------------------------------------------------------- - # Inner classes - # ----------------------------------------------------------------------------------------------------------------- - - class InitThread(QtCore.QThread): - - def __init__(self, tox, ms, tray): - QtCore.QThread.__init__(self) - self.tox, self.ms, self.tray = tox, ms, tray - self.stop = False - - def run(self): - # initializing callbacks - init_callbacks(self.tox, self.ms, self.tray) - # download list of nodes if needed - download_nodes_list() - # bootstrap - try: - for data in generate_nodes(): - if self.stop: - return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) - except: - pass - for _ in range(10): - if self.stop: - return - self.msleep(1000) - while not self.tox.self_get_connection_status(): - try: - for data in generate_nodes(): - if self.stop: - return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) - except: - pass - finally: - self.msleep(5000) - - class ToxIterateThread(QtCore.QThread): - - def __init__(self, tox): - QtCore.QThread.__init__(self) - self.tox = tox - self.stop = False - - def run(self): - while not self.stop: - self.tox.iterate() - self.msleep(self.tox.iteration_interval()) - - class ToxAVIterateThread(QtCore.QThread): - - def __init__(self, toxav): - QtCore.QThread.__init__(self) - self.toxav = toxav - self.stop = False - - def run(self): - while not self.stop: - self.toxav.iterate() - self.msleep(self.toxav.iteration_interval()) class Login: @@ -462,7 +39,12 @@ def reset(): def main(): - if len(sys.argv) == 1: + parser = argparse.ArgumentParser() + parser.add_argument('--version') + parser.add_argument('--clean') + parser.add_argument('--reset') + args = parser.parse_args() + if not len(args): toxygen = Toxygen() else: # started with argument(s) arg = sys.argv[1] diff --git a/toxygen/messenger/__init__.py b/toxygen/messenger/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/messages.py b/toxygen/messenger/messages.py similarity index 100% rename from toxygen/messages.py rename to toxygen/messenger/messages.py diff --git a/toxygen/network/__init__.py b/toxygen/network/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/tox_dns.py b/toxygen/network/tox_dns.py similarity index 98% rename from toxygen/tox_dns.py rename to toxygen/network/tox_dns.py index 26b9619..36702ee 100644 --- a/toxygen/tox_dns.py +++ b/toxygen/network/tox_dns.py @@ -1,7 +1,7 @@ import json import urllib.request from util import log -import settings +from user_data import settings from PyQt5 import QtNetwork, QtCore diff --git a/toxygen/plugin_support/__init__.py b/toxygen/plugin_support/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/plugin_support.py b/toxygen/plugin_support/plugin_support.py similarity index 99% rename from toxygen/plugin_support.py rename to toxygen/plugin_support/plugin_support.py index 0ff7421..de39e5f 100644 --- a/toxygen/plugin_support.py +++ b/toxygen/plugin_support/plugin_support.py @@ -1,10 +1,10 @@ import util -import profile +from contacts import profile import os import importlib import inspect import plugins.plugin_super_class as pl -import toxes +from user_data import toxes import sys diff --git a/toxygen/smileys.py b/toxygen/smileys_and_stickers.py similarity index 100% rename from toxygen/smileys.py rename to toxygen/smileys_and_stickers.py diff --git a/toxygen/threads.py b/toxygen/threads.py new file mode 100644 index 0000000..dae8800 --- /dev/null +++ b/toxygen/threads.py @@ -0,0 +1,62 @@ +class InitThread(QtCore.QThread): + + def __init__(self, tox, ms, tray): + QtCore.QThread.__init__(self) + self.tox, self.ms, self.tray = tox, ms, tray + self.stop = False + + def run(self): + # initializing callbacks + init_callbacks(self.tox, self.ms, self.tray) + # download list of nodes if needed + download_nodes_list() + # bootstrap + try: + for data in generate_nodes(): + if self.stop: + return + self.tox.bootstrap(*data) + self.tox.add_tcp_relay(*data) + except: + pass + for _ in range(10): + if self.stop: + return + self.msleep(1000) + while not self.tox.self_get_connection_status(): + try: + for data in generate_nodes(): + if self.stop: + return + self.tox.bootstrap(*data) + self.tox.add_tcp_relay(*data) + except: + pass + finally: + self.msleep(5000) + + +class ToxIterateThread(QtCore.QThread): + + def __init__(self, tox): + QtCore.QThread.__init__(self) + self.tox = tox + self.stop = False + + def run(self): + while not self.stop: + self.tox.iterate() + self.msleep(self.tox.iteration_interval()) + + +class ToxAVIterateThread(QtCore.QThread): + + def __init__(self, toxav): + QtCore.QThread.__init__(self) + self.toxav = toxav + self.stop = False + + def run(self): + while not self.stop: + self.toxav.iterate() + self.msleep(self.toxav.iteration_interval()) diff --git a/toxygen/toxcore_enums_and_consts.py b/toxygen/toxcore_enums_and_consts.py deleted file mode 100644 index a17d93e..0000000 --- a/toxygen/toxcore_enums_and_consts.py +++ /dev/null @@ -1,220 +0,0 @@ -TOX_USER_STATUS = { - 'NONE': 0, - 'AWAY': 1, - 'BUSY': 2, -} - -TOX_MESSAGE_TYPE = { - 'NORMAL': 0, - 'ACTION': 1, -} - -TOX_PROXY_TYPE = { - 'NONE': 0, - 'HTTP': 1, - 'SOCKS5': 2, -} - -TOX_SAVEDATA_TYPE = { - 'NONE': 0, - 'TOX_SAVE': 1, - 'SECRET_KEY': 2, -} - -TOX_ERR_OPTIONS_NEW = { - 'OK': 0, - 'MALLOC': 1, -} - -TOX_ERR_NEW = { - 'OK': 0, - 'NULL': 1, - 'MALLOC': 2, - 'PORT_ALLOC': 3, - 'PROXY_BAD_TYPE': 4, - 'PROXY_BAD_HOST': 5, - 'PROXY_BAD_PORT': 6, - 'PROXY_NOT_FOUND': 7, - 'LOAD_ENCRYPTED': 8, - 'LOAD_BAD_FORMAT': 9, -} - -TOX_ERR_BOOTSTRAP = { - 'OK': 0, - 'NULL': 1, - 'BAD_HOST': 2, - 'BAD_PORT': 3, -} - -TOX_CONNECTION = { - 'NONE': 0, - 'TCP': 1, - 'UDP': 2, -} - -TOX_ERR_SET_INFO = { - 'OK': 0, - 'NULL': 1, - 'TOO_LONG': 2, -} - -TOX_ERR_FRIEND_ADD = { - 'OK': 0, - 'NULL': 1, - 'TOO_LONG': 2, - 'NO_MESSAGE': 3, - 'OWN_KEY': 4, - 'ALREADY_SENT': 5, - 'BAD_CHECKSUM': 6, - 'SET_NEW_NOSPAM': 7, - 'MALLOC': 8, -} - -TOX_ERR_FRIEND_DELETE = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_BY_PUBLIC_KEY = { - 'OK': 0, - 'NULL': 1, - 'NOT_FOUND': 2, -} - -TOX_ERR_FRIEND_GET_PUBLIC_KEY = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_GET_LAST_ONLINE = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_QUERY = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, -} - -TOX_ERR_SET_TYPING = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_SEND_MESSAGE = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'SENDQ': 4, - 'TOO_LONG': 5, - 'EMPTY': 6, -} - -TOX_FILE_KIND = { - 'DATA': 0, - 'AVATAR': 1, -} - -TOX_FILE_CONTROL = { - 'RESUME': 0, - 'PAUSE': 1, - 'CANCEL': 2, -} - -TOX_ERR_FILE_CONTROL = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, - 'FRIEND_NOT_CONNECTED': 2, - 'NOT_FOUND': 3, - 'NOT_PAUSED': 4, - 'DENIED': 5, - 'ALREADY_PAUSED': 6, - 'SENDQ': 7, -} - -TOX_ERR_FILE_SEEK = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, - 'FRIEND_NOT_CONNECTED': 2, - 'NOT_FOUND': 3, - 'DENIED': 4, - 'INVALID_POSITION': 5, - 'SENDQ': 6, -} - -TOX_ERR_FILE_GET = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'NOT_FOUND': 3, -} - -TOX_ERR_FILE_SEND = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'NAME_TOO_LONG': 4, - 'TOO_MANY': 5, -} - -TOX_ERR_FILE_SEND_CHUNK = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'NOT_FOUND': 4, - 'NOT_TRANSFERRING': 5, - 'INVALID_LENGTH': 6, - 'SENDQ': 7, - 'WRONG_POSITION': 8, -} - -TOX_ERR_FRIEND_CUSTOM_PACKET = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'INVALID': 4, - 'EMPTY': 5, - 'TOO_LONG': 6, - 'SENDQ': 7, -} - -TOX_ERR_GET_PORT = { - 'OK': 0, - 'NOT_BOUND': 1, -} - -TOX_CHAT_CHANGE = { - 'PEER_ADD': 0, - 'PEER_DEL': 1, - 'PEER_NAME': 2 -} - -TOX_GROUPCHAT_TYPE = { - 'TEXT': 0, - 'AV': 1 -} - -TOX_PUBLIC_KEY_SIZE = 32 - -TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6 - -TOX_MAX_FRIEND_REQUEST_LENGTH = 1016 - -TOX_MAX_MESSAGE_LENGTH = 1372 - -TOX_MAX_NAME_LENGTH = 128 - -TOX_MAX_STATUS_MESSAGE_LENGTH = 1007 - -TOX_SECRET_KEY_SIZE = 32 - -TOX_FILE_ID_LENGTH = 32 - -TOX_HASH_LENGTH = 32 - -TOX_MAX_CUSTOM_PACKET_SIZE = 1373 diff --git a/toxygen/tray.py b/toxygen/tray.py new file mode 100644 index 0000000..ffe9aeb --- /dev/null +++ b/toxygen/tray.py @@ -0,0 +1,83 @@ +self.tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) + self.tray.setObjectName('tray') + + class Menu(QtWidgets.QMenu): + + def newStatus(self, status): + if not Settings.get_instance().locked: + profile.Profile.get_instance().set_status(status) + self.aboutToShowHandler() + self.hide() + + def aboutToShowHandler(self): + status = profile.Profile.get_instance().status + act = self.act + if status is None or Settings.get_instance().locked: + self.actions()[1].setVisible(False) + else: + self.actions()[1].setVisible(True) + act.actions()[0].setChecked(False) + act.actions()[1].setChecked(False) + act.actions()[2].setChecked(False) + act.actions()[status].setChecked(True) + self.actions()[2].setVisible(not Settings.get_instance().locked) + + def languageChange(self, *args, **kwargs): + self.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) + self.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Set status')) + self.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Exit')) + self.act.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Online')) + self.act.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Away')) + self.act.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Busy')) + + m = Menu() + show = m.addAction(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) + sub = m.addMenu(QtWidgets.QApplication.translate('tray', 'Set status')) + onl = sub.addAction(QtWidgets.QApplication.translate('tray', 'Online')) + away = sub.addAction(QtWidgets.QApplication.translate('tray', 'Away')) + busy = sub.addAction(QtWidgets.QApplication.translate('tray', 'Busy')) + onl.setCheckable(True) + away.setCheckable(True) + busy.setCheckable(True) + m.act = sub + exit = m.addAction(QtWidgets.QApplication.translate('tray', 'Exit')) + + def show_window(): + s = Settings.get_instance() + + def show(): + if not self.ms.isActiveWindow(): + self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + self.ms.activateWindow() + self.ms.show() + if not s.locked: + show() + else: + def correct_pass(): + show() + s.locked = False + s.unlockScreen = False + if not s.unlockScreen: + s.unlockScreen = True + self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass) + self.p.show() + + def tray_activated(reason): + if reason == QtWidgets.QSystemTrayIcon.DoubleClick: + show_window() + + def close_app(): + if not Settings.get_instance().locked: + settings.closing = True + self.ms.close() + + show.triggered.connect(show_window) + exit.triggered.connect(close_app) + m.aboutToShow.connect(lambda: m.aboutToShowHandler()) + onl.triggered.connect(lambda: m.newStatus(0)) + away.triggered.connect(lambda: m.newStatus(1)) + busy.triggered.connect(lambda: m.newStatus(2)) + + self.tray.setContextMenu(m) + self.tray.show() + self.tray.activated.connect(tray_activated) \ No newline at end of file diff --git a/toxygen/ui/__init__.py b/toxygen/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/avwidgets.py b/toxygen/ui/avwidgets.py similarity index 98% rename from toxygen/avwidgets.py rename to toxygen/ui/avwidgets.py index 8c81387..67bd04a 100644 --- a/toxygen/avwidgets.py +++ b/toxygen/ui/avwidgets.py @@ -1,10 +1,10 @@ from PyQt5 import QtCore, QtGui, QtWidgets -import widgets -import profile +from ui import widgets +from contacts import profile import util import pyaudio import wave -import settings +from user_data import settings from util import curr_directory diff --git a/toxygen/items_factory.py b/toxygen/ui/items_factory.py similarity index 97% rename from toxygen/items_factory.py rename to toxygen/ui/items_factory.py index 44a00ad..cdf127a 100644 --- a/toxygen/items_factory.py +++ b/toxygen/ui/items_factory.py @@ -1,5 +1,4 @@ -from PyQt5 import QtWidgets, QtCore -from list_items import * +from ui.list_items import * class ItemsFactory: diff --git a/toxygen/list_items.py b/toxygen/ui/list_items.py similarity index 99% rename from toxygen/list_items.py rename to toxygen/ui/list_items.py index 9b92f2a..0e4991e 100644 --- a/toxygen/list_items.py +++ b/toxygen/ui/list_items.py @@ -1,12 +1,12 @@ -from toxcore_enums_and_consts import * +from wrapper.toxcore_enums_and_consts import * from PyQt5 import QtCore, QtGui, QtWidgets -import profile +from contacts import profile from file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR from util import curr_directory, convert_time, curr_time -from widgets import DataLabel, create_menu +from ui.widgets import DataLabel, create_menu import html as h import smileys -import settings +from user_data import settings import re @@ -60,7 +60,7 @@ class MessageEdit(QtWidgets.QTextBrowser): def quote_text(self): text = self.textCursor().selection().toPlainText() if text: - import mainscreen + from ui import mainscreen window = mainscreen.MainWindow.get_instance() text = '>' + '\n>'.join(text.split('\n')) if window.messageEdit.toPlainText(): @@ -70,7 +70,7 @@ class MessageEdit(QtWidgets.QTextBrowser): def on_anchor_clicked(self, url): text = str(url.toString()) if text.startswith('tox:'): - import menu + from ui import menu self.add_contact = menu.AddContact(text[4:]) self.add_contact.show() else: diff --git a/toxygen/loginscreen.py b/toxygen/ui/loginscreen.py similarity index 98% rename from toxygen/loginscreen.py rename to toxygen/ui/loginscreen.py index 77aa5ba..cc1f45c 100644 --- a/toxygen/loginscreen.py +++ b/toxygen/ui/loginscreen.py @@ -1,5 +1,4 @@ -from PyQt5 import QtWidgets, QtCore -from widgets import * +from ui.widgets import * class NickEdit(LineEdit): diff --git a/toxygen/mainscreen.py b/toxygen/ui/mainscreen.py similarity index 99% rename from toxygen/mainscreen.py rename to toxygen/ui/mainscreen.py index c76f19b..ce828b0 100644 --- a/toxygen/mainscreen.py +++ b/toxygen/ui/mainscreen.py @@ -1,11 +1,10 @@ -from menu import * -from profile import * -from list_items import * -from widgets import MultilineEdit, ComboBox +from ui.menu import * +from contacts.profile import * +from ui.list_items import * +from ui.widgets import MultilineEdit, ComboBox import plugin_support -from mainscreen_widgets import * -import settings -import toxes +from ui.mainscreen_widgets import * +from user_data import toxes, settings class MainWindow(QtWidgets.QMainWindow, Singleton): diff --git a/toxygen/mainscreen_widgets.py b/toxygen/ui/mainscreen_widgets.py similarity index 99% rename from toxygen/mainscreen_widgets.py rename to toxygen/ui/mainscreen_widgets.py index 9955771..bbf6c5a 100644 --- a/toxygen/mainscreen_widgets.py +++ b/toxygen/ui/mainscreen_widgets.py @@ -1,6 +1,6 @@ from PyQt5 import QtCore, QtGui, QtWidgets -from widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit -from profile import Profile +from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit +from contacts.profile import Profile import smileys import util @@ -333,7 +333,7 @@ class WelcomeScreen(CenteredWidget): QtCore.QTimer.singleShot(1000, self.show) def not_show(self): - import settings + from user_data import settings s = settings.Settings.get_instance() s['show_welcome_screen'] = False s.save() diff --git a/toxygen/menu.py b/toxygen/ui/menu.py similarity index 99% rename from toxygen/menu.py rename to toxygen/ui/menu.py index 17f4e17..606c545 100644 --- a/toxygen/menu.py +++ b/toxygen/ui/menu.py @@ -1,10 +1,10 @@ from PyQt5 import QtCore, QtGui, QtWidgets -from settings import * -from profile import Profile +from user_data.settings import * +from contacts.profile import Profile from util import curr_directory, copy -from widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow +from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio -import toxes +from user_data import toxes import plugin_support import updater @@ -160,7 +160,7 @@ class ProfileSettings(CenteredWidget): self.default = QtWidgets.QPushButton(self) self.default.setGeometry(QtCore.QRect(40, 550, 620, 30)) path, name = Settings.get_auto_profile() - self.auto = path + name == ProfileHelper.get_path() + Settings.get_instance().name + self.auto = path + name == ProfileManager.get_path() + Settings.get_instance().name self.default.clicked.connect(self.auto_profile) self.retranslateUi() if profile.status is not None: @@ -199,7 +199,7 @@ class ProfileSettings(CenteredWidget): if self.auto: Settings.reset_auto_profile() else: - Settings.set_auto_profile(ProfileHelper.get_path(), Settings.get_instance().name) + Settings.set_auto_profile(ProfileManager.get_path(), Settings.get_instance().name) self.auto = not self.auto if self.auto: self.default.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as not default profile")) @@ -275,7 +275,7 @@ class ProfileSettings(CenteredWidget): settings.export(directory) profile = Profile.get_instance() profile.export_db(directory) - ProfileHelper.get_instance().export_profile(directory, reply == QtWidgets.QMessageBox.Yes) + ProfileManager.get_instance().export_profile(directory, reply == QtWidgets.QMessageBox.Yes) def closeEvent(self, event): profile = Profile.get_instance() diff --git a/toxygen/passwordscreen.py b/toxygen/ui/passwordscreen.py similarity index 99% rename from toxygen/passwordscreen.py rename to toxygen/ui/passwordscreen.py index ca721e5..9d3d523 100644 --- a/toxygen/passwordscreen.py +++ b/toxygen/ui/passwordscreen.py @@ -1,4 +1,4 @@ -from widgets import CenteredWidget, LineEdit +from ui.widgets import CenteredWidget, LineEdit from PyQt5 import QtCore, QtWidgets diff --git a/toxygen/widgets.py b/toxygen/ui/widgets.py similarity index 100% rename from toxygen/widgets.py rename to toxygen/ui/widgets.py diff --git a/toxygen/updater/__init__.py b/toxygen/updater/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/updater.py b/toxygen/updater/updater.py similarity index 99% rename from toxygen/updater.py rename to toxygen/updater/updater.py index 762892a..af80624 100644 --- a/toxygen/updater.py +++ b/toxygen/updater/updater.py @@ -1,6 +1,6 @@ import util import os -import settings +from user_data import settings import platform import urllib from PyQt5 import QtNetwork, QtCore diff --git a/toxygen/user_data/__init__.py b/toxygen/user_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py new file mode 100644 index 0000000..96f591c --- /dev/null +++ b/toxygen/user_data/profile_manager.py @@ -0,0 +1,70 @@ +class ProfileManager(): + """ + Class with methods for search, load and save profiles + """ + def __init__(self, path, name): + path = append_slash(path) + self._path = path + name + '.tox' + self._directory = path + # create /avatars if not exists: + directory = path + 'avatars' + if not os.path.exists(directory): + os.makedirs(directory) + + def open_profile(self): + with open(self._path, 'rb') as fl: + data = fl.read() + if data: + return data + else: + raise IOError('Save file has zero size!') + + def get_dir(self): + return self._directory + + def save_profile(self, data): + inst = ToxES.get_instance() + if inst.has_password(): + data = inst.pass_encrypt(data) + with open(self._path, 'wb') as fl: + fl.write(data) + print('Profile saved successfully') + + def export_profile(self, new_path, use_new_path): + path = new_path + os.path.basename(self._path) + with open(self._path, 'rb') as fin: + data = fin.read() + with open(path, 'wb') as fout: + fout.write(data) + print('Profile exported successfully') + copy(self._directory + 'avatars', new_path + 'avatars') + if use_new_path: + self._path = new_path + os.path.basename(self._path) + self._directory = new_path + Settings.get_instance().update_path() + + @staticmethod + def find_profiles(): + """ + Find available tox profiles + """ + path = Settings.get_default_path() + result = [] + # check default path + if not os.path.exists(path): + os.makedirs(path) + for fl in os.listdir(path): + if fl.endswith('.tox'): + name = fl[:-4] + result.append((path, name)) + path = curr_directory() + # check current directory + for fl in os.listdir(path): + if fl.endswith('.tox'): + name = fl[:-4] + result.append((path + '/', name)) + return result + + @staticmethod + def get_path(): + return ProfileManager.get_instance().get_dir() diff --git a/toxygen/settings.py b/toxygen/user_data/settings.py similarity index 72% rename from toxygen/settings.py rename to toxygen/user_data/settings.py index 431688a..45d41bd 100644 --- a/toxygen/settings.py +++ b/toxygen/user_data/settings.py @@ -3,7 +3,7 @@ import json import os from util import Singleton, curr_directory, log, copy, append_slash import pyaudio -from toxes import ToxES +from user_data.toxes import ToxES import smileys @@ -14,7 +14,7 @@ class Settings(dict, Singleton): def __init__(self, name): Singleton.__init__(self) - self.path = ProfileHelper.get_path() + str(name) + '.json' + self.path = ProfileManager.get_path() + str(name) + '.json' self.name = name if os.path.isfile(self.path): with open(self.path, 'rb') as fl: @@ -184,7 +184,7 @@ class Settings(dict, Singleton): fl.write(text) def close(self): - profile_path = ProfileHelper.get_path() + profile_path = ProfileManager.get_path() path = str(profile_path + str(self.name) + '.lock') if os.path.isfile(path): os.remove(path) @@ -193,7 +193,7 @@ class Settings(dict, Singleton): """ Mark current profile as active """ - profile_path = ProfileHelper.get_path() + profile_path = ProfileManager.get_path() path = str(profile_path + str(self.name) + '.lock') with open(path, 'w') as fl: fl.write('active') @@ -204,7 +204,7 @@ class Settings(dict, Singleton): fl.write(text) def update_path(self): - self.path = ProfileHelper.get_path() + self.name + '.json' + self.path = ProfileManager.get_path() + self.name + '.json' @staticmethod def get_global_settings_path(): @@ -219,75 +219,3 @@ class Settings(dict, Singleton): else: return os.getenv('HOME') + '/.config/tox/' - -class ProfileHelper(Singleton): - """ - Class with methods for search, load and save profiles - """ - def __init__(self, path, name): - Singleton.__init__(self) - path = append_slash(path) - self._path = path + name + '.tox' - self._directory = path - # create /avatars if not exists: - directory = path + 'avatars' - if not os.path.exists(directory): - os.makedirs(directory) - - def open_profile(self): - with open(self._path, 'rb') as fl: - data = fl.read() - if data: - return data - else: - raise IOError('Save file has zero size!') - - def get_dir(self): - return self._directory - - def save_profile(self, data): - inst = ToxES.get_instance() - if inst.has_password(): - data = inst.pass_encrypt(data) - with open(self._path, 'wb') as fl: - fl.write(data) - print('Profile saved successfully') - - def export_profile(self, new_path, use_new_path): - path = new_path + os.path.basename(self._path) - with open(self._path, 'rb') as fin: - data = fin.read() - with open(path, 'wb') as fout: - fout.write(data) - print('Profile exported successfully') - copy(self._directory + 'avatars', new_path + 'avatars') - if use_new_path: - self._path = new_path + os.path.basename(self._path) - self._directory = new_path - Settings.get_instance().update_path() - - @staticmethod - def find_profiles(): - """ - Find available tox profiles - """ - path = Settings.get_default_path() - result = [] - # check default path - if not os.path.exists(path): - os.makedirs(path) - for fl in os.listdir(path): - if fl.endswith('.tox'): - name = fl[:-4] - result.append((path, name)) - path = curr_directory() - # check current directory - for fl in os.listdir(path): - if fl.endswith('.tox'): - name = fl[:-4] - result.append((path + '/', name)) - return result - - @staticmethod - def get_path(): - return ProfileHelper.get_instance().get_dir() diff --git a/toxygen/toxes.py b/toxygen/user_data/toxes.py similarity index 77% rename from toxygen/toxes.py rename to toxygen/user_data/toxes.py index 5b7282f..5a22f61 100644 --- a/toxygen/toxes.py +++ b/toxygen/user_data/toxes.py @@ -1,12 +1,8 @@ -import util -import toxencryptsave +class ToxES: -class ToxES(util.Singleton): - - def __init__(self): - super().__init__() - self._toxencryptsave = toxencryptsave.ToxEncryptSave() + def __init__(self, toxencryptsave): + self._toxencryptsave = toxencryptsave self._passphrase = None def set_password(self, passphrase): diff --git a/toxygen/util/__init__.py b/toxygen/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/util.py b/toxygen/util/util.py similarity index 98% rename from toxygen/util.py rename to toxygen/util/util.py index e8d702a..bb27da6 100644 --- a/toxygen/util.py +++ b/toxygen/util/util.py @@ -5,7 +5,7 @@ import sys import re -program_version = '0.4.1' +program_version = '0.5.0' def cached(func): diff --git a/toxygen/wrapper/__init__.py b/toxygen/wrapper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/libtox.py b/toxygen/wrapper/libtox.py similarity index 100% rename from toxygen/libtox.py rename to toxygen/wrapper/libtox.py diff --git a/toxygen/tox.py b/toxygen/wrapper/tox.py similarity index 68% rename from toxygen/tox.py rename to toxygen/wrapper/tox.py index ef4e44c..c6f5912 100644 --- a/toxygen/tox.py +++ b/toxygen/wrapper/tox.py @@ -1,7 +1,7 @@ from ctypes import * -from toxcore_enums_and_consts import * -from toxav import ToxAV -from libtox import LibToxCore +from wrapper.toxcore_enums_and_consts import * +from wrapper.toxav import ToxAV +from wrapper.libtox import LibToxCore class ToxOptions(Structure): @@ -1514,88 +1514,842 @@ class Tox: elif tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']: raise RuntimeError('The instance was not bound to any port.') - # ----------------------------------------------------------------------------------------------------------------- - # Group chats +# ----------------------------------------------------------------------------------------------------------------- + # Group chat instance management # ----------------------------------------------------------------------------------------------------------------- - def del_groupchat(self, groupnumber): - result = Tox.libtoxcore.tox_del_groupchat(self._tox_pointer, c_int(groupnumber), None) + def group_new(self, privacy_state, group_name): + """ + Creates a new group chat. + This function creates a new group chat object and adds it to the chats array. + The client should initiate its peer list with self info after calling this function, as + the peer_join callback will not be triggered. + :param privacy_state: The privacy state of the group. If this is set to TOX_GROUP_PRIVACY_STATE_PUBLIC, + the group will attempt to announce itself to the DHT and anyone with the Chat ID may join. + Otherwise a friend invite will be required to join the group. + :param group_name: The name of the group. The name must be non-NULL. + :return group number on success, UINT32_MAX on failure. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_new(self._tox_pointer, privacy_state, group_name, + len(group_name), byref(error)) return result - def group_peername(self, groupnumber, peernumber): - buffer = create_string_buffer(TOX_MAX_NAME_LENGTH) - result = Tox.libtoxcore.tox_group_peername(self._tox_pointer, c_int(groupnumber), c_int(peernumber), - buffer, None) - return str(buffer[:result], 'utf-8') + def group_join(self, chat_id, password): + """ + Joins a group chat with specified Chat ID. + This function creates a new group chat object, adds it to the chats array, and sends + a DHT announcement to find peers in the group associated with chat_id. Once a peer has been + found a join attempt will be initiated. + :param chat_id: The Chat ID of the group you wish to join. This must be TOX_GROUP_CHAT_ID_SIZE bytes. + :param password: The password required to join the group. Set to NULL if no password is required. + :return groupnumber on success, UINT32_MAX on failure. + """ - def invite_friend(self, friendnumber, groupnumber): - result = Tox.libtoxcore.tox_invite_friend(self._tox_pointer, c_int(friendnumber), - c_int(groupnumber), None) + error = c_int() + result = Tox.libtoxcore.tox_group_join(self._tox_pointer, string_to_bin(chat_id), + password, + len(password) if password is not None else 0, + byref(error)) return result - def join_groupchat(self, friendnumber, data): - result = Tox.libtoxcore.tox_join_groupchat(self._tox_pointer, - c_int(friendnumber), c_char_p(data), c_uint16(len(data)), None) + def group_reconnect(self, groupnumber): + """ + Reconnects to a group. + This function disconnects from all peers in the group, then attempts to reconnect with the group. + The caller's state is not changed (i.e. name, status, role, chat public key etc.) + :param groupnumber: The group number of the group we wish to reconnect to. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_reconnect(self._tox_pointer, groupnumber, byref(error)) return result - def group_message_send(self, groupnumber, message): - result = Tox.libtoxcore.tox_group_message_send(self._tox_pointer, c_int(groupnumber), c_char_p(message), - c_uint16(len(message)), None) + def group_leave(self, groupnumber, message): + """ + Leaves a group. + This function sends a parting packet containing a custom (non-obligatory) message to all + peers in a group, and deletes the group from the chat array. All group state information is permanently + lost, including keys and role credentials. + :param groupnumber: The group number of the group we wish to leave. + :param message: The parting message to be sent to all the peers. Set to NULL if we do not wish to + send a parting message. + :return True if the group chat instance was successfully deleted. + """ + + error = c_int() + f = Tox.libtoxcore.tox_group_leave + f.restype = c_bool + result = f(self._tox_pointer, groupnumber, message, + len(message) if message is not None else 0, byref(error)) return result - def group_action_send(self, groupnumber, action): - result = Tox.libtoxcore.tox_group_action_send(self._tox_pointer, - c_int(groupnumber), c_char_p(action), - c_uint16(len(action)), None) + # ----------------------------------------------------------------------------------------------------------------- + # Group user-visible client information (nickname/status/role/public key) + # ----------------------------------------------------------------------------------------------------------------- + + def group_self_set_name(self, groupnumber, name): + """ + Set the client's nickname for the group instance designated by the given group number. + Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length is equal to zero or name is a NULL + pointer, the function call will fail. + :param name: A byte array containing the new nickname. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_set_name(self._tox_pointer, groupnumber, name, len(name), byref(error)) return result - def group_set_title(self, groupnumber, title): - result = Tox.libtoxcore.tox_group_set_title(self._tox_pointer, c_int(groupnumber), - c_char_p(title), c_uint8(len(title)), None) + def group_self_get_name_size(self, groupnumber): + """ + Return the length of the client's current nickname for the group instance designated + by groupnumber as passed to tox_group_self_set_name. + If no nickname was set before calling this function, the name is empty, + and this function returns 0. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_name_size(self._tox_pointer, groupnumber, byref(error)) return result - def group_get_title(self, groupnumber): - buffer = create_string_buffer(TOX_MAX_NAME_LENGTH) - result = Tox.libtoxcore.tox_group_get_title(self._tox_pointer, - c_int(groupnumber), buffer, - c_uint32(TOX_MAX_NAME_LENGTH), None) - return str(buffer[:result], 'utf-8') + def group_self_get_name(self, groupnumber): + """ + Write the nickname set by tox_group_self_set_name to a byte array. + If no nickname was set before calling this function, the name is empty, + and this function has no effect. + Call tox_group_self_get_name_size to find out how much memory to allocate for the result. + :return nickname + """ - def group_number_peers(self, groupnumber): - result = Tox.libtoxcore.tox_group_number_peers(self._tox_pointer, c_int(groupnumber), None) + error = c_int() + size = self.group_self_get_name_size(groupnumber) + name = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_self_get_name(self._tox_pointer, groupnumber, name, byref(error)) + return str(name[:size], 'utf-8') + + def group_self_set_status(self, groupnumber, status): + + """ + Set the client's status for the group instance. Status must be a TOX_USER_STATUS. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_set_status(self._tox_pointer, groupnumber, status, byref(error)) return result - def add_av_groupchat(self): - result = self.AV.libtoxav.toxav_add_av_groupchat(self._tox_pointer, None, None) + def group_self_get_status(self, groupnumber): + """ + returns the client's status for the group instance on success. + return value is unspecified on failure. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_status(self._tox_pointer, groupnumber, byref(error)) return result - def join_av_groupchat(self, friendnumber, data): - result = self.AV.libtoxav.toxav_join_av_groupchat(self._tox_pointer, c_int32(friendnumber), - c_char_p(data), c_uint16(len(data)), - None, None) + def group_self_get_role(self, groupnumber): + """ + returns the client's role for the group instance on success. + return value is unspecified on failure. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_role(self._tox_pointer, groupnumber, byref(error)) return result - def callback_group_invite(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int32, c_uint8, POINTER(c_uint8), c_uint16, c_void_p) - self.group_invite_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, self.group_invite_cb, user_data) + def group_self_get_peer_id(self, groupnumber): + """ + returns the client's peer id for the group instance on success. + return value is unspecified on failure. + """ - def callback_group_message(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_char_p, c_uint16, c_void_p) + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_peer_id(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_self_get_public_key(self, groupnumber): + """ + Write the client's group public key designated by the given group number to a byte array. + This key will be permanently tied to the client's identity for this particular group until + the client explicitly leaves the group or gets kicked/banned. This key is the only way for + other peers to reliably identify the client across client restarts. + `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. + :return public key + """ + + error = c_int() + key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + result = Tox.libtoxcore.tox_group_self_get_public_key(self._tox_pointer, groupnumber, + key, byref(error)) + return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + + # ----------------------------------------------------------------------------------------------------------------- + # Peer-specific group state queries. + # ----------------------------------------------------------------------------------------------------------------- + + def group_peer_get_name_size(self, groupnumber, peer_id): + """ + Return the length of the peer's name. If the group number or ID is invalid, the + return value is unspecified. + The return value is equal to the `length` argument received by the last + `group_peer_name` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_peer_get_name_size(self._tox_pointer, groupnumber, peer_id, byref(error)) + return result + + def group_peer_get_name(self, groupnumber, peer_id): + """ + Write the name of the peer designated by the given ID to a byte + array. + Call tox_group_peer_get_name_size to determine the allocation size for the `name` parameter. + The data written to `name` is equal to the data received by the last + `group_peer_name` callback. + :param groupnumber: The group number of the group we wish to query. + :param peer_id: The ID of the peer whose name we want to retrieve. + :return name. + """ + error = c_int() + size = self.group_peer_get_name_size(groupnumber, peer_id) + name = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_peer_get_name(self._tox_pointer, groupnumber, peer_id, name, byref(error)) + return str(name[:], 'utf-8') + + def group_peer_get_status(self, groupnumber, peer_id): + """ + Return the peer's user status (away/busy/...). If the ID or group number is + invalid, the return value is unspecified. + The status returned is equal to the last status received through the + `group_peer_status` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_peer_get_status(self._tox_pointer, groupnumber, peer_id, byref(error)) + return result + + def group_peer_get_role(self, groupnumber, peer_id): + """ + Return the peer's role (user/moderator/founder...). If the ID or group number is + invalid, the return value is unspecified. + The role returned is equal to the last role received through the + `group_moderation` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_peer_get_role(self._tox_pointer, groupnumber, peer_id, byref(error)) + return result + + def group_peer_get_public_key(self, groupnumber, peer_id): + """ + Write the group public key with the designated peer_id for the designated group number to public_key. + This key will be parmanently tied to a particular peer until they explicitly leave the group or + get kicked/banned, and is the only way to reliably identify the same peer across client restarts. + `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. + :return public key + """ + + error = c_int() + key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + result = Tox.libtoxcore.tox_group_peer_get_public_key(self._tox_pointer, groupnumber, peer_id, + key, byref(error)) + return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + + def callback_group_peer_name(self, callback, user_data): + """ + Set the callback for the `group_peer_name` event. Pass NULL to unset. + This event is triggered when a peer changes their nickname. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_peer_name_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_name(self._tox_pointer, self.group_peer_name_cb, user_data) + + def callback_group_peer_status(self, callback, user_data): + """ + Set the callback for the `group_peer_status` event. Pass NULL to unset. + This event is triggered when a peer changes their status. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p) + self.group_peer_status_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_status(self._tox_pointer, self.group_peer_status_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat state queries and events. + # ----------------------------------------------------------------------------------------------------------------- + + def group_set_topic(self, groupnumber, topic): + """ + Set the group topic and broadcast it to the rest of the group. + topic length cannot be longer than TOX_GROUP_MAX_TOPIC_LENGTH. If length is equal to zero or + topic is set to NULL, the topic will be unset. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_set_topic(self._tox_pointer, groupnumber, topic, len(topic), byref(error)) + return result + + def group_get_topic_size(self, groupnumber): + """ + Return the length of the group topic. If the group number is invalid, the + return value is unspecified. + The return value is equal to the `length` argument received by the last + `group_topic` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_topic_size(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_topic(self, groupnumber): + """ + Write the topic designated by the given group number to a byte array. + Call tox_group_get_topic_size to determine the allocation size for the `topic` parameter. + The data written to `topic` is equal to the data received by the last + `group_topic` callback. + :return topic + """ + + error = c_int() + size = self.group_get_topic_size(groupnumber) + topic = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_get_topic(self._tox_pointer, groupnumber, topic, byref(error)) + return str(topic[:size], 'utf-8') + + def group_get_name_size(self, groupnumber): + """ + Return the length of the group name. If the group number is invalid, the + return value is unspecified. + """ + error = c_int() + result = Tox.libtoxcore.tox_group_get_name_size(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_name(self, groupnumber): + """ + Write the name of the group designated by the given group number to a byte array. + Call tox_group_get_name_size to determine the allocation size for the `name` parameter. + :return true on success. + """ + + error = c_int() + size = self.group_get_name_size(groupnumber) + name = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_get_name(self._tox_pointer, groupnumber, + name, byref(error)) + return str(name[:size], 'utf-8') + + def group_get_chat_id(self, groupnumber): + """ + Write the Chat ID designated by the given group number to a byte array. + `chat_id` should have room for at least TOX_GROUP_CHAT_ID_SIZE bytes. + :return chat id. + """ + + error = c_int() + buff = create_string_buffer(TOX_GROUP_CHAT_ID_SIZE) + result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer, groupnumber, + buff, byref(error)) + return bin_to_string(buff, TOX_GROUP_CHAT_ID_SIZE) + + def group_get_number_groups(self): + """ + Return the number of groups in the Tox chats array. + """ + + result = Tox.libtoxcore.tox_group_get_number_groups(self._tox_pointer) + return result + + def group_get_privacy_state(self, groupnumber): + """ + Return the privacy state of the group designated by the given group number. If group number + is invalid, the return value is unspecified. + The value returned is equal to the data received by the last + `group_privacy_state` callback. + see the `Group chat founder controls` section for the respective set function. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_privacy_state(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_peer_limit(self, groupnumber): + """ + Return the maximum number of peers allowed for the group designated by the given group number. + If the group number is invalid, the return value is unspecified. + The value returned is equal to the data received by the last + `group_peer_limit` callback. + see the `Group chat founder controls` section for the respective set function. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_peer_limit(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_password_size(self, groupnumber): + """ + Return the length of the group password. If the group number is invalid, the + return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_password_size(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_get_password(self, groupnumber): + """ + Write the password for the group designated by the given group number to a byte array. + Call tox_group_get_password_size to determine the allocation size for the `password` parameter. + The data received is equal to the data received by the last + `group_password` callback. + see the `Group chat founder controls` section for the respective set function. + :return password + """ + + error = c_int() + size = self.group_get_password_size(groupnumber) + password = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_get_password(self._tox_pointer, groupnumber, + password, byref(error)) + return str(password[:size], 'utf-8') + + def callback_group_topic(self, callback, user_data): + """ + Set the callback for the `group_topic` event. Pass NULL to unset. + This event is triggered when a peer changes the group topic. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_topic_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_topic(self._tox_pointer, self.group_topic_cb, user_data) + + def callback_group_privacy_state(self, callback, user_data): + """ + Set the callback for the `group_privacy_state` event. Pass NULL to unset. + This event is triggered when the group founder changes the privacy state. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) + self.group_privacy_state_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_privacy_state(self._tox_pointer, self.group_privacy_state_cb, user_data) + + def callback_group_peer_limit(self, callback, user_data): + """ + Set the callback for the `group_peer_limit` event. Pass NULL to unset. + This event is triggered when the group founder changes the maximum peer limit. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) + self.group_peer_limit_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_limit(self._tox_pointer, self.group_peer_limit_cb, user_data) + + def callback_group_password(self, callback, user_data): + """ + Set the callback for the `group_password` event. Pass NULL to unset. + This event is triggered when the group founder changes the group password. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_password_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_password(self._tox_pointer, self.group_password_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group message sending + # ----------------------------------------------------------------------------------------------------------------- + + def group_send_custom_packet(self, groupnumber, lossless, data): + """ + Send a custom packet to the group. + If lossless is true the packet will be lossless. Lossless packet behaviour is comparable + to TCP (reliability, arrive in order) but with packets instead of a stream. + If lossless is false, the packet will be lossy. Lossy packets behave like UDP packets, + meaning they might never reach the other side or might arrive more than once (if someone + is messing with the connection) or might arrive in the wrong order. + Unless latency is an issue or message reliability is not important, it is recommended that you use + lossless custom packets. + :param groupnumber: The group number of the group the message is intended for. + :param lossless: True if the packet should be lossless. + :param data A byte array containing the packet data. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_send_custom_packet(self._tox_pointer, groupnumber, lossless, data, + len(data), byref(error)) + return result + + def group_send_private_message(self, groupnumber, peer_id, message): + """ + Send a text chat message to the specified peer in the specified group. + This function creates a group private message packet and pushes it into the send + queue. + The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages + must be split by the client and sent as separate messages. Other clients can + then reassemble the fragments. Messages may not be empty. + :param groupnumber: The group number of the group the message is intended for. + :param peer_id: The ID of the peer the message is intended for. + :param message: A non-NULL pointer to the first element of a byte array containing the message text. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_send_private_message(self._tox_pointer, groupnumber, peer_id, message, + len(message), byref(error)) + return result + + def group_send_message(self, groupnumber, type, message): + """ + Send a text chat message to the group. + This function creates a group message packet and pushes it into the send + queue. + The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages + must be split by the client and sent as separate messages. Other clients can + then reassemble the fragments. Messages may not be empty. + :param groupnumber: The group number of the group the message is intended for. + :param type: Message type (normal, action, ...). + :param message: A non-NULL pointer to the first element of a byte array containing the message text. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_send_message(self._tox_pointer, groupnumber, type, message, len(message), + byref(error)) + return result + + # ----------------------------------------------------------------------------------------------------------------- + # Group message receiving + # ----------------------------------------------------------------------------------------------------------------- + + def callback_group_message(self, callback, user_data): + """ + Set the callback for the `group_message` event. Pass NULL to unset. + This event is triggered when the client receives a group message. + Callback: python function with params: + tox Tox* instance + groupnumber The group number of the group the message is intended for. + peer_id The ID of the peer who sent the message. + type The type of message (normal, action, ...). + message The message data. + length The length of the message. + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_char_p, c_size_t, c_void_p) self.group_message_cb = c_callback(callback) Tox.libtoxcore.tox_callback_group_message(self._tox_pointer, self.group_message_cb, user_data) - def callback_group_action(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_char_p, c_uint16, c_void_p) - self.group_action_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_action(self._tox_pointer, self.group_action_cb, user_data) + def callback_group_private_message(self, callback, user_data): + """ + Set the callback for the `group_private_message` event. Pass NULL to unset. + This event is triggered when the client receives a private message. + """ - def callback_group_title(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_char_p, c_uint8, c_void_p) - self.group_title_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_title(self._tox_pointer, self.group_title_cb, user_data) + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_private_message_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_private_message(self._tox_pointer, self.group_private_message_cb, user_data) - def callback_group_namelist_change(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_uint8, c_void_p) - self.group_namelist_change_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_namelist_change(self._tox_pointer, self.group_namelist_change_cb, user_data) + def callback_group_custom_packet(self, callback, user_data): + """ + Set the callback for the `group_custom_packet` event. Pass NULL to unset. + This event is triggered when the client receives a custom packet. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, POINTER(c_uint8), c_void_p) + self.group_custom_packet_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_custom_packet(self._tox_pointer, self.group_custom_packet_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat inviting and join/part events + # ----------------------------------------------------------------------------------------------------------------- + + def group_invite_friend(self, groupnumber, friend_number): + """ + Invite a friend to a group. + This function creates an invite request packet and pushes it to the send queue. + :param groupnumber: The group number of the group the message is intended for. + :param friend_number: The friend number of the friend the invite is intended for. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, groupnumber, friend_number, byref(error)) + return result + + def group_invite_accept(self, invite_data, friend_number, password=None): + """ + Accept an invite to a group chat that the client previously received from a friend. The invite + is only valid while the inviter is present in the group. + :param invite_data: The invite data received from the `group_invite` event. + :param password: The password required to join the group. Set to NULL if no password is required. + :return the groupnumber on success, UINT32_MAX on failure. + """ + + error = c_int() + f = Tox.libtoxcore.tox_group_invite_accept + f.restype = c_uint32 + result = f(self._tox_pointer, friend_number, invite_data, len(invite_data), password, + len(password) if password is not None else 0, byref(error)) + print('Invite accept. Result:', result, 'Error:', error.value) + return result + + def callback_group_invite(self, callback, user_data): + """ + Set the callback for the `group_invite` event. Pass NULL to unset. + This event is triggered when the client receives a group invite from a friend. The client must store + invite_data which is used to join the group via tox_group_invite_accept. + Callback: python function with params: + tox - Tox* + friend_number The friend number of the contact who sent the invite. + invite_data The invite data. + length The length of invite_data. + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) + self.group_invite_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, self.group_invite_cb, user_data) + + def callback_group_peer_join(self, callback, user_data): + """ + Set the callback for the `group_peer_join` event. Pass NULL to unset. + This event is triggered when a peer other than self joins the group. + Callback: python function with params: + tox - Tox* + group_number - group number + peer_id - peer id + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) + self.group_peer_join_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_join(self._tox_pointer, self.group_peer_join_cb, user_data) + + def callback_group_peer_exit(self, callback, user_data): + """ + Set the callback for the `group_peer_exit` event. Pass NULL to unset. + This event is triggered when a peer other than self exits the group. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_peer_exit_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_exit(self._tox_pointer, self.group_peer_exit_cb, user_data) + + def callback_group_self_join(self, callback, user_data): + """ + Set the callback for the `group_self_join` event. Pass NULL to unset. + This event is triggered when the client has successfully joined a group. Use this to initialize + any group information the client may need. + Callback: python fucntion with params: + tox - *Tox + group_number - group number + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_void_p) + self.group_self_join_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_self_join(self._tox_pointer, self.group_self_join_cb, user_data) + + def callback_group_join_fail(self, callback, user_data): + """ + Set the callback for the `group_join_fail` event. Pass NULL to unset. + This event is triggered when the client fails to join a group. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) + self.group_join_fail_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_join_fail(self._tox_pointer, self.group_join_fail_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat founder controls (these only work for the group founder) + # ----------------------------------------------------------------------------------------------------------------- + + def group_founder_set_password(self, groupnumber, password): + """ + Set or unset the group password. + This function sets the groups password, creates a new group shared state including the change, + and distributes it to the rest of the group. + :param groupnumber: The group number of the group for which we wish to set the password. + :param password: The password we want to set. Set password to NULL to unset the password. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_founder_set_password(self._tox_pointer, groupnumber, password, + len(password), byref(error)) + return result + + def group_founder_set_privacy_state(self, groupnumber, privacy_state): + """ + Set the group privacy state. + This function sets the group's privacy state, creates a new group shared state + including the change, and distributes it to the rest of the group. + If an attempt is made to set the privacy state to the same state that the group is already + in, the function call will be successful and no action will be taken. + :param groupnumber: The group number of the group for which we wish to change the privacy state. + :param privacy_state: The privacy state we wish to set the group to. + :return true on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_founder_set_privacy_state(self._tox_pointer, groupnumber, privacy_state, + byref(error)) + return result + + def group_founder_set_peer_limit(self, groupnumber, max_peers): + """ + Set the group peer limit. + This function sets a limit for the number of peers who may be in the group, creates a new + group shared state including the change, and distributes it to the rest of the group. + :param groupnumber: The group number of the group for which we wish to set the peer limit. + :param max_peers: The maximum number of peers to allow in the group. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_founder_set_peer_limit(self._tox_pointer, groupnumber, + max_peers, byref(error)) + return result + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat moderation + # ----------------------------------------------------------------------------------------------------------------- + + def group_toggle_ignore(self, groupnumber, peer_id, ignore): + """ + Ignore or unignore a peer. + :param groupnumber: The group number of the group the in which you wish to ignore a peer. + :param peer_id: The ID of the peer who shall be ignored or unignored. + :param ignore: True to ignore the peer, false to unignore the peer. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_toggle_ignore(self._tox_pointer, groupnumber, peer_id, ignore, byref(error)) + return result + + def group_mod_set_role(self, groupnumber, peer_id, role): + """ + Set a peer's role. + This function will first remove the peer's previous role and then assign them a new role. + It will also send a packet to the rest of the group, requesting that they perform + the role reassignment. Note: peers cannot be set to the founder role. + :param groupnumber: The group number of the group the in which you wish set the peer's role. + :param peer_id: The ID of the peer whose role you wish to set. + :param role: The role you wish to set the peer to. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_set_role(self._tox_pointer, groupnumber, peer_id, role, byref(error)) + return result + + def group_mod_remove_peer(self, groupnumber, peer_id, set_ban): + """ + Kick/ban a peer. + This function will remove a peer from the caller's peer list and optionally add their IP address + to the ban list. It will also send a packet to all group members requesting them + to do the same. + :param groupnumber: The group number of the group the ban is intended for. + :param peer_id: The ID of the peer who will be kicked and/or added to the ban list. + :param set_ban: Set to true if a ban shall be set on the peer's IP address. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_remove_peer(self._tox_pointer, groupnumber, peer_id, + set_ban, byref(error)) + return result + + def group_mod_remove_ban(self, groupnumber, ban_id): + """ + Removes a ban. + This function removes a ban entry from the ban list, and sends a packet to the rest of + the group requesting that they do the same. + :param groupnumber: The group number of the group in which the ban is to be removed. + :param ban_id: The ID of the ban entry that shall be removed. + :return True on success + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_remove_ban(self._tox_pointer, groupnumber, ban_id, byref(error)) + return result + + def callback_group_moderation(self, callback, user_data): + """ + Set the callback for the `group_moderation` event. Pass NULL to unset. + This event is triggered when a moderator or founder executes a moderation event. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_int, c_void_p) + self.group_moderation_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_moderation(self._tox_pointer, self.group_moderation_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat ban list queries + # ----------------------------------------------------------------------------------------------------------------- + + def group_ban_get_list_size(self, groupnumber): + """ + Return the number of entries in the ban list for the group designated by + the given group number. If the group number is invalid, the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_list_size(self._tox_pointer, groupnumber, byref(error)) + return result + + def group_ban_get_list(self, groupnumber): + """ + Copy a list of valid ban list ID's into an array. + Call tox_group_ban_get_list_size to determine the number of elements to allocate. + return true on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_list(self._tox_pointer, groupnumber, POINTER(c_uint32)( + create_string_buffer(sizeof(c_uint32) * self.group_ban_get_list_size(groupnumber)), byref(error))) + return result + + def group_ban_get_name_size(self, groupnumber, ban_id): + """ + Return the length of the name for the ban list entry designated by ban_id, in the + group designated by the given group number. If either groupnumber or ban_id is invalid, + the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_name_size(self._tox_pointer, groupnumber, ban_id, byref(error)) + return result + + def group_ban_get_name(self, groupnumber, ban_id): + """ + Write the name of the ban entry designated by ban_id in the group designated by the + given group number to a byte array. + Call tox_group_ban_get_name_size to find out how much memory to allocate for the result. + :return name + """ + + error = c_int() + size = self.group_ban_get_name_size(groupnumber, ban_id) + name = create_string_buffer() + + result = Tox.libtoxcore.tox_group_ban_get_name(self._tox_pointer, groupnumber, ban_id, + name, byref(error)) + return str(name[:size], 'utf-8') + + def group_ban_get_time_set(self, groupnumber, ban_id): + """ + Return a time stamp indicating the time the ban was set, for the ban list entry + designated by ban_id, in the group designated by the given group number. + If either groupnumber or ban_id is invalid, the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_time_set(self._tox_pointer, groupnumber, ban_id, byref(error)) + return result diff --git a/toxygen/toxav.py b/toxygen/wrapper/toxav.py similarity index 99% rename from toxygen/toxav.py rename to toxygen/wrapper/toxav.py index 0ab891c..78ab11f 100644 --- a/toxygen/toxav.py +++ b/toxygen/wrapper/toxav.py @@ -1,7 +1,7 @@ from ctypes import c_int, POINTER, c_void_p, byref, ArgumentError, c_uint32, CFUNCTYPE, c_size_t, c_uint8, c_uint16 from ctypes import c_char_p, c_int32, c_bool, cast -from libtox import LibToxAV -from toxav_enums import * +from wrapper.libtox import LibToxAV +from wrapper.toxav_enums import * class ToxAV: diff --git a/toxygen/toxav_enums.py b/toxygen/wrapper/toxav_enums.py similarity index 100% rename from toxygen/toxav_enums.py rename to toxygen/wrapper/toxav_enums.py diff --git a/toxygen/wrapper/toxcore_enums_and_consts.py b/toxygen/wrapper/toxcore_enums_and_consts.py new file mode 100644 index 0000000..6942d3a --- /dev/null +++ b/toxygen/wrapper/toxcore_enums_and_consts.py @@ -0,0 +1,944 @@ +TOX_USER_STATUS = { + 'NONE': 0, + 'AWAY': 1, + 'BUSY': 2, +} + +TOX_MESSAGE_TYPE = { + 'NORMAL': 0, + 'ACTION': 1, +} + +TOX_PROXY_TYPE = { + 'NONE': 0, + 'HTTP': 1, + 'SOCKS5': 2, +} + +TOX_SAVEDATA_TYPE = { + 'NONE': 0, + 'TOX_SAVE': 1, + 'SECRET_KEY': 2, +} + +TOX_ERR_OPTIONS_NEW = { + 'OK': 0, + 'MALLOC': 1, +} + +TOX_ERR_NEW = { + 'OK': 0, + 'NULL': 1, + 'MALLOC': 2, + 'PORT_ALLOC': 3, + 'PROXY_BAD_TYPE': 4, + 'PROXY_BAD_HOST': 5, + 'PROXY_BAD_PORT': 6, + 'PROXY_NOT_FOUND': 7, + 'LOAD_ENCRYPTED': 8, + 'LOAD_BAD_FORMAT': 9, +} + +TOX_ERR_BOOTSTRAP = { + 'OK': 0, + 'NULL': 1, + 'BAD_HOST': 2, + 'BAD_PORT': 3, +} + +TOX_CONNECTION = { + 'NONE': 0, + 'TCP': 1, + 'UDP': 2, +} + +TOX_ERR_SET_INFO = { + 'OK': 0, + 'NULL': 1, + 'TOO_LONG': 2, +} + +TOX_ERR_FRIEND_ADD = { + 'OK': 0, + 'NULL': 1, + 'TOO_LONG': 2, + 'NO_MESSAGE': 3, + 'OWN_KEY': 4, + 'ALREADY_SENT': 5, + 'BAD_CHECKSUM': 6, + 'SET_NEW_NOSPAM': 7, + 'MALLOC': 8, +} + +TOX_ERR_FRIEND_DELETE = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_BY_PUBLIC_KEY = { + 'OK': 0, + 'NULL': 1, + 'NOT_FOUND': 2, +} + +TOX_ERR_FRIEND_GET_PUBLIC_KEY = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_GET_LAST_ONLINE = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_QUERY = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, +} + +TOX_ERR_SET_TYPING = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_SEND_MESSAGE = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'SENDQ': 4, + 'TOO_LONG': 5, + 'EMPTY': 6, +} + +TOX_FILE_KIND = { + 'DATA': 0, + 'AVATAR': 1, +} + +TOX_FILE_CONTROL = { + 'RESUME': 0, + 'PAUSE': 1, + 'CANCEL': 2, +} + +TOX_ERR_FILE_CONTROL = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, + 'FRIEND_NOT_CONNECTED': 2, + 'NOT_FOUND': 3, + 'NOT_PAUSED': 4, + 'DENIED': 5, + 'ALREADY_PAUSED': 6, + 'SENDQ': 7, +} + +TOX_ERR_FILE_SEEK = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, + 'FRIEND_NOT_CONNECTED': 2, + 'NOT_FOUND': 3, + 'DENIED': 4, + 'INVALID_POSITION': 5, + 'SENDQ': 6, +} + +TOX_ERR_FILE_GET = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'NOT_FOUND': 3, +} + +TOX_ERR_FILE_SEND = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'NAME_TOO_LONG': 4, + 'TOO_MANY': 5, +} + +TOX_ERR_FILE_SEND_CHUNK = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'NOT_FOUND': 4, + 'NOT_TRANSFERRING': 5, + 'INVALID_LENGTH': 6, + 'SENDQ': 7, + 'WRONG_POSITION': 8, +} + +TOX_ERR_FRIEND_CUSTOM_PACKET = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'INVALID': 4, + 'EMPTY': 5, + 'TOO_LONG': 6, + 'SENDQ': 7, +} + +TOX_ERR_GET_PORT = { + 'OK': 0, + 'NOT_BOUND': 1, +} + +TOX_GROUP_PRIVACY_STATE = { + + # + # The group is considered to be public. Anyone may join the group using the Chat ID. + # + # If the group is in this state, even if the Chat ID is never explicitly shared + # with someone outside of the group, information including the Chat ID, IP addresses, + # and peer ID's (but not Tox ID's) is visible to anyone with access to a node + # storing a DHT entry for the given group. + # + 'TOX_GROUP_PRIVACY_STATE_PUBLIC': 0, + + # + # The group is considered to be private. The only way to join the group is by having + # someone in your contact list send you an invite. + # + # If the group is in this state, no group information (mentioned above) is present in the DHT; + # the DHT is not used for any purpose at all. If a public group is set to private, + # all DHT information related to the group will expire shortly. + # + 'TOX_GROUP_PRIVACY_STATE_PRIVATE': 1 +} + +TOX_GROUP_ROLE = { + + # + # May kick and ban all other peers as well as set their role to anything (except founder). + # Founders may also set the group password, toggle the privacy state, and set the peer limit. + # + 'TOX_GROUP_ROLE_FOUNDER': 0, + + # + # May kick, ban and set the user and observer roles for peers below this role. + # May also set the group topic. + # + 'TOX_GROUP_ROLE_MODERATOR': 1, + + # + # May communicate with other peers normally. + # + 'TOX_GROUP_ROLE_USER': 2, + + # + # May observe the group and ignore peers; may not communicate with other peers or with the group. + # + 'TOX_GROUP_ROLE_OBSERVER': 3 +} + +TOX_ERR_GROUP_NEW = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_NEW_OK': 0, + + # + # The group name exceeded TOX_GROUP_MAX_GROUP_NAME_LENGTH. + # + 'TOX_ERR_GROUP_NEW_TOO_LONG': 1, + + # + # group_name is NULL or length is zero. + # + 'TOX_ERR_GROUP_NEW_EMPTY': 2, + + # + # TOX_GROUP_PRIVACY_STATE is an invalid type. + # + 'TOX_ERR_GROUP_NEW_PRIVACY': 3, + + # + # The group instance failed to initialize. + # + 'TOX_ERR_GROUP_NEW_INIT': 4, + + # + # The group state failed to initialize. This usually indicates that something went wrong + # related to cryptographic signing. + # + 'TOX_ERR_GROUP_NEW_STATE': 5, + + # + # The group failed to announce to the DHT. This indicates a network related error. + # + 'TOX_ERR_GROUP_NEW_ANNOUNCE': 6, +} + +TOX_ERR_GROUP_JOIN = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_JOIN_OK': 0, + + # + # The group instance failed to initialize. + # + 'TOX_ERR_GROUP_JOIN_INIT': 1, + + # + # The chat_id pointer is set to NULL or a group with chat_id already exists. This usually + # happens if the client attempts to create multiple sessions for the same group. + # + 'TOX_ERR_GROUP_JOIN_BAD_CHAT_ID': 2, + + # + # Password length exceeded TOX_GROUP_MAX_PASSWORD_SIZE. + # + 'TOX_ERR_GROUP_JOIN_TOO_LONG': 3, +} + +TOX_ERR_GROUP_RECONNECT = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_RECONNECT_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND': 1, +} + +TOX_ERR_GROUP_LEAVE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_LEAVE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_LEAVE_GROUP_NOT_FOUND': 1, + + # + # Message length exceeded 'TOX_GROUP_MAX_PART_LENGTH. + # + 'TOX_ERR_GROUP_LEAVE_TOO_LONG': 2, + + # + # The parting packet failed to send. + # + 'TOX_ERR_GROUP_LEAVE_FAIL_SEND': 3, + + # + # The group chat instance failed to be deleted. This may occur due to memory related errors. + # + 'TOX_ERR_GROUP_LEAVE_DELETE_FAIL': 4, +} + +TOX_ERR_GROUP_SELF_QUERY = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SELF_QUERY_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND': 1, +} + + +TOX_ERR_GROUP_SELF_NAME_SET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_GROUP_NOT_FOUND': 1, + + # + # Name length exceeded 'TOX_MAX_NAME_LENGTH. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_TOO_LONG': 2, + + # + # The length given to the set function is zero or name is a NULL pointer. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_INVALID': 3, + + # + # The name is already taken by another peer in the group. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_TAKEN': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_SELF_STATUS_SET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_GROUP_NOT_FOUND': 1, + + # + # An invalid type was passed to the set function. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_INVALID': 2, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_FAIL_SEND': 3 +} + +TOX_ERR_GROUP_PEER_QUERY = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_PEER_QUERY_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND': 2 +} + +TOX_ERR_GROUP_STATE_QUERIES = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_STATE_QUERIES_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND': 1 +} + + +TOX_ERR_GROUP_TOPIC_SET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_TOPIC_SET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_TOPIC_SET_GROUP_NOT_FOUND': 1, + + # + # Topic length exceeded 'TOX_GROUP_MAX_TOPIC_LENGTH. + # + 'TOX_ERR_GROUP_TOPIC_SET_TOO_LONG': 2, + + # + # The caller does not have the required permissions to set the topic. + # + 'TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS': 3, + + # + # The packet could not be created. This error is usually related to cryptographic signing. + # + 'TOX_ERR_GROUP_TOPIC_SET_FAIL_CREATE': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_TOPIC_SET_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_SEND_MESSAGE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_GROUP_NOT_FOUND': 1, + + # + # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_TOO_LONG': 2, + + # + # The message pointer is null or length is zero. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_EMPTY': 3, + + # + # The message type is invalid. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_BAD_TYPE': 4, + + # + # The caller does not have the required permissions to send group messages. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS': 5, + + # + # Packet failed to send. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_FAIL_SEND': 6 +} + +TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PEER_NOT_FOUND': 2, + + # + # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_TOO_LONG': 3, + + # + # The message pointer is null or length is zero. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY': 4, + + # + # The caller does not have the required permissions to send group messages. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS': 5, + + # + # Packet failed to send. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_FAIL_SEND': 6 +} + +TOX_ERR_GROUP_SEND_CUSTOM_PACKET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_GROUP_NOT_FOUND': 1, + + # + # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_TOO_LONG': 2, + + # + # The message pointer is null or length is zero. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_EMPTY': 3, + + # + # The caller does not have the required permissions to send group messages. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_PERMISSIONS': 4 +} + +TOX_ERR_GROUP_INVITE_FRIEND = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_GROUP_NOT_FOUND': 1, + + # + # The friend number passed did not designate a valid friend. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_FRIEND_NOT_FOUND': 2, + + # + # Creation of the invite packet failed. This indicates a network related error. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_INVITE_FAIL': 3, + + # + # Packet failed to send. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_FAIL_SEND': 4 +} + +TOX_ERR_GROUP_INVITE_ACCEPT = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_OK': 0, + + # + # The invite data is not in the expected format. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_BAD_INVITE': 1, + + # + # The group instance failed to initialize. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_INIT_FAILED': 2, + + # + # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG': 3 +} + +TOX_GROUP_JOIN_FAIL = { + + # + # You are using the same nickname as someone who is already in the group. + # + 'TOX_GROUP_JOIN_FAIL_NAME_TAKEN': 0, + + # + # The group peer limit has been reached. + # + 'TOX_GROUP_JOIN_FAIL_PEER_LIMIT': 1, + + # + # You have supplied an invalid password. + # + 'TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD': 2, + + # + # The join attempt failed due to an unspecified error. This often occurs when the group is + # not found in the DHT. + # + 'TOX_GROUP_JOIN_FAIL_UNKNOWN': 3 +} + +TOX_ERR_GROUP_FOUNDER_SET_PASSWORD = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_GROUP_NOT_FOUND': 1, + + # + # The caller does not have the required permissions to set the password. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS': 2, + + # + # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG': 3, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_FAIL_SEND': 4 +} + +TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_GROUP_NOT_FOUND': 1, + + # + # 'TOX_GROUP_PRIVACY_STATE is an invalid type. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_INVALID': 2, + + # + # The caller does not have the required permissions to set the privacy state. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS': 3, + + # + # The privacy state could not be set. This may occur due to an error related to + # cryptographic signing of the new shared state. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SET': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_GROUP_NOT_FOUND': 1, + + # + # The caller does not have the required permissions to set the peer limit. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS': 2, + + # + # The peer limit could not be set. This may occur due to an error related to + # cryptographic signing of the new shared state. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SET': 3, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SEND': 4 +} + +TOX_ERR_GROUP_TOGGLE_IGNORE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_TOGGLE_IGNORE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_TOGGLE_IGNORE_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_TOGGLE_IGNORE_PEER_NOT_FOUND': 2 +} + +TOX_ERR_GROUP_MOD_SET_ROLE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. Note: you cannot set your own role. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND': 2, + + # + # The caller does not have the required permissions for this action. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS': 3, + + # + # The role assignment is invalid. This will occur if you try to set a peer's role to + # the role they already have. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT': 4, + + # + # The role was not successfully set. This may occur if something goes wrong with role setting': , + # or if the packet fails to send. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_FAIL_ACTION': 5 +} + +TOX_ERR_GROUP_MOD_REMOVE_PEER = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_PEER_NOT_FOUND': 2, + + # + # The caller does not have the required permissions for this action. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS': 3, + + # + # The peer could not be removed from the group. + # + # If a ban was set': , this error indicates that the ban entry could not be created. + # This is usually due to the peer's IP address already occurring in the ban list. It may also + # be due to the entry containing invalid peer information': , or a failure to cryptographically + # authenticate the entry. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_ACTION': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_MOD_REMOVE_BAN = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_GROUP_NOT_FOUND': 1, + + # + # The caller does not have the required permissions for this action. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS': 2, + + # + # The ban entry could not be removed. This may occur if ban_id does not designate + # a valid ban entry. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION': 3, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_SEND': 4 +} + +TOX_GROUP_MOD_EVENT = { + + # + # A peer has been kicked from the group. + # + 'TOX_GROUP_MOD_EVENT_KICK': 0, + + # + # A peer has been banned from the group. + # + 'TOX_GROUP_MOD_EVENT_BAN': 1, + + # + # A peer as been given the observer role. + # + 'TOX_GROUP_MOD_EVENT_OBSERVER': 2, + + # + # A peer has been given the user role. + # + 'TOX_GROUP_MOD_EVENT_USER': 3, + + # + # A peer has been given the moderator role. + # + 'TOX_GROUP_MOD_EVENT_MODERATOR': 4, +} + +TOX_ERR_GROUP_BAN_QUERY = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_BAN_QUERY_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND': 1, + + # + # The ban_id does not designate a valid ban list entry. + # + 'TOX_ERR_GROUP_BAN_QUERY_BAD_ID': 2, +} + +TOX_PUBLIC_KEY_SIZE = 32 + +TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6 + +TOX_MAX_FRIEND_REQUEST_LENGTH = 1016 + +TOX_MAX_MESSAGE_LENGTH = 1372 + +TOX_GROUP_MAX_TOPIC_LENGTH = 512 + +TOX_GROUP_MAX_PART_LENGTH = 128 + +TOX_GROUP_MAX_GROUP_NAME_LENGTH = 48 + +TOX_GROUP_MAX_PASSWORD_SIZE = 32 + +TOX_GROUP_CHAT_ID_SIZE = 32 + +TOX_GROUP_PEER_PUBLIC_KEY_SIZE = 32 + +TOX_MAX_NAME_LENGTH = 128 + +TOX_MAX_STATUS_MESSAGE_LENGTH = 1007 + +TOX_SECRET_KEY_SIZE = 32 + +TOX_FILE_ID_LENGTH = 32 + +TOX_HASH_LENGTH = 32 + +TOX_MAX_CUSTOM_PACKET_SIZE = 1373 diff --git a/toxygen/toxencryptsave.py b/toxygen/wrapper/toxencryptsave.py similarity index 97% rename from toxygen/toxencryptsave.py rename to toxygen/wrapper/toxencryptsave.py index b579e21..31de085 100644 --- a/toxygen/toxencryptsave.py +++ b/toxygen/wrapper/toxencryptsave.py @@ -1,6 +1,6 @@ -import libtox +from wrapper import libtox from ctypes import c_size_t, create_string_buffer, byref, c_int, ArgumentError, c_char_p, c_bool -from toxencryptsave_enums_and_consts import * +from wrapper.toxencryptsave_enums_and_consts import * class ToxEncryptSave: diff --git a/toxygen/toxencryptsave_enums_and_consts.py b/toxygen/wrapper/toxencryptsave_enums_and_consts.py similarity index 100% rename from toxygen/toxencryptsave_enums_and_consts.py rename to toxygen/wrapper/toxencryptsave_enums_and_consts.py From 593e25efe51fab25a3745c2105bee0e692904dd7 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 14 Feb 2018 20:36:59 +0300 Subject: [PATCH 002/217] more project structure updates --- toxygen/communication/__init__.py | 0 toxygen/{ => communication}/callbacks.py | 67 +------- toxygen/communication/tox_factory.py | 27 ++++ toxygen/contacts/contact.py | 2 +- toxygen/contacts/profile.py | 153 +----------------- toxygen/file_tansfers/__init__.py | 0 toxygen/{ => file_tansfers}/file_transfers.py | 0 .../file_tansfers/file_transfers_handler.py | 0 toxygen/login.py | 19 +++ toxygen/main.py | 21 --- toxygen/threads.py | 65 ++++++++ toxygen/ui/list_items.py | 2 +- toxygen/user_data/toxes.py | 20 +-- 13 files changed, 125 insertions(+), 251 deletions(-) create mode 100644 toxygen/communication/__init__.py rename toxygen/{ => communication}/callbacks.py (90%) create mode 100644 toxygen/communication/tox_factory.py create mode 100644 toxygen/file_tansfers/__init__.py rename toxygen/{ => file_tansfers}/file_transfers.py (100%) create mode 100644 toxygen/file_tansfers/file_transfers_handler.py diff --git a/toxygen/communication/__init__.py b/toxygen/communication/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/callbacks.py b/toxygen/communication/callbacks.py similarity index 90% rename from toxygen/callbacks.py rename to toxygen/communication/callbacks.py index ba2994d..0e7345c 100644 --- a/toxygen/callbacks.py +++ b/toxygen/communication/callbacks.py @@ -12,72 +12,7 @@ import util import cv2 import numpy as np -# ----------------------------------------------------------------------------------------------------------------- -# Threads -# ----------------------------------------------------------------------------------------------------------------- - - -class InvokeEvent(QtCore.QEvent): - EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType()) - - def __init__(self, fn, *args, **kwargs): - QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE) - self.fn = fn - self.args = args - self.kwargs = kwargs - - -class Invoker(QtCore.QObject): - - def event(self, event): - event.fn(*event.args, **event.kwargs) - return True - - -_invoker = Invoker() - - -def invoke_in_main_thread(fn, *args, **kwargs): - QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs)) - - -class FileTransfersThread(threading.Thread): - - def __init__(self): - self._queue = queue.Queue() - self._timeout = 0.01 - self._continue = True - super().__init__() - - def execute(self, function, *args, **kwargs): - self._queue.put((function, args, kwargs)) - - def stop(self): - self._continue = False - - def run(self): - while self._continue: - try: - function, args, kwargs = self._queue.get(timeout=self._timeout) - function(*args, **kwargs) - except queue.Empty: - pass - except queue.Full: - util.log('Queue is Full in _thread') - except Exception as ex: - util.log('Exception in _thread: ' + str(ex)) - - -_thread = FileTransfersThread() - - -def start(): - _thread.start() - - -def stop(): - _thread.stop() - _thread.join() +# TODO: use closures # ----------------------------------------------------------------------------------------------------------------- # Callbacks - current user diff --git a/toxygen/communication/tox_factory.py b/toxygen/communication/tox_factory.py new file mode 100644 index 0000000..a39bb15 --- /dev/null +++ b/toxygen/communication/tox_factory.py @@ -0,0 +1,27 @@ + + +def tox_factory(data=None, settings=None): + """ + :param data: user data from .tox file. None = no saved data, create new profile + :param settings: current profile settings. None = default settings will be used + :return: new tox instance + """ + if settings is None: + settings = Settings.get_default_settings() + tox_options = Tox.options_new() + tox_options.contents.udp_enabled = settings['udp_enabled'] + tox_options.contents.proxy_type = settings['proxy_type'] + tox_options.contents.proxy_host = bytes(settings['proxy_host'], 'UTF-8') + tox_options.contents.proxy_port = settings['proxy_port'] + tox_options.contents.start_port = settings['start_port'] + tox_options.contents.end_port = settings['end_port'] + tox_options.contents.tcp_port = settings['tcp_port'] + if data: # load existing profile + tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['TOX_SAVE'] + tox_options.contents.savedata_data = c_char_p(data) + tox_options.contents.savedata_length = len(data) + else: # create new profile + tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['NONE'] + tox_options.contents.savedata_data = None + tox_options.contents.savedata_length = 0 + return Tox(tox_options) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index ad491fd..4b221e3 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -2,7 +2,7 @@ from db.history import * from contacts import basecontact import util from messenger.messages import * -import file_transfers as ft +from file_tansfers import file_transfers as ft import re diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index f74d134..9c585c2 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -7,7 +7,7 @@ from ctypes import * from util import log, Singleton, curr_directory from network.tox_dns import tox_dns from db.history import * -from file_transfers import * +from file_tansfers.file_transfers import * import time from av import calls import plugin_support @@ -1299,154 +1299,3 @@ class Profile(basecontact.BaseContact, Singleton): if friend_number == self.get_active_number(): self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) self._messages.scrollToBottom() - - # ----------------------------------------------------------------------------------------------------------------- - # GC support - # ----------------------------------------------------------------------------------------------------------------- - - def is_active_a_friend(self): - return type(self.get_curr_friend()) is Friend - - def get_group_by_number(self, number): - groups = filter(lambda x: type(x) is GroupChat and x.number == number, self._contacts) - return list(groups)[0] - - def add_gc(self, number): - widget = self.create_friend_item() - gc = GroupChat('Group chat #' + str(number), '', widget, self._tox, number) - self._contacts.append(gc) - - def create_group_chat(self): - number = self._tox.add_av_groupchat() - self.add_gc(number) - - def leave_gc(self, num): - gc = self._contacts[num] - self._tox.del_groupchat(gc.number) - del self._contacts[num] - self._screen.friends_list.takeItem(num) - if num == self._active_friend: # active friend was deleted - if not len(self._contacts): # last friend was deleted - self.set_active(-1) - else: - self.set_active(0) - - def group_invite(self, friend_number, gc_type, data): - text = QtWidgets.QApplication.translate('MainWindow', 'User {} invites you to group chat. Accept?') - title = QtWidgets.QApplication.translate('MainWindow', 'Group chat invite') - friend = self.get_friend_by_number(friend_number) - reply = QtWidgets.QMessageBox.question(None, title, text.format(friend.name), QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: # accepted - if gc_type == TOX_GROUPCHAT_TYPE['TEXT']: - number = self._tox.join_groupchat(friend_number, data) - else: - number = self._tox.join_av_groupchat(friend_number, data) - self.add_gc(number) - - def new_gc_message(self, group_number, peer_number, message_type, message): - name = self._tox.group_peername(group_number, peer_number) - message_type += 5 - if group_number == self.get_active_number() and not self.is_active_a_friend(): # add message to list - t = time.time() - self.create_gc_message_item(message, t, MESSAGE_OWNER['FRIEND'], name, message_type) - self._messages.scrollToBottom() - self.get_curr_friend().append_message( - GroupChatMessage(message, MESSAGE_OWNER['FRIEND'], t, message_type, name)) - else: - gc = self.get_group_by_number(group_number) - gc.inc_messages() - gc.append_message( - GroupChatMessage(message, MESSAGE_OWNER['FRIEND'], time.time(), message_type, name)) - if not gc.visibility: - self.update_filtration() - - def new_gc_title(self, group_number, title): - gc = self.get_group_by_number(group_number) - gc.new_title(title) - if not self.is_active_a_friend() and self.get_active_number() == group_number: - self.update() - - def update_gc(self, group_number): - count = self._tox.group_number_peers(group_number) - gc = self.get_group_by_number(group_number) - text = QtWidgets.QApplication.translate('MainWindow', '{} users in chat') - gc.status_message = text.format(str(count)).encode('utf-8') - if not self.is_active_a_friend() and self.get_active_number() == group_number: - self.update() - - def send_gc_message(self, text): - group_number = self.get_active_number() - if text.startswith('/me '): - text = text[4:] - self._tox.group_action_send(group_number, text.encode('utf-8')) - else: - self._tox.group_message_send(group_number, text.encode('utf-8')) - self._screen.messageEdit.clear() - - def set_title(self, num): - """ - Set new title for gc - """ - gc = self._contacts[num] - name = gc.name - dialog = QtWidgets.QApplication.translate('MainWindow', - "Enter new title for group {}:") - dialog = dialog.format(name) - title = QtWidgets.QApplication.translate('MainWindow', - 'Set title') - text, ok = QtWidgets.QInputDialog.getText(None, - title, - dialog, - QtWidgets.QLineEdit.Normal, - name) - if ok: - text = text.encode('utf-8') - self._tox.group_set_title(gc.number, text) - self.new_gc_title(gc.number, text) - - def get_group_chats(self): - chats = filter(lambda x: type(x) is GroupChat, self._contacts) - chats = map(lambda c: (c.name, c.number), chats) - return list(chats) - - def invite_friend(self, friend_num, group_number): - friend = self._contacts[friend_num] - self._tox.invite_friend(friend.number, group_number) - - def get_gc_peer_name(self, text): - gc = self.get_curr_friend() - if type(gc) is not GroupChat: - return '\t' - names = gc.get_names() - name = re.split("\s+", text)[-1] - suggested_names = list(filter(lambda x: x.startswith(name), names)) - if not len(suggested_names): - return '\t' - return suggested_names[0][len(name):] + ': ' - - -def tox_factory(data=None, settings=None): - """ - :param data: user data from .tox file. None = no saved data, create new profile - :param settings: current profile settings. None = default settings will be used - :return: new tox instance - """ - if settings is None: - settings = Settings.get_default_settings() - tox_options = Tox.options_new() - tox_options.contents.udp_enabled = settings['udp_enabled'] - tox_options.contents.proxy_type = settings['proxy_type'] - tox_options.contents.proxy_host = bytes(settings['proxy_host'], 'UTF-8') - tox_options.contents.proxy_port = settings['proxy_port'] - tox_options.contents.start_port = settings['start_port'] - tox_options.contents.end_port = settings['end_port'] - tox_options.contents.tcp_port = settings['tcp_port'] - if data: # load existing profile - tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['TOX_SAVE'] - tox_options.contents.savedata_data = c_char_p(data) - tox_options.contents.savedata_length = len(data) - else: # create new profile - tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['NONE'] - tox_options.contents.savedata_data = None - tox_options.contents.savedata_length = 0 - return Tox(tox_options) diff --git a/toxygen/file_tansfers/__init__.py b/toxygen/file_tansfers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/file_transfers.py b/toxygen/file_tansfers/file_transfers.py similarity index 100% rename from toxygen/file_transfers.py rename to toxygen/file_tansfers/file_transfers.py diff --git a/toxygen/file_tansfers/file_transfers_handler.py b/toxygen/file_tansfers/file_transfers_handler.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/login.py b/toxygen/login.py index e69de29..d68629a 100644 --- a/toxygen/login.py +++ b/toxygen/login.py @@ -0,0 +1,19 @@ +class Login: + + def __init__(self, arr): + self.arr = arr + + def login_screen_close(self, t, number=-1, default=False, name=None): + """ Function which processes data from login screen + :param t: 0 - window was closed, 1 - new profile was created, 2 - profile loaded + :param number: num of chosen profile in list (-1 by default) + :param default: was or not chosen profile marked as default + :param name: name of new profile + """ + self.t = t + self.num = number + self.default = default + self.name = name + + def get_data(self): + return self.arr[self.num] \ No newline at end of file diff --git a/toxygen/main.py b/toxygen/main.py index 4e91c50..c8f18f6 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,31 +1,10 @@ import sys from user_data.settings import * -from callbacks import init_callbacks, stop, start from util import curr_directory, program_version, remove -import updater import argparse - class Login: - - def __init__(self, arr): - self.arr = arr - - def login_screen_close(self, t, number=-1, default=False, name=None): - """ Function which processes data from login screen - :param t: 0 - window was closed, 1 - new profile was created, 2 - profile loaded - :param number: num of chosen profile in list (-1 by default) - :param default: was or not chosen profile marked as default - :param name: name of new profile - """ - self.t = t - self.num = number - self.default = default - self.name = name - - def get_data(self): - return self.arr[self.num] def clean(): diff --git a/toxygen/threads.py b/toxygen/threads.py index dae8800..a79297a 100644 --- a/toxygen/threads.py +++ b/toxygen/threads.py @@ -60,3 +60,68 @@ class ToxAVIterateThread(QtCore.QThread): while not self.stop: self.toxav.iterate() self.msleep(self.toxav.iteration_interval()) + + +class FileTransfersThread(threading.Thread): + + def __init__(self): + self._queue = queue.Queue() + self._timeout = 0.01 + self._continue = True + super().__init__() + + def execute(self, function, *args, **kwargs): + self._queue.put((function, args, kwargs)) + + def stop(self): + self._continue = False + + def run(self): + while self._continue: + try: + function, args, kwargs = self._queue.get(timeout=self._timeout) + function(*args, **kwargs) + except queue.Empty: + pass + except queue.Full: + util.log('Queue is Full in _thread') + except Exception as ex: + util.log('Exception in _thread: ' + str(ex)) + + +_thread = FileTransfersThread() + + +def start(): + _thread.start() + + +def stop(): + _thread.stop() + _thread.join() + + + + +class InvokeEvent(QtCore.QEvent): + EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType()) + + def __init__(self, fn, *args, **kwargs): + QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE) + self.fn = fn + self.args = args + self.kwargs = kwargs + + +class Invoker(QtCore.QObject): + + def event(self, event): + event.fn(*event.args, **event.kwargs) + return True + + +_invoker = Invoker() + + +def invoke_in_main_thread(fn, *args, **kwargs): + QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs)) diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py index 0e4991e..f6ab154 100644 --- a/toxygen/ui/list_items.py +++ b/toxygen/ui/list_items.py @@ -1,7 +1,7 @@ from wrapper.toxcore_enums_and_consts import * from PyQt5 import QtCore, QtGui, QtWidgets from contacts import profile -from file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR +from file_tansfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR from util import curr_directory, convert_time, curr_time from ui.widgets import DataLabel, create_menu import html as h diff --git a/toxygen/user_data/toxes.py b/toxygen/user_data/toxes.py index 5a22f61..982f287 100644 --- a/toxygen/user_data/toxes.py +++ b/toxygen/user_data/toxes.py @@ -1,24 +1,24 @@ class ToxES: - def __init__(self, toxencryptsave): - self._toxencryptsave = toxencryptsave - self._passphrase = None + def __init__(self, tox_encrypt_save): + self._tox_encrypt_save = tox_encrypt_save + self._password = None - def set_password(self, passphrase): - self._passphrase = passphrase + def set_password(self, password): + self._password = password def has_password(self): - return bool(self._passphrase) + return bool(self._password) def is_password(self, password): - return self._passphrase == password + return self._password == password def is_data_encrypted(self, data): - return len(data) > 0 and self._toxencryptsave.is_data_encrypted(data) + return len(data) > 0 and self._tox_encrypt_save.is_data_encrypted(data) def pass_encrypt(self, data): - return self._toxencryptsave.pass_encrypt(data, self._passphrase) + return self._tox_encrypt_save.pass_encrypt(data, self._password) def pass_decrypt(self, data): - return self._toxencryptsave.pass_decrypt(data, self._passphrase) + return self._tox_encrypt_save.pass_decrypt(data, self._password) From 20bb694c7ec63326567876a2db7b21186467edf5 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 10 Mar 2018 18:42:53 +0300 Subject: [PATCH 003/217] refactoring - correct namespaces, logic from profile.py moved to different files, calbacks partially fixed --- tests/tests.py | 2 +- toxygen/app.py | 10 +- toxygen/av/calls_manager.py | 104 +++ toxygen/bootstrap/bootstrap.py | 2 +- toxygen/communication/callbacks.py | 148 ++-- toxygen/communication/tox_factory.py | 18 +- toxygen/contacts/contact.py | 4 +- toxygen/contacts/contacts_manager.py | 402 +++++++++ toxygen/contacts/profile.py | 808 +----------------- toxygen/db/{history.py => database.py} | 22 +- .../file_tansfers/file_transfers_handler.py | 0 .../__init__.py | 0 .../file_transfers.py | 0 .../file_transfers/file_transfers_handler.py | 311 +++++++ toxygen/main.py | 10 +- toxygen/notifications.py | 2 +- toxygen/smileys_and_stickers.py | 2 +- toxygen/threads.py | 21 +- toxygen/tray.py | 154 ++-- toxygen/ui/list_items.py | 2 +- toxygen/user_data/profile_manager.py | 2 +- toxygen/util/ui.py | 8 + toxygen/util/util.py | 5 +- 23 files changed, 1071 insertions(+), 966 deletions(-) create mode 100644 toxygen/av/calls_manager.py create mode 100644 toxygen/contacts/contacts_manager.py rename toxygen/db/{history.py => database.py} (92%) delete mode 100644 toxygen/file_tansfers/file_transfers_handler.py rename toxygen/{file_tansfers => file_transfers}/__init__.py (100%) rename toxygen/{file_tansfers => file_transfers}/file_transfers.py (100%) create mode 100644 toxygen/file_transfers/file_transfers_handler.py create mode 100644 toxygen/util/ui.py diff --git a/tests/tests.py b/tests/tests.py index cad1d1c..b21854c 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,6 +1,6 @@ from contacts.profile import * from network.tox_dns import tox_dns -from db.history import History +from db.database import History from toxygen.smileys import SmileyLoader from messenger.messages import * import user_data.toxes as encr diff --git a/toxygen/app.py b/toxygen/app.py index 1dec36f..7f789eb 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -1,3 +1,7 @@ +import threads + + + class App: def __init__(self, path_or_uri=None): @@ -247,14 +251,14 @@ class App: # create new tox instance self.tox = profile.tox_factory(data, Settings.get_instance()) # init thread - self.init = self.InitThread(self.tox, self.ms, self.tray) + self.init = threads.InitThread(self.tox, self.ms, self.tray) self.init.start() # starting threads for tox iterate and toxav iterate - self.mainloop = self.ToxIterateThread(self.tox) + self.mainloop = threads.ToxIterateThread(self.tox) self.mainloop.start() - self.avloop = self.ToxAVIterateThread(self.tox.AV) + self.avloop = threads.ToxAVIterateThread(self.tox.AV) self.avloop.start() plugin_helper = PluginLoader.get_instance() diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py new file mode 100644 index 0000000..6cfe543 --- /dev/null +++ b/toxygen/av/calls_manager.py @@ -0,0 +1,104 @@ +import threading +import cv2 +import av.calls +from PyQt5 import QtWidgets +from messenger.messages import * +import time + + +class CallsManager: + + def __init__(self, tox): + self._call = av.calls.AV(tox.AV) # object with data about calls + self._call_widgets = {} # dict of incoming call widgets + self._incoming_calls = set() + + + # ----------------------------------------------------------------------------------------------------------------- + # AV support + # ----------------------------------------------------------------------------------------------------------------- + + def get_call(self): + return self._call + + call = property(get_call) + + def call_click(self, audio=True, video=False): + """User clicked audio button in main window""" + num = self.get_active_number() + if not self.is_active_a_friend(): + return + if num not in self._call and self.is_active_online(): # start call + if not Settings.get_instance().audio['enabled']: + return + self._call(num, audio, video) + self._screen.active_call() + if video: + text = QtWidgets.QApplication.translate("incoming_call", "Outgoing video call") + else: + text = QtWidgets.QApplication.translate("incoming_call", "Outgoing audio call") + self.get_curr_friend().append_message(InfoMessage(text, time.time())) + self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + self._messages.scrollToBottom() + elif num in self._call: # finish or cancel call if you call with active friend + self.stop_call(num, False) + + def incoming_call(self, audio, video, friend_number): + """ + Incoming call from friend. + """ + if not Settings.get_instance().audio['enabled']: + return + friend = self.get_friend_by_number(friend_number) + if video: + text = QtWidgets.QApplication.translate("incoming_call", "Incoming video call") + else: + text = QtWidgets.QApplication.translate("incoming_call", "Incoming audio call") + friend.append_message(InfoMessage(text, time.time())) + self._incoming_calls.add(friend_number) + if friend_number == self.get_active_number(): + self._screen.incoming_call() + self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + self._messages.scrollToBottom() + else: + friend.actions = True + self._call_widgets[friend_number] = avwidgets.IncomingCallWidget(friend_number, text, friend.name) + self._call_widgets[friend_number].set_pixmap(friend.get_pixmap()) + self._call_widgets[friend_number].show() + + def accept_call(self, friend_number, audio, video): + """ + Accept incoming call with audio or video + """ + self._call.accept_call(friend_number, audio, video) + self._screen.active_call() + if friend_number in self._incoming_calls: + self._incoming_calls.remove(friend_number) + del self._call_widgets[friend_number] + + def stop_call(self, friend_number, by_friend): + """ + Stop call with friend + """ + if friend_number in self._incoming_calls: + self._incoming_calls.remove(friend_number) + text = QtWidgets.QApplication.translate("incoming_call", "Call declined") + else: + text = QtWidgets.QApplication.translate("incoming_call", "Call finished") + self._screen.call_finished() + is_video = self._call.is_video_call(friend_number) + self._call.finish_call(friend_number, by_friend) # finish or decline call + if hasattr(self, '_call_widget'): + self._call_widget[friend_number].close() + del self._call_widget[friend_number] + + def destroy_window(): + if is_video: + cv2.destroyWindow(str(friend_number)) + + threading.Timer(2.0, destroy_window).start() + friend = self.get_friend_by_number(friend_number) + friend.append_message(InfoMessage(text, time.time())) + if friend_number == self.get_active_number(): + self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + self._messages.scrollToBottom() diff --git a/toxygen/bootstrap/bootstrap.py b/toxygen/bootstrap/bootstrap.py index 6d97f25..87c6d9c 100644 --- a/toxygen/bootstrap/bootstrap.py +++ b/toxygen/bootstrap/bootstrap.py @@ -1,6 +1,6 @@ import random import urllib.request -from util import log, curr_directory +from util.util import log, curr_directory from user_data import settings from PyQt5 import QtNetwork, QtCore import json diff --git a/toxygen/communication/callbacks.py b/toxygen/communication/callbacks.py index 0e7345c..e27bd74 100644 --- a/toxygen/communication/callbacks.py +++ b/toxygen/communication/callbacks.py @@ -11,6 +11,7 @@ import threading import util import cv2 import numpy as np +from threads import invoke_in_main_thread, execute # TODO: use closures @@ -19,15 +20,14 @@ import numpy as np # ----------------------------------------------------------------------------------------------------------------- -def self_connection_status(tox_link): +def self_connection_status(tox, profile): """ Current user changed connection status (offline, UDP, TCP) """ - def wrapped(tox, connection, user_data): + def wrapped(tox_link, connection, user_data): print('Connection status: ', str(connection)) - profile = Profile.get_instance() if profile.status is None: - status = tox_link.self_get_status() + status = tox.self_get_status() invoke_in_main_thread(profile.set_status, status) elif connection == TOX_CONNECTION['NONE']: invoke_in_main_thread(profile.set_status, None) @@ -39,67 +39,73 @@ def self_connection_status(tox_link): # ----------------------------------------------------------------------------------------------------------------- -def friend_status(tox, friend_num, new_status, user_data): - """ - Check friend's status (none, busy, away) - """ - print("Friend's #{} status changed!".format(friend_num)) - profile = Profile.get_instance() - friend = profile.get_friend_by_number(friend_num) - if friend.status is None and Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: - sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) - invoke_in_main_thread(friend.set_status, new_status) - invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: profile.send_files(friend_num)) - invoke_in_main_thread(profile.update_filtration) - - -def friend_connection_status(tox, friend_num, new_status, user_data): - """ - Check friend's connection status (offline, udp, tcp) - """ - print("Friend #{} connection status: {}".format(friend_num, new_status)) - profile = Profile.get_instance() - friend = profile.get_friend_by_number(friend_num) - if new_status == TOX_CONNECTION['NONE']: - invoke_in_main_thread(profile.friend_exit, friend_num) - invoke_in_main_thread(profile.update_filtration) - if Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: +def friend_status(profile, settings): + def wrapped(tox, friend_num, new_status, user_data): + """ + Check friend's status (none, busy, away) + """ + print("Friend's #{} status changed!".format(friend_num)) + friend = profile.get_friend_by_number(friend_num) + if friend.status is None and settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) - elif friend.status is None: - invoke_in_main_thread(profile.send_avatar, friend_num) - invoke_in_main_thread(PluginLoader.get_instance().friend_online, friend_num) + invoke_in_main_thread(friend.set_status, new_status) + invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: profile.send_files(friend_num)) + invoke_in_main_thread(profile.update_filtration) + + return wrapped -def friend_name(tox, friend_num, name, size, user_data): - """ - Friend changed his name - """ - profile = Profile.get_instance() - print('New name friend #' + str(friend_num)) - invoke_in_main_thread(profile.new_name, friend_num, name) +def friend_connection_status(profile, settings, plugin_loader): + def wrapped(tox, friend_num, new_status, user_data): + """ + Check friend's connection status (offline, udp, tcp) + """ + print("Friend #{} connection status: {}".format(friend_num, new_status)) + friend = profile.get_friend_by_number(friend_num) + if new_status == TOX_CONNECTION['NONE']: + invoke_in_main_thread(profile.friend_exit, friend_num) + invoke_in_main_thread(profile.update_filtration) + if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) + elif friend.status is None: + invoke_in_main_thread(profile.send_avatar, friend_num) + invoke_in_main_thread(plugin_loader.friend_online, friend_num) + + return wrapped -def friend_status_message(tox, friend_num, status_message, size, user_data): - """ - :return: function for callback friend_status_message. It updates friend's status message - and calls window repaint - """ - profile = Profile.get_instance() - friend = profile.get_friend_by_number(friend_num) - invoke_in_main_thread(friend.set_status_message, status_message) - print('User #{} has new status'.format(friend_num)) - invoke_in_main_thread(profile.send_messages, friend_num) - if profile.get_active_number() == friend_num: - invoke_in_main_thread(profile.set_active) +def friend_name(profile): + def wrapped(tox, friend_num, name, size, user_data): + """ + Friend changed his name + """ + print('New name friend #' + str(friend_num)) + invoke_in_main_thread(profile.new_name, friend_num, name) + + return wrapped -def friend_message(window, tray): +def friend_status_message(profile): + def wrapped(tox, friend_num, status_message, size, user_data): + """ + :return: function for callback friend_status_message. It updates friend's status message + and calls window repaint + """ + friend = profile.get_friend_by_number(friend_num) + invoke_in_main_thread(friend.set_status_message, status_message) + print('User #{} has new status'.format(friend_num)) + invoke_in_main_thread(profile.send_messages, friend_num) + if profile.get_active_number() == friend_num: + invoke_in_main_thread(profile.set_active) + + return wrapped + + +def friend_message(profile, settings, window, tray): """ New message from friend """ def wrapped(tox, friend_number, message_type, message, size, user_data): - profile = Profile.get_instance() - settings = Settings.get_instance() message = str(message, 'utf-8') invoke_in_main_thread(profile.new_message, friend_number, message_type, message) if not window.isActiveWindow(): @@ -109,6 +115,7 @@ def friend_message(window, tray): if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['MESSAGE']) invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) + return wrapped @@ -174,31 +181,40 @@ def tox_file_recv(window, tray): return wrapped -def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data): +def file_recv_chunk(file_transfer_handler): """ Incoming chunk """ - _thread.execute(Profile.get_instance().incoming_chunk, friend_number, file_number, position, - chunk[:length] if length else None) + def wrapped(tox, friend_number, file_number, position, chunk, length, user_data): + execute(file_transfer_handler.incoming_chunk, friend_number, file_number, position, + chunk[:length] if length else None) + + return wrapped -def file_chunk_request(tox, friend_number, file_number, position, size, user_data): +def file_chunk_request(file_transfer_handler): """ Outgoing chunk """ - Profile.get_instance().outgoing_chunk(friend_number, file_number, position, size) + def wrapped(tox, friend_number, file_number, position, size, user_data): + execute(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) + + return wrapped -def file_recv_control(tox, friend_number, file_number, file_control, user_data): +def file_recv_control(file_transfer_handler): """ Friend cancelled, paused or resumed file transfer """ - if file_control == TOX_FILE_CONTROL['CANCEL']: - invoke_in_main_thread(Profile.get_instance().cancel_transfer, friend_number, file_number, True) - elif file_control == TOX_FILE_CONTROL['PAUSE']: - invoke_in_main_thread(Profile.get_instance().pause_transfer, friend_number, file_number, True) - elif file_control == TOX_FILE_CONTROL['RESUME']: - invoke_in_main_thread(Profile.get_instance().resume_transfer, friend_number, file_number, True) + def wrapped(tox, friend_number, file_number, file_control, user_data): + if file_control == TOX_FILE_CONTROL['CANCEL']: + file_transfer_handler.cancel_transfer(friend_number, file_number, True) + elif file_control == TOX_FILE_CONTROL['PAUSE']: + file_transfer_handler.pause_transfer(friend_number, file_number, True) + elif file_control == TOX_FILE_CONTROL['RESUME']: + file_transfer_handler.resume_transfer(friend_number, file_number, True) + + return wrapped # ----------------------------------------------------------------------------------------------------------------- # Callbacks - custom packets diff --git a/toxygen/communication/tox_factory.py b/toxygen/communication/tox_factory.py index a39bb15..588bd30 100644 --- a/toxygen/communication/tox_factory.py +++ b/toxygen/communication/tox_factory.py @@ -1,3 +1,7 @@ +import user_data.settings +import wrapper.tox +import wrapper.toxcore_enums_and_consts as enums +import ctypes def tox_factory(data=None, settings=None): @@ -7,8 +11,9 @@ def tox_factory(data=None, settings=None): :return: new tox instance """ if settings is None: - settings = Settings.get_default_settings() - tox_options = Tox.options_new() + settings = user_data.settings.Settings.get_default_settings() + + tox_options = wrapper.tox.Tox.options_new() tox_options.contents.udp_enabled = settings['udp_enabled'] tox_options.contents.proxy_type = settings['proxy_type'] tox_options.contents.proxy_host = bytes(settings['proxy_host'], 'UTF-8') @@ -17,11 +22,12 @@ def tox_factory(data=None, settings=None): tox_options.contents.end_port = settings['end_port'] tox_options.contents.tcp_port = settings['tcp_port'] if data: # load existing profile - tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['TOX_SAVE'] - tox_options.contents.savedata_data = c_char_p(data) + tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['TOX_SAVE'] + tox_options.contents.savedata_data = ctypes.c_char_p(data) tox_options.contents.savedata_length = len(data) else: # create new profile - tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['NONE'] + tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['NONE'] tox_options.contents.savedata_data = None tox_options.contents.savedata_length = 0 - return Tox(tox_options) + + return wrapper.tox.Tox(tox_options) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 4b221e3..96ff804 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -1,8 +1,8 @@ -from db.history import * +from db.database import * from contacts import basecontact import util from messenger.messages import * -from file_tansfers import file_transfers as ft +from file_transfers import file_transfers as ft import re diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py new file mode 100644 index 0000000..cbac1e1 --- /dev/null +++ b/toxygen/contacts/contacts_manager.py @@ -0,0 +1,402 @@ + + +class ContactsManager: + + def __init__(self, tox, settings, screen): + self._tox = tox + self._settings = settings + self._contacts, self._active_friend = [], -1 + self._sorting = settings['sorting'] + data = tox.self_get_friend_list() + self._filter_string = '' + self._friend_item_height = 40 if settings['compact_mode'] else 70 + screen.online_contacts.setCurrentIndex(int(self._sorting)) + aliases = settings['friends_aliases'] + for i in data: # creates list of friends + tox_id = tox.friend_get_public_key(i) + try: + alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] + except: + alias = '' + item = self.create_friend_item() + name = alias or tox.friend_get_name(i) or tox_id + status_message = tox.friend_get_status_message(i) + if not self._history.friend_exists_in_db(tox_id): + self._history.add_friend_to_db(tox_id) + message_getter = self._history.messages_getter(tox_id) + friend = Friend(message_getter, i, name, status_message, item, tox_id) + friend.set_alias(alias) + self._contacts.append(friend) + if len(self._contacts): + self.set_active(0) + self.filtration_and_sorting(self._sorting) + + + def get_friend(self, num): + if num < 0 or num >= len(self._contacts): + return None + return self._contacts[num] + + def get_curr_friend(self): + return self._contacts[self._active_friend] if self._active_friend + 1 else None + + # ----------------------------------------------------------------------------------------------------------------- + # Work with active friend + # ----------------------------------------------------------------------------------------------------------------- + + def get_active(self): + return self._active_friend + + def set_active(self, value=None): + """ + Change current active friend or update info + :param value: number of new active friend in friend's list or None to update active user's data + """ + if value is None and self._active_friend == -1: # nothing to update + return + if value == -1: # all friends were deleted + self._screen.account_name.setText('') + self._screen.account_status.setText('') + self._screen.account_status.setToolTip('') + self._active_friend = -1 + self._screen.account_avatar.setHidden(True) + self._messages.clear() + self._screen.messageEdit.clear() + return + try: + self.send_typing(False) + self._screen.typing.setVisible(False) + if value is not None: + if self._active_friend + 1 and self._active_friend != value: + try: + self.get_curr_friend().curr_text = self._screen.messageEdit.toPlainText() + except: + pass + friend = self._contacts[value] + friend.remove_invalid_unsent_files() + if self._active_friend != value: + self._screen.messageEdit.setPlainText(friend.curr_text) + self._active_friend = value + friend.reset_messages() + if not Settings.get_instance()['save_history']: + friend.delete_old_messages() + self._messages.clear() + friend.load_corr() + messages = friend.get_corr()[-PAGE_SIZE:] + self._load_history = False + for message in messages: + if message.get_type() <= 1: + data = message.get_data() + self.create_message_item(data[0], + data[2], + data[1], + data[3]) + elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: + if message.get_status() is None: + self.create_unsent_file_item(message) + continue + item = self.create_file_transfer_item(message) + if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer + try: + ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] + ft.set_state_changed_handler(item.update_transfer_state) + ft.signal() + except: + print('Incoming not started transfer - no info found') + elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline + self.create_inline_item(message.get_data()) + elif message.get_type() < 5: # info message + data = message.get_data() + self.create_message_item(data[0], + data[2], + '', + data[3]) + else: + data = message.get_data() + self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3]) + self._messages.scrollToBottom() + self._load_history = True + if value in self._call: + self._screen.active_call() + elif value in self._incoming_calls: + self._screen.incoming_call() + else: + self._screen.call_finished() + else: + friend = self.get_curr_friend() + + self._screen.account_name.setText(friend.name) + self._screen.account_status.setText(friend.status_message) + self._screen.account_status.setToolTip(friend.get_full_status()) + if friend.tox_id is None: + avatar_path = curr_directory() + '/images/group.png' + else: + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + if not os.path.isfile(avatar_path): # load default image + avatar_path = curr_directory() + '/images/avatar.png' + os.chdir(os.path.dirname(avatar_path)) + pixmap = QtGui.QPixmap(avatar_path) + self._screen.account_avatar.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation)) + except Exception as ex: # no friend found. ignore + log('Friend value: ' + str(value)) + log('Error in set active: ' + str(ex)) + raise + + def set_active_by_number_and_type(self, number, is_friend): + for i in range(len(self._contacts)): + c = self._contacts[i] + if c.number == number and (type(c) is Friend == is_friend): + self._active_friend = i + break + + active_friend = property(get_active, set_active) + + # ----------------------------------------------------------------------------------------------------------------- + # Filtration + # ----------------------------------------------------------------------------------------------------------------- + + def filtration_and_sorting(self, sorting=0, filter_str=''): + """ + Filtration of friends list + :param sorting: 0 - no sort, 1 - online only, 2 - online first, 4 - by name + :param filter_str: show contacts which name contains this substring + """ + filter_str = filter_str.lower() + settings = Settings.get_instance() + number = self.get_active_number() + is_friend = self.is_active_a_friend() + if sorting > 1: + if sorting & 2: + self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True) + if sorting & 4: + if not sorting & 2: + self._contacts = sorted(self._contacts, key=lambda x: x.name.lower()) + else: # save results of prev sorting + online_friends = filter(lambda x: x.status is not None, self._contacts) + count = len(list(online_friends)) + part1 = self._contacts[:count] + part2 = self._contacts[count:] + part1 = sorted(part1, key=lambda x: x.name.lower()) + part2 = sorted(part2, key=lambda x: x.name.lower()) + self._contacts = part1 + part2 + else: # sort by number + online_friends = filter(lambda x: x.status is not None, self._contacts) + count = len(list(online_friends)) + part1 = self._contacts[:count] + part2 = self._contacts[count:] + part1 = sorted(part1, key=lambda x: x.number) + part2 = sorted(part2, key=lambda x: x.number) + self._contacts = part1 + part2 + self._screen.friends_list.clear() + for contact in self._contacts: + contact.set_widget(self.create_friend_item()) + for index, friend in enumerate(self._contacts): + friend.visibility = (friend.status is not None or not (sorting & 1)) and (filter_str in friend.name.lower()) + friend.visibility = friend.visibility or friend.messages or friend.actions + if friend.visibility: + self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) + else: + self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) + self._sorting, self._filter_string = sorting, filter_str + settings['sorting'] = self._sorting + settings.save() + self.set_active_by_number_and_type(number, is_friend) + + def update_filtration(self): + """ + Update list of contacts when 1 of friends change connection status + """ + self.filtration_and_sorting(self._sorting, self._filter_string) + + + def create_friend_item(self): + """ + Method-factory + :return: new widget for friend instance + """ + return self._factory.friend_item() + + + + # ----------------------------------------------------------------------------------------------------------------- + # Work with friends (remove, block, set alias, get public key) + # ----------------------------------------------------------------------------------------------------------------- + + def set_alias(self, num): + """ + Set new alias for friend + """ + friend = self._contacts[num] + name = friend.name + dialog = QtWidgets.QApplication.translate('MainWindow', + "Enter new alias for friend {} or leave empty to use friend's name:") + dialog = dialog.format(name) + title = QtWidgets.QApplication.translate('MainWindow', + 'Set alias') + text, ok = QtWidgets.QInputDialog.getText(None, + title, + dialog, + QtWidgets.QLineEdit.Normal, + name) + if ok: + settings = Settings.get_instance() + aliases = settings['friends_aliases'] + if text: + friend.name = bytes(text, 'utf-8') + try: + index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) + aliases[index] = (friend.tox_id, text) + except: + aliases.append((friend.tox_id, text)) + friend.set_alias(text) + else: # use default name + friend.name = bytes(self._tox.friend_get_name(friend.number), 'utf-8') + friend.set_alias('') + try: + index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) + del aliases[index] + except: + pass + settings.save() + if num == self.get_active_number() and self.is_active_a_friend(): + self.update() + + def friend_public_key(self, num): + return self._contacts[num].tox_id + + def delete_friend(self, num): + """ + Removes friend from contact list + :param num: number of friend in list + """ + friend = self._contacts[num] + settings = Settings.get_instance() + try: + index = list(map(lambda x: x[0], settings['friends_aliases'])).index(friend.tox_id) + del settings['friends_aliases'][index] + except: + pass + if friend.tox_id in settings['notes']: + del settings['notes'][friend.tox_id] + settings.save() + self.clear_history(num) + if self._history.friend_exists_in_db(friend.tox_id): + self._history.delete_friend_from_db(friend.tox_id) + self._tox.friend_delete(friend.number) + del self._contacts[num] + self._screen.friends_list.takeItem(num) + if num == self._active_friend: # active friend was deleted + if not len(self._contacts): # last friend was deleted + self.set_active(-1) + else: + self.set_active(0) + data = self._tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + + def add_friend(self, tox_id): + """ + Adds friend to list + """ + num = self._tox.friend_add_norequest(tox_id) # num - friend number + item = self.create_friend_item() + try: + if not self._history.friend_exists_in_db(tox_id): + self._history.add_friend_to_db(tox_id) + message_getter = self._history.messages_getter(tox_id) + except Exception as ex: # something is wrong + log('Accept friend request failed! ' + str(ex)) + message_getter = None + friend = Friend(message_getter, num, tox_id, '', item, tox_id) + self._contacts.append(friend) + + def block_user(self, tox_id): + """ + Block user with specified tox id (or public key) - delete from friends list and ignore friend requests + """ + tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] + if tox_id == self.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]: + return + settings = Settings.get_instance() + if tox_id not in settings['blocked']: + settings['blocked'].append(tox_id) + settings.save() + try: + num = self._tox.friend_by_public_key(tox_id) + self.delete_friend(num) + data = self._tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + except: # not in friend list + pass + + def unblock_user(self, tox_id, add_to_friend_list): + """ + Unblock user + :param tox_id: tox id of contact + :param add_to_friend_list: add this contact to friend list or not + """ + s = Settings.get_instance() + s['blocked'].remove(tox_id) + s.save() + if add_to_friend_list: + self.add_friend(tox_id) + data = self._tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + + # ----------------------------------------------------------------------------------------------------------------- + # Friend requests + # ----------------------------------------------------------------------------------------------------------------- + + def send_friend_request(self, tox_id, message): + """ + Function tries to send request to contact with specified id + :param tox_id: id of new contact or tox dns 4 value + :param message: additional message + :return: True on success else error string + """ + try: + message = message or 'Hello! Add me to your contact list please' + if '@' in tox_id: # value like groupbot@toxme.io + tox_id = tox_dns(tox_id) + if tox_id is None: + raise Exception('TOX DNS lookup failed') + if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key + self.add_friend(tox_id) + msgBox = QtWidgets.QMessageBox() + msgBox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "Friend added")) + text = (QtWidgets.QApplication.translate("MainWindow", 'Friend added without sending friend request')) + msgBox.setText(text) + msgBox.exec_() + else: + result = self._tox.friend_add(tox_id, message.encode('utf-8')) + tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] + item = self.create_friend_item() + if not self._history.friend_exists_in_db(tox_id): + self._history.add_friend_to_db(tox_id) + message_getter = self._history.messages_getter(tox_id) + friend = Friend(message_getter, result, tox_id, '', item, tox_id) + self._contacts.append(friend) + data = self._tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + return True + except Exception as ex: # wrong data + log('Friend request failed with ' + str(ex)) + return str(ex) + + def process_friend_request(self, tox_id, message): + """ + Accept or ignore friend request + :param tox_id: tox id of contact + :param message: message + """ + try: + text = QtWidgets.QApplication.translate('MainWindow', 'User {} wants to add you to contact list. Message:\n{}') + info = text.format(tox_id, message) + fr_req = QtWidgets.QApplication.translate('MainWindow', 'Friend request') + reply = QtWidgets.QMessageBox.question(None, fr_req, info, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: # accepted + self.add_friend(tox_id) + data = self._tox.get_savedata() + ProfileManager.get_instance().save_profile(data) + except Exception as ex: # something is wrong + log('Accept friend request failed! ' + str(ex)) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 9c585c2..483d96b 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -6,8 +6,8 @@ from wrapper.toxcore_enums_and_consts import * from ctypes import * from util import log, Singleton, curr_directory from network.tox_dns import tox_dns -from db.history import * -from file_tansfers.file_transfers import * +from db.database import * +from file_transfers.file_transfers import * import time from av import calls import plugin_support @@ -38,42 +38,15 @@ class Profile(basecontact.BaseContact, Singleton): self._messages = screen.messages self._tox = tox self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number) - self._call = calls.AV(tox.AV) # object with data about calls - self._call_widgets = {} # dict of incoming call widgets - self._incoming_calls = set() self._load_history = True self._waiting_for_reconnection = False self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages) settings = Settings.get_instance() - self._sorting = settings['sorting'] self._show_avatars = settings['show_avatars'] - self._filter_string = '' - self._friend_item_height = 40 if settings['compact_mode'] else 70 self._paused_file_transfers = dict(settings['paused_file_transfers']) # key - file id, value: [path, friend number, is incoming, start position] - screen.online_contacts.setCurrentIndex(int(self._sorting)) - aliases = settings['friends_aliases'] - data = tox.self_get_friend_list() self._history = History(tox.self_get_public_key()) # connection to db - self._contacts, self._active_friend = [], -1 - for i in data: # creates list of friends - tox_id = tox.friend_get_public_key(i) - try: - alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] - except: - alias = '' - item = self.create_friend_item() - name = alias or tox.friend_get_name(i) or tox_id - status_message = tox.friend_get_status_message(i) - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) - friend = Friend(message_getter, i, name, status_message, item, tox_id) - friend.set_alias(alias) - self._contacts.append(friend) - if len(self._contacts): - self.set_active(0) - self.filtration_and_sorting(self._sorting) + # ----------------------------------------------------------------------------------------------------------------- # Edit current user's data @@ -119,190 +92,12 @@ class Profile(basecontact.BaseContact, Singleton): self._tox_id = self._tox.self_get_address() return self._tox_id - # ----------------------------------------------------------------------------------------------------------------- - # Filtration - # ----------------------------------------------------------------------------------------------------------------- - - def filtration_and_sorting(self, sorting=0, filter_str=''): - """ - Filtration of friends list - :param sorting: 0 - no sort, 1 - online only, 2 - online first, 4 - by name - :param filter_str: show contacts which name contains this substring - """ - filter_str = filter_str.lower() - settings = Settings.get_instance() - number = self.get_active_number() - is_friend = self.is_active_a_friend() - if sorting > 1: - if sorting & 2: - self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True) - if sorting & 4: - if not sorting & 2: - self._contacts = sorted(self._contacts, key=lambda x: x.name.lower()) - else: # save results of prev sorting - online_friends = filter(lambda x: x.status is not None, self._contacts) - count = len(list(online_friends)) - part1 = self._contacts[:count] - part2 = self._contacts[count:] - part1 = sorted(part1, key=lambda x: x.name.lower()) - part2 = sorted(part2, key=lambda x: x.name.lower()) - self._contacts = part1 + part2 - else: # sort by number - online_friends = filter(lambda x: x.status is not None, self._contacts) - count = len(list(online_friends)) - part1 = self._contacts[:count] - part2 = self._contacts[count:] - part1 = sorted(part1, key=lambda x: x.number) - part2 = sorted(part2, key=lambda x: x.number) - self._contacts = part1 + part2 - self._screen.friends_list.clear() - for contact in self._contacts: - contact.set_widget(self.create_friend_item()) - for index, friend in enumerate(self._contacts): - friend.visibility = (friend.status is not None or not (sorting & 1)) and (filter_str in friend.name.lower()) - friend.visibility = friend.visibility or friend.messages or friend.actions - if friend.visibility: - self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) - else: - self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) - self._sorting, self._filter_string = sorting, filter_str - settings['sorting'] = self._sorting - settings.save() - self.set_active_by_number_and_type(number, is_friend) - - def update_filtration(self): - """ - Update list of contacts when 1 of friends change connection status - """ - self.filtration_and_sorting(self._sorting, self._filter_string) - # ----------------------------------------------------------------------------------------------------------------- # Friend getters # ----------------------------------------------------------------------------------------------------------------- def get_friend_by_number(self, num): return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0] - - def get_friend(self, num): - if num < 0 or num >= len(self._contacts): - return None - return self._contacts[num] - - def get_curr_friend(self): - return self._contacts[self._active_friend] if self._active_friend + 1 else None - - # ----------------------------------------------------------------------------------------------------------------- - # Work with active friend - # ----------------------------------------------------------------------------------------------------------------- - - def get_active(self): - return self._active_friend - - def set_active(self, value=None): - """ - Change current active friend or update info - :param value: number of new active friend in friend's list or None to update active user's data - """ - if value is None and self._active_friend == -1: # nothing to update - return - if value == -1: # all friends were deleted - self._screen.account_name.setText('') - self._screen.account_status.setText('') - self._screen.account_status.setToolTip('') - self._active_friend = -1 - self._screen.account_avatar.setHidden(True) - self._messages.clear() - self._screen.messageEdit.clear() - return - try: - self.send_typing(False) - self._screen.typing.setVisible(False) - if value is not None: - if self._active_friend + 1 and self._active_friend != value: - try: - self.get_curr_friend().curr_text = self._screen.messageEdit.toPlainText() - except: - pass - friend = self._contacts[value] - friend.remove_invalid_unsent_files() - if self._active_friend != value: - self._screen.messageEdit.setPlainText(friend.curr_text) - self._active_friend = value - friend.reset_messages() - if not Settings.get_instance()['save_history']: - friend.delete_old_messages() - self._messages.clear() - friend.load_corr() - messages = friend.get_corr()[-PAGE_SIZE:] - self._load_history = False - for message in messages: - if message.get_type() <= 1: - data = message.get_data() - self.create_message_item(data[0], - data[2], - data[1], - data[3]) - elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: - if message.get_status() is None: - self.create_unsent_file_item(message) - continue - item = self.create_file_transfer_item(message) - if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer - try: - ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] - ft.set_state_changed_handler(item.update_transfer_state) - ft.signal() - except: - print('Incoming not started transfer - no info found') - elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline - self.create_inline_item(message.get_data()) - elif message.get_type() < 5: # info message - data = message.get_data() - self.create_message_item(data[0], - data[2], - '', - data[3]) - else: - data = message.get_data() - self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3]) - self._messages.scrollToBottom() - self._load_history = True - if value in self._call: - self._screen.active_call() - elif value in self._incoming_calls: - self._screen.incoming_call() - else: - self._screen.call_finished() - else: - friend = self.get_curr_friend() - - self._screen.account_name.setText(friend.name) - self._screen.account_status.setText(friend.status_message) - self._screen.account_status.setToolTip(friend.get_full_status()) - if friend.tox_id is None: - avatar_path = curr_directory() + '/images/group.png' - else: - avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if not os.path.isfile(avatar_path): # load default image - avatar_path = curr_directory() + '/images/avatar.png' - os.chdir(os.path.dirname(avatar_path)) - pixmap = QtGui.QPixmap(avatar_path) - self._screen.account_avatar.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation)) - except Exception as ex: # no friend found. ignore - log('Friend value: ' + str(value)) - log('Error in set active: ' + str(ex)) - raise - - def set_active_by_number_and_type(self, number, is_friend): - for i in range(len(self._contacts)): - c = self._contacts[i] - if c.number == number and (type(c) is Friend == is_friend): - self._active_friend = i - break - - active_friend = property(get_active, set_active) - def get_last_message(self): if self._active_friend + 1: return self.get_curr_friend().get_last_message_text() @@ -428,6 +223,25 @@ class Profile(basecontact.BaseContact, Singleton): except Exception as ex: log('Sending pending messages failed with ' + str(ex)) + def split_message(self, message): + messages = [] + while len(message) > TOX_MAX_MESSAGE_LENGTH: + size = TOX_MAX_MESSAGE_LENGTH * 4 / 5 + last_part = message[size:TOX_MAX_MESSAGE_LENGTH] + if ' ' in last_part: + index = last_part.index(' ') + elif ',' in last_part: + index = last_part.index(',') + elif '.' in last_part: + index = last_part.index('.') + else: + index = TOX_MAX_MESSAGE_LENGTH - size - 1 + index += size + 1 + messages.append(message[:index]) + message = message[index:] + + return messages + def split_and_send(self, number, message_type, message): """ Message splitting. Message length cannot be > TOX_MAX_MESSAGE_LENGTH @@ -629,12 +443,6 @@ class Profile(basecontact.BaseContact, Singleton): # Friend, message and file transfer items creation # ----------------------------------------------------------------------------------------------------------------- - def create_friend_item(self): - """ - Method-factory - :return: new widget for friend instance - """ - return self._factory.friend_item() def create_message_item(self, text, time, owner, message_type, append=True): if message_type == MESSAGE_TYPE['INFO_MESSAGE']: @@ -678,188 +486,6 @@ class Profile(basecontact.BaseContact, Singleton): def create_inline_item(self, data, append=True): return self._factory.inline_item(data, append) - # ----------------------------------------------------------------------------------------------------------------- - # Work with friends (remove, block, set alias, get public key) - # ----------------------------------------------------------------------------------------------------------------- - - def set_alias(self, num): - """ - Set new alias for friend - """ - friend = self._contacts[num] - name = friend.name - dialog = QtWidgets.QApplication.translate('MainWindow', - "Enter new alias for friend {} or leave empty to use friend's name:") - dialog = dialog.format(name) - title = QtWidgets.QApplication.translate('MainWindow', - 'Set alias') - text, ok = QtWidgets.QInputDialog.getText(None, - title, - dialog, - QtWidgets.QLineEdit.Normal, - name) - if ok: - settings = Settings.get_instance() - aliases = settings['friends_aliases'] - if text: - friend.name = bytes(text, 'utf-8') - try: - index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) - aliases[index] = (friend.tox_id, text) - except: - aliases.append((friend.tox_id, text)) - friend.set_alias(text) - else: # use default name - friend.name = bytes(self._tox.friend_get_name(friend.number), 'utf-8') - friend.set_alias('') - try: - index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) - del aliases[index] - except: - pass - settings.save() - if num == self.get_active_number() and self.is_active_a_friend(): - self.update() - - def friend_public_key(self, num): - return self._contacts[num].tox_id - - def delete_friend(self, num): - """ - Removes friend from contact list - :param num: number of friend in list - """ - friend = self._contacts[num] - settings = Settings.get_instance() - try: - index = list(map(lambda x: x[0], settings['friends_aliases'])).index(friend.tox_id) - del settings['friends_aliases'][index] - except: - pass - if friend.tox_id in settings['notes']: - del settings['notes'][friend.tox_id] - settings.save() - self.clear_history(num) - if self._history.friend_exists_in_db(friend.tox_id): - self._history.delete_friend_from_db(friend.tox_id) - self._tox.friend_delete(friend.number) - del self._contacts[num] - self._screen.friends_list.takeItem(num) - if num == self._active_friend: # active friend was deleted - if not len(self._contacts): # last friend was deleted - self.set_active(-1) - else: - self.set_active(0) - data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) - - def add_friend(self, tox_id): - """ - Adds friend to list - """ - num = self._tox.friend_add_norequest(tox_id) # num - friend number - item = self.create_friend_item() - try: - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) - except Exception as ex: # something is wrong - log('Accept friend request failed! ' + str(ex)) - message_getter = None - friend = Friend(message_getter, num, tox_id, '', item, tox_id) - self._contacts.append(friend) - - def block_user(self, tox_id): - """ - Block user with specified tox id (or public key) - delete from friends list and ignore friend requests - """ - tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] - if tox_id == self.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]: - return - settings = Settings.get_instance() - if tox_id not in settings['blocked']: - settings['blocked'].append(tox_id) - settings.save() - try: - num = self._tox.friend_by_public_key(tox_id) - self.delete_friend(num) - data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) - except: # not in friend list - pass - - def unblock_user(self, tox_id, add_to_friend_list): - """ - Unblock user - :param tox_id: tox id of contact - :param add_to_friend_list: add this contact to friend list or not - """ - s = Settings.get_instance() - s['blocked'].remove(tox_id) - s.save() - if add_to_friend_list: - self.add_friend(tox_id) - data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) - - # ----------------------------------------------------------------------------------------------------------------- - # Friend requests - # ----------------------------------------------------------------------------------------------------------------- - - def send_friend_request(self, tox_id, message): - """ - Function tries to send request to contact with specified id - :param tox_id: id of new contact or tox dns 4 value - :param message: additional message - :return: True on success else error string - """ - try: - message = message or 'Hello! Add me to your contact list please' - if '@' in tox_id: # value like groupbot@toxme.io - tox_id = tox_dns(tox_id) - if tox_id is None: - raise Exception('TOX DNS lookup failed') - if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key - self.add_friend(tox_id) - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "Friend added")) - text = (QtWidgets.QApplication.translate("MainWindow", 'Friend added without sending friend request')) - msgBox.setText(text) - msgBox.exec_() - else: - result = self._tox.friend_add(tox_id, message.encode('utf-8')) - tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] - item = self.create_friend_item() - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) - friend = Friend(message_getter, result, tox_id, '', item, tox_id) - self._contacts.append(friend) - data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) - return True - except Exception as ex: # wrong data - log('Friend request failed with ' + str(ex)) - return str(ex) - - def process_friend_request(self, tox_id, message): - """ - Accept or ignore friend request - :param tox_id: tox id of contact - :param message: message - """ - try: - text = QtWidgets.QApplication.translate('MainWindow', 'User {} wants to add you to contact list. Message:\n{}') - info = text.format(tox_id, message) - fr_req = QtWidgets.QApplication.translate('MainWindow', 'Friend request') - reply = QtWidgets.QMessageBox.question(None, fr_req, info, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: # accepted - self.add_friend(tox_id) - data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) - except Exception as ex: # something is wrong - log('Accept friend request failed! ' + str(ex)) - # ----------------------------------------------------------------------------------------------------------------- # Reset # ----------------------------------------------------------------------------------------------------------------- @@ -900,307 +526,6 @@ class Profile(basecontact.BaseContact, Singleton): s['paused_file_transfers'] = dict(self._paused_file_transfers) if s['resend_files'] else {} s.save() - # ----------------------------------------------------------------------------------------------------------------- - # File transfers support - # ----------------------------------------------------------------------------------------------------------------- - - def incoming_file_transfer(self, friend_number, file_number, size, file_name): - """ - New transfer - :param friend_number: number of friend who sent file - :param file_number: file number - :param size: file size in bytes - :param file_name: file name without path - """ - settings = Settings.get_instance() - friend = self.get_friend_by_number(friend_number) - auto = settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends'] - inline = is_inline(file_name) and settings['allow_inline'] - file_id = self._tox.file_get_file_id(friend_number, file_number) - accepted = True - if file_id in self._paused_file_transfers: - data = self._paused_file_transfers[file_id] - pos = data[-1] if os.path.exists(data[0]) else 0 - if pos >= size: - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) - return - self._tox.file_seek(friend_number, file_number, pos) - self.accept_transfer(None, data[0], friend_number, file_number, size, False, pos) - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - size, - file_name, - friend_number, - file_number) - elif inline and size < 1024 * 1024: - self.accept_transfer(None, '', friend_number, file_number, size, True) - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - size, - file_name, - friend_number, - file_number) - - elif auto: - path = settings['auto_accept_path'] or curr_directory() - self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size) - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - size, - file_name, - friend_number, - file_number) - else: - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'], - size, - file_name, - friend_number, - file_number) - accepted = False - if friend_number == self.get_active_number() and self.is_active_a_friend(): - item = self.create_file_transfer_item(tm) - if accepted: - self._file_transfers[(friend_number, file_number)].set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() - else: - friend.actions = True - - friend.append_message(tm) - - def cancel_transfer(self, friend_number, file_number, already_cancelled=False): - """ - Stop transfer - :param friend_number: number of friend - :param file_number: file number - :param already_cancelled: was cancelled by friend - """ - i = self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['CANCELLED']) - if (friend_number, file_number) in self._file_transfers: - tr = self._file_transfers[(friend_number, file_number)] - if not already_cancelled: - tr.cancel() - else: - tr.cancelled() - if (friend_number, file_number) in self._file_transfers: - del tr - del self._file_transfers[(friend_number, file_number)] - else: - if not already_cancelled: - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) - if friend_number == self.get_active_number() and self.is_active_a_friend(): - tmp = self._messages.count() + i - if tmp >= 0: - self._messages.itemWidget( - self._messages.item(tmp)).update_transfer_state(TOX_FILE_TRANSFER_STATE['CANCELLED'], - 0, -1) - - def cancel_not_started_transfer(self, cancel_time): - self.get_curr_friend().delete_one_unsent_file(cancel_time) - self.update() - - def pause_transfer(self, friend_number, file_number, by_friend=False): - """ - Pause transfer with specified data - """ - tr = self._file_transfers[(friend_number, file_number)] - tr.pause(by_friend) - t = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER'] - self.get_friend_by_number(friend_number).update_transfer_data(file_number, t) - - def resume_transfer(self, friend_number, file_number, by_friend=False): - """ - Resume transfer with specified data - """ - self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['RUNNING']) - tr = self._file_transfers[(friend_number, file_number)] - if by_friend: - tr.state = TOX_FILE_TRANSFER_STATE['RUNNING'] - tr.signal() - else: - tr.send_control(TOX_FILE_CONTROL['RESUME']) - - def accept_transfer(self, item, path, friend_number, file_number, size, inline=False, from_position=0): - """ - :param item: transfer item. - :param path: path for saving - :param friend_number: friend number - :param file_number: file number - :param size: file size - :param inline: is inline image - :param from_position: position for start - """ - path, file_name = os.path.split(path) - new_file_name, i = file_name, 1 - if not from_position: - while os.path.isfile(path + '/' + new_file_name): # file with same name already exists - if '.' in file_name: # has extension - d = file_name.rindex('.') - else: # no extension - d = len(file_name) - new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] - i += 1 - path = os.path.join(path, new_file_name) - if not inline: - rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position) - else: - rt = ReceiveToBuffer(self._tox, friend_number, size, file_number) - rt.set_transfer_finished_handler(self.transfer_finished) - self._file_transfers[(friend_number, file_number)] = rt - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) - if item is not None: - rt.set_state_changed_handler(item.update_transfer_state) - self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['RUNNING']) - - def send_screenshot(self, data): - """ - Send screenshot to current active friend - :param data: raw data - png - """ - self.send_inline(data, 'toxygen_inline.png') - self._messages.repaint() - - def send_sticker(self, path): - with open(path, 'rb') as fl: - data = fl.read() - self.send_inline(data, 'sticker.png') - - def send_inline(self, data, file_name, friend_number=None, is_resend=False): - friend_number = friend_number or self.get_active_number() - friend = self.get_friend_by_number(friend_number) - if friend.status is None and not is_resend: - m = UnsentFile(file_name, data, time.time()) - friend.append_message(m) - self.update() - return - elif friend.status is None and is_resend: - raise RuntimeError() - st = SendFromBuffer(self._tox, friend.number, data, file_name) - st.set_transfer_finished_handler(self.transfer_finished) - self._file_transfers[(friend.number, st.get_file_number())] = st - tm = TransferMessage(MESSAGE_OWNER['ME'], - time.time(), - TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], - len(data), - file_name, - friend.number, - st.get_file_number()) - item = self.create_file_transfer_item(tm) - friend.append_message(tm) - st.set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() - - def send_file(self, path, number=None, is_resend=False, file_id=None): - """ - Send file to current active friend - :param path: file path - :param number: friend_number - :param is_resend: is 'offline' message - :param file_id: file id of transfer - """ - friend_number = self.get_active_number() if number is None else number - friend = self.get_friend_by_number(friend_number) - if friend.status is None and not is_resend: - m = UnsentFile(path, None, time.time()) - friend.append_message(m) - self.update() - return - elif friend.status is None and is_resend: - print('Error in sending') - raise RuntimeError() - st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) - st.set_transfer_finished_handler(self.transfer_finished) - self._file_transfers[(friend_number, st.get_file_number())] = st - tm = TransferMessage(MESSAGE_OWNER['ME'], - time.time(), - TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], - os.path.getsize(path), - os.path.basename(path), - friend_number, - st.get_file_number()) - if friend_number == self.get_active_number(): - item = self.create_file_transfer_item(tm) - st.set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() - self._contacts[friend_number].append_message(tm) - - def incoming_chunk(self, friend_number, file_number, position, data): - """ - Incoming chunk - """ - self._file_transfers[(friend_number, file_number)].write_chunk(position, data) - - def outgoing_chunk(self, friend_number, file_number, position, size): - """ - Outgoing chunk - """ - self._file_transfers[(friend_number, file_number)].send_chunk(position, size) - - def transfer_finished(self, friend_number, file_number): - transfer = self._file_transfers[(friend_number, file_number)] - t = type(transfer) - if t is ReceiveAvatar: - self.get_friend_by_number(friend_number).load_avatar() - if friend_number == self.get_active_number() and self.is_active_a_friend(): - self.set_active(None) - elif t is ReceiveToBuffer or (t is SendFromBuffer and Settings.get_instance()['allow_inline']): # inline image - print('inline') - inline = InlineImage(transfer.get_data()) - i = self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['FINISHED'], - inline) - if friend_number == self.get_active_number() and self.is_active_a_friend(): - count = self._messages.count() - if count + i + 1 >= 0: - elem = QtWidgets.QListWidgetItem() - item = InlineImageItem(transfer.get_data(), self._messages.width(), elem) - elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) - self._messages.insertItem(count + i + 1, elem) - self._messages.setItemWidget(elem, item) - self._messages.scrollToBottom() - elif t is not SendAvatar: - self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['FINISHED']) - del self._file_transfers[(friend_number, file_number)] - del transfer - - # ----------------------------------------------------------------------------------------------------------------- - # Avatars support - # ----------------------------------------------------------------------------------------------------------------- - - def send_avatar(self, friend_number): - """ - :param friend_number: number of friend who should get new avatar - """ - avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if not os.path.isfile(avatar_path): # reset image - avatar_path = None - sa = SendAvatar(avatar_path, self._tox, friend_number) - self._file_transfers[(friend_number, sa.get_file_number())] = sa - - def incoming_avatar(self, friend_number, file_number, size): - """ - Friend changed avatar - :param friend_number: friend number - :param file_number: file number - :param size: size of avatar or 0 (default avatar) - """ - ra = ReceiveAvatar(self._tox, friend_number, size, file_number) - if ra.state != TOX_FILE_TRANSFER_STATE['CANCELLED']: - self._file_transfers[(friend_number, file_number)] = ra - ra.set_transfer_finished_handler(self.transfer_finished) - else: - self.get_friend_by_number(friend_number).load_avatar() - if self.get_active_number() == friend_number and self.is_active_a_friend(): - self.set_active(None) - def reset_avatar(self): super(Profile, self).reset_avatar() for friend in filter(lambda x: x.status is not None, self._contacts): @@ -1210,92 +535,3 @@ class Profile(basecontact.BaseContact, Singleton): super(Profile, self).set_avatar(data) for friend in filter(lambda x: x.status is not None, self._contacts): self.send_avatar(friend.number) - - # ----------------------------------------------------------------------------------------------------------------- - # AV support - # ----------------------------------------------------------------------------------------------------------------- - - def get_call(self): - return self._call - - call = property(get_call) - - def call_click(self, audio=True, video=False): - """User clicked audio button in main window""" - num = self.get_active_number() - if not self.is_active_a_friend(): - return - if num not in self._call and self.is_active_online(): # start call - if not Settings.get_instance().audio['enabled']: - return - self._call(num, audio, video) - self._screen.active_call() - if video: - text = QtWidgets.QApplication.translate("incoming_call", "Outgoing video call") - else: - text = QtWidgets.QApplication.translate("incoming_call", "Outgoing audio call") - self.get_curr_friend().append_message(InfoMessage(text, time.time())) - self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() - elif num in self._call: # finish or cancel call if you call with active friend - self.stop_call(num, False) - - def incoming_call(self, audio, video, friend_number): - """ - Incoming call from friend. - """ - if not Settings.get_instance().audio['enabled']: - return - friend = self.get_friend_by_number(friend_number) - if video: - text = QtWidgets.QApplication.translate("incoming_call", "Incoming video call") - else: - text = QtWidgets.QApplication.translate("incoming_call", "Incoming audio call") - friend.append_message(InfoMessage(text, time.time())) - self._incoming_calls.add(friend_number) - if friend_number == self.get_active_number(): - self._screen.incoming_call() - self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() - else: - friend.actions = True - self._call_widgets[friend_number] = avwidgets.IncomingCallWidget(friend_number, text, friend.name) - self._call_widgets[friend_number].set_pixmap(friend.get_pixmap()) - self._call_widgets[friend_number].show() - - def accept_call(self, friend_number, audio, video): - """ - Accept incoming call with audio or video - """ - self._call.accept_call(friend_number, audio, video) - self._screen.active_call() - if friend_number in self._incoming_calls: - self._incoming_calls.remove(friend_number) - del self._call_widgets[friend_number] - - def stop_call(self, friend_number, by_friend): - """ - Stop call with friend - """ - if friend_number in self._incoming_calls: - self._incoming_calls.remove(friend_number) - text = QtWidgets.QApplication.translate("incoming_call", "Call declined") - else: - text = QtWidgets.QApplication.translate("incoming_call", "Call finished") - self._screen.call_finished() - is_video = self._call.is_video_call(friend_number) - self._call.finish_call(friend_number, by_friend) # finish or decline call - if hasattr(self, '_call_widget'): - self._call_widget[friend_number].close() - del self._call_widget[friend_number] - - def destroy_window(): - if is_video: - cv2.destroyWindow(str(friend_number)) - - threading.Timer(2.0, destroy_window).start() - friend = self.get_friend_by_number(friend_number) - friend.append_message(InfoMessage(text, time.time())) - if friend_number == self.get_active_number(): - self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() diff --git a/toxygen/db/history.py b/toxygen/db/database.py similarity index 92% rename from toxygen/db/history.py rename to toxygen/db/database.py index eea7457..b414e8b 100644 --- a/toxygen/db/history.py +++ b/toxygen/db/database.py @@ -17,22 +17,22 @@ MESSAGE_OWNER = { 'NOT_SENT': 2 } -# TODO: unique message id and ngc support +# TODO: unique message id and ngc support, db name as profile name -class History: +class Database: - def __init__(self, name): + def __init__(self, name, toxes): self._name = name + self._toxes = toxes chdir(settings.ProfileManager.get_path()) path = settings.ProfileManager.get_path() + self._name + '.hstr' if os.path.exists(path): - decr = ToxES.get_instance() try: with open(path, 'rb') as fin: data = fin.read() - if decr.is_data_encrypted(data): - data = decr.pass_decrypt(data) + if toxes.is_data_encrypted(data): + data = toxes.pass_decrypt(data) with open(path, 'wb') as fout: fout.write(data) except: @@ -45,12 +45,11 @@ class History: db.close() def save(self): - encr = ToxES.get_instance() - if encr.has_password(): + if self._toxes.has_password(): path = settings.ProfileManager.get_path() + self._name + '.hstr' with open(path, 'rb') as fin: data = fin.read() - data = encr.pass_encrypt(bytes(data)) + data = self._toxes.pass_encrypt(bytes(data)) with open(path, 'wb') as fout: fout.write(data) @@ -136,8 +135,7 @@ class History: finally: db.close() - def delete_message(self, tox_id, time): - start, end = str(time - 0.01), str(time + 0.01) + def delete_message(self, tox_id, message_id): chdir(settings.ProfileManager.get_path()) db = connect(self._name + '.hstr', timeout=TIMEOUT) try: @@ -165,7 +163,7 @@ class History: db.close() def messages_getter(self, tox_id): - return History.MessageGetter(self._name, tox_id) + return Database.MessageGetter(self._name, tox_id) class MessageGetter: diff --git a/toxygen/file_tansfers/file_transfers_handler.py b/toxygen/file_tansfers/file_transfers_handler.py deleted file mode 100644 index e69de29..0000000 diff --git a/toxygen/file_tansfers/__init__.py b/toxygen/file_transfers/__init__.py similarity index 100% rename from toxygen/file_tansfers/__init__.py rename to toxygen/file_transfers/__init__.py diff --git a/toxygen/file_tansfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py similarity index 100% rename from toxygen/file_tansfers/file_transfers.py rename to toxygen/file_transfers/file_transfers.py diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py new file mode 100644 index 0000000..2706d97 --- /dev/null +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -0,0 +1,311 @@ +from file_transfers.file_transfers import * +from messenger.messages import * +import os + + +class FileTransfersHandler: + + def __init__(self, tox, settings): + self._tox = tox + self._settings = settings + self._file_transfers = {} + + # ----------------------------------------------------------------------------------------------------------------- + # File transfers support + # ----------------------------------------------------------------------------------------------------------------- + + def incoming_file_transfer(self, friend_number, file_number, size, file_name): + """ + New transfer + :param friend_number: number of friend who sent file + :param file_number: file number + :param size: file size in bytes + :param file_name: file name without path + """ + friend = self.get_friend_by_number(friend_number) + auto = self._settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends'] + inline = is_inline(file_name) and settings['allow_inline'] + file_id = self._tox.file_get_file_id(friend_number, file_number) + accepted = True + if file_id in self._paused_file_transfers: + data = self._paused_file_transfers[file_id] + pos = data[-1] if os.path.exists(data[0]) else 0 + if pos >= size: + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + return + self._tox.file_seek(friend_number, file_number, pos) + self.accept_transfer(None, data[0], friend_number, file_number, size, False, pos) + tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + time.time(), + TOX_FILE_TRANSFER_STATE['RUNNING'], + size, + file_name, + friend_number, + file_number) + elif inline and size < 1024 * 1024: + self.accept_transfer(None, '', friend_number, file_number, size, True) + tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + time.time(), + TOX_FILE_TRANSFER_STATE['RUNNING'], + size, + file_name, + friend_number, + file_number) + + elif auto: + path = settings['auto_accept_path'] or curr_directory() + self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size) + tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + time.time(), + TOX_FILE_TRANSFER_STATE['RUNNING'], + size, + file_name, + friend_number, + file_number) + else: + tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + time.time(), + TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'], + size, + file_name, + friend_number, + file_number) + accepted = False + if friend_number == self.get_active_number() and self.is_active_a_friend(): + item = self.create_file_transfer_item(tm) + if accepted: + self._file_transfers[(friend_number, file_number)].set_state_changed_handler(item.update_transfer_state) + self._messages.scrollToBottom() + else: + friend.actions = True + + friend.append_message(tm) + + def cancel_transfer(self, friend_number, file_number, already_cancelled=False): + """ + Stop transfer + :param friend_number: number of friend + :param file_number: file number + :param already_cancelled: was cancelled by friend + """ + i = self.get_friend_by_number(friend_number).update_transfer_data(file_number, + TOX_FILE_TRANSFER_STATE['CANCELLED']) + if (friend_number, file_number) in self._file_transfers: + tr = self._file_transfers[(friend_number, file_number)] + if not already_cancelled: + tr.cancel() + else: + tr.cancelled() + if (friend_number, file_number) in self._file_transfers: + del tr + del self._file_transfers[(friend_number, file_number)] + else: + if not already_cancelled: + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + if friend_number == self.get_active_number() and self.is_active_a_friend(): + tmp = self._messages.count() + i + if tmp >= 0: + self._messages.itemWidget( + self._messages.item(tmp)).update_transfer_state(TOX_FILE_TRANSFER_STATE['CANCELLED'], + 0, -1) + + def cancel_not_started_transfer(self, cancel_time): + self.get_curr_friend().delete_one_unsent_file(cancel_time) + self.update() + + def pause_transfer(self, friend_number, file_number, by_friend=False): + """ + Pause transfer with specified data + """ + tr = self._file_transfers[(friend_number, file_number)] + tr.pause(by_friend) + t = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER'] + self.get_friend_by_number(friend_number).update_transfer_data(file_number, t) + + def resume_transfer(self, friend_number, file_number, by_friend=False): + """ + Resume transfer with specified data + """ + self.get_friend_by_number(friend_number).update_transfer_data(file_number, + TOX_FILE_TRANSFER_STATE['RUNNING']) + tr = self._file_transfers[(friend_number, file_number)] + if by_friend: + tr.state = TOX_FILE_TRANSFER_STATE['RUNNING'] + tr.signal() + else: + tr.send_control(TOX_FILE_CONTROL['RESUME']) + + def accept_transfer(self, item, path, friend_number, file_number, size, inline=False, from_position=0): + """ + :param item: transfer item. + :param path: path for saving + :param friend_number: friend number + :param file_number: file number + :param size: file size + :param inline: is inline image + :param from_position: position for start + """ + path, file_name = os.path.split(path) + new_file_name, i = file_name, 1 + if not from_position: + while os.path.isfile(path + '/' + new_file_name): # file with same name already exists + if '.' in file_name: # has extension + d = file_name.rindex('.') + else: # no extension + d = len(file_name) + new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] + i += 1 + path = os.path.join(path, new_file_name) + if not inline: + rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position) + else: + rt = ReceiveToBuffer(self._tox, friend_number, size, file_number) + rt.set_transfer_finished_handler(self.transfer_finished) + self._file_transfers[(friend_number, file_number)] = rt + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) + if item is not None: + rt.set_state_changed_handler(item.update_transfer_state) + self.get_friend_by_number(friend_number).update_transfer_data(file_number, + TOX_FILE_TRANSFER_STATE['RUNNING']) + + def send_screenshot(self, data): + """ + Send screenshot to current active friend + :param data: raw data - png + """ + self.send_inline(data, 'toxygen_inline.png') + self._messages.repaint() + + def send_sticker(self, path): + with open(path, 'rb') as fl: + data = fl.read() + self.send_inline(data, 'sticker.png') + + def send_inline(self, data, file_name, friend_number=None, is_resend=False): + friend_number = friend_number or self.get_active_number() + friend = self.get_friend_by_number(friend_number) + if friend.status is None and not is_resend: + m = UnsentFile(file_name, data, time.time()) + friend.append_message(m) + self.update() + return + elif friend.status is None and is_resend: + raise RuntimeError() + st = SendFromBuffer(self._tox, friend.number, data, file_name) + st.set_transfer_finished_handler(self.transfer_finished) + self._file_transfers[(friend.number, st.get_file_number())] = st + tm = TransferMessage(MESSAGE_OWNER['ME'], + time.time(), + TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], + len(data), + file_name, + friend.number, + st.get_file_number()) + item = self.create_file_transfer_item(tm) + friend.append_message(tm) + st.set_state_changed_handler(item.update_transfer_state) + self._messages.scrollToBottom() + + def send_file(self, path, number=None, is_resend=False, file_id=None): + """ + Send file to current active friend + :param path: file path + :param number: friend_number + :param is_resend: is 'offline' message + :param file_id: file id of transfer + """ + friend_number = self.get_active_number() if number is None else number + friend = self.get_friend_by_number(friend_number) + if friend.status is None and not is_resend: + m = UnsentFile(path, None, time.time()) + friend.append_message(m) + self.update() + return + elif friend.status is None and is_resend: + print('Error in sending') + raise RuntimeError() + st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) + st.set_transfer_finished_handler(self.transfer_finished) + self._file_transfers[(friend_number, st.get_file_number())] = st + tm = TransferMessage(MESSAGE_OWNER['ME'], + time.time(), + TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], + os.path.getsize(path), + os.path.basename(path), + friend_number, + st.get_file_number()) + if friend_number == self.get_active_number(): + item = self.create_file_transfer_item(tm) + st.set_state_changed_handler(item.update_transfer_state) + self._messages.scrollToBottom() + self._contacts[friend_number].append_message(tm) + + def incoming_chunk(self, friend_number, file_number, position, data): + """ + Incoming chunk + """ + self._file_transfers[(friend_number, file_number)].write_chunk(position, data) + + def outgoing_chunk(self, friend_number, file_number, position, size): + """ + Outgoing chunk + """ + self._file_transfers[(friend_number, file_number)].send_chunk(position, size) + + def transfer_finished(self, friend_number, file_number): + transfer = self._file_transfers[(friend_number, file_number)] + t = type(transfer) + if t is ReceiveAvatar: + self.get_friend_by_number(friend_number).load_avatar() + if friend_number == self.get_active_number() and self.is_active_a_friend(): + self.set_active(None) + elif t is ReceiveToBuffer or (t is SendFromBuffer and Settings.get_instance()['allow_inline']): # inline image + print('inline') + inline = InlineImage(transfer.get_data()) + i = self.get_friend_by_number(friend_number).update_transfer_data(file_number, + TOX_FILE_TRANSFER_STATE['FINISHED'], + inline) + if friend_number == self.get_active_number() and self.is_active_a_friend(): + count = self._messages.count() + if count + i + 1 >= 0: + elem = QtWidgets.QListWidgetItem() + item = InlineImageItem(transfer.get_data(), self._messages.width(), elem) + elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) + self._messages.insertItem(count + i + 1, elem) + self._messages.setItemWidget(elem, item) + self._messages.scrollToBottom() + elif t is not SendAvatar: + self.get_friend_by_number(friend_number).update_transfer_data(file_number, + TOX_FILE_TRANSFER_STATE['FINISHED']) + del self._file_transfers[(friend_number, file_number)] + del transfer + + # ----------------------------------------------------------------------------------------------------------------- + # Avatars support + # ----------------------------------------------------------------------------------------------------------------- + + def send_avatar(self, friend_number): + """ + :param friend_number: number of friend who should get new avatar + """ + avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + if not os.path.isfile(avatar_path): # reset image + avatar_path = None + sa = SendAvatar(avatar_path, self._tox, friend_number) + self._file_transfers[(friend_number, sa.get_file_number())] = sa + + def incoming_avatar(self, friend_number, file_number, size): + """ + Friend changed avatar + :param friend_number: friend number + :param file_number: file number + :param size: size of avatar or 0 (default avatar) + """ + ra = ReceiveAvatar(self._tox, friend_number, size, file_number) + if ra.state != TOX_FILE_TRANSFER_STATE['CANCELLED']: + self._file_transfers[(friend_number, file_number)] = ra + ra.set_transfer_finished_handler(self.transfer_finished) + else: + self.get_friend_by_number(friend_number).load_avatar() + if self.get_active_number() == friend_number and self.is_active_a_friend(): + self.set_active(None) diff --git a/toxygen/main.py b/toxygen/main.py index c8f18f6..e8a4694 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,12 +1,10 @@ import sys +import app from user_data.settings import * -from util import curr_directory, program_version, remove +from util.util import curr_directory, program_version, remove import argparse - - - def clean(): """Removes all windows libs from libs folder""" d = curr_directory() + '/libs/' @@ -24,7 +22,7 @@ def main(): parser.add_argument('--reset') args = parser.parse_args() if not len(args): - toxygen = Toxygen() + toxygen = app.App() else: # started with argument(s) arg = sys.argv[1] if arg == '--version': @@ -40,7 +38,7 @@ def main(): reset() return else: - toxygen = Toxygen(arg) + toxygen = app.App(arg) toxygen.main() diff --git a/toxygen/notifications.py b/toxygen/notifications.py index 26a29ec..cf926d1 100644 --- a/toxygen/notifications.py +++ b/toxygen/notifications.py @@ -1,5 +1,5 @@ from PyQt5 import QtCore, QtWidgets -from util import curr_directory +from util.util import curr_directory import wave import pyaudio diff --git a/toxygen/smileys_and_stickers.py b/toxygen/smileys_and_stickers.py index 52cb603..7294185 100644 --- a/toxygen/smileys_and_stickers.py +++ b/toxygen/smileys_and_stickers.py @@ -1,4 +1,4 @@ -import util +from util import util import json import os from collections import OrderedDict diff --git a/toxygen/threads.py b/toxygen/threads.py index a79297a..f437d5a 100644 --- a/toxygen/threads.py +++ b/toxygen/threads.py @@ -1,3 +1,11 @@ +from PyQt5 import QtCore +from communication.callbacks import init_callbacks +from bootstrap.bootstrap import * +import threading +import queue +from util import util + + class InitThread(QtCore.QThread): def __init__(self, tox, ms, tray): @@ -70,8 +78,8 @@ class FileTransfersThread(threading.Thread): self._continue = True super().__init__() - def execute(self, function, *args, **kwargs): - self._queue.put((function, args, kwargs)) + def execute(self, func, *args, **kwargs): + self._queue.put((func, args, kwargs)) def stop(self): self._continue = False @@ -79,12 +87,12 @@ class FileTransfersThread(threading.Thread): def run(self): while self._continue: try: - function, args, kwargs = self._queue.get(timeout=self._timeout) - function(*args, **kwargs) + func, args, kwargs = self._queue.get(timeout=self._timeout) + func(*args, **kwargs) except queue.Empty: pass except queue.Full: - util.log('Queue is Full in _thread') + util.log('Queue is full in _thread') except Exception as ex: util.log('Exception in _thread: ' + str(ex)) @@ -101,6 +109,8 @@ def stop(): _thread.join() +def execute(func, *args, **kwargs): + _thread.execute(func, *args, **kwargs) class InvokeEvent(QtCore.QEvent): @@ -125,3 +135,4 @@ _invoker = Invoker() def invoke_in_main_thread(fn, *args, **kwargs): QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs)) + diff --git a/toxygen/tray.py b/toxygen/tray.py index ffe9aeb..bc22ed4 100644 --- a/toxygen/tray.py +++ b/toxygen/tray.py @@ -1,83 +1,95 @@ -self.tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) - self.tray.setObjectName('tray') +from PyQt5 import QtWidgets, QtGui, QtCore +from util.ui import tr +from util.util import curr_directory - class Menu(QtWidgets.QMenu): - def newStatus(self, status): - if not Settings.get_instance().locked: - profile.Profile.get_instance().set_status(status) - self.aboutToShowHandler() - self.hide() +class Menu(QtWidgets.QMenu): - def aboutToShowHandler(self): - status = profile.Profile.get_instance().status - act = self.act - if status is None or Settings.get_instance().locked: - self.actions()[1].setVisible(False) - else: - self.actions()[1].setVisible(True) - act.actions()[0].setChecked(False) - act.actions()[1].setChecked(False) - act.actions()[2].setChecked(False) - act.actions()[status].setChecked(True) - self.actions()[2].setVisible(not Settings.get_instance().locked) + def __init__(self, settings, profile, *args): + super().__init__(*args) + self._settings = settings + self._profile = profile - def languageChange(self, *args, **kwargs): - self.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) - self.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Set status')) - self.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Exit')) - self.act.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Online')) - self.act.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Away')) - self.act.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Busy')) + def newStatus(self, status): + if not self._settings.locked: + self._profile.Profile.get_instance().set_status(status) + self.aboutToShowHandler() + self.hide() - m = Menu() - show = m.addAction(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) - sub = m.addMenu(QtWidgets.QApplication.translate('tray', 'Set status')) - onl = sub.addAction(QtWidgets.QApplication.translate('tray', 'Online')) - away = sub.addAction(QtWidgets.QApplication.translate('tray', 'Away')) - busy = sub.addAction(QtWidgets.QApplication.translate('tray', 'Busy')) - onl.setCheckable(True) - away.setCheckable(True) - busy.setCheckable(True) - m.act = sub - exit = m.addAction(QtWidgets.QApplication.translate('tray', 'Exit')) + def aboutToShowHandler(self): + status = self._profile.status + act = self.act + if status is None or self._ettings.get_instance().locked: + self.actions()[1].setVisible(False) + else: + self.actions()[1].setVisible(True) + act.actions()[0].setChecked(False) + act.actions()[1].setChecked(False) + act.actions()[2].setChecked(False) + act.actions()[status].setChecked(True) + self.actions()[2].setVisible(not self._settings.locked) - def show_window(): - s = Settings.get_instance() + def languageChange(self, *args, **kwargs): + self.actions()[0].setText(tr('Open Toxygen')) + self.actions()[1].setText(tr('Set status')) + self.actions()[2].setText(tr('Exit')) + self.act.actions()[0].setText(tr('Online')) + self.act.actions()[1].setText(tr('Away')) + self.act.actions()[2].setText(tr('Busy')) - def show(): - if not self.ms.isActiveWindow(): - self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) - self.ms.activateWindow() - self.ms.show() - if not s.locked: + +def init_tray(profile, settings, main_screen): + tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) + tray.setObjectName('tray') + + m = Menu(settings, profile) + show = m.addAction(tr('Open Toxygen')) + sub = m.addMenu(tr('Set status')) + online = sub.addAction(tr('Online')) + away = sub.addAction(tr('Away')) + busy = sub.addAction(tr('Busy')) + online.setCheckable(True) + away.setCheckable(True) + busy.setCheckable(True) + m.act = sub + exit = m.addAction(tr('Exit')) + + def show_window(): + def show(): + if not main_screen.isActiveWindow(): + main_screen.setWindowState(main_screen.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + main_screen.activateWindow() + main_screen.show() + if not settings.locked: + show() + else: + def correct_pass(): show() - else: - def correct_pass(): - show() - s.locked = False - s.unlockScreen = False - if not s.unlockScreen: - s.unlockScreen = True - self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass) - self.p.show() + settings.locked = False + settings.unlockScreen = False + if not settings.unlockScreen: + settings.unlockScreen = True + self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass) + self.p.show() - def tray_activated(reason): - if reason == QtWidgets.QSystemTrayIcon.DoubleClick: - show_window() + def tray_activated(reason): + if reason == QtWidgets.QSystemTrayIcon.DoubleClick: + show_window() - def close_app(): - if not Settings.get_instance().locked: - settings.closing = True - self.ms.close() + def close_app(): + if not settings.locked: + settings.closing = True + main_screen.close() - show.triggered.connect(show_window) - exit.triggered.connect(close_app) - m.aboutToShow.connect(lambda: m.aboutToShowHandler()) - onl.triggered.connect(lambda: m.newStatus(0)) - away.triggered.connect(lambda: m.newStatus(1)) - busy.triggered.connect(lambda: m.newStatus(2)) + show.triggered.connect(show_window) + exit.triggered.connect(close_app) + m.aboutToShow.connect(lambda: m.aboutToShowHandler()) + online.triggered.connect(lambda: m.newStatus(0)) + away.triggered.connect(lambda: m.newStatus(1)) + busy.triggered.connect(lambda: m.newStatus(2)) - self.tray.setContextMenu(m) - self.tray.show() - self.tray.activated.connect(tray_activated) \ No newline at end of file + tray.setContextMenu(m) + tray.show() + tray.activated.connect(tray_activated) + + return tray diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py index f6ab154..64b4a2e 100644 --- a/toxygen/ui/list_items.py +++ b/toxygen/ui/list_items.py @@ -1,7 +1,7 @@ from wrapper.toxcore_enums_and_consts import * from PyQt5 import QtCore, QtGui, QtWidgets from contacts import profile -from file_tansfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR +from file_transfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR from util import curr_directory, convert_time, curr_time from ui.widgets import DataLabel, create_menu import html as h diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index 96f591c..12ce185 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -1,4 +1,4 @@ -class ProfileManager(): +class ProfileManager: """ Class with methods for search, load and save profiles """ diff --git a/toxygen/util/ui.py b/toxygen/util/ui.py new file mode 100644 index 0000000..a0d950d --- /dev/null +++ b/toxygen/util/ui.py @@ -0,0 +1,8 @@ +import PyQt5 + + +def tr(s): + return PyQt5.QtWidgets.QApplication.translate('Toxygen', s) + + +# TODO: move all dialogs here diff --git a/toxygen/util/util.py b/toxygen/util/util.py index bb27da6..33b4758 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -29,9 +29,8 @@ def log(data): pass -@cached -def curr_directory(): - return os.path.dirname(os.path.realpath(__file__)) +def curr_directory(current_file=None): + return os.path.dirname(os.path.realpath(current_file or __file__)) def curr_time(): From 1bead7d55d3b2a2c8a90e2c9df0560792d8ecbf7 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 12 Mar 2018 00:32:46 +0300 Subject: [PATCH 004/217] history improvements --- toxygen/communication/callbacks.py | 27 ++--- toxygen/contacts/contact.py | 6 +- toxygen/contacts/contacts_manager.py | 6 + toxygen/contacts/profile.py | 169 ++------------------------- toxygen/{db => history}/__init__.py | 0 toxygen/{db => history}/database.py | 2 +- toxygen/history/history_loader.py | 134 +++++++++++++++++++++ toxygen/messenger/messages.py | 34 +++--- 8 files changed, 189 insertions(+), 189 deletions(-) rename toxygen/{db => history}/__init__.py (100%) rename toxygen/{db => history}/database.py (99%) create mode 100644 toxygen/history/history_loader.py diff --git a/toxygen/communication/callbacks.py b/toxygen/communication/callbacks.py index e27bd74..ac93173 100644 --- a/toxygen/communication/callbacks.py +++ b/toxygen/communication/callbacks.py @@ -102,10 +102,10 @@ def friend_status_message(profile): def friend_message(profile, settings, window, tray): - """ - New message from friend - """ def wrapped(tox, friend_number, message_type, message, size, user_data): + """ + New message from friend + """ message = str(message, 'utf-8') invoke_in_main_thread(profile.new_message, friend_number, message_type, message) if not window.isActiveWindow(): @@ -119,16 +119,17 @@ def friend_message(profile, settings, window, tray): return wrapped -def friend_request(tox, public_key, message, message_size, user_data): - """ - Called when user get new friend request - """ - print('Friend request') - profile = Profile.get_instance() - key = ''.join(chr(x) for x in public_key[:TOX_PUBLIC_KEY_SIZE]) - tox_id = bin_to_string(key, TOX_PUBLIC_KEY_SIZE) - if tox_id not in Settings.get_instance()['blocked']: - invoke_in_main_thread(profile.process_friend_request, tox_id, str(message, 'utf-8')) +def friend_request(contacts_manager): + def wrapped(tox, public_key, message, message_size, user_data): + """ + Called when user get new friend request + """ + print('Friend request') + key = ''.join(chr(x) for x in public_key[:TOX_PUBLIC_KEY_SIZE]) + tox_id = bin_to_string(key, TOX_PUBLIC_KEY_SIZE) + invoke_in_main_thread(contacts_manager.process_friend_request, tox_id, str(message, 'utf-8')) + + return wrapped def friend_typing(tox, friend_number, typing, user_data): diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 96ff804..22191a7 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -1,4 +1,4 @@ -from db.database import * +from history.database import * from contacts import basecontact import util from messenger.messages import * @@ -124,8 +124,8 @@ class Contact(basecontact.BaseContact): # Message deletion # ----------------------------------------------------------------------------------------------------------------- - def delete_message(self, time): - elem = list(filter(lambda x: type(x) in (TextMessage, GroupChatMessage) and x.get_data()[2] == time, self._corr))[0] + def delete_message(self, message_id): + elem = list(filter(lambda x: type(x) in (TextMessage, GroupChatMessage) and x.message_id == message_id, self._corr))[0] tmp = list(filter(lambda x: x.get_type() <= 1, self._corr)) if elem in tmp[-self._unsaved_messages:] and self._unsaved_messages: self._unsaved_messages -= 1 diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index cbac1e1..7653b56 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -152,6 +152,10 @@ class ContactsManager: active_friend = property(get_active, set_active) + def update(self): + if self._active_friend + 1: + self.set_active(self._active_friend) + # ----------------------------------------------------------------------------------------------------------------- # Filtration # ----------------------------------------------------------------------------------------------------------------- @@ -389,6 +393,8 @@ class ContactsManager: :param tox_id: tox id of contact :param message: message """ + if tox_id in self._settings['blocked']: + return try: text = QtWidgets.QApplication.translate('MainWindow', 'User {} wants to add you to contact list. Message:\n{}') info = text.format(tox_id, message) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 483d96b..9d33f19 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -45,7 +45,6 @@ class Profile(basecontact.BaseContact, Singleton): self._show_avatars = settings['show_avatars'] self._paused_file_transfers = dict(settings['paused_file_transfers']) # key - file id, value: [path, friend number, is incoming, start position] - self._history = History(tox.self_get_public_key()) # connection to db # ----------------------------------------------------------------------------------------------------------------- @@ -128,10 +127,6 @@ class Profile(basecontact.BaseContact, Singleton): self._messages.scrollToBottom() self.set_active(None) - def update(self): - if self._active_friend + 1: - self.set_active(self._active_friend) - # ----------------------------------------------------------------------------------------------------------------- # Friend connection status callbacks # ----------------------------------------------------------------------------------------------------------------- @@ -242,29 +237,6 @@ class Profile(basecontact.BaseContact, Singleton): return messages - def split_and_send(self, number, message_type, message): - """ - Message splitting. Message length cannot be > TOX_MAX_MESSAGE_LENGTH - :param number: friend's number - :param message_type: type of message - :param message: message text - """ - while len(message) > TOX_MAX_MESSAGE_LENGTH: - size = TOX_MAX_MESSAGE_LENGTH * 4 // 5 - last_part = message[size:TOX_MAX_MESSAGE_LENGTH] - if b' ' in last_part: - index = last_part.index(b' ') - elif b',' in last_part: - index = last_part.index(b',') - elif b'.' in last_part: - index = last_part.index(b'.') - else: - index = TOX_MAX_MESSAGE_LENGTH - size - 1 - index += size + 1 - self._tox.friend_send_message(number, message_type, message[:index]) - message = message[index:] - self._tox.friend_send_message(number, message_type, message) - def new_message(self, friend_num, message_type, message): """ Current user gets new message @@ -286,30 +258,29 @@ class Profile(basecontact.BaseContact, Singleton): if not friend.visibility: self.update_filtration() - def send_message(self, text, friend_num=None): + def send_message_to_friend(self, text, friend_number=None): """ Send message :param text: message text - :param friend_num: num of friend + :param friend_number: number of friend """ - if not self.is_active_a_friend(): - self.send_gc_message(text) - return - if friend_num is None: - friend_num = self.get_active_number() + if friend_number is None: + friend_number = self.get_active_number() if text.startswith('/plugin '): - plugin_support.PluginLoader.get_instance().command(text[8:]) + self._plugin_loader.command(text[8:]) self._screen.messageEdit.clear() - elif text and friend_num + 1: + elif text and friend_number >= 0: if text.startswith('/me '): message_type = TOX_MESSAGE_TYPE['ACTION'] text = text[4:] else: message_type = TOX_MESSAGE_TYPE['NORMAL'] - friend = self.get_friend_by_number(friend_num) + friend = self.get_friend_by_number(friend_number) friend.inc_receipts() if friend.status is not None: - self.split_and_send(friend.number, message_type, text.encode('utf-8')) + messages = self.split_message(text.encode('utf-8')) + for message in messages: + self._tox.friend_send_message(friend_number, message_type, message) t = time.time() if friend.number == self.get_active_number() and self.is_active_a_friend(): self.create_message_item(text, t, MESSAGE_OWNER['NOT_SENT'], message_type) @@ -317,128 +288,12 @@ class Profile(basecontact.BaseContact, Singleton): self._messages.scrollToBottom() friend.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], t, message_type)) - def delete_message(self, time): + def delete_message(self, message_id): friend = self.get_curr_friend() friend.delete_message(time) - self._history.delete_message(friend.tox_id, time) + self._history.delete_message(friend.tox_id, message_id) self.update() - # ----------------------------------------------------------------------------------------------------------------- - # History support - # ----------------------------------------------------------------------------------------------------------------- - - def save_history(self): - """ - Save history to db - """ - s = Settings.get_instance() - if hasattr(self, '_history'): - if s['save_history']: - for friend in filter(lambda x: type(x) is Friend, self._contacts): - if not self._history.friend_exists_in_db(friend.tox_id): - self._history.add_friend_to_db(friend.tox_id) - if not s['save_unsent_only']: - messages = friend.get_corr_for_saving() - else: - messages = friend.get_unsent_messages_for_saving() - self._history.delete_messages(friend.tox_id) - self._history.save_messages_to_db(friend.tox_id, messages) - unsent_messages = friend.get_unsent_messages() - unsent_time = unsent_messages[0].get_data()[2] if len(unsent_messages) else time.time() + 1 - self._history.update_messages(friend.tox_id, unsent_time) - self._history.save() - del self._history - - def clear_history(self, num=None, save_unsent=False): - """ - Clear chat history - """ - if num is not None: - friend = self._contacts[num] - friend.clear_corr(save_unsent) - if self._history.friend_exists_in_db(friend.tox_id): - self._history.delete_messages(friend.tox_id) - self._history.delete_friend_from_db(friend.tox_id) - else: # clear all history - for number in range(len(self._contacts)): - self.clear_history(number, save_unsent) - if num is None or num == self.get_active_number(): - self.update() - - def load_history(self): - """ - Tries to load next part of messages - """ - if not self._load_history: - return - self._load_history = False - friend = self.get_curr_friend() - friend.load_corr(False) - data = friend.get_corr() - if not data: - return - data.reverse() - data = data[self._messages.count():self._messages.count() + PAGE_SIZE] - for message in data: - if message.get_type() <= 1: # text message - data = message.get_data() - self.create_message_item(data[0], - data[2], - data[1], - data[3], - False) - elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: # file transfer - if message.get_status() is None: - self.create_unsent_file_item(message) - continue - item = self.create_file_transfer_item(message, False) - if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer - try: - ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] - ft.set_state_changed_handler(item.update_transfer_state) - ft.signal() - except: - print('Incoming not started transfer - no info found') - elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline image - self.create_inline_item(message.get_data(), False) - else: # info message - data = message.get_data() - self.create_message_item(data[0], - data[2], - '', - data[3], - False) - self._load_history = True - - def export_db(self, directory): - self._history.export(directory) - - def export_history(self, num, as_text=True, _range=None): - friend = self._contacts[num] - if _range is None: - friend.load_all_corr() - corr = friend.get_corr() - elif _range[1] + 1: - corr = friend.get_corr()[_range[0]:_range[1] + 1] - else: - corr = friend.get_corr()[_range[0]:] - arr = [] - new_line = '\n' if as_text else '
' - for message in corr: - if type(message) is TextMessage: - data = message.get_data() - if as_text: - x = '[{}] {}: {}\n' - else: - x = '[{}] {}: {}
' - arr.append(x.format(convert_time(data[2]) if data[1] != MESSAGE_OWNER['NOT_SENT'] else 'Unsent', - friend.name if data[1] == MESSAGE_OWNER['FRIEND'] else self.name, - data[0])) - s = new_line.join(arr) - if not as_text: - s = '{}{}'.format(friend.name, s) - return s - # ----------------------------------------------------------------------------------------------------------------- # Friend, message and file transfer items creation # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/db/__init__.py b/toxygen/history/__init__.py similarity index 100% rename from toxygen/db/__init__.py rename to toxygen/history/__init__.py diff --git a/toxygen/db/database.py b/toxygen/history/database.py similarity index 99% rename from toxygen/db/database.py rename to toxygen/history/database.py index b414e8b..ff9ce8e 100644 --- a/toxygen/db/database.py +++ b/toxygen/history/database.py @@ -9,7 +9,7 @@ PAGE_SIZE = 42 TIMEOUT = 11 -SAVE_MESSAGES = 250 +SAVE_MESSAGES = 500 MESSAGE_OWNER = { 'ME': 0, diff --git a/toxygen/history/history_loader.py b/toxygen/history/history_loader.py new file mode 100644 index 0000000..86e61c5 --- /dev/null +++ b/toxygen/history/history_loader.py @@ -0,0 +1,134 @@ +from messenger.messages import * + + +class HistoryLoader: + + def __init__(self, db, settings): + self._db = db + self._settings = settings + + # ----------------------------------------------------------------------------------------------------------------- + # History support + # ----------------------------------------------------------------------------------------------------------------- + + def save_history(self): + """ + Save history to db + """ + if hasattr(self, '_history'): + if self._settings['save_history']: + for friend in filter(lambda x: type(x) is Friend, self._contacts): + if not self._history.friend_exists_in_db(friend.tox_id): + self._history.add_friend_to_db(friend.tox_id) + if not self._settings['save_unsent_only']: + messages = friend.get_corr_for_saving() + else: + messages = friend.get_unsent_messages_for_saving() + self._history.delete_messages(friend.tox_id) + self._history.save_messages_to_db(friend.tox_id, messages) + unsent_messages = friend.get_unsent_messages() + unsent_time = unsent_messages[0].get_data()[2] if len(unsent_messages) else time.time() + 1 + self._history.update_messages(friend.tox_id, unsent_time) + self._history.save() + del self._history + + def clear_history(self, friend, save_unsent=False): + """ + Clear chat history + """ + friend.clear_corr(save_unsent) + if self._db.friend_exists_in_db(friend.tox_id): + self._db.delete_messages(friend.tox_id) + self._db.delete_friend_from_db(friend.tox_id) + + def load_history(self): + """ + Tries to load next part of messages + """ + if not self._load_history: + return + self._load_history = False + friend = self.get_curr_friend() + friend.load_corr(False) + data = friend.get_corr() + if not data: + return + data.reverse() + data = data[self._messages.count():self._messages.count() + PAGE_SIZE] + for message in data: + if message.get_type() <= 1: # text message + data = message.get_data() + self.create_message_item(data[0], + data[2], + data[1], + data[3], + False) + elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: # file transfer + if message.get_status() is None: + self.create_unsent_file_item(message) + continue + item = self.create_file_transfer_item(message, False) + if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer + try: + ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] + ft.set_state_changed_handler(item.update_transfer_state) + ft.signal() + except: + print('Incoming not started transfer - no info found') + elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline image + self.create_inline_item(message.get_data(), False) + else: # info message + data = message.get_data() + self.create_message_item(data[0], + data[2], + '', + data[3], + False) + self._load_history = True + + def export_history(self, friend, as_text=True, _range=None): + if _range is None: + friend.load_all_corr() + corr = friend.get_corr() + elif _range[1] + 1: + corr = friend.get_corr()[_range[0]:_range[1] + 1] + else: + corr = friend.get_corr()[_range[0]:] + + generator = TextHistoryGenerator() if as_text else HtmlHistoryGenerator + + return generator.generate(corr) + + +class HtmlHistoryGenerator: + + def generate(self, corr): + arr = [] + for message in corr: + if type(message) is TextMessage: + data = message.get_data() + x = '[{}] {}: {}
' + arr.append(x.format(convert_time(data[2]) if data[1] != MESSAGE_OWNER['NOT_SENT'] else 'Unsent', + friend.name if data[1] == MESSAGE_OWNER['FRIEND'] else self.name, + data[0])) + s = '
'.join(arr) + s = '{}{}'.format(friend.name, + s) + return s + + +class TextHistoryGenerator: + + def generate(self, corr): + arr = [] + for message in corr: + if type(message) is TextMessage: + data = message.get_data() + x = '[{}] {}: {}\n' + arr.append(x.format(convert_time(data[2]) if data[1] != MESSAGE_OWNER['NOT_SENT'] else 'Unsent', + friend.name if data[1] == MESSAGE_OWNER['FRIEND'] else self.name, + data[0])) + s = '\n'.join(arr) + + return s + diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 8d9f4a3..4baa29a 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -5,18 +5,17 @@ MESSAGE_TYPE = { 'ACTION': 1, 'FILE_TRANSFER': 2, 'INLINE': 3, - 'INFO_MESSAGE': 4, - 'GC_TEXT': 5, - 'GC_ACTION': 6 + 'INFO_MESSAGE': 4 } class Message: - def __init__(self, message_type, owner, time): + def __init__(self, message_id, message_type, owner, time): self._time = time self._type = message_type self._owner = owner + self._message_id = message_id def get_type(self): return self._type @@ -27,14 +26,19 @@ class Message: def mark_as_sent(self): self._owner = 0 + def get_message_id(self): + return self._message_id + + message_id = property(get_message_id) + class TextMessage(Message): """ Plain text or action message """ - def __init__(self, message, owner, time, message_type): - super(TextMessage, self).__init__(message_type, owner, time) + def __init__(self, id, message, owner, time, message_type): + super(TextMessage, self).__init__(id, message_type, owner, time) self._message = message def get_data(self): @@ -43,8 +47,8 @@ class TextMessage(Message): class GroupChatMessage(TextMessage): - def __init__(self, message, owner, time, message_type, name): - super().__init__(message, owner, time, message_type) + def __init__(self, id, message, owner, time, message_type, name): + super().__init__(id, message, owner, time, message_type) self._user_name = name def get_data(self): @@ -56,7 +60,7 @@ class TransferMessage(Message): Message with info about file transfer """ - def __init__(self, owner, time, status, size, name, friend_number, file_number): + def __init__(self, id, owner, time, status, size, name, friend_number, file_number): super(TransferMessage, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time) self._status = status self._size = size @@ -83,8 +87,8 @@ class TransferMessage(Message): class UnsentFile(Message): - def __init__(self, path, data, time): - super(UnsentFile, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], 0, time) + def __init__(self, id, path, data, time): + super(UnsentFile, self).__init__(id, MESSAGE_TYPE['FILE_TRANSFER'], 0, time) self._data, self._path = data, path def get_data(self): @@ -99,8 +103,8 @@ class InlineImage(Message): Inline image """ - def __init__(self, data): - super(InlineImage, self).__init__(MESSAGE_TYPE['INLINE'], None, None) + def __init__(self, id, data): + super(InlineImage, self).__init__(id, MESSAGE_TYPE['INLINE'], None, None) self._data = data def get_data(self): @@ -109,5 +113,5 @@ class InlineImage(Message): class InfoMessage(TextMessage): - def __init__(self, message, time): - super(InfoMessage, self).__init__(message, None, time, MESSAGE_TYPE['INFO_MESSAGE']) + def __init__(self, id, message, time): + super(InfoMessage, self).__init__(id, message, None, time, MESSAGE_TYPE['INFO_MESSAGE']) From 85467e1885daa22c95918b10ac790e5be386bfe1 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 16 Apr 2018 00:11:51 +0300 Subject: [PATCH 005/217] refactoring - login screen, incorrect refs --- toxygen/app.py | 74 ++++--- toxygen/communication/callbacks.py | 201 ++++++++---------- toxygen/contacts/profile.py | 11 +- .../file_transfers/file_transfers_handler.py | 2 + toxygen/main.py | 48 +++-- toxygen/network/tox_dns.py | 2 +- toxygen/plugin_support/plugin_support.py | 14 +- toxygen/smileys_and_stickers.py | 2 +- toxygen/threads.py | 2 +- toxygen/tray.py | 15 +- toxygen/ui/avwidgets.py | 2 +- toxygen/ui/list_items.py | 6 +- .../ui/{loginscreen.py => login_screen.py} | 64 ++++-- toxygen/ui/{mainscreen.py => main_screen.py} | 2 +- ...reen_widgets.py => main_screen_widgets.py} | 0 .../{passwordscreen.py => password_screen.py} | 12 +- toxygen/ui/widgets.py | 16 ++ toxygen/user_data/profile_manager.py | 25 ++- toxygen/user_data/settings.py | 29 ++- toxygen/util/util.py | 27 +-- toxygen/wrapper/libtox.py | 2 +- 21 files changed, 302 insertions(+), 254 deletions(-) rename toxygen/ui/{loginscreen.py => login_screen.py} (69%) rename toxygen/ui/{mainscreen.py => main_screen.py} (99%) rename toxygen/ui/{mainscreen_widgets.py => main_screen_widgets.py} (100%) rename toxygen/ui/{passwordscreen.py => password_screen.py} (95%) diff --git a/toxygen/app.py b/toxygen/app.py index 7f789eb..b4c5d55 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -1,50 +1,60 @@ +import communication.callbacks import threads - +from PyQt5 import QtWidgets, QtGui, QtCore +import ui.password_screen as passwordscreen +from util.util import curr_directory, get_platform, get_images_directory, get_styles_directory, log +import updater.updater as updater +import os +from communication.tox_factory import tox_factory +import wrapper.libtox as libtox +import user_data.toxes +from user_data.settings import Settings +from ui.login_screen import LoginScreen +from user_data.profile_manager import ProfileManager class App: - def __init__(self, path_or_uri=None): + def __init__(self, path_to_profile=None, uri=None): self.tox = self.ms = self.init = self.app = self.tray = self.mainloop = self.avloop = None - if path_or_uri is None: - self.uri = self.path = None - elif path_or_uri.startswith('tox:'): - self.path = None - self.uri = path_or_uri[4:] - else: - self.path = path_or_uri - self.uri = None + self.uri = self.path = self.toxes = None + if uri is not None and uri.startswith('tox:'): + self.uri = uri[4:] + if path_to_profile is not None: + self.path = path_to_profile def enter_pass(self, data): """ Show password screen """ - tmp = [data] - p = PasswordScreen(toxes.ToxES.get_instance(), tmp) + p = passwordscreen.PasswordScreen(self.toxes, data) p.show() self.app.lastWindowClosed.connect(self.app.quit) self.app.exec_() - if tmp[0] == data: + result = p.result + if result is None: raise SystemExit() else: - return tmp[0] + return result def main(self): """ Main function of app. loads login screen if needed and starts main screen """ - app = QtWidgets.QApplication(sys.argv) - app.setWindowIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) + app = QtWidgets.QApplication([]) + icon_file = os.path.join(get_images_directory(), 'icon.png') + app.setWindowIcon(QtGui.QIcon(icon_file)) self.app = app - if platform.system() == 'Linux': + if get_platform() == 'Linux': QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) - with open(curr_directory() + '/styles/dark_style.qss') as fl: + with open(os.path.join(get_styles_directory(), 'dark_style.qss')) as fl: style = fl.read() app.setStyleSheet(style) - encrypt_save = toxes.ToxES() + encrypt_save = libtox.LibToxEncryptSave() + toxes = user_data.toxes.ToxES(encrypt_save) if self.path is not None: path = os.path.dirname(self.path) + '/' @@ -70,15 +80,13 @@ class App: ls = LoginScreen() ls.setWindowIconText("Toxygen") profiles = ProfileManager.find_profiles() - ls.update_select(map(lambda x: x[1], profiles)) - _login = self.Login(profiles) - ls.update_on_close(_login.login_screen_close) + ls.update_select(profiles) ls.show() app.exec_() - if not _login.t: + result = ls.result + if result is None: return - elif _login.t == 1: # create new profile - _login.name = _login.name.strip() + elif result.is_new_profile(): # create new profile name = _login.name if _login.name else 'toxygen_user' pr = map(lambda x: x[1], ProfileManager.find_profiles()) if name in list(pr): @@ -130,14 +138,14 @@ class App: settings['language'] = curr_lang settings.save() else: # load existing profile - path, name = _login.get_data() - if _login.default: - Settings.set_auto_profile(path, name) - data = ProfileManager(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): + path = result.profile_path + if result.load_as_default: + Settings.set_auto_profile(path) + settings = Settings(toxes, path) + data = ProfileManager(settings, toxes, path).open_profile() + if toxes.is_data_encrypted(data): data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) + self.tox = communication.tox_factory.tox_factory(data, settings) else: path, name = auto_profile data = ProfileManager(path, name).open_profile() @@ -264,4 +272,4 @@ class App: plugin_helper = PluginLoader.get_instance() plugin_helper.set_tox(self.tox) - return self.tox \ No newline at end of file + return self.tox diff --git a/toxygen/communication/callbacks.py b/toxygen/communication/callbacks.py index ac93173..0566325 100644 --- a/toxygen/communication/callbacks.py +++ b/toxygen/communication/callbacks.py @@ -5,10 +5,7 @@ from contacts.profile import Profile from wrapper.toxcore_enums_and_consts import * from wrapper.toxav_enums import * from wrapper.tox import bin_to_string -from plugin_support import PluginLoader -import queue -import threading -import util +from plugin_support.plugin_support import PluginLoader import cv2 import numpy as np from threads import invoke_in_main_thread, execute @@ -31,6 +28,7 @@ def self_connection_status(tox, profile): invoke_in_main_thread(profile.set_status, status) elif connection == TOX_CONNECTION['NONE']: invoke_in_main_thread(profile.set_status, None) + return wrapped @@ -132,41 +130,45 @@ def friend_request(contacts_manager): return wrapped -def friend_typing(tox, friend_number, typing, user_data): - invoke_in_main_thread(Profile.get_instance().friend_typing, friend_number, typing) +def friend_typing(contacts_manager): + def wrapped(tox, friend_number, typing, user_data): + invoke_in_main_thread(contacts_manager.friend_typing, friend_number, typing) + + return wrapped -def friend_read_receipt(tox, friend_number, message_id, user_data): - profile = Profile.get_instance() - profile.get_friend_by_number(friend_number).dec_receipt() - if friend_number == profile.get_active_number(): - invoke_in_main_thread(profile.receipt) +def friend_read_receipt(contacts_manager): + def wrapped(tox, friend_number, message_id, user_data): + contacts_manager.get_friend_by_number(friend_number).dec_receipt() + if friend_number == contacts_manager.get_active_number(): + invoke_in_main_thread(contacts_manager.receipt) + + return wrapped + # ----------------------------------------------------------------------------------------------------------------- # Callbacks - file transfers # ----------------------------------------------------------------------------------------------------------------- -def tox_file_recv(window, tray): +def tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager, settings): """ New incoming file """ def wrapped(tox, friend_number, file_number, file_type, size, file_name, file_name_size, user_data): - profile = Profile.get_instance() - settings = Settings.get_instance() if file_type == TOX_FILE_KIND['DATA']: print('File') try: file_name = str(file_name[:file_name_size], 'utf-8') except: file_name = 'toxygen_file' - invoke_in_main_thread(profile.incoming_file_transfer, + invoke_in_main_thread(file_transfer_handler.incoming_file_transfer, friend_number, file_number, size, file_name) if not window.isActiveWindow(): - friend = profile.get_friend_by_number(friend_number) + friend = contacts_manager.get_friend_by_number(friend_number) if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: file_from = QtWidgets.QApplication.translate("Callback", "File from") invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window) @@ -175,7 +177,7 @@ def tox_file_recv(window, tray): invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) else: # AVATAR print('Avatar') - invoke_in_main_thread(profile.incoming_avatar, + invoke_in_main_thread(file_transfer_handler.incoming_avatar, friend_number, file_number, size) @@ -222,55 +224,69 @@ def file_recv_control(file_transfer_handler): # ----------------------------------------------------------------------------------------------------------------- -def lossless_packet(tox, friend_number, data, length, user_data): - """ - Incoming lossless packet - """ - data = data[:length] - plugin = PluginLoader.get_instance() - invoke_in_main_thread(plugin.callback_lossless, friend_number, data) +def lossless_packet(plugin_loader): + def wrapped(tox, friend_number, data, length, user_data): + """ + Incoming lossless packet + """ + data = data[:length] + invoke_in_main_thread(plugin_loader.callback_lossless, friend_number, data) + + return wrapped -def lossy_packet(tox, friend_number, data, length, user_data): - """ - Incoming lossy packet - """ - data = data[:length] - plugin = PluginLoader.get_instance() - invoke_in_main_thread(plugin.callback_lossy, friend_number, data) +def lossy_packet(plugin_loader): + def wrapped(tox, friend_number, data, length, user_data): + """ + Incoming lossy packet + """ + data = data[:length] + plugin = PluginLoader.get_instance() + invoke_in_main_thread(plugin.callback_lossy, friend_number, data) + + return wrapped # ----------------------------------------------------------------------------------------------------------------- # Callbacks - audio # ----------------------------------------------------------------------------------------------------------------- -def call_state(toxav, friend_number, mask, user_data): - """ - New call state - """ - print(friend_number, mask) - if mask == TOXAV_FRIEND_CALL_STATE['FINISHED'] or mask == TOXAV_FRIEND_CALL_STATE['ERROR']: - invoke_in_main_thread(Profile.get_instance().stop_call, friend_number, True) - else: - Profile.get_instance().call.toxav_call_state_cb(friend_number, mask) +def call_state(calls_manager): + def wrapped(toxav, friend_number, mask, user_data): + """ + New call state + """ + print(friend_number, mask) + if mask == TOXAV_FRIEND_CALL_STATE['FINISHED'] or mask == TOXAV_FRIEND_CALL_STATE['ERROR']: + invoke_in_main_thread(calls_manager.stop_call, friend_number, True) + else: + calls_manager.toxav_call_state_cb(friend_number, mask) + + return wrapped -def call(toxav, friend_number, audio, video, user_data): - """ - Incoming call from friend - """ - print(friend_number, audio, video) - invoke_in_main_thread(Profile.get_instance().incoming_call, audio, video, friend_number) +def call(calls_manager): + def wrapped(toxav, friend_number, audio, video, user_data): + """ + Incoming call from friend + """ + print(friend_number, audio, video) + invoke_in_main_thread(calls_manager.incoming_call, audio, video, friend_number) + + return wrapped -def callback_audio(toxav, friend_number, samples, audio_samples_per_channel, audio_channels_count, rate, user_data): - """ - New audio chunk - """ - Profile.get_instance().call.audio_chunk( - bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]), - audio_channels_count, - rate) +def callback_audio(calls_manager): + def wrapped(toxav, friend_number, samples, audio_samples_per_channel, audio_channels_count, rate, user_data): + """ + New audio chunk + """ + calls_manager.call.audio_chunk( + bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]), + audio_channels_count, + rate) + + return wrapped # ----------------------------------------------------------------------------------------------------------------- # Callbacks - video @@ -333,11 +349,6 @@ def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, u # ----------------------------------------------------------------------------------------------------------------- -def group_invite(tox, friend_number, gc_type, data, length, user_data): - invoke_in_main_thread(Profile.get_instance().group_invite, friend_number, gc_type, - bytes(data[:length])) - - def show_gc_notification(window, tray, message, group_number, peer_number): profile = Profile.get_instance() settings = Settings.get_instance() @@ -350,72 +361,40 @@ def show_gc_notification(window, tray, message, group_number, peer_number): sound_notification(SOUND_NOTIFICATION['MESSAGE']) invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) - -def group_message(window, tray): - def wrapped(tox, group_number, peer_number, message, length, user_data): - message = str(message[:length], 'utf-8') - invoke_in_main_thread(Profile.get_instance().new_gc_message, group_number, - peer_number, TOX_MESSAGE_TYPE['NORMAL'], message) - show_gc_notification(window, tray, message, group_number, peer_number) - return wrapped - - -def group_action(window, tray): - def wrapped(tox, group_number, peer_number, message, length, user_data): - message = str(message[:length], 'utf-8') - invoke_in_main_thread(Profile.get_instance().new_gc_message, group_number, - peer_number, TOX_MESSAGE_TYPE['ACTION'], message) - show_gc_notification(window, tray, message, group_number, peer_number) - return wrapped - - -def group_title(tox, group_number, peer_number, title, length, user_data): - invoke_in_main_thread(Profile.get_instance().new_gc_title, group_number, - title[:length]) - - -def group_namelist_change(tox, group_number, peer_number, change, user_data): - invoke_in_main_thread(Profile.get_instance().update_gc, group_number) - # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization # ----------------------------------------------------------------------------------------------------------------- -def init_callbacks(tox, window, tray): +def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, + calls_manager, file_transfer_handler, window, tray): """ Initialization of all callbacks. :param tox: tox instance :param window: main window :param tray: tray (for notifications) """ - tox.callback_self_connection_status(self_connection_status(tox), 0) + tox.callback_self_connection_status(self_connection_status(tox, profile), 0) - tox.callback_friend_status(friend_status, 0) - tox.callback_friend_message(friend_message(window, tray), 0) - tox.callback_friend_connection_status(friend_connection_status, 0) - tox.callback_friend_name(friend_name, 0) - tox.callback_friend_status_message(friend_status_message, 0) - tox.callback_friend_request(friend_request, 0) - tox.callback_friend_typing(friend_typing, 0) - tox.callback_friend_read_receipt(friend_read_receipt, 0) + tox.callback_friend_status(friend_status(profile, settings), 0) + tox.callback_friend_message(friend_message(profile, settings, window, tray), 0) + tox.callback_friend_connection_status(friend_connection_status(profile, settings, plugin_loader), 0) + tox.callback_friend_name(friend_name(profile), 0) + tox.callback_friend_status_message(friend_status_message(profile), 0) + tox.callback_friend_request(friend_request(contacts_manager), 0) + tox.callback_friend_typing(friend_typing(contacts_manager), 0) + tox.callback_friend_read_receipt(friend_read_receipt(contacts_manager), 0) - tox.callback_file_recv(tox_file_recv(window, tray), 0) - tox.callback_file_recv_chunk(file_recv_chunk, 0) - tox.callback_file_chunk_request(file_chunk_request, 0) - tox.callback_file_recv_control(file_recv_control, 0) + tox.callback_file_recv(tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager, settings), 0) + tox.callback_file_recv_chunk(file_recv_chunk(file_transfer_handler), 0) + tox.callback_file_chunk_request(file_chunk_request(file_transfer_handler), 0) + tox.callback_file_recv_control(file_recv_control(file_transfer_handler), 0) - toxav = tox.AV - toxav.callback_call_state(call_state, 0) - toxav.callback_call(call, 0) - toxav.callback_audio_receive_frame(callback_audio, 0) + toxav.callback_call_state(call_state(calls_manager), 0) + toxav.callback_call(call(calls_manager), 0) + toxav.callback_audio_receive_frame(callback_audio(calls_manager), 0) toxav.callback_video_receive_frame(video_receive_frame, 0) - tox.callback_friend_lossless_packet(lossless_packet, 0) - tox.callback_friend_lossy_packet(lossy_packet, 0) + tox.callback_friend_lossless_packet(lossless_packet(plugin_loader), 0) + tox.callback_friend_lossy_packet(lossy_packet(plugin_loader), 0) - tox.callback_group_invite(group_invite) - tox.callback_group_message(group_message(window, tray)) - tox.callback_group_action(group_action(window, tray)) - tox.callback_group_title(group_title) - tox.callback_group_namelist_change(group_namelist_change) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 9d33f19..eb69044 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -3,10 +3,9 @@ from PyQt5 import QtWidgets from contacts.friend import * from user_data.settings import * from wrapper.toxcore_enums_and_consts import * -from ctypes import * -from util import log, Singleton, curr_directory +from util.util import log, curr_directory from network.tox_dns import tox_dns -from db.database import * +from history.database import * from file_transfers.file_transfers import * import time from av import calls @@ -19,7 +18,7 @@ from contacts.group_chat import * import re -class Profile(basecontact.BaseContact, Singleton): +class Profile(basecontact.BaseContact): """ Profile of current toxygen user. Contains friends list, tox instance """ @@ -33,7 +32,6 @@ class Profile(basecontact.BaseContact, Singleton): tox.self_get_status_message(), screen.user_info, tox.self_get_address()) - Singleton.__init__(self) self._screen = screen self._messages = screen.messages self._tox = tox @@ -43,8 +41,6 @@ class Profile(basecontact.BaseContact, Singleton): self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages) settings = Settings.get_instance() self._show_avatars = settings['show_avatars'] - self._paused_file_transfers = dict(settings['paused_file_transfers']) - # key - file id, value: [path, friend number, is incoming, start position] # ----------------------------------------------------------------------------------------------------------------- @@ -97,6 +93,7 @@ class Profile(basecontact.BaseContact, Singleton): def get_friend_by_number(self, num): return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0] + def get_last_message(self): if self._active_friend + 1: return self.get_curr_friend().get_last_message_text() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 2706d97..6431fe4 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -9,6 +9,8 @@ class FileTransfersHandler: self._tox = tox self._settings = settings self._file_transfers = {} + self._paused_file_transfers = dict(settings['paused_file_transfers']) + # key - file id, value: [path, friend number, is incoming, start position] # ----------------------------------------------------------------------------------------------------------------- # File transfers support diff --git a/toxygen/main.py b/toxygen/main.py index e8a4694..42e8b76 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,9 +1,12 @@ import sys import app from user_data.settings import * -from util.util import curr_directory, program_version, remove +from util.util import curr_directory, remove import argparse +__maintainer__ = 'Ingvar' +__version__ = '0.5.0' + def clean(): """Removes all windows libs from libs folder""" @@ -15,30 +18,31 @@ def reset(): Settings.reset_auto_profile() +def print_toxygen_version(): + print('Toxygen v' + __version__) + + def main(): parser = argparse.ArgumentParser() - parser.add_argument('--version') - parser.add_argument('--clean') - parser.add_argument('--reset') + parser.add_argument('--version', help='Prints Toxygen version') + parser.add_argument('--clean', help='Deletes toxcore libs from libs folder') + parser.add_argument('--reset', help='Resets default profile') + parser.add_argument('profile_path', nargs='?', default=None, help='Resets default profile') args = parser.parse_args() - if not len(args): - toxygen = app.App() - else: # started with argument(s) - arg = sys.argv[1] - if arg == '--version': - print('Toxygen v' + program_version) - return - elif arg == '--help': - print('Usage:\ntoxygen path_to_profile\ntoxygen tox_id\ntoxygen --version\ntoxygen --reset') - return - elif arg == '--clean': - clean() - return - elif arg == '--reset': - reset() - return - else: - toxygen = app.App(arg) + + if args.version: + print_toxygen_version() + return + + if args.clean: + clean() + return + + if args.reset: + reset() + return + + toxygen = app.App(path_to_profile=args.profile_path) toxygen.main() diff --git a/toxygen/network/tox_dns.py b/toxygen/network/tox_dns.py index 36702ee..d3d48a1 100644 --- a/toxygen/network/tox_dns.py +++ b/toxygen/network/tox_dns.py @@ -1,6 +1,6 @@ import json import urllib.request -from util import log +from util.util import log from user_data import settings from PyQt5 import QtNetwork, QtCore diff --git a/toxygen/plugin_support/plugin_support.py b/toxygen/plugin_support/plugin_support.py index de39e5f..6f2af9c 100644 --- a/toxygen/plugin_support/plugin_support.py +++ b/toxygen/plugin_support/plugin_support.py @@ -1,4 +1,4 @@ -import util +import util.util as util from contacts import profile import os import importlib @@ -8,15 +8,15 @@ from user_data import toxes import sys -class PluginLoader(util.Singleton): +class PluginLoader(): - def __init__(self, tox, settings): + def __init__(self, tox, toxes, profile, settings): super().__init__() - self._profile = profile.Profile.get_instance() + self._profile = profile self._settings = settings self._plugins = {} # dict. key - plugin unique short name, value - tuple (plugin instance, is active) self._tox = tox - self._encr = toxes.ToxES.get_instance() + self._toxes = toxes def set_tox(self, tox): """ @@ -55,7 +55,7 @@ class PluginLoader(util.Singleton): if inspect.isclass(obj) and hasattr(obj, 'is_plugin') and obj.is_plugin: print('Plugin', elem) try: # create instance of plugin class - inst = obj(self._tox, self._profile, self._settings, self._encr) + inst = obj(self._tox, self._profile, self._settings, self._toxes) autostart = inst.get_short_name() in self._settings['plugins'] if autostart: inst.start() @@ -158,7 +158,7 @@ class PluginLoader(util.Singleton): try: result.extend(elem[0].get_message_menu(menu, selected_text)) except: - continue + pass return result def stop(self): diff --git a/toxygen/smileys_and_stickers.py b/toxygen/smileys_and_stickers.py index 7294185..dd72fd9 100644 --- a/toxygen/smileys_and_stickers.py +++ b/toxygen/smileys_and_stickers.py @@ -5,7 +5,7 @@ from collections import OrderedDict from PyQt5 import QtCore -class SmileyLoader(util.Singleton): +class SmileyLoader: """ Class which loads smileys packs and insert smileys into messages """ diff --git a/toxygen/threads.py b/toxygen/threads.py index f437d5a..4a129d1 100644 --- a/toxygen/threads.py +++ b/toxygen/threads.py @@ -1,11 +1,11 @@ from PyQt5 import QtCore -from communication.callbacks import init_callbacks from bootstrap.bootstrap import * import threading import queue from util import util + class InitThread(QtCore.QThread): def __init__(self, tox, ms, tray): diff --git a/toxygen/tray.py b/toxygen/tray.py index bc22ed4..0c474b4 100644 --- a/toxygen/tray.py +++ b/toxygen/tray.py @@ -3,6 +3,19 @@ from util.ui import tr from util.util import curr_directory +class SystemTrayIcon(QtWidgets.QSystemTrayIcon): + + leftClicked = QtCore.pyqtSignal() + + def __init__(self, icon, parent=None): + super().__init__(self, icon, parent) + self.activated.connect(self.iconActivated) + + def iconActivated(self, reason): + if reason == QtGui.QSystemTrayIcon.Trigger: + self.leftClicked.emit() + + class Menu(QtWidgets.QMenu): def __init__(self, settings, profile, *args): @@ -39,7 +52,7 @@ class Menu(QtWidgets.QMenu): def init_tray(profile, settings, main_screen): - tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) + tray = SystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) tray.setObjectName('tray') m = Menu(settings, profile) diff --git a/toxygen/ui/avwidgets.py b/toxygen/ui/avwidgets.py index 67bd04a..a783eca 100644 --- a/toxygen/ui/avwidgets.py +++ b/toxygen/ui/avwidgets.py @@ -5,7 +5,7 @@ import util import pyaudio import wave from user_data import settings -from util import curr_directory +from util.util import curr_directory class IncomingCallWidget(widgets.CenteredWidget): diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py index 64b4a2e..76e6185 100644 --- a/toxygen/ui/list_items.py +++ b/toxygen/ui/list_items.py @@ -2,7 +2,7 @@ from wrapper.toxcore_enums_and_consts import * from PyQt5 import QtCore, QtGui, QtWidgets from contacts import profile from file_transfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR -from util import curr_directory, convert_time, curr_time +from util.util import curr_directory, convert_time, curr_time from ui.widgets import DataLabel, create_menu import html as h import smileys @@ -60,8 +60,8 @@ class MessageEdit(QtWidgets.QTextBrowser): def quote_text(self): text = self.textCursor().selection().toPlainText() if text: - from ui import mainscreen - window = mainscreen.MainWindow.get_instance() + from ui import main_screen + window = main_screen.MainWindow.get_instance() text = '>' + '\n>'.join(text.split('\n')) if window.messageEdit.toPlainText(): text = '\n' + text diff --git a/toxygen/ui/loginscreen.py b/toxygen/ui/login_screen.py similarity index 69% rename from toxygen/ui/loginscreen.py rename to toxygen/ui/login_screen.py index cc1f45c..a5c91e1 100644 --- a/toxygen/ui/loginscreen.py +++ b/toxygen/ui/login_screen.py @@ -1,4 +1,5 @@ from ui.widgets import * +import os.path class NickEdit(LineEdit): @@ -14,12 +15,40 @@ class NickEdit(LineEdit): super(NickEdit, self).keyPressEvent(event) -class LoginScreen(CenteredWidget): +class LoginScreenResult: + + def __init__(self, profile_path, load_as_default, password=None): + self._profile_path = profile_path + self._load_as_default = load_as_default + self._password = password + + def get_profile_path(self): + return self._profile_path + + profile_path = property(get_profile_path) + + def get_load_as_default(self): + return self._load_as_default + + load_as_default = property(get_load_as_default) + + def get_password(self): + return self._password + + password = property(get_password) + + def is_new_profile(self): + return not os.path.isfile(self._profile_path) + + +class LoginScreen(CenteredWidget, DialogWithResult): def __init__(self): - super(LoginScreen, self).__init__() + CenteredWidget.__init__(self) + DialogWithResult.__init__(self) self.initUI() self.center() + self._profiles = [] def initUI(self): self.resize(400, 200) @@ -34,7 +63,7 @@ class LoginScreen(CenteredWidget): self.new_name.setGeometry(QtCore.QRect(20, 100, 171, 31)) self.load_profile = QtWidgets.QPushButton(self) self.load_profile.setGeometry(QtCore.QRect(220, 150, 161, 27)) - self.load_profile.clicked.connect(self.load_ex_profile) + self.load_profile.clicked.connect(self.load_existing_profile) self.default = QtWidgets.QCheckBox(self) self.default.setGeometry(QtCore.QRect(220, 110, 131, 22)) self.groupBox = QtWidgets.QGroupBox(self) @@ -44,6 +73,7 @@ class LoginScreen(CenteredWidget): self.groupBox_2 = QtWidgets.QGroupBox(self) self.groupBox_2.setGeometry(QtCore.QRect(10, 40, 191, 151)) self.toxygen = QtWidgets.QLabel(self) + self.toxygen.setGeometry(QtCore.QRect(160, 8, 90, 25)) self.groupBox.raise_() self.groupBox_2.raise_() self.comboBox.raise_() @@ -51,16 +81,11 @@ class LoginScreen(CenteredWidget): self.load_profile.raise_() self.new_name.raise_() self.new_profile.raise_() - self.toxygen.setGeometry(QtCore.QRect(160, 8, 90, 25)) font = QtGui.QFont() font.setFamily("Impact") font.setPointSize(16) self.toxygen.setFont(font) self.toxygen.setObjectName("toxygen") - self.type = 0 - self.number = -1 - self.load_as_default = False - self.name = None self.retranslateUi() QtCore.QMetaObject.connectSlotsByName(self) @@ -80,19 +105,18 @@ class LoginScreen(CenteredWidget): self.name = self.new_name.text() self.close() - def load_ex_profile(self): - if not self.create_only: - self.type = 2 - self.number = self.comboBox.currentIndex() - self.load_as_default = self.default.isChecked() - self.close() + def load_existing_profile(self): + index = self.comboBox.currentIndex() + load_as_default = self.default.isChecked() + path = os.path.join(self._profiles[index][0], self._profiles[index][1] + '.tox') + result = LoginScreenResult(path, load_as_default) + self.close_with_result(result) - def update_select(self, data): - list_of_profiles = [] - for elem in data: - list_of_profiles.append(elem) - self.comboBox.addItems(list_of_profiles) - self.create_only = not list_of_profiles + def update_select(self, profiles): + profiles = sorted(profiles, key=lambda p: p[1]) + self._profiles = list(profiles) + self.comboBox.addItems(list(map(lambda p: p[1], profiles))) + self.load_profile.setEnabled(len(profiles) > 0) def update_on_close(self, func): self.onclose = func diff --git a/toxygen/ui/mainscreen.py b/toxygen/ui/main_screen.py similarity index 99% rename from toxygen/ui/mainscreen.py rename to toxygen/ui/main_screen.py index ce828b0..c2912d6 100644 --- a/toxygen/ui/mainscreen.py +++ b/toxygen/ui/main_screen.py @@ -3,7 +3,7 @@ from contacts.profile import * from ui.list_items import * from ui.widgets import MultilineEdit, ComboBox import plugin_support -from ui.mainscreen_widgets import * +from ui.main_screen_widgets import * from user_data import toxes, settings diff --git a/toxygen/ui/mainscreen_widgets.py b/toxygen/ui/main_screen_widgets.py similarity index 100% rename from toxygen/ui/mainscreen_widgets.py rename to toxygen/ui/main_screen_widgets.py diff --git a/toxygen/ui/passwordscreen.py b/toxygen/ui/password_screen.py similarity index 95% rename from toxygen/ui/passwordscreen.py rename to toxygen/ui/password_screen.py index 9d3d523..bd6c146 100644 --- a/toxygen/ui/passwordscreen.py +++ b/toxygen/ui/password_screen.py @@ -1,4 +1,4 @@ -from ui.widgets import CenteredWidget, LineEdit +from ui.widgets import CenteredWidget, LineEdit, DialogWithResult from PyQt5 import QtCore, QtWidgets @@ -16,10 +16,11 @@ class PasswordArea(LineEdit): super(PasswordArea, self).keyPressEvent(event) -class PasswordScreenBase(CenteredWidget): +class PasswordScreenBase(CenteredWidget, DialogWithResult): def __init__(self, encrypt): - super(PasswordScreenBase, self).__init__() + CenteredWidget.__init__(self) + DialogWithResult.__init__(self) self._encrypt = encrypt self.initUI() @@ -73,13 +74,12 @@ class PasswordScreen(PasswordScreenBase): if self.password.text(): try: self._encrypt.set_password(self.password.text()) - new_data = self._encrypt.pass_decrypt(self._data[0]) + new_data = self._encrypt.pass_decrypt(self._data) except Exception as ex: self.warning.setVisible(True) print('Decryption error:', ex) else: - self._data[0] = new_data - self.close() + self.close_with_result(new_data) class UnlockAppScreen(PasswordScreenBase): diff --git a/toxygen/ui/widgets.py b/toxygen/ui/widgets.py index b63deb0..7b811e7 100644 --- a/toxygen/ui/widgets.py +++ b/toxygen/ui/widgets.py @@ -32,6 +32,22 @@ class CenteredWidget(QtWidgets.QWidget): self.move(qr.topLeft()) +class DialogWithResult(QtWidgets.QWidget): + + def __init__(self, parent=None): + super().__init__(parent) + self._result = None + + def get_result(self): + return self._result + + result = property(get_result) + + def close_with_result(self, result): + self._result = result + self.close() + + class LineEdit(QtWidgets.QLineEdit): def __init__(self, parent=None): diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index 12ce185..a810105 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -1,11 +1,17 @@ +import util.util as util +import os +from user_data.settings import Settings + + class ProfileManager: """ Class with methods for search, load and save profiles """ - def __init__(self, path, name): - path = append_slash(path) - self._path = path + name + '.tox' - self._directory = path + def __init__(self, settings, toxes, path): + self._settings = settings + self._toxes = toxes + self._path = path + self._directory = os.path.basename(path) # create /avatars if not exists: directory = path + 'avatars' if not os.path.exists(directory): @@ -23,9 +29,8 @@ class ProfileManager: return self._directory def save_profile(self, data): - inst = ToxES.get_instance() - if inst.has_password(): - data = inst.pass_encrypt(data) + if self._toxes.has_password(): + data = self._toxes.pass_encrypt(data) with open(self._path, 'wb') as fl: fl.write(data) print('Profile saved successfully') @@ -37,11 +42,11 @@ class ProfileManager: with open(path, 'wb') as fout: fout.write(data) print('Profile exported successfully') - copy(self._directory + 'avatars', new_path + 'avatars') + util.copy(self._directory + 'avatars', new_path + 'avatars') if use_new_path: self._path = new_path + os.path.basename(self._path) self._directory = new_path - Settings.get_instance().update_path() + self._settings.update_path() @staticmethod def find_profiles(): @@ -57,7 +62,7 @@ class ProfileManager: if fl.endswith('.tox'): name = fl[:-4] result.append((path, name)) - path = curr_directory() + path = util.get_base_directory(__file__) # check current directory for fl in os.listdir(path): if fl.endswith('.tox'): diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 45d41bd..f879e53 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -1,28 +1,26 @@ from platform import system import json import os -from util import Singleton, curr_directory, log, copy, append_slash +from util.util import log, curr_directory, append_slash import pyaudio from user_data.toxes import ToxES -import smileys +import smileys_and_stickers as smileys -class Settings(dict, Singleton): +class Settings(dict): """ Settings of current profile + global app settings """ - def __init__(self, name): - Singleton.__init__(self) - self.path = ProfileManager.get_path() + str(name) + '.json' - self.name = name - if os.path.isfile(self.path): - with open(self.path, 'rb') as fl: + def __init__(self, toxes, path): + self._path = path + self._toxes = toxes + if os.path.isfile(path): + with open(path, 'rb') as fl: data = fl.read() - inst = ToxES.get_instance() try: - if inst.is_data_encrypted(data): - data = inst.pass_decrypt(data) + if toxes.is_data_encrypted(data): + data = toxes.pass_decrypt(data) info = json.loads(str(data, 'utf-8')) except Exception as ex: info = Settings.get_default_settings() @@ -175,12 +173,11 @@ class Settings(dict, Singleton): def save(self): text = json.dumps(self) - inst = ToxES.get_instance() - if inst.has_password(): - text = bytes(inst.pass_encrypt(bytes(text, 'utf-8'))) + if self._toxes.has_password(): + text = bytes(self._toxes.pass_encrypt(bytes(text, 'utf-8'))) else: text = bytes(text, 'utf-8') - with open(self.path, 'wb') as fl: + with open(self._path, 'wb') as fl: fl.write(text) def close(self): diff --git a/toxygen/util/util.py b/toxygen/util/util.py index 33b4758..45bc345 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -3,9 +3,7 @@ import time import shutil import sys import re - - -program_version = '0.5.0' +import platform def cached(func): @@ -33,6 +31,18 @@ def curr_directory(current_file=None): return os.path.dirname(os.path.realpath(current_file or __file__)) +def get_base_directory(current_file=None): + return os.path.dirname(curr_directory()) + + +def get_images_directory(): + return os.path.join(get_base_directory(), 'images') + + +def get_styles_directory(): + return os.path.join(get_base_directory(), 'styles') + + def curr_time(): return time.strftime('%H:%M') @@ -95,12 +105,5 @@ def is_re_valid(regex): return True -class Singleton: - _instance = None - - def __init__(self): - self.__class__._instance = self - - @classmethod - def get_instance(cls): - return cls._instance +def get_platform(): + return platform.system() diff --git a/toxygen/wrapper/libtox.py b/toxygen/wrapper/libtox.py index 752798f..a04746c 100644 --- a/toxygen/wrapper/libtox.py +++ b/toxygen/wrapper/libtox.py @@ -1,6 +1,6 @@ from platform import system from ctypes import CDLL -import util +import util.util as util class LibToxCore: From 91d3f885c0fe0dabc74968ab0b962c26101ca090 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 16 Apr 2018 23:35:55 +0300 Subject: [PATCH 006/217] create profile screen, main screen opens now --- .gitignore | 1 - toxygen/app.py | 131 ++++++++++------------ toxygen/contacts/basecontact.py | 1 + toxygen/contacts/profile.py | 3 +- toxygen/main.py | 2 +- toxygen/ui/create_profile_screen.py | 43 +++++++ toxygen/ui/login_screen.py | 10 +- toxygen/ui/main_screen.py | 31 +++-- toxygen/ui/menu.py | 2 +- toxygen/ui/views/create_profile_screen.ui | 106 +++++++++++++++++ toxygen/updater/updater.py | 41 +++++-- toxygen/user_data/profile_manager.py | 3 - toxygen/user_data/settings.py | 11 +- toxygen/util/ui.py | 6 + toxygen/util/util.py | 13 ++- 15 files changed, 286 insertions(+), 118 deletions(-) create mode 100644 toxygen/ui/create_profile_screen.py create mode 100644 toxygen/ui/views/create_profile_screen.ui diff --git a/.gitignore b/.gitignore index 78c26f5..f31ca4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ *.pyc *.pyo -*.ui toxygen/toxcore tests/tests tests/libs diff --git a/toxygen/app.py b/toxygen/app.py index b4c5d55..8a7c799 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -2,20 +2,24 @@ import communication.callbacks import threads from PyQt5 import QtWidgets, QtGui, QtCore import ui.password_screen as passwordscreen -from util.util import curr_directory, get_platform, get_images_directory, get_styles_directory, log +from util.util import * import updater.updater as updater import os from communication.tox_factory import tox_factory -import wrapper.libtox as libtox +import wrapper.toxencryptsave as tox_encrypt_save import user_data.toxes from user_data.settings import Settings from ui.login_screen import LoginScreen from user_data.profile_manager import ProfileManager +from plugin_support.plugin_support import PluginLoader +from ui.main_screen import MainWindow class App: - def __init__(self, path_to_profile=None, uri=None): + def __init__(self, version, path_to_profile=None, uri=None): + self._version = version + self._app = None self.tox = self.ms = self.init = self.app = self.tray = self.mainloop = self.avloop = None self.uri = self.path = self.toxes = None if uri is not None and uri.startswith('tox:'): @@ -44,7 +48,7 @@ class App: app = QtWidgets.QApplication([]) icon_file = os.path.join(get_images_directory(), 'icon.png') app.setWindowIcon(QtGui.QIcon(icon_file)) - self.app = app + self._app = app if get_platform() == 'Linux': QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) @@ -53,17 +57,18 @@ class App: style = fl.read() app.setStyleSheet(style) - encrypt_save = libtox.LibToxEncryptSave() - toxes = user_data.toxes.ToxES(encrypt_save) + encrypt_save = tox_encrypt_save.ToxEncryptSave() + self._toxes = user_data.toxes.ToxES(encrypt_save) if self.path is not None: path = os.path.dirname(self.path) + '/' name = os.path.basename(self.path)[:-4] - data = ProfileManager(path, name).open_profile() + self._settings = Settings(self._toxes, self.path.replace('.tox', '.json')) + self._profile_manager = ProfileManager(self._settings, self._toxes, path) + data = self._profile_manager.open_profile() if encrypt_save.is_data_encrypted(data): data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) + self.tox = tox_factory(data, self._settings) else: auto_profile = Settings.get_auto_profile() if not auto_profile[0]: @@ -87,7 +92,7 @@ class App: if result is None: return elif result.is_new_profile(): # create new profile - name = _login.name if _login.name else 'toxygen_user' + name = get_profile_name_from_path(result.profile_path) or 'toxygen_user' pr = map(lambda x: x[1], ProfileManager.find_profiles()) if name in list(pr): msgBox = QtWidgets.QMessageBox() @@ -98,8 +103,8 @@ class App: msgBox.setText(text) msgBox.exec_() return - self.tox = profile.tox_factory() - self.tox.self_set_name(bytes(_login.name, 'utf-8') if _login.name else b'Toxygen User') + self.tox = tox_factory() + self.tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') self.tox.self_set_status_message(b'Toxing on Toxygen') reply = QtWidgets.QMessageBox.question(None, 'Profile {}'.format(name), @@ -133,28 +138,30 @@ class App: msgBox.exec_() return path = Settings.get_default_path() - settings = Settings(name) + self._settings = Settings() if curr_lang in langs: - settings['language'] = curr_lang - settings.save() + self._settings['language'] = curr_lang + self._settings.save() else: # load existing profile path = result.profile_path if result.load_as_default: Settings.set_auto_profile(path) - settings = Settings(toxes, path) - data = ProfileManager(settings, toxes, path).open_profile() - if toxes.is_data_encrypted(data): + self._settings = Settings(self._toxes, path.replace('.tox', '.json')) + self._profile_manager = ProfileManager(self._settings, self._toxes, path) + data = self._profile_manager.open_profile() + if self._toxes.is_data_encrypted(data): data = self.enter_pass(data) - self.tox = communication.tox_factory.tox_factory(data, settings) + self._tox = tox_factory(data, self._settings) else: path, name = auto_profile - data = ProfileManager(path, name).open_profile() + self._settings = Settings(self._toxes, path + name + '.json') + self._profile_manager = ProfileManager(self._settings, self._toxes, path) + data = self._profile_manager.open_profile() if encrypt_save.is_data_encrypted(data): data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) + self.tox = tox_factory(data, self._settings) - if Settings.is_active_profile(path, name): # profile is in use + if Settings.is_active_profile(path, get_profile_name_from_path(path)): # profile is in use reply = QtWidgets.QMessageBox.question(None, 'Profile {}'.format(name), QtWidgets.QApplication.translate("login", 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?'), @@ -163,63 +170,36 @@ class App: if reply != QtWidgets.QMessageBox.Yes: return else: - settings.set_active_profile() + self._settings.set_active_profile() - # application color scheme - for theme in settings.built_in_themes().keys(): - if settings['theme'] == theme: - with open(curr_directory() + settings.built_in_themes()[theme]) as fl: - style = fl.read() - app.setStyleSheet(style) - - lang = Settings.supported_languages()[settings['language']] - translator = QtCore.QTranslator() - translator.load(curr_directory() + '/translations/' + lang) - app.installTranslator(translator) - app.translator = translator + self.load_app_styles() + self.load_app_translations() # tray icon - + self.ms = MainWindow(self._settings, self._tox, self.reset, self.tray) + self._profile = self.ms.profile self.ms.show() - updating = False - if settings['update'] and updater.updater_available() and updater.connection_available(): # auto update - version = updater.check_for_updates() - if version is not None: - if settings['update'] == 2: - updater.download(version) - updating = True - else: - reply = QtWidgets.QMessageBox.question(None, - 'Toxygen', - QtWidgets.QApplication.translate("login", - 'Update for Toxygen was found. Download and install it?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - updater.download(version) - updating = True - + updating = updater.start_update_if_needed(self._version, self._settings) if updating: data = self.tox.get_savedata() - ProfileManager.get_instance().save_profile(data) - settings.close() + self._profile_manager.save_profile(data) + self._settings.close() del self.tox return - plugin_helper = PluginLoader(self.tox, settings) # plugin support + plugin_helper = PluginLoader(self._tox, self._toxes, self._profile, self._settings) # plugin support plugin_helper.load() - start() # init thread - self.init = self.InitThread(self.tox, self.ms, self.tray) + self.init = threads.InitThread(self.tox, self.ms, self.tray) self.init.start() # starting threads for tox iterate and toxav iterate - self.mainloop = self.ToxIterateThread(self.tox) + self.mainloop = threads.ToxIterateThread(self._tox) self.mainloop.start() - self.avloop = self.ToxAVIterateThread(self.tox.AV) + self.avloop = threads.ToxAVIterateThread(self._tox.AV) self.avloop.start() if self.uri is not None: @@ -232,14 +212,13 @@ class App: self.mainloop.stop = True self.avloop.stop = True plugin_helper.stop() - stop() self.mainloop.wait() self.init.wait() self.avloop.wait() self.tray.hide() data = self.tox.get_savedata() - ProfileManager.get_instance().save_profile(data) - settings.close() + self._profile_manager.save_profile(data) + self._settings.close() del self.tox def reset(self): @@ -254,10 +233,10 @@ class App: self.init.wait() self.avloop.wait() data = self.tox.get_savedata() - ProfileManager.get_instance().save_profile(data) + self._profile_manager.save_profile(data) del self.tox # create new tox instance - self.tox = profile.tox_factory(data, Settings.get_instance()) + self.tox = tox_factory(data, self._settings) # init thread self.init = threads.InitThread(self.tox, self.ms, self.tray) self.init.start() @@ -269,7 +248,21 @@ class App: self.avloop = threads.ToxAVIterateThread(self.tox.AV) self.avloop.start() - plugin_helper = PluginLoader.get_instance() - plugin_helper.set_tox(self.tox) + self._plugin_loader.set_tox(self.tox) return self.tox + + def load_app_styles(self): + # application color scheme + for theme in self._settings.built_in_themes().keys(): + if self._settings['theme'] == theme: + with open(curr_directory(__file__) + self._settings.built_in_themes()[theme]) as fl: + style = fl.read() + self._app.setStyleSheet(style) + + def load_app_translations(self): + lang = Settings.supported_languages()[self._settings['language']] + translator = QtCore.QTranslator() + translator.load(curr_directory(__file__) + '/translations/' + lang) + self._app.installTranslator(translator) + self._app.translator = translator diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 6e2d55a..34c100c 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -81,6 +81,7 @@ class BaseContact: """ Tries to load avatar of contact or uses default avatar """ + return prefix = ProfileManager.get_path() + 'avatars/' avatar_path = prefix + '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index eb69044..ae7767a 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -39,8 +39,7 @@ class Profile(basecontact.BaseContact): self._load_history = True self._waiting_for_reconnection = False self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages) - settings = Settings.get_instance() - self._show_avatars = settings['show_avatars'] + #self._show_avatars = settings['show_avatars'] # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/main.py b/toxygen/main.py index 42e8b76..4230668 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -42,7 +42,7 @@ def main(): reset() return - toxygen = app.App(path_to_profile=args.profile_path) + toxygen = app.App(__version__, path_to_profile=args.profile_path) toxygen.main() diff --git a/toxygen/ui/create_profile_screen.py b/toxygen/ui/create_profile_screen.py new file mode 100644 index 0000000..f461719 --- /dev/null +++ b/toxygen/ui/create_profile_screen.py @@ -0,0 +1,43 @@ +from ui.widgets import * +from PyQt5 import uic +import util.util as util +import util.ui as util_ui + + +class CreateProfileScreenResult: + + def __init__(self, save_into_default_folder, password): + self._save_into_default_folder = save_into_default_folder + self._password = password + + def get_save_into_default_folder(self): + return self._save_into_default_folder + + save_into_default_folder = property(get_save_into_default_folder) + + def get_password(self): + return self._password + + password = property(get_password) + + +class CreateProfileScreen(CenteredWidget, DialogWithResult): + + def __init__(self): + CenteredWidget.__init__(self) + DialogWithResult.__init__(self) + uic.loadUi(util.get_views_path('create_profile_screen')) + self.center() + self.createProfile.clicked.connect(self.create_profile) + + def retranslateUi(self): + self.defaultFolder.setPlaceholderText(util_ui.tr('Save in default folder')) + self.programFolder.setPlaceholderText(util_ui.tr('Save in program folder')) + self.createProfile.setText(util_ui.tr('Create profile')) + self.passwordLabel.setText(util_ui.tr('Password:')) + + def create_profile(self): + if self.password.text() != self.confirmPassword.text(): + return # TODO : error + result = CreateProfileScreenResult(self.defaultFolder.isChecked(), self.password.text()) + self.close_with_result(result) diff --git a/toxygen/ui/login_screen.py b/toxygen/ui/login_screen.py index a5c91e1..bbafef9 100644 --- a/toxygen/ui/login_screen.py +++ b/toxygen/ui/login_screen.py @@ -5,7 +5,7 @@ import os.path class NickEdit(LineEdit): def __init__(self, parent): - super(NickEdit, self).__init__(parent) + super().__init__(parent) self.parent = parent def keyPressEvent(self, event): @@ -108,7 +108,7 @@ class LoginScreen(CenteredWidget, DialogWithResult): def load_existing_profile(self): index = self.comboBox.currentIndex() load_as_default = self.default.isChecked() - path = os.path.join(self._profiles[index][0], self._profiles[index][1] + '.tox') + path = os.path.join(self._profiles[index][0], self._profiles[index][1] + '.tox') result = LoginScreenResult(path, load_as_default) self.close_with_result(result) @@ -118,9 +118,3 @@ class LoginScreen(CenteredWidget, DialogWithResult): self.comboBox.addItems(list(map(lambda p: p[1], profiles))) self.load_profile.setEnabled(len(profiles) > 0) - def update_on_close(self, func): - self.onclose = func - - def closeEvent(self, event): - self.onclose(self.type, self.number, self.load_as_default, self.name) - event.accept() diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index c2912d6..d3896be 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -5,19 +5,20 @@ from ui.widgets import MultilineEdit, ComboBox import plugin_support from ui.main_screen_widgets import * from user_data import toxes, settings +import util.util as util -class MainWindow(QtWidgets.QMainWindow, Singleton): +class MainWindow(QtWidgets.QMainWindow): - def __init__(self, tox, reset, tray): + def __init__(self, settings, tox, reset, tray): super().__init__() - Singleton.__init__(self) + self._settings = settings self.reset = reset self.tray = tray self.setAcceptDrops(True) self.initUI(tox) self._saved = False - if settings.Settings.get_instance()['show_welcome_screen']: + if settings['show_welcome_screen']: self.ws = WelcomeScreen() def setup_menu(self, window): @@ -137,7 +138,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Name")) self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online and by name")) self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online first and by name")) - ind = Settings.get_instance()['sorting'] + ind = self._settings['sorting'] d = {0: 0, 1: 1, 2: 2, 3: 4, 1 | 4: 4, 2 | 4: 5} self.online_contacts.setCurrentIndex(d[ind]) self.importPlugin.setText(QtWidgets.QApplication.translate("MainWindow", "Import plugin")) @@ -150,7 +151,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.messageEdit.setObjectName("messageEdit") font = QtGui.QFont() font.setPointSize(11) - font.setFamily(settings.Settings.get_instance()['font']) + font.setFamily(self._settings['font']) self.messageEdit.setFont(font) self.sendMessageButton = QtWidgets.QPushButton(Form) @@ -207,7 +208,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.name = Form.name = DataLabel(Form) Form.name.setGeometry(QtCore.QRect(75, 15, 150, 25)) font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) + font.setFamily(self._settings['font']) font.setPointSize(14) font.setBold(True) Form.name.setFont(font) @@ -235,7 +236,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.account_name.setGeometry(QtCore.QRect(100, 0, 400, 25)) self.account_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) + font.setFamily(self._settings['font']) font.setPointSize(14) font.setBold(True) self.account_name.setFont(font) @@ -297,10 +298,9 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): def initUI(self, tox): self.setMinimumSize(920, 500) - s = Settings.get_instance() + s = self._settings self.setGeometry(s['x'], s['y'], s['width'], s['height']) self.setWindowTitle('Toxygen') - os.chdir(curr_directory() + '/images/') menu = QtWidgets.QWidget() main = QtWidgets.QWidget() grid = QtWidgets.QGridLayout() @@ -317,7 +317,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.setup_right_bottom(message_buttons) self.setup_left_center(main_list) self.setup_menu(menu) - if not Settings.get_instance()['mirror_mode']: + if not self._settings['mirror_mode']: grid.addWidget(search, 2, 0) grid.addWidget(name, 1, 0) grid.addWidget(messages, 2, 1, 2, 1) @@ -391,7 +391,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.account_name.setGeometry(QtCore.QRect(100, 15, self.width() - 560, 25)) self.account_status.setGeometry(QtCore.QRect(100, 35, self.width() - 560, 25)) self.messageEdit.setFocus() - self.profile.update() + #self.profile.update() def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Escape and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): @@ -561,14 +561,13 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): self.update_call_state('call') def update_call_state(self, state): - os.chdir(curr_directory() + '/images/') - pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(state)) + pixmap = QtGui.QPixmap(os.path.join(util.get_images_directory(), '{}.png'.format(state))) icon = QtGui.QIcon(pixmap) self.callButton.setIcon(icon) self.callButton.setIconSize(QtCore.QSize(50, 50)) - pixmap = QtGui.QPixmap(curr_directory() + '/images/{}_video.png'.format(state)) + pixmap = QtGui.QPixmap(os.path.join(util.get_images_directory(), '{}_video.png'.format(state))) icon = QtGui.QIcon(pixmap) self.videocallButton.setIcon(icon) self.videocallButton.setIconSize(QtCore.QSize(35, 35)) @@ -732,7 +731,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): def show(self): super().show() - self.profile.update() + #self.profile.update() def filtering(self): ind = self.online_contacts.currentIndex() diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 606c545..1aa12dc 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -1,7 +1,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets from user_data.settings import * from contacts.profile import Profile -from util import curr_directory, copy +from util.util import curr_directory, copy from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio from user_data import toxes diff --git a/toxygen/ui/views/create_profile_screen.ui b/toxygen/ui/views/create_profile_screen.ui new file mode 100644 index 0000000..f9d3241 --- /dev/null +++ b/toxygen/ui/views/create_profile_screen.ui @@ -0,0 +1,106 @@ + + + Form + + + + 0 + 0 + 400 + 300 + + + + + 400 + 300 + + + + + 400 + 300 + + + + Form + + + + + 30 + 240 + 341 + 51 + + + + PushButton + + + + + + 30 + 190 + 341 + 41 + + + + + + + 30 + 140 + 341 + 41 + + + + + + + 30 + 100 + 67 + 17 + + + + TextLabel + + + + + + 30 + 10 + 112 + 23 + + + + RadioButton + + + true + + + + + + 30 + 50 + 112 + 23 + + + + RadioButton + + + + + + diff --git a/toxygen/updater/updater.py b/toxygen/updater/updater.py index af80624..575dbfe 100644 --- a/toxygen/updater/updater.py +++ b/toxygen/updater/updater.py @@ -1,4 +1,5 @@ -import util +import util.util as util +import util.ui as util_ui import os from user_data import settings import platform @@ -24,12 +25,11 @@ def updater_available(): return os.path.exists(util.curr_directory() + '/toxygen_updater') -def check_for_updates(): - current_version = util.program_version +def check_for_updates(current_version, settings): major, minor, patch = list(map(lambda x: int(x), current_version.split('.'))) versions = generate_versions(major, minor, patch) for version in versions: - if send_request(version): + if send_request(version, settings): return version return None # no new version was found @@ -79,14 +79,13 @@ def download(version): util.log('Exception: running updater failed with ' + str(ex)) -def send_request(version): - s = settings.Settings.get_instance() +def send_request(version, settings): netman = QtNetwork.QNetworkAccessManager() proxy = QtNetwork.QNetworkProxy() - if s['proxy_type']: - proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy if s['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) - proxy.setHostName(s['proxy_host']) - proxy.setPort(s['proxy_port']) + if settings['proxy_type']: + proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy if settings['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) + proxy.setHostName(settings['proxy_host']) + proxy.setPort(settings['proxy_port']) netman.setProxy(proxy) url = test_url(version) try: @@ -108,3 +107,25 @@ def generate_versions(major, minor, patch): new_minor = '.'.join([str(major), str(minor + 1), '0']) new_patch = '.'.join([str(major), str(minor), str(patch + 1)]) return new_major, new_minor, new_patch + + +def start_update_if_needed(version, settings): + updating = False + if settings['update'] and updater_available() and connection_available(): # auto update + version = check_for_updates(version, settings) + if version is not None: + if settings['update'] == 2: + download(version) + updating = True + else: + reply = QtWidgets.QMessageBox.question(None, + 'Toxygen', + QtWidgets.QApplication.translate("login", + 'Update for Toxygen was found. Download and install it?'), + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + if reply == QtWidgets.QMessageBox.Yes: + download(version) + updating = True + + return updating diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index a810105..b897fc3 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -70,6 +70,3 @@ class ProfileManager: result.append((path + '/', name)) return result - @staticmethod - def get_path(): - return ProfileManager.get_instance().get_dir() diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index f879e53..39fd333 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -14,6 +14,7 @@ class Settings(dict): def __init__(self, toxes, path): self._path = path + self._profile_path = path.replace('.json', '.tox') self._toxes = toxes if os.path.isfile(path): with open(path, 'rb') as fl: @@ -25,10 +26,10 @@ class Settings(dict): except Exception as ex: info = Settings.get_default_settings() log('Parsing settings error: ' + str(ex)) - super(Settings, self).__init__(info) + super().__init__(info) self.upgrade() else: - super(Settings, self).__init__(Settings.get_default_settings()) + super().__init__(Settings.get_default_settings()) self.save() smileys.SmileyLoader(self) self.locked = False @@ -181,8 +182,7 @@ class Settings(dict): fl.write(text) def close(self): - profile_path = ProfileManager.get_path() - path = str(profile_path + str(self.name) + '.lock') + path = self._profile_path + '.lock' if os.path.isfile(path): os.remove(path) @@ -190,8 +190,7 @@ class Settings(dict): """ Mark current profile as active """ - profile_path = ProfileManager.get_path() - path = str(profile_path + str(self.name) + '.lock') + path = self._profile_path + '.lock' with open(path, 'w') as fl: fl.write('active') diff --git a/toxygen/util/ui.py b/toxygen/util/ui.py index a0d950d..be613cb 100644 --- a/toxygen/util/ui.py +++ b/toxygen/util/ui.py @@ -5,4 +5,10 @@ def tr(s): return PyQt5.QtWidgets.QApplication.translate('Toxygen', s) +def question(text): + reply = PyQt5.QtWidgets.QMessageBox.question(None, 'Toxygen', text, + PyQt5.QtWidgets.QMessageBox.Yes, + PyQt5.QtWidgets.QMessageBox.No) + return reply == PyQt5.QtWidgets.QMessageBox.Yes + # TODO: move all dialogs here diff --git a/toxygen/util/util.py b/toxygen/util/util.py index 45bc345..91a4aec 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -32,7 +32,7 @@ def curr_directory(current_file=None): def get_base_directory(current_file=None): - return os.path.dirname(curr_directory()) + return os.path.dirname(curr_directory(current_file or __file__)) def get_images_directory(): @@ -43,6 +43,17 @@ def get_styles_directory(): return os.path.join(get_base_directory(), 'styles') +def get_profile_name_from_path(path): + return os.path.basename(path)[:-4] + + +def get_views_path(view_name): + ui_folder = os.path.join(get_base_directory(), 'ui') + views_folder = os.path.join(ui_folder, 'views') + + return os.path.join(views_folder, view_name + '.ui') + + def curr_time(): return time.strftime('%H:%M') From 8a2665ed4d9ddeabdc80c1b23191c27bc9c6d70f Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 17 Apr 2018 15:14:05 +0300 Subject: [PATCH 007/217] refactoring - app.py, files moved to different folders --- setup.py | 4 +- tests/tests.py | 2 + toxygen/app.py | 214 ++++++++---------- toxygen/contacts/profile.py | 2 +- toxygen/login.py | 19 -- toxygen/main.py | 1 + .../{communication => middleware}/__init__.py | 0 .../callbacks.py | 3 +- toxygen/{ => middleware}/threads.py | 66 +++--- .../tox_factory.py | 0 toxygen/notifications.py | 71 ------ toxygen/notifications/__init__.py | 0 toxygen/notifications/sound.py | 54 +++++ toxygen/notifications/tray.py | 22 ++ toxygen/smileys/__init__.py | 0 .../smileys.py} | 16 +- toxygen/stickers/__init__.py | 0 toxygen/stickers/stickers.py | 18 ++ toxygen/ui/{avwidgets.py => av_widgets.py} | 0 toxygen/ui/main_screen.py | 11 +- toxygen/ui/main_screen_widgets.py | 4 +- toxygen/{ => ui}/tray.py | 34 +-- toxygen/updater/updater.py | 10 +- toxygen/user_data/settings.py | 2 +- toxygen/util/ui.py | 11 +- toxygen/util/util.py | 25 +- 26 files changed, 287 insertions(+), 302 deletions(-) delete mode 100644 toxygen/login.py rename toxygen/{communication => middleware}/__init__.py (100%) rename toxygen/{communication => middleware}/callbacks.py (99%) rename toxygen/{ => middleware}/threads.py (71%) rename toxygen/{communication => middleware}/tox_factory.py (100%) delete mode 100644 toxygen/notifications.py create mode 100644 toxygen/notifications/__init__.py create mode 100644 toxygen/notifications/sound.py create mode 100644 toxygen/notifications/tray.py create mode 100644 toxygen/smileys/__init__.py rename toxygen/{smileys_and_stickers.py => smileys/smileys.py} (87%) create mode 100644 toxygen/stickers/__init__.py create mode 100644 toxygen/stickers/stickers.py rename toxygen/ui/{avwidgets.py => av_widgets.py} (100%) rename toxygen/{ => ui}/tray.py (79%) diff --git a/setup.py b/setup.py index 746163e..f586ec3 100644 --- a/setup.py +++ b/setup.py @@ -2,11 +2,11 @@ from setuptools import setup from setuptools.command.install import install from platform import system from subprocess import call -from toxygen.util import program_version +import main import sys -version = program_version + '.0' +version = main.__version__ + '.0' if system() == 'Windows': diff --git a/tests/tests.py b/tests/tests.py index b21854c..b61553e 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -8,6 +8,8 @@ import toxygen.util as util import time +# TODO: fic + class TestTox: def test_creation(self): diff --git a/toxygen/app.py b/toxygen/app.py index 8a7c799..3668c4b 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -1,11 +1,10 @@ -import communication.callbacks -import threads +from middleware import threads from PyQt5 import QtWidgets, QtGui, QtCore import ui.password_screen as passwordscreen from util.util import * import updater.updater as updater import os -from communication.tox_factory import tox_factory +from middleware.tox_factory import tox_factory import wrapper.toxencryptsave as tox_encrypt_save import user_data.toxes from user_data.settings import Settings @@ -13,6 +12,8 @@ from ui.login_screen import LoginScreen from user_data.profile_manager import ProfileManager from plugin_support.plugin_support import PluginLoader from ui.main_screen import MainWindow +from ui import tray +import util.ui as util_ui class App: @@ -20,21 +21,20 @@ class App: def __init__(self, version, path_to_profile=None, uri=None): self._version = version self._app = None - self.tox = self.ms = self.init = self.app = self.tray = self.mainloop = self.avloop = None - self.uri = self.path = self.toxes = None + self._tox = self._ms = self._init = self._app = self.tray = self._main_loop = self._av_loop = None + self.uri = self._toxes = self._tray = None if uri is not None and uri.startswith('tox:'): self.uri = uri[4:] - if path_to_profile is not None: - self.path = path_to_profile + self._path = path_to_profile def enter_pass(self, data): """ Show password screen """ - p = passwordscreen.PasswordScreen(self.toxes, data) + p = passwordscreen.PasswordScreen(self._toxes, data) p.show() - self.app.lastWindowClosed.connect(self.app.quit) - self.app.exec_() + self._app.lastWindowClosed.connect(self._app.quit) + self._app.exec_() result = p.result if result is None: raise SystemExit() @@ -45,30 +45,29 @@ class App: """ Main function of app. loads login screen if needed and starts main screen """ - app = QtWidgets.QApplication([]) + self._app= QtWidgets.QApplication([]) icon_file = os.path.join(get_images_directory(), 'icon.png') - app.setWindowIcon(QtGui.QIcon(icon_file)) - self._app = app + self._app.setWindowIcon(QtGui.QIcon(icon_file)) if get_platform() == 'Linux': QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) with open(os.path.join(get_styles_directory(), 'dark_style.qss')) as fl: style = fl.read() - app.setStyleSheet(style) + self._app.setStyleSheet(style) encrypt_save = tox_encrypt_save.ToxEncryptSave() self._toxes = user_data.toxes.ToxES(encrypt_save) - if self.path is not None: - path = os.path.dirname(self.path) + '/' - name = os.path.basename(self.path)[:-4] - self._settings = Settings(self._toxes, self.path.replace('.tox', '.json')) + if self._path is not None: + path = os.path.dirname(self._path) + '/' + name = os.path.basename(self._path)[:-4] + self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) self._profile_manager = ProfileManager(self._settings, self._toxes, path) data = self._profile_manager.open_profile() if encrypt_save.is_data_encrypted(data): data = self.enter_pass(data) - self.tox = tox_factory(data, self._settings) + self._tox = self.create_tox(data) else: auto_profile = Settings.get_auto_profile() if not auto_profile[0]: @@ -79,15 +78,15 @@ class App: if curr_lang in langs: lang_path = langs[curr_lang] translator = QtCore.QTranslator() - translator.load(curr_directory() + '/translations/' + lang_path) - app.installTranslator(translator) - app.translator = translator + translator.load(get_translations_directory() + lang_path) + self._app.installTranslator(translator) + self._app.translator = translator ls = LoginScreen() ls.setWindowIconText("Toxygen") profiles = ProfileManager.find_profiles() ls.update_select(profiles) ls.show() - app.exec_() + self._app.exec_() result = ls.result if result is None: return @@ -95,47 +94,22 @@ class App: name = get_profile_name_from_path(result.profile_path) or 'toxygen_user' pr = map(lambda x: x[1], ProfileManager.find_profiles()) if name in list(pr): - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("MainWindow", "Error")) - text = (QtWidgets.QApplication.translate("MainWindow", - 'Profile with this name already exists')) - msgBox.setText(text) - msgBox.exec_() + util_ui.message_box(util_ui.tr('Profile with this name already exists'), + util_ui.tr('Error')) return - self.tox = tox_factory() - self.tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') - self.tox.self_set_status_message(b'Toxing on Toxygen') - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", - 'Do you want to set profile password?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - set_pass = SetProfilePasswordScreen(encrypt_save) - set_pass.show() - self.app.lastWindowClosed.connect(self.app.quit) - self.app.exec_() - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", - 'Do you want to save profile in default folder? If no, profile will be saved in program folder'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - path = Settings.get_default_path() - else: - path = curr_directory() + '/' + self._tox = tox_factory() + self._tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') + self._tox.self_set_status_message(b'Toxing on Toxygen') + # TODO: set profile password + path = result.profile_path + self._profile_manager = ProfileManager(self._toxes, path) try: - ProfileManager(path, name).save_profile(self.tox.get_savedata()) + self._profile_manager.save_profile(self._tox.get_savedata()) except Exception as ex: print(str(ex)) log('Profile creation exception: ' + str(ex)) - msgBox = QtWidgets.QMessageBox() - msgBox.setText(QtWidgets.QApplication.translate("login", - 'Profile saving error! Does Toxygen have permission to write to this directory?')) - msgBox.exec_() + text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') + util_ui.message_box(text, util_ui.tr('Error')) return path = Settings.get_default_path() self._settings = Settings() @@ -151,7 +125,7 @@ class App: data = self._profile_manager.open_profile() if self._toxes.is_data_encrypted(data): data = self.enter_pass(data) - self._tox = tox_factory(data, self._settings) + self._tox = self.create_tox(data) else: path, name = auto_profile self._settings = Settings(self._toxes, path + name + '.json') @@ -159,15 +133,13 @@ class App: data = self._profile_manager.open_profile() if encrypt_save.is_data_encrypted(data): data = self.enter_pass(data) - self.tox = tox_factory(data, self._settings) + self.tox = self.create_tox(data) if Settings.is_active_profile(path, get_profile_name_from_path(path)): # profile is in use - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply != QtWidgets.QMessageBox.Yes: + title = util_ui.tr('Profile {}').format(name) + text = util_ui.tr('Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') + reply = util_ui.question(text, title) + if not reply: return else: self._settings.set_active_profile() @@ -175,82 +147,49 @@ class App: self.load_app_styles() self.load_app_translations() - # tray icon - - self.ms = MainWindow(self._settings, self._tox, self.reset, self.tray) - self._profile = self.ms.profile - self.ms.show() - - updating = updater.start_update_if_needed(self._version, self._settings) - if updating: - data = self.tox.get_savedata() - self._profile_manager.save_profile(data) - self._settings.close() - del self.tox + if self.try_to_update(): return - plugin_helper = PluginLoader(self._tox, self._toxes, self._profile, self._settings) # plugin support - plugin_helper.load() + self._ms = MainWindow(self._settings, self._tox, self.reset, self._tray) + self._profile = self._ms.profile + self._ms.show() - # init thread - self.init = threads.InitThread(self.tox, self.ms, self.tray) - self.init.start() + self._tray = tray.init_tray(self._profile, self._settings, self._ms) + self._tray.show() - # starting threads for tox iterate and toxav iterate - self.mainloop = threads.ToxIterateThread(self._tox) - self.mainloop.start() - self.avloop = threads.ToxAVIterateThread(self._tox.AV) - self.avloop.start() + self._plugin_loader = PluginLoader(self._tox, self._toxes, self._profile, self._settings) # plugins support + self._plugin_loader.load() # TODO; move to separate thread? if self.uri is not None: - self.ms.add_contact(self.uri) + self._ms.add_contact(self.uri) - app.lastWindowClosed.connect(app.quit) - app.exec_() + self._app.lastWindowClosed.connect(self._app.quit) + self._app.exec_() - self.init.stop = True - self.mainloop.stop = True - self.avloop.stop = True - plugin_helper.stop() - self.mainloop.wait() - self.init.wait() - self.avloop.wait() - self.tray.hide() - data = self.tox.get_savedata() + self._plugin_loader.stop() + self.stop_threads() + self._tray.hide() + data = self._tox.get_savedata() self._profile_manager.save_profile(data) self._settings.close() - del self.tox + del self._tox def reset(self): """ Create new tox instance (new network settings) :return: tox instance """ - self.mainloop.stop = True - self.init.stop = True - self.avloop.stop = True - self.mainloop.wait() - self.init.wait() - self.avloop.wait() - data = self.tox.get_savedata() + self.stop_threads() + data = self._tox.get_savedata() self._profile_manager.save_profile(data) - del self.tox + del self._tox # create new tox instance - self.tox = tox_factory(data, self._settings) - # init thread - self.init = threads.InitThread(self.tox, self.ms, self.tray) - self.init.start() + self._tox = tox_factory(data, self._settings) + self.start_threads() - # starting threads for tox iterate and toxav iterate - self.mainloop = threads.ToxIterateThread(self.tox) - self.mainloop.start() + self._plugin_loader.set_tox(self._tox) - self.avloop = threads.ToxAVIterateThread(self.tox.AV) - self.avloop.start() - - self._plugin_loader.set_tox(self.tox) - - return self.tox + return self._tox def load_app_styles(self): # application color scheme @@ -266,3 +205,32 @@ class App: translator.load(curr_directory(__file__) + '/translations/' + lang) self._app.installTranslator(translator) self._app.translator = translator + + def try_to_update(self): + updating = updater.start_update_if_needed(self._version, self._settings) + if updating: + data = self._tox.get_savedata() + self._profile_manager.save_profile(data) + self._settings.close() + del self._tox + return updating + + def start_threads(self): + # init thread + self._init = threads.InitThread(self._tox, self._ms, self._tray) + self._init.start() + + # starting threads for tox iterate and toxav iterate + self._main_loop = threads.ToxIterateThread(self._tox) + self._main_loop.start() + self._av_loop = threads.ToxAVIterateThread(self._tox.AV) + self._av_loop.start() + + def stop_threads(self): + self._init.stop_thread() + + self._main_loop.stop_thread() + self._av_loop.stop_thread() + + def create_tox(self, data): + return tox_factory(data, self._settings) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index ae7767a..ef26a51 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -11,7 +11,7 @@ import time from av import calls import plugin_support from contacts import basecontact -from ui import items_factory, avwidgets +from ui import items_factory, av_widgets import cv2 import threading from contacts.group_chat import * diff --git a/toxygen/login.py b/toxygen/login.py deleted file mode 100644 index d68629a..0000000 --- a/toxygen/login.py +++ /dev/null @@ -1,19 +0,0 @@ -class Login: - - def __init__(self, arr): - self.arr = arr - - def login_screen_close(self, t, number=-1, default=False, name=None): - """ Function which processes data from login screen - :param t: 0 - window was closed, 1 - new profile was created, 2 - profile loaded - :param number: num of chosen profile in list (-1 by default) - :param default: was or not chosen profile marked as default - :param name: name of new profile - """ - self.t = t - self.num = number - self.default = default - self.name = name - - def get_data(self): - return self.arr[self.num] \ No newline at end of file diff --git a/toxygen/main.py b/toxygen/main.py index 4230668..97a5363 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -4,6 +4,7 @@ from user_data.settings import * from util.util import curr_directory, remove import argparse + __maintainer__ = 'Ingvar' __version__ = '0.5.0' diff --git a/toxygen/communication/__init__.py b/toxygen/middleware/__init__.py similarity index 100% rename from toxygen/communication/__init__.py rename to toxygen/middleware/__init__.py diff --git a/toxygen/communication/callbacks.py b/toxygen/middleware/callbacks.py similarity index 99% rename from toxygen/communication/callbacks.py rename to toxygen/middleware/callbacks.py index 0566325..4db161e 100644 --- a/toxygen/communication/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -1,5 +1,4 @@ from PyQt5 import QtGui -from notifications import * from user_data.settings import Settings from contacts.profile import Profile from wrapper.toxcore_enums_and_consts import * @@ -8,7 +7,7 @@ from wrapper.tox import bin_to_string from plugin_support.plugin_support import PluginLoader import cv2 import numpy as np -from threads import invoke_in_main_thread, execute +from middleware.threads import invoke_in_main_thread, execute # TODO: use closures diff --git a/toxygen/threads.py b/toxygen/middleware/threads.py similarity index 71% rename from toxygen/threads.py rename to toxygen/middleware/threads.py index 4a129d1..eb5a9d7 100644 --- a/toxygen/threads.py +++ b/toxygen/middleware/threads.py @@ -1,17 +1,26 @@ -from PyQt5 import QtCore from bootstrap.bootstrap import * import threading import queue from util import util +import time +class BaseThread(threading.Thread): -class InitThread(QtCore.QThread): + def __init__(self): + super().__init__() + self._stop = False + + def stop_thread(self): + self._stop = True + self.join() + + +class InitThread(BaseThread): def __init__(self, tox, ms, tray): - QtCore.QThread.__init__(self) + super().__init__() self.tox, self.ms, self.tray = tox, ms, tray - self.stop = False def run(self): # initializing callbacks @@ -21,71 +30,65 @@ class InitThread(QtCore.QThread): # bootstrap try: for data in generate_nodes(): - if self.stop: + if self._stop: return self.tox.bootstrap(*data) self.tox.add_tcp_relay(*data) except: pass for _ in range(10): - if self.stop: + if self._stop: return - self.msleep(1000) + time.sleep(1) while not self.tox.self_get_connection_status(): try: for data in generate_nodes(): - if self.stop: + if self._stop: return self.tox.bootstrap(*data) self.tox.add_tcp_relay(*data) except: pass finally: - self.msleep(5000) + time.sleep(5) -class ToxIterateThread(QtCore.QThread): +class ToxIterateThread(BaseThread): def __init__(self, tox): - QtCore.QThread.__init__(self) - self.tox = tox - self.stop = False + super().__init__() + self._tox = tox def run(self): - while not self.stop: - self.tox.iterate() - self.msleep(self.tox.iteration_interval()) + while not self._stop: + self._tox.iterate() + time.sleep(self._tox.iteration_interval() / 1000) -class ToxAVIterateThread(QtCore.QThread): +class ToxAVIterateThread(BaseThread): def __init__(self, toxav): - QtCore.QThread.__init__(self) - self.toxav = toxav - self.stop = False + super().__init__() + self._toxav = toxav def run(self): - while not self.stop: - self.toxav.iterate() - self.msleep(self.toxav.iteration_interval()) + while not self._stop: + self._toxav.iterate() + time.sleep(self._toxav.iteration_interval() / 1000) -class FileTransfersThread(threading.Thread): +class FileTransfersThread(BaseThread): def __init__(self): + super().__init__() self._queue = queue.Queue() self._timeout = 0.01 - self._continue = True - super().__init__() def execute(self, func, *args, **kwargs): self._queue.put((func, args, kwargs)) - def stop(self): - self._continue = False - def run(self): - while self._continue: + while not self._stop: try: func, args, kwargs = self._queue.get(timeout=self._timeout) func(*args, **kwargs) @@ -105,8 +108,7 @@ def start(): def stop(): - _thread.stop() - _thread.join() + _thread.stop_thread() def execute(func, *args, **kwargs): diff --git a/toxygen/communication/tox_factory.py b/toxygen/middleware/tox_factory.py similarity index 100% rename from toxygen/communication/tox_factory.py rename to toxygen/middleware/tox_factory.py diff --git a/toxygen/notifications.py b/toxygen/notifications.py deleted file mode 100644 index cf926d1..0000000 --- a/toxygen/notifications.py +++ /dev/null @@ -1,71 +0,0 @@ -from PyQt5 import QtCore, QtWidgets -from util.util import curr_directory -import wave -import pyaudio - - -SOUND_NOTIFICATION = { - 'MESSAGE': 0, - 'FRIEND_CONNECTION_STATUS': 1, - 'FILE_TRANSFER': 2 -} - - -def tray_notification(title, text, tray, window): - """ - Show tray notification and activate window icon - NOTE: different behaviour on different OS - :param title: Name of user who sent message or file - :param text: text of message or file info - :param tray: ref to tray icon - :param window: main window - """ - if QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): - if len(text) > 30: - text = text[:27] + '...' - tray.showMessage(title, text, QtWidgets.QSystemTrayIcon.NoIcon, 3000) - QtWidgets.QApplication.alert(window, 0) - - def message_clicked(): - window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) - window.activateWindow() - tray.messageClicked.connect(message_clicked) - - -class AudioFile: - chunk = 1024 - - def __init__(self, fl): - self.wf = wave.open(fl, 'rb') - self.p = pyaudio.PyAudio() - self.stream = self.p.open( - format=self.p.get_format_from_width(self.wf.getsampwidth()), - channels=self.wf.getnchannels(), - rate=self.wf.getframerate(), - output=True) - - def play(self): - data = self.wf.readframes(self.chunk) - while data: - self.stream.write(data) - data = self.wf.readframes(self.chunk) - - def close(self): - self.stream.close() - self.p.terminate() - - -def sound_notification(t): - """ - Plays sound notification - :param t: type of notification - """ - if t == SOUND_NOTIFICATION['MESSAGE']: - f = curr_directory() + '/sounds/message.wav' - elif t == SOUND_NOTIFICATION['FILE_TRANSFER']: - f = curr_directory() + '/sounds/file.wav' - else: - f = curr_directory() + '/sounds/contact.wav' - a = AudioFile(f) - a.play() - a.close() diff --git a/toxygen/notifications/__init__.py b/toxygen/notifications/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/notifications/sound.py b/toxygen/notifications/sound.py new file mode 100644 index 0000000..a0b93f0 --- /dev/null +++ b/toxygen/notifications/sound.py @@ -0,0 +1,54 @@ +import util.util +import wave +import pyaudio +import os.path + + +SOUND_NOTIFICATION = { + 'MESSAGE': 0, + 'FRIEND_CONNECTION_STATUS': 1, + 'FILE_TRANSFER': 2 +} + + +class AudioFile: + chunk = 1024 + + def __init__(self, fl): + self.wf = wave.open(fl, 'rb') + self.p = pyaudio.PyAudio() + self.stream = self.p.open( + format=self.p.get_format_from_width(self.wf.getsampwidth()), + channels=self.wf.getnchannels(), + rate=self.wf.getframerate(), + output=True) + + def play(self): + data = self.wf.readframes(self.chunk) + while data: + self.stream.write(data) + data = self.wf.readframes(self.chunk) + + def close(self): + self.stream.close() + self.p.terminate() + + +def sound_notification(t): + """ + Plays sound notification + :param t: type of notification + """ + if t == SOUND_NOTIFICATION['MESSAGE']: + f = get_file_path('message.wav') + elif t == SOUND_NOTIFICATION['FILE_TRANSFER']: + f = get_file_path('file.wav') + else: + f = get_file_path('contact.wav') + a = AudioFile(f) + a.play() + a.close() + + +def get_file_path(file_name): + return os.path.join(util.util.get_sounds_directory(), file_name) diff --git a/toxygen/notifications/tray.py b/toxygen/notifications/tray.py new file mode 100644 index 0000000..4232253 --- /dev/null +++ b/toxygen/notifications/tray.py @@ -0,0 +1,22 @@ +from PyQt5 import QtCore, QtWidgets + + +def tray_notification(title, text, tray, window): + """ + Show tray notification and activate window icon + NOTE: different behaviour on different OS + :param title: Name of user who sent message or file + :param text: text of message or file info + :param tray: ref to tray icon + :param window: main window + """ + if QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): + if len(text) > 30: + text = text[:27] + '...' + tray.showMessage(title, text, QtWidgets.QSystemTrayIcon.NoIcon, 3000) + QtWidgets.QApplication.alert(window, 0) + + def message_clicked(): + window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + window.activateWindow() + tray.messageClicked.connect(message_clicked) diff --git a/toxygen/smileys/__init__.py b/toxygen/smileys/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/smileys_and_stickers.py b/toxygen/smileys/smileys.py similarity index 87% rename from toxygen/smileys_and_stickers.py rename to toxygen/smileys/smileys.py index dd72fd9..abf6990 100644 --- a/toxygen/smileys_and_stickers.py +++ b/toxygen/smileys/smileys.py @@ -47,6 +47,7 @@ class SmileyLoader: def get_smileys_path(self): return util.curr_directory() + '/smileys/' + self._curr_pack + '/' if self._curr_pack is not None else None + @staticmethod def get_packs_list(self): d = util.curr_directory() + '/smileys/' return [x[1] for x in os.walk(d)][0] @@ -71,18 +72,3 @@ class SmileyLoader: if file_name.endswith('.gif'): # animated smiley edit.addAnimation(QtCore.QUrl(file_name), self.get_smileys_path() + file_name) return ' '.join(arr) - - -def sticker_loader(): - """ - :return list of stickers - """ - result = [] - d = util.curr_directory() + '/stickers/' - keys = [x[1] for x in os.walk(d)][0] - for key in keys: - path = d + key + '/' - files = filter(lambda f: f.endswith('.png'), os.listdir(path)) - files = map(lambda f: str(path + f), files) - result.extend(files) - return result diff --git a/toxygen/stickers/__init__.py b/toxygen/stickers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/stickers/stickers.py b/toxygen/stickers/stickers.py new file mode 100644 index 0000000..5ad6aa1 --- /dev/null +++ b/toxygen/stickers/stickers.py @@ -0,0 +1,18 @@ +import os +import util.util as util + + +def load_stickers(): + """ + :return list of stickers + """ + result = [] + d = util.get_stickers_directory() + keys = [x[1] for x in os.walk(d)][0] + for key in keys: + path = d + key + '/' + files = filter(lambda f: f.endswith('.png'), os.listdir(path)) + files = map(lambda f: str(path + f), files) + result.extend(files) + + return result diff --git a/toxygen/ui/avwidgets.py b/toxygen/ui/av_widgets.py similarity index 100% rename from toxygen/ui/avwidgets.py rename to toxygen/ui/av_widgets.py diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index d3896be..55e7c8e 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -6,6 +6,7 @@ import plugin_support from ui.main_screen_widgets import * from user_data import toxes, settings import util.util as util +import util.ui as util_ui class MainWindow(QtWidgets.QMainWindow): @@ -414,12 +415,10 @@ class MainWindow(QtWidgets.QMainWindow): # ----------------------------------------------------------------------------------------------------------------- def about_program(self): - import util - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "About")) - text = (QtWidgets.QApplication.translate("MainWindow", 'Toxygen is Tox client written on Python.\nVersion: ')) - msgBox.setText(text + util.program_version + '\nGitHub: https://github.com/toxygen-project/toxygen/') - msgBox.exec_() + text = util_ui.tr('Toxygen is Tox client written on Python.\nVersion: ') + text += '' + '\nGitHub: https://github.com/toxygen-project/toxygen/' + title = util_ui.tr('About') + util_ui.message_box(text, title) def network_settings(self): self.n_s = NetworkSettings(self.reset) diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index bbf6c5a..319b3df 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -2,7 +2,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit from contacts.profile import Profile import smileys -import util +import util.util as util class MessageArea(QtWidgets.QPlainTextEdit): @@ -194,7 +194,7 @@ class DropdownMenu(QtWidgets.QWidget): self.stickerButton = QtWidgets.QPushButton(self) self.stickerButton.setGeometry(QtCore.QRect(60, 0, 60, 60)) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/file.png') + pixmap = QtGui.QPixmap(util.get_images_directory() + 'file.png') icon = QtGui.QIcon(pixmap) self.fileTransferButton.setIcon(icon) self.fileTransferButton.setIconSize(QtCore.QSize(50, 50)) diff --git a/toxygen/tray.py b/toxygen/ui/tray.py similarity index 79% rename from toxygen/tray.py rename to toxygen/ui/tray.py index 0c474b4..ebdbd33 100644 --- a/toxygen/tray.py +++ b/toxygen/ui/tray.py @@ -1,6 +1,7 @@ from PyQt5 import QtWidgets, QtGui, QtCore from util.ui import tr -from util.util import curr_directory +from util.util import get_images_directory +import os.path class SystemTrayIcon(QtWidgets.QSystemTrayIcon): @@ -8,11 +9,11 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon): leftClicked = QtCore.pyqtSignal() def __init__(self, icon, parent=None): - super().__init__(self, icon, parent) - self.activated.connect(self.iconActivated) + super().__init__(icon, parent) + self.activated.connect(self.icon_activated) - def iconActivated(self, reason): - if reason == QtGui.QSystemTrayIcon.Trigger: + def icon_activated(self, reason): + if reason == QtWidgets.QSystemTrayIcon.Trigger: self.leftClicked.emit() @@ -52,20 +53,21 @@ class Menu(QtWidgets.QMenu): def init_tray(profile, settings, main_screen): - tray = SystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) + icon = os.path.join(get_images_directory(), 'icon.png') + tray = SystemTrayIcon(QtGui.QIcon(icon)) tray.setObjectName('tray') - m = Menu(settings, profile) - show = m.addAction(tr('Open Toxygen')) - sub = m.addMenu(tr('Set status')) + menu = Menu(settings, profile) + show = menu.addAction(tr('Open Toxygen')) + sub = menu.addMenu(tr('Set status')) online = sub.addAction(tr('Online')) away = sub.addAction(tr('Away')) busy = sub.addAction(tr('Busy')) online.setCheckable(True) away.setCheckable(True) busy.setCheckable(True) - m.act = sub - exit = m.addAction(tr('Exit')) + menu.act = sub + exit = menu.addAction(tr('Exit')) def show_window(): def show(): @@ -96,12 +98,12 @@ def init_tray(profile, settings, main_screen): show.triggered.connect(show_window) exit.triggered.connect(close_app) - m.aboutToShow.connect(lambda: m.aboutToShowHandler()) - online.triggered.connect(lambda: m.newStatus(0)) - away.triggered.connect(lambda: m.newStatus(1)) - busy.triggered.connect(lambda: m.newStatus(2)) + menu.aboutToShow.connect(lambda: menu.aboutToShowHandler()) + online.triggered.connect(lambda: menu.newStatus(0)) + away.triggered.connect(lambda: menu.newStatus(1)) + busy.triggered.connect(lambda: menu.newStatus(2)) - tray.setContextMenu(m) + tray.setContextMenu(menu) tray.show() tray.activated.connect(tray_activated) diff --git a/toxygen/updater/updater.py b/toxygen/updater/updater.py index 575dbfe..f274161 100644 --- a/toxygen/updater/updater.py +++ b/toxygen/updater/updater.py @@ -118,14 +118,8 @@ def start_update_if_needed(version, settings): download(version) updating = True else: - reply = QtWidgets.QMessageBox.question(None, - 'Toxygen', - QtWidgets.QApplication.translate("login", - 'Update for Toxygen was found. Download and install it?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: + reply = util_ui.question(util_ui.tr('Update for Toxygen was found. Download and install it?')) + if reply: download(version) updating = True - return updating diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 39fd333..9226a81 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -4,7 +4,7 @@ import os from util.util import log, curr_directory, append_slash import pyaudio from user_data.toxes import ToxES -import smileys_and_stickers as smileys +import smileys.smileys as smileys class Settings(dict): diff --git a/toxygen/util/ui.py b/toxygen/util/ui.py index be613cb..c485de4 100644 --- a/toxygen/util/ui.py +++ b/toxygen/util/ui.py @@ -5,10 +5,17 @@ def tr(s): return PyQt5.QtWidgets.QApplication.translate('Toxygen', s) -def question(text): - reply = PyQt5.QtWidgets.QMessageBox.question(None, 'Toxygen', text, +def question(text, title=None): + reply = PyQt5.QtWidgets.QMessageBox.question(None, title or 'Toxygen', text, PyQt5.QtWidgets.QMessageBox.Yes, PyQt5.QtWidgets.QMessageBox.No) return reply == PyQt5.QtWidgets.QMessageBox.Yes + +def message_box(text, title=None): + m_box = PyQt5.QtWidgets.QMessageBox() + m_box.setText(tr(text)) + m_box.setWindowTitle(title or 'Toxygen') + m_box.exec_() + # TODO: move all dialogs here diff --git a/toxygen/util/util.py b/toxygen/util/util.py index 91a4aec..5bf5f99 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -35,12 +35,33 @@ def get_base_directory(current_file=None): return os.path.dirname(curr_directory(current_file or __file__)) +@cached def get_images_directory(): - return os.path.join(get_base_directory(), 'images') + return get_app_directory('images') +@cached def get_styles_directory(): - return os.path.join(get_base_directory(), 'styles') + return get_app_directory('styles') + + +@cached +def get_sounds_directory(): + return get_app_directory('sounds') + + +@cached +def get_stickers_directory(): + return get_app_directory('stickers') + + +@cached +def get_translations_directory(): + return get_app_directory('translations') + + +def get_app_directory(directory_name): + return os.path.join(get_base_directory(), directory_name) def get_profile_name_from_path(path): From 0ba1aadf70087ed815fcea6e5ce1410545e6eb95 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 17 Apr 2018 21:08:22 +0300 Subject: [PATCH 008/217] app.py and main.py refactoring and fixes --- toxygen/app.py | 194 +++++++++++++++++--------------- toxygen/main.py | 14 +-- toxygen/middleware/callbacks.py | 38 ++++--- toxygen/middleware/threads.py | 38 +++---- toxygen/user_data/settings.py | 18 ++- 5 files changed, 160 insertions(+), 142 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 3668c4b..85da363 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -1,6 +1,7 @@ from middleware import threads +import middleware.callbacks as callbacks from PyQt5 import QtWidgets, QtGui, QtCore -import ui.password_screen as passwordscreen +import ui.password_screen as password_screen from util.util import * import updater.updater as updater import os @@ -20,32 +21,30 @@ class App: def __init__(self, version, path_to_profile=None, uri=None): self._version = version - self._app = None - self._tox = self._ms = self._init = self._app = self.tray = self._main_loop = self._av_loop = None - self.uri = self._toxes = self._tray = None + self._app = self._settings = self._profile_manager = self._plugin_loader = None + self._tox = self._ms = self._init = self.tray = self._main_loop = self._av_loop = None + self._uri = self._toxes = self._tray = None if uri is not None and uri.startswith('tox:'): - self.uri = uri[4:] + self._uri = uri[4:] self._path = path_to_profile def enter_pass(self, data): """ Show password screen """ - p = passwordscreen.PasswordScreen(self._toxes, data) + p = password_screen.PasswordScreen(self._toxes, data) p.show() self._app.lastWindowClosed.connect(self._app.quit) self._app.exec_() - result = p.result - if result is None: - raise SystemExit() - else: - return result + if p.result is not None: + return p.result + raise SystemExit() def main(self): """ Main function of app. loads login screen if needed and starts main screen """ - self._app= QtWidgets.QApplication([]) + self._app = QtWidgets.QApplication([]) icon_file = os.path.join(get_images_directory(), 'icon.png') self._app.setWindowIcon(QtGui.QIcon(icon_file)) @@ -59,28 +58,13 @@ class App: encrypt_save = tox_encrypt_save.ToxEncryptSave() self._toxes = user_data.toxes.ToxES(encrypt_save) - if self._path is not None: - path = os.path.dirname(self._path) + '/' - name = os.path.basename(self._path)[:-4] - self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) - self._profile_manager = ProfileManager(self._settings, self._toxes, path) - data = self._profile_manager.open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - self._tox = self.create_tox(data) + if self._path is not None: # toxygen was started with path to profile + self.load_existing_profile(self._path) else: auto_profile = Settings.get_auto_profile() - if not auto_profile[0]: + if auto_profile is None: # no default profile # show login screen if default profile not found - current_locale = QtCore.QLocale() - curr_lang = current_locale.languageToString(current_locale.language()) - langs = Settings.supported_languages() - if curr_lang in langs: - lang_path = langs[curr_lang] - translator = QtCore.QTranslator() - translator.load(get_translations_directory() + lang_path) - self._app.installTranslator(translator) - self._app.translator = translator + self.load_login_screen_translations() ls = LoginScreen() ls.setWindowIconText("Toxygen") profiles = ProfileManager.find_profiles() @@ -90,59 +74,25 @@ class App: result = ls.result if result is None: return - elif result.is_new_profile(): # create new profile - name = get_profile_name_from_path(result.profile_path) or 'toxygen_user' - pr = map(lambda x: x[1], ProfileManager.find_profiles()) - if name in list(pr): - util_ui.message_box(util_ui.tr('Profile with this name already exists'), - util_ui.tr('Error')) - return - self._tox = tox_factory() - self._tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') - self._tox.self_set_status_message(b'Toxing on Toxygen') - # TODO: set profile password - path = result.profile_path - self._profile_manager = ProfileManager(self._toxes, path) - try: - self._profile_manager.save_profile(self._tox.get_savedata()) - except Exception as ex: - print(str(ex)) - log('Profile creation exception: ' + str(ex)) - text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') - util_ui.message_box(text, util_ui.tr('Error')) - return - path = Settings.get_default_path() - self._settings = Settings() - if curr_lang in langs: - self._settings['language'] = curr_lang - self._settings.save() + if result.is_new_profile(): # create new profile + self.create_new_profile(result.profile_path) else: # load existing profile - path = result.profile_path - if result.load_as_default: - Settings.set_auto_profile(path) - self._settings = Settings(self._toxes, path.replace('.tox', '.json')) - self._profile_manager = ProfileManager(self._settings, self._toxes, path) - data = self._profile_manager.open_profile() - if self._toxes.is_data_encrypted(data): - data = self.enter_pass(data) - self._tox = self.create_tox(data) - else: + self.load_existing_profile(result.profile_path) + self._path = result.profile_path + else: # default profile path, name = auto_profile - self._settings = Settings(self._toxes, path + name + '.json') - self._profile_manager = ProfileManager(self._settings, self._toxes, path) - data = self._profile_manager.open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - self.tox = self.create_tox(data) + self._path = os.path.join(path, name + '.tox') + self.load_existing_profile(self._path) - if Settings.is_active_profile(path, get_profile_name_from_path(path)): # profile is in use - title = util_ui.tr('Profile {}').format(name) + if Settings.is_active_profile(self._path): # profile is in use + profile_name = get_profile_name_from_path(self._path) + title = util_ui.tr('Profile {}').format(profile_name) text = util_ui.tr('Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') reply = util_ui.question(text, title) if not reply: return - else: - self._settings.set_active_profile() + + self._settings.set_active_profile() self.load_app_styles() self.load_app_translations() @@ -151,17 +101,21 @@ class App: return self._ms = MainWindow(self._settings, self._tox, self.reset, self._tray) - self._profile = self._ms.profile + profile = self._ms.profile self._ms.show() - self._tray = tray.init_tray(self._profile, self._settings, self._ms) + self._tray = tray.init_tray(profile, self._settings, self._ms) self._tray.show() - self._plugin_loader = PluginLoader(self._tox, self._toxes, self._profile, self._settings) # plugins support - self._plugin_loader.load() # TODO; move to separate thread? + self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) # plugins support + self.start_threads() - if self.uri is not None: - self._ms.add_contact(self.uri) + # callbacks initialization + callbacks.init_callbacks(self._tox, profile, self._settings, self._plugin_loader, None, None, None, + self._ms, self._tray) + + if self._uri is not None: + self._ms.add_contact(self._uri) self._app.lastWindowClosed.connect(self._app.quit) self._app.exec_() @@ -169,8 +123,7 @@ class App: self._plugin_loader.stop() self.stop_threads() self._tray.hide() - data = self._tox.get_savedata() - self._profile_manager.save_profile(data) + self.save_profile() self._settings.close() del self._tox @@ -181,10 +134,10 @@ class App: """ self.stop_threads() data = self._tox.get_savedata() - self._profile_manager.save_profile(data) + self.save_profile(data) del self._tox # create new tox instance - self._tox = tox_factory(data, self._settings) + self._tox = self.create_tox(data) self.start_threads() self._plugin_loader.set_tox(self._tox) @@ -199,25 +152,41 @@ class App: style = fl.read() self._app.setStyleSheet(style) + def load_login_screen_translations(self): + current_language, supported_languages = self.get_languages() + if current_language in supported_languages: + lang_path = supported_languages[current_language] + translator = QtCore.QTranslator() + translator.load(get_translations_directory() + lang_path) + self._app.installTranslator(translator) + self._app.translator = translator + + @staticmethod + def get_languages(): + current_locale = QtCore.QLocale() + curr_language = current_locale.languageToString(current_locale.language()) + supported_languages = Settings.supported_languages() + + return curr_language, supported_languages + def load_app_translations(self): lang = Settings.supported_languages()[self._settings['language']] translator = QtCore.QTranslator() - translator.load(curr_directory(__file__) + '/translations/' + lang) + translator.load(os.path.join(get_translations_directory(), lang)) self._app.installTranslator(translator) self._app.translator = translator def try_to_update(self): updating = updater.start_update_if_needed(self._version, self._settings) if updating: - data = self._tox.get_savedata() - self._profile_manager.save_profile(data) + self.save_profile() self._settings.close() del self._tox return updating def start_threads(self): # init thread - self._init = threads.InitThread(self._tox, self._ms, self._tray) + self._init = threads.InitThread(self._tox, self._plugin_loader) self._init.start() # starting threads for tox iterate and toxav iterate @@ -226,11 +195,52 @@ class App: self._av_loop = threads.ToxAVIterateThread(self._tox.AV) self._av_loop.start() + threads.start_file_transfer_thread() + def stop_threads(self): self._init.stop_thread() - self._main_loop.stop_thread() self._av_loop.stop_thread() + self._main_loop.stop_thread() + + threads.stop_file_transfer_thread() def create_tox(self, data): return tox_factory(data, self._settings) + + def load_existing_profile(self, profile_path): + self._settings = Settings(self._toxes, profile_path.replace('.tox', '.json')) + self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) + data = self._profile_manager.open_profile() + if self._toxes.is_data_encrypted(data): + data = self.enter_pass(data) + self._tox = self.create_tox(data) + + def create_new_profile(self, profile_path): + name = get_profile_name_from_path(profile_path) or 'toxygen_user' + if os.path.isfile(profile_path): + util_ui.message_box(util_ui.tr('Profile with this name already exists'), + util_ui.tr('Error')) + return + self._tox = tox_factory() + self._tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') + self._tox.self_set_status_message(b'Toxing on Toxygen') + # TODO: set profile password + self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) + self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) + try: + self.save_profile() + except Exception as ex: + print(ex) + log('Profile creation exception: ' + str(ex)) + text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') + util_ui.message_box(text, util_ui.tr('Error')) + return + current_language, supported_languages = self.get_languages() + if current_language in supported_languages: + self._settings['language'] = current_language + self._settings.save() + + def save_profile(self, data=None): + data = data or self._tox.get_savedata() + self._profile_manager.save_profile(data) diff --git a/toxygen/main.py b/toxygen/main.py index 97a5363..cdaa7e5 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,4 +1,3 @@ -import sys import app from user_data.settings import * from util.util import curr_directory, remove @@ -11,7 +10,7 @@ __version__ = '0.5.0' def clean(): """Removes all windows libs from libs folder""" - d = curr_directory() + '/libs/' + d = os.path.join(curr_directory(__file__), 'libs') remove(d) @@ -25,10 +24,11 @@ def print_toxygen_version(): def main(): parser = argparse.ArgumentParser() - parser.add_argument('--version', help='Prints Toxygen version') - parser.add_argument('--clean', help='Deletes toxcore libs from libs folder') - parser.add_argument('--reset', help='Resets default profile') - parser.add_argument('profile_path', nargs='?', default=None, help='Resets default profile') + parser.add_argument('--version', action='store_true', help='Prints Toxygen version') + parser.add_argument('--clean', action='store_true', help='Deletes toxcore libs from libs folder') + parser.add_argument('--reset', action='store_true', help='Resets default profile') + parser.add_argument('--uri', help='Adds specified TOX ID to friends') + parser.add_argument('profile', nargs='?', default=None, help='Path to TOX profile') args = parser.parse_args() if args.version: @@ -43,7 +43,7 @@ def main(): reset() return - toxygen = app.App(__version__, path_to_profile=args.profile_path) + toxygen = app.App(__version__, args.profile, args.uri) toxygen.main() diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 4db161e..926c7af 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -1,15 +1,18 @@ -from PyQt5 import QtGui +from PyQt5 import QtGui, QtCore from user_data.settings import Settings from contacts.profile import Profile from wrapper.toxcore_enums_and_consts import * from wrapper.toxav_enums import * from wrapper.tox import bin_to_string -from plugin_support.plugin_support import PluginLoader +import util.ui as util_ui +import util.util as util import cv2 import numpy as np from middleware.threads import invoke_in_main_thread, execute +from notifications.tray import tray_notification +from notifications.sound import * -# TODO: use closures +# TODO: gc callbacks and refactoring # ----------------------------------------------------------------------------------------------------------------- # Callbacks - current user @@ -111,7 +114,8 @@ def friend_message(profile, settings, window, tray): invoke_in_main_thread(tray_notification, friend.name, message, tray, window) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['MESSAGE']) - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) return wrapped @@ -169,11 +173,12 @@ def tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager if not window.isActiveWindow(): friend = contacts_manager.get_friend_by_number(friend_number) if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: - file_from = QtWidgets.QApplication.translate("Callback", "File from") + file_from = util_ui.tr("File from") invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER']) - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) else: # AVATAR print('Avatar') invoke_in_main_thread(file_transfer_handler.incoming_avatar, @@ -240,8 +245,7 @@ def lossy_packet(plugin_loader): Incoming lossy packet """ data = data[:length] - plugin = PluginLoader.get_instance() - invoke_in_main_thread(plugin.callback_lossy, friend_number, data) + invoke_in_main_thread(plugin_loader.callback_lossy, friend_number, data) return wrapped @@ -358,7 +362,8 @@ def show_gc_notification(window, tray, message, group_number, peer_number): invoke_in_main_thread(tray_notification, chat.name + ' ' + peer_name, message, tray, window) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['MESSAGE']) - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization @@ -366,17 +371,20 @@ def show_gc_notification(window, tray, message, group_number, peer_number): def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, - calls_manager, file_transfer_handler, window, tray): + calls_manager, file_transfer_handler, main_window, tray): """ Initialization of all callbacks. - :param tox: tox instance - :param window: main window + :param tox: Tox instance + :param profile: Profile instance + :param settings: Settings instance + :param plugin_loader: PluginLoader instance + :param main_window: main window screen :param tray: tray (for notifications) """ tox.callback_self_connection_status(self_connection_status(tox, profile), 0) tox.callback_friend_status(friend_status(profile, settings), 0) - tox.callback_friend_message(friend_message(profile, settings, window, tray), 0) + tox.callback_friend_message(friend_message(profile, settings, main_window, tray), 0) tox.callback_friend_connection_status(friend_connection_status(profile, settings, plugin_loader), 0) tox.callback_friend_name(friend_name(profile), 0) tox.callback_friend_status_message(friend_status_message(profile), 0) @@ -384,11 +392,13 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_friend_typing(friend_typing(contacts_manager), 0) tox.callback_friend_read_receipt(friend_read_receipt(contacts_manager), 0) - tox.callback_file_recv(tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager, settings), 0) + tox.callback_file_recv(tox_file_recv(main_window, tray, profile, file_transfer_handler, + contacts_manager, settings), 0) tox.callback_file_recv_chunk(file_recv_chunk(file_transfer_handler), 0) tox.callback_file_chunk_request(file_chunk_request(file_transfer_handler), 0) tox.callback_file_recv_control(file_recv_control(file_transfer_handler), 0) + toxav = tox.AV toxav.callback_call_state(call_state(calls_manager), 0) toxav.callback_call(call(calls_manager), 0) toxav.callback_audio_receive_frame(callback_audio(calls_manager), 0) diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py index eb5a9d7..2d7bdf4 100644 --- a/toxygen/middleware/threads.py +++ b/toxygen/middleware/threads.py @@ -9,44 +9,44 @@ class BaseThread(threading.Thread): def __init__(self): super().__init__() - self._stop = False + self._stop_thread = False def stop_thread(self): - self._stop = True + self._stop_thread = True self.join() class InitThread(BaseThread): - def __init__(self, tox, ms, tray): + def __init__(self, tox, plugin_loader): super().__init__() - self.tox, self.ms, self.tray = tox, ms, tray + self._tox, self._plugin_loader = tox, plugin_loader def run(self): - # initializing callbacks - init_callbacks(self.tox, self.ms, self.tray) # download list of nodes if needed download_nodes_list() + # start plugins + self._plugin_loader.load() # bootstrap try: for data in generate_nodes(): - if self._stop: + if self._stop_thread: return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) + self._tox.bootstrap(*data) + self._tox.add_tcp_relay(*data) except: pass for _ in range(10): - if self._stop: + if self._stop_thread: return time.sleep(1) - while not self.tox.self_get_connection_status(): + while not self._tox.self_get_connection_status(): try: for data in generate_nodes(): - if self._stop: + if self._stop_thread: return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) + self._tox.bootstrap(*data) + self._tox.add_tcp_relay(*data) except: pass finally: @@ -60,7 +60,7 @@ class ToxIterateThread(BaseThread): self._tox = tox def run(self): - while not self._stop: + while not self._stop_thread: self._tox.iterate() time.sleep(self._tox.iteration_interval() / 1000) @@ -72,7 +72,7 @@ class ToxAVIterateThread(BaseThread): self._toxav = toxav def run(self): - while not self._stop: + while not self._stop_thread: self._toxav.iterate() time.sleep(self._toxav.iteration_interval() / 1000) @@ -88,7 +88,7 @@ class FileTransfersThread(BaseThread): self._queue.put((func, args, kwargs)) def run(self): - while not self._stop: + while not self._stop_thread: try: func, args, kwargs = self._queue.get(timeout=self._timeout) func(*args, **kwargs) @@ -103,11 +103,11 @@ class FileTransfersThread(BaseThread): _thread = FileTransfersThread() -def start(): +def start_file_transfer_thread(): _thread.start() -def stop(): +def stop_file_transfer_thread(): _thread.stop_thread() diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 9226a81..2831799 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -1,9 +1,7 @@ -from platform import system import json import os -from util.util import log, curr_directory, append_slash +from util.util import log, get_base_directory, append_slash, get_platform import pyaudio -from user_data.toxes import ToxES import smileys.smileys as smileys @@ -60,7 +58,7 @@ class Settings(dict): name = str(auto['name']) if os.path.isfile(append_slash(path) + name + '.tox'): return path, name - return '', '' + return None @staticmethod def set_auto_profile(path, name): @@ -92,9 +90,8 @@ class Settings(dict): fl.write(json.dumps(data)) @staticmethod - def is_active_profile(path, name): - path = path + name + '.lock' - return os.path.isfile(path) + def is_active_profile(profile_path): + return os.path.isfile(profile_path + '.lock') @staticmethod def get_default_settings(): @@ -204,13 +201,14 @@ class Settings(dict): @staticmethod def get_global_settings_path(): - return curr_directory() + '/toxygen.json' + return os.path.join(get_base_directory(), 'toxygen.json') @staticmethod def get_default_path(): - if system() == 'Windows': + system = get_platform() + if system == 'Windows': return os.getenv('APPDATA') + '/Tox/' - elif system() == 'Darwin': + elif system == 'Darwin': return os.getenv('HOME') + '/Library/Application Support/Tox/' else: return os.getenv('HOME') + '/.config/tox/' From dec4990d32a093e247b7e476d9f0da4aa7903148 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 18 Apr 2018 23:55:51 +0300 Subject: [PATCH 009/217] contacts minor refactoring --- toxygen/app.py | 22 +-- toxygen/av/calls.py | 16 +-- toxygen/av/calls_manager.py | 11 +- toxygen/contacts/basecontact.py | 25 ++-- toxygen/contacts/contact.py | 6 +- toxygen/contacts/contacts_manager.py | 201 +++++++++++++++++---------- toxygen/contacts/friend.py | 4 +- toxygen/contacts/group_chat.py | 16 +-- toxygen/contacts/profile.py | 64 +-------- toxygen/user_data/profile_manager.py | 2 +- toxygen/util/ui.py | 21 ++- toxygen/util/util.py | 4 + 12 files changed, 204 insertions(+), 188 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 85da363..e038980 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -22,7 +22,7 @@ class App: def __init__(self, version, path_to_profile=None, uri=None): self._version = version self._app = self._settings = self._profile_manager = self._plugin_loader = None - self._tox = self._ms = self._init = self.tray = self._main_loop = self._av_loop = None + self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] @@ -63,15 +63,7 @@ class App: else: auto_profile = Settings.get_auto_profile() if auto_profile is None: # no default profile - # show login screen if default profile not found - self.load_login_screen_translations() - ls = LoginScreen() - ls.setWindowIconText("Toxygen") - profiles = ProfileManager.find_profiles() - ls.update_select(profiles) - ls.show() - self._app.exec_() - result = ls.result + result = self.select_profile() if result is None: return if result.is_new_profile(): # create new profile @@ -208,6 +200,16 @@ class App: def create_tox(self, data): return tox_factory(data, self._settings) + def select_profile(self): + self.load_login_screen_translations() + ls = LoginScreen() + profiles = ProfileManager.find_profiles() + ls.update_select(profiles) + ls.show() + self._app.exec_() + + return ls.result + def load_existing_profile(self, profile_path): self._settings = Settings(self._toxes, profile_path.replace('.tox', '.json')) self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) diff --git a/toxygen/av/calls.py b/toxygen/av/calls.py index 20684b2..3782318 100644 --- a/toxygen/av/calls.py +++ b/toxygen/av/calls.py @@ -1,12 +1,12 @@ import pyaudio import time import threading -from user_data import settings from wrapper.toxav_enums import * import cv2 import itertools import numpy as np from av import screen_sharing +from av.call import Call # TODO: play sound until outgoing call will be started or cancelled @@ -14,8 +14,9 @@ from av import screen_sharing class AV: - def __init__(self, toxav): + def __init__(self, toxav, settings): self._toxav = toxav + self._settings = settings self._running = True self._calls = {} # dict: key - friend number, value - Call instance @@ -118,7 +119,7 @@ class AV: rate=self._audio_rate, channels=self._audio_channels, input=True, - input_device_index=settings.Settings.get_instance().audio['input'], + input_device_index=self._settings.audio['input'], frames_per_buffer=self._audio_sample_count * 10) self._audio_thread = threading.Thread(target=self.send_audio) @@ -147,15 +148,14 @@ class AV: return self._video_running = True - s = settings.Settings.get_instance() self._video_width = s.video['width'] self._video_height = s.video['height'] if s.video['device'] == -1: - self._video = screen_sharing.DesktopGrabber(s.video['x'], s.video['y'], - s.video['width'], s.video['height']) + self._video = screen_sharing.DesktopGrabber(self._settings.video['x'], self._settings.video['y'], + self._settings.video['width'], self._settings.video['height']) else: - self._video = cv2.VideoCapture(s.video['device']) + self._video = cv2.VideoCapture(self._settings.video['device']) self._video.set(cv2.CAP_PROP_FPS, 25) self._video.set(cv2.CAP_PROP_FRAME_WIDTH, self._video_width) self._video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._video_height) @@ -185,7 +185,7 @@ class AV: self._out_stream = self._audio.open(format=pyaudio.paInt16, channels=channels_count, rate=rate, - output_device_index=settings.Settings.get_instance().audio['output'], + output_device_index=self._settings.audio['output'], output=True) self._out_stream.write(samples) diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py index 6cfe543..8060881 100644 --- a/toxygen/av/calls_manager.py +++ b/toxygen/av/calls_manager.py @@ -4,15 +4,16 @@ import av.calls from PyQt5 import QtWidgets from messenger.messages import * import time +from ui import av_widgets class CallsManager: - def __init__(self, tox): + def __init__(self, tox, settings): self._call = av.calls.AV(tox.AV) # object with data about calls self._call_widgets = {} # dict of incoming call widgets self._incoming_calls = set() - + self._settings = settings # ----------------------------------------------------------------------------------------------------------------- # AV support @@ -29,7 +30,7 @@ class CallsManager: if not self.is_active_a_friend(): return if num not in self._call and self.is_active_online(): # start call - if not Settings.get_instance().audio['enabled']: + if not self._settings.audio['enabled']: return self._call(num, audio, video) self._screen.active_call() @@ -47,7 +48,7 @@ class CallsManager: """ Incoming call from friend. """ - if not Settings.get_instance().audio['enabled']: + if not self._settings.audio['enabled']: return friend = self.get_friend_by_number(friend_number) if video: @@ -62,7 +63,7 @@ class CallsManager: self._messages.scrollToBottom() else: friend.actions = True - self._call_widgets[friend_number] = avwidgets.IncomingCallWidget(friend_number, text, friend.name) + self._call_widgets[friend_number] = av_widgets.IncomingCallWidget(friend_number, text, friend.name) self._call_widgets[friend_number].set_pixmap(friend.get_pixmap()) self._call_widgets[friend_number].show() diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 34c100c..da992de 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -1,6 +1,7 @@ from user_data.settings import * from PyQt5 import QtCore, QtGui from wrapper.toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE +import util.util as util class BaseContact: @@ -11,13 +12,14 @@ class BaseContact: Base class for all contacts. """ - def __init__(self, name, status_message, widget, tox_id): + def __init__(self, profile_manager, name, status_message, widget, tox_id): """ :param name: name, example: 'Toxygen user' :param status_message: status message, example: 'Toxing on Toxygen' :param widget: ContactItem instance :param tox_id: tox id of contact """ + self._profile_manager = profile_manager self._name, self._status_message = name, status_message self._status, self._widget = None, widget self._tox_id = tox_id @@ -81,11 +83,7 @@ class BaseContact: """ Tries to load avatar of contact or uses default avatar """ - return - prefix = ProfileManager.get_path() + 'avatars/' - avatar_path = prefix + '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image - avatar_path = curr_directory() + '/images/avatar.png' + avatar_path = self.get_avatar_path() width = self._widget.avatar_label.width() pixmap = QtGui.QPixmap(avatar_path) self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, @@ -93,13 +91,13 @@ class BaseContact: self._widget.avatar_label.repaint() def reset_avatar(self): - avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = self.get_avatar_path() if os.path.isfile(avatar_path): os.remove(avatar_path) self.load_avatar() def set_avatar(self, avatar): - avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = self.get_avatar_path() with open(avatar_path, 'wb') as f: f.write(avatar) self.load_avatar() @@ -107,6 +105,17 @@ class BaseContact: def get_pixmap(self): return self._widget.avatar_label.pixmap() + def get_avatar_path(self): + directory = util.join_path(self._profile_manager.get_path(), 'avatars') + avatar_path = util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) + if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image + avatar_path = util.join_path(util.get_images_directory(), self.get_default_avatar_name()) + + return avatar_path + + @staticmethod + def get_default_avatar_name(): + return 'avatar.png' # ----------------------------------------------------------------------------------------------------------------- # Widgets # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 22191a7..ecaeec2 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -1,6 +1,6 @@ from history.database import * from contacts import basecontact -import util +import util.util as util from messenger.messages import * from file_transfers import file_transfers as ft import re @@ -12,12 +12,12 @@ class Contact(basecontact.BaseContact): Properties: number, message getter, history etc. Base class for friend and gc classes """ - def __init__(self, message_getter, number, name, status_message, widget, tox_id): + def __init__(self, message_getter, number, profile_manager, name, status_message, widget, tox_id): """ :param message_getter: gets messages from db :param number: number of friend. """ - super().__init__(name, status_message, widget, tox_id) + super().__init__(profile_manager, name, status_message, widget, tox_id) self._number = number self._new_messages = False self._visible = True diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 7653b56..122db5c 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,36 +1,56 @@ +import util.util as util +import util.ui as util_ui +from contacts.friend import Friend +import os +from PyQt5 import QtCore, QtGui, QtWidgets +from messenger.messages import * +from wrapper.toxcore_enums_and_consts import * +from network.tox_dns import tox_dns class ContactsManager: - def __init__(self, tox, settings, screen): + def __init__(self, tox, settings, screen, profile_manager): self._tox = tox self._settings = settings + self._screen = screen + self._profile_manager = profile_manager + self._messages = screen.messages self._contacts, self._active_friend = [], -1 self._sorting = settings['sorting'] - data = tox.self_get_friend_list() self._filter_string = '' self._friend_item_height = 40 if settings['compact_mode'] else 70 screen.online_contacts.setCurrentIndex(int(self._sorting)) - aliases = settings['friends_aliases'] - for i in data: # creates list of friends - tox_id = tox.friend_get_public_key(i) + self.load_contacts() + + def load_contacts(self): + self.load_friends() + self.load_groups() + if len(self._contacts): + self.set_active(0) + self.filtration_and_sorting(self._sorting) + + def load_friends(self): + aliases = self._settings['friends_aliases'] + friend_numbers = self._tox.self_get_friend_list() + for friend_number in friend_numbers: # creates list of friends + tox_id = self._tox.friend_get_public_key(friend_number) try: alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] except: alias = '' item = self.create_friend_item() - name = alias or tox.friend_get_name(i) or tox_id - status_message = tox.friend_get_status_message(i) + name = alias or self._tox.friend_get_name(friend_number) or tox_id + status_message = self._tox.friend_get_status_message(i) if not self._history.friend_exists_in_db(tox_id): self._history.add_friend_to_db(tox_id) message_getter = self._history.messages_getter(tox_id) - friend = Friend(message_getter, i, name, status_message, item, tox_id) + friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, tox_id) friend.set_alias(alias) self._contacts.append(friend) - if len(self._contacts): - self.set_active(0) - self.filtration_and_sorting(self._sorting) + def load_groups(self): + pass def get_friend(self, num): if num < 0 or num >= len(self._contacts): @@ -40,6 +60,10 @@ class ContactsManager: def get_curr_friend(self): return self._contacts[self._active_friend] if self._active_friend + 1 else None + def save_profile(self): + data = self._tox.get_savedata() + self._profile_manager.save_profile(data) + # ----------------------------------------------------------------------------------------------------------------- # Work with active friend # ----------------------------------------------------------------------------------------------------------------- @@ -78,7 +102,7 @@ class ContactsManager: self._screen.messageEdit.setPlainText(friend.curr_text) self._active_friend = value friend.reset_messages() - if not Settings.get_instance()['save_history']: + if not self._settings['save_history']: friend.delete_old_messages() self._messages.clear() friend.load_corr() @@ -128,19 +152,14 @@ class ContactsManager: self._screen.account_name.setText(friend.name) self._screen.account_status.setText(friend.status_message) self._screen.account_status.setToolTip(friend.get_full_status()) - if friend.tox_id is None: - avatar_path = curr_directory() + '/images/group.png' - else: - avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if not os.path.isfile(avatar_path): # load default image - avatar_path = curr_directory() + '/images/avatar.png' + avatar_path = friend.get_avatar_path() os.chdir(os.path.dirname(avatar_path)) pixmap = QtGui.QPixmap(avatar_path) self._screen.account_avatar.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) except Exception as ex: # no friend found. ignore - log('Friend value: ' + str(value)) - log('Error in set active: ' + str(ex)) + util.log('Friend value: ' + str(value)) + util.log('Error in set active: ' + str(ex)) raise def set_active_by_number_and_type(self, number, is_friend): @@ -167,7 +186,6 @@ class ContactsManager: :param filter_str: show contacts which name contains this substring """ filter_str = filter_str.lower() - settings = Settings.get_instance() number = self.get_active_number() is_friend = self.is_active_a_friend() if sorting > 1: @@ -203,8 +221,8 @@ class ContactsManager: else: self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) self._sorting, self._filter_string = sorting, filter_str - settings['sorting'] = self._sorting - settings.save() + self._settings['sorting'] = self._sorting + self._settings.save() self.set_active_by_number_and_type(number, is_friend) def update_filtration(self): @@ -213,7 +231,6 @@ class ContactsManager: """ self.filtration_and_sorting(self._sorting, self._filter_string) - def create_friend_item(self): """ Method-factory @@ -221,7 +238,42 @@ class ContactsManager: """ return self._factory.friend_item() + # ----------------------------------------------------------------------------------------------------------------- + # Friend getters + # ----------------------------------------------------------------------------------------------------------------- + def get_friend_by_number(self, num): + return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0] + + def get_last_message(self): + if self._active_friend + 1: + return self.get_curr_friend().get_last_message_text() + else: + return '' + + def get_active_number(self): + return self.get_curr_friend().number if self._active_friend + 1 else -1 + + def get_active_name(self): + return self.get_curr_friend().name if self._active_friend + 1 else '' + + def is_active_online(self): + return self._active_friend + 1 and self.get_curr_friend().status is not None + + def new_name(self, number, name): + friend = self.get_friend_by_number(number) + tmp = friend.name + friend.set_name(name) + name = str(name, 'utf-8') + if friend.name == name and tmp != name: + message = QtWidgets.QApplication.translate("MainWindow", 'User {} is now known as {}') + message = message.format(tmp, name) + friend.append_message(InfoMessage(message, time.time())) + friend.actions = True + if number == self.get_active_number(): + self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + self._messages.scrollToBottom() + self.set_active(None) # ----------------------------------------------------------------------------------------------------------------- # Work with friends (remove, block, set alias, get public key) @@ -233,19 +285,11 @@ class ContactsManager: """ friend = self._contacts[num] name = friend.name - dialog = QtWidgets.QApplication.translate('MainWindow', - "Enter new alias for friend {} or leave empty to use friend's name:") - dialog = dialog.format(name) - title = QtWidgets.QApplication.translate('MainWindow', - 'Set alias') - text, ok = QtWidgets.QInputDialog.getText(None, - title, - dialog, - QtWidgets.QLineEdit.Normal, - name) + text = util_ui.tr("Enter new alias for friend {} or leave empty to use friend's name:").format(name) + title = util_ui.tr('Set alias') + text, ok = util_ui.text_dialog(text, title, name) if ok: - settings = Settings.get_instance() - aliases = settings['friends_aliases'] + aliases = self._settings['friends_aliases'] if text: friend.name = bytes(text, 'utf-8') try: @@ -262,7 +306,7 @@ class ContactsManager: del aliases[index] except: pass - settings.save() + self._settings.save() if num == self.get_active_number() and self.is_active_a_friend(): self.update() @@ -275,15 +319,14 @@ class ContactsManager: :param num: number of friend in list """ friend = self._contacts[num] - settings = Settings.get_instance() try: - index = list(map(lambda x: x[0], settings['friends_aliases'])).index(friend.tox_id) - del settings['friends_aliases'][index] + index = list(map(lambda x: x[0], self._settings['friends_aliases'])).index(friend.tox_id) + del self._settings['friends_aliases'][index] except: pass - if friend.tox_id in settings['notes']: - del settings['notes'][friend.tox_id] - settings.save() + if friend.tox_id in self._settings['notes']: + del self._settings['notes'][friend.tox_id] + self._settings.save() self.clear_history(num) if self._history.friend_exists_in_db(friend.tox_id): self._history.delete_friend_from_db(friend.tox_id) @@ -296,7 +339,7 @@ class ContactsManager: else: self.set_active(0) data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) + self._profile_manager.save_profile(data) def add_friend(self, tox_id): """ @@ -309,7 +352,7 @@ class ContactsManager: self._history.add_friend_to_db(tox_id) message_getter = self._history.messages_getter(tox_id) except Exception as ex: # something is wrong - log('Accept friend request failed! ' + str(ex)) + util.log('Accept friend request failed! ' + str(ex)) message_getter = None friend = Friend(message_getter, num, tox_id, '', item, tox_id) self._contacts.append(friend) @@ -319,17 +362,15 @@ class ContactsManager: Block user with specified tox id (or public key) - delete from friends list and ignore friend requests """ tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] - if tox_id == self.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]: + if tox_id == self._tox.self_get_address[:TOX_PUBLIC_KEY_SIZE * 2]: return - settings = Settings.get_instance() - if tox_id not in settings['blocked']: - settings['blocked'].append(tox_id) - settings.save() + if tox_id not in self._settings['blocked']: + self._settings['blocked'].append(tox_id) + self._settings.save() try: num = self._tox.friend_by_public_key(tox_id) self.delete_friend(num) - data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) + self.save_profile() except: # not in friend list pass @@ -339,13 +380,11 @@ class ContactsManager: :param tox_id: tox id of contact :param add_to_friend_list: add this contact to friend list or not """ - s = Settings.get_instance() - s['blocked'].remove(tox_id) - s.save() + self._settings['blocked'].remove(tox_id) + self._settings.save() if add_to_friend_list: self.add_friend(tox_id) - data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) + self.save_profile() # ----------------------------------------------------------------------------------------------------------------- # Friend requests @@ -366,11 +405,9 @@ class ContactsManager: raise Exception('TOX DNS lookup failed') if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key self.add_friend(tox_id) - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "Friend added")) - text = (QtWidgets.QApplication.translate("MainWindow", 'Friend added without sending friend request')) - msgBox.setText(text) - msgBox.exec_() + title = util_ui.tr('Friend added') + text = util_ui.tr('Friend added without sending friend request') + util_ui.message_box(text, title) else: result = self._tox.friend_add(tox_id, message.encode('utf-8')) tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] @@ -380,11 +417,10 @@ class ContactsManager: message_getter = self._history.messages_getter(tox_id) friend = Friend(message_getter, result, tox_id, '', item, tox_id) self._contacts.append(friend) - data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) + self.save_profile() return True except Exception as ex: # wrong data - log('Friend request failed with ' + str(ex)) + util.log('Friend request failed with ' + str(ex)) return str(ex) def process_friend_request(self, tox_id, message): @@ -396,13 +432,34 @@ class ContactsManager: if tox_id in self._settings['blocked']: return try: - text = QtWidgets.QApplication.translate('MainWindow', 'User {} wants to add you to contact list. Message:\n{}') - info = text.format(tox_id, message) - fr_req = QtWidgets.QApplication.translate('MainWindow', 'Friend request') - reply = QtWidgets.QMessageBox.question(None, fr_req, info, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: # accepted + text = util_ui.tr('User {} wants to add you to contact list. Message:\n{}') + reply = util_ui.question(text.format(tox_id, message), util_ui.tr('Friend request')) + if reply: # accepted self.add_friend(tox_id) data = self._tox.get_savedata() - ProfileManager.get_instance().save_profile(data) + self._profile_manager.save_profile(data) except Exception as ex: # something is wrong - log('Accept friend request failed! ' + str(ex)) + util.log('Accept friend request failed! ' + str(ex)) + + # ----------------------------------------------------------------------------------------------------------------- + # Typing notifications + # ----------------------------------------------------------------------------------------------------------------- + + def send_typing(self, typing): + """ + Send typing notification to a friend + """ + if self._settings['typing_notifications'] and self._active_friend + 1: + try: + friend = self.get_curr_friend() + if friend.status is not None: + self._tox.self_set_typing(friend.number, typing) + except: + pass + + def friend_typing(self, friend_number, typing): + """ + Display incoming typing notification + """ + if friend_number == self.get_active_number() and self.is_active_a_friend(): + self._screen.typing.setVisible(typing) diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index b80032a..4a22329 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -8,8 +8,8 @@ class Friend(contact.Contact): Friend in list of friends. """ - def __init__(self, message_getter, number, name, status_message, widget, tox_id): - super().__init__(message_getter, number, name, status_message, widget, tox_id) + def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id): + super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) self._receipts = 0 # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 05faaa9..b247281 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -1,5 +1,5 @@ from contacts import contact -import util +import util.util as util from PyQt5 import QtGui, QtCore from wrapper import toxcore_enums_and_consts as constants @@ -8,8 +8,8 @@ from wrapper import toxcore_enums_and_consts as constants class GroupChat(contact.Contact): - def __init__(self, name, status_message, widget, tox, group_number): - super().__init__(None, group_number, name, status_message, widget, None) + def __init__(self, profile_manager, name, status_message, widget, tox, group_number): + super().__init__(None, group_number, profile_manager, name, status_message, widget, None) self._tox = tox self.set_status(constants.TOX_USER_STATUS['NONE']) @@ -23,13 +23,9 @@ class GroupChat(contact.Contact): def new_title(self, title): super().set_name(title) - def load_avatar(self): - path = util.curr_directory() + '/images/group.png' - width = self._widget.avatar_label.width() - pixmap = QtGui.QPixmap(path) - self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation)) - self._widget.avatar_label.repaint() + @staticmethod + def get_default_avatar_name(): + return 'group.png' def remove_invalid_unsent_files(self): pass diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index ef26a51..a107602 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -22,12 +22,13 @@ class Profile(basecontact.BaseContact): """ Profile of current toxygen user. Contains friends list, tox instance """ - def __init__(self, tox, screen): + def __init__(self, profile_manager, tox, screen): """ :param tox: tox instance :param screen: ref to main screen """ basecontact.BaseContact.__init__(self, + profile_manager, tox.self_get_name(), tox.self_get_status_message(), screen.user_info, @@ -86,43 +87,6 @@ class Profile(basecontact.BaseContact): self._tox_id = self._tox.self_get_address() return self._tox_id - # ----------------------------------------------------------------------------------------------------------------- - # Friend getters - # ----------------------------------------------------------------------------------------------------------------- - - def get_friend_by_number(self, num): - return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0] - - def get_last_message(self): - if self._active_friend + 1: - return self.get_curr_friend().get_last_message_text() - else: - return '' - - def get_active_number(self): - return self.get_curr_friend().number if self._active_friend + 1 else -1 - - def get_active_name(self): - return self.get_curr_friend().name if self._active_friend + 1 else '' - - def is_active_online(self): - return self._active_friend + 1 and self.get_curr_friend().status is not None - - def new_name(self, number, name): - friend = self.get_friend_by_number(number) - tmp = friend.name - friend.set_name(name) - name = str(name, 'utf-8') - if friend.name == name and tmp != name: - message = QtWidgets.QApplication.translate("MainWindow", 'User {} is now known as {}') - message = message.format(tmp, name) - friend.append_message(InfoMessage(message, time.time())) - friend.actions = True - if number == self.get_active_number(): - self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() - self.set_active(None) - # ----------------------------------------------------------------------------------------------------------------- # Friend connection status callbacks # ----------------------------------------------------------------------------------------------------------------- @@ -168,29 +132,6 @@ class Profile(basecontact.BaseContact): self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, True, ft.total_size()] self.cancel_transfer(friend_num, file_num, True) - # ----------------------------------------------------------------------------------------------------------------- - # Typing notifications - # ----------------------------------------------------------------------------------------------------------------- - - def send_typing(self, typing): - """ - Send typing notification to a friend - """ - if Settings.get_instance()['typing_notifications'] and self._active_friend + 1: - try: - friend = self.get_curr_friend() - if friend.status is not None: - self._tox.self_set_typing(friend.number, typing) - except: - pass - - def friend_typing(self, friend_number, typing): - """ - Display incoming typing notification - """ - if friend_number == self.get_active_number() and self.is_active_a_friend(): - self._screen.typing.setVisible(typing) - # ----------------------------------------------------------------------------------------------------------------- # Private messages # ----------------------------------------------------------------------------------------------------------------- @@ -294,7 +235,6 @@ class Profile(basecontact.BaseContact): # Friend, message and file transfer items creation # ----------------------------------------------------------------------------------------------------------------- - def create_message_item(self, text, time, owner, message_type, append=True): if message_type == MESSAGE_TYPE['INFO_MESSAGE']: name = '' diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index b897fc3..823dbc0 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -13,7 +13,7 @@ class ProfileManager: self._path = path self._directory = os.path.basename(path) # create /avatars if not exists: - directory = path + 'avatars' + directory = util.join_path(self._directory, 'avatars') if not os.path.exists(directory): os.makedirs(directory) diff --git a/toxygen/util/ui.py b/toxygen/util/ui.py index c485de4..4b9e806 100644 --- a/toxygen/util/ui.py +++ b/toxygen/util/ui.py @@ -1,21 +1,28 @@ -import PyQt5 +from PyQt5 import QtWidgets def tr(s): - return PyQt5.QtWidgets.QApplication.translate('Toxygen', s) + return QtWidgets.QApplication.translate('Toxygen', s) def question(text, title=None): - reply = PyQt5.QtWidgets.QMessageBox.question(None, title or 'Toxygen', text, - PyQt5.QtWidgets.QMessageBox.Yes, - PyQt5.QtWidgets.QMessageBox.No) - return reply == PyQt5.QtWidgets.QMessageBox.Yes + reply = QtWidgets.QMessageBox.question(None, title or 'Toxygen', text, + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + return reply == QtWidgets.QMessageBox.Yes def message_box(text, title=None): - m_box = PyQt5.QtWidgets.QMessageBox() + m_box = QtWidgets.QMessageBox() m_box.setText(tr(text)) m_box.setWindowTitle(title or 'Toxygen') m_box.exec_() + +def text_dialog(text, title='', default_value=''): + text, ok = QtWidgets.QInputDialog.getText(None, title, text, QtWidgets.QLineEdit.Normal, default_value) + + return text, ok + + # TODO: move all dialogs here diff --git a/toxygen/util/util.py b/toxygen/util/util.py index 5bf5f99..61df479 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -79,6 +79,10 @@ def curr_time(): return time.strftime('%H:%M') +def join_path(a, b): + return os.path.join(a, b) + + def copy(src, dest): if not os.path.exists(dest): os.makedirs(dest) From a9d2d3d8093ed4793fe0873b724faf33df074d60 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 26 Apr 2018 23:54:39 +0300 Subject: [PATCH 010/217] more refacrtoring - contact provider, deps creation --- toxygen/app.py | 29 ++- toxygen/bootstrap/bootstrap.py | 13 +- toxygen/contacts/basecontact.py | 2 +- toxygen/contacts/contact_provider.py | 38 ++++ toxygen/contacts/friend_factory.py | 38 ++++ toxygen/contacts/profile.py | 8 +- toxygen/file_transfers/file_transfers.py | 27 +-- .../file_transfers/file_transfers_handler.py | 30 +-- toxygen/history/database.py | 11 +- toxygen/messenger/messages.py | 52 ++++- toxygen/middleware/threads.py | 6 +- toxygen/smileys/smileys.py | 4 +- toxygen/ui/items_factory.py | 6 +- toxygen/ui/list_items.py | 202 ----------------- toxygen/ui/main_screen.py | 138 ++++++------ toxygen/ui/menu.py | 1 + toxygen/ui/messages_widgets.py | 212 ++++++++++++++++++ toxygen/ui/widgets.py | 6 +- toxygen/user_data/profile_manager.py | 8 +- toxygen/util/util.py | 14 ++ 20 files changed, 500 insertions(+), 345 deletions(-) create mode 100644 toxygen/contacts/contact_provider.py create mode 100644 toxygen/contacts/friend_factory.py create mode 100644 toxygen/ui/messages_widgets.py diff --git a/toxygen/app.py b/toxygen/app.py index e038980..8dd4ea8 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -15,6 +15,11 @@ from plugin_support.plugin_support import PluginLoader from ui.main_screen import MainWindow from ui import tray import util.ui as util_ui +import util.util as util +from contacts.profile import Profile +from file_transfers.file_transfers_handler import FileTransfersHandler +from contacts.contact_provider import ContactProvider +from contacts.friend_factory import FriendFactory class App: @@ -23,7 +28,7 @@ class App: self._version = version self._app = self._settings = self._profile_manager = self._plugin_loader = None self._tox = self._ms = self._init = self._main_loop = self._av_loop = None - self._uri = self._toxes = self._tray = None + self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] self._path = path_to_profile @@ -93,7 +98,11 @@ class App: return self._ms = MainWindow(self._settings, self._tox, self.reset, self._tray) - profile = self._ms.profile + self._friend_factory = FriendFactory(None, self._profile_manager, self._settings, self._tox) + self._contacts_provider = ContactProvider(self._tox, self._friend_factory) + self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) + profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler) + self._ms.profile = profile self._ms.show() self._tray = tray.init_tray(profile, self._settings, self._ms) @@ -110,7 +119,17 @@ class App: self._ms.add_contact(self._uri) self._app.lastWindowClosed.connect(self._app.quit) - self._app.exec_() + # main + while True: + try: + self._app.exec_() + except KeyboardInterrupt: + print('Closing Toxygen...') + break + except Exception as ex: + util.log('Unhandled exception: ' + str(ex)) + else: + break self._plugin_loader.stop() self.stop_threads() @@ -132,7 +151,7 @@ class App: self._tox = self.create_tox(data) self.start_threads() - self._plugin_loader.set_tox(self._tox) + # TODO: foreach in list of tox savers set_tox return self._tox @@ -178,7 +197,7 @@ class App: def start_threads(self): # init thread - self._init = threads.InitThread(self._tox, self._plugin_loader) + self._init = threads.InitThread(self._tox, self._plugin_loader, self._settings) self._init.start() # starting threads for tox iterate and toxav iterate diff --git a/toxygen/bootstrap/bootstrap.py b/toxygen/bootstrap/bootstrap.py index 87c6d9c..aa6f863 100644 --- a/toxygen/bootstrap/bootstrap.py +++ b/toxygen/bootstrap/bootstrap.py @@ -38,13 +38,12 @@ def save_nodes(nodes): fl.write(nodes) -def download_nodes_list(): +def download_nodes_list(settings): url = 'https://nodes.tox.chat/json' - s = settings.Settings.get_instance() - if not s['download_nodes_list']: + if not settings['download_nodes_list']: return - if not s['proxy_type']: # no proxy + if not settings['proxy_type']: # no proxy try: req = urllib.request.Request(url) req.add_header('Content-Type', 'application/json') @@ -57,9 +56,9 @@ def download_nodes_list(): netman = QtNetwork.QNetworkAccessManager() proxy = QtNetwork.QNetworkProxy() proxy.setType( - QtNetwork.QNetworkProxy.Socks5Proxy if s['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) - proxy.setHostName(s['proxy_host']) - proxy.setPort(s['proxy_port']) + QtNetwork.QNetworkProxy.Socks5Proxy if settings['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) + proxy.setHostName(settings['proxy_host']) + proxy.setPort(settings['proxy_port']) netman.setProxy(proxy) try: request = QtNetwork.QNetworkRequest() diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index da992de..71690dd 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -106,7 +106,7 @@ class BaseContact: return self._widget.avatar_label.pixmap() def get_avatar_path(self): - directory = util.join_path(self._profile_manager.get_path(), 'avatars') + directory = util.join_path(self._profile_manager.get_dir(), 'avatars') avatar_path = util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image avatar_path = util.join_path(util.get_images_directory(), self.get_default_avatar_name()) diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py new file mode 100644 index 0000000..10b778b --- /dev/null +++ b/toxygen/contacts/contact_provider.py @@ -0,0 +1,38 @@ +import util.util as util + + +class ContactProvider(util.ToxSave): + + def __init__(self, tox, friend_factory): + super().__init__(tox) + self._friend_factory = friend_factory + self._cache = {} # key - contact's public key, value - contact instance + + def get_friend_by_number(self, friend_number): + public_key = self._tox.friend_get_public_key(friend_number) + + return self.get_friend_by_public_key(public_key) + + def get_friend_by_public_key(self, public_key): + friend = self._get_contact_from_cache(public_key) + if friend is not None: + return friend + friend = self._friend_factory.create_friend_by_public_key(public_key) + self._add_to_cache(public_key, friend) + + return friend + + def get_gc_by_number(self): + pass + + def get_gc_by_public_key(self): + pass + + def clear_cache(self): + self._cache.clear() + + def _get_contact_from_cache(self, public_key): + return self._cache[public_key] if public_key in self._cache else None + + def _add_to_cache(self, public_key, contact): + self._cache[public_key] = contact diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py new file mode 100644 index 0000000..556f2d4 --- /dev/null +++ b/toxygen/contacts/friend_factory.py @@ -0,0 +1,38 @@ +from contacts.friend import Friend + + +class FriendFactory: + + def __init__(self, history, profile_manager, settings, tox): + self._history, self._profile_manager = history, profile_manager + self._settings, self._tox = settings, tox + + def create_friend_by_number(self, friend_number): + aliases = self._settings['friends_aliases'] + tox_id = self._tox.friend_get_public_key(friend_number) + try: + alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] + except: + alias = '' + item = self.create_friend_item() + name = alias or self._tox.friend_get_name(friend_number) or tox_id + status_message = self._tox.friend_get_status_message(friend_number) + if not self._history.friend_exists_in_db(tox_id): + self._history.add_friend_to_db(tox_id) + message_getter = self._history.messages_getter(tox_id) + friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, tox_id) + friend.set_alias(alias) + + return friend + + def create_friend_by_public_key(self, public_key): + friend_number = self._tox.friend_by_public_key(public_key) + + return self.create_friend_by_number(friend_number) + + def create_friend_item(self): + """ + Method-factory + :return: new widget for friend instance + """ + return self._factory.friend_item() diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 4181753..0bfc764 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -40,7 +40,8 @@ class Profile(basecontact.BaseContact): self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number) self._load_history = True self._waiting_for_reconnection = False - self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages) + #self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages) + self._contacts_manager = None #self._show_avatars = settings['show_avatars'] # ----------------------------------------------------------------------------------------------------------------- @@ -77,7 +78,7 @@ class Profile(basecontact.BaseContact): self._messages.scrollToBottom() def set_status_message(self, value): - super(Profile, self).set_status_message(value) + super().set_status_message(value) self._tox.self_set_status_message(self._status_message.encode('utf-8')) def new_nospam(self): @@ -85,6 +86,7 @@ class Profile(basecontact.BaseContact): import random self._tox.self_set_nospam(random.randint(0, 4294967295)) # no spam - uint32 self._tox_id = self._tox.self_get_address() + return self._tox_id # ----------------------------------------------------------------------------------------------------------------- @@ -296,7 +298,7 @@ class Profile(basecontact.BaseContact): self.status = None for friend in self._contacts: friend.number = self._tox.friend_by_public_key(friend.tox_id) # numbers update - self.update_filtration() + self._contacts_manager.update_filtration() def reconnect(self): self._waiting_for_reconnection = False diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 6c65809..5aab6f9 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -48,7 +48,7 @@ class FileTransfer(QtCore.QObject): """ def __init__(self, path, tox, friend_number, size, file_number=None): - QtCore.QObject.__init__(self) + super().__init__(self) self._path = path self._tox = tox self._friend_number = friend_number @@ -134,7 +134,7 @@ class SendTransfer(FileTransfer): size = getsize(path) else: size = 0 - super(SendTransfer, self).__init__(path, tox, friend_number, size) + super().__init__(path, tox, friend_number, size) self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] self._file_number = tox.file_send(friend_number, kind, size, file_id, bytes(basename(path), 'utf-8') if path else b'') @@ -168,11 +168,11 @@ class SendAvatar(SendTransfer): def __init__(self, path, tox, friend_number): if path is None: - hash = None + avatar_hash = None else: with open(path, 'rb') as fl: - hash = Tox.hash(fl.read()) - super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], hash) + avatar_hash = Tox.hash(fl.read()) + super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], avatar_hash) class SendFromBuffer(FileTransfer): @@ -181,7 +181,7 @@ class SendFromBuffer(FileTransfer): """ def __init__(self, tox, friend_number, data, file_name): - super(SendFromBuffer, self).__init__(None, tox, friend_number, len(data)) + super().__init__(None, tox, friend_number, len(data)) self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] self._data = data self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], @@ -206,10 +206,10 @@ class SendFromBuffer(FileTransfer): class SendFromFileBuffer(SendTransfer): def __init__(self, *args): - super(SendFromFileBuffer, self).__init__(*args) + super().__init__(*args) def send_chunk(self, position, size): - super(SendFromFileBuffer, self).send_chunk(position, size) + super().send_chunk(position, size) if not size: chdir(dirname(self._path)) remove(self._path) @@ -222,7 +222,7 @@ class SendFromFileBuffer(SendTransfer): class ReceiveTransfer(FileTransfer): def __init__(self, path, tox, friend_number, size, file_number, position=0): - super(ReceiveTransfer, self).__init__(path, tox, friend_number, size, file_number) + super().__init__(path, tox, friend_number, size, file_number) self._file = open(self._path, 'wb') self._file_size = position self._file.truncate(position) @@ -231,11 +231,12 @@ class ReceiveTransfer(FileTransfer): self._done = position def cancel(self): - super(ReceiveTransfer, self).cancel() + super().cancel() remove(self._path) def total_size(self): self._missed.add(self._file_size) + return min(self._missed) def write_chunk(self, position, data): @@ -273,7 +274,7 @@ class ReceiveToBuffer(FileTransfer): """ def __init__(self, tox, friend_number, size, file_number): - super(ReceiveToBuffer, self).__init__(None, tox, friend_number, size, file_number) + super().__init__(None, tox, friend_number, size, file_number) self._data = bytes() self._data_size = 0 @@ -306,7 +307,7 @@ class ReceiveAvatar(ReceiveTransfer): def __init__(self, tox, friend_number, size, file_number): path = settings.ProfileManager.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) - super(ReceiveAvatar, self).__init__(path + '.tmp', tox, friend_number, size, file_number) + super().__init__(path + '.tmp', tox, friend_number, size, file_number) if size > self.MAX_AVATAR_SIZE: self.send_control(TOX_FILE_CONTROL['CANCEL']) self._file.close() @@ -333,7 +334,7 @@ class ReceiveAvatar(ReceiveTransfer): self.send_control(TOX_FILE_CONTROL['RESUME']) def write_chunk(self, position, data): - super(ReceiveAvatar, self).write_chunk(position, data) + super().write_chunk(position, data) if self.state: avatar_path = self._path[:-4] if exists(avatar_path): diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 6431fe4..11c47c5 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -1,14 +1,18 @@ from file_transfers.file_transfers import * from messenger.messages import * +from history.database import MESSAGE_OWNER import os +import util.util as util class FileTransfersHandler: - def __init__(self, tox, settings): + def __init__(self, tox, settings, contact_provider): self._tox = tox self._settings = settings + self._contact_provider = contact_provider self._file_transfers = {} + # key = (friend number, file number), value - transfer instance self._paused_file_transfers = dict(settings['paused_file_transfers']) # key - file id, value: [path, friend number, is incoming, start position] @@ -24,9 +28,9 @@ class FileTransfersHandler: :param size: file size in bytes :param file_name: file name without path """ - friend = self.get_friend_by_number(friend_number) - auto = self._settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends'] - inline = is_inline(file_name) and settings['allow_inline'] + friend = self._get_friend_by_number(friend_number) + auto = self._settings['allow_auto_accept'] and friend.tox_id in self._settings['auto_accept_from_friends'] + inline = is_inline(file_name) and self._settings['allow_inline'] file_id = self._tox.file_get_file_id(friend_number, file_number) accepted = True if file_id in self._paused_file_transfers: @@ -55,7 +59,7 @@ class FileTransfersHandler: file_number) elif auto: - path = settings['auto_accept_path'] or curr_directory() + path = self._settings['auto_accept_path'] or util.curr_directory() self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size) tm = TransferMessage(MESSAGE_OWNER['FRIEND'], time.time(), @@ -90,7 +94,7 @@ class FileTransfersHandler: :param file_number: file number :param already_cancelled: was cancelled by friend """ - i = self.get_friend_by_number(friend_number).update_transfer_data(file_number, + i = self._get_friend_by_number(friend_number).update_transfer_data(file_number, TOX_FILE_TRANSFER_STATE['CANCELLED']) if (friend_number, file_number) in self._file_transfers: tr = self._file_transfers[(friend_number, file_number)] @@ -128,8 +132,8 @@ class FileTransfersHandler: """ Resume transfer with specified data """ - self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['RUNNING']) + # self.get_friend_by_number(friend_number).update_transfer_data(file_number, + # TOX_FILE_TRANSFER_STATE['RUNNING']) tr = self._file_transfers[(friend_number, file_number)] if by_friend: tr.state = TOX_FILE_TRANSFER_STATE['RUNNING'] @@ -261,7 +265,7 @@ class FileTransfersHandler: self.get_friend_by_number(friend_number).load_avatar() if friend_number == self.get_active_number() and self.is_active_a_friend(): self.set_active(None) - elif t is ReceiveToBuffer or (t is SendFromBuffer and Settings.get_instance()['allow_inline']): # inline image + elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings.get_instance()['allow_inline']): # inline image print('inline') inline = InlineImage(transfer.get_data()) i = self.get_friend_by_number(friend_number).update_transfer_data(file_number, @@ -286,13 +290,10 @@ class FileTransfersHandler: # Avatars support # ----------------------------------------------------------------------------------------------------------------- - def send_avatar(self, friend_number): + def send_avatar(self, friend_number, avatar_path=None): """ :param friend_number: number of friend who should get new avatar """ - avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if not os.path.isfile(avatar_path): # reset image - avatar_path = None sa = SendAvatar(avatar_path, self._tox, friend_number) self._file_transfers[(friend_number, sa.get_file_number())] = sa @@ -311,3 +312,6 @@ class FileTransfersHandler: self.get_friend_by_number(friend_number).load_avatar() if self.get_active_number() == friend_number and self.is_active_a_friend(): self.set_active(None) + + def _get_friend_by_number(self, friend_number): + return self._contact_provider.get_friend_by_number(friend_number) diff --git a/toxygen/history/database.py b/toxygen/history/database.py index ff9ce8e..9960b54 100644 --- a/toxygen/history/database.py +++ b/toxygen/history/database.py @@ -2,7 +2,6 @@ from sqlite3 import connect from user_data import settings from os import chdir import os.path -from user_data.toxes import ToxES PAGE_SIZE = 42 @@ -14,10 +13,11 @@ SAVE_MESSAGES = 500 MESSAGE_OWNER = { 'ME': 0, 'FRIEND': 1, - 'NOT_SENT': 2 + 'NOT_SENT': 2, + 'GC_PEER': 3 } -# TODO: unique message id and ngc support, db name as profile name +# TODO: unique message id and ngc support, profile name as db name class Database: @@ -58,9 +58,8 @@ class Database: new_path = directory + self._name + '.hstr' with open(path, 'rb') as fin: data = fin.read() - encr = ToxES.get_instance() - if encr.has_password(): - data = encr.pass_encrypt(data) + if self._toxes.has_password(): + data = self._toxes.pass_encrypt(data) with open(new_path, 'wb') as fout: fout.write(data) diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 4baa29a..e8abec7 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -1,3 +1,4 @@ +from history.database import MESSAGE_OWNER MESSAGE_TYPE = { @@ -9,28 +10,54 @@ MESSAGE_TYPE = { } +class MessageAuthor: + + def __init__(self, author_name, author_type): + self.name = author_name + self.type = author_type + + class Message: - def __init__(self, message_id, message_type, owner, time): + def __init__(self, message_id, message_type, author, time): self._time = time self._type = message_type - self._owner = owner + self._author = author self._message_id = message_id + self._widget = None def get_type(self): return self._type - def get_owner(self): - return self._owner + type = property(get_type) - def mark_as_sent(self): - self._owner = 0 + def get_author(self): + return self._author + + author = property(get_author) def get_message_id(self): return self._message_id message_id = property(get_message_id) + def get_widget(self): + if self._widget is None: + self._widget = self._create_widget() + + return self._widget + + widget = property(get_widget) + + def remove_widget(self): + self._widget = None + + def mark_as_sent(self): + self._author.author_type = MESSAGE_OWNER['ME'] + + def _create_widget(self): + pass + class TextMessage(Message): """ @@ -38,12 +65,15 @@ class TextMessage(Message): """ def __init__(self, id, message, owner, time, message_type): - super(TextMessage, self).__init__(id, message_type, owner, time) + super().__init__(id, message_type, owner, time) self._message = message def get_data(self): return self._message, self._owner, self._time, self._type + def _create_widget(self): + return + class GroupChatMessage(TextMessage): @@ -61,7 +91,7 @@ class TransferMessage(Message): """ def __init__(self, id, owner, time, status, size, name, friend_number, file_number): - super(TransferMessage, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time) + super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time) self._status = status self._size = size self._file_name = name @@ -88,7 +118,7 @@ class TransferMessage(Message): class UnsentFile(Message): def __init__(self, id, path, data, time): - super(UnsentFile, self).__init__(id, MESSAGE_TYPE['FILE_TRANSFER'], 0, time) + super().__init__(id, MESSAGE_TYPE['FILE_TRANSFER'], 0, time) self._data, self._path = data, path def get_data(self): @@ -104,7 +134,7 @@ class InlineImage(Message): """ def __init__(self, id, data): - super(InlineImage, self).__init__(id, MESSAGE_TYPE['INLINE'], None, None) + super().__init__(id, MESSAGE_TYPE['INLINE'], None, None) self._data = data def get_data(self): @@ -114,4 +144,4 @@ class InlineImage(Message): class InfoMessage(TextMessage): def __init__(self, id, message, time): - super(InfoMessage, self).__init__(id, message, None, time, MESSAGE_TYPE['INFO_MESSAGE']) + super().__init__(id, message, None, time, MESSAGE_TYPE['INFO_MESSAGE']) diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py index 2d7bdf4..5d722f0 100644 --- a/toxygen/middleware/threads.py +++ b/toxygen/middleware/threads.py @@ -18,13 +18,13 @@ class BaseThread(threading.Thread): class InitThread(BaseThread): - def __init__(self, tox, plugin_loader): + def __init__(self, tox, plugin_loader, settings): super().__init__() - self._tox, self._plugin_loader = tox, plugin_loader + self._tox, self._plugin_loader, self._settings = tox, plugin_loader, settings def run(self): # download list of nodes if needed - download_nodes_list() + download_nodes_list(self._settings) # start plugins self._plugin_loader.load() # bootstrap diff --git a/toxygen/smileys/smileys.py b/toxygen/smileys/smileys.py index abf6990..c20d1a7 100644 --- a/toxygen/smileys/smileys.py +++ b/toxygen/smileys/smileys.py @@ -25,7 +25,7 @@ class SmileyLoader: pack_name = self._settings['smiley_pack'] if self._settings['smileys'] and self._curr_pack != pack_name: self._curr_pack = pack_name - path = self.get_smileys_path() + 'config.json' + path = util.join_path(self.get_smileys_path(), 'config.json') try: with open(path, encoding='utf8') as fl: self._smileys = json.loads(fl.read()) @@ -45,7 +45,7 @@ class SmileyLoader: print('Smiley pack {} was not loaded. Error: {}'.format(pack_name, ex)) def get_smileys_path(self): - return util.curr_directory() + '/smileys/' + self._curr_pack + '/' if self._curr_pack is not None else None + return util.join_path(util.get_smileys_directory(), self._curr_pack) if self._curr_pack is not None else None @staticmethod def get_packs_list(self): diff --git a/toxygen/ui/items_factory.py b/toxygen/ui/items_factory.py index cdf127a..386e762 100644 --- a/toxygen/ui/items_factory.py +++ b/toxygen/ui/items_factory.py @@ -3,9 +3,9 @@ from ui.list_items import * class ItemsFactory: - def __init__(self, friends_list, messages): - self._friends = friends_list - self._messages = messages + def __init__(self, settings, plugin_loader, smiley_loader, main_screen): + self._settings, self._plugin_loader = settings, plugin_loader + self._smiley_loader, self._main_screen = smiley_loader, main_screen def friend_item(self): item = ContactItem() diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py index 76e6185..1891d45 100644 --- a/toxygen/ui/list_items.py +++ b/toxygen/ui/list_items.py @@ -10,208 +10,6 @@ from user_data import settings import re -class MessageEdit(QtWidgets.QTextBrowser): - - def __init__(self, text, width, message_type, parent=None): - super(MessageEdit, self).__init__(parent) - self.urls = {} - self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere) - self.document().setTextWidth(width) - self.setOpenExternalLinks(True) - self.setAcceptRichText(True) - self.setOpenLinks(False) - path = smileys.SmileyLoader.get_instance().get_smileys_path() - if path is not None: - self.setSearchPaths([path]) - self.document().setDefaultStyleSheet('a { color: #306EFF; }') - text = self.decoratedText(text) - if message_type != TOX_MESSAGE_TYPE['NORMAL']: - self.setHtml('

' + text + '

') - else: - self.setHtml(text) - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPixelSize(settings.Settings.get_instance()['message_font_size']) - font.setBold(False) - self.setFont(font) - self.resize(width, self.document().size().height()) - self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse) - self.anchorClicked.connect(self.on_anchor_clicked) - - def contextMenuEvent(self, event): - menu = create_menu(self.createStandardContextMenu(event.pos())) - quote = menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Quote selected text')) - quote.triggered.connect(self.quote_text) - text = self.textCursor().selection().toPlainText() - if not text: - quote.setEnabled(False) - else: - import plugin_support - submenu = plugin_support.PluginLoader.get_instance().get_message_menu(menu, text) - if len(submenu): - plug = menu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Plugins')) - plug.addActions(submenu) - menu.popup(event.globalPos()) - menu.exec_(event.globalPos()) - del menu - - def quote_text(self): - text = self.textCursor().selection().toPlainText() - if text: - from ui import main_screen - window = main_screen.MainWindow.get_instance() - text = '>' + '\n>'.join(text.split('\n')) - if window.messageEdit.toPlainText(): - text = '\n' + text - window.messageEdit.appendPlainText(text) - - def on_anchor_clicked(self, url): - text = str(url.toString()) - if text.startswith('tox:'): - from ui import menu - self.add_contact = menu.AddContact(text[4:]) - self.add_contact.show() - else: - QtGui.QDesktopServices.openUrl(url) - self.clearFocus() - - def addAnimation(self, url, fileName): - movie = QtGui.QMovie(self) - movie.setFileName(fileName) - self.urls[movie] = url - movie.frameChanged[int].connect(lambda x: self.animate(movie)) - movie.start() - - def animate(self, movie): - self.document().addResource(QtGui.QTextDocument.ImageResource, - self.urls[movie], - movie.currentPixmap()) - self.setLineWrapColumnOrWidth(self.lineWrapColumnOrWidth()) - - def decoratedText(self, text): - text = h.escape(text) # replace < and > - exp = QtCore.QRegExp( - '(' - '(?:\\b)((www\\.)|(http[s]?|ftp)://)' - '\\w+\\S+)' - '|(?:\\b)(file:///)([\\S| ]*)' - '|(?:\\b)(tox:[a-zA-Z\\d]{76}$)' - '|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)' - '|(?:\\b)(tox:\\S+@\\S+)') - offset = exp.indexIn(text, 0) - while offset != -1: # add links - url = exp.cap() - if exp.cap(2) == 'www.': - html = '{0}'.format(url) - else: - html = '{0}'.format(url) - text = text[:offset] + html + text[offset + len(exp.cap()):] - offset += len(html) - offset = exp.indexIn(text, offset) - arr = text.split('\n') - for i in range(len(arr)): # quotes - if arr[i].startswith('>'): - arr[i] = '' + arr[i][4:] + '' - text = '
'.join(arr) - text = smileys.SmileyLoader.get_instance().add_smileys_to_text(text, self) # smileys - return text - - -class MessageItem(QtWidgets.QWidget): - """ - Message in messages list - """ - def __init__(self, text, time, user='', sent=True, message_type=TOX_MESSAGE_TYPE['NORMAL'], parent=None): - QtWidgets.QWidget.__init__(self, parent) - self.name = DataLabel(self) - self.name.setGeometry(QtCore.QRect(2, 2, 95, 23)) - self.name.setTextFormat(QtCore.Qt.PlainText) - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPointSize(11) - font.setBold(True) - self.name.setFont(font) - self.name.setText(user) - - self.time = QtWidgets.QLabel(self) - self.time.setGeometry(QtCore.QRect(parent.width() - 60, 0, 50, 25)) - font.setPointSize(10) - font.setBold(False) - self.time.setFont(font) - self._time = time - if not sent: - movie = QtGui.QMovie(curr_directory() + '/images/spinner.gif') - self.time.setMovie(movie) - movie.start() - self.t = True - else: - self.time.setText(convert_time(time)) - self.t = False - - self.message = MessageEdit(text, parent.width() - 160, message_type, self) - if message_type != TOX_MESSAGE_TYPE['NORMAL']: - self.name.setStyleSheet("QLabel { color: #5CB3FF; }") - self.message.setAlignment(QtCore.Qt.AlignCenter) - self.time.setStyleSheet("QLabel { color: #5CB3FF; }") - self.message.setGeometry(QtCore.QRect(100, 0, parent.width() - 160, self.message.height())) - self.setFixedHeight(self.message.height()) - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.RightButton and event.x() > self.time.x(): - self.listMenu = QtWidgets.QMenu() - delete_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Delete message')) - delete_item.triggered.connect(self.delete) - parent_position = self.time.mapToGlobal(QtCore.QPoint(0, 0)) - self.listMenu.move(parent_position) - self.listMenu.show() - - def delete(self): - pr = profile.Profile.get_instance() - pr.delete_message(self._time) - - def mark_as_sent(self): - if self.t: - self.time.setText(convert_time(self._time)) - self.t = False - return True - return False - - def set_avatar(self, pixmap): - self.name.setAlignment(QtCore.Qt.AlignCenter) - self.message.setAlignment(QtCore.Qt.AlignVCenter) - self.setFixedHeight(max(self.height(), 36)) - self.name.setFixedHeight(self.height()) - self.message.setFixedHeight(self.height()) - self.name.setPixmap(pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) - - def select_text(self, text): - tmp = self.message.toHtml() - text = h.escape(text) - strings = re.findall(text, tmp, flags=re.IGNORECASE) - for s in strings: - tmp = self.replace_all(tmp, s) - self.message.setHtml(tmp) - - @staticmethod - def replace_all(text, substring): - i, l = 0, len(substring) - while i < len(text) - l + 1: - index = text[i:].find(substring) - if index == -1: - break - i += index - lgt, rgt = text[i:].find('<'), text[i:].find('>') - if rgt < lgt: - i += rgt + 1 - continue - sub = '{}'.format(substring) - text = text[:i] + sub + text[i + l:] - i += len(sub) - return text - - class ContactItem(QtWidgets.QWidget): """ Contact in friends list diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 223e6dd..3852867 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -21,6 +21,7 @@ class MainWindow(QtWidgets.QMainWindow): self._saved = False if settings['show_welcome_screen']: self.ws = WelcomeScreen() + self.profile = None def setup_menu(self, window): self.menubar = QtWidgets.QMenuBar(window) @@ -108,42 +109,42 @@ class MainWindow(QtWidgets.QMainWindow): if event.type() == QtCore.QEvent.WindowActivate: self.tray.setIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) self.messages.repaint() - return super(MainWindow, self).event(event) + return super().event(event) def retranslateUi(self): - self.lockApp.setText(QtWidgets.QApplication.translate("MainWindow", "Lock")) - self.menuPlugins.setTitle(QtWidgets.QApplication.translate("MainWindow", "Plugins")) - self.pluginData.setText(QtWidgets.QApplication.translate("MainWindow", "List of plugins")) - self.menuProfile.setTitle(QtWidgets.QApplication.translate("MainWindow", "Profile")) - self.menuSettings.setTitle(QtWidgets.QApplication.translate("MainWindow", "Settings")) - self.menuAbout.setTitle(QtWidgets.QApplication.translate("MainWindow", "About")) - self.actionAdd_friend.setText(QtWidgets.QApplication.translate("MainWindow", "Add contact")) - self.actionAdd_gc.setText(QtWidgets.QApplication.translate("MainWindow", "Create group chat")) - self.actionprofilesettings.setText(QtWidgets.QApplication.translate("MainWindow", "Profile")) - self.actionPrivacy_settings.setText(QtWidgets.QApplication.translate("MainWindow", "Privacy")) - self.actionInterface_settings.setText(QtWidgets.QApplication.translate("MainWindow", "Interface")) - self.actionNotifications.setText(QtWidgets.QApplication.translate("MainWindow", "Notifications")) - self.actionNetwork.setText(QtWidgets.QApplication.translate("MainWindow", "Network")) - self.actionAbout_program.setText(QtWidgets.QApplication.translate("MainWindow", "About program")) - self.actionSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Settings")) - self.audioSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Audio")) - self.videoSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Video")) - self.updateSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Updates")) - self.contact_name.setPlaceholderText(QtWidgets.QApplication.translate("MainWindow", "Search")) - self.sendMessageButton.setToolTip(QtWidgets.QApplication.translate("MainWindow", "Send message")) - self.callButton.setToolTip(QtWidgets.QApplication.translate("MainWindow", "Start audio call with friend")) + self.lockApp.setText(util_ui.tr("Lock")) + self.menuPlugins.setTitle(util_ui.tr("Plugins")) + self.pluginData.setText(util_ui.tr("List of plugins")) + self.menuProfile.setTitle(util_ui.tr("Profile")) + self.menuSettings.setTitle(util_ui.tr("Settings")) + self.menuAbout.setTitle(util_ui.tr("About")) + self.actionAdd_friend.setText(util_ui.tr("Add contact")) + self.actionAdd_gc.setText(util_ui.tr("Create group chat")) + self.actionprofilesettings.setText(util_ui.tr("Profile")) + self.actionPrivacy_settings.setText(util_ui.tr("Privacy")) + self.actionInterface_settings.setText(util_ui.tr("Interface")) + self.actionNotifications.setText(util_ui.tr("Notifications")) + self.actionNetwork.setText(util_ui.tr("Network")) + self.actionAbout_program.setText(util_ui.tr("About program")) + self.actionSettings.setText(util_ui.tr("Settings")) + self.audioSettings.setText(util_ui.tr("Audio")) + self.videoSettings.setText(util_ui.tr("Video")) + self.updateSettings.setText(util_ui.tr("Updates")) + self.contact_name.setPlaceholderText(util_ui.tr("Search")) + self.sendMessageButton.setToolTip(util_ui.tr("Send message")) + self.callButton.setToolTip(util_ui.tr("Start audio call with friend")) self.online_contacts.clear() - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "All")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online first")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Name")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online and by name")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online first and by name")) + self.online_contacts.addItem(util_ui.tr("All")) + self.online_contacts.addItem(util_ui.tr("Online")) + self.online_contacts.addItem(util_ui.tr("Online first")) + self.online_contacts.addItem(util_ui.tr("Name")) + self.online_contacts.addItem(util_ui.tr("Online and by name")) + self.online_contacts.addItem(util_ui.tr("Online first and by name")) ind = self._settings['sorting'] d = {0: 0, 1: 1, 2: 2, 3: 4, 1 | 4: 4, 2 | 4: 5} self.online_contacts.setCurrentIndex(d[ind]) - self.importPlugin.setText(QtWidgets.QApplication.translate("MainWindow", "Import plugin")) - self.reloadPlugins.setText(QtWidgets.QApplication.translate("MainWindow", "Reload plugins")) + self.importPlugin.setText(util_ui.tr("Import plugin")) + self.reloadPlugins.setText(util_ui.tr("Reload plugins")) def setup_right_bottom(self, Form): Form.resize(650, 60) @@ -353,28 +354,27 @@ class MainWindow(QtWidgets.QMainWindow): self.user_info = name self.friend_info = info self.retranslateUi() - self.profile = Profile(tox, self) def closeEvent(self, event): - s = Settings.get_instance() - if not s['close_to_tray'] or s.closing: - if not self._saved: - self._saved = True - self.profile.save_history() - self.profile.close() - s['x'] = self.geometry().x() - s['y'] = self.geometry().y() - s['width'] = self.width() - s['height'] = self.height() - s.save() - QtWidgets.QApplication.closeAllWindows() - event.accept() + if not self._settings['close_to_tray'] or self._settings.closing: + if self._saved: + return + self._saved = True + self.profile.save_history() + self.profile.close() + self._settings['x'] = self.geometry().x() + self._settings['y'] = self.geometry().y() + self._settings['width'] = self.width() + self._settings['height'] = self.height() + self._settings.save() + QtWidgets.QApplication.closeAllWindows() + event.accept() elif QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): event.ignore() self.hide() def close_window(self): - Settings.get_instance().closing = True + self._settings.closing = True self.close() def resizeEvent(self, *args, **kwargs): @@ -471,7 +471,7 @@ class MainWindow(QtWidgets.QMainWindow): def import_plugin(self): import util directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", 'Choose folder with plugin'), + util_ui.tr('Choose folder with plugin'), util.curr_directory(), QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) if directory: @@ -480,9 +480,9 @@ class MainWindow(QtWidgets.QMainWindow): util.copy(src, dest) msgBox = QtWidgets.QMessageBox() msgBox.setWindowTitle( - QtWidgets.QApplication.translate("MainWindow", "Restart Toxygen")) + util_ui.tr("Restart Toxygen")) msgBox.setText( - QtWidgets.QApplication.translate("MainWindow", 'Plugin will be loaded after restart')) + util_ui.tr('Plugin will be loaded after restart')) msgBox.exec_() def lock_app(self): @@ -492,9 +492,9 @@ class MainWindow(QtWidgets.QMainWindow): else: msgBox = QtWidgets.QMessageBox() msgBox.setWindowTitle( - QtWidgets.QApplication.translate("MainWindow", "Cannot lock app")) + util_ui.tr("Cannot lock app")) msgBox.setText( - QtWidgets.QApplication.translate("MainWindow", 'Error. Profile password is not set.')) + util_ui.tr('Error. Profile password is not set.')) msgBox.exec_() def show_menu(self): @@ -517,7 +517,7 @@ class MainWindow(QtWidgets.QMainWindow): def send_file(self): self.menu.hide() if self.profile.active_friend + 1and self.profile.is_active_a_friend(): - choose = QtWidgets.QApplication.translate("MainWindow", 'Choose file') + choose = util_ui.tr('Choose file') name = QtWidgets.QFileDialog.getOpenFileName(self, choose, options=QtWidgets.QFileDialog.DontUseNativeDialog) if name[0]: self.profile.send_file(name[0]) @@ -583,33 +583,33 @@ class MainWindow(QtWidgets.QMainWindow): return settings = Settings.get_instance() allowed = friend.tox_id in settings['auto_accept_from_friends'] - auto = QtWidgets.QApplication.translate("MainWindow", 'Disallow auto accept') if allowed else QtWidgets.QApplication.translate("MainWindow", 'Allow auto accept') + auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') if item is not None: self.listMenu = QtWidgets.QMenu() is_friend = type(friend) is Friend if is_friend: - set_alias_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Set alias')) + set_alias_item = self.listMenu.addAction(util_ui.tr('Set alias')) set_alias_item.triggered.connect(lambda: self.set_alias(num)) - history_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Chat history')) - clear_history_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Clear history')) - export_to_text_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Export as text')) - export_to_html_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Export as HTML')) + history_menu = self.listMenu.addMenu(util_ui.tr('Chat history')) + clear_history_item = history_menu.addAction(util_ui.tr('Clear history')) + export_to_text_item = history_menu.addAction(util_ui.tr('Export as text')) + export_to_html_item = history_menu.addAction(util_ui.tr('Export as HTML')) - copy_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Copy')) - copy_name_item = copy_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Name')) - copy_status_item = copy_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Status message')) + copy_menu = self.listMenu.addMenu(util_ui.tr('Copy')) + copy_name_item = copy_menu.addAction(util_ui.tr('Name')) + copy_status_item = copy_menu.addAction(util_ui.tr('Status message')) if is_friend: - copy_key_item = copy_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Public key')) + copy_key_item = copy_menu.addAction(util_ui.tr('Public key')) auto_accept_item = self.listMenu.addAction(auto) - remove_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Remove friend')) - block_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Block friend')) - notes_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Notes')) + remove_item = self.listMenu.addAction(util_ui.tr('Remove friend')) + block_item = self.listMenu.addAction(util_ui.tr('Block friend')) + notes_item = self.listMenu.addAction(util_ui.tr('Notes')) chats = self.profile.get_group_chats() if len(chats) and self.profile.is_active_online(): - invite_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Invite to group chat')) + invite_menu = self.listMenu.addMenu(util_ui.tr('Invite to group chat')) for i in range(len(chats)): name, number = chats[i] item = invite_menu.addAction(name) @@ -619,7 +619,7 @@ class MainWindow(QtWidgets.QMainWindow): if plugins_loader is not None: submenu = plugins_loader.get_menu(self.listMenu, num) if len(submenu): - plug = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Plugins')) + plug = self.listMenu.addMenu(util_ui.tr('Plugins')) plug.addActions(submenu) copy_key_item.triggered.connect(lambda: self.copy_friend_key(num)) remove_item.triggered.connect(lambda: self.remove_friend(num)) @@ -627,8 +627,8 @@ class MainWindow(QtWidgets.QMainWindow): auto_accept_item.triggered.connect(lambda: self.auto_accept(num, not allowed)) notes_item.triggered.connect(lambda: self.show_note(friend)) else: - leave_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Leave chat')) - set_title_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Set title')) + leave_item = self.listMenu.addAction(util_ui.tr('Leave chat')) + set_title_item = self.listMenu.addAction(util_ui.tr('Set title')) leave_item.triggered.connect(lambda: self.leave_gc(num)) set_title_item.triggered.connect(lambda: self.set_title(num)) clear_history_item.triggered.connect(lambda: self.clear_history(num)) @@ -643,7 +643,7 @@ class MainWindow(QtWidgets.QMainWindow): def show_note(self, friend): s = Settings.get_instance() note = s['notes'][friend.tox_id] if friend.tox_id in s['notes'] else '' - user = QtWidgets.QApplication.translate("MainWindow", 'Notes about user') + user = util_ui.tr('Notes about user') user = '{} {}'.format(user, friend.name) def save_note(text): diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 1aa12dc..9dcf180 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -16,6 +16,7 @@ class AddContact(CenteredWidget): super(AddContact, self).__init__() self.initUI(tox_id) self._adding = False + self.setAttribute(QtCore.Qt.WA_DeleteOnClose) def initUI(self, tox_id): self.setObjectName('AddContact') diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py new file mode 100644 index 0000000..ae21f8a --- /dev/null +++ b/toxygen/ui/messages_widgets.py @@ -0,0 +1,212 @@ +from PyQt5 import QtWidgets, QtGui, QtCore +from wrapper.toxcore_enums_and_consts import * +import ui.widgets as widgets +import util.ui as util_ui +import util.util as util +import ui.menu as menu +import html as h +import re + + +class MessageEdit(QtWidgets.QTextBrowser): + + def __init__(self, settings, message_edit, smileys_loader, plugin_loader, text, width, message_type, parent=None): + super().__init__(parent) + self.urls = {} + self._message_edit = message_edit + self._smileys_loader = smileys_loader + self._plugin_loader = plugin_loader + self._add_contact = None + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere) + self.document().setTextWidth(width) + self.setOpenExternalLinks(True) + self.setAcceptRichText(True) + self.setOpenLinks(False) + path = smileys_loader.get_smileys_path() + if path is not None: + self.setSearchPaths([path]) + self.document().setDefaultStyleSheet('a { color: #306EFF; }') + text = self.decoratedText(text) + if message_type != TOX_MESSAGE_TYPE['NORMAL']: + self.setHtml('

' + text + '

') + else: + self.setHtml(text) + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPixelSize(settings['message_font_size']) + font.setBold(False) + self.setFont(font) + self.resize(width, self.document().size().height()) + self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse) + self.anchorClicked.connect(self.on_anchor_clicked) + + def contextMenuEvent(self, event): + menu = widgets.create_menu(self.createStandardContextMenu(event.pos())) + quote = menu.addAction(util_ui.tr('Quote selected text')) + quote.triggered.connect(self.quote_text) + text = self.textCursor().selection().toPlainText() + if not text: + quote.setEnabled(False) + else: + sub_menu = self._plugin_loader.get_message_menu(menu, text) + if len(sub_menu): + plugins_menu = menu.addMenu(util_ui.tr('Plugins')) + plugins_menu.addActions(sub_menu) + menu.popup(event.globalPos()) + menu.exec_(event.globalPos()) + del menu + + def quote_text(self): + text = self.textCursor().selection().toPlainText() + if not text: + return + text = '>' + '\n>'.join(text.split('\n')) + if self._message_edit.toPlainText(): + text = '\n' + text + self._message_edit.appendPlainText(text) + + def on_anchor_clicked(self, url): + text = str(url.toString()) + if text.startswith('tox:'): + self._add_contact = menu.AddContact(text[4:]) + self._add_contact.show() + else: + QtGui.QDesktopServices.openUrl(url) + self.clearFocus() + + def addAnimation(self, url, file_name): + movie = QtGui.QMovie(self) + movie.setFileName(file_name) + self.urls[movie] = url + movie.frameChanged[int].connect(lambda x: self.animate(movie)) + movie.start() + + def animate(self, movie): + self.document().addResource(QtGui.QTextDocument.ImageResource, + self.urls[movie], + movie.currentPixmap()) + self.setLineWrapColumnOrWidth(self.lineWrapColumnOrWidth()) + + def decoratedText(self, text): + text = h.escape(text) # replace < and > + exp = QtCore.QRegExp( + '(' + '(?:\\b)((www\\.)|(http[s]?|ftp)://)' + '\\w+\\S+)' + '|(?:\\b)(file:///)([\\S| ]*)' + '|(?:\\b)(tox:[a-zA-Z\\d]{76}$)' + '|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)' + '|(?:\\b)(tox:\\S+@\\S+)') + offset = exp.indexIn(text, 0) + while offset != -1: # add links + url = exp.cap() + if exp.cap(2) == 'www.': + html = '{0}'.format(url) + else: + html = '{0}'.format(url) + text = text[:offset] + html + text[offset + len(exp.cap()):] + offset += len(html) + offset = exp.indexIn(text, offset) + arr = text.split('\n') + for i in range(len(arr)): # quotes + if arr[i].startswith('>'): + arr[i] = '' + arr[i][4:] + '' + text = '
'.join(arr) + text = self._smileys_loader.add_smileys_to_text(text, self) # smileys + return text + + +class MessageItem(QtWidgets.QWidget): + """ + Message in messages list + """ + def __init__(self, settings, message_edit_factory, text_message, parent=None): + QtWidgets.QWidget.__init__(self, parent) + self.name = widgets.DataLabel(self) + self.name.setGeometry(QtCore.QRect(2, 2, 95, 23)) + self.name.setTextFormat(QtCore.Qt.PlainText) + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPointSize(11) + font.setBold(True) + self.name.setFont(font) + self.name.setText(text_message.user) + + self.time = QtWidgets.QLabel(self) + self.time.setGeometry(QtCore.QRect(parent.width() - 60, 0, 50, 25)) + font.setPointSize(10) + font.setBold(False) + self.time.setFont(font) + self._time = time + if not sent: + movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) + self.time.setMovie(movie) + movie.start() + self.t = True + else: + self.time.setText(util.convert_time(time)) + self.t = False + + self.message = MessageEdit(text, parent.width() - 160, message_type, self) + if message_type != TOX_MESSAGE_TYPE['NORMAL']: + self.name.setStyleSheet("QLabel { color: #5CB3FF; }") + self.message.setAlignment(QtCore.Qt.AlignCenter) + self.time.setStyleSheet("QLabel { color: #5CB3FF; }") + self.message.setGeometry(QtCore.QRect(100, 0, parent.width() - 160, self.message.height())) + self.setFixedHeight(self.message.height()) + + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.RightButton and event.x() > self.time.x(): + self.listMenu = QtWidgets.QMenu() + delete_item = self.listMenu.addAction(util_ui.tr('Delete message')) + delete_item.triggered.connect(self.delete) + parent_position = self.time.mapToGlobal(QtCore.QPoint(0, 0)) + self.listMenu.move(parent_position) + self.listMenu.show() + + def delete(self): + pr = profile.Profile.get_instance() + pr.delete_message(self._time) + + def mark_as_sent(self): + if self.t: + self.time.setText(convert_time(self._time)) + self.t = False + return True + return False + + def set_avatar(self, pixmap): + self.name.setAlignment(QtCore.Qt.AlignCenter) + self.message.setAlignment(QtCore.Qt.AlignVCenter) + self.setFixedHeight(max(self.height(), 36)) + self.name.setFixedHeight(self.height()) + self.message.setFixedHeight(self.height()) + self.name.setPixmap(pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) + + def select_text(self, text): + tmp = self.message.toHtml() + text = h.escape(text) + strings = re.findall(text, tmp, flags=re.IGNORECASE) + for s in strings: + tmp = self.replace_all(tmp, s) + self.message.setHtml(tmp) + + @staticmethod + def replace_all(text, substring): + i, l = 0, len(substring) + while i < len(text) - l + 1: + index = text[i:].find(substring) + if index == -1: + break + i += index + lgt, rgt = text[i:].find('<'), text[i:].find('>') + if rgt < lgt: + i += rgt + 1 + continue + sub = '{}'.format(substring) + text = text[:i] + sub + text[i + l:] + i += len(sub) + return text + diff --git a/toxygen/ui/widgets.py b/toxygen/ui/widgets.py index 7b811e7..aab027a 100644 --- a/toxygen/ui/widgets.py +++ b/toxygen/ui/widgets.py @@ -66,14 +66,14 @@ class QRightClickButton(QtWidgets.QPushButton): rightClicked = QtCore.pyqtSignal() - def __init__(self, parent): - super(QRightClickButton, self).__init__(parent) + def __init__(self, parent=None): + super().__init__(parent) def mousePressEvent(self, event): if event.button() == QtCore.Qt.RightButton: self.rightClicked.emit() else: - super(QRightClickButton, self).mousePressEvent(event) + super().mousePressEvent(event) class RubberBand(QtWidgets.QRubberBand): diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index 823dbc0..6107d43 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -11,11 +11,11 @@ class ProfileManager: self._settings = settings self._toxes = toxes self._path = path - self._directory = os.path.basename(path) + self._directory = os.path.dirname(path) # create /avatars if not exists: - directory = util.join_path(self._directory, 'avatars') - if not os.path.exists(directory): - os.makedirs(directory) + avatars_directory = util.join_path(self._directory, 'avatars') + if not os.path.exists(avatars_directory): + os.makedirs(avatars_directory) def open_profile(self): with open(self._path, 'rb') as fl: diff --git a/toxygen/util/util.py b/toxygen/util/util.py index 61df479..b93a45a 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -55,6 +55,11 @@ def get_stickers_directory(): return get_app_directory('stickers') +@cached +def get_smileys_directory(): + return get_app_directory('smileys') + + @cached def get_translations_directory(): return get_app_directory('translations') @@ -143,3 +148,12 @@ def is_re_valid(regex): def get_platform(): return platform.system() + + +class ToxSave: + + def __init__(self, tox): + self._tox = tox + + def set_tox(self, tox): + self._tox = tox From e9272eee2a04e682fb2f396390e2120d208f8ba1 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 29 Apr 2018 00:52:42 +0300 Subject: [PATCH 011/217] deps creation - improvements. db and history fixes. widgets creation - factory --- toxygen/app.py | 46 +++-- toxygen/av/calls_manager.py | 4 +- toxygen/contacts/contact_provider.py | 46 ++++- toxygen/contacts/contacts_manager.py | 82 +++++---- toxygen/contacts/friend_factory.py | 9 +- toxygen/contacts/profile.py | 11 +- .../file_transfers/file_transfers_handler.py | 26 +-- toxygen/history/database.py | 131 +++++++------- toxygen/history/history_loader.py | 84 +++++---- toxygen/messenger/messages.py | 6 +- toxygen/middleware/callbacks.py | 4 + toxygen/ui/av_widgets.py | 2 +- toxygen/ui/main_screen.py | 3 +- toxygen/ui/main_screen_widgets.py | 86 ++++----- toxygen/ui/menu.py | 163 +++++++++--------- toxygen/ui/widgets.py | 2 +- toxygen/ui/widgets_factory.py | 27 +++ toxygen/util/ui.py | 6 + 18 files changed, 419 insertions(+), 319 deletions(-) create mode 100644 toxygen/ui/widgets_factory.py diff --git a/toxygen/app.py b/toxygen/app.py index 8dd4ea8..423ae5f 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -20,6 +20,9 @@ from contacts.profile import Profile from file_transfers.file_transfers_handler import FileTransfersHandler from contacts.contact_provider import ContactProvider from contacts.friend_factory import FriendFactory +from contacts.contacts_manager import ContactsManager +from av.calls_manager import CallsManager +from history.database import Database class App: @@ -29,6 +32,7 @@ class App: self._app = self._settings = self._profile_manager = self._plugin_loader = None self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None + self._friend_factory = self._calls_manager = self._contacts_manager = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] self._path = path_to_profile @@ -97,24 +101,9 @@ class App: if self.try_to_update(): return - self._ms = MainWindow(self._settings, self._tox, self.reset, self._tray) - self._friend_factory = FriendFactory(None, self._profile_manager, self._settings, self._tox) - self._contacts_provider = ContactProvider(self._tox, self._friend_factory) - self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) - profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler) - self._ms.profile = profile - self._ms.show() - - self._tray = tray.init_tray(profile, self._settings, self._ms) - self._tray.show() - - self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) # plugins support + self.create_dependencies() self.start_threads() - # callbacks initialization - callbacks.init_callbacks(self._tox, profile, self._settings, self._plugin_loader, None, None, None, - self._ms, self._tray) - if self._uri is not None: self._ms.add_contact(self._uri) @@ -131,6 +120,9 @@ class App: else: break + self.stop_app() + + def stop_app(self): self._plugin_loader.stop() self.stop_threads() self._tray.hide() @@ -155,6 +147,28 @@ class App: return self._tox + def create_dependencies(self): + self._ms = MainWindow(self._settings, self._tox, self.reset, self._tray) + db = Database(self._path.replace('.tox', '.db'), self._toxes) + self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db) + self._contacts_provider = ContactProvider(self._tox, self._friend_factory) + self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, + self._contacts_provider, db) + self._calls_manager = CallsManager(self._tox.AV, self._settings) + self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) + profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler) + self._ms.profile = profile + self._ms.show() + + self._tray = tray.init_tray(profile, self._settings, self._ms) + self._tray.show() + + self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) # plugins support + + # callbacks initialization + callbacks.init_callbacks(self._tox, profile, self._settings, self._plugin_loader, self._contacts_manager, + self._calls_manager, self._file_transfer_handler, self._ms, self._tray) + def load_app_styles(self): # application color scheme for theme in self._settings.built_in_themes().keys(): diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py index 8060881..03d5c53 100644 --- a/toxygen/av/calls_manager.py +++ b/toxygen/av/calls_manager.py @@ -9,8 +9,8 @@ from ui import av_widgets class CallsManager: - def __init__(self, tox, settings): - self._call = av.calls.AV(tox.AV) # object with data about calls + def __init__(self, toxAV, settings): + self._call = av.calls.AV(toxAV, settings) # object with data about calls self._call_widgets = {} # dict of incoming call widgets self._incoming_calls = set() self._settings = settings diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py index 10b778b..52da937 100644 --- a/toxygen/contacts/contact_provider.py +++ b/toxygen/contacts/contact_provider.py @@ -8,6 +8,20 @@ class ContactProvider(util.ToxSave): self._friend_factory = friend_factory self._cache = {} # key - contact's public key, value - contact instance + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _get_contact_from_cache(self, public_key): + return self._cache[public_key] if public_key in self._cache else None + + def _add_to_cache(self, public_key, contact): + self._cache[public_key] = contact + + # ----------------------------------------------------------------------------------------------------------------- + # Friends + # ----------------------------------------------------------------------------------------------------------------- + def get_friend_by_number(self, friend_number): public_key = self._tox.friend_get_public_key(friend_number) @@ -22,17 +36,39 @@ class ContactProvider(util.ToxSave): return friend + def get_all_friends(self): + friend_numbers = self._tox.self_get_friend_list() + friends = map(lambda n: self.get_friend_by_number(n), friend_numbers) + + return list(friends) + + # ----------------------------------------------------------------------------------------------------------------- + # GC + # ----------------------------------------------------------------------------------------------------------------- + + def get_all_gc(self): + return [] + def get_gc_by_number(self): pass def get_gc_by_public_key(self): pass + # ----------------------------------------------------------------------------------------------------------------- + # All contacts + # ----------------------------------------------------------------------------------------------------------------- + + def get_all(self): + return self.get_all_friends() + self.get_all_gc() + + # ----------------------------------------------------------------------------------------------------------------- + # Caching + # ----------------------------------------------------------------------------------------------------------------- + def clear_cache(self): self._cache.clear() - def _get_contact_from_cache(self, public_key): - return self._cache[public_key] if public_key in self._cache else None - - def _add_to_cache(self, public_key, contact): - self._cache[public_key] = contact + def remove_friend_from_cache(self, friend_public_key): + if friend_public_key in self._cache: + del self._cache[friend_public_key] diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 122db5c..510dbb3 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -6,31 +6,40 @@ from PyQt5 import QtCore, QtGui, QtWidgets from messenger.messages import * from wrapper.toxcore_enums_and_consts import * from network.tox_dns import tox_dns +from history.history_loader import HistoryLoader class ContactsManager: - def __init__(self, tox, settings, screen, profile_manager): + def __init__(self, tox, settings, screen, profile_manager, contact_provider, db): self._tox = tox self._settings = settings self._screen = screen self._profile_manager = profile_manager + self._contact_provider = contact_provider self._messages = screen.messages - self._contacts, self._active_friend = [], -1 + self._contacts, self._active_contact = [], -1 self._sorting = settings['sorting'] self._filter_string = '' self._friend_item_height = 40 if settings['compact_mode'] else 70 screen.online_contacts.setCurrentIndex(int(self._sorting)) - self.load_contacts() + self._history = HistoryLoader(contact_provider, db, settings) + self._load_contacts() - def load_contacts(self): - self.load_friends() - self.load_groups() - if len(self._contacts): - self.set_active(0) + def __del__(self): + del self._history + + def _is_active_a_friend(self): + return type(self.get_curr_contact()) is Friend + + def _load_contacts(self): + self._load_friends() + self._load_groups() + # if len(self._contacts): + # self.set_active(0) self.filtration_and_sorting(self._sorting) - def load_friends(self): + def _load_friends(self): aliases = self._settings['friends_aliases'] friend_numbers = self._tox.self_get_friend_list() for friend_number in friend_numbers: # creates list of friends @@ -41,15 +50,14 @@ class ContactsManager: alias = '' item = self.create_friend_item() name = alias or self._tox.friend_get_name(friend_number) or tox_id - status_message = self._tox.friend_get_status_message(i) - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) + status_message = self._tox.friend_get_status_message(friend_number) + self._history.get_message_getter(tox_id) + message_getter = self._history.get_message_getter(tox_id) friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, tox_id) friend.set_alias(alias) self._contacts.append(friend) - def load_groups(self): + def _load_groups(self): pass def get_friend(self, num): @@ -57,8 +65,8 @@ class ContactsManager: return None return self._contacts[num] - def get_curr_friend(self): - return self._contacts[self._active_friend] if self._active_friend + 1 else None + def get_curr_contact(self): + return self._contacts[self._active_contact] if self._active_contact + 1 else None def save_profile(self): data = self._tox.get_savedata() @@ -69,20 +77,20 @@ class ContactsManager: # ----------------------------------------------------------------------------------------------------------------- def get_active(self): - return self._active_friend + return self._active_contact def set_active(self, value=None): """ Change current active friend or update info :param value: number of new active friend in friend's list or None to update active user's data """ - if value is None and self._active_friend == -1: # nothing to update + if value is None and self._active_contact == -1: # nothing to update return if value == -1: # all friends were deleted self._screen.account_name.setText('') self._screen.account_status.setText('') self._screen.account_status.setToolTip('') - self._active_friend = -1 + self._active_contact = -1 self._screen.account_avatar.setHidden(True) self._messages.clear() self._screen.messageEdit.clear() @@ -91,16 +99,16 @@ class ContactsManager: self.send_typing(False) self._screen.typing.setVisible(False) if value is not None: - if self._active_friend + 1 and self._active_friend != value: + if self._active_contact + 1 and self._active_contact != value: try: self.get_curr_friend().curr_text = self._screen.messageEdit.toPlainText() except: pass friend = self._contacts[value] friend.remove_invalid_unsent_files() - if self._active_friend != value: + if self._active_contact != value: self._screen.messageEdit.setPlainText(friend.curr_text) - self._active_friend = value + self._active_contact = value friend.reset_messages() if not self._settings['save_history']: friend.delete_old_messages() @@ -166,14 +174,14 @@ class ContactsManager: for i in range(len(self._contacts)): c = self._contacts[i] if c.number == number and (type(c) is Friend == is_friend): - self._active_friend = i + self._active_contact = i break active_friend = property(get_active, set_active) def update(self): - if self._active_friend + 1: - self.set_active(self._active_friend) + if self._active_contact + 1: + self.set_active(self._active_contact) # ----------------------------------------------------------------------------------------------------------------- # Filtration @@ -187,7 +195,7 @@ class ContactsManager: """ filter_str = filter_str.lower() number = self.get_active_number() - is_friend = self.is_active_a_friend() + is_friend = self._is_active_a_friend() if sorting > 1: if sorting & 2: self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True) @@ -216,10 +224,10 @@ class ContactsManager: for index, friend in enumerate(self._contacts): friend.visibility = (friend.status is not None or not (sorting & 1)) and (filter_str in friend.name.lower()) friend.visibility = friend.visibility or friend.messages or friend.actions - if friend.visibility: - self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) - else: - self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) + # if friend.visibility: + # self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) + # else: + # self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) self._sorting, self._filter_string = sorting, filter_str self._settings['sorting'] = self._sorting self._settings.save() @@ -236,7 +244,7 @@ class ContactsManager: Method-factory :return: new widget for friend instance """ - return self._factory.friend_item() + return None #self._factory.friend_item() # ----------------------------------------------------------------------------------------------------------------- # Friend getters @@ -246,19 +254,19 @@ class ContactsManager: return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0] def get_last_message(self): - if self._active_friend + 1: + if self._active_contact + 1: return self.get_curr_friend().get_last_message_text() else: return '' def get_active_number(self): - return self.get_curr_friend().number if self._active_friend + 1 else -1 + return self.get_curr_friend().number if self._active_contact + 1 else -1 def get_active_name(self): - return self.get_curr_friend().name if self._active_friend + 1 else '' + return self.get_curr_friend().name if self._active_contact + 1 else '' def is_active_online(self): - return self._active_friend + 1 and self.get_curr_friend().status is not None + return self._active_contact + 1 and self.get_curr_friend().status is not None def new_name(self, number, name): friend = self.get_friend_by_number(number) @@ -333,7 +341,7 @@ class ContactsManager: self._tox.friend_delete(friend.number) del self._contacts[num] self._screen.friends_list.takeItem(num) - if num == self._active_friend: # active friend was deleted + if num == self._active_contact: # active friend was deleted if not len(self._contacts): # last friend was deleted self.set_active(-1) else: @@ -449,7 +457,7 @@ class ContactsManager: """ Send typing notification to a friend """ - if self._settings['typing_notifications'] and self._active_friend + 1: + if self._settings['typing_notifications'] and self._active_contact + 1: try: friend = self.get_curr_friend() if friend.status is not None: diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py index 556f2d4..23966f8 100644 --- a/toxygen/contacts/friend_factory.py +++ b/toxygen/contacts/friend_factory.py @@ -3,9 +3,10 @@ from contacts.friend import Friend class FriendFactory: - def __init__(self, history, profile_manager, settings, tox): - self._history, self._profile_manager = history, profile_manager + def __init__(self, profile_manager, settings, tox, db): + self._profile_manager = profile_manager self._settings, self._tox = settings, tox + self._db = db def create_friend_by_number(self, friend_number): aliases = self._settings['friends_aliases'] @@ -17,9 +18,7 @@ class FriendFactory: item = self.create_friend_item() name = alias or self._tox.friend_get_name(friend_number) or tox_id status_message = self._tox.friend_get_status_message(friend_number) - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) + message_getter = self._db.messages_getter(tox_id) friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, tox_id) friend.set_alias(alias) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 0bfc764..70a6f7e 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -16,6 +16,7 @@ import cv2 import threading from contacts.group_chat import * import re +import util.ui as util_ui class Profile(basecontact.BaseContact): @@ -69,7 +70,7 @@ class Profile(basecontact.BaseContact): tmp = self.name super(Profile, self).set_name(value.encode('utf-8')) self._tox.self_set_name(self._name.encode('utf-8')) - message = QtWidgets.QApplication.translate("MainWindow", 'User {} is now known as {}') + message = util_ui.tr('User {} is now known as {}') message = message.format(tmp, value) for friend in self._contacts: friend.append_message(InfoMessage(message, time.time())) @@ -290,14 +291,9 @@ class Profile(basecontact.BaseContact): """ for friend in self._contacts: self.friend_exit(friend.number) - self._call.stop() - del self._call del self._tox self._tox = restart() - self._call = calls.AV(self._tox.AV) self.status = None - for friend in self._contacts: - friend.number = self._tox.friend_by_public_key(friend.tox_id) # numbers update self._contacts_manager.update_filtration() def reconnect(self): @@ -315,9 +311,6 @@ class Profile(basecontact.BaseContact): if hasattr(self, '_call'): self._call.stop() del self._call - s = Settings.get_instance() - s['paused_file_transfers'] = dict(self._paused_file_transfers) if s['resend_files'] else {} - s.save() def reset_avatar(self): super().reset_avatar() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 11c47c5..826adc0 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -1,6 +1,6 @@ from file_transfers.file_transfers import * from messenger.messages import * -from history.database import MESSAGE_OWNER +from history.database import MESSAGE_AUTHOR import os import util.util as util @@ -15,6 +15,13 @@ class FileTransfersHandler: # key = (friend number, file number), value - transfer instance self._paused_file_transfers = dict(settings['paused_file_transfers']) # key - file id, value: [path, friend number, is incoming, start position] + + def __del__(self): + self._settings['paused_file_transfers'] = self._paused_file_transfers if self._settings['resend_files'] else {} + self._settings.save() + + def _get_friend_by_number(self, friend_number): + return self._contact_provider.get_friend_by_number(friend_number) # ----------------------------------------------------------------------------------------------------------------- # File transfers support @@ -41,7 +48,7 @@ class FileTransfersHandler: return self._tox.file_seek(friend_number, file_number, pos) self.accept_transfer(None, data[0], friend_number, file_number, size, False, pos) - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + tm = TransferMessage(MESSAGE_AUTHOR['FRIEND'], time.time(), TOX_FILE_TRANSFER_STATE['RUNNING'], size, @@ -50,7 +57,7 @@ class FileTransfersHandler: file_number) elif inline and size < 1024 * 1024: self.accept_transfer(None, '', friend_number, file_number, size, True) - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + tm = TransferMessage(MESSAGE_AUTHOR['FRIEND'], time.time(), TOX_FILE_TRANSFER_STATE['RUNNING'], size, @@ -61,7 +68,7 @@ class FileTransfersHandler: elif auto: path = self._settings['auto_accept_path'] or util.curr_directory() self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size) - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + tm = TransferMessage(MESSAGE_AUTHOR['FRIEND'], time.time(), TOX_FILE_TRANSFER_STATE['RUNNING'], size, @@ -69,7 +76,7 @@ class FileTransfersHandler: friend_number, file_number) else: - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + tm = TransferMessage(MESSAGE_AUTHOR['FRIEND'], time.time(), TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'], size, @@ -180,7 +187,6 @@ class FileTransfersHandler: :param data: raw data - png """ self.send_inline(data, 'toxygen_inline.png') - self._messages.repaint() def send_sticker(self, path): with open(path, 'rb') as fl: @@ -200,7 +206,7 @@ class FileTransfersHandler: st = SendFromBuffer(self._tox, friend.number, data, file_name) st.set_transfer_finished_handler(self.transfer_finished) self._file_transfers[(friend.number, st.get_file_number())] = st - tm = TransferMessage(MESSAGE_OWNER['ME'], + tm = TransferMessage(MESSAGE_AUTHOR['ME'], time.time(), TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], len(data), @@ -233,7 +239,7 @@ class FileTransfersHandler: st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) st.set_transfer_finished_handler(self.transfer_finished) self._file_transfers[(friend_number, st.get_file_number())] = st - tm = TransferMessage(MESSAGE_OWNER['ME'], + tm = TransferMessage(MESSAGE_AUTHOR['ME'], time.time(), TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], os.path.getsize(path), @@ -293,6 +299,7 @@ class FileTransfersHandler: def send_avatar(self, friend_number, avatar_path=None): """ :param friend_number: number of friend who should get new avatar + :param avatar_path: path to avatar or None if reset """ sa = SendAvatar(avatar_path, self._tox, friend_number) self._file_transfers[(friend_number, sa.get_file_number())] = sa @@ -312,6 +319,3 @@ class FileTransfersHandler: self.get_friend_by_number(friend_number).load_avatar() if self.get_active_number() == friend_number and self.is_active_a_friend(): self.set_active(None) - - def _get_friend_by_number(self, friend_number): - return self._contact_provider.get_friend_by_number(friend_number) diff --git a/toxygen/history/database.py b/toxygen/history/database.py index 9960b54..d7c10eb 100644 --- a/toxygen/history/database.py +++ b/toxygen/history/database.py @@ -1,32 +1,31 @@ from sqlite3 import connect -from user_data import settings -from os import chdir import os.path +import util.util as util -PAGE_SIZE = 42 - TIMEOUT = 11 SAVE_MESSAGES = 500 -MESSAGE_OWNER = { +MESSAGE_AUTHOR = { 'ME': 0, 'FRIEND': 1, 'NOT_SENT': 2, 'GC_PEER': 3 } -# TODO: unique message id and ngc support, profile name as db name +CONTACT_TYPE = { + 'FRIEND': 0, + 'GC_PEER': 1, + 'GC_PEER_PRIVATE': 2 +} class Database: - def __init__(self, name, toxes): - self._name = name - self._toxes = toxes - chdir(settings.ProfileManager.get_path()) - path = settings.ProfileManager.get_path() + self._name + '.hstr' + def __init__(self, path, toxes): + self._path, self._toxes = path, toxes + self._name = os.path.basename(path) if os.path.exists(path): try: with open(path, 'rb') as fin: @@ -35,28 +34,35 @@ class Database: data = toxes.pass_decrypt(data) with open(path, 'wb') as fout: fout.write(data) - except: + except Exception as ex: + util.log('Db reading error: ' + str(ex)) os.remove(path) - db = connect(name + '.hstr', timeout=TIMEOUT) + db = self._connect() cursor = db.cursor() - cursor.execute('CREATE TABLE IF NOT EXISTS friends(' - ' tox_id TEXT PRIMARY KEY' + cursor.execute('CREATE TABLE IF NOT EXISTS contacts (' + ' tox_id TEXT PRIMARY KEY,' + ' contact_type INTEGER' ')') db.close() + def _connect(self): + return connect(self._path, timeout=TIMEOUT) + + # ----------------------------------------------------------------------------------------------------------------- + # Public methods + # ----------------------------------------------------------------------------------------------------------------- + def save(self): if self._toxes.has_password(): - path = settings.ProfileManager.get_path() + self._name + '.hstr' - with open(path, 'rb') as fin: + with open(self._path, 'rb') as fin: data = fin.read() data = self._toxes.pass_encrypt(bytes(data)) - with open(path, 'wb') as fout: + with open(self._path, 'wb') as fout: fout.write(data) def export(self, directory): - path = settings.ProfileManager.get_path() + self._name + '.hstr' - new_path = directory + self._name + '.hstr' - with open(path, 'rb') as fin: + new_path = util.join_path(directory, self._name) + with open(self._path, 'rb') as fin: data = fin.read() if self._toxes.has_password(): data = self._toxes.pass_encrypt(data) @@ -64,15 +70,16 @@ class Database: fout.write(data) def add_friend_to_db(self, tox_id): - chdir(settings.ProfileManager.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) + db = self._connect() try: cursor = db.cursor() - cursor.execute('INSERT INTO friends VALUES (?);', (tox_id, )) + cursor.execute('INSERT INTO contacts VALUES (?);', (tox_id, )) cursor.execute('CREATE TABLE id' + tox_id + '(' ' id INTEGER PRIMARY KEY,' + ' message_id INTEGER,' + ' author_name TEXT,' ' message TEXT,' - ' owner INTEGER,' + ' author INTEGER,' ' unix_time REAL,' ' message_type INTEGER' ')') @@ -84,11 +91,10 @@ class Database: db.close() def delete_friend_from_db(self, tox_id): - chdir(settings.ProfileManager.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) + db = self._connect() try: cursor = db.cursor() - cursor.execute('DELETE FROM friends WHERE tox_id=?;', (tox_id, )) + cursor.execute('DELETE FROM contacts WHERE tox_id=?;', (tox_id, )) cursor.execute('DROP TABLE id' + tox_id + ';') db.commit() except: @@ -98,21 +104,20 @@ class Database: db.close() def friend_exists_in_db(self, tox_id): - chdir(settings.ProfileManager.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) + db = self._connect() cursor = db.cursor() - cursor.execute('SELECT 0 FROM friends WHERE tox_id=?', (tox_id, )) + cursor.execute('SELECT 1 FROM contacts WHERE tox_id=?', (tox_id, )) result = cursor.fetchone() db.close() return result is not None def save_messages_to_db(self, tox_id, messages_iter): - chdir(settings.ProfileManager.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) + db = self._connect() try: cursor = db.cursor() - cursor.executemany('INSERT INTO id' + tox_id + '(message, owner, unix_time, message_type) ' - 'VALUES (?, ?, ?, ?);', messages_iter) + cursor.executemany('INSERT INTO id' + tox_id + + '(message, message_id, author_name, author, unix_time, message_type) ' + + 'VALUES (?, ?, ?, ?, ?, ?);', messages_iter) db.commit() except: print('Database is locked!') @@ -120,13 +125,12 @@ class Database: finally: db.close() - def update_messages(self, tox_id, unsent_time): - chdir(settings.ProfileManager.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) + def update_messages(self, tox_id, message_id): + db = self._connect() try: cursor = db.cursor() - cursor.execute('UPDATE id' + tox_id + ' SET owner = 0 ' - 'WHERE unix_time < ' + str(unsent_time) + ' AND owner = 2;') + cursor.execute('UPDATE id' + tox_id + ' SET author = 0 ' + 'WHERE message_id = ' + str(message_id) + ' AND author = 2;') db.commit() except: print('Database is locked!') @@ -134,13 +138,11 @@ class Database: finally: db.close() - def delete_message(self, tox_id, message_id): - chdir(settings.ProfileManager.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) + def delete_message(self, tox_id, unique_id): + db = self._connect() try: cursor = db.cursor() - cursor.execute('DELETE FROM id' + tox_id + ' WHERE unix_time < ' + end + ' AND unix_time > ' + - start + ';') + cursor.execute('DELETE FROM id' + tox_id + ' WHERE id = ' + str(unique_id) + ';') db.commit() except: print('Database is locked!') @@ -149,8 +151,7 @@ class Database: db.close() def delete_messages(self, tox_id): - chdir(settings.ProfileManager.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) + db = self._connect() try: cursor = db.cursor() cursor.execute('DELETE FROM id' + tox_id + ';') @@ -162,46 +163,44 @@ class Database: db.close() def messages_getter(self, tox_id): - return Database.MessageGetter(self._name, tox_id) + return Database.MessageGetter(self._path, tox_id) + + # ----------------------------------------------------------------------------------------------------------------- + # Messages loading + # ----------------------------------------------------------------------------------------------------------------- class MessageGetter: - def __init__(self, name, tox_id): + def __init__(self, path, tox_id): self._count = 0 - self._name = name + self._path = path self._tox_id = tox_id self._db = self._cursor = None - def connect(self): - chdir(settings.ProfileManager.get_path()) - self._db = connect(self._name + '.hstr', timeout=TIMEOUT) + def _connect(self): + self._db = connect(self._path, timeout=TIMEOUT) self._cursor = self._db.cursor() - self._cursor.execute('SELECT message, owner, unix_time, message_type FROM id' + self._tox_id + - ' ORDER BY unix_time DESC;') + self._cursor.execute('SELECT id, message_id, message, author, unix_time, message_type FROM id' + + self._tox_id + ' ORDER BY unix_time DESC;') - def disconnect(self): + def _disconnect(self): self._db.close() def get_one(self): - self.connect() - self.skip() - data = self._cursor.fetchone() - self._count += 1 - self.disconnect() - return data + return self.get(1) def get_all(self): - self.connect() + self._connect() data = self._cursor.fetchall() - self.disconnect() + self._disconnect() self._count = len(data) return data def get(self, count): - self.connect() + self._connect() self.skip() data = self._cursor.fetchmany(count) - self.disconnect() + self._disconnect() self._count += len(data) return data diff --git a/toxygen/history/history_loader.py b/toxygen/history/history_loader.py index 86e61c5..801125b 100644 --- a/toxygen/history/history_loader.py +++ b/toxygen/history/history_loader.py @@ -1,11 +1,17 @@ from messenger.messages import * +# TODO: fix history loading and saving + class HistoryLoader: - def __init__(self, db, settings): + def __init__(self, contact_provider, db, settings): + self._contact_provider = contact_provider self._db = db self._settings = settings + + def __del__(self): + del self._db # ----------------------------------------------------------------------------------------------------------------- # History support @@ -15,22 +21,20 @@ class HistoryLoader: """ Save history to db """ - if hasattr(self, '_history'): - if self._settings['save_history']: - for friend in filter(lambda x: type(x) is Friend, self._contacts): - if not self._history.friend_exists_in_db(friend.tox_id): - self._history.add_friend_to_db(friend.tox_id) - if not self._settings['save_unsent_only']: - messages = friend.get_corr_for_saving() - else: - messages = friend.get_unsent_messages_for_saving() - self._history.delete_messages(friend.tox_id) - self._history.save_messages_to_db(friend.tox_id, messages) - unsent_messages = friend.get_unsent_messages() - unsent_time = unsent_messages[0].get_data()[2] if len(unsent_messages) else time.time() + 1 - self._history.update_messages(friend.tox_id, unsent_time) - self._history.save() - del self._history + if self._settings['save_db']: + for friend in self._contact_provider.get_all_friends(): + if not self._db.friend_exists_in_db(friend.tox_id): + self._db.add_friend_to_db(friend.tox_id) + if not self._settings['save_unsent_only']: + messages = friend.get_corr_for_saving() + else: + messages = friend.get_unsent_messages_for_saving() + self._db.delete_messages(friend.tox_id) + self._db.save_messages_to_db(friend.tox_id, messages) + unsent_messages = friend.get_unsent_messages() + # unsent_time = unsent_messages[0].get_data()[2] if len(unsent_messages) else time.time() + 1 + # self._db.update_messages(friend.tox_id, unsent_time) + self._db.save() def clear_history(self, friend, save_unsent=False): """ @@ -45,9 +49,9 @@ class HistoryLoader: """ Tries to load next part of messages """ - if not self._load_history: + if not self._load_db: return - self._load_history = False + self._load_db = False friend = self.get_curr_friend() friend.load_corr(False) data = friend.get_corr() @@ -84,9 +88,16 @@ class HistoryLoader: '', data[3], False) - self._load_history = True + self._load_db = True - def export_history(self, friend, as_text=True, _range=None): + def get_message_getter(self, friend_public_key): + if not self._db.friend_exists_in_db(friend_public_key): + self._db.add_friend_to_db(friend_public_key) + + return self._db.messages_getter(friend_public_key) + + @staticmethod + def export_history(friend, as_text=True, _range=None): if _range is None: friend.load_all_corr() corr = friend.get_corr() @@ -95,16 +106,28 @@ class HistoryLoader: else: corr = friend.get_corr()[_range[0]:] - generator = TextHistoryGenerator() if as_text else HtmlHistoryGenerator + generator = TextHistoryGenerator(corr) if as_text else HtmlHistoryGenerator(corr) - return generator.generate(corr) + return generator.generate() -class HtmlHistoryGenerator: +class HistoryLogsGenerator: - def generate(self, corr): + def __init__(self, history): + self._history = history + + def generate(self): + return str() + + +class HtmlHistoryGenerator(HistoryLogsGenerator): + + def __init__(self, history): + super().__init__(history) + + def generate(self): arr = [] - for message in corr: + for message in self._history: if type(message) is TextMessage: data = message.get_data() x = '[{}] {}: {}
' @@ -117,11 +140,14 @@ class HtmlHistoryGenerator: return s -class TextHistoryGenerator: +class TextHistoryGenerator(HistoryLogsGenerator): - def generate(self, corr): + def __init__(self, history): + super().__init__(history) + + def generate(self): arr = [] - for message in corr: + for message in self._history: if type(message) is TextMessage: data = message.get_data() x = '[{}] {}: {}\n' diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index e8abec7..023a2e7 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -1,4 +1,4 @@ -from history.database import MESSAGE_OWNER +from history.database import MESSAGE_AUTHOR MESSAGE_TYPE = { @@ -9,6 +9,8 @@ MESSAGE_TYPE = { 'INFO_MESSAGE': 4 } +PAGE_SIZE = 42 + class MessageAuthor: @@ -53,7 +55,7 @@ class Message: self._widget = None def mark_as_sent(self): - self._author.author_type = MESSAGE_OWNER['ME'] + self._author.author_type = MESSAGE_AUTHOR['ME'] def _create_widget(self): pass diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 926c7af..153f341 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -377,6 +377,10 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, :param tox: Tox instance :param profile: Profile instance :param settings: Settings instance + :param contacts_manager: ContactsManager instance + :param contacts_manager: ContactsManager instance + :param calls_manager: CallsManager instance + :param file_transfer_handler: FileTransferHandler instance :param plugin_loader: PluginLoader instance :param main_window: main window screen :param tray: tray (for notifications) diff --git a/toxygen/ui/av_widgets.py b/toxygen/ui/av_widgets.py index a783eca..676df78 100644 --- a/toxygen/ui/av_widgets.py +++ b/toxygen/ui/av_widgets.py @@ -11,7 +11,7 @@ from util.util import curr_directory class IncomingCallWidget(widgets.CenteredWidget): def __init__(self, friend_number, text, name): - super(IncomingCallWidget, self).__init__() + super().__init__() self.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowStaysOnTopHint) self.resize(QtCore.QSize(500, 270)) self.avatar_label = QtWidgets.QLabel(self) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 3852867..73f4d4e 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -360,7 +360,6 @@ class MainWindow(QtWidgets.QMainWindow): if self._saved: return self._saved = True - self.profile.save_history() self.profile.close() self._settings['x'] = self.geometry().x() self._settings['y'] = self.geometry().y() @@ -500,7 +499,7 @@ class MainWindow(QtWidgets.QMainWindow): def show_menu(self): if not hasattr(self, 'menu'): self.menu = DropdownMenu(self) - self.menu.setGeometry(QtCore.QRect(0 if Settings.get_instance()['mirror_mode'] else 270, + self.menu.setGeometry(QtCore.QRect(0 if self._settings['mirror_mode'] else 270, self.height() - 120, 180, 120)) diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index 064c745..fe66a68 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -2,14 +2,16 @@ from PyQt5 import QtCore, QtGui, QtWidgets from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit from contacts.profile import Profile import smileys +import urllib import util.util as util +import util.ui as util_ui class MessageArea(QtWidgets.QPlainTextEdit): """User types messages here""" def __init__(self, parent, form): - super(MessageArea, self).__init__(parent) + super().__init__(parent) self.parent = form self.setAcceptDrops(True) self.timer = QtCore.QTimer(self) @@ -76,15 +78,18 @@ class MessageArea(QtWidgets.QPlainTextEdit): self.insertPlainText(text) def parse_file_name(self, file_name): - import urllib if file_name.endswith('\r\n'): file_name = file_name[:-2] file_name = urllib.parse.unquote(file_name) - return file_name[8 if platform.system() == 'Windows' else 7:] + return file_name[8 if util.get_platform() == 'Windows' else 7:] class ScreenShotWindow(RubberBandWindow): + def __init__(self, file_transfer_handler, *args): + super().__init__(*args) + self._file_transfer_handler = file_transfer_handler + def closeEvent(self, *args): if self.parent.isHidden(): self.parent.show() @@ -104,7 +109,7 @@ class ScreenShotWindow(RubberBandWindow): buffer = QtCore.QBuffer(byte_array) buffer.open(QtCore.QIODevice.WriteOnly) p.save(buffer, 'PNG') - Profile.get_instance().send_screenshot(bytes(byte_array.data())) + self._file_transfer_handler.send_screenshot(bytes(byte_array.data())) self.close() @@ -113,11 +118,10 @@ class SmileyWindow(QtWidgets.QWidget): Smiley selection window """ - def __init__(self, parent): - super(SmileyWindow, self).__init__() + def __init__(self, parent, smiley_loader): + super().__init__(parent) self.setWindowFlags(QtCore.Qt.FramelessWindowHint) - inst = smileys.SmileyLoader.get_instance() - self.data = inst.get_smileys() + self.data = smiley_loader.get_smileys() count = len(self.data) if not count: self.close() @@ -172,18 +176,18 @@ class SmileyWindow(QtWidgets.QWidget): class MenuButton(QtWidgets.QPushButton): def __init__(self, parent, enter): - super(MenuButton, self).__init__(parent) + super().__init__(parent) self.enter = enter def enterEvent(self, event): self.enter() - super(MenuButton, self).enterEvent(event) + super().enterEvent(event) class DropdownMenu(QtWidgets.QWidget): def __init__(self, parent): - super(DropdownMenu, self).__init__(parent) + super().__init__(parent) self.installEventFilter(self) self.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.setMaximumSize(120, 120) @@ -245,7 +249,7 @@ class DropdownMenu(QtWidgets.QWidget): class StickerItem(QtWidgets.QWidget): def __init__(self, fl): - super(StickerItem, self).__init__() + super().__init__() self._image_label = QtWidgets.QLabel(self) self.path = fl self.pixmap = QtGui.QPixmap() @@ -260,7 +264,7 @@ class StickerWindow(QtWidgets.QWidget): """Sticker selection window""" def __init__(self, parent): - super(StickerWindow, self).__init__() + super().__init__() self.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.setMaximumSize(250, 200) self.setMinimumSize(250, 200) @@ -289,8 +293,9 @@ class StickerWindow(QtWidgets.QWidget): class WelcomeScreen(CenteredWidget): - def __init__(self): + def __init__(self, settings): super().__init__() + self._settings = settings self.setMaximumSize(250, 200) self.setMinimumSize(250, 200) self.center() @@ -300,51 +305,39 @@ class WelcomeScreen(CenteredWidget): self.text.setOpenExternalLinks(True) self.checkbox = QtWidgets.QCheckBox(self) self.checkbox.setGeometry(QtCore.QRect(5, 170, 240, 30)) - self.checkbox.setText(QtWidgets.QApplication.translate('WelcomeScreen', "Don't show again")) - self.setWindowTitle(QtWidgets.QApplication.translate('WelcomeScreen', 'Tip of the day')) + self.checkbox.setText(util_ui.tr( "Don't show again")) + self.setWindowTitle(util_ui.tr( 'Tip of the day')) import random num = random.randint(0, 10) if num == 0: - text = QtWidgets.QApplication.translate('WelcomeScreen', 'Press Esc if you want hide app to tray.') + text = util_ui.tr('Press Esc if you want hide app to tray.') elif num == 1: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Right click on screenshot button hides app to tray during screenshot.') + text = util_ui.tr('Right click on screenshot button hides app to tray during screenshot.') elif num == 2: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'You can use Tox over Tor. For more info read this post') + text = util_ui.tr('You can use Tox over Tor. For more info read this post') elif num == 3: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Use Settings -> Interface to customize interface.') + text = util_ui.tr('Use Settings -> Interface to customize interface.') elif num == 4: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Set profile password via Profile -> Settings. Password allows Toxygen encrypt your history and settings.') + text = util_ui.tr('Set profile password via Profile -> Settings. Password allows Toxygen encrypt your history and settings.') elif num == 5: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Since v0.1.3 Toxygen supports plugins. Read more') + text = util_ui.tr('Since v0.1.3 Toxygen supports plugins. Read more') elif num == 6: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.') + text = util_ui.tr('Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.') elif num == 7: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'New in Toxygen 0.4.1:
Downloading nodes from tox.chat
Bug fixes') + text = util_ui.tr('New in Toxygen 0.4.1:
Downloading nodes from tox.chat
Bug fixes') elif num == 8: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Delete single message in chat: make right click on spinner or message time and choose "Delete" in menu') + text = util_ui.tr('Delete single message in chat: make right click on spinner or message time and choose "Delete" in menu') elif num == 9: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Use right click on inline image to save it') + text = util_ui.tr( 'Use right click on inline image to save it') else: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Set new NoSpam to avoid spam friend requests: Profile -> Settings -> Set new NoSpam.') + text = util_ui.tr('Set new NoSpam to avoid spam friend requests: Profile -> Settings -> Set new NoSpam.') self.text.setHtml(text) self.checkbox.stateChanged.connect(self.not_show) QtCore.QTimer.singleShot(1000, self.show) def not_show(self): - from user_data import settings - s = settings.Settings.get_instance() - s['show_welcome_screen'] = False - s.save() + self._settings['show_welcome_screen'] = False + self._settings.save() class MainMenuButton(QtWidgets.QPushButton): @@ -417,7 +410,7 @@ class SearchScreen(QtWidgets.QWidget): self.retranslateUi() def retranslateUi(self): - self.search_text.setPlaceholderText(QtWidgets.QApplication.translate("MainWindow", "Search")) + self.search_text.setPlaceholderText(util_ui.tr('Search')) def show(self): super().show() @@ -473,11 +466,4 @@ class SearchScreen(QtWidgets.QWidget): @staticmethod def not_found(text): - mbox = QtWidgets.QMessageBox() - mbox_text = QtWidgets.QApplication.translate("MainWindow", - 'Text "{}" was not found') - - mbox.setText(mbox_text.format(text)) - mbox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", - 'Not found')) - mbox.exec_() + util_ui.message_box(util_ui.tr('Text "{}" was not found').format(text), util_ui.tr('Not found')) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 9dcf180..c509359 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -7,13 +7,15 @@ import pyaudio from user_data import toxes import plugin_support import updater +import util.ui as util_ui class AddContact(CenteredWidget): """Add contact form""" - def __init__(self, tox_id=''): - super(AddContact, self).__init__() + def __init__(self, contacts_manager, tox_id=''): + super().__init__() + self._contacts_manager = contacts_manager self.initUI(tox_id) self._adding = False self.setAttribute(QtCore.Qt.WA_DeleteOnClose) @@ -61,8 +63,10 @@ class AddContact(CenteredWidget): if self._adding: return self._adding = True - profile = Profile.get_instance() - send = profile.send_friend_request(self.tox_id.text().strip(), self.message_edit.toPlainText()) + tox_id = self.tox_id.text().strip() + if tox_id.startswith('tox:'): + tox_id = tox_id[4:] + send = self._contacts_manager.send_friend_request(tox_id, self.message_edit.toPlainText()) self._adding = False if send is True: # request was successful @@ -71,17 +75,18 @@ class AddContact(CenteredWidget): self.error_label.setText(send) def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate('AddContact', "Add contact")) - self.sendRequestButton.setText(QtWidgets.QApplication.translate("Form", "Send request")) - self.label.setText(QtWidgets.QApplication.translate('AddContact', "TOX ID:")) - self.message.setText(QtWidgets.QApplication.translate('AddContact', "Message:")) - self.tox_id.setPlaceholderText(QtWidgets.QApplication.translate('AddContact', "TOX ID or public key of contact")) + self.setWindowTitle(util_ui.tr('Add contact')) + self.sendRequestButton.setText(util_ui.tr('Send request')) + self.label.setText(util_ui.tr('TOX ID:')) + self.message.setText(util_ui.tr('Message:')) + self.tox_id.setPlaceholderText(util_ui.tr('TOX ID or public key of contact')) class ProfileSettings(CenteredWidget): """Form with profile settings such as name, status, TOX ID""" - def __init__(self): - super(ProfileSettings, self).__init__() + def __init__(self, profile): + super().__init__() + self._profile = profile self.initUI() self.center() @@ -91,13 +96,12 @@ class ProfileSettings(CenteredWidget): self.setMaximumSize(QtCore.QSize(700, 600)) self.nick = LineEdit(self) self.nick.setGeometry(QtCore.QRect(30, 60, 350, 27)) - profile = Profile.get_instance() - self.nick.setText(profile.name) + self.nick.setText(self._profile.name) self.status = QtWidgets.QComboBox(self) self.status.setGeometry(QtCore.QRect(400, 60, 200, 27)) self.status_message = LineEdit(self) self.status_message.setGeometry(QtCore.QRect(30, 130, 350, 27)) - self.status_message.setText(profile.status_message) + self.status_message.setText(self._profile.status_message) self.label = QtWidgets.QLabel(self) self.label.setGeometry(QtCore.QRect(40, 30, 91, 25)) font = QtGui.QFont() @@ -164,37 +168,37 @@ class ProfileSettings(CenteredWidget): self.auto = path + name == ProfileManager.get_path() + Settings.get_instance().name self.default.clicked.connect(self.auto_profile) self.retranslateUi() - if profile.status is not None: - self.status.setCurrentIndex(profile.status) + if self._profile.status is not None: + self.status.setCurrentIndex(self._profile.status) else: self.status.setVisible(False) QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): - self.export.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Export profile")) - self.setWindowTitle(QtWidgets.QApplication.translate("ProfileSettingsForm", "Profile settings")) - self.label.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Name:")) - self.label_2.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Status:")) - self.label_3.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "TOX ID:")) - self.copyId.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Copy TOX ID")) - self.new_avatar.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "New avatar")) - self.delete_avatar.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Reset avatar")) - self.new_nospam.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "New NoSpam")) - self.profilepass.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Profile password")) - self.password.setPlaceholderText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Password (at least 8 symbols)")) - self.confirm_password.setPlaceholderText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Confirm password")) - self.set_password.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Set password")) - self.not_match.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Passwords do not match")) - self.leave_blank.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Leaving blank will reset current password")) - self.warning.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "There is no way to recover lost passwords")) - self.status.addItem(QtWidgets.QApplication.translate("ProfileSettingsForm", "Online")) - self.status.addItem(QtWidgets.QApplication.translate("ProfileSettingsForm", "Away")) - self.status.addItem(QtWidgets.QApplication.translate("ProfileSettingsForm", "Busy")) - self.copy_pk.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Copy public key")) + self.export.setText(util_ui.tr("Export profile")) + self.setWindowTitle(util_ui.tr("Profile settings")) + self.label.setText(util_ui.tr("Name:")) + self.label_2.setText(util_ui.tr("Status:")) + self.label_3.setText(util_ui.tr("TOX ID:")) + self.copyId.setText(util_ui.tr("Copy TOX ID")) + self.new_avatar.setText(util_ui.tr("New avatar")) + self.delete_avatar.setText(util_ui.tr("Reset avatar")) + self.new_nospam.setText(util_ui.tr("New NoSpam")) + self.profilepass.setText(util_ui.tr("Profile password")) + self.password.setPlaceholderText(util_ui.tr("Password (at least 8 symbols)")) + self.confirm_password.setPlaceholderText(util_ui.tr("Confirm password")) + self.set_password.setText(util_ui.tr("Set password")) + self.not_match.setText(util_ui.tr("Passwords do not match")) + self.leave_blank.setText(util_ui.tr("Leaving blank will reset current password")) + self.warning.setText(util_ui.tr("There is no way to recover lost passwords")) + self.status.addItem(util_ui.tr("Online")) + self.status.addItem(util_ui.tr("Away")) + self.status.addItem(util_ui.tr("Busy")) + self.copy_pk.setText(util_ui.tr("Copy public key")) if self.auto: - self.default.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as not default profile")) + self.default.setText(util_ui.tr("Mark as not default profile")) else: - self.default.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as default profile")) + self.default.setText(util_ui.tr("Mark as default profile")) def auto_profile(self): if self.auto: @@ -203,10 +207,10 @@ class ProfileSettings(CenteredWidget): Settings.set_auto_profile(ProfileManager.get_path(), Settings.get_instance().name) self.auto = not self.auto if self.auto: - self.default.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as not default profile")) + self.default.setText(util_ui.tr("Mark as not default profile")) else: self.default.setText( - QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as default profile")) + util_ui.tr("Mark as default profile")) def new_password(self): if self.password.text() == self.confirm_password.text(): @@ -216,10 +220,10 @@ class ProfileSettings(CenteredWidget): self.close() else: self.not_match.setText( - QtWidgets.QApplication.translate("ProfileSettingsForm", "Password must be at least 8 symbols")) + util_ui.tr("Password must be at least 8 symbols")) self.not_match.setVisible(True) else: - self.not_match.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Passwords do not match")) + self.not_match.setText(util_ui.tr("Passwords do not match")) self.not_match.setVisible(True) def copy(self): @@ -244,10 +248,10 @@ class ProfileSettings(CenteredWidget): self.tox_id.setText(Profile.get_instance().new_nospam()) def reset_avatar(self): - Profile.get_instance().reset_avatar() + self._profile.reset_avatar() def set_avatar(self): - choose = QtWidgets.QApplication.translate("ProfileSettingsForm", "Choose avatar") + choose = util_ui.tr("Choose avatar") name = QtWidgets.QFileDialog.getOpenFileName(self, choose, None, 'Images (*.png)', options=QtWidgets.QFileDialog.DontUseNativeDialog) if name[0]: @@ -262,21 +266,15 @@ class ProfileSettings(CenteredWidget): Profile.get_instance().set_avatar(bytes(byte_array.data())) def export_profile(self): - directory = QtWidgets.QFileDialog.getExistingDirectory(self, '', curr_directory(), - QtWidgets.QFileDialog.DontUseNativeDialog) + '/' + directory = util_ui.directory_dialog() + '/' if directory != '/': - reply = QtWidgets.QMessageBox.question(None, - QtWidgets.QApplication.translate("ProfileSettingsForm", - 'Use new path'), - QtWidgets.QApplication.translate("ProfileSettingsForm", - 'Do you want to move your profile to this location?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) + reply = util_ui.question(util_ui.tr('Do you want to move your profile to this location?'), + util_ui.tr('Use new path')) settings = Settings.get_instance() settings.export(directory) profile = Profile.get_instance() profile.export_db(directory) - ProfileManager.get_instance().export_profile(directory, reply == QtWidgets.QMessageBox.Yes) + ProfileManager.get_instance().export_profile(directory, reply) def closeEvent(self, event): profile = Profile.get_instance() @@ -287,8 +285,9 @@ class ProfileSettings(CenteredWidget): class NetworkSettings(CenteredWidget): """Network settings form: UDP, Ipv6 and proxy""" - def __init__(self, reset): - super(NetworkSettings, self).__init__() + def __init__(self, settings, reset): + super().__init__() + self._settings = settings self.reset = reset self.initUI() self.center() @@ -323,35 +322,34 @@ class NetworkSettings(CenteredWidget): self.reconnect = QtWidgets.QPushButton(self) self.reconnect.setGeometry(QtCore.QRect(40, 230, 231, 30)) self.reconnect.clicked.connect(self.restart_core) - settings = Settings.get_instance() - self.ipv.setChecked(settings['ipv6_enabled']) - self.udp.setChecked(settings['udp_enabled']) - self.proxy.setChecked(settings['proxy_type']) - self.proxyip.setText(settings['proxy_host']) - self.proxyport.setText(str(settings['proxy_port'])) - self.http.setChecked(settings['proxy_type'] == 1) + self.ipv.setChecked(self._settings['ipv6_enabled']) + self.udp.setChecked(self._settings['udp_enabled']) + self.proxy.setChecked(self._settings['proxy_type']) + self.proxyip.setText(self._settings['proxy_host']) + self.proxyport.setText(str(self._settings['proxy_port'])) + self.http.setChecked(self._settings['proxy_type'] == 1) self.warning = QtWidgets.QLabel(self) self.warning.setGeometry(QtCore.QRect(5, 270, 290, 60)) self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') self.nodes = QtWidgets.QCheckBox(self) self.nodes.setGeometry(QtCore.QRect(20, 350, 270, 22)) - self.nodes.setChecked(settings['download_nodes_list']) + self.nodes.setChecked(self._settings['download_nodes_list']) self.retranslateUi() self.proxy.stateChanged.connect(lambda x: self.activate()) self.activate() QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("NetworkSettings", "Network settings")) - self.ipv.setText(QtWidgets.QApplication.translate("Form", "IPv6")) - self.udp.setText(QtWidgets.QApplication.translate("Form", "UDP")) - self.proxy.setText(QtWidgets.QApplication.translate("Form", "Proxy")) - self.label.setText(QtWidgets.QApplication.translate("Form", "IP:")) - self.label_2.setText(QtWidgets.QApplication.translate("Form", "Port:")) - self.reconnect.setText(QtWidgets.QApplication.translate("NetworkSettings", "Restart TOX core")) - self.http.setText(QtWidgets.QApplication.translate("Form", "HTTP")) - self.nodes.setText(QtWidgets.QApplication.translate("Form", "Download nodes list from tox.chat")) - self.warning.setText(QtWidgets.QApplication.translate("Form", "WARNING:\nusing proxy with enabled UDP\ncan produce IP leak")) + self.setWindowTitle(util_ui.tr("Network settings")) + self.ipv.setText(util_ui.tr("IPv6")) + self.udp.setText(util_ui.tr("UDP")) + self.proxy.setText(util_ui.tr("Proxy")) + self.label.setText(util_ui.tr("IP:")) + self.label_2.setText(util_ui.tr("Port:")) + self.reconnect.setText(util_ui.tr("Restart TOX core")) + self.http.setText(util_ui.tr("HTTP")) + self.nodes.setText(util_ui.tr("Download nodes list from tox.chat")) + self.warning.setText(util_ui.tr("WARNING:\nusing proxy with enabled UDP\ncan produce IP leak")) def activate(self): bl = self.proxy.isChecked() @@ -361,14 +359,13 @@ class NetworkSettings(CenteredWidget): def restart_core(self): try: - settings = Settings.get_instance() - settings['ipv6_enabled'] = self.ipv.isChecked() - settings['udp_enabled'] = self.udp.isChecked() - settings['proxy_type'] = 2 - int(self.http.isChecked()) if self.proxy.isChecked() else 0 - settings['proxy_host'] = str(self.proxyip.text()) - settings['proxy_port'] = int(self.proxyport.text()) - settings['download_nodes_list'] = self.nodes.isChecked() - settings.save() + self._settings['ipv6_enabled'] = self.ipv.isChecked() + self._settings['udp_enabled'] = self.udp.isChecked() + self._settings['proxy_type'] = 2 - int(self.http.isChecked()) if self.proxy.isChecked() else 0 + self._settings['proxy_host'] = str(self.proxyip.text()) + self._settings['proxy_port'] = int(self.proxyport.text()) + self._settings['download_nodes_list'] = self.nodes.isChecked() + self._settings.save() # recreate tox instance Profile.get_instance().reset(self.reset) self.close() @@ -380,7 +377,7 @@ class PrivacySettings(CenteredWidget): """Privacy settings form: history, typing notifications""" def __init__(self): - super(PrivacySettings, self).__init__() + super().__init__() self.initUI() self.center() diff --git a/toxygen/ui/widgets.py b/toxygen/ui/widgets.py index aab027a..735ad39 100644 --- a/toxygen/ui/widgets.py +++ b/toxygen/ui/widgets.py @@ -79,7 +79,7 @@ class QRightClickButton(QtWidgets.QPushButton): class RubberBand(QtWidgets.QRubberBand): def __init__(self): - super(RubberBand, self).__init__(QtWidgets.QRubberBand.Rectangle, None) + super().__init__(QtWidgets.QRubberBand.Rectangle, None) self.setPalette(QtGui.QPalette(QtCore.Qt.transparent)) self.pen = QtGui.QPen(QtCore.Qt.blue, 4) self.pen.setStyle(QtCore.Qt.SolidLine) diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py new file mode 100644 index 0000000..60ae8c3 --- /dev/null +++ b/toxygen/ui/widgets_factory.py @@ -0,0 +1,27 @@ +from ui.main_screen_widgets import * +from ui.menu import * + + +class WidgetsFactory: + + def __init__(self, settings, profile, contacts_manager, file_transfer_handler, smiley_loader): + self._settings = settings + self._profile = profile + self._contacts_manager = contacts_manager + self._file_transfer_handler = file_transfer_handler + self._smiley_loader = smiley_loader + + def create_screenshot_window(self, *args): + return ScreenShotWindow(self._file_transfer_handler, *args) + + def create_smiley_window(self, parent): + return SmileyWindow(parent, self._smiley_loader) + + def create_welcome_window(self): + return WelcomeScreen(self._settings) + + def create_profile_settings_window(self): + return ProfileSettings(self._profile) + + def create_network_settings_window(self): + return NetworkSettings(self._settings, self._profile.reset) diff --git a/toxygen/util/ui.py b/toxygen/util/ui.py index 4b9e806..a117202 100644 --- a/toxygen/util/ui.py +++ b/toxygen/util/ui.py @@ -1,4 +1,5 @@ from PyQt5 import QtWidgets +import util.util as util def tr(s): @@ -25,4 +26,9 @@ def text_dialog(text, title='', default_value=''): return text, ok +def directory_dialog(caption=''): + return QtWidgets.QFileDialog.getExistingDirectory(None, caption, util.curr_directory(), + QtWidgets.QFileDialog.DontUseNativeDialog) + + # TODO: move all dialogs here From 5ebfa702ec06136431e1a8ead6fd6b9a800f9bbd Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 30 Apr 2018 00:33:25 +0300 Subject: [PATCH 012/217] screens creation improvements. bug fixes --- toxygen/app.py | 10 +- toxygen/bootstrap/bootstrap.py | 21 +- toxygen/contacts/profile.py | 11 +- toxygen/middleware/callbacks.py | 36 +-- toxygen/middleware/threads.py | 3 +- toxygen/smileys/smileys.py | 4 +- toxygen/ui/main_screen.py | 131 +++++----- toxygen/ui/menu.py | 424 ++++++++++++++------------------ toxygen/ui/widgets_factory.py | 31 ++- toxygen/util/ui.py | 17 +- toxygen/util/util.py | 11 +- 11 files changed, 338 insertions(+), 361 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 423ae5f..17c760e 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -23,6 +23,8 @@ from contacts.friend_factory import FriendFactory from contacts.contacts_manager import ContactsManager from av.calls_manager import CallsManager from history.database import Database +from ui.widgets_factory import WidgetsFactory +from smileys.smileys import SmileyLoader class App: @@ -148,16 +150,20 @@ class App: return self._tox def create_dependencies(self): - self._ms = MainWindow(self._settings, self._tox, self.reset, self._tray) + self._ms = MainWindow(self._settings, self._tox, self._tray) db = Database(self._path.replace('.tox', '.db'), self._toxes) self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db) self._contacts_provider = ContactProvider(self._tox, self._friend_factory) + profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler) + self._smiley_loader = SmileyLoader(self._settings) + widgets_factory = WidgetsFactory(self._settings, profile, self._contacts_manager, self._file_transfer_handler, + self._smiley_loader, self._plugin_loader) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, self._contacts_provider, db) self._calls_manager = CallsManager(self._tox.AV, self._settings) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) - profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler) self._ms.profile = profile + self._ms.set_widget_factory(widgets_factory) self._ms.show() self._tray = tray.init_tray(profile, self._settings, self._ms) diff --git a/toxygen/bootstrap/bootstrap.py b/toxygen/bootstrap/bootstrap.py index aa6f863..ccce1c7 100644 --- a/toxygen/bootstrap/bootstrap.py +++ b/toxygen/bootstrap/bootstrap.py @@ -1,11 +1,13 @@ import random import urllib.request -from util.util import log, curr_directory -from user_data import settings +from util.util import log, curr_directory, join_path from PyQt5 import QtNetwork, QtCore import json +DEFAULT_NODES_COUNT = 4 + + class Node: def __init__(self, node): @@ -21,11 +23,18 @@ class Node: return bytes(self._ip, 'utf-8'), self._port, self._tox_key -def generate_nodes(): - with open(curr_directory() + '/nodes.json', 'rt') as fl: +def _get_nodes_path(): + return join_path(curr_directory(__file__), 'nodes.json') + + +def generate_nodes(nodes_count=DEFAULT_NODES_COUNT): + with open(_get_nodes_path(), 'rt') as fl: json_nodes = json.loads(fl.read())['nodes'] nodes = map(lambda json_node: Node(json_node), json_nodes) - sorted_nodes = sorted(nodes, key=lambda x: x.priority)[-4:] + nodes = filter(lambda n: n.priority > 0, nodes) + sorted_nodes = sorted(nodes, key=lambda x: x.priority) + if nodes_count is not None: + sorted_nodes = sorted_nodes[-DEFAULT_NODES_COUNT:] for node in sorted_nodes: yield node.get_data() @@ -34,7 +43,7 @@ def save_nodes(nodes): if not nodes: return print('Saving nodes...') - with open(curr_directory() + '/nodes.json', 'wb') as fl: + with open(_get_nodes_path(), 'wb') as fl: fl.write(nodes) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 70a6f7e..642cc04 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -1,21 +1,12 @@ -from ui.list_items import * -from PyQt5 import QtWidgets from contacts.friend import * from user_data.settings import * from wrapper.toxcore_enums_and_consts import * -from util.util import log, curr_directory -from network.tox_dns import tox_dns +from util.util import log from history.database import * from file_transfers.file_transfers import * import time -from av import calls -import plugin_support from contacts import basecontact -from ui import items_factory, av_widgets -import cv2 -import threading from contacts.group_chat import * -import re import util.ui as util_ui diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 153f341..e139883 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -40,62 +40,62 @@ def self_connection_status(tox, profile): def friend_status(profile, settings): - def wrapped(tox, friend_num, new_status, user_data): + def wrapped(tox, friend_number, new_status, user_data): """ Check friend's status (none, busy, away) """ - print("Friend's #{} status changed!".format(friend_num)) - friend = profile.get_friend_by_number(friend_num) + print("Friend's #{} status changed!".format(friend_number)) + friend = profile.get_friend_by_number(friend_number) if friend.status is None and settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) invoke_in_main_thread(friend.set_status, new_status) - invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: profile.send_files(friend_num)) + invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: profile.send_files(friend_number)) invoke_in_main_thread(profile.update_filtration) return wrapped def friend_connection_status(profile, settings, plugin_loader): - def wrapped(tox, friend_num, new_status, user_data): + def wrapped(tox, friend_number, new_status, user_data): """ Check friend's connection status (offline, udp, tcp) """ - print("Friend #{} connection status: {}".format(friend_num, new_status)) - friend = profile.get_friend_by_number(friend_num) + print("Friend #{} connection status: {}".format(friend_number, new_status)) + friend = profile.get_friend_by_number(friend_number) if new_status == TOX_CONNECTION['NONE']: - invoke_in_main_thread(profile.friend_exit, friend_num) + invoke_in_main_thread(profile.friend_exit, friend_number) invoke_in_main_thread(profile.update_filtration) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) elif friend.status is None: - invoke_in_main_thread(profile.send_avatar, friend_num) - invoke_in_main_thread(plugin_loader.friend_online, friend_num) + invoke_in_main_thread(profile.send_avatar, friend_number) + invoke_in_main_thread(plugin_loader.friend_online, friend_number) return wrapped def friend_name(profile): - def wrapped(tox, friend_num, name, size, user_data): + def wrapped(tox, friend_number, name, size, user_data): """ Friend changed his name """ - print('New name friend #' + str(friend_num)) - invoke_in_main_thread(profile.new_name, friend_num, name) + print('New name friend #' + str(friend_number)) + invoke_in_main_thread(profile.new_name, friend_number, name) return wrapped def friend_status_message(profile): - def wrapped(tox, friend_num, status_message, size, user_data): + def wrapped(tox, friend_number, status_message, size, user_data): """ :return: function for callback friend_status_message. It updates friend's status message and calls window repaint """ - friend = profile.get_friend_by_number(friend_num) + friend = profile.get_friend_by_number(friend_number) invoke_in_main_thread(friend.set_status_message, status_message) - print('User #{} has new status'.format(friend_num)) - invoke_in_main_thread(profile.send_messages, friend_num) - if profile.get_active_number() == friend_num: + print('User #{} has new status'.format(friend_number)) + invoke_in_main_thread(profile.send_messages, friend_number) + if profile.get_active_number() == friend_number: invoke_in_main_thread(profile.set_active) return wrapped diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py index 5d722f0..e654a66 100644 --- a/toxygen/middleware/threads.py +++ b/toxygen/middleware/threads.py @@ -42,7 +42,7 @@ class InitThread(BaseThread): time.sleep(1) while not self._tox.self_get_connection_status(): try: - for data in generate_nodes(): + for data in generate_nodes(None): if self._stop_thread: return self._tox.bootstrap(*data) @@ -137,4 +137,3 @@ _invoker = Invoker() def invoke_in_main_thread(fn, *args, **kwargs): QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs)) - diff --git a/toxygen/smileys/smileys.py b/toxygen/smileys/smileys.py index c20d1a7..fc40a69 100644 --- a/toxygen/smileys/smileys.py +++ b/toxygen/smileys/smileys.py @@ -48,8 +48,8 @@ class SmileyLoader: return util.join_path(util.get_smileys_directory(), self._curr_pack) if self._curr_pack is not None else None @staticmethod - def get_packs_list(self): - d = util.curr_directory() + '/smileys/' + def get_packs_list(): + d = util.get_smileys_directory() return [x[1] for x in os.walk(d)][0] def get_smileys(self): diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 73f4d4e..41c7c38 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -2,27 +2,32 @@ from ui.menu import * from contacts.profile import * from ui.list_items import * from ui.widgets import MultilineEdit, ComboBox -import plugin_support from ui.main_screen_widgets import * -from user_data import toxes, settings import util.util as util import util.ui as util_ui class MainWindow(QtWidgets.QMainWindow): - def __init__(self, settings, tox, reset, tray): + def __init__(self, settings, tox, tray): super().__init__() self._settings = settings - self.reset = reset self.tray = tray + self._widget_factory = None + self._modal_window = None self.setAcceptDrops(True) self.initUI(tox) self._saved = False - if settings['show_welcome_screen']: - self.ws = WelcomeScreen() self.profile = None + def set_widget_factory(self, widget_factory): + self._widget_factory = widget_factory + + def show(self): + super().show() + if self._settings['show_welcome_screen']: + self._modal_window = self._widget_factory.create_welcome_window() + def setup_menu(self, window): self.menubar = QtWidgets.QMenuBar(window) self.menubar.setObjectName("menubar") @@ -366,7 +371,7 @@ class MainWindow(QtWidgets.QMainWindow): self._settings['width'] = self.width() self._settings['height'] = self.height() self._settings.save() - QtWidgets.QApplication.closeAllWindows() + util_ui.close_all_windows() event.accept() elif QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): event.ignore() @@ -407,7 +412,7 @@ class MainWindow(QtWidgets.QMainWindow): elif event.key() == QtCore.Qt.Key_F and event.modifiers() & QtCore.Qt.ControlModifier: self.show_search_field() else: - super(MainWindow, self).keyPressEvent(event) + super().keyPressEvent(event) # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user click in menu @@ -420,81 +425,66 @@ class MainWindow(QtWidgets.QMainWindow): util_ui.message_box(text, title) def network_settings(self): - self.n_s = NetworkSettings(self.reset) - self.n_s.show() + self._modal_window = self._widget_factory.create_network_settings_window() + self._modal_window.show() def plugins_menu(self): - self.p_s = PluginsSettings() - self.p_s.show() + self._modal_window = self._widget_factory.create_plugins_settings_window() + self._modal_window.show() def add_contact(self, link=''): - self.a_c = AddContact(link or '') - self.a_c.show() + self._modal_window = self._widget_factory.create_add_contact_window(link or '') + self._modal_window.show() def create_gc(self): self.profile.create_group_chat() def profile_settings(self, *args): - self.p_s = ProfileSettings() - self.p_s.show() + self._modal_window = self._widget_factory.create_profile_settings_window() + self._modal_window.show() def privacy_settings(self): - self.priv_s = PrivacySettings() - self.priv_s.show() + self._modal_window = self._widget_factory.create_privacy_settings_window() + self._modal_window.show() def notification_settings(self): - self.notif_s = NotificationsSettings() - self.notif_s.show() + self._modal_window = self._widget_factory.create_notification_settings_window() + self._modal_window.show() def interface_settings(self): - self.int_s = InterfaceSettings() - self.int_s.show() + self._modal_window = self._widget_factory.create_interface_settings_window() + self._modal_window.show() def audio_settings(self): - self.audio_s = AudioSettings() - self.audio_s.show() + self._modal_window = self._widget_factory.create_audio_settings_window() + self._modal_window.show() def video_settings(self): - self.video_s = VideoSettings() - self.video_s.show() + self._modal_window = self._widget_factory.create_video_settings_window() + self._modal_window.show() def update_settings(self): - self.update_s = UpdateSettings() - self.update_s.show() + self._modal_window = self._widget_factory.create_update_settings_window() + self._modal_window.show() def reload_plugins(self): - plugin_loader = plugin_support.PluginLoader.get_instance() - if plugin_loader is not None: - plugin_loader.reload() + if self._plugin_loader is not None: + self._plugin_loader.reload() def import_plugin(self): - import util - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - util_ui.tr('Choose folder with plugin'), - util.curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) + directory = util_ui.directory_dialog(util_ui.tr('Choose folder with plugin')) if directory: src = directory + '/' - dest = curr_directory() + '/plugins/' + dest = util.get_plugins_directory() util.copy(src, dest) - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - util_ui.tr("Restart Toxygen")) - msgBox.setText( - util_ui.tr('Plugin will be loaded after restart')) - msgBox.exec_() + util_ui.message_box(util_ui.tr('Plugin will be loaded after restart'), util_ui.tr("Restart Toxygen")) def lock_app(self): - if toxes.ToxES.get_instance().has_password(): - Settings.get_instance().locked = True + if self._toxes.has_password(): + self._settings.locked = True self.hide() else: - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - util_ui.tr("Cannot lock app")) - msgBox.setText( - util_ui.tr('Error. Profile password is not set.')) - msgBox.exec_() + util_ui.message_box(util_ui.tr('Error. Profile password is not set.'), util_ui.tr("Cannot lock app")) def show_menu(self): if not hasattr(self, 'menu'): @@ -516,8 +506,8 @@ class MainWindow(QtWidgets.QMainWindow): def send_file(self): self.menu.hide() if self.profile.active_friend + 1and self.profile.is_active_a_friend(): - choose = util_ui.tr('Choose file') - name = QtWidgets.QFileDialog.getOpenFileName(self, choose, options=QtWidgets.QFileDialog.DontUseNativeDialog) + caption = util_ui.tr('Choose file') + name = util_ui.file_dialog(caption) if name[0]: self.profile.send_file(name[0]) @@ -533,7 +523,7 @@ class MainWindow(QtWidgets.QMainWindow): self.menu.hide() if self.profile.active_friend + 1: self.smiley = SmileyWindow(self) - self.smiley.setGeometry(QtCore.QRect(self.x() if Settings.get_instance()['mirror_mode'] else 270 + self.x(), + self.smiley.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), self.y() + self.height() - 200, self.smiley.width(), self.smiley.height())) @@ -543,7 +533,7 @@ class MainWindow(QtWidgets.QMainWindow): self.menu.hide() if self.profile.active_friend + 1 and self.profile.is_active_a_friend(): self.sticker = StickerWindow(self) - self.sticker.setGeometry(QtCore.QRect(self.x() if Settings.get_instance()['mirror_mode'] else 270 + self.x(), + self.sticker.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), self.y() + self.height() - 200, self.sticker.width(), self.sticker.height())) @@ -580,7 +570,6 @@ class MainWindow(QtWidgets.QMainWindow): friend = Profile.get_instance().get_friend(num) if friend is None: return - settings = Settings.get_instance() allowed = friend.tox_id in settings['auto_accept_from_friends'] auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') if item is not None: @@ -614,9 +603,8 @@ class MainWindow(QtWidgets.QMainWindow): item = invite_menu.addAction(name) item.triggered.connect(lambda: self.invite_friend_to_gc(num, number)) - plugins_loader = plugin_support.PluginLoader.get_instance() - if plugins_loader is not None: - submenu = plugins_loader.get_menu(self.listMenu, num) + if self._plugins_loader is not None: + submenu = self._plugins_loader.get_menu(self.listMenu, num) if len(submenu): plug = self.listMenu.addMenu(util_ui.tr('Plugins')) plug.addActions(submenu) @@ -640,29 +628,23 @@ class MainWindow(QtWidgets.QMainWindow): self.listMenu.show() def show_note(self, friend): - s = Settings.get_instance() - note = s['notes'][friend.tox_id] if friend.tox_id in s['notes'] else '' + note = self._settings['notes'][friend.tox_id] if friend.tox_id in s['notes'] else '' user = util_ui.tr('Notes about user') user = '{} {}'.format(user, friend.name) def save_note(text): - if friend.tox_id in s['notes']: - del s['notes'][friend.tox_id] + if friend.tox_id in self._settings['notes']: + del self._settings['notes'][friend.tox_id] if text: - s['notes'][friend.tox_id] = text - s.save() + self._settings['notes'][friend.tox_id] = text + self._settings.save() self.note = MultilineEdit(user, note, save_note) self.note.show() def export_history(self, num, as_text=True): s = self.profile.export_history(num, as_text) extension = 'txt' if as_text else 'html' - file_name, _ = QtWidgets.QFileDialog.getSaveFileName(None, - QtWidgets.QApplication.translate("MainWindow", - 'Choose file name'), - curr_directory(), - filter=extension, - options=QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) + file_name, _ = util_ui.save_file_dialog(util_ui.tr('Choose file name'), extension) if file_name: if not file_name.endswith('.' + extension): @@ -703,13 +685,12 @@ class MainWindow(QtWidgets.QMainWindow): self.profile.set_title(num) def auto_accept(self, num, value): - settings = Settings.get_instance() tox_id = self.profile.friend_public_key(num) if value: - settings['auto_accept_from_friends'].append(tox_id) + self._settings['auto_accept_from_friends'].append(tox_id) else: - settings['auto_accept_from_friends'].remove(tox_id) - settings.save() + self._settings['auto_accept_from_friends'].remove(tox_id) + self._settings.save() def invite_friend_to_gc(self, friend_number, group_number): self.profile.invite_friend(friend_number, group_number) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index c509359..9369fdd 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -5,8 +5,7 @@ from util.util import curr_directory, copy from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio from user_data import toxes -import plugin_support -import updater +import updater.updater as updater import util.ui as util_ui @@ -228,8 +227,7 @@ class ProfileSettings(CenteredWidget): def copy(self): clipboard = QtWidgets.QApplication.clipboard() - profile = Profile.get_instance() - clipboard.setText(profile.tox_id) + clipboard.setText(self._profile.tox_id) pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png') icon = QtGui.QIcon(pixmap) self.copyId.setIcon(icon) @@ -237,8 +235,7 @@ class ProfileSettings(CenteredWidget): def copy_public_key(self): clipboard = QtWidgets.QApplication.clipboard() - profile = Profile.get_instance() - clipboard.setText(profile.tox_id[:64]) + clipboard.setText(self._profile.tox_id[:64]) pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png') icon = QtGui.QIcon(pixmap) self.copy_pk.setIcon(icon) @@ -252,8 +249,7 @@ class ProfileSettings(CenteredWidget): def set_avatar(self): choose = util_ui.tr("Choose avatar") - name = QtWidgets.QFileDialog.getOpenFileName(self, choose, None, 'Images (*.png)', - options=QtWidgets.QFileDialog.DontUseNativeDialog) + name = util_ui.file_dialog(choose, 'Images (*.png)') if name[0]: bitmap = QtGui.QPixmap(name[0]) bitmap.scaled(QtCore.QSize(128, 128), aspectRatioMode=QtCore.Qt.KeepAspectRatio, @@ -263,24 +259,21 @@ class ProfileSettings(CenteredWidget): buffer = QtCore.QBuffer(byte_array) buffer.open(QtCore.QIODevice.WriteOnly) bitmap.save(buffer, 'PNG') - Profile.get_instance().set_avatar(bytes(byte_array.data())) + self._profile.set_avatar(bytes(byte_array.data())) def export_profile(self): directory = util_ui.directory_dialog() + '/' if directory != '/': reply = util_ui.question(util_ui.tr('Do you want to move your profile to this location?'), util_ui.tr('Use new path')) - settings = Settings.get_instance() settings.export(directory) - profile = Profile.get_instance() - profile.export_db(directory) + self._profile.export_db(directory) ProfileManager.get_instance().export_profile(directory, reply) def closeEvent(self, event): - profile = Profile.get_instance() - profile.set_name(self.nick.text()) - profile.set_status_message(self.status_message.text().encode('utf-8')) - profile.set_status(self.status.currentIndex()) + self._profile.set_name(self.nick.text()) + self._profile.set_status_message(self.status_message.text().encode('utf-8')) + self._profile.set_status(self.status.currentIndex()) class NetworkSettings(CenteredWidget): @@ -367,7 +360,7 @@ class NetworkSettings(CenteredWidget): self._settings['download_nodes_list'] = self.nodes.isChecked() self._settings.save() # recreate tox instance - Profile.get_instance().reset(self.reset) + self._profile.reset() self.close() except Exception as ex: log('Exception in restart: ' + str(ex)) @@ -376,8 +369,13 @@ class NetworkSettings(CenteredWidget): class PrivacySettings(CenteredWidget): """Privacy settings form: history, typing notifications""" - def __init__(self): + def __init__(self, contacts_manager, settings): + """ + :type contacts_manager: ContactsManager + """ super().__init__() + self._contacts_manager = contacts_manager + self._settings = settings self.initUI() self.center() @@ -404,15 +402,14 @@ class PrivacySettings(CenteredWidget): self.path.setGeometry(QtCore.QRect(10, 265, 350, 45)) self.change_path = QtWidgets.QPushButton(self) self.change_path.setGeometry(QtCore.QRect(10, 320, 350, 30)) - settings = Settings.get_instance() - self.typingNotifications.setChecked(settings['typing_notifications']) - self.fileautoaccept.setChecked(settings['allow_auto_accept']) - self.saveHistory.setChecked(settings['save_history']) - self.inlines.setChecked(settings['allow_inline']) - self.saveUnsentOnly.setChecked(settings['save_unsent_only']) - self.saveUnsentOnly.setEnabled(settings['save_history']) + self.typingNotifications.setChecked(self._settings['typing_notifications']) + self.fileautoaccept.setChecked(self._settings['allow_auto_accept']) + self.saveHistory.setChecked(self._settings['save_history']) + self.inlines.setChecked(self._settings['allow_inline']) + self.saveUnsentOnly.setChecked(self._settings['save_unsent_only']) + self.saveUnsentOnly.setEnabled(self._settings['save_history']) self.saveHistory.stateChanged.connect(self.update) - self.path.setPlainText(settings['auto_accept_path'] or curr_directory()) + self.path.setPlainText(self._settings['auto_accept_path'] or curr_directory()) self.change_path.clicked.connect(self.new_path) self.block_user_label = QtWidgets.QLabel(self) self.block_user_label.setGeometry(QtCore.QRect(10, 360, 350, 30)) @@ -420,12 +417,12 @@ class PrivacySettings(CenteredWidget): self.block_id.setGeometry(QtCore.QRect(10, 390, 350, 30)) self.block = QtWidgets.QPushButton(self) self.block.setGeometry(QtCore.QRect(10, 430, 350, 30)) - self.block.clicked.connect(lambda: Profile.get_instance().block_user(self.block_id.toPlainText()) or self.close()) + self.block.clicked.connect(lambda: self._contacts_manager.block_user(self.block_id.toPlainText()) or self.close()) self.blocked_users_label = QtWidgets.QLabel(self) self.blocked_users_label.setGeometry(QtCore.QRect(10, 470, 350, 30)) self.comboBox = QtWidgets.QComboBox(self) self.comboBox.setGeometry(QtCore.QRect(10, 500, 350, 30)) - self.comboBox.addItems(settings['blocked']) + self.comboBox.addItems(self._settings['blocked']) self.unblock = QtWidgets.QPushButton(self) self.unblock.setGeometry(QtCore.QRect(10, 540, 350, 30)) self.unblock.clicked.connect(lambda: self.unblock_user()) @@ -433,18 +430,18 @@ class PrivacySettings(CenteredWidget): QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("privacySettings", "Privacy settings")) - self.saveHistory.setText(QtWidgets.QApplication.translate("privacySettings", "Save chat history")) - self.fileautoaccept.setText(QtWidgets.QApplication.translate("privacySettings", "Allow file auto accept")) - self.typingNotifications.setText(QtWidgets.QApplication.translate("privacySettings", "Send typing notifications")) - self.auto_path.setText(QtWidgets.QApplication.translate("privacySettings", "Auto accept default path:")) - self.change_path.setText(QtWidgets.QApplication.translate("privacySettings", "Change")) - self.inlines.setText(QtWidgets.QApplication.translate("privacySettings", "Allow inlines")) - self.block_user_label.setText(QtWidgets.QApplication.translate("privacySettings", "Block by public key:")) - self.blocked_users_label.setText(QtWidgets.QApplication.translate("privacySettings", "Blocked users:")) - self.unblock.setText(QtWidgets.QApplication.translate("privacySettings", "Unblock")) - self.block.setText(QtWidgets.QApplication.translate("privacySettings", "Block user")) - self.saveUnsentOnly.setText(QtWidgets.QApplication.translate("privacySettings", "Save unsent messages only")) + self.setWindowTitle(util_ui.tr("Privacy settings")) + self.saveHistory.setText(util_ui.tr("Save chat history")) + self.fileautoaccept.setText(util_ui.tr("Allow file auto accept")) + self.typingNotifications.setText(util_ui.tr("Send typing notifications")) + self.auto_path.setText(util_ui.tr("Auto accept default path:")) + self.change_path.setText(util_ui.tr("Change")) + self.inlines.setText(util_ui.tr("Allow inlines")) + self.block_user_label.setText(util_ui.tr("Block by public key:")) + self.blocked_users_label.setText(util_ui.tr("Blocked users:")) + self.unblock.setText(util_ui.tr("Unblock")) + self.block.setText(util_ui.tr("Block user")) + self.saveUnsentOnly.setText(util_ui.tr("Save unsent messages only")) def update(self, new_state): self.saveUnsentOnly.setEnabled(new_state) @@ -454,58 +451,48 @@ class PrivacySettings(CenteredWidget): def unblock_user(self): if not self.comboBox.count(): return - title = QtWidgets.QApplication.translate("privacySettings", "Add to friend list") - info = QtWidgets.QApplication.translate("privacySettings", "Do you want to add this user to friend list?") - reply = QtWidgets.QMessageBox.question(None, title, info, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) - Profile.get_instance().unblock_user(self.comboBox.currentText(), reply == QtWidgets.QMessageBox.Yes) + title = util_ui.tr("Add to friend list") + info = util_ui.tr("Do you want to add this user to friend list?") + reply = util_ui.question(info, title) + self._contacts_manager.unblock_user(self.comboBox.currentText(), reply) self.close() def closeEvent(self, event): - settings = Settings.get_instance() - settings['typing_notifications'] = self.typingNotifications.isChecked() - settings['allow_auto_accept'] = self.fileautoaccept.isChecked() + self._settings['typing_notifications'] = self.typingNotifications.isChecked() + self._settings['allow_auto_accept'] = self.fileautoaccept.isChecked() + text = util_ui.tr('History will be cleaned! Continue?') + title = util_ui.tr('Chat history') - if settings['save_history'] and not self.saveHistory.isChecked(): # clear history - reply = QtWidgets.QMessageBox.question(None, - QtWidgets.QApplication.translate("privacySettings", - 'Chat history'), - QtWidgets.QApplication.translate("privacySettings", - 'History will be cleaned! Continue?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - Profile.get_instance().clear_history() - settings['save_history'] = self.saveHistory.isChecked() + if self._settings['save_history'] and not self.saveHistory.isChecked(): # clear history + reply = util_ui.question(text, title) + if reply: + self._history_loader.clear_history() + self._settings['save_history'] = self.saveHistory.isChecked() else: - settings['save_history'] = self.saveHistory.isChecked() - if self.saveUnsentOnly.isChecked() and not settings['save_unsent_only']: - reply = QtWidgets.QMessageBox.question(None, - QtWidgets.QApplication.translate("privacySettings", - 'Chat history'), - QtWidgets.QApplication.translate("privacySettings", - 'History will be cleaned! Continue?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - Profile.get_instance().clear_history(None, True) - settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() + self._settings['save_history'] = self.saveHistory.isChecked() + if self.saveUnsentOnly.isChecked() and not self._settings['save_unsent_only']: + reply = util_ui.question(text, title) + if reply: + self._history_loader.clear_history(None, True) + self._settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() else: - settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() - settings['auto_accept_path'] = self.path.toPlainText() - settings['allow_inline'] = self.inlines.isChecked() - settings.save() + self._settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() + self._settings['auto_accept_path'] = self.path.toPlainText() + self._settings['allow_inline'] = self.inlines.isChecked() + self._settings.save() def new_path(self): - directory = QtWidgets.QFileDialog.getExistingDirectory(options=QtWidgets.QFileDialog.DontUseNativeDialog) + '/' - if directory != '/': + directory = util_ui.directory_dialog() + if directory: self.path.setPlainText(directory) class NotificationsSettings(CenteredWidget): """Notifications settings form""" - def __init__(self): - super(NotificationsSettings, self).__init__() + def __init__(self, setttings): + super().__init__() + self._settings = setttings self.initUI() self.center() @@ -523,40 +510,40 @@ class NotificationsSettings(CenteredWidget): self.groupNotifications = QtWidgets.QCheckBox(self) self.groupNotifications.setGeometry(QtCore.QRect(10, 120, 340, 18)) font = QtGui.QFont() - s = Settings.get_instance() - font.setFamily(s['font']) + font.setFamily(self._settings['font']) font.setPointSize(12) self.callsSound.setFont(font) self.soundNotifications.setFont(font) self.enableNotifications.setFont(font) self.groupNotifications.setFont(font) - self.enableNotifications.setChecked(s['notifications']) - self.soundNotifications.setChecked(s['sound_notifications']) - self.groupNotifications.setChecked(s['group_notifications']) - self.callsSound.setChecked(s['calls_sound']) + self.enableNotifications.setChecked(self._settings['notifications']) + self.soundNotifications.setChecked(self._settings['sound_notifications']) + self.groupNotifications.setChecked(self._settings['group_notifications']) + self.callsSound.setChecked(self._settings['calls_sound']) self.retranslateUi() QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("notificationsForm", "Notification settings")) - self.enableNotifications.setText(QtWidgets.QApplication.translate("notificationsForm", "Enable notifications")) - self.groupNotifications.setText(QtWidgets.QApplication.translate("notificationsForm", "Notify about all messages in groups")) - self.callsSound.setText(QtWidgets.QApplication.translate("notificationsForm", "Enable call\'s sound")) - self.soundNotifications.setText(QtWidgets.QApplication.translate("notificationsForm", "Enable sound notifications")) + self.setWindowTitle(util_ui.tr("Notification settings")) + self.enableNotifications.setText(util_ui.tr("Enable notifications")) + self.groupNotifications.setText(util_ui.tr("Notify about all messages in groups")) + self.callsSound.setText(util_ui.tr("Enable call\'s sound")) + self.soundNotifications.setText(util_ui.tr("Enable sound notifications")) def closeEvent(self, *args, **kwargs): - settings = Settings.get_instance() - settings['notifications'] = self.enableNotifications.isChecked() - settings['sound_notifications'] = self.soundNotifications.isChecked() - settings['group_notifications'] = self.groupNotifications.isChecked() - settings['calls_sound'] = self.callsSound.isChecked() - settings.save() + self._settings['notifications'] = self.enableNotifications.isChecked() + self._settings['sound_notifications'] = self.soundNotifications.isChecked() + self._settings['group_notifications'] = self.groupNotifications.isChecked() + self._settings['calls_sound'] = self.callsSound.isChecked() + self._settings.save() class InterfaceSettings(CenteredWidget): """Interface settings form""" - def __init__(self): - super(InterfaceSettings, self).__init__() + def __init__(self, settings, smiley_loader): + super().__init__() + self._settings = settings + self._smiley_loader = smiley_loader self.initUI() self.center() @@ -566,18 +553,17 @@ class InterfaceSettings(CenteredWidget): self.setMaximumSize(QtCore.QSize(400, 650)) self.label = QtWidgets.QLabel(self) self.label.setGeometry(QtCore.QRect(30, 10, 370, 20)) - settings = Settings.get_instance() font = QtGui.QFont() font.setPointSize(14) font.setBold(True) - font.setFamily(settings['font']) + font.setFamily(self._settings['font']) self.label.setFont(font) self.themeSelect = QtWidgets.QComboBox(self) self.themeSelect.setGeometry(QtCore.QRect(30, 40, 120, 30)) - self.themeSelect.addItems(list(settings.built_in_themes().keys())) - theme = settings['theme'] - if theme in settings.built_in_themes().keys(): - index = list(settings.built_in_themes().keys()).index(theme) + self.themeSelect.addItems(list(self._settings.built_in_themes().keys())) + theme = self._settings['theme'] + if theme in self._settings.built_in_themes().keys(): + index = list(self._settings.built_in_themes().keys()).index(theme) else: index = 0 self.themeSelect.setCurrentIndex(index) @@ -586,28 +572,27 @@ class InterfaceSettings(CenteredWidget): supported = sorted(Settings.supported_languages().keys(), reverse=True) for key in supported: self.lang_choose.insertItem(0, key) - if settings['language'] == key: + if self._settings['language'] == key: self.lang_choose.setCurrentIndex(0) self.lang = QtWidgets.QLabel(self) self.lang.setGeometry(QtCore.QRect(30, 80, 370, 20)) self.lang.setFont(font) self.mirror_mode = QtWidgets.QCheckBox(self) self.mirror_mode.setGeometry(QtCore.QRect(30, 160, 370, 20)) - self.mirror_mode.setChecked(settings['mirror_mode']) + self.mirror_mode.setChecked(self._settings['mirror_mode']) self.smileys = QtWidgets.QCheckBox(self) self.smileys.setGeometry(QtCore.QRect(30, 190, 120, 20)) - self.smileys.setChecked(settings['smileys']) + self.smileys.setChecked(self._settings['smileys']) self.smiley_pack_label = QtWidgets.QLabel(self) self.smiley_pack_label.setGeometry(QtCore.QRect(30, 230, 370, 20)) self.smiley_pack_label.setFont(font) self.smiley_pack = QtWidgets.QComboBox(self) self.smiley_pack.setGeometry(QtCore.QRect(30, 260, 160, 30)) - sm = smileys.SmileyLoader.get_instance() - self.smiley_pack.addItems(sm.get_packs_list()) + self.smiley_pack.addItems(self._smiley_loader.get_packs_list()) try: - ind = sm.get_packs_list().index(settings['smiley_pack']) + ind = self._smiley_loader.get_packs_list().index(self._settings['smiley_pack']) except: - ind = sm.get_packs_list().index('default') + ind = self._smiley_loader.get_packs_list().index('default') self.smiley_pack.setCurrentIndex(ind) self.messages_font_size_label = QtWidgets.QLabel(self) self.messages_font_size_label.setGeometry(QtCore.QRect(30, 300, 370, 20)) @@ -615,7 +600,7 @@ class InterfaceSettings(CenteredWidget): self.messages_font_size = QtWidgets.QComboBox(self) self.messages_font_size.setGeometry(QtCore.QRect(30, 330, 160, 30)) self.messages_font_size.addItems([str(x) for x in range(10, 25)]) - self.messages_font_size.setCurrentIndex(settings['message_font_size'] - 10) + self.messages_font_size.setCurrentIndex(self._settings['message_font_size'] - 10) self.unread = QtWidgets.QPushButton(self) self.unread.setGeometry(QtCore.QRect(30, 470, 340, 30)) @@ -623,15 +608,15 @@ class InterfaceSettings(CenteredWidget): self.compact_mode = QtWidgets.QCheckBox(self) self.compact_mode.setGeometry(QtCore.QRect(30, 380, 370, 20)) - self.compact_mode.setChecked(settings['compact_mode']) + self.compact_mode.setChecked(self._settings['compact_mode']) self.close_to_tray = QtWidgets.QCheckBox(self) self.close_to_tray.setGeometry(QtCore.QRect(30, 410, 370, 20)) - self.close_to_tray.setChecked(settings['close_to_tray']) + self.close_to_tray.setChecked(self._settings['close_to_tray']) self.show_avatars = QtWidgets.QCheckBox(self) self.show_avatars.setGeometry(QtCore.QRect(30, 440, 370, 20)) - self.show_avatars.setChecked(settings['show_avatars']) + self.show_avatars.setChecked(self._settings['show_avatars']) self.choose_font = QtWidgets.QPushButton(self) self.choose_font.setGeometry(QtCore.QRect(30, 510, 340, 30)) @@ -649,54 +634,43 @@ class InterfaceSettings(CenteredWidget): QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): - self.show_avatars.setText(QtWidgets.QApplication.translate("interfaceForm", "Show avatars in chat")) - self.setWindowTitle(QtWidgets.QApplication.translate("interfaceForm", "Interface settings")) - self.label.setText(QtWidgets.QApplication.translate("interfaceForm", "Theme:")) - self.lang.setText(QtWidgets.QApplication.translate("interfaceForm", "Language:")) - self.smileys.setText(QtWidgets.QApplication.translate("interfaceForm", "Smileys")) - self.smiley_pack_label.setText(QtWidgets.QApplication.translate("interfaceForm", "Smiley pack:")) - self.mirror_mode.setText(QtWidgets.QApplication.translate("interfaceForm", "Mirror mode")) - self.messages_font_size_label.setText(QtWidgets.QApplication.translate("interfaceForm", "Messages font size:")) - self.unread.setText(QtWidgets.QApplication.translate("interfaceForm", "Select unread messages notification color")) - self.compact_mode.setText(QtWidgets.QApplication.translate("interfaceForm", "Compact contact list")) - self.import_smileys.setText(QtWidgets.QApplication.translate("interfaceForm", "Import smiley pack")) - self.import_stickers.setText(QtWidgets.QApplication.translate("interfaceForm", "Import sticker pack")) - self.close_to_tray.setText(QtWidgets.QApplication.translate("interfaceForm", "Close to tray")) - self.choose_font.setText(QtWidgets.QApplication.translate("interfaceForm", "Select font")) + self.show_avatars.setText(util_ui.tr("Show avatars in chat")) + self.setWindowTitle(util_ui.tr("Interface settings")) + self.label.setText(util_ui.tr("Theme:")) + self.lang.setText(util_ui.tr("Language:")) + self.smileys.setText(util_ui.tr("Smileys")) + self.smiley_pack_label.setText(util_ui.tr("Smiley pack:")) + self.mirror_mode.setText(util_ui.tr("Mirror mode")) + self.messages_font_size_label.setText(util_ui.tr("Messages font size:")) + self.unread.setText(util_ui.tr("Select unread messages notification color")) + self.compact_mode.setText(util_ui.tr("Compact contact list")) + self.import_smileys.setText(util_ui.tr("Import smiley pack")) + self.import_stickers.setText(util_ui.tr("Import sticker pack")) + self.close_to_tray.setText(util_ui.tr("Close to tray")) + self.choose_font.setText(util_ui.tr("Select font")) def import_st(self): - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", - 'Choose folder with sticker pack'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - + directory = util_ui.directory_dialog(util_ui.tr('Choose folder with sticker pack')) if directory: src = directory + '/' dest = curr_directory() + '/stickers/' + os.path.basename(directory) + '/' copy(src, dest) def import_sm(self): - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", - 'Choose folder with smiley pack'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - + directory = util_ui.directory_dialog(util_ui.tr('Choose folder with smiley pack')) if directory: src = directory + '/' dest = curr_directory() + '/smileys/' + os.path.basename(directory) + '/' copy(src, dest) def new_font(self): - settings = Settings.get_instance() - font, ok = QtWidgets.QFontDialog.getFont(QtGui.QFont(settings['font'], 10), self) + font, ok = QtWidgets.QFontDialog.getFont(QtGui.QFont(self._settings['font'], 10), self) if ok: - settings['font'] = font.family() - settings.save() + self._settings['font'] = font.family() + self._settings.save() msgBox = QtWidgets.QMessageBox() - text = QtWidgets.QApplication.translate("interfaceForm", 'Restart app to apply settings') - msgBox.setWindowTitle(QtWidgets.QApplication.translate("interfaceForm", 'Restart required')) + text = util_ui.tr('Restart app to apply settings') + msgBox.setWindowTitle(util_ui.tr('Restart required')) msgBox.setText(text) msgBox.exec_() @@ -747,11 +721,7 @@ class InterfaceSettings(CenteredWidget): Profile.get_instance().update() settings.save() if restart: - msgBox = QtWidgets.QMessageBox() - text = QtWidgets.QApplication.translate("interfaceForm", 'Restart app to apply settings') - msgBox.setWindowTitle(QtWidgets.QApplication.translate("interfaceForm", 'Restart required')) - msgBox.setText(text) - msgBox.exec_() + util_ui.message_box(util_ui.tr('Restart app to apply settings'), util_ui.tr('Restart required')) class AudioSettings(CenteredWidget): @@ -759,8 +729,9 @@ class AudioSettings(CenteredWidget): Audio calls settings form """ - def __init__(self): - super(AudioSettings, self).__init__() + def __init__(self, settings): + super().__init__() + self._settings = settings self.initUI() self.retranslateUi() self.center() @@ -774,11 +745,10 @@ class AudioSettings(CenteredWidget): self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20)) self.out_label = QtWidgets.QLabel(self) self.out_label.setGeometry(QtCore.QRect(25, 65, 350, 20)) - settings = Settings.get_instance() font = QtGui.QFont() font.setPointSize(16) font.setBold(True) - font.setFamily(settings['font']) + font.setFamily(self._settings['font']) self.in_label.setFont(font) self.out_label.setFont(font) self.input = QtWidgets.QComboBox(self) @@ -795,20 +765,19 @@ class AudioSettings(CenteredWidget): if device["maxOutputChannels"]: self.output.addItem(str(device["name"])) self.out_indexes.append(i) - self.input.setCurrentIndex(self.in_indexes.index(settings.audio['input'])) - self.output.setCurrentIndex(self.out_indexes.index(settings.audio['output'])) + self.input.setCurrentIndex(self.in_indexes.index(self._settings.audio['input'])) + self.output.setCurrentIndex(self.out_indexes.index(self._settings.audio['output'])) QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("audioSettingsForm", "Audio settings")) - self.in_label.setText(QtWidgets.QApplication.translate("audioSettingsForm", "Input device:")) - self.out_label.setText(QtWidgets.QApplication.translate("audioSettingsForm", "Output device:")) + self.setWindowTitle(util_ui.tr("Audio settings")) + self.in_label.setText(util_ui.tr("Input device:")) + self.out_label.setText(util_ui.tr("Output device:")) def closeEvent(self, event): - settings = Settings.get_instance() - settings.audio['input'] = self.in_indexes[self.input.currentIndex()] - settings.audio['output'] = self.out_indexes[self.output.currentIndex()] - settings.save() + self._settings.audio['input'] = self.in_indexes[self.input.currentIndex()] + self._settings.audio['output'] = self.out_indexes[self.output.currentIndex()] + self._settings.save() class DesktopAreaSelectionWindow(RubberBandWindow): @@ -828,8 +797,9 @@ class VideoSettings(CenteredWidget): Audio calls settings form """ - def __init__(self): + def __init__(self, settings): super().__init__() + self._settings = settings self.initUI() self.retranslateUi() self.center() @@ -842,11 +812,10 @@ class VideoSettings(CenteredWidget): self.setMaximumSize(QtCore.QSize(400, 120)) self.in_label = QtWidgets.QLabel(self) self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20)) - settings = Settings.get_instance() font = QtGui.QFont() font.setPointSize(16) font.setBold(True) - font.setFamily(settings['font']) + font.setFamily(self._settings['font']) self.in_label.setFont(font) self.video_size = QtWidgets.QComboBox(self) self.video_size.setGeometry(QtCore.QRect(25, 70, 350, 30)) @@ -861,7 +830,7 @@ class VideoSettings(CenteredWidget): screen = QtWidgets.QApplication.primaryScreen() size = screen.size() self.frame_max_sizes = [(size.width(), size.height())] - desktop = QtWidgets.QApplication.translate("videoSettingsForm", "Desktop") + desktop = util_ui.tr("Desktop") self.input.addItem(desktop) for i in range(10): v = cv2.VideoCapture(i) @@ -876,15 +845,15 @@ class VideoSettings(CenteredWidget): self.frame_max_sizes.append((width, height)) self.input.addItem('Device #' + str(i)) try: - index = self.devices.index(settings.video['device']) + index = self.devices.index(self._settings.video['device']) self.input.setCurrentIndex(index) except: print('Video devices error!') def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("videoSettingsForm", "Video settings")) - self.in_label.setText(QtWidgets.QApplication.translate("videoSettingsForm", "Device:")) - self.button.setText(QtWidgets.QApplication.translate("videoSettingsForm", "Select region")) + self.setWindowTitle(util_ui.tr("Video settings")) + self.in_label.setText(util_ui.tr("Device:")) + self.button.setText(util_ui.tr("Select region")) def button_clicked(self): self.desktopAreaSelection = DesktopAreaSelectionWindow(self) @@ -893,24 +862,22 @@ class VideoSettings(CenteredWidget): if self.input.currentIndex() == 0: return try: - settings = Settings.get_instance() - settings.video['device'] = self.devices[self.input.currentIndex()] + self._settings.video['device'] = self.devices[self.input.currentIndex()] text = self.video_size.currentText() - settings.video['width'] = int(text.split(' ')[0]) - settings.video['height'] = int(text.split(' ')[-1]) - settings.save() + self._settings.video['width'] = int(text.split(' ')[0]) + self._settings.video['height'] = int(text.split(' ')[-1]) + self._settings.save() except Exception as ex: print('Saving video settings error: ' + str(ex)) def save(self, x, y, width, height): self.desktopAreaSelection = None - settings = Settings.get_instance() - settings.video['device'] = -1 - settings.video['width'] = width - settings.video['height'] = height - settings.video['x'] = x - settings.video['y'] = y - settings.save() + self._settings.video['device'] = -1 + self._settings.video['width'] = width + self._settings.video['height'] = height + self._settings.video['x'] = x + self._settings.video['y'] = y + self._settings.save() def selectionChanged(self): if self.input.currentIndex() == 0: @@ -940,8 +907,10 @@ class PluginsSettings(CenteredWidget): Plugins settings form """ - def __init__(self): - super(PluginsSettings, self).__init__() + def __init__(self, plugin_loader): + super().__init__() + self._plugin_loader = plugin_loader + self._window = None self.initUI() self.center() self.retranslateUi() @@ -961,32 +930,27 @@ class PluginsSettings(CenteredWidget): self.open = QtWidgets.QPushButton(self) self.open.setGeometry(QtCore.QRect(30, 170, 340, 30)) self.open.clicked.connect(self.open_plugin) - self.pl_loader = plugin_support.PluginLoader.get_instance() self.update_list() self.comboBox.currentIndexChanged.connect(self.show_data) self.show_data() def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate('PluginsForm', "Plugins")) - self.open.setText(QtWidgets.QApplication.translate('PluginsForm', "Open selected plugin")) + self.setWindowTitle(util_ui.tr("Plugins")) + self.open.setText(util_ui.tr("Open selected plugin")) def open_plugin(self): ind = self.comboBox.currentIndex() plugin = self.data[ind] window = self.pl_loader.plugin_window(plugin[-1]) if window is not None: - self.window = window - self.window.show() + self._window = window + self._window.show() else: - msgBox = QtWidgets.QMessageBox() - text = QtWidgets.QApplication.translate("PluginsForm", 'No GUI found for this plugin') - msgBox.setWindowTitle(QtWidgets.QApplication.translate("PluginsForm", 'Error')) - msgBox.setText(text) - msgBox.exec_() + util_ui.message_box(util_ui.tr('No GUI found for this plugin'), util_ui.tr('Error')) def update_list(self): self.comboBox.clear() - data = self.pl_loader.get_plugins_list() + data = self._plugin_loader.get_plugins_list() self.comboBox.addItems(list(map(lambda x: x[0], data))) self.data = data @@ -994,26 +958,26 @@ class PluginsSettings(CenteredWidget): ind = self.comboBox.currentIndex() if len(self.data): plugin = self.data[ind] - descr = plugin[2] or QtWidgets.QApplication.translate("PluginsForm", "No description available") + descr = plugin[2] or util_ui.tr("No description available") self.label.setText(descr) if plugin[1]: - self.button.setText(QtWidgets.QApplication.translate("PluginsForm", "Disable plugin")) + self.button.setText(util_ui.tr("Disable plugin")) else: - self.button.setText(QtWidgets.QApplication.translate("PluginsForm", "Enable plugin")) + self.button.setText(util_ui.tr("Enable plugin")) else: self.open.setVisible(False) self.button.setVisible(False) - self.label.setText(QtWidgets.QApplication.translate("PluginsForm", "No plugins found")) + self.label.setText(util_ui.tr("No plugins found")) def button_click(self): ind = self.comboBox.currentIndex() plugin = self.data[ind] - self.pl_loader.toggle_plugin(plugin[-1]) + self._plugin_loader.toggle_plugin(plugin[-1]) plugin[1] = not plugin[1] if plugin[1]: - self.button.setText(QtWidgets.QApplication.translate("PluginsForm", "Disable plugin")) + self.button.setText(util_ui.tr("Disable plugin")) else: - self.button.setText(QtWidgets.QApplication.translate("PluginsForm", "Enable plugin")) + self.button.setText(util_ui.tr("Enable plugin")) class UpdateSettings(CenteredWidget): @@ -1021,8 +985,9 @@ class UpdateSettings(CenteredWidget): Updates settings form """ - def __init__(self): - super(UpdateSettings, self).__init__() + def __init__(self, settings): + super().__init__() + self._settings = settings self.initUI() self.center() @@ -1033,61 +998,44 @@ class UpdateSettings(CenteredWidget): self.setMaximumSize(QtCore.QSize(400, 120)) self.in_label = QtWidgets.QLabel(self) self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20)) - settings = Settings.get_instance() font = QtGui.QFont() font.setPointSize(16) font.setBold(True) - font.setFamily(settings['font']) + font.setFamily(self._settings['font']) self.in_label.setFont(font) self.autoupdate = QtWidgets.QComboBox(self) self.autoupdate.setGeometry(QtCore.QRect(25, 30, 350, 30)) self.button = QtWidgets.QPushButton(self) self.button.setGeometry(QtCore.QRect(25, 70, 350, 30)) - self.button.setEnabled(settings['update']) + self.button.setEnabled(self._settings['update']) self.button.clicked.connect(self.update_client) self.retranslateUi() - self.autoupdate.setCurrentIndex(settings['update']) + self.autoupdate.setCurrentIndex(self._settings['update']) QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("updateSettingsForm", "Update settings")) - self.in_label.setText(QtWidgets.QApplication.translate("updateSettingsForm", "Select update mode:")) - self.button.setText(QtWidgets.QApplication.translate("updateSettingsForm", "Update Toxygen")) - self.autoupdate.addItem(QtWidgets.QApplication.translate("updateSettingsForm", "Disabled")) - self.autoupdate.addItem(QtWidgets.QApplication.translate("updateSettingsForm", "Manual")) - self.autoupdate.addItem(QtWidgets.QApplication.translate("updateSettingsForm", "Auto")) + self.setWindowTitle(util_ui.tr("Update settings")) + self.in_label.setText(util_ui.tr("Select update mode:")) + self.button.setText(util_ui.tr("Update Toxygen")) + self.autoupdate.addItem(util_ui.tr("Disabled")) + self.autoupdate.addItem(util_ui.tr("Manual")) + self.autoupdate.addItem(util_ui.tr("Auto")) def closeEvent(self, event): - settings = Settings.get_instance() - settings['update'] = self.autoupdate.currentIndex() - settings.save() + self._settings['update'] = self.autoupdate.currentIndex() + self._settings.save() def update_client(self): if not updater.connection_available(): - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("updateSettingsForm", "Error")) - text = (QtWidgets.QApplication.translate("updateSettingsForm", 'Problems with internet connection')) - msgBox.setText(text) - msgBox.exec_() + util_ui.message_box(util_ui.tr('Problems with internet connection'), util_ui.tr("Error")) return if not updater.updater_available(): - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("updateSettingsForm", "Error")) - text = (QtWidgets.QApplication.translate("updateSettingsForm", 'Updater not found')) - msgBox.setText(text) - msgBox.exec_() + util_ui.message_box(util_ui.tr('Updater not found'), util_ui.tr("Error")) return version = updater.check_for_updates() if version is not None: updater.download(version) - QtWidgets.QApplication.closeAllWindows() + util_ui.close_all_windows() else: - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("updateSettingsForm", "No updates found")) - text = (QtWidgets.QApplication.translate("updateSettingsForm", 'Toxygen is up to date')) - msgBox.setText(text) - msgBox.exec_() + util_ui.message_box(util_ui.tr('Toxygen is up to date'), util_ui.tr("No updates found")) diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 60ae8c3..46c7c5c 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -4,12 +4,13 @@ from ui.menu import * class WidgetsFactory: - def __init__(self, settings, profile, contacts_manager, file_transfer_handler, smiley_loader): + def __init__(self, settings, profile, contacts_manager, file_transfer_handler, smiley_loader, plugin_loader): self._settings = settings self._profile = profile self._contacts_manager = contacts_manager self._file_transfer_handler = file_transfer_handler self._smiley_loader = smiley_loader + self._plugin_loader = plugin_loader def create_screenshot_window(self, *args): return ScreenShotWindow(self._file_transfer_handler, *args) @@ -25,3 +26,31 @@ class WidgetsFactory: def create_network_settings_window(self): return NetworkSettings(self._settings, self._profile.reset) + + def create_audio_settings_window(self): + return AudioSettings(self._settings) + + def create_video_settings_window(self): + return VideoSettings(self._settings) + + def create_update_settings_window(self): + return UpdateSettings(self._settings) + + def create_plugins_settings_window(self): + return PluginsSettings(self._plugin_loader) + + def create_add_contact_window(self, tox_id): + return AddContact(self._contacts_manager, tox_id) + + def create_welcome_window(self): + return WelcomeScreen(self._settings) + + def create_privacy_settings_window(self): + return PrivacySettings(self._contacts_manager, self._settings) + + def create_interface_settings_window(self): + return InterfaceSettings(self._settings, self._smiley_loader) + + def create_notification_settings_window(self): + return NotificationsSettings(self._settings) + diff --git a/toxygen/util/ui.py b/toxygen/util/ui.py index a117202..b2b5712 100644 --- a/toxygen/util/ui.py +++ b/toxygen/util/ui.py @@ -31,4 +31,19 @@ def directory_dialog(caption=''): QtWidgets.QFileDialog.DontUseNativeDialog) -# TODO: move all dialogs here +def file_dialog(caption, file_filter=None): + return QtWidgets.QFileDialog.getOpenFileName(None, caption, util.curr_directory(), file_filter, + options=QtWidgets.QFileDialog.DontUseNativeDialog) + + +def save_file_dialog(caption, filter=None): + return QtWidgets.QFileDialog.getSaveFileName(None, caption, util.curr_directory(), + filter=filter, + options=QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) + + +def close_all_windows(): + QtWidgets.QApplication.closeAllWindows() + + +# TODO: all dialogs diff --git a/toxygen/util/util.py b/toxygen/util/util.py index b93a45a..f7ac9ff 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -65,6 +65,11 @@ def get_translations_directory(): return get_app_directory('translations') +@cached +def get_plugins_directory(): + return get_app_directory('plugins') + + def get_app_directory(directory_name): return os.path.join(get_base_directory(), directory_name) @@ -126,12 +131,6 @@ def time_offset(): return result -def append_slash(s): - if len(s) and s[-1] not in ('\\', '/'): - s += '/' - return s - - @cached def is_64_bit(): return sys.maxsize > 2 ** 32 From c81d9a3696d0d0b547683482757742326b1c6eed Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 30 Apr 2018 20:46:44 +0300 Subject: [PATCH 013/217] images path fixes, all screens loading fixed --- toxygen/app.py | 6 +-- toxygen/ui/main_screen.py | 16 +++---- toxygen/ui/main_screen_widgets.py | 28 +++++++------ toxygen/ui/menu.py | 69 +++++++++++++++---------------- toxygen/ui/widgets.py | 21 +++++----- toxygen/ui/widgets_factory.py | 8 ++-- toxygen/user_data/settings.py | 4 +- 7 files changed, 79 insertions(+), 73 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 17c760e..c8f7f14 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -156,8 +156,9 @@ class App: self._contacts_provider = ContactProvider(self._tox, self._friend_factory) profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler) self._smiley_loader = SmileyLoader(self._settings) + self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) # plugins support widgets_factory = WidgetsFactory(self._settings, profile, self._contacts_manager, self._file_transfer_handler, - self._smiley_loader, self._plugin_loader) + self._smiley_loader, self._plugin_loader, self._toxes) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, self._contacts_provider, db) self._calls_manager = CallsManager(self._tox.AV, self._settings) @@ -167,10 +168,9 @@ class App: self._ms.show() self._tray = tray.init_tray(profile, self._settings, self._ms) + self._ms.set_tray(self._tray) self._tray.show() - self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) # plugins support - # callbacks initialization callbacks.init_callbacks(self._tox, profile, self._settings, self._plugin_loader, self._contacts_manager, self._calls_manager, self._file_transfer_handler, self._ms, self._tray) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 41c7c38..7f2a1c0 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -1,4 +1,3 @@ -from ui.menu import * from contacts.profile import * from ui.list_items import * from ui.widgets import MultilineEdit, ComboBox @@ -12,7 +11,7 @@ class MainWindow(QtWidgets.QMainWindow): def __init__(self, settings, tox, tray): super().__init__() self._settings = settings - self.tray = tray + self._tray = tray self._widget_factory = None self._modal_window = None self.setAcceptDrops(True) @@ -23,6 +22,9 @@ class MainWindow(QtWidgets.QMainWindow): def set_widget_factory(self, widget_factory): self._widget_factory = widget_factory + def set_tray(self, tray): + self._tray = tray + def show(self): super().show() if self._settings['show_welcome_screen']: @@ -112,7 +114,7 @@ class MainWindow(QtWidgets.QMainWindow): def event(self, event): if event.type() == QtCore.QEvent.WindowActivate: - self.tray.setIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) + self._tray.setIcon(QtGui.QIcon(util.join_path(util.get_images_directory(), 'icon.png'))) self.messages.repaint() return super().event(event) @@ -168,12 +170,12 @@ class MainWindow(QtWidgets.QMainWindow): self.menuButton = MenuButton(Form, self.show_menu) self.menuButton.setGeometry(QtCore.QRect(QtCore.QRect(455, 3, 55, 55))) - pixmap = QtGui.QPixmap('send.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'send.png')) icon = QtGui.QIcon(pixmap) self.sendMessageButton.setIcon(icon) self.sendMessageButton.setIconSize(QtCore.QSize(45, 60)) - pixmap = QtGui.QPixmap('menu.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png')) icon = QtGui.QIcon(pixmap) self.menuButton.setIcon(icon) self.menuButton.setIconSize(QtCore.QSize(40, 40)) @@ -187,7 +189,7 @@ class MainWindow(QtWidgets.QMainWindow): self.search_label = QtWidgets.QLabel(Form) self.search_label.setGeometry(QtCore.QRect(3, 2, 20, 20)) pixmap = QtGui.QPixmap() - pixmap.load(curr_directory() + '/images/search.png') + pixmap.load(util.join_path(util.get_images_directory(), 'search.png')) self.search_label.setScaledContents(False) self.search_label.setPixmap(pixmap) @@ -267,7 +269,7 @@ class MainWindow(QtWidgets.QMainWindow): self.typing = QtWidgets.QLabel(Form) self.typing.setGeometry(QtCore.QRect(500, 25, 50, 30)) pixmap = QtGui.QPixmap(QtCore.QSize(50, 30)) - pixmap.load(curr_directory() + '/images/typing.png') + pixmap.load(util.join_path(util.get_images_directory(), 'typing.png')) self.typing.setScaledContents(False) self.typing.setPixmap(pixmap.scaled(50, 30, QtCore.Qt.KeepAspectRatio)) self.typing.setVisible(False) diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index fe66a68..390a55a 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -5,6 +5,7 @@ import smileys import urllib import util.util as util import util.ui as util_ui +from stickers.stickers import load_stickers class MessageArea(QtWidgets.QPlainTextEdit): @@ -206,30 +207,30 @@ class DropdownMenu(QtWidgets.QWidget): self.stickerButton = QtWidgets.QPushButton(self) self.stickerButton.setGeometry(QtCore.QRect(60, 0, 60, 60)) - pixmap = QtGui.QPixmap(util.get_images_directory() + 'file.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'file.png')) icon = QtGui.QIcon(pixmap) self.fileTransferButton.setIcon(icon) self.fileTransferButton.setIconSize(QtCore.QSize(50, 50)) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/screenshot.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'screenshot.png')) icon = QtGui.QIcon(pixmap) self.screenshotButton.setIcon(icon) self.screenshotButton.setIconSize(QtCore.QSize(50, 60)) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/smiley.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'smiley.png')) icon = QtGui.QIcon(pixmap) self.smileyButton.setIcon(icon) self.smileyButton.setIconSize(QtCore.QSize(50, 50)) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/sticker.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'sticker.png')) icon = QtGui.QIcon(pixmap) self.stickerButton.setIcon(icon) self.stickerButton.setIconSize(QtCore.QSize(55, 55)) - self.screenshotButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send screenshot")) - self.fileTransferButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send file")) - self.smileyButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Add smiley")) - self.stickerButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send sticker")) + self.screenshotButton.setToolTip(util_ui.tr("Send screenshot")) + self.fileTransferButton.setToolTip(util_ui.tr("Send file")) + self.smileyButton.setToolTip(util_ui.tr("Add smiley")) + self.stickerButton.setToolTip(util_ui.tr("Send sticker")) self.fileTransferButton.clicked.connect(parent.send_file) self.screenshotButton.clicked.connect(parent.send_screenshot) @@ -263,15 +264,16 @@ class StickerItem(QtWidgets.QWidget): class StickerWindow(QtWidgets.QWidget): """Sticker selection window""" - def __init__(self, parent): + def __init__(self, parent, file_transfer_handler): super().__init__() + self._file_transfer_handler = file_transfer_handler self.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.setMaximumSize(250, 200) self.setMinimumSize(250, 200) self.list = QtWidgets.QListWidget(self) self.list.setGeometry(QtCore.QRect(0, 0, 250, 200)) - self.arr = smileys.sticker_loader() - for sticker in self.arr: + self._stickers = load_stickers() + for sticker in self._stickers: item = StickerItem(sticker) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(250, item.height())) @@ -284,7 +286,7 @@ class StickerWindow(QtWidgets.QWidget): def click(self, index): num = index.row() - self.parent.profile.send_sticker(self.arr[num]) + self._file_transfer_handler.send_sticker(self._stickers[num]) self.close() def leaveEvent(self, event): @@ -377,7 +379,7 @@ class SearchScreen(QtWidgets.QWidget): self.search_button = ClickableLabel(self) self.search_button.setGeometry(width - 160, 0, 40, 40) pixmap = QtGui.QPixmap() - pixmap.load(util.curr_directory() + '/images/search.png') + pixmap.load(util.join_path(util.get_images_directory(), 'search.png')) self.search_button.setScaledContents(False) self.search_button.setAlignment(QtCore.Qt.AlignCenter) self.search_button.setPixmap(pixmap) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 9369fdd..b694574 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -1,7 +1,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets from user_data.settings import * from contacts.profile import Profile -from util.util import curr_directory, copy +from util.util import curr_directory, copy, get_stickers_directory, join_path from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio from user_data import toxes @@ -12,8 +12,9 @@ import util.ui as util_ui class AddContact(CenteredWidget): """Add contact form""" - def __init__(self, contacts_manager, tox_id=''): + def __init__(self, settings, contacts_manager, tox_id=''): super().__init__() + self._settings = settings self._contacts_manager = contacts_manager self.initUI(tox_id) self._adding = False @@ -37,7 +38,7 @@ class AddContact(CenteredWidget): self.error_label = DataLabel(self) self.error_label.setGeometry(QtCore.QRect(120, 10, 420, 20)) font = QtGui.QFont() - font.setFamily(Settings.get_instance()['font']) + font.setFamily(self._settings['font']) font.setPointSize(10) font.setWeight(30) self.error_label.setFont(font) @@ -83,9 +84,11 @@ class AddContact(CenteredWidget): class ProfileSettings(CenteredWidget): """Form with profile settings such as name, status, TOX ID""" - def __init__(self, profile): + def __init__(self, profile, settings, toxes): super().__init__() self._profile = profile + self._settings = settings + self._toxes = toxes self.initUI() self.center() @@ -104,7 +107,7 @@ class ProfileSettings(CenteredWidget): self.label = QtWidgets.QLabel(self) self.label.setGeometry(QtCore.QRect(40, 30, 91, 25)) font = QtGui.QFont() - font.setFamily(Settings.get_instance()['font']) + font.setFamily(self._settings['font']) font.setPointSize(18) font.setWeight(75) font.setBold(True) @@ -119,8 +122,7 @@ class ProfileSettings(CenteredWidget): self.tox_id.setGeometry(QtCore.QRect(15, 210, 685, 21)) font.setPointSize(10) self.tox_id.setFont(font) - s = profile.tox_id - self.tox_id.setText(s) + self.tox_id.setText(self._profile.tox_id) self.copyId = QtWidgets.QPushButton(self) self.copyId.setGeometry(QtCore.QRect(40, 250, 180, 30)) self.copyId.clicked.connect(self.copy) @@ -163,7 +165,7 @@ class ProfileSettings(CenteredWidget): self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') self.default = QtWidgets.QPushButton(self) self.default.setGeometry(QtCore.QRect(40, 550, 620, 30)) - path, name = Settings.get_auto_profile() + auto_profile = Settings.get_auto_profile() self.auto = path + name == ProfileManager.get_path() + Settings.get_instance().name self.default.clicked.connect(self.auto_profile) self.retranslateUi() @@ -214,8 +216,7 @@ class ProfileSettings(CenteredWidget): def new_password(self): if self.password.text() == self.confirm_password.text(): if not len(self.password.text()) or len(self.password.text()) >= 8: - e = toxes.ToxES.get_instance() - e.set_password(self.password.text()) + self._toxes.set_password(self.password.text()) self.close() else: self.not_match.setText( @@ -266,7 +267,7 @@ class ProfileSettings(CenteredWidget): if directory != '/': reply = util_ui.question(util_ui.tr('Do you want to move your profile to this location?'), util_ui.tr('Use new path')) - settings.export(directory) + self._settings.export(directory) self._profile.export_db(directory) ProfileManager.get_instance().export_profile(directory, reply) @@ -652,9 +653,8 @@ class InterfaceSettings(CenteredWidget): def import_st(self): directory = util_ui.directory_dialog(util_ui.tr('Choose folder with sticker pack')) if directory: - src = directory + '/' - dest = curr_directory() + '/stickers/' + os.path.basename(directory) + '/' - copy(src, dest) + dest = join_path(get_stickers_directory(), os.path.basename(directory)) + copy(directory, dest) def import_sm(self): directory = util_ui.directory_dialog(util_ui.tr('Choose folder with smiley pack')) @@ -668,6 +668,7 @@ class InterfaceSettings(CenteredWidget): if ok: self._settings['font'] = font.family() self._settings.save() + util_ui.question() msgBox = QtWidgets.QMessageBox() text = util_ui.tr('Restart app to apply settings') msgBox.setWindowTitle(util_ui.tr('Restart required')) @@ -675,51 +676,49 @@ class InterfaceSettings(CenteredWidget): msgBox.exec_() def select_color(self): - settings = Settings.get_instance() - col = QtWidgets.QColorDialog.getColor(QtGui.QColor(settings['unread_color'])) + col = QtWidgets.QColorDialog.getColor(QtGui.QColor(self._settings['unread_color'])) if col.isValid(): name = col.name() - settings['unread_color'] = name - settings.save() + self._settings['unread_color'] = name + self._settings.save() def closeEvent(self, event): - settings = Settings.get_instance() - settings['theme'] = str(self.themeSelect.currentText()) + self._settings['theme'] = str(self.themeSelect.currentText()) try: - theme = settings['theme'] + theme = self._settings['theme'] app = QtWidgets.QApplication.instance() - with open(curr_directory() + settings.built_in_themes()[theme]) as fl: + with open(curr_directory() + self._settings.built_in_themes()[theme]) as fl: style = fl.read() app.setStyleSheet(style) except IsADirectoryError: app.setStyleSheet('') # for default style - settings['smileys'] = self.smileys.isChecked() + self._settings['smileys'] = self.smileys.isChecked() restart = False - if settings['mirror_mode'] != self.mirror_mode.isChecked(): - settings['mirror_mode'] = self.mirror_mode.isChecked() + if self._settings['mirror_mode'] != self.mirror_mode.isChecked(): + self._settings['mirror_mode'] = self.mirror_mode.isChecked() restart = True - if settings['compact_mode'] != self.compact_mode.isChecked(): - settings['compact_mode'] = self.compact_mode.isChecked() + if self._settings['compact_mode'] != self.compact_mode.isChecked(): + self._settings['compact_mode'] = self.compact_mode.isChecked() restart = True - if settings['show_avatars'] != self.show_avatars.isChecked(): - settings['show_avatars'] = self.show_avatars.isChecked() + if self._settings['show_avatars'] != self.show_avatars.isChecked(): + self._settings['show_avatars'] = self.show_avatars.isChecked() restart = True - settings['smiley_pack'] = self.smiley_pack.currentText() - settings['close_to_tray'] = self.close_to_tray.isChecked() + self._settings['smiley_pack'] = self.smiley_pack.currentText() + self._settings['close_to_tray'] = self.close_to_tray.isChecked() smileys.SmileyLoader.get_instance().load_pack() language = self.lang_choose.currentText() - if settings['language'] != language: - settings['language'] = language + if self._settings['language'] != language: + self._settings['language'] = language text = self.lang_choose.currentText() path = Settings.supported_languages()[text] app = QtWidgets.QApplication.instance() app.removeTranslator(app.translator) app.translator.load(curr_directory() + '/translations/' + path) app.installTranslator(app.translator) - settings['message_font_size'] = self.messages_font_size.currentIndex() + 10 + self._settings['message_font_size'] = self.messages_font_size.currentIndex() + 10 Profile.get_instance().update() - settings.save() + self._settings.save() if restart: util_ui.message_box(util_ui.tr('Restart app to apply settings'), util_ui.tr('Restart required')) diff --git a/toxygen/ui/widgets.py b/toxygen/ui/widgets.py index 735ad39..7585f06 100644 --- a/toxygen/ui/widgets.py +++ b/toxygen/ui/widgets.py @@ -1,4 +1,5 @@ from PyQt5 import QtCore, QtGui, QtWidgets +import util.ui as util_ui class DataLabel(QtWidgets.QLabel): @@ -22,7 +23,7 @@ class ComboBox(QtWidgets.QComboBox): class CenteredWidget(QtWidgets.QWidget): def __init__(self): - super(CenteredWidget, self).__init__() + super().__init__() self.center() def center(self): @@ -137,21 +138,21 @@ def create_menu(menu): text = action.text() if 'Link Location' in text: text = text.replace('Copy &Link Location', - QtWidgets.QApplication.translate("MainWindow", "Copy link location")) + util_ui.tr("Copy link location")) elif '&Copy' in text: - text = text.replace('&Copy', QtWidgets.QApplication.translate("MainWindow", "Copy")) + text = text.replace('&Copy', util_ui.tr("Copy")) elif 'All' in text: - text = text.replace('Select All', QtWidgets.QApplication.translate("MainWindow", "Select all")) + text = text.replace('Select All', util_ui.tr("Select all")) elif 'Delete' in text: - text = text.replace('Delete', QtWidgets.QApplication.translate("MainWindow", "Delete")) + text = text.replace('Delete', util_ui.tr("Delete")) elif '&Paste' in text: - text = text.replace('&Paste', QtWidgets.QApplication.translate("MainWindow", "Paste")) + text = text.replace('&Paste', util_ui.tr("Paste")) elif 'Cu&t' in text: - text = text.replace('Cu&t', QtWidgets.QApplication.translate("MainWindow", "Cut")) + text = text.replace('Cu&t', util_ui.tr("Cut")) elif '&Undo' in text: - text = text.replace('&Undo', QtWidgets.QApplication.translate("MainWindow", "Undo")) + text = text.replace('&Undo', util_ui.tr("Undo")) elif '&Redo' in text: - text = text.replace('&Redo', QtWidgets.QApplication.translate("MainWindow", "Redo")) + text = text.replace('&Redo', util_ui.tr("Redo")) else: menu.removeAction(action) continue @@ -172,7 +173,7 @@ class MultilineEdit(CenteredWidget): self.edit.setText(text) self.button = QtWidgets.QPushButton(self) self.button.setGeometry(QtCore.QRect(0, 150, 350, 50)) - self.button.setText(QtWidgets.QApplication.translate("MainWindow", "Save")) + self.button.setText(util_ui.tr("Save")) self.button.clicked.connect(self.button_click) self.center() self.save = save diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 46c7c5c..a9be90a 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -4,13 +4,15 @@ from ui.menu import * class WidgetsFactory: - def __init__(self, settings, profile, contacts_manager, file_transfer_handler, smiley_loader, plugin_loader): + def __init__(self, settings, profile, contacts_manager, file_transfer_handler, smiley_loader, plugin_loader, + toxes): self._settings = settings self._profile = profile self._contacts_manager = contacts_manager self._file_transfer_handler = file_transfer_handler self._smiley_loader = smiley_loader self._plugin_loader = plugin_loader + self._toxes = toxes def create_screenshot_window(self, *args): return ScreenShotWindow(self._file_transfer_handler, *args) @@ -22,7 +24,7 @@ class WidgetsFactory: return WelcomeScreen(self._settings) def create_profile_settings_window(self): - return ProfileSettings(self._profile) + return ProfileSettings(self._profile, self._settings, self._toxes) def create_network_settings_window(self): return NetworkSettings(self._settings, self._profile.reset) @@ -40,7 +42,7 @@ class WidgetsFactory: return PluginsSettings(self._plugin_loader) def create_add_contact_window(self, tox_id): - return AddContact(self._contacts_manager, tox_id) + return AddContact(self._settings, self._contacts_manager, tox_id) def create_welcome_window(self): return WelcomeScreen(self._settings) diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index e387722..09fe373 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -1,6 +1,6 @@ import json import os -from util.util import log, get_base_directory, append_slash, get_platform +from util.util import log, get_base_directory, get_platform, join_path import pyaudio import smileys.smileys as smileys @@ -56,7 +56,7 @@ class Settings(dict): if 'path' in auto and 'name' in auto: path = str(auto['path']) name = str(auto['name']) - if os.path.isfile(append_slash(path) + name + '.tox'): + if os.path.isfile(join_path(path, name + '.tox')): return path, name return None From ddf6cd832826fd8d559ae016e211e58d636e6e91 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 30 Apr 2018 22:28:33 +0300 Subject: [PATCH 014/217] contact list loading --- toxygen/app.py | 401 +++++++++++++++------------ toxygen/contacts/contact.py | 2 +- toxygen/contacts/contact_provider.py | 6 +- toxygen/contacts/contacts_manager.py | 147 +++++----- toxygen/contacts/friend_factory.py | 3 +- toxygen/main.py | 6 +- toxygen/ui/items_factory.py | 8 +- toxygen/ui/list_items.py | 19 +- toxygen/util/util.py | 5 + 9 files changed, 310 insertions(+), 287 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index c8f7f14..432be07 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -25,6 +25,7 @@ from av.calls_manager import CallsManager from history.database import Database from ui.widgets_factory import WidgetsFactory from smileys.smileys import SmileyLoader +from ui.items_factory import ItemsFactory class App: @@ -34,83 +35,16 @@ class App: self._app = self._settings = self._profile_manager = self._plugin_loader = None self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None - self._friend_factory = self._calls_manager = self._contacts_manager = None + self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] self._path = path_to_profile - def enter_pass(self, data): - """ - Show password screen - """ - p = password_screen.PasswordScreen(self._toxes, data) - p.show() - self._app.lastWindowClosed.connect(self._app.quit) - self._app.exec_() - if p.result is not None: - return p.result - raise SystemExit() + # ----------------------------------------------------------------------------------------------------------------- + # App executing + # ----------------------------------------------------------------------------------------------------------------- - def main(self): - """ - Main function of app. loads login screen if needed and starts main screen - """ - self._app = QtWidgets.QApplication([]) - icon_file = os.path.join(get_images_directory(), 'icon.png') - self._app.setWindowIcon(QtGui.QIcon(icon_file)) - - if get_platform() == 'Linux': - QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) - - with open(os.path.join(get_styles_directory(), 'dark_style.qss')) as fl: - style = fl.read() - self._app.setStyleSheet(style) - - encrypt_save = tox_encrypt_save.ToxEncryptSave() - self._toxes = user_data.toxes.ToxES(encrypt_save) - - if self._path is not None: # toxygen was started with path to profile - self.load_existing_profile(self._path) - else: - auto_profile = Settings.get_auto_profile() - if auto_profile is None: # no default profile - result = self.select_profile() - if result is None: - return - if result.is_new_profile(): # create new profile - self.create_new_profile(result.profile_path) - else: # load existing profile - self.load_existing_profile(result.profile_path) - self._path = result.profile_path - else: # default profile - path, name = auto_profile - self._path = os.path.join(path, name + '.tox') - self.load_existing_profile(self._path) - - if Settings.is_active_profile(self._path): # profile is in use - profile_name = get_profile_name_from_path(self._path) - title = util_ui.tr('Profile {}').format(profile_name) - text = util_ui.tr('Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') - reply = util_ui.question(text, title) - if not reply: - return - - self._settings.set_active_profile() - - self.load_app_styles() - self.load_app_translations() - - if self.try_to_update(): - return - - self.create_dependencies() - self.start_threads() - - if self._uri is not None: - self._ms.add_contact(self._uri) - - self._app.lastWindowClosed.connect(self._app.quit) - # main + def _execute_app(self): while True: try: self._app.exec_() @@ -122,41 +56,177 @@ class App: else: break - self.stop_app() - - def stop_app(self): + def _stop_app(self): self._plugin_loader.stop() - self.stop_threads() + self._stop_threads() self._tray.hide() - self.save_profile() + self._save_profile() self._settings.close() del self._tox - def reset(self): + # ----------------------------------------------------------------------------------------------------------------- + # App loading + # ----------------------------------------------------------------------------------------------------------------- + + def _load_base_style(self): + with open(join_path(get_styles_directory(), 'dark_style.qss')) as fl: + style = fl.read() + self._app.setStyleSheet(style) + + def _load_app_styles(self): + # application color scheme + for theme in self._settings.built_in_themes().keys(): + if self._settings['theme'] == theme: + with open(curr_directory(__file__) + self._settings.built_in_themes()[theme]) as fl: + style = fl.read() + self._app.setStyleSheet(style) + + def _load_login_screen_translations(self): + current_language, supported_languages = self._get_languages() + if current_language in supported_languages: + lang_path = supported_languages[current_language] + translator = QtCore.QTranslator() + translator.load(get_translations_directory() + lang_path) + self._app.installTranslator(translator) + self._app.translator = translator + + def _load_icon(self): + icon_file = os.path.join(get_images_directory(), 'icon.png') + self._app.setWindowIcon(QtGui.QIcon(icon_file)) + + @staticmethod + def _get_languages(): + current_locale = QtCore.QLocale() + curr_language = current_locale.languageToString(current_locale.language()) + supported_languages = Settings.supported_languages() + + return curr_language, supported_languages + + def _load_app_translations(self): + lang = Settings.supported_languages()[self._settings['language']] + translator = QtCore.QTranslator() + translator.load(os.path.join(get_translations_directory(), lang)) + self._app.installTranslator(translator) + self._app.translator = translator + + # ----------------------------------------------------------------------------------------------------------------- + # Threads + # ----------------------------------------------------------------------------------------------------------------- + + def _start_threads(self): + # init thread + self._init = threads.InitThread(self._tox, self._plugin_loader, self._settings) + self._init.start() + + # starting threads for tox iterate and toxav iterate + self._main_loop = threads.ToxIterateThread(self._tox) + self._main_loop.start() + self._av_loop = threads.ToxAVIterateThread(self._tox.AV) + self._av_loop.start() + + threads.start_file_transfer_thread() + + def _stop_threads(self): + self._init.stop_thread() + + self._av_loop.stop_thread() + self._main_loop.stop_thread() + + threads.stop_file_transfer_thread() + + # ----------------------------------------------------------------------------------------------------------------- + # Profiles + # ----------------------------------------------------------------------------------------------------------------- + + def _select_profile(self): + self._load_login_screen_translations() + ls = LoginScreen() + profiles = ProfileManager.find_profiles() + ls.update_select(profiles) + ls.show() + self._app.exec_() + + return ls.result + + def _load_existing_profile(self, profile_path): + self._settings = Settings(self._toxes, profile_path.replace('.tox', '.json')) + self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) + data = self._profile_manager.open_profile() + if self._toxes.is_data_encrypted(data): + data = self._enter_pass(data) + self._tox = self._create_tox(data) + + def _create_new_profile(self, profile_path): + name = get_profile_name_from_path(profile_path) or 'toxygen_user' + if os.path.isfile(profile_path): + util_ui.message_box(util_ui.tr('Profile with this name already exists'), + util_ui.tr('Error')) + return + self._tox = tox_factory() + self._tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') + self._tox.self_set_status_message(b'Toxing on Toxygen') + # TODO: set profile password + self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) + self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) + try: + self._save_profile() + except Exception as ex: + print(ex) + log('Profile creation exception: ' + str(ex)) + text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') + util_ui.message_box(text, util_ui.tr('Error')) + return + current_language, supported_languages = self._get_languages() + if current_language in supported_languages: + self._settings['language'] = current_language + self._settings.save() + + def _save_profile(self, data=None): + data = data or self._tox.get_savedata() + self._profile_manager.save_profile(data) + + # ----------------------------------------------------------------------------------------------------------------- + # Other private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _enter_pass(self, data): + """ + Show password screen + """ + p = password_screen.PasswordScreen(self._toxes, data) + p.show() + self._app.lastWindowClosed.connect(self._app.quit) + self._app.exec_() + if p.result is not None: + return p.result + raise SystemExit() + + def _reset(self): """ Create new tox instance (new network settings) :return: tox instance """ - self.stop_threads() + self._stop_threads() data = self._tox.get_savedata() - self.save_profile(data) + self._save_profile(data) del self._tox # create new tox instance - self._tox = self.create_tox(data) - self.start_threads() + self._tox = self._create_tox(data) + self._start_threads() # TODO: foreach in list of tox savers set_tox return self._tox - def create_dependencies(self): + def _create_dependencies(self): + self._smiley_loader = SmileyLoader(self._settings) self._ms = MainWindow(self._settings, self._tox, self._tray) db = Database(self._path.replace('.tox', '.db'), self._toxes) - self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db) - self._contacts_provider = ContactProvider(self._tox, self._friend_factory) profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler) - self._smiley_loader = SmileyLoader(self._settings) self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) # plugins support + items_factory = ItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, self._ms) + self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, items_factory) + self._contacts_provider = ContactProvider(self._tox, self._friend_factory) widgets_factory = WidgetsFactory(self._settings, profile, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, self._toxes) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, @@ -175,113 +245,78 @@ class App: callbacks.init_callbacks(self._tox, profile, self._settings, self._plugin_loader, self._contacts_manager, self._calls_manager, self._file_transfer_handler, self._ms, self._tray) - def load_app_styles(self): - # application color scheme - for theme in self._settings.built_in_themes().keys(): - if self._settings['theme'] == theme: - with open(curr_directory(__file__) + self._settings.built_in_themes()[theme]) as fl: - style = fl.read() - self._app.setStyleSheet(style) - - def load_login_screen_translations(self): - current_language, supported_languages = self.get_languages() - if current_language in supported_languages: - lang_path = supported_languages[current_language] - translator = QtCore.QTranslator() - translator.load(get_translations_directory() + lang_path) - self._app.installTranslator(translator) - self._app.translator = translator - - @staticmethod - def get_languages(): - current_locale = QtCore.QLocale() - curr_language = current_locale.languageToString(current_locale.language()) - supported_languages = Settings.supported_languages() - - return curr_language, supported_languages - - def load_app_translations(self): - lang = Settings.supported_languages()[self._settings['language']] - translator = QtCore.QTranslator() - translator.load(os.path.join(get_translations_directory(), lang)) - self._app.installTranslator(translator) - self._app.translator = translator - - def try_to_update(self): + def _try_to_update(self): updating = updater.start_update_if_needed(self._version, self._settings) if updating: - self.save_profile() + self._save_profile() self._settings.close() del self._tox return updating - def start_threads(self): - # init thread - self._init = threads.InitThread(self._tox, self._plugin_loader, self._settings) - self._init.start() - - # starting threads for tox iterate and toxav iterate - self._main_loop = threads.ToxIterateThread(self._tox) - self._main_loop.start() - self._av_loop = threads.ToxAVIterateThread(self._tox.AV) - self._av_loop.start() - - threads.start_file_transfer_thread() - - def stop_threads(self): - self._init.stop_thread() - - self._av_loop.stop_thread() - self._main_loop.stop_thread() - - threads.stop_file_transfer_thread() - - def create_tox(self, data): + def _create_tox(self, data): return tox_factory(data, self._settings) - def select_profile(self): - self.load_login_screen_translations() - ls = LoginScreen() - profiles = ProfileManager.find_profiles() - ls.update_select(profiles) - ls.show() - self._app.exec_() + # ----------------------------------------------------------------------------------------------------------------- + # Public methods + # ----------------------------------------------------------------------------------------------------------------- - return ls.result + def main(self): + """ + Main function of app. loads login screen if needed and starts main screen + """ + self._app = QtWidgets.QApplication([]) + self._load_icon() - def load_existing_profile(self, profile_path): - self._settings = Settings(self._toxes, profile_path.replace('.tox', '.json')) - self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) - data = self._profile_manager.open_profile() - if self._toxes.is_data_encrypted(data): - data = self.enter_pass(data) - self._tox = self.create_tox(data) + if get_platform() == 'Linux': + QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) - def create_new_profile(self, profile_path): - name = get_profile_name_from_path(profile_path) or 'toxygen_user' - if os.path.isfile(profile_path): - util_ui.message_box(util_ui.tr('Profile with this name already exists'), - util_ui.tr('Error')) + self._load_base_style() + + encrypt_save = tox_encrypt_save.ToxEncryptSave() + self._toxes = user_data.toxes.ToxES(encrypt_save) + + if self._path is not None: # toxygen was started with path to profile + self._load_existing_profile(self._path) + else: + auto_profile = Settings.get_auto_profile() + if auto_profile is None: # no default profile + result = self._select_profile() + if result is None: + return + if result.is_new_profile(): # create new profile + self._create_new_profile(result.profile_path) + else: # load existing profile + self._load_existing_profile(result.profile_path) + self._path = result.profile_path + else: # default profile + path, name = auto_profile + self._path = os.path.join(path, name + '.tox') + self._load_existing_profile(self._path) + + if Settings.is_active_profile(self._path): # profile is in use + profile_name = get_profile_name_from_path(self._path) + title = util_ui.tr('Profile {}').format(profile_name) + text = util_ui.tr('Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') + reply = util_ui.question(text, title) + if not reply: + return + + self._settings.set_active_profile() + + self._load_app_styles() + self._load_app_translations() + + if self._try_to_update(): return - self._tox = tox_factory() - self._tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') - self._tox.self_set_status_message(b'Toxing on Toxygen') - # TODO: set profile password - self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) - self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) - try: - self.save_profile() - except Exception as ex: - print(ex) - log('Profile creation exception: ' + str(ex)) - text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') - util_ui.message_box(text, util_ui.tr('Error')) - return - current_language, supported_languages = self.get_languages() - if current_language in supported_languages: - self._settings['language'] = current_language - self._settings.save() - def save_profile(self, data=None): - data = data or self._tox.get_savedata() - self._profile_manager.save_profile(data) + self._create_dependencies() + self._start_threads() + + if self._uri is not None: + self._ms.add_contact(self._uri) + + self._app.lastWindowClosed.connect(self._app.quit) + + self._execute_app() + + self._stop_app() diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index ecaeec2..84844b6 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -12,7 +12,7 @@ class Contact(basecontact.BaseContact): Properties: number, message getter, history etc. Base class for friend and gc classes """ - def __init__(self, message_getter, number, profile_manager, name, status_message, widget, tox_id): + def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id): """ :param message_getter: gets messages from db :param number: number of friend. diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py index 52da937..aca10ad 100644 --- a/toxygen/contacts/contact_provider.py +++ b/toxygen/contacts/contact_provider.py @@ -46,13 +46,13 @@ class ContactProvider(util.ToxSave): # GC # ----------------------------------------------------------------------------------------------------------------- - def get_all_gc(self): + def get_all_groups(self): return [] - def get_gc_by_number(self): + def get_group_by_number(self): pass - def get_gc_by_public_key(self): + def get_group_by_public_key(self): pass # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 510dbb3..6c6f817 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -2,7 +2,7 @@ import util.util as util import util.ui as util_ui from contacts.friend import Friend import os -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtGui from messenger.messages import * from wrapper.toxcore_enums_and_consts import * from network.tox_dns import tox_dns @@ -28,6 +28,10 @@ class ContactsManager: def __del__(self): del self._history + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- def _is_active_a_friend(self): return type(self.get_curr_contact()) is Friend @@ -35,30 +39,15 @@ class ContactsManager: def _load_contacts(self): self._load_friends() self._load_groups() - # if len(self._contacts): - # self.set_active(0) + if len(self._contacts): + self.set_active(0) self.filtration_and_sorting(self._sorting) def _load_friends(self): - aliases = self._settings['friends_aliases'] - friend_numbers = self._tox.self_get_friend_list() - for friend_number in friend_numbers: # creates list of friends - tox_id = self._tox.friend_get_public_key(friend_number) - try: - alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] - except: - alias = '' - item = self.create_friend_item() - name = alias or self._tox.friend_get_name(friend_number) or tox_id - status_message = self._tox.friend_get_status_message(friend_number) - self._history.get_message_getter(tox_id) - message_getter = self._history.get_message_getter(tox_id) - friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, tox_id) - friend.set_alias(alias) - self._contacts.append(friend) + self._contacts.extend(self._contact_provider.get_all_friends()) def _load_groups(self): - pass + self._contacts.extend(self._contact_provider.get_all_groups()) def get_friend(self, num): if num < 0 or num >= len(self._contacts): @@ -101,7 +90,7 @@ class ContactsManager: if value is not None: if self._active_contact + 1 and self._active_contact != value: try: - self.get_curr_friend().curr_text = self._screen.messageEdit.toPlainText() + self.get_curr_contact().curr_text = self._screen.messageEdit.toPlainText() except: pass friend = self._contacts[value] @@ -113,49 +102,49 @@ class ContactsManager: if not self._settings['save_history']: friend.delete_old_messages() self._messages.clear() - friend.load_corr() - messages = friend.get_corr()[-PAGE_SIZE:] - self._load_history = False - for message in messages: - if message.get_type() <= 1: - data = message.get_data() - self.create_message_item(data[0], - data[2], - data[1], - data[3]) - elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: - if message.get_status() is None: - self.create_unsent_file_item(message) - continue - item = self.create_file_transfer_item(message) - if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer - try: - ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] - ft.set_state_changed_handler(item.update_transfer_state) - ft.signal() - except: - print('Incoming not started transfer - no info found') - elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline - self.create_inline_item(message.get_data()) - elif message.get_type() < 5: # info message - data = message.get_data() - self.create_message_item(data[0], - data[2], - '', - data[3]) - else: - data = message.get_data() - self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3]) - self._messages.scrollToBottom() - self._load_history = True - if value in self._call: - self._screen.active_call() - elif value in self._incoming_calls: - self._screen.incoming_call() - else: - self._screen.call_finished() + # friend.load_corr() + # messages = friend.get_corr()[-PAGE_SIZE:] + # self._load_history = False + # for message in messages: + # if message.get_type() <= 1: + # data = message.get_data() + # self.create_message_item(data[0], + # data[2], + # data[1], + # data[3]) + # elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: + # if message.get_status() is None: + # self.create_unsent_file_item(message) + # continue + # item = self.create_file_transfer_item(message) + # if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer + # try: + # ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] + # ft.set_state_changed_handler(item.update_transfer_state) + # ft.signal() + # except: + # print('Incoming not started transfer - no info found') + # elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline + # self.create_inline_item(message.get_data()) + # elif message.get_type() < 5: # info message + # data = message.get_data() + # self.create_message_item(data[0], + # data[2], + # '', + # data[3]) + # else: + # data = message.get_data() + # self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3]) + # self._messages.scrollToBottom() + # self._load_history = True + # if value in self._call: + # self._screen.active_call() + # elif value in self._incoming_calls: + # self._screen.incoming_call() + # else: + # self._screen.call_finished() else: - friend = self.get_curr_friend() + friend = self.get_curr_contact() self._screen.account_name.setText(friend.name) self._screen.account_status.setText(friend.status_message) @@ -239,12 +228,12 @@ class ContactsManager: """ self.filtration_and_sorting(self._sorting, self._filter_string) - def create_friend_item(self): + def _create_friend_item(self): """ Method-factory :return: new widget for friend instance """ - return None #self._factory.friend_item() + return self._factory.friend_item() # ----------------------------------------------------------------------------------------------------------------- # Friend getters @@ -255,18 +244,18 @@ class ContactsManager: def get_last_message(self): if self._active_contact + 1: - return self.get_curr_friend().get_last_message_text() + return self.get_current_contact().get_last_message_text() else: return '' def get_active_number(self): - return self.get_curr_friend().number if self._active_contact + 1 else -1 + return self.get_curr_contact().number if self._active_contact + 1 else -1 def get_active_name(self): - return self.get_curr_friend().name if self._active_contact + 1 else '' + return self.get_current_contact().name if self._active_contact + 1 else '' def is_active_online(self): - return self._active_contact + 1 and self.get_curr_friend().status is not None + return self._active_contact + 1 and self.get_current_contact().status is not None def new_name(self, number, name): friend = self.get_friend_by_number(number) @@ -274,7 +263,7 @@ class ContactsManager: friend.set_name(name) name = str(name, 'utf-8') if friend.name == name and tmp != name: - message = QtWidgets.QApplication.translate("MainWindow", 'User {} is now known as {}') + message = util_ui.tr('User {} is now known as {}') message = message.format(tmp, name) friend.append_message(InfoMessage(message, time.time())) friend.actions = True @@ -353,16 +342,8 @@ class ContactsManager: """ Adds friend to list """ - num = self._tox.friend_add_norequest(tox_id) # num - friend number - item = self.create_friend_item() - try: - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) - except Exception as ex: # something is wrong - util.log('Accept friend request failed! ' + str(ex)) - message_getter = None - friend = Friend(message_getter, num, tox_id, '', item, tox_id) + self._tox.friend_add_norequest(tox_id) + friend = self._contact_provider.get_friend_by_public_key(tox_id) self._contacts.append(friend) def block_user(self, tox_id): @@ -459,9 +440,9 @@ class ContactsManager: """ if self._settings['typing_notifications'] and self._active_contact + 1: try: - friend = self.get_curr_friend() - if friend.status is not None: - self._tox.self_set_typing(friend.number, typing) + contact = self.get_curr_contact() + if contact.status is not None and self._is_active_a_friend(): # TODO: fix - no type check + self._tox.self_set_typing(contact.number, typing) except: pass diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py index 23966f8..76273de 100644 --- a/toxygen/contacts/friend_factory.py +++ b/toxygen/contacts/friend_factory.py @@ -3,10 +3,11 @@ from contacts.friend import Friend class FriendFactory: - def __init__(self, profile_manager, settings, tox, db): + def __init__(self, profile_manager, settings, tox, db, items_factory): self._profile_manager = profile_manager self._settings, self._tox = settings, tox self._db = db + self._factory = items_factory def create_friend_by_number(self, friend_number): aliases = self._settings['friends_aliases'] diff --git a/toxygen/main.py b/toxygen/main.py index cdaa7e5..1171aac 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,6 +1,6 @@ import app from user_data.settings import * -from util.util import curr_directory, remove +from util.util import remove, get_libs_directory import argparse @@ -10,8 +10,8 @@ __version__ = '0.5.0' def clean(): """Removes all windows libs from libs folder""" - d = os.path.join(curr_directory(__file__), 'libs') - remove(d) + directory = get_libs_directory() + remove(directory) def reset(): diff --git a/toxygen/ui/items_factory.py b/toxygen/ui/items_factory.py index 386e762..b4a0b24 100644 --- a/toxygen/ui/items_factory.py +++ b/toxygen/ui/items_factory.py @@ -8,11 +8,11 @@ class ItemsFactory: self._smiley_loader, self._main_screen = smiley_loader, main_screen def friend_item(self): - item = ContactItem() - elem = QtWidgets.QListWidgetItem(self._friends) + item = ContactItem(self._settings) + elem = QtWidgets.QListWidgetItem(self._main_screen.friends_list) elem.setSizeHint(QtCore.QSize(250, item.height())) - self._friends.addItem(elem) - self._friends.setItemWidget(elem, item) + self._main_screen.friends_list.addItem(elem) + self._main_screen.friends_list.setItemWidget(elem, item) return item def message_item(self, text, time, name, sent, message_type, append, pixmap): diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py index 1891d45..a947919 100644 --- a/toxygen/ui/list_items.py +++ b/toxygen/ui/list_items.py @@ -15,9 +15,9 @@ class ContactItem(QtWidgets.QWidget): Contact in friends list """ - def __init__(self, parent=None): + def __init__(self, settings, parent=None): QtWidgets.QWidget.__init__(self, parent) - mode = settings.Settings.get_instance()['compact_mode'] + mode = settings['compact_mode'] self.setBaseSize(QtCore.QSize(250, 40 if mode else 70)) self.avatar_label = QtWidgets.QLabel(self) size = 32 if mode else 64 @@ -27,7 +27,7 @@ class ContactItem(QtWidgets.QWidget): self.name = DataLabel(self) self.name.setGeometry(QtCore.QRect(50 if mode else 75, 3 if mode else 10, 150, 15 if mode else 25)) font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) + font.setFamily(settings['font']) font.setPointSize(10 if mode else 12) font.setBold(True) self.name.setFont(font) @@ -38,7 +38,7 @@ class ContactItem(QtWidgets.QWidget): self.status_message.setFont(font) self.connection_status = StatusCircle(self) self.connection_status.setGeometry(QtCore.QRect(230, -2 if mode else 5, 32, 32)) - self.messages = UnreadMessagesCount(self) + self.messages = UnreadMessagesCount(settings, self) self.messages.setGeometry(QtCore.QRect(20 if mode else 52, 20 if mode else 50, 30, 20)) @@ -77,23 +77,24 @@ class StatusCircle(QtWidgets.QWidget): class UnreadMessagesCount(QtWidgets.QWidget): - def __init__(self, parent=None): - super(UnreadMessagesCount, self).__init__(parent) + def __init__(self, settings, parent=None): + super().__init__(parent) + self._settings = settings self.resize(30, 20) self.label = QtWidgets.QLabel(self) self.label.setGeometry(QtCore.QRect(0, 0, 30, 20)) self.label.setVisible(False) font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) + font.setFamily(settings['font']) font.setPointSize(12) font.setBold(True) self.label.setFont(font) self.label.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignCenter) - color = settings.Settings.get_instance()['unread_color'] + color = settings['unread_color'] self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') def update(self, messages_count): - color = settings.Settings.get_instance()['unread_color'] + color = self._settings['unread_color'] self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') if messages_count: self.label.setVisible(True) diff --git a/toxygen/util/util.py b/toxygen/util/util.py index f7ac9ff..afa6a91 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -70,6 +70,11 @@ def get_plugins_directory(): return get_app_directory('plugins') +@cached +def get_libs_directory(): + return get_app_directory('libs') + + def get_app_directory(directory_name): return os.path.join(get_base_directory(), directory_name) From 6ebafbda447625cdc1fd82a0c3bddb3a085f7122 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 1 May 2018 16:39:09 +0300 Subject: [PATCH 015/217] messenger created. callbacks fixes. contacts refactoring --- toxygen/app.py | 150 +++++++++--------- toxygen/contacts/common.py | 24 +++ toxygen/contacts/contact.py | 11 +- toxygen/contacts/contact_provider.py | 20 +-- toxygen/contacts/contacts_manager.py | 67 ++++---- toxygen/contacts/friend.py | 10 +- toxygen/contacts/friend_factory.py | 20 ++- toxygen/contacts/profile.py | 76 +-------- .../file_transfers/file_transfers_handler.py | 10 +- toxygen/messenger/messenger.py | 128 +++++++++++++++ toxygen/middleware/callbacks.py | 47 +++--- toxygen/ui/items_factory.py | 11 +- toxygen/ui/main_screen.py | 77 ++++----- toxygen/ui/main_screen_widgets.py | 33 ++-- toxygen/ui/messages_widgets.py | 2 +- toxygen/ui/widgets_factory.py | 5 + toxygen/util/util.py | 4 + 17 files changed, 406 insertions(+), 289 deletions(-) create mode 100644 toxygen/contacts/common.py create mode 100644 toxygen/messenger/messenger.py diff --git a/toxygen/app.py b/toxygen/app.py index 432be07..82d1618 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -26,13 +26,14 @@ from history.database import Database from ui.widgets_factory import WidgetsFactory from smileys.smileys import SmileyLoader from ui.items_factory import ItemsFactory +from messenger.messenger import Messenger class App: def __init__(self, version, path_to_profile=None, uri=None): self._version = version - self._app = self._settings = self._profile_manager = self._plugin_loader = None + self._app = self._settings = self._profile_manager = self._plugin_loader = self._messenger = None self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = None @@ -40,6 +41,71 @@ class App: self._uri = uri[4:] self._path = path_to_profile + # ----------------------------------------------------------------------------------------------------------------- + # Public methods + # ----------------------------------------------------------------------------------------------------------------- + + def main(self): + """ + Main function of app. loads login screen if needed and starts main screen + """ + self._app = QtWidgets.QApplication([]) + self._load_icon() + + if get_platform() == 'Linux': + QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) + + self._load_base_style() + + encrypt_save = tox_encrypt_save.ToxEncryptSave() + self._toxes = user_data.toxes.ToxES(encrypt_save) + + if self._path is not None: # toxygen was started with path to profile + self._load_existing_profile(self._path) + else: + auto_profile = Settings.get_auto_profile() + if auto_profile is None: # no default profile + result = self._select_profile() + if result is None: + return + if result.is_new_profile(): # create new profile + self._create_new_profile(result.profile_path) + else: # load existing profile + self._load_existing_profile(result.profile_path) + self._path = result.profile_path + else: # default profile + path, name = auto_profile + self._path = os.path.join(path, name + '.tox') + self._load_existing_profile(self._path) + + if Settings.is_active_profile(self._path): # profile is in use + profile_name = get_profile_name_from_path(self._path) + title = util_ui.tr('Profile {}').format(profile_name) + text = util_ui.tr('Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') + reply = util_ui.question(text, title) + if not reply: + return + + self._settings.set_active_profile() + + self._load_app_styles() + self._load_app_translations() + + if self._try_to_update(): + return + + self._create_dependencies() + self._start_threads() + + if self._uri is not None: + self._ms.add_contact(self._uri) + + self._app.lastWindowClosed.connect(self._app.quit) + + self._execute_app() + + self._stop_app() + # ----------------------------------------------------------------------------------------------------------------- # App executing # ----------------------------------------------------------------------------------------------------------------- @@ -220,7 +286,7 @@ class App: def _create_dependencies(self): self._smiley_loader = SmileyLoader(self._settings) - self._ms = MainWindow(self._settings, self._tox, self._tray) + self._ms = MainWindow(self._settings, self._tray) db = Database(self._path.replace('.tox', '.db'), self._toxes) profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler) self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) # plugins support @@ -231,19 +297,20 @@ class App: self._smiley_loader, self._plugin_loader, self._toxes) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, self._contacts_provider, db) + self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, + self._contacts_provider) + self._tray = tray.init_tray(profile, self._settings, self._ms) + self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger) self._calls_manager = CallsManager(self._tox.AV, self._settings) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) - self._ms.profile = profile - self._ms.set_widget_factory(widgets_factory) - self._ms.show() - - self._tray = tray.init_tray(profile, self._settings, self._ms) - self._ms.set_tray(self._tray) self._tray.show() + self._ms.show() + # callbacks initialization callbacks.init_callbacks(self._tox, profile, self._settings, self._plugin_loader, self._contacts_manager, - self._calls_manager, self._file_transfer_handler, self._ms, self._tray) + self._calls_manager, self._file_transfer_handler, self._ms, self._tray, + self._messenger) def _try_to_update(self): updating = updater.start_update_if_needed(self._version, self._settings) @@ -255,68 +322,3 @@ class App: def _create_tox(self, data): return tox_factory(data, self._settings) - - # ----------------------------------------------------------------------------------------------------------------- - # Public methods - # ----------------------------------------------------------------------------------------------------------------- - - def main(self): - """ - Main function of app. loads login screen if needed and starts main screen - """ - self._app = QtWidgets.QApplication([]) - self._load_icon() - - if get_platform() == 'Linux': - QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) - - self._load_base_style() - - encrypt_save = tox_encrypt_save.ToxEncryptSave() - self._toxes = user_data.toxes.ToxES(encrypt_save) - - if self._path is not None: # toxygen was started with path to profile - self._load_existing_profile(self._path) - else: - auto_profile = Settings.get_auto_profile() - if auto_profile is None: # no default profile - result = self._select_profile() - if result is None: - return - if result.is_new_profile(): # create new profile - self._create_new_profile(result.profile_path) - else: # load existing profile - self._load_existing_profile(result.profile_path) - self._path = result.profile_path - else: # default profile - path, name = auto_profile - self._path = os.path.join(path, name + '.tox') - self._load_existing_profile(self._path) - - if Settings.is_active_profile(self._path): # profile is in use - profile_name = get_profile_name_from_path(self._path) - title = util_ui.tr('Profile {}').format(profile_name) - text = util_ui.tr('Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') - reply = util_ui.question(text, title) - if not reply: - return - - self._settings.set_active_profile() - - self._load_app_styles() - self._load_app_translations() - - if self._try_to_update(): - return - - self._create_dependencies() - self._start_threads() - - if self._uri is not None: - self._ms.add_contact(self._uri) - - self._app.lastWindowClosed.connect(self._app.quit) - - self._execute_app() - - self._stop_app() diff --git a/toxygen/contacts/common.py b/toxygen/contacts/common.py new file mode 100644 index 0000000..584afc3 --- /dev/null +++ b/toxygen/contacts/common.py @@ -0,0 +1,24 @@ + + +class BaseTypingNotificationHandler: + + DEFAULT_HANDLER = None + + def __init__(self): + pass + + def send(self, tox, is_typing): + pass + + +class FriendTypingNotificationHandler(BaseTypingNotificationHandler): + + def __init__(self, friend_number): + super().__init__() + self._friend_number = friend_number + + def send(self, tox, is_typing): + tox.self_set_typing(self._friend_number, is_typing) + + +BaseTypingNotificationHandler.DEFAULT_HANDLER = BaseTypingNotificationHandler() diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 84844b6..c9274ac 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -1,5 +1,5 @@ from history.database import * -from contacts import basecontact +from contacts import basecontact, common import util.util as util from messenger.messages import * from file_transfers import file_transfers as ft @@ -285,3 +285,12 @@ class Contact(basecontact.BaseContact): self._number = value number = property(get_number, set_number) + + # ----------------------------------------------------------------------------------------------------------------- + # Typing notifications + # ----------------------------------------------------------------------------------------------------------------- + + def get_typing_notification_handler(self): + return common.BaseTypingNotificationHandler.DEFAULT_HANDLER + + typing_notification_handler = property(get_typing_notification_handler) diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py index aca10ad..82cf6e7 100644 --- a/toxygen/contacts/contact_provider.py +++ b/toxygen/contacts/contact_provider.py @@ -8,16 +8,6 @@ class ContactProvider(util.ToxSave): self._friend_factory = friend_factory self._cache = {} # key - contact's public key, value - contact instance - # ----------------------------------------------------------------------------------------------------------------- - # Private methods - # ----------------------------------------------------------------------------------------------------------------- - - def _get_contact_from_cache(self, public_key): - return self._cache[public_key] if public_key in self._cache else None - - def _add_to_cache(self, public_key, contact): - self._cache[public_key] = contact - # ----------------------------------------------------------------------------------------------------------------- # Friends # ----------------------------------------------------------------------------------------------------------------- @@ -72,3 +62,13 @@ class ContactProvider(util.ToxSave): def remove_friend_from_cache(self, friend_public_key): if friend_public_key in self._cache: del self._cache[friend_public_key] + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _get_contact_from_cache(self, public_key): + return self._cache[public_key] if public_key in self._cache else None + + def _add_to_cache(self, public_key, contact): + self._cache[public_key] = contact diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 6c6f817..0f7b4bc 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -9,6 +9,9 @@ from network.tox_dns import tox_dns from history.history_loader import HistoryLoader +# TODO: move messaging and typing notifications to other class + + class ContactsManager: def __init__(self, tox, settings, screen, profile_manager, contact_provider, db): @@ -29,26 +32,6 @@ class ContactsManager: def __del__(self): del self._history - # ----------------------------------------------------------------------------------------------------------------- - # Private methods - # ----------------------------------------------------------------------------------------------------------------- - - def _is_active_a_friend(self): - return type(self.get_curr_contact()) is Friend - - def _load_contacts(self): - self._load_friends() - self._load_groups() - if len(self._contacts): - self.set_active(0) - self.filtration_and_sorting(self._sorting) - - def _load_friends(self): - self._contacts.extend(self._contact_provider.get_all_friends()) - - def _load_groups(self): - self._contacts.extend(self._contact_provider.get_all_groups()) - def get_friend(self, num): if num < 0 or num >= len(self._contacts): return None @@ -61,6 +44,12 @@ class ContactsManager: data = self._tox.get_savedata() self._profile_manager.save_profile(data) + def is_friend_active(self, friend_number): + if not self._is_active_a_friend(): + return False + + return self.get_curr_contact().number == friend_number + # ----------------------------------------------------------------------------------------------------------------- # Work with active friend # ----------------------------------------------------------------------------------------------------------------- @@ -85,7 +74,7 @@ class ContactsManager: self._screen.messageEdit.clear() return try: - self.send_typing(False) + # self.send_typing(False) # TODO: fix self._screen.typing.setVisible(False) if value is not None: if self._active_contact + 1 and self._active_contact != value: @@ -430,25 +419,25 @@ class ContactsManager: except Exception as ex: # something is wrong util.log('Accept friend request failed! ' + str(ex)) + def can_send_typing_notification(self): + return self._settings['typing_notifications'] and self._active_contact != -1 + # ----------------------------------------------------------------------------------------------------------------- - # Typing notifications + # Private methods # ----------------------------------------------------------------------------------------------------------------- - def send_typing(self, typing): - """ - Send typing notification to a friend - """ - if self._settings['typing_notifications'] and self._active_contact + 1: - try: - contact = self.get_curr_contact() - if contact.status is not None and self._is_active_a_friend(): # TODO: fix - no type check - self._tox.self_set_typing(contact.number, typing) - except: - pass + def _is_active_a_friend(self): + return type(self.get_curr_contact()) is Friend - def friend_typing(self, friend_number, typing): - """ - Display incoming typing notification - """ - if friend_number == self.get_active_number() and self.is_active_a_friend(): - self._screen.typing.setVisible(typing) + def _load_contacts(self): + self._load_friends() + self._load_groups() + if len(self._contacts): + self.set_active(0) + self.filtration_and_sorting(self._sorting) + + def _load_friends(self): + self._contacts.extend(self._contact_provider.get_all_friends()) + + def _load_groups(self): + self._contacts.extend(self._contact_provider.get_all_groups()) diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 4a22329..2e65e53 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -1,4 +1,4 @@ -from contacts import contact +from contacts import contact, common from messenger.messages import * import os @@ -11,6 +11,7 @@ class Friend(contact.Contact): def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id): super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) self._receipts = 0 + self._typing_notification_handler = common.FriendTypingNotificationHandler(number) # ----------------------------------------------------------------------------------------------------------------- # File transfers support @@ -73,3 +74,10 @@ class Friend(contact.Contact): def get_full_status(self): return self._status_message + + # ----------------------------------------------------------------------------------------------------------------- + # Typing notifications + # ----------------------------------------------------------------------------------------------------------------- + + def get_typing_notification_handler(self): + return self._typing_notification_handler diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py index 76273de..6803c61 100644 --- a/toxygen/contacts/friend_factory.py +++ b/toxygen/contacts/friend_factory.py @@ -7,7 +7,12 @@ class FriendFactory: self._profile_manager = profile_manager self._settings, self._tox = settings, tox self._db = db - self._factory = items_factory + self._items_factory = items_factory + + def create_friend_by_public_key(self, public_key): + friend_number = self._tox.friend_by_public_key(public_key) + + return self.create_friend_by_number(friend_number) def create_friend_by_number(self, friend_number): aliases = self._settings['friends_aliases'] @@ -16,7 +21,7 @@ class FriendFactory: alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] except: alias = '' - item = self.create_friend_item() + item = self._create_friend_item() name = alias or self._tox.friend_get_name(friend_number) or tox_id status_message = self._tox.friend_get_status_message(friend_number) message_getter = self._db.messages_getter(tox_id) @@ -25,14 +30,13 @@ class FriendFactory: return friend - def create_friend_by_public_key(self, public_key): - friend_number = self._tox.friend_by_public_key(public_key) + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- - return self.create_friend_by_number(friend_number) - - def create_friend_item(self): + def _create_friend_item(self): """ Method-factory :return: new widget for friend instance """ - return self._factory.friend_item() + return self._items_factory.friend_item() diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 642cc04..4949175 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -32,9 +32,7 @@ class Profile(basecontact.BaseContact): self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number) self._load_history = True self._waiting_for_reconnection = False - #self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages) self._contacts_manager = None - #self._show_avatars = settings['show_avatars'] # ----------------------------------------------------------------------------------------------------------------- # Edit current user's data @@ -48,7 +46,7 @@ class Profile(basecontact.BaseContact): self.set_status((self._status + 1) % 3) def set_status(self, status): - super(Profile, self).set_status(status) + super().set_status(status) if status is not None: self._tox.self_set_status(status) elif not self._waiting_for_reconnection: @@ -59,7 +57,7 @@ class Profile(basecontact.BaseContact): if self.name == value: return tmp = self.name - super(Profile, self).set_name(value.encode('utf-8')) + super().set_name(value.encode('utf-8')) self._tox.self_set_name(self._name.encode('utf-8')) message = util_ui.tr('User {} is now known as {}') message = message.format(tmp, value) @@ -149,76 +147,6 @@ class Profile(basecontact.BaseContact): except Exception as ex: log('Sending pending messages failed with ' + str(ex)) - def split_message(self, message): - messages = [] - while len(message) > TOX_MAX_MESSAGE_LENGTH: - size = TOX_MAX_MESSAGE_LENGTH * 4 / 5 - last_part = message[size:TOX_MAX_MESSAGE_LENGTH] - if ' ' in last_part: - index = last_part.index(' ') - elif ',' in last_part: - index = last_part.index(',') - elif '.' in last_part: - index = last_part.index('.') - else: - index = TOX_MAX_MESSAGE_LENGTH - size - 1 - index += size + 1 - messages.append(message[:index]) - message = message[index:] - - return messages - - def new_message(self, friend_num, message_type, message): - """ - Current user gets new message - :param friend_num: friend_num of friend who sent message - :param message_type: message type - plain text or action message (/me) - :param message: text of message - """ - if friend_num == self.get_active_number()and self.is_active_a_friend(): # add message to list - t = time.time() - self.create_message_item(message, t, MESSAGE_OWNER['FRIEND'], message_type) - self._messages.scrollToBottom() - self.get_curr_friend().append_message( - TextMessage(message, MESSAGE_OWNER['FRIEND'], t, message_type)) - else: - friend = self.get_friend_by_number(friend_num) - friend.inc_messages() - friend.append_message( - TextMessage(message, MESSAGE_OWNER['FRIEND'], time.time(), message_type)) - if not friend.visibility: - self.update_filtration() - - def send_message_to_friend(self, text, friend_number=None): - """ - Send message - :param text: message text - :param friend_number: number of friend - """ - if friend_number is None: - friend_number = self.get_active_number() - if text.startswith('/plugin '): - self._plugin_loader.command(text[8:]) - self._screen.messageEdit.clear() - elif text and friend_number >= 0: - if text.startswith('/me '): - message_type = TOX_MESSAGE_TYPE['ACTION'] - text = text[4:] - else: - message_type = TOX_MESSAGE_TYPE['NORMAL'] - friend = self.get_friend_by_number(friend_number) - friend.inc_receipts() - if friend.status is not None: - messages = self.split_message(text.encode('utf-8')) - for message in messages: - self._tox.friend_send_message(friend_number, message_type, message) - t = time.time() - if friend.number == self.get_active_number() and self.is_active_a_friend(): - self.create_message_item(text, t, MESSAGE_OWNER['NOT_SENT'], message_type) - self._screen.messageEdit.clear() - self._messages.scrollToBottom() - friend.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], t, message_type)) - def delete_message(self, message_id): friend = self.get_curr_friend() friend.delete_message(time) diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 826adc0..e7a0308 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -20,9 +20,6 @@ class FileTransfersHandler: self._settings['paused_file_transfers'] = self._paused_file_transfers if self._settings['resend_files'] else {} self._settings.save() - def _get_friend_by_number(self, friend_number): - return self._contact_provider.get_friend_by_number(friend_number) - # ----------------------------------------------------------------------------------------------------------------- # File transfers support # ----------------------------------------------------------------------------------------------------------------- @@ -319,3 +316,10 @@ class FileTransfersHandler: self.get_friend_by_number(friend_number).load_avatar() if self.get_active_number() == friend_number and self.is_active_a_friend(): self.set_active(None) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _get_friend_by_number(self, friend_number): + return self._contact_provider.get_friend_by_number(friend_number) diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py new file mode 100644 index 0000000..77cb431 --- /dev/null +++ b/toxygen/messenger/messenger.py @@ -0,0 +1,128 @@ +import util.util as util +from wrapper.toxcore_enums_and_consts import * +from messenger.messages import * + + +class Messenger(util.ToxSave): + + def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider): + super().__init__(tox) + self._plugin_loader = plugin_loader + self._screen = screen + self._contacts_manager = contacts_manager + self._contacts_provider = contacts_provider + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _create_message_item(self): + pass + + # ----------------------------------------------------------------------------------------------------------------- + # Messaging + # ----------------------------------------------------------------------------------------------------------------- + + def new_message(self, friend_number, message_type, message): + """ + Current user gets new message + :param friend_number: friend_num of friend who sent message + :param message_type: message type - plain text or action message (/me) + :param message: text of message + """ + t = util.get_unix_time() + + if self._contacts_manager.is_friend_active(friend_number): # add message to list + self.create_message_item(message, t, MESSAGE_AUTHOR['FRIEND'], message_type) + self._screen.messages.scrollToBottom() + self._contacts_manager.get_curr_contact().append_message( + TextMessage(message, MESSAGE_AUTHOR['FRIEND'], t, message_type)) + else: + friend = self.get_friend_by_number(friend_number) + friend.inc_messages() + friend.append_message( + TextMessage(message, MESSAGE_AUTHOR['FRIEND'], t, message_type)) + if not friend.visibility: + self._contacts_manager.update_filtration() + + def send_message(self): + self.send_message_to_friend(self._screen.messageEdit.toPlainText()) + + def send_message_to_friend(self, text, friend_number=None): + """ + Send message + :param text: message text + :param friend_number: number of friend + """ + if friend_number is None: + friend_number = self._contacts_manager.get_active_number() + if text.startswith('/plugin '): + self._plugin_loader.command(text[8:]) + self._screen.messageEdit.clear() + elif text and friend_number >= 0: + if text.startswith('/me '): + message_type = TOX_MESSAGE_TYPE['ACTION'] + text = text[4:] + else: + message_type = TOX_MESSAGE_TYPE['NORMAL'] + friend = self.get_friend_by_number(friend_number) + friend.inc_receipts() + if friend.status is not None: + messages = self._split_message(text.encode('utf-8')) + t = util.get_unix_time() + for message in messages: + message_id = self._tox.friend_send_message(friend_number, message_type, message) + friend.append_message(TextMessage(message_id, text, MESSAGE_AUTHOR['NOT_SENT'], t, message_type)) + if self._contacts_manager.is_friend_active(friend_number): + self.create_message_item(text, t, MESSAGE_AUTHOR['NOT_SENT'], message_type) + self._screen.messageEdit.clear() + self._screen.messages.scrollToBottom() + + # ----------------------------------------------------------------------------------------------------------------- + # Typing notifications + # ----------------------------------------------------------------------------------------------------------------- + + def send_typing(self, typing): + """ + Send typing notification to a friend + """ + if self._contacts_manager.can_send_typing_notification(): + try: + contact = self._contacts_manager.get_curr_contact() + contact.typing_notification_handler.send(self._tox, typing) + except: + pass + + def friend_typing(self, friend_number, typing): + """ + Display incoming typing notification + """ + if self._contacts_manager.is_friend_active(friend_number): + self._screen.typing.setVisible(typing) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def _split_message(message): + messages = [] + while len(message) > TOX_MAX_MESSAGE_LENGTH: + size = TOX_MAX_MESSAGE_LENGTH * 4 / 5 + last_part = message[size:TOX_MAX_MESSAGE_LENGTH] + if ' ' in last_part: + index = last_part.index(' ') + elif ',' in last_part: + index = last_part.index(',') + elif '.' in last_part: + index = last_part.index('.') + else: + index = TOX_MAX_MESSAGE_LENGTH - size - 1 + index += size + 1 + messages.append(message[:index]) + message = message[index:] + + return messages + + def get_friend_by_number(self, friend_number): + return self._contacts_provider.get_friend_by_number(friend_number) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index e139883..69a7f68 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -39,18 +39,18 @@ def self_connection_status(tox, profile): # ----------------------------------------------------------------------------------------------------------------- -def friend_status(profile, settings): +def friend_status(contacts_manager, file_transfer_handler, profile, settings): def wrapped(tox, friend_number, new_status, user_data): """ Check friend's status (none, busy, away) """ print("Friend's #{} status changed!".format(friend_number)) - friend = profile.get_friend_by_number(friend_number) + friend = contacts_manager.get_friend_by_number(friend_number) if friend.status is None and settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) invoke_in_main_thread(friend.set_status, new_status) - invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: profile.send_files(friend_number)) - invoke_in_main_thread(profile.update_filtration) + invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: file_transfer_handler.send_files(friend_number)) + invoke_in_main_thread(contacts_manager.update_filtration) return wrapped @@ -74,42 +74,42 @@ def friend_connection_status(profile, settings, plugin_loader): return wrapped -def friend_name(profile): +def friend_name(contacts_manager): def wrapped(tox, friend_number, name, size, user_data): """ Friend changed his name """ print('New name friend #' + str(friend_number)) - invoke_in_main_thread(profile.new_name, friend_number, name) + invoke_in_main_thread(contacts_manager.new_name, friend_number, name) return wrapped -def friend_status_message(profile): +def friend_status_message(contacts_manager, messenger): def wrapped(tox, friend_number, status_message, size, user_data): """ :return: function for callback friend_status_message. It updates friend's status message and calls window repaint """ - friend = profile.get_friend_by_number(friend_number) + friend = contacts_manager.get_friend_by_number(friend_number) invoke_in_main_thread(friend.set_status_message, status_message) print('User #{} has new status'.format(friend_number)) - invoke_in_main_thread(profile.send_messages, friend_number) - if profile.get_active_number() == friend_number: - invoke_in_main_thread(profile.set_active) + invoke_in_main_thread(messenger.send_messages, friend_number) + if contacts_manager.is_friend_active(friend_number): + invoke_in_main_thread(contacts_manager.set_active) return wrapped -def friend_message(profile, settings, window, tray): +def friend_message(messenger, contacts_manager, profile, settings, window, tray): def wrapped(tox, friend_number, message_type, message, size, user_data): """ New message from friend """ message = str(message, 'utf-8') - invoke_in_main_thread(profile.new_message, friend_number, message_type, message) + invoke_in_main_thread(messenger.new_message, friend_number, message_type, message) if not window.isActiveWindow(): - friend = profile.get_friend_by_number(friend_number) + friend = contacts_manager.get_friend_by_number(friend_number) if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: invoke_in_main_thread(tray_notification, friend.name, message, tray, window) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: @@ -371,7 +371,7 @@ def show_gc_notification(window, tray, message, group_number, peer_number): def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, - calls_manager, file_transfer_handler, main_window, tray): + calls_manager, file_transfer_handler, main_window, tray, messenger): """ Initialization of all callbacks. :param tox: Tox instance @@ -382,32 +382,37 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, :param calls_manager: CallsManager instance :param file_transfer_handler: FileTransferHandler instance :param plugin_loader: PluginLoader instance - :param main_window: main window screen + :param main_window: MainWindow instance :param tray: tray (for notifications) + :param messenger: Messenger instance """ + # self callbacks tox.callback_self_connection_status(self_connection_status(tox, profile), 0) - tox.callback_friend_status(friend_status(profile, settings), 0) - tox.callback_friend_message(friend_message(profile, settings, main_window, tray), 0) + # friend callbacks + tox.callback_friend_status(friend_status(contacts_manager, file_transfer_handler, profile, settings), 0) + tox.callback_friend_message(friend_message(messenger, contacts_manager, profile, settings, main_window, tray), 0) tox.callback_friend_connection_status(friend_connection_status(profile, settings, plugin_loader), 0) - tox.callback_friend_name(friend_name(profile), 0) - tox.callback_friend_status_message(friend_status_message(profile), 0) + tox.callback_friend_name(friend_name(contacts_manager), 0) + tox.callback_friend_status_message(friend_status_message(contacts_manager, messenger), 0) tox.callback_friend_request(friend_request(contacts_manager), 0) tox.callback_friend_typing(friend_typing(contacts_manager), 0) tox.callback_friend_read_receipt(friend_read_receipt(contacts_manager), 0) + # file transfer tox.callback_file_recv(tox_file_recv(main_window, tray, profile, file_transfer_handler, contacts_manager, settings), 0) tox.callback_file_recv_chunk(file_recv_chunk(file_transfer_handler), 0) tox.callback_file_chunk_request(file_chunk_request(file_transfer_handler), 0) tox.callback_file_recv_control(file_recv_control(file_transfer_handler), 0) + # av toxav = tox.AV toxav.callback_call_state(call_state(calls_manager), 0) toxav.callback_call(call(calls_manager), 0) toxav.callback_audio_receive_frame(callback_audio(calls_manager), 0) toxav.callback_video_receive_frame(video_receive_frame, 0) + # custom packets tox.callback_friend_lossless_packet(lossless_packet(plugin_loader), 0) tox.callback_friend_lossy_packet(lossy_packet(plugin_loader), 0) - diff --git a/toxygen/ui/items_factory.py b/toxygen/ui/items_factory.py index b4a0b24..347424d 100644 --- a/toxygen/ui/items_factory.py +++ b/toxygen/ui/items_factory.py @@ -1,18 +1,21 @@ from ui.list_items import * +from ui.messages_widgets import * class ItemsFactory: def __init__(self, settings, plugin_loader, smiley_loader, main_screen): self._settings, self._plugin_loader = settings, plugin_loader - self._smiley_loader, self._main_screen = smiley_loader, main_screen + self._smiley_loader = smiley_loader + self._messages = main_screen.messages + self._friends_list = main_screen.friends_list def friend_item(self): item = ContactItem(self._settings) - elem = QtWidgets.QListWidgetItem(self._main_screen.friends_list) + elem = QtWidgets.QListWidgetItem(self._friends_list) elem.setSizeHint(QtCore.QSize(250, item.height())) - self._main_screen.friends_list.addItem(elem) - self._main_screen.friends_list.setItemWidget(elem, item) + self._friends_list.addItem(elem) + self._friends_list.setItemWidget(elem, item) return item def message_item(self, text, time, name, sent, message_type, append, pixmap): diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 7f2a1c0..c059b1f 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -8,22 +8,23 @@ import util.ui as util_ui class MainWindow(QtWidgets.QMainWindow): - def __init__(self, settings, tox, tray): + def __init__(self, settings, tray): super().__init__() self._settings = settings + self._contacts_manager = None self._tray = tray self._widget_factory = None self._modal_window = None self.setAcceptDrops(True) - self.initUI(tox) self._saved = False self.profile = None + self.initUI() - def set_widget_factory(self, widget_factory): + def set_dependencies(self, widget_factory, tray, contacts_manager, messenger): self._widget_factory = widget_factory - - def set_tray(self, tray): self._tray = tray + self._contacts_manager = contacts_manager + self.messageEdit.set_messenger(messenger) def show(self): super().show() @@ -305,7 +306,7 @@ class MainWindow(QtWidgets.QMainWindow): self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.messages.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - def initUI(self, tox): + def initUI(self): self.setMinimumSize(920, 500) s = self._settings self.setGeometry(s['x'], s['y'], s['width'], s['height']) @@ -326,7 +327,7 @@ class MainWindow(QtWidgets.QMainWindow): self.setup_right_bottom(message_buttons) self.setup_left_center(main_list) self.setup_menu(menu) - if not self._settings['mirror_mode']: + if not s['mirror_mode']: grid.addWidget(search, 2, 0) grid.addWidget(name, 1, 0) grid.addWidget(messages, 2, 1, 2, 1) @@ -367,7 +368,6 @@ class MainWindow(QtWidgets.QMainWindow): if self._saved: return self._saved = True - self.profile.close() self._settings['x'] = self.geometry().x() self._settings['y'] = self.geometry().y() self._settings['width'] = self.width() @@ -441,7 +441,7 @@ class MainWindow(QtWidgets.QMainWindow): def create_gc(self): self.profile.create_group_chat() - def profile_settings(self, *args): + def profile_settings(self): self._modal_window = self._widget_factory.create_profile_settings_window() self._modal_window.show() @@ -473,7 +473,8 @@ class MainWindow(QtWidgets.QMainWindow): if self._plugin_loader is not None: self._plugin_loader.reload() - def import_plugin(self): + @staticmethod + def import_plugin(): directory = util_ui.directory_dialog(util_ui.tr('Choose folder with plugin')) if directory: src = directory + '/' @@ -502,20 +503,19 @@ class MainWindow(QtWidgets.QMainWindow): # ----------------------------------------------------------------------------------------------------------------- def send_message(self): - text = self.messageEdit.toPlainText() - self.profile.send_message(text) + self._messenger.send_message() def send_file(self): self.menu.hide() - if self.profile.active_friend + 1and self.profile.is_active_a_friend(): + if self._contacts_manager.active_friend + 1 and self._contacts_manager.is_active_a_friend(): caption = util_ui.tr('Choose file') name = util_ui.file_dialog(caption) if name[0]: - self.profile.send_file(name[0]) + self._contacts_manager.send_file(name[0]) def send_screenshot(self, hide=False): self.menu.hide() - if self.profile.active_friend + 1 and self.profile.is_active_a_friend(): + if self._contacts_manager.active_friend + 1 and self._contacts_manager.is_active_a_friend(): self.sw = ScreenShotWindow(self) self.sw.show() if hide: @@ -523,8 +523,8 @@ class MainWindow(QtWidgets.QMainWindow): def send_smiley(self): self.menu.hide() - if self.profile.active_friend + 1: - self.smiley = SmileyWindow(self) + if self._contacts_manager.active_friend + 1: + self.smiley = self._widget_factory.create_smiley_window(self) self.smiley.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), self.y() + self.height() - 200, self.smiley.width(), @@ -533,8 +533,8 @@ class MainWindow(QtWidgets.QMainWindow): def send_sticker(self): self.menu.hide() - if self.profile.active_friend + 1 and self.profile.is_active_a_friend(): - self.sticker = StickerWindow(self) + if self._contacts_manager.active_friend + 1 and self._contacts_manager.is_active_a_friend(): + self.sticker = self._widget_factory.create_sticker_window(self) self.sticker.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), self.y() + self.height() - 200, self.sticker.width(), @@ -551,7 +551,6 @@ class MainWindow(QtWidgets.QMainWindow): self.update_call_state('call') def update_call_state(self, state): - pixmap = QtGui.QPixmap(os.path.join(util.get_images_directory(), '{}.png'.format(state))) icon = QtGui.QIcon(pixmap) self.callButton.setIcon(icon) @@ -569,10 +568,10 @@ class MainWindow(QtWidgets.QMainWindow): def friend_right_click(self, pos): item = self.friends_list.itemAt(pos) num = self.friends_list.indexFromItem(item).row() - friend = Profile.get_instance().get_friend(num) + friend = self._contacts_manager.get_friend(num) if friend is None: return - allowed = friend.tox_id in settings['auto_accept_from_friends'] + allowed = friend.tox_id in self._settings['auto_accept_from_friends'] auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') if item is not None: self.listMenu = QtWidgets.QMenu() @@ -597,7 +596,7 @@ class MainWindow(QtWidgets.QMainWindow): block_item = self.listMenu.addAction(util_ui.tr('Block friend')) notes_item = self.listMenu.addAction(util_ui.tr('Notes')) - chats = self.profile.get_group_chats() + chats = self._contacts_manager.get_group_chats() if len(chats) and self.profile.is_active_online(): invite_menu = self.listMenu.addMenu(util_ui.tr('Invite to group chat')) for i in range(len(chats)): @@ -630,7 +629,7 @@ class MainWindow(QtWidgets.QMainWindow): self.listMenu.show() def show_note(self, friend): - note = self._settings['notes'][friend.tox_id] if friend.tox_id in s['notes'] else '' + note = self._settings['notes'][friend.tox_id] if friend.tox_id in self._settings['notes'] else '' user = util_ui.tr('Notes about user') user = '{} {}'.format(user, friend.name) @@ -644,7 +643,7 @@ class MainWindow(QtWidgets.QMainWindow): self.note.show() def export_history(self, num, as_text=True): - s = self.profile.export_history(num, as_text) + s = self._history_loader.export_history(num, as_text) extension = 'txt' if as_text else 'html' file_name, _ = util_ui.save_file_dialog(util_ui.tr('Choose file name'), extension) @@ -655,30 +654,32 @@ class MainWindow(QtWidgets.QMainWindow): fl.write(s) def set_alias(self, num): - self.profile.set_alias(num) + self._contacts_manager.set_alias(num) def remove_friend(self, num): - self.profile.delete_friend(num) + self._contacts_manager.delete_friend(num) def block_friend(self, num): friend = self.profile.get_friend(num) - self.profile.block_user(friend.tox_id) + self._contacts_manager.block_user(friend.tox_id) def copy_friend_key(self, num): - tox_id = self.profile.friend_public_key(num) + tox_id = self._contacts_manager.friend_public_key(num) clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(tox_id) - def copy_name(self, friend): + @staticmethod + def copy_name(friend): clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(friend.name) - def copy_status(self, friend): + @staticmethod + def copy_status(friend): clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(friend.status_message) def clear_history(self, num): - self.profile.clear_history(num) + self._contacts_manager.clear_history(num) def leave_gc(self, num): self.profile.leave_gc(num) @@ -687,7 +688,7 @@ class MainWindow(QtWidgets.QMainWindow): self.profile.set_title(num) def auto_accept(self, num, value): - tox_id = self.profile.friend_public_key(num) + tox_id = self._contacts_manager.friend_public_key(num) if value: self._settings['auto_accept_from_friends'].append(tox_id) else: @@ -695,7 +696,7 @@ class MainWindow(QtWidgets.QMainWindow): self._settings.save() def invite_friend_to_gc(self, friend_number, group_number): - self.profile.invite_friend(friend_number, group_number) + self._contacts_manager.invite_friend(friend_number, group_number) # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user click somewhere else @@ -703,7 +704,7 @@ class MainWindow(QtWidgets.QMainWindow): def friend_click(self, index): num = index.row() - self.profile.set_active(num) + self._contacts_manager.set_active(num) def mouseReleaseEvent(self, event): pos = self.connection_status.pos() @@ -715,17 +716,17 @@ class MainWindow(QtWidgets.QMainWindow): def show(self): super().show() - #self.profile.update() + #self._contacts_manager.update() def filtering(self): ind = self.online_contacts.currentIndex() d = {0: 0, 1: 1, 2: 2, 3: 4, 4: 1 | 4, 5: 2 | 4} - self.profile.filtration_and_sorting(d[ind], self.contact_name.text()) + self._contacts_manager.filtration_and_sorting(d[ind], self.contact_name.text()) def show_search_field(self): if hasattr(self, 'search_field') and self.search_field.isVisible(): return - if self.profile.get_curr_friend() is None: + if self._c4.get_curr_friend() is None: return self.search_field = SearchScreen(self.messages, self.messages.width(), self.messages.parent()) x, y = self.messages.x(), self.messages.y() + self.messages.height() - 40 diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index 390a55a..665e5dc 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -1,7 +1,6 @@ from PyQt5 import QtCore, QtGui, QtWidgets from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit from contacts.profile import Profile -import smileys import urllib import util.util as util import util.ui as util_ui @@ -13,10 +12,14 @@ class MessageArea(QtWidgets.QPlainTextEdit): def __init__(self, parent, form): super().__init__(parent) + self._messenger = None self.parent = form self.setAcceptDrops(True) - self.timer = QtCore.QTimer(self) - self.timer.timeout.connect(lambda: self.parent.profile.send_typing(False)) + self._timer = QtCore.QTimer(self) + self._timer.timeout.connect(lambda: self._messenger.send_typing(False)) + + def set_messenger(self, messenger): + self._messenger = messenger def keyPressEvent(self, event): if event.matches(QtGui.QKeySequence.Paste): @@ -31,22 +34,22 @@ class MessageArea(QtWidgets.QPlainTextEdit): if modifiers & QtCore.Qt.ControlModifier or modifiers & QtCore.Qt.ShiftModifier: self.insertPlainText('\n') else: - if self.timer.isActive(): - self.timer.stop() - self.parent.profile.send_typing(False) - self.parent.send_message() + if self._timer.isActive(): + self._timer.stop() + self._messenger.send_typing(False) + self._messenger.send_message() elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText(): - self.appendPlainText(Profile.get_instance().get_last_message()) - elif event.key() == QtCore.Qt.Key_Tab and not self.parent.profile.is_active_a_friend(): + self.appendPlainText(self._messenger.get_last_message()) + elif event.key() == QtCore.Qt.Key_Tab and not self._messenger.is_active_a_friend(): text = self.toPlainText() pos = self.textCursor().position() - self.insertPlainText(Profile.get_instance().get_gc_peer_name(text[:pos])) + self.insertPlainText(self._messenger.get_gc_peer_name(text[:pos])) else: - self.parent.profile.send_typing(True) - if self.timer.isActive(): - self.timer.stop() - self.timer.start(5000) - super(MessageArea, self).keyPressEvent(event) + self._messenger.send_typing(True) + if self._timer.isActive(): + self._timer.stop() + self._timer.start(5000) + super().keyPressEvent(event) def contextMenuEvent(self, event): menu = create_menu(self.createStandardContextMenu()) diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index ae21f8a..0d65c8a 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -122,7 +122,7 @@ class MessageItem(QtWidgets.QWidget): """ Message in messages list """ - def __init__(self, settings, message_edit_factory, text_message, parent=None): + def __init__(self, settings, text_message, parent=None): QtWidgets.QWidget.__init__(self, parent) self.name = widgets.DataLabel(self) self.name.setGeometry(QtCore.QRect(2, 2, 95, 23)) diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index a9be90a..4051656 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -56,3 +56,8 @@ class WidgetsFactory: def create_notification_settings_window(self): return NotificationsSettings(self._settings) + def create_smiley_window(self, parent): + return SmileyWindow(parent, self._smiley_loader) + + def create_sticker_window(self, parent): + return StickerWindow(parent, self._file_transfer_handler) diff --git a/toxygen/util/util.py b/toxygen/util/util.py index afa6a91..09e6877 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -94,6 +94,10 @@ def curr_time(): return time.strftime('%H:%M') +def get_unix_time(): + return int(time.time()) + + def join_path(a, b): return os.path.join(a, b) From ad351030d928d012ae557ab9cc140bb950bf70fc Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 1 May 2018 21:40:29 +0300 Subject: [PATCH 016/217] messenger fixes, refactoring (history) --- toxygen/app.py | 10 +-- toxygen/contacts/contacts_manager.py | 68 ++++++++----------- toxygen/history/history_loader.py | 76 +++++----------------- toxygen/history/history_logs_generators.py | 48 ++++++++++++++ toxygen/messenger/messages.py | 10 +++ toxygen/messenger/messenger.py | 33 ++++++---- toxygen/ui/items_factory.py | 4 +- toxygen/ui/main_screen.py | 33 +++++----- toxygen/ui/widgets_factory.py | 3 +- 9 files changed, 150 insertions(+), 135 deletions(-) create mode 100644 toxygen/history/history_logs_generators.py diff --git a/toxygen/app.py b/toxygen/app.py index 82d1618..53c4dff 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -141,6 +141,8 @@ class App: def _load_app_styles(self): # application color scheme + if self._settings['theme'] == 'dark': + return for theme in self._settings.built_in_themes().keys(): if self._settings['theme'] == theme: with open(curr_directory(__file__) + self._settings.built_in_themes()[theme]) as fl: @@ -294,17 +296,17 @@ class App: self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory) widgets_factory = WidgetsFactory(self._settings, profile, self._contacts_manager, self._file_transfer_handler, - self._smiley_loader, self._plugin_loader, self._toxes) + self._smiley_loader, self._plugin_loader, self._toxes, self._version) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, self._contacts_provider, db) self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, - self._contacts_provider) + self._contacts_provider, items_factory) self._tray = tray.init_tray(profile, self._settings, self._ms) - self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger) + self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile) self._calls_manager = CallsManager(self._tox.AV, self._settings) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) - self._tray.show() + self._tray.show() self._ms.show() # callbacks initialization diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 0f7b4bc..e468ecc 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,7 +1,6 @@ import util.util as util import util.ui as util_ui from contacts.friend import Friend -import os from PyQt5 import QtCore, QtGui from messenger.messages import * from wrapper.toxcore_enums_and_consts import * @@ -9,10 +8,10 @@ from network.tox_dns import tox_dns from history.history_loader import HistoryLoader -# TODO: move messaging and typing notifications to other class - - class ContactsManager: + """ + Represents contacts list. + """ def __init__(self, tox, settings, screen, profile_manager, contact_provider, db): self._tox = tox @@ -134,12 +133,11 @@ class ContactsManager: # self._screen.call_finished() else: friend = self.get_curr_contact() - + # TODO: to separate method self._screen.account_name.setText(friend.name) self._screen.account_status.setText(friend.status_message) self._screen.account_status.setToolTip(friend.get_full_status()) avatar_path = friend.get_avatar_path() - os.chdir(os.path.dirname(avatar_path)) pixmap = QtGui.QPixmap(avatar_path) self._screen.account_avatar.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) @@ -171,6 +169,7 @@ class ContactsManager: :param sorting: 0 - no sort, 1 - online only, 2 - online first, 4 - by name :param filter_str: show contacts which name contains this substring """ + # TODO: simplify? filter_str = filter_str.lower() number = self.get_active_number() is_friend = self._is_active_a_friend() @@ -196,16 +195,16 @@ class ContactsManager: part1 = sorted(part1, key=lambda x: x.number) part2 = sorted(part2, key=lambda x: x.number) self._contacts = part1 + part2 - self._screen.friends_list.clear() - for contact in self._contacts: - contact.set_widget(self.create_friend_item()) + # self._screen.friends_list.clear() + # for contact in self._contacts: + # contact.set_widget(self.create_friend_item()) for index, friend in enumerate(self._contacts): friend.visibility = (friend.status is not None or not (sorting & 1)) and (filter_str in friend.name.lower()) friend.visibility = friend.visibility or friend.messages or friend.actions - # if friend.visibility: - # self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) - # else: - # self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) + if friend.visibility: + self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) + else: + self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) self._sorting, self._filter_string = sorting, filter_str self._settings['sorting'] = self._sorting self._settings.save() @@ -217,13 +216,6 @@ class ContactsManager: """ self.filtration_and_sorting(self._sorting, self._filter_string) - def _create_friend_item(self): - """ - Method-factory - :return: new widget for friend instance - """ - return self._factory.friend_item() - # ----------------------------------------------------------------------------------------------------------------- # Friend getters # ----------------------------------------------------------------------------------------------------------------- @@ -233,7 +225,7 @@ class ContactsManager: def get_last_message(self): if self._active_contact + 1: - return self.get_current_contact().get_last_message_text() + return self.get_curr_contact().get_last_message_text() else: return '' @@ -241,12 +233,13 @@ class ContactsManager: return self.get_curr_contact().number if self._active_contact + 1 else -1 def get_active_name(self): - return self.get_current_contact().name if self._active_contact + 1 else '' + return self.get_curr_contact().name if self._active_contact + 1 else '' def is_active_online(self): - return self._active_contact + 1 and self.get_current_contact().status is not None + return self._active_contact + 1 and self.get_curr_contact().status is not None def new_name(self, number, name): + # TODO: move to somewhere else? friend = self.get_friend_by_number(number) tmp = friend.name friend.set_name(name) @@ -254,7 +247,7 @@ class ContactsManager: if friend.name == name and tmp != name: message = util_ui.tr('User {} is now known as {}') message = message.format(tmp, name) - friend.append_message(InfoMessage(message, time.time())) + friend.append_message(InfoMessage(0, message, util.get_unix_time())) friend.actions = True if number == self.get_active_number(): self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) @@ -293,12 +286,16 @@ class ContactsManager: except: pass self._settings.save() - if num == self.get_active_number() and self.is_active_a_friend(): - self.update() + # if num == self.get_active_number() and self.is_active_a_friend(): + # self.update() def friend_public_key(self, num): return self._contacts[num].tox_id + def export_history(self, num, as_text): + contact = self._contacts[num] + return self._history.export_history(contact, as_text) + def delete_friend(self, num): """ Removes friend from contact list @@ -313,17 +310,12 @@ class ContactsManager: if friend.tox_id in self._settings['notes']: del self._settings['notes'][friend.tox_id] self._settings.save() - self.clear_history(num) - if self._history.friend_exists_in_db(friend.tox_id): - self._history.delete_friend_from_db(friend.tox_id) + self._history.delete_history(friend) self._tox.friend_delete(friend.number) del self._contacts[num] self._screen.friends_list.takeItem(num) if num == self._active_contact: # active friend was deleted - if not len(self._contacts): # last friend was deleted - self.set_active(-1) - else: - self.set_active(0) + self.set_active(0 if len(self._contacts) else -1) data = self._tox.get_savedata() self._profile_manager.save_profile(data) @@ -387,13 +379,9 @@ class ContactsManager: text = util_ui.tr('Friend added without sending friend request') util_ui.message_box(text, title) else: - result = self._tox.friend_add(tox_id, message.encode('utf-8')) + self._tox.friend_add(tox_id, message.encode('utf-8')) tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] - item = self.create_friend_item() - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) - friend = Friend(message_getter, result, tox_id, '', item, tox_id) + friend = self._contact_provider.get_friend_by_public_key(tox_id) self._contacts.append(friend) self.save_profile() return True @@ -420,7 +408,7 @@ class ContactsManager: util.log('Accept friend request failed! ' + str(ex)) def can_send_typing_notification(self): - return self._settings['typing_notifications'] and self._active_contact != -1 + return self._settings['typing_notifications'] and self._active_contact + 1 # ----------------------------------------------------------------------------------------------------------------- # Private methods diff --git a/toxygen/history/history_loader.py b/toxygen/history/history_loader.py index 801125b..a380ddb 100644 --- a/toxygen/history/history_loader.py +++ b/toxygen/history/history_loader.py @@ -1,4 +1,4 @@ -from messenger.messages import * +from history.history_logs_generators import * # TODO: fix history loading and saving @@ -96,65 +96,21 @@ class HistoryLoader: return self._db.messages_getter(friend_public_key) - @staticmethod - def export_history(friend, as_text=True, _range=None): - if _range is None: - friend.load_all_corr() - corr = friend.get_corr() - elif _range[1] + 1: - corr = friend.get_corr()[_range[0]:_range[1] + 1] - else: - corr = friend.get_corr()[_range[0]:] + def delete_history(self, friend): + self.clear_history(friend) + if self._db.friend_exists_in_db(friend.tox_id): + self._db.delete_friend_from_db(friend.tox_id) - generator = TextHistoryGenerator(corr) if as_text else HtmlHistoryGenerator(corr) + @staticmethod + def export_history(contact, as_text=True, _range=None): + if _range is None: + contact.load_all_corr() + corr = contact.get_corr() + elif _range[1] + 1: + corr = contact.get_corr()[_range[0]:_range[1] + 1] + else: + corr = contact.get_corr()[_range[0]:] + + generator = TextHistoryGenerator(corr, contact.name) if as_text else HtmlHistoryGenerator(corr, contact.name) return generator.generate() - - -class HistoryLogsGenerator: - - def __init__(self, history): - self._history = history - - def generate(self): - return str() - - -class HtmlHistoryGenerator(HistoryLogsGenerator): - - def __init__(self, history): - super().__init__(history) - - def generate(self): - arr = [] - for message in self._history: - if type(message) is TextMessage: - data = message.get_data() - x = '[{}] {}: {}
' - arr.append(x.format(convert_time(data[2]) if data[1] != MESSAGE_OWNER['NOT_SENT'] else 'Unsent', - friend.name if data[1] == MESSAGE_OWNER['FRIEND'] else self.name, - data[0])) - s = '
'.join(arr) - s = '{}{}'.format(friend.name, - s) - return s - - -class TextHistoryGenerator(HistoryLogsGenerator): - - def __init__(self, history): - super().__init__(history) - - def generate(self): - arr = [] - for message in self._history: - if type(message) is TextMessage: - data = message.get_data() - x = '[{}] {}: {}\n' - arr.append(x.format(convert_time(data[2]) if data[1] != MESSAGE_OWNER['NOT_SENT'] else 'Unsent', - friend.name if data[1] == MESSAGE_OWNER['FRIEND'] else self.name, - data[0])) - s = '\n'.join(arr) - - return s - diff --git a/toxygen/history/history_logs_generators.py b/toxygen/history/history_logs_generators.py new file mode 100644 index 0000000..3e46562 --- /dev/null +++ b/toxygen/history/history_logs_generators.py @@ -0,0 +1,48 @@ +from messenger.messages import * +import util.util as util + + +class HistoryLogsGenerator: + + def __init__(self, history, contact_name): + self._history = history + self._contact_name = contact_name + + def generate(self): + return str() + + @staticmethod + def _get_message_time(message): + return util.convert_time(message.time) if message.author.type != MESSAGE_AUTHOR['NOT_SENT'] else 'Unsent' + + +class HtmlHistoryGenerator(HistoryLogsGenerator): + + def __init__(self, history, contact_name): + super().__init__(history, contact_name) + + def generate(self): + arr = [] + for message in self._history: + if type(message) is TextMessage: + x = '[{}] {}: {}
' + arr.append(x.format(self._get_message_time(message), message.author.name, message.text)) + s = '
'.join(arr) + html = '{}{}' + + return html.format(self._contact_name, s) + + +class TextHistoryGenerator(HistoryLogsGenerator): + + def __init__(self, history, contact_name): + super().__init__(history, contact_name) + + def generate(self): + arr = [self._contact_name] + for message in self._history: + if type(message) is TextMessage: + x = '[{}] {}: {}\n' + arr.append(x.format(self._get_message_time(message), message.author.name, message.text)) + + return '\n'.join(arr) diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 023a2e7..41c046d 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -38,6 +38,11 @@ class Message: author = property(get_author) + def get_time(self): + return self._time + + time = property(get_time) + def get_message_id(self): return self._message_id @@ -70,6 +75,11 @@ class TextMessage(Message): super().__init__(id, message_type, owner, time) self._message = message + def get_text(self): + return self._message + + text = property(get_text) + def get_data(self): return self._message, self._owner, self._time, self._type diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 77cb431..8cb25e1 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -5,19 +5,21 @@ from messenger.messages import * class Messenger(util.ToxSave): - def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider): + def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider, items_factory): super().__init__(tox) self._plugin_loader = plugin_loader self._screen = screen self._contacts_manager = contacts_manager self._contacts_provider = contacts_provider + self._items_factory = items_factory # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- - def _create_message_item(self): - pass + def _create_message_item(self, text_message): + # pixmap = self._contacts_manager.get_curr_contact().get_pixmap() + self._items_factory.message_item(text_message) # ----------------------------------------------------------------------------------------------------------------- # Messaging @@ -66,17 +68,20 @@ class Messenger(util.ToxSave): else: message_type = TOX_MESSAGE_TYPE['NORMAL'] friend = self.get_friend_by_number(friend_number) - friend.inc_receipts() - if friend.status is not None: - messages = self._split_message(text.encode('utf-8')) - t = util.get_unix_time() - for message in messages: + messages = self._split_message(text.encode('utf-8')) + t = util.get_unix_time() + for message in messages: + if friend.status is not None: message_id = self._tox.friend_send_message(friend_number, message_type, message) - friend.append_message(TextMessage(message_id, text, MESSAGE_AUTHOR['NOT_SENT'], t, message_type)) - if self._contacts_manager.is_friend_active(friend_number): - self.create_message_item(text, t, MESSAGE_AUTHOR['NOT_SENT'], message_type) - self._screen.messageEdit.clear() - self._screen.messages.scrollToBottom() + friend.inc_receipts() + else: + message_id = 0 + message = TextMessage(message_id, text, MESSAGE_AUTHOR['NOT_SENT'], t, message_type) + friend.append_message(message) + if self._contacts_manager.is_friend_active(friend_number): + self._create_message_item(message) + self._screen.messageEdit.clear() + self._screen.messages.scrollToBottom() # ----------------------------------------------------------------------------------------------------------------- # Typing notifications @@ -121,6 +126,8 @@ class Messenger(util.ToxSave): index += size + 1 messages.append(message[:index]) message = message[index:] + if message: + messages.append(message) return messages diff --git a/toxygen/ui/items_factory.py b/toxygen/ui/items_factory.py index 347424d..10ca02b 100644 --- a/toxygen/ui/items_factory.py +++ b/toxygen/ui/items_factory.py @@ -18,8 +18,8 @@ class ItemsFactory: self._friends_list.setItemWidget(elem, item) return item - def message_item(self, text, time, name, sent, message_type, append, pixmap): - item = MessageItem(text, time, name, sent, message_type, self._messages) + def message_item(self, message, pixmap=None): + item = MessageItem(message, self._messages) if pixmap is not None: item.set_avatar(pixmap) elem = QtWidgets.QListWidgetItem() diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index c059b1f..05007f9 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -17,13 +17,14 @@ class MainWindow(QtWidgets.QMainWindow): self._modal_window = None self.setAcceptDrops(True) self._saved = False - self.profile = None + self._profile = None self.initUI() - def set_dependencies(self, widget_factory, tray, contacts_manager, messenger): + def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile): self._widget_factory = widget_factory self._tray = tray self._contacts_manager = contacts_manager + self._profile = profile self.messageEdit.set_messenger(messenger) def show(self): @@ -401,17 +402,18 @@ class MainWindow(QtWidgets.QMainWindow): #self.profile.update() def keyPressEvent(self, event): - if event.key() == QtCore.Qt.Key_Escape and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): + key, modifiers = event.key(), event.modifiers() + if key == QtCore.Qt.Key_Escape and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): self.hide() - elif event.key() == QtCore.Qt.Key_C and event.modifiers() & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): + elif key == QtCore.Qt.Key_C and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): rows = list(map(lambda x: self.messages.row(x), self.messages.selectedItems())) indexes = (rows[0] - self.messages.count(), rows[-1] - self.messages.count()) s = self.profile.export_history(self.profile.active_friend, True, indexes) clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(s) - elif event.key() == QtCore.Qt.Key_Z and event.modifiers() & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): + elif key == QtCore.Qt.Key_Z and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): self.messages.clearSelection() - elif event.key() == QtCore.Qt.Key_F and event.modifiers() & QtCore.Qt.ControlModifier: + elif key == QtCore.Qt.Key_F and modifiers & QtCore.Qt.ControlModifier: self.show_search_field() else: super().keyPressEvent(event) @@ -421,6 +423,7 @@ class MainWindow(QtWidgets.QMainWindow): # ----------------------------------------------------------------------------------------------------------------- def about_program(self): + # TODO: replace with window text = util_ui.tr('Toxygen is Tox client written on Python.\nVersion: ') text += '' + '\nGitHub: https://github.com/toxygen-project/toxygen/' title = util_ui.tr('About') @@ -435,7 +438,7 @@ class MainWindow(QtWidgets.QMainWindow): self._modal_window.show() def add_contact(self, link=''): - self._modal_window = self._widget_factory.create_add_contact_window(link or '') + self._modal_window = self._widget_factory.create_add_contact_window(link) self._modal_window.show() def create_gc(self): @@ -507,7 +510,7 @@ class MainWindow(QtWidgets.QMainWindow): def send_file(self): self.menu.hide() - if self._contacts_manager.active_friend + 1 and self._contacts_manager.is_active_a_friend(): + if self._contacts_manager.is_active_a_friend(): caption = util_ui.tr('Choose file') name = util_ui.file_dialog(caption) if name[0]: @@ -515,8 +518,8 @@ class MainWindow(QtWidgets.QMainWindow): def send_screenshot(self, hide=False): self.menu.hide() - if self._contacts_manager.active_friend + 1 and self._contacts_manager.is_active_a_friend(): - self.sw = ScreenShotWindow(self) + if self._contacts_manager.is_active_a_friend(): + self.sw = self._widget_factory.create_screenshot_window(self) self.sw.show() if hide: self.hide() @@ -533,7 +536,7 @@ class MainWindow(QtWidgets.QMainWindow): def send_sticker(self): self.menu.hide() - if self._contacts_manager.active_friend + 1 and self._contacts_manager.is_active_a_friend(): + if self._contacts_manager.is_active_a_friend(): self.sticker = self._widget_factory.create_sticker_window(self) self.sticker.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), self.y() + self.height() - 200, @@ -643,7 +646,7 @@ class MainWindow(QtWidgets.QMainWindow): self.note.show() def export_history(self, num, as_text=True): - s = self._history_loader.export_history(num, as_text) + s = self._contacts_manager.export_history(num, as_text) extension = 'txt' if as_text else 'html' file_name, _ = util_ui.save_file_dialog(util_ui.tr('Choose file name'), extension) @@ -712,11 +715,11 @@ class MainWindow(QtWidgets.QMainWindow): if (x < event.x() < x + 32) and (y < event.y() < y + 32): self.profile.change_status() else: - super(MainWindow, self).mouseReleaseEvent(event) + super().mouseReleaseEvent(event) def show(self): super().show() - #self._contacts_manager.update() + self._contacts_manager.update() def filtering(self): ind = self.online_contacts.currentIndex() @@ -726,7 +729,7 @@ class MainWindow(QtWidgets.QMainWindow): def show_search_field(self): if hasattr(self, 'search_field') and self.search_field.isVisible(): return - if self._c4.get_curr_friend() is None: + if self._contacts_manager.get_curr_friend() is None: return self.search_field = SearchScreen(self.messages, self.messages.width(), self.messages.parent()) x, y = self.messages.x(), self.messages.y() + self.messages.height() - 40 diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 4051656..1bba4a7 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -5,7 +5,7 @@ from ui.menu import * class WidgetsFactory: def __init__(self, settings, profile, contacts_manager, file_transfer_handler, smiley_loader, plugin_loader, - toxes): + toxes, version): self._settings = settings self._profile = profile self._contacts_manager = contacts_manager @@ -13,6 +13,7 @@ class WidgetsFactory: self._smiley_loader = smiley_loader self._plugin_loader = plugin_loader self._toxes = toxes + self._version = version def create_screenshot_window(self, *args): return ScreenShotWindow(self._file_transfer_handler, *args) From c8443b56ddfae359e3cb070d1e57827dc40dd803 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 4 May 2018 00:17:48 +0300 Subject: [PATCH 017/217] messages - minimal working version --- toxygen/app.py | 15 +-- toxygen/bootstrap/bootstrap.py | 2 +- toxygen/contacts/contacts_manager.py | 1 - toxygen/contacts/profile.py | 43 +------ toxygen/file_transfers/file_transfers.py | 2 +- .../file_transfers/file_transfers_handler.py | 46 ++++++-- toxygen/history/database.py | 3 + toxygen/main.py | 6 +- toxygen/messenger/messages.py | 5 + toxygen/messenger/messenger.py | 37 ++++-- toxygen/middleware/callbacks.py | 17 +-- toxygen/network/tox_dns.py | 106 +++++++++--------- toxygen/ui/av_widgets.py | 2 +- toxygen/ui/items_factory.py | 11 +- toxygen/ui/list_items.py | 2 +- toxygen/ui/menu.py | 2 +- toxygen/ui/messages_widgets.py | 18 +-- toxygen/ui/tray.py | 2 +- toxygen/user_data/settings.py | 2 +- 19 files changed, 174 insertions(+), 148 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 53c4dff..87d3c60 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -289,22 +289,23 @@ class App: def _create_dependencies(self): self._smiley_loader = SmileyLoader(self._settings) self._ms = MainWindow(self._settings, self._tray) + self._calls_manager = CallsManager(self._tox.AV, self._settings) db = Database(self._path.replace('.tox', '.db'), self._toxes) - profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler) - self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) # plugins support + + profile = Profile(self._profile_manager, self._tox, self._ms) + self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) items_factory = ItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, self._ms) self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory) - widgets_factory = WidgetsFactory(self._settings, profile, self._contacts_manager, self._file_transfer_handler, - self._smiley_loader, self._plugin_loader, self._toxes, self._version) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, self._contacts_provider, db) + self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) + widgets_factory = WidgetsFactory(self._settings, profile, self._contacts_manager, self._file_transfer_handler, + self._smiley_loader, self._plugin_loader, self._toxes, self._version) self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, - self._contacts_provider, items_factory) + self._contacts_provider, items_factory, profile) self._tray = tray.init_tray(profile, self._settings, self._ms) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile) - self._calls_manager = CallsManager(self._tox.AV, self._settings) - self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) self._tray.show() self._ms.show() diff --git a/toxygen/bootstrap/bootstrap.py b/toxygen/bootstrap/bootstrap.py index ccce1c7..2e5d3dc 100644 --- a/toxygen/bootstrap/bootstrap.py +++ b/toxygen/bootstrap/bootstrap.py @@ -1,6 +1,6 @@ import random import urllib.request -from util.util import log, curr_directory, join_path +from util.util import * from PyQt5 import QtNetwork, QtCore import json diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index e468ecc..8fb1b92 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -4,7 +4,6 @@ from contacts.friend import Friend from PyQt5 import QtCore, QtGui from messenger.messages import * from wrapper.toxcore_enums_and_consts import * -from network.tox_dns import tox_dns from history.history_loader import HistoryLoader diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 4949175..cfeae94 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -1,7 +1,5 @@ from contacts.friend import * from user_data.settings import * -from wrapper.toxcore_enums_and_consts import * -from util.util import log from history.database import * from file_transfers.file_transfers import * import time @@ -14,7 +12,7 @@ class Profile(basecontact.BaseContact): """ Profile of current toxygen user. Contains friends list, tox instance """ - def __init__(self, profile_manager, tox, screen, file_transfer_handler): + def __init__(self, profile_manager, tox, screen): """ :param tox: tox instance :param screen: ref to main screen @@ -25,7 +23,6 @@ class Profile(basecontact.BaseContact): tox.self_get_status_message(), screen.user_info, tox.self_get_address()) - self._file_transfer_handler = file_transfer_handler self._screen = screen self._messages = screen.messages self._tox = tox @@ -83,30 +80,6 @@ class Profile(basecontact.BaseContact): # Friend connection status callbacks # ----------------------------------------------------------------------------------------------------------------- - def send_files(self, friend_number): - friend = self.get_friend_by_number(friend_number) - friend.remove_invalid_unsent_files() - files = friend.get_unsent_files() - try: - for fl in files: - data = fl.get_data() - if data[1] is not None: - self.send_inline(data[1], data[0], friend_number, True) - else: - self.send_file(data[0], friend_number, True) - friend.clear_unsent_files() - for key in list(self._paused_file_transfers.keys()): - data = self._paused_file_transfers[key] - if not os.path.exists(data[0]): - del self._paused_file_transfers[key] - elif data[1] == friend_number and not data[2]: - self.send_file(data[0], friend_number, True, key) - del self._paused_file_transfers[key] - if friend_number == self.get_active_number() and self.is_active_a_friend(): - self.update() - except Exception as ex: - print('Exception in file sending: ' + str(ex)) - def friend_exit(self, friend_number): """ Friend with specified number quit @@ -133,20 +106,6 @@ class Profile(basecontact.BaseContact): while i < self._messages.count() and not self._messages.itemWidget(self._messages.item(i)).mark_as_sent(): i += 1 - def send_messages(self, friend_number): - """ - Send 'offline' messages to friend - """ - friend = self.get_friend_by_number(friend_number) - friend.load_corr() - messages = friend.get_unsent_messages() - try: - for message in messages: - self.split_and_send(friend_number, message.get_data()[-1], message.get_data()[0].encode('utf-8')) - friend.inc_receipts() - except Exception as ex: - log('Sending pending messages failed with ' + str(ex)) - def delete_message(self, message_id): friend = self.get_curr_friend() friend.delete_message(time) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 5aab6f9..937ab12 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -172,7 +172,7 @@ class SendAvatar(SendTransfer): else: with open(path, 'rb') as fl: avatar_hash = Tox.hash(fl.read()) - super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], avatar_hash) + super().__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], avatar_hash) class SendFromBuffer(FileTransfer): diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index e7a0308..12bc60d 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -2,6 +2,8 @@ from file_transfers.file_transfers import * from messenger.messages import * from history.database import MESSAGE_AUTHOR import os +from ui.list_items import * +from PyQt5 import QtWidgets import util.util as util @@ -99,7 +101,7 @@ class FileTransfersHandler: :param already_cancelled: was cancelled by friend """ i = self._get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['CANCELLED']) + TOX_FILE_TRANSFER_STATE['CANCELLED']) if (friend_number, file_number) in self._file_transfers: tr = self._file_transfers[(friend_number, file_number)] if not already_cancelled: @@ -130,7 +132,7 @@ class FileTransfersHandler: tr = self._file_transfers[(friend_number, file_number)] tr.pause(by_friend) t = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER'] - self.get_friend_by_number(friend_number).update_transfer_data(file_number, t) + self._get_friend_by_number(friend_number).update_transfer_data(file_number, t) def resume_transfer(self, friend_number, file_number, by_friend=False): """ @@ -175,7 +177,7 @@ class FileTransfersHandler: self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) if item is not None: rt.set_state_changed_handler(item.update_transfer_state) - self.get_friend_by_number(friend_number).update_transfer_data(file_number, + self._get_friend_by_number(friend_number).update_transfer_data(file_number, TOX_FILE_TRANSFER_STATE['RUNNING']) def send_screenshot(self, data): @@ -192,7 +194,7 @@ class FileTransfersHandler: def send_inline(self, data, file_name, friend_number=None, is_resend=False): friend_number = friend_number or self.get_active_number() - friend = self.get_friend_by_number(friend_number) + friend = self._get_friend_by_number(friend_number) if friend.status is None and not is_resend: m = UnsentFile(file_name, data, time.time()) friend.append_message(m) @@ -224,7 +226,7 @@ class FileTransfersHandler: :param file_id: file id of transfer """ friend_number = self.get_active_number() if number is None else number - friend = self.get_friend_by_number(friend_number) + friend = self._get_friend_by_number(friend_number) if friend.status is None and not is_resend: m = UnsentFile(path, None, time.time()) friend.append_message(m) @@ -265,13 +267,13 @@ class FileTransfersHandler: transfer = self._file_transfers[(friend_number, file_number)] t = type(transfer) if t is ReceiveAvatar: - self.get_friend_by_number(friend_number).load_avatar() + self._get_friend_by_number(friend_number).load_avatar() if friend_number == self.get_active_number() and self.is_active_a_friend(): self.set_active(None) - elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings.get_instance()['allow_inline']): # inline image + elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']): # inline image print('inline') inline = InlineImage(transfer.get_data()) - i = self.get_friend_by_number(friend_number).update_transfer_data(file_number, + i = self._get_friend_by_number(friend_number).update_transfer_data(file_number, TOX_FILE_TRANSFER_STATE['FINISHED'], inline) if friend_number == self.get_active_number() and self.is_active_a_friend(): @@ -284,11 +286,35 @@ class FileTransfersHandler: self._messages.setItemWidget(elem, item) self._messages.scrollToBottom() elif t is not SendAvatar: - self.get_friend_by_number(friend_number).update_transfer_data(file_number, + self._get_friend_by_number(friend_number).update_transfer_data(file_number, TOX_FILE_TRANSFER_STATE['FINISHED']) del self._file_transfers[(friend_number, file_number)] del transfer + def send_files(self, friend_number): + friend = self._get_friend_by_number(friend_number) + friend.remove_invalid_unsent_files() + files = friend.get_unsent_files() + try: + for fl in files: + data = fl.get_data() + if data[1] is not None: + self.send_inline(data[1], data[0], friend_number, True) + else: + self.send_file(data[0], friend_number, True) + friend.clear_unsent_files() + for key in list(self._paused_file_transfers.keys()): + data = self._paused_file_transfers[key] + if not os.path.exists(data[0]): + del self._paused_file_transfers[key] + elif data[1] == friend_number and not data[2]: + self.send_file(data[0], friend_number, True, key) + del self._paused_file_transfers[key] + if friend_number == self.get_active_number() and self.is_active_a_friend(): + self.update() + except Exception as ex: + print('Exception in file sending: ' + str(ex)) + # ----------------------------------------------------------------------------------------------------------------- # Avatars support # ----------------------------------------------------------------------------------------------------------------- @@ -313,7 +339,7 @@ class FileTransfersHandler: self._file_transfers[(friend_number, file_number)] = ra ra.set_transfer_finished_handler(self.transfer_finished) else: - self.get_friend_by_number(friend_number).load_avatar() + self._get_friend_by_number(friend_number).load_avatar() if self.get_active_number() == friend_number and self.is_active_a_friend(): self.set_active(None) diff --git a/toxygen/history/database.py b/toxygen/history/database.py index d7c10eb..188e85b 100644 --- a/toxygen/history/database.py +++ b/toxygen/history/database.py @@ -163,6 +163,9 @@ class Database: db.close() def messages_getter(self, tox_id): + if not self.friend_exists_in_db(tox_id): + self.add_friend_to_db(tox_id) + return Database.MessageGetter(self._path, tox_id) # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/main.py b/toxygen/main.py index 1171aac..5ecbd77 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,6 +1,6 @@ import app from user_data.settings import * -from util.util import remove, get_libs_directory +import util.util as util import argparse @@ -10,8 +10,8 @@ __version__ = '0.5.0' def clean(): """Removes all windows libs from libs folder""" - directory = get_libs_directory() - remove(directory) + directory = util.get_libs_directory() + util.remove(directory) def reset(): diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 41c046d..830e72e 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -48,6 +48,11 @@ class Message: message_id = property(get_message_id) + def get_type(self): + return self._type + + type = property(get_type) + def get_widget(self): if self._widget is None: self._widget = self._create_widget() diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 8cb25e1..cb17398 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -5,13 +5,14 @@ from messenger.messages import * class Messenger(util.ToxSave): - def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider, items_factory): + def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider, items_factory, profile): super().__init__(tox) self._plugin_loader = plugin_loader self._screen = screen self._contacts_manager = contacts_manager self._contacts_provider = contacts_provider self._items_factory = items_factory + self._profile = profile # ----------------------------------------------------------------------------------------------------------------- # Private methods @@ -33,17 +34,16 @@ class Messenger(util.ToxSave): :param message: text of message """ t = util.get_unix_time() + friend = self._get_friend_by_number(friend_number) + text_message = TextMessage(0, message, MessageAuthor(friend.name, MESSAGE_AUTHOR['FRIEND']), t, message_type) if self._contacts_manager.is_friend_active(friend_number): # add message to list - self.create_message_item(message, t, MESSAGE_AUTHOR['FRIEND'], message_type) + self._create_message_item(text_message) self._screen.messages.scrollToBottom() - self._contacts_manager.get_curr_contact().append_message( - TextMessage(message, MESSAGE_AUTHOR['FRIEND'], t, message_type)) + self._contacts_manager.get_curr_contact().append_message(text_message) else: - friend = self.get_friend_by_number(friend_number) friend.inc_messages() - friend.append_message( - TextMessage(message, MESSAGE_AUTHOR['FRIEND'], t, message_type)) + friend.append_message(text_message) if not friend.visibility: self._contacts_manager.update_filtration() @@ -67,7 +67,7 @@ class Messenger(util.ToxSave): text = text[4:] else: message_type = TOX_MESSAGE_TYPE['NORMAL'] - friend = self.get_friend_by_number(friend_number) + friend = self._get_friend_by_number(friend_number) messages = self._split_message(text.encode('utf-8')) t = util.get_unix_time() for message in messages: @@ -76,13 +76,30 @@ class Messenger(util.ToxSave): friend.inc_receipts() else: message_id = 0 - message = TextMessage(message_id, text, MESSAGE_AUTHOR['NOT_SENT'], t, message_type) + message_author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['NOT_SENT']) + message = TextMessage(message_id, text, message_author, t, message_type) friend.append_message(message) if self._contacts_manager.is_friend_active(friend_number): self._create_message_item(message) self._screen.messageEdit.clear() self._screen.messages.scrollToBottom() + def send_messages(self, friend_number): + """ + Send 'offline' messages to friend + """ + friend = self._get_friend_by_number(friend_number) + friend.load_corr() + messages = friend.get_unsent_messages() + try: + for message in messages: + tox_messages = self._split_message(message.text) + for tox_message in tox_messages: + self._tox.friend_send_message(friend_number, message.message_type, tox_message) + friend.inc_receipts() + except Exception as ex: + util.log('Sending pending messages failed with ' + str(ex)) + # ----------------------------------------------------------------------------------------------------------------- # Typing notifications # ----------------------------------------------------------------------------------------------------------------- @@ -131,5 +148,5 @@ class Messenger(util.ToxSave): return messages - def get_friend_by_number(self, friend_number): + def _get_friend_by_number(self, friend_number): return self._contacts_provider.get_friend_by_number(friend_number) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 69a7f68..2d09c82 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -55,20 +55,20 @@ def friend_status(contacts_manager, file_transfer_handler, profile, settings): return wrapped -def friend_connection_status(profile, settings, plugin_loader): +def friend_connection_status(contacts_manager, profile, settings, plugin_loader, file_transfer_handler): def wrapped(tox, friend_number, new_status, user_data): """ Check friend's connection status (offline, udp, tcp) """ print("Friend #{} connection status: {}".format(friend_number, new_status)) - friend = profile.get_friend_by_number(friend_number) + friend = contacts_manager.get_friend_by_number(friend_number) if new_status == TOX_CONNECTION['NONE']: invoke_in_main_thread(profile.friend_exit, friend_number) - invoke_in_main_thread(profile.update_filtration) + invoke_in_main_thread(contacts_manager.update_filtration) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) elif friend.status is None: - invoke_in_main_thread(profile.send_avatar, friend_number) + invoke_in_main_thread(file_transfer_handler.send_avatar, friend_number) invoke_in_main_thread(plugin_loader.friend_online, friend_number) return wrapped @@ -133,9 +133,9 @@ def friend_request(contacts_manager): return wrapped -def friend_typing(contacts_manager): +def friend_typing(messenger): def wrapped(tox, friend_number, typing, user_data): - invoke_in_main_thread(contacts_manager.friend_typing, friend_number, typing) + invoke_in_main_thread(messenger.friend_typing, friend_number, typing) return wrapped @@ -392,11 +392,12 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, # friend callbacks tox.callback_friend_status(friend_status(contacts_manager, file_transfer_handler, profile, settings), 0) tox.callback_friend_message(friend_message(messenger, contacts_manager, profile, settings, main_window, tray), 0) - tox.callback_friend_connection_status(friend_connection_status(profile, settings, plugin_loader), 0) + tox.callback_friend_connection_status(friend_connection_status(contacts_manager, profile, settings, plugin_loader, + file_transfer_handler), 0) tox.callback_friend_name(friend_name(contacts_manager), 0) tox.callback_friend_status_message(friend_status_message(contacts_manager, messenger), 0) tox.callback_friend_request(friend_request(contacts_manager), 0) - tox.callback_friend_typing(friend_typing(contacts_manager), 0) + tox.callback_friend_typing(friend_typing(messenger), 0) tox.callback_friend_read_receipt(friend_read_receipt(contacts_manager), 0) # file transfer diff --git a/toxygen/network/tox_dns.py b/toxygen/network/tox_dns.py index d3d48a1..1a525bc 100644 --- a/toxygen/network/tox_dns.py +++ b/toxygen/network/tox_dns.py @@ -1,59 +1,65 @@ import json import urllib.request -from util.util import log -from user_data import settings +import util.util as util from PyQt5 import QtNetwork, QtCore -def tox_dns(email): - """ - TOX DNS 4 - :param email: data like 'groupbot@toxme.io' - :return: tox id on success else None - """ - site = email.split('@')[1] - data = {"action": 3, "name": "{}".format(email)} - urls = ('https://{}/api'.format(site), 'http://{}/api'.format(site)) - s = settings.Settings.get_instance() - if not s['proxy_type']: # no proxy - for url in urls: - try: - return send_request(url, data) - except Exception as ex: - log('TOX DNS ERROR: ' + str(ex)) - else: # proxy - netman = QtNetwork.QNetworkAccessManager() - proxy = QtNetwork.QNetworkProxy() - proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy if s['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) - proxy.setHostName(s['proxy_host']) - proxy.setPort(s['proxy_port']) - netman.setProxy(proxy) - for url in urls: - try: - request = QtNetwork.QNetworkRequest() - request.setUrl(QtCore.QUrl(url)) - request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, "application/json") - reply = netman.post(request, bytes(json.dumps(data), 'utf-8')) +class ToxDns: - while not reply.isFinished(): - QtCore.QThread.msleep(1) - QtCore.QCoreApplication.processEvents() - data = bytes(reply.readAll().data()) - result = json.loads(str(data, 'utf-8')) - if not result['c']: - return result['tox_id'] - except Exception as ex: - log('TOX DNS ERROR: ' + str(ex)) + def __init__(self, settings): + self._settings = settings - return None # error + @staticmethod + def _send_request(url, data): + req = urllib.request.Request(url) + req.add_header('Content-Type', 'application/json') + response = urllib.request.urlopen(req, bytes(json.dumps(data), 'utf-8')) + res = json.loads(str(response.read(), 'utf-8')) + if not res['c']: + return res['tox_id'] + else: + raise LookupError() + def lookup(self, email): + """ + TOX DNS 4 + :param email: data like 'groupbot@toxme.io' + :return: tox id on success else None + """ + site = email.split('@')[1] + data = {"action": 3, "name": "{}".format(email)} + urls = ('https://{}/api'.format(site), 'http://{}/api'.format(site)) + if not self._settings['proxy_type']: # no proxy + for url in urls: + try: + return self._send_request(url, data) + except Exception as ex: + util.log('TOX DNS ERROR: ' + str(ex)) + else: # proxy + netman = QtNetwork.QNetworkAccessManager() + proxy = QtNetwork.QNetworkProxy() + if self._settings['proxy_type'] == 2: + proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy) + else: + proxy.setType(QtNetwork.QNetworkProxy.HttpProxy) + proxy.setHostName(self._settings['proxy_host']) + proxy.setPort(self._settings['proxy_port']) + netman.setProxy(proxy) + for url in urls: + try: + request = QtNetwork.QNetworkRequest() + request.setUrl(QtCore.QUrl(url)) + request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, "application/json") + reply = netman.post(request, bytes(json.dumps(data), 'utf-8')) -def send_request(url, data): - req = urllib.request.Request(url) - req.add_header('Content-Type', 'application/json') - response = urllib.request.urlopen(req, bytes(json.dumps(data), 'utf-8')) - res = json.loads(str(response.read(), 'utf-8')) - if not res['c']: - return res['tox_id'] - else: - raise LookupError() + while not reply.isFinished(): + QtCore.QThread.msleep(1) + QtCore.QCoreApplication.processEvents() + data = bytes(reply.readAll().data()) + result = json.loads(str(data, 'utf-8')) + if not result['c']: + return result['tox_id'] + except Exception as ex: + util.log('TOX DNS ERROR: ' + str(ex)) + + return None # error diff --git a/toxygen/ui/av_widgets.py b/toxygen/ui/av_widgets.py index 676df78..6fe3f13 100644 --- a/toxygen/ui/av_widgets.py +++ b/toxygen/ui/av_widgets.py @@ -5,7 +5,7 @@ import util import pyaudio import wave from user_data import settings -from util.util import curr_directory +from util.util import * class IncomingCallWidget(widgets.CenteredWidget): diff --git a/toxygen/ui/items_factory.py b/toxygen/ui/items_factory.py index 10ca02b..fe3e4e4 100644 --- a/toxygen/ui/items_factory.py +++ b/toxygen/ui/items_factory.py @@ -1,6 +1,8 @@ from ui.list_items import * from ui.messages_widgets import * +# rename methods + class ItemsFactory: @@ -9,6 +11,11 @@ class ItemsFactory: self._smiley_loader = smiley_loader self._messages = main_screen.messages self._friends_list = main_screen.friends_list + self._message_edit = main_screen.messageEdit + + def _create_message_browser(self, text, width, message_type, parent=None): + return MessageBrowser(self._settings, self._message_edit, self._smiley_loader, self._plugin_loader, + text, width, message_type, parent) def friend_item(self): item = ContactItem(self._settings) @@ -18,8 +25,8 @@ class ItemsFactory: self._friends_list.setItemWidget(elem, item) return item - def message_item(self, message, pixmap=None): - item = MessageItem(message, self._messages) + def message_item(self, message, append=True, pixmap=None): + item = MessageItem(self._settings, self._create_message_browser, message, self._messages) if pixmap is not None: item.set_avatar(pixmap) elem = QtWidgets.QListWidgetItem() diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py index a947919..84ba1b4 100644 --- a/toxygen/ui/list_items.py +++ b/toxygen/ui/list_items.py @@ -2,7 +2,7 @@ from wrapper.toxcore_enums_and_consts import * from PyQt5 import QtCore, QtGui, QtWidgets from contacts import profile from file_transfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR -from util.util import curr_directory, convert_time, curr_time +from util.util import * from ui.widgets import DataLabel, create_menu import html as h import smileys diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index b694574..b4fc083 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -1,7 +1,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets from user_data.settings import * from contacts.profile import Profile -from util.util import curr_directory, copy, get_stickers_directory, join_path +from util.util import * from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio from user_data import toxes diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index 0d65c8a..fbd3154 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -6,9 +6,10 @@ import util.util as util import ui.menu as menu import html as h import re +from messenger.messages import MESSAGE_AUTHOR -class MessageEdit(QtWidgets.QTextBrowser): +class MessageBrowser(QtWidgets.QTextBrowser): def __init__(self, settings, message_edit, smileys_loader, plugin_loader, text, width, message_type, parent=None): super().__init__(parent) @@ -122,7 +123,7 @@ class MessageItem(QtWidgets.QWidget): """ Message in messages list """ - def __init__(self, settings, text_message, parent=None): + def __init__(self, settings, message_browser_factory_method, text_message, parent=None): QtWidgets.QWidget.__init__(self, parent) self.name = widgets.DataLabel(self) self.name.setGeometry(QtCore.QRect(2, 2, 95, 23)) @@ -132,25 +133,26 @@ class MessageItem(QtWidgets.QWidget): font.setPointSize(11) font.setBold(True) self.name.setFont(font) - self.name.setText(text_message.user) + self.name.setText(text_message.author.name) self.time = QtWidgets.QLabel(self) self.time.setGeometry(QtCore.QRect(parent.width() - 60, 0, 50, 25)) font.setPointSize(10) font.setBold(False) self.time.setFont(font) - self._time = time - if not sent: + self._time = text_message.time + if text_message.author.type == MESSAGE_AUTHOR['NOT_SENT']: movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) self.time.setMovie(movie) movie.start() self.t = True else: - self.time.setText(util.convert_time(time)) + self.time.setText(util.convert_time(text_message.time)) self.t = False - self.message = MessageEdit(text, parent.width() - 160, message_type, self) - if message_type != TOX_MESSAGE_TYPE['NORMAL']: + self.message = message_browser_factory_method(text_message.text, parent.width() - 160, + text_message.type, self) + if text_message.type != TOX_MESSAGE_TYPE['NORMAL']: self.name.setStyleSheet("QLabel { color: #5CB3FF; }") self.message.setAlignment(QtCore.Qt.AlignCenter) self.time.setStyleSheet("QLabel { color: #5CB3FF; }") diff --git a/toxygen/ui/tray.py b/toxygen/ui/tray.py index ebdbd33..859eade 100644 --- a/toxygen/ui/tray.py +++ b/toxygen/ui/tray.py @@ -1,6 +1,6 @@ from PyQt5 import QtWidgets, QtGui, QtCore from util.ui import tr -from util.util import get_images_directory +from util.util import * import os.path diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 09fe373..8952956 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -1,6 +1,6 @@ import json import os -from util.util import log, get_base_directory, get_platform, join_path +from util.util import * import pyaudio import smileys.smileys as smileys From ae903cf405556161b959a12f7aebcf0f58a50b00 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 5 May 2018 00:09:33 +0300 Subject: [PATCH 018/217] statuses fixed. events added. --- toxygen/app.py | 14 +++--- toxygen/common/__init__.py | 0 toxygen/common/event.py | 22 ++++++++++ toxygen/contacts/basecontact.py | 43 +++++++++++++++---- toxygen/contacts/contacts_manager.py | 42 ++++++++++++------ .../file_transfers/file_transfers_handler.py | 1 - toxygen/messenger/messenger.py | 9 ++++ toxygen/middleware/callbacks.py | 10 ++--- toxygen/ui/items_factory.py | 8 ++-- toxygen/ui/list_items.py | 6 ++- toxygen/ui/main_screen.py | 2 +- toxygen/ui/menu.py | 7 +-- toxygen/ui/widgets_factory.py | 7 +-- toxygen/util/util.py | 2 + 14 files changed, 126 insertions(+), 47 deletions(-) create mode 100644 toxygen/common/__init__.py create mode 100644 toxygen/common/event.py diff --git a/toxygen/app.py b/toxygen/app.py index 87d3c60..7215a7a 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -27,6 +27,7 @@ from ui.widgets_factory import WidgetsFactory from smileys.smileys import SmileyLoader from ui.items_factory import ItemsFactory from messenger.messenger import Messenger +from network.tox_dns import ToxDns class App: @@ -36,7 +37,7 @@ class App: self._app = self._settings = self._profile_manager = self._plugin_loader = self._messenger = None self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None - self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = None + self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = self._tox_dns = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] self._path = path_to_profile @@ -114,9 +115,6 @@ class App: while True: try: self._app.exec_() - except KeyboardInterrupt: - print('Closing Toxygen...') - break except Exception as ex: util.log('Unhandled exception: ' + str(ex)) else: @@ -288,6 +286,7 @@ class App: def _create_dependencies(self): self._smiley_loader = SmileyLoader(self._settings) + self._tox_dns = ToxDns(self._settings) self._ms = MainWindow(self._settings, self._tray) self._calls_manager = CallsManager(self._tox.AV, self._settings) db = Database(self._path.replace('.tox', '.db'), self._toxes) @@ -298,10 +297,11 @@ class App: self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, - self._contacts_provider, db) + self._contacts_provider, db, self._tox_dns) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) - widgets_factory = WidgetsFactory(self._settings, profile, self._contacts_manager, self._file_transfer_handler, - self._smiley_loader, self._plugin_loader, self._toxes, self._version) + widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, + self._file_transfer_handler, self._smiley_loader, self._plugin_loader, + self._toxes, self._version) self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, self._contacts_provider, items_factory, profile) self._tray = tray.init_tray(profile, self._settings, self._ms) diff --git a/toxygen/common/__init__.py b/toxygen/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/common/event.py b/toxygen/common/event.py new file mode 100644 index 0000000..75c29a5 --- /dev/null +++ b/toxygen/common/event.py @@ -0,0 +1,22 @@ + + +class Event: + + def __init__(self): + self._callbacks = set() + + def __iadd__(self, callback): + self._callbacks.add(callback) + + def __isub__(self, callback): + self.remove_callback(callback) + + def __call__(self, *args, **kwargs): + for callback in self._callbacks: + callback(*args, **kwargs) + + def add_callback(self, callback): + self._callbacks.add(callback) + + def remove_callback(self, callback): + self._callbacks.discard(callback) diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 71690dd..fd084d7 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -2,6 +2,7 @@ from user_data.settings import * from PyQt5 import QtCore, QtGui from wrapper.toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE import util.util as util +import common.event as event class BaseContact: @@ -24,6 +25,9 @@ class BaseContact: self._status, self._widget = None, widget self._tox_id = tox_id self.init_widget() + self._name_changed_event = event.Event() + self._status_message_changed_event = event.Event() + self._status_changed_event = event.Event() # ----------------------------------------------------------------------------------------------------------------- # Name - current name or alias of user @@ -33,12 +37,20 @@ class BaseContact: return self._name def set_name(self, value): - self._name = str(value, 'utf-8') - self._widget.name.setText(self._name) - self._widget.name.repaint() + value = str(value, 'utf-8') + if self._name != value: + self._name = value + self._widget.name.setText(self._name) + self._widget.name.repaint() + self._name_changed_event(self._name) name = property(get_name, set_name) + def get_name_changed_event(self): + return self._name_changed_event + + name_changed_event = property(get_name_changed_event) + # ----------------------------------------------------------------------------------------------------------------- # Status message # ----------------------------------------------------------------------------------------------------------------- @@ -47,12 +59,20 @@ class BaseContact: return self._status_message def set_status_message(self, value): - self._status_message = str(value, 'utf-8') - self._widget.status_message.setText(self._status_message) - self._widget.status_message.repaint() + value = str(value, 'utf-8') + if self._status_message != value: + self._status_message = value + self._widget.status_message.setText(self._status_message) + self._widget.status_message.repaint() + self._status_message_changed_event(self._status_message) status_message = property(get_status_message, set_status_message) + def get_status_message_changed_event(self): + return self._status_message_changed_event + + status_message_changed_event = property(get_status_message_changed_event) + # ----------------------------------------------------------------------------------------------------------------- # Status # ----------------------------------------------------------------------------------------------------------------- @@ -61,11 +81,18 @@ class BaseContact: return self._status def set_status(self, value): - self._status = value - self._widget.connection_status.update(value) + if self._status != value: + self._status = value + self._widget.connection_status.update(value) + self._status_changed_event(self._status) status = property(get_status, set_status) + def get_status_changed_event(self): + return self._status_changed_event + + status_changed_event = property(get_status_changed_event) + # ----------------------------------------------------------------------------------------------------------------- # TOX ID. WARNING: for friend it will return public key, for profile - full address # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 8fb1b92..818ae88 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -12,12 +12,13 @@ class ContactsManager: Represents contacts list. """ - def __init__(self, tox, settings, screen, profile_manager, contact_provider, db): + def __init__(self, tox, settings, screen, profile_manager, contact_provider, db, tox_dns): self._tox = tox self._settings = settings self._screen = screen self._profile_manager = profile_manager self._contact_provider = contact_provider + self._tox_dns = tox_dns self._messages = screen.messages self._contacts, self._active_contact = [], -1 self._sorting = settings['sorting'] @@ -74,13 +75,17 @@ class ContactsManager: try: # self.send_typing(False) # TODO: fix self._screen.typing.setVisible(False) + current_contact = self.get_curr_contact() + if current_contact is not None: + self._unsubscribe_from_events(current_contact) if value is not None: if self._active_contact + 1 and self._active_contact != value: try: - self.get_curr_contact().curr_text = self._screen.messageEdit.toPlainText() + current_contact.curr_text = self._screen.messageEdit.toPlainText() except: pass friend = self._contacts[value] + self._subscribe_to_events(friend) friend.remove_invalid_unsent_files() if self._active_contact != value: self._screen.messageEdit.setPlainText(friend.curr_text) @@ -133,8 +138,7 @@ class ContactsManager: else: friend = self.get_curr_contact() # TODO: to separate method - self._screen.account_name.setText(friend.name) - self._screen.account_status.setText(friend.status_message) + self._screen.account_status.setToolTip(friend.get_full_status()) avatar_path = friend.get_avatar_path() pixmap = QtGui.QPixmap(avatar_path) @@ -244,14 +248,15 @@ class ContactsManager: friend.set_name(name) name = str(name, 'utf-8') if friend.name == name and tmp != name: + # TODO: move to friend? message = util_ui.tr('User {} is now known as {}') - message = message.format(tmp, name) - friend.append_message(InfoMessage(0, message, util.get_unix_time())) - friend.actions = True - if number == self.get_active_number(): - self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() - self.set_active(None) + # message = message.format(tmp, name) + # friend.append_message(InfoMessage(0, message, util.get_unix_time())) + # friend.actions = True + # if number == self.get_active_number(): + # self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + # self._messages.scrollToBottom() + # self.set_active(None) # ----------------------------------------------------------------------------------------------------------------- # Work with friends (remove, block, set alias, get public key) @@ -369,7 +374,7 @@ class ContactsManager: try: message = message or 'Hello! Add me to your contact list please' if '@' in tox_id: # value like groupbot@toxme.io - tox_id = tox_dns(tox_id) + tox_id = self._tox_dns.lookup(tox_id) if tox_id is None: raise Exception('TOX DNS lookup failed') if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key @@ -428,3 +433,16 @@ class ContactsManager: def _load_groups(self): self._contacts.extend(self._contact_provider.get_all_groups()) + + # ----------------------------------------------------------------------------------------------------------------- + # Current contact subscriptions + # ----------------------------------------------------------------------------------------------------------------- + + def _subscribe_to_events(self, contact): + contact.name_changed_event.add_callback(self._current_contact_name_changed) + + def _unsubscribe_from_events(self, contact): + contact.name_changed_event.remove_callback(self._current_contact_name_changed) + + def _current_contact_name_changed(self, name): + self._screen.account_name.setText(name) diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 12bc60d..5d9b052 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -1,7 +1,6 @@ from file_transfers.file_transfers import * from messenger.messages import * from history.database import MESSAGE_AUTHOR -import os from ui.list_items import * from PyQt5 import QtWidgets import util.util as util diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index cb17398..1021629 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -3,6 +3,8 @@ from wrapper.toxcore_enums_and_consts import * from messenger.messages import * +# TODO: sub profile name changed event? + class Messenger(util.ToxSave): def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider, items_factory, profile): @@ -100,6 +102,13 @@ class Messenger(util.ToxSave): except Exception as ex: util.log('Sending pending messages failed with ' + str(ex)) + # ----------------------------------------------------------------------------------------------------------------- + # Message receipts + # ----------------------------------------------------------------------------------------------------------------- + + def receipt(self, friend_number, message_id): + pass # TODO: process + # ----------------------------------------------------------------------------------------------------------------- # Typing notifications # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 2d09c82..912696b 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -12,7 +12,7 @@ from middleware.threads import invoke_in_main_thread, execute from notifications.tray import tray_notification from notifications.sound import * -# TODO: gc callbacks and refactoring +# TODO: gc callbacks and refactoring. Use contact provider instead of manager # ----------------------------------------------------------------------------------------------------------------- # Callbacks - current user @@ -140,11 +140,9 @@ def friend_typing(messenger): return wrapped -def friend_read_receipt(contacts_manager): +def friend_read_receipt(messenger): def wrapped(tox, friend_number, message_id, user_data): - contacts_manager.get_friend_by_number(friend_number).dec_receipt() - if friend_number == contacts_manager.get_active_number(): - invoke_in_main_thread(contacts_manager.receipt) + invoke_in_main_thread(messenger.receipt, friend_number, message_id) return wrapped @@ -398,7 +396,7 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_friend_status_message(friend_status_message(contacts_manager, messenger), 0) tox.callback_friend_request(friend_request(contacts_manager), 0) tox.callback_friend_typing(friend_typing(messenger), 0) - tox.callback_friend_read_receipt(friend_read_receipt(contacts_manager), 0) + tox.callback_friend_read_receipt(friend_read_receipt(messenger), 0) # file transfer tox.callback_file_recv(tox_file_recv(main_window, tray, profile, file_transfer_handler, diff --git a/toxygen/ui/items_factory.py b/toxygen/ui/items_factory.py index fe3e4e4..1c4e7a9 100644 --- a/toxygen/ui/items_factory.py +++ b/toxygen/ui/items_factory.py @@ -13,10 +13,6 @@ class ItemsFactory: self._friends_list = main_screen.friends_list self._message_edit = main_screen.messageEdit - def _create_message_browser(self, text, width, message_type, parent=None): - return MessageBrowser(self._settings, self._message_edit, self._smiley_loader, self._plugin_loader, - text, width, message_type, parent) - def friend_item(self): item = ContactItem(self._settings) elem = QtWidgets.QListWidgetItem(self._friends_list) @@ -75,3 +71,7 @@ class ItemsFactory: self._messages.insertItem(0, elem) self._messages.setItemWidget(elem, item) return item + + def _create_message_browser(self, text, width, message_type, parent=None): + return MessageBrowser(self._settings, self._message_edit, self._smiley_loader, self._plugin_loader, + text, width, message_type, parent) diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py index 84ba1b4..d075391 100644 --- a/toxygen/ui/list_items.py +++ b/toxygen/ui/list_items.py @@ -71,7 +71,7 @@ class StatusCircle(QtWidgets.QWidget): self.label.setGeometry(QtCore.QRect(0, 0, 32, 32)) else: self.label.setGeometry(QtCore.QRect(2, 0, 32, 32)) - pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(name)) + pixmap = QtGui.QPixmap(join_path(get_images_directory(), '{}.png'.format(name))) self.label.setPixmap(pixmap) @@ -121,6 +121,7 @@ class FileTransferItem(QtWidgets.QListWidget): self.name.setGeometry(QtCore.QRect(3, 7, 95, 25)) self.name.setTextFormat(QtCore.Qt.PlainText) font = QtGui.QFont() + # FIXME font.setFamily(settings.Settings.get_instance()['font']) font.setPointSize(11) font.setBold(True) @@ -281,7 +282,7 @@ class UnsentFileItem(FileTransferItem): TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'], width, parent) self._time = time self.pb.setVisible(False) - movie = QtGui.QMovie(curr_directory() + '/images/spinner.gif') + movie = QtGui.QMovie(join_path(get_images_directory(), 'spinner.gif')) self.time.setMovie(movie) movie.start() @@ -331,6 +332,7 @@ class InlineImageItem(QtWidgets.QScrollArea): self._full_size = not self._full_size self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) elif event.button() == QtCore.Qt.RightButton: # save inline + # TODO: dialog directory = QtWidgets.QFileDialog.getExistingDirectory(self, QtWidgets.QApplication.translate("MainWindow", 'Choose folder'), diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 05007f9..412f487 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -444,7 +444,7 @@ class MainWindow(QtWidgets.QMainWindow): def create_gc(self): self.profile.create_group_chat() - def profile_settings(self): + def profile_settings(self, *args): self._modal_window = self._widget_factory.create_profile_settings_window() self._modal_window.show() diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index b4fc083..33f0690 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -84,9 +84,10 @@ class AddContact(CenteredWidget): class ProfileSettings(CenteredWidget): """Form with profile settings such as name, status, TOX ID""" - def __init__(self, profile, settings, toxes): + def __init__(self, profile, profile_manager, settings, toxes): super().__init__() self._profile = profile + self._profile_manager = profile_manager self._settings = settings self._toxes = toxes self.initUI() @@ -166,7 +167,7 @@ class ProfileSettings(CenteredWidget): self.default = QtWidgets.QPushButton(self) self.default.setGeometry(QtCore.QRect(40, 550, 620, 30)) auto_profile = Settings.get_auto_profile() - self.auto = path + name == ProfileManager.get_path() + Settings.get_instance().name + # self.auto = path + name == ProfileManager.get_path() + Settings.get_instance().name self.default.clicked.connect(self.auto_profile) self.retranslateUi() if self._profile.status is not None: @@ -243,7 +244,7 @@ class ProfileSettings(CenteredWidget): self.copy_pk.setIconSize(QtCore.QSize(10, 10)) def new_no_spam(self): - self.tox_id.setText(Profile.get_instance().new_nospam()) + self.tox_id.setText(self._profile.new_nospam()) def reset_avatar(self): self._profile.reset_avatar() diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 1bba4a7..0c0bcbc 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -4,10 +4,11 @@ from ui.menu import * class WidgetsFactory: - def __init__(self, settings, profile, contacts_manager, file_transfer_handler, smiley_loader, plugin_loader, - toxes, version): + def __init__(self, settings, profile, profile_manager, contacts_manager, file_transfer_handler, smiley_loader, + plugin_loader, toxes, version): self._settings = settings self._profile = profile + self._profile_manager = profile_manager self._contacts_manager = contacts_manager self._file_transfer_handler = file_transfer_handler self._smiley_loader = smiley_loader @@ -25,7 +26,7 @@ class WidgetsFactory: return WelcomeScreen(self._settings) def create_profile_settings_window(self): - return ProfileSettings(self._profile, self._settings, self._toxes) + return ProfileSettings(self._profile, self._profile_manager, self._settings, self._toxes) def create_network_settings_window(self): return NetworkSettings(self._settings, self._profile.reset) diff --git a/toxygen/util/util.py b/toxygen/util/util.py index 09e6877..329285c 100644 --- a/toxygen/util/util.py +++ b/toxygen/util/util.py @@ -158,6 +158,8 @@ def get_platform(): return platform.system() +# TODO: to common + class ToxSave: def __init__(self, tox): From 729bd84d2baaaee371e948bd0a9b12148db5564d Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 10 May 2018 20:47:34 +0300 Subject: [PATCH 019/217] utils refactoring --- tests/tests.py | 2 +- toxygen/app.py | 6 ++--- toxygen/bootstrap/bootstrap.py | 2 +- toxygen/common/event.py | 2 +- toxygen/common/tox_save.py | 9 ++++++++ toxygen/contacts/basecontact.py | 21 +++++++++++------ toxygen/contacts/contact.py | 2 +- toxygen/contacts/contact_provider.py | 7 +++--- toxygen/contacts/contacts_manager.py | 23 ++++++++++++++++--- toxygen/contacts/group_chat.py | 2 +- toxygen/contacts/profile.py | 2 +- .../file_transfers/file_transfers_handler.py | 2 +- toxygen/history/database.py | 2 +- toxygen/history/history_logs_generators.py | 2 +- toxygen/main.py | 2 +- toxygen/messenger/messenger.py | 5 ++-- toxygen/middleware/callbacks.py | 4 ++-- toxygen/middleware/threads.py | 2 +- toxygen/network/tox_dns.py | 2 +- toxygen/notifications/sound.py | 4 ++-- toxygen/plugin_support/plugin_support.py | 2 +- toxygen/smileys/smileys.py | 2 +- toxygen/stickers/stickers.py | 2 +- toxygen/ui/av_widgets.py | 10 ++++---- toxygen/ui/create_profile_screen.py | 4 ++-- toxygen/ui/list_items.py | 2 +- toxygen/ui/main_screen.py | 4 ++-- toxygen/ui/main_screen_widgets.py | 4 ++-- toxygen/ui/menu.py | 4 ++-- toxygen/ui/messages_widgets.py | 4 ++-- toxygen/ui/tray.py | 4 ++-- toxygen/ui/widgets.py | 2 +- toxygen/updater/updater.py | 5 ++-- toxygen/user_data/profile_manager.py | 2 +- toxygen/user_data/settings.py | 2 +- toxygen/{util => utils}/__init__.py | 0 toxygen/{util => utils}/ui.py | 2 +- toxygen/{util => utils}/util.py | 11 --------- toxygen/wrapper/libtox.py | 2 +- 39 files changed, 97 insertions(+), 74 deletions(-) create mode 100644 toxygen/common/tox_save.py rename toxygen/{util => utils}/__init__.py (100%) rename toxygen/{util => utils}/ui.py (98%) rename toxygen/{util => utils}/util.py (95%) diff --git a/tests/tests.py b/tests/tests.py index b61553e..1047de4 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -4,7 +4,7 @@ from db.database import History from toxygen.smileys import SmileyLoader from messenger.messages import * import user_data.toxes as encr -import toxygen.util as util +import toxygen.utils as util import time diff --git a/toxygen/app.py b/toxygen/app.py index 7215a7a..d5fd513 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -2,7 +2,7 @@ from middleware import threads import middleware.callbacks as callbacks from PyQt5 import QtWidgets, QtGui, QtCore import ui.password_screen as password_screen -from util.util import * +from utils.util import * import updater.updater as updater import os from middleware.tox_factory import tox_factory @@ -14,8 +14,8 @@ from user_data.profile_manager import ProfileManager from plugin_support.plugin_support import PluginLoader from ui.main_screen import MainWindow from ui import tray -import util.ui as util_ui -import util.util as util +import utils.ui as util_ui +import utils.util as util from contacts.profile import Profile from file_transfers.file_transfers_handler import FileTransfersHandler from contacts.contact_provider import ContactProvider diff --git a/toxygen/bootstrap/bootstrap.py b/toxygen/bootstrap/bootstrap.py index 2e5d3dc..6c34e0e 100644 --- a/toxygen/bootstrap/bootstrap.py +++ b/toxygen/bootstrap/bootstrap.py @@ -1,6 +1,6 @@ import random import urllib.request -from util.util import * +from utils.util import * from PyQt5 import QtNetwork, QtCore import json diff --git a/toxygen/common/event.py b/toxygen/common/event.py index 75c29a5..e3ecbf9 100644 --- a/toxygen/common/event.py +++ b/toxygen/common/event.py @@ -6,7 +6,7 @@ class Event: self._callbacks = set() def __iadd__(self, callback): - self._callbacks.add(callback) + self.add_callback(callback) def __isub__(self, callback): self.remove_callback(callback) diff --git a/toxygen/common/tox_save.py b/toxygen/common/tox_save.py new file mode 100644 index 0000000..5d4cee0 --- /dev/null +++ b/toxygen/common/tox_save.py @@ -0,0 +1,9 @@ + + +class ToxSave: + + def __init__(self, tox): + self._tox = tox + + def set_tox(self, tox): + self._tox = tox diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index fd084d7..44a18b3 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -1,7 +1,7 @@ from user_data.settings import * from PyQt5 import QtCore, QtGui from wrapper.toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE -import util.util as util +import utils.util as util import common.event as event @@ -24,10 +24,11 @@ class BaseContact: self._name, self._status_message = name, status_message self._status, self._widget = None, widget self._tox_id = tox_id - self.init_widget() self._name_changed_event = event.Event() self._status_message_changed_event = event.Event() self._status_changed_event = event.Event() + self._avatar_changed_event = event.Event() + self.init_widget() # ----------------------------------------------------------------------------------------------------------------- # Name - current name or alias of user @@ -116,6 +117,7 @@ class BaseContact: self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) self._widget.avatar_label.repaint() + self._avatar_changed_event(avatar_path) def reset_avatar(self): avatar_path = self.get_avatar_path() @@ -143,13 +145,18 @@ class BaseContact: @staticmethod def get_default_avatar_name(): return 'avatar.png' + + def get_avatar_changed_event(self): + return self._avatar_changed_event + + avatar_changed_event = property(get_avatar_changed_event) + # ----------------------------------------------------------------------------------------------------------------- # Widgets # ----------------------------------------------------------------------------------------------------------------- def init_widget(self): - if self._widget is not None: - self._widget.name.setText(self._name) - self._widget.status_message.setText(self._status_message) - self._widget.connection_status.update(self._status) - self.load_avatar() + self._widget.name.setText(self._name) + self._widget.status_message.setText(self._status_message) + self._widget.connection_status.update(self._status) + self.load_avatar() diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index c9274ac..43f7728 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -1,6 +1,6 @@ from history.database import * from contacts import basecontact, common -import util.util as util +import utils.util as util from messenger.messages import * from file_transfers import file_transfers as ft import re diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py index 82cf6e7..5042f97 100644 --- a/toxygen/contacts/contact_provider.py +++ b/toxygen/contacts/contact_provider.py @@ -1,7 +1,8 @@ -import util.util as util +import utils.util as util +import common.tox_save as tox_save -class ContactProvider(util.ToxSave): +class ContactProvider(tox_save.ToxSave): def __init__(self, tox, friend_factory): super().__init__(tox) @@ -50,7 +51,7 @@ class ContactProvider(util.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def get_all(self): - return self.get_all_friends() + self.get_all_gc() + return self.get_all_friends() + self.get_all_groups() # ----------------------------------------------------------------------------------------------------------------- # Caching diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 818ae88..464d448 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,8 +1,7 @@ -import util.util as util -import util.ui as util_ui +import utils.util as util +import utils.ui as util_ui from contacts.friend import Friend from PyQt5 import QtCore, QtGui -from messenger.messages import * from wrapper.toxcore_enums_and_consts import * from history.history_loader import HistoryLoader @@ -440,9 +439,27 @@ class ContactsManager: def _subscribe_to_events(self, contact): contact.name_changed_event.add_callback(self._current_contact_name_changed) + contact.status_changed_event.add_callback(self._current_contact_status_changed) + contact.status_message_changed_event.add_callback(self._current_contact_status_message_changed) + contact.avatar_changed_event.add_callback(self._current_contact_avatar_changed) def _unsubscribe_from_events(self, contact): contact.name_changed_event.remove_callback(self._current_contact_name_changed) + contact.status_changed_event.remove_callback(self._current_contact_status_changed) + contact.status_message_changed_event.remove_callback(self._current_contact_status_message_changed) + contact.avatar_changed_event.remove_callback(self._current_contact_avatar_changed) def _current_contact_name_changed(self, name): self._screen.account_name.setText(name) + + def _current_contact_status_changed(self, status): + pass + + def _current_contact_status_message_changed(self, status_message): + self._screen.account_status.setText(status_message) + + def _current_contact_avatar_changed(self, avatar_path): + width = self._screen.account_avatar.width() + pixmap = QtGui.QPixmap(avatar_path) + self._screen.account_avatar.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation)) diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index b247281..5121be7 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -1,5 +1,5 @@ from contacts import contact -import util.util as util +import utils.util as util from PyQt5 import QtGui, QtCore from wrapper import toxcore_enums_and_consts as constants diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index cfeae94..8102f56 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -5,7 +5,7 @@ from file_transfers.file_transfers import * import time from contacts import basecontact from contacts.group_chat import * -import util.ui as util_ui +import utils.ui as util_ui class Profile(basecontact.BaseContact): diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 5d9b052..c4ab8b0 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -3,7 +3,7 @@ from messenger.messages import * from history.database import MESSAGE_AUTHOR from ui.list_items import * from PyQt5 import QtWidgets -import util.util as util +import utils.util as util class FileTransfersHandler: diff --git a/toxygen/history/database.py b/toxygen/history/database.py index 188e85b..bf593d8 100644 --- a/toxygen/history/database.py +++ b/toxygen/history/database.py @@ -1,6 +1,6 @@ from sqlite3 import connect import os.path -import util.util as util +import utils.util as util TIMEOUT = 11 diff --git a/toxygen/history/history_logs_generators.py b/toxygen/history/history_logs_generators.py index 3e46562..b8d0a56 100644 --- a/toxygen/history/history_logs_generators.py +++ b/toxygen/history/history_logs_generators.py @@ -1,5 +1,5 @@ from messenger.messages import * -import util.util as util +import utils.util as util class HistoryLogsGenerator: diff --git a/toxygen/main.py b/toxygen/main.py index 5ecbd77..232ad93 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,6 +1,6 @@ import app from user_data.settings import * -import util.util as util +import utils.util as util import argparse diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 1021629..5a94f7f 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -1,11 +1,12 @@ -import util.util as util +import utils.util as util +import common.tox_save as tox_save from wrapper.toxcore_enums_and_consts import * from messenger.messages import * # TODO: sub profile name changed event? -class Messenger(util.ToxSave): +class Messenger(tox_save.ToxSave): def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider, items_factory, profile): super().__init__(tox) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 912696b..7b7b759 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -4,8 +4,8 @@ from contacts.profile import Profile from wrapper.toxcore_enums_and_consts import * from wrapper.toxav_enums import * from wrapper.tox import bin_to_string -import util.ui as util_ui -import util.util as util +import utils.ui as util_ui +import utils.util as util import cv2 import numpy as np from middleware.threads import invoke_in_main_thread, execute diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py index e654a66..2e432b6 100644 --- a/toxygen/middleware/threads.py +++ b/toxygen/middleware/threads.py @@ -1,7 +1,7 @@ from bootstrap.bootstrap import * import threading import queue -from util import util +from utils import util import time diff --git a/toxygen/network/tox_dns.py b/toxygen/network/tox_dns.py index 1a525bc..02e97f5 100644 --- a/toxygen/network/tox_dns.py +++ b/toxygen/network/tox_dns.py @@ -1,6 +1,6 @@ import json import urllib.request -import util.util as util +import utils.util as util from PyQt5 import QtNetwork, QtCore diff --git a/toxygen/notifications/sound.py b/toxygen/notifications/sound.py index a0b93f0..361cd05 100644 --- a/toxygen/notifications/sound.py +++ b/toxygen/notifications/sound.py @@ -1,4 +1,4 @@ -import util.util +import utils.util import wave import pyaudio import os.path @@ -51,4 +51,4 @@ def sound_notification(t): def get_file_path(file_name): - return os.path.join(util.util.get_sounds_directory(), file_name) + return os.path.join(utils.util.get_sounds_directory(), file_name) diff --git a/toxygen/plugin_support/plugin_support.py b/toxygen/plugin_support/plugin_support.py index 6f2af9c..bd50cb7 100644 --- a/toxygen/plugin_support/plugin_support.py +++ b/toxygen/plugin_support/plugin_support.py @@ -1,4 +1,4 @@ -import util.util as util +import utils.util as util from contacts import profile import os import importlib diff --git a/toxygen/smileys/smileys.py b/toxygen/smileys/smileys.py index fc40a69..9027bcb 100644 --- a/toxygen/smileys/smileys.py +++ b/toxygen/smileys/smileys.py @@ -1,4 +1,4 @@ -from util import util +from utils import util import json import os from collections import OrderedDict diff --git a/toxygen/stickers/stickers.py b/toxygen/stickers/stickers.py index 5ad6aa1..a406b6c 100644 --- a/toxygen/stickers/stickers.py +++ b/toxygen/stickers/stickers.py @@ -1,5 +1,5 @@ import os -import util.util as util +import utils.util as util def load_stickers(): diff --git a/toxygen/ui/av_widgets.py b/toxygen/ui/av_widgets.py index 6fe3f13..f9eb6ed 100644 --- a/toxygen/ui/av_widgets.py +++ b/toxygen/ui/av_widgets.py @@ -1,11 +1,11 @@ from PyQt5 import QtCore, QtGui, QtWidgets from ui import widgets from contacts import profile -import util +import utils import pyaudio import wave from user_data import settings -from util.util import * +from utils.util import * class IncomingCallWidget(widgets.CenteredWidget): @@ -34,13 +34,13 @@ class IncomingCallWidget(widgets.CenteredWidget): self.accept_video.setGeometry(QtCore.QRect(170, 100, 150, 150)) self.decline = QtWidgets.QPushButton(self) self.decline.setGeometry(QtCore.QRect(320, 100, 150, 150)) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/accept_audio.png') + pixmap = QtGui.QPixmap(utils.curr_directory() + '/images/accept_audio.png') icon = QtGui.QIcon(pixmap) self.accept_audio.setIcon(icon) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/accept_video.png') + pixmap = QtGui.QPixmap(utils.curr_directory() + '/images/accept_video.png') icon = QtGui.QIcon(pixmap) self.accept_video.setIcon(icon) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/decline_call.png') + pixmap = QtGui.QPixmap(utils.curr_directory() + '/images/decline_call.png') icon = QtGui.QIcon(pixmap) self.decline.setIcon(icon) self.accept_audio.setIconSize(QtCore.QSize(150, 150)) diff --git a/toxygen/ui/create_profile_screen.py b/toxygen/ui/create_profile_screen.py index f461719..40c9091 100644 --- a/toxygen/ui/create_profile_screen.py +++ b/toxygen/ui/create_profile_screen.py @@ -1,7 +1,7 @@ from ui.widgets import * from PyQt5 import uic -import util.util as util -import util.ui as util_ui +import utils.util as util +import utils.ui as util_ui class CreateProfileScreenResult: diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py index d075391..a305ffc 100644 --- a/toxygen/ui/list_items.py +++ b/toxygen/ui/list_items.py @@ -2,7 +2,7 @@ from wrapper.toxcore_enums_and_consts import * from PyQt5 import QtCore, QtGui, QtWidgets from contacts import profile from file_transfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR -from util.util import * +from utils.util import * from ui.widgets import DataLabel, create_menu import html as h import smileys diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 412f487..54ca652 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -2,8 +2,8 @@ from contacts.profile import * from ui.list_items import * from ui.widgets import MultilineEdit, ComboBox from ui.main_screen_widgets import * -import util.util as util -import util.ui as util_ui +import utils.util as util +import utils.ui as util_ui class MainWindow(QtWidgets.QMainWindow): diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index 665e5dc..e739167 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -2,8 +2,8 @@ from PyQt5 import QtCore, QtGui, QtWidgets from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit from contacts.profile import Profile import urllib -import util.util as util -import util.ui as util_ui +import utils.util as util +import utils.ui as util_ui from stickers.stickers import load_stickers diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 33f0690..b28ab3b 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -1,12 +1,12 @@ from PyQt5 import QtCore, QtGui, QtWidgets from user_data.settings import * from contacts.profile import Profile -from util.util import * +from utils.util import * from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio from user_data import toxes import updater.updater as updater -import util.ui as util_ui +import utils.ui as util_ui class AddContact(CenteredWidget): diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index fbd3154..0e31891 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -1,8 +1,8 @@ from PyQt5 import QtWidgets, QtGui, QtCore from wrapper.toxcore_enums_and_consts import * import ui.widgets as widgets -import util.ui as util_ui -import util.util as util +import utils.ui as util_ui +import utils.util as util import ui.menu as menu import html as h import re diff --git a/toxygen/ui/tray.py b/toxygen/ui/tray.py index 859eade..2003053 100644 --- a/toxygen/ui/tray.py +++ b/toxygen/ui/tray.py @@ -1,6 +1,6 @@ from PyQt5 import QtWidgets, QtGui, QtCore -from util.ui import tr -from util.util import * +from utils.ui import tr +from utils.util import * import os.path diff --git a/toxygen/ui/widgets.py b/toxygen/ui/widgets.py index 7585f06..ec6c3e4 100644 --- a/toxygen/ui/widgets.py +++ b/toxygen/ui/widgets.py @@ -1,5 +1,5 @@ from PyQt5 import QtCore, QtGui, QtWidgets -import util.ui as util_ui +import utils.ui as util_ui class DataLabel(QtWidgets.QLabel): diff --git a/toxygen/updater/updater.py b/toxygen/updater/updater.py index f274161..329353c 100644 --- a/toxygen/updater/updater.py +++ b/toxygen/updater/updater.py @@ -1,7 +1,6 @@ -import util.util as util -import util.ui as util_ui +import utils.util as util +import utils.ui as util_ui import os -from user_data import settings import platform import urllib from PyQt5 import QtNetwork, QtCore diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index 6107d43..a95253f 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -1,4 +1,4 @@ -import util.util as util +import utils.util as util import os from user_data.settings import Settings diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 8952956..f9390f3 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -1,6 +1,6 @@ import json import os -from util.util import * +from utils.util import * import pyaudio import smileys.smileys as smileys diff --git a/toxygen/util/__init__.py b/toxygen/utils/__init__.py similarity index 100% rename from toxygen/util/__init__.py rename to toxygen/utils/__init__.py diff --git a/toxygen/util/ui.py b/toxygen/utils/ui.py similarity index 98% rename from toxygen/util/ui.py rename to toxygen/utils/ui.py index b2b5712..cdb5f9a 100644 --- a/toxygen/util/ui.py +++ b/toxygen/utils/ui.py @@ -1,5 +1,5 @@ from PyQt5 import QtWidgets -import util.util as util +import utils.util as util def tr(s): diff --git a/toxygen/util/util.py b/toxygen/utils/util.py similarity index 95% rename from toxygen/util/util.py rename to toxygen/utils/util.py index 329285c..5790a5b 100644 --- a/toxygen/util/util.py +++ b/toxygen/utils/util.py @@ -156,14 +156,3 @@ def is_re_valid(regex): def get_platform(): return platform.system() - - -# TODO: to common - -class ToxSave: - - def __init__(self, tox): - self._tox = tox - - def set_tox(self, tox): - self._tox = tox diff --git a/toxygen/wrapper/libtox.py b/toxygen/wrapper/libtox.py index a04746c..402aa5f 100644 --- a/toxygen/wrapper/libtox.py +++ b/toxygen/wrapper/libtox.py @@ -1,6 +1,6 @@ from platform import system from ctypes import CDLL -import util.util as util +import utils.util as util class LibToxCore: From 25dbb85ef06280343aa41bac0403593016a21d62 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 10 May 2018 23:54:51 +0300 Subject: [PATCH 020/217] various fixes. profile settings and add account fixes --- toxygen/app.py | 87 ++++++++++--------- toxygen/contacts/basecontact.py | 20 +++-- toxygen/file_transfers/file_transfers.py | 15 ++-- .../file_transfers/file_transfers_handler.py | 9 +- toxygen/ui/main_screen.py | 7 +- toxygen/ui/menu.py | 37 ++++---- toxygen/user_data/profile_manager.py | 3 + toxygen/user_data/settings.py | 23 ++--- 8 files changed, 109 insertions(+), 92 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index d5fd513..713d250 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -2,7 +2,6 @@ from middleware import threads import middleware.callbacks as callbacks from PyQt5 import QtWidgets, QtGui, QtCore import ui.password_screen as password_screen -from utils.util import * import updater.updater as updater import os from middleware.tox_factory import tox_factory @@ -53,48 +52,20 @@ class App: self._app = QtWidgets.QApplication([]) self._load_icon() - if get_platform() == 'Linux': + if util.get_platform() == 'Linux': QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) self._load_base_style() - encrypt_save = tox_encrypt_save.ToxEncryptSave() - self._toxes = user_data.toxes.ToxES(encrypt_save) - - if self._path is not None: # toxygen was started with path to profile - self._load_existing_profile(self._path) - else: - auto_profile = Settings.get_auto_profile() - if auto_profile is None: # no default profile - result = self._select_profile() - if result is None: - return - if result.is_new_profile(): # create new profile - self._create_new_profile(result.profile_path) - else: # load existing profile - self._load_existing_profile(result.profile_path) - self._path = result.profile_path - else: # default profile - path, name = auto_profile - self._path = os.path.join(path, name + '.tox') - self._load_existing_profile(self._path) - - if Settings.is_active_profile(self._path): # profile is in use - profile_name = get_profile_name_from_path(self._path) - title = util_ui.tr('Profile {}').format(profile_name) - text = util_ui.tr('Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') - reply = util_ui.question(text, title) - if not reply: - return - - self._settings.set_active_profile() - - self._load_app_styles() - self._load_app_translations() + if not self._select_and_load_profile(): + return if self._try_to_update(): return + self._load_app_styles() + self._load_app_translations() + self._create_dependencies() self._start_threads() @@ -133,7 +104,7 @@ class App: # ----------------------------------------------------------------------------------------------------------------- def _load_base_style(self): - with open(join_path(get_styles_directory(), 'dark_style.qss')) as fl: + with open(util.join_path(util.get_styles_directory(), 'dark_style.qss')) as fl: style = fl.read() self._app.setStyleSheet(style) @@ -143,7 +114,7 @@ class App: return for theme in self._settings.built_in_themes().keys(): if self._settings['theme'] == theme: - with open(curr_directory(__file__) + self._settings.built_in_themes()[theme]) as fl: + with open(util.curr_directory(__file__) + self._settings.built_in_themes()[theme]) as fl: style = fl.read() self._app.setStyleSheet(style) @@ -152,12 +123,12 @@ class App: if current_language in supported_languages: lang_path = supported_languages[current_language] translator = QtCore.QTranslator() - translator.load(get_translations_directory() + lang_path) + translator.load(util.get_translations_directory() + lang_path) self._app.installTranslator(translator) self._app.translator = translator def _load_icon(self): - icon_file = os.path.join(get_images_directory(), 'icon.png') + icon_file = os.path.join(util.get_images_directory(), 'icon.png') self._app.setWindowIcon(QtGui.QIcon(icon_file)) @staticmethod @@ -171,10 +142,44 @@ class App: def _load_app_translations(self): lang = Settings.supported_languages()[self._settings['language']] translator = QtCore.QTranslator() - translator.load(os.path.join(get_translations_directory(), lang)) + translator.load(os.path.join(util.get_translations_directory(), lang)) self._app.installTranslator(translator) self._app.translator = translator + def _select_and_load_profile(self): + encrypt_save = tox_encrypt_save.ToxEncryptSave() + self._toxes = user_data.toxes.ToxES(encrypt_save) + + if self._path is not None: # toxygen was started with path to profile + self._load_existing_profile(self._path) + else: + auto_profile = Settings.get_auto_profile() + if auto_profile is None: # no default profile + result = self._select_profile() + if result is None: + return False + if result.is_new_profile(): # create new profile + self._create_new_profile(result.profile_path) + else: # load existing profile + self._load_existing_profile(result.profile_path) + self._path = result.profile_path + else: # default profile + self._path = auto_profile + self._load_existing_profile(auto_profile) + + if Settings.is_active_profile(self._path): # profile is in use + profile_name = util.get_profile_name_from_path(self._path) + title = util_ui.tr('Profile {}').format(profile_name) + text = util_ui.tr( + 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') + reply = util_ui.question(text, title) + if not reply: + return False + + self._settings.set_active_profile() + + return True + # ----------------------------------------------------------------------------------------------------------------- # Threads # ----------------------------------------------------------------------------------------------------------------- @@ -223,7 +228,7 @@ class App: self._tox = self._create_tox(data) def _create_new_profile(self, profile_path): - name = get_profile_name_from_path(profile_path) or 'toxygen_user' + name = util.get_profile_name_from_path(profile_path) or 'toxygen_user' if os.path.isfile(profile_path): util_ui.message_box(util_ui.tr('Profile with this name already exists'), util_ui.tr('Error')) diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 44a18b3..037289c 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -135,16 +135,16 @@ class BaseContact: return self._widget.avatar_label.pixmap() def get_avatar_path(self): - directory = util.join_path(self._profile_manager.get_dir(), 'avatars') - avatar_path = util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) + avatar_path = self.get_contact_avatar_path() if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image - avatar_path = util.join_path(util.get_images_directory(), self.get_default_avatar_name()) + avatar_path = util.join_path(util.get_images_directory(), self._get_default_avatar_name()) return avatar_path - @staticmethod - def get_default_avatar_name(): - return 'avatar.png' + def get_contact_avatar_path(self): + directory = util.join_path(self._profile_manager.get_dir(), 'avatars') + + return util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) def get_avatar_changed_event(self): return self._avatar_changed_event @@ -160,3 +160,11 @@ class BaseContact: self._widget.status_message.setText(self._status_message) self._widget.connection_status.update(self._status) self.load_avatar() + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def _get_default_avatar_name(): + return 'avatar.png' diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 937ab12..596642e 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -3,7 +3,6 @@ from os.path import basename, getsize, exists, dirname from os import remove, rename, chdir from time import time, sleep from wrapper.tox import Tox -from user_data import settings from PyQt5 import QtCore @@ -48,7 +47,7 @@ class FileTransfer(QtCore.QObject): """ def __init__(self, path, tox, friend_number, size, file_number=None): - super().__init__(self) + QtCore.QObject.__init__(self) self._path = path self._tox = tox self._friend_number = friend_number @@ -305,20 +304,20 @@ class ReceiveAvatar(ReceiveTransfer): """ MAX_AVATAR_SIZE = 512 * 1024 - def __init__(self, tox, friend_number, size, file_number): - path = settings.ProfileManager.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) - super().__init__(path + '.tmp', tox, friend_number, size, file_number) + def __init__(self, path, tox, friend_number, size, file_number): + full_path = path + '.tmp' + super().__init__(full_path, tox, friend_number, size, file_number) if size > self.MAX_AVATAR_SIZE: self.send_control(TOX_FILE_CONTROL['CANCEL']) self._file.close() - remove(path + '.tmp') + remove(full_path) elif not size: self.send_control(TOX_FILE_CONTROL['CANCEL']) self._file.close() if exists(path): remove(path) self._file.close() - remove(path + '.tmp') + remove(full_path) elif exists(path): hash = self.get_file_id() with open(path, 'rb') as fl: @@ -327,7 +326,7 @@ class ReceiveAvatar(ReceiveTransfer): if hash == existing_hash: self.send_control(TOX_FILE_CONTROL['CANCEL']) self._file.close() - remove(path + '.tmp') + remove(full_path) else: self.send_control(TOX_FILE_CONTROL['RESUME']) else: diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index c4ab8b0..59d94b2 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -309,8 +309,6 @@ class FileTransfersHandler: elif data[1] == friend_number and not data[2]: self.send_file(data[0], friend_number, True, key) del self._paused_file_transfers[key] - if friend_number == self.get_active_number() and self.is_active_a_friend(): - self.update() except Exception as ex: print('Exception in file sending: ' + str(ex)) @@ -333,14 +331,13 @@ class FileTransfersHandler: :param file_number: file number :param size: size of avatar or 0 (default avatar) """ - ra = ReceiveAvatar(self._tox, friend_number, size, file_number) + friend = self._get_friend_by_number(friend_number) + ra = ReceiveAvatar(friend.get_contact_avatar_path(), self._tox, friend_number, size, file_number) if ra.state != TOX_FILE_TRANSFER_STATE['CANCELLED']: self._file_transfers[(friend_number, file_number)] = ra ra.set_transfer_finished_handler(self.transfer_finished) else: - self._get_friend_by_number(friend_number).load_avatar() - if self.get_active_number() == friend_number and self.is_active_a_friend(): - self.set_active(None) + friend.load_avatar() # ----------------------------------------------------------------------------------------------------------------- # Private methods diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 54ca652..f871940 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -97,7 +97,7 @@ class MainWindow(QtWidgets.QMainWindow): self.actionAbout_program.triggered.connect(self.about_program) self.actionNetwork.triggered.connect(self.network_settings) - self.actionAdd_friend.triggered.connect(self.add_contact) + self.actionAdd_friend.triggered.connect(self.add_contact_triggered) self.actionAdd_gc.triggered.connect(self.create_gc) self.actionSettings.triggered.connect(self.profile_settings) self.actionPrivacy_settings.triggered.connect(self.privacy_settings) @@ -437,6 +437,9 @@ class MainWindow(QtWidgets.QMainWindow): self._modal_window = self._widget_factory.create_plugins_settings_window() self._modal_window.show() + def add_contact_triggered(self, _): + self.add_contact() + def add_contact(self, link=''): self._modal_window = self._widget_factory.create_add_contact_window(link) self._modal_window.show() @@ -444,7 +447,7 @@ class MainWindow(QtWidgets.QMainWindow): def create_gc(self): self.profile.create_group_chat() - def profile_settings(self, *args): + def profile_settings(self, _): self._modal_window = self._widget_factory.create_profile_settings_window() self._modal_window.show() diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index b28ab3b..0585c4c 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -4,7 +4,6 @@ from contacts.profile import Profile from utils.util import * from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio -from user_data import toxes import updater.updater as updater import utils.ui as util_ui @@ -90,6 +89,7 @@ class ProfileSettings(CenteredWidget): self._profile_manager = profile_manager self._settings = settings self._toxes = toxes + self._auto = False self.initUI() self.center() @@ -166,8 +166,7 @@ class ProfileSettings(CenteredWidget): self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') self.default = QtWidgets.QPushButton(self) self.default.setGeometry(QtCore.QRect(40, 550, 620, 30)) - auto_profile = Settings.get_auto_profile() - # self.auto = path + name == ProfileManager.get_path() + Settings.get_instance().name + self._auto = Settings.get_auto_profile() == self._profile_manager.get_path() self.default.clicked.connect(self.auto_profile) self.retranslateUi() if self._profile.status is not None: @@ -197,23 +196,23 @@ class ProfileSettings(CenteredWidget): self.status.addItem(util_ui.tr("Away")) self.status.addItem(util_ui.tr("Busy")) self.copy_pk.setText(util_ui.tr("Copy public key")) - if self.auto: + + self.set_default_profile_button_text() + + def auto_profile(self): + if self._auto: + Settings.reset_auto_profile() + else: + Settings.set_auto_profile(self._profile_manager.get_path()) + self._auto = not self._auto + self.set_default_profile_button_text() + + def set_default_profile_button_text(self): + if self._auto: self.default.setText(util_ui.tr("Mark as not default profile")) else: self.default.setText(util_ui.tr("Mark as default profile")) - def auto_profile(self): - if self.auto: - Settings.reset_auto_profile() - else: - Settings.set_auto_profile(ProfileManager.get_path(), Settings.get_instance().name) - self.auto = not self.auto - if self.auto: - self.default.setText(util_ui.tr("Mark as not default profile")) - else: - self.default.setText( - util_ui.tr("Mark as default profile")) - def new_password(self): if self.password.text() == self.confirm_password.text(): if not len(self.password.text()) or len(self.password.text()) >= 8: @@ -230,7 +229,7 @@ class ProfileSettings(CenteredWidget): def copy(self): clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(self._profile.tox_id) - pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png') + pixmap = QtGui.QPixmap(join_path(get_images_directory(), 'accept.png')) icon = QtGui.QIcon(pixmap) self.copyId.setIcon(icon) self.copyId.setIconSize(QtCore.QSize(10, 10)) @@ -238,7 +237,7 @@ class ProfileSettings(CenteredWidget): def copy_public_key(self): clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(self._profile.tox_id[:64]) - pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png') + pixmap = QtGui.QPixmap(join_path(get_images_directory(), 'accept.png')) icon = QtGui.QIcon(pixmap) self.copy_pk.setIcon(icon) self.copy_pk.setIconSize(QtCore.QSize(10, 10)) @@ -270,7 +269,7 @@ class ProfileSettings(CenteredWidget): util_ui.tr('Use new path')) self._settings.export(directory) self._profile.export_db(directory) - ProfileManager.get_instance().export_profile(directory, reply) + self._profile_manager.export_profile(directory, reply) def closeEvent(self, event): self._profile.set_name(self.nick.text()) diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index a95253f..3324fce 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -28,6 +28,9 @@ class ProfileManager: def get_dir(self): return self._directory + def get_path(self): + return self._path + def save_profile(self, data): if self._toxes.has_password(): data = self._toxes.pass_encrypt(data) diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index f9390f3..8289663 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -1,5 +1,4 @@ import json -import os from utils.util import * import pyaudio import smileys.smileys as smileys @@ -52,16 +51,21 @@ class Settings(dict): if os.path.isfile(p): with open(p) as fl: data = fl.read() - auto = json.loads(data) - if 'path' in auto and 'name' in auto: - path = str(auto['path']) - name = str(auto['name']) - if os.path.isfile(join_path(path, name + '.tox')): - return path, name + try: + auto = json.loads(data) + except Exception as ex: + log(str(ex)) + auto = {} + if 'profile_path' in auto: + path = str(auto['profile_path']) + if not os.path.isabs(path): + path = join_path(path, curr_directory(__file__)) + if os.path.isfile(path): + return path return None @staticmethod - def set_auto_profile(path, name): + def set_auto_profile(path): p = Settings.get_global_settings_path() if os.path.isfile(p): with open(p) as fl: @@ -69,8 +73,7 @@ class Settings(dict): data = json.loads(data) else: data = {} - data['path'] = str(path) - data['name'] = str(name) + data['profile_path'] = str(path) with open(p, 'w') as fl: fl.write(json.dumps(data)) From 7898363dcb38cdf665cbd59fb394453a8b22b3b1 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 11 May 2018 00:35:56 +0300 Subject: [PATCH 021/217] contact context menu fixes --- toxygen/app.py | 3 ++- toxygen/contacts/contacts_manager.py | 9 ++++++++- toxygen/ui/main_screen.py | 23 +++++++++++++---------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 713d250..de15277 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -310,7 +310,8 @@ class App: self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, self._contacts_provider, items_factory, profile) self._tray = tray.init_tray(profile, self._settings, self._ms) - self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile) + self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, + self._plugin_loader) self._tray.show() self._ms.show() diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 464d448..4bdf78b 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -30,7 +30,7 @@ class ContactsManager: def __del__(self): del self._history - def get_friend(self, num): + def get_contact(self, num): if num < 0 or num >= len(self._contacts): return None return self._contacts[num] @@ -359,6 +359,13 @@ class ContactsManager: self.add_friend(tox_id) self.save_profile() + # ----------------------------------------------------------------------------------------------------------------- + # Groups support + # ----------------------------------------------------------------------------------------------------------------- + + def get_group_chats(self): + return list(filter(lambda c: type(c) is not Friend, self._contacts)) # TODO: fix after gc implementation + # ----------------------------------------------------------------------------------------------------------------- # Friend requests # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index f871940..821b123 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -15,16 +15,18 @@ class MainWindow(QtWidgets.QMainWindow): self._tray = tray self._widget_factory = None self._modal_window = None + self._plugins_loader = None self.setAcceptDrops(True) self._saved = False self._profile = None self.initUI() - def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile): + def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader): self._widget_factory = widget_factory self._tray = tray self._contacts_manager = contacts_manager self._profile = profile + self._plugins_loader = plugins_loader self.messageEdit.set_messenger(messenger) def show(self): @@ -572,16 +574,17 @@ class MainWindow(QtWidgets.QMainWindow): # ----------------------------------------------------------------------------------------------------------------- def friend_right_click(self, pos): + # TODO: move to contact? item = self.friends_list.itemAt(pos) num = self.friends_list.indexFromItem(item).row() - friend = self._contacts_manager.get_friend(num) - if friend is None: + contact = self._contacts_manager.get_contact(num) + if contact is None: return - allowed = friend.tox_id in self._settings['auto_accept_from_friends'] + allowed = contact.tox_id in self._settings['auto_accept_from_friends'] auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') if item is not None: self.listMenu = QtWidgets.QMenu() - is_friend = type(friend) is Friend + is_friend = type(contact) is Friend if is_friend: set_alias_item = self.listMenu.addAction(util_ui.tr('Set alias')) set_alias_item.triggered.connect(lambda: self.set_alias(num)) @@ -603,7 +606,7 @@ class MainWindow(QtWidgets.QMainWindow): notes_item = self.listMenu.addAction(util_ui.tr('Notes')) chats = self._contacts_manager.get_group_chats() - if len(chats) and self.profile.is_active_online(): + if len(chats) and contact.status is not None: invite_menu = self.listMenu.addMenu(util_ui.tr('Invite to group chat')) for i in range(len(chats)): name, number = chats[i] @@ -619,15 +622,15 @@ class MainWindow(QtWidgets.QMainWindow): remove_item.triggered.connect(lambda: self.remove_friend(num)) block_item.triggered.connect(lambda: self.block_friend(num)) auto_accept_item.triggered.connect(lambda: self.auto_accept(num, not allowed)) - notes_item.triggered.connect(lambda: self.show_note(friend)) + notes_item.triggered.connect(lambda: self.show_note(contact)) else: leave_item = self.listMenu.addAction(util_ui.tr('Leave chat')) set_title_item = self.listMenu.addAction(util_ui.tr('Set title')) leave_item.triggered.connect(lambda: self.leave_gc(num)) set_title_item.triggered.connect(lambda: self.set_title(num)) clear_history_item.triggered.connect(lambda: self.clear_history(num)) - copy_name_item.triggered.connect(lambda: self.copy_name(friend)) - copy_status_item.triggered.connect(lambda: self.copy_status(friend)) + copy_name_item.triggered.connect(lambda: self.copy_name(contact)) + copy_status_item.triggered.connect(lambda: self.copy_status(contact)) export_to_text_item.triggered.connect(lambda: self.export_history(num)) export_to_html_item.triggered.connect(lambda: self.export_history(num, False)) parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) @@ -666,7 +669,7 @@ class MainWindow(QtWidgets.QMainWindow): self._contacts_manager.delete_friend(num) def block_friend(self, num): - friend = self.profile.get_friend(num) + friend = self.profile.get_contact(num) self._contacts_manager.block_user(friend.tox_id) def copy_friend_key(self, num): From c6192de9ddf433d1f237b9c60fc86371b9f8b57d Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 11 May 2018 21:27:46 +0300 Subject: [PATCH 022/217] new context menu generation - builder, generators --- toxygen/contacts/contact.py | 20 +++- toxygen/contacts/contact_menu.py | 144 +++++++++++++++++++++++ toxygen/contacts/contact_provider.py | 1 - toxygen/contacts/friend.py | 9 ++ toxygen/plugin_support/plugin_support.py | 4 +- toxygen/plugins/plugin_super_class.py | 5 +- toxygen/ui/main_screen.py | 73 ++---------- 7 files changed, 178 insertions(+), 78 deletions(-) create mode 100644 toxygen/contacts/contact_menu.py diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 43f7728..ddbd1d4 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -2,6 +2,7 @@ from history.database import * from contacts import basecontact, common import utils.util as util from messenger.messages import * +from contacts.contact_menu import * from file_transfers import file_transfers as ft import re @@ -89,7 +90,7 @@ class Contact(basecontact.BaseContact): self._unsaved_messages += 1 def get_last_message_text(self): - messages = list(filter(lambda x: x.get_type() <= 1 and x.get_owner() != MESSAGE_OWNER['FRIEND'], self._corr)) + messages = list(filter(lambda x: x.get_type() <= 1 and x.get_owner() != MESSAGE_AUTHOR['FRIEND'], self._corr)) if messages: return messages[-1].get_data()[0] else: @@ -103,19 +104,19 @@ class Contact(basecontact.BaseContact): """ :return list of unsent messages """ - messages = filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr) + messages = filter(lambda x: x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(messages) def get_unsent_messages_for_saving(self): """ :return list of unsent messages for saving """ - messages = filter(lambda x: x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr) + messages = filter(lambda x: x.get_type() <= 1 and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(map(lambda x: x.get_data(), messages)) def mark_as_sent(self): try: - message = list(filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr))[0] + message = list(filter(lambda x: x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr))[0] message.mark_as_sent() except Exception as ex: util.log('Mark as sent ex: ' + str(ex)) @@ -140,7 +141,7 @@ class Contact(basecontact.BaseContact): def save_message(x): if x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None): return True - return x.get_owner() == MESSAGE_OWNER['NOT_SENT'] + return x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'] old = filter(save_message, self._corr[:-SAVE_MESSAGES]) self._corr = list(old) + self._corr[-SAVE_MESSAGES:] @@ -162,7 +163,7 @@ class Contact(basecontact.BaseContact): self._unsaved_messages = 0 else: self._corr = list(filter(lambda x: (x.get_type() == 2 and x.get_status() in ft.ACTIVE_FILE_TRANSFERS) - or (x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT']), + or (x.get_type() <= 1 and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT']), self._corr)) self._unsaved_messages = len(self.get_unsent_messages()) @@ -294,3 +295,10 @@ class Contact(basecontact.BaseContact): return common.BaseTypingNotificationHandler.DEFAULT_HANDLER typing_notification_handler = property(get_typing_notification_handler) + + # ----------------------------------------------------------------------------------------------------------------- + # Context menu support + # ----------------------------------------------------------------------------------------------------------------- + + def get_context_menu_generator(self): + return BaseContactMenuGenerator(self) diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py new file mode 100644 index 0000000..d49c975 --- /dev/null +++ b/toxygen/contacts/contact_menu.py @@ -0,0 +1,144 @@ +from PyQt5 import QtWidgets +import utils.ui as util_ui + + +# ----------------------------------------------------------------------------------------------------------------- +# Builder +# ----------------------------------------------------------------------------------------------------------------- + +def _create_menu(menu_name): + return QtWidgets.QMenu(menu_name or '') + + +class ContactMenuBuilder: + + def __init__(self): + self._actions = [] + self._submenus = [] + self._name = None + + def with_name(self, name): + self._name = name + + return self + + def with_action(self, text, handler): + self._actions.append((text, handler)) + + return self + + def with_actions(self, actions): + self._actions.extend(actions) + + return self + + def with_submenu(self, submenu): + self._add_submenu(submenu) + + return self + + def with_optional_submenu(self, submenu): + if submenu is not None: + self._add_submenu(submenu) + + return self + + def build(self): # TODO: actions order + menu = _create_menu(self._name) + + for text, handler in self._actions: + action = menu.addAction(text) + action.triggered.connect(handler) + + for submenu in self._submenus: + menu.addMenu(submenu) + + return menu + + def _add_submenu(self, submenu): + self._submenus.append(submenu) + +# ----------------------------------------------------------------------------------------------------------------- +# Generators +# ----------------------------------------------------------------------------------------------------------------- + + +class BaseContactMenuGenerator: + + def __init__(self, contact): + self._contact = contact + + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number): + return ContactMenuBuilder().build() + + +class FriendMenuGenerator(BaseContactMenuGenerator): + + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number): + history_menu = self._generate_history_menu(main_screen, number) + copy_menu = self._generate_copy_menu(main_screen) + plugins_menu = self._generate_plugins_menu(plugin_loader, number) + + allowed = self._contact.tox_id in settings['auto_accept_from_friends'] + auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') + + builder = ContactMenuBuilder() + menu = (builder + .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_action(util_ui.tr('Chat history'), lambda: main_screen.clear_history(number)) + .with_submenu(history_menu) + .with_submenu(copy_menu) + .with_action(auto, lambda: main_screen.auto_accept(number, not allowed)) + .with_action(util_ui.tr('Remove friend'), lambda: main_screen.remove_friend(number)) + .with_action(util_ui.tr('Block friend'), lambda: main_screen.block_friend(number)) + .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) + .with_optional_submenu(plugins_menu) + ).build() + + return menu + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def _generate_history_menu(main_screen, number): + history_menu_builder = ContactMenuBuilder() + history_menu = (history_menu_builder + .with_name(util_ui.tr('Chat history')) + .with_action(util_ui.tr('Clear history'), lambda: main_screen.clear_history(number)) + .with_action(util_ui.tr('Export as text'), lambda: main_screen.export_history(number)) + .with_action(util_ui.tr('Export as HTML'), lambda: main_screen.export_history(number, False)) + ).build() + return history_menu + + def _generate_copy_menu(self, main_screen): + copy_menu_builder = ContactMenuBuilder() + copy_menu = (copy_menu_builder + .with_name(util_ui.tr('Copy')) + .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name)) + .with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.name)) + .with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id)) + ).build() + + return copy_menu + + @staticmethod + def _generate_plugins_menu(plugin_loader, number): + if plugin_loader is None: + return None + plugins_actions = plugin_loader.get_menu(number) + if not len(plugins_actions): + return None + plugins_menu_builder = ContactMenuBuilder() + plugins_menu = (plugins_menu_builder + .with_name(util_ui.tr('Plugins')) + .with_actions(plugins_actions) + ) + + return plugins_menu + + def _generate_groups_menu(self, contacts_manager): + chats = contacts_manager.get_group_chats() + if not len(chats) or self._contact.status is None: + return None diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py index 5042f97..1a08589 100644 --- a/toxygen/contacts/contact_provider.py +++ b/toxygen/contacts/contact_provider.py @@ -1,4 +1,3 @@ -import utils.util as util import common.tox_save as tox_save diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 2e65e53..9602eb1 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -1,6 +1,7 @@ from contacts import contact, common from messenger.messages import * import os +from contacts.contact_menu import * class Friend(contact.Contact): @@ -46,6 +47,7 @@ class Friend(contact.Contact): if message.get_data()[1] is not None: return True return os.path.exists(message.get_data()[0]) + self._corr = list(filter(is_valid, self._corr)) def delete_one_unsent_file(self, time): @@ -81,3 +83,10 @@ class Friend(contact.Contact): def get_typing_notification_handler(self): return self._typing_notification_handler + + # ----------------------------------------------------------------------------------------------------------------- + # Context menu support + # ----------------------------------------------------------------------------------------------------------------- + + def get_context_menu_generator(self): + return FriendMenuGenerator(self) diff --git a/toxygen/plugin_support/plugin_support.py b/toxygen/plugin_support/plugin_support.py index bd50cb7..5871afa 100644 --- a/toxygen/plugin_support/plugin_support.py +++ b/toxygen/plugin_support/plugin_support.py @@ -138,7 +138,7 @@ class PluginLoader(): if name in self._plugins and self._plugins[name][1]: self._plugins[name][0].command(text[len(name) + 1:]) - def get_menu(self, menu, num): + def get_menu(self, num): """ Return list of items for menu """ @@ -146,7 +146,7 @@ class PluginLoader(): for elem in self._plugins.values(): if elem[1]: try: - result.extend(elem[0].get_menu(menu, num)) + result.extend(elem[0].get_menu(num)) except: continue return result diff --git a/toxygen/plugins/plugin_super_class.py b/toxygen/plugins/plugin_super_class.py index c857c56..abb4da6 100644 --- a/toxygen/plugins/plugin_super_class.py +++ b/toxygen/plugins/plugin_super_class.py @@ -76,12 +76,11 @@ class PluginSuperClass: """ return self.__doc__ - def get_menu(self, menu, row_number): + def get_menu(self, row_number): """ This method creates items for menu which called on right click in list of friends - :param menu: menu instance :param row_number: number of selected row in list of contacts - :return list of QAction's + :return list of tuples (text, handler) """ return [] diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 821b123..5c96e97 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -576,63 +576,14 @@ class MainWindow(QtWidgets.QMainWindow): def friend_right_click(self, pos): # TODO: move to contact? item = self.friends_list.itemAt(pos) - num = self.friends_list.indexFromItem(item).row() - contact = self._contacts_manager.get_contact(num) + number = self.friends_list.indexFromItem(item).row() + contact = self._contacts_manager.get_contact(number) if contact is None: return - allowed = contact.tox_id in self._settings['auto_accept_from_friends'] - auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') if item is not None: - self.listMenu = QtWidgets.QMenu() - is_friend = type(contact) is Friend - if is_friend: - set_alias_item = self.listMenu.addAction(util_ui.tr('Set alias')) - set_alias_item.triggered.connect(lambda: self.set_alias(num)) - - history_menu = self.listMenu.addMenu(util_ui.tr('Chat history')) - clear_history_item = history_menu.addAction(util_ui.tr('Clear history')) - export_to_text_item = history_menu.addAction(util_ui.tr('Export as text')) - export_to_html_item = history_menu.addAction(util_ui.tr('Export as HTML')) - - copy_menu = self.listMenu.addMenu(util_ui.tr('Copy')) - copy_name_item = copy_menu.addAction(util_ui.tr('Name')) - copy_status_item = copy_menu.addAction(util_ui.tr('Status message')) - if is_friend: - copy_key_item = copy_menu.addAction(util_ui.tr('Public key')) - - auto_accept_item = self.listMenu.addAction(auto) - remove_item = self.listMenu.addAction(util_ui.tr('Remove friend')) - block_item = self.listMenu.addAction(util_ui.tr('Block friend')) - notes_item = self.listMenu.addAction(util_ui.tr('Notes')) - - chats = self._contacts_manager.get_group_chats() - if len(chats) and contact.status is not None: - invite_menu = self.listMenu.addMenu(util_ui.tr('Invite to group chat')) - for i in range(len(chats)): - name, number = chats[i] - item = invite_menu.addAction(name) - item.triggered.connect(lambda: self.invite_friend_to_gc(num, number)) - - if self._plugins_loader is not None: - submenu = self._plugins_loader.get_menu(self.listMenu, num) - if len(submenu): - plug = self.listMenu.addMenu(util_ui.tr('Plugins')) - plug.addActions(submenu) - copy_key_item.triggered.connect(lambda: self.copy_friend_key(num)) - remove_item.triggered.connect(lambda: self.remove_friend(num)) - block_item.triggered.connect(lambda: self.block_friend(num)) - auto_accept_item.triggered.connect(lambda: self.auto_accept(num, not allowed)) - notes_item.triggered.connect(lambda: self.show_note(contact)) - else: - leave_item = self.listMenu.addAction(util_ui.tr('Leave chat')) - set_title_item = self.listMenu.addAction(util_ui.tr('Set title')) - leave_item.triggered.connect(lambda: self.leave_gc(num)) - set_title_item.triggered.connect(lambda: self.set_title(num)) - clear_history_item.triggered.connect(lambda: self.clear_history(num)) - copy_name_item.triggered.connect(lambda: self.copy_name(contact)) - copy_status_item.triggered.connect(lambda: self.copy_status(contact)) - export_to_text_item.triggered.connect(lambda: self.export_history(num)) - export_to_html_item.triggered.connect(lambda: self.export_history(num, False)) + generator = contact.get_context_menu_generator() + self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, + self._settings, number) parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) self.listMenu.move(parent_position + pos) self.listMenu.show() @@ -672,20 +623,10 @@ class MainWindow(QtWidgets.QMainWindow): friend = self.profile.get_contact(num) self._contacts_manager.block_user(friend.tox_id) - def copy_friend_key(self, num): - tox_id = self._contacts_manager.friend_public_key(num) - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(tox_id) - @staticmethod - def copy_name(friend): + def copy_text(text): clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(friend.name) - - @staticmethod - def copy_status(friend): - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(friend.status_message) + clipboard.setText(text) def clear_history(self, num): self._contacts_manager.clear_history(num) From e21a9355e7b9f5814eb30362e2aec959ccc74c7e Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 11 May 2018 22:02:03 +0300 Subject: [PATCH 023/217] minor fixes - context menu --- toxygen/contacts/contact_menu.py | 5 +++-- toxygen/ui/main_screen.py | 15 ++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index d49c975..d924702 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -110,6 +110,7 @@ class FriendMenuGenerator(BaseContactMenuGenerator): .with_action(util_ui.tr('Export as text'), lambda: main_screen.export_history(number)) .with_action(util_ui.tr('Export as HTML'), lambda: main_screen.export_history(number, False)) ).build() + return history_menu def _generate_copy_menu(self, main_screen): @@ -134,11 +135,11 @@ class FriendMenuGenerator(BaseContactMenuGenerator): plugins_menu = (plugins_menu_builder .with_name(util_ui.tr('Plugins')) .with_actions(plugins_actions) - ) + ).build() return plugins_menu - def _generate_groups_menu(self, contacts_manager): + def _generate_groups_menu(self, contacts_manager): # TODO: fix chats = contacts_manager.get_group_chats() if not len(chats) or self._contact.status is None: return None diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 5c96e97..b318ccc 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -574,19 +574,16 @@ class MainWindow(QtWidgets.QMainWindow): # ----------------------------------------------------------------------------------------------------------------- def friend_right_click(self, pos): - # TODO: move to contact? item = self.friends_list.itemAt(pos) number = self.friends_list.indexFromItem(item).row() contact = self._contacts_manager.get_contact(number) - if contact is None: + if contact is None or item is None: return - if item is not None: - generator = contact.get_context_menu_generator() - self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, - self._settings, number) - parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) - self.listMenu.move(parent_position + pos) - self.listMenu.show() + generator = contact.get_context_menu_generator() + self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, self._settings, number) + parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) + self.listMenu.move(parent_position + pos) + self.listMenu.show() def show_note(self, friend): note = self._settings['notes'][friend.tox_id] if friend.tox_id in self._settings['notes'] else '' From 98dbe6a4932bae36f789c4df9975afad78fe77b0 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 15 May 2018 13:40:59 +0300 Subject: [PATCH 024/217] widgets fixes --- toxygen/app.py | 4 ++-- toxygen/contacts/contacts_manager.py | 14 +++++++------- .../file_transfers/file_transfers_handler.py | 12 +++++------- toxygen/ui/main_screen.py | 2 +- toxygen/ui/main_screen_widgets.py | 17 ++++++++++------- toxygen/ui/widgets_factory.py | 6 +++--- 6 files changed, 28 insertions(+), 27 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index de15277..20fed55 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -303,12 +303,12 @@ class App: self._contacts_provider = ContactProvider(self._tox, self._friend_factory) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, self._contacts_provider, db, self._tox_dns) + self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, + self._contacts_provider, items_factory, profile) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, self._toxes, self._version) - self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, - self._contacts_provider, items_factory, profile) self._tray = tray.init_tray(profile, self._settings, self._ms) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, self._plugin_loader) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 4bdf78b..efe479c 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -43,7 +43,7 @@ class ContactsManager: self._profile_manager.save_profile(data) def is_friend_active(self, friend_number): - if not self._is_active_a_friend(): + if not self.is_active_a_friend(): return False return self.get_curr_contact().number == friend_number @@ -148,6 +148,8 @@ class ContactsManager: util.log('Error in set active: ' + str(ex)) raise + active_friend = property(get_active, set_active) + def set_active_by_number_and_type(self, number, is_friend): for i in range(len(self._contacts)): c = self._contacts[i] @@ -155,12 +157,13 @@ class ContactsManager: self._active_contact = i break - active_friend = property(get_active, set_active) - def update(self): if self._active_contact + 1: self.set_active(self._active_contact) + def is_active_a_friend(self): + return type(self.get_curr_contact()) is Friend + # ----------------------------------------------------------------------------------------------------------------- # Filtration # ----------------------------------------------------------------------------------------------------------------- @@ -174,7 +177,7 @@ class ContactsManager: # TODO: simplify? filter_str = filter_str.lower() number = self.get_active_number() - is_friend = self._is_active_a_friend() + is_friend = self.is_active_a_friend() if sorting > 1: if sorting & 2: self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True) @@ -424,9 +427,6 @@ class ContactsManager: # Private methods # ----------------------------------------------------------------------------------------------------------------- - def _is_active_a_friend(self): - return type(self.get_curr_contact()) is Friend - def _load_contacts(self): self._load_friends() self._load_groups() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 59d94b2..073b8b6 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -179,25 +179,23 @@ class FileTransfersHandler: self._get_friend_by_number(friend_number).update_transfer_data(file_number, TOX_FILE_TRANSFER_STATE['RUNNING']) - def send_screenshot(self, data): + def send_screenshot(self, data, friend_number): """ Send screenshot to current active friend :param data: raw data - png """ - self.send_inline(data, 'toxygen_inline.png') + self.send_inline(data, 'toxygen_inline.png', friend_number) - def send_sticker(self, path): + def send_sticker(self, path, friend_number): with open(path, 'rb') as fl: data = fl.read() - self.send_inline(data, 'sticker.png') + self.send_inline(data, 'sticker.png', friend_number) - def send_inline(self, data, file_name, friend_number=None, is_resend=False): - friend_number = friend_number or self.get_active_number() + def send_inline(self, data, file_name, friend_number, is_resend=False): friend = self._get_friend_by_number(friend_number) if friend.status is None and not is_resend: m = UnsentFile(file_name, data, time.time()) friend.append_message(m) - self.update() return elif friend.status is None and is_resend: raise RuntimeError() diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index b318ccc..f0a1ef5 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -542,7 +542,7 @@ class MainWindow(QtWidgets.QMainWindow): def send_sticker(self): self.menu.hide() if self._contacts_manager.is_active_a_friend(): - self.sticker = self._widget_factory.create_sticker_window(self) + self.sticker = self._widget_factory.create_sticker_window() self.sticker.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), self.y() + self.height() - 200, self.sticker.width(), diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index e739167..ecf329a 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -81,7 +81,8 @@ class MessageArea(QtWidgets.QPlainTextEdit): else: self.insertPlainText(text) - def parse_file_name(self, file_name): + @staticmethod + def parse_file_name(file_name): if file_name.endswith('\r\n'): file_name = file_name[:-2] file_name = urllib.parse.unquote(file_name) @@ -90,9 +91,10 @@ class MessageArea(QtWidgets.QPlainTextEdit): class ScreenShotWindow(RubberBandWindow): - def __init__(self, file_transfer_handler, *args): + def __init__(self, file_transfer_handler, contacts_manager, *args): super().__init__(*args) self._file_transfer_handler = file_transfer_handler + self._contacts_manager = contacts_manager def closeEvent(self, *args): if self.parent.isHidden(): @@ -113,7 +115,8 @@ class ScreenShotWindow(RubberBandWindow): buffer = QtCore.QBuffer(byte_array) buffer.open(QtCore.QIODevice.WriteOnly) p.save(buffer, 'PNG') - self._file_transfer_handler.send_screenshot(bytes(byte_array.data())) + friend = self._contacts_manager.get_curr_contact() + self._file_transfer_handler.send_screenshot(bytes(byte_array.data(), friend.number)) self.close() @@ -267,9 +270,10 @@ class StickerItem(QtWidgets.QWidget): class StickerWindow(QtWidgets.QWidget): """Sticker selection window""" - def __init__(self, parent, file_transfer_handler): + def __init__(self, file_transfer_handler, contacts_manager): super().__init__() self._file_transfer_handler = file_transfer_handler + self._contacts_manager = contacts_manager self.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.setMaximumSize(250, 200) self.setMinimumSize(250, 200) @@ -285,11 +289,11 @@ class StickerWindow(QtWidgets.QWidget): self.list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.list.setSpacing(3) self.list.clicked.connect(self.click) - self.parent = parent def click(self, index): num = index.row() - self._file_transfer_handler.send_sticker(self._stickers[num]) + friend = self._contacts_manager.get_curr_contact() + self._file_transfer_handler.send_sticker(self._stickers[num], friend.number) self.close() def leaveEvent(self, event): @@ -465,7 +469,6 @@ class SearchScreen(QtWidgets.QWidget): self.not_found(text) def closeEvent(self, *args): - Profile.get_instance().update() self._messages.setGeometry(0, 0, self._messages.width(), self._messages.height() + 40) super().closeEvent(*args) diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 0c0bcbc..b42e342 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -17,7 +17,7 @@ class WidgetsFactory: self._version = version def create_screenshot_window(self, *args): - return ScreenShotWindow(self._file_transfer_handler, *args) + return ScreenShotWindow(self._file_transfer_handler, self._contacts_manager, *args) def create_smiley_window(self, parent): return SmileyWindow(parent, self._smiley_loader) @@ -61,5 +61,5 @@ class WidgetsFactory: def create_smiley_window(self, parent): return SmileyWindow(parent, self._smiley_loader) - def create_sticker_window(self, parent): - return StickerWindow(parent, self._file_transfer_handler) + def create_sticker_window(self): + return StickerWindow(self._file_transfer_handler, self._contacts_manager) From f1c63bb4e8dab2415708025323fcf57fd28e60ef Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 15 May 2018 17:00:12 +0300 Subject: [PATCH 025/217] history loading after friend switching. refactoring --- toxygen/app.py | 15 ++- toxygen/contacts/contact.py | 4 + toxygen/contacts/contacts_manager.py | 109 ++++++------------ toxygen/contacts/friend_factory.py | 2 +- toxygen/contacts/profile.py | 22 ++-- toxygen/history/database.py | 2 +- .../history/{history_loader.py => history.py} | 5 +- toxygen/messenger/messages.py | 42 +++---- toxygen/messenger/messenger.py | 6 +- toxygen/middleware/callbacks.py | 2 - .../{items_factory.py => items_factories.py} | 42 ++++--- toxygen/ui/list_items.py | 3 - toxygen/ui/main_screen.py | 12 +- toxygen/ui/menu.py | 2 - toxygen/ui/messages_widgets.py | 11 +- 15 files changed, 135 insertions(+), 144 deletions(-) rename toxygen/history/{history_loader.py => history.py} (98%) rename toxygen/ui/{items_factory.py => items_factories.py} (73%) diff --git a/toxygen/app.py b/toxygen/app.py index 20fed55..9f41b47 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -24,9 +24,10 @@ from av.calls_manager import CallsManager from history.database import Database from ui.widgets_factory import WidgetsFactory from smileys.smileys import SmileyLoader -from ui.items_factory import ItemsFactory +from ui.items_factories import MessagesItemsFactory, FriendItemsFactory from messenger.messenger import Messenger from network.tox_dns import ToxDns +from history.history import History class App: @@ -298,13 +299,17 @@ class App: profile = Profile(self._profile_manager, self._tox, self._ms) self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) - items_factory = ItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, self._ms) - self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, items_factory) + friend_items_factory = FriendItemsFactory(self._settings, self._ms) + self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, friend_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory) + history = History(self._contacts_provider, db, self._settings) + messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, + self._ms, history) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, - self._contacts_provider, db, self._tox_dns) + self._contacts_provider, history, self._tox_dns, + messages_items_factory) self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, - self._contacts_provider, items_factory, profile) + self._contacts_provider, messages_items_factory, profile) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index ddbd1d4..4c19e33 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -96,6 +96,10 @@ class Contact(basecontact.BaseContact): else: return '' + def remove_messages_widgets(self): + for message in self._corr: + message.remove_widget() + # ----------------------------------------------------------------------------------------------------------------- # Unsent messages # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index efe479c..c3db871 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -3,7 +3,7 @@ import utils.ui as util_ui from contacts.friend import Friend from PyQt5 import QtCore, QtGui from wrapper.toxcore_enums_and_consts import * -from history.history_loader import HistoryLoader +from messenger.messages import * class ContactsManager: @@ -11,25 +11,24 @@ class ContactsManager: Represents contacts list. """ - def __init__(self, tox, settings, screen, profile_manager, contact_provider, db, tox_dns): + def __init__(self, tox, settings, screen, profile_manager, contact_provider, history, tox_dns, + messages_items_factory): self._tox = tox self._settings = settings self._screen = screen self._profile_manager = profile_manager self._contact_provider = contact_provider self._tox_dns = tox_dns + self._messages_items_factory = messages_items_factory self._messages = screen.messages self._contacts, self._active_contact = [], -1 self._sorting = settings['sorting'] self._filter_string = '' self._friend_item_height = 40 if settings['compact_mode'] else 70 screen.online_contacts.setCurrentIndex(int(self._sorting)) - self._history = HistoryLoader(contact_provider, db, settings) + self._history = history self._load_contacts() - def __del__(self): - del self._history - def get_contact(self, num): if num < 0 or num >= len(self._contacts): return None @@ -55,10 +54,10 @@ class ContactsManager: def get_active(self): return self._active_contact - def set_active(self, value=None): + def set_active(self, value): """ Change current active friend or update info - :param value: number of new active friend in friend's list or None to update active user's data + :param value: number of new active friend in friend's list """ if value is None and self._active_contact == -1: # nothing to update return @@ -76,67 +75,34 @@ class ContactsManager: self._screen.typing.setVisible(False) current_contact = self.get_curr_contact() if current_contact is not None: + current_contact.remove_messages_widgets() # TODO: if required self._unsubscribe_from_events(current_contact) - if value is not None: - if self._active_contact + 1 and self._active_contact != value: - try: - current_contact.curr_text = self._screen.messageEdit.toPlainText() - except: - pass - friend = self._contacts[value] - self._subscribe_to_events(friend) - friend.remove_invalid_unsent_files() - if self._active_contact != value: - self._screen.messageEdit.setPlainText(friend.curr_text) - self._active_contact = value - friend.reset_messages() - if not self._settings['save_history']: - friend.delete_old_messages() - self._messages.clear() - # friend.load_corr() - # messages = friend.get_corr()[-PAGE_SIZE:] - # self._load_history = False - # for message in messages: - # if message.get_type() <= 1: - # data = message.get_data() - # self.create_message_item(data[0], - # data[2], - # data[1], - # data[3]) - # elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: - # if message.get_status() is None: - # self.create_unsent_file_item(message) - # continue - # item = self.create_file_transfer_item(message) - # if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer - # try: - # ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] - # ft.set_state_changed_handler(item.update_transfer_state) - # ft.signal() - # except: - # print('Incoming not started transfer - no info found') - # elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline - # self.create_inline_item(message.get_data()) - # elif message.get_type() < 5: # info message - # data = message.get_data() - # self.create_message_item(data[0], - # data[2], - # '', - # data[3]) - # else: - # data = message.get_data() - # self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3]) - # self._messages.scrollToBottom() - # self._load_history = True - # if value in self._call: - # self._screen.active_call() - # elif value in self._incoming_calls: - # self._screen.incoming_call() - # else: - # self._screen.call_finished() - else: - friend = self.get_curr_contact() - # TODO: to separate method + + if self._active_contact + 1 and self._active_contact != value: + try: + current_contact.curr_text = self._screen.messageEdit.toPlainText() + except: + pass + friend = self._contacts[value] + self._subscribe_to_events(friend) + friend.remove_invalid_unsent_files() + if self._active_contact != value: + self._screen.messageEdit.setPlainText(friend.curr_text) + self._active_contact = value + friend.reset_messages() + if not self._settings['save_history']: + friend.delete_old_messages() + self._messages.clear() + friend.load_corr() + corr = friend.get_corr()[-PAGE_SIZE:] + for message in corr: + self._messages_items_factory.create_message_item(message) + # if value in self._call: + # self._screen.active_call() + # elif value in self._incoming_calls: + # self._screen.incoming_call() + # else: + # self._screen.call_finished() self._screen.account_status.setToolTip(friend.get_full_status()) avatar_path = friend.get_avatar_path() @@ -150,7 +116,7 @@ class ContactsManager: active_friend = property(get_active, set_active) - def set_active_by_number_and_type(self, number, is_friend): + def set_active_by_number_and_type(self, number, is_friend): # TODO: by id for i in range(len(self._contacts)): c = self._contacts[i] if c.number == number and (type(c) is Friend == is_friend): @@ -468,5 +434,6 @@ class ContactsManager: def _current_contact_avatar_changed(self, avatar_path): width = self._screen.account_avatar.width() pixmap = QtGui.QPixmap(avatar_path) - self._screen.account_avatar.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation)) + self._screen.avatar_label.setPixmap(pixmap.scaled(width, width, + QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation)) diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py index 6803c61..9ba859f 100644 --- a/toxygen/contacts/friend_factory.py +++ b/toxygen/contacts/friend_factory.py @@ -39,4 +39,4 @@ class FriendFactory: Method-factory :return: new widget for friend instance """ - return self._items_factory.friend_item() + return self._items_factory.create_friend_item() diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 8102f56..302dc0b 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -129,8 +129,8 @@ class Profile(basecontact.BaseContact): pixmap = self.get_curr_friend().get_pixmap() else: pixmap = self.get_pixmap() - return self._factory.message_item(text, time, name, owner != MESSAGE_OWNER['NOT_SENT'], - message_type, append, pixmap) + return self._factory.create_message_item(text, time, name, owner != MESSAGE_OWNER['NOT_SENT'], + message_type, append, pixmap) def create_gc_message_item(self, text, time, owner, name, message_type, append=True): pixmap = None @@ -139,24 +139,24 @@ class Profile(basecontact.BaseContact): pixmap = self.get_curr_friend().get_pixmap() else: pixmap = self.get_pixmap() - return self._factory.message_item(text, time, name, True, - message_type - 5, append, pixmap) + return self._factory.create_message_item(text, time, name, True, + message_type - 5, append, pixmap) def create_file_transfer_item(self, tm, append=True): data = list(tm.get_data()) data[3] = self.get_friend_by_number(data[4]).name if data[3] else self._name - return self._factory.file_transfer_item(data, append) + return self._factory.create_file_transfer_item(data, append) def create_unsent_file_item(self, message, append=True): data = message.get_data() - return self._factory.unsent_file_item(os.path.basename(data[0]), - os.path.getsize(data[0]) if data[1] is None else len(data[1]), - self.name, - data[2], - append) + return self._factory.create_unsent_file_item(os.path.basename(data[0]), + os.path.getsize(data[0]) if data[1] is None else len(data[1]), + self.name, + data[2], + append) def create_inline_item(self, data, append=True): - return self._factory.inline_item(data, append) + return self._factory.create_inline_item(data, append) # ----------------------------------------------------------------------------------------------------------------- # Reset diff --git a/toxygen/history/database.py b/toxygen/history/database.py index bf593d8..4be00c4 100644 --- a/toxygen/history/database.py +++ b/toxygen/history/database.py @@ -73,7 +73,7 @@ class Database: db = self._connect() try: cursor = db.cursor() - cursor.execute('INSERT INTO contacts VALUES (?);', (tox_id, )) + # cursor.execute('INSERT INTO contacts VALUES (?);', (tox_id, )) cursor.execute('CREATE TABLE id' + tox_id + '(' ' id INTEGER PRIMARY KEY,' ' message_id INTEGER,' diff --git a/toxygen/history/history_loader.py b/toxygen/history/history.py similarity index 98% rename from toxygen/history/history_loader.py rename to toxygen/history/history.py index a380ddb..6ded428 100644 --- a/toxygen/history/history_loader.py +++ b/toxygen/history/history.py @@ -3,7 +3,7 @@ from history.history_logs_generators import * # TODO: fix history loading and saving -class HistoryLoader: +class History: def __init__(self, contact_provider, db, settings): self._contact_provider = contact_provider @@ -45,6 +45,9 @@ class HistoryLoader: self._db.delete_messages(friend.tox_id) self._db.delete_friend_from_db(friend.tox_id) + def delete_message(self, message): + pass + def load_history(self): """ Tries to load next part of messages diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 830e72e..dc70e8d 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -1,4 +1,5 @@ from history.database import MESSAGE_AUTHOR +from ui.messages_widgets import * MESSAGE_TYPE = { @@ -21,11 +22,10 @@ class MessageAuthor: class Message: - def __init__(self, message_id, message_type, author, time): + def __init__(self, message_type, author, time): self._time = time self._type = message_type self._author = author - self._message_id = message_id self._widget = None def get_type(self): @@ -43,19 +43,9 @@ class Message: time = property(get_time) - def get_message_id(self): - return self._message_id - - message_id = property(get_message_id) - - def get_type(self): - return self._type - - type = property(get_type) - - def get_widget(self): + def get_widget(self, *args): if self._widget is None: - self._widget = self._create_widget() + self._widget = self._create_widget(*args) return self._widget @@ -67,7 +57,7 @@ class Message: def mark_as_sent(self): self._author.author_type = MESSAGE_AUTHOR['ME'] - def _create_widget(self): + def _create_widget(self, *args): pass @@ -76,8 +66,8 @@ class TextMessage(Message): Plain text or action message """ - def __init__(self, id, message, owner, time, message_type): - super().__init__(id, message_type, owner, time) + def __init__(self, message, owner, time, message_type): + super().__init__(message_type, owner, time) self._message = message def get_text(self): @@ -88,8 +78,20 @@ class TextMessage(Message): def get_data(self): return self._message, self._owner, self._time, self._type - def _create_widget(self): - return + def _create_widget(self, *args): + return MessageItem(self, *args) + + +class OutgoingTextMessage(TextMessage): + + def __init__(self, message, owner, time, message_type, tox_message_id): + super().__init__(message, owner, time, message_type) + self._tox_message_id = tox_message_id + + def get_tox_message_id(self): + return self._tox_message_id + + tox_message_id = property(get_tox_message_id) class GroupChatMessage(TextMessage): @@ -107,7 +109,7 @@ class TransferMessage(Message): Message with info about file transfer """ - def __init__(self, id, owner, time, status, size, name, friend_number, file_number): + def __init__(self, owner, time, status, size, name, friend_number, file_number): super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time) self._status = status self._size = size diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 5a94f7f..968ca97 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -23,7 +23,7 @@ class Messenger(tox_save.ToxSave): def _create_message_item(self, text_message): # pixmap = self._contacts_manager.get_curr_contact().get_pixmap() - self._items_factory.message_item(text_message) + self._items_factory.create_message_item(text_message) # ----------------------------------------------------------------------------------------------------------------- # Messaging @@ -38,7 +38,7 @@ class Messenger(tox_save.ToxSave): """ t = util.get_unix_time() friend = self._get_friend_by_number(friend_number) - text_message = TextMessage(0, message, MessageAuthor(friend.name, MESSAGE_AUTHOR['FRIEND']), t, message_type) + text_message = TextMessage(message, MessageAuthor(friend.name, MESSAGE_AUTHOR['FRIEND']), t, message_type) if self._contacts_manager.is_friend_active(friend_number): # add message to list self._create_message_item(text_message) @@ -80,7 +80,7 @@ class Messenger(tox_save.ToxSave): else: message_id = 0 message_author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['NOT_SENT']) - message = TextMessage(message_id, text, message_author, t, message_type) + message = OutgoingTextMessage(text, message_author, t, message_type, message_id) friend.append_message(message) if self._contacts_manager.is_friend_active(friend_number): self._create_message_item(message) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 7b7b759..c9571a2 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -95,8 +95,6 @@ def friend_status_message(contacts_manager, messenger): invoke_in_main_thread(friend.set_status_message, status_message) print('User #{} has new status'.format(friend_number)) invoke_in_main_thread(messenger.send_messages, friend_number) - if contacts_manager.is_friend_active(friend_number): - invoke_in_main_thread(contacts_manager.set_active) return wrapped diff --git a/toxygen/ui/items_factory.py b/toxygen/ui/items_factories.py similarity index 73% rename from toxygen/ui/items_factory.py rename to toxygen/ui/items_factories.py index 1c4e7a9..db2f065 100644 --- a/toxygen/ui/items_factory.py +++ b/toxygen/ui/items_factories.py @@ -1,28 +1,34 @@ from ui.list_items import * from ui.messages_widgets import * -# rename methods +class FriendItemsFactory: -class ItemsFactory: - - def __init__(self, settings, plugin_loader, smiley_loader, main_screen): - self._settings, self._plugin_loader = settings, plugin_loader - self._smiley_loader = smiley_loader - self._messages = main_screen.messages + def __init__(self, settings, main_screen): + self._settings = settings self._friends_list = main_screen.friends_list - self._message_edit = main_screen.messageEdit - def friend_item(self): + def create_friend_item(self): item = ContactItem(self._settings) elem = QtWidgets.QListWidgetItem(self._friends_list) elem.setSizeHint(QtCore.QSize(250, item.height())) self._friends_list.addItem(elem) self._friends_list.setItemWidget(elem, item) + return item - def message_item(self, message, append=True, pixmap=None): - item = MessageItem(self._settings, self._create_message_browser, message, self._messages) + +class MessagesItemsFactory: + + def __init__(self, settings, plugin_loader, smiley_loader, main_screen, history): + self._settings, self._plugin_loader = settings, plugin_loader + self._smiley_loader, self._history = smiley_loader, history + self._messages = main_screen.messages + self._message_edit = main_screen.messageEdit + + def create_message_item(self, message, append=True, pixmap=None): + item = message.get_widget(self._settings, self._create_message_browser, + self._history.delete_message, self._messages) if pixmap is not None: item.set_avatar(pixmap) elem = QtWidgets.QListWidgetItem() @@ -32,9 +38,10 @@ class ItemsFactory: else: self._messages.insertItem(0, elem) self._messages.setItemWidget(elem, item) + return item - def inline_item(self, data, append): + def create_inline_item(self, data, append): elem = QtWidgets.QListWidgetItem() item = InlineImageItem(data, self._messages.width(), elem) elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) @@ -43,9 +50,10 @@ class ItemsFactory: else: self._messages.insertItem(0, elem) self._messages.setItemWidget(elem, item) + return item - def unsent_file_item(self, file_name, size, name, time, append): + def create_unsent_file_item(self, file_name, size, name, time, append): item = UnsentFileItem(file_name, size, name, @@ -58,9 +66,10 @@ class ItemsFactory: else: self._messages.insertItem(0, elem) self._messages.setItemWidget(elem, item) + return item - def file_transfer_item(self, data, append): + def create_file_transfer_item(self, data, append): data.append(self._messages.width()) item = FileTransferItem(*data) elem = QtWidgets.QListWidgetItem() @@ -70,8 +79,13 @@ class ItemsFactory: else: self._messages.insertItem(0, elem) self._messages.setItemWidget(elem, item) + return item + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + def _create_message_browser(self, text, width, message_type, parent=None): return MessageBrowser(self._settings, self._message_edit, self._smiley_loader, self._plugin_loader, text, width, message_type, parent) diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py index a305ffc..c9638ec 100644 --- a/toxygen/ui/list_items.py +++ b/toxygen/ui/list_items.py @@ -4,10 +4,7 @@ from contacts import profile from file_transfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR from utils.util import * from ui.widgets import DataLabel, create_menu -import html as h -import smileys from user_data import settings -import re class ContactItem(QtWidgets.QWidget): diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index f0a1ef5..a8a7c13 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -604,11 +604,13 @@ class MainWindow(QtWidgets.QMainWindow): extension = 'txt' if as_text else 'html' file_name, _ = util_ui.save_file_dialog(util_ui.tr('Choose file name'), extension) - if file_name: - if not file_name.endswith('.' + extension): - file_name += '.' + extension - with open(file_name, 'wt') as fl: - fl.write(s) + if not file_name: + return + + if not file_name.endswith('.' + extension): + file_name += '.' + extension + with open(file_name, 'wt') as fl: + fl.write(s) def set_alias(self, num): self._contacts_manager.set_alias(num) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 0585c4c..d023cff 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -1,6 +1,5 @@ from PyQt5 import QtCore, QtGui, QtWidgets from user_data.settings import * -from contacts.profile import Profile from utils.util import * from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio @@ -717,7 +716,6 @@ class InterfaceSettings(CenteredWidget): app.translator.load(curr_directory() + '/translations/' + path) app.installTranslator(app.translator) self._settings['message_font_size'] = self.messages_font_size.currentIndex() + 10 - Profile.get_instance().update() self._settings.save() if restart: util_ui.message_box(util_ui.tr('Restart app to apply settings'), util_ui.tr('Restart required')) diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index 0e31891..0f510ea 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -115,7 +115,7 @@ class MessageBrowser(QtWidgets.QTextBrowser): if arr[i].startswith('>'): arr[i] = '' + arr[i][4:] + '' text = '
'.join(arr) - text = self._smileys_loader.add_smileys_to_text(text, self) # smileys + text = self._smileys_loader.add_smileys_to_text(text, self) return text @@ -123,8 +123,10 @@ class MessageItem(QtWidgets.QWidget): """ Message in messages list """ - def __init__(self, settings, message_browser_factory_method, text_message, parent=None): + def __init__(self, text_message, settings, message_browser_factory_method, delete_action, parent=None): QtWidgets.QWidget.__init__(self, parent) + self._message = text_message + self._delete_action = delete_action self.name = widgets.DataLabel(self) self.name.setGeometry(QtCore.QRect(2, 2, 95, 23)) self.name.setTextFormat(QtCore.Qt.PlainText) @@ -169,12 +171,11 @@ class MessageItem(QtWidgets.QWidget): self.listMenu.show() def delete(self): - pr = profile.Profile.get_instance() - pr.delete_message(self._time) + self._delete_action(self._message) def mark_as_sent(self): if self.t: - self.time.setText(convert_time(self._time)) + self.time.setText(util.convert_time(self._time)) self.t = False return True return False From eef02a1173f39424d27bafaadacb07742aa1605b Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 15 May 2018 22:51:42 +0300 Subject: [PATCH 026/217] history fixes - db cleanup --- toxygen/app.py | 5 +- toxygen/contacts/contacts_manager.py | 1 + toxygen/contacts/profile.py | 56 +-- .../file_transfers/file_transfers_handler.py | 2 +- toxygen/history/database.py | 55 +-- toxygen/history/history.py | 86 +++-- toxygen/ui/contact_items.py | 100 +++++ toxygen/ui/items_factories.py | 12 +- toxygen/ui/list_items.py | 343 ------------------ toxygen/ui/main_screen.py | 2 +- toxygen/ui/messages_widgets.py | 242 ++++++++++++ 11 files changed, 418 insertions(+), 486 deletions(-) create mode 100644 toxygen/ui/contact_items.py delete mode 100644 toxygen/ui/list_items.py diff --git a/toxygen/app.py b/toxygen/app.py index 9f41b47..ef5e351 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -302,9 +302,10 @@ class App: friend_items_factory = FriendItemsFactory(self._settings, self._ms) self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, friend_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory) - history = History(self._contacts_provider, db, self._settings) + history = None messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, - self._ms, history) + self._ms, lambda m: history.delete_message(m)) + history = History(self._contacts_provider, db, self._settings, self._ms, messages_items_factory) self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, self._contacts_provider, history, self._tox_dns, messages_items_factory) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index c3db871..b65acfa 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -296,6 +296,7 @@ class ContactsManager: Adds friend to list """ self._tox.friend_add_norequest(tox_id) + self._history.add_friend_to_db(tox_id) friend = self._contact_provider.get_friend_by_public_key(tox_id) self._contacts.append(friend) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 302dc0b..af5b87e 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -1,11 +1,10 @@ from contacts.friend import * -from user_data.settings import * -from history.database import * from file_transfers.file_transfers import * import time from contacts import basecontact from contacts.group_chat import * import utils.ui as util_ui +import random class Profile(basecontact.BaseContact): @@ -70,7 +69,6 @@ class Profile(basecontact.BaseContact): def new_nospam(self): """Sets new nospam part of tox id""" - import random self._tox.self_set_nospam(random.randint(0, 4294967295)) # no spam - uint32 self._tox_id = self._tox.self_get_address() @@ -106,58 +104,6 @@ class Profile(basecontact.BaseContact): while i < self._messages.count() and not self._messages.itemWidget(self._messages.item(i)).mark_as_sent(): i += 1 - def delete_message(self, message_id): - friend = self.get_curr_friend() - friend.delete_message(time) - self._history.delete_message(friend.tox_id, message_id) - self.update() - - # ----------------------------------------------------------------------------------------------------------------- - # Friend, message and file transfer items creation - # ----------------------------------------------------------------------------------------------------------------- - - def create_message_item(self, text, time, owner, message_type, append=True): - if message_type == MESSAGE_TYPE['INFO_MESSAGE']: - name = '' - elif owner == MESSAGE_OWNER['FRIEND']: - name = self.get_active_name() - else: - name = self._name - pixmap = None - if self._show_avatars: - if owner == MESSAGE_OWNER['FRIEND']: - pixmap = self.get_curr_friend().get_pixmap() - else: - pixmap = self.get_pixmap() - return self._factory.create_message_item(text, time, name, owner != MESSAGE_OWNER['NOT_SENT'], - message_type, append, pixmap) - - def create_gc_message_item(self, text, time, owner, name, message_type, append=True): - pixmap = None - if self._show_avatars: - if owner == MESSAGE_OWNER['FRIEND']: - pixmap = self.get_curr_friend().get_pixmap() - else: - pixmap = self.get_pixmap() - return self._factory.create_message_item(text, time, name, True, - message_type - 5, append, pixmap) - - def create_file_transfer_item(self, tm, append=True): - data = list(tm.get_data()) - data[3] = self.get_friend_by_number(data[4]).name if data[3] else self._name - return self._factory.create_file_transfer_item(data, append) - - def create_unsent_file_item(self, message, append=True): - data = message.get_data() - return self._factory.create_unsent_file_item(os.path.basename(data[0]), - os.path.getsize(data[0]) if data[1] is None else len(data[1]), - self.name, - data[2], - append) - - def create_inline_item(self, data, append=True): - return self._factory.create_inline_item(data, append) - # ----------------------------------------------------------------------------------------------------------------- # Reset # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 073b8b6..8da0811 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -1,7 +1,7 @@ from file_transfers.file_transfers import * from messenger.messages import * from history.database import MESSAGE_AUTHOR -from ui.list_items import * +from ui.contact_items import * from PyQt5 import QtWidgets import utils.util as util diff --git a/toxygen/history/database.py b/toxygen/history/database.py index 4be00c4..78c0bea 100644 --- a/toxygen/history/database.py +++ b/toxygen/history/database.py @@ -37,16 +37,6 @@ class Database: except Exception as ex: util.log('Db reading error: ' + str(ex)) os.remove(path) - db = self._connect() - cursor = db.cursor() - cursor.execute('CREATE TABLE IF NOT EXISTS contacts (' - ' tox_id TEXT PRIMARY KEY,' - ' contact_type INTEGER' - ')') - db.close() - - def _connect(self): - return connect(self._path, timeout=TIMEOUT) # ----------------------------------------------------------------------------------------------------------------- # Public methods @@ -73,10 +63,8 @@ class Database: db = self._connect() try: cursor = db.cursor() - # cursor.execute('INSERT INTO contacts VALUES (?);', (tox_id, )) - cursor.execute('CREATE TABLE id' + tox_id + '(' + cursor.execute('CREATE TABLE IF NOT EXISTS id' + tox_id + '(' ' id INTEGER PRIMARY KEY,' - ' message_id INTEGER,' ' author_name TEXT,' ' message TEXT,' ' author INTEGER,' @@ -94,7 +82,6 @@ class Database: db = self._connect() try: cursor = db.cursor() - cursor.execute('DELETE FROM contacts WHERE tox_id=?;', (tox_id, )) cursor.execute('DROP TABLE id' + tox_id + ';') db.commit() except: @@ -103,20 +90,12 @@ class Database: finally: db.close() - def friend_exists_in_db(self, tox_id): - db = self._connect() - cursor = db.cursor() - cursor.execute('SELECT 1 FROM contacts WHERE tox_id=?', (tox_id, )) - result = cursor.fetchone() - db.close() - return result is not None - def save_messages_to_db(self, tox_id, messages_iter): db = self._connect() try: cursor = db.cursor() cursor.executemany('INSERT INTO id' + tox_id + - '(message, message_id, author_name, author, unix_time, message_type) ' + + '(message, author_name, author, unix_time, message_type) ' + 'VALUES (?, ?, ?, ?, ?, ?);', messages_iter) db.commit() except: @@ -130,7 +109,7 @@ class Database: try: cursor = db.cursor() cursor.execute('UPDATE id' + tox_id + ' SET author = 0 ' - 'WHERE message_id = ' + str(message_id) + ' AND author = 2;') + 'WHERE id = ' + str(message_id) + ' AND author = 2;') db.commit() except: print('Database is locked!') @@ -163,8 +142,7 @@ class Database: db.close() def messages_getter(self, tox_id): - if not self.friend_exists_in_db(tox_id): - self.add_friend_to_db(tox_id) + self.add_friend_to_db(tox_id) return Database.MessageGetter(self._path, tox_id) @@ -180,15 +158,6 @@ class Database: self._tox_id = tox_id self._db = self._cursor = None - def _connect(self): - self._db = connect(self._path, timeout=TIMEOUT) - self._cursor = self._db.cursor() - self._cursor.execute('SELECT id, message_id, message, author, unix_time, message_type FROM id' + - self._tox_id + ' ORDER BY unix_time DESC;') - - def _disconnect(self): - self._db.close() - def get_one(self): return self.get(1) @@ -214,3 +183,19 @@ class Database: def delete_one(self): if self._count: self._count -= 1 + + def _connect(self): + self._db = connect(self._path, timeout=TIMEOUT) + self._cursor = self._db.cursor() + self._cursor.execute('SELECT id, message, author, unix_time, message_type FROM id' + + self._tox_id + ' ORDER BY unix_time DESC;') + + def _disconnect(self): + self._db.close() + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _connect(self): + return connect(self._path, timeout=TIMEOUT) diff --git a/toxygen/history/history.py b/toxygen/history/history.py index 6ded428..234b640 100644 --- a/toxygen/history/history.py +++ b/toxygen/history/history.py @@ -1,14 +1,15 @@ from history.history_logs_generators import * -# TODO: fix history loading and saving - class History: - def __init__(self, contact_provider, db, settings): + def __init__(self, contact_provider, db, settings, main_screen, messages_items_factory): self._contact_provider = contact_provider self._db = db self._settings = settings + self._messages = main_screen.messages + self._messages_items_factory = messages_items_factory + self._is_loading = False def __del__(self): del self._db @@ -23,8 +24,7 @@ class History: """ if self._settings['save_db']: for friend in self._contact_provider.get_all_friends(): - if not self._db.friend_exists_in_db(friend.tox_id): - self._db.add_friend_to_db(friend.tox_id) + self._db.add_friend_to_db(friend.tox_id) if not self._settings['save_unsent_only']: messages = friend.get_corr_for_saving() else: @@ -41,68 +41,50 @@ class History: Clear chat history """ friend.clear_corr(save_unsent) - if self._db.friend_exists_in_db(friend.tox_id): - self._db.delete_messages(friend.tox_id) - self._db.delete_friend_from_db(friend.tox_id) + self._db.delete_friend_from_db(friend.tox_id) def delete_message(self, message): pass - def load_history(self): + def load_history(self, friend): """ Tries to load next part of messages """ - if not self._load_db: + if self._is_loading: return - self._load_db = False - friend = self.get_curr_friend() + self._is_loading = True friend.load_corr(False) - data = friend.get_corr() - if not data: + messages = friend.get_corr() + if not messages: + self._is_loading = False return - data.reverse() - data = data[self._messages.count():self._messages.count() + PAGE_SIZE] - for message in data: + messages.reverse() + messages = messages[self._messages.count():self._messages.count() + PAGE_SIZE] + for message in messages: if message.get_type() <= 1: # text message - data = message.get_data() - self.create_message_item(data[0], - data[2], - data[1], - data[3], - False) + self._create_message_item(message) elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: # file transfer if message.get_status() is None: - self.create_unsent_file_item(message) + self._create_unsent_file_item(message) continue - item = self.create_file_transfer_item(message, False) - if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer - try: - ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] - ft.set_state_changed_handler(item.update_transfer_state) - ft.signal() - except: - print('Incoming not started transfer - no info found') + self._create_file_transfer_item(message) elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline image - self.create_inline_item(message.get_data(), False) + self._create_inline_item(message) else: # info message - data = message.get_data() - self.create_message_item(data[0], - data[2], - '', - data[3], - False) - self._load_db = True + self._create_message_item(message) + self._is_loading = False def get_message_getter(self, friend_public_key): - if not self._db.friend_exists_in_db(friend_public_key): - self._db.add_friend_to_db(friend_public_key) + self._db.add_friend_to_db(friend_public_key) return self._db.messages_getter(friend_public_key) def delete_history(self, friend): self.clear_history(friend) - if self._db.friend_exists_in_db(friend.tox_id): - self._db.delete_friend_from_db(friend.tox_id) + self._db.delete_friend_from_db(friend.tox_id) + + def add_friend_to_db(self, tox_id): + self._db.add_friend_to_db(tox_id) @staticmethod def export_history(contact, as_text=True, _range=None): @@ -117,3 +99,19 @@ class History: generator = TextHistoryGenerator(corr, contact.name) if as_text else HtmlHistoryGenerator(corr, contact.name) return generator.generate() + + # ----------------------------------------------------------------------------------------------------------------- + # Items creation + # ----------------------------------------------------------------------------------------------------------------- + + def _create_message_item(self, message): + return self._messages_items_factory.create_message_item(message, False) + + def _create_unsent_file_item(self, message): + return self._messages_items_factory.create_unsent_file_item(message, False) + + def _create_file_transfer_item(self, message): + return self._messages_items_factory.create_file_transfer_item(message, False) + + def _create_inline_item(self, message): + return self._messages_items_factory.create_inline_item(message, False) diff --git a/toxygen/ui/contact_items.py b/toxygen/ui/contact_items.py new file mode 100644 index 0000000..25b3c0a --- /dev/null +++ b/toxygen/ui/contact_items.py @@ -0,0 +1,100 @@ +from wrapper.toxcore_enums_and_consts import * +from PyQt5 import QtCore, QtGui, QtWidgets +from contacts import profile +from file_transfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR +from utils.util import * +from ui.widgets import DataLabel, create_menu +from user_data import settings + + +class ContactItem(QtWidgets.QWidget): + """ + Contact in friends list + """ + + def __init__(self, settings, parent=None): + QtWidgets.QWidget.__init__(self, parent) + mode = settings['compact_mode'] + self.setBaseSize(QtCore.QSize(250, 40 if mode else 70)) + self.avatar_label = QtWidgets.QLabel(self) + size = 32 if mode else 64 + self.avatar_label.setGeometry(QtCore.QRect(3, 4, size, size)) + self.avatar_label.setScaledContents(False) + self.avatar_label.setAlignment(QtCore.Qt.AlignCenter) + self.name = DataLabel(self) + self.name.setGeometry(QtCore.QRect(50 if mode else 75, 3 if mode else 10, 150, 15 if mode else 25)) + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPointSize(10 if mode else 12) + font.setBold(True) + self.name.setFont(font) + self.status_message = DataLabel(self) + self.status_message.setGeometry(QtCore.QRect(50 if mode else 75, 20 if mode else 30, 170, 15 if mode else 20)) + font.setPointSize(10) + font.setBold(False) + self.status_message.setFont(font) + self.connection_status = StatusCircle(self) + self.connection_status.setGeometry(QtCore.QRect(230, -2 if mode else 5, 32, 32)) + self.messages = UnreadMessagesCount(settings, self) + self.messages.setGeometry(QtCore.QRect(20 if mode else 52, 20 if mode else 50, 30, 20)) + + +class StatusCircle(QtWidgets.QWidget): + """ + Connection status + """ + def __init__(self, parent): + QtWidgets.QWidget.__init__(self, parent) + self.setGeometry(0, 0, 32, 32) + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(0, 0, 32, 32)) + self.unread = False + + def update(self, status, unread_messages=None): + if unread_messages is None: + unread_messages = self.unread + else: + self.unread = unread_messages + if status == TOX_USER_STATUS['NONE']: + name = 'online' + elif status == TOX_USER_STATUS['AWAY']: + name = 'idle' + elif status == TOX_USER_STATUS['BUSY']: + name = 'busy' + else: + name = 'offline' + if unread_messages: + name += '_notification' + self.label.setGeometry(QtCore.QRect(0, 0, 32, 32)) + else: + self.label.setGeometry(QtCore.QRect(2, 0, 32, 32)) + pixmap = QtGui.QPixmap(join_path(get_images_directory(), '{}.png'.format(name))) + self.label.setPixmap(pixmap) + + +class UnreadMessagesCount(QtWidgets.QWidget): + + def __init__(self, settings, parent=None): + super().__init__(parent) + self._settings = settings + self.resize(30, 20) + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(0, 0, 30, 20)) + self.label.setVisible(False) + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPointSize(12) + font.setBold(True) + self.label.setFont(font) + self.label.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignCenter) + color = settings['unread_color'] + self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') + + def update(self, messages_count): + color = self._settings['unread_color'] + self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') + if messages_count: + self.label.setVisible(True) + self.label.setText(str(messages_count)) + else: + self.label.setVisible(False) diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index db2f065..dc9c002 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -1,4 +1,4 @@ -from ui.list_items import * +from ui.contact_items import * from ui.messages_widgets import * @@ -18,17 +18,19 @@ class FriendItemsFactory: return item +# TODO: accept messages everywhere instead of params + class MessagesItemsFactory: - def __init__(self, settings, plugin_loader, smiley_loader, main_screen, history): + def __init__(self, settings, plugin_loader, smiley_loader, main_screen, delete_action): self._settings, self._plugin_loader = settings, plugin_loader - self._smiley_loader, self._history = smiley_loader, history + self._smiley_loader, self._delete_action = smiley_loader, delete_action self._messages = main_screen.messages self._message_edit = main_screen.messageEdit def create_message_item(self, message, append=True, pixmap=None): item = message.get_widget(self._settings, self._create_message_browser, - self._history.delete_message, self._messages) + self._delete_action, self._messages) if pixmap is not None: item.set_avatar(pixmap) elem = QtWidgets.QListWidgetItem() @@ -69,7 +71,7 @@ class MessagesItemsFactory: return item - def create_file_transfer_item(self, data, append): + def create_file_transfer_item(self, data, append=True): data.append(self._messages.width()) item = FileTransferItem(*data) elem = QtWidgets.QListWidgetItem() diff --git a/toxygen/ui/list_items.py b/toxygen/ui/list_items.py deleted file mode 100644 index c9638ec..0000000 --- a/toxygen/ui/list_items.py +++ /dev/null @@ -1,343 +0,0 @@ -from wrapper.toxcore_enums_and_consts import * -from PyQt5 import QtCore, QtGui, QtWidgets -from contacts import profile -from file_transfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR -from utils.util import * -from ui.widgets import DataLabel, create_menu -from user_data import settings - - -class ContactItem(QtWidgets.QWidget): - """ - Contact in friends list - """ - - def __init__(self, settings, parent=None): - QtWidgets.QWidget.__init__(self, parent) - mode = settings['compact_mode'] - self.setBaseSize(QtCore.QSize(250, 40 if mode else 70)) - self.avatar_label = QtWidgets.QLabel(self) - size = 32 if mode else 64 - self.avatar_label.setGeometry(QtCore.QRect(3, 4, size, size)) - self.avatar_label.setScaledContents(False) - self.avatar_label.setAlignment(QtCore.Qt.AlignCenter) - self.name = DataLabel(self) - self.name.setGeometry(QtCore.QRect(50 if mode else 75, 3 if mode else 10, 150, 15 if mode else 25)) - font = QtGui.QFont() - font.setFamily(settings['font']) - font.setPointSize(10 if mode else 12) - font.setBold(True) - self.name.setFont(font) - self.status_message = DataLabel(self) - self.status_message.setGeometry(QtCore.QRect(50 if mode else 75, 20 if mode else 30, 170, 15 if mode else 20)) - font.setPointSize(10) - font.setBold(False) - self.status_message.setFont(font) - self.connection_status = StatusCircle(self) - self.connection_status.setGeometry(QtCore.QRect(230, -2 if mode else 5, 32, 32)) - self.messages = UnreadMessagesCount(settings, self) - self.messages.setGeometry(QtCore.QRect(20 if mode else 52, 20 if mode else 50, 30, 20)) - - -class StatusCircle(QtWidgets.QWidget): - """ - Connection status - """ - def __init__(self, parent): - QtWidgets.QWidget.__init__(self, parent) - self.setGeometry(0, 0, 32, 32) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(0, 0, 32, 32)) - self.unread = False - - def update(self, status, unread_messages=None): - if unread_messages is None: - unread_messages = self.unread - else: - self.unread = unread_messages - if status == TOX_USER_STATUS['NONE']: - name = 'online' - elif status == TOX_USER_STATUS['AWAY']: - name = 'idle' - elif status == TOX_USER_STATUS['BUSY']: - name = 'busy' - else: - name = 'offline' - if unread_messages: - name += '_notification' - self.label.setGeometry(QtCore.QRect(0, 0, 32, 32)) - else: - self.label.setGeometry(QtCore.QRect(2, 0, 32, 32)) - pixmap = QtGui.QPixmap(join_path(get_images_directory(), '{}.png'.format(name))) - self.label.setPixmap(pixmap) - - -class UnreadMessagesCount(QtWidgets.QWidget): - - def __init__(self, settings, parent=None): - super().__init__(parent) - self._settings = settings - self.resize(30, 20) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(0, 0, 30, 20)) - self.label.setVisible(False) - font = QtGui.QFont() - font.setFamily(settings['font']) - font.setPointSize(12) - font.setBold(True) - self.label.setFont(font) - self.label.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignCenter) - color = settings['unread_color'] - self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') - - def update(self, messages_count): - color = self._settings['unread_color'] - self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') - if messages_count: - self.label.setVisible(True) - self.label.setText(str(messages_count)) - else: - self.label.setVisible(False) - - -class FileTransferItem(QtWidgets.QListWidget): - - def __init__(self, file_name, size, time, user, friend_number, file_number, state, width, parent=None): - - QtWidgets.QListWidget.__init__(self, parent) - self.resize(QtCore.QSize(width, 34)) - if state == TOX_FILE_TRANSFER_STATE['CANCELLED']: - self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') - elif state in PAUSED_FILE_TRANSFERS: - self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') - else: - self.setStyleSheet('QListWidget { border: 1px solid green; }') - self.state = state - - self.name = DataLabel(self) - self.name.setGeometry(QtCore.QRect(3, 7, 95, 25)) - self.name.setTextFormat(QtCore.Qt.PlainText) - font = QtGui.QFont() - # FIXME - font.setFamily(settings.Settings.get_instance()['font']) - font.setPointSize(11) - font.setBold(True) - self.name.setFont(font) - self.name.setText(user) - - self.time = QtWidgets.QLabel(self) - self.time.setGeometry(QtCore.QRect(width - 60, 7, 50, 25)) - font.setPointSize(10) - font.setBold(False) - self.time.setFont(font) - self.time.setText(convert_time(time)) - - self.cancel = QtWidgets.QPushButton(self) - self.cancel.setGeometry(QtCore.QRect(width - 125, 2, 30, 30)) - pixmap = QtGui.QPixmap(curr_directory() + '/images/decline.png') - icon = QtGui.QIcon(pixmap) - self.cancel.setIcon(icon) - self.cancel.setIconSize(QtCore.QSize(30, 30)) - self.cancel.setVisible(state in ACTIVE_FILE_TRANSFERS) - self.cancel.clicked.connect(lambda: self.cancel_transfer(friend_number, file_number)) - self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}') - - self.accept_or_pause = QtWidgets.QPushButton(self) - self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30)) - if state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: - self.accept_or_pause.setVisible(True) - self.button_update('accept') - elif state in DO_NOT_SHOW_ACCEPT_BUTTON: - self.accept_or_pause.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # setup for continue - self.accept_or_pause.setVisible(True) - self.button_update('resume') - else: # pause - self.accept_or_pause.setVisible(True) - self.button_update('pause') - self.accept_or_pause.clicked.connect(lambda: self.accept_or_pause_transfer(friend_number, file_number, size)) - - self.accept_or_pause.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none}') - - self.pb = QtWidgets.QProgressBar(self) - self.pb.setGeometry(QtCore.QRect(100, 7, 100, 20)) - self.pb.setValue(0) - self.pb.setStyleSheet('QProgressBar { background-color: #302F2F; }') - self.pb.setVisible(state in SHOW_PROGRESS_BAR) - - self.file_name = DataLabel(self) - self.file_name.setGeometry(QtCore.QRect(210, 7, width - 420, 20)) - font.setPointSize(12) - self.file_name.setFont(font) - file_size = size // 1024 - if not file_size: - file_size = '{}B'.format(size) - elif file_size >= 1024: - file_size = '{}MB'.format(file_size // 1024) - else: - file_size = '{}KB'.format(file_size) - file_data = '{} {}'.format(file_size, file_name) - self.file_name.setText(file_data) - self.file_name.setToolTip(file_name) - self.saved_name = file_name - self.time_left = QtWidgets.QLabel(self) - self.time_left.setGeometry(QtCore.QRect(width - 92, 7, 30, 20)) - font.setPointSize(10) - self.time_left.setFont(font) - self.time_left.setVisible(state == TOX_FILE_TRANSFER_STATE['RUNNING']) - self.setFocusPolicy(QtCore.Qt.NoFocus) - self.paused = False - - def cancel_transfer(self, friend_number, file_number): - pr = profile.Profile.get_instance() - pr.cancel_transfer(friend_number, file_number) - self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') - self.cancel.setVisible(False) - self.accept_or_pause.setVisible(False) - self.pb.setVisible(False) - - def accept_or_pause_transfer(self, friend_number, file_number, size): - if self.state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", 'Choose folder'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - self.pb.setVisible(True) - if directory: - pr = profile.Profile.get_instance() - pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size) - self.button_update('pause') - elif self.state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # resume - self.paused = False - profile.Profile.get_instance().resume_transfer(friend_number, file_number) - self.button_update('pause') - self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] - else: # pause - self.paused = True - self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER'] - profile.Profile.get_instance().pause_transfer(friend_number, file_number) - self.button_update('resume') - self.accept_or_pause.clearFocus() - - def button_update(self, path): - pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(path)) - icon = QtGui.QIcon(pixmap) - self.accept_or_pause.setIcon(icon) - self.accept_or_pause.setIconSize(QtCore.QSize(30, 30)) - - def update_transfer_state(self, state, progress, time): - self.pb.setValue(int(progress * 100)) - if time + 1: - m, s = divmod(time, 60) - self.time_left.setText('{0:02d}:{1:02d}'.format(m, s)) - if self.state != state and self.state in ACTIVE_FILE_TRANSFERS: - if state == TOX_FILE_TRANSFER_STATE['CANCELLED']: - self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') - self.cancel.setVisible(False) - self.accept_or_pause.setVisible(False) - self.pb.setVisible(False) - self.state = state - self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['FINISHED']: - self.accept_or_pause.setVisible(False) - self.pb.setVisible(False) - self.cancel.setVisible(False) - self.setStyleSheet('QListWidget { border: 1px solid green; }') - self.state = state - self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']: - self.accept_or_pause.setVisible(False) - self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') - self.state = state - self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: - self.button_update('resume') # setup button continue - self.setStyleSheet('QListWidget { border: 1px solid green; }') - self.state = state - self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']: - self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') - self.accept_or_pause.setVisible(False) - self.time_left.setVisible(False) - self.pb.setVisible(False) - elif not self.paused: # active - self.pb.setVisible(True) - self.accept_or_pause.setVisible(True) # setup to pause - self.button_update('pause') - self.setStyleSheet('QListWidget { border: 1px solid green; }') - self.state = state - self.time_left.setVisible(True) - - def mark_as_sent(self): - return False - - -class UnsentFileItem(FileTransferItem): - - def __init__(self, file_name, size, user, time, width, parent=None): - super(UnsentFileItem, self).__init__(file_name, size, time, user, -1, -1, - TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'], width, parent) - self._time = time - self.pb.setVisible(False) - movie = QtGui.QMovie(join_path(get_images_directory(), 'spinner.gif')) - self.time.setMovie(movie) - movie.start() - - def cancel_transfer(self, *args): - pr = profile.Profile.get_instance() - pr.cancel_not_started_transfer(self._time) - - -class InlineImageItem(QtWidgets.QScrollArea): - - def __init__(self, data, width, elem): - - QtWidgets.QScrollArea.__init__(self) - self.setFocusPolicy(QtCore.Qt.NoFocus) - self._elem = elem - self._image_label = QtWidgets.QLabel(self) - self._image_label.raise_() - self.setWidget(self._image_label) - self._image_label.setScaledContents(False) - self._pixmap = QtGui.QPixmap() - self._pixmap.loadFromData(data, 'PNG') - self._max_size = width - 30 - self._resize_needed = not (self._pixmap.width() <= self._max_size) - self._full_size = not self._resize_needed - if not self._resize_needed: - self._image_label.setPixmap(self._pixmap) - self.resize(QtCore.QSize(self._max_size + 5, self._pixmap.height() + 5)) - self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height()) - else: - pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio) - self._image_label.setPixmap(pixmap) - self.resize(QtCore.QSize(self._max_size + 5, pixmap.height())) - self._image_label.setGeometry(5, 0, self._max_size + 5, pixmap.height()) - self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.LeftButton and self._resize_needed: # scale inline - if self._full_size: - pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio) - self._image_label.setPixmap(pixmap) - self.resize(QtCore.QSize(self._max_size, pixmap.height())) - self._image_label.setGeometry(5, 0, pixmap.width(), pixmap.height()) - else: - self._image_label.setPixmap(self._pixmap) - self.resize(QtCore.QSize(self._max_size, self._pixmap.height() + 17)) - self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height()) - self._full_size = not self._full_size - self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) - elif event.button() == QtCore.Qt.RightButton: # save inline - # TODO: dialog - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", - 'Choose folder'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - if directory: - fl = QtCore.QFile(directory + '/toxygen_inline_' + curr_time().replace(':', '_') + '.png') - self._pixmap.save(fl, 'PNG') - - def mark_as_sent(self): - return False diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index a8a7c13..cce78a4 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -1,5 +1,5 @@ from contacts.profile import * -from ui.list_items import * +from ui.contact_items import * from ui.widgets import MultilineEdit, ComboBox from ui.main_screen_widgets import * import utils.util as util diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index 0f510ea..dea7a5c 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -213,3 +213,245 @@ class MessageItem(QtWidgets.QWidget): i += len(sub) return text + +class FileTransferItem(QtWidgets.QListWidget): + + def __init__(self, file_name, size, time, user, friend_number, file_number, state, width, parent=None): + + QtWidgets.QListWidget.__init__(self, parent) + self.resize(QtCore.QSize(width, 34)) + if state == TOX_FILE_TRANSFER_STATE['CANCELLED']: + self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') + elif state in PAUSED_FILE_TRANSFERS: + self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') + else: + self.setStyleSheet('QListWidget { border: 1px solid green; }') + self.state = state + + self.name = DataLabel(self) + self.name.setGeometry(QtCore.QRect(3, 7, 95, 25)) + self.name.setTextFormat(QtCore.Qt.PlainText) + font = QtGui.QFont() + # FIXME + font.setFamily(settings.Settings.get_instance()['font']) + font.setPointSize(11) + font.setBold(True) + self.name.setFont(font) + self.name.setText(user) + + self.time = QtWidgets.QLabel(self) + self.time.setGeometry(QtCore.QRect(width - 60, 7, 50, 25)) + font.setPointSize(10) + font.setBold(False) + self.time.setFont(font) + self.time.setText(convert_time(time)) + + self.cancel = QtWidgets.QPushButton(self) + self.cancel.setGeometry(QtCore.QRect(width - 125, 2, 30, 30)) + pixmap = QtGui.QPixmap(curr_directory() + '/images/decline.png') + icon = QtGui.QIcon(pixmap) + self.cancel.setIcon(icon) + self.cancel.setIconSize(QtCore.QSize(30, 30)) + self.cancel.setVisible(state in ACTIVE_FILE_TRANSFERS) + self.cancel.clicked.connect(lambda: self.cancel_transfer(friend_number, file_number)) + self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}') + + self.accept_or_pause = QtWidgets.QPushButton(self) + self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30)) + if state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + self.accept_or_pause.setVisible(True) + self.button_update('accept') + elif state in DO_NOT_SHOW_ACCEPT_BUTTON: + self.accept_or_pause.setVisible(False) + elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # setup for continue + self.accept_or_pause.setVisible(True) + self.button_update('resume') + else: # pause + self.accept_or_pause.setVisible(True) + self.button_update('pause') + self.accept_or_pause.clicked.connect(lambda: self.accept_or_pause_transfer(friend_number, file_number, size)) + + self.accept_or_pause.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none}') + + self.pb = QtWidgets.QProgressBar(self) + self.pb.setGeometry(QtCore.QRect(100, 7, 100, 20)) + self.pb.setValue(0) + self.pb.setStyleSheet('QProgressBar { background-color: #302F2F; }') + self.pb.setVisible(state in SHOW_PROGRESS_BAR) + + self.file_name = DataLabel(self) + self.file_name.setGeometry(QtCore.QRect(210, 7, width - 420, 20)) + font.setPointSize(12) + self.file_name.setFont(font) + file_size = size // 1024 + if not file_size: + file_size = '{}B'.format(size) + elif file_size >= 1024: + file_size = '{}MB'.format(file_size // 1024) + else: + file_size = '{}KB'.format(file_size) + file_data = '{} {}'.format(file_size, file_name) + self.file_name.setText(file_data) + self.file_name.setToolTip(file_name) + self.saved_name = file_name + self.time_left = QtWidgets.QLabel(self) + self.time_left.setGeometry(QtCore.QRect(width - 92, 7, 30, 20)) + font.setPointSize(10) + self.time_left.setFont(font) + self.time_left.setVisible(state == TOX_FILE_TRANSFER_STATE['RUNNING']) + self.setFocusPolicy(QtCore.Qt.NoFocus) + self.paused = False + + def cancel_transfer(self, friend_number, file_number): + pr = profile.Profile.get_instance() + pr.cancel_transfer(friend_number, file_number) + self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') + self.cancel.setVisible(False) + self.accept_or_pause.setVisible(False) + self.pb.setVisible(False) + + def accept_or_pause_transfer(self, friend_number, file_number, size): + if self.state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + directory = QtWidgets.QFileDialog.getExistingDirectory(self, + QtWidgets.QApplication.translate("MainWindow", 'Choose folder'), + curr_directory(), + QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) + self.pb.setVisible(True) + if directory: + pr = profile.Profile.get_instance() + pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size) + self.button_update('pause') + elif self.state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # resume + self.paused = False + profile.Profile.get_instance().resume_transfer(friend_number, file_number) + self.button_update('pause') + self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] + else: # pause + self.paused = True + self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER'] + profile.Profile.get_instance().pause_transfer(friend_number, file_number) + self.button_update('resume') + self.accept_or_pause.clearFocus() + + def button_update(self, path): + pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(path)) + icon = QtGui.QIcon(pixmap) + self.accept_or_pause.setIcon(icon) + self.accept_or_pause.setIconSize(QtCore.QSize(30, 30)) + + def update_transfer_state(self, state, progress, time): + self.pb.setValue(int(progress * 100)) + if time + 1: + m, s = divmod(time, 60) + self.time_left.setText('{0:02d}:{1:02d}'.format(m, s)) + if self.state != state and self.state in ACTIVE_FILE_TRANSFERS: + if state == TOX_FILE_TRANSFER_STATE['CANCELLED']: + self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') + self.cancel.setVisible(False) + self.accept_or_pause.setVisible(False) + self.pb.setVisible(False) + self.state = state + self.time_left.setVisible(False) + elif state == TOX_FILE_TRANSFER_STATE['FINISHED']: + self.accept_or_pause.setVisible(False) + self.pb.setVisible(False) + self.cancel.setVisible(False) + self.setStyleSheet('QListWidget { border: 1px solid green; }') + self.state = state + self.time_left.setVisible(False) + elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']: + self.accept_or_pause.setVisible(False) + self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') + self.state = state + self.time_left.setVisible(False) + elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: + self.button_update('resume') # setup button continue + self.setStyleSheet('QListWidget { border: 1px solid green; }') + self.state = state + self.time_left.setVisible(False) + elif state == TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']: + self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') + self.accept_or_pause.setVisible(False) + self.time_left.setVisible(False) + self.pb.setVisible(False) + elif not self.paused: # active + self.pb.setVisible(True) + self.accept_or_pause.setVisible(True) # setup to pause + self.button_update('pause') + self.setStyleSheet('QListWidget { border: 1px solid green; }') + self.state = state + self.time_left.setVisible(True) + + def mark_as_sent(self): + return False + + +class UnsentFileItem(FileTransferItem): + + def __init__(self, file_name, size, user, time, width, parent=None): + super(UnsentFileItem, self).__init__(file_name, size, time, user, -1, -1, + TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'], width, parent) + self._time = time + self.pb.setVisible(False) + movie = QtGui.QMovie(join_path(get_images_directory(), 'spinner.gif')) + self.time.setMovie(movie) + movie.start() + + def cancel_transfer(self, *args): + pr = profile.Profile.get_instance() + pr.cancel_not_started_transfer(self._time) + + +class InlineImageItem(QtWidgets.QScrollArea): + + def __init__(self, data, width, elem): + + QtWidgets.QScrollArea.__init__(self) + self.setFocusPolicy(QtCore.Qt.NoFocus) + self._elem = elem + self._image_label = QtWidgets.QLabel(self) + self._image_label.raise_() + self.setWidget(self._image_label) + self._image_label.setScaledContents(False) + self._pixmap = QtGui.QPixmap() + self._pixmap.loadFromData(data, 'PNG') + self._max_size = width - 30 + self._resize_needed = not (self._pixmap.width() <= self._max_size) + self._full_size = not self._resize_needed + if not self._resize_needed: + self._image_label.setPixmap(self._pixmap) + self.resize(QtCore.QSize(self._max_size + 5, self._pixmap.height() + 5)) + self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height()) + else: + pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio) + self._image_label.setPixmap(pixmap) + self.resize(QtCore.QSize(self._max_size + 5, pixmap.height())) + self._image_label.setGeometry(5, 0, self._max_size + 5, pixmap.height()) + self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) + + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.LeftButton and self._resize_needed: # scale inline + if self._full_size: + pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio) + self._image_label.setPixmap(pixmap) + self.resize(QtCore.QSize(self._max_size, pixmap.height())) + self._image_label.setGeometry(5, 0, pixmap.width(), pixmap.height()) + else: + self._image_label.setPixmap(self._pixmap) + self.resize(QtCore.QSize(self._max_size, self._pixmap.height() + 17)) + self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height()) + self._full_size = not self._full_size + self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) + elif event.button() == QtCore.Qt.RightButton: # save inline + # TODO: dialog + directory = QtWidgets.QFileDialog.getExistingDirectory(self, + QtWidgets.QApplication.translate("MainWindow", + 'Choose folder'), + curr_directory(), + QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) + if directory: + fl = QtCore.QFile(directory + '/toxygen_inline_' + curr_time().replace(':', '_') + '.png') + self._pixmap.save(fl, 'PNG') + + def mark_as_sent(self): + return False From 2883ce5c4c0ff2858f65a562a8c82f65d5ae0bc0 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 May 2018 14:10:24 +0300 Subject: [PATCH 027/217] messaging - db and saving fixes --- toxygen/app.py | 1 + toxygen/contacts/contact.py | 22 +++++++++++++------- toxygen/history/database.py | 6 +++--- toxygen/history/history.py | 17 +++++++++++----- toxygen/messenger/messages.py | 38 ++++++++++++++++++++++++----------- 5 files changed, 57 insertions(+), 27 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index ef5e351..2d12876 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -309,6 +309,7 @@ class App: self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, self._contacts_provider, history, self._tox_dns, messages_items_factory) + history.set_contacts_manager(self._contacts_manager) self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, self._contacts_provider, messages_items_factory, profile) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 4c19e33..0718e5e 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -53,7 +53,7 @@ class Contact(basecontact.BaseContact): data.reverse() else: return - data = list(map(lambda tupl: TextMessage(*tupl), data)) + data = list(map(lambda p: self._get_text_message(p), data)) self._corr = data + self._corr self._history_loaded = True @@ -66,7 +66,7 @@ class Contact(basecontact.BaseContact): data = list(self._message_getter.get_all()) if data is not None and len(data): data.reverse() - data = list(map(lambda tupl: TextMessage(*tupl), data)) + data = list(map(lambda p: self._get_text_message(p), data)) self._corr = data + self._corr self._history_loaded = True @@ -76,7 +76,7 @@ class Contact(basecontact.BaseContact): :return: list of unsaved messages or [] """ messages = list(filter(lambda x: x.get_type() <= 1, self._corr)) - return list(map(lambda x: x.get_data(), messages[-self._unsaved_messages:])) if self._unsaved_messages else [] + return messages[-self._unsaved_messages:] if self._unsaved_messages else [] def get_corr(self): return self._corr[:] @@ -92,7 +92,7 @@ class Contact(basecontact.BaseContact): def get_last_message_text(self): messages = list(filter(lambda x: x.get_type() <= 1 and x.get_owner() != MESSAGE_AUTHOR['FRIEND'], self._corr)) if messages: - return messages[-1].get_data()[0] + return messages[-1].text else: return '' @@ -100,6 +100,13 @@ class Contact(basecontact.BaseContact): for message in self._corr: message.remove_widget() + @staticmethod + def _get_text_message(params): + (message, author_type, author_name, unix_time, message_type, unique_id) = params + author = MessageAuthor(author_name, author_type) + + return TextMessage(message, author, unix_time, message_type, unique_id) + # ----------------------------------------------------------------------------------------------------------------- # Unsent messages # ----------------------------------------------------------------------------------------------------------------- @@ -130,8 +137,9 @@ class Contact(basecontact.BaseContact): # ----------------------------------------------------------------------------------------------------------------- def delete_message(self, message_id): - elem = list(filter(lambda x: type(x) in (TextMessage, GroupChatMessage) and x.message_id == message_id, self._corr))[0] - tmp = list(filter(lambda x: x.get_type() <= 1, self._corr)) + elem = list(filter(lambda x: type(x) in (TextMessage, GroupChatMessage) and x.message_id == message_id, + self._corr))[0] + tmp = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']), self._corr)) if elem in tmp[-self._unsaved_messages:] and self._unsaved_messages: self._unsaved_messages -= 1 self._corr.remove(elem) @@ -143,7 +151,7 @@ class Contact(basecontact.BaseContact): Delete old messages (reduces RAM usage if messages saving is not enabled) """ def save_message(x): - if x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None): + if x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None): # FIXME MAGIC NUMBERS return True return x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'] diff --git a/toxygen/history/database.py b/toxygen/history/database.py index 78c0bea..751c74b 100644 --- a/toxygen/history/database.py +++ b/toxygen/history/database.py @@ -67,7 +67,7 @@ class Database: ' id INTEGER PRIMARY KEY,' ' author_name TEXT,' ' message TEXT,' - ' author INTEGER,' + ' author_type INTEGER,' ' unix_time REAL,' ' message_type INTEGER' ')') @@ -95,7 +95,7 @@ class Database: try: cursor = db.cursor() cursor.executemany('INSERT INTO id' + tox_id + - '(message, author_name, author, unix_time, message_type) ' + + '(message, author_name, author_type, unix_time, message_type) ' + 'VALUES (?, ?, ?, ?, ?, ?);', messages_iter) db.commit() except: @@ -187,7 +187,7 @@ class Database: def _connect(self): self._db = connect(self._path, timeout=TIMEOUT) self._cursor = self._db.cursor() - self._cursor.execute('SELECT id, message, author, unix_time, message_type FROM id' + + self._cursor.execute('SELECT message, author_type, author_name, unix_time, message_type, id FROM id' + self._tox_id + ' ORDER BY unix_time DESC;') def _disconnect(self): diff --git a/toxygen/history/history.py b/toxygen/history/history.py index 234b640..21bad8c 100644 --- a/toxygen/history/history.py +++ b/toxygen/history/history.py @@ -10,10 +10,15 @@ class History: self._messages = main_screen.messages self._messages_items_factory = messages_items_factory self._is_loading = False + self._contacts_manager = None def __del__(self): del self._db + + def set_contacts_manager(self, contacts_manager): + self._contacts_manager = contacts_manager + # ----------------------------------------------------------------------------------------------------------------- # History support # ----------------------------------------------------------------------------------------------------------------- @@ -30,10 +35,9 @@ class History: else: messages = friend.get_unsent_messages_for_saving() self._db.delete_messages(friend.tox_id) + messages = map(lambda m: (m.text, m.author.name, m.author.type, m.time, m.type), messages) self._db.save_messages_to_db(friend.tox_id, messages) - unsent_messages = friend.get_unsent_messages() - # unsent_time = unsent_messages[0].get_data()[2] if len(unsent_messages) else time.time() + 1 - # self._db.update_messages(friend.tox_id, unsent_time) + self._db.save() def clear_history(self, friend, save_unsent=False): @@ -44,7 +48,11 @@ class History: self._db.delete_friend_from_db(friend.tox_id) def delete_message(self, message): - pass + contact = self._contacts_manager.get_curr_contact() + if message.type in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']): + if message.is_saved(): + self._db.delete_message(contact.tox_id, message.id) + contact.delete_message(message.message_id) def load_history(self, friend): """ @@ -80,7 +88,6 @@ class History: return self._db.messages_getter(friend_public_key) def delete_history(self, friend): - self.clear_history(friend) self._db.delete_friend_from_db(friend.tox_id) def add_friend_to_db(self, tox_id): diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index dc70e8d..80376f4 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -22,11 +22,14 @@ class MessageAuthor: class Message: + MESSAGE_ID = 0 + def __init__(self, message_type, author, time): self._time = time self._type = message_type self._author = author self._widget = None + self._message_id = self._get_id() def get_type(self): return self._type @@ -43,6 +46,11 @@ class Message: time = property(get_time) + def get_message_id(self): + return self._message_id + + message_id = property(get_message_id) + def get_widget(self, *args): if self._widget is None: self._widget = self._create_widget(*args) @@ -60,23 +68,35 @@ class Message: def _create_widget(self, *args): pass + @staticmethod + def _get_id(): + Message.MESSAGE_ID += 1 + + return Message.MESSAGE_ID + class TextMessage(Message): """ Plain text or action message """ - def __init__(self, message, owner, time, message_type): + def __init__(self, message, owner, time, message_type, message_id=0): super().__init__(message_type, owner, time) self._message = message + self._id = message_id def get_text(self): return self._message text = property(get_text) - def get_data(self): - return self._message, self._owner, self._time, self._type + def get_id(self): + return self._id + + id = property(get_id) + + def is_saved(self): + return self._id > 0 def _create_widget(self, *args): return MessageItem(self, *args) @@ -100,9 +120,6 @@ class GroupChatMessage(TextMessage): super().__init__(id, message, owner, time, message_type) self._user_name = name - def get_data(self): - return self._message, self._owner, self._time, self._type, self._user_name - class TransferMessage(Message): """ @@ -131,18 +148,13 @@ class TransferMessage(Message): def set_status(self, value): self._status = value - def get_data(self): - return self._file_name, self._size, self._time, self._owner, self._friend_number, self._file_number, self._status - class UnsentFile(Message): + def __init__(self, id, path, data, time): super().__init__(id, MESSAGE_TYPE['FILE_TRANSFER'], 0, time) self._data, self._path = data, path - def get_data(self): - return self._path, self._data, self._time - def get_status(self): return None @@ -159,6 +171,8 @@ class InlineImage(Message): def get_data(self): return self._data + data = property(get_data) + class InfoMessage(TextMessage): From 7209dfae72e2769d0fee2e16930d2de7dff208c1 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 May 2018 14:47:14 +0300 Subject: [PATCH 028/217] minor refactoring and todo's for file transfers --- toxygen/contacts/contact.py | 19 ++++++----- toxygen/file_transfers/file_transfers.py | 8 +++-- .../file_transfers/file_transfers_handler.py | 34 ++++++++----------- toxygen/history/history.py | 3 +- toxygen/messenger/messages.py | 8 ++--- toxygen/ui/main_screen.py | 2 +- toxygen/ui/main_screen_widgets.py | 2 +- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 0718e5e..8c1fb06 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -75,7 +75,7 @@ class Contact(basecontact.BaseContact): Get data to save in db :return: list of unsaved messages or [] """ - messages = list(filter(lambda x: x.get_type() <= 1, self._corr)) + messages = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']), self._corr)) return messages[-self._unsaved_messages:] if self._unsaved_messages else [] def get_corr(self): @@ -86,11 +86,12 @@ class Contact(basecontact.BaseContact): :param message: text or file transfer message """ self._corr.append(message) - if message.get_type() <= 1: + if message.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']): self._unsaved_messages += 1 def get_last_message_text(self): - messages = list(filter(lambda x: x.get_type() <= 1 and x.get_owner() != MESSAGE_AUTHOR['FRIEND'], self._corr)) + messages = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']) + and x.get_owner() != MESSAGE_AUTHOR['FRIEND'], self._corr)) if messages: return messages[-1].text else: @@ -122,7 +123,8 @@ class Contact(basecontact.BaseContact): """ :return list of unsent messages for saving """ - messages = filter(lambda x: x.get_type() <= 1 and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr) + messages = filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']) + and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(map(lambda x: x.get_data(), messages)) def mark_as_sent(self): @@ -157,7 +159,7 @@ class Contact(basecontact.BaseContact): old = filter(save_message, self._corr[:-SAVE_MESSAGES]) self._corr = list(old) + self._corr[-SAVE_MESSAGES:] - text_messages = filter(lambda x: x.get_type() <= 1, self._corr) + text_messages = filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']), self._corr) self._unsaved_messages = min(self._unsaved_messages, len(list(text_messages))) self._search_index = 0 @@ -175,7 +177,8 @@ class Contact(basecontact.BaseContact): self._unsaved_messages = 0 else: self._corr = list(filter(lambda x: (x.get_type() == 2 and x.get_status() in ft.ACTIVE_FILE_TRANSFERS) - or (x.get_type() <= 1 and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT']), + or (x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']) + and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT']), self._corr)) self._unsaved_messages = len(self.get_unsent_messages()) @@ -193,7 +196,7 @@ class Contact(basecontact.BaseContact): for i in range(self._search_index - 1, -l - 1, -1): if self._corr[i].get_type() > 1: continue - message = self._corr[i].get_data()[0] + message = self._corr[i].text if re.search(self._search_string, message, re.IGNORECASE) is not None: self._search_index = i return i @@ -208,7 +211,7 @@ class Contact(basecontact.BaseContact): for i in range(self._search_index + 1, 0): if self._corr[i].get_type() > 1: continue - message = self._corr[i].get_data()[0] + message = self._corr[i].text if re.search(self._search_string, message, re.IGNORECASE) is not None: self._search_index = i return i diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 596642e..4bbcc18 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -24,12 +24,14 @@ DO_NOT_SHOW_ACCEPT_BUTTON = (2, 3, 4, 6) SHOW_PROGRESS_BAR = (0, 1, 4) -ALLOWED_FILES = ('toxygen_inline.png', 'utox-inline.png', 'sticker.png') - def is_inline(file_name): - return file_name in ALLOWED_FILES or file_name.startswith('qTox_Screenshot_') + allowed_inlines = ('toxygen_inline.png', 'utox-inline.png', 'sticker.png') + return file_name in allowed_inlines or file_name.startswith('qTox_Screenshot_') + + +# TODO: use events from common.event.py class StateSignal(QtCore.QObject): diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 8da0811..42a69fa 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -214,20 +214,18 @@ class FileTransfersHandler: st.set_state_changed_handler(item.update_transfer_state) self._messages.scrollToBottom() - def send_file(self, path, number=None, is_resend=False, file_id=None): + def send_file(self, path, friend_number, is_resend=False, file_id=None): """ Send file to current active friend :param path: file path - :param number: friend_number + :param friend_number: friend_number :param is_resend: is 'offline' message :param file_id: file id of transfer """ - friend_number = self.get_active_number() if number is None else number friend = self._get_friend_by_number(friend_number) if friend.status is None and not is_resend: m = UnsentFile(path, None, time.time()) friend.append_message(m) - self.update() return elif friend.status is None and is_resend: print('Error in sending') @@ -235,18 +233,18 @@ class FileTransfersHandler: st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) st.set_transfer_finished_handler(self.transfer_finished) self._file_transfers[(friend_number, st.get_file_number())] = st - tm = TransferMessage(MESSAGE_AUTHOR['ME'], - time.time(), - TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], - os.path.getsize(path), - os.path.basename(path), - friend_number, - st.get_file_number()) - if friend_number == self.get_active_number(): - item = self.create_file_transfer_item(tm) - st.set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() - self._contacts[friend_number].append_message(tm) + # tm = TransferMessage(MESSAGE_AUTHOR['ME'], + # time.time(), + # TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], + # os.path.getsize(path), + # os.path.basename(path), + # friend_number, + # st.get_file_number()) + # if friend_number == self.get_active_number(): + # item = self.create_file_transfer_item(tm) + # st.set_state_changed_handler(item.update_transfer_state) + # self._messages.scrollToBottom() + # self._contacts[friend_number].append_message(tm) def incoming_chunk(self, friend_number, file_number, position, data): """ @@ -265,8 +263,6 @@ class FileTransfersHandler: t = type(transfer) if t is ReceiveAvatar: self._get_friend_by_number(friend_number).load_avatar() - if friend_number == self.get_active_number() and self.is_active_a_friend(): - self.set_active(None) elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']): # inline image print('inline') inline = InlineImage(transfer.get_data()) @@ -284,7 +280,7 @@ class FileTransfersHandler: self._messages.scrollToBottom() elif t is not SendAvatar: self._get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['FINISHED']) + TOX_FILE_TRANSFER_STATE['FINISHED']) del self._file_transfers[(friend_number, file_number)] del transfer diff --git a/toxygen/history/history.py b/toxygen/history/history.py index 21bad8c..e6f5e44 100644 --- a/toxygen/history/history.py +++ b/toxygen/history/history.py @@ -15,7 +15,6 @@ class History: def __del__(self): del self._db - def set_contacts_manager(self, contacts_manager): self._contacts_manager = contacts_manager @@ -69,7 +68,7 @@ class History: messages.reverse() messages = messages[self._messages.count():self._messages.count() + PAGE_SIZE] for message in messages: - if message.get_type() <= 1: # text message + if message.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']): # text message self._create_message_item(message) elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: # file transfer if message.get_status() is None: diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 80376f4..42a0fd1 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -151,8 +151,8 @@ class TransferMessage(Message): class UnsentFile(Message): - def __init__(self, id, path, data, time): - super().__init__(id, MESSAGE_TYPE['FILE_TRANSFER'], 0, time) + def __init__(self, path, data, time): + super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], 0, time) self._data, self._path = data, path def get_status(self): @@ -164,8 +164,8 @@ class InlineImage(Message): Inline image """ - def __init__(self, id, data): - super().__init__(id, MESSAGE_TYPE['INLINE'], None, None) + def __init__(self, data): + super().__init__(MESSAGE_TYPE['INLINE'], None, None) self._data = data def get_data(self): diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index cce78a4..3d9fd0f 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -519,7 +519,7 @@ class MainWindow(QtWidgets.QMainWindow): caption = util_ui.tr('Choose file') name = util_ui.file_dialog(caption) if name[0]: - self._contacts_manager.send_file(name[0]) + self._contacts_manager.send_file(name[0], self._contacts_manager.get_contact().number) def send_screenshot(self, hide=False): self.menu.hide() diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index ecf329a..dfd5ea7 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -116,7 +116,7 @@ class ScreenShotWindow(RubberBandWindow): buffer.open(QtCore.QIODevice.WriteOnly) p.save(buffer, 'PNG') friend = self._contacts_manager.get_curr_contact() - self._file_transfer_handler.send_screenshot(bytes(byte_array.data(), friend.number)) + self._file_transfer_handler.send_screenshot(bytes(byte_array.data()), friend.number) self.close() From bfd2a92ddec6b6e86136640fafc26faf4708afe0 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 May 2018 19:04:02 +0300 Subject: [PATCH 029/217] initial fixes for file transfers - messages, widgets --- tests/tests.py | 2 +- toxygen/app.py | 7 +- toxygen/contacts/contacts_manager.py | 35 +++--- toxygen/contacts/profile.py | 2 +- toxygen/file_transfers/file_transfers.py | 96 +++++++-------- .../file_transfers/file_transfers_handler.py | 106 ++++------------- .../file_transfers_messages_service.py | 58 +++++++++ toxygen/messenger/messages.py | 34 ++++-- toxygen/ui/contact_items.py | 2 +- toxygen/ui/items_factories.py | 23 ++-- toxygen/ui/messages_widgets.py | 110 +++++++++--------- 11 files changed, 245 insertions(+), 230 deletions(-) create mode 100644 toxygen/file_transfers/file_transfers_messages_service.py diff --git a/tests/tests.py b/tests/tests.py index 1047de4..442a677 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -126,7 +126,7 @@ class TestFriend: assert arr[0][0] == 'Not sent' tm = TransferMessage(MESSAGE_OWNER['FRIEND'], time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], + FILE_TRANSFER_STATE['RUNNING'], 100, 'file_name', friend.number, 0) friend.append_message(tm) friend.clear_corr() diff --git a/toxygen/app.py b/toxygen/app.py index 2d12876..ce15093 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -28,6 +28,7 @@ from ui.items_factories import MessagesItemsFactory, FriendItemsFactory from messenger.messenger import Messenger from network.tox_dns import ToxDns from history.history import History +from file_transfers.file_transfers_messages_service import FileTransfersMessagesService class App: @@ -312,7 +313,11 @@ class App: history.set_contacts_manager(self._contacts_manager) self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, self._contacts_provider, messages_items_factory, profile) - self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider) + file_transfers_message_service = FileTransfersMessagesService(self._contacts_manager, messages_items_factory, + profile, self._ms) + self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider, + file_transfers_message_service) + messages_items_factory.set_file_transfers_handler(self._file_transfer_handler) widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, self._toxes, self._version) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index b65acfa..7570c06 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -83,18 +83,18 @@ class ContactsManager: current_contact.curr_text = self._screen.messageEdit.toPlainText() except: pass - friend = self._contacts[value] - self._subscribe_to_events(friend) - friend.remove_invalid_unsent_files() + contact = self._contacts[value] + self._subscribe_to_events(contact) + contact.remove_invalid_unsent_files() if self._active_contact != value: - self._screen.messageEdit.setPlainText(friend.curr_text) + self._screen.messageEdit.setPlainText(contact.curr_text) self._active_contact = value - friend.reset_messages() + contact.reset_messages() if not self._settings['save_history']: - friend.delete_old_messages() + contact.delete_old_messages() self._messages.clear() - friend.load_corr() - corr = friend.get_corr()[-PAGE_SIZE:] + contact.load_corr() + corr = contact.get_corr()[-PAGE_SIZE:] for message in corr: self._messages_items_factory.create_message_item(message) # if value in self._call: @@ -103,12 +103,8 @@ class ContactsManager: # self._screen.incoming_call() # else: # self._screen.call_finished() + self._set_current_contact_data(contact) - self._screen.account_status.setToolTip(friend.get_full_status()) - avatar_path = friend.get_avatar_path() - pixmap = QtGui.QPixmap(avatar_path) - self._screen.account_avatar.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation)) except Exception as ex: # no friend found. ignore util.log('Friend value: ' + str(value)) util.log('Error in set active: ' + str(ex)) @@ -433,8 +429,15 @@ class ContactsManager: self._screen.account_status.setText(status_message) def _current_contact_avatar_changed(self, avatar_path): + self._set_current_contact_avatar(avatar_path) + + def _set_current_contact_data(self, contact): + self._screen.account_name.setText(contact.name) + self._screen.account_status.setText(contact.status_message) + self._set_current_contact_avatar(contact.get_avatar_path()) + + def _set_current_contact_avatar(self, avatar_path): width = self._screen.account_avatar.width() pixmap = QtGui.QPixmap(avatar_path) - self._screen.avatar_label.setPixmap(pixmap.scaled(width, width, - QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation)) + self._screen.account_avatar.setPixmap(pixmap.scaled(width, width, + QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index af5b87e..1a8fe0f 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -91,7 +91,7 @@ class Profile(basecontact.BaseContact): ft = self._file_transfers[(friend_num, file_num)] if type(ft) is SendTransfer: self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, False, -1] - elif type(ft) is ReceiveTransfer and ft.state != TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + elif type(ft) is ReceiveTransfer and ft.state != FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, True, ft.total_size()] self.cancel_transfer(friend_num, file_num, True) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 4bbcc18..6673e17 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -3,10 +3,10 @@ from os.path import basename, getsize, exists, dirname from os import remove, rename, chdir from time import time, sleep from wrapper.tox import Tox -from PyQt5 import QtCore +from common.event import Event -TOX_FILE_TRANSFER_STATE = { +FILE_TRANSFER_STATE = { 'RUNNING': 0, 'PAUSED_BY_USER': 1, 'CANCELLED': 2, @@ -31,69 +31,58 @@ def is_inline(file_name): return file_name in allowed_inlines or file_name.startswith('qTox_Screenshot_') -# TODO: use events from common.event.py - -class StateSignal(QtCore.QObject): - - signal = QtCore.pyqtSignal(int, float, int) # state, progress, time in sec - - -class TransferFinishedSignal(QtCore.QObject): - - signal = QtCore.pyqtSignal(int, int) # friend number, file number - - -class FileTransfer(QtCore.QObject): +class FileTransfer: """ Superclass for file transfers """ def __init__(self, path, tox, friend_number, size, file_number=None): - QtCore.QObject.__init__(self) self._path = path self._tox = tox self._friend_number = friend_number - self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] + self.state = FILE_TRANSFER_STATE['RUNNING'] self._file_number = file_number self._creation_time = None self._size = float(size) self._done = 0 - self._state_changed = StateSignal() - self._finished = TransferFinishedSignal() + self._state_changed_event = Event() + self._finished_event = Event() self._file_id = None def set_tox(self, tox): self._tox = tox def set_state_changed_handler(self, handler): - self._state_changed.signal.connect(handler) + self._state_changed_event += handler def set_transfer_finished_handler(self, handler): - self._finished.signal.connect(handler) - - def signal(self): - percentage = self._done / self._size if self._size else 0 - if self._creation_time is None or not percentage: - t = -1 - else: - t = ((time() - self._creation_time) / percentage) * (1 - percentage) - self._state_changed.signal.emit(self.state, percentage, int(t)) - - def finished(self): - self._finished.signal.emit(self._friend_number, self._file_number) + self._finished_event += handler def get_file_number(self): return self._file_number + file_number = property(get_file_number) + def get_friend_number(self): return self._friend_number - def get_id(self): + friend_number = property(get_friend_number) + + def get_file_id(self): return self._file_id + file_id = property(get_file_id) + def get_path(self): return self._path + path = property(get_path) + + def get_size(self): + return self._size + + size = property(get_size) + def cancel(self): self.send_control(TOX_FILE_CONTROL['CANCEL']) if hasattr(self, '_file'): @@ -104,14 +93,14 @@ class FileTransfer(QtCore.QObject): if hasattr(self, '_file'): sleep(0.1) self._file.close() - self.state = TOX_FILE_TRANSFER_STATE['CANCELLED'] + self.state = FILE_TRANSFER_STATE['CANCELLED'] self.signal() def pause(self, by_friend): if not by_friend: self.send_control(TOX_FILE_CONTROL['PAUSE']) else: - self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] + self.state = FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] self.signal() def send_control(self, control): @@ -122,6 +111,17 @@ class FileTransfer(QtCore.QObject): def get_file_id(self): return self._tox.file_get_file_id(self._friend_number, self._file_number) + def signal(self): + percentage = self._done / self._size if self._size else 0 + if self._creation_time is None or not percentage: + t = -1 + else: + t = ((time() - self._creation_time) / percentage) * (1 - percentage) + self._state_changed_event(self.state, percentage, int(t)) + + def _finished(self): + self._finished_event(self._friend_number, self._file_number) + # ----------------------------------------------------------------------------------------------------------------- # Send file # ----------------------------------------------------------------------------------------------------------------- @@ -136,7 +136,7 @@ class SendTransfer(FileTransfer): else: size = 0 super().__init__(path, tox, friend_number, size) - self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] + self.state = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] self._file_number = tox.file_send(friend_number, kind, size, file_id, bytes(basename(path), 'utf-8') if path else b'') self._file_id = self.get_file_id() @@ -157,8 +157,8 @@ class SendTransfer(FileTransfer): else: if hasattr(self, '_file'): self._file.close() - self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self.finished() + self.state = FILE_TRANSFER_STATE['FINISHED'] + self._finished() self.signal() @@ -183,7 +183,7 @@ class SendFromBuffer(FileTransfer): def __init__(self, tox, friend_number, data, file_name): super().__init__(None, tox, friend_number, len(data)) - self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] + self.state = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] self._data = data self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], len(data), None, bytes(file_name, 'utf-8')) @@ -199,8 +199,8 @@ class SendFromBuffer(FileTransfer): self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) self._done += size else: - self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self.finished() + self.state = FILE_TRANSFER_STATE['FINISHED'] + self._finished() self.signal() @@ -250,8 +250,8 @@ class ReceiveTransfer(FileTransfer): self._creation_time = time() if data is None: self._file.close() - self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self.finished() + self.state = FILE_TRANSFER_STATE['FINISHED'] + self._finished() else: data = bytearray(data) if self._file_size < position: @@ -286,8 +286,8 @@ class ReceiveToBuffer(FileTransfer): if self._creation_time is None: self._creation_time = time() if data is None: - self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self.finished() + self.state = FILE_TRANSFER_STATE['FINISHED'] + self._finished() else: data = bytes(data) l = len(data) @@ -342,8 +342,8 @@ class ReceiveAvatar(ReceiveTransfer): chdir(dirname(avatar_path)) remove(avatar_path) rename(self._path, avatar_path) - self.finished(True) + self._finished(True) - def finished(self, emit=False): + def _finished(self, emit=False): if emit: - super().finished() + super()._finished() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 42a69fa..470299c 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -8,10 +8,11 @@ import utils.util as util class FileTransfersHandler: - def __init__(self, tox, settings, contact_provider): + def __init__(self, tox, settings, contact_provider, file_transfers_message_service): self._tox = tox self._settings = settings self._contact_provider = contact_provider + self._file_transfers_message_service = file_transfers_message_service self._file_transfers = {} # key = (friend number, file number), value - transfer instance self._paused_file_transfers = dict(settings['paused_file_transfers']) @@ -46,51 +47,17 @@ class FileTransfersHandler: return self._tox.file_seek(friend_number, file_number, pos) self.accept_transfer(None, data[0], friend_number, file_number, size, False, pos) - tm = TransferMessage(MESSAGE_AUTHOR['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - size, - file_name, - friend_number, - file_number) elif inline and size < 1024 * 1024: self.accept_transfer(None, '', friend_number, file_number, size, True) - tm = TransferMessage(MESSAGE_AUTHOR['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - size, - file_name, - friend_number, - file_number) elif auto: path = self._settings['auto_accept_path'] or util.curr_directory() self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size) - tm = TransferMessage(MESSAGE_AUTHOR['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - size, - file_name, - friend_number, - file_number) else: - tm = TransferMessage(MESSAGE_AUTHOR['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'], - size, - file_name, - friend_number, - file_number) accepted = False - if friend_number == self.get_active_number() and self.is_active_a_friend(): - item = self.create_file_transfer_item(tm) - if accepted: - self._file_transfers[(friend_number, file_number)].set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() - else: - friend.actions = True - friend.append_message(tm) + self._file_transfers_message_service.add_incoming_transfer_message( + friend, accepted, size, file_name,file_number) def cancel_transfer(self, friend_number, file_number, already_cancelled=False): """ @@ -100,7 +67,7 @@ class FileTransfersHandler: :param already_cancelled: was cancelled by friend """ i = self._get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['CANCELLED']) + FILE_TRANSFER_STATE['CANCELLED']) if (friend_number, file_number) in self._file_transfers: tr = self._file_transfers[(friend_number, file_number)] if not already_cancelled: @@ -117,12 +84,11 @@ class FileTransfersHandler: tmp = self._messages.count() + i if tmp >= 0: self._messages.itemWidget( - self._messages.item(tmp)).update_transfer_state(TOX_FILE_TRANSFER_STATE['CANCELLED'], + self._messages.item(tmp)).update_transfer_state(FILE_TRANSFER_STATE['CANCELLED'], 0, -1) def cancel_not_started_transfer(self, cancel_time): self.get_curr_friend().delete_one_unsent_file(cancel_time) - self.update() def pause_transfer(self, friend_number, file_number, by_friend=False): """ @@ -130,7 +96,7 @@ class FileTransfersHandler: """ tr = self._file_transfers[(friend_number, file_number)] tr.pause(by_friend) - t = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER'] + t = FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else FILE_TRANSFER_STATE['PAUSED_BY_USER'] self._get_friend_by_number(friend_number).update_transfer_data(file_number, t) def resume_transfer(self, friend_number, file_number, by_friend=False): @@ -141,7 +107,7 @@ class FileTransfersHandler: # TOX_FILE_TRANSFER_STATE['RUNNING']) tr = self._file_transfers[(friend_number, file_number)] if by_friend: - tr.state = TOX_FILE_TRANSFER_STATE['RUNNING'] + tr.state = FILE_TRANSFER_STATE['RUNNING'] tr.signal() else: tr.send_control(TOX_FILE_CONTROL['RESUME']) @@ -177,7 +143,7 @@ class FileTransfersHandler: if item is not None: rt.set_state_changed_handler(item.update_transfer_state) self._get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['RUNNING']) + FILE_TRANSFER_STATE['RUNNING']) def send_screenshot(self, data, friend_number): """ @@ -201,18 +167,9 @@ class FileTransfersHandler: raise RuntimeError() st = SendFromBuffer(self._tox, friend.number, data, file_name) st.set_transfer_finished_handler(self.transfer_finished) - self._file_transfers[(friend.number, st.get_file_number())] = st - tm = TransferMessage(MESSAGE_AUTHOR['ME'], - time.time(), - TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], - len(data), - file_name, - friend.number, - st.get_file_number()) - item = self.create_file_transfer_item(tm) - friend.append_message(tm) - st.set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() + file_number = st.get_file_number() + self._file_transfers[(friend.number, file_number)] = st + self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) def send_file(self, path, friend_number, is_resend=False, file_id=None): """ @@ -232,19 +189,10 @@ class FileTransfersHandler: raise RuntimeError() st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) st.set_transfer_finished_handler(self.transfer_finished) - self._file_transfers[(friend_number, st.get_file_number())] = st - # tm = TransferMessage(MESSAGE_AUTHOR['ME'], - # time.time(), - # TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], - # os.path.getsize(path), - # os.path.basename(path), - # friend_number, - # st.get_file_number()) - # if friend_number == self.get_active_number(): - # item = self.create_file_transfer_item(tm) - # st.set_state_changed_handler(item.update_transfer_state) - # self._messages.scrollToBottom() - # self._contacts[friend_number].append_message(tm) + file_number = st.get_file_number() + self._file_transfers[(friend_number, file_number)] = st + file_name = os.path.basename(path) + self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) def incoming_chunk(self, friend_number, file_number, position, data): """ @@ -266,21 +214,13 @@ class FileTransfersHandler: elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']): # inline image print('inline') inline = InlineImage(transfer.get_data()) - i = self._get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['FINISHED'], - inline) - if friend_number == self.get_active_number() and self.is_active_a_friend(): - count = self._messages.count() - if count + i + 1 >= 0: - elem = QtWidgets.QListWidgetItem() - item = InlineImageItem(transfer.get_data(), self._messages.width(), elem) - elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) - self._messages.insertItem(count + i + 1, elem) - self._messages.setItemWidget(elem, item) - self._messages.scrollToBottom() + index = self._get_friend_by_number(friend_number).update_transfer_data(file_number, + FILE_TRANSFER_STATE['FINISHED'], + inline) + self._file_transfers_message_service.add_inline_message(transfer, index) elif t is not SendAvatar: self._get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['FINISHED']) + FILE_TRANSFER_STATE['FINISHED']) del self._file_transfers[(friend_number, file_number)] del transfer @@ -288,7 +228,7 @@ class FileTransfersHandler: friend = self._get_friend_by_number(friend_number) friend.remove_invalid_unsent_files() files = friend.get_unsent_files() - try: + try: # TODO: fix for fl in files: data = fl.get_data() if data[1] is not None: @@ -327,7 +267,7 @@ class FileTransfersHandler: """ friend = self._get_friend_by_number(friend_number) ra = ReceiveAvatar(friend.get_contact_avatar_path(), self._tox, friend_number, size, file_number) - if ra.state != TOX_FILE_TRANSFER_STATE['CANCELLED']: + if ra.state != FILE_TRANSFER_STATE['CANCELLED']: self._file_transfers[(friend_number, file_number)] = ra ra.set_transfer_finished_handler(self.transfer_finished) else: diff --git a/toxygen/file_transfers/file_transfers_messages_service.py b/toxygen/file_transfers/file_transfers_messages_service.py new file mode 100644 index 0000000..963eeb6 --- /dev/null +++ b/toxygen/file_transfers/file_transfers_messages_service.py @@ -0,0 +1,58 @@ +from messenger.messenger import * +import utils.util as util +from file_transfers.file_transfers import * + + +class FileTransfersMessagesService: + + def __init__(self, contacts_manager, messages_items_factory, profile, main_screen): + self._contacts_manager = contacts_manager + self._messages_items_factory = messages_items_factory + self._profile = profile + self._messages = main_screen.messages + + def add_incoming_transfer_message(self, friend, accepted, size, file_name, file_number): + author = MessageAuthor(friend.name, MESSAGE_AUTHOR['FRIEND']) + status = FILE_TRANSFER_STATE['RUNNING'] if accepted else FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'] + tm = TransferMessage(author, util.get_unix_time(), status, size, file_name, friend.number, file_number) + + if self._is_active(friend.number): + self._create_file_transfer_item(tm) + self._messages.scrollToBottom() + else: + friend.actions = True + + friend.append_message(tm) + + def add_outgoing_transfer_message(self, friend, size, file_name, file_number): + author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['ME']) + status = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] + tm = TransferMessage(author, util.get_unix_time(), status, size, file_name, friend.number, file_number) + + if self._is_active(friend.number): + self._create_file_transfer_item(tm) + self._messages.scrollToBottom() + + friend.append_message(tm) + + def add_inline_message(self, transfer, index): + if self._is_active(transfer.friend_number): + count = self._messages.count() + if count + index + 1 >= 0: + self._create_inline_item(transfer.data, count + index + 1) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _is_active(self, friend_number): + if not self._contacts_manager.is_active_a_friend(): + return False + + return friend_number == self._contacts_manager.get_active_number() + + def _create_file_transfer_item(self, tm): + return self._messages_items_factory.create_file_transfer_item(tm) + + def _create_inline_item(self, data, position): + return self._messages_items_factory.create_inline_item(data, False, position) diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 42a0fd1..0dfc797 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -126,27 +126,43 @@ class TransferMessage(Message): Message with info about file transfer """ - def __init__(self, owner, time, status, size, name, friend_number, file_number): - super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time) - self._status = status + def __init__(self, author, time, state, size, file_name, friend_number, file_number): + super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], author, time) + self._state = state self._size = size - self._file_name = name + self._file_name = file_name self._friend_number, self._file_number = friend_number, file_number def is_active(self, file_number): - return self._file_number == file_number and self._status not in (2, 3) + return self._file_number == file_number and self._state not in (2, 3) def get_friend_number(self): return self._friend_number + friend_number = property(get_friend_number) + def get_file_number(self): return self._file_number - def get_status(self): - return self._status + file_number = property(get_file_number) - def set_status(self, value): - self._status = value + def get_state(self): + return self._state + + def set_state(self, value): + self._state = value + + state = property(get_state, set_state) + + def get_size(self): + return self._size + + size = property(get_size) + + def get_file_name(self): + return self._file_name + + file_name = property(get_file_name) class UnsentFile(Message): diff --git a/toxygen/ui/contact_items.py b/toxygen/ui/contact_items.py index 25b3c0a..2ad3ad2 100644 --- a/toxygen/ui/contact_items.py +++ b/toxygen/ui/contact_items.py @@ -1,7 +1,7 @@ from wrapper.toxcore_enums_and_consts import * from PyQt5 import QtCore, QtGui, QtWidgets from contacts import profile -from file_transfers.file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR +from file_transfers.file_transfers import FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR from utils.util import * from ui.widgets import DataLabel, create_menu from user_data import settings diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index dc9c002..3257279 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -18,16 +18,18 @@ class FriendItemsFactory: return item -# TODO: accept messages everywhere instead of params - class MessagesItemsFactory: def __init__(self, settings, plugin_loader, smiley_loader, main_screen, delete_action): + self._file_transfers_handler = None self._settings, self._plugin_loader = settings, plugin_loader self._smiley_loader, self._delete_action = smiley_loader, delete_action self._messages = main_screen.messages self._message_edit = main_screen.messageEdit + def set_file_transfers_handler(self, file_transfers_handler): + self._file_transfers_handler = file_transfers_handler + def create_message_item(self, message, append=True, pixmap=None): item = message.get_widget(self._settings, self._create_message_browser, self._delete_action, self._messages) @@ -43,24 +45,20 @@ class MessagesItemsFactory: return item - def create_inline_item(self, data, append): + def create_inline_item(self, data, append, position=0): elem = QtWidgets.QListWidgetItem() item = InlineImageItem(data, self._messages.width(), elem) elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) if append: self._messages.addItem(elem) else: - self._messages.insertItem(0, elem) + self._messages.insertItem(position, elem) self._messages.setItemWidget(elem, item) return item - def create_unsent_file_item(self, file_name, size, name, time, append): - item = UnsentFileItem(file_name, - size, - name, - time, - self._messages.width()) + def create_unsent_file_item(self, tm, append): + item = UnsentFileItem(self._file_transfers_handler, self._settings, tm, self._messages.width()) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) if append: @@ -71,9 +69,8 @@ class MessagesItemsFactory: return item - def create_file_transfer_item(self, data, append=True): - data.append(self._messages.width()) - item = FileTransferItem(*data) + def create_file_transfer_item(self, tm, append=True): + item = FileTransferItem(self._file_transfers_handler, self._settings, tm, self._messages.width()) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) if append: diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index dea7a5c..89e4ac3 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -6,7 +6,9 @@ import utils.util as util import ui.menu as menu import html as h import re +from ui.widgets import * from messenger.messages import MESSAGE_AUTHOR +from file_transfers.file_transfers import * class MessageBrowser(QtWidgets.QTextBrowser): @@ -216,60 +218,63 @@ class MessageItem(QtWidgets.QWidget): class FileTransferItem(QtWidgets.QListWidget): - def __init__(self, file_name, size, time, user, friend_number, file_number, state, width, parent=None): + def __init__(self, file_transfers_handler, settings, transfer_message, width, parent=None): QtWidgets.QListWidget.__init__(self, parent) + self._file_transfers_handler = file_transfers_handler self.resize(QtCore.QSize(width, 34)) - if state == TOX_FILE_TRANSFER_STATE['CANCELLED']: + if transfer_message.state == FILE_TRANSFER_STATE['CANCELLED']: self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') - elif state in PAUSED_FILE_TRANSFERS: + elif transfer_message.state in PAUSED_FILE_TRANSFERS: self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') else: self.setStyleSheet('QListWidget { border: 1px solid green; }') - self.state = state + self.state = transfer_message.state self.name = DataLabel(self) self.name.setGeometry(QtCore.QRect(3, 7, 95, 25)) self.name.setTextFormat(QtCore.Qt.PlainText) font = QtGui.QFont() - # FIXME - font.setFamily(settings.Settings.get_instance()['font']) + font.setFamily(settings['font']) font.setPointSize(11) font.setBold(True) self.name.setFont(font) - self.name.setText(user) + self.name.setText(transfer_message.author.name) self.time = QtWidgets.QLabel(self) self.time.setGeometry(QtCore.QRect(width - 60, 7, 50, 25)) font.setPointSize(10) font.setBold(False) self.time.setFont(font) - self.time.setText(convert_time(time)) + self.time.setText(util.convert_time(time)) self.cancel = QtWidgets.QPushButton(self) self.cancel.setGeometry(QtCore.QRect(width - 125, 2, 30, 30)) - pixmap = QtGui.QPixmap(curr_directory() + '/images/decline.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'decline.png')) icon = QtGui.QIcon(pixmap) self.cancel.setIcon(icon) self.cancel.setIconSize(QtCore.QSize(30, 30)) - self.cancel.setVisible(state in ACTIVE_FILE_TRANSFERS) - self.cancel.clicked.connect(lambda: self.cancel_transfer(friend_number, file_number)) + self.cancel.setVisible(transfer_message.state in ACTIVE_FILE_TRANSFERS) + self.cancel.clicked.connect( + lambda: self.cancel_transfer(transfer_message.friend_number, transfer_message.file_number)) self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}') self.accept_or_pause = QtWidgets.QPushButton(self) self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30)) - if state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + if transfer_message.state == FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: self.accept_or_pause.setVisible(True) self.button_update('accept') - elif state in DO_NOT_SHOW_ACCEPT_BUTTON: + elif transfer_message.state in DO_NOT_SHOW_ACCEPT_BUTTON: self.accept_or_pause.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # setup for continue + elif transfer_message.state == FILE_TRANSFER_STATE['PAUSED_BY_USER']: # setup for continue self.accept_or_pause.setVisible(True) self.button_update('resume') else: # pause self.accept_or_pause.setVisible(True) self.button_update('pause') - self.accept_or_pause.clicked.connect(lambda: self.accept_or_pause_transfer(friend_number, file_number, size)) + self.accept_or_pause.clicked.connect( + lambda: self.accept_or_pause_transfer(transfer_message.friend_number, transfer_message.file_number, + transfer_message.size)) self.accept_or_pause.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none}') @@ -277,64 +282,60 @@ class FileTransferItem(QtWidgets.QListWidget): self.pb.setGeometry(QtCore.QRect(100, 7, 100, 20)) self.pb.setValue(0) self.pb.setStyleSheet('QProgressBar { background-color: #302F2F; }') - self.pb.setVisible(state in SHOW_PROGRESS_BAR) + self.pb.setVisible(transfer_message.state in SHOW_PROGRESS_BAR) self.file_name = DataLabel(self) self.file_name.setGeometry(QtCore.QRect(210, 7, width - 420, 20)) font.setPointSize(12) self.file_name.setFont(font) - file_size = size // 1024 + file_size = transfer_message.size // 1024 if not file_size: - file_size = '{}B'.format(size) + file_size = '{}B'.format(transfer_message.size) elif file_size >= 1024: file_size = '{}MB'.format(file_size // 1024) else: file_size = '{}KB'.format(file_size) - file_data = '{} {}'.format(file_size, file_name) + file_data = '{} {}'.format(file_size, transfer_message.file_name) self.file_name.setText(file_data) - self.file_name.setToolTip(file_name) - self.saved_name = file_name + self.file_name.setToolTip(transfer_message.file_name) + self.saved_name = transfer_message.file_name self.time_left = QtWidgets.QLabel(self) self.time_left.setGeometry(QtCore.QRect(width - 92, 7, 30, 20)) font.setPointSize(10) self.time_left.setFont(font) - self.time_left.setVisible(state == TOX_FILE_TRANSFER_STATE['RUNNING']) + self.time_left.setVisible(transfer_message.state == FILE_TRANSFER_STATE['RUNNING']) self.setFocusPolicy(QtCore.Qt.NoFocus) self.paused = False def cancel_transfer(self, friend_number, file_number): - pr = profile.Profile.get_instance() - pr.cancel_transfer(friend_number, file_number) + self._file_transfers_handler.cancel_transfer(friend_number, file_number) self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') self.cancel.setVisible(False) self.accept_or_pause.setVisible(False) self.pb.setVisible(False) def accept_or_pause_transfer(self, friend_number, file_number, size): - if self.state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", 'Choose folder'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) + if self.state == FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + directory = util_ui.directory_dialog(util_ui.tr('Choose folder')) self.pb.setVisible(True) if directory: - pr = profile.Profile.get_instance() - pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size) + self._file_transfer_handler.accept_transfer(self, directory + '/' + self.saved_name, + friend_number, file_number, size) self.button_update('pause') - elif self.state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # resume + elif self.state == FILE_TRANSFER_STATE['PAUSED_BY_USER']: # resume self.paused = False - profile.Profile.get_instance().resume_transfer(friend_number, file_number) + self._file_transfer_handler.resume_transfer(friend_number, file_number) self.button_update('pause') - self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] + self.state = FILE_TRANSFER_STATE['RUNNING'] else: # pause self.paused = True - self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER'] - profile.Profile.get_instance().pause_transfer(friend_number, file_number) + self.state = FILE_TRANSFER_STATE['PAUSED_BY_USER'] + self._file_transfer_handler.pause_transfer(friend_number, file_number) self.button_update('resume') self.accept_or_pause.clearFocus() def button_update(self, path): - pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(path)) + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), '{}.png'.format(path))) icon = QtGui.QIcon(pixmap) self.accept_or_pause.setIcon(icon) self.accept_or_pause.setIconSize(QtCore.QSize(30, 30)) @@ -345,31 +346,31 @@ class FileTransferItem(QtWidgets.QListWidget): m, s = divmod(time, 60) self.time_left.setText('{0:02d}:{1:02d}'.format(m, s)) if self.state != state and self.state in ACTIVE_FILE_TRANSFERS: - if state == TOX_FILE_TRANSFER_STATE['CANCELLED']: + if state == FILE_TRANSFER_STATE['CANCELLED']: self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') self.cancel.setVisible(False) self.accept_or_pause.setVisible(False) self.pb.setVisible(False) self.state = state self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['FINISHED']: + elif state == FILE_TRANSFER_STATE['FINISHED']: self.accept_or_pause.setVisible(False) self.pb.setVisible(False) self.cancel.setVisible(False) self.setStyleSheet('QListWidget { border: 1px solid green; }') self.state = state self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']: + elif state == FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']: self.accept_or_pause.setVisible(False) self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') self.state = state self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: + elif state == FILE_TRANSFER_STATE['PAUSED_BY_USER']: self.button_update('resume') # setup button continue self.setStyleSheet('QListWidget { border: 1px solid green; }') self.state = state self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']: + elif state == FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']: self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') self.accept_or_pause.setVisible(False) self.time_left.setVisible(False) @@ -382,24 +383,23 @@ class FileTransferItem(QtWidgets.QListWidget): self.state = state self.time_left.setVisible(True) - def mark_as_sent(self): + @staticmethod + def mark_as_sent(): return False class UnsentFileItem(FileTransferItem): - def __init__(self, file_name, size, user, time, width, parent=None): - super(UnsentFileItem, self).__init__(file_name, size, time, user, -1, -1, - TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'], width, parent) + def __init__(self, file_transfers_handler, settings, transfer_message, width, parent=None): + super().__init__(file_transfers_handler, settings, transfer_message, width, parent) self._time = time self.pb.setVisible(False) - movie = QtGui.QMovie(join_path(get_images_directory(), 'spinner.gif')) + movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) self.time.setMovie(movie) movie.start() def cancel_transfer(self, *args): - pr = profile.Profile.get_instance() - pr.cancel_not_started_transfer(self._time) + self._file_transfers_handler.cancel_not_started_transfer(self._time) class InlineImageItem(QtWidgets.QScrollArea): @@ -443,15 +443,11 @@ class InlineImageItem(QtWidgets.QScrollArea): self._full_size = not self._full_size self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) elif event.button() == QtCore.Qt.RightButton: # save inline - # TODO: dialog - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", - 'Choose folder'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) + directory = util_ui.directory_dialog(util_ui.tr('Choose folder')) if directory: - fl = QtCore.QFile(directory + '/toxygen_inline_' + curr_time().replace(':', '_') + '.png') + fl = QtCore.QFile(directory + '/toxygen_inline_' + util.curr_time().replace(':', '_') + '.png') self._pixmap.save(fl, 'PNG') - def mark_as_sent(self): + @staticmethod + def mark_as_sent(): return False From f3aa0aeda354969da9ce4bffde406a3559f22934 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 May 2018 19:31:08 +0300 Subject: [PATCH 030/217] file transfers fixes - part 2 --- toxygen/contacts/contact.py | 14 +++++++------- toxygen/messenger/messages.py | 6 ++++++ toxygen/ui/items_factories.py | 2 +- toxygen/ui/messages_widgets.py | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 8c1fb06..c5c0dac 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -75,7 +75,7 @@ class Contact(basecontact.BaseContact): Get data to save in db :return: list of unsaved messages or [] """ - messages = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']), self._corr)) + messages = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) return messages[-self._unsaved_messages:] if self._unsaved_messages else [] def get_corr(self): @@ -86,11 +86,11 @@ class Contact(basecontact.BaseContact): :param message: text or file transfer message """ self._corr.append(message) - if message.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']): + if message.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): self._unsaved_messages += 1 def get_last_message_text(self): - messages = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']) + messages = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) and x.get_owner() != MESSAGE_AUTHOR['FRIEND'], self._corr)) if messages: return messages[-1].text @@ -123,7 +123,7 @@ class Contact(basecontact.BaseContact): """ :return list of unsent messages for saving """ - messages = filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']) + messages = filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(map(lambda x: x.get_data(), messages)) @@ -141,7 +141,7 @@ class Contact(basecontact.BaseContact): def delete_message(self, message_id): elem = list(filter(lambda x: type(x) in (TextMessage, GroupChatMessage) and x.message_id == message_id, self._corr))[0] - tmp = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']), self._corr)) + tmp = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) if elem in tmp[-self._unsaved_messages:] and self._unsaved_messages: self._unsaved_messages -= 1 self._corr.remove(elem) @@ -159,7 +159,7 @@ class Contact(basecontact.BaseContact): old = filter(save_message, self._corr[:-SAVE_MESSAGES]) self._corr = list(old) + self._corr[-SAVE_MESSAGES:] - text_messages = filter(lambda x: x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']), self._corr) + text_messages = filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr) self._unsaved_messages = min(self._unsaved_messages, len(list(text_messages))) self._search_index = 0 @@ -177,7 +177,7 @@ class Contact(basecontact.BaseContact): self._unsaved_messages = 0 else: self._corr = list(filter(lambda x: (x.get_type() == 2 and x.get_status() in ft.ACTIVE_FILE_TRANSFERS) - or (x.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']) + or (x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT']), self._corr)) self._unsaved_messages = len(self.get_unsent_messages()) diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 0dfc797..34aff38 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -164,6 +164,9 @@ class TransferMessage(Message): file_name = property(get_file_name) + def _create_widget(self, *args): + return FileTransferItem(self, *args) + class UnsentFile(Message): @@ -189,6 +192,9 @@ class InlineImage(Message): data = property(get_data) + def _create_widget(self, *args): + return InlineImageItem(self, *args) + class InfoMessage(TextMessage): diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index 3257279..674291c 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -70,7 +70,7 @@ class MessagesItemsFactory: return item def create_file_transfer_item(self, tm, append=True): - item = FileTransferItem(self._file_transfers_handler, self._settings, tm, self._messages.width()) + item = tm.get_widget(self._file_transfers_handler, self._settings, tm, self._messages.width()) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) if append: diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index 89e4ac3..c5b2120 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -246,7 +246,7 @@ class FileTransferItem(QtWidgets.QListWidget): font.setPointSize(10) font.setBold(False) self.time.setFont(font) - self.time.setText(util.convert_time(time)) + self.time.setText(util.convert_time(transfer_message.time)) self.cancel = QtWidgets.QPushButton(self) self.cancel.setGeometry(QtCore.QRect(width - 125, 2, 30, 30)) From c0a143c81719879db69b990cf249e33cb109f472 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 May 2018 20:25:21 +0300 Subject: [PATCH 031/217] identicons basic support --- setup.py | 6 +++++- toxygen/contacts/basecontact.py | 16 ++++++++++------ toxygen/contacts/common.py | 15 +++++++++++++++ toxygen/contacts/contacts_manager.py | 1 + toxygen/contacts/profile.py | 4 ++-- toxygen/file_transfers/file_transfers.py | 3 --- toxygen/file_transfers/file_transfers_handler.py | 2 +- toxygen/ui/menu.py | 2 +- toxygen/user_data/settings.py | 1 + 9 files changed, 36 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index f586ec3..8457ef5 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ version = main.__version__ + '.0' if system() == 'Windows': - MODULES = ['PyQt5', 'PyAudio', 'numpy', 'opencv-python'] + MODULES = ['PyQt5', 'PyAudio', 'numpy', 'opencv-python', 'pydenticon'] else: MODULES = [] try: @@ -29,6 +29,10 @@ else: import cv2 except ImportError: MODULES.append('opencv-python') + try: + import pydenticon + except ImportError: + MODULES.append('pydenticon') class InstallScript(install): diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 037289c..988b0bc 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -3,6 +3,7 @@ from PyQt5 import QtCore, QtGui from wrapper.toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE import utils.util as util import common.event as event +import contacts.common as common class BaseContact: @@ -119,14 +120,17 @@ class BaseContact: self._widget.avatar_label.repaint() self._avatar_changed_event(avatar_path) - def reset_avatar(self): + def reset_avatar(self, generate_new): avatar_path = self.get_avatar_path() - if os.path.isfile(avatar_path): + if os.path.isfile(avatar_path) and not avatar_path == self._get_default_avatar_path(): os.remove(avatar_path) + if generate_new: + self.set_avatar(common.generate_avatar(self.tox_id)) + else: self.load_avatar() def set_avatar(self, avatar): - avatar_path = self.get_avatar_path() + avatar_path = self.get_contact_avatar_path() with open(avatar_path, 'wb') as f: f.write(avatar) self.load_avatar() @@ -137,7 +141,7 @@ class BaseContact: def get_avatar_path(self): avatar_path = self.get_contact_avatar_path() if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image - avatar_path = util.join_path(util.get_images_directory(), self._get_default_avatar_name()) + avatar_path = self._get_default_avatar_path() return avatar_path @@ -166,5 +170,5 @@ class BaseContact: # ----------------------------------------------------------------------------------------------------------------- @staticmethod - def _get_default_avatar_name(): - return 'avatar.png' + def _get_default_avatar_path(): + return util.join_path(util.get_images_directory(), 'avatar.png') diff --git a/toxygen/contacts/common.py b/toxygen/contacts/common.py index 584afc3..b37c6a2 100644 --- a/toxygen/contacts/common.py +++ b/toxygen/contacts/common.py @@ -1,3 +1,4 @@ +from pydenticon import Generator class BaseTypingNotificationHandler: @@ -22,3 +23,17 @@ class FriendTypingNotificationHandler(BaseTypingNotificationHandler): BaseTypingNotificationHandler.DEFAULT_HANDLER = BaseTypingNotificationHandler() + + +def generate_avatar(tox_id): + foreground = ["rgb(45,79,255)", + "rgb(254,180,44)", + "rgb(226,121,234)", + "rgb(30,179,253)", + "rgb(232,77,65)", + "rgb(49,203,115)", + "rgb(141,69,170)"] + generator = Generator(5, 5, foreground=foreground, background="rgba(42,42,42,0)") + identicon = generator.generate(tox_id, 220, 220, padding=(10, 10, 10, 10)) + + return identicon diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 7570c06..b4c1803 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -295,6 +295,7 @@ class ContactsManager: self._history.add_friend_to_db(tox_id) friend = self._contact_provider.get_friend_by_public_key(tox_id) self._contacts.append(friend) + friend.reset_avatar() def block_user(self, tox_id): """ diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 1a8fe0f..b4dbaf2 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -136,8 +136,8 @@ class Profile(basecontact.BaseContact): self._call.stop() del self._call - def reset_avatar(self): - super().reset_avatar() + def reset_avatar(self, generate_new): + super().reset_avatar(generate_new) for friend in filter(lambda x: x.status is not None, self._contacts): self.send_avatar(friend.number) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 6673e17..91aeb86 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -316,9 +316,6 @@ class ReceiveAvatar(ReceiveTransfer): elif not size: self.send_control(TOX_FILE_CONTROL['CANCEL']) self._file.close() - if exists(path): - remove(path) - self._file.close() remove(full_path) elif exists(path): hash = self.get_file_id() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 470299c..6269295 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -271,7 +271,7 @@ class FileTransfersHandler: self._file_transfers[(friend_number, file_number)] = ra ra.set_transfer_finished_handler(self.transfer_finished) else: - friend.load_avatar() + friend.reset_avatar(self._settings['identicons']) # ----------------------------------------------------------------------------------------------------------------- # Private methods diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index d023cff..1a33bde 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -245,7 +245,7 @@ class ProfileSettings(CenteredWidget): self.tox_id.setText(self._profile.new_nospam()) def reset_avatar(self): - self._profile.reset_avatar() + self._profile.reset_avatar(self._settings['identicons']) def set_avatar(self): choose = util_ui.tr("Choose avatar") diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 8289663..1d7329e 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -140,6 +140,7 @@ class Settings(dict): 'unread_color': 'red', 'save_unsent_only': False, 'compact_mode': False, + 'identicons': True, 'show_welcome_screen': True, 'close_to_tray': False, 'font': 'Times New Roman', From a96f6d292878167c888b19ae51f5e1ce29e744dd Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 May 2018 00:02:22 +0300 Subject: [PATCH 032/217] file transfers fixes - part 3 --- toxygen/app.py | 2 +- toxygen/common/event.py | 4 + toxygen/contacts/contact.py | 3 + toxygen/contacts/contacts_manager.py | 13 +-- toxygen/contacts/friend.py | 3 +- toxygen/file_transfers/file_transfers.py | 9 +- .../file_transfers/file_transfers_handler.py | 83 ++++++++----------- .../file_transfers_messages_service.py | 4 + toxygen/messenger/messages.py | 5 ++ toxygen/ui/items_factories.py | 6 +- toxygen/ui/main_screen.py | 7 +- toxygen/ui/messages_widgets.py | 14 ++-- 12 files changed, 79 insertions(+), 74 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index ce15093..40fb7c6 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -323,7 +323,7 @@ class App: self._toxes, self._version) self._tray = tray.init_tray(profile, self._settings, self._ms) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, - self._plugin_loader) + self._plugin_loader, self._file_transfer_handler) self._tray.show() self._ms.show() diff --git a/toxygen/common/event.py b/toxygen/common/event.py index e3ecbf9..687a34d 100644 --- a/toxygen/common/event.py +++ b/toxygen/common/event.py @@ -8,9 +8,13 @@ class Event: def __iadd__(self, callback): self.add_callback(callback) + return self + def __isub__(self, callback): self.remove_callback(callback) + return self + def __call__(self, *args, **kwargs): for callback in self._callbacks: callback(*args, **kwargs) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index c5c0dac..71248db 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -101,6 +101,9 @@ class Contact(basecontact.BaseContact): for message in self._corr: message.remove_widget() + def get_message(self, _filter): + return list(filter(lambda m: _filter(m), self._corr))[0] + @staticmethod def _get_text_message(params): (message, author_type, author_name, unix_time, message_type, unique_id) = params diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index b4c1803..b826a37 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,8 +1,4 @@ -import utils.util as util -import utils.ui as util_ui from contacts.friend import Friend -from PyQt5 import QtCore, QtGui -from wrapper.toxcore_enums_and_consts import * from messenger.messages import * @@ -71,10 +67,10 @@ class ContactsManager: self._screen.messageEdit.clear() return try: - # self.send_typing(False) # TODO: fix self._screen.typing.setVisible(False) current_contact = self.get_curr_contact() if current_contact is not None: + current_contact.typing_notification_handler.send(self._tox, False) current_contact.remove_messages_widgets() # TODO: if required self._unsubscribe_from_events(current_contact) @@ -96,7 +92,12 @@ class ContactsManager: contact.load_corr() corr = contact.get_corr()[-PAGE_SIZE:] for message in corr: - self._messages_items_factory.create_message_item(message) + if message.type == MESSAGE_TYPE['FILE_TRANSFER']: + self._messages_items_factory.create_file_transfer_item(message) + elif message.type == MESSAGE_TYPE['INLINE']: + self._messages_items_factory.create_inline_item(message.data) + else: + self._messages_items_factory.create_message_item(message) # if value in self._call: # self._screen.active_call() # elif value in self._incoming_calls: diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 9602eb1..4b189a9 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -18,14 +18,13 @@ class Friend(contact.Contact): # File transfers support # ----------------------------------------------------------------------------------------------------------------- - def update_transfer_data(self, file_number, status, inline=None): + def update_transfer_data(self, file_number, inline): # TODO: rewrite """ Update status of active transfer and load inline if needed """ try: tr = list(filter(lambda x: x.get_type() == MESSAGE_TYPE['FILE_TRANSFER'] and x.is_active(file_number), self._corr))[0] - tr.set_status(status) i = self._corr.index(tr) if inline: # inline was loaded self._corr.insert(i, inline) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 91aeb86..92d92a6 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -1,7 +1,7 @@ from wrapper.toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL from os.path import basename, getsize, exists, dirname from os import remove, rename, chdir -from time import time, sleep +from time import time from wrapper.tox import Tox from common.event import Event @@ -47,7 +47,7 @@ class FileTransfer: self._done = 0 self._state_changed_event = Event() self._finished_event = Event() - self._file_id = None + self._file_id = self._file = None def set_tox(self, tox): self._tox = tox @@ -85,13 +85,12 @@ class FileTransfer: def cancel(self): self.send_control(TOX_FILE_CONTROL['CANCEL']) - if hasattr(self, '_file'): + if self._file is not None: self._file.close() self.signal() def cancelled(self): - if hasattr(self, '_file'): - sleep(0.1) + if self._file is not None: self._file.close() self.state = FILE_TRANSFER_STATE['CANCELLED'] self.signal() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 6269295..3d51767 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -1,8 +1,5 @@ -from file_transfers.file_transfers import * from messenger.messages import * -from history.database import MESSAGE_AUTHOR from ui.contact_items import * -from PyQt5 import QtWidgets import utils.util as util @@ -46,18 +43,18 @@ class FileTransfersHandler: self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) return self._tox.file_seek(friend_number, file_number, pos) - self.accept_transfer(None, data[0], friend_number, file_number, size, False, pos) + self.accept_transfer(data[0], friend_number, file_number, size, False, pos) elif inline and size < 1024 * 1024: - self.accept_transfer(None, '', friend_number, file_number, size, True) + self.accept_transfer('', friend_number, file_number, size, True) elif auto: path = self._settings['auto_accept_path'] or util.curr_directory() - self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size) + self.accept_transfer(path + '/' + file_name, friend_number, file_number, size) else: accepted = False self._file_transfers_message_service.add_incoming_transfer_message( - friend, accepted, size, file_name,file_number) + friend, accepted, size, file_name, file_number) def cancel_transfer(self, friend_number, file_number, already_cancelled=False): """ @@ -66,8 +63,6 @@ class FileTransfersHandler: :param file_number: file number :param already_cancelled: was cancelled by friend """ - i = self._get_friend_by_number(friend_number).update_transfer_data(file_number, - FILE_TRANSFER_STATE['CANCELLED']) if (friend_number, file_number) in self._file_transfers: tr = self._file_transfers[(friend_number, file_number)] if not already_cancelled: @@ -77,15 +72,8 @@ class FileTransfersHandler: if (friend_number, file_number) in self._file_transfers: del tr del self._file_transfers[(friend_number, file_number)] - else: - if not already_cancelled: - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) - if friend_number == self.get_active_number() and self.is_active_a_friend(): - tmp = self._messages.count() + i - if tmp >= 0: - self._messages.itemWidget( - self._messages.item(tmp)).update_transfer_state(FILE_TRANSFER_STATE['CANCELLED'], - 0, -1) + elif not already_cancelled: + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) def cancel_not_started_transfer(self, cancel_time): self.get_curr_friend().delete_one_unsent_file(cancel_time) @@ -96,15 +84,11 @@ class FileTransfersHandler: """ tr = self._file_transfers[(friend_number, file_number)] tr.pause(by_friend) - t = FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else FILE_TRANSFER_STATE['PAUSED_BY_USER'] - self._get_friend_by_number(friend_number).update_transfer_data(file_number, t) def resume_transfer(self, friend_number, file_number, by_friend=False): """ Resume transfer with specified data """ - # self.get_friend_by_number(friend_number).update_transfer_data(file_number, - # TOX_FILE_TRANSFER_STATE['RUNNING']) tr = self._file_transfers[(friend_number, file_number)] if by_friend: tr.state = FILE_TRANSFER_STATE['RUNNING'] @@ -112,9 +96,8 @@ class FileTransfersHandler: else: tr.send_control(TOX_FILE_CONTROL['RESUME']) - def accept_transfer(self, item, path, friend_number, file_number, size, inline=False, from_position=0): + def accept_transfer(self, path, friend_number, file_number, size, inline=False, from_position=0): """ - :param item: transfer item. :param path: path for saving :param friend_number: friend number :param file_number: file number @@ -122,32 +105,23 @@ class FileTransfersHandler: :param inline: is inline image :param from_position: position for start """ - path, file_name = os.path.split(path) - new_file_name, i = file_name, 1 - if not from_position: - while os.path.isfile(path + '/' + new_file_name): # file with same name already exists - if '.' in file_name: # has extension - d = file_name.rindex('.') - else: # no extension - d = len(file_name) - new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] - i += 1 - path = os.path.join(path, new_file_name) + path = self._generate_valid_path(path, from_position) + friend = self._get_friend_by_number(friend_number) if not inline: rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position) else: rt = ReceiveToBuffer(self._tox, friend_number, size, file_number) rt.set_transfer_finished_handler(self.transfer_finished) + message = friend.get_message(lambda m: m.type == MESSAGE_TYPE['FILE_TRANSFER'] + and m.state == FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'] + and m.file_number == file_number) + rt.set_state_changed_handler(message.transfer_updated) self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) - if item is not None: - rt.set_state_changed_handler(item.update_transfer_state) - self._get_friend_by_number(friend_number).update_transfer_data(file_number, - FILE_TRANSFER_STATE['RUNNING']) def send_screenshot(self, data, friend_number): """ - Send screenshot to current active friend + Send screenshot :param data: raw data - png """ self.send_inline(data, 'toxygen_inline.png', friend_number) @@ -169,7 +143,8 @@ class FileTransfersHandler: st.set_transfer_finished_handler(self.transfer_finished) file_number = st.get_file_number() self._file_transfers[(friend.number, file_number)] = st - self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + st.set_state_changed_handler(tm.transfer_updated) def send_file(self, path, friend_number, is_resend=False, file_id=None): """ @@ -192,7 +167,8 @@ class FileTransfersHandler: file_number = st.get_file_number() self._file_transfers[(friend_number, file_number)] = st file_name = os.path.basename(path) - self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + st.set_state_changed_handler(tm.transfer_updated) def incoming_chunk(self, friend_number, file_number, position, data): """ @@ -214,13 +190,8 @@ class FileTransfersHandler: elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']): # inline image print('inline') inline = InlineImage(transfer.get_data()) - index = self._get_friend_by_number(friend_number).update_transfer_data(file_number, - FILE_TRANSFER_STATE['FINISHED'], - inline) + index = self._get_friend_by_number(friend_number).update_transfer_data(file_number, inline) self._file_transfers_message_service.add_inline_message(transfer, index) - elif t is not SendAvatar: - self._get_friend_by_number(friend_number).update_transfer_data(file_number, - FILE_TRANSFER_STATE['FINISHED']) del self._file_transfers[(friend_number, file_number)] del transfer @@ -279,3 +250,19 @@ class FileTransfersHandler: def _get_friend_by_number(self, friend_number): return self._contact_provider.get_friend_by_number(friend_number) + + @staticmethod + def _generate_valid_path(path, from_position): + path, file_name = os.path.split(path) + new_file_name, i = file_name, 1 + if not from_position: + while os.path.isfile(path + '/' + new_file_name): # file with same name already exists + if '.' in file_name: # has extension + d = file_name.rindex('.') + else: # no extension + d = len(file_name) + new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] + i += 1 + path = os.path.join(path, new_file_name) + + return path diff --git a/toxygen/file_transfers/file_transfers_messages_service.py b/toxygen/file_transfers/file_transfers_messages_service.py index 963eeb6..06b1e14 100644 --- a/toxygen/file_transfers/file_transfers_messages_service.py +++ b/toxygen/file_transfers/file_transfers_messages_service.py @@ -24,6 +24,8 @@ class FileTransfersMessagesService: friend.append_message(tm) + return tm + def add_outgoing_transfer_message(self, friend, size, file_name, file_number): author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['ME']) status = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] @@ -35,6 +37,8 @@ class FileTransfersMessagesService: friend.append_message(tm) + return tm + def add_inline_message(self, transfer, index): if self._is_active(transfer.friend_number): count = self._messages.count() diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 34aff38..b14dfd5 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -164,6 +164,11 @@ class TransferMessage(Message): file_name = property(get_file_name) + def transfer_updated(self, state, percentage, time): + self._state = state + if self._widget is not None: + self._widget.update_transfer_state(state, percentage, time) + def _create_widget(self, *args): return FileTransferItem(self, *args) diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index 674291c..a9b1c1a 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -45,7 +45,7 @@ class MessagesItemsFactory: return item - def create_inline_item(self, data, append, position=0): + def create_inline_item(self, data, append=True, position=0): elem = QtWidgets.QListWidgetItem() item = InlineImageItem(data, self._messages.width(), elem) elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) @@ -57,7 +57,7 @@ class MessagesItemsFactory: return item - def create_unsent_file_item(self, tm, append): + def create_unsent_file_item(self, tm, append=True): item = UnsentFileItem(self._file_transfers_handler, self._settings, tm, self._messages.width()) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) @@ -70,7 +70,7 @@ class MessagesItemsFactory: return item def create_file_transfer_item(self, tm, append=True): - item = tm.get_widget(self._file_transfers_handler, self._settings, tm, self._messages.width()) + item = tm.get_widget(self._file_transfers_handler, self._settings, self._messages.width()) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) if append: diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 3d9fd0f..c267a12 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -19,14 +19,17 @@ class MainWindow(QtWidgets.QMainWindow): self.setAcceptDrops(True) self._saved = False self._profile = None + self._file_transfer_handler = None self.initUI() - def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader): + def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader, + file_transfer_handler): self._widget_factory = widget_factory self._tray = tray self._contacts_manager = contacts_manager self._profile = profile self._plugins_loader = plugins_loader + self._file_transfer_handler = file_transfer_handler self.messageEdit.set_messenger(messenger) def show(self): @@ -519,7 +522,7 @@ class MainWindow(QtWidgets.QMainWindow): caption = util_ui.tr('Choose file') name = util_ui.file_dialog(caption) if name[0]: - self._contacts_manager.send_file(name[0], self._contacts_manager.get_contact().number) + self._file_transfer_handler.send_file(name[0], self._contacts_manager.get_active_number()) def send_screenshot(self, hide=False): self.menu.hide() diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index c5b2120..5d86b64 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -218,10 +218,10 @@ class MessageItem(QtWidgets.QWidget): class FileTransferItem(QtWidgets.QListWidget): - def __init__(self, file_transfers_handler, settings, transfer_message, width, parent=None): + def __init__(self, transfer_message, file_transfer_handler, settings, width, parent=None): QtWidgets.QListWidget.__init__(self, parent) - self._file_transfers_handler = file_transfers_handler + self._file_transfer_handler = file_transfer_handler self.resize(QtCore.QSize(width, 34)) if transfer_message.state == FILE_TRANSFER_STATE['CANCELLED']: self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') @@ -308,7 +308,7 @@ class FileTransferItem(QtWidgets.QListWidget): self.paused = False def cancel_transfer(self, friend_number, file_number): - self._file_transfers_handler.cancel_transfer(friend_number, file_number) + self._file_transfer_handler.cancel_transfer(friend_number, file_number) self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') self.cancel.setVisible(False) self.accept_or_pause.setVisible(False) @@ -319,7 +319,7 @@ class FileTransferItem(QtWidgets.QListWidget): directory = util_ui.directory_dialog(util_ui.tr('Choose folder')) self.pb.setVisible(True) if directory: - self._file_transfer_handler.accept_transfer(self, directory + '/' + self.saved_name, + self._file_transfer_handler.accept_transfer(directory + '/' + self.saved_name, friend_number, file_number, size) self.button_update('pause') elif self.state == FILE_TRANSFER_STATE['PAUSED_BY_USER']: # resume @@ -390,8 +390,8 @@ class FileTransferItem(QtWidgets.QListWidget): class UnsentFileItem(FileTransferItem): - def __init__(self, file_transfers_handler, settings, transfer_message, width, parent=None): - super().__init__(file_transfers_handler, settings, transfer_message, width, parent) + def __init__(self, file_transfer_handler, settings, transfer_message, width, parent=None): + super().__init__(file_transfer_handler, settings, transfer_message, width, parent) self._time = time self.pb.setVisible(False) movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) @@ -399,7 +399,7 @@ class UnsentFileItem(FileTransferItem): movie.start() def cancel_transfer(self, *args): - self._file_transfers_handler.cancel_not_started_transfer(self._time) + self._file_transfer_handler.cancel_not_started_transfer(self._time) class InlineImageItem(QtWidgets.QScrollArea): From 9294c3e77913fd78192a20a438da6c210c0f3aad Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 May 2018 15:20:47 +0300 Subject: [PATCH 033/217] file transfers fixes - part 4 --- toxygen/app.py | 4 +- toxygen/contacts/contact.py | 7 ++- toxygen/contacts/profile.py | 17 ++----- toxygen/file_transfers/file_transfers.py | 34 +++++++------- .../file_transfers/file_transfers_handler.py | 46 ++++++++++++------- toxygen/messenger/messages.py | 2 +- toxygen/middleware/callbacks.py | 7 ++- 7 files changed, 63 insertions(+), 54 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 40fb7c6..03509a6 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -245,7 +245,7 @@ class App: self._save_profile() except Exception as ex: print(ex) - log('Profile creation exception: ' + str(ex)) + util.log('Profile creation exception: ' + str(ex)) text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') util_ui.message_box(text, util_ui.tr('Error')) return @@ -316,7 +316,7 @@ class App: file_transfers_message_service = FileTransfersMessagesService(self._contacts_manager, messages_items_factory, profile, self._ms) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider, - file_transfers_message_service) + file_transfers_message_service, profile) messages_items_factory.set_file_transfers_handler(self._file_transfer_handler) widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 71248db..7faf36d 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -1,6 +1,5 @@ from history.database import * from contacts import basecontact, common -import utils.util as util from messenger.messages import * from contacts.contact_menu import * from file_transfers import file_transfers as ft @@ -119,7 +118,7 @@ class Contact(basecontact.BaseContact): """ :return list of unsent messages """ - messages = filter(lambda x: x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr) + messages = filter(lambda x: x.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(messages) def get_unsent_messages_for_saving(self): @@ -127,12 +126,12 @@ class Contact(basecontact.BaseContact): :return list of unsent messages for saving """ messages = filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) - and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr) + and x.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(map(lambda x: x.get_data(), messages)) def mark_as_sent(self): try: - message = list(filter(lambda x: x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr))[0] + message = list(filter(lambda x: x.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr))[0] message.mark_as_sent() except Exception as ex: util.log('Mark as sent ex: ' + str(ex)) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index b4dbaf2..63775ff 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -2,9 +2,9 @@ from contacts.friend import * from file_transfers.file_transfers import * import time from contacts import basecontact -from contacts.group_chat import * import utils.ui as util_ui import random +import threading class Profile(basecontact.BaseContact): @@ -29,6 +29,7 @@ class Profile(basecontact.BaseContact): self._load_history = True self._waiting_for_reconnection = False self._contacts_manager = None + self._timer = threading.Timer(50, self.reconnect) # ----------------------------------------------------------------------------------------------------------------- # Edit current user's data @@ -47,7 +48,7 @@ class Profile(basecontact.BaseContact): self._tox.self_set_status(status) elif not self._waiting_for_reconnection: self._waiting_for_reconnection = True - QtCore.QTimer.singleShot(50000, self.reconnect) + self._timer.start() def set_name(self, value): if self.name == value: @@ -125,7 +126,7 @@ class Profile(basecontact.BaseContact): if self.status is None or all(list(map(lambda x: x.status is None, self._contacts))) and len(self._contacts): self._waiting_for_reconnection = True self.reset(self._screen.reset) - QtCore.QTimer.singleShot(50000, self.reconnect) + self._timer.start() def close(self): for friend in filter(lambda x: type(x) is Friend, self._contacts): @@ -135,13 +136,3 @@ class Profile(basecontact.BaseContact): if hasattr(self, '_call'): self._call.stop() del self._call - - def reset_avatar(self, generate_new): - super().reset_avatar(generate_new) - for friend in filter(lambda x: x.status is not None, self._contacts): - self.send_avatar(friend.number) - - def set_avatar(self, data): - super().set_avatar(data) - for friend in filter(lambda x: x.status is not None, self._contacts): - self.send_avatar(friend.number) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 92d92a6..aa1be58 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -40,7 +40,7 @@ class FileTransfer: self._path = path self._tox = tox self._friend_number = friend_number - self.state = FILE_TRANSFER_STATE['RUNNING'] + self._state = FILE_TRANSFER_STATE['RUNNING'] self._file_number = file_number self._creation_time = None self._size = float(size) @@ -63,6 +63,15 @@ class FileTransfer: file_number = property(get_file_number) + def get_state(self): + return self._state + + def set_state(self, value): + self._state = value + self._signal() + + state = property(get_state, set_state) + def get_friend_number(self): return self._friend_number @@ -87,30 +96,27 @@ class FileTransfer: self.send_control(TOX_FILE_CONTROL['CANCEL']) if self._file is not None: self._file.close() - self.signal() + self._signal() def cancelled(self): if self._file is not None: self._file.close() self.state = FILE_TRANSFER_STATE['CANCELLED'] - self.signal() def pause(self, by_friend): if not by_friend: self.send_control(TOX_FILE_CONTROL['PAUSE']) else: self.state = FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] - self.signal() def send_control(self, control): if self._tox.file_control(self._friend_number, self._file_number, control): self.state = control - self.signal() def get_file_id(self): return self._tox.file_get_file_id(self._friend_number, self._file_number) - def signal(self): + def _signal(self): percentage = self._done / self._size if self._size else 0 if self._creation_time is None or not percentage: t = -1 @@ -154,11 +160,11 @@ class SendTransfer(FileTransfer): self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) self._done += size else: - if hasattr(self, '_file'): + if self._file is not None: self._file.close() self.state = FILE_TRANSFER_STATE['FINISHED'] self._finished() - self.signal() + self._signal() class SendAvatar(SendTransfer): @@ -200,7 +206,7 @@ class SendFromBuffer(FileTransfer): else: self.state = FILE_TRANSFER_STATE['FINISHED'] self._finished() - self.signal() + self._signal() class SendFromFileBuffer(SendTransfer): @@ -265,7 +271,7 @@ class ReceiveTransfer(FileTransfer): if position + l > self._file_size: self._file_size = position + l self._done += l - self.signal() + self._signal() class ReceiveToBuffer(FileTransfer): @@ -296,7 +302,7 @@ class ReceiveToBuffer(FileTransfer): if position + l > self._data_size: self._data_size = position + l self._done += l - self.signal() + self._signal() class ReceiveAvatar(ReceiveTransfer): @@ -338,8 +344,4 @@ class ReceiveAvatar(ReceiveTransfer): chdir(dirname(avatar_path)) remove(avatar_path) rename(self._path, avatar_path) - self._finished(True) - - def _finished(self, emit=False): - if emit: - super()._finished() + self._finished() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 3d51767..bb4041f 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -5,7 +5,7 @@ import utils.util as util class FileTransfersHandler: - def __init__(self, tox, settings, contact_provider, file_transfers_message_service): + def __init__(self, tox, settings, contact_provider, file_transfers_message_service, profile): self._tox = tox self._settings = settings self._contact_provider = contact_provider @@ -14,6 +14,8 @@ class FileTransfersHandler: # key = (friend number, file number), value - transfer instance self._paused_file_transfers = dict(settings['paused_file_transfers']) # key - file id, value: [path, friend number, is incoming, start position] + + profile.avatar_changed_event.add_callback(self._send_avatar_to_contacts) def __del__(self): self._settings['paused_file_transfers'] = self._paused_file_transfers if self._settings['resend_files'] else {} @@ -43,18 +45,22 @@ class FileTransfersHandler: self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) return self._tox.file_seek(friend_number, file_number, pos) + self._file_transfers_message_service.add_incoming_transfer_message( + friend, accepted, size, file_name, file_number) self.accept_transfer(data[0], friend_number, file_number, size, False, pos) elif inline and size < 1024 * 1024: + self._file_transfers_message_service.add_incoming_transfer_message( + friend, accepted, size, file_name, file_number) self.accept_transfer('', friend_number, file_number, size, True) - elif auto: path = self._settings['auto_accept_path'] or util.curr_directory() + self._file_transfers_message_service.add_incoming_transfer_message( + friend, accepted, size, file_name, file_number) self.accept_transfer(path + '/' + file_name, friend_number, file_number, size) else: accepted = False - - self._file_transfers_message_service.add_incoming_transfer_message( - friend, accepted, size, file_name, file_number) + self._file_transfers_message_service.add_incoming_transfer_message( + friend, accepted, size, file_name, file_number) def cancel_transfer(self, friend_number, file_number, already_cancelled=False): """ @@ -92,7 +98,6 @@ class FileTransfersHandler: tr = self._file_transfers[(friend_number, file_number)] if by_friend: tr.state = FILE_TRANSFER_STATE['RUNNING'] - tr.signal() else: tr.send_control(TOX_FILE_CONTROL['RESUME']) @@ -117,7 +122,7 @@ class FileTransfersHandler: and m.file_number == file_number) rt.set_state_changed_handler(message.transfer_updated) self._file_transfers[(friend_number, file_number)] = rt - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) + rt.send_control(TOX_FILE_CONTROL['RESUME']) def send_screenshot(self, data, friend_number): """ @@ -140,11 +145,7 @@ class FileTransfersHandler: elif friend.status is None and is_resend: raise RuntimeError() st = SendFromBuffer(self._tox, friend.number, data, file_name) - st.set_transfer_finished_handler(self.transfer_finished) - file_number = st.get_file_number() - self._file_transfers[(friend.number, file_number)] = st - tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) - st.set_state_changed_handler(tm.transfer_updated) + self._send_file_add_handlers(st, friend, file_name) def send_file(self, path, friend_number, is_resend=False, file_id=None): """ @@ -163,12 +164,8 @@ class FileTransfersHandler: print('Error in sending') raise RuntimeError() st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) - st.set_transfer_finished_handler(self.transfer_finished) - file_number = st.get_file_number() - self._file_transfers[(friend_number, file_number)] = st file_name = os.path.basename(path) - tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) - st.set_state_changed_handler(tm.transfer_updated) + self._send_file_add_handlers(st, friend, file_name) def incoming_chunk(self, friend_number, file_number, position, data): """ @@ -244,6 +241,11 @@ class FileTransfersHandler: else: friend.reset_avatar(self._settings['identicons']) + def _send_avatar_to_contacts(self): + friends = self._get_all_friends() + for friend in friends: + self.send_avatar(friend.number) + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- @@ -251,6 +253,16 @@ class FileTransfersHandler: def _get_friend_by_number(self, friend_number): return self._contact_provider.get_friend_by_number(friend_number) + def _get_all_friends(self): + return self._contact_provider.get_all_friends() + + def _send_file_add_handlers(self, st, friend, file_name): + st.set_transfer_finished_handler(self.transfer_finished) + file_number = st.get_file_number() + self._file_transfers[(friend.number, file_number)] = st + tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + st.set_state_changed_handler(tm.transfer_updated) + @staticmethod def _generate_valid_path(path, from_position): path, file_name = os.path.split(path) diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index b14dfd5..9a56aba 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -72,7 +72,7 @@ class Message: def _get_id(): Message.MESSAGE_ID += 1 - return Message.MESSAGE_ID + return int(Message.MESSAGE_ID) class TextMessage(Message): diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index c9571a2..d4fc7c4 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -11,6 +11,7 @@ import numpy as np from middleware.threads import invoke_in_main_thread, execute from notifications.tray import tray_notification from notifications.sound import * +import threading # TODO: gc callbacks and refactoring. Use contact provider instead of manager @@ -49,7 +50,11 @@ def friend_status(contacts_manager, file_transfer_handler, profile, settings): if friend.status is None and settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) invoke_in_main_thread(friend.set_status, new_status) - invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: file_transfer_handler.send_files(friend_number)) + + def set_timer(): + t = threading.Timer(5, lambda: file_transfer_handler.send_files(friend_number)) + t.start() + invoke_in_main_thread(set_timer) invoke_in_main_thread(contacts_manager.update_filtration) return wrapped From bcefe9bc79d2a3c2477ee000f7c1f2020de1f856 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 May 2018 16:59:46 +0300 Subject: [PATCH 034/217] friend menu fixes - correct ordering, submenus fixes --- toxygen/contacts/contact_menu.py | 109 +++++++++++++++++-------------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index d924702..8047392 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -6,16 +6,19 @@ import utils.ui as util_ui # Builder # ----------------------------------------------------------------------------------------------------------------- -def _create_menu(menu_name): - return QtWidgets.QMenu(menu_name or '') +def _create_menu(menu_name, parent): + menu_name = menu_name or '' + + return QtWidgets.QMenu(menu_name) if parent is None else parent.addMenu(menu_name) class ContactMenuBuilder: def __init__(self): - self._actions = [] - self._submenus = [] + self._actions = {} + self._submenus = {} self._name = None + self._index = 0 def with_name(self, name): self._name = name @@ -23,40 +26,49 @@ class ContactMenuBuilder: return self def with_action(self, text, handler): - self._actions.append((text, handler)) + self._add_action(text, handler) return self def with_actions(self, actions): - self._actions.extend(actions) + for action in actions: + self._add_action(*action) return self - def with_submenu(self, submenu): - self._add_submenu(submenu) + def with_submenu(self, submenu_builder): + self._add_submenu(submenu_builder) return self - def with_optional_submenu(self, submenu): - if submenu is not None: - self._add_submenu(submenu) + def with_optional_submenu(self, submenu_builder): + if submenu_builder is not None: + self._add_submenu(submenu_builder) return self - def build(self): # TODO: actions order - menu = _create_menu(self._name) + def build(self, parent=None): + menu = _create_menu(self._name, parent) - for text, handler in self._actions: - action = menu.addAction(text) - action.triggered.connect(handler) - - for submenu in self._submenus: - menu.addMenu(submenu) + for i in range(self._index): + if i in self._actions: + text, handler = self._actions[i] + action = menu.addAction(text) + action.triggered.connect(handler) + else: + submenu_builder = self._submenus[i] + submenu = submenu_builder.build(menu) + menu.addMenu(submenu) return menu def _add_submenu(self, submenu): - self._submenus.append(submenu) + self._submenus[self._index] = submenu + self._index += 1 + + def _add_action(self, text, handler): + self._actions[self._index] = (text, handler) + self._index += 1 # ----------------------------------------------------------------------------------------------------------------- # Generators @@ -75,9 +87,9 @@ class BaseContactMenuGenerator: class FriendMenuGenerator(BaseContactMenuGenerator): def generate(self, plugin_loader, contacts_manager, main_screen, settings, number): - history_menu = self._generate_history_menu(main_screen, number) - copy_menu = self._generate_copy_menu(main_screen) - plugins_menu = self._generate_plugins_menu(plugin_loader, number) + history_menu_builder = self._generate_history_menu_builder(main_screen, number) + copy_menu_builder = self._generate_copy_menu_builder(main_screen) + plugins_menu_builder = self._generate_plugins_menu_builder(plugin_loader, number) allowed = self._contact.tox_id in settings['auto_accept_from_friends'] auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') @@ -85,14 +97,13 @@ class FriendMenuGenerator(BaseContactMenuGenerator): builder = ContactMenuBuilder() menu = (builder .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) - .with_action(util_ui.tr('Chat history'), lambda: main_screen.clear_history(number)) - .with_submenu(history_menu) - .with_submenu(copy_menu) + .with_submenu(history_menu_builder) + .with_submenu(copy_menu_builder) .with_action(auto, lambda: main_screen.auto_accept(number, not allowed)) .with_action(util_ui.tr('Remove friend'), lambda: main_screen.remove_friend(number)) .with_action(util_ui.tr('Block friend'), lambda: main_screen.block_friend(number)) .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) - .with_optional_submenu(plugins_menu) + .with_optional_submenu(plugins_menu_builder) ).build() return menu @@ -102,42 +113,42 @@ class FriendMenuGenerator(BaseContactMenuGenerator): # ----------------------------------------------------------------------------------------------------------------- @staticmethod - def _generate_history_menu(main_screen, number): + def _generate_history_menu_builder(main_screen, number): history_menu_builder = ContactMenuBuilder() - history_menu = (history_menu_builder - .with_name(util_ui.tr('Chat history')) - .with_action(util_ui.tr('Clear history'), lambda: main_screen.clear_history(number)) - .with_action(util_ui.tr('Export as text'), lambda: main_screen.export_history(number)) - .with_action(util_ui.tr('Export as HTML'), lambda: main_screen.export_history(number, False)) - ).build() + (history_menu_builder + .with_name(util_ui.tr('Chat history')) + .with_action(util_ui.tr('Clear history'), lambda: main_screen.clear_history(number)) + .with_action(util_ui.tr('Export as text'), lambda: main_screen.export_history(number)) + .with_action(util_ui.tr('Export as HTML'), lambda: main_screen.export_history(number, False)) + ) - return history_menu + return history_menu_builder - def _generate_copy_menu(self, main_screen): + def _generate_copy_menu_builder(self, main_screen): copy_menu_builder = ContactMenuBuilder() - copy_menu = (copy_menu_builder - .with_name(util_ui.tr('Copy')) - .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name)) - .with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.name)) - .with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id)) - ).build() + (copy_menu_builder + .with_name(util_ui.tr('Copy')) + .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name)) + .with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.status_message)) + .with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id)) + ) - return copy_menu + return copy_menu_builder @staticmethod - def _generate_plugins_menu(plugin_loader, number): + def _generate_plugins_menu_builder(plugin_loader, number): if plugin_loader is None: return None plugins_actions = plugin_loader.get_menu(number) if not len(plugins_actions): return None plugins_menu_builder = ContactMenuBuilder() - plugins_menu = (plugins_menu_builder - .with_name(util_ui.tr('Plugins')) - .with_actions(plugins_actions) - ).build() + (plugins_menu_builder + .with_name(util_ui.tr('Plugins')) + .with_actions(plugins_actions) + ) - return plugins_menu + return plugins_menu_builder def _generate_groups_menu(self, contacts_manager): # TODO: fix chats = contacts_manager.get_group_chats() From 0b1e899931f97ee559a16b8e809fedcf7f889439 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 May 2018 19:03:58 +0300 Subject: [PATCH 035/217] various fixes - file transfers, friend exit callback --- toxygen/av/calls_manager.py | 18 +++++++++------- toxygen/contacts/contact_menu.py | 3 ++- toxygen/contacts/profile.py | 21 ------------------- .../file_transfers/file_transfers_handler.py | 13 +++++++++++- toxygen/middleware/callbacks.py | 10 ++++++--- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py index 03d5c53..6e290e2 100644 --- a/toxygen/av/calls_manager.py +++ b/toxygen/av/calls_manager.py @@ -1,7 +1,7 @@ import threading import cv2 import av.calls -from PyQt5 import QtWidgets +import utils.ui as util_ui from messenger.messages import * import time from ui import av_widgets @@ -35,9 +35,9 @@ class CallsManager: self._call(num, audio, video) self._screen.active_call() if video: - text = QtWidgets.QApplication.translate("incoming_call", "Outgoing video call") + text = util_ui.tr("Outgoing video call") else: - text = QtWidgets.QApplication.translate("incoming_call", "Outgoing audio call") + text = util_ui.tr("Outgoing audio call") self.get_curr_friend().append_message(InfoMessage(text, time.time())) self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) self._messages.scrollToBottom() @@ -52,9 +52,9 @@ class CallsManager: return friend = self.get_friend_by_number(friend_number) if video: - text = QtWidgets.QApplication.translate("incoming_call", "Incoming video call") + text = util_ui.tr("Incoming video call") else: - text = QtWidgets.QApplication.translate("incoming_call", "Incoming audio call") + text = util_ui.tr("Incoming audio call") friend.append_message(InfoMessage(text, time.time())) self._incoming_calls.add(friend_number) if friend_number == self.get_active_number(): @@ -83,9 +83,9 @@ class CallsManager: """ if friend_number in self._incoming_calls: self._incoming_calls.remove(friend_number) - text = QtWidgets.QApplication.translate("incoming_call", "Call declined") + text = util_ui.tr("Call declined") else: - text = QtWidgets.QApplication.translate("incoming_call", "Call finished") + text = util_ui.tr("Call finished") self._screen.call_finished() is_video = self._call.is_video_call(friend_number) self._call.finish_call(friend_number, by_friend) # finish or decline call @@ -103,3 +103,7 @@ class CallsManager: if friend_number == self.get_active_number(): self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) self._messages.scrollToBottom() + + def friend_exit(self, friend_number): + if friend_number in self._call: + self._call.finish_call(friend_number, True) diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index 8047392..c580a3f 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -32,7 +32,8 @@ class ContactMenuBuilder: def with_actions(self, actions): for action in actions: - self._add_action(*action) + (text, handler) = action + self._add_action(text, handler) return self diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 63775ff..4edae71 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -75,27 +75,6 @@ class Profile(basecontact.BaseContact): return self._tox_id - # ----------------------------------------------------------------------------------------------------------------- - # Friend connection status callbacks - # ----------------------------------------------------------------------------------------------------------------- - - def friend_exit(self, friend_number): - """ - Friend with specified number quit - """ - self.get_friend_by_number(friend_number).status = None - self.friend_typing(friend_number, False) - if friend_number in self._call: - self._call.finish_call(friend_number, True) - for friend_num, file_num in list(self._file_transfers.keys()): - if friend_num == friend_number: - ft = self._file_transfers[(friend_num, file_num)] - if type(ft) is SendTransfer: - self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, False, -1] - elif type(ft) is ReceiveTransfer and ft.state != FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: - self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, True, ft.total_size()] - self.cancel_transfer(friend_num, file_num, True) - # ----------------------------------------------------------------------------------------------------------------- # Private messages # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index bb4041f..d060ae7 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -118,7 +118,8 @@ class FileTransfersHandler: rt = ReceiveToBuffer(self._tox, friend_number, size, file_number) rt.set_transfer_finished_handler(self.transfer_finished) message = friend.get_message(lambda m: m.type == MESSAGE_TYPE['FILE_TRANSFER'] - and m.state == FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'] + and m.state in (FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'], + FILE_TRANSFER_STATE['RUNNING']) and m.file_number == file_number) rt.set_state_changed_handler(message.transfer_updated) self._file_transfers[(friend_number, file_number)] = rt @@ -214,6 +215,16 @@ class FileTransfersHandler: except Exception as ex: print('Exception in file sending: ' + str(ex)) + def friend_exit(self, friend_number): + for friend_num, file_num in list(self._file_transfers.keys()): + if friend_num == friend_number: + ft = self._file_transfers[(friend_num, file_num)] + if type(ft) is SendTransfer: + self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, False, -1] + elif type(ft) is ReceiveTransfer and ft.state != FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, True, ft.total_size()] + self.cancel_transfer(friend_num, file_num, True) + # ----------------------------------------------------------------------------------------------------------------- # Avatars support # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index d4fc7c4..860070f 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -60,7 +60,8 @@ def friend_status(contacts_manager, file_transfer_handler, profile, settings): return wrapped -def friend_connection_status(contacts_manager, profile, settings, plugin_loader, file_transfer_handler): +def friend_connection_status(contacts_manager, profile, settings, plugin_loader, file_transfer_handler, + messenger, calls_manager): def wrapped(tox, friend_number, new_status, user_data): """ Check friend's connection status (offline, udp, tcp) @@ -68,8 +69,11 @@ def friend_connection_status(contacts_manager, profile, settings, plugin_loader, print("Friend #{} connection status: {}".format(friend_number, new_status)) friend = contacts_manager.get_friend_by_number(friend_number) if new_status == TOX_CONNECTION['NONE']: - invoke_in_main_thread(profile.friend_exit, friend_number) + invoke_in_main_thread(friend.set_status, None) + invoke_in_main_thread(file_transfer_handler.friend_exit, friend_number) invoke_in_main_thread(contacts_manager.update_filtration) + invoke_in_main_thread(messenger.friend_typing, friend_number, False) + invoke_in_main_thread(calls_manager.friend_exit, friend_number) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) elif friend.status is None: @@ -394,7 +398,7 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_friend_status(friend_status(contacts_manager, file_transfer_handler, profile, settings), 0) tox.callback_friend_message(friend_message(messenger, contacts_manager, profile, settings, main_window, tray), 0) tox.callback_friend_connection_status(friend_connection_status(contacts_manager, profile, settings, plugin_loader, - file_transfer_handler), 0) + file_transfer_handler, messenger, calls_manager), 0) tox.callback_friend_name(friend_name(contacts_manager), 0) tox.callback_friend_status_message(friend_status_message(contacts_manager, messenger), 0) tox.callback_friend_request(friend_request(contacts_manager), 0) From bfa91df927236ad81b2b31dfd7076c91db496678 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 May 2018 19:28:44 +0300 Subject: [PATCH 036/217] fixed deps in main_screen.py --- toxygen/app.py | 2 +- toxygen/ui/main_screen.py | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 03509a6..a618cc2 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -323,7 +323,7 @@ class App: self._toxes, self._version) self._tray = tray.init_tray(profile, self._settings, self._ms) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, - self._plugin_loader, self._file_transfer_handler) + self._plugin_loader, self._file_transfer_handler, history, self._calls_manager) self._tray.show() self._ms.show() diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index c267a12..67b09a7 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -19,17 +19,19 @@ class MainWindow(QtWidgets.QMainWindow): self.setAcceptDrops(True) self._saved = False self._profile = None - self._file_transfer_handler = None + self._file_transfer_handler = self._history_loader = self._calls_manager = None self.initUI() def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader, - file_transfer_handler): + file_transfer_handler, history_loader, calls_manager): self._widget_factory = widget_factory self._tray = tray self._contacts_manager = contacts_manager self._profile = profile self._plugins_loader = plugins_loader self._file_transfer_handler = file_transfer_handler + self._history_loader = history_loader + self._calls_manager = calls_manager self.messageEdit.set_messenger(messenger) def show(self): @@ -267,11 +269,11 @@ class MainWindow(QtWidgets.QMainWindow): self.callButton = QtWidgets.QPushButton(Form) self.callButton.setGeometry(QtCore.QRect(550, 5, 50, 50)) self.callButton.setObjectName("callButton") - self.callButton.clicked.connect(lambda: self.profile.call_click(True)) + self.callButton.clicked.connect(lambda: self._calls_manager.call_click(True)) self.videocallButton = QtWidgets.QPushButton(Form) self.videocallButton.setGeometry(QtCore.QRect(550, 5, 50, 50)) self.videocallButton.setObjectName("videocallButton") - self.videocallButton.clicked.connect(lambda: self.profile.call_click(True, True)) + self.videocallButton.clicked.connect(lambda: self._calls_manager.call_click(True, True)) self.update_call_state('call') self.typing = QtWidgets.QLabel(Form) self.typing.setGeometry(QtCore.QRect(500, 25, 50, 30)) @@ -306,7 +308,7 @@ class MainWindow(QtWidgets.QMainWindow): def load(pos): if not pos: - self.profile.load_history() + self._history_loader.load_history() self.messages.verticalScrollBar().setValue(1) self.messages.verticalScrollBar().valueChanged.connect(load) self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) @@ -413,7 +415,7 @@ class MainWindow(QtWidgets.QMainWindow): elif key == QtCore.Qt.Key_C and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): rows = list(map(lambda x: self.messages.row(x), self.messages.selectedItems())) indexes = (rows[0] - self.messages.count(), rows[-1] - self.messages.count()) - s = self.profile.export_history(self.profile.active_friend, True, indexes) + s = self._history_loader.export_history(self._contacts_manager.get_curr_friend(), True, indexes) clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(s) elif key == QtCore.Qt.Key_Z and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): @@ -622,7 +624,7 @@ class MainWindow(QtWidgets.QMainWindow): self._contacts_manager.delete_friend(num) def block_friend(self, num): - friend = self.profile.get_contact(num) + friend = self._contacts_managere.get_contact(num) self._contacts_manager.block_user(friend.tox_id) @staticmethod @@ -662,7 +664,7 @@ class MainWindow(QtWidgets.QMainWindow): pos = self.connection_status.pos() x, y = pos.x() + self.user_info.pos().x(), pos.y() + self.user_info.pos().y() if (x < event.x() < x + 32) and (y < event.y() < y + 32): - self.profile.change_status() + self._profile.change_status() else: super().mouseReleaseEvent(event) From 9365ca291363311031a54f7b6ee615fa332fffa8 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 May 2018 21:45:35 +0300 Subject: [PATCH 037/217] file transfers fixes - part 5 --- toxygen/contacts/friend.py | 4 ++-- toxygen/file_transfers/file_transfers_handler.py | 15 ++++++++------- toxygen/ui/messages_widgets.py | 4 +++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 4b189a9..9c4e719 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -49,8 +49,8 @@ class Friend(contact.Contact): self._corr = list(filter(is_valid, self._corr)) - def delete_one_unsent_file(self, time): - self._corr = list(filter(lambda x: not (type(x) is UnsentFile and x.get_data()[2] == time), self._corr)) + def delete_one_unsent_file(self, message_id): + self._corr = list(filter(lambda m: not (type(m) is UnsentFile and m.message_id == message_id), self._corr)) # ----------------------------------------------------------------------------------------------------------------- # History support diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index d060ae7..4ee1282 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -81,8 +81,8 @@ class FileTransfersHandler: elif not already_cancelled: self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) - def cancel_not_started_transfer(self, cancel_time): - self.get_curr_friend().delete_one_unsent_file(cancel_time) + def cancel_not_started_transfer(self, friend_number, message_id): + self._get_friend_by_number(friend_number).delete_one_unsent_file(message_id) def pause_transfer(self, friend_number, file_number, by_friend=False): """ @@ -128,7 +128,8 @@ class FileTransfersHandler: def send_screenshot(self, data, friend_number): """ Send screenshot - :param data: raw data - png + :param data: raw data - png format + :param friend_number: friend number """ self.send_inline(data, 'toxygen_inline.png', friend_number) @@ -158,7 +159,7 @@ class FileTransfersHandler: """ friend = self._get_friend_by_number(friend_number) if friend.status is None and not is_resend: - m = UnsentFile(path, None, time.time()) + m = UnsentFile(path, None, util.get_unix_time()) friend.append_message(m) return elif friend.status is None and is_resend: @@ -220,9 +221,9 @@ class FileTransfersHandler: if friend_num == friend_number: ft = self._file_transfers[(friend_num, file_num)] if type(ft) is SendTransfer: - self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, False, -1] + self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, False, -1] elif type(ft) is ReceiveTransfer and ft.state != FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: - self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, True, ft.total_size()] + self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, True, ft.total_size()] self.cancel_transfer(friend_num, file_num, True) # ----------------------------------------------------------------------------------------------------------------- @@ -235,7 +236,7 @@ class FileTransfersHandler: :param avatar_path: path to avatar or None if reset """ sa = SendAvatar(avatar_path, self._tox, friend_number) - self._file_transfers[(friend_number, sa.get_file_number())] = sa + self._file_transfers[(friend_number, sa.file_number)] = sa def incoming_avatar(self, friend_number, file_number, size): """ diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index 5d86b64..818f766 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -397,9 +397,11 @@ class UnsentFileItem(FileTransferItem): movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) self.time.setMovie(movie) movie.start() + self._message_id = transfer_message.message_id + self._friend_number = transfer_message.friend_number def cancel_transfer(self, *args): - self._file_transfer_handler.cancel_not_started_transfer(self._time) + self._file_transfer_handler.cancel_not_started_transfer(self._friend_number, self._message_id) class InlineImageItem(QtWidgets.QScrollArea): From a3103f6fb94619512b963190d16dff3b18c4b610 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 May 2018 23:31:48 +0300 Subject: [PATCH 038/217] file transfers fixes - part 6 --- toxygen/file_transfers/file_transfers.py | 4 +++- toxygen/middleware/callbacks.py | 7 ++----- toxygen/ui/items_factories.py | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index aa1be58..e5c2bb4 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -136,11 +136,13 @@ class SendTransfer(FileTransfer): def __init__(self, path, tox, friend_number, kind=TOX_FILE_KIND['DATA'], file_id=None): if path is not None: - self._file = open(path, 'rb') + fl = open(path, 'rb') size = getsize(path) else: + fl = None size = 0 super().__init__(path, tox, friend_number, size) + self._file = fl self.state = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] self._file_number = tox.file_send(friend_number, kind, size, file_id, bytes(basename(path), 'utf-8') if path else b'') diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 860070f..9f4cdd8 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -26,11 +26,8 @@ def self_connection_status(tox, profile): """ def wrapped(tox_link, connection, user_data): print('Connection status: ', str(connection)) - if profile.status is None: - status = tox.self_get_status() - invoke_in_main_thread(profile.set_status, status) - elif connection == TOX_CONNECTION['NONE']: - invoke_in_main_thread(profile.set_status, None) + status = None if connection == TOX_CONNECTION['NONE'] else tox.self_get_status() + invoke_in_main_thread(profile.set_status, None) return wrapped diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index a9b1c1a..46e9df8 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -70,7 +70,7 @@ class MessagesItemsFactory: return item def create_file_transfer_item(self, tm, append=True): - item = tm.get_widget(self._file_transfers_handler, self._settings, self._messages.width()) + item = tm.get_widget(self._file_transfers_handler, self._settings, self._messages.width(), self._messages) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) if append: From 1b8241eee9ea628f45b25b89f4ec0af8c780cd64 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 May 2018 00:06:14 +0300 Subject: [PATCH 039/217] profile minor fixes --- toxygen/app.py | 4 ++-- toxygen/contacts/profile.py | 29 +++++++++--------------- toxygen/file_transfers/file_transfers.py | 2 +- toxygen/middleware/callbacks.py | 6 ++--- toxygen/ui/menu.py | 2 +- 5 files changed, 18 insertions(+), 25 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index a618cc2..3a3476e 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -298,11 +298,11 @@ class App: self._calls_manager = CallsManager(self._tox.AV, self._settings) db = Database(self._path.replace('.tox', '.db'), self._toxes) - profile = Profile(self._profile_manager, self._tox, self._ms) - self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) friend_items_factory = FriendItemsFactory(self._settings, self._ms) self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, friend_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory) + profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) + self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) history = None messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, self._ms, lambda m: history.delete_message(m)) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 4edae71..6393f7e 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -11,7 +11,7 @@ class Profile(basecontact.BaseContact): """ Profile of current toxygen user. Contains friends list, tox instance """ - def __init__(self, profile_manager, tox, screen): + def __init__(self, profile_manager, tox, screen, contacts_provider, reset_action): """ :param tox: tox instance :param screen: ref to main screen @@ -25,11 +25,10 @@ class Profile(basecontact.BaseContact): self._screen = screen self._messages = screen.messages self._tox = tox - self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number) - self._load_history = True + self._contacts_provider = contacts_provider + self._reset_action = reset_action self._waiting_for_reconnection = False - self._contacts_manager = None - self._timer = threading.Timer(50, self.reconnect) + self._timer = threading.Timer(50, self._reconnect) # ----------------------------------------------------------------------------------------------------------------- # Edit current user's data @@ -68,7 +67,7 @@ class Profile(basecontact.BaseContact): super().set_status_message(value) self._tox.self_set_status_message(self._status_message.encode('utf-8')) - def new_nospam(self): + def set_new_nospam(self): """Sets new nospam part of tox id""" self._tox.self_set_nospam(random.randint(0, 4294967295)) # no spam - uint32 self._tox_id = self._tox.self_get_address() @@ -88,23 +87,20 @@ class Profile(basecontact.BaseContact): # Reset # ----------------------------------------------------------------------------------------------------------------- - def reset(self, restart): + def _restart(self): """ Recreate tox instance - :param restart: method which calls restart and returns new tox instance """ - for friend in self._contacts: - self.friend_exit(friend.number) del self._tox - self._tox = restart() + self._tox = self._reset_action() self.status = None - self._contacts_manager.update_filtration() - def reconnect(self): + def _reconnect(self): self._waiting_for_reconnection = False - if self.status is None or all(list(map(lambda x: x.status is None, self._contacts))) and len(self._contacts): + contacts = self._contacts_provider.get_all() + if self.status is None or all(list(map(lambda x: x.status is None, contacts))) and len(contacts): self._waiting_for_reconnection = True - self.reset(self._screen.reset) + self._restart() self._timer.start() def close(self): @@ -112,6 +108,3 @@ class Profile(basecontact.BaseContact): self.friend_exit(friend.number) for i in range(len(self._contacts)): del self._contacts[0] - if hasattr(self, '_call'): - self._call.stop() - del self._call diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index e5c2bb4..710320c 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -161,12 +161,12 @@ class SendTransfer(FileTransfer): data = self._file.read(size) self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) self._done += size + self._signal() else: if self._file is not None: self._file.close() self.state = FILE_TRANSFER_STATE['FINISHED'] self._finished() - self._signal() class SendAvatar(SendTransfer): diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 9f4cdd8..83dcc64 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -1,4 +1,4 @@ -from PyQt5 import QtGui, QtCore +from PyQt5 import QtGui from user_data.settings import Settings from contacts.profile import Profile from wrapper.toxcore_enums_and_consts import * @@ -26,8 +26,8 @@ def self_connection_status(tox, profile): """ def wrapped(tox_link, connection, user_data): print('Connection status: ', str(connection)) - status = None if connection == TOX_CONNECTION['NONE'] else tox.self_get_status() - invoke_in_main_thread(profile.set_status, None) + status = tox.self_get_status() if connection != TOX_CONNECTION['NONE'] else None + invoke_in_main_thread(profile.set_status, status) return wrapped diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 1a33bde..d912619 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -242,7 +242,7 @@ class ProfileSettings(CenteredWidget): self.copy_pk.setIconSize(QtCore.QSize(10, 10)) def new_no_spam(self): - self.tox_id.setText(self._profile.new_nospam()) + self.tox_id.setText(self._profile.set_new_nospam()) def reset_avatar(self): self._profile.reset_avatar(self._settings['identicons']) From bde69bd417172ab761e1a184f14fb82e9d7b9b5d Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 May 2018 12:26:02 +0300 Subject: [PATCH 040/217] file transfers fixes - part 7 --- toxygen/app.py | 1 + toxygen/contacts/friend.py | 5 ++--- toxygen/file_transfers/file_transfers.py | 4 ++++ .../file_transfers/file_transfers_handler.py | 19 +++++++++++++------ toxygen/middleware/callbacks.py | 12 +++++++++--- toxygen/ui/items_factories.py | 2 +- toxygen/ui/messages_widgets.py | 4 ++-- 7 files changed, 32 insertions(+), 15 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 3a3476e..d20eea7 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -29,6 +29,7 @@ from messenger.messenger import Messenger from network.tox_dns import ToxDns from history.history import History from file_transfers.file_transfers_messages_service import FileTransfersMessagesService +import styles.style # TODO: dynamic loading class App: diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 9c4e719..81830da 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -18,13 +18,12 @@ class Friend(contact.Contact): # File transfers support # ----------------------------------------------------------------------------------------------------------------- - def update_transfer_data(self, file_number, inline): # TODO: rewrite + def insert_inline(self, before_message_id, inline): """ Update status of active transfer and load inline if needed """ try: - tr = list(filter(lambda x: x.get_type() == MESSAGE_TYPE['FILE_TRANSFER'] and x.is_active(file_number), - self._corr))[0] + tr = list(filter(lambda x: x.message_id == before_message_id, self._corr))[0] i = self._corr.index(tr) if inline: # inline was loaded self._corr.insert(i, inline) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 710320c..166cc7f 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -198,6 +198,8 @@ class SendFromBuffer(FileTransfer): def get_data(self): return self._data + data = property(get_data) + def send_chunk(self, position, size): if self._creation_time is None: self._creation_time = time() @@ -289,6 +291,8 @@ class ReceiveToBuffer(FileTransfer): def get_data(self): return self._data + data = property(get_data) + def write_chunk(self, position, data): if self._creation_time is None: self._creation_time = time() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 4ee1282..f64c201 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -14,6 +14,8 @@ class FileTransfersHandler: # key = (friend number, file number), value - transfer instance self._paused_file_transfers = dict(settings['paused_file_transfers']) # key - file id, value: [path, friend number, is incoming, start position] + self._insert_inline_before = {} + # key = (friend number, file number), value - message id profile.avatar_changed_event.add_callback(self._send_avatar_to_contacts) @@ -124,6 +126,8 @@ class FileTransfersHandler: rt.set_state_changed_handler(message.transfer_updated) self._file_transfers[(friend_number, file_number)] = rt rt.send_control(TOX_FILE_CONTROL['RESUME']) + if inline: + self._insert_inline_before[(friend_number, file_number)] = message.message_id def send_screenshot(self, data, friend_number): """ @@ -147,7 +151,7 @@ class FileTransfersHandler: elif friend.status is None and is_resend: raise RuntimeError() st = SendFromBuffer(self._tox, friend.number, data, file_name) - self._send_file_add_handlers(st, friend, file_name) + self._send_file_add_set_handlers(st, friend, file_name, True) def send_file(self, path, friend_number, is_resend=False, file_id=None): """ @@ -167,7 +171,7 @@ class FileTransfersHandler: raise RuntimeError() st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) file_name = os.path.basename(path) - self._send_file_add_handlers(st, friend, file_name) + self._send_file_add_set_handlers(st, friend, file_name) def incoming_chunk(self, friend_number, file_number, position, data): """ @@ -188,11 +192,12 @@ class FileTransfersHandler: self._get_friend_by_number(friend_number).load_avatar() elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']): # inline image print('inline') - inline = InlineImage(transfer.get_data()) - index = self._get_friend_by_number(friend_number).update_transfer_data(file_number, inline) + inline = InlineImage(transfer.data) + message_id = self._insert_inline_before[(friend_number, file_number)] + del self._insert_inline_before[(friend_number, file_number)] + index = self._get_friend_by_number(friend_number).insert_inline(message_id, inline) self._file_transfers_message_service.add_inline_message(transfer, index) del self._file_transfers[(friend_number, file_number)] - del transfer def send_files(self, friend_number): friend = self._get_friend_by_number(friend_number) @@ -268,12 +273,14 @@ class FileTransfersHandler: def _get_all_friends(self): return self._contact_provider.get_all_friends() - def _send_file_add_handlers(self, st, friend, file_name): + def _send_file_add_set_handlers(self, st, friend, file_name, inline=False): st.set_transfer_finished_handler(self.transfer_finished) file_number = st.get_file_number() self._file_transfers[(friend.number, file_number)] = st tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) st.set_state_changed_handler(tm.transfer_updated) + if inline: + self._insert_inline_before[(friend.number, file_number)] = tm.message_id @staticmethod def _generate_valid_path(path, from_position): diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 83dcc64..bd122d6 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -195,8 +195,11 @@ def file_recv_chunk(file_transfer_handler): Incoming chunk """ def wrapped(tox, friend_number, file_number, position, chunk, length, user_data): - execute(file_transfer_handler.incoming_chunk, friend_number, file_number, position, - chunk[:length] if length else None) + chunk = chunk[:length] if length else None + if length: + execute(file_transfer_handler.incoming_chunk, friend_number, file_number, position, chunk) + else: + invoke_in_main_thread(file_transfer_handler.incoming_chunk, friend_number, file_number, position, chunk) return wrapped @@ -206,7 +209,10 @@ def file_chunk_request(file_transfer_handler): Outgoing chunk """ def wrapped(tox, friend_number, file_number, position, size, user_data): - execute(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) + if size: + execute(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) + else: + invoke_in_main_thread(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) return wrapped diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index 46e9df8..84bf9c3 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -47,7 +47,7 @@ class MessagesItemsFactory: def create_inline_item(self, data, append=True, position=0): elem = QtWidgets.QListWidgetItem() - item = InlineImageItem(data, self._messages.width(), elem) + item = InlineImageItem(data, self._messages.width(), elem, self._messages) elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) if append: self._messages.addItem(elem) diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index 818f766..ad337dd 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -406,9 +406,9 @@ class UnsentFileItem(FileTransferItem): class InlineImageItem(QtWidgets.QScrollArea): - def __init__(self, data, width, elem): + def __init__(self, data, width, elem, parent=None): - QtWidgets.QScrollArea.__init__(self) + QtWidgets.QScrollArea.__init__(self, parent) self.setFocusPolicy(QtCore.Qt.NoFocus) self._elem = elem self._image_label = QtWidgets.QLabel(self) From e8a0a3f5bebb171f05bdb6cd0e60e60d8f4696d3 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 May 2018 12:54:00 +0300 Subject: [PATCH 041/217] file transfers fixes - part 8 (unsent files minor fixes) --- toxygen/contacts/friend.py | 13 +++++----- .../file_transfers/file_transfers_handler.py | 26 +++++++++---------- .../file_transfers_messages_service.py | 13 ++++++++++ toxygen/messenger/messages.py | 17 ++++++++++-- toxygen/ui/items_factories.py | 8 +++--- toxygen/ui/main_screen.py | 3 ++- toxygen/ui/messages_widgets.py | 4 +-- 7 files changed, 55 insertions(+), 29 deletions(-) diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 81830da..63edd6a 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -32,15 +32,15 @@ class Friend(contact.Contact): pass def get_unsent_files(self): - messages = filter(lambda x: type(x) is UnsentFile, self._corr) - return messages + messages = filter(lambda x: type(x) is UnsentFileMessage, self._corr) + return list(messages) def clear_unsent_files(self): - self._corr = list(filter(lambda x: type(x) is not UnsentFile, self._corr)) + self._corr = list(filter(lambda x: type(x) is not UnsentFileMessage, self._corr)) - def remove_invalid_unsent_files(self): + def remove_invalid_unsent_files(self): # TODO: fix def is_valid(message): - if type(message) is not UnsentFile: + if type(message) is not UnsentFileMessage: return True if message.get_data()[1] is not None: return True @@ -49,7 +49,8 @@ class Friend(contact.Contact): self._corr = list(filter(is_valid, self._corr)) def delete_one_unsent_file(self, message_id): - self._corr = list(filter(lambda m: not (type(m) is UnsentFile and m.message_id == message_id), self._corr)) + self._corr = list(filter(lambda m: not (type(m) is UnsentFileMessage and m.message_id == message_id), + self._corr)) # ----------------------------------------------------------------------------------------------------------------- # History support diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index f64c201..c46ee58 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -145,8 +145,7 @@ class FileTransfersHandler: def send_inline(self, data, file_name, friend_number, is_resend=False): friend = self._get_friend_by_number(friend_number) if friend.status is None and not is_resend: - m = UnsentFile(file_name, data, time.time()) - friend.append_message(m) + self._file_transfers_message_service.add_unsent_file_message(friend, file_name, data) return elif friend.status is None and is_resend: raise RuntimeError() @@ -163,8 +162,7 @@ class FileTransfersHandler: """ friend = self._get_friend_by_number(friend_number) if friend.status is None and not is_resend: - m = UnsentFile(path, None, util.get_unix_time()) - friend.append_message(m) + self._file_transfers_message_service.add_unsent_file_message(friend, path, None) return elif friend.status is None and is_resend: print('Error in sending') @@ -192,7 +190,7 @@ class FileTransfersHandler: self._get_friend_by_number(friend_number).load_avatar() elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']): # inline image print('inline') - inline = InlineImage(transfer.data) + inline = InlineImageMessage(transfer.data) message_id = self._insert_inline_before[(friend_number, file_number)] del self._insert_inline_before[(friend_number, file_number)] index = self._get_friend_by_number(friend_number).insert_inline(message_id, inline) @@ -203,20 +201,20 @@ class FileTransfersHandler: friend = self._get_friend_by_number(friend_number) friend.remove_invalid_unsent_files() files = friend.get_unsent_files() - try: # TODO: fix + try: for fl in files: - data = fl.get_data() - if data[1] is not None: - self.send_inline(data[1], data[0], friend_number, True) + data, path = fl.data, fl.path + if data is not None: + self.send_inline(data, path, friend_number, True) else: - self.send_file(data[0], friend_number, True) + self.send_file(path, friend_number, True) friend.clear_unsent_files() for key in list(self._paused_file_transfers.keys()): - data = self._paused_file_transfers[key] - if not os.path.exists(data[0]): + (path, ft_friend_number, is_incoming, start_position) = self._paused_file_transfers[key] + if not os.path.exists(path): del self._paused_file_transfers[key] - elif data[1] == friend_number and not data[2]: - self.send_file(data[0], friend_number, True, key) + elif ft_friend_number == friend_number and not is_incoming: + self.send_file(path, friend_number, True, key) del self._paused_file_transfers[key] except Exception as ex: print('Exception in file sending: ' + str(ex)) diff --git a/toxygen/file_transfers/file_transfers_messages_service.py b/toxygen/file_transfers/file_transfers_messages_service.py index 06b1e14..7f8b5f6 100644 --- a/toxygen/file_transfers/file_transfers_messages_service.py +++ b/toxygen/file_transfers/file_transfers_messages_service.py @@ -45,6 +45,16 @@ class FileTransfersMessagesService: if count + index + 1 >= 0: self._create_inline_item(transfer.data, count + index + 1) + def add_unsent_file_message(self, friend, file_path, data): + tm = UnsentFileMessage(file_path, data, util.get_unix_time()) + friend.append_message(tm) + + if self._is_active(friend.number): + self._create_unsent_file_item(tm) + self._messages.scrollToBottom() + + return tm + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- @@ -60,3 +70,6 @@ class FileTransfersMessagesService: def _create_inline_item(self, data, position): return self._messages_items_factory.create_inline_item(data, False, position) + + def _create_unsent_file_item(self, tm): + return self._messages_items_factory.create_unsent_file_item(tm) diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 9a56aba..69b5b39 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -173,17 +173,30 @@ class TransferMessage(Message): return FileTransferItem(self, *args) -class UnsentFile(Message): +class UnsentFileMessage(Message): def __init__(self, path, data, time): super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], 0, time) self._data, self._path = data, path + def get_data(self): + return self._data + + data = property(get_data) + + def get_path(self): + return self._path + + path = property(get_path) + def get_status(self): return None + def _create_widget(self, *args): + return UnsentFileItem(self, *args) -class InlineImage(Message): + +class InlineImageMessage(Message): """ Inline image """ diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index 84bf9c3..6197066 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -57,8 +57,8 @@ class MessagesItemsFactory: return item - def create_unsent_file_item(self, tm, append=True): - item = UnsentFileItem(self._file_transfers_handler, self._settings, tm, self._messages.width()) + def create_unsent_file_item(self, message, append=True): + item = message.get_widget(self._file_transfers_handler, self._settings, self._messages.width(), self._messages) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) if append: @@ -69,8 +69,8 @@ class MessagesItemsFactory: return item - def create_file_transfer_item(self, tm, append=True): - item = tm.get_widget(self._file_transfers_handler, self._settings, self._messages.width(), self._messages) + def create_file_transfer_item(self, message, append=True): + item = message.get_widget(self._file_transfers_handler, self._settings, self._messages.width(), self._messages) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) if append: diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 67b09a7..c87a675 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -308,7 +308,8 @@ class MainWindow(QtWidgets.QMainWindow): def load(pos): if not pos: - self._history_loader.load_history() + friend = self._contacts_manager.get_curr_friend() + self._history_loader.load_history(friend) self.messages.verticalScrollBar().setValue(1) self.messages.verticalScrollBar().valueChanged.connect(load) self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index ad337dd..9b5f1cb 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -390,8 +390,8 @@ class FileTransferItem(QtWidgets.QListWidget): class UnsentFileItem(FileTransferItem): - def __init__(self, file_transfer_handler, settings, transfer_message, width, parent=None): - super().__init__(file_transfer_handler, settings, transfer_message, width, parent) + def __init__(self, transfer_message, file_transfer_handler, settings, width, parent=None): + super().__init__(transfer_message, file_transfer_handler, settings, width, parent) self._time = time self.pb.setVisible(False) movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) From ec5bcbddec63ace9f5a735cf24b9c3002c71828b Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 May 2018 13:23:48 +0300 Subject: [PATCH 042/217] calls manager fixes --- toxygen/app.py | 4 +-- toxygen/av/calls_manager.py | 57 +++++++++++++++++++--------------- toxygen/contacts/profile.py | 31 ++---------------- toxygen/messenger/messages.py | 4 +-- toxygen/messenger/messenger.py | 45 ++++++++++++++++++++++++--- 5 files changed, 79 insertions(+), 62 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index d20eea7..a4c466d 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -296,7 +296,6 @@ class App: self._smiley_loader = SmileyLoader(self._settings) self._tox_dns = ToxDns(self._settings) self._ms = MainWindow(self._settings, self._tray) - self._calls_manager = CallsManager(self._tox.AV, self._settings) db = Database(self._path.replace('.tox', '.db'), self._toxes) friend_items_factory = FriendItemsFactory(self._settings, self._ms) @@ -312,8 +311,9 @@ class App: self._contacts_provider, history, self._tox_dns, messages_items_factory) history.set_contacts_manager(self._contacts_manager) + self._calls_manager = CallsManager(self._tox.AV, self._settings, self._ms, self._contacts_manager) self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, - self._contacts_provider, messages_items_factory, profile) + self._contacts_provider, messages_items_factory, profile, self._calls_manager) file_transfers_message_service = FileTransfersMessagesService(self._contacts_manager, messages_items_factory, profile, self._ms) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider, diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py index 6e290e2..26a06f4 100644 --- a/toxygen/av/calls_manager.py +++ b/toxygen/av/calls_manager.py @@ -1,19 +1,37 @@ import threading import cv2 import av.calls -import utils.ui as util_ui from messenger.messages import * import time from ui import av_widgets +import common.event as event class CallsManager: - def __init__(self, toxAV, settings): + def __init__(self, toxAV, settings, screen, contacts_manager): self._call = av.calls.AV(toxAV, settings) # object with data about calls self._call_widgets = {} # dict of incoming call widgets self._incoming_calls = set() self._settings = settings + self._screen = screen + self._contacts_manager = contacts_manager + self._call_started_event = event.Event() # friend_number, audio, video, is_outgoing + self._call_finished_event = event.Event() # friend_number, is_declined + + # ----------------------------------------------------------------------------------------------------------------- + # Events + # ----------------------------------------------------------------------------------------------------------------- + + def get_call_started_event(self): + return self._call_started_event + + call_started_event = property(get_call_started_event) + + def get_call_finished_event(self): + return self._call_finished_event + + call_finished_event = property(get_call_finished_event) # ----------------------------------------------------------------------------------------------------------------- # AV support @@ -26,21 +44,17 @@ class CallsManager: def call_click(self, audio=True, video=False): """User clicked audio button in main window""" - num = self.get_active_number() - if not self.is_active_a_friend(): + num = self._contacts_manager.get_active_number() + if not self._contacts_manager.is_active_a_friend(): return - if num not in self._call and self.is_active_online(): # start call + if num not in self._call and self._contacts_manager.is_active_online(): # start call if not self._settings.audio['enabled']: return self._call(num, audio, video) self._screen.active_call() - if video: - text = util_ui.tr("Outgoing video call") - else: - text = util_ui.tr("Outgoing audio call") - self.get_curr_friend().append_message(InfoMessage(text, time.time())) - self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() + self._call_started_event(num, audio, video, True) + self._contacts_manager.get_curr_friend().append_message(InfoMessage(text, time.time())) + self._screen.messages.scrollToBottom() elif num in self._call: # finish or cancel call if you call with active friend self.stop_call(num, False) @@ -50,17 +64,12 @@ class CallsManager: """ if not self._settings.audio['enabled']: return - friend = self.get_friend_by_number(friend_number) - if video: - text = util_ui.tr("Incoming video call") - else: - text = util_ui.tr("Incoming audio call") + friend = self._contacts_manager.get_friend_by_number(friend_number) + self._call_started_event(friend_number, audio, video, False) friend.append_message(InfoMessage(text, time.time())) self._incoming_calls.add(friend_number) - if friend_number == self.get_active_number(): + if friend_number == self._contacts_manager.get_active_number(): self._screen.incoming_call() - self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() else: friend.actions = True self._call_widgets[friend_number] = av_widgets.IncomingCallWidget(friend_number, text, friend.name) @@ -83,8 +92,10 @@ class CallsManager: """ if friend_number in self._incoming_calls: self._incoming_calls.remove(friend_number) + is_declined = True text = util_ui.tr("Call declined") else: + is_declined = False text = util_ui.tr("Call finished") self._screen.call_finished() is_video = self._call.is_video_call(friend_number) @@ -98,11 +109,7 @@ class CallsManager: cv2.destroyWindow(str(friend_number)) threading.Timer(2.0, destroy_window).start() - friend = self.get_friend_by_number(friend_number) - friend.append_message(InfoMessage(text, time.time())) - if friend_number == self.get_active_number(): - self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() + self._call_finished_event(friend_number, is_declined) def friend_exit(self, friend_number): if friend_number in self._call: diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 6393f7e..c775268 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -1,8 +1,4 @@ -from contacts.friend import * -from file_transfers.file_transfers import * -import time from contacts import basecontact -import utils.ui as util_ui import random import threading @@ -28,7 +24,7 @@ class Profile(basecontact.BaseContact): self._contacts_provider = contacts_provider self._reset_action = reset_action self._waiting_for_reconnection = False - self._timer = threading.Timer(50, self._reconnect) + self._timer = None # ----------------------------------------------------------------------------------------------------------------- # Edit current user's data @@ -47,21 +43,14 @@ class Profile(basecontact.BaseContact): self._tox.self_set_status(status) elif not self._waiting_for_reconnection: self._waiting_for_reconnection = True + self._timer = threading.Timer(50, self._reconnect) self._timer.start() def set_name(self, value): if self.name == value: return - tmp = self.name super().set_name(value.encode('utf-8')) self._tox.self_set_name(self._name.encode('utf-8')) - message = util_ui.tr('User {} is now known as {}') - message = message.format(tmp, value) - for friend in self._contacts: - friend.append_message(InfoMessage(message, time.time())) - if self._active_friend + 1: - self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() def set_status_message(self, value): super().set_status_message(value) @@ -74,15 +63,6 @@ class Profile(basecontact.BaseContact): return self._tox_id - # ----------------------------------------------------------------------------------------------------------------- - # Private messages - # ----------------------------------------------------------------------------------------------------------------- - - def receipt(self): - i = 0 - while i < self._messages.count() and not self._messages.itemWidget(self._messages.item(i)).mark_as_sent(): - i += 1 - # ----------------------------------------------------------------------------------------------------------------- # Reset # ----------------------------------------------------------------------------------------------------------------- @@ -101,10 +81,5 @@ class Profile(basecontact.BaseContact): if self.status is None or all(list(map(lambda x: x.status is None, contacts))) and len(contacts): self._waiting_for_reconnection = True self._restart() + self._timer = threading.Timer(50, self._reconnect) self._timer.start() - - def close(self): - for friend in filter(lambda x: type(x) is Friend, self._contacts): - self.friend_exit(friend.number) - for i in range(len(self._contacts)): - del self._contacts[0] diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 69b5b39..62f8804 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -216,5 +216,5 @@ class InlineImageMessage(Message): class InfoMessage(TextMessage): - def __init__(self, id, message, time): - super().__init__(id, message, None, time, MESSAGE_TYPE['INFO_MESSAGE']) + def __init__(self, message, time): + super().__init__(message, None, time, MESSAGE_TYPE['INFO_MESSAGE']) diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 968ca97..8f3feb2 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -1,14 +1,11 @@ -import utils.util as util import common.tox_save as tox_save -from wrapper.toxcore_enums_and_consts import * from messenger.messages import * -# TODO: sub profile name changed event? - class Messenger(tox_save.ToxSave): - def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider, items_factory, profile): + def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider, items_factory, profile, + calls_manager): super().__init__(tox) self._plugin_loader = plugin_loader self._screen = screen @@ -16,6 +13,11 @@ class Messenger(tox_save.ToxSave): self._contacts_provider = contacts_provider self._items_factory = items_factory self._profile = profile + self._profile_name = profile.name + + profile.name_changed_event.add_callback(self._on_profile_name_changed) + calls_manager.call_started_event.add_callback(self._on_call_started) + calls_manager.call_finished_event.add_callback(self._on_call_finished) # ----------------------------------------------------------------------------------------------------------------- # Private methods @@ -160,3 +162,36 @@ class Messenger(tox_save.ToxSave): def _get_friend_by_number(self, friend_number): return self._contacts_provider.get_friend_by_number(friend_number) + + def _on_profile_name_changed(self, new_name): + if self._profile_name == new_name: + return + message = util_ui.tr('User {} is now known as {}') + message = message.format(self._profile_name, new_name) + for friend in self._contacts_provider.get_all_friends(): + friend.append_message(InfoMessage(message, util.get_unix_time())) + if self._contacts_manager.is_active_a_friend(): + self._items_factory.create_message_item(message) + self._screen.messages.scrollToBottom() + self._profile_name = new_name + + def _on_call_started(self, friend_number, audio, video, is_outgoing): + if is_outgoing: + text = util_ui.tr("Outgoing video call") if video else util_ui.tr("Outgoing audio call") + else: + text = util_ui.tr("Incoming video call") if video else util_ui.tr("Incoming audio call") + friend = self._get_friend_by_number(friend_number) + message = InfoMessage(text, util.get_unix_time()) + friend.append_message(message) + if self._contacts_manager.is_friend_active(friend_number): + self._items_factory.create_message_item(message) + self._screen.messages.scrollToBottom() + + def _on_call_finished(self, friend_number, is_declined): + text = util_ui.tr("Call declined") if is_declined else util_ui.tr("Call finished") + friend = self._get_friend_by_number(friend_number) + message = InfoMessage(text, util.get_unix_time()) + friend.append_message(message) + if self._contacts_manager.is_friend_active(friend_number): + self._items_factory.create_message_item(message) + self._screen.messages.scrollToBottom() From 42049d6a44928b926e3b24951adddab569b767ea Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 May 2018 18:40:41 +0300 Subject: [PATCH 043/217] messaing fixes - receipts, faux offline messages --- toxygen/contacts/contact.py | 7 ++++--- toxygen/contacts/friend.py | 17 ----------------- toxygen/messenger/messages.py | 7 ++++++- toxygen/messenger/messenger.py | 29 ++++++++++++++--------------- toxygen/styles/dark_style.qss | 8 ++++---- toxygen/ui/messages_widgets.py | 2 -- 6 files changed, 28 insertions(+), 42 deletions(-) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 7faf36d..fb0267c 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -118,7 +118,7 @@ class Contact(basecontact.BaseContact): """ :return list of unsent messages """ - messages = filter(lambda x: x.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) + messages = filter(lambda m: m.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(messages) def get_unsent_messages_for_saving(self): @@ -129,9 +129,10 @@ class Contact(basecontact.BaseContact): and x.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(map(lambda x: x.get_data(), messages)) - def mark_as_sent(self): + def mark_as_sent(self, tox_message_id): try: - message = list(filter(lambda x: x.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr))[0] + message = list(filter(lambda m: m.author.type == MESSAGE_AUTHOR['NOT_SENT'] + and m.tox_message_id == tox_message_id, self._corr))[0] message.mark_as_sent() except Exception as ex: util.log('Mark as sent ex: ' + str(ex)) diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 63edd6a..0bc7b99 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -52,23 +52,6 @@ class Friend(contact.Contact): self._corr = list(filter(lambda m: not (type(m) is UnsentFileMessage and m.message_id == message_id), self._corr)) - # ----------------------------------------------------------------------------------------------------------------- - # History support - # ----------------------------------------------------------------------------------------------------------------- - - def get_receipts(self): - return self._receipts - - receipts = property(get_receipts) # read receipts - - def inc_receipts(self): - self._receipts += 1 - - def dec_receipt(self): - if self._receipts: - self._receipts -= 1 - self.mark_as_sent() - # ----------------------------------------------------------------------------------------------------------------- # Full status # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 62f8804..85b16f7 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -64,6 +64,8 @@ class Message: def mark_as_sent(self): self._author.author_type = MESSAGE_AUTHOR['ME'] + if self._widget is not None: + self._widget.mark_as_sent() def _create_widget(self, *args): pass @@ -111,7 +113,10 @@ class OutgoingTextMessage(TextMessage): def get_tox_message_id(self): return self._tox_message_id - tox_message_id = property(get_tox_message_id) + def set_tox_message_id(self, tox_message_id): + self._tox_message_id = tox_message_id + + tox_message_id = property(get_tox_message_id, set_tox_message_id) class GroupChatMessage(TextMessage): diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 8f3feb2..6042db5 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -98,10 +98,8 @@ class Messenger(tox_save.ToxSave): messages = friend.get_unsent_messages() try: for message in messages: - tox_messages = self._split_message(message.text) - for tox_message in tox_messages: - self._tox.friend_send_message(friend_number, message.message_type, tox_message) - friend.inc_receipts() + message_id = self._tox.friend_send_message(friend_number, message.type, message.text.encode('utf-8')) + message.tox_message_id = message_id except Exception as ex: util.log('Sending pending messages failed with ' + str(ex)) @@ -110,7 +108,8 @@ class Messenger(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def receipt(self, friend_number, message_id): - pass # TODO: process + friend = self._get_friend_by_number(friend_number) + friend.mark_as_sent(message_id) # ----------------------------------------------------------------------------------------------------------------- # Typing notifications @@ -171,8 +170,7 @@ class Messenger(tox_save.ToxSave): for friend in self._contacts_provider.get_all_friends(): friend.append_message(InfoMessage(message, util.get_unix_time())) if self._contacts_manager.is_active_a_friend(): - self._items_factory.create_message_item(message) - self._screen.messages.scrollToBottom() + self._create_info_message_item(message) self._profile_name = new_name def _on_call_started(self, friend_number, audio, video, is_outgoing): @@ -180,18 +178,19 @@ class Messenger(tox_save.ToxSave): text = util_ui.tr("Outgoing video call") if video else util_ui.tr("Outgoing audio call") else: text = util_ui.tr("Incoming video call") if video else util_ui.tr("Incoming audio call") - friend = self._get_friend_by_number(friend_number) - message = InfoMessage(text, util.get_unix_time()) - friend.append_message(message) - if self._contacts_manager.is_friend_active(friend_number): - self._items_factory.create_message_item(message) - self._screen.messages.scrollToBottom() + self._add_info_message(friend_number, text) def _on_call_finished(self, friend_number, is_declined): text = util_ui.tr("Call declined") if is_declined else util_ui.tr("Call finished") + self._add_info_message(friend_number, text) + + def _add_info_message(self, friend_number, text): friend = self._get_friend_by_number(friend_number) message = InfoMessage(text, util.get_unix_time()) friend.append_message(message) if self._contacts_manager.is_friend_active(friend_number): - self._items_factory.create_message_item(message) - self._screen.messages.scrollToBottom() + self._create_info_message_item(message) + + def _create_info_message_item(self, message): + self._items_factory.create_message_item(message) + self._screen.messages.scrollToBottom() diff --git a/toxygen/styles/dark_style.qss b/toxygen/styles/dark_style.qss index 0216f23..714f946 100644 --- a/toxygen/styles/dark_style.qss +++ b/toxygen/styles/dark_style.qss @@ -1207,12 +1207,12 @@ MessageItem border: none; } -MessageEdit +MessageBrowser { border: none; } -MessageEdit::focus +MessageBrowser::focus { border: none; } @@ -1222,7 +1222,7 @@ MessageItem::focus border: none; } -MessageEdit:hover +MessageBrowser:hover { border: none; } @@ -1243,7 +1243,7 @@ QPushButton:hover background-color: #1E90FF; } -MessageEdit +MessageBrowser { background-color: transparent; } diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index 9b5f1cb..f69cbfb 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -1,7 +1,5 @@ -from PyQt5 import QtWidgets, QtGui, QtCore from wrapper.toxcore_enums_and_consts import * import ui.widgets as widgets -import utils.ui as util_ui import utils.util as util import ui.menu as menu import html as h From a5753121678cdab1ba26c118420ca3b936fed770 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 May 2018 19:40:34 +0300 Subject: [PATCH 044/217] messages refactoring and fixes, calls fixes --- toxygen/av/calls_manager.py | 27 ++++++------- toxygen/contacts/contact.py | 39 +++++++++---------- toxygen/file_transfers/file_transfers.py | 3 +- .../file_transfers/file_transfers_handler.py | 6 +-- toxygen/messenger/messages.py | 8 ++-- toxygen/ui/av_widgets.py | 30 +++++++------- toxygen/ui/messages_widgets.py | 7 ++-- 7 files changed, 57 insertions(+), 63 deletions(-) diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py index 26a06f4..5e84573 100644 --- a/toxygen/av/calls_manager.py +++ b/toxygen/av/calls_manager.py @@ -2,7 +2,6 @@ import threading import cv2 import av.calls from messenger.messages import * -import time from ui import av_widgets import common.event as event @@ -37,11 +36,6 @@ class CallsManager: # AV support # ----------------------------------------------------------------------------------------------------------------- - def get_call(self): - return self._call - - call = property(get_call) - def call_click(self, audio=True, video=False): """User clicked audio button in main window""" num = self._contacts_manager.get_active_number() @@ -53,8 +47,6 @@ class CallsManager: self._call(num, audio, video) self._screen.active_call() self._call_started_event(num, audio, video, True) - self._contacts_manager.get_curr_friend().append_message(InfoMessage(text, time.time())) - self._screen.messages.scrollToBottom() elif num in self._call: # finish or cancel call if you call with active friend self.stop_call(num, False) @@ -66,13 +58,13 @@ class CallsManager: return friend = self._contacts_manager.get_friend_by_number(friend_number) self._call_started_event(friend_number, audio, video, False) - friend.append_message(InfoMessage(text, time.time())) self._incoming_calls.add(friend_number) if friend_number == self._contacts_manager.get_active_number(): self._screen.incoming_call() else: friend.actions = True - self._call_widgets[friend_number] = av_widgets.IncomingCallWidget(friend_number, text, friend.name) + text = util_ui.tr("Incoming video call") if video else util_ui.tr("Incoming audio call") + self._call_widgets[friend_number] = self._get_incoming_call_widget(friend_number, text, friend.name) self._call_widgets[friend_number].set_pixmap(friend.get_pixmap()) self._call_widgets[friend_number].show() @@ -93,16 +85,14 @@ class CallsManager: if friend_number in self._incoming_calls: self._incoming_calls.remove(friend_number) is_declined = True - text = util_ui.tr("Call declined") else: is_declined = False - text = util_ui.tr("Call finished") self._screen.call_finished() is_video = self._call.is_video_call(friend_number) self._call.finish_call(friend_number, by_friend) # finish or decline call - if hasattr(self, '_call_widget'): - self._call_widget[friend_number].close() - del self._call_widget[friend_number] + if friend_number in self._call_widgets: + self._call_widgets[friend_number].close() + del self._call_widgets[friend_number] def destroy_window(): if is_video: @@ -114,3 +104,10 @@ class CallsManager: def friend_exit(self, friend_number): if friend_number in self._call: self._call.finish_call(friend_number, True) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _get_incoming_call_widget(self, friend_number, text, friend_name): + return av_widgets.IncomingCallWidget(self._settings, self, friend_number, text, friend_name) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index fb0267c..a142c61 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -74,7 +74,7 @@ class Contact(basecontact.BaseContact): Get data to save in db :return: list of unsaved messages or [] """ - messages = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) + messages = list(filter(lambda x: x.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) return messages[-self._unsaved_messages:] if self._unsaved_messages else [] def get_corr(self): @@ -85,12 +85,12 @@ class Contact(basecontact.BaseContact): :param message: text or file transfer message """ self._corr.append(message) - if message.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): + if message.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): self._unsaved_messages += 1 def get_last_message_text(self): - messages = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) - and x.get_owner() != MESSAGE_AUTHOR['FRIEND'], self._corr)) + messages = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) + and m.author.type != MESSAGE_AUTHOR['FRIEND'], self._corr)) if messages: return messages[-1].text else: @@ -125,9 +125,9 @@ class Contact(basecontact.BaseContact): """ :return list of unsent messages for saving """ - messages = filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) + messages = filter(lambda x: x.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) and x.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) - return list(map(lambda x: x.get_data(), messages)) + return list(messages) def mark_as_sent(self, tox_message_id): try: @@ -144,7 +144,7 @@ class Contact(basecontact.BaseContact): def delete_message(self, message_id): elem = list(filter(lambda x: type(x) in (TextMessage, GroupChatMessage) and x.message_id == message_id, self._corr))[0] - tmp = list(filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) + tmp = list(filter(lambda x: x.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) if elem in tmp[-self._unsaved_messages:] and self._unsaved_messages: self._unsaved_messages -= 1 self._corr.remove(elem) @@ -156,13 +156,13 @@ class Contact(basecontact.BaseContact): Delete old messages (reduces RAM usage if messages saving is not enabled) """ def save_message(x): - if x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None): # FIXME MAGIC NUMBERS + if x.type == MESSAGE_TYPE['FILE_TRANSFER'] and (x.state not in ACTIVE_FILE_TRANSFERS): return True - return x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'] + return x.author.type == MESSAGE_AUTHOR['NOT_SENT'] old = filter(save_message, self._corr[:-SAVE_MESSAGES]) self._corr = list(old) + self._corr[-SAVE_MESSAGES:] - text_messages = filter(lambda x: x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr) + text_messages = filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr) self._unsaved_messages = min(self._unsaved_messages, len(list(text_messages))) self._search_index = 0 @@ -175,13 +175,14 @@ class Contact(basecontact.BaseContact): self._search_index = 0 # don't delete data about active file transfer if not save_unsent: - self._corr = list(filter(lambda x: x.get_type() == 2 and - x.get_status() in ft.ACTIVE_FILE_TRANSFERS, self._corr)) + self._corr = list(filter(lambda m: m.type == MESSAGE_TYPE['FILE_TRANSFER'] and + m.state in ft.ACTIVE_FILE_TRANSFERS, self._corr)) self._unsaved_messages = 0 else: - self._corr = list(filter(lambda x: (x.get_type() == 2 and x.get_status() in ft.ACTIVE_FILE_TRANSFERS) - or (x.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) - and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT']), + self._corr = list(filter(lambda m: (m.type == MESSAGE_TYPE['FILE_TRANSFER'] + and m.state in ft.ACTIVE_FILE_TRANSFERS) + or (m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) + and m.author.type == MESSAGE_AUTHOR['NOT_SENT']), self._corr)) self._unsaved_messages = len(self.get_unsent_messages()) @@ -197,7 +198,7 @@ class Contact(basecontact.BaseContact): while True: l = len(self._corr) for i in range(self._search_index - 1, -l - 1, -1): - if self._corr[i].get_type() > 1: + if self._corr[i].type not in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): continue message = self._corr[i].text if re.search(self._search_string, message, re.IGNORECASE) is not None: @@ -212,7 +213,7 @@ class Contact(basecontact.BaseContact): if not self._search_index: return None for i in range(self._search_index + 1, 0): - if self._corr[i].get_type() > 1: + if self._corr[i].get_type() > (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): continue message = self._corr[i].text if re.search(self._search_string, message, re.IGNORECASE) is not None: @@ -259,10 +260,6 @@ class Contact(basecontact.BaseContact): visibility = property(get_visibility, set_visibility) - def set_widget(self, widget): - self._widget = widget - self.init_widget() - # ----------------------------------------------------------------------------------------------------------------- # Unread messages and other actions from friend # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 166cc7f..61a379f 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -13,7 +13,8 @@ FILE_TRANSFER_STATE = { 'FINISHED': 3, 'PAUSED_BY_FRIEND': 4, 'INCOMING_NOT_STARTED': 5, - 'OUTGOING_NOT_STARTED': 6 + 'OUTGOING_NOT_STARTED': 6, + 'UNSENT': 7 } ACTIVE_FILE_TRANSFERS = (0, 1, 4, 5, 6) diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index c46ee58..1f5c9d0 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -41,15 +41,15 @@ class FileTransfersHandler: file_id = self._tox.file_get_file_id(friend_number, file_number) accepted = True if file_id in self._paused_file_transfers: - data = self._paused_file_transfers[file_id] - pos = data[-1] if os.path.exists(data[0]) else 0 + (path, ft_friend_number, is_incoming, start_position) = self._paused_file_transfers[file_id] + pos = start_position if os.path.exists(path) else 0 if pos >= size: self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) return self._tox.file_seek(friend_number, file_number, pos) self._file_transfers_message_service.add_incoming_transfer_message( friend, accepted, size, file_name, file_number) - self.accept_transfer(data[0], friend_number, file_number, size, False, pos) + self.accept_transfer(path, friend_number, file_number, size, False, pos) elif inline and size < 1024 * 1024: self._file_transfers_message_service.add_incoming_transfer_message( friend, accepted, size, file_name, file_number) diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 85b16f7..c356485 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -189,14 +189,16 @@ class UnsentFileMessage(Message): data = property(get_data) + def get_state(self): + return FILE_TRANSFER_STATE['UNSENT'] + + state = property(get_state) + def get_path(self): return self._path path = property(get_path) - def get_status(self): - return None - def _create_widget(self, *args): return UnsentFileItem(self, *args) diff --git a/toxygen/ui/av_widgets.py b/toxygen/ui/av_widgets.py index f9eb6ed..e5773a8 100644 --- a/toxygen/ui/av_widgets.py +++ b/toxygen/ui/av_widgets.py @@ -1,17 +1,16 @@ from PyQt5 import QtCore, QtGui, QtWidgets from ui import widgets -from contacts import profile -import utils +import utils.util as util import pyaudio import wave -from user_data import settings -from utils.util import * class IncomingCallWidget(widgets.CenteredWidget): - def __init__(self, friend_number, text, name): + def __init__(self, settings, calls_manager, friend_number, text, name): super().__init__() + self._settings = settings + self._calls_manager = calls_manager self.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowStaysOnTopHint) self.resize(QtCore.QSize(500, 270)) self.avatar_label = QtWidgets.QLabel(self) @@ -21,7 +20,7 @@ class IncomingCallWidget(widgets.CenteredWidget): self.name.setGeometry(QtCore.QRect(90, 20, 300, 25)) self._friend_number = friend_number font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) + font.setFamily(settings['font']) font.setPointSize(16) font.setBold(True) self.name.setFont(font) @@ -34,13 +33,13 @@ class IncomingCallWidget(widgets.CenteredWidget): self.accept_video.setGeometry(QtCore.QRect(170, 100, 150, 150)) self.decline = QtWidgets.QPushButton(self) self.decline.setGeometry(QtCore.QRect(320, 100, 150, 150)) - pixmap = QtGui.QPixmap(utils.curr_directory() + '/images/accept_audio.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'accept_audio.png')) icon = QtGui.QIcon(pixmap) self.accept_audio.setIcon(icon) - pixmap = QtGui.QPixmap(utils.curr_directory() + '/images/accept_video.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'accept_video.png')) icon = QtGui.QIcon(pixmap) self.accept_video.setIcon(icon) - pixmap = QtGui.QPixmap(utils.curr_directory() + '/images/decline_call.png') + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'decline_call.png')) icon = QtGui.QIcon(pixmap) self.decline.setIcon(icon) self.accept_audio.setIconSize(QtCore.QSize(150, 150)) @@ -90,11 +89,11 @@ class IncomingCallWidget(widgets.CenteredWidget): self.stream.close() self.p.terminate() - self.a = AudioFile(curr_directory() + '/sounds/call.wav') + self.a = AudioFile(util.join_path(util.get_sounds_directory(), 'call.wav')) self.a.play() self.a.close() - if settings.Settings.get_instance()['calls_sound']: + if self._settings['calls_sound']: self.thread = SoundPlay() self.thread.start() else: @@ -110,24 +109,21 @@ class IncomingCallWidget(widgets.CenteredWidget): if self._processing: return self._processing = True - pr = profile.Profile.get_instance() - pr.accept_call(self._friend_number, True, False) + self._calls_manager.accept_call(self._friend_number, True, False) self.stop() def accept_call_with_video(self): if self._processing: return self._processing = True - pr = profile.Profile.get_instance() - pr.accept_call(self._friend_number, True, True) + self._calls_manager.accept_call(self._friend_number, True, True) self.stop() def decline_call(self): if self._processing: return self._processing = True - pr = profile.Profile.get_instance() - pr.stop_call(self._friend_number, False) + self._calls_manager.stop_call(self._friend_number, False) self.stop() def set_pixmap(self, pixmap): diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index f69cbfb..c676783 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -134,8 +134,9 @@ class MessageItem(QtWidgets.QWidget): font.setFamily(settings['font']) font.setPointSize(11) font.setBold(True) - self.name.setFont(font) - self.name.setText(text_message.author.name) + if text_message.author is not None: + self.name.setFont(font) + self.name.setText(text_message.author.name) self.time = QtWidgets.QLabel(self) self.time.setGeometry(QtCore.QRect(parent.width() - 60, 0, 50, 25)) @@ -143,7 +144,7 @@ class MessageItem(QtWidgets.QWidget): font.setBold(False) self.time.setFont(font) self._time = text_message.time - if text_message.author.type == MESSAGE_AUTHOR['NOT_SENT']: + if text_message.author and text_message.author.type == MESSAGE_AUTHOR['NOT_SENT']: movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) self.time.setMovie(movie) movie.start() From 88786b03987fb6c320b365b89692889b00c8b73a Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 May 2018 21:07:59 +0300 Subject: [PATCH 045/217] setup.py fixes --- setup.py | 17 ++++++++++++++--- toxygen/contacts/friend.py | 6 +++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 8457ef5..fb80363 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,8 @@ from platform import system from subprocess import call import main import sys +import os +from utils.util import curr_directory, join_path version = main.__version__ + '.0' @@ -35,6 +37,15 @@ else: MODULES.append('pydenticon') +def get_packages(): + directory = join_path(curr_directory(__file__), 'toxygen') + for root, dirs, files in os.walk(directory): + packages = map(lambda d: 'toxygen.' + d, dirs) + packages = ['toxygen'] + list(packages) + + return packages + + class InstallScript(install): """This class configures Toxygen after installation""" @@ -66,7 +77,7 @@ setup(name='Toxygen', author='Ingvar', maintainer='Ingvar', license='GPL3', - packages=['toxygen', 'toxygen.plugins', 'toxygen.styles'], + packages=get_packages(), install_requires=MODULES, include_package_data=True, classifiers=[ @@ -75,8 +86,8 @@ setup(name='Toxygen', 'Programming Language :: Python :: 3.6', ], entry_points={ - 'console_scripts': ['toxygen=toxygen.main:main'], + 'console_scripts': ['toxygen=toxygen.main:main'] }, cmdclass={ - 'install': InstallScript, + 'install': InstallScript }) diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 0bc7b99..9103463 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -38,13 +38,13 @@ class Friend(contact.Contact): def clear_unsent_files(self): self._corr = list(filter(lambda x: type(x) is not UnsentFileMessage, self._corr)) - def remove_invalid_unsent_files(self): # TODO: fix + def remove_invalid_unsent_files(self): def is_valid(message): if type(message) is not UnsentFileMessage: return True - if message.get_data()[1] is not None: + if message.data is not None: return True - return os.path.exists(message.get_data()[0]) + return os.path.exists(message.path) self._corr = list(filter(is_valid, self._corr)) From acf75a68188a29a81f67a3d7239bd032cf16bcb0 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 00:07:49 +0300 Subject: [PATCH 046/217] groups initial commit --- toxygen/app.py | 5 ++++- toxygen/contacts/contact_provider.py | 30 ++++++++++++++++++-------- toxygen/contacts/contacts_manager.py | 5 +++++ toxygen/contacts/group_chat.py | 4 ++-- toxygen/contacts/group_factory.py | 6 ++++++ toxygen/groups/__init__.py | 0 toxygen/groups/groups_service.py | 32 ++++++++++++++++++++++++++++ toxygen/ui/main_screen.py | 16 +++++++++----- 8 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 toxygen/contacts/group_factory.py create mode 100644 toxygen/groups/__init__.py create mode 100644 toxygen/groups/groups_service.py diff --git a/toxygen/app.py b/toxygen/app.py index a4c466d..311ebe9 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -19,6 +19,7 @@ from contacts.profile import Profile from file_transfers.file_transfers_handler import FileTransfersHandler from contacts.contact_provider import ContactProvider from contacts.friend_factory import FriendFactory +from contacts.group_factory import GroupFactory from contacts.contacts_manager import ContactsManager from av.calls_manager import CallsManager from history.database import Database @@ -40,6 +41,7 @@ class App: self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = self._tox_dns = None + self._group_factory = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] self._path = path_to_profile @@ -300,7 +302,8 @@ class App: friend_items_factory = FriendItemsFactory(self._settings, self._ms) self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, friend_items_factory) - self._contacts_provider = ContactProvider(self._tox, self._friend_factory) + self._group_factory = GroupFactory() + self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory) profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) history = None diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py index 1a08589..6d4e6b8 100644 --- a/toxygen/contacts/contact_provider.py +++ b/toxygen/contacts/contact_provider.py @@ -3,9 +3,10 @@ import common.tox_save as tox_save class ContactProvider(tox_save.ToxSave): - def __init__(self, tox, friend_factory): + def __init__(self, tox, friend_factory, group_factory): super().__init__(tox) self._friend_factory = friend_factory + self._group_factory = group_factory self._cache = {} # key - contact's public key, value - contact instance # ----------------------------------------------------------------------------------------------------------------- @@ -37,13 +38,24 @@ class ContactProvider(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def get_all_groups(self): - return [] + group_numbers = range(self._tox.group_get_number_groups) + groups = map(lambda n: self.get_group_by_number(n), group_numbers) - def get_group_by_number(self): - pass + return list(groups) - def get_group_by_public_key(self): - pass + def get_group_by_number(self, group_number): + public_key = self._tox.group_get_chat_id(group_number) + + return self.get_group_by_public_key(public_key) + + def get_group_by_public_key(self, public_key): + group = self._get_contact_from_cache(public_key) + if group is not None: + return group + group = self._group_factory.create_group_by_public_key(public_key) + self._add_to_cache(public_key, group) + + return group # ----------------------------------------------------------------------------------------------------------------- # All contacts @@ -59,9 +71,9 @@ class ContactProvider(tox_save.ToxSave): def clear_cache(self): self._cache.clear() - def remove_friend_from_cache(self, friend_public_key): - if friend_public_key in self._cache: - del self._cache[friend_public_key] + def remove_contact_from_cache(self, contact_public_key): + if contact_public_key in self._cache: + del self._cache[contact_public_key] # ----------------------------------------------------------------------------------------------------------------- # Private methods diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index b826a37..efca671 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -298,6 +298,11 @@ class ContactsManager: self._contacts.append(friend) friend.reset_avatar() + def add_group(self, group_number): + group = self._contact_provider.get_group_by_numner(group_number) + self._contacts.append(group) + group.reset_avatar() + def block_user(self, tox_id): """ Block user with specified tox id (or public key) - delete from friends list and ignore friend requests diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 5121be7..c069ca4 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -24,8 +24,8 @@ class GroupChat(contact.Contact): super().set_name(title) @staticmethod - def get_default_avatar_name(): - return 'group.png' + def _get_default_avatar_path(): + return util.join_path(util.get_images_directory(), 'group.png') def remove_invalid_unsent_files(self): pass diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py new file mode 100644 index 0000000..953c483 --- /dev/null +++ b/toxygen/contacts/group_factory.py @@ -0,0 +1,6 @@ + + +class GroupFactory: + + def create_group_by_public_key(self, public_key): + pass diff --git a/toxygen/groups/__init__.py b/toxygen/groups/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py new file mode 100644 index 0000000..febdb9e --- /dev/null +++ b/toxygen/groups/groups_service.py @@ -0,0 +1,32 @@ +import common.tox_save as tox_save + + +class GroupsService(tox_save.ToxSave): + + def __init__(self, tox, contacts_manager, contacts_provider): + super().__init__(tox) + self._contacts_manager = contacts_manager + self._contacts_provider = contacts_provider + + # ----------------------------------------------------------------------------------------------------------------- + # Groups creation + # ----------------------------------------------------------------------------------------------------------------- + + def create_new_gc(self, name, privacy_state): + group_number = self._tox.group_new(privacy_state, name) + self._add_new_group_by_number(group_number) + + def join_gc_by_id(self, chat_id, password): + group_number = self._tox.group_join(chat_id, password) + self._add_new_group_by_number(group_number) + + def join_gc_via_invite(self, invite_data, friend_number, password): + group_number = self._tox.group_invite_accept(invite_data, friend_number, password) + self._add_new_group_by_number(group_number) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _add_new_group_by_number(self, group_number): + self._contacts_manager.add_group(group_number) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index c87a675..85c2d41 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -1,4 +1,3 @@ -from contacts.profile import * from ui.contact_items import * from ui.widgets import MultilineEdit, ComboBox from ui.main_screen_widgets import * @@ -50,6 +49,7 @@ class MainWindow(QtWidgets.QMainWindow): self.menuProfile = QtWidgets.QMenu(self.menubar) self.menuProfile.setObjectName("menuProfile") + self.menuGC = QtWidgets.QMenu(self.menubar) self.menuSettings = QtWidgets.QMenu(self.menubar) self.menuSettings.setObjectName("menuSettings") self.menuPlugins = QtWidgets.QMenu(self.menubar) @@ -58,7 +58,6 @@ class MainWindow(QtWidgets.QMainWindow): self.menuAbout.setObjectName("menuAbout") self.actionAdd_friend = QtWidgets.QAction(window) - self.actionAdd_gc = QtWidgets.QAction(window) self.actionAdd_friend.setObjectName("actionAdd_friend") self.actionprofilesettings = QtWidgets.QAction(window) self.actionprofilesettings.setObjectName("actionprofilesettings") @@ -81,10 +80,14 @@ class MainWindow(QtWidgets.QMainWindow): self.importPlugin = QtWidgets.QAction(window) self.reloadPlugins = QtWidgets.QAction(window) self.lockApp = QtWidgets.QAction(window) + self.createGC = QtWidgets.QAction(window) + self.joinGC = QtWidgets.QAction(window) + self.menuProfile.addAction(self.actionAdd_friend) - self.menuProfile.addAction(self.actionAdd_gc) self.menuProfile.addAction(self.actionSettings) self.menuProfile.addAction(self.lockApp) + self.menuGC.addAction(self.createGC) + self.menuGC.addAction(self.joinGC) self.menuSettings.addAction(self.actionPrivacy_settings) self.menuSettings.addAction(self.actionInterface_settings) self.menuSettings.addAction(self.actionNotifications) @@ -98,6 +101,7 @@ class MainWindow(QtWidgets.QMainWindow): self.menuAbout.addAction(self.actionAbout_program) self.menubar.addAction(self.menuProfile.menuAction()) + self.menubar.addAction(self.menuGC.menuAction()) self.menubar.addAction(self.menuSettings.menuAction()) self.menubar.addAction(self.menuPlugins.menuAction()) self.menubar.addAction(self.menuAbout.menuAction()) @@ -105,7 +109,7 @@ class MainWindow(QtWidgets.QMainWindow): self.actionAbout_program.triggered.connect(self.about_program) self.actionNetwork.triggered.connect(self.network_settings) self.actionAdd_friend.triggered.connect(self.add_contact_triggered) - self.actionAdd_gc.triggered.connect(self.create_gc) + self.createGC.triggered.connect(self.create_gc) self.actionSettings.triggered.connect(self.profile_settings) self.actionPrivacy_settings.triggered.connect(self.privacy_settings) self.actionInterface_settings.triggered.connect(self.interface_settings) @@ -130,12 +134,14 @@ class MainWindow(QtWidgets.QMainWindow): def retranslateUi(self): self.lockApp.setText(util_ui.tr("Lock")) self.menuPlugins.setTitle(util_ui.tr("Plugins")) + self.menuGC.setTitle(util_ui.tr("Group chats")) self.pluginData.setText(util_ui.tr("List of plugins")) self.menuProfile.setTitle(util_ui.tr("Profile")) self.menuSettings.setTitle(util_ui.tr("Settings")) self.menuAbout.setTitle(util_ui.tr("About")) self.actionAdd_friend.setText(util_ui.tr("Add contact")) - self.actionAdd_gc.setText(util_ui.tr("Create group chat")) + self.createGC.setText(util_ui.tr("Create group chat")) + self.joinGC.setText(util_ui.tr("Join group chat")) self.actionprofilesettings.setText(util_ui.tr("Profile")) self.actionPrivacy_settings.setText(util_ui.tr("Privacy")) self.actionInterface_settings.setText(util_ui.tr("Interface")) From dfe7601dc102a7bec7b7f730238765f1181b06ef Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 16:00:28 +0300 Subject: [PATCH 047/217] groups - service, chat, callbacks --- toxygen/app.py | 8 ++- toxygen/contacts/contact_provider.py | 2 +- toxygen/contacts/contacts_manager.py | 8 ++- toxygen/contacts/group_chat.py | 36 ++++++----- toxygen/contacts/group_peer_contact.py | 13 ++++ toxygen/groups/group_peer.py | 10 +++ toxygen/messenger/messages.py | 2 +- toxygen/messenger/messenger.py | 41 ++++++++++++- toxygen/middleware/callbacks.py | 66 +++++++++++++++----- toxygen/ui/create_profile_screen.py | 6 +- toxygen/ui/groups_widgets.py | 72 ++++++++++++++++++++++ toxygen/ui/main_screen.py | 8 ++- toxygen/ui/views/create_group_screen.ui | 81 +++++++++++++++++++++++++ toxygen/ui/views/join_group_screen.ui | 81 +++++++++++++++++++++++++ toxygen/ui/widgets_factory.py | 10 ++- 15 files changed, 400 insertions(+), 44 deletions(-) create mode 100644 toxygen/contacts/group_peer_contact.py create mode 100644 toxygen/groups/group_peer.py create mode 100644 toxygen/ui/groups_widgets.py create mode 100644 toxygen/ui/views/create_group_screen.ui create mode 100644 toxygen/ui/views/join_group_screen.ui diff --git a/toxygen/app.py b/toxygen/app.py index 311ebe9..b76a047 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -30,6 +30,7 @@ from messenger.messenger import Messenger from network.tox_dns import ToxDns from history.history import History from file_transfers.file_transfers_messages_service import FileTransfersMessagesService +from groups.groups_service import GroupsService import styles.style # TODO: dynamic loading @@ -41,7 +42,7 @@ class App: self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = self._tox_dns = None - self._group_factory = None + self._group_factory = self._groups_service = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] self._path = path_to_profile @@ -322,9 +323,10 @@ class App: self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider, file_transfers_message_service, profile) messages_items_factory.set_file_transfers_handler(self._file_transfer_handler) + self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider) widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, - self._toxes, self._version) + self._toxes, self._version, self._groups_service) self._tray = tray.init_tray(profile, self._settings, self._ms) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, self._plugin_loader, self._file_transfer_handler, history, self._calls_manager) @@ -335,7 +337,7 @@ class App: # callbacks initialization callbacks.init_callbacks(self._tox, profile, self._settings, self._plugin_loader, self._contacts_manager, self._calls_manager, self._file_transfer_handler, self._ms, self._tray, - self._messenger) + self._messenger, self._groups_service, self._contacts_provider) def _try_to_update(self): updating = updater.start_update_if_needed(self._version, self._settings) diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py index 6d4e6b8..6f13596 100644 --- a/toxygen/contacts/contact_provider.py +++ b/toxygen/contacts/contact_provider.py @@ -38,7 +38,7 @@ class ContactProvider(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def get_all_groups(self): - group_numbers = range(self._tox.group_get_number_groups) + group_numbers = range(self._tox.group_get_number_groups()) groups = map(lambda n: self.get_group_by_number(n), group_numbers) return list(groups) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index efca671..7dad02f 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -43,6 +43,12 @@ class ContactsManager: return self.get_curr_contact().number == friend_number + def is_group_active(self, group_number): + if self.is_active_a_friend(): + return False + + return self.get_curr_contact().number == friend_number + # ----------------------------------------------------------------------------------------------------------------- # Work with active friend # ----------------------------------------------------------------------------------------------------------------- @@ -299,7 +305,7 @@ class ContactsManager: friend.reset_avatar() def add_group(self, group_number): - group = self._contact_provider.get_group_by_numner(group_number) + group = self._contact_provider.get_group_by_number(group_number) self._contacts.append(group) group.reset_avatar() diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index c069ca4..d37bbe7 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -1,31 +1,21 @@ from contacts import contact import utils.util as util -from PyQt5 import QtGui, QtCore +from wrapper.toxcore_enums_and_consts import * from wrapper import toxcore_enums_and_consts as constants -# TODO: ngc - class GroupChat(contact.Contact): def __init__(self, profile_manager, name, status_message, widget, tox, group_number): super().__init__(None, group_number, profile_manager, name, status_message, widget, None) self._tox = tox self.set_status(constants.TOX_USER_STATUS['NONE']) + self._peers = [] + self._add_self_to_gc() - def set_name(self, name): - self._tox.group_set_title(self._number, name) - super().set_name(name) - - def send_message(self, message): - self._tox.group_message_send(self._number, message.encode('utf-8')) - - def new_title(self, title): - super().set_name(title) - - @staticmethod - def _get_default_avatar_path(): - return util.join_path(util.get_images_directory(), 'group.png') + def set_topic(self, topic): + self._tox.group_set_topic(self._number, topic.encode('utf-8')) + super().set_status_message(topic) def remove_invalid_unsent_files(self): pass @@ -45,3 +35,17 @@ class GroupChat(contact.Contact): def get_peer_name(self, peer_number): return self._tox.group_peername(self._number, peer_number) + + def get_self_name(self): + return self._peers[0].name + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def _get_default_avatar_path(): + return util.join_path(util.get_images_directory(), 'group.png') + + def _add_self_to_gc(self): + pass diff --git a/toxygen/contacts/group_peer_contact.py b/toxygen/contacts/group_peer_contact.py new file mode 100644 index 0000000..1c51f67 --- /dev/null +++ b/toxygen/contacts/group_peer_contact.py @@ -0,0 +1,13 @@ +import contacts.contact + + +class GroupPeerContact(contacts.contact.Contact): + + def __init__(self, profile_manager, message_getter, peer_number, name, status_messsage, widget, tox_id, group_pk): + super().__init__(profile_manager, message_getter, peer_number, name, status_messsage, widget, tox_id) + self._group_pk = group_pk + + def get_group_pk(self): + return self._group_pk + + group_pk = property(get_group_pk) diff --git a/toxygen/groups/group_peer.py b/toxygen/groups/group_peer.py new file mode 100644 index 0000000..f91decc --- /dev/null +++ b/toxygen/groups/group_peer.py @@ -0,0 +1,10 @@ + + +class GroupChatPeer: + + def __init__(self, peer_number, name, status, role, public_key): + self.peer_number = peer_number + self.name = name + self.status = status + self.role = role + self.public_key = public_key diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index c356485..91a1a04 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -106,7 +106,7 @@ class TextMessage(Message): class OutgoingTextMessage(TextMessage): - def __init__(self, message, owner, time, message_type, tox_message_id): + def __init__(self, message, owner, time, message_type, tox_message_id=0): super().__init__(message, owner, time, message_type) self._tox_message_id = tox_message_id diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 6042db5..0a321f8 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -28,7 +28,7 @@ class Messenger(tox_save.ToxSave): self._items_factory.create_message_item(text_message) # ----------------------------------------------------------------------------------------------------------------- - # Messaging + # Messaging - friends # ----------------------------------------------------------------------------------------------------------------- def new_message(self, friend_number, message_type, message): @@ -53,7 +53,11 @@ class Messenger(tox_save.ToxSave): self._contacts_manager.update_filtration() def send_message(self): - self.send_message_to_friend(self._screen.messageEdit.toPlainText()) + text = self._screen.messageEdit.toPlainText() + if self._contacts_manager.is_active_a_friend(): + self.send_message_to_friend(text) + else: + self.send_message_to_group(text) def send_message_to_friend(self, text, friend_number=None): """ @@ -78,7 +82,6 @@ class Messenger(tox_save.ToxSave): for message in messages: if friend.status is not None: message_id = self._tox.friend_send_message(friend_number, message_type, message) - friend.inc_receipts() else: message_id = 0 message_author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['NOT_SENT']) @@ -103,6 +106,35 @@ class Messenger(tox_save.ToxSave): except Exception as ex: util.log('Sending pending messages failed with ' + str(ex)) + # ----------------------------------------------------------------------------------------------------------------- + # Messaging - groups + # ----------------------------------------------------------------------------------------------------------------- + + def send_message_to_group(self, text, group_number=None): + if group_number is None: + group_number = self._contacts_manager.get_active_number() + if text.startswith('/plugin '): + self._plugin_loader.command(text[8:]) + self._screen.messageEdit.clear() + elif text and group_number >= 0: + if text.startswith('/me '): + message_type = TOX_MESSAGE_TYPE['ACTION'] + text = text[4:] + else: + message_type = TOX_MESSAGE_TYPE['NORMAL'] + group = self._get_group_by_number(group_number) + messages = self._split_message(text.encode('utf-8')) + t = util.get_unix_time() + for message in messages: + self._tox.group_send_message(group_number, message_type, message) + message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER']) + message = OutgoingTextMessage(text, message_author, t, message_type) + group.append_message(message) + if self._contacts_manager.is_group_active(group_number): + self._create_message_item(message) + self._screen.messageEdit.clear() + self._screen.messages.scrollToBottom() + # ----------------------------------------------------------------------------------------------------------------- # Message receipts # ----------------------------------------------------------------------------------------------------------------- @@ -162,6 +194,9 @@ class Messenger(tox_save.ToxSave): def _get_friend_by_number(self, friend_number): return self._contacts_provider.get_friend_by_number(friend_number) + def _get_group_by_number(self, group_number): + return self._contacts_provider.get_group_by_number(group_number) + def _on_profile_name_changed(self, new_name): if self._profile_name == new_name: return diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index bd122d6..40b3284 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -13,7 +13,7 @@ from notifications.tray import tray_notification from notifications.sound import * import threading -# TODO: gc callbacks and refactoring. Use contact provider instead of manager +# TODO: refactoring. Use contact provider instead of manager # ----------------------------------------------------------------------------------------------------------------- # Callbacks - current user @@ -360,18 +360,49 @@ def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, u # ----------------------------------------------------------------------------------------------------------------- -def show_gc_notification(window, tray, message, group_number, peer_number): - profile = Profile.get_instance() - settings = Settings.get_instance() - chat = profile.get_group_by_number(group_number) - peer_name = chat.get_peer_name(peer_number) - if not window.isActiveWindow() and (profile.name in message or settings['group_notifications']): - if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: - invoke_in_main_thread(tray_notification, chat.name + ' ' + peer_name, message, tray, window) - if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: - sound_notification(SOUND_NOTIFICATION['MESSAGE']) - icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) +def group_message(window, tray, tox, messenger, settings, profile): + """ + New message in group chat + """ + def wrapped(tox_link, group_number, peer_id, message_type, message, length, user_data): + message = str(message[:length], 'utf-8') + invoke_in_main_thread(messenger.new_group_message, group_number, message_type, message, peer_id) + if not window.isActiveWindow(): + bl = settings['notify_all_gc'] or profile.name in message + name = tox.group_peer_get_name(group_number, peer_id) + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and (not settings.locked) and bl: + invoke_in_main_thread(tray_notification, name, message, tray, window) + if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['MESSAGE']) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + return wrapped + + +def group_invite(groups_service): + def wrapped(tox, friend_number, invite_data, length, user_data): + invoke_in_main_thread(groups_service.process_group_invite, + friend_number, + bytes(invite_data[:length])) + + return wrapped + + +def group_self_join(contacts_provider): + def wrapped(tox, group_number, user_data): + group = contacts_provider.get_group_by_number(group_number) + invoke_in_main_thread(group.set_status, TOX_USER_STATUS['NONE']) + + return wrapped + + +def group_peer_join(contacts_provider): + def wrapped(tox, group_number, peer_id, user_data): + gc = contacts_provider.get_group_by_number(group_number) + gc.add_peer(peer_id) + + return wrapped + # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization @@ -379,7 +410,8 @@ def show_gc_notification(window, tray, message, group_number, peer_number): def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, - calls_manager, file_transfer_handler, main_window, tray, messenger): + calls_manager, file_transfer_handler, main_window, tray, messenger, groups_service, + contacts_provider): """ Initialization of all callbacks. :param tox: Tox instance @@ -425,3 +457,9 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, # custom packets tox.callback_friend_lossless_packet(lossless_packet(plugin_loader), 0) tox.callback_friend_lossy_packet(lossy_packet(plugin_loader), 0) + + # gc callbacks + tox.callback_group_message(group_message(main_window, tray, tox, messenger, settings, profile), 0) + tox.callback_group_invite(group_invite(groups_service), 0) + tox.callback_group_self_join(group_self_join(contacts_provider), 0) + tox.callback_group_peer_join(group_peer_join(contacts_provider), 0) diff --git a/toxygen/ui/create_profile_screen.py b/toxygen/ui/create_profile_screen.py index 40c9091..08b85cf 100644 --- a/toxygen/ui/create_profile_screen.py +++ b/toxygen/ui/create_profile_screen.py @@ -26,9 +26,9 @@ class CreateProfileScreen(CenteredWidget, DialogWithResult): def __init__(self): CenteredWidget.__init__(self) DialogWithResult.__init__(self) - uic.loadUi(util.get_views_path('create_profile_screen')) + uic.loadUi(util.get_views_path('create_profile_screen'), self) self.center() - self.createProfile.clicked.connect(self.create_profile) + self.createProfile.clicked.connect(self._create_profile) def retranslateUi(self): self.defaultFolder.setPlaceholderText(util_ui.tr('Save in default folder')) @@ -36,7 +36,7 @@ class CreateProfileScreen(CenteredWidget, DialogWithResult): self.createProfile.setText(util_ui.tr('Create profile')) self.passwordLabel.setText(util_ui.tr('Password:')) - def create_profile(self): + def _create_profile(self): if self.password.text() != self.confirmPassword.text(): return # TODO : error result = CreateProfileScreenResult(self.defaultFolder.isChecked(), self.password.text()) diff --git a/toxygen/ui/groups_widgets.py b/toxygen/ui/groups_widgets.py new file mode 100644 index 0000000..4dc39d5 --- /dev/null +++ b/toxygen/ui/groups_widgets.py @@ -0,0 +1,72 @@ +from PyQt5 import uic +import utils.util as util +from ui.widgets import * +from wrapper.toxcore_enums_and_consts import * + + +class CreateGroupScreen(CenteredWidget): + + def __init__(self, groups_service): + super().__init__() + self._groups_service = groups_service + uic.loadUi(util.get_views_path('create_group_screen'), self) + self.center() + self.retranslateUi() + self.addGroupButton.clicked.connect(self._create_group) + self.groupNameLineEdit.textChanged.connect(self._group_name_changed) + + def retranslateUi(self): + self.setWindowTitle(util_ui.tr('Create new group chat')) + self.groupNameLabel.setText(util_ui.tr('Group name:')) + self.groupTypeLabel.setText(util_ui.tr('Group type:')) + self.groupNameLineEdit.setPlaceholderText(util_ui.tr('Group\'s persistent name')) + self.addGroupButton.setText(util_ui.tr('Create group')) + self.groupTypeComboBox.addItem(util_ui.tr('Public')) + self.groupTypeComboBox.addItem(util_ui.tr('Private')) + + def _create_group(self): + name = self.groupNameLineEdit.text() + privacy_state = self.groupTypeComboBox.currentIndex() + self._groups_service.create_new_gc(name, privacy_state) + self.close() + + def _group_name_changed(self): + name = self.groupNameLineEdit.text() + self.addGroupButton.setEnabled(bool(name.strip())) + + +class JoinGroupScreen(CenteredWidget): + + def __init__(self, groups_service): + super().__init__() + self._groups_service = groups_service + uic.loadUi(util.get_views_path('join_group_screen'), self) + self.center() + self.retranslateUi() + self.chatIdLineEdit.textChanged.connect(self._chat_id_changed) + self.joinGroupButton.clicked.connect(self._join_group) + + def retranslateUi(self): + self.setWindowTitle(util_ui.tr('Join public group chat')) + self.chatIdLabel.setText(util_ui.tr('Group ID:')) + self.passwordLabel.setText(util_ui.tr('Password:')) + self.chatIdLineEdit.setPlaceholderText(util_ui.tr('Group\'s chat ID')) + self.joinGroupButton.setText(util_ui.tr('Join group')) + self.passwordLineEdit.setPlaceholderText(util_ui.tr('Optional password')) + + def _chat_id_changed(self): + chat_id = self._get_chat_id() + self.joinGroupButton.setEnabled(len(chat_id) == TOX_GROUP_CHAT_ID_SIZE * 2) + + def _join_group(self): + chat_id = self._get_chat_id() + password = self.passwordLineEdit.text() + self._groups_service.join_gc_by_id(chat_id, password) + self.close() + + def _get_chat_id(self): + chat_id = self.chatIdLineEdit.text().strip() + if chat_id.startswith('tox:'): + chat_id = chat_id[4:] + + return chat_id diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 85c2d41..fd404b2 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -110,6 +110,7 @@ class MainWindow(QtWidgets.QMainWindow): self.actionNetwork.triggered.connect(self.network_settings) self.actionAdd_friend.triggered.connect(self.add_contact_triggered) self.createGC.triggered.connect(self.create_gc) + self.joinGC.triggered.connect(self.join_gc) self.actionSettings.triggered.connect(self.profile_settings) self.actionPrivacy_settings.triggered.connect(self.privacy_settings) self.actionInterface_settings.triggered.connect(self.interface_settings) @@ -459,7 +460,12 @@ class MainWindow(QtWidgets.QMainWindow): self._modal_window.show() def create_gc(self): - self.profile.create_group_chat() + self._modal_window = self._widget_factory.create_group_screen_window() + self._modal_window.show() + + def join_gc(self): + self._modal_window = self._widget_factory.create_join_group_screen_window() + self._modal_window.show() def profile_settings(self, _): self._modal_window = self._widget_factory.create_profile_settings_window() diff --git a/toxygen/ui/views/create_group_screen.ui b/toxygen/ui/views/create_group_screen.ui new file mode 100644 index 0000000..08a27ff --- /dev/null +++ b/toxygen/ui/views/create_group_screen.ui @@ -0,0 +1,81 @@ + + + Form + + + + 0 + 0 + 639 + 199 + + + + Form + + + + false + + + + 180 + 150 + 271 + 41 + + + + + + + + + + 140 + 40 + 471 + 31 + + + + + + + 140 + 100 + 471 + 41 + + + + + + + 10 + 40 + 121 + 31 + + + + TextLabel + + + + + + 10 + 100 + 121 + 31 + + + + TextLabel + + + + + + diff --git a/toxygen/ui/views/join_group_screen.ui b/toxygen/ui/views/join_group_screen.ui new file mode 100644 index 0000000..66b0420 --- /dev/null +++ b/toxygen/ui/views/join_group_screen.ui @@ -0,0 +1,81 @@ + + + Form + + + + 0 + 0 + 739 + 212 + + + + Form + + + + + 30 + 40 + 67 + 17 + + + + TextLabel + + + + + + 30 + 100 + 67 + 17 + + + + TextLabel + + + + + false + + + + 258 + 150 + 241 + 51 + + + + + + + + + + 190 + 20 + 431 + 41 + + + + + + + 190 + 90 + 431 + 41 + + + + + + + diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index b42e342..66aca2c 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -1,11 +1,12 @@ from ui.main_screen_widgets import * from ui.menu import * +from ui.groups_widgets import * class WidgetsFactory: def __init__(self, settings, profile, profile_manager, contacts_manager, file_transfer_handler, smiley_loader, - plugin_loader, toxes, version): + plugin_loader, toxes, version, groups_service): self._settings = settings self._profile = profile self._profile_manager = profile_manager @@ -15,6 +16,7 @@ class WidgetsFactory: self._plugin_loader = plugin_loader self._toxes = toxes self._version = version + self._groups_service = groups_service def create_screenshot_window(self, *args): return ScreenShotWindow(self._file_transfer_handler, self._contacts_manager, *args) @@ -63,3 +65,9 @@ class WidgetsFactory: def create_sticker_window(self): return StickerWindow(self._file_transfer_handler, self._contacts_manager) + + def create_group_screen_window(self): + return CreateGroupScreen(self._groups_service) + + def create_join_group_screen_window(self): + return JoinGroupScreen(self._groups_service) From eed31bf61be6f6d630e33aaaaf82319c3ff36c13 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 16:07:16 +0300 Subject: [PATCH 048/217] wrapper update - ngc --- toxygen/wrapper/tox.py | 144 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 10 deletions(-) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index c6f5912..18c2439 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1,4 +1,6 @@ -from ctypes import * +# -*- coding: utf-8 -*- +from ctypes import c_char_p, Structure, c_bool, byref, c_int, c_size_t, POINTER, c_uint16, c_void_p, c_uint64 +from ctypes import create_string_buffer, ArgumentError, CFUNCTYPE, c_uint32, sizeof, c_uint8 from wrapper.toxcore_enums_and_consts import * from wrapper.toxav import ToxAV from wrapper.libtox import LibToxCore @@ -20,6 +22,14 @@ class ToxOptions(Structure): ] +class GroupChatSelfPeerInfo(Structure): + _fields_ = [ + ('nick', c_char_p), + ('nick_length', c_uint16), + ('user_status', c_int) + ] + + def string_to_bin(tox_id): return c_char_p(bytes.fromhex(tox_id)) if tox_id is not None else None @@ -30,9 +40,8 @@ def bin_to_string(raw_id, length): class Tox: - libtoxcore = LibToxCore() - + def __init__(self, tox_options=None, tox_pointer=None): """ Creates and initialises a new Tox instance with the options passed. @@ -90,11 +99,22 @@ class Tox: self.file_recv_chunk_cb = None self.friend_lossy_packet_cb = None self.friend_lossless_packet_cb = None - self.group_namelist_change_cb = None - self.group_title_cb = None - self.group_action_cb = None - self.group_message_cb = None + self.group_moderation_cb = None + self.group_join_fail_cb = None + self.group_self_join_cb = None self.group_invite_cb = None + self.group_custom_packet_cb = None + self.group_private_message_cb = None + self.group_private_message_cb = None + self.group_message_cb = None + self.group_password_cb = None + self.group_peer_limit_cb = None + self.group_privacy_state_cb = None + self.group_topic_cb = None + self.group_peer_status_cb = None + self.group_peer_name_cb = None + self.group_peer_exit_cb = None + self.group_peer_join_cb = None self.AV = ToxAV(self._tox_pointer) @@ -1514,51 +1534,63 @@ class Tox: elif tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']: raise RuntimeError('The instance was not bound to any port.') -# ----------------------------------------------------------------------------------------------------------------- + # ----------------------------------------------------------------------------------------------------------------- # Group chat instance management # ----------------------------------------------------------------------------------------------------------------- def group_new(self, privacy_state, group_name): """ Creates a new group chat. + This function creates a new group chat object and adds it to the chats array. + The client should initiate its peer list with self info after calling this function, as the peer_join callback will not be triggered. + :param privacy_state: The privacy state of the group. If this is set to TOX_GROUP_PRIVACY_STATE_PUBLIC, the group will attempt to announce itself to the DHT and anyone with the Chat ID may join. Otherwise a friend invite will be required to join the group. :param group_name: The name of the group. The name must be non-NULL. + :return group number on success, UINT32_MAX on failure. """ error = c_int() + peer_info = self.group_chat_self_peer_info_new() result = Tox.libtoxcore.tox_group_new(self._tox_pointer, privacy_state, group_name, - len(group_name), byref(error)) + len(group_name), peer_info, byref(error)) return result def group_join(self, chat_id, password): """ Joins a group chat with specified Chat ID. + This function creates a new group chat object, adds it to the chats array, and sends a DHT announcement to find peers in the group associated with chat_id. Once a peer has been found a join attempt will be initiated. + :param chat_id: The Chat ID of the group you wish to join. This must be TOX_GROUP_CHAT_ID_SIZE bytes. :param password: The password required to join the group. Set to NULL if no password is required. + :return groupnumber on success, UINT32_MAX on failure. """ error = c_int() + peer_info = self.group_chat_self_peer_info_new() result = Tox.libtoxcore.tox_group_join(self._tox_pointer, string_to_bin(chat_id), password, len(password) if password is not None else 0, + peer_info, byref(error)) return result def group_reconnect(self, groupnumber): """ Reconnects to a group. + This function disconnects from all peers in the group, then attempts to reconnect with the group. The caller's state is not changed (i.e. name, status, role, chat public key etc.) + :param groupnumber: The group number of the group we wish to reconnect to. :return True on success. """ @@ -1570,12 +1602,15 @@ class Tox: def group_leave(self, groupnumber, message): """ Leaves a group. + This function sends a parting packet containing a custom (non-obligatory) message to all peers in a group, and deletes the group from the chat array. All group state information is permanently lost, including keys and role credentials. + :param groupnumber: The group number of the group we wish to leave. :param message: The parting message to be sent to all the peers. Set to NULL if we do not wish to send a parting message. + :return True if the group chat instance was successfully deleted. """ @@ -1593,9 +1628,12 @@ class Tox: def group_self_set_name(self, groupnumber, name): """ Set the client's nickname for the group instance designated by the given group number. + Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length is equal to zero or name is a NULL pointer, the function call will fail. + :param name: A byte array containing the new nickname. + :return True on success. """ @@ -1607,6 +1645,7 @@ class Tox: """ Return the length of the client's current nickname for the group instance designated by groupnumber as passed to tox_group_self_set_name. + If no nickname was set before calling this function, the name is empty, and this function returns 0. """ @@ -1618,8 +1657,10 @@ class Tox: def group_self_get_name(self, groupnumber): """ Write the nickname set by tox_group_self_set_name to a byte array. + If no nickname was set before calling this function, the name is empty, and this function has no effect. + Call tox_group_self_get_name_size to find out how much memory to allocate for the result. :return nickname """ @@ -1674,10 +1715,13 @@ class Tox: def group_self_get_public_key(self, groupnumber): """ Write the client's group public key designated by the given group number to a byte array. + This key will be permanently tied to the client's identity for this particular group until the client explicitly leaves the group or gets kicked/banned. This key is the only way for other peers to reliably identify the client across client restarts. + `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. + :return public key """ @@ -1695,6 +1739,7 @@ class Tox: """ Return the length of the peer's name. If the group number or ID is invalid, the return value is unspecified. + The return value is equal to the `length` argument received by the last `group_peer_name` callback. """ @@ -1707,11 +1752,15 @@ class Tox: """ Write the name of the peer designated by the given ID to a byte array. + Call tox_group_peer_get_name_size to determine the allocation size for the `name` parameter. + The data written to `name` is equal to the data received by the last `group_peer_name` callback. + :param groupnumber: The group number of the group we wish to query. :param peer_id: The ID of the peer whose name we want to retrieve. + :return name. """ error = c_int() @@ -1724,6 +1773,7 @@ class Tox: """ Return the peer's user status (away/busy/...). If the ID or group number is invalid, the return value is unspecified. + The status returned is equal to the last status received through the `group_peer_status` callback. """ @@ -1736,6 +1786,7 @@ class Tox: """ Return the peer's role (user/moderator/founder...). If the ID or group number is invalid, the return value is unspecified. + The role returned is equal to the last role received through the `group_moderation` callback. """ @@ -1747,9 +1798,12 @@ class Tox: def group_peer_get_public_key(self, groupnumber, peer_id): """ Write the group public key with the designated peer_id for the designated group number to public_key. + This key will be parmanently tied to a particular peer until they explicitly leave the group or get kicked/banned, and is the only way to reliably identify the same peer across client restarts. + `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. + :return public key """ @@ -1786,8 +1840,10 @@ class Tox: def group_set_topic(self, groupnumber, topic): """ Set the group topic and broadcast it to the rest of the group. + topic length cannot be longer than TOX_GROUP_MAX_TOPIC_LENGTH. If length is equal to zero or topic is set to NULL, the topic will be unset. + :return True on success. """ @@ -1799,6 +1855,7 @@ class Tox: """ Return the length of the group topic. If the group number is invalid, the return value is unspecified. + The return value is equal to the `length` argument received by the last `group_topic` callback. """ @@ -1813,6 +1870,7 @@ class Tox: Call tox_group_get_topic_size to determine the allocation size for the `topic` parameter. The data written to `topic` is equal to the data received by the last `group_topic` callback. + :return topic """ @@ -1870,8 +1928,10 @@ class Tox: """ Return the privacy state of the group designated by the given group number. If group number is invalid, the return value is unspecified. + The value returned is equal to the data received by the last `group_privacy_state` callback. + see the `Group chat founder controls` section for the respective set function. """ @@ -1883,8 +1943,10 @@ class Tox: """ Return the maximum number of peers allowed for the group designated by the given group number. If the group number is invalid, the return value is unspecified. + The value returned is equal to the data received by the last `group_peer_limit` callback. + see the `Group chat founder controls` section for the respective set function. """ @@ -1905,10 +1967,14 @@ class Tox: def group_get_password(self, groupnumber): """ Write the password for the group designated by the given group number to a byte array. + Call tox_group_get_password_size to determine the allocation size for the `password` parameter. + The data received is equal to the data received by the last `group_password` callback. + see the `Group chat founder controls` section for the respective set function. + :return password """ @@ -1966,13 +2032,17 @@ class Tox: def group_send_custom_packet(self, groupnumber, lossless, data): """ Send a custom packet to the group. + If lossless is true the packet will be lossless. Lossless packet behaviour is comparable to TCP (reliability, arrive in order) but with packets instead of a stream. + If lossless is false, the packet will be lossy. Lossy packets behave like UDP packets, meaning they might never reach the other side or might arrive more than once (if someone is messing with the connection) or might arrive in the wrong order. + Unless latency is an issue or message reliability is not important, it is recommended that you use lossless custom packets. + :param groupnumber: The group number of the group the message is intended for. :param lossless: True if the packet should be lossless. :param data A byte array containing the packet data. @@ -1987,14 +2057,18 @@ class Tox: def group_send_private_message(self, groupnumber, peer_id, message): """ Send a text chat message to the specified peer in the specified group. + This function creates a group private message packet and pushes it into the send queue. + The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages must be split by the client and sent as separate messages. Other clients can then reassemble the fragments. Messages may not be empty. + :param groupnumber: The group number of the group the message is intended for. :param peer_id: The ID of the peer the message is intended for. :param message: A non-NULL pointer to the first element of a byte array containing the message text. + :return True on success. """ @@ -2006,14 +2080,18 @@ class Tox: def group_send_message(self, groupnumber, type, message): """ Send a text chat message to the group. + This function creates a group message packet and pushes it into the send queue. + The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages must be split by the client and sent as separate messages. Other clients can then reassemble the fragments. Messages may not be empty. + :param groupnumber: The group number of the group the message is intended for. :param type: Message type (normal, action, ...). :param message: A non-NULL pointer to the first element of a byte array containing the message text. + :return True on success. """ @@ -2030,6 +2108,7 @@ class Tox: """ Set the callback for the `group_message` event. Pass NULL to unset. This event is triggered when the client receives a group message. + Callback: python function with params: tox Tox* instance groupnumber The group number of the group the message is intended for. @@ -2057,6 +2136,7 @@ class Tox: def callback_group_custom_packet(self, callback, user_data): """ Set the callback for the `group_custom_packet` event. Pass NULL to unset. + This event is triggered when the client receives a custom packet. """ @@ -2071,9 +2151,12 @@ class Tox: def group_invite_friend(self, groupnumber, friend_number): """ Invite a friend to a group. + This function creates an invite request packet and pushes it to the send queue. + :param groupnumber: The group number of the group the message is intended for. :param friend_number: The friend number of the friend the invite is intended for. + :return True on success. """ @@ -2081,10 +2164,19 @@ class Tox: result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, groupnumber, friend_number, byref(error)) return result + def group_chat_self_peer_info_new(self): + error = c_int() + f = Tox.libtoxcore.group_chat_self_peer_info_new + f.restype = POINTER(GroupChatSelfPeerInfo) + result = f(self._tox_pointer, byref(error)) + + return result + def group_invite_accept(self, invite_data, friend_number, password=None): """ Accept an invite to a group chat that the client previously received from a friend. The invite is only valid while the inviter is present in the group. + :param invite_data: The invite data received from the `group_invite` event. :param password: The password required to join the group. Set to NULL if no password is required. :return the groupnumber on success, UINT32_MAX on failure. @@ -2093,16 +2185,19 @@ class Tox: error = c_int() f = Tox.libtoxcore.tox_group_invite_accept f.restype = c_uint32 + peer_info = self.group_chat_self_peer_info_new() result = f(self._tox_pointer, friend_number, invite_data, len(invite_data), password, - len(password) if password is not None else 0, byref(error)) + len(password) if password is not None else 0, peer_info, byref(error)) print('Invite accept. Result:', result, 'Error:', error.value) return result def callback_group_invite(self, callback, user_data): """ Set the callback for the `group_invite` event. Pass NULL to unset. + This event is triggered when the client receives a group invite from a friend. The client must store invite_data which is used to join the group via tox_group_invite_accept. + Callback: python function with params: tox - Tox* friend_number The friend number of the contact who sent the invite. @@ -2118,6 +2213,7 @@ class Tox: def callback_group_peer_join(self, callback, user_data): """ Set the callback for the `group_peer_join` event. Pass NULL to unset. + This event is triggered when a peer other than self joins the group. Callback: python function with params: tox - Tox* @@ -2133,6 +2229,7 @@ class Tox: def callback_group_peer_exit(self, callback, user_data): """ Set the callback for the `group_peer_exit` event. Pass NULL to unset. + This event is triggered when a peer other than self exits the group. """ @@ -2143,6 +2240,7 @@ class Tox: def callback_group_self_join(self, callback, user_data): """ Set the callback for the `group_self_join` event. Pass NULL to unset. + This event is triggered when the client has successfully joined a group. Use this to initialize any group information the client may need. Callback: python fucntion with params: @@ -2158,6 +2256,7 @@ class Tox: def callback_group_join_fail(self, callback, user_data): """ Set the callback for the `group_join_fail` event. Pass NULL to unset. + This event is triggered when the client fails to join a group. """ @@ -2172,10 +2271,13 @@ class Tox: def group_founder_set_password(self, groupnumber, password): """ Set or unset the group password. + This function sets the groups password, creates a new group shared state including the change, and distributes it to the rest of the group. + :param groupnumber: The group number of the group for which we wish to set the password. :param password: The password we want to set. Set password to NULL to unset the password. + :return True on success. """ @@ -2187,12 +2289,16 @@ class Tox: def group_founder_set_privacy_state(self, groupnumber, privacy_state): """ Set the group privacy state. + This function sets the group's privacy state, creates a new group shared state including the change, and distributes it to the rest of the group. + If an attempt is made to set the privacy state to the same state that the group is already in, the function call will be successful and no action will be taken. + :param groupnumber: The group number of the group for which we wish to change the privacy state. :param privacy_state: The privacy state we wish to set the group to. + :return true on success. """ @@ -2204,10 +2310,13 @@ class Tox: def group_founder_set_peer_limit(self, groupnumber, max_peers): """ Set the group peer limit. + This function sets a limit for the number of peers who may be in the group, creates a new group shared state including the change, and distributes it to the rest of the group. + :param groupnumber: The group number of the group for which we wish to set the peer limit. :param max_peers: The maximum number of peers to allow in the group. + :return True on success. """ @@ -2223,9 +2332,11 @@ class Tox: def group_toggle_ignore(self, groupnumber, peer_id, ignore): """ Ignore or unignore a peer. + :param groupnumber: The group number of the group the in which you wish to ignore a peer. :param peer_id: The ID of the peer who shall be ignored or unignored. :param ignore: True to ignore the peer, false to unignore the peer. + :return True on success. """ @@ -2236,12 +2347,15 @@ class Tox: def group_mod_set_role(self, groupnumber, peer_id, role): """ Set a peer's role. + This function will first remove the peer's previous role and then assign them a new role. It will also send a packet to the rest of the group, requesting that they perform the role reassignment. Note: peers cannot be set to the founder role. + :param groupnumber: The group number of the group the in which you wish set the peer's role. :param peer_id: The ID of the peer whose role you wish to set. :param role: The role you wish to set the peer to. + :return True on success. """ @@ -2252,12 +2366,15 @@ class Tox: def group_mod_remove_peer(self, groupnumber, peer_id, set_ban): """ Kick/ban a peer. + This function will remove a peer from the caller's peer list and optionally add their IP address to the ban list. It will also send a packet to all group members requesting them to do the same. + :param groupnumber: The group number of the group the ban is intended for. :param peer_id: The ID of the peer who will be kicked and/or added to the ban list. :param set_ban: Set to true if a ban shall be set on the peer's IP address. + :return True on success. """ @@ -2269,10 +2386,13 @@ class Tox: def group_mod_remove_ban(self, groupnumber, ban_id): """ Removes a ban. + This function removes a ban entry from the ban list, and sends a packet to the rest of the group requesting that they do the same. + :param groupnumber: The group number of the group in which the ban is to be removed. :param ban_id: The ID of the ban entry that shall be removed. + :return True on success """ @@ -2283,6 +2403,7 @@ class Tox: def callback_group_moderation(self, callback, user_data): """ Set the callback for the `group_moderation` event. Pass NULL to unset. + This event is triggered when a moderator or founder executes a moderation event. """ @@ -2307,6 +2428,7 @@ class Tox: def group_ban_get_list(self, groupnumber): """ Copy a list of valid ban list ID's into an array. + Call tox_group_ban_get_list_size to determine the number of elements to allocate. return true on success. """ @@ -2331,7 +2453,9 @@ class Tox: """ Write the name of the ban entry designated by ban_id in the group designated by the given group number to a byte array. + Call tox_group_ban_get_name_size to find out how much memory to allocate for the result. + :return name """ From ef4a1b18fdbbe385d939e98c3561ca82c8df781c Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 18:08:25 +0300 Subject: [PATCH 049/217] ngc - invites, gc menu, callbacks etc --- toxygen/app.py | 12 +++-- toxygen/contacts/contact_menu.py | 53 ++++++++++++++------ toxygen/contacts/contacts_manager.py | 73 ++++++++++++++++++---------- toxygen/contacts/friend_factory.py | 2 +- toxygen/contacts/group_chat.py | 42 +++++++++------- toxygen/contacts/group_factory.py | 45 ++++++++++++++++- toxygen/groups/group_peer.py | 46 +++++++++++++++--- toxygen/groups/groups_service.py | 30 ++++++++++++ toxygen/middleware/callbacks.py | 24 ++++++++- toxygen/ui/items_factories.py | 4 +- toxygen/ui/main_screen.py | 9 ++-- toxygen/wrapper/tox.py | 4 +- 12 files changed, 262 insertions(+), 82 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index b76a047..a4fa37b 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -25,7 +25,7 @@ from av.calls_manager import CallsManager from history.database import Database from ui.widgets_factory import WidgetsFactory from smileys.smileys import SmileyLoader -from ui.items_factories import MessagesItemsFactory, FriendItemsFactory +from ui.items_factories import MessagesItemsFactory, ContactItemsFactory from messenger.messenger import Messenger from network.tox_dns import ToxDns from history.history import History @@ -301,9 +301,10 @@ class App: self._ms = MainWindow(self._settings, self._tray) db = Database(self._path.replace('.tox', '.db'), self._toxes) - friend_items_factory = FriendItemsFactory(self._settings, self._ms) - self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, friend_items_factory) - self._group_factory = GroupFactory() + contact_items_factory = ContactItemsFactory(self._settings, self._ms) + self._friend_factory = FriendFactory(self._profile_manager, self._settings, + self._tox, db, contact_items_factory) + self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory) profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) @@ -329,7 +330,8 @@ class App: self._toxes, self._version, self._groups_service) self._tray = tray.init_tray(profile, self._settings, self._ms) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, - self._plugin_loader, self._file_transfer_handler, history, self._calls_manager) + self._plugin_loader, self._file_transfer_handler, history, self._calls_manager, + self._groups_service) self._tray.show() self._ms.show() diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index c580a3f..4a0dcaa 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -81,16 +81,28 @@ class BaseContactMenuGenerator: def __init__(self, contact): self._contact = contact - def generate(self, plugin_loader, contacts_manager, main_screen, settings, number): + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): return ContactMenuBuilder().build() + def _generate_copy_menu_builder(self, main_screen): + copy_menu_builder = ContactMenuBuilder() + (copy_menu_builder + .with_name(util_ui.tr('Copy')) + .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name)) + .with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.status_message)) + .with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id)) + ) + + return copy_menu_builder + class FriendMenuGenerator(BaseContactMenuGenerator): - def generate(self, plugin_loader, contacts_manager, main_screen, settings, number): + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): history_menu_builder = self._generate_history_menu_builder(main_screen, number) copy_menu_builder = self._generate_copy_menu_builder(main_screen) plugins_menu_builder = self._generate_plugins_menu_builder(plugin_loader, number) + groups_menu_builder = self._generate_groups_menu(contacts_manager, groups_service) allowed = self._contact.tox_id in settings['auto_accept_from_friends'] auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') @@ -105,6 +117,7 @@ class FriendMenuGenerator(BaseContactMenuGenerator): .with_action(util_ui.tr('Block friend'), lambda: main_screen.block_friend(number)) .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) .with_optional_submenu(plugins_menu_builder) + .with_optional_submenu(groups_menu_builder) ).build() return menu @@ -125,17 +138,6 @@ class FriendMenuGenerator(BaseContactMenuGenerator): return history_menu_builder - def _generate_copy_menu_builder(self, main_screen): - copy_menu_builder = ContactMenuBuilder() - (copy_menu_builder - .with_name(util_ui.tr('Copy')) - .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name)) - .with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.status_message)) - .with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id)) - ) - - return copy_menu_builder - @staticmethod def _generate_plugins_menu_builder(plugin_loader, number): if plugin_loader is None: @@ -151,7 +153,30 @@ class FriendMenuGenerator(BaseContactMenuGenerator): return plugins_menu_builder - def _generate_groups_menu(self, contacts_manager): # TODO: fix + def _generate_groups_menu(self, contacts_manager, groups_service): chats = contacts_manager.get_group_chats() if not len(chats) or self._contact.status is None: return None + groups_menu_builder = ContactMenuBuilder() + (groups_menu_builder + .with_name(util_ui.tr('Invite to group')) + .with_actions([(g.name, lambda: groups_service.invite_friend(self._contact.number, g.number)) for g in chats]) + ) + + return groups_menu_builder + + +class GroupMenuGenerator(BaseContactMenuGenerator): + + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): + copy_menu_builder = self._generate_copy_menu_builder(main_screen) + + builder = ContactMenuBuilder() + menu = (builder + .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_submenu(copy_menu_builder) + .with_action(util_ui.tr('Leave group'), lambda: groups_service.leave_group(self._contact.number)) + .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) + ).build() + + return menu diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 7dad02f..6c19b26 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,4 +1,5 @@ from contacts.friend import Friend +from contacts.group_chat import GroupChat from messenger.messages import * @@ -47,7 +48,7 @@ class ContactsManager: if self.is_active_a_friend(): return False - return self.get_curr_contact().number == friend_number + return self.get_curr_contact().number == group_number # ----------------------------------------------------------------------------------------------------------------- # Work with active friend @@ -194,8 +195,11 @@ class ContactsManager: # Friend getters # ----------------------------------------------------------------------------------------------------------------- - def get_friend_by_number(self, num): - return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0] + def get_friend_by_number(self, number): + return list(filter(lambda x: x.number == number and type(x) is Friend, self._contacts))[0] + + def get_group_by_number(self, number): + return list(filter(lambda x: x.number == number and type(x) is GroupChat, self._contacts))[0] def get_last_message(self): if self._active_contact + 1: @@ -261,8 +265,6 @@ class ContactsManager: except: pass self._settings.save() - # if num == self.get_active_number() and self.is_active_a_friend(): - # self.update() def friend_public_key(self, num): return self._contacts[num].tox_id @@ -277,22 +279,9 @@ class ContactsManager: :param num: number of friend in list """ friend = self._contacts[num] - try: - index = list(map(lambda x: x[0], self._settings['friends_aliases'])).index(friend.tox_id) - del self._settings['friends_aliases'][index] - except: - pass - if friend.tox_id in self._settings['notes']: - del self._settings['notes'][friend.tox_id] - self._settings.save() - self._history.delete_history(friend) + self._cleanup_contact_data(friend) self._tox.friend_delete(friend.number) - del self._contacts[num] - self._screen.friends_list.takeItem(num) - if num == self._active_contact: # active friend was deleted - self.set_active(0 if len(self._contacts) else -1) - data = self._tox.get_savedata() - self._profile_manager.save_profile(data) + self._delete_contact(num) def add_friend(self, tox_id): """ @@ -302,12 +291,8 @@ class ContactsManager: self._history.add_friend_to_db(tox_id) friend = self._contact_provider.get_friend_by_public_key(tox_id) self._contacts.append(friend) - friend.reset_avatar() - - def add_group(self, group_number): - group = self._contact_provider.get_group_by_number(group_number) - self._contacts.append(group) - group.reset_avatar() + friend.reset_avatar(self._settings['identicons']) + self._save_profile() def block_user(self, tox_id): """ @@ -343,7 +328,19 @@ class ContactsManager: # ----------------------------------------------------------------------------------------------------------------- def get_group_chats(self): - return list(filter(lambda c: type(c) is not Friend, self._contacts)) # TODO: fix after gc implementation + return list(filter(lambda c: type(c) is GroupChat, self._contacts)) + + def add_group(self, group_number): + group = self._contact_provider.get_group_by_number(group_number) + self._contacts.append(group) + group.reset_avatar(self._settings['identicons']) + self._save_profile() + + def delete_group(self, group_number): + group = self.get_group_by_number(group_number) + self._cleanup_contact_data(group) + num = self._contacts.index(group) + self._delete_contact(num) # ----------------------------------------------------------------------------------------------------------------- # Friend requests @@ -454,3 +451,25 @@ class ContactsManager: pixmap = QtGui.QPixmap(avatar_path) self._screen.account_avatar.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) + + def _save_profile(self): + data = self._tox.get_savedata() + self._profile_manager.save_profile(data) + + def _cleanup_contact_data(self, contact): + try: + index = list(map(lambda x: x[0], self._settings['friends_aliases'])).index(contact.tox_id) + del self._settings['friends_aliases'][index] + except: + pass + if contact.tox_id in self._settings['notes']: + del self._settings['notes'][contact.tox_id] + self._settings.save() + self._history.delete_history(contact) + + def _delete_contact(self, num): + del self._contacts[num] + self._screen.friends_list.takeItem(num) + if num == self._active_contact: # active friend was deleted + self.set_active(0 if len(self._contacts) else -1) + self._save_profile() diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py index 9ba859f..a9b0477 100644 --- a/toxygen/contacts/friend_factory.py +++ b/toxygen/contacts/friend_factory.py @@ -39,4 +39,4 @@ class FriendFactory: Method-factory :return: new widget for friend instance """ - return self._items_factory.create_friend_item() + return self._items_factory.create_contact_item() diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index d37bbe7..79c2cfa 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -1,13 +1,14 @@ from contacts import contact +from contacts.contact_menu import GroupMenuGenerator import utils.util as util -from wrapper.toxcore_enums_and_consts import * +from groups.group_peer import GroupChatPeer from wrapper import toxcore_enums_and_consts as constants class GroupChat(contact.Contact): - def __init__(self, profile_manager, name, status_message, widget, tox, group_number): - super().__init__(None, group_number, profile_manager, name, status_message, widget, None) + def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id): + super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) self._tox = tox self.set_status(constants.TOX_USER_STATUS['NONE']) self._peers = [] @@ -20,25 +21,29 @@ class GroupChat(contact.Contact): def remove_invalid_unsent_files(self): pass - def get_names(self): - peers_count = self._tox.group_number_peers(self._number) - names = [] - for i in range(peers_count): - name = self._tox.group_peername(self._number, i) - names.append(name) - names = sorted(names, key=lambda n: n.lower()) - return names + def get_context_menu_generator(self): + return GroupMenuGenerator(self) - def get_full_status(self): - names = self.get_names() - return '\n'.join(names) - - def get_peer_name(self, peer_number): - return self._tox.group_peername(self._number, peer_number) + # ----------------------------------------------------------------------------------------------------------------- + # Peers methods + # ----------------------------------------------------------------------------------------------------------------- def get_self_name(self): return self._peers[0].name + def add_peer(self, peer_id): + peer = GroupChatPeer(peer_id, + self._tox.group_peer_get_name(self._number, peer_id), + self._tox.group_peer_get_status(self._number, peer_id), + self._tox.group_peer_get_role(self._number, peer_id), + self._tox.group_peer_get_public_key(self._number, peer_id)) + self._peers.append(peer) + + def get_peer(self, peer_id): + peers = list(filter(lambda p: p.id == peer_id, self._peers)) + + return peers[0] + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- @@ -48,4 +53,5 @@ class GroupChat(contact.Contact): return util.join_path(util.get_images_directory(), 'group.png') def _add_self_to_gc(self): - pass + peer_id = self._tox.group_self_get_peer_id(self._number) + self.add_peer(peer_id) diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py index 953c483..089fc09 100644 --- a/toxygen/contacts/group_factory.py +++ b/toxygen/contacts/group_factory.py @@ -1,6 +1,49 @@ +from contacts.group_chat import GroupChat class GroupFactory: + def __init__(self, profile_manager, settings, tox, db, items_factory): + self._profile_manager = profile_manager + self._settings, self._tox = settings, tox + self._db = db + self._items_factory = items_factory + def create_group_by_public_key(self, public_key): - pass + group_number = self._get_group_number_by_chat_id(public_key) + + return self.create_group_by_number(group_number) + + def create_group_by_number(self, group_number): + aliases = self._settings['friends_aliases'] + tox_id = self._tox.group_get_chat_id(group_number) + try: + alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] + except: + alias = '' + item = self._create_group_item() + name = alias or self._tox.group_get_name(group_number) or tox_id + status_message = self._tox.group_get_topic(group_number) + message_getter = self._db.messages_getter(tox_id) + group = GroupChat(self._tox, self._profile_manager, message_getter, group_number, name, status_message, + item, tox_id) + group.set_alias(alias) + + return group + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _create_group_item(self): + """ + Method-factory + :return: new widget for group instance + """ + return self._items_factory.create_contact_item() + + def _get_group_number_by_chat_id(self, chat_id): + for i in range(self._tox.group_get_number_groups()): + if self._tox.group_get_chat_id(i) == chat_id: + return i + return -1 diff --git a/toxygen/groups/group_peer.py b/toxygen/groups/group_peer.py index f91decc..6736185 100644 --- a/toxygen/groups/group_peer.py +++ b/toxygen/groups/group_peer.py @@ -2,9 +2,43 @@ class GroupChatPeer: - def __init__(self, peer_number, name, status, role, public_key): - self.peer_number = peer_number - self.name = name - self.status = status - self.role = role - self.public_key = public_key + def __init__(self, peer_id, name, status, role, public_key): + self._peer_id = peer_id + self._name = name + self._status = status + self._role = role + self._public_key = public_key + + def get_id(self): + return self._peer_id + + id = property(get_id) + + def get_name(self): + return self._name + + def set_name(self, name): + self._name = name + + name = property(get_name, set_name) + + def get_status(self): + return self._status + + def set_status(self, status): + self._status = status + + status = property(get_status, set_status) + + def get_role(self): + return self._role + + def set_role(self, role): + self._role = role + + role = property(get_role, set_role) + + def get_public_key(self): + return self._public_key + + public_key = property(get_public_key) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index febdb9e..beb0853 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -1,4 +1,5 @@ import common.tox_save as tox_save +import utils.ui as util_ui class GroupsService(tox_save.ToxSave): @@ -24,9 +25,38 @@ class GroupsService(tox_save.ToxSave): group_number = self._tox.group_invite_accept(invite_data, friend_number, password) self._add_new_group_by_number(group_number) + # ----------------------------------------------------------------------------------------------------------------- + # Groups reconnect and leaving + # ----------------------------------------------------------------------------------------------------------------- + + def leave_group(self, group_number): + group = self._get_group(group_number) + self._tox.group_leave(group_number) + self._contacts_manager.delete_group(group_number) + self._contacts_provider.remove_contact_from_cache(group.tox_id) + + # ----------------------------------------------------------------------------------------------------------------- + # Group invites + # ----------------------------------------------------------------------------------------------------------------- + + def invite_friend(self, friend_number, group_number): + self._tox.group_invite_friend(group_number, friend_number) + + def process_group_invite(self, friend_number, invite_data): + friend = self._get_friend(friend_number) + text = util_ui.tr('Friend {} invites you to group. Accept?') + if util_ui.question(text.format(friend.name), util_ui.tr('Group invite')): + self.join_gc_via_invite(invite_data, friend_number, None) + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- def _add_new_group_by_number(self, group_number): self._contacts_manager.add_group(group_number) + + def _get_group(self, group_number): + return self._contacts_provider.get_group_by_number(group_number) + + def _get_friend(self, friend_number): + return self._contacts_provider.get_friend_by_number(friend_number) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 40b3284..7b0872d 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -398,8 +398,26 @@ def group_self_join(contacts_provider): def group_peer_join(contacts_provider): def wrapped(tox, group_number, peer_id, user_data): - gc = contacts_provider.get_group_by_number(group_number) - gc.add_peer(peer_id) + group = contacts_provider.get_group_by_number(group_number) + group.add_peer(peer_id) + + return wrapped + + +def group_peer_name(contacts_provider): + def wrapped(tox, group_number, peer_id, name, length, user_data): + group = contacts_provider.get_group_by_number(group_number) + peer = group.get_peer(peer_id) + peer.name = str(name[:length]) + + return wrapped + + +def group_peer_status(contacts_provider): + def wrapped(tox, group_number, peer_id, peer_status, user_data): + group = contacts_provider.get_group_by_number(group_number) + peer = group.get_peer(peer_id) + peer.status = peer_status return wrapped @@ -463,3 +481,5 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_group_invite(group_invite(groups_service), 0) tox.callback_group_self_join(group_self_join(contacts_provider), 0) tox.callback_group_peer_join(group_peer_join(contacts_provider), 0) + tox.callback_group_peer_name(group_peer_name(contacts_provider), 0) + tox.callback_group_peer_status(group_peer_status(contacts_provider), 0) diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index 6197066..2ea5660 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -2,13 +2,13 @@ from ui.contact_items import * from ui.messages_widgets import * -class FriendItemsFactory: +class ContactItemsFactory: def __init__(self, settings, main_screen): self._settings = settings self._friends_list = main_screen.friends_list - def create_friend_item(self): + def create_contact_item(self): item = ContactItem(self._settings) elem = QtWidgets.QListWidgetItem(self._friends_list) elem.setSizeHint(QtCore.QSize(250, item.height())) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index fd404b2..9a4df47 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -18,11 +18,11 @@ class MainWindow(QtWidgets.QMainWindow): self.setAcceptDrops(True) self._saved = False self._profile = None - self._file_transfer_handler = self._history_loader = self._calls_manager = None + self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None self.initUI() def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader, - file_transfer_handler, history_loader, calls_manager): + file_transfer_handler, history_loader, calls_manager, groups_service): self._widget_factory = widget_factory self._tray = tray self._contacts_manager = contacts_manager @@ -31,6 +31,7 @@ class MainWindow(QtWidgets.QMainWindow): self._file_transfer_handler = file_transfer_handler self._history_loader = history_loader self._calls_manager = calls_manager + self._groups_service = groups_service self.messageEdit.set_messenger(messenger) def show(self): @@ -414,7 +415,6 @@ class MainWindow(QtWidgets.QMainWindow): self.account_name.setGeometry(QtCore.QRect(100, 15, self.width() - 560, 25)) self.account_status.setGeometry(QtCore.QRect(100, 35, self.width() - 560, 25)) self.messageEdit.setFocus() - #self.profile.update() def keyPressEvent(self, event): key, modifiers = event.key(), event.modifiers() @@ -598,7 +598,8 @@ class MainWindow(QtWidgets.QMainWindow): if contact is None or item is None: return generator = contact.get_context_menu_generator() - self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, self._settings, number) + self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, self._settings, number, + self._groups_service) parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) self.listMenu.move(parent_position + pos) self.listMenu.show() diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 18c2439..f455e30 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1599,7 +1599,7 @@ class Tox: result = Tox.libtoxcore.tox_group_reconnect(self._tox_pointer, groupnumber, byref(error)) return result - def group_leave(self, groupnumber, message): + def group_leave(self, groupnumber, message=''): """ Leaves a group. @@ -1887,7 +1887,7 @@ class Tox: """ error = c_int() result = Tox.libtoxcore.tox_group_get_name_size(self._tox_pointer, groupnumber, byref(error)) - return result + return int(result) def group_get_name(self, groupnumber): """ From a935d602f8c0a52194980ef36703209113098733 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 19:27:27 +0300 Subject: [PATCH 050/217] minimal working ngc version - sending messages, invites, char creation --- toxygen/bootstrap/nodes.json | 2 +- toxygen/contacts/contacts_manager.py | 3 +++ toxygen/contacts/group_chat.py | 2 +- toxygen/groups/groups_service.py | 10 +++++++- toxygen/messenger/messenger.py | 34 ++++++++++++++++++++-------- toxygen/middleware/callbacks.py | 9 ++++---- 6 files changed, 43 insertions(+), 17 deletions(-) diff --git a/toxygen/bootstrap/nodes.json b/toxygen/bootstrap/nodes.json index 003bbc0..619fe67 100644 --- a/toxygen/bootstrap/nodes.json +++ b/toxygen/bootstrap/nodes.json @@ -1 +1 @@ -{"last_scan":1516822981,"last_refresh":1516822982,"nodes":[{"ipv4":"node.tox.biribiri.org","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67","maintainer":"nurupo","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Welcome, stranger #7985. I'm up for 5d 14h 34m 34s, running since Jan 19 05:08:27 UTC. If I get outdated, please ping my maintainer at nurupo.contributions@gmail.com","last_ping":1516822981},{"ipv4":"nodes.tox.chat","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"6FC41E2BD381D37E9748FC0E0328CE086AF9598BECC8FEB7DDF2E440475F300E","maintainer":"Impyy","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Straps boots like no other","last_ping":1516822981},{"ipv4":"130.133.110.14","ipv6":"2001:6f8:1c3c:babe::14:1","port":33445,"tcp_ports":[33445],"public_key":"461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F","maintainer":"Manolis","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Spline tox bootstrap node","last_ping":1516822981},{"ipv4":"205.185.116.116","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702","maintainer":"Busindre","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"198.98.51.198","ipv6":"2605:6400:1:fed5:22:45af:ec10:f329","port":33445,"tcp_ports":[33445,3389],"public_key":"1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F","maintainer":"Busindre","location":"US","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"85.172.30.117","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832","maintainer":"ray65536","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Ray's Tox Node","last_ping":1516822981},{"ipv4":"194.249.212.109","ipv6":"2001:1470:fbfe::109","port":33445,"tcp_ports":[33445,3389],"public_key":"3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B","maintainer":"fluke571","location":"SI","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"185.25.116.107","ipv6":"2a00:7a60:0:746b::3","port":33445,"tcp_ports":[33445,3389],"public_key":"DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43","maintainer":"MAH69K","location":"UA","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Saluton! Mia Tox ID: B229B7BD68FC66C2716EAB8671A461906321C764782D7B3EDBB650A315F6C458EF744CE89F07. Scribu! ;)","last_ping":1516822981},{"ipv4":"5.189.176.217","ipv6":"2a02:c200:1:10:3:1:605:1337","port":5190,"tcp_ports":[3389,33445,5190],"public_key":"2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F","maintainer":"tastytea","location":"DE","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1516822981},{"ipv4":"217.182.143.254","ipv6":"2001:41d0:302:1000::e111","port":2306,"tcp_ports":[33445,2306,443],"public_key":"7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147","maintainer":"pucetox","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"by pucetox,\nipv4/ipv6 UDP:2306 TCP:21/80/443/2306/33445\nsync your nodes here tox.0x10k.com/bootstrapd-conf , \n for communication: 1D1C0B992DEB6D7F18561176F7F5E572BCC7F2BA5CFA7E9E437B9134122CE96D906A6119F9D2","last_ping":1516822981},{"ipv4":"104.223.122.15","ipv6":"2607:ff48:aa81:800::35eb:1","port":33445,"tcp_ports":[3389,33445],"public_key":"0FB96EEBFB1650DDB52E70CF773DDFCABE25A95CC3BB50FC251082E4B63EF82A","maintainer":"ru_maniac","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"built on: Tue Feb 21st 2017, 10:52:30 UTC+3\nplease note: running on TokTox Toxcore!\nmore info on the matter: goo.gl/Gz5KhK \u0026 goo.gl/i2TZJr\n\ntox id for queries and general info: EBD2A7B649ABB10ED9F47E5113F04000F39D46F087CEB62FCCE1069471FD6915256D197F2A97","last_ping":1516822981},{"ipv4":"tox.verdict.gg","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976","maintainer":"Deliran","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Praise The Sun!","last_ping":1516822981},{"ipv4":"d4rk4.ru","ipv6":"-","port":1813,"tcp_ports":[1813],"public_key":"53737F6D47FA6BD2808F378E339AF45BF86F39B64E79D6D491C53A1D522E7039","maintainer":"D4rk4","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"TOX ID: 35EDC07AEB18B163E07EE33F6CDDA63969F394FF6A617CEAB22A7EBBEAAAF854C0EDFBD46898","last_ping":1516822981},{"ipv4":"51.254.84.212","ipv6":"2001:41d0:a:1a3b::18","port":33445,"tcp_ports":[3389,33445],"public_key":"AEC204B9A4501412D5F0BB67D9C81B5DB3EE6ADA64122D32A3E9B093D544327D","maintainer":"a68366","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Since 26.12.2015","last_ping":1516822981},{"ipv4":"88.99.133.52","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"2D320F971EF2CA18004416C2AAE7BA52BF7949DB34EA8E2E21AF67BD367BE211","maintainer":"Skey","location":"FR","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"92.54.84.70","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"5625A62618CB4FCA70E147A71B29695F38CC65FF0CBD68AD46254585BE564802","maintainer":"t3mp","location":"RU","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"tox.uplinklabs.net","ipv6":"tox.uplinklabs.net","port":33445,"tcp_ports":[3389,33445],"public_key":"1A56EA3EDF5DF4C0AEABBF3C2E4E603890F87E983CAC8A0D532A335F2C6E3E1F","maintainer":"AbacusAvenger","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"i don't know what this is for","last_ping":1516822981},{"ipv4":"toxnode.nek0.net","ipv6":"toxnode.nek0.net","port":33445,"tcp_ports":[3389,33445],"public_key":"20965721D32CE50C3E837DD75B33908B33037E6225110BFF209277AEAF3F9639","maintainer":"Phsm","location":"UA","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"95.215.44.78","ipv6":"2a02:7aa0:1619::c6fe:d0cb","port":33445,"tcp_ports":[33445,3389],"public_key":"672DBE27B4ADB9D5FB105A6BB648B2F8FDB89B3323486A7A21968316E012023C","maintainer":"HooinKyoma","location":"SE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Thanx to Hooin Kyoma","last_ping":1516822981},{"ipv4":"163.172.136.118","ipv6":"2001:bc8:4400:2100::1c:50f","port":33445,"tcp_ports":[33445,3389],"public_key":"2C289F9F37C20D09DA83565588BF496FAB3764853FA38141817A72E3F18ACA0B","maintainer":"LittleVulpix","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"LittleTox - your friendly neighbourhood tox node!","last_ping":1516822981},{"ipv4":"sorunome.de","ipv6":"sorunome.de","port":33445,"tcp_ports":[3389,33445],"public_key":"02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46","maintainer":"Sorunome","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Keep calm and pony on","last_ping":1516822981},{"ipv4":"37.97.185.116","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"E59A0E71ADA20D35BD1B0957059D7EF7E7792B3D680AE25C6F4DBBA09114D165","maintainer":"Yani","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Yani's node of pleasure and leisure","last_ping":1516822981},{"ipv4":"80.87.193.193","ipv6":"2a01:230:2:6::46a8","port":33445,"tcp_ports":[3389,33445],"public_key":"B38255EE4B054924F6D79A5E6E5889EC94B6ADF6FE9906F97A3D01E3D083223A","maintainer":"linxon","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Tox DHT node by Linxon. Author ToxID: EC774ED05A7E71EEE2EBA939A27CD4FF403D7D79E1E685CFD0394B1770498217C6107E4D3C26","last_ping":1516822981},{"ipv4":"initramfs.io","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25","maintainer":"initramfs","location":"TW","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"initramfs' Tox DHT Node","last_ping":1516822981},{"ipv4":"hibiki.eve.moe","ipv6":"hibiki.eve.moe","port":33445,"tcp_ports":[33445],"public_key":"D3EB45181B343C2C222A5BCF72B760638E15ED87904625AAD351C594EEFAE03E","maintainer":"EveNeko","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd@hibiki.eve.moe","last_ping":1516822981},{"ipv4":"tox.deadteam.org","ipv6":"tox.deadteam.org","port":33445,"tcp_ports":[33445],"public_key":"C7D284129E83877D63591F14B3F658D77FF9BA9BA7293AEB2BDFBFE1A803AF47","maintainer":"DeadTeam","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Vive le TOX","last_ping":1516822981},{"ipv4":"46.229.52.198","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"813C8F4187833EF0655B10F7752141A352248462A567529A38B6BBF73E979307","maintainer":"Stranger","location":"UA","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Freedom to parrots!","last_ping":1516822981},{"ipv4":"node.tox.ngc.network","ipv6":"node.tox.ngc.network","port":33445,"tcp_ports":[3389,33445],"public_key":"A856243058D1DE633379508ADCAFCF944E40E1672FF402750EF712E30C42012A","maintainer":"Nolz","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Unlike Others","last_ping":1516822981},{"ipv4":"149.56.140.5","ipv6":"2607:5300:0201:3100:0000:0000:0000:3ec2","port":33445,"tcp_ports":[3389,33445],"public_key":"7E5668E0EE09E19F320AD47902419331FFEE147BB3606769CFBE921A2A2FD34C","maintainer":"velusip","location":"CA","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Jera","last_ping":1516822981},{"ipv4":"185.14.30.213","ipv6":"2a00:1ca8:a7::e8b","port":443,"tcp_ports":[33445,3389,443],"public_key":"2555763C8C460495B14157D234DD56B86300A2395554BCAE4621AC345B8C1B1B","maintainer":"dvor","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Just another tox node.","last_ping":1516822981},{"ipv4":"tox.natalenko.name","ipv6":"tox.natalenko.name","port":33445,"tcp_ports":[33445],"public_key":"1CB6EBFD9D85448FA70D3CAE1220B76BF6FCE911B46ACDCF88054C190589650B","maintainer":"post-factum","location":"DE","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1516822981},{"ipv4":"136.243.141.187","ipv6":"2a01:4f8:212:2459::a:1337","port":443,"tcp_ports":[33445,3389,443],"public_key":"6EE1FADE9F55CC7938234CC07C864081FC606D8FE7B751EDA217F268F1078A39","maintainer":"CeBe","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"uTox is the future! - maintained by CeBe - contact: tox@cebe.cc - tox: 7F50119368DC8FD3B1ECAF5D18E3F8854F0484CEC5BBF625D420B8E38638733C02486E387AF8","last_ping":1516822981},{"ipv4":"tox.abilinski.com","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"0E9D7FEE2AA4B42A4C18FE81C038E32FFD8D907AAA7896F05AA76C8D31A20065","maintainer":"flobe","location":"CA","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1516822981},{"ipv4":"m.loskiq.it","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"88124F3C18C6CFA8778B7679B7329A333616BD27A4DFB562D476681315CF143D","maintainer":"loskiq","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"https://t.me/loskiq","last_ping":1516822981},{"ipv4":"192.99.232.158","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"7B6CB208C811DEA8782711CE0CAD456AAC0C7B165A0498A1AA7010D2F2EC996C","maintainer":"basiljose","location":"CA","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"tmux.ru","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"7467AFA626D3246343170B309BA5BDC975DF3924FC9D7A5917FBFA9F5CD5CD38","maintainer":"nrn","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"https://t.me/nyoroon","last_ping":1516822981},{"ipv4":"37.48.122.22","ipv6":"2001:1af8:4700:a115:6::b","port":33445,"tcp_ports":[33445,3389],"public_key":"1B5A8AB25FFFB66620A531C4646B47F0F32B74C547B30AF8BD8266CA50A3AB59","maintainer":"Pokemon","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Those who would give up essential Liberty, to purchase a little temporary Safety, deserve neither Liberty nor Safety","last_ping":1516822981},{"ipv4":"tox.novg.net","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"D527E5847F8330D628DAB1814F0A422F6DC9D0A300E6C357634EE2DA88C35463","maintainer":"blind_oracle","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"t0x-node1.weba.ru","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"5A59705F86B9FC0671FDF72ED9BB5E55015FF20B349985543DDD4B0656CA1C63","maintainer":"Amin","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"T0X-Node #1","last_ping":1516822981},{"ipv4":"109.195.99.39","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"EF937F61B4979B60BBF306752D8F32029A2A05CD2615B2E9FBFFEADD8E7D5032","maintainer":"NaCl","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"NaCl node respond","last_ping":1516822981},{"ipv4":"79.140.30.52","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"FFAC871E85B1E1487F87AE7C76726AE0E60318A85F6A1669E04C47EB8DC7C72D","maintainer":"warlomak","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-easy-bootstrap","last_ping":1516822981},{"ipv4":"94.41.167.70","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"E519B2C1098999B60190012C7B53E8C43A73C535721036CD9DEC7CCA06741A7D","maintainer":"warlomak","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-easy-bootstrap","last_ping":1516822981},{"ipv4":"104.223.122.204","ipv6":"-","port":33445,"tcp_ports":[3389],"public_key":"3925752E43BF2F8EB4E12B0E9414311064FF2D76707DC7D5D2CCB43F75081F6B","maintainer":"ru_maniac","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"rmnc_third_node","last_ping":1516822981},{"ipv4":"77.55.211.53","ipv6":"-","port":53,"tcp_ports":[443,33445,3389],"public_key":"B9D109CC820C69A5D97A4A1A15708107C6BA85C13BC6188CC809D374AFF18E63","maintainer":"GDR!","location":"PL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"GDR!'s tox-bootstrapd https://gdr.name/","last_ping":1516822922},{"ipv4":"boseburo.ddns.net","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"AF3FC9FC3D121E82E362B4FA84A53E63F58C11C2BA61D988855289B8CABC9B18","maintainer":"LowEel","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"This is the Bose Buro bootstrap daemon","last_ping":1516822981},{"ipv4":"46.101.197.175","ipv6":"2a03:b0c0:3:d0::ac:5001","port":443,"tcp_ports":[443,33445,3389],"public_key":"CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707","maintainer":"clearmartin","location":"DE","status_udp":false,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"104.233.104.126","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"EDEE8F2E839A57820DE3DA4156D88350E53D4161447068A3457EE8F59F362414","maintainer":"wildermesser","location":"CA","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"195.93.190.6","ipv6":"2a01:d0:ffff:a8a::2","port":33445,"tcp_ports":[],"public_key":"FB4CE0DDEFEED45F26917053E5D24BDDA0FA0A3D83A672A9DA2375928B37023D","maintainer":"strngr","location":"UA","status_udp":false,"status_tcp":false,"version":"2016010100","motd":"tox node at strngr.name","last_ping":1516816803},{"ipv4":"193.124.186.205","ipv6":"2a02:f680:1:1100::542a","port":5228,"tcp_ports":[],"public_key":"9906D65F2A4751068A59D30505C5FC8AE1A95E0843AE9372EAFA3BAB6AC16C2C","maintainer":"Cactus","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"85.21.144.224","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"8F738BBC8FA9394670BCAB146C67A507B9907C8E564E28C2B59BEBB2FF68711B","maintainer":"himura","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"37.187.122.30","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"BEB71F97ED9C99C04B8489BB75579EB4DC6AB6F441B603D63533122F1858B51D","maintainer":"dolohow","location":"FR","status_udp":false,"status_tcp":false,"version":"2016010100","motd":"#stay frosty 8218DB335926393789859EDF2D79AC4CC805ADF73472D08165FEA51555502A58AE84FCE7C3D4","last_ping":1515853621},{"ipv4":"95.215.46.114","ipv6":"2a02:7aa0:1619::bdbd:17b8","port":33445,"tcp_ports":[],"public_key":"5823FB947FF24CF83DDFAC3F3BAA18F96EA2018B16CC08429CB97FA502F40C23","maintainer":"isotoxin","location":"SE","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"tox.dumalogiya.ru","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"2DAE6EB8C16131761A675D7C723F618FBA9D29DD8B4E0A39E7E3E8D7055EF113","maintainer":"mikhailnov","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0}]} \ No newline at end of file +{"nodes":[{"ipv4":"127.0.0.1","ipv6":"-","port":33445,"public_key":"617DA0076546F9A801D06AAA2E20234DA6A1DDA90583FB02B59E3501CA84D061","status_udp":true,"status_tcp":true}]} \ No newline at end of file diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 6c19b26..f56f13a 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -50,6 +50,9 @@ class ContactsManager: return self.get_curr_contact().number == group_number + def is_contact_active(self, contact): + return self._contacts[self._active_contact].tox_id == contact.tox_id + # ----------------------------------------------------------------------------------------------------------------- # Work with active friend # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 79c2cfa..cf4e815 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -39,7 +39,7 @@ class GroupChat(contact.Contact): self._tox.group_peer_get_public_key(self._number, peer_id)) self._peers.append(peer) - def get_peer(self, peer_id): + def get_peer_by_id(self, peer_id): peers = list(filter(lambda p: p.id == peer_id, self._peers)) return peers[0] diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index beb0853..c5be5cf 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -14,7 +14,7 @@ class GroupsService(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def create_new_gc(self, name, privacy_state): - group_number = self._tox.group_new(privacy_state, name) + group_number = self._tox.group_new(privacy_state, name.encode('utf-8')) self._add_new_group_by_number(group_number) def join_gc_by_id(self, chat_id, password): @@ -48,6 +48,14 @@ class GroupsService(tox_save.ToxSave): if util_ui.question(text.format(friend.name), util_ui.tr('Group invite')): self.join_gc_via_invite(invite_data, friend_number, None) + # ----------------------------------------------------------------------------------------------------------------- + # Group info methods + # ----------------------------------------------------------------------------------------------------------------- + + def update_group_info(self, group): + group.name = self._tox.group_get_name(group.number).encode('utf-8') + group.status_message = self._tox.group_get_topic(group.number).encode('utf-8') + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 0a321f8..7dff4e2 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -41,16 +41,7 @@ class Messenger(tox_save.ToxSave): t = util.get_unix_time() friend = self._get_friend_by_number(friend_number) text_message = TextMessage(message, MessageAuthor(friend.name, MESSAGE_AUTHOR['FRIEND']), t, message_type) - - if self._contacts_manager.is_friend_active(friend_number): # add message to list - self._create_message_item(text_message) - self._screen.messages.scrollToBottom() - self._contacts_manager.get_curr_contact().append_message(text_message) - else: - friend.inc_messages() - friend.append_message(text_message) - if not friend.visibility: - self._contacts_manager.update_filtration() + self._add_message(text_message, friend) def send_message(self): text = self._screen.messageEdit.toPlainText() @@ -135,6 +126,18 @@ class Messenger(tox_save.ToxSave): self._screen.messageEdit.clear() self._screen.messages.scrollToBottom() + def new_group_message(self, group_number, message_type, message, peer_id): + """ + Current user gets new message + :param message_type: message type - plain text or action message (/me) + :param message: text of message + """ + t = util.get_unix_time() + group = self._get_group_by_number(group_number) + peer = group.get_peer_by_id(peer_id) + text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']), t, message_type) + self._add_message(text_message, group) + # ----------------------------------------------------------------------------------------------------------------- # Message receipts # ----------------------------------------------------------------------------------------------------------------- @@ -229,3 +232,14 @@ class Messenger(tox_save.ToxSave): def _create_info_message_item(self, message): self._items_factory.create_message_item(message) self._screen.messages.scrollToBottom() + + def _add_message(self, text_message, contact): + if self._contacts_manager.is_contact_active(contact): # add message to list + self._create_message_item(text_message) + self._screen.messages.scrollToBottom() + self._contacts_manager.get_curr_contact().append_message(text_message) + else: + contact.inc_messages() + contact.append_message(text_message) + if not contact.visibility: + self._contacts_manager.update_filtration() diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 7b0872d..1182466 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -388,10 +388,11 @@ def group_invite(groups_service): return wrapped -def group_self_join(contacts_provider): +def group_self_join(contacts_provider, groups_service): def wrapped(tox, group_number, user_data): group = contacts_provider.get_group_by_number(group_number) invoke_in_main_thread(group.set_status, TOX_USER_STATUS['NONE']) + invoke_in_main_thread(groups_service.update_group_info, group) return wrapped @@ -407,7 +408,7 @@ def group_peer_join(contacts_provider): def group_peer_name(contacts_provider): def wrapped(tox, group_number, peer_id, name, length, user_data): group = contacts_provider.get_group_by_number(group_number) - peer = group.get_peer(peer_id) + peer = group.get_peer_by_id(peer_id) peer.name = str(name[:length]) return wrapped @@ -416,7 +417,7 @@ def group_peer_name(contacts_provider): def group_peer_status(contacts_provider): def wrapped(tox, group_number, peer_id, peer_status, user_data): group = contacts_provider.get_group_by_number(group_number) - peer = group.get_peer(peer_id) + peer = group.get_peer_by_id(peer_id) peer.status = peer_status return wrapped @@ -479,7 +480,7 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, # gc callbacks tox.callback_group_message(group_message(main_window, tray, tox, messenger, settings, profile), 0) tox.callback_group_invite(group_invite(groups_service), 0) - tox.callback_group_self_join(group_self_join(contacts_provider), 0) + tox.callback_group_self_join(group_self_join(contacts_provider, groups_service), 0) tox.callback_group_peer_join(group_peer_join(contacts_provider), 0) tox.callback_group_peer_name(group_peer_name(contacts_provider), 0) tox.callback_group_peer_status(group_peer_status(contacts_provider), 0) From b591ac13ba09e7fb409a21f668a3f25c2f43fb04 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 19:38:54 +0300 Subject: [PATCH 051/217] utf-8 decoding moved from contacts --- toxygen/contacts/basecontact.py | 2 -- toxygen/contacts/contacts_manager.py | 1 - toxygen/contacts/profile.py | 2 +- toxygen/groups/groups_service.py | 4 ++-- toxygen/middleware/callbacks.py | 4 ++-- toxygen/ui/menu.py | 2 +- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 988b0bc..0357809 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -39,7 +39,6 @@ class BaseContact: return self._name def set_name(self, value): - value = str(value, 'utf-8') if self._name != value: self._name = value self._widget.name.setText(self._name) @@ -61,7 +60,6 @@ class BaseContact: return self._status_message def set_status_message(self, value): - value = str(value, 'utf-8') if self._status_message != value: self._status_message = value self._widget.status_message.setText(self._status_message) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index f56f13a..c140b59 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -224,7 +224,6 @@ class ContactsManager: friend = self.get_friend_by_number(number) tmp = friend.name friend.set_name(name) - name = str(name, 'utf-8') if friend.name == name and tmp != name: # TODO: move to friend? message = util_ui.tr('User {} is now known as {}') diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index c775268..4e29147 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -49,7 +49,7 @@ class Profile(basecontact.BaseContact): def set_name(self, value): if self.name == value: return - super().set_name(value.encode('utf-8')) + super().set_name(value) self._tox.self_set_name(self._name.encode('utf-8')) def set_status_message(self, value): diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index c5be5cf..5b0dd67 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -53,8 +53,8 @@ class GroupsService(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def update_group_info(self, group): - group.name = self._tox.group_get_name(group.number).encode('utf-8') - group.status_message = self._tox.group_get_topic(group.number).encode('utf-8') + group.name = self._tox.group_get_name(group.number) + group.status_message = self._tox.group_get_topic(group.number) # ----------------------------------------------------------------------------------------------------------------- # Private methods diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 1182466..99f9db8 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -86,7 +86,7 @@ def friend_name(contacts_manager): Friend changed his name """ print('New name friend #' + str(friend_number)) - invoke_in_main_thread(contacts_manager.new_name, friend_number, name) + invoke_in_main_thread(contacts_manager.new_name, friend_number, str(name, 'utf-8')) return wrapped @@ -98,7 +98,7 @@ def friend_status_message(contacts_manager, messenger): and calls window repaint """ friend = contacts_manager.get_friend_by_number(friend_number) - invoke_in_main_thread(friend.set_status_message, status_message) + invoke_in_main_thread(friend.set_status_message, str(status_message, 'utf-8')) print('User #{} has new status'.format(friend_number)) invoke_in_main_thread(messenger.send_messages, friend_number) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index d912619..e98405d 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -272,7 +272,7 @@ class ProfileSettings(CenteredWidget): def closeEvent(self, event): self._profile.set_name(self.nick.text()) - self._profile.set_status_message(self.status_message.text().encode('utf-8')) + self._profile.set_status_message(self.status_message.text()) self._profile.set_status(self.status.currentIndex()) From 6495aa99207ad3f955dde9e8a972c5301c65ce3c Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 20:07:42 +0300 Subject: [PATCH 052/217] name changing fixes --- toxygen/contacts/contact.py | 5 ++++- toxygen/contacts/contacts_manager.py | 20 ++------------------ toxygen/messenger/messenger.py | 28 +++++++++++++++++----------- toxygen/middleware/callbacks.py | 19 ++++++++++++++++--- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index a142c61..d3861bc 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -118,7 +118,7 @@ class Contact(basecontact.BaseContact): """ :return list of unsent messages """ - messages = filter(lambda m: m.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) + messages = filter(lambda m: m.author is not None and m.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(messages) def get_unsent_messages_for_saving(self): @@ -248,6 +248,9 @@ class Contact(basecontact.BaseContact): def set_alias(self, alias): self._alias = bool(alias) + def has_alias(self): + return self._alias + # ----------------------------------------------------------------------------------------------------------------- # Visibility in friends' list # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index c140b59..94affd9 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -219,22 +219,6 @@ class ContactsManager: def is_active_online(self): return self._active_contact + 1 and self.get_curr_contact().status is not None - def new_name(self, number, name): - # TODO: move to somewhere else? - friend = self.get_friend_by_number(number) - tmp = friend.name - friend.set_name(name) - if friend.name == name and tmp != name: - # TODO: move to friend? - message = util_ui.tr('User {} is now known as {}') - # message = message.format(tmp, name) - # friend.append_message(InfoMessage(0, message, util.get_unix_time())) - # friend.actions = True - # if number == self.get_active_number(): - # self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - # self._messages.scrollToBottom() - # self.set_active(None) - # ----------------------------------------------------------------------------------------------------------------- # Work with friends (remove, block, set alias, get public key) # ----------------------------------------------------------------------------------------------------------------- @@ -251,7 +235,7 @@ class ContactsManager: if ok: aliases = self._settings['friends_aliases'] if text: - friend.name = bytes(text, 'utf-8') + friend.name = text try: index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) aliases[index] = (friend.tox_id, text) @@ -259,7 +243,7 @@ class ContactsManager: aliases.append((friend.tox_id, text)) friend.set_alias(text) else: # use default name - friend.name = bytes(self._tox.friend_get_name(friend.number), 'utf-8') + friend.name = self._tox.friend_get_name(friend.number) friend.set_alias('') try: index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 7dff4e2..af66f0b 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -19,14 +19,6 @@ class Messenger(tox_save.ToxSave): calls_manager.call_started_event.add_callback(self._on_call_started) calls_manager.call_finished_event.add_callback(self._on_call_finished) - # ----------------------------------------------------------------------------------------------------------------- - # Private methods - # ----------------------------------------------------------------------------------------------------------------- - - def _create_message_item(self, text_message): - # pixmap = self._contacts_manager.get_curr_contact().get_pixmap() - self._items_factory.create_message_item(text_message) - # ----------------------------------------------------------------------------------------------------------------- # Messaging - friends # ----------------------------------------------------------------------------------------------------------------- @@ -168,6 +160,18 @@ class Messenger(tox_save.ToxSave): if self._contacts_manager.is_friend_active(friend_number): self._screen.typing.setVisible(typing) + # ----------------------------------------------------------------------------------------------------------------- + # Contact info updated + # ----------------------------------------------------------------------------------------------------------------- + + def new_friend_name(self, friend, old_name, new_name): + if old_name == new_name or friend.has_alias(): + return + message = util_ui.tr('User {} is now known as {}') + message = message.format(old_name, new_name) + friend.actions = True + self._add_info_message(friend.number, message) + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- @@ -206,9 +210,7 @@ class Messenger(tox_save.ToxSave): message = util_ui.tr('User {} is now known as {}') message = message.format(self._profile_name, new_name) for friend in self._contacts_provider.get_all_friends(): - friend.append_message(InfoMessage(message, util.get_unix_time())) - if self._contacts_manager.is_active_a_friend(): - self._create_info_message_item(message) + self._add_info_message(friend.number, message) self._profile_name = new_name def _on_call_started(self, friend_number, audio, video, is_outgoing): @@ -243,3 +245,7 @@ class Messenger(tox_save.ToxSave): contact.append_message(text_message) if not contact.visibility: self._contacts_manager.update_filtration() + + def _create_message_item(self, text_message): + # pixmap = self._contacts_manager.get_curr_contact().get_pixmap() + self._items_factory.create_message_item(text_message) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 99f9db8..f5ad9da 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -80,13 +80,17 @@ def friend_connection_status(contacts_manager, profile, settings, plugin_loader, return wrapped -def friend_name(contacts_manager): +def friend_name(contacts_provider, messenger): def wrapped(tox, friend_number, name, size, user_data): """ Friend changed his name """ print('New name friend #' + str(friend_number)) - invoke_in_main_thread(contacts_manager.new_name, friend_number, str(name, 'utf-8')) + friend = contacts_provider.get_friend_by_number(friend_number) + old_name = friend.name + new_name = str(name, 'utf-8') + invoke_in_main_thread(friend.set_name, new_name) + invoke_in_main_thread(messenger.new_friend_name, friend, old_name, new_name) return wrapped @@ -423,6 +427,14 @@ def group_peer_status(contacts_provider): return wrapped +def group_topic(contacts_provider): + def wrapped(tox, group_number, peer_id, topic, length, user_data): + group = contacts_provider.get_group_by_number(group_number) + topic = str(topic[:length], 'utf-8') + invoke_in_main_thread(group.set_status_message, topic) + + return wrapped + # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization # ----------------------------------------------------------------------------------------------------------------- @@ -453,7 +465,7 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_friend_message(friend_message(messenger, contacts_manager, profile, settings, main_window, tray), 0) tox.callback_friend_connection_status(friend_connection_status(contacts_manager, profile, settings, plugin_loader, file_transfer_handler, messenger, calls_manager), 0) - tox.callback_friend_name(friend_name(contacts_manager), 0) + tox.callback_friend_name(friend_name(contacts_provider, messenger), 0) tox.callback_friend_status_message(friend_status_message(contacts_manager, messenger), 0) tox.callback_friend_request(friend_request(contacts_manager), 0) tox.callback_friend_typing(friend_typing(messenger), 0) @@ -484,3 +496,4 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_group_peer_join(group_peer_join(contacts_provider), 0) tox.callback_group_peer_name(group_peer_name(contacts_provider), 0) tox.callback_group_peer_status(group_peer_status(contacts_provider), 0) + tox.callback_group_topic(group_topic(contacts_provider), 0) From 206c5c4905d24776ce264ea715e0fbf32686caec Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 21:04:40 +0300 Subject: [PATCH 053/217] unsent files fixes - part 1 --- .../file_transfers_messages_service.py | 14 ++++---- toxygen/messenger/messages.py | 32 ++++++++++++------- toxygen/ui/main_screen.py | 4 +-- toxygen/ui/messages_widgets.py | 15 +++------ 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/toxygen/file_transfers/file_transfers_messages_service.py b/toxygen/file_transfers/file_transfers_messages_service.py index 7f8b5f6..7cb90f5 100644 --- a/toxygen/file_transfers/file_transfers_messages_service.py +++ b/toxygen/file_transfers/file_transfers_messages_service.py @@ -16,7 +16,7 @@ class FileTransfersMessagesService: status = FILE_TRANSFER_STATE['RUNNING'] if accepted else FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'] tm = TransferMessage(author, util.get_unix_time(), status, size, file_name, friend.number, file_number) - if self._is_active(friend.number): + if self._is_friend_active(friend.number): self._create_file_transfer_item(tm) self._messages.scrollToBottom() else: @@ -31,7 +31,7 @@ class FileTransfersMessagesService: status = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] tm = TransferMessage(author, util.get_unix_time(), status, size, file_name, friend.number, file_number) - if self._is_active(friend.number): + if self._is_friend_active(friend.number): self._create_file_transfer_item(tm) self._messages.scrollToBottom() @@ -40,16 +40,18 @@ class FileTransfersMessagesService: return tm def add_inline_message(self, transfer, index): - if self._is_active(transfer.friend_number): + if self._is_friend_active(transfer.friend_number): count = self._messages.count() if count + index + 1 >= 0: self._create_inline_item(transfer.data, count + index + 1) def add_unsent_file_message(self, friend, file_path, data): - tm = UnsentFileMessage(file_path, data, util.get_unix_time()) + author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['ME']) + size = os.path.getsize(file_path) if data is None else len(data) + tm = UnsentFileMessage(file_path, data, util.get_unix_time(), author, size, friend.number) friend.append_message(tm) - if self._is_active(friend.number): + if self._is_friend_active(friend.number): self._create_unsent_file_item(tm) self._messages.scrollToBottom() @@ -59,7 +61,7 @@ class FileTransfersMessagesService: # Private methods # ----------------------------------------------------------------------------------------------------------------- - def _is_active(self, friend_number): + def _is_friend_active(self, friend_number): if not self._contacts_manager.is_active_a_friend(): return False diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 91a1a04..57ed9bb 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -1,4 +1,5 @@ from history.database import MESSAGE_AUTHOR +import os.path from ui.messages_widgets import * @@ -16,8 +17,18 @@ PAGE_SIZE = 42 class MessageAuthor: def __init__(self, author_name, author_type): - self.name = author_name - self.type = author_type + self._name = author_name + self._type = author_type + + def get_name(self): + return self._name + + name = property(get_name) + + def get_type(self): + return self._type + + type = property(get_type) class Message: @@ -139,7 +150,10 @@ class TransferMessage(Message): self._friend_number, self._file_number = friend_number, file_number def is_active(self, file_number): - return self._file_number == file_number and self._state not in (2, 3) + if self._file_number != file_number: + return False + + return self._state not in (FILE_TRANSFER_STATE['FINISHED'], FILE_TRANSFER_STATE['CANCELLED']) def get_friend_number(self): return self._friend_number @@ -178,10 +192,11 @@ class TransferMessage(Message): return FileTransferItem(self, *args) -class UnsentFileMessage(Message): +class UnsentFileMessage(TransferMessage): - def __init__(self, path, data, time): - super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], 0, time) + def __init__(self, path, data, time, author, size, friend_number): + file_name = os.path.basename(path) + super().__init__(author, time, FILE_TRANSFER_STATE['UNSENT'], size, file_name, friend_number, -1) self._data, self._path = data, path def get_data(self): @@ -189,11 +204,6 @@ class UnsentFileMessage(Message): data = property(get_data) - def get_state(self): - return FILE_TRANSFER_STATE['UNSENT'] - - state = property(get_state) - def get_path(self): return self._path diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 9a4df47..8e19c5a 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -316,8 +316,8 @@ class MainWindow(QtWidgets.QMainWindow): def load(pos): if not pos: - friend = self._contacts_manager.get_curr_friend() - self._history_loader.load_history(friend) + contact = self._contacts_manager.get_curr_contact() + self._history_loader.load_history(contact) self.messages.verticalScrollBar().setValue(1) self.messages.verticalScrollBar().valueChanged.connect(load) self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index c676783..8a46fd0 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -253,7 +253,8 @@ class FileTransferItem(QtWidgets.QListWidget): icon = QtGui.QIcon(pixmap) self.cancel.setIcon(icon) self.cancel.setIconSize(QtCore.QSize(30, 30)) - self.cancel.setVisible(transfer_message.state in ACTIVE_FILE_TRANSFERS) + self.cancel.setVisible(transfer_message.state in ACTIVE_FILE_TRANSFERS or + transfer_message.state == FILE_TRANSFER_STATE['UNSENT']) self.cancel.clicked.connect( lambda: self.cancel_transfer(transfer_message.friend_number, transfer_message.file_number)) self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}') @@ -268,6 +269,9 @@ class FileTransferItem(QtWidgets.QListWidget): elif transfer_message.state == FILE_TRANSFER_STATE['PAUSED_BY_USER']: # setup for continue self.accept_or_pause.setVisible(True) self.button_update('resume') + elif transfer_message.state == FILE_TRANSFER_STATE['UNSENT']: + self.accept_or_pause.setVisible(False) + self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') else: # pause self.accept_or_pause.setVisible(True) self.button_update('pause') @@ -382,17 +386,12 @@ class FileTransferItem(QtWidgets.QListWidget): self.state = state self.time_left.setVisible(True) - @staticmethod - def mark_as_sent(): - return False - class UnsentFileItem(FileTransferItem): def __init__(self, transfer_message, file_transfer_handler, settings, width, parent=None): super().__init__(transfer_message, file_transfer_handler, settings, width, parent) self._time = time - self.pb.setVisible(False) movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) self.time.setMovie(movie) movie.start() @@ -448,7 +447,3 @@ class InlineImageItem(QtWidgets.QScrollArea): if directory: fl = QtCore.QFile(directory + '/toxygen_inline_' + util.curr_time().replace(':', '_') + '.png') self._pixmap.save(fl, 'PNG') - - @staticmethod - def mark_as_sent(): - return False From 77bdabb993fccdbbc337f2b5015a68e94c53f9d9 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 21:25:57 +0300 Subject: [PATCH 054/217] minor fixes - history --- toxygen/contacts/contact.py | 20 ++++++++++---------- toxygen/contacts/friend.py | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index d3861bc..d7fe35e 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -74,7 +74,7 @@ class Contact(basecontact.BaseContact): Get data to save in db :return: list of unsaved messages or [] """ - messages = list(filter(lambda x: x.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) + messages = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) return messages[-self._unsaved_messages:] if self._unsaved_messages else [] def get_corr(self): @@ -125,8 +125,8 @@ class Contact(basecontact.BaseContact): """ :return list of unsent messages for saving """ - messages = filter(lambda x: x.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) - and x.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) + messages = filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) + and m.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) return list(messages) def mark_as_sent(self, tox_message_id): @@ -142,9 +142,9 @@ class Contact(basecontact.BaseContact): # ----------------------------------------------------------------------------------------------------------------- def delete_message(self, message_id): - elem = list(filter(lambda x: type(x) in (TextMessage, GroupChatMessage) and x.message_id == message_id, + elem = list(filter(lambda m: type(m) in (TextMessage, GroupChatMessage) and m.message_id == message_id, self._corr))[0] - tmp = list(filter(lambda x: x.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) + tmp = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) if elem in tmp[-self._unsaved_messages:] and self._unsaved_messages: self._unsaved_messages -= 1 self._corr.remove(elem) @@ -155,10 +155,10 @@ class Contact(basecontact.BaseContact): """ Delete old messages (reduces RAM usage if messages saving is not enabled) """ - def save_message(x): - if x.type == MESSAGE_TYPE['FILE_TRANSFER'] and (x.state not in ACTIVE_FILE_TRANSFERS): + def save_message(m): + if m.type == MESSAGE_TYPE['FILE_TRANSFER'] and (m.state not in ACTIVE_FILE_TRANSFERS): return True - return x.author.type == MESSAGE_AUTHOR['NOT_SENT'] + return m.author is not None and m.author.type == MESSAGE_AUTHOR['NOT_SENT'] old = filter(save_message, self._corr[:-SAVE_MESSAGES]) self._corr = list(old) + self._corr[-SAVE_MESSAGES:] @@ -213,7 +213,7 @@ class Contact(basecontact.BaseContact): if not self._search_index: return None for i in range(self._search_index + 1, 0): - if self._corr[i].get_type() > (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): + if self._corr[i].type not in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): continue message = self._corr[i].text if re.search(self._search_string, message, re.IGNORECASE) is not None: @@ -294,7 +294,7 @@ class Contact(basecontact.BaseContact): messages = property(get_messages) # ----------------------------------------------------------------------------------------------------------------- - # Friend's number (can be used in toxcore) + # Friend's or group's number (can be used in toxcore) # ----------------------------------------------------------------------------------------------------------------- def get_number(self): diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 9103463..5c8eabb 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -23,7 +23,7 @@ class Friend(contact.Contact): Update status of active transfer and load inline if needed """ try: - tr = list(filter(lambda x: x.message_id == before_message_id, self._corr))[0] + tr = list(filter(lambda m: m.message_id == before_message_id, self._corr))[0] i = self._corr.index(tr) if inline: # inline was loaded self._corr.insert(i, inline) @@ -32,11 +32,11 @@ class Friend(contact.Contact): pass def get_unsent_files(self): - messages = filter(lambda x: type(x) is UnsentFileMessage, self._corr) + messages = filter(lambda m: type(m) is UnsentFileMessage, self._corr) return list(messages) def clear_unsent_files(self): - self._corr = list(filter(lambda x: type(x) is not UnsentFileMessage, self._corr)) + self._corr = list(filter(lambda m: type(m) is not UnsentFileMessage, self._corr)) def remove_invalid_unsent_files(self): def is_valid(message): From f67de1ba91c003accf473bacca9ff9176813695a Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 23:20:37 +0300 Subject: [PATCH 055/217] minor tray fixes --- toxygen/app.py | 2 +- toxygen/ui/tray.py | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index a4fa37b..568797a 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -328,7 +328,7 @@ class App: widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, self._toxes, self._version, self._groups_service) - self._tray = tray.init_tray(profile, self._settings, self._ms) + self._tray = tray.init_tray(profile, self._settings, self._ms, self._toxes) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, self._plugin_loader, self._file_transfer_handler, history, self._calls_manager, self._groups_service) diff --git a/toxygen/ui/tray.py b/toxygen/ui/tray.py index 2003053..3bfc7f3 100644 --- a/toxygen/ui/tray.py +++ b/toxygen/ui/tray.py @@ -1,6 +1,7 @@ from PyQt5 import QtWidgets, QtGui, QtCore from utils.ui import tr from utils.util import * +from ui.password_screen import UnlockAppScreen import os.path @@ -24,16 +25,16 @@ class Menu(QtWidgets.QMenu): self._settings = settings self._profile = profile - def newStatus(self, status): + def new_status(self, status): if not self._settings.locked: - self._profile.Profile.get_instance().set_status(status) - self.aboutToShowHandler() + self._profile.set_status(status) + self.about_to_show_handler() self.hide() - def aboutToShowHandler(self): + def about_to_show_handler(self): status = self._profile.status act = self.act - if status is None or self._ettings.get_instance().locked: + if status is None or self._settings.locked: self.actions()[1].setVisible(False) else: self.actions()[1].setVisible(True) @@ -52,10 +53,9 @@ class Menu(QtWidgets.QMenu): self.act.actions()[2].setText(tr('Busy')) -def init_tray(profile, settings, main_screen): +def init_tray(profile, settings, main_screen, toxes): icon = os.path.join(get_images_directory(), 'icon.png') tray = SystemTrayIcon(QtGui.QIcon(icon)) - tray.setObjectName('tray') menu = Menu(settings, profile) show = menu.addAction(tr('Open Toxygen')) @@ -72,7 +72,8 @@ def init_tray(profile, settings, main_screen): def show_window(): def show(): if not main_screen.isActiveWindow(): - main_screen.setWindowState(main_screen.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + main_screen.setWindowState( + main_screen.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) main_screen.activateWindow() main_screen.show() if not settings.locked: @@ -84,8 +85,8 @@ def init_tray(profile, settings, main_screen): settings.unlockScreen = False if not settings.unlockScreen: settings.unlockScreen = True - self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass) - self.p.show() + show_window.screen = UnlockAppScreen(toxes, correct_pass) + show_window.screen.show() def tray_activated(reason): if reason == QtWidgets.QSystemTrayIcon.DoubleClick: @@ -98,10 +99,10 @@ def init_tray(profile, settings, main_screen): show.triggered.connect(show_window) exit.triggered.connect(close_app) - menu.aboutToShow.connect(lambda: menu.aboutToShowHandler()) - online.triggered.connect(lambda: menu.newStatus(0)) - away.triggered.connect(lambda: menu.newStatus(1)) - busy.triggered.connect(lambda: menu.newStatus(2)) + menu.aboutToShow.connect(menu.about_to_show_handler) + online.triggered.connect(lambda: menu.new_status(0)) + away.triggered.connect(lambda: menu.new_status(1)) + busy.triggered.connect(lambda: menu.new_status(2)) tray.setContextMenu(menu) tray.show() From dcc3a3dcfafc8ae883a4dad09d556deabc41e98a Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 23:59:39 +0300 Subject: [PATCH 056/217] group peers list - base commit --- toxygen/ui/main_screen.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 8e19c5a..44c343d 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -18,7 +18,8 @@ class MainWindow(QtWidgets.QMainWindow): self.setAcceptDrops(True) self._saved = False self._profile = None - self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None + self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None + self._should_show_group_peers_list = False self.initUI() def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader, @@ -36,6 +37,7 @@ class MainWindow(QtWidgets.QMainWindow): def show(self): super().show() + self._contacts_manager.update() if self._settings['show_welcome_screen']: self._modal_window = self._widget_factory.create_welcome_window() @@ -174,7 +176,6 @@ class MainWindow(QtWidgets.QMainWindow): Form.resize(650, 60) self.messageEdit = MessageArea(Form, self) self.messageEdit.setGeometry(QtCore.QRect(0, 3, 450, 55)) - self.messageEdit.setObjectName("messageEdit") font = QtGui.QFont() font.setPointSize(11) font.setFamily(self._settings['font']) @@ -182,7 +183,6 @@ class MainWindow(QtWidgets.QMainWindow): self.sendMessageButton = QtWidgets.QPushButton(Form) self.sendMessageButton.setGeometry(QtCore.QRect(565, 3, 60, 55)) - self.sendMessageButton.setObjectName("sendMessageButton") self.menuButton = MenuButton(Form, self.show_menu) self.menuButton.setGeometry(QtCore.QRect(QtCore.QRect(455, 3, 55, 55))) @@ -212,7 +212,6 @@ class MainWindow(QtWidgets.QMainWindow): self.contact_name = LineEdit(Form) self.contact_name.setGeometry(QtCore.QRect(0, 0, 150, 25)) - self.contact_name.setObjectName("contact_name") self.contact_name.textChanged.connect(self.filtering) self.online_contacts = ComboBox(Form) @@ -238,20 +237,17 @@ class MainWindow(QtWidgets.QMainWindow): font.setPointSize(14) font.setBold(True) Form.name.setFont(font) - Form.name.setObjectName("name") self.status_message = Form.status_message = DataLabel(Form) Form.status_message.setGeometry(QtCore.QRect(75, 35, 170, 25)) font.setPointSize(12) font.setBold(False) Form.status_message.setFont(font) - Form.status_message.setObjectName("status_message") self.connection_status = Form.connection_status = StatusCircle(Form) Form.connection_status.setGeometry(QtCore.QRect(230, 10, 32, 32)) self.avatar_label.mouseReleaseEvent = self.profile_settings self.status_message.mouseReleaseEvent = self.profile_settings self.name.mouseReleaseEvent = self.profile_settings self.connection_status.raise_() - Form.connection_status.setObjectName("connection_status") def setup_right_top(self, Form): Form.resize(650, 75) @@ -266,7 +262,6 @@ class MainWindow(QtWidgets.QMainWindow): font.setPointSize(14) font.setBold(True) self.account_name.setFont(font) - self.account_name.setObjectName("account_name") self.account_status = DataLabel(Form) self.account_status.setGeometry(QtCore.QRect(100, 20, 400, 25)) self.account_status.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) @@ -282,6 +277,13 @@ class MainWindow(QtWidgets.QMainWindow): self.videocallButton.setGeometry(QtCore.QRect(550, 5, 50, 50)) self.videocallButton.setObjectName("videocallButton") self.videocallButton.clicked.connect(lambda: self._calls_manager.call_click(True, True)) + self.groupMenuButton = QtWidgets.QPushButton(Form) + self.groupMenuButton.setGeometry(QtCore.QRect(470, 10, 50, 50)) + self.groupMenuButton.clicked.connect(self._show_gc_peers_list) + self.groupMenuButton.setVisible(False) + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png')) + icon = QtGui.QIcon(pixmap) + self.groupMenuButton.setIcon(icon) self.update_call_state('call') self.typing = QtWidgets.QLabel(Form) self.typing.setGeometry(QtCore.QRect(500, 25, 50, 30)) @@ -401,7 +403,10 @@ class MainWindow(QtWidgets.QMainWindow): self.close() def resizeEvent(self, *args, **kwargs): - self.messages.setGeometry(0, 0, self.width() - 270, self.height() - 155) + if not self._should_show_group_peers_list: + self.messages.setGeometry(0, 0, self.width() - 270, self.height() - 155) + else: + self.messages.setGeometry(0, 0, self.width() - 450, self.height() - 155) self.friends_list.setGeometry(0, 0, 270, self.height() - 125) self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 10, 50, 50)) @@ -424,8 +429,7 @@ class MainWindow(QtWidgets.QMainWindow): rows = list(map(lambda x: self.messages.row(x), self.messages.selectedItems())) indexes = (rows[0] - self.messages.count(), rows[-1] - self.messages.count()) s = self._history_loader.export_history(self._contacts_manager.get_curr_friend(), True, indexes) - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(s) + self.copy_text(s) elif key == QtCore.Qt.Key_Z and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): self.messages.clearSelection() elif key == QtCore.Qt.Key_F and modifiers & QtCore.Qt.ControlModifier: @@ -673,6 +677,8 @@ class MainWindow(QtWidgets.QMainWindow): def friend_click(self, index): num = index.row() self._contacts_manager.set_active(num) + self.groupMenuButton.setVisible(not self._contacts_manager.is_active_a_friend()) + self.resizeEvent() def mouseReleaseEvent(self, event): pos = self.connection_status.pos() @@ -682,10 +688,6 @@ class MainWindow(QtWidgets.QMainWindow): else: super().mouseReleaseEvent(event) - def show(self): - super().show() - self._contacts_manager.update() - def filtering(self): ind = self.online_contacts.currentIndex() d = {0: 0, 1: 1, 2: 2, 3: 4, 4: 1 | 4, 5: 2 | 4} @@ -701,3 +703,7 @@ class MainWindow(QtWidgets.QMainWindow): self.search_field.setGeometry(x, y, self.messages.width(), 40) self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40) self.search_field.show() + + def _show_gc_peers_list(self): + self._should_show_group_peers_list = not self._should_show_group_peers_list + self.resizeEvent() From 02af0f767159b7fd5be3a28732bd71c4d6121936 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 20 May 2018 13:33:56 +0300 Subject: [PATCH 057/217] broken peers list --- toxygen/app.py | 2 +- toxygen/contacts/group_chat.py | 12 ++- toxygen/groups/group_peer.py | 8 +- toxygen/groups/groups_service.py | 15 +++- toxygen/groups/peers_list.py | 99 +++++++++++++++++++++ toxygen/ui/group_peers_list.py | 33 +++++++ toxygen/ui/main_screen.py | 23 ++++- toxygen/wrapper/toxcore_enums_and_consts.py | 22 ++--- 8 files changed, 193 insertions(+), 21 deletions(-) create mode 100644 toxygen/groups/peers_list.py create mode 100644 toxygen/ui/group_peers_list.py diff --git a/toxygen/app.py b/toxygen/app.py index 568797a..ff86acb 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -324,7 +324,7 @@ class App: self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider, file_transfers_message_service, profile) messages_items_factory.set_file_transfers_handler(self._file_transfer_handler) - self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider) + self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms) widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, self._toxes, self._version, self._groups_service) diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index cf4e815..4d561cf 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -31,12 +31,13 @@ class GroupChat(contact.Contact): def get_self_name(self): return self._peers[0].name - def add_peer(self, peer_id): + def add_peer(self, peer_id, is_current_user=False): peer = GroupChatPeer(peer_id, self._tox.group_peer_get_name(self._number, peer_id), self._tox.group_peer_get_status(self._number, peer_id), self._tox.group_peer_get_role(self._number, peer_id), - self._tox.group_peer_get_public_key(self._number, peer_id)) + self._tox.group_peer_get_public_key(self._number, peer_id), + is_current_user) self._peers.append(peer) def get_peer_by_id(self, peer_id): @@ -44,6 +45,11 @@ class GroupChat(contact.Contact): return peers[0] + def get_peers(self): + return self._peers[:] + + peers = property(get_peers) + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- @@ -54,4 +60,4 @@ class GroupChat(contact.Contact): def _add_self_to_gc(self): peer_id = self._tox.group_self_get_peer_id(self._number) - self.add_peer(peer_id) + self.add_peer(peer_id, True) diff --git a/toxygen/groups/group_peer.py b/toxygen/groups/group_peer.py index 6736185..d2d5aeb 100644 --- a/toxygen/groups/group_peer.py +++ b/toxygen/groups/group_peer.py @@ -2,12 +2,13 @@ class GroupChatPeer: - def __init__(self, peer_id, name, status, role, public_key): + def __init__(self, peer_id, name, status, role, public_key, is_current_user): self._peer_id = peer_id self._name = name self._status = status self._role = role self._public_key = public_key + self._is_current_user = is_current_user def get_id(self): return self._peer_id @@ -42,3 +43,8 @@ class GroupChatPeer: return self._public_key public_key = property(get_public_key) + + def get_is_current_user(self): + return self._is_current_user + + is_current_user = property(get_is_current_user) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 5b0dd67..01c1f44 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -1,13 +1,15 @@ import common.tox_save as tox_save import utils.ui as util_ui +from groups.peers_list import PeersListGenerator class GroupsService(tox_save.ToxSave): - def __init__(self, tox, contacts_manager, contacts_provider): + def __init__(self, tox, contacts_manager, contacts_provider, main_screen): super().__init__(tox) self._contacts_manager = contacts_manager self._contacts_provider = contacts_provider + self._peers_list_widget = main_screen.peers_list # ----------------------------------------------------------------------------------------------------------------- # Groups creation @@ -56,6 +58,17 @@ class GroupsService(tox_save.ToxSave): group.name = self._tox.group_get_name(group.number) group.status_message = self._tox.group_get_topic(group.number) + # ----------------------------------------------------------------------------------------------------------------- + # Peers list + # ----------------------------------------------------------------------------------------------------------------- + + def generate_peers_list(self): + group = self._contacts_manager.get_curr_contact() + PeersListGenerator().generate(group.peers, self, self._peers_list_widget, group.tox_id) + + def peer_selected(self, chat_id, peer_id): + pass + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/groups/peers_list.py b/toxygen/groups/peers_list.py new file mode 100644 index 0000000..e1db064 --- /dev/null +++ b/toxygen/groups/peers_list.py @@ -0,0 +1,99 @@ +from PyQt5 import QtWidgets, QtCore +from ui.group_peers_list import PeerItem, PeerTypeItem +import utils.ui as util_ui +from wrapper.toxcore_enums_and_consts import * + +# ----------------------------------------------------------------------------------------------------------------- +# Builder +# ----------------------------------------------------------------------------------------------------------------- + + +class PeerListBuilder: + + def __init__(self): + self._peers = {} + self._titles = {} + self._index = 0 + self._handler = None + + def with_click_handler(self, handler): + self._handler = handler + + return self + + def with_title(self, title): + self._titles[self._index] = title + self._index += 1 + + return self + + def with_peers(self, peers): + for peer in peers: + self._add_peer(peer) + + return self + + def build(self, parent): + parent.clear() + + for i in range(self._index): + if i in self._peers: + peer = self._peers[i] + self._add_peer_item(peer, parent) + else: + title = self._titles[i] + self._add_peer_type_item(title, parent) + + return parent + + def _add_peer_item(self, peer, parent): + item = PeerItem(peer, self._handler, parent.width()) + self._add_item(parent, item) + + def _add_peer_type_item(self, text, parent): + item = PeerTypeItem(text, parent.width()) + self._add_item(parent, item) + + @staticmethod + def _add_item(parent, item): + elem = QtWidgets.QListWidgetItem() + elem.setSizeHint(QtCore.QSize(parent.width(), item.height())) + parent.addItem(elem) + parent.setItemWidget(elem, item) + + def _add_peer(self, peer): + self._peers[self._index] = peer + self._index += 1 + +# ----------------------------------------------------------------------------------------------------------------- +# Generators +# ----------------------------------------------------------------------------------------------------------------- + + +class PeersListGenerator: + + @staticmethod + def generate(peers_list, groups_service, parent, chat_id): + admin_title = util_ui.tr('Administrator') + moderators_title = util_ui.tr('Moderators') + users_title = util_ui.tr('Users') + observers_title = util_ui.tr('Observers') + + admins = list(filter(lambda p: p.role == TOX_GROUP_ROLE['FOUNDER'], peers_list)) + moderators = list(filter(lambda p: p.role == TOX_GROUP_ROLE['MODERATOR'], peers_list)) + users = list(filter(lambda p: p.role == TOX_GROUP_ROLE['USER'], peers_list)) + observers = list(filter(lambda p: p.role == TOX_GROUP_ROLE['OBSERVER'], peers_list)) + + builder = (PeerListBuilder() + .with_click_handler(lambda peer_id: groups_service.peer_selected(chat_id, peer_id)) + .with_title(admin_title) + .with_peers(admins) + .with_title(moderators_title) + .with_peers(moderators) + .with_title(users_title) + .with_peers(users) + .with_title(observers_title) + .with_peers(observers) + ) + + return builder.build(parent) diff --git a/toxygen/ui/group_peers_list.py b/toxygen/ui/group_peers_list.py new file mode 100644 index 0000000..a648c04 --- /dev/null +++ b/toxygen/ui/group_peers_list.py @@ -0,0 +1,33 @@ +from ui.widgets import * +from wrapper.toxcore_enums_and_consts import * + + +class PeerItem(QtWidgets.QWidget): + + def __init__(self, peer, handler, width, parent=None): + super().__init__(parent) + self.resize(QtCore.QSize(width, 34)) + self.nameLabel = DataLabel(self) + self.nameLabel.setGeometry(0, 0, width, 34) + name = peer.name + if peer.is_current_user: + name += util_ui.tr(' (You)') + self.nameLabel.setText(name) + if peer.status == TOX_USER_STATUS['NONE']: + style = 'QLabel {color: green}' + elif peer.status == TOX_USER_STATUS['AWAY']: + style = 'QLabel {color: yellow}' + else: + style = 'QLabel {color: red}' + self.nameLabel.setStyleSheet(style) + self.nameLabel.mousePressEvent = lambda x: handler(peer.id) + + +class PeerTypeItem(QtWidgets.QWidget): + + def __init__(self, text, width, parent=None): + super().__init__(parent) + self.resize(QtCore.QSize(width, 34)) + self.nameLabel = DataLabel(self) + self.nameLabel.setGeometry(0, 0, width, 34) + self.nameLabel.setText(text) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 44c343d..81c4f15 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -279,7 +279,7 @@ class MainWindow(QtWidgets.QMainWindow): self.videocallButton.clicked.connect(lambda: self._calls_manager.call_click(True, True)) self.groupMenuButton = QtWidgets.QPushButton(Form) self.groupMenuButton.setGeometry(QtCore.QRect(470, 10, 50, 50)) - self.groupMenuButton.clicked.connect(self._show_gc_peers_list) + self.groupMenuButton.clicked.connect(self._toggle_gc_peers_list) self.groupMenuButton.setVisible(False) pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png')) icon = QtGui.QIcon(pixmap) @@ -325,6 +325,13 @@ class MainWindow(QtWidgets.QMainWindow): self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.messages.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + self.peers_list = QtWidgets.QListWidget(widget) + self.peers_list.setGeometry(0, 0, 0, 0) + self.peers_list.setObjectName("peersList") + self.peers_list.setSpacing(1) + self.peers_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.peers_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + def initUI(self): self.setMinimumSize(920, 500) s = self._settings @@ -403,14 +410,18 @@ class MainWindow(QtWidgets.QMainWindow): self.close() def resizeEvent(self, *args, **kwargs): + width = self.width() - 270 if not self._should_show_group_peers_list: - self.messages.setGeometry(0, 0, self.width() - 270, self.height() - 155) + self.messages.setGeometry(0, 0, width, self.height() - 155) + self.peers_list.setGeometry(0, 0, 0, 0) else: - self.messages.setGeometry(0, 0, self.width() - 450, self.height() - 155) + self.messages.setGeometry(0, 0, width * 3 // 4, self.height() - 155) + self.peers_list.setGeometry(width * 3 // 4, 0, width - width * 3 // 4, self.height() - 155) self.friends_list.setGeometry(0, 0, 270, self.height() - 125) self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 10, 50, 50)) self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 10, 50, 50)) + self.groupMenuButton.setGeometry(QtCore.QRect(self.width() - 450, 10, 50, 50)) self.typing.setGeometry(QtCore.QRect(self.width() - 450, 20, 50, 30)) self.messageEdit.setGeometry(QtCore.QRect(55, 0, self.width() - 395, 55)) @@ -678,6 +689,8 @@ class MainWindow(QtWidgets.QMainWindow): num = index.row() self._contacts_manager.set_active(num) self.groupMenuButton.setVisible(not self._contacts_manager.is_active_a_friend()) + if self._should_show_group_peers_list: + self._toggle_gc_peers_list() self.resizeEvent() def mouseReleaseEvent(self, event): @@ -704,6 +717,8 @@ class MainWindow(QtWidgets.QMainWindow): self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40) self.search_field.show() - def _show_gc_peers_list(self): + def _toggle_gc_peers_list(self): self._should_show_group_peers_list = not self._should_show_group_peers_list + if self._should_show_group_peers_list: + self._groups_service.generate_peers_list() self.resizeEvent() diff --git a/toxygen/wrapper/toxcore_enums_and_consts.py b/toxygen/wrapper/toxcore_enums_and_consts.py index 6942d3a..4d09338 100644 --- a/toxygen/wrapper/toxcore_enums_and_consts.py +++ b/toxygen/wrapper/toxcore_enums_and_consts.py @@ -198,7 +198,7 @@ TOX_GROUP_PRIVACY_STATE = { # and peer ID's (but not Tox ID's) is visible to anyone with access to a node # storing a DHT entry for the given group. # - 'TOX_GROUP_PRIVACY_STATE_PUBLIC': 0, + 'PUBLIC': 0, # # The group is considered to be private. The only way to join the group is by having @@ -208,7 +208,7 @@ TOX_GROUP_PRIVACY_STATE = { # the DHT is not used for any purpose at all. If a public group is set to private, # all DHT information related to the group will expire shortly. # - 'TOX_GROUP_PRIVACY_STATE_PRIVATE': 1 + 'PRIVATE': 1 } TOX_GROUP_ROLE = { @@ -217,23 +217,23 @@ TOX_GROUP_ROLE = { # May kick and ban all other peers as well as set their role to anything (except founder). # Founders may also set the group password, toggle the privacy state, and set the peer limit. # - 'TOX_GROUP_ROLE_FOUNDER': 0, + 'FOUNDER': 0, # # May kick, ban and set the user and observer roles for peers below this role. # May also set the group topic. # - 'TOX_GROUP_ROLE_MODERATOR': 1, + 'MODERATOR': 1, # # May communicate with other peers normally. # - 'TOX_GROUP_ROLE_USER': 2, + 'USER': 2, # # May observe the group and ignore peers; may not communicate with other peers or with the group. # - 'TOX_GROUP_ROLE_OBSERVER': 3 + 'OBSERVER': 3 } TOX_ERR_GROUP_NEW = { @@ -870,27 +870,27 @@ TOX_GROUP_MOD_EVENT = { # # A peer has been kicked from the group. # - 'TOX_GROUP_MOD_EVENT_KICK': 0, + 'KICK': 0, # # A peer has been banned from the group. # - 'TOX_GROUP_MOD_EVENT_BAN': 1, + 'BAN': 1, # # A peer as been given the observer role. # - 'TOX_GROUP_MOD_EVENT_OBSERVER': 2, + 'OBSERVER': 2, # # A peer has been given the user role. # - 'TOX_GROUP_MOD_EVENT_USER': 3, + 'USER': 3, # # A peer has been given the moderator role. # - 'TOX_GROUP_MOD_EVENT_MODERATOR': 4, + 'MODERATOR': 4, } TOX_ERR_GROUP_BAN_QUERY = { From b8fa8df41a4091d6ecab0bf29ff6646a53c249a5 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 20 May 2018 15:57:08 +0300 Subject: [PATCH 058/217] various fixes - peers list, resize event, tox instance recreation --- toxygen/app.py | 8 ++++-- toxygen/av/calls.py | 3 +++ toxygen/av/calls_manager.py | 3 +++ toxygen/contacts/contacts_manager.py | 15 ++++++++--- toxygen/contacts/friend_factory.py | 6 +++-- toxygen/contacts/group_chat.py | 5 ++-- toxygen/contacts/group_factory.py | 6 +++-- toxygen/contacts/profile.py | 4 +-- .../file_transfers/file_transfers_handler.py | 5 ++-- toxygen/groups/groups_service.py | 8 ++++++ toxygen/groups/peers_list.py | 8 +++--- toxygen/plugin_support/plugin_support.py | 10 +++---- toxygen/ui/group_peers_list.py | 4 +-- toxygen/ui/main_screen.py | 21 ++++++++++----- toxygen/ui/main_screen_widgets.py | 27 ++++++++++--------- toxygen/ui/menu.py | 4 +-- toxygen/ui/widgets_factory.py | 8 ++++-- toxygen/user_data/settings.py | 3 ++- 18 files changed, 97 insertions(+), 51 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index ff86acb..2f18045 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -291,7 +291,11 @@ class App: self._tox = self._create_tox(data) self._start_threads() - # TODO: foreach in list of tox savers set_tox + tox_savers = [self._friend_factory, self._group_factory, self._plugin_loader, self._contacts_manager, + self._contacts_provider, self._messenger, self._file_transfer_handler, self._groups_service] + for tox_saver in tox_savers: + tox_saver.set_tox(self._tox) + self._calls_manager.set_toxav(self._tox.AV) return self._tox @@ -327,7 +331,7 @@ class App: self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms) widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, - self._toxes, self._version, self._groups_service) + self._toxes, self._version, self._groups_service, history) self._tray = tray.init_tray(profile, self._settings, self._ms, self._toxes) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, self._plugin_loader, self._file_transfer_handler, history, self._calls_manager, diff --git a/toxygen/av/calls.py b/toxygen/av/calls.py index 6f3eccf..2673f1a 100644 --- a/toxygen/av/calls.py +++ b/toxygen/av/calls.py @@ -36,6 +36,9 @@ class AV: self._video_width = 640 self._video_height = 480 + def set_toxav(self, toxav): + self._toxav = toxav + def stop(self): self._running = False self.stop_audio_thread() diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py index 5e84573..eefd03a 100644 --- a/toxygen/av/calls_manager.py +++ b/toxygen/av/calls_manager.py @@ -18,6 +18,9 @@ class CallsManager: self._call_started_event = event.Event() # friend_number, audio, video, is_outgoing self._call_finished_event = event.Event() # friend_number, is_declined + def set_toxav(self, toxav): + self._call.set_toxav(toxav) + # ----------------------------------------------------------------------------------------------------------------- # Events # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 94affd9..d0f0980 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,16 +1,17 @@ from contacts.friend import Friend from contacts.group_chat import GroupChat from messenger.messages import * +from common.tox_save import ToxSave -class ContactsManager: +class ContactsManager(ToxSave): """ Represents contacts list. """ def __init__(self, tox, settings, screen, profile_manager, contact_provider, history, tox_dns, messages_items_factory): - self._tox = tox + super().__init__(tox) self._settings = settings self._screen = screen self._profile_manager = profile_manager @@ -19,6 +20,7 @@ class ContactsManager: self._messages_items_factory = messages_items_factory self._messages = screen.messages self._contacts, self._active_contact = [], -1 + self._active_contact_changed = Event() self._sorting = settings['sorting'] self._filter_string = '' self._friend_item_height = 40 if settings['compact_mode'] else 70 @@ -115,13 +117,18 @@ class ContactsManager: # else: # self._screen.call_finished() self._set_current_contact_data(contact) - + self._active_contact_changed(contact) except Exception as ex: # no friend found. ignore util.log('Friend value: ' + str(value)) util.log('Error in set active: ' + str(ex)) raise - active_friend = property(get_active, set_active) + active_contact = property(get_active, set_active) + + def get_active_contact_changed(self): + return self._active_contact_changed + + active_contact_changed = property(get_active_contact_changed) def set_active_by_number_and_type(self, number, is_friend): # TODO: by id for i in range(len(self._contacts)): diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py index a9b0477..8ebafd6 100644 --- a/toxygen/contacts/friend_factory.py +++ b/toxygen/contacts/friend_factory.py @@ -1,11 +1,13 @@ from contacts.friend import Friend +from common.tox_save import ToxSave -class FriendFactory: +class FriendFactory(ToxSave): def __init__(self, profile_manager, settings, tox, db, items_factory): + super().__init__(tox) self._profile_manager = profile_manager - self._settings, self._tox = settings, tox + self._settings = settings self._db = db self._items_factory = items_factory diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 4d561cf..1f964c6 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -3,13 +3,14 @@ from contacts.contact_menu import GroupMenuGenerator import utils.util as util from groups.group_peer import GroupChatPeer from wrapper import toxcore_enums_and_consts as constants +from common.tox_save import ToxSave -class GroupChat(contact.Contact): +class GroupChat(contact.Contact, ToxSave): def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id): super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) - self._tox = tox + ToxSave.__init__(self, tox) self.set_status(constants.TOX_USER_STATUS['NONE']) self._peers = [] self._add_self_to_gc() diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py index 089fc09..8db3e9a 100644 --- a/toxygen/contacts/group_factory.py +++ b/toxygen/contacts/group_factory.py @@ -1,11 +1,13 @@ from contacts.group_chat import GroupChat +from common.tox_save import ToxSave -class GroupFactory: +class GroupFactory(ToxSave): def __init__(self, profile_manager, settings, tox, db, items_factory): + super().__init__(tox) self._profile_manager = profile_manager - self._settings, self._tox = settings, tox + self._settings = settings self._db = db self._items_factory = items_factory diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 4e29147..469b8fe 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -67,7 +67,7 @@ class Profile(basecontact.BaseContact): # Reset # ----------------------------------------------------------------------------------------------------------------- - def _restart(self): + def restart(self): """ Recreate tox instance """ @@ -80,6 +80,6 @@ class Profile(basecontact.BaseContact): contacts = self._contacts_provider.get_all() if self.status is None or all(list(map(lambda x: x.status is None, contacts))) and len(contacts): self._waiting_for_reconnection = True - self._restart() + self.restart() self._timer = threading.Timer(50, self._reconnect) self._timer.start() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 1f5c9d0..64a2bfe 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -1,12 +1,13 @@ from messenger.messages import * from ui.contact_items import * import utils.util as util +from common.tox_save import ToxSave -class FileTransfersHandler: +class FileTransfersHandler(ToxSave): def __init__(self, tox, settings, contact_provider, file_transfers_message_service, profile): - self._tox = tox + super().__init__(tox) self._settings = settings self._contact_provider = contact_provider self._file_transfers_message_service = file_transfers_message_service diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 01c1f44..c9960fc 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -11,6 +11,11 @@ class GroupsService(tox_save.ToxSave): self._contacts_provider = contacts_provider self._peers_list_widget = main_screen.peers_list + def set_tox(self, tox): + super().set_tox(tox) + for group in self._get_all_groups(): + group.set_tox(tox) + # ----------------------------------------------------------------------------------------------------------------- # Groups creation # ----------------------------------------------------------------------------------------------------------------- @@ -81,3 +86,6 @@ class GroupsService(tox_save.ToxSave): def _get_friend(self, friend_number): return self._contacts_provider.get_friend_by_number(friend_number) + + def _get_all_groups(self): + return self._contacts_provider.get_all_groups() diff --git a/toxygen/groups/peers_list.py b/toxygen/groups/peers_list.py index e1db064..21995b4 100644 --- a/toxygen/groups/peers_list.py +++ b/toxygen/groups/peers_list.py @@ -2,6 +2,8 @@ from PyQt5 import QtWidgets, QtCore from ui.group_peers_list import PeerItem, PeerTypeItem import utils.ui as util_ui from wrapper.toxcore_enums_and_consts import * +from ui.widgets import * + # ----------------------------------------------------------------------------------------------------------------- # Builder @@ -47,16 +49,16 @@ class PeerListBuilder: return parent def _add_peer_item(self, peer, parent): - item = PeerItem(peer, self._handler, parent.width()) + item = PeerItem(peer, self._handler, parent.width(), parent) self._add_item(parent, item) def _add_peer_type_item(self, text, parent): - item = PeerTypeItem(text, parent.width()) + item = PeerTypeItem(text, parent.width(), parent) self._add_item(parent, item) @staticmethod def _add_item(parent, item): - elem = QtWidgets.QListWidgetItem() + elem = QtWidgets.QListWidgetItem(parent) elem.setSizeHint(QtCore.QSize(parent.width(), item.height())) parent.addItem(elem) parent.setItemWidget(elem, item) diff --git a/toxygen/plugin_support/plugin_support.py b/toxygen/plugin_support/plugin_support.py index 5871afa..e8c9a56 100644 --- a/toxygen/plugin_support/plugin_support.py +++ b/toxygen/plugin_support/plugin_support.py @@ -1,28 +1,26 @@ import utils.util as util -from contacts import profile import os import importlib import inspect import plugins.plugin_super_class as pl -from user_data import toxes import sys +from common.tox_save import ToxSave -class PluginLoader(): +class PluginLoader(ToxSave): def __init__(self, tox, toxes, profile, settings): - super().__init__() + super().__init__(tox) self._profile = profile self._settings = settings self._plugins = {} # dict. key - plugin unique short name, value - tuple (plugin instance, is active) - self._tox = tox self._toxes = toxes def set_tox(self, tox): """ New tox instance """ - self._tox = tox + super().set_tox(tox) for value in self._plugins.values(): value[0].set_tox(tox) diff --git a/toxygen/ui/group_peers_list.py b/toxygen/ui/group_peers_list.py index a648c04..9d2632d 100644 --- a/toxygen/ui/group_peers_list.py +++ b/toxygen/ui/group_peers_list.py @@ -8,7 +8,7 @@ class PeerItem(QtWidgets.QWidget): super().__init__(parent) self.resize(QtCore.QSize(width, 34)) self.nameLabel = DataLabel(self) - self.nameLabel.setGeometry(0, 0, width, 34) + self.nameLabel.setGeometry(5, 0, width - 5, 34) name = peer.name if peer.is_current_user: name += util_ui.tr(' (You)') @@ -29,5 +29,5 @@ class PeerTypeItem(QtWidgets.QWidget): super().__init__(parent) self.resize(QtCore.QSize(width, 34)) self.nameLabel = DataLabel(self) - self.nameLabel.setGeometry(0, 0, width, 34) + self.nameLabel.setGeometry(5, 0, width - 5, 34) self.nameLabel.setText(text) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 81c4f15..c32d0fa 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -33,6 +33,7 @@ class MainWindow(QtWidgets.QMainWindow): self._history_loader = history_loader self._calls_manager = calls_manager self._groups_service = groups_service + self._contacts_manager.active_contact_changed.add_callback(self._new_contact_selected) self.messageEdit.set_messenger(messenger) def show(self): @@ -284,6 +285,7 @@ class MainWindow(QtWidgets.QMainWindow): pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png')) icon = QtGui.QIcon(pixmap) self.groupMenuButton.setIcon(icon) + self.groupMenuButton.setIconSize(QtCore.QSize(45, 60)) self.update_call_state('call') self.typing = QtWidgets.QLabel(Form) self.typing.setGeometry(QtCore.QRect(500, 25, 50, 30)) @@ -331,6 +333,8 @@ class MainWindow(QtWidgets.QMainWindow): self.peers_list.setSpacing(1) self.peers_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.peers_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.peers_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) + self.peers_list.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) def initUI(self): self.setMinimumSize(920, 500) @@ -431,6 +435,7 @@ class MainWindow(QtWidgets.QMainWindow): self.account_name.setGeometry(QtCore.QRect(100, 15, self.width() - 560, 25)) self.account_status.setGeometry(QtCore.QRect(100, 35, self.width() - 560, 25)) self.messageEdit.setFocus() + self._contacts_manager.update() def keyPressEvent(self, event): key, modifiers = event.key(), event.modifiers() @@ -564,7 +569,7 @@ class MainWindow(QtWidgets.QMainWindow): def send_smiley(self): self.menu.hide() - if self._contacts_manager.active_friend + 1: + if self._contacts_manager.get_curr_contact() is not None: self.smiley = self._widget_factory.create_smiley_window(self) self.smiley.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), self.y() + self.height() - 200, @@ -687,11 +692,8 @@ class MainWindow(QtWidgets.QMainWindow): def friend_click(self, index): num = index.row() - self._contacts_manager.set_active(num) + self._contacts_manager.active_contact = num self.groupMenuButton.setVisible(not self._contacts_manager.is_active_a_friend()) - if self._should_show_group_peers_list: - self._toggle_gc_peers_list() - self.resizeEvent() def mouseReleaseEvent(self, event): pos = self.connection_status.pos() @@ -711,14 +713,21 @@ class MainWindow(QtWidgets.QMainWindow): return if self._contacts_manager.get_curr_friend() is None: return - self.search_field = SearchScreen(self.messages, self.messages.width(), self.messages.parent()) + self.search_field = self._widget_factory.create_search_screen(self.messages) x, y = self.messages.x(), self.messages.y() + self.messages.height() - 40 self.search_field.setGeometry(x, y, self.messages.width(), 40) self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40) + if self._should_show_group_peers_list: + self.peers_list.setFixedHeight(self.peers_list.height() - 40) self.search_field.show() def _toggle_gc_peers_list(self): self._should_show_group_peers_list = not self._should_show_group_peers_list + self.resizeEvent() if self._should_show_group_peers_list: self._groups_service.generate_peers_list() + + def _new_contact_selected(self, contact): + if self._should_show_group_peers_list: + self._toggle_gc_peers_list() self.resizeEvent() diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index dfd5ea7..479575e 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -374,8 +374,10 @@ class ClickableLabel(QtWidgets.QLabel): class SearchScreen(QtWidgets.QWidget): - def __init__(self, messages, width, *args): + def __init__(self, contacts_manager, history_loader, messages, width, *args): super().__init__(*args) + self._contacts_manager = contacts_manager + self._history_loader = history_loader self.setMaximumSize(width, 40) self.setMinimumSize(width, 40) self._messages = messages @@ -426,24 +428,24 @@ class SearchScreen(QtWidgets.QWidget): self.search_text.setFocus() def search(self): - Profile.get_instance().update() + self._contacts_manager.update() text = self.search_text.text() - friend = Profile.get_instance().get_curr_friend() - if text and friend and util.is_re_valid(text): - index = friend.search_string(text) + contact = self._contacts_manager.get_curr_contact() + if text and contact and util.is_re_valid(text): + index = contact.search_string(text) self.load_messages(index) def prev(self): - friend = Profile.get_instance().get_curr_friend() - if friend is not None: - index = friend.search_prev() + contact = self._contacts_manager.get_curr_contact() + if contact is not None: + index = contact.search_prev() self.load_messages(index) def next(self): - friend = Profile.get_instance().get_curr_friend() + contact = self._contacts_manager.get_curr_contact() text = self.search_text.text() - if friend is not None: - index = friend.search_next() + if contact is not None: + index = contact.search_next() if index is not None: count = self._messages.count() index += count @@ -456,10 +458,9 @@ class SearchScreen(QtWidgets.QWidget): def load_messages(self, index): text = self.search_text.text() if index is not None: - profile = Profile.get_instance() count = self._messages.count() while count + index < 0: - profile.load_history() + self._history_loader.load_history() count = self._messages.count() index += count item = self._messages.item(index) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index e98405d..2f55bb5 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -281,7 +281,7 @@ class NetworkSettings(CenteredWidget): def __init__(self, settings, reset): super().__init__() self._settings = settings - self.reset = reset + self._reset = reset self.initUI() self.center() @@ -360,7 +360,7 @@ class NetworkSettings(CenteredWidget): self._settings['download_nodes_list'] = self.nodes.isChecked() self._settings.save() # recreate tox instance - self._profile.reset() + self._reset() self.close() except Exception as ex: log('Exception in restart: ' + str(ex)) diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 66aca2c..4c4f1c6 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -6,7 +6,7 @@ from ui.groups_widgets import * class WidgetsFactory: def __init__(self, settings, profile, profile_manager, contacts_manager, file_transfer_handler, smiley_loader, - plugin_loader, toxes, version, groups_service): + plugin_loader, toxes, version, groups_service, history): self._settings = settings self._profile = profile self._profile_manager = profile_manager @@ -17,6 +17,7 @@ class WidgetsFactory: self._toxes = toxes self._version = version self._groups_service = groups_service + self._history = history def create_screenshot_window(self, *args): return ScreenShotWindow(self._file_transfer_handler, self._contacts_manager, *args) @@ -31,7 +32,7 @@ class WidgetsFactory: return ProfileSettings(self._profile, self._profile_manager, self._settings, self._toxes) def create_network_settings_window(self): - return NetworkSettings(self._settings, self._profile.reset) + return NetworkSettings(self._settings, self._profile.restart) def create_audio_settings_window(self): return AudioSettings(self._settings) @@ -71,3 +72,6 @@ class WidgetsFactory: def create_join_group_screen_window(self): return JoinGroupScreen(self._groups_service) + + def create_search_screen(self, messages): + return SearchScreen(self._contacts_manager, self._history, messages, messages.parent()) diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 1d7329e..76a37c0 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -146,7 +146,8 @@ class Settings(dict): 'font': 'Times New Roman', 'update': 1, 'group_notifications': True, - 'download_nodes_list': False + 'download_nodes_list': False, + 'notify_all_gc': False } @staticmethod From c6b67452ed3606ffb665c260e07db73b4b10607a Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 20 May 2018 17:22:44 +0300 Subject: [PATCH 059/217] peers - more callback and peers list refactoring --- toxygen/contacts/contacts_manager.py | 3 ++ toxygen/contacts/group_chat.py | 4 +++ toxygen/groups/groups_service.py | 2 ++ toxygen/groups/peers_list.py | 43 +++++++++++++++------------- toxygen/middleware/callbacks.py | 27 +++++++++++++---- toxygen/ui/main_screen.py | 1 - 6 files changed, 53 insertions(+), 27 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index d0f0980..02028d7 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -144,6 +144,9 @@ class ContactsManager(ToxSave): def is_active_a_friend(self): return type(self.get_curr_contact()) is Friend + def is_active_a_group(self): + return type(self.get_curr_contact()) is GroupChat + # ----------------------------------------------------------------------------------------------------------------- # Filtration # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 1f964c6..b7429d4 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -41,6 +41,10 @@ class GroupChat(contact.Contact, ToxSave): is_current_user) self._peers.append(peer) + def remove_peer(self, peer_id): + peer = self.get_peer_by_id(peer_id) + self._peers.remove(peer) + def get_peer_by_id(self, peer_id): peers = list(filter(lambda p: p.id == peer_id, self._peers)) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index c9960fc..9b4287d 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -68,6 +68,8 @@ class GroupsService(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def generate_peers_list(self): + if not self._contacts_manager.is_active_a_group(): + return group = self._contacts_manager.get_curr_contact() PeersListGenerator().generate(group.peers, self, self._peers_list_widget, group.tox_id) diff --git a/toxygen/groups/peers_list.py b/toxygen/groups/peers_list.py index 21995b4..17495f5 100644 --- a/toxygen/groups/peers_list.py +++ b/toxygen/groups/peers_list.py @@ -1,6 +1,4 @@ -from PyQt5 import QtWidgets, QtCore from ui.group_peers_list import PeerItem, PeerTypeItem -import utils.ui as util_ui from wrapper.toxcore_enums_and_consts import * from ui.widgets import * @@ -35,18 +33,16 @@ class PeerListBuilder: return self - def build(self, parent): - parent.clear() + def build(self, list_widget): + list_widget.clear() for i in range(self._index): if i in self._peers: peer = self._peers[i] - self._add_peer_item(peer, parent) + self._add_peer_item(peer, list_widget) else: title = self._titles[i] - self._add_peer_type_item(title, parent) - - return parent + self._add_peer_type_item(title, list_widget) def _add_peer_item(self, peer, parent): item = PeerItem(peer, self._handler, parent.width(), parent) @@ -75,7 +71,7 @@ class PeerListBuilder: class PeersListGenerator: @staticmethod - def generate(peers_list, groups_service, parent, chat_id): + def generate(peers_list, groups_service, list_widget, chat_id): admin_title = util_ui.tr('Administrator') moderators_title = util_ui.tr('Moderators') users_title = util_ui.tr('Users') @@ -87,15 +83,22 @@ class PeersListGenerator: observers = list(filter(lambda p: p.role == TOX_GROUP_ROLE['OBSERVER'], peers_list)) builder = (PeerListBuilder() - .with_click_handler(lambda peer_id: groups_service.peer_selected(chat_id, peer_id)) - .with_title(admin_title) - .with_peers(admins) - .with_title(moderators_title) - .with_peers(moderators) - .with_title(users_title) - .with_peers(users) - .with_title(observers_title) - .with_peers(observers) - ) + .with_click_handler(lambda peer_id: groups_service.peer_selected(chat_id, peer_id))) + if len(admins): + (builder + .with_title(admin_title) + .with_peers(admins)) + if len(moderators): + (builder + .with_title(moderators_title) + .with_peers(moderators)) + if len(users): + (builder + .with_title(users_title) + .with_peers(users)) + if len(observers): + (builder + .with_title(observers_title) + .with_peers(observers)) - return builder.build(parent) + builder.build(list_widget) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index f5ad9da..f7a9531 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -401,28 +401,40 @@ def group_self_join(contacts_provider, groups_service): return wrapped -def group_peer_join(contacts_provider): +def group_peer_join(contacts_provider, groups_service): def wrapped(tox, group_number, peer_id, user_data): group = contacts_provider.get_group_by_number(group_number) group.add_peer(peer_id) + invoke_in_main_thread(groups_service.generate_peers_list) return wrapped -def group_peer_name(contacts_provider): +def group_peer_exit(contacts_provider, groups_service): + def wrapped(tox, group_number, peer_id, message, length, user_data): + group = contacts_provider.get_group_by_number(group_number) + group.remove_peer(peer_id) + invoke_in_main_thread(groups_service.generate_peers_list) + + return wrapped + + +def group_peer_name(contacts_provider, groups_service): def wrapped(tox, group_number, peer_id, name, length, user_data): group = contacts_provider.get_group_by_number(group_number) peer = group.get_peer_by_id(peer_id) peer.name = str(name[:length]) + invoke_in_main_thread(groups_service.generate_peers_list) return wrapped -def group_peer_status(contacts_provider): +def group_peer_status(contacts_provider, groups_service): def wrapped(tox, group_number, peer_id, peer_status, user_data): group = contacts_provider.get_group_by_number(group_number) peer = group.get_peer_by_id(peer_id) peer.status = peer_status + invoke_in_main_thread(groups_service.generate_peers_list) return wrapped @@ -456,6 +468,8 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, :param main_window: MainWindow instance :param tray: tray (for notifications) :param messenger: Messenger instance + :param groups_service: GroupsService instance + :param contacts_provider: ContactsProvider instance """ # self callbacks tox.callback_self_connection_status(self_connection_status(tox, profile), 0) @@ -493,7 +507,8 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_group_message(group_message(main_window, tray, tox, messenger, settings, profile), 0) tox.callback_group_invite(group_invite(groups_service), 0) tox.callback_group_self_join(group_self_join(contacts_provider, groups_service), 0) - tox.callback_group_peer_join(group_peer_join(contacts_provider), 0) - tox.callback_group_peer_name(group_peer_name(contacts_provider), 0) - tox.callback_group_peer_status(group_peer_status(contacts_provider), 0) + tox.callback_group_peer_join(group_peer_join(contacts_provider, groups_service), 0) + tox.callback_group_peer_exit(group_peer_exit(contacts_provider, groups_service), 0) + tox.callback_group_peer_name(group_peer_name(contacts_provider, groups_service), 0) + tox.callback_group_peer_status(group_peer_status(contacts_provider, groups_service), 0) tox.callback_group_topic(group_topic(contacts_provider), 0) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index c32d0fa..d39cc91 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -435,7 +435,6 @@ class MainWindow(QtWidgets.QMainWindow): self.account_name.setGeometry(QtCore.QRect(100, 15, self.width() - 560, 25)) self.account_status.setGeometry(QtCore.QRect(100, 35, self.width() - 560, 25)) self.messageEdit.setFocus() - self._contacts_manager.update() def keyPressEvent(self, event): key, modifiers = event.key(), event.modifiers() From 0a9939f33b8aca2f4fbc7b420a32e8450fa3675f Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 23 May 2018 21:23:51 +0300 Subject: [PATCH 060/217] Tests cleanup --- .travis.yml | 3 +- tests/tests.py | 165 +------------------------------------------------ 2 files changed, 4 insertions(+), 164 deletions(-) diff --git a/.travis.yml b/.travis.yml index cfabadd..8de5702 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ install: - pip install pyqt5 - pip install pyaudio - pip install opencv-python + - pip install pydenticon before_script: # Opus - wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz @@ -37,7 +38,7 @@ before_script: - sudo ldconfig - cd .. # Toxcore - - git clone https://github.com/irungentoo/toxcore.git + - git clone https://github.com/ingvar1995/toxcore.git --branch=new_gc - cd toxcore - autoreconf -if - ./configure diff --git a/tests/tests.py b/tests/tests.py index 442a677..130f56d 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,14 +1,7 @@ -from contacts.profile import * -from network.tox_dns import tox_dns -from db.database import History -from toxygen.smileys import SmileyLoader -from messenger.messages import * -import user_data.toxes as encr -import toxygen.utils as util -import time +from middleware.tox_factory import * -# TODO: fic +# TODO: add new tests class TestTox: @@ -23,157 +16,3 @@ class TestTox: tox = tox_factory(data) assert tox.self_get_name() == str(name, 'utf-8') assert tox.self_get_status_message() == str(status_message, 'utf-8') - - -class TestProfileManager: - - def test_creation(self): - file_name, path = 'test.tox', os.path.dirname(os.path.realpath(__file__)) + '/' - data = b'test' - with open(path + file_name, 'wb') as fl: - fl.write(data) - ph = ProfileManager(path, file_name[:4]) - assert ProfileManager.get_path() == path - assert ph.open_profile() == data - assert os.path.exists(path + 'avatars/') - - -class TestDNS: - - def test_dns(self): - Settings._instance = Settings.get_default_settings() - bot_id = '56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE51855D34D34D37CB5' - tox_id = tox_dns('groupbot@toxme.io') - assert tox_id == bot_id - - def test_dns2(self): - Settings._instance = Settings.get_default_settings() - bot_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6' - tox_id = tox_dns('echobot@toxme.io') - assert tox_id == bot_id - - -class TestEncryption: - - def test_encr_decr(self): - tox = tox_factory() - data = tox.get_savedata() - lib = encr.ToxES() - for password in ('easypassword', 'njvnFjfn7vaGGV6', 'toxygen'): - lib.set_password(password) - copy_data = data[:] - new_data = lib.pass_encrypt(data) - assert lib.is_data_encrypted(new_data) - new_data = lib.pass_decrypt(new_data) - assert copy_data == new_data - - -class TestSmileys: - - def test_loading(self): - settings = {'smiley_pack': 'default', 'smileys': True} - sm = SmileyLoader(settings) - assert sm.get_smileys_path() is not None - l = sm.get_packs_list() - assert len(l) == 4 - - -def create_singletons(): - folder = util.curr_directory() + '/abc' - Settings._instance = Settings.get_default_settings() - if not os.path.exists(folder): - os.makedirs(folder) - ProfileManager(folder, 'test') - - -def create_friend(name, status_message, number, tox_id): - friend = Friend(None, number, name, status_message, None, tox_id) - return friend - - -def create_random_friend(): - name, status_message, number = 'Friend', 'I am friend!', 0 - tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6' - friend = create_friend(name, status_message, number, tox_id) - return friend - - -class TestFriend: - - def test_friend_creation(self): - create_singletons() - name, status_message, number = 'Friend', 'I am friend!', 0 - tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6' - friend = create_friend(name, status_message, number, tox_id) - assert friend.name == name - assert friend.tox_id == tox_id - assert friend.status_message == status_message - assert friend.number == number - - def test_friend_corr(self): - create_singletons() - friend = create_random_friend() - t = time.time() - friend.append_message(InfoMessage('Info message', t)) - friend.append_message(TextMessage('Hello! It is test!', MESSAGE_OWNER['ME'], t + 0.001, 0)) - friend.append_message(TextMessage('Hello!', MESSAGE_OWNER['FRIEND'], t + 0.002, 0)) - assert friend.get_last_message_text() == 'Hello! It is test!' - assert len(friend.get_corr()) == 3 - assert len(friend.get_corr_for_saving()) == 2 - friend.append_message(TextMessage('Not sent', MESSAGE_OWNER['NOT_SENT'], t + 0.002, 0)) - arr = friend.get_unsent_messages_for_saving() - assert len(arr) == 1 - assert arr[0][0] == 'Not sent' - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - FILE_TRANSFER_STATE['RUNNING'], - 100, 'file_name', friend.number, 0) - friend.append_message(tm) - friend.clear_corr() - assert len(friend.get_corr()) == 1 - assert len(friend.get_corr_for_saving()) == 0 - friend.append_message(TextMessage('Hello! It is test!', MESSAGE_OWNER['ME'], t, 0)) - assert len(friend.get_corr()) == 2 - assert len(friend.get_corr_for_saving()) == 1 - - def test_history_search(self): - create_singletons() - friend = create_random_friend() - message = 'Hello! It is test!' - friend.append_message(TextMessage(message, MESSAGE_OWNER['ME'], time.time(), 0)) - last_message = friend.get_last_message_text() - assert last_message == message - result = friend.search_string('e[m|s]') - assert result is not None - result = friend.search_string('tox') - assert result is None - - -class TestHistory: - - def test_history(self): - create_singletons() - db_name = 'my_name' - name, status_message, number = 'Friend', 'I am friend!', 0 - tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6' - friend = create_friend(name, status_message, number, tox_id) - history = History(db_name) - history.add_friend_to_db(friend.tox_id) - assert history.friend_exists_in_db(friend.tox_id) - text_message = 'Test!' - t = time.time() - friend.append_message(TextMessage(text_message, MESSAGE_OWNER['ME'], t, 0)) - messages = friend.get_corr_for_saving() - history.save_messages_to_db(friend.tox_id, messages) - getter = history.messages_getter(friend.tox_id) - messages = getter.get_all() - assert len(messages) == 1 - assert messages[0][0] == text_message - assert messages[0][1] == MESSAGE_OWNER['ME'] - assert messages[0][-1] == 0 - history.delete_message(friend.tox_id, t) - getter = history.messages_getter(friend.tox_id) - messages = getter.get_all() - assert len(messages) == 0 - history.delete_friend_from_db(friend.tox_id) - assert not history.friend_exists_in_db(friend.tox_id) From 43302b0130963d4fc36eed25e1c9ec9cd033c4bb Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 23 May 2018 21:32:14 +0300 Subject: [PATCH 061/217] test import fixed --- tests/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests.py b/tests/tests.py index 130f56d..54e374e 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,4 +1,4 @@ -from middleware.tox_factory import * +from toxygen.middleware.tox_factory import * # TODO: add new tests From eb9ab56c6ec2037a24dfba970059a61ed072ff68 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 24 May 2018 15:01:17 +0300 Subject: [PATCH 062/217] fix for deleting last contact in list --- toxygen/contacts/contacts_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 02028d7..c99d439 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -464,8 +464,8 @@ class ContactsManager(ToxSave): self._history.delete_history(contact) def _delete_contact(self, num): + if num == self._active_contact: # active friend was deleted + self.set_active(0 if len(self._contacts) - 1 else -1) del self._contacts[num] self._screen.friends_list.takeItem(num) - if num == self._active_contact: # active friend was deleted - self.set_active(0 if len(self._contacts) else -1) self._save_profile() From c97fb6b467c5822d8cd50e34d8f47827b5eb641f Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 24 May 2018 15:20:21 +0300 Subject: [PATCH 063/217] minor stickers and smileys window fixes --- toxygen/smileys/smileys.py | 2 +- toxygen/stickers/stickers.py | 4 ++-- toxygen/ui/main_screen.py | 4 ++-- toxygen/ui/widgets_factory.py | 6 ------ 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/toxygen/smileys/smileys.py b/toxygen/smileys/smileys.py index 9027bcb..0391856 100644 --- a/toxygen/smileys/smileys.py +++ b/toxygen/smileys/smileys.py @@ -34,7 +34,7 @@ class SmileyLoader: print('Smiley pack {} loaded'.format(pack_name)) keys, values, self._list = [], [], [] for key, value in tmp.items(): - value = self.get_smileys_path() + value + value = util.join_path(self.get_smileys_path(), value) if value not in values: keys.append(key) values.append(value) diff --git a/toxygen/stickers/stickers.py b/toxygen/stickers/stickers.py index a406b6c..14142c7 100644 --- a/toxygen/stickers/stickers.py +++ b/toxygen/stickers/stickers.py @@ -10,9 +10,9 @@ def load_stickers(): d = util.get_stickers_directory() keys = [x[1] for x in os.walk(d)][0] for key in keys: - path = d + key + '/' + path = util.join_path(d, key) files = filter(lambda f: f.endswith('.png'), os.listdir(path)) - files = map(lambda f: str(path + f), files) + files = map(lambda f: util.join_path(path, f), files) result.extend(files) return result diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index d39cc91..415d8a5 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -570,8 +570,8 @@ class MainWindow(QtWidgets.QMainWindow): self.menu.hide() if self._contacts_manager.get_curr_contact() is not None: self.smiley = self._widget_factory.create_smiley_window(self) - self.smiley.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), - self.y() + self.height() - 200, + self.smiley.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 200 + self.x(), + self.y() + self.height() - 400, self.smiley.width(), self.smiley.height())) self.smiley.show() diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 4c4f1c6..68583d6 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -22,9 +22,6 @@ class WidgetsFactory: def create_screenshot_window(self, *args): return ScreenShotWindow(self._file_transfer_handler, self._contacts_manager, *args) - def create_smiley_window(self, parent): - return SmileyWindow(parent, self._smiley_loader) - def create_welcome_window(self): return WelcomeScreen(self._settings) @@ -49,9 +46,6 @@ class WidgetsFactory: def create_add_contact_window(self, tox_id): return AddContact(self._settings, self._contacts_manager, tox_id) - def create_welcome_window(self): - return WelcomeScreen(self._settings) - def create_privacy_settings_window(self): return PrivacySettings(self._contacts_manager, self._settings) From 486c13a3d36f7eaefadacb4f3910cb21005db769 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 24 May 2018 21:22:12 +0300 Subject: [PATCH 064/217] Login screen converted, create profile screen fixed --- toxygen/app.py | 33 ++++-- toxygen/ui/create_profile_screen.py | 21 ++-- toxygen/ui/login_screen.py | 109 ++++++------------- toxygen/ui/password_screen.py | 37 ++++--- toxygen/ui/views/create_profile_screen.ui | 38 +++++-- toxygen/ui/views/login_screen.ui | 124 ++++++++++++++++++++++ toxygen/ui/widgets.py | 15 ++- 7 files changed, 261 insertions(+), 116 deletions(-) create mode 100644 toxygen/ui/views/login_screen.ui diff --git a/toxygen/app.py b/toxygen/app.py index 2f18045..05a38d7 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -31,6 +31,7 @@ from network.tox_dns import ToxDns from history.history import History from file_transfers.file_transfers_messages_service import FileTransfersMessagesService from groups.groups_service import GroupsService +from ui.create_profile_screen import CreateProfileScreen import styles.style # TODO: dynamic loading @@ -165,7 +166,8 @@ class App: if result is None: return False if result.is_new_profile(): # create new profile - self._create_new_profile(result.profile_path) + if not self._create_new_profile(result.profile_path): + return False else: # load existing profile self._load_existing_profile(result.profile_path) self._path = result.profile_path @@ -233,16 +235,25 @@ class App: data = self._enter_pass(data) self._tox = self._create_tox(data) - def _create_new_profile(self, profile_path): - name = util.get_profile_name_from_path(profile_path) or 'toxygen_user' + def _create_new_profile(self, profile_name): + result = self._get_create_profile_screen_result() + if result is None: + return False + if result.save_into_default_folder: + profile_path = util.join_path(Settings.get_default_path(), profile_name + '.tox') + else: + profile_path = util.join_path(util.curr_directory(__file__), profile_name + '.tox') if os.path.isfile(profile_path): util_ui.message_box(util_ui.tr('Profile with this name already exists'), util_ui.tr('Error')) - return + return False + name = profile_name or 'toxygen_user' self._tox = tox_factory() self._tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') self._tox.self_set_status_message(b'Toxing on Toxygen') - # TODO: set profile password + self._path = profile_path + if result.password: + self._toxes.set_password(result.password) self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) try: @@ -252,12 +263,22 @@ class App: util.log('Profile creation exception: ' + str(ex)) text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') util_ui.message_box(text, util_ui.tr('Error')) - return + + return False current_language, supported_languages = self._get_languages() if current_language in supported_languages: self._settings['language'] = current_language self._settings.save() + return True + + def _get_create_profile_screen_result(self): + cps = CreateProfileScreen() + cps.show() + self._app.exec_() + + return cps.result + def _save_profile(self, data=None): data = data or self._tox.get_savedata() self._profile_manager.save_profile(data) diff --git a/toxygen/ui/create_profile_screen.py b/toxygen/ui/create_profile_screen.py index 08b85cf..9d7b503 100644 --- a/toxygen/ui/create_profile_screen.py +++ b/toxygen/ui/create_profile_screen.py @@ -29,15 +29,24 @@ class CreateProfileScreen(CenteredWidget, DialogWithResult): uic.loadUi(util.get_views_path('create_profile_screen'), self) self.center() self.createProfile.clicked.connect(self._create_profile) + self.retranslateUi() def retranslateUi(self): - self.defaultFolder.setPlaceholderText(util_ui.tr('Save in default folder')) - self.programFolder.setPlaceholderText(util_ui.tr('Save in program folder')) + self.setWindowTitle(util_ui.tr('New profile settings')) + self.defaultFolder.setText(util_ui.tr('Save in default folder')) + self.programFolder.setText(util_ui.tr('Save in program folder')) + self.password.setPlaceholderText(util_ui.tr('Password')) + self.confirmPassword.setPlaceholderText(util_ui.tr('Confirm password')) self.createProfile.setText(util_ui.tr('Create profile')) - self.passwordLabel.setText(util_ui.tr('Password:')) + self.passwordLabel.setText(util_ui.tr('Password (at least 8 symbols):')) def _create_profile(self): - if self.password.text() != self.confirmPassword.text(): - return # TODO : error - result = CreateProfileScreenResult(self.defaultFolder.isChecked(), self.password.text()) + password = self.password.text() + if password != self.confirmPassword.text(): + self.errorLabel.setText(util_ui.tr('Passwords do not match')) + return + if 0 < len(password) < 8: + self.errorLabel.setText(util_ui.tr('Password must be at least 8 symbols')) + return + result = CreateProfileScreenResult(self.defaultFolder.isChecked(), password) self.close_with_result(result) diff --git a/toxygen/ui/login_screen.py b/toxygen/ui/login_screen.py index bbafef9..946dc7a 100644 --- a/toxygen/ui/login_screen.py +++ b/toxygen/ui/login_screen.py @@ -1,20 +1,10 @@ from ui.widgets import * +from PyQt5 import uic +import utils.util as util +import utils.ui as util_ui import os.path -class NickEdit(LineEdit): - - def __init__(self, parent): - super().__init__(parent) - self.parent = parent - - def keyPressEvent(self, event): - if event.key() == QtCore.Qt.Key_Return: - self.parent.create_profile() - else: - super(NickEdit, self).keyPressEvent(event) - - class LoginScreenResult: def __init__(self, profile_path, load_as_default, password=None): @@ -46,75 +36,42 @@ class LoginScreen(CenteredWidget, DialogWithResult): def __init__(self): CenteredWidget.__init__(self) DialogWithResult.__init__(self) - self.initUI() + uic.loadUi(util.get_views_path('login_screen'), self) self.center() self._profiles = [] - - def initUI(self): - self.resize(400, 200) - self.setMinimumSize(QtCore.QSize(400, 200)) - self.setMaximumSize(QtCore.QSize(400, 200)) - self.new_profile = QtWidgets.QPushButton(self) - self.new_profile.setGeometry(QtCore.QRect(20, 150, 171, 27)) - self.new_profile.clicked.connect(self.create_profile) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(20, 70, 101, 17)) - self.new_name = NickEdit(self) - self.new_name.setGeometry(QtCore.QRect(20, 100, 171, 31)) - self.load_profile = QtWidgets.QPushButton(self) - self.load_profile.setGeometry(QtCore.QRect(220, 150, 161, 27)) - self.load_profile.clicked.connect(self.load_existing_profile) - self.default = QtWidgets.QCheckBox(self) - self.default.setGeometry(QtCore.QRect(220, 110, 131, 22)) - self.groupBox = QtWidgets.QGroupBox(self) - self.groupBox.setGeometry(QtCore.QRect(210, 40, 181, 151)) - self.comboBox = QtWidgets.QComboBox(self.groupBox) - self.comboBox.setGeometry(QtCore.QRect(10, 30, 161, 27)) - self.groupBox_2 = QtWidgets.QGroupBox(self) - self.groupBox_2.setGeometry(QtCore.QRect(10, 40, 191, 151)) - self.toxygen = QtWidgets.QLabel(self) - self.toxygen.setGeometry(QtCore.QRect(160, 8, 90, 25)) - self.groupBox.raise_() - self.groupBox_2.raise_() - self.comboBox.raise_() - self.default.raise_() - self.load_profile.raise_() - self.new_name.raise_() - self.new_profile.raise_() - font = QtGui.QFont() - font.setFamily("Impact") - font.setPointSize(16) - self.toxygen.setFont(font) - self.toxygen.setObjectName("toxygen") - self.retranslateUi() - QtCore.QMetaObject.connectSlotsByName(self) + self._update_ui() def retranslateUi(self): - self.new_name.setPlaceholderText(QtWidgets.QApplication.translate("login", "Profile name")) - self.setWindowTitle(QtWidgets.QApplication.translate("login", "Log in")) - self.new_profile.setText(QtWidgets.QApplication.translate("login", "Create")) - self.label.setText(QtWidgets.QApplication.translate("login", "Profile name:")) - self.load_profile.setText(QtWidgets.QApplication.translate("login", "Load profile")) - self.default.setText(QtWidgets.QApplication.translate("login", "Use as default")) - self.groupBox.setTitle(QtWidgets.QApplication.translate("login", "Load existing profile")) - self.groupBox_2.setTitle(QtWidgets.QApplication.translate("login", "Create new profile")) - self.toxygen.setText(QtWidgets.QApplication.translate("login", "toxygen")) - - def create_profile(self): - self.type = 1 - self.name = self.new_name.text() - self.close() - - def load_existing_profile(self): - index = self.comboBox.currentIndex() - load_as_default = self.default.isChecked() - path = os.path.join(self._profiles[index][0], self._profiles[index][1] + '.tox') - result = LoginScreenResult(path, load_as_default) - self.close_with_result(result) + self.setWindowTitle(util_ui.tr('Log in')) + self.profileNameLineEdit.setPlaceholderText(util_ui.tr('Profile name')) + self.createProfilePushButton.setText(util_ui.tr('Create')) + self.loadProfilePushButton.setText(util_ui.tr('Load profile')) + self.defaultProfileCheckBox.setText(util_ui.tr('Use as default')) + self.existingProfileGroupBox.setTitle(util_ui.tr('Load existing profile')) + self.newProfileGroupBox.setTitle(util_ui.tr('Create new profile')) def update_select(self, profiles): profiles = sorted(profiles, key=lambda p: p[1]) self._profiles = list(profiles) - self.comboBox.addItems(list(map(lambda p: p[1], profiles))) - self.load_profile.setEnabled(len(profiles) > 0) + self.profilesComboBox.addItems(list(map(lambda p: p[1], profiles))) + self.loadProfilePushButton.setEnabled(len(profiles) > 0) + def _update_ui(self): + self.profileNameLineEdit = LineEditWithEnterSupport(self._create_profile, self) + self.profileNameLineEdit.setGeometry(QtCore.QRect(20, 100, 170, 30)) + self.retranslateUi() + self.createProfilePushButton.clicked.connect(self._create_profile) + self.loadProfilePushButton.clicked.connect(self._load_existing_profile) + + def _create_profile(self): + path = self.profileNameLineEdit.text() + load_as_default = self.defaultProfileCheckBox.isChecked() + result = LoginScreenResult(path, load_as_default) + self.close_with_result(result) + + def _load_existing_profile(self): + index = self.profilesComboBox.currentIndex() + load_as_default = self.defaultProfileCheckBox.isChecked() + path = util.join_path(self._profiles[index][0], self._profiles[index][1] + '.tox') + result = LoginScreenResult(path, load_as_default) + self.close_with_result(result) diff --git a/toxygen/ui/password_screen.py b/toxygen/ui/password_screen.py index bd6c146..bbae7ff 100644 --- a/toxygen/ui/password_screen.py +++ b/toxygen/ui/password_screen.py @@ -1,19 +1,20 @@ from ui.widgets import CenteredWidget, LineEdit, DialogWithResult from PyQt5 import QtCore, QtWidgets +import utils.ui as util_ui class PasswordArea(LineEdit): def __init__(self, parent): - super(PasswordArea, self).__init__(parent) - self.parent = parent + super().__init__(parent) + self._parent = parent self.setEchoMode(QtWidgets.QLineEdit.Password) def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Return: - self.parent.button_click() + self._parent.button_click() else: - super(PasswordArea, self).keyPressEvent(event) + super().keyPressEvent(event) class PasswordScreenBase(CenteredWidget, DialogWithResult): @@ -37,7 +38,7 @@ class PasswordScreenBase(CenteredWidget, DialogWithResult): self.button = QtWidgets.QPushButton(self) self.button.setGeometry(QtCore.QRect(30, 90, 300, 30)) - self.button.setText('OK') + self.button.setText(util_ui.tr('OK')) self.button.clicked.connect(self.button_click) self.warning = QtWidgets.QLabel(self) @@ -59,15 +60,15 @@ class PasswordScreenBase(CenteredWidget, DialogWithResult): super(PasswordScreenBase, self).keyPressEvent(event) def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("pass", "Enter password")) - self.enter_pass.setText(QtWidgets.QApplication.translate("pass", "Password:")) - self.warning.setText(QtWidgets.QApplication.translate("pass", "Incorrect password")) + self.setWindowTitle(util_ui.tr('Enter password')) + self.enter_pass.setText(util_ui.tr('Password:')) + self.warning.setText(util_ui.tr('Incorrect password')) class PasswordScreen(PasswordScreenBase): def __init__(self, encrypt, data): - super(PasswordScreen, self).__init__(encrypt) + super().__init__(encrypt) self._data = data def button_click(self): @@ -129,16 +130,15 @@ class SetProfilePasswordScreen(CenteredWidget): self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("PasswordScreen", "Profile password")) + self.setWindowTitle(util_ui.tr('Profile password')) self.password.setPlaceholderText( - QtWidgets.QApplication.translate("PasswordScreen", "Password (at least 8 symbols)")) + util_ui.tr('Password (at least 8 symbols)')) self.confirm_password.setPlaceholderText( - QtWidgets.QApplication.translate("PasswordScreen", "Confirm password")) + util_ui.tr('Confirm password')) self.set_password.setText( - QtWidgets.QApplication.translate("PasswordScreen", "Set password")) - self.not_match.setText(QtWidgets.QApplication.translate("PasswordScreen", "Passwords do not match")) - self.warning.setText( - QtWidgets.QApplication.translate("PasswordScreen", "There is no way to recover lost passwords")) + util_ui.tr('Set password')) + self.not_match.setText(util_ui.tr('Passwords do not match')) + self.warning.setText(util_ui.tr('There is no way to recover lost passwords')) def new_password(self): if self.password.text() == self.confirm_password.text(): @@ -146,9 +146,8 @@ class SetProfilePasswordScreen(CenteredWidget): self._encrypt.set_password(self.password.text()) self.close() else: - self.not_match.setText( - QtWidgets.QApplication.translate("PasswordScreen", "Password must be at least 8 symbols")) + self.not_match.setText(util_ui.tr('Password must be at least 8 symbols')) self.not_match.setVisible(True) else: - self.not_match.setText(QtWidgets.QApplication.translate("PasswordScreen", "Passwords do not match")) + self.not_match.setText(util_ui.tr('Passwords do not match')) self.not_match.setVisible(True) diff --git a/toxygen/ui/views/create_profile_screen.ui b/toxygen/ui/views/create_profile_screen.ui index f9d3241..5407dca 100644 --- a/toxygen/ui/views/create_profile_screen.ui +++ b/toxygen/ui/views/create_profile_screen.ui @@ -7,19 +7,19 @@ 0 0 400 - 300 + 380 400 - 300 + 380 400 - 300 + 380 @@ -29,7 +29,7 @@ 30 - 240 + 290 341 51 @@ -47,6 +47,9 @@ 41 + + QLineEdit::Password + @@ -57,14 +60,17 @@ 41 + + QLineEdit::Password + 30 100 - 67 - 17 + 330 + 20 @@ -76,7 +82,7 @@ 30 10 - 112 + 330 23 @@ -92,7 +98,7 @@ 30 50 - 112 + 330 23 @@ -100,6 +106,22 @@ RadioButton + + + + 30 + 250 + 341 + 30 + + + + + + + Qt::AlignCenter + + diff --git a/toxygen/ui/views/login_screen.ui b/toxygen/ui/views/login_screen.ui new file mode 100644 index 0000000..a4c205c --- /dev/null +++ b/toxygen/ui/views/login_screen.ui @@ -0,0 +1,124 @@ + + + loginScreen + + + + 0 + 0 + 400 + 200 + + + + Form + + + + + 0 + 5 + 401 + 30 + + + + + Garuda + 16 + 75 + true + + + + Toxygen + + + Qt::AlignCenter + + + + + + 10 + 40 + 180 + 150 + + + + GroupBox + + + Qt::AlignCenter + + + + + 20 + 110 + 150 + 27 + + + + PushButton + + + + + + + 210 + 40 + 180 + 150 + + + + GroupBox + + + Qt::AlignCenter + + + + + 10 + 40 + 160 + 27 + + + + + + + 10 + 75 + 160 + 27 + + + + CheckBox + + + + + + 20 + 110 + 150 + 27 + + + + PushButton + + + + + + + diff --git a/toxygen/ui/widgets.py b/toxygen/ui/widgets.py index ec6c3e4..c25607b 100644 --- a/toxygen/ui/widgets.py +++ b/toxygen/ui/widgets.py @@ -52,7 +52,7 @@ class DialogWithResult(QtWidgets.QWidget): class LineEdit(QtWidgets.QLineEdit): def __init__(self, parent=None): - super(LineEdit, self).__init__(parent) + super().__init__(parent) def contextMenuEvent(self, event): menu = create_menu(self.createStandardContextMenu()) @@ -181,3 +181,16 @@ class MultilineEdit(CenteredWidget): def button_click(self): self.save(self.edit.toPlainText()) self.close() + + +class LineEditWithEnterSupport(LineEdit): + + def __init__(self, enter_action, parent=None): + super().__init__(parent) + self._action = enter_action + + def keyPressEvent(self, event): + if event.key() == QtCore.Qt.Key_Return: + self._action() + else: + super().keyPressEvent(event) From 439ce30e6ec0c83fdc79984b249293dadc8bbd3e Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 24 May 2018 21:43:34 +0300 Subject: [PATCH 065/217] reconnection fixes --- toxygen/app.py | 51 ++++++++++++++++------------ toxygen/contacts/contacts_manager.py | 9 +++++ toxygen/contacts/profile.py | 12 +++---- toxygen/ui/main_screen.py | 1 + 4 files changed, 45 insertions(+), 28 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 05a38d7..29619fd 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -43,7 +43,7 @@ class App: self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = self._tox_dns = None - self._group_factory = self._groups_service = None + self._group_factory = self._groups_service = self._profile = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] self._path = path_to_profile @@ -192,7 +192,7 @@ class App: # Threads # ----------------------------------------------------------------------------------------------------------------- - def _start_threads(self): + def _start_threads(self, initial_start=True): # init thread self._init = threads.InitThread(self._tox, self._plugin_loader, self._settings) self._init.start() @@ -203,15 +203,17 @@ class App: self._av_loop = threads.ToxAVIterateThread(self._tox.AV) self._av_loop.start() - threads.start_file_transfer_thread() + if initial_start: + threads.start_file_transfer_thread() - def _stop_threads(self): + def _stop_threads(self, is_app_closing=True): self._init.stop_thread() self._av_loop.stop_thread() self._main_loop.stop_thread() - threads.stop_file_transfer_thread() + if is_app_closing: + threads.stop_file_transfer_thread() # ----------------------------------------------------------------------------------------------------------------- # Profiles @@ -304,21 +306,24 @@ class App: Create new tox instance (new network settings) :return: tox instance """ - self._stop_threads() + self._stop_threads(False) data = self._tox.get_savedata() self._save_profile(data) del self._tox # create new tox instance self._tox = self._create_tox(data) - self._start_threads() + self._start_threads(False) tox_savers = [self._friend_factory, self._group_factory, self._plugin_loader, self._contacts_manager, - self._contacts_provider, self._messenger, self._file_transfer_handler, self._groups_service] + self._contacts_provider, self._messenger, self._file_transfer_handler, self._groups_service, + self._profile] for tox_saver in tox_savers: tox_saver.set_tox(self._tox) - self._calls_manager.set_toxav(self._tox.AV) - return self._tox + self._calls_manager.set_toxav(self._tox.AV) + self._contacts_manager.update_friends_numbers() + + self._init_callbacks() def _create_dependencies(self): self._smiley_loader = SmileyLoader(self._settings) @@ -331,8 +336,8 @@ class App: self._tox, db, contact_items_factory) self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory) - profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) - self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) + self._profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) + self._plugin_loader = PluginLoader(self._tox, self._toxes, self._profile, self._settings) history = None messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, self._ms, lambda m: history.delete_message(m)) @@ -343,28 +348,25 @@ class App: history.set_contacts_manager(self._contacts_manager) self._calls_manager = CallsManager(self._tox.AV, self._settings, self._ms, self._contacts_manager) self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, - self._contacts_provider, messages_items_factory, profile, self._calls_manager) + self._contacts_provider, messages_items_factory, self._profile, self._calls_manager) file_transfers_message_service = FileTransfersMessagesService(self._contacts_manager, messages_items_factory, - profile, self._ms) + self._profile, self._ms) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider, - file_transfers_message_service, profile) + file_transfers_message_service, self._profile) messages_items_factory.set_file_transfers_handler(self._file_transfer_handler) self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms) - widgets_factory = WidgetsFactory(self._settings, profile, self._profile_manager, self._contacts_manager, + widgets_factory = WidgetsFactory(self._settings, self._profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, self._toxes, self._version, self._groups_service, history) - self._tray = tray.init_tray(profile, self._settings, self._ms, self._toxes) - self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, + self._tray = tray.init_tray(self._profile, self._settings, self._ms, self._toxes) + self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, self._profile, self._plugin_loader, self._file_transfer_handler, history, self._calls_manager, self._groups_service) self._tray.show() self._ms.show() - # callbacks initialization - callbacks.init_callbacks(self._tox, profile, self._settings, self._plugin_loader, self._contacts_manager, - self._calls_manager, self._file_transfer_handler, self._ms, self._tray, - self._messenger, self._groups_service, self._contacts_provider) + self._init_callbacks() def _try_to_update(self): updating = updater.start_update_if_needed(self._version, self._settings) @@ -376,3 +378,8 @@ class App: def _create_tox(self, data): return tox_factory(data, self._settings) + + def _init_callbacks(self): + callbacks.init_callbacks(self._tox, self._profile, self._settings, self._plugin_loader, self._contacts_manager, + self._calls_manager, self._file_transfer_handler, self._ms, self._tray, + self._messenger, self._groups_service, self._contacts_provider) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index c99d439..546f04d 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -392,6 +392,15 @@ class ContactsManager(ToxSave): def can_send_typing_notification(self): return self._settings['typing_notifications'] and self._active_contact + 1 + # ----------------------------------------------------------------------------------------------------------------- + # Contacts numbers update + # ----------------------------------------------------------------------------------------------------------------- + + def update_friends_numbers(self): + for friend in self._contact_provider.get_all_friends(): + friend.number = self._tox.friend_by_public_key(friend.tox_id) + self.update_filtration() + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 469b8fe..3529be4 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -1,9 +1,10 @@ from contacts import basecontact import random import threading +import common.tox_save as tox_save -class Profile(basecontact.BaseContact): +class Profile(basecontact.BaseContact, tox_save.ToxSave): """ Profile of current toxygen user. Contains friends list, tox instance """ @@ -18,9 +19,9 @@ class Profile(basecontact.BaseContact): tox.self_get_status_message(), screen.user_info, tox.self_get_address()) + tox_save.ToxSave.__init__(self, tox) self._screen = screen self._messages = screen.messages - self._tox = tox self._contacts_provider = contacts_provider self._reset_action = reset_action self._waiting_for_reconnection = False @@ -71,14 +72,13 @@ class Profile(basecontact.BaseContact): """ Recreate tox instance """ - del self._tox - self._tox = self._reset_action() self.status = None + self._reset_action() def _reconnect(self): self._waiting_for_reconnection = False - contacts = self._contacts_provider.get_all() - if self.status is None or all(list(map(lambda x: x.status is None, contacts))) and len(contacts): + contacts = self._contacts_provider.get_all_friends() + if self.status is None or (all(list(map(lambda x: x.status is None, contacts))) and len(contacts)): self._waiting_for_reconnection = True self.restart() self._timer = threading.Timer(50, self._reconnect) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 415d8a5..652bbe3 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -212,6 +212,7 @@ class MainWindow(QtWidgets.QMainWindow): self.search_label.setPixmap(pixmap) self.contact_name = LineEdit(Form) + self.contact_name.setObjectName('contact_name') self.contact_name.setGeometry(QtCore.QRect(0, 0, 150, 25)) self.contact_name.textChanged.connect(self.filtering) From 370716015b30b8f6908ba47ee829a96474f78626 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 24 May 2018 21:55:44 +0300 Subject: [PATCH 066/217] groups numbers update --- toxygen/contacts/contacts_manager.py | 20 ++++++++++++++++---- toxygen/groups/groups_service.py | 3 +-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 546f04d..74452e1 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -205,14 +205,17 @@ class ContactsManager(ToxSave): self.filtration_and_sorting(self._sorting, self._filter_string) # ----------------------------------------------------------------------------------------------------------------- - # Friend getters + # Contact getters # ----------------------------------------------------------------------------------------------------------------- def get_friend_by_number(self, number): - return list(filter(lambda x: x.number == number and type(x) is Friend, self._contacts))[0] + return list(filter(lambda c: c.number == number and type(c) is Friend, self._contacts))[0] def get_group_by_number(self, number): - return list(filter(lambda x: x.number == number and type(x) is GroupChat, self._contacts))[0] + return list(filter(lambda c: c.number == number and type(c) is GroupChat, self._contacts))[0] + + def get_contact_by_tox_id(self, tox_id): + return list(filter(lambda c: c.tox_id == tox_id, self._contacts))[0] def get_last_message(self): if self._active_contact + 1: @@ -401,6 +404,14 @@ class ContactsManager(ToxSave): friend.number = self._tox.friend_by_public_key(friend.tox_id) self.update_filtration() + def update_groups_numbers(self): + groups = self._contact_provider.get_all_groups() + for i in range(len(groups)): + chat_id = self._tox.group_get_chat_id(i) + group = self.get_contact_by_tox_id(chat_id) + group.number = i + self.update_filtration() + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- @@ -410,7 +421,7 @@ class ContactsManager(ToxSave): self._load_groups() if len(self._contacts): self.set_active(0) - self.filtration_and_sorting(self._sorting) + self.update_filtration() def _load_friends(self): self._contacts.extend(self._contact_provider.get_all_friends()) @@ -475,6 +486,7 @@ class ContactsManager(ToxSave): def _delete_contact(self, num): if num == self._active_contact: # active friend was deleted self.set_active(0 if len(self._contacts) - 1 else -1) + self._contact_provider.remove_contact_from_cache(self._contacts[num].tox_id) del self._contacts[num] self._screen.friends_list.takeItem(num) self._save_profile() diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 9b4287d..0e9bef7 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -37,10 +37,9 @@ class GroupsService(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def leave_group(self, group_number): - group = self._get_group(group_number) self._tox.group_leave(group_number) self._contacts_manager.delete_group(group_number) - self._contacts_provider.remove_contact_from_cache(group.tox_id) + self._contacts_manager.update_groups_numbers() # ----------------------------------------------------------------------------------------------------------------- # Group invites From 13b2d17786d1e1f5a1ade312573fba9ac9ff60be Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 24 May 2018 23:58:39 +0300 Subject: [PATCH 067/217] notifications and audio settings views converted --- toxygen/ui/create_profile_screen.py | 4 +- toxygen/ui/groups_widgets.py | 13 +- toxygen/ui/login_screen.py | 20 +-- toxygen/ui/menu.py | 118 ++++++------------ toxygen/ui/views/audio_settings_screen.ui | 87 +++++++++++++ toxygen/ui/views/create_profile_screen.ui | 18 +-- .../ui/views/notifications_settings_screen.ui | 71 +++++++++++ 7 files changed, 229 insertions(+), 102 deletions(-) create mode 100644 toxygen/ui/views/audio_settings_screen.ui create mode 100644 toxygen/ui/views/notifications_settings_screen.ui diff --git a/toxygen/ui/create_profile_screen.py b/toxygen/ui/create_profile_screen.py index 9d7b503..512c141 100644 --- a/toxygen/ui/create_profile_screen.py +++ b/toxygen/ui/create_profile_screen.py @@ -29,9 +29,9 @@ class CreateProfileScreen(CenteredWidget, DialogWithResult): uic.loadUi(util.get_views_path('create_profile_screen'), self) self.center() self.createProfile.clicked.connect(self._create_profile) - self.retranslateUi() + self._retranslate_ui() - def retranslateUi(self): + def _retranslate_ui(self): self.setWindowTitle(util_ui.tr('New profile settings')) self.defaultFolder.setText(util_ui.tr('Save in default folder')) self.programFolder.setText(util_ui.tr('Save in program folder')) diff --git a/toxygen/ui/groups_widgets.py b/toxygen/ui/groups_widgets.py index 4dc39d5..c6d0248 100644 --- a/toxygen/ui/groups_widgets.py +++ b/toxygen/ui/groups_widgets.py @@ -11,11 +11,14 @@ class CreateGroupScreen(CenteredWidget): self._groups_service = groups_service uic.loadUi(util.get_views_path('create_group_screen'), self) self.center() - self.retranslateUi() + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() self.addGroupButton.clicked.connect(self._create_group) self.groupNameLineEdit.textChanged.connect(self._group_name_changed) - def retranslateUi(self): + def _retranslate_ui(self): self.setWindowTitle(util_ui.tr('Create new group chat')) self.groupNameLabel.setText(util_ui.tr('Group name:')) self.groupTypeLabel.setText(util_ui.tr('Group type:')) @@ -42,11 +45,13 @@ class JoinGroupScreen(CenteredWidget): self._groups_service = groups_service uic.loadUi(util.get_views_path('join_group_screen'), self) self.center() - self.retranslateUi() + + def _update_ui(self): + self._retranslate_ui() self.chatIdLineEdit.textChanged.connect(self._chat_id_changed) self.joinGroupButton.clicked.connect(self._join_group) - def retranslateUi(self): + def _retranslate_ui(self): self.setWindowTitle(util_ui.tr('Join public group chat')) self.chatIdLabel.setText(util_ui.tr('Group ID:')) self.passwordLabel.setText(util_ui.tr('Password:')) diff --git a/toxygen/ui/login_screen.py b/toxygen/ui/login_screen.py index 946dc7a..7198c36 100644 --- a/toxygen/ui/login_screen.py +++ b/toxygen/ui/login_screen.py @@ -41,15 +41,6 @@ class LoginScreen(CenteredWidget, DialogWithResult): self._profiles = [] self._update_ui() - def retranslateUi(self): - self.setWindowTitle(util_ui.tr('Log in')) - self.profileNameLineEdit.setPlaceholderText(util_ui.tr('Profile name')) - self.createProfilePushButton.setText(util_ui.tr('Create')) - self.loadProfilePushButton.setText(util_ui.tr('Load profile')) - self.defaultProfileCheckBox.setText(util_ui.tr('Use as default')) - self.existingProfileGroupBox.setTitle(util_ui.tr('Load existing profile')) - self.newProfileGroupBox.setTitle(util_ui.tr('Create new profile')) - def update_select(self, profiles): profiles = sorted(profiles, key=lambda p: p[1]) self._profiles = list(profiles) @@ -59,7 +50,7 @@ class LoginScreen(CenteredWidget, DialogWithResult): def _update_ui(self): self.profileNameLineEdit = LineEditWithEnterSupport(self._create_profile, self) self.profileNameLineEdit.setGeometry(QtCore.QRect(20, 100, 170, 30)) - self.retranslateUi() + self._retranslate_ui() self.createProfilePushButton.clicked.connect(self._create_profile) self.loadProfilePushButton.clicked.connect(self._load_existing_profile) @@ -75,3 +66,12 @@ class LoginScreen(CenteredWidget, DialogWithResult): path = util.join_path(self._profiles[index][0], self._profiles[index][1] + '.tox') result = LoginScreenResult(path, load_as_default) self.close_with_result(result) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Log in')) + self.profileNameLineEdit.setPlaceholderText(util_ui.tr('Profile name')) + self.createProfilePushButton.setText(util_ui.tr('Create')) + self.loadProfilePushButton.setText(util_ui.tr('Load profile')) + self.defaultProfileCheckBox.setText(util_ui.tr('Use as default')) + self.existingProfileGroupBox.setTitle(util_ui.tr('Load existing profile')) + self.newProfileGroupBox.setTitle(util_ui.tr('Create new profile')) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 2f55bb5..36930dd 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -1,4 +1,4 @@ -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets, uic from user_data.settings import * from utils.util import * from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow @@ -493,50 +493,31 @@ class NotificationsSettings(CenteredWidget): def __init__(self, setttings): super().__init__() self._settings = setttings - self.initUI() + uic.loadUi(get_views_path('notifications_settings_screen'), self) + self._update_ui() self.center() - def initUI(self): - self.setObjectName("notificationsForm") - self.resize(350, 210) - self.setMinimumSize(QtCore.QSize(350, 210)) - self.setMaximumSize(QtCore.QSize(350, 210)) - self.enableNotifications = QtWidgets.QCheckBox(self) - self.enableNotifications.setGeometry(QtCore.QRect(10, 20, 340, 18)) - self.callsSound = QtWidgets.QCheckBox(self) - self.callsSound.setGeometry(QtCore.QRect(10, 170, 340, 18)) - self.soundNotifications = QtWidgets.QCheckBox(self) - self.soundNotifications.setGeometry(QtCore.QRect(10, 70, 340, 18)) - self.groupNotifications = QtWidgets.QCheckBox(self) - self.groupNotifications.setGeometry(QtCore.QRect(10, 120, 340, 18)) - font = QtGui.QFont() - font.setFamily(self._settings['font']) - font.setPointSize(12) - self.callsSound.setFont(font) - self.soundNotifications.setFont(font) - self.enableNotifications.setFont(font) - self.groupNotifications.setFont(font) - self.enableNotifications.setChecked(self._settings['notifications']) - self.soundNotifications.setChecked(self._settings['sound_notifications']) - self.groupNotifications.setChecked(self._settings['group_notifications']) - self.callsSound.setChecked(self._settings['calls_sound']) - self.retranslateUi() - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.setWindowTitle(util_ui.tr("Notification settings")) - self.enableNotifications.setText(util_ui.tr("Enable notifications")) - self.groupNotifications.setText(util_ui.tr("Notify about all messages in groups")) - self.callsSound.setText(util_ui.tr("Enable call\'s sound")) - self.soundNotifications.setText(util_ui.tr("Enable sound notifications")) - def closeEvent(self, *args, **kwargs): - self._settings['notifications'] = self.enableNotifications.isChecked() - self._settings['sound_notifications'] = self.soundNotifications.isChecked() - self._settings['group_notifications'] = self.groupNotifications.isChecked() - self._settings['calls_sound'] = self.callsSound.isChecked() + self._settings['notifications'] = self.notificationsCheckBox.isChecked() + self._settings['sound_notifications'] = self.soundNotificationsCheckBox.isChecked() + self._settings['group_notifications'] = self.groupNotificationsCheckBox.isChecked() + self._settings['calls_sound'] = self.callsSoundCheckBox.isChecked() self._settings.save() + def _update_ui(self): + self.notificationsCheckBox.setChecked(self._settings['notifications']) + self.soundNotificationsCheckBox.setChecked(self._settings['sound_notifications']) + self.groupNotificationsCheckBox.setChecked(self._settings['group_notifications']) + self.callsSoundCheckBox.setChecked(self._settings['calls_sound']) + self._retranslate_ui() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Notifications settings")) + self.notificationsCheckBox.setText(util_ui.tr("Enable notifications")) + self.groupNotificationsCheckBox.setText(util_ui.tr("Notify about all messages in groups")) + self.callsSoundCheckBox.setText(util_ui.tr("Enable call\'s sound")) + self.soundNotificationsCheckBox.setText(util_ui.tr("Enable sound notifications")) + class InterfaceSettings(CenteredWidget): """Interface settings form""" @@ -729,52 +710,35 @@ class AudioSettings(CenteredWidget): def __init__(self, settings): super().__init__() self._settings = settings - self.initUI() - self.retranslateUi() + self._in_indexes = self._out_indexes = None + uic.loadUi(get_views_path('audio_settings_screen'), self) + self._update_ui() self.center() - def initUI(self): - self.setObjectName("audioSettingsForm") - self.resize(400, 150) - self.setMinimumSize(QtCore.QSize(400, 150)) - self.setMaximumSize(QtCore.QSize(400, 150)) - self.in_label = QtWidgets.QLabel(self) - self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20)) - self.out_label = QtWidgets.QLabel(self) - self.out_label.setGeometry(QtCore.QRect(25, 65, 350, 20)) - font = QtGui.QFont() - font.setPointSize(16) - font.setBold(True) - font.setFamily(self._settings['font']) - self.in_label.setFont(font) - self.out_label.setFont(font) - self.input = QtWidgets.QComboBox(self) - self.input.setGeometry(QtCore.QRect(25, 30, 350, 30)) - self.output = QtWidgets.QComboBox(self) - self.output.setGeometry(QtCore.QRect(25, 90, 350, 30)) + def closeEvent(self, event): + self._settings.audio['input'] = self._in_indexes[self.inputDeviceComboBox.currentIndex()] + self._settings.audio['output'] = self._out_indexes[self.outputDeviceComboBox.currentIndex()] + self._settings.save() + + def _update_ui(self): p = pyaudio.PyAudio() - self.in_indexes, self.out_indexes = [], [] + self._in_indexes, self._out_indexes = [], [] for i in range(p.get_device_count()): device = p.get_device_info_by_index(i) if device["maxInputChannels"]: - self.input.addItem(str(device["name"])) - self.in_indexes.append(i) + self.inputDeviceComboBox.addItem(str(device["name"])) + self._in_indexes.append(i) if device["maxOutputChannels"]: - self.output.addItem(str(device["name"])) - self.out_indexes.append(i) - self.input.setCurrentIndex(self.in_indexes.index(self._settings.audio['input'])) - self.output.setCurrentIndex(self.out_indexes.index(self._settings.audio['output'])) - QtCore.QMetaObject.connectSlotsByName(self) + self.outputDeviceComboBox.addItem(str(device["name"])) + self._out_indexes.append(i) + self.inputDeviceComboBox.setCurrentIndex(self._in_indexes.index(self._settings.audio['input'])) + self.outputDeviceComboBox.setCurrentIndex(self._out_indexes.index(self._settings.audio['output'])) + self._retranslate_ui() - def retranslateUi(self): + def _retranslate_ui(self): self.setWindowTitle(util_ui.tr("Audio settings")) - self.in_label.setText(util_ui.tr("Input device:")) - self.out_label.setText(util_ui.tr("Output device:")) - - def closeEvent(self, event): - self._settings.audio['input'] = self.in_indexes[self.input.currentIndex()] - self._settings.audio['output'] = self.out_indexes[self.output.currentIndex()] - self._settings.save() + self.inputDeviceLabel.setText(util_ui.tr("Input device:")) + self.outputDeviceLabel.setText(util_ui.tr("Output device:")) class DesktopAreaSelectionWindow(RubberBandWindow): diff --git a/toxygen/ui/views/audio_settings_screen.ui b/toxygen/ui/views/audio_settings_screen.ui new file mode 100644 index 0000000..a404592 --- /dev/null +++ b/toxygen/ui/views/audio_settings_screen.ui @@ -0,0 +1,87 @@ + + + Form + + + + 0 + 0 + 315 + 218 + + + + + 315 + 218 + + + + + 315 + 218 + + + + Form + + + + + 30 + 10 + 261 + 30 + + + + + 16 + + + + TextLabel + + + + + + 30 + 100 + 261 + 30 + + + + + 16 + + + + TextLabel + + + + + + 30 + 50 + 255 + 41 + + + + + + + 30 + 140 + 255 + 41 + + + + + + + diff --git a/toxygen/ui/views/create_profile_screen.ui b/toxygen/ui/views/create_profile_screen.ui index 5407dca..bfffee5 100644 --- a/toxygen/ui/views/create_profile_screen.ui +++ b/toxygen/ui/views/create_profile_screen.ui @@ -7,19 +7,19 @@ 0 0 400 - 380 + 340 400 - 380 + 340 400 - 380 + 340 @@ -29,7 +29,7 @@ 30 - 290 + 270 341 51 @@ -42,7 +42,7 @@ 30 - 190 + 170 341 41 @@ -55,7 +55,7 @@ 30 - 140 + 120 341 41 @@ -68,7 +68,7 @@ 30 - 100 + 80 330 20 @@ -97,7 +97,7 @@ 30 - 50 + 40 330 23 @@ -110,7 +110,7 @@ 30 - 250 + 220 341 30 diff --git a/toxygen/ui/views/notifications_settings_screen.ui b/toxygen/ui/views/notifications_settings_screen.ui new file mode 100644 index 0000000..67e2dc6 --- /dev/null +++ b/toxygen/ui/views/notifications_settings_screen.ui @@ -0,0 +1,71 @@ + + + Form + + + + 0 + 0 + 320 + 201 + + + + Form + + + + + 20 + 20 + 271 + 41 + + + + CheckBox + + + + + + 20 + 60 + 271 + 41 + + + + CheckBox + + + + + + 20 + 100 + 271 + 41 + + + + CheckBox + + + + + + 20 + 140 + 271 + 41 + + + + CheckBox + + + + + + From 238f7e367a45da2197e558c80685d762d0d9a587 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 25 May 2018 00:16:21 +0300 Subject: [PATCH 068/217] update settings screen converted --- toxygen/app.py | 2 +- toxygen/ui/main_screen.py | 5 +- toxygen/ui/menu.py | 61 ++++++++------------ toxygen/ui/views/update_settings_screen.ui | 67 ++++++++++++++++++++++ toxygen/ui/widgets_factory.py | 2 +- 5 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 toxygen/ui/views/update_settings_screen.ui diff --git a/toxygen/app.py b/toxygen/app.py index 29619fd..60bb8bf 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -361,7 +361,7 @@ class App: self._tray = tray.init_tray(self._profile, self._settings, self._ms, self._toxes) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, self._profile, self._plugin_loader, self._file_transfer_handler, history, self._calls_manager, - self._groups_service) + self._groups_service, self._toxes) self._tray.show() self._ms.show() diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 652bbe3..e88437a 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -17,13 +17,13 @@ class MainWindow(QtWidgets.QMainWindow): self._plugins_loader = None self.setAcceptDrops(True) self._saved = False - self._profile = None + self._profile = self._toxes = None self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None self._should_show_group_peers_list = False self.initUI() def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader, - file_transfer_handler, history_loader, calls_manager, groups_service): + file_transfer_handler, history_loader, calls_manager, groups_service, toxes): self._widget_factory = widget_factory self._tray = tray self._contacts_manager = contacts_manager @@ -33,6 +33,7 @@ class MainWindow(QtWidgets.QMainWindow): self._history_loader = history_loader self._calls_manager = calls_manager self._groups_service = groups_service + self._toxes = toxes self._contacts_manager.active_contact_changed.add_callback(self._new_contact_selected) self.messageEdit.set_messenger(messenger) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 36930dd..fff95a9 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -946,55 +946,44 @@ class UpdateSettings(CenteredWidget): Updates settings form """ - def __init__(self, settings): + def __init__(self, settings, version): super().__init__() self._settings = settings - self.initUI() + self._version = version + uic.loadUi(get_views_path('update_settings_screen'), self) + self._update_ui() self.center() - def initUI(self): - self.setObjectName("updateSettingsForm") - self.resize(400, 150) - self.setMinimumSize(QtCore.QSize(400, 120)) - self.setMaximumSize(QtCore.QSize(400, 120)) - self.in_label = QtWidgets.QLabel(self) - self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20)) - font = QtGui.QFont() - font.setPointSize(16) - font.setBold(True) - font.setFamily(self._settings['font']) - self.in_label.setFont(font) - self.autoupdate = QtWidgets.QComboBox(self) - self.autoupdate.setGeometry(QtCore.QRect(25, 30, 350, 30)) - self.button = QtWidgets.QPushButton(self) - self.button.setGeometry(QtCore.QRect(25, 70, 350, 30)) - self.button.setEnabled(self._settings['update']) - self.button.clicked.connect(self.update_client) - - self.retranslateUi() - self.autoupdate.setCurrentIndex(self._settings['update']) - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.setWindowTitle(util_ui.tr("Update settings")) - self.in_label.setText(util_ui.tr("Select update mode:")) - self.button.setText(util_ui.tr("Update Toxygen")) - self.autoupdate.addItem(util_ui.tr("Disabled")) - self.autoupdate.addItem(util_ui.tr("Manual")) - self.autoupdate.addItem(util_ui.tr("Auto")) - def closeEvent(self, event): - self._settings['update'] = self.autoupdate.currentIndex() + self._settings['update'] = self.updateModeComboBox.currentIndex() self._settings.save() - def update_client(self): + def _update_ui(self): + self.updatePushButton.clicked.connect(self._update_client) + self.updateModeComboBox.currentIndexChanged.connect(self._update_mode_changed) + self._retranslate_ui() + self.updateModeComboBox.setCurrentIndex(self._settings['update']) + + def _update_mode_changed(self): + index = self.updateModeComboBox.currentIndex() + self.updatePushButton.setEnabled(index > 0) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Update settings")) + self.updateModeLabel.setText(util_ui.tr("Select update mode:")) + self.updatePushButton.setText(util_ui.tr("Update Toxygen")) + self.updateModeComboBox.addItem(util_ui.tr("Disabled")) + self.updateModeComboBox.addItem(util_ui.tr("Manual")) + self.updateModeComboBox.addItem(util_ui.tr("Auto")) + + def _update_client(self): if not updater.connection_available(): util_ui.message_box(util_ui.tr('Problems with internet connection'), util_ui.tr("Error")) return if not updater.updater_available(): util_ui.message_box(util_ui.tr('Updater not found'), util_ui.tr("Error")) return - version = updater.check_for_updates() + version = updater.check_for_updates(self._version, self._settings) if version is not None: updater.download(version) util_ui.close_all_windows() diff --git a/toxygen/ui/views/update_settings_screen.ui b/toxygen/ui/views/update_settings_screen.ui new file mode 100644 index 0000000..76e7c57 --- /dev/null +++ b/toxygen/ui/views/update_settings_screen.ui @@ -0,0 +1,67 @@ + + + Form + + + + 0 + 0 + 400 + 120 + + + + + 400 + 120 + + + + + 400 + 120 + + + + Form + + + + + 25 + 5 + 350 + 20 + + + + TextLabel + + + + + + 25 + 30 + 350 + 30 + + + + + + + 25 + 70 + 350 + 30 + + + + PushButton + + + + + + diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 68583d6..ead09ce 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -38,7 +38,7 @@ class WidgetsFactory: return VideoSettings(self._settings) def create_update_settings_window(self): - return UpdateSettings(self._settings) + return UpdateSettings(self._settings, self._version) def create_plugins_settings_window(self): return PluginsSettings(self._plugin_loader) From 423bda93c691ebd959b99ecb363f971848778c68 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 25 May 2018 11:26:22 +0300 Subject: [PATCH 069/217] video settings screen converted --- toxygen/ui/menu.py | 116 +++++++++------------- toxygen/ui/views/video_settings_screen.ui | 77 ++++++++++++++ 2 files changed, 126 insertions(+), 67 deletions(-) create mode 100644 toxygen/ui/views/video_settings_screen.ui diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index fff95a9..c473c8b 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -5,6 +5,7 @@ from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow import pyaudio import updater.updater as updater import utils.ui as util_ui +import cv2 class AddContact(CenteredWidget): @@ -761,70 +762,18 @@ class VideoSettings(CenteredWidget): def __init__(self, settings): super().__init__() self._settings = settings - self.initUI() - self.retranslateUi() + uic.loadUi(get_views_path('video_settings_screen'), self) + self._devices = self._frame_max_sizes = None + self._update_ui() self.center() self.desktopAreaSelection = None - def initUI(self): - self.setObjectName("videoSettingsForm") - self.resize(400, 120) - self.setMinimumSize(QtCore.QSize(400, 120)) - self.setMaximumSize(QtCore.QSize(400, 120)) - self.in_label = QtWidgets.QLabel(self) - self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20)) - font = QtGui.QFont() - font.setPointSize(16) - font.setBold(True) - font.setFamily(self._settings['font']) - self.in_label.setFont(font) - self.video_size = QtWidgets.QComboBox(self) - self.video_size.setGeometry(QtCore.QRect(25, 70, 350, 30)) - self.input = QtWidgets.QComboBox(self) - self.input.setGeometry(QtCore.QRect(25, 30, 350, 30)) - self.input.currentIndexChanged.connect(self.selectionChanged) - self.button = QtWidgets.QPushButton(self) - self.button.clicked.connect(self.button_clicked) - self.button.setGeometry(QtCore.QRect(25, 70, 350, 30)) - import cv2 - self.devices = [-1] - screen = QtWidgets.QApplication.primaryScreen() - size = screen.size() - self.frame_max_sizes = [(size.width(), size.height())] - desktop = util_ui.tr("Desktop") - self.input.addItem(desktop) - for i in range(10): - v = cv2.VideoCapture(i) - if v.isOpened(): - v.set(cv2.CAP_PROP_FRAME_WIDTH, 10000) - v.set(cv2.CAP_PROP_FRAME_HEIGHT, 10000) - - width = int(v.get(cv2.CAP_PROP_FRAME_WIDTH)) - height = int(v.get(cv2.CAP_PROP_FRAME_HEIGHT)) - del v - self.devices.append(i) - self.frame_max_sizes.append((width, height)) - self.input.addItem('Device #' + str(i)) - try: - index = self.devices.index(self._settings.video['device']) - self.input.setCurrentIndex(index) - except: - print('Video devices error!') - - def retranslateUi(self): - self.setWindowTitle(util_ui.tr("Video settings")) - self.in_label.setText(util_ui.tr("Device:")) - self.button.setText(util_ui.tr("Select region")) - - def button_clicked(self): - self.desktopAreaSelection = DesktopAreaSelectionWindow(self) - def closeEvent(self, event): - if self.input.currentIndex() == 0: + if self.deviceComboBox.currentIndex() == 0: return try: self._settings.video['device'] = self.devices[self.input.currentIndex()] - text = self.video_size.currentText() + text = self.resolutionComboBox.currentText() self._settings.video['width'] = int(text.split(' ')[0]) self._settings.video['height'] = int(text.split(' ')[-1]) self._settings.save() @@ -840,15 +789,48 @@ class VideoSettings(CenteredWidget): self._settings.video['y'] = y self._settings.save() - def selectionChanged(self): - if self.input.currentIndex() == 0: - self.button.setVisible(True) - self.video_size.setVisible(False) - else: - self.button.setVisible(False) - self.video_size.setVisible(True) - width, height = self.frame_max_sizes[self.input.currentIndex()] - self.video_size.clear() + def _update_ui(self): + self.deviceComboBox.currentIndexChanged.connect(self._device_changed) + self.selectRegionPushButton.clicked.connect(self._button_clicked) + self._devices = [-1] + screen = QtWidgets.QApplication.primaryScreen() + size = screen.size() + self._frame_max_sizes = [(size.width(), size.height())] + desktop = util_ui.tr("Desktop") + self.deviceComboBox.addItem(desktop) + for i in range(10): + v = cv2.VideoCapture(i) + if v.isOpened(): + v.set(cv2.CAP_PROP_FRAME_WIDTH, 10000) + v.set(cv2.CAP_PROP_FRAME_HEIGHT, 10000) + + width = int(v.get(cv2.CAP_PROP_FRAME_WIDTH)) + height = int(v.get(cv2.CAP_PROP_FRAME_HEIGHT)) + del v + self._devices.append(i) + self._frame_max_sizes.append((width, height)) + self.deviceComboBox.addItem(util_ui.tr('Device #') + str(i)) + try: + index = self._devices.index(self._settings.video['device']) + self.deviceComboBox.setCurrentIndex(index) + except: + print('Video devices error!') + self._retranslate_ui() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Video settings")) + self.deviceLabel.setText(util_ui.tr("Device:")) + self.selectRegionPushButton.setText(util_ui.tr("Select region")) + + def _button_clicked(self): + self.desktopAreaSelection = DesktopAreaSelectionWindow(self) + + def _device_changed(self): + index = self.deviceComboBox.currentIndex() + self.selectRegionPushButton.setVisible(index == 0) + self.resolutionComboBox.setVisible(index != 0) + width, height = self._frame_max_sizes[index] + self.resolutionComboBox.clear() dims = [ (320, 240), (640, 360), @@ -860,7 +842,7 @@ class VideoSettings(CenteredWidget): ] for w, h in dims: if w <= width and h <= height: - self.video_size.addItem(str(w) + ' * ' + str(h)) + self.resolutionComboBox.addItem(str(w) + ' * ' + str(h)) class PluginsSettings(CenteredWidget): diff --git a/toxygen/ui/views/video_settings_screen.ui b/toxygen/ui/views/video_settings_screen.ui new file mode 100644 index 0000000..cfa36fb --- /dev/null +++ b/toxygen/ui/views/video_settings_screen.ui @@ -0,0 +1,77 @@ + + + Form + + + + 0 + 0 + 400 + 120 + + + + + 400 + 120 + + + + + 400 + 120 + + + + Form + + + + + 25 + 5 + 350 + 20 + + + + TextLabel + + + + + + 25 + 30 + 350 + 30 + + + + + + + 25 + 70 + 350 + 30 + + + + PushButton + + + + + + 25 + 70 + 350 + 30 + + + + + + + From 03e2fa4cb862c2748d99078f7f5c64419a1b9017 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 25 May 2018 11:48:47 +0300 Subject: [PATCH 070/217] add friend screen coverted --- toxygen/ui/menu.py | 69 ++++++------------ toxygen/ui/views/add_contact_screen.ui | 99 ++++++++++++++++++++++++++ toxygen/ui/widgets.py | 1 + 3 files changed, 120 insertions(+), 49 deletions(-) create mode 100644 toxygen/ui/views/add_contact_screen.ui diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index c473c8b..6c9218c 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -15,70 +15,41 @@ class AddContact(CenteredWidget): super().__init__() self._settings = settings self._contacts_manager = contacts_manager - self.initUI(tox_id) + uic.loadUi(get_views_path('add_contact_screen'), self) + self._update_ui(tox_id) self._adding = False - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - def initUI(self, tox_id): - self.setObjectName('AddContact') - self.resize(568, 306) - self.sendRequestButton = QtWidgets.QPushButton(self) - self.sendRequestButton.setGeometry(QtCore.QRect(50, 270, 471, 31)) - self.sendRequestButton.setMinimumSize(QtCore.QSize(0, 0)) - self.sendRequestButton.setBaseSize(QtCore.QSize(0, 0)) - self.sendRequestButton.setObjectName("sendRequestButton") - self.sendRequestButton.clicked.connect(self.add_friend) - self.tox_id = LineEdit(self) - self.tox_id.setGeometry(QtCore.QRect(50, 40, 471, 27)) - self.tox_id.setObjectName("lineEdit") - self.tox_id.setText(tox_id) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(50, 10, 80, 20)) - self.error_label = DataLabel(self) - self.error_label.setGeometry(QtCore.QRect(120, 10, 420, 20)) - font = QtGui.QFont() - font.setFamily(self._settings['font']) - font.setPointSize(10) - font.setWeight(30) - self.error_label.setFont(font) - self.error_label.setStyleSheet("QLabel { color: #BC1C1C; }") - self.label.setObjectName("label") - self.message_edit = QtWidgets.QTextEdit(self) - self.message_edit.setGeometry(QtCore.QRect(50, 110, 471, 151)) - self.message_edit.setObjectName("textEdit") - self.message = QtWidgets.QLabel(self) - self.message.setGeometry(QtCore.QRect(50, 70, 101, 31)) - self.message.setFont(font) - self.message.setObjectName("label_2") - self.retranslateUi() - self.message_edit.setText('Hello! Add me to your contact list please') - font.setPointSize(12) - font.setBold(True) - self.label.setFont(font) - self.message.setFont(font) - QtCore.QMetaObject.connectSlotsByName(self) + def _update_ui(self, tox_id): + self.toxIdLineEdit = LineEdit(self) + self.toxIdLineEdit.setGeometry(QtCore.QRect(50, 40, 460, 30)) + self.toxIdLineEdit.setText(tox_id) - def add_friend(self): + self.messagePlainTextEdit.document().setPlainText(util_ui.tr('Hello! Please add me to your contact list.')) + self.addContactPushButton.clicked.connect(self._add_friend) + self._retranslate_ui() + + def _add_friend(self): if self._adding: return self._adding = True - tox_id = self.tox_id.text().strip() + tox_id = self.toxIdLineEdit.text().strip() if tox_id.startswith('tox:'): tox_id = tox_id[4:] - send = self._contacts_manager.send_friend_request(tox_id, self.message_edit.toPlainText()) + message = self.messagePlainTextEdit.toPlainText() + send = self._contacts_manager.send_friend_request(tox_id, message) self._adding = False if send is True: # request was successful self.close() else: # print error data - self.error_label.setText(send) + self.errorLabel.setText(send) - def retranslateUi(self): + def _retranslate_ui(self): self.setWindowTitle(util_ui.tr('Add contact')) - self.sendRequestButton.setText(util_ui.tr('Send request')) - self.label.setText(util_ui.tr('TOX ID:')) - self.message.setText(util_ui.tr('Message:')) - self.tox_id.setPlaceholderText(util_ui.tr('TOX ID or public key of contact')) + self.addContactPushButton.setText(util_ui.tr('Send request')) + self.toxIdLabel.setText(util_ui.tr('TOX ID:')) + self.messageLabel.setText(util_ui.tr('Message:')) + self.toxIdLineEdit.setPlaceholderText(util_ui.tr('TOX ID or public key of contact')) class ProfileSettings(CenteredWidget): diff --git a/toxygen/ui/views/add_contact_screen.ui b/toxygen/ui/views/add_contact_screen.ui new file mode 100644 index 0000000..0f26a25 --- /dev/null +++ b/toxygen/ui/views/add_contact_screen.ui @@ -0,0 +1,99 @@ + + + Form + + + + 0 + 0 + 560 + 320 + + + + + 560 + 320 + + + + + 560 + 320 + + + + Form + + + + + 50 + 10 + 150 + 20 + + + + TextLabel + + + + + + 50 + 70 + 150 + 30 + + + + TextLabel + + + + + + 50 + 110 + 460 + 150 + + + + + + + 50 + 270 + 460 + 30 + + + + PushButton + + + + + true + + + + 220 + 10 + 321 + 31 + + + + Qt::NoContextMenu + + + + + + + + + diff --git a/toxygen/ui/widgets.py b/toxygen/ui/widgets.py index c25607b..e7fe623 100644 --- a/toxygen/ui/widgets.py +++ b/toxygen/ui/widgets.py @@ -24,6 +24,7 @@ class CenteredWidget(QtWidgets.QWidget): def __init__(self): super().__init__() + self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.center() def center(self): From 74a5f95a5656861949b60596837f0110919f73b1 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 24 May 2018 19:13:19 +0300 Subject: [PATCH 071/217] rebased ngc - initial commit --- toxygen/bootstrap/nodes.json | 2 +- toxygen/middleware/callbacks.py | 36 ++++++------- toxygen/middleware/tox_factory.py | 1 + toxygen/user_data/settings.py | 3 +- toxygen/wrapper/libtox.py | 16 +++--- toxygen/wrapper/tox.py | 87 ++++++++++++++----------------- 6 files changed, 69 insertions(+), 76 deletions(-) diff --git a/toxygen/bootstrap/nodes.json b/toxygen/bootstrap/nodes.json index 619fe67..f9574dc 100644 --- a/toxygen/bootstrap/nodes.json +++ b/toxygen/bootstrap/nodes.json @@ -1 +1 @@ -{"nodes":[{"ipv4":"127.0.0.1","ipv6":"-","port":33445,"public_key":"617DA0076546F9A801D06AAA2E20234DA6A1DDA90583FB02B59E3501CA84D061","status_udp":true,"status_tcp":true}]} \ No newline at end of file +{"nodes":[{"ipv4":"127.0.0.1","ipv6":"-","port":33445,"public_key":"82B9E28CF62A2D78D83BAC452CD18778F1F36B7BDF2989A3B8927D86D09C2E3D","status_udp":true,"status_tcp":true}]} \ No newline at end of file diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index f7a9531..6855adc 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -1,6 +1,4 @@ from PyQt5 import QtGui -from user_data.settings import Settings -from contacts.profile import Profile from wrapper.toxcore_enums_and_consts import * from wrapper.toxav_enums import * from wrapper.tox import bin_to_string @@ -22,7 +20,7 @@ import threading def self_connection_status(tox, profile): """ - Current user changed connection status (offline, UDP, TCP) + Current user changed connection status (offline, TCP, UDP) """ def wrapped(tox_link, connection, user_data): print('Connection status: ', str(connection)) @@ -103,7 +101,7 @@ def friend_status_message(contacts_manager, messenger): """ friend = contacts_manager.get_friend_by_number(friend_number) invoke_in_main_thread(friend.set_status_message, str(status_message, 'utf-8')) - print('User #{} has new status'.format(friend_number)) + print('User #{} has new status message'.format(friend_number)) invoke_in_main_thread(messenger.send_messages, friend_number) return wrapped @@ -472,25 +470,25 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, :param contacts_provider: ContactsProvider instance """ # self callbacks - tox.callback_self_connection_status(self_connection_status(tox, profile), 0) + tox.callback_self_connection_status(self_connection_status(tox, profile)) # friend callbacks - tox.callback_friend_status(friend_status(contacts_manager, file_transfer_handler, profile, settings), 0) - tox.callback_friend_message(friend_message(messenger, contacts_manager, profile, settings, main_window, tray), 0) + tox.callback_friend_status(friend_status(contacts_manager, file_transfer_handler, profile, settings)) + tox.callback_friend_message(friend_message(messenger, contacts_manager, profile, settings, main_window, tray)) tox.callback_friend_connection_status(friend_connection_status(contacts_manager, profile, settings, plugin_loader, - file_transfer_handler, messenger, calls_manager), 0) - tox.callback_friend_name(friend_name(contacts_provider, messenger), 0) - tox.callback_friend_status_message(friend_status_message(contacts_manager, messenger), 0) - tox.callback_friend_request(friend_request(contacts_manager), 0) - tox.callback_friend_typing(friend_typing(messenger), 0) - tox.callback_friend_read_receipt(friend_read_receipt(messenger), 0) + file_transfer_handler, messenger, calls_manager)) + tox.callback_friend_name(friend_name(contacts_provider, messenger)) + tox.callback_friend_status_message(friend_status_message(contacts_manager, messenger)) + tox.callback_friend_request(friend_request(contacts_manager)) + tox.callback_friend_typing(friend_typing(messenger)) + tox.callback_friend_read_receipt(friend_read_receipt(messenger)) # file transfer tox.callback_file_recv(tox_file_recv(main_window, tray, profile, file_transfer_handler, - contacts_manager, settings), 0) - tox.callback_file_recv_chunk(file_recv_chunk(file_transfer_handler), 0) - tox.callback_file_chunk_request(file_chunk_request(file_transfer_handler), 0) - tox.callback_file_recv_control(file_recv_control(file_transfer_handler), 0) + contacts_manager, settings)) + tox.callback_file_recv_chunk(file_recv_chunk(file_transfer_handler)) + tox.callback_file_chunk_request(file_chunk_request(file_transfer_handler)) + tox.callback_file_recv_control(file_recv_control(file_transfer_handler)) # av toxav = tox.AV @@ -500,8 +498,8 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, toxav.callback_video_receive_frame(video_receive_frame, 0) # custom packets - tox.callback_friend_lossless_packet(lossless_packet(plugin_loader), 0) - tox.callback_friend_lossy_packet(lossy_packet(plugin_loader), 0) + tox.callback_friend_lossless_packet(lossless_packet(plugin_loader)) + tox.callback_friend_lossy_packet(lossy_packet(plugin_loader)) # gc callbacks tox.callback_group_message(group_message(main_window, tray, tox, messenger, settings, profile), 0) diff --git a/toxygen/middleware/tox_factory.py b/toxygen/middleware/tox_factory.py index 588bd30..9ee5c01 100644 --- a/toxygen/middleware/tox_factory.py +++ b/toxygen/middleware/tox_factory.py @@ -21,6 +21,7 @@ def tox_factory(data=None, settings=None): tox_options.contents.start_port = settings['start_port'] tox_options.contents.end_port = settings['end_port'] tox_options.contents.tcp_port = settings['tcp_port'] + tox_options.contents.local_discovery_enabled = settings['lan_discovery'] if data: # load existing profile tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['TOX_SAVE'] tox_options.contents.savedata_data = ctypes.c_char_p(data) diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 76a37c0..4171978 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -147,7 +147,8 @@ class Settings(dict): 'update': 1, 'group_notifications': True, 'download_nodes_list': False, - 'notify_all_gc': False + 'notify_all_gc': False, + 'lan_discovery': True } @staticmethod diff --git a/toxygen/wrapper/libtox.py b/toxygen/wrapper/libtox.py index 402aa5f..d5f599a 100644 --- a/toxygen/wrapper/libtox.py +++ b/toxygen/wrapper/libtox.py @@ -28,13 +28,13 @@ class LibToxAV: # on Windows av api is in libtox.dll self._libtoxav = CDLL(util.curr_directory() + '/libs/libtox.dll') elif system() == 'Darwin': - self._libtoxav = CDLL('libtoxav.dylib') + self._libtoxav = CDLL('libtoxcore.dylib') else: - # /usr/lib/libtoxav.so must exists + # /usr/lib/libtoxcore.so must exists try: - self._libtoxav = CDLL('libtoxav.so') + self._libtoxav = CDLL('libtoxcore.so') except: - self._libtoxav = CDLL(util.curr_directory() + '/libs/libtoxav.so') + self._libtoxav = CDLL(util.curr_directory() + '/libs/libtoxcore.so') def __getattr__(self, item): return self._libtoxav.__getattr__(item) @@ -47,13 +47,13 @@ class LibToxEncryptSave: # on Windows profile encryption api is in libtox.dll self._lib_tox_encrypt_save = CDLL(util.curr_directory() + '/libs/libtox.dll') elif system() == 'Darwin': - self._lib_tox_encrypt_save = CDLL('libtoxencryptsave.dylib') + self._lib_tox_encrypt_save = CDLL('libtoxcore.dylib') else: - # /usr/lib/libtoxencryptsave.so must exists + # /usr/lib/libtoxcore.so must exists try: - self._lib_tox_encrypt_save = CDLL('libtoxencryptsave.so') + self._lib_tox_encrypt_save = CDLL('libtoxcore.so') except: - self._lib_tox_encrypt_save = CDLL(util.curr_directory() + '/libs/libtoxencryptsave.so') + self._lib_tox_encrypt_save = CDLL(util.curr_directory() + '/libs/libtoxcore.so') def __getattr__(self, item): return self._lib_tox_encrypt_save.__getattr__(item) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index f455e30..9d955a4 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -10,15 +10,19 @@ class ToxOptions(Structure): _fields_ = [ ('ipv6_enabled', c_bool), ('udp_enabled', c_bool), + ('local_discovery_enabled', c_bool), ('proxy_type', c_int), ('proxy_host', c_char_p), ('proxy_port', c_uint16), ('start_port', c_uint16), ('end_port', c_uint16), ('tcp_port', c_uint16), + ('hole_punching_enabled', c_bool), ('savedata_type', c_int), ('savedata_data', c_char_p), - ('savedata_length', c_size_t) + ('savedata_length', c_size_t), + ('log_callback', c_void_p), + ('log_user_data', c_void_p) ] @@ -265,7 +269,7 @@ class Tox: """ return Tox.libtoxcore.tox_self_get_connection_status(self._tox_pointer) - def callback_self_connection_status(self, callback, user_data): + def callback_self_connection_status(self, callback): """ Set the callback for the `self_connection_status` event. Pass None to unset. @@ -276,12 +280,11 @@ class Tox: :param callback: Python function. Should take pointer (c_void_p) to Tox object, TOX_CONNECTION (c_int), pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_int, c_void_p) self.self_connection_status_cb = c_callback(callback) Tox.libtoxcore.tox_callback_self_connection_status(self._tox_pointer, - self.self_connection_status_cb, user_data) + self.self_connection_status_cb) def iteration_interval(self): """ @@ -290,11 +293,13 @@ class Tox: """ return Tox.libtoxcore.tox_iteration_interval(self._tox_pointer) - def iterate(self): + def iterate(self, user_data=None): """ The main loop that needs to be run in intervals of tox_iteration_interval() milliseconds. """ - Tox.libtoxcore.tox_iterate(self._tox_pointer) + if user_data is not None: + user_data = c_char_p(user_data) + Tox.libtoxcore.tox_iterate(self._tox_pointer, user_data) # ----------------------------------------------------------------------------------------------------------------- # Internal client information (Tox address/id) @@ -719,7 +724,7 @@ class Tox: elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: raise ArgumentError('The friend_number did not designate a valid friend.') - def callback_friend_name(self, callback, user_data): + def callback_friend_name(self, callback): """ Set the callback for the `friend_name` event. Pass None to unset. @@ -730,11 +735,10 @@ class Tox: A byte array (c_char_p) containing the same data as tox_friend_get_name would write to its `name` parameter, A value (c_size_t) equal to the return value of tox_friend_get_name_size, pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) self.friend_name_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_name(self._tox_pointer, self.friend_name_cb, user_data) + Tox.libtoxcore.tox_callback_friend_name(self._tox_pointer, self.friend_name_cb) def friend_get_status_message_size(self, friend_number): """ @@ -783,7 +787,7 @@ class Tox: elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: raise ArgumentError('The friend_number did not designate a valid friend.') - def callback_friend_status_message(self, callback, user_data): + def callback_friend_status_message(self, callback): """ Set the callback for the `friend_status_message` event. Pass NULL to unset. @@ -795,12 +799,11 @@ class Tox: `status_message` parameter, A value (c_size_t) equal to the return value of tox_friend_get_status_message_size, pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) self.friend_status_message_cb = c_callback(callback) Tox.libtoxcore.tox_callback_friend_status_message(self._tox_pointer, - self.friend_status_message_cb, c_void_p(user_data)) + self.friend_status_message_cb) def friend_get_status(self, friend_number): """ @@ -824,7 +827,7 @@ class Tox: elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: raise ArgumentError('The friend_number did not designate a valid friend.') - def callback_friend_status(self, callback, user_data): + def callback_friend_status(self, callback): """ Set the callback for the `friend_status` event. Pass None to unset. @@ -838,7 +841,7 @@ class Tox: """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) self.friend_status_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_status(self._tox_pointer, self.friend_status_cb, c_void_p(user_data)) + Tox.libtoxcore.tox_callback_friend_status(self._tox_pointer, self.friend_status_cb) def friend_get_connection_status(self, friend_number): """ @@ -863,7 +866,7 @@ class Tox: elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: raise ArgumentError('The friend_number did not designate a valid friend.') - def callback_friend_connection_status(self, callback, user_data): + def callback_friend_connection_status(self, callback): """ Set the callback for the `friend_connection_status` event. Pass NULL to unset. @@ -876,12 +879,11 @@ class Tox: The friend number (c_uint32) of the friend whose connection status changed, The result of calling tox_friend_get_connection_status (TOX_CONNECTION) on the passed friend_number, pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) self.friend_connection_status_cb = c_callback(callback) Tox.libtoxcore.tox_callback_friend_connection_status(self._tox_pointer, - self.friend_connection_status_cb, c_void_p(user_data)) + self.friend_connection_status_cb) def friend_get_typing(self, friend_number): """ @@ -903,7 +905,7 @@ class Tox: elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: raise ArgumentError('The friend_number did not designate a valid friend.') - def callback_friend_typing(self, callback, user_data): + def callback_friend_typing(self, callback): """ Set the callback for the `friend_typing` event. Pass NULL to unset. @@ -913,11 +915,10 @@ class Tox: The friend number (c_uint32) of the friend who started or stopped typing, The result of calling tox_friend_get_typing (c_bool) on the passed friend_number, pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_void_p) self.friend_typing_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_typing(self._tox_pointer, self.friend_typing_cb, c_void_p(user_data)) + Tox.libtoxcore.tox_callback_friend_typing(self._tox_pointer, self.friend_typing_cb) # ----------------------------------------------------------------------------------------------------------------- # Sending private messages @@ -982,7 +983,7 @@ class Tox: elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['EMPTY']: raise ArgumentError('Attempted to send a zero-length message.') - def callback_friend_read_receipt(self, callback, user_data): + def callback_friend_read_receipt(self, callback): """ Set the callback for the `friend_read_receipt` event. Pass None to unset. @@ -998,13 +999,13 @@ class Tox: c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) self.friend_read_receipt_cb = c_callback(callback) Tox.libtoxcore.tox_callback_friend_read_receipt(self._tox_pointer, - self.friend_read_receipt_cb, c_void_p(user_data)) + self.friend_read_receipt_cb) # ----------------------------------------------------------------------------------------------------------------- # Receiving private messages and friend requests # ----------------------------------------------------------------------------------------------------------------- - def callback_friend_request(self, callback, user_data): + def callback_friend_request(self, callback): """ Set the callback for the `friend_request` event. Pass None to unset. @@ -1019,9 +1020,9 @@ class Tox: """ c_callback = CFUNCTYPE(None, c_void_p, POINTER(c_uint8), c_char_p, c_size_t, c_void_p) self.friend_request_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_request(self._tox_pointer, self.friend_request_cb, c_void_p(user_data)) + Tox.libtoxcore.tox_callback_friend_request(self._tox_pointer, self.friend_request_cb) - def callback_friend_message(self, callback, user_data): + def callback_friend_message(self, callback): """ Set the callback for the `friend_message` event. Pass None to unset. @@ -1033,11 +1034,10 @@ class Tox: The message data (c_char_p) they sent, The size (c_size_t) of the message byte array. pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_char_p, c_size_t, c_void_p) self.friend_message_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_message(self._tox_pointer, self.friend_message_cb, c_void_p(user_data)) + Tox.libtoxcore.tox_callback_friend_message(self._tox_pointer, self.friend_message_cb) # ----------------------------------------------------------------------------------------------------------------- # File transmission: common between sending and receiving @@ -1095,7 +1095,7 @@ class Tox: elif tox_err_file_control == TOX_ERR_FILE_CONTROL['SENDQ']: raise RuntimeError('Packet queue is full.') - def callback_file_recv_control(self, callback, user_data): + def callback_file_recv_control(self, callback): """ Set the callback for the `file_recv_control` event. Pass NULL to unset. @@ -1110,12 +1110,11 @@ class Tox: The friend-specific file number (c_uint32) the data received is associated with. The file control (TOX_FILE_CONTROL) command received. pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p) self.file_recv_control_cb = c_callback(callback) Tox.libtoxcore.tox_callback_file_recv_control(self._tox_pointer, - self.file_recv_control_cb, user_data) + self.file_recv_control_cb) def file_seek(self, friend_number, file_number, position): """ @@ -1285,7 +1284,7 @@ class Tox: elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['WRONG_POSITION']: raise ArgumentError('Position parameter was wrong.') - def callback_file_chunk_request(self, callback, user_data): + def callback_file_chunk_request(self, callback): """ Set the callback for the `file_chunk_request` event. Pass None to unset. @@ -1311,17 +1310,16 @@ class Tox: The file or stream position (c_uint64) from which to continue reading. The number of bytes (c_size_t) requested for the current chunk. pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, c_size_t, c_void_p) self.file_chunk_request_cb = c_callback(callback) - self.libtoxcore.tox_callback_file_chunk_request(self._tox_pointer, self.file_chunk_request_cb, user_data) + self.libtoxcore.tox_callback_file_chunk_request(self._tox_pointer, self.file_chunk_request_cb) # ----------------------------------------------------------------------------------------------------------------- # File transmission: receiving # ----------------------------------------------------------------------------------------------------------------- - def callback_file_recv(self, callback, user_data): + def callback_file_recv(self, callback): """ Set the callback for the `file_recv` event. Pass None to unset. @@ -1341,13 +1339,12 @@ class Tox: send request. Size in bytes (c_size_t) of the filename. pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t, c_void_p) + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t) self.file_recv_cb = c_callback(callback) - self.libtoxcore.tox_callback_file_recv(self._tox_pointer, self.file_recv_cb, user_data) + self.libtoxcore.tox_callback_file_recv(self._tox_pointer, self.file_recv_cb) - def callback_file_recv_chunk(self, callback, user_data): + def callback_file_recv_chunk(self, callback): """ Set the callback for the `file_recv_chunk` event. Pass NULL to unset. @@ -1368,11 +1365,10 @@ class Tox: A byte array (c_char_p) containing the received chunk. The length (c_size_t) of the received chunk. pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, POINTER(c_uint8), c_size_t, c_void_p) self.file_recv_chunk_cb = c_callback(callback) - self.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, self.file_recv_chunk_cb, user_data) + self.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, self.file_recv_chunk_cb) # ----------------------------------------------------------------------------------------------------------------- # Low-level custom packet sending and receiving @@ -1453,7 +1449,7 @@ class Tox: elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']: raise RuntimeError('Packet queue is full.') - def callback_friend_lossy_packet(self, callback, user_data): + def callback_friend_lossy_packet(self, callback): """ Set the callback for the `friend_lossy_packet` event. Pass NULL to unset. @@ -1463,13 +1459,12 @@ class Tox: A byte array (c_uint8 array) containing the received packet data, length (c_size_t) - The length of the packet data byte array, pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) self.friend_lossy_packet_cb = c_callback(callback) - self.libtoxcore.tox_callback_friend_lossy_packet(self._tox_pointer, self.friend_lossy_packet_cb, user_data) + self.libtoxcore.tox_callback_friend_lossy_packet(self._tox_pointer, self.friend_lossy_packet_cb) - def callback_friend_lossless_packet(self, callback, user_data): + def callback_friend_lossless_packet(self, callback): """ Set the callback for the `friend_lossless_packet` event. Pass NULL to unset. @@ -1479,12 +1474,10 @@ class Tox: A byte array (c_uint8 array) containing the received packet data, length (c_size_t) - The length of the packet data byte array, pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data """ c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) self.friend_lossless_packet_cb = c_callback(callback) - self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, self.friend_lossless_packet_cb, - user_data) + self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, self.friend_lossless_packet_cb) # ----------------------------------------------------------------------------------------------------------------- # Low-level network information From fa3529f5f27db7720335a1da28245e404e172d9b Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 25 May 2018 16:55:36 +0300 Subject: [PATCH 072/217] fixed broken ft callback --- toxygen/wrapper/tox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 9d955a4..b4c401c 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1340,7 +1340,7 @@ class Tox: Size in bytes (c_size_t) of the filename. pointer (c_void_p) to user_data """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t) + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t, c_void_p) self.file_recv_cb = c_callback(callback) self.libtoxcore.tox_callback_file_recv(self._tox_pointer, self.file_recv_cb) From 1c80b4fd7dfdcfe6d3341f20506f04df8e479668 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 26 May 2018 16:27:30 +0300 Subject: [PATCH 073/217] process group creation fail --- toxygen/groups/groups_service.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 0e9bef7..454efda 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -22,7 +22,8 @@ class GroupsService(tox_save.ToxSave): def create_new_gc(self, name, privacy_state): group_number = self._tox.group_new(privacy_state, name.encode('utf-8')) - self._add_new_group_by_number(group_number) + if group_number != -1: + self._add_new_group_by_number(group_number) def join_gc_by_id(self, chat_id, password): group_number = self._tox.group_join(chat_id, password) From 56731be79dd4becf1f35dd462ec54ee23d7e7d02 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 2 Jun 2018 20:20:57 +0300 Subject: [PATCH 074/217] minor ui issues fixed --- toxygen/bootstrap/nodes.json | 2 +- toxygen/ui/groups_widgets.py | 1 + toxygen/ui/views/login_screen.ui | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/toxygen/bootstrap/nodes.json b/toxygen/bootstrap/nodes.json index f9574dc..c71642b 100644 --- a/toxygen/bootstrap/nodes.json +++ b/toxygen/bootstrap/nodes.json @@ -1 +1 @@ -{"nodes":[{"ipv4":"127.0.0.1","ipv6":"-","port":33445,"public_key":"82B9E28CF62A2D78D83BAC452CD18778F1F36B7BDF2989A3B8927D86D09C2E3D","status_udp":true,"status_tcp":true}]} \ No newline at end of file +{"nodes":[{"ipv4":"127.0.0.1","ipv6":"-","port":33445,"public_key":"AB38C55C594B9D6BF23A896345DB832722146FCAA120EFA0EE721B7BFB4DB83F","status_udp":true,"status_tcp":true}]} \ No newline at end of file diff --git a/toxygen/ui/groups_widgets.py b/toxygen/ui/groups_widgets.py index c6d0248..d666614 100644 --- a/toxygen/ui/groups_widgets.py +++ b/toxygen/ui/groups_widgets.py @@ -45,6 +45,7 @@ class JoinGroupScreen(CenteredWidget): self._groups_service = groups_service uic.loadUi(util.get_views_path('join_group_screen'), self) self.center() + self._update_ui() def _update_ui(self): self._retranslate_ui() diff --git a/toxygen/ui/views/login_screen.ui b/toxygen/ui/views/login_screen.ui index a4c205c..524b7b7 100644 --- a/toxygen/ui/views/login_screen.ui +++ b/toxygen/ui/views/login_screen.ui @@ -10,6 +10,18 @@ 200 + + + 400 + 200 + + + + + 400 + 200 + + Form From 41de31549678fc5f28f6f3e26a83ac5df36cdcc4 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 3 Jun 2018 21:18:22 +0300 Subject: [PATCH 075/217] Filtration fixed --- toxygen/contacts/contact.py | 8 ++++++++ toxygen/contacts/contacts_manager.py | 9 +++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index d7fe35e..fb124dd 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -320,3 +320,11 @@ class Contact(basecontact.BaseContact): def get_context_menu_generator(self): return BaseContactMenuGenerator(self) + + # ----------------------------------------------------------------------------------------------------------------- + # Filtration support + # ----------------------------------------------------------------------------------------------------------------- + + def set_widget(self, widget): + self._widget = widget + self.init_widget() diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 74452e1..08ee678 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -154,7 +154,7 @@ class ContactsManager(ToxSave): def filtration_and_sorting(self, sorting=0, filter_str=''): """ Filtration of friends list - :param sorting: 0 - no sort, 1 - online only, 2 - online first, 4 - by name + :param sorting: 0 - no sorting, 1 - online only, 2 - online first, 4 - by name :param filter_str: show contacts which name contains this substring """ # TODO: simplify? @@ -183,9 +183,10 @@ class ContactsManager(ToxSave): part1 = sorted(part1, key=lambda x: x.number) part2 = sorted(part2, key=lambda x: x.number) self._contacts = part1 + part2 - # self._screen.friends_list.clear() - # for contact in self._contacts: - # contact.set_widget(self.create_friend_item()) + for index, contact in enumerate(self._contacts): + list_item = self._screen.friends_list.item(index) + item_widget = self._screen.friends_list.itemWidget(list_item) + contact.set_widget(item_widget) for index, friend in enumerate(self._contacts): friend.visibility = (friend.status is not None or not (sorting & 1)) and (filter_str in friend.name.lower()) friend.visibility = friend.visibility or friend.messages or friend.actions From 8809ef1f6e8b145b97033d0fd2aa346f29eee5eb Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 5 Jun 2018 23:58:14 +0300 Subject: [PATCH 076/217] minor ui fixes --- toxygen/ui/groups_widgets.py | 1 + toxygen/ui/login_screen.py | 2 +- toxygen/ui/views/login_screen.ui | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/toxygen/ui/groups_widgets.py b/toxygen/ui/groups_widgets.py index d666614..aa91935 100644 --- a/toxygen/ui/groups_widgets.py +++ b/toxygen/ui/groups_widgets.py @@ -26,6 +26,7 @@ class CreateGroupScreen(CenteredWidget): self.addGroupButton.setText(util_ui.tr('Create group')) self.groupTypeComboBox.addItem(util_ui.tr('Public')) self.groupTypeComboBox.addItem(util_ui.tr('Private')) + self.groupTypeComboBox.setCurrentIndex(1) def _create_group(self): name = self.groupNameLineEdit.text() diff --git a/toxygen/ui/login_screen.py b/toxygen/ui/login_screen.py index 7198c36..35e33b5 100644 --- a/toxygen/ui/login_screen.py +++ b/toxygen/ui/login_screen.py @@ -49,7 +49,7 @@ class LoginScreen(CenteredWidget, DialogWithResult): def _update_ui(self): self.profileNameLineEdit = LineEditWithEnterSupport(self._create_profile, self) - self.profileNameLineEdit.setGeometry(QtCore.QRect(20, 100, 170, 30)) + self.profileNameLineEdit.setGeometry(QtCore.QRect(20, 100, 160, 30)) self._retranslate_ui() self.createProfilePushButton.clicked.connect(self._create_profile) self.loadProfilePushButton.clicked.connect(self._load_existing_profile) diff --git a/toxygen/ui/views/login_screen.ui b/toxygen/ui/views/login_screen.ui index 524b7b7..50ca1e0 100644 --- a/toxygen/ui/views/login_screen.ui +++ b/toxygen/ui/views/login_screen.ui @@ -67,9 +67,9 @@ - 20 + 10 110 - 150 + 160 27 @@ -119,9 +119,9 @@ - 20 + 10 110 - 150 + 160 27 From b2ecf5314e45777c00cbe3ff25f8d64762cdc60c Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 23 Jun 2018 00:20:13 +0300 Subject: [PATCH 077/217] gc invite - support of gc name added --- toxygen/groups/groups_service.py | 6 +++--- toxygen/middleware/callbacks.py | 5 +++-- toxygen/wrapper/tox.py | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 454efda..83f6a4e 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -49,10 +49,10 @@ class GroupsService(tox_save.ToxSave): def invite_friend(self, friend_number, group_number): self._tox.group_invite_friend(group_number, friend_number) - def process_group_invite(self, friend_number, invite_data): + def process_group_invite(self, friend_number, group_name, invite_data): friend = self._get_friend(friend_number) - text = util_ui.tr('Friend {} invites you to group. Accept?') - if util_ui.question(text.format(friend.name), util_ui.tr('Group invite')): + text = util_ui.tr('Friend {} invites you to group "{}". Accept?') + if util_ui.question(text.format(friend.name, group_name), util_ui.tr('Group invite')): self.join_gc_via_invite(invite_data, friend_number, None) # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 6855adc..41a5717 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -382,9 +382,10 @@ def group_message(window, tray, tox, messenger, settings, profile): def group_invite(groups_service): - def wrapped(tox, friend_number, invite_data, length, user_data): + def wrapped(tox, friend_number, invite_data, length, group_name, group_name_length, user_data): + group_name = bytes(group_name[:group_name_length]) invoke_in_main_thread(groups_service.process_group_invite, - friend_number, + friend_number, str(group_name, 'utf-8'), bytes(invite_data[:length])) return wrapped diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index b4c401c..96e7fd5 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -2199,7 +2199,8 @@ class Tox: user_data - user data """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, + POINTER(c_uint8), c_size_t, c_void_p) self.group_invite_cb = c_callback(callback) Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, self.group_invite_cb, user_data) From 47c115e6993e20a501cf711408e98a843fb1d25e Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 30 Jun 2018 14:56:41 +0300 Subject: [PATCH 078/217] avatars support fixed --- .gitignore | 2 ++ toxygen/app.py | 6 +++--- toxygen/contacts/basecontact.py | 5 ++--- toxygen/contacts/contact.py | 4 ++-- toxygen/contacts/friend.py | 4 ++-- toxygen/contacts/friend_factory.py | 5 ++--- toxygen/contacts/group_chat.py | 4 ++-- toxygen/contacts/group_factory.py | 5 ++--- toxygen/contacts/group_peer_contact.py | 4 ++-- toxygen/contacts/profile.py | 3 +-- toxygen/file_transfers/file_transfers.py | 8 ++------ 11 files changed, 22 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index f31ca4c..0a8182a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ html Toxygen.egg-info *.tox .cache +*.db + diff --git a/toxygen/app.py b/toxygen/app.py index 60bb8bf..f3fd429 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -332,11 +332,11 @@ class App: db = Database(self._path.replace('.tox', '.db'), self._toxes) contact_items_factory = ContactItemsFactory(self._settings, self._ms) - self._friend_factory = FriendFactory(self._profile_manager, self._settings, + self._friend_factory = FriendFactory(self._settings, self._tox, db, contact_items_factory) - self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) + self._group_factory = GroupFactory(self._settings, self._tox, db, contact_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory) - self._profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) + self._profile = Profile(self._tox, self._ms, self._contacts_provider, self._reset) self._plugin_loader = PluginLoader(self._tox, self._toxes, self._profile, self._settings) history = None messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 0357809..0a7a456 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -14,14 +14,13 @@ class BaseContact: Base class for all contacts. """ - def __init__(self, profile_manager, name, status_message, widget, tox_id): + def __init__(self, name, status_message, widget, tox_id): """ :param name: name, example: 'Toxygen user' :param status_message: status message, example: 'Toxing on Toxygen' :param widget: ContactItem instance :param tox_id: tox id of contact """ - self._profile_manager = profile_manager self._name, self._status_message = name, status_message self._status, self._widget = None, widget self._tox_id = tox_id @@ -144,7 +143,7 @@ class BaseContact: return avatar_path def get_contact_avatar_path(self): - directory = util.join_path(self._profile_manager.get_dir(), 'avatars') + directory = util.join_path(Settings.get_default_path(), 'avatars') return util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index fb124dd..4969b73 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -12,12 +12,12 @@ class Contact(basecontact.BaseContact): Properties: number, message getter, history etc. Base class for friend and gc classes """ - def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id): + def __init__(self, message_getter, number, name, status_message, widget, tox_id): """ :param message_getter: gets messages from db :param number: number of friend. """ - super().__init__(profile_manager, name, status_message, widget, tox_id) + super().__init__(name, status_message, widget, tox_id) self._number = number self._new_messages = False self._visible = True diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 5c8eabb..142185a 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -9,8 +9,8 @@ class Friend(contact.Contact): Friend in list of friends. """ - def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id): - super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) + def __init__(self, message_getter, number, name, status_message, widget, tox_id): + super().__init__(message_getter, number, name, status_message, widget, tox_id) self._receipts = 0 self._typing_notification_handler = common.FriendTypingNotificationHandler(number) diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py index 8ebafd6..a85f4c9 100644 --- a/toxygen/contacts/friend_factory.py +++ b/toxygen/contacts/friend_factory.py @@ -4,9 +4,8 @@ from common.tox_save import ToxSave class FriendFactory(ToxSave): - def __init__(self, profile_manager, settings, tox, db, items_factory): + def __init__(self, settings, tox, db, items_factory): super().__init__(tox) - self._profile_manager = profile_manager self._settings = settings self._db = db self._items_factory = items_factory @@ -27,7 +26,7 @@ class FriendFactory(ToxSave): name = alias or self._tox.friend_get_name(friend_number) or tox_id status_message = self._tox.friend_get_status_message(friend_number) message_getter = self._db.messages_getter(tox_id) - friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, tox_id) + friend = Friend(message_getter, friend_number, name, status_message, item, tox_id) friend.set_alias(alias) return friend diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index b7429d4..0feed0f 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -8,8 +8,8 @@ from common.tox_save import ToxSave class GroupChat(contact.Contact, ToxSave): - def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id): - super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) + def __init__(self, tox, message_getter, number, name, status_message, widget, tox_id): + super().__init__(message_getter, number, name, status_message, widget, tox_id) ToxSave.__init__(self, tox) self.set_status(constants.TOX_USER_STATUS['NONE']) self._peers = [] diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py index 8db3e9a..0bd3285 100644 --- a/toxygen/contacts/group_factory.py +++ b/toxygen/contacts/group_factory.py @@ -4,9 +4,8 @@ from common.tox_save import ToxSave class GroupFactory(ToxSave): - def __init__(self, profile_manager, settings, tox, db, items_factory): + def __init__(self, settings, tox, db, items_factory): super().__init__(tox) - self._profile_manager = profile_manager self._settings = settings self._db = db self._items_factory = items_factory @@ -27,7 +26,7 @@ class GroupFactory(ToxSave): name = alias or self._tox.group_get_name(group_number) or tox_id status_message = self._tox.group_get_topic(group_number) message_getter = self._db.messages_getter(tox_id) - group = GroupChat(self._tox, self._profile_manager, message_getter, group_number, name, status_message, + group = GroupChat(self._tox, message_getter, group_number, name, status_message, item, tox_id) group.set_alias(alias) diff --git a/toxygen/contacts/group_peer_contact.py b/toxygen/contacts/group_peer_contact.py index 1c51f67..6480985 100644 --- a/toxygen/contacts/group_peer_contact.py +++ b/toxygen/contacts/group_peer_contact.py @@ -3,8 +3,8 @@ import contacts.contact class GroupPeerContact(contacts.contact.Contact): - def __init__(self, profile_manager, message_getter, peer_number, name, status_messsage, widget, tox_id, group_pk): - super().__init__(profile_manager, message_getter, peer_number, name, status_messsage, widget, tox_id) + def __init__(self, message_getter, peer_number, name, status_messsage, widget, tox_id, group_pk): + super().__init__(message_getter, peer_number, name, status_messsage, widget, tox_id) self._group_pk = group_pk def get_group_pk(self): diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 3529be4..847039b 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -8,13 +8,12 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave): """ Profile of current toxygen user. Contains friends list, tox instance """ - def __init__(self, profile_manager, tox, screen, contacts_provider, reset_action): + def __init__(self, tox, screen, contacts_provider, reset_action): """ :param tox: tox instance :param screen: ref to main screen """ basecontact.BaseContact.__init__(self, - profile_manager, tox.self_get_name(), tox.self_get_status_message(), screen.user_info, diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 61a379f..46a777f 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -50,9 +50,6 @@ class FileTransfer: self._finished_event = Event() self._file_id = self._file = None - def set_tox(self, tox): - self._tox = tox - def set_state_changed_handler(self, handler): self._state_changed_event += handler @@ -344,11 +341,10 @@ class ReceiveAvatar(ReceiveTransfer): self.send_control(TOX_FILE_CONTROL['RESUME']) def write_chunk(self, position, data): - super().write_chunk(position, data) - if self.state: + if data is None: avatar_path = self._path[:-4] if exists(avatar_path): chdir(dirname(avatar_path)) remove(avatar_path) rename(self._path, avatar_path) - self._finished() + super().write_chunk(position, data) From 8411f08348008f5c8f3bef7e1d023aefe3801588 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 30 Jun 2018 15:23:04 +0300 Subject: [PATCH 079/217] bug with avatars fixed. bug with contacts statuses during reconnection was fixed --- toxygen/app.py | 1 + toxygen/contacts/contacts_manager.py | 8 ++++++++ toxygen/file_transfers/file_transfers_handler.py | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/toxygen/app.py b/toxygen/app.py index f3fd429..db3ddc8 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -306,6 +306,7 @@ class App: Create new tox instance (new network settings) :return: tox instance """ + self._contacts_manager.reset_contacts_statuses() self._stop_threads(False) data = self._tox.get_savedata() self._save_profile(data) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 08ee678..46e98a2 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -55,6 +55,14 @@ class ContactsManager(ToxSave): def is_contact_active(self, contact): return self._contacts[self._active_contact].tox_id == contact.tox_id + # ----------------------------------------------------------------------------------------------------------------- + # Reconnection support + # ----------------------------------------------------------------------------------------------------------------- + + def reset_contacts_statuses(self): + for contact in self._contacts: + contact.status = None + # ----------------------------------------------------------------------------------------------------------------- # Work with active friend # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 64a2bfe..d462c89 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -254,7 +254,7 @@ class FileTransfersHandler(ToxSave): if ra.state != FILE_TRANSFER_STATE['CANCELLED']: self._file_transfers[(friend_number, file_number)] = ra ra.set_transfer_finished_handler(self.transfer_finished) - else: + elif not size: friend.reset_avatar(self._settings['identicons']) def _send_avatar_to_contacts(self): From 04f0aef3df92a7c26851cbc42d081634fc5943e0 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 30 Jun 2018 18:49:25 +0300 Subject: [PATCH 080/217] Threads fixed --- toxygen/middleware/callbacks.py | 10 ++-------- toxygen/middleware/threads.py | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 41a5717..513cdd2 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -198,10 +198,7 @@ def file_recv_chunk(file_transfer_handler): """ def wrapped(tox, friend_number, file_number, position, chunk, length, user_data): chunk = chunk[:length] if length else None - if length: - execute(file_transfer_handler.incoming_chunk, friend_number, file_number, position, chunk) - else: - invoke_in_main_thread(file_transfer_handler.incoming_chunk, friend_number, file_number, position, chunk) + execute(file_transfer_handler.incoming_chunk, friend_number, file_number, position, chunk) return wrapped @@ -211,10 +208,7 @@ def file_chunk_request(file_transfer_handler): Outgoing chunk """ def wrapped(tox, friend_number, file_number, position, size, user_data): - if size: - execute(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) - else: - invoke_in_main_thread(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) + invoke_in_main_thread(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) return wrapped diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py index 2e432b6..192f723 100644 --- a/toxygen/middleware/threads.py +++ b/toxygen/middleware/threads.py @@ -3,8 +3,13 @@ import threading import queue from utils import util import time +from PyQt5 import QtCore +# ----------------------------------------------------------------------------------------------------------------- +# Base threads +# ----------------------------------------------------------------------------------------------------------------- + class BaseThread(threading.Thread): def __init__(self): @@ -16,6 +21,21 @@ class BaseThread(threading.Thread): self.join() +class BaseQThread(QtCore.QThread): + + def __init__(self): + super().__init__() + self._stop_thread = False + + def stop_thread(self): + self._stop_thread = True + self.wait() + + +# ----------------------------------------------------------------------------------------------------------------- +# Toxcore threads +# ----------------------------------------------------------------------------------------------------------------- + class InitThread(BaseThread): def __init__(self, tox, plugin_loader, settings): @@ -53,7 +73,7 @@ class InitThread(BaseThread): time.sleep(5) -class ToxIterateThread(BaseThread): +class ToxIterateThread(BaseQThread): def __init__(self, tox): super().__init__() @@ -65,7 +85,7 @@ class ToxIterateThread(BaseThread): time.sleep(self._tox.iteration_interval() / 1000) -class ToxAVIterateThread(BaseThread): +class ToxAVIterateThread(BaseQThread): def __init__(self, toxav): super().__init__() @@ -77,6 +97,10 @@ class ToxAVIterateThread(BaseThread): time.sleep(self._toxav.iteration_interval() / 1000) +# ----------------------------------------------------------------------------------------------------------------- +# File transfers thread +# ----------------------------------------------------------------------------------------------------------------- + class FileTransfersThread(BaseThread): def __init__(self): @@ -115,6 +139,10 @@ def execute(func, *args, **kwargs): _thread.execute(func, *args, **kwargs) +# ----------------------------------------------------------------------------------------------------------------- +# Invoking in main thread +# ----------------------------------------------------------------------------------------------------------------- + class InvokeEvent(QtCore.QEvent): EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType()) From a0cae147276b40c928570b3b6d2af16a1eddb137 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 30 Jun 2018 18:57:41 +0300 Subject: [PATCH 081/217] travis.yml updated --- .travis.yml | 7 +++---- MANIFEST.in | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8de5702..d4a7fae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,15 +38,14 @@ before_script: - sudo ldconfig - cd .. # Toxcore - - git clone https://github.com/ingvar1995/toxcore.git --branch=new_gc + - git clone https://github.com/ingvar1995/toxcore.git --branch=ngc_rebase - cd toxcore - - autoreconf -if - - ./configure + - mkdir _build && cd _build + - cmake .. - make -j$(nproc) - sudo make install - echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf - sudo ldconfig - - cd .. script: - py.test tests/travis.py - py.test tests/tests.py diff --git a/MANIFEST.in b/MANIFEST.in index 6629fb6..89e57c6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -16,4 +16,4 @@ include toxygen/styles/*.qss include toxygen/translations/*.qm include toxygen/libs/libtox.dll include toxygen/libs/libsodium.a -include toxygen/nodes.json +include toxygen/bootstrap/nodes.json From 595c35a6b874025a264514d2047cf01c130a4793 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 30 Jun 2018 19:54:08 +0300 Subject: [PATCH 082/217] network settings screen converted --- .travis.yml | 1 + toxygen/main.py | 10 +- toxygen/styles/dark_style.qss | 5 + toxygen/styles/style.qss | 5 + toxygen/ui/menu.py | 119 ++++++------- toxygen/ui/views/network_settings_screen.ui | 180 ++++++++++++++++++++ 6 files changed, 245 insertions(+), 75 deletions(-) create mode 100644 toxygen/ui/views/network_settings_screen.ui diff --git a/.travis.yml b/.travis.yml index d4a7fae..378e5e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,7 @@ before_script: - sudo make install - echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf - sudo ldconfig + - cd .. script: - py.test tests/travis.py - py.test tests/tests.py diff --git a/toxygen/main.py b/toxygen/main.py index 232ad93..eca3ac3 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -9,7 +9,7 @@ __version__ = '0.5.0' def clean(): - """Removes all windows libs from libs folder""" + """Removes libs folder""" directory = util.get_libs_directory() util.remove(directory) @@ -25,10 +25,10 @@ def print_toxygen_version(): def main(): parser = argparse.ArgumentParser() parser.add_argument('--version', action='store_true', help='Prints Toxygen version') - parser.add_argument('--clean', action='store_true', help='Deletes toxcore libs from libs folder') - parser.add_argument('--reset', action='store_true', help='Resets default profile') - parser.add_argument('--uri', help='Adds specified TOX ID to friends') - parser.add_argument('profile', nargs='?', default=None, help='Path to TOX profile') + parser.add_argument('--clean', action='store_true', help='Delete toxcore libs from libs folder') + parser.add_argument('--reset', action='store_true', help='Reset default profile') + parser.add_argument('--uri', help='Add specified Tox ID to friends') + parser.add_argument('profile', nargs='?', default=None, help='Path to Tox profile') args = parser.parse_args() if args.version: diff --git a/toxygen/styles/dark_style.qss b/toxygen/styles/dark_style.qss index 714f946..3b2837b 100644 --- a/toxygen/styles/dark_style.qss +++ b/toxygen/styles/dark_style.qss @@ -1322,3 +1322,8 @@ ClickableLabel:hover { background-color: #4A4949; } + +#warningLabel +{ + color: #BC1C1C; +} diff --git a/toxygen/styles/style.qss b/toxygen/styles/style.qss index 26fbaf2..956ee63 100644 --- a/toxygen/styles/style.qss +++ b/toxygen/styles/style.qss @@ -27,3 +27,8 @@ MessageEdit { background-color: transparent; } + +#warningLabel +{ + color: #BC1C1C; +} diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 6c9218c..07fbb85 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -254,82 +254,61 @@ class NetworkSettings(CenteredWidget): super().__init__() self._settings = settings self._reset = reset - self.initUI() - self.center() + uic.loadUi(get_views_path('network_settings_screen'), self) + self._update_ui() - def initUI(self): - self.setObjectName("NetworkSettings") - self.resize(300, 400) - self.setMinimumSize(QtCore.QSize(300, 400)) - self.setMaximumSize(QtCore.QSize(300, 400)) - self.setBaseSize(QtCore.QSize(300, 400)) - self.ipv = QtWidgets.QCheckBox(self) - self.ipv.setGeometry(QtCore.QRect(20, 10, 97, 22)) - self.ipv.setObjectName("ipv") - self.udp = QtWidgets.QCheckBox(self) - self.udp.setGeometry(QtCore.QRect(150, 10, 97, 22)) - self.udp.setObjectName("udp") - self.proxy = QtWidgets.QCheckBox(self) - self.proxy.setGeometry(QtCore.QRect(20, 40, 97, 22)) - self.http = QtWidgets.QCheckBox(self) - self.http.setGeometry(QtCore.QRect(20, 70, 97, 22)) - self.proxy.setObjectName("proxy") - self.proxyip = LineEdit(self) - self.proxyip.setGeometry(QtCore.QRect(40, 130, 231, 27)) - self.proxyip.setObjectName("proxyip") - self.proxyport = LineEdit(self) - self.proxyport.setGeometry(QtCore.QRect(40, 190, 231, 27)) - self.proxyport.setObjectName("proxyport") - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(40, 100, 66, 17)) - self.label_2 = QtWidgets.QLabel(self) - self.label_2.setGeometry(QtCore.QRect(40, 165, 66, 17)) - self.reconnect = QtWidgets.QPushButton(self) - self.reconnect.setGeometry(QtCore.QRect(40, 230, 231, 30)) - self.reconnect.clicked.connect(self.restart_core) - self.ipv.setChecked(self._settings['ipv6_enabled']) - self.udp.setChecked(self._settings['udp_enabled']) - self.proxy.setChecked(self._settings['proxy_type']) - self.proxyip.setText(self._settings['proxy_host']) - self.proxyport.setText(str(self._settings['proxy_port'])) - self.http.setChecked(self._settings['proxy_type'] == 1) - self.warning = QtWidgets.QLabel(self) - self.warning.setGeometry(QtCore.QRect(5, 270, 290, 60)) - self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') - self.nodes = QtWidgets.QCheckBox(self) - self.nodes.setGeometry(QtCore.QRect(20, 350, 270, 22)) - self.nodes.setChecked(self._settings['download_nodes_list']) - self.retranslateUi() - self.proxy.stateChanged.connect(lambda x: self.activate()) - self.activate() - QtCore.QMetaObject.connectSlotsByName(self) + def _update_ui(self): + self.ipLineEdit = LineEdit(self) + self.ipLineEdit.setGeometry(100, 280, 270, 30) + self.portLineEdit = LineEdit(self) + self.portLineEdit.setGeometry(100, 325, 270, 30) + self.restartCorePushButton.clicked.connect(self._restart_core) + self.ipv6CheckBox.setChecked(self._settings['ipv6_enabled']) + self.udpCheckBox.setChecked(self._settings['udp_enabled']) + self.proxyCheckBox.setChecked(self._settings['proxy_type']) + self.ipLineEdit.setText(self._settings['proxy_host']) + self.portLineEdit.setText(str(self._settings['proxy_port'])) + self.httpProxyRadioButton.setChecked(self._settings['proxy_type'] == 1) + self.socksProxyRadioButton.setChecked(self._settings['proxy_type'] != 1) + self.downloadNodesCheckBox.setChecked(self._settings['download_nodes_list']) + self.lanCheckBox.setChecked(self._settings['lan_discovery']) + self._retranslate_ui() + self.proxyCheckBox.stateChanged.connect(lambda x: self._activate_proxy()) + self._activate_proxy() - def retranslateUi(self): + def _retranslate_ui(self): self.setWindowTitle(util_ui.tr("Network settings")) - self.ipv.setText(util_ui.tr("IPv6")) - self.udp.setText(util_ui.tr("UDP")) - self.proxy.setText(util_ui.tr("Proxy")) - self.label.setText(util_ui.tr("IP:")) - self.label_2.setText(util_ui.tr("Port:")) - self.reconnect.setText(util_ui.tr("Restart TOX core")) - self.http.setText(util_ui.tr("HTTP")) - self.nodes.setText(util_ui.tr("Download nodes list from tox.chat")) - self.warning.setText(util_ui.tr("WARNING:\nusing proxy with enabled UDP\ncan produce IP leak")) + self.ipv6CheckBox.setText(util_ui.tr("IPv6")) + self.udpCheckBox.setText(util_ui.tr("UDP")) + self.lanCheckBox.setText(util_ui.tr("LAN")) + self.proxyCheckBox.setText(util_ui.tr("Proxy")) + self.ipLabel.setText(util_ui.tr("IP:")) + self.portLabel.setText(util_ui.tr("Port:")) + self.restartCorePushButton.setText(util_ui.tr("Restart TOX core")) + self.httpProxyRadioButton.setText(util_ui.tr("HTTP")) + self.socksProxyRadioButton.setText(util_ui.tr("Socks 5")) + self.downloadNodesCheckBox.setText(util_ui.tr("Download nodes list from tox.chat")) + self.warningLabel.setText(util_ui.tr("WARNING:\nusing proxy with enabled UDP\ncan produce IP leak")) - def activate(self): - bl = self.proxy.isChecked() - self.proxyip.setEnabled(bl) - self.http.setEnabled(bl) - self.proxyport.setEnabled(bl) + def _activate_proxy(self): + bl = self.proxyCheckBox.isChecked() + self.ipLineEdit.setEnabled(bl) + self.portLineEdit.setEnabled(bl) + self.httpProxyRadioButton.setEnabled(bl) + self.socksProxyRadioButton.setEnabled(bl) + self.ipLabel.setEnabled(bl) + self.portLabel.setEnabled(bl) - def restart_core(self): + def _restart_core(self): try: - self._settings['ipv6_enabled'] = self.ipv.isChecked() - self._settings['udp_enabled'] = self.udp.isChecked() - self._settings['proxy_type'] = 2 - int(self.http.isChecked()) if self.proxy.isChecked() else 0 - self._settings['proxy_host'] = str(self.proxyip.text()) - self._settings['proxy_port'] = int(self.proxyport.text()) - self._settings['download_nodes_list'] = self.nodes.isChecked() + self._settings['ipv6_enabled'] = self.ipv6CheckBox.isChecked() + self._settings['udp_enabled'] = self.udpCheckBox.isChecked() + proxy_enabled = self.proxyCheckBox.isChecked() + self._settings['proxy_type'] = 2 - int(self.httpProxyRadioButton.isChecked()) if proxy_enabled else 0 + self._settings['proxy_host'] = str(self.ipLineEdit.text()) + self._settings['proxy_port'] = int(self.portLineEdit.text()) + self._settings['download_nodes_list'] = self.downloadNodesCheckBox.isChecked() + self._settings['lan_discovery'] = self.lanCheckBox.isChecked() self._settings.save() # recreate tox instance self._reset() diff --git a/toxygen/ui/views/network_settings_screen.ui b/toxygen/ui/views/network_settings_screen.ui new file mode 100644 index 0000000..aacf1e0 --- /dev/null +++ b/toxygen/ui/views/network_settings_screen.ui @@ -0,0 +1,180 @@ + + + Form + + + + 0 + 0 + 400 + 500 + + + + + 400 + 500 + + + + + 400 + 500 + + + + Form + + + + + 30 + 20 + 150 + 30 + + + + CheckBox + + + + + + 210 + 20 + 150 + 30 + + + + CheckBox + + + + + + 30 + 140 + 150 + 30 + + + + CheckBox + + + + + + 30 + 190 + 150 + 25 + + + + RadioButton + + + + + + 30 + 230 + 150 + 25 + + + + RadioButton + + + + + + 30 + 100 + 150 + 30 + + + + CheckBox + + + + + + 30 + 280 + 60 + 20 + + + + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 30 + 330 + 60 + 20 + + + + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 30 + 370 + 340 + 40 + + + + PushButton + + + + + + 30 + 60 + 340 + 30 + + + + CheckBox + + + + + + 30 + 420 + 340 + 65 + + + + TextLabel + + + + + + From 0adb9c1e5219513051ee480fbf3b267468798a43 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 30 Jun 2018 20:02:44 +0300 Subject: [PATCH 083/217] fixed wrong avatars directory path --- .travis.yml | 1 + toxygen/user_data/profile_manager.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 378e5e5..a4011e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,6 +47,7 @@ before_script: - echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf - sudo ldconfig - cd .. + - cd .. script: - py.test tests/travis.py - py.test tests/tests.py diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index 3324fce..6643d68 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -13,7 +13,7 @@ class ProfileManager: self._path = path self._directory = os.path.dirname(path) # create /avatars if not exists: - avatars_directory = util.join_path(self._directory, 'avatars') + avatars_directory = util.join_path(Settings.get_default_path(), 'avatars') if not os.path.exists(avatars_directory): os.makedirs(avatars_directory) From bc485372090f40b529086581a2095f03d83ae130 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 2 Jul 2018 22:50:46 +0300 Subject: [PATCH 084/217] Revert "avatars support fixed" This reverts commit 47c115e6993e20a501cf711408e98a843fb1d25e. --- .gitignore | 2 -- toxygen/app.py | 6 +++--- toxygen/contacts/basecontact.py | 5 +++-- toxygen/contacts/contact.py | 4 ++-- toxygen/contacts/friend.py | 4 ++-- toxygen/contacts/friend_factory.py | 5 +++-- toxygen/contacts/group_chat.py | 4 ++-- toxygen/contacts/group_factory.py | 5 +++-- toxygen/contacts/group_peer_contact.py | 4 ++-- toxygen/contacts/profile.py | 3 ++- toxygen/file_transfers/file_transfers.py | 8 ++++++-- 11 files changed, 28 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 0a8182a..f31ca4c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,5 +24,3 @@ html Toxygen.egg-info *.tox .cache -*.db - diff --git a/toxygen/app.py b/toxygen/app.py index db3ddc8..3d20389 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -333,11 +333,11 @@ class App: db = Database(self._path.replace('.tox', '.db'), self._toxes) contact_items_factory = ContactItemsFactory(self._settings, self._ms) - self._friend_factory = FriendFactory(self._settings, + self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) - self._group_factory = GroupFactory(self._settings, self._tox, db, contact_items_factory) + self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory) - self._profile = Profile(self._tox, self._ms, self._contacts_provider, self._reset) + self._profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) self._plugin_loader = PluginLoader(self._tox, self._toxes, self._profile, self._settings) history = None messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 0a7a456..0357809 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -14,13 +14,14 @@ class BaseContact: Base class for all contacts. """ - def __init__(self, name, status_message, widget, tox_id): + def __init__(self, profile_manager, name, status_message, widget, tox_id): """ :param name: name, example: 'Toxygen user' :param status_message: status message, example: 'Toxing on Toxygen' :param widget: ContactItem instance :param tox_id: tox id of contact """ + self._profile_manager = profile_manager self._name, self._status_message = name, status_message self._status, self._widget = None, widget self._tox_id = tox_id @@ -143,7 +144,7 @@ class BaseContact: return avatar_path def get_contact_avatar_path(self): - directory = util.join_path(Settings.get_default_path(), 'avatars') + directory = util.join_path(self._profile_manager.get_dir(), 'avatars') return util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 4969b73..fb124dd 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -12,12 +12,12 @@ class Contact(basecontact.BaseContact): Properties: number, message getter, history etc. Base class for friend and gc classes """ - def __init__(self, message_getter, number, name, status_message, widget, tox_id): + def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id): """ :param message_getter: gets messages from db :param number: number of friend. """ - super().__init__(name, status_message, widget, tox_id) + super().__init__(profile_manager, name, status_message, widget, tox_id) self._number = number self._new_messages = False self._visible = True diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 142185a..5c8eabb 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -9,8 +9,8 @@ class Friend(contact.Contact): Friend in list of friends. """ - def __init__(self, message_getter, number, name, status_message, widget, tox_id): - super().__init__(message_getter, number, name, status_message, widget, tox_id) + def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id): + super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) self._receipts = 0 self._typing_notification_handler = common.FriendTypingNotificationHandler(number) diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py index a85f4c9..8ebafd6 100644 --- a/toxygen/contacts/friend_factory.py +++ b/toxygen/contacts/friend_factory.py @@ -4,8 +4,9 @@ from common.tox_save import ToxSave class FriendFactory(ToxSave): - def __init__(self, settings, tox, db, items_factory): + def __init__(self, profile_manager, settings, tox, db, items_factory): super().__init__(tox) + self._profile_manager = profile_manager self._settings = settings self._db = db self._items_factory = items_factory @@ -26,7 +27,7 @@ class FriendFactory(ToxSave): name = alias or self._tox.friend_get_name(friend_number) or tox_id status_message = self._tox.friend_get_status_message(friend_number) message_getter = self._db.messages_getter(tox_id) - friend = Friend(message_getter, friend_number, name, status_message, item, tox_id) + friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, tox_id) friend.set_alias(alias) return friend diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 0feed0f..b7429d4 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -8,8 +8,8 @@ from common.tox_save import ToxSave class GroupChat(contact.Contact, ToxSave): - def __init__(self, tox, message_getter, number, name, status_message, widget, tox_id): - super().__init__(message_getter, number, name, status_message, widget, tox_id) + def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id): + super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) ToxSave.__init__(self, tox) self.set_status(constants.TOX_USER_STATUS['NONE']) self._peers = [] diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py index 0bd3285..8db3e9a 100644 --- a/toxygen/contacts/group_factory.py +++ b/toxygen/contacts/group_factory.py @@ -4,8 +4,9 @@ from common.tox_save import ToxSave class GroupFactory(ToxSave): - def __init__(self, settings, tox, db, items_factory): + def __init__(self, profile_manager, settings, tox, db, items_factory): super().__init__(tox) + self._profile_manager = profile_manager self._settings = settings self._db = db self._items_factory = items_factory @@ -26,7 +27,7 @@ class GroupFactory(ToxSave): name = alias or self._tox.group_get_name(group_number) or tox_id status_message = self._tox.group_get_topic(group_number) message_getter = self._db.messages_getter(tox_id) - group = GroupChat(self._tox, message_getter, group_number, name, status_message, + group = GroupChat(self._tox, self._profile_manager, message_getter, group_number, name, status_message, item, tox_id) group.set_alias(alias) diff --git a/toxygen/contacts/group_peer_contact.py b/toxygen/contacts/group_peer_contact.py index 6480985..1c51f67 100644 --- a/toxygen/contacts/group_peer_contact.py +++ b/toxygen/contacts/group_peer_contact.py @@ -3,8 +3,8 @@ import contacts.contact class GroupPeerContact(contacts.contact.Contact): - def __init__(self, message_getter, peer_number, name, status_messsage, widget, tox_id, group_pk): - super().__init__(message_getter, peer_number, name, status_messsage, widget, tox_id) + def __init__(self, profile_manager, message_getter, peer_number, name, status_messsage, widget, tox_id, group_pk): + super().__init__(profile_manager, message_getter, peer_number, name, status_messsage, widget, tox_id) self._group_pk = group_pk def get_group_pk(self): diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 847039b..3529be4 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -8,12 +8,13 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave): """ Profile of current toxygen user. Contains friends list, tox instance """ - def __init__(self, tox, screen, contacts_provider, reset_action): + def __init__(self, profile_manager, tox, screen, contacts_provider, reset_action): """ :param tox: tox instance :param screen: ref to main screen """ basecontact.BaseContact.__init__(self, + profile_manager, tox.self_get_name(), tox.self_get_status_message(), screen.user_info, diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 46a777f..61a379f 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -50,6 +50,9 @@ class FileTransfer: self._finished_event = Event() self._file_id = self._file = None + def set_tox(self, tox): + self._tox = tox + def set_state_changed_handler(self, handler): self._state_changed_event += handler @@ -341,10 +344,11 @@ class ReceiveAvatar(ReceiveTransfer): self.send_control(TOX_FILE_CONTROL['RESUME']) def write_chunk(self, position, data): - if data is None: + super().write_chunk(position, data) + if self.state: avatar_path = self._path[:-4] if exists(avatar_path): chdir(dirname(avatar_path)) remove(avatar_path) rename(self._path, avatar_path) - super().write_chunk(position, data) + self._finished() From e8193afedfc95995b40f80fe261a9a4212e01d9e Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 2 Jul 2018 22:53:07 +0300 Subject: [PATCH 085/217] fixes after revert --- .gitignore | 2 ++ toxygen/contacts/common.py | 20 ++++++++++---------- toxygen/file_transfers/file_transfers.py | 7 ++----- toxygen/user_data/profile_manager.py | 2 +- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index f31ca4c..0a8182a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ html Toxygen.egg-info *.tox .cache +*.db + diff --git a/toxygen/contacts/common.py b/toxygen/contacts/common.py index b37c6a2..4dc25e8 100644 --- a/toxygen/contacts/common.py +++ b/toxygen/contacts/common.py @@ -25,15 +25,15 @@ class FriendTypingNotificationHandler(BaseTypingNotificationHandler): BaseTypingNotificationHandler.DEFAULT_HANDLER = BaseTypingNotificationHandler() -def generate_avatar(tox_id): - foreground = ["rgb(45,79,255)", - "rgb(254,180,44)", - "rgb(226,121,234)", - "rgb(30,179,253)", - "rgb(232,77,65)", - "rgb(49,203,115)", - "rgb(141,69,170)"] - generator = Generator(5, 5, foreground=foreground, background="rgba(42,42,42,0)") - identicon = generator.generate(tox_id, 220, 220, padding=(10, 10, 10, 10)) +def generate_avatar(public_key): + foreground = ['rgb(45,79,255)', 'rgb(185, 66, 244)', 'rgb(185, 66, 244)', + 'rgb(254,180,44)', 'rgb(252, 2, 2)', 'rgb(109, 198, 0)', + 'rgb(226,121,234)', 'rgb(130, 135, 124)', + 'rgb(30,179,253)', 'rgb(160, 157, 0)', + 'rgb(232,77,65)', 'rgb(102, 4, 4)', + 'rgb(49,203,115)', + 'rgb(141,69,170)'] + generator = Generator(5, 5, foreground=foreground, background='rgba(42,42,42,0)') + identicon = generator.generate(public_key, 220, 220, padding=(10, 10, 10, 10)) return identicon diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 61a379f..c2d1168 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -50,9 +50,6 @@ class FileTransfer: self._finished_event = Event() self._file_id = self._file = None - def set_tox(self, tox): - self._tox = tox - def set_state_changed_handler(self, handler): self._state_changed_event += handler @@ -344,11 +341,11 @@ class ReceiveAvatar(ReceiveTransfer): self.send_control(TOX_FILE_CONTROL['RESUME']) def write_chunk(self, position, data): - super().write_chunk(position, data) - if self.state: + if data is None: avatar_path = self._path[:-4] if exists(avatar_path): chdir(dirname(avatar_path)) remove(avatar_path) rename(self._path, avatar_path) self._finished() + super().write_chunk(position, data) diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index 6643d68..3324fce 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -13,7 +13,7 @@ class ProfileManager: self._path = path self._directory = os.path.dirname(path) # create /avatars if not exists: - avatars_directory = util.join_path(Settings.get_default_path(), 'avatars') + avatars_directory = util.join_path(self._directory, 'avatars') if not os.path.exists(avatars_directory): os.makedirs(avatars_directory) From d09609a5e5a2254f82ab4077cbc9e9feccc9ea0d Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 5 Jul 2018 00:26:05 +0300 Subject: [PATCH 086/217] fixes after revert. identicons update --- toxygen/app.py | 5 ++++- toxygen/contacts/common.py | 4 +++- toxygen/file_transfers/file_transfers.py | 1 - 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 3d20389..3f66e2c 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -299,7 +299,7 @@ class App: self._app.exec_() if p.result is not None: return p.result - raise SystemExit() + self._force_exit() def _reset(self): """ @@ -380,6 +380,9 @@ class App: def _create_tox(self, data): return tox_factory(data, self._settings) + def _force_exit(self): + raise SystemExit() + def _init_callbacks(self): callbacks.init_callbacks(self._tox, self._profile, self._settings, self._plugin_loader, self._contacts_manager, self._calls_manager, self._file_transfer_handler, self._ms, self._tray, diff --git a/toxygen/contacts/common.py b/toxygen/contacts/common.py index 4dc25e8..ba5dac6 100644 --- a/toxygen/contacts/common.py +++ b/toxygen/contacts/common.py @@ -1,4 +1,5 @@ from pydenticon import Generator +import hashlib class BaseTypingNotificationHandler: @@ -34,6 +35,7 @@ def generate_avatar(public_key): 'rgb(49,203,115)', 'rgb(141,69,170)'] generator = Generator(5, 5, foreground=foreground, background='rgba(42,42,42,0)') - identicon = generator.generate(public_key, 220, 220, padding=(10, 10, 10, 10)) + digest = hashlib.sha256(public_key.encode('utf-8')).hexdigest() + identicon = generator.generate(digest, 220, 220, padding=(10, 10, 10, 10)) return identicon diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index c2d1168..46a777f 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -347,5 +347,4 @@ class ReceiveAvatar(ReceiveTransfer): chdir(dirname(avatar_path)) remove(avatar_path) rename(self._path, avatar_path) - self._finished() super().write_chunk(position, data) From 7aac248bf9b81546e147bdcfb3d3513f0d2c3b84 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 10 Jul 2018 00:41:08 +0300 Subject: [PATCH 087/217] identicons fixes. sending messages button fixed --- toxygen/contacts/basecontact.py | 5 +++++ toxygen/contacts/contacts_manager.py | 19 ++++++++++++------- toxygen/ui/main_screen.py | 3 ++- toxygen/utils/util.py | 4 ++++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 0357809..fa709db 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -148,6 +148,11 @@ class BaseContact: return util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) + def has_avatar(self): + path = self.get_contact_avatar_path() + + return util.file_exists(path) + def get_avatar_changed_event(self): return self._avatar_changed_event diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 46e98a2..6812391 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -296,11 +296,7 @@ class ContactsManager(ToxSave): Adds friend to list """ self._tox.friend_add_norequest(tox_id) - self._history.add_friend_to_db(tox_id) - friend = self._contact_provider.get_friend_by_public_key(tox_id) - self._contacts.append(friend) - friend.reset_avatar(self._settings['identicons']) - self._save_profile() + self._add_friend(tox_id) def block_user(self, tox_id): """ @@ -375,8 +371,7 @@ class ContactsManager(ToxSave): else: self._tox.friend_add(tox_id, message.encode('utf-8')) tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] - friend = self._contact_provider.get_friend_by_public_key(tox_id) - self._contacts.append(friend) + self._add_friend(tox_id) self.save_profile() return True except Exception as ex: # wrong data @@ -430,6 +425,8 @@ class ContactsManager(ToxSave): self._load_groups() if len(self._contacts): self.set_active(0) + for contact in filter(lambda c: not c.has_avatar(), self._contacts): + contact.reset_avatar(self._settings['identicons']) self.update_filtration() def _load_friends(self): @@ -477,6 +474,14 @@ class ContactsManager(ToxSave): self._screen.account_avatar.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) + def _add_friend(self, tox_id): + self._history.add_friend_to_db(tox_id) + friend = self._contact_provider.get_friend_by_public_key(tox_id) + self._contacts.append(friend) + if not friend.has_avatar(): + friend.reset_avatar(self._settings['identicons']) + self._save_profile() + def _save_profile(self): data = self._tox.get_savedata() self._profile_manager.save_profile(data) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index e88437a..5bcd15a 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -17,7 +17,7 @@ class MainWindow(QtWidgets.QMainWindow): self._plugins_loader = None self.setAcceptDrops(True) self._saved = False - self._profile = self._toxes = None + self._profile = self._toxes = self._messenger = None self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None self._should_show_group_peers_list = False self.initUI() @@ -34,6 +34,7 @@ class MainWindow(QtWidgets.QMainWindow): self._calls_manager = calls_manager self._groups_service = groups_service self._toxes = toxes + self._messenger = messenger self._contacts_manager.active_contact_changed.add_callback(self._new_contact_selected) self.messageEdit.set_messenger(messenger) diff --git a/toxygen/utils/util.py b/toxygen/utils/util.py index 5790a5b..0f718ef 100644 --- a/toxygen/utils/util.py +++ b/toxygen/utils/util.py @@ -102,6 +102,10 @@ def join_path(a, b): return os.path.join(a, b) +def file_exists(file_path): + return os.path.exists(file_path) + + def copy(src, dest): if not os.path.exists(dest): os.makedirs(dest) From 2a97beb5afa29cb14b511dca1c292294e0d1470d Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 15 Jul 2018 17:04:51 +0300 Subject: [PATCH 088/217] minor bug fixes --- toxygen/contacts/contacts_manager.py | 6 ------ toxygen/messenger/messenger.py | 7 +++++++ toxygen/ui/main_screen.py | 2 +- toxygen/ui/main_screen_widgets.py | 1 - 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 6812391..5d07bd2 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -226,12 +226,6 @@ class ContactsManager(ToxSave): def get_contact_by_tox_id(self, tox_id): return list(filter(lambda c: c.tox_id == tox_id, self._contacts))[0] - def get_last_message(self): - if self._active_contact + 1: - return self.get_curr_contact().get_last_message_text() - else: - return '' - def get_active_number(self): return self.get_curr_contact().number if self._active_contact + 1 else -1 diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index af66f0b..dbd2d1e 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -19,6 +19,13 @@ class Messenger(tox_save.ToxSave): calls_manager.call_started_event.add_callback(self._on_call_started) calls_manager.call_finished_event.add_callback(self._on_call_finished) + def get_last_message(self): + contact = self._contacts_manager.get_curr_contact() + if contact is None: + return str() + + return contact.get_last_message_text() + # ----------------------------------------------------------------------------------------------------------------- # Messaging - friends # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 5bcd15a..1bc3612 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -170,7 +170,7 @@ class MainWindow(QtWidgets.QMainWindow): self.online_contacts.addItem(util_ui.tr("Online and by name")) self.online_contacts.addItem(util_ui.tr("Online first and by name")) ind = self._settings['sorting'] - d = {0: 0, 1: 1, 2: 2, 3: 4, 1 | 4: 4, 2 | 4: 5} + d = {0: 0, 1: 1, 2: 2, 3: 4, 4: 3, 1 | 4: 4, 2 | 4: 5} self.online_contacts.setCurrentIndex(d[ind]) self.importPlugin.setText(util_ui.tr("Import plugin")) self.reloadPlugins.setText(util_ui.tr("Reload plugins")) diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index 479575e..0945b0c 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -1,6 +1,5 @@ from PyQt5 import QtCore, QtGui, QtWidgets from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit -from contacts.profile import Profile import urllib import utils.util as util import utils.ui as util_ui From 9c742d10de8db9b87413e1533cfae397c7584641 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 16 Jul 2018 21:29:15 +0300 Subject: [PATCH 089/217] plugins refactoring --- toxygen/app.py | 2 +- toxygen/contacts/profile.py | 2 +- toxygen/plugin_support/plugin_support.py | 134 +++++++++++++---------- toxygen/plugins/plugin_super_class.py | 34 ++---- 4 files changed, 91 insertions(+), 81 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 3f66e2c..1dd74b4 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -338,7 +338,7 @@ class App: self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory) self._profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) - self._plugin_loader = PluginLoader(self._tox, self._toxes, self._profile, self._settings) + self._plugin_loader = PluginLoader(self._settings, self) history = None messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, self._ms, lambda m: history.delete_message(m)) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 3529be4..ab8cb2a 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -6,7 +6,7 @@ import common.tox_save as tox_save class Profile(basecontact.BaseContact, tox_save.ToxSave): """ - Profile of current toxygen user. Contains friends list, tox instance + Profile of current toxygen user. """ def __init__(self, profile_manager, tox, screen, contacts_provider, reset_action): """ diff --git a/toxygen/plugin_support/plugin_support.py b/toxygen/plugin_support/plugin_support.py index e8c9a56..ed45910 100644 --- a/toxygen/plugin_support/plugin_support.py +++ b/toxygen/plugin_support/plugin_support.py @@ -4,31 +4,47 @@ import importlib import inspect import plugins.plugin_super_class as pl import sys -from common.tox_save import ToxSave -class PluginLoader(ToxSave): +class Plugin: - def __init__(self, tox, toxes, profile, settings): - super().__init__(tox) - self._profile = profile + def __init__(self, plugin, is_active): + self._instance = plugin + self._is_active = is_active + + def get_instance(self): + return self._instance + + instance = property(get_instance) + + def get_is_active(self): + return self._is_active + + def set_is_active(self, is_active): + self._is_active = is_active + + is_active = property(get_is_active, set_is_active) + + +class PluginLoader: + + def __init__(self, settings, app): self._settings = settings - self._plugins = {} # dict. key - plugin unique short name, value - tuple (plugin instance, is active) - self._toxes = toxes + self._app = app + self._plugins = {} # dict. key - plugin unique short name, value - Plugin instance def set_tox(self, tox): """ New tox instance """ - super().set_tox(tox) - for value in self._plugins.values(): - value[0].set_tox(tox) + for plugin in self._plugins.values(): + plugin.instance.set_tox(tox) def load(self): """ Load all plugins in plugins folder """ - path = util.curr_directory() + '/plugins/' + path = util.get_plugins_directory() if not os.path.exists(path): util.log('Plugin dir not found') return @@ -50,18 +66,19 @@ class PluginLoader(ToxSave): for elem in dir(module): obj = getattr(module, elem) # looking for plugin class in module - if inspect.isclass(obj) and hasattr(obj, 'is_plugin') and obj.is_plugin: - print('Plugin', elem) - try: # create instance of plugin class - inst = obj(self._tox, self._profile, self._settings, self._toxes) - autostart = inst.get_short_name() in self._settings['plugins'] - if autostart: - inst.start() - except Exception as ex: - util.log('Exception in module ' + name + ' Exception: ' + str(ex)) - continue - self._plugins[inst.get_short_name()] = [inst, autostart] # (inst, is active) - break + if not inspect.isclass(obj) or not hasattr(obj, 'is_plugin') or not obj.is_plugin: + continue + print('Plugin', elem) + try: # create instance of plugin class + instance = obj(self._app) + is_active = instance.get_short_name() in self._settings['plugins'] + if is_active: + instance.start() + except Exception as ex: + util.log('Exception in module ' + name + ' Exception: ' + str(ex)) + continue + self._plugins[instance.get_short_name()] = Plugin(instance, is_active) + break def callback_lossless(self, friend_number, data): """ @@ -69,8 +86,8 @@ class PluginLoader(ToxSave): """ l = data[0] - pl.LOSSLESS_FIRST_BYTE name = ''.join(chr(x) for x in data[1:l + 1]) - if name in self._plugins and self._plugins[name][1]: - self._plugins[name][0].lossless_packet(''.join(chr(x) for x in data[l + 1:]), friend_number) + if name in self._plugins and self._plugins[name].is_active: + self._plugins[name].instance.lossless_packet(''.join(chr(x) for x in data[l + 1:]), friend_number) def callback_lossy(self, friend_number, data): """ @@ -78,37 +95,38 @@ class PluginLoader(ToxSave): """ l = data[0] - pl.LOSSY_FIRST_BYTE name = ''.join(chr(x) for x in data[1:l + 1]) - if name in self._plugins and self._plugins[name][1]: - self._plugins[name][0].lossy_packet(''.join(chr(x) for x in data[l + 1:]), friend_number) + if name in self._plugins and self._plugins[name].is_active: + self._plugins[name].instance.lossy_packet(''.join(chr(x) for x in data[l + 1:]), friend_number) def friend_online(self, friend_number): """ Friend with specified number is online """ - for elem in self._plugins.values(): - if elem[1]: - elem[0].friend_connected(friend_number) + for plugin in self._plugins.values(): + if plugin.is_active: + plugin.instance.friend_connected(friend_number) def get_plugins_list(self): """ Returns list of all plugins """ result = [] - for data in self._plugins.values(): + for plugin in self._plugins.values(): try: - result.append([data[0].get_name(), # plugin full name - data[1], # is enabled - data[0].get_description(), # plugin description - data[0].get_short_name()]) # key - short unique name + result.append([plugin.instance.get_name(), # plugin full name + plugin.is_active, # is enabled + plugin.instance.get_description(), # plugin description + plugin.instance.get_short_name()]) # key - short unique name except: continue + return result def plugin_window(self, key): """ Return window or None for specified plugin """ - return self._plugins[key][0].get_window() + return self._plugins[key].instance.get_window() def toggle_plugin(self, key): """ @@ -116,12 +134,12 @@ class PluginLoader(ToxSave): :param key: plugin short name """ plugin = self._plugins[key] - if plugin[1]: - plugin[0].stop() + if plugin.is_active: + plugin.instance.stop() else: - plugin[0].start() - plugin[1] = not plugin[1] - if plugin[1]: + plugin.instance.start() + plugin.is_active = not plugin.is_active + if plugin.is_active: self._settings['plugins'].append(key) else: self._settings['plugins'].remove(key) @@ -133,30 +151,32 @@ class PluginLoader(ToxSave): """ text = text.strip() name = text.split()[0] - if name in self._plugins and self._plugins[name][1]: - self._plugins[name][0].command(text[len(name) + 1:]) + if name in self._plugins and self._plugins[name].is_active: + self._plugins[name].instance.command(text[len(name) + 1:]) def get_menu(self, num): """ Return list of items for menu """ result = [] - for elem in self._plugins.values(): - if elem[1]: - try: - result.extend(elem[0].get_menu(num)) - except: - continue + for plugin in self._plugins.values(): + if not plugin.is_active: + continue + try: + result.extend(plugin.instance.get_menu(num)) + except: + continue return result def get_message_menu(self, menu, selected_text): result = [] - for elem in self._plugins.values(): - if elem[1]: - try: - result.extend(elem[0].get_message_menu(menu, selected_text)) - except: - pass + for plugin in self._plugins.values(): + if not plugin.is_active: + continue + try: + result.extend(plugin.instance.get_message_menu(menu, selected_text)) + except: + pass return result def stop(self): @@ -164,8 +184,8 @@ class PluginLoader(ToxSave): App is closing, stop all plugins """ for key in list(self._plugins.keys()): - if self._plugins[key][1]: - self._plugins[key][0].close() + if self._plugins[key].is_active: + self._plugins[key].instance.close() del self._plugins[key] def reload(self): diff --git a/toxygen/plugins/plugin_super_class.py b/toxygen/plugins/plugin_super_class.py index abb4da6..0056d36 100644 --- a/toxygen/plugins/plugin_super_class.py +++ b/toxygen/plugins/plugin_super_class.py @@ -1,5 +1,7 @@ import os from PyQt5 import QtCore, QtWidgets +import utils.ui as util_ui +import common.tox_save as tox_save MAX_SHORT_NAME_LENGTH = 5 @@ -26,25 +28,22 @@ def log(name, data): fl.write(str(data) + '\n') -class PluginSuperClass: +class PluginSuperClass(tox_save.ToxSave): """ Superclass for all plugins. Plugin is Python3 module with at least one class derived from PluginSuperClass. """ is_plugin = True - def __init__(self, name, short_name, tox=None, profile=None, settings=None, encrypt_save=None): + def __init__(self, name, short_name, app): """ - Constructor. In plugin __init__ should take only 4 last arguments + Constructor. In plugin __init__ should take only 1 last argument :param name: plugin full name :param short_name: plugin unique short name (length of short name should not exceed MAX_SHORT_NAME_LENGTH) - :param tox: tox instance - :param profile: profile instance - :param settings: profile settings - :param encrypt_save: ToxES instance. + :param app: App instance """ - self._settings = settings - self._profile = profile - self._tox = tox + tox = getattr(app, '_tox') + super().__init__(tox) + self._settings = getattr(app, '_settings') name = name.strip() short_name = short_name.strip() if not name or not short_name: @@ -52,7 +51,6 @@ class PluginSuperClass: self._name = name self._short_name = short_name[:MAX_SHORT_NAME_LENGTH] self._translator = None # translator for plugin's GUI - self._encrypt_save = encrypt_save # ----------------------------------------------------------------------------------------------------------------- # Get methods @@ -99,12 +97,6 @@ class PluginSuperClass: """ return None - def set_tox(self, tox): - """ - New tox instance - """ - self._tox = tox - # ----------------------------------------------------------------------------------------------------------------- # Plugin was stopped, started or new command received # ----------------------------------------------------------------------------------------------------------------- @@ -133,11 +125,9 @@ class PluginSuperClass: :param command: string with command """ if command == 'help': - msgbox = QtWidgets.QMessageBox() - title = QtWidgets.QApplication.translate("PluginWindow", "List of commands for plugin {}") - msgbox.setWindowTitle(title.format(self._name)) - msgbox.setText(QtWidgets.QApplication.translate("PluginWindow", "No commands available")) - msgbox.exec_() + text = util_ui.tr('No commands available') + title = util_ui.tr('List of commands for plugin {}').format(self._name) + util_ui.message_box(text, title) # ----------------------------------------------------------------------------------------------------------------- # Translations support From 329ab23f89c01c4b85c7589cfcbd7b5199b11280 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 17 Jul 2018 20:52:42 +0300 Subject: [PATCH 090/217] api changes - new methods and renaming --- toxygen/wrapper/tox.py | 228 +++++++++++++++++++++-------------------- 1 file changed, 119 insertions(+), 109 deletions(-) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 96e7fd5..fc438d8 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1565,7 +1565,7 @@ class Tox: :param chat_id: The Chat ID of the group you wish to join. This must be TOX_GROUP_CHAT_ID_SIZE bytes. :param password: The password required to join the group. Set to NULL if no password is required. - :return groupnumber on success, UINT32_MAX on failure. + :return group_number on success, UINT32_MAX on failure. """ error = c_int() @@ -1577,22 +1577,32 @@ class Tox: byref(error)) return result - def group_reconnect(self, groupnumber): + def group_reconnect(self, group_number): """ Reconnects to a group. This function disconnects from all peers in the group, then attempts to reconnect with the group. The caller's state is not changed (i.e. name, status, role, chat public key etc.) - :param groupnumber: The group number of the group we wish to reconnect to. + :param group_number: The group number of the group we wish to reconnect to. :return True on success. """ error = c_int() - result = Tox.libtoxcore.tox_group_reconnect(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_reconnect(self._tox_pointer, group_number, byref(error)) return result - def group_leave(self, groupnumber, message=''): + def group_is_connected(self, group_number): + error = c_int() + result = Tox.libtoxcore.tox_group_is_connected(self._tox_pointer, group_number, byref(error)) + return result + + def group_disconnect(self, group_number): + error = c_int() + result = Tox.libtoxcore.tox_group_disconnect(self._tox_pointer, group_number, byref(error)) + return result + + def group_leave(self, group_number, message=''): """ Leaves a group. @@ -1600,7 +1610,7 @@ class Tox: peers in a group, and deletes the group from the chat array. All group state information is permanently lost, including keys and role credentials. - :param groupnumber: The group number of the group we wish to leave. + :param group_number: The group number of the group we wish to leave. :param message: The parting message to be sent to all the peers. Set to NULL if we do not wish to send a parting message. @@ -1610,7 +1620,7 @@ class Tox: error = c_int() f = Tox.libtoxcore.tox_group_leave f.restype = c_bool - result = f(self._tox_pointer, groupnumber, message, + result = f(self._tox_pointer, group_number, message, len(message) if message is not None else 0, byref(error)) return result @@ -1618,7 +1628,7 @@ class Tox: # Group user-visible client information (nickname/status/role/public key) # ----------------------------------------------------------------------------------------------------------------- - def group_self_set_name(self, groupnumber, name): + def group_self_set_name(self, group_number, name): """ Set the client's nickname for the group instance designated by the given group number. @@ -1631,23 +1641,23 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_self_set_name(self._tox_pointer, groupnumber, name, len(name), byref(error)) + result = Tox.libtoxcore.tox_group_self_set_name(self._tox_pointer, group_number, name, len(name), byref(error)) return result - def group_self_get_name_size(self, groupnumber): + def group_self_get_name_size(self, group_number): """ Return the length of the client's current nickname for the group instance designated - by groupnumber as passed to tox_group_self_set_name. + by group_number as passed to tox_group_self_set_name. If no nickname was set before calling this function, the name is empty, and this function returns 0. """ error = c_int() - result = Tox.libtoxcore.tox_group_self_get_name_size(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_self_get_name_size(self._tox_pointer, group_number, byref(error)) return result - def group_self_get_name(self, groupnumber): + def group_self_get_name(self, group_number): """ Write the nickname set by tox_group_self_set_name to a byte array. @@ -1659,12 +1669,12 @@ class Tox: """ error = c_int() - size = self.group_self_get_name_size(groupnumber) + size = self.group_self_get_name_size(group_number) name = create_string_buffer(size) - result = Tox.libtoxcore.tox_group_self_get_name(self._tox_pointer, groupnumber, name, byref(error)) + result = Tox.libtoxcore.tox_group_self_get_name(self._tox_pointer, group_number, name, byref(error)) return str(name[:size], 'utf-8') - def group_self_set_status(self, groupnumber, status): + def group_self_set_status(self, group_number, status): """ Set the client's status for the group instance. Status must be a TOX_USER_STATUS. @@ -1672,40 +1682,40 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_self_set_status(self._tox_pointer, groupnumber, status, byref(error)) + result = Tox.libtoxcore.tox_group_self_set_status(self._tox_pointer, group_number, status, byref(error)) return result - def group_self_get_status(self, groupnumber): + def group_self_get_status(self, group_number): """ returns the client's status for the group instance on success. return value is unspecified on failure. """ error = c_int() - result = Tox.libtoxcore.tox_group_self_get_status(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_self_get_status(self._tox_pointer, group_number, byref(error)) return result - def group_self_get_role(self, groupnumber): + def group_self_get_role(self, group_number): """ returns the client's role for the group instance on success. return value is unspecified on failure. """ error = c_int() - result = Tox.libtoxcore.tox_group_self_get_role(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_self_get_role(self._tox_pointer, group_number, byref(error)) return result - def group_self_get_peer_id(self, groupnumber): + def group_self_get_peer_id(self, group_number): """ returns the client's peer id for the group instance on success. return value is unspecified on failure. """ error = c_int() - result = Tox.libtoxcore.tox_group_self_get_peer_id(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_self_get_peer_id(self._tox_pointer, group_number, byref(error)) return result - def group_self_get_public_key(self, groupnumber): + def group_self_get_public_key(self, group_number): """ Write the client's group public key designated by the given group number to a byte array. @@ -1720,7 +1730,7 @@ class Tox: error = c_int() key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) - result = Tox.libtoxcore.tox_group_self_get_public_key(self._tox_pointer, groupnumber, + result = Tox.libtoxcore.tox_group_self_get_public_key(self._tox_pointer, group_number, key, byref(error)) return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) @@ -1728,7 +1738,7 @@ class Tox: # Peer-specific group state queries. # ----------------------------------------------------------------------------------------------------------------- - def group_peer_get_name_size(self, groupnumber, peer_id): + def group_peer_get_name_size(self, group_number, peer_id): """ Return the length of the peer's name. If the group number or ID is invalid, the return value is unspecified. @@ -1738,10 +1748,10 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_peer_get_name_size(self._tox_pointer, groupnumber, peer_id, byref(error)) + result = Tox.libtoxcore.tox_group_peer_get_name_size(self._tox_pointer, group_number, peer_id, byref(error)) return result - def group_peer_get_name(self, groupnumber, peer_id): + def group_peer_get_name(self, group_number, peer_id): """ Write the name of the peer designated by the given ID to a byte array. @@ -1751,18 +1761,18 @@ class Tox: The data written to `name` is equal to the data received by the last `group_peer_name` callback. - :param groupnumber: The group number of the group we wish to query. + :param group_number: The group number of the group we wish to query. :param peer_id: The ID of the peer whose name we want to retrieve. :return name. """ error = c_int() - size = self.group_peer_get_name_size(groupnumber, peer_id) + size = self.group_peer_get_name_size(group_number, peer_id) name = create_string_buffer(size) - result = Tox.libtoxcore.tox_group_peer_get_name(self._tox_pointer, groupnumber, peer_id, name, byref(error)) + result = Tox.libtoxcore.tox_group_peer_get_name(self._tox_pointer, group_number, peer_id, name, byref(error)) return str(name[:], 'utf-8') - def group_peer_get_status(self, groupnumber, peer_id): + def group_peer_get_status(self, group_number, peer_id): """ Return the peer's user status (away/busy/...). If the ID or group number is invalid, the return value is unspecified. @@ -1772,10 +1782,10 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_peer_get_status(self._tox_pointer, groupnumber, peer_id, byref(error)) + result = Tox.libtoxcore.tox_group_peer_get_status(self._tox_pointer, group_number, peer_id, byref(error)) return result - def group_peer_get_role(self, groupnumber, peer_id): + def group_peer_get_role(self, group_number, peer_id): """ Return the peer's role (user/moderator/founder...). If the ID or group number is invalid, the return value is unspecified. @@ -1785,10 +1795,10 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_peer_get_role(self._tox_pointer, groupnumber, peer_id, byref(error)) + result = Tox.libtoxcore.tox_group_peer_get_role(self._tox_pointer, group_number, peer_id, byref(error)) return result - def group_peer_get_public_key(self, groupnumber, peer_id): + def group_peer_get_public_key(self, group_number, peer_id): """ Write the group public key with the designated peer_id for the designated group number to public_key. @@ -1802,7 +1812,7 @@ class Tox: error = c_int() key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) - result = Tox.libtoxcore.tox_group_peer_get_public_key(self._tox_pointer, groupnumber, peer_id, + result = Tox.libtoxcore.tox_group_peer_get_public_key(self._tox_pointer, group_number, peer_id, key, byref(error)) return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) @@ -1830,7 +1840,7 @@ class Tox: # Group chat state queries and events. # ----------------------------------------------------------------------------------------------------------------- - def group_set_topic(self, groupnumber, topic): + def group_set_topic(self, group_number, topic): """ Set the group topic and broadcast it to the rest of the group. @@ -1841,10 +1851,10 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_set_topic(self._tox_pointer, groupnumber, topic, len(topic), byref(error)) + result = Tox.libtoxcore.tox_group_set_topic(self._tox_pointer, group_number, topic, len(topic), byref(error)) return result - def group_get_topic_size(self, groupnumber): + def group_get_topic_size(self, group_number): """ Return the length of the group topic. If the group number is invalid, the return value is unspecified. @@ -1854,10 +1864,10 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_get_topic_size(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_get_topic_size(self._tox_pointer, group_number, byref(error)) return result - def group_get_topic(self, groupnumber): + def group_get_topic(self, group_number): """ Write the topic designated by the given group number to a byte array. Call tox_group_get_topic_size to determine the allocation size for the `topic` parameter. @@ -1868,21 +1878,21 @@ class Tox: """ error = c_int() - size = self.group_get_topic_size(groupnumber) + size = self.group_get_topic_size(group_number) topic = create_string_buffer(size) - result = Tox.libtoxcore.tox_group_get_topic(self._tox_pointer, groupnumber, topic, byref(error)) + result = Tox.libtoxcore.tox_group_get_topic(self._tox_pointer, group_number, topic, byref(error)) return str(topic[:size], 'utf-8') - def group_get_name_size(self, groupnumber): + def group_get_name_size(self, group_number): """ Return the length of the group name. If the group number is invalid, the return value is unspecified. """ error = c_int() - result = Tox.libtoxcore.tox_group_get_name_size(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_get_name_size(self._tox_pointer, group_number, byref(error)) return int(result) - def group_get_name(self, groupnumber): + def group_get_name(self, group_number): """ Write the name of the group designated by the given group number to a byte array. Call tox_group_get_name_size to determine the allocation size for the `name` parameter. @@ -1890,13 +1900,13 @@ class Tox: """ error = c_int() - size = self.group_get_name_size(groupnumber) + size = self.group_get_name_size(group_number) name = create_string_buffer(size) - result = Tox.libtoxcore.tox_group_get_name(self._tox_pointer, groupnumber, + result = Tox.libtoxcore.tox_group_get_name(self._tox_pointer, group_number, name, byref(error)) return str(name[:size], 'utf-8') - def group_get_chat_id(self, groupnumber): + def group_get_chat_id(self, group_number): """ Write the Chat ID designated by the given group number to a byte array. `chat_id` should have room for at least TOX_GROUP_CHAT_ID_SIZE bytes. @@ -1905,7 +1915,7 @@ class Tox: error = c_int() buff = create_string_buffer(TOX_GROUP_CHAT_ID_SIZE) - result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer, groupnumber, + result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer, group_number, buff, byref(error)) return bin_to_string(buff, TOX_GROUP_CHAT_ID_SIZE) @@ -1917,7 +1927,7 @@ class Tox: result = Tox.libtoxcore.tox_group_get_number_groups(self._tox_pointer) return result - def group_get_privacy_state(self, groupnumber): + def group_get_privacy_state(self, group_number): """ Return the privacy state of the group designated by the given group number. If group number is invalid, the return value is unspecified. @@ -1929,10 +1939,10 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_get_privacy_state(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_get_privacy_state(self._tox_pointer, group_number, byref(error)) return result - def group_get_peer_limit(self, groupnumber): + def group_get_peer_limit(self, group_number): """ Return the maximum number of peers allowed for the group designated by the given group number. If the group number is invalid, the return value is unspecified. @@ -1944,20 +1954,20 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_get_peer_limit(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_get_peer_limit(self._tox_pointer, group_number, byref(error)) return result - def group_get_password_size(self, groupnumber): + def group_get_password_size(self, group_number): """ Return the length of the group password. If the group number is invalid, the return value is unspecified. """ error = c_int() - result = Tox.libtoxcore.tox_group_get_password_size(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_get_password_size(self._tox_pointer, group_number, byref(error)) return result - def group_get_password(self, groupnumber): + def group_get_password(self, group_number): """ Write the password for the group designated by the given group number to a byte array. @@ -1972,9 +1982,9 @@ class Tox: """ error = c_int() - size = self.group_get_password_size(groupnumber) + size = self.group_get_password_size(group_number) password = create_string_buffer(size) - result = Tox.libtoxcore.tox_group_get_password(self._tox_pointer, groupnumber, + result = Tox.libtoxcore.tox_group_get_password(self._tox_pointer, group_number, password, byref(error)) return str(password[:size], 'utf-8') @@ -2022,7 +2032,7 @@ class Tox: # Group message sending # ----------------------------------------------------------------------------------------------------------------- - def group_send_custom_packet(self, groupnumber, lossless, data): + def group_send_custom_packet(self, group_number, lossless, data): """ Send a custom packet to the group. @@ -2036,18 +2046,18 @@ class Tox: Unless latency is an issue or message reliability is not important, it is recommended that you use lossless custom packets. - :param groupnumber: The group number of the group the message is intended for. + :param group_number: The group number of the group the message is intended for. :param lossless: True if the packet should be lossless. :param data A byte array containing the packet data. :return True on success. """ error = c_int() - result = Tox.libtoxcore.tox_group_send_custom_packet(self._tox_pointer, groupnumber, lossless, data, + result = Tox.libtoxcore.tox_group_send_custom_packet(self._tox_pointer, group_number, lossless, data, len(data), byref(error)) return result - def group_send_private_message(self, groupnumber, peer_id, message): + def group_send_private_message(self, group_number, peer_id, message): """ Send a text chat message to the specified peer in the specified group. @@ -2058,7 +2068,7 @@ class Tox: must be split by the client and sent as separate messages. Other clients can then reassemble the fragments. Messages may not be empty. - :param groupnumber: The group number of the group the message is intended for. + :param group_number: The group number of the group the message is intended for. :param peer_id: The ID of the peer the message is intended for. :param message: A non-NULL pointer to the first element of a byte array containing the message text. @@ -2066,11 +2076,11 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_send_private_message(self._tox_pointer, groupnumber, peer_id, message, + result = Tox.libtoxcore.tox_group_send_private_message(self._tox_pointer, group_number, peer_id, message, len(message), byref(error)) return result - def group_send_message(self, groupnumber, type, message): + def group_send_message(self, group_number, type, message): """ Send a text chat message to the group. @@ -2081,7 +2091,7 @@ class Tox: must be split by the client and sent as separate messages. Other clients can then reassemble the fragments. Messages may not be empty. - :param groupnumber: The group number of the group the message is intended for. + :param group_number: The group number of the group the message is intended for. :param type: Message type (normal, action, ...). :param message: A non-NULL pointer to the first element of a byte array containing the message text. @@ -2089,7 +2099,7 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_send_message(self._tox_pointer, groupnumber, type, message, len(message), + result = Tox.libtoxcore.tox_group_send_message(self._tox_pointer, group_number, type, message, len(message), byref(error)) return result @@ -2104,7 +2114,7 @@ class Tox: Callback: python function with params: tox Tox* instance - groupnumber The group number of the group the message is intended for. + group_number The group number of the group the message is intended for. peer_id The ID of the peer who sent the message. type The type of message (normal, action, ...). message The message data. @@ -2141,20 +2151,20 @@ class Tox: # Group chat inviting and join/part events # ----------------------------------------------------------------------------------------------------------------- - def group_invite_friend(self, groupnumber, friend_number): + def group_invite_friend(self, group_number, friend_number): """ Invite a friend to a group. This function creates an invite request packet and pushes it to the send queue. - :param groupnumber: The group number of the group the message is intended for. + :param group_number: The group number of the group the message is intended for. :param friend_number: The friend number of the friend the invite is intended for. :return True on success. """ error = c_int() - result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, groupnumber, friend_number, byref(error)) + result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, group_number, friend_number, byref(error)) return result def group_chat_self_peer_info_new(self): @@ -2172,7 +2182,7 @@ class Tox: :param invite_data: The invite data received from the `group_invite` event. :param password: The password required to join the group. Set to NULL if no password is required. - :return the groupnumber on success, UINT32_MAX on failure. + :return the group_number on success, UINT32_MAX on failure. """ error = c_int() @@ -2262,25 +2272,25 @@ class Tox: # Group chat founder controls (these only work for the group founder) # ----------------------------------------------------------------------------------------------------------------- - def group_founder_set_password(self, groupnumber, password): + def group_founder_set_password(self, group_number, password): """ Set or unset the group password. This function sets the groups password, creates a new group shared state including the change, and distributes it to the rest of the group. - :param groupnumber: The group number of the group for which we wish to set the password. + :param group_number: The group number of the group for which we wish to set the password. :param password: The password we want to set. Set password to NULL to unset the password. :return True on success. """ error = c_int() - result = Tox.libtoxcore.tox_group_founder_set_password(self._tox_pointer, groupnumber, password, + result = Tox.libtoxcore.tox_group_founder_set_password(self._tox_pointer, group_number, password, len(password), byref(error)) return result - def group_founder_set_privacy_state(self, groupnumber, privacy_state): + def group_founder_set_privacy_state(self, group_number, privacy_state): """ Set the group privacy state. @@ -2290,32 +2300,32 @@ class Tox: If an attempt is made to set the privacy state to the same state that the group is already in, the function call will be successful and no action will be taken. - :param groupnumber: The group number of the group for which we wish to change the privacy state. + :param group_number: The group number of the group for which we wish to change the privacy state. :param privacy_state: The privacy state we wish to set the group to. :return true on success. """ error = c_int() - result = Tox.libtoxcore.tox_group_founder_set_privacy_state(self._tox_pointer, groupnumber, privacy_state, + result = Tox.libtoxcore.tox_group_founder_set_privacy_state(self._tox_pointer, group_number, privacy_state, byref(error)) return result - def group_founder_set_peer_limit(self, groupnumber, max_peers): + def group_founder_set_peer_limit(self, group_number, max_peers): """ Set the group peer limit. This function sets a limit for the number of peers who may be in the group, creates a new group shared state including the change, and distributes it to the rest of the group. - :param groupnumber: The group number of the group for which we wish to set the peer limit. + :param group_number: The group number of the group for which we wish to set the peer limit. :param max_peers: The maximum number of peers to allow in the group. :return True on success. """ error = c_int() - result = Tox.libtoxcore.tox_group_founder_set_peer_limit(self._tox_pointer, groupnumber, + result = Tox.libtoxcore.tox_group_founder_set_peer_limit(self._tox_pointer, group_number, max_peers, byref(error)) return result @@ -2323,11 +2333,11 @@ class Tox: # Group chat moderation # ----------------------------------------------------------------------------------------------------------------- - def group_toggle_ignore(self, groupnumber, peer_id, ignore): + def group_toggle_ignore(self, group_number, peer_id, ignore): """ Ignore or unignore a peer. - :param groupnumber: The group number of the group the in which you wish to ignore a peer. + :param group_number: The group number of the group the in which you wish to ignore a peer. :param peer_id: The ID of the peer who shall be ignored or unignored. :param ignore: True to ignore the peer, false to unignore the peer. @@ -2335,10 +2345,10 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_toggle_ignore(self._tox_pointer, groupnumber, peer_id, ignore, byref(error)) + result = Tox.libtoxcore.tox_group_toggle_ignore(self._tox_pointer, group_number, peer_id, ignore, byref(error)) return result - def group_mod_set_role(self, groupnumber, peer_id, role): + def group_mod_set_role(self, group_number, peer_id, role): """ Set a peer's role. @@ -2346,7 +2356,7 @@ class Tox: It will also send a packet to the rest of the group, requesting that they perform the role reassignment. Note: peers cannot be set to the founder role. - :param groupnumber: The group number of the group the in which you wish set the peer's role. + :param group_number: The group number of the group the in which you wish set the peer's role. :param peer_id: The ID of the peer whose role you wish to set. :param role: The role you wish to set the peer to. @@ -2354,10 +2364,10 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_mod_set_role(self._tox_pointer, groupnumber, peer_id, role, byref(error)) + result = Tox.libtoxcore.tox_group_mod_set_role(self._tox_pointer, group_number, peer_id, role, byref(error)) return result - def group_mod_remove_peer(self, groupnumber, peer_id, set_ban): + def group_mod_remove_peer(self, group_number, peer_id, set_ban): """ Kick/ban a peer. @@ -2365,7 +2375,7 @@ class Tox: to the ban list. It will also send a packet to all group members requesting them to do the same. - :param groupnumber: The group number of the group the ban is intended for. + :param group_number: The group number of the group the ban is intended for. :param peer_id: The ID of the peer who will be kicked and/or added to the ban list. :param set_ban: Set to true if a ban shall be set on the peer's IP address. @@ -2373,25 +2383,25 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_mod_remove_peer(self._tox_pointer, groupnumber, peer_id, + result = Tox.libtoxcore.tox_group_mod_remove_peer(self._tox_pointer, group_number, peer_id, set_ban, byref(error)) return result - def group_mod_remove_ban(self, groupnumber, ban_id): + def group_mod_remove_ban(self, group_number, ban_id): """ Removes a ban. This function removes a ban entry from the ban list, and sends a packet to the rest of the group requesting that they do the same. - :param groupnumber: The group number of the group in which the ban is to be removed. + :param group_number: The group number of the group in which the ban is to be removed. :param ban_id: The ID of the ban entry that shall be removed. :return True on success """ error = c_int() - result = Tox.libtoxcore.tox_group_mod_remove_ban(self._tox_pointer, groupnumber, ban_id, byref(error)) + result = Tox.libtoxcore.tox_group_mod_remove_ban(self._tox_pointer, group_number, ban_id, byref(error)) return result def callback_group_moderation(self, callback, user_data): @@ -2409,17 +2419,17 @@ class Tox: # Group chat ban list queries # ----------------------------------------------------------------------------------------------------------------- - def group_ban_get_list_size(self, groupnumber): + def group_ban_get_list_size(self, group_number): """ Return the number of entries in the ban list for the group designated by the given group number. If the group number is invalid, the return value is unspecified. """ error = c_int() - result = Tox.libtoxcore.tox_group_ban_get_list_size(self._tox_pointer, groupnumber, byref(error)) + result = Tox.libtoxcore.tox_group_ban_get_list_size(self._tox_pointer, group_number, byref(error)) return result - def group_ban_get_list(self, groupnumber): + def group_ban_get_list(self, group_number): """ Copy a list of valid ban list ID's into an array. @@ -2428,22 +2438,22 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_ban_get_list(self._tox_pointer, groupnumber, POINTER(c_uint32)( - create_string_buffer(sizeof(c_uint32) * self.group_ban_get_list_size(groupnumber)), byref(error))) + result = Tox.libtoxcore.tox_group_ban_get_list(self._tox_pointer, group_number, POINTER(c_uint32)( + create_string_buffer(sizeof(c_uint32) * self.group_ban_get_list_size(group_number)), byref(error))) return result - def group_ban_get_name_size(self, groupnumber, ban_id): + def group_ban_get_name_size(self, group_number, ban_id): """ Return the length of the name for the ban list entry designated by ban_id, in the - group designated by the given group number. If either groupnumber or ban_id is invalid, + group designated by the given group number. If either group_number or ban_id is invalid, the return value is unspecified. """ error = c_int() - result = Tox.libtoxcore.tox_group_ban_get_name_size(self._tox_pointer, groupnumber, ban_id, byref(error)) + result = Tox.libtoxcore.tox_group_ban_get_name_size(self._tox_pointer, group_number, ban_id, byref(error)) return result - def group_ban_get_name(self, groupnumber, ban_id): + def group_ban_get_name(self, group_number, ban_id): """ Write the name of the ban entry designated by ban_id in the group designated by the given group number to a byte array. @@ -2454,20 +2464,20 @@ class Tox: """ error = c_int() - size = self.group_ban_get_name_size(groupnumber, ban_id) + size = self.group_ban_get_name_size(group_number, ban_id) name = create_string_buffer() - result = Tox.libtoxcore.tox_group_ban_get_name(self._tox_pointer, groupnumber, ban_id, + result = Tox.libtoxcore.tox_group_ban_get_name(self._tox_pointer, group_number, ban_id, name, byref(error)) return str(name[:size], 'utf-8') - def group_ban_get_time_set(self, groupnumber, ban_id): + def group_ban_get_time_set(self, group_number, ban_id): """ Return a time stamp indicating the time the ban was set, for the ban list entry designated by ban_id, in the group designated by the given group number. - If either groupnumber or ban_id is invalid, the return value is unspecified. + If either group_number or ban_id is invalid, the return value is unspecified. """ error = c_int() - result = Tox.libtoxcore.tox_group_ban_get_time_set(self._tox_pointer, groupnumber, ban_id, byref(error)) + result = Tox.libtoxcore.tox_group_ban_get_time_set(self._tox_pointer, group_number, ban_id, byref(error)) return result From 6538cedcf2f62c260369561ace108d4bd498a584 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 19 Jul 2018 00:00:01 +0300 Subject: [PATCH 091/217] reconnect/disconnect functionality --- toxygen/app.py | 8 +++++++- toxygen/contacts/contact_menu.py | 11 +++++++++++ toxygen/contacts/contacts_manager.py | 5 ++++- toxygen/contacts/group_chat.py | 3 ++- toxygen/groups/groups_service.py | 11 +++++++++++ 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 1dd74b4..62025d9 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -338,6 +338,7 @@ class App: self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory) self._profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) + self._init_profile() self._plugin_loader = PluginLoader(self._settings, self) history = None messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, @@ -349,7 +350,8 @@ class App: history.set_contacts_manager(self._contacts_manager) self._calls_manager = CallsManager(self._tox.AV, self._settings, self._ms, self._contacts_manager) self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, - self._contacts_provider, messages_items_factory, self._profile, self._calls_manager) + self._contacts_provider, messages_items_factory, self._profile, + self._calls_manager) file_transfers_message_service = FileTransfersMessagesService(self._contacts_manager, messages_items_factory, self._profile, self._ms) self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider, @@ -387,3 +389,7 @@ class App: callbacks.init_callbacks(self._tox, self._profile, self._settings, self._plugin_loader, self._contacts_manager, self._calls_manager, self._file_transfer_handler, self._ms, self._tray, self._messenger, self._groups_service, self._contacts_provider) + + def _init_profile(self): + if not self._profile.has_avatar(): + self._profile.reset_avatar(self._settings['identicons']) diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index 4a0dcaa..ff6a9ed 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -30,6 +30,12 @@ class ContactMenuBuilder: return self + def with_optional_action(self, text, handler, show_action): + if show_action: + self._add_action(text, handler) + + return self + def with_actions(self, actions): for action in actions: (text, handler) = action @@ -175,6 +181,11 @@ class GroupMenuGenerator(BaseContactMenuGenerator): menu = (builder .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) .with_submenu(copy_menu_builder) + .with_action(util_ui.tr('Reconnect to group'), + lambda: groups_service.reconnect_to_group(self._contact.number)) + .with_optional_action(util_ui.tr('Disconnect from group'), + lambda: groups_service.disconnect_from_group(self._contact.number), + self._contact.status is not None) .with_action(util_ui.tr('Leave group'), lambda: groups_service.leave_group(self._contact.number)) .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) ).build() diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 5d07bd2..2729959 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -490,10 +490,13 @@ class ContactsManager(ToxSave): del self._settings['notes'][contact.tox_id] self._settings.save() self._history.delete_history(contact) + if contact.has_avatar(): + avatar_path = contact.get_contact_avatar_path() + remove(avatar_path) def _delete_contact(self, num): if num == self._active_contact: # active friend was deleted - self.set_active(0 if len(self._contacts) - 1 else -1) + self.set_active(0 if len(self._contacts) > 1 else -1) self._contact_provider.remove_contact_from_cache(self._contacts[num].tox_id) del self._contacts[num] self._screen.friends_list.takeItem(num) diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index b7429d4..57d471a 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -11,7 +11,8 @@ class GroupChat(contact.Contact, ToxSave): def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id): super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) ToxSave.__init__(self, tox) - self.set_status(constants.TOX_USER_STATUS['NONE']) + status = self._tox.group_is_connected(number) + self.set_status(constants.TOX_USER_STATUS['NONE'] if status else None) self._peers = [] self._add_self_to_gc() diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 83f6a4e..69f8ec1 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -1,6 +1,7 @@ import common.tox_save as tox_save import utils.ui as util_ui from groups.peers_list import PeersListGenerator +import wrapper.toxcore_enums_and_consts as constants class GroupsService(tox_save.ToxSave): @@ -42,6 +43,16 @@ class GroupsService(tox_save.ToxSave): self._contacts_manager.delete_group(group_number) self._contacts_manager.update_groups_numbers() + def disconnect_from_group(self, group_number): + self._tox.group_disconnect(group_number) + group = self._get_group(group_number) + group.status = None + + def reconnect_to_group(self, group_number): + self._tox.group_reconnect(group_number) + group = self._get_group(group_number) + group.status = constants.TOX_USER_STATUS['NONE'] + # ----------------------------------------------------------------------------------------------------------------- # Group invites # ----------------------------------------------------------------------------------------------------------------- From 820b5a02534e2ce75382c27a90a6eb75ab7ad35a Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 21 Jul 2018 17:16:01 +0300 Subject: [PATCH 092/217] reconnection - clear peers list --- toxygen/contacts/group_chat.py | 3 +++ toxygen/groups/groups_service.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 57d471a..b18fb14 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -51,6 +51,9 @@ class GroupChat(contact.Contact, ToxSave): return peers[0] + def remove_all_peers_except_self(self): + self._peers = self._peers[:1] + def get_peers(self): return self._peers[:] diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 69f8ec1..2c0d4c3 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -47,11 +47,13 @@ class GroupsService(tox_save.ToxSave): self._tox.group_disconnect(group_number) group = self._get_group(group_number) group.status = None + self._clear_peers_list(group) def reconnect_to_group(self, group_number): self._tox.group_reconnect(group_number) group = self._get_group(group_number) group.status = constants.TOX_USER_STATUS['NONE'] + self._clear_peers_list(group) # ----------------------------------------------------------------------------------------------------------------- # Group invites @@ -102,3 +104,7 @@ class GroupsService(tox_save.ToxSave): def _get_all_groups(self): return self._contacts_provider.get_all_groups() + + def _clear_peers_list(self, group): + group.remove_all_peers_except_self() + self.generate_peers_list() From 7e08be71e05444bd4e96ae04b5acea65e44b4a7a Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 21 Jul 2018 20:25:10 +0300 Subject: [PATCH 093/217] group topic support --- toxygen/contacts/contact_menu.py | 3 +++ toxygen/contacts/group_chat.py | 6 ++++++ toxygen/groups/groups_service.py | 11 +++++++++++ toxygen/middleware/callbacks.py | 1 + toxygen/wrapper/tox.py | 1 + 5 files changed, 22 insertions(+) diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index ff6a9ed..6a2eb86 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -181,6 +181,9 @@ class GroupMenuGenerator(BaseContactMenuGenerator): menu = (builder .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) .with_submenu(copy_menu_builder) + .with_optional_action(util_ui.tr('Set topic'), + lambda: groups_service.set_group_topic(self._contact), + self._contact.is_moderator_or_founder()) .with_action(util_ui.tr('Reconnect to group'), lambda: groups_service.reconnect_to_group(self._contact.number)) .with_optional_action(util_ui.tr('Disconnect from group'), diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index b18fb14..fac2165 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -33,6 +33,12 @@ class GroupChat(contact.Contact, ToxSave): def get_self_name(self): return self._peers[0].name + def get_self_role(self): + return self._peers[0].role + + def is_moderator_or_founder(self): + return self.get_self_role() <= constants.TOX_GROUP_ROLE['MODERATOR'] + def add_peer(self, peer_id, is_current_user=False): peer = GroupChatPeer(peer_id, self._tox.group_peer_get_name(self._number, peer_id), diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 2c0d4c3..1a8ccc0 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -76,6 +76,17 @@ class GroupsService(tox_save.ToxSave): group.name = self._tox.group_get_name(group.number) group.status_message = self._tox.group_get_topic(group.number) + def set_group_topic(self, group): + if not group.is_moderator_or_founder(): + return + text = util_ui.tr('New topic for group {}:'.format(group.name)) + title = util_ui.tr('Set group topic') + topic, ok = util_ui.text_dialog(text, title, group.status_message) + if not ok or not topic: + return + self._tox.group_set_topic(group.number, topic) + group.status_message = topic + # ----------------------------------------------------------------------------------------------------------------- # Peers list # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 513cdd2..8730b54 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -399,6 +399,7 @@ def group_peer_join(contacts_provider, groups_service): group = contacts_provider.get_group_by_number(group_number) group.add_peer(peer_id) invoke_in_main_thread(groups_service.generate_peers_list) + invoke_in_main_thread(groups_service.update_group_info, group) return wrapped diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index fc438d8..836d38c 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1851,6 +1851,7 @@ class Tox: """ error = c_int() + topic = bytes(topic, 'utf-8') result = Tox.libtoxcore.tox_group_set_topic(self._tox_pointer, group_number, topic, len(topic), byref(error)) return result From e15620c3ad763b33659d5fa659cac6d007a3dc83 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 21 Jul 2018 20:43:16 +0300 Subject: [PATCH 094/217] str to bytes convert moved to wrapper --- tests/tests.py | 8 ++++---- toxygen/app.py | 4 ++-- toxygen/contacts/profile.py | 4 ++-- toxygen/wrapper/tox.py | 2 ++ 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index 54e374e..e3c9b6b 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -6,13 +6,13 @@ from toxygen.middleware.tox_factory import * class TestTox: def test_creation(self): - name = b'Toxygen User' - status_message = b'Toxing on Toxygen' + name = 'Toxygen User' + status_message = 'Toxing on Toxygen' tox = tox_factory() tox.self_set_name(name) tox.self_set_status_message(status_message) data = tox.get_savedata() del tox tox = tox_factory(data) - assert tox.self_get_name() == str(name, 'utf-8') - assert tox.self_get_status_message() == str(status_message, 'utf-8') + assert tox.self_get_name() == name + assert tox.self_get_status_message() == status_message diff --git a/toxygen/app.py b/toxygen/app.py index 62025d9..ec0a1cb 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -251,8 +251,8 @@ class App: return False name = profile_name or 'toxygen_user' self._tox = tox_factory() - self._tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') - self._tox.self_set_status_message(b'Toxing on Toxygen') + self._tox.self_set_name(name if name else 'Toxygen User') + self._tox.self_set_status_message('Toxing on Toxygen') self._path = profile_path if result.password: self._toxes.set_password(result.password) diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index ab8cb2a..c47eca2 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -51,11 +51,11 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave): if self.name == value: return super().set_name(value) - self._tox.self_set_name(self._name.encode('utf-8')) + self._tox.self_set_name(self._name) def set_status_message(self, value): super().set_status_message(value) - self._tox.self_set_status_message(self._status_message.encode('utf-8')) + self._tox.self_set_status_message(self._status_message) def set_new_nospam(self): """Sets new nospam part of tox id""" diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 836d38c..9cdac2e 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -375,6 +375,7 @@ class Tox: :return: True on success. """ tox_err_set_info = c_int() + name = bytes(name, 'utf-8') result = Tox.libtoxcore.tox_self_set_name(self._tox_pointer, c_char_p(name), c_size_t(len(name)), byref(tox_err_set_info)) tox_err_set_info = tox_err_set_info.value @@ -423,6 +424,7 @@ class Tox: :return: True on success. """ tox_err_set_info = c_int() + status_message = bytes(status_message, 'utf-8') result = Tox.libtoxcore.tox_self_set_status_message(self._tox_pointer, c_char_p(status_message), c_size_t(len(status_message)), byref(tox_err_set_info)) tox_err_set_info = tox_err_set_info.value From 5521b768bc5fe0cc46579f042f6e1ff7127a1108 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 22 Jul 2018 12:59:52 +0300 Subject: [PATCH 095/217] private messages support --- toxygen/app.py | 14 ++- toxygen/common/provider.py | 13 +++ toxygen/contacts/contact_provider.py | 27 ++++- toxygen/contacts/contacts_manager.py | 22 ++++ toxygen/contacts/group_chat.py | 7 +- toxygen/contacts/group_peer_contact.py | 10 +- toxygen/contacts/group_peer_factory.py | 23 ++++ toxygen/groups/group_peer.py | 8 +- toxygen/groups/groups_service.py | 32 ++++-- toxygen/messenger/messenger.py | 142 ++++++++++++++++++------- toxygen/middleware/callbacks.py | 21 ++++ toxygen/ui/contact_items.py | 5 +- toxygen/ui/peer_screen.py | 36 +++++++ toxygen/ui/views/peer_screen.ui | 83 +++++++++++++++ toxygen/ui/widgets_factory.py | 4 + toxygen/wrapper/tox.py | 2 +- 16 files changed, 384 insertions(+), 65 deletions(-) create mode 100644 toxygen/common/provider.py create mode 100644 toxygen/contacts/group_peer_factory.py create mode 100644 toxygen/ui/peer_screen.py create mode 100644 toxygen/ui/views/peer_screen.ui diff --git a/toxygen/app.py b/toxygen/app.py index ec0a1cb..b270f10 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -32,6 +32,8 @@ from history.history import History from file_transfers.file_transfers_messages_service import FileTransfersMessagesService from groups.groups_service import GroupsService from ui.create_profile_screen import CreateProfileScreen +from common.provider import Provider +from contacts.group_peer_factory import GroupPeerFactory import styles.style # TODO: dynamic loading @@ -42,7 +44,8 @@ class App: self._app = self._settings = self._profile_manager = self._plugin_loader = self._messenger = None self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None - self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = self._tox_dns = None + self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = None + self._group_peer_factory = self._tox_dns = None self._group_factory = self._groups_service = self._profile = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] @@ -336,7 +339,9 @@ class App: self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) - self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory) + self._group_peer_factory = GroupPeerFactory(self._tox, self._profile_manager, db, contact_items_factory) + self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory, + self._group_peer_factory) self._profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) self._init_profile() self._plugin_loader = PluginLoader(self._settings, self) @@ -357,7 +362,10 @@ class App: self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider, file_transfers_message_service, self._profile) messages_items_factory.set_file_transfers_handler(self._file_transfer_handler) - self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms) + widgets_factory = None + widgets_factory_provider = Provider(lambda: widgets_factory) + self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms, + widgets_factory_provider) widgets_factory = WidgetsFactory(self._settings, self._profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, self._toxes, self._version, self._groups_service, history) diff --git a/toxygen/common/provider.py b/toxygen/common/provider.py new file mode 100644 index 0000000..d16edb4 --- /dev/null +++ b/toxygen/common/provider.py @@ -0,0 +1,13 @@ + + +class Provider: + + def __init__(self, get_item_action): + self._get_item_action = get_item_action + self._item = None + + def get_item(self): + if self._item is None: + self._item = self._get_item_action() + + return self._item diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py index 6f13596..76e8e79 100644 --- a/toxygen/contacts/contact_provider.py +++ b/toxygen/contacts/contact_provider.py @@ -3,10 +3,11 @@ import common.tox_save as tox_save class ContactProvider(tox_save.ToxSave): - def __init__(self, tox, friend_factory, group_factory): + def __init__(self, tox, friend_factory, group_factory, group_peer_factory): super().__init__(tox) self._friend_factory = friend_factory self._group_factory = group_factory + self._group_peer_factory = group_peer_factory self._cache = {} # key - contact's public key, value - contact instance # ----------------------------------------------------------------------------------------------------------------- @@ -34,7 +35,7 @@ class ContactProvider(tox_save.ToxSave): return list(friends) # ----------------------------------------------------------------------------------------------------------------- - # GC + # Groups # ----------------------------------------------------------------------------------------------------------------- def get_all_groups(self): @@ -57,12 +58,29 @@ class ContactProvider(tox_save.ToxSave): return group + # ----------------------------------------------------------------------------------------------------------------- + # Group peers + # ----------------------------------------------------------------------------------------------------------------- + + def get_all_group_peers(self): + return list() + + def get_group_peer_by_id(self, group, peer_id): + peer = group.get_peer_by_id(peer_id) + + return self._get_group_peer(group, peer) + + def get_group_peer_by_public_key(self, group, public_key): + peer = group.get_peer_by_public_key(public_key) + + return self._get_group_peer(group, peer) + # ----------------------------------------------------------------------------------------------------------------- # All contacts # ----------------------------------------------------------------------------------------------------------------- def get_all(self): - return self.get_all_friends() + self.get_all_groups() + return self.get_all_friends() + self.get_all_groups() + self.get_all_group_peers() # ----------------------------------------------------------------------------------------------------------------- # Caching @@ -84,3 +102,6 @@ class ContactProvider(tox_save.ToxSave): def _add_to_cache(self, public_key, contact): self._cache[public_key] = contact + + def _get_group_peer(self, group, peer): + return self._group_peer_factory.create_group_peer(group, peer) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 2729959..6135967 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -2,6 +2,7 @@ from contacts.friend import Friend from contacts.group_chat import GroupChat from messenger.messages import * from common.tox_save import ToxSave +from contacts.group_peer_contact import GroupPeerContact class ContactsManager(ToxSave): @@ -223,6 +224,17 @@ class ContactsManager(ToxSave): def get_group_by_number(self, number): return list(filter(lambda c: c.number == number and type(c) is GroupChat, self._contacts))[0] + def get_or_create_group_peer_contact(self, group_number, peer_id): + group = self.get_group_by_number(group_number) + peer = group.get_peer_by_id(peer_id) + if not self.check_if_contact_exists(peer.public_key): + self.add_group_peer(group, peer) + + return self.get_contact_by_tox_id(peer.public_key) + + def check_if_contact_exists(self, tox_id): + return any(filter(lambda c: c.tox_id == tox_id, self._contacts)) + def get_contact_by_tox_id(self, tox_id): return list(filter(lambda c: c.tox_id == tox_id, self._contacts))[0] @@ -340,6 +352,16 @@ class ContactsManager(ToxSave): num = self._contacts.index(group) self._delete_contact(num) + # ----------------------------------------------------------------------------------------------------------------- + # Groups private messaging + # ----------------------------------------------------------------------------------------------------------------- + + def add_group_peer(self, group, peer): + contact = self._contact_provider.get_group_peer_by_id(group, peer.id) + self._contacts.append(contact) + contact.reset_avatar(self._settings['identicons']) + self._save_profile() + # ----------------------------------------------------------------------------------------------------------------- # Friend requests # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index fac2165..61b3858 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -11,8 +11,6 @@ class GroupChat(contact.Contact, ToxSave): def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id): super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) ToxSave.__init__(self, tox) - status = self._tox.group_is_connected(number) - self.set_status(constants.TOX_USER_STATUS['NONE'] if status else None) self._peers = [] self._add_self_to_gc() @@ -57,6 +55,11 @@ class GroupChat(contact.Contact, ToxSave): return peers[0] + def get_peer_by_public_key(self, public_key): + peers = list(filter(lambda p: p.public_key == public_key, self._peers)) + + return peers[0] + def remove_all_peers_except_self(self): self._peers = self._peers[:1] diff --git a/toxygen/contacts/group_peer_contact.py b/toxygen/contacts/group_peer_contact.py index 1c51f67..47cd16b 100644 --- a/toxygen/contacts/group_peer_contact.py +++ b/toxygen/contacts/group_peer_contact.py @@ -3,11 +3,17 @@ import contacts.contact class GroupPeerContact(contacts.contact.Contact): - def __init__(self, profile_manager, message_getter, peer_number, name, status_messsage, widget, tox_id, group_pk): - super().__init__(profile_manager, message_getter, peer_number, name, status_messsage, widget, tox_id) + def __init__(self, profile_manager, message_getter, peer_number, name, widget, tox_id, group_pk): + super().__init__(profile_manager, message_getter, peer_number, name, str(), widget, tox_id) self._group_pk = group_pk def get_group_pk(self): return self._group_pk group_pk = property(get_group_pk) + + def remove_invalid_unsent_files(self): + pass + + def get_context_menu_generator(self): + return None diff --git a/toxygen/contacts/group_peer_factory.py b/toxygen/contacts/group_peer_factory.py new file mode 100644 index 0000000..38b3a20 --- /dev/null +++ b/toxygen/contacts/group_peer_factory.py @@ -0,0 +1,23 @@ +from common.tox_save import ToxSave +from contacts.group_peer_contact import GroupPeerContact + + +class GroupPeerFactory(ToxSave): + + def __init__(self, tox, profile_manager, db, items_factory): + super().__init__(tox) + self._profile_manager = profile_manager + self._db = db + self._items_factory = items_factory + + def create_group_peer(self, group, peer): + item = self._create_group_peer_item() + message_getter = self._db.messages_getter(peer.public_key) + group_peer_contact = GroupPeerContact(self._profile_manager, message_getter, peer.id, peer.name, + item, peer.public_key, group.tox_id) + group_peer_contact.status = peer.status + + return group_peer_contact + + def _create_group_peer_item(self): + return self._items_factory.create_contact_item() diff --git a/toxygen/groups/group_peer.py b/toxygen/groups/group_peer.py index d2d5aeb..57724a5 100644 --- a/toxygen/groups/group_peer.py +++ b/toxygen/groups/group_peer.py @@ -2,13 +2,14 @@ class GroupChatPeer: - def __init__(self, peer_id, name, status, role, public_key, is_current_user): + def __init__(self, peer_id, name, status, role, public_key, is_current_user=False, is_muted=False): self._peer_id = peer_id self._name = name self._status = status self._role = role self._public_key = public_key self._is_current_user = is_current_user + self._is_muted = is_muted def get_id(self): return self._peer_id @@ -48,3 +49,8 @@ class GroupChatPeer: return self._is_current_user is_current_user = property(get_is_current_user) + + def get_is_muted(self): + return self._is_muted + + is_muted = property(get_is_muted) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 1a8ccc0..49d5f25 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -6,11 +6,13 @@ import wrapper.toxcore_enums_and_consts as constants class GroupsService(tox_save.ToxSave): - def __init__(self, tox, contacts_manager, contacts_provider, main_screen): + def __init__(self, tox, contacts_manager, contacts_provider, main_screen, widgets_factory_provider): super().__init__(tox) self._contacts_manager = contacts_manager self._contacts_provider = contacts_provider self._peers_list_widget = main_screen.peers_list + self._widgets_factory_provider = widgets_factory_provider + self._peer_screen = None def set_tox(self, tox): super().set_tox(tox) @@ -23,8 +25,12 @@ class GroupsService(tox_save.ToxSave): def create_new_gc(self, name, privacy_state): group_number = self._tox.group_new(privacy_state, name.encode('utf-8')) - if group_number != -1: - self._add_new_group_by_number(group_number) + if group_number == -1: + return + + self._add_new_group_by_number(group_number) + group = self._get_group_by_number(group_number) + group.status = constants.TOX_USER_STATUS['NONE'] def join_gc_by_id(self, chat_id, password): group_number = self._tox.group_join(chat_id, password) @@ -45,13 +51,13 @@ class GroupsService(tox_save.ToxSave): def disconnect_from_group(self, group_number): self._tox.group_disconnect(group_number) - group = self._get_group(group_number) + group = self._get_group_by_number(group_number) group.status = None self._clear_peers_list(group) def reconnect_to_group(self, group_number): self._tox.group_reconnect(group_number) - group = self._get_group(group_number) + group = self._get_group_by_number(group_number) group.status = constants.TOX_USER_STATUS['NONE'] self._clear_peers_list(group) @@ -63,7 +69,7 @@ class GroupsService(tox_save.ToxSave): self._tox.group_invite_friend(group_number, friend_number) def process_group_invite(self, friend_number, group_name, invite_data): - friend = self._get_friend(friend_number) + friend = self._get_friend_by_number(friend_number) text = util_ui.tr('Friend {} invites you to group "{}". Accept?') if util_ui.question(text.format(friend.name, group_name), util_ui.tr('Group invite')): self.join_gc_via_invite(invite_data, friend_number, None) @@ -98,7 +104,10 @@ class GroupsService(tox_save.ToxSave): PeersListGenerator().generate(group.peers, self, self._peers_list_widget, group.tox_id) def peer_selected(self, chat_id, peer_id): - pass + widgets_factory = self._widgets_factory_provider.get_item() + group = self._get_group_by_public_key(chat_id) + self._peer_screen = widgets_factory.create_peer_screen_window(group, peer_id) + self._peer_screen.show() # ----------------------------------------------------------------------------------------------------------------- # Private methods @@ -107,15 +116,18 @@ class GroupsService(tox_save.ToxSave): def _add_new_group_by_number(self, group_number): self._contacts_manager.add_group(group_number) - def _get_group(self, group_number): + def _get_group_by_number(self, group_number): return self._contacts_provider.get_group_by_number(group_number) - def _get_friend(self, friend_number): - return self._contacts_provider.get_friend_by_number(friend_number) + def _get_group_by_public_key(self, public_key): + return self._contacts_provider.get_group_by_public_key(public_key) def _get_all_groups(self): return self._contacts_provider.get_all_groups() + def _get_friend_by_number(self, friend_number): + return self._contacts_provider.get_friend_by_number(friend_number) + def _clear_peers_list(self, group): group.remove_all_peers_except_self() self.generate_peers_list() diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index dbd2d1e..0768f2b 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -46,8 +46,10 @@ class Messenger(tox_save.ToxSave): text = self._screen.messageEdit.toPlainText() if self._contacts_manager.is_active_a_friend(): self.send_message_to_friend(text) - else: + elif self._contacts_manager.is_active_a_group(): self.send_message_to_group(text) + else: + self.send_message_to_group_peer(text) def send_message_to_friend(self, text, friend_number=None): """ @@ -57,30 +59,36 @@ class Messenger(tox_save.ToxSave): """ if friend_number is None: friend_number = self._contacts_manager.get_active_number() + if text.startswith('/plugin '): self._plugin_loader.command(text[8:]) self._screen.messageEdit.clear() - elif text and friend_number >= 0: - if text.startswith('/me '): - message_type = TOX_MESSAGE_TYPE['ACTION'] - text = text[4:] + return + + if not text or friend_number < 0: + return + + if text.startswith('/me '): + message_type = TOX_MESSAGE_TYPE['ACTION'] + text = text[4:] + else: + message_type = TOX_MESSAGE_TYPE['NORMAL'] + friend = self._get_friend_by_number(friend_number) + messages = self._split_message(text.encode('utf-8')) + t = util.get_unix_time() + for message in messages: + if friend.status is not None: + message_id = self._tox.friend_send_message(friend_number, message_type, message) else: - message_type = TOX_MESSAGE_TYPE['NORMAL'] - friend = self._get_friend_by_number(friend_number) - messages = self._split_message(text.encode('utf-8')) - t = util.get_unix_time() - for message in messages: - if friend.status is not None: - message_id = self._tox.friend_send_message(friend_number, message_type, message) - else: - message_id = 0 - message_author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['NOT_SENT']) - message = OutgoingTextMessage(text, message_author, t, message_type, message_id) - friend.append_message(message) - if self._contacts_manager.is_friend_active(friend_number): - self._create_message_item(message) - self._screen.messageEdit.clear() - self._screen.messages.scrollToBottom() + message_id = 0 + message_author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['NOT_SENT']) + message = OutgoingTextMessage(text, message_author, t, message_type, message_id) + friend.append_message(message) + if not self._contacts_manager.is_friend_active(friend_number): + return + self._create_message_item(message) + self._screen.messageEdit.clear() + self._screen.messages.scrollToBottom() def send_messages(self, friend_number): """ @@ -103,27 +111,33 @@ class Messenger(tox_save.ToxSave): def send_message_to_group(self, text, group_number=None): if group_number is None: group_number = self._contacts_manager.get_active_number() + if text.startswith('/plugin '): self._plugin_loader.command(text[8:]) self._screen.messageEdit.clear() - elif text and group_number >= 0: - if text.startswith('/me '): - message_type = TOX_MESSAGE_TYPE['ACTION'] - text = text[4:] - else: - message_type = TOX_MESSAGE_TYPE['NORMAL'] - group = self._get_group_by_number(group_number) - messages = self._split_message(text.encode('utf-8')) - t = util.get_unix_time() - for message in messages: - self._tox.group_send_message(group_number, message_type, message) - message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER']) - message = OutgoingTextMessage(text, message_author, t, message_type) - group.append_message(message) - if self._contacts_manager.is_group_active(group_number): - self._create_message_item(message) - self._screen.messageEdit.clear() - self._screen.messages.scrollToBottom() + return + + if not text or group_number < 0: + return + + if text.startswith('/me '): + message_type = TOX_MESSAGE_TYPE['ACTION'] + text = text[4:] + else: + message_type = TOX_MESSAGE_TYPE['NORMAL'] + group = self._get_group_by_number(group_number) + messages = self._split_message(text.encode('utf-8')) + t = util.get_unix_time() + for message in messages: + self._tox.group_send_message(group_number, message_type, message) + message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER']) + message = OutgoingTextMessage(text, message_author, t, message_type) + group.append_message(message) + if not self._contacts_manager.is_group_active(group_number): + return + self._create_message_item(message) + self._screen.messageEdit.clear() + self._screen.messages.scrollToBottom() def new_group_message(self, group_number, message_type, message, peer_id): """ @@ -137,6 +151,53 @@ class Messenger(tox_save.ToxSave): text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']), t, message_type) self._add_message(text_message, group) + # ----------------------------------------------------------------------------------------------------------------- + # Messaging - group peers + # ----------------------------------------------------------------------------------------------------------------- + + def send_message_to_group_peer(self, text, group_number=None, peer_id=None): + if group_number is None or peer_id is None: + group_peer_contact = self._contacts_manager.get_curr_contact() + peer_id = group_peer_contact.number + group = self._get_group_by_public_key(group_peer_contact.group_pk) + group_number = group.number + + if text.startswith('/plugin '): + self._plugin_loader.command(text[8:]) + self._screen.messageEdit.clear() + return + + if not text or group_number < 0 or peer_id < 0: + return + + group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id) + group = self._get_group_by_number(group_number) + messages = self._split_message(text.encode('utf-8')) + t = util.get_unix_time() + for message in messages: + self._tox.group_send_private_message(group_number, peer_id, message) + message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER']) + message = OutgoingTextMessage(text, message_author, t, MESSAGE_TYPE['TEXT']) + group_peer_contact.append_message(message) + if not self._contacts_manager.is_contact_active(group_peer_contact): + return + self._create_message_item(message) + self._screen.messageEdit.clear() + self._screen.messages.scrollToBottom() + + def new_group_private_message(self, group_number, message, peer_id): + """ + Current user gets new message + :param message: text of message + """ + t = util.get_unix_time() + group = self._get_group_by_number(group_number) + peer = group.get_peer_by_id(peer_id) + text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']), + t, MESSAGE_TYPE['TEXT']) + group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id) + self._add_message(text_message, group_peer_contact) + # ----------------------------------------------------------------------------------------------------------------- # Message receipts # ----------------------------------------------------------------------------------------------------------------- @@ -211,6 +272,9 @@ class Messenger(tox_save.ToxSave): def _get_group_by_number(self, group_number): return self._contacts_provider.get_group_by_number(group_number) + def _get_group_by_public_key(self, public_key): + return self._contacts_provider.get_group_by_public_key( public_key) + def _on_profile_name_changed(self, new_name): if self._profile_name == new_name: return diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 8730b54..323d5d1 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -375,6 +375,26 @@ def group_message(window, tray, tox, messenger, settings, profile): return wrapped +def group_private_message(window, tray, tox, messenger, settings, profile): + """ + New private message in group chat + """ + def wrapped(tox_link, group_number, peer_id, message, length, user_data): + message = str(message[:length], 'utf-8') + invoke_in_main_thread(messenger.new_group_private_message, group_number, message, peer_id) + if not window.isActiveWindow(): + bl = settings['notify_all_gc'] or profile.name in message + name = tox.group_peer_get_name(group_number, peer_id) + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and (not settings.locked) and bl: + invoke_in_main_thread(tray_notification, name, message, tray, window) + if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['MESSAGE']) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + + return wrapped + + def group_invite(groups_service): def wrapped(tox, friend_number, invite_data, length, group_name, group_name_length, user_data): group_name = bytes(group_name[:group_name_length]) @@ -499,6 +519,7 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, # gc callbacks tox.callback_group_message(group_message(main_window, tray, tox, messenger, settings, profile), 0) + tox.callback_group_private_message(group_private_message(main_window, tray, tox, messenger, settings, profile), 0) tox.callback_group_invite(group_invite(groups_service), 0) tox.callback_group_self_join(group_self_join(contacts_provider, groups_service), 0) tox.callback_group_peer_join(group_peer_join(contacts_provider, groups_service), 0) diff --git a/toxygen/ui/contact_items.py b/toxygen/ui/contact_items.py index 2ad3ad2..7a32284 100644 --- a/toxygen/ui/contact_items.py +++ b/toxygen/ui/contact_items.py @@ -1,10 +1,7 @@ from wrapper.toxcore_enums_and_consts import * from PyQt5 import QtCore, QtGui, QtWidgets -from contacts import profile -from file_transfers.file_transfers import FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR from utils.util import * -from ui.widgets import DataLabel, create_menu -from user_data import settings +from ui.widgets import DataLabel class ContactItem(QtWidgets.QWidget): diff --git a/toxygen/ui/peer_screen.py b/toxygen/ui/peer_screen.py new file mode 100644 index 0000000..4145fe1 --- /dev/null +++ b/toxygen/ui/peer_screen.py @@ -0,0 +1,36 @@ +from ui.widgets import CenteredWidget +from PyQt5 import QtCore, QtWidgets, uic +import utils.util as util +import utils.ui as util_ui +from ui.contact_items import * + + +class PeerScreen(CenteredWidget): + + def __init__(self, contacts_manager, groups_service, group, peer_id): + super().__init__() + self._contacts_manager = contacts_manager + self._groups_service = groups_service + self._group = group + self._peer = group.get_peer_by_id(peer_id) + + uic.loadUi(util.get_views_path('peer_screen'), self) + self._update_ui() + + def _update_ui(self): + self.statusCircle = StatusCircle(self) + self.statusCircle.setGeometry(50, 20, 20, 20) + self.statusCircle.update(self._peer.status) + self.peerNameLabel.setText(self._peer.name) + self.ignorePeerCheckBox.setChecked(self._peer.is_muted) + self.sendPrivateMessagePushButton.clicked.connect(self._send_private_message) + self._retranslate_ui() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Peer details')) + self.ignorePeerCheckBox.setText(util_ui.tr('Ignore peer')) + self.sendPrivateMessagePushButton.setText(util_ui.tr('Send private message')) + + def _send_private_message(self): + self._contacts_manager.add_group_peer(self._group, self._peer) + self.close() diff --git a/toxygen/ui/views/peer_screen.ui b/toxygen/ui/views/peer_screen.ui new file mode 100644 index 0000000..cf221c0 --- /dev/null +++ b/toxygen/ui/views/peer_screen.ui @@ -0,0 +1,83 @@ + + + Form + + + + 0 + 0 + 600 + 400 + + + + + 600 + 400 + + + + + 600 + 400 + + + + Form + + + + + 110 + 10 + 431 + 41 + + + + TextLabel + + + + + + 50 + 120 + 500 + 50 + + + + PushButton + + + + + + 50 + 70 + 500 + 23 + + + + CheckBox + + + + + + 50 + 200 + 521 + 161 + + + + GroupBox + + + + + + diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index ead09ce..6a0d772 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -1,6 +1,7 @@ from ui.main_screen_widgets import * from ui.menu import * from ui.groups_widgets import * +from ui.peer_screen import * class WidgetsFactory: @@ -69,3 +70,6 @@ class WidgetsFactory: def create_search_screen(self, messages): return SearchScreen(self._contacts_manager, self._history, messages, messages.parent()) + + def create_peer_screen_window(self, group, peer_id): + return PeerScreen(self._contacts_manager, self._groups_service, group, peer_id) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 9cdac2e..7912103 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1804,7 +1804,7 @@ class Tox: """ Write the group public key with the designated peer_id for the designated group number to public_key. - This key will be parmanently tied to a particular peer until they explicitly leave the group or + This key will be permanently tied to a particular peer until they explicitly leave the group or get kicked/banned, and is the only way to reliably identify the same peer across client restarts. `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. From eba7e0c0dcd38c7129c036b4929f5e5f273e53cd Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 22 Jul 2018 14:08:47 +0300 Subject: [PATCH 096/217] group peer context menu --- toxygen/contacts/contact_menu.py | 17 +++++++++++++++++ toxygen/contacts/contacts_manager.py | 6 ++++++ toxygen/contacts/group_peer_contact.py | 3 ++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index 6a2eb86..e10f07d 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -194,3 +194,20 @@ class GroupMenuGenerator(BaseContactMenuGenerator): ).build() return menu + + +class GroupPeerMenuGenerator(BaseContactMenuGenerator): + + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): + copy_menu_builder = self._generate_copy_menu_builder(main_screen) + + builder = ContactMenuBuilder() + menu = (builder + .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_submenu(copy_menu_builder) + .with_action(util_ui.tr('Quit chat'), + lambda: contacts_manager.remove_group_peer(self._contact)) + .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) + ).build() + + return menu diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 6135967..5913617 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -362,6 +362,12 @@ class ContactsManager(ToxSave): contact.reset_avatar(self._settings['identicons']) self._save_profile() + def remove_group_peer(self, group_peer_contact): + contact = self.get_contact_by_tox_id(group_peer_contact.tox_id) + self._cleanup_contact_data(contact) + num = self._contacts.index(contact) + self._delete_contact(num) + # ----------------------------------------------------------------------------------------------------------------- # Friend requests # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/group_peer_contact.py b/toxygen/contacts/group_peer_contact.py index 47cd16b..8854198 100644 --- a/toxygen/contacts/group_peer_contact.py +++ b/toxygen/contacts/group_peer_contact.py @@ -1,4 +1,5 @@ import contacts.contact +from contacts.contact_menu import GroupPeerMenuGenerator class GroupPeerContact(contacts.contact.Contact): @@ -16,4 +17,4 @@ class GroupPeerContact(contacts.contact.Contact): pass def get_context_menu_generator(self): - return None + return GroupPeerMenuGenerator(self) From 5e1f060fac4eecb06d251072695b3bd46dc1ffca Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 22 Jul 2018 19:39:42 +0300 Subject: [PATCH 097/217] private messages - types --- toxygen/messenger/messenger.py | 14 ++++++--- toxygen/middleware/callbacks.py | 4 +-- toxygen/ui/peer_screen.py | 18 +++++++++++ toxygen/ui/views/peer_screen.ui | 55 ++++++++++++++++++++++++++++----- toxygen/wrapper/tox.py | 7 +++-- 5 files changed, 81 insertions(+), 17 deletions(-) diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 0768f2b..99d7f2f 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -170,14 +170,20 @@ class Messenger(tox_save.ToxSave): if not text or group_number < 0 or peer_id < 0: return + if text.startswith('/me '): + message_type = TOX_MESSAGE_TYPE['ACTION'] + text = text[4:] + else: + message_type = TOX_MESSAGE_TYPE['NORMAL'] + group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id) group = self._get_group_by_number(group_number) messages = self._split_message(text.encode('utf-8')) t = util.get_unix_time() for message in messages: - self._tox.group_send_private_message(group_number, peer_id, message) + self._tox.group_send_private_message(group_number, peer_id, message_type, message) message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER']) - message = OutgoingTextMessage(text, message_author, t, MESSAGE_TYPE['TEXT']) + message = OutgoingTextMessage(text, message_author, t, message_type) group_peer_contact.append_message(message) if not self._contacts_manager.is_contact_active(group_peer_contact): return @@ -185,7 +191,7 @@ class Messenger(tox_save.ToxSave): self._screen.messageEdit.clear() self._screen.messages.scrollToBottom() - def new_group_private_message(self, group_number, message, peer_id): + def new_group_private_message(self, group_number, message_type, message, peer_id): """ Current user gets new message :param message: text of message @@ -194,7 +200,7 @@ class Messenger(tox_save.ToxSave): group = self._get_group_by_number(group_number) peer = group.get_peer_by_id(peer_id) text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']), - t, MESSAGE_TYPE['TEXT']) + t, message_type) group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id) self._add_message(text_message, group_peer_contact) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 323d5d1..9e6059c 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -379,9 +379,9 @@ def group_private_message(window, tray, tox, messenger, settings, profile): """ New private message in group chat """ - def wrapped(tox_link, group_number, peer_id, message, length, user_data): + def wrapped(tox_link, group_number, peer_id, message_type, message, length, user_data): message = str(message[:length], 'utf-8') - invoke_in_main_thread(messenger.new_group_private_message, group_number, message, peer_id) + invoke_in_main_thread(messenger.new_group_private_message, group_number, message_type, message, peer_id) if not window.isActiveWindow(): bl = settings['notify_all_gc'] or profile.name in message name = tox.group_peer_get_name(group_number, peer_id) diff --git a/toxygen/ui/peer_screen.py b/toxygen/ui/peer_screen.py index 4145fe1..6850125 100644 --- a/toxygen/ui/peer_screen.py +++ b/toxygen/ui/peer_screen.py @@ -24,13 +24,31 @@ class PeerScreen(CenteredWidget): self.peerNameLabel.setText(self._peer.name) self.ignorePeerCheckBox.setChecked(self._peer.is_muted) self.sendPrivateMessagePushButton.clicked.connect(self._send_private_message) + self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key) + self.roleNameLabel.setText(self._get_role_name()) self._retranslate_ui() def _retranslate_ui(self): self.setWindowTitle(util_ui.tr('Peer details')) self.ignorePeerCheckBox.setText(util_ui.tr('Ignore peer')) + self.roleLabel.setText(util_ui.tr('Role:')) + self.copyPublicKeyPushButton.setText(util_ui.tr('Copy public key')) self.sendPrivateMessagePushButton.setText(util_ui.tr('Send private message')) + def _get_role_name(self): + roles = { + 0: util_ui.tr('Administrator'), + 1: util_ui.tr('Moderator'), + 2: util_ui.tr('User'), + 3: util_ui.tr('Observer') + } + + return roles[self._peer.role] + def _send_private_message(self): self._contacts_manager.add_group_peer(self._group, self._peer) self.close() + + def _copy_public_key(self): + clipboard = QtWidgets.QApplication.clipboard() + clipboard.setText(self._peer.public_key) diff --git a/toxygen/ui/views/peer_screen.ui b/toxygen/ui/views/peer_screen.ui index cf221c0..f719ec6 100644 --- a/toxygen/ui/views/peer_screen.ui +++ b/toxygen/ui/views/peer_screen.ui @@ -7,19 +7,19 @@ 0 0 600 - 400 + 500 600 - 400 + 500 600 - 400 + 500 @@ -31,7 +31,7 @@ 110 10 431 - 41 + 40 @@ -42,7 +42,7 @@ 50 - 120 + 140 500 50 @@ -55,7 +55,7 @@ 50 - 70 + 100 500 23 @@ -68,8 +68,8 @@ 50 - 200 - 521 + 300 + 500 161 @@ -77,6 +77,45 @@ GroupBox + + + + 50 + 60 + 67 + 20 + + + + TextLabel + + + + + + 140 + 60 + 401 + 20 + + + + TextLabel + + + + + + 50 + 210 + 500 + 50 + + + + PushButton + + diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 7912103..64b55ea 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -2060,7 +2060,7 @@ class Tox: len(data), byref(error)) return result - def group_send_private_message(self, group_number, peer_id, message): + def group_send_private_message(self, group_number, peer_id, message_type, message): """ Send a text chat message to the specified peer in the specified group. @@ -2079,7 +2079,8 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_send_private_message(self._tox_pointer, group_number, peer_id, message, + result = Tox.libtoxcore.tox_group_send_private_message(self._tox_pointer, group_number, peer_id, + message_type, message, len(message), byref(error)) return result @@ -2135,7 +2136,7 @@ class Tox: This event is triggered when the client receives a private message. """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint8, c_char_p, c_size_t, c_void_p) self.group_private_message_cb = c_callback(callback) Tox.libtoxcore.tox_callback_group_private_message(self._tox_pointer, self.group_private_message_cb, user_data) From 20f36e06ad59d5cadbf96340708b652564c7f553 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 23 Jul 2018 00:35:52 +0300 Subject: [PATCH 098/217] roles support - callbacks, peer screen --- toxygen/app.py | 5 +-- toxygen/contacts/contacts_manager.py | 51 +++++++++++++++++----------- toxygen/contacts/group_chat.py | 3 ++ toxygen/groups/groups_service.py | 13 +++++++ toxygen/middleware/callbacks.py | 37 ++++++++++++++++++-- toxygen/ui/peer_screen.py | 49 +++++++++++++++++++++----- toxygen/ui/views/peer_screen.ui | 14 ++++++-- 7 files changed, 139 insertions(+), 33 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index b270f10..23a465a 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -237,7 +237,7 @@ class App: self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) data = self._profile_manager.open_profile() if self._toxes.is_data_encrypted(data): - data = self._enter_pass(data) + data = self._enter_password(data) self._tox = self._create_tox(data) def _create_new_profile(self, profile_name): @@ -292,7 +292,7 @@ class App: # Other private methods # ----------------------------------------------------------------------------------------------------------------- - def _enter_pass(self, data): + def _enter_password(self, data): """ Show password screen """ @@ -326,6 +326,7 @@ class App: self._calls_manager.set_toxav(self._tox.AV) self._contacts_manager.update_friends_numbers() + self._contacts_manager.update_groups_lists() self._init_callbacks() diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 5913617..10f50ca 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -260,25 +260,26 @@ class ContactsManager(ToxSave): text = util_ui.tr("Enter new alias for friend {} or leave empty to use friend's name:").format(name) title = util_ui.tr('Set alias') text, ok = util_ui.text_dialog(text, title, name) - if ok: - aliases = self._settings['friends_aliases'] - if text: - friend.name = text - try: - index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) - aliases[index] = (friend.tox_id, text) - except: - aliases.append((friend.tox_id, text)) - friend.set_alias(text) - else: # use default name - friend.name = self._tox.friend_get_name(friend.number) - friend.set_alias('') - try: - index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) - del aliases[index] - except: - pass - self._settings.save() + if not ok: + return + aliases = self._settings['friends_aliases'] + if text: + friend.name = text + try: + index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) + aliases[index] = (friend.tox_id, text) + except: + aliases.append((friend.tox_id, text)) + friend.set_alias(text) + else: # use default name + friend.name = self._tox.friend_get_name(friend.number) + friend.set_alias('') + try: + index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) + del aliases[index] + except: + pass + self._settings.save() def friend_public_key(self, num): return self._contacts[num].tox_id @@ -362,6 +363,13 @@ class ContactsManager(ToxSave): contact.reset_avatar(self._settings['identicons']) self._save_profile() + def remove_group_peer_by_id(self, group, peer_id): + peer = group.get_peer_by_id(peer_id) + if not self.check_if_contact_exists(peer.public_key): + return + contact = self.get_contact_by_tox_id(peer.public_key) + self.remove_group_peer(contact) + def remove_group_peer(self, group_peer_contact): contact = self.get_contact_by_tox_id(group_peer_contact.tox_id) self._cleanup_contact_data(contact) @@ -438,6 +446,11 @@ class ContactsManager(ToxSave): group.number = i self.update_filtration() + def update_groups_lists(self): + groups = self._contact_provider.get_all_groups() + for group in groups: + group.remove_all_peers_except_self() + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 61b3858..5409a26 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -28,6 +28,9 @@ class GroupChat(contact.Contact, ToxSave): # Peers methods # ----------------------------------------------------------------------------------------------------------------- + def get_self_peer(self): + return self._peers[0] + def get_self_name(self): return self._peers[0].name diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 49d5f25..692fb31 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -109,6 +109,19 @@ class GroupsService(tox_save.ToxSave): self._peer_screen = widgets_factory.create_peer_screen_window(group, peer_id) self._peer_screen.show() + # ----------------------------------------------------------------------------------------------------------------- + # Peers actions + # ----------------------------------------------------------------------------------------------------------------- + + def set_new_peer_role(self, group, peer, role): + self._tox.group_mod_set_role(group.number, peer.id, role) + peer.role = role + self.generate_peers_list() + + def toggle_ignore_peer(self, group, peer, ignore): + self._tox.group_toggle_ignore(group.number, peer.id, ignore) + peer.is_muted = ignore + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 9e6059c..a480223 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -424,9 +424,10 @@ def group_peer_join(contacts_provider, groups_service): return wrapped -def group_peer_exit(contacts_provider, groups_service): +def group_peer_exit(contacts_provider, groups_service, contacts_manager): def wrapped(tox, group_number, peer_id, message, length, user_data): group = contacts_provider.get_group_by_number(group_number) + contacts_manager.remove_group_peer_by_id(group, peer_id) group.remove_peer(peer_id) invoke_in_main_thread(groups_service.generate_peers_list) @@ -461,6 +462,37 @@ def group_topic(contacts_provider): return wrapped + +def group_moderation(groups_service, contacts_provider, contacts_manager, messenger): + + def update_peer_role(group, mod_peer_id, peer_id, new_role): + peer = group.get_peer_by_id(peer_id) + peer.role = new_role + # TODO: add info message + + def remove_peer(group, mod_peer_id, peer_id, is_ban): + contacts_manager.remove_group_peer_by_id(group, peer_id) + group.remove_peer(peer_id) + # TODO: add info message + + def wrapped(tox, group_number, mod_peer_id, peer_id, event_type, user_data): + group = contacts_provider.get_group_by_number(group_number) + + if event_type == TOX_GROUP_MOD_EVENT['KICK']: + remove_peer(group, mod_peer_id, peer_id, False) + elif event_type == TOX_GROUP_MOD_EVENT['BAN']: + remove_peer(group, mod_peer_id, peer_id, True) + elif event_type == TOX_GROUP_MOD_EVENT['OBSERVER']: + update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['OBSERVER']) + elif event_type == TOX_GROUP_MOD_EVENT['USER']: + update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['USER']) + elif event_type == TOX_GROUP_MOD_EVENT['MODERATOR']: + update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['MODERATOR']) + + groups_service.generate_peers_list() + + return wrapped + # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization # ----------------------------------------------------------------------------------------------------------------- @@ -523,7 +555,8 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_group_invite(group_invite(groups_service), 0) tox.callback_group_self_join(group_self_join(contacts_provider, groups_service), 0) tox.callback_group_peer_join(group_peer_join(contacts_provider, groups_service), 0) - tox.callback_group_peer_exit(group_peer_exit(contacts_provider, groups_service), 0) + tox.callback_group_peer_exit(group_peer_exit(contacts_provider, groups_service, contacts_manager), 0) tox.callback_group_peer_name(group_peer_name(contacts_provider, groups_service), 0) tox.callback_group_peer_status(group_peer_status(contacts_provider, groups_service), 0) tox.callback_group_topic(group_topic(contacts_provider), 0) + tox.callback_group_moderation(group_moderation(groups_service, contacts_provider, contacts_manager, messenger), 0) diff --git a/toxygen/ui/peer_screen.py b/toxygen/ui/peer_screen.py index 6850125..651d998 100644 --- a/toxygen/ui/peer_screen.py +++ b/toxygen/ui/peer_screen.py @@ -14,36 +14,69 @@ class PeerScreen(CenteredWidget): self._group = group self._peer = group.get_peer_by_id(peer_id) + self._roles = { + TOX_GROUP_ROLE['FOUNDER']: util_ui.tr('Administrator'), + TOX_GROUP_ROLE['MODERATOR']: util_ui.tr('Moderator'), + TOX_GROUP_ROLE['USER']: util_ui.tr('User'), + TOX_GROUP_ROLE['OBSERVER']: util_ui.tr('Observer') + } + uic.loadUi(util.get_views_path('peer_screen'), self) self._update_ui() def _update_ui(self): self.statusCircle = StatusCircle(self) - self.statusCircle.setGeometry(50, 20, 20, 20) + self.statusCircle.setGeometry(50, 15, 30, 30) + self.statusCircle.update(self._peer.status) self.peerNameLabel.setText(self._peer.name) self.ignorePeerCheckBox.setChecked(self._peer.is_muted) + self.ignorePeerCheckBox.clicked.connect(self._toggle_ignore) self.sendPrivateMessagePushButton.clicked.connect(self._send_private_message) self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key) self.roleNameLabel.setText(self._get_role_name()) + can_change_role = self._can_change_role() + self.rolesComboBox.setVisible(can_change_role) + self.roleNameLabel.setVisible(not can_change_role) + self._retranslate_ui() + self.rolesComboBox.currentIndexChanged.connect(self._role_set) + def _retranslate_ui(self): self.setWindowTitle(util_ui.tr('Peer details')) self.ignorePeerCheckBox.setText(util_ui.tr('Ignore peer')) self.roleLabel.setText(util_ui.tr('Role:')) self.copyPublicKeyPushButton.setText(util_ui.tr('Copy public key')) self.sendPrivateMessagePushButton.setText(util_ui.tr('Send private message')) + self.banGroupBox.setTitle(util_ui.tr('Moderation')) + + self.rolesComboBox.clear() + index = self._group.get_self_peer().role + roles = list(self._roles.values()) + for role in roles[index + 1:]: + self.rolesComboBox.addItem(role) + self.rolesComboBox.setCurrentIndex(self._peer.role - index - 1) + + def _can_change_role(self): + self_peer = self._group.get_self_peer() + if self_peer.role > TOX_GROUP_ROLE['MODERATOR']: + return False + + return self_peer.role < self._peer.role + + def _role_set(self): + index = self.rolesComboBox.currentIndex() + all_roles_count = len(self._roles) + diff = all_roles_count - self.rolesComboBox.count() + self._groups_service.set_new_peer_role(self._group, self._peer, index + diff) def _get_role_name(self): - roles = { - 0: util_ui.tr('Administrator'), - 1: util_ui.tr('Moderator'), - 2: util_ui.tr('User'), - 3: util_ui.tr('Observer') - } + return self._roles[self._peer.role] - return roles[self._peer.role] + def _toggle_ignore(self): + ignore = self.ignorePeerCheckBox.isChecked() + self._groups_service.toggle_ignore_peer(self._group, self._peer, ignore) def _send_private_message(self): self._contacts_manager.add_group_peer(self._group, self._peer) diff --git a/toxygen/ui/views/peer_screen.ui b/toxygen/ui/views/peer_screen.ui index f719ec6..f6b13b3 100644 --- a/toxygen/ui/views/peer_screen.ui +++ b/toxygen/ui/views/peer_screen.ui @@ -93,9 +93,9 @@ - 140 + 130 60 - 401 + 411 20 @@ -116,6 +116,16 @@ PushButton + + + + 130 + 55 + 291 + 30 + + + From 27d24ecaf4dcabad9ad7c76b6c78441a40dc1f32 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 23 Jul 2018 00:50:53 +0300 Subject: [PATCH 099/217] group - privacy state added --- toxygen/contacts/group_chat.py | 12 +++++++++++- toxygen/contacts/group_factory.py | 4 +++- toxygen/middleware/callbacks.py | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 5409a26..38ab686 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -8,9 +8,10 @@ from common.tox_save import ToxSave class GroupChat(contact.Contact, ToxSave): - def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id): + def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id, is_private): super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) ToxSave.__init__(self, tox) + self._is_private = is_private self._peers = [] self._add_self_to_gc() @@ -24,6 +25,15 @@ class GroupChat(contact.Contact, ToxSave): def get_context_menu_generator(self): return GroupMenuGenerator(self) + # ----------------------------------------------------------------------------------------------------------------- + # Properties + # ----------------------------------------------------------------------------------------------------------------- + + def get_is_private(self): + return self._is_private + + is_private = property(get_is_private) + # ----------------------------------------------------------------------------------------------------------------- # Peers methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py index 8db3e9a..2a925f3 100644 --- a/toxygen/contacts/group_factory.py +++ b/toxygen/contacts/group_factory.py @@ -1,5 +1,6 @@ from contacts.group_chat import GroupChat from common.tox_save import ToxSave +import wrapper.toxcore_enums_and_consts as constants class GroupFactory(ToxSave): @@ -27,8 +28,9 @@ class GroupFactory(ToxSave): name = alias or self._tox.group_get_name(group_number) or tox_id status_message = self._tox.group_get_topic(group_number) message_getter = self._db.messages_getter(tox_id) + is_private = self._tox.group_get_privacy_state() == constants.TOX_GROUP_PRIVACY_STATE['PRIVATE'] group = GroupChat(self._tox, self._profile_manager, message_getter, group_number, name, status_message, - item, tox_id) + item, tox_id, is_private) group.set_alias(alias) return group diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index a480223..3d40cc9 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -489,7 +489,7 @@ def group_moderation(groups_service, contacts_provider, contacts_manager, messen elif event_type == TOX_GROUP_MOD_EVENT['MODERATOR']: update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['MODERATOR']) - groups_service.generate_peers_list() + invoke_in_main_thread(groups_service.generate_peers_list) return wrapped From 850c3b1ca3398a1b7df85826f2088cbccebc3466 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 24 Jul 2018 23:40:29 +0300 Subject: [PATCH 100/217] self peer screen added --- toxygen/contacts/group_factory.py | 2 +- toxygen/groups/groups_service.py | 13 +++++- toxygen/ui/self_peer_screen.py | 67 +++++++++++++++++++++++++++++++ toxygen/ui/widgets_factory.py | 4 ++ 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 toxygen/ui/self_peer_screen.py diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py index 2a925f3..4083438 100644 --- a/toxygen/contacts/group_factory.py +++ b/toxygen/contacts/group_factory.py @@ -28,7 +28,7 @@ class GroupFactory(ToxSave): name = alias or self._tox.group_get_name(group_number) or tox_id status_message = self._tox.group_get_topic(group_number) message_getter = self._db.messages_getter(tox_id) - is_private = self._tox.group_get_privacy_state() == constants.TOX_GROUP_PRIVACY_STATE['PRIVATE'] + is_private = self._tox.group_get_privacy_state(group_number) == constants.TOX_GROUP_PRIVACY_STATE['PRIVATE'] group = GroupChat(self._tox, self._profile_manager, message_getter, group_number, name, status_message, item, tox_id, is_private) group.set_alias(alias) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 692fb31..94d5653 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -106,7 +106,11 @@ class GroupsService(tox_save.ToxSave): def peer_selected(self, chat_id, peer_id): widgets_factory = self._widgets_factory_provider.get_item() group = self._get_group_by_public_key(chat_id) - self._peer_screen = widgets_factory.create_peer_screen_window(group, peer_id) + self_peer = group.get_self_peer() + if self_peer.id != peer_id: + self._peer_screen = widgets_factory.create_peer_screen_window(group, peer_id) + else: + self._peer_screen = widgets_factory.create_self_peer_screen_window(group) self._peer_screen.show() # ----------------------------------------------------------------------------------------------------------------- @@ -122,6 +126,13 @@ class GroupsService(tox_save.ToxSave): self._tox.group_toggle_ignore(group.number, peer.id, ignore) peer.is_muted = ignore + def set_self_info(self, group, name, status): + self._tox.group_self_set_name(group.number, name) + self._tox.group_self_set_status(group.number, status) + self_peer = group.get_self_peer() + self_peer.name = name + self_peer.status = status + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/ui/self_peer_screen.py b/toxygen/ui/self_peer_screen.py new file mode 100644 index 0000000..ebc1426 --- /dev/null +++ b/toxygen/ui/self_peer_screen.py @@ -0,0 +1,67 @@ +from ui.widgets import CenteredWidget, LineEdit +from PyQt5 import QtCore, QtWidgets, uic +import utils.util as util +import utils.ui as util_ui +from ui.contact_items import * + + +class SelfPeerScreen(CenteredWidget): + + def __init__(self, contacts_manager, groups_service, group): + super().__init__() + self._contacts_manager = contacts_manager + self._groups_service = groups_service + self._group = group + self._peer = group.get_self_peer() + self._roles = { + TOX_GROUP_ROLE['FOUNDER']: util_ui.tr('Administrator'), + TOX_GROUP_ROLE['MODERATOR']: util_ui.tr('Moderator'), + TOX_GROUP_ROLE['USER']: util_ui.tr('User'), + TOX_GROUP_ROLE['OBSERVER']: util_ui.tr('Observer') + } + + uic.loadUi(util.get_views_path('self_peer_screen'), self) + self._update_ui() + + def _update_ui(self): + self.lineEdit = LineEdit(self) + self.lineEdit.setGeometry(140, 40, 400, 30) + self.lineEdit.setText(self._peer.name) + self.lineEdit.textChanged.connect(self._nick_changed) + + self.savePushButton.clicked.connect(self._save) + self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key) + + self._retranslate_ui() + + self.statusComboBox.setCurrentIndex(self._peer.status) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Change credentials in group')) + self.lineEdit.setPlaceholderText(util_ui.tr('Your nickname in group')) + self.nameLabel.setText(util_ui.tr('Name:')) + self.roleLabel.setText(util_ui.tr('Role:')) + self.statusLabel.setText(util_ui.tr('Status:')) + self.copyPublicKeyPushButton.setText(util_ui.tr('Copy public key')) + self.savePushButton.setText(util_ui.tr('Save')) + self.roleNameLabel.setText(self._get_role_name()) + self.statusComboBox.addItem(util_ui.tr('Online')) + self.statusComboBox.addItem(util_ui.tr('Away')) + self.statusComboBox.addItem(util_ui.tr('Busy')) + + def _get_role_name(self): + return self._roles[self._peer.role] + + def _nick_changed(self): + nick = self.lineEdit.text() + self.savePushButton.setEnabled(bool(nick)) + + def _save(self): + nick = self.lineEdit.text() + status = self.statusComboBox.currentIndex() + self._groups_service.set_self_info(self._group, nick, status) + self.close() + + def _copy_public_key(self): + clipboard = QtWidgets.QApplication.clipboard() + clipboard.setText(self._peer.public_key) diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 6a0d772..1ddc6a5 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -2,6 +2,7 @@ from ui.main_screen_widgets import * from ui.menu import * from ui.groups_widgets import * from ui.peer_screen import * +from ui.self_peer_screen import * class WidgetsFactory: @@ -73,3 +74,6 @@ class WidgetsFactory: def create_peer_screen_window(self, group, peer_id): return PeerScreen(self._contacts_manager, self._groups_service, group, peer_id) + + def create_self_peer_screen_window(self, group): + return SelfPeerScreen(self._contacts_manager, self._groups_service, group) From 3272617403699e3b6d1a6f1197047ec046c2bd57 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 26 Jul 2018 00:38:25 +0300 Subject: [PATCH 101/217] join group with different credentials --- toxygen/app.py | 2 +- toxygen/groups/groups_service.py | 17 +++--- toxygen/ui/groups_widgets.py | 60 ++++++++++++++++--- toxygen/ui/views/create_group_screen.ui | 80 +++++++++++++++++++------ toxygen/ui/views/join_group_screen.ui | 78 ++++++++++++++++++++---- toxygen/ui/widgets_factory.py | 4 +- toxygen/wrapper/tox.py | 32 ++++++---- 7 files changed, 217 insertions(+), 56 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 23a465a..77864e7 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -366,7 +366,7 @@ class App: widgets_factory = None widgets_factory_provider = Provider(lambda: widgets_factory) self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms, - widgets_factory_provider) + widgets_factory_provider, self._profile) widgets_factory = WidgetsFactory(self._settings, self._profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, self._toxes, self._version, self._groups_service, history) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 94d5653..f9cdfc9 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -6,12 +6,13 @@ import wrapper.toxcore_enums_and_consts as constants class GroupsService(tox_save.ToxSave): - def __init__(self, tox, contacts_manager, contacts_provider, main_screen, widgets_factory_provider): + def __init__(self, tox, contacts_manager, contacts_provider, main_screen, widgets_factory_provider, profile): super().__init__(tox) self._contacts_manager = contacts_manager self._contacts_provider = contacts_provider self._peers_list_widget = main_screen.peers_list self._widgets_factory_provider = widgets_factory_provider + self._profile = profile self._peer_screen = None def set_tox(self, tox): @@ -23,8 +24,8 @@ class GroupsService(tox_save.ToxSave): # Groups creation # ----------------------------------------------------------------------------------------------------------------- - def create_new_gc(self, name, privacy_state): - group_number = self._tox.group_new(privacy_state, name.encode('utf-8')) + def create_new_gc(self, name, privacy_state, nick, status): + group_number = self._tox.group_new(privacy_state, name, nick, status) if group_number == -1: return @@ -32,12 +33,12 @@ class GroupsService(tox_save.ToxSave): group = self._get_group_by_number(group_number) group.status = constants.TOX_USER_STATUS['NONE'] - def join_gc_by_id(self, chat_id, password): - group_number = self._tox.group_join(chat_id, password) + def join_gc_by_id(self, chat_id, password, nick, status): + group_number = self._tox.group_join(chat_id, password, nick, status) self._add_new_group_by_number(group_number) - def join_gc_via_invite(self, invite_data, friend_number, password): - group_number = self._tox.group_invite_accept(invite_data, friend_number, password) + def join_gc_via_invite(self, invite_data, friend_number, nick, status, password): + group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, status, password) self._add_new_group_by_number(group_number) # ----------------------------------------------------------------------------------------------------------------- @@ -72,7 +73,7 @@ class GroupsService(tox_save.ToxSave): friend = self._get_friend_by_number(friend_number) text = util_ui.tr('Friend {} invites you to group "{}". Accept?') if util_ui.question(text.format(friend.name, group_name), util_ui.tr('Group invite')): - self.join_gc_via_invite(invite_data, friend_number, None) + self.join_gc_via_invite(invite_data, friend_number, self._profile.name, self._profile.status or 0, None) # ----------------------------------------------------------------------------------------------------------------- # Group info methods diff --git a/toxygen/ui/groups_widgets.py b/toxygen/ui/groups_widgets.py index aa91935..b9f38a9 100644 --- a/toxygen/ui/groups_widgets.py +++ b/toxygen/ui/groups_widgets.py @@ -3,72 +3,116 @@ import utils.util as util from ui.widgets import * from wrapper.toxcore_enums_and_consts import * +# TODO: move common logic to separate class + class CreateGroupScreen(CenteredWidget): - def __init__(self, groups_service): + def __init__(self, groups_service, profile): super().__init__() self._groups_service = groups_service + self._profile = profile uic.loadUi(util.get_views_path('create_group_screen'), self) self.center() self._update_ui() def _update_ui(self): self._retranslate_ui() + + self.statusComboBox.setCurrentIndex(self._profile.status or 0) + self.nickLineEdit.setText(self._profile.name) + self.addGroupButton.clicked.connect(self._create_group) self.groupNameLineEdit.textChanged.connect(self._group_name_changed) + self.nickLineEdit.textChanged.connect(self._nick_changed) def _retranslate_ui(self): self.setWindowTitle(util_ui.tr('Create new group chat')) self.groupNameLabel.setText(util_ui.tr('Group name:')) self.groupTypeLabel.setText(util_ui.tr('Group type:')) + self.nickLabel.setText(util_ui.tr('Nickname:')) + self.statusLabel.setText(util_ui.tr('Status:')) + self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat')) self.groupNameLineEdit.setPlaceholderText(util_ui.tr('Group\'s persistent name')) self.addGroupButton.setText(util_ui.tr('Create group')) self.groupTypeComboBox.addItem(util_ui.tr('Public')) self.groupTypeComboBox.addItem(util_ui.tr('Private')) self.groupTypeComboBox.setCurrentIndex(1) + self.statusComboBox.addItem(util_ui.tr('Online')) + self.statusComboBox.addItem(util_ui.tr('Away')) + self.statusComboBox.addItem(util_ui.tr('Busy')) def _create_group(self): - name = self.groupNameLineEdit.text() + group_name = self.groupNameLineEdit.text() privacy_state = self.groupTypeComboBox.currentIndex() - self._groups_service.create_new_gc(name, privacy_state) + nick = self.nickLineEdit.text() + status = self.statusComboBox.currentIndex() + self._groups_service.create_new_gc(group_name, privacy_state, nick, status) self.close() + def _nick_changed(self): + self._update_button_state() + def _group_name_changed(self): - name = self.groupNameLineEdit.text() - self.addGroupButton.setEnabled(bool(name.strip())) + self._update_button_state() + + def _update_button_state(self): + is_nick_set = bool(self.nickLineEdit.text()) + is_group_name_set = bool(self.groupNameLineEdit.text()) + self.addGroupButton.setEnabled(is_nick_set and is_group_name_set) class JoinGroupScreen(CenteredWidget): - def __init__(self, groups_service): + def __init__(self, groups_service, profile): super().__init__() self._groups_service = groups_service + self._profile = profile uic.loadUi(util.get_views_path('join_group_screen'), self) self.center() self._update_ui() def _update_ui(self): self._retranslate_ui() + + self.statusComboBox.setCurrentIndex(self._profile.status or 0) + self.nickLineEdit.setText(self._profile.name) + self.chatIdLineEdit.textChanged.connect(self._chat_id_changed) self.joinGroupButton.clicked.connect(self._join_group) + self.nickLineEdit.textChanged.connect(self._nick_changed) def _retranslate_ui(self): self.setWindowTitle(util_ui.tr('Join public group chat')) self.chatIdLabel.setText(util_ui.tr('Group ID:')) self.passwordLabel.setText(util_ui.tr('Password:')) + self.nickLabel.setText(util_ui.tr('Nickname:')) + self.statusLabel.setText(util_ui.tr('Status:')) self.chatIdLineEdit.setPlaceholderText(util_ui.tr('Group\'s chat ID')) + self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat')) self.joinGroupButton.setText(util_ui.tr('Join group')) self.passwordLineEdit.setPlaceholderText(util_ui.tr('Optional password')) + self.statusComboBox.addItem(util_ui.tr('Online')) + self.statusComboBox.addItem(util_ui.tr('Away')) + self.statusComboBox.addItem(util_ui.tr('Busy')) def _chat_id_changed(self): + self._update_button_state() + + def _nick_changed(self): + self._update_button_state() + + def _update_button_state(self): chat_id = self._get_chat_id() - self.joinGroupButton.setEnabled(len(chat_id) == TOX_GROUP_CHAT_ID_SIZE * 2) + is_nick_set = bool(self.nickLineEdit.text()) + self.joinGroupButton.setEnabled(len(chat_id) == TOX_GROUP_CHAT_ID_SIZE * 2 and is_nick_set) def _join_group(self): chat_id = self._get_chat_id() password = self.passwordLineEdit.text() - self._groups_service.join_gc_by_id(chat_id, password) + nick = self.nickLineEdit.text() + status = self.statusComboBox.currentIndex() + self._groups_service.join_gc_by_id(chat_id, password, nick, status) self.close() def _get_chat_id(self): diff --git a/toxygen/ui/views/create_group_screen.ui b/toxygen/ui/views/create_group_screen.ui index 08a27ff..3a3358a 100644 --- a/toxygen/ui/views/create_group_screen.ui +++ b/toxygen/ui/views/create_group_screen.ui @@ -6,8 +6,8 @@ 0 0 - 639 - 199 + 640 + 300 @@ -19,9 +19,9 @@ - 180 - 150 - 271 + 20 + 250 + 601 41 @@ -32,28 +32,28 @@ - 140 - 40 - 471 - 31 + 150 + 20 + 470 + 35 - 140 - 100 - 471 - 41 + 150 + 80 + 470 + 35 - 10 - 40 + 20 + 20 121 31 @@ -65,8 +65,8 @@ - 10 - 100 + 20 + 80 121 31 @@ -75,6 +75,52 @@ TextLabel + + + + 20 + 200 + 111 + 17 + + + + TextLabel + + + + + + 20 + 150 + 111 + 17 + + + + TextLabel + + + + + + 150 + 140 + 470 + 35 + + + + + + + 150 + 190 + 470 + 35 + + + diff --git a/toxygen/ui/views/join_group_screen.ui b/toxygen/ui/views/join_group_screen.ui index 66b0420..077a332 100644 --- a/toxygen/ui/views/join_group_screen.ui +++ b/toxygen/ui/views/join_group_screen.ui @@ -6,10 +6,22 @@ 0 0 - 739 - 212 + 740 + 320 + + + 740 + 320 + + + + + 740 + 320 + + Form @@ -17,7 +29,7 @@ 30 - 40 + 30 67 17 @@ -30,7 +42,7 @@ 30 - 100 + 90 67 17 @@ -45,9 +57,9 @@ - 258 - 150 - 241 + 30 + 260 + 680 51 @@ -60,7 +72,7 @@ 190 20 - 431 + 520 41 @@ -69,8 +81,54 @@ 190 - 90 - 431 + 80 + 520 + 41 + + + + + + + 30 + 150 + 67 + 17 + + + + TextLabel + + + + + + 30 + 210 + 67 + 17 + + + + TextLabel + + + + + + 190 + 140 + 520 + 41 + + + + + + + 190 + 200 + 520 41 diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 1ddc6a5..8f341df 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -64,10 +64,10 @@ class WidgetsFactory: return StickerWindow(self._file_transfer_handler, self._contacts_manager) def create_group_screen_window(self): - return CreateGroupScreen(self._groups_service) + return CreateGroupScreen(self._groups_service, self._profile) def create_join_group_screen_window(self): - return JoinGroupScreen(self._groups_service) + return JoinGroupScreen(self._groups_service, self._profile) def create_search_screen(self, messages): return SearchScreen(self._contacts_manager, self._history, messages, messages.parent()) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 64b55ea..cb2f25e 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -29,7 +29,7 @@ class ToxOptions(Structure): class GroupChatSelfPeerInfo(Structure): _fields_ = [ ('nick', c_char_p), - ('nick_length', c_uint16), + ('nick_length', c_uint8), ('user_status', c_int) ] @@ -1533,7 +1533,7 @@ class Tox: # Group chat instance management # ----------------------------------------------------------------------------------------------------------------- - def group_new(self, privacy_state, group_name): + def group_new(self, privacy_state, group_name, nick, status): """ Creates a new group chat. @@ -1551,12 +1551,16 @@ class Tox: """ error = c_int() - peer_info = self.group_chat_self_peer_info_new() - result = Tox.libtoxcore.tox_group_new(self._tox_pointer, privacy_state, group_name, + peer_info = self.group_self_peer_info_new() + nick = bytes(nick, 'utf-8') + peer_info.contents.nick = c_char_p(nick) + peer_info.contents.nick_length = len(nick) + peer_info.contents.user_status = status + result = Tox.libtoxcore.tox_group_new(self._tox_pointer, privacy_state, group_name.encode('utf-8'), len(group_name), peer_info, byref(error)) return result - def group_join(self, chat_id, password): + def group_join(self, chat_id, password, nick, status): """ Joins a group chat with specified Chat ID. @@ -1571,7 +1575,11 @@ class Tox: """ error = c_int() - peer_info = self.group_chat_self_peer_info_new() + peer_info = self.group_self_peer_info_new() + nick = bytes(nick, 'utf-8') + peer_info.contents.nick = c_char_p(nick) + peer_info.contents.nick_length = len(nick) + peer_info.contents.user_status = status result = Tox.libtoxcore.tox_group_join(self._tox_pointer, string_to_bin(chat_id), password, len(password) if password is not None else 0, @@ -2171,15 +2179,15 @@ class Tox: result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, group_number, friend_number, byref(error)) return result - def group_chat_self_peer_info_new(self): + def group_self_peer_info_new(self): error = c_int() - f = Tox.libtoxcore.group_chat_self_peer_info_new + f = Tox.libtoxcore.tox_group_self_peer_info_new f.restype = POINTER(GroupChatSelfPeerInfo) result = f(self._tox_pointer, byref(error)) return result - def group_invite_accept(self, invite_data, friend_number, password=None): + def group_invite_accept(self, invite_data, friend_number, nick, status, password=None): """ Accept an invite to a group chat that the client previously received from a friend. The invite is only valid while the inviter is present in the group. @@ -2192,7 +2200,11 @@ class Tox: error = c_int() f = Tox.libtoxcore.tox_group_invite_accept f.restype = c_uint32 - peer_info = self.group_chat_self_peer_info_new() + peer_info = self.group_self_peer_info_new() + nick = bytes(nick, 'utf-8') + peer_info.contents.nick = c_char_p(nick) + peer_info.contents.nick_length = len(nick) + peer_info.contents.user_status = status result = f(self._tox_pointer, friend_number, invite_data, len(invite_data), password, len(password) if password is not None else 0, peer_info, byref(error)) print('Invite accept. Result:', result, 'Error:', error.value) From 1728a45cf35a25c5921443469c56609280aaa6ff Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 26 Jul 2018 21:27:20 +0300 Subject: [PATCH 102/217] peers screen refactoring --- toxygen/ui/groups_widgets.py | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/toxygen/ui/groups_widgets.py b/toxygen/ui/groups_widgets.py index b9f38a9..ad4b703 100644 --- a/toxygen/ui/groups_widgets.py +++ b/toxygen/ui/groups_widgets.py @@ -3,15 +3,27 @@ import utils.util as util from ui.widgets import * from wrapper.toxcore_enums_and_consts import * -# TODO: move common logic to separate class - -class CreateGroupScreen(CenteredWidget): +class BaseGroupScreen(CenteredWidget): def __init__(self, groups_service, profile): super().__init__() self._groups_service = groups_service self._profile = profile + + def _retranslate_ui(self): + self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat')) + self.nickLabel.setText(util_ui.tr('Nickname:')) + self.statusLabel.setText(util_ui.tr('Status:')) + self.statusComboBox.addItem(util_ui.tr('Online')) + self.statusComboBox.addItem(util_ui.tr('Away')) + self.statusComboBox.addItem(util_ui.tr('Busy')) + + +class CreateGroupScreen(BaseGroupScreen): + + def __init__(self, groups_service, profile): + super().__init__(groups_service, profile) uic.loadUi(util.get_views_path('create_group_screen'), self) self.center() self._update_ui() @@ -27,20 +39,15 @@ class CreateGroupScreen(CenteredWidget): self.nickLineEdit.textChanged.connect(self._nick_changed) def _retranslate_ui(self): + super()._retranslate_ui() self.setWindowTitle(util_ui.tr('Create new group chat')) self.groupNameLabel.setText(util_ui.tr('Group name:')) self.groupTypeLabel.setText(util_ui.tr('Group type:')) - self.nickLabel.setText(util_ui.tr('Nickname:')) - self.statusLabel.setText(util_ui.tr('Status:')) - self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat')) self.groupNameLineEdit.setPlaceholderText(util_ui.tr('Group\'s persistent name')) self.addGroupButton.setText(util_ui.tr('Create group')) self.groupTypeComboBox.addItem(util_ui.tr('Public')) self.groupTypeComboBox.addItem(util_ui.tr('Private')) self.groupTypeComboBox.setCurrentIndex(1) - self.statusComboBox.addItem(util_ui.tr('Online')) - self.statusComboBox.addItem(util_ui.tr('Away')) - self.statusComboBox.addItem(util_ui.tr('Busy')) def _create_group(self): group_name = self.groupNameLineEdit.text() @@ -62,12 +69,10 @@ class CreateGroupScreen(CenteredWidget): self.addGroupButton.setEnabled(is_nick_set and is_group_name_set) -class JoinGroupScreen(CenteredWidget): +class JoinGroupScreen(BaseGroupScreen): def __init__(self, groups_service, profile): - super().__init__() - self._groups_service = groups_service - self._profile = profile + super().__init__(groups_service, profile) uic.loadUi(util.get_views_path('join_group_screen'), self) self.center() self._update_ui() @@ -83,18 +88,13 @@ class JoinGroupScreen(CenteredWidget): self.nickLineEdit.textChanged.connect(self._nick_changed) def _retranslate_ui(self): + super()._retranslate_ui() self.setWindowTitle(util_ui.tr('Join public group chat')) self.chatIdLabel.setText(util_ui.tr('Group ID:')) self.passwordLabel.setText(util_ui.tr('Password:')) - self.nickLabel.setText(util_ui.tr('Nickname:')) - self.statusLabel.setText(util_ui.tr('Status:')) self.chatIdLineEdit.setPlaceholderText(util_ui.tr('Group\'s chat ID')) - self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat')) self.joinGroupButton.setText(util_ui.tr('Join group')) self.passwordLineEdit.setPlaceholderText(util_ui.tr('Optional password')) - self.statusComboBox.addItem(util_ui.tr('Online')) - self.statusComboBox.addItem(util_ui.tr('Away')) - self.statusComboBox.addItem(util_ui.tr('Busy')) def _chat_id_changed(self): self._update_button_state() From 184ba55aedc802b5e9aaa9ee4cb0e12ea325cb85 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 28 Jul 2018 13:14:16 +0300 Subject: [PATCH 103/217] group invites page --- toxygen/app.py | 5 +- toxygen/groups/group_invite.py | 23 +++++ toxygen/groups/groups_service.py | 40 ++++++-- toxygen/middleware/callbacks.py | 2 +- toxygen/ui/group_invites_widgets.py | 120 +++++++++++++++++++++++ toxygen/ui/main_screen.py | 8 ++ toxygen/ui/views/gc_invite_item.ui | 71 ++++++++++++++ toxygen/ui/views/group_invites_screen.ui | 113 +++++++++++++++++++++ toxygen/ui/views/self_peer_screen.ui | 119 ++++++++++++++++++++++ toxygen/ui/widgets_factory.py | 7 +- toxygen/wrapper/tox.py | 1 + 11 files changed, 496 insertions(+), 13 deletions(-) create mode 100644 toxygen/groups/group_invite.py create mode 100644 toxygen/ui/group_invites_widgets.py create mode 100644 toxygen/ui/views/gc_invite_item.ui create mode 100644 toxygen/ui/views/group_invites_screen.ui create mode 100644 toxygen/ui/views/self_peer_screen.ui diff --git a/toxygen/app.py b/toxygen/app.py index 77864e7..a7fbaff 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -366,10 +366,11 @@ class App: widgets_factory = None widgets_factory_provider = Provider(lambda: widgets_factory) self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms, - widgets_factory_provider, self._profile) + widgets_factory_provider) widgets_factory = WidgetsFactory(self._settings, self._profile, self._profile_manager, self._contacts_manager, self._file_transfer_handler, self._smiley_loader, self._plugin_loader, - self._toxes, self._version, self._groups_service, history) + self._toxes, self._version, self._groups_service, history, + self._contacts_provider) self._tray = tray.init_tray(self._profile, self._settings, self._ms, self._toxes) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, self._profile, self._plugin_loader, self._file_transfer_handler, history, self._calls_manager, diff --git a/toxygen/groups/group_invite.py b/toxygen/groups/group_invite.py new file mode 100644 index 0000000..a2eed47 --- /dev/null +++ b/toxygen/groups/group_invite.py @@ -0,0 +1,23 @@ + + +class GroupInvite: + + def __init__(self, friend_public_key, chat_name, invite_data): + self._friend_public_key = friend_public_key + self._chat_name = chat_name + self._invite_data = invite_data[:] + + def get_friend_public_key(self): + return self._friend_public_key + + friend_public_key = property(get_friend_public_key) + + def get_chat_name(self): + return self._chat_name + + chat_name = property(get_chat_name) + + def get_invite_data(self): + return self._invite_data[:] + + invite_data = property(get_invite_data) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index f9cdfc9..76ecdaf 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -1,18 +1,19 @@ import common.tox_save as tox_save import utils.ui as util_ui from groups.peers_list import PeersListGenerator +from groups.group_invite import GroupInvite import wrapper.toxcore_enums_and_consts as constants class GroupsService(tox_save.ToxSave): - def __init__(self, tox, contacts_manager, contacts_provider, main_screen, widgets_factory_provider, profile): + def __init__(self, tox, contacts_manager, contacts_provider, main_screen, widgets_factory_provider): super().__init__(tox) self._contacts_manager = contacts_manager self._contacts_provider = contacts_provider self._peers_list_widget = main_screen.peers_list self._widgets_factory_provider = widgets_factory_provider - self._profile = profile + self._group_invites = [] self._peer_screen = None def set_tox(self, tox): @@ -37,10 +38,6 @@ class GroupsService(tox_save.ToxSave): group_number = self._tox.group_join(chat_id, password, nick, status) self._add_new_group_by_number(group_number) - def join_gc_via_invite(self, invite_data, friend_number, nick, status, password): - group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, status, password) - self._add_new_group_by_number(group_number) - # ----------------------------------------------------------------------------------------------------------------- # Groups reconnect and leaving # ----------------------------------------------------------------------------------------------------------------- @@ -71,9 +68,23 @@ class GroupsService(tox_save.ToxSave): def process_group_invite(self, friend_number, group_name, invite_data): friend = self._get_friend_by_number(friend_number) - text = util_ui.tr('Friend {} invites you to group "{}". Accept?') - if util_ui.question(text.format(friend.name, group_name), util_ui.tr('Group invite')): - self.join_gc_via_invite(invite_data, friend_number, self._profile.name, self._profile.status or 0, None) + invite = GroupInvite(friend.tox_id, group_name, invite_data) + self._group_invites.append(invite) + # TODO: notification on main screen + + def accept_group_invite(self, invite, name, status, password): + pk = invite.friend_public_key + friend = self._get_friend_by_public_key(pk) + self._join_gc_via_invite(invite.invite_data, friend.number, name, status, password) + self._delete_group_invite(invite) + + def decline_group_invite(self, invite): + self._delete_group_invite(invite) + + def get_group_invites(self): + return self._group_invites[:] + + group_invites = property(get_group_invites) # ----------------------------------------------------------------------------------------------------------------- # Group info methods @@ -153,6 +164,17 @@ class GroupsService(tox_save.ToxSave): def _get_friend_by_number(self, friend_number): return self._contacts_provider.get_friend_by_number(friend_number) + def _get_friend_by_public_key(self, public_key): + return self._contacts_provider.get_friend_by_public_key(public_key) + def _clear_peers_list(self, group): group.remove_all_peers_except_self() self.generate_peers_list() + + def _delete_group_invite(self, invite): + if invite in self._group_invites: + self._group_invites.remove(invite) + + def _join_gc_via_invite(self, invite_data, friend_number, nick, status, password): + group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, status, password) + self._add_new_group_by_number(group_number) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 3d40cc9..3f25e0c 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -438,7 +438,7 @@ def group_peer_name(contacts_provider, groups_service): def wrapped(tox, group_number, peer_id, name, length, user_data): group = contacts_provider.get_group_by_number(group_number) peer = group.get_peer_by_id(peer_id) - peer.name = str(name[:length]) + peer.name = str(name[:length], 'utf-8') invoke_in_main_thread(groups_service.generate_peers_list) return wrapped diff --git a/toxygen/ui/group_invites_widgets.py b/toxygen/ui/group_invites_widgets.py new file mode 100644 index 0000000..77403d9 --- /dev/null +++ b/toxygen/ui/group_invites_widgets.py @@ -0,0 +1,120 @@ +from PyQt5 import uic, QtWidgets +import utils.util as util +from ui.widgets import * + + +class GroupInviteItem(QtWidgets.QWidget): + + def __init__(self, parent, chat_name, avatar, friend_name): + super().__init__(parent) + uic.loadUi(util.get_views_path('gc_invite_item'), self) + + self.groupNameLabel.setText(chat_name) + self.friendNameLabel.setText(friend_name) + self.friendAvatarLabel.setPixmap(avatar) + + def is_selected(self): + return self.selectCheckBox.isChecked() + + def subscribe_checked_event(self, callback): + self.selectCheckBox.clicked.connect(callback) + + +class GroupInvitesScreen(CenteredWidget): + + def __init__(self, groups_service, profile, contacts_provider): + super().__init__() + self._groups_service = groups_service + self._profile = profile + self._contacts_provider = contacts_provider + + uic.loadUi(util.get_views_path('group_invites_screen'), self) + + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self._refresh_invites_list() + + self.nickLineEdit.setText(self._profile.name) + self.statusComboBox.setCurrentIndex(self._profile.status or 0) + + self.nickLineEdit.textChanged.connect(self._nick_changed) + self.acceptPushButton.clicked.connect(self._accept_invites) + self.declinePushButton.clicked.connect(self._decline_invites) + + self.invitesListWidget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + + self._update_buttons_state() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Group chat invites')) + self.noInvitesLabel.setText(util_ui.tr('No group invites found')) + self.acceptPushButton.setText(util_ui.tr('Accept')) + self.declinePushButton.setText(util_ui.tr('Decline')) + self.statusComboBox.addItem(util_ui.tr('Online')) + self.statusComboBox.addItem(util_ui.tr('Away')) + self.statusComboBox.addItem(util_ui.tr('Busy')) + self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat')) + self.passwordLineEdit.setPlaceholderText(util_ui.tr('Optional password')) + + def _get_friend(self, public_key): + return self._contacts_provider.get_friend_by_public_key(public_key) + + def _accept_invites(self): + nick = self.nickLineEdit.text() + password = self.passwordLineEdit.text() + status = self.statusComboBox.currentIndex() + + selected_invites = self._get_selected_invites() + for invite in selected_invites: + self._groups_service.accept_group_invite(invite, nick, status, password) + + self._refresh_invites_list() + + def _decline_invites(self): + selected_invites = self._get_selected_invites() + for invite in selected_invites: + self._groups_service.decline_group_invite(invite) + + self._refresh_invites_list() + + def _get_selected_invites(self): + all_invites = self._groups_service.get_group_invites() + selected = [] + items_count = len(all_invites) + for index in range(items_count): + list_item = self.invitesListWidget.item(index) + item_widget = self.invitesListWidget.itemWidget(list_item) + if item_widget.is_selected(): + selected.append(all_invites[index]) + + return selected + + def _refresh_invites_list(self): + self.invitesListWidget.clear() + invites = self._groups_service.get_group_invites() + for invite in invites: + self._create_invite_item(invite) + + def _create_invite_item(self, invite): + friend = self._get_friend(invite.friend_public_key) + item = GroupInviteItem(self.invitesListWidget, invite.chat_name, friend.get_pixmap(), friend.name) + item.subscribe_checked_event(self._item_selected) + elem = QtWidgets.QListWidgetItem() + elem.setSizeHint(QtCore.QSize(item.width(), item.height())) + self.invitesListWidget.addItem(elem) + self.invitesListWidget.setItemWidget(elem, item) + + def _item_selected(self): + self._update_buttons_state() + + def _nick_changed(self): + self._update_buttons_state() + + def _update_buttons_state(self): + nick = self.nickLineEdit.text() + selected_items = self._get_selected_invites() + self.acceptPushButton.setEnabled(bool(nick) and len(selected_items)) + self.declinePushButton.setEnabled(len(selected_items) > 0) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 1bc3612..d02c689 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -88,12 +88,14 @@ class MainWindow(QtWidgets.QMainWindow): self.lockApp = QtWidgets.QAction(window) self.createGC = QtWidgets.QAction(window) self.joinGC = QtWidgets.QAction(window) + self.gc_invites = QtWidgets.QAction(window) self.menuProfile.addAction(self.actionAdd_friend) self.menuProfile.addAction(self.actionSettings) self.menuProfile.addAction(self.lockApp) self.menuGC.addAction(self.createGC) self.menuGC.addAction(self.joinGC) + self.menuGC.addAction(self.gc_invites) self.menuSettings.addAction(self.actionPrivacy_settings) self.menuSettings.addAction(self.actionInterface_settings) self.menuSettings.addAction(self.actionNotifications) @@ -128,6 +130,7 @@ class MainWindow(QtWidgets.QMainWindow): self.lockApp.triggered.connect(self.lock_app) self.importPlugin.triggered.connect(self.import_plugin) self.reloadPlugins.triggered.connect(self.reload_plugins) + self.gc_invites.triggered.connect(self._open_gc_invites_list) def languageChange(self, *args, **kwargs): self.retranslateUi() @@ -149,6 +152,7 @@ class MainWindow(QtWidgets.QMainWindow): self.actionAdd_friend.setText(util_ui.tr("Add contact")) self.createGC.setText(util_ui.tr("Create group chat")) self.joinGC.setText(util_ui.tr("Join group chat")) + self.gc_invites.setText(util_ui.tr("Group invites")) self.actionprofilesettings.setText(util_ui.tr("Profile")) self.actionPrivacy_settings.setText(util_ui.tr("Privacy")) self.actionInterface_settings.setText(util_ui.tr("Interface")) @@ -733,3 +737,7 @@ class MainWindow(QtWidgets.QMainWindow): if self._should_show_group_peers_list: self._toggle_gc_peers_list() self.resizeEvent() + + def _open_gc_invites_list(self): + self._modal_window = self._widget_factory.create_group_invites_window() + self._modal_window.show() diff --git a/toxygen/ui/views/gc_invite_item.ui b/toxygen/ui/views/gc_invite_item.ui new file mode 100644 index 0000000..1048a55 --- /dev/null +++ b/toxygen/ui/views/gc_invite_item.ui @@ -0,0 +1,71 @@ + + + Form + + + + 0 + 0 + 600 + 150 + + + + Form + + + + + 250 + 30 + 300 + 21 + + + + TextLabel + + + + + + 250 + 70 + 300 + 21 + + + + TextLabel + + + + + + 140 + 30 + 60 + 60 + + + + TextLabel + + + + + + 40 + 50 + 16 + 23 + + + + + + + + + + diff --git a/toxygen/ui/views/group_invites_screen.ui b/toxygen/ui/views/group_invites_screen.ui new file mode 100644 index 0000000..183f801 --- /dev/null +++ b/toxygen/ui/views/group_invites_screen.ui @@ -0,0 +1,113 @@ + + + Form + + + + 0 + 0 + 600 + 500 + + + + + 600 + 500 + + + + + 600 + 500 + + + + Form + + + + + 0 + 150 + 600 + 25 + + + + TextLabel + + + Qt::AlignCenter + + + + + + 0 + 0 + 600 + 341 + + + + + + + 10 + 360 + 350 + 35 + + + + + + + 10 + 410 + 350 + 35 + + + + + + + 390 + 390 + 200 + 35 + + + + + + + 40 + 460 + 201 + 31 + + + + PushButton + + + + + + 360 + 460 + 201 + 31 + + + + PushButton + + + + + + diff --git a/toxygen/ui/views/self_peer_screen.ui b/toxygen/ui/views/self_peer_screen.ui new file mode 100644 index 0000000..38e1f88 --- /dev/null +++ b/toxygen/ui/views/self_peer_screen.ui @@ -0,0 +1,119 @@ + + + Form + + + + 0 + 0 + 600 + 500 + + + + + 600 + 500 + + + + + 600 + 500 + + + + Form + + + + + 50 + 120 + 67 + 20 + + + + TextLabel + + + + + + 50 + 250 + 500 + 50 + + + + PushButton + + + + + + 140 + 110 + 400 + 40 + + + + + + + 50 + 40 + 67 + 20 + + + + TextLabel + + + + + + 50 + 190 + 67 + 20 + + + + TextLabel + + + + + + 140 + 190 + 411 + 20 + + + + TextLabel + + + + + + 50 + 330 + 500 + 50 + + + + PushButton + + + + + + diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 8f341df..75b83b4 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -3,12 +3,13 @@ from ui.menu import * from ui.groups_widgets import * from ui.peer_screen import * from ui.self_peer_screen import * +from ui.group_invites_widgets import * class WidgetsFactory: def __init__(self, settings, profile, profile_manager, contacts_manager, file_transfer_handler, smiley_loader, - plugin_loader, toxes, version, groups_service, history): + plugin_loader, toxes, version, groups_service, history, contacts_provider): self._settings = settings self._profile = profile self._profile_manager = profile_manager @@ -20,6 +21,7 @@ class WidgetsFactory: self._version = version self._groups_service = groups_service self._history = history + self._contacts_provider = contacts_provider def create_screenshot_window(self, *args): return ScreenShotWindow(self._file_transfer_handler, self._contacts_manager, *args) @@ -77,3 +79,6 @@ class WidgetsFactory: def create_self_peer_screen_window(self, group): return SelfPeerScreen(self._contacts_manager, self._groups_service, group) + + def create_group_invites_window(self): + return GroupInvitesScreen(self._groups_service, self._profile, self._contacts_provider) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index cb2f25e..d705293 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1651,6 +1651,7 @@ class Tox: """ error = c_int() + name = bytes(name, 'utf-8') result = Tox.libtoxcore.tox_group_self_set_name(self._tox_pointer, group_number, name, len(name), byref(error)) return result From 603dfd40b55689d33d1393faaa4f1f7551ec6ff2 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 28 Jul 2018 18:16:26 +0300 Subject: [PATCH 104/217] tray notification on gc invite --- toxygen/middleware/callbacks.py | 56 ++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 3f25e0c..1716b49 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -363,15 +363,17 @@ def group_message(window, tray, tox, messenger, settings, profile): def wrapped(tox_link, group_number, peer_id, message_type, message, length, user_data): message = str(message[:length], 'utf-8') invoke_in_main_thread(messenger.new_group_message, group_number, message_type, message, peer_id) - if not window.isActiveWindow(): - bl = settings['notify_all_gc'] or profile.name in message - name = tox.group_peer_get_name(group_number, peer_id) - if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and (not settings.locked) and bl: - invoke_in_main_thread(tray_notification, name, message, tray, window) - if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: - sound_notification(SOUND_NOTIFICATION['MESSAGE']) - icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + if window.isActiveWindow(): + return + bl = settings['notify_all_gc'] or profile.name in message + name = tox.group_peer_get_name(group_number, peer_id) + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and (not settings.locked) and bl: + invoke_in_main_thread(tray_notification, name, message, tray, window) + if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['MESSAGE']) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + return wrapped @@ -382,25 +384,35 @@ def group_private_message(window, tray, tox, messenger, settings, profile): def wrapped(tox_link, group_number, peer_id, message_type, message, length, user_data): message = str(message[:length], 'utf-8') invoke_in_main_thread(messenger.new_group_private_message, group_number, message_type, message, peer_id) - if not window.isActiveWindow(): - bl = settings['notify_all_gc'] or profile.name in message - name = tox.group_peer_get_name(group_number, peer_id) - if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and (not settings.locked) and bl: - invoke_in_main_thread(tray_notification, name, message, tray, window) - if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: - sound_notification(SOUND_NOTIFICATION['MESSAGE']) - icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + if window.isActiveWindow(): + return + bl = settings['notify_all_gc'] or profile.name in message + name = tox.group_peer_get_name(group_number, peer_id) + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and (not settings.locked) and bl: + invoke_in_main_thread(tray_notification, name, message, tray, window) + if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['MESSAGE']) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) return wrapped -def group_invite(groups_service): +def group_invite(window, settings, tray, profile, groups_service, contacts_provider): def wrapped(tox, friend_number, invite_data, length, group_name, group_name_length, user_data): - group_name = bytes(group_name[:group_name_length]) + group_name = str(bytes(group_name[:group_name_length]), 'utf-8') invoke_in_main_thread(groups_service.process_group_invite, - friend_number, str(group_name, 'utf-8'), + friend_number, group_name, bytes(invite_data[:length])) + if window.isActiveWindow(): + return + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: + friend = contacts_provider.get_friend_by_number(friend_number) + title = util_ui.tr('New invite to group chat') + text = util_ui.tr('{} invites you to group {}').format(friend.name, group_name) + invoke_in_main_thread(tray_notification, title, text, tray, window) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) return wrapped @@ -552,7 +564,7 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, # gc callbacks tox.callback_group_message(group_message(main_window, tray, tox, messenger, settings, profile), 0) tox.callback_group_private_message(group_private_message(main_window, tray, tox, messenger, settings, profile), 0) - tox.callback_group_invite(group_invite(groups_service), 0) + tox.callback_group_invite(group_invite(main_window, settings, tray, profile, groups_service, contacts_provider), 0) tox.callback_group_self_join(group_self_join(contacts_provider, groups_service), 0) tox.callback_group_peer_join(group_peer_join(contacts_provider, groups_service), 0) tox.callback_group_peer_exit(group_peer_exit(contacts_provider, groups_service, contacts_manager), 0) From 10a77960dc3a50ecf789916cf20e6ea41d68d999 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 29 Jul 2018 00:06:33 +0300 Subject: [PATCH 105/217] friends column converted to .ui. added gc invites button --- toxygen/contacts/contacts_manager.py | 2 +- toxygen/contacts/profile.py | 2 +- toxygen/groups/groups_service.py | 10 +- toxygen/styles/dark_style.qss | 10 +- toxygen/styles/style.qss | 8 +- toxygen/ui/main_screen.py | 146 ++++++++++++--------------- toxygen/ui/views/gc_invite_item.ui | 2 +- 7 files changed, 94 insertions(+), 86 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 10f50ca..ca9e00d 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -25,7 +25,7 @@ class ContactsManager(ToxSave): self._sorting = settings['sorting'] self._filter_string = '' self._friend_item_height = 40 if settings['compact_mode'] else 70 - screen.online_contacts.setCurrentIndex(int(self._sorting)) + #screen.online_contacts.setCurrentIndex(int(self._sorting)) self._history = history self._load_contacts() diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index c47eca2..ed7cd83 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -17,7 +17,7 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave): profile_manager, tox.self_get_name(), tox.self_get_status_message(), - screen.user_info, + screen, tox.self_get_address()) tox_save.ToxSave.__init__(self, tox) self._screen = screen diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 76ecdaf..5aefe5a 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -11,6 +11,7 @@ class GroupsService(tox_save.ToxSave): super().__init__(tox) self._contacts_manager = contacts_manager self._contacts_provider = contacts_provider + self._main_screen = main_screen self._peers_list_widget = main_screen.peers_list self._widgets_factory_provider = widgets_factory_provider self._group_invites = [] @@ -70,22 +71,29 @@ class GroupsService(tox_save.ToxSave): friend = self._get_friend_by_number(friend_number) invite = GroupInvite(friend.tox_id, group_name, invite_data) self._group_invites.append(invite) - # TODO: notification on main screen + self._main_screen.update_gc_invites_button_state() def accept_group_invite(self, invite, name, status, password): pk = invite.friend_public_key friend = self._get_friend_by_public_key(pk) self._join_gc_via_invite(invite.invite_data, friend.number, name, status, password) self._delete_group_invite(invite) + self._main_screen.update_gc_invites_button_state() def decline_group_invite(self, invite): self._delete_group_invite(invite) + self._main_screen.update_gc_invites_button_state() def get_group_invites(self): return self._group_invites[:] group_invites = property(get_group_invites) + def get_group_invites_count(self): + return len(self._group_invites) + + group_invites_count = property(get_group_invites_count) + # ----------------------------------------------------------------------------------------------------------------- # Group info methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/styles/dark_style.qss b/toxygen/styles/dark_style.qss index 3b2837b..ece5ec3 100644 --- a/toxygen/styles/dark_style.qss +++ b/toxygen/styles/dark_style.qss @@ -1253,7 +1253,7 @@ MessageBrowser background-color: #1E90FF; } -#friends_list:item:selected +#friendsListWidget:item:selected { background-color: #333333; } @@ -1277,7 +1277,7 @@ QListWidget > QLabel color: #A9A9A9; } -#contact_name +#searchLineEdit { padding-left: 22px; } @@ -1327,3 +1327,9 @@ ClickableLabel:hover { color: #BC1C1C; } + +#groupInvitesPushButton +{ + background-color: #009c00; +} + diff --git a/toxygen/styles/style.qss b/toxygen/styles/style.qss index 956ee63..ff9f614 100644 --- a/toxygen/styles/style.qss +++ b/toxygen/styles/style.qss @@ -1,4 +1,4 @@ -#contact_name +#searchLineEdit { padding-left: 22px; } @@ -32,3 +32,9 @@ MessageEdit { color: #BC1C1C; } + +#groupInvitesPushButton +{ + background-color: #009c00; +} + diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index d02c689..43a6b19 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -3,6 +3,7 @@ from ui.widgets import MultilineEdit, ComboBox from ui.main_screen_widgets import * import utils.util as util import utils.ui as util_ui +from PyQt5 import uic class MainWindow(QtWidgets.QMainWindow): @@ -38,6 +39,8 @@ class MainWindow(QtWidgets.QMainWindow): self._contacts_manager.active_contact_changed.add_callback(self._new_contact_selected) self.messageEdit.set_messenger(messenger) + self.update_gc_invites_button_state() + def show(self): super().show() self._contacts_manager.update() @@ -163,19 +166,21 @@ class MainWindow(QtWidgets.QMainWindow): self.audioSettings.setText(util_ui.tr("Audio")) self.videoSettings.setText(util_ui.tr("Video")) self.updateSettings.setText(util_ui.tr("Updates")) - self.contact_name.setPlaceholderText(util_ui.tr("Search")) + + self.searchLineEdit.setPlaceholderText(util_ui.tr("Search")) self.sendMessageButton.setToolTip(util_ui.tr("Send message")) self.callButton.setToolTip(util_ui.tr("Start audio call with friend")) - self.online_contacts.clear() - self.online_contacts.addItem(util_ui.tr("All")) - self.online_contacts.addItem(util_ui.tr("Online")) - self.online_contacts.addItem(util_ui.tr("Online first")) - self.online_contacts.addItem(util_ui.tr("Name")) - self.online_contacts.addItem(util_ui.tr("Online and by name")) - self.online_contacts.addItem(util_ui.tr("Online first and by name")) + self.contactsFilterComboBox.clear() + self.contactsFilterComboBox.addItem(util_ui.tr("All")) + self.contactsFilterComboBox.addItem(util_ui.tr("Online")) + self.contactsFilterComboBox.addItem(util_ui.tr("Online first")) + self.contactsFilterComboBox.addItem(util_ui.tr("Name")) + self.contactsFilterComboBox.addItem(util_ui.tr("Online and by name")) + self.contactsFilterComboBox.addItem(util_ui.tr("Online first and by name")) + ind = self._settings['sorting'] d = {0: 0, 1: 1, 2: 2, 3: 4, 4: 3, 1 | 4: 4, 2 | 4: 5} - self.online_contacts.setCurrentIndex(d[ind]) + self.contactsFilterComboBox.setCurrentIndex(d[ind]) self.importPlugin.setText(util_ui.tr("Import plugin")) self.reloadPlugins.setText(util_ui.tr("Reload plugins")) @@ -208,54 +213,48 @@ class MainWindow(QtWidgets.QMainWindow): QtCore.QMetaObject.connectSlotsByName(Form) - def setup_left_center_menu(self, Form): - Form.resize(270, 25) - self.search_label = QtWidgets.QLabel(Form) - self.search_label.setGeometry(QtCore.QRect(3, 2, 20, 20)) + def setup_left_column(self, left_column): + uic.loadUi(util.get_views_path('ms_left_column'), left_column) + pixmap = QtGui.QPixmap() pixmap.load(util.join_path(util.get_images_directory(), 'search.png')) - self.search_label.setScaledContents(False) - self.search_label.setPixmap(pixmap) + left_column.searchLabel.setPixmap(pixmap) - self.contact_name = LineEdit(Form) - self.contact_name.setObjectName('contact_name') - self.contact_name.setGeometry(QtCore.QRect(0, 0, 150, 25)) - self.contact_name.textChanged.connect(self.filtering) - - self.online_contacts = ComboBox(Form) - self.online_contacts.setGeometry(QtCore.QRect(150, 0, 120, 25)) - self.online_contacts.activated[int].connect(lambda x: self.filtering()) - self.search_label.raise_() - - QtCore.QMetaObject.connectSlotsByName(Form) - - def setup_left_top(self, Form): - Form.setCursor(QtCore.Qt.PointingHandCursor) - Form.setMinimumSize(QtCore.QSize(270, 75)) - Form.setMaximumSize(QtCore.QSize(270, 75)) - Form.setBaseSize(QtCore.QSize(270, 75)) - self.avatar_label = Form.avatar_label = QtWidgets.QLabel(Form) - self.avatar_label.setGeometry(QtCore.QRect(5, 5, 64, 64)) - self.avatar_label.setScaledContents(False) - self.avatar_label.setAlignment(QtCore.Qt.AlignCenter) - self.name = Form.name = DataLabel(Form) - Form.name.setGeometry(QtCore.QRect(75, 15, 150, 25)) + self.name = DataLabel(left_column) + self.name.setGeometry(QtCore.QRect(75, 15, 150, 25)) font = QtGui.QFont() font.setFamily(self._settings['font']) font.setPointSize(14) font.setBold(True) - Form.name.setFont(font) - self.status_message = Form.status_message = DataLabel(Form) - Form.status_message.setGeometry(QtCore.QRect(75, 35, 170, 25)) - font.setPointSize(12) - font.setBold(False) - Form.status_message.setFont(font) - self.connection_status = Form.connection_status = StatusCircle(Form) - Form.connection_status.setGeometry(QtCore.QRect(230, 10, 32, 32)) + self.name.setFont(font) + + self.status_message = DataLabel(left_column) + self.status_message.setGeometry(QtCore.QRect(75, 35, 170, 25)) + + self.connection_status = StatusCircle(left_column) + self.connection_status.setGeometry(QtCore.QRect(230, 10, 32, 32)) + + left_column.contactsFilterComboBox.activated[int].connect(lambda x: self._filtering()) + + self.avatar_label = left_column.avatarLabel + self.searchLineEdit = left_column.searchLineEdit + self.contactsFilterComboBox = left_column.contactsFilterComboBox + + self.groupInvitesPushButton = left_column.groupInvitesPushButton + + self.groupInvitesPushButton.clicked.connect(self._open_gc_invites_list) self.avatar_label.mouseReleaseEvent = self.profile_settings self.status_message.mouseReleaseEvent = self.profile_settings self.name.mouseReleaseEvent = self.profile_settings - self.connection_status.raise_() + + self.friends_list = left_column.friendsListWidget + self.friends_list.clicked.connect(self._friend_click) + self.friends_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.friends_list.customContextMenuRequested.connect(self._friend_right_click) + self.friends_list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) + self.friends_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.friends_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.friends_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) def setup_right_top(self, Form): Form.resize(650, 75) @@ -303,18 +302,6 @@ class MainWindow(QtWidgets.QMainWindow): self.typing.setVisible(False) QtCore.QMetaObject.connectSlotsByName(Form) - def setup_left_center(self, widget): - self.friends_list = QtWidgets.QListWidget(widget) - self.friends_list.setObjectName("friends_list") - self.friends_list.setGeometry(0, 0, 270, 310) - self.friends_list.clicked.connect(self.friend_click) - self.friends_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - self.friends_list.customContextMenuRequested.connect(self.friend_right_click) - self.friends_list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) - self.friends_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) - self.friends_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.friends_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) - def setup_right_center(self, widget): self.messages = QtWidgets.QListWidget(widget) self.messages.setGeometry(0, 0, 620, 310) @@ -351,35 +338,27 @@ class MainWindow(QtWidgets.QMainWindow): menu = QtWidgets.QWidget() main = QtWidgets.QWidget() grid = QtWidgets.QGridLayout() - search = QtWidgets.QWidget() - name = QtWidgets.QWidget() info = QtWidgets.QWidget() - main_list = QtWidgets.QWidget() + left_column = QtWidgets.QWidget() messages = QtWidgets.QWidget() message_buttons = QtWidgets.QWidget() - self.setup_left_center_menu(search) - self.setup_left_top(name) self.setup_right_center(messages) self.setup_right_top(info) self.setup_right_bottom(message_buttons) - self.setup_left_center(main_list) + self.setup_left_column(left_column) self.setup_menu(menu) if not s['mirror_mode']: - grid.addWidget(search, 2, 0) - grid.addWidget(name, 1, 0) + grid.addWidget(left_column, 1, 0, 4, 1) grid.addWidget(messages, 2, 1, 2, 1) grid.addWidget(info, 1, 1) grid.addWidget(message_buttons, 4, 1) - grid.addWidget(main_list, 3, 0, 2, 1) grid.setColumnMinimumWidth(1, 500) grid.setColumnMinimumWidth(0, 270) else: - grid.addWidget(search, 2, 1) - grid.addWidget(name, 1, 1) + grid.addWidget(left_column, 1, 1, 4, 1) grid.addWidget(messages, 2, 0, 2, 1) grid.addWidget(info, 1, 0) grid.addWidget(message_buttons, 4, 0) - grid.addWidget(main_list, 3, 1, 2, 1) grid.setColumnMinimumWidth(0, 500) grid.setColumnMinimumWidth(1, 270) @@ -396,7 +375,6 @@ class MainWindow(QtWidgets.QMainWindow): main.setLayout(grid) self.setCentralWidget(main) self.messageEdit.setFocus() - self.user_info = name self.friend_info = info self.retranslateUi() @@ -428,7 +406,10 @@ class MainWindow(QtWidgets.QMainWindow): else: self.messages.setGeometry(0, 0, width * 3 // 4, self.height() - 155) self.peers_list.setGeometry(width * 3 // 4, 0, width - width * 3 // 4, self.height() - 155) - self.friends_list.setGeometry(0, 0, 270, self.height() - 125) + + invites_button_visible = self.groupInvitesPushButton.isVisible() + self.friends_list.setGeometry(0, 125 if invites_button_visible else 100, + 270, self.height() - 150 if invites_button_visible else self.height() - 125) self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 10, 50, 50)) self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 10, 50, 50)) @@ -617,7 +598,7 @@ class MainWindow(QtWidgets.QMainWindow): # Functions which called when user open context menu in friends list # ----------------------------------------------------------------------------------------------------------------- - def friend_right_click(self, pos): + def _friend_right_click(self, pos): item = self.friends_list.itemAt(pos) number = self.friends_list.indexFromItem(item).row() contact = self._contacts_manager.get_contact(number) @@ -696,23 +677,23 @@ class MainWindow(QtWidgets.QMainWindow): # Functions which called when user click somewhere else # ----------------------------------------------------------------------------------------------------------------- - def friend_click(self, index): + def _friend_click(self, index): num = index.row() self._contacts_manager.active_contact = num self.groupMenuButton.setVisible(not self._contacts_manager.is_active_a_friend()) def mouseReleaseEvent(self, event): pos = self.connection_status.pos() - x, y = pos.x() + self.user_info.pos().x(), pos.y() + self.user_info.pos().y() + x, y = pos.x(), pos.y() + 25 if (x < event.x() < x + 32) and (y < event.y() < y + 32): self._profile.change_status() else: super().mouseReleaseEvent(event) - def filtering(self): - ind = self.online_contacts.currentIndex() + def _filtering(self): + ind = self.contactsFilterComboBox.currentIndex() d = {0: 0, 1: 1, 2: 2, 3: 4, 4: 1 | 4, 5: 2 | 4} - self._contacts_manager.filtration_and_sorting(d[ind], self.contact_name.text()) + self._contacts_manager.filtration_and_sorting(d[ind], self.searchLineEdit.text()) def show_search_field(self): if hasattr(self, 'search_field') and self.search_field.isVisible(): @@ -741,3 +722,10 @@ class MainWindow(QtWidgets.QMainWindow): def _open_gc_invites_list(self): self._modal_window = self._widget_factory.create_group_invites_window() self._modal_window.show() + + def update_gc_invites_button_state(self): + invites_count = self._groups_service.group_invites_count + self.groupInvitesPushButton.setVisible(invites_count > 0) + text = util_ui.tr('{} new invites to group chats').format(invites_count) + self.groupInvitesPushButton.setText(text) + self.resizeEvent() diff --git a/toxygen/ui/views/gc_invite_item.ui b/toxygen/ui/views/gc_invite_item.ui index 1048a55..dc077a7 100644 --- a/toxygen/ui/views/gc_invite_item.ui +++ b/toxygen/ui/views/gc_invite_item.ui @@ -57,7 +57,7 @@ 40 50 - 16 + 23 23 From f38df2494798a414507b0d7f23bec77186036c92 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 29 Jul 2018 11:16:03 +0300 Subject: [PATCH 106/217] filtering fixed --- toxygen/contacts/contacts_manager.py | 71 +++++++++++---------- toxygen/ui/main_screen.py | 16 ++--- toxygen/ui/views/gc_invite_item.ui | 2 +- toxygen/ui/views/ms_left_column.ui | 94 ++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+), 44 deletions(-) create mode 100644 toxygen/ui/views/ms_left_column.ui diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index ca9e00d..3499f42 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -25,7 +25,7 @@ class ContactsManager(ToxSave): self._sorting = settings['sorting'] self._filter_string = '' self._friend_item_height = 40 if settings['compact_mode'] else 70 - #screen.online_contacts.setCurrentIndex(int(self._sorting)) + screen.contacts_filter.setCurrentIndex(int(self._sorting)) self._history = history self._load_contacts() @@ -163,50 +163,55 @@ class ContactsManager(ToxSave): def filtration_and_sorting(self, sorting=0, filter_str=''): """ Filtration of friends list - :param sorting: 0 - no sorting, 1 - online only, 2 - online first, 4 - by name + :param sorting: 0 - no sorting, 1 - online only, 2 - online first, 3 - by name, + 4 - online and by name, 5 - online first and by name :param filter_str: show contacts which name contains this substring """ - # TODO: simplify? filter_str = filter_str.lower() - number = self.get_active_number() - is_friend = self.is_active_a_friend() - if sorting > 1: - if sorting & 2: - self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True) - if sorting & 4: - if not sorting & 2: - self._contacts = sorted(self._contacts, key=lambda x: x.name.lower()) - else: # save results of prev sorting - online_friends = filter(lambda x: x.status is not None, self._contacts) - count = len(list(online_friends)) - part1 = self._contacts[:count] - part2 = self._contacts[count:] - part1 = sorted(part1, key=lambda x: x.name.lower()) - part2 = sorted(part2, key=lambda x: x.name.lower()) - self._contacts = part1 + part2 - else: # sort by number - online_friends = filter(lambda x: x.status is not None, self._contacts) - count = len(list(online_friends)) - part1 = self._contacts[:count] - part2 = self._contacts[count:] - part1 = sorted(part1, key=lambda x: x.number) - part2 = sorted(part2, key=lambda x: x.number) - self._contacts = part1 + part2 - for index, contact in enumerate(self._contacts): - list_item = self._screen.friends_list.item(index) - item_widget = self._screen.friends_list.itemWidget(list_item) - contact.set_widget(item_widget) + contact = self.get_curr_contact() + + if sorting > 5 or sorting < 0: + sorting = 0 + + if sorting in (1, 2, 4, 5): # online first + self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True) + sort_by_name = sorting in (4, 5) + # save results of previous sorting + online_friends = filter(lambda x: x.status is not None, self._contacts) + online_friends_count = len(list(online_friends)) + part1 = self._contacts[:online_friends_count] + part2 = self._contacts[online_friends_count:] + key_lambda = lambda x: x.name.lower() if sort_by_name else x.number + part1 = sorted(part1, key=key_lambda) + part2 = sorted(part2, key=key_lambda) + self._contacts = part1 + part2 + elif sorting == 0: + self._contacts = sorted(self._contacts, key=lambda x: x.number) + else: + self._contacts = sorted(self._contacts, key=lambda x: x.name.lower()) + + # change item widgets + for index, contact in enumerate(self._contacts): + list_item = self._screen.friends_list.item(index) + item_widget = self._screen.friends_list.itemWidget(list_item) + contact.set_widget(item_widget) + for index, friend in enumerate(self._contacts): - friend.visibility = (friend.status is not None or not (sorting & 1)) and (filter_str in friend.name.lower()) + filtered_by_name = filter_str in friend.name.lower() + friend.visibility = (friend.status is not None or sorting not in (1, 4)) and filtered_by_name + # show friend even if it's hidden when there any unread messages/actions friend.visibility = friend.visibility or friend.messages or friend.actions if friend.visibility: self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) else: self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) + # save soring results self._sorting, self._filter_string = sorting, filter_str self._settings['sorting'] = self._sorting self._settings.save() - self.set_active_by_number_and_type(number, is_friend) + # update active contact + index = self._contacts.index(contact) + self.set_active(index) def update_filtration(self): """ diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 43a6b19..d57667b 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -166,6 +166,8 @@ class MainWindow(QtWidgets.QMainWindow): self.audioSettings.setText(util_ui.tr("Audio")) self.videoSettings.setText(util_ui.tr("Video")) self.updateSettings.setText(util_ui.tr("Updates")) + self.importPlugin.setText(util_ui.tr("Import plugin")) + self.reloadPlugins.setText(util_ui.tr("Reload plugins")) self.searchLineEdit.setPlaceholderText(util_ui.tr("Search")) self.sendMessageButton.setToolTip(util_ui.tr("Send message")) @@ -178,12 +180,6 @@ class MainWindow(QtWidgets.QMainWindow): self.contactsFilterComboBox.addItem(util_ui.tr("Online and by name")) self.contactsFilterComboBox.addItem(util_ui.tr("Online first and by name")) - ind = self._settings['sorting'] - d = {0: 0, 1: 1, 2: 2, 3: 4, 4: 3, 1 | 4: 4, 2 | 4: 5} - self.contactsFilterComboBox.setCurrentIndex(d[ind]) - self.importPlugin.setText(util_ui.tr("Import plugin")) - self.reloadPlugins.setText(util_ui.tr("Reload plugins")) - def setup_right_bottom(self, Form): Form.resize(650, 60) self.messageEdit = MessageArea(Form, self) @@ -238,7 +234,7 @@ class MainWindow(QtWidgets.QMainWindow): self.avatar_label = left_column.avatarLabel self.searchLineEdit = left_column.searchLineEdit - self.contactsFilterComboBox = left_column.contactsFilterComboBox + self.contacts_filter = self.contactsFilterComboBox = left_column.contactsFilterComboBox self.groupInvitesPushButton = left_column.groupInvitesPushButton @@ -691,9 +687,9 @@ class MainWindow(QtWidgets.QMainWindow): super().mouseReleaseEvent(event) def _filtering(self): - ind = self.contactsFilterComboBox.currentIndex() - d = {0: 0, 1: 1, 2: 2, 3: 4, 4: 1 | 4, 5: 2 | 4} - self._contacts_manager.filtration_and_sorting(d[ind], self.searchLineEdit.text()) + index = self.contactsFilterComboBox.currentIndex() + search_text = self.searchLineEdit.text() + self._contacts_manager.filtration_and_sorting(index, search_text) def show_search_field(self): if hasattr(self, 'search_field') and self.search_field.isVisible(): diff --git a/toxygen/ui/views/gc_invite_item.ui b/toxygen/ui/views/gc_invite_item.ui index dc077a7..6eddbeb 100644 --- a/toxygen/ui/views/gc_invite_item.ui +++ b/toxygen/ui/views/gc_invite_item.ui @@ -57,7 +57,7 @@ 40 50 - 23 + 20 23 diff --git a/toxygen/ui/views/ms_left_column.ui b/toxygen/ui/views/ms_left_column.ui new file mode 100644 index 0000000..ffbff71 --- /dev/null +++ b/toxygen/ui/views/ms_left_column.ui @@ -0,0 +1,94 @@ + + + Form + + + + 0 + 0 + 270 + 500 + + + + PointingHandCursor + + + Form + + + + + 5 + 5 + 64 + 64 + + + + PointingHandCursor + + + TextLabel + + + + + + 0 + 75 + 150 + 25 + + + + + + + 150 + 75 + 120 + 25 + + + + + + + 0 + 77 + 20 + 20 + + + + TextLabel + + + + + + 0 + 100 + 270 + 400 + + + + + + + 0 + 100 + 270 + 30 + + + + PushButton + + + + + + From 250551e7527b16aee29ad63457f2337852606270 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 29 Jul 2018 13:36:16 +0300 Subject: [PATCH 107/217] fixed group numbers restoring. contact selection fixed --- toxygen/app.py | 1 + toxygen/contacts/contact.py | 28 ++++++++++++++++------------ toxygen/contacts/contacts_manager.py | 16 +++++++--------- toxygen/groups/groups_service.py | 1 - toxygen/middleware/callbacks.py | 5 +++-- toxygen/ui/main_screen.py | 9 ++++++--- 6 files changed, 33 insertions(+), 27 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index a7fbaff..80c26ea 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -327,6 +327,7 @@ class App: self._calls_manager.set_toxav(self._tox.AV) self._contacts_manager.update_friends_numbers() self._contacts_manager.update_groups_lists() + self._contacts_manager.update_groups_numbers() self._init_callbacks() diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index fb124dd..418ac15 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -43,18 +43,22 @@ class Contact(basecontact.BaseContact): """ :param first_time: friend became active, load first part of messages """ - if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')): - return - if self._message_getter is None: - return - data = list(self._message_getter.get(PAGE_SIZE)) - if data is not None and len(data): - data.reverse() - else: - return - data = list(map(lambda p: self._get_text_message(p), data)) - self._corr = data + self._corr - self._history_loaded = True + try: + if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')): + return + if self._message_getter is None: + return + data = list(self._message_getter.get(PAGE_SIZE)) + if data is not None and len(data): + data.reverse() + else: + return + data = list(map(lambda p: self._get_text_message(p), data)) + self._corr = data + self._corr + except: + pass + finally: + self._history_loaded = True def load_all_corr(self): """ diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 3499f42..0800655 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -139,13 +139,6 @@ class ContactsManager(ToxSave): active_contact_changed = property(get_active_contact_changed) - def set_active_by_number_and_type(self, number, is_friend): # TODO: by id - for i in range(len(self._contacts)): - c = self._contacts[i] - if c.number == number and (type(c) is Friend == is_friend): - self._active_contact = i - break - def update(self): if self._active_contact + 1: self.set_active(self._active_contact) @@ -309,6 +302,7 @@ class ContactsManager(ToxSave): """ self._tox.friend_add_norequest(tox_id) self._add_friend(tox_id) + self.update_filtration() def block_user(self, tox_id): """ @@ -351,6 +345,7 @@ class ContactsManager(ToxSave): self._contacts.append(group) group.reset_avatar(self._settings['identicons']) self._save_profile() + self.update_filtration() def delete_group(self, group_number): group = self.get_group_by_number(group_number) @@ -464,7 +459,7 @@ class ContactsManager(ToxSave): self._load_friends() self._load_groups() if len(self._contacts): - self.set_active(0) + self._screen.select_contact_row(0) for contact in filter(lambda c: not c.has_avatar(), self._contacts): contact.reset_avatar(self._settings['identicons']) self.update_filtration() @@ -542,7 +537,10 @@ class ContactsManager(ToxSave): def _delete_contact(self, num): if num == self._active_contact: # active friend was deleted - self.set_active(0 if len(self._contacts) > 1 else -1) + if len(self._contacts) == 0: + self.set_active(-1) + else: + self._screen.select_contact_row(0) self._contact_provider.remove_contact_from_cache(self._contacts[num].tox_id) del self._contacts[num] self._screen.friends_list.takeItem(num) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 5aefe5a..c062685 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -46,7 +46,6 @@ class GroupsService(tox_save.ToxSave): def leave_group(self, group_number): self._tox.group_leave(group_number) self._contacts_manager.delete_group(group_number) - self._contacts_manager.update_groups_numbers() def disconnect_from_group(self, group_number): self._tox.group_disconnect(group_number) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 1716b49..f8300ff 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -417,11 +417,12 @@ def group_invite(window, settings, tray, profile, groups_service, contacts_provi return wrapped -def group_self_join(contacts_provider, groups_service): +def group_self_join(contacts_provider, contacts_manager, groups_service): def wrapped(tox, group_number, user_data): group = contacts_provider.get_group_by_number(group_number) invoke_in_main_thread(group.set_status, TOX_USER_STATUS['NONE']) invoke_in_main_thread(groups_service.update_group_info, group) + invoke_in_main_thread(contacts_manager.update_filtration) return wrapped @@ -565,7 +566,7 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_group_message(group_message(main_window, tray, tox, messenger, settings, profile), 0) tox.callback_group_private_message(group_private_message(main_window, tray, tox, messenger, settings, profile), 0) tox.callback_group_invite(group_invite(main_window, settings, tray, profile, groups_service, contacts_provider), 0) - tox.callback_group_self_join(group_self_join(contacts_provider, groups_service), 0) + tox.callback_group_self_join(group_self_join(contacts_provider, contacts_manager, groups_service), 0) tox.callback_group_peer_join(group_peer_join(contacts_provider, groups_service), 0) tox.callback_group_peer_exit(group_peer_exit(contacts_provider, groups_service, contacts_manager), 0) tox.callback_group_peer_name(group_peer_name(contacts_provider, groups_service), 0) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index d57667b..54b6804 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -244,7 +244,7 @@ class MainWindow(QtWidgets.QMainWindow): self.name.mouseReleaseEvent = self.profile_settings self.friends_list = left_column.friendsListWidget - self.friends_list.clicked.connect(self._friend_click) + self.friends_list.itemSelectionChanged.connect(self._selected_contact_changed) self.friends_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.friends_list.customContextMenuRequested.connect(self._friend_right_click) self.friends_list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) @@ -669,12 +669,15 @@ class MainWindow(QtWidgets.QMainWindow): def invite_friend_to_gc(self, friend_number, group_number): self._contacts_manager.invite_friend(friend_number, group_number) + def select_contact_row(self, row_index): + self.friends_list.setCurrentRow(row_index) + # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user click somewhere else # ----------------------------------------------------------------------------------------------------------------- - def _friend_click(self, index): - num = index.row() + def _selected_contact_changed(self): + num = self.friends_list.currentRow() self._contacts_manager.active_contact = num self.groupMenuButton.setVisible(not self._contacts_manager.is_active_a_friend()) From c66dcb0ca2c348bd9e20bbdd056bdcef5795a9cd Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 29 Jul 2018 16:11:34 +0300 Subject: [PATCH 108/217] contact selection fixes --- toxygen/contacts/contacts_manager.py | 19 +++++++++++++------ toxygen/ui/main_screen.py | 5 ++++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 0800655..9b4ad93 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -342,9 +342,11 @@ class ContactsManager(ToxSave): def add_group(self, group_number): group = self._contact_provider.get_group_by_number(group_number) + index = len(self._contacts) self._contacts.append(group) group.reset_avatar(self._settings['identicons']) self._save_profile() + self.set_active(index) self.update_filtration() def delete_group(self, group_number): @@ -402,6 +404,7 @@ class ContactsManager(ToxSave): self._tox.friend_add(tox_id, message.encode('utf-8')) tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] self._add_friend(tox_id) + self.update_filtration() self.save_profile() return True except Exception as ex: # wrong data @@ -459,7 +462,7 @@ class ContactsManager(ToxSave): self._load_friends() self._load_groups() if len(self._contacts): - self._screen.select_contact_row(0) + self.set_active(0) for contact in filter(lambda c: not c.has_avatar(), self._contacts): contact.reset_avatar(self._settings['identicons']) self.update_filtration() @@ -512,10 +515,12 @@ class ContactsManager(ToxSave): def _add_friend(self, tox_id): self._history.add_friend_to_db(tox_id) friend = self._contact_provider.get_friend_by_public_key(tox_id) + index = len(self._contacts) self._contacts.append(friend) if not friend.has_avatar(): friend.reset_avatar(self._settings['identicons']) self._save_profile() + self.set_active(index) def _save_profile(self): data = self._tox.get_savedata() @@ -536,12 +541,14 @@ class ContactsManager(ToxSave): remove(avatar_path) def _delete_contact(self, num): - if num == self._active_contact: # active friend was deleted - if len(self._contacts) == 0: - self.set_active(-1) - else: - self._screen.select_contact_row(0) + if len(self._contacts) == 1: + self.set_active(-1) + else: + self.set_active(0) + self._contact_provider.remove_contact_from_cache(self._contacts[num].tox_id) del self._contacts[num] self._screen.friends_list.takeItem(num) self._save_profile() + + self.update_filtration() diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 54b6804..edf7ac6 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -713,9 +713,12 @@ class MainWindow(QtWidgets.QMainWindow): if self._should_show_group_peers_list: self._groups_service.generate_peers_list() - def _new_contact_selected(self, contact): + def _new_contact_selected(self, _): if self._should_show_group_peers_list: self._toggle_gc_peers_list() + index = self.friends_list.currentRow() + if self._contacts_manager.active_contact != index: + self.friends_list.setCurrentRow(self._contacts_manager.active_contact) self.resizeEvent() def _open_gc_invites_list(self): From dd323e3cbb41861d83abada047c106c6564676e6 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 29 Jul 2018 21:26:53 +0300 Subject: [PATCH 109/217] minor fixes for group invites screen --- toxygen/ui/group_invites_widgets.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/toxygen/ui/group_invites_widgets.py b/toxygen/ui/group_invites_widgets.py index 77403d9..d35aca1 100644 --- a/toxygen/ui/group_invites_widgets.py +++ b/toxygen/ui/group_invites_widgets.py @@ -45,6 +45,7 @@ class GroupInvitesScreen(CenteredWidget): self.declinePushButton.clicked.connect(self._decline_invites) self.invitesListWidget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.invitesListWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self._update_buttons_state() @@ -72,6 +73,7 @@ class GroupInvitesScreen(CenteredWidget): self._groups_service.accept_group_invite(invite, nick, status, password) self._refresh_invites_list() + self._close_window_if_needed() def _decline_invites(self): selected_invites = self._get_selected_invites() @@ -79,6 +81,7 @@ class GroupInvitesScreen(CenteredWidget): self._groups_service.decline_group_invite(invite) self._refresh_invites_list() + self._close_window_if_needed() def _get_selected_invites(self): all_invites = self._groups_service.get_group_invites() @@ -118,3 +121,7 @@ class GroupInvitesScreen(CenteredWidget): selected_items = self._get_selected_invites() self.acceptPushButton.setEnabled(bool(nick) and len(selected_items)) self.declinePushButton.setEnabled(len(selected_items) > 0) + + def _close_window_if_needed(self): + if self._groups_service.group_invites_count == 0: + self.close() From c7a83055b15c9c2e963be6d57d07f0098abd90fc Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 1 Aug 2018 00:47:57 +0300 Subject: [PATCH 110/217] various fixes --- toxygen/contacts/contacts_manager.py | 5 +++-- toxygen/middleware/callbacks.py | 2 +- toxygen/ui/main_screen.py | 5 +++-- toxygen/wrapper/tox.py | 8 +++++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 9b4ad93..c1d0b10 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -203,8 +203,9 @@ class ContactsManager(ToxSave): self._settings['sorting'] = self._sorting self._settings.save() # update active contact - index = self._contacts.index(contact) - self.set_active(index) + if contact is not None: + index = self._contacts.index(contact) + self.set_active(index) def update_filtration(self): """ diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index f8300ff..36bb960 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -409,7 +409,7 @@ def group_invite(window, settings, tray, profile, groups_service, contacts_provi if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: friend = contacts_provider.get_friend_by_number(friend_number) title = util_ui.tr('New invite to group chat') - text = util_ui.tr('{} invites you to group {}').format(friend.name, group_name) + text = util_ui.tr('{} invites you to group "{}"').format(friend.name, group_name) invoke_in_main_thread(tray_notification, title, text, tray, window) icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index edf7ac6..4cccdd5 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -678,8 +678,9 @@ class MainWindow(QtWidgets.QMainWindow): def _selected_contact_changed(self): num = self.friends_list.currentRow() - self._contacts_manager.active_contact = num - self.groupMenuButton.setVisible(not self._contacts_manager.is_active_a_friend()) + if self._contacts_manager.active_contact != num: + self._contacts_manager.active_contact = num + self.groupMenuButton.setVisible(self._contacts_manager.is_active_a_group()) def mouseReleaseEvent(self, event): pos = self.connection_status.pos() diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index d705293..782020c 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1553,10 +1553,11 @@ class Tox: error = c_int() peer_info = self.group_self_peer_info_new() nick = bytes(nick, 'utf-8') + group_name = group_name.encode('utf-8') peer_info.contents.nick = c_char_p(nick) peer_info.contents.nick_length = len(nick) peer_info.contents.user_status = status - result = Tox.libtoxcore.tox_group_new(self._tox_pointer, privacy_state, group_name.encode('utf-8'), + result = Tox.libtoxcore.tox_group_new(self._tox_pointer, privacy_state, group_name, len(group_name), peer_info, byref(error)) return result @@ -2180,11 +2181,12 @@ class Tox: result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, group_number, friend_number, byref(error)) return result - def group_self_peer_info_new(self): + @staticmethod + def group_self_peer_info_new(): error = c_int() f = Tox.libtoxcore.tox_group_self_peer_info_new f.restype = POINTER(GroupChatSelfPeerInfo) - result = f(self._tox_pointer, byref(error)) + result = f(byref(error)) return result From 25de4fa2ef99668d33911493c9c77f95448b10a9 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 3 Aug 2018 18:04:28 +0300 Subject: [PATCH 111/217] wrapper update and minor fixes --- toxygen/contacts/contacts_manager.py | 12 +++++++----- toxygen/groups/groups_service.py | 3 ++- toxygen/wrapper/tox.py | 23 +++++++++++++++-------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index c1d0b10..4a5454b 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -179,7 +179,11 @@ class ContactsManager(ToxSave): part2 = sorted(part2, key=key_lambda) self._contacts = part1 + part2 elif sorting == 0: - self._contacts = sorted(self._contacts, key=lambda x: x.number) + contacts = sorted(self._contacts, key=lambda c: c.number) + friends = filter(lambda c: type(c) is Friend, contacts) + groups = filter(lambda c: type(c) is GroupChat, contacts) + group_peers = filter(lambda c: type(c) is GroupPeerContact, contacts) + self._contacts = list(friends) + list(groups) + list(group_peers) else: self._contacts = sorted(self._contacts, key=lambda x: x.name.lower()) @@ -194,6 +198,7 @@ class ContactsManager(ToxSave): friend.visibility = (friend.status is not None or sorting not in (1, 4)) and filtered_by_name # show friend even if it's hidden when there any unread messages/actions friend.visibility = friend.visibility or friend.messages or friend.actions + # TODO: calculate height if friend.visibility: self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) else: @@ -542,10 +547,7 @@ class ContactsManager(ToxSave): remove(avatar_path) def _delete_contact(self, num): - if len(self._contacts) == 1: - self.set_active(-1) - else: - self.set_active(0) + self.set_active(-1 if len(self._contacts) == 1 else 0) self._contact_provider.remove_contact_from_cache(self._contacts[num].tox_id) del self._contacts[num] diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index c062685..1016bd7 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -104,7 +104,7 @@ class GroupsService(tox_save.ToxSave): def set_group_topic(self, group): if not group.is_moderator_or_founder(): return - text = util_ui.tr('New topic for group {}:'.format(group.name)) + text = util_ui.tr('New topic for group "{}":'.format(group.name)) title = util_ui.tr('Set group topic') topic, ok = util_ui.text_dialog(text, title, group.status_message) if not ok or not topic: @@ -151,6 +151,7 @@ class GroupsService(tox_save.ToxSave): self_peer = group.get_self_peer() self_peer.name = name self_peer.status = status + self.generate_peers_list() # ----------------------------------------------------------------------------------------------------------------- # Private methods diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 782020c..ebb6aee 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1940,6 +1940,13 @@ class Tox: result = Tox.libtoxcore.tox_group_get_number_groups(self._tox_pointer) return result + def groups_get_list(self): + groups_list_size = self.group_get_number_groups() + groups_list = create_string_buffer(sizeof(c_uint32) * groups_list_size) + groups_list = POINTER(c_uint32)(groups_list) + Tox.libtoxcore.tox_groups_get_list(self._tox_pointer, groups_list) + return groups_list[0:groups_list_size] + def group_get_privacy_state(self, group_number): """ Return the privacy state of the group designated by the given group number. If group number @@ -2461,7 +2468,7 @@ class Tox: create_string_buffer(sizeof(c_uint32) * self.group_ban_get_list_size(group_number)), byref(error))) return result - def group_ban_get_name_size(self, group_number, ban_id): + def group_ban_get_target_size(self, group_number, ban_id): """ Return the length of the name for the ban list entry designated by ban_id, in the group designated by the given group number. If either group_number or ban_id is invalid, @@ -2469,10 +2476,10 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_ban_get_name_size(self._tox_pointer, group_number, ban_id, byref(error)) + result = Tox.libtoxcore.tox_group_ban_get_target_size(self._tox_pointer, group_number, ban_id, byref(error)) return result - def group_ban_get_name(self, group_number, ban_id): + def group_ban_get_target(self, group_number, ban_id): """ Write the name of the ban entry designated by ban_id in the group designated by the given group number to a byte array. @@ -2483,12 +2490,12 @@ class Tox: """ error = c_int() - size = self.group_ban_get_name_size(group_number, ban_id) - name = create_string_buffer() + size = self.group_ban_get_target_size(group_number, ban_id) + target = create_string_buffer(size) - result = Tox.libtoxcore.tox_group_ban_get_name(self._tox_pointer, group_number, ban_id, - name, byref(error)) - return str(name[:size], 'utf-8') + result = Tox.libtoxcore.tox_group_ban_get_target(self._tox_pointer, group_number, ban_id, + target, byref(error)) + return str(target[:size], 'utf-8') def group_ban_get_time_set(self, group_number, ban_id): """ From 5f56d630ce19dcf2ab4ca68f6c7befbdbd905eb6 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 3 Aug 2018 21:07:18 +0300 Subject: [PATCH 112/217] messenger refactoring --- toxygen/contacts/contacts_manager.py | 14 +++--- toxygen/messenger/messenger.py | 73 ++++++++++------------------ toxygen/ui/menu.py | 34 +++++++------ 3 files changed, 52 insertions(+), 69 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 4a5454b..757bfb0 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -24,7 +24,6 @@ class ContactsManager(ToxSave): self._active_contact_changed = Event() self._sorting = settings['sorting'] self._filter_string = '' - self._friend_item_height = 40 if settings['compact_mode'] else 70 screen.contacts_filter.setCurrentIndex(int(self._sorting)) self._history = history self._load_contacts() @@ -149,6 +148,9 @@ class ContactsManager(ToxSave): def is_active_a_group(self): return type(self.get_curr_contact()) is GroupChat + def is_active_a_group_chat_peer(self): + return type(self.get_curr_contact()) is GroupPeerContact + # ----------------------------------------------------------------------------------------------------------------- # Filtration # ----------------------------------------------------------------------------------------------------------------- @@ -198,11 +200,9 @@ class ContactsManager(ToxSave): friend.visibility = (friend.status is not None or sorting not in (1, 4)) and filtered_by_name # show friend even if it's hidden when there any unread messages/actions friend.visibility = friend.visibility or friend.messages or friend.actions - # TODO: calculate height - if friend.visibility: - self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) - else: - self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) + item = self._screen.friends_list.item(index) + item_widget = self._screen.friends_list.itemWidget(item) + item.setSizeHint(QtCore.QSize(250, item_widget.height() if friend.visibility else 0)) # save soring results self._sorting, self._filter_string = sorting, filter_str self._settings['sorting'] = self._sorting @@ -436,7 +436,7 @@ class ContactsManager(ToxSave): util.log('Accept friend request failed! ' + str(ex)) def can_send_typing_notification(self): - return self._settings['typing_notifications'] and self._active_contact + 1 + return self._settings['typing_notifications'] and not self.is_active_a_group_chat_peer() # ----------------------------------------------------------------------------------------------------------------- # Contacts numbers update diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 99d7f2f..cce619d 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -44,14 +44,28 @@ class Messenger(tox_save.ToxSave): def send_message(self): text = self._screen.messageEdit.toPlainText() - if self._contacts_manager.is_active_a_friend(): - self.send_message_to_friend(text) - elif self._contacts_manager.is_active_a_group(): - self.send_message_to_group(text) - else: - self.send_message_to_group_peer(text) - def send_message_to_friend(self, text, friend_number=None): + plugin_command_prefix = '/plugin ' + if text.startswith(plugin_command_prefix): + self._plugin_loader.command(text[len(plugin_command_prefix):]) + self._screen.messageEdit.clear() + return + + action_message_prefix = '/me ' + if text.startswith(action_message_prefix): + message_type = TOX_MESSAGE_TYPE['ACTION'] + text = text[len(action_message_prefix):] + else: + message_type = TOX_MESSAGE_TYPE['NORMAL'] + + if self._contacts_manager.is_active_a_friend(): + self.send_message_to_friend(text, message_type) + elif self._contacts_manager.is_active_a_group(): + self.send_message_to_group(text, message_type) + elif self._contacts_manager.is_active_a_group_chat_peer(): + self.send_message_to_group_peer(text, message_type) + + def send_message_to_friend(self, text, message_type, friend_number=None): """ Send message :param text: message text @@ -60,19 +74,9 @@ class Messenger(tox_save.ToxSave): if friend_number is None: friend_number = self._contacts_manager.get_active_number() - if text.startswith('/plugin '): - self._plugin_loader.command(text[8:]) - self._screen.messageEdit.clear() - return - if not text or friend_number < 0: return - if text.startswith('/me '): - message_type = TOX_MESSAGE_TYPE['ACTION'] - text = text[4:] - else: - message_type = TOX_MESSAGE_TYPE['NORMAL'] friend = self._get_friend_by_number(friend_number) messages = self._split_message(text.encode('utf-8')) t = util.get_unix_time() @@ -108,23 +112,13 @@ class Messenger(tox_save.ToxSave): # Messaging - groups # ----------------------------------------------------------------------------------------------------------------- - def send_message_to_group(self, text, group_number=None): + def send_message_to_group(self, text, message_type, group_number=None): if group_number is None: group_number = self._contacts_manager.get_active_number() - if text.startswith('/plugin '): - self._plugin_loader.command(text[8:]) - self._screen.messageEdit.clear() - return - if not text or group_number < 0: return - if text.startswith('/me '): - message_type = TOX_MESSAGE_TYPE['ACTION'] - text = text[4:] - else: - message_type = TOX_MESSAGE_TYPE['NORMAL'] group = self._get_group_by_number(group_number) messages = self._split_message(text.encode('utf-8')) t = util.get_unix_time() @@ -155,27 +149,16 @@ class Messenger(tox_save.ToxSave): # Messaging - group peers # ----------------------------------------------------------------------------------------------------------------- - def send_message_to_group_peer(self, text, group_number=None, peer_id=None): + def send_message_to_group_peer(self, text, message_type, group_number=None, peer_id=None): if group_number is None or peer_id is None: group_peer_contact = self._contacts_manager.get_curr_contact() peer_id = group_peer_contact.number group = self._get_group_by_public_key(group_peer_contact.group_pk) group_number = group.number - if text.startswith('/plugin '): - self._plugin_loader.command(text[8:]) - self._screen.messageEdit.clear() - return - if not text or group_number < 0 or peer_id < 0: return - if text.startswith('/me '): - message_type = TOX_MESSAGE_TYPE['ACTION'] - text = text[4:] - else: - message_type = TOX_MESSAGE_TYPE['NORMAL'] - group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id) group = self._get_group_by_number(group_number) messages = self._split_message(text.encode('utf-8')) @@ -220,12 +203,10 @@ class Messenger(tox_save.ToxSave): """ Send typing notification to a friend """ - if self._contacts_manager.can_send_typing_notification(): - try: - contact = self._contacts_manager.get_curr_contact() - contact.typing_notification_handler.send(self._tox, typing) - except: - pass + if not self._contacts_manager.can_send_typing_notification(): + return + contact = self._contacts_manager.get_curr_contact() + contact.typing_notification_handler.send(self._tox, typing) def friend_typing(self, friend_number, typing): """ diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 07fbb85..ea49e82 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -589,22 +589,24 @@ class InterfaceSettings(CenteredWidget): def import_sm(self): directory = util_ui.directory_dialog(util_ui.tr('Choose folder with smiley pack')) - if directory: - src = directory + '/' - dest = curr_directory() + '/smileys/' + os.path.basename(directory) + '/' - copy(src, dest) + if not directory: + return + src = directory + '/' + dest = get_smileys_directory() + os.path.basename(directory) + '/' + copy(src, dest) def new_font(self): font, ok = QtWidgets.QFontDialog.getFont(QtGui.QFont(self._settings['font'], 10), self) - if ok: - self._settings['font'] = font.family() - self._settings.save() - util_ui.question() - msgBox = QtWidgets.QMessageBox() - text = util_ui.tr('Restart app to apply settings') - msgBox.setWindowTitle(util_ui.tr('Restart required')) - msgBox.setText(text) - msgBox.exec_() + if not ok: + return + self._settings['font'] = font.family() + self._settings.save() + util_ui.question() + msgBox = QtWidgets.QMessageBox() + text = util_ui.tr('Restart app to apply settings') + msgBox.setWindowTitle(util_ui.tr('Restart required')) + msgBox.setText(text) + msgBox.exec_() def select_color(self): col = QtWidgets.QColorDialog.getColor(QtGui.QColor(self._settings['unread_color'])) @@ -616,10 +618,10 @@ class InterfaceSettings(CenteredWidget): def closeEvent(self, event): self._settings['theme'] = str(self.themeSelect.currentText()) + app = QtWidgets.QApplication.instance() try: theme = self._settings['theme'] - app = QtWidgets.QApplication.instance() - with open(curr_directory() + self._settings.built_in_themes()[theme]) as fl: + with open(get_styles_directory() + self._settings.built_in_themes()[theme]) as fl: style = fl.read() app.setStyleSheet(style) except IsADirectoryError: @@ -637,7 +639,7 @@ class InterfaceSettings(CenteredWidget): restart = True self._settings['smiley_pack'] = self.smiley_pack.currentText() self._settings['close_to_tray'] = self.close_to_tray.isChecked() - smileys.SmileyLoader.get_instance().load_pack() + self._smiley_loader.load_pack() language = self.lang_choose.currentText() if self._settings['language'] != language: self._settings['language'] = language From bc9dfd1bc43aaa4887b8f563e787d2c01e30fcb6 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 4 Aug 2018 17:46:02 +0300 Subject: [PATCH 113/217] interface settings screen converted. --- toxygen/bootstrap/nodes.json | 2 +- toxygen/ui/main_screen.py | 8 +- toxygen/ui/menu.py | 232 ++++++++++++++-------------------- toxygen/user_data/settings.py | 6 +- 4 files changed, 102 insertions(+), 146 deletions(-) diff --git a/toxygen/bootstrap/nodes.json b/toxygen/bootstrap/nodes.json index c71642b..382e000 100644 --- a/toxygen/bootstrap/nodes.json +++ b/toxygen/bootstrap/nodes.json @@ -1 +1 @@ -{"nodes":[{"ipv4":"127.0.0.1","ipv6":"-","port":33445,"public_key":"AB38C55C594B9D6BF23A896345DB832722146FCAA120EFA0EE721B7BFB4DB83F","status_udp":true,"status_tcp":true}]} \ No newline at end of file +{"nodes":[{"ipv4":"80.211.19.83","ipv6":"-","port":33445,"public_key":"C78997BEA65096B09EAD41F850116D798A0D4A14D2D8652FCD2495ADDE006523","status_udp":true,"status_tcp":true}]} \ No newline at end of file diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 4cccdd5..9b80576 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -375,7 +375,8 @@ class MainWindow(QtWidgets.QMainWindow): self.retranslateUi() def closeEvent(self, event): - if not self._settings['close_to_tray'] or self._settings.closing: + close_setting = self._settings['close_app'] + if close_setting == 0 or self._settings.closing: if self._saved: return self._saved = True @@ -386,9 +387,12 @@ class MainWindow(QtWidgets.QMainWindow): self._settings.save() util_ui.close_all_windows() event.accept() - elif QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): + elif close_setting == 2 and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): event.ignore() self.hide() + else: + event.ignore() + self.showMinimized() def close_window(self): self._settings.closing = True diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index ea49e82..96067fb 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -222,16 +222,17 @@ class ProfileSettings(CenteredWidget): def set_avatar(self): choose = util_ui.tr("Choose avatar") name = util_ui.file_dialog(choose, 'Images (*.png)') - if name[0]: - bitmap = QtGui.QPixmap(name[0]) - bitmap.scaled(QtCore.QSize(128, 128), aspectRatioMode=QtCore.Qt.KeepAspectRatio, - transformMode=QtCore.Qt.SmoothTransformation) + if not name[0]: + return + bitmap = QtGui.QPixmap(name[0]) + bitmap.scaled(QtCore.QSize(128, 128), aspectRatioMode=QtCore.Qt.KeepAspectRatio, + transformMode=QtCore.Qt.SmoothTransformation) - byte_array = QtCore.QByteArray() - buffer = QtCore.QBuffer(byte_array) - buffer.open(QtCore.QIODevice.WriteOnly) - bitmap.save(buffer, 'PNG') - self._profile.set_avatar(bytes(byte_array.data())) + byte_array = QtCore.QByteArray() + buffer = QtCore.QBuffer(byte_array) + buffer.open(QtCore.QIODevice.WriteOnly) + bitmap.save(buffer, 'PNG') + self._profile.set_avatar(bytes(byte_array.data())) def export_profile(self): directory = util_ui.directory_dialog() + '/' @@ -260,8 +261,10 @@ class NetworkSettings(CenteredWidget): def _update_ui(self): self.ipLineEdit = LineEdit(self) self.ipLineEdit.setGeometry(100, 280, 270, 30) + self.portLineEdit = LineEdit(self) self.portLineEdit.setGeometry(100, 325, 270, 30) + self.restartCorePushButton.clicked.connect(self._restart_core) self.ipv6CheckBox.setChecked(self._settings['ipv6_enabled']) self.udpCheckBox.setChecked(self._settings['udp_enabled']) @@ -472,185 +475,134 @@ class NotificationsSettings(CenteredWidget): class InterfaceSettings(CenteredWidget): """Interface settings form""" + def __init__(self, settings, smiley_loader): super().__init__() self._settings = settings self._smiley_loader = smiley_loader - self.initUI() + + uic.loadUi(get_views_path('interface_settings_screen'), self) + self._update_ui() self.center() - def initUI(self): - self.setObjectName("interfaceForm") - self.setMinimumSize(QtCore.QSize(400, 650)) - self.setMaximumSize(QtCore.QSize(400, 650)) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(30, 10, 370, 20)) - font = QtGui.QFont() - font.setPointSize(14) - font.setBold(True) - font.setFamily(self._settings['font']) - self.label.setFont(font) - self.themeSelect = QtWidgets.QComboBox(self) - self.themeSelect.setGeometry(QtCore.QRect(30, 40, 120, 30)) - self.themeSelect.addItems(list(self._settings.built_in_themes().keys())) + def _update_ui(self): + themes = list(self._settings.built_in_themes().keys()) + self.themeComboBox.addItems(themes) theme = self._settings['theme'] if theme in self._settings.built_in_themes().keys(): - index = list(self._settings.built_in_themes().keys()).index(theme) + index = themes.index(theme) else: index = 0 - self.themeSelect.setCurrentIndex(index) - self.lang_choose = QtWidgets.QComboBox(self) - self.lang_choose.setGeometry(QtCore.QRect(30, 110, 120, 30)) - supported = sorted(Settings.supported_languages().keys(), reverse=True) - for key in supported: - self.lang_choose.insertItem(0, key) + self.themeComboBox.setCurrentIndex(index) + + supported_languages = sorted(Settings.supported_languages().keys(), reverse=True) + for key in supported_languages: + self.languageComboBox.insertItem(0, key) if self._settings['language'] == key: - self.lang_choose.setCurrentIndex(0) - self.lang = QtWidgets.QLabel(self) - self.lang.setGeometry(QtCore.QRect(30, 80, 370, 20)) - self.lang.setFont(font) - self.mirror_mode = QtWidgets.QCheckBox(self) - self.mirror_mode.setGeometry(QtCore.QRect(30, 160, 370, 20)) - self.mirror_mode.setChecked(self._settings['mirror_mode']) - self.smileys = QtWidgets.QCheckBox(self) - self.smileys.setGeometry(QtCore.QRect(30, 190, 120, 20)) - self.smileys.setChecked(self._settings['smileys']) - self.smiley_pack_label = QtWidgets.QLabel(self) - self.smiley_pack_label.setGeometry(QtCore.QRect(30, 230, 370, 20)) - self.smiley_pack_label.setFont(font) - self.smiley_pack = QtWidgets.QComboBox(self) - self.smiley_pack.setGeometry(QtCore.QRect(30, 260, 160, 30)) - self.smiley_pack.addItems(self._smiley_loader.get_packs_list()) + self.languageComboBox.setCurrentIndex(0) + + smiley_packs = self._smiley_loader.get_packs_list() + self.smileysPackComboBox.addItems(smiley_packs) try: - ind = self._smiley_loader.get_packs_list().index(self._settings['smiley_pack']) + index = smiley_packs.index(self._settings['smiley_pack']) except: - ind = self._smiley_loader.get_packs_list().index('default') - self.smiley_pack.setCurrentIndex(ind) - self.messages_font_size_label = QtWidgets.QLabel(self) - self.messages_font_size_label.setGeometry(QtCore.QRect(30, 300, 370, 20)) - self.messages_font_size_label.setFont(font) - self.messages_font_size = QtWidgets.QComboBox(self) - self.messages_font_size.setGeometry(QtCore.QRect(30, 330, 160, 30)) - self.messages_font_size.addItems([str(x) for x in range(10, 25)]) - self.messages_font_size.setCurrentIndex(self._settings['message_font_size'] - 10) + index = smiley_packs.index('default') + self.smileysPackComboBox.setCurrentIndex(index) - self.unread = QtWidgets.QPushButton(self) - self.unread.setGeometry(QtCore.QRect(30, 470, 340, 30)) - self.unread.clicked.connect(self.select_color) + app_closing_setting = self._settings['close_app'] + self.closeRadioButton.setChecked(app_closing_setting == 0) + self.hideRadioButton.setChecked(app_closing_setting == 1) + self.closeToTrayRadioButton.setChecked(app_closing_setting == 2) - self.compact_mode = QtWidgets.QCheckBox(self) - self.compact_mode.setGeometry(QtCore.QRect(30, 380, 370, 20)) - self.compact_mode.setChecked(self._settings['compact_mode']) + self.compactModeCheckBox.setChecked(self._settings['compact_mode']) + self.showAvatarsCheckBox.setChecked(self._settings['show_avatars']) + self.smileysCheckBox.setChecked(self._settings['smileys']) - self.close_to_tray = QtWidgets.QCheckBox(self) - self.close_to_tray.setGeometry(QtCore.QRect(30, 410, 370, 20)) - self.close_to_tray.setChecked(self._settings['close_to_tray']) + self.importSmileysPushButton.clicked.connect(self._import_smileys) + self.importStickersPushButton.clicked.connect(self._import_stickers) - self.show_avatars = QtWidgets.QCheckBox(self) - self.show_avatars.setGeometry(QtCore.QRect(30, 440, 370, 20)) - self.show_avatars.setChecked(self._settings['show_avatars']) + self._retranslate_ui() - self.choose_font = QtWidgets.QPushButton(self) - self.choose_font.setGeometry(QtCore.QRect(30, 510, 340, 30)) - self.choose_font.clicked.connect(self.new_font) - - self.import_smileys = QtWidgets.QPushButton(self) - self.import_smileys.setGeometry(QtCore.QRect(30, 550, 340, 30)) - self.import_smileys.clicked.connect(self.import_sm) - - self.import_stickers = QtWidgets.QPushButton(self) - self.import_stickers.setGeometry(QtCore.QRect(30, 590, 340, 30)) - self.import_stickers.clicked.connect(self.import_st) - - self.retranslateUi() - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.show_avatars.setText(util_ui.tr("Show avatars in chat")) + def _retranslate_ui(self): self.setWindowTitle(util_ui.tr("Interface settings")) - self.label.setText(util_ui.tr("Theme:")) - self.lang.setText(util_ui.tr("Language:")) - self.smileys.setText(util_ui.tr("Smileys")) - self.smiley_pack_label.setText(util_ui.tr("Smiley pack:")) - self.mirror_mode.setText(util_ui.tr("Mirror mode")) - self.messages_font_size_label.setText(util_ui.tr("Messages font size:")) - self.unread.setText(util_ui.tr("Select unread messages notification color")) - self.compact_mode.setText(util_ui.tr("Compact contact list")) - self.import_smileys.setText(util_ui.tr("Import smiley pack")) - self.import_stickers.setText(util_ui.tr("Import sticker pack")) - self.close_to_tray.setText(util_ui.tr("Close to tray")) - self.choose_font.setText(util_ui.tr("Select font")) + self.showAvatarsCheckBox.setText(util_ui.tr("Show avatars in chat")) + self.themeLabel.setText(util_ui.tr("Theme:")) + self.languageLabel.setText(util_ui.tr("Language:")) + self.smileysGroupBox.setTitle(util_ui.tr("Smileys settings")) + self.smileysPackLabel.setText(util_ui.tr("Smiley pack:")) + self.smileysCheckBox.setText(util_ui.tr("Smileys")) + self.closeRadioButton.setText(util_ui.tr("Close app")) + self.hideRadioButton.setText(util_ui.tr("Hide app")) + self.closeToTrayRadioButton.setText(util_ui.tr("Close to tray")) + self.mirrorModeCheckBox.setText(util_ui.tr("Mirror mode")) + self.compactModeCheckBox.setText(util_ui.tr("Compact contact list")) + self.importSmileysPushButton.setText(util_ui.tr("Import smiley pack")) + self.importStickersPushButton.setText(util_ui.tr("Import sticker pack")) + self.appClosingGroupBox.setTitle(util_ui.tr("App closing settings")) - def import_st(self): + @staticmethod + def _import_stickers(): directory = util_ui.directory_dialog(util_ui.tr('Choose folder with sticker pack')) if directory: dest = join_path(get_stickers_directory(), os.path.basename(directory)) copy(directory, dest) - def import_sm(self): + @staticmethod + def _import_smileys(): directory = util_ui.directory_dialog(util_ui.tr('Choose folder with smiley pack')) if not directory: return src = directory + '/' - dest = get_smileys_directory() + os.path.basename(directory) + '/' + dest = join_path(get_smileys_directory(), os.path.basename(directory)) copy(src, dest) - def new_font(self): - font, ok = QtWidgets.QFontDialog.getFont(QtGui.QFont(self._settings['font'], 10), self) - if not ok: - return - self._settings['font'] = font.family() - self._settings.save() - util_ui.question() - msgBox = QtWidgets.QMessageBox() - text = util_ui.tr('Restart app to apply settings') - msgBox.setWindowTitle(util_ui.tr('Restart required')) - msgBox.setText(text) - msgBox.exec_() - - def select_color(self): - col = QtWidgets.QColorDialog.getColor(QtGui.QColor(self._settings['unread_color'])) - - if col.isValid(): - name = col.name() - self._settings['unread_color'] = name - self._settings.save() - def closeEvent(self, event): - self._settings['theme'] = str(self.themeSelect.currentText()) app = QtWidgets.QApplication.instance() + + self._settings['theme'] = str(self.themeComboBox.currentText()) try: theme = self._settings['theme'] - with open(get_styles_directory() + self._settings.built_in_themes()[theme]) as fl: + styles_path = join_path(get_styles_directory(), self._settings.built_in_themes()[theme]) + with open(styles_path) as fl: style = fl.read() app.setStyleSheet(style) except IsADirectoryError: - app.setStyleSheet('') # for default style - self._settings['smileys'] = self.smileys.isChecked() + pass + + self._settings['smileys'] = self.smileysCheckBox.isChecked() + restart = False - if self._settings['mirror_mode'] != self.mirror_mode.isChecked(): - self._settings['mirror_mode'] = self.mirror_mode.isChecked() + if self._settings['mirror_mode'] != self.mirrorModeCheckBox.isChecked(): + self._settings['mirror_mode'] = self.mirrorModeCheckBox.isChecked() restart = True - if self._settings['compact_mode'] != self.compact_mode.isChecked(): - self._settings['compact_mode'] = self.compact_mode.isChecked() + + if self._settings['compact_mode'] != self.compactModeCheckBox.isChecked(): + self._settings['compact_mode'] = self.compactModeCheckBox.isChecked() restart = True - if self._settings['show_avatars'] != self.show_avatars.isChecked(): - self._settings['show_avatars'] = self.show_avatars.isChecked() + + if self._settings['show_avatars'] != self.showAvatarsCheckBox.isChecked(): + self._settings['show_avatars'] = self.showAvatarsCheckBox.isChecked() restart = True - self._settings['smiley_pack'] = self.smiley_pack.currentText() - self._settings['close_to_tray'] = self.close_to_tray.isChecked() + + self._settings['smiley_pack'] = self.smileysPackComboBox.currentText() self._smiley_loader.load_pack() - language = self.lang_choose.currentText() + + language = self.languageComboBox.currentText() if self._settings['language'] != language: self._settings['language'] = language - text = self.lang_choose.currentText() - path = Settings.supported_languages()[text] - app = QtWidgets.QApplication.instance() + path = Settings.supported_languages()[language] app.removeTranslator(app.translator) - app.translator.load(curr_directory() + '/translations/' + path) + app.translator.load(join_path(get_translations_directory(), path)) app.installTranslator(app.translator) - self._settings['message_font_size'] = self.messages_font_size.currentIndex() + 10 self._settings.save() + + app_closing_setting = 0 + if self.hideRadioButton.isChecked(): + app_closing_setting = 1 + elif self.closeToTrayRadioButton.isChecked(): + app_closing_setting = 2 + self._settings['close_app'] = app_closing_setting + if restart: util_ui.message_box(util_ui.tr('Restart app to apply settings'), util_ui.tr('Restart required')) diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 4171978..d375f92 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -142,7 +142,7 @@ class Settings(dict): 'compact_mode': False, 'identicons': True, 'show_welcome_screen': True, - 'close_to_tray': False, + 'close_app': 0, 'font': 'Times New Roman', 'update': 1, 'group_notifications': True, @@ -163,8 +163,8 @@ class Settings(dict): @staticmethod def built_in_themes(): return { - 'dark': '/styles/dark_style.qss', - 'default': '/styles/style.qss' + 'dark': 'dark_style.qss', + 'default': 'style.qss' } def upgrade(self): From 9f702339dddb76d5be7ed3d9751bffe4855927ac Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 5 Aug 2018 11:19:07 +0300 Subject: [PATCH 114/217] dockerfile for building - initial version --- build/Dockerfile | 11 +++++++++++ build/build.sh | 19 +++++++++++++++++++ toxygen/ui/items_factories.py | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 build/Dockerfile create mode 100644 build/build.sh diff --git a/build/Dockerfile b/build/Dockerfile new file mode 100644 index 0000000..04c59e3 --- /dev/null +++ b/build/Dockerfile @@ -0,0 +1,11 @@ +FROM ubuntu:16.04 + +RUN apt-get update && \ +apt-get install build-essential libtool autotools-dev automake checkinstall cmake check git yasm libsodium-dev libopus-dev libvpx-dev pkg-config -y && \ +git clone https://github.com/ingvar1995/toxcore.git --branch=ngc_rebase && \ +cd toxcore && mkdir _build && cd _build && \ +cmake .. && make && make install + +RUN apt-get install portaudio19-dev python3-pyqt5 python3-pyaudio python3-pip -y && \ +pip3 install --upgrade pip && \ +pip3 install numpy pydenticon opencv-python pyinstaller diff --git a/build/build.sh b/build/build.sh new file mode 100644 index 0000000..97f8e60 --- /dev/null +++ b/build/build.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +git clone https://github.com/toxygen-project/toxygen.git --branch=next_gen +cd toxygen/toxygen +ln -sf /usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/ /usr/bin/ +pyinstaller --windowed --icon=images/icon.ico --hidden-import=PyQt5.uic.plugins main.py +cp -r styles dist/main/ +cp -r plugins dist/main/ +cp -r sounds dist/main/ +cp -r smileys dist/main/ +cp -r stickers dist/main/ +cp -r bootstrap dist/main/ +cp -r images dist/main/ +cd dist +mv main toxygen +cd toxygen +mv main toxygen +cd .. +tar -zcvf toxygen_linux_64.tar.gz toxygen +rm -rf toxygen diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index 2ea5660..df6c95d 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -11,7 +11,7 @@ class ContactItemsFactory: def create_contact_item(self): item = ContactItem(self._settings) elem = QtWidgets.QListWidgetItem(self._friends_list) - elem.setSizeHint(QtCore.QSize(250, item.height())) + elem.setSizeHint(QtCore.QSize(250, 40 if self._settings['compact_mode'] else 70)) self._friends_list.addItem(elem) self._friends_list.setItemWidget(elem, item) From 8f9b57325316b0b9d47a55dfe055cb14d850a9fd Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 5 Aug 2018 11:35:24 +0300 Subject: [PATCH 115/217] settings.py refactoring --- toxygen/user_data/profile_manager.py | 3 +- toxygen/user_data/settings.py | 124 +++++++++++++++------------ 2 files changed, 68 insertions(+), 59 deletions(-) diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index 3324fce..12e998f 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -49,7 +49,7 @@ class ProfileManager: if use_new_path: self._path = new_path + os.path.basename(self._path) self._directory = new_path - self._settings.update_path() + self._settings.update_path(new_path) @staticmethod def find_profiles(): @@ -72,4 +72,3 @@ class ProfileManager: name = fl[:-4] result.append((path + '/', name)) return result - diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index d375f92..768a07e 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -1,7 +1,6 @@ import json from utils.util import * import pyaudio -import smileys.smileys as smileys class Settings(dict): @@ -24,11 +23,10 @@ class Settings(dict): info = Settings.get_default_settings() log('Parsing settings error: ' + str(ex)) super().__init__(info) - self.upgrade() + self._upgrade() else: super().__init__(Settings.get_default_settings()) - self.save() - smileys.SmileyLoader(self) + self.save() self.locked = False self.closing = False self.unlockScreen = False @@ -45,24 +43,64 @@ class Settings(dict): 'enabled': input_devices and output_devices} self.video = {'device': -1, 'width': 640, 'height': 480, 'x': 0, 'y': 0} + # ----------------------------------------------------------------------------------------------------------------- + # Public methods + # ----------------------------------------------------------------------------------------------------------------- + + def save(self): + text = json.dumps(self) + if self._toxes.has_password(): + text = bytes(self._toxes.pass_encrypt(bytes(text, 'utf-8'))) + else: + text = bytes(text, 'utf-8') + with open(self._path, 'wb') as fl: + fl.write(text) + + def close(self): + path = self._profile_path + '.lock' + if os.path.isfile(path): + os.remove(path) + + def set_active_profile(self): + """ + Mark current profile as active + """ + path = self._profile_path + '.lock' + with open(path, 'w') as fl: + fl.write('active') + + def export(self, path): + text = json.dumps(self) + name = os.path.basename(self._path) + with open(join_path(path, str(name)), 'w') as fl: + fl.write(text) + + def update_path(self, new_path): + self._path = new_path + self.save() + + # ----------------------------------------------------------------------------------------------------------------- + # Static methods + # ----------------------------------------------------------------------------------------------------------------- + @staticmethod def get_auto_profile(): p = Settings.get_global_settings_path() - if os.path.isfile(p): - with open(p) as fl: - data = fl.read() - try: - auto = json.loads(data) - except Exception as ex: - log(str(ex)) - auto = {} - if 'profile_path' in auto: - path = str(auto['profile_path']) - if not os.path.isabs(path): - path = join_path(path, curr_directory(__file__)) - if os.path.isfile(path): - return path - return None + if not os.path.isfile(p): + return None + with open(p) as fl: + data = fl.read() + try: + auto = json.loads(data) + except Exception as ex: + log(str(ex)) + auto = {} + if 'profile_path' in auto: + path = str(auto['profile_path']) + if not os.path.isabs(path): + path = join_path(path, curr_directory(__file__)) + if os.path.isfile(path): + return path @staticmethod def set_auto_profile(path): @@ -167,44 +205,6 @@ class Settings(dict): 'default': 'style.qss' } - def upgrade(self): - default = Settings.get_default_settings() - for key in default: - if key not in self: - print(key) - self[key] = default[key] - self.save() - - def save(self): - text = json.dumps(self) - if self._toxes.has_password(): - text = bytes(self._toxes.pass_encrypt(bytes(text, 'utf-8'))) - else: - text = bytes(text, 'utf-8') - with open(self._path, 'wb') as fl: - fl.write(text) - - def close(self): - path = self._profile_path + '.lock' - if os.path.isfile(path): - os.remove(path) - - def set_active_profile(self): - """ - Mark current profile as active - """ - path = self._profile_path + '.lock' - with open(path, 'w') as fl: - fl.write('active') - - def export(self, path): - text = json.dumps(self) - with open(path + str(self.name) + '.json', 'w') as fl: - fl.write(text) - - def update_path(self): - self.path = ProfileManager.get_path() + self.name + '.json' - @staticmethod def get_global_settings_path(): return os.path.join(get_base_directory(), 'toxygen.json') @@ -219,3 +219,13 @@ class Settings(dict): else: return os.getenv('HOME') + '/.config/tox/' + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _upgrade(self): + default = Settings.get_default_settings() + for key in default: + if key not in self: + print(key) + self[key] = default[key] From 33052f8a98a4f4e80a222e58cf7e446497161104 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 5 Aug 2018 12:34:11 +0300 Subject: [PATCH 116/217] group moderation screen and all callbacks --- toxygen/contacts/contact_menu.py | 5 +- toxygen/contacts/group_chat.py | 28 +- toxygen/groups/groups_service.py | 40 ++- toxygen/middleware/callbacks.py | 31 +++ toxygen/ui/group_management_screen.py | 46 ++++ toxygen/ui/views/group_management_screen.ui | 110 ++++++++ toxygen/ui/views/interface_settings_screen.ui | 253 ++++++++++++++++++ toxygen/ui/widgets_factory.py | 4 + 8 files changed, 509 insertions(+), 8 deletions(-) create mode 100644 toxygen/ui/group_management_screen.py create mode 100644 toxygen/ui/views/group_management_screen.ui create mode 100644 toxygen/ui/views/interface_settings_screen.ui diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index e10f07d..63d0d36 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -181,9 +181,12 @@ class GroupMenuGenerator(BaseContactMenuGenerator): menu = (builder .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) .with_submenu(copy_menu_builder) + .with_optional_action(util_ui.tr('Manage group'), + lambda: groups_service.show_group_management_screen(self._contact), + self._contact.is_self_founder()) .with_optional_action(util_ui.tr('Set topic'), lambda: groups_service.set_group_topic(self._contact), - self._contact.is_moderator_or_founder()) + self._contact.is_self_moderator_or_founder()) .with_action(util_ui.tr('Reconnect to group'), lambda: groups_service.reconnect_to_group(self._contact.number)) .with_optional_action(util_ui.tr('Disconnect from group'), diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 38ab686..b38e538 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -12,6 +12,8 @@ class GroupChat(contact.Contact, ToxSave): super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) ToxSave.__init__(self, tox) self._is_private = is_private + self._password = None + self._peers_limit = 512 self._peers = [] self._add_self_to_gc() @@ -32,7 +34,26 @@ class GroupChat(contact.Contact, ToxSave): def get_is_private(self): return self._is_private - is_private = property(get_is_private) + def set_is_private(self, is_private): + self._is_private = is_private + + is_private = property(get_is_private, set_is_private) + + def get_password(self): + return self._password + + def set_password(self, password): + self._password = password + + password = property(get_password, set_password) + + def get_peers_limit(self): + return self._peers_limit + + def set_peers_limit(self, peers_limit): + self._peers_limit = peers_limit + + peers_limit = property(get_peers_limit, set_peers_limit) # ----------------------------------------------------------------------------------------------------------------- # Peers methods @@ -47,9 +68,12 @@ class GroupChat(contact.Contact, ToxSave): def get_self_role(self): return self._peers[0].role - def is_moderator_or_founder(self): + def is_self_moderator_or_founder(self): return self.get_self_role() <= constants.TOX_GROUP_ROLE['MODERATOR'] + def is_self_founder(self): + return self.get_self_role() == constants.TOX_GROUP_ROLE['FOUNDER'] + def add_peer(self, peer_id, is_current_user=False): peer = GroupChatPeer(peer_id, self._tox.group_peer_get_name(self._number, peer_id), diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 1016bd7..4cf1368 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -15,7 +15,7 @@ class GroupsService(tox_save.ToxSave): self._peers_list_widget = main_screen.peers_list self._widgets_factory_provider = widgets_factory_provider self._group_invites = [] - self._peer_screen = None + self._peer_screen = self._management_screen = None def set_tox(self, tox): super().set_tox(tox) @@ -70,14 +70,14 @@ class GroupsService(tox_save.ToxSave): friend = self._get_friend_by_number(friend_number) invite = GroupInvite(friend.tox_id, group_name, invite_data) self._group_invites.append(invite) - self._main_screen.update_gc_invites_button_state() + self._update_invites_button_state() def accept_group_invite(self, invite, name, status, password): pk = invite.friend_public_key friend = self._get_friend_by_public_key(pk) self._join_gc_via_invite(invite.invite_data, friend.number, name, status, password) self._delete_group_invite(invite) - self._main_screen.update_gc_invites_button_state() + self._update_invites_button_state() def decline_group_invite(self, invite): self._delete_group_invite(invite) @@ -102,7 +102,7 @@ class GroupsService(tox_save.ToxSave): group.status_message = self._tox.group_get_topic(group.number) def set_group_topic(self, group): - if not group.is_moderator_or_founder(): + if not group.is_self_moderator_or_founder(): return text = util_ui.tr('New topic for group "{}":'.format(group.name)) title = util_ui.tr('Set group topic') @@ -112,6 +112,30 @@ class GroupsService(tox_save.ToxSave): self._tox.group_set_topic(group.number, topic) group.status_message = topic + def show_group_management_screen(self, group): + widgets_factory = self._get_widgets_factory() + self._management_screen = widgets_factory.create_group_management_screen(group) + self._management_screen.show() + + def set_group_password(self, group, password): + if group.password == password: + return + self._tox.group_founder_set_password(group.number, password) + group.password = password + + def set_group_peers_limit(self, group, peers_limit): + if group.peers_limit == peers_limit: + return + self._tox.group_founder_set_peer_limit(group.number, peers_limit) + group.peers_limit = peers_limit + + def set_group_privacy_state(self, group, privacy_state): + is_private = privacy_state == constants.TOX_GROUP_PRIVACY_STATE['PRIVATE'] + if group.is_private == is_private: + return + self._tox.group_founder_set_privacy_state(group.number, privacy_state) + group.is_private = is_private + # ----------------------------------------------------------------------------------------------------------------- # Peers list # ----------------------------------------------------------------------------------------------------------------- @@ -123,7 +147,7 @@ class GroupsService(tox_save.ToxSave): PeersListGenerator().generate(group.peers, self, self._peers_list_widget, group.tox_id) def peer_selected(self, chat_id, peer_id): - widgets_factory = self._widgets_factory_provider.get_item() + widgets_factory = self._get_widgets_factory() group = self._get_group_by_public_key(chat_id) self_peer = group.get_self_peer() if self_peer.id != peer_id: @@ -186,3 +210,9 @@ class GroupsService(tox_save.ToxSave): def _join_gc_via_invite(self, invite_data, friend_number, nick, status, password): group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, status, password) self._add_new_group_by_number(group_number) + + def _update_invites_button_state(self): + self._main_screen.update_gc_invites_button_state() + + def _get_widgets_factory(self): + return self._widgets_factory_provider.get_item() diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 36bb960..77a029d 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -506,6 +506,34 @@ def group_moderation(groups_service, contacts_provider, contacts_manager, messen return wrapped + +def group_password(contacts_provider): + + def wrapped(tox_link, group_number, password, length, user_data): + password = str(password[:length], 'utf-8') + group = contacts_provider.get_group_by_number(group_number) + group.password = password + + return wrapped + + +def group_peer_limit(contacts_provider): + + def wrapped(tox_link, group_number, peer_limit, user_data): + group = contacts_provider.get_group_by_number(group_number) + group.peer_limit = peer_limit + + return wrapped + + +def group_privacy_state(contacts_provider): + + def wrapped(tox_link, group_number, privacy_state, user_data): + group = contacts_provider.get_group_by_number(group_number) + group.is_private = privacy_state == TOX_GROUP_PRIVACY_STATE['PRIVATE'] + + return wrapped + # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization # ----------------------------------------------------------------------------------------------------------------- @@ -573,3 +601,6 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_group_peer_status(group_peer_status(contacts_provider, groups_service), 0) tox.callback_group_topic(group_topic(contacts_provider), 0) tox.callback_group_moderation(group_moderation(groups_service, contacts_provider, contacts_manager, messenger), 0) + tox.callback_group_password(group_password(contacts_provider), 0) + tox.callback_group_peer_limit(group_peer_limit(contacts_provider), 0) + tox.callback_group_privacy_state(group_privacy_state(contacts_provider), 0) diff --git a/toxygen/ui/group_management_screen.py b/toxygen/ui/group_management_screen.py new file mode 100644 index 0000000..bbc2629 --- /dev/null +++ b/toxygen/ui/group_management_screen.py @@ -0,0 +1,46 @@ +from ui.widgets import CenteredWidget +from PyQt5 import uic +import utils.util as util +import utils.ui as util_ui + + +class GroupManagementScreen(CenteredWidget): + + def __init__(self, groups_service, group): + super().__init__() + self._groups_service = groups_service + self._group = group + + uic.loadUi(util.get_views_path('group_management_screen'), self) + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self.passwordLineEdit.setText(self._group.password) + self.privacyStateComboBox.setCurrentIndex(1 if self._group.is_private else 0) + self.peersLimitSpinBox.setValue(self._group.peers_limit) + + self.savePushButton.clicked.connect(self._save) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Group "{}"').format(self._group.name)) + self.passwordLabel.setText(util_ui.tr('Password')) + self.peerLimitLabel.setText(util_ui.tr('Peer limit:')) + self.privacyStateLabel.setText(util_ui.tr('Privacy state')) + self.savePushButton.setText(util_ui.tr('Save')) + + self.privacyStateComboBox.clear() + self.privacyStateComboBox.addItem(util_ui.tr('Public')) + self.privacyStateComboBox.addItem(util_ui.tr('Private')) + + def _save(self): + password = self.passwordLineEdit.text() + privacy_state = self.privacyStateComboBox.currentIndex() + peers_limit = self.peersLimitSpinBox.value() + + self._groups_service.set_group_password(self._group, password) + self._groups_service.set_group_privacy_state(self._group, privacy_state) + self._groups_service.set_group_peers_limit(self._group, peers_limit) + + self.close() diff --git a/toxygen/ui/views/group_management_screen.ui b/toxygen/ui/views/group_management_screen.ui new file mode 100644 index 0000000..859754b --- /dev/null +++ b/toxygen/ui/views/group_management_screen.ui @@ -0,0 +1,110 @@ + + + Form + + + + 0 + 0 + 658 + 238 + + + + Form + + + + + 180 + 20 + 450 + 41 + + + + + + + 20 + 30 + 145 + 20 + + + + TextLabel + + + + + + 20 + 80 + 145 + 20 + + + + TextLabel + + + + + + 180 + 70 + 450 + 40 + + + + 2 + + + 9999 + + + 512 + + + + + + 20 + 130 + 145 + 20 + + + + TextLabel + + + + + + 180 + 120 + 450 + 40 + + + + + + + 20 + 180 + 611 + 41 + + + + PushButton + + + + + + diff --git a/toxygen/ui/views/interface_settings_screen.ui b/toxygen/ui/views/interface_settings_screen.ui new file mode 100644 index 0000000..fb0bcf1 --- /dev/null +++ b/toxygen/ui/views/interface_settings_screen.ui @@ -0,0 +1,253 @@ + + + Form + + + + 0 + 0 + 552 + 847 + + + + Form + + + + + + Qt::ScrollBarAsNeeded + + + true + + + + + 0 + 0 + 532 + 827 + + + + + + 30 + 140 + 67 + 17 + + + + TextLabel + + + + + + 20 + 180 + 471 + 31 + + + + + + + 20 + 60 + 471 + 31 + + + + + + + 30 + 20 + 67 + 17 + + + + TextLabel + + + + + + 30 + 220 + 461 + 23 + + + + CheckBox + + + + + + 30 + 280 + 461 + 221 + + + + GroupBox + + + + + 30 + 40 + 92 + 23 + + + + CheckBox + + + + + + 30 + 80 + 411 + 17 + + + + TextLabel + + + + + + 30 + 120 + 411 + 31 + + + + + + + + 30 + 250 + 461 + 23 + + + + CheckBox + + + + + + 30 + 750 + 471 + 40 + + + + PushButton + + + + + + 30 + 690 + 471 + 40 + + + + PushButton + + + + + + 30 + 520 + 461 + 23 + + + + CheckBox + + + + + + 30 + 550 + 471 + 131 + + + + GroupBox + + + + + 30 + 30 + 421 + 23 + + + + RadioButton + + + + + + 30 + 60 + 431 + 23 + + + + RadioButton + + + + + + 30 + 90 + 421 + 23 + + + + RadioButton + + + + + + + + + + + diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index 75b83b4..e394684 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -4,6 +4,7 @@ from ui.groups_widgets import * from ui.peer_screen import * from ui.self_peer_screen import * from ui.group_invites_widgets import * +from ui.group_management_screen import * class WidgetsFactory: @@ -82,3 +83,6 @@ class WidgetsFactory: def create_group_invites_window(self): return GroupInvitesScreen(self._groups_service, self._profile, self._contacts_provider) + + def create_group_management_screen(self, group): + return GroupManagementScreen(self._groups_service, group) From 37541db07ddf8342cd1087a1b67c8fbb3dc6e702 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 5 Aug 2018 16:33:51 +0300 Subject: [PATCH 117/217] bans - wrapper --- toxygen/contacts/group_chat.py | 7 ++----- toxygen/wrapper/tox.py | 16 ++++++++++------ toxygen/wrapper/toxcore_enums_and_consts.py | 11 +++++++++++ 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index b38e538..e89b561 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -11,16 +11,13 @@ class GroupChat(contact.Contact, ToxSave): def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id, is_private): super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) ToxSave.__init__(self, tox) + self._is_private = is_private - self._password = None + self._password = str() self._peers_limit = 512 self._peers = [] self._add_self_to_gc() - def set_topic(self, topic): - self._tox.group_set_topic(self._number, topic.encode('utf-8')) - super().set_status_message(topic) - def remove_invalid_unsent_files(self): pass diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index ebb6aee..0e3310d 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from ctypes import c_char_p, Structure, c_bool, byref, c_int, c_size_t, POINTER, c_uint16, c_void_p, c_uint64 -from ctypes import create_string_buffer, ArgumentError, CFUNCTYPE, c_uint32, sizeof, c_uint8 +from ctypes import * from wrapper.toxcore_enums_and_consts import * from wrapper.toxav import ToxAV from wrapper.libtox import LibToxCore @@ -109,7 +108,6 @@ class Tox: self.group_invite_cb = None self.group_custom_packet_cb = None self.group_private_message_cb = None - self.group_private_message_cb = None self.group_message_cb = None self.group_password_cb = None self.group_peer_limit_cb = None @@ -2393,7 +2391,7 @@ class Tox: result = Tox.libtoxcore.tox_group_mod_set_role(self._tox_pointer, group_number, peer_id, role, byref(error)) return result - def group_mod_remove_peer(self, group_number, peer_id, set_ban): + def group_mod_remove_peer(self, group_number, peer_id): """ Kick/ban a peer. @@ -2403,14 +2401,20 @@ class Tox: :param group_number: The group number of the group the ban is intended for. :param peer_id: The ID of the peer who will be kicked and/or added to the ban list. - :param set_ban: Set to true if a ban shall be set on the peer's IP address. :return True on success. """ error = c_int() result = Tox.libtoxcore.tox_group_mod_remove_peer(self._tox_pointer, group_number, peer_id, - set_ban, byref(error)) + byref(error)) + return result + + def group_mod_ban_peer(self, group_number, peer_id, ban_type): + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_ban_peer(self._tox_pointer, group_number, peer_id, + ban_type, byref(error)) return result def group_mod_remove_ban(self, group_number, ban_id): diff --git a/toxygen/wrapper/toxcore_enums_and_consts.py b/toxygen/wrapper/toxcore_enums_and_consts.py index 4d09338..7c6478c 100644 --- a/toxygen/wrapper/toxcore_enums_and_consts.py +++ b/toxygen/wrapper/toxcore_enums_and_consts.py @@ -911,6 +911,17 @@ TOX_ERR_GROUP_BAN_QUERY = { 'TOX_ERR_GROUP_BAN_QUERY_BAD_ID': 2, } + +TOX_GROUP_BAN_TYPE = { + + 'TOX_GROUP_BAN_TYPE_IP_PORT': 0, + + 'TOX_GROUP_BAN_TYPE_PUBLIC_KEY': 1, + + 'TOX_GROUP_BAN_TYPE_NICK': 2 + +} + TOX_PUBLIC_KEY_SIZE = 32 TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6 From 741adcdf182317a98a0815ad07a8c4fc82f1e808 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 5 Aug 2018 21:05:18 +0300 Subject: [PATCH 118/217] bans - untested --- toxygen/contacts/contact_menu.py | 3 + toxygen/contacts/group_chat.py | 14 +++++ toxygen/groups/group_ban.py | 23 +++++++ toxygen/groups/groups_service.py | 32 ++++++++-- toxygen/ui/group_bans_widgets.py | 62 +++++++++++++++++++ toxygen/ui/peer_screen.py | 35 +++++++++-- toxygen/ui/views/bans_list_screen.ui | 29 +++++++++ toxygen/ui/views/gc_ban_item.ui | 58 ++++++++++++++++++ toxygen/ui/views/peer_screen.ui | 68 +++++++++++++++++++++ toxygen/ui/widgets_factory.py | 4 ++ toxygen/utils/util.py | 13 +++- toxygen/wrapper/tox.py | 8 ++- toxygen/wrapper/toxcore_enums_and_consts.py | 6 +- 13 files changed, 334 insertions(+), 21 deletions(-) create mode 100644 toxygen/groups/group_ban.py create mode 100644 toxygen/ui/group_bans_widgets.py create mode 100644 toxygen/ui/views/bans_list_screen.ui create mode 100644 toxygen/ui/views/gc_ban_item.ui diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index 63d0d36..7330b35 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -187,6 +187,9 @@ class GroupMenuGenerator(BaseContactMenuGenerator): .with_optional_action(util_ui.tr('Set topic'), lambda: groups_service.set_group_topic(self._contact), self._contact.is_self_moderator_or_founder()) + .with_optional_action(util_ui.tr('Bans list'), + lambda: groups_service.show_bans_list(self._contact), + self._contact.is_self_moderator_or_founder()) .with_action(util_ui.tr('Reconnect to group'), lambda: groups_service.reconnect_to_group(self._contact.number)) .with_optional_action(util_ui.tr('Disconnect from group'), diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index e89b561..f173695 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -4,6 +4,7 @@ import utils.util as util from groups.group_peer import GroupChatPeer from wrapper import toxcore_enums_and_consts as constants from common.tox_save import ToxSave +from groups.group_ban import GroupBan class GroupChat(contact.Contact, ToxSave): @@ -102,6 +103,19 @@ class GroupChat(contact.Contact, ToxSave): peers = property(get_peers) + def get_bans(self): + ban_ids = self._tox.group_ban_get_list(self._number) + bans = [] + for ban_id in ban_ids: + ban = GroupBan(ban_id, + self._tox.group_ban_get_target(self._number, ban_id), + self._tox.group_ban_get_time_set(self._number, ban_id)) + bans.append(ban) + + return bans + + bans = property(get_bans) + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/groups/group_ban.py b/toxygen/groups/group_ban.py new file mode 100644 index 0000000..89ecc7e --- /dev/null +++ b/toxygen/groups/group_ban.py @@ -0,0 +1,23 @@ + + +class GroupBan: + + def __init__(self, ban_id, ban_target, ban_time): + self._ban_id = ban_id + self._ban_target = ban_target + self._ban_time = ban_time + + def get_ban_id(self): + return self._ban_id + + ban_id = property(get_ban_id) + + def get_ban_target(self): + return self._ban_target + + ban_target = property(get_ban_target) + + def get_ban_time(self): + return self._ban_time + + ban_time = property(get_ban_time) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 4cf1368..19b53cf 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -15,7 +15,7 @@ class GroupsService(tox_save.ToxSave): self._peers_list_widget = main_screen.peers_list self._widgets_factory_provider = widgets_factory_provider self._group_invites = [] - self._peer_screen = self._management_screen = None + self._screen = None def set_tox(self, tox): super().set_tox(tox) @@ -114,8 +114,8 @@ class GroupsService(tox_save.ToxSave): def show_group_management_screen(self, group): widgets_factory = self._get_widgets_factory() - self._management_screen = widgets_factory.create_group_management_screen(group) - self._management_screen.show() + self._screen = widgets_factory.create_group_management_screen(group) + self._screen.show() def set_group_password(self, group, password): if group.password == password: @@ -151,10 +151,10 @@ class GroupsService(tox_save.ToxSave): group = self._get_group_by_public_key(chat_id) self_peer = group.get_self_peer() if self_peer.id != peer_id: - self._peer_screen = widgets_factory.create_peer_screen_window(group, peer_id) + self._screen = widgets_factory.create_peer_screen_window(group, peer_id) else: - self._peer_screen = widgets_factory.create_self_peer_screen_window(group) - self._peer_screen.show() + self._screen = widgets_factory.create_self_peer_screen_window(group) + self._screen.show() # ----------------------------------------------------------------------------------------------------------------- # Peers actions @@ -177,6 +177,26 @@ class GroupsService(tox_save.ToxSave): self_peer.status = status self.generate_peers_list() + # ----------------------------------------------------------------------------------------------------------------- + # Bans support + # ----------------------------------------------------------------------------------------------------------------- + + def show_bans_list(self, group): + widgets_factory = self._get_widgets_factory() + self._screen = widgets_factory.create_groups_bans_screen(group) + self._screen.show() + + def ban_peer(self, group, peer_id, ban_type): + self._tox.group_mod_ban_peer(group.number, peer_id, ban_type) + group.remove_peer(peer_id) + + def kick_peer(self, group, peer_id): + self._tox.group_mod_remove_peer(group.number, peer_id) + group.remove_peer(peer_id) + + def cancel_ban(self, group_number, ban_id): + self._tox.group_mod_remove_ban(group_number, ban_id) + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/ui/group_bans_widgets.py b/toxygen/ui/group_bans_widgets.py new file mode 100644 index 0000000..45a5f2b --- /dev/null +++ b/toxygen/ui/group_bans_widgets.py @@ -0,0 +1,62 @@ +from ui.widgets import CenteredWidget +from PyQt5 import uic, QtWidgets, QtCore +import utils.util as util +import utils.ui as util_ui + + +class GroupBanItem(QtWidgets.QWidget): + + def __init__(self, ban, cancel_ban, parent=None): + super().__init__(parent) + self._ban = ban + self._cancel_ban = cancel_ban + + def _update_ui(self): + self._retranslate_ui() + + self.banTargetLabel.setText(self._ban.target) + ban_time = self._ban.ban_time + self.banTimeLabel.setText(util.unix_time_to_long_str(ban_time)) + + self.cancelPushButton.clicked.connect(self._cancel_ban) + + def _retranslate_ui(self): + self.cancelPushButton.setText(util_ui.tr('Cancel ban')) + + def _cancel_ban(self): + self._cancel_ban(self._ban.ban_id) + + +class GroupBansScreen(CenteredWidget): + + def __init__(self, groups_service, group): + super().__init__() + self._groups_service = groups_service + self._group = group + + uic.loadUi(util.get_views_path('bans_list_screen'), self) + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self._refresh_bans_list() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Bans list for group "{}"').format(self._group.name)) + + def _refresh_bans_list(self): + self.bansListWidget.clear() + for ban in self._group.bans: + self._create_ban_item(ban) + + def _create_ban_item(self, ban): + item = GroupBanItem(ban, self._on_ban_cancelled, self.bansListWidget) + elem = QtWidgets.QListWidgetItem() + elem.setSizeHint(QtCore.QSize(item.width(), item.height())) + self.bansListWidget.addItem(elem) + self.bansListWidget.setItemWidget(elem, item) + + def _on_ban_cancelled(self, ban_id): + self._groups_service.cancel_ban(self._group.number, ban_id) + self._refresh_bans_list() diff --git a/toxygen/ui/peer_screen.py b/toxygen/ui/peer_screen.py index 651d998..687c818 100644 --- a/toxygen/ui/peer_screen.py +++ b/toxygen/ui/peer_screen.py @@ -1,8 +1,9 @@ from ui.widgets import CenteredWidget -from PyQt5 import QtCore, QtWidgets, uic +from PyQt5 import uic import utils.util as util import utils.ui as util_ui from ui.contact_items import * +import wrapper.toxcore_enums_and_consts as consts class PeerScreen(CenteredWidget): @@ -35,9 +36,12 @@ class PeerScreen(CenteredWidget): self.sendPrivateMessagePushButton.clicked.connect(self._send_private_message) self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key) self.roleNameLabel.setText(self._get_role_name()) - can_change_role = self._can_change_role() - self.rolesComboBox.setVisible(can_change_role) - self.roleNameLabel.setVisible(not can_change_role) + can_change_role_or_ban = self._can_change_role_or_ban() + self.rolesComboBox.setVisible(can_change_role_or_ban) + self.roleNameLabel.setVisible(not can_change_role_or_ban) + self.banGroupBox.setEnabled(can_change_role_or_ban) + self.banPushButton.clicked.connect(self._ban_peer) + self.kickPushButton.clicked.connect(self._kick_peer) self._retranslate_ui() @@ -49,7 +53,12 @@ class PeerScreen(CenteredWidget): self.roleLabel.setText(util_ui.tr('Role:')) self.copyPublicKeyPushButton.setText(util_ui.tr('Copy public key')) self.sendPrivateMessagePushButton.setText(util_ui.tr('Send private message')) - self.banGroupBox.setTitle(util_ui.tr('Moderation')) + self.banPushButton.setText(util_ui.tr('Ban peer')) + self.kickPushButton.setText(util_ui.tr('Kick peer')) + self.banGroupBox.setTitle(util_ui.tr('Ban peer')) + self.ipBanRadioButton.setText(util_ui.tr('IP')) + self.nickBanRadioButton.setText(util_ui.tr('Nickname')) + self.pkBanRadioButton.setText(util_ui.tr('Public key')) self.rolesComboBox.clear() index = self._group.get_self_peer().role @@ -58,7 +67,7 @@ class PeerScreen(CenteredWidget): self.rolesComboBox.addItem(role) self.rolesComboBox.setCurrentIndex(self._peer.role - index - 1) - def _can_change_role(self): + def _can_change_role_or_ban(self): self_peer = self._group.get_self_peer() if self_peer.role > TOX_GROUP_ROLE['MODERATOR']: return False @@ -85,3 +94,17 @@ class PeerScreen(CenteredWidget): def _copy_public_key(self): clipboard = QtWidgets.QApplication.clipboard() clipboard.setText(self._peer.public_key) + + def _ban_peer(self): + ban_type = self._get_ban_type() + self._groups_service.ban_peer(self._group, self._peer.id, ban_type) + + def _kick_peer(self): + self._groups_service.kick_peer(self._group, self._peer.id) + + def _get_ban_type(self): + if self.ipBanRadioButton.isChecked(): + return consts.TOX_GROUP_BAN_TYPE['IP_PORT'] + elif self.nickRadioButton.isCHecked(): + return consts.TOX_GROUP_BAN_TYPE['NICK'] + return consts.TOX_GROUP_BAN_TYPE['PUBLIC_KEY'] diff --git a/toxygen/ui/views/bans_list_screen.ui b/toxygen/ui/views/bans_list_screen.ui new file mode 100644 index 0000000..16339d8 --- /dev/null +++ b/toxygen/ui/views/bans_list_screen.ui @@ -0,0 +1,29 @@ + + + Form + + + + 0 + 0 + 500 + 375 + + + + Form + + + + + 0 + 0 + 500 + 375 + + + + + + + diff --git a/toxygen/ui/views/gc_ban_item.ui b/toxygen/ui/views/gc_ban_item.ui new file mode 100644 index 0000000..20e1e16 --- /dev/null +++ b/toxygen/ui/views/gc_ban_item.ui @@ -0,0 +1,58 @@ + + + Form + + + + 0 + 0 + 500 + 100 + + + + Form + + + + + 320 + 30 + 161 + 41 + + + + PushButton + + + + + + 20 + 20 + 200 + 20 + + + + TextLabel + + + + + + 20 + 50 + 200 + 20 + + + + TextLabel + + + + + + diff --git a/toxygen/ui/views/peer_screen.ui b/toxygen/ui/views/peer_screen.ui index f6b13b3..a66ec11 100644 --- a/toxygen/ui/views/peer_screen.ui +++ b/toxygen/ui/views/peer_screen.ui @@ -76,6 +76,74 @@ GroupBox + + + + 380 + 50 + 101 + 41 + + + + PushButton + + + + + + 40 + 40 + 251 + 23 + + + + RadioButton + + + true + + + + + + 40 + 80 + 251 + 23 + + + + RadioButton + + + + + + 40 + 120 + 251 + 23 + + + + RadioButton + + + + + + 380 + 100 + 101 + 41 + + + + PushButton + + diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index e394684..b146088 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -5,6 +5,7 @@ from ui.peer_screen import * from ui.self_peer_screen import * from ui.group_invites_widgets import * from ui.group_management_screen import * +from ui.group_bans_widgets import * class WidgetsFactory: @@ -86,3 +87,6 @@ class WidgetsFactory: def create_group_management_screen(self, group): return GroupManagementScreen(self._groups_service, group) + + def create_groups_bans_screen(self, group): + return GroupBansScreen(self._groups_service, group) diff --git a/toxygen/utils/util.py b/toxygen/utils/util.py index 0f718ef..3c6910e 100644 --- a/toxygen/utils/util.py +++ b/toxygen/utils/util.py @@ -4,6 +4,7 @@ import shutil import sys import re import platform +import datetime def cached(func): @@ -21,10 +22,10 @@ def cached(func): def log(data): try: - with open(curr_directory() + '/logs.log', 'a') as fl: + with open(join_path(curr_directory(), 'logs.log'), 'a') as fl: fl.write(str(data) + '\n') - except: - pass + except Exception as ex: + print(ex) def curr_directory(current_file=None): @@ -144,6 +145,12 @@ def time_offset(): return result +def unix_time_to_long_str(unix_time): + date_time = datetime.datetime.utcfromtimestamp(unix_time) + + return date_time.strftime('%Y-%m-%d %H:%M:%S') + + @cached def is_64_bit(): return sys.maxsize > 2 ** 32 diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 0e3310d..a2abf6a 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -2468,9 +2468,11 @@ class Tox: """ error = c_int() - result = Tox.libtoxcore.tox_group_ban_get_list(self._tox_pointer, group_number, POINTER(c_uint32)( - create_string_buffer(sizeof(c_uint32) * self.group_ban_get_list_size(group_number)), byref(error))) - return result + bans_list_size = self.group_ban_get_list_size(group_number) + bans_list = create_string_buffer(sizeof(c_uint32) * bans_list_size) + bans_list = POINTER(c_uint32)(bans_list) + result = Tox.libtoxcore.tox_group_ban_get_list(self._tox_pointer, group_number, bans_list, byref(error)) + return bans_list[:bans_list_size] def group_ban_get_target_size(self, group_number, ban_id): """ diff --git a/toxygen/wrapper/toxcore_enums_and_consts.py b/toxygen/wrapper/toxcore_enums_and_consts.py index 7c6478c..1e134f9 100644 --- a/toxygen/wrapper/toxcore_enums_and_consts.py +++ b/toxygen/wrapper/toxcore_enums_and_consts.py @@ -914,11 +914,11 @@ TOX_ERR_GROUP_BAN_QUERY = { TOX_GROUP_BAN_TYPE = { - 'TOX_GROUP_BAN_TYPE_IP_PORT': 0, + 'IP_PORT': 0, - 'TOX_GROUP_BAN_TYPE_PUBLIC_KEY': 1, + 'PUBLIC_KEY': 1, - 'TOX_GROUP_BAN_TYPE_NICK': 2 + 'NICK': 2 } From 1a0bd9deee3f37c9228035c81e1e5232636f4ad4 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 6 Aug 2018 00:52:01 +0300 Subject: [PATCH 119/217] dockerfile for linux builds --- build/Dockerfile | 4 +++- build/build.sh | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index 04c59e3..0b45358 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -7,5 +7,7 @@ cd toxcore && mkdir _build && cd _build && \ cmake .. && make && make install RUN apt-get install portaudio19-dev python3-pyqt5 python3-pyaudio python3-pip -y && \ -pip3 install --upgrade pip && \ pip3 install numpy pydenticon opencv-python pyinstaller + +RUN useradd -ms /bin/bash toxygen +USER toxygen diff --git a/build/build.sh b/build/build.sh index 97f8e60..b415054 100644 --- a/build/build.sh +++ b/build/build.sh @@ -1,19 +1,29 @@ #!/usr/bin/env bash + git clone https://github.com/toxygen-project/toxygen.git --branch=next_gen cd toxygen/toxygen -ln -sf /usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/ /usr/bin/ -pyinstaller --windowed --icon=images/icon.ico --hidden-import=PyQt5.uic.plugins main.py + +pyinstaller --windowed --icon=images/icon.ico main.py + cp -r styles dist/main/ cp -r plugins dist/main/ +mkdir -p dist/main/ui/views +cp -r ui/views dist/main/ui/ cp -r sounds dist/main/ cp -r smileys dist/main/ cp -r stickers dist/main/ cp -r bootstrap dist/main/ cp -r images dist/main/ +cp -r translations dist/main/ + cd dist mv main toxygen cd toxygen mv main toxygen +wget -O updater https://github.com/toxygen-project/toxygen_updater/releases/download/v0.1/toxygen_updater_linux_64 +echo "[Paths]" >> qt.conf +echo "Prefix = PyQt5/Qt" >> qt.conf cd .. -tar -zcvf toxygen_linux_64.tar.gz toxygen + +tar -zcvf toxygen_linux_64.tar.gz toxygen > /dev/null rm -rf toxygen From 318c9c942df5ced0a351a612a020b70af74a5b93 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 7 Aug 2018 00:41:27 +0300 Subject: [PATCH 120/217] ngc bug fixes --- build/build.sh | 1 + toxygen/app.py | 12 ++++++++---- toxygen/history/history.py | 2 +- toxygen/ui/peer_screen.py | 2 +- toxygen/ui/views/peer_screen.ui | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/build/build.sh b/build/build.sh index b415054..522d6b2 100644 --- a/build/build.sh +++ b/build/build.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash +cd ~ git clone https://github.com/toxygen-project/toxygen.git --branch=next_gen cd toxygen/toxygen diff --git a/toxygen/app.py b/toxygen/app.py index 80c26ea..cfdac4f 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -123,10 +123,14 @@ class App: if self._settings['theme'] == 'dark': return for theme in self._settings.built_in_themes().keys(): - if self._settings['theme'] == theme: - with open(util.curr_directory(__file__) + self._settings.built_in_themes()[theme]) as fl: - style = fl.read() - self._app.setStyleSheet(style) + if self._settings['theme'] != theme: + continue + theme_path = self._settings.built_in_themes()[theme] + file_path = util.join_path(util.get_styles_directory(), theme_path) + with open(file_path) as fl: + style = fl.read() + self._app.setStyleSheet(style) + break def _load_login_screen_translations(self): current_language, supported_languages = self._get_languages() diff --git a/toxygen/history/history.py b/toxygen/history/history.py index e6f5e44..c430117 100644 --- a/toxygen/history/history.py +++ b/toxygen/history/history.py @@ -68,7 +68,7 @@ class History: messages.reverse() messages = messages[self._messages.count():self._messages.count() + PAGE_SIZE] for message in messages: - if message.get_type() in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']): # text message + if message.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): # text message self._create_message_item(message) elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: # file transfer if message.get_status() is None: diff --git a/toxygen/ui/peer_screen.py b/toxygen/ui/peer_screen.py index 687c818..acc93ee 100644 --- a/toxygen/ui/peer_screen.py +++ b/toxygen/ui/peer_screen.py @@ -105,6 +105,6 @@ class PeerScreen(CenteredWidget): def _get_ban_type(self): if self.ipBanRadioButton.isChecked(): return consts.TOX_GROUP_BAN_TYPE['IP_PORT'] - elif self.nickRadioButton.isCHecked(): + elif self.nickBanRadioButton.isChecked(): return consts.TOX_GROUP_BAN_TYPE['NICK'] return consts.TOX_GROUP_BAN_TYPE['PUBLIC_KEY'] diff --git a/toxygen/ui/views/peer_screen.ui b/toxygen/ui/views/peer_screen.ui index a66ec11..e8e9e31 100644 --- a/toxygen/ui/views/peer_screen.ui +++ b/toxygen/ui/views/peer_screen.ui @@ -105,7 +105,7 @@ true - + 40 From 4ecf666b2f5f66bf7563d6234b9e1f03308b9eed Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 9 Aug 2018 23:30:05 +0300 Subject: [PATCH 121/217] various fixes --- toxygen/contacts/contact.py | 2 +- toxygen/history/history.py | 2 +- toxygen/messenger/messenger.py | 3 ++- toxygen/ui/main_screen.py | 8 +------- toxygen/utils/util.py | 1 + toxygen/wrapper/libtox.py | 28 +++++++++++++++------------- 6 files changed, 21 insertions(+), 23 deletions(-) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index 418ac15..ecf944d 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -135,7 +135,7 @@ class Contact(basecontact.BaseContact): def mark_as_sent(self, tox_message_id): try: - message = list(filter(lambda m: m.author.type == MESSAGE_AUTHOR['NOT_SENT'] + message = list(filter(lambda m: m.author is not None and m.author.type == MESSAGE_AUTHOR['NOT_SENT'] and m.tox_message_id == tox_message_id, self._corr))[0] message.mark_as_sent() except Exception as ex: diff --git a/toxygen/history/history.py b/toxygen/history/history.py index c430117..4850f7d 100644 --- a/toxygen/history/history.py +++ b/toxygen/history/history.py @@ -48,7 +48,7 @@ class History: def delete_message(self, message): contact = self._contacts_manager.get_curr_contact() - if message.type in (MESSAGE_TYPE['NORMAL'], MESSAGE_TYPE['ACTION']): + if message.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): if message.is_saved(): self._db.delete_message(contact.tox_id, message.id) contact.delete_message(message.message_id) diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index cce619d..9ee86dd 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -224,7 +224,8 @@ class Messenger(tox_save.ToxSave): return message = util_ui.tr('User {} is now known as {}') message = message.format(old_name, new_name) - friend.actions = True + if self._contacts_manager.is_friend_active(friend.number): + friend.actions = True self._add_info_message(friend.number, message) # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 9b80576..7b2b17d 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -645,7 +645,7 @@ class MainWindow(QtWidgets.QMainWindow): self._contacts_manager.delete_friend(num) def block_friend(self, num): - friend = self._contacts_managere.get_contact(num) + friend = self._contacts_manager.get_contact(num) self._contacts_manager.block_user(friend.tox_id) @staticmethod @@ -656,12 +656,6 @@ class MainWindow(QtWidgets.QMainWindow): def clear_history(self, num): self._contacts_manager.clear_history(num) - def leave_gc(self, num): - self.profile.leave_gc(num) - - def set_title(self, num): - self.profile.set_title(num) - def auto_accept(self, num, value): tox_id = self._contacts_manager.friend_public_key(num) if value: diff --git a/toxygen/utils/util.py b/toxygen/utils/util.py index 3c6910e..5bd5c3a 100644 --- a/toxygen/utils/util.py +++ b/toxygen/utils/util.py @@ -165,5 +165,6 @@ def is_re_valid(regex): return True +@cached def get_platform(): return platform.system() diff --git a/toxygen/wrapper/libtox.py b/toxygen/wrapper/libtox.py index d5f599a..01d41f1 100644 --- a/toxygen/wrapper/libtox.py +++ b/toxygen/wrapper/libtox.py @@ -1,4 +1,3 @@ -from platform import system from ctypes import CDLL import utils.util as util @@ -6,16 +5,17 @@ import utils.util as util class LibToxCore: def __init__(self): - if system() == 'Windows': - self._libtoxcore = CDLL(util.curr_directory() + '/libs/libtox.dll') - elif system() == 'Darwin': + platform = util.get_platform() + if platform == 'Windows': + self._libtoxcore = CDLL(util.join_path(util.get_libs_directory(), 'libtox.dll')) + elif platform == 'Darwin': self._libtoxcore = CDLL('libtoxcore.dylib') else: # libtoxcore and libsodium must be installed in your os try: self._libtoxcore = CDLL('libtoxcore.so') except: - self._libtoxcore = CDLL(util.curr_directory() + '/libs/libtoxcore.so') + self._libtoxcore = CDLL(util.join_path(util.get_libs_directory(), 'libtoxcore.so')) def __getattr__(self, item): return self._libtoxcore.__getattr__(item) @@ -24,17 +24,18 @@ class LibToxCore: class LibToxAV: def __init__(self): - if system() == 'Windows': + platform = util.get_platform() + if platform == 'Windows': # on Windows av api is in libtox.dll - self._libtoxav = CDLL(util.curr_directory() + '/libs/libtox.dll') - elif system() == 'Darwin': + self._libtoxav = CDLL(util.join_path(util.get_libs_directory(), 'libtox.dll')) + elif platform == 'Darwin': self._libtoxav = CDLL('libtoxcore.dylib') else: # /usr/lib/libtoxcore.so must exists try: self._libtoxav = CDLL('libtoxcore.so') except: - self._libtoxav = CDLL(util.curr_directory() + '/libs/libtoxcore.so') + self._libtoxav = CDLL(util.join_path(util.get_libs_directory(), 'libtoxcore.so')) def __getattr__(self, item): return self._libtoxav.__getattr__(item) @@ -43,17 +44,18 @@ class LibToxAV: class LibToxEncryptSave: def __init__(self): - if system() == 'Windows': + platform = util.get_platform() + if platform == 'Windows': # on Windows profile encryption api is in libtox.dll - self._lib_tox_encrypt_save = CDLL(util.curr_directory() + '/libs/libtox.dll') - elif system() == 'Darwin': + self._lib_tox_encrypt_save = CDLL(util.join_path(util.get_libs_directory(), 'libtox.dll')) + elif platform == 'Darwin': self._lib_tox_encrypt_save = CDLL('libtoxcore.dylib') else: # /usr/lib/libtoxcore.so must exists try: self._lib_tox_encrypt_save = CDLL('libtoxcore.so') except: - self._lib_tox_encrypt_save = CDLL(util.curr_directory() + '/libs/libtoxcore.so') + self._lib_tox_encrypt_save = CDLL(util.join_path(util.get_libs_directory(), 'libtoxcore.so')) def __getattr__(self, item): return self._lib_tox_encrypt_save.__getattr__(item) From 85ea9ab6e815a44017fc577a8537b74cc4e800e0 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 11 Aug 2018 00:30:33 +0300 Subject: [PATCH 122/217] fixes for messaging, contacts filtering etc --- toxygen/app.py | 19 ++++++++++--------- toxygen/contacts/contacts_manager.py | 10 ++++++---- toxygen/groups/groups_service.py | 1 + toxygen/messenger/messages.py | 7 +++++-- toxygen/ui/main_screen.py | 2 +- toxygen/ui/menu.py | 2 +- toxygen/user_data/profile_manager.py | 7 +++---- 7 files changed, 27 insertions(+), 21 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index cfdac4f..a370a91 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -134,12 +134,13 @@ class App: def _load_login_screen_translations(self): current_language, supported_languages = self._get_languages() - if current_language in supported_languages: - lang_path = supported_languages[current_language] - translator = QtCore.QTranslator() - translator.load(util.get_translations_directory() + lang_path) - self._app.installTranslator(translator) - self._app.translator = translator + if current_language not in supported_languages: + return + lang_path = supported_languages[current_language] + translator = QtCore.QTranslator() + translator.load(util.get_translations_directory() + lang_path) + self._app.installTranslator(translator) + self._app.translator = translator def _load_icon(self): icon_file = os.path.join(util.get_images_directory(), 'icon.png') @@ -237,11 +238,11 @@ class App: return ls.result def _load_existing_profile(self, profile_path): - self._settings = Settings(self._toxes, profile_path.replace('.tox', '.json')) - self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) + self._profile_manager = ProfileManager(self._toxes, profile_path) data = self._profile_manager.open_profile() if self._toxes.is_data_encrypted(data): data = self._enter_password(data) + self._settings = Settings(self._toxes, profile_path.replace('.tox', '.json')) self._tox = self._create_tox(data) def _create_new_profile(self, profile_name): @@ -264,7 +265,7 @@ class App: if result.password: self._toxes.set_password(result.password) self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) - self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) + self._profile_manager = ProfileManager(self._toxes, profile_path) try: self._save_profile() except Exception as ex: diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 757bfb0..48038ca 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -163,7 +163,7 @@ class ContactsManager(ToxSave): :param filter_str: show contacts which name contains this substring """ filter_str = filter_str.lower() - contact = self.get_curr_contact() + current_contact = self.get_curr_contact() if sorting > 5 or sorting < 0: sorting = 0 @@ -189,7 +189,7 @@ class ContactsManager(ToxSave): else: self._contacts = sorted(self._contacts, key=lambda x: x.name.lower()) - # change item widgets + # change item widgets for index, contact in enumerate(self._contacts): list_item = self._screen.friends_list.item(index) item_widget = self._screen.friends_list.itemWidget(list_item) @@ -203,13 +203,15 @@ class ContactsManager(ToxSave): item = self._screen.friends_list.item(index) item_widget = self._screen.friends_list.itemWidget(item) item.setSizeHint(QtCore.QSize(250, item_widget.height() if friend.visibility else 0)) + # save soring results self._sorting, self._filter_string = sorting, filter_str self._settings['sorting'] = self._sorting self._settings.save() + # update active contact - if contact is not None: - index = self._contacts.index(contact) + if current_contact is not None: + index = self._contacts.index(current_contact) self.set_active(index) def update_filtration(self): diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 19b53cf..941ee1b 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -34,6 +34,7 @@ class GroupsService(tox_save.ToxSave): self._add_new_group_by_number(group_number) group = self._get_group_by_number(group_number) group.status = constants.TOX_USER_STATUS['NONE'] + self._contacts_manager.update_filtration() def join_gc_by_id(self, chat_id, password, nick, status): group_number = self._tox.group_join(chat_id, password, nick, status) diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 57ed9bb..d5882b8 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -28,7 +28,10 @@ class MessageAuthor: def get_type(self): return self._type - type = property(get_type) + def set_type(self, value): + self._type = value + + type = property(get_type, set_type) class Message: @@ -74,7 +77,7 @@ class Message: self._widget = None def mark_as_sent(self): - self._author.author_type = MESSAGE_AUTHOR['ME'] + self._author.type = MESSAGE_AUTHOR['ME'] if self._widget is not None: self._widget.mark_as_sent() diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 7b2b17d..3df3162 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -654,7 +654,7 @@ class MainWindow(QtWidgets.QMainWindow): clipboard.setText(text) def clear_history(self, num): - self._contacts_manager.clear_history(num) + self._history_loader.clear_history(num) def auto_accept(self, num, value): tox_id = self._contacts_manager.friend_public_key(num) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 96067fb..75db280 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -241,7 +241,7 @@ class ProfileSettings(CenteredWidget): util_ui.tr('Use new path')) self._settings.export(directory) self._profile.export_db(directory) - self._profile_manager.export_profile(directory, reply) + self._profile_manager.export_profile(self._settings, directory, reply) def closeEvent(self, event): self._profile.set_name(self.nick.text()) diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index 12e998f..e49f5f4 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -7,8 +7,7 @@ class ProfileManager: """ Class with methods for search, load and save profiles """ - def __init__(self, settings, toxes, path): - self._settings = settings + def __init__(self, toxes, path): self._toxes = toxes self._path = path self._directory = os.path.dirname(path) @@ -38,7 +37,7 @@ class ProfileManager: fl.write(data) print('Profile saved successfully') - def export_profile(self, new_path, use_new_path): + def export_profile(self, settings, new_path, use_new_path): path = new_path + os.path.basename(self._path) with open(self._path, 'rb') as fin: data = fin.read() @@ -49,7 +48,7 @@ class ProfileManager: if use_new_path: self._path = new_path + os.path.basename(self._path) self._directory = new_path - self._settings.update_path(new_path) + settings.update_path(new_path) @staticmethod def find_profiles(): From 0ee8a0ec21ae09a5cd3e6c25dcdc009449a26fa9 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 22 Aug 2018 12:08:00 +0300 Subject: [PATCH 123/217] gc settings screen added --- toxygen/bootstrap/nodes.json | 2 +- toxygen/contacts/contact_menu.py | 3 + toxygen/groups/groups_service.py | 5 ++ ...nt_screen.py => group_settings_widgets.py} | 35 +++++++- toxygen/ui/main_screen.py | 3 +- toxygen/ui/peer_screen.py | 3 +- toxygen/ui/self_peer_screen.py | 5 +- toxygen/ui/views/gc_settings_screen.ui | 83 +++++++++++++++++++ toxygen/ui/widgets_factory.py | 6 +- toxygen/utils/ui.py | 5 ++ 10 files changed, 139 insertions(+), 11 deletions(-) rename toxygen/ui/{group_management_screen.py => group_settings_widgets.py} (55%) create mode 100644 toxygen/ui/views/gc_settings_screen.ui diff --git a/toxygen/bootstrap/nodes.json b/toxygen/bootstrap/nodes.json index 382e000..4f4f011 100644 --- a/toxygen/bootstrap/nodes.json +++ b/toxygen/bootstrap/nodes.json @@ -1 +1 @@ -{"nodes":[{"ipv4":"80.211.19.83","ipv6":"-","port":33445,"public_key":"C78997BEA65096B09EAD41F850116D798A0D4A14D2D8652FCD2495ADDE006523","status_udp":true,"status_tcp":true}]} \ No newline at end of file +{"nodes":[{"ipv4":"80.211.19.83","ipv6":"-","port":33445,"public_key":"A05AFD9C7B785ADBB93DCD55FC8992177A2BA50413AAD7AD8D3442EE5E7ADA21","status_udp":true,"status_tcp":true}]} \ No newline at end of file diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index 7330b35..da1cddb 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -184,6 +184,9 @@ class GroupMenuGenerator(BaseContactMenuGenerator): .with_optional_action(util_ui.tr('Manage group'), lambda: groups_service.show_group_management_screen(self._contact), self._contact.is_self_founder()) + .with_optional_action(util_ui.tr('Group settings'), + lambda: groups_service.show_group_settings_screen(self._contact), + not self._contact.is_self_founder()) .with_optional_action(util_ui.tr('Set topic'), lambda: groups_service.set_group_topic(self._contact), self._contact.is_self_moderator_or_founder()) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 941ee1b..058e7ea 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -118,6 +118,11 @@ class GroupsService(tox_save.ToxSave): self._screen = widgets_factory.create_group_management_screen(group) self._screen.show() + def show_group_settings_screen(self, group): + widgets_factory = self._get_widgets_factory() + self._screen = widgets_factory.create_group_settings_screen(group) + self._screen.show() + def set_group_password(self, group, password): if group.password == password: return diff --git a/toxygen/ui/group_management_screen.py b/toxygen/ui/group_settings_widgets.py similarity index 55% rename from toxygen/ui/group_management_screen.py rename to toxygen/ui/group_settings_widgets.py index bbc2629..c32168b 100644 --- a/toxygen/ui/group_management_screen.py +++ b/toxygen/ui/group_settings_widgets.py @@ -25,9 +25,9 @@ class GroupManagementScreen(CenteredWidget): def _retranslate_ui(self): self.setWindowTitle(util_ui.tr('Group "{}"').format(self._group.name)) - self.passwordLabel.setText(util_ui.tr('Password')) + self.passwordLabel.setText(util_ui.tr('Password:')) self.peerLimitLabel.setText(util_ui.tr('Peer limit:')) - self.privacyStateLabel.setText(util_ui.tr('Privacy state')) + self.privacyStateLabel.setText(util_ui.tr('Privacy state:')) self.savePushButton.setText(util_ui.tr('Save')) self.privacyStateComboBox.clear() @@ -44,3 +44,34 @@ class GroupManagementScreen(CenteredWidget): self._groups_service.set_group_peers_limit(self._group, peers_limit) self.close() + + +class GroupSettingsScreen(CenteredWidget): + + def __init__(self, group): + super().__init__() + self._group = group + + uic.loadUi(util.get_views_path('gc_settings_screen'), self) + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self.copyPasswordPushButton.clicked.connect(self._copy_password) + self.copyPasswordPushButton.setEnabled(bool(self._group.password)) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Group "{}"').format(self._group.name)) + if self._group.password: + password_label_text = '{} {}'.format(util_ui.tr('Password:'), self._group.password) + else: + password_label_text = util_ui.tr('Password is not set') + self.passwordLabel.setText(password_label_text) + self.peerLimitLabel.setText('{} {}'.format(util_ui.tr('Peer limit:'), self._group.peers_limit)) + privacy_state = util_ui.tr('Private') if self._group.is_private else util_ui.tr('Public') + self.privacyStateLabel.setText('{} {}'.format(util_ui.tr('Privacy state:'), privacy_state)) + self.copyPasswordPushButton.setText(util_ui.tr('Copy password')) + + def _copy_password(self): + util_ui.copy_to_clipboard(self._group.password) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 3df3162..afb3ef9 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -650,8 +650,7 @@ class MainWindow(QtWidgets.QMainWindow): @staticmethod def copy_text(text): - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(text) + util_ui.copy_to_clipboard(text) def clear_history(self, num): self._history_loader.clear_history(num) diff --git a/toxygen/ui/peer_screen.py b/toxygen/ui/peer_screen.py index acc93ee..f366aad 100644 --- a/toxygen/ui/peer_screen.py +++ b/toxygen/ui/peer_screen.py @@ -92,8 +92,7 @@ class PeerScreen(CenteredWidget): self.close() def _copy_public_key(self): - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(self._peer.public_key) + util_ui.copy_to_clipboard(self._peer.public_key) def _ban_peer(self): ban_type = self._get_ban_type() diff --git a/toxygen/ui/self_peer_screen.py b/toxygen/ui/self_peer_screen.py index ebc1426..cf252d3 100644 --- a/toxygen/ui/self_peer_screen.py +++ b/toxygen/ui/self_peer_screen.py @@ -1,5 +1,5 @@ from ui.widgets import CenteredWidget, LineEdit -from PyQt5 import QtCore, QtWidgets, uic +from PyQt5 import uic import utils.util as util import utils.ui as util_ui from ui.contact_items import * @@ -63,5 +63,4 @@ class SelfPeerScreen(CenteredWidget): self.close() def _copy_public_key(self): - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(self._peer.public_key) + util_ui.copy_to_clipboard(self._peer.public_key) diff --git a/toxygen/ui/views/gc_settings_screen.ui b/toxygen/ui/views/gc_settings_screen.ui new file mode 100644 index 0000000..526c156 --- /dev/null +++ b/toxygen/ui/views/gc_settings_screen.ui @@ -0,0 +1,83 @@ + + + Form + + + + 0 + 0 + 400 + 220 + + + + + 400 + 220 + + + + + 400 + 220 + + + + Form + + + + + 10 + 20 + 380 + 20 + + + + TextLabel + + + + + + 10 + 60 + 380 + 40 + + + + PushButton + + + + + + 10 + 120 + 380 + 20 + + + + TextLabel + + + + + + 10 + 160 + 380 + 20 + + + + TextLabel + + + + + + diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index b146088..aa70861 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -4,7 +4,7 @@ from ui.groups_widgets import * from ui.peer_screen import * from ui.self_peer_screen import * from ui.group_invites_widgets import * -from ui.group_management_screen import * +from ui.group_settings_widgets import * from ui.group_bans_widgets import * @@ -88,5 +88,9 @@ class WidgetsFactory: def create_group_management_screen(self, group): return GroupManagementScreen(self._groups_service, group) + @staticmethod + def create_group_settings_screen(group): + return GroupSettingsScreen(group) + def create_groups_bans_screen(self, group): return GroupBansScreen(self._groups_service, group) diff --git a/toxygen/utils/ui.py b/toxygen/utils/ui.py index cdb5f9a..d2d7122 100644 --- a/toxygen/utils/ui.py +++ b/toxygen/utils/ui.py @@ -46,4 +46,9 @@ def close_all_windows(): QtWidgets.QApplication.closeAllWindows() +def copy_to_clipboard(text): + clipboard = QtWidgets.QApplication.clipboard() + clipboard.setText(text) + + # TODO: all dialogs From c0a34d3e140994bd78591f8f044c25a4b81d0a21 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 22 Aug 2018 13:36:22 +0300 Subject: [PATCH 124/217] groups - nicks auto complete --- toxygen/contacts/contacts_manager.py | 16 ++++++++++++++++ toxygen/contacts/group_chat.py | 5 +++++ toxygen/ui/main_screen.py | 4 ++-- toxygen/ui/main_screen_widgets.py | 19 ++++++++++++++----- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 48038ca..23dc61e 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -386,6 +386,22 @@ class ContactsManager(ToxSave): num = self._contacts.index(contact) self._delete_contact(num) + def get_gc_peer_name(self, name): + group = self.get_curr_contact() + + names = sorted(group.get_peers_names()) + if name in names: # return next nick + index = names.index(name) + index = (index + 1) % len(names) + + return names[index] + + suggested_names = list(filter(lambda x: x.startswith(name), names)) + if not len(suggested_names): + return '\t' + + return suggested_names[0] + # ----------------------------------------------------------------------------------------------------------------- # Friend requests # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index f173695..1ef6742 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -98,6 +98,11 @@ class GroupChat(contact.Contact, ToxSave): def remove_all_peers_except_self(self): self._peers = self._peers[:1] + def get_peers_names(self): + peers_names = map(lambda p: p.name, self._peers) + + return list(peers_names) + def get_peers(self): return self._peers[:] diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index afb3ef9..6217b47 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -1,5 +1,5 @@ from ui.contact_items import * -from ui.widgets import MultilineEdit, ComboBox +from ui.widgets import MultilineEdit from ui.main_screen_widgets import * import utils.util as util import utils.ui as util_ui @@ -37,7 +37,7 @@ class MainWindow(QtWidgets.QMainWindow): self._toxes = toxes self._messenger = messenger self._contacts_manager.active_contact_changed.add_callback(self._new_contact_selected) - self.messageEdit.set_messenger(messenger) + self.messageEdit.set_dependencies(messenger, contacts_manager) self.update_gc_invites_button_state() diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index 0945b0c..8b7bf52 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -1,6 +1,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit import urllib +import re import utils.util as util import utils.ui as util_ui from stickers.stickers import load_stickers @@ -11,14 +12,15 @@ class MessageArea(QtWidgets.QPlainTextEdit): def __init__(self, parent, form): super().__init__(parent) - self._messenger = None + self._messenger = self._contacts_manager = None self.parent = form self.setAcceptDrops(True) self._timer = QtCore.QTimer(self) self._timer.timeout.connect(lambda: self._messenger.send_typing(False)) - def set_messenger(self, messenger): + def set_dependencies(self, messenger, contacts_manager): self._messenger = messenger + self._contacts_manager = contacts_manager def keyPressEvent(self, event): if event.matches(QtGui.QKeySequence.Paste): @@ -39,10 +41,17 @@ class MessageArea(QtWidgets.QPlainTextEdit): self._messenger.send_message() elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText(): self.appendPlainText(self._messenger.get_last_message()) - elif event.key() == QtCore.Qt.Key_Tab and not self._messenger.is_active_a_friend(): + elif event.key() == QtCore.Qt.Key_Tab and self._contacts_manager.is_active_a_group(): text = self.toPlainText() - pos = self.textCursor().position() - self.insertPlainText(self._messenger.get_gc_peer_name(text[:pos])) + text_cursor = self.textCursor() + pos = text_cursor.position() + current_word = re.split("\s+", text[:pos])[-1] + start_index = text.rindex(current_word, 0, pos) + peer_name = self._contacts_manager.get_gc_peer_name(current_word) + self.setPlainText(text[:start_index] + peer_name + text[pos:]) + new_pos = start_index + len(peer_name) + text_cursor.setPosition(new_pos, QtGui.QTextCursor.MoveAnchor) + self.setTextCursor(text_cursor) else: self._messenger.send_typing(True) if self._timer.isActive(): From ce19efe34065710b045b4d64b5fed1d15b05480e Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 23 Aug 2018 16:02:29 +0300 Subject: [PATCH 125/217] ban fixes --- toxygen/bootstrap/nodes.json | 2 +- toxygen/contacts/contact_menu.py | 5 ++--- toxygen/contacts/group_chat.py | 7 +++++-- toxygen/groups/groups_service.py | 2 -- toxygen/ui/group_bans_widgets.py | 16 +++++++++++----- toxygen/ui/peer_screen.py | 2 ++ toxygen/ui/views/gc_ban_item.ui | 10 +++++----- toxygen/wrapper/tox.py | 14 ++++++++++++++ toxygen/wrapper/toxcore_enums_and_consts.py | 1 - 9 files changed, 40 insertions(+), 19 deletions(-) diff --git a/toxygen/bootstrap/nodes.json b/toxygen/bootstrap/nodes.json index 4f4f011..5314998 100644 --- a/toxygen/bootstrap/nodes.json +++ b/toxygen/bootstrap/nodes.json @@ -1 +1 @@ -{"nodes":[{"ipv4":"80.211.19.83","ipv6":"-","port":33445,"public_key":"A05AFD9C7B785ADBB93DCD55FC8992177A2BA50413AAD7AD8D3442EE5E7ADA21","status_udp":true,"status_tcp":true}]} \ No newline at end of file +{"nodes":[{"ipv4":"80.211.19.83","ipv6":"-","port":33445,"public_key":"A2D7BF17C10A12C339B9F4E8DD77DEEE8457D580535A6F0D0F9AF04B8B4C4420","status_udp":true,"status_tcp":true}]} \ No newline at end of file diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index da1cddb..b1c23f5 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -190,9 +190,8 @@ class GroupMenuGenerator(BaseContactMenuGenerator): .with_optional_action(util_ui.tr('Set topic'), lambda: groups_service.set_group_topic(self._contact), self._contact.is_self_moderator_or_founder()) - .with_optional_action(util_ui.tr('Bans list'), - lambda: groups_service.show_bans_list(self._contact), - self._contact.is_self_moderator_or_founder()) + .with_action(util_ui.tr('Bans list'), + lambda: groups_service.show_bans_list(self._contact)) .with_action(util_ui.tr('Reconnect to group'), lambda: groups_service.reconnect_to_group(self._contact.number)) .with_optional_action(util_ui.tr('Disconnect from group'), diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 1ef6742..19ebc8e 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -82,8 +82,11 @@ class GroupChat(contact.Contact, ToxSave): self._peers.append(peer) def remove_peer(self, peer_id): - peer = self.get_peer_by_id(peer_id) - self._peers.remove(peer) + if peer_id == self.get_self_peer().id: # we were kicked or banned + self.remove_all_peers_except_self() + else: + peer = self.get_peer_by_id(peer_id) + self._peers.remove(peer) def get_peer_by_id(self, peer_id): peers = list(filter(lambda p: p.id == peer_id, self._peers)) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index 058e7ea..b8fc7cc 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -194,11 +194,9 @@ class GroupsService(tox_save.ToxSave): def ban_peer(self, group, peer_id, ban_type): self._tox.group_mod_ban_peer(group.number, peer_id, ban_type) - group.remove_peer(peer_id) def kick_peer(self, group, peer_id): self._tox.group_mod_remove_peer(group.number, peer_id) - group.remove_peer(peer_id) def cancel_ban(self, group_number, ban_id): self._tox.group_mod_remove_ban(group_number, ban_id) diff --git a/toxygen/ui/group_bans_widgets.py b/toxygen/ui/group_bans_widgets.py index 45a5f2b..b2758c7 100644 --- a/toxygen/ui/group_bans_widgets.py +++ b/toxygen/ui/group_bans_widgets.py @@ -6,19 +6,24 @@ import utils.ui as util_ui class GroupBanItem(QtWidgets.QWidget): - def __init__(self, ban, cancel_ban, parent=None): + def __init__(self, ban, cancel_ban, can_cancel_ban, parent=None): super().__init__(parent) self._ban = ban self._cancel_ban = cancel_ban + self._can_cancel_ban = can_cancel_ban + + uic.loadUi(util.get_views_path('gc_ban_item'), self) + self._update_ui() def _update_ui(self): self._retranslate_ui() - self.banTargetLabel.setText(self._ban.target) + self.banTargetLabel.setText(self._ban.ban_target) ban_time = self._ban.ban_time self.banTimeLabel.setText(util.unix_time_to_long_str(ban_time)) self.cancelPushButton.clicked.connect(self._cancel_ban) + self.cancelPushButton.setEnabled(self._can_cancel_ban) def _retranslate_ui(self): self.cancelPushButton.setText(util_ui.tr('Cancel ban')) @@ -47,11 +52,12 @@ class GroupBansScreen(CenteredWidget): def _refresh_bans_list(self): self.bansListWidget.clear() + can_cancel_ban = self._group.is_self_moderator_or_founder() for ban in self._group.bans: - self._create_ban_item(ban) + self._create_ban_item(ban, can_cancel_ban) - def _create_ban_item(self, ban): - item = GroupBanItem(ban, self._on_ban_cancelled, self.bansListWidget) + def _create_ban_item(self, ban, can_cancel_ban): + item = GroupBanItem(ban, self._on_ban_cancelled, can_cancel_ban, self.bansListWidget) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(item.width(), item.height())) self.bansListWidget.addItem(elem) diff --git a/toxygen/ui/peer_screen.py b/toxygen/ui/peer_screen.py index f366aad..8f2d5ba 100644 --- a/toxygen/ui/peer_screen.py +++ b/toxygen/ui/peer_screen.py @@ -97,9 +97,11 @@ class PeerScreen(CenteredWidget): def _ban_peer(self): ban_type = self._get_ban_type() self._groups_service.ban_peer(self._group, self._peer.id, ban_type) + self.close() def _kick_peer(self): self._groups_service.kick_peer(self._group, self._peer.id) + self.close() def _get_ban_type(self): if self.ipBanRadioButton.isChecked(): diff --git a/toxygen/ui/views/gc_ban_item.ui b/toxygen/ui/views/gc_ban_item.ui index 20e1e16..a57d0e1 100644 --- a/toxygen/ui/views/gc_ban_item.ui +++ b/toxygen/ui/views/gc_ban_item.ui @@ -16,7 +16,7 @@ - 320 + 330 30 161 41 @@ -29,9 +29,9 @@ - 20 + 15 20 - 200 + 305 20 @@ -42,9 +42,9 @@ - 20 + 15 50 - 200 + 305 20 diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index a2abf6a..fcdf266 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -2474,6 +2474,17 @@ class Tox: result = Tox.libtoxcore.tox_group_ban_get_list(self._tox_pointer, group_number, bans_list, byref(error)) return bans_list[:bans_list_size] + def group_ban_get_type(self, group_number, ban_id): + """ + Return the type for the ban list entry designated by ban_id, in the + group designated by the given group number. If either group_number or ban_id is invalid, + the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_type(self._tox_pointer, group_number, ban_id, byref(error)) + return result + def group_ban_get_target_size(self, group_number, ban_id): """ Return the length of the name for the ban list entry designated by ban_id, in the @@ -2498,9 +2509,12 @@ class Tox: error = c_int() size = self.group_ban_get_target_size(group_number, ban_id) target = create_string_buffer(size) + target_type = self.group_ban_get_type(group_number, ban_id) result = Tox.libtoxcore.tox_group_ban_get_target(self._tox_pointer, group_number, ban_id, target, byref(error)) + if target_type == TOX_GROUP_BAN_TYPE['PUBLIC_KEY']: + return bin_to_string(target, size) return str(target[:size], 'utf-8') def group_ban_get_time_set(self, group_number, ban_id): diff --git a/toxygen/wrapper/toxcore_enums_and_consts.py b/toxygen/wrapper/toxcore_enums_and_consts.py index 1e134f9..b34e272 100644 --- a/toxygen/wrapper/toxcore_enums_and_consts.py +++ b/toxygen/wrapper/toxcore_enums_and_consts.py @@ -919,7 +919,6 @@ TOX_GROUP_BAN_TYPE = { 'PUBLIC_KEY': 1, 'NICK': 2 - } TOX_PUBLIC_KEY_SIZE = 32 From 0f9aa4f51561dbca3f1ffd84788eb4a03e81f009 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 23 Aug 2018 23:51:05 +0300 Subject: [PATCH 126/217] refactoring and is_muted fix --- toxygen/bootstrap/bootstrap.py | 30 +++++++++++++-------------- toxygen/contacts/basecontact.py | 31 +++++++++++++++------------- toxygen/contacts/common.py | 9 +++++++++ toxygen/groups/group_peer.py | 36 +++++++++++++++++++++++---------- toxygen/wrapper/tox.py | 2 ++ 5 files changed, 68 insertions(+), 40 deletions(-) diff --git a/toxygen/bootstrap/bootstrap.py b/toxygen/bootstrap/bootstrap.py index 6c34e0e..fad68c4 100644 --- a/toxygen/bootstrap/bootstrap.py +++ b/toxygen/bootstrap/bootstrap.py @@ -20,11 +20,7 @@ class Node: priority = property(get_priority) def get_data(self): - return bytes(self._ip, 'utf-8'), self._port, self._tox_key - - -def _get_nodes_path(): - return join_path(curr_directory(__file__), 'nodes.json') + return self._ip, self._port, self._tox_key def generate_nodes(nodes_count=DEFAULT_NODES_COUNT): @@ -39,14 +35,6 @@ def generate_nodes(nodes_count=DEFAULT_NODES_COUNT): yield node.get_data() -def save_nodes(nodes): - if not nodes: - return - print('Saving nodes...') - with open(_get_nodes_path(), 'wb') as fl: - fl.write(nodes) - - def download_nodes_list(settings): url = 'https://nodes.tox.chat/json' if not settings['download_nodes_list']: @@ -58,7 +46,7 @@ def download_nodes_list(settings): req.add_header('Content-Type', 'application/json') response = urllib.request.urlopen(req) result = response.read() - save_nodes(result) + _save_nodes(result) except Exception as ex: log('TOX nodes loading error: ' + str(ex)) else: # proxy @@ -78,6 +66,18 @@ def download_nodes_list(settings): QtCore.QThread.msleep(1) QtCore.QCoreApplication.processEvents() data = bytes(reply.readAll().data()) - save_nodes(data) + _save_nodes(data) except Exception as ex: log('TOX nodes loading error: ' + str(ex)) + + +def _get_nodes_path(): + return join_path(curr_directory(__file__), 'nodes.json') + + +def _save_nodes(nodes): + if not nodes: + return + print('Saving nodes...') + with open(_get_nodes_path(), 'wb') as fl: + fl.write(nodes) diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index fa709db..c64ffdb 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -39,11 +39,12 @@ class BaseContact: return self._name def set_name(self, value): - if self._name != value: - self._name = value - self._widget.name.setText(self._name) - self._widget.name.repaint() - self._name_changed_event(self._name) + if self._name == value: + return + self._name = value + self._widget.name.setText(self._name) + self._widget.name.repaint() + self._name_changed_event(self._name) name = property(get_name, set_name) @@ -60,11 +61,12 @@ class BaseContact: return self._status_message def set_status_message(self, value): - if self._status_message != value: - self._status_message = value - self._widget.status_message.setText(self._status_message) - self._widget.status_message.repaint() - self._status_message_changed_event(self._status_message) + if self._status_message == value: + return + self._status_message = value + self._widget.status_message.setText(self._status_message) + self._widget.status_message.repaint() + self._status_message_changed_event(self._status_message) status_message = property(get_status_message, set_status_message) @@ -81,10 +83,11 @@ class BaseContact: return self._status def set_status(self, value): - if self._status != value: - self._status = value - self._widget.connection_status.update(value) - self._status_changed_event(self._status) + if self._status == value: + return + self._status = value + self._widget.connection_status.update(value) + self._status_changed_event(self._status) status = property(get_status, set_status) diff --git a/toxygen/contacts/common.py b/toxygen/contacts/common.py index ba5dac6..27750a2 100644 --- a/toxygen/contacts/common.py +++ b/toxygen/contacts/common.py @@ -2,6 +2,10 @@ from pydenticon import Generator import hashlib +# ----------------------------------------------------------------------------------------------------------------- +# Typing notifications +# ----------------------------------------------------------------------------------------------------------------- + class BaseTypingNotificationHandler: DEFAULT_HANDLER = None @@ -26,6 +30,11 @@ class FriendTypingNotificationHandler(BaseTypingNotificationHandler): BaseTypingNotificationHandler.DEFAULT_HANDLER = BaseTypingNotificationHandler() +# ----------------------------------------------------------------------------------------------------------------- +# Identicons support +# ----------------------------------------------------------------------------------------------------------------- + + def generate_avatar(public_key): foreground = ['rgb(45,79,255)', 'rgb(185, 66, 244)', 'rgb(185, 66, 244)', 'rgb(254,180,44)', 'rgb(252, 2, 2)', 'rgb(109, 198, 0)', diff --git a/toxygen/groups/group_peer.py b/toxygen/groups/group_peer.py index 57724a5..4eaf255 100644 --- a/toxygen/groups/group_peer.py +++ b/toxygen/groups/group_peer.py @@ -1,6 +1,9 @@ class GroupChatPeer: + """ + Represents peer in group chat. + """ def __init__(self, peer_id, name, status, role, public_key, is_current_user=False, is_muted=False): self._peer_id = peer_id @@ -11,11 +14,29 @@ class GroupChatPeer: self._is_current_user = is_current_user self._is_muted = is_muted + # ----------------------------------------------------------------------------------------------------------------- + # Readonly properties + # ----------------------------------------------------------------------------------------------------------------- + def get_id(self): return self._peer_id id = property(get_id) + def get_public_key(self): + return self._public_key + + public_key = property(get_public_key) + + def get_is_current_user(self): + return self._is_current_user + + is_current_user = property(get_is_current_user) + + # ----------------------------------------------------------------------------------------------------------------- + # Read-write properties + # ----------------------------------------------------------------------------------------------------------------- + def get_name(self): return self._name @@ -40,17 +61,10 @@ class GroupChatPeer: role = property(get_role, set_role) - def get_public_key(self): - return self._public_key - - public_key = property(get_public_key) - - def get_is_current_user(self): - return self._is_current_user - - is_current_user = property(get_is_current_user) - def get_is_muted(self): return self._is_muted - is_muted = property(get_is_muted) + def set_is_muted(self, is_muted): + self._is_muted = is_muted + + is_muted = property(get_is_muted, set_is_muted) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index fcdf266..1ba0bbc 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -218,6 +218,7 @@ class Tox: :param public_key: The long term public key of the bootstrap node (TOX_PUBLIC_KEY_SIZE bytes). :return: True on success. """ + address = bytes(address, 'utf-8') tox_err_bootstrap = c_int() result = Tox.libtoxcore.tox_bootstrap(self._tox_pointer, c_char_p(address), c_uint16(port), string_to_bin(public_key), byref(tox_err_bootstrap)) @@ -244,6 +245,7 @@ class Tox: :param public_key: The long term public key of the TCP relay (TOX_PUBLIC_KEY_SIZE bytes). :return: True on success. """ + address = bytes(address, 'utf-8') tox_err_bootstrap = c_int() result = Tox.libtoxcore.tox_add_tcp_relay(self._tox_pointer, c_char_p(address), c_uint16(port), string_to_bin(public_key), byref(tox_err_bootstrap)) From 531fa81bba940354be317c9023c7904a194bd3d0 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 25 Aug 2018 13:31:43 +0300 Subject: [PATCH 127/217] fixed bugs with plugin reloading and toxav_kill --- toxygen/app.py | 3 ++- toxygen/av/calls.py | 8 +++----- toxygen/av/calls_manager.py | 4 ++-- toxygen/common/tox_save.py | 9 +++++++++ toxygen/middleware/threads.py | 15 ++++++++++----- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index a370a91..497a460 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -107,6 +107,7 @@ class App: self._tray.hide() self._save_profile() self._settings.close() + self._calls_manager.set_toxav(None) del self._tox # ----------------------------------------------------------------------------------------------------------------- @@ -202,7 +203,7 @@ class App: def _start_threads(self, initial_start=True): # init thread - self._init = threads.InitThread(self._tox, self._plugin_loader, self._settings) + self._init = threads.InitThread(self._tox, self._plugin_loader, self._settings, initial_start) self._init.start() # starting threads for tox iterate and toxav iterate diff --git a/toxygen/av/calls.py b/toxygen/av/calls.py index 2673f1a..d5f2fe7 100644 --- a/toxygen/av/calls.py +++ b/toxygen/av/calls.py @@ -7,12 +7,13 @@ import itertools import numpy as np from av import screen_sharing from av.call import Call +import common.tox_save -class AV: +class AV(common.tox_save.ToxAvSave): def __init__(self, toxav, settings): - self._toxav = toxav + super().__init__(toxav) self._settings = settings self._running = True @@ -36,9 +37,6 @@ class AV: self._video_width = 640 self._video_height = 480 - def set_toxav(self, toxav): - self._toxav = toxav - def stop(self): self._running = False self.stop_audio_thread() diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py index eefd03a..5a48672 100644 --- a/toxygen/av/calls_manager.py +++ b/toxygen/av/calls_manager.py @@ -8,8 +8,8 @@ import common.event as event class CallsManager: - def __init__(self, toxAV, settings, screen, contacts_manager): - self._call = av.calls.AV(toxAV, settings) # object with data about calls + def __init__(self, toxav, settings, screen, contacts_manager): + self._call = av.calls.AV(toxav, settings) # object with data about calls self._call_widgets = {} # dict of incoming call widgets self._incoming_calls = set() self._settings = settings diff --git a/toxygen/common/tox_save.py b/toxygen/common/tox_save.py index 5d4cee0..09c159b 100644 --- a/toxygen/common/tox_save.py +++ b/toxygen/common/tox_save.py @@ -7,3 +7,12 @@ class ToxSave: def set_tox(self, tox): self._tox = tox + + +class ToxAvSave: + + def __init__(self, toxav): + self._toxav = toxav + + def set_toxav(self, toxav): + self._toxav = toxav diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py index 192f723..15e9665 100644 --- a/toxygen/middleware/threads.py +++ b/toxygen/middleware/threads.py @@ -38,15 +38,18 @@ class BaseQThread(QtCore.QThread): class InitThread(BaseThread): - def __init__(self, tox, plugin_loader, settings): + def __init__(self, tox, plugin_loader, settings, is_first_start): super().__init__() self._tox, self._plugin_loader, self._settings = tox, plugin_loader, settings + self._is_first_start = is_first_start def run(self): - # download list of nodes if needed - download_nodes_list(self._settings) - # start plugins - self._plugin_loader.load() + if self._is_first_start: + # download list of nodes if needed + download_nodes_list(self._settings) + # start plugins + self._plugin_loader.load() + # bootstrap try: for data in generate_nodes(): @@ -56,10 +59,12 @@ class InitThread(BaseThread): self._tox.add_tcp_relay(*data) except: pass + for _ in range(10): if self._stop_thread: return time.sleep(1) + while not self._tox.self_get_connection_status(): try: for data in generate_nodes(None): From 6e07d3e3d4f33faf9d7d641e9a57bc23a610bd24 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 25 Aug 2018 14:23:59 +0300 Subject: [PATCH 128/217] contacts menu history fixes --- toxygen/app.py | 1 + toxygen/contacts/contact_menu.py | 42 +++++++++++++++++----------- toxygen/contacts/contacts_manager.py | 4 --- toxygen/history/history.py | 16 ++++++++++- toxygen/ui/main_screen.py | 18 +----------- 5 files changed, 42 insertions(+), 39 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 497a460..e28ccea 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -319,6 +319,7 @@ class App: self._stop_threads(False) data = self._tox.get_savedata() self._save_profile(data) + self._calls_manager.set_toxav(None) del self._tox # create new tox instance self._tox = self._create_tox(data) diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index b1c23f5..865e7d5 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -87,9 +87,13 @@ class BaseContactMenuGenerator: def __init__(self, contact): self._contact = contact - def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader): return ContactMenuBuilder().build() + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + def _generate_copy_menu_builder(self, main_screen): copy_menu_builder = ContactMenuBuilder() (copy_menu_builder @@ -101,11 +105,23 @@ class BaseContactMenuGenerator: return copy_menu_builder + def _generate_history_menu_builder(self, history_loader, main_screen): + history_menu_builder = ContactMenuBuilder() + (history_menu_builder + .with_name(util_ui.tr('Chat history')) + .with_action(util_ui.tr('Clear history'), lambda: history_loader.clear_history(self._contact) + and main_screen.messages.clear()) + .with_action(util_ui.tr('Export as text'), lambda: history_loader.export_history(self._contact)) + .with_action(util_ui.tr('Export as HTML'), lambda: history_loader.export_history(self._contact, False)) + ) + + return history_menu_builder + class FriendMenuGenerator(BaseContactMenuGenerator): - def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): - history_menu_builder = self._generate_history_menu_builder(main_screen, number) + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader): + history_menu_builder = self._generate_history_menu_builder(history_loader, main_screen) copy_menu_builder = self._generate_copy_menu_builder(main_screen) plugins_menu_builder = self._generate_plugins_menu_builder(plugin_loader, number) groups_menu_builder = self._generate_groups_menu(contacts_manager, groups_service) @@ -132,18 +148,6 @@ class FriendMenuGenerator(BaseContactMenuGenerator): # Private methods # ----------------------------------------------------------------------------------------------------------------- - @staticmethod - def _generate_history_menu_builder(main_screen, number): - history_menu_builder = ContactMenuBuilder() - (history_menu_builder - .with_name(util_ui.tr('Chat history')) - .with_action(util_ui.tr('Clear history'), lambda: main_screen.clear_history(number)) - .with_action(util_ui.tr('Export as text'), lambda: main_screen.export_history(number)) - .with_action(util_ui.tr('Export as HTML'), lambda: main_screen.export_history(number, False)) - ) - - return history_menu_builder - @staticmethod def _generate_plugins_menu_builder(plugin_loader, number): if plugin_loader is None: @@ -174,13 +178,15 @@ class FriendMenuGenerator(BaseContactMenuGenerator): class GroupMenuGenerator(BaseContactMenuGenerator): - def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader): copy_menu_builder = self._generate_copy_menu_builder(main_screen) + history_menu_builder = self._generate_history_menu_builder(history_loader, main_screen) builder = ContactMenuBuilder() menu = (builder .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) .with_submenu(copy_menu_builder) + .with_submenu(history_menu_builder) .with_optional_action(util_ui.tr('Manage group'), lambda: groups_service.show_group_management_screen(self._contact), self._contact.is_self_founder()) @@ -206,13 +212,15 @@ class GroupMenuGenerator(BaseContactMenuGenerator): class GroupPeerMenuGenerator(BaseContactMenuGenerator): - def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader): copy_menu_builder = self._generate_copy_menu_builder(main_screen) + history_menu_builder = self._generate_history_menu_builder(history_loader, main_screen) builder = ContactMenuBuilder() menu = (builder .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) .with_submenu(copy_menu_builder) + .with_submenu(history_menu_builder) .with_action(util_ui.tr('Quit chat'), lambda: contacts_manager.remove_group_peer(self._contact)) .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 23dc61e..50dbc87 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -290,10 +290,6 @@ class ContactsManager(ToxSave): def friend_public_key(self, num): return self._contacts[num].tox_id - def export_history(self, num, as_text): - contact = self._contacts[num] - return self._history.export_history(contact, as_text) - def delete_friend(self, num): """ Removes friend from contact list diff --git a/toxygen/history/history.py b/toxygen/history/history.py index 4850f7d..f551df5 100644 --- a/toxygen/history/history.py +++ b/toxygen/history/history.py @@ -46,6 +46,20 @@ class History: friend.clear_corr(save_unsent) self._db.delete_friend_from_db(friend.tox_id) + def export_history(self, contact, as_text=True): + extension = 'txt' if as_text else 'html' + file_name, _ = util_ui.save_file_dialog(util_ui.tr('Choose file name'), extension) + + if not file_name: + return + + if not file_name.endswith('.' + extension): + file_name += '.' + extension + + history = self.generate_history(contact, as_text) + with open(file_name, 'wt') as fl: + fl.write(history) + def delete_message(self, message): contact = self._contacts_manager.get_curr_contact() if message.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): @@ -93,7 +107,7 @@ class History: self._db.add_friend_to_db(tox_id) @staticmethod - def export_history(contact, as_text=True, _range=None): + def generate_history(contact, as_text=True, _range=None): if _range is None: contact.load_all_corr() corr = contact.get_corr() diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 6217b47..c89f811 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -606,7 +606,7 @@ class MainWindow(QtWidgets.QMainWindow): return generator = contact.get_context_menu_generator() self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, self._settings, number, - self._groups_service) + self._groups_service, self._history_loader) parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) self.listMenu.move(parent_position + pos) self.listMenu.show() @@ -625,19 +625,6 @@ class MainWindow(QtWidgets.QMainWindow): self.note = MultilineEdit(user, note, save_note) self.note.show() - def export_history(self, num, as_text=True): - s = self._contacts_manager.export_history(num, as_text) - extension = 'txt' if as_text else 'html' - file_name, _ = util_ui.save_file_dialog(util_ui.tr('Choose file name'), extension) - - if not file_name: - return - - if not file_name.endswith('.' + extension): - file_name += '.' + extension - with open(file_name, 'wt') as fl: - fl.write(s) - def set_alias(self, num): self._contacts_manager.set_alias(num) @@ -652,9 +639,6 @@ class MainWindow(QtWidgets.QMainWindow): def copy_text(text): util_ui.copy_to_clipboard(text) - def clear_history(self, num): - self._history_loader.clear_history(num) - def auto_accept(self, num, value): tox_id = self._contacts_manager.friend_public_key(num) if value: From ee994973db8ca4077ff85b8a1dc2aee48230c26e Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 25 Aug 2018 14:45:58 +0300 Subject: [PATCH 129/217] toxav kill fixed --- toxygen/app.py | 8 ++++++-- toxygen/wrapper/tox.py | 1 - toxygen/wrapper/toxav.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index e28ccea..7810751 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -107,7 +107,7 @@ class App: self._tray.hide() self._save_profile() self._settings.close() - self._calls_manager.set_toxav(None) + self._kill_toxav() del self._tox # ----------------------------------------------------------------------------------------------------------------- @@ -319,7 +319,7 @@ class App: self._stop_threads(False) data = self._tox.get_savedata() self._save_profile(data) - self._calls_manager.set_toxav(None) + self._kill_toxav() del self._tox # create new tox instance self._tox = self._create_tox(data) @@ -411,3 +411,7 @@ class App: def _init_profile(self): if not self._profile.has_avatar(): self._profile.reset_avatar(self._settings['identicons']) + + def _kill_toxav(self): + self._calls_manager.set_toxav(None) + self._tox.AV.kill() diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 1ba0bbc..6ca7741 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -121,7 +121,6 @@ class Tox: self.AV = ToxAV(self._tox_pointer) def __del__(self): - del self.AV Tox.libtoxcore.tox_kill(self._tox_pointer) # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/wrapper/toxav.py b/toxygen/wrapper/toxav.py index 78ab11f..be61309 100644 --- a/toxygen/wrapper/toxav.py +++ b/toxygen/wrapper/toxav.py @@ -40,7 +40,7 @@ class ToxAV: self.video_receive_frame_cb = None self.call_cb = None - def __del__(self): + def kill(self): """ Releases all resources associated with the A/V session. From a4ceeccfd8cff20c26b5bf1514a58dc2d7096a56 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 27 Aug 2018 00:51:40 +0300 Subject: [PATCH 130/217] various bug fixes --- toxygen/app.py | 5 +++ toxygen/contacts/contact.py | 3 +- toxygen/contacts/contact_menu.py | 2 +- toxygen/contacts/contacts_manager.py | 4 +++ toxygen/middleware/callbacks.py | 47 ++++++++++++++++++++++++++++ toxygen/ui/menu.py | 2 +- toxygen/wrapper/tox.py | 1 + 7 files changed, 60 insertions(+), 4 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 7810751..4bca27c 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -107,6 +107,7 @@ class App: self._tray.hide() self._save_profile() self._settings.close() + self._unset_callbacks() self._kill_toxav() del self._tox @@ -319,6 +320,7 @@ class App: self._stop_threads(False) data = self._tox.get_savedata() self._save_profile(data) + self._unset_callbacks() self._kill_toxav() del self._tox # create new tox instance @@ -408,6 +410,9 @@ class App: self._calls_manager, self._file_transfer_handler, self._ms, self._tray, self._messenger, self._groups_service, self._contacts_provider) + def _unset_callbacks(self): + callbacks.unset_callbacks(self._tox) + def _init_profile(self): if not self._profile.has_avatar(): self._profile.reset_avatar(self._settings['identicons']) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index ecf944d..e88acf2 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -146,8 +146,7 @@ class Contact(basecontact.BaseContact): # ----------------------------------------------------------------------------------------------------------------- def delete_message(self, message_id): - elem = list(filter(lambda m: type(m) in (TextMessage, GroupChatMessage) and m.message_id == message_id, - self._corr))[0] + elem = list(filter(lambda m: m.message_id == message_id, self._corr))[0] tmp = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) if elem in tmp[-self._unsaved_messages:] and self._unsaved_messages: self._unsaved_messages -= 1 diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index 865e7d5..8178d31 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -110,7 +110,7 @@ class BaseContactMenuGenerator: (history_menu_builder .with_name(util_ui.tr('Chat history')) .with_action(util_ui.tr('Clear history'), lambda: history_loader.clear_history(self._contact) - and main_screen.messages.clear()) + or main_screen.messages.clear()) .with_action(util_ui.tr('Export as text'), lambda: history_loader.export_history(self._contact)) .with_action(util_ui.tr('Export as HTML'), lambda: history_loader.export_history(self._contact, False)) ) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 50dbc87..156c409 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -90,6 +90,7 @@ class ContactsManager(ToxSave): self._screen.typing.setVisible(False) current_contact = self.get_curr_contact() if current_contact is not None: + # TODO: send when needed current_contact.typing_notification_handler.send(self._tox, False) current_contact.remove_messages_widgets() # TODO: if required self._unsubscribe_from_events(current_contact) @@ -118,6 +119,7 @@ class ContactsManager(ToxSave): self._messages_items_factory.create_inline_item(message.data) else: self._messages_items_factory.create_message_item(message) + self._messages.scrollToBottom() # if value in self._call: # self._screen.active_call() # elif value in self._incoming_calls: @@ -365,6 +367,8 @@ class ContactsManager(ToxSave): def add_group_peer(self, group, peer): contact = self._contact_provider.get_group_peer_by_id(group, peer.id) + if self.check_if_contact_exists(contact.tox_id): + return self._contacts.append(contact) contact.reset_avatar(self._settings['identicons']) self._save_profile() diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 77a029d..5fcce03 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -604,3 +604,50 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_group_password(group_password(contacts_provider), 0) tox.callback_group_peer_limit(group_peer_limit(contacts_provider), 0) tox.callback_group_privacy_state(group_privacy_state(contacts_provider), 0) + + +def unset_callbacks(tox): + # self callbacks + tox.callback_self_connection_status(0) + + # friend callbacks + tox.callback_friend_status(0) + tox.callback_friend_message(0) + tox.callback_friend_connection_status(0) + tox.callback_friend_name(0) + tox.callback_friend_status_message(0) + tox.callback_friend_request(0) + tox.callback_friend_typing(0) + tox.callback_friend_read_receipt(0) + + # file transfer + tox.callback_file_recv(0) + tox.callback_file_recv_chunk(0) + tox.callback_file_chunk_request(0) + tox.callback_file_recv_control(0) + + # av + toxav = tox.AV + toxav.callback_call_state(0, 0) + toxav.callback_call(0, 0) + toxav.callback_audio_receive_frame(0, 0) + toxav.callback_video_receive_frame(0, 0) + + # custom packets + tox.callback_friend_lossless_packet(0) + tox.callback_friend_lossy_packet(0) + + # gc callbacks + tox.callback_group_message(0, 0) + tox.callback_group_private_message(0, 0) + tox.callback_group_invite(0, 0) + tox.callback_group_self_join(0, 0) + tox.callback_group_peer_join(0, 0) + tox.callback_group_peer_exit(0, 0) + tox.callback_group_peer_name(0, 0) + tox.callback_group_peer_status(0, 0) + tox.callback_group_topic(0, 0) + tox.callback_group_moderation(0, 0) + tox.callback_group_password(0, 0) + tox.callback_group_peer_limit(0, 0) + tox.callback_group_privacy_state(0, 0) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 75db280..45e8765 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -594,7 +594,6 @@ class InterfaceSettings(CenteredWidget): app.removeTranslator(app.translator) app.translator.load(join_path(get_translations_directory(), path)) app.installTranslator(app.translator) - self._settings.save() app_closing_setting = 0 if self.hideRadioButton.isChecked(): @@ -602,6 +601,7 @@ class InterfaceSettings(CenteredWidget): elif self.closeToTrayRadioButton.isChecked(): app_closing_setting = 2 self._settings['close_app'] = app_closing_setting + self._settings.save() if restart: util_ui.message_box(util_ui.tr('Restart app to apply settings'), util_ui.tr('Restart required')) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 6ca7741..1ba0bbc 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -121,6 +121,7 @@ class Tox: self.AV = ToxAV(self._tox_pointer) def __del__(self): + del self.AV Tox.libtoxcore.tox_kill(self._tox_pointer) # ----------------------------------------------------------------------------------------------------------------- From 5e788a543d2d3219549db403156ee5ffb54e92cc Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 30 Aug 2018 00:36:07 +0300 Subject: [PATCH 131/217] fixed messages caching and drag n drop --- toxygen/messenger/messages.py | 3 +-- toxygen/ui/main_screen.py | 2 +- toxygen/ui/main_screen_widgets.py | 15 ++++++++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index d5882b8..e777c4b 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -66,8 +66,7 @@ class Message: message_id = property(get_message_id) def get_widget(self, *args): - if self._widget is None: - self._widget = self._create_widget(*args) + self._widget = self._create_widget(*args) return self._widget diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index c89f811..96b2b9e 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -37,7 +37,7 @@ class MainWindow(QtWidgets.QMainWindow): self._toxes = toxes self._messenger = messenger self._contacts_manager.active_contact_changed.add_callback(self._new_contact_selected) - self.messageEdit.set_dependencies(messenger, contacts_manager) + self.messageEdit.set_dependencies(messenger, contacts_manager, file_transfer_handler) self.update_gc_invites_button_state() diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index 8b7bf52..a2d7526 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -12,15 +12,16 @@ class MessageArea(QtWidgets.QPlainTextEdit): def __init__(self, parent, form): super().__init__(parent) - self._messenger = self._contacts_manager = None + self._messenger = self._contacts_manager = self._file_transfer_handler = None self.parent = form self.setAcceptDrops(True) self._timer = QtCore.QTimer(self) self._timer.timeout.connect(lambda: self._messenger.send_typing(False)) - def set_dependencies(self, messenger, contacts_manager): + def set_dependencies(self, messenger, contacts_manager, file_transfer_handler): self._messenger = messenger self._contacts_manager = contacts_manager + self._file_transfer_handler = file_transfer_handler def keyPressEvent(self, event): if event.matches(QtGui.QKeySequence.Paste): @@ -84,16 +85,20 @@ class MessageArea(QtWidgets.QPlainTextEdit): def pasteEvent(self, text=None): text = text or QtWidgets.QApplication.clipboard().text() if text.startswith('file://'): - file_name = self.parse_file_name(text) - self.parent.profile.send_file(file_name) + if not self._contacts_manager.is_active_a_friend(): + return + friend_number = self._contacts_manager.get_active_number() + file_path = self._parse_file_path(text) + self._file_transfer_handler.send_file(file_path, friend_number) else: self.insertPlainText(text) @staticmethod - def parse_file_name(file_name): + def _parse_file_path(file_name): if file_name.endswith('\r\n'): file_name = file_name[:-2] file_name = urllib.parse.unquote(file_name) + return file_name[8 if util.get_platform() == 'Windows' else 7:] From 9a580824960aa255ea8986ddae943bae4544ee7f Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 13 Sep 2018 23:04:22 +0300 Subject: [PATCH 132/217] bug fixes --- toxygen/app.py | 10 ++-- toxygen/file_transfers/file_transfers.py | 2 +- .../file_transfers/file_transfers_handler.py | 6 +-- toxygen/messenger/messenger.py | 16 +++---- toxygen/middleware/callbacks.py | 47 ------------------- toxygen/wrapper/tox.py | 7 +-- toxygen/wrapper/toxav.py | 5 +- 7 files changed, 24 insertions(+), 69 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 4bca27c..18e5236 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -107,8 +107,8 @@ class App: self._tray.hide() self._save_profile() self._settings.close() - self._unset_callbacks() self._kill_toxav() + self._kill_tox() del self._tox # ----------------------------------------------------------------------------------------------------------------- @@ -320,8 +320,8 @@ class App: self._stop_threads(False) data = self._tox.get_savedata() self._save_profile(data) - self._unset_callbacks() self._kill_toxav() + self._kill_tox() del self._tox # create new tox instance self._tox = self._create_tox(data) @@ -410,9 +410,6 @@ class App: self._calls_manager, self._file_transfer_handler, self._ms, self._tray, self._messenger, self._groups_service, self._contacts_provider) - def _unset_callbacks(self): - callbacks.unset_callbacks(self._tox) - def _init_profile(self): if not self._profile.has_avatar(): self._profile.reset_avatar(self._settings['identicons']) @@ -420,3 +417,6 @@ class App: def _kill_toxav(self): self._calls_manager.set_toxav(None) self._tox.AV.kill() + + def _kill_tox(self): + self._tox.kill() diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 46a777f..445dac9 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -29,7 +29,7 @@ SHOW_PROGRESS_BAR = (0, 1, 4) def is_inline(file_name): allowed_inlines = ('toxygen_inline.png', 'utox-inline.png', 'sticker.png') - return file_name in allowed_inlines or file_name.startswith('qTox_Screenshot_') + return file_name in allowed_inlines or file_name.startswith('qTox_Image_') class FileTransfer: diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index d462c89..cde1795 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -257,7 +257,7 @@ class FileTransfersHandler(ToxSave): elif not size: friend.reset_avatar(self._settings['identicons']) - def _send_avatar_to_contacts(self): + def _send_avatar_to_contacts(self, _): friends = self._get_all_friends() for friend in friends: self.send_avatar(friend.number) @@ -286,13 +286,13 @@ class FileTransfersHandler(ToxSave): path, file_name = os.path.split(path) new_file_name, i = file_name, 1 if not from_position: - while os.path.isfile(path + '/' + new_file_name): # file with same name already exists + while os.path.isfile(join_path(path, new_file_name)): # file with same name already exists if '.' in file_name: # has extension d = file_name.rindex('.') else: # no extension d = len(file_name) new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] i += 1 - path = os.path.join(path, new_file_name) + path = join_path(path, new_file_name) return path diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py index 9ee86dd..e859135 100644 --- a/toxygen/messenger/messenger.py +++ b/toxygen/messenger/messenger.py @@ -224,7 +224,7 @@ class Messenger(tox_save.ToxSave): return message = util_ui.tr('User {} is now known as {}') message = message.format(old_name, new_name) - if self._contacts_manager.is_friend_active(friend.number): + if not self._contacts_manager.is_friend_active(friend.number): friend.actions = True self._add_info_message(friend.number, message) @@ -236,14 +236,14 @@ class Messenger(tox_save.ToxSave): def _split_message(message): messages = [] while len(message) > TOX_MAX_MESSAGE_LENGTH: - size = TOX_MAX_MESSAGE_LENGTH * 4 / 5 + size = TOX_MAX_MESSAGE_LENGTH * 4 // 5 last_part = message[size:TOX_MAX_MESSAGE_LENGTH] - if ' ' in last_part: - index = last_part.index(' ') - elif ',' in last_part: - index = last_part.index(',') - elif '.' in last_part: - index = last_part.index('.') + if b' ' in last_part: + index = last_part.index(b' ') + elif b',' in last_part: + index = last_part.index(b',') + elif b'.' in last_part: + index = last_part.index(b'.') else: index = TOX_MAX_MESSAGE_LENGTH - size - 1 index += size + 1 diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 5fcce03..77a029d 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -604,50 +604,3 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_group_password(group_password(contacts_provider), 0) tox.callback_group_peer_limit(group_peer_limit(contacts_provider), 0) tox.callback_group_privacy_state(group_privacy_state(contacts_provider), 0) - - -def unset_callbacks(tox): - # self callbacks - tox.callback_self_connection_status(0) - - # friend callbacks - tox.callback_friend_status(0) - tox.callback_friend_message(0) - tox.callback_friend_connection_status(0) - tox.callback_friend_name(0) - tox.callback_friend_status_message(0) - tox.callback_friend_request(0) - tox.callback_friend_typing(0) - tox.callback_friend_read_receipt(0) - - # file transfer - tox.callback_file_recv(0) - tox.callback_file_recv_chunk(0) - tox.callback_file_chunk_request(0) - tox.callback_file_recv_control(0) - - # av - toxav = tox.AV - toxav.callback_call_state(0, 0) - toxav.callback_call(0, 0) - toxav.callback_audio_receive_frame(0, 0) - toxav.callback_video_receive_frame(0, 0) - - # custom packets - tox.callback_friend_lossless_packet(0) - tox.callback_friend_lossy_packet(0) - - # gc callbacks - tox.callback_group_message(0, 0) - tox.callback_group_private_message(0, 0) - tox.callback_group_invite(0, 0) - tox.callback_group_self_join(0, 0) - tox.callback_group_peer_join(0, 0) - tox.callback_group_peer_exit(0, 0) - tox.callback_group_peer_name(0, 0) - tox.callback_group_peer_status(0, 0) - tox.callback_group_topic(0, 0) - tox.callback_group_moderation(0, 0) - tox.callback_group_password(0, 0) - tox.callback_group_peer_limit(0, 0) - tox.callback_group_privacy_state(0, 0) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 1ba0bbc..21b0ebc 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -59,8 +59,9 @@ class Tox: self._tox_pointer = tox_pointer else: tox_err_new = c_int() - Tox.libtoxcore.tox_new.restype = POINTER(c_void_p) - self._tox_pointer = Tox.libtoxcore.tox_new(tox_options, byref(tox_err_new)) + f = Tox.libtoxcore.tox_new + f.restype = POINTER(c_void_p) + self._tox_pointer = f(tox_options, byref(tox_err_new)) tox_err_new = tox_err_new.value if tox_err_new == TOX_ERR_NEW['NULL']: raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') @@ -120,7 +121,7 @@ class Tox: self.AV = ToxAV(self._tox_pointer) - def __del__(self): + def kill(self): del self.AV Tox.libtoxcore.tox_kill(self._tox_pointer) diff --git a/toxygen/wrapper/toxav.py b/toxygen/wrapper/toxav.py index be61309..98e1c73 100644 --- a/toxygen/wrapper/toxav.py +++ b/toxygen/wrapper/toxav.py @@ -24,8 +24,9 @@ class ToxAV: """ self.libtoxav = LibToxAV() toxav_err_new = c_int() - self.libtoxav.toxav_new.restype = POINTER(c_void_p) - self._toxav_pointer = self.libtoxav.toxav_new(tox_pointer, byref(toxav_err_new)) + f = self.libtoxav.toxav_new + f.restype = POINTER(c_void_p) + self._toxav_pointer = f(tox_pointer, byref(toxav_err_new)) toxav_err_new = toxav_err_new.value if toxav_err_new == TOXAV_ERR_NEW['NULL']: raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') From 3582722faa9623061da29331a8f35fc9d3cd16ee Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 13 Sep 2018 23:23:25 +0300 Subject: [PATCH 133/217] fixed 2 bugs with gc --- toxygen/mainscreen.py | 2 +- toxygen/profile.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/toxygen/mainscreen.py b/toxygen/mainscreen.py index 7d7b9e7..93ec72d 100644 --- a/toxygen/mainscreen.py +++ b/toxygen/mainscreen.py @@ -618,7 +618,7 @@ class MainWindow(QtWidgets.QMainWindow, Singleton): for i in range(len(chats)): name, number = chats[i] item = invite_menu.addAction(name) - item.triggered.connect(lambda: self.invite_friend_to_gc(num, number)) + item.triggered.connect(lambda number=number: self.invite_friend_to_gc(num, number)) plugins_loader = plugin_support.PluginLoader.get_instance() if plugins_loader is not None: diff --git a/toxygen/profile.py b/toxygen/profile.py index 16d117d..ec5b545 100644 --- a/toxygen/profile.py +++ b/toxygen/profile.py @@ -1316,6 +1316,8 @@ class Profile(basecontact.BaseContact, Singleton): return list(groups)[0] def add_gc(self, number): + if number == -1: + return widget = self.create_friend_item() gc = GroupChat('Group chat #' + str(number), '', widget, self._tox, number) self._contacts.append(gc) From 9f7de204d4fddf0eabf5dff51d6e14270168ebd8 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 14 Sep 2018 18:35:07 +0300 Subject: [PATCH 134/217] fixes for smileys selection and file transfers --- toxygen/file_transfers/file_transfers.py | 5 +- .../file_transfers_messages_service.py | 9 +- toxygen/middleware/threads.py | 2 +- toxygen/ui/main_screen.py | 17 ++-- toxygen/ui/main_screen_widgets.py | 83 ++++++++++--------- 5 files changed, 63 insertions(+), 53 deletions(-) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 445dac9..9b29575 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -4,6 +4,7 @@ from os import remove, rename, chdir from time import time from wrapper.tox import Tox from common.event import Event +from middleware.threads import invoke_in_main_thread FILE_TRANSFER_STATE = { @@ -120,10 +121,10 @@ class FileTransfer: t = -1 else: t = ((time() - self._creation_time) / percentage) * (1 - percentage) - self._state_changed_event(self.state, percentage, int(t)) + invoke_in_main_thread(self._state_changed_event, self.state, percentage, int(t)) def _finished(self): - self._finished_event(self._friend_number, self._file_number) + invoke_in_main_thread(self._finished_event, self._friend_number, self._file_number) # ----------------------------------------------------------------------------------------------------------------- # Send file diff --git a/toxygen/file_transfers/file_transfers_messages_service.py b/toxygen/file_transfers/file_transfers_messages_service.py index 7cb90f5..4509183 100644 --- a/toxygen/file_transfers/file_transfers_messages_service.py +++ b/toxygen/file_transfers/file_transfers_messages_service.py @@ -40,10 +40,11 @@ class FileTransfersMessagesService: return tm def add_inline_message(self, transfer, index): - if self._is_friend_active(transfer.friend_number): - count = self._messages.count() - if count + index + 1 >= 0: - self._create_inline_item(transfer.data, count + index + 1) + if not self._is_friend_active(transfer.friend_number): + return + count = self._messages.count() + if count + index + 1 >= 0: + self._create_inline_item(transfer.data, count + index + 1) def add_unsent_file_message(self, friend, file_path, data): author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['ME']) diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py index 15e9665..5f9404b 100644 --- a/toxygen/middleware/threads.py +++ b/toxygen/middleware/threads.py @@ -106,7 +106,7 @@ class ToxAVIterateThread(BaseQThread): # File transfers thread # ----------------------------------------------------------------------------------------------------------------- -class FileTransfersThread(BaseThread): +class FileTransfersThread(BaseQThread): def __init__(self): super().__init__() diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 96b2b9e..5a510a5 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -18,6 +18,7 @@ class MainWindow(QtWidgets.QMainWindow): self._plugins_loader = None self.setAcceptDrops(True) self._saved = False + self._smiley_window = None self._profile = self._toxes = self._messenger = None self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None self._should_show_group_peers_list = False @@ -556,13 +557,15 @@ class MainWindow(QtWidgets.QMainWindow): def send_smiley(self): self.menu.hide() - if self._contacts_manager.get_curr_contact() is not None: - self.smiley = self._widget_factory.create_smiley_window(self) - self.smiley.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 200 + self.x(), - self.y() + self.height() - 400, - self.smiley.width(), - self.smiley.height())) - self.smiley.show() + if self._contacts_manager.get_curr_contact() is None: + return + self._smiley_window = self._widget_factory.create_smiley_window(self) + rect = QtCore.QRect(self.menu.x(), + self.menu.y() - self.menu.height(), + self._smiley_window.width(), + self._smiley_window.height()) + self._smiley_window.setGeometry(rect) + self._smiley_window.show() def send_sticker(self): self.menu.hide() diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py index a2d7526..122561b 100644 --- a/toxygen/ui/main_screen_widgets.py +++ b/toxygen/ui/main_screen_widgets.py @@ -141,57 +141,62 @@ class SmileyWindow(QtWidgets.QWidget): def __init__(self, parent, smiley_loader): super().__init__(parent) self.setWindowFlags(QtCore.Qt.FramelessWindowHint) - self.data = smiley_loader.get_smileys() - count = len(self.data) + self._parent = parent + self._data = smiley_loader.get_smileys() + + count = len(self._data) if not count: self.close() - self.page_size = int(pow(count / 8, 0.5) + 1) * 8 # smileys per page - if count % self.page_size == 0: - self.page_count = count // self.page_size + + self._page_size = int(pow(count / 8, 0.5) + 1) * 8 # smileys per page + if count % self._page_size == 0: + self._page_count = count // self._page_size else: - self.page_count = round(count / self.page_size + 0.5) - self.page = -1 - self.radio = [] - self.parent = parent - for i in range(self.page_count): # buttons with smileys + self._page_count = round(count / self._page_size + 0.5) + self._page = -1 + self._radio = [] + + for i in range(self._page_count): # pages - radio buttons elem = QtWidgets.QRadioButton(self) - elem.setGeometry(QtCore.QRect(i * 20 + 5, 180, 20, 20)) - elem.clicked.connect(lambda c, t=i: self.checked(t)) - self.radio.append(elem) - width = max(self.page_count * 20 + 30, (self.page_size + 5) * 8 // 10) + elem.setGeometry(QtCore.QRect(i * 20 + 5, 160, 20, 20)) + elem.clicked.connect(lambda c, t=i: self._checked(t)) + self._radio.append(elem) + + width = max(self._page_count * 20 + 30, (self._page_size + 5) * 8 // 10) self.setMaximumSize(width, 200) self.setMinimumSize(width, 200) - self.buttons = [] - for i in range(self.page_size): # pages - radio buttons + self._buttons = [] + + for i in range(self._page_size): # buttons with smileys b = QtWidgets.QPushButton(self) b.setGeometry(QtCore.QRect((i // 8) * 20 + 5, (i % 8) * 20, 20, 20)) - b.clicked.connect(lambda c, t=i: self.clicked(t)) - self.buttons.append(b) - self.checked(0) - - def checked(self, pos): # new page opened - self.radio[self.page].setChecked(False) - self.radio[pos].setChecked(True) - self.page = pos - start = self.page * self.page_size - for i in range(self.page_size): - try: - self.buttons[i].setVisible(True) - pixmap = QtGui.QPixmap(self.data[start + i][1]) - icon = QtGui.QIcon(pixmap) - self.buttons[i].setIcon(icon) - except: - self.buttons[i].setVisible(False) - - def clicked(self, pos): # smiley selected - pos += self.page * self.page_size - smiley = self.data[pos][0] - self.parent.messageEdit.insertPlainText(smiley) - self.close() + b.clicked.connect(lambda c, t=i: self._clicked(t)) + self._buttons.append(b) + self._checked(0) def leaveEvent(self, event): self.close() + def _checked(self, pos): # new page opened + self._radio[self._page].setChecked(False) + self._radio[pos].setChecked(True) + self._page = pos + start = self._page * self._page_size + for i in range(self._page_size): + try: + self._buttons[i].setVisible(True) + pixmap = QtGui.QPixmap(self._data[start + i][1]) + icon = QtGui.QIcon(pixmap) + self._buttons[i].setIcon(icon) + except: + self._buttons[i].setVisible(False) + + def _clicked(self, pos): # smiley selected + pos += self._page * self._page_size + smiley = self._data[pos][0] + self._parent.messageEdit.insertPlainText(smiley) + self.close() + class MenuButton(QtWidgets.QPushButton): From 02b2d07b6d2745fc676a7ee49e72a69dc0839abd Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 14 Sep 2018 19:10:35 +0300 Subject: [PATCH 135/217] profile backup - initial infrastructure --- toxygen/app.py | 4 ++- toxygen/user_data/backup_service.py | 40 ++++++++++++++++++++++++++++ toxygen/user_data/profile_manager.py | 17 ++++++++++++ toxygen/user_data/settings.py | 16 ++++++++++- 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 toxygen/user_data/backup_service.py diff --git a/toxygen/app.py b/toxygen/app.py index 18e5236..4597d0a 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -34,6 +34,7 @@ from groups.groups_service import GroupsService from ui.create_profile_screen import CreateProfileScreen from common.provider import Provider from contacts.group_peer_factory import GroupPeerFactory +from user_data.backup_service import BackupService import styles.style # TODO: dynamic loading @@ -45,7 +46,7 @@ class App: self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = None - self._group_peer_factory = self._tox_dns = None + self._group_peer_factory = self._tox_dns = self._backup_service = None self._group_factory = self._groups_service = self._profile = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] @@ -341,6 +342,7 @@ class App: self._init_callbacks() def _create_dependencies(self): + self._backup_service = BackupService(self._settings, self._profile_manager) self._smiley_loader = SmileyLoader(self._settings) self._tox_dns = ToxDns(self._settings) self._ms = MainWindow(self._settings, self._tray) diff --git a/toxygen/user_data/backup_service.py b/toxygen/user_data/backup_service.py new file mode 100644 index 0000000..bb0cef9 --- /dev/null +++ b/toxygen/user_data/backup_service.py @@ -0,0 +1,40 @@ +import os.path +from utils.util import get_profile_name_from_path, join_path + + +class BackupService: + + def __init__(self, settings, profile_manager): + self._settings = settings + self._profile_name = get_profile_name_from_path(profile_manager.get_path()) + + settings.settings_saved_event.add_callback(self._settings_saved) + profile_manager.profile_saved_event.add_callback(self._profile_saved) + + def _settings_saved(self, data): + if not self._check_if_should_save_backup(): + return + + file_path = join_path(self._get_backup_directory(), self._profile_name + '.json') + + with open(file_path, 'wt') as fl: + fl.write(data) + + def _profile_saved(self, data): + if not self._check_if_should_save_backup(): + return + + file_path = join_path(self._get_backup_directory(), self._profile_name + '.tox') + + with open(file_path, 'wb') as fl: + fl.write(data) + + def _check_if_should_save_backup(self): + backup_directory = self._get_backup_directory() + if backup_directory is None: + return False + + return os.path.exists(backup_directory) and os.path.isdir(backup_directory) + + def _get_backup_directory(self): + return self._settings['backup_directory'] diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py index e49f5f4..05e2f2d 100644 --- a/toxygen/user_data/profile_manager.py +++ b/toxygen/user_data/profile_manager.py @@ -1,6 +1,7 @@ import utils.util as util import os from user_data.settings import Settings +from common.event import Event class ProfileManager: @@ -11,11 +12,25 @@ class ProfileManager: self._toxes = toxes self._path = path self._directory = os.path.dirname(path) + self._profile_saved_event = Event() # create /avatars if not exists: avatars_directory = util.join_path(self._directory, 'avatars') if not os.path.exists(avatars_directory): os.makedirs(avatars_directory) + # ----------------------------------------------------------------------------------------------------------------- + # Properties + # ----------------------------------------------------------------------------------------------------------------- + + def get_profile_saved_event(self): + return self._profile_saved_event + + profile_saved_event = property(get_profile_saved_event) + + # ----------------------------------------------------------------------------------------------------------------- + # Public methods + # ----------------------------------------------------------------------------------------------------------------- + def open_profile(self): with open(self._path, 'rb') as fl: data = fl.read() @@ -37,6 +52,8 @@ class ProfileManager: fl.write(data) print('Profile saved successfully') + self._profile_saved_event(data) + def export_profile(self, settings, new_path, use_new_path): path = new_path + os.path.basename(self._path) with open(self._path, 'rb') as fin: diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 768a07e..c986019 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -1,6 +1,7 @@ import json from utils.util import * import pyaudio +from common.event import Event class Settings(dict): @@ -12,6 +13,7 @@ class Settings(dict): self._path = path self._profile_path = path.replace('.json', '.tox') self._toxes = toxes + self._settings_saved_event = Event() if os.path.isfile(path): with open(path, 'rb') as fl: data = fl.read() @@ -43,6 +45,15 @@ class Settings(dict): 'enabled': input_devices and output_devices} self.video = {'device': -1, 'width': 640, 'height': 480, 'x': 0, 'y': 0} + # ----------------------------------------------------------------------------------------------------------------- + # Properties + # ----------------------------------------------------------------------------------------------------------------- + + def get_settings_saved_event(self): + return self._settings_saved_event + + settings_saved_event = property(get_settings_saved_event) + # ----------------------------------------------------------------------------------------------------------------- # Public methods # ----------------------------------------------------------------------------------------------------------------- @@ -56,6 +67,8 @@ class Settings(dict): with open(self._path, 'wb') as fl: fl.write(text) + self._settings_saved_event(text) + def close(self): path = self._profile_path + '.lock' if os.path.isfile(path): @@ -186,7 +199,8 @@ class Settings(dict): 'group_notifications': True, 'download_nodes_list': False, 'notify_all_gc': False, - 'lan_discovery': True + 'lan_discovery': True, + 'backup_directory': None } @staticmethod From ad3bbb5e4599de2b21a2f59842e5305b920ca7c0 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 14 Sep 2018 20:38:18 +0300 Subject: [PATCH 136/217] profile settings screen converted --- toxygen/ui/menu.py | 197 -------------- toxygen/ui/profile_settings_screen.py | 157 +++++++++++ toxygen/ui/views/profile_settings_screen.ui | 280 ++++++++++++++++++++ toxygen/ui/widgets_factory.py | 1 + toxygen/user_data/settings.py | 5 +- 5 files changed, 440 insertions(+), 200 deletions(-) create mode 100644 toxygen/ui/profile_settings_screen.py create mode 100644 toxygen/ui/views/profile_settings_screen.ui diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py index 45e8765..8aec578 100644 --- a/toxygen/ui/menu.py +++ b/toxygen/ui/menu.py @@ -52,203 +52,6 @@ class AddContact(CenteredWidget): self.toxIdLineEdit.setPlaceholderText(util_ui.tr('TOX ID or public key of contact')) -class ProfileSettings(CenteredWidget): - """Form with profile settings such as name, status, TOX ID""" - def __init__(self, profile, profile_manager, settings, toxes): - super().__init__() - self._profile = profile - self._profile_manager = profile_manager - self._settings = settings - self._toxes = toxes - self._auto = False - self.initUI() - self.center() - - def initUI(self): - self.setObjectName("ProfileSettingsForm") - self.setMinimumSize(QtCore.QSize(700, 600)) - self.setMaximumSize(QtCore.QSize(700, 600)) - self.nick = LineEdit(self) - self.nick.setGeometry(QtCore.QRect(30, 60, 350, 27)) - self.nick.setText(self._profile.name) - self.status = QtWidgets.QComboBox(self) - self.status.setGeometry(QtCore.QRect(400, 60, 200, 27)) - self.status_message = LineEdit(self) - self.status_message.setGeometry(QtCore.QRect(30, 130, 350, 27)) - self.status_message.setText(self._profile.status_message) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(40, 30, 91, 25)) - font = QtGui.QFont() - font.setFamily(self._settings['font']) - font.setPointSize(18) - font.setWeight(75) - font.setBold(True) - self.label.setFont(font) - self.label_2 = QtWidgets.QLabel(self) - self.label_2.setGeometry(QtCore.QRect(40, 100, 100, 25)) - self.label_2.setFont(font) - self.label_3 = QtWidgets.QLabel(self) - self.label_3.setGeometry(QtCore.QRect(40, 180, 100, 25)) - self.label_3.setFont(font) - self.tox_id = QtWidgets.QLabel(self) - self.tox_id.setGeometry(QtCore.QRect(15, 210, 685, 21)) - font.setPointSize(10) - self.tox_id.setFont(font) - self.tox_id.setText(self._profile.tox_id) - self.copyId = QtWidgets.QPushButton(self) - self.copyId.setGeometry(QtCore.QRect(40, 250, 180, 30)) - self.copyId.clicked.connect(self.copy) - self.export = QtWidgets.QPushButton(self) - self.export.setGeometry(QtCore.QRect(230, 250, 180, 30)) - self.export.clicked.connect(self.export_profile) - self.new_nospam = QtWidgets.QPushButton(self) - self.new_nospam.setGeometry(QtCore.QRect(420, 250, 180, 30)) - self.new_nospam.clicked.connect(self.new_no_spam) - self.copy_pk = QtWidgets.QPushButton(self) - self.copy_pk.setGeometry(QtCore.QRect(40, 300, 180, 30)) - self.copy_pk.clicked.connect(self.copy_public_key) - self.new_avatar = QtWidgets.QPushButton(self) - self.new_avatar.setGeometry(QtCore.QRect(230, 300, 180, 30)) - self.delete_avatar = QtWidgets.QPushButton(self) - self.delete_avatar.setGeometry(QtCore.QRect(420, 300, 180, 30)) - self.delete_avatar.clicked.connect(self.reset_avatar) - self.new_avatar.clicked.connect(self.set_avatar) - self.profilepass = QtWidgets.QLabel(self) - self.profilepass.setGeometry(QtCore.QRect(40, 340, 300, 30)) - font.setPointSize(18) - self.profilepass.setFont(font) - self.password = LineEdit(self) - self.password.setGeometry(QtCore.QRect(40, 380, 300, 30)) - self.password.setEchoMode(QtWidgets.QLineEdit.Password) - self.leave_blank = QtWidgets.QLabel(self) - self.leave_blank.setGeometry(QtCore.QRect(350, 380, 300, 30)) - self.confirm_password = LineEdit(self) - self.confirm_password.setGeometry(QtCore.QRect(40, 420, 300, 30)) - self.confirm_password.setEchoMode(QtWidgets.QLineEdit.Password) - self.set_password = QtWidgets.QPushButton(self) - self.set_password.setGeometry(QtCore.QRect(40, 470, 300, 30)) - self.set_password.clicked.connect(self.new_password) - self.not_match = QtWidgets.QLabel(self) - self.not_match.setGeometry(QtCore.QRect(350, 420, 300, 30)) - self.not_match.setVisible(False) - self.not_match.setStyleSheet('QLabel { color: #BC1C1C; }') - self.warning = QtWidgets.QLabel(self) - self.warning.setGeometry(QtCore.QRect(40, 510, 500, 30)) - self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') - self.default = QtWidgets.QPushButton(self) - self.default.setGeometry(QtCore.QRect(40, 550, 620, 30)) - self._auto = Settings.get_auto_profile() == self._profile_manager.get_path() - self.default.clicked.connect(self.auto_profile) - self.retranslateUi() - if self._profile.status is not None: - self.status.setCurrentIndex(self._profile.status) - else: - self.status.setVisible(False) - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.export.setText(util_ui.tr("Export profile")) - self.setWindowTitle(util_ui.tr("Profile settings")) - self.label.setText(util_ui.tr("Name:")) - self.label_2.setText(util_ui.tr("Status:")) - self.label_3.setText(util_ui.tr("TOX ID:")) - self.copyId.setText(util_ui.tr("Copy TOX ID")) - self.new_avatar.setText(util_ui.tr("New avatar")) - self.delete_avatar.setText(util_ui.tr("Reset avatar")) - self.new_nospam.setText(util_ui.tr("New NoSpam")) - self.profilepass.setText(util_ui.tr("Profile password")) - self.password.setPlaceholderText(util_ui.tr("Password (at least 8 symbols)")) - self.confirm_password.setPlaceholderText(util_ui.tr("Confirm password")) - self.set_password.setText(util_ui.tr("Set password")) - self.not_match.setText(util_ui.tr("Passwords do not match")) - self.leave_blank.setText(util_ui.tr("Leaving blank will reset current password")) - self.warning.setText(util_ui.tr("There is no way to recover lost passwords")) - self.status.addItem(util_ui.tr("Online")) - self.status.addItem(util_ui.tr("Away")) - self.status.addItem(util_ui.tr("Busy")) - self.copy_pk.setText(util_ui.tr("Copy public key")) - - self.set_default_profile_button_text() - - def auto_profile(self): - if self._auto: - Settings.reset_auto_profile() - else: - Settings.set_auto_profile(self._profile_manager.get_path()) - self._auto = not self._auto - self.set_default_profile_button_text() - - def set_default_profile_button_text(self): - if self._auto: - self.default.setText(util_ui.tr("Mark as not default profile")) - else: - self.default.setText(util_ui.tr("Mark as default profile")) - - def new_password(self): - if self.password.text() == self.confirm_password.text(): - if not len(self.password.text()) or len(self.password.text()) >= 8: - self._toxes.set_password(self.password.text()) - self.close() - else: - self.not_match.setText( - util_ui.tr("Password must be at least 8 symbols")) - self.not_match.setVisible(True) - else: - self.not_match.setText(util_ui.tr("Passwords do not match")) - self.not_match.setVisible(True) - - def copy(self): - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(self._profile.tox_id) - pixmap = QtGui.QPixmap(join_path(get_images_directory(), 'accept.png')) - icon = QtGui.QIcon(pixmap) - self.copyId.setIcon(icon) - self.copyId.setIconSize(QtCore.QSize(10, 10)) - - def copy_public_key(self): - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(self._profile.tox_id[:64]) - pixmap = QtGui.QPixmap(join_path(get_images_directory(), 'accept.png')) - icon = QtGui.QIcon(pixmap) - self.copy_pk.setIcon(icon) - self.copy_pk.setIconSize(QtCore.QSize(10, 10)) - - def new_no_spam(self): - self.tox_id.setText(self._profile.set_new_nospam()) - - def reset_avatar(self): - self._profile.reset_avatar(self._settings['identicons']) - - def set_avatar(self): - choose = util_ui.tr("Choose avatar") - name = util_ui.file_dialog(choose, 'Images (*.png)') - if not name[0]: - return - bitmap = QtGui.QPixmap(name[0]) - bitmap.scaled(QtCore.QSize(128, 128), aspectRatioMode=QtCore.Qt.KeepAspectRatio, - transformMode=QtCore.Qt.SmoothTransformation) - - byte_array = QtCore.QByteArray() - buffer = QtCore.QBuffer(byte_array) - buffer.open(QtCore.QIODevice.WriteOnly) - bitmap.save(buffer, 'PNG') - self._profile.set_avatar(bytes(byte_array.data())) - - def export_profile(self): - directory = util_ui.directory_dialog() + '/' - if directory != '/': - reply = util_ui.question(util_ui.tr('Do you want to move your profile to this location?'), - util_ui.tr('Use new path')) - self._settings.export(directory) - self._profile.export_db(directory) - self._profile_manager.export_profile(self._settings, directory, reply) - - def closeEvent(self, event): - self._profile.set_name(self.nick.text()) - self._profile.set_status_message(self.status_message.text()) - self._profile.set_status(self.status.currentIndex()) - - class NetworkSettings(CenteredWidget): """Network settings form: UDP, Ipv6 and proxy""" def __init__(self, settings, reset): diff --git a/toxygen/ui/profile_settings_screen.py b/toxygen/ui/profile_settings_screen.py new file mode 100644 index 0000000..2e55d3d --- /dev/null +++ b/toxygen/ui/profile_settings_screen.py @@ -0,0 +1,157 @@ +from ui.widgets import CenteredWidget +import utils.ui as util_ui +from utils.util import join_path, get_images_directory, get_views_path +from user_data.settings import Settings +from PyQt5 import QtGui, QtCore, uic + + +class ProfileSettings(CenteredWidget): + """Form with profile settings such as name, status, TOX ID""" + def __init__(self, profile, profile_manager, settings, toxes): + super().__init__() + self._profile = profile + self._profile_manager = profile_manager + self._settings = settings + self._toxes = toxes + self._auto = False + + uic.loadUi(get_views_path('profile_settings_screen'), self) + + self._init_ui() + self.center() + + def closeEvent(self, event): + self._profile.set_name(self.nameLineEdit.text()) + self._profile.set_status_message(self.statusMessageLineEdit.text()) + self._profile.set_status(self.statusComboBox.currentIndex()) + + def _init_ui(self): + self._auto = Settings.get_auto_profile() == self._profile_manager.get_path() + self.toxIdLabel.setText(self._profile.tox_id) + self.nameLineEdit.setText(self._profile.name) + self.statusMessageLineEdit.setText(self._profile.status_message) + self.defaultProfilePushButton.clicked.connect(self._toggle_auto_profile) + self.copyToxIdPushButton.clicked.connect(self._copy_tox_id) + self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key) + self.changePasswordPushButton.clicked.connect(self._save_password) + self.exportProfilePushButton.clicked.connect(self._export_profile) + self.newNoSpamPushButton.clicked.connect(self._set_new_no_spam) + self.newAvatarPushButton.clicked.connect(self._set_avatar) + self.resetAvatarPushButton.clicked.connect(self._reset_avatar) + + self.invalidPasswordsLabel.setVisible(False) + + self._retranslate_ui() + + if self._profile.status is not None: + self.statusComboBox.setCurrentIndex(self._profile.status) + else: + self.statusComboBox.setVisible(False) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Profile settings")) + + self.exportProfilePushButton.setText(util_ui.tr("Export profile")) + self.nameLabel.setText(util_ui.tr("Name:")) + self.statusLabel.setText(util_ui.tr("Status:")) + self.toxIdTitleLabel.setText(util_ui.tr("TOX ID:")) + self.copyToxIdPushButton.setText(util_ui.tr("Copy TOX ID")) + self.newAvatarPushButton.setText(util_ui.tr("New avatar")) + self.resetAvatarPushButton.setText(util_ui.tr("Reset avatar")) + self.newNoSpamPushButton.setText(util_ui.tr("New NoSpam")) + self.profilePasswordLabel.setText(util_ui.tr("Profile password")) + self.passwordLineEdit.setPlaceholderText(util_ui.tr("Password (at least 8 symbols)")) + self.confirmPasswordLineEdit.setPlaceholderText(util_ui.tr("Confirm password")) + self.changePasswordPushButton.setText(util_ui.tr("Set password")) + self.invalidPasswordsLabel.setText(util_ui.tr("Passwords do not match")) + self.emptyPasswordLabel.setText(util_ui.tr("Leaving blank will reset current password")) + self.warningLabel.setText(util_ui.tr("There is no way to recover lost passwords")) + self.statusComboBox.addItem(util_ui.tr("Online")) + self.statusComboBox.addItem(util_ui.tr("Away")) + self.statusComboBox.addItem(util_ui.tr("Busy")) + self.copyPublicKeyPushButton.setText(util_ui.tr("Copy public key")) + + self._set_default_profile_button_text() + + def _toggle_auto_profile(self): + if self._auto: + Settings.reset_auto_profile() + else: + Settings.set_auto_profile(self._profile_manager.get_path()) + self._auto = not self._auto + self._set_default_profile_button_text() + + def _set_default_profile_button_text(self): + if self._auto: + self.defaultProfilePushButton.setText(util_ui.tr("Mark as not default profile")) + else: + self.defaultProfilePushButton.setText(util_ui.tr("Mark as default profile")) + + def _save_password(self): + password = self.passwordLineEdit.text() + confirm_password = self.confirmPasswordLineEdit.text() + if password == confirm_password: + if not len(password) or len(password) >= 8: + self._toxes.set_password(password) + self.close() + else: + self.invalidPasswordsLabel.setText( + util_ui.tr("Password must be at least 8 symbols")) + self.invalidPasswordsLabel.setVisible(True) + else: + self.invalidPasswordsLabel.setText(util_ui.tr("Passwords do not match")) + self.invalidPasswordsLabel.setVisible(True) + + def _copy_tox_id(self): + util_ui.copy_to_clipboard(self._profile.tox_id) + + icon = self._get_accept_icon() + self.copyToxIdPushButton.setIcon(icon) + self.copyToxIdPushButton.setIconSize(QtCore.QSize(10, 10)) + + def _copy_public_key(self): + util_ui.copy_to_clipboard(self._profile.tox_id[:64]) + + icon = self._get_accept_icon() + self.copyPublicKeyPushButton.setIcon(icon) + self.copyPublicKeyPushButton.setIconSize(QtCore.QSize(10, 10)) + + def _set_new_no_spam(self): + self.toxIdLabel.setText(self._profile.set_new_nospam()) + + def _reset_avatar(self): + self._profile.reset_avatar(self._settings['identicons']) + + def _set_avatar(self): + choose = util_ui.tr("Choose avatar") + name = util_ui.file_dialog(choose, 'Images (*.png)') + if not name[0]: + return + bitmap = QtGui.QPixmap(name[0]) + bitmap.scaled(QtCore.QSize(128, 128), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + + byte_array = QtCore.QByteArray() + buffer = QtCore.QBuffer(byte_array) + buffer.open(QtCore.QIODevice.WriteOnly) + bitmap.save(buffer, 'PNG') + + self._profile.set_avatar(bytes(byte_array.data())) + + def _export_profile(self): + directory = util_ui.directory_dialog() + if not directory: + return + + reply = util_ui.question(util_ui.tr('Do you want to move your profile to this location?'), + util_ui.tr('Use new path')) + + self._settings.export(directory) + self._profile.export_db(directory) + self._profile_manager.export_profile(self._settings, directory, reply) + + @staticmethod + def _get_accept_icon(): + pixmap = QtGui.QPixmap(join_path(get_images_directory(), 'accept.png')) + + return QtGui.QIcon(pixmap) + diff --git a/toxygen/ui/views/profile_settings_screen.ui b/toxygen/ui/views/profile_settings_screen.ui new file mode 100644 index 0000000..ece0083 --- /dev/null +++ b/toxygen/ui/views/profile_settings_screen.ui @@ -0,0 +1,280 @@ + + + Form + + + + 0 + 0 + 900 + 702 + + + + Form + + + + + 30 + 10 + 161 + 31 + + + + TextLabel + + + + + + 30 + 90 + 161 + 31 + + + + TextLabel + + + + + + 30 + 50 + 421 + 31 + + + + + + + 30 + 130 + 421 + 31 + + + + + + + 520 + 30 + 311 + 31 + + + + + + + 40 + 180 + 131 + 21 + + + + TextLabel + + + + + + 40 + 210 + 831 + 61 + + + + TextLabel + + + true + + + + + + 40 + 280 + 371 + 31 + + + + PushButton + + + + + + 440 + 280 + 371 + 31 + + + + PushButton + + + + + + 520 + 80 + 321 + 35 + + + + PushButton + + + + + + 520 + 130 + 321 + 35 + + + + PushButton + + + + + + 60 + 380 + 161 + 31 + + + + TextLabel + + + + + + 50 + 420 + 421 + 31 + + + + + + + 50 + 470 + 421 + 31 + + + + + + + 500 + 420 + 381 + 21 + + + + TextLabel + + + + + + 60 + 580 + 381 + 21 + + + + TextLabel + + + + + + 40 + 630 + 831 + 35 + + + + PushButton + + + + + + 50 + 520 + 421 + 35 + + + + PushButton + + + + + + 500 + 470 + 381 + 21 + + + + TextLabel + + + + + + 40 + 330 + 371 + 35 + + + + PushButton + + + + + + 440 + 330 + 371 + 35 + + + + PushButton + + + + + + diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py index aa70861..128e85e 100644 --- a/toxygen/ui/widgets_factory.py +++ b/toxygen/ui/widgets_factory.py @@ -6,6 +6,7 @@ from ui.self_peer_screen import * from ui.group_invites_widgets import * from ui.group_settings_widgets import * from ui.group_bans_widgets import * +from ui.profile_settings_screen import ProfileSettings class WidgetsFactory: diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index c986019..71422c2 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -137,9 +137,8 @@ class Settings(dict): data = json.loads(data) else: data = {} - if 'path' in data: - del data['path'] - del data['name'] + if 'profile_path' in data: + del data['profile_path'] with open(p, 'w') as fl: fl.write(json.dumps(data)) From 1fa13db4e4343c60d66d78642c82b60eace067e6 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 15 Sep 2018 22:29:30 +0300 Subject: [PATCH 137/217] fixed bug with history loading and qtox screenshots autoaccept --- toxygen/file_transfers.py | 2 +- toxygen/profile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/toxygen/file_transfers.py b/toxygen/file_transfers.py index 7e0b193..1bbabe5 100644 --- a/toxygen/file_transfers.py +++ b/toxygen/file_transfers.py @@ -29,7 +29,7 @@ ALLOWED_FILES = ('toxygen_inline.png', 'utox-inline.png', 'sticker.png') def is_inline(file_name): - return file_name in ALLOWED_FILES or file_name.startswith('qTox_Screenshot_') + return file_name in ALLOWED_FILES or file_name.startswith('qTox_Screenshot_') or file_name.startswith('qTox_Image_') class StateSignal(QtCore.QObject): diff --git a/toxygen/profile.py b/toxygen/profile.py index ec5b545..ab73b69 100644 --- a/toxygen/profile.py +++ b/toxygen/profile.py @@ -567,7 +567,7 @@ class Profile(basecontact.BaseContact, Singleton): data.reverse() data = data[self._messages.count():self._messages.count() + PAGE_SIZE] for message in data: - if message.get_type() <= 1: # text message + if message.get_type() <= 1 or message.get_type() >= 5: # text message data = message.get_data() self.create_message_item(data[0], data[2], From 2aea5df33c4dd5d3155916c8e7ef25efdcf7cfc1 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 15 Sep 2018 22:50:25 +0300 Subject: [PATCH 138/217] proper fix for gc history --- toxygen/profile.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/toxygen/profile.py b/toxygen/profile.py index ab73b69..47f15f2 100644 --- a/toxygen/profile.py +++ b/toxygen/profile.py @@ -567,7 +567,7 @@ class Profile(basecontact.BaseContact, Singleton): data.reverse() data = data[self._messages.count():self._messages.count() + PAGE_SIZE] for message in data: - if message.get_type() <= 1 or message.get_type() >= 5: # text message + if message.get_type() <= 1: # text message data = message.get_data() self.create_message_item(data[0], data[2], @@ -588,13 +588,16 @@ class Profile(basecontact.BaseContact, Singleton): print('Incoming not started transfer - no info found') elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline image self.create_inline_item(message.get_data(), False) - else: # info message + elif message.get_type() < 5: # info message data = message.get_data() self.create_message_item(data[0], data[2], '', data[3], False) + else: + data = message.get_data() + self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3]) self._load_history = True def export_db(self, directory): From ae4eae92ae6ba6db699b27e266a652444c7f16f7 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 23 Sep 2018 13:04:27 +0300 Subject: [PATCH 139/217] minor bug fixes --- toxygen/app.py | 6 +++--- toxygen/contacts/basecontact.py | 2 +- toxygen/file_transfers/file_transfers_handler.py | 7 +++++-- toxygen/middleware/callbacks.py | 8 ++++---- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 4597d0a..a23816d 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -105,12 +105,12 @@ class App: def _stop_app(self): self._plugin_loader.stop() self._stop_threads() + self._file_transfer_handler.stop() self._tray.hide() self._save_profile() self._settings.close() self._kill_toxav() self._kill_tox() - del self._tox # ----------------------------------------------------------------------------------------------------------------- # App loading @@ -323,7 +323,6 @@ class App: self._save_profile(data) self._kill_toxav() self._kill_tox() - del self._tox # create new tox instance self._tox = self._create_tox(data) self._start_threads(False) @@ -398,7 +397,8 @@ class App: if updating: self._save_profile() self._settings.close() - del self._tox + self._kill_toxav() + self._kill_tox() return updating def _create_tox(self, data): diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index c64ffdb..2058890 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -126,7 +126,7 @@ class BaseContact: if os.path.isfile(avatar_path) and not avatar_path == self._get_default_avatar_path(): os.remove(avatar_path) if generate_new: - self.set_avatar(common.generate_avatar(self.tox_id)) + self.set_avatar(common.generate_avatar(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) else: self.load_avatar() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index cde1795..463152f 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -20,7 +20,7 @@ class FileTransfersHandler(ToxSave): profile.avatar_changed_event.add_callback(self._send_avatar_to_contacts) - def __del__(self): + def stop(self): self._settings['paused_file_transfers'] = self._paused_file_transfers if self._settings['resend_files'] else {} self._settings.save() @@ -259,13 +259,16 @@ class FileTransfersHandler(ToxSave): def _send_avatar_to_contacts(self, _): friends = self._get_all_friends() - for friend in friends: + for friend in filter(self._is_friend_online, friends): self.send_avatar(friend.number) # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- + def _is_friend_online(self, friend_number): + return self._get_friend_by_number(friend_number).status is not None + def _get_friend_by_number(self, friend_number): return self._contact_provider.get_friend_by_number(friend_number) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 77a029d..da94a7f 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -181,7 +181,7 @@ def tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER']) - icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) else: # AVATAR print('Avatar') @@ -371,7 +371,7 @@ def group_message(window, tray, tox, messenger, settings, profile): invoke_in_main_thread(tray_notification, name, message, tray, window) if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['MESSAGE']) - icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) return wrapped @@ -392,7 +392,7 @@ def group_private_message(window, tray, tox, messenger, settings, profile): invoke_in_main_thread(tray_notification, name, message, tray, window) if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['MESSAGE']) - icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) return wrapped @@ -411,7 +411,7 @@ def group_invite(window, settings, tray, profile, groups_service, contacts_provi title = util_ui.tr('New invite to group chat') text = util_ui.tr('{} invites you to group "{}"').format(friend.name, group_name) invoke_in_main_thread(tray_notification, title, text, tray, window) - icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) return wrapped From cf4cfa979cc0973595446789320ab38f3752c12c Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 24 Sep 2018 22:06:30 +0300 Subject: [PATCH 140/217] Fixed crash on reconnection, file transfers fixes --- toxygen/contacts/contacts_manager.py | 2 +- toxygen/contacts/profile.py | 6 +++-- toxygen/file_transfers/file_transfers.py | 14 +++++------ .../file_transfers/file_transfers_handler.py | 25 +++++++++++-------- toxygen/history/history.py | 13 +++++----- toxygen/middleware/callbacks.py | 5 ++-- toxygen/ui/items_factories.py | 4 +-- 7 files changed, 37 insertions(+), 32 deletions(-) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 156c409..87a61ff 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -116,7 +116,7 @@ class ContactsManager(ToxSave): if message.type == MESSAGE_TYPE['FILE_TRANSFER']: self._messages_items_factory.create_file_transfer_item(message) elif message.type == MESSAGE_TYPE['INLINE']: - self._messages_items_factory.create_inline_item(message.data) + self._messages_items_factory.create_inline_item(message) else: self._messages_items_factory.create_message_item(message) self._messages.scrollToBottom() diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index ed7cd83..81220af 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -2,6 +2,7 @@ from contacts import basecontact import random import threading import common.tox_save as tox_save +from middleware.threads import invoke_in_main_thread class Profile(basecontact.BaseContact, tox_save.ToxSave): @@ -73,12 +74,13 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave): Recreate tox instance """ self.status = None - self._reset_action() + invoke_in_main_thread(self._reset_action) def _reconnect(self): self._waiting_for_reconnection = False contacts = self._contacts_provider.get_all_friends() - if self.status is None or (all(list(map(lambda x: x.status is None, contacts))) and len(contacts)): + all_friends_offline = all(list(map(lambda x: x.status is None, contacts))) + if self.status is None or (all_friends_offline and len(contacts)): self._waiting_for_reconnection = True self.restart() self._timer = threading.Timer(50, self._reconnect) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 9b29575..0f04e5b 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -52,10 +52,10 @@ class FileTransfer: self._file_id = self._file = None def set_state_changed_handler(self, handler): - self._state_changed_event += handler + self._state_changed_event += lambda *args: invoke_in_main_thread(handler, *args) def set_transfer_finished_handler(self, handler): - self._finished_event += handler + self._finished_event += lambda *args: invoke_in_main_thread(handler, *args) def get_file_number(self): return self._file_number @@ -100,17 +100,17 @@ class FileTransfer: def cancelled(self): if self._file is not None: self._file.close() - self.state = FILE_TRANSFER_STATE['CANCELLED'] + self.set_state(FILE_TRANSFER_STATE['CANCELLED']) def pause(self, by_friend): if not by_friend: self.send_control(TOX_FILE_CONTROL['PAUSE']) else: - self.state = FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] + self.set_state(FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']) def send_control(self, control): if self._tox.file_control(self._friend_number, self._file_number, control): - self.state = control + self.set_state(control) def get_file_id(self): return self._tox.file_get_file_id(self._friend_number, self._file_number) @@ -121,10 +121,10 @@ class FileTransfer: t = -1 else: t = ((time() - self._creation_time) / percentage) * (1 - percentage) - invoke_in_main_thread(self._state_changed_event, self.state, percentage, int(t)) + self._state_changed_event(self.state, percentage, int(t)) def _finished(self): - invoke_in_main_thread(self._finished_event, self._friend_number, self._file_number) + self._finished_event(self._friend_number, self._file_number) # ----------------------------------------------------------------------------------------------------------------- # Send file diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 463152f..114383b 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -167,7 +167,7 @@ class FileTransfersHandler(ToxSave): return elif friend.status is None and is_resend: print('Error in sending') - raise RuntimeError() + return st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) file_name = os.path.basename(path) self._send_file_add_set_handlers(st, friend, file_name) @@ -210,7 +210,7 @@ class FileTransfersHandler(ToxSave): else: self.send_file(path, friend_number, True) friend.clear_unsent_files() - for key in list(self._paused_file_transfers.keys()): + for key in self._paused_file_transfers.keys(): (path, ft_friend_number, is_incoming, start_position) = self._paused_file_transfers[key] if not os.path.exists(path): del self._paused_file_transfers[key] @@ -221,14 +221,15 @@ class FileTransfersHandler(ToxSave): print('Exception in file sending: ' + str(ex)) def friend_exit(self, friend_number): - for friend_num, file_num in list(self._file_transfers.keys()): - if friend_num == friend_number: - ft = self._file_transfers[(friend_num, file_num)] - if type(ft) is SendTransfer: - self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, False, -1] - elif type(ft) is ReceiveTransfer and ft.state != FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: - self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, True, ft.total_size()] - self.cancel_transfer(friend_num, file_num, True) + for friend_num, file_num in self._file_transfers.keys(): + if friend_num != friend_number: + continue + ft = self._file_transfers[(friend_num, file_num)] + if type(ft) is SendTransfer: + self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, False, -1] + elif type(ft) is ReceiveTransfer and ft.state != FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, True, ft.total_size()] + self.cancel_transfer(friend_num, file_num, True) # ----------------------------------------------------------------------------------------------------------------- # Avatars support @@ -267,7 +268,9 @@ class FileTransfersHandler(ToxSave): # ----------------------------------------------------------------------------------------------------------------- def _is_friend_online(self, friend_number): - return self._get_friend_by_number(friend_number).status is not None + friend = self._get_friend_by_number(friend_number) + + return friend.status is not None def _get_friend_by_number(self, friend_number): return self._contact_provider.get_friend_by_number(friend_number) diff --git a/toxygen/history/history.py b/toxygen/history/history.py index f551df5..bd7e353 100644 --- a/toxygen/history/history.py +++ b/toxygen/history/history.py @@ -82,14 +82,15 @@ class History: messages.reverse() messages = messages[self._messages.count():self._messages.count() + PAGE_SIZE] for message in messages: - if message.get_type() in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): # text message + message_type = message.get_type() + if message_type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): # text message self._create_message_item(message) - elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: # file transfer - if message.get_status() is None: + elif message_type == MESSAGE_TYPE['FILE_TRANSFER']: # file transfer + if message.state == FILE_TRANSFER_STATE['UNSENT']: self._create_unsent_file_item(message) - continue - self._create_file_transfer_item(message) - elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline image + else: + self._create_file_transfer_item(message) + elif message_type == MESSAGE_TYPE['INLINE']: # inline image self._create_inline_item(message) else: # info message self._create_message_item(message) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index da94a7f..b9a4099 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -183,7 +183,7 @@ def tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER']) icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) - else: # AVATAR + else: # avatar print('Avatar') invoke_in_main_thread(file_transfer_handler.incoming_avatar, friend_number, @@ -208,7 +208,7 @@ def file_chunk_request(file_transfer_handler): Outgoing chunk """ def wrapped(tox, friend_number, file_number, position, size, user_data): - invoke_in_main_thread(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) + execute(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) return wrapped @@ -440,7 +440,6 @@ def group_peer_join(contacts_provider, groups_service): def group_peer_exit(contacts_provider, groups_service, contacts_manager): def wrapped(tox, group_number, peer_id, message, length, user_data): group = contacts_provider.get_group_by_number(group_number) - contacts_manager.remove_group_peer_by_id(group, peer_id) group.remove_peer(peer_id) invoke_in_main_thread(groups_service.generate_peers_list) diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index df6c95d..7346f8f 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -45,9 +45,9 @@ class MessagesItemsFactory: return item - def create_inline_item(self, data, append=True, position=0): + def create_inline_item(self, message, append=True, position=0): elem = QtWidgets.QListWidgetItem() - item = InlineImageItem(data, self._messages.width(), elem, self._messages) + item = InlineImageItem(message.data, self._messages.width(), elem, self._messages) elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) if append: self._messages.addItem(elem) From 62c6dbfb34223a3ccfb7b08a7cbd1f408fb3504d Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 29 Sep 2018 16:50:17 +0300 Subject: [PATCH 141/217] build.sh and docs update --- build/build.sh | 3 +++ docs/compile.md | 18 +++++++++++++----- docs/contact.md | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/build/build.sh b/build/build.sh index 522d6b2..fb6c4b2 100644 --- a/build/build.sh +++ b/build/build.sh @@ -7,6 +7,7 @@ cd toxygen/toxygen pyinstaller --windowed --icon=images/icon.ico main.py cp -r styles dist/main/ +find . -type f ! -name '*.qss' -delete cp -r plugins dist/main/ mkdir -p dist/main/ui/views cp -r ui/views dist/main/ui/ @@ -14,8 +15,10 @@ cp -r sounds dist/main/ cp -r smileys dist/main/ cp -r stickers dist/main/ cp -r bootstrap dist/main/ +find . -type f ! -name '*.json' -delete cp -r images dist/main/ cp -r translations dist/main/ +find . -name "*.ts" -type f -delete cd dist mv main toxygen diff --git a/docs/compile.md b/docs/compile.md index 995dc35..b4f6810 100644 --- a/docs/compile.md +++ b/docs/compile.md @@ -2,10 +2,18 @@ You can compile Toxygen using [PyInstaller](http://www.pyinstaller.org/) -Install PyInstaller: -``pip3 install pyinstaller`` +Use Dockerfile and build script from `build` directory: -Compile Toxygen: -``pyinstaller --windowed --icon images/icon.ico main.py`` +1. Build image: +``` +docker build -t toxygen . +``` -Don't forget to copy /images/, /sounds/, /translations/, /styles/, /smileys/, /stickers/, /plugins/ (and /libs/libtox.dll, /libs/libsodium.a on Windows) to /dist/main/ +2. Run container: +``` +docker run -it toxygen bash +``` + +3. Execute `build.sh` script: + +```./build.sh``` diff --git a/docs/contact.md b/docs/contact.md index c66da1c..9f80595 100644 --- a/docs/contact.md +++ b/docs/contact.md @@ -2,4 +2,4 @@ 1) Using GitHub - open issue -2) Use Toxygen Tox Group - add bot kalina@toxme.io (or 12EDB939AA529641CE53830B518D6EB30241868EE0E5023C46A372363CAEC91C2C948AEFE4EB) +2) Use Toxygen Tox Group (NGC) - ID: 59D68B2709E81A679CF91416CB0E3692851C6CFCABEFF98B7131E3805A6D75FA From a984b624b5b8894135856c576cc9152cb9c1913d Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 4 Mar 2020 00:34:10 +0300 Subject: [PATCH 142/217] Added ability to paste image --- toxygen/mainscreen_widgets.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/toxygen/mainscreen_widgets.py b/toxygen/mainscreen_widgets.py index 0d1c26b..dcbc075 100644 --- a/toxygen/mainscreen_widgets.py +++ b/toxygen/mainscreen_widgets.py @@ -73,8 +73,16 @@ class MessageArea(QtWidgets.QPlainTextEdit): if text.startswith('file://'): file_name = self.parse_file_name(text) self.parent.profile.send_file(file_name) - else: + elif text: self.insertPlainText(text) + else: + image = QtWidgets.QApplication.clipboard().image() + if image is not None: + byte_array = QtCore.QByteArray() + buffer = QtCore.QBuffer(byte_array) + buffer.open(QtCore.QIODevice.WriteOnly) + image.save(buffer, 'PNG') + self.parent.profile.send_screenshot(bytes(byte_array.data())) def parse_file_name(self, file_name): import urllib From 1554d9e53a76490534797e1df53b991953e85249 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 14 Mar 2020 15:33:57 +0300 Subject: [PATCH 143/217] Fixed bug with sending faux offline inlines --- toxygen/profile.py | 7 ++++--- toxygen/util.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/toxygen/profile.py b/toxygen/profile.py index 47f15f2..8cc6c6e 100644 --- a/toxygen/profile.py +++ b/toxygen/profile.py @@ -1099,10 +1099,11 @@ class Profile(basecontact.BaseContact, Singleton): file_name, friend.number, st.get_file_number()) - item = self.create_file_transfer_item(tm) friend.append_message(tm) - st.set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() + if friend_number == self.get_active_number(): + item = self.create_file_transfer_item(tm) + st.set_state_changed_handler(item.update_transfer_state) + self._messages.scrollToBottom() def send_file(self, path, number=None, is_resend=False, file_id=None): """ diff --git a/toxygen/util.py b/toxygen/util.py index d862d56..73959c6 100644 --- a/toxygen/util.py +++ b/toxygen/util.py @@ -5,7 +5,7 @@ import sys import re -program_version = '0.4.2' +program_version = '0.4.3' def cached(func): From 5019535c0d4dedf7e5a42f98f9f695d1346bfb3f Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 21 Mar 2020 22:05:17 +0300 Subject: [PATCH 144/217] Fixed bug with loading old messages for groups --- toxygen/profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toxygen/profile.py b/toxygen/profile.py index 8cc6c6e..cf15e02 100644 --- a/toxygen/profile.py +++ b/toxygen/profile.py @@ -597,7 +597,7 @@ class Profile(basecontact.BaseContact, Singleton): False) else: data = message.get_data() - self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3]) + self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3], False) self._load_history = True def export_db(self, directory): From 021ec52e3d9ef3fc812e6567a84f95c249b448fe Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 23 May 2020 18:43:52 +0300 Subject: [PATCH 145/217] Fixed travis build --- .travis.yml | 9 +++++---- tests/tests.py | 15 --------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index cfabadd..7fc55ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,12 +12,13 @@ before_install: - sudo apt-get install -y checkinstall build-essential - sudo apt-get install portaudio19-dev - sudo apt-get install libsecret-1-dev + - sudo apt-get install libconfig-dev libvpx-dev check -qq install: - - pip install sip - - pip install pyqt5 - - pip install pyaudio - - pip install opencv-python + - pip3 install sip + - pip3 install pyaudio + - pip3 install pyqt5==5.14 + - pip3 install opencv-python before_script: # Opus - wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz diff --git a/tests/tests.py b/tests/tests.py index 27618af..bbb877c 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -36,21 +36,6 @@ class TestProfileHelper: assert os.path.exists(path + 'avatars/') -class TestDNS: - - def test_dns(self): - Settings._instance = Settings.get_default_settings() - bot_id = '56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE51855D34D34D37CB5' - tox_id = tox_dns('groupbot@toxme.io') - assert tox_id == bot_id - - def test_dns2(self): - Settings._instance = Settings.get_default_settings() - bot_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6' - tox_id = tox_dns('echobot@toxme.io') - assert tox_id == bot_id - - class TestEncryption: def test_encr_decr(self): From 0a54012cf5ee72434b923bcde7d8f1a4e575ce2f Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sun, 24 May 2020 22:01:09 +0300 Subject: [PATCH 146/217] Fixed bug with auto accept if dir doesn't exist --- toxygen/profile.py | 4 +++- toxygen/util.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/toxygen/profile.py b/toxygen/profile.py index cf15e02..a0d8cd4 100644 --- a/toxygen/profile.py +++ b/toxygen/profile.py @@ -951,7 +951,9 @@ class Profile(basecontact.BaseContact, Singleton): file_number) elif auto: - path = settings['auto_accept_path'] or curr_directory() + path = settings['auto_accept_path'] + if not path or not os.path.exists(path): + path = curr_directory() self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size) tm = TransferMessage(MESSAGE_OWNER['FRIEND'], time.time(), diff --git a/toxygen/util.py b/toxygen/util.py index 73959c6..6157775 100644 --- a/toxygen/util.py +++ b/toxygen/util.py @@ -5,7 +5,7 @@ import sys import re -program_version = '0.4.3' +program_version = '0.4.4' def cached(func): From fda07698db7635e08da9259ce9d4b3d5ac3c1ffe Mon Sep 17 00:00:00 2001 From: emdee Date: Tue, 27 Sep 2022 12:36:20 +0000 Subject: [PATCH 147/217] merge in next_gen branch --- toxygen/app.py | 424 +++++++++++++++++++++++++++++++++++ toxygen/smileys/__init__.py | 0 toxygen/smileys/smileys.py | 74 ++++++ toxygen/stickers/__init__.py | 0 toxygen/stickers/stickers.py | 18 ++ 5 files changed, 516 insertions(+) create mode 100644 toxygen/app.py create mode 100644 toxygen/smileys/__init__.py create mode 100644 toxygen/smileys/smileys.py create mode 100644 toxygen/stickers/__init__.py create mode 100644 toxygen/stickers/stickers.py diff --git a/toxygen/app.py b/toxygen/app.py new file mode 100644 index 0000000..a23816d --- /dev/null +++ b/toxygen/app.py @@ -0,0 +1,424 @@ +from middleware import threads +import middleware.callbacks as callbacks +from PyQt5 import QtWidgets, QtGui, QtCore +import ui.password_screen as password_screen +import updater.updater as updater +import os +from middleware.tox_factory import tox_factory +import wrapper.toxencryptsave as tox_encrypt_save +import user_data.toxes +from user_data.settings import Settings +from ui.login_screen import LoginScreen +from user_data.profile_manager import ProfileManager +from plugin_support.plugin_support import PluginLoader +from ui.main_screen import MainWindow +from ui import tray +import utils.ui as util_ui +import utils.util as util +from contacts.profile import Profile +from file_transfers.file_transfers_handler import FileTransfersHandler +from contacts.contact_provider import ContactProvider +from contacts.friend_factory import FriendFactory +from contacts.group_factory import GroupFactory +from contacts.contacts_manager import ContactsManager +from av.calls_manager import CallsManager +from history.database import Database +from ui.widgets_factory import WidgetsFactory +from smileys.smileys import SmileyLoader +from ui.items_factories import MessagesItemsFactory, ContactItemsFactory +from messenger.messenger import Messenger +from network.tox_dns import ToxDns +from history.history import History +from file_transfers.file_transfers_messages_service import FileTransfersMessagesService +from groups.groups_service import GroupsService +from ui.create_profile_screen import CreateProfileScreen +from common.provider import Provider +from contacts.group_peer_factory import GroupPeerFactory +from user_data.backup_service import BackupService +import styles.style # TODO: dynamic loading + + +class App: + + def __init__(self, version, path_to_profile=None, uri=None): + self._version = version + self._app = self._settings = self._profile_manager = self._plugin_loader = self._messenger = None + self._tox = self._ms = self._init = self._main_loop = self._av_loop = None + self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None + self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = None + self._group_peer_factory = self._tox_dns = self._backup_service = None + self._group_factory = self._groups_service = self._profile = None + if uri is not None and uri.startswith('tox:'): + self._uri = uri[4:] + self._path = path_to_profile + + # ----------------------------------------------------------------------------------------------------------------- + # Public methods + # ----------------------------------------------------------------------------------------------------------------- + + def main(self): + """ + Main function of app. loads login screen if needed and starts main screen + """ + self._app = QtWidgets.QApplication([]) + self._load_icon() + + if util.get_platform() == 'Linux': + QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) + + self._load_base_style() + + if not self._select_and_load_profile(): + return + + if self._try_to_update(): + return + + self._load_app_styles() + self._load_app_translations() + + self._create_dependencies() + self._start_threads() + + if self._uri is not None: + self._ms.add_contact(self._uri) + + self._app.lastWindowClosed.connect(self._app.quit) + + self._execute_app() + + self._stop_app() + + # ----------------------------------------------------------------------------------------------------------------- + # App executing + # ----------------------------------------------------------------------------------------------------------------- + + def _execute_app(self): + while True: + try: + self._app.exec_() + except Exception as ex: + util.log('Unhandled exception: ' + str(ex)) + else: + break + + def _stop_app(self): + self._plugin_loader.stop() + self._stop_threads() + self._file_transfer_handler.stop() + self._tray.hide() + self._save_profile() + self._settings.close() + self._kill_toxav() + self._kill_tox() + + # ----------------------------------------------------------------------------------------------------------------- + # App loading + # ----------------------------------------------------------------------------------------------------------------- + + def _load_base_style(self): + with open(util.join_path(util.get_styles_directory(), 'dark_style.qss')) as fl: + style = fl.read() + self._app.setStyleSheet(style) + + def _load_app_styles(self): + # application color scheme + if self._settings['theme'] == 'dark': + return + for theme in self._settings.built_in_themes().keys(): + if self._settings['theme'] != theme: + continue + theme_path = self._settings.built_in_themes()[theme] + file_path = util.join_path(util.get_styles_directory(), theme_path) + with open(file_path) as fl: + style = fl.read() + self._app.setStyleSheet(style) + break + + def _load_login_screen_translations(self): + current_language, supported_languages = self._get_languages() + if current_language not in supported_languages: + return + lang_path = supported_languages[current_language] + translator = QtCore.QTranslator() + translator.load(util.get_translations_directory() + lang_path) + self._app.installTranslator(translator) + self._app.translator = translator + + def _load_icon(self): + icon_file = os.path.join(util.get_images_directory(), 'icon.png') + self._app.setWindowIcon(QtGui.QIcon(icon_file)) + + @staticmethod + def _get_languages(): + current_locale = QtCore.QLocale() + curr_language = current_locale.languageToString(current_locale.language()) + supported_languages = Settings.supported_languages() + + return curr_language, supported_languages + + def _load_app_translations(self): + lang = Settings.supported_languages()[self._settings['language']] + translator = QtCore.QTranslator() + translator.load(os.path.join(util.get_translations_directory(), lang)) + self._app.installTranslator(translator) + self._app.translator = translator + + def _select_and_load_profile(self): + encrypt_save = tox_encrypt_save.ToxEncryptSave() + self._toxes = user_data.toxes.ToxES(encrypt_save) + + if self._path is not None: # toxygen was started with path to profile + self._load_existing_profile(self._path) + else: + auto_profile = Settings.get_auto_profile() + if auto_profile is None: # no default profile + result = self._select_profile() + if result is None: + return False + if result.is_new_profile(): # create new profile + if not self._create_new_profile(result.profile_path): + return False + else: # load existing profile + self._load_existing_profile(result.profile_path) + self._path = result.profile_path + else: # default profile + self._path = auto_profile + self._load_existing_profile(auto_profile) + + if Settings.is_active_profile(self._path): # profile is in use + profile_name = util.get_profile_name_from_path(self._path) + title = util_ui.tr('Profile {}').format(profile_name) + text = util_ui.tr( + 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') + reply = util_ui.question(text, title) + if not reply: + return False + + self._settings.set_active_profile() + + return True + + # ----------------------------------------------------------------------------------------------------------------- + # Threads + # ----------------------------------------------------------------------------------------------------------------- + + def _start_threads(self, initial_start=True): + # init thread + self._init = threads.InitThread(self._tox, self._plugin_loader, self._settings, initial_start) + self._init.start() + + # starting threads for tox iterate and toxav iterate + self._main_loop = threads.ToxIterateThread(self._tox) + self._main_loop.start() + self._av_loop = threads.ToxAVIterateThread(self._tox.AV) + self._av_loop.start() + + if initial_start: + threads.start_file_transfer_thread() + + def _stop_threads(self, is_app_closing=True): + self._init.stop_thread() + + self._av_loop.stop_thread() + self._main_loop.stop_thread() + + if is_app_closing: + threads.stop_file_transfer_thread() + + # ----------------------------------------------------------------------------------------------------------------- + # Profiles + # ----------------------------------------------------------------------------------------------------------------- + + def _select_profile(self): + self._load_login_screen_translations() + ls = LoginScreen() + profiles = ProfileManager.find_profiles() + ls.update_select(profiles) + ls.show() + self._app.exec_() + + return ls.result + + def _load_existing_profile(self, profile_path): + self._profile_manager = ProfileManager(self._toxes, profile_path) + data = self._profile_manager.open_profile() + if self._toxes.is_data_encrypted(data): + data = self._enter_password(data) + self._settings = Settings(self._toxes, profile_path.replace('.tox', '.json')) + self._tox = self._create_tox(data) + + def _create_new_profile(self, profile_name): + result = self._get_create_profile_screen_result() + if result is None: + return False + if result.save_into_default_folder: + profile_path = util.join_path(Settings.get_default_path(), profile_name + '.tox') + else: + profile_path = util.join_path(util.curr_directory(__file__), profile_name + '.tox') + if os.path.isfile(profile_path): + util_ui.message_box(util_ui.tr('Profile with this name already exists'), + util_ui.tr('Error')) + return False + name = profile_name or 'toxygen_user' + self._tox = tox_factory() + self._tox.self_set_name(name if name else 'Toxygen User') + self._tox.self_set_status_message('Toxing on Toxygen') + self._path = profile_path + if result.password: + self._toxes.set_password(result.password) + self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) + self._profile_manager = ProfileManager(self._toxes, profile_path) + try: + self._save_profile() + except Exception as ex: + print(ex) + util.log('Profile creation exception: ' + str(ex)) + text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') + util_ui.message_box(text, util_ui.tr('Error')) + + return False + current_language, supported_languages = self._get_languages() + if current_language in supported_languages: + self._settings['language'] = current_language + self._settings.save() + + return True + + def _get_create_profile_screen_result(self): + cps = CreateProfileScreen() + cps.show() + self._app.exec_() + + return cps.result + + def _save_profile(self, data=None): + data = data or self._tox.get_savedata() + self._profile_manager.save_profile(data) + + # ----------------------------------------------------------------------------------------------------------------- + # Other private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _enter_password(self, data): + """ + Show password screen + """ + p = password_screen.PasswordScreen(self._toxes, data) + p.show() + self._app.lastWindowClosed.connect(self._app.quit) + self._app.exec_() + if p.result is not None: + return p.result + self._force_exit() + + def _reset(self): + """ + Create new tox instance (new network settings) + :return: tox instance + """ + self._contacts_manager.reset_contacts_statuses() + self._stop_threads(False) + data = self._tox.get_savedata() + self._save_profile(data) + self._kill_toxav() + self._kill_tox() + # create new tox instance + self._tox = self._create_tox(data) + self._start_threads(False) + + tox_savers = [self._friend_factory, self._group_factory, self._plugin_loader, self._contacts_manager, + self._contacts_provider, self._messenger, self._file_transfer_handler, self._groups_service, + self._profile] + for tox_saver in tox_savers: + tox_saver.set_tox(self._tox) + + self._calls_manager.set_toxav(self._tox.AV) + self._contacts_manager.update_friends_numbers() + self._contacts_manager.update_groups_lists() + self._contacts_manager.update_groups_numbers() + + self._init_callbacks() + + def _create_dependencies(self): + self._backup_service = BackupService(self._settings, self._profile_manager) + self._smiley_loader = SmileyLoader(self._settings) + self._tox_dns = ToxDns(self._settings) + self._ms = MainWindow(self._settings, self._tray) + db = Database(self._path.replace('.tox', '.db'), self._toxes) + + contact_items_factory = ContactItemsFactory(self._settings, self._ms) + self._friend_factory = FriendFactory(self._profile_manager, self._settings, + self._tox, db, contact_items_factory) + self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) + self._group_peer_factory = GroupPeerFactory(self._tox, self._profile_manager, db, contact_items_factory) + self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory, + self._group_peer_factory) + self._profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) + self._init_profile() + self._plugin_loader = PluginLoader(self._settings, self) + history = None + messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, + self._ms, lambda m: history.delete_message(m)) + history = History(self._contacts_provider, db, self._settings, self._ms, messages_items_factory) + self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, + self._contacts_provider, history, self._tox_dns, + messages_items_factory) + history.set_contacts_manager(self._contacts_manager) + self._calls_manager = CallsManager(self._tox.AV, self._settings, self._ms, self._contacts_manager) + self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, + self._contacts_provider, messages_items_factory, self._profile, + self._calls_manager) + file_transfers_message_service = FileTransfersMessagesService(self._contacts_manager, messages_items_factory, + self._profile, self._ms) + self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider, + file_transfers_message_service, self._profile) + messages_items_factory.set_file_transfers_handler(self._file_transfer_handler) + widgets_factory = None + widgets_factory_provider = Provider(lambda: widgets_factory) + self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms, + widgets_factory_provider) + widgets_factory = WidgetsFactory(self._settings, self._profile, self._profile_manager, self._contacts_manager, + self._file_transfer_handler, self._smiley_loader, self._plugin_loader, + self._toxes, self._version, self._groups_service, history, + self._contacts_provider) + self._tray = tray.init_tray(self._profile, self._settings, self._ms, self._toxes) + self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, self._profile, + self._plugin_loader, self._file_transfer_handler, history, self._calls_manager, + self._groups_service, self._toxes) + + self._tray.show() + self._ms.show() + + self._init_callbacks() + + def _try_to_update(self): + updating = updater.start_update_if_needed(self._version, self._settings) + if updating: + self._save_profile() + self._settings.close() + self._kill_toxav() + self._kill_tox() + return updating + + def _create_tox(self, data): + return tox_factory(data, self._settings) + + def _force_exit(self): + raise SystemExit() + + def _init_callbacks(self): + callbacks.init_callbacks(self._tox, self._profile, self._settings, self._plugin_loader, self._contacts_manager, + self._calls_manager, self._file_transfer_handler, self._ms, self._tray, + self._messenger, self._groups_service, self._contacts_provider) + + def _init_profile(self): + if not self._profile.has_avatar(): + self._profile.reset_avatar(self._settings['identicons']) + + def _kill_toxav(self): + self._calls_manager.set_toxav(None) + self._tox.AV.kill() + + def _kill_tox(self): + self._tox.kill() diff --git a/toxygen/smileys/__init__.py b/toxygen/smileys/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/smileys/smileys.py b/toxygen/smileys/smileys.py new file mode 100644 index 0000000..0391856 --- /dev/null +++ b/toxygen/smileys/smileys.py @@ -0,0 +1,74 @@ +from utils import util +import json +import os +from collections import OrderedDict +from PyQt5 import QtCore + + +class SmileyLoader: + """ + Class which loads smileys packs and insert smileys into messages + """ + + def __init__(self, settings): + super().__init__() + self._settings = settings + self._curr_pack = None # current pack name + self._smileys = {} # smileys dict. key - smiley (str), value - path to image (str) + self._list = [] # smileys list without duplicates + self.load_pack() + + def load_pack(self): + """ + Loads smiley pack + """ + pack_name = self._settings['smiley_pack'] + if self._settings['smileys'] and self._curr_pack != pack_name: + self._curr_pack = pack_name + path = util.join_path(self.get_smileys_path(), 'config.json') + try: + with open(path, encoding='utf8') as fl: + self._smileys = json.loads(fl.read()) + fl.seek(0) + tmp = json.loads(fl.read(), object_pairs_hook=OrderedDict) + print('Smiley pack {} loaded'.format(pack_name)) + keys, values, self._list = [], [], [] + for key, value in tmp.items(): + value = util.join_path(self.get_smileys_path(), value) + if value not in values: + keys.append(key) + values.append(value) + self._list = list(zip(keys, values)) + except Exception as ex: + self._smileys = {} + self._list = [] + print('Smiley pack {} was not loaded. Error: {}'.format(pack_name, ex)) + + def get_smileys_path(self): + return util.join_path(util.get_smileys_directory(), self._curr_pack) if self._curr_pack is not None else None + + @staticmethod + def get_packs_list(): + d = util.get_smileys_directory() + return [x[1] for x in os.walk(d)][0] + + def get_smileys(self): + return list(self._list) + + def add_smileys_to_text(self, text, edit): + """ + Adds smileys to text + :param text: message + :param edit: MessageEdit instance + :return text with smileys + """ + if not self._settings['smileys'] or not len(self._smileys): + return text + arr = text.split(' ') + for i in range(len(arr)): + if arr[i] in self._smileys: + file_name = self._smileys[arr[i]] # image name + arr[i] = ''.format(arr[i], file_name) + if file_name.endswith('.gif'): # animated smiley + edit.addAnimation(QtCore.QUrl(file_name), self.get_smileys_path() + file_name) + return ' '.join(arr) diff --git a/toxygen/stickers/__init__.py b/toxygen/stickers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/stickers/stickers.py b/toxygen/stickers/stickers.py new file mode 100644 index 0000000..14142c7 --- /dev/null +++ b/toxygen/stickers/stickers.py @@ -0,0 +1,18 @@ +import os +import utils.util as util + + +def load_stickers(): + """ + :return list of stickers + """ + result = [] + d = util.get_stickers_directory() + keys = [x[1] for x in os.walk(d)][0] + for key in keys: + path = util.join_path(d, key) + files = filter(lambda f: f.endswith('.png'), os.listdir(path)) + files = map(lambda f: util.join_path(path, f), files) + result.extend(files) + + return result From b51ec9bd71eee8b46c5a4d2e17824a870e6374cd Mon Sep 17 00:00:00 2001 From: emdee Date: Tue, 27 Sep 2022 12:38:39 +0000 Subject: [PATCH 148/217] merge in next_gen branch --- build/Dockerfile | 13 + build/build.sh | 33 + toxygen/av/__init__.py | 0 toxygen/av/call.py | 58 + toxygen/av/calls.py | 281 ++ toxygen/av/calls_manager.py | 116 + toxygen/av/screen_sharing.py | 22 + toxygen/bootstrap/__init__.py | 0 toxygen/bootstrap/bootstrap.py | 83 + toxygen/bootstrap/nodes.json | 1 + toxygen/common/__init__.py | 0 toxygen/common/event.py | 26 + toxygen/common/provider.py | 13 + toxygen/common/tox_save.py | 18 + toxygen/contacts/__init__.py | 0 toxygen/contacts/basecontact.py | 180 ++ toxygen/contacts/common.py | 50 + toxygen/contacts/contact.py | 333 +++ toxygen/contacts/contact_menu.py | 229 ++ toxygen/contacts/contact_provider.py | 107 + toxygen/contacts/contacts_manager.py | 575 ++++ toxygen/contacts/friend.py | 74 + toxygen/contacts/friend_factory.py | 44 + toxygen/contacts/group_chat.py | 137 + toxygen/contacts/group_factory.py | 53 + toxygen/contacts/group_peer_contact.py | 20 + toxygen/contacts/group_peer_factory.py | 23 + toxygen/contacts/profile.py | 87 + toxygen/file_transfers/__init__.py | 0 toxygen/file_transfers/file_transfers.py | 351 +++ .../file_transfers/file_transfers_handler.py | 304 ++ .../file_transfers_messages_service.py | 78 + toxygen/groups/__init__.py | 0 toxygen/groups/group_ban.py | 23 + toxygen/groups/group_invite.py | 23 + toxygen/groups/group_peer.py | 70 + toxygen/groups/groups_service.py | 242 ++ toxygen/groups/peers_list.py | 104 + toxygen/history/__init__.py | 0 toxygen/history/database.py | 201 ++ toxygen/history/history.py | 138 + toxygen/history/history_logs_generators.py | 48 + toxygen/messenger/__init__.py | 0 toxygen/messenger/messages.py | 239 ++ toxygen/messenger/messenger.py | 310 ++ toxygen/middleware/__init__.py | 0 toxygen/middleware/callbacks.py | 605 ++++ toxygen/middleware/threads.py | 172 ++ toxygen/middleware/tox_factory.py | 34 + toxygen/network/__init__.py | 0 toxygen/network/tox_dns.py | 65 + toxygen/notifications/__init__.py | 0 toxygen/notifications/sound.py | 54 + toxygen/notifications/tray.py | 22 + toxygen/plugin_support/__init__.py | 0 toxygen/plugin_support/plugin_support.py | 194 ++ toxygen/plugins/plugin_super_class.py | 39 +- toxygen/styles/dark_style.qss | 23 +- toxygen/styles/style.qss | 13 +- toxygen/ui/__init__.py | 0 toxygen/ui/av_widgets.py | 130 + toxygen/ui/contact_items.py | 97 + toxygen/ui/create_profile_screen.py | 52 + toxygen/ui/group_bans_widgets.py | 68 + toxygen/ui/group_invites_widgets.py | 127 + toxygen/ui/group_peers_list.py | 33 + toxygen/ui/group_settings_widgets.py | 77 + toxygen/ui/groups_widgets.py | 123 + toxygen/ui/items_factories.py | 90 + toxygen/ui/login_screen.py | 77 + toxygen/ui/main_screen.py | 718 +++++ toxygen/ui/main_screen_widgets.py | 496 ++++ toxygen/ui/menu.py | 680 +++++ toxygen/ui/messages_widgets.py | 449 +++ toxygen/ui/password_screen.py | 153 + toxygen/ui/peer_screen.py | 111 + toxygen/ui/profile_settings_screen.py | 157 + toxygen/ui/self_peer_screen.py | 66 + toxygen/ui/tray.py | 111 + toxygen/ui/widgets.py | 197 ++ toxygen/ui/widgets_factory.py | 97 + toxygen/updater/__init__.py | 0 toxygen/updater/updater.py | 124 + toxygen/user_data/__init__.py | 0 toxygen/user_data/backup_service.py | 40 + toxygen/user_data/profile_manager.py | 90 + toxygen/user_data/settings.py | 244 ++ toxygen/user_data/toxes.py | 24 + toxygen/utils/__init__.py | 0 toxygen/utils/ui.py | 54 + toxygen/utils/util.py | 170 ++ toxygen/wrapper/__init__.py | 0 toxygen/wrapper/libtox.py | 61 + toxygen/wrapper/tox.py | 2532 +++++++++++++++++ toxygen/wrapper/toxav.py | 363 +++ toxygen/wrapper/toxav_enums.py | 131 + toxygen/wrapper/toxcore_enums_and_consts.py | 954 +++++++ toxygen/wrapper/toxencryptsave.py | 74 + .../toxencryptsave_enums_and_consts.py | 29 + 99 files changed, 14895 insertions(+), 32 deletions(-) create mode 100644 build/Dockerfile create mode 100644 build/build.sh create mode 100644 toxygen/av/__init__.py create mode 100644 toxygen/av/call.py create mode 100644 toxygen/av/calls.py create mode 100644 toxygen/av/calls_manager.py create mode 100644 toxygen/av/screen_sharing.py create mode 100644 toxygen/bootstrap/__init__.py create mode 100644 toxygen/bootstrap/bootstrap.py create mode 100644 toxygen/bootstrap/nodes.json create mode 100644 toxygen/common/__init__.py create mode 100644 toxygen/common/event.py create mode 100644 toxygen/common/provider.py create mode 100644 toxygen/common/tox_save.py create mode 100644 toxygen/contacts/__init__.py create mode 100644 toxygen/contacts/basecontact.py create mode 100644 toxygen/contacts/common.py create mode 100644 toxygen/contacts/contact.py create mode 100644 toxygen/contacts/contact_menu.py create mode 100644 toxygen/contacts/contact_provider.py create mode 100644 toxygen/contacts/contacts_manager.py create mode 100644 toxygen/contacts/friend.py create mode 100644 toxygen/contacts/friend_factory.py create mode 100644 toxygen/contacts/group_chat.py create mode 100644 toxygen/contacts/group_factory.py create mode 100644 toxygen/contacts/group_peer_contact.py create mode 100644 toxygen/contacts/group_peer_factory.py create mode 100644 toxygen/contacts/profile.py create mode 100644 toxygen/file_transfers/__init__.py create mode 100644 toxygen/file_transfers/file_transfers.py create mode 100644 toxygen/file_transfers/file_transfers_handler.py create mode 100644 toxygen/file_transfers/file_transfers_messages_service.py create mode 100644 toxygen/groups/__init__.py create mode 100644 toxygen/groups/group_ban.py create mode 100644 toxygen/groups/group_invite.py create mode 100644 toxygen/groups/group_peer.py create mode 100644 toxygen/groups/groups_service.py create mode 100644 toxygen/groups/peers_list.py create mode 100644 toxygen/history/__init__.py create mode 100644 toxygen/history/database.py create mode 100644 toxygen/history/history.py create mode 100644 toxygen/history/history_logs_generators.py create mode 100644 toxygen/messenger/__init__.py create mode 100644 toxygen/messenger/messages.py create mode 100644 toxygen/messenger/messenger.py create mode 100644 toxygen/middleware/__init__.py create mode 100644 toxygen/middleware/callbacks.py create mode 100644 toxygen/middleware/threads.py create mode 100644 toxygen/middleware/tox_factory.py create mode 100644 toxygen/network/__init__.py create mode 100644 toxygen/network/tox_dns.py create mode 100644 toxygen/notifications/__init__.py create mode 100644 toxygen/notifications/sound.py create mode 100644 toxygen/notifications/tray.py create mode 100644 toxygen/plugin_support/__init__.py create mode 100644 toxygen/plugin_support/plugin_support.py create mode 100644 toxygen/ui/__init__.py create mode 100644 toxygen/ui/av_widgets.py create mode 100644 toxygen/ui/contact_items.py create mode 100644 toxygen/ui/create_profile_screen.py create mode 100644 toxygen/ui/group_bans_widgets.py create mode 100644 toxygen/ui/group_invites_widgets.py create mode 100644 toxygen/ui/group_peers_list.py create mode 100644 toxygen/ui/group_settings_widgets.py create mode 100644 toxygen/ui/groups_widgets.py create mode 100644 toxygen/ui/items_factories.py create mode 100644 toxygen/ui/login_screen.py create mode 100644 toxygen/ui/main_screen.py create mode 100644 toxygen/ui/main_screen_widgets.py create mode 100644 toxygen/ui/menu.py create mode 100644 toxygen/ui/messages_widgets.py create mode 100644 toxygen/ui/password_screen.py create mode 100644 toxygen/ui/peer_screen.py create mode 100644 toxygen/ui/profile_settings_screen.py create mode 100644 toxygen/ui/self_peer_screen.py create mode 100644 toxygen/ui/tray.py create mode 100644 toxygen/ui/widgets.py create mode 100644 toxygen/ui/widgets_factory.py create mode 100644 toxygen/updater/__init__.py create mode 100644 toxygen/updater/updater.py create mode 100644 toxygen/user_data/__init__.py create mode 100644 toxygen/user_data/backup_service.py create mode 100644 toxygen/user_data/profile_manager.py create mode 100644 toxygen/user_data/settings.py create mode 100644 toxygen/user_data/toxes.py create mode 100644 toxygen/utils/__init__.py create mode 100644 toxygen/utils/ui.py create mode 100644 toxygen/utils/util.py create mode 100644 toxygen/wrapper/__init__.py create mode 100644 toxygen/wrapper/libtox.py create mode 100644 toxygen/wrapper/tox.py create mode 100644 toxygen/wrapper/toxav.py create mode 100644 toxygen/wrapper/toxav_enums.py create mode 100644 toxygen/wrapper/toxcore_enums_and_consts.py create mode 100644 toxygen/wrapper/toxencryptsave.py create mode 100644 toxygen/wrapper/toxencryptsave_enums_and_consts.py diff --git a/build/Dockerfile b/build/Dockerfile new file mode 100644 index 0000000..0b45358 --- /dev/null +++ b/build/Dockerfile @@ -0,0 +1,13 @@ +FROM ubuntu:16.04 + +RUN apt-get update && \ +apt-get install build-essential libtool autotools-dev automake checkinstall cmake check git yasm libsodium-dev libopus-dev libvpx-dev pkg-config -y && \ +git clone https://github.com/ingvar1995/toxcore.git --branch=ngc_rebase && \ +cd toxcore && mkdir _build && cd _build && \ +cmake .. && make && make install + +RUN apt-get install portaudio19-dev python3-pyqt5 python3-pyaudio python3-pip -y && \ +pip3 install numpy pydenticon opencv-python pyinstaller + +RUN useradd -ms /bin/bash toxygen +USER toxygen diff --git a/build/build.sh b/build/build.sh new file mode 100644 index 0000000..fb6c4b2 --- /dev/null +++ b/build/build.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +cd ~ +git clone https://github.com/toxygen-project/toxygen.git --branch=next_gen +cd toxygen/toxygen + +pyinstaller --windowed --icon=images/icon.ico main.py + +cp -r styles dist/main/ +find . -type f ! -name '*.qss' -delete +cp -r plugins dist/main/ +mkdir -p dist/main/ui/views +cp -r ui/views dist/main/ui/ +cp -r sounds dist/main/ +cp -r smileys dist/main/ +cp -r stickers dist/main/ +cp -r bootstrap dist/main/ +find . -type f ! -name '*.json' -delete +cp -r images dist/main/ +cp -r translations dist/main/ +find . -name "*.ts" -type f -delete + +cd dist +mv main toxygen +cd toxygen +mv main toxygen +wget -O updater https://github.com/toxygen-project/toxygen_updater/releases/download/v0.1/toxygen_updater_linux_64 +echo "[Paths]" >> qt.conf +echo "Prefix = PyQt5/Qt" >> qt.conf +cd .. + +tar -zcvf toxygen_linux_64.tar.gz toxygen > /dev/null +rm -rf toxygen diff --git a/toxygen/av/__init__.py b/toxygen/av/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/av/call.py b/toxygen/av/call.py new file mode 100644 index 0000000..d3e023b --- /dev/null +++ b/toxygen/av/call.py @@ -0,0 +1,58 @@ + + +class Call: + + def __init__(self, out_audio, out_video, in_audio=False, in_video=False): + self._in_audio = in_audio + self._in_video = in_video + self._out_audio = out_audio + self._out_video = out_video + self._is_active = False + + def get_is_active(self): + return self._is_active + + def set_is_active(self, value): + self._is_active = value + + is_active = property(get_is_active, set_is_active) + + # ----------------------------------------------------------------------------------------------------------------- + # Audio + # ----------------------------------------------------------------------------------------------------------------- + + def get_in_audio(self): + return self._in_audio + + def set_in_audio(self, value): + self._in_audio = value + + in_audio = property(get_in_audio, set_in_audio) + + def get_out_audio(self): + return self._out_audio + + def set_out_audio(self, value): + self._out_audio = value + + out_audio = property(get_out_audio, set_out_audio) + + # ----------------------------------------------------------------------------------------------------------------- + # Video + # ----------------------------------------------------------------------------------------------------------------- + + def get_in_video(self): + return self._in_video + + def set_in_video(self, value): + self._in_video = value + + in_video = property(get_in_video, set_in_video) + + def get_out_video(self): + return self._out_video + + def set_out_video(self, value): + self._out_video = value + + out_video = property(get_out_video, set_out_video) diff --git a/toxygen/av/calls.py b/toxygen/av/calls.py new file mode 100644 index 0000000..d5f2fe7 --- /dev/null +++ b/toxygen/av/calls.py @@ -0,0 +1,281 @@ +import pyaudio +import time +import threading +from wrapper.toxav_enums import * +import cv2 +import itertools +import numpy as np +from av import screen_sharing +from av.call import Call +import common.tox_save + + +class AV(common.tox_save.ToxAvSave): + + def __init__(self, toxav, settings): + super().__init__(toxav) + self._settings = settings + self._running = True + + self._calls = {} # dict: key - friend number, value - Call instance + + self._audio = None + self._audio_stream = None + self._audio_thread = None + self._audio_running = False + self._out_stream = None + + self._audio_rate = 8000 + self._audio_channels = 1 + self._audio_duration = 60 + self._audio_sample_count = self._audio_rate * self._audio_channels * self._audio_duration // 1000 + + self._video = None + self._video_thread = None + self._video_running = False + + self._video_width = 640 + self._video_height = 480 + + def stop(self): + self._running = False + self.stop_audio_thread() + self.stop_video_thread() + + def __contains__(self, friend_number): + return friend_number in self._calls + + # ----------------------------------------------------------------------------------------------------------------- + # Calls + # ----------------------------------------------------------------------------------------------------------------- + + def __call__(self, friend_number, audio, video): + """Call friend with specified number""" + self._toxav.call(friend_number, 32 if audio else 0, 5000 if video else 0) + self._calls[friend_number] = Call(audio, video) + threading.Timer(30.0, lambda: self.finish_not_started_call(friend_number)).start() + + def accept_call(self, friend_number, audio_enabled, video_enabled): + if self._running: + self._calls[friend_number] = Call(audio_enabled, video_enabled) + self._toxav.answer(friend_number, 32 if audio_enabled else 0, 5000 if video_enabled else 0) + if audio_enabled: + self.start_audio_thread() + if video_enabled: + self.start_video_thread() + + def finish_call(self, friend_number, by_friend=False): + if not by_friend: + self._toxav.call_control(friend_number, TOXAV_CALL_CONTROL['CANCEL']) + if friend_number in self._calls: + del self._calls[friend_number] + if not len(list(filter(lambda c: c.out_audio, self._calls))): + self.stop_audio_thread() + if not len(list(filter(lambda c: c.out_video, self._calls))): + self.stop_video_thread() + + def finish_not_started_call(self, friend_number): + if friend_number in self: + call = self._calls[friend_number] + if not call.is_active: + self.finish_call(friend_number) + + def toxav_call_state_cb(self, friend_number, state): + """ + New call state + """ + call = self._calls[friend_number] + call.is_active = True + + call.in_audio = state | TOXAV_FRIEND_CALL_STATE['SENDING_A'] > 0 + call.in_video = state | TOXAV_FRIEND_CALL_STATE['SENDING_V'] > 0 + + if state | TOXAV_FRIEND_CALL_STATE['ACCEPTING_A'] and call.out_audio: + self.start_audio_thread() + + if state | TOXAV_FRIEND_CALL_STATE['ACCEPTING_V'] and call.out_video: + self.start_video_thread() + + def is_video_call(self, number): + return number in self and self._calls[number].in_video + + # ----------------------------------------------------------------------------------------------------------------- + # Threads + # ----------------------------------------------------------------------------------------------------------------- + + def start_audio_thread(self): + """ + Start audio sending + """ + if self._audio_thread is not None: + return + + self._audio_running = True + + self._audio = pyaudio.PyAudio() + self._audio_stream = self._audio.open(format=pyaudio.paInt16, + rate=self._audio_rate, + channels=self._audio_channels, + input=True, + input_device_index=self._settings.audio['input'], + frames_per_buffer=self._audio_sample_count * 10) + + self._audio_thread = threading.Thread(target=self.send_audio) + self._audio_thread.start() + + def stop_audio_thread(self): + + if self._audio_thread is None: + return + + self._audio_running = False + + self._audio_thread.join() + + self._audio_thread = None + self._audio_stream = None + self._audio = None + + if self._out_stream is not None: + self._out_stream.stop_stream() + self._out_stream.close() + self._out_stream = None + + def start_video_thread(self): + if self._video_thread is not None: + return + + self._video_running = True + self._video_width = s.video['width'] + self._video_height = s.video['height'] + + if s.video['device'] == -1: + self._video = screen_sharing.DesktopGrabber(self._settings.video['x'], self._settings.video['y'], + self._settings.video['width'], self._settings.video['height']) + else: + self._video = cv2.VideoCapture(self._settings.video['device']) + self._video.set(cv2.CAP_PROP_FPS, 25) + self._video.set(cv2.CAP_PROP_FRAME_WIDTH, self._video_width) + self._video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._video_height) + + self._video_thread = threading.Thread(target=self.send_video) + self._video_thread.start() + + def stop_video_thread(self): + if self._video_thread is None: + return + + self._video_running = False + self._video_thread.join() + self._video_thread = None + self._video = None + + # ----------------------------------------------------------------------------------------------------------------- + # Incoming chunks + # ----------------------------------------------------------------------------------------------------------------- + + def audio_chunk(self, samples, channels_count, rate): + """ + Incoming chunk + """ + + if self._out_stream is None: + self._out_stream = self._audio.open(format=pyaudio.paInt16, + channels=channels_count, + rate=rate, + output_device_index=self._settings.audio['output'], + output=True) + self._out_stream.write(samples) + + # ----------------------------------------------------------------------------------------------------------------- + # AV sending + # ----------------------------------------------------------------------------------------------------------------- + + def send_audio(self): + """ + This method sends audio to friends + """ + + while self._audio_running: + try: + pcm = self._audio_stream.read(self._audio_sample_count) + if pcm: + for friend_num in self._calls: + if self._calls[friend_num].out_audio: + try: + self._toxav.audio_send_frame(friend_num, pcm, self._audio_sample_count, + self._audio_channels, self._audio_rate) + except: + pass + except: + pass + + time.sleep(0.01) + + def send_video(self): + """ + This method sends video to friends + """ + while self._video_running: + try: + result, frame = self._video.read() + if result: + height, width, channels = frame.shape + for friend_num in self._calls: + if self._calls[friend_num].out_video: + try: + y, u, v = self.convert_bgr_to_yuv(frame) + self._toxav.video_send_frame(friend_num, width, height, y, u, v) + except: + pass + except: + pass + + time.sleep(0.01) + + def convert_bgr_to_yuv(self, frame): + """ + :param frame: input bgr frame + :return y, u, v: y, u, v values of frame + + How this function works: + OpenCV creates YUV420 frame from BGR + This frame has following structure and size: + width, height - dim of input frame + width, height * 1.5 - dim of output frame + + width + ------------------------- + | | + | Y | height + | | + ------------------------- + | | | + | U even | U odd | height // 4 + | | | + ------------------------- + | | | + | V even | V odd | height // 4 + | | | + ------------------------- + + width // 2 width // 2 + + Y, U, V can be extracted using slices and joined in one list using itertools.chain.from_iterable() + Function returns bytes(y), bytes(u), bytes(v), because it is required for ctypes + """ + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV_I420) + + y = frame[:self._video_height, :] + y = list(itertools.chain.from_iterable(y)) + + u = np.zeros((self._video_height // 2, self._video_width // 2), dtype=np.int) + u[::2, :] = frame[self._video_height:self._video_height * 5 // 4, :self._video_width // 2] + u[1::2, :] = frame[self._video_height:self._video_height * 5 // 4, self._video_width // 2:] + u = list(itertools.chain.from_iterable(u)) + v = np.zeros((self._video_height // 2, self._video_width // 2), dtype=np.int) + v[::2, :] = frame[self._video_height * 5 // 4:, :self._video_width // 2] + v[1::2, :] = frame[self._video_height * 5 // 4:, self._video_width // 2:] + v = list(itertools.chain.from_iterable(v)) + + return bytes(y), bytes(u), bytes(v) diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py new file mode 100644 index 0000000..5a48672 --- /dev/null +++ b/toxygen/av/calls_manager.py @@ -0,0 +1,116 @@ +import threading +import cv2 +import av.calls +from messenger.messages import * +from ui import av_widgets +import common.event as event + + +class CallsManager: + + def __init__(self, toxav, settings, screen, contacts_manager): + self._call = av.calls.AV(toxav, settings) # object with data about calls + self._call_widgets = {} # dict of incoming call widgets + self._incoming_calls = set() + self._settings = settings + self._screen = screen + self._contacts_manager = contacts_manager + self._call_started_event = event.Event() # friend_number, audio, video, is_outgoing + self._call_finished_event = event.Event() # friend_number, is_declined + + def set_toxav(self, toxav): + self._call.set_toxav(toxav) + + # ----------------------------------------------------------------------------------------------------------------- + # Events + # ----------------------------------------------------------------------------------------------------------------- + + def get_call_started_event(self): + return self._call_started_event + + call_started_event = property(get_call_started_event) + + def get_call_finished_event(self): + return self._call_finished_event + + call_finished_event = property(get_call_finished_event) + + # ----------------------------------------------------------------------------------------------------------------- + # AV support + # ----------------------------------------------------------------------------------------------------------------- + + def call_click(self, audio=True, video=False): + """User clicked audio button in main window""" + num = self._contacts_manager.get_active_number() + if not self._contacts_manager.is_active_a_friend(): + return + if num not in self._call and self._contacts_manager.is_active_online(): # start call + if not self._settings.audio['enabled']: + return + self._call(num, audio, video) + self._screen.active_call() + self._call_started_event(num, audio, video, True) + elif num in self._call: # finish or cancel call if you call with active friend + self.stop_call(num, False) + + def incoming_call(self, audio, video, friend_number): + """ + Incoming call from friend. + """ + if not self._settings.audio['enabled']: + return + friend = self._contacts_manager.get_friend_by_number(friend_number) + self._call_started_event(friend_number, audio, video, False) + self._incoming_calls.add(friend_number) + if friend_number == self._contacts_manager.get_active_number(): + self._screen.incoming_call() + else: + friend.actions = True + text = util_ui.tr("Incoming video call") if video else util_ui.tr("Incoming audio call") + self._call_widgets[friend_number] = self._get_incoming_call_widget(friend_number, text, friend.name) + self._call_widgets[friend_number].set_pixmap(friend.get_pixmap()) + self._call_widgets[friend_number].show() + + def accept_call(self, friend_number, audio, video): + """ + Accept incoming call with audio or video + """ + self._call.accept_call(friend_number, audio, video) + self._screen.active_call() + if friend_number in self._incoming_calls: + self._incoming_calls.remove(friend_number) + del self._call_widgets[friend_number] + + def stop_call(self, friend_number, by_friend): + """ + Stop call with friend + """ + if friend_number in self._incoming_calls: + self._incoming_calls.remove(friend_number) + is_declined = True + else: + is_declined = False + self._screen.call_finished() + is_video = self._call.is_video_call(friend_number) + self._call.finish_call(friend_number, by_friend) # finish or decline call + if friend_number in self._call_widgets: + self._call_widgets[friend_number].close() + del self._call_widgets[friend_number] + + def destroy_window(): + if is_video: + cv2.destroyWindow(str(friend_number)) + + threading.Timer(2.0, destroy_window).start() + self._call_finished_event(friend_number, is_declined) + + def friend_exit(self, friend_number): + if friend_number in self._call: + self._call.finish_call(friend_number, True) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _get_incoming_call_widget(self, friend_number, text, friend_name): + return av_widgets.IncomingCallWidget(self._settings, self, friend_number, text, friend_name) diff --git a/toxygen/av/screen_sharing.py b/toxygen/av/screen_sharing.py new file mode 100644 index 0000000..265658c --- /dev/null +++ b/toxygen/av/screen_sharing.py @@ -0,0 +1,22 @@ +import numpy as np +from PyQt5 import QtWidgets + + +class DesktopGrabber: + + def __init__(self, x, y, width, height): + self._x = x + self._y = y + self._width = width + self._height = height + self._width -= width % 4 + self._height -= height % 4 + self._screen = QtWidgets.QApplication.primaryScreen() + + def read(self): + pixmap = self._screen.grabWindow(0, self._x, self._y, self._width, self._height) + image = pixmap.toImage() + s = image.bits().asstring(self._width * self._height * 4) + arr = np.fromstring(s, dtype=np.uint8).reshape((self._height, self._width, 4)) + + return True, arr diff --git a/toxygen/bootstrap/__init__.py b/toxygen/bootstrap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/bootstrap/bootstrap.py b/toxygen/bootstrap/bootstrap.py new file mode 100644 index 0000000..fad68c4 --- /dev/null +++ b/toxygen/bootstrap/bootstrap.py @@ -0,0 +1,83 @@ +import random +import urllib.request +from utils.util import * +from PyQt5 import QtNetwork, QtCore +import json + + +DEFAULT_NODES_COUNT = 4 + + +class Node: + + def __init__(self, node): + self._ip, self._port, self._tox_key = node['ipv4'], node['port'], node['public_key'] + self._priority = random.randint(1, 1000000) if node['status_tcp'] and node['status_udp'] else 0 + + def get_priority(self): + return self._priority + + priority = property(get_priority) + + def get_data(self): + return self._ip, self._port, self._tox_key + + +def generate_nodes(nodes_count=DEFAULT_NODES_COUNT): + with open(_get_nodes_path(), 'rt') as fl: + json_nodes = json.loads(fl.read())['nodes'] + nodes = map(lambda json_node: Node(json_node), json_nodes) + nodes = filter(lambda n: n.priority > 0, nodes) + sorted_nodes = sorted(nodes, key=lambda x: x.priority) + if nodes_count is not None: + sorted_nodes = sorted_nodes[-DEFAULT_NODES_COUNT:] + for node in sorted_nodes: + yield node.get_data() + + +def download_nodes_list(settings): + url = 'https://nodes.tox.chat/json' + if not settings['download_nodes_list']: + return + + if not settings['proxy_type']: # no proxy + try: + req = urllib.request.Request(url) + req.add_header('Content-Type', 'application/json') + response = urllib.request.urlopen(req) + result = response.read() + _save_nodes(result) + except Exception as ex: + log('TOX nodes loading error: ' + str(ex)) + else: # proxy + netman = QtNetwork.QNetworkAccessManager() + proxy = QtNetwork.QNetworkProxy() + proxy.setType( + QtNetwork.QNetworkProxy.Socks5Proxy if settings['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) + proxy.setHostName(settings['proxy_host']) + proxy.setPort(settings['proxy_port']) + netman.setProxy(proxy) + try: + request = QtNetwork.QNetworkRequest() + request.setUrl(QtCore.QUrl(url)) + reply = netman.get(request) + + while not reply.isFinished(): + QtCore.QThread.msleep(1) + QtCore.QCoreApplication.processEvents() + data = bytes(reply.readAll().data()) + _save_nodes(data) + except Exception as ex: + log('TOX nodes loading error: ' + str(ex)) + + +def _get_nodes_path(): + return join_path(curr_directory(__file__), 'nodes.json') + + +def _save_nodes(nodes): + if not nodes: + return + print('Saving nodes...') + with open(_get_nodes_path(), 'wb') as fl: + fl.write(nodes) diff --git a/toxygen/bootstrap/nodes.json b/toxygen/bootstrap/nodes.json new file mode 100644 index 0000000..5314998 --- /dev/null +++ b/toxygen/bootstrap/nodes.json @@ -0,0 +1 @@ +{"nodes":[{"ipv4":"80.211.19.83","ipv6":"-","port":33445,"public_key":"A2D7BF17C10A12C339B9F4E8DD77DEEE8457D580535A6F0D0F9AF04B8B4C4420","status_udp":true,"status_tcp":true}]} \ No newline at end of file diff --git a/toxygen/common/__init__.py b/toxygen/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/common/event.py b/toxygen/common/event.py new file mode 100644 index 0000000..687a34d --- /dev/null +++ b/toxygen/common/event.py @@ -0,0 +1,26 @@ + + +class Event: + + def __init__(self): + self._callbacks = set() + + def __iadd__(self, callback): + self.add_callback(callback) + + return self + + def __isub__(self, callback): + self.remove_callback(callback) + + return self + + def __call__(self, *args, **kwargs): + for callback in self._callbacks: + callback(*args, **kwargs) + + def add_callback(self, callback): + self._callbacks.add(callback) + + def remove_callback(self, callback): + self._callbacks.discard(callback) diff --git a/toxygen/common/provider.py b/toxygen/common/provider.py new file mode 100644 index 0000000..d16edb4 --- /dev/null +++ b/toxygen/common/provider.py @@ -0,0 +1,13 @@ + + +class Provider: + + def __init__(self, get_item_action): + self._get_item_action = get_item_action + self._item = None + + def get_item(self): + if self._item is None: + self._item = self._get_item_action() + + return self._item diff --git a/toxygen/common/tox_save.py b/toxygen/common/tox_save.py new file mode 100644 index 0000000..09c159b --- /dev/null +++ b/toxygen/common/tox_save.py @@ -0,0 +1,18 @@ + + +class ToxSave: + + def __init__(self, tox): + self._tox = tox + + def set_tox(self, tox): + self._tox = tox + + +class ToxAvSave: + + def __init__(self, toxav): + self._toxav = toxav + + def set_toxav(self, toxav): + self._toxav = toxav diff --git a/toxygen/contacts/__init__.py b/toxygen/contacts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py new file mode 100644 index 0000000..2058890 --- /dev/null +++ b/toxygen/contacts/basecontact.py @@ -0,0 +1,180 @@ +from user_data.settings import * +from PyQt5 import QtCore, QtGui +from wrapper.toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE +import utils.util as util +import common.event as event +import contacts.common as common + + +class BaseContact: + """ + Class encapsulating TOX contact + Properties: name (alias of contact or name), status_message, status (connection status) + widget - widget for update, tox id (or public key) + Base class for all contacts. + """ + + def __init__(self, profile_manager, name, status_message, widget, tox_id): + """ + :param name: name, example: 'Toxygen user' + :param status_message: status message, example: 'Toxing on Toxygen' + :param widget: ContactItem instance + :param tox_id: tox id of contact + """ + self._profile_manager = profile_manager + self._name, self._status_message = name, status_message + self._status, self._widget = None, widget + self._tox_id = tox_id + self._name_changed_event = event.Event() + self._status_message_changed_event = event.Event() + self._status_changed_event = event.Event() + self._avatar_changed_event = event.Event() + self.init_widget() + + # ----------------------------------------------------------------------------------------------------------------- + # Name - current name or alias of user + # ----------------------------------------------------------------------------------------------------------------- + + def get_name(self): + return self._name + + def set_name(self, value): + if self._name == value: + return + self._name = value + self._widget.name.setText(self._name) + self._widget.name.repaint() + self._name_changed_event(self._name) + + name = property(get_name, set_name) + + def get_name_changed_event(self): + return self._name_changed_event + + name_changed_event = property(get_name_changed_event) + + # ----------------------------------------------------------------------------------------------------------------- + # Status message + # ----------------------------------------------------------------------------------------------------------------- + + def get_status_message(self): + return self._status_message + + def set_status_message(self, value): + if self._status_message == value: + return + self._status_message = value + self._widget.status_message.setText(self._status_message) + self._widget.status_message.repaint() + self._status_message_changed_event(self._status_message) + + status_message = property(get_status_message, set_status_message) + + def get_status_message_changed_event(self): + return self._status_message_changed_event + + status_message_changed_event = property(get_status_message_changed_event) + + # ----------------------------------------------------------------------------------------------------------------- + # Status + # ----------------------------------------------------------------------------------------------------------------- + + def get_status(self): + return self._status + + def set_status(self, value): + if self._status == value: + return + self._status = value + self._widget.connection_status.update(value) + self._status_changed_event(self._status) + + status = property(get_status, set_status) + + def get_status_changed_event(self): + return self._status_changed_event + + status_changed_event = property(get_status_changed_event) + + # ----------------------------------------------------------------------------------------------------------------- + # TOX ID. WARNING: for friend it will return public key, for profile - full address + # ----------------------------------------------------------------------------------------------------------------- + + def get_tox_id(self): + return self._tox_id + + tox_id = property(get_tox_id) + + # ----------------------------------------------------------------------------------------------------------------- + # Avatars + # ----------------------------------------------------------------------------------------------------------------- + + def load_avatar(self): + """ + Tries to load avatar of contact or uses default avatar + """ + avatar_path = self.get_avatar_path() + width = self._widget.avatar_label.width() + pixmap = QtGui.QPixmap(avatar_path) + self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation)) + self._widget.avatar_label.repaint() + self._avatar_changed_event(avatar_path) + + def reset_avatar(self, generate_new): + avatar_path = self.get_avatar_path() + if os.path.isfile(avatar_path) and not avatar_path == self._get_default_avatar_path(): + os.remove(avatar_path) + if generate_new: + self.set_avatar(common.generate_avatar(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) + else: + self.load_avatar() + + def set_avatar(self, avatar): + avatar_path = self.get_contact_avatar_path() + with open(avatar_path, 'wb') as f: + f.write(avatar) + self.load_avatar() + + def get_pixmap(self): + return self._widget.avatar_label.pixmap() + + def get_avatar_path(self): + avatar_path = self.get_contact_avatar_path() + if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image + avatar_path = self._get_default_avatar_path() + + return avatar_path + + def get_contact_avatar_path(self): + directory = util.join_path(self._profile_manager.get_dir(), 'avatars') + + return util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])) + + def has_avatar(self): + path = self.get_contact_avatar_path() + + return util.file_exists(path) + + def get_avatar_changed_event(self): + return self._avatar_changed_event + + avatar_changed_event = property(get_avatar_changed_event) + + # ----------------------------------------------------------------------------------------------------------------- + # Widgets + # ----------------------------------------------------------------------------------------------------------------- + + def init_widget(self): + self._widget.name.setText(self._name) + self._widget.status_message.setText(self._status_message) + self._widget.connection_status.update(self._status) + self.load_avatar() + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def _get_default_avatar_path(): + return util.join_path(util.get_images_directory(), 'avatar.png') diff --git a/toxygen/contacts/common.py b/toxygen/contacts/common.py new file mode 100644 index 0000000..27750a2 --- /dev/null +++ b/toxygen/contacts/common.py @@ -0,0 +1,50 @@ +from pydenticon import Generator +import hashlib + + +# ----------------------------------------------------------------------------------------------------------------- +# Typing notifications +# ----------------------------------------------------------------------------------------------------------------- + +class BaseTypingNotificationHandler: + + DEFAULT_HANDLER = None + + def __init__(self): + pass + + def send(self, tox, is_typing): + pass + + +class FriendTypingNotificationHandler(BaseTypingNotificationHandler): + + def __init__(self, friend_number): + super().__init__() + self._friend_number = friend_number + + def send(self, tox, is_typing): + tox.self_set_typing(self._friend_number, is_typing) + + +BaseTypingNotificationHandler.DEFAULT_HANDLER = BaseTypingNotificationHandler() + + +# ----------------------------------------------------------------------------------------------------------------- +# Identicons support +# ----------------------------------------------------------------------------------------------------------------- + + +def generate_avatar(public_key): + foreground = ['rgb(45,79,255)', 'rgb(185, 66, 244)', 'rgb(185, 66, 244)', + 'rgb(254,180,44)', 'rgb(252, 2, 2)', 'rgb(109, 198, 0)', + 'rgb(226,121,234)', 'rgb(130, 135, 124)', + 'rgb(30,179,253)', 'rgb(160, 157, 0)', + 'rgb(232,77,65)', 'rgb(102, 4, 4)', + 'rgb(49,203,115)', + 'rgb(141,69,170)'] + generator = Generator(5, 5, foreground=foreground, background='rgba(42,42,42,0)') + digest = hashlib.sha256(public_key.encode('utf-8')).hexdigest() + identicon = generator.generate(digest, 220, 220, padding=(10, 10, 10, 10)) + + return identicon diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py new file mode 100644 index 0000000..e88acf2 --- /dev/null +++ b/toxygen/contacts/contact.py @@ -0,0 +1,333 @@ +from history.database import * +from contacts import basecontact, common +from messenger.messages import * +from contacts.contact_menu import * +from file_transfers import file_transfers as ft +import re + + +class Contact(basecontact.BaseContact): + """ + Class encapsulating TOX contact + Properties: number, message getter, history etc. Base class for friend and gc classes + """ + + def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id): + """ + :param message_getter: gets messages from db + :param number: number of friend. + """ + super().__init__(profile_manager, name, status_message, widget, tox_id) + self._number = number + self._new_messages = False + self._visible = True + self._alias = False + self._message_getter = message_getter + self._corr = [] + self._unsaved_messages = 0 + self._history_loaded = self._new_actions = False + self._curr_text = self._search_string = '' + self._search_index = 0 + + def __del__(self): + self.set_visibility(False) + del self._widget + if hasattr(self, '_message_getter'): + del self._message_getter + + # ----------------------------------------------------------------------------------------------------------------- + # History support + # ----------------------------------------------------------------------------------------------------------------- + + def load_corr(self, first_time=True): + """ + :param first_time: friend became active, load first part of messages + """ + try: + if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')): + return + if self._message_getter is None: + return + data = list(self._message_getter.get(PAGE_SIZE)) + if data is not None and len(data): + data.reverse() + else: + return + data = list(map(lambda p: self._get_text_message(p), data)) + self._corr = data + self._corr + except: + pass + finally: + self._history_loaded = True + + def load_all_corr(self): + """ + Get all chat history from db for current friend + """ + if self._message_getter is None: + return + data = list(self._message_getter.get_all()) + if data is not None and len(data): + data.reverse() + data = list(map(lambda p: self._get_text_message(p), data)) + self._corr = data + self._corr + self._history_loaded = True + + def get_corr_for_saving(self): + """ + Get data to save in db + :return: list of unsaved messages or [] + """ + messages = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) + return messages[-self._unsaved_messages:] if self._unsaved_messages else [] + + def get_corr(self): + return self._corr[:] + + def append_message(self, message): + """ + :param message: text or file transfer message + """ + self._corr.append(message) + if message.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): + self._unsaved_messages += 1 + + def get_last_message_text(self): + messages = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) + and m.author.type != MESSAGE_AUTHOR['FRIEND'], self._corr)) + if messages: + return messages[-1].text + else: + return '' + + def remove_messages_widgets(self): + for message in self._corr: + message.remove_widget() + + def get_message(self, _filter): + return list(filter(lambda m: _filter(m), self._corr))[0] + + @staticmethod + def _get_text_message(params): + (message, author_type, author_name, unix_time, message_type, unique_id) = params + author = MessageAuthor(author_name, author_type) + + return TextMessage(message, author, unix_time, message_type, unique_id) + + # ----------------------------------------------------------------------------------------------------------------- + # Unsent messages + # ----------------------------------------------------------------------------------------------------------------- + + def get_unsent_messages(self): + """ + :return list of unsent messages + """ + messages = filter(lambda m: m.author is not None and m.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) + return list(messages) + + def get_unsent_messages_for_saving(self): + """ + :return list of unsent messages for saving + """ + messages = filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) + and m.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr) + return list(messages) + + def mark_as_sent(self, tox_message_id): + try: + message = list(filter(lambda m: m.author is not None and m.author.type == MESSAGE_AUTHOR['NOT_SENT'] + and m.tox_message_id == tox_message_id, self._corr))[0] + message.mark_as_sent() + except Exception as ex: + util.log('Mark as sent ex: ' + str(ex)) + + # ----------------------------------------------------------------------------------------------------------------- + # Message deletion + # ----------------------------------------------------------------------------------------------------------------- + + def delete_message(self, message_id): + elem = list(filter(lambda m: m.message_id == message_id, self._corr))[0] + tmp = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)) + if elem in tmp[-self._unsaved_messages:] and self._unsaved_messages: + self._unsaved_messages -= 1 + self._corr.remove(elem) + self._message_getter.delete_one() + self._search_index = 0 + + def delete_old_messages(self): + """ + Delete old messages (reduces RAM usage if messages saving is not enabled) + """ + def save_message(m): + if m.type == MESSAGE_TYPE['FILE_TRANSFER'] and (m.state not in ACTIVE_FILE_TRANSFERS): + return True + return m.author is not None and m.author.type == MESSAGE_AUTHOR['NOT_SENT'] + + old = filter(save_message, self._corr[:-SAVE_MESSAGES]) + self._corr = list(old) + self._corr[-SAVE_MESSAGES:] + text_messages = filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr) + self._unsaved_messages = min(self._unsaved_messages, len(list(text_messages))) + self._search_index = 0 + + def clear_corr(self, save_unsent=False): + """ + Clear messages list + """ + if hasattr(self, '_message_getter'): + del self._message_getter + self._search_index = 0 + # don't delete data about active file transfer + if not save_unsent: + self._corr = list(filter(lambda m: m.type == MESSAGE_TYPE['FILE_TRANSFER'] and + m.state in ft.ACTIVE_FILE_TRANSFERS, self._corr)) + self._unsaved_messages = 0 + else: + self._corr = list(filter(lambda m: (m.type == MESSAGE_TYPE['FILE_TRANSFER'] + and m.state in ft.ACTIVE_FILE_TRANSFERS) + or (m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']) + and m.author.type == MESSAGE_AUTHOR['NOT_SENT']), + self._corr)) + self._unsaved_messages = len(self.get_unsent_messages()) + + # ----------------------------------------------------------------------------------------------------------------- + # Chat history search + # ----------------------------------------------------------------------------------------------------------------- + + def search_string(self, search_string): + self._search_string, self._search_index = search_string, 0 + return self.search_prev() + + def search_prev(self): + while True: + l = len(self._corr) + for i in range(self._search_index - 1, -l - 1, -1): + if self._corr[i].type not in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): + continue + message = self._corr[i].text + if re.search(self._search_string, message, re.IGNORECASE) is not None: + self._search_index = i + return i + self._search_index = -l + self.load_corr(False) + if len(self._corr) == l: + return None # not found + + def search_next(self): + if not self._search_index: + return None + for i in range(self._search_index + 1, 0): + if self._corr[i].type not in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): + continue + message = self._corr[i].text + if re.search(self._search_string, message, re.IGNORECASE) is not None: + self._search_index = i + return i + return None # not found + + # ----------------------------------------------------------------------------------------------------------------- + # Current text - text from message area + # ----------------------------------------------------------------------------------------------------------------- + + def get_curr_text(self): + return self._curr_text + + def set_curr_text(self, value): + self._curr_text = value + + curr_text = property(get_curr_text, set_curr_text) + + # ----------------------------------------------------------------------------------------------------------------- + # Alias support + # ----------------------------------------------------------------------------------------------------------------- + + def set_name(self, value): + """ + Set new name or ignore if alias exists + :param value: new name + """ + if not self._alias: + super().set_name(value) + + def set_alias(self, alias): + self._alias = bool(alias) + + def has_alias(self): + return self._alias + + # ----------------------------------------------------------------------------------------------------------------- + # Visibility in friends' list + # ----------------------------------------------------------------------------------------------------------------- + + def get_visibility(self): + return self._visible + + def set_visibility(self, value): + self._visible = value + + visibility = property(get_visibility, set_visibility) + + # ----------------------------------------------------------------------------------------------------------------- + # Unread messages and other actions from friend + # ----------------------------------------------------------------------------------------------------------------- + + def get_actions(self): + return self._new_actions + + def set_actions(self, value): + self._new_actions = value + self._widget.connection_status.update(self.status, value) + + actions = property(get_actions, set_actions) # unread messages, incoming files, av calls + + def get_messages(self): + return self._new_messages + + def inc_messages(self): + self._new_messages += 1 + self._new_actions = True + self._widget.connection_status.update(self.status, True) + self._widget.messages.update(self._new_messages) + + def reset_messages(self): + self._new_actions = False + self._new_messages = 0 + self._widget.messages.update(self._new_messages) + self._widget.connection_status.update(self.status, False) + + messages = property(get_messages) + + # ----------------------------------------------------------------------------------------------------------------- + # Friend's or group's number (can be used in toxcore) + # ----------------------------------------------------------------------------------------------------------------- + + def get_number(self): + return self._number + + def set_number(self, value): + self._number = value + + number = property(get_number, set_number) + + # ----------------------------------------------------------------------------------------------------------------- + # Typing notifications + # ----------------------------------------------------------------------------------------------------------------- + + def get_typing_notification_handler(self): + return common.BaseTypingNotificationHandler.DEFAULT_HANDLER + + typing_notification_handler = property(get_typing_notification_handler) + + # ----------------------------------------------------------------------------------------------------------------- + # Context menu support + # ----------------------------------------------------------------------------------------------------------------- + + def get_context_menu_generator(self): + return BaseContactMenuGenerator(self) + + # ----------------------------------------------------------------------------------------------------------------- + # Filtration support + # ----------------------------------------------------------------------------------------------------------------- + + def set_widget(self, widget): + self._widget = widget + self.init_widget() diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py new file mode 100644 index 0000000..8178d31 --- /dev/null +++ b/toxygen/contacts/contact_menu.py @@ -0,0 +1,229 @@ +from PyQt5 import QtWidgets +import utils.ui as util_ui + + +# ----------------------------------------------------------------------------------------------------------------- +# Builder +# ----------------------------------------------------------------------------------------------------------------- + +def _create_menu(menu_name, parent): + menu_name = menu_name or '' + + return QtWidgets.QMenu(menu_name) if parent is None else parent.addMenu(menu_name) + + +class ContactMenuBuilder: + + def __init__(self): + self._actions = {} + self._submenus = {} + self._name = None + self._index = 0 + + def with_name(self, name): + self._name = name + + return self + + def with_action(self, text, handler): + self._add_action(text, handler) + + return self + + def with_optional_action(self, text, handler, show_action): + if show_action: + self._add_action(text, handler) + + return self + + def with_actions(self, actions): + for action in actions: + (text, handler) = action + self._add_action(text, handler) + + return self + + def with_submenu(self, submenu_builder): + self._add_submenu(submenu_builder) + + return self + + def with_optional_submenu(self, submenu_builder): + if submenu_builder is not None: + self._add_submenu(submenu_builder) + + return self + + def build(self, parent=None): + menu = _create_menu(self._name, parent) + + for i in range(self._index): + if i in self._actions: + text, handler = self._actions[i] + action = menu.addAction(text) + action.triggered.connect(handler) + else: + submenu_builder = self._submenus[i] + submenu = submenu_builder.build(menu) + menu.addMenu(submenu) + + return menu + + def _add_submenu(self, submenu): + self._submenus[self._index] = submenu + self._index += 1 + + def _add_action(self, text, handler): + self._actions[self._index] = (text, handler) + self._index += 1 + +# ----------------------------------------------------------------------------------------------------------------- +# Generators +# ----------------------------------------------------------------------------------------------------------------- + + +class BaseContactMenuGenerator: + + def __init__(self, contact): + self._contact = contact + + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader): + return ContactMenuBuilder().build() + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _generate_copy_menu_builder(self, main_screen): + copy_menu_builder = ContactMenuBuilder() + (copy_menu_builder + .with_name(util_ui.tr('Copy')) + .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name)) + .with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.status_message)) + .with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id)) + ) + + return copy_menu_builder + + def _generate_history_menu_builder(self, history_loader, main_screen): + history_menu_builder = ContactMenuBuilder() + (history_menu_builder + .with_name(util_ui.tr('Chat history')) + .with_action(util_ui.tr('Clear history'), lambda: history_loader.clear_history(self._contact) + or main_screen.messages.clear()) + .with_action(util_ui.tr('Export as text'), lambda: history_loader.export_history(self._contact)) + .with_action(util_ui.tr('Export as HTML'), lambda: history_loader.export_history(self._contact, False)) + ) + + return history_menu_builder + + +class FriendMenuGenerator(BaseContactMenuGenerator): + + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader): + history_menu_builder = self._generate_history_menu_builder(history_loader, main_screen) + copy_menu_builder = self._generate_copy_menu_builder(main_screen) + plugins_menu_builder = self._generate_plugins_menu_builder(plugin_loader, number) + groups_menu_builder = self._generate_groups_menu(contacts_manager, groups_service) + + allowed = self._contact.tox_id in settings['auto_accept_from_friends'] + auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') + + builder = ContactMenuBuilder() + menu = (builder + .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_submenu(history_menu_builder) + .with_submenu(copy_menu_builder) + .with_action(auto, lambda: main_screen.auto_accept(number, not allowed)) + .with_action(util_ui.tr('Remove friend'), lambda: main_screen.remove_friend(number)) + .with_action(util_ui.tr('Block friend'), lambda: main_screen.block_friend(number)) + .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) + .with_optional_submenu(plugins_menu_builder) + .with_optional_submenu(groups_menu_builder) + ).build() + + return menu + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def _generate_plugins_menu_builder(plugin_loader, number): + if plugin_loader is None: + return None + plugins_actions = plugin_loader.get_menu(number) + if not len(plugins_actions): + return None + plugins_menu_builder = ContactMenuBuilder() + (plugins_menu_builder + .with_name(util_ui.tr('Plugins')) + .with_actions(plugins_actions) + ) + + return plugins_menu_builder + + def _generate_groups_menu(self, contacts_manager, groups_service): + chats = contacts_manager.get_group_chats() + if not len(chats) or self._contact.status is None: + return None + groups_menu_builder = ContactMenuBuilder() + (groups_menu_builder + .with_name(util_ui.tr('Invite to group')) + .with_actions([(g.name, lambda: groups_service.invite_friend(self._contact.number, g.number)) for g in chats]) + ) + + return groups_menu_builder + + +class GroupMenuGenerator(BaseContactMenuGenerator): + + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader): + copy_menu_builder = self._generate_copy_menu_builder(main_screen) + history_menu_builder = self._generate_history_menu_builder(history_loader, main_screen) + + builder = ContactMenuBuilder() + menu = (builder + .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_submenu(copy_menu_builder) + .with_submenu(history_menu_builder) + .with_optional_action(util_ui.tr('Manage group'), + lambda: groups_service.show_group_management_screen(self._contact), + self._contact.is_self_founder()) + .with_optional_action(util_ui.tr('Group settings'), + lambda: groups_service.show_group_settings_screen(self._contact), + not self._contact.is_self_founder()) + .with_optional_action(util_ui.tr('Set topic'), + lambda: groups_service.set_group_topic(self._contact), + self._contact.is_self_moderator_or_founder()) + .with_action(util_ui.tr('Bans list'), + lambda: groups_service.show_bans_list(self._contact)) + .with_action(util_ui.tr('Reconnect to group'), + lambda: groups_service.reconnect_to_group(self._contact.number)) + .with_optional_action(util_ui.tr('Disconnect from group'), + lambda: groups_service.disconnect_from_group(self._contact.number), + self._contact.status is not None) + .with_action(util_ui.tr('Leave group'), lambda: groups_service.leave_group(self._contact.number)) + .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) + ).build() + + return menu + + +class GroupPeerMenuGenerator(BaseContactMenuGenerator): + + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader): + copy_menu_builder = self._generate_copy_menu_builder(main_screen) + history_menu_builder = self._generate_history_menu_builder(history_loader, main_screen) + + builder = ContactMenuBuilder() + menu = (builder + .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_submenu(copy_menu_builder) + .with_submenu(history_menu_builder) + .with_action(util_ui.tr('Quit chat'), + lambda: contacts_manager.remove_group_peer(self._contact)) + .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) + ).build() + + return menu diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py new file mode 100644 index 0000000..76e8e79 --- /dev/null +++ b/toxygen/contacts/contact_provider.py @@ -0,0 +1,107 @@ +import common.tox_save as tox_save + + +class ContactProvider(tox_save.ToxSave): + + def __init__(self, tox, friend_factory, group_factory, group_peer_factory): + super().__init__(tox) + self._friend_factory = friend_factory + self._group_factory = group_factory + self._group_peer_factory = group_peer_factory + self._cache = {} # key - contact's public key, value - contact instance + + # ----------------------------------------------------------------------------------------------------------------- + # Friends + # ----------------------------------------------------------------------------------------------------------------- + + def get_friend_by_number(self, friend_number): + public_key = self._tox.friend_get_public_key(friend_number) + + return self.get_friend_by_public_key(public_key) + + def get_friend_by_public_key(self, public_key): + friend = self._get_contact_from_cache(public_key) + if friend is not None: + return friend + friend = self._friend_factory.create_friend_by_public_key(public_key) + self._add_to_cache(public_key, friend) + + return friend + + def get_all_friends(self): + friend_numbers = self._tox.self_get_friend_list() + friends = map(lambda n: self.get_friend_by_number(n), friend_numbers) + + return list(friends) + + # ----------------------------------------------------------------------------------------------------------------- + # Groups + # ----------------------------------------------------------------------------------------------------------------- + + def get_all_groups(self): + group_numbers = range(self._tox.group_get_number_groups()) + groups = map(lambda n: self.get_group_by_number(n), group_numbers) + + return list(groups) + + def get_group_by_number(self, group_number): + public_key = self._tox.group_get_chat_id(group_number) + + return self.get_group_by_public_key(public_key) + + def get_group_by_public_key(self, public_key): + group = self._get_contact_from_cache(public_key) + if group is not None: + return group + group = self._group_factory.create_group_by_public_key(public_key) + self._add_to_cache(public_key, group) + + return group + + # ----------------------------------------------------------------------------------------------------------------- + # Group peers + # ----------------------------------------------------------------------------------------------------------------- + + def get_all_group_peers(self): + return list() + + def get_group_peer_by_id(self, group, peer_id): + peer = group.get_peer_by_id(peer_id) + + return self._get_group_peer(group, peer) + + def get_group_peer_by_public_key(self, group, public_key): + peer = group.get_peer_by_public_key(public_key) + + return self._get_group_peer(group, peer) + + # ----------------------------------------------------------------------------------------------------------------- + # All contacts + # ----------------------------------------------------------------------------------------------------------------- + + def get_all(self): + return self.get_all_friends() + self.get_all_groups() + self.get_all_group_peers() + + # ----------------------------------------------------------------------------------------------------------------- + # Caching + # ----------------------------------------------------------------------------------------------------------------- + + def clear_cache(self): + self._cache.clear() + + def remove_contact_from_cache(self, contact_public_key): + if contact_public_key in self._cache: + del self._cache[contact_public_key] + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _get_contact_from_cache(self, public_key): + return self._cache[public_key] if public_key in self._cache else None + + def _add_to_cache(self, public_key, contact): + self._cache[public_key] = contact + + def _get_group_peer(self, group, peer): + return self._group_peer_factory.create_group_peer(group, peer) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py new file mode 100644 index 0000000..87a61ff --- /dev/null +++ b/toxygen/contacts/contacts_manager.py @@ -0,0 +1,575 @@ +from contacts.friend import Friend +from contacts.group_chat import GroupChat +from messenger.messages import * +from common.tox_save import ToxSave +from contacts.group_peer_contact import GroupPeerContact + + +class ContactsManager(ToxSave): + """ + Represents contacts list. + """ + + def __init__(self, tox, settings, screen, profile_manager, contact_provider, history, tox_dns, + messages_items_factory): + super().__init__(tox) + self._settings = settings + self._screen = screen + self._profile_manager = profile_manager + self._contact_provider = contact_provider + self._tox_dns = tox_dns + self._messages_items_factory = messages_items_factory + self._messages = screen.messages + self._contacts, self._active_contact = [], -1 + self._active_contact_changed = Event() + self._sorting = settings['sorting'] + self._filter_string = '' + screen.contacts_filter.setCurrentIndex(int(self._sorting)) + self._history = history + self._load_contacts() + + def get_contact(self, num): + if num < 0 or num >= len(self._contacts): + return None + return self._contacts[num] + + def get_curr_contact(self): + return self._contacts[self._active_contact] if self._active_contact + 1 else None + + def save_profile(self): + data = self._tox.get_savedata() + self._profile_manager.save_profile(data) + + def is_friend_active(self, friend_number): + if not self.is_active_a_friend(): + return False + + return self.get_curr_contact().number == friend_number + + def is_group_active(self, group_number): + if self.is_active_a_friend(): + return False + + return self.get_curr_contact().number == group_number + + def is_contact_active(self, contact): + return self._contacts[self._active_contact].tox_id == contact.tox_id + + # ----------------------------------------------------------------------------------------------------------------- + # Reconnection support + # ----------------------------------------------------------------------------------------------------------------- + + def reset_contacts_statuses(self): + for contact in self._contacts: + contact.status = None + + # ----------------------------------------------------------------------------------------------------------------- + # Work with active friend + # ----------------------------------------------------------------------------------------------------------------- + + def get_active(self): + return self._active_contact + + def set_active(self, value): + """ + Change current active friend or update info + :param value: number of new active friend in friend's list + """ + if value is None and self._active_contact == -1: # nothing to update + return + if value == -1: # all friends were deleted + self._screen.account_name.setText('') + self._screen.account_status.setText('') + self._screen.account_status.setToolTip('') + self._active_contact = -1 + self._screen.account_avatar.setHidden(True) + self._messages.clear() + self._screen.messageEdit.clear() + return + try: + self._screen.typing.setVisible(False) + current_contact = self.get_curr_contact() + if current_contact is not None: + # TODO: send when needed + current_contact.typing_notification_handler.send(self._tox, False) + current_contact.remove_messages_widgets() # TODO: if required + self._unsubscribe_from_events(current_contact) + + if self._active_contact + 1 and self._active_contact != value: + try: + current_contact.curr_text = self._screen.messageEdit.toPlainText() + except: + pass + contact = self._contacts[value] + self._subscribe_to_events(contact) + contact.remove_invalid_unsent_files() + if self._active_contact != value: + self._screen.messageEdit.setPlainText(contact.curr_text) + self._active_contact = value + contact.reset_messages() + if not self._settings['save_history']: + contact.delete_old_messages() + self._messages.clear() + contact.load_corr() + corr = contact.get_corr()[-PAGE_SIZE:] + for message in corr: + if message.type == MESSAGE_TYPE['FILE_TRANSFER']: + self._messages_items_factory.create_file_transfer_item(message) + elif message.type == MESSAGE_TYPE['INLINE']: + self._messages_items_factory.create_inline_item(message) + else: + self._messages_items_factory.create_message_item(message) + self._messages.scrollToBottom() + # if value in self._call: + # self._screen.active_call() + # elif value in self._incoming_calls: + # self._screen.incoming_call() + # else: + # self._screen.call_finished() + self._set_current_contact_data(contact) + self._active_contact_changed(contact) + except Exception as ex: # no friend found. ignore + util.log('Friend value: ' + str(value)) + util.log('Error in set active: ' + str(ex)) + raise + + active_contact = property(get_active, set_active) + + def get_active_contact_changed(self): + return self._active_contact_changed + + active_contact_changed = property(get_active_contact_changed) + + def update(self): + if self._active_contact + 1: + self.set_active(self._active_contact) + + def is_active_a_friend(self): + return type(self.get_curr_contact()) is Friend + + def is_active_a_group(self): + return type(self.get_curr_contact()) is GroupChat + + def is_active_a_group_chat_peer(self): + return type(self.get_curr_contact()) is GroupPeerContact + + # ----------------------------------------------------------------------------------------------------------------- + # Filtration + # ----------------------------------------------------------------------------------------------------------------- + + def filtration_and_sorting(self, sorting=0, filter_str=''): + """ + Filtration of friends list + :param sorting: 0 - no sorting, 1 - online only, 2 - online first, 3 - by name, + 4 - online and by name, 5 - online first and by name + :param filter_str: show contacts which name contains this substring + """ + filter_str = filter_str.lower() + current_contact = self.get_curr_contact() + + if sorting > 5 or sorting < 0: + sorting = 0 + + if sorting in (1, 2, 4, 5): # online first + self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True) + sort_by_name = sorting in (4, 5) + # save results of previous sorting + online_friends = filter(lambda x: x.status is not None, self._contacts) + online_friends_count = len(list(online_friends)) + part1 = self._contacts[:online_friends_count] + part2 = self._contacts[online_friends_count:] + key_lambda = lambda x: x.name.lower() if sort_by_name else x.number + part1 = sorted(part1, key=key_lambda) + part2 = sorted(part2, key=key_lambda) + self._contacts = part1 + part2 + elif sorting == 0: + contacts = sorted(self._contacts, key=lambda c: c.number) + friends = filter(lambda c: type(c) is Friend, contacts) + groups = filter(lambda c: type(c) is GroupChat, contacts) + group_peers = filter(lambda c: type(c) is GroupPeerContact, contacts) + self._contacts = list(friends) + list(groups) + list(group_peers) + else: + self._contacts = sorted(self._contacts, key=lambda x: x.name.lower()) + + # change item widgets + for index, contact in enumerate(self._contacts): + list_item = self._screen.friends_list.item(index) + item_widget = self._screen.friends_list.itemWidget(list_item) + contact.set_widget(item_widget) + + for index, friend in enumerate(self._contacts): + filtered_by_name = filter_str in friend.name.lower() + friend.visibility = (friend.status is not None or sorting not in (1, 4)) and filtered_by_name + # show friend even if it's hidden when there any unread messages/actions + friend.visibility = friend.visibility or friend.messages or friend.actions + item = self._screen.friends_list.item(index) + item_widget = self._screen.friends_list.itemWidget(item) + item.setSizeHint(QtCore.QSize(250, item_widget.height() if friend.visibility else 0)) + + # save soring results + self._sorting, self._filter_string = sorting, filter_str + self._settings['sorting'] = self._sorting + self._settings.save() + + # update active contact + if current_contact is not None: + index = self._contacts.index(current_contact) + self.set_active(index) + + def update_filtration(self): + """ + Update list of contacts when 1 of friends change connection status + """ + self.filtration_and_sorting(self._sorting, self._filter_string) + + # ----------------------------------------------------------------------------------------------------------------- + # Contact getters + # ----------------------------------------------------------------------------------------------------------------- + + def get_friend_by_number(self, number): + return list(filter(lambda c: c.number == number and type(c) is Friend, self._contacts))[0] + + def get_group_by_number(self, number): + return list(filter(lambda c: c.number == number and type(c) is GroupChat, self._contacts))[0] + + def get_or_create_group_peer_contact(self, group_number, peer_id): + group = self.get_group_by_number(group_number) + peer = group.get_peer_by_id(peer_id) + if not self.check_if_contact_exists(peer.public_key): + self.add_group_peer(group, peer) + + return self.get_contact_by_tox_id(peer.public_key) + + def check_if_contact_exists(self, tox_id): + return any(filter(lambda c: c.tox_id == tox_id, self._contacts)) + + def get_contact_by_tox_id(self, tox_id): + return list(filter(lambda c: c.tox_id == tox_id, self._contacts))[0] + + def get_active_number(self): + return self.get_curr_contact().number if self._active_contact + 1 else -1 + + def get_active_name(self): + return self.get_curr_contact().name if self._active_contact + 1 else '' + + def is_active_online(self): + return self._active_contact + 1 and self.get_curr_contact().status is not None + + # ----------------------------------------------------------------------------------------------------------------- + # Work with friends (remove, block, set alias, get public key) + # ----------------------------------------------------------------------------------------------------------------- + + def set_alias(self, num): + """ + Set new alias for friend + """ + friend = self._contacts[num] + name = friend.name + text = util_ui.tr("Enter new alias for friend {} or leave empty to use friend's name:").format(name) + title = util_ui.tr('Set alias') + text, ok = util_ui.text_dialog(text, title, name) + if not ok: + return + aliases = self._settings['friends_aliases'] + if text: + friend.name = text + try: + index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) + aliases[index] = (friend.tox_id, text) + except: + aliases.append((friend.tox_id, text)) + friend.set_alias(text) + else: # use default name + friend.name = self._tox.friend_get_name(friend.number) + friend.set_alias('') + try: + index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) + del aliases[index] + except: + pass + self._settings.save() + + def friend_public_key(self, num): + return self._contacts[num].tox_id + + def delete_friend(self, num): + """ + Removes friend from contact list + :param num: number of friend in list + """ + friend = self._contacts[num] + self._cleanup_contact_data(friend) + self._tox.friend_delete(friend.number) + self._delete_contact(num) + + def add_friend(self, tox_id): + """ + Adds friend to list + """ + self._tox.friend_add_norequest(tox_id) + self._add_friend(tox_id) + self.update_filtration() + + def block_user(self, tox_id): + """ + Block user with specified tox id (or public key) - delete from friends list and ignore friend requests + """ + tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] + if tox_id == self._tox.self_get_address[:TOX_PUBLIC_KEY_SIZE * 2]: + return + if tox_id not in self._settings['blocked']: + self._settings['blocked'].append(tox_id) + self._settings.save() + try: + num = self._tox.friend_by_public_key(tox_id) + self.delete_friend(num) + self.save_profile() + except: # not in friend list + pass + + def unblock_user(self, tox_id, add_to_friend_list): + """ + Unblock user + :param tox_id: tox id of contact + :param add_to_friend_list: add this contact to friend list or not + """ + self._settings['blocked'].remove(tox_id) + self._settings.save() + if add_to_friend_list: + self.add_friend(tox_id) + self.save_profile() + + # ----------------------------------------------------------------------------------------------------------------- + # Groups support + # ----------------------------------------------------------------------------------------------------------------- + + def get_group_chats(self): + return list(filter(lambda c: type(c) is GroupChat, self._contacts)) + + def add_group(self, group_number): + group = self._contact_provider.get_group_by_number(group_number) + index = len(self._contacts) + self._contacts.append(group) + group.reset_avatar(self._settings['identicons']) + self._save_profile() + self.set_active(index) + self.update_filtration() + + def delete_group(self, group_number): + group = self.get_group_by_number(group_number) + self._cleanup_contact_data(group) + num = self._contacts.index(group) + self._delete_contact(num) + + # ----------------------------------------------------------------------------------------------------------------- + # Groups private messaging + # ----------------------------------------------------------------------------------------------------------------- + + def add_group_peer(self, group, peer): + contact = self._contact_provider.get_group_peer_by_id(group, peer.id) + if self.check_if_contact_exists(contact.tox_id): + return + self._contacts.append(contact) + contact.reset_avatar(self._settings['identicons']) + self._save_profile() + + def remove_group_peer_by_id(self, group, peer_id): + peer = group.get_peer_by_id(peer_id) + if not self.check_if_contact_exists(peer.public_key): + return + contact = self.get_contact_by_tox_id(peer.public_key) + self.remove_group_peer(contact) + + def remove_group_peer(self, group_peer_contact): + contact = self.get_contact_by_tox_id(group_peer_contact.tox_id) + self._cleanup_contact_data(contact) + num = self._contacts.index(contact) + self._delete_contact(num) + + def get_gc_peer_name(self, name): + group = self.get_curr_contact() + + names = sorted(group.get_peers_names()) + if name in names: # return next nick + index = names.index(name) + index = (index + 1) % len(names) + + return names[index] + + suggested_names = list(filter(lambda x: x.startswith(name), names)) + if not len(suggested_names): + return '\t' + + return suggested_names[0] + + # ----------------------------------------------------------------------------------------------------------------- + # Friend requests + # ----------------------------------------------------------------------------------------------------------------- + + def send_friend_request(self, tox_id, message): + """ + Function tries to send request to contact with specified id + :param tox_id: id of new contact or tox dns 4 value + :param message: additional message + :return: True on success else error string + """ + try: + message = message or 'Hello! Add me to your contact list please' + if '@' in tox_id: # value like groupbot@toxme.io + tox_id = self._tox_dns.lookup(tox_id) + if tox_id is None: + raise Exception('TOX DNS lookup failed') + if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key + self.add_friend(tox_id) + title = util_ui.tr('Friend added') + text = util_ui.tr('Friend added without sending friend request') + util_ui.message_box(text, title) + else: + self._tox.friend_add(tox_id, message.encode('utf-8')) + tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] + self._add_friend(tox_id) + self.update_filtration() + self.save_profile() + return True + except Exception as ex: # wrong data + util.log('Friend request failed with ' + str(ex)) + return str(ex) + + def process_friend_request(self, tox_id, message): + """ + Accept or ignore friend request + :param tox_id: tox id of contact + :param message: message + """ + if tox_id in self._settings['blocked']: + return + try: + text = util_ui.tr('User {} wants to add you to contact list. Message:\n{}') + reply = util_ui.question(text.format(tox_id, message), util_ui.tr('Friend request')) + if reply: # accepted + self.add_friend(tox_id) + data = self._tox.get_savedata() + self._profile_manager.save_profile(data) + except Exception as ex: # something is wrong + util.log('Accept friend request failed! ' + str(ex)) + + def can_send_typing_notification(self): + return self._settings['typing_notifications'] and not self.is_active_a_group_chat_peer() + + # ----------------------------------------------------------------------------------------------------------------- + # Contacts numbers update + # ----------------------------------------------------------------------------------------------------------------- + + def update_friends_numbers(self): + for friend in self._contact_provider.get_all_friends(): + friend.number = self._tox.friend_by_public_key(friend.tox_id) + self.update_filtration() + + def update_groups_numbers(self): + groups = self._contact_provider.get_all_groups() + for i in range(len(groups)): + chat_id = self._tox.group_get_chat_id(i) + group = self.get_contact_by_tox_id(chat_id) + group.number = i + self.update_filtration() + + def update_groups_lists(self): + groups = self._contact_provider.get_all_groups() + for group in groups: + group.remove_all_peers_except_self() + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _load_contacts(self): + self._load_friends() + self._load_groups() + if len(self._contacts): + self.set_active(0) + for contact in filter(lambda c: not c.has_avatar(), self._contacts): + contact.reset_avatar(self._settings['identicons']) + self.update_filtration() + + def _load_friends(self): + self._contacts.extend(self._contact_provider.get_all_friends()) + + def _load_groups(self): + self._contacts.extend(self._contact_provider.get_all_groups()) + + # ----------------------------------------------------------------------------------------------------------------- + # Current contact subscriptions + # ----------------------------------------------------------------------------------------------------------------- + + def _subscribe_to_events(self, contact): + contact.name_changed_event.add_callback(self._current_contact_name_changed) + contact.status_changed_event.add_callback(self._current_contact_status_changed) + contact.status_message_changed_event.add_callback(self._current_contact_status_message_changed) + contact.avatar_changed_event.add_callback(self._current_contact_avatar_changed) + + def _unsubscribe_from_events(self, contact): + contact.name_changed_event.remove_callback(self._current_contact_name_changed) + contact.status_changed_event.remove_callback(self._current_contact_status_changed) + contact.status_message_changed_event.remove_callback(self._current_contact_status_message_changed) + contact.avatar_changed_event.remove_callback(self._current_contact_avatar_changed) + + def _current_contact_name_changed(self, name): + self._screen.account_name.setText(name) + + def _current_contact_status_changed(self, status): + pass + + def _current_contact_status_message_changed(self, status_message): + self._screen.account_status.setText(status_message) + + def _current_contact_avatar_changed(self, avatar_path): + self._set_current_contact_avatar(avatar_path) + + def _set_current_contact_data(self, contact): + self._screen.account_name.setText(contact.name) + self._screen.account_status.setText(contact.status_message) + self._set_current_contact_avatar(contact.get_avatar_path()) + + def _set_current_contact_avatar(self, avatar_path): + width = self._screen.account_avatar.width() + pixmap = QtGui.QPixmap(avatar_path) + self._screen.account_avatar.setPixmap(pixmap.scaled(width, width, + QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) + + def _add_friend(self, tox_id): + self._history.add_friend_to_db(tox_id) + friend = self._contact_provider.get_friend_by_public_key(tox_id) + index = len(self._contacts) + self._contacts.append(friend) + if not friend.has_avatar(): + friend.reset_avatar(self._settings['identicons']) + self._save_profile() + self.set_active(index) + + def _save_profile(self): + data = self._tox.get_savedata() + self._profile_manager.save_profile(data) + + def _cleanup_contact_data(self, contact): + try: + index = list(map(lambda x: x[0], self._settings['friends_aliases'])).index(contact.tox_id) + del self._settings['friends_aliases'][index] + except: + pass + if contact.tox_id in self._settings['notes']: + del self._settings['notes'][contact.tox_id] + self._settings.save() + self._history.delete_history(contact) + if contact.has_avatar(): + avatar_path = contact.get_contact_avatar_path() + remove(avatar_path) + + def _delete_contact(self, num): + self.set_active(-1 if len(self._contacts) == 1 else 0) + + self._contact_provider.remove_contact_from_cache(self._contacts[num].tox_id) + del self._contacts[num] + self._screen.friends_list.takeItem(num) + self._save_profile() + + self.update_filtration() diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py new file mode 100644 index 0000000..5c8eabb --- /dev/null +++ b/toxygen/contacts/friend.py @@ -0,0 +1,74 @@ +from contacts import contact, common +from messenger.messages import * +import os +from contacts.contact_menu import * + + +class Friend(contact.Contact): + """ + Friend in list of friends. + """ + + def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id): + super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) + self._receipts = 0 + self._typing_notification_handler = common.FriendTypingNotificationHandler(number) + + # ----------------------------------------------------------------------------------------------------------------- + # File transfers support + # ----------------------------------------------------------------------------------------------------------------- + + def insert_inline(self, before_message_id, inline): + """ + Update status of active transfer and load inline if needed + """ + try: + tr = list(filter(lambda m: m.message_id == before_message_id, self._corr))[0] + i = self._corr.index(tr) + if inline: # inline was loaded + self._corr.insert(i, inline) + return i - len(self._corr) + except: + pass + + def get_unsent_files(self): + messages = filter(lambda m: type(m) is UnsentFileMessage, self._corr) + return list(messages) + + def clear_unsent_files(self): + self._corr = list(filter(lambda m: type(m) is not UnsentFileMessage, self._corr)) + + def remove_invalid_unsent_files(self): + def is_valid(message): + if type(message) is not UnsentFileMessage: + return True + if message.data is not None: + return True + return os.path.exists(message.path) + + self._corr = list(filter(is_valid, self._corr)) + + def delete_one_unsent_file(self, message_id): + self._corr = list(filter(lambda m: not (type(m) is UnsentFileMessage and m.message_id == message_id), + self._corr)) + + # ----------------------------------------------------------------------------------------------------------------- + # Full status + # ----------------------------------------------------------------------------------------------------------------- + + def get_full_status(self): + return self._status_message + + # ----------------------------------------------------------------------------------------------------------------- + # Typing notifications + # ----------------------------------------------------------------------------------------------------------------- + + def get_typing_notification_handler(self): + return self._typing_notification_handler + + # ----------------------------------------------------------------------------------------------------------------- + # Context menu support + # ----------------------------------------------------------------------------------------------------------------- + + def get_context_menu_generator(self): + return FriendMenuGenerator(self) diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py new file mode 100644 index 0000000..8ebafd6 --- /dev/null +++ b/toxygen/contacts/friend_factory.py @@ -0,0 +1,44 @@ +from contacts.friend import Friend +from common.tox_save import ToxSave + + +class FriendFactory(ToxSave): + + def __init__(self, profile_manager, settings, tox, db, items_factory): + super().__init__(tox) + self._profile_manager = profile_manager + self._settings = settings + self._db = db + self._items_factory = items_factory + + def create_friend_by_public_key(self, public_key): + friend_number = self._tox.friend_by_public_key(public_key) + + return self.create_friend_by_number(friend_number) + + def create_friend_by_number(self, friend_number): + aliases = self._settings['friends_aliases'] + tox_id = self._tox.friend_get_public_key(friend_number) + try: + alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] + except: + alias = '' + item = self._create_friend_item() + name = alias or self._tox.friend_get_name(friend_number) or tox_id + status_message = self._tox.friend_get_status_message(friend_number) + message_getter = self._db.messages_getter(tox_id) + friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, tox_id) + friend.set_alias(alias) + + return friend + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _create_friend_item(self): + """ + Method-factory + :return: new widget for friend instance + """ + return self._items_factory.create_contact_item() diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py new file mode 100644 index 0000000..19ebc8e --- /dev/null +++ b/toxygen/contacts/group_chat.py @@ -0,0 +1,137 @@ +from contacts import contact +from contacts.contact_menu import GroupMenuGenerator +import utils.util as util +from groups.group_peer import GroupChatPeer +from wrapper import toxcore_enums_and_consts as constants +from common.tox_save import ToxSave +from groups.group_ban import GroupBan + + +class GroupChat(contact.Contact, ToxSave): + + def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id, is_private): + super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) + ToxSave.__init__(self, tox) + + self._is_private = is_private + self._password = str() + self._peers_limit = 512 + self._peers = [] + self._add_self_to_gc() + + def remove_invalid_unsent_files(self): + pass + + def get_context_menu_generator(self): + return GroupMenuGenerator(self) + + # ----------------------------------------------------------------------------------------------------------------- + # Properties + # ----------------------------------------------------------------------------------------------------------------- + + def get_is_private(self): + return self._is_private + + def set_is_private(self, is_private): + self._is_private = is_private + + is_private = property(get_is_private, set_is_private) + + def get_password(self): + return self._password + + def set_password(self, password): + self._password = password + + password = property(get_password, set_password) + + def get_peers_limit(self): + return self._peers_limit + + def set_peers_limit(self, peers_limit): + self._peers_limit = peers_limit + + peers_limit = property(get_peers_limit, set_peers_limit) + + # ----------------------------------------------------------------------------------------------------------------- + # Peers methods + # ----------------------------------------------------------------------------------------------------------------- + + def get_self_peer(self): + return self._peers[0] + + def get_self_name(self): + return self._peers[0].name + + def get_self_role(self): + return self._peers[0].role + + def is_self_moderator_or_founder(self): + return self.get_self_role() <= constants.TOX_GROUP_ROLE['MODERATOR'] + + def is_self_founder(self): + return self.get_self_role() == constants.TOX_GROUP_ROLE['FOUNDER'] + + def add_peer(self, peer_id, is_current_user=False): + peer = GroupChatPeer(peer_id, + self._tox.group_peer_get_name(self._number, peer_id), + self._tox.group_peer_get_status(self._number, peer_id), + self._tox.group_peer_get_role(self._number, peer_id), + self._tox.group_peer_get_public_key(self._number, peer_id), + is_current_user) + self._peers.append(peer) + + def remove_peer(self, peer_id): + if peer_id == self.get_self_peer().id: # we were kicked or banned + self.remove_all_peers_except_self() + else: + peer = self.get_peer_by_id(peer_id) + self._peers.remove(peer) + + def get_peer_by_id(self, peer_id): + peers = list(filter(lambda p: p.id == peer_id, self._peers)) + + return peers[0] + + def get_peer_by_public_key(self, public_key): + peers = list(filter(lambda p: p.public_key == public_key, self._peers)) + + return peers[0] + + def remove_all_peers_except_self(self): + self._peers = self._peers[:1] + + def get_peers_names(self): + peers_names = map(lambda p: p.name, self._peers) + + return list(peers_names) + + def get_peers(self): + return self._peers[:] + + peers = property(get_peers) + + def get_bans(self): + ban_ids = self._tox.group_ban_get_list(self._number) + bans = [] + for ban_id in ban_ids: + ban = GroupBan(ban_id, + self._tox.group_ban_get_target(self._number, ban_id), + self._tox.group_ban_get_time_set(self._number, ban_id)) + bans.append(ban) + + return bans + + bans = property(get_bans) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def _get_default_avatar_path(): + return util.join_path(util.get_images_directory(), 'group.png') + + def _add_self_to_gc(self): + peer_id = self._tox.group_self_get_peer_id(self._number) + self.add_peer(peer_id, True) diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py new file mode 100644 index 0000000..4083438 --- /dev/null +++ b/toxygen/contacts/group_factory.py @@ -0,0 +1,53 @@ +from contacts.group_chat import GroupChat +from common.tox_save import ToxSave +import wrapper.toxcore_enums_and_consts as constants + + +class GroupFactory(ToxSave): + + def __init__(self, profile_manager, settings, tox, db, items_factory): + super().__init__(tox) + self._profile_manager = profile_manager + self._settings = settings + self._db = db + self._items_factory = items_factory + + def create_group_by_public_key(self, public_key): + group_number = self._get_group_number_by_chat_id(public_key) + + return self.create_group_by_number(group_number) + + def create_group_by_number(self, group_number): + aliases = self._settings['friends_aliases'] + tox_id = self._tox.group_get_chat_id(group_number) + try: + alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] + except: + alias = '' + item = self._create_group_item() + name = alias or self._tox.group_get_name(group_number) or tox_id + status_message = self._tox.group_get_topic(group_number) + message_getter = self._db.messages_getter(tox_id) + is_private = self._tox.group_get_privacy_state(group_number) == constants.TOX_GROUP_PRIVACY_STATE['PRIVATE'] + group = GroupChat(self._tox, self._profile_manager, message_getter, group_number, name, status_message, + item, tox_id, is_private) + group.set_alias(alias) + + return group + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _create_group_item(self): + """ + Method-factory + :return: new widget for group instance + """ + return self._items_factory.create_contact_item() + + def _get_group_number_by_chat_id(self, chat_id): + for i in range(self._tox.group_get_number_groups()): + if self._tox.group_get_chat_id(i) == chat_id: + return i + return -1 diff --git a/toxygen/contacts/group_peer_contact.py b/toxygen/contacts/group_peer_contact.py new file mode 100644 index 0000000..8854198 --- /dev/null +++ b/toxygen/contacts/group_peer_contact.py @@ -0,0 +1,20 @@ +import contacts.contact +from contacts.contact_menu import GroupPeerMenuGenerator + + +class GroupPeerContact(contacts.contact.Contact): + + def __init__(self, profile_manager, message_getter, peer_number, name, widget, tox_id, group_pk): + super().__init__(profile_manager, message_getter, peer_number, name, str(), widget, tox_id) + self._group_pk = group_pk + + def get_group_pk(self): + return self._group_pk + + group_pk = property(get_group_pk) + + def remove_invalid_unsent_files(self): + pass + + def get_context_menu_generator(self): + return GroupPeerMenuGenerator(self) diff --git a/toxygen/contacts/group_peer_factory.py b/toxygen/contacts/group_peer_factory.py new file mode 100644 index 0000000..38b3a20 --- /dev/null +++ b/toxygen/contacts/group_peer_factory.py @@ -0,0 +1,23 @@ +from common.tox_save import ToxSave +from contacts.group_peer_contact import GroupPeerContact + + +class GroupPeerFactory(ToxSave): + + def __init__(self, tox, profile_manager, db, items_factory): + super().__init__(tox) + self._profile_manager = profile_manager + self._db = db + self._items_factory = items_factory + + def create_group_peer(self, group, peer): + item = self._create_group_peer_item() + message_getter = self._db.messages_getter(peer.public_key) + group_peer_contact = GroupPeerContact(self._profile_manager, message_getter, peer.id, peer.name, + item, peer.public_key, group.tox_id) + group_peer_contact.status = peer.status + + return group_peer_contact + + def _create_group_peer_item(self): + return self._items_factory.create_contact_item() diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py new file mode 100644 index 0000000..81220af --- /dev/null +++ b/toxygen/contacts/profile.py @@ -0,0 +1,87 @@ +from contacts import basecontact +import random +import threading +import common.tox_save as tox_save +from middleware.threads import invoke_in_main_thread + + +class Profile(basecontact.BaseContact, tox_save.ToxSave): + """ + Profile of current toxygen user. + """ + def __init__(self, profile_manager, tox, screen, contacts_provider, reset_action): + """ + :param tox: tox instance + :param screen: ref to main screen + """ + basecontact.BaseContact.__init__(self, + profile_manager, + tox.self_get_name(), + tox.self_get_status_message(), + screen, + tox.self_get_address()) + tox_save.ToxSave.__init__(self, tox) + self._screen = screen + self._messages = screen.messages + self._contacts_provider = contacts_provider + self._reset_action = reset_action + self._waiting_for_reconnection = False + self._timer = None + + # ----------------------------------------------------------------------------------------------------------------- + # Edit current user's data + # ----------------------------------------------------------------------------------------------------------------- + + def change_status(self): + """ + Changes status of user (online, away, busy) + """ + if self._status is not None: + self.set_status((self._status + 1) % 3) + + def set_status(self, status): + super().set_status(status) + if status is not None: + self._tox.self_set_status(status) + elif not self._waiting_for_reconnection: + self._waiting_for_reconnection = True + self._timer = threading.Timer(50, self._reconnect) + self._timer.start() + + def set_name(self, value): + if self.name == value: + return + super().set_name(value) + self._tox.self_set_name(self._name) + + def set_status_message(self, value): + super().set_status_message(value) + self._tox.self_set_status_message(self._status_message) + + def set_new_nospam(self): + """Sets new nospam part of tox id""" + self._tox.self_set_nospam(random.randint(0, 4294967295)) # no spam - uint32 + self._tox_id = self._tox.self_get_address() + + return self._tox_id + + # ----------------------------------------------------------------------------------------------------------------- + # Reset + # ----------------------------------------------------------------------------------------------------------------- + + def restart(self): + """ + Recreate tox instance + """ + self.status = None + invoke_in_main_thread(self._reset_action) + + def _reconnect(self): + self._waiting_for_reconnection = False + contacts = self._contacts_provider.get_all_friends() + all_friends_offline = all(list(map(lambda x: x.status is None, contacts))) + if self.status is None or (all_friends_offline and len(contacts)): + self._waiting_for_reconnection = True + self.restart() + self._timer = threading.Timer(50, self._reconnect) + self._timer.start() diff --git a/toxygen/file_transfers/__init__.py b/toxygen/file_transfers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py new file mode 100644 index 0000000..0f04e5b --- /dev/null +++ b/toxygen/file_transfers/file_transfers.py @@ -0,0 +1,351 @@ +from wrapper.toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL +from os.path import basename, getsize, exists, dirname +from os import remove, rename, chdir +from time import time +from wrapper.tox import Tox +from common.event import Event +from middleware.threads import invoke_in_main_thread + + +FILE_TRANSFER_STATE = { + 'RUNNING': 0, + 'PAUSED_BY_USER': 1, + 'CANCELLED': 2, + 'FINISHED': 3, + 'PAUSED_BY_FRIEND': 4, + 'INCOMING_NOT_STARTED': 5, + 'OUTGOING_NOT_STARTED': 6, + 'UNSENT': 7 +} + +ACTIVE_FILE_TRANSFERS = (0, 1, 4, 5, 6) + +PAUSED_FILE_TRANSFERS = (1, 4, 5, 6) + +DO_NOT_SHOW_ACCEPT_BUTTON = (2, 3, 4, 6) + +SHOW_PROGRESS_BAR = (0, 1, 4) + + +def is_inline(file_name): + allowed_inlines = ('toxygen_inline.png', 'utox-inline.png', 'sticker.png') + + return file_name in allowed_inlines or file_name.startswith('qTox_Image_') + + +class FileTransfer: + """ + Superclass for file transfers + """ + + def __init__(self, path, tox, friend_number, size, file_number=None): + self._path = path + self._tox = tox + self._friend_number = friend_number + self._state = FILE_TRANSFER_STATE['RUNNING'] + self._file_number = file_number + self._creation_time = None + self._size = float(size) + self._done = 0 + self._state_changed_event = Event() + self._finished_event = Event() + self._file_id = self._file = None + + def set_state_changed_handler(self, handler): + self._state_changed_event += lambda *args: invoke_in_main_thread(handler, *args) + + def set_transfer_finished_handler(self, handler): + self._finished_event += lambda *args: invoke_in_main_thread(handler, *args) + + def get_file_number(self): + return self._file_number + + file_number = property(get_file_number) + + def get_state(self): + return self._state + + def set_state(self, value): + self._state = value + self._signal() + + state = property(get_state, set_state) + + def get_friend_number(self): + return self._friend_number + + friend_number = property(get_friend_number) + + def get_file_id(self): + return self._file_id + + file_id = property(get_file_id) + + def get_path(self): + return self._path + + path = property(get_path) + + def get_size(self): + return self._size + + size = property(get_size) + + def cancel(self): + self.send_control(TOX_FILE_CONTROL['CANCEL']) + if self._file is not None: + self._file.close() + self._signal() + + def cancelled(self): + if self._file is not None: + self._file.close() + self.set_state(FILE_TRANSFER_STATE['CANCELLED']) + + def pause(self, by_friend): + if not by_friend: + self.send_control(TOX_FILE_CONTROL['PAUSE']) + else: + self.set_state(FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']) + + def send_control(self, control): + if self._tox.file_control(self._friend_number, self._file_number, control): + self.set_state(control) + + def get_file_id(self): + return self._tox.file_get_file_id(self._friend_number, self._file_number) + + def _signal(self): + percentage = self._done / self._size if self._size else 0 + if self._creation_time is None or not percentage: + t = -1 + else: + t = ((time() - self._creation_time) / percentage) * (1 - percentage) + self._state_changed_event(self.state, percentage, int(t)) + + def _finished(self): + self._finished_event(self._friend_number, self._file_number) + +# ----------------------------------------------------------------------------------------------------------------- +# Send file +# ----------------------------------------------------------------------------------------------------------------- + + +class SendTransfer(FileTransfer): + + def __init__(self, path, tox, friend_number, kind=TOX_FILE_KIND['DATA'], file_id=None): + if path is not None: + fl = open(path, 'rb') + size = getsize(path) + else: + fl = None + size = 0 + super().__init__(path, tox, friend_number, size) + self._file = fl + self.state = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] + self._file_number = tox.file_send(friend_number, kind, size, file_id, + bytes(basename(path), 'utf-8') if path else b'') + self._file_id = self.get_file_id() + + def send_chunk(self, position, size): + """ + Send chunk + :param position: start position in file + :param size: chunk max size + """ + if self._creation_time is None: + self._creation_time = time() + if size: + self._file.seek(position) + data = self._file.read(size) + self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) + self._done += size + self._signal() + else: + if self._file is not None: + self._file.close() + self.state = FILE_TRANSFER_STATE['FINISHED'] + self._finished() + + +class SendAvatar(SendTransfer): + """ + Send avatar to friend. Doesn't need file transfer item + """ + + def __init__(self, path, tox, friend_number): + if path is None: + avatar_hash = None + else: + with open(path, 'rb') as fl: + avatar_hash = Tox.hash(fl.read()) + super().__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], avatar_hash) + + +class SendFromBuffer(FileTransfer): + """ + Send inline image + """ + + def __init__(self, tox, friend_number, data, file_name): + super().__init__(None, tox, friend_number, len(data)) + self.state = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] + self._data = data + self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], + len(data), None, bytes(file_name, 'utf-8')) + + def get_data(self): + return self._data + + data = property(get_data) + + def send_chunk(self, position, size): + if self._creation_time is None: + self._creation_time = time() + if size: + data = self._data[position:position + size] + self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) + self._done += size + else: + self.state = FILE_TRANSFER_STATE['FINISHED'] + self._finished() + self._signal() + + +class SendFromFileBuffer(SendTransfer): + + def __init__(self, *args): + super().__init__(*args) + + def send_chunk(self, position, size): + super().send_chunk(position, size) + if not size: + chdir(dirname(self._path)) + remove(self._path) + +# ----------------------------------------------------------------------------------------------------------------- +# Receive file +# ----------------------------------------------------------------------------------------------------------------- + + +class ReceiveTransfer(FileTransfer): + + def __init__(self, path, tox, friend_number, size, file_number, position=0): + super().__init__(path, tox, friend_number, size, file_number) + self._file = open(self._path, 'wb') + self._file_size = position + self._file.truncate(position) + self._missed = set() + self._file_id = self.get_file_id() + self._done = position + + def cancel(self): + super().cancel() + remove(self._path) + + def total_size(self): + self._missed.add(self._file_size) + + return min(self._missed) + + def write_chunk(self, position, data): + """ + Incoming chunk + :param position: position in file to save data + :param data: raw data (string) + """ + if self._creation_time is None: + self._creation_time = time() + if data is None: + self._file.close() + self.state = FILE_TRANSFER_STATE['FINISHED'] + self._finished() + else: + data = bytearray(data) + if self._file_size < position: + self._file.seek(0, 2) + self._file.write(b'\0' * (position - self._file_size)) + self._missed.add(self._file_size) + else: + self._missed.discard(position) + self._file.seek(position) + self._file.write(data) + l = len(data) + if position + l > self._file_size: + self._file_size = position + l + self._done += l + self._signal() + + +class ReceiveToBuffer(FileTransfer): + """ + Inline image - save in buffer not in file system + """ + + def __init__(self, tox, friend_number, size, file_number): + super().__init__(None, tox, friend_number, size, file_number) + self._data = bytes() + self._data_size = 0 + + def get_data(self): + return self._data + + data = property(get_data) + + def write_chunk(self, position, data): + if self._creation_time is None: + self._creation_time = time() + if data is None: + self.state = FILE_TRANSFER_STATE['FINISHED'] + self._finished() + else: + data = bytes(data) + l = len(data) + if self._data_size < position: + self._data += (b'\0' * (position - self._data_size)) + self._data = self._data[:position] + data + self._data[position + l:] + if position + l > self._data_size: + self._data_size = position + l + self._done += l + self._signal() + + +class ReceiveAvatar(ReceiveTransfer): + """ + Get friend's avatar. Doesn't need file transfer item + """ + MAX_AVATAR_SIZE = 512 * 1024 + + def __init__(self, path, tox, friend_number, size, file_number): + full_path = path + '.tmp' + super().__init__(full_path, tox, friend_number, size, file_number) + if size > self.MAX_AVATAR_SIZE: + self.send_control(TOX_FILE_CONTROL['CANCEL']) + self._file.close() + remove(full_path) + elif not size: + self.send_control(TOX_FILE_CONTROL['CANCEL']) + self._file.close() + remove(full_path) + elif exists(path): + hash = self.get_file_id() + with open(path, 'rb') as fl: + data = fl.read() + existing_hash = Tox.hash(data) + if hash == existing_hash: + self.send_control(TOX_FILE_CONTROL['CANCEL']) + self._file.close() + remove(full_path) + else: + self.send_control(TOX_FILE_CONTROL['RESUME']) + else: + self.send_control(TOX_FILE_CONTROL['RESUME']) + + def write_chunk(self, position, data): + if data is None: + avatar_path = self._path[:-4] + if exists(avatar_path): + chdir(dirname(avatar_path)) + remove(avatar_path) + rename(self._path, avatar_path) + super().write_chunk(position, data) diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py new file mode 100644 index 0000000..114383b --- /dev/null +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -0,0 +1,304 @@ +from messenger.messages import * +from ui.contact_items import * +import utils.util as util +from common.tox_save import ToxSave + + +class FileTransfersHandler(ToxSave): + + def __init__(self, tox, settings, contact_provider, file_transfers_message_service, profile): + super().__init__(tox) + self._settings = settings + self._contact_provider = contact_provider + self._file_transfers_message_service = file_transfers_message_service + self._file_transfers = {} + # key = (friend number, file number), value - transfer instance + self._paused_file_transfers = dict(settings['paused_file_transfers']) + # key - file id, value: [path, friend number, is incoming, start position] + self._insert_inline_before = {} + # key = (friend number, file number), value - message id + + profile.avatar_changed_event.add_callback(self._send_avatar_to_contacts) + + def stop(self): + self._settings['paused_file_transfers'] = self._paused_file_transfers if self._settings['resend_files'] else {} + self._settings.save() + + # ----------------------------------------------------------------------------------------------------------------- + # File transfers support + # ----------------------------------------------------------------------------------------------------------------- + + def incoming_file_transfer(self, friend_number, file_number, size, file_name): + """ + New transfer + :param friend_number: number of friend who sent file + :param file_number: file number + :param size: file size in bytes + :param file_name: file name without path + """ + friend = self._get_friend_by_number(friend_number) + auto = self._settings['allow_auto_accept'] and friend.tox_id in self._settings['auto_accept_from_friends'] + inline = is_inline(file_name) and self._settings['allow_inline'] + file_id = self._tox.file_get_file_id(friend_number, file_number) + accepted = True + if file_id in self._paused_file_transfers: + (path, ft_friend_number, is_incoming, start_position) = self._paused_file_transfers[file_id] + pos = start_position if os.path.exists(path) else 0 + if pos >= size: + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + return + self._tox.file_seek(friend_number, file_number, pos) + self._file_transfers_message_service.add_incoming_transfer_message( + friend, accepted, size, file_name, file_number) + self.accept_transfer(path, friend_number, file_number, size, False, pos) + elif inline and size < 1024 * 1024: + self._file_transfers_message_service.add_incoming_transfer_message( + friend, accepted, size, file_name, file_number) + self.accept_transfer('', friend_number, file_number, size, True) + elif auto: + path = self._settings['auto_accept_path'] or util.curr_directory() + self._file_transfers_message_service.add_incoming_transfer_message( + friend, accepted, size, file_name, file_number) + self.accept_transfer(path + '/' + file_name, friend_number, file_number, size) + else: + accepted = False + self._file_transfers_message_service.add_incoming_transfer_message( + friend, accepted, size, file_name, file_number) + + def cancel_transfer(self, friend_number, file_number, already_cancelled=False): + """ + Stop transfer + :param friend_number: number of friend + :param file_number: file number + :param already_cancelled: was cancelled by friend + """ + if (friend_number, file_number) in self._file_transfers: + tr = self._file_transfers[(friend_number, file_number)] + if not already_cancelled: + tr.cancel() + else: + tr.cancelled() + if (friend_number, file_number) in self._file_transfers: + del tr + del self._file_transfers[(friend_number, file_number)] + elif not already_cancelled: + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + + def cancel_not_started_transfer(self, friend_number, message_id): + self._get_friend_by_number(friend_number).delete_one_unsent_file(message_id) + + def pause_transfer(self, friend_number, file_number, by_friend=False): + """ + Pause transfer with specified data + """ + tr = self._file_transfers[(friend_number, file_number)] + tr.pause(by_friend) + + def resume_transfer(self, friend_number, file_number, by_friend=False): + """ + Resume transfer with specified data + """ + tr = self._file_transfers[(friend_number, file_number)] + if by_friend: + tr.state = FILE_TRANSFER_STATE['RUNNING'] + else: + tr.send_control(TOX_FILE_CONTROL['RESUME']) + + def accept_transfer(self, path, friend_number, file_number, size, inline=False, from_position=0): + """ + :param path: path for saving + :param friend_number: friend number + :param file_number: file number + :param size: file size + :param inline: is inline image + :param from_position: position for start + """ + path = self._generate_valid_path(path, from_position) + friend = self._get_friend_by_number(friend_number) + if not inline: + rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position) + else: + rt = ReceiveToBuffer(self._tox, friend_number, size, file_number) + rt.set_transfer_finished_handler(self.transfer_finished) + message = friend.get_message(lambda m: m.type == MESSAGE_TYPE['FILE_TRANSFER'] + and m.state in (FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'], + FILE_TRANSFER_STATE['RUNNING']) + and m.file_number == file_number) + rt.set_state_changed_handler(message.transfer_updated) + self._file_transfers[(friend_number, file_number)] = rt + rt.send_control(TOX_FILE_CONTROL['RESUME']) + if inline: + self._insert_inline_before[(friend_number, file_number)] = message.message_id + + def send_screenshot(self, data, friend_number): + """ + Send screenshot + :param data: raw data - png format + :param friend_number: friend number + """ + self.send_inline(data, 'toxygen_inline.png', friend_number) + + def send_sticker(self, path, friend_number): + with open(path, 'rb') as fl: + data = fl.read() + self.send_inline(data, 'sticker.png', friend_number) + + def send_inline(self, data, file_name, friend_number, is_resend=False): + friend = self._get_friend_by_number(friend_number) + if friend.status is None and not is_resend: + self._file_transfers_message_service.add_unsent_file_message(friend, file_name, data) + return + elif friend.status is None and is_resend: + raise RuntimeError() + st = SendFromBuffer(self._tox, friend.number, data, file_name) + self._send_file_add_set_handlers(st, friend, file_name, True) + + def send_file(self, path, friend_number, is_resend=False, file_id=None): + """ + Send file to current active friend + :param path: file path + :param friend_number: friend_number + :param is_resend: is 'offline' message + :param file_id: file id of transfer + """ + friend = self._get_friend_by_number(friend_number) + if friend.status is None and not is_resend: + self._file_transfers_message_service.add_unsent_file_message(friend, path, None) + return + elif friend.status is None and is_resend: + print('Error in sending') + return + st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) + file_name = os.path.basename(path) + self._send_file_add_set_handlers(st, friend, file_name) + + def incoming_chunk(self, friend_number, file_number, position, data): + """ + Incoming chunk + """ + self._file_transfers[(friend_number, file_number)].write_chunk(position, data) + + def outgoing_chunk(self, friend_number, file_number, position, size): + """ + Outgoing chunk + """ + self._file_transfers[(friend_number, file_number)].send_chunk(position, size) + + def transfer_finished(self, friend_number, file_number): + transfer = self._file_transfers[(friend_number, file_number)] + t = type(transfer) + if t is ReceiveAvatar: + self._get_friend_by_number(friend_number).load_avatar() + elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']): # inline image + print('inline') + inline = InlineImageMessage(transfer.data) + message_id = self._insert_inline_before[(friend_number, file_number)] + del self._insert_inline_before[(friend_number, file_number)] + index = self._get_friend_by_number(friend_number).insert_inline(message_id, inline) + self._file_transfers_message_service.add_inline_message(transfer, index) + del self._file_transfers[(friend_number, file_number)] + + def send_files(self, friend_number): + friend = self._get_friend_by_number(friend_number) + friend.remove_invalid_unsent_files() + files = friend.get_unsent_files() + try: + for fl in files: + data, path = fl.data, fl.path + if data is not None: + self.send_inline(data, path, friend_number, True) + else: + self.send_file(path, friend_number, True) + friend.clear_unsent_files() + for key in self._paused_file_transfers.keys(): + (path, ft_friend_number, is_incoming, start_position) = self._paused_file_transfers[key] + if not os.path.exists(path): + del self._paused_file_transfers[key] + elif ft_friend_number == friend_number and not is_incoming: + self.send_file(path, friend_number, True, key) + del self._paused_file_transfers[key] + except Exception as ex: + print('Exception in file sending: ' + str(ex)) + + def friend_exit(self, friend_number): + for friend_num, file_num in self._file_transfers.keys(): + if friend_num != friend_number: + continue + ft = self._file_transfers[(friend_num, file_num)] + if type(ft) is SendTransfer: + self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, False, -1] + elif type(ft) is ReceiveTransfer and ft.state != FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, True, ft.total_size()] + self.cancel_transfer(friend_num, file_num, True) + + # ----------------------------------------------------------------------------------------------------------------- + # Avatars support + # ----------------------------------------------------------------------------------------------------------------- + + def send_avatar(self, friend_number, avatar_path=None): + """ + :param friend_number: number of friend who should get new avatar + :param avatar_path: path to avatar or None if reset + """ + sa = SendAvatar(avatar_path, self._tox, friend_number) + self._file_transfers[(friend_number, sa.file_number)] = sa + + def incoming_avatar(self, friend_number, file_number, size): + """ + Friend changed avatar + :param friend_number: friend number + :param file_number: file number + :param size: size of avatar or 0 (default avatar) + """ + friend = self._get_friend_by_number(friend_number) + ra = ReceiveAvatar(friend.get_contact_avatar_path(), self._tox, friend_number, size, file_number) + if ra.state != FILE_TRANSFER_STATE['CANCELLED']: + self._file_transfers[(friend_number, file_number)] = ra + ra.set_transfer_finished_handler(self.transfer_finished) + elif not size: + friend.reset_avatar(self._settings['identicons']) + + def _send_avatar_to_contacts(self, _): + friends = self._get_all_friends() + for friend in filter(self._is_friend_online, friends): + self.send_avatar(friend.number) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _is_friend_online(self, friend_number): + friend = self._get_friend_by_number(friend_number) + + return friend.status is not None + + def _get_friend_by_number(self, friend_number): + return self._contact_provider.get_friend_by_number(friend_number) + + def _get_all_friends(self): + return self._contact_provider.get_all_friends() + + def _send_file_add_set_handlers(self, st, friend, file_name, inline=False): + st.set_transfer_finished_handler(self.transfer_finished) + file_number = st.get_file_number() + self._file_transfers[(friend.number, file_number)] = st + tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + st.set_state_changed_handler(tm.transfer_updated) + if inline: + self._insert_inline_before[(friend.number, file_number)] = tm.message_id + + @staticmethod + def _generate_valid_path(path, from_position): + path, file_name = os.path.split(path) + new_file_name, i = file_name, 1 + if not from_position: + while os.path.isfile(join_path(path, new_file_name)): # file with same name already exists + if '.' in file_name: # has extension + d = file_name.rindex('.') + else: # no extension + d = len(file_name) + new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] + i += 1 + path = join_path(path, new_file_name) + + return path diff --git a/toxygen/file_transfers/file_transfers_messages_service.py b/toxygen/file_transfers/file_transfers_messages_service.py new file mode 100644 index 0000000..4509183 --- /dev/null +++ b/toxygen/file_transfers/file_transfers_messages_service.py @@ -0,0 +1,78 @@ +from messenger.messenger import * +import utils.util as util +from file_transfers.file_transfers import * + + +class FileTransfersMessagesService: + + def __init__(self, contacts_manager, messages_items_factory, profile, main_screen): + self._contacts_manager = contacts_manager + self._messages_items_factory = messages_items_factory + self._profile = profile + self._messages = main_screen.messages + + def add_incoming_transfer_message(self, friend, accepted, size, file_name, file_number): + author = MessageAuthor(friend.name, MESSAGE_AUTHOR['FRIEND']) + status = FILE_TRANSFER_STATE['RUNNING'] if accepted else FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'] + tm = TransferMessage(author, util.get_unix_time(), status, size, file_name, friend.number, file_number) + + if self._is_friend_active(friend.number): + self._create_file_transfer_item(tm) + self._messages.scrollToBottom() + else: + friend.actions = True + + friend.append_message(tm) + + return tm + + def add_outgoing_transfer_message(self, friend, size, file_name, file_number): + author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['ME']) + status = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] + tm = TransferMessage(author, util.get_unix_time(), status, size, file_name, friend.number, file_number) + + if self._is_friend_active(friend.number): + self._create_file_transfer_item(tm) + self._messages.scrollToBottom() + + friend.append_message(tm) + + return tm + + def add_inline_message(self, transfer, index): + if not self._is_friend_active(transfer.friend_number): + return + count = self._messages.count() + if count + index + 1 >= 0: + self._create_inline_item(transfer.data, count + index + 1) + + def add_unsent_file_message(self, friend, file_path, data): + author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['ME']) + size = os.path.getsize(file_path) if data is None else len(data) + tm = UnsentFileMessage(file_path, data, util.get_unix_time(), author, size, friend.number) + friend.append_message(tm) + + if self._is_friend_active(friend.number): + self._create_unsent_file_item(tm) + self._messages.scrollToBottom() + + return tm + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _is_friend_active(self, friend_number): + if not self._contacts_manager.is_active_a_friend(): + return False + + return friend_number == self._contacts_manager.get_active_number() + + def _create_file_transfer_item(self, tm): + return self._messages_items_factory.create_file_transfer_item(tm) + + def _create_inline_item(self, data, position): + return self._messages_items_factory.create_inline_item(data, False, position) + + def _create_unsent_file_item(self, tm): + return self._messages_items_factory.create_unsent_file_item(tm) diff --git a/toxygen/groups/__init__.py b/toxygen/groups/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/groups/group_ban.py b/toxygen/groups/group_ban.py new file mode 100644 index 0000000..89ecc7e --- /dev/null +++ b/toxygen/groups/group_ban.py @@ -0,0 +1,23 @@ + + +class GroupBan: + + def __init__(self, ban_id, ban_target, ban_time): + self._ban_id = ban_id + self._ban_target = ban_target + self._ban_time = ban_time + + def get_ban_id(self): + return self._ban_id + + ban_id = property(get_ban_id) + + def get_ban_target(self): + return self._ban_target + + ban_target = property(get_ban_target) + + def get_ban_time(self): + return self._ban_time + + ban_time = property(get_ban_time) diff --git a/toxygen/groups/group_invite.py b/toxygen/groups/group_invite.py new file mode 100644 index 0000000..a2eed47 --- /dev/null +++ b/toxygen/groups/group_invite.py @@ -0,0 +1,23 @@ + + +class GroupInvite: + + def __init__(self, friend_public_key, chat_name, invite_data): + self._friend_public_key = friend_public_key + self._chat_name = chat_name + self._invite_data = invite_data[:] + + def get_friend_public_key(self): + return self._friend_public_key + + friend_public_key = property(get_friend_public_key) + + def get_chat_name(self): + return self._chat_name + + chat_name = property(get_chat_name) + + def get_invite_data(self): + return self._invite_data[:] + + invite_data = property(get_invite_data) diff --git a/toxygen/groups/group_peer.py b/toxygen/groups/group_peer.py new file mode 100644 index 0000000..4eaf255 --- /dev/null +++ b/toxygen/groups/group_peer.py @@ -0,0 +1,70 @@ + + +class GroupChatPeer: + """ + Represents peer in group chat. + """ + + def __init__(self, peer_id, name, status, role, public_key, is_current_user=False, is_muted=False): + self._peer_id = peer_id + self._name = name + self._status = status + self._role = role + self._public_key = public_key + self._is_current_user = is_current_user + self._is_muted = is_muted + + # ----------------------------------------------------------------------------------------------------------------- + # Readonly properties + # ----------------------------------------------------------------------------------------------------------------- + + def get_id(self): + return self._peer_id + + id = property(get_id) + + def get_public_key(self): + return self._public_key + + public_key = property(get_public_key) + + def get_is_current_user(self): + return self._is_current_user + + is_current_user = property(get_is_current_user) + + # ----------------------------------------------------------------------------------------------------------------- + # Read-write properties + # ----------------------------------------------------------------------------------------------------------------- + + def get_name(self): + return self._name + + def set_name(self, name): + self._name = name + + name = property(get_name, set_name) + + def get_status(self): + return self._status + + def set_status(self, status): + self._status = status + + status = property(get_status, set_status) + + def get_role(self): + return self._role + + def set_role(self, role): + self._role = role + + role = property(get_role, set_role) + + def get_is_muted(self): + return self._is_muted + + def set_is_muted(self, is_muted): + self._is_muted = is_muted + + is_muted = property(get_is_muted, set_is_muted) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py new file mode 100644 index 0000000..b8fc7cc --- /dev/null +++ b/toxygen/groups/groups_service.py @@ -0,0 +1,242 @@ +import common.tox_save as tox_save +import utils.ui as util_ui +from groups.peers_list import PeersListGenerator +from groups.group_invite import GroupInvite +import wrapper.toxcore_enums_and_consts as constants + + +class GroupsService(tox_save.ToxSave): + + def __init__(self, tox, contacts_manager, contacts_provider, main_screen, widgets_factory_provider): + super().__init__(tox) + self._contacts_manager = contacts_manager + self._contacts_provider = contacts_provider + self._main_screen = main_screen + self._peers_list_widget = main_screen.peers_list + self._widgets_factory_provider = widgets_factory_provider + self._group_invites = [] + self._screen = None + + def set_tox(self, tox): + super().set_tox(tox) + for group in self._get_all_groups(): + group.set_tox(tox) + + # ----------------------------------------------------------------------------------------------------------------- + # Groups creation + # ----------------------------------------------------------------------------------------------------------------- + + def create_new_gc(self, name, privacy_state, nick, status): + group_number = self._tox.group_new(privacy_state, name, nick, status) + if group_number == -1: + return + + self._add_new_group_by_number(group_number) + group = self._get_group_by_number(group_number) + group.status = constants.TOX_USER_STATUS['NONE'] + self._contacts_manager.update_filtration() + + def join_gc_by_id(self, chat_id, password, nick, status): + group_number = self._tox.group_join(chat_id, password, nick, status) + self._add_new_group_by_number(group_number) + + # ----------------------------------------------------------------------------------------------------------------- + # Groups reconnect and leaving + # ----------------------------------------------------------------------------------------------------------------- + + def leave_group(self, group_number): + self._tox.group_leave(group_number) + self._contacts_manager.delete_group(group_number) + + def disconnect_from_group(self, group_number): + self._tox.group_disconnect(group_number) + group = self._get_group_by_number(group_number) + group.status = None + self._clear_peers_list(group) + + def reconnect_to_group(self, group_number): + self._tox.group_reconnect(group_number) + group = self._get_group_by_number(group_number) + group.status = constants.TOX_USER_STATUS['NONE'] + self._clear_peers_list(group) + + # ----------------------------------------------------------------------------------------------------------------- + # Group invites + # ----------------------------------------------------------------------------------------------------------------- + + def invite_friend(self, friend_number, group_number): + self._tox.group_invite_friend(group_number, friend_number) + + def process_group_invite(self, friend_number, group_name, invite_data): + friend = self._get_friend_by_number(friend_number) + invite = GroupInvite(friend.tox_id, group_name, invite_data) + self._group_invites.append(invite) + self._update_invites_button_state() + + def accept_group_invite(self, invite, name, status, password): + pk = invite.friend_public_key + friend = self._get_friend_by_public_key(pk) + self._join_gc_via_invite(invite.invite_data, friend.number, name, status, password) + self._delete_group_invite(invite) + self._update_invites_button_state() + + def decline_group_invite(self, invite): + self._delete_group_invite(invite) + self._main_screen.update_gc_invites_button_state() + + def get_group_invites(self): + return self._group_invites[:] + + group_invites = property(get_group_invites) + + def get_group_invites_count(self): + return len(self._group_invites) + + group_invites_count = property(get_group_invites_count) + + # ----------------------------------------------------------------------------------------------------------------- + # Group info methods + # ----------------------------------------------------------------------------------------------------------------- + + def update_group_info(self, group): + group.name = self._tox.group_get_name(group.number) + group.status_message = self._tox.group_get_topic(group.number) + + def set_group_topic(self, group): + if not group.is_self_moderator_or_founder(): + return + text = util_ui.tr('New topic for group "{}":'.format(group.name)) + title = util_ui.tr('Set group topic') + topic, ok = util_ui.text_dialog(text, title, group.status_message) + if not ok or not topic: + return + self._tox.group_set_topic(group.number, topic) + group.status_message = topic + + def show_group_management_screen(self, group): + widgets_factory = self._get_widgets_factory() + self._screen = widgets_factory.create_group_management_screen(group) + self._screen.show() + + def show_group_settings_screen(self, group): + widgets_factory = self._get_widgets_factory() + self._screen = widgets_factory.create_group_settings_screen(group) + self._screen.show() + + def set_group_password(self, group, password): + if group.password == password: + return + self._tox.group_founder_set_password(group.number, password) + group.password = password + + def set_group_peers_limit(self, group, peers_limit): + if group.peers_limit == peers_limit: + return + self._tox.group_founder_set_peer_limit(group.number, peers_limit) + group.peers_limit = peers_limit + + def set_group_privacy_state(self, group, privacy_state): + is_private = privacy_state == constants.TOX_GROUP_PRIVACY_STATE['PRIVATE'] + if group.is_private == is_private: + return + self._tox.group_founder_set_privacy_state(group.number, privacy_state) + group.is_private = is_private + + # ----------------------------------------------------------------------------------------------------------------- + # Peers list + # ----------------------------------------------------------------------------------------------------------------- + + def generate_peers_list(self): + if not self._contacts_manager.is_active_a_group(): + return + group = self._contacts_manager.get_curr_contact() + PeersListGenerator().generate(group.peers, self, self._peers_list_widget, group.tox_id) + + def peer_selected(self, chat_id, peer_id): + widgets_factory = self._get_widgets_factory() + group = self._get_group_by_public_key(chat_id) + self_peer = group.get_self_peer() + if self_peer.id != peer_id: + self._screen = widgets_factory.create_peer_screen_window(group, peer_id) + else: + self._screen = widgets_factory.create_self_peer_screen_window(group) + self._screen.show() + + # ----------------------------------------------------------------------------------------------------------------- + # Peers actions + # ----------------------------------------------------------------------------------------------------------------- + + def set_new_peer_role(self, group, peer, role): + self._tox.group_mod_set_role(group.number, peer.id, role) + peer.role = role + self.generate_peers_list() + + def toggle_ignore_peer(self, group, peer, ignore): + self._tox.group_toggle_ignore(group.number, peer.id, ignore) + peer.is_muted = ignore + + def set_self_info(self, group, name, status): + self._tox.group_self_set_name(group.number, name) + self._tox.group_self_set_status(group.number, status) + self_peer = group.get_self_peer() + self_peer.name = name + self_peer.status = status + self.generate_peers_list() + + # ----------------------------------------------------------------------------------------------------------------- + # Bans support + # ----------------------------------------------------------------------------------------------------------------- + + def show_bans_list(self, group): + widgets_factory = self._get_widgets_factory() + self._screen = widgets_factory.create_groups_bans_screen(group) + self._screen.show() + + def ban_peer(self, group, peer_id, ban_type): + self._tox.group_mod_ban_peer(group.number, peer_id, ban_type) + + def kick_peer(self, group, peer_id): + self._tox.group_mod_remove_peer(group.number, peer_id) + + def cancel_ban(self, group_number, ban_id): + self._tox.group_mod_remove_ban(group_number, ban_id) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _add_new_group_by_number(self, group_number): + self._contacts_manager.add_group(group_number) + + def _get_group_by_number(self, group_number): + return self._contacts_provider.get_group_by_number(group_number) + + def _get_group_by_public_key(self, public_key): + return self._contacts_provider.get_group_by_public_key(public_key) + + def _get_all_groups(self): + return self._contacts_provider.get_all_groups() + + def _get_friend_by_number(self, friend_number): + return self._contacts_provider.get_friend_by_number(friend_number) + + def _get_friend_by_public_key(self, public_key): + return self._contacts_provider.get_friend_by_public_key(public_key) + + def _clear_peers_list(self, group): + group.remove_all_peers_except_self() + self.generate_peers_list() + + def _delete_group_invite(self, invite): + if invite in self._group_invites: + self._group_invites.remove(invite) + + def _join_gc_via_invite(self, invite_data, friend_number, nick, status, password): + group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, status, password) + self._add_new_group_by_number(group_number) + + def _update_invites_button_state(self): + self._main_screen.update_gc_invites_button_state() + + def _get_widgets_factory(self): + return self._widgets_factory_provider.get_item() diff --git a/toxygen/groups/peers_list.py b/toxygen/groups/peers_list.py new file mode 100644 index 0000000..17495f5 --- /dev/null +++ b/toxygen/groups/peers_list.py @@ -0,0 +1,104 @@ +from ui.group_peers_list import PeerItem, PeerTypeItem +from wrapper.toxcore_enums_and_consts import * +from ui.widgets import * + + +# ----------------------------------------------------------------------------------------------------------------- +# Builder +# ----------------------------------------------------------------------------------------------------------------- + + +class PeerListBuilder: + + def __init__(self): + self._peers = {} + self._titles = {} + self._index = 0 + self._handler = None + + def with_click_handler(self, handler): + self._handler = handler + + return self + + def with_title(self, title): + self._titles[self._index] = title + self._index += 1 + + return self + + def with_peers(self, peers): + for peer in peers: + self._add_peer(peer) + + return self + + def build(self, list_widget): + list_widget.clear() + + for i in range(self._index): + if i in self._peers: + peer = self._peers[i] + self._add_peer_item(peer, list_widget) + else: + title = self._titles[i] + self._add_peer_type_item(title, list_widget) + + def _add_peer_item(self, peer, parent): + item = PeerItem(peer, self._handler, parent.width(), parent) + self._add_item(parent, item) + + def _add_peer_type_item(self, text, parent): + item = PeerTypeItem(text, parent.width(), parent) + self._add_item(parent, item) + + @staticmethod + def _add_item(parent, item): + elem = QtWidgets.QListWidgetItem(parent) + elem.setSizeHint(QtCore.QSize(parent.width(), item.height())) + parent.addItem(elem) + parent.setItemWidget(elem, item) + + def _add_peer(self, peer): + self._peers[self._index] = peer + self._index += 1 + +# ----------------------------------------------------------------------------------------------------------------- +# Generators +# ----------------------------------------------------------------------------------------------------------------- + + +class PeersListGenerator: + + @staticmethod + def generate(peers_list, groups_service, list_widget, chat_id): + admin_title = util_ui.tr('Administrator') + moderators_title = util_ui.tr('Moderators') + users_title = util_ui.tr('Users') + observers_title = util_ui.tr('Observers') + + admins = list(filter(lambda p: p.role == TOX_GROUP_ROLE['FOUNDER'], peers_list)) + moderators = list(filter(lambda p: p.role == TOX_GROUP_ROLE['MODERATOR'], peers_list)) + users = list(filter(lambda p: p.role == TOX_GROUP_ROLE['USER'], peers_list)) + observers = list(filter(lambda p: p.role == TOX_GROUP_ROLE['OBSERVER'], peers_list)) + + builder = (PeerListBuilder() + .with_click_handler(lambda peer_id: groups_service.peer_selected(chat_id, peer_id))) + if len(admins): + (builder + .with_title(admin_title) + .with_peers(admins)) + if len(moderators): + (builder + .with_title(moderators_title) + .with_peers(moderators)) + if len(users): + (builder + .with_title(users_title) + .with_peers(users)) + if len(observers): + (builder + .with_title(observers_title) + .with_peers(observers)) + + builder.build(list_widget) diff --git a/toxygen/history/__init__.py b/toxygen/history/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/history/database.py b/toxygen/history/database.py new file mode 100644 index 0000000..751c74b --- /dev/null +++ b/toxygen/history/database.py @@ -0,0 +1,201 @@ +from sqlite3 import connect +import os.path +import utils.util as util + + +TIMEOUT = 11 + +SAVE_MESSAGES = 500 + +MESSAGE_AUTHOR = { + 'ME': 0, + 'FRIEND': 1, + 'NOT_SENT': 2, + 'GC_PEER': 3 +} + +CONTACT_TYPE = { + 'FRIEND': 0, + 'GC_PEER': 1, + 'GC_PEER_PRIVATE': 2 +} + + +class Database: + + def __init__(self, path, toxes): + self._path, self._toxes = path, toxes + self._name = os.path.basename(path) + if os.path.exists(path): + try: + with open(path, 'rb') as fin: + data = fin.read() + if toxes.is_data_encrypted(data): + data = toxes.pass_decrypt(data) + with open(path, 'wb') as fout: + fout.write(data) + except Exception as ex: + util.log('Db reading error: ' + str(ex)) + os.remove(path) + + # ----------------------------------------------------------------------------------------------------------------- + # Public methods + # ----------------------------------------------------------------------------------------------------------------- + + def save(self): + if self._toxes.has_password(): + with open(self._path, 'rb') as fin: + data = fin.read() + data = self._toxes.pass_encrypt(bytes(data)) + with open(self._path, 'wb') as fout: + fout.write(data) + + def export(self, directory): + new_path = util.join_path(directory, self._name) + with open(self._path, 'rb') as fin: + data = fin.read() + if self._toxes.has_password(): + data = self._toxes.pass_encrypt(data) + with open(new_path, 'wb') as fout: + fout.write(data) + + def add_friend_to_db(self, tox_id): + db = self._connect() + try: + cursor = db.cursor() + cursor.execute('CREATE TABLE IF NOT EXISTS id' + tox_id + '(' + ' id INTEGER PRIMARY KEY,' + ' author_name TEXT,' + ' message TEXT,' + ' author_type INTEGER,' + ' unix_time REAL,' + ' message_type INTEGER' + ')') + db.commit() + except: + print('Database is locked!') + db.rollback() + finally: + db.close() + + def delete_friend_from_db(self, tox_id): + db = self._connect() + try: + cursor = db.cursor() + cursor.execute('DROP TABLE id' + tox_id + ';') + db.commit() + except: + print('Database is locked!') + db.rollback() + finally: + db.close() + + def save_messages_to_db(self, tox_id, messages_iter): + db = self._connect() + try: + cursor = db.cursor() + cursor.executemany('INSERT INTO id' + tox_id + + '(message, author_name, author_type, unix_time, message_type) ' + + 'VALUES (?, ?, ?, ?, ?, ?);', messages_iter) + db.commit() + except: + print('Database is locked!') + db.rollback() + finally: + db.close() + + def update_messages(self, tox_id, message_id): + db = self._connect() + try: + cursor = db.cursor() + cursor.execute('UPDATE id' + tox_id + ' SET author = 0 ' + 'WHERE id = ' + str(message_id) + ' AND author = 2;') + db.commit() + except: + print('Database is locked!') + db.rollback() + finally: + db.close() + + def delete_message(self, tox_id, unique_id): + db = self._connect() + try: + cursor = db.cursor() + cursor.execute('DELETE FROM id' + tox_id + ' WHERE id = ' + str(unique_id) + ';') + db.commit() + except: + print('Database is locked!') + db.rollback() + finally: + db.close() + + def delete_messages(self, tox_id): + db = self._connect() + try: + cursor = db.cursor() + cursor.execute('DELETE FROM id' + tox_id + ';') + db.commit() + except: + print('Database is locked!') + db.rollback() + finally: + db.close() + + def messages_getter(self, tox_id): + self.add_friend_to_db(tox_id) + + return Database.MessageGetter(self._path, tox_id) + + # ----------------------------------------------------------------------------------------------------------------- + # Messages loading + # ----------------------------------------------------------------------------------------------------------------- + + class MessageGetter: + + def __init__(self, path, tox_id): + self._count = 0 + self._path = path + self._tox_id = tox_id + self._db = self._cursor = None + + def get_one(self): + return self.get(1) + + def get_all(self): + self._connect() + data = self._cursor.fetchall() + self._disconnect() + self._count = len(data) + return data + + def get(self, count): + self._connect() + self.skip() + data = self._cursor.fetchmany(count) + self._disconnect() + self._count += len(data) + return data + + def skip(self): + if self._count: + self._cursor.fetchmany(self._count) + + def delete_one(self): + if self._count: + self._count -= 1 + + def _connect(self): + self._db = connect(self._path, timeout=TIMEOUT) + self._cursor = self._db.cursor() + self._cursor.execute('SELECT message, author_type, author_name, unix_time, message_type, id FROM id' + + self._tox_id + ' ORDER BY unix_time DESC;') + + def _disconnect(self): + self._db.close() + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _connect(self): + return connect(self._path, timeout=TIMEOUT) diff --git a/toxygen/history/history.py b/toxygen/history/history.py new file mode 100644 index 0000000..bd7e353 --- /dev/null +++ b/toxygen/history/history.py @@ -0,0 +1,138 @@ +from history.history_logs_generators import * + + +class History: + + def __init__(self, contact_provider, db, settings, main_screen, messages_items_factory): + self._contact_provider = contact_provider + self._db = db + self._settings = settings + self._messages = main_screen.messages + self._messages_items_factory = messages_items_factory + self._is_loading = False + self._contacts_manager = None + + def __del__(self): + del self._db + + def set_contacts_manager(self, contacts_manager): + self._contacts_manager = contacts_manager + + # ----------------------------------------------------------------------------------------------------------------- + # History support + # ----------------------------------------------------------------------------------------------------------------- + + def save_history(self): + """ + Save history to db + """ + if self._settings['save_db']: + for friend in self._contact_provider.get_all_friends(): + self._db.add_friend_to_db(friend.tox_id) + if not self._settings['save_unsent_only']: + messages = friend.get_corr_for_saving() + else: + messages = friend.get_unsent_messages_for_saving() + self._db.delete_messages(friend.tox_id) + messages = map(lambda m: (m.text, m.author.name, m.author.type, m.time, m.type), messages) + self._db.save_messages_to_db(friend.tox_id, messages) + + self._db.save() + + def clear_history(self, friend, save_unsent=False): + """ + Clear chat history + """ + friend.clear_corr(save_unsent) + self._db.delete_friend_from_db(friend.tox_id) + + def export_history(self, contact, as_text=True): + extension = 'txt' if as_text else 'html' + file_name, _ = util_ui.save_file_dialog(util_ui.tr('Choose file name'), extension) + + if not file_name: + return + + if not file_name.endswith('.' + extension): + file_name += '.' + extension + + history = self.generate_history(contact, as_text) + with open(file_name, 'wt') as fl: + fl.write(history) + + def delete_message(self, message): + contact = self._contacts_manager.get_curr_contact() + if message.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): + if message.is_saved(): + self._db.delete_message(contact.tox_id, message.id) + contact.delete_message(message.message_id) + + def load_history(self, friend): + """ + Tries to load next part of messages + """ + if self._is_loading: + return + self._is_loading = True + friend.load_corr(False) + messages = friend.get_corr() + if not messages: + self._is_loading = False + return + messages.reverse() + messages = messages[self._messages.count():self._messages.count() + PAGE_SIZE] + for message in messages: + message_type = message.get_type() + if message_type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']): # text message + self._create_message_item(message) + elif message_type == MESSAGE_TYPE['FILE_TRANSFER']: # file transfer + if message.state == FILE_TRANSFER_STATE['UNSENT']: + self._create_unsent_file_item(message) + else: + self._create_file_transfer_item(message) + elif message_type == MESSAGE_TYPE['INLINE']: # inline image + self._create_inline_item(message) + else: # info message + self._create_message_item(message) + self._is_loading = False + + def get_message_getter(self, friend_public_key): + self._db.add_friend_to_db(friend_public_key) + + return self._db.messages_getter(friend_public_key) + + def delete_history(self, friend): + self._db.delete_friend_from_db(friend.tox_id) + + def add_friend_to_db(self, tox_id): + self._db.add_friend_to_db(tox_id) + + @staticmethod + def generate_history(contact, as_text=True, _range=None): + if _range is None: + contact.load_all_corr() + corr = contact.get_corr() + elif _range[1] + 1: + corr = contact.get_corr()[_range[0]:_range[1] + 1] + else: + corr = contact.get_corr()[_range[0]:] + + generator = TextHistoryGenerator(corr, contact.name) if as_text else HtmlHistoryGenerator(corr, contact.name) + + return generator.generate() + + # ----------------------------------------------------------------------------------------------------------------- + # Items creation + # ----------------------------------------------------------------------------------------------------------------- + + def _create_message_item(self, message): + return self._messages_items_factory.create_message_item(message, False) + + def _create_unsent_file_item(self, message): + return self._messages_items_factory.create_unsent_file_item(message, False) + + def _create_file_transfer_item(self, message): + return self._messages_items_factory.create_file_transfer_item(message, False) + + def _create_inline_item(self, message): + return self._messages_items_factory.create_inline_item(message, False) diff --git a/toxygen/history/history_logs_generators.py b/toxygen/history/history_logs_generators.py new file mode 100644 index 0000000..b8d0a56 --- /dev/null +++ b/toxygen/history/history_logs_generators.py @@ -0,0 +1,48 @@ +from messenger.messages import * +import utils.util as util + + +class HistoryLogsGenerator: + + def __init__(self, history, contact_name): + self._history = history + self._contact_name = contact_name + + def generate(self): + return str() + + @staticmethod + def _get_message_time(message): + return util.convert_time(message.time) if message.author.type != MESSAGE_AUTHOR['NOT_SENT'] else 'Unsent' + + +class HtmlHistoryGenerator(HistoryLogsGenerator): + + def __init__(self, history, contact_name): + super().__init__(history, contact_name) + + def generate(self): + arr = [] + for message in self._history: + if type(message) is TextMessage: + x = '[{}] {}: {}
' + arr.append(x.format(self._get_message_time(message), message.author.name, message.text)) + s = '
'.join(arr) + html = '{}{}' + + return html.format(self._contact_name, s) + + +class TextHistoryGenerator(HistoryLogsGenerator): + + def __init__(self, history, contact_name): + super().__init__(history, contact_name) + + def generate(self): + arr = [self._contact_name] + for message in self._history: + if type(message) is TextMessage: + x = '[{}] {}: {}\n' + arr.append(x.format(self._get_message_time(message), message.author.name, message.text)) + + return '\n'.join(arr) diff --git a/toxygen/messenger/__init__.py b/toxygen/messenger/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py new file mode 100644 index 0000000..e777c4b --- /dev/null +++ b/toxygen/messenger/messages.py @@ -0,0 +1,239 @@ +from history.database import MESSAGE_AUTHOR +import os.path +from ui.messages_widgets import * + + +MESSAGE_TYPE = { + 'TEXT': 0, + 'ACTION': 1, + 'FILE_TRANSFER': 2, + 'INLINE': 3, + 'INFO_MESSAGE': 4 +} + +PAGE_SIZE = 42 + + +class MessageAuthor: + + def __init__(self, author_name, author_type): + self._name = author_name + self._type = author_type + + def get_name(self): + return self._name + + name = property(get_name) + + def get_type(self): + return self._type + + def set_type(self, value): + self._type = value + + type = property(get_type, set_type) + + +class Message: + + MESSAGE_ID = 0 + + def __init__(self, message_type, author, time): + self._time = time + self._type = message_type + self._author = author + self._widget = None + self._message_id = self._get_id() + + def get_type(self): + return self._type + + type = property(get_type) + + def get_author(self): + return self._author + + author = property(get_author) + + def get_time(self): + return self._time + + time = property(get_time) + + def get_message_id(self): + return self._message_id + + message_id = property(get_message_id) + + def get_widget(self, *args): + self._widget = self._create_widget(*args) + + return self._widget + + widget = property(get_widget) + + def remove_widget(self): + self._widget = None + + def mark_as_sent(self): + self._author.type = MESSAGE_AUTHOR['ME'] + if self._widget is not None: + self._widget.mark_as_sent() + + def _create_widget(self, *args): + pass + + @staticmethod + def _get_id(): + Message.MESSAGE_ID += 1 + + return int(Message.MESSAGE_ID) + + +class TextMessage(Message): + """ + Plain text or action message + """ + + def __init__(self, message, owner, time, message_type, message_id=0): + super().__init__(message_type, owner, time) + self._message = message + self._id = message_id + + def get_text(self): + return self._message + + text = property(get_text) + + def get_id(self): + return self._id + + id = property(get_id) + + def is_saved(self): + return self._id > 0 + + def _create_widget(self, *args): + return MessageItem(self, *args) + + +class OutgoingTextMessage(TextMessage): + + def __init__(self, message, owner, time, message_type, tox_message_id=0): + super().__init__(message, owner, time, message_type) + self._tox_message_id = tox_message_id + + def get_tox_message_id(self): + return self._tox_message_id + + def set_tox_message_id(self, tox_message_id): + self._tox_message_id = tox_message_id + + tox_message_id = property(get_tox_message_id, set_tox_message_id) + + +class GroupChatMessage(TextMessage): + + def __init__(self, id, message, owner, time, message_type, name): + super().__init__(id, message, owner, time, message_type) + self._user_name = name + + +class TransferMessage(Message): + """ + Message with info about file transfer + """ + + def __init__(self, author, time, state, size, file_name, friend_number, file_number): + super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], author, time) + self._state = state + self._size = size + self._file_name = file_name + self._friend_number, self._file_number = friend_number, file_number + + def is_active(self, file_number): + if self._file_number != file_number: + return False + + return self._state not in (FILE_TRANSFER_STATE['FINISHED'], FILE_TRANSFER_STATE['CANCELLED']) + + def get_friend_number(self): + return self._friend_number + + friend_number = property(get_friend_number) + + def get_file_number(self): + return self._file_number + + file_number = property(get_file_number) + + def get_state(self): + return self._state + + def set_state(self, value): + self._state = value + + state = property(get_state, set_state) + + def get_size(self): + return self._size + + size = property(get_size) + + def get_file_name(self): + return self._file_name + + file_name = property(get_file_name) + + def transfer_updated(self, state, percentage, time): + self._state = state + if self._widget is not None: + self._widget.update_transfer_state(state, percentage, time) + + def _create_widget(self, *args): + return FileTransferItem(self, *args) + + +class UnsentFileMessage(TransferMessage): + + def __init__(self, path, data, time, author, size, friend_number): + file_name = os.path.basename(path) + super().__init__(author, time, FILE_TRANSFER_STATE['UNSENT'], size, file_name, friend_number, -1) + self._data, self._path = data, path + + def get_data(self): + return self._data + + data = property(get_data) + + def get_path(self): + return self._path + + path = property(get_path) + + def _create_widget(self, *args): + return UnsentFileItem(self, *args) + + +class InlineImageMessage(Message): + """ + Inline image + """ + + def __init__(self, data): + super().__init__(MESSAGE_TYPE['INLINE'], None, None) + self._data = data + + def get_data(self): + return self._data + + data = property(get_data) + + def _create_widget(self, *args): + return InlineImageItem(self, *args) + + +class InfoMessage(TextMessage): + + def __init__(self, message, time): + super().__init__(message, None, time, MESSAGE_TYPE['INFO_MESSAGE']) diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py new file mode 100644 index 0000000..e859135 --- /dev/null +++ b/toxygen/messenger/messenger.py @@ -0,0 +1,310 @@ +import common.tox_save as tox_save +from messenger.messages import * + + +class Messenger(tox_save.ToxSave): + + def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider, items_factory, profile, + calls_manager): + super().__init__(tox) + self._plugin_loader = plugin_loader + self._screen = screen + self._contacts_manager = contacts_manager + self._contacts_provider = contacts_provider + self._items_factory = items_factory + self._profile = profile + self._profile_name = profile.name + + profile.name_changed_event.add_callback(self._on_profile_name_changed) + calls_manager.call_started_event.add_callback(self._on_call_started) + calls_manager.call_finished_event.add_callback(self._on_call_finished) + + def get_last_message(self): + contact = self._contacts_manager.get_curr_contact() + if contact is None: + return str() + + return contact.get_last_message_text() + + # ----------------------------------------------------------------------------------------------------------------- + # Messaging - friends + # ----------------------------------------------------------------------------------------------------------------- + + def new_message(self, friend_number, message_type, message): + """ + Current user gets new message + :param friend_number: friend_num of friend who sent message + :param message_type: message type - plain text or action message (/me) + :param message: text of message + """ + t = util.get_unix_time() + friend = self._get_friend_by_number(friend_number) + text_message = TextMessage(message, MessageAuthor(friend.name, MESSAGE_AUTHOR['FRIEND']), t, message_type) + self._add_message(text_message, friend) + + def send_message(self): + text = self._screen.messageEdit.toPlainText() + + plugin_command_prefix = '/plugin ' + if text.startswith(plugin_command_prefix): + self._plugin_loader.command(text[len(plugin_command_prefix):]) + self._screen.messageEdit.clear() + return + + action_message_prefix = '/me ' + if text.startswith(action_message_prefix): + message_type = TOX_MESSAGE_TYPE['ACTION'] + text = text[len(action_message_prefix):] + else: + message_type = TOX_MESSAGE_TYPE['NORMAL'] + + if self._contacts_manager.is_active_a_friend(): + self.send_message_to_friend(text, message_type) + elif self._contacts_manager.is_active_a_group(): + self.send_message_to_group(text, message_type) + elif self._contacts_manager.is_active_a_group_chat_peer(): + self.send_message_to_group_peer(text, message_type) + + def send_message_to_friend(self, text, message_type, friend_number=None): + """ + Send message + :param text: message text + :param friend_number: number of friend + """ + if friend_number is None: + friend_number = self._contacts_manager.get_active_number() + + if not text or friend_number < 0: + return + + friend = self._get_friend_by_number(friend_number) + messages = self._split_message(text.encode('utf-8')) + t = util.get_unix_time() + for message in messages: + if friend.status is not None: + message_id = self._tox.friend_send_message(friend_number, message_type, message) + else: + message_id = 0 + message_author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['NOT_SENT']) + message = OutgoingTextMessage(text, message_author, t, message_type, message_id) + friend.append_message(message) + if not self._contacts_manager.is_friend_active(friend_number): + return + self._create_message_item(message) + self._screen.messageEdit.clear() + self._screen.messages.scrollToBottom() + + def send_messages(self, friend_number): + """ + Send 'offline' messages to friend + """ + friend = self._get_friend_by_number(friend_number) + friend.load_corr() + messages = friend.get_unsent_messages() + try: + for message in messages: + message_id = self._tox.friend_send_message(friend_number, message.type, message.text.encode('utf-8')) + message.tox_message_id = message_id + except Exception as ex: + util.log('Sending pending messages failed with ' + str(ex)) + + # ----------------------------------------------------------------------------------------------------------------- + # Messaging - groups + # ----------------------------------------------------------------------------------------------------------------- + + def send_message_to_group(self, text, message_type, group_number=None): + if group_number is None: + group_number = self._contacts_manager.get_active_number() + + if not text or group_number < 0: + return + + group = self._get_group_by_number(group_number) + messages = self._split_message(text.encode('utf-8')) + t = util.get_unix_time() + for message in messages: + self._tox.group_send_message(group_number, message_type, message) + message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER']) + message = OutgoingTextMessage(text, message_author, t, message_type) + group.append_message(message) + if not self._contacts_manager.is_group_active(group_number): + return + self._create_message_item(message) + self._screen.messageEdit.clear() + self._screen.messages.scrollToBottom() + + def new_group_message(self, group_number, message_type, message, peer_id): + """ + Current user gets new message + :param message_type: message type - plain text or action message (/me) + :param message: text of message + """ + t = util.get_unix_time() + group = self._get_group_by_number(group_number) + peer = group.get_peer_by_id(peer_id) + text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']), t, message_type) + self._add_message(text_message, group) + + # ----------------------------------------------------------------------------------------------------------------- + # Messaging - group peers + # ----------------------------------------------------------------------------------------------------------------- + + def send_message_to_group_peer(self, text, message_type, group_number=None, peer_id=None): + if group_number is None or peer_id is None: + group_peer_contact = self._contacts_manager.get_curr_contact() + peer_id = group_peer_contact.number + group = self._get_group_by_public_key(group_peer_contact.group_pk) + group_number = group.number + + if not text or group_number < 0 or peer_id < 0: + return + + group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id) + group = self._get_group_by_number(group_number) + messages = self._split_message(text.encode('utf-8')) + t = util.get_unix_time() + for message in messages: + self._tox.group_send_private_message(group_number, peer_id, message_type, message) + message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER']) + message = OutgoingTextMessage(text, message_author, t, message_type) + group_peer_contact.append_message(message) + if not self._contacts_manager.is_contact_active(group_peer_contact): + return + self._create_message_item(message) + self._screen.messageEdit.clear() + self._screen.messages.scrollToBottom() + + def new_group_private_message(self, group_number, message_type, message, peer_id): + """ + Current user gets new message + :param message: text of message + """ + t = util.get_unix_time() + group = self._get_group_by_number(group_number) + peer = group.get_peer_by_id(peer_id) + text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']), + t, message_type) + group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id) + self._add_message(text_message, group_peer_contact) + + # ----------------------------------------------------------------------------------------------------------------- + # Message receipts + # ----------------------------------------------------------------------------------------------------------------- + + def receipt(self, friend_number, message_id): + friend = self._get_friend_by_number(friend_number) + friend.mark_as_sent(message_id) + + # ----------------------------------------------------------------------------------------------------------------- + # Typing notifications + # ----------------------------------------------------------------------------------------------------------------- + + def send_typing(self, typing): + """ + Send typing notification to a friend + """ + if not self._contacts_manager.can_send_typing_notification(): + return + contact = self._contacts_manager.get_curr_contact() + contact.typing_notification_handler.send(self._tox, typing) + + def friend_typing(self, friend_number, typing): + """ + Display incoming typing notification + """ + if self._contacts_manager.is_friend_active(friend_number): + self._screen.typing.setVisible(typing) + + # ----------------------------------------------------------------------------------------------------------------- + # Contact info updated + # ----------------------------------------------------------------------------------------------------------------- + + def new_friend_name(self, friend, old_name, new_name): + if old_name == new_name or friend.has_alias(): + return + message = util_ui.tr('User {} is now known as {}') + message = message.format(old_name, new_name) + if not self._contacts_manager.is_friend_active(friend.number): + friend.actions = True + self._add_info_message(friend.number, message) + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def _split_message(message): + messages = [] + while len(message) > TOX_MAX_MESSAGE_LENGTH: + size = TOX_MAX_MESSAGE_LENGTH * 4 // 5 + last_part = message[size:TOX_MAX_MESSAGE_LENGTH] + if b' ' in last_part: + index = last_part.index(b' ') + elif b',' in last_part: + index = last_part.index(b',') + elif b'.' in last_part: + index = last_part.index(b'.') + else: + index = TOX_MAX_MESSAGE_LENGTH - size - 1 + index += size + 1 + messages.append(message[:index]) + message = message[index:] + if message: + messages.append(message) + + return messages + + def _get_friend_by_number(self, friend_number): + return self._contacts_provider.get_friend_by_number(friend_number) + + def _get_group_by_number(self, group_number): + return self._contacts_provider.get_group_by_number(group_number) + + def _get_group_by_public_key(self, public_key): + return self._contacts_provider.get_group_by_public_key( public_key) + + def _on_profile_name_changed(self, new_name): + if self._profile_name == new_name: + return + message = util_ui.tr('User {} is now known as {}') + message = message.format(self._profile_name, new_name) + for friend in self._contacts_provider.get_all_friends(): + self._add_info_message(friend.number, message) + self._profile_name = new_name + + def _on_call_started(self, friend_number, audio, video, is_outgoing): + if is_outgoing: + text = util_ui.tr("Outgoing video call") if video else util_ui.tr("Outgoing audio call") + else: + text = util_ui.tr("Incoming video call") if video else util_ui.tr("Incoming audio call") + self._add_info_message(friend_number, text) + + def _on_call_finished(self, friend_number, is_declined): + text = util_ui.tr("Call declined") if is_declined else util_ui.tr("Call finished") + self._add_info_message(friend_number, text) + + def _add_info_message(self, friend_number, text): + friend = self._get_friend_by_number(friend_number) + message = InfoMessage(text, util.get_unix_time()) + friend.append_message(message) + if self._contacts_manager.is_friend_active(friend_number): + self._create_info_message_item(message) + + def _create_info_message_item(self, message): + self._items_factory.create_message_item(message) + self._screen.messages.scrollToBottom() + + def _add_message(self, text_message, contact): + if self._contacts_manager.is_contact_active(contact): # add message to list + self._create_message_item(text_message) + self._screen.messages.scrollToBottom() + self._contacts_manager.get_curr_contact().append_message(text_message) + else: + contact.inc_messages() + contact.append_message(text_message) + if not contact.visibility: + self._contacts_manager.update_filtration() + + def _create_message_item(self, text_message): + # pixmap = self._contacts_manager.get_curr_contact().get_pixmap() + self._items_factory.create_message_item(text_message) diff --git a/toxygen/middleware/__init__.py b/toxygen/middleware/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py new file mode 100644 index 0000000..b9a4099 --- /dev/null +++ b/toxygen/middleware/callbacks.py @@ -0,0 +1,605 @@ +from PyQt5 import QtGui +from wrapper.toxcore_enums_and_consts import * +from wrapper.toxav_enums import * +from wrapper.tox import bin_to_string +import utils.ui as util_ui +import utils.util as util +import cv2 +import numpy as np +from middleware.threads import invoke_in_main_thread, execute +from notifications.tray import tray_notification +from notifications.sound import * +import threading + +# TODO: refactoring. Use contact provider instead of manager + +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - current user +# ----------------------------------------------------------------------------------------------------------------- + + +def self_connection_status(tox, profile): + """ + Current user changed connection status (offline, TCP, UDP) + """ + def wrapped(tox_link, connection, user_data): + print('Connection status: ', str(connection)) + status = tox.self_get_status() if connection != TOX_CONNECTION['NONE'] else None + invoke_in_main_thread(profile.set_status, status) + + return wrapped + + +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - friends +# ----------------------------------------------------------------------------------------------------------------- + + +def friend_status(contacts_manager, file_transfer_handler, profile, settings): + def wrapped(tox, friend_number, new_status, user_data): + """ + Check friend's status (none, busy, away) + """ + print("Friend's #{} status changed!".format(friend_number)) + friend = contacts_manager.get_friend_by_number(friend_number) + if friend.status is None and settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) + invoke_in_main_thread(friend.set_status, new_status) + + def set_timer(): + t = threading.Timer(5, lambda: file_transfer_handler.send_files(friend_number)) + t.start() + invoke_in_main_thread(set_timer) + invoke_in_main_thread(contacts_manager.update_filtration) + + return wrapped + + +def friend_connection_status(contacts_manager, profile, settings, plugin_loader, file_transfer_handler, + messenger, calls_manager): + def wrapped(tox, friend_number, new_status, user_data): + """ + Check friend's connection status (offline, udp, tcp) + """ + print("Friend #{} connection status: {}".format(friend_number, new_status)) + friend = contacts_manager.get_friend_by_number(friend_number) + if new_status == TOX_CONNECTION['NONE']: + invoke_in_main_thread(friend.set_status, None) + invoke_in_main_thread(file_transfer_handler.friend_exit, friend_number) + invoke_in_main_thread(contacts_manager.update_filtration) + invoke_in_main_thread(messenger.friend_typing, friend_number, False) + invoke_in_main_thread(calls_manager.friend_exit, friend_number) + if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) + elif friend.status is None: + invoke_in_main_thread(file_transfer_handler.send_avatar, friend_number) + invoke_in_main_thread(plugin_loader.friend_online, friend_number) + + return wrapped + + +def friend_name(contacts_provider, messenger): + def wrapped(tox, friend_number, name, size, user_data): + """ + Friend changed his name + """ + print('New name friend #' + str(friend_number)) + friend = contacts_provider.get_friend_by_number(friend_number) + old_name = friend.name + new_name = str(name, 'utf-8') + invoke_in_main_thread(friend.set_name, new_name) + invoke_in_main_thread(messenger.new_friend_name, friend, old_name, new_name) + + return wrapped + + +def friend_status_message(contacts_manager, messenger): + def wrapped(tox, friend_number, status_message, size, user_data): + """ + :return: function for callback friend_status_message. It updates friend's status message + and calls window repaint + """ + friend = contacts_manager.get_friend_by_number(friend_number) + invoke_in_main_thread(friend.set_status_message, str(status_message, 'utf-8')) + print('User #{} has new status message'.format(friend_number)) + invoke_in_main_thread(messenger.send_messages, friend_number) + + return wrapped + + +def friend_message(messenger, contacts_manager, profile, settings, window, tray): + def wrapped(tox, friend_number, message_type, message, size, user_data): + """ + New message from friend + """ + message = str(message, 'utf-8') + invoke_in_main_thread(messenger.new_message, friend_number, message_type, message) + if not window.isActiveWindow(): + friend = contacts_manager.get_friend_by_number(friend_number) + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: + invoke_in_main_thread(tray_notification, friend.name, message, tray, window) + if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['MESSAGE']) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + + return wrapped + + +def friend_request(contacts_manager): + def wrapped(tox, public_key, message, message_size, user_data): + """ + Called when user get new friend request + """ + print('Friend request') + key = ''.join(chr(x) for x in public_key[:TOX_PUBLIC_KEY_SIZE]) + tox_id = bin_to_string(key, TOX_PUBLIC_KEY_SIZE) + invoke_in_main_thread(contacts_manager.process_friend_request, tox_id, str(message, 'utf-8')) + + return wrapped + + +def friend_typing(messenger): + def wrapped(tox, friend_number, typing, user_data): + invoke_in_main_thread(messenger.friend_typing, friend_number, typing) + + return wrapped + + +def friend_read_receipt(messenger): + def wrapped(tox, friend_number, message_id, user_data): + invoke_in_main_thread(messenger.receipt, friend_number, message_id) + + return wrapped + + +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - file transfers +# ----------------------------------------------------------------------------------------------------------------- + + +def tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager, settings): + """ + New incoming file + """ + def wrapped(tox, friend_number, file_number, file_type, size, file_name, file_name_size, user_data): + if file_type == TOX_FILE_KIND['DATA']: + print('File') + try: + file_name = str(file_name[:file_name_size], 'utf-8') + except: + file_name = 'toxygen_file' + invoke_in_main_thread(file_transfer_handler.incoming_file_transfer, + friend_number, + file_number, + size, + file_name) + if not window.isActiveWindow(): + friend = contacts_manager.get_friend_by_number(friend_number) + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: + file_from = util_ui.tr("File from") + invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window) + if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER']) + icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + else: # avatar + print('Avatar') + invoke_in_main_thread(file_transfer_handler.incoming_avatar, + friend_number, + file_number, + size) + return wrapped + + +def file_recv_chunk(file_transfer_handler): + """ + Incoming chunk + """ + def wrapped(tox, friend_number, file_number, position, chunk, length, user_data): + chunk = chunk[:length] if length else None + execute(file_transfer_handler.incoming_chunk, friend_number, file_number, position, chunk) + + return wrapped + + +def file_chunk_request(file_transfer_handler): + """ + Outgoing chunk + """ + def wrapped(tox, friend_number, file_number, position, size, user_data): + execute(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size) + + return wrapped + + +def file_recv_control(file_transfer_handler): + """ + Friend cancelled, paused or resumed file transfer + """ + def wrapped(tox, friend_number, file_number, file_control, user_data): + if file_control == TOX_FILE_CONTROL['CANCEL']: + file_transfer_handler.cancel_transfer(friend_number, file_number, True) + elif file_control == TOX_FILE_CONTROL['PAUSE']: + file_transfer_handler.pause_transfer(friend_number, file_number, True) + elif file_control == TOX_FILE_CONTROL['RESUME']: + file_transfer_handler.resume_transfer(friend_number, file_number, True) + + return wrapped + +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - custom packets +# ----------------------------------------------------------------------------------------------------------------- + + +def lossless_packet(plugin_loader): + def wrapped(tox, friend_number, data, length, user_data): + """ + Incoming lossless packet + """ + data = data[:length] + invoke_in_main_thread(plugin_loader.callback_lossless, friend_number, data) + + return wrapped + + +def lossy_packet(plugin_loader): + def wrapped(tox, friend_number, data, length, user_data): + """ + Incoming lossy packet + """ + data = data[:length] + invoke_in_main_thread(plugin_loader.callback_lossy, friend_number, data) + + return wrapped + + +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - audio +# ----------------------------------------------------------------------------------------------------------------- + +def call_state(calls_manager): + def wrapped(toxav, friend_number, mask, user_data): + """ + New call state + """ + print(friend_number, mask) + if mask == TOXAV_FRIEND_CALL_STATE['FINISHED'] or mask == TOXAV_FRIEND_CALL_STATE['ERROR']: + invoke_in_main_thread(calls_manager.stop_call, friend_number, True) + else: + calls_manager.toxav_call_state_cb(friend_number, mask) + + return wrapped + + +def call(calls_manager): + def wrapped(toxav, friend_number, audio, video, user_data): + """ + Incoming call from friend + """ + print(friend_number, audio, video) + invoke_in_main_thread(calls_manager.incoming_call, audio, video, friend_number) + + return wrapped + + +def callback_audio(calls_manager): + def wrapped(toxav, friend_number, samples, audio_samples_per_channel, audio_channels_count, rate, user_data): + """ + New audio chunk + """ + calls_manager.call.audio_chunk( + bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]), + audio_channels_count, + rate) + + return wrapped + +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - video +# ----------------------------------------------------------------------------------------------------------------- + + +def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data): + """ + Creates yuv frame from y, u, v and shows it using OpenCV + For yuv => bgr we need this YUV420 frame: + + width + ------------------------- + | | + | Y | height + | | + ------------------------- + | | | + | U even | U odd | height // 4 + | | | + ------------------------- + | | | + | V even | V odd | height // 4 + | | | + ------------------------- + + width // 2 width // 2 + + It can be created from initial y, u, v using slices + """ + try: + y_size = abs(max(width, abs(ystride))) + u_size = abs(max(width // 2, abs(ustride))) + v_size = abs(max(width // 2, abs(vstride))) + + y = np.asarray(y[:y_size * height], dtype=np.uint8).reshape(height, y_size) + u = np.asarray(u[:u_size * height // 2], dtype=np.uint8).reshape(height // 2, u_size) + v = np.asarray(v[:v_size * height // 2], dtype=np.uint8).reshape(height // 2, v_size) + + width -= width % 4 + height -= height % 4 + + frame = np.zeros((int(height * 1.5), width), dtype=np.uint8) + + frame[:height, :] = y[:height, :width] + frame[height:height * 5 // 4, :width // 2] = u[:height // 2:2, :width // 2] + frame[height:height * 5 // 4, width // 2:] = u[1:height // 2:2, :width // 2] + + frame[height * 5 // 4:, :width // 2] = v[:height // 2:2, :width // 2] + frame[height * 5 // 4:, width // 2:] = v[1:height // 2:2, :width // 2] + + frame = cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_I420) + + invoke_in_main_thread(cv2.imshow, str(friend_number), frame) + except Exception as ex: + print(ex) + +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - groups +# ----------------------------------------------------------------------------------------------------------------- + + +def group_message(window, tray, tox, messenger, settings, profile): + """ + New message in group chat + """ + def wrapped(tox_link, group_number, peer_id, message_type, message, length, user_data): + message = str(message[:length], 'utf-8') + invoke_in_main_thread(messenger.new_group_message, group_number, message_type, message, peer_id) + if window.isActiveWindow(): + return + bl = settings['notify_all_gc'] or profile.name in message + name = tox.group_peer_get_name(group_number, peer_id) + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and (not settings.locked) and bl: + invoke_in_main_thread(tray_notification, name, message, tray, window) + if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['MESSAGE']) + icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + + return wrapped + + +def group_private_message(window, tray, tox, messenger, settings, profile): + """ + New private message in group chat + """ + def wrapped(tox_link, group_number, peer_id, message_type, message, length, user_data): + message = str(message[:length], 'utf-8') + invoke_in_main_thread(messenger.new_group_private_message, group_number, message_type, message, peer_id) + if window.isActiveWindow(): + return + bl = settings['notify_all_gc'] or profile.name in message + name = tox.group_peer_get_name(group_number, peer_id) + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and (not settings.locked) and bl: + invoke_in_main_thread(tray_notification, name, message, tray, window) + if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: + sound_notification(SOUND_NOTIFICATION['MESSAGE']) + icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + + return wrapped + + +def group_invite(window, settings, tray, profile, groups_service, contacts_provider): + def wrapped(tox, friend_number, invite_data, length, group_name, group_name_length, user_data): + group_name = str(bytes(group_name[:group_name_length]), 'utf-8') + invoke_in_main_thread(groups_service.process_group_invite, + friend_number, group_name, + bytes(invite_data[:length])) + if window.isActiveWindow(): + return + if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: + friend = contacts_provider.get_friend_by_number(friend_number) + title = util_ui.tr('New invite to group chat') + text = util_ui.tr('{} invites you to group "{}"').format(friend.name, group_name) + invoke_in_main_thread(tray_notification, title, text, tray, window) + icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + + return wrapped + + +def group_self_join(contacts_provider, contacts_manager, groups_service): + def wrapped(tox, group_number, user_data): + group = contacts_provider.get_group_by_number(group_number) + invoke_in_main_thread(group.set_status, TOX_USER_STATUS['NONE']) + invoke_in_main_thread(groups_service.update_group_info, group) + invoke_in_main_thread(contacts_manager.update_filtration) + + return wrapped + + +def group_peer_join(contacts_provider, groups_service): + def wrapped(tox, group_number, peer_id, user_data): + group = contacts_provider.get_group_by_number(group_number) + group.add_peer(peer_id) + invoke_in_main_thread(groups_service.generate_peers_list) + invoke_in_main_thread(groups_service.update_group_info, group) + + return wrapped + + +def group_peer_exit(contacts_provider, groups_service, contacts_manager): + def wrapped(tox, group_number, peer_id, message, length, user_data): + group = contacts_provider.get_group_by_number(group_number) + group.remove_peer(peer_id) + invoke_in_main_thread(groups_service.generate_peers_list) + + return wrapped + + +def group_peer_name(contacts_provider, groups_service): + def wrapped(tox, group_number, peer_id, name, length, user_data): + group = contacts_provider.get_group_by_number(group_number) + peer = group.get_peer_by_id(peer_id) + peer.name = str(name[:length], 'utf-8') + invoke_in_main_thread(groups_service.generate_peers_list) + + return wrapped + + +def group_peer_status(contacts_provider, groups_service): + def wrapped(tox, group_number, peer_id, peer_status, user_data): + group = contacts_provider.get_group_by_number(group_number) + peer = group.get_peer_by_id(peer_id) + peer.status = peer_status + invoke_in_main_thread(groups_service.generate_peers_list) + + return wrapped + + +def group_topic(contacts_provider): + def wrapped(tox, group_number, peer_id, topic, length, user_data): + group = contacts_provider.get_group_by_number(group_number) + topic = str(topic[:length], 'utf-8') + invoke_in_main_thread(group.set_status_message, topic) + + return wrapped + + +def group_moderation(groups_service, contacts_provider, contacts_manager, messenger): + + def update_peer_role(group, mod_peer_id, peer_id, new_role): + peer = group.get_peer_by_id(peer_id) + peer.role = new_role + # TODO: add info message + + def remove_peer(group, mod_peer_id, peer_id, is_ban): + contacts_manager.remove_group_peer_by_id(group, peer_id) + group.remove_peer(peer_id) + # TODO: add info message + + def wrapped(tox, group_number, mod_peer_id, peer_id, event_type, user_data): + group = contacts_provider.get_group_by_number(group_number) + + if event_type == TOX_GROUP_MOD_EVENT['KICK']: + remove_peer(group, mod_peer_id, peer_id, False) + elif event_type == TOX_GROUP_MOD_EVENT['BAN']: + remove_peer(group, mod_peer_id, peer_id, True) + elif event_type == TOX_GROUP_MOD_EVENT['OBSERVER']: + update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['OBSERVER']) + elif event_type == TOX_GROUP_MOD_EVENT['USER']: + update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['USER']) + elif event_type == TOX_GROUP_MOD_EVENT['MODERATOR']: + update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['MODERATOR']) + + invoke_in_main_thread(groups_service.generate_peers_list) + + return wrapped + + +def group_password(contacts_provider): + + def wrapped(tox_link, group_number, password, length, user_data): + password = str(password[:length], 'utf-8') + group = contacts_provider.get_group_by_number(group_number) + group.password = password + + return wrapped + + +def group_peer_limit(contacts_provider): + + def wrapped(tox_link, group_number, peer_limit, user_data): + group = contacts_provider.get_group_by_number(group_number) + group.peer_limit = peer_limit + + return wrapped + + +def group_privacy_state(contacts_provider): + + def wrapped(tox_link, group_number, privacy_state, user_data): + group = contacts_provider.get_group_by_number(group_number) + group.is_private = privacy_state == TOX_GROUP_PRIVACY_STATE['PRIVATE'] + + return wrapped + +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - initialization +# ----------------------------------------------------------------------------------------------------------------- + + +def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, + calls_manager, file_transfer_handler, main_window, tray, messenger, groups_service, + contacts_provider): + """ + Initialization of all callbacks. + :param tox: Tox instance + :param profile: Profile instance + :param settings: Settings instance + :param contacts_manager: ContactsManager instance + :param contacts_manager: ContactsManager instance + :param calls_manager: CallsManager instance + :param file_transfer_handler: FileTransferHandler instance + :param plugin_loader: PluginLoader instance + :param main_window: MainWindow instance + :param tray: tray (for notifications) + :param messenger: Messenger instance + :param groups_service: GroupsService instance + :param contacts_provider: ContactsProvider instance + """ + # self callbacks + tox.callback_self_connection_status(self_connection_status(tox, profile)) + + # friend callbacks + tox.callback_friend_status(friend_status(contacts_manager, file_transfer_handler, profile, settings)) + tox.callback_friend_message(friend_message(messenger, contacts_manager, profile, settings, main_window, tray)) + tox.callback_friend_connection_status(friend_connection_status(contacts_manager, profile, settings, plugin_loader, + file_transfer_handler, messenger, calls_manager)) + tox.callback_friend_name(friend_name(contacts_provider, messenger)) + tox.callback_friend_status_message(friend_status_message(contacts_manager, messenger)) + tox.callback_friend_request(friend_request(contacts_manager)) + tox.callback_friend_typing(friend_typing(messenger)) + tox.callback_friend_read_receipt(friend_read_receipt(messenger)) + + # file transfer + tox.callback_file_recv(tox_file_recv(main_window, tray, profile, file_transfer_handler, + contacts_manager, settings)) + tox.callback_file_recv_chunk(file_recv_chunk(file_transfer_handler)) + tox.callback_file_chunk_request(file_chunk_request(file_transfer_handler)) + tox.callback_file_recv_control(file_recv_control(file_transfer_handler)) + + # av + toxav = tox.AV + toxav.callback_call_state(call_state(calls_manager), 0) + toxav.callback_call(call(calls_manager), 0) + toxav.callback_audio_receive_frame(callback_audio(calls_manager), 0) + toxav.callback_video_receive_frame(video_receive_frame, 0) + + # custom packets + tox.callback_friend_lossless_packet(lossless_packet(plugin_loader)) + tox.callback_friend_lossy_packet(lossy_packet(plugin_loader)) + + # gc callbacks + tox.callback_group_message(group_message(main_window, tray, tox, messenger, settings, profile), 0) + tox.callback_group_private_message(group_private_message(main_window, tray, tox, messenger, settings, profile), 0) + tox.callback_group_invite(group_invite(main_window, settings, tray, profile, groups_service, contacts_provider), 0) + tox.callback_group_self_join(group_self_join(contacts_provider, contacts_manager, groups_service), 0) + tox.callback_group_peer_join(group_peer_join(contacts_provider, groups_service), 0) + tox.callback_group_peer_exit(group_peer_exit(contacts_provider, groups_service, contacts_manager), 0) + tox.callback_group_peer_name(group_peer_name(contacts_provider, groups_service), 0) + tox.callback_group_peer_status(group_peer_status(contacts_provider, groups_service), 0) + tox.callback_group_topic(group_topic(contacts_provider), 0) + tox.callback_group_moderation(group_moderation(groups_service, contacts_provider, contacts_manager, messenger), 0) + tox.callback_group_password(group_password(contacts_provider), 0) + tox.callback_group_peer_limit(group_peer_limit(contacts_provider), 0) + tox.callback_group_privacy_state(group_privacy_state(contacts_provider), 0) diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py new file mode 100644 index 0000000..5f9404b --- /dev/null +++ b/toxygen/middleware/threads.py @@ -0,0 +1,172 @@ +from bootstrap.bootstrap import * +import threading +import queue +from utils import util +import time +from PyQt5 import QtCore + + +# ----------------------------------------------------------------------------------------------------------------- +# Base threads +# ----------------------------------------------------------------------------------------------------------------- + +class BaseThread(threading.Thread): + + def __init__(self): + super().__init__() + self._stop_thread = False + + def stop_thread(self): + self._stop_thread = True + self.join() + + +class BaseQThread(QtCore.QThread): + + def __init__(self): + super().__init__() + self._stop_thread = False + + def stop_thread(self): + self._stop_thread = True + self.wait() + + +# ----------------------------------------------------------------------------------------------------------------- +# Toxcore threads +# ----------------------------------------------------------------------------------------------------------------- + +class InitThread(BaseThread): + + def __init__(self, tox, plugin_loader, settings, is_first_start): + super().__init__() + self._tox, self._plugin_loader, self._settings = tox, plugin_loader, settings + self._is_first_start = is_first_start + + def run(self): + if self._is_first_start: + # download list of nodes if needed + download_nodes_list(self._settings) + # start plugins + self._plugin_loader.load() + + # bootstrap + try: + for data in generate_nodes(): + if self._stop_thread: + return + self._tox.bootstrap(*data) + self._tox.add_tcp_relay(*data) + except: + pass + + for _ in range(10): + if self._stop_thread: + return + time.sleep(1) + + while not self._tox.self_get_connection_status(): + try: + for data in generate_nodes(None): + if self._stop_thread: + return + self._tox.bootstrap(*data) + self._tox.add_tcp_relay(*data) + except: + pass + finally: + time.sleep(5) + + +class ToxIterateThread(BaseQThread): + + def __init__(self, tox): + super().__init__() + self._tox = tox + + def run(self): + while not self._stop_thread: + self._tox.iterate() + time.sleep(self._tox.iteration_interval() / 1000) + + +class ToxAVIterateThread(BaseQThread): + + def __init__(self, toxav): + super().__init__() + self._toxav = toxav + + def run(self): + while not self._stop_thread: + self._toxav.iterate() + time.sleep(self._toxav.iteration_interval() / 1000) + + +# ----------------------------------------------------------------------------------------------------------------- +# File transfers thread +# ----------------------------------------------------------------------------------------------------------------- + +class FileTransfersThread(BaseQThread): + + def __init__(self): + super().__init__() + self._queue = queue.Queue() + self._timeout = 0.01 + + def execute(self, func, *args, **kwargs): + self._queue.put((func, args, kwargs)) + + def run(self): + while not self._stop_thread: + try: + func, args, kwargs = self._queue.get(timeout=self._timeout) + func(*args, **kwargs) + except queue.Empty: + pass + except queue.Full: + util.log('Queue is full in _thread') + except Exception as ex: + util.log('Exception in _thread: ' + str(ex)) + + +_thread = FileTransfersThread() + + +def start_file_transfer_thread(): + _thread.start() + + +def stop_file_transfer_thread(): + _thread.stop_thread() + + +def execute(func, *args, **kwargs): + _thread.execute(func, *args, **kwargs) + + +# ----------------------------------------------------------------------------------------------------------------- +# Invoking in main thread +# ----------------------------------------------------------------------------------------------------------------- + +class InvokeEvent(QtCore.QEvent): + EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType()) + + def __init__(self, fn, *args, **kwargs): + QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE) + self.fn = fn + self.args = args + self.kwargs = kwargs + + +class Invoker(QtCore.QObject): + + def event(self, event): + event.fn(*event.args, **event.kwargs) + return True + + +_invoker = Invoker() + + +def invoke_in_main_thread(fn, *args, **kwargs): + QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs)) diff --git a/toxygen/middleware/tox_factory.py b/toxygen/middleware/tox_factory.py new file mode 100644 index 0000000..9ee5c01 --- /dev/null +++ b/toxygen/middleware/tox_factory.py @@ -0,0 +1,34 @@ +import user_data.settings +import wrapper.tox +import wrapper.toxcore_enums_and_consts as enums +import ctypes + + +def tox_factory(data=None, settings=None): + """ + :param data: user data from .tox file. None = no saved data, create new profile + :param settings: current profile settings. None = default settings will be used + :return: new tox instance + """ + if settings is None: + settings = user_data.settings.Settings.get_default_settings() + + tox_options = wrapper.tox.Tox.options_new() + tox_options.contents.udp_enabled = settings['udp_enabled'] + tox_options.contents.proxy_type = settings['proxy_type'] + tox_options.contents.proxy_host = bytes(settings['proxy_host'], 'UTF-8') + tox_options.contents.proxy_port = settings['proxy_port'] + tox_options.contents.start_port = settings['start_port'] + tox_options.contents.end_port = settings['end_port'] + tox_options.contents.tcp_port = settings['tcp_port'] + tox_options.contents.local_discovery_enabled = settings['lan_discovery'] + if data: # load existing profile + tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['TOX_SAVE'] + tox_options.contents.savedata_data = ctypes.c_char_p(data) + tox_options.contents.savedata_length = len(data) + else: # create new profile + tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['NONE'] + tox_options.contents.savedata_data = None + tox_options.contents.savedata_length = 0 + + return wrapper.tox.Tox(tox_options) diff --git a/toxygen/network/__init__.py b/toxygen/network/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/network/tox_dns.py b/toxygen/network/tox_dns.py new file mode 100644 index 0000000..02e97f5 --- /dev/null +++ b/toxygen/network/tox_dns.py @@ -0,0 +1,65 @@ +import json +import urllib.request +import utils.util as util +from PyQt5 import QtNetwork, QtCore + + +class ToxDns: + + def __init__(self, settings): + self._settings = settings + + @staticmethod + def _send_request(url, data): + req = urllib.request.Request(url) + req.add_header('Content-Type', 'application/json') + response = urllib.request.urlopen(req, bytes(json.dumps(data), 'utf-8')) + res = json.loads(str(response.read(), 'utf-8')) + if not res['c']: + return res['tox_id'] + else: + raise LookupError() + + def lookup(self, email): + """ + TOX DNS 4 + :param email: data like 'groupbot@toxme.io' + :return: tox id on success else None + """ + site = email.split('@')[1] + data = {"action": 3, "name": "{}".format(email)} + urls = ('https://{}/api'.format(site), 'http://{}/api'.format(site)) + if not self._settings['proxy_type']: # no proxy + for url in urls: + try: + return self._send_request(url, data) + except Exception as ex: + util.log('TOX DNS ERROR: ' + str(ex)) + else: # proxy + netman = QtNetwork.QNetworkAccessManager() + proxy = QtNetwork.QNetworkProxy() + if self._settings['proxy_type'] == 2: + proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy) + else: + proxy.setType(QtNetwork.QNetworkProxy.HttpProxy) + proxy.setHostName(self._settings['proxy_host']) + proxy.setPort(self._settings['proxy_port']) + netman.setProxy(proxy) + for url in urls: + try: + request = QtNetwork.QNetworkRequest() + request.setUrl(QtCore.QUrl(url)) + request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, "application/json") + reply = netman.post(request, bytes(json.dumps(data), 'utf-8')) + + while not reply.isFinished(): + QtCore.QThread.msleep(1) + QtCore.QCoreApplication.processEvents() + data = bytes(reply.readAll().data()) + result = json.loads(str(data, 'utf-8')) + if not result['c']: + return result['tox_id'] + except Exception as ex: + util.log('TOX DNS ERROR: ' + str(ex)) + + return None # error diff --git a/toxygen/notifications/__init__.py b/toxygen/notifications/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/notifications/sound.py b/toxygen/notifications/sound.py new file mode 100644 index 0000000..361cd05 --- /dev/null +++ b/toxygen/notifications/sound.py @@ -0,0 +1,54 @@ +import utils.util +import wave +import pyaudio +import os.path + + +SOUND_NOTIFICATION = { + 'MESSAGE': 0, + 'FRIEND_CONNECTION_STATUS': 1, + 'FILE_TRANSFER': 2 +} + + +class AudioFile: + chunk = 1024 + + def __init__(self, fl): + self.wf = wave.open(fl, 'rb') + self.p = pyaudio.PyAudio() + self.stream = self.p.open( + format=self.p.get_format_from_width(self.wf.getsampwidth()), + channels=self.wf.getnchannels(), + rate=self.wf.getframerate(), + output=True) + + def play(self): + data = self.wf.readframes(self.chunk) + while data: + self.stream.write(data) + data = self.wf.readframes(self.chunk) + + def close(self): + self.stream.close() + self.p.terminate() + + +def sound_notification(t): + """ + Plays sound notification + :param t: type of notification + """ + if t == SOUND_NOTIFICATION['MESSAGE']: + f = get_file_path('message.wav') + elif t == SOUND_NOTIFICATION['FILE_TRANSFER']: + f = get_file_path('file.wav') + else: + f = get_file_path('contact.wav') + a = AudioFile(f) + a.play() + a.close() + + +def get_file_path(file_name): + return os.path.join(utils.util.get_sounds_directory(), file_name) diff --git a/toxygen/notifications/tray.py b/toxygen/notifications/tray.py new file mode 100644 index 0000000..4232253 --- /dev/null +++ b/toxygen/notifications/tray.py @@ -0,0 +1,22 @@ +from PyQt5 import QtCore, QtWidgets + + +def tray_notification(title, text, tray, window): + """ + Show tray notification and activate window icon + NOTE: different behaviour on different OS + :param title: Name of user who sent message or file + :param text: text of message or file info + :param tray: ref to tray icon + :param window: main window + """ + if QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): + if len(text) > 30: + text = text[:27] + '...' + tray.showMessage(title, text, QtWidgets.QSystemTrayIcon.NoIcon, 3000) + QtWidgets.QApplication.alert(window, 0) + + def message_clicked(): + window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + window.activateWindow() + tray.messageClicked.connect(message_clicked) diff --git a/toxygen/plugin_support/__init__.py b/toxygen/plugin_support/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/plugin_support/plugin_support.py b/toxygen/plugin_support/plugin_support.py new file mode 100644 index 0000000..ed45910 --- /dev/null +++ b/toxygen/plugin_support/plugin_support.py @@ -0,0 +1,194 @@ +import utils.util as util +import os +import importlib +import inspect +import plugins.plugin_super_class as pl +import sys + + +class Plugin: + + def __init__(self, plugin, is_active): + self._instance = plugin + self._is_active = is_active + + def get_instance(self): + return self._instance + + instance = property(get_instance) + + def get_is_active(self): + return self._is_active + + def set_is_active(self, is_active): + self._is_active = is_active + + is_active = property(get_is_active, set_is_active) + + +class PluginLoader: + + def __init__(self, settings, app): + self._settings = settings + self._app = app + self._plugins = {} # dict. key - plugin unique short name, value - Plugin instance + + def set_tox(self, tox): + """ + New tox instance + """ + for plugin in self._plugins.values(): + plugin.instance.set_tox(tox) + + def load(self): + """ + Load all plugins in plugins folder + """ + path = util.get_plugins_directory() + if not os.path.exists(path): + util.log('Plugin dir not found') + return + else: + sys.path.append(path) + files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] + for fl in files: + if fl in ('plugin_super_class.py', '__init__.py') or not fl.endswith('.py'): + continue + name = fl[:-3] # module name without .py + try: + module = importlib.import_module(name) # import plugin + except ImportError: + util.log('Import error in module ' + name) + continue + except Exception as ex: + util.log('Exception in module ' + name + ' Exception: ' + str(ex)) + continue + for elem in dir(module): + obj = getattr(module, elem) + # looking for plugin class in module + if not inspect.isclass(obj) or not hasattr(obj, 'is_plugin') or not obj.is_plugin: + continue + print('Plugin', elem) + try: # create instance of plugin class + instance = obj(self._app) + is_active = instance.get_short_name() in self._settings['plugins'] + if is_active: + instance.start() + except Exception as ex: + util.log('Exception in module ' + name + ' Exception: ' + str(ex)) + continue + self._plugins[instance.get_short_name()] = Plugin(instance, is_active) + break + + def callback_lossless(self, friend_number, data): + """ + New incoming custom lossless packet (callback) + """ + l = data[0] - pl.LOSSLESS_FIRST_BYTE + name = ''.join(chr(x) for x in data[1:l + 1]) + if name in self._plugins and self._plugins[name].is_active: + self._plugins[name].instance.lossless_packet(''.join(chr(x) for x in data[l + 1:]), friend_number) + + def callback_lossy(self, friend_number, data): + """ + New incoming custom lossy packet (callback) + """ + l = data[0] - pl.LOSSY_FIRST_BYTE + name = ''.join(chr(x) for x in data[1:l + 1]) + if name in self._plugins and self._plugins[name].is_active: + self._plugins[name].instance.lossy_packet(''.join(chr(x) for x in data[l + 1:]), friend_number) + + def friend_online(self, friend_number): + """ + Friend with specified number is online + """ + for plugin in self._plugins.values(): + if plugin.is_active: + plugin.instance.friend_connected(friend_number) + + def get_plugins_list(self): + """ + Returns list of all plugins + """ + result = [] + for plugin in self._plugins.values(): + try: + result.append([plugin.instance.get_name(), # plugin full name + plugin.is_active, # is enabled + plugin.instance.get_description(), # plugin description + plugin.instance.get_short_name()]) # key - short unique name + except: + continue + + return result + + def plugin_window(self, key): + """ + Return window or None for specified plugin + """ + return self._plugins[key].instance.get_window() + + def toggle_plugin(self, key): + """ + Enable/disable plugin + :param key: plugin short name + """ + plugin = self._plugins[key] + if plugin.is_active: + plugin.instance.stop() + else: + plugin.instance.start() + plugin.is_active = not plugin.is_active + if plugin.is_active: + self._settings['plugins'].append(key) + else: + self._settings['plugins'].remove(key) + self._settings.save() + + def command(self, text): + """ + New command for plugin + """ + text = text.strip() + name = text.split()[0] + if name in self._plugins and self._plugins[name].is_active: + self._plugins[name].instance.command(text[len(name) + 1:]) + + def get_menu(self, num): + """ + Return list of items for menu + """ + result = [] + for plugin in self._plugins.values(): + if not plugin.is_active: + continue + try: + result.extend(plugin.instance.get_menu(num)) + except: + continue + return result + + def get_message_menu(self, menu, selected_text): + result = [] + for plugin in self._plugins.values(): + if not plugin.is_active: + continue + try: + result.extend(plugin.instance.get_message_menu(menu, selected_text)) + except: + pass + return result + + def stop(self): + """ + App is closing, stop all plugins + """ + for key in list(self._plugins.keys()): + if self._plugins[key].is_active: + self._plugins[key].instance.close() + del self._plugins[key] + + def reload(self): + print('Reloading plugins') + self.stop() + self.load() diff --git a/toxygen/plugins/plugin_super_class.py b/toxygen/plugins/plugin_super_class.py index c857c56..0056d36 100644 --- a/toxygen/plugins/plugin_super_class.py +++ b/toxygen/plugins/plugin_super_class.py @@ -1,5 +1,7 @@ import os from PyQt5 import QtCore, QtWidgets +import utils.ui as util_ui +import common.tox_save as tox_save MAX_SHORT_NAME_LENGTH = 5 @@ -26,25 +28,22 @@ def log(name, data): fl.write(str(data) + '\n') -class PluginSuperClass: +class PluginSuperClass(tox_save.ToxSave): """ Superclass for all plugins. Plugin is Python3 module with at least one class derived from PluginSuperClass. """ is_plugin = True - def __init__(self, name, short_name, tox=None, profile=None, settings=None, encrypt_save=None): + def __init__(self, name, short_name, app): """ - Constructor. In plugin __init__ should take only 4 last arguments + Constructor. In plugin __init__ should take only 1 last argument :param name: plugin full name :param short_name: plugin unique short name (length of short name should not exceed MAX_SHORT_NAME_LENGTH) - :param tox: tox instance - :param profile: profile instance - :param settings: profile settings - :param encrypt_save: ToxES instance. + :param app: App instance """ - self._settings = settings - self._profile = profile - self._tox = tox + tox = getattr(app, '_tox') + super().__init__(tox) + self._settings = getattr(app, '_settings') name = name.strip() short_name = short_name.strip() if not name or not short_name: @@ -52,7 +51,6 @@ class PluginSuperClass: self._name = name self._short_name = short_name[:MAX_SHORT_NAME_LENGTH] self._translator = None # translator for plugin's GUI - self._encrypt_save = encrypt_save # ----------------------------------------------------------------------------------------------------------------- # Get methods @@ -76,12 +74,11 @@ class PluginSuperClass: """ return self.__doc__ - def get_menu(self, menu, row_number): + def get_menu(self, row_number): """ This method creates items for menu which called on right click in list of friends - :param menu: menu instance :param row_number: number of selected row in list of contacts - :return list of QAction's + :return list of tuples (text, handler) """ return [] @@ -100,12 +97,6 @@ class PluginSuperClass: """ return None - def set_tox(self, tox): - """ - New tox instance - """ - self._tox = tox - # ----------------------------------------------------------------------------------------------------------------- # Plugin was stopped, started or new command received # ----------------------------------------------------------------------------------------------------------------- @@ -134,11 +125,9 @@ class PluginSuperClass: :param command: string with command """ if command == 'help': - msgbox = QtWidgets.QMessageBox() - title = QtWidgets.QApplication.translate("PluginWindow", "List of commands for plugin {}") - msgbox.setWindowTitle(title.format(self._name)) - msgbox.setText(QtWidgets.QApplication.translate("PluginWindow", "No commands available")) - msgbox.exec_() + text = util_ui.tr('No commands available') + title = util_ui.tr('List of commands for plugin {}').format(self._name) + util_ui.message_box(text, title) # ----------------------------------------------------------------------------------------------------------------- # Translations support diff --git a/toxygen/styles/dark_style.qss b/toxygen/styles/dark_style.qss index 0216f23..ece5ec3 100644 --- a/toxygen/styles/dark_style.qss +++ b/toxygen/styles/dark_style.qss @@ -1207,12 +1207,12 @@ MessageItem border: none; } -MessageEdit +MessageBrowser { border: none; } -MessageEdit::focus +MessageBrowser::focus { border: none; } @@ -1222,7 +1222,7 @@ MessageItem::focus border: none; } -MessageEdit:hover +MessageBrowser:hover { border: none; } @@ -1243,7 +1243,7 @@ QPushButton:hover background-color: #1E90FF; } -MessageEdit +MessageBrowser { background-color: transparent; } @@ -1253,7 +1253,7 @@ MessageEdit background-color: #1E90FF; } -#friends_list:item:selected +#friendsListWidget:item:selected { background-color: #333333; } @@ -1277,7 +1277,7 @@ QListWidget > QLabel color: #A9A9A9; } -#contact_name +#searchLineEdit { padding-left: 22px; } @@ -1322,3 +1322,14 @@ ClickableLabel:hover { background-color: #4A4949; } + +#warningLabel +{ + color: #BC1C1C; +} + +#groupInvitesPushButton +{ + background-color: #009c00; +} + diff --git a/toxygen/styles/style.qss b/toxygen/styles/style.qss index 26fbaf2..ff9f614 100644 --- a/toxygen/styles/style.qss +++ b/toxygen/styles/style.qss @@ -1,4 +1,4 @@ -#contact_name +#searchLineEdit { padding-left: 22px; } @@ -27,3 +27,14 @@ MessageEdit { background-color: transparent; } + +#warningLabel +{ + color: #BC1C1C; +} + +#groupInvitesPushButton +{ + background-color: #009c00; +} + diff --git a/toxygen/ui/__init__.py b/toxygen/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/ui/av_widgets.py b/toxygen/ui/av_widgets.py new file mode 100644 index 0000000..e5773a8 --- /dev/null +++ b/toxygen/ui/av_widgets.py @@ -0,0 +1,130 @@ +from PyQt5 import QtCore, QtGui, QtWidgets +from ui import widgets +import utils.util as util +import pyaudio +import wave + + +class IncomingCallWidget(widgets.CenteredWidget): + + def __init__(self, settings, calls_manager, friend_number, text, name): + super().__init__() + self._settings = settings + self._calls_manager = calls_manager + self.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowStaysOnTopHint) + self.resize(QtCore.QSize(500, 270)) + self.avatar_label = QtWidgets.QLabel(self) + self.avatar_label.setGeometry(QtCore.QRect(10, 20, 64, 64)) + self.avatar_label.setScaledContents(False) + self.name = widgets.DataLabel(self) + self.name.setGeometry(QtCore.QRect(90, 20, 300, 25)) + self._friend_number = friend_number + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPointSize(16) + font.setBold(True) + self.name.setFont(font) + self.call_type = widgets.DataLabel(self) + self.call_type.setGeometry(QtCore.QRect(90, 55, 300, 25)) + self.call_type.setFont(font) + self.accept_audio = QtWidgets.QPushButton(self) + self.accept_audio.setGeometry(QtCore.QRect(20, 100, 150, 150)) + self.accept_video = QtWidgets.QPushButton(self) + self.accept_video.setGeometry(QtCore.QRect(170, 100, 150, 150)) + self.decline = QtWidgets.QPushButton(self) + self.decline.setGeometry(QtCore.QRect(320, 100, 150, 150)) + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'accept_audio.png')) + icon = QtGui.QIcon(pixmap) + self.accept_audio.setIcon(icon) + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'accept_video.png')) + icon = QtGui.QIcon(pixmap) + self.accept_video.setIcon(icon) + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'decline_call.png')) + icon = QtGui.QIcon(pixmap) + self.decline.setIcon(icon) + self.accept_audio.setIconSize(QtCore.QSize(150, 150)) + self.accept_video.setIconSize(QtCore.QSize(140, 140)) + self.decline.setIconSize(QtCore.QSize(140, 140)) + self.accept_audio.setStyleSheet("QPushButton { border: none }") + self.accept_video.setStyleSheet("QPushButton { border: none }") + self.decline.setStyleSheet("QPushButton { border: none }") + self.setWindowTitle(text) + self.name.setText(name) + self.call_type.setText(text) + self._processing = False + self.accept_audio.clicked.connect(self.accept_call_with_audio) + self.accept_video.clicked.connect(self.accept_call_with_video) + self.decline.clicked.connect(self.decline_call) + + class SoundPlay(QtCore.QThread): + + def __init__(self): + QtCore.QThread.__init__(self) + self.a = None + + def run(self): + class AudioFile: + chunk = 1024 + + def __init__(self, fl): + self.stop = False + self.fl = fl + self.wf = wave.open(self.fl, 'rb') + self.p = pyaudio.PyAudio() + self.stream = self.p.open( + format=self.p.get_format_from_width(self.wf.getsampwidth()), + channels=self.wf.getnchannels(), + rate=self.wf.getframerate(), + output=True) + + def play(self): + while not self.stop: + data = self.wf.readframes(self.chunk) + while data and not self.stop: + self.stream.write(data) + data = self.wf.readframes(self.chunk) + self.wf = wave.open(self.fl, 'rb') + + def close(self): + self.stream.close() + self.p.terminate() + + self.a = AudioFile(util.join_path(util.get_sounds_directory(), 'call.wav')) + self.a.play() + self.a.close() + + if self._settings['calls_sound']: + self.thread = SoundPlay() + self.thread.start() + else: + self.thread = None + + def stop(self): + if self.thread is not None: + self.thread.a.stop = True + self.thread.wait() + self.close() + + def accept_call_with_audio(self): + if self._processing: + return + self._processing = True + self._calls_manager.accept_call(self._friend_number, True, False) + self.stop() + + def accept_call_with_video(self): + if self._processing: + return + self._processing = True + self._calls_manager.accept_call(self._friend_number, True, True) + self.stop() + + def decline_call(self): + if self._processing: + return + self._processing = True + self._calls_manager.stop_call(self._friend_number, False) + self.stop() + + def set_pixmap(self, pixmap): + self.avatar_label.setPixmap(pixmap) diff --git a/toxygen/ui/contact_items.py b/toxygen/ui/contact_items.py new file mode 100644 index 0000000..7a32284 --- /dev/null +++ b/toxygen/ui/contact_items.py @@ -0,0 +1,97 @@ +from wrapper.toxcore_enums_and_consts import * +from PyQt5 import QtCore, QtGui, QtWidgets +from utils.util import * +from ui.widgets import DataLabel + + +class ContactItem(QtWidgets.QWidget): + """ + Contact in friends list + """ + + def __init__(self, settings, parent=None): + QtWidgets.QWidget.__init__(self, parent) + mode = settings['compact_mode'] + self.setBaseSize(QtCore.QSize(250, 40 if mode else 70)) + self.avatar_label = QtWidgets.QLabel(self) + size = 32 if mode else 64 + self.avatar_label.setGeometry(QtCore.QRect(3, 4, size, size)) + self.avatar_label.setScaledContents(False) + self.avatar_label.setAlignment(QtCore.Qt.AlignCenter) + self.name = DataLabel(self) + self.name.setGeometry(QtCore.QRect(50 if mode else 75, 3 if mode else 10, 150, 15 if mode else 25)) + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPointSize(10 if mode else 12) + font.setBold(True) + self.name.setFont(font) + self.status_message = DataLabel(self) + self.status_message.setGeometry(QtCore.QRect(50 if mode else 75, 20 if mode else 30, 170, 15 if mode else 20)) + font.setPointSize(10) + font.setBold(False) + self.status_message.setFont(font) + self.connection_status = StatusCircle(self) + self.connection_status.setGeometry(QtCore.QRect(230, -2 if mode else 5, 32, 32)) + self.messages = UnreadMessagesCount(settings, self) + self.messages.setGeometry(QtCore.QRect(20 if mode else 52, 20 if mode else 50, 30, 20)) + + +class StatusCircle(QtWidgets.QWidget): + """ + Connection status + """ + def __init__(self, parent): + QtWidgets.QWidget.__init__(self, parent) + self.setGeometry(0, 0, 32, 32) + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(0, 0, 32, 32)) + self.unread = False + + def update(self, status, unread_messages=None): + if unread_messages is None: + unread_messages = self.unread + else: + self.unread = unread_messages + if status == TOX_USER_STATUS['NONE']: + name = 'online' + elif status == TOX_USER_STATUS['AWAY']: + name = 'idle' + elif status == TOX_USER_STATUS['BUSY']: + name = 'busy' + else: + name = 'offline' + if unread_messages: + name += '_notification' + self.label.setGeometry(QtCore.QRect(0, 0, 32, 32)) + else: + self.label.setGeometry(QtCore.QRect(2, 0, 32, 32)) + pixmap = QtGui.QPixmap(join_path(get_images_directory(), '{}.png'.format(name))) + self.label.setPixmap(pixmap) + + +class UnreadMessagesCount(QtWidgets.QWidget): + + def __init__(self, settings, parent=None): + super().__init__(parent) + self._settings = settings + self.resize(30, 20) + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(0, 0, 30, 20)) + self.label.setVisible(False) + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPointSize(12) + font.setBold(True) + self.label.setFont(font) + self.label.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignCenter) + color = settings['unread_color'] + self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') + + def update(self, messages_count): + color = self._settings['unread_color'] + self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') + if messages_count: + self.label.setVisible(True) + self.label.setText(str(messages_count)) + else: + self.label.setVisible(False) diff --git a/toxygen/ui/create_profile_screen.py b/toxygen/ui/create_profile_screen.py new file mode 100644 index 0000000..512c141 --- /dev/null +++ b/toxygen/ui/create_profile_screen.py @@ -0,0 +1,52 @@ +from ui.widgets import * +from PyQt5 import uic +import utils.util as util +import utils.ui as util_ui + + +class CreateProfileScreenResult: + + def __init__(self, save_into_default_folder, password): + self._save_into_default_folder = save_into_default_folder + self._password = password + + def get_save_into_default_folder(self): + return self._save_into_default_folder + + save_into_default_folder = property(get_save_into_default_folder) + + def get_password(self): + return self._password + + password = property(get_password) + + +class CreateProfileScreen(CenteredWidget, DialogWithResult): + + def __init__(self): + CenteredWidget.__init__(self) + DialogWithResult.__init__(self) + uic.loadUi(util.get_views_path('create_profile_screen'), self) + self.center() + self.createProfile.clicked.connect(self._create_profile) + self._retranslate_ui() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('New profile settings')) + self.defaultFolder.setText(util_ui.tr('Save in default folder')) + self.programFolder.setText(util_ui.tr('Save in program folder')) + self.password.setPlaceholderText(util_ui.tr('Password')) + self.confirmPassword.setPlaceholderText(util_ui.tr('Confirm password')) + self.createProfile.setText(util_ui.tr('Create profile')) + self.passwordLabel.setText(util_ui.tr('Password (at least 8 symbols):')) + + def _create_profile(self): + password = self.password.text() + if password != self.confirmPassword.text(): + self.errorLabel.setText(util_ui.tr('Passwords do not match')) + return + if 0 < len(password) < 8: + self.errorLabel.setText(util_ui.tr('Password must be at least 8 symbols')) + return + result = CreateProfileScreenResult(self.defaultFolder.isChecked(), password) + self.close_with_result(result) diff --git a/toxygen/ui/group_bans_widgets.py b/toxygen/ui/group_bans_widgets.py new file mode 100644 index 0000000..b2758c7 --- /dev/null +++ b/toxygen/ui/group_bans_widgets.py @@ -0,0 +1,68 @@ +from ui.widgets import CenteredWidget +from PyQt5 import uic, QtWidgets, QtCore +import utils.util as util +import utils.ui as util_ui + + +class GroupBanItem(QtWidgets.QWidget): + + def __init__(self, ban, cancel_ban, can_cancel_ban, parent=None): + super().__init__(parent) + self._ban = ban + self._cancel_ban = cancel_ban + self._can_cancel_ban = can_cancel_ban + + uic.loadUi(util.get_views_path('gc_ban_item'), self) + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self.banTargetLabel.setText(self._ban.ban_target) + ban_time = self._ban.ban_time + self.banTimeLabel.setText(util.unix_time_to_long_str(ban_time)) + + self.cancelPushButton.clicked.connect(self._cancel_ban) + self.cancelPushButton.setEnabled(self._can_cancel_ban) + + def _retranslate_ui(self): + self.cancelPushButton.setText(util_ui.tr('Cancel ban')) + + def _cancel_ban(self): + self._cancel_ban(self._ban.ban_id) + + +class GroupBansScreen(CenteredWidget): + + def __init__(self, groups_service, group): + super().__init__() + self._groups_service = groups_service + self._group = group + + uic.loadUi(util.get_views_path('bans_list_screen'), self) + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self._refresh_bans_list() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Bans list for group "{}"').format(self._group.name)) + + def _refresh_bans_list(self): + self.bansListWidget.clear() + can_cancel_ban = self._group.is_self_moderator_or_founder() + for ban in self._group.bans: + self._create_ban_item(ban, can_cancel_ban) + + def _create_ban_item(self, ban, can_cancel_ban): + item = GroupBanItem(ban, self._on_ban_cancelled, can_cancel_ban, self.bansListWidget) + elem = QtWidgets.QListWidgetItem() + elem.setSizeHint(QtCore.QSize(item.width(), item.height())) + self.bansListWidget.addItem(elem) + self.bansListWidget.setItemWidget(elem, item) + + def _on_ban_cancelled(self, ban_id): + self._groups_service.cancel_ban(self._group.number, ban_id) + self._refresh_bans_list() diff --git a/toxygen/ui/group_invites_widgets.py b/toxygen/ui/group_invites_widgets.py new file mode 100644 index 0000000..d35aca1 --- /dev/null +++ b/toxygen/ui/group_invites_widgets.py @@ -0,0 +1,127 @@ +from PyQt5 import uic, QtWidgets +import utils.util as util +from ui.widgets import * + + +class GroupInviteItem(QtWidgets.QWidget): + + def __init__(self, parent, chat_name, avatar, friend_name): + super().__init__(parent) + uic.loadUi(util.get_views_path('gc_invite_item'), self) + + self.groupNameLabel.setText(chat_name) + self.friendNameLabel.setText(friend_name) + self.friendAvatarLabel.setPixmap(avatar) + + def is_selected(self): + return self.selectCheckBox.isChecked() + + def subscribe_checked_event(self, callback): + self.selectCheckBox.clicked.connect(callback) + + +class GroupInvitesScreen(CenteredWidget): + + def __init__(self, groups_service, profile, contacts_provider): + super().__init__() + self._groups_service = groups_service + self._profile = profile + self._contacts_provider = contacts_provider + + uic.loadUi(util.get_views_path('group_invites_screen'), self) + + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self._refresh_invites_list() + + self.nickLineEdit.setText(self._profile.name) + self.statusComboBox.setCurrentIndex(self._profile.status or 0) + + self.nickLineEdit.textChanged.connect(self._nick_changed) + self.acceptPushButton.clicked.connect(self._accept_invites) + self.declinePushButton.clicked.connect(self._decline_invites) + + self.invitesListWidget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.invitesListWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) + + self._update_buttons_state() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Group chat invites')) + self.noInvitesLabel.setText(util_ui.tr('No group invites found')) + self.acceptPushButton.setText(util_ui.tr('Accept')) + self.declinePushButton.setText(util_ui.tr('Decline')) + self.statusComboBox.addItem(util_ui.tr('Online')) + self.statusComboBox.addItem(util_ui.tr('Away')) + self.statusComboBox.addItem(util_ui.tr('Busy')) + self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat')) + self.passwordLineEdit.setPlaceholderText(util_ui.tr('Optional password')) + + def _get_friend(self, public_key): + return self._contacts_provider.get_friend_by_public_key(public_key) + + def _accept_invites(self): + nick = self.nickLineEdit.text() + password = self.passwordLineEdit.text() + status = self.statusComboBox.currentIndex() + + selected_invites = self._get_selected_invites() + for invite in selected_invites: + self._groups_service.accept_group_invite(invite, nick, status, password) + + self._refresh_invites_list() + self._close_window_if_needed() + + def _decline_invites(self): + selected_invites = self._get_selected_invites() + for invite in selected_invites: + self._groups_service.decline_group_invite(invite) + + self._refresh_invites_list() + self._close_window_if_needed() + + def _get_selected_invites(self): + all_invites = self._groups_service.get_group_invites() + selected = [] + items_count = len(all_invites) + for index in range(items_count): + list_item = self.invitesListWidget.item(index) + item_widget = self.invitesListWidget.itemWidget(list_item) + if item_widget.is_selected(): + selected.append(all_invites[index]) + + return selected + + def _refresh_invites_list(self): + self.invitesListWidget.clear() + invites = self._groups_service.get_group_invites() + for invite in invites: + self._create_invite_item(invite) + + def _create_invite_item(self, invite): + friend = self._get_friend(invite.friend_public_key) + item = GroupInviteItem(self.invitesListWidget, invite.chat_name, friend.get_pixmap(), friend.name) + item.subscribe_checked_event(self._item_selected) + elem = QtWidgets.QListWidgetItem() + elem.setSizeHint(QtCore.QSize(item.width(), item.height())) + self.invitesListWidget.addItem(elem) + self.invitesListWidget.setItemWidget(elem, item) + + def _item_selected(self): + self._update_buttons_state() + + def _nick_changed(self): + self._update_buttons_state() + + def _update_buttons_state(self): + nick = self.nickLineEdit.text() + selected_items = self._get_selected_invites() + self.acceptPushButton.setEnabled(bool(nick) and len(selected_items)) + self.declinePushButton.setEnabled(len(selected_items) > 0) + + def _close_window_if_needed(self): + if self._groups_service.group_invites_count == 0: + self.close() diff --git a/toxygen/ui/group_peers_list.py b/toxygen/ui/group_peers_list.py new file mode 100644 index 0000000..9d2632d --- /dev/null +++ b/toxygen/ui/group_peers_list.py @@ -0,0 +1,33 @@ +from ui.widgets import * +from wrapper.toxcore_enums_and_consts import * + + +class PeerItem(QtWidgets.QWidget): + + def __init__(self, peer, handler, width, parent=None): + super().__init__(parent) + self.resize(QtCore.QSize(width, 34)) + self.nameLabel = DataLabel(self) + self.nameLabel.setGeometry(5, 0, width - 5, 34) + name = peer.name + if peer.is_current_user: + name += util_ui.tr(' (You)') + self.nameLabel.setText(name) + if peer.status == TOX_USER_STATUS['NONE']: + style = 'QLabel {color: green}' + elif peer.status == TOX_USER_STATUS['AWAY']: + style = 'QLabel {color: yellow}' + else: + style = 'QLabel {color: red}' + self.nameLabel.setStyleSheet(style) + self.nameLabel.mousePressEvent = lambda x: handler(peer.id) + + +class PeerTypeItem(QtWidgets.QWidget): + + def __init__(self, text, width, parent=None): + super().__init__(parent) + self.resize(QtCore.QSize(width, 34)) + self.nameLabel = DataLabel(self) + self.nameLabel.setGeometry(5, 0, width - 5, 34) + self.nameLabel.setText(text) diff --git a/toxygen/ui/group_settings_widgets.py b/toxygen/ui/group_settings_widgets.py new file mode 100644 index 0000000..c32168b --- /dev/null +++ b/toxygen/ui/group_settings_widgets.py @@ -0,0 +1,77 @@ +from ui.widgets import CenteredWidget +from PyQt5 import uic +import utils.util as util +import utils.ui as util_ui + + +class GroupManagementScreen(CenteredWidget): + + def __init__(self, groups_service, group): + super().__init__() + self._groups_service = groups_service + self._group = group + + uic.loadUi(util.get_views_path('group_management_screen'), self) + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self.passwordLineEdit.setText(self._group.password) + self.privacyStateComboBox.setCurrentIndex(1 if self._group.is_private else 0) + self.peersLimitSpinBox.setValue(self._group.peers_limit) + + self.savePushButton.clicked.connect(self._save) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Group "{}"').format(self._group.name)) + self.passwordLabel.setText(util_ui.tr('Password:')) + self.peerLimitLabel.setText(util_ui.tr('Peer limit:')) + self.privacyStateLabel.setText(util_ui.tr('Privacy state:')) + self.savePushButton.setText(util_ui.tr('Save')) + + self.privacyStateComboBox.clear() + self.privacyStateComboBox.addItem(util_ui.tr('Public')) + self.privacyStateComboBox.addItem(util_ui.tr('Private')) + + def _save(self): + password = self.passwordLineEdit.text() + privacy_state = self.privacyStateComboBox.currentIndex() + peers_limit = self.peersLimitSpinBox.value() + + self._groups_service.set_group_password(self._group, password) + self._groups_service.set_group_privacy_state(self._group, privacy_state) + self._groups_service.set_group_peers_limit(self._group, peers_limit) + + self.close() + + +class GroupSettingsScreen(CenteredWidget): + + def __init__(self, group): + super().__init__() + self._group = group + + uic.loadUi(util.get_views_path('gc_settings_screen'), self) + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self.copyPasswordPushButton.clicked.connect(self._copy_password) + self.copyPasswordPushButton.setEnabled(bool(self._group.password)) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Group "{}"').format(self._group.name)) + if self._group.password: + password_label_text = '{} {}'.format(util_ui.tr('Password:'), self._group.password) + else: + password_label_text = util_ui.tr('Password is not set') + self.passwordLabel.setText(password_label_text) + self.peerLimitLabel.setText('{} {}'.format(util_ui.tr('Peer limit:'), self._group.peers_limit)) + privacy_state = util_ui.tr('Private') if self._group.is_private else util_ui.tr('Public') + self.privacyStateLabel.setText('{} {}'.format(util_ui.tr('Privacy state:'), privacy_state)) + self.copyPasswordPushButton.setText(util_ui.tr('Copy password')) + + def _copy_password(self): + util_ui.copy_to_clipboard(self._group.password) diff --git a/toxygen/ui/groups_widgets.py b/toxygen/ui/groups_widgets.py new file mode 100644 index 0000000..ad4b703 --- /dev/null +++ b/toxygen/ui/groups_widgets.py @@ -0,0 +1,123 @@ +from PyQt5 import uic +import utils.util as util +from ui.widgets import * +from wrapper.toxcore_enums_and_consts import * + + +class BaseGroupScreen(CenteredWidget): + + def __init__(self, groups_service, profile): + super().__init__() + self._groups_service = groups_service + self._profile = profile + + def _retranslate_ui(self): + self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat')) + self.nickLabel.setText(util_ui.tr('Nickname:')) + self.statusLabel.setText(util_ui.tr('Status:')) + self.statusComboBox.addItem(util_ui.tr('Online')) + self.statusComboBox.addItem(util_ui.tr('Away')) + self.statusComboBox.addItem(util_ui.tr('Busy')) + + +class CreateGroupScreen(BaseGroupScreen): + + def __init__(self, groups_service, profile): + super().__init__(groups_service, profile) + uic.loadUi(util.get_views_path('create_group_screen'), self) + self.center() + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self.statusComboBox.setCurrentIndex(self._profile.status or 0) + self.nickLineEdit.setText(self._profile.name) + + self.addGroupButton.clicked.connect(self._create_group) + self.groupNameLineEdit.textChanged.connect(self._group_name_changed) + self.nickLineEdit.textChanged.connect(self._nick_changed) + + def _retranslate_ui(self): + super()._retranslate_ui() + self.setWindowTitle(util_ui.tr('Create new group chat')) + self.groupNameLabel.setText(util_ui.tr('Group name:')) + self.groupTypeLabel.setText(util_ui.tr('Group type:')) + self.groupNameLineEdit.setPlaceholderText(util_ui.tr('Group\'s persistent name')) + self.addGroupButton.setText(util_ui.tr('Create group')) + self.groupTypeComboBox.addItem(util_ui.tr('Public')) + self.groupTypeComboBox.addItem(util_ui.tr('Private')) + self.groupTypeComboBox.setCurrentIndex(1) + + def _create_group(self): + group_name = self.groupNameLineEdit.text() + privacy_state = self.groupTypeComboBox.currentIndex() + nick = self.nickLineEdit.text() + status = self.statusComboBox.currentIndex() + self._groups_service.create_new_gc(group_name, privacy_state, nick, status) + self.close() + + def _nick_changed(self): + self._update_button_state() + + def _group_name_changed(self): + self._update_button_state() + + def _update_button_state(self): + is_nick_set = bool(self.nickLineEdit.text()) + is_group_name_set = bool(self.groupNameLineEdit.text()) + self.addGroupButton.setEnabled(is_nick_set and is_group_name_set) + + +class JoinGroupScreen(BaseGroupScreen): + + def __init__(self, groups_service, profile): + super().__init__(groups_service, profile) + uic.loadUi(util.get_views_path('join_group_screen'), self) + self.center() + self._update_ui() + + def _update_ui(self): + self._retranslate_ui() + + self.statusComboBox.setCurrentIndex(self._profile.status or 0) + self.nickLineEdit.setText(self._profile.name) + + self.chatIdLineEdit.textChanged.connect(self._chat_id_changed) + self.joinGroupButton.clicked.connect(self._join_group) + self.nickLineEdit.textChanged.connect(self._nick_changed) + + def _retranslate_ui(self): + super()._retranslate_ui() + self.setWindowTitle(util_ui.tr('Join public group chat')) + self.chatIdLabel.setText(util_ui.tr('Group ID:')) + self.passwordLabel.setText(util_ui.tr('Password:')) + self.chatIdLineEdit.setPlaceholderText(util_ui.tr('Group\'s chat ID')) + self.joinGroupButton.setText(util_ui.tr('Join group')) + self.passwordLineEdit.setPlaceholderText(util_ui.tr('Optional password')) + + def _chat_id_changed(self): + self._update_button_state() + + def _nick_changed(self): + self._update_button_state() + + def _update_button_state(self): + chat_id = self._get_chat_id() + is_nick_set = bool(self.nickLineEdit.text()) + self.joinGroupButton.setEnabled(len(chat_id) == TOX_GROUP_CHAT_ID_SIZE * 2 and is_nick_set) + + def _join_group(self): + chat_id = self._get_chat_id() + password = self.passwordLineEdit.text() + nick = self.nickLineEdit.text() + status = self.statusComboBox.currentIndex() + self._groups_service.join_gc_by_id(chat_id, password, nick, status) + self.close() + + def _get_chat_id(self): + chat_id = self.chatIdLineEdit.text().strip() + if chat_id.startswith('tox:'): + chat_id = chat_id[4:] + + return chat_id diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py new file mode 100644 index 0000000..7346f8f --- /dev/null +++ b/toxygen/ui/items_factories.py @@ -0,0 +1,90 @@ +from ui.contact_items import * +from ui.messages_widgets import * + + +class ContactItemsFactory: + + def __init__(self, settings, main_screen): + self._settings = settings + self._friends_list = main_screen.friends_list + + def create_contact_item(self): + item = ContactItem(self._settings) + elem = QtWidgets.QListWidgetItem(self._friends_list) + elem.setSizeHint(QtCore.QSize(250, 40 if self._settings['compact_mode'] else 70)) + self._friends_list.addItem(elem) + self._friends_list.setItemWidget(elem, item) + + return item + + +class MessagesItemsFactory: + + def __init__(self, settings, plugin_loader, smiley_loader, main_screen, delete_action): + self._file_transfers_handler = None + self._settings, self._plugin_loader = settings, plugin_loader + self._smiley_loader, self._delete_action = smiley_loader, delete_action + self._messages = main_screen.messages + self._message_edit = main_screen.messageEdit + + def set_file_transfers_handler(self, file_transfers_handler): + self._file_transfers_handler = file_transfers_handler + + def create_message_item(self, message, append=True, pixmap=None): + item = message.get_widget(self._settings, self._create_message_browser, + self._delete_action, self._messages) + if pixmap is not None: + item.set_avatar(pixmap) + elem = QtWidgets.QListWidgetItem() + elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) + if append: + self._messages.addItem(elem) + else: + self._messages.insertItem(0, elem) + self._messages.setItemWidget(elem, item) + + return item + + def create_inline_item(self, message, append=True, position=0): + elem = QtWidgets.QListWidgetItem() + item = InlineImageItem(message.data, self._messages.width(), elem, self._messages) + elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) + if append: + self._messages.addItem(elem) + else: + self._messages.insertItem(position, elem) + self._messages.setItemWidget(elem, item) + + return item + + def create_unsent_file_item(self, message, append=True): + item = message.get_widget(self._file_transfers_handler, self._settings, self._messages.width(), self._messages) + elem = QtWidgets.QListWidgetItem() + elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) + if append: + self._messages.addItem(elem) + else: + self._messages.insertItem(0, elem) + self._messages.setItemWidget(elem, item) + + return item + + def create_file_transfer_item(self, message, append=True): + item = message.get_widget(self._file_transfers_handler, self._settings, self._messages.width(), self._messages) + elem = QtWidgets.QListWidgetItem() + elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) + if append: + self._messages.addItem(elem) + else: + self._messages.insertItem(0, elem) + self._messages.setItemWidget(elem, item) + + return item + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _create_message_browser(self, text, width, message_type, parent=None): + return MessageBrowser(self._settings, self._message_edit, self._smiley_loader, self._plugin_loader, + text, width, message_type, parent) diff --git a/toxygen/ui/login_screen.py b/toxygen/ui/login_screen.py new file mode 100644 index 0000000..35e33b5 --- /dev/null +++ b/toxygen/ui/login_screen.py @@ -0,0 +1,77 @@ +from ui.widgets import * +from PyQt5 import uic +import utils.util as util +import utils.ui as util_ui +import os.path + + +class LoginScreenResult: + + def __init__(self, profile_path, load_as_default, password=None): + self._profile_path = profile_path + self._load_as_default = load_as_default + self._password = password + + def get_profile_path(self): + return self._profile_path + + profile_path = property(get_profile_path) + + def get_load_as_default(self): + return self._load_as_default + + load_as_default = property(get_load_as_default) + + def get_password(self): + return self._password + + password = property(get_password) + + def is_new_profile(self): + return not os.path.isfile(self._profile_path) + + +class LoginScreen(CenteredWidget, DialogWithResult): + + def __init__(self): + CenteredWidget.__init__(self) + DialogWithResult.__init__(self) + uic.loadUi(util.get_views_path('login_screen'), self) + self.center() + self._profiles = [] + self._update_ui() + + def update_select(self, profiles): + profiles = sorted(profiles, key=lambda p: p[1]) + self._profiles = list(profiles) + self.profilesComboBox.addItems(list(map(lambda p: p[1], profiles))) + self.loadProfilePushButton.setEnabled(len(profiles) > 0) + + def _update_ui(self): + self.profileNameLineEdit = LineEditWithEnterSupport(self._create_profile, self) + self.profileNameLineEdit.setGeometry(QtCore.QRect(20, 100, 160, 30)) + self._retranslate_ui() + self.createProfilePushButton.clicked.connect(self._create_profile) + self.loadProfilePushButton.clicked.connect(self._load_existing_profile) + + def _create_profile(self): + path = self.profileNameLineEdit.text() + load_as_default = self.defaultProfileCheckBox.isChecked() + result = LoginScreenResult(path, load_as_default) + self.close_with_result(result) + + def _load_existing_profile(self): + index = self.profilesComboBox.currentIndex() + load_as_default = self.defaultProfileCheckBox.isChecked() + path = util.join_path(self._profiles[index][0], self._profiles[index][1] + '.tox') + result = LoginScreenResult(path, load_as_default) + self.close_with_result(result) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Log in')) + self.profileNameLineEdit.setPlaceholderText(util_ui.tr('Profile name')) + self.createProfilePushButton.setText(util_ui.tr('Create')) + self.loadProfilePushButton.setText(util_ui.tr('Load profile')) + self.defaultProfileCheckBox.setText(util_ui.tr('Use as default')) + self.existingProfileGroupBox.setTitle(util_ui.tr('Load existing profile')) + self.newProfileGroupBox.setTitle(util_ui.tr('Create new profile')) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py new file mode 100644 index 0000000..5a510a5 --- /dev/null +++ b/toxygen/ui/main_screen.py @@ -0,0 +1,718 @@ +from ui.contact_items import * +from ui.widgets import MultilineEdit +from ui.main_screen_widgets import * +import utils.util as util +import utils.ui as util_ui +from PyQt5 import uic + + +class MainWindow(QtWidgets.QMainWindow): + + def __init__(self, settings, tray): + super().__init__() + self._settings = settings + self._contacts_manager = None + self._tray = tray + self._widget_factory = None + self._modal_window = None + self._plugins_loader = None + self.setAcceptDrops(True) + self._saved = False + self._smiley_window = None + self._profile = self._toxes = self._messenger = None + self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None + self._should_show_group_peers_list = False + self.initUI() + + def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader, + file_transfer_handler, history_loader, calls_manager, groups_service, toxes): + self._widget_factory = widget_factory + self._tray = tray + self._contacts_manager = contacts_manager + self._profile = profile + self._plugins_loader = plugins_loader + self._file_transfer_handler = file_transfer_handler + self._history_loader = history_loader + self._calls_manager = calls_manager + self._groups_service = groups_service + self._toxes = toxes + self._messenger = messenger + self._contacts_manager.active_contact_changed.add_callback(self._new_contact_selected) + self.messageEdit.set_dependencies(messenger, contacts_manager, file_transfer_handler) + + self.update_gc_invites_button_state() + + def show(self): + super().show() + self._contacts_manager.update() + if self._settings['show_welcome_screen']: + self._modal_window = self._widget_factory.create_welcome_window() + + def setup_menu(self, window): + self.menubar = QtWidgets.QMenuBar(window) + self.menubar.setObjectName("menubar") + self.menubar.setNativeMenuBar(False) + self.menubar.setMinimumSize(self.width(), 25) + self.menubar.setMaximumSize(self.width(), 25) + self.menubar.setBaseSize(self.width(), 25) + self.menuProfile = QtWidgets.QMenu(self.menubar) + + self.menuProfile = QtWidgets.QMenu(self.menubar) + self.menuProfile.setObjectName("menuProfile") + self.menuGC = QtWidgets.QMenu(self.menubar) + self.menuSettings = QtWidgets.QMenu(self.menubar) + self.menuSettings.setObjectName("menuSettings") + self.menuPlugins = QtWidgets.QMenu(self.menubar) + self.menuPlugins.setObjectName("menuPlugins") + self.menuAbout = QtWidgets.QMenu(self.menubar) + self.menuAbout.setObjectName("menuAbout") + + self.actionAdd_friend = QtWidgets.QAction(window) + self.actionAdd_friend.setObjectName("actionAdd_friend") + self.actionprofilesettings = QtWidgets.QAction(window) + self.actionprofilesettings.setObjectName("actionprofilesettings") + self.actionPrivacy_settings = QtWidgets.QAction(window) + self.actionPrivacy_settings.setObjectName("actionPrivacy_settings") + self.actionInterface_settings = QtWidgets.QAction(window) + self.actionInterface_settings.setObjectName("actionInterface_settings") + self.actionNotifications = QtWidgets.QAction(window) + self.actionNotifications.setObjectName("actionNotifications") + self.actionNetwork = QtWidgets.QAction(window) + self.actionNetwork.setObjectName("actionNetwork") + self.actionAbout_program = QtWidgets.QAction(window) + self.actionAbout_program.setObjectName("actionAbout_program") + self.updateSettings = QtWidgets.QAction(window) + self.actionSettings = QtWidgets.QAction(window) + self.actionSettings.setObjectName("actionSettings") + self.audioSettings = QtWidgets.QAction(window) + self.videoSettings = QtWidgets.QAction(window) + self.pluginData = QtWidgets.QAction(window) + self.importPlugin = QtWidgets.QAction(window) + self.reloadPlugins = QtWidgets.QAction(window) + self.lockApp = QtWidgets.QAction(window) + self.createGC = QtWidgets.QAction(window) + self.joinGC = QtWidgets.QAction(window) + self.gc_invites = QtWidgets.QAction(window) + + self.menuProfile.addAction(self.actionAdd_friend) + self.menuProfile.addAction(self.actionSettings) + self.menuProfile.addAction(self.lockApp) + self.menuGC.addAction(self.createGC) + self.menuGC.addAction(self.joinGC) + self.menuGC.addAction(self.gc_invites) + self.menuSettings.addAction(self.actionPrivacy_settings) + self.menuSettings.addAction(self.actionInterface_settings) + self.menuSettings.addAction(self.actionNotifications) + self.menuSettings.addAction(self.actionNetwork) + self.menuSettings.addAction(self.audioSettings) + self.menuSettings.addAction(self.videoSettings) + self.menuSettings.addAction(self.updateSettings) + self.menuPlugins.addAction(self.pluginData) + self.menuPlugins.addAction(self.importPlugin) + self.menuPlugins.addAction(self.reloadPlugins) + self.menuAbout.addAction(self.actionAbout_program) + + self.menubar.addAction(self.menuProfile.menuAction()) + self.menubar.addAction(self.menuGC.menuAction()) + self.menubar.addAction(self.menuSettings.menuAction()) + self.menubar.addAction(self.menuPlugins.menuAction()) + self.menubar.addAction(self.menuAbout.menuAction()) + + self.actionAbout_program.triggered.connect(self.about_program) + self.actionNetwork.triggered.connect(self.network_settings) + self.actionAdd_friend.triggered.connect(self.add_contact_triggered) + self.createGC.triggered.connect(self.create_gc) + self.joinGC.triggered.connect(self.join_gc) + self.actionSettings.triggered.connect(self.profile_settings) + self.actionPrivacy_settings.triggered.connect(self.privacy_settings) + self.actionInterface_settings.triggered.connect(self.interface_settings) + self.actionNotifications.triggered.connect(self.notification_settings) + self.audioSettings.triggered.connect(self.audio_settings) + self.videoSettings.triggered.connect(self.video_settings) + self.updateSettings.triggered.connect(self.update_settings) + self.pluginData.triggered.connect(self.plugins_menu) + self.lockApp.triggered.connect(self.lock_app) + self.importPlugin.triggered.connect(self.import_plugin) + self.reloadPlugins.triggered.connect(self.reload_plugins) + self.gc_invites.triggered.connect(self._open_gc_invites_list) + + def languageChange(self, *args, **kwargs): + self.retranslateUi() + + def event(self, event): + if event.type() == QtCore.QEvent.WindowActivate: + self._tray.setIcon(QtGui.QIcon(util.join_path(util.get_images_directory(), 'icon.png'))) + self.messages.repaint() + return super().event(event) + + def retranslateUi(self): + self.lockApp.setText(util_ui.tr("Lock")) + self.menuPlugins.setTitle(util_ui.tr("Plugins")) + self.menuGC.setTitle(util_ui.tr("Group chats")) + self.pluginData.setText(util_ui.tr("List of plugins")) + self.menuProfile.setTitle(util_ui.tr("Profile")) + self.menuSettings.setTitle(util_ui.tr("Settings")) + self.menuAbout.setTitle(util_ui.tr("About")) + self.actionAdd_friend.setText(util_ui.tr("Add contact")) + self.createGC.setText(util_ui.tr("Create group chat")) + self.joinGC.setText(util_ui.tr("Join group chat")) + self.gc_invites.setText(util_ui.tr("Group invites")) + self.actionprofilesettings.setText(util_ui.tr("Profile")) + self.actionPrivacy_settings.setText(util_ui.tr("Privacy")) + self.actionInterface_settings.setText(util_ui.tr("Interface")) + self.actionNotifications.setText(util_ui.tr("Notifications")) + self.actionNetwork.setText(util_ui.tr("Network")) + self.actionAbout_program.setText(util_ui.tr("About program")) + self.actionSettings.setText(util_ui.tr("Settings")) + self.audioSettings.setText(util_ui.tr("Audio")) + self.videoSettings.setText(util_ui.tr("Video")) + self.updateSettings.setText(util_ui.tr("Updates")) + self.importPlugin.setText(util_ui.tr("Import plugin")) + self.reloadPlugins.setText(util_ui.tr("Reload plugins")) + + self.searchLineEdit.setPlaceholderText(util_ui.tr("Search")) + self.sendMessageButton.setToolTip(util_ui.tr("Send message")) + self.callButton.setToolTip(util_ui.tr("Start audio call with friend")) + self.contactsFilterComboBox.clear() + self.contactsFilterComboBox.addItem(util_ui.tr("All")) + self.contactsFilterComboBox.addItem(util_ui.tr("Online")) + self.contactsFilterComboBox.addItem(util_ui.tr("Online first")) + self.contactsFilterComboBox.addItem(util_ui.tr("Name")) + self.contactsFilterComboBox.addItem(util_ui.tr("Online and by name")) + self.contactsFilterComboBox.addItem(util_ui.tr("Online first and by name")) + + def setup_right_bottom(self, Form): + Form.resize(650, 60) + self.messageEdit = MessageArea(Form, self) + self.messageEdit.setGeometry(QtCore.QRect(0, 3, 450, 55)) + font = QtGui.QFont() + font.setPointSize(11) + font.setFamily(self._settings['font']) + self.messageEdit.setFont(font) + + self.sendMessageButton = QtWidgets.QPushButton(Form) + self.sendMessageButton.setGeometry(QtCore.QRect(565, 3, 60, 55)) + + self.menuButton = MenuButton(Form, self.show_menu) + self.menuButton.setGeometry(QtCore.QRect(QtCore.QRect(455, 3, 55, 55))) + + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'send.png')) + icon = QtGui.QIcon(pixmap) + self.sendMessageButton.setIcon(icon) + self.sendMessageButton.setIconSize(QtCore.QSize(45, 60)) + + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png')) + icon = QtGui.QIcon(pixmap) + self.menuButton.setIcon(icon) + self.menuButton.setIconSize(QtCore.QSize(40, 40)) + + self.sendMessageButton.clicked.connect(self.send_message) + + QtCore.QMetaObject.connectSlotsByName(Form) + + def setup_left_column(self, left_column): + uic.loadUi(util.get_views_path('ms_left_column'), left_column) + + pixmap = QtGui.QPixmap() + pixmap.load(util.join_path(util.get_images_directory(), 'search.png')) + left_column.searchLabel.setPixmap(pixmap) + + self.name = DataLabel(left_column) + self.name.setGeometry(QtCore.QRect(75, 15, 150, 25)) + font = QtGui.QFont() + font.setFamily(self._settings['font']) + font.setPointSize(14) + font.setBold(True) + self.name.setFont(font) + + self.status_message = DataLabel(left_column) + self.status_message.setGeometry(QtCore.QRect(75, 35, 170, 25)) + + self.connection_status = StatusCircle(left_column) + self.connection_status.setGeometry(QtCore.QRect(230, 10, 32, 32)) + + left_column.contactsFilterComboBox.activated[int].connect(lambda x: self._filtering()) + + self.avatar_label = left_column.avatarLabel + self.searchLineEdit = left_column.searchLineEdit + self.contacts_filter = self.contactsFilterComboBox = left_column.contactsFilterComboBox + + self.groupInvitesPushButton = left_column.groupInvitesPushButton + + self.groupInvitesPushButton.clicked.connect(self._open_gc_invites_list) + self.avatar_label.mouseReleaseEvent = self.profile_settings + self.status_message.mouseReleaseEvent = self.profile_settings + self.name.mouseReleaseEvent = self.profile_settings + + self.friends_list = left_column.friendsListWidget + self.friends_list.itemSelectionChanged.connect(self._selected_contact_changed) + self.friends_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.friends_list.customContextMenuRequested.connect(self._friend_right_click) + self.friends_list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) + self.friends_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.friends_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.friends_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) + + def setup_right_top(self, Form): + Form.resize(650, 75) + self.account_avatar = QtWidgets.QLabel(Form) + self.account_avatar.setGeometry(QtCore.QRect(10, 5, 64, 64)) + self.account_avatar.setScaledContents(False) + self.account_name = DataLabel(Form) + self.account_name.setGeometry(QtCore.QRect(100, 0, 400, 25)) + self.account_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) + font = QtGui.QFont() + font.setFamily(self._settings['font']) + font.setPointSize(14) + font.setBold(True) + self.account_name.setFont(font) + self.account_status = DataLabel(Form) + self.account_status.setGeometry(QtCore.QRect(100, 20, 400, 25)) + self.account_status.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) + font.setPointSize(12) + font.setBold(False) + self.account_status.setFont(font) + self.account_status.setObjectName("account_status") + self.callButton = QtWidgets.QPushButton(Form) + self.callButton.setGeometry(QtCore.QRect(550, 5, 50, 50)) + self.callButton.setObjectName("callButton") + self.callButton.clicked.connect(lambda: self._calls_manager.call_click(True)) + self.videocallButton = QtWidgets.QPushButton(Form) + self.videocallButton.setGeometry(QtCore.QRect(550, 5, 50, 50)) + self.videocallButton.setObjectName("videocallButton") + self.videocallButton.clicked.connect(lambda: self._calls_manager.call_click(True, True)) + self.groupMenuButton = QtWidgets.QPushButton(Form) + self.groupMenuButton.setGeometry(QtCore.QRect(470, 10, 50, 50)) + self.groupMenuButton.clicked.connect(self._toggle_gc_peers_list) + self.groupMenuButton.setVisible(False) + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png')) + icon = QtGui.QIcon(pixmap) + self.groupMenuButton.setIcon(icon) + self.groupMenuButton.setIconSize(QtCore.QSize(45, 60)) + self.update_call_state('call') + self.typing = QtWidgets.QLabel(Form) + self.typing.setGeometry(QtCore.QRect(500, 25, 50, 30)) + pixmap = QtGui.QPixmap(QtCore.QSize(50, 30)) + pixmap.load(util.join_path(util.get_images_directory(), 'typing.png')) + self.typing.setScaledContents(False) + self.typing.setPixmap(pixmap.scaled(50, 30, QtCore.Qt.KeepAspectRatio)) + self.typing.setVisible(False) + QtCore.QMetaObject.connectSlotsByName(Form) + + def setup_right_center(self, widget): + self.messages = QtWidgets.QListWidget(widget) + self.messages.setGeometry(0, 0, 620, 310) + self.messages.setObjectName("messages") + self.messages.setSpacing(1) + self.messages.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.messages.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.messages.focusOutEvent = lambda event: self.messages.clearSelection() + self.messages.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) + + def load(pos): + if not pos: + contact = self._contacts_manager.get_curr_contact() + self._history_loader.load_history(contact) + self.messages.verticalScrollBar().setValue(1) + self.messages.verticalScrollBar().valueChanged.connect(load) + self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) + self.messages.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + + self.peers_list = QtWidgets.QListWidget(widget) + self.peers_list.setGeometry(0, 0, 0, 0) + self.peers_list.setObjectName("peersList") + self.peers_list.setSpacing(1) + self.peers_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.peers_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.peers_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) + self.peers_list.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + + def initUI(self): + self.setMinimumSize(920, 500) + s = self._settings + self.setGeometry(s['x'], s['y'], s['width'], s['height']) + self.setWindowTitle('Toxygen') + menu = QtWidgets.QWidget() + main = QtWidgets.QWidget() + grid = QtWidgets.QGridLayout() + info = QtWidgets.QWidget() + left_column = QtWidgets.QWidget() + messages = QtWidgets.QWidget() + message_buttons = QtWidgets.QWidget() + self.setup_right_center(messages) + self.setup_right_top(info) + self.setup_right_bottom(message_buttons) + self.setup_left_column(left_column) + self.setup_menu(menu) + if not s['mirror_mode']: + grid.addWidget(left_column, 1, 0, 4, 1) + grid.addWidget(messages, 2, 1, 2, 1) + grid.addWidget(info, 1, 1) + grid.addWidget(message_buttons, 4, 1) + grid.setColumnMinimumWidth(1, 500) + grid.setColumnMinimumWidth(0, 270) + else: + grid.addWidget(left_column, 1, 1, 4, 1) + grid.addWidget(messages, 2, 0, 2, 1) + grid.addWidget(info, 1, 0) + grid.addWidget(message_buttons, 4, 0) + grid.setColumnMinimumWidth(0, 500) + grid.setColumnMinimumWidth(1, 270) + + grid.addWidget(menu, 0, 0, 1, 2) + grid.setSpacing(0) + grid.setContentsMargins(0, 0, 0, 0) + grid.setRowMinimumHeight(0, 25) + grid.setRowMinimumHeight(1, 75) + grid.setRowMinimumHeight(2, 25) + grid.setRowMinimumHeight(3, 320) + grid.setRowMinimumHeight(4, 55) + grid.setColumnStretch(1, 1) + grid.setRowStretch(3, 1) + main.setLayout(grid) + self.setCentralWidget(main) + self.messageEdit.setFocus() + self.friend_info = info + self.retranslateUi() + + def closeEvent(self, event): + close_setting = self._settings['close_app'] + if close_setting == 0 or self._settings.closing: + if self._saved: + return + self._saved = True + self._settings['x'] = self.geometry().x() + self._settings['y'] = self.geometry().y() + self._settings['width'] = self.width() + self._settings['height'] = self.height() + self._settings.save() + util_ui.close_all_windows() + event.accept() + elif close_setting == 2 and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): + event.ignore() + self.hide() + else: + event.ignore() + self.showMinimized() + + def close_window(self): + self._settings.closing = True + self.close() + + def resizeEvent(self, *args, **kwargs): + width = self.width() - 270 + if not self._should_show_group_peers_list: + self.messages.setGeometry(0, 0, width, self.height() - 155) + self.peers_list.setGeometry(0, 0, 0, 0) + else: + self.messages.setGeometry(0, 0, width * 3 // 4, self.height() - 155) + self.peers_list.setGeometry(width * 3 // 4, 0, width - width * 3 // 4, self.height() - 155) + + invites_button_visible = self.groupInvitesPushButton.isVisible() + self.friends_list.setGeometry(0, 125 if invites_button_visible else 100, + 270, self.height() - 150 if invites_button_visible else self.height() - 125) + + self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 10, 50, 50)) + self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 10, 50, 50)) + self.groupMenuButton.setGeometry(QtCore.QRect(self.width() - 450, 10, 50, 50)) + self.typing.setGeometry(QtCore.QRect(self.width() - 450, 20, 50, 30)) + + self.messageEdit.setGeometry(QtCore.QRect(55, 0, self.width() - 395, 55)) + self.menuButton.setGeometry(QtCore.QRect(0, 0, 55, 55)) + self.sendMessageButton.setGeometry(QtCore.QRect(self.width() - 340, 0, 70, 55)) + + self.account_name.setGeometry(QtCore.QRect(100, 15, self.width() - 560, 25)) + self.account_status.setGeometry(QtCore.QRect(100, 35, self.width() - 560, 25)) + self.messageEdit.setFocus() + + def keyPressEvent(self, event): + key, modifiers = event.key(), event.modifiers() + if key == QtCore.Qt.Key_Escape and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): + self.hide() + elif key == QtCore.Qt.Key_C and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): + rows = list(map(lambda x: self.messages.row(x), self.messages.selectedItems())) + indexes = (rows[0] - self.messages.count(), rows[-1] - self.messages.count()) + s = self._history_loader.export_history(self._contacts_manager.get_curr_friend(), True, indexes) + self.copy_text(s) + elif key == QtCore.Qt.Key_Z and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): + self.messages.clearSelection() + elif key == QtCore.Qt.Key_F and modifiers & QtCore.Qt.ControlModifier: + self.show_search_field() + else: + super().keyPressEvent(event) + + # ----------------------------------------------------------------------------------------------------------------- + # Functions which called when user click in menu + # ----------------------------------------------------------------------------------------------------------------- + + def about_program(self): + # TODO: replace with window + text = util_ui.tr('Toxygen is Tox client written on Python.\nVersion: ') + text += '' + '\nGitHub: https://github.com/toxygen-project/toxygen/' + title = util_ui.tr('About') + util_ui.message_box(text, title) + + def network_settings(self): + self._modal_window = self._widget_factory.create_network_settings_window() + self._modal_window.show() + + def plugins_menu(self): + self._modal_window = self._widget_factory.create_plugins_settings_window() + self._modal_window.show() + + def add_contact_triggered(self, _): + self.add_contact() + + def add_contact(self, link=''): + self._modal_window = self._widget_factory.create_add_contact_window(link) + self._modal_window.show() + + def create_gc(self): + self._modal_window = self._widget_factory.create_group_screen_window() + self._modal_window.show() + + def join_gc(self): + self._modal_window = self._widget_factory.create_join_group_screen_window() + self._modal_window.show() + + def profile_settings(self, _): + self._modal_window = self._widget_factory.create_profile_settings_window() + self._modal_window.show() + + def privacy_settings(self): + self._modal_window = self._widget_factory.create_privacy_settings_window() + self._modal_window.show() + + def notification_settings(self): + self._modal_window = self._widget_factory.create_notification_settings_window() + self._modal_window.show() + + def interface_settings(self): + self._modal_window = self._widget_factory.create_interface_settings_window() + self._modal_window.show() + + def audio_settings(self): + self._modal_window = self._widget_factory.create_audio_settings_window() + self._modal_window.show() + + def video_settings(self): + self._modal_window = self._widget_factory.create_video_settings_window() + self._modal_window.show() + + def update_settings(self): + self._modal_window = self._widget_factory.create_update_settings_window() + self._modal_window.show() + + def reload_plugins(self): + if self._plugin_loader is not None: + self._plugin_loader.reload() + + @staticmethod + def import_plugin(): + directory = util_ui.directory_dialog(util_ui.tr('Choose folder with plugin')) + if directory: + src = directory + '/' + dest = util.get_plugins_directory() + util.copy(src, dest) + util_ui.message_box(util_ui.tr('Plugin will be loaded after restart'), util_ui.tr("Restart Toxygen")) + + def lock_app(self): + if self._toxes.has_password(): + self._settings.locked = True + self.hide() + else: + util_ui.message_box(util_ui.tr('Error. Profile password is not set.'), util_ui.tr("Cannot lock app")) + + def show_menu(self): + if not hasattr(self, 'menu'): + self.menu = DropdownMenu(self) + self.menu.setGeometry(QtCore.QRect(0 if self._settings['mirror_mode'] else 270, + self.height() - 120, + 180, + 120)) + self.menu.show() + + # ----------------------------------------------------------------------------------------------------------------- + # Messages, calls and file transfers + # ----------------------------------------------------------------------------------------------------------------- + + def send_message(self): + self._messenger.send_message() + + def send_file(self): + self.menu.hide() + if self._contacts_manager.is_active_a_friend(): + caption = util_ui.tr('Choose file') + name = util_ui.file_dialog(caption) + if name[0]: + self._file_transfer_handler.send_file(name[0], self._contacts_manager.get_active_number()) + + def send_screenshot(self, hide=False): + self.menu.hide() + if self._contacts_manager.is_active_a_friend(): + self.sw = self._widget_factory.create_screenshot_window(self) + self.sw.show() + if hide: + self.hide() + + def send_smiley(self): + self.menu.hide() + if self._contacts_manager.get_curr_contact() is None: + return + self._smiley_window = self._widget_factory.create_smiley_window(self) + rect = QtCore.QRect(self.menu.x(), + self.menu.y() - self.menu.height(), + self._smiley_window.width(), + self._smiley_window.height()) + self._smiley_window.setGeometry(rect) + self._smiley_window.show() + + def send_sticker(self): + self.menu.hide() + if self._contacts_manager.is_active_a_friend(): + self.sticker = self._widget_factory.create_sticker_window() + self.sticker.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), + self.y() + self.height() - 200, + self.sticker.width(), + self.sticker.height())) + self.sticker.show() + + def active_call(self): + self.update_call_state('finish_call') + + def incoming_call(self): + self.update_call_state('incoming_call') + + def call_finished(self): + self.update_call_state('call') + + def update_call_state(self, state): + pixmap = QtGui.QPixmap(os.path.join(util.get_images_directory(), '{}.png'.format(state))) + icon = QtGui.QIcon(pixmap) + self.callButton.setIcon(icon) + self.callButton.setIconSize(QtCore.QSize(50, 50)) + + pixmap = QtGui.QPixmap(os.path.join(util.get_images_directory(), '{}_video.png'.format(state))) + icon = QtGui.QIcon(pixmap) + self.videocallButton.setIcon(icon) + self.videocallButton.setIconSize(QtCore.QSize(35, 35)) + + # ----------------------------------------------------------------------------------------------------------------- + # Functions which called when user open context menu in friends list + # ----------------------------------------------------------------------------------------------------------------- + + def _friend_right_click(self, pos): + item = self.friends_list.itemAt(pos) + number = self.friends_list.indexFromItem(item).row() + contact = self._contacts_manager.get_contact(number) + if contact is None or item is None: + return + generator = contact.get_context_menu_generator() + self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, self._settings, number, + self._groups_service, self._history_loader) + parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) + self.listMenu.move(parent_position + pos) + self.listMenu.show() + + def show_note(self, friend): + note = self._settings['notes'][friend.tox_id] if friend.tox_id in self._settings['notes'] else '' + user = util_ui.tr('Notes about user') + user = '{} {}'.format(user, friend.name) + + def save_note(text): + if friend.tox_id in self._settings['notes']: + del self._settings['notes'][friend.tox_id] + if text: + self._settings['notes'][friend.tox_id] = text + self._settings.save() + self.note = MultilineEdit(user, note, save_note) + self.note.show() + + def set_alias(self, num): + self._contacts_manager.set_alias(num) + + def remove_friend(self, num): + self._contacts_manager.delete_friend(num) + + def block_friend(self, num): + friend = self._contacts_manager.get_contact(num) + self._contacts_manager.block_user(friend.tox_id) + + @staticmethod + def copy_text(text): + util_ui.copy_to_clipboard(text) + + def auto_accept(self, num, value): + tox_id = self._contacts_manager.friend_public_key(num) + if value: + self._settings['auto_accept_from_friends'].append(tox_id) + else: + self._settings['auto_accept_from_friends'].remove(tox_id) + self._settings.save() + + def invite_friend_to_gc(self, friend_number, group_number): + self._contacts_manager.invite_friend(friend_number, group_number) + + def select_contact_row(self, row_index): + self.friends_list.setCurrentRow(row_index) + + # ----------------------------------------------------------------------------------------------------------------- + # Functions which called when user click somewhere else + # ----------------------------------------------------------------------------------------------------------------- + + def _selected_contact_changed(self): + num = self.friends_list.currentRow() + if self._contacts_manager.active_contact != num: + self._contacts_manager.active_contact = num + self.groupMenuButton.setVisible(self._contacts_manager.is_active_a_group()) + + def mouseReleaseEvent(self, event): + pos = self.connection_status.pos() + x, y = pos.x(), pos.y() + 25 + if (x < event.x() < x + 32) and (y < event.y() < y + 32): + self._profile.change_status() + else: + super().mouseReleaseEvent(event) + + def _filtering(self): + index = self.contactsFilterComboBox.currentIndex() + search_text = self.searchLineEdit.text() + self._contacts_manager.filtration_and_sorting(index, search_text) + + def show_search_field(self): + if hasattr(self, 'search_field') and self.search_field.isVisible(): + return + if self._contacts_manager.get_curr_friend() is None: + return + self.search_field = self._widget_factory.create_search_screen(self.messages) + x, y = self.messages.x(), self.messages.y() + self.messages.height() - 40 + self.search_field.setGeometry(x, y, self.messages.width(), 40) + self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40) + if self._should_show_group_peers_list: + self.peers_list.setFixedHeight(self.peers_list.height() - 40) + self.search_field.show() + + def _toggle_gc_peers_list(self): + self._should_show_group_peers_list = not self._should_show_group_peers_list + self.resizeEvent() + if self._should_show_group_peers_list: + self._groups_service.generate_peers_list() + + def _new_contact_selected(self, _): + if self._should_show_group_peers_list: + self._toggle_gc_peers_list() + index = self.friends_list.currentRow() + if self._contacts_manager.active_contact != index: + self.friends_list.setCurrentRow(self._contacts_manager.active_contact) + self.resizeEvent() + + def _open_gc_invites_list(self): + self._modal_window = self._widget_factory.create_group_invites_window() + self._modal_window.show() + + def update_gc_invites_button_state(self): + invites_count = self._groups_service.group_invites_count + self.groupInvitesPushButton.setVisible(invites_count > 0) + text = util_ui.tr('{} new invites to group chats').format(invites_count) + self.groupInvitesPushButton.setText(text) + self.resizeEvent() diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py new file mode 100644 index 0000000..122561b --- /dev/null +++ b/toxygen/ui/main_screen_widgets.py @@ -0,0 +1,496 @@ +from PyQt5 import QtCore, QtGui, QtWidgets +from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit +import urllib +import re +import utils.util as util +import utils.ui as util_ui +from stickers.stickers import load_stickers + + +class MessageArea(QtWidgets.QPlainTextEdit): + """User types messages here""" + + def __init__(self, parent, form): + super().__init__(parent) + self._messenger = self._contacts_manager = self._file_transfer_handler = None + self.parent = form + self.setAcceptDrops(True) + self._timer = QtCore.QTimer(self) + self._timer.timeout.connect(lambda: self._messenger.send_typing(False)) + + def set_dependencies(self, messenger, contacts_manager, file_transfer_handler): + self._messenger = messenger + self._contacts_manager = contacts_manager + self._file_transfer_handler = file_transfer_handler + + def keyPressEvent(self, event): + if event.matches(QtGui.QKeySequence.Paste): + mimeData = QtWidgets.QApplication.clipboard().mimeData() + if mimeData.hasUrls(): + for url in mimeData.urls(): + self.pasteEvent(url.toString()) + else: + self.pasteEvent() + elif event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter): + modifiers = event.modifiers() + if modifiers & QtCore.Qt.ControlModifier or modifiers & QtCore.Qt.ShiftModifier: + self.insertPlainText('\n') + else: + if self._timer.isActive(): + self._timer.stop() + self._messenger.send_typing(False) + self._messenger.send_message() + elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText(): + self.appendPlainText(self._messenger.get_last_message()) + elif event.key() == QtCore.Qt.Key_Tab and self._contacts_manager.is_active_a_group(): + text = self.toPlainText() + text_cursor = self.textCursor() + pos = text_cursor.position() + current_word = re.split("\s+", text[:pos])[-1] + start_index = text.rindex(current_word, 0, pos) + peer_name = self._contacts_manager.get_gc_peer_name(current_word) + self.setPlainText(text[:start_index] + peer_name + text[pos:]) + new_pos = start_index + len(peer_name) + text_cursor.setPosition(new_pos, QtGui.QTextCursor.MoveAnchor) + self.setTextCursor(text_cursor) + else: + self._messenger.send_typing(True) + if self._timer.isActive(): + self._timer.stop() + self._timer.start(5000) + super().keyPressEvent(event) + + def contextMenuEvent(self, event): + menu = create_menu(self.createStandardContextMenu()) + menu.exec_(event.globalPos()) + del menu + + def dragEnterEvent(self, e): + e.accept() + + def dragMoveEvent(self, e): + e.accept() + + def dropEvent(self, e): + if e.mimeData().hasFormat('text/plain') or e.mimeData().hasFormat('text/html'): + e.accept() + self.pasteEvent(e.mimeData().text()) + elif e.mimeData().hasUrls(): + for url in e.mimeData().urls(): + self.pasteEvent(url.toString()) + e.accept() + else: + e.ignore() + + def pasteEvent(self, text=None): + text = text or QtWidgets.QApplication.clipboard().text() + if text.startswith('file://'): + if not self._contacts_manager.is_active_a_friend(): + return + friend_number = self._contacts_manager.get_active_number() + file_path = self._parse_file_path(text) + self._file_transfer_handler.send_file(file_path, friend_number) + else: + self.insertPlainText(text) + + @staticmethod + def _parse_file_path(file_name): + if file_name.endswith('\r\n'): + file_name = file_name[:-2] + file_name = urllib.parse.unquote(file_name) + + return file_name[8 if util.get_platform() == 'Windows' else 7:] + + +class ScreenShotWindow(RubberBandWindow): + + def __init__(self, file_transfer_handler, contacts_manager, *args): + super().__init__(*args) + self._file_transfer_handler = file_transfer_handler + self._contacts_manager = contacts_manager + + def closeEvent(self, *args): + if self.parent.isHidden(): + self.parent.show() + + def mouseReleaseEvent(self, event): + if self.rubberband.isVisible(): + self.rubberband.hide() + rect = self.rubberband.geometry() + if rect.width() and rect.height(): + screen = QtWidgets.QApplication.primaryScreen() + p = screen.grabWindow(0, + rect.x() + 4, + rect.y() + 4, + rect.width() - 8, + rect.height() - 8) + byte_array = QtCore.QByteArray() + buffer = QtCore.QBuffer(byte_array) + buffer.open(QtCore.QIODevice.WriteOnly) + p.save(buffer, 'PNG') + friend = self._contacts_manager.get_curr_contact() + self._file_transfer_handler.send_screenshot(bytes(byte_array.data()), friend.number) + self.close() + + +class SmileyWindow(QtWidgets.QWidget): + """ + Smiley selection window + """ + + def __init__(self, parent, smiley_loader): + super().__init__(parent) + self.setWindowFlags(QtCore.Qt.FramelessWindowHint) + self._parent = parent + self._data = smiley_loader.get_smileys() + + count = len(self._data) + if not count: + self.close() + + self._page_size = int(pow(count / 8, 0.5) + 1) * 8 # smileys per page + if count % self._page_size == 0: + self._page_count = count // self._page_size + else: + self._page_count = round(count / self._page_size + 0.5) + self._page = -1 + self._radio = [] + + for i in range(self._page_count): # pages - radio buttons + elem = QtWidgets.QRadioButton(self) + elem.setGeometry(QtCore.QRect(i * 20 + 5, 160, 20, 20)) + elem.clicked.connect(lambda c, t=i: self._checked(t)) + self._radio.append(elem) + + width = max(self._page_count * 20 + 30, (self._page_size + 5) * 8 // 10) + self.setMaximumSize(width, 200) + self.setMinimumSize(width, 200) + self._buttons = [] + + for i in range(self._page_size): # buttons with smileys + b = QtWidgets.QPushButton(self) + b.setGeometry(QtCore.QRect((i // 8) * 20 + 5, (i % 8) * 20, 20, 20)) + b.clicked.connect(lambda c, t=i: self._clicked(t)) + self._buttons.append(b) + self._checked(0) + + def leaveEvent(self, event): + self.close() + + def _checked(self, pos): # new page opened + self._radio[self._page].setChecked(False) + self._radio[pos].setChecked(True) + self._page = pos + start = self._page * self._page_size + for i in range(self._page_size): + try: + self._buttons[i].setVisible(True) + pixmap = QtGui.QPixmap(self._data[start + i][1]) + icon = QtGui.QIcon(pixmap) + self._buttons[i].setIcon(icon) + except: + self._buttons[i].setVisible(False) + + def _clicked(self, pos): # smiley selected + pos += self._page * self._page_size + smiley = self._data[pos][0] + self._parent.messageEdit.insertPlainText(smiley) + self.close() + + +class MenuButton(QtWidgets.QPushButton): + + def __init__(self, parent, enter): + super().__init__(parent) + self.enter = enter + + def enterEvent(self, event): + self.enter() + super().enterEvent(event) + + +class DropdownMenu(QtWidgets.QWidget): + + def __init__(self, parent): + super().__init__(parent) + self.installEventFilter(self) + self.setWindowFlags(QtCore.Qt.FramelessWindowHint) + self.setMaximumSize(120, 120) + self.setMinimumSize(120, 120) + self.screenshotButton = QRightClickButton(self) + self.screenshotButton.setGeometry(QtCore.QRect(0, 60, 60, 60)) + self.screenshotButton.setObjectName("screenshotButton") + + self.fileTransferButton = QtWidgets.QPushButton(self) + self.fileTransferButton.setGeometry(QtCore.QRect(60, 60, 60, 60)) + self.fileTransferButton.setObjectName("fileTransferButton") + + self.smileyButton = QtWidgets.QPushButton(self) + self.smileyButton.setGeometry(QtCore.QRect(0, 0, 60, 60)) + + self.stickerButton = QtWidgets.QPushButton(self) + self.stickerButton.setGeometry(QtCore.QRect(60, 0, 60, 60)) + + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'file.png')) + icon = QtGui.QIcon(pixmap) + self.fileTransferButton.setIcon(icon) + self.fileTransferButton.setIconSize(QtCore.QSize(50, 50)) + + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'screenshot.png')) + icon = QtGui.QIcon(pixmap) + self.screenshotButton.setIcon(icon) + self.screenshotButton.setIconSize(QtCore.QSize(50, 60)) + + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'smiley.png')) + icon = QtGui.QIcon(pixmap) + self.smileyButton.setIcon(icon) + self.smileyButton.setIconSize(QtCore.QSize(50, 50)) + + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'sticker.png')) + icon = QtGui.QIcon(pixmap) + self.stickerButton.setIcon(icon) + self.stickerButton.setIconSize(QtCore.QSize(55, 55)) + + self.screenshotButton.setToolTip(util_ui.tr("Send screenshot")) + self.fileTransferButton.setToolTip(util_ui.tr("Send file")) + self.smileyButton.setToolTip(util_ui.tr("Add smiley")) + self.stickerButton.setToolTip(util_ui.tr("Send sticker")) + + self.fileTransferButton.clicked.connect(parent.send_file) + self.screenshotButton.clicked.connect(parent.send_screenshot) + self.screenshotButton.rightClicked.connect(lambda: parent.send_screenshot(True)) + self.smileyButton.clicked.connect(parent.send_smiley) + self.stickerButton.clicked.connect(parent.send_sticker) + + def leaveEvent(self, event): + self.close() + + def eventFilter(self, obj, event): + if event.type() == QtCore.QEvent.WindowDeactivate: + self.close() + return False + + +class StickerItem(QtWidgets.QWidget): + + def __init__(self, fl): + super().__init__() + self._image_label = QtWidgets.QLabel(self) + self.path = fl + self.pixmap = QtGui.QPixmap() + self.pixmap.load(fl) + if self.pixmap.width() > 150: + self.pixmap = self.pixmap.scaled(150, 200, QtCore.Qt.KeepAspectRatio) + self.setFixedSize(150, self.pixmap.height()) + self._image_label.setPixmap(self.pixmap) + + +class StickerWindow(QtWidgets.QWidget): + """Sticker selection window""" + + def __init__(self, file_transfer_handler, contacts_manager): + super().__init__() + self._file_transfer_handler = file_transfer_handler + self._contacts_manager = contacts_manager + self.setWindowFlags(QtCore.Qt.FramelessWindowHint) + self.setMaximumSize(250, 200) + self.setMinimumSize(250, 200) + self.list = QtWidgets.QListWidget(self) + self.list.setGeometry(QtCore.QRect(0, 0, 250, 200)) + self._stickers = load_stickers() + for sticker in self._stickers: + item = StickerItem(sticker) + elem = QtWidgets.QListWidgetItem() + elem.setSizeHint(QtCore.QSize(250, item.height())) + self.list.addItem(elem) + self.list.setItemWidget(elem, item) + self.list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) + self.list.setSpacing(3) + self.list.clicked.connect(self.click) + + def click(self, index): + num = index.row() + friend = self._contacts_manager.get_curr_contact() + self._file_transfer_handler.send_sticker(self._stickers[num], friend.number) + self.close() + + def leaveEvent(self, event): + self.close() + + +class WelcomeScreen(CenteredWidget): + + def __init__(self, settings): + super().__init__() + self._settings = settings + self.setMaximumSize(250, 200) + self.setMinimumSize(250, 200) + self.center() + self.setAttribute(QtCore.Qt.WA_DeleteOnClose) + self.text = QtWidgets.QTextBrowser(self) + self.text.setGeometry(QtCore.QRect(0, 0, 250, 170)) + self.text.setOpenExternalLinks(True) + self.checkbox = QtWidgets.QCheckBox(self) + self.checkbox.setGeometry(QtCore.QRect(5, 170, 240, 30)) + self.checkbox.setText(util_ui.tr( "Don't show again")) + self.setWindowTitle(util_ui.tr( 'Tip of the day')) + import random + num = random.randint(0, 10) + if num == 0: + text = util_ui.tr('Press Esc if you want hide app to tray.') + elif num == 1: + text = util_ui.tr('Right click on screenshot button hides app to tray during screenshot.') + elif num == 2: + text = util_ui.tr('You can use Tox over Tor. For more info read this post') + elif num == 3: + text = util_ui.tr('Use Settings -> Interface to customize interface.') + elif num == 4: + text = util_ui.tr('Set profile password via Profile -> Settings. Password allows Toxygen encrypt your history and settings.') + elif num == 5: + text = util_ui.tr('Since v0.1.3 Toxygen supports plugins. Read more') + elif num == 6: + text = util_ui.tr('Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.') + elif num == 7: + text = util_ui.tr('New in Toxygen 0.4.1:
Downloading nodes from tox.chat
Bug fixes') + elif num == 8: + text = util_ui.tr('Delete single message in chat: make right click on spinner or message time and choose "Delete" in menu') + elif num == 9: + text = util_ui.tr( 'Use right click on inline image to save it') + else: + text = util_ui.tr('Set new NoSpam to avoid spam friend requests: Profile -> Settings -> Set new NoSpam.') + self.text.setHtml(text) + self.checkbox.stateChanged.connect(self.not_show) + QtCore.QTimer.singleShot(1000, self.show) + + def not_show(self): + self._settings['show_welcome_screen'] = False + self._settings.save() + + +class MainMenuButton(QtWidgets.QPushButton): + + def __init__(self, *args): + super().__init__(*args) + self.setObjectName("mainmenubutton") + + def setText(self, text): + metrics = QtGui.QFontMetrics(self.font()) + self.setFixedWidth(metrics.size(QtCore.Qt.TextSingleLine, text).width() + 20) + super().setText(text) + + +class ClickableLabel(QtWidgets.QLabel): + + clicked = QtCore.pyqtSignal() + + def __init__(self, *args): + super().__init__(*args) + + def mouseReleaseEvent(self, ev): + self.clicked.emit() + + +class SearchScreen(QtWidgets.QWidget): + + def __init__(self, contacts_manager, history_loader, messages, width, *args): + super().__init__(*args) + self._contacts_manager = contacts_manager + self._history_loader = history_loader + self.setMaximumSize(width, 40) + self.setMinimumSize(width, 40) + self._messages = messages + + self.search_text = LineEdit(self) + self.search_text.setGeometry(0, 0, width - 160, 40) + + self.search_button = ClickableLabel(self) + self.search_button.setGeometry(width - 160, 0, 40, 40) + pixmap = QtGui.QPixmap() + pixmap.load(util.join_path(util.get_images_directory(), 'search.png')) + self.search_button.setScaledContents(False) + self.search_button.setAlignment(QtCore.Qt.AlignCenter) + self.search_button.setPixmap(pixmap) + self.search_button.clicked.connect(self.search) + + font = QtGui.QFont() + font.setPointSize(32) + font.setBold(True) + + self.prev_button = QtWidgets.QPushButton(self) + self.prev_button.setGeometry(width - 120, 0, 40, 40) + self.prev_button.clicked.connect(self.prev) + self.prev_button.setText('\u25B2') + + self.next_button = QtWidgets.QPushButton(self) + self.next_button.setGeometry(width - 80, 0, 40, 40) + self.next_button.clicked.connect(self.next) + self.next_button.setText('\u25BC') + + self.close_button = QtWidgets.QPushButton(self) + self.close_button.setGeometry(width - 40, 0, 40, 40) + self.close_button.clicked.connect(self.close) + self.close_button.setText('×') + self.close_button.setFont(font) + + font.setPointSize(18) + self.next_button.setFont(font) + self.prev_button.setFont(font) + + self.retranslateUi() + + def retranslateUi(self): + self.search_text.setPlaceholderText(util_ui.tr('Search')) + + def show(self): + super().show() + self.search_text.setFocus() + + def search(self): + self._contacts_manager.update() + text = self.search_text.text() + contact = self._contacts_manager.get_curr_contact() + if text and contact and util.is_re_valid(text): + index = contact.search_string(text) + self.load_messages(index) + + def prev(self): + contact = self._contacts_manager.get_curr_contact() + if contact is not None: + index = contact.search_prev() + self.load_messages(index) + + def next(self): + contact = self._contacts_manager.get_curr_contact() + text = self.search_text.text() + if contact is not None: + index = contact.search_next() + if index is not None: + count = self._messages.count() + index += count + item = self._messages.item(index) + self._messages.scrollToItem(item) + self._messages.itemWidget(item).select_text(text) + else: + self.not_found(text) + + def load_messages(self, index): + text = self.search_text.text() + if index is not None: + count = self._messages.count() + while count + index < 0: + self._history_loader.load_history() + count = self._messages.count() + index += count + item = self._messages.item(index) + self._messages.scrollToItem(item) + self._messages.itemWidget(item).select_text(text) + else: + self.not_found(text) + + def closeEvent(self, *args): + self._messages.setGeometry(0, 0, self._messages.width(), self._messages.height() + 40) + super().closeEvent(*args) + + @staticmethod + def not_found(text): + util_ui.message_box(util_ui.tr('Text "{}" was not found').format(text), util_ui.tr('Not found')) diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py new file mode 100644 index 0000000..8aec578 --- /dev/null +++ b/toxygen/ui/menu.py @@ -0,0 +1,680 @@ +from PyQt5 import QtCore, QtGui, QtWidgets, uic +from user_data.settings import * +from utils.util import * +from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow +import pyaudio +import updater.updater as updater +import utils.ui as util_ui +import cv2 + + +class AddContact(CenteredWidget): + """Add contact form""" + + def __init__(self, settings, contacts_manager, tox_id=''): + super().__init__() + self._settings = settings + self._contacts_manager = contacts_manager + uic.loadUi(get_views_path('add_contact_screen'), self) + self._update_ui(tox_id) + self._adding = False + + def _update_ui(self, tox_id): + self.toxIdLineEdit = LineEdit(self) + self.toxIdLineEdit.setGeometry(QtCore.QRect(50, 40, 460, 30)) + self.toxIdLineEdit.setText(tox_id) + + self.messagePlainTextEdit.document().setPlainText(util_ui.tr('Hello! Please add me to your contact list.')) + self.addContactPushButton.clicked.connect(self._add_friend) + self._retranslate_ui() + + def _add_friend(self): + if self._adding: + return + self._adding = True + tox_id = self.toxIdLineEdit.text().strip() + if tox_id.startswith('tox:'): + tox_id = tox_id[4:] + message = self.messagePlainTextEdit.toPlainText() + send = self._contacts_manager.send_friend_request(tox_id, message) + self._adding = False + if send is True: + # request was successful + self.close() + else: # print error data + self.errorLabel.setText(send) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Add contact')) + self.addContactPushButton.setText(util_ui.tr('Send request')) + self.toxIdLabel.setText(util_ui.tr('TOX ID:')) + self.messageLabel.setText(util_ui.tr('Message:')) + self.toxIdLineEdit.setPlaceholderText(util_ui.tr('TOX ID or public key of contact')) + + +class NetworkSettings(CenteredWidget): + """Network settings form: UDP, Ipv6 and proxy""" + def __init__(self, settings, reset): + super().__init__() + self._settings = settings + self._reset = reset + uic.loadUi(get_views_path('network_settings_screen'), self) + self._update_ui() + + def _update_ui(self): + self.ipLineEdit = LineEdit(self) + self.ipLineEdit.setGeometry(100, 280, 270, 30) + + self.portLineEdit = LineEdit(self) + self.portLineEdit.setGeometry(100, 325, 270, 30) + + self.restartCorePushButton.clicked.connect(self._restart_core) + self.ipv6CheckBox.setChecked(self._settings['ipv6_enabled']) + self.udpCheckBox.setChecked(self._settings['udp_enabled']) + self.proxyCheckBox.setChecked(self._settings['proxy_type']) + self.ipLineEdit.setText(self._settings['proxy_host']) + self.portLineEdit.setText(str(self._settings['proxy_port'])) + self.httpProxyRadioButton.setChecked(self._settings['proxy_type'] == 1) + self.socksProxyRadioButton.setChecked(self._settings['proxy_type'] != 1) + self.downloadNodesCheckBox.setChecked(self._settings['download_nodes_list']) + self.lanCheckBox.setChecked(self._settings['lan_discovery']) + self._retranslate_ui() + self.proxyCheckBox.stateChanged.connect(lambda x: self._activate_proxy()) + self._activate_proxy() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Network settings")) + self.ipv6CheckBox.setText(util_ui.tr("IPv6")) + self.udpCheckBox.setText(util_ui.tr("UDP")) + self.lanCheckBox.setText(util_ui.tr("LAN")) + self.proxyCheckBox.setText(util_ui.tr("Proxy")) + self.ipLabel.setText(util_ui.tr("IP:")) + self.portLabel.setText(util_ui.tr("Port:")) + self.restartCorePushButton.setText(util_ui.tr("Restart TOX core")) + self.httpProxyRadioButton.setText(util_ui.tr("HTTP")) + self.socksProxyRadioButton.setText(util_ui.tr("Socks 5")) + self.downloadNodesCheckBox.setText(util_ui.tr("Download nodes list from tox.chat")) + self.warningLabel.setText(util_ui.tr("WARNING:\nusing proxy with enabled UDP\ncan produce IP leak")) + + def _activate_proxy(self): + bl = self.proxyCheckBox.isChecked() + self.ipLineEdit.setEnabled(bl) + self.portLineEdit.setEnabled(bl) + self.httpProxyRadioButton.setEnabled(bl) + self.socksProxyRadioButton.setEnabled(bl) + self.ipLabel.setEnabled(bl) + self.portLabel.setEnabled(bl) + + def _restart_core(self): + try: + self._settings['ipv6_enabled'] = self.ipv6CheckBox.isChecked() + self._settings['udp_enabled'] = self.udpCheckBox.isChecked() + proxy_enabled = self.proxyCheckBox.isChecked() + self._settings['proxy_type'] = 2 - int(self.httpProxyRadioButton.isChecked()) if proxy_enabled else 0 + self._settings['proxy_host'] = str(self.ipLineEdit.text()) + self._settings['proxy_port'] = int(self.portLineEdit.text()) + self._settings['download_nodes_list'] = self.downloadNodesCheckBox.isChecked() + self._settings['lan_discovery'] = self.lanCheckBox.isChecked() + self._settings.save() + # recreate tox instance + self._reset() + self.close() + except Exception as ex: + log('Exception in restart: ' + str(ex)) + + +class PrivacySettings(CenteredWidget): + """Privacy settings form: history, typing notifications""" + + def __init__(self, contacts_manager, settings): + """ + :type contacts_manager: ContactsManager + """ + super().__init__() + self._contacts_manager = contacts_manager + self._settings = settings + self.initUI() + self.center() + + def initUI(self): + self.setObjectName("privacySettings") + self.resize(370, 600) + self.setMinimumSize(QtCore.QSize(370, 600)) + self.setMaximumSize(QtCore.QSize(370, 600)) + self.saveHistory = QtWidgets.QCheckBox(self) + self.saveHistory.setGeometry(QtCore.QRect(10, 20, 350, 22)) + self.saveUnsentOnly = QtWidgets.QCheckBox(self) + self.saveUnsentOnly.setGeometry(QtCore.QRect(10, 60, 350, 22)) + + self.fileautoaccept = QtWidgets.QCheckBox(self) + self.fileautoaccept.setGeometry(QtCore.QRect(10, 100, 350, 22)) + + self.typingNotifications = QtWidgets.QCheckBox(self) + self.typingNotifications.setGeometry(QtCore.QRect(10, 140, 350, 30)) + self.inlines = QtWidgets.QCheckBox(self) + self.inlines.setGeometry(QtCore.QRect(10, 180, 350, 30)) + self.auto_path = QtWidgets.QLabel(self) + self.auto_path.setGeometry(QtCore.QRect(10, 230, 350, 30)) + self.path = QtWidgets.QPlainTextEdit(self) + self.path.setGeometry(QtCore.QRect(10, 265, 350, 45)) + self.change_path = QtWidgets.QPushButton(self) + self.change_path.setGeometry(QtCore.QRect(10, 320, 350, 30)) + self.typingNotifications.setChecked(self._settings['typing_notifications']) + self.fileautoaccept.setChecked(self._settings['allow_auto_accept']) + self.saveHistory.setChecked(self._settings['save_history']) + self.inlines.setChecked(self._settings['allow_inline']) + self.saveUnsentOnly.setChecked(self._settings['save_unsent_only']) + self.saveUnsentOnly.setEnabled(self._settings['save_history']) + self.saveHistory.stateChanged.connect(self.update) + self.path.setPlainText(self._settings['auto_accept_path'] or curr_directory()) + self.change_path.clicked.connect(self.new_path) + self.block_user_label = QtWidgets.QLabel(self) + self.block_user_label.setGeometry(QtCore.QRect(10, 360, 350, 30)) + self.block_id = QtWidgets.QPlainTextEdit(self) + self.block_id.setGeometry(QtCore.QRect(10, 390, 350, 30)) + self.block = QtWidgets.QPushButton(self) + self.block.setGeometry(QtCore.QRect(10, 430, 350, 30)) + self.block.clicked.connect(lambda: self._contacts_manager.block_user(self.block_id.toPlainText()) or self.close()) + self.blocked_users_label = QtWidgets.QLabel(self) + self.blocked_users_label.setGeometry(QtCore.QRect(10, 470, 350, 30)) + self.comboBox = QtWidgets.QComboBox(self) + self.comboBox.setGeometry(QtCore.QRect(10, 500, 350, 30)) + self.comboBox.addItems(self._settings['blocked']) + self.unblock = QtWidgets.QPushButton(self) + self.unblock.setGeometry(QtCore.QRect(10, 540, 350, 30)) + self.unblock.clicked.connect(lambda: self.unblock_user()) + self.retranslateUi() + QtCore.QMetaObject.connectSlotsByName(self) + + def retranslateUi(self): + self.setWindowTitle(util_ui.tr("Privacy settings")) + self.saveHistory.setText(util_ui.tr("Save chat history")) + self.fileautoaccept.setText(util_ui.tr("Allow file auto accept")) + self.typingNotifications.setText(util_ui.tr("Send typing notifications")) + self.auto_path.setText(util_ui.tr("Auto accept default path:")) + self.change_path.setText(util_ui.tr("Change")) + self.inlines.setText(util_ui.tr("Allow inlines")) + self.block_user_label.setText(util_ui.tr("Block by public key:")) + self.blocked_users_label.setText(util_ui.tr("Blocked users:")) + self.unblock.setText(util_ui.tr("Unblock")) + self.block.setText(util_ui.tr("Block user")) + self.saveUnsentOnly.setText(util_ui.tr("Save unsent messages only")) + + def update(self, new_state): + self.saveUnsentOnly.setEnabled(new_state) + if not new_state: + self.saveUnsentOnly.setChecked(False) + + def unblock_user(self): + if not self.comboBox.count(): + return + title = util_ui.tr("Add to friend list") + info = util_ui.tr("Do you want to add this user to friend list?") + reply = util_ui.question(info, title) + self._contacts_manager.unblock_user(self.comboBox.currentText(), reply) + self.close() + + def closeEvent(self, event): + self._settings['typing_notifications'] = self.typingNotifications.isChecked() + self._settings['allow_auto_accept'] = self.fileautoaccept.isChecked() + text = util_ui.tr('History will be cleaned! Continue?') + title = util_ui.tr('Chat history') + + if self._settings['save_history'] and not self.saveHistory.isChecked(): # clear history + reply = util_ui.question(text, title) + if reply: + self._history_loader.clear_history() + self._settings['save_history'] = self.saveHistory.isChecked() + else: + self._settings['save_history'] = self.saveHistory.isChecked() + if self.saveUnsentOnly.isChecked() and not self._settings['save_unsent_only']: + reply = util_ui.question(text, title) + if reply: + self._history_loader.clear_history(None, True) + self._settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() + else: + self._settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() + self._settings['auto_accept_path'] = self.path.toPlainText() + self._settings['allow_inline'] = self.inlines.isChecked() + self._settings.save() + + def new_path(self): + directory = util_ui.directory_dialog() + if directory: + self.path.setPlainText(directory) + + +class NotificationsSettings(CenteredWidget): + """Notifications settings form""" + + def __init__(self, setttings): + super().__init__() + self._settings = setttings + uic.loadUi(get_views_path('notifications_settings_screen'), self) + self._update_ui() + self.center() + + def closeEvent(self, *args, **kwargs): + self._settings['notifications'] = self.notificationsCheckBox.isChecked() + self._settings['sound_notifications'] = self.soundNotificationsCheckBox.isChecked() + self._settings['group_notifications'] = self.groupNotificationsCheckBox.isChecked() + self._settings['calls_sound'] = self.callsSoundCheckBox.isChecked() + self._settings.save() + + def _update_ui(self): + self.notificationsCheckBox.setChecked(self._settings['notifications']) + self.soundNotificationsCheckBox.setChecked(self._settings['sound_notifications']) + self.groupNotificationsCheckBox.setChecked(self._settings['group_notifications']) + self.callsSoundCheckBox.setChecked(self._settings['calls_sound']) + self._retranslate_ui() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Notifications settings")) + self.notificationsCheckBox.setText(util_ui.tr("Enable notifications")) + self.groupNotificationsCheckBox.setText(util_ui.tr("Notify about all messages in groups")) + self.callsSoundCheckBox.setText(util_ui.tr("Enable call\'s sound")) + self.soundNotificationsCheckBox.setText(util_ui.tr("Enable sound notifications")) + + +class InterfaceSettings(CenteredWidget): + """Interface settings form""" + + def __init__(self, settings, smiley_loader): + super().__init__() + self._settings = settings + self._smiley_loader = smiley_loader + + uic.loadUi(get_views_path('interface_settings_screen'), self) + self._update_ui() + self.center() + + def _update_ui(self): + themes = list(self._settings.built_in_themes().keys()) + self.themeComboBox.addItems(themes) + theme = self._settings['theme'] + if theme in self._settings.built_in_themes().keys(): + index = themes.index(theme) + else: + index = 0 + self.themeComboBox.setCurrentIndex(index) + + supported_languages = sorted(Settings.supported_languages().keys(), reverse=True) + for key in supported_languages: + self.languageComboBox.insertItem(0, key) + if self._settings['language'] == key: + self.languageComboBox.setCurrentIndex(0) + + smiley_packs = self._smiley_loader.get_packs_list() + self.smileysPackComboBox.addItems(smiley_packs) + try: + index = smiley_packs.index(self._settings['smiley_pack']) + except: + index = smiley_packs.index('default') + self.smileysPackComboBox.setCurrentIndex(index) + + app_closing_setting = self._settings['close_app'] + self.closeRadioButton.setChecked(app_closing_setting == 0) + self.hideRadioButton.setChecked(app_closing_setting == 1) + self.closeToTrayRadioButton.setChecked(app_closing_setting == 2) + + self.compactModeCheckBox.setChecked(self._settings['compact_mode']) + self.showAvatarsCheckBox.setChecked(self._settings['show_avatars']) + self.smileysCheckBox.setChecked(self._settings['smileys']) + + self.importSmileysPushButton.clicked.connect(self._import_smileys) + self.importStickersPushButton.clicked.connect(self._import_stickers) + + self._retranslate_ui() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Interface settings")) + self.showAvatarsCheckBox.setText(util_ui.tr("Show avatars in chat")) + self.themeLabel.setText(util_ui.tr("Theme:")) + self.languageLabel.setText(util_ui.tr("Language:")) + self.smileysGroupBox.setTitle(util_ui.tr("Smileys settings")) + self.smileysPackLabel.setText(util_ui.tr("Smiley pack:")) + self.smileysCheckBox.setText(util_ui.tr("Smileys")) + self.closeRadioButton.setText(util_ui.tr("Close app")) + self.hideRadioButton.setText(util_ui.tr("Hide app")) + self.closeToTrayRadioButton.setText(util_ui.tr("Close to tray")) + self.mirrorModeCheckBox.setText(util_ui.tr("Mirror mode")) + self.compactModeCheckBox.setText(util_ui.tr("Compact contact list")) + self.importSmileysPushButton.setText(util_ui.tr("Import smiley pack")) + self.importStickersPushButton.setText(util_ui.tr("Import sticker pack")) + self.appClosingGroupBox.setTitle(util_ui.tr("App closing settings")) + + @staticmethod + def _import_stickers(): + directory = util_ui.directory_dialog(util_ui.tr('Choose folder with sticker pack')) + if directory: + dest = join_path(get_stickers_directory(), os.path.basename(directory)) + copy(directory, dest) + + @staticmethod + def _import_smileys(): + directory = util_ui.directory_dialog(util_ui.tr('Choose folder with smiley pack')) + if not directory: + return + src = directory + '/' + dest = join_path(get_smileys_directory(), os.path.basename(directory)) + copy(src, dest) + + def closeEvent(self, event): + app = QtWidgets.QApplication.instance() + + self._settings['theme'] = str(self.themeComboBox.currentText()) + try: + theme = self._settings['theme'] + styles_path = join_path(get_styles_directory(), self._settings.built_in_themes()[theme]) + with open(styles_path) as fl: + style = fl.read() + app.setStyleSheet(style) + except IsADirectoryError: + pass + + self._settings['smileys'] = self.smileysCheckBox.isChecked() + + restart = False + if self._settings['mirror_mode'] != self.mirrorModeCheckBox.isChecked(): + self._settings['mirror_mode'] = self.mirrorModeCheckBox.isChecked() + restart = True + + if self._settings['compact_mode'] != self.compactModeCheckBox.isChecked(): + self._settings['compact_mode'] = self.compactModeCheckBox.isChecked() + restart = True + + if self._settings['show_avatars'] != self.showAvatarsCheckBox.isChecked(): + self._settings['show_avatars'] = self.showAvatarsCheckBox.isChecked() + restart = True + + self._settings['smiley_pack'] = self.smileysPackComboBox.currentText() + self._smiley_loader.load_pack() + + language = self.languageComboBox.currentText() + if self._settings['language'] != language: + self._settings['language'] = language + path = Settings.supported_languages()[language] + app.removeTranslator(app.translator) + app.translator.load(join_path(get_translations_directory(), path)) + app.installTranslator(app.translator) + + app_closing_setting = 0 + if self.hideRadioButton.isChecked(): + app_closing_setting = 1 + elif self.closeToTrayRadioButton.isChecked(): + app_closing_setting = 2 + self._settings['close_app'] = app_closing_setting + self._settings.save() + + if restart: + util_ui.message_box(util_ui.tr('Restart app to apply settings'), util_ui.tr('Restart required')) + + +class AudioSettings(CenteredWidget): + """ + Audio calls settings form + """ + + def __init__(self, settings): + super().__init__() + self._settings = settings + self._in_indexes = self._out_indexes = None + uic.loadUi(get_views_path('audio_settings_screen'), self) + self._update_ui() + self.center() + + def closeEvent(self, event): + self._settings.audio['input'] = self._in_indexes[self.inputDeviceComboBox.currentIndex()] + self._settings.audio['output'] = self._out_indexes[self.outputDeviceComboBox.currentIndex()] + self._settings.save() + + def _update_ui(self): + p = pyaudio.PyAudio() + self._in_indexes, self._out_indexes = [], [] + for i in range(p.get_device_count()): + device = p.get_device_info_by_index(i) + if device["maxInputChannels"]: + self.inputDeviceComboBox.addItem(str(device["name"])) + self._in_indexes.append(i) + if device["maxOutputChannels"]: + self.outputDeviceComboBox.addItem(str(device["name"])) + self._out_indexes.append(i) + self.inputDeviceComboBox.setCurrentIndex(self._in_indexes.index(self._settings.audio['input'])) + self.outputDeviceComboBox.setCurrentIndex(self._out_indexes.index(self._settings.audio['output'])) + self._retranslate_ui() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Audio settings")) + self.inputDeviceLabel.setText(util_ui.tr("Input device:")) + self.outputDeviceLabel.setText(util_ui.tr("Output device:")) + + +class DesktopAreaSelectionWindow(RubberBandWindow): + + def mouseReleaseEvent(self, event): + if self.rubberband.isVisible(): + self.rubberband.hide() + rect = self.rubberband.geometry() + width, height = rect.width(), rect.height() + if width >= 8 and height >= 8: + self.parent.save(rect.x(), rect.y(), width - (width % 4), height - (height % 4)) + self.close() + + +class VideoSettings(CenteredWidget): + """ + Audio calls settings form + """ + + def __init__(self, settings): + super().__init__() + self._settings = settings + uic.loadUi(get_views_path('video_settings_screen'), self) + self._devices = self._frame_max_sizes = None + self._update_ui() + self.center() + self.desktopAreaSelection = None + + def closeEvent(self, event): + if self.deviceComboBox.currentIndex() == 0: + return + try: + self._settings.video['device'] = self.devices[self.input.currentIndex()] + text = self.resolutionComboBox.currentText() + self._settings.video['width'] = int(text.split(' ')[0]) + self._settings.video['height'] = int(text.split(' ')[-1]) + self._settings.save() + except Exception as ex: + print('Saving video settings error: ' + str(ex)) + + def save(self, x, y, width, height): + self.desktopAreaSelection = None + self._settings.video['device'] = -1 + self._settings.video['width'] = width + self._settings.video['height'] = height + self._settings.video['x'] = x + self._settings.video['y'] = y + self._settings.save() + + def _update_ui(self): + self.deviceComboBox.currentIndexChanged.connect(self._device_changed) + self.selectRegionPushButton.clicked.connect(self._button_clicked) + self._devices = [-1] + screen = QtWidgets.QApplication.primaryScreen() + size = screen.size() + self._frame_max_sizes = [(size.width(), size.height())] + desktop = util_ui.tr("Desktop") + self.deviceComboBox.addItem(desktop) + for i in range(10): + v = cv2.VideoCapture(i) + if v.isOpened(): + v.set(cv2.CAP_PROP_FRAME_WIDTH, 10000) + v.set(cv2.CAP_PROP_FRAME_HEIGHT, 10000) + + width = int(v.get(cv2.CAP_PROP_FRAME_WIDTH)) + height = int(v.get(cv2.CAP_PROP_FRAME_HEIGHT)) + del v + self._devices.append(i) + self._frame_max_sizes.append((width, height)) + self.deviceComboBox.addItem(util_ui.tr('Device #') + str(i)) + try: + index = self._devices.index(self._settings.video['device']) + self.deviceComboBox.setCurrentIndex(index) + except: + print('Video devices error!') + self._retranslate_ui() + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Video settings")) + self.deviceLabel.setText(util_ui.tr("Device:")) + self.selectRegionPushButton.setText(util_ui.tr("Select region")) + + def _button_clicked(self): + self.desktopAreaSelection = DesktopAreaSelectionWindow(self) + + def _device_changed(self): + index = self.deviceComboBox.currentIndex() + self.selectRegionPushButton.setVisible(index == 0) + self.resolutionComboBox.setVisible(index != 0) + width, height = self._frame_max_sizes[index] + self.resolutionComboBox.clear() + dims = [ + (320, 240), + (640, 360), + (640, 480), + (720, 480), + (1280, 720), + (1920, 1080), + (2560, 1440) + ] + for w, h in dims: + if w <= width and h <= height: + self.resolutionComboBox.addItem(str(w) + ' * ' + str(h)) + + +class PluginsSettings(CenteredWidget): + """ + Plugins settings form + """ + + def __init__(self, plugin_loader): + super().__init__() + self._plugin_loader = plugin_loader + self._window = None + self.initUI() + self.center() + self.retranslateUi() + + def initUI(self): + self.resize(400, 210) + self.setMinimumSize(QtCore.QSize(400, 210)) + self.setMaximumSize(QtCore.QSize(400, 210)) + self.comboBox = QtWidgets.QComboBox(self) + self.comboBox.setGeometry(QtCore.QRect(30, 10, 340, 30)) + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(30, 40, 340, 90)) + self.label.setWordWrap(True) + self.button = QtWidgets.QPushButton(self) + self.button.setGeometry(QtCore.QRect(30, 130, 340, 30)) + self.button.clicked.connect(self.button_click) + self.open = QtWidgets.QPushButton(self) + self.open.setGeometry(QtCore.QRect(30, 170, 340, 30)) + self.open.clicked.connect(self.open_plugin) + self.update_list() + self.comboBox.currentIndexChanged.connect(self.show_data) + self.show_data() + + def retranslateUi(self): + self.setWindowTitle(util_ui.tr("Plugins")) + self.open.setText(util_ui.tr("Open selected plugin")) + + def open_plugin(self): + ind = self.comboBox.currentIndex() + plugin = self.data[ind] + window = self.pl_loader.plugin_window(plugin[-1]) + if window is not None: + self._window = window + self._window.show() + else: + util_ui.message_box(util_ui.tr('No GUI found for this plugin'), util_ui.tr('Error')) + + def update_list(self): + self.comboBox.clear() + data = self._plugin_loader.get_plugins_list() + self.comboBox.addItems(list(map(lambda x: x[0], data))) + self.data = data + + def show_data(self): + ind = self.comboBox.currentIndex() + if len(self.data): + plugin = self.data[ind] + descr = plugin[2] or util_ui.tr("No description available") + self.label.setText(descr) + if plugin[1]: + self.button.setText(util_ui.tr("Disable plugin")) + else: + self.button.setText(util_ui.tr("Enable plugin")) + else: + self.open.setVisible(False) + self.button.setVisible(False) + self.label.setText(util_ui.tr("No plugins found")) + + def button_click(self): + ind = self.comboBox.currentIndex() + plugin = self.data[ind] + self._plugin_loader.toggle_plugin(plugin[-1]) + plugin[1] = not plugin[1] + if plugin[1]: + self.button.setText(util_ui.tr("Disable plugin")) + else: + self.button.setText(util_ui.tr("Enable plugin")) + + +class UpdateSettings(CenteredWidget): + """ + Updates settings form + """ + + def __init__(self, settings, version): + super().__init__() + self._settings = settings + self._version = version + uic.loadUi(get_views_path('update_settings_screen'), self) + self._update_ui() + self.center() + + def closeEvent(self, event): + self._settings['update'] = self.updateModeComboBox.currentIndex() + self._settings.save() + + def _update_ui(self): + self.updatePushButton.clicked.connect(self._update_client) + self.updateModeComboBox.currentIndexChanged.connect(self._update_mode_changed) + self._retranslate_ui() + self.updateModeComboBox.setCurrentIndex(self._settings['update']) + + def _update_mode_changed(self): + index = self.updateModeComboBox.currentIndex() + self.updatePushButton.setEnabled(index > 0) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Update settings")) + self.updateModeLabel.setText(util_ui.tr("Select update mode:")) + self.updatePushButton.setText(util_ui.tr("Update Toxygen")) + self.updateModeComboBox.addItem(util_ui.tr("Disabled")) + self.updateModeComboBox.addItem(util_ui.tr("Manual")) + self.updateModeComboBox.addItem(util_ui.tr("Auto")) + + def _update_client(self): + if not updater.connection_available(): + util_ui.message_box(util_ui.tr('Problems with internet connection'), util_ui.tr("Error")) + return + if not updater.updater_available(): + util_ui.message_box(util_ui.tr('Updater not found'), util_ui.tr("Error")) + return + version = updater.check_for_updates(self._version, self._settings) + if version is not None: + updater.download(version) + util_ui.close_all_windows() + else: + util_ui.message_box(util_ui.tr('Toxygen is up to date'), util_ui.tr("No updates found")) diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py new file mode 100644 index 0000000..8a46fd0 --- /dev/null +++ b/toxygen/ui/messages_widgets.py @@ -0,0 +1,449 @@ +from wrapper.toxcore_enums_and_consts import * +import ui.widgets as widgets +import utils.util as util +import ui.menu as menu +import html as h +import re +from ui.widgets import * +from messenger.messages import MESSAGE_AUTHOR +from file_transfers.file_transfers import * + + +class MessageBrowser(QtWidgets.QTextBrowser): + + def __init__(self, settings, message_edit, smileys_loader, plugin_loader, text, width, message_type, parent=None): + super().__init__(parent) + self.urls = {} + self._message_edit = message_edit + self._smileys_loader = smileys_loader + self._plugin_loader = plugin_loader + self._add_contact = None + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere) + self.document().setTextWidth(width) + self.setOpenExternalLinks(True) + self.setAcceptRichText(True) + self.setOpenLinks(False) + path = smileys_loader.get_smileys_path() + if path is not None: + self.setSearchPaths([path]) + self.document().setDefaultStyleSheet('a { color: #306EFF; }') + text = self.decoratedText(text) + if message_type != TOX_MESSAGE_TYPE['NORMAL']: + self.setHtml('

' + text + '

') + else: + self.setHtml(text) + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPixelSize(settings['message_font_size']) + font.setBold(False) + self.setFont(font) + self.resize(width, self.document().size().height()) + self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse) + self.anchorClicked.connect(self.on_anchor_clicked) + + def contextMenuEvent(self, event): + menu = widgets.create_menu(self.createStandardContextMenu(event.pos())) + quote = menu.addAction(util_ui.tr('Quote selected text')) + quote.triggered.connect(self.quote_text) + text = self.textCursor().selection().toPlainText() + if not text: + quote.setEnabled(False) + else: + sub_menu = self._plugin_loader.get_message_menu(menu, text) + if len(sub_menu): + plugins_menu = menu.addMenu(util_ui.tr('Plugins')) + plugins_menu.addActions(sub_menu) + menu.popup(event.globalPos()) + menu.exec_(event.globalPos()) + del menu + + def quote_text(self): + text = self.textCursor().selection().toPlainText() + if not text: + return + text = '>' + '\n>'.join(text.split('\n')) + if self._message_edit.toPlainText(): + text = '\n' + text + self._message_edit.appendPlainText(text) + + def on_anchor_clicked(self, url): + text = str(url.toString()) + if text.startswith('tox:'): + self._add_contact = menu.AddContact(text[4:]) + self._add_contact.show() + else: + QtGui.QDesktopServices.openUrl(url) + self.clearFocus() + + def addAnimation(self, url, file_name): + movie = QtGui.QMovie(self) + movie.setFileName(file_name) + self.urls[movie] = url + movie.frameChanged[int].connect(lambda x: self.animate(movie)) + movie.start() + + def animate(self, movie): + self.document().addResource(QtGui.QTextDocument.ImageResource, + self.urls[movie], + movie.currentPixmap()) + self.setLineWrapColumnOrWidth(self.lineWrapColumnOrWidth()) + + def decoratedText(self, text): + text = h.escape(text) # replace < and > + exp = QtCore.QRegExp( + '(' + '(?:\\b)((www\\.)|(http[s]?|ftp)://)' + '\\w+\\S+)' + '|(?:\\b)(file:///)([\\S| ]*)' + '|(?:\\b)(tox:[a-zA-Z\\d]{76}$)' + '|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)' + '|(?:\\b)(tox:\\S+@\\S+)') + offset = exp.indexIn(text, 0) + while offset != -1: # add links + url = exp.cap() + if exp.cap(2) == 'www.': + html = '{0}'.format(url) + else: + html = '{0}'.format(url) + text = text[:offset] + html + text[offset + len(exp.cap()):] + offset += len(html) + offset = exp.indexIn(text, offset) + arr = text.split('\n') + for i in range(len(arr)): # quotes + if arr[i].startswith('>'): + arr[i] = '' + arr[i][4:] + '' + text = '
'.join(arr) + text = self._smileys_loader.add_smileys_to_text(text, self) + return text + + +class MessageItem(QtWidgets.QWidget): + """ + Message in messages list + """ + def __init__(self, text_message, settings, message_browser_factory_method, delete_action, parent=None): + QtWidgets.QWidget.__init__(self, parent) + self._message = text_message + self._delete_action = delete_action + self.name = widgets.DataLabel(self) + self.name.setGeometry(QtCore.QRect(2, 2, 95, 23)) + self.name.setTextFormat(QtCore.Qt.PlainText) + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPointSize(11) + font.setBold(True) + if text_message.author is not None: + self.name.setFont(font) + self.name.setText(text_message.author.name) + + self.time = QtWidgets.QLabel(self) + self.time.setGeometry(QtCore.QRect(parent.width() - 60, 0, 50, 25)) + font.setPointSize(10) + font.setBold(False) + self.time.setFont(font) + self._time = text_message.time + if text_message.author and text_message.author.type == MESSAGE_AUTHOR['NOT_SENT']: + movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) + self.time.setMovie(movie) + movie.start() + self.t = True + else: + self.time.setText(util.convert_time(text_message.time)) + self.t = False + + self.message = message_browser_factory_method(text_message.text, parent.width() - 160, + text_message.type, self) + if text_message.type != TOX_MESSAGE_TYPE['NORMAL']: + self.name.setStyleSheet("QLabel { color: #5CB3FF; }") + self.message.setAlignment(QtCore.Qt.AlignCenter) + self.time.setStyleSheet("QLabel { color: #5CB3FF; }") + self.message.setGeometry(QtCore.QRect(100, 0, parent.width() - 160, self.message.height())) + self.setFixedHeight(self.message.height()) + + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.RightButton and event.x() > self.time.x(): + self.listMenu = QtWidgets.QMenu() + delete_item = self.listMenu.addAction(util_ui.tr('Delete message')) + delete_item.triggered.connect(self.delete) + parent_position = self.time.mapToGlobal(QtCore.QPoint(0, 0)) + self.listMenu.move(parent_position) + self.listMenu.show() + + def delete(self): + self._delete_action(self._message) + + def mark_as_sent(self): + if self.t: + self.time.setText(util.convert_time(self._time)) + self.t = False + return True + return False + + def set_avatar(self, pixmap): + self.name.setAlignment(QtCore.Qt.AlignCenter) + self.message.setAlignment(QtCore.Qt.AlignVCenter) + self.setFixedHeight(max(self.height(), 36)) + self.name.setFixedHeight(self.height()) + self.message.setFixedHeight(self.height()) + self.name.setPixmap(pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) + + def select_text(self, text): + tmp = self.message.toHtml() + text = h.escape(text) + strings = re.findall(text, tmp, flags=re.IGNORECASE) + for s in strings: + tmp = self.replace_all(tmp, s) + self.message.setHtml(tmp) + + @staticmethod + def replace_all(text, substring): + i, l = 0, len(substring) + while i < len(text) - l + 1: + index = text[i:].find(substring) + if index == -1: + break + i += index + lgt, rgt = text[i:].find('<'), text[i:].find('>') + if rgt < lgt: + i += rgt + 1 + continue + sub = '{}'.format(substring) + text = text[:i] + sub + text[i + l:] + i += len(sub) + return text + + +class FileTransferItem(QtWidgets.QListWidget): + + def __init__(self, transfer_message, file_transfer_handler, settings, width, parent=None): + + QtWidgets.QListWidget.__init__(self, parent) + self._file_transfer_handler = file_transfer_handler + self.resize(QtCore.QSize(width, 34)) + if transfer_message.state == FILE_TRANSFER_STATE['CANCELLED']: + self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') + elif transfer_message.state in PAUSED_FILE_TRANSFERS: + self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') + else: + self.setStyleSheet('QListWidget { border: 1px solid green; }') + self.state = transfer_message.state + + self.name = DataLabel(self) + self.name.setGeometry(QtCore.QRect(3, 7, 95, 25)) + self.name.setTextFormat(QtCore.Qt.PlainText) + font = QtGui.QFont() + font.setFamily(settings['font']) + font.setPointSize(11) + font.setBold(True) + self.name.setFont(font) + self.name.setText(transfer_message.author.name) + + self.time = QtWidgets.QLabel(self) + self.time.setGeometry(QtCore.QRect(width - 60, 7, 50, 25)) + font.setPointSize(10) + font.setBold(False) + self.time.setFont(font) + self.time.setText(util.convert_time(transfer_message.time)) + + self.cancel = QtWidgets.QPushButton(self) + self.cancel.setGeometry(QtCore.QRect(width - 125, 2, 30, 30)) + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'decline.png')) + icon = QtGui.QIcon(pixmap) + self.cancel.setIcon(icon) + self.cancel.setIconSize(QtCore.QSize(30, 30)) + self.cancel.setVisible(transfer_message.state in ACTIVE_FILE_TRANSFERS or + transfer_message.state == FILE_TRANSFER_STATE['UNSENT']) + self.cancel.clicked.connect( + lambda: self.cancel_transfer(transfer_message.friend_number, transfer_message.file_number)) + self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}') + + self.accept_or_pause = QtWidgets.QPushButton(self) + self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30)) + if transfer_message.state == FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + self.accept_or_pause.setVisible(True) + self.button_update('accept') + elif transfer_message.state in DO_NOT_SHOW_ACCEPT_BUTTON: + self.accept_or_pause.setVisible(False) + elif transfer_message.state == FILE_TRANSFER_STATE['PAUSED_BY_USER']: # setup for continue + self.accept_or_pause.setVisible(True) + self.button_update('resume') + elif transfer_message.state == FILE_TRANSFER_STATE['UNSENT']: + self.accept_or_pause.setVisible(False) + self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') + else: # pause + self.accept_or_pause.setVisible(True) + self.button_update('pause') + self.accept_or_pause.clicked.connect( + lambda: self.accept_or_pause_transfer(transfer_message.friend_number, transfer_message.file_number, + transfer_message.size)) + + self.accept_or_pause.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none}') + + self.pb = QtWidgets.QProgressBar(self) + self.pb.setGeometry(QtCore.QRect(100, 7, 100, 20)) + self.pb.setValue(0) + self.pb.setStyleSheet('QProgressBar { background-color: #302F2F; }') + self.pb.setVisible(transfer_message.state in SHOW_PROGRESS_BAR) + + self.file_name = DataLabel(self) + self.file_name.setGeometry(QtCore.QRect(210, 7, width - 420, 20)) + font.setPointSize(12) + self.file_name.setFont(font) + file_size = transfer_message.size // 1024 + if not file_size: + file_size = '{}B'.format(transfer_message.size) + elif file_size >= 1024: + file_size = '{}MB'.format(file_size // 1024) + else: + file_size = '{}KB'.format(file_size) + file_data = '{} {}'.format(file_size, transfer_message.file_name) + self.file_name.setText(file_data) + self.file_name.setToolTip(transfer_message.file_name) + self.saved_name = transfer_message.file_name + self.time_left = QtWidgets.QLabel(self) + self.time_left.setGeometry(QtCore.QRect(width - 92, 7, 30, 20)) + font.setPointSize(10) + self.time_left.setFont(font) + self.time_left.setVisible(transfer_message.state == FILE_TRANSFER_STATE['RUNNING']) + self.setFocusPolicy(QtCore.Qt.NoFocus) + self.paused = False + + def cancel_transfer(self, friend_number, file_number): + self._file_transfer_handler.cancel_transfer(friend_number, file_number) + self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') + self.cancel.setVisible(False) + self.accept_or_pause.setVisible(False) + self.pb.setVisible(False) + + def accept_or_pause_transfer(self, friend_number, file_number, size): + if self.state == FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: + directory = util_ui.directory_dialog(util_ui.tr('Choose folder')) + self.pb.setVisible(True) + if directory: + self._file_transfer_handler.accept_transfer(directory + '/' + self.saved_name, + friend_number, file_number, size) + self.button_update('pause') + elif self.state == FILE_TRANSFER_STATE['PAUSED_BY_USER']: # resume + self.paused = False + self._file_transfer_handler.resume_transfer(friend_number, file_number) + self.button_update('pause') + self.state = FILE_TRANSFER_STATE['RUNNING'] + else: # pause + self.paused = True + self.state = FILE_TRANSFER_STATE['PAUSED_BY_USER'] + self._file_transfer_handler.pause_transfer(friend_number, file_number) + self.button_update('resume') + self.accept_or_pause.clearFocus() + + def button_update(self, path): + pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), '{}.png'.format(path))) + icon = QtGui.QIcon(pixmap) + self.accept_or_pause.setIcon(icon) + self.accept_or_pause.setIconSize(QtCore.QSize(30, 30)) + + def update_transfer_state(self, state, progress, time): + self.pb.setValue(int(progress * 100)) + if time + 1: + m, s = divmod(time, 60) + self.time_left.setText('{0:02d}:{1:02d}'.format(m, s)) + if self.state != state and self.state in ACTIVE_FILE_TRANSFERS: + if state == FILE_TRANSFER_STATE['CANCELLED']: + self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') + self.cancel.setVisible(False) + self.accept_or_pause.setVisible(False) + self.pb.setVisible(False) + self.state = state + self.time_left.setVisible(False) + elif state == FILE_TRANSFER_STATE['FINISHED']: + self.accept_or_pause.setVisible(False) + self.pb.setVisible(False) + self.cancel.setVisible(False) + self.setStyleSheet('QListWidget { border: 1px solid green; }') + self.state = state + self.time_left.setVisible(False) + elif state == FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']: + self.accept_or_pause.setVisible(False) + self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') + self.state = state + self.time_left.setVisible(False) + elif state == FILE_TRANSFER_STATE['PAUSED_BY_USER']: + self.button_update('resume') # setup button continue + self.setStyleSheet('QListWidget { border: 1px solid green; }') + self.state = state + self.time_left.setVisible(False) + elif state == FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']: + self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') + self.accept_or_pause.setVisible(False) + self.time_left.setVisible(False) + self.pb.setVisible(False) + elif not self.paused: # active + self.pb.setVisible(True) + self.accept_or_pause.setVisible(True) # setup to pause + self.button_update('pause') + self.setStyleSheet('QListWidget { border: 1px solid green; }') + self.state = state + self.time_left.setVisible(True) + + +class UnsentFileItem(FileTransferItem): + + def __init__(self, transfer_message, file_transfer_handler, settings, width, parent=None): + super().__init__(transfer_message, file_transfer_handler, settings, width, parent) + self._time = time + movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) + self.time.setMovie(movie) + movie.start() + self._message_id = transfer_message.message_id + self._friend_number = transfer_message.friend_number + + def cancel_transfer(self, *args): + self._file_transfer_handler.cancel_not_started_transfer(self._friend_number, self._message_id) + + +class InlineImageItem(QtWidgets.QScrollArea): + + def __init__(self, data, width, elem, parent=None): + + QtWidgets.QScrollArea.__init__(self, parent) + self.setFocusPolicy(QtCore.Qt.NoFocus) + self._elem = elem + self._image_label = QtWidgets.QLabel(self) + self._image_label.raise_() + self.setWidget(self._image_label) + self._image_label.setScaledContents(False) + self._pixmap = QtGui.QPixmap() + self._pixmap.loadFromData(data, 'PNG') + self._max_size = width - 30 + self._resize_needed = not (self._pixmap.width() <= self._max_size) + self._full_size = not self._resize_needed + if not self._resize_needed: + self._image_label.setPixmap(self._pixmap) + self.resize(QtCore.QSize(self._max_size + 5, self._pixmap.height() + 5)) + self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height()) + else: + pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio) + self._image_label.setPixmap(pixmap) + self.resize(QtCore.QSize(self._max_size + 5, pixmap.height())) + self._image_label.setGeometry(5, 0, self._max_size + 5, pixmap.height()) + self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) + + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.LeftButton and self._resize_needed: # scale inline + if self._full_size: + pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio) + self._image_label.setPixmap(pixmap) + self.resize(QtCore.QSize(self._max_size, pixmap.height())) + self._image_label.setGeometry(5, 0, pixmap.width(), pixmap.height()) + else: + self._image_label.setPixmap(self._pixmap) + self.resize(QtCore.QSize(self._max_size, self._pixmap.height() + 17)) + self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height()) + self._full_size = not self._full_size + self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) + elif event.button() == QtCore.Qt.RightButton: # save inline + directory = util_ui.directory_dialog(util_ui.tr('Choose folder')) + if directory: + fl = QtCore.QFile(directory + '/toxygen_inline_' + util.curr_time().replace(':', '_') + '.png') + self._pixmap.save(fl, 'PNG') diff --git a/toxygen/ui/password_screen.py b/toxygen/ui/password_screen.py new file mode 100644 index 0000000..bbae7ff --- /dev/null +++ b/toxygen/ui/password_screen.py @@ -0,0 +1,153 @@ +from ui.widgets import CenteredWidget, LineEdit, DialogWithResult +from PyQt5 import QtCore, QtWidgets +import utils.ui as util_ui + + +class PasswordArea(LineEdit): + + def __init__(self, parent): + super().__init__(parent) + self._parent = parent + self.setEchoMode(QtWidgets.QLineEdit.Password) + + def keyPressEvent(self, event): + if event.key() == QtCore.Qt.Key_Return: + self._parent.button_click() + else: + super().keyPressEvent(event) + + +class PasswordScreenBase(CenteredWidget, DialogWithResult): + + def __init__(self, encrypt): + CenteredWidget.__init__(self) + DialogWithResult.__init__(self) + self._encrypt = encrypt + self.initUI() + + def initUI(self): + self.resize(360, 170) + self.setMinimumSize(QtCore.QSize(360, 170)) + self.setMaximumSize(QtCore.QSize(360, 170)) + + self.enter_pass = QtWidgets.QLabel(self) + self.enter_pass.setGeometry(QtCore.QRect(30, 10, 300, 30)) + + self.password = PasswordArea(self) + self.password.setGeometry(QtCore.QRect(30, 50, 300, 30)) + + self.button = QtWidgets.QPushButton(self) + self.button.setGeometry(QtCore.QRect(30, 90, 300, 30)) + self.button.setText(util_ui.tr('OK')) + self.button.clicked.connect(self.button_click) + + self.warning = QtWidgets.QLabel(self) + self.warning.setGeometry(QtCore.QRect(30, 130, 300, 30)) + self.warning.setStyleSheet('QLabel { color: #F70D1A; }') + self.warning.setVisible(False) + + self.retranslateUi() + self.center() + QtCore.QMetaObject.connectSlotsByName(self) + + def button_click(self): + pass + + def keyPressEvent(self, event): + if event.key() == QtCore.Qt.Key_Enter: + self.button_click() + else: + super(PasswordScreenBase, self).keyPressEvent(event) + + def retranslateUi(self): + self.setWindowTitle(util_ui.tr('Enter password')) + self.enter_pass.setText(util_ui.tr('Password:')) + self.warning.setText(util_ui.tr('Incorrect password')) + + +class PasswordScreen(PasswordScreenBase): + + def __init__(self, encrypt, data): + super().__init__(encrypt) + self._data = data + + def button_click(self): + if self.password.text(): + try: + self._encrypt.set_password(self.password.text()) + new_data = self._encrypt.pass_decrypt(self._data) + except Exception as ex: + self.warning.setVisible(True) + print('Decryption error:', ex) + else: + self.close_with_result(new_data) + + +class UnlockAppScreen(PasswordScreenBase): + + def __init__(self, encrypt, callback): + super(UnlockAppScreen, self).__init__(encrypt) + self._callback = callback + self.setWindowFlags(QtCore.Qt.FramelessWindowHint) + + def button_click(self): + if self.password.text(): + if self._encrypt.is_password(self.password.text()): + self._callback() + self.close() + else: + self.warning.setVisible(True) + print('Wrong password!') + + +class SetProfilePasswordScreen(CenteredWidget): + + def __init__(self, encrypt): + super(SetProfilePasswordScreen, self).__init__() + self._encrypt = encrypt + self.initUI() + self.retranslateUi() + self.center() + + def initUI(self): + self.setMinimumSize(QtCore.QSize(700, 200)) + self.setMaximumSize(QtCore.QSize(700, 200)) + self.password = LineEdit(self) + self.password.setGeometry(QtCore.QRect(40, 10, 300, 30)) + self.password.setEchoMode(QtWidgets.QLineEdit.Password) + self.confirm_password = LineEdit(self) + self.confirm_password.setGeometry(QtCore.QRect(40, 50, 300, 30)) + self.confirm_password.setEchoMode(QtWidgets.QLineEdit.Password) + self.set_password = QtWidgets.QPushButton(self) + self.set_password.setGeometry(QtCore.QRect(40, 100, 300, 30)) + self.set_password.clicked.connect(self.new_password) + self.not_match = QtWidgets.QLabel(self) + self.not_match.setGeometry(QtCore.QRect(350, 50, 300, 30)) + self.not_match.setVisible(False) + self.not_match.setStyleSheet('QLabel { color: #BC1C1C; }') + self.warning = QtWidgets.QLabel(self) + self.warning.setGeometry(QtCore.QRect(40, 160, 500, 30)) + self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') + + def retranslateUi(self): + self.setWindowTitle(util_ui.tr('Profile password')) + self.password.setPlaceholderText( + util_ui.tr('Password (at least 8 symbols)')) + self.confirm_password.setPlaceholderText( + util_ui.tr('Confirm password')) + self.set_password.setText( + util_ui.tr('Set password')) + self.not_match.setText(util_ui.tr('Passwords do not match')) + self.warning.setText(util_ui.tr('There is no way to recover lost passwords')) + + def new_password(self): + if self.password.text() == self.confirm_password.text(): + if len(self.password.text()) >= 8: + self._encrypt.set_password(self.password.text()) + self.close() + else: + self.not_match.setText(util_ui.tr('Password must be at least 8 symbols')) + self.not_match.setVisible(True) + else: + self.not_match.setText(util_ui.tr('Passwords do not match')) + self.not_match.setVisible(True) diff --git a/toxygen/ui/peer_screen.py b/toxygen/ui/peer_screen.py new file mode 100644 index 0000000..8f2d5ba --- /dev/null +++ b/toxygen/ui/peer_screen.py @@ -0,0 +1,111 @@ +from ui.widgets import CenteredWidget +from PyQt5 import uic +import utils.util as util +import utils.ui as util_ui +from ui.contact_items import * +import wrapper.toxcore_enums_and_consts as consts + + +class PeerScreen(CenteredWidget): + + def __init__(self, contacts_manager, groups_service, group, peer_id): + super().__init__() + self._contacts_manager = contacts_manager + self._groups_service = groups_service + self._group = group + self._peer = group.get_peer_by_id(peer_id) + + self._roles = { + TOX_GROUP_ROLE['FOUNDER']: util_ui.tr('Administrator'), + TOX_GROUP_ROLE['MODERATOR']: util_ui.tr('Moderator'), + TOX_GROUP_ROLE['USER']: util_ui.tr('User'), + TOX_GROUP_ROLE['OBSERVER']: util_ui.tr('Observer') + } + + uic.loadUi(util.get_views_path('peer_screen'), self) + self._update_ui() + + def _update_ui(self): + self.statusCircle = StatusCircle(self) + self.statusCircle.setGeometry(50, 15, 30, 30) + + self.statusCircle.update(self._peer.status) + self.peerNameLabel.setText(self._peer.name) + self.ignorePeerCheckBox.setChecked(self._peer.is_muted) + self.ignorePeerCheckBox.clicked.connect(self._toggle_ignore) + self.sendPrivateMessagePushButton.clicked.connect(self._send_private_message) + self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key) + self.roleNameLabel.setText(self._get_role_name()) + can_change_role_or_ban = self._can_change_role_or_ban() + self.rolesComboBox.setVisible(can_change_role_or_ban) + self.roleNameLabel.setVisible(not can_change_role_or_ban) + self.banGroupBox.setEnabled(can_change_role_or_ban) + self.banPushButton.clicked.connect(self._ban_peer) + self.kickPushButton.clicked.connect(self._kick_peer) + + self._retranslate_ui() + + self.rolesComboBox.currentIndexChanged.connect(self._role_set) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Peer details')) + self.ignorePeerCheckBox.setText(util_ui.tr('Ignore peer')) + self.roleLabel.setText(util_ui.tr('Role:')) + self.copyPublicKeyPushButton.setText(util_ui.tr('Copy public key')) + self.sendPrivateMessagePushButton.setText(util_ui.tr('Send private message')) + self.banPushButton.setText(util_ui.tr('Ban peer')) + self.kickPushButton.setText(util_ui.tr('Kick peer')) + self.banGroupBox.setTitle(util_ui.tr('Ban peer')) + self.ipBanRadioButton.setText(util_ui.tr('IP')) + self.nickBanRadioButton.setText(util_ui.tr('Nickname')) + self.pkBanRadioButton.setText(util_ui.tr('Public key')) + + self.rolesComboBox.clear() + index = self._group.get_self_peer().role + roles = list(self._roles.values()) + for role in roles[index + 1:]: + self.rolesComboBox.addItem(role) + self.rolesComboBox.setCurrentIndex(self._peer.role - index - 1) + + def _can_change_role_or_ban(self): + self_peer = self._group.get_self_peer() + if self_peer.role > TOX_GROUP_ROLE['MODERATOR']: + return False + + return self_peer.role < self._peer.role + + def _role_set(self): + index = self.rolesComboBox.currentIndex() + all_roles_count = len(self._roles) + diff = all_roles_count - self.rolesComboBox.count() + self._groups_service.set_new_peer_role(self._group, self._peer, index + diff) + + def _get_role_name(self): + return self._roles[self._peer.role] + + def _toggle_ignore(self): + ignore = self.ignorePeerCheckBox.isChecked() + self._groups_service.toggle_ignore_peer(self._group, self._peer, ignore) + + def _send_private_message(self): + self._contacts_manager.add_group_peer(self._group, self._peer) + self.close() + + def _copy_public_key(self): + util_ui.copy_to_clipboard(self._peer.public_key) + + def _ban_peer(self): + ban_type = self._get_ban_type() + self._groups_service.ban_peer(self._group, self._peer.id, ban_type) + self.close() + + def _kick_peer(self): + self._groups_service.kick_peer(self._group, self._peer.id) + self.close() + + def _get_ban_type(self): + if self.ipBanRadioButton.isChecked(): + return consts.TOX_GROUP_BAN_TYPE['IP_PORT'] + elif self.nickBanRadioButton.isChecked(): + return consts.TOX_GROUP_BAN_TYPE['NICK'] + return consts.TOX_GROUP_BAN_TYPE['PUBLIC_KEY'] diff --git a/toxygen/ui/profile_settings_screen.py b/toxygen/ui/profile_settings_screen.py new file mode 100644 index 0000000..2e55d3d --- /dev/null +++ b/toxygen/ui/profile_settings_screen.py @@ -0,0 +1,157 @@ +from ui.widgets import CenteredWidget +import utils.ui as util_ui +from utils.util import join_path, get_images_directory, get_views_path +from user_data.settings import Settings +from PyQt5 import QtGui, QtCore, uic + + +class ProfileSettings(CenteredWidget): + """Form with profile settings such as name, status, TOX ID""" + def __init__(self, profile, profile_manager, settings, toxes): + super().__init__() + self._profile = profile + self._profile_manager = profile_manager + self._settings = settings + self._toxes = toxes + self._auto = False + + uic.loadUi(get_views_path('profile_settings_screen'), self) + + self._init_ui() + self.center() + + def closeEvent(self, event): + self._profile.set_name(self.nameLineEdit.text()) + self._profile.set_status_message(self.statusMessageLineEdit.text()) + self._profile.set_status(self.statusComboBox.currentIndex()) + + def _init_ui(self): + self._auto = Settings.get_auto_profile() == self._profile_manager.get_path() + self.toxIdLabel.setText(self._profile.tox_id) + self.nameLineEdit.setText(self._profile.name) + self.statusMessageLineEdit.setText(self._profile.status_message) + self.defaultProfilePushButton.clicked.connect(self._toggle_auto_profile) + self.copyToxIdPushButton.clicked.connect(self._copy_tox_id) + self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key) + self.changePasswordPushButton.clicked.connect(self._save_password) + self.exportProfilePushButton.clicked.connect(self._export_profile) + self.newNoSpamPushButton.clicked.connect(self._set_new_no_spam) + self.newAvatarPushButton.clicked.connect(self._set_avatar) + self.resetAvatarPushButton.clicked.connect(self._reset_avatar) + + self.invalidPasswordsLabel.setVisible(False) + + self._retranslate_ui() + + if self._profile.status is not None: + self.statusComboBox.setCurrentIndex(self._profile.status) + else: + self.statusComboBox.setVisible(False) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr("Profile settings")) + + self.exportProfilePushButton.setText(util_ui.tr("Export profile")) + self.nameLabel.setText(util_ui.tr("Name:")) + self.statusLabel.setText(util_ui.tr("Status:")) + self.toxIdTitleLabel.setText(util_ui.tr("TOX ID:")) + self.copyToxIdPushButton.setText(util_ui.tr("Copy TOX ID")) + self.newAvatarPushButton.setText(util_ui.tr("New avatar")) + self.resetAvatarPushButton.setText(util_ui.tr("Reset avatar")) + self.newNoSpamPushButton.setText(util_ui.tr("New NoSpam")) + self.profilePasswordLabel.setText(util_ui.tr("Profile password")) + self.passwordLineEdit.setPlaceholderText(util_ui.tr("Password (at least 8 symbols)")) + self.confirmPasswordLineEdit.setPlaceholderText(util_ui.tr("Confirm password")) + self.changePasswordPushButton.setText(util_ui.tr("Set password")) + self.invalidPasswordsLabel.setText(util_ui.tr("Passwords do not match")) + self.emptyPasswordLabel.setText(util_ui.tr("Leaving blank will reset current password")) + self.warningLabel.setText(util_ui.tr("There is no way to recover lost passwords")) + self.statusComboBox.addItem(util_ui.tr("Online")) + self.statusComboBox.addItem(util_ui.tr("Away")) + self.statusComboBox.addItem(util_ui.tr("Busy")) + self.copyPublicKeyPushButton.setText(util_ui.tr("Copy public key")) + + self._set_default_profile_button_text() + + def _toggle_auto_profile(self): + if self._auto: + Settings.reset_auto_profile() + else: + Settings.set_auto_profile(self._profile_manager.get_path()) + self._auto = not self._auto + self._set_default_profile_button_text() + + def _set_default_profile_button_text(self): + if self._auto: + self.defaultProfilePushButton.setText(util_ui.tr("Mark as not default profile")) + else: + self.defaultProfilePushButton.setText(util_ui.tr("Mark as default profile")) + + def _save_password(self): + password = self.passwordLineEdit.text() + confirm_password = self.confirmPasswordLineEdit.text() + if password == confirm_password: + if not len(password) or len(password) >= 8: + self._toxes.set_password(password) + self.close() + else: + self.invalidPasswordsLabel.setText( + util_ui.tr("Password must be at least 8 symbols")) + self.invalidPasswordsLabel.setVisible(True) + else: + self.invalidPasswordsLabel.setText(util_ui.tr("Passwords do not match")) + self.invalidPasswordsLabel.setVisible(True) + + def _copy_tox_id(self): + util_ui.copy_to_clipboard(self._profile.tox_id) + + icon = self._get_accept_icon() + self.copyToxIdPushButton.setIcon(icon) + self.copyToxIdPushButton.setIconSize(QtCore.QSize(10, 10)) + + def _copy_public_key(self): + util_ui.copy_to_clipboard(self._profile.tox_id[:64]) + + icon = self._get_accept_icon() + self.copyPublicKeyPushButton.setIcon(icon) + self.copyPublicKeyPushButton.setIconSize(QtCore.QSize(10, 10)) + + def _set_new_no_spam(self): + self.toxIdLabel.setText(self._profile.set_new_nospam()) + + def _reset_avatar(self): + self._profile.reset_avatar(self._settings['identicons']) + + def _set_avatar(self): + choose = util_ui.tr("Choose avatar") + name = util_ui.file_dialog(choose, 'Images (*.png)') + if not name[0]: + return + bitmap = QtGui.QPixmap(name[0]) + bitmap.scaled(QtCore.QSize(128, 128), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + + byte_array = QtCore.QByteArray() + buffer = QtCore.QBuffer(byte_array) + buffer.open(QtCore.QIODevice.WriteOnly) + bitmap.save(buffer, 'PNG') + + self._profile.set_avatar(bytes(byte_array.data())) + + def _export_profile(self): + directory = util_ui.directory_dialog() + if not directory: + return + + reply = util_ui.question(util_ui.tr('Do you want to move your profile to this location?'), + util_ui.tr('Use new path')) + + self._settings.export(directory) + self._profile.export_db(directory) + self._profile_manager.export_profile(self._settings, directory, reply) + + @staticmethod + def _get_accept_icon(): + pixmap = QtGui.QPixmap(join_path(get_images_directory(), 'accept.png')) + + return QtGui.QIcon(pixmap) + diff --git a/toxygen/ui/self_peer_screen.py b/toxygen/ui/self_peer_screen.py new file mode 100644 index 0000000..cf252d3 --- /dev/null +++ b/toxygen/ui/self_peer_screen.py @@ -0,0 +1,66 @@ +from ui.widgets import CenteredWidget, LineEdit +from PyQt5 import uic +import utils.util as util +import utils.ui as util_ui +from ui.contact_items import * + + +class SelfPeerScreen(CenteredWidget): + + def __init__(self, contacts_manager, groups_service, group): + super().__init__() + self._contacts_manager = contacts_manager + self._groups_service = groups_service + self._group = group + self._peer = group.get_self_peer() + self._roles = { + TOX_GROUP_ROLE['FOUNDER']: util_ui.tr('Administrator'), + TOX_GROUP_ROLE['MODERATOR']: util_ui.tr('Moderator'), + TOX_GROUP_ROLE['USER']: util_ui.tr('User'), + TOX_GROUP_ROLE['OBSERVER']: util_ui.tr('Observer') + } + + uic.loadUi(util.get_views_path('self_peer_screen'), self) + self._update_ui() + + def _update_ui(self): + self.lineEdit = LineEdit(self) + self.lineEdit.setGeometry(140, 40, 400, 30) + self.lineEdit.setText(self._peer.name) + self.lineEdit.textChanged.connect(self._nick_changed) + + self.savePushButton.clicked.connect(self._save) + self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key) + + self._retranslate_ui() + + self.statusComboBox.setCurrentIndex(self._peer.status) + + def _retranslate_ui(self): + self.setWindowTitle(util_ui.tr('Change credentials in group')) + self.lineEdit.setPlaceholderText(util_ui.tr('Your nickname in group')) + self.nameLabel.setText(util_ui.tr('Name:')) + self.roleLabel.setText(util_ui.tr('Role:')) + self.statusLabel.setText(util_ui.tr('Status:')) + self.copyPublicKeyPushButton.setText(util_ui.tr('Copy public key')) + self.savePushButton.setText(util_ui.tr('Save')) + self.roleNameLabel.setText(self._get_role_name()) + self.statusComboBox.addItem(util_ui.tr('Online')) + self.statusComboBox.addItem(util_ui.tr('Away')) + self.statusComboBox.addItem(util_ui.tr('Busy')) + + def _get_role_name(self): + return self._roles[self._peer.role] + + def _nick_changed(self): + nick = self.lineEdit.text() + self.savePushButton.setEnabled(bool(nick)) + + def _save(self): + nick = self.lineEdit.text() + status = self.statusComboBox.currentIndex() + self._groups_service.set_self_info(self._group, nick, status) + self.close() + + def _copy_public_key(self): + util_ui.copy_to_clipboard(self._peer.public_key) diff --git a/toxygen/ui/tray.py b/toxygen/ui/tray.py new file mode 100644 index 0000000..3bfc7f3 --- /dev/null +++ b/toxygen/ui/tray.py @@ -0,0 +1,111 @@ +from PyQt5 import QtWidgets, QtGui, QtCore +from utils.ui import tr +from utils.util import * +from ui.password_screen import UnlockAppScreen +import os.path + + +class SystemTrayIcon(QtWidgets.QSystemTrayIcon): + + leftClicked = QtCore.pyqtSignal() + + def __init__(self, icon, parent=None): + super().__init__(icon, parent) + self.activated.connect(self.icon_activated) + + def icon_activated(self, reason): + if reason == QtWidgets.QSystemTrayIcon.Trigger: + self.leftClicked.emit() + + +class Menu(QtWidgets.QMenu): + + def __init__(self, settings, profile, *args): + super().__init__(*args) + self._settings = settings + self._profile = profile + + def new_status(self, status): + if not self._settings.locked: + self._profile.set_status(status) + self.about_to_show_handler() + self.hide() + + def about_to_show_handler(self): + status = self._profile.status + act = self.act + if status is None or self._settings.locked: + self.actions()[1].setVisible(False) + else: + self.actions()[1].setVisible(True) + act.actions()[0].setChecked(False) + act.actions()[1].setChecked(False) + act.actions()[2].setChecked(False) + act.actions()[status].setChecked(True) + self.actions()[2].setVisible(not self._settings.locked) + + def languageChange(self, *args, **kwargs): + self.actions()[0].setText(tr('Open Toxygen')) + self.actions()[1].setText(tr('Set status')) + self.actions()[2].setText(tr('Exit')) + self.act.actions()[0].setText(tr('Online')) + self.act.actions()[1].setText(tr('Away')) + self.act.actions()[2].setText(tr('Busy')) + + +def init_tray(profile, settings, main_screen, toxes): + icon = os.path.join(get_images_directory(), 'icon.png') + tray = SystemTrayIcon(QtGui.QIcon(icon)) + + menu = Menu(settings, profile) + show = menu.addAction(tr('Open Toxygen')) + sub = menu.addMenu(tr('Set status')) + online = sub.addAction(tr('Online')) + away = sub.addAction(tr('Away')) + busy = sub.addAction(tr('Busy')) + online.setCheckable(True) + away.setCheckable(True) + busy.setCheckable(True) + menu.act = sub + exit = menu.addAction(tr('Exit')) + + def show_window(): + def show(): + if not main_screen.isActiveWindow(): + main_screen.setWindowState( + main_screen.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + main_screen.activateWindow() + main_screen.show() + if not settings.locked: + show() + else: + def correct_pass(): + show() + settings.locked = False + settings.unlockScreen = False + if not settings.unlockScreen: + settings.unlockScreen = True + show_window.screen = UnlockAppScreen(toxes, correct_pass) + show_window.screen.show() + + def tray_activated(reason): + if reason == QtWidgets.QSystemTrayIcon.DoubleClick: + show_window() + + def close_app(): + if not settings.locked: + settings.closing = True + main_screen.close() + + show.triggered.connect(show_window) + exit.triggered.connect(close_app) + menu.aboutToShow.connect(menu.about_to_show_handler) + online.triggered.connect(lambda: menu.new_status(0)) + away.triggered.connect(lambda: menu.new_status(1)) + busy.triggered.connect(lambda: menu.new_status(2)) + + tray.setContextMenu(menu) + tray.show() + tray.activated.connect(tray_activated) + + return tray diff --git a/toxygen/ui/widgets.py b/toxygen/ui/widgets.py new file mode 100644 index 0000000..e7fe623 --- /dev/null +++ b/toxygen/ui/widgets.py @@ -0,0 +1,197 @@ +from PyQt5 import QtCore, QtGui, QtWidgets +import utils.ui as util_ui + + +class DataLabel(QtWidgets.QLabel): + """ + Label with elided text + """ + def setText(self, text): + text = ''.join('\u25AF' if len(bytes(c, 'utf-8')) >= 4 else c for c in text) + metrics = QtGui.QFontMetrics(self.font()) + text = metrics.elidedText(text, QtCore.Qt.ElideRight, self.width()) + super().setText(text) + + +class ComboBox(QtWidgets.QComboBox): + + def __init__(self, *args): + super().__init__(*args) + self.view().setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) + + +class CenteredWidget(QtWidgets.QWidget): + + def __init__(self): + super().__init__() + self.setAttribute(QtCore.Qt.WA_DeleteOnClose) + self.center() + + def center(self): + qr = self.frameGeometry() + cp = QtWidgets.QDesktopWidget().availableGeometry().center() + qr.moveCenter(cp) + self.move(qr.topLeft()) + + +class DialogWithResult(QtWidgets.QWidget): + + def __init__(self, parent=None): + super().__init__(parent) + self._result = None + + def get_result(self): + return self._result + + result = property(get_result) + + def close_with_result(self, result): + self._result = result + self.close() + + +class LineEdit(QtWidgets.QLineEdit): + + def __init__(self, parent=None): + super().__init__(parent) + + def contextMenuEvent(self, event): + menu = create_menu(self.createStandardContextMenu()) + menu.exec_(event.globalPos()) + del menu + + +class QRightClickButton(QtWidgets.QPushButton): + """ + Button with right click support + """ + + rightClicked = QtCore.pyqtSignal() + + def __init__(self, parent=None): + super().__init__(parent) + + def mousePressEvent(self, event): + if event.button() == QtCore.Qt.RightButton: + self.rightClicked.emit() + else: + super().mousePressEvent(event) + + +class RubberBand(QtWidgets.QRubberBand): + + def __init__(self): + super().__init__(QtWidgets.QRubberBand.Rectangle, None) + self.setPalette(QtGui.QPalette(QtCore.Qt.transparent)) + self.pen = QtGui.QPen(QtCore.Qt.blue, 4) + self.pen.setStyle(QtCore.Qt.SolidLine) + self.painter = QtGui.QPainter() + + def paintEvent(self, event): + + self.painter.begin(self) + self.painter.setPen(self.pen) + self.painter.drawRect(event.rect()) + self.painter.end() + + +class RubberBandWindow(QtWidgets.QWidget): + + def __init__(self, parent): + super().__init__() + self.parent = parent + self.setMouseTracking(True) + self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint) + self.showFullScreen() + self.setWindowOpacity(0.5) + self.rubberband = RubberBand() + self.rubberband.setWindowFlags(self.rubberband.windowFlags() | QtCore.Qt.FramelessWindowHint) + self.rubberband.setAttribute(QtCore.Qt.WA_TranslucentBackground) + + def mousePressEvent(self, event): + self.origin = event.pos() + self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize())) + self.rubberband.show() + QtWidgets.QWidget.mousePressEvent(self, event) + + def mouseMoveEvent(self, event): + if self.rubberband.isVisible(): + self.rubberband.setGeometry(QtCore.QRect(self.origin, event.pos()).normalized()) + left = QtGui.QRegion(QtCore.QRect(0, 0, self.rubberband.x(), self.height())) + right = QtGui.QRegion(QtCore.QRect(self.rubberband.x() + self.rubberband.width(), 0, self.width(), self.height())) + top = QtGui.QRegion(0, 0, self.width(), self.rubberband.y()) + bottom = QtGui.QRegion(0, self.rubberband.y() + self.rubberband.height(), self.width(), self.height()) + self.setMask(left + right + top + bottom) + + def keyPressEvent(self, event): + if event.key() == QtCore.Qt.Key_Escape: + self.rubberband.setHidden(True) + self.close() + else: + super().keyPressEvent(event) + + +def create_menu(menu): + """ + :return translated menu + """ + for action in menu.actions(): + text = action.text() + if 'Link Location' in text: + text = text.replace('Copy &Link Location', + util_ui.tr("Copy link location")) + elif '&Copy' in text: + text = text.replace('&Copy', util_ui.tr("Copy")) + elif 'All' in text: + text = text.replace('Select All', util_ui.tr("Select all")) + elif 'Delete' in text: + text = text.replace('Delete', util_ui.tr("Delete")) + elif '&Paste' in text: + text = text.replace('&Paste', util_ui.tr("Paste")) + elif 'Cu&t' in text: + text = text.replace('Cu&t', util_ui.tr("Cut")) + elif '&Undo' in text: + text = text.replace('&Undo', util_ui.tr("Undo")) + elif '&Redo' in text: + text = text.replace('&Redo', util_ui.tr("Redo")) + else: + menu.removeAction(action) + continue + action.setText(text) + return menu + + +class MultilineEdit(CenteredWidget): + + def __init__(self, title, text, save): + super(MultilineEdit, self).__init__() + self.resize(350, 200) + self.setMinimumSize(QtCore.QSize(350, 200)) + self.setMaximumSize(QtCore.QSize(350, 200)) + self.setWindowTitle(title) + self.edit = QtWidgets.QTextEdit(self) + self.edit.setGeometry(QtCore.QRect(0, 0, 350, 150)) + self.edit.setText(text) + self.button = QtWidgets.QPushButton(self) + self.button.setGeometry(QtCore.QRect(0, 150, 350, 50)) + self.button.setText(util_ui.tr("Save")) + self.button.clicked.connect(self.button_click) + self.center() + self.save = save + + def button_click(self): + self.save(self.edit.toPlainText()) + self.close() + + +class LineEditWithEnterSupport(LineEdit): + + def __init__(self, enter_action, parent=None): + super().__init__(parent) + self._action = enter_action + + def keyPressEvent(self, event): + if event.key() == QtCore.Qt.Key_Return: + self._action() + else: + super().keyPressEvent(event) diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py new file mode 100644 index 0000000..128e85e --- /dev/null +++ b/toxygen/ui/widgets_factory.py @@ -0,0 +1,97 @@ +from ui.main_screen_widgets import * +from ui.menu import * +from ui.groups_widgets import * +from ui.peer_screen import * +from ui.self_peer_screen import * +from ui.group_invites_widgets import * +from ui.group_settings_widgets import * +from ui.group_bans_widgets import * +from ui.profile_settings_screen import ProfileSettings + + +class WidgetsFactory: + + def __init__(self, settings, profile, profile_manager, contacts_manager, file_transfer_handler, smiley_loader, + plugin_loader, toxes, version, groups_service, history, contacts_provider): + self._settings = settings + self._profile = profile + self._profile_manager = profile_manager + self._contacts_manager = contacts_manager + self._file_transfer_handler = file_transfer_handler + self._smiley_loader = smiley_loader + self._plugin_loader = plugin_loader + self._toxes = toxes + self._version = version + self._groups_service = groups_service + self._history = history + self._contacts_provider = contacts_provider + + def create_screenshot_window(self, *args): + return ScreenShotWindow(self._file_transfer_handler, self._contacts_manager, *args) + + def create_welcome_window(self): + return WelcomeScreen(self._settings) + + def create_profile_settings_window(self): + return ProfileSettings(self._profile, self._profile_manager, self._settings, self._toxes) + + def create_network_settings_window(self): + return NetworkSettings(self._settings, self._profile.restart) + + def create_audio_settings_window(self): + return AudioSettings(self._settings) + + def create_video_settings_window(self): + return VideoSettings(self._settings) + + def create_update_settings_window(self): + return UpdateSettings(self._settings, self._version) + + def create_plugins_settings_window(self): + return PluginsSettings(self._plugin_loader) + + def create_add_contact_window(self, tox_id): + return AddContact(self._settings, self._contacts_manager, tox_id) + + def create_privacy_settings_window(self): + return PrivacySettings(self._contacts_manager, self._settings) + + def create_interface_settings_window(self): + return InterfaceSettings(self._settings, self._smiley_loader) + + def create_notification_settings_window(self): + return NotificationsSettings(self._settings) + + def create_smiley_window(self, parent): + return SmileyWindow(parent, self._smiley_loader) + + def create_sticker_window(self): + return StickerWindow(self._file_transfer_handler, self._contacts_manager) + + def create_group_screen_window(self): + return CreateGroupScreen(self._groups_service, self._profile) + + def create_join_group_screen_window(self): + return JoinGroupScreen(self._groups_service, self._profile) + + def create_search_screen(self, messages): + return SearchScreen(self._contacts_manager, self._history, messages, messages.parent()) + + def create_peer_screen_window(self, group, peer_id): + return PeerScreen(self._contacts_manager, self._groups_service, group, peer_id) + + def create_self_peer_screen_window(self, group): + return SelfPeerScreen(self._contacts_manager, self._groups_service, group) + + def create_group_invites_window(self): + return GroupInvitesScreen(self._groups_service, self._profile, self._contacts_provider) + + def create_group_management_screen(self, group): + return GroupManagementScreen(self._groups_service, group) + + @staticmethod + def create_group_settings_screen(group): + return GroupSettingsScreen(group) + + def create_groups_bans_screen(self, group): + return GroupBansScreen(self._groups_service, group) diff --git a/toxygen/updater/__init__.py b/toxygen/updater/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/updater/updater.py b/toxygen/updater/updater.py new file mode 100644 index 0000000..329353c --- /dev/null +++ b/toxygen/updater/updater.py @@ -0,0 +1,124 @@ +import utils.util as util +import utils.ui as util_ui +import os +import platform +import urllib +from PyQt5 import QtNetwork, QtCore +import subprocess + + +def connection_available(): + try: + urllib.request.urlopen('http://216.58.192.142', timeout=1) # google.com + return True + except: + return False + + +def updater_available(): + if is_from_sources(): + return os.path.exists(util.curr_directory() + '/toxygen_updater.py') + elif platform.system() == 'Windows': + return os.path.exists(util.curr_directory() + '/toxygen_updater.exe') + else: + return os.path.exists(util.curr_directory() + '/toxygen_updater') + + +def check_for_updates(current_version, settings): + major, minor, patch = list(map(lambda x: int(x), current_version.split('.'))) + versions = generate_versions(major, minor, patch) + for version in versions: + if send_request(version, settings): + return version + return None # no new version was found + + +def is_from_sources(): + return __file__.endswith('.py') + + +def test_url(version): + return 'https://github.com/toxygen-project/toxygen/releases/tag/v' + version + + +def get_url(version): + if is_from_sources(): + return 'https://github.com/toxygen-project/toxygen/archive/v' + version + '.zip' + else: + if platform.system() == 'Windows': + name = 'toxygen_windows.zip' + elif util.is_64_bit(): + name = 'toxygen_linux_64.tar.gz' + else: + name = 'toxygen_linux.tar.gz' + return 'https://github.com/toxygen-project/toxygen/releases/download/v{}/{}'.format(version, name) + + +def get_params(url, version): + if is_from_sources(): + if platform.system() == 'Windows': + return ['python', 'toxygen_updater.py', url, version] + else: + return ['python3', 'toxygen_updater.py', url, version] + elif platform.system() == 'Windows': + return [util.curr_directory() + '/toxygen_updater.exe', url, version] + else: + return ['./toxygen_updater', url, version] + + +def download(version): + os.chdir(util.curr_directory()) + url = get_url(version) + params = get_params(url, version) + print('Updating Toxygen') + util.log('Updating Toxygen') + try: + subprocess.Popen(params) + except Exception as ex: + util.log('Exception: running updater failed with ' + str(ex)) + + +def send_request(version, settings): + netman = QtNetwork.QNetworkAccessManager() + proxy = QtNetwork.QNetworkProxy() + if settings['proxy_type']: + proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy if settings['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) + proxy.setHostName(settings['proxy_host']) + proxy.setPort(settings['proxy_port']) + netman.setProxy(proxy) + url = test_url(version) + try: + request = QtNetwork.QNetworkRequest() + request.setUrl(QtCore.QUrl(url)) + reply = netman.get(request) + while not reply.isFinished(): + QtCore.QThread.msleep(1) + QtCore.QCoreApplication.processEvents() + attr = reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute) + return attr is not None and 200 <= attr < 300 + except Exception as ex: + util.log('TOXYGEN UPDATER ERROR: ' + str(ex)) + return False + + +def generate_versions(major, minor, patch): + new_major = '.'.join([str(major + 1), '0', '0']) + new_minor = '.'.join([str(major), str(minor + 1), '0']) + new_patch = '.'.join([str(major), str(minor), str(patch + 1)]) + return new_major, new_minor, new_patch + + +def start_update_if_needed(version, settings): + updating = False + if settings['update'] and updater_available() and connection_available(): # auto update + version = check_for_updates(version, settings) + if version is not None: + if settings['update'] == 2: + download(version) + updating = True + else: + reply = util_ui.question(util_ui.tr('Update for Toxygen was found. Download and install it?')) + if reply: + download(version) + updating = True + return updating diff --git a/toxygen/user_data/__init__.py b/toxygen/user_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/user_data/backup_service.py b/toxygen/user_data/backup_service.py new file mode 100644 index 0000000..bb0cef9 --- /dev/null +++ b/toxygen/user_data/backup_service.py @@ -0,0 +1,40 @@ +import os.path +from utils.util import get_profile_name_from_path, join_path + + +class BackupService: + + def __init__(self, settings, profile_manager): + self._settings = settings + self._profile_name = get_profile_name_from_path(profile_manager.get_path()) + + settings.settings_saved_event.add_callback(self._settings_saved) + profile_manager.profile_saved_event.add_callback(self._profile_saved) + + def _settings_saved(self, data): + if not self._check_if_should_save_backup(): + return + + file_path = join_path(self._get_backup_directory(), self._profile_name + '.json') + + with open(file_path, 'wt') as fl: + fl.write(data) + + def _profile_saved(self, data): + if not self._check_if_should_save_backup(): + return + + file_path = join_path(self._get_backup_directory(), self._profile_name + '.tox') + + with open(file_path, 'wb') as fl: + fl.write(data) + + def _check_if_should_save_backup(self): + backup_directory = self._get_backup_directory() + if backup_directory is None: + return False + + return os.path.exists(backup_directory) and os.path.isdir(backup_directory) + + def _get_backup_directory(self): + return self._settings['backup_directory'] diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py new file mode 100644 index 0000000..05e2f2d --- /dev/null +++ b/toxygen/user_data/profile_manager.py @@ -0,0 +1,90 @@ +import utils.util as util +import os +from user_data.settings import Settings +from common.event import Event + + +class ProfileManager: + """ + Class with methods for search, load and save profiles + """ + def __init__(self, toxes, path): + self._toxes = toxes + self._path = path + self._directory = os.path.dirname(path) + self._profile_saved_event = Event() + # create /avatars if not exists: + avatars_directory = util.join_path(self._directory, 'avatars') + if not os.path.exists(avatars_directory): + os.makedirs(avatars_directory) + + # ----------------------------------------------------------------------------------------------------------------- + # Properties + # ----------------------------------------------------------------------------------------------------------------- + + def get_profile_saved_event(self): + return self._profile_saved_event + + profile_saved_event = property(get_profile_saved_event) + + # ----------------------------------------------------------------------------------------------------------------- + # Public methods + # ----------------------------------------------------------------------------------------------------------------- + + def open_profile(self): + with open(self._path, 'rb') as fl: + data = fl.read() + if data: + return data + else: + raise IOError('Save file has zero size!') + + def get_dir(self): + return self._directory + + def get_path(self): + return self._path + + def save_profile(self, data): + if self._toxes.has_password(): + data = self._toxes.pass_encrypt(data) + with open(self._path, 'wb') as fl: + fl.write(data) + print('Profile saved successfully') + + self._profile_saved_event(data) + + def export_profile(self, settings, new_path, use_new_path): + path = new_path + os.path.basename(self._path) + with open(self._path, 'rb') as fin: + data = fin.read() + with open(path, 'wb') as fout: + fout.write(data) + print('Profile exported successfully') + util.copy(self._directory + 'avatars', new_path + 'avatars') + if use_new_path: + self._path = new_path + os.path.basename(self._path) + self._directory = new_path + settings.update_path(new_path) + + @staticmethod + def find_profiles(): + """ + Find available tox profiles + """ + path = Settings.get_default_path() + result = [] + # check default path + if not os.path.exists(path): + os.makedirs(path) + for fl in os.listdir(path): + if fl.endswith('.tox'): + name = fl[:-4] + result.append((path, name)) + path = util.get_base_directory(__file__) + # check current directory + for fl in os.listdir(path): + if fl.endswith('.tox'): + name = fl[:-4] + result.append((path + '/', name)) + return result diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py new file mode 100644 index 0000000..71422c2 --- /dev/null +++ b/toxygen/user_data/settings.py @@ -0,0 +1,244 @@ +import json +from utils.util import * +import pyaudio +from common.event import Event + + +class Settings(dict): + """ + Settings of current profile + global app settings + """ + + def __init__(self, toxes, path): + self._path = path + self._profile_path = path.replace('.json', '.tox') + self._toxes = toxes + self._settings_saved_event = Event() + if os.path.isfile(path): + with open(path, 'rb') as fl: + data = fl.read() + try: + if toxes.is_data_encrypted(data): + data = toxes.pass_decrypt(data) + info = json.loads(str(data, 'utf-8')) + except Exception as ex: + info = Settings.get_default_settings() + log('Parsing settings error: ' + str(ex)) + super().__init__(info) + self._upgrade() + else: + super().__init__(Settings.get_default_settings()) + self.save() + self.locked = False + self.closing = False + self.unlockScreen = False + p = pyaudio.PyAudio() + input_devices = output_devices = 0 + for i in range(p.get_device_count()): + device = p.get_device_info_by_index(i) + if device["maxInputChannels"]: + input_devices += 1 + if device["maxOutputChannels"]: + output_devices += 1 + self.audio = {'input': p.get_default_input_device_info()['index'] if input_devices else -1, + 'output': p.get_default_output_device_info()['index'] if output_devices else -1, + 'enabled': input_devices and output_devices} + self.video = {'device': -1, 'width': 640, 'height': 480, 'x': 0, 'y': 0} + + # ----------------------------------------------------------------------------------------------------------------- + # Properties + # ----------------------------------------------------------------------------------------------------------------- + + def get_settings_saved_event(self): + return self._settings_saved_event + + settings_saved_event = property(get_settings_saved_event) + + # ----------------------------------------------------------------------------------------------------------------- + # Public methods + # ----------------------------------------------------------------------------------------------------------------- + + def save(self): + text = json.dumps(self) + if self._toxes.has_password(): + text = bytes(self._toxes.pass_encrypt(bytes(text, 'utf-8'))) + else: + text = bytes(text, 'utf-8') + with open(self._path, 'wb') as fl: + fl.write(text) + + self._settings_saved_event(text) + + def close(self): + path = self._profile_path + '.lock' + if os.path.isfile(path): + os.remove(path) + + def set_active_profile(self): + """ + Mark current profile as active + """ + path = self._profile_path + '.lock' + with open(path, 'w') as fl: + fl.write('active') + + def export(self, path): + text = json.dumps(self) + name = os.path.basename(self._path) + with open(join_path(path, str(name)), 'w') as fl: + fl.write(text) + + def update_path(self, new_path): + self._path = new_path + self.save() + + # ----------------------------------------------------------------------------------------------------------------- + # Static methods + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def get_auto_profile(): + p = Settings.get_global_settings_path() + if not os.path.isfile(p): + return None + with open(p) as fl: + data = fl.read() + try: + auto = json.loads(data) + except Exception as ex: + log(str(ex)) + auto = {} + if 'profile_path' in auto: + path = str(auto['profile_path']) + if not os.path.isabs(path): + path = join_path(path, curr_directory(__file__)) + if os.path.isfile(path): + return path + + @staticmethod + def set_auto_profile(path): + p = Settings.get_global_settings_path() + if os.path.isfile(p): + with open(p) as fl: + data = fl.read() + data = json.loads(data) + else: + data = {} + data['profile_path'] = str(path) + with open(p, 'w') as fl: + fl.write(json.dumps(data)) + + @staticmethod + def reset_auto_profile(): + p = Settings.get_global_settings_path() + if os.path.isfile(p): + with open(p) as fl: + data = fl.read() + data = json.loads(data) + else: + data = {} + if 'profile_path' in data: + del data['profile_path'] + with open(p, 'w') as fl: + fl.write(json.dumps(data)) + + @staticmethod + def is_active_profile(profile_path): + return os.path.isfile(profile_path + '.lock') + + @staticmethod + def get_default_settings(): + """ + Default profile settings + """ + return { + 'theme': 'dark', + 'ipv6_enabled': False, + 'udp_enabled': True, + 'proxy_type': 0, + 'proxy_host': '127.0.0.1', + 'proxy_port': 9050, + 'start_port': 0, + 'end_port': 0, + 'tcp_port': 0, + 'notifications': True, + 'sound_notifications': False, + 'language': 'English', + 'save_history': False, + 'allow_inline': True, + 'allow_auto_accept': True, + 'auto_accept_path': None, + 'sorting': 0, + 'auto_accept_from_friends': [], + 'paused_file_transfers': {}, + 'resend_files': True, + 'friends_aliases': [], + 'show_avatars': False, + 'typing_notifications': False, + 'calls_sound': True, + 'blocked': [], + 'plugins': [], + 'notes': {}, + 'smileys': True, + 'smiley_pack': 'default', + 'mirror_mode': False, + 'width': 920, + 'height': 500, + 'x': 400, + 'y': 400, + 'message_font_size': 14, + 'unread_color': 'red', + 'save_unsent_only': False, + 'compact_mode': False, + 'identicons': True, + 'show_welcome_screen': True, + 'close_app': 0, + 'font': 'Times New Roman', + 'update': 1, + 'group_notifications': True, + 'download_nodes_list': False, + 'notify_all_gc': False, + 'lan_discovery': True, + 'backup_directory': None + } + + @staticmethod + def supported_languages(): + return { + 'English': 'en_EN', + 'French': 'fr_FR', + 'Russian': 'ru_RU', + 'Ukrainian': 'uk_UA' + } + + @staticmethod + def built_in_themes(): + return { + 'dark': 'dark_style.qss', + 'default': 'style.qss' + } + + @staticmethod + def get_global_settings_path(): + return os.path.join(get_base_directory(), 'toxygen.json') + + @staticmethod + def get_default_path(): + system = get_platform() + if system == 'Windows': + return os.getenv('APPDATA') + '/Tox/' + elif system == 'Darwin': + return os.getenv('HOME') + '/Library/Application Support/Tox/' + else: + return os.getenv('HOME') + '/.config/tox/' + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _upgrade(self): + default = Settings.get_default_settings() + for key in default: + if key not in self: + print(key) + self[key] = default[key] diff --git a/toxygen/user_data/toxes.py b/toxygen/user_data/toxes.py new file mode 100644 index 0000000..982f287 --- /dev/null +++ b/toxygen/user_data/toxes.py @@ -0,0 +1,24 @@ + +class ToxES: + + def __init__(self, tox_encrypt_save): + self._tox_encrypt_save = tox_encrypt_save + self._password = None + + def set_password(self, password): + self._password = password + + def has_password(self): + return bool(self._password) + + def is_password(self, password): + return self._password == password + + def is_data_encrypted(self, data): + return len(data) > 0 and self._tox_encrypt_save.is_data_encrypted(data) + + def pass_encrypt(self, data): + return self._tox_encrypt_save.pass_encrypt(data, self._password) + + def pass_decrypt(self, data): + return self._tox_encrypt_save.pass_decrypt(data, self._password) diff --git a/toxygen/utils/__init__.py b/toxygen/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/utils/ui.py b/toxygen/utils/ui.py new file mode 100644 index 0000000..d2d7122 --- /dev/null +++ b/toxygen/utils/ui.py @@ -0,0 +1,54 @@ +from PyQt5 import QtWidgets +import utils.util as util + + +def tr(s): + return QtWidgets.QApplication.translate('Toxygen', s) + + +def question(text, title=None): + reply = QtWidgets.QMessageBox.question(None, title or 'Toxygen', text, + QtWidgets.QMessageBox.Yes, + QtWidgets.QMessageBox.No) + return reply == QtWidgets.QMessageBox.Yes + + +def message_box(text, title=None): + m_box = QtWidgets.QMessageBox() + m_box.setText(tr(text)) + m_box.setWindowTitle(title or 'Toxygen') + m_box.exec_() + + +def text_dialog(text, title='', default_value=''): + text, ok = QtWidgets.QInputDialog.getText(None, title, text, QtWidgets.QLineEdit.Normal, default_value) + + return text, ok + + +def directory_dialog(caption=''): + return QtWidgets.QFileDialog.getExistingDirectory(None, caption, util.curr_directory(), + QtWidgets.QFileDialog.DontUseNativeDialog) + + +def file_dialog(caption, file_filter=None): + return QtWidgets.QFileDialog.getOpenFileName(None, caption, util.curr_directory(), file_filter, + options=QtWidgets.QFileDialog.DontUseNativeDialog) + + +def save_file_dialog(caption, filter=None): + return QtWidgets.QFileDialog.getSaveFileName(None, caption, util.curr_directory(), + filter=filter, + options=QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) + + +def close_all_windows(): + QtWidgets.QApplication.closeAllWindows() + + +def copy_to_clipboard(text): + clipboard = QtWidgets.QApplication.clipboard() + clipboard.setText(text) + + +# TODO: all dialogs diff --git a/toxygen/utils/util.py b/toxygen/utils/util.py new file mode 100644 index 0000000..5bd5c3a --- /dev/null +++ b/toxygen/utils/util.py @@ -0,0 +1,170 @@ +import os +import time +import shutil +import sys +import re +import platform +import datetime + + +def cached(func): + saved_result = None + + def wrapped_func(): + nonlocal saved_result + if saved_result is None: + saved_result = func() + + return saved_result + + return wrapped_func + + +def log(data): + try: + with open(join_path(curr_directory(), 'logs.log'), 'a') as fl: + fl.write(str(data) + '\n') + except Exception as ex: + print(ex) + + +def curr_directory(current_file=None): + return os.path.dirname(os.path.realpath(current_file or __file__)) + + +def get_base_directory(current_file=None): + return os.path.dirname(curr_directory(current_file or __file__)) + + +@cached +def get_images_directory(): + return get_app_directory('images') + + +@cached +def get_styles_directory(): + return get_app_directory('styles') + + +@cached +def get_sounds_directory(): + return get_app_directory('sounds') + + +@cached +def get_stickers_directory(): + return get_app_directory('stickers') + + +@cached +def get_smileys_directory(): + return get_app_directory('smileys') + + +@cached +def get_translations_directory(): + return get_app_directory('translations') + + +@cached +def get_plugins_directory(): + return get_app_directory('plugins') + + +@cached +def get_libs_directory(): + return get_app_directory('libs') + + +def get_app_directory(directory_name): + return os.path.join(get_base_directory(), directory_name) + + +def get_profile_name_from_path(path): + return os.path.basename(path)[:-4] + + +def get_views_path(view_name): + ui_folder = os.path.join(get_base_directory(), 'ui') + views_folder = os.path.join(ui_folder, 'views') + + return os.path.join(views_folder, view_name + '.ui') + + +def curr_time(): + return time.strftime('%H:%M') + + +def get_unix_time(): + return int(time.time()) + + +def join_path(a, b): + return os.path.join(a, b) + + +def file_exists(file_path): + return os.path.exists(file_path) + + +def copy(src, dest): + if not os.path.exists(dest): + os.makedirs(dest) + src_files = os.listdir(src) + for file_name in src_files: + full_file_name = os.path.join(src, file_name) + if os.path.isfile(full_file_name): + shutil.copy(full_file_name, dest) + else: + copy(full_file_name, os.path.join(dest, file_name)) + + +def remove(folder): + if os.path.isdir(folder): + shutil.rmtree(folder) + + +def convert_time(t): + offset = time.timezone + time_offset() * 60 + sec = int(t) - offset + m, s = divmod(sec, 60) + h, m = divmod(m, 60) + d, h = divmod(h, 24) + return '%02d:%02d' % (h, m) + + +@cached +def time_offset(): + hours = int(time.strftime('%H')) + minutes = int(time.strftime('%M')) + sec = int(time.time()) - time.timezone + m, s = divmod(sec, 60) + h, m = divmod(m, 60) + d, h = divmod(h, 24) + result = hours * 60 + minutes - h * 60 - m + return result + + +def unix_time_to_long_str(unix_time): + date_time = datetime.datetime.utcfromtimestamp(unix_time) + + return date_time.strftime('%Y-%m-%d %H:%M:%S') + + +@cached +def is_64_bit(): + return sys.maxsize > 2 ** 32 + + +def is_re_valid(regex): + try: + re.compile(regex) + except re.error: + return False + else: + return True + + +@cached +def get_platform(): + return platform.system() diff --git a/toxygen/wrapper/__init__.py b/toxygen/wrapper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toxygen/wrapper/libtox.py b/toxygen/wrapper/libtox.py new file mode 100644 index 0000000..01d41f1 --- /dev/null +++ b/toxygen/wrapper/libtox.py @@ -0,0 +1,61 @@ +from ctypes import CDLL +import utils.util as util + + +class LibToxCore: + + def __init__(self): + platform = util.get_platform() + if platform == 'Windows': + self._libtoxcore = CDLL(util.join_path(util.get_libs_directory(), 'libtox.dll')) + elif platform == 'Darwin': + self._libtoxcore = CDLL('libtoxcore.dylib') + else: + # libtoxcore and libsodium must be installed in your os + try: + self._libtoxcore = CDLL('libtoxcore.so') + except: + self._libtoxcore = CDLL(util.join_path(util.get_libs_directory(), 'libtoxcore.so')) + + def __getattr__(self, item): + return self._libtoxcore.__getattr__(item) + + +class LibToxAV: + + def __init__(self): + platform = util.get_platform() + if platform == 'Windows': + # on Windows av api is in libtox.dll + self._libtoxav = CDLL(util.join_path(util.get_libs_directory(), 'libtox.dll')) + elif platform == 'Darwin': + self._libtoxav = CDLL('libtoxcore.dylib') + else: + # /usr/lib/libtoxcore.so must exists + try: + self._libtoxav = CDLL('libtoxcore.so') + except: + self._libtoxav = CDLL(util.join_path(util.get_libs_directory(), 'libtoxcore.so')) + + def __getattr__(self, item): + return self._libtoxav.__getattr__(item) + + +class LibToxEncryptSave: + + def __init__(self): + platform = util.get_platform() + if platform == 'Windows': + # on Windows profile encryption api is in libtox.dll + self._lib_tox_encrypt_save = CDLL(util.join_path(util.get_libs_directory(), 'libtox.dll')) + elif platform == 'Darwin': + self._lib_tox_encrypt_save = CDLL('libtoxcore.dylib') + else: + # /usr/lib/libtoxcore.so must exists + try: + self._lib_tox_encrypt_save = CDLL('libtoxcore.so') + except: + self._lib_tox_encrypt_save = CDLL(util.join_path(util.get_libs_directory(), 'libtoxcore.so')) + + def __getattr__(self, item): + return self._lib_tox_encrypt_save.__getattr__(item) diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py new file mode 100644 index 0000000..21b0ebc --- /dev/null +++ b/toxygen/wrapper/tox.py @@ -0,0 +1,2532 @@ +# -*- coding: utf-8 -*- +from ctypes import * +from wrapper.toxcore_enums_and_consts import * +from wrapper.toxav import ToxAV +from wrapper.libtox import LibToxCore + + +class ToxOptions(Structure): + _fields_ = [ + ('ipv6_enabled', c_bool), + ('udp_enabled', c_bool), + ('local_discovery_enabled', c_bool), + ('proxy_type', c_int), + ('proxy_host', c_char_p), + ('proxy_port', c_uint16), + ('start_port', c_uint16), + ('end_port', c_uint16), + ('tcp_port', c_uint16), + ('hole_punching_enabled', c_bool), + ('savedata_type', c_int), + ('savedata_data', c_char_p), + ('savedata_length', c_size_t), + ('log_callback', c_void_p), + ('log_user_data', c_void_p) + ] + + +class GroupChatSelfPeerInfo(Structure): + _fields_ = [ + ('nick', c_char_p), + ('nick_length', c_uint8), + ('user_status', c_int) + ] + + +def string_to_bin(tox_id): + return c_char_p(bytes.fromhex(tox_id)) if tox_id is not None else None + + +def bin_to_string(raw_id, length): + res = ''.join('{:02x}'.format(ord(raw_id[i])) for i in range(length)) + return res.upper() + + +class Tox: + libtoxcore = LibToxCore() + + def __init__(self, tox_options=None, tox_pointer=None): + """ + Creates and initialises a new Tox instance with the options passed. + + This function will bring the instance into a valid state. Running the event loop with a new instance will + operate correctly. + + :param tox_options: An options object. If this parameter is None, the default options are used. + :param tox_pointer: Tox instance pointer. If this parameter is not None, tox_options will be ignored. + """ + if tox_pointer is not None: + self._tox_pointer = tox_pointer + else: + tox_err_new = c_int() + f = Tox.libtoxcore.tox_new + f.restype = POINTER(c_void_p) + self._tox_pointer = f(tox_options, byref(tox_err_new)) + tox_err_new = tox_err_new.value + if tox_err_new == TOX_ERR_NEW['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_new == TOX_ERR_NEW['MALLOC']: + raise MemoryError('The function was unable to allocate enough ' + 'memory to store the internal structures for the Tox object.') + elif tox_err_new == TOX_ERR_NEW['PORT_ALLOC']: + raise RuntimeError('The function was unable to bind to a port. This may mean that all ports have ' + 'already been bound, e.g. by other Tox instances, or it may mean a permission error.' + ' You may be able to gather more information from errno.') + elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_TYPE']: + raise ArgumentError('proxy_type was invalid.') + elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_HOST']: + raise ArgumentError('proxy_type was valid but the proxy_host passed had an invalid format or was NULL.') + elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_PORT']: + raise ArgumentError('proxy_type was valid, but the proxy_port was invalid.') + elif tox_err_new == TOX_ERR_NEW['PROXY_NOT_FOUND']: + raise ArgumentError('The proxy address passed could not be resolved.') + elif tox_err_new == TOX_ERR_NEW['LOAD_ENCRYPTED']: + raise ArgumentError('The byte array to be loaded contained an encrypted save.') + elif tox_err_new == TOX_ERR_NEW['LOAD_BAD_FORMAT']: + raise ArgumentError('The data format was invalid. This can happen when loading data that was saved by' + ' an older version of Tox, or when the data has been corrupted. When loading from' + ' badly formatted data, some data may have been loaded, and the rest is discarded.' + ' Passing an invalid length parameter also causes this error.') + + self.self_connection_status_cb = None + self.friend_name_cb = None + self.friend_status_message_cb = None + self.friend_status_cb = None + self.friend_connection_status_cb = None + self.friend_request_cb = None + self.friend_read_receipt_cb = None + self.friend_typing_cb = None + self.friend_message_cb = None + self.file_recv_control_cb = None + self.file_chunk_request_cb = None + self.file_recv_cb = None + self.file_recv_chunk_cb = None + self.friend_lossy_packet_cb = None + self.friend_lossless_packet_cb = None + self.group_moderation_cb = None + self.group_join_fail_cb = None + self.group_self_join_cb = None + self.group_invite_cb = None + self.group_custom_packet_cb = None + self.group_private_message_cb = None + self.group_message_cb = None + self.group_password_cb = None + self.group_peer_limit_cb = None + self.group_privacy_state_cb = None + self.group_topic_cb = None + self.group_peer_status_cb = None + self.group_peer_name_cb = None + self.group_peer_exit_cb = None + self.group_peer_join_cb = None + + self.AV = ToxAV(self._tox_pointer) + + def kill(self): + del self.AV + Tox.libtoxcore.tox_kill(self._tox_pointer) + + # ----------------------------------------------------------------------------------------------------------------- + # Startup options + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def options_default(tox_options): + """ + Initialises a Tox_Options object with the default options. + + The result of this function is independent of the original options. All values will be overwritten, no values + will be read (so it is permissible to pass an uninitialised object). + + If options is NULL, this function has no effect. + + :param tox_options: A pointer to options object to be filled with default options. + """ + Tox.libtoxcore.tox_options_default(tox_options) + + @staticmethod + def options_new(): + """ + Allocates a new Tox_Options object and initialises it with the default options. This function can be used to + preserve long term ABI compatibility by giving the responsibility of allocation and deallocation to the Tox + library. + + Objects returned from this function must be freed using the tox_options_free function. + + :return: A pointer to new ToxOptions object with default options or raise MemoryError. + """ + tox_err_options_new = c_int() + f = Tox.libtoxcore.tox_options_new + f.restype = POINTER(ToxOptions) + result = f(byref(tox_err_options_new)) + tox_err_options_new = tox_err_options_new.value + if tox_err_options_new == TOX_ERR_OPTIONS_NEW['OK']: + return result + elif tox_err_options_new == TOX_ERR_OPTIONS_NEW['MALLOC']: + raise MemoryError('The function failed to allocate enough memory for the options struct.') + + @staticmethod + def options_free(tox_options): + """ + Releases all resources associated with an options objects. + + Passing a pointer that was not returned by tox_options_new results in undefined behaviour. + + :param tox_options: A pointer to new ToxOptions object + """ + Tox.libtoxcore.tox_options_free(tox_options) + + # ----------------------------------------------------------------------------------------------------------------- + # Creation and destruction + # ----------------------------------------------------------------------------------------------------------------- + + def get_savedata_size(self): + """ + Calculates the number of bytes required to store the tox instance with tox_get_savedata. + This function cannot fail. The result is always greater than 0. + + :return: number of bytes + """ + return Tox.libtoxcore.tox_get_savedata_size(self._tox_pointer) + + def get_savedata(self, savedata=None): + """ + Store all information associated with the tox instance to a byte array. + + :param savedata: pointer (c_char_p) to a memory region large enough to store the tox instance data. + Call tox_get_savedata_size to find the number of bytes required. If this parameter is None, this function + allocates memory for the tox instance data. + :return: pointer (c_char_p) to a memory region with the tox instance data + """ + if savedata is None: + savedata_size = self.get_savedata_size() + savedata = create_string_buffer(savedata_size) + Tox.libtoxcore.tox_get_savedata(self._tox_pointer, savedata) + return savedata[:] + + # ----------------------------------------------------------------------------------------------------------------- + # Connection lifecycle and event loop + # ----------------------------------------------------------------------------------------------------------------- + + def bootstrap(self, address, port, public_key): + """ + Sends a "get nodes" request to the given bootstrap node with IP, port, and public key to setup connections. + + This function will attempt to connect to the node using UDP. You must use this function even if + Tox_Options.udp_enabled was set to false. + + :param address: The hostname or IP address (IPv4 or IPv6) of the node. + :param port: The port on the host on which the bootstrap Tox instance is listening. + :param public_key: The long term public key of the bootstrap node (TOX_PUBLIC_KEY_SIZE bytes). + :return: True on success. + """ + address = bytes(address, 'utf-8') + tox_err_bootstrap = c_int() + result = Tox.libtoxcore.tox_bootstrap(self._tox_pointer, c_char_p(address), c_uint16(port), + string_to_bin(public_key), byref(tox_err_bootstrap)) + tox_err_bootstrap = tox_err_bootstrap.value + if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']: + return bool(result) + elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_HOST']: + raise ArgumentError('The address could not be resolved to an IP ' + 'address, or the IP address passed was invalid.') + elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_PORT']: + raise ArgumentError('The port passed was invalid. The valid port range is (1, 65535).') + + def add_tcp_relay(self, address, port, public_key): + """ + Adds additional host:port pair as TCP relay. + + This function can be used to initiate TCP connections to different ports on the same bootstrap node, or to add + TCP relays without using them as bootstrap nodes. + + :param address: The hostname or IP address (IPv4 or IPv6) of the TCP relay. + :param port: The port on the host on which the TCP relay is listening. + :param public_key: The long term public key of the TCP relay (TOX_PUBLIC_KEY_SIZE bytes). + :return: True on success. + """ + address = bytes(address, 'utf-8') + tox_err_bootstrap = c_int() + result = Tox.libtoxcore.tox_add_tcp_relay(self._tox_pointer, c_char_p(address), c_uint16(port), + string_to_bin(public_key), byref(tox_err_bootstrap)) + tox_err_bootstrap = tox_err_bootstrap.value + if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']: + return bool(result) + elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_HOST']: + raise ArgumentError('The address could not be resolved to an IP ' + 'address, or the IP address passed was invalid.') + elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_PORT']: + raise ArgumentError('The port passed was invalid. The valid port range is (1, 65535).') + + def self_get_connection_status(self): + """ + Return whether we are connected to the DHT. The return value is equal to the last value received through the + `self_connection_status` callback. + + :return: TOX_CONNECTION + """ + return Tox.libtoxcore.tox_self_get_connection_status(self._tox_pointer) + + def callback_self_connection_status(self, callback): + """ + Set the callback for the `self_connection_status` event. Pass None to unset. + + This event is triggered whenever there is a change in the DHT connection state. When disconnected, a client may + choose to call tox_bootstrap again, to reconnect to the DHT. Note that this state may frequently change for + short amounts of time. Clients should therefore not immediately bootstrap on receiving a disconnect. + + :param callback: Python function. Should take pointer (c_void_p) to Tox object, + TOX_CONNECTION (c_int), + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_int, c_void_p) + self.self_connection_status_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_self_connection_status(self._tox_pointer, + self.self_connection_status_cb) + + def iteration_interval(self): + """ + Return the time in milliseconds before tox_iterate() should be called again for optimal performance. + :return: time in milliseconds + """ + return Tox.libtoxcore.tox_iteration_interval(self._tox_pointer) + + def iterate(self, user_data=None): + """ + The main loop that needs to be run in intervals of tox_iteration_interval() milliseconds. + """ + if user_data is not None: + user_data = c_char_p(user_data) + Tox.libtoxcore.tox_iterate(self._tox_pointer, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Internal client information (Tox address/id) + # ----------------------------------------------------------------------------------------------------------------- + + def self_get_address(self, address=None): + """ + Writes the Tox friend address of the client to a byte array. The address is not in human-readable format. If a + client wants to display the address, formatting is required. + + :param address: pointer (c_char_p) to a memory region of at least TOX_ADDRESS_SIZE bytes. If this parameter is + None, this function allocates memory for address. + :return: Tox friend address + """ + if address is None: + address = create_string_buffer(TOX_ADDRESS_SIZE) + Tox.libtoxcore.tox_self_get_address(self._tox_pointer, address) + return bin_to_string(address, TOX_ADDRESS_SIZE) + + def self_set_nospam(self, nospam): + """ + Set the 4-byte nospam part of the address. + + :param nospam: Any 32 bit unsigned integer. + """ + Tox.libtoxcore.tox_self_set_nospam(self._tox_pointer, c_uint32(nospam)) + + def self_get_nospam(self): + """ + Get the 4-byte nospam part of the address. + + :return: nospam part of the address + """ + return Tox.libtoxcore.tox_self_get_nospam(self._tox_pointer) + + def self_get_public_key(self, public_key=None): + """ + Copy the Tox Public Key (long term) from the Tox object. + + :param public_key: A memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this parameter is NULL, this + function allocates memory for Tox Public Key. + :return: Tox Public Key + """ + if public_key is None: + public_key = create_string_buffer(TOX_PUBLIC_KEY_SIZE) + Tox.libtoxcore.tox_self_get_public_key(self._tox_pointer, public_key) + return bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE) + + def self_get_secret_key(self, secret_key=None): + """ + Copy the Tox Secret Key from the Tox object. + + :param secret_key: pointer (c_char_p) to a memory region of at least TOX_SECRET_KEY_SIZE bytes. If this + parameter is NULL, this function allocates memory for Tox Secret Key. + :return: Tox Secret Key + """ + if secret_key is None: + secret_key = create_string_buffer(TOX_SECRET_KEY_SIZE) + Tox.libtoxcore.tox_self_get_secret_key(self._tox_pointer, secret_key) + return bin_to_string(secret_key, TOX_SECRET_KEY_SIZE) + + # ----------------------------------------------------------------------------------------------------------------- + # User-visible client information (nickname/status) + # ----------------------------------------------------------------------------------------------------------------- + + def self_set_name(self, name): + """ + Set the nickname for the Tox client. + + Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length is 0, the name parameter is ignored + (it can be None), and the nickname is set back to empty. + :param name: New nickname. + :return: True on success. + """ + tox_err_set_info = c_int() + name = bytes(name, 'utf-8') + result = Tox.libtoxcore.tox_self_set_name(self._tox_pointer, c_char_p(name), + c_size_t(len(name)), byref(tox_err_set_info)) + tox_err_set_info = tox_err_set_info.value + if tox_err_set_info == TOX_ERR_SET_INFO['OK']: + return bool(result) + elif tox_err_set_info == TOX_ERR_SET_INFO['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_set_info == TOX_ERR_SET_INFO['TOO_LONG']: + raise ArgumentError('Information length exceeded maximum permissible size.') + + def self_get_name_size(self): + """ + Return the length of the current nickname as passed to tox_self_set_name. + + If no nickname was set before calling this function, the name is empty, and this function returns 0. + + :return: length of the current nickname + """ + return Tox.libtoxcore.tox_self_get_name_size(self._tox_pointer) + + def self_get_name(self, name=None): + """ + Write the nickname set by tox_self_set_name to a byte array. + + If no nickname was set before calling this function, the name is empty, and this function has no effect. + + Call tox_self_get_name_size to find out how much memory to allocate for the result. + + :param name: pointer (c_char_p) to a memory region location large enough to hold the nickname. If this parameter + is NULL, the function allocates memory for the nickname. + :return: nickname + """ + if name is None: + name = create_string_buffer(self.self_get_name_size()) + Tox.libtoxcore.tox_self_get_name(self._tox_pointer, name) + return str(name.value, 'utf-8') + + def self_set_status_message(self, status_message): + """ + Set the client's status message. + + Status message length cannot exceed TOX_MAX_STATUS_MESSAGE_LENGTH. If length is 0, the status parameter is + ignored, and the user status is set back to empty. + + :param status_message: new status message + :return: True on success. + """ + tox_err_set_info = c_int() + status_message = bytes(status_message, 'utf-8') + result = Tox.libtoxcore.tox_self_set_status_message(self._tox_pointer, c_char_p(status_message), + c_size_t(len(status_message)), byref(tox_err_set_info)) + tox_err_set_info = tox_err_set_info.value + if tox_err_set_info == TOX_ERR_SET_INFO['OK']: + return bool(result) + elif tox_err_set_info == TOX_ERR_SET_INFO['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_set_info == TOX_ERR_SET_INFO['TOO_LONG']: + raise ArgumentError('Information length exceeded maximum permissible size.') + + def self_get_status_message_size(self): + """ + Return the length of the current status message as passed to tox_self_set_status_message. + + If no status message was set before calling this function, the status is empty, and this function returns 0. + + :return: length of the current status message + """ + return Tox.libtoxcore.tox_self_get_status_message_size(self._tox_pointer) + + def self_get_status_message(self, status_message=None): + """ + Write the status message set by tox_self_set_status_message to a byte array. + + If no status message was set before calling this function, the status is empty, and this function has no effect. + + Call tox_self_get_status_message_size to find out how much memory to allocate for the result. + + :param status_message: pointer (c_char_p) to a valid memory location large enough to hold the status message. + If this parameter is None, the function allocates memory for the status message. + :return: status message + """ + if status_message is None: + status_message = create_string_buffer(self.self_get_status_message_size()) + Tox.libtoxcore.tox_self_get_status_message(self._tox_pointer, status_message) + return str(status_message.value, 'utf-8') + + def self_set_status(self, status): + """ + Set the client's user status. + + :param status: One of the user statuses listed in the enumeration TOX_USER_STATUS. + """ + Tox.libtoxcore.tox_self_set_status(self._tox_pointer, c_int(status)) + + def self_get_status(self): + """ + Returns the client's user status. + + :return: client's user status + """ + return Tox.libtoxcore.tox_self_get_status(self._tox_pointer) + + # ----------------------------------------------------------------------------------------------------------------- + # Friend list management + # ----------------------------------------------------------------------------------------------------------------- + + def friend_add(self, address, message): + """ + Add a friend to the friend list and send a friend request. + + A friend request message must be at least 1 byte long and at most TOX_MAX_FRIEND_REQUEST_LENGTH. + + Friend numbers are unique identifiers used in all functions that operate on friends. Once added, a friend number + is stable for the lifetime of the Tox object. After saving the state and reloading it, the friend numbers may + not be the same as before. Deleting a friend creates a gap in the friend number set, which is filled by the next + adding of a friend. Any pattern in friend numbers should not be relied on. + + If more than INT32_MAX friends are added, this function causes undefined behaviour. + + :param address: The address of the friend (returned by tox_self_get_address of the friend you wish to add) it + must be TOX_ADDRESS_SIZE bytes. + :param message: The message that will be sent along with the friend request. + :return: the friend number on success, UINT32_MAX on failure. + """ + tox_err_friend_add = c_int() + result = Tox.libtoxcore.tox_friend_add(self._tox_pointer, string_to_bin(address), c_char_p(message), + c_size_t(len(message)), byref(tox_err_friend_add)) + tox_err_friend_add = tox_err_friend_add.value + if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OK']: + return result + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['TOO_LONG']: + raise ArgumentError('The length of the friend request message exceeded TOX_MAX_FRIEND_REQUEST_LENGTH.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NO_MESSAGE']: + raise ArgumentError('The friend request message was empty. This, and the TOO_LONG code will never be' + ' returned from tox_friend_add_norequest.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['OWN_KEY']: + raise ArgumentError('The friend address belongs to the sending client.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['ALREADY_SENT']: + raise ArgumentError('A friend request has already been sent, or the address belongs to a friend that is' + ' already on the friend list.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['BAD_CHECKSUM']: + raise ArgumentError('The friend address checksum failed.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['SET_NEW_NOSPAM']: + raise ArgumentError('The friend was already there, but the nospam value was different.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['MALLOC']: + raise MemoryError('A memory allocation failed when trying to increase the friend list size.') + + def friend_add_norequest(self, public_key): + """ + Add a friend without sending a friend request. + + This function is used to add a friend in response to a friend request. If the client receives a friend request, + it can be reasonably sure that the other client added this client as a friend, eliminating the need for a friend + request. + + This function is also useful in a situation where both instances are controlled by the same entity, so that this + entity can perform the mutual friend adding. In this case, there is no need for a friend request, either. + + :param public_key: A byte array of length TOX_PUBLIC_KEY_SIZE containing the Public Key (not the Address) of the + friend to add. + :return: the friend number on success, UINT32_MAX on failure. + """ + tox_err_friend_add = c_int() + result = Tox.libtoxcore.tox_friend_add_norequest(self._tox_pointer, string_to_bin(public_key), + byref(tox_err_friend_add)) + tox_err_friend_add = tox_err_friend_add.value + if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OK']: + return result + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['TOO_LONG']: + raise ArgumentError('The length of the friend request message exceeded TOX_MAX_FRIEND_REQUEST_LENGTH.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NO_MESSAGE']: + raise ArgumentError('The friend request message was empty. This, and the TOO_LONG code will never be' + ' returned from tox_friend_add_norequest.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['OWN_KEY']: + raise ArgumentError('The friend address belongs to the sending client.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['ALREADY_SENT']: + raise ArgumentError('A friend request has already been sent, or the address belongs to a friend that is' + ' already on the friend list.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['BAD_CHECKSUM']: + raise ArgumentError('The friend address checksum failed.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['SET_NEW_NOSPAM']: + raise ArgumentError('The friend was already there, but the nospam value was different.') + elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['MALLOC']: + raise MemoryError('A memory allocation failed when trying to increase the friend list size.') + + def friend_delete(self, friend_number): + """ + Remove a friend from the friend list. + + This does not notify the friend of their deletion. After calling this function, this client will appear offline + to the friend and no communication can occur between the two. + + :param friend_number: Friend number for the friend to be deleted. + :return: True on success. + """ + tox_err_friend_delete = c_int() + result = Tox.libtoxcore.tox_friend_delete(self._tox_pointer, c_uint32(friend_number), + byref(tox_err_friend_delete)) + tox_err_friend_delete = tox_err_friend_delete.value + if tox_err_friend_delete == TOX_ERR_FRIEND_DELETE['OK']: + return bool(result) + elif tox_err_friend_delete == TOX_ERR_FRIEND_DELETE['FRIEND_NOT_FOUND']: + raise ArgumentError('There was no friend with the given friend number. No friends were deleted.') + + # ----------------------------------------------------------------------------------------------------------------- + # Friend list queries + # ----------------------------------------------------------------------------------------------------------------- + + def friend_by_public_key(self, public_key): + """ + Return the friend number associated with that Public Key. + + :param public_key: A byte array containing the Public Key. + :return: friend number + """ + tox_err_friend_by_public_key = c_int() + result = Tox.libtoxcore.tox_friend_by_public_key(self._tox_pointer, string_to_bin(public_key), + byref(tox_err_friend_by_public_key)) + tox_err_friend_by_public_key = tox_err_friend_by_public_key.value + if tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['OK']: + return result + elif tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['NOT_FOUND']: + raise ArgumentError('No friend with the given Public Key exists on the friend list.') + + def friend_exists(self, friend_number): + """ + Checks if a friend with the given friend number exists and returns true if it does. + """ + return bool(Tox.libtoxcore.tox_friend_exists(self._tox_pointer, c_uint32(friend_number))) + + def self_get_friend_list_size(self): + """ + Return the number of friends on the friend list. + + This function can be used to determine how much memory to allocate for tox_self_get_friend_list. + + :return: number of friends + """ + return Tox.libtoxcore.tox_self_get_friend_list_size(self._tox_pointer) + + def self_get_friend_list(self, friend_list=None): + """ + Copy a list of valid friend numbers into an array. + + Call tox_self_get_friend_list_size to determine the number of elements to allocate. + + :param friend_list: pointer (c_char_p) to a memory region with enough space to hold the friend list. If this + parameter is None, this function allocates memory for the friend list. + :return: friend list + """ + friend_list_size = self.self_get_friend_list_size() + if friend_list is None: + friend_list = create_string_buffer(sizeof(c_uint32) * friend_list_size) + friend_list = POINTER(c_uint32)(friend_list) + Tox.libtoxcore.tox_self_get_friend_list(self._tox_pointer, friend_list) + return friend_list[0:friend_list_size] + + def friend_get_public_key(self, friend_number, public_key=None): + """ + Copies the Public Key associated with a given friend number to a byte array. + + :param friend_number: The friend number you want the Public Key of. + :param public_key: pointer (c_char_p) to a memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this + parameter is None, this function allocates memory for Tox Public Key. + :return: Tox Public Key + """ + if public_key is None: + public_key = create_string_buffer(TOX_PUBLIC_KEY_SIZE) + tox_err_friend_get_public_key = c_int() + Tox.libtoxcore.tox_friend_get_public_key(self._tox_pointer, c_uint32(friend_number), public_key, + byref(tox_err_friend_get_public_key)) + tox_err_friend_get_public_key = tox_err_friend_get_public_key.value + if tox_err_friend_get_public_key == TOX_ERR_FRIEND_GET_PUBLIC_KEY['OK']: + return bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE) + elif tox_err_friend_get_public_key == TOX_ERR_FRIEND_GET_PUBLIC_KEY['FRIEND_NOT_FOUND']: + raise ArgumentError('No friend with the given number exists on the friend list.') + + def friend_get_last_online(self, friend_number): + """ + Return a unix-time timestamp of the last time the friend associated with a given friend number was seen online. + This function will return UINT64_MAX on error. + + :param friend_number: The friend number you want to query. + :return: unix-time timestamp + """ + tox_err_last_online = c_int() + result = Tox.libtoxcore.tox_friend_get_last_online(self._tox_pointer, c_uint32(friend_number), + byref(tox_err_last_online)) + tox_err_last_online = tox_err_last_online.value + if tox_err_last_online == TOX_ERR_FRIEND_GET_LAST_ONLINE['OK']: + return result + elif tox_err_last_online == TOX_ERR_FRIEND_GET_LAST_ONLINE['FRIEND_NOT_FOUND']: + raise ArgumentError('No friend with the given number exists on the friend list.') + + # ----------------------------------------------------------------------------------------------------------------- + # Friend-specific state queries (can also be received through callbacks) + # ----------------------------------------------------------------------------------------------------------------- + + def friend_get_name_size(self, friend_number): + """ + Return the length of the friend's name. If the friend number is invalid, the return value is unspecified. + + The return value is equal to the `length` argument received by the last `friend_name` callback. + """ + tox_err_friend_query = c_int() + result = Tox.libtoxcore.tox_friend_get_name_size(self._tox_pointer, c_uint32(friend_number), + byref(tox_err_friend_query)) + tox_err_friend_query = tox_err_friend_query.value + if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: + return result + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: + raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' + ' the `_self_` variants of these functions, which have no effect when a parameter is' + ' NULL, these functions return an error in that case.') + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number did not designate a valid friend.') + + def friend_get_name(self, friend_number, name=None): + """ + Write the name of the friend designated by the given friend number to a byte array. + + Call tox_friend_get_name_size to determine the allocation size for the `name` parameter. + + The data written to `name` is equal to the data received by the last `friend_name` callback. + + :param friend_number: number of friend + :param name: pointer (c_char_p) to a valid memory region large enough to store the friend's name. + :return: name of the friend + """ + if name is None: + name = create_string_buffer(self.friend_get_name_size(friend_number)) + tox_err_friend_query = c_int() + Tox.libtoxcore.tox_friend_get_name(self._tox_pointer, c_uint32(friend_number), name, + byref(tox_err_friend_query)) + tox_err_friend_query = tox_err_friend_query.value + if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: + return str(name.value, 'utf-8') + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: + raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' + ' the `_self_` variants of these functions, which have no effect when a parameter is' + ' NULL, these functions return an error in that case.') + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number did not designate a valid friend.') + + def callback_friend_name(self, callback): + """ + Set the callback for the `friend_name` event. Pass None to unset. + + This event is triggered when a friend changes their name. + + :param callback: Python function. Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend whose name changed, + A byte array (c_char_p) containing the same data as tox_friend_get_name would write to its `name` parameter, + A value (c_size_t) equal to the return value of tox_friend_get_name_size, + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) + self.friend_name_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_friend_name(self._tox_pointer, self.friend_name_cb) + + def friend_get_status_message_size(self, friend_number): + """ + Return the length of the friend's status message. If the friend number is invalid, the return value is SIZE_MAX. + + :return: length of the friend's status message + """ + tox_err_friend_query = c_int() + result = Tox.libtoxcore.tox_friend_get_status_message_size(self._tox_pointer, c_uint32(friend_number), + byref(tox_err_friend_query)) + tox_err_friend_query = tox_err_friend_query.value + if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: + return result + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: + raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' + ' the `_self_` variants of these functions, which have no effect when a parameter is' + ' NULL, these functions return an error in that case.') + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number did not designate a valid friend.') + + def friend_get_status_message(self, friend_number, status_message=None): + """ + Write the status message of the friend designated by the given friend number to a byte array. + + Call tox_friend_get_status_message_size to determine the allocation size for the `status_name` parameter. + + The data written to `status_message` is equal to the data received by the last `friend_status_message` callback. + + :param friend_number: + :param status_message: pointer (c_char_p) to a valid memory region large enough to store the friend's status + message. + :return: status message of the friend + """ + if status_message is None: + status_message = create_string_buffer(self.friend_get_status_message_size(friend_number)) + tox_err_friend_query = c_int() + Tox.libtoxcore.tox_friend_get_status_message(self._tox_pointer, c_uint32(friend_number), status_message, + byref(tox_err_friend_query)) + tox_err_friend_query = tox_err_friend_query.value + if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: + return str(status_message.value, 'utf-8') + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: + raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' + ' the `_self_` variants of these functions, which have no effect when a parameter is' + ' NULL, these functions return an error in that case.') + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number did not designate a valid friend.') + + def callback_friend_status_message(self, callback): + """ + Set the callback for the `friend_status_message` event. Pass NULL to unset. + + This event is triggered when a friend changes their status message. + + :param callback: Python function. Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend whose status message changed, + A byte array (c_char_p) containing the same data as tox_friend_get_status_message would write to its + `status_message` parameter, + A value (c_size_t) equal to the return value of tox_friend_get_status_message_size, + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) + self.friend_status_message_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_friend_status_message(self._tox_pointer, + self.friend_status_message_cb) + + def friend_get_status(self, friend_number): + """ + Return the friend's user status (away/busy/...). If the friend number is invalid, the return value is + unspecified. + + The status returned is equal to the last status received through the `friend_status` callback. + + :return: TOX_USER_STATUS + """ + tox_err_friend_query = c_int() + result = Tox.libtoxcore.tox_friend_get_status(self._tox_pointer, c_uint32(friend_number), + byref(tox_err_friend_query)) + tox_err_friend_query = tox_err_friend_query.value + if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: + return result + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: + raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' + ' the `_self_` variants of these functions, which have no effect when a parameter is' + ' NULL, these functions return an error in that case.') + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number did not designate a valid friend.') + + def callback_friend_status(self, callback): + """ + Set the callback for the `friend_status` event. Pass None to unset. + + This event is triggered when a friend changes their user status. + + :param callback: Python function. Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend whose user status changed, + The new user status (TOX_USER_STATUS), + pointer (c_void_p) to user_data + :param user_data: pointer (c_void_p) to user data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) + self.friend_status_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_friend_status(self._tox_pointer, self.friend_status_cb) + + def friend_get_connection_status(self, friend_number): + """ + Check whether a friend is currently connected to this client. + + The result of this function is equal to the last value received by the `friend_connection_status` callback. + + :param friend_number: The friend number for which to query the connection status. + :return: the friend's connection status (TOX_CONNECTION) as it was received through the + `friend_connection_status` event. + """ + tox_err_friend_query = c_int() + result = Tox.libtoxcore.tox_friend_get_connection_status(self._tox_pointer, c_uint32(friend_number), + byref(tox_err_friend_query)) + tox_err_friend_query = tox_err_friend_query.value + if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: + return result + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: + raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' + ' the `_self_` variants of these functions, which have no effect when a parameter is' + ' NULL, these functions return an error in that case.') + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number did not designate a valid friend.') + + def callback_friend_connection_status(self, callback): + """ + Set the callback for the `friend_connection_status` event. Pass NULL to unset. + + This event is triggered when a friend goes offline after having been online, or when a friend goes online. + + This callback is not called when adding friends. It is assumed that when adding friends, their connection status + is initially offline. + + :param callback: Python function. Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend whose connection status changed, + The result of calling tox_friend_get_connection_status (TOX_CONNECTION) on the passed friend_number, + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) + self.friend_connection_status_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_friend_connection_status(self._tox_pointer, + self.friend_connection_status_cb) + + def friend_get_typing(self, friend_number): + """ + Check whether a friend is currently typing a message. + + :param friend_number: The friend number for which to query the typing status. + :return: true if the friend is typing. + """ + tox_err_friend_query = c_int() + result = Tox.libtoxcore.tox_friend_get_typing(self._tox_pointer, c_uint32(friend_number), + byref(tox_err_friend_query)) + tox_err_friend_query = tox_err_friend_query.value + if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: + return bool(result) + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: + raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' + ' the `_self_` variants of these functions, which have no effect when a parameter is' + ' NULL, these functions return an error in that case.') + elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number did not designate a valid friend.') + + def callback_friend_typing(self, callback): + """ + Set the callback for the `friend_typing` event. Pass NULL to unset. + + This event is triggered when a friend starts or stops typing. + + :param callback: Python function. Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend who started or stopped typing, + The result of calling tox_friend_get_typing (c_bool) on the passed friend_number, + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_void_p) + self.friend_typing_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_friend_typing(self._tox_pointer, self.friend_typing_cb) + + # ----------------------------------------------------------------------------------------------------------------- + # Sending private messages + # ----------------------------------------------------------------------------------------------------------------- + + def self_set_typing(self, friend_number, typing): + """ + Set the client's typing status for a friend. + + The client is responsible for turning it on or off. + + :param friend_number: The friend to which the client is typing a message. + :param typing: The typing status. True means the client is typing. + :return: True on success. + """ + tox_err_set_typing = c_int() + result = Tox.libtoxcore.tox_self_set_typing(self._tox_pointer, c_uint32(friend_number), + c_bool(typing), byref(tox_err_set_typing)) + tox_err_set_typing = tox_err_set_typing.value + if tox_err_set_typing == TOX_ERR_SET_TYPING['OK']: + return bool(result) + elif tox_err_set_typing == TOX_ERR_SET_TYPING['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend number did not designate a valid friend.') + + def friend_send_message(self, friend_number, message_type, message): + """ + Send a text chat message to an online friend. + + This function creates a chat message packet and pushes it into the send queue. + + The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages must be split by the client and sent + as separate messages. Other clients can then reassemble the fragments. Messages may not be empty. + + The return value of this function is the message ID. If a read receipt is received, the triggered + `friend_read_receipt` event will be passed this message ID. + + Message IDs are unique per friend. The first message ID is 0. Message IDs are incremented by 1 each time a + message is sent. If UINT32_MAX messages were sent, the next message ID is 0. + + :param friend_number: The friend number of the friend to send the message to. + :param message_type: Message type (TOX_MESSAGE_TYPE). + :param message: A non-None message text. + :return: message ID + """ + tox_err_friend_send_message = c_int() + result = Tox.libtoxcore.tox_friend_send_message(self._tox_pointer, c_uint32(friend_number), + c_int(message_type), c_char_p(message), c_size_t(len(message)), + byref(tox_err_friend_send_message)) + tox_err_friend_send_message = tox_err_friend_send_message.value + if tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['OK']: + return result + elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend number did not designate a valid friend.') + elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_CONNECTED']: + raise ArgumentError('This client is currently not connected to the friend.') + elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['SENDQ']: + raise MemoryError('An allocation error occurred while increasing the send queue size.') + elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['TOO_LONG']: + raise ArgumentError('Message length exceeded TOX_MAX_MESSAGE_LENGTH.') + elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['EMPTY']: + raise ArgumentError('Attempted to send a zero-length message.') + + def callback_friend_read_receipt(self, callback): + """ + Set the callback for the `friend_read_receipt` event. Pass None to unset. + + This event is triggered when the friend receives the message sent with tox_friend_send_message with the + corresponding message ID. + + :param callback: Python function. Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend who received the message, + The message ID (c_uint32) as returned from tox_friend_send_message corresponding to the message sent, + pointer (c_void_p) to user_data + :param user_data: pointer (c_void_p) to user data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) + self.friend_read_receipt_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_friend_read_receipt(self._tox_pointer, + self.friend_read_receipt_cb) + + # ----------------------------------------------------------------------------------------------------------------- + # Receiving private messages and friend requests + # ----------------------------------------------------------------------------------------------------------------- + + def callback_friend_request(self, callback): + """ + Set the callback for the `friend_request` event. Pass None to unset. + + This event is triggered when a friend request is received. + + :param callback: Python function. Should take pointer (c_void_p) to Tox object, + The Public Key (c_uint8 array) of the user who sent the friend request, + The message (c_char_p) they sent along with the request, + The size (c_size_t) of the message byte array, + pointer (c_void_p) to user_data + :param user_data: pointer (c_void_p) to user data + """ + c_callback = CFUNCTYPE(None, c_void_p, POINTER(c_uint8), c_char_p, c_size_t, c_void_p) + self.friend_request_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_friend_request(self._tox_pointer, self.friend_request_cb) + + def callback_friend_message(self, callback): + """ + Set the callback for the `friend_message` event. Pass None to unset. + + This event is triggered when a message from a friend is received. + + :param callback: Python function. Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend who sent the message, + Message type (TOX_MESSAGE_TYPE), + The message data (c_char_p) they sent, + The size (c_size_t) of the message byte array. + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_char_p, c_size_t, c_void_p) + self.friend_message_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_friend_message(self._tox_pointer, self.friend_message_cb) + + # ----------------------------------------------------------------------------------------------------------------- + # File transmission: common between sending and receiving + # ----------------------------------------------------------------------------------------------------------------- + + @staticmethod + def hash(data, hash=None): + """ + Generates a cryptographic hash of the given data. + + This function may be used by clients for any purpose, but is provided primarily for validating cached avatars. + This use is highly recommended to avoid unnecessary avatar updates. + + If hash is NULL or data is NULL while length is not 0 the function returns false, otherwise it returns true. + + This function is a wrapper to internal message-digest functions. + + :param hash: A valid memory location the hash data. It must be at least TOX_HASH_LENGTH bytes in size. + :param data: Data to be hashed or NULL. + :return: true if hash was not NULL. + """ + if hash is None: + hash = create_string_buffer(TOX_HASH_LENGTH) + Tox.libtoxcore.tox_hash(hash, c_char_p(data), len(data)) + return bin_to_string(hash, TOX_HASH_LENGTH) + + def file_control(self, friend_number, file_number, control): + """ + Sends a file control command to a friend for a given file transfer. + + :param friend_number: The friend number of the friend the file is being transferred to or received from. + :param file_number: The friend-specific identifier for the file transfer. + :param control: The control (TOX_FILE_CONTROL) command to send. + :return: True on success. + """ + tox_err_file_control = c_int() + result = Tox.libtoxcore.tox_file_control(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), + c_int(control), byref(tox_err_file_control)) + tox_err_file_control = tox_err_file_control.value + if tox_err_file_control == TOX_ERR_FILE_CONTROL['OK']: + return bool(result) + elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number passed did not designate a valid friend.') + elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_CONNECTED']: + raise ArgumentError('This client is currently not connected to the friend.') + elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_FOUND']: + raise ArgumentError('No file transfer with the given file number was found for the given friend.') + elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_PAUSED']: + raise RuntimeError('A RESUME control was sent, but the file transfer is running normally.') + elif tox_err_file_control == TOX_ERR_FILE_CONTROL['DENIED']: + raise RuntimeError('A RESUME control was sent, but the file transfer was paused by the other party. Only ' + 'the party that paused the transfer can resume it.') + elif tox_err_file_control == TOX_ERR_FILE_CONTROL['ALREADY_PAUSED']: + raise RuntimeError('A PAUSE control was sent, but the file transfer was already paused.') + elif tox_err_file_control == TOX_ERR_FILE_CONTROL['SENDQ']: + raise RuntimeError('Packet queue is full.') + + def callback_file_recv_control(self, callback): + """ + Set the callback for the `file_recv_control` event. Pass NULL to unset. + + This event is triggered when a file control command is received from a friend. + + :param callback: Python function. + When receiving TOX_FILE_CONTROL_CANCEL, the client should release the resources associated with the file number + and consider the transfer failed. + + Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend who is sending the file. + The friend-specific file number (c_uint32) the data received is associated with. + The file control (TOX_FILE_CONTROL) command received. + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p) + self.file_recv_control_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_file_recv_control(self._tox_pointer, + self.file_recv_control_cb) + + def file_seek(self, friend_number, file_number, position): + """ + Sends a file seek control command to a friend for a given file transfer. + + This function can only be called to resume a file transfer right before TOX_FILE_CONTROL_RESUME is sent. + + :param friend_number: The friend number of the friend the file is being received from. + :param file_number: The friend-specific identifier for the file transfer. + :param position: The position that the file should be seeked to. + :return: True on success. + """ + tox_err_file_seek = c_int() + result = Tox.libtoxcore.tox_file_control(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), + c_uint64(position), byref(tox_err_file_seek)) + tox_err_file_seek = tox_err_file_seek.value + if tox_err_file_seek == TOX_ERR_FILE_SEEK['OK']: + return bool(result) + elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number passed did not designate a valid friend.') + elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_CONNECTED']: + raise ArgumentError('This client is currently not connected to the friend.') + elif tox_err_file_seek == TOX_ERR_FILE_SEEK['NOT_FOUND']: + raise ArgumentError('No file transfer with the given file number was found for the given friend.') + elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SEEK_DENIED']: + raise IOError('File was not in a state where it could be seeked.') + elif tox_err_file_seek == TOX_ERR_FILE_SEEK['INVALID_POSITION']: + raise ArgumentError('Seek position was invalid') + elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SENDQ']: + raise RuntimeError('Packet queue is full.') + + def file_get_file_id(self, friend_number, file_number, file_id=None): + """ + Copy the file id associated to the file transfer to a byte array. + + :param friend_number: The friend number of the friend the file is being transferred to or received from. + :param file_number: The friend-specific identifier for the file transfer. + :param file_id: A pointer (c_char_p) to memory region of at least TOX_FILE_ID_LENGTH bytes. If this parameter is + None, this function has no effect. + :return: file id. + """ + if file_id is None: + file_id = create_string_buffer(TOX_FILE_ID_LENGTH) + tox_err_file_get = c_int() + Tox.libtoxcore.tox_file_get_file_id(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), file_id, + byref(tox_err_file_get)) + tox_err_file_get = tox_err_file_get.value + if tox_err_file_get == TOX_ERR_FILE_GET['OK']: + return bin_to_string(file_id, TOX_FILE_ID_LENGTH) + elif tox_err_file_get == TOX_ERR_FILE_GET['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_file_get == TOX_ERR_FILE_GET['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number passed did not designate a valid friend.') + elif tox_err_file_get == TOX_ERR_FILE_GET['NOT_FOUND']: + raise ArgumentError('No file transfer with the given file number was found for the given friend.') + + # ----------------------------------------------------------------------------------------------------------------- + # File transmission: sending + # ----------------------------------------------------------------------------------------------------------------- + + def file_send(self, friend_number, kind, file_size, file_id, filename): + """ + Send a file transmission request. + + Maximum filename length is TOX_MAX_FILENAME_LENGTH bytes. The filename should generally just be a file name, not + a path with directory names. + + If a non-UINT64_MAX file size is provided, it can be used by both sides to determine the sending progress. File + size can be set to UINT64_MAX for streaming data of unknown size. + + File transmission occurs in chunks, which are requested through the `file_chunk_request` event. + + When a friend goes offline, all file transfers associated with the friend are purged from core. + + If the file contents change during a transfer, the behaviour is unspecified in general. What will actually + happen depends on the mode in which the file was modified and how the client determines the file size. + + - If the file size was increased + - and sending mode was streaming (file_size = UINT64_MAX), the behaviour will be as expected. + - and sending mode was file (file_size != UINT64_MAX), the file_chunk_request callback will receive length = + 0 when Core thinks the file transfer has finished. If the client remembers the file size as it was when + sending the request, it will terminate the transfer normally. If the client re-reads the size, it will think + the friend cancelled the transfer. + - If the file size was decreased + - and sending mode was streaming, the behaviour is as expected. + - and sending mode was file, the callback will return 0 at the new (earlier) end-of-file, signalling to the + friend that the transfer was cancelled. + - If the file contents were modified + - at a position before the current read, the two files (local and remote) will differ after the transfer + terminates. + - at a position after the current read, the file transfer will succeed as expected. + - In either case, both sides will regard the transfer as complete and successful. + + :param friend_number: The friend number of the friend the file send request should be sent to. + :param kind: The meaning of the file to be sent. + :param file_size: Size in bytes of the file the client wants to send, UINT64_MAX if unknown or streaming. + :param file_id: A file identifier of length TOX_FILE_ID_LENGTH that can be used to uniquely identify file + transfers across core restarts. If NULL, a random one will be generated by core. It can then be obtained by + using tox_file_get_file_id(). + :param filename: Name of the file. Does not need to be the actual name. This name will be sent along with the + file send request. + :return: A file number used as an identifier in subsequent callbacks. This number is per friend. File numbers + are reused after a transfer terminates. On failure, this function returns UINT32_MAX. Any pattern in file + numbers should not be relied on. + """ + tox_err_file_send = c_int() + result = self.libtoxcore.tox_file_send(self._tox_pointer, c_uint32(friend_number), c_uint32(kind), + c_uint64(file_size), + string_to_bin(file_id), + c_char_p(filename), + c_size_t(len(filename)), byref(tox_err_file_send)) + tox_err_file_send = tox_err_file_send.value + if tox_err_file_send == TOX_ERR_FILE_SEND['OK']: + return result + elif tox_err_file_send == TOX_ERR_FILE_SEND['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number passed did not designate a valid friend.') + elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_CONNECTED']: + raise ArgumentError('This client is currently not connected to the friend.') + elif tox_err_file_send == TOX_ERR_FILE_SEND['NAME_TOO_LONG']: + raise ArgumentError('Filename length exceeded TOX_MAX_FILENAME_LENGTH bytes.') + elif tox_err_file_send == TOX_ERR_FILE_SEND['TOO_MANY']: + raise RuntimeError('Too many ongoing transfers. The maximum number of concurrent file transfers is 256 per' + 'friend per direction (sending and receiving).') + + def file_send_chunk(self, friend_number, file_number, position, data): + """ + Send a chunk of file data to a friend. + + This function is called in response to the `file_chunk_request` callback. The length parameter should be equal + to the one received though the callback. If it is zero, the transfer is assumed complete. For files with known + size, Core will know that the transfer is complete after the last byte has been received, so it is not necessary + (though not harmful) to send a zero-length chunk to terminate. For streams, core will know that the transfer is + finished if a chunk with length less than the length requested in the callback is sent. + + :param friend_number: The friend number of the receiving friend for this file. + :param file_number: The file transfer identifier returned by tox_file_send. + :param position: The file or stream position from which to continue reading. + :param data: Chunk of file data + :return: true on success. + """ + tox_err_file_send_chunk = c_int() + result = self.libtoxcore.tox_file_send_chunk(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), + c_uint64(position), c_char_p(data), c_size_t(len(data)), + byref(tox_err_file_send_chunk)) + tox_err_file_send_chunk = tox_err_file_send_chunk.value + if tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['OK']: + return bool(result) + elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NULL']: + raise ArgumentError('The length parameter was non-zero, but data was NULL.') + elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['FRIEND_NOT_FOUND']: + ArgumentError('The friend_number passed did not designate a valid friend.') + elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['FRIEND_NOT_CONNECTED']: + raise ArgumentError('This client is currently not connected to the friend.') + elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NOT_FOUND']: + raise ArgumentError('No file transfer with the given file number was found for the given friend.') + elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NOT_TRANSFERRING']: + raise ArgumentError('File transfer was found but isn\'t in a transferring state: (paused, done, broken, ' + 'etc...) (happens only when not called from the request chunk callback).') + elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['INVALID_LENGTH']: + raise ArgumentError('Attempted to send more or less data than requested. The requested data size is ' + 'adjusted according to maximum transmission unit and the expected end of the file. ' + 'Trying to send less or more than requested will return this error.') + elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['SENDQ']: + raise RuntimeError('Packet queue is full.') + elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['WRONG_POSITION']: + raise ArgumentError('Position parameter was wrong.') + + def callback_file_chunk_request(self, callback): + """ + Set the callback for the `file_chunk_request` event. Pass None to unset. + + This event is triggered when Core is ready to send more file data. + + :param callback: Python function. + If the length parameter is 0, the file transfer is finished, and the client's resources associated with the file + number should be released. After a call with zero length, the file number can be reused for future file + transfers. + + If the requested position is not equal to the client's idea of the current file or stream position, it will need + to seek. In case of read-once streams, the client should keep the last read chunk so that a seek back can be + supported. A seek-back only ever needs to read from the last requested chunk. This happens when a chunk was + requested, but the send failed. A seek-back request can occur an arbitrary number of times for any given chunk. + + In response to receiving this callback, the client should call the function `tox_file_send_chunk` with the + requested chunk. If the number of bytes sent through that function is zero, the file transfer is assumed + complete. A client must send the full length of data requested with this callback. + + Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the receiving friend for this file. + The file transfer identifier (c_uint32) returned by tox_file_send. + The file or stream position (c_uint64) from which to continue reading. + The number of bytes (c_size_t) requested for the current chunk. + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, c_size_t, c_void_p) + self.file_chunk_request_cb = c_callback(callback) + self.libtoxcore.tox_callback_file_chunk_request(self._tox_pointer, self.file_chunk_request_cb) + + # ----------------------------------------------------------------------------------------------------------------- + # File transmission: receiving + # ----------------------------------------------------------------------------------------------------------------- + + def callback_file_recv(self, callback): + """ + Set the callback for the `file_recv` event. Pass None to unset. + + This event is triggered when a file transfer request is received. + + :param callback: Python function. + The client should acquire resources to be associated with the file transfer. Incoming file transfers start in + the PAUSED state. After this callback returns, a transfer can be rejected by sending a TOX_FILE_CONTROL_CANCEL + control command before any other control commands. It can be accepted by sending TOX_FILE_CONTROL_RESUME. + + Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend who is sending the file transfer request. + The friend-specific file number (c_uint32) the data received is associated with. + The meaning of the file (c_uint32) to be sent. + Size in bytes (c_uint64) of the file the client wants to send, UINT64_MAX if unknown or streaming. + Name of the file (c_char_p). Does not need to be the actual name. This name will be sent along with the file + send request. + Size in bytes (c_size_t) of the filename. + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t, c_void_p) + self.file_recv_cb = c_callback(callback) + self.libtoxcore.tox_callback_file_recv(self._tox_pointer, self.file_recv_cb) + + def callback_file_recv_chunk(self, callback): + """ + Set the callback for the `file_recv_chunk` event. Pass NULL to unset. + + This event is first triggered when a file transfer request is received, and subsequently when a chunk of file + data for an accepted request was received. + + :param callback: Python function. + When length is 0, the transfer is finished and the client should release the resources it acquired for the + transfer. After a call with length = 0, the file number can be reused for new file transfers. + + If position is equal to file_size (received in the file_receive callback) when the transfer finishes, the file + was received completely. Otherwise, if file_size was UINT64_MAX, streaming ended successfully when length is 0. + + Should take pointer (c_void_p) to Tox object, + The friend number (c_uint32) of the friend who is sending the file. + The friend-specific file number (c_uint32) the data received is associated with. + The file position (c_uint64) of the first byte in data. + A byte array (c_char_p) containing the received chunk. + The length (c_size_t) of the received chunk. + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, POINTER(c_uint8), c_size_t, c_void_p) + self.file_recv_chunk_cb = c_callback(callback) + self.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, self.file_recv_chunk_cb) + + # ----------------------------------------------------------------------------------------------------------------- + # Low-level custom packet sending and receiving + # ----------------------------------------------------------------------------------------------------------------- + + def friend_send_lossy_packet(self, friend_number, data): + """ + Send a custom lossy packet to a friend. + The first byte of data must be in the range 200-254. Maximum length of a + custom packet is TOX_MAX_CUSTOM_PACKET_SIZE. + + Lossy packets behave like UDP packets, meaning they might never reach the + other side or might arrive more than once (if someone is messing with the + connection) or might arrive in the wrong order. + + Unless latency is an issue, it is recommended that you use lossless custom packets instead. + + :param friend_number: The friend number of the friend this lossy packet + :param data: python string containing the packet data + :return: True on success. + """ + tox_err_friend_custom_packet = c_int() + result = self.libtoxcore.tox_friend_send_lossy_packet(self._tox_pointer, c_uint32(friend_number), + c_char_p(data), c_size_t(len(data)), + byref(tox_err_friend_custom_packet)) + tox_err_friend_custom_packet = tox_err_friend_custom_packet.value + if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']: + return bool(result) + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend number did not designate a valid friend.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']: + raise ArgumentError('This client is currently not connected to the friend.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']: + raise ArgumentError('The first byte of data was not in the specified range for the packet type.' + 'This range is 200-254 for lossy, and 160-191 for lossless packets.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']: + raise ArgumentError('Attempted to send an empty packet.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']: + raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']: + raise RuntimeError('Packet queue is full.') + + def friend_send_lossless_packet(self, friend_number, data): + """ + Send a custom lossless packet to a friend. + The first byte of data must be in the range 160-191. Maximum length of a + custom packet is TOX_MAX_CUSTOM_PACKET_SIZE. + + Lossless packet behaviour is comparable to TCP (reliability, arrive in order) + but with packets instead of a stream. + + :param friend_number: The friend number of the friend this lossless packet + :param data: python string containing the packet data + :return: True on success. + """ + tox_err_friend_custom_packet = c_int() + result = self.libtoxcore.tox_friend_send_lossless_packet(self._tox_pointer, c_uint32(friend_number), + c_char_p(data), c_size_t(len(data)), + byref(tox_err_friend_custom_packet)) + tox_err_friend_custom_packet = tox_err_friend_custom_packet.value + if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']: + return bool(result) + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend number did not designate a valid friend.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']: + raise ArgumentError('This client is currently not connected to the friend.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']: + raise ArgumentError('The first byte of data was not in the specified range for the packet type.' + 'This range is 200-254 for lossy, and 160-191 for lossless packets.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']: + raise ArgumentError('Attempted to send an empty packet.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']: + raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.') + elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']: + raise RuntimeError('Packet queue is full.') + + def callback_friend_lossy_packet(self, callback): + """ + Set the callback for the `friend_lossy_packet` event. Pass NULL to unset. + + :param callback: Python function. + Should take pointer (c_void_p) to Tox object, + friend_number (c_uint32) - The friend number of the friend who sent a lossy packet, + A byte array (c_uint8 array) containing the received packet data, + length (c_size_t) - The length of the packet data byte array, + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) + self.friend_lossy_packet_cb = c_callback(callback) + self.libtoxcore.tox_callback_friend_lossy_packet(self._tox_pointer, self.friend_lossy_packet_cb) + + def callback_friend_lossless_packet(self, callback): + """ + Set the callback for the `friend_lossless_packet` event. Pass NULL to unset. + + :param callback: Python function. + Should take pointer (c_void_p) to Tox object, + friend_number (c_uint32) - The friend number of the friend who sent a lossless packet, + A byte array (c_uint8 array) containing the received packet data, + length (c_size_t) - The length of the packet data byte array, + pointer (c_void_p) to user_data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) + self.friend_lossless_packet_cb = c_callback(callback) + self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, self.friend_lossless_packet_cb) + + # ----------------------------------------------------------------------------------------------------------------- + # Low-level network information + # ----------------------------------------------------------------------------------------------------------------- + + def self_get_dht_id(self, dht_id=None): + """ + Writes the temporary DHT public key of this instance to a byte array. + + This can be used in combination with an externally accessible IP address and the bound port (from + tox_self_get_udp_port) to run a temporary bootstrap node. + + Be aware that every time a new instance is created, the DHT public key changes, meaning this cannot be used to + run a permanent bootstrap node. + + :param dht_id: pointer (c_char_p) to a memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this parameter is + None, this function allocates memory for dht_id. + :return: dht_id + """ + if dht_id is None: + dht_id = create_string_buffer(TOX_PUBLIC_KEY_SIZE) + Tox.libtoxcore.tox_self_get_dht_id(self._tox_pointer, dht_id) + return bin_to_string(dht_id, TOX_PUBLIC_KEY_SIZE) + + def self_get_udp_port(self): + """ + Return the UDP port this Tox instance is bound to. + """ + tox_err_get_port = c_int() + result = Tox.libtoxcore.tox_self_get_udp_port(self._tox_pointer, byref(tox_err_get_port)) + tox_err_get_port = tox_err_get_port.value + if tox_err_get_port == TOX_ERR_GET_PORT['OK']: + return result + elif tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']: + raise RuntimeError('The instance was not bound to any port.') + + def self_get_tcp_port(self): + """ + Return the TCP port this Tox instance is bound to. This is only relevant if the instance is acting as a TCP + relay. + """ + tox_err_get_port = c_int() + result = Tox.libtoxcore.tox_self_get_tcp_port(self._tox_pointer, byref(tox_err_get_port)) + tox_err_get_port = tox_err_get_port.value + if tox_err_get_port == TOX_ERR_GET_PORT['OK']: + return result + elif tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']: + raise RuntimeError('The instance was not bound to any port.') + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat instance management + # ----------------------------------------------------------------------------------------------------------------- + + def group_new(self, privacy_state, group_name, nick, status): + """ + Creates a new group chat. + + This function creates a new group chat object and adds it to the chats array. + + The client should initiate its peer list with self info after calling this function, as + the peer_join callback will not be triggered. + + :param privacy_state: The privacy state of the group. If this is set to TOX_GROUP_PRIVACY_STATE_PUBLIC, + the group will attempt to announce itself to the DHT and anyone with the Chat ID may join. + Otherwise a friend invite will be required to join the group. + :param group_name: The name of the group. The name must be non-NULL. + + :return group number on success, UINT32_MAX on failure. + """ + + error = c_int() + peer_info = self.group_self_peer_info_new() + nick = bytes(nick, 'utf-8') + group_name = group_name.encode('utf-8') + peer_info.contents.nick = c_char_p(nick) + peer_info.contents.nick_length = len(nick) + peer_info.contents.user_status = status + result = Tox.libtoxcore.tox_group_new(self._tox_pointer, privacy_state, group_name, + len(group_name), peer_info, byref(error)) + return result + + def group_join(self, chat_id, password, nick, status): + """ + Joins a group chat with specified Chat ID. + + This function creates a new group chat object, adds it to the chats array, and sends + a DHT announcement to find peers in the group associated with chat_id. Once a peer has been + found a join attempt will be initiated. + + :param chat_id: The Chat ID of the group you wish to join. This must be TOX_GROUP_CHAT_ID_SIZE bytes. + :param password: The password required to join the group. Set to NULL if no password is required. + + :return group_number on success, UINT32_MAX on failure. + """ + + error = c_int() + peer_info = self.group_self_peer_info_new() + nick = bytes(nick, 'utf-8') + peer_info.contents.nick = c_char_p(nick) + peer_info.contents.nick_length = len(nick) + peer_info.contents.user_status = status + result = Tox.libtoxcore.tox_group_join(self._tox_pointer, string_to_bin(chat_id), + password, + len(password) if password is not None else 0, + peer_info, + byref(error)) + return result + + def group_reconnect(self, group_number): + """ + Reconnects to a group. + + This function disconnects from all peers in the group, then attempts to reconnect with the group. + The caller's state is not changed (i.e. name, status, role, chat public key etc.) + + :param group_number: The group number of the group we wish to reconnect to. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_reconnect(self._tox_pointer, group_number, byref(error)) + return result + + def group_is_connected(self, group_number): + error = c_int() + result = Tox.libtoxcore.tox_group_is_connected(self._tox_pointer, group_number, byref(error)) + return result + + def group_disconnect(self, group_number): + error = c_int() + result = Tox.libtoxcore.tox_group_disconnect(self._tox_pointer, group_number, byref(error)) + return result + + def group_leave(self, group_number, message=''): + """ + Leaves a group. + + This function sends a parting packet containing a custom (non-obligatory) message to all + peers in a group, and deletes the group from the chat array. All group state information is permanently + lost, including keys and role credentials. + + :param group_number: The group number of the group we wish to leave. + :param message: The parting message to be sent to all the peers. Set to NULL if we do not wish to + send a parting message. + + :return True if the group chat instance was successfully deleted. + """ + + error = c_int() + f = Tox.libtoxcore.tox_group_leave + f.restype = c_bool + result = f(self._tox_pointer, group_number, message, + len(message) if message is not None else 0, byref(error)) + return result + + # ----------------------------------------------------------------------------------------------------------------- + # Group user-visible client information (nickname/status/role/public key) + # ----------------------------------------------------------------------------------------------------------------- + + def group_self_set_name(self, group_number, name): + """ + Set the client's nickname for the group instance designated by the given group number. + + Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length is equal to zero or name is a NULL + pointer, the function call will fail. + + :param name: A byte array containing the new nickname. + + :return True on success. + """ + + error = c_int() + name = bytes(name, 'utf-8') + result = Tox.libtoxcore.tox_group_self_set_name(self._tox_pointer, group_number, name, len(name), byref(error)) + return result + + def group_self_get_name_size(self, group_number): + """ + Return the length of the client's current nickname for the group instance designated + by group_number as passed to tox_group_self_set_name. + + If no nickname was set before calling this function, the name is empty, + and this function returns 0. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_name_size(self._tox_pointer, group_number, byref(error)) + return result + + def group_self_get_name(self, group_number): + """ + Write the nickname set by tox_group_self_set_name to a byte array. + + If no nickname was set before calling this function, the name is empty, + and this function has no effect. + + Call tox_group_self_get_name_size to find out how much memory to allocate for the result. + :return nickname + """ + + error = c_int() + size = self.group_self_get_name_size(group_number) + name = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_self_get_name(self._tox_pointer, group_number, name, byref(error)) + return str(name[:size], 'utf-8') + + def group_self_set_status(self, group_number, status): + + """ + Set the client's status for the group instance. Status must be a TOX_USER_STATUS. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_set_status(self._tox_pointer, group_number, status, byref(error)) + return result + + def group_self_get_status(self, group_number): + """ + returns the client's status for the group instance on success. + return value is unspecified on failure. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_status(self._tox_pointer, group_number, byref(error)) + return result + + def group_self_get_role(self, group_number): + """ + returns the client's role for the group instance on success. + return value is unspecified on failure. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_role(self._tox_pointer, group_number, byref(error)) + return result + + def group_self_get_peer_id(self, group_number): + """ + returns the client's peer id for the group instance on success. + return value is unspecified on failure. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_self_get_peer_id(self._tox_pointer, group_number, byref(error)) + return result + + def group_self_get_public_key(self, group_number): + """ + Write the client's group public key designated by the given group number to a byte array. + + This key will be permanently tied to the client's identity for this particular group until + the client explicitly leaves the group or gets kicked/banned. This key is the only way for + other peers to reliably identify the client across client restarts. + + `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. + + :return public key + """ + + error = c_int() + key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + result = Tox.libtoxcore.tox_group_self_get_public_key(self._tox_pointer, group_number, + key, byref(error)) + return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + + # ----------------------------------------------------------------------------------------------------------------- + # Peer-specific group state queries. + # ----------------------------------------------------------------------------------------------------------------- + + def group_peer_get_name_size(self, group_number, peer_id): + """ + Return the length of the peer's name. If the group number or ID is invalid, the + return value is unspecified. + + The return value is equal to the `length` argument received by the last + `group_peer_name` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_peer_get_name_size(self._tox_pointer, group_number, peer_id, byref(error)) + return result + + def group_peer_get_name(self, group_number, peer_id): + """ + Write the name of the peer designated by the given ID to a byte + array. + + Call tox_group_peer_get_name_size to determine the allocation size for the `name` parameter. + + The data written to `name` is equal to the data received by the last + `group_peer_name` callback. + + :param group_number: The group number of the group we wish to query. + :param peer_id: The ID of the peer whose name we want to retrieve. + + :return name. + """ + error = c_int() + size = self.group_peer_get_name_size(group_number, peer_id) + name = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_peer_get_name(self._tox_pointer, group_number, peer_id, name, byref(error)) + return str(name[:], 'utf-8') + + def group_peer_get_status(self, group_number, peer_id): + """ + Return the peer's user status (away/busy/...). If the ID or group number is + invalid, the return value is unspecified. + + The status returned is equal to the last status received through the + `group_peer_status` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_peer_get_status(self._tox_pointer, group_number, peer_id, byref(error)) + return result + + def group_peer_get_role(self, group_number, peer_id): + """ + Return the peer's role (user/moderator/founder...). If the ID or group number is + invalid, the return value is unspecified. + + The role returned is equal to the last role received through the + `group_moderation` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_peer_get_role(self._tox_pointer, group_number, peer_id, byref(error)) + return result + + def group_peer_get_public_key(self, group_number, peer_id): + """ + Write the group public key with the designated peer_id for the designated group number to public_key. + + This key will be permanently tied to a particular peer until they explicitly leave the group or + get kicked/banned, and is the only way to reliably identify the same peer across client restarts. + + `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. + + :return public key + """ + + error = c_int() + key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + result = Tox.libtoxcore.tox_group_peer_get_public_key(self._tox_pointer, group_number, peer_id, + key, byref(error)) + return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) + + def callback_group_peer_name(self, callback, user_data): + """ + Set the callback for the `group_peer_name` event. Pass NULL to unset. + This event is triggered when a peer changes their nickname. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_peer_name_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_name(self._tox_pointer, self.group_peer_name_cb, user_data) + + def callback_group_peer_status(self, callback, user_data): + """ + Set the callback for the `group_peer_status` event. Pass NULL to unset. + This event is triggered when a peer changes their status. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p) + self.group_peer_status_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_status(self._tox_pointer, self.group_peer_status_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat state queries and events. + # ----------------------------------------------------------------------------------------------------------------- + + def group_set_topic(self, group_number, topic): + """ + Set the group topic and broadcast it to the rest of the group. + + topic length cannot be longer than TOX_GROUP_MAX_TOPIC_LENGTH. If length is equal to zero or + topic is set to NULL, the topic will be unset. + + :return True on success. + """ + + error = c_int() + topic = bytes(topic, 'utf-8') + result = Tox.libtoxcore.tox_group_set_topic(self._tox_pointer, group_number, topic, len(topic), byref(error)) + return result + + def group_get_topic_size(self, group_number): + """ + Return the length of the group topic. If the group number is invalid, the + return value is unspecified. + + The return value is equal to the `length` argument received by the last + `group_topic` callback. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_topic_size(self._tox_pointer, group_number, byref(error)) + return result + + def group_get_topic(self, group_number): + """ + Write the topic designated by the given group number to a byte array. + Call tox_group_get_topic_size to determine the allocation size for the `topic` parameter. + The data written to `topic` is equal to the data received by the last + `group_topic` callback. + + :return topic + """ + + error = c_int() + size = self.group_get_topic_size(group_number) + topic = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_get_topic(self._tox_pointer, group_number, topic, byref(error)) + return str(topic[:size], 'utf-8') + + def group_get_name_size(self, group_number): + """ + Return the length of the group name. If the group number is invalid, the + return value is unspecified. + """ + error = c_int() + result = Tox.libtoxcore.tox_group_get_name_size(self._tox_pointer, group_number, byref(error)) + return int(result) + + def group_get_name(self, group_number): + """ + Write the name of the group designated by the given group number to a byte array. + Call tox_group_get_name_size to determine the allocation size for the `name` parameter. + :return true on success. + """ + + error = c_int() + size = self.group_get_name_size(group_number) + name = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_get_name(self._tox_pointer, group_number, + name, byref(error)) + return str(name[:size], 'utf-8') + + def group_get_chat_id(self, group_number): + """ + Write the Chat ID designated by the given group number to a byte array. + `chat_id` should have room for at least TOX_GROUP_CHAT_ID_SIZE bytes. + :return chat id. + """ + + error = c_int() + buff = create_string_buffer(TOX_GROUP_CHAT_ID_SIZE) + result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer, group_number, + buff, byref(error)) + return bin_to_string(buff, TOX_GROUP_CHAT_ID_SIZE) + + def group_get_number_groups(self): + """ + Return the number of groups in the Tox chats array. + """ + + result = Tox.libtoxcore.tox_group_get_number_groups(self._tox_pointer) + return result + + def groups_get_list(self): + groups_list_size = self.group_get_number_groups() + groups_list = create_string_buffer(sizeof(c_uint32) * groups_list_size) + groups_list = POINTER(c_uint32)(groups_list) + Tox.libtoxcore.tox_groups_get_list(self._tox_pointer, groups_list) + return groups_list[0:groups_list_size] + + def group_get_privacy_state(self, group_number): + """ + Return the privacy state of the group designated by the given group number. If group number + is invalid, the return value is unspecified. + + The value returned is equal to the data received by the last + `group_privacy_state` callback. + + see the `Group chat founder controls` section for the respective set function. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_privacy_state(self._tox_pointer, group_number, byref(error)) + return result + + def group_get_peer_limit(self, group_number): + """ + Return the maximum number of peers allowed for the group designated by the given group number. + If the group number is invalid, the return value is unspecified. + + The value returned is equal to the data received by the last + `group_peer_limit` callback. + + see the `Group chat founder controls` section for the respective set function. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_peer_limit(self._tox_pointer, group_number, byref(error)) + return result + + def group_get_password_size(self, group_number): + """ + Return the length of the group password. If the group number is invalid, the + return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_get_password_size(self._tox_pointer, group_number, byref(error)) + return result + + def group_get_password(self, group_number): + """ + Write the password for the group designated by the given group number to a byte array. + + Call tox_group_get_password_size to determine the allocation size for the `password` parameter. + + The data received is equal to the data received by the last + `group_password` callback. + + see the `Group chat founder controls` section for the respective set function. + + :return password + """ + + error = c_int() + size = self.group_get_password_size(group_number) + password = create_string_buffer(size) + result = Tox.libtoxcore.tox_group_get_password(self._tox_pointer, group_number, + password, byref(error)) + return str(password[:size], 'utf-8') + + def callback_group_topic(self, callback, user_data): + """ + Set the callback for the `group_topic` event. Pass NULL to unset. + This event is triggered when a peer changes the group topic. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_topic_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_topic(self._tox_pointer, self.group_topic_cb, user_data) + + def callback_group_privacy_state(self, callback, user_data): + """ + Set the callback for the `group_privacy_state` event. Pass NULL to unset. + This event is triggered when the group founder changes the privacy state. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) + self.group_privacy_state_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_privacy_state(self._tox_pointer, self.group_privacy_state_cb, user_data) + + def callback_group_peer_limit(self, callback, user_data): + """ + Set the callback for the `group_peer_limit` event. Pass NULL to unset. + This event is triggered when the group founder changes the maximum peer limit. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) + self.group_peer_limit_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_limit(self._tox_pointer, self.group_peer_limit_cb, user_data) + + def callback_group_password(self, callback, user_data): + """ + Set the callback for the `group_password` event. Pass NULL to unset. + This event is triggered when the group founder changes the group password. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_password_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_password(self._tox_pointer, self.group_password_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group message sending + # ----------------------------------------------------------------------------------------------------------------- + + def group_send_custom_packet(self, group_number, lossless, data): + """ + Send a custom packet to the group. + + If lossless is true the packet will be lossless. Lossless packet behaviour is comparable + to TCP (reliability, arrive in order) but with packets instead of a stream. + + If lossless is false, the packet will be lossy. Lossy packets behave like UDP packets, + meaning they might never reach the other side or might arrive more than once (if someone + is messing with the connection) or might arrive in the wrong order. + + Unless latency is an issue or message reliability is not important, it is recommended that you use + lossless custom packets. + + :param group_number: The group number of the group the message is intended for. + :param lossless: True if the packet should be lossless. + :param data A byte array containing the packet data. + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_send_custom_packet(self._tox_pointer, group_number, lossless, data, + len(data), byref(error)) + return result + + def group_send_private_message(self, group_number, peer_id, message_type, message): + """ + Send a text chat message to the specified peer in the specified group. + + This function creates a group private message packet and pushes it into the send + queue. + + The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages + must be split by the client and sent as separate messages. Other clients can + then reassemble the fragments. Messages may not be empty. + + :param group_number: The group number of the group the message is intended for. + :param peer_id: The ID of the peer the message is intended for. + :param message: A non-NULL pointer to the first element of a byte array containing the message text. + + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_send_private_message(self._tox_pointer, group_number, peer_id, + message_type, message, + len(message), byref(error)) + return result + + def group_send_message(self, group_number, type, message): + """ + Send a text chat message to the group. + + This function creates a group message packet and pushes it into the send + queue. + + The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages + must be split by the client and sent as separate messages. Other clients can + then reassemble the fragments. Messages may not be empty. + + :param group_number: The group number of the group the message is intended for. + :param type: Message type (normal, action, ...). + :param message: A non-NULL pointer to the first element of a byte array containing the message text. + + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_send_message(self._tox_pointer, group_number, type, message, len(message), + byref(error)) + return result + + # ----------------------------------------------------------------------------------------------------------------- + # Group message receiving + # ----------------------------------------------------------------------------------------------------------------- + + def callback_group_message(self, callback, user_data): + """ + Set the callback for the `group_message` event. Pass NULL to unset. + This event is triggered when the client receives a group message. + + Callback: python function with params: + tox Tox* instance + group_number The group number of the group the message is intended for. + peer_id The ID of the peer who sent the message. + type The type of message (normal, action, ...). + message The message data. + length The length of the message. + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_char_p, c_size_t, c_void_p) + self.group_message_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_message(self._tox_pointer, self.group_message_cb, user_data) + + def callback_group_private_message(self, callback, user_data): + """ + Set the callback for the `group_private_message` event. Pass NULL to unset. + This event is triggered when the client receives a private message. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint8, c_char_p, c_size_t, c_void_p) + self.group_private_message_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_private_message(self._tox_pointer, self.group_private_message_cb, user_data) + + def callback_group_custom_packet(self, callback, user_data): + """ + Set the callback for the `group_custom_packet` event. Pass NULL to unset. + + This event is triggered when the client receives a custom packet. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, POINTER(c_uint8), c_void_p) + self.group_custom_packet_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_custom_packet(self._tox_pointer, self.group_custom_packet_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat inviting and join/part events + # ----------------------------------------------------------------------------------------------------------------- + + def group_invite_friend(self, group_number, friend_number): + """ + Invite a friend to a group. + + This function creates an invite request packet and pushes it to the send queue. + + :param group_number: The group number of the group the message is intended for. + :param friend_number: The friend number of the friend the invite is intended for. + + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, group_number, friend_number, byref(error)) + return result + + @staticmethod + def group_self_peer_info_new(): + error = c_int() + f = Tox.libtoxcore.tox_group_self_peer_info_new + f.restype = POINTER(GroupChatSelfPeerInfo) + result = f(byref(error)) + + return result + + def group_invite_accept(self, invite_data, friend_number, nick, status, password=None): + """ + Accept an invite to a group chat that the client previously received from a friend. The invite + is only valid while the inviter is present in the group. + + :param invite_data: The invite data received from the `group_invite` event. + :param password: The password required to join the group. Set to NULL if no password is required. + :return the group_number on success, UINT32_MAX on failure. + """ + + error = c_int() + f = Tox.libtoxcore.tox_group_invite_accept + f.restype = c_uint32 + peer_info = self.group_self_peer_info_new() + nick = bytes(nick, 'utf-8') + peer_info.contents.nick = c_char_p(nick) + peer_info.contents.nick_length = len(nick) + peer_info.contents.user_status = status + result = f(self._tox_pointer, friend_number, invite_data, len(invite_data), password, + len(password) if password is not None else 0, peer_info, byref(error)) + print('Invite accept. Result:', result, 'Error:', error.value) + return result + + def callback_group_invite(self, callback, user_data): + """ + Set the callback for the `group_invite` event. Pass NULL to unset. + + This event is triggered when the client receives a group invite from a friend. The client must store + invite_data which is used to join the group via tox_group_invite_accept. + + Callback: python function with params: + tox - Tox* + friend_number The friend number of the contact who sent the invite. + invite_data The invite data. + length The length of invite_data. + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, + POINTER(c_uint8), c_size_t, c_void_p) + self.group_invite_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, self.group_invite_cb, user_data) + + def callback_group_peer_join(self, callback, user_data): + """ + Set the callback for the `group_peer_join` event. Pass NULL to unset. + + This event is triggered when a peer other than self joins the group. + Callback: python function with params: + tox - Tox* + group_number - group number + peer_id - peer id + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) + self.group_peer_join_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_join(self._tox_pointer, self.group_peer_join_cb, user_data) + + def callback_group_peer_exit(self, callback, user_data): + """ + Set the callback for the `group_peer_exit` event. Pass NULL to unset. + + This event is triggered when a peer other than self exits the group. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) + self.group_peer_exit_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_peer_exit(self._tox_pointer, self.group_peer_exit_cb, user_data) + + def callback_group_self_join(self, callback, user_data): + """ + Set the callback for the `group_self_join` event. Pass NULL to unset. + + This event is triggered when the client has successfully joined a group. Use this to initialize + any group information the client may need. + Callback: python fucntion with params: + tox - *Tox + group_number - group number + user_data - user data + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_void_p) + self.group_self_join_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_self_join(self._tox_pointer, self.group_self_join_cb, user_data) + + def callback_group_join_fail(self, callback, user_data): + """ + Set the callback for the `group_join_fail` event. Pass NULL to unset. + + This event is triggered when the client fails to join a group. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) + self.group_join_fail_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_join_fail(self._tox_pointer, self.group_join_fail_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat founder controls (these only work for the group founder) + # ----------------------------------------------------------------------------------------------------------------- + + def group_founder_set_password(self, group_number, password): + """ + Set or unset the group password. + + This function sets the groups password, creates a new group shared state including the change, + and distributes it to the rest of the group. + + :param group_number: The group number of the group for which we wish to set the password. + :param password: The password we want to set. Set password to NULL to unset the password. + + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_founder_set_password(self._tox_pointer, group_number, password, + len(password), byref(error)) + return result + + def group_founder_set_privacy_state(self, group_number, privacy_state): + """ + Set the group privacy state. + + This function sets the group's privacy state, creates a new group shared state + including the change, and distributes it to the rest of the group. + + If an attempt is made to set the privacy state to the same state that the group is already + in, the function call will be successful and no action will be taken. + + :param group_number: The group number of the group for which we wish to change the privacy state. + :param privacy_state: The privacy state we wish to set the group to. + + :return true on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_founder_set_privacy_state(self._tox_pointer, group_number, privacy_state, + byref(error)) + return result + + def group_founder_set_peer_limit(self, group_number, max_peers): + """ + Set the group peer limit. + + This function sets a limit for the number of peers who may be in the group, creates a new + group shared state including the change, and distributes it to the rest of the group. + + :param group_number: The group number of the group for which we wish to set the peer limit. + :param max_peers: The maximum number of peers to allow in the group. + + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_founder_set_peer_limit(self._tox_pointer, group_number, + max_peers, byref(error)) + return result + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat moderation + # ----------------------------------------------------------------------------------------------------------------- + + def group_toggle_ignore(self, group_number, peer_id, ignore): + """ + Ignore or unignore a peer. + + :param group_number: The group number of the group the in which you wish to ignore a peer. + :param peer_id: The ID of the peer who shall be ignored or unignored. + :param ignore: True to ignore the peer, false to unignore the peer. + + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_toggle_ignore(self._tox_pointer, group_number, peer_id, ignore, byref(error)) + return result + + def group_mod_set_role(self, group_number, peer_id, role): + """ + Set a peer's role. + + This function will first remove the peer's previous role and then assign them a new role. + It will also send a packet to the rest of the group, requesting that they perform + the role reassignment. Note: peers cannot be set to the founder role. + + :param group_number: The group number of the group the in which you wish set the peer's role. + :param peer_id: The ID of the peer whose role you wish to set. + :param role: The role you wish to set the peer to. + + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_set_role(self._tox_pointer, group_number, peer_id, role, byref(error)) + return result + + def group_mod_remove_peer(self, group_number, peer_id): + """ + Kick/ban a peer. + + This function will remove a peer from the caller's peer list and optionally add their IP address + to the ban list. It will also send a packet to all group members requesting them + to do the same. + + :param group_number: The group number of the group the ban is intended for. + :param peer_id: The ID of the peer who will be kicked and/or added to the ban list. + + :return True on success. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_remove_peer(self._tox_pointer, group_number, peer_id, + byref(error)) + return result + + def group_mod_ban_peer(self, group_number, peer_id, ban_type): + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_ban_peer(self._tox_pointer, group_number, peer_id, + ban_type, byref(error)) + return result + + def group_mod_remove_ban(self, group_number, ban_id): + """ + Removes a ban. + + This function removes a ban entry from the ban list, and sends a packet to the rest of + the group requesting that they do the same. + + :param group_number: The group number of the group in which the ban is to be removed. + :param ban_id: The ID of the ban entry that shall be removed. + + :return True on success + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_mod_remove_ban(self._tox_pointer, group_number, ban_id, byref(error)) + return result + + def callback_group_moderation(self, callback, user_data): + """ + Set the callback for the `group_moderation` event. Pass NULL to unset. + + This event is triggered when a moderator or founder executes a moderation event. + """ + + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_int, c_void_p) + self.group_moderation_cb = c_callback(callback) + Tox.libtoxcore.tox_callback_group_moderation(self._tox_pointer, self.group_moderation_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Group chat ban list queries + # ----------------------------------------------------------------------------------------------------------------- + + def group_ban_get_list_size(self, group_number): + """ + Return the number of entries in the ban list for the group designated by + the given group number. If the group number is invalid, the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_list_size(self._tox_pointer, group_number, byref(error)) + return result + + def group_ban_get_list(self, group_number): + """ + Copy a list of valid ban list ID's into an array. + + Call tox_group_ban_get_list_size to determine the number of elements to allocate. + return true on success. + """ + + error = c_int() + bans_list_size = self.group_ban_get_list_size(group_number) + bans_list = create_string_buffer(sizeof(c_uint32) * bans_list_size) + bans_list = POINTER(c_uint32)(bans_list) + result = Tox.libtoxcore.tox_group_ban_get_list(self._tox_pointer, group_number, bans_list, byref(error)) + return bans_list[:bans_list_size] + + def group_ban_get_type(self, group_number, ban_id): + """ + Return the type for the ban list entry designated by ban_id, in the + group designated by the given group number. If either group_number or ban_id is invalid, + the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_type(self._tox_pointer, group_number, ban_id, byref(error)) + return result + + def group_ban_get_target_size(self, group_number, ban_id): + """ + Return the length of the name for the ban list entry designated by ban_id, in the + group designated by the given group number. If either group_number or ban_id is invalid, + the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_target_size(self._tox_pointer, group_number, ban_id, byref(error)) + return result + + def group_ban_get_target(self, group_number, ban_id): + """ + Write the name of the ban entry designated by ban_id in the group designated by the + given group number to a byte array. + + Call tox_group_ban_get_name_size to find out how much memory to allocate for the result. + + :return name + """ + + error = c_int() + size = self.group_ban_get_target_size(group_number, ban_id) + target = create_string_buffer(size) + target_type = self.group_ban_get_type(group_number, ban_id) + + result = Tox.libtoxcore.tox_group_ban_get_target(self._tox_pointer, group_number, ban_id, + target, byref(error)) + if target_type == TOX_GROUP_BAN_TYPE['PUBLIC_KEY']: + return bin_to_string(target, size) + return str(target[:size], 'utf-8') + + def group_ban_get_time_set(self, group_number, ban_id): + """ + Return a time stamp indicating the time the ban was set, for the ban list entry + designated by ban_id, in the group designated by the given group number. + If either group_number or ban_id is invalid, the return value is unspecified. + """ + + error = c_int() + result = Tox.libtoxcore.tox_group_ban_get_time_set(self._tox_pointer, group_number, ban_id, byref(error)) + return result diff --git a/toxygen/wrapper/toxav.py b/toxygen/wrapper/toxav.py new file mode 100644 index 0000000..98e1c73 --- /dev/null +++ b/toxygen/wrapper/toxav.py @@ -0,0 +1,363 @@ +from ctypes import c_int, POINTER, c_void_p, byref, ArgumentError, c_uint32, CFUNCTYPE, c_size_t, c_uint8, c_uint16 +from ctypes import c_char_p, c_int32, c_bool, cast +from wrapper.libtox import LibToxAV +from wrapper.toxav_enums import * + + +class ToxAV: + """ + The ToxAV instance type. Each ToxAV instance can be bound to only one Tox instance, and Tox instance can have only + one ToxAV instance. One must make sure to close ToxAV instance prior closing Tox instance otherwise undefined + behaviour occurs. Upon closing of ToxAV instance, all active calls will be forcibly terminated without notifying + peers. + """ + + # ----------------------------------------------------------------------------------------------------------------- + # Creation and destruction + # ----------------------------------------------------------------------------------------------------------------- + + def __init__(self, tox_pointer): + """ + Start new A/V session. There can only be only one session per Tox instance. + + :param tox_pointer: pointer to Tox instance + """ + self.libtoxav = LibToxAV() + toxav_err_new = c_int() + f = self.libtoxav.toxav_new + f.restype = POINTER(c_void_p) + self._toxav_pointer = f(tox_pointer, byref(toxav_err_new)) + toxav_err_new = toxav_err_new.value + if toxav_err_new == TOXAV_ERR_NEW['NULL']: + raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') + elif toxav_err_new == TOXAV_ERR_NEW['MALLOC']: + raise MemoryError('Memory allocation failure while trying to allocate structures required for the A/V ' + 'session.') + elif toxav_err_new == TOXAV_ERR_NEW['MULTIPLE']: + raise RuntimeError('Attempted to create a second session for the same Tox instance.') + + self.call_state_cb = None + self.audio_receive_frame_cb = None + self.video_receive_frame_cb = None + self.call_cb = None + + def kill(self): + """ + Releases all resources associated with the A/V session. + + If any calls were ongoing, these will be forcibly terminated without notifying peers. After calling this + function, no other functions may be called and the av pointer becomes invalid. + """ + self.libtoxav.toxav_kill(self._toxav_pointer) + + def get_tox_pointer(self): + """ + Returns the Tox instance the A/V object was created for. + + :return: pointer to the Tox instance + """ + self.libtoxav.toxav_get_tox.restype = POINTER(c_void_p) + return self.libtoxav.toxav_get_tox(self._toxav_pointer) + + # ----------------------------------------------------------------------------------------------------------------- + # A/V event loop + # ----------------------------------------------------------------------------------------------------------------- + + def iteration_interval(self): + """ + Returns the interval in milliseconds when the next toxav_iterate call should be. If no call is active at the + moment, this function returns 200. + + :return: interval in milliseconds + """ + return self.libtoxav.toxav_iteration_interval(self._toxav_pointer) + + def iterate(self): + """ + Main loop for the session. This function needs to be called in intervals of toxav_iteration_interval() + milliseconds. It is best called in the separate thread from tox_iterate. + """ + self.libtoxav.toxav_iterate(self._toxav_pointer) + + # ----------------------------------------------------------------------------------------------------------------- + # Call setup + # ----------------------------------------------------------------------------------------------------------------- + + def call(self, friend_number, audio_bit_rate, video_bit_rate): + """ + Call a friend. This will start ringing the friend. + + It is the client's responsibility to stop ringing after a certain timeout, if such behaviour is desired. If the + client does not stop ringing, the library will not stop until the friend is disconnected. Audio and video + receiving are both enabled by default. + + :param friend_number: The friend number of the friend that should be called. + :param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending. + :param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending. + :return: True on success. + """ + toxav_err_call = c_int() + result = self.libtoxav.toxav_call(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate), + c_uint32(video_bit_rate), byref(toxav_err_call)) + toxav_err_call = toxav_err_call.value + if toxav_err_call == TOXAV_ERR_CALL['OK']: + return bool(result) + elif toxav_err_call == TOXAV_ERR_CALL['MALLOC']: + raise MemoryError('A resource allocation error occurred while trying to create the structures required for ' + 'the call.') + elif toxav_err_call == TOXAV_ERR_CALL['SYNC']: + raise RuntimeError('Synchronization error occurred.') + elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend number did not designate a valid friend.') + elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_CONNECTED']: + raise ArgumentError('The friend was valid, but not currently connected.') + elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_ALREADY_IN_CALL']: + raise ArgumentError('Attempted to call a friend while already in an audio or video call with them.') + elif toxav_err_call == TOXAV_ERR_CALL['INVALID_BIT_RATE']: + raise ArgumentError('Audio or video bit rate is invalid.') + + def callback_call(self, callback, user_data): + """ + Set the callback for the `call` event. Pass None to unset. + + :param callback: The function for the call callback. + + Should take pointer (c_void_p) to ToxAV object, + The friend number (c_uint32) from which the call is incoming. + True (c_bool) if friend is sending audio. + True (c_bool) if friend is sending video. + pointer (c_void_p) to user_data + :param user_data: pointer (c_void_p) to user data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_bool, c_void_p) + self.call_cb = c_callback(callback) + self.libtoxav.toxav_callback_call(self._toxav_pointer, self.call_cb, user_data) + + def answer(self, friend_number, audio_bit_rate, video_bit_rate): + """ + Accept an incoming call. + + If answering fails for any reason, the call will still be pending and it is possible to try and answer it later. + Audio and video receiving are both enabled by default. + + :param friend_number: The friend number of the friend that is calling. + :param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending. + :param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending. + :return: True on success. + """ + toxav_err_answer = c_int() + result = self.libtoxav.toxav_answer(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate), + c_uint32(video_bit_rate), byref(toxav_err_answer)) + toxav_err_answer = toxav_err_answer.value + if toxav_err_answer == TOXAV_ERR_ANSWER['OK']: + return bool(result) + elif toxav_err_answer == TOXAV_ERR_ANSWER['SYNC']: + raise RuntimeError('Synchronization error occurred.') + elif toxav_err_answer == TOXAV_ERR_ANSWER['CODEC_INITIALIZATION']: + raise RuntimeError('Failed to initialize codecs for call session. Note that codec initiation will fail if ' + 'there is no receive callback registered for either audio or video.') + elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend number did not designate a valid friend.') + elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_CALLING']: + raise ArgumentError('The friend was valid, but they are not currently trying to initiate a call. This is ' + 'also returned if this client is already in a call with the friend.') + elif toxav_err_answer == TOXAV_ERR_ANSWER['INVALID_BIT_RATE']: + raise ArgumentError('Audio or video bit rate is invalid.') + + # ----------------------------------------------------------------------------------------------------------------- + # Call state graph + # ----------------------------------------------------------------------------------------------------------------- + + def callback_call_state(self, callback, user_data): + """ + Set the callback for the `call_state` event. Pass None to unset. + + :param callback: Python function. + The function for the call_state callback. + + Should take pointer (c_void_p) to ToxAV object, + The friend number (c_uint32) for which the call state changed. + The bitmask of the new call state which is guaranteed to be different than the previous state. The state is set + to 0 when the call is paused. The bitmask represents all the activities currently performed by the friend. + pointer (c_void_p) to user_data + :param user_data: pointer (c_void_p) to user data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) + self.call_state_cb = c_callback(callback) + self.libtoxav.toxav_callback_call_state(self._toxav_pointer, self.call_state_cb, user_data) + + # ----------------------------------------------------------------------------------------------------------------- + # Call control + # ----------------------------------------------------------------------------------------------------------------- + + def call_control(self, friend_number, control): + """ + Sends a call control command to a friend. + + :param friend_number: The friend number of the friend this client is in a call with. + :param control: The control command to send. + :return: True on success. + """ + toxav_err_call_control = c_int() + result = self.libtoxav.toxav_call_control(self._toxav_pointer, c_uint32(friend_number), c_int(control), + byref(toxav_err_call_control)) + toxav_err_call_control = toxav_err_call_control.value + if toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['OK']: + return bool(result) + elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['SYNC']: + raise RuntimeError('Synchronization error occurred.') + elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number passed did not designate a valid friend.') + elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_IN_CALL']: + raise RuntimeError('This client is currently not in a call with the friend. Before the call is answered, ' + 'only CANCEL is a valid control.') + elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['INVALID_TRANSITION']: + raise RuntimeError('Happens if user tried to pause an already paused call or if trying to resume a call ' + 'that is not paused.') + + # ----------------------------------------------------------------------------------------------------------------- + # TODO Controlling bit rates + # ----------------------------------------------------------------------------------------------------------------- + + # ----------------------------------------------------------------------------------------------------------------- + # A/V sending + # ----------------------------------------------------------------------------------------------------------------- + + def audio_send_frame(self, friend_number, pcm, sample_count, channels, sampling_rate): + """ + Send an audio frame to a friend. + + The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]... + Meaning: sample 1 for channel 1, sample 1 for channel 2, ... + For mono audio, this has no meaning, every sample is subsequent. For stereo, this means the expected format is + LRLRLR... with samples for left and right alternating. + + :param friend_number: The friend number of the friend to which to send an audio frame. + :param pcm: An array of audio samples. The size of this array must be sample_count * channels. + :param sample_count: Number of samples in this frame. Valid numbers here are + ((sample rate) * (audio length) / 1000), where audio length can be 2.5, 5, 10, 20, 40 or 60 milliseconds. + :param channels: Number of audio channels. Sulpported values are 1 and 2. + :param sampling_rate: Audio sampling rate used in this frame. Valid sampling rates are 8000, 12000, 16000, + 24000, or 48000. + """ + toxav_err_send_frame = c_int() + result = self.libtoxav.toxav_audio_send_frame(self._toxav_pointer, c_uint32(friend_number), + cast(pcm, c_void_p), + c_size_t(sample_count), c_uint8(channels), + c_uint32(sampling_rate), byref(toxav_err_send_frame)) + toxav_err_send_frame = toxav_err_send_frame.value + if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']: + return bool(result) + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']: + raise ArgumentError('The samples data pointer was NULL.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number passed did not designate a valid friend.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']: + raise RuntimeError('This client is currently not in a call with the friend.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']: + raise RuntimeError('Synchronization error occurred.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']: + raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too ' + 'large, or the audio sampling rate may be unsupported.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']: + raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said' + 'payload.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']: + RuntimeError('Failed to push frame through rtp interface.') + + def video_send_frame(self, friend_number, width, height, y, u, v): + """ + Send a video frame to a friend. + + Y - plane should be of size: height * width + U - plane should be of size: (height/2) * (width/2) + V - plane should be of size: (height/2) * (width/2) + + :param friend_number: The friend number of the friend to which to send a video frame. + :param width: Width of the frame in pixels. + :param height: Height of the frame in pixels. + :param y: Y (Luminance) plane data. + :param u: U (Chroma) plane data. + :param v: V (Chroma) plane data. + """ + toxav_err_send_frame = c_int() + result = self.libtoxav.toxav_video_send_frame(self._toxav_pointer, c_uint32(friend_number), c_uint16(width), + c_uint16(height), c_char_p(y), c_char_p(u), c_char_p(v), + byref(toxav_err_send_frame)) + toxav_err_send_frame = toxav_err_send_frame.value + if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']: + return bool(result) + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']: + raise ArgumentError('One of Y, U, or V was NULL.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']: + raise ArgumentError('The friend_number passed did not designate a valid friend.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']: + raise RuntimeError('This client is currently not in a call with the friend.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']: + raise RuntimeError('Synchronization error occurred.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']: + raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too ' + 'large, or the audio sampling rate may be unsupported.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']: + raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said' + 'payload.') + elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']: + RuntimeError('Failed to push frame through rtp interface.') + + # ----------------------------------------------------------------------------------------------------------------- + # A/V receiving + # ----------------------------------------------------------------------------------------------------------------- + + def callback_audio_receive_frame(self, callback, user_data): + """ + Set the callback for the `audio_receive_frame` event. Pass None to unset. + + :param callback: Python function. + Function for the audio_receive_frame callback. The callback can be called multiple times per single + iteration depending on the amount of queued frames in the buffer. The received format is the same as in send + function. + + Should take pointer (c_void_p) to ToxAV object, + The friend number (c_uint32) of the friend who sent an audio frame. + An array (c_uint8) of audio samples (sample_count * channels elements). + The number (c_size_t) of audio samples per channel in the PCM array. + Number (c_uint8) of audio channels. + Sampling rate (c_uint32) used in this frame. + pointer (c_void_p) to user_data + :param user_data: pointer (c_void_p) to user data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_uint8, c_uint32, c_void_p) + self.audio_receive_frame_cb = c_callback(callback) + self.libtoxav.toxav_callback_audio_receive_frame(self._toxav_pointer, self.audio_receive_frame_cb, user_data) + + def callback_video_receive_frame(self, callback, user_data): + """ + Set the callback for the `video_receive_frame` event. Pass None to unset. + + :param callback: Python function. + The function type for the video_receive_frame callback. + + Should take + toxAV pointer (c_void_p) to ToxAV object, + friend_number The friend number (c_uint32) of the friend who sent a video frame. + width Width (c_uint16) of the frame in pixels. + height Height (c_uint16) of the frame in pixels. + y + u + v Plane data (POINTER(c_uint8)). + The size of plane data is derived from width and height where + Y = MAX(width, abs(ystride)) * height, + U = MAX(width/2, abs(ustride)) * (height/2) and + V = MAX(width/2, abs(vstride)) * (height/2). + ystride + ustride + vstride Strides data (c_int32). Strides represent padding for each plane that may or may not be present. You must + handle strides in your image processing code. Strides are negative if the image is bottom-up + hence why you MUST abs() it when calculating plane buffer size. + user_data pointer (c_void_p) to user_data + :param user_data: pointer (c_void_p) to user data + """ + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint16, c_uint16, POINTER(c_uint8), POINTER(c_uint8), + POINTER(c_uint8), c_int32, c_int32, c_int32, c_void_p) + self.video_receive_frame_cb = c_callback(callback) + self.libtoxav.toxav_callback_video_receive_frame(self._toxav_pointer, self.video_receive_frame_cb, user_data) diff --git a/toxygen/wrapper/toxav_enums.py b/toxygen/wrapper/toxav_enums.py new file mode 100644 index 0000000..3f3977a --- /dev/null +++ b/toxygen/wrapper/toxav_enums.py @@ -0,0 +1,131 @@ +TOXAV_ERR_NEW = { + # The function returned successfully. + 'OK': 0, + # One of the arguments to the function was NULL when it was not expected. + 'NULL': 1, + # Memory allocation failure while trying to allocate structures required for the A/V session. + 'MALLOC': 2, + # Attempted to create a second session for the same Tox instance. + 'MULTIPLE': 3, +} + +TOXAV_ERR_CALL = { + # The function returned successfully. + 'OK': 0, + # A resource allocation error occurred while trying to create the structures required for the call. + 'MALLOC': 1, + # Synchronization error occurred. + 'SYNC': 2, + # The friend number did not designate a valid friend. + 'FRIEND_NOT_FOUND': 3, + # The friend was valid, but not currently connected. + 'FRIEND_NOT_CONNECTED': 4, + # Attempted to call a friend while already in an audio or video call with them. + 'FRIEND_ALREADY_IN_CALL': 5, + # Audio or video bit rate is invalid. + 'INVALID_BIT_RATE': 6, +} + +TOXAV_ERR_ANSWER = { + # The function returned successfully. + 'OK': 0, + # Synchronization error occurred. + 'SYNC': 1, + # Failed to initialize codecs for call session. Note that codec initiation will fail if there is no receive callback + # registered for either audio or video. + 'CODEC_INITIALIZATION': 2, + # The friend number did not designate a valid friend. + 'FRIEND_NOT_FOUND': 3, + # The friend was valid, but they are not currently trying to initiate a call. This is also returned if this client + # is already in a call with the friend. + 'FRIEND_NOT_CALLING': 4, + # Audio or video bit rate is invalid. + 'INVALID_BIT_RATE': 5, +} + +TOXAV_FRIEND_CALL_STATE = { + # Set by the AV core if an error occurred on the remote end or if friend timed out. This is the final state after + # which no more state transitions can occur for the call. This call state will never be triggered in combination + # with other call states. + 'ERROR': 1, + # The call has finished. This is the final state after which no more state transitions can occur for the call. This + # call state will never be triggered in combination with other call states. + 'FINISHED': 2, + # The flag that marks that friend is sending audio. + 'SENDING_A': 4, + # The flag that marks that friend is sending video. + 'SENDING_V': 8, + # The flag that marks that friend is receiving audio. + 'ACCEPTING_A': 16, + # The flag that marks that friend is receiving video. + 'ACCEPTING_V': 32, +} + +TOXAV_CALL_CONTROL = { + # Resume a previously paused call. Only valid if the pause was caused by this client, if not, this control is + # ignored. Not valid before the call is accepted. + 'RESUME': 0, + # Put a call on hold. Not valid before the call is accepted. + 'PAUSE': 1, + # Reject a call if it was not answered, yet. Cancel a call after it was answered. + 'CANCEL': 2, + # Request that the friend stops sending audio. Regardless of the friend's compliance, this will cause the + # audio_receive_frame event to stop being triggered on receiving an audio frame from the friend. + 'MUTE_AUDIO': 3, + # Calling this control will notify client to start sending audio again. + 'UNMUTE_AUDIO': 4, + # Request that the friend stops sending video. Regardless of the friend's compliance, this will cause the + # video_receive_frame event to stop being triggered on receiving a video frame from the friend. + 'HIDE_VIDEO': 5, + # Calling this control will notify client to start sending video again. + 'SHOW_VIDEO': 6, +} + +TOXAV_ERR_CALL_CONTROL = { + # The function returned successfully. + 'OK': 0, + # Synchronization error occurred. + 'SYNC': 1, + # The friend_number passed did not designate a valid friend. + 'FRIEND_NOT_FOUND': 2, + # This client is currently not in a call with the friend. Before the call is answered, only CANCEL is a valid + # control. + 'FRIEND_NOT_IN_CALL': 3, + # Happens if user tried to pause an already paused call or if trying to resume a call that is not paused. + 'INVALID_TRANSITION': 4, +} + +TOXAV_ERR_BIT_RATE_SET = { + # The function returned successfully. + 'OK': 0, + # Synchronization error occurred. + 'SYNC': 1, + # The audio bit rate passed was not one of the supported values. + 'INVALID_AUDIO_BIT_RATE': 2, + # The video bit rate passed was not one of the supported values. + 'INVALID_VIDEO_BIT_RATE': 3, + # The friend_number passed did not designate a valid friend. + 'FRIEND_NOT_FOUND': 4, + # This client is currently not in a call with the friend. + 'FRIEND_NOT_IN_CALL': 5, +} + +TOXAV_ERR_SEND_FRAME = { + # The function returned successfully. + 'OK': 0, + # In case of video, one of Y, U, or V was NULL. In case of audio, the samples data pointer was NULL. + 'NULL': 1, + # The friend_number passed did not designate a valid friend. + 'FRIEND_NOT_FOUND': 2, + # This client is currently not in a call with the friend. + 'FRIEND_NOT_IN_CALL': 3, + # Synchronization error occurred. + 'SYNC': 4, + # One of the frame parameters was invalid. E.g. the resolution may be too small or too large, or the audio sampling + # rate may be unsupported. + 'INVALID': 5, + # Either friend turned off audio or video receiving or we turned off sending for the said payload. + 'PAYLOAD_TYPE_DISABLED': 6, + # Failed to push frame through rtp interface. + 'RTP_FAILED': 7, +} diff --git a/toxygen/wrapper/toxcore_enums_and_consts.py b/toxygen/wrapper/toxcore_enums_and_consts.py new file mode 100644 index 0000000..b34e272 --- /dev/null +++ b/toxygen/wrapper/toxcore_enums_and_consts.py @@ -0,0 +1,954 @@ +TOX_USER_STATUS = { + 'NONE': 0, + 'AWAY': 1, + 'BUSY': 2, +} + +TOX_MESSAGE_TYPE = { + 'NORMAL': 0, + 'ACTION': 1, +} + +TOX_PROXY_TYPE = { + 'NONE': 0, + 'HTTP': 1, + 'SOCKS5': 2, +} + +TOX_SAVEDATA_TYPE = { + 'NONE': 0, + 'TOX_SAVE': 1, + 'SECRET_KEY': 2, +} + +TOX_ERR_OPTIONS_NEW = { + 'OK': 0, + 'MALLOC': 1, +} + +TOX_ERR_NEW = { + 'OK': 0, + 'NULL': 1, + 'MALLOC': 2, + 'PORT_ALLOC': 3, + 'PROXY_BAD_TYPE': 4, + 'PROXY_BAD_HOST': 5, + 'PROXY_BAD_PORT': 6, + 'PROXY_NOT_FOUND': 7, + 'LOAD_ENCRYPTED': 8, + 'LOAD_BAD_FORMAT': 9, +} + +TOX_ERR_BOOTSTRAP = { + 'OK': 0, + 'NULL': 1, + 'BAD_HOST': 2, + 'BAD_PORT': 3, +} + +TOX_CONNECTION = { + 'NONE': 0, + 'TCP': 1, + 'UDP': 2, +} + +TOX_ERR_SET_INFO = { + 'OK': 0, + 'NULL': 1, + 'TOO_LONG': 2, +} + +TOX_ERR_FRIEND_ADD = { + 'OK': 0, + 'NULL': 1, + 'TOO_LONG': 2, + 'NO_MESSAGE': 3, + 'OWN_KEY': 4, + 'ALREADY_SENT': 5, + 'BAD_CHECKSUM': 6, + 'SET_NEW_NOSPAM': 7, + 'MALLOC': 8, +} + +TOX_ERR_FRIEND_DELETE = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_BY_PUBLIC_KEY = { + 'OK': 0, + 'NULL': 1, + 'NOT_FOUND': 2, +} + +TOX_ERR_FRIEND_GET_PUBLIC_KEY = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_GET_LAST_ONLINE = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_QUERY = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, +} + +TOX_ERR_SET_TYPING = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, +} + +TOX_ERR_FRIEND_SEND_MESSAGE = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'SENDQ': 4, + 'TOO_LONG': 5, + 'EMPTY': 6, +} + +TOX_FILE_KIND = { + 'DATA': 0, + 'AVATAR': 1, +} + +TOX_FILE_CONTROL = { + 'RESUME': 0, + 'PAUSE': 1, + 'CANCEL': 2, +} + +TOX_ERR_FILE_CONTROL = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, + 'FRIEND_NOT_CONNECTED': 2, + 'NOT_FOUND': 3, + 'NOT_PAUSED': 4, + 'DENIED': 5, + 'ALREADY_PAUSED': 6, + 'SENDQ': 7, +} + +TOX_ERR_FILE_SEEK = { + 'OK': 0, + 'FRIEND_NOT_FOUND': 1, + 'FRIEND_NOT_CONNECTED': 2, + 'NOT_FOUND': 3, + 'DENIED': 4, + 'INVALID_POSITION': 5, + 'SENDQ': 6, +} + +TOX_ERR_FILE_GET = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'NOT_FOUND': 3, +} + +TOX_ERR_FILE_SEND = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'NAME_TOO_LONG': 4, + 'TOO_MANY': 5, +} + +TOX_ERR_FILE_SEND_CHUNK = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'NOT_FOUND': 4, + 'NOT_TRANSFERRING': 5, + 'INVALID_LENGTH': 6, + 'SENDQ': 7, + 'WRONG_POSITION': 8, +} + +TOX_ERR_FRIEND_CUSTOM_PACKET = { + 'OK': 0, + 'NULL': 1, + 'FRIEND_NOT_FOUND': 2, + 'FRIEND_NOT_CONNECTED': 3, + 'INVALID': 4, + 'EMPTY': 5, + 'TOO_LONG': 6, + 'SENDQ': 7, +} + +TOX_ERR_GET_PORT = { + 'OK': 0, + 'NOT_BOUND': 1, +} + +TOX_GROUP_PRIVACY_STATE = { + + # + # The group is considered to be public. Anyone may join the group using the Chat ID. + # + # If the group is in this state, even if the Chat ID is never explicitly shared + # with someone outside of the group, information including the Chat ID, IP addresses, + # and peer ID's (but not Tox ID's) is visible to anyone with access to a node + # storing a DHT entry for the given group. + # + 'PUBLIC': 0, + + # + # The group is considered to be private. The only way to join the group is by having + # someone in your contact list send you an invite. + # + # If the group is in this state, no group information (mentioned above) is present in the DHT; + # the DHT is not used for any purpose at all. If a public group is set to private, + # all DHT information related to the group will expire shortly. + # + 'PRIVATE': 1 +} + +TOX_GROUP_ROLE = { + + # + # May kick and ban all other peers as well as set their role to anything (except founder). + # Founders may also set the group password, toggle the privacy state, and set the peer limit. + # + 'FOUNDER': 0, + + # + # May kick, ban and set the user and observer roles for peers below this role. + # May also set the group topic. + # + 'MODERATOR': 1, + + # + # May communicate with other peers normally. + # + 'USER': 2, + + # + # May observe the group and ignore peers; may not communicate with other peers or with the group. + # + 'OBSERVER': 3 +} + +TOX_ERR_GROUP_NEW = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_NEW_OK': 0, + + # + # The group name exceeded TOX_GROUP_MAX_GROUP_NAME_LENGTH. + # + 'TOX_ERR_GROUP_NEW_TOO_LONG': 1, + + # + # group_name is NULL or length is zero. + # + 'TOX_ERR_GROUP_NEW_EMPTY': 2, + + # + # TOX_GROUP_PRIVACY_STATE is an invalid type. + # + 'TOX_ERR_GROUP_NEW_PRIVACY': 3, + + # + # The group instance failed to initialize. + # + 'TOX_ERR_GROUP_NEW_INIT': 4, + + # + # The group state failed to initialize. This usually indicates that something went wrong + # related to cryptographic signing. + # + 'TOX_ERR_GROUP_NEW_STATE': 5, + + # + # The group failed to announce to the DHT. This indicates a network related error. + # + 'TOX_ERR_GROUP_NEW_ANNOUNCE': 6, +} + +TOX_ERR_GROUP_JOIN = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_JOIN_OK': 0, + + # + # The group instance failed to initialize. + # + 'TOX_ERR_GROUP_JOIN_INIT': 1, + + # + # The chat_id pointer is set to NULL or a group with chat_id already exists. This usually + # happens if the client attempts to create multiple sessions for the same group. + # + 'TOX_ERR_GROUP_JOIN_BAD_CHAT_ID': 2, + + # + # Password length exceeded TOX_GROUP_MAX_PASSWORD_SIZE. + # + 'TOX_ERR_GROUP_JOIN_TOO_LONG': 3, +} + +TOX_ERR_GROUP_RECONNECT = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_RECONNECT_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND': 1, +} + +TOX_ERR_GROUP_LEAVE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_LEAVE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_LEAVE_GROUP_NOT_FOUND': 1, + + # + # Message length exceeded 'TOX_GROUP_MAX_PART_LENGTH. + # + 'TOX_ERR_GROUP_LEAVE_TOO_LONG': 2, + + # + # The parting packet failed to send. + # + 'TOX_ERR_GROUP_LEAVE_FAIL_SEND': 3, + + # + # The group chat instance failed to be deleted. This may occur due to memory related errors. + # + 'TOX_ERR_GROUP_LEAVE_DELETE_FAIL': 4, +} + +TOX_ERR_GROUP_SELF_QUERY = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SELF_QUERY_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND': 1, +} + + +TOX_ERR_GROUP_SELF_NAME_SET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_GROUP_NOT_FOUND': 1, + + # + # Name length exceeded 'TOX_MAX_NAME_LENGTH. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_TOO_LONG': 2, + + # + # The length given to the set function is zero or name is a NULL pointer. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_INVALID': 3, + + # + # The name is already taken by another peer in the group. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_TAKEN': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_SELF_NAME_SET_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_SELF_STATUS_SET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_GROUP_NOT_FOUND': 1, + + # + # An invalid type was passed to the set function. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_INVALID': 2, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_SELF_STATUS_SET_FAIL_SEND': 3 +} + +TOX_ERR_GROUP_PEER_QUERY = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_PEER_QUERY_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND': 2 +} + +TOX_ERR_GROUP_STATE_QUERIES = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_STATE_QUERIES_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND': 1 +} + + +TOX_ERR_GROUP_TOPIC_SET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_TOPIC_SET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_TOPIC_SET_GROUP_NOT_FOUND': 1, + + # + # Topic length exceeded 'TOX_GROUP_MAX_TOPIC_LENGTH. + # + 'TOX_ERR_GROUP_TOPIC_SET_TOO_LONG': 2, + + # + # The caller does not have the required permissions to set the topic. + # + 'TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS': 3, + + # + # The packet could not be created. This error is usually related to cryptographic signing. + # + 'TOX_ERR_GROUP_TOPIC_SET_FAIL_CREATE': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_TOPIC_SET_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_SEND_MESSAGE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_GROUP_NOT_FOUND': 1, + + # + # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_TOO_LONG': 2, + + # + # The message pointer is null or length is zero. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_EMPTY': 3, + + # + # The message type is invalid. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_BAD_TYPE': 4, + + # + # The caller does not have the required permissions to send group messages. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS': 5, + + # + # Packet failed to send. + # + 'TOX_ERR_GROUP_SEND_MESSAGE_FAIL_SEND': 6 +} + +TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PEER_NOT_FOUND': 2, + + # + # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_TOO_LONG': 3, + + # + # The message pointer is null or length is zero. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY': 4, + + # + # The caller does not have the required permissions to send group messages. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS': 5, + + # + # Packet failed to send. + # + 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_FAIL_SEND': 6 +} + +TOX_ERR_GROUP_SEND_CUSTOM_PACKET = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_GROUP_NOT_FOUND': 1, + + # + # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_TOO_LONG': 2, + + # + # The message pointer is null or length is zero. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_EMPTY': 3, + + # + # The caller does not have the required permissions to send group messages. + # + 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_PERMISSIONS': 4 +} + +TOX_ERR_GROUP_INVITE_FRIEND = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_GROUP_NOT_FOUND': 1, + + # + # The friend number passed did not designate a valid friend. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_FRIEND_NOT_FOUND': 2, + + # + # Creation of the invite packet failed. This indicates a network related error. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_INVITE_FAIL': 3, + + # + # Packet failed to send. + # + 'TOX_ERR_GROUP_INVITE_FRIEND_FAIL_SEND': 4 +} + +TOX_ERR_GROUP_INVITE_ACCEPT = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_OK': 0, + + # + # The invite data is not in the expected format. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_BAD_INVITE': 1, + + # + # The group instance failed to initialize. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_INIT_FAILED': 2, + + # + # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE. + # + 'TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG': 3 +} + +TOX_GROUP_JOIN_FAIL = { + + # + # You are using the same nickname as someone who is already in the group. + # + 'TOX_GROUP_JOIN_FAIL_NAME_TAKEN': 0, + + # + # The group peer limit has been reached. + # + 'TOX_GROUP_JOIN_FAIL_PEER_LIMIT': 1, + + # + # You have supplied an invalid password. + # + 'TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD': 2, + + # + # The join attempt failed due to an unspecified error. This often occurs when the group is + # not found in the DHT. + # + 'TOX_GROUP_JOIN_FAIL_UNKNOWN': 3 +} + +TOX_ERR_GROUP_FOUNDER_SET_PASSWORD = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_GROUP_NOT_FOUND': 1, + + # + # The caller does not have the required permissions to set the password. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS': 2, + + # + # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG': 3, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_FAIL_SEND': 4 +} + +TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_GROUP_NOT_FOUND': 1, + + # + # 'TOX_GROUP_PRIVACY_STATE is an invalid type. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_INVALID': 2, + + # + # The caller does not have the required permissions to set the privacy state. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS': 3, + + # + # The privacy state could not be set. This may occur due to an error related to + # cryptographic signing of the new shared state. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SET': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_GROUP_NOT_FOUND': 1, + + # + # The caller does not have the required permissions to set the peer limit. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS': 2, + + # + # The peer limit could not be set. This may occur due to an error related to + # cryptographic signing of the new shared state. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SET': 3, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SEND': 4 +} + +TOX_ERR_GROUP_TOGGLE_IGNORE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_TOGGLE_IGNORE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_TOGGLE_IGNORE_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_TOGGLE_IGNORE_PEER_NOT_FOUND': 2 +} + +TOX_ERR_GROUP_MOD_SET_ROLE = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. Note: you cannot set your own role. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND': 2, + + # + # The caller does not have the required permissions for this action. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS': 3, + + # + # The role assignment is invalid. This will occur if you try to set a peer's role to + # the role they already have. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT': 4, + + # + # The role was not successfully set. This may occur if something goes wrong with role setting': , + # or if the packet fails to send. + # + 'TOX_ERR_GROUP_MOD_SET_ROLE_FAIL_ACTION': 5 +} + +TOX_ERR_GROUP_MOD_REMOVE_PEER = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_GROUP_NOT_FOUND': 1, + + # + # The ID passed did not designate a valid peer. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_PEER_NOT_FOUND': 2, + + # + # The caller does not have the required permissions for this action. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS': 3, + + # + # The peer could not be removed from the group. + # + # If a ban was set': , this error indicates that the ban entry could not be created. + # This is usually due to the peer's IP address already occurring in the ban list. It may also + # be due to the entry containing invalid peer information': , or a failure to cryptographically + # authenticate the entry. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_ACTION': 4, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_SEND': 5 +} + +TOX_ERR_GROUP_MOD_REMOVE_BAN = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_GROUP_NOT_FOUND': 1, + + # + # The caller does not have the required permissions for this action. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS': 2, + + # + # The ban entry could not be removed. This may occur if ban_id does not designate + # a valid ban entry. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION': 3, + + # + # The packet failed to send. + # + 'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_SEND': 4 +} + +TOX_GROUP_MOD_EVENT = { + + # + # A peer has been kicked from the group. + # + 'KICK': 0, + + # + # A peer has been banned from the group. + # + 'BAN': 1, + + # + # A peer as been given the observer role. + # + 'OBSERVER': 2, + + # + # A peer has been given the user role. + # + 'USER': 3, + + # + # A peer has been given the moderator role. + # + 'MODERATOR': 4, +} + +TOX_ERR_GROUP_BAN_QUERY = { + + # + # The function returned successfully. + # + 'TOX_ERR_GROUP_BAN_QUERY_OK': 0, + + # + # The group number passed did not designate a valid group. + # + 'TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND': 1, + + # + # The ban_id does not designate a valid ban list entry. + # + 'TOX_ERR_GROUP_BAN_QUERY_BAD_ID': 2, +} + + +TOX_GROUP_BAN_TYPE = { + + 'IP_PORT': 0, + + 'PUBLIC_KEY': 1, + + 'NICK': 2 +} + +TOX_PUBLIC_KEY_SIZE = 32 + +TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6 + +TOX_MAX_FRIEND_REQUEST_LENGTH = 1016 + +TOX_MAX_MESSAGE_LENGTH = 1372 + +TOX_GROUP_MAX_TOPIC_LENGTH = 512 + +TOX_GROUP_MAX_PART_LENGTH = 128 + +TOX_GROUP_MAX_GROUP_NAME_LENGTH = 48 + +TOX_GROUP_MAX_PASSWORD_SIZE = 32 + +TOX_GROUP_CHAT_ID_SIZE = 32 + +TOX_GROUP_PEER_PUBLIC_KEY_SIZE = 32 + +TOX_MAX_NAME_LENGTH = 128 + +TOX_MAX_STATUS_MESSAGE_LENGTH = 1007 + +TOX_SECRET_KEY_SIZE = 32 + +TOX_FILE_ID_LENGTH = 32 + +TOX_HASH_LENGTH = 32 + +TOX_MAX_CUSTOM_PACKET_SIZE = 1373 diff --git a/toxygen/wrapper/toxencryptsave.py b/toxygen/wrapper/toxencryptsave.py new file mode 100644 index 0000000..31de085 --- /dev/null +++ b/toxygen/wrapper/toxencryptsave.py @@ -0,0 +1,74 @@ +from wrapper import libtox +from ctypes import c_size_t, create_string_buffer, byref, c_int, ArgumentError, c_char_p, c_bool +from wrapper.toxencryptsave_enums_and_consts import * + + +class ToxEncryptSave: + + def __init__(self): + self.libtoxencryptsave = libtox.LibToxEncryptSave() + + def is_data_encrypted(self, data): + """ + Checks if given data is encrypted + """ + func = self.libtoxencryptsave.tox_is_data_encrypted + func.restype = c_bool + result = func(c_char_p(bytes(data))) + return result + + def pass_encrypt(self, data, password): + """ + Encrypts the given data with the given password. + + :return: output array + """ + out = create_string_buffer(len(data) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH) + tox_err_encryption = c_int() + self.libtoxencryptsave.tox_pass_encrypt(c_char_p(data), + c_size_t(len(data)), + c_char_p(bytes(password, 'utf-8')), + c_size_t(len(password)), + out, + byref(tox_err_encryption)) + tox_err_encryption = tox_err_encryption.value + if tox_err_encryption == TOX_ERR_ENCRYPTION['OK']: + return out[:] + elif tox_err_encryption == TOX_ERR_ENCRYPTION['NULL']: + raise ArgumentError('Some input data, or maybe the output pointer, was null.') + elif tox_err_encryption == TOX_ERR_ENCRYPTION['KEY_DERIVATION_FAILED']: + raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a' + ' lack of memory issue. The functions accepting keys do not produce this error.') + elif tox_err_encryption == TOX_ERR_ENCRYPTION['FAILED']: + raise RuntimeError('The encryption itself failed.') + + def pass_decrypt(self, data, password): + """ + Decrypts the given data with the given password. + + :return: output array + """ + out = create_string_buffer(len(data) - TOX_PASS_ENCRYPTION_EXTRA_LENGTH) + tox_err_decryption = c_int() + self.libtoxencryptsave.tox_pass_decrypt(c_char_p(bytes(data)), + c_size_t(len(data)), + c_char_p(bytes(password, 'utf-8')), + c_size_t(len(password)), + out, + byref(tox_err_decryption)) + tox_err_decryption = tox_err_decryption.value + if tox_err_decryption == TOX_ERR_DECRYPTION['OK']: + return out[:] + elif tox_err_decryption == TOX_ERR_DECRYPTION['NULL']: + raise ArgumentError('Some input data, or maybe the output pointer, was null.') + elif tox_err_decryption == TOX_ERR_DECRYPTION['INVALID_LENGTH']: + raise ArgumentError('The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes') + elif tox_err_decryption == TOX_ERR_DECRYPTION['BAD_FORMAT']: + raise ArgumentError('The input data is missing the magic number (i.e. wasn\'t created by this module, or is' + ' corrupted)') + elif tox_err_decryption == TOX_ERR_DECRYPTION['KEY_DERIVATION_FAILED']: + raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a' + ' lack of memory issue. The functions accepting keys do not produce this error.') + elif tox_err_decryption == TOX_ERR_DECRYPTION['FAILED']: + raise RuntimeError('The encrypted byte array could not be decrypted. Either the data was corrupt or the ' + 'password/key was incorrect.') diff --git a/toxygen/wrapper/toxencryptsave_enums_and_consts.py b/toxygen/wrapper/toxencryptsave_enums_and_consts.py new file mode 100644 index 0000000..cf795f8 --- /dev/null +++ b/toxygen/wrapper/toxencryptsave_enums_and_consts.py @@ -0,0 +1,29 @@ +TOX_ERR_ENCRYPTION = { + # The function returned successfully. + 'OK': 0, + # Some input data, or maybe the output pointer, was null. + 'NULL': 1, + # The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The + # functions accepting keys do not produce this error. + 'KEY_DERIVATION_FAILED': 2, + # The encryption itself failed. + 'FAILED': 3 +} + +TOX_ERR_DECRYPTION = { + # The function returned successfully. + 'OK': 0, + # Some input data, or maybe the output pointer, was null. + 'NULL': 1, + # The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes + 'INVALID_LENGTH': 2, + # The input data is missing the magic number (i.e. wasn't created by this module, or is corrupted) + 'BAD_FORMAT': 3, + # The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The + # functions accepting keys do not produce this error. + 'KEY_DERIVATION_FAILED': 4, + # The encrypted byte array could not be decrypted. Either the data was corrupt or the password/key was incorrect. + 'FAILED': 5, +} + +TOX_PASS_ENCRYPTION_EXTRA_LENGTH = 80 From 6f0c1a444edd5cada66a5ce1bc29dc5d688664dc Mon Sep 17 00:00:00 2001 From: emdee Date: Tue, 27 Sep 2022 12:41:23 +0000 Subject: [PATCH 149/217] merge in next_gen branch --- toxygen/avwidgets.py | 134 -- toxygen/basecontact.py | 118 -- toxygen/bootstrap.py | 75 - toxygen/callbacks.py | 469 ------ toxygen/calls.py | 339 ----- toxygen/contact.py | 288 ---- toxygen/file_transfers.py | 347 ----- toxygen/friend.py | 75 - toxygen/group_chat.py | 49 - toxygen/history.py | 215 --- toxygen/items_factory.py | 68 - toxygen/libtox.py | 59 - toxygen/list_items.py | 545 ------- toxygen/loginscreen.py | 103 -- toxygen/mainscreen.py | 757 --------- toxygen/mainscreen_widgets.py | 492 ------ toxygen/menu.py | 1095 ------------- toxygen/messages.py | 113 -- toxygen/nodes.json | 1 - toxygen/notifications.py | 71 - toxygen/passwordscreen.py | 154 -- toxygen/plugin_support.py | 176 --- toxygen/profile.py | 1466 ------------------ toxygen/screen_sharing.py | 22 - toxygen/settings.py | 293 ---- toxygen/smileys.py | 88 -- toxygen/tox.py | 1601 -------------------- toxygen/tox_dns.py | 59 - toxygen/toxav.py | 362 ----- toxygen/toxav_enums.py | 131 -- toxygen/toxcore_enums_and_consts.py | 220 --- toxygen/toxencryptsave.py | 74 - toxygen/toxencryptsave_enums_and_consts.py | 29 - toxygen/toxes.py | 28 - toxygen/updater.py | 110 -- toxygen/util.py | 107 -- toxygen/widgets.py | 166 -- 37 files changed, 10499 deletions(-) delete mode 100644 toxygen/avwidgets.py delete mode 100644 toxygen/basecontact.py delete mode 100644 toxygen/bootstrap.py delete mode 100644 toxygen/callbacks.py delete mode 100644 toxygen/calls.py delete mode 100644 toxygen/contact.py delete mode 100644 toxygen/file_transfers.py delete mode 100644 toxygen/friend.py delete mode 100644 toxygen/group_chat.py delete mode 100644 toxygen/history.py delete mode 100644 toxygen/items_factory.py delete mode 100644 toxygen/libtox.py delete mode 100644 toxygen/list_items.py delete mode 100644 toxygen/loginscreen.py delete mode 100644 toxygen/mainscreen.py delete mode 100644 toxygen/mainscreen_widgets.py delete mode 100644 toxygen/menu.py delete mode 100644 toxygen/messages.py delete mode 100644 toxygen/nodes.json delete mode 100644 toxygen/notifications.py delete mode 100644 toxygen/passwordscreen.py delete mode 100644 toxygen/plugin_support.py delete mode 100644 toxygen/profile.py delete mode 100644 toxygen/screen_sharing.py delete mode 100644 toxygen/settings.py delete mode 100644 toxygen/smileys.py delete mode 100644 toxygen/tox.py delete mode 100644 toxygen/tox_dns.py delete mode 100644 toxygen/toxav.py delete mode 100644 toxygen/toxav_enums.py delete mode 100644 toxygen/toxcore_enums_and_consts.py delete mode 100644 toxygen/toxencryptsave.py delete mode 100644 toxygen/toxencryptsave_enums_and_consts.py delete mode 100644 toxygen/toxes.py delete mode 100644 toxygen/updater.py delete mode 100644 toxygen/util.py delete mode 100644 toxygen/widgets.py diff --git a/toxygen/avwidgets.py b/toxygen/avwidgets.py deleted file mode 100644 index 8c81387..0000000 --- a/toxygen/avwidgets.py +++ /dev/null @@ -1,134 +0,0 @@ -from PyQt5 import QtCore, QtGui, QtWidgets -import widgets -import profile -import util -import pyaudio -import wave -import settings -from util import curr_directory - - -class IncomingCallWidget(widgets.CenteredWidget): - - def __init__(self, friend_number, text, name): - super(IncomingCallWidget, self).__init__() - self.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowStaysOnTopHint) - self.resize(QtCore.QSize(500, 270)) - self.avatar_label = QtWidgets.QLabel(self) - self.avatar_label.setGeometry(QtCore.QRect(10, 20, 64, 64)) - self.avatar_label.setScaledContents(False) - self.name = widgets.DataLabel(self) - self.name.setGeometry(QtCore.QRect(90, 20, 300, 25)) - self._friend_number = friend_number - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPointSize(16) - font.setBold(True) - self.name.setFont(font) - self.call_type = widgets.DataLabel(self) - self.call_type.setGeometry(QtCore.QRect(90, 55, 300, 25)) - self.call_type.setFont(font) - self.accept_audio = QtWidgets.QPushButton(self) - self.accept_audio.setGeometry(QtCore.QRect(20, 100, 150, 150)) - self.accept_video = QtWidgets.QPushButton(self) - self.accept_video.setGeometry(QtCore.QRect(170, 100, 150, 150)) - self.decline = QtWidgets.QPushButton(self) - self.decline.setGeometry(QtCore.QRect(320, 100, 150, 150)) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/accept_audio.png') - icon = QtGui.QIcon(pixmap) - self.accept_audio.setIcon(icon) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/accept_video.png') - icon = QtGui.QIcon(pixmap) - self.accept_video.setIcon(icon) - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/decline_call.png') - icon = QtGui.QIcon(pixmap) - self.decline.setIcon(icon) - self.accept_audio.setIconSize(QtCore.QSize(150, 150)) - self.accept_video.setIconSize(QtCore.QSize(140, 140)) - self.decline.setIconSize(QtCore.QSize(140, 140)) - self.accept_audio.setStyleSheet("QPushButton { border: none }") - self.accept_video.setStyleSheet("QPushButton { border: none }") - self.decline.setStyleSheet("QPushButton { border: none }") - self.setWindowTitle(text) - self.name.setText(name) - self.call_type.setText(text) - self._processing = False - self.accept_audio.clicked.connect(self.accept_call_with_audio) - self.accept_video.clicked.connect(self.accept_call_with_video) - self.decline.clicked.connect(self.decline_call) - - class SoundPlay(QtCore.QThread): - - def __init__(self): - QtCore.QThread.__init__(self) - self.a = None - - def run(self): - class AudioFile: - chunk = 1024 - - def __init__(self, fl): - self.stop = False - self.fl = fl - self.wf = wave.open(self.fl, 'rb') - self.p = pyaudio.PyAudio() - self.stream = self.p.open( - format=self.p.get_format_from_width(self.wf.getsampwidth()), - channels=self.wf.getnchannels(), - rate=self.wf.getframerate(), - output=True) - - def play(self): - while not self.stop: - data = self.wf.readframes(self.chunk) - while data and not self.stop: - self.stream.write(data) - data = self.wf.readframes(self.chunk) - self.wf = wave.open(self.fl, 'rb') - - def close(self): - self.stream.close() - self.p.terminate() - - self.a = AudioFile(curr_directory() + '/sounds/call.wav') - self.a.play() - self.a.close() - - if settings.Settings.get_instance()['calls_sound']: - self.thread = SoundPlay() - self.thread.start() - else: - self.thread = None - - def stop(self): - if self.thread is not None: - self.thread.a.stop = True - self.thread.wait() - self.close() - - def accept_call_with_audio(self): - if self._processing: - return - self._processing = True - pr = profile.Profile.get_instance() - pr.accept_call(self._friend_number, True, False) - self.stop() - - def accept_call_with_video(self): - if self._processing: - return - self._processing = True - pr = profile.Profile.get_instance() - pr.accept_call(self._friend_number, True, True) - self.stop() - - def decline_call(self): - if self._processing: - return - self._processing = True - pr = profile.Profile.get_instance() - pr.stop_call(self._friend_number, False) - self.stop() - - def set_pixmap(self, pixmap): - self.avatar_label.setPixmap(pixmap) diff --git a/toxygen/basecontact.py b/toxygen/basecontact.py deleted file mode 100644 index e1243a4..0000000 --- a/toxygen/basecontact.py +++ /dev/null @@ -1,118 +0,0 @@ -from settings import * -from PyQt5 import QtCore, QtGui -from toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE - - -class BaseContact: - """ - Class encapsulating TOX contact - Properties: name (alias of contact or name), status_message, status (connection status) - widget - widget for update, tox id (or public key) - Base class for all contacts. - """ - - def __init__(self, name, status_message, widget, tox_id): - """ - :param name: name, example: 'Toxygen user' - :param status_message: status message, example: 'Toxing on Toxygen' - :param widget: ContactItem instance - :param tox_id: tox id of contact - """ - self._name, self._status_message = name, status_message - self._status, self._widget = None, widget - self._tox_id = tox_id - self.init_widget() - - # ----------------------------------------------------------------------------------------------------------------- - # Name - current name or alias of user - # ----------------------------------------------------------------------------------------------------------------- - - def get_name(self): - return self._name - - def set_name(self, value): - self._name = str(value, 'utf-8') - self._widget.name.setText(self._name) - self._widget.name.repaint() - - name = property(get_name, set_name) - - # ----------------------------------------------------------------------------------------------------------------- - # Status message - # ----------------------------------------------------------------------------------------------------------------- - - def get_status_message(self): - return self._status_message - - def set_status_message(self, value): - self._status_message = str(value, 'utf-8') - self._widget.status_message.setText(self._status_message) - self._widget.status_message.repaint() - - status_message = property(get_status_message, set_status_message) - - # ----------------------------------------------------------------------------------------------------------------- - # Status - # ----------------------------------------------------------------------------------------------------------------- - - def get_status(self): - return self._status - - def set_status(self, value): - self._status = value - self._widget.connection_status.update(value) - - status = property(get_status, set_status) - - # ----------------------------------------------------------------------------------------------------------------- - # TOX ID. WARNING: for friend it will return public key, for profile - full address - # ----------------------------------------------------------------------------------------------------------------- - - def get_tox_id(self): - return self._tox_id - - tox_id = property(get_tox_id) - - # ----------------------------------------------------------------------------------------------------------------- - # Avatars - # ----------------------------------------------------------------------------------------------------------------- - - def load_avatar(self): - """ - Tries to load avatar of contact or uses default avatar - """ - prefix = ProfileHelper.get_path() + 'avatars/' - avatar_path = prefix + '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image - avatar_path = curr_directory() + '/images/avatar.png' - width = self._widget.avatar_label.width() - pixmap = QtGui.QPixmap(avatar_path) - self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation)) - self._widget.avatar_label.repaint() - - def reset_avatar(self): - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if os.path.isfile(avatar_path): - os.remove(avatar_path) - self.load_avatar() - - def set_avatar(self, avatar): - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - with open(avatar_path, 'wb') as f: - f.write(avatar) - self.load_avatar() - - def get_pixmap(self): - return self._widget.avatar_label.pixmap() - - # ----------------------------------------------------------------------------------------------------------------- - # Widgets - # ----------------------------------------------------------------------------------------------------------------- - - def init_widget(self): - if self._widget is not None: - self._widget.name.setText(self._name) - self._widget.status_message.setText(self._status_message) - self._widget.connection_status.update(self._status) - self.load_avatar() diff --git a/toxygen/bootstrap.py b/toxygen/bootstrap.py deleted file mode 100644 index 0589940..0000000 --- a/toxygen/bootstrap.py +++ /dev/null @@ -1,75 +0,0 @@ -import random -import urllib.request -from util import log, curr_directory -import settings -from PyQt5 import QtNetwork, QtCore -import json - - -class Node: - - def __init__(self, node): - self._ip, self._port, self._tox_key = node['ipv4'], node['port'], node['public_key'] - self._priority = random.randint(1, 1000000) if node['status_tcp'] and node['status_udp'] else 0 - - def get_priority(self): - return self._priority - - priority = property(get_priority) - - def get_data(self): - return bytes(self._ip, 'utf-8'), self._port, self._tox_key - - -def generate_nodes(): - with open(curr_directory() + '/nodes.json', 'rt') as fl: - json_nodes = json.loads(fl.read())['nodes'] - nodes = map(lambda json_node: Node(json_node), json_nodes) - sorted_nodes = sorted(nodes, key=lambda x: x.priority)[-4:] - for node in sorted_nodes: - yield node.get_data() - - -def save_nodes(nodes): - if not nodes: - return - print('Saving nodes...') - with open(curr_directory() + '/nodes.json', 'wb') as fl: - fl.write(nodes) - - -def download_nodes_list(): - url = 'https://nodes.tox.chat/json' - s = settings.Settings.get_instance() - if not s['download_nodes_list']: - return - - if not s['proxy_type']: # no proxy - try: - req = urllib.request.Request(url) - req.add_header('Content-Type', 'application/json') - response = urllib.request.urlopen(req) - result = response.read() - save_nodes(result) - except Exception as ex: - log('TOX nodes loading error: ' + str(ex)) - else: # proxy - netman = QtNetwork.QNetworkAccessManager() - proxy = QtNetwork.QNetworkProxy() - proxy.setType( - QtNetwork.QNetworkProxy.Socks5Proxy if s['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) - proxy.setHostName(s['proxy_host']) - proxy.setPort(s['proxy_port']) - netman.setProxy(proxy) - try: - request = QtNetwork.QNetworkRequest() - request.setUrl(QtCore.QUrl(url)) - reply = netman.get(request) - - while not reply.isFinished(): - QtCore.QThread.msleep(1) - QtCore.QCoreApplication.processEvents() - data = bytes(reply.readAll().data()) - save_nodes(data) - except Exception as ex: - log('TOX nodes loading error: ' + str(ex)) diff --git a/toxygen/callbacks.py b/toxygen/callbacks.py deleted file mode 100644 index b59d17c..0000000 --- a/toxygen/callbacks.py +++ /dev/null @@ -1,469 +0,0 @@ -from PyQt5 import QtCore, QtGui, QtWidgets -from notifications import * -from settings import Settings -from profile import Profile -from toxcore_enums_and_consts import * -from toxav_enums import * -from tox import bin_to_string -from plugin_support import PluginLoader -import queue -import threading -import util -import cv2 -import numpy as np - -# ----------------------------------------------------------------------------------------------------------------- -# Threads -# ----------------------------------------------------------------------------------------------------------------- - - -class InvokeEvent(QtCore.QEvent): - EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType()) - - def __init__(self, fn, *args, **kwargs): - QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE) - self.fn = fn - self.args = args - self.kwargs = kwargs - - -class Invoker(QtCore.QObject): - - def event(self, event): - event.fn(*event.args, **event.kwargs) - return True - - -_invoker = Invoker() - - -def invoke_in_main_thread(fn, *args, **kwargs): - QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs)) - - -class FileTransfersThread(threading.Thread): - - def __init__(self): - self._queue = queue.Queue() - self._timeout = 0.01 - self._continue = True - super().__init__() - - def execute(self, function, *args, **kwargs): - self._queue.put((function, args, kwargs)) - - def stop(self): - self._continue = False - - def run(self): - while self._continue: - try: - function, args, kwargs = self._queue.get(timeout=self._timeout) - function(*args, **kwargs) - except queue.Empty: - pass - except queue.Full: - util.log('Queue is Full in _thread') - except Exception as ex: - util.log('Exception in _thread: ' + str(ex)) - - -_thread = FileTransfersThread() - - -def start(): - _thread.start() - - -def stop(): - _thread.stop() - _thread.join() - -# ----------------------------------------------------------------------------------------------------------------- -# Callbacks - current user -# ----------------------------------------------------------------------------------------------------------------- - - -def self_connection_status(tox_link): - """ - Current user changed connection status (offline, UDP, TCP) - """ - def wrapped(tox, connection, user_data): - print('Connection status: ', str(connection)) - profile = Profile.get_instance() - if profile.status is None: - status = tox_link.self_get_status() - invoke_in_main_thread(profile.set_status, status) - elif connection == TOX_CONNECTION['NONE']: - invoke_in_main_thread(profile.set_status, None) - return wrapped - - -# ----------------------------------------------------------------------------------------------------------------- -# Callbacks - friends -# ----------------------------------------------------------------------------------------------------------------- - - -def friend_status(tox, friend_num, new_status, user_data): - """ - Check friend's status (none, busy, away) - """ - print("Friend's #{} status changed!".format(friend_num)) - profile = Profile.get_instance() - friend = profile.get_friend_by_number(friend_num) - if friend.status is None and Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: - sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) - invoke_in_main_thread(friend.set_status, new_status) - invoke_in_main_thread(QtCore.QTimer.singleShot, 5000, lambda: profile.send_files(friend_num)) - invoke_in_main_thread(profile.update_filtration) - - -def friend_connection_status(tox, friend_num, new_status, user_data): - """ - Check friend's connection status (offline, udp, tcp) - """ - print("Friend #{} connection status: {}".format(friend_num, new_status)) - profile = Profile.get_instance() - friend = profile.get_friend_by_number(friend_num) - if new_status == TOX_CONNECTION['NONE']: - invoke_in_main_thread(profile.friend_exit, friend_num) - invoke_in_main_thread(profile.update_filtration) - if Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: - sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS']) - elif friend.status is None: - invoke_in_main_thread(profile.send_avatar, friend_num) - invoke_in_main_thread(PluginLoader.get_instance().friend_online, friend_num) - - -def friend_name(tox, friend_num, name, size, user_data): - """ - Friend changed his name - """ - profile = Profile.get_instance() - print('New name friend #' + str(friend_num)) - invoke_in_main_thread(profile.new_name, friend_num, name) - - -def friend_status_message(tox, friend_num, status_message, size, user_data): - """ - :return: function for callback friend_status_message. It updates friend's status message - and calls window repaint - """ - profile = Profile.get_instance() - friend = profile.get_friend_by_number(friend_num) - invoke_in_main_thread(friend.set_status_message, status_message) - print('User #{} has new status'.format(friend_num)) - invoke_in_main_thread(profile.send_messages, friend_num) - if profile.get_active_number() == friend_num: - invoke_in_main_thread(profile.set_active) - - -def friend_message(window, tray): - """ - New message from friend - """ - def wrapped(tox, friend_number, message_type, message, size, user_data): - profile = Profile.get_instance() - settings = Settings.get_instance() - message = str(message, 'utf-8') - invoke_in_main_thread(profile.new_message, friend_number, message_type, message) - if not window.isActiveWindow(): - friend = profile.get_friend_by_number(friend_number) - if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: - invoke_in_main_thread(tray_notification, friend.name, message, tray, window) - if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: - sound_notification(SOUND_NOTIFICATION['MESSAGE']) - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) - return wrapped - - -def friend_request(tox, public_key, message, message_size, user_data): - """ - Called when user get new friend request - """ - print('Friend request') - profile = Profile.get_instance() - key = ''.join(chr(x) for x in public_key[:TOX_PUBLIC_KEY_SIZE]) - tox_id = bin_to_string(key, TOX_PUBLIC_KEY_SIZE) - if tox_id not in Settings.get_instance()['blocked']: - invoke_in_main_thread(profile.process_friend_request, tox_id, str(message, 'utf-8')) - - -def friend_typing(tox, friend_number, typing, user_data): - invoke_in_main_thread(Profile.get_instance().friend_typing, friend_number, typing) - - -def friend_read_receipt(tox, friend_number, message_id, user_data): - profile = Profile.get_instance() - profile.get_friend_by_number(friend_number).dec_receipt() - if friend_number == profile.get_active_number(): - invoke_in_main_thread(profile.receipt) - -# ----------------------------------------------------------------------------------------------------------------- -# Callbacks - file transfers -# ----------------------------------------------------------------------------------------------------------------- - - -def tox_file_recv(window, tray): - """ - New incoming file - """ - def wrapped(tox, friend_number, file_number, file_type, size, file_name, file_name_size, user_data): - profile = Profile.get_instance() - settings = Settings.get_instance() - if file_type == TOX_FILE_KIND['DATA']: - print('File') - try: - file_name = str(file_name[:file_name_size], 'utf-8') - except: - file_name = 'toxygen_file' - invoke_in_main_thread(profile.incoming_file_transfer, - friend_number, - file_number, - size, - file_name) - if not window.isActiveWindow(): - friend = profile.get_friend_by_number(friend_number) - if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: - file_from = QtWidgets.QApplication.translate("Callback", "File from") - invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window) - if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: - sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER']) - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) - else: # AVATAR - print('Avatar') - invoke_in_main_thread(profile.incoming_avatar, - friend_number, - file_number, - size) - return wrapped - - -def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data): - """ - Incoming chunk - """ - _thread.execute(Profile.get_instance().incoming_chunk, friend_number, file_number, position, - chunk[:length] if length else None) - - -def file_chunk_request(tox, friend_number, file_number, position, size, user_data): - """ - Outgoing chunk - """ - Profile.get_instance().outgoing_chunk(friend_number, file_number, position, size) - - -def file_recv_control(tox, friend_number, file_number, file_control, user_data): - """ - Friend cancelled, paused or resumed file transfer - """ - if file_control == TOX_FILE_CONTROL['CANCEL']: - invoke_in_main_thread(Profile.get_instance().cancel_transfer, friend_number, file_number, True) - elif file_control == TOX_FILE_CONTROL['PAUSE']: - invoke_in_main_thread(Profile.get_instance().pause_transfer, friend_number, file_number, True) - elif file_control == TOX_FILE_CONTROL['RESUME']: - invoke_in_main_thread(Profile.get_instance().resume_transfer, friend_number, file_number, True) - -# ----------------------------------------------------------------------------------------------------------------- -# Callbacks - custom packets -# ----------------------------------------------------------------------------------------------------------------- - - -def lossless_packet(tox, friend_number, data, length, user_data): - """ - Incoming lossless packet - """ - data = data[:length] - plugin = PluginLoader.get_instance() - invoke_in_main_thread(plugin.callback_lossless, friend_number, data) - - -def lossy_packet(tox, friend_number, data, length, user_data): - """ - Incoming lossy packet - """ - data = data[:length] - plugin = PluginLoader.get_instance() - invoke_in_main_thread(plugin.callback_lossy, friend_number, data) - - -# ----------------------------------------------------------------------------------------------------------------- -# Callbacks - audio -# ----------------------------------------------------------------------------------------------------------------- - -def call_state(toxav, friend_number, mask, user_data): - """ - New call state - """ - print(friend_number, mask) - if mask == TOXAV_FRIEND_CALL_STATE['FINISHED'] or mask == TOXAV_FRIEND_CALL_STATE['ERROR']: - invoke_in_main_thread(Profile.get_instance().stop_call, friend_number, True) - else: - Profile.get_instance().call.toxav_call_state_cb(friend_number, mask) - - -def call(toxav, friend_number, audio, video, user_data): - """ - Incoming call from friend - """ - print(friend_number, audio, video) - invoke_in_main_thread(Profile.get_instance().incoming_call, audio, video, friend_number) - - -def callback_audio(toxav, friend_number, samples, audio_samples_per_channel, audio_channels_count, rate, user_data): - """ - New audio chunk - """ - Profile.get_instance().call.audio_chunk( - bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]), - audio_channels_count, - rate) - -# ----------------------------------------------------------------------------------------------------------------- -# Callbacks - video -# ----------------------------------------------------------------------------------------------------------------- - - -def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data): - """ - Creates yuv frame from y, u, v and shows it using OpenCV - For yuv => bgr we need this YUV420 frame: - - width - ------------------------- - | | - | Y | height - | | - ------------------------- - | | | - | U even | U odd | height // 4 - | | | - ------------------------- - | | | - | V even | V odd | height // 4 - | | | - ------------------------- - - width // 2 width // 2 - - It can be created from initial y, u, v using slices - """ - try: - y_size = abs(max(width, abs(ystride))) - u_size = abs(max(width // 2, abs(ustride))) - v_size = abs(max(width // 2, abs(vstride))) - - y = np.asarray(y[:y_size * height], dtype=np.uint8).reshape(height, y_size) - u = np.asarray(u[:u_size * height // 2], dtype=np.uint8).reshape(height // 2, u_size) - v = np.asarray(v[:v_size * height // 2], dtype=np.uint8).reshape(height // 2, v_size) - - width -= width % 4 - height -= height % 4 - - frame = np.zeros((int(height * 1.5), width), dtype=np.uint8) - - frame[:height, :] = y[:height, :width] - frame[height:height * 5 // 4, :width // 2] = u[:height // 2:2, :width // 2] - frame[height:height * 5 // 4, width // 2:] = u[1:height // 2:2, :width // 2] - - frame[height * 5 // 4:, :width // 2] = v[:height // 2:2, :width // 2] - frame[height * 5 // 4:, width // 2:] = v[1:height // 2:2, :width // 2] - - frame = cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_I420) - - invoke_in_main_thread(cv2.imshow, str(friend_number), frame) - except Exception as ex: - print(ex) - -# ----------------------------------------------------------------------------------------------------------------- -# Callbacks - groups -# ----------------------------------------------------------------------------------------------------------------- - - -def group_invite(tox, friend_number, gc_type, data, length, user_data): - invoke_in_main_thread(Profile.get_instance().group_invite, friend_number, gc_type, - bytes(data[:length])) - - -def show_gc_notification(window, tray, message, group_number, peer_number): - profile = Profile.get_instance() - settings = Settings.get_instance() - chat = profile.get_group_by_number(group_number) - peer_name = chat.get_peer_name(peer_number) - if not window.isActiveWindow() and (profile.name in message or settings['group_notifications']): - if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: - invoke_in_main_thread(tray_notification, chat.name + ' ' + peer_name, message, tray, window) - if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: - sound_notification(SOUND_NOTIFICATION['MESSAGE']) - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) - - -def group_message(window, tray): - def wrapped(tox, group_number, peer_number, message, length, user_data): - message = str(message[:length], 'utf-8') - invoke_in_main_thread(Profile.get_instance().new_gc_message, group_number, - peer_number, TOX_MESSAGE_TYPE['NORMAL'], message) - show_gc_notification(window, tray, message, group_number, peer_number) - return wrapped - - -def group_action(window, tray): - def wrapped(tox, group_number, peer_number, message, length, user_data): - message = str(message[:length], 'utf-8') - invoke_in_main_thread(Profile.get_instance().new_gc_message, group_number, - peer_number, TOX_MESSAGE_TYPE['ACTION'], message) - show_gc_notification(window, tray, message, group_number, peer_number) - return wrapped - - -def group_title(tox, group_number, peer_number, title, length, user_data): - invoke_in_main_thread(Profile.get_instance().new_gc_title, group_number, - title[:length]) - - -def group_namelist_change(tox, group_number, peer_number, change, user_data): - invoke_in_main_thread(Profile.get_instance().update_gc, group_number) - -# ----------------------------------------------------------------------------------------------------------------- -# Callbacks - initialization -# ----------------------------------------------------------------------------------------------------------------- - - -def init_callbacks(tox, window, tray): - """ - Initialization of all callbacks. - :param tox: tox instance - :param window: main window - :param tray: tray (for notifications) - """ - tox.callback_self_connection_status(self_connection_status(tox), 0) - - tox.callback_friend_status(friend_status, 0) - tox.callback_friend_message(friend_message(window, tray), 0) - tox.callback_friend_connection_status(friend_connection_status, 0) - tox.callback_friend_name(friend_name, 0) - tox.callback_friend_status_message(friend_status_message, 0) - tox.callback_friend_request(friend_request, 0) - tox.callback_friend_typing(friend_typing, 0) - tox.callback_friend_read_receipt(friend_read_receipt, 0) - - tox.callback_file_recv(tox_file_recv(window, tray), 0) - tox.callback_file_recv_chunk(file_recv_chunk, 0) - tox.callback_file_chunk_request(file_chunk_request, 0) - tox.callback_file_recv_control(file_recv_control, 0) - - toxav = tox.AV - toxav.callback_call_state(call_state, 0) - toxav.callback_call(call, 0) - toxav.callback_audio_receive_frame(callback_audio, 0) - toxav.callback_video_receive_frame(video_receive_frame, 0) - - tox.callback_friend_lossless_packet(lossless_packet, 0) - tox.callback_friend_lossy_packet(lossy_packet, 0) - - tox.callback_group_invite(group_invite) - tox.callback_group_message(group_message(window, tray)) - tox.callback_group_action(group_action(window, tray)) - tox.callback_group_title(group_title) - tox.callback_group_namelist_change(group_namelist_change) diff --git a/toxygen/calls.py b/toxygen/calls.py deleted file mode 100644 index 3d02110..0000000 --- a/toxygen/calls.py +++ /dev/null @@ -1,339 +0,0 @@ -import pyaudio -import time -import threading -import settings -from toxav_enums import * -import cv2 -import itertools -import numpy as np -import screen_sharing -# TODO: play sound until outgoing call will be started or cancelled - - -class Call: - - def __init__(self, out_audio, out_video, in_audio=False, in_video=False): - self._in_audio = in_audio - self._in_video = in_video - self._out_audio = out_audio - self._out_video = out_video - self._is_active = False - - def get_is_active(self): - return self._is_active - - def set_is_active(self, value): - self._is_active = value - - is_active = property(get_is_active, set_is_active) - - # ----------------------------------------------------------------------------------------------------------------- - # Audio - # ----------------------------------------------------------------------------------------------------------------- - - def get_in_audio(self): - return self._in_audio - - def set_in_audio(self, value): - self._in_audio = value - - in_audio = property(get_in_audio, set_in_audio) - - def get_out_audio(self): - return self._out_audio - - def set_out_audio(self, value): - self._out_audio = value - - out_audio = property(get_out_audio, set_out_audio) - - # ----------------------------------------------------------------------------------------------------------------- - # Video - # ----------------------------------------------------------------------------------------------------------------- - - def get_in_video(self): - return self._in_video - - def set_in_video(self, value): - self._in_video = value - - in_video = property(get_in_video, set_in_video) - - def get_out_video(self): - return self._out_video - - def set_out_video(self, value): - self._out_video = value - - out_video = property(get_out_video, set_out_video) - - -class AV: - - def __init__(self, toxav): - self._toxav = toxav - self._running = True - - self._calls = {} # dict: key - friend number, value - Call instance - - self._audio = None - self._audio_stream = None - self._audio_thread = None - self._audio_running = False - self._out_stream = None - - self._audio_rate = 8000 - self._audio_channels = 1 - self._audio_duration = 60 - self._audio_sample_count = self._audio_rate * self._audio_channels * self._audio_duration // 1000 - - self._video = None - self._video_thread = None - self._video_running = False - - self._video_width = 640 - self._video_height = 480 - - def stop(self): - self._running = False - self.stop_audio_thread() - self.stop_video_thread() - - def __contains__(self, friend_number): - return friend_number in self._calls - - # ----------------------------------------------------------------------------------------------------------------- - # Calls - # ----------------------------------------------------------------------------------------------------------------- - - def __call__(self, friend_number, audio, video): - """Call friend with specified number""" - self._toxav.call(friend_number, 32 if audio else 0, 5000 if video else 0) - self._calls[friend_number] = Call(audio, video) - threading.Timer(30.0, lambda: self.finish_not_started_call(friend_number)).start() - - def accept_call(self, friend_number, audio_enabled, video_enabled): - if self._running: - self._calls[friend_number] = Call(audio_enabled, video_enabled) - self._toxav.answer(friend_number, 32 if audio_enabled else 0, 5000 if video_enabled else 0) - if audio_enabled: - self.start_audio_thread() - if video_enabled: - self.start_video_thread() - - def finish_call(self, friend_number, by_friend=False): - if not by_friend: - self._toxav.call_control(friend_number, TOXAV_CALL_CONTROL['CANCEL']) - if friend_number in self._calls: - del self._calls[friend_number] - if not len(list(filter(lambda c: c.out_audio, self._calls))): - self.stop_audio_thread() - if not len(list(filter(lambda c: c.out_video, self._calls))): - self.stop_video_thread() - - def finish_not_started_call(self, friend_number): - if friend_number in self: - call = self._calls[friend_number] - if not call.is_active: - self.finish_call(friend_number) - - def toxav_call_state_cb(self, friend_number, state): - """ - New call state - """ - call = self._calls[friend_number] - call.is_active = True - - call.in_audio = state | TOXAV_FRIEND_CALL_STATE['SENDING_A'] > 0 - call.in_video = state | TOXAV_FRIEND_CALL_STATE['SENDING_V'] > 0 - - if state | TOXAV_FRIEND_CALL_STATE['ACCEPTING_A'] and call.out_audio: - self.start_audio_thread() - - if state | TOXAV_FRIEND_CALL_STATE['ACCEPTING_V'] and call.out_video: - self.start_video_thread() - - def is_video_call(self, number): - return number in self and self._calls[number].in_video - - # ----------------------------------------------------------------------------------------------------------------- - # Threads - # ----------------------------------------------------------------------------------------------------------------- - - def start_audio_thread(self): - """ - Start audio sending - """ - if self._audio_thread is not None: - return - - self._audio_running = True - - self._audio = pyaudio.PyAudio() - self._audio_stream = self._audio.open(format=pyaudio.paInt16, - rate=self._audio_rate, - channels=self._audio_channels, - input=True, - input_device_index=settings.Settings.get_instance().audio['input'], - frames_per_buffer=self._audio_sample_count * 10) - - self._audio_thread = threading.Thread(target=self.send_audio) - self._audio_thread.start() - - def stop_audio_thread(self): - - if self._audio_thread is None: - return - - self._audio_running = False - - self._audio_thread.join() - - self._audio_thread = None - self._audio_stream = None - self._audio = None - - if self._out_stream is not None: - self._out_stream.stop_stream() - self._out_stream.close() - self._out_stream = None - - def start_video_thread(self): - if self._video_thread is not None: - return - - self._video_running = True - s = settings.Settings.get_instance() - self._video_width = s.video['width'] - self._video_height = s.video['height'] - - if s.video['device'] == -1: - self._video = screen_sharing.DesktopGrabber(s.video['x'], s.video['y'], - s.video['width'], s.video['height']) - else: - self._video = cv2.VideoCapture(s.video['device']) - self._video.set(cv2.CAP_PROP_FPS, 25) - self._video.set(cv2.CAP_PROP_FRAME_WIDTH, self._video_width) - self._video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._video_height) - - self._video_thread = threading.Thread(target=self.send_video) - self._video_thread.start() - - def stop_video_thread(self): - if self._video_thread is None: - return - - self._video_running = False - self._video_thread.join() - self._video_thread = None - self._video = None - - # ----------------------------------------------------------------------------------------------------------------- - # Incoming chunks - # ----------------------------------------------------------------------------------------------------------------- - - def audio_chunk(self, samples, channels_count, rate): - """ - Incoming chunk - """ - - if self._out_stream is None: - self._out_stream = self._audio.open(format=pyaudio.paInt16, - channels=channels_count, - rate=rate, - output_device_index=settings.Settings.get_instance().audio['output'], - output=True) - self._out_stream.write(samples) - - # ----------------------------------------------------------------------------------------------------------------- - # AV sending - # ----------------------------------------------------------------------------------------------------------------- - - def send_audio(self): - """ - This method sends audio to friends - """ - - while self._audio_running: - try: - pcm = self._audio_stream.read(self._audio_sample_count) - if pcm: - for friend_num in self._calls: - if self._calls[friend_num].out_audio: - try: - self._toxav.audio_send_frame(friend_num, pcm, self._audio_sample_count, - self._audio_channels, self._audio_rate) - except: - pass - except: - pass - - time.sleep(0.01) - - def send_video(self): - """ - This method sends video to friends - """ - while self._video_running: - try: - result, frame = self._video.read() - if result: - height, width, channels = frame.shape - for friend_num in self._calls: - if self._calls[friend_num].out_video: - try: - y, u, v = self.convert_bgr_to_yuv(frame) - self._toxav.video_send_frame(friend_num, width, height, y, u, v) - except: - pass - except: - pass - - time.sleep(0.01) - - def convert_bgr_to_yuv(self, frame): - """ - :param frame: input bgr frame - :return y, u, v: y, u, v values of frame - - How this function works: - OpenCV creates YUV420 frame from BGR - This frame has following structure and size: - width, height - dim of input frame - width, height * 1.5 - dim of output frame - - width - ------------------------- - | | - | Y | height - | | - ------------------------- - | | | - | U even | U odd | height // 4 - | | | - ------------------------- - | | | - | V even | V odd | height // 4 - | | | - ------------------------- - - width // 2 width // 2 - - Y, U, V can be extracted using slices and joined in one list using itertools.chain.from_iterable() - Function returns bytes(y), bytes(u), bytes(v), because it is required for ctypes - """ - frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV_I420) - - y = frame[:self._video_height, :] - y = list(itertools.chain.from_iterable(y)) - - u = np.zeros((self._video_height // 2, self._video_width // 2), dtype=np.int) - u[::2, :] = frame[self._video_height:self._video_height * 5 // 4, :self._video_width // 2] - u[1::2, :] = frame[self._video_height:self._video_height * 5 // 4, self._video_width // 2:] - u = list(itertools.chain.from_iterable(u)) - v = np.zeros((self._video_height // 2, self._video_width // 2), dtype=np.int) - v[::2, :] = frame[self._video_height * 5 // 4:, :self._video_width // 2] - v[1::2, :] = frame[self._video_height * 5 // 4:, self._video_width // 2:] - v = list(itertools.chain.from_iterable(v)) - - return bytes(y), bytes(u), bytes(v) diff --git a/toxygen/contact.py b/toxygen/contact.py deleted file mode 100644 index 9f27a1d..0000000 --- a/toxygen/contact.py +++ /dev/null @@ -1,288 +0,0 @@ -from PyQt5 import QtCore, QtGui -from history import * -import basecontact -import util -from messages import * -import file_transfers as ft -import re - - -class Contact(basecontact.BaseContact): - """ - Class encapsulating TOX contact - Properties: number, message getter, history etc. Base class for friend and gc classes - """ - - def __init__(self, message_getter, number, name, status_message, widget, tox_id): - """ - :param message_getter: gets messages from db - :param number: number of friend. - """ - super().__init__(name, status_message, widget, tox_id) - self._number = number - self._new_messages = False - self._visible = True - self._alias = False - self._message_getter = message_getter - self._corr = [] - self._unsaved_messages = 0 - self._history_loaded = self._new_actions = False - self._curr_text = self._search_string = '' - self._search_index = 0 - - def __del__(self): - self.set_visibility(False) - del self._widget - if hasattr(self, '_message_getter'): - del self._message_getter - - # ----------------------------------------------------------------------------------------------------------------- - # History support - # ----------------------------------------------------------------------------------------------------------------- - - def load_corr(self, first_time=True): - """ - :param first_time: friend became active, load first part of messages - """ - if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')): - return - if self._message_getter is None: - return - data = list(self._message_getter.get(PAGE_SIZE)) - if data is not None and len(data): - data.reverse() - else: - return - data = list(map(lambda tupl: TextMessage(*tupl), data)) - self._corr = data + self._corr - self._history_loaded = True - - def load_all_corr(self): - """ - Get all chat history from db for current friend - """ - if self._message_getter is None: - return - data = list(self._message_getter.get_all()) - if data is not None and len(data): - data.reverse() - data = list(map(lambda tupl: TextMessage(*tupl), data)) - self._corr = data + self._corr - self._history_loaded = True - - def get_corr_for_saving(self): - """ - Get data to save in db - :return: list of unsaved messages or [] - """ - messages = list(filter(lambda x: x.get_type() <= 1, self._corr)) - return list(map(lambda x: x.get_data(), messages[-self._unsaved_messages:])) if self._unsaved_messages else [] - - def get_corr(self): - return self._corr[:] - - def append_message(self, message): - """ - :param message: text or file transfer message - """ - self._corr.append(message) - if message.get_type() <= 1: - self._unsaved_messages += 1 - - def get_last_message_text(self): - messages = list(filter(lambda x: x.get_type() <= 1 and x.get_owner() != MESSAGE_OWNER['FRIEND'], self._corr)) - if messages: - return messages[-1].get_data()[0] - else: - return '' - - # ----------------------------------------------------------------------------------------------------------------- - # Unsent messages - # ----------------------------------------------------------------------------------------------------------------- - - def get_unsent_messages(self): - """ - :return list of unsent messages - """ - messages = filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr) - return list(messages) - - def get_unsent_messages_for_saving(self): - """ - :return list of unsent messages for saving - """ - messages = filter(lambda x: x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr) - return list(map(lambda x: x.get_data(), messages)) - - def mark_as_sent(self): - try: - message = list(filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr))[0] - message.mark_as_sent() - except Exception as ex: - util.log('Mark as sent ex: ' + str(ex)) - - # ----------------------------------------------------------------------------------------------------------------- - # Message deletion - # ----------------------------------------------------------------------------------------------------------------- - - def delete_message(self, time): - elem = list(filter(lambda x: type(x) in (TextMessage, GroupChatMessage) and x.get_data()[2] == time, self._corr))[0] - tmp = list(filter(lambda x: x.get_type() <= 1, self._corr)) - if elem in tmp[-self._unsaved_messages:] and self._unsaved_messages: - self._unsaved_messages -= 1 - self._corr.remove(elem) - self._message_getter.delete_one() - self._search_index = 0 - - def delete_old_messages(self): - """ - Delete old messages (reduces RAM usage if messages saving is not enabled) - """ - def save_message(x): - if x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None): - return True - return x.get_owner() == MESSAGE_OWNER['NOT_SENT'] - - old = filter(save_message, self._corr[:-SAVE_MESSAGES]) - self._corr = list(old) + self._corr[-SAVE_MESSAGES:] - text_messages = filter(lambda x: x.get_type() <= 1, self._corr) - self._unsaved_messages = min(self._unsaved_messages, len(list(text_messages))) - self._search_index = 0 - - def clear_corr(self, save_unsent=False): - """ - Clear messages list - """ - if hasattr(self, '_message_getter'): - del self._message_getter - self._search_index = 0 - # don't delete data about active file transfer - if not save_unsent: - self._corr = list(filter(lambda x: x.get_type() == 2 and - x.get_status() in ft.ACTIVE_FILE_TRANSFERS, self._corr)) - self._unsaved_messages = 0 - else: - self._corr = list(filter(lambda x: (x.get_type() == 2 and x.get_status() in ft.ACTIVE_FILE_TRANSFERS) - or (x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT']), - self._corr)) - self._unsaved_messages = len(self.get_unsent_messages()) - - # ----------------------------------------------------------------------------------------------------------------- - # Chat history search - # ----------------------------------------------------------------------------------------------------------------- - - def search_string(self, search_string): - self._search_string, self._search_index = search_string, 0 - return self.search_prev() - - def search_prev(self): - while True: - l = len(self._corr) - for i in range(self._search_index - 1, -l - 1, -1): - if self._corr[i].get_type() > 1: - continue - message = self._corr[i].get_data()[0] - if re.search(self._search_string, message, re.IGNORECASE) is not None: - self._search_index = i - return i - self._search_index = -l - self.load_corr(False) - if len(self._corr) == l: - return None # not found - - def search_next(self): - if not self._search_index: - return None - for i in range(self._search_index + 1, 0): - if self._corr[i].get_type() > 1: - continue - message = self._corr[i].get_data()[0] - if re.search(self._search_string, message, re.IGNORECASE) is not None: - self._search_index = i - return i - return None # not found - - # ----------------------------------------------------------------------------------------------------------------- - # Current text - text from message area - # ----------------------------------------------------------------------------------------------------------------- - - def get_curr_text(self): - return self._curr_text - - def set_curr_text(self, value): - self._curr_text = value - - curr_text = property(get_curr_text, set_curr_text) - - # ----------------------------------------------------------------------------------------------------------------- - # Alias support - # ----------------------------------------------------------------------------------------------------------------- - - def set_name(self, value): - """ - Set new name or ignore if alias exists - :param value: new name - """ - if not self._alias: - super().set_name(value) - - def set_alias(self, alias): - self._alias = bool(alias) - - # ----------------------------------------------------------------------------------------------------------------- - # Visibility in friends' list - # ----------------------------------------------------------------------------------------------------------------- - - def get_visibility(self): - return self._visible - - def set_visibility(self, value): - self._visible = value - - visibility = property(get_visibility, set_visibility) - - def set_widget(self, widget): - self._widget = widget - self.init_widget() - - # ----------------------------------------------------------------------------------------------------------------- - # Unread messages and other actions from friend - # ----------------------------------------------------------------------------------------------------------------- - - def get_actions(self): - return self._new_actions - - def set_actions(self, value): - self._new_actions = value - self._widget.connection_status.update(self.status, value) - - actions = property(get_actions, set_actions) # unread messages, incoming files, av calls - - def get_messages(self): - return self._new_messages - - def inc_messages(self): - self._new_messages += 1 - self._new_actions = True - self._widget.connection_status.update(self.status, True) - self._widget.messages.update(self._new_messages) - - def reset_messages(self): - self._new_actions = False - self._new_messages = 0 - self._widget.messages.update(self._new_messages) - self._widget.connection_status.update(self.status, False) - - messages = property(get_messages) - - # ----------------------------------------------------------------------------------------------------------------- - # Friend's number (can be used in toxcore) - # ----------------------------------------------------------------------------------------------------------------- - - def get_number(self): - return self._number - - def set_number(self, value): - self._number = value - - number = property(get_number, set_number) diff --git a/toxygen/file_transfers.py b/toxygen/file_transfers.py deleted file mode 100644 index 1bbabe5..0000000 --- a/toxygen/file_transfers.py +++ /dev/null @@ -1,347 +0,0 @@ -from toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL -from os.path import basename, getsize, exists, dirname -from os import remove, rename, chdir -from time import time, sleep -from tox import Tox -import settings -from PyQt5 import QtCore - - -TOX_FILE_TRANSFER_STATE = { - 'RUNNING': 0, - 'PAUSED_BY_USER': 1, - 'CANCELLED': 2, - 'FINISHED': 3, - 'PAUSED_BY_FRIEND': 4, - 'INCOMING_NOT_STARTED': 5, - 'OUTGOING_NOT_STARTED': 6 -} - -ACTIVE_FILE_TRANSFERS = (0, 1, 4, 5, 6) - -PAUSED_FILE_TRANSFERS = (1, 4, 5, 6) - -DO_NOT_SHOW_ACCEPT_BUTTON = (2, 3, 4, 6) - -SHOW_PROGRESS_BAR = (0, 1, 4) - -ALLOWED_FILES = ('toxygen_inline.png', 'utox-inline.png', 'sticker.png') - - -def is_inline(file_name): - return file_name in ALLOWED_FILES or file_name.startswith('qTox_Screenshot_') or file_name.startswith('qTox_Image_') - - -class StateSignal(QtCore.QObject): - - signal = QtCore.pyqtSignal(int, float, int) # state, progress, time in sec - - -class TransferFinishedSignal(QtCore.QObject): - - signal = QtCore.pyqtSignal(int, int) # friend number, file number - - -class FileTransfer(QtCore.QObject): - """ - Superclass for file transfers - """ - - def __init__(self, path, tox, friend_number, size, file_number=None): - QtCore.QObject.__init__(self) - self._path = path - self._tox = tox - self._friend_number = friend_number - self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] - self._file_number = file_number - self._creation_time = None - self._size = float(size) - self._done = 0 - self._state_changed = StateSignal() - self._finished = TransferFinishedSignal() - self._file_id = None - - def set_tox(self, tox): - self._tox = tox - - def set_state_changed_handler(self, handler): - self._state_changed.signal.connect(handler) - - def set_transfer_finished_handler(self, handler): - self._finished.signal.connect(handler) - - def signal(self): - percentage = self._done / self._size if self._size else 0 - if self._creation_time is None or not percentage: - t = -1 - else: - t = ((time() - self._creation_time) / percentage) * (1 - percentage) - self._state_changed.signal.emit(self.state, percentage, int(t)) - - def finished(self): - self._finished.signal.emit(self._friend_number, self._file_number) - - def get_file_number(self): - return self._file_number - - def get_friend_number(self): - return self._friend_number - - def get_id(self): - return self._file_id - - def get_path(self): - return self._path - - def cancel(self): - self.send_control(TOX_FILE_CONTROL['CANCEL']) - if hasattr(self, '_file'): - self._file.close() - self.signal() - - def cancelled(self): - if hasattr(self, '_file'): - sleep(0.1) - self._file.close() - self.state = TOX_FILE_TRANSFER_STATE['CANCELLED'] - self.signal() - - def pause(self, by_friend): - if not by_friend: - self.send_control(TOX_FILE_CONTROL['PAUSE']) - else: - self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] - self.signal() - - def send_control(self, control): - if self._tox.file_control(self._friend_number, self._file_number, control): - self.state = control - self.signal() - - def get_file_id(self): - return self._tox.file_get_file_id(self._friend_number, self._file_number) - -# ----------------------------------------------------------------------------------------------------------------- -# Send file -# ----------------------------------------------------------------------------------------------------------------- - - -class SendTransfer(FileTransfer): - - def __init__(self, path, tox, friend_number, kind=TOX_FILE_KIND['DATA'], file_id=None): - if path is not None: - self._file = open(path, 'rb') - size = getsize(path) - else: - size = 0 - super(SendTransfer, self).__init__(path, tox, friend_number, size) - self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] - self._file_number = tox.file_send(friend_number, kind, size, file_id, - bytes(basename(path), 'utf-8') if path else b'') - self._file_id = self.get_file_id() - - def send_chunk(self, position, size): - """ - Send chunk - :param position: start position in file - :param size: chunk max size - """ - if self._creation_time is None: - self._creation_time = time() - if size: - self._file.seek(position) - data = self._file.read(size) - self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) - self._done += size - else: - if hasattr(self, '_file'): - self._file.close() - self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self.finished() - self.signal() - - -class SendAvatar(SendTransfer): - """ - Send avatar to friend. Doesn't need file transfer item - """ - - def __init__(self, path, tox, friend_number): - if path is None: - hash = None - else: - with open(path, 'rb') as fl: - hash = Tox.hash(fl.read()) - super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], hash) - - -class SendFromBuffer(FileTransfer): - """ - Send inline image - """ - - def __init__(self, tox, friend_number, data, file_name): - super(SendFromBuffer, self).__init__(None, tox, friend_number, len(data)) - self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] - self._data = data - self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], - len(data), None, bytes(file_name, 'utf-8')) - - def get_data(self): - return self._data - - def send_chunk(self, position, size): - if self._creation_time is None: - self._creation_time = time() - if size: - data = self._data[position:position + size] - self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) - self._done += size - else: - self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self.finished() - self.signal() - - -class SendFromFileBuffer(SendTransfer): - - def __init__(self, *args): - super(SendFromFileBuffer, self).__init__(*args) - - def send_chunk(self, position, size): - super(SendFromFileBuffer, self).send_chunk(position, size) - if not size: - chdir(dirname(self._path)) - remove(self._path) - -# ----------------------------------------------------------------------------------------------------------------- -# Receive file -# ----------------------------------------------------------------------------------------------------------------- - - -class ReceiveTransfer(FileTransfer): - - def __init__(self, path, tox, friend_number, size, file_number, position=0): - super(ReceiveTransfer, self).__init__(path, tox, friend_number, size, file_number) - self._file = open(self._path, 'wb') - self._file_size = position - self._file.truncate(position) - self._missed = set() - self._file_id = self.get_file_id() - self._done = position - - def cancel(self): - super(ReceiveTransfer, self).cancel() - remove(self._path) - - def total_size(self): - self._missed.add(self._file_size) - return min(self._missed) - - def write_chunk(self, position, data): - """ - Incoming chunk - :param position: position in file to save data - :param data: raw data (string) - """ - if self._creation_time is None: - self._creation_time = time() - if data is None: - self._file.close() - self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self.finished() - else: - data = bytearray(data) - if self._file_size < position: - self._file.seek(0, 2) - self._file.write(b'\0' * (position - self._file_size)) - self._missed.add(self._file_size) - else: - self._missed.discard(position) - self._file.seek(position) - self._file.write(data) - l = len(data) - if position + l > self._file_size: - self._file_size = position + l - self._done += l - self.signal() - - -class ReceiveToBuffer(FileTransfer): - """ - Inline image - save in buffer not in file system - """ - - def __init__(self, tox, friend_number, size, file_number): - super(ReceiveToBuffer, self).__init__(None, tox, friend_number, size, file_number) - self._data = bytes() - self._data_size = 0 - - def get_data(self): - return self._data - - def write_chunk(self, position, data): - if self._creation_time is None: - self._creation_time = time() - if data is None: - self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self.finished() - else: - data = bytes(data) - l = len(data) - if self._data_size < position: - self._data += (b'\0' * (position - self._data_size)) - self._data = self._data[:position] + data + self._data[position + l:] - if position + l > self._data_size: - self._data_size = position + l - self._done += l - self.signal() - - -class ReceiveAvatar(ReceiveTransfer): - """ - Get friend's avatar. Doesn't need file transfer item - """ - MAX_AVATAR_SIZE = 512 * 1024 - - def __init__(self, tox, friend_number, size, file_number): - path = settings.ProfileHelper.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) - super(ReceiveAvatar, self).__init__(path + '.tmp', tox, friend_number, size, file_number) - if size > self.MAX_AVATAR_SIZE: - self.send_control(TOX_FILE_CONTROL['CANCEL']) - self._file.close() - remove(path + '.tmp') - elif not size: - self.send_control(TOX_FILE_CONTROL['CANCEL']) - self._file.close() - if exists(path): - remove(path) - self._file.close() - remove(path + '.tmp') - elif exists(path): - hash = self.get_file_id() - with open(path, 'rb') as fl: - data = fl.read() - existing_hash = Tox.hash(data) - if hash == existing_hash: - self.send_control(TOX_FILE_CONTROL['CANCEL']) - self._file.close() - remove(path + '.tmp') - else: - self.send_control(TOX_FILE_CONTROL['RESUME']) - else: - self.send_control(TOX_FILE_CONTROL['RESUME']) - - def write_chunk(self, position, data): - super(ReceiveAvatar, self).write_chunk(position, data) - if self.state: - avatar_path = self._path[:-4] - if exists(avatar_path): - chdir(dirname(avatar_path)) - remove(avatar_path) - rename(self._path, avatar_path) - self.finished(True) - - def finished(self, emit=False): - if emit: - super().finished() diff --git a/toxygen/friend.py b/toxygen/friend.py deleted file mode 100644 index d912708..0000000 --- a/toxygen/friend.py +++ /dev/null @@ -1,75 +0,0 @@ -import contact -from messages import * -import os - - -class Friend(contact.Contact): - """ - Friend in list of friends. - """ - - def __init__(self, message_getter, number, name, status_message, widget, tox_id): - super().__init__(message_getter, number, name, status_message, widget, tox_id) - self._receipts = 0 - - # ----------------------------------------------------------------------------------------------------------------- - # File transfers support - # ----------------------------------------------------------------------------------------------------------------- - - def update_transfer_data(self, file_number, status, inline=None): - """ - Update status of active transfer and load inline if needed - """ - try: - tr = list(filter(lambda x: x.get_type() == MESSAGE_TYPE['FILE_TRANSFER'] and x.is_active(file_number), - self._corr))[0] - tr.set_status(status) - i = self._corr.index(tr) - if inline: # inline was loaded - self._corr.insert(i, inline) - return i - len(self._corr) - except: - pass - - def get_unsent_files(self): - messages = filter(lambda x: type(x) is UnsentFile, self._corr) - return messages - - def clear_unsent_files(self): - self._corr = list(filter(lambda x: type(x) is not UnsentFile, self._corr)) - - def remove_invalid_unsent_files(self): - def is_valid(message): - if type(message) is not UnsentFile: - return True - if message.get_data()[1] is not None: - return True - return os.path.exists(message.get_data()[0]) - self._corr = list(filter(is_valid, self._corr)) - - def delete_one_unsent_file(self, time): - self._corr = list(filter(lambda x: not (type(x) is UnsentFile and x.get_data()[2] == time), self._corr)) - - # ----------------------------------------------------------------------------------------------------------------- - # History support - # ----------------------------------------------------------------------------------------------------------------- - - def get_receipts(self): - return self._receipts - - receipts = property(get_receipts) # read receipts - - def inc_receipts(self): - self._receipts += 1 - - def dec_receipt(self): - if self._receipts: - self._receipts -= 1 - self.mark_as_sent() - - # ----------------------------------------------------------------------------------------------------------------- - # Full status - # ----------------------------------------------------------------------------------------------------------------- - - def get_full_status(self): - return self._status_message diff --git a/toxygen/group_chat.py b/toxygen/group_chat.py deleted file mode 100644 index f7921a1..0000000 --- a/toxygen/group_chat.py +++ /dev/null @@ -1,49 +0,0 @@ -import contact -import util -from PyQt5 import QtGui, QtCore -import toxcore_enums_and_consts as constants - - -class GroupChat(contact.Contact): - - def __init__(self, name, status_message, widget, tox, group_number): - super().__init__(None, group_number, name, status_message, widget, None) - self._tox = tox - self.set_status(constants.TOX_USER_STATUS['NONE']) - - def set_name(self, name): - self._tox.group_set_title(self._number, name) - super().set_name(name) - - def send_message(self, message): - self._tox.group_message_send(self._number, message.encode('utf-8')) - - def new_title(self, title): - super().set_name(title) - - def load_avatar(self): - path = util.curr_directory() + '/images/group.png' - width = self._widget.avatar_label.width() - pixmap = QtGui.QPixmap(path) - self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation)) - self._widget.avatar_label.repaint() - - def remove_invalid_unsent_files(self): - pass - - def get_names(self): - peers_count = self._tox.group_number_peers(self._number) - names = [] - for i in range(peers_count): - name = self._tox.group_peername(self._number, i) - names.append(name) - names = sorted(names, key=lambda n: n.lower()) - return names - - def get_full_status(self): - names = self.get_names() - return '\n'.join(names) - - def get_peer_name(self, peer_number): - return self._tox.group_peername(self._number, peer_number) diff --git a/toxygen/history.py b/toxygen/history.py deleted file mode 100644 index 586981a..0000000 --- a/toxygen/history.py +++ /dev/null @@ -1,215 +0,0 @@ -from sqlite3 import connect -import settings -from os import chdir -import os.path -from toxes import ToxES - - -PAGE_SIZE = 42 - -TIMEOUT = 11 - -SAVE_MESSAGES = 250 - -MESSAGE_OWNER = { - 'ME': 0, - 'FRIEND': 1, - 'NOT_SENT': 2 -} - - -class History: - - def __init__(self, name): - self._name = name - chdir(settings.ProfileHelper.get_path()) - path = settings.ProfileHelper.get_path() + self._name + '.hstr' - if os.path.exists(path): - decr = ToxES.get_instance() - try: - with open(path, 'rb') as fin: - data = fin.read() - if decr.is_data_encrypted(data): - data = decr.pass_decrypt(data) - with open(path, 'wb') as fout: - fout.write(data) - except: - os.remove(path) - db = connect(name + '.hstr', timeout=TIMEOUT) - cursor = db.cursor() - cursor.execute('CREATE TABLE IF NOT EXISTS friends(' - ' tox_id TEXT PRIMARY KEY' - ')') - db.close() - - def save(self): - encr = ToxES.get_instance() - if encr.has_password(): - path = settings.ProfileHelper.get_path() + self._name + '.hstr' - with open(path, 'rb') as fin: - data = fin.read() - data = encr.pass_encrypt(bytes(data)) - with open(path, 'wb') as fout: - fout.write(data) - - def export(self, directory): - path = settings.ProfileHelper.get_path() + self._name + '.hstr' - new_path = directory + self._name + '.hstr' - with open(path, 'rb') as fin: - data = fin.read() - encr = ToxES.get_instance() - if encr.has_password(): - data = encr.pass_encrypt(data) - with open(new_path, 'wb') as fout: - fout.write(data) - - def add_friend_to_db(self, tox_id): - chdir(settings.ProfileHelper.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) - try: - cursor = db.cursor() - cursor.execute('INSERT INTO friends VALUES (?);', (tox_id, )) - cursor.execute('CREATE TABLE id' + tox_id + '(' - ' id INTEGER PRIMARY KEY,' - ' message TEXT,' - ' owner INTEGER,' - ' unix_time REAL,' - ' message_type INTEGER' - ')') - db.commit() - except: - print('Database is locked!') - db.rollback() - finally: - db.close() - - def delete_friend_from_db(self, tox_id): - chdir(settings.ProfileHelper.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) - try: - cursor = db.cursor() - cursor.execute('DELETE FROM friends WHERE tox_id=?;', (tox_id, )) - cursor.execute('DROP TABLE id' + tox_id + ';') - db.commit() - except: - print('Database is locked!') - db.rollback() - finally: - db.close() - - def friend_exists_in_db(self, tox_id): - chdir(settings.ProfileHelper.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) - cursor = db.cursor() - cursor.execute('SELECT 0 FROM friends WHERE tox_id=?', (tox_id, )) - result = cursor.fetchone() - db.close() - return result is not None - - def save_messages_to_db(self, tox_id, messages_iter): - chdir(settings.ProfileHelper.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) - try: - cursor = db.cursor() - cursor.executemany('INSERT INTO id' + tox_id + '(message, owner, unix_time, message_type) ' - 'VALUES (?, ?, ?, ?);', messages_iter) - db.commit() - except: - print('Database is locked!') - db.rollback() - finally: - db.close() - - def update_messages(self, tox_id, unsent_time): - chdir(settings.ProfileHelper.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) - try: - cursor = db.cursor() - cursor.execute('UPDATE id' + tox_id + ' SET owner = 0 ' - 'WHERE unix_time < ' + str(unsent_time) + ' AND owner = 2;') - db.commit() - except: - print('Database is locked!') - db.rollback() - finally: - db.close() - - def delete_message(self, tox_id, time): - start, end = str(time - 0.01), str(time + 0.01) - chdir(settings.ProfileHelper.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) - try: - cursor = db.cursor() - cursor.execute('DELETE FROM id' + tox_id + ' WHERE unix_time < ' + end + ' AND unix_time > ' + - start + ';') - db.commit() - except: - print('Database is locked!') - db.rollback() - finally: - db.close() - - def delete_messages(self, tox_id): - chdir(settings.ProfileHelper.get_path()) - db = connect(self._name + '.hstr', timeout=TIMEOUT) - try: - cursor = db.cursor() - cursor.execute('DELETE FROM id' + tox_id + ';') - db.commit() - except: - print('Database is locked!') - db.rollback() - finally: - db.close() - - def messages_getter(self, tox_id): - return History.MessageGetter(self._name, tox_id) - - class MessageGetter: - - def __init__(self, name, tox_id): - self._count = 0 - self._name = name - self._tox_id = tox_id - self._db = self._cursor = None - - def connect(self): - chdir(settings.ProfileHelper.get_path()) - self._db = connect(self._name + '.hstr', timeout=TIMEOUT) - self._cursor = self._db.cursor() - self._cursor.execute('SELECT message, owner, unix_time, message_type FROM id' + self._tox_id + - ' ORDER BY unix_time DESC;') - - def disconnect(self): - self._db.close() - - def get_one(self): - self.connect() - self.skip() - data = self._cursor.fetchone() - self._count += 1 - self.disconnect() - return data - - def get_all(self): - self.connect() - data = self._cursor.fetchall() - self.disconnect() - self._count = len(data) - return data - - def get(self, count): - self.connect() - self.skip() - data = self._cursor.fetchmany(count) - self.disconnect() - self._count += len(data) - return data - - def skip(self): - if self._count: - self._cursor.fetchmany(self._count) - - def delete_one(self): - if self._count: - self._count -= 1 diff --git a/toxygen/items_factory.py b/toxygen/items_factory.py deleted file mode 100644 index 44a00ad..0000000 --- a/toxygen/items_factory.py +++ /dev/null @@ -1,68 +0,0 @@ -from PyQt5 import QtWidgets, QtCore -from list_items import * - - -class ItemsFactory: - - def __init__(self, friends_list, messages): - self._friends = friends_list - self._messages = messages - - def friend_item(self): - item = ContactItem() - elem = QtWidgets.QListWidgetItem(self._friends) - elem.setSizeHint(QtCore.QSize(250, item.height())) - self._friends.addItem(elem) - self._friends.setItemWidget(elem, item) - return item - - def message_item(self, text, time, name, sent, message_type, append, pixmap): - item = MessageItem(text, time, name, sent, message_type, self._messages) - if pixmap is not None: - item.set_avatar(pixmap) - elem = QtWidgets.QListWidgetItem() - elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) - if append: - self._messages.addItem(elem) - else: - self._messages.insertItem(0, elem) - self._messages.setItemWidget(elem, item) - return item - - def inline_item(self, data, append): - elem = QtWidgets.QListWidgetItem() - item = InlineImageItem(data, self._messages.width(), elem) - elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) - if append: - self._messages.addItem(elem) - else: - self._messages.insertItem(0, elem) - self._messages.setItemWidget(elem, item) - return item - - def unsent_file_item(self, file_name, size, name, time, append): - item = UnsentFileItem(file_name, - size, - name, - time, - self._messages.width()) - elem = QtWidgets.QListWidgetItem() - elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) - if append: - self._messages.addItem(elem) - else: - self._messages.insertItem(0, elem) - self._messages.setItemWidget(elem, item) - return item - - def file_transfer_item(self, data, append): - data.append(self._messages.width()) - item = FileTransferItem(*data) - elem = QtWidgets.QListWidgetItem() - elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) - if append: - self._messages.addItem(elem) - else: - self._messages.insertItem(0, elem) - self._messages.setItemWidget(elem, item) - return item diff --git a/toxygen/libtox.py b/toxygen/libtox.py deleted file mode 100644 index 752798f..0000000 --- a/toxygen/libtox.py +++ /dev/null @@ -1,59 +0,0 @@ -from platform import system -from ctypes import CDLL -import util - - -class LibToxCore: - - def __init__(self): - if system() == 'Windows': - self._libtoxcore = CDLL(util.curr_directory() + '/libs/libtox.dll') - elif system() == 'Darwin': - self._libtoxcore = CDLL('libtoxcore.dylib') - else: - # libtoxcore and libsodium must be installed in your os - try: - self._libtoxcore = CDLL('libtoxcore.so') - except: - self._libtoxcore = CDLL(util.curr_directory() + '/libs/libtoxcore.so') - - def __getattr__(self, item): - return self._libtoxcore.__getattr__(item) - - -class LibToxAV: - - def __init__(self): - if system() == 'Windows': - # on Windows av api is in libtox.dll - self._libtoxav = CDLL(util.curr_directory() + '/libs/libtox.dll') - elif system() == 'Darwin': - self._libtoxav = CDLL('libtoxav.dylib') - else: - # /usr/lib/libtoxav.so must exists - try: - self._libtoxav = CDLL('libtoxav.so') - except: - self._libtoxav = CDLL(util.curr_directory() + '/libs/libtoxav.so') - - def __getattr__(self, item): - return self._libtoxav.__getattr__(item) - - -class LibToxEncryptSave: - - def __init__(self): - if system() == 'Windows': - # on Windows profile encryption api is in libtox.dll - self._lib_tox_encrypt_save = CDLL(util.curr_directory() + '/libs/libtox.dll') - elif system() == 'Darwin': - self._lib_tox_encrypt_save = CDLL('libtoxencryptsave.dylib') - else: - # /usr/lib/libtoxencryptsave.so must exists - try: - self._lib_tox_encrypt_save = CDLL('libtoxencryptsave.so') - except: - self._lib_tox_encrypt_save = CDLL(util.curr_directory() + '/libs/libtoxencryptsave.so') - - def __getattr__(self, item): - return self._lib_tox_encrypt_save.__getattr__(item) diff --git a/toxygen/list_items.py b/toxygen/list_items.py deleted file mode 100644 index 9b92f2a..0000000 --- a/toxygen/list_items.py +++ /dev/null @@ -1,545 +0,0 @@ -from toxcore_enums_and_consts import * -from PyQt5 import QtCore, QtGui, QtWidgets -import profile -from file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR -from util import curr_directory, convert_time, curr_time -from widgets import DataLabel, create_menu -import html as h -import smileys -import settings -import re - - -class MessageEdit(QtWidgets.QTextBrowser): - - def __init__(self, text, width, message_type, parent=None): - super(MessageEdit, self).__init__(parent) - self.urls = {} - self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere) - self.document().setTextWidth(width) - self.setOpenExternalLinks(True) - self.setAcceptRichText(True) - self.setOpenLinks(False) - path = smileys.SmileyLoader.get_instance().get_smileys_path() - if path is not None: - self.setSearchPaths([path]) - self.document().setDefaultStyleSheet('a { color: #306EFF; }') - text = self.decoratedText(text) - if message_type != TOX_MESSAGE_TYPE['NORMAL']: - self.setHtml('

' + text + '

') - else: - self.setHtml(text) - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPixelSize(settings.Settings.get_instance()['message_font_size']) - font.setBold(False) - self.setFont(font) - self.resize(width, self.document().size().height()) - self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse) - self.anchorClicked.connect(self.on_anchor_clicked) - - def contextMenuEvent(self, event): - menu = create_menu(self.createStandardContextMenu(event.pos())) - quote = menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Quote selected text')) - quote.triggered.connect(self.quote_text) - text = self.textCursor().selection().toPlainText() - if not text: - quote.setEnabled(False) - else: - import plugin_support - submenu = plugin_support.PluginLoader.get_instance().get_message_menu(menu, text) - if len(submenu): - plug = menu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Plugins')) - plug.addActions(submenu) - menu.popup(event.globalPos()) - menu.exec_(event.globalPos()) - del menu - - def quote_text(self): - text = self.textCursor().selection().toPlainText() - if text: - import mainscreen - window = mainscreen.MainWindow.get_instance() - text = '>' + '\n>'.join(text.split('\n')) - if window.messageEdit.toPlainText(): - text = '\n' + text - window.messageEdit.appendPlainText(text) - - def on_anchor_clicked(self, url): - text = str(url.toString()) - if text.startswith('tox:'): - import menu - self.add_contact = menu.AddContact(text[4:]) - self.add_contact.show() - else: - QtGui.QDesktopServices.openUrl(url) - self.clearFocus() - - def addAnimation(self, url, fileName): - movie = QtGui.QMovie(self) - movie.setFileName(fileName) - self.urls[movie] = url - movie.frameChanged[int].connect(lambda x: self.animate(movie)) - movie.start() - - def animate(self, movie): - self.document().addResource(QtGui.QTextDocument.ImageResource, - self.urls[movie], - movie.currentPixmap()) - self.setLineWrapColumnOrWidth(self.lineWrapColumnOrWidth()) - - def decoratedText(self, text): - text = h.escape(text) # replace < and > - exp = QtCore.QRegExp( - '(' - '(?:\\b)((www\\.)|(http[s]?|ftp)://)' - '\\w+\\S+)' - '|(?:\\b)(file:///)([\\S| ]*)' - '|(?:\\b)(tox:[a-zA-Z\\d]{76}$)' - '|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)' - '|(?:\\b)(tox:\\S+@\\S+)') - offset = exp.indexIn(text, 0) - while offset != -1: # add links - url = exp.cap() - if exp.cap(2) == 'www.': - html = '{0}'.format(url) - else: - html = '{0}'.format(url) - text = text[:offset] + html + text[offset + len(exp.cap()):] - offset += len(html) - offset = exp.indexIn(text, offset) - arr = text.split('\n') - for i in range(len(arr)): # quotes - if arr[i].startswith('>'): - arr[i] = '' + arr[i][4:] + '' - text = '
'.join(arr) - text = smileys.SmileyLoader.get_instance().add_smileys_to_text(text, self) # smileys - return text - - -class MessageItem(QtWidgets.QWidget): - """ - Message in messages list - """ - def __init__(self, text, time, user='', sent=True, message_type=TOX_MESSAGE_TYPE['NORMAL'], parent=None): - QtWidgets.QWidget.__init__(self, parent) - self.name = DataLabel(self) - self.name.setGeometry(QtCore.QRect(2, 2, 95, 23)) - self.name.setTextFormat(QtCore.Qt.PlainText) - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPointSize(11) - font.setBold(True) - self.name.setFont(font) - self.name.setText(user) - - self.time = QtWidgets.QLabel(self) - self.time.setGeometry(QtCore.QRect(parent.width() - 60, 0, 50, 25)) - font.setPointSize(10) - font.setBold(False) - self.time.setFont(font) - self._time = time - if not sent: - movie = QtGui.QMovie(curr_directory() + '/images/spinner.gif') - self.time.setMovie(movie) - movie.start() - self.t = True - else: - self.time.setText(convert_time(time)) - self.t = False - - self.message = MessageEdit(text, parent.width() - 160, message_type, self) - if message_type != TOX_MESSAGE_TYPE['NORMAL']: - self.name.setStyleSheet("QLabel { color: #5CB3FF; }") - self.message.setAlignment(QtCore.Qt.AlignCenter) - self.time.setStyleSheet("QLabel { color: #5CB3FF; }") - self.message.setGeometry(QtCore.QRect(100, 0, parent.width() - 160, self.message.height())) - self.setFixedHeight(self.message.height()) - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.RightButton and event.x() > self.time.x(): - self.listMenu = QtWidgets.QMenu() - delete_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Delete message')) - delete_item.triggered.connect(self.delete) - parent_position = self.time.mapToGlobal(QtCore.QPoint(0, 0)) - self.listMenu.move(parent_position) - self.listMenu.show() - - def delete(self): - pr = profile.Profile.get_instance() - pr.delete_message(self._time) - - def mark_as_sent(self): - if self.t: - self.time.setText(convert_time(self._time)) - self.t = False - return True - return False - - def set_avatar(self, pixmap): - self.name.setAlignment(QtCore.Qt.AlignCenter) - self.message.setAlignment(QtCore.Qt.AlignVCenter) - self.setFixedHeight(max(self.height(), 36)) - self.name.setFixedHeight(self.height()) - self.message.setFixedHeight(self.height()) - self.name.setPixmap(pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) - - def select_text(self, text): - tmp = self.message.toHtml() - text = h.escape(text) - strings = re.findall(text, tmp, flags=re.IGNORECASE) - for s in strings: - tmp = self.replace_all(tmp, s) - self.message.setHtml(tmp) - - @staticmethod - def replace_all(text, substring): - i, l = 0, len(substring) - while i < len(text) - l + 1: - index = text[i:].find(substring) - if index == -1: - break - i += index - lgt, rgt = text[i:].find('<'), text[i:].find('>') - if rgt < lgt: - i += rgt + 1 - continue - sub = '{}'.format(substring) - text = text[:i] + sub + text[i + l:] - i += len(sub) - return text - - -class ContactItem(QtWidgets.QWidget): - """ - Contact in friends list - """ - - def __init__(self, parent=None): - QtWidgets.QWidget.__init__(self, parent) - mode = settings.Settings.get_instance()['compact_mode'] - self.setBaseSize(QtCore.QSize(250, 40 if mode else 70)) - self.avatar_label = QtWidgets.QLabel(self) - size = 32 if mode else 64 - self.avatar_label.setGeometry(QtCore.QRect(3, 4, size, size)) - self.avatar_label.setScaledContents(False) - self.avatar_label.setAlignment(QtCore.Qt.AlignCenter) - self.name = DataLabel(self) - self.name.setGeometry(QtCore.QRect(50 if mode else 75, 3 if mode else 10, 150, 15 if mode else 25)) - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPointSize(10 if mode else 12) - font.setBold(True) - self.name.setFont(font) - self.status_message = DataLabel(self) - self.status_message.setGeometry(QtCore.QRect(50 if mode else 75, 20 if mode else 30, 170, 15 if mode else 20)) - font.setPointSize(10) - font.setBold(False) - self.status_message.setFont(font) - self.connection_status = StatusCircle(self) - self.connection_status.setGeometry(QtCore.QRect(230, -2 if mode else 5, 32, 32)) - self.messages = UnreadMessagesCount(self) - self.messages.setGeometry(QtCore.QRect(20 if mode else 52, 20 if mode else 50, 30, 20)) - - -class StatusCircle(QtWidgets.QWidget): - """ - Connection status - """ - def __init__(self, parent): - QtWidgets.QWidget.__init__(self, parent) - self.setGeometry(0, 0, 32, 32) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(0, 0, 32, 32)) - self.unread = False - - def update(self, status, unread_messages=None): - if unread_messages is None: - unread_messages = self.unread - else: - self.unread = unread_messages - if status == TOX_USER_STATUS['NONE']: - name = 'online' - elif status == TOX_USER_STATUS['AWAY']: - name = 'idle' - elif status == TOX_USER_STATUS['BUSY']: - name = 'busy' - else: - name = 'offline' - if unread_messages: - name += '_notification' - self.label.setGeometry(QtCore.QRect(0, 0, 32, 32)) - else: - self.label.setGeometry(QtCore.QRect(2, 0, 32, 32)) - pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(name)) - self.label.setPixmap(pixmap) - - -class UnreadMessagesCount(QtWidgets.QWidget): - - def __init__(self, parent=None): - super(UnreadMessagesCount, self).__init__(parent) - self.resize(30, 20) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(0, 0, 30, 20)) - self.label.setVisible(False) - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPointSize(12) - font.setBold(True) - self.label.setFont(font) - self.label.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignCenter) - color = settings.Settings.get_instance()['unread_color'] - self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') - - def update(self, messages_count): - color = settings.Settings.get_instance()['unread_color'] - self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }') - if messages_count: - self.label.setVisible(True) - self.label.setText(str(messages_count)) - else: - self.label.setVisible(False) - - -class FileTransferItem(QtWidgets.QListWidget): - - def __init__(self, file_name, size, time, user, friend_number, file_number, state, width, parent=None): - - QtWidgets.QListWidget.__init__(self, parent) - self.resize(QtCore.QSize(width, 34)) - if state == TOX_FILE_TRANSFER_STATE['CANCELLED']: - self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') - elif state in PAUSED_FILE_TRANSFERS: - self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') - else: - self.setStyleSheet('QListWidget { border: 1px solid green; }') - self.state = state - - self.name = DataLabel(self) - self.name.setGeometry(QtCore.QRect(3, 7, 95, 25)) - self.name.setTextFormat(QtCore.Qt.PlainText) - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPointSize(11) - font.setBold(True) - self.name.setFont(font) - self.name.setText(user) - - self.time = QtWidgets.QLabel(self) - self.time.setGeometry(QtCore.QRect(width - 60, 7, 50, 25)) - font.setPointSize(10) - font.setBold(False) - self.time.setFont(font) - self.time.setText(convert_time(time)) - - self.cancel = QtWidgets.QPushButton(self) - self.cancel.setGeometry(QtCore.QRect(width - 125, 2, 30, 30)) - pixmap = QtGui.QPixmap(curr_directory() + '/images/decline.png') - icon = QtGui.QIcon(pixmap) - self.cancel.setIcon(icon) - self.cancel.setIconSize(QtCore.QSize(30, 30)) - self.cancel.setVisible(state in ACTIVE_FILE_TRANSFERS) - self.cancel.clicked.connect(lambda: self.cancel_transfer(friend_number, file_number)) - self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}') - - self.accept_or_pause = QtWidgets.QPushButton(self) - self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30)) - if state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: - self.accept_or_pause.setVisible(True) - self.button_update('accept') - elif state in DO_NOT_SHOW_ACCEPT_BUTTON: - self.accept_or_pause.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # setup for continue - self.accept_or_pause.setVisible(True) - self.button_update('resume') - else: # pause - self.accept_or_pause.setVisible(True) - self.button_update('pause') - self.accept_or_pause.clicked.connect(lambda: self.accept_or_pause_transfer(friend_number, file_number, size)) - - self.accept_or_pause.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none}') - - self.pb = QtWidgets.QProgressBar(self) - self.pb.setGeometry(QtCore.QRect(100, 7, 100, 20)) - self.pb.setValue(0) - self.pb.setStyleSheet('QProgressBar { background-color: #302F2F; }') - self.pb.setVisible(state in SHOW_PROGRESS_BAR) - - self.file_name = DataLabel(self) - self.file_name.setGeometry(QtCore.QRect(210, 7, width - 420, 20)) - font.setPointSize(12) - self.file_name.setFont(font) - file_size = size // 1024 - if not file_size: - file_size = '{}B'.format(size) - elif file_size >= 1024: - file_size = '{}MB'.format(file_size // 1024) - else: - file_size = '{}KB'.format(file_size) - file_data = '{} {}'.format(file_size, file_name) - self.file_name.setText(file_data) - self.file_name.setToolTip(file_name) - self.saved_name = file_name - self.time_left = QtWidgets.QLabel(self) - self.time_left.setGeometry(QtCore.QRect(width - 92, 7, 30, 20)) - font.setPointSize(10) - self.time_left.setFont(font) - self.time_left.setVisible(state == TOX_FILE_TRANSFER_STATE['RUNNING']) - self.setFocusPolicy(QtCore.Qt.NoFocus) - self.paused = False - - def cancel_transfer(self, friend_number, file_number): - pr = profile.Profile.get_instance() - pr.cancel_transfer(friend_number, file_number) - self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') - self.cancel.setVisible(False) - self.accept_or_pause.setVisible(False) - self.pb.setVisible(False) - - def accept_or_pause_transfer(self, friend_number, file_number, size): - if self.state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", 'Choose folder'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - self.pb.setVisible(True) - if directory: - pr = profile.Profile.get_instance() - pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size) - self.button_update('pause') - elif self.state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # resume - self.paused = False - profile.Profile.get_instance().resume_transfer(friend_number, file_number) - self.button_update('pause') - self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] - else: # pause - self.paused = True - self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER'] - profile.Profile.get_instance().pause_transfer(friend_number, file_number) - self.button_update('resume') - self.accept_or_pause.clearFocus() - - def button_update(self, path): - pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(path)) - icon = QtGui.QIcon(pixmap) - self.accept_or_pause.setIcon(icon) - self.accept_or_pause.setIconSize(QtCore.QSize(30, 30)) - - def update_transfer_state(self, state, progress, time): - self.pb.setValue(int(progress * 100)) - if time + 1: - m, s = divmod(time, 60) - self.time_left.setText('{0:02d}:{1:02d}'.format(m, s)) - if self.state != state and self.state in ACTIVE_FILE_TRANSFERS: - if state == TOX_FILE_TRANSFER_STATE['CANCELLED']: - self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') - self.cancel.setVisible(False) - self.accept_or_pause.setVisible(False) - self.pb.setVisible(False) - self.state = state - self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['FINISHED']: - self.accept_or_pause.setVisible(False) - self.pb.setVisible(False) - self.cancel.setVisible(False) - self.setStyleSheet('QListWidget { border: 1px solid green; }') - self.state = state - self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']: - self.accept_or_pause.setVisible(False) - self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') - self.state = state - self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: - self.button_update('resume') # setup button continue - self.setStyleSheet('QListWidget { border: 1px solid green; }') - self.state = state - self.time_left.setVisible(False) - elif state == TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']: - self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }') - self.accept_or_pause.setVisible(False) - self.time_left.setVisible(False) - self.pb.setVisible(False) - elif not self.paused: # active - self.pb.setVisible(True) - self.accept_or_pause.setVisible(True) # setup to pause - self.button_update('pause') - self.setStyleSheet('QListWidget { border: 1px solid green; }') - self.state = state - self.time_left.setVisible(True) - - def mark_as_sent(self): - return False - - -class UnsentFileItem(FileTransferItem): - - def __init__(self, file_name, size, user, time, width, parent=None): - super(UnsentFileItem, self).__init__(file_name, size, time, user, -1, -1, - TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'], width, parent) - self._time = time - self.pb.setVisible(False) - movie = QtGui.QMovie(curr_directory() + '/images/spinner.gif') - self.time.setMovie(movie) - movie.start() - - def cancel_transfer(self, *args): - pr = profile.Profile.get_instance() - pr.cancel_not_started_transfer(self._time) - - -class InlineImageItem(QtWidgets.QScrollArea): - - def __init__(self, data, width, elem): - - QtWidgets.QScrollArea.__init__(self) - self.setFocusPolicy(QtCore.Qt.NoFocus) - self._elem = elem - self._image_label = QtWidgets.QLabel(self) - self._image_label.raise_() - self.setWidget(self._image_label) - self._image_label.setScaledContents(False) - self._pixmap = QtGui.QPixmap() - self._pixmap.loadFromData(data, 'PNG') - self._max_size = width - 30 - self._resize_needed = not (self._pixmap.width() <= self._max_size) - self._full_size = not self._resize_needed - if not self._resize_needed: - self._image_label.setPixmap(self._pixmap) - self.resize(QtCore.QSize(self._max_size + 5, self._pixmap.height() + 5)) - self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height()) - else: - pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio) - self._image_label.setPixmap(pixmap) - self.resize(QtCore.QSize(self._max_size + 5, pixmap.height())) - self._image_label.setGeometry(5, 0, self._max_size + 5, pixmap.height()) - self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.LeftButton and self._resize_needed: # scale inline - if self._full_size: - pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio) - self._image_label.setPixmap(pixmap) - self.resize(QtCore.QSize(self._max_size, pixmap.height())) - self._image_label.setGeometry(5, 0, pixmap.width(), pixmap.height()) - else: - self._image_label.setPixmap(self._pixmap) - self.resize(QtCore.QSize(self._max_size, self._pixmap.height() + 17)) - self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height()) - self._full_size = not self._full_size - self._elem.setSizeHint(QtCore.QSize(self.width(), self.height())) - elif event.button() == QtCore.Qt.RightButton: # save inline - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", - 'Choose folder'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - if directory: - fl = QtCore.QFile(directory + '/toxygen_inline_' + curr_time().replace(':', '_') + '.png') - self._pixmap.save(fl, 'PNG') - - def mark_as_sent(self): - return False diff --git a/toxygen/loginscreen.py b/toxygen/loginscreen.py deleted file mode 100644 index 77aa5ba..0000000 --- a/toxygen/loginscreen.py +++ /dev/null @@ -1,103 +0,0 @@ -from PyQt5 import QtWidgets, QtCore -from widgets import * - - -class NickEdit(LineEdit): - - def __init__(self, parent): - super(NickEdit, self).__init__(parent) - self.parent = parent - - def keyPressEvent(self, event): - if event.key() == QtCore.Qt.Key_Return: - self.parent.create_profile() - else: - super(NickEdit, self).keyPressEvent(event) - - -class LoginScreen(CenteredWidget): - - def __init__(self): - super(LoginScreen, self).__init__() - self.initUI() - self.center() - - def initUI(self): - self.resize(400, 200) - self.setMinimumSize(QtCore.QSize(400, 200)) - self.setMaximumSize(QtCore.QSize(400, 200)) - self.new_profile = QtWidgets.QPushButton(self) - self.new_profile.setGeometry(QtCore.QRect(20, 150, 171, 27)) - self.new_profile.clicked.connect(self.create_profile) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(20, 70, 101, 17)) - self.new_name = NickEdit(self) - self.new_name.setGeometry(QtCore.QRect(20, 100, 171, 31)) - self.load_profile = QtWidgets.QPushButton(self) - self.load_profile.setGeometry(QtCore.QRect(220, 150, 161, 27)) - self.load_profile.clicked.connect(self.load_ex_profile) - self.default = QtWidgets.QCheckBox(self) - self.default.setGeometry(QtCore.QRect(220, 110, 131, 22)) - self.groupBox = QtWidgets.QGroupBox(self) - self.groupBox.setGeometry(QtCore.QRect(210, 40, 181, 151)) - self.comboBox = QtWidgets.QComboBox(self.groupBox) - self.comboBox.setGeometry(QtCore.QRect(10, 30, 161, 27)) - self.groupBox_2 = QtWidgets.QGroupBox(self) - self.groupBox_2.setGeometry(QtCore.QRect(10, 40, 191, 151)) - self.toxygen = QtWidgets.QLabel(self) - self.groupBox.raise_() - self.groupBox_2.raise_() - self.comboBox.raise_() - self.default.raise_() - self.load_profile.raise_() - self.new_name.raise_() - self.new_profile.raise_() - self.toxygen.setGeometry(QtCore.QRect(160, 8, 90, 25)) - font = QtGui.QFont() - font.setFamily("Impact") - font.setPointSize(16) - self.toxygen.setFont(font) - self.toxygen.setObjectName("toxygen") - self.type = 0 - self.number = -1 - self.load_as_default = False - self.name = None - self.retranslateUi() - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.new_name.setPlaceholderText(QtWidgets.QApplication.translate("login", "Profile name")) - self.setWindowTitle(QtWidgets.QApplication.translate("login", "Log in")) - self.new_profile.setText(QtWidgets.QApplication.translate("login", "Create")) - self.label.setText(QtWidgets.QApplication.translate("login", "Profile name:")) - self.load_profile.setText(QtWidgets.QApplication.translate("login", "Load profile")) - self.default.setText(QtWidgets.QApplication.translate("login", "Use as default")) - self.groupBox.setTitle(QtWidgets.QApplication.translate("login", "Load existing profile")) - self.groupBox_2.setTitle(QtWidgets.QApplication.translate("login", "Create new profile")) - self.toxygen.setText(QtWidgets.QApplication.translate("login", "toxygen")) - - def create_profile(self): - self.type = 1 - self.name = self.new_name.text() - self.close() - - def load_ex_profile(self): - if not self.create_only: - self.type = 2 - self.number = self.comboBox.currentIndex() - self.load_as_default = self.default.isChecked() - self.close() - - def update_select(self, data): - list_of_profiles = [] - for elem in data: - list_of_profiles.append(elem) - self.comboBox.addItems(list_of_profiles) - self.create_only = not list_of_profiles - - def update_on_close(self, func): - self.onclose = func - - def closeEvent(self, event): - self.onclose(self.type, self.number, self.load_as_default, self.name) - event.accept() diff --git a/toxygen/mainscreen.py b/toxygen/mainscreen.py deleted file mode 100644 index 93ec72d..0000000 --- a/toxygen/mainscreen.py +++ /dev/null @@ -1,757 +0,0 @@ -from menu import * -from profile import * -from list_items import * -from widgets import MultilineEdit, ComboBox -import plugin_support -from mainscreen_widgets import * -import settings -import toxes - - -class MainWindow(QtWidgets.QMainWindow, Singleton): - - def __init__(self, tox, reset, tray): - super().__init__() - Singleton.__init__(self) - self.reset = reset - self.tray = tray - self.setAcceptDrops(True) - self.initUI(tox) - self._saved = False - if settings.Settings.get_instance()['show_welcome_screen']: - self.ws = WelcomeScreen() - - def setup_menu(self, window): - self.menubar = QtWidgets.QMenuBar(window) - self.menubar.setObjectName("menubar") - self.menubar.setNativeMenuBar(False) - self.menubar.setMinimumSize(self.width(), 25) - self.menubar.setMaximumSize(self.width(), 25) - self.menubar.setBaseSize(self.width(), 25) - self.menuProfile = QtWidgets.QMenu(self.menubar) - - self.menuProfile = QtWidgets.QMenu(self.menubar) - self.menuProfile.setObjectName("menuProfile") - self.menuSettings = QtWidgets.QMenu(self.menubar) - self.menuSettings.setObjectName("menuSettings") - self.menuPlugins = QtWidgets.QMenu(self.menubar) - self.menuPlugins.setObjectName("menuPlugins") - self.menuAbout = QtWidgets.QMenu(self.menubar) - self.menuAbout.setObjectName("menuAbout") - - self.actionAdd_friend = QtWidgets.QAction(window) - self.actionAdd_gc = QtWidgets.QAction(window) - self.actionAdd_friend.setObjectName("actionAdd_friend") - self.actionprofilesettings = QtWidgets.QAction(window) - self.actionprofilesettings.setObjectName("actionprofilesettings") - self.actionPrivacy_settings = QtWidgets.QAction(window) - self.actionPrivacy_settings.setObjectName("actionPrivacy_settings") - self.actionInterface_settings = QtWidgets.QAction(window) - self.actionInterface_settings.setObjectName("actionInterface_settings") - self.actionNotifications = QtWidgets.QAction(window) - self.actionNotifications.setObjectName("actionNotifications") - self.actionNetwork = QtWidgets.QAction(window) - self.actionNetwork.setObjectName("actionNetwork") - self.actionAbout_program = QtWidgets.QAction(window) - self.actionAbout_program.setObjectName("actionAbout_program") - self.updateSettings = QtWidgets.QAction(window) - self.actionSettings = QtWidgets.QAction(window) - self.actionSettings.setObjectName("actionSettings") - self.audioSettings = QtWidgets.QAction(window) - self.videoSettings = QtWidgets.QAction(window) - self.pluginData = QtWidgets.QAction(window) - self.importPlugin = QtWidgets.QAction(window) - self.reloadPlugins = QtWidgets.QAction(window) - self.lockApp = QtWidgets.QAction(window) - self.menuProfile.addAction(self.actionAdd_friend) - self.menuProfile.addAction(self.actionAdd_gc) - self.menuProfile.addAction(self.actionSettings) - self.menuProfile.addAction(self.lockApp) - self.menuSettings.addAction(self.actionPrivacy_settings) - self.menuSettings.addAction(self.actionInterface_settings) - self.menuSettings.addAction(self.actionNotifications) - self.menuSettings.addAction(self.actionNetwork) - self.menuSettings.addAction(self.audioSettings) - self.menuSettings.addAction(self.videoSettings) - self.menuSettings.addAction(self.updateSettings) - self.menuPlugins.addAction(self.pluginData) - self.menuPlugins.addAction(self.importPlugin) - self.menuPlugins.addAction(self.reloadPlugins) - self.menuAbout.addAction(self.actionAbout_program) - - self.menubar.addAction(self.menuProfile.menuAction()) - self.menubar.addAction(self.menuSettings.menuAction()) - self.menubar.addAction(self.menuPlugins.menuAction()) - self.menubar.addAction(self.menuAbout.menuAction()) - - self.actionAbout_program.triggered.connect(self.about_program) - self.actionNetwork.triggered.connect(self.network_settings) - self.actionAdd_friend.triggered.connect(self.add_contact) - self.actionAdd_gc.triggered.connect(self.create_gc) - self.actionSettings.triggered.connect(self.profile_settings) - self.actionPrivacy_settings.triggered.connect(self.privacy_settings) - self.actionInterface_settings.triggered.connect(self.interface_settings) - self.actionNotifications.triggered.connect(self.notification_settings) - self.audioSettings.triggered.connect(self.audio_settings) - self.videoSettings.triggered.connect(self.video_settings) - self.updateSettings.triggered.connect(self.update_settings) - self.pluginData.triggered.connect(self.plugins_menu) - self.lockApp.triggered.connect(self.lock_app) - self.importPlugin.triggered.connect(self.import_plugin) - self.reloadPlugins.triggered.connect(self.reload_plugins) - - def languageChange(self, *args, **kwargs): - self.retranslateUi() - - def event(self, event): - if event.type() == QtCore.QEvent.WindowActivate: - self.tray.setIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) - self.messages.repaint() - return super(MainWindow, self).event(event) - - def retranslateUi(self): - self.lockApp.setText(QtWidgets.QApplication.translate("MainWindow", "Lock")) - self.menuPlugins.setTitle(QtWidgets.QApplication.translate("MainWindow", "Plugins")) - self.pluginData.setText(QtWidgets.QApplication.translate("MainWindow", "List of plugins")) - self.menuProfile.setTitle(QtWidgets.QApplication.translate("MainWindow", "Profile")) - self.menuSettings.setTitle(QtWidgets.QApplication.translate("MainWindow", "Settings")) - self.menuAbout.setTitle(QtWidgets.QApplication.translate("MainWindow", "About")) - self.actionAdd_friend.setText(QtWidgets.QApplication.translate("MainWindow", "Add contact")) - self.actionAdd_gc.setText(QtWidgets.QApplication.translate("MainWindow", "Create group chat")) - self.actionprofilesettings.setText(QtWidgets.QApplication.translate("MainWindow", "Profile")) - self.actionPrivacy_settings.setText(QtWidgets.QApplication.translate("MainWindow", "Privacy")) - self.actionInterface_settings.setText(QtWidgets.QApplication.translate("MainWindow", "Interface")) - self.actionNotifications.setText(QtWidgets.QApplication.translate("MainWindow", "Notifications")) - self.actionNetwork.setText(QtWidgets.QApplication.translate("MainWindow", "Network")) - self.actionAbout_program.setText(QtWidgets.QApplication.translate("MainWindow", "About program")) - self.actionSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Settings")) - self.audioSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Audio")) - self.videoSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Video")) - self.updateSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Updates")) - self.contact_name.setPlaceholderText(QtWidgets.QApplication.translate("MainWindow", "Search")) - self.sendMessageButton.setToolTip(QtWidgets.QApplication.translate("MainWindow", "Send message")) - self.callButton.setToolTip(QtWidgets.QApplication.translate("MainWindow", "Start audio call with friend")) - self.online_contacts.clear() - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "All")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online first")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Name")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online and by name")) - self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online first and by name")) - ind = Settings.get_instance()['sorting'] - d = {0: 0, 1: 1, 2: 2, 3: 4, 1 | 4: 4, 2 | 4: 5} - self.online_contacts.setCurrentIndex(d[ind]) - self.importPlugin.setText(QtWidgets.QApplication.translate("MainWindow", "Import plugin")) - self.reloadPlugins.setText(QtWidgets.QApplication.translate("MainWindow", "Reload plugins")) - - def setup_right_bottom(self, Form): - Form.resize(650, 60) - self.messageEdit = MessageArea(Form, self) - self.messageEdit.setGeometry(QtCore.QRect(0, 3, 450, 55)) - self.messageEdit.setObjectName("messageEdit") - font = QtGui.QFont() - font.setPointSize(11) - font.setFamily(settings.Settings.get_instance()['font']) - self.messageEdit.setFont(font) - - self.sendMessageButton = QtWidgets.QPushButton(Form) - self.sendMessageButton.setGeometry(QtCore.QRect(565, 3, 60, 55)) - self.sendMessageButton.setObjectName("sendMessageButton") - - self.menuButton = MenuButton(Form, self.show_menu) - self.menuButton.setGeometry(QtCore.QRect(QtCore.QRect(455, 3, 55, 55))) - - pixmap = QtGui.QPixmap('send.png') - icon = QtGui.QIcon(pixmap) - self.sendMessageButton.setIcon(icon) - self.sendMessageButton.setIconSize(QtCore.QSize(45, 60)) - - pixmap = QtGui.QPixmap('menu.png') - icon = QtGui.QIcon(pixmap) - self.menuButton.setIcon(icon) - self.menuButton.setIconSize(QtCore.QSize(40, 40)) - - self.sendMessageButton.clicked.connect(self.send_message) - - QtCore.QMetaObject.connectSlotsByName(Form) - - def setup_left_center_menu(self, Form): - Form.resize(270, 25) - self.search_label = QtWidgets.QLabel(Form) - self.search_label.setGeometry(QtCore.QRect(3, 2, 20, 20)) - pixmap = QtGui.QPixmap() - pixmap.load(curr_directory() + '/images/search.png') - self.search_label.setScaledContents(False) - self.search_label.setPixmap(pixmap) - - self.contact_name = LineEdit(Form) - self.contact_name.setGeometry(QtCore.QRect(0, 0, 150, 25)) - self.contact_name.setObjectName("contact_name") - self.contact_name.textChanged.connect(self.filtering) - - self.online_contacts = ComboBox(Form) - self.online_contacts.setGeometry(QtCore.QRect(150, 0, 120, 25)) - self.online_contacts.activated[int].connect(lambda x: self.filtering()) - self.search_label.raise_() - - QtCore.QMetaObject.connectSlotsByName(Form) - - def setup_left_top(self, Form): - Form.setCursor(QtCore.Qt.PointingHandCursor) - Form.setMinimumSize(QtCore.QSize(270, 75)) - Form.setMaximumSize(QtCore.QSize(270, 75)) - Form.setBaseSize(QtCore.QSize(270, 75)) - self.avatar_label = Form.avatar_label = QtWidgets.QLabel(Form) - self.avatar_label.setGeometry(QtCore.QRect(5, 5, 64, 64)) - self.avatar_label.setScaledContents(False) - self.avatar_label.setAlignment(QtCore.Qt.AlignCenter) - self.name = Form.name = DataLabel(Form) - Form.name.setGeometry(QtCore.QRect(75, 15, 150, 25)) - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPointSize(14) - font.setBold(True) - Form.name.setFont(font) - Form.name.setObjectName("name") - self.status_message = Form.status_message = DataLabel(Form) - Form.status_message.setGeometry(QtCore.QRect(75, 35, 170, 25)) - font.setPointSize(12) - font.setBold(False) - Form.status_message.setFont(font) - Form.status_message.setObjectName("status_message") - self.connection_status = Form.connection_status = StatusCircle(Form) - Form.connection_status.setGeometry(QtCore.QRect(230, 10, 32, 32)) - self.avatar_label.mouseReleaseEvent = self.profile_settings - self.status_message.mouseReleaseEvent = self.profile_settings - self.name.mouseReleaseEvent = self.profile_settings - self.connection_status.raise_() - Form.connection_status.setObjectName("connection_status") - - def setup_right_top(self, Form): - Form.resize(650, 75) - self.account_avatar = QtWidgets.QLabel(Form) - self.account_avatar.setGeometry(QtCore.QRect(10, 5, 64, 64)) - self.account_avatar.setScaledContents(False) - self.account_name = DataLabel(Form) - self.account_name.setGeometry(QtCore.QRect(100, 0, 400, 25)) - self.account_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) - font = QtGui.QFont() - font.setFamily(settings.Settings.get_instance()['font']) - font.setPointSize(14) - font.setBold(True) - self.account_name.setFont(font) - self.account_name.setObjectName("account_name") - self.account_status = DataLabel(Form) - self.account_status.setGeometry(QtCore.QRect(100, 20, 400, 25)) - self.account_status.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) - font.setPointSize(12) - font.setBold(False) - self.account_status.setFont(font) - self.account_status.setObjectName("account_status") - self.callButton = QtWidgets.QPushButton(Form) - self.callButton.setGeometry(QtCore.QRect(550, 5, 50, 50)) - self.callButton.setObjectName("callButton") - self.callButton.clicked.connect(lambda: self.profile.call_click(True)) - self.videocallButton = QtWidgets.QPushButton(Form) - self.videocallButton.setGeometry(QtCore.QRect(550, 5, 50, 50)) - self.videocallButton.setObjectName("videocallButton") - self.videocallButton.clicked.connect(lambda: self.profile.call_click(True, True)) - self.update_call_state('call') - self.typing = QtWidgets.QLabel(Form) - self.typing.setGeometry(QtCore.QRect(500, 25, 50, 30)) - pixmap = QtGui.QPixmap(QtCore.QSize(50, 30)) - pixmap.load(curr_directory() + '/images/typing.png') - self.typing.setScaledContents(False) - self.typing.setPixmap(pixmap.scaled(50, 30, QtCore.Qt.KeepAspectRatio)) - self.typing.setVisible(False) - QtCore.QMetaObject.connectSlotsByName(Form) - - def setup_left_center(self, widget): - self.friends_list = QtWidgets.QListWidget(widget) - self.friends_list.setObjectName("friends_list") - self.friends_list.setGeometry(0, 0, 270, 310) - self.friends_list.clicked.connect(self.friend_click) - self.friends_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - self.friends_list.customContextMenuRequested.connect(self.friend_right_click) - self.friends_list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) - self.friends_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) - self.friends_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.friends_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) - - def setup_right_center(self, widget): - self.messages = QtWidgets.QListWidget(widget) - self.messages.setGeometry(0, 0, 620, 310) - self.messages.setObjectName("messages") - self.messages.setSpacing(1) - self.messages.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) - self.messages.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.messages.focusOutEvent = lambda event: self.messages.clearSelection() - self.messages.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) - - def load(pos): - if not pos: - self.profile.load_history() - self.messages.verticalScrollBar().setValue(1) - self.messages.verticalScrollBar().valueChanged.connect(load) - self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) - self.messages.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - - def initUI(self, tox): - self.setMinimumSize(920, 500) - s = Settings.get_instance() - self.setGeometry(s['x'], s['y'], s['width'], s['height']) - self.setWindowTitle('Toxygen') - os.chdir(curr_directory() + '/images/') - menu = QtWidgets.QWidget() - main = QtWidgets.QWidget() - grid = QtWidgets.QGridLayout() - search = QtWidgets.QWidget() - name = QtWidgets.QWidget() - info = QtWidgets.QWidget() - main_list = QtWidgets.QWidget() - messages = QtWidgets.QWidget() - message_buttons = QtWidgets.QWidget() - self.setup_left_center_menu(search) - self.setup_left_top(name) - self.setup_right_center(messages) - self.setup_right_top(info) - self.setup_right_bottom(message_buttons) - self.setup_left_center(main_list) - self.setup_menu(menu) - if not Settings.get_instance()['mirror_mode']: - grid.addWidget(search, 2, 0) - grid.addWidget(name, 1, 0) - grid.addWidget(messages, 2, 1, 2, 1) - grid.addWidget(info, 1, 1) - grid.addWidget(message_buttons, 4, 1) - grid.addWidget(main_list, 3, 0, 2, 1) - grid.setColumnMinimumWidth(1, 500) - grid.setColumnMinimumWidth(0, 270) - else: - grid.addWidget(search, 2, 1) - grid.addWidget(name, 1, 1) - grid.addWidget(messages, 2, 0, 2, 1) - grid.addWidget(info, 1, 0) - grid.addWidget(message_buttons, 4, 0) - grid.addWidget(main_list, 3, 1, 2, 1) - grid.setColumnMinimumWidth(0, 500) - grid.setColumnMinimumWidth(1, 270) - - grid.addWidget(menu, 0, 0, 1, 2) - grid.setSpacing(0) - grid.setContentsMargins(0, 0, 0, 0) - grid.setRowMinimumHeight(0, 25) - grid.setRowMinimumHeight(1, 75) - grid.setRowMinimumHeight(2, 25) - grid.setRowMinimumHeight(3, 320) - grid.setRowMinimumHeight(4, 55) - grid.setColumnStretch(1, 1) - grid.setRowStretch(3, 1) - main.setLayout(grid) - self.setCentralWidget(main) - self.messageEdit.setFocus() - self.user_info = name - self.friend_info = info - self.retranslateUi() - self.profile = Profile(tox, self) - - def closeEvent(self, event): - s = Settings.get_instance() - if not s['close_to_tray'] or s.closing: - if not self._saved: - self._saved = True - self.profile.save_history() - self.profile.close() - s['x'] = self.geometry().x() - s['y'] = self.geometry().y() - s['width'] = self.width() - s['height'] = self.height() - s.save() - QtWidgets.QApplication.closeAllWindows() - event.accept() - elif QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): - event.ignore() - self.hide() - - def close_window(self): - Settings.get_instance().closing = True - self.close() - - def resizeEvent(self, *args, **kwargs): - self.messages.setGeometry(0, 0, self.width() - 270, self.height() - 155) - self.friends_list.setGeometry(0, 0, 270, self.height() - 125) - - self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 10, 50, 50)) - self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 10, 50, 50)) - self.typing.setGeometry(QtCore.QRect(self.width() - 450, 20, 50, 30)) - - self.messageEdit.setGeometry(QtCore.QRect(55, 0, self.width() - 395, 55)) - self.menuButton.setGeometry(QtCore.QRect(0, 0, 55, 55)) - self.sendMessageButton.setGeometry(QtCore.QRect(self.width() - 340, 0, 70, 55)) - - self.account_name.setGeometry(QtCore.QRect(100, 15, self.width() - 560, 25)) - self.account_status.setGeometry(QtCore.QRect(100, 35, self.width() - 560, 25)) - self.messageEdit.setFocus() - self.profile.update() - - def keyPressEvent(self, event): - if event.key() == QtCore.Qt.Key_Escape and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): - self.hide() - elif event.key() == QtCore.Qt.Key_C and event.modifiers() & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): - rows = list(map(lambda x: self.messages.row(x), self.messages.selectedItems())) - indexes = (rows[0] - self.messages.count(), rows[-1] - self.messages.count()) - s = self.profile.export_history(self.profile.active_friend, True, indexes) - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(s) - elif event.key() == QtCore.Qt.Key_Z and event.modifiers() & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): - self.messages.clearSelection() - elif event.key() == QtCore.Qt.Key_F and event.modifiers() & QtCore.Qt.ControlModifier: - self.show_search_field() - else: - super(MainWindow, self).keyPressEvent(event) - - # ----------------------------------------------------------------------------------------------------------------- - # Functions which called when user click in menu - # ----------------------------------------------------------------------------------------------------------------- - - def about_program(self): - import util - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "About")) - text = (QtWidgets.QApplication.translate("MainWindow", 'Toxygen is Tox client written on Python.
Version: ')) - github = '
Github' - submit_a_bug = '
Submit a bug' - msgBox.setText(text + util.program_version + github + submit_a_bug) - msgBox.exec_() - - def network_settings(self): - self.n_s = NetworkSettings(self.reset) - self.n_s.show() - - def plugins_menu(self): - self.p_s = PluginsSettings() - self.p_s.show() - - def add_contact(self, link=''): - self.a_c = AddContact(link or '') - self.a_c.show() - - def create_gc(self): - self.profile.create_group_chat() - - def profile_settings(self, *args): - self.p_s = ProfileSettings() - self.p_s.show() - - def privacy_settings(self): - self.priv_s = PrivacySettings() - self.priv_s.show() - - def notification_settings(self): - self.notif_s = NotificationsSettings() - self.notif_s.show() - - def interface_settings(self): - self.int_s = InterfaceSettings() - self.int_s.show() - - def audio_settings(self): - self.audio_s = AudioSettings() - self.audio_s.show() - - def video_settings(self): - self.video_s = VideoSettings() - self.video_s.show() - - def update_settings(self): - self.update_s = UpdateSettings() - self.update_s.show() - - def reload_plugins(self): - plugin_loader = plugin_support.PluginLoader.get_instance() - if plugin_loader is not None: - plugin_loader.reload() - - def import_plugin(self): - import util - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", 'Choose folder with plugin'), - util.curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - if directory: - src = directory + '/' - dest = curr_directory() + '/plugins/' - util.copy(src, dest) - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("MainWindow", "Restart Toxygen")) - msgBox.setText( - QtWidgets.QApplication.translate("MainWindow", 'Plugin will be loaded after restart')) - msgBox.exec_() - - def lock_app(self): - if toxes.ToxES.get_instance().has_password(): - Settings.get_instance().locked = True - self.hide() - else: - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("MainWindow", "Cannot lock app")) - msgBox.setText( - QtWidgets.QApplication.translate("MainWindow", 'Error. Profile password is not set.')) - msgBox.exec_() - - def show_menu(self): - if not hasattr(self, 'menu'): - self.menu = DropdownMenu(self) - self.menu.setGeometry(QtCore.QRect(0 if Settings.get_instance()['mirror_mode'] else 270, - self.height() - 120, - 180, - 120)) - self.menu.show() - - # ----------------------------------------------------------------------------------------------------------------- - # Messages, calls and file transfers - # ----------------------------------------------------------------------------------------------------------------- - - def send_message(self): - text = self.messageEdit.toPlainText() - self.profile.send_message(text) - - def send_file(self): - self.menu.hide() - if self.profile.active_friend + 1and self.profile.is_active_a_friend(): - choose = QtWidgets.QApplication.translate("MainWindow", 'Choose file') - name = QtWidgets.QFileDialog.getOpenFileName(self, choose, options=QtWidgets.QFileDialog.DontUseNativeDialog) - if name[0]: - self.profile.send_file(name[0]) - - def send_screenshot(self, hide=False): - self.menu.hide() - if self.profile.active_friend + 1 and self.profile.is_active_a_friend(): - self.sw = ScreenShotWindow(self) - self.sw.show() - if hide: - self.hide() - - def send_smiley(self): - self.menu.hide() - if self.profile.active_friend + 1: - self.smiley = SmileyWindow(self) - self.smiley.setGeometry(QtCore.QRect(self.x() if Settings.get_instance()['mirror_mode'] else 270 + self.x(), - self.y() + self.height() - 200, - self.smiley.width(), - self.smiley.height())) - self.smiley.show() - - def send_sticker(self): - self.menu.hide() - if self.profile.active_friend + 1 and self.profile.is_active_a_friend(): - self.sticker = StickerWindow(self) - self.sticker.setGeometry(QtCore.QRect(self.x() if Settings.get_instance()['mirror_mode'] else 270 + self.x(), - self.y() + self.height() - 200, - self.sticker.width(), - self.sticker.height())) - self.sticker.show() - - def active_call(self): - self.update_call_state('finish_call') - - def incoming_call(self): - self.update_call_state('incoming_call') - - def call_finished(self): - self.update_call_state('call') - - def update_call_state(self, state): - os.chdir(curr_directory() + '/images/') - - pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(state)) - icon = QtGui.QIcon(pixmap) - self.callButton.setIcon(icon) - self.callButton.setIconSize(QtCore.QSize(50, 50)) - - pixmap = QtGui.QPixmap(curr_directory() + '/images/{}_video.png'.format(state)) - icon = QtGui.QIcon(pixmap) - self.videocallButton.setIcon(icon) - self.videocallButton.setIconSize(QtCore.QSize(35, 35)) - - # ----------------------------------------------------------------------------------------------------------------- - # Functions which called when user open context menu in friends list - # ----------------------------------------------------------------------------------------------------------------- - - def friend_right_click(self, pos): - item = self.friends_list.itemAt(pos) - num = self.friends_list.indexFromItem(item).row() - friend = Profile.get_instance().get_friend(num) - if friend is None: - return - settings = Settings.get_instance() - allowed = friend.tox_id in settings['auto_accept_from_friends'] - auto = QtWidgets.QApplication.translate("MainWindow", 'Disallow auto accept') if allowed else QtWidgets.QApplication.translate("MainWindow", 'Allow auto accept') - if item is not None: - self.listMenu = QtWidgets.QMenu() - is_friend = type(friend) is Friend - if is_friend: - set_alias_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Set alias')) - set_alias_item.triggered.connect(lambda: self.set_alias(num)) - - history_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Chat history')) - clear_history_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Clear history')) - export_to_text_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Export as text')) - export_to_html_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Export as HTML')) - - copy_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Copy')) - copy_name_item = copy_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Name')) - copy_status_item = copy_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Status message')) - if is_friend: - copy_key_item = copy_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Public key')) - - auto_accept_item = self.listMenu.addAction(auto) - remove_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Remove friend')) - block_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Block friend')) - notes_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Notes')) - - chats = self.profile.get_group_chats() - if len(chats) and self.profile.is_active_online(): - invite_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Invite to group chat')) - for i in range(len(chats)): - name, number = chats[i] - item = invite_menu.addAction(name) - item.triggered.connect(lambda number=number: self.invite_friend_to_gc(num, number)) - - plugins_loader = plugin_support.PluginLoader.get_instance() - if plugins_loader is not None: - submenu = plugins_loader.get_menu(self.listMenu, num) - if len(submenu): - plug = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Plugins')) - plug.addActions(submenu) - copy_key_item.triggered.connect(lambda: self.copy_friend_key(num)) - remove_item.triggered.connect(lambda: self.remove_friend(num)) - block_item.triggered.connect(lambda: self.block_friend(num)) - auto_accept_item.triggered.connect(lambda: self.auto_accept(num, not allowed)) - notes_item.triggered.connect(lambda: self.show_note(friend)) - else: - leave_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Leave chat')) - set_title_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Set title')) - leave_item.triggered.connect(lambda: self.leave_gc(num)) - set_title_item.triggered.connect(lambda: self.set_title(num)) - clear_history_item.triggered.connect(lambda: self.clear_history(num)) - copy_name_item.triggered.connect(lambda: self.copy_name(friend)) - copy_status_item.triggered.connect(lambda: self.copy_status(friend)) - export_to_text_item.triggered.connect(lambda: self.export_history(num)) - export_to_html_item.triggered.connect(lambda: self.export_history(num, False)) - parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) - self.listMenu.move(parent_position + pos) - self.listMenu.show() - - def show_note(self, friend): - s = Settings.get_instance() - note = s['notes'][friend.tox_id] if friend.tox_id in s['notes'] else '' - user = QtWidgets.QApplication.translate("MainWindow", 'Notes about user') - user = '{} {}'.format(user, friend.name) - - def save_note(text): - if friend.tox_id in s['notes']: - del s['notes'][friend.tox_id] - if text: - s['notes'][friend.tox_id] = text - s.save() - self.note = MultilineEdit(user, note, save_note) - self.note.show() - - def export_history(self, num, as_text=True): - s = self.profile.export_history(num, as_text) - extension = 'txt' if as_text else 'html' - file_name, _ = QtWidgets.QFileDialog.getSaveFileName(None, - QtWidgets.QApplication.translate("MainWindow", - 'Choose file name'), - curr_directory(), - filter=extension, - options=QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - - if file_name: - if not file_name.endswith('.' + extension): - file_name += '.' + extension - with open(file_name, 'wt') as fl: - fl.write(s) - - def set_alias(self, num): - self.profile.set_alias(num) - - def remove_friend(self, num): - self.profile.delete_friend(num) - - def block_friend(self, num): - friend = self.profile.get_friend(num) - self.profile.block_user(friend.tox_id) - - def copy_friend_key(self, num): - tox_id = self.profile.friend_public_key(num) - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(tox_id) - - def copy_name(self, friend): - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(friend.name) - - def copy_status(self, friend): - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(friend.status_message) - - def clear_history(self, num): - self.profile.clear_history(num) - - def leave_gc(self, num): - self.profile.leave_gc(num) - - def set_title(self, num): - self.profile.set_title(num) - - def auto_accept(self, num, value): - settings = Settings.get_instance() - tox_id = self.profile.friend_public_key(num) - if value: - settings['auto_accept_from_friends'].append(tox_id) - else: - settings['auto_accept_from_friends'].remove(tox_id) - settings.save() - - def invite_friend_to_gc(self, friend_number, group_number): - self.profile.invite_friend(friend_number, group_number) - - # ----------------------------------------------------------------------------------------------------------------- - # Functions which called when user click somewhere else - # ----------------------------------------------------------------------------------------------------------------- - - def friend_click(self, index): - num = index.row() - self.profile.set_active(num) - - def mouseReleaseEvent(self, event): - pos = self.connection_status.pos() - x, y = pos.x() + self.user_info.pos().x(), pos.y() + self.user_info.pos().y() - if (x < event.x() < x + 32) and (y < event.y() < y + 32): - self.profile.change_status() - else: - super(MainWindow, self).mouseReleaseEvent(event) - - def show(self): - super().show() - self.profile.update() - - def filtering(self): - ind = self.online_contacts.currentIndex() - d = {0: 0, 1: 1, 2: 2, 3: 4, 4: 1 | 4, 5: 2 | 4} - self.profile.filtration_and_sorting(d[ind], self.contact_name.text()) - - def show_search_field(self): - if hasattr(self, 'search_field') and self.search_field.isVisible(): - return - if self.profile.get_curr_friend() is None: - return - self.search_field = SearchScreen(self.messages, self.messages.width(), self.messages.parent()) - x, y = self.messages.x(), self.messages.y() + self.messages.height() - 40 - self.search_field.setGeometry(x, y, self.messages.width(), 40) - self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40) - self.search_field.show() diff --git a/toxygen/mainscreen_widgets.py b/toxygen/mainscreen_widgets.py deleted file mode 100644 index dcbc075..0000000 --- a/toxygen/mainscreen_widgets.py +++ /dev/null @@ -1,492 +0,0 @@ -from PyQt5 import QtCore, QtGui, QtWidgets -from widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit -from profile import Profile -import smileys -import util -import platform - - -class MessageArea(QtWidgets.QPlainTextEdit): - """User types messages here""" - - def __init__(self, parent, form): - super(MessageArea, self).__init__(parent) - self.parent = form - self.setAcceptDrops(True) - self.timer = QtCore.QTimer(self) - self.timer.timeout.connect(lambda: self.parent.profile.send_typing(False)) - - def keyPressEvent(self, event): - if event.matches(QtGui.QKeySequence.Paste): - mimeData = QtWidgets.QApplication.clipboard().mimeData() - if mimeData.hasUrls(): - for url in mimeData.urls(): - self.pasteEvent(url.toString()) - else: - self.pasteEvent() - elif event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter): - modifiers = event.modifiers() - if modifiers & QtCore.Qt.ControlModifier or modifiers & QtCore.Qt.ShiftModifier: - self.insertPlainText('\n') - else: - if self.timer.isActive(): - self.timer.stop() - self.parent.profile.send_typing(False) - self.parent.send_message() - elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText(): - self.appendPlainText(Profile.get_instance().get_last_message()) - elif event.key() == QtCore.Qt.Key_Tab and not self.parent.profile.is_active_a_friend(): - text = self.toPlainText() - pos = self.textCursor().position() - self.insertPlainText(Profile.get_instance().get_gc_peer_name(text[:pos])) - else: - self.parent.profile.send_typing(True) - if self.timer.isActive(): - self.timer.stop() - self.timer.start(5000) - super(MessageArea, self).keyPressEvent(event) - - def contextMenuEvent(self, event): - menu = create_menu(self.createStandardContextMenu()) - menu.exec_(event.globalPos()) - del menu - - def dragEnterEvent(self, e): - e.accept() - - def dragMoveEvent(self, e): - e.accept() - - def dropEvent(self, e): - if e.mimeData().hasFormat('text/plain') or e.mimeData().hasFormat('text/html'): - e.accept() - self.pasteEvent(e.mimeData().text()) - elif e.mimeData().hasUrls(): - for url in e.mimeData().urls(): - self.pasteEvent(url.toString()) - e.accept() - else: - e.ignore() - - def pasteEvent(self, text=None): - text = text or QtWidgets.QApplication.clipboard().text() - if text.startswith('file://'): - file_name = self.parse_file_name(text) - self.parent.profile.send_file(file_name) - elif text: - self.insertPlainText(text) - else: - image = QtWidgets.QApplication.clipboard().image() - if image is not None: - byte_array = QtCore.QByteArray() - buffer = QtCore.QBuffer(byte_array) - buffer.open(QtCore.QIODevice.WriteOnly) - image.save(buffer, 'PNG') - self.parent.profile.send_screenshot(bytes(byte_array.data())) - - def parse_file_name(self, file_name): - import urllib - if file_name.endswith('\r\n'): - file_name = file_name[:-2] - file_name = urllib.parse.unquote(file_name) - return file_name[8 if platform.system() == 'Windows' else 7:] - - -class ScreenShotWindow(RubberBandWindow): - - def closeEvent(self, *args): - if self.parent.isHidden(): - self.parent.show() - - def mouseReleaseEvent(self, event): - if self.rubberband.isVisible(): - self.rubberband.hide() - rect = self.rubberband.geometry() - if rect.width() and rect.height(): - screen = QtWidgets.QApplication.primaryScreen() - p = screen.grabWindow(0, - rect.x() + 4, - rect.y() + 4, - rect.width() - 8, - rect.height() - 8) - byte_array = QtCore.QByteArray() - buffer = QtCore.QBuffer(byte_array) - buffer.open(QtCore.QIODevice.WriteOnly) - p.save(buffer, 'PNG') - Profile.get_instance().send_screenshot(bytes(byte_array.data())) - self.close() - - -class SmileyWindow(QtWidgets.QWidget): - """ - Smiley selection window - """ - - def __init__(self, parent): - super(SmileyWindow, self).__init__() - self.setWindowFlags(QtCore.Qt.FramelessWindowHint) - inst = smileys.SmileyLoader.get_instance() - self.data = inst.get_smileys() - count = len(self.data) - if not count: - self.close() - self.page_size = int(pow(count / 8, 0.5) + 1) * 8 # smileys per page - if count % self.page_size == 0: - self.page_count = count // self.page_size - else: - self.page_count = round(count / self.page_size + 0.5) - self.page = -1 - self.radio = [] - self.parent = parent - for i in range(self.page_count): # buttons with smileys - elem = QtWidgets.QRadioButton(self) - elem.setGeometry(QtCore.QRect(i * 20 + 5, 180, 20, 20)) - elem.clicked.connect(lambda c, t=i: self.checked(t)) - self.radio.append(elem) - width = max(self.page_count * 20 + 30, (self.page_size + 5) * 8 // 10) - self.setMaximumSize(width, 200) - self.setMinimumSize(width, 200) - self.buttons = [] - for i in range(self.page_size): # pages - radio buttons - b = QtWidgets.QPushButton(self) - b.setGeometry(QtCore.QRect((i // 8) * 20 + 5, (i % 8) * 20, 20, 20)) - b.clicked.connect(lambda c, t=i: self.clicked(t)) - self.buttons.append(b) - self.checked(0) - - def checked(self, pos): # new page opened - self.radio[self.page].setChecked(False) - self.radio[pos].setChecked(True) - self.page = pos - start = self.page * self.page_size - for i in range(self.page_size): - try: - self.buttons[i].setVisible(True) - pixmap = QtGui.QPixmap(self.data[start + i][1]) - icon = QtGui.QIcon(pixmap) - self.buttons[i].setIcon(icon) - except: - self.buttons[i].setVisible(False) - - def clicked(self, pos): # smiley selected - pos += self.page * self.page_size - smiley = self.data[pos][0] - self.parent.messageEdit.insertPlainText(smiley) - self.close() - - def leaveEvent(self, event): - self.close() - - -class MenuButton(QtWidgets.QPushButton): - - def __init__(self, parent, enter): - super(MenuButton, self).__init__(parent) - self.enter = enter - - def enterEvent(self, event): - self.enter() - super(MenuButton, self).enterEvent(event) - - -class DropdownMenu(QtWidgets.QWidget): - - def __init__(self, parent): - super(DropdownMenu, self).__init__(parent) - self.installEventFilter(self) - self.setWindowFlags(QtCore.Qt.FramelessWindowHint) - self.setMaximumSize(120, 120) - self.setMinimumSize(120, 120) - self.screenshotButton = QRightClickButton(self) - self.screenshotButton.setGeometry(QtCore.QRect(0, 60, 60, 60)) - self.screenshotButton.setObjectName("screenshotButton") - - self.fileTransferButton = QtWidgets.QPushButton(self) - self.fileTransferButton.setGeometry(QtCore.QRect(60, 60, 60, 60)) - self.fileTransferButton.setObjectName("fileTransferButton") - - self.smileyButton = QtWidgets.QPushButton(self) - self.smileyButton.setGeometry(QtCore.QRect(0, 0, 60, 60)) - - self.stickerButton = QtWidgets.QPushButton(self) - self.stickerButton.setGeometry(QtCore.QRect(60, 0, 60, 60)) - - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/file.png') - icon = QtGui.QIcon(pixmap) - self.fileTransferButton.setIcon(icon) - self.fileTransferButton.setIconSize(QtCore.QSize(50, 50)) - - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/screenshot.png') - icon = QtGui.QIcon(pixmap) - self.screenshotButton.setIcon(icon) - self.screenshotButton.setIconSize(QtCore.QSize(50, 60)) - - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/smiley.png') - icon = QtGui.QIcon(pixmap) - self.smileyButton.setIcon(icon) - self.smileyButton.setIconSize(QtCore.QSize(50, 50)) - - pixmap = QtGui.QPixmap(util.curr_directory() + '/images/sticker.png') - icon = QtGui.QIcon(pixmap) - self.stickerButton.setIcon(icon) - self.stickerButton.setIconSize(QtCore.QSize(55, 55)) - - self.screenshotButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send screenshot")) - self.fileTransferButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send file")) - self.smileyButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Add smiley")) - self.stickerButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send sticker")) - - self.fileTransferButton.clicked.connect(parent.send_file) - self.screenshotButton.clicked.connect(parent.send_screenshot) - self.screenshotButton.rightClicked.connect(lambda: parent.send_screenshot(True)) - self.smileyButton.clicked.connect(parent.send_smiley) - self.stickerButton.clicked.connect(parent.send_sticker) - - def leaveEvent(self, event): - self.close() - - def eventFilter(self, obj, event): - if event.type() == QtCore.QEvent.WindowDeactivate: - self.close() - return False - - -class StickerItem(QtWidgets.QWidget): - - def __init__(self, fl): - super(StickerItem, self).__init__() - self._image_label = QtWidgets.QLabel(self) - self.path = fl - self.pixmap = QtGui.QPixmap() - self.pixmap.load(fl) - if self.pixmap.width() > 150: - self.pixmap = self.pixmap.scaled(150, 200, QtCore.Qt.KeepAspectRatio) - self.setFixedSize(150, self.pixmap.height()) - self._image_label.setPixmap(self.pixmap) - - -class StickerWindow(QtWidgets.QWidget): - """Sticker selection window""" - - def __init__(self, parent): - super(StickerWindow, self).__init__() - self.setWindowFlags(QtCore.Qt.FramelessWindowHint) - self.setMaximumSize(250, 200) - self.setMinimumSize(250, 200) - self.list = QtWidgets.QListWidget(self) - self.list.setGeometry(QtCore.QRect(0, 0, 250, 200)) - self.arr = smileys.sticker_loader() - for sticker in self.arr: - item = StickerItem(sticker) - elem = QtWidgets.QListWidgetItem() - elem.setSizeHint(QtCore.QSize(250, item.height())) - self.list.addItem(elem) - self.list.setItemWidget(elem, item) - self.list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) - self.list.setSpacing(3) - self.list.clicked.connect(self.click) - self.parent = parent - - def click(self, index): - num = index.row() - self.parent.profile.send_sticker(self.arr[num]) - self.close() - - def leaveEvent(self, event): - self.close() - - -class WelcomeScreen(CenteredWidget): - - def __init__(self): - super().__init__() - self.setMaximumSize(250, 200) - self.setMinimumSize(250, 200) - self.center() - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - self.text = QtWidgets.QTextBrowser(self) - self.text.setGeometry(QtCore.QRect(0, 0, 250, 170)) - self.text.setOpenExternalLinks(True) - self.checkbox = QtWidgets.QCheckBox(self) - self.checkbox.setGeometry(QtCore.QRect(5, 170, 240, 30)) - self.checkbox.setText(QtWidgets.QApplication.translate('WelcomeScreen', "Don't show again")) - self.setWindowTitle(QtWidgets.QApplication.translate('WelcomeScreen', 'Tip of the day')) - import random - num = random.randint(0, 10) - if num == 0: - text = QtWidgets.QApplication.translate('WelcomeScreen', 'Press Esc if you want hide app to tray.') - elif num == 1: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Right click on screenshot button hides app to tray during screenshot.') - elif num == 2: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'You can use Tox over Tor. For more info read this post') - elif num == 3: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Use Settings -> Interface to customize interface.') - elif num == 4: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Set profile password via Profile -> Settings. Password allows Toxygen encrypt your history and settings.') - elif num == 5: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Since v0.1.3 Toxygen supports plugins. Read more') - elif num == 6: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.') - elif num == 7: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'New in Toxygen 0.4.1:
Downloading nodes from tox.chat
Bug fixes') - elif num == 8: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Delete single message in chat: make right click on spinner or message time and choose "Delete" in menu') - elif num == 9: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Use right click on inline image to save it') - else: - text = QtWidgets.QApplication.translate('WelcomeScreen', - 'Set new NoSpam to avoid spam friend requests: Profile -> Settings -> Set new NoSpam.') - self.text.setHtml(text) - self.checkbox.stateChanged.connect(self.not_show) - QtCore.QTimer.singleShot(1000, self.show) - - def not_show(self): - import settings - s = settings.Settings.get_instance() - s['show_welcome_screen'] = False - s.save() - - -class MainMenuButton(QtWidgets.QPushButton): - - def __init__(self, *args): - super().__init__(*args) - self.setObjectName("mainmenubutton") - - def setText(self, text): - metrics = QtGui.QFontMetrics(self.font()) - self.setFixedWidth(metrics.size(QtCore.Qt.TextSingleLine, text).width() + 20) - super().setText(text) - - -class ClickableLabel(QtWidgets.QLabel): - - clicked = QtCore.pyqtSignal() - - def __init__(self, *args): - super().__init__(*args) - - def mouseReleaseEvent(self, ev): - self.clicked.emit() - - -class SearchScreen(QtWidgets.QWidget): - - def __init__(self, messages, width, *args): - super().__init__(*args) - self.setMaximumSize(width, 40) - self.setMinimumSize(width, 40) - self._messages = messages - - self.search_text = LineEdit(self) - self.search_text.setGeometry(0, 0, width - 160, 40) - - self.search_button = ClickableLabel(self) - self.search_button.setGeometry(width - 160, 0, 40, 40) - pixmap = QtGui.QPixmap() - pixmap.load(util.curr_directory() + '/images/search.png') - self.search_button.setScaledContents(False) - self.search_button.setAlignment(QtCore.Qt.AlignCenter) - self.search_button.setPixmap(pixmap) - self.search_button.clicked.connect(self.search) - - font = QtGui.QFont() - font.setPointSize(32) - font.setBold(True) - - self.prev_button = QtWidgets.QPushButton(self) - self.prev_button.setGeometry(width - 120, 0, 40, 40) - self.prev_button.clicked.connect(self.prev) - self.prev_button.setText('\u25B2') - - self.next_button = QtWidgets.QPushButton(self) - self.next_button.setGeometry(width - 80, 0, 40, 40) - self.next_button.clicked.connect(self.next) - self.next_button.setText('\u25BC') - - self.close_button = QtWidgets.QPushButton(self) - self.close_button.setGeometry(width - 40, 0, 40, 40) - self.close_button.clicked.connect(self.close) - self.close_button.setText('×') - self.close_button.setFont(font) - - font.setPointSize(18) - self.next_button.setFont(font) - self.prev_button.setFont(font) - - self.retranslateUi() - - def retranslateUi(self): - self.search_text.setPlaceholderText(QtWidgets.QApplication.translate("MainWindow", "Search")) - - def show(self): - super().show() - self.search_text.setFocus() - - def search(self): - Profile.get_instance().update() - text = self.search_text.text() - friend = Profile.get_instance().get_curr_friend() - if text and friend and util.is_re_valid(text): - index = friend.search_string(text) - self.load_messages(index) - - def prev(self): - friend = Profile.get_instance().get_curr_friend() - if friend is not None: - index = friend.search_prev() - self.load_messages(index) - - def next(self): - friend = Profile.get_instance().get_curr_friend() - text = self.search_text.text() - if friend is not None: - index = friend.search_next() - if index is not None: - count = self._messages.count() - index += count - item = self._messages.item(index) - self._messages.scrollToItem(item) - self._messages.itemWidget(item).select_text(text) - else: - self.not_found(text) - - def load_messages(self, index): - text = self.search_text.text() - if index is not None: - profile = Profile.get_instance() - count = self._messages.count() - while count + index < 0: - profile.load_history() - count = self._messages.count() - index += count - item = self._messages.item(index) - self._messages.scrollToItem(item) - self._messages.itemWidget(item).select_text(text) - else: - self.not_found(text) - - def closeEvent(self, *args): - Profile.get_instance().update() - self._messages.setGeometry(0, 0, self._messages.width(), self._messages.height() + 40) - super().closeEvent(*args) - - @staticmethod - def not_found(text): - mbox = QtWidgets.QMessageBox() - mbox_text = QtWidgets.QApplication.translate("MainWindow", - 'Text "{}" was not found') - - mbox.setText(mbox_text.format(text)) - mbox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", - 'Not found')) - mbox.exec_() diff --git a/toxygen/menu.py b/toxygen/menu.py deleted file mode 100644 index 17f4e17..0000000 --- a/toxygen/menu.py +++ /dev/null @@ -1,1095 +0,0 @@ -from PyQt5 import QtCore, QtGui, QtWidgets -from settings import * -from profile import Profile -from util import curr_directory, copy -from widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow -import pyaudio -import toxes -import plugin_support -import updater - - -class AddContact(CenteredWidget): - """Add contact form""" - - def __init__(self, tox_id=''): - super(AddContact, self).__init__() - self.initUI(tox_id) - self._adding = False - - def initUI(self, tox_id): - self.setObjectName('AddContact') - self.resize(568, 306) - self.sendRequestButton = QtWidgets.QPushButton(self) - self.sendRequestButton.setGeometry(QtCore.QRect(50, 270, 471, 31)) - self.sendRequestButton.setMinimumSize(QtCore.QSize(0, 0)) - self.sendRequestButton.setBaseSize(QtCore.QSize(0, 0)) - self.sendRequestButton.setObjectName("sendRequestButton") - self.sendRequestButton.clicked.connect(self.add_friend) - self.tox_id = LineEdit(self) - self.tox_id.setGeometry(QtCore.QRect(50, 40, 471, 27)) - self.tox_id.setObjectName("lineEdit") - self.tox_id.setText(tox_id) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(50, 10, 80, 20)) - self.error_label = DataLabel(self) - self.error_label.setGeometry(QtCore.QRect(120, 10, 420, 20)) - font = QtGui.QFont() - font.setFamily(Settings.get_instance()['font']) - font.setPointSize(10) - font.setWeight(30) - self.error_label.setFont(font) - self.error_label.setStyleSheet("QLabel { color: #BC1C1C; }") - self.label.setObjectName("label") - self.message_edit = QtWidgets.QTextEdit(self) - self.message_edit.setGeometry(QtCore.QRect(50, 110, 471, 151)) - self.message_edit.setObjectName("textEdit") - self.message = QtWidgets.QLabel(self) - self.message.setGeometry(QtCore.QRect(50, 70, 101, 31)) - self.message.setFont(font) - self.message.setObjectName("label_2") - self.retranslateUi() - self.message_edit.setText('Hello! Add me to your contact list please') - font.setPointSize(12) - font.setBold(True) - self.label.setFont(font) - self.message.setFont(font) - QtCore.QMetaObject.connectSlotsByName(self) - - def add_friend(self): - if self._adding: - return - self._adding = True - profile = Profile.get_instance() - send = profile.send_friend_request(self.tox_id.text().strip(), self.message_edit.toPlainText()) - self._adding = False - if send is True: - # request was successful - self.close() - else: # print error data - self.error_label.setText(send) - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate('AddContact', "Add contact")) - self.sendRequestButton.setText(QtWidgets.QApplication.translate("Form", "Send request")) - self.label.setText(QtWidgets.QApplication.translate('AddContact', "TOX ID:")) - self.message.setText(QtWidgets.QApplication.translate('AddContact', "Message:")) - self.tox_id.setPlaceholderText(QtWidgets.QApplication.translate('AddContact', "TOX ID or public key of contact")) - - -class ProfileSettings(CenteredWidget): - """Form with profile settings such as name, status, TOX ID""" - def __init__(self): - super(ProfileSettings, self).__init__() - self.initUI() - self.center() - - def initUI(self): - self.setObjectName("ProfileSettingsForm") - self.setMinimumSize(QtCore.QSize(700, 600)) - self.setMaximumSize(QtCore.QSize(700, 600)) - self.nick = LineEdit(self) - self.nick.setGeometry(QtCore.QRect(30, 60, 350, 27)) - profile = Profile.get_instance() - self.nick.setText(profile.name) - self.status = QtWidgets.QComboBox(self) - self.status.setGeometry(QtCore.QRect(400, 60, 200, 27)) - self.status_message = LineEdit(self) - self.status_message.setGeometry(QtCore.QRect(30, 130, 350, 27)) - self.status_message.setText(profile.status_message) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(40, 30, 91, 25)) - font = QtGui.QFont() - font.setFamily(Settings.get_instance()['font']) - font.setPointSize(18) - font.setWeight(75) - font.setBold(True) - self.label.setFont(font) - self.label_2 = QtWidgets.QLabel(self) - self.label_2.setGeometry(QtCore.QRect(40, 100, 100, 25)) - self.label_2.setFont(font) - self.label_3 = QtWidgets.QLabel(self) - self.label_3.setGeometry(QtCore.QRect(40, 180, 100, 25)) - self.label_3.setFont(font) - self.tox_id = QtWidgets.QLabel(self) - self.tox_id.setGeometry(QtCore.QRect(15, 210, 685, 21)) - font.setPointSize(10) - self.tox_id.setFont(font) - s = profile.tox_id - self.tox_id.setText(s) - self.copyId = QtWidgets.QPushButton(self) - self.copyId.setGeometry(QtCore.QRect(40, 250, 180, 30)) - self.copyId.clicked.connect(self.copy) - self.export = QtWidgets.QPushButton(self) - self.export.setGeometry(QtCore.QRect(230, 250, 180, 30)) - self.export.clicked.connect(self.export_profile) - self.new_nospam = QtWidgets.QPushButton(self) - self.new_nospam.setGeometry(QtCore.QRect(420, 250, 180, 30)) - self.new_nospam.clicked.connect(self.new_no_spam) - self.copy_pk = QtWidgets.QPushButton(self) - self.copy_pk.setGeometry(QtCore.QRect(40, 300, 180, 30)) - self.copy_pk.clicked.connect(self.copy_public_key) - self.new_avatar = QtWidgets.QPushButton(self) - self.new_avatar.setGeometry(QtCore.QRect(230, 300, 180, 30)) - self.delete_avatar = QtWidgets.QPushButton(self) - self.delete_avatar.setGeometry(QtCore.QRect(420, 300, 180, 30)) - self.delete_avatar.clicked.connect(self.reset_avatar) - self.new_avatar.clicked.connect(self.set_avatar) - self.profilepass = QtWidgets.QLabel(self) - self.profilepass.setGeometry(QtCore.QRect(40, 340, 300, 30)) - font.setPointSize(18) - self.profilepass.setFont(font) - self.password = LineEdit(self) - self.password.setGeometry(QtCore.QRect(40, 380, 300, 30)) - self.password.setEchoMode(QtWidgets.QLineEdit.Password) - self.leave_blank = QtWidgets.QLabel(self) - self.leave_blank.setGeometry(QtCore.QRect(350, 380, 300, 30)) - self.confirm_password = LineEdit(self) - self.confirm_password.setGeometry(QtCore.QRect(40, 420, 300, 30)) - self.confirm_password.setEchoMode(QtWidgets.QLineEdit.Password) - self.set_password = QtWidgets.QPushButton(self) - self.set_password.setGeometry(QtCore.QRect(40, 470, 300, 30)) - self.set_password.clicked.connect(self.new_password) - self.not_match = QtWidgets.QLabel(self) - self.not_match.setGeometry(QtCore.QRect(350, 420, 300, 30)) - self.not_match.setVisible(False) - self.not_match.setStyleSheet('QLabel { color: #BC1C1C; }') - self.warning = QtWidgets.QLabel(self) - self.warning.setGeometry(QtCore.QRect(40, 510, 500, 30)) - self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') - self.default = QtWidgets.QPushButton(self) - self.default.setGeometry(QtCore.QRect(40, 550, 620, 30)) - path, name = Settings.get_auto_profile() - self.auto = path + name == ProfileHelper.get_path() + Settings.get_instance().name - self.default.clicked.connect(self.auto_profile) - self.retranslateUi() - if profile.status is not None: - self.status.setCurrentIndex(profile.status) - else: - self.status.setVisible(False) - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.export.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Export profile")) - self.setWindowTitle(QtWidgets.QApplication.translate("ProfileSettingsForm", "Profile settings")) - self.label.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Name:")) - self.label_2.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Status:")) - self.label_3.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "TOX ID:")) - self.copyId.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Copy TOX ID")) - self.new_avatar.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "New avatar")) - self.delete_avatar.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Reset avatar")) - self.new_nospam.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "New NoSpam")) - self.profilepass.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Profile password")) - self.password.setPlaceholderText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Password (at least 8 symbols)")) - self.confirm_password.setPlaceholderText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Confirm password")) - self.set_password.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Set password")) - self.not_match.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Passwords do not match")) - self.leave_blank.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Leaving blank will reset current password")) - self.warning.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "There is no way to recover lost passwords")) - self.status.addItem(QtWidgets.QApplication.translate("ProfileSettingsForm", "Online")) - self.status.addItem(QtWidgets.QApplication.translate("ProfileSettingsForm", "Away")) - self.status.addItem(QtWidgets.QApplication.translate("ProfileSettingsForm", "Busy")) - self.copy_pk.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Copy public key")) - if self.auto: - self.default.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as not default profile")) - else: - self.default.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as default profile")) - - def auto_profile(self): - if self.auto: - Settings.reset_auto_profile() - else: - Settings.set_auto_profile(ProfileHelper.get_path(), Settings.get_instance().name) - self.auto = not self.auto - if self.auto: - self.default.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as not default profile")) - else: - self.default.setText( - QtWidgets.QApplication.translate("ProfileSettingsForm", "Mark as default profile")) - - def new_password(self): - if self.password.text() == self.confirm_password.text(): - if not len(self.password.text()) or len(self.password.text()) >= 8: - e = toxes.ToxES.get_instance() - e.set_password(self.password.text()) - self.close() - else: - self.not_match.setText( - QtWidgets.QApplication.translate("ProfileSettingsForm", "Password must be at least 8 symbols")) - self.not_match.setVisible(True) - else: - self.not_match.setText(QtWidgets.QApplication.translate("ProfileSettingsForm", "Passwords do not match")) - self.not_match.setVisible(True) - - def copy(self): - clipboard = QtWidgets.QApplication.clipboard() - profile = Profile.get_instance() - clipboard.setText(profile.tox_id) - pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png') - icon = QtGui.QIcon(pixmap) - self.copyId.setIcon(icon) - self.copyId.setIconSize(QtCore.QSize(10, 10)) - - def copy_public_key(self): - clipboard = QtWidgets.QApplication.clipboard() - profile = Profile.get_instance() - clipboard.setText(profile.tox_id[:64]) - pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png') - icon = QtGui.QIcon(pixmap) - self.copy_pk.setIcon(icon) - self.copy_pk.setIconSize(QtCore.QSize(10, 10)) - - def new_no_spam(self): - self.tox_id.setText(Profile.get_instance().new_nospam()) - - def reset_avatar(self): - Profile.get_instance().reset_avatar() - - def set_avatar(self): - choose = QtWidgets.QApplication.translate("ProfileSettingsForm", "Choose avatar") - name = QtWidgets.QFileDialog.getOpenFileName(self, choose, None, 'Images (*.png)', - options=QtWidgets.QFileDialog.DontUseNativeDialog) - if name[0]: - bitmap = QtGui.QPixmap(name[0]) - bitmap.scaled(QtCore.QSize(128, 128), aspectRatioMode=QtCore.Qt.KeepAspectRatio, - transformMode=QtCore.Qt.SmoothTransformation) - - byte_array = QtCore.QByteArray() - buffer = QtCore.QBuffer(byte_array) - buffer.open(QtCore.QIODevice.WriteOnly) - bitmap.save(buffer, 'PNG') - Profile.get_instance().set_avatar(bytes(byte_array.data())) - - def export_profile(self): - directory = QtWidgets.QFileDialog.getExistingDirectory(self, '', curr_directory(), - QtWidgets.QFileDialog.DontUseNativeDialog) + '/' - if directory != '/': - reply = QtWidgets.QMessageBox.question(None, - QtWidgets.QApplication.translate("ProfileSettingsForm", - 'Use new path'), - QtWidgets.QApplication.translate("ProfileSettingsForm", - 'Do you want to move your profile to this location?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - settings = Settings.get_instance() - settings.export(directory) - profile = Profile.get_instance() - profile.export_db(directory) - ProfileHelper.get_instance().export_profile(directory, reply == QtWidgets.QMessageBox.Yes) - - def closeEvent(self, event): - profile = Profile.get_instance() - profile.set_name(self.nick.text()) - profile.set_status_message(self.status_message.text().encode('utf-8')) - profile.set_status(self.status.currentIndex()) - - -class NetworkSettings(CenteredWidget): - """Network settings form: UDP, Ipv6 and proxy""" - def __init__(self, reset): - super(NetworkSettings, self).__init__() - self.reset = reset - self.initUI() - self.center() - - def initUI(self): - self.setObjectName("NetworkSettings") - self.resize(300, 400) - self.setMinimumSize(QtCore.QSize(300, 400)) - self.setMaximumSize(QtCore.QSize(300, 400)) - self.setBaseSize(QtCore.QSize(300, 400)) - self.ipv = QtWidgets.QCheckBox(self) - self.ipv.setGeometry(QtCore.QRect(20, 10, 97, 22)) - self.ipv.setObjectName("ipv") - self.udp = QtWidgets.QCheckBox(self) - self.udp.setGeometry(QtCore.QRect(150, 10, 97, 22)) - self.udp.setObjectName("udp") - self.proxy = QtWidgets.QCheckBox(self) - self.proxy.setGeometry(QtCore.QRect(20, 40, 97, 22)) - self.http = QtWidgets.QCheckBox(self) - self.http.setGeometry(QtCore.QRect(20, 70, 97, 22)) - self.proxy.setObjectName("proxy") - self.proxyip = LineEdit(self) - self.proxyip.setGeometry(QtCore.QRect(40, 130, 231, 27)) - self.proxyip.setObjectName("proxyip") - self.proxyport = LineEdit(self) - self.proxyport.setGeometry(QtCore.QRect(40, 190, 231, 27)) - self.proxyport.setObjectName("proxyport") - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(40, 100, 66, 17)) - self.label_2 = QtWidgets.QLabel(self) - self.label_2.setGeometry(QtCore.QRect(40, 165, 66, 17)) - self.reconnect = QtWidgets.QPushButton(self) - self.reconnect.setGeometry(QtCore.QRect(40, 230, 231, 30)) - self.reconnect.clicked.connect(self.restart_core) - settings = Settings.get_instance() - self.ipv.setChecked(settings['ipv6_enabled']) - self.udp.setChecked(settings['udp_enabled']) - self.proxy.setChecked(settings['proxy_type']) - self.proxyip.setText(settings['proxy_host']) - self.proxyport.setText(str(settings['proxy_port'])) - self.http.setChecked(settings['proxy_type'] == 1) - self.warning = QtWidgets.QLabel(self) - self.warning.setGeometry(QtCore.QRect(5, 270, 290, 60)) - self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') - self.nodes = QtWidgets.QCheckBox(self) - self.nodes.setGeometry(QtCore.QRect(20, 350, 270, 22)) - self.nodes.setChecked(settings['download_nodes_list']) - self.retranslateUi() - self.proxy.stateChanged.connect(lambda x: self.activate()) - self.activate() - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("NetworkSettings", "Network settings")) - self.ipv.setText(QtWidgets.QApplication.translate("Form", "IPv6")) - self.udp.setText(QtWidgets.QApplication.translate("Form", "UDP")) - self.proxy.setText(QtWidgets.QApplication.translate("Form", "Proxy")) - self.label.setText(QtWidgets.QApplication.translate("Form", "IP:")) - self.label_2.setText(QtWidgets.QApplication.translate("Form", "Port:")) - self.reconnect.setText(QtWidgets.QApplication.translate("NetworkSettings", "Restart TOX core")) - self.http.setText(QtWidgets.QApplication.translate("Form", "HTTP")) - self.nodes.setText(QtWidgets.QApplication.translate("Form", "Download nodes list from tox.chat")) - self.warning.setText(QtWidgets.QApplication.translate("Form", "WARNING:\nusing proxy with enabled UDP\ncan produce IP leak")) - - def activate(self): - bl = self.proxy.isChecked() - self.proxyip.setEnabled(bl) - self.http.setEnabled(bl) - self.proxyport.setEnabled(bl) - - def restart_core(self): - try: - settings = Settings.get_instance() - settings['ipv6_enabled'] = self.ipv.isChecked() - settings['udp_enabled'] = self.udp.isChecked() - settings['proxy_type'] = 2 - int(self.http.isChecked()) if self.proxy.isChecked() else 0 - settings['proxy_host'] = str(self.proxyip.text()) - settings['proxy_port'] = int(self.proxyport.text()) - settings['download_nodes_list'] = self.nodes.isChecked() - settings.save() - # recreate tox instance - Profile.get_instance().reset(self.reset) - self.close() - except Exception as ex: - log('Exception in restart: ' + str(ex)) - - -class PrivacySettings(CenteredWidget): - """Privacy settings form: history, typing notifications""" - - def __init__(self): - super(PrivacySettings, self).__init__() - self.initUI() - self.center() - - def initUI(self): - self.setObjectName("privacySettings") - self.resize(370, 600) - self.setMinimumSize(QtCore.QSize(370, 600)) - self.setMaximumSize(QtCore.QSize(370, 600)) - self.saveHistory = QtWidgets.QCheckBox(self) - self.saveHistory.setGeometry(QtCore.QRect(10, 20, 350, 22)) - self.saveUnsentOnly = QtWidgets.QCheckBox(self) - self.saveUnsentOnly.setGeometry(QtCore.QRect(10, 60, 350, 22)) - - self.fileautoaccept = QtWidgets.QCheckBox(self) - self.fileautoaccept.setGeometry(QtCore.QRect(10, 100, 350, 22)) - - self.typingNotifications = QtWidgets.QCheckBox(self) - self.typingNotifications.setGeometry(QtCore.QRect(10, 140, 350, 30)) - self.inlines = QtWidgets.QCheckBox(self) - self.inlines.setGeometry(QtCore.QRect(10, 180, 350, 30)) - self.auto_path = QtWidgets.QLabel(self) - self.auto_path.setGeometry(QtCore.QRect(10, 230, 350, 30)) - self.path = QtWidgets.QPlainTextEdit(self) - self.path.setGeometry(QtCore.QRect(10, 265, 350, 45)) - self.change_path = QtWidgets.QPushButton(self) - self.change_path.setGeometry(QtCore.QRect(10, 320, 350, 30)) - settings = Settings.get_instance() - self.typingNotifications.setChecked(settings['typing_notifications']) - self.fileautoaccept.setChecked(settings['allow_auto_accept']) - self.saveHistory.setChecked(settings['save_history']) - self.inlines.setChecked(settings['allow_inline']) - self.saveUnsentOnly.setChecked(settings['save_unsent_only']) - self.saveUnsentOnly.setEnabled(settings['save_history']) - self.saveHistory.stateChanged.connect(self.update) - self.path.setPlainText(settings['auto_accept_path'] or curr_directory()) - self.change_path.clicked.connect(self.new_path) - self.block_user_label = QtWidgets.QLabel(self) - self.block_user_label.setGeometry(QtCore.QRect(10, 360, 350, 30)) - self.block_id = QtWidgets.QPlainTextEdit(self) - self.block_id.setGeometry(QtCore.QRect(10, 390, 350, 30)) - self.block = QtWidgets.QPushButton(self) - self.block.setGeometry(QtCore.QRect(10, 430, 350, 30)) - self.block.clicked.connect(lambda: Profile.get_instance().block_user(self.block_id.toPlainText()) or self.close()) - self.blocked_users_label = QtWidgets.QLabel(self) - self.blocked_users_label.setGeometry(QtCore.QRect(10, 470, 350, 30)) - self.comboBox = QtWidgets.QComboBox(self) - self.comboBox.setGeometry(QtCore.QRect(10, 500, 350, 30)) - self.comboBox.addItems(settings['blocked']) - self.unblock = QtWidgets.QPushButton(self) - self.unblock.setGeometry(QtCore.QRect(10, 540, 350, 30)) - self.unblock.clicked.connect(lambda: self.unblock_user()) - self.retranslateUi() - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("privacySettings", "Privacy settings")) - self.saveHistory.setText(QtWidgets.QApplication.translate("privacySettings", "Save chat history")) - self.fileautoaccept.setText(QtWidgets.QApplication.translate("privacySettings", "Allow file auto accept")) - self.typingNotifications.setText(QtWidgets.QApplication.translate("privacySettings", "Send typing notifications")) - self.auto_path.setText(QtWidgets.QApplication.translate("privacySettings", "Auto accept default path:")) - self.change_path.setText(QtWidgets.QApplication.translate("privacySettings", "Change")) - self.inlines.setText(QtWidgets.QApplication.translate("privacySettings", "Allow inlines")) - self.block_user_label.setText(QtWidgets.QApplication.translate("privacySettings", "Block by public key:")) - self.blocked_users_label.setText(QtWidgets.QApplication.translate("privacySettings", "Blocked users:")) - self.unblock.setText(QtWidgets.QApplication.translate("privacySettings", "Unblock")) - self.block.setText(QtWidgets.QApplication.translate("privacySettings", "Block user")) - self.saveUnsentOnly.setText(QtWidgets.QApplication.translate("privacySettings", "Save unsent messages only")) - - def update(self, new_state): - self.saveUnsentOnly.setEnabled(new_state) - if not new_state: - self.saveUnsentOnly.setChecked(False) - - def unblock_user(self): - if not self.comboBox.count(): - return - title = QtWidgets.QApplication.translate("privacySettings", "Add to friend list") - info = QtWidgets.QApplication.translate("privacySettings", "Do you want to add this user to friend list?") - reply = QtWidgets.QMessageBox.question(None, title, info, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) - Profile.get_instance().unblock_user(self.comboBox.currentText(), reply == QtWidgets.QMessageBox.Yes) - self.close() - - def closeEvent(self, event): - settings = Settings.get_instance() - settings['typing_notifications'] = self.typingNotifications.isChecked() - settings['allow_auto_accept'] = self.fileautoaccept.isChecked() - - if settings['save_history'] and not self.saveHistory.isChecked(): # clear history - reply = QtWidgets.QMessageBox.question(None, - QtWidgets.QApplication.translate("privacySettings", - 'Chat history'), - QtWidgets.QApplication.translate("privacySettings", - 'History will be cleaned! Continue?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - Profile.get_instance().clear_history() - settings['save_history'] = self.saveHistory.isChecked() - else: - settings['save_history'] = self.saveHistory.isChecked() - if self.saveUnsentOnly.isChecked() and not settings['save_unsent_only']: - reply = QtWidgets.QMessageBox.question(None, - QtWidgets.QApplication.translate("privacySettings", - 'Chat history'), - QtWidgets.QApplication.translate("privacySettings", - 'History will be cleaned! Continue?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - Profile.get_instance().clear_history(None, True) - settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() - else: - settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() - settings['auto_accept_path'] = self.path.toPlainText() - settings['allow_inline'] = self.inlines.isChecked() - settings.save() - - def new_path(self): - directory = QtWidgets.QFileDialog.getExistingDirectory(options=QtWidgets.QFileDialog.DontUseNativeDialog) + '/' - if directory != '/': - self.path.setPlainText(directory) - - -class NotificationsSettings(CenteredWidget): - """Notifications settings form""" - - def __init__(self): - super(NotificationsSettings, self).__init__() - self.initUI() - self.center() - - def initUI(self): - self.setObjectName("notificationsForm") - self.resize(350, 210) - self.setMinimumSize(QtCore.QSize(350, 210)) - self.setMaximumSize(QtCore.QSize(350, 210)) - self.enableNotifications = QtWidgets.QCheckBox(self) - self.enableNotifications.setGeometry(QtCore.QRect(10, 20, 340, 18)) - self.callsSound = QtWidgets.QCheckBox(self) - self.callsSound.setGeometry(QtCore.QRect(10, 170, 340, 18)) - self.soundNotifications = QtWidgets.QCheckBox(self) - self.soundNotifications.setGeometry(QtCore.QRect(10, 70, 340, 18)) - self.groupNotifications = QtWidgets.QCheckBox(self) - self.groupNotifications.setGeometry(QtCore.QRect(10, 120, 340, 18)) - font = QtGui.QFont() - s = Settings.get_instance() - font.setFamily(s['font']) - font.setPointSize(12) - self.callsSound.setFont(font) - self.soundNotifications.setFont(font) - self.enableNotifications.setFont(font) - self.groupNotifications.setFont(font) - self.enableNotifications.setChecked(s['notifications']) - self.soundNotifications.setChecked(s['sound_notifications']) - self.groupNotifications.setChecked(s['group_notifications']) - self.callsSound.setChecked(s['calls_sound']) - self.retranslateUi() - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("notificationsForm", "Notification settings")) - self.enableNotifications.setText(QtWidgets.QApplication.translate("notificationsForm", "Enable notifications")) - self.groupNotifications.setText(QtWidgets.QApplication.translate("notificationsForm", "Notify about all messages in groups")) - self.callsSound.setText(QtWidgets.QApplication.translate("notificationsForm", "Enable call\'s sound")) - self.soundNotifications.setText(QtWidgets.QApplication.translate("notificationsForm", "Enable sound notifications")) - - def closeEvent(self, *args, **kwargs): - settings = Settings.get_instance() - settings['notifications'] = self.enableNotifications.isChecked() - settings['sound_notifications'] = self.soundNotifications.isChecked() - settings['group_notifications'] = self.groupNotifications.isChecked() - settings['calls_sound'] = self.callsSound.isChecked() - settings.save() - - -class InterfaceSettings(CenteredWidget): - """Interface settings form""" - def __init__(self): - super(InterfaceSettings, self).__init__() - self.initUI() - self.center() - - def initUI(self): - self.setObjectName("interfaceForm") - self.setMinimumSize(QtCore.QSize(400, 650)) - self.setMaximumSize(QtCore.QSize(400, 650)) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(30, 10, 370, 20)) - settings = Settings.get_instance() - font = QtGui.QFont() - font.setPointSize(14) - font.setBold(True) - font.setFamily(settings['font']) - self.label.setFont(font) - self.themeSelect = QtWidgets.QComboBox(self) - self.themeSelect.setGeometry(QtCore.QRect(30, 40, 120, 30)) - self.themeSelect.addItems(list(settings.built_in_themes().keys())) - theme = settings['theme'] - if theme in settings.built_in_themes().keys(): - index = list(settings.built_in_themes().keys()).index(theme) - else: - index = 0 - self.themeSelect.setCurrentIndex(index) - self.lang_choose = QtWidgets.QComboBox(self) - self.lang_choose.setGeometry(QtCore.QRect(30, 110, 120, 30)) - supported = sorted(Settings.supported_languages().keys(), reverse=True) - for key in supported: - self.lang_choose.insertItem(0, key) - if settings['language'] == key: - self.lang_choose.setCurrentIndex(0) - self.lang = QtWidgets.QLabel(self) - self.lang.setGeometry(QtCore.QRect(30, 80, 370, 20)) - self.lang.setFont(font) - self.mirror_mode = QtWidgets.QCheckBox(self) - self.mirror_mode.setGeometry(QtCore.QRect(30, 160, 370, 20)) - self.mirror_mode.setChecked(settings['mirror_mode']) - self.smileys = QtWidgets.QCheckBox(self) - self.smileys.setGeometry(QtCore.QRect(30, 190, 120, 20)) - self.smileys.setChecked(settings['smileys']) - self.smiley_pack_label = QtWidgets.QLabel(self) - self.smiley_pack_label.setGeometry(QtCore.QRect(30, 230, 370, 20)) - self.smiley_pack_label.setFont(font) - self.smiley_pack = QtWidgets.QComboBox(self) - self.smiley_pack.setGeometry(QtCore.QRect(30, 260, 160, 30)) - sm = smileys.SmileyLoader.get_instance() - self.smiley_pack.addItems(sm.get_packs_list()) - try: - ind = sm.get_packs_list().index(settings['smiley_pack']) - except: - ind = sm.get_packs_list().index('default') - self.smiley_pack.setCurrentIndex(ind) - self.messages_font_size_label = QtWidgets.QLabel(self) - self.messages_font_size_label.setGeometry(QtCore.QRect(30, 300, 370, 20)) - self.messages_font_size_label.setFont(font) - self.messages_font_size = QtWidgets.QComboBox(self) - self.messages_font_size.setGeometry(QtCore.QRect(30, 330, 160, 30)) - self.messages_font_size.addItems([str(x) for x in range(10, 25)]) - self.messages_font_size.setCurrentIndex(settings['message_font_size'] - 10) - - self.unread = QtWidgets.QPushButton(self) - self.unread.setGeometry(QtCore.QRect(30, 470, 340, 30)) - self.unread.clicked.connect(self.select_color) - - self.compact_mode = QtWidgets.QCheckBox(self) - self.compact_mode.setGeometry(QtCore.QRect(30, 380, 370, 20)) - self.compact_mode.setChecked(settings['compact_mode']) - - self.close_to_tray = QtWidgets.QCheckBox(self) - self.close_to_tray.setGeometry(QtCore.QRect(30, 410, 370, 20)) - self.close_to_tray.setChecked(settings['close_to_tray']) - - self.show_avatars = QtWidgets.QCheckBox(self) - self.show_avatars.setGeometry(QtCore.QRect(30, 440, 370, 20)) - self.show_avatars.setChecked(settings['show_avatars']) - - self.choose_font = QtWidgets.QPushButton(self) - self.choose_font.setGeometry(QtCore.QRect(30, 510, 340, 30)) - self.choose_font.clicked.connect(self.new_font) - - self.import_smileys = QtWidgets.QPushButton(self) - self.import_smileys.setGeometry(QtCore.QRect(30, 550, 340, 30)) - self.import_smileys.clicked.connect(self.import_sm) - - self.import_stickers = QtWidgets.QPushButton(self) - self.import_stickers.setGeometry(QtCore.QRect(30, 590, 340, 30)) - self.import_stickers.clicked.connect(self.import_st) - - self.retranslateUi() - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.show_avatars.setText(QtWidgets.QApplication.translate("interfaceForm", "Show avatars in chat")) - self.setWindowTitle(QtWidgets.QApplication.translate("interfaceForm", "Interface settings")) - self.label.setText(QtWidgets.QApplication.translate("interfaceForm", "Theme:")) - self.lang.setText(QtWidgets.QApplication.translate("interfaceForm", "Language:")) - self.smileys.setText(QtWidgets.QApplication.translate("interfaceForm", "Smileys")) - self.smiley_pack_label.setText(QtWidgets.QApplication.translate("interfaceForm", "Smiley pack:")) - self.mirror_mode.setText(QtWidgets.QApplication.translate("interfaceForm", "Mirror mode")) - self.messages_font_size_label.setText(QtWidgets.QApplication.translate("interfaceForm", "Messages font size:")) - self.unread.setText(QtWidgets.QApplication.translate("interfaceForm", "Select unread messages notification color")) - self.compact_mode.setText(QtWidgets.QApplication.translate("interfaceForm", "Compact contact list")) - self.import_smileys.setText(QtWidgets.QApplication.translate("interfaceForm", "Import smiley pack")) - self.import_stickers.setText(QtWidgets.QApplication.translate("interfaceForm", "Import sticker pack")) - self.close_to_tray.setText(QtWidgets.QApplication.translate("interfaceForm", "Close to tray")) - self.choose_font.setText(QtWidgets.QApplication.translate("interfaceForm", "Select font")) - - def import_st(self): - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", - 'Choose folder with sticker pack'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - - if directory: - src = directory + '/' - dest = curr_directory() + '/stickers/' + os.path.basename(directory) + '/' - copy(src, dest) - - def import_sm(self): - directory = QtWidgets.QFileDialog.getExistingDirectory(self, - QtWidgets.QApplication.translate("MainWindow", - 'Choose folder with smiley pack'), - curr_directory(), - QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) - - if directory: - src = directory + '/' - dest = curr_directory() + '/smileys/' + os.path.basename(directory) + '/' - copy(src, dest) - - def new_font(self): - settings = Settings.get_instance() - font, ok = QtWidgets.QFontDialog.getFont(QtGui.QFont(settings['font'], 10), self) - if ok: - settings['font'] = font.family() - settings.save() - msgBox = QtWidgets.QMessageBox() - text = QtWidgets.QApplication.translate("interfaceForm", 'Restart app to apply settings') - msgBox.setWindowTitle(QtWidgets.QApplication.translate("interfaceForm", 'Restart required')) - msgBox.setText(text) - msgBox.exec_() - - def select_color(self): - settings = Settings.get_instance() - col = QtWidgets.QColorDialog.getColor(QtGui.QColor(settings['unread_color'])) - - if col.isValid(): - name = col.name() - settings['unread_color'] = name - settings.save() - - def closeEvent(self, event): - settings = Settings.get_instance() - settings['theme'] = str(self.themeSelect.currentText()) - try: - theme = settings['theme'] - app = QtWidgets.QApplication.instance() - with open(curr_directory() + settings.built_in_themes()[theme]) as fl: - style = fl.read() - app.setStyleSheet(style) - except IsADirectoryError: - app.setStyleSheet('') # for default style - settings['smileys'] = self.smileys.isChecked() - restart = False - if settings['mirror_mode'] != self.mirror_mode.isChecked(): - settings['mirror_mode'] = self.mirror_mode.isChecked() - restart = True - if settings['compact_mode'] != self.compact_mode.isChecked(): - settings['compact_mode'] = self.compact_mode.isChecked() - restart = True - if settings['show_avatars'] != self.show_avatars.isChecked(): - settings['show_avatars'] = self.show_avatars.isChecked() - restart = True - settings['smiley_pack'] = self.smiley_pack.currentText() - settings['close_to_tray'] = self.close_to_tray.isChecked() - smileys.SmileyLoader.get_instance().load_pack() - language = self.lang_choose.currentText() - if settings['language'] != language: - settings['language'] = language - text = self.lang_choose.currentText() - path = Settings.supported_languages()[text] - app = QtWidgets.QApplication.instance() - app.removeTranslator(app.translator) - app.translator.load(curr_directory() + '/translations/' + path) - app.installTranslator(app.translator) - settings['message_font_size'] = self.messages_font_size.currentIndex() + 10 - Profile.get_instance().update() - settings.save() - if restart: - msgBox = QtWidgets.QMessageBox() - text = QtWidgets.QApplication.translate("interfaceForm", 'Restart app to apply settings') - msgBox.setWindowTitle(QtWidgets.QApplication.translate("interfaceForm", 'Restart required')) - msgBox.setText(text) - msgBox.exec_() - - -class AudioSettings(CenteredWidget): - """ - Audio calls settings form - """ - - def __init__(self): - super(AudioSettings, self).__init__() - self.initUI() - self.retranslateUi() - self.center() - - def initUI(self): - self.setObjectName("audioSettingsForm") - self.resize(400, 150) - self.setMinimumSize(QtCore.QSize(400, 150)) - self.setMaximumSize(QtCore.QSize(400, 150)) - self.in_label = QtWidgets.QLabel(self) - self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20)) - self.out_label = QtWidgets.QLabel(self) - self.out_label.setGeometry(QtCore.QRect(25, 65, 350, 20)) - settings = Settings.get_instance() - font = QtGui.QFont() - font.setPointSize(16) - font.setBold(True) - font.setFamily(settings['font']) - self.in_label.setFont(font) - self.out_label.setFont(font) - self.input = QtWidgets.QComboBox(self) - self.input.setGeometry(QtCore.QRect(25, 30, 350, 30)) - self.output = QtWidgets.QComboBox(self) - self.output.setGeometry(QtCore.QRect(25, 90, 350, 30)) - p = pyaudio.PyAudio() - self.in_indexes, self.out_indexes = [], [] - for i in range(p.get_device_count()): - device = p.get_device_info_by_index(i) - if device["maxInputChannels"]: - self.input.addItem(str(device["name"])) - self.in_indexes.append(i) - if device["maxOutputChannels"]: - self.output.addItem(str(device["name"])) - self.out_indexes.append(i) - self.input.setCurrentIndex(self.in_indexes.index(settings.audio['input'])) - self.output.setCurrentIndex(self.out_indexes.index(settings.audio['output'])) - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("audioSettingsForm", "Audio settings")) - self.in_label.setText(QtWidgets.QApplication.translate("audioSettingsForm", "Input device:")) - self.out_label.setText(QtWidgets.QApplication.translate("audioSettingsForm", "Output device:")) - - def closeEvent(self, event): - settings = Settings.get_instance() - settings.audio['input'] = self.in_indexes[self.input.currentIndex()] - settings.audio['output'] = self.out_indexes[self.output.currentIndex()] - settings.save() - - -class DesktopAreaSelectionWindow(RubberBandWindow): - - def mouseReleaseEvent(self, event): - if self.rubberband.isVisible(): - self.rubberband.hide() - rect = self.rubberband.geometry() - width, height = rect.width(), rect.height() - if width >= 8 and height >= 8: - self.parent.save(rect.x(), rect.y(), width - (width % 4), height - (height % 4)) - self.close() - - -class VideoSettings(CenteredWidget): - """ - Audio calls settings form - """ - - def __init__(self): - super().__init__() - self.initUI() - self.retranslateUi() - self.center() - self.desktopAreaSelection = None - - def initUI(self): - self.setObjectName("videoSettingsForm") - self.resize(400, 120) - self.setMinimumSize(QtCore.QSize(400, 120)) - self.setMaximumSize(QtCore.QSize(400, 120)) - self.in_label = QtWidgets.QLabel(self) - self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20)) - settings = Settings.get_instance() - font = QtGui.QFont() - font.setPointSize(16) - font.setBold(True) - font.setFamily(settings['font']) - self.in_label.setFont(font) - self.video_size = QtWidgets.QComboBox(self) - self.video_size.setGeometry(QtCore.QRect(25, 70, 350, 30)) - self.input = QtWidgets.QComboBox(self) - self.input.setGeometry(QtCore.QRect(25, 30, 350, 30)) - self.input.currentIndexChanged.connect(self.selectionChanged) - self.button = QtWidgets.QPushButton(self) - self.button.clicked.connect(self.button_clicked) - self.button.setGeometry(QtCore.QRect(25, 70, 350, 30)) - import cv2 - self.devices = [-1] - screen = QtWidgets.QApplication.primaryScreen() - size = screen.size() - self.frame_max_sizes = [(size.width(), size.height())] - desktop = QtWidgets.QApplication.translate("videoSettingsForm", "Desktop") - self.input.addItem(desktop) - for i in range(10): - v = cv2.VideoCapture(i) - if v.isOpened(): - v.set(cv2.CAP_PROP_FRAME_WIDTH, 10000) - v.set(cv2.CAP_PROP_FRAME_HEIGHT, 10000) - - width = int(v.get(cv2.CAP_PROP_FRAME_WIDTH)) - height = int(v.get(cv2.CAP_PROP_FRAME_HEIGHT)) - del v - self.devices.append(i) - self.frame_max_sizes.append((width, height)) - self.input.addItem('Device #' + str(i)) - try: - index = self.devices.index(settings.video['device']) - self.input.setCurrentIndex(index) - except: - print('Video devices error!') - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("videoSettingsForm", "Video settings")) - self.in_label.setText(QtWidgets.QApplication.translate("videoSettingsForm", "Device:")) - self.button.setText(QtWidgets.QApplication.translate("videoSettingsForm", "Select region")) - - def button_clicked(self): - self.desktopAreaSelection = DesktopAreaSelectionWindow(self) - - def closeEvent(self, event): - if self.input.currentIndex() == 0: - return - try: - settings = Settings.get_instance() - settings.video['device'] = self.devices[self.input.currentIndex()] - text = self.video_size.currentText() - settings.video['width'] = int(text.split(' ')[0]) - settings.video['height'] = int(text.split(' ')[-1]) - settings.save() - except Exception as ex: - print('Saving video settings error: ' + str(ex)) - - def save(self, x, y, width, height): - self.desktopAreaSelection = None - settings = Settings.get_instance() - settings.video['device'] = -1 - settings.video['width'] = width - settings.video['height'] = height - settings.video['x'] = x - settings.video['y'] = y - settings.save() - - def selectionChanged(self): - if self.input.currentIndex() == 0: - self.button.setVisible(True) - self.video_size.setVisible(False) - else: - self.button.setVisible(False) - self.video_size.setVisible(True) - width, height = self.frame_max_sizes[self.input.currentIndex()] - self.video_size.clear() - dims = [ - (320, 240), - (640, 360), - (640, 480), - (720, 480), - (1280, 720), - (1920, 1080), - (2560, 1440) - ] - for w, h in dims: - if w <= width and h <= height: - self.video_size.addItem(str(w) + ' * ' + str(h)) - - -class PluginsSettings(CenteredWidget): - """ - Plugins settings form - """ - - def __init__(self): - super(PluginsSettings, self).__init__() - self.initUI() - self.center() - self.retranslateUi() - - def initUI(self): - self.resize(400, 210) - self.setMinimumSize(QtCore.QSize(400, 210)) - self.setMaximumSize(QtCore.QSize(400, 210)) - self.comboBox = QtWidgets.QComboBox(self) - self.comboBox.setGeometry(QtCore.QRect(30, 10, 340, 30)) - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(30, 40, 340, 90)) - self.label.setWordWrap(True) - self.button = QtWidgets.QPushButton(self) - self.button.setGeometry(QtCore.QRect(30, 130, 340, 30)) - self.button.clicked.connect(self.button_click) - self.open = QtWidgets.QPushButton(self) - self.open.setGeometry(QtCore.QRect(30, 170, 340, 30)) - self.open.clicked.connect(self.open_plugin) - self.pl_loader = plugin_support.PluginLoader.get_instance() - self.update_list() - self.comboBox.currentIndexChanged.connect(self.show_data) - self.show_data() - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate('PluginsForm', "Plugins")) - self.open.setText(QtWidgets.QApplication.translate('PluginsForm', "Open selected plugin")) - - def open_plugin(self): - ind = self.comboBox.currentIndex() - plugin = self.data[ind] - window = self.pl_loader.plugin_window(plugin[-1]) - if window is not None: - self.window = window - self.window.show() - else: - msgBox = QtWidgets.QMessageBox() - text = QtWidgets.QApplication.translate("PluginsForm", 'No GUI found for this plugin') - msgBox.setWindowTitle(QtWidgets.QApplication.translate("PluginsForm", 'Error')) - msgBox.setText(text) - msgBox.exec_() - - def update_list(self): - self.comboBox.clear() - data = self.pl_loader.get_plugins_list() - self.comboBox.addItems(list(map(lambda x: x[0], data))) - self.data = data - - def show_data(self): - ind = self.comboBox.currentIndex() - if len(self.data): - plugin = self.data[ind] - descr = plugin[2] or QtWidgets.QApplication.translate("PluginsForm", "No description available") - self.label.setText(descr) - if plugin[1]: - self.button.setText(QtWidgets.QApplication.translate("PluginsForm", "Disable plugin")) - else: - self.button.setText(QtWidgets.QApplication.translate("PluginsForm", "Enable plugin")) - else: - self.open.setVisible(False) - self.button.setVisible(False) - self.label.setText(QtWidgets.QApplication.translate("PluginsForm", "No plugins found")) - - def button_click(self): - ind = self.comboBox.currentIndex() - plugin = self.data[ind] - self.pl_loader.toggle_plugin(plugin[-1]) - plugin[1] = not plugin[1] - if plugin[1]: - self.button.setText(QtWidgets.QApplication.translate("PluginsForm", "Disable plugin")) - else: - self.button.setText(QtWidgets.QApplication.translate("PluginsForm", "Enable plugin")) - - -class UpdateSettings(CenteredWidget): - """ - Updates settings form - """ - - def __init__(self): - super(UpdateSettings, self).__init__() - self.initUI() - self.center() - - def initUI(self): - self.setObjectName("updateSettingsForm") - self.resize(400, 150) - self.setMinimumSize(QtCore.QSize(400, 120)) - self.setMaximumSize(QtCore.QSize(400, 120)) - self.in_label = QtWidgets.QLabel(self) - self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20)) - settings = Settings.get_instance() - font = QtGui.QFont() - font.setPointSize(16) - font.setBold(True) - font.setFamily(settings['font']) - self.in_label.setFont(font) - self.autoupdate = QtWidgets.QComboBox(self) - self.autoupdate.setGeometry(QtCore.QRect(25, 30, 350, 30)) - self.button = QtWidgets.QPushButton(self) - self.button.setGeometry(QtCore.QRect(25, 70, 350, 30)) - self.button.setEnabled(settings['update']) - self.button.clicked.connect(self.update_client) - - self.retranslateUi() - self.autoupdate.setCurrentIndex(settings['update']) - QtCore.QMetaObject.connectSlotsByName(self) - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("updateSettingsForm", "Update settings")) - self.in_label.setText(QtWidgets.QApplication.translate("updateSettingsForm", "Select update mode:")) - self.button.setText(QtWidgets.QApplication.translate("updateSettingsForm", "Update Toxygen")) - self.autoupdate.addItem(QtWidgets.QApplication.translate("updateSettingsForm", "Disabled")) - self.autoupdate.addItem(QtWidgets.QApplication.translate("updateSettingsForm", "Manual")) - self.autoupdate.addItem(QtWidgets.QApplication.translate("updateSettingsForm", "Auto")) - - def closeEvent(self, event): - settings = Settings.get_instance() - settings['update'] = self.autoupdate.currentIndex() - settings.save() - - def update_client(self): - if not updater.connection_available(): - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("updateSettingsForm", "Error")) - text = (QtWidgets.QApplication.translate("updateSettingsForm", 'Problems with internet connection')) - msgBox.setText(text) - msgBox.exec_() - return - if not updater.updater_available(): - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("updateSettingsForm", "Error")) - text = (QtWidgets.QApplication.translate("updateSettingsForm", 'Updater not found')) - msgBox.setText(text) - msgBox.exec_() - return - version = updater.check_for_updates() - if version is not None: - updater.download(version) - QtWidgets.QApplication.closeAllWindows() - else: - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("updateSettingsForm", "No updates found")) - text = (QtWidgets.QApplication.translate("updateSettingsForm", 'Toxygen is up to date')) - msgBox.setText(text) - msgBox.exec_() diff --git a/toxygen/messages.py b/toxygen/messages.py deleted file mode 100644 index 8d9f4a3..0000000 --- a/toxygen/messages.py +++ /dev/null @@ -1,113 +0,0 @@ - - -MESSAGE_TYPE = { - 'TEXT': 0, - 'ACTION': 1, - 'FILE_TRANSFER': 2, - 'INLINE': 3, - 'INFO_MESSAGE': 4, - 'GC_TEXT': 5, - 'GC_ACTION': 6 -} - - -class Message: - - def __init__(self, message_type, owner, time): - self._time = time - self._type = message_type - self._owner = owner - - def get_type(self): - return self._type - - def get_owner(self): - return self._owner - - def mark_as_sent(self): - self._owner = 0 - - -class TextMessage(Message): - """ - Plain text or action message - """ - - def __init__(self, message, owner, time, message_type): - super(TextMessage, self).__init__(message_type, owner, time) - self._message = message - - def get_data(self): - return self._message, self._owner, self._time, self._type - - -class GroupChatMessage(TextMessage): - - def __init__(self, message, owner, time, message_type, name): - super().__init__(message, owner, time, message_type) - self._user_name = name - - def get_data(self): - return self._message, self._owner, self._time, self._type, self._user_name - - -class TransferMessage(Message): - """ - Message with info about file transfer - """ - - def __init__(self, owner, time, status, size, name, friend_number, file_number): - super(TransferMessage, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time) - self._status = status - self._size = size - self._file_name = name - self._friend_number, self._file_number = friend_number, file_number - - def is_active(self, file_number): - return self._file_number == file_number and self._status not in (2, 3) - - def get_friend_number(self): - return self._friend_number - - def get_file_number(self): - return self._file_number - - def get_status(self): - return self._status - - def set_status(self, value): - self._status = value - - def get_data(self): - return self._file_name, self._size, self._time, self._owner, self._friend_number, self._file_number, self._status - - -class UnsentFile(Message): - def __init__(self, path, data, time): - super(UnsentFile, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], 0, time) - self._data, self._path = data, path - - def get_data(self): - return self._path, self._data, self._time - - def get_status(self): - return None - - -class InlineImage(Message): - """ - Inline image - """ - - def __init__(self, data): - super(InlineImage, self).__init__(MESSAGE_TYPE['INLINE'], None, None) - self._data = data - - def get_data(self): - return self._data - - -class InfoMessage(TextMessage): - - def __init__(self, message, time): - super(InfoMessage, self).__init__(message, None, time, MESSAGE_TYPE['INFO_MESSAGE']) diff --git a/toxygen/nodes.json b/toxygen/nodes.json deleted file mode 100644 index 003bbc0..0000000 --- a/toxygen/nodes.json +++ /dev/null @@ -1 +0,0 @@ -{"last_scan":1516822981,"last_refresh":1516822982,"nodes":[{"ipv4":"node.tox.biribiri.org","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67","maintainer":"nurupo","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Welcome, stranger #7985. I'm up for 5d 14h 34m 34s, running since Jan 19 05:08:27 UTC. If I get outdated, please ping my maintainer at nurupo.contributions@gmail.com","last_ping":1516822981},{"ipv4":"nodes.tox.chat","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"6FC41E2BD381D37E9748FC0E0328CE086AF9598BECC8FEB7DDF2E440475F300E","maintainer":"Impyy","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Straps boots like no other","last_ping":1516822981},{"ipv4":"130.133.110.14","ipv6":"2001:6f8:1c3c:babe::14:1","port":33445,"tcp_ports":[33445],"public_key":"461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F","maintainer":"Manolis","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Spline tox bootstrap node","last_ping":1516822981},{"ipv4":"205.185.116.116","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702","maintainer":"Busindre","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"198.98.51.198","ipv6":"2605:6400:1:fed5:22:45af:ec10:f329","port":33445,"tcp_ports":[33445,3389],"public_key":"1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F","maintainer":"Busindre","location":"US","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"85.172.30.117","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832","maintainer":"ray65536","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Ray's Tox Node","last_ping":1516822981},{"ipv4":"194.249.212.109","ipv6":"2001:1470:fbfe::109","port":33445,"tcp_ports":[33445,3389],"public_key":"3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B","maintainer":"fluke571","location":"SI","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"185.25.116.107","ipv6":"2a00:7a60:0:746b::3","port":33445,"tcp_ports":[33445,3389],"public_key":"DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43","maintainer":"MAH69K","location":"UA","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Saluton! Mia Tox ID: B229B7BD68FC66C2716EAB8671A461906321C764782D7B3EDBB650A315F6C458EF744CE89F07. Scribu! ;)","last_ping":1516822981},{"ipv4":"5.189.176.217","ipv6":"2a02:c200:1:10:3:1:605:1337","port":5190,"tcp_ports":[3389,33445,5190],"public_key":"2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F","maintainer":"tastytea","location":"DE","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1516822981},{"ipv4":"217.182.143.254","ipv6":"2001:41d0:302:1000::e111","port":2306,"tcp_ports":[33445,2306,443],"public_key":"7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147","maintainer":"pucetox","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"by pucetox,\nipv4/ipv6 UDP:2306 TCP:21/80/443/2306/33445\nsync your nodes here tox.0x10k.com/bootstrapd-conf , \n for communication: 1D1C0B992DEB6D7F18561176F7F5E572BCC7F2BA5CFA7E9E437B9134122CE96D906A6119F9D2","last_ping":1516822981},{"ipv4":"104.223.122.15","ipv6":"2607:ff48:aa81:800::35eb:1","port":33445,"tcp_ports":[3389,33445],"public_key":"0FB96EEBFB1650DDB52E70CF773DDFCABE25A95CC3BB50FC251082E4B63EF82A","maintainer":"ru_maniac","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"built on: Tue Feb 21st 2017, 10:52:30 UTC+3\nplease note: running on TokTox Toxcore!\nmore info on the matter: goo.gl/Gz5KhK \u0026 goo.gl/i2TZJr\n\ntox id for queries and general info: EBD2A7B649ABB10ED9F47E5113F04000F39D46F087CEB62FCCE1069471FD6915256D197F2A97","last_ping":1516822981},{"ipv4":"tox.verdict.gg","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976","maintainer":"Deliran","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Praise The Sun!","last_ping":1516822981},{"ipv4":"d4rk4.ru","ipv6":"-","port":1813,"tcp_ports":[1813],"public_key":"53737F6D47FA6BD2808F378E339AF45BF86F39B64E79D6D491C53A1D522E7039","maintainer":"D4rk4","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"TOX ID: 35EDC07AEB18B163E07EE33F6CDDA63969F394FF6A617CEAB22A7EBBEAAAF854C0EDFBD46898","last_ping":1516822981},{"ipv4":"51.254.84.212","ipv6":"2001:41d0:a:1a3b::18","port":33445,"tcp_ports":[3389,33445],"public_key":"AEC204B9A4501412D5F0BB67D9C81B5DB3EE6ADA64122D32A3E9B093D544327D","maintainer":"a68366","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Since 26.12.2015","last_ping":1516822981},{"ipv4":"88.99.133.52","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"2D320F971EF2CA18004416C2AAE7BA52BF7949DB34EA8E2E21AF67BD367BE211","maintainer":"Skey","location":"FR","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"92.54.84.70","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"5625A62618CB4FCA70E147A71B29695F38CC65FF0CBD68AD46254585BE564802","maintainer":"t3mp","location":"RU","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"tox.uplinklabs.net","ipv6":"tox.uplinklabs.net","port":33445,"tcp_ports":[3389,33445],"public_key":"1A56EA3EDF5DF4C0AEABBF3C2E4E603890F87E983CAC8A0D532A335F2C6E3E1F","maintainer":"AbacusAvenger","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"i don't know what this is for","last_ping":1516822981},{"ipv4":"toxnode.nek0.net","ipv6":"toxnode.nek0.net","port":33445,"tcp_ports":[3389,33445],"public_key":"20965721D32CE50C3E837DD75B33908B33037E6225110BFF209277AEAF3F9639","maintainer":"Phsm","location":"UA","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"95.215.44.78","ipv6":"2a02:7aa0:1619::c6fe:d0cb","port":33445,"tcp_ports":[33445,3389],"public_key":"672DBE27B4ADB9D5FB105A6BB648B2F8FDB89B3323486A7A21968316E012023C","maintainer":"HooinKyoma","location":"SE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Thanx to Hooin Kyoma","last_ping":1516822981},{"ipv4":"163.172.136.118","ipv6":"2001:bc8:4400:2100::1c:50f","port":33445,"tcp_ports":[33445,3389],"public_key":"2C289F9F37C20D09DA83565588BF496FAB3764853FA38141817A72E3F18ACA0B","maintainer":"LittleVulpix","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"LittleTox - your friendly neighbourhood tox node!","last_ping":1516822981},{"ipv4":"sorunome.de","ipv6":"sorunome.de","port":33445,"tcp_ports":[3389,33445],"public_key":"02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46","maintainer":"Sorunome","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Keep calm and pony on","last_ping":1516822981},{"ipv4":"37.97.185.116","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"E59A0E71ADA20D35BD1B0957059D7EF7E7792B3D680AE25C6F4DBBA09114D165","maintainer":"Yani","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Yani's node of pleasure and leisure","last_ping":1516822981},{"ipv4":"80.87.193.193","ipv6":"2a01:230:2:6::46a8","port":33445,"tcp_ports":[3389,33445],"public_key":"B38255EE4B054924F6D79A5E6E5889EC94B6ADF6FE9906F97A3D01E3D083223A","maintainer":"linxon","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Tox DHT node by Linxon. Author ToxID: EC774ED05A7E71EEE2EBA939A27CD4FF403D7D79E1E685CFD0394B1770498217C6107E4D3C26","last_ping":1516822981},{"ipv4":"initramfs.io","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25","maintainer":"initramfs","location":"TW","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"initramfs' Tox DHT Node","last_ping":1516822981},{"ipv4":"hibiki.eve.moe","ipv6":"hibiki.eve.moe","port":33445,"tcp_ports":[33445],"public_key":"D3EB45181B343C2C222A5BCF72B760638E15ED87904625AAD351C594EEFAE03E","maintainer":"EveNeko","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd@hibiki.eve.moe","last_ping":1516822981},{"ipv4":"tox.deadteam.org","ipv6":"tox.deadteam.org","port":33445,"tcp_ports":[33445],"public_key":"C7D284129E83877D63591F14B3F658D77FF9BA9BA7293AEB2BDFBFE1A803AF47","maintainer":"DeadTeam","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Vive le TOX","last_ping":1516822981},{"ipv4":"46.229.52.198","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"813C8F4187833EF0655B10F7752141A352248462A567529A38B6BBF73E979307","maintainer":"Stranger","location":"UA","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Freedom to parrots!","last_ping":1516822981},{"ipv4":"node.tox.ngc.network","ipv6":"node.tox.ngc.network","port":33445,"tcp_ports":[3389,33445],"public_key":"A856243058D1DE633379508ADCAFCF944E40E1672FF402750EF712E30C42012A","maintainer":"Nolz","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Unlike Others","last_ping":1516822981},{"ipv4":"149.56.140.5","ipv6":"2607:5300:0201:3100:0000:0000:0000:3ec2","port":33445,"tcp_ports":[3389,33445],"public_key":"7E5668E0EE09E19F320AD47902419331FFEE147BB3606769CFBE921A2A2FD34C","maintainer":"velusip","location":"CA","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Jera","last_ping":1516822981},{"ipv4":"185.14.30.213","ipv6":"2a00:1ca8:a7::e8b","port":443,"tcp_ports":[33445,3389,443],"public_key":"2555763C8C460495B14157D234DD56B86300A2395554BCAE4621AC345B8C1B1B","maintainer":"dvor","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Just another tox node.","last_ping":1516822981},{"ipv4":"tox.natalenko.name","ipv6":"tox.natalenko.name","port":33445,"tcp_ports":[33445],"public_key":"1CB6EBFD9D85448FA70D3CAE1220B76BF6FCE911B46ACDCF88054C190589650B","maintainer":"post-factum","location":"DE","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1516822981},{"ipv4":"136.243.141.187","ipv6":"2a01:4f8:212:2459::a:1337","port":443,"tcp_ports":[33445,3389,443],"public_key":"6EE1FADE9F55CC7938234CC07C864081FC606D8FE7B751EDA217F268F1078A39","maintainer":"CeBe","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"uTox is the future! - maintained by CeBe - contact: tox@cebe.cc - tox: 7F50119368DC8FD3B1ECAF5D18E3F8854F0484CEC5BBF625D420B8E38638733C02486E387AF8","last_ping":1516822981},{"ipv4":"tox.abilinski.com","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"0E9D7FEE2AA4B42A4C18FE81C038E32FFD8D907AAA7896F05AA76C8D31A20065","maintainer":"flobe","location":"CA","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1516822981},{"ipv4":"m.loskiq.it","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"88124F3C18C6CFA8778B7679B7329A333616BD27A4DFB562D476681315CF143D","maintainer":"loskiq","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"https://t.me/loskiq","last_ping":1516822981},{"ipv4":"192.99.232.158","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"7B6CB208C811DEA8782711CE0CAD456AAC0C7B165A0498A1AA7010D2F2EC996C","maintainer":"basiljose","location":"CA","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"tmux.ru","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"7467AFA626D3246343170B309BA5BDC975DF3924FC9D7A5917FBFA9F5CD5CD38","maintainer":"nrn","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"https://t.me/nyoroon","last_ping":1516822981},{"ipv4":"37.48.122.22","ipv6":"2001:1af8:4700:a115:6::b","port":33445,"tcp_ports":[33445,3389],"public_key":"1B5A8AB25FFFB66620A531C4646B47F0F32B74C547B30AF8BD8266CA50A3AB59","maintainer":"Pokemon","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Those who would give up essential Liberty, to purchase a little temporary Safety, deserve neither Liberty nor Safety","last_ping":1516822981},{"ipv4":"tox.novg.net","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"D527E5847F8330D628DAB1814F0A422F6DC9D0A300E6C357634EE2DA88C35463","maintainer":"blind_oracle","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"t0x-node1.weba.ru","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"5A59705F86B9FC0671FDF72ED9BB5E55015FF20B349985543DDD4B0656CA1C63","maintainer":"Amin","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"T0X-Node #1","last_ping":1516822981},{"ipv4":"109.195.99.39","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"EF937F61B4979B60BBF306752D8F32029A2A05CD2615B2E9FBFFEADD8E7D5032","maintainer":"NaCl","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"NaCl node respond","last_ping":1516822981},{"ipv4":"79.140.30.52","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"FFAC871E85B1E1487F87AE7C76726AE0E60318A85F6A1669E04C47EB8DC7C72D","maintainer":"warlomak","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-easy-bootstrap","last_ping":1516822981},{"ipv4":"94.41.167.70","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"E519B2C1098999B60190012C7B53E8C43A73C535721036CD9DEC7CCA06741A7D","maintainer":"warlomak","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-easy-bootstrap","last_ping":1516822981},{"ipv4":"104.223.122.204","ipv6":"-","port":33445,"tcp_ports":[3389],"public_key":"3925752E43BF2F8EB4E12B0E9414311064FF2D76707DC7D5D2CCB43F75081F6B","maintainer":"ru_maniac","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"rmnc_third_node","last_ping":1516822981},{"ipv4":"77.55.211.53","ipv6":"-","port":53,"tcp_ports":[443,33445,3389],"public_key":"B9D109CC820C69A5D97A4A1A15708107C6BA85C13BC6188CC809D374AFF18E63","maintainer":"GDR!","location":"PL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"GDR!'s tox-bootstrapd https://gdr.name/","last_ping":1516822922},{"ipv4":"boseburo.ddns.net","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"AF3FC9FC3D121E82E362B4FA84A53E63F58C11C2BA61D988855289B8CABC9B18","maintainer":"LowEel","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"This is the Bose Buro bootstrap daemon","last_ping":1516822981},{"ipv4":"46.101.197.175","ipv6":"2a03:b0c0:3:d0::ac:5001","port":443,"tcp_ports":[443,33445,3389],"public_key":"CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707","maintainer":"clearmartin","location":"DE","status_udp":false,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1516822981},{"ipv4":"104.233.104.126","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"EDEE8F2E839A57820DE3DA4156D88350E53D4161447068A3457EE8F59F362414","maintainer":"wildermesser","location":"CA","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"195.93.190.6","ipv6":"2a01:d0:ffff:a8a::2","port":33445,"tcp_ports":[],"public_key":"FB4CE0DDEFEED45F26917053E5D24BDDA0FA0A3D83A672A9DA2375928B37023D","maintainer":"strngr","location":"UA","status_udp":false,"status_tcp":false,"version":"2016010100","motd":"tox node at strngr.name","last_ping":1516816803},{"ipv4":"193.124.186.205","ipv6":"2a02:f680:1:1100::542a","port":5228,"tcp_ports":[],"public_key":"9906D65F2A4751068A59D30505C5FC8AE1A95E0843AE9372EAFA3BAB6AC16C2C","maintainer":"Cactus","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"85.21.144.224","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"8F738BBC8FA9394670BCAB146C67A507B9907C8E564E28C2B59BEBB2FF68711B","maintainer":"himura","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"37.187.122.30","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"BEB71F97ED9C99C04B8489BB75579EB4DC6AB6F441B603D63533122F1858B51D","maintainer":"dolohow","location":"FR","status_udp":false,"status_tcp":false,"version":"2016010100","motd":"#stay frosty 8218DB335926393789859EDF2D79AC4CC805ADF73472D08165FEA51555502A58AE84FCE7C3D4","last_ping":1515853621},{"ipv4":"95.215.46.114","ipv6":"2a02:7aa0:1619::bdbd:17b8","port":33445,"tcp_ports":[],"public_key":"5823FB947FF24CF83DDFAC3F3BAA18F96EA2018B16CC08429CB97FA502F40C23","maintainer":"isotoxin","location":"SE","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"tox.dumalogiya.ru","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"2DAE6EB8C16131761A675D7C723F618FBA9D29DD8B4E0A39E7E3E8D7055EF113","maintainer":"mikhailnov","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0}]} \ No newline at end of file diff --git a/toxygen/notifications.py b/toxygen/notifications.py deleted file mode 100644 index 26a29ec..0000000 --- a/toxygen/notifications.py +++ /dev/null @@ -1,71 +0,0 @@ -from PyQt5 import QtCore, QtWidgets -from util import curr_directory -import wave -import pyaudio - - -SOUND_NOTIFICATION = { - 'MESSAGE': 0, - 'FRIEND_CONNECTION_STATUS': 1, - 'FILE_TRANSFER': 2 -} - - -def tray_notification(title, text, tray, window): - """ - Show tray notification and activate window icon - NOTE: different behaviour on different OS - :param title: Name of user who sent message or file - :param text: text of message or file info - :param tray: ref to tray icon - :param window: main window - """ - if QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): - if len(text) > 30: - text = text[:27] + '...' - tray.showMessage(title, text, QtWidgets.QSystemTrayIcon.NoIcon, 3000) - QtWidgets.QApplication.alert(window, 0) - - def message_clicked(): - window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) - window.activateWindow() - tray.messageClicked.connect(message_clicked) - - -class AudioFile: - chunk = 1024 - - def __init__(self, fl): - self.wf = wave.open(fl, 'rb') - self.p = pyaudio.PyAudio() - self.stream = self.p.open( - format=self.p.get_format_from_width(self.wf.getsampwidth()), - channels=self.wf.getnchannels(), - rate=self.wf.getframerate(), - output=True) - - def play(self): - data = self.wf.readframes(self.chunk) - while data: - self.stream.write(data) - data = self.wf.readframes(self.chunk) - - def close(self): - self.stream.close() - self.p.terminate() - - -def sound_notification(t): - """ - Plays sound notification - :param t: type of notification - """ - if t == SOUND_NOTIFICATION['MESSAGE']: - f = curr_directory() + '/sounds/message.wav' - elif t == SOUND_NOTIFICATION['FILE_TRANSFER']: - f = curr_directory() + '/sounds/file.wav' - else: - f = curr_directory() + '/sounds/contact.wav' - a = AudioFile(f) - a.play() - a.close() diff --git a/toxygen/passwordscreen.py b/toxygen/passwordscreen.py deleted file mode 100644 index ca721e5..0000000 --- a/toxygen/passwordscreen.py +++ /dev/null @@ -1,154 +0,0 @@ -from widgets import CenteredWidget, LineEdit -from PyQt5 import QtCore, QtWidgets - - -class PasswordArea(LineEdit): - - def __init__(self, parent): - super(PasswordArea, self).__init__(parent) - self.parent = parent - self.setEchoMode(QtWidgets.QLineEdit.Password) - - def keyPressEvent(self, event): - if event.key() == QtCore.Qt.Key_Return: - self.parent.button_click() - else: - super(PasswordArea, self).keyPressEvent(event) - - -class PasswordScreenBase(CenteredWidget): - - def __init__(self, encrypt): - super(PasswordScreenBase, self).__init__() - self._encrypt = encrypt - self.initUI() - - def initUI(self): - self.resize(360, 170) - self.setMinimumSize(QtCore.QSize(360, 170)) - self.setMaximumSize(QtCore.QSize(360, 170)) - - self.enter_pass = QtWidgets.QLabel(self) - self.enter_pass.setGeometry(QtCore.QRect(30, 10, 300, 30)) - - self.password = PasswordArea(self) - self.password.setGeometry(QtCore.QRect(30, 50, 300, 30)) - - self.button = QtWidgets.QPushButton(self) - self.button.setGeometry(QtCore.QRect(30, 90, 300, 30)) - self.button.setText('OK') - self.button.clicked.connect(self.button_click) - - self.warning = QtWidgets.QLabel(self) - self.warning.setGeometry(QtCore.QRect(30, 130, 300, 30)) - self.warning.setStyleSheet('QLabel { color: #F70D1A; }') - self.warning.setVisible(False) - - self.retranslateUi() - self.center() - QtCore.QMetaObject.connectSlotsByName(self) - - def button_click(self): - pass - - def keyPressEvent(self, event): - if event.key() == QtCore.Qt.Key_Enter: - self.button_click() - else: - super(PasswordScreenBase, self).keyPressEvent(event) - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("pass", "Enter password")) - self.enter_pass.setText(QtWidgets.QApplication.translate("pass", "Password:")) - self.warning.setText(QtWidgets.QApplication.translate("pass", "Incorrect password")) - - -class PasswordScreen(PasswordScreenBase): - - def __init__(self, encrypt, data): - super(PasswordScreen, self).__init__(encrypt) - self._data = data - - def button_click(self): - if self.password.text(): - try: - self._encrypt.set_password(self.password.text()) - new_data = self._encrypt.pass_decrypt(self._data[0]) - except Exception as ex: - self.warning.setVisible(True) - print('Decryption error:', ex) - else: - self._data[0] = new_data - self.close() - - -class UnlockAppScreen(PasswordScreenBase): - - def __init__(self, encrypt, callback): - super(UnlockAppScreen, self).__init__(encrypt) - self._callback = callback - self.setWindowFlags(QtCore.Qt.FramelessWindowHint) - - def button_click(self): - if self.password.text(): - if self._encrypt.is_password(self.password.text()): - self._callback() - self.close() - else: - self.warning.setVisible(True) - print('Wrong password!') - - -class SetProfilePasswordScreen(CenteredWidget): - - def __init__(self, encrypt): - super(SetProfilePasswordScreen, self).__init__() - self._encrypt = encrypt - self.initUI() - self.retranslateUi() - self.center() - - def initUI(self): - self.setMinimumSize(QtCore.QSize(700, 200)) - self.setMaximumSize(QtCore.QSize(700, 200)) - self.password = LineEdit(self) - self.password.setGeometry(QtCore.QRect(40, 10, 300, 30)) - self.password.setEchoMode(QtWidgets.QLineEdit.Password) - self.confirm_password = LineEdit(self) - self.confirm_password.setGeometry(QtCore.QRect(40, 50, 300, 30)) - self.confirm_password.setEchoMode(QtWidgets.QLineEdit.Password) - self.set_password = QtWidgets.QPushButton(self) - self.set_password.setGeometry(QtCore.QRect(40, 100, 300, 30)) - self.set_password.clicked.connect(self.new_password) - self.not_match = QtWidgets.QLabel(self) - self.not_match.setGeometry(QtCore.QRect(350, 50, 300, 30)) - self.not_match.setVisible(False) - self.not_match.setStyleSheet('QLabel { color: #BC1C1C; }') - self.warning = QtWidgets.QLabel(self) - self.warning.setGeometry(QtCore.QRect(40, 160, 500, 30)) - self.warning.setStyleSheet('QLabel { color: #BC1C1C; }') - - def retranslateUi(self): - self.setWindowTitle(QtWidgets.QApplication.translate("PasswordScreen", "Profile password")) - self.password.setPlaceholderText( - QtWidgets.QApplication.translate("PasswordScreen", "Password (at least 8 symbols)")) - self.confirm_password.setPlaceholderText( - QtWidgets.QApplication.translate("PasswordScreen", "Confirm password")) - self.set_password.setText( - QtWidgets.QApplication.translate("PasswordScreen", "Set password")) - self.not_match.setText(QtWidgets.QApplication.translate("PasswordScreen", "Passwords do not match")) - self.warning.setText( - QtWidgets.QApplication.translate("PasswordScreen", "There is no way to recover lost passwords")) - - def new_password(self): - if self.password.text() == self.confirm_password.text(): - if len(self.password.text()) >= 8: - self._encrypt.set_password(self.password.text()) - self.close() - else: - self.not_match.setText( - QtWidgets.QApplication.translate("PasswordScreen", "Password must be at least 8 symbols")) - self.not_match.setVisible(True) - else: - self.not_match.setText(QtWidgets.QApplication.translate("PasswordScreen", "Passwords do not match")) - self.not_match.setVisible(True) diff --git a/toxygen/plugin_support.py b/toxygen/plugin_support.py deleted file mode 100644 index 0ff7421..0000000 --- a/toxygen/plugin_support.py +++ /dev/null @@ -1,176 +0,0 @@ -import util -import profile -import os -import importlib -import inspect -import plugins.plugin_super_class as pl -import toxes -import sys - - -class PluginLoader(util.Singleton): - - def __init__(self, tox, settings): - super().__init__() - self._profile = profile.Profile.get_instance() - self._settings = settings - self._plugins = {} # dict. key - plugin unique short name, value - tuple (plugin instance, is active) - self._tox = tox - self._encr = toxes.ToxES.get_instance() - - def set_tox(self, tox): - """ - New tox instance - """ - self._tox = tox - for value in self._plugins.values(): - value[0].set_tox(tox) - - def load(self): - """ - Load all plugins in plugins folder - """ - path = util.curr_directory() + '/plugins/' - if not os.path.exists(path): - util.log('Plugin dir not found') - return - else: - sys.path.append(path) - files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] - for fl in files: - if fl in ('plugin_super_class.py', '__init__.py') or not fl.endswith('.py'): - continue - name = fl[:-3] # module name without .py - try: - module = importlib.import_module(name) # import plugin - except ImportError: - util.log('Import error in module ' + name) - continue - except Exception as ex: - util.log('Exception in module ' + name + ' Exception: ' + str(ex)) - continue - for elem in dir(module): - obj = getattr(module, elem) - # looking for plugin class in module - if inspect.isclass(obj) and hasattr(obj, 'is_plugin') and obj.is_plugin: - print('Plugin', elem) - try: # create instance of plugin class - inst = obj(self._tox, self._profile, self._settings, self._encr) - autostart = inst.get_short_name() in self._settings['plugins'] - if autostart: - inst.start() - except Exception as ex: - util.log('Exception in module ' + name + ' Exception: ' + str(ex)) - continue - self._plugins[inst.get_short_name()] = [inst, autostart] # (inst, is active) - break - - def callback_lossless(self, friend_number, data): - """ - New incoming custom lossless packet (callback) - """ - l = data[0] - pl.LOSSLESS_FIRST_BYTE - name = ''.join(chr(x) for x in data[1:l + 1]) - if name in self._plugins and self._plugins[name][1]: - self._plugins[name][0].lossless_packet(''.join(chr(x) for x in data[l + 1:]), friend_number) - - def callback_lossy(self, friend_number, data): - """ - New incoming custom lossy packet (callback) - """ - l = data[0] - pl.LOSSY_FIRST_BYTE - name = ''.join(chr(x) for x in data[1:l + 1]) - if name in self._plugins and self._plugins[name][1]: - self._plugins[name][0].lossy_packet(''.join(chr(x) for x in data[l + 1:]), friend_number) - - def friend_online(self, friend_number): - """ - Friend with specified number is online - """ - for elem in self._plugins.values(): - if elem[1]: - elem[0].friend_connected(friend_number) - - def get_plugins_list(self): - """ - Returns list of all plugins - """ - result = [] - for data in self._plugins.values(): - try: - result.append([data[0].get_name(), # plugin full name - data[1], # is enabled - data[0].get_description(), # plugin description - data[0].get_short_name()]) # key - short unique name - except: - continue - return result - - def plugin_window(self, key): - """ - Return window or None for specified plugin - """ - return self._plugins[key][0].get_window() - - def toggle_plugin(self, key): - """ - Enable/disable plugin - :param key: plugin short name - """ - plugin = self._plugins[key] - if plugin[1]: - plugin[0].stop() - else: - plugin[0].start() - plugin[1] = not plugin[1] - if plugin[1]: - self._settings['plugins'].append(key) - else: - self._settings['plugins'].remove(key) - self._settings.save() - - def command(self, text): - """ - New command for plugin - """ - text = text.strip() - name = text.split()[0] - if name in self._plugins and self._plugins[name][1]: - self._plugins[name][0].command(text[len(name) + 1:]) - - def get_menu(self, menu, num): - """ - Return list of items for menu - """ - result = [] - for elem in self._plugins.values(): - if elem[1]: - try: - result.extend(elem[0].get_menu(menu, num)) - except: - continue - return result - - def get_message_menu(self, menu, selected_text): - result = [] - for elem in self._plugins.values(): - if elem[1]: - try: - result.extend(elem[0].get_message_menu(menu, selected_text)) - except: - continue - return result - - def stop(self): - """ - App is closing, stop all plugins - """ - for key in list(self._plugins.keys()): - if self._plugins[key][1]: - self._plugins[key][0].close() - del self._plugins[key] - - def reload(self): - print('Reloading plugins') - self.stop() - self.load() diff --git a/toxygen/profile.py b/toxygen/profile.py deleted file mode 100644 index a0d8cd4..0000000 --- a/toxygen/profile.py +++ /dev/null @@ -1,1466 +0,0 @@ -from list_items import * -from PyQt5 import QtGui, QtWidgets -from friend import * -from settings import * -from toxcore_enums_and_consts import * -from ctypes import * -from util import log, Singleton, curr_directory -from tox_dns import tox_dns -from history import * -from file_transfers import * -import time -import calls -import avwidgets -import plugin_support -import basecontact -import items_factory -import cv2 -import threading -from group_chat import * -import re - - -class Profile(basecontact.BaseContact, Singleton): - """ - Profile of current toxygen user. Contains friends list, tox instance - """ - def __init__(self, tox, screen): - """ - :param tox: tox instance - :param screen: ref to main screen - """ - basecontact.BaseContact.__init__(self, - tox.self_get_name(), - tox.self_get_status_message(), - screen.user_info, - tox.self_get_address()) - Singleton.__init__(self) - self._screen = screen - self._messages = screen.messages - self._tox = tox - self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number) - self._call = calls.AV(tox.AV) # object with data about calls - self._call_widgets = {} # dict of incoming call widgets - self._incoming_calls = set() - self._load_history = True - self._waiting_for_reconnection = False - self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages) - settings = Settings.get_instance() - self._sorting = settings['sorting'] - self._show_avatars = settings['show_avatars'] - self._filter_string = '' - self._friend_item_height = 40 if settings['compact_mode'] else 70 - self._paused_file_transfers = dict(settings['paused_file_transfers']) - # key - file id, value: [path, friend number, is incoming, start position] - screen.online_contacts.setCurrentIndex(int(self._sorting)) - aliases = settings['friends_aliases'] - data = tox.self_get_friend_list() - self._history = History(tox.self_get_public_key()) # connection to db - self._contacts, self._active_friend = [], -1 - for i in data: # creates list of friends - tox_id = tox.friend_get_public_key(i) - try: - alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] - except: - alias = '' - item = self.create_friend_item() - name = alias or tox.friend_get_name(i) or tox_id - status_message = tox.friend_get_status_message(i) - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) - friend = Friend(message_getter, i, name, status_message, item, tox_id) - friend.set_alias(alias) - self._contacts.append(friend) - if len(self._contacts): - self.set_active(0) - self.filtration_and_sorting(self._sorting) - - # ----------------------------------------------------------------------------------------------------------------- - # Edit current user's data - # ----------------------------------------------------------------------------------------------------------------- - - def change_status(self): - """ - Changes status of user (online, away, busy) - """ - if self._status is not None: - self.set_status((self._status + 1) % 3) - - def set_status(self, status): - super(Profile, self).set_status(status) - if status is not None: - self._tox.self_set_status(status) - elif not self._waiting_for_reconnection: - self._waiting_for_reconnection = True - QtCore.QTimer.singleShot(50000, self.reconnect) - - def set_name(self, value): - if self.name == value: - return - tmp = self.name - super(Profile, self).set_name(value.encode('utf-8')) - self._tox.self_set_name(self._name.encode('utf-8')) - message = QtWidgets.QApplication.translate("MainWindow", 'User {} is now known as {}') - message = message.format(tmp, value) - for friend in self._contacts: - friend.append_message(InfoMessage(message, time.time())) - if self._active_friend + 1: - self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() - - def set_status_message(self, value): - super(Profile, self).set_status_message(value) - self._tox.self_set_status_message(self._status_message.encode('utf-8')) - - def new_nospam(self): - """Sets new nospam part of tox id""" - import random - self._tox.self_set_nospam(random.randint(0, 4294967295)) # no spam - uint32 - self._tox_id = self._tox.self_get_address() - return self._tox_id - - # ----------------------------------------------------------------------------------------------------------------- - # Filtration - # ----------------------------------------------------------------------------------------------------------------- - - def filtration_and_sorting(self, sorting=0, filter_str=''): - """ - Filtration of friends list - :param sorting: 0 - no sort, 1 - online only, 2 - online first, 4 - by name - :param filter_str: show contacts which name contains this substring - """ - filter_str = filter_str.lower() - settings = Settings.get_instance() - number = self.get_active_number() - is_friend = self.is_active_a_friend() - if sorting > 1: - if sorting & 2: - self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True) - if sorting & 4: - if not sorting & 2: - self._contacts = sorted(self._contacts, key=lambda x: x.name.lower()) - else: # save results of prev sorting - online_friends = filter(lambda x: x.status is not None, self._contacts) - count = len(list(online_friends)) - part1 = self._contacts[:count] - part2 = self._contacts[count:] - part1 = sorted(part1, key=lambda x: x.name.lower()) - part2 = sorted(part2, key=lambda x: x.name.lower()) - self._contacts = part1 + part2 - else: # sort by number - online_friends = filter(lambda x: x.status is not None, self._contacts) - count = len(list(online_friends)) - part1 = self._contacts[:count] - part2 = self._contacts[count:] - part1 = sorted(part1, key=lambda x: x.number) - part2 = sorted(part2, key=lambda x: x.number) - self._contacts = part1 + part2 - self._screen.friends_list.clear() - for contact in self._contacts: - contact.set_widget(self.create_friend_item()) - for index, friend in enumerate(self._contacts): - friend.visibility = (friend.status is not None or not (sorting & 1)) and (filter_str in friend.name.lower()) - friend.visibility = friend.visibility or friend.messages or friend.actions - if friend.visibility: - self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, self._friend_item_height)) - else: - self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0)) - self._sorting, self._filter_string = sorting, filter_str - settings['sorting'] = self._sorting - settings.save() - self.set_active_by_number_and_type(number, is_friend) - - def update_filtration(self): - """ - Update list of contacts when 1 of friends change connection status - """ - self.filtration_and_sorting(self._sorting, self._filter_string) - - # ----------------------------------------------------------------------------------------------------------------- - # Friend getters - # ----------------------------------------------------------------------------------------------------------------- - - def get_friend_by_number(self, num): - return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0] - - def get_friend(self, num): - if num < 0 or num >= len(self._contacts): - return None - return self._contacts[num] - - def get_curr_friend(self): - return self._contacts[self._active_friend] if self._active_friend + 1 else None - - # ----------------------------------------------------------------------------------------------------------------- - # Work with active friend - # ----------------------------------------------------------------------------------------------------------------- - - def get_active(self): - return self._active_friend - - def set_active(self, value=None): - """ - Change current active friend or update info - :param value: number of new active friend in friend's list or None to update active user's data - """ - if value is None and self._active_friend == -1: # nothing to update - return - if value == -1: # all friends were deleted - self._screen.account_name.setText('') - self._screen.account_status.setText('') - self._screen.account_status.setToolTip('') - self._active_friend = -1 - self._screen.account_avatar.setHidden(True) - self._messages.clear() - self._screen.messageEdit.clear() - return - try: - self.send_typing(False) - self._screen.typing.setVisible(False) - if value is not None: - if self._active_friend + 1 and self._active_friend != value: - try: - self.get_curr_friend().curr_text = self._screen.messageEdit.toPlainText() - except: - pass - friend = self._contacts[value] - friend.remove_invalid_unsent_files() - if self._active_friend != value: - self._screen.messageEdit.setPlainText(friend.curr_text) - self._active_friend = value - friend.reset_messages() - if not Settings.get_instance()['save_history']: - friend.delete_old_messages() - self._messages.clear() - friend.load_corr() - messages = friend.get_corr()[-PAGE_SIZE:] - self._load_history = False - for message in messages: - if message.get_type() <= 1: - data = message.get_data() - self.create_message_item(data[0], - data[2], - data[1], - data[3]) - elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: - if message.get_status() is None: - self.create_unsent_file_item(message) - continue - item = self.create_file_transfer_item(message) - if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer - try: - ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] - ft.set_state_changed_handler(item.update_transfer_state) - ft.signal() - except: - print('Incoming not started transfer - no info found') - elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline - self.create_inline_item(message.get_data()) - elif message.get_type() < 5: # info message - data = message.get_data() - self.create_message_item(data[0], - data[2], - '', - data[3]) - else: - data = message.get_data() - self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3]) - self._messages.scrollToBottom() - self._load_history = True - if value in self._call: - self._screen.active_call() - elif value in self._incoming_calls: - self._screen.incoming_call() - else: - self._screen.call_finished() - else: - friend = self.get_curr_friend() - - self._screen.account_name.setText(friend.name) - self._screen.account_status.setText(friend.status_message) - self._screen.account_status.setToolTip(friend.get_full_status()) - if friend.tox_id is None: - avatar_path = curr_directory() + '/images/group.png' - else: - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if not os.path.isfile(avatar_path): # load default image - avatar_path = curr_directory() + '/images/avatar.png' - os.chdir(os.path.dirname(avatar_path)) - pixmap = QtGui.QPixmap(avatar_path) - self._screen.account_avatar.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation)) - except Exception as ex: # no friend found. ignore - log('Friend value: ' + str(value)) - log('Error in set active: ' + str(ex)) - raise - - def set_active_by_number_and_type(self, number, is_friend): - for i in range(len(self._contacts)): - c = self._contacts[i] - if c.number == number and (type(c) is Friend == is_friend): - self._active_friend = i - break - - active_friend = property(get_active, set_active) - - def get_last_message(self): - if self._active_friend + 1: - return self.get_curr_friend().get_last_message_text() - else: - return '' - - def get_active_number(self): - return self.get_curr_friend().number if self._active_friend + 1 else -1 - - def get_active_name(self): - return self.get_curr_friend().name if self._active_friend + 1 else '' - - def is_active_online(self): - return self._active_friend + 1 and self.get_curr_friend().status is not None - - def new_name(self, number, name): - friend = self.get_friend_by_number(number) - tmp = friend.name - friend.set_name(name) - name = str(name, 'utf-8') - if friend.name == name and tmp != name: - message = QtWidgets.QApplication.translate("MainWindow", 'User {} is now known as {}') - message = message.format(tmp, name) - friend.append_message(InfoMessage(message, time.time())) - friend.actions = True - if number == self.get_active_number(): - self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() - self.set_active(None) - - def update(self): - if self._active_friend + 1: - self.set_active(self._active_friend) - - # ----------------------------------------------------------------------------------------------------------------- - # Friend connection status callbacks - # ----------------------------------------------------------------------------------------------------------------- - - def send_files(self, friend_number): - friend = self.get_friend_by_number(friend_number) - friend.remove_invalid_unsent_files() - files = friend.get_unsent_files() - try: - for fl in files: - data = fl.get_data() - if data[1] is not None: - self.send_inline(data[1], data[0], friend_number, True) - else: - self.send_file(data[0], friend_number, True) - friend.clear_unsent_files() - for key in list(self._paused_file_transfers.keys()): - data = self._paused_file_transfers[key] - if not os.path.exists(data[0]): - del self._paused_file_transfers[key] - elif data[1] == friend_number and not data[2]: - self.send_file(data[0], friend_number, True, key) - del self._paused_file_transfers[key] - if friend_number == self.get_active_number() and self.is_active_a_friend(): - self.update() - except Exception as ex: - print('Exception in file sending: ' + str(ex)) - - def friend_exit(self, friend_number): - """ - Friend with specified number quit - """ - self.get_friend_by_number(friend_number).status = None - self.friend_typing(friend_number, False) - if friend_number in self._call: - self._call.finish_call(friend_number, True) - for friend_num, file_num in list(self._file_transfers.keys()): - if friend_num == friend_number: - ft = self._file_transfers[(friend_num, file_num)] - if type(ft) is SendTransfer: - self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, False, -1] - elif type(ft) is ReceiveTransfer and ft.state != TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']: - self._paused_file_transfers[ft.get_id()] = [ft.get_path(), friend_num, True, ft.total_size()] - self.cancel_transfer(friend_num, file_num, True) - - # ----------------------------------------------------------------------------------------------------------------- - # Typing notifications - # ----------------------------------------------------------------------------------------------------------------- - - def send_typing(self, typing): - """ - Send typing notification to a friend - """ - if Settings.get_instance()['typing_notifications'] and self._active_friend + 1: - try: - friend = self.get_curr_friend() - if friend.status is not None: - self._tox.self_set_typing(friend.number, typing) - except: - pass - - def friend_typing(self, friend_number, typing): - """ - Display incoming typing notification - """ - if friend_number == self.get_active_number() and self.is_active_a_friend(): - self._screen.typing.setVisible(typing) - - # ----------------------------------------------------------------------------------------------------------------- - # Private messages - # ----------------------------------------------------------------------------------------------------------------- - - def receipt(self): - i = 0 - while i < self._messages.count() and not self._messages.itemWidget(self._messages.item(i)).mark_as_sent(): - i += 1 - - def send_messages(self, friend_number): - """ - Send 'offline' messages to friend - """ - friend = self.get_friend_by_number(friend_number) - friend.load_corr() - messages = friend.get_unsent_messages() - try: - for message in messages: - self.split_and_send(friend_number, message.get_data()[-1], message.get_data()[0].encode('utf-8')) - friend.inc_receipts() - except Exception as ex: - log('Sending pending messages failed with ' + str(ex)) - - def split_and_send(self, number, message_type, message): - """ - Message splitting. Message length cannot be > TOX_MAX_MESSAGE_LENGTH - :param number: friend's number - :param message_type: type of message - :param message: message text - """ - while len(message) > TOX_MAX_MESSAGE_LENGTH: - size = TOX_MAX_MESSAGE_LENGTH * 4 // 5 - last_part = message[size:TOX_MAX_MESSAGE_LENGTH] - if b' ' in last_part: - index = last_part.index(b' ') - elif b',' in last_part: - index = last_part.index(b',') - elif b'.' in last_part: - index = last_part.index(b'.') - else: - index = TOX_MAX_MESSAGE_LENGTH - size - 1 - index += size + 1 - self._tox.friend_send_message(number, message_type, message[:index]) - message = message[index:] - self._tox.friend_send_message(number, message_type, message) - - def new_message(self, friend_num, message_type, message): - """ - Current user gets new message - :param friend_num: friend_num of friend who sent message - :param message_type: message type - plain text or action message (/me) - :param message: text of message - """ - if friend_num == self.get_active_number()and self.is_active_a_friend(): # add message to list - t = time.time() - self.create_message_item(message, t, MESSAGE_OWNER['FRIEND'], message_type) - self._messages.scrollToBottom() - self.get_curr_friend().append_message( - TextMessage(message, MESSAGE_OWNER['FRIEND'], t, message_type)) - else: - friend = self.get_friend_by_number(friend_num) - friend.inc_messages() - friend.append_message( - TextMessage(message, MESSAGE_OWNER['FRIEND'], time.time(), message_type)) - if not friend.visibility: - self.update_filtration() - - def send_message(self, text, friend_num=None): - """ - Send message - :param text: message text - :param friend_num: num of friend - """ - if not self.is_active_a_friend(): - self.send_gc_message(text) - return - if friend_num is None: - friend_num = self.get_active_number() - if text.startswith('/plugin '): - plugin_support.PluginLoader.get_instance().command(text[8:]) - self._screen.messageEdit.clear() - elif text and friend_num + 1: - if text.startswith('/me '): - message_type = TOX_MESSAGE_TYPE['ACTION'] - text = text[4:] - else: - message_type = TOX_MESSAGE_TYPE['NORMAL'] - friend = self.get_friend_by_number(friend_num) - friend.inc_receipts() - if friend.status is not None: - self.split_and_send(friend.number, message_type, text.encode('utf-8')) - t = time.time() - if friend.number == self.get_active_number() and self.is_active_a_friend(): - self.create_message_item(text, t, MESSAGE_OWNER['NOT_SENT'], message_type) - self._screen.messageEdit.clear() - self._messages.scrollToBottom() - friend.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], t, message_type)) - - def delete_message(self, time): - friend = self.get_curr_friend() - friend.delete_message(time) - self._history.delete_message(friend.tox_id, time) - self.update() - - # ----------------------------------------------------------------------------------------------------------------- - # History support - # ----------------------------------------------------------------------------------------------------------------- - - def save_history(self): - """ - Save history to db - """ - s = Settings.get_instance() - if hasattr(self, '_history'): - if s['save_history']: - for friend in filter(lambda x: type(x) is Friend, self._contacts): - if not self._history.friend_exists_in_db(friend.tox_id): - self._history.add_friend_to_db(friend.tox_id) - if not s['save_unsent_only']: - messages = friend.get_corr_for_saving() - else: - messages = friend.get_unsent_messages_for_saving() - self._history.delete_messages(friend.tox_id) - self._history.save_messages_to_db(friend.tox_id, messages) - unsent_messages = friend.get_unsent_messages() - unsent_time = unsent_messages[0].get_data()[2] if len(unsent_messages) else time.time() + 1 - self._history.update_messages(friend.tox_id, unsent_time) - self._history.save() - del self._history - - def clear_history(self, num=None, save_unsent=False): - """ - Clear chat history - """ - if num is not None: - friend = self._contacts[num] - friend.clear_corr(save_unsent) - if self._history.friend_exists_in_db(friend.tox_id): - self._history.delete_messages(friend.tox_id) - self._history.delete_friend_from_db(friend.tox_id) - else: # clear all history - for number in range(len(self._contacts)): - self.clear_history(number, save_unsent) - if num is None or num == self.get_active_number(): - self.update() - - def load_history(self): - """ - Tries to load next part of messages - """ - if not self._load_history: - return - self._load_history = False - friend = self.get_curr_friend() - friend.load_corr(False) - data = friend.get_corr() - if not data: - return - data.reverse() - data = data[self._messages.count():self._messages.count() + PAGE_SIZE] - for message in data: - if message.get_type() <= 1: # text message - data = message.get_data() - self.create_message_item(data[0], - data[2], - data[1], - data[3], - False) - elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: # file transfer - if message.get_status() is None: - self.create_unsent_file_item(message) - continue - item = self.create_file_transfer_item(message, False) - if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer - try: - ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] - ft.set_state_changed_handler(item.update_transfer_state) - ft.signal() - except: - print('Incoming not started transfer - no info found') - elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline image - self.create_inline_item(message.get_data(), False) - elif message.get_type() < 5: # info message - data = message.get_data() - self.create_message_item(data[0], - data[2], - '', - data[3], - False) - else: - data = message.get_data() - self.create_gc_message_item(data[0], data[2], data[1], data[4], data[3], False) - self._load_history = True - - def export_db(self, directory): - self._history.export(directory) - - def export_history(self, num, as_text=True, _range=None): - friend = self._contacts[num] - if _range is None: - friend.load_all_corr() - corr = friend.get_corr() - elif _range[1] + 1: - corr = friend.get_corr()[_range[0]:_range[1] + 1] - else: - corr = friend.get_corr()[_range[0]:] - arr = [] - new_line = '\n' if as_text else '
' - for message in corr: - if type(message) is TextMessage: - data = message.get_data() - if as_text: - x = '[{}] {}: {}\n' - else: - x = '[{}] {}: {}
' - arr.append(x.format(convert_time(data[2]) if data[1] != MESSAGE_OWNER['NOT_SENT'] else 'Unsent', - friend.name if data[1] == MESSAGE_OWNER['FRIEND'] else self.name, - data[0])) - s = new_line.join(arr) - if not as_text: - s = '{}{}'.format(friend.name, s) - return s - - # ----------------------------------------------------------------------------------------------------------------- - # Friend, message and file transfer items creation - # ----------------------------------------------------------------------------------------------------------------- - - def create_friend_item(self): - """ - Method-factory - :return: new widget for friend instance - """ - return self._factory.friend_item() - - def create_message_item(self, text, time, owner, message_type, append=True): - if message_type == MESSAGE_TYPE['INFO_MESSAGE']: - name = '' - elif owner == MESSAGE_OWNER['FRIEND']: - name = self.get_active_name() - else: - name = self._name - pixmap = None - if self._show_avatars: - if owner == MESSAGE_OWNER['FRIEND']: - pixmap = self.get_curr_friend().get_pixmap() - else: - pixmap = self.get_pixmap() - return self._factory.message_item(text, time, name, owner != MESSAGE_OWNER['NOT_SENT'], - message_type, append, pixmap) - - def create_gc_message_item(self, text, time, owner, name, message_type, append=True): - pixmap = None - if self._show_avatars: - if owner == MESSAGE_OWNER['FRIEND']: - pixmap = self.get_curr_friend().get_pixmap() - else: - pixmap = self.get_pixmap() - return self._factory.message_item(text, time, name, True, - message_type - 5, append, pixmap) - - def create_file_transfer_item(self, tm, append=True): - data = list(tm.get_data()) - data[3] = self.get_friend_by_number(data[4]).name if data[3] else self._name - return self._factory.file_transfer_item(data, append) - - def create_unsent_file_item(self, message, append=True): - data = message.get_data() - return self._factory.unsent_file_item(os.path.basename(data[0]), - os.path.getsize(data[0]) if data[1] is None else len(data[1]), - self.name, - data[2], - append) - - def create_inline_item(self, data, append=True): - return self._factory.inline_item(data, append) - - # ----------------------------------------------------------------------------------------------------------------- - # Work with friends (remove, block, set alias, get public key) - # ----------------------------------------------------------------------------------------------------------------- - - def set_alias(self, num): - """ - Set new alias for friend - """ - friend = self._contacts[num] - name = friend.name - dialog = QtWidgets.QApplication.translate('MainWindow', - "Enter new alias for friend {} or leave empty to use friend's name:") - dialog = dialog.format(name) - title = QtWidgets.QApplication.translate('MainWindow', - 'Set alias') - text, ok = QtWidgets.QInputDialog.getText(None, - title, - dialog, - QtWidgets.QLineEdit.Normal, - name) - if ok: - settings = Settings.get_instance() - aliases = settings['friends_aliases'] - if text: - friend.name = bytes(text, 'utf-8') - try: - index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) - aliases[index] = (friend.tox_id, text) - except: - aliases.append((friend.tox_id, text)) - friend.set_alias(text) - else: # use default name - friend.name = bytes(self._tox.friend_get_name(friend.number), 'utf-8') - friend.set_alias('') - try: - index = list(map(lambda x: x[0], aliases)).index(friend.tox_id) - del aliases[index] - except: - pass - settings.save() - if num == self.get_active_number() and self.is_active_a_friend(): - self.update() - - def friend_public_key(self, num): - return self._contacts[num].tox_id - - def delete_friend(self, num): - """ - Removes friend from contact list - :param num: number of friend in list - """ - friend = self._contacts[num] - settings = Settings.get_instance() - try: - index = list(map(lambda x: x[0], settings['friends_aliases'])).index(friend.tox_id) - del settings['friends_aliases'][index] - except: - pass - if friend.tox_id in settings['notes']: - del settings['notes'][friend.tox_id] - settings.save() - self.clear_history(num) - if self._history.friend_exists_in_db(friend.tox_id): - self._history.delete_friend_from_db(friend.tox_id) - self._tox.friend_delete(friend.number) - del self._contacts[num] - self._screen.friends_list.takeItem(num) - if num == self._active_friend: # active friend was deleted - if not len(self._contacts): # last friend was deleted - self.set_active(-1) - else: - self.set_active(0) - data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - - def add_friend(self, tox_id): - """ - Adds friend to list - """ - num = self._tox.friend_add_norequest(tox_id) # num - friend number - item = self.create_friend_item() - try: - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) - except Exception as ex: # something is wrong - log('Accept friend request failed! ' + str(ex)) - message_getter = None - friend = Friend(message_getter, num, tox_id, '', item, tox_id) - self._contacts.append(friend) - - def block_user(self, tox_id): - """ - Block user with specified tox id (or public key) - delete from friends list and ignore friend requests - """ - tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] - if tox_id == self.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]: - return - settings = Settings.get_instance() - if tox_id not in settings['blocked']: - settings['blocked'].append(tox_id) - settings.save() - try: - num = self._tox.friend_by_public_key(tox_id) - self.delete_friend(num) - data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - except: # not in friend list - pass - - def unblock_user(self, tox_id, add_to_friend_list): - """ - Unblock user - :param tox_id: tox id of contact - :param add_to_friend_list: add this contact to friend list or not - """ - s = Settings.get_instance() - s['blocked'].remove(tox_id) - s.save() - if add_to_friend_list: - self.add_friend(tox_id) - data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - - # ----------------------------------------------------------------------------------------------------------------- - # Friend requests - # ----------------------------------------------------------------------------------------------------------------- - - def send_friend_request(self, tox_id, message): - """ - Function tries to send request to contact with specified id - :param tox_id: id of new contact or tox dns 4 value - :param message: additional message - :return: True on success else error string - """ - try: - message = message or 'Hello! Add me to your contact list please' - if '@' in tox_id: # value like groupbot@toxme.io - tox_id = tox_dns(tox_id) - if tox_id is None: - raise Exception('TOX DNS lookup failed') - if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key - self.add_friend(tox_id) - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "Friend added")) - text = (QtWidgets.QApplication.translate("MainWindow", 'Friend added without sending friend request')) - msgBox.setText(text) - msgBox.exec_() - else: - result = self._tox.friend_add(tox_id, message.encode('utf-8')) - tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] - item = self.create_friend_item() - if not self._history.friend_exists_in_db(tox_id): - self._history.add_friend_to_db(tox_id) - message_getter = self._history.messages_getter(tox_id) - friend = Friend(message_getter, result, tox_id, '', item, tox_id) - self._contacts.append(friend) - data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - return True - except Exception as ex: # wrong data - log('Friend request failed with ' + str(ex)) - return str(ex) - - def process_friend_request(self, tox_id, message): - """ - Accept or ignore friend request - :param tox_id: tox id of contact - :param message: message - """ - try: - text = QtWidgets.QApplication.translate('MainWindow', 'User {} wants to add you to contact list. Message:\n{}') - info = text.format(tox_id, message) - fr_req = QtWidgets.QApplication.translate('MainWindow', 'Friend request') - reply = QtWidgets.QMessageBox.question(None, fr_req, info, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: # accepted - self.add_friend(tox_id) - data = self._tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - except Exception as ex: # something is wrong - log('Accept friend request failed! ' + str(ex)) - - # ----------------------------------------------------------------------------------------------------------------- - # Reset - # ----------------------------------------------------------------------------------------------------------------- - - def reset(self, restart): - """ - Recreate tox instance - :param restart: method which calls restart and returns new tox instance - """ - for contact in self._contacts: - if type(contact) is Friend: - self.friend_exit(contact.number) - else: - self.leave_gc(contact.number) - self._call.stop() - del self._call - del self._tox - self._tox = restart() - self._call = calls.AV(self._tox.AV) - self.status = None - for friend in self._contacts: - friend.number = self._tox.friend_by_public_key(friend.tox_id) # numbers update - self.update_filtration() - - def reconnect(self): - self._waiting_for_reconnection = False - if self.status is None or all(list(map(lambda x: x.status is None, self._contacts))) and len(self._contacts): - self._waiting_for_reconnection = True - self.reset(self._screen.reset) - QtCore.QTimer.singleShot(50000, self.reconnect) - - def close(self): - for friend in filter(lambda x: type(x) is Friend, self._contacts): - self.friend_exit(friend.number) - for i in range(len(self._contacts)): - del self._contacts[0] - if hasattr(self, '_call'): - self._call.stop() - del self._call - s = Settings.get_instance() - s['paused_file_transfers'] = dict(self._paused_file_transfers) if s['resend_files'] else {} - s.save() - - # ----------------------------------------------------------------------------------------------------------------- - # File transfers support - # ----------------------------------------------------------------------------------------------------------------- - - def incoming_file_transfer(self, friend_number, file_number, size, file_name): - """ - New transfer - :param friend_number: number of friend who sent file - :param file_number: file number - :param size: file size in bytes - :param file_name: file name without path - """ - settings = Settings.get_instance() - friend = self.get_friend_by_number(friend_number) - auto = settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends'] - inline = is_inline(file_name) and settings['allow_inline'] - file_id = self._tox.file_get_file_id(friend_number, file_number) - accepted = True - if file_id in self._paused_file_transfers: - data = self._paused_file_transfers[file_id] - pos = data[-1] if os.path.exists(data[0]) else 0 - if pos >= size: - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) - return - self._tox.file_seek(friend_number, file_number, pos) - self.accept_transfer(None, data[0], friend_number, file_number, size, False, pos) - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - size, - file_name, - friend_number, - file_number) - elif inline and size < 1024 * 1024: - self.accept_transfer(None, '', friend_number, file_number, size, True) - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - size, - file_name, - friend_number, - file_number) - - elif auto: - path = settings['auto_accept_path'] - if not path or not os.path.exists(path): - path = curr_directory() - self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size) - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - size, - file_name, - friend_number, - file_number) - else: - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'], - size, - file_name, - friend_number, - file_number) - accepted = False - if friend_number == self.get_active_number() and self.is_active_a_friend(): - item = self.create_file_transfer_item(tm) - if accepted: - self._file_transfers[(friend_number, file_number)].set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() - else: - friend.actions = True - - friend.append_message(tm) - - def cancel_transfer(self, friend_number, file_number, already_cancelled=False): - """ - Stop transfer - :param friend_number: number of friend - :param file_number: file number - :param already_cancelled: was cancelled by friend - """ - i = self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['CANCELLED']) - if (friend_number, file_number) in self._file_transfers: - tr = self._file_transfers[(friend_number, file_number)] - if not already_cancelled: - tr.cancel() - else: - tr.cancelled() - if (friend_number, file_number) in self._file_transfers: - del tr - del self._file_transfers[(friend_number, file_number)] - else: - if not already_cancelled: - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) - if friend_number == self.get_active_number() and self.is_active_a_friend(): - tmp = self._messages.count() + i - if tmp >= 0: - self._messages.itemWidget( - self._messages.item(tmp)).update_transfer_state(TOX_FILE_TRANSFER_STATE['CANCELLED'], - 0, -1) - - def cancel_not_started_transfer(self, cancel_time): - self.get_curr_friend().delete_one_unsent_file(cancel_time) - self.update() - - def pause_transfer(self, friend_number, file_number, by_friend=False): - """ - Pause transfer with specified data - """ - tr = self._file_transfers[(friend_number, file_number)] - tr.pause(by_friend) - t = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER'] - self.get_friend_by_number(friend_number).update_transfer_data(file_number, t) - - def resume_transfer(self, friend_number, file_number, by_friend=False): - """ - Resume transfer with specified data - """ - self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['RUNNING']) - tr = self._file_transfers[(friend_number, file_number)] - if by_friend: - tr.state = TOX_FILE_TRANSFER_STATE['RUNNING'] - tr.signal() - else: - tr.send_control(TOX_FILE_CONTROL['RESUME']) - - def accept_transfer(self, item, path, friend_number, file_number, size, inline=False, from_position=0): - """ - :param item: transfer item. - :param path: path for saving - :param friend_number: friend number - :param file_number: file number - :param size: file size - :param inline: is inline image - :param from_position: position for start - """ - path, file_name = os.path.split(path) - new_file_name, i = file_name, 1 - if not from_position: - while os.path.isfile(path + '/' + new_file_name): # file with same name already exists - if '.' in file_name: # has extension - d = file_name.rindex('.') - else: # no extension - d = len(file_name) - new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] - i += 1 - path = os.path.join(path, new_file_name) - if not inline: - rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position) - else: - rt = ReceiveToBuffer(self._tox, friend_number, size, file_number) - rt.set_transfer_finished_handler(self.transfer_finished) - self._file_transfers[(friend_number, file_number)] = rt - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) - if item is not None: - rt.set_state_changed_handler(item.update_transfer_state) - self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['RUNNING']) - - def send_screenshot(self, data): - """ - Send screenshot to current active friend - :param data: raw data - png - """ - self.send_inline(data, 'toxygen_inline.png') - self._messages.repaint() - - def send_sticker(self, path): - with open(path, 'rb') as fl: - data = fl.read() - self.send_inline(data, 'sticker.png') - - def send_inline(self, data, file_name, friend_number=None, is_resend=False): - friend_number = friend_number or self.get_active_number() - friend = self.get_friend_by_number(friend_number) - if friend.status is None and not is_resend: - m = UnsentFile(file_name, data, time.time()) - friend.append_message(m) - self.update() - return - elif friend.status is None and is_resend: - raise RuntimeError() - st = SendFromBuffer(self._tox, friend.number, data, file_name) - st.set_transfer_finished_handler(self.transfer_finished) - self._file_transfers[(friend.number, st.get_file_number())] = st - tm = TransferMessage(MESSAGE_OWNER['ME'], - time.time(), - TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], - len(data), - file_name, - friend.number, - st.get_file_number()) - friend.append_message(tm) - if friend_number == self.get_active_number(): - item = self.create_file_transfer_item(tm) - st.set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() - - def send_file(self, path, number=None, is_resend=False, file_id=None): - """ - Send file to current active friend - :param path: file path - :param number: friend_number - :param is_resend: is 'offline' message - :param file_id: file id of transfer - """ - friend_number = self.get_active_number() if number is None else number - friend = self.get_friend_by_number(friend_number) - if friend.status is None and not is_resend: - m = UnsentFile(path, None, time.time()) - friend.append_message(m) - self.update() - return - elif friend.status is None and is_resend: - print('Error in sending') - raise RuntimeError() - st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) - st.set_transfer_finished_handler(self.transfer_finished) - self._file_transfers[(friend_number, st.get_file_number())] = st - tm = TransferMessage(MESSAGE_OWNER['ME'], - time.time(), - TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'], - os.path.getsize(path), - os.path.basename(path), - friend_number, - st.get_file_number()) - if friend_number == self.get_active_number(): - item = self.create_file_transfer_item(tm) - st.set_state_changed_handler(item.update_transfer_state) - self._messages.scrollToBottom() - self._contacts[friend_number].append_message(tm) - - def incoming_chunk(self, friend_number, file_number, position, data): - """ - Incoming chunk - """ - self._file_transfers[(friend_number, file_number)].write_chunk(position, data) - - def outgoing_chunk(self, friend_number, file_number, position, size): - """ - Outgoing chunk - """ - self._file_transfers[(friend_number, file_number)].send_chunk(position, size) - - def transfer_finished(self, friend_number, file_number): - transfer = self._file_transfers[(friend_number, file_number)] - t = type(transfer) - if t is ReceiveAvatar: - self.get_friend_by_number(friend_number).load_avatar() - if friend_number == self.get_active_number() and self.is_active_a_friend(): - self.set_active(None) - elif t is ReceiveToBuffer or (t is SendFromBuffer and Settings.get_instance()['allow_inline']): # inline image - print('inline') - inline = InlineImage(transfer.get_data()) - i = self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['FINISHED'], - inline) - if friend_number == self.get_active_number() and self.is_active_a_friend(): - count = self._messages.count() - if count + i + 1 >= 0: - elem = QtWidgets.QListWidgetItem() - item = InlineImageItem(transfer.get_data(), self._messages.width(), elem) - elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) - self._messages.insertItem(count + i + 1, elem) - self._messages.setItemWidget(elem, item) - self._messages.scrollToBottom() - elif t is not SendAvatar: - self.get_friend_by_number(friend_number).update_transfer_data(file_number, - TOX_FILE_TRANSFER_STATE['FINISHED']) - del self._file_transfers[(friend_number, file_number)] - del transfer - - # ----------------------------------------------------------------------------------------------------------------- - # Avatars support - # ----------------------------------------------------------------------------------------------------------------- - - def send_avatar(self, friend_number): - """ - :param friend_number: number of friend who should get new avatar - """ - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if not os.path.isfile(avatar_path): # reset image - avatar_path = None - sa = SendAvatar(avatar_path, self._tox, friend_number) - self._file_transfers[(friend_number, sa.get_file_number())] = sa - - def incoming_avatar(self, friend_number, file_number, size): - """ - Friend changed avatar - :param friend_number: friend number - :param file_number: file number - :param size: size of avatar or 0 (default avatar) - """ - ra = ReceiveAvatar(self._tox, friend_number, size, file_number) - if ra.state != TOX_FILE_TRANSFER_STATE['CANCELLED']: - self._file_transfers[(friend_number, file_number)] = ra - ra.set_transfer_finished_handler(self.transfer_finished) - else: - self.get_friend_by_number(friend_number).load_avatar() - if self.get_active_number() == friend_number and self.is_active_a_friend(): - self.set_active(None) - - def reset_avatar(self): - super(Profile, self).reset_avatar() - for friend in filter(lambda x: x.status is not None, self._contacts): - self.send_avatar(friend.number) - - def set_avatar(self, data): - super(Profile, self).set_avatar(data) - for friend in filter(lambda x: x.status is not None, self._contacts): - self.send_avatar(friend.number) - - # ----------------------------------------------------------------------------------------------------------------- - # AV support - # ----------------------------------------------------------------------------------------------------------------- - - def get_call(self): - return self._call - - call = property(get_call) - - def call_click(self, audio=True, video=False): - """User clicked audio button in main window""" - num = self.get_active_number() - if not self.is_active_a_friend(): - return - if num not in self._call and self.is_active_online(): # start call - if not Settings.get_instance().audio['enabled']: - return - self._call(num, audio, video) - self._screen.active_call() - if video: - text = QtWidgets.QApplication.translate("incoming_call", "Outgoing video call") - else: - text = QtWidgets.QApplication.translate("incoming_call", "Outgoing audio call") - self.get_curr_friend().append_message(InfoMessage(text, time.time())) - self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() - elif num in self._call: # finish or cancel call if you call with active friend - self.stop_call(num, False) - - def incoming_call(self, audio, video, friend_number): - """ - Incoming call from friend. - """ - if not Settings.get_instance().audio['enabled']: - return - friend = self.get_friend_by_number(friend_number) - if video: - text = QtWidgets.QApplication.translate("incoming_call", "Incoming video call") - else: - text = QtWidgets.QApplication.translate("incoming_call", "Incoming audio call") - friend.append_message(InfoMessage(text, time.time())) - self._incoming_calls.add(friend_number) - if friend_number == self.get_active_number(): - self._screen.incoming_call() - self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() - else: - friend.actions = True - self._call_widgets[friend_number] = avwidgets.IncomingCallWidget(friend_number, text, friend.name) - self._call_widgets[friend_number].set_pixmap(friend.get_pixmap()) - self._call_widgets[friend_number].show() - - def accept_call(self, friend_number, audio, video): - """ - Accept incoming call with audio or video - """ - self._call.accept_call(friend_number, audio, video) - self._screen.active_call() - if friend_number in self._incoming_calls: - self._incoming_calls.remove(friend_number) - del self._call_widgets[friend_number] - - def stop_call(self, friend_number, by_friend): - """ - Stop call with friend - """ - if friend_number in self._incoming_calls: - self._incoming_calls.remove(friend_number) - text = QtWidgets.QApplication.translate("incoming_call", "Call declined") - else: - text = QtWidgets.QApplication.translate("incoming_call", "Call finished") - self._screen.call_finished() - is_video = self._call.is_video_call(friend_number) - self._call.finish_call(friend_number, by_friend) # finish or decline call - if hasattr(self, '_call_widget'): - self._call_widget[friend_number].close() - del self._call_widget[friend_number] - - def destroy_window(): - if is_video: - cv2.destroyWindow(str(friend_number)) - - threading.Timer(2.0, destroy_window).start() - friend = self.get_friend_by_number(friend_number) - friend.append_message(InfoMessage(text, time.time())) - if friend_number == self.get_active_number(): - self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) - self._messages.scrollToBottom() - - # ----------------------------------------------------------------------------------------------------------------- - # GC support - # ----------------------------------------------------------------------------------------------------------------- - - def is_active_a_friend(self): - return type(self.get_curr_friend()) is Friend - - def get_group_by_number(self, number): - groups = filter(lambda x: type(x) is GroupChat and x.number == number, self._contacts) - return list(groups)[0] - - def add_gc(self, number): - if number == -1: - return - widget = self.create_friend_item() - gc = GroupChat('Group chat #' + str(number), '', widget, self._tox, number) - self._contacts.append(gc) - - def create_group_chat(self): - number = self._tox.add_av_groupchat() - self.add_gc(number) - - def leave_gc(self, num): - gc = self._contacts[num] - self._tox.del_groupchat(gc.number) - del self._contacts[num] - self._screen.friends_list.takeItem(num) - if num == self._active_friend: # active friend was deleted - if not len(self._contacts): # last friend was deleted - self.set_active(-1) - else: - self.set_active(0) - - def group_invite(self, friend_number, gc_type, data): - text = QtWidgets.QApplication.translate('MainWindow', 'User {} invites you to group chat. Accept?') - title = QtWidgets.QApplication.translate('MainWindow', 'Group chat invite') - friend = self.get_friend_by_number(friend_number) - reply = QtWidgets.QMessageBox.question(None, title, text.format(friend.name), QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: # accepted - if gc_type == TOX_GROUPCHAT_TYPE['TEXT']: - number = self._tox.join_groupchat(friend_number, data) - else: - number = self._tox.join_av_groupchat(friend_number, data) - self.add_gc(number) - - def new_gc_message(self, group_number, peer_number, message_type, message): - name = self._tox.group_peername(group_number, peer_number) - message_type += 5 - if group_number == self.get_active_number() and not self.is_active_a_friend(): # add message to list - t = time.time() - self.create_gc_message_item(message, t, MESSAGE_OWNER['FRIEND'], name, message_type) - self._messages.scrollToBottom() - self.get_curr_friend().append_message( - GroupChatMessage(message, MESSAGE_OWNER['FRIEND'], t, message_type, name)) - else: - gc = self.get_group_by_number(group_number) - gc.inc_messages() - gc.append_message( - GroupChatMessage(message, MESSAGE_OWNER['FRIEND'], time.time(), message_type, name)) - if not gc.visibility: - self.update_filtration() - - def new_gc_title(self, group_number, title): - gc = self.get_group_by_number(group_number) - gc.new_title(title) - if not self.is_active_a_friend() and self.get_active_number() == group_number: - self.update() - - def update_gc(self, group_number): - count = self._tox.group_number_peers(group_number) - gc = self.get_group_by_number(group_number) - text = QtWidgets.QApplication.translate('MainWindow', '{} users in chat') - gc.status_message = text.format(str(count)).encode('utf-8') - if not self.is_active_a_friend() and self.get_active_number() == group_number: - self.update() - - def send_gc_message(self, text): - group_number = self.get_active_number() - if text.startswith('/me '): - text = text[4:] - self._tox.group_action_send(group_number, text.encode('utf-8')) - else: - self._tox.group_message_send(group_number, text.encode('utf-8')) - self._screen.messageEdit.clear() - - def set_title(self, num): - """ - Set new title for gc - """ - gc = self._contacts[num] - name = gc.name - dialog = QtWidgets.QApplication.translate('MainWindow', - "Enter new title for group {}:") - dialog = dialog.format(name) - title = QtWidgets.QApplication.translate('MainWindow', - 'Set title') - text, ok = QtWidgets.QInputDialog.getText(None, - title, - dialog, - QtWidgets.QLineEdit.Normal, - name) - if ok: - text = text.encode('utf-8') - self._tox.group_set_title(gc.number, text) - self.new_gc_title(gc.number, text) - - def get_group_chats(self): - chats = filter(lambda x: type(x) is GroupChat, self._contacts) - chats = map(lambda c: (c.name, c.number), chats) - return list(chats) - - def invite_friend(self, friend_num, group_number): - friend = self._contacts[friend_num] - self._tox.invite_friend(friend.number, group_number) - - def get_gc_peer_name(self, text): - gc = self.get_curr_friend() - if type(gc) is not GroupChat: - return '\t' - names = gc.get_names() - name = re.split("\s+", text)[-1] - suggested_names = list(filter(lambda x: x.startswith(name), names)) - if not len(suggested_names): - return '\t' - return suggested_names[0][len(name):] + ': ' - - -def tox_factory(data=None, settings=None): - """ - :param data: user data from .tox file. None = no saved data, create new profile - :param settings: current profile settings. None = default settings will be used - :return: new tox instance - """ - if settings is None: - settings = Settings.get_default_settings() - tox_options = Tox.options_new() - # see lines 393-401 - tox_options.contents.ipv6_enabled = settings['ipv6_enabled'] - tox_options.contents.udp_enabled = settings['udp_enabled'] - tox_options.contents.proxy_type = settings['proxy_type'] - tox_options.contents.proxy_host = bytes(settings['proxy_host'], 'UTF-8') - tox_options.contents.proxy_port = settings['proxy_port'] - tox_options.contents.start_port = settings['start_port'] - tox_options.contents.end_port = settings['end_port'] - tox_options.contents.tcp_port = settings['tcp_port'] - if data: # load existing profile - tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['TOX_SAVE'] - tox_options.contents.savedata_data = c_char_p(data) - tox_options.contents.savedata_length = len(data) - else: # create new profile - tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['NONE'] - tox_options.contents.savedata_data = None - tox_options.contents.savedata_length = 0 - return Tox(tox_options) diff --git a/toxygen/screen_sharing.py b/toxygen/screen_sharing.py deleted file mode 100644 index 265658c..0000000 --- a/toxygen/screen_sharing.py +++ /dev/null @@ -1,22 +0,0 @@ -import numpy as np -from PyQt5 import QtWidgets - - -class DesktopGrabber: - - def __init__(self, x, y, width, height): - self._x = x - self._y = y - self._width = width - self._height = height - self._width -= width % 4 - self._height -= height % 4 - self._screen = QtWidgets.QApplication.primaryScreen() - - def read(self): - pixmap = self._screen.grabWindow(0, self._x, self._y, self._width, self._height) - image = pixmap.toImage() - s = image.bits().asstring(self._width * self._height * 4) - arr = np.fromstring(s, dtype=np.uint8).reshape((self._height, self._width, 4)) - - return True, arr diff --git a/toxygen/settings.py b/toxygen/settings.py deleted file mode 100644 index 101f372..0000000 --- a/toxygen/settings.py +++ /dev/null @@ -1,293 +0,0 @@ -from platform import system -import json -import os -from util import Singleton, curr_directory, log, copy, append_slash -import pyaudio -from toxes import ToxES -import smileys - - -class Settings(dict, Singleton): - """ - Settings of current profile + global app settings - """ - - def __init__(self, name): - Singleton.__init__(self) - self.path = ProfileHelper.get_path() + str(name) + '.json' - self.name = name - if os.path.isfile(self.path): - with open(self.path, 'rb') as fl: - data = fl.read() - inst = ToxES.get_instance() - try: - if inst.is_data_encrypted(data): - data = inst.pass_decrypt(data) - info = json.loads(str(data, 'utf-8')) - except Exception as ex: - info = Settings.get_default_settings() - log('Parsing settings error: ' + str(ex)) - super(Settings, self).__init__(info) - self.upgrade() - else: - super(Settings, self).__init__(Settings.get_default_settings()) - self.save() - smileys.SmileyLoader(self) - self.locked = False - self.closing = False - self.unlockScreen = False - p = pyaudio.PyAudio() - input_devices = output_devices = 0 - for i in range(p.get_device_count()): - device = p.get_device_info_by_index(i) - if device["maxInputChannels"]: - input_devices += 1 - if device["maxOutputChannels"]: - output_devices += 1 - self.audio = {'input': p.get_default_input_device_info()['index'] if input_devices else -1, - 'output': p.get_default_output_device_info()['index'] if output_devices else -1, - 'enabled': input_devices and output_devices} - self.video = {'device': -1, 'width': 640, 'height': 480, 'x': 0, 'y': 0} - - @staticmethod - def get_auto_profile(): - p = Settings.get_global_settings_path() - if os.path.isfile(p): - with open(p) as fl: - data = fl.read() - auto = json.loads(data) - if 'path' in auto and 'name' in auto: - path = str(auto['path']) - name = str(auto['name']) - if os.path.isfile(append_slash(path) + name + '.tox'): - return path, name - return '', '' - - @staticmethod - def set_auto_profile(path, name): - p = Settings.get_global_settings_path() - if os.path.isfile(p): - with open(p) as fl: - data = fl.read() - data = json.loads(data) - else: - data = {} - data['path'] = str(path) - data['name'] = str(name) - with open(p, 'w') as fl: - fl.write(json.dumps(data)) - - @staticmethod - def reset_auto_profile(): - p = Settings.get_global_settings_path() - if os.path.isfile(p): - with open(p) as fl: - data = fl.read() - data = json.loads(data) - else: - data = {} - if 'path' in data: - del data['path'] - del data['name'] - with open(p, 'w') as fl: - fl.write(json.dumps(data)) - - @staticmethod - def is_active_profile(path, name): - path = path + name + '.lock' - return os.path.isfile(path) - - @staticmethod - def get_default_settings(): - """ - Default profile settings - """ - return { - 'theme': 'dark', - 'ipv6_enabled': False, - 'udp_enabled': True, - 'proxy_type': 0, - 'proxy_host': '127.0.0.1', - 'proxy_port': 9050, - 'start_port': 0, - 'end_port': 0, - 'tcp_port': 0, - 'notifications': True, - 'sound_notifications': False, - 'language': 'English', - 'save_history': False, - 'allow_inline': True, - 'allow_auto_accept': True, - 'auto_accept_path': None, - 'sorting': 0, - 'auto_accept_from_friends': [], - 'paused_file_transfers': {}, - 'resend_files': True, - 'friends_aliases': [], - 'show_avatars': False, - 'typing_notifications': False, - 'calls_sound': True, - 'blocked': [], - 'plugins': [], - 'notes': {}, - 'smileys': True, - 'smiley_pack': 'default', - 'mirror_mode': False, - 'width': 920, - 'height': 500, - 'x': 400, - 'y': 400, - 'message_font_size': 14, - 'unread_color': 'red', - 'save_unsent_only': False, - 'compact_mode': False, - 'show_welcome_screen': True, - 'close_to_tray': False, - 'font': 'Times New Roman', - 'update': 1, - 'group_notifications': True, - 'download_nodes_list': False - } - - @staticmethod - def supported_languages(): - return { - 'English': 'en_EN', - 'French': 'fr_FR', - 'Russian': 'ru_RU', - 'Ukrainian': 'uk_UA' - } - - @staticmethod - def built_in_themes(): - return { - 'dark': '/styles/dark_style.qss', - 'default': '/styles/style.qss' - } - - def upgrade(self): - default = Settings.get_default_settings() - for key in default: - if key not in self: - print(key) - self[key] = default[key] - self.save() - - def save(self): - text = json.dumps(self) - inst = ToxES.get_instance() - if inst.has_password(): - text = bytes(inst.pass_encrypt(bytes(text, 'utf-8'))) - else: - text = bytes(text, 'utf-8') - with open(self.path, 'wb') as fl: - fl.write(text) - - def close(self): - profile_path = ProfileHelper.get_path() - path = str(profile_path + str(self.name) + '.lock') - if os.path.isfile(path): - os.remove(path) - - def set_active_profile(self): - """ - Mark current profile as active - """ - profile_path = ProfileHelper.get_path() - path = str(profile_path + str(self.name) + '.lock') - with open(path, 'w') as fl: - fl.write('active') - - def export(self, path): - text = json.dumps(self) - with open(path + str(self.name) + '.json', 'w') as fl: - fl.write(text) - - def update_path(self): - self.path = ProfileHelper.get_path() + self.name + '.json' - - @staticmethod - def get_global_settings_path(): - return curr_directory() + '/toxygen.json' - - @staticmethod - def get_default_path(): - if system() == 'Windows': - return os.getenv('APPDATA') + '/Tox/' - elif system() == 'Darwin': - return os.getenv('HOME') + '/Library/Application Support/Tox/' - else: - return os.getenv('HOME') + '/.config/tox/' - - -class ProfileHelper(Singleton): - """ - Class with methods for search, load and save profiles - """ - def __init__(self, path, name): - Singleton.__init__(self) - path = append_slash(path) - self._path = path + name + '.tox' - self._directory = path - # create /avatars if not exists: - directory = path + 'avatars' - if not os.path.exists(directory): - os.makedirs(directory) - - def open_profile(self): - with open(self._path, 'rb') as fl: - data = fl.read() - if data: - return data - else: - raise IOError('Save file has zero size!') - - def get_dir(self): - return self._directory - - def save_profile(self, data): - inst = ToxES.get_instance() - if inst.has_password(): - data = inst.pass_encrypt(data) - with open(self._path, 'wb') as fl: - fl.write(data) - print('Profile saved successfully') - - def export_profile(self, new_path, use_new_path): - path = new_path + os.path.basename(self._path) - with open(self._path, 'rb') as fin: - data = fin.read() - with open(path, 'wb') as fout: - fout.write(data) - print('Profile exported successfully') - copy(self._directory + 'avatars', new_path + 'avatars') - if use_new_path: - self._path = new_path + os.path.basename(self._path) - self._directory = new_path - Settings.get_instance().update_path() - - @staticmethod - def find_profiles(): - """ - Find available tox profiles - """ - path = Settings.get_default_path() - result = [] - # check default path - if not os.path.exists(path): - os.makedirs(path) - for fl in os.listdir(path): - if fl.endswith('.tox'): - name = fl[:-4] - result.append((path, name)) - path = curr_directory() - # check current directory - for fl in os.listdir(path): - if fl.endswith('.tox'): - name = fl[:-4] - result.append((path + '/', name)) - return result - - @staticmethod - def get_path(): - return ProfileHelper.get_instance().get_dir() diff --git a/toxygen/smileys.py b/toxygen/smileys.py deleted file mode 100644 index 52cb603..0000000 --- a/toxygen/smileys.py +++ /dev/null @@ -1,88 +0,0 @@ -import util -import json -import os -from collections import OrderedDict -from PyQt5 import QtCore - - -class SmileyLoader(util.Singleton): - """ - Class which loads smileys packs and insert smileys into messages - """ - - def __init__(self, settings): - super().__init__() - self._settings = settings - self._curr_pack = None # current pack name - self._smileys = {} # smileys dict. key - smiley (str), value - path to image (str) - self._list = [] # smileys list without duplicates - self.load_pack() - - def load_pack(self): - """ - Loads smiley pack - """ - pack_name = self._settings['smiley_pack'] - if self._settings['smileys'] and self._curr_pack != pack_name: - self._curr_pack = pack_name - path = self.get_smileys_path() + 'config.json' - try: - with open(path, encoding='utf8') as fl: - self._smileys = json.loads(fl.read()) - fl.seek(0) - tmp = json.loads(fl.read(), object_pairs_hook=OrderedDict) - print('Smiley pack {} loaded'.format(pack_name)) - keys, values, self._list = [], [], [] - for key, value in tmp.items(): - value = self.get_smileys_path() + value - if value not in values: - keys.append(key) - values.append(value) - self._list = list(zip(keys, values)) - except Exception as ex: - self._smileys = {} - self._list = [] - print('Smiley pack {} was not loaded. Error: {}'.format(pack_name, ex)) - - def get_smileys_path(self): - return util.curr_directory() + '/smileys/' + self._curr_pack + '/' if self._curr_pack is not None else None - - def get_packs_list(self): - d = util.curr_directory() + '/smileys/' - return [x[1] for x in os.walk(d)][0] - - def get_smileys(self): - return list(self._list) - - def add_smileys_to_text(self, text, edit): - """ - Adds smileys to text - :param text: message - :param edit: MessageEdit instance - :return text with smileys - """ - if not self._settings['smileys'] or not len(self._smileys): - return text - arr = text.split(' ') - for i in range(len(arr)): - if arr[i] in self._smileys: - file_name = self._smileys[arr[i]] # image name - arr[i] = ''.format(arr[i], file_name) - if file_name.endswith('.gif'): # animated smiley - edit.addAnimation(QtCore.QUrl(file_name), self.get_smileys_path() + file_name) - return ' '.join(arr) - - -def sticker_loader(): - """ - :return list of stickers - """ - result = [] - d = util.curr_directory() + '/stickers/' - keys = [x[1] for x in os.walk(d)][0] - for key in keys: - path = d + key + '/' - files = filter(lambda f: f.endswith('.png'), os.listdir(path)) - files = map(lambda f: str(path + f), files) - result.extend(files) - return result diff --git a/toxygen/tox.py b/toxygen/tox.py deleted file mode 100644 index ef4e44c..0000000 --- a/toxygen/tox.py +++ /dev/null @@ -1,1601 +0,0 @@ -from ctypes import * -from toxcore_enums_and_consts import * -from toxav import ToxAV -from libtox import LibToxCore - - -class ToxOptions(Structure): - _fields_ = [ - ('ipv6_enabled', c_bool), - ('udp_enabled', c_bool), - ('proxy_type', c_int), - ('proxy_host', c_char_p), - ('proxy_port', c_uint16), - ('start_port', c_uint16), - ('end_port', c_uint16), - ('tcp_port', c_uint16), - ('savedata_type', c_int), - ('savedata_data', c_char_p), - ('savedata_length', c_size_t) - ] - - -def string_to_bin(tox_id): - return c_char_p(bytes.fromhex(tox_id)) if tox_id is not None else None - - -def bin_to_string(raw_id, length): - res = ''.join('{:02x}'.format(ord(raw_id[i])) for i in range(length)) - return res.upper() - - -class Tox: - - libtoxcore = LibToxCore() - - def __init__(self, tox_options=None, tox_pointer=None): - """ - Creates and initialises a new Tox instance with the options passed. - - This function will bring the instance into a valid state. Running the event loop with a new instance will - operate correctly. - - :param tox_options: An options object. If this parameter is None, the default options are used. - :param tox_pointer: Tox instance pointer. If this parameter is not None, tox_options will be ignored. - """ - if tox_pointer is not None: - self._tox_pointer = tox_pointer - else: - tox_err_new = c_int() - Tox.libtoxcore.tox_new.restype = POINTER(c_void_p) - self._tox_pointer = Tox.libtoxcore.tox_new(tox_options, byref(tox_err_new)) - tox_err_new = tox_err_new.value - if tox_err_new == TOX_ERR_NEW['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_new == TOX_ERR_NEW['MALLOC']: - raise MemoryError('The function was unable to allocate enough ' - 'memory to store the internal structures for the Tox object.') - elif tox_err_new == TOX_ERR_NEW['PORT_ALLOC']: - raise RuntimeError('The function was unable to bind to a port. This may mean that all ports have ' - 'already been bound, e.g. by other Tox instances, or it may mean a permission error.' - ' You may be able to gather more information from errno.') - elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_TYPE']: - raise ArgumentError('proxy_type was invalid.') - elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_HOST']: - raise ArgumentError('proxy_type was valid but the proxy_host passed had an invalid format or was NULL.') - elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_PORT']: - raise ArgumentError('proxy_type was valid, but the proxy_port was invalid.') - elif tox_err_new == TOX_ERR_NEW['PROXY_NOT_FOUND']: - raise ArgumentError('The proxy address passed could not be resolved.') - elif tox_err_new == TOX_ERR_NEW['LOAD_ENCRYPTED']: - raise ArgumentError('The byte array to be loaded contained an encrypted save.') - elif tox_err_new == TOX_ERR_NEW['LOAD_BAD_FORMAT']: - raise ArgumentError('The data format was invalid. This can happen when loading data that was saved by' - ' an older version of Tox, or when the data has been corrupted. When loading from' - ' badly formatted data, some data may have been loaded, and the rest is discarded.' - ' Passing an invalid length parameter also causes this error.') - - self.self_connection_status_cb = None - self.friend_name_cb = None - self.friend_status_message_cb = None - self.friend_status_cb = None - self.friend_connection_status_cb = None - self.friend_request_cb = None - self.friend_read_receipt_cb = None - self.friend_typing_cb = None - self.friend_message_cb = None - self.file_recv_control_cb = None - self.file_chunk_request_cb = None - self.file_recv_cb = None - self.file_recv_chunk_cb = None - self.friend_lossy_packet_cb = None - self.friend_lossless_packet_cb = None - self.group_namelist_change_cb = None - self.group_title_cb = None - self.group_action_cb = None - self.group_message_cb = None - self.group_invite_cb = None - - self.AV = ToxAV(self._tox_pointer) - - def __del__(self): - del self.AV - Tox.libtoxcore.tox_kill(self._tox_pointer) - - # ----------------------------------------------------------------------------------------------------------------- - # Startup options - # ----------------------------------------------------------------------------------------------------------------- - - @staticmethod - def options_default(tox_options): - """ - Initialises a Tox_Options object with the default options. - - The result of this function is independent of the original options. All values will be overwritten, no values - will be read (so it is permissible to pass an uninitialised object). - - If options is NULL, this function has no effect. - - :param tox_options: A pointer to options object to be filled with default options. - """ - Tox.libtoxcore.tox_options_default(tox_options) - - @staticmethod - def options_new(): - """ - Allocates a new Tox_Options object and initialises it with the default options. This function can be used to - preserve long term ABI compatibility by giving the responsibility of allocation and deallocation to the Tox - library. - - Objects returned from this function must be freed using the tox_options_free function. - - :return: A pointer to new ToxOptions object with default options or raise MemoryError. - """ - tox_err_options_new = c_int() - f = Tox.libtoxcore.tox_options_new - f.restype = POINTER(ToxOptions) - result = f(byref(tox_err_options_new)) - tox_err_options_new = tox_err_options_new.value - if tox_err_options_new == TOX_ERR_OPTIONS_NEW['OK']: - return result - elif tox_err_options_new == TOX_ERR_OPTIONS_NEW['MALLOC']: - raise MemoryError('The function failed to allocate enough memory for the options struct.') - - @staticmethod - def options_free(tox_options): - """ - Releases all resources associated with an options objects. - - Passing a pointer that was not returned by tox_options_new results in undefined behaviour. - - :param tox_options: A pointer to new ToxOptions object - """ - Tox.libtoxcore.tox_options_free(tox_options) - - # ----------------------------------------------------------------------------------------------------------------- - # Creation and destruction - # ----------------------------------------------------------------------------------------------------------------- - - def get_savedata_size(self): - """ - Calculates the number of bytes required to store the tox instance with tox_get_savedata. - This function cannot fail. The result is always greater than 0. - - :return: number of bytes - """ - return Tox.libtoxcore.tox_get_savedata_size(self._tox_pointer) - - def get_savedata(self, savedata=None): - """ - Store all information associated with the tox instance to a byte array. - - :param savedata: pointer (c_char_p) to a memory region large enough to store the tox instance data. - Call tox_get_savedata_size to find the number of bytes required. If this parameter is None, this function - allocates memory for the tox instance data. - :return: pointer (c_char_p) to a memory region with the tox instance data - """ - if savedata is None: - savedata_size = self.get_savedata_size() - savedata = create_string_buffer(savedata_size) - Tox.libtoxcore.tox_get_savedata(self._tox_pointer, savedata) - return savedata[:] - - # ----------------------------------------------------------------------------------------------------------------- - # Connection lifecycle and event loop - # ----------------------------------------------------------------------------------------------------------------- - - def bootstrap(self, address, port, public_key): - """ - Sends a "get nodes" request to the given bootstrap node with IP, port, and public key to setup connections. - - This function will attempt to connect to the node using UDP. You must use this function even if - Tox_Options.udp_enabled was set to false. - - :param address: The hostname or IP address (IPv4 or IPv6) of the node. - :param port: The port on the host on which the bootstrap Tox instance is listening. - :param public_key: The long term public key of the bootstrap node (TOX_PUBLIC_KEY_SIZE bytes). - :return: True on success. - """ - tox_err_bootstrap = c_int() - result = Tox.libtoxcore.tox_bootstrap(self._tox_pointer, c_char_p(address), c_uint16(port), - string_to_bin(public_key), byref(tox_err_bootstrap)) - tox_err_bootstrap = tox_err_bootstrap.value - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']: - return bool(result) - elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_HOST']: - raise ArgumentError('The address could not be resolved to an IP ' - 'address, or the IP address passed was invalid.') - elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_PORT']: - raise ArgumentError('The port passed was invalid. The valid port range is (1, 65535).') - - def add_tcp_relay(self, address, port, public_key): - """ - Adds additional host:port pair as TCP relay. - - This function can be used to initiate TCP connections to different ports on the same bootstrap node, or to add - TCP relays without using them as bootstrap nodes. - - :param address: The hostname or IP address (IPv4 or IPv6) of the TCP relay. - :param port: The port on the host on which the TCP relay is listening. - :param public_key: The long term public key of the TCP relay (TOX_PUBLIC_KEY_SIZE bytes). - :return: True on success. - """ - tox_err_bootstrap = c_int() - result = Tox.libtoxcore.tox_add_tcp_relay(self._tox_pointer, c_char_p(address), c_uint16(port), - string_to_bin(public_key), byref(tox_err_bootstrap)) - tox_err_bootstrap = tox_err_bootstrap.value - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']: - return bool(result) - elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_HOST']: - raise ArgumentError('The address could not be resolved to an IP ' - 'address, or the IP address passed was invalid.') - elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_PORT']: - raise ArgumentError('The port passed was invalid. The valid port range is (1, 65535).') - - def self_get_connection_status(self): - """ - Return whether we are connected to the DHT. The return value is equal to the last value received through the - `self_connection_status` callback. - - :return: TOX_CONNECTION - """ - return Tox.libtoxcore.tox_self_get_connection_status(self._tox_pointer) - - def callback_self_connection_status(self, callback, user_data): - """ - Set the callback for the `self_connection_status` event. Pass None to unset. - - This event is triggered whenever there is a change in the DHT connection state. When disconnected, a client may - choose to call tox_bootstrap again, to reconnect to the DHT. Note that this state may frequently change for - short amounts of time. Clients should therefore not immediately bootstrap on receiving a disconnect. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - TOX_CONNECTION (c_int), - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_void_p) - self.self_connection_status_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_self_connection_status(self._tox_pointer, - self.self_connection_status_cb, user_data) - - def iteration_interval(self): - """ - Return the time in milliseconds before tox_iterate() should be called again for optimal performance. - :return: time in milliseconds - """ - return Tox.libtoxcore.tox_iteration_interval(self._tox_pointer) - - def iterate(self): - """ - The main loop that needs to be run in intervals of tox_iteration_interval() milliseconds. - """ - Tox.libtoxcore.tox_iterate(self._tox_pointer) - - # ----------------------------------------------------------------------------------------------------------------- - # Internal client information (Tox address/id) - # ----------------------------------------------------------------------------------------------------------------- - - def self_get_address(self, address=None): - """ - Writes the Tox friend address of the client to a byte array. The address is not in human-readable format. If a - client wants to display the address, formatting is required. - - :param address: pointer (c_char_p) to a memory region of at least TOX_ADDRESS_SIZE bytes. If this parameter is - None, this function allocates memory for address. - :return: Tox friend address - """ - if address is None: - address = create_string_buffer(TOX_ADDRESS_SIZE) - Tox.libtoxcore.tox_self_get_address(self._tox_pointer, address) - return bin_to_string(address, TOX_ADDRESS_SIZE) - - def self_set_nospam(self, nospam): - """ - Set the 4-byte nospam part of the address. - - :param nospam: Any 32 bit unsigned integer. - """ - Tox.libtoxcore.tox_self_set_nospam(self._tox_pointer, c_uint32(nospam)) - - def self_get_nospam(self): - """ - Get the 4-byte nospam part of the address. - - :return: nospam part of the address - """ - return Tox.libtoxcore.tox_self_get_nospam(self._tox_pointer) - - def self_get_public_key(self, public_key=None): - """ - Copy the Tox Public Key (long term) from the Tox object. - - :param public_key: A memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this parameter is NULL, this - function allocates memory for Tox Public Key. - :return: Tox Public Key - """ - if public_key is None: - public_key = create_string_buffer(TOX_PUBLIC_KEY_SIZE) - Tox.libtoxcore.tox_self_get_public_key(self._tox_pointer, public_key) - return bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE) - - def self_get_secret_key(self, secret_key=None): - """ - Copy the Tox Secret Key from the Tox object. - - :param secret_key: pointer (c_char_p) to a memory region of at least TOX_SECRET_KEY_SIZE bytes. If this - parameter is NULL, this function allocates memory for Tox Secret Key. - :return: Tox Secret Key - """ - if secret_key is None: - secret_key = create_string_buffer(TOX_SECRET_KEY_SIZE) - Tox.libtoxcore.tox_self_get_secret_key(self._tox_pointer, secret_key) - return bin_to_string(secret_key, TOX_SECRET_KEY_SIZE) - - # ----------------------------------------------------------------------------------------------------------------- - # User-visible client information (nickname/status) - # ----------------------------------------------------------------------------------------------------------------- - - def self_set_name(self, name): - """ - Set the nickname for the Tox client. - - Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length is 0, the name parameter is ignored - (it can be None), and the nickname is set back to empty. - :param name: New nickname. - :return: True on success. - """ - tox_err_set_info = c_int() - result = Tox.libtoxcore.tox_self_set_name(self._tox_pointer, c_char_p(name), - c_size_t(len(name)), byref(tox_err_set_info)) - tox_err_set_info = tox_err_set_info.value - if tox_err_set_info == TOX_ERR_SET_INFO['OK']: - return bool(result) - elif tox_err_set_info == TOX_ERR_SET_INFO['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_set_info == TOX_ERR_SET_INFO['TOO_LONG']: - raise ArgumentError('Information length exceeded maximum permissible size.') - - def self_get_name_size(self): - """ - Return the length of the current nickname as passed to tox_self_set_name. - - If no nickname was set before calling this function, the name is empty, and this function returns 0. - - :return: length of the current nickname - """ - return Tox.libtoxcore.tox_self_get_name_size(self._tox_pointer) - - def self_get_name(self, name=None): - """ - Write the nickname set by tox_self_set_name to a byte array. - - If no nickname was set before calling this function, the name is empty, and this function has no effect. - - Call tox_self_get_name_size to find out how much memory to allocate for the result. - - :param name: pointer (c_char_p) to a memory region location large enough to hold the nickname. If this parameter - is NULL, the function allocates memory for the nickname. - :return: nickname - """ - if name is None: - name = create_string_buffer(self.self_get_name_size()) - Tox.libtoxcore.tox_self_get_name(self._tox_pointer, name) - return str(name.value, 'utf-8') - - def self_set_status_message(self, status_message): - """ - Set the client's status message. - - Status message length cannot exceed TOX_MAX_STATUS_MESSAGE_LENGTH. If length is 0, the status parameter is - ignored, and the user status is set back to empty. - - :param status_message: new status message - :return: True on success. - """ - tox_err_set_info = c_int() - result = Tox.libtoxcore.tox_self_set_status_message(self._tox_pointer, c_char_p(status_message), - c_size_t(len(status_message)), byref(tox_err_set_info)) - tox_err_set_info = tox_err_set_info.value - if tox_err_set_info == TOX_ERR_SET_INFO['OK']: - return bool(result) - elif tox_err_set_info == TOX_ERR_SET_INFO['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_set_info == TOX_ERR_SET_INFO['TOO_LONG']: - raise ArgumentError('Information length exceeded maximum permissible size.') - - def self_get_status_message_size(self): - """ - Return the length of the current status message as passed to tox_self_set_status_message. - - If no status message was set before calling this function, the status is empty, and this function returns 0. - - :return: length of the current status message - """ - return Tox.libtoxcore.tox_self_get_status_message_size(self._tox_pointer) - - def self_get_status_message(self, status_message=None): - """ - Write the status message set by tox_self_set_status_message to a byte array. - - If no status message was set before calling this function, the status is empty, and this function has no effect. - - Call tox_self_get_status_message_size to find out how much memory to allocate for the result. - - :param status_message: pointer (c_char_p) to a valid memory location large enough to hold the status message. - If this parameter is None, the function allocates memory for the status message. - :return: status message - """ - if status_message is None: - status_message = create_string_buffer(self.self_get_status_message_size()) - Tox.libtoxcore.tox_self_get_status_message(self._tox_pointer, status_message) - return str(status_message.value, 'utf-8') - - def self_set_status(self, status): - """ - Set the client's user status. - - :param status: One of the user statuses listed in the enumeration TOX_USER_STATUS. - """ - Tox.libtoxcore.tox_self_set_status(self._tox_pointer, c_int(status)) - - def self_get_status(self): - """ - Returns the client's user status. - - :return: client's user status - """ - return Tox.libtoxcore.tox_self_get_status(self._tox_pointer) - - # ----------------------------------------------------------------------------------------------------------------- - # Friend list management - # ----------------------------------------------------------------------------------------------------------------- - - def friend_add(self, address, message): - """ - Add a friend to the friend list and send a friend request. - - A friend request message must be at least 1 byte long and at most TOX_MAX_FRIEND_REQUEST_LENGTH. - - Friend numbers are unique identifiers used in all functions that operate on friends. Once added, a friend number - is stable for the lifetime of the Tox object. After saving the state and reloading it, the friend numbers may - not be the same as before. Deleting a friend creates a gap in the friend number set, which is filled by the next - adding of a friend. Any pattern in friend numbers should not be relied on. - - If more than INT32_MAX friends are added, this function causes undefined behaviour. - - :param address: The address of the friend (returned by tox_self_get_address of the friend you wish to add) it - must be TOX_ADDRESS_SIZE bytes. - :param message: The message that will be sent along with the friend request. - :return: the friend number on success, UINT32_MAX on failure. - """ - tox_err_friend_add = c_int() - result = Tox.libtoxcore.tox_friend_add(self._tox_pointer, string_to_bin(address), c_char_p(message), - c_size_t(len(message)), byref(tox_err_friend_add)) - tox_err_friend_add = tox_err_friend_add.value - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OK']: - return result - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['TOO_LONG']: - raise ArgumentError('The length of the friend request message exceeded TOX_MAX_FRIEND_REQUEST_LENGTH.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NO_MESSAGE']: - raise ArgumentError('The friend request message was empty. This, and the TOO_LONG code will never be' - ' returned from tox_friend_add_norequest.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['OWN_KEY']: - raise ArgumentError('The friend address belongs to the sending client.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['ALREADY_SENT']: - raise ArgumentError('A friend request has already been sent, or the address belongs to a friend that is' - ' already on the friend list.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['BAD_CHECKSUM']: - raise ArgumentError('The friend address checksum failed.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['SET_NEW_NOSPAM']: - raise ArgumentError('The friend was already there, but the nospam value was different.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['MALLOC']: - raise MemoryError('A memory allocation failed when trying to increase the friend list size.') - - def friend_add_norequest(self, public_key): - """ - Add a friend without sending a friend request. - - This function is used to add a friend in response to a friend request. If the client receives a friend request, - it can be reasonably sure that the other client added this client as a friend, eliminating the need for a friend - request. - - This function is also useful in a situation where both instances are controlled by the same entity, so that this - entity can perform the mutual friend adding. In this case, there is no need for a friend request, either. - - :param public_key: A byte array of length TOX_PUBLIC_KEY_SIZE containing the Public Key (not the Address) of the - friend to add. - :return: the friend number on success, UINT32_MAX on failure. - """ - tox_err_friend_add = c_int() - result = Tox.libtoxcore.tox_friend_add_norequest(self._tox_pointer, string_to_bin(public_key), - byref(tox_err_friend_add)) - tox_err_friend_add = tox_err_friend_add.value - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OK']: - return result - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['TOO_LONG']: - raise ArgumentError('The length of the friend request message exceeded TOX_MAX_FRIEND_REQUEST_LENGTH.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NO_MESSAGE']: - raise ArgumentError('The friend request message was empty. This, and the TOO_LONG code will never be' - ' returned from tox_friend_add_norequest.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['OWN_KEY']: - raise ArgumentError('The friend address belongs to the sending client.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['ALREADY_SENT']: - raise ArgumentError('A friend request has already been sent, or the address belongs to a friend that is' - ' already on the friend list.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['BAD_CHECKSUM']: - raise ArgumentError('The friend address checksum failed.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['SET_NEW_NOSPAM']: - raise ArgumentError('The friend was already there, but the nospam value was different.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['MALLOC']: - raise MemoryError('A memory allocation failed when trying to increase the friend list size.') - - def friend_delete(self, friend_number): - """ - Remove a friend from the friend list. - - This does not notify the friend of their deletion. After calling this function, this client will appear offline - to the friend and no communication can occur between the two. - - :param friend_number: Friend number for the friend to be deleted. - :return: True on success. - """ - tox_err_friend_delete = c_int() - result = Tox.libtoxcore.tox_friend_delete(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_delete)) - tox_err_friend_delete = tox_err_friend_delete.value - if tox_err_friend_delete == TOX_ERR_FRIEND_DELETE['OK']: - return bool(result) - elif tox_err_friend_delete == TOX_ERR_FRIEND_DELETE['FRIEND_NOT_FOUND']: - raise ArgumentError('There was no friend with the given friend number. No friends were deleted.') - - # ----------------------------------------------------------------------------------------------------------------- - # Friend list queries - # ----------------------------------------------------------------------------------------------------------------- - - def friend_by_public_key(self, public_key): - """ - Return the friend number associated with that Public Key. - - :param public_key: A byte array containing the Public Key. - :return: friend number - """ - tox_err_friend_by_public_key = c_int() - result = Tox.libtoxcore.tox_friend_by_public_key(self._tox_pointer, string_to_bin(public_key), - byref(tox_err_friend_by_public_key)) - tox_err_friend_by_public_key = tox_err_friend_by_public_key.value - if tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['OK']: - return result - elif tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['NOT_FOUND']: - raise ArgumentError('No friend with the given Public Key exists on the friend list.') - - def friend_exists(self, friend_number): - """ - Checks if a friend with the given friend number exists and returns true if it does. - """ - return bool(Tox.libtoxcore.tox_friend_exists(self._tox_pointer, c_uint32(friend_number))) - - def self_get_friend_list_size(self): - """ - Return the number of friends on the friend list. - - This function can be used to determine how much memory to allocate for tox_self_get_friend_list. - - :return: number of friends - """ - return Tox.libtoxcore.tox_self_get_friend_list_size(self._tox_pointer) - - def self_get_friend_list(self, friend_list=None): - """ - Copy a list of valid friend numbers into an array. - - Call tox_self_get_friend_list_size to determine the number of elements to allocate. - - :param friend_list: pointer (c_char_p) to a memory region with enough space to hold the friend list. If this - parameter is None, this function allocates memory for the friend list. - :return: friend list - """ - friend_list_size = self.self_get_friend_list_size() - if friend_list is None: - friend_list = create_string_buffer(sizeof(c_uint32) * friend_list_size) - friend_list = POINTER(c_uint32)(friend_list) - Tox.libtoxcore.tox_self_get_friend_list(self._tox_pointer, friend_list) - return friend_list[0:friend_list_size] - - def friend_get_public_key(self, friend_number, public_key=None): - """ - Copies the Public Key associated with a given friend number to a byte array. - - :param friend_number: The friend number you want the Public Key of. - :param public_key: pointer (c_char_p) to a memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this - parameter is None, this function allocates memory for Tox Public Key. - :return: Tox Public Key - """ - if public_key is None: - public_key = create_string_buffer(TOX_PUBLIC_KEY_SIZE) - tox_err_friend_get_public_key = c_int() - Tox.libtoxcore.tox_friend_get_public_key(self._tox_pointer, c_uint32(friend_number), public_key, - byref(tox_err_friend_get_public_key)) - tox_err_friend_get_public_key = tox_err_friend_get_public_key.value - if tox_err_friend_get_public_key == TOX_ERR_FRIEND_GET_PUBLIC_KEY['OK']: - return bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE) - elif tox_err_friend_get_public_key == TOX_ERR_FRIEND_GET_PUBLIC_KEY['FRIEND_NOT_FOUND']: - raise ArgumentError('No friend with the given number exists on the friend list.') - - def friend_get_last_online(self, friend_number): - """ - Return a unix-time timestamp of the last time the friend associated with a given friend number was seen online. - This function will return UINT64_MAX on error. - - :param friend_number: The friend number you want to query. - :return: unix-time timestamp - """ - tox_err_last_online = c_int() - result = Tox.libtoxcore.tox_friend_get_last_online(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_last_online)) - tox_err_last_online = tox_err_last_online.value - if tox_err_last_online == TOX_ERR_FRIEND_GET_LAST_ONLINE['OK']: - return result - elif tox_err_last_online == TOX_ERR_FRIEND_GET_LAST_ONLINE['FRIEND_NOT_FOUND']: - raise ArgumentError('No friend with the given number exists on the friend list.') - - # ----------------------------------------------------------------------------------------------------------------- - # Friend-specific state queries (can also be received through callbacks) - # ----------------------------------------------------------------------------------------------------------------- - - def friend_get_name_size(self, friend_number): - """ - Return the length of the friend's name. If the friend number is invalid, the return value is unspecified. - - The return value is equal to the `length` argument received by the last `friend_name` callback. - """ - tox_err_friend_query = c_int() - result = Tox.libtoxcore.tox_friend_get_name_size(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return result - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def friend_get_name(self, friend_number, name=None): - """ - Write the name of the friend designated by the given friend number to a byte array. - - Call tox_friend_get_name_size to determine the allocation size for the `name` parameter. - - The data written to `name` is equal to the data received by the last `friend_name` callback. - - :param friend_number: number of friend - :param name: pointer (c_char_p) to a valid memory region large enough to store the friend's name. - :return: name of the friend - """ - if name is None: - name = create_string_buffer(self.friend_get_name_size(friend_number)) - tox_err_friend_query = c_int() - Tox.libtoxcore.tox_friend_get_name(self._tox_pointer, c_uint32(friend_number), name, - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return str(name.value, 'utf-8') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def callback_friend_name(self, callback, user_data): - """ - Set the callback for the `friend_name` event. Pass None to unset. - - This event is triggered when a friend changes their name. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend whose name changed, - A byte array (c_char_p) containing the same data as tox_friend_get_name would write to its `name` parameter, - A value (c_size_t) equal to the return value of tox_friend_get_name_size, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) - self.friend_name_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_name(self._tox_pointer, self.friend_name_cb, user_data) - - def friend_get_status_message_size(self, friend_number): - """ - Return the length of the friend's status message. If the friend number is invalid, the return value is SIZE_MAX. - - :return: length of the friend's status message - """ - tox_err_friend_query = c_int() - result = Tox.libtoxcore.tox_friend_get_status_message_size(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return result - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def friend_get_status_message(self, friend_number, status_message=None): - """ - Write the status message of the friend designated by the given friend number to a byte array. - - Call tox_friend_get_status_message_size to determine the allocation size for the `status_name` parameter. - - The data written to `status_message` is equal to the data received by the last `friend_status_message` callback. - - :param friend_number: - :param status_message: pointer (c_char_p) to a valid memory region large enough to store the friend's status - message. - :return: status message of the friend - """ - if status_message is None: - status_message = create_string_buffer(self.friend_get_status_message_size(friend_number)) - tox_err_friend_query = c_int() - Tox.libtoxcore.tox_friend_get_status_message(self._tox_pointer, c_uint32(friend_number), status_message, - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return str(status_message.value, 'utf-8') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def callback_friend_status_message(self, callback, user_data): - """ - Set the callback for the `friend_status_message` event. Pass NULL to unset. - - This event is triggered when a friend changes their status message. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend whose status message changed, - A byte array (c_char_p) containing the same data as tox_friend_get_status_message would write to its - `status_message` parameter, - A value (c_size_t) equal to the return value of tox_friend_get_status_message_size, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) - self.friend_status_message_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_status_message(self._tox_pointer, - self.friend_status_message_cb, c_void_p(user_data)) - - def friend_get_status(self, friend_number): - """ - Return the friend's user status (away/busy/...). If the friend number is invalid, the return value is - unspecified. - - The status returned is equal to the last status received through the `friend_status` callback. - - :return: TOX_USER_STATUS - """ - tox_err_friend_query = c_int() - result = Tox.libtoxcore.tox_friend_get_status(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return result - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def callback_friend_status(self, callback, user_data): - """ - Set the callback for the `friend_status` event. Pass None to unset. - - This event is triggered when a friend changes their user status. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend whose user status changed, - The new user status (TOX_USER_STATUS), - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) - self.friend_status_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_status(self._tox_pointer, self.friend_status_cb, c_void_p(user_data)) - - def friend_get_connection_status(self, friend_number): - """ - Check whether a friend is currently connected to this client. - - The result of this function is equal to the last value received by the `friend_connection_status` callback. - - :param friend_number: The friend number for which to query the connection status. - :return: the friend's connection status (TOX_CONNECTION) as it was received through the - `friend_connection_status` event. - """ - tox_err_friend_query = c_int() - result = Tox.libtoxcore.tox_friend_get_connection_status(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return result - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def callback_friend_connection_status(self, callback, user_data): - """ - Set the callback for the `friend_connection_status` event. Pass NULL to unset. - - This event is triggered when a friend goes offline after having been online, or when a friend goes online. - - This callback is not called when adding friends. It is assumed that when adding friends, their connection status - is initially offline. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend whose connection status changed, - The result of calling tox_friend_get_connection_status (TOX_CONNECTION) on the passed friend_number, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) - self.friend_connection_status_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_connection_status(self._tox_pointer, - self.friend_connection_status_cb, c_void_p(user_data)) - - def friend_get_typing(self, friend_number): - """ - Check whether a friend is currently typing a message. - - :param friend_number: The friend number for which to query the typing status. - :return: true if the friend is typing. - """ - tox_err_friend_query = c_int() - result = Tox.libtoxcore.tox_friend_get_typing(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return bool(result) - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def callback_friend_typing(self, callback, user_data): - """ - Set the callback for the `friend_typing` event. Pass NULL to unset. - - This event is triggered when a friend starts or stops typing. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who started or stopped typing, - The result of calling tox_friend_get_typing (c_bool) on the passed friend_number, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_void_p) - self.friend_typing_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_typing(self._tox_pointer, self.friend_typing_cb, c_void_p(user_data)) - - # ----------------------------------------------------------------------------------------------------------------- - # Sending private messages - # ----------------------------------------------------------------------------------------------------------------- - - def self_set_typing(self, friend_number, typing): - """ - Set the client's typing status for a friend. - - The client is responsible for turning it on or off. - - :param friend_number: The friend to which the client is typing a message. - :param typing: The typing status. True means the client is typing. - :return: True on success. - """ - tox_err_set_typing = c_int() - result = Tox.libtoxcore.tox_self_set_typing(self._tox_pointer, c_uint32(friend_number), - c_bool(typing), byref(tox_err_set_typing)) - tox_err_set_typing = tox_err_set_typing.value - if tox_err_set_typing == TOX_ERR_SET_TYPING['OK']: - return bool(result) - elif tox_err_set_typing == TOX_ERR_SET_TYPING['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - - def friend_send_message(self, friend_number, message_type, message): - """ - Send a text chat message to an online friend. - - This function creates a chat message packet and pushes it into the send queue. - - The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages must be split by the client and sent - as separate messages. Other clients can then reassemble the fragments. Messages may not be empty. - - The return value of this function is the message ID. If a read receipt is received, the triggered - `friend_read_receipt` event will be passed this message ID. - - Message IDs are unique per friend. The first message ID is 0. Message IDs are incremented by 1 each time a - message is sent. If UINT32_MAX messages were sent, the next message ID is 0. - - :param friend_number: The friend number of the friend to send the message to. - :param message_type: Message type (TOX_MESSAGE_TYPE). - :param message: A non-None message text. - :return: message ID - """ - tox_err_friend_send_message = c_int() - result = Tox.libtoxcore.tox_friend_send_message(self._tox_pointer, c_uint32(friend_number), - c_int(message_type), c_char_p(message), c_size_t(len(message)), - byref(tox_err_friend_send_message)) - tox_err_friend_send_message = tox_err_friend_send_message.value - if tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['OK']: - return result - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['SENDQ']: - raise MemoryError('An allocation error occurred while increasing the send queue size.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['TOO_LONG']: - raise ArgumentError('Message length exceeded TOX_MAX_MESSAGE_LENGTH.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['EMPTY']: - raise ArgumentError('Attempted to send a zero-length message.') - - def callback_friend_read_receipt(self, callback, user_data): - """ - Set the callback for the `friend_read_receipt` event. Pass None to unset. - - This event is triggered when the friend receives the message sent with tox_friend_send_message with the - corresponding message ID. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who received the message, - The message ID (c_uint32) as returned from tox_friend_send_message corresponding to the message sent, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) - self.friend_read_receipt_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_read_receipt(self._tox_pointer, - self.friend_read_receipt_cb, c_void_p(user_data)) - - # ----------------------------------------------------------------------------------------------------------------- - # Receiving private messages and friend requests - # ----------------------------------------------------------------------------------------------------------------- - - def callback_friend_request(self, callback, user_data): - """ - Set the callback for the `friend_request` event. Pass None to unset. - - This event is triggered when a friend request is received. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The Public Key (c_uint8 array) of the user who sent the friend request, - The message (c_char_p) they sent along with the request, - The size (c_size_t) of the message byte array, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, POINTER(c_uint8), c_char_p, c_size_t, c_void_p) - self.friend_request_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_request(self._tox_pointer, self.friend_request_cb, c_void_p(user_data)) - - def callback_friend_message(self, callback, user_data): - """ - Set the callback for the `friend_message` event. Pass None to unset. - - This event is triggered when a message from a friend is received. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who sent the message, - Message type (TOX_MESSAGE_TYPE), - The message data (c_char_p) they sent, - The size (c_size_t) of the message byte array. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_char_p, c_size_t, c_void_p) - self.friend_message_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_friend_message(self._tox_pointer, self.friend_message_cb, c_void_p(user_data)) - - # ----------------------------------------------------------------------------------------------------------------- - # File transmission: common between sending and receiving - # ----------------------------------------------------------------------------------------------------------------- - - @staticmethod - def hash(data, hash=None): - """ - Generates a cryptographic hash of the given data. - - This function may be used by clients for any purpose, but is provided primarily for validating cached avatars. - This use is highly recommended to avoid unnecessary avatar updates. - - If hash is NULL or data is NULL while length is not 0 the function returns false, otherwise it returns true. - - This function is a wrapper to internal message-digest functions. - - :param hash: A valid memory location the hash data. It must be at least TOX_HASH_LENGTH bytes in size. - :param data: Data to be hashed or NULL. - :return: true if hash was not NULL. - """ - if hash is None: - hash = create_string_buffer(TOX_HASH_LENGTH) - Tox.libtoxcore.tox_hash(hash, c_char_p(data), len(data)) - return bin_to_string(hash, TOX_HASH_LENGTH) - - def file_control(self, friend_number, file_number, control): - """ - Sends a file control command to a friend for a given file transfer. - - :param friend_number: The friend number of the friend the file is being transferred to or received from. - :param file_number: The friend-specific identifier for the file transfer. - :param control: The control (TOX_FILE_CONTROL) command to send. - :return: True on success. - """ - tox_err_file_control = c_int() - result = Tox.libtoxcore.tox_file_control(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), - c_int(control), byref(tox_err_file_control)) - tox_err_file_control = tox_err_file_control.value - if tox_err_file_control == TOX_ERR_FILE_CONTROL['OK']: - return bool(result) - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_FOUND']: - raise ArgumentError('No file transfer with the given file number was found for the given friend.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_PAUSED']: - raise RuntimeError('A RESUME control was sent, but the file transfer is running normally.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['DENIED']: - raise RuntimeError('A RESUME control was sent, but the file transfer was paused by the other party. Only ' - 'the party that paused the transfer can resume it.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['ALREADY_PAUSED']: - raise RuntimeError('A PAUSE control was sent, but the file transfer was already paused.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['SENDQ']: - raise RuntimeError('Packet queue is full.') - - def callback_file_recv_control(self, callback, user_data): - """ - Set the callback for the `file_recv_control` event. Pass NULL to unset. - - This event is triggered when a file control command is received from a friend. - - :param callback: Python function. - When receiving TOX_FILE_CONTROL_CANCEL, the client should release the resources associated with the file number - and consider the transfer failed. - - Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who is sending the file. - The friend-specific file number (c_uint32) the data received is associated with. - The file control (TOX_FILE_CONTROL) command received. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p) - self.file_recv_control_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_file_recv_control(self._tox_pointer, - self.file_recv_control_cb, user_data) - - def file_seek(self, friend_number, file_number, position): - """ - Sends a file seek control command to a friend for a given file transfer. - - This function can only be called to resume a file transfer right before TOX_FILE_CONTROL_RESUME is sent. - - :param friend_number: The friend number of the friend the file is being received from. - :param file_number: The friend-specific identifier for the file transfer. - :param position: The position that the file should be seeked to. - :return: True on success. - """ - tox_err_file_seek = c_int() - result = Tox.libtoxcore.tox_file_control(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), - c_uint64(position), byref(tox_err_file_seek)) - tox_err_file_seek = tox_err_file_seek.value - if tox_err_file_seek == TOX_ERR_FILE_SEEK['OK']: - return bool(result) - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['NOT_FOUND']: - raise ArgumentError('No file transfer with the given file number was found for the given friend.') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SEEK_DENIED']: - raise IOError('File was not in a state where it could be seeked.') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['INVALID_POSITION']: - raise ArgumentError('Seek position was invalid') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SENDQ']: - raise RuntimeError('Packet queue is full.') - - def file_get_file_id(self, friend_number, file_number, file_id=None): - """ - Copy the file id associated to the file transfer to a byte array. - - :param friend_number: The friend number of the friend the file is being transferred to or received from. - :param file_number: The friend-specific identifier for the file transfer. - :param file_id: A pointer (c_char_p) to memory region of at least TOX_FILE_ID_LENGTH bytes. If this parameter is - None, this function has no effect. - :return: file id. - """ - if file_id is None: - file_id = create_string_buffer(TOX_FILE_ID_LENGTH) - tox_err_file_get = c_int() - Tox.libtoxcore.tox_file_get_file_id(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), file_id, - byref(tox_err_file_get)) - tox_err_file_get = tox_err_file_get.value - if tox_err_file_get == TOX_ERR_FILE_GET['OK']: - return bin_to_string(file_id, TOX_FILE_ID_LENGTH) - elif tox_err_file_get == TOX_ERR_FILE_GET['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_file_get == TOX_ERR_FILE_GET['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif tox_err_file_get == TOX_ERR_FILE_GET['NOT_FOUND']: - raise ArgumentError('No file transfer with the given file number was found for the given friend.') - - # ----------------------------------------------------------------------------------------------------------------- - # File transmission: sending - # ----------------------------------------------------------------------------------------------------------------- - - def file_send(self, friend_number, kind, file_size, file_id, filename): - """ - Send a file transmission request. - - Maximum filename length is TOX_MAX_FILENAME_LENGTH bytes. The filename should generally just be a file name, not - a path with directory names. - - If a non-UINT64_MAX file size is provided, it can be used by both sides to determine the sending progress. File - size can be set to UINT64_MAX for streaming data of unknown size. - - File transmission occurs in chunks, which are requested through the `file_chunk_request` event. - - When a friend goes offline, all file transfers associated with the friend are purged from core. - - If the file contents change during a transfer, the behaviour is unspecified in general. What will actually - happen depends on the mode in which the file was modified and how the client determines the file size. - - - If the file size was increased - - and sending mode was streaming (file_size = UINT64_MAX), the behaviour will be as expected. - - and sending mode was file (file_size != UINT64_MAX), the file_chunk_request callback will receive length = - 0 when Core thinks the file transfer has finished. If the client remembers the file size as it was when - sending the request, it will terminate the transfer normally. If the client re-reads the size, it will think - the friend cancelled the transfer. - - If the file size was decreased - - and sending mode was streaming, the behaviour is as expected. - - and sending mode was file, the callback will return 0 at the new (earlier) end-of-file, signalling to the - friend that the transfer was cancelled. - - If the file contents were modified - - at a position before the current read, the two files (local and remote) will differ after the transfer - terminates. - - at a position after the current read, the file transfer will succeed as expected. - - In either case, both sides will regard the transfer as complete and successful. - - :param friend_number: The friend number of the friend the file send request should be sent to. - :param kind: The meaning of the file to be sent. - :param file_size: Size in bytes of the file the client wants to send, UINT64_MAX if unknown or streaming. - :param file_id: A file identifier of length TOX_FILE_ID_LENGTH that can be used to uniquely identify file - transfers across core restarts. If NULL, a random one will be generated by core. It can then be obtained by - using tox_file_get_file_id(). - :param filename: Name of the file. Does not need to be the actual name. This name will be sent along with the - file send request. - :return: A file number used as an identifier in subsequent callbacks. This number is per friend. File numbers - are reused after a transfer terminates. On failure, this function returns UINT32_MAX. Any pattern in file - numbers should not be relied on. - """ - tox_err_file_send = c_int() - result = self.libtoxcore.tox_file_send(self._tox_pointer, c_uint32(friend_number), c_uint32(kind), - c_uint64(file_size), - string_to_bin(file_id), - c_char_p(filename), - c_size_t(len(filename)), byref(tox_err_file_send)) - tox_err_file_send = tox_err_file_send.value - if tox_err_file_send == TOX_ERR_FILE_SEND['OK']: - return result - elif tox_err_file_send == TOX_ERR_FILE_SEND['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_file_send == TOX_ERR_FILE_SEND['NAME_TOO_LONG']: - raise ArgumentError('Filename length exceeded TOX_MAX_FILENAME_LENGTH bytes.') - elif tox_err_file_send == TOX_ERR_FILE_SEND['TOO_MANY']: - raise RuntimeError('Too many ongoing transfers. The maximum number of concurrent file transfers is 256 per' - 'friend per direction (sending and receiving).') - - def file_send_chunk(self, friend_number, file_number, position, data): - """ - Send a chunk of file data to a friend. - - This function is called in response to the `file_chunk_request` callback. The length parameter should be equal - to the one received though the callback. If it is zero, the transfer is assumed complete. For files with known - size, Core will know that the transfer is complete after the last byte has been received, so it is not necessary - (though not harmful) to send a zero-length chunk to terminate. For streams, core will know that the transfer is - finished if a chunk with length less than the length requested in the callback is sent. - - :param friend_number: The friend number of the receiving friend for this file. - :param file_number: The file transfer identifier returned by tox_file_send. - :param position: The file or stream position from which to continue reading. - :param data: Chunk of file data - :return: true on success. - """ - tox_err_file_send_chunk = c_int() - result = self.libtoxcore.tox_file_send_chunk(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), - c_uint64(position), c_char_p(data), c_size_t(len(data)), - byref(tox_err_file_send_chunk)) - tox_err_file_send_chunk = tox_err_file_send_chunk.value - if tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['OK']: - return bool(result) - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NULL']: - raise ArgumentError('The length parameter was non-zero, but data was NULL.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['FRIEND_NOT_FOUND']: - ArgumentError('The friend_number passed did not designate a valid friend.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NOT_FOUND']: - raise ArgumentError('No file transfer with the given file number was found for the given friend.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NOT_TRANSFERRING']: - raise ArgumentError('File transfer was found but isn\'t in a transferring state: (paused, done, broken, ' - 'etc...) (happens only when not called from the request chunk callback).') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['INVALID_LENGTH']: - raise ArgumentError('Attempted to send more or less data than requested. The requested data size is ' - 'adjusted according to maximum transmission unit and the expected end of the file. ' - 'Trying to send less or more than requested will return this error.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['SENDQ']: - raise RuntimeError('Packet queue is full.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['WRONG_POSITION']: - raise ArgumentError('Position parameter was wrong.') - - def callback_file_chunk_request(self, callback, user_data): - """ - Set the callback for the `file_chunk_request` event. Pass None to unset. - - This event is triggered when Core is ready to send more file data. - - :param callback: Python function. - If the length parameter is 0, the file transfer is finished, and the client's resources associated with the file - number should be released. After a call with zero length, the file number can be reused for future file - transfers. - - If the requested position is not equal to the client's idea of the current file or stream position, it will need - to seek. In case of read-once streams, the client should keep the last read chunk so that a seek back can be - supported. A seek-back only ever needs to read from the last requested chunk. This happens when a chunk was - requested, but the send failed. A seek-back request can occur an arbitrary number of times for any given chunk. - - In response to receiving this callback, the client should call the function `tox_file_send_chunk` with the - requested chunk. If the number of bytes sent through that function is zero, the file transfer is assumed - complete. A client must send the full length of data requested with this callback. - - Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the receiving friend for this file. - The file transfer identifier (c_uint32) returned by tox_file_send. - The file or stream position (c_uint64) from which to continue reading. - The number of bytes (c_size_t) requested for the current chunk. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, c_size_t, c_void_p) - self.file_chunk_request_cb = c_callback(callback) - self.libtoxcore.tox_callback_file_chunk_request(self._tox_pointer, self.file_chunk_request_cb, user_data) - - # ----------------------------------------------------------------------------------------------------------------- - # File transmission: receiving - # ----------------------------------------------------------------------------------------------------------------- - - def callback_file_recv(self, callback, user_data): - """ - Set the callback for the `file_recv` event. Pass None to unset. - - This event is triggered when a file transfer request is received. - - :param callback: Python function. - The client should acquire resources to be associated with the file transfer. Incoming file transfers start in - the PAUSED state. After this callback returns, a transfer can be rejected by sending a TOX_FILE_CONTROL_CANCEL - control command before any other control commands. It can be accepted by sending TOX_FILE_CONTROL_RESUME. - - Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who is sending the file transfer request. - The friend-specific file number (c_uint32) the data received is associated with. - The meaning of the file (c_uint32) to be sent. - Size in bytes (c_uint64) of the file the client wants to send, UINT64_MAX if unknown or streaming. - Name of the file (c_char_p). Does not need to be the actual name. This name will be sent along with the file - send request. - Size in bytes (c_size_t) of the filename. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t, c_void_p) - self.file_recv_cb = c_callback(callback) - self.libtoxcore.tox_callback_file_recv(self._tox_pointer, self.file_recv_cb, user_data) - - def callback_file_recv_chunk(self, callback, user_data): - """ - Set the callback for the `file_recv_chunk` event. Pass NULL to unset. - - This event is first triggered when a file transfer request is received, and subsequently when a chunk of file - data for an accepted request was received. - - :param callback: Python function. - When length is 0, the transfer is finished and the client should release the resources it acquired for the - transfer. After a call with length = 0, the file number can be reused for new file transfers. - - If position is equal to file_size (received in the file_receive callback) when the transfer finishes, the file - was received completely. Otherwise, if file_size was UINT64_MAX, streaming ended successfully when length is 0. - - Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who is sending the file. - The friend-specific file number (c_uint32) the data received is associated with. - The file position (c_uint64) of the first byte in data. - A byte array (c_char_p) containing the received chunk. - The length (c_size_t) of the received chunk. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, POINTER(c_uint8), c_size_t, c_void_p) - self.file_recv_chunk_cb = c_callback(callback) - self.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, self.file_recv_chunk_cb, user_data) - - # ----------------------------------------------------------------------------------------------------------------- - # Low-level custom packet sending and receiving - # ----------------------------------------------------------------------------------------------------------------- - - def friend_send_lossy_packet(self, friend_number, data): - """ - Send a custom lossy packet to a friend. - The first byte of data must be in the range 200-254. Maximum length of a - custom packet is TOX_MAX_CUSTOM_PACKET_SIZE. - - Lossy packets behave like UDP packets, meaning they might never reach the - other side or might arrive more than once (if someone is messing with the - connection) or might arrive in the wrong order. - - Unless latency is an issue, it is recommended that you use lossless custom packets instead. - - :param friend_number: The friend number of the friend this lossy packet - :param data: python string containing the packet data - :return: True on success. - """ - tox_err_friend_custom_packet = c_int() - result = self.libtoxcore.tox_friend_send_lossy_packet(self._tox_pointer, c_uint32(friend_number), - c_char_p(data), c_size_t(len(data)), - byref(tox_err_friend_custom_packet)) - tox_err_friend_custom_packet = tox_err_friend_custom_packet.value - if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']: - return bool(result) - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']: - raise ArgumentError('The first byte of data was not in the specified range for the packet type.' - 'This range is 200-254 for lossy, and 160-191 for lossless packets.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']: - raise ArgumentError('Attempted to send an empty packet.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']: - raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']: - raise RuntimeError('Packet queue is full.') - - def friend_send_lossless_packet(self, friend_number, data): - """ - Send a custom lossless packet to a friend. - The first byte of data must be in the range 160-191. Maximum length of a - custom packet is TOX_MAX_CUSTOM_PACKET_SIZE. - - Lossless packet behaviour is comparable to TCP (reliability, arrive in order) - but with packets instead of a stream. - - :param friend_number: The friend number of the friend this lossless packet - :param data: python string containing the packet data - :return: True on success. - """ - tox_err_friend_custom_packet = c_int() - result = self.libtoxcore.tox_friend_send_lossless_packet(self._tox_pointer, c_uint32(friend_number), - c_char_p(data), c_size_t(len(data)), - byref(tox_err_friend_custom_packet)) - tox_err_friend_custom_packet = tox_err_friend_custom_packet.value - if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']: - return bool(result) - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']: - raise ArgumentError('The first byte of data was not in the specified range for the packet type.' - 'This range is 200-254 for lossy, and 160-191 for lossless packets.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']: - raise ArgumentError('Attempted to send an empty packet.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']: - raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']: - raise RuntimeError('Packet queue is full.') - - def callback_friend_lossy_packet(self, callback, user_data): - """ - Set the callback for the `friend_lossy_packet` event. Pass NULL to unset. - - :param callback: Python function. - Should take pointer (c_void_p) to Tox object, - friend_number (c_uint32) - The friend number of the friend who sent a lossy packet, - A byte array (c_uint8 array) containing the received packet data, - length (c_size_t) - The length of the packet data byte array, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) - self.friend_lossy_packet_cb = c_callback(callback) - self.libtoxcore.tox_callback_friend_lossy_packet(self._tox_pointer, self.friend_lossy_packet_cb, user_data) - - def callback_friend_lossless_packet(self, callback, user_data): - """ - Set the callback for the `friend_lossless_packet` event. Pass NULL to unset. - - :param callback: Python function. - Should take pointer (c_void_p) to Tox object, - friend_number (c_uint32) - The friend number of the friend who sent a lossless packet, - A byte array (c_uint8 array) containing the received packet data, - length (c_size_t) - The length of the packet data byte array, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) - self.friend_lossless_packet_cb = c_callback(callback) - self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, self.friend_lossless_packet_cb, - user_data) - - # ----------------------------------------------------------------------------------------------------------------- - # Low-level network information - # ----------------------------------------------------------------------------------------------------------------- - - def self_get_dht_id(self, dht_id=None): - """ - Writes the temporary DHT public key of this instance to a byte array. - - This can be used in combination with an externally accessible IP address and the bound port (from - tox_self_get_udp_port) to run a temporary bootstrap node. - - Be aware that every time a new instance is created, the DHT public key changes, meaning this cannot be used to - run a permanent bootstrap node. - - :param dht_id: pointer (c_char_p) to a memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this parameter is - None, this function allocates memory for dht_id. - :return: dht_id - """ - if dht_id is None: - dht_id = create_string_buffer(TOX_PUBLIC_KEY_SIZE) - Tox.libtoxcore.tox_self_get_dht_id(self._tox_pointer, dht_id) - return bin_to_string(dht_id, TOX_PUBLIC_KEY_SIZE) - - def self_get_udp_port(self): - """ - Return the UDP port this Tox instance is bound to. - """ - tox_err_get_port = c_int() - result = Tox.libtoxcore.tox_self_get_udp_port(self._tox_pointer, byref(tox_err_get_port)) - tox_err_get_port = tox_err_get_port.value - if tox_err_get_port == TOX_ERR_GET_PORT['OK']: - return result - elif tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']: - raise RuntimeError('The instance was not bound to any port.') - - def self_get_tcp_port(self): - """ - Return the TCP port this Tox instance is bound to. This is only relevant if the instance is acting as a TCP - relay. - """ - tox_err_get_port = c_int() - result = Tox.libtoxcore.tox_self_get_tcp_port(self._tox_pointer, byref(tox_err_get_port)) - tox_err_get_port = tox_err_get_port.value - if tox_err_get_port == TOX_ERR_GET_PORT['OK']: - return result - elif tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']: - raise RuntimeError('The instance was not bound to any port.') - - # ----------------------------------------------------------------------------------------------------------------- - # Group chats - # ----------------------------------------------------------------------------------------------------------------- - - def del_groupchat(self, groupnumber): - result = Tox.libtoxcore.tox_del_groupchat(self._tox_pointer, c_int(groupnumber), None) - return result - - def group_peername(self, groupnumber, peernumber): - buffer = create_string_buffer(TOX_MAX_NAME_LENGTH) - result = Tox.libtoxcore.tox_group_peername(self._tox_pointer, c_int(groupnumber), c_int(peernumber), - buffer, None) - return str(buffer[:result], 'utf-8') - - def invite_friend(self, friendnumber, groupnumber): - result = Tox.libtoxcore.tox_invite_friend(self._tox_pointer, c_int(friendnumber), - c_int(groupnumber), None) - return result - - def join_groupchat(self, friendnumber, data): - result = Tox.libtoxcore.tox_join_groupchat(self._tox_pointer, - c_int(friendnumber), c_char_p(data), c_uint16(len(data)), None) - return result - - def group_message_send(self, groupnumber, message): - result = Tox.libtoxcore.tox_group_message_send(self._tox_pointer, c_int(groupnumber), c_char_p(message), - c_uint16(len(message)), None) - return result - - def group_action_send(self, groupnumber, action): - result = Tox.libtoxcore.tox_group_action_send(self._tox_pointer, - c_int(groupnumber), c_char_p(action), - c_uint16(len(action)), None) - return result - - def group_set_title(self, groupnumber, title): - result = Tox.libtoxcore.tox_group_set_title(self._tox_pointer, c_int(groupnumber), - c_char_p(title), c_uint8(len(title)), None) - return result - - def group_get_title(self, groupnumber): - buffer = create_string_buffer(TOX_MAX_NAME_LENGTH) - result = Tox.libtoxcore.tox_group_get_title(self._tox_pointer, - c_int(groupnumber), buffer, - c_uint32(TOX_MAX_NAME_LENGTH), None) - return str(buffer[:result], 'utf-8') - - def group_number_peers(self, groupnumber): - result = Tox.libtoxcore.tox_group_number_peers(self._tox_pointer, c_int(groupnumber), None) - return result - - def add_av_groupchat(self): - result = self.AV.libtoxav.toxav_add_av_groupchat(self._tox_pointer, None, None) - return result - - def join_av_groupchat(self, friendnumber, data): - result = self.AV.libtoxav.toxav_join_av_groupchat(self._tox_pointer, c_int32(friendnumber), - c_char_p(data), c_uint16(len(data)), - None, None) - return result - - def callback_group_invite(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int32, c_uint8, POINTER(c_uint8), c_uint16, c_void_p) - self.group_invite_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, self.group_invite_cb, user_data) - - def callback_group_message(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_char_p, c_uint16, c_void_p) - self.group_message_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_message(self._tox_pointer, self.group_message_cb, user_data) - - def callback_group_action(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_char_p, c_uint16, c_void_p) - self.group_action_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_action(self._tox_pointer, self.group_action_cb, user_data) - - def callback_group_title(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_char_p, c_uint8, c_void_p) - self.group_title_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_title(self._tox_pointer, self.group_title_cb, user_data) - - def callback_group_namelist_change(self, callback, user_data=None): - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_int, c_uint8, c_void_p) - self.group_namelist_change_cb = c_callback(callback) - Tox.libtoxcore.tox_callback_group_namelist_change(self._tox_pointer, self.group_namelist_change_cb, user_data) diff --git a/toxygen/tox_dns.py b/toxygen/tox_dns.py deleted file mode 100644 index 26b9619..0000000 --- a/toxygen/tox_dns.py +++ /dev/null @@ -1,59 +0,0 @@ -import json -import urllib.request -from util import log -import settings -from PyQt5 import QtNetwork, QtCore - - -def tox_dns(email): - """ - TOX DNS 4 - :param email: data like 'groupbot@toxme.io' - :return: tox id on success else None - """ - site = email.split('@')[1] - data = {"action": 3, "name": "{}".format(email)} - urls = ('https://{}/api'.format(site), 'http://{}/api'.format(site)) - s = settings.Settings.get_instance() - if not s['proxy_type']: # no proxy - for url in urls: - try: - return send_request(url, data) - except Exception as ex: - log('TOX DNS ERROR: ' + str(ex)) - else: # proxy - netman = QtNetwork.QNetworkAccessManager() - proxy = QtNetwork.QNetworkProxy() - proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy if s['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) - proxy.setHostName(s['proxy_host']) - proxy.setPort(s['proxy_port']) - netman.setProxy(proxy) - for url in urls: - try: - request = QtNetwork.QNetworkRequest() - request.setUrl(QtCore.QUrl(url)) - request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, "application/json") - reply = netman.post(request, bytes(json.dumps(data), 'utf-8')) - - while not reply.isFinished(): - QtCore.QThread.msleep(1) - QtCore.QCoreApplication.processEvents() - data = bytes(reply.readAll().data()) - result = json.loads(str(data, 'utf-8')) - if not result['c']: - return result['tox_id'] - except Exception as ex: - log('TOX DNS ERROR: ' + str(ex)) - - return None # error - - -def send_request(url, data): - req = urllib.request.Request(url) - req.add_header('Content-Type', 'application/json') - response = urllib.request.urlopen(req, bytes(json.dumps(data), 'utf-8')) - res = json.loads(str(response.read(), 'utf-8')) - if not res['c']: - return res['tox_id'] - else: - raise LookupError() diff --git a/toxygen/toxav.py b/toxygen/toxav.py deleted file mode 100644 index 0ab891c..0000000 --- a/toxygen/toxav.py +++ /dev/null @@ -1,362 +0,0 @@ -from ctypes import c_int, POINTER, c_void_p, byref, ArgumentError, c_uint32, CFUNCTYPE, c_size_t, c_uint8, c_uint16 -from ctypes import c_char_p, c_int32, c_bool, cast -from libtox import LibToxAV -from toxav_enums import * - - -class ToxAV: - """ - The ToxAV instance type. Each ToxAV instance can be bound to only one Tox instance, and Tox instance can have only - one ToxAV instance. One must make sure to close ToxAV instance prior closing Tox instance otherwise undefined - behaviour occurs. Upon closing of ToxAV instance, all active calls will be forcibly terminated without notifying - peers. - """ - - # ----------------------------------------------------------------------------------------------------------------- - # Creation and destruction - # ----------------------------------------------------------------------------------------------------------------- - - def __init__(self, tox_pointer): - """ - Start new A/V session. There can only be only one session per Tox instance. - - :param tox_pointer: pointer to Tox instance - """ - self.libtoxav = LibToxAV() - toxav_err_new = c_int() - self.libtoxav.toxav_new.restype = POINTER(c_void_p) - self._toxav_pointer = self.libtoxav.toxav_new(tox_pointer, byref(toxav_err_new)) - toxav_err_new = toxav_err_new.value - if toxav_err_new == TOXAV_ERR_NEW['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif toxav_err_new == TOXAV_ERR_NEW['MALLOC']: - raise MemoryError('Memory allocation failure while trying to allocate structures required for the A/V ' - 'session.') - elif toxav_err_new == TOXAV_ERR_NEW['MULTIPLE']: - raise RuntimeError('Attempted to create a second session for the same Tox instance.') - - self.call_state_cb = None - self.audio_receive_frame_cb = None - self.video_receive_frame_cb = None - self.call_cb = None - - def __del__(self): - """ - Releases all resources associated with the A/V session. - - If any calls were ongoing, these will be forcibly terminated without notifying peers. After calling this - function, no other functions may be called and the av pointer becomes invalid. - """ - self.libtoxav.toxav_kill(self._toxav_pointer) - - def get_tox_pointer(self): - """ - Returns the Tox instance the A/V object was created for. - - :return: pointer to the Tox instance - """ - self.libtoxav.toxav_get_tox.restype = POINTER(c_void_p) - return self.libtoxav.toxav_get_tox(self._toxav_pointer) - - # ----------------------------------------------------------------------------------------------------------------- - # A/V event loop - # ----------------------------------------------------------------------------------------------------------------- - - def iteration_interval(self): - """ - Returns the interval in milliseconds when the next toxav_iterate call should be. If no call is active at the - moment, this function returns 200. - - :return: interval in milliseconds - """ - return self.libtoxav.toxav_iteration_interval(self._toxav_pointer) - - def iterate(self): - """ - Main loop for the session. This function needs to be called in intervals of toxav_iteration_interval() - milliseconds. It is best called in the separate thread from tox_iterate. - """ - self.libtoxav.toxav_iterate(self._toxav_pointer) - - # ----------------------------------------------------------------------------------------------------------------- - # Call setup - # ----------------------------------------------------------------------------------------------------------------- - - def call(self, friend_number, audio_bit_rate, video_bit_rate): - """ - Call a friend. This will start ringing the friend. - - It is the client's responsibility to stop ringing after a certain timeout, if such behaviour is desired. If the - client does not stop ringing, the library will not stop until the friend is disconnected. Audio and video - receiving are both enabled by default. - - :param friend_number: The friend number of the friend that should be called. - :param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending. - :param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending. - :return: True on success. - """ - toxav_err_call = c_int() - result = self.libtoxav.toxav_call(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate), - c_uint32(video_bit_rate), byref(toxav_err_call)) - toxav_err_call = toxav_err_call.value - if toxav_err_call == TOXAV_ERR_CALL['OK']: - return bool(result) - elif toxav_err_call == TOXAV_ERR_CALL['MALLOC']: - raise MemoryError('A resource allocation error occurred while trying to create the structures required for ' - 'the call.') - elif toxav_err_call == TOXAV_ERR_CALL['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_CONNECTED']: - raise ArgumentError('The friend was valid, but not currently connected.') - elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_ALREADY_IN_CALL']: - raise ArgumentError('Attempted to call a friend while already in an audio or video call with them.') - elif toxav_err_call == TOXAV_ERR_CALL['INVALID_BIT_RATE']: - raise ArgumentError('Audio or video bit rate is invalid.') - - def callback_call(self, callback, user_data): - """ - Set the callback for the `call` event. Pass None to unset. - - :param callback: The function for the call callback. - - Should take pointer (c_void_p) to ToxAV object, - The friend number (c_uint32) from which the call is incoming. - True (c_bool) if friend is sending audio. - True (c_bool) if friend is sending video. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_bool, c_void_p) - self.call_cb = c_callback(callback) - self.libtoxav.toxav_callback_call(self._toxav_pointer, self.call_cb, user_data) - - def answer(self, friend_number, audio_bit_rate, video_bit_rate): - """ - Accept an incoming call. - - If answering fails for any reason, the call will still be pending and it is possible to try and answer it later. - Audio and video receiving are both enabled by default. - - :param friend_number: The friend number of the friend that is calling. - :param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending. - :param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending. - :return: True on success. - """ - toxav_err_answer = c_int() - result = self.libtoxav.toxav_answer(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate), - c_uint32(video_bit_rate), byref(toxav_err_answer)) - toxav_err_answer = toxav_err_answer.value - if toxav_err_answer == TOXAV_ERR_ANSWER['OK']: - return bool(result) - elif toxav_err_answer == TOXAV_ERR_ANSWER['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_answer == TOXAV_ERR_ANSWER['CODEC_INITIALIZATION']: - raise RuntimeError('Failed to initialize codecs for call session. Note that codec initiation will fail if ' - 'there is no receive callback registered for either audio or video.') - elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_CALLING']: - raise ArgumentError('The friend was valid, but they are not currently trying to initiate a call. This is ' - 'also returned if this client is already in a call with the friend.') - elif toxav_err_answer == TOXAV_ERR_ANSWER['INVALID_BIT_RATE']: - raise ArgumentError('Audio or video bit rate is invalid.') - - # ----------------------------------------------------------------------------------------------------------------- - # Call state graph - # ----------------------------------------------------------------------------------------------------------------- - - def callback_call_state(self, callback, user_data): - """ - Set the callback for the `call_state` event. Pass None to unset. - - :param callback: Python function. - The function for the call_state callback. - - Should take pointer (c_void_p) to ToxAV object, - The friend number (c_uint32) for which the call state changed. - The bitmask of the new call state which is guaranteed to be different than the previous state. The state is set - to 0 when the call is paused. The bitmask represents all the activities currently performed by the friend. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) - self.call_state_cb = c_callback(callback) - self.libtoxav.toxav_callback_call_state(self._toxav_pointer, self.call_state_cb, user_data) - - # ----------------------------------------------------------------------------------------------------------------- - # Call control - # ----------------------------------------------------------------------------------------------------------------- - - def call_control(self, friend_number, control): - """ - Sends a call control command to a friend. - - :param friend_number: The friend number of the friend this client is in a call with. - :param control: The control command to send. - :return: True on success. - """ - toxav_err_call_control = c_int() - result = self.libtoxav.toxav_call_control(self._toxav_pointer, c_uint32(friend_number), c_int(control), - byref(toxav_err_call_control)) - toxav_err_call_control = toxav_err_call_control.value - if toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['OK']: - return bool(result) - elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_IN_CALL']: - raise RuntimeError('This client is currently not in a call with the friend. Before the call is answered, ' - 'only CANCEL is a valid control.') - elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['INVALID_TRANSITION']: - raise RuntimeError('Happens if user tried to pause an already paused call or if trying to resume a call ' - 'that is not paused.') - - # ----------------------------------------------------------------------------------------------------------------- - # TODO Controlling bit rates - # ----------------------------------------------------------------------------------------------------------------- - - # ----------------------------------------------------------------------------------------------------------------- - # A/V sending - # ----------------------------------------------------------------------------------------------------------------- - - def audio_send_frame(self, friend_number, pcm, sample_count, channels, sampling_rate): - """ - Send an audio frame to a friend. - - The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]... - Meaning: sample 1 for channel 1, sample 1 for channel 2, ... - For mono audio, this has no meaning, every sample is subsequent. For stereo, this means the expected format is - LRLRLR... with samples for left and right alternating. - - :param friend_number: The friend number of the friend to which to send an audio frame. - :param pcm: An array of audio samples. The size of this array must be sample_count * channels. - :param sample_count: Number of samples in this frame. Valid numbers here are - ((sample rate) * (audio length) / 1000), where audio length can be 2.5, 5, 10, 20, 40 or 60 milliseconds. - :param channels: Number of audio channels. Sulpported values are 1 and 2. - :param sampling_rate: Audio sampling rate used in this frame. Valid sampling rates are 8000, 12000, 16000, - 24000, or 48000. - """ - toxav_err_send_frame = c_int() - result = self.libtoxav.toxav_audio_send_frame(self._toxav_pointer, c_uint32(friend_number), - cast(pcm, c_void_p), - c_size_t(sample_count), c_uint8(channels), - c_uint32(sampling_rate), byref(toxav_err_send_frame)) - toxav_err_send_frame = toxav_err_send_frame.value - if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']: - return bool(result) - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']: - raise ArgumentError('The samples data pointer was NULL.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']: - raise RuntimeError('This client is currently not in a call with the friend.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']: - raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too ' - 'large, or the audio sampling rate may be unsupported.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']: - raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said' - 'payload.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']: - RuntimeError('Failed to push frame through rtp interface.') - - def video_send_frame(self, friend_number, width, height, y, u, v): - """ - Send a video frame to a friend. - - Y - plane should be of size: height * width - U - plane should be of size: (height/2) * (width/2) - V - plane should be of size: (height/2) * (width/2) - - :param friend_number: The friend number of the friend to which to send a video frame. - :param width: Width of the frame in pixels. - :param height: Height of the frame in pixels. - :param y: Y (Luminance) plane data. - :param u: U (Chroma) plane data. - :param v: V (Chroma) plane data. - """ - toxav_err_send_frame = c_int() - result = self.libtoxav.toxav_video_send_frame(self._toxav_pointer, c_uint32(friend_number), c_uint16(width), - c_uint16(height), c_char_p(y), c_char_p(u), c_char_p(v), - byref(toxav_err_send_frame)) - toxav_err_send_frame = toxav_err_send_frame.value - if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']: - return bool(result) - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']: - raise ArgumentError('One of Y, U, or V was NULL.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']: - raise RuntimeError('This client is currently not in a call with the friend.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']: - raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too ' - 'large, or the audio sampling rate may be unsupported.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']: - raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said' - 'payload.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']: - RuntimeError('Failed to push frame through rtp interface.') - - # ----------------------------------------------------------------------------------------------------------------- - # A/V receiving - # ----------------------------------------------------------------------------------------------------------------- - - def callback_audio_receive_frame(self, callback, user_data): - """ - Set the callback for the `audio_receive_frame` event. Pass None to unset. - - :param callback: Python function. - Function for the audio_receive_frame callback. The callback can be called multiple times per single - iteration depending on the amount of queued frames in the buffer. The received format is the same as in send - function. - - Should take pointer (c_void_p) to ToxAV object, - The friend number (c_uint32) of the friend who sent an audio frame. - An array (c_uint8) of audio samples (sample_count * channels elements). - The number (c_size_t) of audio samples per channel in the PCM array. - Number (c_uint8) of audio channels. - Sampling rate (c_uint32) used in this frame. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_uint8, c_uint32, c_void_p) - self.audio_receive_frame_cb = c_callback(callback) - self.libtoxav.toxav_callback_audio_receive_frame(self._toxav_pointer, self.audio_receive_frame_cb, user_data) - - def callback_video_receive_frame(self, callback, user_data): - """ - Set the callback for the `video_receive_frame` event. Pass None to unset. - - :param callback: Python function. - The function type for the video_receive_frame callback. - - Should take - toxAV pointer (c_void_p) to ToxAV object, - friend_number The friend number (c_uint32) of the friend who sent a video frame. - width Width (c_uint16) of the frame in pixels. - height Height (c_uint16) of the frame in pixels. - y - u - v Plane data (POINTER(c_uint8)). - The size of plane data is derived from width and height where - Y = MAX(width, abs(ystride)) * height, - U = MAX(width/2, abs(ustride)) * (height/2) and - V = MAX(width/2, abs(vstride)) * (height/2). - ystride - ustride - vstride Strides data (c_int32). Strides represent padding for each plane that may or may not be present. You must - handle strides in your image processing code. Strides are negative if the image is bottom-up - hence why you MUST abs() it when calculating plane buffer size. - user_data pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint16, c_uint16, POINTER(c_uint8), POINTER(c_uint8), - POINTER(c_uint8), c_int32, c_int32, c_int32, c_void_p) - self.video_receive_frame_cb = c_callback(callback) - self.libtoxav.toxav_callback_video_receive_frame(self._toxav_pointer, self.video_receive_frame_cb, user_data) diff --git a/toxygen/toxav_enums.py b/toxygen/toxav_enums.py deleted file mode 100644 index 3f3977a..0000000 --- a/toxygen/toxav_enums.py +++ /dev/null @@ -1,131 +0,0 @@ -TOXAV_ERR_NEW = { - # The function returned successfully. - 'OK': 0, - # One of the arguments to the function was NULL when it was not expected. - 'NULL': 1, - # Memory allocation failure while trying to allocate structures required for the A/V session. - 'MALLOC': 2, - # Attempted to create a second session for the same Tox instance. - 'MULTIPLE': 3, -} - -TOXAV_ERR_CALL = { - # The function returned successfully. - 'OK': 0, - # A resource allocation error occurred while trying to create the structures required for the call. - 'MALLOC': 1, - # Synchronization error occurred. - 'SYNC': 2, - # The friend number did not designate a valid friend. - 'FRIEND_NOT_FOUND': 3, - # The friend was valid, but not currently connected. - 'FRIEND_NOT_CONNECTED': 4, - # Attempted to call a friend while already in an audio or video call with them. - 'FRIEND_ALREADY_IN_CALL': 5, - # Audio or video bit rate is invalid. - 'INVALID_BIT_RATE': 6, -} - -TOXAV_ERR_ANSWER = { - # The function returned successfully. - 'OK': 0, - # Synchronization error occurred. - 'SYNC': 1, - # Failed to initialize codecs for call session. Note that codec initiation will fail if there is no receive callback - # registered for either audio or video. - 'CODEC_INITIALIZATION': 2, - # The friend number did not designate a valid friend. - 'FRIEND_NOT_FOUND': 3, - # The friend was valid, but they are not currently trying to initiate a call. This is also returned if this client - # is already in a call with the friend. - 'FRIEND_NOT_CALLING': 4, - # Audio or video bit rate is invalid. - 'INVALID_BIT_RATE': 5, -} - -TOXAV_FRIEND_CALL_STATE = { - # Set by the AV core if an error occurred on the remote end or if friend timed out. This is the final state after - # which no more state transitions can occur for the call. This call state will never be triggered in combination - # with other call states. - 'ERROR': 1, - # The call has finished. This is the final state after which no more state transitions can occur for the call. This - # call state will never be triggered in combination with other call states. - 'FINISHED': 2, - # The flag that marks that friend is sending audio. - 'SENDING_A': 4, - # The flag that marks that friend is sending video. - 'SENDING_V': 8, - # The flag that marks that friend is receiving audio. - 'ACCEPTING_A': 16, - # The flag that marks that friend is receiving video. - 'ACCEPTING_V': 32, -} - -TOXAV_CALL_CONTROL = { - # Resume a previously paused call. Only valid if the pause was caused by this client, if not, this control is - # ignored. Not valid before the call is accepted. - 'RESUME': 0, - # Put a call on hold. Not valid before the call is accepted. - 'PAUSE': 1, - # Reject a call if it was not answered, yet. Cancel a call after it was answered. - 'CANCEL': 2, - # Request that the friend stops sending audio. Regardless of the friend's compliance, this will cause the - # audio_receive_frame event to stop being triggered on receiving an audio frame from the friend. - 'MUTE_AUDIO': 3, - # Calling this control will notify client to start sending audio again. - 'UNMUTE_AUDIO': 4, - # Request that the friend stops sending video. Regardless of the friend's compliance, this will cause the - # video_receive_frame event to stop being triggered on receiving a video frame from the friend. - 'HIDE_VIDEO': 5, - # Calling this control will notify client to start sending video again. - 'SHOW_VIDEO': 6, -} - -TOXAV_ERR_CALL_CONTROL = { - # The function returned successfully. - 'OK': 0, - # Synchronization error occurred. - 'SYNC': 1, - # The friend_number passed did not designate a valid friend. - 'FRIEND_NOT_FOUND': 2, - # This client is currently not in a call with the friend. Before the call is answered, only CANCEL is a valid - # control. - 'FRIEND_NOT_IN_CALL': 3, - # Happens if user tried to pause an already paused call or if trying to resume a call that is not paused. - 'INVALID_TRANSITION': 4, -} - -TOXAV_ERR_BIT_RATE_SET = { - # The function returned successfully. - 'OK': 0, - # Synchronization error occurred. - 'SYNC': 1, - # The audio bit rate passed was not one of the supported values. - 'INVALID_AUDIO_BIT_RATE': 2, - # The video bit rate passed was not one of the supported values. - 'INVALID_VIDEO_BIT_RATE': 3, - # The friend_number passed did not designate a valid friend. - 'FRIEND_NOT_FOUND': 4, - # This client is currently not in a call with the friend. - 'FRIEND_NOT_IN_CALL': 5, -} - -TOXAV_ERR_SEND_FRAME = { - # The function returned successfully. - 'OK': 0, - # In case of video, one of Y, U, or V was NULL. In case of audio, the samples data pointer was NULL. - 'NULL': 1, - # The friend_number passed did not designate a valid friend. - 'FRIEND_NOT_FOUND': 2, - # This client is currently not in a call with the friend. - 'FRIEND_NOT_IN_CALL': 3, - # Synchronization error occurred. - 'SYNC': 4, - # One of the frame parameters was invalid. E.g. the resolution may be too small or too large, or the audio sampling - # rate may be unsupported. - 'INVALID': 5, - # Either friend turned off audio or video receiving or we turned off sending for the said payload. - 'PAYLOAD_TYPE_DISABLED': 6, - # Failed to push frame through rtp interface. - 'RTP_FAILED': 7, -} diff --git a/toxygen/toxcore_enums_and_consts.py b/toxygen/toxcore_enums_and_consts.py deleted file mode 100644 index a17d93e..0000000 --- a/toxygen/toxcore_enums_and_consts.py +++ /dev/null @@ -1,220 +0,0 @@ -TOX_USER_STATUS = { - 'NONE': 0, - 'AWAY': 1, - 'BUSY': 2, -} - -TOX_MESSAGE_TYPE = { - 'NORMAL': 0, - 'ACTION': 1, -} - -TOX_PROXY_TYPE = { - 'NONE': 0, - 'HTTP': 1, - 'SOCKS5': 2, -} - -TOX_SAVEDATA_TYPE = { - 'NONE': 0, - 'TOX_SAVE': 1, - 'SECRET_KEY': 2, -} - -TOX_ERR_OPTIONS_NEW = { - 'OK': 0, - 'MALLOC': 1, -} - -TOX_ERR_NEW = { - 'OK': 0, - 'NULL': 1, - 'MALLOC': 2, - 'PORT_ALLOC': 3, - 'PROXY_BAD_TYPE': 4, - 'PROXY_BAD_HOST': 5, - 'PROXY_BAD_PORT': 6, - 'PROXY_NOT_FOUND': 7, - 'LOAD_ENCRYPTED': 8, - 'LOAD_BAD_FORMAT': 9, -} - -TOX_ERR_BOOTSTRAP = { - 'OK': 0, - 'NULL': 1, - 'BAD_HOST': 2, - 'BAD_PORT': 3, -} - -TOX_CONNECTION = { - 'NONE': 0, - 'TCP': 1, - 'UDP': 2, -} - -TOX_ERR_SET_INFO = { - 'OK': 0, - 'NULL': 1, - 'TOO_LONG': 2, -} - -TOX_ERR_FRIEND_ADD = { - 'OK': 0, - 'NULL': 1, - 'TOO_LONG': 2, - 'NO_MESSAGE': 3, - 'OWN_KEY': 4, - 'ALREADY_SENT': 5, - 'BAD_CHECKSUM': 6, - 'SET_NEW_NOSPAM': 7, - 'MALLOC': 8, -} - -TOX_ERR_FRIEND_DELETE = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_BY_PUBLIC_KEY = { - 'OK': 0, - 'NULL': 1, - 'NOT_FOUND': 2, -} - -TOX_ERR_FRIEND_GET_PUBLIC_KEY = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_GET_LAST_ONLINE = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_QUERY = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, -} - -TOX_ERR_SET_TYPING = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_SEND_MESSAGE = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'SENDQ': 4, - 'TOO_LONG': 5, - 'EMPTY': 6, -} - -TOX_FILE_KIND = { - 'DATA': 0, - 'AVATAR': 1, -} - -TOX_FILE_CONTROL = { - 'RESUME': 0, - 'PAUSE': 1, - 'CANCEL': 2, -} - -TOX_ERR_FILE_CONTROL = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, - 'FRIEND_NOT_CONNECTED': 2, - 'NOT_FOUND': 3, - 'NOT_PAUSED': 4, - 'DENIED': 5, - 'ALREADY_PAUSED': 6, - 'SENDQ': 7, -} - -TOX_ERR_FILE_SEEK = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, - 'FRIEND_NOT_CONNECTED': 2, - 'NOT_FOUND': 3, - 'DENIED': 4, - 'INVALID_POSITION': 5, - 'SENDQ': 6, -} - -TOX_ERR_FILE_GET = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'NOT_FOUND': 3, -} - -TOX_ERR_FILE_SEND = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'NAME_TOO_LONG': 4, - 'TOO_MANY': 5, -} - -TOX_ERR_FILE_SEND_CHUNK = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'NOT_FOUND': 4, - 'NOT_TRANSFERRING': 5, - 'INVALID_LENGTH': 6, - 'SENDQ': 7, - 'WRONG_POSITION': 8, -} - -TOX_ERR_FRIEND_CUSTOM_PACKET = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'INVALID': 4, - 'EMPTY': 5, - 'TOO_LONG': 6, - 'SENDQ': 7, -} - -TOX_ERR_GET_PORT = { - 'OK': 0, - 'NOT_BOUND': 1, -} - -TOX_CHAT_CHANGE = { - 'PEER_ADD': 0, - 'PEER_DEL': 1, - 'PEER_NAME': 2 -} - -TOX_GROUPCHAT_TYPE = { - 'TEXT': 0, - 'AV': 1 -} - -TOX_PUBLIC_KEY_SIZE = 32 - -TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6 - -TOX_MAX_FRIEND_REQUEST_LENGTH = 1016 - -TOX_MAX_MESSAGE_LENGTH = 1372 - -TOX_MAX_NAME_LENGTH = 128 - -TOX_MAX_STATUS_MESSAGE_LENGTH = 1007 - -TOX_SECRET_KEY_SIZE = 32 - -TOX_FILE_ID_LENGTH = 32 - -TOX_HASH_LENGTH = 32 - -TOX_MAX_CUSTOM_PACKET_SIZE = 1373 diff --git a/toxygen/toxencryptsave.py b/toxygen/toxencryptsave.py deleted file mode 100644 index b579e21..0000000 --- a/toxygen/toxencryptsave.py +++ /dev/null @@ -1,74 +0,0 @@ -import libtox -from ctypes import c_size_t, create_string_buffer, byref, c_int, ArgumentError, c_char_p, c_bool -from toxencryptsave_enums_and_consts import * - - -class ToxEncryptSave: - - def __init__(self): - self.libtoxencryptsave = libtox.LibToxEncryptSave() - - def is_data_encrypted(self, data): - """ - Checks if given data is encrypted - """ - func = self.libtoxencryptsave.tox_is_data_encrypted - func.restype = c_bool - result = func(c_char_p(bytes(data))) - return result - - def pass_encrypt(self, data, password): - """ - Encrypts the given data with the given password. - - :return: output array - """ - out = create_string_buffer(len(data) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH) - tox_err_encryption = c_int() - self.libtoxencryptsave.tox_pass_encrypt(c_char_p(data), - c_size_t(len(data)), - c_char_p(bytes(password, 'utf-8')), - c_size_t(len(password)), - out, - byref(tox_err_encryption)) - tox_err_encryption = tox_err_encryption.value - if tox_err_encryption == TOX_ERR_ENCRYPTION['OK']: - return out[:] - elif tox_err_encryption == TOX_ERR_ENCRYPTION['NULL']: - raise ArgumentError('Some input data, or maybe the output pointer, was null.') - elif tox_err_encryption == TOX_ERR_ENCRYPTION['KEY_DERIVATION_FAILED']: - raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a' - ' lack of memory issue. The functions accepting keys do not produce this error.') - elif tox_err_encryption == TOX_ERR_ENCRYPTION['FAILED']: - raise RuntimeError('The encryption itself failed.') - - def pass_decrypt(self, data, password): - """ - Decrypts the given data with the given password. - - :return: output array - """ - out = create_string_buffer(len(data) - TOX_PASS_ENCRYPTION_EXTRA_LENGTH) - tox_err_decryption = c_int() - self.libtoxencryptsave.tox_pass_decrypt(c_char_p(bytes(data)), - c_size_t(len(data)), - c_char_p(bytes(password, 'utf-8')), - c_size_t(len(password)), - out, - byref(tox_err_decryption)) - tox_err_decryption = tox_err_decryption.value - if tox_err_decryption == TOX_ERR_DECRYPTION['OK']: - return out[:] - elif tox_err_decryption == TOX_ERR_DECRYPTION['NULL']: - raise ArgumentError('Some input data, or maybe the output pointer, was null.') - elif tox_err_decryption == TOX_ERR_DECRYPTION['INVALID_LENGTH']: - raise ArgumentError('The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes') - elif tox_err_decryption == TOX_ERR_DECRYPTION['BAD_FORMAT']: - raise ArgumentError('The input data is missing the magic number (i.e. wasn\'t created by this module, or is' - ' corrupted)') - elif tox_err_decryption == TOX_ERR_DECRYPTION['KEY_DERIVATION_FAILED']: - raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a' - ' lack of memory issue. The functions accepting keys do not produce this error.') - elif tox_err_decryption == TOX_ERR_DECRYPTION['FAILED']: - raise RuntimeError('The encrypted byte array could not be decrypted. Either the data was corrupt or the ' - 'password/key was incorrect.') diff --git a/toxygen/toxencryptsave_enums_and_consts.py b/toxygen/toxencryptsave_enums_and_consts.py deleted file mode 100644 index cf795f8..0000000 --- a/toxygen/toxencryptsave_enums_and_consts.py +++ /dev/null @@ -1,29 +0,0 @@ -TOX_ERR_ENCRYPTION = { - # The function returned successfully. - 'OK': 0, - # Some input data, or maybe the output pointer, was null. - 'NULL': 1, - # The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The - # functions accepting keys do not produce this error. - 'KEY_DERIVATION_FAILED': 2, - # The encryption itself failed. - 'FAILED': 3 -} - -TOX_ERR_DECRYPTION = { - # The function returned successfully. - 'OK': 0, - # Some input data, or maybe the output pointer, was null. - 'NULL': 1, - # The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes - 'INVALID_LENGTH': 2, - # The input data is missing the magic number (i.e. wasn't created by this module, or is corrupted) - 'BAD_FORMAT': 3, - # The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The - # functions accepting keys do not produce this error. - 'KEY_DERIVATION_FAILED': 4, - # The encrypted byte array could not be decrypted. Either the data was corrupt or the password/key was incorrect. - 'FAILED': 5, -} - -TOX_PASS_ENCRYPTION_EXTRA_LENGTH = 80 diff --git a/toxygen/toxes.py b/toxygen/toxes.py deleted file mode 100644 index 5b7282f..0000000 --- a/toxygen/toxes.py +++ /dev/null @@ -1,28 +0,0 @@ -import util -import toxencryptsave - - -class ToxES(util.Singleton): - - def __init__(self): - super().__init__() - self._toxencryptsave = toxencryptsave.ToxEncryptSave() - self._passphrase = None - - def set_password(self, passphrase): - self._passphrase = passphrase - - def has_password(self): - return bool(self._passphrase) - - def is_password(self, password): - return self._passphrase == password - - def is_data_encrypted(self, data): - return len(data) > 0 and self._toxencryptsave.is_data_encrypted(data) - - def pass_encrypt(self, data): - return self._toxencryptsave.pass_encrypt(data, self._passphrase) - - def pass_decrypt(self, data): - return self._toxencryptsave.pass_decrypt(data, self._passphrase) diff --git a/toxygen/updater.py b/toxygen/updater.py deleted file mode 100644 index 762892a..0000000 --- a/toxygen/updater.py +++ /dev/null @@ -1,110 +0,0 @@ -import util -import os -import settings -import platform -import urllib -from PyQt5 import QtNetwork, QtCore -import subprocess - - -def connection_available(): - try: - urllib.request.urlopen('http://216.58.192.142', timeout=1) # google.com - return True - except: - return False - - -def updater_available(): - if is_from_sources(): - return os.path.exists(util.curr_directory() + '/toxygen_updater.py') - elif platform.system() == 'Windows': - return os.path.exists(util.curr_directory() + '/toxygen_updater.exe') - else: - return os.path.exists(util.curr_directory() + '/toxygen_updater') - - -def check_for_updates(): - current_version = util.program_version - major, minor, patch = list(map(lambda x: int(x), current_version.split('.'))) - versions = generate_versions(major, minor, patch) - for version in versions: - if send_request(version): - return version - return None # no new version was found - - -def is_from_sources(): - return __file__.endswith('.py') - - -def test_url(version): - return 'https://github.com/toxygen-project/toxygen/releases/tag/v' + version - - -def get_url(version): - if is_from_sources(): - return 'https://github.com/toxygen-project/toxygen/archive/v' + version + '.zip' - else: - if platform.system() == 'Windows': - name = 'toxygen_windows.zip' - elif util.is_64_bit(): - name = 'toxygen_linux_64.tar.gz' - else: - name = 'toxygen_linux.tar.gz' - return 'https://github.com/toxygen-project/toxygen/releases/download/v{}/{}'.format(version, name) - - -def get_params(url, version): - if is_from_sources(): - if platform.system() == 'Windows': - return ['python', 'toxygen_updater.py', url, version] - else: - return ['python3', 'toxygen_updater.py', url, version] - elif platform.system() == 'Windows': - return [util.curr_directory() + '/toxygen_updater.exe', url, version] - else: - return ['./toxygen_updater', url, version] - - -def download(version): - os.chdir(util.curr_directory()) - url = get_url(version) - params = get_params(url, version) - print('Updating Toxygen') - util.log('Updating Toxygen') - try: - subprocess.Popen(params) - except Exception as ex: - util.log('Exception: running updater failed with ' + str(ex)) - - -def send_request(version): - s = settings.Settings.get_instance() - netman = QtNetwork.QNetworkAccessManager() - proxy = QtNetwork.QNetworkProxy() - if s['proxy_type']: - proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy if s['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) - proxy.setHostName(s['proxy_host']) - proxy.setPort(s['proxy_port']) - netman.setProxy(proxy) - url = test_url(version) - try: - request = QtNetwork.QNetworkRequest() - request.setUrl(QtCore.QUrl(url)) - reply = netman.get(request) - while not reply.isFinished(): - QtCore.QThread.msleep(1) - QtCore.QCoreApplication.processEvents() - attr = reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute) - return attr is not None and 200 <= attr < 300 - except Exception as ex: - util.log('TOXYGEN UPDATER ERROR: ' + str(ex)) - return False - - -def generate_versions(major, minor, patch): - new_major = '.'.join([str(major + 1), '0', '0']) - new_minor = '.'.join([str(major), str(minor + 1), '0']) - new_patch = '.'.join([str(major), str(minor), str(patch + 1)]) - return new_major, new_minor, new_patch diff --git a/toxygen/util.py b/toxygen/util.py deleted file mode 100644 index 6157775..0000000 --- a/toxygen/util.py +++ /dev/null @@ -1,107 +0,0 @@ -import os -import time -import shutil -import sys -import re - - -program_version = '0.4.4' - - -def cached(func): - saved_result = None - - def wrapped_func(): - nonlocal saved_result - if saved_result is None: - saved_result = func() - - return saved_result - - return wrapped_func - - -def log(data): - try: - with open(curr_directory() + '/logs.log', 'a') as fl: - fl.write(str(data) + '\n') - except: - pass - - -@cached -def curr_directory(): - return os.path.dirname(os.path.realpath(__file__)) - - -def curr_time(): - return time.strftime('%H:%M') - - -def copy(src, dest): - if not os.path.exists(dest): - os.makedirs(dest) - src_files = os.listdir(src) - for file_name in src_files: - full_file_name = os.path.join(src, file_name) - if os.path.isfile(full_file_name): - shutil.copy(full_file_name, dest) - else: - copy(full_file_name, os.path.join(dest, file_name)) - - -def remove(folder): - if os.path.isdir(folder): - shutil.rmtree(folder) - - -def convert_time(t): - offset = time.timezone + time_offset() * 60 - sec = int(t) - offset - m, s = divmod(sec, 60) - h, m = divmod(m, 60) - d, h = divmod(h, 24) - return '%02d:%02d' % (h, m) - - -@cached -def time_offset(): - hours = int(time.strftime('%H')) - minutes = int(time.strftime('%M')) - sec = int(time.time()) - time.timezone - m, s = divmod(sec, 60) - h, m = divmod(m, 60) - d, h = divmod(h, 24) - result = hours * 60 + minutes - h * 60 - m - return result - - -def append_slash(s): - if len(s) and s[-1] not in ('\\', '/'): - s += '/' - return s - - -@cached -def is_64_bit(): - return sys.maxsize > 2 ** 32 - - -def is_re_valid(regex): - try: - re.compile(regex) - except re.error: - return False - else: - return True - - -class Singleton: - _instance = None - - def __init__(self): - self.__class__._instance = self - - @classmethod - def get_instance(cls): - return cls._instance diff --git a/toxygen/widgets.py b/toxygen/widgets.py deleted file mode 100644 index b63deb0..0000000 --- a/toxygen/widgets.py +++ /dev/null @@ -1,166 +0,0 @@ -from PyQt5 import QtCore, QtGui, QtWidgets - - -class DataLabel(QtWidgets.QLabel): - """ - Label with elided text - """ - def setText(self, text): - text = ''.join('\u25AF' if len(bytes(c, 'utf-8')) >= 4 else c for c in text) - metrics = QtGui.QFontMetrics(self.font()) - text = metrics.elidedText(text, QtCore.Qt.ElideRight, self.width()) - super().setText(text) - - -class ComboBox(QtWidgets.QComboBox): - - def __init__(self, *args): - super().__init__(*args) - self.view().setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) - - -class CenteredWidget(QtWidgets.QWidget): - - def __init__(self): - super(CenteredWidget, self).__init__() - self.center() - - def center(self): - qr = self.frameGeometry() - cp = QtWidgets.QDesktopWidget().availableGeometry().center() - qr.moveCenter(cp) - self.move(qr.topLeft()) - - -class LineEdit(QtWidgets.QLineEdit): - - def __init__(self, parent=None): - super(LineEdit, self).__init__(parent) - - def contextMenuEvent(self, event): - menu = create_menu(self.createStandardContextMenu()) - menu.exec_(event.globalPos()) - del menu - - -class QRightClickButton(QtWidgets.QPushButton): - """ - Button with right click support - """ - - rightClicked = QtCore.pyqtSignal() - - def __init__(self, parent): - super(QRightClickButton, self).__init__(parent) - - def mousePressEvent(self, event): - if event.button() == QtCore.Qt.RightButton: - self.rightClicked.emit() - else: - super(QRightClickButton, self).mousePressEvent(event) - - -class RubberBand(QtWidgets.QRubberBand): - - def __init__(self): - super(RubberBand, self).__init__(QtWidgets.QRubberBand.Rectangle, None) - self.setPalette(QtGui.QPalette(QtCore.Qt.transparent)) - self.pen = QtGui.QPen(QtCore.Qt.blue, 4) - self.pen.setStyle(QtCore.Qt.SolidLine) - self.painter = QtGui.QPainter() - - def paintEvent(self, event): - - self.painter.begin(self) - self.painter.setPen(self.pen) - self.painter.drawRect(event.rect()) - self.painter.end() - - -class RubberBandWindow(QtWidgets.QWidget): - - def __init__(self, parent): - super().__init__() - self.parent = parent - self.setMouseTracking(True) - self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint) - self.showFullScreen() - self.setWindowOpacity(0.5) - self.rubberband = RubberBand() - self.rubberband.setWindowFlags(self.rubberband.windowFlags() | QtCore.Qt.FramelessWindowHint) - self.rubberband.setAttribute(QtCore.Qt.WA_TranslucentBackground) - - def mousePressEvent(self, event): - self.origin = event.pos() - self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize())) - self.rubberband.show() - QtWidgets.QWidget.mousePressEvent(self, event) - - def mouseMoveEvent(self, event): - if self.rubberband.isVisible(): - self.rubberband.setGeometry(QtCore.QRect(self.origin, event.pos()).normalized()) - left = QtGui.QRegion(QtCore.QRect(0, 0, self.rubberband.x(), self.height())) - right = QtGui.QRegion(QtCore.QRect(self.rubberband.x() + self.rubberband.width(), 0, self.width(), self.height())) - top = QtGui.QRegion(0, 0, self.width(), self.rubberband.y()) - bottom = QtGui.QRegion(0, self.rubberband.y() + self.rubberband.height(), self.width(), self.height()) - self.setMask(left + right + top + bottom) - - def keyPressEvent(self, event): - if event.key() == QtCore.Qt.Key_Escape: - self.rubberband.setHidden(True) - self.close() - else: - super().keyPressEvent(event) - - -def create_menu(menu): - """ - :return translated menu - """ - for action in menu.actions(): - text = action.text() - if 'Link Location' in text: - text = text.replace('Copy &Link Location', - QtWidgets.QApplication.translate("MainWindow", "Copy link location")) - elif '&Copy' in text: - text = text.replace('&Copy', QtWidgets.QApplication.translate("MainWindow", "Copy")) - elif 'All' in text: - text = text.replace('Select All', QtWidgets.QApplication.translate("MainWindow", "Select all")) - elif 'Delete' in text: - text = text.replace('Delete', QtWidgets.QApplication.translate("MainWindow", "Delete")) - elif '&Paste' in text: - text = text.replace('&Paste', QtWidgets.QApplication.translate("MainWindow", "Paste")) - elif 'Cu&t' in text: - text = text.replace('Cu&t', QtWidgets.QApplication.translate("MainWindow", "Cut")) - elif '&Undo' in text: - text = text.replace('&Undo', QtWidgets.QApplication.translate("MainWindow", "Undo")) - elif '&Redo' in text: - text = text.replace('&Redo', QtWidgets.QApplication.translate("MainWindow", "Redo")) - else: - menu.removeAction(action) - continue - action.setText(text) - return menu - - -class MultilineEdit(CenteredWidget): - - def __init__(self, title, text, save): - super(MultilineEdit, self).__init__() - self.resize(350, 200) - self.setMinimumSize(QtCore.QSize(350, 200)) - self.setMaximumSize(QtCore.QSize(350, 200)) - self.setWindowTitle(title) - self.edit = QtWidgets.QTextEdit(self) - self.edit.setGeometry(QtCore.QRect(0, 0, 350, 150)) - self.edit.setText(text) - self.button = QtWidgets.QPushButton(self) - self.button.setGeometry(QtCore.QRect(0, 150, 350, 50)) - self.button.setText(QtWidgets.QApplication.translate("MainWindow", "Save")) - self.button.clicked.connect(self.button_click) - self.center() - self.save = save - - def button_click(self): - self.save(self.edit.toPlainText()) - self.close() From 39f263893143a73aea6a69b25d2bcfd6550d963a Mon Sep 17 00:00:00 2001 From: emdee Date: Tue, 27 Sep 2022 12:52:32 +0000 Subject: [PATCH 150/217] next_gen branch README --- MANIFEST.in | 2 +- README.md | 21 +- docs/compile.md | 18 +- docs/contact.md | 2 +- setup.py | 27 +- tests/tests.py | 158 +------ toxygen/main.py | 504 ++-------------------- toxygen/smileys/default/003020E3.png | Bin 1305 -> 1317 bytes toxygen/smileys/default/003120E3.png | Bin 1151 -> 1198 bytes toxygen/smileys/default/003220E3.png | Bin 1291 -> 1317 bytes toxygen/smileys/default/003320E3.png | Bin 1281 -> 1308 bytes toxygen/smileys/default/003420E3.png | Bin 1238 -> 1255 bytes toxygen/smileys/default/003520E3.png | Bin 1286 -> 1319 bytes toxygen/smileys/default/003620E3.png | Bin 1328 -> 1348 bytes toxygen/smileys/default/003720E3.png | Bin 1227 -> 1274 bytes toxygen/smileys/default/003820E3.png | Bin 1328 -> 1342 bytes toxygen/smileys/default/003920E3.png | Bin 1329 -> 1345 bytes toxygen/smileys/default/00A9.png | Bin 1257 -> 1146 bytes toxygen/smileys/default/00AE.png | Bin 1262 -> 1152 bytes toxygen/smileys/default/203C.png | Bin 1199 -> 1311 bytes toxygen/smileys/default/2049.png | Bin 1304 -> 1513 bytes toxygen/smileys/default/2122.png | Bin 1126 -> 1054 bytes toxygen/smileys/default/2139.png | Bin 1314 -> 1330 bytes toxygen/smileys/default/2194.png | Bin 1277 -> 1268 bytes toxygen/smileys/default/2195.png | Bin 1283 -> 1282 bytes toxygen/smileys/default/2196.png | Bin 1306 -> 1315 bytes toxygen/smileys/default/2197.png | Bin 1289 -> 1322 bytes toxygen/smileys/default/2198.png | Bin 1308 -> 1319 bytes toxygen/smileys/default/2199.png | Bin 1287 -> 1325 bytes toxygen/smileys/default/21A9.png | Bin 1340 -> 1336 bytes toxygen/smileys/default/21AA.png | Bin 1325 -> 1337 bytes toxygen/smileys/default/231A.png | Bin 1466 -> 1344 bytes toxygen/smileys/default/231B.png | Bin 856 -> 1100 bytes toxygen/smileys/default/23E9.png | Bin 1324 -> 1338 bytes toxygen/smileys/default/23EA.png | Bin 1269 -> 1281 bytes toxygen/smileys/default/23EB.png | Bin 1340 -> 1333 bytes toxygen/smileys/default/23EC.png | Bin 1355 -> 1325 bytes toxygen/smileys/default/23F0.png | Bin 1688 -> 1600 bytes toxygen/smileys/default/23F3.png | Bin 1465 -> 1397 bytes toxygen/smileys/default/24C2.png | Bin 1492 -> 1570 bytes toxygen/smileys/default/25AA.png | Bin 1118 -> 1034 bytes toxygen/smileys/default/25AB.png | Bin 1100 -> 1026 bytes toxygen/smileys/default/25B6.png | Bin 1277 -> 1296 bytes toxygen/smileys/default/25C0.png | Bin 1274 -> 1305 bytes toxygen/smileys/default/25FB.png | Bin 515 -> 840 bytes toxygen/smileys/default/25FC.png | Bin 560 -> 519 bytes toxygen/smileys/default/25FD.png | Bin 1143 -> 1051 bytes toxygen/smileys/default/25FE.png | Bin 1195 -> 1080 bytes toxygen/smileys/default/2600.png | Bin 1410 -> 1312 bytes toxygen/smileys/default/2601.png | Bin 1373 -> 1418 bytes toxygen/smileys/default/260E.png | Bin 1462 -> 1334 bytes toxygen/smileys/default/2611.png | Bin 1338 -> 1166 bytes toxygen/smileys/default/2614.png | Bin 1592 -> 1582 bytes toxygen/smileys/default/2615.png | Bin 1607 -> 1581 bytes toxygen/smileys/default/261D.png | Bin 1466 -> 1442 bytes toxygen/smileys/default/263A.png | Bin 1779 -> 1852 bytes toxygen/smileys/default/2648.png | Bin 1389 -> 1396 bytes toxygen/smileys/default/2649.png | Bin 1421 -> 1401 bytes toxygen/smileys/default/264A.png | Bin 1341 -> 1321 bytes toxygen/smileys/default/264B.png | Bin 1525 -> 1535 bytes toxygen/smileys/default/264C.png | Bin 1422 -> 1454 bytes toxygen/smileys/default/264D.png | Bin 1362 -> 1355 bytes toxygen/smileys/default/264E.png | Bin 1341 -> 1308 bytes toxygen/smileys/default/264F.png | Bin 1290 -> 1287 bytes toxygen/smileys/default/2650.png | Bin 1380 -> 1330 bytes toxygen/smileys/default/2651.png | Bin 1390 -> 1413 bytes toxygen/smileys/default/2652.png | Bin 1274 -> 1305 bytes toxygen/smileys/default/2653.png | Bin 1381 -> 1376 bytes toxygen/smileys/default/2660.png | Bin 1382 -> 1216 bytes toxygen/smileys/default/2663.png | Bin 1400 -> 1228 bytes toxygen/smileys/default/2665.png | Bin 1444 -> 1486 bytes toxygen/smileys/default/2666.png | Bin 1506 -> 1460 bytes toxygen/smileys/default/2668.png | Bin 1582 -> 1559 bytes toxygen/smileys/default/267B.png | Bin 1521 -> 1597 bytes toxygen/smileys/default/267F.png | Bin 1336 -> 1348 bytes toxygen/smileys/default/2693.png | Bin 1692 -> 1742 bytes toxygen/smileys/default/26A0.png | Bin 1482 -> 1430 bytes toxygen/smileys/default/26A1.png | Bin 1387 -> 1368 bytes toxygen/smileys/default/26AA.png | Bin 1410 -> 1228 bytes toxygen/smileys/default/26AB.png | Bin 1431 -> 1235 bytes toxygen/smileys/default/26BD.png | Bin 1825 -> 1850 bytes toxygen/smileys/default/26BE.png | Bin 1712 -> 1815 bytes toxygen/smileys/default/26C4.png | Bin 1639 -> 1562 bytes toxygen/smileys/default/26C5.png | Bin 1569 -> 1585 bytes toxygen/smileys/default/26CE.png | Bin 1396 -> 1377 bytes toxygen/smileys/default/26D4.png | Bin 1542 -> 1536 bytes toxygen/smileys/default/26EA.png | Bin 1557 -> 1470 bytes toxygen/smileys/default/26F2.png | Bin 1533 -> 1587 bytes toxygen/smileys/default/26F3.png | Bin 1408 -> 1493 bytes toxygen/smileys/default/26F5.png | Bin 1470 -> 1423 bytes toxygen/smileys/default/26FA.png | Bin 1449 -> 1505 bytes toxygen/smileys/default/26FD.png | Bin 1423 -> 1327 bytes toxygen/smileys/default/2702.png | Bin 1572 -> 1539 bytes toxygen/smileys/default/2705.png | Bin 1258 -> 1273 bytes toxygen/smileys/default/2708.png | Bin 1433 -> 1347 bytes toxygen/smileys/default/2709.png | Bin 1332 -> 1295 bytes toxygen/smileys/default/270A.png | Bin 1692 -> 1735 bytes toxygen/smileys/default/270B.png | Bin 1521 -> 1514 bytes toxygen/smileys/default/270C.png | Bin 1600 -> 1607 bytes toxygen/smileys/default/270F.png | Bin 1581 -> 1528 bytes toxygen/smileys/default/2712.png | Bin 1609 -> 1517 bytes toxygen/smileys/default/2714.png | Bin 1262 -> 1400 bytes toxygen/smileys/default/2716.png | Bin 1207 -> 1209 bytes toxygen/smileys/default/2728.png | Bin 1347 -> 1385 bytes toxygen/smileys/default/2733.png | Bin 1416 -> 1388 bytes toxygen/smileys/default/2734.png | Bin 1322 -> 1297 bytes toxygen/smileys/default/2744.png | Bin 1833 -> 2099 bytes toxygen/smileys/default/2747.png | Bin 1493 -> 1462 bytes toxygen/smileys/default/274C.png | Bin 1169 -> 1396 bytes toxygen/smileys/default/274E.png | Bin 1319 -> 1324 bytes toxygen/smileys/default/2753.png | Bin 1262 -> 1379 bytes toxygen/smileys/default/2754.png | Bin 1234 -> 1156 bytes toxygen/smileys/default/2755.png | Bin 1134 -> 1075 bytes toxygen/smileys/default/2757.png | Bin 1169 -> 1291 bytes toxygen/smileys/default/2764.png | Bin 1583 -> 1616 bytes toxygen/smileys/default/2795.png | Bin 1081 -> 1050 bytes toxygen/smileys/default/2796.png | Bin 1031 -> 992 bytes toxygen/smileys/default/2797.png | Bin 1060 -> 1021 bytes toxygen/smileys/default/27A1.png | Bin 1226 -> 1257 bytes toxygen/smileys/default/27B0.png | Bin 1274 -> 1434 bytes toxygen/smileys/default/27BF.png | Bin 1376 -> 1335 bytes toxygen/smileys/default/2934.png | Bin 1261 -> 1288 bytes toxygen/smileys/default/2935.png | Bin 1295 -> 1293 bytes toxygen/smileys/default/2B05.png | Bin 1220 -> 1257 bytes toxygen/smileys/default/2B06.png | Bin 1238 -> 1260 bytes toxygen/smileys/default/2B07.png | Bin 1229 -> 1261 bytes toxygen/smileys/default/2B1B.png | Bin 1250 -> 1115 bytes toxygen/smileys/default/2B1C.png | Bin 1170 -> 1071 bytes toxygen/smileys/default/2B50.png | Bin 1461 -> 1450 bytes toxygen/smileys/default/2B55.png | Bin 1307 -> 1466 bytes toxygen/smileys/default/3030.png | Bin 1106 -> 1036 bytes toxygen/smileys/default/303D.png | Bin 1500 -> 1503 bytes toxygen/smileys/default/D83CDC04.png | Bin 1516 -> 1501 bytes toxygen/smileys/default/D83CDCCF.png | Bin 1559 -> 1507 bytes toxygen/smileys/default/D83CDD70.png | Bin 1261 -> 1282 bytes toxygen/smileys/default/D83CDD71.png | Bin 1221 -> 1262 bytes toxygen/smileys/default/D83CDD7E.png | Bin 1272 -> 1296 bytes toxygen/smileys/default/D83CDD7F.png | Bin 1222 -> 1269 bytes toxygen/smileys/default/D83CDD8E.png | Bin 1309 -> 1336 bytes toxygen/smileys/default/D83CDD91.png | Bin 1276 -> 1297 bytes toxygen/smileys/default/D83CDD92.png | Bin 1277 -> 1289 bytes toxygen/smileys/default/D83CDD93.png | Bin 1207 -> 1242 bytes toxygen/smileys/default/D83CDD94.png | Bin 1254 -> 1263 bytes toxygen/smileys/default/D83CDD95.png | Bin 1270 -> 1283 bytes toxygen/smileys/default/D83CDD96.png | Bin 1335 -> 1338 bytes toxygen/smileys/default/D83CDD97.png | Bin 1366 -> 1382 bytes toxygen/smileys/default/D83CDD98.png | Bin 1335 -> 1366 bytes toxygen/smileys/default/D83CDD99.png | Bin 1236 -> 1266 bytes toxygen/smileys/default/D83CDD9A.png | Bin 1362 -> 1402 bytes toxygen/smileys/default/D83CDE01.png | Bin 1169 -> 1199 bytes toxygen/smileys/default/D83CDF00.png | Bin 1397 -> 1794 bytes toxygen/smileys/default/D83CDF01.png | Bin 1472 -> 1615 bytes toxygen/smileys/default/D83CDF02.png | Bin 1527 -> 1601 bytes toxygen/smileys/default/D83CDF03.png | Bin 1372 -> 1286 bytes toxygen/smileys/default/D83CDF04.png | Bin 1623 -> 1776 bytes toxygen/smileys/default/D83CDF05.png | Bin 1559 -> 1782 bytes toxygen/smileys/default/D83CDF06.png | Bin 1375 -> 1350 bytes toxygen/smileys/default/D83CDF07.png | Bin 1514 -> 1554 bytes toxygen/smileys/default/D83CDF08.png | Bin 1482 -> 1560 bytes toxygen/smileys/default/D83CDF09.png | Bin 1514 -> 1470 bytes toxygen/smileys/default/D83CDF0A.png | Bin 1646 -> 1628 bytes toxygen/smileys/default/D83CDF0B.png | Bin 1597 -> 1697 bytes toxygen/smileys/default/D83CDF0C.png | Bin 1582 -> 1731 bytes toxygen/smileys/default/D83CDF0D.png | Bin 1786 -> 1865 bytes toxygen/smileys/default/D83CDF0E.png | Bin 1777 -> 1867 bytes toxygen/smileys/default/D83CDF0F.png | Bin 1769 -> 1878 bytes toxygen/smileys/default/D83CDF10.png | Bin 1403 -> 1771 bytes toxygen/smileys/default/D83CDF11.png | Bin 1687 -> 1753 bytes toxygen/smileys/default/D83CDF12.png | Bin 1734 -> 1828 bytes toxygen/smileys/default/D83CDF13.png | Bin 1717 -> 1820 bytes toxygen/smileys/default/D83CDF14.png | Bin 1756 -> 1844 bytes toxygen/smileys/default/D83CDF15.png | Bin 1688 -> 1821 bytes toxygen/smileys/default/D83CDF16.png | Bin 1768 -> 1849 bytes toxygen/smileys/default/D83CDF17.png | Bin 1702 -> 1836 bytes toxygen/smileys/default/D83CDF18.png | Bin 1716 -> 1821 bytes toxygen/smileys/default/D83CDF19.png | Bin 1457 -> 1504 bytes toxygen/smileys/default/D83CDF1A.png | Bin 1748 -> 1801 bytes toxygen/smileys/default/D83CDF1B.png | Bin 1501 -> 1510 bytes toxygen/smileys/default/D83CDF1C.png | Bin 1496 -> 1518 bytes toxygen/smileys/default/D83CDF1D.png | Bin 1691 -> 1833 bytes toxygen/smileys/default/D83CDF1E.png | Bin 1650 -> 1631 bytes toxygen/smileys/default/D83CDF1F.png | Bin 1671 -> 1652 bytes toxygen/smileys/default/D83CDF20.png | Bin 1463 -> 1605 bytes toxygen/smileys/default/D83CDF30.png | Bin 1713 -> 1875 bytes toxygen/smileys/default/D83CDF31.png | Bin 1371 -> 1353 bytes toxygen/smileys/default/D83CDF32.png | Bin 1549 -> 1607 bytes toxygen/smileys/default/D83CDF33.png | Bin 1667 -> 1740 bytes toxygen/smileys/default/D83CDF34.png | Bin 1606 -> 1676 bytes toxygen/smileys/default/D83CDF35.png | Bin 1542 -> 1617 bytes toxygen/smileys/default/D83CDF37.png | Bin 1636 -> 1559 bytes toxygen/smileys/default/D83CDF38.png | Bin 1744 -> 1805 bytes toxygen/smileys/default/D83CDF39.png | Bin 1510 -> 1468 bytes toxygen/smileys/default/D83CDF3A.png | Bin 1755 -> 1971 bytes toxygen/smileys/default/D83CDF3B.png | Bin 1678 -> 1695 bytes toxygen/smileys/default/D83CDF3C.png | Bin 1640 -> 1685 bytes toxygen/smileys/default/D83CDF3D.png | Bin 1770 -> 1937 bytes toxygen/smileys/default/D83CDF3E.png | Bin 1632 -> 1888 bytes toxygen/smileys/default/D83CDF3F.png | Bin 1679 -> 1655 bytes toxygen/smileys/default/D83CDF40.png | Bin 1601 -> 1752 bytes toxygen/smileys/default/D83CDF41.png | Bin 1624 -> 1662 bytes toxygen/smileys/default/D83CDF42.png | Bin 1614 -> 1628 bytes toxygen/smileys/default/D83CDF43.png | Bin 1665 -> 1780 bytes toxygen/smileys/default/D83CDF44.png | Bin 1667 -> 1725 bytes toxygen/smileys/default/D83CDF45.png | Bin 1716 -> 1911 bytes toxygen/smileys/default/D83CDF46.png | Bin 1729 -> 1938 bytes toxygen/smileys/default/D83CDF47.png | Bin 1668 -> 1681 bytes toxygen/smileys/default/D83CDF48.png | Bin 1665 -> 1831 bytes toxygen/smileys/default/D83CDF49.png | Bin 1669 -> 1763 bytes toxygen/smileys/default/D83CDF4A.png | Bin 1690 -> 1936 bytes toxygen/smileys/default/D83CDF4B.png | Bin 1644 -> 1924 bytes toxygen/smileys/default/D83CDF4C.png | Bin 1635 -> 1727 bytes toxygen/smileys/default/D83CDF4D.png | Bin 1570 -> 1699 bytes toxygen/smileys/default/D83CDF4E.png | Bin 1666 -> 1875 bytes toxygen/smileys/default/D83CDF4F.png | Bin 1690 -> 1882 bytes toxygen/smileys/default/D83CDF50.png | Bin 1722 -> 1950 bytes toxygen/smileys/default/D83CDF51.png | Bin 1731 -> 1999 bytes toxygen/smileys/default/D83CDF52.png | Bin 1665 -> 1725 bytes toxygen/smileys/default/D83CDF53.png | Bin 1830 -> 1989 bytes toxygen/smileys/default/D83CDF54.png | Bin 1681 -> 1720 bytes toxygen/smileys/default/D83CDF55.png | Bin 1578 -> 1620 bytes toxygen/smileys/default/D83CDF56.png | Bin 1772 -> 1981 bytes toxygen/smileys/default/D83CDF57.png | Bin 1574 -> 1608 bytes toxygen/smileys/default/D83CDF58.png | Bin 876 -> 1289 bytes toxygen/smileys/default/D83CDF59.png | Bin 929 -> 1258 bytes toxygen/smileys/default/D83CDF5A.png | Bin 1722 -> 1741 bytes toxygen/smileys/default/D83CDF5B.png | Bin 1708 -> 1742 bytes toxygen/smileys/default/D83CDF5C.png | Bin 1778 -> 1845 bytes toxygen/smileys/default/D83CDF5D.png | Bin 1776 -> 1848 bytes toxygen/smileys/default/D83CDF5E.png | Bin 1686 -> 1754 bytes toxygen/smileys/default/D83CDF5F.png | Bin 1681 -> 1743 bytes toxygen/smileys/default/D83CDF60.png | Bin 1672 -> 1715 bytes toxygen/smileys/default/D83CDF61.png | Bin 1549 -> 1506 bytes toxygen/smileys/default/D83CDF62.png | Bin 1605 -> 1564 bytes toxygen/smileys/default/D83CDF63.png | Bin 1608 -> 1636 bytes toxygen/smileys/default/D83CDF64.png | Bin 1773 -> 1953 bytes toxygen/smileys/default/D83CDF65.png | Bin 1678 -> 1755 bytes toxygen/smileys/default/D83CDF66.png | Bin 1521 -> 1527 bytes toxygen/smileys/default/D83CDF67.png | Bin 1646 -> 1646 bytes toxygen/smileys/default/D83CDF68.png | Bin 1614 -> 1646 bytes toxygen/smileys/default/D83CDF69.png | Bin 1626 -> 1671 bytes toxygen/smileys/default/D83CDF6A.png | Bin 1867 -> 2023 bytes toxygen/smileys/default/D83CDF6B.png | Bin 1690 -> 1680 bytes toxygen/smileys/default/D83CDF6C.png | Bin 1527 -> 1558 bytes toxygen/smileys/default/D83CDF6D.png | Bin 1600 -> 1584 bytes toxygen/smileys/default/D83CDF6E.png | Bin 1741 -> 1748 bytes toxygen/smileys/default/D83CDF6F.png | Bin 1714 -> 1882 bytes toxygen/smileys/default/D83CDF70.png | Bin 1549 -> 1652 bytes toxygen/smileys/default/D83CDF71.png | Bin 1504 -> 1418 bytes toxygen/smileys/default/D83CDF72.png | Bin 1623 -> 1685 bytes toxygen/smileys/default/D83CDF73.png | Bin 1663 -> 1772 bytes toxygen/smileys/default/D83CDF74.png | Bin 1376 -> 1347 bytes toxygen/smileys/default/D83CDF75.png | Bin 1778 -> 1792 bytes toxygen/smileys/default/D83CDF76.png | Bin 1598 -> 1618 bytes toxygen/smileys/default/D83CDF77.png | Bin 1616 -> 1609 bytes toxygen/smileys/default/D83CDF78.png | Bin 1403 -> 1342 bytes toxygen/smileys/default/D83CDF79.png | Bin 1599 -> 1587 bytes toxygen/smileys/default/D83CDF7A.png | Bin 1630 -> 1642 bytes toxygen/smileys/default/D83CDF7B.png | Bin 1574 -> 1523 bytes toxygen/smileys/default/D83CDF7C.png | Bin 1593 -> 1631 bytes toxygen/smileys/default/D83CDF80.png | Bin 1639 -> 1656 bytes toxygen/smileys/default/D83CDF81.png | Bin 1690 -> 1657 bytes toxygen/smileys/default/D83CDF82.png | Bin 1718 -> 1648 bytes toxygen/smileys/default/D83CDF83.png | Bin 1724 -> 1780 bytes toxygen/smileys/default/D83CDF84.png | Bin 1502 -> 1490 bytes toxygen/smileys/default/D83CDF85.png | Bin 1771 -> 1786 bytes toxygen/smileys/default/D83CDF86.png | Bin 1602 -> 1618 bytes toxygen/smileys/default/D83CDF87.png | Bin 1578 -> 1599 bytes toxygen/smileys/default/D83CDF88.png | Bin 1352 -> 1308 bytes toxygen/smileys/default/D83CDF89.png | Bin 1817 -> 1812 bytes toxygen/smileys/default/D83CDF8A.png | Bin 1720 -> 1574 bytes toxygen/smileys/default/D83CDF8B.png | Bin 1571 -> 1535 bytes toxygen/smileys/default/D83CDF8C.png | Bin 1523 -> 1534 bytes toxygen/smileys/default/D83CDF8D.png | Bin 1438 -> 1378 bytes toxygen/smileys/default/D83CDF8E.png | Bin 1629 -> 1569 bytes toxygen/smileys/default/D83CDF8F.png | Bin 1658 -> 1696 bytes toxygen/smileys/default/D83CDF90.png | Bin 1526 -> 1503 bytes toxygen/smileys/default/D83CDF91.png | Bin 1536 -> 1552 bytes toxygen/smileys/default/D83CDF92.png | Bin 1767 -> 1896 bytes toxygen/smileys/default/D83CDF93.png | Bin 1463 -> 1411 bytes toxygen/smileys/default/D83CDFA0.png | Bin 1492 -> 1467 bytes toxygen/smileys/default/D83CDFA1.png | Bin 1581 -> 1534 bytes toxygen/smileys/default/D83CDFA2.png | Bin 1436 -> 1401 bytes toxygen/smileys/default/D83CDFA3.png | Bin 1482 -> 1577 bytes toxygen/smileys/default/D83CDFA4.png | Bin 1590 -> 1539 bytes toxygen/smileys/default/D83CDFA5.png | Bin 1559 -> 1619 bytes toxygen/smileys/default/D83CDFA6.png | Bin 1318 -> 1322 bytes toxygen/smileys/default/D83CDFA7.png | Bin 1463 -> 1399 bytes toxygen/smileys/default/D83CDFA8.png | Bin 1704 -> 1719 bytes toxygen/smileys/default/D83CDFA9.png | Bin 1528 -> 1617 bytes toxygen/smileys/default/D83CDFAA.png | Bin 1669 -> 1738 bytes toxygen/smileys/default/D83CDFAB.png | Bin 1315 -> 1179 bytes toxygen/smileys/default/D83CDFAC.png | Bin 1522 -> 1506 bytes toxygen/smileys/default/D83CDFAD.png | Bin 1778 -> 1809 bytes toxygen/smileys/default/D83CDFAE.png | Bin 1665 -> 1749 bytes toxygen/smileys/default/D83CDFAF.png | Bin 1634 -> 1722 bytes toxygen/smileys/default/D83CDFB0.png | Bin 1359 -> 1218 bytes toxygen/smileys/default/D83CDFB1.png | Bin 1723 -> 1808 bytes toxygen/smileys/default/D83CDFB2.png | Bin 1683 -> 1713 bytes toxygen/smileys/default/D83CDFB3.png | Bin 1679 -> 1713 bytes toxygen/smileys/default/D83CDFB4.png | Bin 1495 -> 1450 bytes toxygen/smileys/default/D83CDFB5.png | Bin 1559 -> 1796 bytes toxygen/smileys/default/D83CDFB6.png | Bin 1276 -> 1152 bytes toxygen/smileys/default/D83CDFB7.png | Bin 1497 -> 1606 bytes toxygen/smileys/default/D83CDFB8.png | Bin 1543 -> 1551 bytes toxygen/smileys/default/D83CDFB9.png | Bin 1153 -> 1103 bytes toxygen/smileys/default/D83CDFBA.png | Bin 1540 -> 1583 bytes toxygen/smileys/default/D83CDFBB.png | Bin 1655 -> 1689 bytes toxygen/smileys/default/D83CDFBC.png | Bin 1413 -> 1488 bytes toxygen/smileys/default/D83CDFBD.png | Bin 1605 -> 1704 bytes toxygen/smileys/default/D83CDFBE.png | Bin 1691 -> 1825 bytes toxygen/smileys/default/D83CDFBF.png | Bin 1635 -> 1659 bytes toxygen/smileys/default/D83CDFC0.png | Bin 1785 -> 1926 bytes toxygen/smileys/default/D83CDFC1.png | Bin 1539 -> 1396 bytes toxygen/smileys/default/D83CDFC2.png | Bin 1731 -> 1759 bytes toxygen/smileys/default/D83CDFC3.png | Bin 1482 -> 1441 bytes toxygen/smileys/default/D83CDFC4.png | Bin 1689 -> 1708 bytes toxygen/smileys/default/D83CDFC6.png | Bin 1653 -> 1564 bytes toxygen/smileys/default/D83CDFC7.png | Bin 1648 -> 1651 bytes toxygen/smileys/default/D83CDFC8.png | Bin 1843 -> 1852 bytes toxygen/smileys/default/D83CDFC9.png | Bin 1785 -> 1852 bytes toxygen/smileys/default/D83CDFCA.png | Bin 1473 -> 1482 bytes toxygen/smileys/default/D83CDFE0.png | Bin 1481 -> 1365 bytes toxygen/smileys/default/D83CDFE1.png | Bin 1649 -> 1620 bytes toxygen/smileys/default/D83CDFE2.png | Bin 1246 -> 1169 bytes toxygen/smileys/default/D83CDFE3.png | Bin 1542 -> 1339 bytes toxygen/smileys/default/D83CDFE4.png | Bin 1557 -> 1399 bytes toxygen/smileys/default/D83CDFE5.png | Bin 1442 -> 1314 bytes toxygen/smileys/default/D83CDFE7.png | Bin 1285 -> 1289 bytes toxygen/smileys/default/D83CDFE8.png | Bin 1459 -> 1294 bytes toxygen/smileys/default/D83CDFE9.png | Bin 1519 -> 1401 bytes toxygen/smileys/default/D83CDFEA.png | Bin 1409 -> 1259 bytes toxygen/smileys/default/D83CDFEB.png | Bin 1497 -> 1370 bytes toxygen/smileys/default/D83CDFEC.png | Bin 1384 -> 1242 bytes toxygen/smileys/default/D83CDFED.png | Bin 1348 -> 1287 bytes toxygen/smileys/default/D83CDFEE.png | Bin 1485 -> 1496 bytes toxygen/smileys/default/D83CDFEF.png | Bin 1403 -> 1330 bytes toxygen/smileys/default/D83CDFF0.png | Bin 1353 -> 1313 bytes toxygen/smileys/default/D83DDC00.png | Bin 1685 -> 1731 bytes toxygen/smileys/default/D83DDC01.png | Bin 1582 -> 1606 bytes toxygen/smileys/default/D83DDC02.png | Bin 1576 -> 1625 bytes toxygen/smileys/default/D83DDC03.png | Bin 1694 -> 1625 bytes toxygen/smileys/default/D83DDC04.png | Bin 1599 -> 1548 bytes toxygen/smileys/default/D83DDC05.png | Bin 1701 -> 1656 bytes toxygen/smileys/default/D83DDC06.png | Bin 1647 -> 1767 bytes toxygen/smileys/default/D83DDC07.png | Bin 1575 -> 1544 bytes toxygen/smileys/default/D83DDC08.png | Bin 1595 -> 1626 bytes toxygen/smileys/default/D83DDC09.png | Bin 1802 -> 1856 bytes toxygen/smileys/default/D83DDC0A.png | Bin 1712 -> 1800 bytes toxygen/smileys/default/D83DDC0B.png | Bin 1649 -> 1699 bytes toxygen/smileys/default/D83DDC0C.png | Bin 1710 -> 1883 bytes toxygen/smileys/default/D83DDC0D.png | Bin 1584 -> 1693 bytes toxygen/smileys/default/D83DDC0E.png | Bin 1613 -> 1527 bytes toxygen/smileys/default/D83DDC0F.png | Bin 1648 -> 1560 bytes toxygen/smileys/default/D83DDC10.png | Bin 1547 -> 1513 bytes toxygen/smileys/default/D83DDC11.png | Bin 1634 -> 1521 bytes toxygen/smileys/default/D83DDC12.png | Bin 1698 -> 1731 bytes toxygen/smileys/default/D83DDC13.png | Bin 1650 -> 1597 bytes toxygen/smileys/default/D83DDC14.png | Bin 1668 -> 1654 bytes toxygen/smileys/default/D83DDC15.png | Bin 1633 -> 1643 bytes toxygen/smileys/default/D83DDC16.png | Bin 1530 -> 1555 bytes toxygen/smileys/default/D83DDC17.png | Bin 1606 -> 1743 bytes toxygen/smileys/default/D83DDC18.png | Bin 1571 -> 1309 bytes toxygen/smileys/default/D83DDC19.png | Bin 1637 -> 1618 bytes toxygen/smileys/default/D83DDC1A.png | Bin 1720 -> 1862 bytes toxygen/smileys/default/D83DDC1B.png | Bin 1556 -> 1611 bytes toxygen/smileys/default/D83DDC1C.png | Bin 1695 -> 1738 bytes toxygen/smileys/default/D83DDC1D.png | Bin 1842 -> 1899 bytes toxygen/smileys/default/D83DDC1E.png | Bin 1603 -> 1548 bytes toxygen/smileys/default/D83DDC1F.png | Bin 1571 -> 1672 bytes toxygen/smileys/default/D83DDC20.png | Bin 1657 -> 1820 bytes toxygen/smileys/default/D83DDC21.png | Bin 1487 -> 1458 bytes toxygen/smileys/default/D83DDC22.png | Bin 1614 -> 1629 bytes toxygen/smileys/default/D83DDC23.png | Bin 1680 -> 1698 bytes toxygen/smileys/default/D83DDC24.png | Bin 1595 -> 1808 bytes toxygen/smileys/default/D83DDC25.png | Bin 1629 -> 1622 bytes toxygen/smileys/default/D83DDC26.png | Bin 1676 -> 1718 bytes toxygen/smileys/default/D83DDC27.png | Bin 1674 -> 1716 bytes toxygen/smileys/default/D83DDC28.png | Bin 1657 -> 1584 bytes toxygen/smileys/default/D83DDC2A.png | Bin 1706 -> 1737 bytes toxygen/smileys/default/D83DDC2B.png | Bin 1613 -> 1561 bytes toxygen/smileys/default/D83DDC2C.png | Bin 1570 -> 1654 bytes toxygen/smileys/default/D83DDC2D.png | Bin 1744 -> 1692 bytes toxygen/smileys/default/D83DDC2E.png | Bin 1609 -> 1599 bytes toxygen/smileys/default/D83DDC2F.png | Bin 1797 -> 1753 bytes toxygen/smileys/default/D83DDC30.png | Bin 1609 -> 1634 bytes toxygen/smileys/default/D83DDC31.png | Bin 1621 -> 1528 bytes toxygen/smileys/default/D83DDC32.png | Bin 1601 -> 1719 bytes toxygen/smileys/default/D83DDC33.png | Bin 1619 -> 1721 bytes toxygen/smileys/default/D83DDC34.png | Bin 1639 -> 1616 bytes toxygen/smileys/default/D83DDC35.png | Bin 1696 -> 1713 bytes toxygen/smileys/default/D83DDC36.png | Bin 1697 -> 1752 bytes toxygen/smileys/default/D83DDC37.png | Bin 1685 -> 1636 bytes toxygen/smileys/default/D83DDC38.png | Bin 1558 -> 1640 bytes toxygen/smileys/default/D83DDC39.png | Bin 1835 -> 1844 bytes toxygen/smileys/default/D83DDC3A.png | Bin 1722 -> 1676 bytes toxygen/smileys/default/D83DDC3B.png | Bin 1685 -> 1642 bytes toxygen/smileys/default/D83DDC3C.png | Bin 1780 -> 1735 bytes toxygen/smileys/default/D83DDC3D.png | Bin 1592 -> 1544 bytes toxygen/smileys/default/D83DDC3E.png | Bin 1289 -> 1541 bytes toxygen/smileys/default/D83DDC40.png | Bin 1367 -> 1349 bytes toxygen/smileys/default/D83DDC42.png | Bin 1565 -> 1558 bytes toxygen/smileys/default/D83DDC43.png | Bin 1451 -> 1406 bytes toxygen/smileys/default/D83DDC44.png | Bin 1599 -> 1530 bytes toxygen/smileys/default/D83DDC45.png | Bin 1609 -> 1632 bytes toxygen/smileys/default/D83DDC46.png | Bin 1388 -> 1328 bytes toxygen/smileys/default/D83DDC47.png | Bin 1374 -> 1329 bytes toxygen/smileys/default/D83DDC48.png | Bin 1358 -> 1318 bytes toxygen/smileys/default/D83DDC49.png | Bin 1339 -> 1319 bytes toxygen/smileys/default/D83DDC4A.png | Bin 1546 -> 1625 bytes toxygen/smileys/default/D83DDC4B.png | Bin 1701 -> 1764 bytes toxygen/smileys/default/D83DDC4C.png | Bin 1607 -> 1618 bytes toxygen/smileys/default/D83DDC4D.png | Bin 1567 -> 1570 bytes toxygen/smileys/default/D83DDC4E.png | Bin 1590 -> 1566 bytes toxygen/smileys/default/D83DDC4F.png | Bin 1701 -> 1730 bytes toxygen/smileys/default/D83DDC50.png | Bin 1564 -> 1591 bytes toxygen/smileys/default/D83DDC51.png | Bin 1711 -> 1832 bytes toxygen/smileys/default/D83DDC52.png | Bin 1816 -> 1942 bytes toxygen/smileys/default/D83DDC53.png | Bin 1325 -> 1297 bytes toxygen/smileys/default/D83DDC54.png | Bin 1685 -> 1605 bytes toxygen/smileys/default/D83DDC55.png | Bin 1664 -> 1673 bytes toxygen/smileys/default/D83DDC56.png | Bin 1366 -> 1356 bytes toxygen/smileys/default/D83DDC57.png | Bin 1467 -> 1534 bytes toxygen/smileys/default/D83DDC58.png | Bin 1608 -> 1629 bytes toxygen/smileys/default/D83DDC59.png | Bin 1542 -> 1504 bytes toxygen/smileys/default/D83DDC5A.png | Bin 1626 -> 1656 bytes toxygen/smileys/default/D83DDC5B.png | Bin 1608 -> 1760 bytes toxygen/smileys/default/D83DDC5C.png | Bin 1687 -> 1842 bytes toxygen/smileys/default/D83DDC5D.png | Bin 1487 -> 1618 bytes toxygen/smileys/default/D83DDC5E.png | Bin 1686 -> 1736 bytes toxygen/smileys/default/D83DDC5F.png | Bin 1733 -> 1779 bytes toxygen/smileys/default/D83DDC60.png | Bin 1558 -> 1592 bytes toxygen/smileys/default/D83DDC61.png | Bin 1489 -> 1485 bytes toxygen/smileys/default/D83DDC62.png | Bin 1448 -> 1453 bytes toxygen/smileys/default/D83DDC63.png | Bin 1313 -> 1569 bytes toxygen/smileys/default/D83DDC64.png | Bin 1203 -> 1272 bytes toxygen/smileys/default/D83DDC65.png | Bin 1338 -> 1551 bytes toxygen/smileys/default/D83DDC66.png | Bin 1766 -> 1856 bytes toxygen/smileys/default/D83DDC67.png | Bin 1794 -> 1764 bytes toxygen/smileys/default/D83DDC68.png | Bin 1797 -> 1863 bytes toxygen/smileys/default/D83DDC69.png | Bin 1806 -> 1924 bytes toxygen/smileys/default/D83DDC6A.png | Bin 1763 -> 1834 bytes toxygen/smileys/default/D83DDC6B.png | Bin 1719 -> 1600 bytes toxygen/smileys/default/D83DDC6C.png | Bin 1621 -> 1551 bytes toxygen/smileys/default/D83DDC6D.png | Bin 1737 -> 1664 bytes toxygen/smileys/default/D83DDC6E.png | Bin 1790 -> 1786 bytes toxygen/smileys/default/D83DDC6F.png | Bin 1518 -> 1485 bytes toxygen/smileys/default/D83DDC70.png | Bin 1829 -> 1880 bytes toxygen/smileys/default/D83DDC71.png | Bin 1800 -> 1806 bytes toxygen/smileys/default/D83DDC72.png | Bin 1770 -> 1694 bytes toxygen/smileys/default/D83DDC73.png | Bin 1759 -> 1769 bytes toxygen/smileys/default/D83DDC74.png | Bin 1684 -> 1654 bytes toxygen/smileys/default/D83DDC75.png | Bin 1692 -> 1725 bytes toxygen/smileys/default/D83DDC76.png | Bin 1650 -> 1644 bytes toxygen/smileys/default/D83DDC77.png | Bin 1733 -> 1763 bytes toxygen/smileys/default/D83DDC78.png | Bin 1748 -> 1851 bytes toxygen/smileys/default/D83DDC79.png | Bin 1780 -> 1680 bytes toxygen/smileys/default/D83DDC7A.png | Bin 1708 -> 1635 bytes toxygen/smileys/default/D83DDC7B.png | Bin 1687 -> 1723 bytes toxygen/smileys/default/D83DDC7C.png | Bin 1661 -> 1611 bytes toxygen/smileys/default/D83DDC7D.png | Bin 1796 -> 1737 bytes toxygen/smileys/default/D83DDC7E.png | Bin 1373 -> 1234 bytes toxygen/smileys/default/D83DDC7F.png | Bin 1847 -> 1826 bytes toxygen/smileys/default/D83DDC80.png | Bin 1697 -> 1665 bytes toxygen/smileys/default/D83DDC81.png | Bin 1741 -> 1779 bytes toxygen/smileys/default/D83DDC82.png | Bin 1614 -> 1673 bytes toxygen/smileys/default/D83DDC83.png | Bin 1579 -> 1556 bytes toxygen/smileys/default/D83DDC84.png | Bin 1393 -> 1374 bytes toxygen/smileys/default/D83DDC85.png | Bin 1584 -> 1585 bytes toxygen/smileys/default/D83DDC86.png | Bin 1795 -> 1793 bytes toxygen/smileys/default/D83DDC87.png | Bin 1804 -> 1846 bytes toxygen/smileys/default/D83DDC88.png | Bin 1487 -> 1294 bytes toxygen/smileys/default/D83DDC89.png | Bin 1490 -> 1447 bytes toxygen/smileys/default/D83DDC8A.png | Bin 1666 -> 1718 bytes toxygen/smileys/default/D83DDC8B.png | Bin 1698 -> 1838 bytes toxygen/smileys/default/D83DDC8C.png | Bin 1460 -> 1415 bytes toxygen/smileys/default/D83DDC8D.png | Bin 1010 -> 1341 bytes toxygen/smileys/default/D83DDC8E.png | Bin 1442 -> 1335 bytes toxygen/smileys/default/D83DDC8F.png | Bin 1746 -> 1781 bytes toxygen/smileys/default/D83DDC90.png | Bin 1729 -> 1785 bytes toxygen/smileys/default/D83DDC91.png | Bin 1793 -> 1825 bytes toxygen/smileys/default/D83DDC92.png | Bin 1697 -> 1612 bytes toxygen/smileys/default/D83DDC93.png | Bin 553 -> 749 bytes toxygen/smileys/default/D83DDC94.png | Bin 741 -> 1008 bytes toxygen/smileys/default/D83DDC95.png | Bin 1450 -> 1444 bytes toxygen/smileys/default/D83DDC96.png | Bin 762 -> 1016 bytes toxygen/smileys/default/D83DDC97.png | Bin 841 -> 1022 bytes toxygen/smileys/default/D83DDC98.png | Bin 1673 -> 1738 bytes toxygen/smileys/default/D83DDC99.png | Bin 595 -> 910 bytes toxygen/smileys/default/D83DDC9A.png | Bin 616 -> 947 bytes toxygen/smileys/default/D83DDC9B.png | Bin 608 -> 922 bytes toxygen/smileys/default/D83DDC9C.png | Bin 705 -> 938 bytes toxygen/smileys/default/D83DDC9D.png | Bin 799 -> 1063 bytes toxygen/smileys/default/D83DDC9E.png | Bin 770 -> 1100 bytes toxygen/smileys/default/D83DDC9F.png | Bin 1322 -> 1341 bytes toxygen/smileys/default/D83DDCA0.png | Bin 1669 -> 1688 bytes toxygen/smileys/default/D83DDCA1.png | Bin 1474 -> 1436 bytes toxygen/smileys/default/D83DDCA2.png | Bin 1598 -> 1573 bytes toxygen/smileys/default/D83DDCA3.png | Bin 1628 -> 1652 bytes toxygen/smileys/default/D83DDCA4.png | Bin 1162 -> 1267 bytes toxygen/smileys/default/D83DDCA5.png | Bin 1544 -> 1838 bytes toxygen/smileys/default/D83DDCA6.png | Bin 1577 -> 1681 bytes toxygen/smileys/default/D83DDCA7.png | Bin 1356 -> 1340 bytes toxygen/smileys/default/D83DDCA8.png | Bin 1727 -> 1862 bytes toxygen/smileys/default/D83DDCA9.png | Bin 1622 -> 1588 bytes toxygen/smileys/default/D83DDCAA.png | Bin 1526 -> 1573 bytes toxygen/smileys/default/D83DDCAB.png | Bin 1510 -> 1705 bytes toxygen/smileys/default/D83DDCAC.png | Bin 1571 -> 1543 bytes toxygen/smileys/default/D83DDCAD.png | Bin 1636 -> 1634 bytes toxygen/smileys/default/D83DDCAE.png | Bin 1027 -> 1534 bytes toxygen/smileys/default/D83DDCAF.png | Bin 1502 -> 1801 bytes toxygen/smileys/default/D83DDCB0.png | Bin 1657 -> 1705 bytes toxygen/smileys/default/D83DDCB1.png | Bin 1342 -> 1650 bytes toxygen/smileys/default/D83DDCB2.png | Bin 1246 -> 1384 bytes toxygen/smileys/default/D83DDCB3.png | Bin 1325 -> 1294 bytes toxygen/smileys/default/D83DDCB4.png | Bin 1565 -> 1724 bytes toxygen/smileys/default/D83DDCB5.png | Bin 1555 -> 1654 bytes toxygen/smileys/default/D83DDCB6.png | Bin 1579 -> 1730 bytes toxygen/smileys/default/D83DDCB7.png | Bin 1583 -> 1730 bytes toxygen/smileys/default/D83DDCB8.png | Bin 1876 -> 1888 bytes toxygen/smileys/default/D83DDCB9.png | Bin 1355 -> 1461 bytes toxygen/smileys/default/D83DDCBA.png | Bin 1639 -> 1588 bytes toxygen/smileys/default/D83DDCBB.png | Bin 1332 -> 1279 bytes toxygen/smileys/default/D83DDCBC.png | Bin 1399 -> 1266 bytes toxygen/smileys/default/D83DDCBD.png | Bin 1674 -> 1716 bytes toxygen/smileys/default/D83DDCBE.png | Bin 1460 -> 1393 bytes toxygen/smileys/default/D83DDCBF.png | Bin 1788 -> 1853 bytes toxygen/smileys/default/D83DDCC0.png | Bin 1792 -> 1892 bytes toxygen/smileys/default/D83DDCC1.png | Bin 1603 -> 1826 bytes toxygen/smileys/default/D83DDCC2.png | Bin 1645 -> 1874 bytes toxygen/smileys/default/D83DDCC3.png | Bin 1331 -> 1290 bytes toxygen/smileys/default/D83DDCC4.png | Bin 1308 -> 1290 bytes toxygen/smileys/default/D83DDCC5.png | Bin 1383 -> 1298 bytes toxygen/smileys/default/D83DDCC6.png | Bin 1407 -> 1288 bytes toxygen/smileys/default/D83DDCC7.png | Bin 1465 -> 1384 bytes toxygen/smileys/default/D83DDCC8.png | Bin 1468 -> 1406 bytes toxygen/smileys/default/D83DDCC9.png | Bin 1462 -> 1400 bytes toxygen/smileys/default/D83DDCCA.png | Bin 1368 -> 1357 bytes toxygen/smileys/default/D83DDCCB.png | Bin 1499 -> 1421 bytes toxygen/smileys/default/D83DDCCC.png | Bin 1567 -> 1532 bytes toxygen/smileys/default/D83DDCCD.png | Bin 1339 -> 1294 bytes toxygen/smileys/default/D83DDCCE.png | Bin 1221 -> 1212 bytes toxygen/smileys/default/D83DDCCF.png | Bin 1336 -> 1185 bytes toxygen/smileys/default/D83DDCD0.png | Bin 1404 -> 1575 bytes toxygen/smileys/default/D83DDCD1.png | Bin 1455 -> 1313 bytes toxygen/smileys/default/D83DDCD2.png | Bin 1247 -> 1205 bytes toxygen/smileys/default/D83DDCD3.png | Bin 1593 -> 1513 bytes toxygen/smileys/default/D83DDCD4.png | Bin 1220 -> 1180 bytes toxygen/smileys/default/D83DDCD5.png | Bin 1260 -> 1245 bytes toxygen/smileys/default/D83DDCD6.png | Bin 1424 -> 1346 bytes toxygen/smileys/default/D83DDCD7.png | Bin 1240 -> 1242 bytes toxygen/smileys/default/D83DDCD8.png | Bin 1211 -> 1243 bytes toxygen/smileys/default/D83DDCD9.png | Bin 1250 -> 1242 bytes toxygen/smileys/default/D83DDCDA.png | Bin 1647 -> 1652 bytes toxygen/smileys/default/D83DDCDB.png | Bin 1607 -> 1624 bytes toxygen/smileys/default/D83DDCDC.png | Bin 1297 -> 1184 bytes toxygen/smileys/default/D83DDCDD.png | Bin 1562 -> 1559 bytes toxygen/smileys/default/D83DDCDE.png | Bin 1438 -> 1642 bytes toxygen/smileys/default/D83DDCDF.png | Bin 1376 -> 1271 bytes toxygen/smileys/default/D83DDCE0.png | Bin 1389 -> 1326 bytes toxygen/smileys/default/D83DDCE1.png | Bin 1720 -> 1813 bytes toxygen/smileys/default/D83DDCE2.png | Bin 1683 -> 1727 bytes toxygen/smileys/default/D83DDCE3.png | Bin 1593 -> 1684 bytes toxygen/smileys/default/D83DDCE4.png | Bin 1466 -> 1467 bytes toxygen/smileys/default/D83DDCE5.png | Bin 1473 -> 1460 bytes toxygen/smileys/default/D83DDCE6.png | Bin 1692 -> 1858 bytes toxygen/smileys/default/D83DDCE7.png | Bin 1398 -> 1340 bytes toxygen/smileys/default/D83DDCE8.png | Bin 1372 -> 1366 bytes toxygen/smileys/default/D83DDCE9.png | Bin 1406 -> 1356 bytes toxygen/smileys/default/D83DDCEA.png | Bin 1410 -> 1482 bytes toxygen/smileys/default/D83DDCEB.png | Bin 1444 -> 1529 bytes toxygen/smileys/default/D83DDCEC.png | Bin 1495 -> 1540 bytes toxygen/smileys/default/D83DDCED.png | Bin 1435 -> 1455 bytes toxygen/smileys/default/D83DDCEE.png | Bin 1466 -> 1430 bytes toxygen/smileys/default/D83DDCEF.png | Bin 1624 -> 1616 bytes toxygen/smileys/default/D83DDCF0.png | Bin 1306 -> 1284 bytes toxygen/smileys/default/D83DDCF1.png | Bin 1526 -> 1631 bytes toxygen/smileys/default/D83DDCF2.png | Bin 1592 -> 1586 bytes toxygen/smileys/default/D83DDCF3.png | Bin 1460 -> 1421 bytes toxygen/smileys/default/D83DDCF4.png | Bin 1281 -> 1292 bytes toxygen/smileys/default/D83DDCF5.png | Bin 1584 -> 1665 bytes toxygen/smileys/default/D83DDCF6.png | Bin 1173 -> 1197 bytes toxygen/smileys/default/D83DDCF7.png | Bin 1516 -> 1513 bytes toxygen/smileys/default/D83DDCF9.png | Bin 1582 -> 1541 bytes toxygen/smileys/default/D83DDCFA.png | Bin 1492 -> 1404 bytes toxygen/smileys/default/D83DDCFB.png | Bin 1490 -> 1393 bytes toxygen/smileys/default/D83DDCFC.png | Bin 1285 -> 1173 bytes toxygen/smileys/default/D83DDD00.png | Bin 1391 -> 1356 bytes toxygen/smileys/default/D83DDD01.png | Bin 1396 -> 1387 bytes toxygen/smileys/default/D83DDD02.png | Bin 1424 -> 1407 bytes toxygen/smileys/default/D83DDD03.png | Bin 1326 -> 1562 bytes toxygen/smileys/default/D83DDD04.png | Bin 1452 -> 1392 bytes toxygen/smileys/default/D83DDD05.png | Bin 1330 -> 1253 bytes toxygen/smileys/default/D83DDD06.png | Bin 1526 -> 1443 bytes toxygen/smileys/default/D83DDD07.png | Bin 1902 -> 1961 bytes toxygen/smileys/default/D83DDD09.png | Bin 1915 -> 2009 bytes toxygen/smileys/default/D83DDD0B.png | Bin 1342 -> 1204 bytes toxygen/smileys/default/D83DDD0C.png | Bin 1570 -> 1591 bytes toxygen/smileys/default/D83DDD0D.png | Bin 1574 -> 1583 bytes toxygen/smileys/default/D83DDD0E.png | Bin 1559 -> 1588 bytes toxygen/smileys/default/D83DDD0F.png | Bin 1626 -> 1596 bytes toxygen/smileys/default/D83DDD10.png | Bin 1572 -> 1622 bytes toxygen/smileys/default/D83DDD11.png | Bin 1570 -> 1574 bytes toxygen/smileys/default/D83DDD12.png | Bin 1371 -> 1290 bytes toxygen/smileys/default/D83DDD13.png | Bin 1398 -> 1323 bytes toxygen/smileys/default/D83DDD14.png | Bin 1637 -> 1761 bytes toxygen/smileys/default/D83DDD15.png | Bin 1703 -> 1787 bytes toxygen/smileys/default/D83DDD16.png | Bin 1568 -> 1560 bytes toxygen/smileys/default/D83DDD17.png | Bin 1712 -> 1694 bytes toxygen/smileys/default/D83DDD18.png | Bin 1567 -> 1316 bytes toxygen/smileys/default/D83DDD19.png | Bin 1286 -> 1499 bytes toxygen/smileys/default/D83DDD1A.png | Bin 1246 -> 1379 bytes toxygen/smileys/default/D83DDD1B.png | Bin 1313 -> 1499 bytes toxygen/smileys/default/D83DDD1C.png | Bin 1263 -> 1465 bytes toxygen/smileys/default/D83DDD1D.png | Bin 1244 -> 1424 bytes toxygen/smileys/default/D83DDD1E.png | Bin 1583 -> 1653 bytes toxygen/smileys/default/D83DDD1F.png | Bin 1344 -> 1347 bytes toxygen/smileys/default/D83DDD20.png | Bin 1489 -> 1435 bytes toxygen/smileys/default/D83DDD21.png | Bin 1368 -> 1383 bytes toxygen/smileys/default/D83DDD22.png | Bin 1372 -> 1340 bytes toxygen/smileys/default/D83DDD23.png | Bin 1278 -> 1535 bytes toxygen/smileys/default/D83DDD24.png | Bin 1302 -> 1305 bytes toxygen/smileys/default/D83DDD25.png | Bin 1534 -> 1832 bytes toxygen/smileys/default/D83DDD26.png | Bin 1597 -> 1555 bytes toxygen/smileys/default/D83DDD27.png | Bin 1611 -> 1573 bytes toxygen/smileys/default/D83DDD28.png | Bin 1440 -> 1444 bytes toxygen/smileys/default/D83DDD29.png | Bin 1590 -> 1503 bytes toxygen/smileys/default/D83DDD2A.png | Bin 1384 -> 1211 bytes toxygen/smileys/default/D83DDD2B.png | Bin 1422 -> 1403 bytes toxygen/smileys/default/D83DDD2C.png | Bin 1530 -> 1421 bytes toxygen/smileys/default/D83DDD2D.png | Bin 1496 -> 1469 bytes toxygen/smileys/default/D83DDD2E.png | Bin 1824 -> 1863 bytes toxygen/smileys/default/D83DDD31.png | Bin 1598 -> 1513 bytes toxygen/smileys/default/D83DDD32.png | Bin 1327 -> 1146 bytes toxygen/smileys/default/D83DDD33.png | Bin 1249 -> 1122 bytes toxygen/smileys/default/D83DDD34.png | Bin 1453 -> 1541 bytes toxygen/smileys/default/D83DDD35.png | Bin 1444 -> 1516 bytes toxygen/smileys/default/D83DDD36.png | Bin 1490 -> 1468 bytes toxygen/smileys/default/D83DDD37.png | Bin 1531 -> 1462 bytes toxygen/smileys/default/D83DDD38.png | Bin 1257 -> 1198 bytes toxygen/smileys/default/D83DDD39.png | Bin 1272 -> 1192 bytes toxygen/smileys/default/D83DDD3A.png | Bin 1177 -> 1127 bytes toxygen/smileys/default/D83DDD3B.png | Bin 1179 -> 1130 bytes toxygen/smileys/default/D83DDD3C.png | Bin 1259 -> 1291 bytes toxygen/smileys/default/D83DDD3D.png | Bin 1279 -> 1290 bytes toxygen/smileys/default/D83DDDFB.png | Bin 1372 -> 1424 bytes toxygen/smileys/default/D83DDDFC.png | Bin 1389 -> 1359 bytes toxygen/smileys/default/D83DDDFD.png | Bin 1608 -> 1690 bytes toxygen/smileys/default/D83DDDFE.png | Bin 1297 -> 1283 bytes toxygen/smileys/default/D83DDDFF.png | Bin 1499 -> 1252 bytes toxygen/smileys/default/D83DDE00.png | Bin 1639 -> 1645 bytes toxygen/smileys/default/D83DDE01.png | Bin 1607 -> 1649 bytes toxygen/smileys/default/D83DDE02.png | Bin 1774 -> 1767 bytes toxygen/smileys/default/D83DDE03.png | Bin 1699 -> 1718 bytes toxygen/smileys/default/D83DDE04.png | Bin 1660 -> 1676 bytes toxygen/smileys/default/D83DDE05.png | Bin 1698 -> 1721 bytes toxygen/smileys/default/D83DDE06.png | Bin 1721 -> 1734 bytes toxygen/smileys/default/D83DDE07.png | Bin 1685 -> 1761 bytes toxygen/smileys/default/D83DDE08.png | Bin 1860 -> 1856 bytes toxygen/smileys/default/D83DDE09.png | Bin 1630 -> 1703 bytes toxygen/smileys/default/D83DDE0A.png | Bin 1681 -> 1763 bytes toxygen/smileys/default/D83DDE0B.png | Bin 1633 -> 1713 bytes toxygen/smileys/default/D83DDE0C.png | Bin 1643 -> 1677 bytes toxygen/smileys/default/D83DDE0D.png | Bin 1676 -> 1746 bytes toxygen/smileys/default/D83DDE0E.png | Bin 1641 -> 1652 bytes toxygen/smileys/default/D83DDE0F.png | Bin 1604 -> 1658 bytes toxygen/smileys/default/D83DDE10.png | Bin 1549 -> 1597 bytes toxygen/smileys/default/D83DDE11.png | Bin 1557 -> 1612 bytes toxygen/smileys/default/D83DDE12.png | Bin 1715 -> 1724 bytes toxygen/smileys/default/D83DDE13.png | Bin 1656 -> 1720 bytes toxygen/smileys/default/D83DDE14.png | Bin 1623 -> 1661 bytes toxygen/smileys/default/D83DDE15.png | Bin 1585 -> 1655 bytes toxygen/smileys/default/D83DDE16.png | Bin 1685 -> 1717 bytes toxygen/smileys/default/D83DDE17.png | Bin 1528 -> 1599 bytes toxygen/smileys/default/D83DDE18.png | Bin 1659 -> 1758 bytes toxygen/smileys/default/D83DDE19.png | Bin 1561 -> 1631 bytes toxygen/smileys/default/D83DDE1A.png | Bin 1712 -> 1760 bytes toxygen/smileys/default/D83DDE1B.png | Bin 1578 -> 1593 bytes toxygen/smileys/default/D83DDE1C.png | Bin 1663 -> 1693 bytes toxygen/smileys/default/D83DDE1D.png | Bin 1659 -> 1693 bytes toxygen/smileys/default/D83DDE1E.png | Bin 1563 -> 1614 bytes toxygen/smileys/default/D83DDE1F.png | Bin 1625 -> 1649 bytes toxygen/smileys/default/D83DDE20.png | Bin 1625 -> 1654 bytes toxygen/smileys/default/D83DDE21.png | Bin 1712 -> 1726 bytes toxygen/smileys/default/D83DDE22.png | Bin 1657 -> 1644 bytes toxygen/smileys/default/D83DDE23.png | Bin 1636 -> 1677 bytes toxygen/smileys/default/D83DDE24.png | Bin 1716 -> 1736 bytes toxygen/smileys/default/D83DDE25.png | Bin 1619 -> 1663 bytes toxygen/smileys/default/D83DDE26.png | Bin 1559 -> 1607 bytes toxygen/smileys/default/D83DDE27.png | Bin 1600 -> 1644 bytes toxygen/smileys/default/D83DDE28.png | Bin 1700 -> 1726 bytes toxygen/smileys/default/D83DDE29.png | Bin 1694 -> 1749 bytes toxygen/smileys/default/D83DDE2A.png | Bin 1685 -> 1722 bytes toxygen/smileys/default/D83DDE2B.png | Bin 1722 -> 1741 bytes toxygen/smileys/default/D83DDE2C.png | Bin 1585 -> 1622 bytes toxygen/smileys/default/D83DDE2D.png | Bin 1719 -> 1785 bytes toxygen/smileys/default/D83DDE2E.png | Bin 1552 -> 1597 bytes toxygen/smileys/default/D83DDE2F.png | Bin 1572 -> 1620 bytes toxygen/smileys/default/D83DDE30.png | Bin 1741 -> 1702 bytes toxygen/smileys/default/D83DDE31.png | Bin 1795 -> 1796 bytes toxygen/smileys/default/D83DDE32.png | Bin 1658 -> 1678 bytes toxygen/smileys/default/D83DDE33.png | Bin 1759 -> 1805 bytes toxygen/smileys/default/D83DDE34.png | Bin 1599 -> 1654 bytes toxygen/smileys/default/D83DDE35.png | Bin 1651 -> 1685 bytes toxygen/smileys/default/D83DDE36.png | Bin 1509 -> 1579 bytes toxygen/smileys/default/D83DDE37.png | Bin 1679 -> 1667 bytes toxygen/smileys/default/D83DDE38.png | Bin 1695 -> 1740 bytes toxygen/smileys/default/D83DDE39.png | Bin 1755 -> 1801 bytes toxygen/smileys/default/D83DDE3A.png | Bin 1644 -> 1711 bytes toxygen/smileys/default/D83DDE3B.png | Bin 1668 -> 1721 bytes toxygen/smileys/default/D83DDE3C.png | Bin 1654 -> 1696 bytes toxygen/smileys/default/D83DDE3D.png | Bin 1716 -> 1782 bytes toxygen/smileys/default/D83DDE3E.png | Bin 1583 -> 1727 bytes toxygen/smileys/default/D83DDE3F.png | Bin 1679 -> 1733 bytes toxygen/smileys/default/D83DDE40.png | Bin 1747 -> 1792 bytes toxygen/smileys/default/D83DDE45.png | Bin 1783 -> 1757 bytes toxygen/smileys/default/D83DDE46.png | Bin 1858 -> 1806 bytes toxygen/smileys/default/D83DDE47.png | Bin 1659 -> 1661 bytes toxygen/smileys/default/D83DDE48.png | Bin 1785 -> 1778 bytes toxygen/smileys/default/D83DDE49.png | Bin 1747 -> 1728 bytes toxygen/smileys/default/D83DDE4A.png | Bin 1750 -> 1789 bytes toxygen/smileys/default/D83DDE4B.png | Bin 1826 -> 1886 bytes toxygen/smileys/default/D83DDE4C.png | Bin 1629 -> 1557 bytes toxygen/smileys/default/D83DDE4D.png | Bin 1645 -> 1667 bytes toxygen/smileys/default/D83DDE4E.png | Bin 1699 -> 1710 bytes toxygen/smileys/default/D83DDE4F.png | Bin 1590 -> 1567 bytes toxygen/smileys/default/D83DDE80.png | Bin 1679 -> 1695 bytes toxygen/smileys/default/D83DDE81.png | Bin 1571 -> 1552 bytes toxygen/smileys/default/D83DDE82.png | Bin 1516 -> 1430 bytes toxygen/smileys/default/D83DDE83.png | Bin 1277 -> 1240 bytes toxygen/smileys/default/D83DDE84.png | Bin 1561 -> 1633 bytes toxygen/smileys/default/D83DDE85.png | Bin 1474 -> 1488 bytes toxygen/smileys/default/D83DDE86.png | Bin 1698 -> 1611 bytes toxygen/smileys/default/D83DDE87.png | Bin 1600 -> 1459 bytes toxygen/smileys/default/D83DDE88.png | Bin 1531 -> 1540 bytes toxygen/smileys/default/D83DDE89.png | Bin 1681 -> 1550 bytes toxygen/smileys/default/D83DDE8A.png | Bin 1615 -> 1470 bytes toxygen/smileys/default/D83DDE8B.png | Bin 1317 -> 1257 bytes toxygen/smileys/default/D83DDE8C.png | Bin 1350 -> 1289 bytes toxygen/smileys/default/D83DDE8D.png | Bin 1657 -> 1577 bytes toxygen/smileys/default/D83DDE8E.png | Bin 1399 -> 1359 bytes toxygen/smileys/default/D83DDE8F.png | Bin 1310 -> 1233 bytes toxygen/smileys/default/D83DDE90.png | Bin 1463 -> 1462 bytes toxygen/smileys/default/D83DDE91.png | Bin 1528 -> 1547 bytes toxygen/smileys/default/D83DDE92.png | Bin 1562 -> 1490 bytes toxygen/smileys/default/D83DDE93.png | Bin 1559 -> 1531 bytes toxygen/smileys/default/D83DDE94.png | Bin 1628 -> 1594 bytes toxygen/smileys/default/D83DDE95.png | Bin 1491 -> 1477 bytes toxygen/smileys/default/D83DDE96.png | Bin 1618 -> 1615 bytes toxygen/smileys/default/D83DDE97.png | Bin 1502 -> 1568 bytes toxygen/smileys/default/D83DDE98.png | Bin 1577 -> 1602 bytes toxygen/smileys/default/D83DDE99.png | Bin 1513 -> 1546 bytes toxygen/smileys/default/D83DDE9A.png | Bin 1438 -> 1385 bytes toxygen/smileys/default/D83DDE9B.png | Bin 1398 -> 1335 bytes toxygen/smileys/default/D83DDE9C.png | Bin 1658 -> 1628 bytes toxygen/smileys/default/D83DDE9D.png | Bin 1453 -> 1431 bytes toxygen/smileys/default/D83DDE9E.png | Bin 1441 -> 1441 bytes toxygen/smileys/default/D83DDE9F.png | Bin 1318 -> 1261 bytes toxygen/smileys/default/D83DDEA0.png | Bin 1515 -> 1394 bytes toxygen/smileys/default/D83DDEA1.png | Bin 1513 -> 1457 bytes toxygen/smileys/default/D83DDEA2.png | Bin 1314 -> 1301 bytes toxygen/smileys/default/D83DDEA3.png | Bin 1458 -> 1408 bytes toxygen/smileys/default/D83DDEA4.png | Bin 1413 -> 1431 bytes toxygen/smileys/default/D83DDEA5.png | Bin 1462 -> 1385 bytes toxygen/smileys/default/D83DDEA6.png | Bin 1452 -> 1425 bytes toxygen/smileys/default/D83DDEA7.png | Bin 1351 -> 1298 bytes toxygen/smileys/default/D83DDEA8.png | Bin 1811 -> 1872 bytes toxygen/smileys/default/D83DDEA9.png | Bin 1256 -> 1253 bytes toxygen/smileys/default/D83DDEAA.png | Bin 1363 -> 1334 bytes toxygen/smileys/default/D83DDEAB.png | Bin 1798 -> 1869 bytes toxygen/smileys/default/D83DDEAC.png | Bin 1558 -> 1528 bytes toxygen/smileys/default/D83DDEAD.png | Bin 1435 -> 1415 bytes toxygen/smileys/default/D83DDEAE.png | Bin 1358 -> 1334 bytes toxygen/smileys/default/D83DDEAF.png | Bin 1621 -> 1693 bytes toxygen/smileys/default/D83DDEB0.png | Bin 1310 -> 1322 bytes toxygen/smileys/default/D83DDEB1.png | Bin 1637 -> 1706 bytes toxygen/smileys/default/D83DDEB2.png | Bin 1429 -> 1513 bytes toxygen/smileys/default/D83DDEB3.png | Bin 1647 -> 1721 bytes toxygen/smileys/default/D83DDEB4.png | Bin 1636 -> 1685 bytes toxygen/smileys/default/D83DDEB5.png | Bin 1766 -> 1716 bytes toxygen/smileys/default/D83DDEB6.png | Bin 1312 -> 1303 bytes toxygen/smileys/default/D83DDEB7.png | Bin 1643 -> 1730 bytes toxygen/smileys/default/D83DDEB8.png | Bin 1802 -> 1786 bytes toxygen/smileys/default/D83DDEB9.png | Bin 1269 -> 1285 bytes toxygen/smileys/default/D83DDEBA.png | Bin 1318 -> 1309 bytes toxygen/smileys/default/D83DDEBB.png | Bin 1348 -> 1319 bytes toxygen/smileys/default/D83DDEBC.png | Bin 1298 -> 1314 bytes toxygen/smileys/default/D83DDEBD.png | Bin 1435 -> 1431 bytes toxygen/smileys/default/D83DDEBE.png | Bin 1367 -> 1398 bytes toxygen/smileys/default/D83DDEBF.png | Bin 1624 -> 1838 bytes toxygen/smileys/default/D83DDEC0.png | Bin 1405 -> 1385 bytes toxygen/smileys/default/D83DDEC1.png | Bin 1360 -> 1346 bytes toxygen/smileys/default/D83DDEC2.png | Bin 1380 -> 1366 bytes toxygen/smileys/default/D83DDEC3.png | Bin 1317 -> 1329 bytes toxygen/smileys/default/D83DDEC4.png | Bin 1251 -> 1244 bytes toxygen/smileys/default/D83DDEC5.png | Bin 1337 -> 1371 bytes toxygen/smileys/default/ad.png | Bin 643 -> 977 bytes toxygen/smileys/default/ae.png | Bin 408 -> 781 bytes toxygen/smileys/default/af.png | Bin 604 -> 945 bytes toxygen/smileys/default/ag.png | Bin 591 -> 920 bytes toxygen/smileys/default/ai.png | Bin 643 -> 974 bytes toxygen/smileys/default/al.png | Bin 600 -> 949 bytes toxygen/smileys/default/am.png | Bin 497 -> 957 bytes toxygen/smileys/default/an.png | Bin 488 -> 811 bytes toxygen/smileys/default/ao.png | Bin 428 -> 828 bytes toxygen/smileys/default/ar.png | Bin 506 -> 937 bytes toxygen/smileys/default/as.png | Bin 647 -> 978 bytes toxygen/smileys/default/at.png | Bin 403 -> 856 bytes toxygen/smileys/default/au.png | Bin 673 -> 995 bytes toxygen/smileys/default/aw.png | Bin 524 -> 1004 bytes toxygen/smileys/default/ax.png | Bin 663 -> 1013 bytes toxygen/smileys/default/az.png | Bin 589 -> 1001 bytes toxygen/smileys/default/ba.png | Bin 593 -> 960 bytes toxygen/smileys/default/bb.png | Bin 585 -> 975 bytes toxygen/smileys/default/bd.png | Bin 504 -> 856 bytes toxygen/smileys/default/be.png | Bin 449 -> 886 bytes toxygen/smileys/default/bf.png | Bin 497 -> 933 bytes toxygen/smileys/default/bg.png | Bin 462 -> 911 bytes toxygen/smileys/default/bh.png | Bin 457 -> 836 bytes toxygen/smileys/default/bi.png | Bin 675 -> 962 bytes toxygen/smileys/default/bj.png | Bin 486 -> 906 bytes toxygen/smileys/default/bm.png | Bin 611 -> 969 bytes toxygen/smileys/default/bn.png | Bin 639 -> 958 bytes toxygen/smileys/default/bo.png | Bin 500 -> 957 bytes toxygen/smileys/default/br.png | Bin 593 -> 930 bytes toxygen/smileys/default/bs.png | Bin 526 -> 942 bytes toxygen/smileys/default/bt.png | Bin 631 -> 946 bytes toxygen/smileys/default/bv.png | Bin 512 -> 860 bytes toxygen/smileys/default/bw.png | Bin 443 -> 895 bytes toxygen/smileys/default/by.png | Bin 514 -> 927 bytes toxygen/smileys/default/bz.png | Bin 600 -> 961 bytes toxygen/smileys/default/ca.png | Bin 628 -> 926 bytes toxygen/smileys/default/catalonia.png | Bin 398 -> 890 bytes toxygen/smileys/default/cc.png | Bin 625 -> 945 bytes toxygen/smileys/default/cd.png | Bin 528 -> 826 bytes toxygen/smileys/default/cf.png | Bin 614 -> 969 bytes toxygen/smileys/default/cg.png | Bin 521 -> 861 bytes toxygen/smileys/default/ch.png | Bin 367 -> 615 bytes toxygen/smileys/default/ci.png | Bin 453 -> 832 bytes toxygen/smileys/default/ck.png | Bin 586 -> 930 bytes toxygen/smileys/default/cl.png | Bin 450 -> 836 bytes toxygen/smileys/default/cm.png | Bin 525 -> 957 bytes toxygen/smileys/default/cn.png | Bin 472 -> 890 bytes toxygen/smileys/default/co.png | Bin 483 -> 964 bytes toxygen/smileys/default/cr.png | Bin 477 -> 974 bytes toxygen/smileys/default/cs.png | Bin 439 -> 892 bytes toxygen/smileys/default/cu.png | Bin 563 -> 973 bytes toxygen/smileys/default/cv.png | Bin 529 -> 899 bytes toxygen/smileys/default/cx.png | Bin 608 -> 998 bytes toxygen/smileys/default/cy.png | Bin 428 -> 687 bytes toxygen/smileys/default/cz.png | Bin 476 -> 850 bytes toxygen/smileys/default/de.png | Bin 545 -> 966 bytes toxygen/smileys/default/dj.png | Bin 572 -> 951 bytes toxygen/smileys/default/dk.png | Bin 495 -> 853 bytes toxygen/smileys/default/dm.png | Bin 620 -> 928 bytes toxygen/smileys/default/do.png | Bin 508 -> 911 bytes toxygen/smileys/default/dz.png | Bin 582 -> 900 bytes toxygen/smileys/default/ec.png | Bin 500 -> 952 bytes toxygen/smileys/default/ee.png | Bin 429 -> 819 bytes toxygen/smileys/default/eg.png | Bin 465 -> 852 bytes toxygen/smileys/default/eh.png | Bin 508 -> 916 bytes toxygen/smileys/default/england.png | Bin 496 -> 790 bytes toxygen/smileys/default/er.png | Bin 653 -> 995 bytes toxygen/smileys/default/es.png | Bin 469 -> 889 bytes toxygen/smileys/default/et.png | Bin 592 -> 971 bytes toxygen/smileys/default/europeanunion.png | Bin 479 -> 902 bytes toxygen/smileys/default/fam.png | Bin 532 -> 971 bytes toxygen/smileys/default/fi.png | Bin 489 -> 859 bytes toxygen/smileys/default/fj.png | Bin 610 -> 989 bytes toxygen/smileys/default/fk.png | Bin 648 -> 1001 bytes toxygen/smileys/default/fm.png | Bin 552 -> 905 bytes toxygen/smileys/default/fo.png | Bin 474 -> 780 bytes toxygen/smileys/default/fr.png | Bin 545 -> 873 bytes toxygen/smileys/default/ga.png | Bin 489 -> 960 bytes toxygen/smileys/default/gb.png | Bin 599 -> 893 bytes toxygen/smileys/default/gd.png | Bin 637 -> 960 bytes toxygen/smileys/default/ge.png | Bin 594 -> 865 bytes toxygen/smileys/default/gf.png | Bin 545 -> 873 bytes toxygen/smileys/default/gh.png | Bin 490 -> 930 bytes toxygen/smileys/default/gi.png | Bin 463 -> 816 bytes toxygen/smileys/default/gl.png | Bin 470 -> 847 bytes toxygen/smileys/default/gm.png | Bin 493 -> 990 bytes toxygen/smileys/default/gn.png | Bin 480 -> 924 bytes toxygen/smileys/default/gp.png | Bin 488 -> 930 bytes toxygen/smileys/default/gq.png | Bin 537 -> 963 bytes toxygen/smileys/default/gr.png | Bin 487 -> 942 bytes toxygen/smileys/default/gs.png | Bin 630 -> 932 bytes toxygen/smileys/default/gt.png | Bin 493 -> 896 bytes toxygen/smileys/default/gu.png | Bin 509 -> 893 bytes toxygen/smileys/default/gw.png | Bin 516 -> 946 bytes toxygen/smileys/default/gy.png | Bin 645 -> 989 bytes toxygen/smileys/default/hk.png | Bin 527 -> 898 bytes toxygen/smileys/default/hm.png | Bin 673 -> 995 bytes toxygen/smileys/default/hn.png | Bin 537 -> 949 bytes toxygen/smileys/default/hr.png | Bin 524 -> 875 bytes toxygen/smileys/default/ht.png | Bin 487 -> 965 bytes toxygen/smileys/default/hu.png | Bin 432 -> 827 bytes toxygen/smileys/default/id.png | Bin 430 -> 803 bytes toxygen/smileys/default/ie.png | Bin 481 -> 887 bytes toxygen/smileys/default/il.png | Bin 431 -> 744 bytes toxygen/smileys/default/in.png | Bin 503 -> 955 bytes toxygen/smileys/default/io.png | Bin 658 -> 1004 bytes toxygen/smileys/default/iq.png | Bin 515 -> 897 bytes toxygen/smileys/default/ir.png | Bin 512 -> 931 bytes toxygen/smileys/default/is.png | Bin 532 -> 873 bytes toxygen/smileys/default/it.png | Bin 420 -> 808 bytes toxygen/smileys/default/jm.png | Bin 637 -> 955 bytes toxygen/smileys/default/jo.png | Bin 473 -> 821 bytes toxygen/smileys/default/jp.png | Bin 420 -> 703 bytes toxygen/smileys/default/ke.png | Bin 569 -> 937 bytes toxygen/smileys/default/kg.png | Bin 510 -> 882 bytes toxygen/smileys/default/kh.png | Bin 549 -> 972 bytes toxygen/smileys/default/ki.png | Bin 656 -> 984 bytes toxygen/smileys/default/km.png | Bin 577 -> 935 bytes toxygen/smileys/default/kn.png | Bin 604 -> 927 bytes toxygen/smileys/default/kp.png | Bin 561 -> 965 bytes toxygen/smileys/default/kr.png | Bin 592 -> 848 bytes toxygen/smileys/default/kw.png | Bin 486 -> 901 bytes toxygen/smileys/default/ky.png | Bin 643 -> 942 bytes toxygen/smileys/default/kz.png | Bin 616 -> 980 bytes toxygen/smileys/default/la.png | Bin 563 -> 943 bytes toxygen/smileys/default/lb.png | Bin 517 -> 900 bytes toxygen/smileys/default/lc.png | Bin 520 -> 951 bytes toxygen/smileys/default/li.png | Bin 537 -> 977 bytes toxygen/smileys/default/lk.png | Bin 627 -> 969 bytes toxygen/smileys/default/lr.png | Bin 466 -> 794 bytes toxygen/smileys/default/ls.png | Bin 628 -> 911 bytes toxygen/smileys/default/lt.png | Bin 508 -> 975 bytes toxygen/smileys/default/lu.png | Bin 481 -> 913 bytes toxygen/smileys/default/lv.png | Bin 465 -> 962 bytes toxygen/smileys/default/ly.png | Bin 419 -> 882 bytes toxygen/smileys/default/ma.png | Bin 432 -> 849 bytes toxygen/smileys/default/mc.png | Bin 380 -> 783 bytes toxygen/smileys/default/md.png | Bin 566 -> 959 bytes toxygen/smileys/default/me.png | Bin 448 -> 930 bytes toxygen/smileys/default/mg.png | Bin 453 -> 882 bytes toxygen/smileys/default/mh.png | Bin 628 -> 965 bytes toxygen/smileys/default/mk.png | Bin 664 -> 986 bytes toxygen/smileys/default/ml.png | Bin 474 -> 935 bytes toxygen/smileys/default/mm.png | Bin 483 -> 873 bytes toxygen/smileys/default/mn.png | Bin 492 -> 911 bytes toxygen/smileys/default/mo.png | Bin 588 -> 959 bytes toxygen/smileys/default/mp.png | Bin 597 -> 947 bytes toxygen/smileys/default/mq.png | Bin 655 -> 941 bytes toxygen/smileys/default/mr.png | Bin 569 -> 960 bytes toxygen/smileys/default/ms.png | Bin 614 -> 973 bytes toxygen/smileys/default/mt.png | Bin 420 -> 814 bytes toxygen/smileys/default/mu.png | Bin 496 -> 973 bytes toxygen/smileys/default/mv.png | Bin 542 -> 935 bytes toxygen/smileys/default/mw.png | Bin 529 -> 926 bytes toxygen/smileys/default/mx.png | Bin 574 -> 953 bytes toxygen/smileys/default/my.png | Bin 571 -> 966 bytes toxygen/smileys/default/mz.png | Bin 584 -> 1007 bytes toxygen/smileys/default/na.png | Bin 647 -> 969 bytes toxygen/smileys/default/nc.png | Bin 591 -> 953 bytes toxygen/smileys/default/ne.png | Bin 537 -> 954 bytes toxygen/smileys/default/nf.png | Bin 602 -> 927 bytes toxygen/smileys/default/ng.png | Bin 482 -> 881 bytes toxygen/smileys/default/ni.png | Bin 508 -> 928 bytes toxygen/smileys/default/nl.png | Bin 453 -> 884 bytes toxygen/smileys/default/no.png | Bin 512 -> 860 bytes toxygen/smileys/default/np.png | Bin 443 -> 617 bytes toxygen/smileys/default/nr.png | Bin 527 -> 963 bytes toxygen/smileys/default/nu.png | Bin 572 -> 934 bytes toxygen/smileys/default/nz.png | Bin 639 -> 986 bytes toxygen/smileys/default/om.png | Bin 478 -> 883 bytes toxygen/smileys/default/pa.png | Bin 519 -> 875 bytes toxygen/smileys/default/pe.png | Bin 397 -> 762 bytes toxygen/smileys/default/pf.png | Bin 498 -> 887 bytes toxygen/smileys/default/pg.png | Bin 593 -> 898 bytes toxygen/smileys/default/ph.png | Bin 538 -> 960 bytes toxygen/smileys/default/pk.png | Bin 569 -> 923 bytes toxygen/smileys/default/pl.png | Bin 374 -> 777 bytes toxygen/smileys/default/pm.png | Bin 689 -> 1004 bytes toxygen/smileys/default/pn.png | Bin 657 -> 976 bytes toxygen/smileys/default/pr.png | Bin 556 -> 962 bytes toxygen/smileys/default/ps.png | Bin 472 -> 800 bytes toxygen/smileys/default/pt.png | Bin 554 -> 923 bytes toxygen/smileys/default/pw.png | Bin 550 -> 942 bytes toxygen/smileys/default/py.png | Bin 473 -> 926 bytes toxygen/smileys/default/qa.png | Bin 450 -> 794 bytes toxygen/smileys/default/re.png | Bin 545 -> 873 bytes toxygen/smileys/default/ro.png | Bin 495 -> 948 bytes toxygen/smileys/default/rs.png | Bin 423 -> 829 bytes toxygen/smileys/default/ru.png | Bin 420 -> 885 bytes toxygen/smileys/default/rw.png | Bin 533 -> 964 bytes toxygen/smileys/default/sa.png | Bin 551 -> 895 bytes toxygen/smileys/default/sb.png | Bin 624 -> 914 bytes toxygen/smileys/default/sc.png | Bin 608 -> 963 bytes toxygen/smileys/default/scotland.png | Bin 649 -> 915 bytes toxygen/smileys/default/sd.png | Bin 492 -> 878 bytes toxygen/smileys/default/se.png | Bin 542 -> 917 bytes toxygen/smileys/default/sg.png | Bin 468 -> 833 bytes toxygen/smileys/default/sh.png | Bin 645 -> 984 bytes toxygen/smileys/default/si.png | Bin 510 -> 930 bytes toxygen/smileys/default/sj.png | Bin 512 -> 860 bytes toxygen/smileys/default/sk.png | Bin 562 -> 942 bytes toxygen/smileys/default/sl.png | Bin 436 -> 887 bytes toxygen/smileys/default/sm.png | Bin 502 -> 881 bytes toxygen/smileys/default/sn.png | Bin 532 -> 949 bytes toxygen/smileys/default/so.png | Bin 527 -> 962 bytes toxygen/smileys/default/sr.png | Bin 513 -> 974 bytes toxygen/smileys/default/st.png | Bin 584 -> 998 bytes toxygen/smileys/default/sv.png | Bin 501 -> 914 bytes toxygen/smileys/default/sy.png | Bin 422 -> 789 bytes toxygen/smileys/default/sz.png | Bin 643 -> 1001 bytes toxygen/smileys/default/tc.png | Bin 624 -> 980 bytes toxygen/smileys/default/td.png | Bin 570 -> 983 bytes toxygen/smileys/default/tf.png | Bin 527 -> 919 bytes toxygen/smileys/default/tg.png | Bin 562 -> 958 bytes toxygen/smileys/default/th.png | Bin 452 -> 908 bytes toxygen/smileys/default/tj.png | Bin 496 -> 822 bytes toxygen/smileys/default/tk.png | Bin 638 -> 959 bytes toxygen/smileys/default/tl.png | Bin 514 -> 887 bytes toxygen/smileys/default/tm.png | Bin 593 -> 913 bytes toxygen/smileys/default/tn.png | Bin 495 -> 895 bytes toxygen/smileys/default/to.png | Bin 426 -> 826 bytes toxygen/smileys/default/tox.png | Bin 3339 -> 942 bytes toxygen/smileys/default/tr.png | Bin 492 -> 886 bytes toxygen/smileys/default/tt.png | Bin 617 -> 927 bytes toxygen/smileys/default/tv.png | Bin 536 -> 903 bytes toxygen/smileys/default/tw.png | Bin 465 -> 875 bytes toxygen/smileys/default/tz.png | Bin 642 -> 966 bytes toxygen/smileys/default/ua.png | Bin 446 -> 931 bytes toxygen/smileys/default/ug.png | Bin 531 -> 932 bytes toxygen/smileys/default/um.png | Bin 571 -> 877 bytes toxygen/smileys/default/us.png | Bin 609 -> 894 bytes toxygen/smileys/default/uy.png | Bin 532 -> 977 bytes toxygen/smileys/default/uz.png | Bin 515 -> 897 bytes toxygen/smileys/default/va.png | Bin 553 -> 919 bytes toxygen/smileys/default/vc.png | Bin 577 -> 987 bytes toxygen/smileys/default/ve.png | Bin 528 -> 969 bytes toxygen/smileys/default/vg.png | Bin 630 -> 989 bytes toxygen/smileys/default/vi.png | Bin 616 -> 916 bytes toxygen/smileys/default/vn.png | Bin 474 -> 861 bytes toxygen/smileys/default/vu.png | Bin 604 -> 958 bytes toxygen/smileys/default/wales.png | Bin 652 -> 977 bytes toxygen/smileys/default/wf.png | Bin 554 -> 897 bytes toxygen/smileys/default/ws.png | Bin 476 -> 879 bytes toxygen/smileys/default/wtox.png | Bin 3075 -> 587 bytes toxygen/smileys/default/ye.png | Bin 413 -> 820 bytes toxygen/smileys/default/yt.png | Bin 593 -> 884 bytes toxygen/smileys/default/za.png | Bin 642 -> 1001 bytes toxygen/smileys/default/zm.png | Bin 500 -> 919 bytes toxygen/smileys/default/zw.png | Bin 574 -> 995 bytes 1044 files changed, 80 insertions(+), 652 deletions(-) mode change 100755 => 100644 toxygen/smileys/default/ad.png mode change 100755 => 100644 toxygen/smileys/default/ae.png mode change 100755 => 100644 toxygen/smileys/default/af.png mode change 100755 => 100644 toxygen/smileys/default/ag.png mode change 100755 => 100644 toxygen/smileys/default/ai.png mode change 100755 => 100644 toxygen/smileys/default/al.png mode change 100755 => 100644 toxygen/smileys/default/am.png mode change 100755 => 100644 toxygen/smileys/default/an.png mode change 100755 => 100644 toxygen/smileys/default/ar.png mode change 100755 => 100644 toxygen/smileys/default/as.png mode change 100755 => 100644 toxygen/smileys/default/at.png mode change 100755 => 100644 toxygen/smileys/default/au.png mode change 100755 => 100644 toxygen/smileys/default/aw.png mode change 100755 => 100644 toxygen/smileys/default/ax.png mode change 100755 => 100644 toxygen/smileys/default/az.png mode change 100755 => 100644 toxygen/smileys/default/ba.png mode change 100755 => 100644 toxygen/smileys/default/bb.png mode change 100755 => 100644 toxygen/smileys/default/bd.png mode change 100755 => 100644 toxygen/smileys/default/be.png mode change 100755 => 100644 toxygen/smileys/default/bf.png mode change 100755 => 100644 toxygen/smileys/default/bg.png mode change 100755 => 100644 toxygen/smileys/default/bh.png mode change 100755 => 100644 toxygen/smileys/default/bi.png mode change 100755 => 100644 toxygen/smileys/default/bj.png mode change 100755 => 100644 toxygen/smileys/default/bm.png mode change 100755 => 100644 toxygen/smileys/default/bn.png mode change 100755 => 100644 toxygen/smileys/default/bo.png mode change 100755 => 100644 toxygen/smileys/default/br.png mode change 100755 => 100644 toxygen/smileys/default/bs.png mode change 100755 => 100644 toxygen/smileys/default/bt.png mode change 100755 => 100644 toxygen/smileys/default/bv.png mode change 100755 => 100644 toxygen/smileys/default/bw.png mode change 100755 => 100644 toxygen/smileys/default/by.png mode change 100755 => 100644 toxygen/smileys/default/bz.png mode change 100755 => 100644 toxygen/smileys/default/ca.png mode change 100755 => 100644 toxygen/smileys/default/cc.png mode change 100755 => 100644 toxygen/smileys/default/cf.png mode change 100755 => 100644 toxygen/smileys/default/cg.png mode change 100755 => 100644 toxygen/smileys/default/ch.png mode change 100755 => 100644 toxygen/smileys/default/ci.png mode change 100755 => 100644 toxygen/smileys/default/ck.png mode change 100755 => 100644 toxygen/smileys/default/cl.png mode change 100755 => 100644 toxygen/smileys/default/cm.png mode change 100755 => 100644 toxygen/smileys/default/cn.png mode change 100755 => 100644 toxygen/smileys/default/co.png mode change 100755 => 100644 toxygen/smileys/default/cr.png mode change 100755 => 100644 toxygen/smileys/default/cs.png mode change 100755 => 100644 toxygen/smileys/default/cu.png mode change 100755 => 100644 toxygen/smileys/default/cv.png mode change 100755 => 100644 toxygen/smileys/default/cx.png mode change 100755 => 100644 toxygen/smileys/default/cy.png mode change 100755 => 100644 toxygen/smileys/default/cz.png mode change 100755 => 100644 toxygen/smileys/default/de.png mode change 100755 => 100644 toxygen/smileys/default/dj.png mode change 100755 => 100644 toxygen/smileys/default/dk.png mode change 100755 => 100644 toxygen/smileys/default/dm.png mode change 100755 => 100644 toxygen/smileys/default/do.png mode change 100755 => 100644 toxygen/smileys/default/dz.png mode change 100755 => 100644 toxygen/smileys/default/ec.png mode change 100755 => 100644 toxygen/smileys/default/ee.png mode change 100755 => 100644 toxygen/smileys/default/eg.png mode change 100755 => 100644 toxygen/smileys/default/eh.png mode change 100755 => 100644 toxygen/smileys/default/england.png mode change 100755 => 100644 toxygen/smileys/default/er.png mode change 100755 => 100644 toxygen/smileys/default/es.png mode change 100755 => 100644 toxygen/smileys/default/et.png mode change 100755 => 100644 toxygen/smileys/default/fam.png mode change 100755 => 100644 toxygen/smileys/default/fi.png mode change 100755 => 100644 toxygen/smileys/default/fj.png mode change 100755 => 100644 toxygen/smileys/default/fk.png mode change 100755 => 100644 toxygen/smileys/default/fm.png mode change 100755 => 100644 toxygen/smileys/default/fo.png mode change 100755 => 100644 toxygen/smileys/default/fr.png mode change 100755 => 100644 toxygen/smileys/default/ga.png mode change 100755 => 100644 toxygen/smileys/default/gd.png mode change 100755 => 100644 toxygen/smileys/default/ge.png mode change 100755 => 100644 toxygen/smileys/default/gf.png mode change 100755 => 100644 toxygen/smileys/default/gh.png mode change 100755 => 100644 toxygen/smileys/default/gi.png mode change 100755 => 100644 toxygen/smileys/default/gl.png mode change 100755 => 100644 toxygen/smileys/default/gm.png mode change 100755 => 100644 toxygen/smileys/default/gn.png mode change 100755 => 100644 toxygen/smileys/default/gp.png mode change 100755 => 100644 toxygen/smileys/default/gq.png mode change 100755 => 100644 toxygen/smileys/default/gr.png mode change 100755 => 100644 toxygen/smileys/default/gs.png mode change 100755 => 100644 toxygen/smileys/default/gt.png mode change 100755 => 100644 toxygen/smileys/default/gu.png mode change 100755 => 100644 toxygen/smileys/default/gw.png mode change 100755 => 100644 toxygen/smileys/default/gy.png mode change 100755 => 100644 toxygen/smileys/default/hk.png mode change 100755 => 100644 toxygen/smileys/default/hm.png mode change 100755 => 100644 toxygen/smileys/default/hn.png mode change 100755 => 100644 toxygen/smileys/default/hr.png mode change 100755 => 100644 toxygen/smileys/default/ht.png mode change 100755 => 100644 toxygen/smileys/default/hu.png mode change 100755 => 100644 toxygen/smileys/default/id.png mode change 100755 => 100644 toxygen/smileys/default/ie.png mode change 100755 => 100644 toxygen/smileys/default/il.png mode change 100755 => 100644 toxygen/smileys/default/in.png mode change 100755 => 100644 toxygen/smileys/default/io.png mode change 100755 => 100644 toxygen/smileys/default/iq.png mode change 100755 => 100644 toxygen/smileys/default/ir.png mode change 100755 => 100644 toxygen/smileys/default/is.png mode change 100755 => 100644 toxygen/smileys/default/it.png mode change 100755 => 100644 toxygen/smileys/default/jm.png mode change 100755 => 100644 toxygen/smileys/default/jo.png mode change 100755 => 100644 toxygen/smileys/default/jp.png mode change 100755 => 100644 toxygen/smileys/default/ke.png mode change 100755 => 100644 toxygen/smileys/default/kg.png mode change 100755 => 100644 toxygen/smileys/default/kh.png mode change 100755 => 100644 toxygen/smileys/default/ki.png mode change 100755 => 100644 toxygen/smileys/default/km.png mode change 100755 => 100644 toxygen/smileys/default/kn.png mode change 100755 => 100644 toxygen/smileys/default/kp.png mode change 100755 => 100644 toxygen/smileys/default/kr.png mode change 100755 => 100644 toxygen/smileys/default/kw.png mode change 100755 => 100644 toxygen/smileys/default/ky.png mode change 100755 => 100644 toxygen/smileys/default/kz.png mode change 100755 => 100644 toxygen/smileys/default/la.png mode change 100755 => 100644 toxygen/smileys/default/lb.png mode change 100755 => 100644 toxygen/smileys/default/li.png mode change 100755 => 100644 toxygen/smileys/default/lk.png mode change 100755 => 100644 toxygen/smileys/default/lr.png mode change 100755 => 100644 toxygen/smileys/default/ls.png mode change 100755 => 100644 toxygen/smileys/default/lt.png mode change 100755 => 100644 toxygen/smileys/default/lu.png mode change 100755 => 100644 toxygen/smileys/default/lv.png mode change 100755 => 100644 toxygen/smileys/default/ly.png mode change 100755 => 100644 toxygen/smileys/default/ma.png mode change 100755 => 100644 toxygen/smileys/default/mc.png mode change 100755 => 100644 toxygen/smileys/default/md.png mode change 100755 => 100644 toxygen/smileys/default/mg.png mode change 100755 => 100644 toxygen/smileys/default/mh.png mode change 100755 => 100644 toxygen/smileys/default/mk.png mode change 100755 => 100644 toxygen/smileys/default/ml.png mode change 100755 => 100644 toxygen/smileys/default/mm.png mode change 100755 => 100644 toxygen/smileys/default/mn.png mode change 100755 => 100644 toxygen/smileys/default/mo.png mode change 100755 => 100644 toxygen/smileys/default/mp.png mode change 100755 => 100644 toxygen/smileys/default/mq.png mode change 100755 => 100644 toxygen/smileys/default/mr.png mode change 100755 => 100644 toxygen/smileys/default/ms.png mode change 100755 => 100644 toxygen/smileys/default/mt.png mode change 100755 => 100644 toxygen/smileys/default/mu.png mode change 100755 => 100644 toxygen/smileys/default/mv.png mode change 100755 => 100644 toxygen/smileys/default/mw.png mode change 100755 => 100644 toxygen/smileys/default/mx.png mode change 100755 => 100644 toxygen/smileys/default/my.png mode change 100755 => 100644 toxygen/smileys/default/mz.png mode change 100755 => 100644 toxygen/smileys/default/na.png mode change 100755 => 100644 toxygen/smileys/default/nc.png mode change 100755 => 100644 toxygen/smileys/default/ne.png mode change 100755 => 100644 toxygen/smileys/default/nf.png mode change 100755 => 100644 toxygen/smileys/default/ng.png mode change 100755 => 100644 toxygen/smileys/default/ni.png mode change 100755 => 100644 toxygen/smileys/default/nl.png mode change 100755 => 100644 toxygen/smileys/default/no.png mode change 100755 => 100644 toxygen/smileys/default/np.png mode change 100755 => 100644 toxygen/smileys/default/nr.png mode change 100755 => 100644 toxygen/smileys/default/nu.png mode change 100755 => 100644 toxygen/smileys/default/nz.png mode change 100755 => 100644 toxygen/smileys/default/om.png mode change 100755 => 100644 toxygen/smileys/default/pa.png mode change 100755 => 100644 toxygen/smileys/default/pe.png mode change 100755 => 100644 toxygen/smileys/default/pf.png mode change 100755 => 100644 toxygen/smileys/default/pg.png mode change 100755 => 100644 toxygen/smileys/default/ph.png mode change 100755 => 100644 toxygen/smileys/default/pk.png mode change 100755 => 100644 toxygen/smileys/default/pl.png mode change 100755 => 100644 toxygen/smileys/default/pm.png mode change 100755 => 100644 toxygen/smileys/default/pn.png mode change 100755 => 100644 toxygen/smileys/default/pr.png mode change 100755 => 100644 toxygen/smileys/default/ps.png mode change 100755 => 100644 toxygen/smileys/default/pt.png mode change 100755 => 100644 toxygen/smileys/default/pw.png mode change 100755 => 100644 toxygen/smileys/default/py.png mode change 100755 => 100644 toxygen/smileys/default/qa.png mode change 100755 => 100644 toxygen/smileys/default/re.png mode change 100755 => 100644 toxygen/smileys/default/ro.png mode change 100755 => 100644 toxygen/smileys/default/ru.png mode change 100755 => 100644 toxygen/smileys/default/rw.png mode change 100755 => 100644 toxygen/smileys/default/sa.png mode change 100755 => 100644 toxygen/smileys/default/sb.png mode change 100755 => 100644 toxygen/smileys/default/sc.png mode change 100755 => 100644 toxygen/smileys/default/scotland.png mode change 100755 => 100644 toxygen/smileys/default/sd.png mode change 100755 => 100644 toxygen/smileys/default/se.png mode change 100755 => 100644 toxygen/smileys/default/sg.png mode change 100755 => 100644 toxygen/smileys/default/sh.png mode change 100755 => 100644 toxygen/smileys/default/si.png mode change 100755 => 100644 toxygen/smileys/default/sj.png mode change 100755 => 100644 toxygen/smileys/default/sk.png mode change 100755 => 100644 toxygen/smileys/default/sl.png mode change 100755 => 100644 toxygen/smileys/default/sm.png mode change 100755 => 100644 toxygen/smileys/default/sn.png mode change 100755 => 100644 toxygen/smileys/default/so.png mode change 100755 => 100644 toxygen/smileys/default/sr.png mode change 100755 => 100644 toxygen/smileys/default/st.png mode change 100755 => 100644 toxygen/smileys/default/sv.png mode change 100755 => 100644 toxygen/smileys/default/sy.png mode change 100755 => 100644 toxygen/smileys/default/sz.png mode change 100755 => 100644 toxygen/smileys/default/tc.png mode change 100755 => 100644 toxygen/smileys/default/td.png mode change 100755 => 100644 toxygen/smileys/default/tf.png mode change 100755 => 100644 toxygen/smileys/default/tg.png mode change 100755 => 100644 toxygen/smileys/default/th.png mode change 100755 => 100644 toxygen/smileys/default/tj.png mode change 100755 => 100644 toxygen/smileys/default/tk.png mode change 100755 => 100644 toxygen/smileys/default/tl.png mode change 100755 => 100644 toxygen/smileys/default/tm.png mode change 100755 => 100644 toxygen/smileys/default/tn.png mode change 100755 => 100644 toxygen/smileys/default/to.png mode change 100755 => 100644 toxygen/smileys/default/tox.png mode change 100755 => 100644 toxygen/smileys/default/tr.png mode change 100755 => 100644 toxygen/smileys/default/tt.png mode change 100755 => 100644 toxygen/smileys/default/tv.png mode change 100755 => 100644 toxygen/smileys/default/tw.png mode change 100755 => 100644 toxygen/smileys/default/tz.png mode change 100755 => 100644 toxygen/smileys/default/ua.png mode change 100755 => 100644 toxygen/smileys/default/ug.png mode change 100755 => 100644 toxygen/smileys/default/um.png mode change 100755 => 100644 toxygen/smileys/default/us.png mode change 100755 => 100644 toxygen/smileys/default/uy.png mode change 100755 => 100644 toxygen/smileys/default/uz.png mode change 100755 => 100644 toxygen/smileys/default/va.png mode change 100755 => 100644 toxygen/smileys/default/vc.png mode change 100755 => 100644 toxygen/smileys/default/ve.png mode change 100755 => 100644 toxygen/smileys/default/vg.png mode change 100755 => 100644 toxygen/smileys/default/vi.png mode change 100755 => 100644 toxygen/smileys/default/vn.png mode change 100755 => 100644 toxygen/smileys/default/vu.png mode change 100755 => 100644 toxygen/smileys/default/wales.png mode change 100755 => 100644 toxygen/smileys/default/wf.png mode change 100755 => 100644 toxygen/smileys/default/ws.png mode change 100755 => 100644 toxygen/smileys/default/wtox.png mode change 100755 => 100644 toxygen/smileys/default/ye.png mode change 100755 => 100644 toxygen/smileys/default/yt.png mode change 100755 => 100644 toxygen/smileys/default/za.png mode change 100755 => 100644 toxygen/smileys/default/zm.png mode change 100755 => 100644 toxygen/smileys/default/zw.png diff --git a/MANIFEST.in b/MANIFEST.in index 6629fb6..89e57c6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -16,4 +16,4 @@ include toxygen/styles/*.qss include toxygen/translations/*.qm include toxygen/libs/libtox.dll include toxygen/libs/libsodium.a -include toxygen/nodes.json +include toxygen/bootstrap/nodes.json diff --git a/README.md b/README.md index 914fdfe..8aa90bf 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,6 @@ Toxygen is powerful cross-platform [Tox](https://tox.chat/) client written in pure Python3. -[![Release](https://img.shields.io/github/release/toxygen-project/toxygen.svg?style=flat)](https://github.com/toxygen-project/toxygen/releases/latest) -[![Stars](https://img.shields.io/github/stars/toxygen-project/toxygen.svg?style=flat)](https://github.com/toxygen-project/toxygen/stargazers) -[![Open issues](https://img.shields.io/github/issues/toxygen-project/toxygen.svg?style=flat)](https://github.com/toxygen-project/toxygen/issues) -[![License](https://img.shields.io/badge/license-GPLv3-blue.svg?style=flat)](https://raw.githubusercontent.com/toxygen-project/toxygen/master/LICENSE.md) -[![Build Status](https://travis-ci.org/toxygen-project/toxygen.svg?branch=master)](https://travis-ci.org/toxygen-project/toxygen) - ### [Install](/docs/install.md) - [Contribute](/docs/contributing.md) - [Plugins](/docs/plugins.md) - [Compile](/docs/compile.md) - [Contact](/docs/contact.md) - [Updater](https://github.com/toxygen-project/toxygen_updater) ### Supported OS: Linux and Windows @@ -44,21 +38,12 @@ Toxygen is powerful cross-platform [Tox](https://tox.chat/) client written in pu - File resuming - Read receipts -### Downloads -[Releases](https://github.com/toxygen-project/toxygen/releases) - -[Download last stable version](https://github.com/toxygen-project/toxygen/archive/master.zip) - -[Download develop version](https://github.com/toxygen-project/toxygen/archive/develop.zip) - ### Screenshots *Toxygen on Ubuntu and Windows* ![Ubuntu](/docs/ubuntu.png) ![Windows](/docs/windows.png) -### Docs -[Check /docs/ for more info](/docs/) +## Forked -Also visit [pythonhosted.org/Toxygen/](http://pythonhosted.org/Toxygen/) - -[Wiki](https://wiki.tox.chat/clients/toxygen) +This hard-forked from https://github.com/toxygen-project/toxygen +```next_gen``` branch. diff --git a/docs/compile.md b/docs/compile.md index 995dc35..b4f6810 100644 --- a/docs/compile.md +++ b/docs/compile.md @@ -2,10 +2,18 @@ You can compile Toxygen using [PyInstaller](http://www.pyinstaller.org/) -Install PyInstaller: -``pip3 install pyinstaller`` +Use Dockerfile and build script from `build` directory: -Compile Toxygen: -``pyinstaller --windowed --icon images/icon.ico main.py`` +1. Build image: +``` +docker build -t toxygen . +``` -Don't forget to copy /images/, /sounds/, /translations/, /styles/, /smileys/, /stickers/, /plugins/ (and /libs/libtox.dll, /libs/libsodium.a on Windows) to /dist/main/ +2. Run container: +``` +docker run -it toxygen bash +``` + +3. Execute `build.sh` script: + +```./build.sh``` diff --git a/docs/contact.md b/docs/contact.md index c66da1c..9f80595 100644 --- a/docs/contact.md +++ b/docs/contact.md @@ -2,4 +2,4 @@ 1) Using GitHub - open issue -2) Use Toxygen Tox Group - add bot kalina@toxme.io (or 12EDB939AA529641CE53830B518D6EB30241868EE0E5023C46A372363CAEC91C2C948AEFE4EB) +2) Use Toxygen Tox Group (NGC) - ID: 59D68B2709E81A679CF91416CB0E3692851C6CFCABEFF98B7131E3805A6D75FA diff --git a/setup.py b/setup.py index 746163e..fb80363 100644 --- a/setup.py +++ b/setup.py @@ -2,15 +2,17 @@ from setuptools import setup from setuptools.command.install import install from platform import system from subprocess import call -from toxygen.util import program_version +import main import sys +import os +from utils.util import curr_directory, join_path -version = program_version + '.0' +version = main.__version__ + '.0' if system() == 'Windows': - MODULES = ['PyQt5', 'PyAudio', 'numpy', 'opencv-python'] + MODULES = ['PyQt5', 'PyAudio', 'numpy', 'opencv-python', 'pydenticon'] else: MODULES = [] try: @@ -29,6 +31,19 @@ else: import cv2 except ImportError: MODULES.append('opencv-python') + try: + import pydenticon + except ImportError: + MODULES.append('pydenticon') + + +def get_packages(): + directory = join_path(curr_directory(__file__), 'toxygen') + for root, dirs, files in os.walk(directory): + packages = map(lambda d: 'toxygen.' + d, dirs) + packages = ['toxygen'] + list(packages) + + return packages class InstallScript(install): @@ -62,7 +77,7 @@ setup(name='Toxygen', author='Ingvar', maintainer='Ingvar', license='GPL3', - packages=['toxygen', 'toxygen.plugins', 'toxygen.styles'], + packages=get_packages(), install_requires=MODULES, include_package_data=True, classifiers=[ @@ -71,8 +86,8 @@ setup(name='Toxygen', 'Programming Language :: Python :: 3.6', ], entry_points={ - 'console_scripts': ['toxygen=toxygen.main:main'], + 'console_scripts': ['toxygen=toxygen.main:main'] }, cmdclass={ - 'install': InstallScript, + 'install': InstallScript }) diff --git a/tests/tests.py b/tests/tests.py index bbb877c..e3c9b6b 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,162 +1,18 @@ -from toxygen.profile import * -from toxygen.tox_dns import tox_dns -from toxygen.history import History -from toxygen.smileys import SmileyLoader -from toxygen.messages import * -import toxygen.toxes as encr -import toxygen.util as util -import time +from toxygen.middleware.tox_factory import * +# TODO: add new tests + class TestTox: def test_creation(self): - name = b'Toxygen User' - status_message = b'Toxing on Toxygen' + name = 'Toxygen User' + status_message = 'Toxing on Toxygen' tox = tox_factory() tox.self_set_name(name) tox.self_set_status_message(status_message) data = tox.get_savedata() del tox tox = tox_factory(data) - assert tox.self_get_name() == str(name, 'utf-8') - assert tox.self_get_status_message() == str(status_message, 'utf-8') - - -class TestProfileHelper: - - def test_creation(self): - file_name, path = 'test.tox', os.path.dirname(os.path.realpath(__file__)) + '/' - data = b'test' - with open(path + file_name, 'wb') as fl: - fl.write(data) - ph = ProfileHelper(path, file_name[:4]) - assert ProfileHelper.get_path() == path - assert ph.open_profile() == data - assert os.path.exists(path + 'avatars/') - - -class TestEncryption: - - def test_encr_decr(self): - tox = tox_factory() - data = tox.get_savedata() - lib = encr.ToxES() - for password in ('easypassword', 'njvnFjfn7vaGGV6', 'toxygen'): - lib.set_password(password) - copy_data = data[:] - new_data = lib.pass_encrypt(data) - assert lib.is_data_encrypted(new_data) - new_data = lib.pass_decrypt(new_data) - assert copy_data == new_data - - -class TestSmileys: - - def test_loading(self): - settings = {'smiley_pack': 'default', 'smileys': True} - sm = SmileyLoader(settings) - assert sm.get_smileys_path() is not None - l = sm.get_packs_list() - assert len(l) == 4 - - -def create_singletons(): - folder = util.curr_directory() + '/abc' - Settings._instance = Settings.get_default_settings() - if not os.path.exists(folder): - os.makedirs(folder) - ProfileHelper(folder, 'test') - - -def create_friend(name, status_message, number, tox_id): - friend = Friend(None, number, name, status_message, None, tox_id) - return friend - - -def create_random_friend(): - name, status_message, number = 'Friend', 'I am friend!', 0 - tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6' - friend = create_friend(name, status_message, number, tox_id) - return friend - - -class TestFriend: - - def test_friend_creation(self): - create_singletons() - name, status_message, number = 'Friend', 'I am friend!', 0 - tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6' - friend = create_friend(name, status_message, number, tox_id) - assert friend.name == name - assert friend.tox_id == tox_id - assert friend.status_message == status_message - assert friend.number == number - - def test_friend_corr(self): - create_singletons() - friend = create_random_friend() - t = time.time() - friend.append_message(InfoMessage('Info message', t)) - friend.append_message(TextMessage('Hello! It is test!', MESSAGE_OWNER['ME'], t + 0.001, 0)) - friend.append_message(TextMessage('Hello!', MESSAGE_OWNER['FRIEND'], t + 0.002, 0)) - assert friend.get_last_message_text() == 'Hello! It is test!' - assert len(friend.get_corr()) == 3 - assert len(friend.get_corr_for_saving()) == 2 - friend.append_message(TextMessage('Not sent', MESSAGE_OWNER['NOT_SENT'], t + 0.002, 0)) - arr = friend.get_unsent_messages_for_saving() - assert len(arr) == 1 - assert arr[0][0] == 'Not sent' - tm = TransferMessage(MESSAGE_OWNER['FRIEND'], - time.time(), - TOX_FILE_TRANSFER_STATE['RUNNING'], - 100, 'file_name', friend.number, 0) - friend.append_message(tm) - friend.clear_corr() - assert len(friend.get_corr()) == 1 - assert len(friend.get_corr_for_saving()) == 0 - friend.append_message(TextMessage('Hello! It is test!', MESSAGE_OWNER['ME'], t, 0)) - assert len(friend.get_corr()) == 2 - assert len(friend.get_corr_for_saving()) == 1 - - def test_history_search(self): - create_singletons() - friend = create_random_friend() - message = 'Hello! It is test!' - friend.append_message(TextMessage(message, MESSAGE_OWNER['ME'], time.time(), 0)) - last_message = friend.get_last_message_text() - assert last_message == message - result = friend.search_string('e[m|s]') - assert result is not None - result = friend.search_string('tox') - assert result is None - - -class TestHistory: - - def test_history(self): - create_singletons() - db_name = 'my_name' - name, status_message, number = 'Friend', 'I am friend!', 0 - tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6' - friend = create_friend(name, status_message, number, tox_id) - history = History(db_name) - history.add_friend_to_db(friend.tox_id) - assert history.friend_exists_in_db(friend.tox_id) - text_message = 'Test!' - t = time.time() - friend.append_message(TextMessage(text_message, MESSAGE_OWNER['ME'], t, 0)) - messages = friend.get_corr_for_saving() - history.save_messages_to_db(friend.tox_id, messages) - getter = history.messages_getter(friend.tox_id) - messages = getter.get_all() - assert len(messages) == 1 - assert messages[0][0] == text_message - assert messages[0][1] == MESSAGE_OWNER['ME'] - assert messages[0][-1] == 0 - history.delete_message(friend.tox_id, t) - getter = history.messages_getter(friend.tox_id) - messages = getter.get_all() - assert len(messages) == 0 - history.delete_friend_from_db(friend.tox_id) - assert not history.friend_exists_in_db(friend.tox_id) + assert tox.self_get_name() == name + assert tox.self_get_status_message() == status_message diff --git a/toxygen/main.py b/toxygen/main.py index d630bb6..eca3ac3 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,485 +1,49 @@ -import sys -from loginscreen import LoginScreen -import profile -from settings import * -from PyQt5 import QtCore, QtGui, QtWidgets -from bootstrap import generate_nodes, download_nodes_list -from mainscreen import MainWindow -from callbacks import init_callbacks, stop, start -from util import curr_directory, program_version, remove -import styles.style # reqired for styles loading -import platform -import toxes -from passwordscreen import PasswordScreen, UnlockAppScreen, SetProfilePasswordScreen -from plugin_support import PluginLoader -import updater +import app +from user_data.settings import * +import utils.util as util +import argparse -class Toxygen: - - def __init__(self, path_or_uri=None): - super(Toxygen, self).__init__() - self.tox = self.ms = self.init = self.app = self.tray = self.mainloop = self.avloop = None - if path_or_uri is None: - self.uri = self.path = None - elif path_or_uri.startswith('tox:'): - self.path = None - self.uri = path_or_uri[4:] - else: - self.path = path_or_uri - self.uri = None - - def enter_pass(self, data): - """ - Show password screen - """ - tmp = [data] - p = PasswordScreen(toxes.ToxES.get_instance(), tmp) - p.show() - self.app.lastWindowClosed.connect(self.app.quit) - self.app.exec_() - if tmp[0] == data: - raise SystemExit() - else: - return tmp[0] - - def main(self): - """ - Main function of app. loads login screen if needed and starts main screen - """ - app = QtWidgets.QApplication(sys.argv) - app.setWindowIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) - self.app = app - - if platform.system() == 'Linux': - QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads) - - with open(curr_directory() + '/styles/dark_style.qss') as fl: - style = fl.read() - app.setStyleSheet(style) - - encrypt_save = toxes.ToxES() - - if self.path is not None: - path = os.path.dirname(self.path) + '/' - name = os.path.basename(self.path)[:-4] - data = ProfileHelper(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) - else: - auto_profile = Settings.get_auto_profile() - if not auto_profile[0]: - # show login screen if default profile not found - current_locale = QtCore.QLocale() - curr_lang = current_locale.languageToString(current_locale.language()) - langs = Settings.supported_languages() - if curr_lang in langs: - lang_path = langs[curr_lang] - translator = QtCore.QTranslator() - translator.load(curr_directory() + '/translations/' + lang_path) - app.installTranslator(translator) - app.translator = translator - ls = LoginScreen() - ls.setWindowIconText("Toxygen") - profiles = ProfileHelper.find_profiles() - ls.update_select(map(lambda x: x[1], profiles)) - _login = self.Login(profiles) - ls.update_on_close(_login.login_screen_close) - ls.show() - app.exec_() - if not _login.t: - return - elif _login.t == 1: # create new profile - _login.name = _login.name.strip() - name = _login.name if _login.name else 'toxygen_user' - pr = map(lambda x: x[1], ProfileHelper.find_profiles()) - if name in list(pr): - msgBox = QtWidgets.QMessageBox() - msgBox.setWindowTitle( - QtWidgets.QApplication.translate("MainWindow", "Error")) - text = (QtWidgets.QApplication.translate("MainWindow", - 'Profile with this name already exists')) - msgBox.setText(text) - msgBox.exec_() - return - self.tox = profile.tox_factory() - self.tox.self_set_name(bytes(_login.name, 'utf-8') if _login.name else b'Toxygen User') - self.tox.self_set_status_message(b'Toxing on Toxygen') - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", - 'Do you want to set profile password?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - set_pass = SetProfilePasswordScreen(encrypt_save) - set_pass.show() - self.app.lastWindowClosed.connect(self.app.quit) - self.app.exec_() - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", - 'Do you want to save profile in default folder? If no, profile will be saved in program folder'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - path = Settings.get_default_path() - else: - path = curr_directory() + '/' - try: - ProfileHelper(path, name).save_profile(self.tox.get_savedata()) - except Exception as ex: - print(str(ex)) - log('Profile creation exception: ' + str(ex)) - msgBox = QtWidgets.QMessageBox() - msgBox.setText(QtWidgets.QApplication.translate("login", - 'Profile saving error! Does Toxygen have permission to write to this directory?')) - msgBox.exec_() - return - path = Settings.get_default_path() - settings = Settings(name) - if curr_lang in langs: - settings['language'] = curr_lang - settings.save() - else: # load existing profile - path, name = _login.get_data() - if _login.default: - Settings.set_auto_profile(path, name) - data = ProfileHelper(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) - else: - path, name = auto_profile - data = ProfileHelper(path, name).open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - settings = Settings(name) - self.tox = profile.tox_factory(data, settings) - - if Settings.is_active_profile(path, name): # profile is in use - reply = QtWidgets.QMessageBox.question(None, - 'Profile {}'.format(name), - QtWidgets.QApplication.translate("login", 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply != QtWidgets.QMessageBox.Yes: - return - else: - settings.set_active_profile() - - # application color scheme - for theme in settings.built_in_themes().keys(): - if settings['theme'] == theme: - with open(curr_directory() + settings.built_in_themes()[theme]) as fl: - style = fl.read() - app.setStyleSheet(style) - - lang = Settings.supported_languages()[settings['language']] - translator = QtCore.QTranslator() - translator.load(curr_directory() + '/translations/' + lang) - app.installTranslator(translator) - app.translator = translator - - # tray icon - self.tray = QtWidgets.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png')) - self.tray.setObjectName('tray') - - self.ms = MainWindow(self.tox, self.reset, self.tray) - app.aboutToQuit.connect(self.ms.close_window) - - class Menu(QtWidgets.QMenu): - - def newStatus(self, status): - if not Settings.get_instance().locked: - profile.Profile.get_instance().set_status(status) - self.aboutToShowHandler() - self.hide() - - def aboutToShowHandler(self): - status = profile.Profile.get_instance().status - act = self.act - if status is None or Settings.get_instance().locked: - self.actions()[1].setVisible(False) - else: - self.actions()[1].setVisible(True) - act.actions()[0].setChecked(False) - act.actions()[1].setChecked(False) - act.actions()[2].setChecked(False) - act.actions()[status].setChecked(True) - self.actions()[2].setVisible(not Settings.get_instance().locked) - - def languageChange(self, *args, **kwargs): - self.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) - self.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Set status')) - self.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Exit')) - self.act.actions()[0].setText(QtWidgets.QApplication.translate('tray', 'Online')) - self.act.actions()[1].setText(QtWidgets.QApplication.translate('tray', 'Away')) - self.act.actions()[2].setText(QtWidgets.QApplication.translate('tray', 'Busy')) - - m = Menu() - show = m.addAction(QtWidgets.QApplication.translate('tray', 'Open Toxygen')) - sub = m.addMenu(QtWidgets.QApplication.translate('tray', 'Set status')) - onl = sub.addAction(QtWidgets.QApplication.translate('tray', 'Online')) - away = sub.addAction(QtWidgets.QApplication.translate('tray', 'Away')) - busy = sub.addAction(QtWidgets.QApplication.translate('tray', 'Busy')) - onl.setCheckable(True) - away.setCheckable(True) - busy.setCheckable(True) - m.act = sub - exit = m.addAction(QtWidgets.QApplication.translate('tray', 'Exit')) - - def show_window(): - s = Settings.get_instance() - - def show(): - if not self.ms.isActiveWindow(): - self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) - self.ms.activateWindow() - self.ms.show() - if not s.locked: - show() - else: - def correct_pass(): - show() - s.locked = False - s.unlockScreen = False - if not s.unlockScreen: - s.unlockScreen = True - self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass) - self.p.show() - - def tray_activated(reason): - if reason == QtWidgets.QSystemTrayIcon.DoubleClick: - show_window() - - def close_app(): - if not Settings.get_instance().locked: - settings.closing = True - self.ms.close() - - show.triggered.connect(show_window) - exit.triggered.connect(close_app) - m.aboutToShow.connect(lambda: m.aboutToShowHandler()) - onl.triggered.connect(lambda: m.newStatus(0)) - away.triggered.connect(lambda: m.newStatus(1)) - busy.triggered.connect(lambda: m.newStatus(2)) - - self.tray.setContextMenu(m) - self.tray.show() - self.tray.activated.connect(tray_activated) - - self.ms.show() - - updating = False - if settings['update'] and updater.updater_available() and updater.connection_available(): # auto update - version = updater.check_for_updates() - if version is not None: - if settings['update'] == 2: - updater.download(version) - updating = True - else: - reply = QtWidgets.QMessageBox.question(None, - 'Toxygen', - QtWidgets.QApplication.translate("login", - 'Update for Toxygen was found. Download and install it?'), - QtWidgets.QMessageBox.Yes, - QtWidgets.QMessageBox.No) - if reply == QtWidgets.QMessageBox.Yes: - updater.download(version) - updating = True - - if updating: - data = self.tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - settings.close() - del self.tox - return - - plugin_helper = PluginLoader(self.tox, settings) # plugin support - plugin_helper.load() - - start() - # init thread - self.init = self.InitThread(self.tox, self.ms, self.tray) - self.init.start() - - # starting threads for tox iterate and toxav iterate - self.mainloop = self.ToxIterateThread(self.tox) - self.mainloop.start() - self.avloop = self.ToxAVIterateThread(self.tox.AV) - self.avloop.start() - - if self.uri is not None: - self.ms.add_contact(self.uri) - - app.lastWindowClosed.connect(app.quit) - app.exec_() - - self.init.stop = True - self.mainloop.stop = True - self.avloop.stop = True - plugin_helper.stop() - stop() - self.mainloop.wait() - self.init.wait() - self.avloop.wait() - self.tray.hide() - data = self.tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - settings.close() - del self.tox - - def reset(self): - """ - Create new tox instance (new network settings) - :return: tox instance - """ - self.mainloop.stop = True - self.init.stop = True - self.avloop.stop = True - self.mainloop.wait() - self.init.wait() - self.avloop.wait() - data = self.tox.get_savedata() - ProfileHelper.get_instance().save_profile(data) - del self.tox - # create new tox instance - self.tox = profile.tox_factory(data, Settings.get_instance()) - # init thread - self.init = self.InitThread(self.tox, self.ms, self.tray) - self.init.start() - - # starting threads for tox iterate and toxav iterate - self.mainloop = self.ToxIterateThread(self.tox) - self.mainloop.start() - - self.avloop = self.ToxAVIterateThread(self.tox.AV) - self.avloop.start() - - plugin_helper = PluginLoader.get_instance() - plugin_helper.set_tox(self.tox) - - return self.tox - - # ----------------------------------------------------------------------------------------------------------------- - # Inner classes - # ----------------------------------------------------------------------------------------------------------------- - - class InitThread(QtCore.QThread): - - def __init__(self, tox, ms, tray): - QtCore.QThread.__init__(self) - self.tox, self.ms, self.tray = tox, ms, tray - self.stop = False - - def run(self): - # initializing callbacks - init_callbacks(self.tox, self.ms, self.tray) - # download list of nodes if needed - download_nodes_list() - # bootstrap - try: - for data in generate_nodes(): - if self.stop: - return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) - except: - pass - for _ in range(10): - if self.stop: - return - self.msleep(1000) - while not self.tox.self_get_connection_status(): - try: - for data in generate_nodes(): - if self.stop: - return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) - except: - pass - finally: - self.msleep(5000) - - class ToxIterateThread(QtCore.QThread): - - def __init__(self, tox): - QtCore.QThread.__init__(self) - self.tox = tox - self.stop = False - - def run(self): - while not self.stop: - self.tox.iterate() - self.msleep(self.tox.iteration_interval()) - - class ToxAVIterateThread(QtCore.QThread): - - def __init__(self, toxav): - QtCore.QThread.__init__(self) - self.toxav = toxav - self.stop = False - - def run(self): - while not self.stop: - self.toxav.iterate() - self.msleep(self.toxav.iteration_interval()) - - class Login: - - def __init__(self, arr): - self.arr = arr - - def login_screen_close(self, t, number=-1, default=False, name=None): - """ Function which processes data from login screen - :param t: 0 - window was closed, 1 - new profile was created, 2 - profile loaded - :param number: num of chosen profile in list (-1 by default) - :param default: was or not chosen profile marked as default - :param name: name of new profile - """ - self.t = t - self.num = number - self.default = default - self.name = name - - def get_data(self): - return self.arr[self.num] +__maintainer__ = 'Ingvar' +__version__ = '0.5.0' def clean(): - """Removes all windows libs from libs folder""" - d = curr_directory() + '/libs/' - remove(d) + """Removes libs folder""" + directory = util.get_libs_directory() + util.remove(directory) def reset(): Settings.reset_auto_profile() +def print_toxygen_version(): + print('Toxygen v' + __version__) + + def main(): - if len(sys.argv) == 1: - toxygen = Toxygen() - else: # started with argument(s) - arg = sys.argv[1] - if arg == '--version': - print('Toxygen v' + program_version) - return - elif arg == '--help': - print('Usage:\ntoxygen path_to_profile\ntoxygen tox_id\ntoxygen --version\ntoxygen --reset') - return - elif arg == '--clean': - clean() - return - elif arg == '--reset': - reset() - return - else: - toxygen = Toxygen(arg) + parser = argparse.ArgumentParser() + parser.add_argument('--version', action='store_true', help='Prints Toxygen version') + parser.add_argument('--clean', action='store_true', help='Delete toxcore libs from libs folder') + parser.add_argument('--reset', action='store_true', help='Reset default profile') + parser.add_argument('--uri', help='Add specified Tox ID to friends') + parser.add_argument('profile', nargs='?', default=None, help='Path to Tox profile') + args = parser.parse_args() + + if args.version: + print_toxygen_version() + return + + if args.clean: + clean() + return + + if args.reset: + reset() + return + + toxygen = app.App(__version__, args.profile, args.uri) toxygen.main() diff --git a/toxygen/smileys/default/003020E3.png b/toxygen/smileys/default/003020E3.png index a196fa1dd7ae2ace89ef20fd440768260a14720e..e64ea3aedb9925cdd6b634951ef6a9fe834bff24 100644 GIT binary patch delta 1273 zcmbQqwUlduWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D0wYrg&boi_h4(6ayk|3^&Q4Ycyx zk6%eMb^$GZ^yX99;{BzI4gg)S`Q)twR~|(5Z`^D9TUE%t=)!sVqoUsK_l~V6f<&S|94YX^sKUo!26k zO&71rv`apYUA5`V$IF|ZJ4;O3Y<-h2zUp4iKo^6&$e9WI`VyBMY zJbO6&{!+vDf6I24rkx1CJ|&_fbMhkHF0PW*>z9~rJK#3?Mu~Z8kAX&$@0m-f&y~Kf z*yLK8^JQAXzJina`+v(#%AUgUq26upa`{bXTc`i}@FbUQQ}nwF*ZK6qwni%YJ+I=K zDDhtD&IA96t_@lp;+tGUbe>PRtN3)|xjTmEwhM1xxQ+KHuhzk*C7+dT+QcsyhK1U< z9y1X$-Xqnu`$StoRZsW#2C?^tA2WPX%D$)XFY(@t@j;+R!~8QIAuX&eG5_y!?_gL} z@4)ig%4n;S^M#6aznwfH_*KmI&g!%+n#g&LZIykET*L3#YKPb-z2$uv@*~8EVTrgh z&o=dJ_Qyh5k?G4@E=>JuFMZyNhlPFayk|~sHgB9XjwafFE)u=5zj@Y%lGfiQKTn)r zcJh`LKQGT6Q(upWX}On{tbVoP%}&l~rE`|?KIy6t5p8r3>v_PmeRX4i$SS=Vnr?1i zQ~x@;1+1R2PU7d`$=px06I?xoH>E3`n)Q`S#^!N_iq(W*y~FBJr~T+1{64Tlf2 zC3ny!6P?czJ7f(i-48NwPT(|4<0gq);U?U;>p?nlbEcYDJ&F>U(ng~NG0y=#CAvP zAG$Z4N@n`-KT{Ll1xIFO1LR>dLv>Ea3`FqHUfojv*44V~<@GYYGskXT6ZTy&-8)lHhcq zfQ_I3+n+N1%(P>^*$4kg?wXNIQwpaA9Z*@{p?NXwbL%l}NA=TJt2RHKnkaDDNM~|* zqi;*HbyIk&x6S>pwsE|SH(MPwDoR~c?zy+mxPD-w%JR>u4^0bIxBq_p^U>B1=du@n zKl!SB0pGrr3vc~p49igMkF!gW=K#7%wZt`|BqgyV)hf9t6-Y4{85kMq8kp-EnuHh{ xSQ(pInVM)D7+4t?r1BlziJ~DlKP5A*61N7C;9Ay+it1bp44$rjF6*2UngHBtOWyzh literal 1305 zcmbVMZEVzJ9It{q*lmFcE~pGj7o*1A^?EPewYhWl+MYY{#^rXpMVX;%pWQXPwnzJn zyG{Kv1*0Skm?3I{QFEfk%%E{NaIgf;qG5?T_XV?nZ1^%`MBP~63;1-~O+PGruxb1J zpQr!d@BiMH6HSj+mfus3VOV9XQAnb7z5T2yL+`D#Hzl;(4aHX2Y;?f9WCDy+jCO#> zG${>|KvKGQe+@zywyHx-wZhi;Z12pTc$CoYwSu>Rm zHYr4xY@uD4$UvB5Niv_$yYoJ`VWvrnVHlgk>vbUwm({I9so>JB+7g2REZJ0ZP&IVi zW|Z2EE*K_|r;8zIxp;h;ShtoEg;GWqq#Q}PJ*1}DaV=~cegfVDO;LQ1W_WLjV06Jw&$I6n6*fnm4BVgi>c{7~#4dF+eC#?g@%KOKzMymQxpyLxisZSqI`k>?#% z4#!{Ho?G2g^DeBOtbrR|QO1fV_EuL=KljD-;r9l%o4(@dKc_GB&mO&edD1F=-?VOj z-Rss@GuGkT=hC|qcaG}UFVtO=#+xUPot+wD()0bny&ngsKWN#wbIb2<4IVqYnHlQ# zURr%8G-x-_A^(Tbmox4jnkyAB?b*KGJ<&|vm^Xa~XD*J;H;b`zRxjIvM^XcnZdegmk^foZRz-t?JVf$aG*cn=Xqt*Ud N#l$AzqsXq_e*hEf%5?w$ diff --git a/toxygen/smileys/default/003120E3.png b/toxygen/smileys/default/003120E3.png index 26d6754a982430fc7987db9a258c8e9a529a2d46..9501bdf842032b041545ba56dbdddf4655c59e1e 100644 GIT binary patch literal 1198 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>l{+gt#WHxR|)?eEi}w zu?tT|&pQ@1`%uJ;ePL5}g-qNU)VndDYps9#O5c`c-c5@=8x{Z+L(~Jg5D}mtL>z8N z=Z_wbFb%5cw8TC1Bx>ydAqyF%wch42Xfd; zJbhi+pRx0CsWP4kowJ*PfvK)4B%&lJv0R~`C_gPTCsm=OvLIEVBDa8n!J>C+sPDDg z3Osj;yW|+Xothq7Y`pn(uU_?^a}ISe$8+}|(?2HWqyqiTgf=uT|Td-7j@h zJbZs8=j1n!jvk)YkdW7*AZCzgF!dvDU!2xji?V_DUP$2%HU zMW`%_Ek9Y+zbZTAxYK=(XWc3Le&78ba86Z8LHhGKo07T0`rjv>wC2e%U$@~p*U6=~ z!it2{t|!mfa$=p9^rw@%Gy+7w9@@4d^PzE0c|_`&?vkF*?>x6X+{SlYR8R3|$;rhw zj$$7)lhW$6d@s#NsWjyYzb5+hi-i46=D1D$Jl72y z^ZN5lnhshViuc%pd3g>#wtVl+lxXXqvw7ALxvdHZw>iG*pTqv(-!#pKtSP_SinNMO z=`egrayjsJZKB_9c1|b}{d@l4&CbS04=zjUm0VF?$GO(x(d6l?5B-b1(&8Jw zE`%-J&Ub3f5eVDsxZ`;zsBo6Mi%-xPKU+9%KZVZ7Lt`M*=dTK5Mg z6K+m8HLXM_+-|~c>7NX1+#h_YKf!VAou0t)9fe%o@9VDX&h2aIabCr*F6%#YxijP2 z8+*3;+_mWT)p)ygBEuxUBhxlcmZ_*XXdG=MZ&mqqf53!383C!ItbBnUJ`dT|zfF<- zl_saS&1Y%*hp9_4l-)Nk?>`YgX!jjjMpS>~jKRW&EFSKtn)3>bj|RV6X7MnwE8WKT zwZt`|BqgyV)hf9t6-Y4{ z85kMq8kp-EnuHh{SQ(pInOJBW7+4t?Lv^kWCK~q6clWdm$qpW)@@yqYGs|HD_BKgY4Wy(EqPh;X1iYI z2lxRBQ^A7=2O>M_j*5cHJZu+5JP6(dFP;>?rZB88TRT0B9Sq6){QrLc?_3$^KfH5C z+YXANcBYT0Su(c--?m0FwtubYWNO7)9uJ~nT+}^CB~4U-blTQOU>54;)Wj0(q^K>! zRxXe8nLgP-Hme6RY{_;BnxZ%UYV?eV~(L$MetaHSu-l18K4#9K|0EY zLk153Es89NL`4xCq6HoZ98X>`1R`=&l!X{weHfz6Gl%7@nyTs|y96_eu`6?2u~=k_ zVHSBK9FQa_;1GlmkqG%y4%SN{$M2{ysL(e&%f%LQ=zvi#ph=uyh^1>T*ls4XPVD&A zK#`DfCEeu!%X79J__ZqS<1GAZ#)i^nQBs-!KvIi<-e^<=K#7ZzBGfoG!X`CMQb8)B zMKqELP3Q%@q(#Ch4a7iH5#lv$+VQdO7_g?-B6_RX@TOQ<@t}^8mqTd0RsjQ}2qS+K zxwJAU(g%)Pj){u?!5}?r-m1{E&OkHeA)8)}uWW6gKN^n2_+%0&8nJ-*9p}|ll6WkV zl_Y6lriwNHCuSTmh6{@0pJJ&UkqQi!>!nXN>&*il(lH)sjlt6o7D-z@NUO0P1k z}>Kom|v+ZBX;K2zSo7B@=VvkThr5(qc=X5=ey>nE5C)I zg_CzrH8&Nmwz^9T{NmR9HO_zA@aEx%_ve0AKD9TF$ICDE`TA2|_-6dP{fXFeW8~NJ zrL%R@+gd*_$LjX1%spy4(0rlNu$$a`vrqh(J^$pzVtrrc)zV@U{keWuW99c;PxxZP i_S>`d6K$94T81cl@4YMF%>JI>myp)_)%(3e=l=kJReMhW diff --git a/toxygen/smileys/default/003220E3.png b/toxygen/smileys/default/003220E3.png index 645c904ee6a1e91a215987e2a1a61dbf5318f115..8c447462445262217ddfc7cf5ffa600e12ecac8f 100644 GIT binary patch delta 1273 zcmeC?TFNy+vYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_Ct=ABvc< zFKo)LkcnG^dN&4it@UqT>D#i*yJ@j!!vdgUAgTvy0P-P1Kv9SU+?0~_7w*3L{O{lY zx1WD>?zjdt^xD%88_qq>S$=xavAg%*d;!|=>J)RgU4AHUgi=>gCkA-x-qUVnV_UyUW^Cvv6kG28D8I!!-T^#=1jJW~iu$OrH zy0SlG=i^f4if(r`W?*1ys0xWF2}&$iC@9KL%gjktD5)$+Rj9}+Iy6@9(;IG=T=)cwA@J)nM_s?q`J&yMybcg5pgU7oyM@` z30avZb!|DZOiT7(O039=t_sEID_aWqZpsGDe67U%_}@2`+Er_V3tPQB?CyTz@PEKs z(luk|e5c7K;*xQ^hv$~MS|~qLjdt+f_Og(n^5U`IPdgvnEoZ0@kvhRI+1a(B;lS7X z^?Mt*Jw$~R@2Lgz@;E-;`OcduQQ1Lf@2yVTXH6{8?yKx;<~7{5_T`jUxNUuC=<4PTKtta>fLy7qD=B*5jOij>ul%twe&b=`KZp_eCG1P zhBA|U-RgTg>bumYe9Ka4Si-z9wQLeIzx<5NDxVil3jfc|`tk(hLPrM!8|8-y4-X#p zvHmo>vgO96^eQzuqj?-pGS|6Zq4>DSDUP_-hQkz{iBV?rLUgFVYmH{PXF>a zd_nZ1OGo)v8dw|4-2AWlQoQkfSB3nJ58A*q+T`it7$R{w_E>nmP?Ljz>&5mYKAx_~ zBOR97D>@4QS1;OiTysB^dPnEtx=_{oFH<^LagR@$8G{vi6HPkYg) z&Q;SAuIyX5E0B+YrA0l`RV~GK2hcsLC9V-ADTyViR>?)FK#IZ0z{p6~z+BhRB*f6b x%GlJ()L7fVz{nC}Q!>*kacj8v#kqT;qB<7?gQu&X%Q~loCIGo4Nn!v1 literal 1291 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 diff --git a/toxygen/smileys/default/003520E3.png b/toxygen/smileys/default/003520E3.png index 782ee47aadbfca5ad1da7edede68a4be897d50c1..c3d3077557523f445734c3d6c791f1cd0bab77d0 100644 GIT binary patch delta 1275 zcmZqUTFx~=vYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_Ct=ABvc< zFKo)LkcnG^dN&4it@UqT>D#i*yJ@j!!vdgUAgTvy0P-P1Kv9SU+?2PUfBgUdA81zQ zvQr3jFq3qWg*-+sR1;)CevdnRnV@Z{|m zpsnGPwgD~v`Rh;gq%CPPcYXTy6X=Lb4_>Y~d^K^(mQAN_9j(9q2`C z|9daqr%c%lccUoRkq)5e#7lzwKmmjdJfA&#_T20F6CT$`+koPXN#5=*GILlQ*?}DP z5>H=O_Gj#TT&g^qo;+YD9TUE%t=)!sVqoUsK~7^U|_K5of;ZA z?Y08XpXVaBOdd{6k1Y=V`MS66+Yi}JmDiQ+t`#JpA!})5uALlMN zJsmaW>$0mYnK2+G;9cX9! zMWcTLYmHWC$2_maBHeLu#s?=?I$K_TnKj@fKUwyRy=1ahlKuq~-OZOQPRf2PpCh8$Uq z<%-MSur|c?a9GS+CEK*w^Q)b;;G}hn>Sga(Uks3`S=MgcSLI>3P*i!}=c3RX`@5qy zyyW^W`RU+!wRKVP?QD$i9x2{fwSDbrl~@(4>RhM#l+Mdr9AloToiQjl$f5gTMF>Zi z+tj9+2`VM?zsu)v=J8(cn|RM?r^7y$cTRF0Hdaj&A3um@x_Yttz>gO@GUV-fW<1}p zMY(`0CEw$__NyaT&7b^htYOe|fAFRL1jE;N>jd2YJP6URdo|bHDwb1n`V}7azQ}L4 z7dDm2+zXqYx39j-+2e2N1cpg`N3L0Ta@qVjcqiIO-m3EQ{(uQ~g$x;2@^`o>ZsgYg zdeHeq->1#-ib2yZDb^G-?XY%Vy}bX#{i%O1NMDp5$<9GF6zJzX3_BreAus}C1yau9I6n0{bQ zLy{beaDtfdvpxS!Bk!7qHaveG_h9}iQ|DP+FVk2Y8%bHOJ3jqji%6s-`D=OgJ{vw2i+Sh7_c5(|QdVyhU*o#x zvDb>aMOmp5U-MTahSa_Ns_L4iT)hrdu&9=}MwFx^mZVxG7o`Fz1|tI_BV7Y?T|<)) zLjx;gQ!7&wZ36=<1A|n)qdQSFQLCO&Esli1dbHXtkNoHVb;UELBCcI1FLXh^uKMCu$H2rqm?F zBN=fB#-NxQKX@5-VAy?WIWcGqM*BEP&A3EU$Cb}y5gNlfy7F038i59$ghR3tAQnHJ zC2%Hbr!G;vIPo5oDkt;`z*|qI3BGvBt|G**qo* zd`-m|2@vZ}4MqoWUezG(b-A4q1pv-4F5vMp3~0w`3eY5lZpH~boR{HfA6~l%WKBz@ zxmYM%vxRm6V%RXU97*PKIaki@QneuxuqmDn=v}AkY_AN=|W<+s`v$!25VUEYJW1 z1rO-)dKmzCKg05LontdB+r!fyz|ew6000Oy%lm0oV0>YU5hzyh*Rc^rH$+8(b-OaM zTf>5TVmV%eqM>RDRUNH&z`(F-sQR#)#rZgc@7pgcDK)3Jn&nxKHUu^K5KM(NHG{9^ zmy3~4sUf10I!ggVd^H(DQU zHiic(Xks)p8aF<9uLZ*zm`Es?$bXycd+y-WbGXJMTHo}+_g1t|9y7l>O{2pwe8fd%iA@uZ`%^p-uF>@dHJ`8DytQr<67t8 zS1J=-Z(A0QW!pN%s$q8;xLW`K diff --git a/toxygen/smileys/default/003620E3.png b/toxygen/smileys/default/003620E3.png index 07f549a29a81544e15cbab51b8fd02c62dc10002..617d2ca6a00a4e5b1ed54badc4a0f63b405c5f73 100644 GIT binary patch delta 1305 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 literal 1227 zcmbVMO>Em#95*ynXluoUhO}{@#zlf`iS6gaPV6=9T-({gmbOZxwnAcxW4|<3>yO$l zadRo_VZek?H&Df4eC&Y6m&UkElZLcO8xjK!^a2PWSOpsF0G*fyXssHa+oW))cEGaz z{_pw!`~AQD($K(x&TWrw!!WEfcTg^%bw~I-*pA*^w;L*2cKgbhKWtb0n(9JK)a^3F zbEY~D3sBW(rmn#r3~Q?x#W8;@e^}6LlT}+Gos?~D+Ju&$lQRVRLh6Pt(22CfWr38Eq^!aKASy}F&$1i< zQkvr>rp2)pR^n4UEh@5-Q~&@9!*gkrJw7F}$)uFz`&-zY<@u_m!IoVE*==H_dtwF2 zg{p76McbZib-+;7_HD0fJGeB=;ZKbkmTuR)Xjq<&Xl3Xc&p|!w+9tl9U%}YIKAT9U zXi)@`f;>QRr)fDWqKG*p@w}2Cnppk+J;LjjjVr`#v}h-aT}SDGh(|M`O9` zliy!^z`QaV|xbM*C(XU>9T%5acX8)=7 cG8Wl|eSF0JX6A*j@$mPOQwHR-{iV5o0Ka{rI{*Lx diff --git a/toxygen/smileys/default/003820E3.png b/toxygen/smileys/default/003820E3.png index aea2c90eb7ea43e98dfdc72c7f9861d8ee315843..3ecb8fca96f837612b6332daa2dd1df729252f3a 100644 GIT binary patch delta 1300 zcmdnMwU29pWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DD#YA zcV2xiT6+O#=&q|zH=ci-y7=U_OHcP*e-52yL9oraLzv2GN4?t&DF5G?Z#k(Irf8TrYzI@?sxT^zieG&qCSG*+14-{m`!1LL&XV1N! zKjCqGv<)cEnB?v5Vr+i3CJxA9FY)wsWq-!b$EC`w6#aKP0|QfCRY*ihP<>*#LP1e} zT4qkFLP=#oszOC>0Rw|Y@6=GoZMO|L?i6>)IkK@_DwH{W>+4><>OXuzk9PFk{&!4& zTCV`JvB$Fc_vEm))#JuF(rav< z&9pvNY5vIU&b5a-4jr|O)vKF!`~;KT%+RZPOAUKZYOLS!$ZON0Iqx1H-+j3I`^1gR zl@sfwu21BAd{2$RsHpITa8HF}q(jHg`N!-N3m})cwzcr|UM6CE!uU3!~=Fu4iL3>lJupdCM&2(kd&fYCWeE z+1;6~JCs~q6DF`KZ#j@UOHo5ARNO|>&F#DE|ATG?+7)XgekxCveqx;v(JS)XZTdt< zKAAt$8>L>b&M3B9D0rBUU8mc=$eQ`GLtopJ3{UPypV*6PmFG8nVP zoH;T_%Wp5a__W@`!(z#=f=M5zJSsV{Kz8=_Co7}RBncPUnh80)aLJLgKH1PNZsYE~ zELeU0H*u~PXB-`z!;ULTr8@m^Zcw!T;#=7A!f2}8q3Mq9ynSi@^C#}t_`UP~i`Bg4 zFSkFBJomSB!iB${#ig^CKT7=jL*#^?Vd-bbQwz^&v_Ie8QpXs4qP{EXi}4Cz4rl1_ zba4!kxEy=zs#sHi0PBV1?F~unYGrY*tRlPr{cnyj)^GTo$^2hs=aWgQ4w}`Pj3IxT zR!oZ8d+|zz;ueqHm*0M^Oni}IaK$}2jz!pL?pbl~I1%klVoo^~hB=)ZZ_D$<$*672 zxxB4{Q|9DJOKb5sq41*J_Vx4hI|o}*~U%}>cptHiBg Yr=CnS1H(jRRW1ewPgg&ebxsLQ07%eJ-T(jq literal 1328 zcmbVMZA{!`9Pc*57#_|{lYuXzcAK)qyWZ2gD_nC2l=e<;?tlQvPE8!W_JM2Q+R_&9 zIA0j*78c_~NtiKANxYbt7-9Os7c#-f%od{4ESgA65JL>OMUiPp&Xe;gaOelJ4>oO| zm;Qgh-~a9DnWlz^ON#F<#xSfTRL?h~@eb=LDn#${n>hguWo95^wy5o9LeL?`N@^71 zp}5cio1q}}c3*)$4BOQ%w?@oJ_!uLqafe{pIFfMZo)`;rVIh(lArlkqOaV3K}fotHs#YnvqS zbrZAGPi#092{+-Kszco4aM?vC060xMfZIdU;31rH0*Z8^o3;Zt(iaQSNRnWEmoFm_}VjMG?AqRqTmF#SHO&0)25vvcx!DRU91zTqVZ^0~7#k zzzym=9vT3yiuQ6;o?|;KPuH=mt11w12LJ#8s;Z7+S*I5O7tgzS3godN#V`d$gn3;V z>26~ATVfebhk~iBTv*=S@>6CgVe37Rv3(j` zX#JK3y-BQIu5k#+t1&La}K62IQ;+r zf1t$|AH7+4=z83&eS0oF`uG1o&>h8#4?cPO`On|KtB&3L_TyJjeecHnd3&F}`wVp5 z>yKaC*Bwooy7k1(Cp*sF1v<8E^`W<)zFvCpB7WiqxWiAg-rNB6v3N<4A1Kt2f#SB)P z?muQck*9-IEb-URzwEW$@t56-roTMft*chH`PHeTH&*Q|y`5r^|G8|pZ`z67nI5aW zuPVB>i;Lbcd%fgr`0;~U`(A}z-Ee4OJqMrR=_$S4clMrqb++JwTf@yms`uXJ&Gvqw zAt2CutH&bcuBiU!o+oeFHbu|5aGg_c>&q3b7EQC~DXrYo`N6@)Rg7_!Li`D_z?B=@ z-|%TJpI4c5_kVB2lAE*Iw0-8Be)-V7sz)OsI5PC@6sce><-O0kmY?V6_*KdF4z66j*xDZ==yJ{a^e>O_7JUB|q)=(3`Nn;bEDg zxbO9NykahGi~&ZBsHlyANIEfAY`Gj^VZ7jDP%+ z=N9eOK5=n}h3TaE^<~o?mro2ZSdu2?`OeFSy`{nX)Y^A@TE(VM`2AXmv4yX7ZRKR1 zii(4FQJZ9}D!1;R($Hsm;*Fqnn}!D8qpzM0m-9ZU+M zcU0uN;_9Y&>UO_QmvAUQh^kMk%5tsu7SC(p-G6Lft9hTm8qe&fq|8Qfw|c$ITQ`K f`6-!cmAEyyWd|`Xny9GG#lYa{>gTe~DWM4fVmWH` literal 1329 zcmbVMZA{!`9Ph?P$Z~UvP7WPg?Y6`vqwRBd?OiL!akRa03x|Y5xlBLMyFSNNxc1f- z??59UalXVXuo=+IiBV(xqQOiPlbJBWV9`vYi6)CK0ma~EX2wM38*)Ac4*fv(!KUr= z(%Pagr8xRXBdK}{6 zwA==ppsaKq{RCEFSW!}Kj#;rtJ)h9iq-}wkh`VUuqG)upmB7O@EbsE-YZrm68A_6G z5<_dY(8^DwEGxrPR5qI>vu;v1+9<$roUP$u2M7i78FEiCJZ%Wsk(;S zigH}wO*yUM`R)mK7I8;K0p2k=5 z%c~pMXWSky9Sj0NLLQ*FeY6+~qKH{E<2cDptZ|k9lQRlAL)p#opJpj+p$@dC>#dI# z>xTzw=)@T4Xnb}vGK^uH>UX%g3+b?Z#p?J zwC~IKK=(lPfuWwB{NWQgSSX-a(-s9>{#qhuDIi;nwPKUrvr{Fv4>t9mM10;IRZ~D$1ml+ zyQBa32Uro?i8~zq0q5s`=JSDx>FKd?mR?FIGp#Ur?(N&Ye zsdsSKWBYa-_aarNvlu_sc;k)!mhx9WgwDDB-?~5k>z9fvJL?(x;%3*@ipxEVefR0( z)$}6|4?H`5M0jD-DL!>@Nv^rzjy~$F-zhn__+n1t-LK2ZKyI=(Qc-ed=|ZXRO!hRa z%X*VDKhCv&bn|y+;@65c=kjcccz34m&AwUhNG|}`OQjXHM2N$Av eLlM9>JFw9w_Rn3rclR~>Cl;0(#P@1jpZ*75JkJ;a diff --git a/toxygen/smileys/default/00A9.png b/toxygen/smileys/default/00A9.png index 5f524260c794f415e16d75f2e021ae430d7d1e36..57666e9f557766f039574b55384b9629b917de5f 100644 GIT binary patch delta 1101 zcmV-T1hV_-3Hk_-8Gi%-006c6H|hWY00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33MBx$Qmici00Mk^R9JLUVRs;Ka&Km7Y-J#Hd2nSQczv-F4{1Pb$F))K<`u+14{fJM~Msk!i(B$HpqY>Q>b-BA^I1G0T*Vi`>1* z>dmmxMu6GEC4c_g-8Pq4qd-75YBTbXbzgY7XC@zEV7tW*eeK{4a)8gtn0uyU?5PmKGo5s!3R({Y81b2&ZyD36f^d@8G|ol0ZM#VD`2 z;wiGCoay=D&EdscX6!&-q{s8hgz-Ce0A%Fi&?gx2hJUhv5bK|_KnjowLa_B@s)HeT zJlAa@5H=meMpiqqM=gS29gzAh=LFn7WFq_l*)~v4Lrx_M0KFIya6DBz1Q*vDeRd** zuA+Y(JUY=)9DHVkbIE$xgwTo^KX|z+?B7N0uylp{4Ef+^)ljYPhXw)e8X?4Mv~9?w zT{Sy<-+zS2nx|=nxFStOV2}kPw7_FpAt)ME7t9nRqx+)&U?aFSOglz?%j5$2D`1K) z1u|z2z`@rOHP9ogE{wTgB|J1%HTf^MQT(*K3_4v21((!JC&^if+X3-$NV(Nrw|`j0A>{r#NC2S&u{BSU0S|vLkCrTl zPG0RV6woIQvNRM`AaD>aS|2>5?9jIaN4o4vr|_`FjSw8|J$*#}hV0LNqyKHVQ@=g# za-PqNgsyLzv)g_vzN~Mr0@)~*E?!7A)X8#qf0xi7|L%&cF0T!u0002hNklR%owe}8=jh6wuxRfR)3q&FZPQ>a(<4ya?= z7Y-cw#A6md^O;|H%qI?ogMnJ=Dbk2W6sf27c{adcaEc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;?0Q|?%$$rZTuoff4Gmq5ESxQkT#Zef zEnLhE-JHzKVR}9Dic1pnl2c*!W`gYn>ov!#*UGslHL)bWC?r2W2bKZ?GV)9Ei!<^I z6r6+26g0v!^HTE5i#0*f1M#g(YEfocYKmJ?ey##Ie62FE*l%iVVs7B*Xz1(;bOF%c z76vYEjzAxq0a=!ouEt7Gy(#2`nLf}l`k=&tlvrRwz!V5#!jnFb1J69EdB7Y~1k4(I zmjziF7#JlzT^vIyZoRp*(d&qVK*Phcg#tM~v#nZ}#)|x&Y@*g`d1d>A%@!&VS{+UA z7tT;i7Z#A_Zryz0oViDFt01SR=>MagCmu#+ygqQ;FKrs*)?N}kUUnl)rvS`hkRakvKjK_@LpdXu^d--_2& z{GRik>#tdU<0yxFkoT9(A9?n@-=7pH-@5f+)4BbZs-I=gtqTpEe^_Pvvv{YwMskhM znAtXc6kPbc^7f9arq_J4f**$$#D>RT;Jmh>r}*XD2b|>+;;x22^Bm&MWsXOx?ssoe zeYw{A?IxYmogamEpWcvbeTes7%Q@%EPfORl^Z%3k<%;~{`i7wQOgsz>3(mgHC}r6V PDttU${an^LB{Ts5<&?6$ diff --git a/toxygen/smileys/default/00AE.png b/toxygen/smileys/default/00AE.png index ebc7dd9113fda178db5e174f7e62677a0fff75fb..98fb62aa4d2e887dc853405b1a1e1d86b8043df9 100644 GIT binary patch delta 1107 zcmV-Z1g!h+34jTZ8Gi%-006c6H|hWY00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33MC3#heb0000Mt{R9JLUVRs;Ka&Km7Y-J#Hd2nSQczVl1;i7PqvuY&9W;e zW6S1NZI{Np_uK8(nPIP{VaYC8n&t%EhpkguKO@a=3Rsv z@=3A?PzCkEwunlOd)PKudCW6+jPu`!+i+Wl;|dq&bh`Ln9wQ6*WL8x>mButCqrBpZ zE6Iv-x{nX<4lmwE#t!5~YFy7u82@boAR`xtKEa3=lz#<;nE#jsQh-ztf~^jwDj0&t zW8M}5VckJ&WHpIBY7qo$gVb+155WB;6X5}5TSGYoIi)B7^kPK7_E7B*TwHVX*@+Ok zivD@<=tM_x@aYlGWz@qagjO8!gO{tqzAtLR(iQF_QP&BG8xKoUb?u-6|jo?;rTQTxmCKt$;zzaGX zT?#OK894w4KOd-p9$6t8U<@ql-KUN%wn?MYk&tjm_2DGrF2(7PK|3~Y$$wL~n8haK`rJqWp#!mb>?8vo9&jHm znGT)2+7}ei2Mz!&QA~osLAYqX^DtzGzA26nU8d4Qc-Z1Z2##h?ACZ45`=Q_Hrww=N zxBFSn{c(`c^+|Jfo2TN_`t~Z2onq?Zg_J{`Q4Y^{3H<|3*ow|XeW4Nn007uYL_t(2 z&wtg9F-ikL7=^!Ggux<1ItOwE%NVSt64N+ata}c@J8WyJ+(Ob>sq6(}pFM$%APC_f zhADnacP#9EuY4bmuTtPPaL)y>XR~{vNDWP&AM!RY@*;2Zrs;nH>zmZ2RobPmG^W$E zO5HjXgdcpXcvbwR|4@AE2j!=AT*g<3Kz}q*AwI|D^x#AQ5YClP?m(a_9OO-|K#((e zmlu$0*%uBjxPzNJRb1yD-P|2qm^z@?-j24lGdr`b9c@3I6%@rZW-v!{G=mA{tW?V1 zsrd)MH|Qg5PmW&H+W-InC3HntbYx+4WjbSWWnpw>05UK#GA%GSEip7yF)%taH9k5u zGb=DKIxsMxsrxqo001R)MObuXVRU6WZEs|0W_bWIFfuYNFgGnRG*mG#Ix{soH8U$P ZFgh?W(ey)$ks&7v002ovPDHLkV1gDk+$aD5 literal 1262 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Yb>$Rf8ytJ4U07D7px@=m z{&Ph?tYY4M>FnnZ4CPV+FXS`j7rox~>33hEYuk*}nNKV4vDI;Jd&v3i<#xV}`rLhw zbzW{!{v$K%(DZM5pKa!s#IfvTZ_2%`Xme<}Qr(3$<%y=|AGcpMu3z(?A$cFG1cMUe VeAR1B5rLo*$kWx&Wt~$(696$Qv{nEB diff --git a/toxygen/smileys/default/203C.png b/toxygen/smileys/default/203C.png index e1c30571cad887cdc93fa3a53a75fb5d8abdc302..36d8dcf715877e9eb47a210f5fa489b35ba8b22b 100644 GIT binary patch literal 1311 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>sei`8KXiFNYp}mnVtFCQ^jMPdfhYq=Jw!2FJzNV+7hEGy z7X$(ILbL--KrsY|jX=dfM21sKV#?PQsvfZC|%0Hz|>R~5>XPASgue| zl%JNFld4csS&*twkz2sPV9`4@G8US1S99*u@(XoOmKDoi zwv-ii;aYX!!u|X8FT($HiB0m|CGNj%#*MR^wAGVOKMTv9er(V8+i$PUOkBS#@uXhl zY?a)3ArGS0OcINi<=*^bRdv|mjs~TOBRa8Vew!*MUhPg?xRqgZYw(`iX}i781S%*< zZJYA%#{>Wq9Zv)R~u`SY{JU4L!uzo$Xt;4G?{f1dY zc@9 zCneK$>1sXIvl&jDIJ(f^;1O#oqvA9Bv_=sH(UyeO%`3Uu9(p30 z<=`!auhHk23+hk0R50GW7U1XTr^&^zS3+=uZHP6C_;s1uGNqPvi|XZUtS$!1)Ks+; zOMczJxlq*o&x5C{HoWhiwV|Z-H%I03^s51D&-pep%y#4WD3dE~lo={J*H&C{Q{UY! zPB9Y~DH)t-5{!CabXGv5cNO*wxtNbzd>X{N_Jr5P%8V_1N1kn*%u`Wuuy{*~ zRmrEU_y7la3kH*{a+yVeGA-9@o+qB*GYgVY+~u>h{lkW%odPmv=h-R!5BcW2|DwP2 zyDRzMH_!R~SYhGo$@x z>Eak7aXIv;yHJyZ0PBVOPad3USfs%gP{jA$evWJC?hmtb5I{pEK%)iSLR` z8%O(Xxz(3S=OwtrUUzLfD14^){&(@qPdLMDc${-0Z4U11dt9+Ocisb)%Oc;spMPZezjfW~)88u(J(PFsFsbgoYwg{t;PLaOUD5RXol{trc5MpU zy-H`wBK;YKqA^cPHLn0&saoP1QIe8al4_M)lnSI6j0}v7bPddP4NXD}4Xlh!txS!z z4GgRd3>K{XZH}TLH$NpatrE9}i(j0(ff^)1HU#IVm6RtIr7}3CLv1^+VYQLz4G- z-#owH^FOcT#`pDfJ<>%GL{DZ+&Exff@VW0^{C<4xZyhfWBdvrc+zP7eJ|tw*EkiP6 z>yt1Kb#r$5A{-`&dn#6;gi6_GCBwC&dZ-g^*d9g`#PCSN(~TNLWEoCcPJ;UN)3X$5 znF*@MXPK<0z^XMi=fjD)@q#f|GenabNs_}22@}{5>14y6assK5pw@XMEQiN5MXsx$ zT7uees+7%uDP{v-`VQ4Bd)Hi9)G!K{PyM#Kqv+YBlU4BzsQT{}Q|_^%sVY6pc`57K!UxHGrv@# zgK_TeZYWg5-bnj>3l~M7Qe9)lh7QW82?~FSnwBXsvA80nfik2hX^jOy);N&jc>w?= zE{F=-=GY1=q+~V*xR}OiaR4+{C=0U8@QfhHZEVH~knR|;ZP&tfTUhq4SV{4r zj$FUsx>M~A$W>kB236N1l?j0yC|Zu`)`KU*@@zz_Lf?7;nrYv)$@Tn7))w~p7&pYo zGEg+^0ggM)sA(BTEMQ3#wHVdHn*S$fGb*%k2jmc106guJ|2y+ z%OhXlvHC8f%7wUIL zSD$;Td%ll`FW#sXA6)$Nu`ib{?J3{knpcnRdQVPv)GijAi^W{$JbCJ+uda8$nd_P- zU*Fbq>*ayv*Mc|pb?{dXbe+&UVBem<4$5aTz0d6J&z*kv+)Dnf(KE@9`kP~;pLAAk zFPzx^@z*l&7tRGg_x<#(e0lf6;L3$-r9;=7{nT1H$^N*Oyn5sNrCXh6cXj`=^l0_> z)%XwZ4}2IIJb&DppH5wA9(`zS+Pw44g_X~@?I4y{7oUE9@6FrcFC?Rls~@Be9r*{> CGLkL; diff --git a/toxygen/smileys/default/2049.png b/toxygen/smileys/default/2049.png index 0bacbe987316f94506cabea55109cce9950047c8..feb53688a863811b0f8891353f48e5042b3af11b 100644 GIT binary patch literal 1513 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>DEBv>V`e!WsLznln2K!qjmKSnNk0lu&h%$f_V?{s{;0S0GRs=L3 zf`GQb5zsm~0@{m)ETw=hfgqrJkP*;r5Cn7~1OXk1jDQY?AfU71h@fYITK@n4FB7;N z6vX}|L4KexMh1T*+RlG@x>)Ub+6S$(=M>jF39%P$JNIGkydB?LCBLr7&12sGUM{5j zpO1~L&%b%9=k>V%vNpYMZ2kP@=jZk>Ev>DbC2m0T7?Zr+U7}0&Of>;=*h@TpUD=

XvOY{CL)0eT;S5EBP@$0Pj zj1+NwxqZLZ`KG+zetT`V!@gaCK~CMKXRj&EKD6!EtTo5PZbX=v-R`zbvj{n=Cir%j z_qFPfyNj&Ms+4EwPx|?;{J!%_Zx;uXXLAc*n2G9t7ChN5eIoBp;(FOh-dn?pgxIbp zcWgPaPK)`U3$jlTkF|`O$T2UMNnz0i2KzN0Axl_UUfH`h3OI;% z2*kTD4GZBA^WUq_$@;qSkkP8h8_ME38%zWKihX9dW1rzw;ka?H;@qwoM#2u2hKCcY zHEG7kvLpq|8zsBAWq)>LJ8AR%UvoM%>Azprp=H{4mY;U7HB-)h zZDUeUV7}{uMo#pll+`z1yxGY)FL%x|-Y2WVf?FNL&pco{ytX00WR-4&WSZmmjsF$A z4OU02oAFe46897D2dgG6Xkz{Fu+!ql^+vBRS=+)x9zPU#<{?c~;#&?~oy7_|? zt(9eG-kEak7aXI$H^{_(@A`Tb18I^*ROoLbq8%zY3X!tfc zyzo>jRr~*6@!SFF-%r<+GQ8uQwT$z_6pv*sC2HR7D_-w)I(#^rv@+&v$aeEufwS;9C|K*>Hx+v#71LJ|9-*JsU6X;`J3ZR!%OI#yLQW8s2t&)pUffR$0fsv7} zfw``sNr<6=m9eRnsiC%kft7)Qx!Ef@6b-rgDVb@NxHY(C2Q30>kObKfoS#-wo>-L1 l;Fyx1l&avFo0y&&l$w}QS$HxPSpG6Fc)I$ztaD0e0s#FofRq3L literal 1304 zcmbVMZA=?w96z8k>>NSk*qDmvkqnXbuFqY2ZBHs`@2<9vzNA#?GR!>Mr*J~=Zg+<* zU?yUgIG4C(3Gsz2afav@HnaGWWp(i_Bx+)2qGrqdAQRa_qHb;xNfhxppy&s(4_@x> z`9IJ7f4~3x9ZJL=-D2Ni$1rS5BrGJ+T4y~q)#!cy&1DHK4W^hjJJp<7kaUOzvT6^+ zBYCM8CZUua+W!S?!?3EHoJyPNXa|>3^Q2_ykj1=)&=}U%Ueu&aKQ!?k*eff3;#%n= z0++LXqRSn1Ml~Mx$>Cugb`Hl{NrByhP|7VN@lC!i@Ox>*Nsac-8QJ^0#1AZvOy z$0dc(nk}^R6Md$saTHZ36vzTYs(LR4d_JGWLDLST;V_02Qz|+Xqp8dwKqI5enklOa zZZS$d>Y(W-kf*C5N!DhUmB zP|u)x$v0}_3zsTzH%hVz{){$Q7^WYvPvVwGn#S^?_vGcX&{)jYnE zUrye@zMFA*oPhw~MdSgB+v^lU0TeNdB%e=Yh&62X|KyB9&QMl!{HIyUN2miWalQ4? zW_@^|f+j{sqp|qcm2nKK2uFlKs(5Mg8?d_yY_Fc_c06(Er56q_RZYLY_fFgNchh2b z_fm!Z*T)~Kz3p&J8Jn4!xpG7^w%$GB-!?wX8@}0z1%EixX|HZPR=Rd$uGCF_{c!7# z?T$_M_eAi4t@(P*4<9a9Ur2m)mu-M4+`(JznJ#a9)Ggq;>W)&2j4tkyZ4h#uHr9G2aha; zcNI_GQSW&t(K9~>Hvhio?2vL`ax5iRY^&oZE}W{Ly{i1&HCloFaqe4V@a5M?>t7WS LW5TK6Q)B-Cc~-r` diff --git a/toxygen/smileys/default/2122.png b/toxygen/smileys/default/2122.png index 8b5e91a3fe4d2d2dc47b90ced5eb7e2f0f9f9b1b..5119eae764a869f6e99610b72efb2908b2ffe521 100644 GIT binary patch delta 1008 zcmaFHF^^+{WIYQ51H;x|=C6PhOS+@4BLl<6e(pbstU$g(vPY0F14ES>14Ba#1H&(% zP{RubhEf9thF1v;3|2E37{m+a>T%n*SKP@vSRiUJ^AXTBFKDU5@!J>C+XyCNl1{{B$i`X{ta6EZz zG4bp5@4s$7E_d*}_PXMB?w_Qa9EJwXDZl=H7XRB3f7z{Q`opu`x@u*cU!6L7W7W>J z+q~279e-OUeP+Y@Wr-{GLU)V2jSG2jJ4ZElpQTvXN8j(O+5{P0H*`d{y@`49&etrz zt@Rr788N;3k9p;{W0U%r7;moVJ>k5~@?+6?AG<>b%DP2=MIBsf_I9g)d(q*`DpSh^ z-;fbf-V(rEH4T5*1 z_76Sx1g|F<9-Y&+D6HG3 zxL*DQdx9O)<^PJzCcAb!$@KOHFaD8O{<6eMh>LIKM88S?s($TUw>DNr&#m8eRL4wl z{rX-O0V{#%Vs*(6ADW(LoGg0rrTXx1&igNxOTW94-@kdz@y`kiUr&Da#?1fd^sjG&6|9e5Im*7W%_@@b zd_L!2#@>Lgs;T1pP61PBxu=U`h{pN1^*tLo4?FO<&hPQ|e^enfeWU*GlujPO;#@P& zYXJ)nJuQF8ZOPHX^MKvS=HrC9u8Ox+{C=8#wb{IPeXg0rSAzyoq3foB7a3e8D(DGl zu3EtQL5YvY_}kZq!awfj@2iOAxagT${?~3dcj6;+UiS~-8yM$2(LQ9l`P&Mh8&peN zBce-E5=&C8l8aJ-6oZk0k&&)}xvrr}h@pX%v8k1*fwqBxm4U%T$A_z+Iv^Ty^HVa@ aDsgLgAQLGzQBj?Xfx*+&&t;ucLK6UXqqFA# literal 1126 zcmbVL&r1|h93OumW*X8@lcHg~6tp|@_Q&k*;AZRm$c5Hq-Ea{EXJ_8(w9dS7=Gl%C zq(Mc|!7f={3hEXpbO_2rL?C2Qx1@rgzaS*&(4p;(?pg=&&@eOa``-J0KHuLr2Kvvm zwRE;n6xEjQld@!O_rK;Q@;~;YqLSq>mh*TJ6>(X0AeGWl0n%wx9fMh@>eG{NU^hiI z6pdUS=QC#o4VgjJ*9m&2P0$q8-Q(G+R)Ux=z%j#0GT)b9GPI#5nW1Qg%h)0uH~MBA zI5^Xv(`HIqLT7qT(cPXv2uz4o+A}9CSMZX|2CqP5|CnXy4HaBUGMi52GXu1U97sol z;ef^gK=XVMM4~(oPSPO`gjkNed;mm*C@+L!boF6~HAgQBSxKqdBD*9rj)5zrfr@&Jf&J|Tu`9NS^T5iur# zgeXVk7yxo8E^#RZM7VfwgptQPi-SCUe8{kLRCZ7J<=Ko@f{t+!>WYI*dLzGrv5oy`I1=MhDIm(k0}^+flav&R zm?u&qA%~eNR{x)zvBVkHZ;pSOrFKL*&=CwGBrrW!ja=lzKF_-pBqKly37 KUs~)Pz48bD*lNB2 diff --git a/toxygen/smileys/default/2139.png b/toxygen/smileys/default/2139.png index 89e6eb401ca1a130faae0ce96899b1783bebfcfe..4393f8a8b74bbb27d04961125c4ff9581644e6a5 100644 GIT binary patch literal 1330 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>Z?K`N`ey06$*;-(=u~X6-p`# zQWYw43m6zIdZ&iQUb?NoQ}bNJmT6iG%kjdd%GW;f)qmbS`m>|&c3p2elbDnCTdp~E zb^j;s`xw4fZSU(2shi^A`ztvozj<`@@a+6suGNxGKjVD8_Tf`I z*3ru}FEDXLru3OB4OCxi+p^A->Jkm;3pKyNOeG>KPfl`#*1q z|MpU=bp@O!m%Gb7<=oWZFKGNZICIi7rPHeuD>kjuNI&xDq}Yc^xoI*Eaa%rCrYik- zZ_QxD#rwk9ZP6kfg@V7|btW(^X%cvpYjpL1fX%$u`&0zXn2s2&o_k}ln92>SCI3!T zGUesZ6pC}$Rz4+oVsIMc0&!)gZ_BgVA4rAWNLP2d6#B)UJzbrxtNDDwjti0fJJhy3 z>hX_T$Mw5B&^l+=q1v{Z51XYzvwtsiY}jqs;+A+jNG&^U^XBiq9XX#()(V7gou$RI zE1^ZK;tm&!>*8BYGZRHhYrEI?POmbgZgq$HN4S|t~y z0x1R~10y3{19M$NlMq7#D`QhDQv+=S11kfAiH;9fp=ij>PsvQH#I50hOr#i4gCxj? s;QX|b^2DN42FH~Aq*MjZ+{ELs4ZrNe2tkGl+xZkY^aI%ko3_tO z|G(e=_kVdh)KFhnxOLZ748sbe`-C_ew>!^61?c_wLQXl#=iO{xvx`Zl0;24US+e0AO$T6w^LoJ$=V@em6t_xXPYGO zRTHZ-M68Kwi8bK7WR!nx-YZ}5gnGLvm}{Lr@d*vS2Nm4z%Y!%;q#Rvi*mD9wIsV-HB0jh0yJeq(Je(& zafeY#XgyYlKuXs_Na?ZI2C-_cCkmyEv?ZMcUW!bmoVZr4O)C!nm9eR{+0?5;G7e3x z$B{)7HN1;ufIN9vupD=8tup@?ZTV;B)>U&AKx^q=My!MT;(lI9&A z*?l!J+&5fZHj>HYZp?Iz?Vj4+fnU~_7K@IS4bI8_BLlzxmd#dvHWWP3-U_9}_isPe zI&pT}?3R-SFWM__aN@}~rC;Bx^L#n;`ATm2!7sGk9lePc8Q+jxB zO$Fco%9H%^tw~Sj<(muNX0unWF4RuG>Ykb%ETY`;(Gvd9kxPF(dhCa=ryp};x6j_l TPU#O9JAbXHSTBs%wjTNiT0YdB diff --git a/toxygen/smileys/default/2194.png b/toxygen/smileys/default/2194.png index 87aa8732063088148f12785696f1409dacefa1b5..c8f4d491311763e843550a5cb1014ddf0145f1d0 100644 GIT binary patch literal 1268 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>6**|TTQy`DeeaecH6D9)JV?e3y@ zB!^cF$YC$>^mS!_#?Hs3%C2f=*~h@ZR8Q8D4&k)=<`^ROwdDBkp&h%L2ebu)sFD_)k?KP9l;$_)a|5#NW zdYGqK$>NBPbD3VzceT~#haGP-oe|Ufm|K23HmRS9@!pEw56;(iehl*Wzn7-4`zUAa z+y_Ck^R^1K7atB*nOZJ*?}7hB*9NT)@lCEFS!)z`JD=Kl-p1hkcES9W+m=1z5`A7I z^@pSXA!~`&>Q!@{l24uGSidt#BvSC)hnkA=jF0o=tS5-SG>n^PaPKxtgS#GIos#lB z0WpE3|K<7*m{u?e=GYovov4^$^F3ElCC5TYI_~Tdxt$#>uh~}F*Z3Xy>nr?_`Q$d< zhbBKllo*zX8}sD(U*kJ=bgSFkxk66i*Z$|&Xk8SPDRG-oe6r$3OF&nq{ih<)8~dAQ zZ76B|ZSwQP`Bev_=1*s1dncuKBzsEkg(b13+SPJRsb_C*X^NS+OiAEGlVnW7j4**N zr>m^mN6)O-x5EAeM;7ntzM1wmJ0141tW#QIy0*MxqPC4`U?bm?9a6r*vnEtHrak07 zVa*V%}>!*dx04R>MIqmF;hoS0nJ z60@lFh0#pEM~dBj0+zE(YhC_ny=|{ryk6|i%6oEq=N#WW@j~fx%hhMs{g_nuhbv|I zja@$!uO_G;e)v;=Vf?3CUCZLw!=3{ZW}~NzV~E7%*yA_FniM2lA70Mb9A&2TFh5Px z!))*W)vDsHUt-^EHT!U$e_PlC=7;Na-z2<$_2|K?MnkdUG?~Y0wHy-*gxEg1bTD2D zc8kAhDXX-9Lg`ssk)?n3>U0ZES*4=coU-(aMAYl(wY|UYr8Ko?cP-Cud%8N;)O|}` z^z4j}jC~&zB(>Cyi-3+%Epd$~Nl7e8wMs5Z1yT$~21Z7@2IjhkCLx9fR>r1QriR)E z237_J=4P+tP&DM`r(~v8;@04n9kd9jK@wy`aDG}zd16s2gJVj5QmTSyZen_BP-FVdQ&MBb@0Nj5b?*IS* literal 1277 zcmbVMZD`zN98b4;o88S7h3%@KS?Y&Xy-RX;N$*0hyEMt!8@sMt4_3sN<&x*x&?e6< zNxkcR=&h|E>{{5(LU9P%6`c-5%09?c#zyN)i$gcvKol7jCTv(74lT;O^GVyQekl83 zNb>xjC;#8?|DLhVj%Rn)Jz9rh*v@#nm_%#6^K5sa_Y-$3GFlpJsoU;?89OJN0OM7d z26$YTdq5J%>fq5u(1KxGGg_+K?oJ%!6sY@TN5_}fvj~l0E&KCXS?L2do(4Ud5g~s4 zL$Y&B z68Nf$-4`L&oa#<=;sP`Q&iDdeg`#PkWqovzVOjba+)vSdl0rA@rGp&9a{gv~{UVSx zQ_XNmF}7|C?IJ|4ZD%=>%;j>vT)+p-9+D1+!w!ev??oD3YtXReyw|WAs|+Hr6jRIE z8Z>Z+QBK1FJ3=5&*Fw;)0t!i6#68!7NrrK6&Fbl{e zu;75Hpn7B)t6-G7`x|l;kvH5SQ$t0O`$eb>=)kb!VuV0ne5$5$R3IdQcV}tj_a)JqD8=5Hy`>P$$*$ZuG^};MJbg}rp!sa;w_r&+2;r~@5wqxI2d zV|ajpCdNdgar^Ad77VNL#zj7r|2}>2xuYkCAHG!k>6_7e?pG_5$KK+9r)Td~R-msm z_2kubsZeTdnkp762VT2Xm~EXZR?5lUuIL`>((JxHJA~r&<6F;p+$#^<8oqh;vs2~D z-}BfUzW7wm>ylr5$o0C%zk6v3`*QT!-tj=}@^>pQjhq-g{rMNzd#5I+CvOauzq)en zCNV#L=m!6M>`bv(rr!Rcul}8e+URZnFV`EQe?>hW*Tsf2%O5Y?`T5)C88>$Jjk?sX zZ??@%@E0EA9~|XJPK=+XFCXV?+qbRgb63uPxUlQ}kA|+((|4DqYA#^z?e#-{lphg( z{7rDf)*2-JMcSOGSRxA(q5YW gv#I{wVGma0!eHaI&hwcUCY&EqT14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>kVwPk3A(Z3BukCV9KN z$g}ba>;-bzOFVsD*`KlVajCNGvClAJU|?#f3W+EQN-S3>D9TUE%t=)!sVqoUsK_l~ zV6f<&8X7%mjsee~>zYL@io4}# z>?)hIyXVElnxc=hqczOa&H3loPM%sRz5RBsfwSH2Wk;NR!ZUZd1vln>pSk6@kVS@2 zciyr-NjBGlj>xv0J5S_dr@iQF-lsOh|IVk|@AtJnkyB;b6Da?(UFY{P@4YrZ4VZ80 z`oDP1V*Ywpl(OG*w<#Vy=7Mn#{U>`iXmyBh@(j_rKH;w7(_86sGrn&ZUcYqPlt)Zc zACyV`;pl(JTGA7G)qkPk)wH(j&W5veIMQ8g_polM(C%kGG5L&T+&qJOulX4IotXZl zxGhrFQF!t%UH<{o3MRof#i?0tZ3{l$`QABUgP=!F)mHUyCtTGc!UO(3D`w1-Hxaa( zkP|QH*Y2;&>`-NR_`$D~w+S&$y070%5!kiq-`0<7jyM!tkj&Iu@+eW{A-jN~KR+++#8AApic;UdyLlxUj@c z({pW98GCbK%)LD8{GHw2CqBPcVr=2-+`7w?&!#3}uIf#`se69J2RY0yV#v54m9c1n zMQ8YG8=DOF{kqW}%M@qoJyJZ~H^K5&_OFgVvA6iYy4}~^5xm#7%CbLQdEsln;;`Gh zA9?@!rahsz==G!al#_m~hkx&H`NyCe)wM5m;lEwLZgnLefBA8nXcib z#l6-rEkCeUtM<8YOLu;*q+h+it%B3;Q>ANxj#4dgjVMV;EJ?LWE=mPb3`Pb9nO2Eg!vmQ}F`x!XkPX54X(i=}MX3yq iDfvmM3ZA)%>8U}fi7AzZCsRQM6N9I#pUXO@geCxFJuQL& literal 1283 zcmbVMZD<>196y(>o2A3d4`K(U$5muPle_1VyCi2~y<9GBwCO?`SsW<6}kR8&YN`wFYT6`wgyqoDk%meL=|Nq z87nBmFbNfPVr&Tp3Br-nQfVt4?-#PD;8tuMcezl+Xo3j#l#5Ds1X^SU4r_Xd`t{O0 zMQUn@I>g19cu|6Rt#8tRgOdZP?Bqx`pi(`LkioKm2@23s$Z}y+H-&PD+TaziY#-AU zxuIf>gs4rY((wc-Ap??}+vCbI0FXTI240TmK{v@VfTbCH^Df{OI9_0VWaFZ+HABq_ z$w;(e3-3Zy-m;1UO_xd~cgf>M#xM;6fq>1yvM#LQGADFPDZ6yDtH}_7X4cS(mWFiF zW>hk0+zL_H)6EbH#dv&+ST`Gq!YQN6N|6R`hAtHBxHhy+D+&K~V_R)AHBp3g5}Ih- z$l`kBx|(2|yE_}Q6|pzMK|{kuQAQ&uJ6?dg6^n!@{Kc(ms=#>s5+4PUPm-cC3xFtl zL73xs03<&jkk}^2c36Lu_lq*;m%R}g0GSmf(H|8(yy)Y7zHoqRVq>~#DS8$*?P}O= z1M9mZR*(#+Sjb2rG}`QdL>^hl%%dVH4f5oHLz=FllKGHbp3P_@(9oWNYScglawETj zwvBzxT5WDnKAs{bcvG#4l$T=N|m%gGB3D`NAvhpMSYf z1M7e7{()5X>2sgB&$WevKM(axue1(x=l$;;cILvqi;GVYisvhesl4S%8%c6Qf;@M{cLJ}ex`UCUAs61=0;qPk)ICS z9C9w-SWMjd>3d)0>XQ#MHP8C)7AISM>fPC8E&*8Lck%kl5%K=6j^MGinbWzYrz>A| zz905FG-u}nFSt6UlGn}J)z#WJy_IuUF11%fNAG@JJlg%%Z@W(9YNP4zcD<9w88~_J h`Pm&~`<=+Khaq0>3hrZ0^;YcPQcNC*RKr8l{{W>cxZVH& diff --git a/toxygen/smileys/default/2196.png b/toxygen/smileys/default/2196.png index a1769d44761d72851ccd5c79242f46b98b03d826..210315d2bb5f65bff00dd4b13e2492cced5a8aac 100644 GIT binary patch delta 1271 zcmbQmwU}#yWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!Dshm|{@At9?lo{nB|dh&3iOS5 zNsu2XM38~!vuDqqdp&=`#89N`BidBlM%RL4L zrkbjdh?1bha)pAT{ItxRRE3htf>ecy+yVv$i{7cBj+5>faP0ZqRd3_S#`3AyM)~!$ zRWhIdm}I0~d;L*OdiSYKf{PYrp8WFrx&AM~@XKyR(;uGg)>SLp{OZ)v8>@D%-R7Ns z@A%s?=`$PFFH2mh7rI;IZCuEM+c~Pa`z*!6KKg!N)h5X3x}hVo?M=*+cfMx%ZLQas z&xq-L%q_nio7Bg|cymSX3FmE=^&gAQ``8^iP}VK_E9&43?BdqaJSmnP z*)0O=6>n!uUbkxHo1GjJP0KI0J?kgQ*(P01dePi=#wX6llo!(4m;WiZW^*l7I&ppBGiNKN;72S^2`6php ze`37Rci>C?2?pNMa1;0B$5%A!Zp2#6?Q3ar&RR10Np$Dsg-vBS_rlWeCyC9TaQ(U) z6NhZa+MSbmD(Zh6sP@<-GiA@sc#Q@AG7M5*E2k`T+>zlcCztE~P_<7WHf{Olj~iHd z4_>_TMeyhDT>BTj`?PmVz9*M%+5g

AL#ao4ND4^k3gz?@-pYvRgi=_g#vm&41M| z?B8~Et*~S+`VLH=6FglULnJQ8o_;RWWFWxuU~4(!QL!Gk#g2mY)$iZ`4~tu5+;D&X z-P<#n|3#LmCq*}0?kx^_p}O>-#OEz`#zqh3yskEQ;wI+AVcT=;&IFN+#k|9d~3B9DE2URM8N#el&Jb7#dg^ vn_8KeX&V?=85r1EU*SX1kei>9nO2EggMV7iriqH`Tnr4Ju6{1-oD!M8###JVvN1^0V-U3w1qn^ znqz!eSYiTB!!}~vCtnhmWiF^o7G(jIkt{kjOAMM^e2H@sl|>PvNPP--=m)Y7Hf^8( z^Ys7w{oi}MCHi>z&ii*_7*-x`7Gh|vbe5*B5Xl%gi(;Om5Kuf~YiC)d*(g9+fmqW61OcD6H ziq#V!Hk|5+wBWpMK-^DyYh)S#oMA}d^D_*16!*};L(%AFYJiXPGn}Uu&tC+xW+T7JQD(ZEzgHU4*)^Y z2M+lC3;=u`!}6X2$5vP%Ff_`1t>_a(07MTfFkZe6uyl}R1+O3!uwl)#Bu$0|yDGAq z$1=CYa=Za0OE=ndy|>T-E#11Mo85X6=UW-Px?R;2J!S55%Cixz01b5jDj`Ep;OqJ2 z)Gh4$y}nvH7zDg1`kc7yXdx6t5i>|)S!Z!) z@IVbsjDbdD-p<^EVMPbSLa;48A3t=Y@5Q5cU*7)Vt4*t}5&NZ~Gr=Wre$`&q$+7Xh zpT);AV-5SpvswG#iJO`84dYpRZCbqVqtnuxoN>cHb5>sNo7iR)Md#l7w1K|**udyF zxm@;Q`HH<<5uLMbyV!3_E2Fd6!(-drp)&6CVb^QtCS1KP+dX#uLHYG;cF0v-dUXG2 z&hvHpj|a4ocVDRF-@bHb@q(RoU3pe=|2Uj;(a$Z;%f~*6yiqmJyb`~7>-SyL4_%mc z!KeRhI=Kp)o~W3vCQnU%S3mRKnJM=NYb$ftCV#1$S$agj>)4g!^mg}t$t)U}dcLgc z!Vf!U-kH@emp+wQJRvRJT5$j5{`;%;wb_$LzI^g@6>)abJ-++>j~5pzLN9y2jg;Yw zyQ&+rxsv)jZsu|~liXeD$wp^7_xtpAQR*QQ_Qy HXNLa)*r3hd diff --git a/toxygen/smileys/default/2197.png b/toxygen/smileys/default/2197.png index 2b637fe5048390005ebb9327eb3c030e528c7ed9..b7f91c48f95f414f84253e6f244d2e75f29e3741 100644 GIT binary patch delta 1278 zcmeC=TE#U%vYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_Ct=ABvc< zFKo)LkcnG^dN&4it@UqT>D#i*yJ@j!!vdgUAgTvy0P-P1Kv9SU+?2z&U;Y38A81zM z!V_1Xz6Y8ZKkvxpC+~pf#?Lu$1o?r&1sQlgd-m+P*YhVlu8+0>#Tk>l-CbnnusE^ysCH(*4K@P(n2cjB#C|CR2 zleZ4;Y>E1IbEQJb)4$ozZ+135Nw|DD_QZ+ySdO(8f6`;u3H>e)5}vlMV7{LG*PAAh z(a#q)Ht$}>&3Ss8@2rwg*}1mn2RHX!-qIK|ag|cNz=aWM3z%ZXPoy;HTzIUb$oAqROKDL50h0-v z6;-1zZ7%rMwx>=(zTqo}&;NFw3vcq6EoZSGH~RUmJhMcKlht_@zp`Y$>hk7=Z!Gp) zRm)#_RPo8z()tMuPVI+oS$Oi<)I6|_-XvpH`Fa182D_^a7d&(fTpSC9)=zF=fBRtj z6ON0iMz$U8I_gf>GQNEg{JDFJ{IA}9w+oinRi7z(ciz$I`s8OhX3HOW|N5pqVQx_A zN9Ro`=}QiOj(4hO`2DSGmDz-JF<>I?@pN$vk+>Xt`nu2|10I+9i%ZKVIlNd_?qizZ zwc`K(;1f+VRpu<;JxlPz`Ayk&x0&WtO%Qfe*6C8q=4@7KGkaZfwo$21?lu3JMy1Au zhEkuoLxqHm=3bno&yva&bn0o>x2nx|1D7wHa_h34jOb~H>{_et)1mdL4=3LK`|$6R z4?L$Af8o*+e-L(W->T?84BnZE%C)j96tjRXQ!R0gC`m~yNwrEYN(E93Mg~Skx(4RD zh9)6~23E$VRwfqO1_o9J26-&&LHQD)AvZrIGp!Q02Kh+w_Y)P>xfmEcUHx3vIVCg! E0P6ckp8x;= literal 1289 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 H1`quM4z9n~ diff --git a/toxygen/smileys/default/2199.png b/toxygen/smileys/default/2199.png index 377567308b79739a706f0bee9a5d50a9ca15b671..34cbf642cdfe7650711691d40b1729fd2df7c242 100644 GIT binary patch delta 1281 zcmZqYTFW&-vYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_Ct=ABvc< zFKo)LkcnG^dN&4it@UqT>D#i*yJ@j!!vdgUAgTvy0P-P1Kv9SU+?3>Hr#D}GnzZyZ z(72>UC$2yL`2YX^%TL}XE<6D=J!$^Y8_zy$IQI}}&7td06J{SsoOAH{)Av^%zm1=B z0BG^WM{lk_eRuiMo0u7UfsVNR@OAXm9YCjCdhjx0;ufHTF4o_F5#F}}=&pSi@4}rn z*I~C9&`;teL4KfcK?a`Bo;`c+_52Bs>!WQzamFNXcNfJYIlLhC>?NMQuI$g)`M6Zs zf($t&7#Nu9t3o15f)dLW3X1a6GILTDN-7Id6)JKI7#J*ir-piOs-I)PbLY2+Wz)oz zlXiT@$99LL&V0Q5_L+7AmC8mfo=t0}?Mj_oSNDJ7zK^TICf_S{>3!3^`u?64&$5q? z9-cLSt8{zE-8^TOZ_nJkId!j@M)&7%-G8MU*=xFCL&lnIqQyNNE4o~jtV$-Y`MzS8 zTdB?$KZAIc&$r+2Yc0|fWV|_}p8wgx*LEL^=Fi-7_(0iXuIjZDVpe^fG}&OM;N>N* zW}z=nsJ~w15PF6C_M&C2?>F2{oNB3WWbn@4|I+f?RG%%VBU5?BPaS)%GsQHaBIDzc+Ul_tL*XGIvkd%{%mtFT((+nFV@jh9{0oVB7a zdB*&7$jg7iTp-hU`M;vF#O~b=GLMgG>eT&+t+tZolx)8;!_Vrzk$*eWt&Nr0H%lMu zm@V9L-Hpkk-bx_)nP1U^hs^JDPS(75a`nHHqg9bZ#)+;&3I?3vVmxwko1E{TQ9d#) z_NAsxj*Z}ni}x0(5>kZ`t@a?6=g z&&Hr#5$mp1T-TE^GK_#iIci88|XaM64!{5l*E!$tK_0oAjM#0U}U6g zV6JOu5@KjzWo&9?YN%~sU}a!nZuUwJMMG|WN@iLmZVhhPL5n6Ts&g?gc)I$ztaD0e F0s#9sO$h)1 literal 1287 zcmbVMTWs4@7@{7 zOsaALrlFjlICK^E62z7%|VKqHJ z#;3*jx)xqVsG@0>d77?PtKO>Li}V5w!r`#Z!7v_-@E8-CDc3xj(b-@Sp^?*-vZ)}A zv>D|rs+bW9d%6~aT27@lh&5w9Q8;CEO)k^G>!VfGj%yWdm}&U08=Iny%tRT|X=tE| zp2PJR>uea~+}&Bo24Zjchjj%PMIIMXuA)NCOo|Z-fAQv(Jn!>|1TGGQpdiF027ss( z05O*301!f4SYR3)o7u+Mun^>=kQ5L_03;?9lcYEs^9Owl7i8IR!#1fIrmW>)Ls!AN z>$br=+VX-9WfSQcgvJ{kFkD0?GK#273L_lZyi z?maC>+^!D~xSu?XnB~82?__sb*53>D<_rFnLbtIkqd3hgd14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>nr|*I0XDmDdw4!R|@pqrUr_Md_ z>-Qg^b={j!fB*G6W6s{=H=ka8{03dy*Pogdhk!0wu;;?JpTExBep_wbFb%5cw8TC1Bx>y zdAqwvGbLPE0_3olc>21sKV#?PQn40xD)C@oV5+SOi6{w5ELSKf%1_J8NmVGREJ#(T z$Sq)Cu;`r{>NxGT0mt3qE;&awmQRIqPTcysSFid{UBIM|7W2xop1K+-*wsw%*_s;F^jll5JINg6^`97W&vl3LJf&`i& z57~sD7fJo`=oe(o(OSJ~ZcwsU>XPd*hFv)x=@oW+tk+bW?~pywy~Z$ZqQSr4EDkPu zeZN?g?@bUBaQbh_)zT2OP#`DUXls*Vgw5A&0vytONy@7{-)t0f+)y^@k72kDnr+_K{5mAPXY({VlHw#MXjt8U&b7PiTrw~Y6wXr>lZqt1*# zZl23sO^X6c4|*9I@6!LpZ`2fdxajDa{E0CN@rq4YG|d|4(?RvupPv3n}*HZl4anyY#X|RU{@z zxmP7zHLsoPR$)c-+){2`KE?Ixn^^>`1foByNPhUxRJtX_vgA`*{lbPm8J3a>ecmFf zetKD-K4@6Kihjg(F*VGJn?Ldn$H9wlUMhbwKVA36{8!%Lxxd~PPXAmHr&3aTdCu8q zYky4ISId>MJRr7aLRN_S<%d6Y7urwz($#Re+kY)EZBF%caSV~T9DC-fR8xQe%Y%*4 z4u;nc9J9D)Xb{lzyyk%s+PD$l%yn< zq*^5xr2;7iBLgENT?2DnLz56g11n=wD-&~V0|P4qgBd>eccEy=%}>cptHiD0rC>T6 xP=h4MhT#0PlJdl&R0hYC{G?O`&)mfH)S%SFl*+=Bsi5MF!PC{xWt~$(698M1TBQI0 literal 1340 zcmbVMZ%o`|7%!s71ZIdq#x|9%%xy&P+P8OC?i%jkuI+7SI3$4E#l(zqeGe*Jduxk# zyJ!wa#+YJ+nJ_+$xqZ<^88dO3xd{e7sEZ0QanXI)@ZaLpEm4p~kn=5Y=m)Y7Hf`VM zef#`=&;K5+uiL#be@i}wVH<;c_y)8Vna>6bdYAo?5z(^Q5W+^I8Z{E41~H$cwm>`> z6I)>e6s3a)K7~~nmK&9u!bUh$%SP0gO*D0E$ygkrF|4XO85bk%(7;<@tE_m4+aHb* zxGZ^yea;XSigU0{-jmW`W2&wxl4_5*C8FAkS0!0Q5QBz@Cu1Fo&L%y?GB1l{^Oz*? zWfh~{L##Lz4%OqFszKaovs)t+064?gfWyf!;0c_j08LWpW~{)$IvJL(#8)o@S<|E_ z+rS4_ZK0iqXfupBOOlC1!j`bxRIQZ+ZnxXyplK`8u<8dDLrhu~y*$gnLp`F&aYI%W z++-A6)K0@gAWv69h{Z#pHDX0yO%zHQnH1wBuu)_zX2!Lwts4#SUpLm()|(E-A=v@*&gf}L2!O8+NkByxr{o8v#tl08BlXo_pC zk2Y(=0~Ite8XAq`&YeGDSWZWf_cbNIZK-|cz`#onj^8_ZqGqwMH}iVeux}2GE@tLc zTmRtp>6ZRfzqevAoz6UY?B~>|cQBnGOLjdSlEDd|2_v@`cYYA}<|oNVhop_d*eCqMt9sP&Ccn$PU|tmwJz zm!~fNMqemcqOMP<-WTCe;!a8U+`FY^Jyl)EcWUFr1j#IYY=y`gUY wZpTRZ-rEI3ITcfV+kAd+GJjIWEj3*^*u{Yx!wUcXtWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D$h^Y`Ds`TH&bol>@N&zaj#zt#WvHGl6#pu2wj{N1tk zFwk{xKYcBpy$#|ZgC@u4K+lPn1o?pi2pM=jd-m+P*YhVlu8+0>#Tk>l-Cg90xD>R3 z9QG1VUsv{L?0j4*mVbRjQy3VSs;fdGN`ey06$*;-(=u~X6-p`#QWYw4>kAkdEPAJg zI!>Bnz_aJKXl2vHl#_LQ%^!6`Eua0&Jb6av^oEmcs|-$Tih4E8_t)Rg;lCb+Pn}iq zIPvNxy;Hv~BsoQTcF!`qyHoh>xM80lS-DI^En~UwZlOjpvh;6{Igdto&f59`|nINq31Ga{HX( zv^Az{?S0}h@6HB;!i95ZT75dX#~?uR>!EEM3O<(KFpJQ9*2#A4=M@**JJ$+g9wtrE zHNA6u^XW=cHluqNHK(>D7di+B5#d`tER{}h7{v7fls zSEN;RDvsgB(T;|{D{pC9%ocgSnOpIt-mmKi_X_hKJ(yl$@pwi16yYetN7nk~t$%l~ z;GF%gV7?-MU0C7OTX#%2HgvWpXy4nWH`!$3i#IztCf2K#UFIsBx!U0H0dBX9#^|LE z0Vb<-DujDl-hKYptb1_nqpgWcw)^Bwc;6sspzhKpz%h+k(6@W}Vyi0aqKOW14>l+C z)G+?zv|!z2u2Sv2N;v%9gxl<&ICsoxy!>BLc}978qs1fXD&ELH&Qb~~sQl4XR zZ_|7})-B1OuNjrre~!#?%39ycGND9i+Y!H@2M?Lw-<$OF%E{ILTANCq9az}RJVnjV zLf%GZ6T30P#8fTWLx-QKHx-*y|LXbUd-MF?Y5Q&$Os}&(S0r`5S>^70lkn}8TKm@I z?`OI<+5hgJ30!ljky`HPL z_v9+~LTzh(nya^Jy=JeS z+NAsT8+*CMtxO9!tGTl3&E3zdCf84N{U{f5%2C_CMpXAd%dYz>0ht}Iz5-pTTH+c} zl9E`GYL#4+3Zxi}42+C)4a{{7O+pL}tc*>qOw6PsvQH#I3 literal 1325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y-EelE=kNwPKDW<3A7ig*A=f`E9aur#FG4?ko^1{SPBTp$S=t+&d4uN za1J(8&rI%ZaxrN_)dV}u zY4!F0y(Yb^+F&=W{{O#49a@})hAgkVjaPJiHY(V-alwhBx>l`;Tqjry`etdEJ1=b) zW7g&t4%41^^$_om|KIuN2xqCVU#t1`FT2fE{hjfi-@hg=uy4?FkWf%R!@KOc?7pKP z9{$#UwCLmg`Sv^pJk0eIk0ew?ZBcEWq@dnrae4p#{gMek4zBJ`V4jqFRiedWhUA2& zER_}#S{0fho61^?ik~y z79erp!`0*Y5?Ryb6D+EqOCJ6{e}7@MOa_M!zn4zKfwSrPlC!SM9K5i3p;FJo=j-ix zgtyH}u&8@2`K_dZf6wnfy$f@nI<`gY+b?i^ynp}s&Xe4Q!q*mZ?`@Erz;sqZNaacM ga)&u=NenC!4AQJ8pW3Rzopr0Jp!<5C8xG diff --git a/toxygen/smileys/default/231A.png b/toxygen/smileys/default/231A.png index 699ddddf6762c56e0f1fbe6ecb493a1712a6bb27..dbe2607376507f182e62a42926a414fab7ec9e06 100644 GIT binary patch delta 1300 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 SR8;3;VDNPHb6Mw<&;$T5i_Xaa delta 810 zcmV+_1J(S@2-pUY83+ad001BJ|6!9G1Sfw2`bk7VRCwBKQ%g@1Q5gO1Obac2q-_GU zlt-kzBU+(qAOXT@Vvu+^|wH!59-wG%31JS!oO*1PP!7uo-p2aZBL5MKn&HB^A0(ZGst z6UOJgFgP<0i(3lm9Issj7?XL5U4hL2H(z?({M&e}qQp>OK+NEXsjw6Ty$<-S2$~c* z#JHshEls;$s&%jk5!eIbwv+e0y)AzuBg2>wL=+J*Nu^I(r9Jif{P=xr{5kxGv;#r_ z<#l>^kYnw3@0wm^F&Yezl4YsXfQsO8yTLs>%gc2xmy2~eo#KHAGCmEw8Qt1kpIMrl zvJ~X!Q>9YDNCqGpg}EO_I9SfzaNhhNYA3>#@uwJt!NfSdLB^JaKRw@1O9GnMNO+r+)q9f z0n(!&P+on;oo~&F{F)feu6CTJWu)O-TU(nnQX~=%2rzryw=X+8I_m8;_OJDgO~p#d zpQG*qTUBOz_M-dg6ZsyoojHF}U`Ls?Saa^_wQ7x4rKFZT3ov35tQIpID=KKcd;8WU zLhh%X92wze>vhN4+*GZa#vruxlPM{-!QA)&5F|L`o;3-vjjK}G=N_k1ZhZI|OLH?X zh9NVT6Ih7Jb`nt#QI1DRSZRa|y?6rqvbChKCwk}=9#lh}iN)6^PgQ@@TuUw}Gt@+- zQG-FN0=W9scWzz37y{mTPu z(!vHG1=kl3OeBaH6wna@7~hQ`=Rto8mWqtz;}2iVmZYg{Rs&hF^@L)B7cd@-C&OYY ovX+A7sE8jVvVVsw{u35p0C4zheS<=x?0Wm@ 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 diff --git a/toxygen/smileys/default/23EA.png b/toxygen/smileys/default/23EA.png index 557b09feaaabac24d757a608d72cc118e46ffe1d..4909b069d7d1b41889bd845141acc6d823d19751 100644 GIT binary patch literal 1281 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>I@&oxB8F)T>_UyUW^Cvv6 zkG28D8I!!-T@*HF+cp3>>?NMQuI$g)`M6Z9g`G+~7#NruszM@4f)dLW3X1a6GILTD zN-7Id6)JKI7#J*ir-sH}yQ9ESn=TsL#K;osC-gY_{LAF&_q)&e&+@#z{_)HVft?3j zo$UAjUy%P%JIbT%Wt`2;q@>xY;eN-aon3oNvG?bT+uKUhPOQ%K*yMdp&2zT8=nJvc zOOA#gKd7_s)x@g?hZk`08G27|p8O^*n8%eG0^@5O7Dl~+1h(^1fD?e1UR!Hjdjh+u(+HR9XAC}15+sQ=L@bt@`=)NMM?=NxQ zjPb$5?&kR?I>cL8TW)JH0sfYksf0s#R07AAhl)vYeUX zlt&xnD|d3gx$o#3vFmW{tePk3R}^Ev-AeVX32tUK*kA24|~dAc};NL-FR z_EPMifr#tF;ElwN|0|x?Yr1QCg$1(237_JGkosvLeY?$pOTqYiCe=mdKI;Vst07f`50ssI2 literal 1269 zcmbVMTWs4@81|ZNjFF0IE0FO3p1Yw=BC$^rJBih_O>CzPT0%)vEnLLn*e8u?Y-hH+ zqpth%vm3o1zG;hSIcNzvYe+9zRndT0Ox6n! zPpaYw%s^2dKlwTA#xPe=$>ywF>KH5Os!w!ud^NR#&=}U;TdRoDD75eb98t6=@#oS< z1g^+Y;y9h6QWXxCl!3YdhwAC9R3DWhGSSGvg-%hTWLXuKB&*e`uNv^_#s~=_k%+_L_j{3s*BsX@vF6px&K3g?P03IymZEF8 z!zdQ?F)K23Z;y!i4_v~C{k6OxYo5zD+B*^V_R)AJ6?fg2AcYq zA)$H{J6m9syE_|l6p=UV?+9a zCN^+KEXx^Cv~(k@>*ZDl43>0DH%od2=Y|-(>$sxHdeuDYlxHJa9vaFiC?^bE#n7mjcBOFasgtix?f-b^@_gT_`;7hC)p5eKqq(Zh5e`BpY3>N<}dq%8_d-|*kMn$wVf-wcb&iD{(YvcYi4+! z^%QK%ef#E1hdz7aUhdGI`~lZH{mHxRwYB1}Kb(5yr>{T#&hzqx#?-f+(j(2r!iUd~ zl%A|kiByoTKxq3SBEH?E(v$VPr+Vwg(Jn-de$C+>X+>@9KyT^5P{>CDG P+W8SBg*1OYo}c~)o`14- diff --git a/toxygen/smileys/default/23EB.png b/toxygen/smileys/default/23EB.png index 80b209b2aeb06dcd977e41520457e6bb7e297614..3240476709a90151e86afaedc5049490e3b67ca1 100644 GIT binary patch literal 1333 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>xKPD|b3p9M|<)^!^JqKDb^T4ft|NqZDbQ@?_ z{l;@Ye*gLX=WpZ2^FVvwefa^jI&$jn^ABEMd-4|OhRtX0?znIt=#t6X&d%6*9_XIR z#ruB#`U7;;+fQHLe)o zS$PHa0y*p@p1!W^&)E66R4o7ch^8xlq$-qD7Njav_TV$B`-A$VI`~Pd)f0T8J zYyF+Qm0M5VkXxn~((7|JciUX^MSJb#`u$jb=Lm_FhTYt8>RG77zO&Di&Way*N{vqm zH|*g!(c|iJr*iqJ)#h<~B*kutC71>F=11q2_w5woIN`R&U2acL=V4pp%J#;$g4$0a z97AX4L~*+o-MxIo%i1{i#PUx^98#}v-#&6k>iveb8-mUWm-al)Y~5B8eW|dqJ9zJ% zFFO4bSZk)Z9MM}Ec=Dvi`kfEf?O8DAUFF4h9~Rjfmnrz$9Is7rs>owzkhHY^!s)hW zp^n0lKkp+ISUp%3PdrP>VsrjbarSnILqxfNbR=)m`6!lyn;c*D&k-)DKOORcaZ|3d zC9maG4TdjCE(hK&y`^cfJLAjE)(J)8f6pIWEG&EU;Bt41=WE(~xT6dov+I|){@uNT zG5TFWyyE;nIzp??CSO)Kpe5K8yExZbZKms{&Bv8orrq)7K9ZVuh=Wvs+je;eY_iX@6=O|6&6dXtP1yC zwU8CrlKk0BcGpoIGsUyV-IzQq1y}E!%v15>K=qas+tN=-^$QowvE;~@@@RpqQ2U2P zCl3dI(=hE5nAvtovt|l^P}Aazm(!o;0~%Hz~u&*D<)<&P5o z{t*2Tm-Omc`;vuz)0`j22h}mg_;sas{5bssm^i0 zov|AmZm@3L``>w{&bbBi?5j6hOFx*u?7H(VwkcAJZf33CEaK>1Qhh~!MOTuBn~O=9 z=;0RM^cu(FlNLUgV{}rNU7cmkczRO~*Nr=#IW3!)&Rwk4y4T{v@4!$g^|CN~y-AWg z*1e8xUMg2Ew(tFYJMZ^*EN$wZ2G-TB7D)(+UpOnUhWXxA)rH124MIRCs+PD$l%yn< zq*^5xr2;7iBLgENT?2DnLz56g11n=wD-$zq0|P4q13T+0d?*@n^HVa@DsgM@Ps`Z^ x)F276Aviy+q&%@GmBBG3KPgqgGdD3kH7GSPrLyp3DyS@D@O1TaS?83{1OQyuQELDI literal 1340 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y7g>=oo!a;y_9)Fd<+H1To=BAIO1cp42>Gjwu3W zjVVhRLm3ztb39!fLn>}9nS9#ou!Bh3f1gK7A8Zx%jM^&oYN?XhqeWS)tzt_wN*=q3 zhwYh?*ERbQ_Ybyr+i&l*%A7O(4lJ^Mzf3(a&bzHgE4B2E{*lzs3G98m z^L>&et~PPHKKgdEuXCvjkK^73tql*0gJX_PSmt$m=}Mm7Br(Um4v)9})m>$8QyCa4 zaz`#|!=>Pgw>1ye{pC4c`t*B)_56;aS>}6F&L0Vx(>JxzM?GOtlR|a3RiD|}3-NqK zpEcI12wvrKlGE|;UlexA(|fPZy6V?~{7$hMVV@*qe%w8hmipRV+~obUug8AY?tXuK z$;I5N#TkDdD(?F&9TS)%SS5c_c9EJ|JQELt_Sw*V-p&WwLFJjJtDnm{r-UW|!WQzamFNXcNdvCERO6z4tt5GuPggA zc0MjuZk>kGr3?&AbyXn|B|(Yh3I#>^X_+~x3MG{VsR|Xj1q=)py;DQIH`U)!;JNc! z#FFWnxcqVB8L_K2o%wjV>8FL9dZpwlwrGy6S9A^j{`uFu@9Fw1)t%n`Qb)zb_wQ^i zDlGf_;c9v2nd<#_^X5u^yK1T{mUL@LM)xA7`>(V!dv!NlR582jD{IWul_0#ik)o=MX| zi{oJZKE7pP1}y#0cj>bns%p4#CQGRLiW28Fww3lpat*(u)edo=_|{gWRiqWk@ZxAs z!`qd&6nC^leY@Eyu&e9e^9MIO8y`KmY#ei9MSCpAT8l^O`r)mAcdy{|4&QNp0)L&E z@6~IS83GU1Nje?clpCxT8|t2Z-nV1TW0PEg@FlA_c33!wiz^Fsy5HALcU!K#@WG^4hdqx4pS*q0!J03B z>&obLj|4vbvhrZ4U&y@C%x@AizkbEJX*%tG_x|XMT$tG0GEHZ>!vmF=D{TDyH#dLS z<5V)!i~r$do;w^TFFtwD@yGRM|6jNG+#SLDZQm8WJKx>1E`QeM+j~Ugul4&kzM0}J zF0bNU>Sk%PU-b*OqE6S28EP$dz!W;k)5S4F;&SYnt3rny1Y9m2n_I6UpwlJX@!;M6 z|G{T?LRc;ItE=V2ek@;b*>UXR1$ae*gR6^UDoZ{RTXXcfN6Ti9IlBO~rciIj%W3`}nF?3h}+E z*mO>OrBPOR*>3wMYr_|q&)c{xE~l39bcSN6)Kc#wK=-MZxJHzuB$lLFB^RXvDF!10 zBO_e{b6rD|5JLkiV^b?r6Kw+nD+7a6zN0%)H00)|WTsW()*upG%Q{g}or{6N)78&q Iol`;+0KSex6#xJL literal 1355 zcmbVMZA{!`94|~y+>6_cW*j5Xb&3+8FZa^BYdB_Ud+rR!2oT4NQF_+`75YM3xP#5z z=H|hsi8n{cOca?POw>i0pO#<*+@fO`qRBphOC%WMyRu0bBLmK-z@Z<=KG?MVKhM+u z@ArT2sjAASw{6bZ3_;Mg;C{9m$Q{;`Wdra0*%S|i-Q?=b8nwZU^12Aof*Ka#pv*Ul z)gmu+w2q3U5VWa5s;x8YLKRd*l~LZ(L1VH8&=6F*FQ)O4X3>PhVxy$^k(t3E1eOFp za?l;ZLmDGCN&7o>v8J=KHqzM~@d?PjGPpEG0RmYxc{nDwC;vUaeHw#K!b=$u=4phC$feW{!h*@;0$Ip$A6k7eFQqtTCTS~ z*sKqasDO#l!Du{weliz=G7bk>x;FM*xZ=6iGshnuzVAxkQ;S=>Q)iF8OV1Gli>Y}P zP4+(aZ8+JPEGz7d$5YR|`D^DuS#LbG`~_E#$i}nui__O{E#JTri|$=jm!V%WZP|UF z&J#9=Lf`zF;dZJ+iy_jSG1vkMlPl;HLlj z{eWF~)oXvgO_&bfz)Hgt(OLVmCk`zyEu7fnO??xx*=fZVlJ+FFY@fo5%i3R?o{InR zLS*Y(mmWo(MWd(aWY?xBd~{(XNvip$j}|1i)L&|xElfn(HcPAzXvHB*Pul1*KqrO9!gTFqR_u(M=y*SaH<1Qd`hsFoyuf6ymAe;0Esezmi^Vz}o{ZcomcTWbI4JIxO{-~N2z-1);xI}=0j z&+mPZ@xv}Sr>Cc;-_B%C{uq66h`9dAL|0LHUuJ7gcgBu-s91YqY*HN`wtjCxu9E$@ Iy#AGc0JkyZ5C8xG diff --git a/toxygen/smileys/default/23F0.png b/toxygen/smileys/default/23F0.png index c8ec471c1ff7498cd7a71cc01b1b9154a5f1496f..63485f650ce4861c29da9cc89043bed25a379180 100644 GIT binary patch delta 1559 zcmZ`%eK^wz9RC^HVne2`E$_m4t(mt>#bg`D<~1*oylkT)VvdD2Y+T{dk=Jw`Iol*J z(TdI`jM8$V$5~2^~?wkJOM0q^5!rE)Q5qFG5ky|M*BTA?>&|B1iir}%MuVOv0?Esk z+v{BM&9vi9QQ<2qD|N1p%NFY3>T`zh;Rb$vSRg+vXnJ~jTo0CmL8WVd#loP9h%P^i zL_BMYEDUOJH}rmxPnowzo-@8nBs%3_7pumG$8X=(6XFM&mf9F`(du17;VDP^qcA0g zDy+zpD77(c+XAiEY`AEcb_X@upau3sw z`sI?5Q=4KRZ~Clka&ohOV17I;P_-&lQTnvr=A1TuHEi-U0Ai87dk`Kuxc zugpG~Wwq$)mxV9({g6z^jwd#}%+TdbSWuJ^7Z)vhgdfaofZ==%*l#go)PB3hmZ81e zI;Z!?wb(JnuUlBe3&8|L?9*dQ^TDxsQ}7ao^r&4y0Z zR(+>v-5yaca5FR?EIg|l zu`J+Wo1B-;e9e&^#(Kq$cB?$QIzm-hL96nnuesoYs}l9HYP#xweeja9DV!WhMJ%!X z67*6#R^JedmlCS3KRaikdpF@8@KIm)AL)!7mIo+{u!HPdew}MhHNU zMn`l563fQ81(&Lv!crc_J=y%aph1{?CRun&&|s=aOtZb!p2A#PU1O%St0Gw#^m;)7 z#p|)eO7hr?QeY)Rqt|aV#>XwP7jzaDWe zMqi0tATyZC5UFDaOBMpf%;+nS&iB;L6p!3osdZH*wQy)e`jHe0JLL$Sx5LflC@GK{ zP6(&^l9UZOkP6`Qd&~RZtT&B;mW`RjU?we+NykSg(ZK}ZusEC**1^i!HozK-x3R<9 s*;-(+cr2E#Chqu;AU=V1g2DQK!47{&CiHuQuT=oRgGhC2a*fFT3oIS7cK`qY literal 1688 zcmbVNc~BE~6pm;?0Ubc_z!cY|9YG=4O+sKJ5tHl&qJ%57fHlG<*}w|fO*cykIDiMO zB06f-dX&~1Mn#=gsY2^Pg&MFkDitYFi^ou_RT-pJ2L-y}u>HgFkM8X5@7VW!-+S-( z-fVeNLhMA(Ku;czH&GLZrf{Q=>lyFPy=V3g;@mKu#nM?FZDtE_hTthow1EIL7Cf6s zA#hXS$IV1Ek2ls#rlzy$+IX3fwg_;SjlgcPa%dheI>v6rjkyF17>I0=l7rpfp92BX zBnQ(Xw2;=SB67&MB8JcvC8QdQa*c=yjF}Ha+hrVqgx{r)6^~ zBnpc_F-(DpU|6A2V^LCN6oje}3|2>ua=ZzPi4_llV>jhgh1BRrm)LxU<(?al)_eVh)TR`MOv zUUl7Ro*e+kj%(V%-q};>HMgeXyu^^Q)Zbr|+^Rhlst?cyDBqcmPTh}g$e%oIZuMsB zo1KD%mItj5oGdl(Mn}iSKVK$jPhRS~-6iktjqYB4wj1g6%?O+F>ayQp^ZgIzw1r)O z$er<0<-;wd4|atl6dvB+7oEJ|?!FI?6dy)hD zR<|7xU-5Xa`-}d8ZvI8G>1xi&)-HCR+2Lti7EsoZR_C5r?0b5bV8YJp{?$K}{1I?- zl1KU%M}J;^+p)eQ`J(&gjEtXp0guuqYHQ&7@%6WqRS`|0iTRWW6qh!VxK)=SU zeeGAw&P6f7U`OqOy>X4{^Ns67niIEiT%wd#$0x=Zo_frSFhE=%&g2pq@^v;aZqF> zg9f^M=5MJ;b+UoB+Q0L12gS0f*zq-`%K`s|UHY?K@oVf~9`3$y&!hF`%~j>+uRY$h zwzBZ$r_Us%FYFc3es!mMgdcS_oC~X8o$2Py3)qy2@LTjVUH=LVmVh2pX07@M;O2tp diff --git a/toxygen/smileys/default/23F3.png b/toxygen/smileys/default/23F3.png index eadb18c4889a64cf75b56ef410c14c403802de8a..0958429bcdf2364bf395f865a8e56c935ffe511b 100644 GIT binary patch delta 1354 zcmV-Q1-1IQ3-t<+8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0TED4R7L;)|GtMgxxK=+y1%ox zysxymtFgALv9_qLvZ$`Iq^hrvZ#shp#!nV_b{j5*q&Lx0_?Q{S{>;k$6*zjolg za^SaV-mzQVs7=LfZMD^~uZ5`SDjy9ynX4~yzcDb`TF^&uCv6*%k=W?-p;S$z=8keqv61T-OjJ`@$CQq z|B(pSVE_OC4|GyaQvd*K8HbF3f`NjJhZ!7e+~vTj0Dk}g0b)x>L;#2d9Y_EG00(qQ zO+^Rh2M!7)1iU|JGynhseR@<_bW&k=AaHVTW@&6?Aar?fWgvKMZ~y=}jg?hFvfD5S z-E)c_K?VfUah%xNb*9T6zYm-^j^p3{S~HdmhzAcO)9;_Z=tn$F8_8XUH!n7s*v+ad z7h|jD)_-l4g?Syf+ifsI-c3VNU8*ipbn&7B+h(1#`Rv$O#b;fs8fG{Y!6s%IvT~8T zH<`T|Hrf(kHgSn>yW8p#Itm0tqc$QAnK#95o{_xVKz55Ay6@mpn=Z8wM{vVaB$ZbY zUJy@_Nq{QI7qUrIYOEos+ZLqL5L1Z+Krd!ET%J`sI2YF%eRd** zu7A9L?L0csksNGBxO2&T*o4rE=|6b6O6=cBZLoBO`*iu>XV$1%-wq8N+%;T?*J#@) zlXlhY>}?Y~Yo4ZAYZiLK%p&U`qFXRKT9D(WokO3Oq zT~Qp3gHt2RbR`sAQX`EdS0!$T3dcCSt!{M-Ee;{~{U8B^4#d_xNd`Q8U>z-4 z4xK#PUnrnA4sg_=>hVU(@UZj1S%4{0Me-em!G-f^g95n|;_k`85&4^CKlU4aw}0VI z{r0%ac|0!?y1r@7X4|c}OFw=EvQnrnen>T{lj-pGFQGp-kcz*Uz-h4n005XtL_t&- z(_>&@;1>`S5*85^6PJ)=00Sv$8Cf}b1w|!g6^Hr6hyb&$o<2~( zz)+VNBEZOEWNcz;VrI_52o+#twSTa*vbJGmgbOgT+1lB%Ga?Ca=-TUYA_;IgI665y zaKQz*U0mJVJzQP5p#nUfUfw>we*OW0ybuAtpx}_uu<(e;AU+&UC*U~%AdVJ_8DUS4 z0000bbVXQnWMOn=I%9HWVRU5xGB7eSEigANF*H;$Fgi0eIyEpWFfckWFg1}u;i>=t z03~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536*Gc`IjFe@-HIxsNc99b5TAtwp| M07*qoM6N<$g1}UXY5)KL literal 1465 zcmbVMdrZ`J9IreM%UwAtTzm8icOatZ zAIbz}%tTpa;7D}KWE*Bqk*OFoQCL{;!6Njna| zc0ICFWs;j*My7~OuM`+dWwzB;S!~nUk?qNFl9vDiPDZ3*ud{@66J9;Cs7rvkZyQD6 zMTl6eN0yw*HRZrYUSMFAOd++&F$`9#WtdW>R$~b;F2`_G4sNv+QxYmQfotI93jx*y zdm&*aQ#w%N;!ATiUP^FZ362O zS)PM^igW>ACh8I3=~4(zm&vp$%(<5n1yY83X%~vgi)#_>7R}7RZmfxRTPs`) zYG&MgnP3CwQ5dsA2Dy8+As-NUL%b`n;85rilDCyP8BWX~^$7SPv$J-B!gLsgD=17w z;S`2p+EiSj)hgsEDl$cdE2$NYwYb_;N=M;prG})`7*NrwQd5njT%%BHbSb!Tm7Br2 zMVhlQD|T66cbThrC6_P?3@!44mFG)VDj=tb7kPIP?}CjMH5|8-Flkb%~vBVce6e%$D6k{H)W0f zdaWSyLS){)5n;-6y{Gd?;&6lZ_|5M+&)$ACp}q1v%#7#%7SJ7fv<;fhC#!=x9^d5J z!YBJP4-Jo;*l>SuT~lK)x9zosnS`Ue`~m`ltGcm>M-soMLCCkq=q=F`j|0f5!-+)* zGsF7nF+<;$k*Y=mU%j8bm{;||ov&S$`%YWW!*}qcUuu|1V^dt~z{P>SsLY=A=CmX7 zu$Jg9Q?T-{(_j7_VQIK`Lp$iWO8Sq@@9!!7F}CG87t`EdGmS9owI6kaQ9J#u*dVD)&Nu_^qZX9Elx@{ zUy5&_N}I-e+eSwQe`-P`qP2Z4ai-pMrpvqT^M#PX{ve_*|72$1xh`GFj)j*kW9K1% YC>{wNa|fI)_x(&WsBH3^ls$+30YDlWfB*mh diff --git a/toxygen/smileys/default/24C2.png b/toxygen/smileys/default/24C2.png index 8af22067dad0ee4dc9178eb6a4b4b3f7d900376d..bccac5e6e6157df97f4403db38674776e2358840 100644 GIT binary patch literal 1570 zcmZ`(3pCVO7{A_+8Lvc5NwZdY&raTE+r@ZJ8q4G@%w%SmuwJ9+aLgc#*9=K@IFceW zN;rCuO)R2^4MnAgkqBj3Eyd{Wf6kt>=j=JV_x$etzI)I2-QV}S_nyo1@%B(t-JuEq zP$PM|QV^>t?aFeBRwf(08Z=$fRh8jIl0@WKU2LISL=hAk}{+1aZM1_H!c zNlID_2v|CN_?3ad2Pzez{s$i-B9O(m9v(q#WY5tG>zZ|QCPSCbh~8FUDz5>5|QC@k1JeNgmn5H@_V+YqU* z&C_OPBY$z<(A#{{+BCMgD<$)wCui7Hu19vnZ$o5;>?U)>%fpq`8PnT_OqAFpvYXP3 zlDg_1lsPA*4ggus3BMpVg?1d9kQC2k#WJw$v;+n=H7)@F`{L97&Sh12bmxLjT2?82 zaDQI+=L(VDilnU<{lGG|m1~M_y;2bp=hBt=wRU%+O`KsPIzBwsvL(+y5Z52@DDDQC zuwtvvzeml@uvPO)5x%I4UaOUT!?a?p^tCh>eAnS`Q>1L1<@V5${??$;F1Qn;8=`@) zyw!X2>W%hZJq6iT-<_p-<{Q1@>T~Hi+>csjpMtvUY(HMt^ho-3)bO;O*5bhB0YfL9 zC#VTSQ=8YcP};kyDE;kgm$lZ|p(Bah!sPas5_5j3Lb_?ADC|Jw{Xyev?NhkcnksSH z^32kdh1D_aPnP#G=sl^cy8x!$eqFmL)g{;||I2T|ScNv#Vru0nAqL(3)+Sz?+bqV~ zw!Yx~ux6ks(N+Ml_SM^>gh{M`K@j0SN>^Xk5T88~3%Sm-`JlQD|DE7Br%=Ehzm6B_GV*WB{2 zv{w?zf{SNG){LV1=+1(%4EJD{)@H9xn`nUvSuYgbafg|dosL3(RS~)Wu2r8eyRA}Z zSx(3JoYGc)%h4d29hjb*;^#wm%Ll&kR3U|E2EGx@YD{Z4S8?Z{KxW5$Q*=pxztC6G z>`w?Q$;om#dg;`3C~x$F>JX>r>S9J=a4fN!Uo5Xl&G1j)J_Am77E-p?-=NfGv+28os=BjA08}sFMoa0>a9kW}7udAaR_~XvR zHKRvQ;~aG~7ACGa*%k;ZcT%GREh=P)^9IQ3+JjY3{&VJiRBdJbGMGYJ- zm&YR=CT{UOaDXRAVk+wD>arp*Sd#@aCBtCMvuDqyH@9*wWpx<{=uwlCJ`{CjKe3OR zs?VX>$w~U;)QGaW%G2o)RW+5d$*C#kqKE4%wh?>4bY2o7>s^xb@=OAeGF#q$6EA}Xy5`sa*Yw>vX3J_D%itz z3u4o0Yz84RiGdhkiMO=0z}s2uv-aDECs^4KY^==icmf_@Biyk2S3+DooyAQ1|AgUB xE`>+}M#^v~p2 znW8{M@EgpWTX2bpvO%X&r?3Taj)h5JPQ;9D7)R#j3|pKN{7A;GNZtN0{;|v5z3=nh z^ZPyDSDT&r`uzBWc!feS-=0o6<#j>ynLSH>zjSAkmY3y{HCM{vi=-eeFbcxM7czjI zql+0QLwmyOdl{obF{6leG%UxdL#ynt}88C)$nSf&?8VGSdzleuS;1n+|@1w^M z2u!I+B_{BZQ@M_8faC=RK-4;wTLZ%YimG8ff}-$BK&yeZkVd{y6|Bb*6xSMn=?j#t z37#U{N!g}t$&v~5N>TuapkOel4(imrPz=EshDABFT9vG!62pFp4ypWNQjCFOM7O{O zB$oFBQAWCuFO^K7?CGNrxPZg)nAk5)CrVBk6ruwVtkysr7maI5Ta=v4zivEHTXcm3 z4CG`)zEp6_^(ab;f#uvi-B46f_6E-qSh*;)kK)~>9OIYllnIo-s6DI)*TOJLrCLxM zfss~Ou#g(mrX^DmEveDk3`mUQNmzqbZy*gCJ)y%87`AE&%!(3-71n7is3sLnjbZJ6 zQKJ2BCT5qF?M`ELPsQS-z|ayexOmws)8FY%(64*+Bi3cRw0^?UfBm=Z0|qi88c zV9OYfP2f3TD!(}U1p63jL1BWVQV~KIjhjG79cjQUI*iaGRx1TgV?F;TXOQd+6m5?G zG)wGA?!f5waqG*%KRVx(F%(PR4D>QsK=Z&nztCsv-geQ7i^xv=* zGpk>in=*$^>R7QuJ-DoJu;9vIqS8=vZ8S}>|H6XwK&3JxXzB_LB#Ww1mZ+yo1L}kX2 zCD#*MB605Z7e*t2@ErX?QAp@k+Tu1vZe&Nu9_;ED30IqEN$K_VMxt$hW7*kxKb$!W zj+I=!Hu}r1%*?hTu(ABbp_6$Pi{HKR_WMmi-+lcUHfDT&b;XK`?z#}FZ146d6Ix8( zlTzPA(`{SQXCLOC{n%Yrwza)=Us9D+f5+M%elJ1qgDWf6SDw@7pZ@3yy=!FM(yqM5 z_>C98{_b$^8WqQvim+p2g{(W0j z$ee$uc zo%RQFZ;q+EhAu9=f4jCec>c|CJ$I;U)t8(9%-S5MfR3%pPu!ihJ^G)uTQjM4OTm_Z E0QCDSLjV8( diff --git a/toxygen/smileys/default/25AA.png b/toxygen/smileys/default/25AA.png index baed68614f565c39b420e1612bffe3590a5f4a92..54992ea60805a6de57bfecb91f408342adb14da6 100644 GIT binary patch delta 988 zcmcb|(Zw-AvYv&3fnn<}^H)HMCEd~2k%3`jKlh(RRv=#?*(1o8fuTx`fuW&=f#DZW zsNn?zL#Y7+!>a@a2CEqi4B`cIb_Lo1C76=D-CY>|xA&jf59G0zc>21sKV#?PQsvm% zsUi=QudfP;C<#g|S12gTPs_|nRVb+}NL8q)&n;kJu;`r{>U-_B0?(b|E;+{6;(d=L zHovZp{q{q)aq^D7+jYJ1J-RJ!vji>v{`sd|+Z{h~QpMwUM>p#p`({wOFefD|?`m0| zOLe`C++2=tSwdo^VVQH5R*JRdZ=AK{$E%1HZ&tt2Y7=Cfxv`_e`u&v3&xclvA9l)S zOyho9vAzCm+4C9RjSV?Rs-7HtJ*Q%4zt6qIgz6()m1`HQah>(dWn#?t#H6KjXG(oK zxyK+t^6Q~(8#Wv>&XHbY@~qS9Smkz?+#PEV7q)f>?~5s!;(mh3&Pyq2`X!CtlN#%H zJUF#y(VTaW-FF}E{x)$VbLB&+>)OJH_WClUJb80LUE*WYriOZf&->FGMHEC^6jnE{ zrz>_rK>4E_U`kdT?2J&gT{FJ;G6iNBHC3as4h26gIp2(9T)z&l;gsS9fL# zBuwTwxbEUM#mONPFWxK`RH{FH!&m!As@@?UW_^i;ta`$&3o=3?Kd4A>-WC7L9^Dju zF!`v5{LDKF_ZgLSzDfOX&T&=Xllk-7f%l70q=LAM$PEUM8KE;HZ%A73KFRRto3=&4 z&wj#d>7Oh<=?}itpWqO?yH>zG>2Ze0{oKdX^xU0}DPCG=ek^I@cY5s5B+N}@u{+7*odSyPl=Pvuqb$OpdY9j6JSobWSvFoQo<_hCe z3xDc*?q?7^*tO-xYK4=)B%0*u;uxZFzW(Thjl2y8JS^usmNYd*&gk!&DbYO1QbV|a zk+rL3s)Ka6-n;np(?|cCTw%YprRP}LR33{N7K|UxENwf{bbO)4Miv&cG6ps$&rfrI zWxS|7V|7`<^WIGD5TgZ;R_9-MFINAIm-7eEI@J=_h?11Vl2ohYqEsNoU}Ruqq-$XA zu4`x#VrXDxY-(j{q-|heWnjQ|??W?+j@71g;I7(8A5T-G@y GGywqD@T(aB literal 1118 zcmbVLOK8+k6pbH5#}5TTPz0Y*QPG*?WhUt)&RE7IV@I8Kn2uP4f@$*FiEZ-Ix+t+;TbVsR%5F2t(nOFL6t6c>gh?|09+=iYbknL^9% z<&iZJhGCZHnzbSw8^Uk#BKlwV<(EN+)kJS6ZMch63?DHW3wI)rbBu0OM20oc_Y|cV zWlm=W56gX)9f^_Lg(Q&$AW1S(vxOqM78G&K*c@+ zVk{mtc?f|d#b82|B)A<2JQO&dzETt>6j4%yMo@cL>dm*hl%kfc`J$^dTPDO)IIdEu z#47O^_PaSK%W}ve2vKSg4F+6dRHJUNq0XS8!1Qg8*w_UjqtS``Nt&ffXF_nie14YL z4Qh#^DdVb!$H5rSIZhbYv~@s==#Pv!t%K5lhqxjNaKCTT`{>$G2h-gB+fisp)hMmL zO)tgh)v(#`AeZE{G)qrnmTf6~JgG`qs5Yu@5=n1$>6eS4Nq$I0ContPno@^AM zuEzC*E<>mb$*itw*$k8fS=1#c)v-A@AckwAx?h|6tzm_KVina#2El#_i0o%23CD{u}dmTBYHPevlo9)G)*TseMua_z+*kqZTDqa@a2CEqi4B`cIb_Lo1C76=D-CY>|xA&jf59G0zc>21sKV#?PQZbkM zVv!7#udfP;C<#g|S12gTPs_|nRVb+}NL8q)&n;kJu;`r{>U-_B0?(b|E;&YTr>4gi z8*hHyt5^N!oI_pA@!b8#^pA--DWBz-Q&;zY;=YgJYt{B<_eSXnC#`vM%-3zW&UJF>t*{~?wd=_t_7Kh?J zwqRbKgO4rWdov~4I_PYkbwqBf!oh8hulnb(KlnFI^C4@>@3tbXqEk8yUy@u7yj^)q zaYvizx0{_4N<{yjKX|jV@zI0Jl6oaql-F^twRkjn`szdfVz0FLhVM8(fxj+naaL}g z6w3~2=M0f;x~ZGe%&e;Q>YY-gcW1WlSamfd@dB&zmIF`26f~r+irGlIIey>xKf$|T zbw!TEQ{~CrPqY*AF0{-xoFOXw=)rw9t&6J-r&fwzW#nplaMJZB#~d~z>7bjF{2rZR zFRE6SZ(TQ4uc!IBI~`NJ+CI5&`I*ZL8_II-MJ=n(n{!lc z%C}n{44&*sr*C-j+3a~RQ+Fo++&#bIH9Xw;IC^(6wQHy>`;d_I$soo3;7s=p-Qbss zHaRweCoaBusr*Si)&5)budKs!f4wc7=3OtR^77Xb%iU*#Kc@cuA@X9LMd?RJt&sF7 z%Acbr)iH8ecCFhjzF!oWM58=i978nD*C+ou-ym;b$g_t@OyV(LS9Yj`p~#=)fHv>@ zN7D|)xAF7*mz0qFXKY~f=jgHr{D;tD53fZ*vge@c^7CKq@>}a_< zyQf(}k0CZg^sy?-Tr;3GswJ)wB`Jv|saDBFsX&Us$iT=**T7uY&?Lmrz{=R()XKzE t+rYrez<~eqxydLxa`RI%(<;$*AT-qVmFZ7ZROez~@O1TaS?83{1OR)qoA&?! literal 1100 zcmbVLO=#3W6pl(oN{b%6SVYJ4(xSV`WOuWh&{%hqth?aWk}h_=sA)3WhBld)Ox?6t zkb-{)!Gk9$;ziJdibzEe)Po3KJoH#E{(vAT3RZ7gC+%+aPM3#uawesaj!hnRqAIU~wK7BnB0ss(mkHnd_>i|!P5j1XVuxJILq zY^0Jn80DZONil~YB&bCqobrenCA_e^#h{_k3LKv}*aI=6If5sN!ctFHLvZ~{WsTSi zn~9<+<08}NV3OxtH;!w?IwS`A>&Cj)VRgz!oPk0-8Cdi_>fJ3c&E3XAu_5(FJ{mam zQp^brTazyGNLf=@`X^~Sw#=t;s#t_-R#l6-0AXHF!$Kw_La63MNflZg>tT!8Vp`4V z1wE}x5b8osOVc5w)Ld35s2bnGmc5Xeo`qVv4%Ka9soVxwSq+d$a8Si~qE!KdW0>G@ z4EsPG5<$eK@ocaGPJc^a`u zSCXWs*e2HgpPX^j87?l4e~P7bL@O{}u9ZIBtThkvXvYM!H8$V>;n21^Th{W`Xz}w< zx&1llnA_PlbMf~wyzyiE9_8^)rR4NpSvuCWZJ#Q19KSt(A^Ygvt(kL&<~Du(^`&}u zeyKD#eja{Y*j;$r+j9%Ny!7-0d;P(O1G97Azg=5+an0Cb?Z0Zgk4`Bkjhpj(Di2GS kGHtJ=pIx7p+dgmZVBX&8Kf3th&1~G?Wqm-qS2#8M2fO5B_W%F@ diff --git a/toxygen/smileys/default/25B6.png b/toxygen/smileys/default/25B6.png index 7ffe84e84b335c2b8a00206b4a3e0ac8ceb24f6b..9c291f6e36eb3e277440fc548dff2a0bb5685e47 100644 GIT binary patch literal 1296 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>Fdh=jGd25m02nJ?{o$Rrn;(- zh?1bha)pAT{ItxRRE3htf>ecy+yVv$i{7cBfzxg)@cel$V#_qGh2?l*(&y{nf8Bgs z&NgYy^^eoHXqFJ9UrcH88(*iDgrSEDycWhW$FjL6l0CLtIg+U>ICwO-`=z|!7V5ig_<)=WH8 zzW=w}shut?DarR{wy!IG^wiGiCkONGUhOwqor+d&n>wfEIctl*)Yk^O0tjlR8Pv?vG#CbtG9<;>?aNX39L0;GiJ{BnrxyL8OM8gYN@NG z?J@sv4b$H!KW6x}sQ0?I@WI;I3@0>97Vt}Uc5P}n@bi460;>nB;*r}4s~Qz6Y@XM8 zDy(7eNDZ5OW3iaZ4Kt6w(>^oYu}|@;V7$3+;he5HM%)a0B?JqqSCw(BOKhzzQ&Nd_ z{cmMqZ5SX^V|BQ-uj+{DLQ(gcPeq|O_IJ!C8<`)MX5lF!N|bKNY}tz*U%)y(7?*r)XKzC+rYrez~DsjlIJKIa`RI%(<*Um z*r_KI4b&hBvLQG>t)x7$D3!r6B|j-u!8128JvAsbF{QHbWGbkbV(@hJb6Mw<&;$UM CX+Eg{ literal 1277 zcmbVMTWs4@7+U;+x__>m7_Cx$g=rDV!T#SgL>Rdx!NjT`k zp1>tBK=ga!R6NhYA*s8h!M;*YGE*AP_(fuW5brFqh(LyhfEVQvMQ4ivVwIOgvUN-n z_^OIA93a-5O2vC|PSqgpak?BC3ILp8oWSj27_bkgDL|7Hx)}#>vmS<}y?Fg1kTp%r zvI#y~w}o~AV#qM^EJ+p$1!uwKRJ9xl{C>a1LDLST;n2qwLnt~F-QHl}p`OvCydkLy zZZQgJb<_wD$kVkDG#71ye^ZY1EpZfvNnC&%-UOh8>7 z)iS6aS$hMFa(8<}mLl?oeMpl~QG^j*&5X)WF=BjxKwq4qB(jvt$1zdBc{wf`p#ca- z+~9!6!vMhf7(YihI5xu4UJwRumnY)pqX0x`znhLmeZU7?KHkgu{0(eO(G5Y#z=mB3 z*{x&Ux5cuY1_eXalBzn==z!iK)ll^zHIH+B41RyVq=;%k@36|V7A+4oX$*={O_lN0 z{Ib#p_B}4QmkNgg7m2v7xP25K4Woz|B>DXj7g5KG|0ic8a)z{;<3G*PI6@t0iR-P8 zHtWL!6*Ms#8jUZN3(FYR1k&2AHdvRn<9l zV$Y}PsnS%i<3zb^KJdcNrMcj2*}O&LS36GqWxKq0!DgDTK7DloZ+oTv(&FWX*5Om< z2T$+4IQP?1;P^eK->qz0GRr1~XWFN>|B!wC=4ZnDa}SG^927(ScP@t7YVS#`-EKen z?DVFiXU=V2Ty|A=jvapV;!5?K@(#~F^H%M?_Cqg))C;LM=2*{%B%;{;pjs zfBsD#|GrD5etGBSv2S-z-#gG&GtC*{zwztMk=H9;X zd-K2-XKG!p%Ju4u?b5b^?3KrU3tnkChDF)QYmam#G_h%R_DAu%meyjE3vZbQd%8cV g?U?*LWShX6F?sM@ diff --git a/toxygen/smileys/default/25C0.png b/toxygen/smileys/default/25C0.png index ea2a965ca27071ea622faab957785d738cf4a720..0ab4d16800d9bdd93cf349925b3b23592f3cd48a 100644 GIT binary patch delta 1261 zcmeyxIg@LGWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DTcTqf&!z%{lu$OrHy0SlG=i^dkR*L>Roq>U=u_`2@ zBq*_5p`a)~Ei)%op`@}PRiPrcfPulHcWS8jraKBecV3HFGEHk?IbOf9_vTWq(=ztD zGx{GFPMTggHL)eY`!3Vv^Y86nME~g$n-sfC+<)7Q8)-MEPTrVWyt>RI+3wf2+_{q9 zvZSWHnY7jO(lbxieMhx2KVIEv*uB0g#k7Y*qsjNorKb;6c8f>ul`xCqHi))3T7Ua( zL8mDfOUmMF%KVYf51p<}`NYS3yI#!t%hu*#y<0^}eNT4rPfoFO*`YXJrJEr%KtHE@ z#giF_w;eu}Ja=dEoP6cH!@073+A(u3KYi$4*`xDda%AY+AgRj{J)gR?=c#C)dTCcz zW3lIlgrxNZ@s%I<7$xqx&Envu$5-1VsIS4zvGHHEm%!Y<~U^>6|}F|sr}}@t8c`v!?kTSA2w&L2z$QJu_0EurJ`>e@60W$ z`p(UpEf9G;GfHutN1&iY%0Uj@4>Qs@y4^XnHWWr)a)u>BNJN`7r+&`iIponeR ze`mK1FW&JKn(+DcJ=*>D(u*Qhm6)Jfi<2{!?vOQND5&tBx9g}DpW^lFj!YbLJI+2< zk^Jz1{c}eB$&y!3uKsgkoNLLEG3DtF7sWyje!Hgj_BRU@j*2ci9a6;+zo=#f+wz;2 z)1T;{s(WYtE9-FVueXKMi|gZ5O6o7i+H-apNV^8QNb^?Fu@OGn~Gm7?tHIJs(+OI;c<8G z^S<}|e$V$k+S_wavty?N!?5PqJ|ThD?e?>+5xwvH)soQCW{Ul0Qp=h}NrzZi*3uA< z<)tB*fRa2u`abN$u+3Q|)o=F4ySa>(cT2X8yOb{=G=_DCN(Cu13{5-@hZHqPTz>s6 z0$1c9anKj1;sqY&lzn9#Cd)mkOnErN%0#FO?<{eMAP-FmFXcy6gDVAzRbCFs_AyD| zt19MjkXUo7Ki-S;nhtTF+vCbm0N@Pc23{Y-fDW9d08LWpW?aC_`52D&#slM|CZaujZFi zHn8vWc>Pp340uuW+HnUcAsR*zGe|;Vd59WT{y#Y*ku#*-9RF#S`Vs0tTU>8_v{@e> zsG^C{(P&hEpYFr3O?zTOI92*0-F;~EiK&*iw=6%ix7u{fdTR37@NZzDYW<Bhf6fCh>^?es;T!Pqz`W;*z9@|@X;t^1TbI8izZwdZ zFShe9yx4f;xs#onolfWdw^38oM^1(w{QDhh{9X3wx!qUWF1=ZrZfX2}=7w7qU%hhT zjMlpU)=SdND;GP?es?+a^8C{5O@o8yKOY!8-n@V%KQ`!J8n(SL*;Z-zu(f4Z?jgsr df;aA++=QJDeeH;E`O*HTVKK2scquY?_#buTx5fYf diff --git a/toxygen/smileys/default/25FB.png b/toxygen/smileys/default/25FB.png index 1a9b1e4e1e8056f0f1300cb0506afb6652409289..02b39c87550c76032d123c330c68069b5e25e4e7 100644 GIT binary patch delta 793 zcmZo>Il(qTvYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_CeZV!Z{E6f>&~4!ckkY<2jT}09y|ntM~@zT|Ni~Qj~~B(|Nism z&)2VCKYjZ2`Sa&rzkdDv`Sb1Dw>NIwxPSlt`}gl(y?XWc?_VP$qq@4fii(PsmX_Am z)|#4{>gsA+Tie&KUq65T{M)y00RaJX=FFKkZQA_#^A|2$ICJLA88c?2q@=uf@#690 z#~(j_)U4Ok%*)G5O-)TtPtVHAvb3~(^5h8*509gxqpz=TXlSU5iwi$L|FdV$I5;?z zl$2y-WaQ-J6crV@xw(Pip{lB?rl$7tgGi;p4|Y?}0Xh!TUe;V3DAJ>xY0!7?Zr+T_WCYJ<PZ!4!iOaSpUIra9kZ66#uXSOQzUmfFDTQlyrD8N43t0m% z1c>N<{rCQGl>7Xd#dUiaR&H6At74)%m+8cUX{rA=tr0utFfHJ@zJb@~3E9?f{mknx zCiXmcxL@+~+P8D>?)^KcxY1Vji?U>h8> zaXQJpGbn3sUw?ZX=czlXg1#cwd(U9@vYUTq z-du5gALFZdjhoYQT0a5(tyqJF$E(QiqS3j3^P627?5U83+ad001BJ|6!9C11En0rAb6VRCwBiQ_G6NKoIOq#%RR+fe3mL z{1(5!|MUkudlMBGSrEN>5#lq(%ucmdHmtjBLo+>-uA1uZNidyGKY}1=JLfVH3A~Gs z=_S8@AtdF|GK$qMNw!jR$zaj zz~CTB5=qlk_WQlm>-As3x7$s+-L6DYBvP@d7mX>t6 zTvXj;GLi9kEX`(9@;ujXmNkJLB%Mx29RzV4E9v!mRf2xMuQ~Vz3``Y(jZIpuR!V?d z0p%@EyheKJ;PXidEWg`{qnF`Jt1Q^$U#t{Px1qnX?0X$FE zh6JqqhFjrm>Z`E{2Kwn`xBVu2_U0Q@odWl-*e?!JcV7Yw0DhkOG_q3&wg3PC07*qo IM6N<$g6j6x00000 diff --git a/toxygen/smileys/default/25FC.png b/toxygen/smileys/default/25FC.png index 8ae60bfa4657067c3e2f12dbeebbf4627bf83e4a..c1ba9c6e6c78e2367bbc1de304fc7a509316af37 100644 GIT binary patch delta 442 zcmdnM(#|qLQi6qnfnn<}^H)HMCEd~2k%3`jKlh(R)`@0%JPb@p-tI08|J(b|?4PJ8 zQO{oD>Fdh=jGd25l`rO1qZUxlFHaZ85RLPt7j5%`14WK}ykGg zNKW3_iyb)>Rf1IulcaCIuM4;#p>%N0xAhm#%O&*Zk(ZkA(K0k-?uXJRB;x-R_LVrgA7eNW*9g9QsMD?f!!xBT&NE~n@Iv>Q>kmCHk2_ubif zv?0sNT13>i_$5<{>xx^JDWR=D;~gd*xf&UmtJ@Q<@G>ss>2~KEyFRD)_g!9EuUG%vhb7N{938*)n|xBoSAFN>C99rQiZKJjLAAs+q9i4;B-JXpC>2OC z7#SED=^B{p8k&R{8dw>dTA7+^8yHv_7#yxV$BUvNH$NpatrE9}O)6y@ff^V**4`RaMk=jrShyz2|njQI;k9{hsZ1%XmCSL`ae( z?(MzD7{g|>Aw0JvN(anFBUB&{_`l6;*F;08>B t;h)YQiFYj`zr6Pg7qtF-5)mnW4*=$JVebF{002ovPDHLkV1hsE;_Ltb diff --git a/toxygen/smileys/default/25FD.png b/toxygen/smileys/default/25FD.png index 66144a8d1c8b6618d4c56fda2568893581ead3f0..0aab8473db1d2824cc69727a1c533f73ccdda963 100644 GIT binary patch delta 1005 zcmey)F`Hw8WIYQ51H;x|=C6PhOS+@4BLl<6e(pbstU$g(vPY0F14ES>14Ba#1H&(% zP{RubhEf9thF1v;3|2E37{m+a>zF*7zG)jqSlcD?>=j*i(pV}E$wl}^#Cj0G{;?9uQs_KbF%F8^vb5>oN z=zq<%an%a(ZLYrD&o|snoVrub$l%@oc}x7ams+hW;5@n9UG6F8rUrjOBoC(1|u%s7tU^r7S-!06#V_JGl6MIlfa`~ zqpJr5Z05b*ry^j+bi`=&+#8FFR7<&F2$#T!`%7p|<5wkAK`cuHWT>);YTl)wb1q*en&A{d=Kf z!*0VCx5V2)YT0RntsvT?s8>6?eE;To>PJnwcn4BLACz8_V{t z%)=V;Gh-Cu84q`Do+h|=rPQomi_g6)T=Gw>WoS}1K4TnygLnDaHym@+jHH8ZPV#$n zioK{>S-#=5pv7N#qo$3!xsMpi%{la_{r=YFm#0oB^z41PqywUvCSipRR~gdHHLZgU*R@(yb~Lo*A*;QTh@>q%Eqd5=0@>jj|Ep5 zDr)WtY`Ef?!@2UH#(@`t-!kObSWP6}WphqtYkRYD>5tjgVf&RIe`2$h0NSrw;u=ws zk{D5vYL#4+npl#`U}Ruqq-$WVYiJT;XkcY*YGq=fZD3$!V35bMJ{X|`s3A8$B{QuO Zw+8u0@%IxI)wviLJYD@<);T3K0RV?=u>Jr5 literal 1143 zcmbVMOK8+U7>*Tu6a`PdiW(w_MR$|QZqiNcTDD16qi(I+1s6qVnoQf+CX*&pcUvEn zf~X)`P!UwrgQ$lhDvF?@#m7NKFH#Q*iiq?iDtZt_>!jVS9*PG;lKE%yf8YPkfyRb4 zvnv-?QWP~?U#n)xIwyQ)Oe60lUq%hGEWlbGH=$NsFnma*EYt#N-7(r=78+Jh*CSX< zQBzy(Tps5$8)OqXQ6toe79EeEDXO-v=ow}Q#B>X6v)v@~@z!02wyh+yS;%l1Pl4_B z+Fl5t9Re7R4xt3!(^C(mV%vmLspu)iPZ4cYXr9(!e1$E;jLp&W1!SOPg z31T-WC5ohsEgBvRq8#fuVO(R{0nWldZcM5j%x(v|SXYw_`4Y8kOXgw;MN9*wMp4ol4?s$bgL**_0Z3vS?++w zc7##Tjyzgv66xieZP!ADpgJtic(f|??H$lc`^cfk@+;eu*cW2)8ZMOribgyjaVI!6 zogxv7M3N*e#+0zu|KyA%&ah!~{M9VwBhrDPIMMoKGci2SB@^S5(YTRbxQL9^1zk<$ ziZ34=(JSwBv$XeJZ#(9{8C`s*{*%!EIlt%Z$z_|WZeJW~9{Bp}_;fIG?%2S!`A?{y zug@LbbNb;Ml>6C%CHGTarPIM1d3|`;&czASZxZ+tw j%fZ8Eu73N0cFp9dc_;0<=l9O82!92-)}UUhZ|?sM9u0aL diff --git a/toxygen/smileys/default/25FE.png b/toxygen/smileys/default/25FE.png index 300b92dd3a559836094d65a76f287c07d4f9c69b..1de985b27c1812d3ce5fb658e5f7f13e41a8b3d6 100644 GIT binary patch delta 1044 zcmZ3@xr1YZWIYQ51H;x|=C6PhOS+@4BLl<6e(pbstU$g(vPY0F14ES>14Ba#1H&(% zP{RubhEf9thF1v;3|2E37{m+a>vQL)wbHJi#r{598jHDSNGrX{v)j@53`qXHaC-!_)o8!)1x~(dTXrI z=X>(~?&)oNX0@s8PRX6Lr81~7ccWKSO{vbAN2}jO@+!-8B?xXVxoLUwnQNGSTkAEZ zGh%)pbL(fn)mymO!NDZU_R)k=t+;2(leSBrklW}KuWj*kk*@k9j&;8UJfFq3NI&x2 z#1X*y(`j3WSD$5y^b(sdXJjAONEb?{y*7wRd^AVT-2QU13*+oFtFoqFnbBr+NqgOn z$!k28|K3qi9`VsmsZ2pX=y9Fvp^ELC4IMEue_UMmDCjEGxBUCAA;7ePNie50WYr?4 z4HjRv32-9DBNdcZsD|O^NUsa(Ad13FJu98Ame01E)~J~3QZ#ZFU}n>nXp(Os-rmW|Kl5gr?2_T+Y!8ulGfLC+Ywy{zvC?~9{hn5_*%Mx8 zD>JtEwVq8@m;CUdv2sg_Rq3a!dQHZ;*#Z|lE=?5B;Cu9S#o-0AFPB?5mCW?vf27zg zte9m|{j2AX>#g&D-Qsh1c<;A8U-a&{qtkcwvsZKGb?Lu6zTTmX>*`s4O?GQ#*?Iqk zezC6n)YUe7+N*!S6gtt<#W6(V{L=b~CwUJ!@VL%DtkE@LlJe955#2zJ*bB@uD;QW# zS~O%Gn%aBLDCR%TDm!nLO)n zJx@;8be~NOtLJ@XldRPW{8n=06VQ38C9V-ADTyViR>?)FK#IZ0z{p6~z+BhRB*f6b z%GlJ(#8lhBz{nC}Q!>*kacijSE7J#RkObKfoIg=pmWzSG)78&qol`;+ E0CuRe!vFvP literal 1195 zcmbVMZD`zN91qHDS2it$b!<2qQp5_qBu{cl@8aFIyS%udck8aV?Z6F|CeO8@O`bH# zdFc&7ER#+Il?mby=?C2>-Pj8Xow$A29HlVW2UoD*ss)h=PAJOQgzZUtSN%}-!I0$n zKTrO@-~T-)2aB(4?bz8t5X4q}pHjkWXZ*B3gWo+Xj|{x*2-QkBgsNfP@F9`2&@d!* z#~6VnXjqdIb1*{?Pgm`7C9D))lT75$Myx|ej)&0%k=YY@hB*d9au|--ZkD=t`3gna zR+c&}6xf0%!%=%*!-qqSV%cnrnQ4pKvzyFB5+-n9XpoUJ?gmnnrB-<*EXT(TMXsuZ zV_9m=sY+pxl#vfff#wq?3jisKG)M}f2>MBm1sucTTTFnYB#06>KsGN5Tl1}|R8q92 zExgN8qhaVt3{$Vy={ip%e}n<)bUNnXxCGWn1e0!PL0Ucjf-N8E6A)l&<%AZOW`lHWm^)q!lPMw315M)vR$%kV7FKtI&~Q!IvTI|zO|0-ltR(x; z2$5e#XuQ<{gQF-!!6@=bc}OJt4%@DU>cLBKdDfy;pl`ncEzL&`xtd?e-oU=VCkNPE z4#+C@0LPtT6)lG&7O|90t31`jTK^|!40eWzo8v#t(mKK&7>nzzk2mYX16@2ZJ|2y> zA9ntX$7)ema^>j9Yo``BpI|$c7r}J<1N((;_-gm?_P=L(4$bs^_g8W6pC7)pbmM4y z{~r-|S-53x1+p;I_r=n~-A5KqbUyXzEq2Rqv(K(% zdiAc0Q}*e>gWK-TzVlIO>ZW&=Y`=7~qi??J%{#Aub^NoDw)d8&`{yrBzkGhv?2L2! z^qE`UuNv`ie6d?SP}!v%_`LSL`RyrZVqxy$)WF2T(dt!m`NF2L+dIDe{atoT&29Aj v^GWS4edoFJ%c*ndmwUzOwk7nwz5N(*@~6hfAM}57J^p>@YEe0xKlb)xS<8{i diff --git a/toxygen/smileys/default/2600.png b/toxygen/smileys/default/2600.png index ad91b055b94dfd35ff49be78efeb5c2515e4ceef..fcbfe564743edf74c220173ebadafde4adbb11fd 100644 GIT binary patch delta 1268 zcmZ{kdrZ?;6vq!>DPVaB1*JsgArH4v+X|Fcr_i+m3Pqp=nKCF31qIq-E5!s`ih_}0 zi)_OO(}tG_hzL`e;1C%Nh$w=oFu~yiwOD*15f#1rVV3N#CEwh0zUQ9L$@$~vDi>-j z)Q)l!0Pym*j>5u%9f4ti0OwT;=L9&+J0}H4h5_vL08s1)_<)rZ&j6$pfXM^^=23vf zqVmfTYq5*^&7nLFy0Kzj+GX=+-xoC)Axs@hP^&Ha5l}YFCq*!?FP-v#8O&n) z4^wG-Oey}eGuBv(QR1c`w!a2nt-}mn5)#J2KfzmBxD%QmkDCG*=kDcgkwglzokdx~ z?P=*L&XOEainA<31mIu%Ge1sOPBF{fVIRj@U@T_rys`C`KB?^Qg*R2w^pDakxt7gp z73Br5??4*i>s@~Z`#IikA5KoJ=f{w>(ft`0Q%?-b&YboX zu2!EFv*JBl@6=pzcqq!09jbaGCtq9onRskLty1Q4f?cg8HCsmB%zcvgUPcm!UvRfy zTRa6Rd%7nOZ|D1oFfcvpPja4IlSuWE;*I( zr_(iKmI1xMZ40PVn_XlaJKkyIbMm9#A~Pqe#IE}M;oW3q!c_1~;VpA{YsU~Wr1H_7qRtAk5l z3_Z?2#c|s;ecOl9ly=nST*So^k6-yM&8qL38b-p3bfIteALG0^t7XPeNlf!KgOttf z*CWll511$73S+`E)k1lmr&#G|qvUx_wsD+ND{APL2MQeqYjbs}gdr22IFV@*r1UO#I5w@Riu$#(d_Im z1FK4}9x9C2o2|nhXhjG+khf$gC49t689?}^sk~lsx1v^4Q89W6p&fp0wJzKb16=0N zN`+!YC$U*R_uSrCv-7e=0Fi6c^_y4H9h#+Ubf~ax`qQV*6UE2hQ_aE!OUg5-^>1xC zF%3?f<)UrHV~_Bz`)K{rggo{UZLGs-bK6bi-lFEZ>#k32_s7vQ?p_G{_V~k*b&drw zh*8uTU7E1=a^ty<+vud-OQH6OWqu;T@ZiC#`9CJ!UBcNSNft;_m`PbFSO6M@M)Ri7 zy{SGtDuub)m+9;CHHE^YP*SYAf5(XbfDB=B+V-6P2Z&L*xDppn3xE&~m)*!pQ2qmC CKRUbs literal 1410 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYDOA8lcC8*vMa>7g>=oo!a;y_9)Fd<+H1To=BAIO1cp42>Gjwu3W z4K}NleGCkYn><|{Ln?071ljsCISSMY9O#&yptpikP-D*#2^BH1q!qeb4NU5U{<{BT zTI{BsnWMW#p&?4bBua+$(?p4nQW-W!ipwGs_$PSAm3=BpD|>d;MC! z4?i1xrL0X{W2bV8eiD7u*O)2aee#s1-t9A`DjOZwZ;NkGd#v*+@XM(`9xOY~$^^aP z?7i?xtfOdY_ETHC&na?0PHk=~OD^4$V=NM{6(+zDcj&QY>CIgU-?Sfyc+P!i6;qwj z{cc}JPD^%>wV!^AOXTv9FYO=vJQg>!Y`Z0UP;={zCi&+VSWn*yW!ck|T(0A$!k@3W zYTaVT=dug6+!9uBtUH*TWR|%^aLYY+EeXq8C+x&~X13pw`{(iNfqU2I`x<&X)Rrbq z{r#}D^3mCQ&U|MIP1(#Qyjywis*1oKp?#VkcLgv@L_J(^ao*C@zAg5QYz$p$Q-2+h Sy2$}56+K=3T-G@yGywn(y6cJn diff --git a/toxygen/smileys/default/2601.png b/toxygen/smileys/default/2601.png index 14ee8fd38b463bf5a8ad5a42f1c171bdbc19dc26..d1f979d328302fad3bd4147581356798c43dd060 100644 GIT binary patch literal 1418 zcmZ{kdot4QHZpDwl?Fr<%ctiY((+yE2)POLCjsc3Xo=!^x5?Qm5l^ zl%Wh#D1{-Dt>hNg%rx$|8DlOkGnD<=&e_xcvG4i3?{l8#{XXyeyzhC>OY!zPp{%H@ z2mny_aCh~Atm<}$?SRIEW-7jrk-LcT!~jtDyV53pC*;FJ-F-X(NHqq4d<_686e7<7 za0Lm#LI?m3#Q=--@6$N!tGAq&-RzbwW)pp+v2Ccb_I1VM)~&5ADEJL% z>U$vBHzg#7sBi82m)e&15H-(xAgZ6ffp}C;hk(}np6iQeWIlMTATk&&6r0m8QnmHf2yhUi;_=mXBak9Eqa5V-Z>N^=`s#KJ{Ha#_RmJ`eHr>ch_qs1#5|N}sN>x`hs48*3-Jo9UqH)lKxnS_z z4yzWQrlF3WLtlD-(vpKuPpKf-6NHgzW#n8v0xfeUIj%#r+btt2HciFR4$W|^(5_DQ zHgRL9*P{zEjf4U8-x#*i$d3C$p5PDBug4CMzVzSgS`831ZU{{BZcWThWZ{e)=6CbA z)TKKsWV5e5*xVHfz0SKHt{9y5KxhaYrd>wPEXXO?;f_LDSj35zh09e{QQ?sSl!XNn zg;EM%oLiXRr>(1_WQju2Xf#+vaq-=|C8ec7upAolW;W&A*%B8rrSX zOg1JP^H<;7(JqZ$KXF-1qBh~E(@HC;t!=D(_)IQ_eH@O2$!54X)AwrcExbb?M`H^M z)0LHxv~rmaSrBZhPiZjTtb?jVkg$Oy0-i*42#qH~23R63EzOX&W)@aB3#0?e+QAxW zfVFcVV+avpDgU3q(F@Xq5_GnA@Qn#0CE??V07HnmK!kfl<1Z3@hTgU9Fp-j5IDPA literal 1373 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYACkt0MQwtYE0~aNz-V}1eOdseNeNf^+N-QuTUXcNZbFLbMM?advrz$b5nY5z(=C(E?X4R?eN9cBZEtH}-7N<_tbIFV43q{%3Q&TYp-IaZ>vZWxd5^KU6j+C}y=j*!F9-{M4|GcVE7~ z#l3gK>f7o24{n{@^)}78uw+;5jy(REB|K#o9L;T_^PHC6JC?ojkH^;Nr~^x<)HT07 zn!_|vpvlZ8xq~G%=u>GN#r ztM$b<`Eea&P;Y*zykcUz)8*WSVYv-k-0w_I+8?#nK*|5-&-l0l>W?O9yy`Yt;G1}8 me)G#||GuZsy80lvfssM3?DJ|8Va=2WS6J4gb4;)>~tZ|2{VV{p`Nz$^Y)jxmqv8 z`B9$dlL8OWy+A90K6riTumX@JT@vI63KS49s$O^L`Iq_w`|}I-@3(i@zh5EYz|zCU zjX){JByV>Yh7ML)4T{*l=8=0O&2EV$m=$)GN9UGv%Ca)us9*8*f#T@^ znP(@Tyk*sQkuW?H{zqNzqbz4w< zf!~3@v1*67C*?{X%6t%_#IQu%-{MYOsI{f|b*b7iPma5l|2Kbr;o@Gf;pM|~s*e+e zowS$zKXE!X;Xhy2flIRQPCqra?{>TT%+RD@L)H#~^%u7Vs?81Ey!p6V#kD!hUZ`zZ zJfnV{FX5McibeOkbsBxg9$ewr zcUwz3wy&khdDZfXPgWgy8NhmuYrEAeu_zwJ^T!n!>s$Of*IIhA+3ZP>)s6IDsLi_5OadIlXf~ zpU^J*%ynsxLu{7F&u6-+8DcKKMC$6vf{GgN7vh|~U7K}^JMRFK=Uh)0#}J9jQ_mmg zYjO~2efYZJ(%X6y$Er}a4N6VF{^dXQ;OH!TI(_r6a`p%H%56_898GuvA6QOfoN#7s zs`a|(JNt5TeL4Rq%vr=CC^)ONk-u=#x$@uV@+PVo25{&a&k=X++bp_>A!Cakf93Ol zQ(x3Ijtko*Z#o?)IeEjrur1QrpDR^237_J t3s(L%N70a*pOTqYiCe?PFV5XS4U!-mg7YV8%W^R=c)I$ztaD0e0s!q+Ek6JN literal 1462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL0ZI2jmNm>Zaz zTe`Wpx|x~5^m^tMmn7yTr^4*b1lkMH>*R)4ua$FAYGO%#QAmD%4lD%(WaO9R7iZ)b zC^!e3DQJXe=B4D97i)r|2jW|o)S}F?)D*X({9FZa_*!LRvELBnN(&Qb7Zachfc|y_ zI^V_7)!fX{*wozB+))XtH-(%q(+4_6ACx$d5(`WSm;ymec+v-Q;F%{i513<$fLTNN z@S5ce42;h_T^vIyZY>Gk?;ji}@^9UH@ugRyyu5w0nK(B#8!8Gl9u;BpNl@Se0yRE8 zz9f#mw=4Kw#V_dG+^u*~f=y1*Z;8$ZhYkaq1$)f?GX7P}y>ES|+A*=Ag=aDsDCLix7nz% z#e=2KzG2m>r2%tiE;_8oAG#wy&iKrsYvDDQO@v-Y{-6B2^y&=ph(P76n|?N4iuux~ z+IepryuqDzTmI0*3p+pGnyItG+D7zTXeQ#yeFOL$XS#@sj z&ih|-)hVvYpPI}Pc7>C3gZx1Sw#)JxOMVwB6~14$@_03)tL^_&bJbpD&yI+Qky4E{ zfB(E_`F=y84@*ntv!82D6q)bOuyj$(`D5>%2(YvzPqq+TkaFPF*CPkcuDDsbw>$o) z{gQ{fPP~6MA@07M?CtyJs^R;)rd(XH(DG9Mr^AMwuYVYoC+5`56E*exm+8f3xGLz( z@`mo`L25Um+4=IGX8Razh_GJssXi;hZ1u?v?Z1T`pGRz+Ui5j%vi7aN=Jc~jFf`Y^ V{pFT=%@kCUdb;|#taD0e0svH67>57= diff --git a/toxygen/smileys/default/2611.png b/toxygen/smileys/default/2611.png index 21f462ca0a9cfcf52ba3a6aa214dcc87d6c7839d..c4b49d6590263e5be645600281acfdc2c56ef329 100644 GIT binary patch delta 1121 zcmV-n1fKi43XTbo8Gi%-006c6H|hWY00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33MCA(Pbn7qTlcH;_rt&m)*wUU#NT#sSWBD<ikO+7Qj;_$nO+UU zm32cpJ;qP=fS2rpaR7PImDh`j;NB(x7=;G(VMU@MEq@?z{&yCj0I48^_&O@JqC)U^ z&ij&3upTHZSO;@8pv!&WlK5>S3Z!}H}V@8SsZ}V{~;q7 zySqRfNpNx&jn*?B9$#%u7W3}_Jx3$d{f8`F9ddP@m-lFCvo zg0YDaB)V@iFbu=)rk?*N>#5CGQtOcpYu!KN6T6XD*q>%Xrb{mThPHbh6bdc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Yjh$T$On_QkEes7^jV#Pu9i0u` zoXuPuUEB^NLFn^O93x_GSX@h3d7$tJli8C^fMpzbGU>KL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDj~FY+`QU=xFHd3UmR` z-xdZgZjL}7n*mvtmafK1P`xSSgqc3jG5Vmyfs|NaLckOVV#1R?kOR*=sd>O0Qv}Q! zU3{T!mtW@Gmg}6|!x^GB`Cz_E)>f%!pDkZ~-PLn;)*|K1NU=#O6EmK9 zD9<~ama+Y|M6X*B)8=)XZm#Kwx!(Hzw~#Me)8m&VU0zF@G`a*>7>jnwtWaLcBK4)p zc1x6Q&++6br&Cu>zxdgvQ>3`J`t#2}*M%%L#^?#QDRQ+ku{AT!I=jrJ&(eOT&jX9T z8L`tdzW%Pgr`>m4`P*;Xw!?|HG?KR8PTUx=AVR0>An%$joh9qSRu@gO=qt34Q7Jm} z^Y$4{-_0FCD^*I@hH01Vj?FQX&U|D(Gx)}vC#F8-*I%1T^&aa_GBhZ!<&~Shhh694 wWB&h48>cQ`^KsSAr?0CT-p|ti^G_s!VT(h0iQye)R#0i?>FVdQ&MBb@0QdXYh5!Hn diff --git a/toxygen/smileys/default/2614.png b/toxygen/smileys/default/2614.png index 154540cc4e1aff7e3606ecca4ae56f86a6843b0f..2dad11e752c9c53108cff11de219b5e0fb429f5d 100644 GIT binary patch delta 1540 zcmZ`%YdF+t6o1EM2)V3ssi$pZq*fT0mgF`g8@|0IX-sCFSdEx z0)U7idXQX3q-(Ggc)Td|5EOa}*j@s*r=Wy5dx1Dx?klYD6P5Z1iwQ4(@e^&?h6dCQ zKxv2I1E78o*Y!8=V(p$xFrWPHN$d@D8qp^AfQ&u>-Nk!7(4qFK26q9d6T~*cfbC3& z>3lZm7Y=O3+;adJdT$`X)Bz5b0f22(iv2QfqgzrXp2$H(p2oFr@gOFC6Ckz6 zjrgdVD4t`jh|jFO&04MTg%-1{-rS9nOs6cDxQnVt^UqS2o2g657IW9h;wfl(inTnQ zzS0=9m~1}RLi;!ct<;j=!sWvMY+1CPX!;p6pvdjFO)9>fO&XNyk_uC7kfKD_6A ztA0;Ed@Xdt(uc%E9&gZ6A%2RWp+THsAR()fcbdtc66o)(%itFfRQLVPh&35wK1>uC zcuyAvk%Fp{f$}aE^9lemX^Er&hEHfbihk@!M09L8ijhDMN1cqL17Mw7pvg0J3RTTGkJW>np@2}+BC6uQva5Ya{%nz!KGcc??Zrp~;UZ}mv z{k5Jg7M=;gIAwWxc1J5?0HW@(SDMn^LO?Z$!NNuOb z6`ve7M;aj0GrX!=M}Ev}+0LOh6GGm}e2pi{{yZ({^stu(<^-1SAMNfwWLfDQ(B#2> z81ctVo?bu`-?M%@r>d>gUo+=0uA`>t293HaaG>bcxlNoWEspne{2I)j_8F1tY!L!b8FMue+8`u#}^GYQlxN?NLspd9E@z8x3-z;T~~g_eFzJ zLQ%(;gFA4Ms_NG*rY|Kr(ffM~PwT(c9KR&L1`tkJ^)ry4f5{y#S67-H?eLVme25$T z=54|S_oQI->(#5RwYlWQ{M2M62H|;|6tyv;`n3KmxiA&+xbR#tC!|scFKG|n8B5X` zrBQ&4t1A+jijv=sEjfD@+l^HpKo+QLW#m_3G89YXLcPt7t~12%jEvSb3`C-ekJFDb zkAD_J49)=}iAwY*Egt(QS^Tu5Mpsht36G%o_^O>yTDM*~?@Z3Q^N)x$%6Lw0HUa11 z=7slkA0Bu@r92wCKXmWmpy`fXKbh<_9u1|8jg4AiEX_@<%ywHeH{PaFXpwU-gfB%q z=LDiZg_KAIoAQDJHO2c<9WE;h5wkglgdiW;n{*8T)olIrz3-5%a6bkNmjFgsC?nj4 zb}SrLz#L<4Zj7-qHnSv|VQegR+w8V9!eDGLm~hp;|0cv835$+M`2U0rzA5qrdWK2> N;9b0&8=NTVe*yIuw3q+@ literal 1592 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 Rpg$Y{U#~#VMi0Wte*l_o`m+E4 literal 1607 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 literal 1466 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYCo z7DkR{Ze}LNFuk66#U+V($*C}VGlBL(^g6lW)obNkl$uzQUlfv`p94z)0U7xv`NbLe z1q#l=W(peNnRzMs<;9wy=z;jwCABECEH%ZgC_h&L9KKeWSnRiSH8V9cbu}?DHZ^cH zGBk8_Gj()xGck9xa4~T;HU_#KqBn({Fw+M*MjwJd40Mo zv3={89EF1$bjppKFRnX(zxGo-*CLHKorQm;R`0J9)112LveY6286O8;Rj+s*XcVfl8!8!iGS($9tFe01>8I<-Ni zF)`xJwEK#|J9^ySas)nDxa+(0!uQ|8AIQx55N@xOu`}W1r*HeGcFIgz{CGvUkIYM} zS=&r!vX`9;Rag~iuf`E@OkZ)Loa^GOhYwDxv+QFxU9T9vhcRx-y)PjTO#QblUpH-w z#l$WC54L)QE`8iMA!uU2}>lM*cx7&)Iv9H@(wBu*;$sfNY zOP$-ZqgP1&=8$23cFg?G&wblmkJ-K5-5K)Xz@3QteCgl(k<(_EzUDjJ{Yh=-exDEA gQc3gI{Z~K0V6XAy2+#Iezd)s_r>mdKI;Vst0B~R!{{R30 diff --git a/toxygen/smileys/default/263A.png b/toxygen/smileys/default/263A.png index 8a3409d83e44fb5764fce2d7a731c64e2e771b54..5db95a5e98fab6c37ae7e1d233773416a2dbd309 100644 GIT binary patch literal 1852 zcmZ`)dpOkT7k}Myi`H61ZDuRNtQxmmrd^q(qSClbghZ~%y-88O&29)uE@33oEFl?X zD5OaUk6XhqNe(&>n-*e9Ac|Xs2pYxpeIdNp`<1$hzQV1a# z%M<3dnEhsJONd~nhV~9S%!vA!oi;;=bzgeJT?~tH9w%&1BNU;IP-GNBYZw(dh0s+3 zLNjg%na~hY3Vu{&a|9t_4e}{_i_9|!5)i~9h(QpKAR57ge|?O(i{RcCiQRcf-U8;( z&!V@t2-UwHqEk>aBLG7a-LHx6TbCX1KKpV@=7rWwS3w4DDPs@N5M&@o{}(9;Fx*F5 zB*r8oV0;gc_dptgLVYMFLb(YP>TQL&I4Jl5@+6=DZ2@y~aai7>a|l?!z)&X4vEeft zhG-ivObgDUR=P(|afqPj4_IV_fDMap7kP}$B&x?pNxb8};$hOx-YfQ914&UAiV z&eHVY=EnNcWFPkO#mRmEXfLM1Oh$VVb!ld3VWM|^V}0}MSFCRD9|yvCL>)bFV{>zT z_3u$WcTOM}<#)E``9r@03^+DF@gE=R9vSGUXQgvFjlIJo4XlhWRff=n1h0)6Sec#e zP23L+uW7-83XA&3K9#hfhKN05!+C4$EN(4{xm5=!?JiXbAohuC6GFqGk;l>og27$(7NJS^nH!ZVmj z2kt2-)r5Lo;K)Na0v>{T!t|O;I#v`zs;%`_-)$|e#E&aIrnS{S53j5Y+S?5DZLF=V ztgo+r`N+GU^y5a_wm0l;4>mVxlK75JRVSzZ+^0bBc zgt)BKexf{ydID?9xCnbks;&DqTySWR_a%QX95p=H3wJ#r7$Kv3X%y!aK5qNQntjfL1KRHS~6XY78Q=U~QP1$F6 zWwu;^>z|kl%QEj>P-o?Gv~Cw|e^F;Nz*M?aZE{d+T`MczJ%;4%vUjX`7p3~fcz4!l z&OKBX?Z+M%JUp3{b~Mu9qTc6`t4i#|0<|E6-Zu?0;e(84r(=3Z?)*Rb7xQF5=!TZO zNLn^=S@EpmJWZ*}u1Txslze;qfsjM`q&B>)30->0wCOrX#YpHzz@B_$Mu&3aM`Wn+TJ zG%NBVOQvy$nf-A;HSU#2|BZaB)L5wE8coXeXt_*fmzy2ABGR>uY(%#qYWOJKJ3^GR zeg5)R@ey6_=x$CMdGgWMq!2T${I4VbZDHLazIvaI=NGhyn--b#)?D?;!5QS*=gH-A z>eG8O`_|Qb)0z1ceUZ0r>JvA+94EDo{i0@8CnM5~uXjpvp_OQlEc!Q#Eh!Lig}BF- z48_dBsQMf~5rruYm$;igaTW9_?NXH*8Ow&_BPmVENxL;KnLYZIG#~Uyq(t?%*1wa4 zRQie~63s__jZEh_xsRMk7O4-zW3Nzhi4wWgta%%0WvisMqJ(dA*uLG&eIhEZC5chY zJ4amSUWh&r5#daSQgfJsWTA=AWY!M95|XME9T(smm3Y9m_M2DBzRo9gLtc)U-hStE ze$}-O=V@3ah0^B z9wGMn^@#9@^_8`?RdLa6Vs~|8qoZPCB4VQN95)wMwi(<0Emuc}#SIY%B;$@AOWA+h zKGe#}%Fij{n73k5vi1jDYHC(S)&;VoqrFRtgOhqI&cW5@9Ob;7v$LH8<=il?ozcg`}n-PCI)Y?Yv7})``D9ayZq&4DqipDQ|qjsJtmlaS0*@FrvG{POiCoT z|M%#-iigfs`S&d;NGmxiz)0UH0To&Hi6X0AvJ2PEI z&@0g`D^cF5qMSujdB|cB%7oF;6^hZ6_RT=-AFEL<9I2k}R4)^cP%q3NU4pKzHo;I^ zN8etDV4`PWqHl;N5KIUJ+Nt5H{}BWPd0z4k|Nnw%IiGEqKzS>JU642RntP}hGV=_& g=!LTkaQE@D^>X(N4{7$oMjAqv7S`s^Np8RY1AG;cR{#J2 literal 1779 zcmbVNc~BE~6pop85S}!BUG!;NY+aYp#N;Z&xZ33Oe9ADHj~lJ;x-xh-Y?G29orBHyf@*J zW#EUPqBY@woVJjF*jp^rix3!)NWEdPj}(Sy0f-1jAy@<>Bu0FceLg;gg@YuB9QDD1R4NG!D^aOJf(9z37>X%WV$2uq z!>Y|JN0{|wA1}r8_F^TU#o}@cNpQ49OVh?a1%xNk9L*-u3?P>S)1xS}fwrBXr}&`W#_dW5gF5XDfj(4hB0g#;==gcvEo;t?IjO9}>i@rM7C zHi!=ha_Hkfdg6?zJ^RQBt-8pHnlf9sS#=YIA2aK7YHB=I?s&aQvzaXcKfGf_x^!v5);SW#u z>t8r1=U(`jFBSxi+Gp3_%+B-8wl7I(yT9o7r-QC;RiR&3yr}M6jyBFawQO_8DM{!{ zU}ov=SLH3QDsN9)cYZiH)V(C7smIn8kgpwm_jTs3_V^Js<*hywmyUZdr*hl%i%&Q- zlWK1*+LsU;_ggo3%X+R;Lp2nfi8FB-&5ZHQJdcqZVIP^?$OLb4d7QbrHDjgDQ`5D0 zby9bjSJ}?Hj4`(b-P0(&E^E6Gz2u3yFgqowF1dcu-_DIE)XR#YEXe)&K^D6Bvc67{ zp(>^mHeA@4A9~>`b>r=hDW17CBcA3f<{t@oTNEF2sov?48A$$N|GQ>UhiABW5*wFe zf7D!*#fFw;n*6}6vc}+fznG#(qdZ*4k{%rf6-~*eQ zZ4dD8N(S_^pKY2Y?9P?8igvpUJUDlPaZyF}sP57o4_2(4KQ7bv@KJH#Wh85jDfi3Q zXOCY)dUQ*N+yNe58}`jpkJ>i*c4A#qS4`ot{G{WXJSIfmjQq7~|3PrrS}-oLAT4|1 z`gzZ6bao6IyX%O{^Haga=Qfw;dJU_K(GZy1mO-g=xrK+B5OI;qHLO(r)8LLVQ*USv z7WsewZedAr(DhO~u1PD};^A_4ab?50N7qL$n5&z+Dn4&R^;maz#1t~LE{FQ1;N6s} zu&?romt(th*05FM_S9lkW$v?y)MK*aRazJ5Bpz0tHu-thnFnm!_NY#_mCbT{^_TU{ z*_*{R3feH)PwC4%o60>*Ex#OD+#x(wXS+Y~ q$$hV*-A)hdM;`D!*1xJ}Oq^hE!t9|M*FRkyzah0UR8bNXx9T6=Yr`x6 diff --git a/toxygen/smileys/default/2648.png b/toxygen/smileys/default/2648.png index 36ca321ff27549ba0d10507be5aa2bed66b52e81..9d529e596bee03254f007e908bcf4057957e54b1 100644 GIT binary patch delta 1353 zcmZ{idsxzE6vtmw(!8Y3QfD_RGnT^;HO(y4V2Y(f({g6!4GXh^gaoW`)6!g;;=HtI zU7VF^T8hxZHgR}cgd%1)MP&sFMY=LIMfTg{KieOBpZ9srbH3+2=X}n2-nZr-%!@u} z8v_84DC^yjF*z6z8UVn(3l?(;aL7j_1xJJez;Xc~D+hp2kd!qF038j0CLRFa3IH4^ z+y~))P+*R@D-!?g+kvO-7kww6^)marPskr1@98`$5vO*ENNo=jS|7yy%8wB??zw+& zH@}|9gC0Yd4JAPSf&{4xfrTm>!I=J&e(b&mDmObmtLxCsPwNMY2M0KVx^^AZz%Vy2 zWA&+d>UZ7a6W!zLMzxID2X!5njVpc{l4Z*EQ*%8`c@IktaY!<{wQbso{)xe>3dMEB z_>&3E?;0^XwQJdliw$Rcu^VcyV{DS_Chv{R}U6*R#c zsn%dHXrx+bl;NhKA>Pmez2d3aNoW%ew1}{Ow)yWvP^nbv5cMldypj*;u*97~xX`Ra z2ohieU`iu|;!RbiOU&0VZY^AT4gjM}R%A3SA|V4oIesjKoJK;>m=qF%afAYZPeEx^ z0A~Pg*8D!-FTlv;uXMjM`p&3A=4mH#yLOUM^7wwLv+?z7u#2Y+b9k(BZxbi2_aUnx zEz#Pwj(h%2#mhvYr|Kbg)di{LOrjH;qm1AYdQ}8C|A~FCrsmIh+XM7XsnH`uliZ4` zM^eJ(`UaNQ3Z8v@*_Jlp5xy{|&x@u>q|B<)M@KD-Dk5I5q4!d$^o}4bG1%U`&2zGq8lTD&A69e zU8IGtyu8g?)L2N436Cwlz1AkUJwImH9fd?XMR}aaFO{8=v5Raa8~GM{PJjPu(Tu%g zNmvVUy1s(6gloL4CPnOA^aXG8+P|iW}N{M%i}T~B>?xn_5;!{nYzv-sPSfZ=MZYPdT3N=l`$qz@0aT7 z#Xb7abgxTnfpX2M*`=Lf*B;CuN^ePnbJc>Jc@VA0PadwL=Ru$K1_2ik=_uddj<;_` z19-~?;bHTDY*VYcw!wF7hd4RhFU6|`djG~{K4s!9CLqFg+HtUd~RUY4j zkljkY4o@{oI+Iu}ajc0v3~!eVH!BYDUo^`#o@@td zI-g%nEpw$el{F?+KG%H|-r*XXjxl^SROOfZ>%6zQ4ODeZR(BK(Yxj0$Q(ozpS!H5* zEt^)hY@vHY$m?nNXj*atjpUnjoCFzwL1Qo|v^UD#GtwRH>*3|=g>glrebMMeo{I78 h|709FmP}4z{(nYBb}+Vh%?5J-2>38uU10ooe*+)qksJU3 literal 1389 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y~xZe?{SE}9B99KVbN8IfQqlTDrK*p7C+B2>2!?1 z#Ge{xqJ@90>PygJ(D!F~%sA0J-^b;-()}!_TVd5ytTI6=G{}5KeG%4{Z=Z4rH9O@@hyC& zp#RH;S8?+?YPY=2%H27YWmWmsg}dwLC3sxain@?idZUf?TX*C8g3mX8 w9FAWQs5JM?{KsMclY9CV>Yn8Ue^5NYFzbZ)E9(#JpFjnmr>mdKI;Vst04#6xu>b%7 diff --git a/toxygen/smileys/default/2649.png b/toxygen/smileys/default/2649.png index 4e0687e0a5ff1cb5be09930d1ea1cbdc3a3ce5ff..d67cb8496998820f8d82ffce7012140030cd94a1 100644 GIT binary patch delta 1358 zcmZuxdo1zS|%2pFRy5BYilkY)}*799WuOl3_0kb(nXIt&29bpVW# ziW`0G0Z>C6J?u{uof{EC;=y#$E80;1S$=Qw%dUhM?WcIHQQW8D&l^LV*~gn|k3FgO zV?R1t2VaIa1PfsNk%HNe2mfT**w|Q?sTN-?4bjDLMj>N(b#hfcBOg2`6wpQTS-EUX zHgrJ*SK?<6DVLRN(`&`^9>4HgJw6YpWOe<{~nPPVtkaMu;M{h+Pc|H?L|Ow69DSpr}+m`eZ#*)CnYCF$HqmWskEdh zbZUGO0CpFz1UMItptU(O+i2=n=^Fiu+bNy_`LxXAxUu(X9A0#=Z5U?Ix4vaXIk{h< z_w3Gja*>ox-$>Xi;TsV2$UHIq8;zG&qBSbOSmbkYD^Xy^ir>6~&UG5J(Oltbhw`Gu*^fcKXvo9mH z?giIPxb5Pln40rAc}cDgBtx|aWuYQNA0D0&pL3;?K zt(fW+*{HY_TtF+zK6HkSLe6ZB*j-7Q4a!3Y?ySh{p#iC>wO*(b+xD^_(=S)uX}`Nh z+J&#k9_eqmR>p#$yisc(o3_?-@p@ zJFfq@_}rfxOf}=t5=?$aE6*pr-pe?IMW=f{(6!i6Yf$g1IcLW)J2D!vjlrBb!vroC z5zBTYO?yt53HF9}2}gXwvh2t%U|d58%xcGQBsRJ^rdis*l&zqeHOzc-ejetZ9G~yb zoIa>XXbVxy(XK%hC}a;6;s=jP9;ODqaX{4gS&gPMlkeMfzm;Rl9T8h|S+NeU;4c8& zA-OsGZ|8?}5DnGw28aPh)Aw3P?=h)VTC;I?p-rP9hk#Jk4riF2-TB7?M6pDz-KDo@ zmp|S^cXc-ZwS~6FIFHh&&-1@Yu_XufktuQ){ISm={^_F_fpKKj@2E;*QJuIX;gKV% zvW@vu$K^%0Dic5JTYiR~)tXgZX|BU8u5tBlW!oavex7?XNsm>poKra16c;4!+cHu* z$q>e8sI_HdCwe4HPOx1wDlw2s4yQ&DB9o)w0IYG=)>zy@EdGE$9!IdTB^S2;6Gh8Dq)@7bC>TrsIp_WGun4jX9Gg$V)KbYY#Z|1K9_gw*Qwt z&+~iv|9)+A78fKXW+VatkYwLOy3{c_@+53l-dohp5Yv~9^>*KvD8vye1eO}sA&nlpst>FXzAZJAtNLKbp zoLAvQ0gNcpZn06ZK&q#!A@E*@V_jH~*Ak_sOy{G$Iz$WWcs>%>3R+fN?7wboh?dL! zURLK~WwBB6sOM3g6(y^=d$gelQ1u2YkvR2G=mt{sH1e#V*hvede$g@;s7sZBX1vqO(MU-nrFG!T3;Byt6z=+LqcI0?gqa|61 zYhoFjB=X=&elczX`-YrcBaGt+L9KdVf=L^$MvNjR*leb9pfxV@e{!Z%ozX>#<3Gg` z-BK$sGF>lyb+O(&te|#`q_#$zb>9#G#Dwf5UhbRtu*^{uTbgQ_-`RL$>oafmmQba^ zzY6$QzGwptbJx`fm z#f9H?Onmxsn&u#*Y`xT-eP{E5o8CU3`(aW1mV!HboqudwXm1RT#Jx8rm)Da!uQ!p^ z)~>;k>G1Z+qJ8u4l4oqYD*euPUK;>P6QGN``d>U(g#|wV87u z)uonD_>(Wc3y$wkMTH-Znp}0$sptJ;0lhukU#NMgX&SBzSb`lV^=F42$J>uiH8kE{ zy0X93*th)W5Eg%Aq@lVrPn$k-Y`EY-2I1>ZTe{a0dSmQxiuZ%J1F_Tg-6wuYNr`zZ zJYEvNFc7?UE>S!cPifslKi)kSI6st8-`DHxnZ7xizP$VHuiv(S+uWbE&7Qd+zP0`I z$KQm!o{he8CMU@`JQf#pLJKsw4jZ7M%j! diff --git a/toxygen/smileys/default/264A.png b/toxygen/smileys/default/264A.png index 1e131e0abe06a65f37e3cfe5d07a2460633963da..92fa0e7b0c5944c72a32c36d3ddd7dccc983180e 100644 GIT binary patch literal 1321 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>&JTUv~Ly(S;LvXO3i@JeYcPZ{nd{ar?GK?cN-=V`K2P^#NOeiXrNOT!;uz5F!pS z#Tk>l-Cfk8IA&}Ga@b2eeO=j~vGZ}Ma%}BXk!N6F zs;vr%C<#g|S12gTPs_|nRVb+}NL8rFEnr}<=$#tsIO&c8&z{$!l}yuGSk8ZxDE1ER ztE`JMc>J|$&&jkH(*(sUlXe~a^82~|FTwqbT2m&!J33ic?c0Vor%vA3v@`VfQp5Lu zKUS2a-B_I&vB~?In&)hF(H~-~mmCd0eoSZIt9e%qlooK98G28jJ2@vVYeG6Y zDW~#_`}yR&N<`S_%RP*7v(0gu@Tfz+cBl56`>wtbyAId3)_hC9ny~hqb2GziH;#`o zn|Nn!S;d!n-nV1TW0SQC;Y(I<@N7z85wGwu=4cHlJ?LX-bVdAEbMJ#NgS8Srbtg+d ziB6D_WXd|RMxk58f<3E8RC$j(+sEL!H$~29yOc65Ym{l4GGU49<4@8>e;w+Welu7w zUjDDhY;t9;W8blZCj)D?`%1;GYiaRb#jh^8S!6r&qQ?`8PEOt)aa8e1m5fTmg_RpC z=cq8t#ToE>E}J|l{I@svi(vjm22c97E#9IK_jxD3qi2lbE}v!WA037BT3%ed@5 z?NH$nVA`DO>Eak7aXI#IJ71H5gzLlC_n6)-S`_v(b#X>$!QTJTo|DxC_D^R1bNNf9 z<8_AQz_gG9Gj_(T)lJLYW<7bMmc@Pf9IIzH+ijnlS_rL_UCtM-wDq;w1J5Pj_Ndi# zUfFlwNnxTD$1M)kp!P)ei7u}+UMcnYKX1{iJo!`VxGhKFZu?6RdP`(kYX@0Ff!6LFxNFS2{AOVGB&j` zHPSXPure@UyZ50PMMG|WN@iLmZVlzl9-!bP0l+XkK+7?|& literal 1341 zcmbVMZEO=|96#o^D7uX}6-APr2SEs~_tN&-Ue=A<^{#71yV8|xe9_&mPq)i@z3%Qt z+rbn;g2W00M7KqVpayhgQJH>Vm<tIclW&9 z@Avz^Jpcdg4+gv?%U3N&5TwLc=Lx}ah56iG4Br($-xT5SfWbE#jcU|LiW)%Jh}sHJ zpCYz_5D+8j&MTk_L2ir6;bx=RznPI##Uh#*OG-&VHiA^uq!Oak0SvSiw8?Qdc4K%1 zL*Ds3%20OjkOy_g2C}*GHFR#EvnXrlP;Ib)S#$Jh^W-laYIa1#`QIM1rN|AO->lH z8b?h^^@T*il;J5cfs+;jR}?d@Ikawsz`t%RiPppE1i(W; zSGzO`o=0>|o(yw$YeOawdc!nmGCUM9=24|C1;h=X$Bn@+mWUi-2&>B+#idx9bvX%wx0y_ya*=|-@>Z*jWqGI7<;Zh=aorH(63E+? zq1`#ILlADsWjGCphN^{CHI}b{V7qFldb^rHxkegY-z3K)YEs`|9?yKV9-ztFK}670 z1)a+;BQIg!ZnZfGmL)lU-UBD$5m*>8O*#pei??D6+{pjQ84jJn&Eoh^vE;X41)9^v z(ua%1<^gfoF&b=*0ZZpW1X-r|JZw1i#hJ#2`T}-!&*00sb$!v6qWiYIaC#;feE3A* z*q&;(uxA%jJn?;b;g$+)_ssZZu3~i4NqVfd#C~G-=Cm4X`?%!!i9Mf;O3|z5f5|yM z$V^QKFFe-&io|&z^`0ubK3x4q>EvX0&8L&^)#5w9$vpjFu5ZGZJzEk!x~l(G`n2#8 zz3sbwm??K)6%A+a-&Xq25S1B?A=&W{tFQbpP_{KYJ+*%A zGvB2%2u2lW(R)HXT6F2)Wao}UB;PvtqWkmXdpnIkh^Q zYv`TQHg32|-|Zbpy&nz+nr42Q-Jjc0KXR^gZSLZxkM>>5uE6dTpUPdV`|+#Z$3MHA x+xqv)C)lB;uge?K!+($`dwO+d`=x93yOtqI?~o&0mj2cJq51fLXRx+q_di*(+v5NL diff --git a/toxygen/smileys/default/264B.png b/toxygen/smileys/default/264B.png index b02cca61315f9d0c2d3aa48ab1999b329d32097d..07535932e2d419d3f83cf65dafebae0a269a22ab 100644 GIT binary patch delta 1493 zcmZ8gdoH=^%1ppBQU7i6T1_Qtx5dhpR0MsMzuufV6 zAmQ!e=#J;nMuw3;tRn@&L~v9KVj%o_tMVJ&iK8F z0V;3g#^^kIUc9n7Q#A`Uo^PIC>{{%j4UPXfAzBpmCk?F(FHe+BLIZ7WY;{xmdJ_7@ zYvPwNJqy$q3($-f;fyn@%8E1Uf1{Fd+ze#4A-byK*oF_1e1 zN#WN`&$Z08MRg3_=B>}IL&|2kvkjzX@!BRQm;7Ibo0dXiM83aE%CNkC>D$e`U?6g zJ^!_fS@j#_Jy0jRI{))lUoOJ*^fcd|KPitNXF|BdS%+gsQB4x($awsUu3-zT2te*7>)@O%~ zsVp*IFDOT=z=`(Piw~Q89GG^6RovP z>FXRGt?qc{2V!GQ^=>M#Gq^5F(ecez`|9kriJXh|Ll-y`m;cBGe&8wP6g(;B8l7`a zHohDWf4ryk(u5^g&oWl>MKaAw+}PjXVcF^jVNA~fXAalwSn+A8KWb;r$YeQ_56KiC z81u2S3bhU9D*Z%$<}m+`;N-<>O1zRVS9#9;$xEkXP-q8V(xis%C-pwU;o=Q!0}!<# z`1AO*G&WMst{+wqVrSFyfc2hWWXv)YDPogOas0V$5su;65j4EXE^H%PlKNir!CqR1 zkT)gkoGjzT0a#woqafnGww`Qd{Sa-ROGnDJ+R#J`$-W(JNmJGj_Yd#ek3RP4EjEN~ zp{%U%(CSVA)w1+3!dkL0-{Eddx_78;d8-`^UX;@1kJbxxSancV%p-_R+2*l|h6D5xz5Qu~RUn3A| z(r#`h(Qa}MZb&)h4+q>{3`*)GFuYv-$Ux*)ZUPNzSZxy7rYV+^heg~F8PlF58`?}4fnVO;oC zd1PB>$IJGv7ly!?vNuIGhlVjsP7PLqrT!|A09EHL_Df`p-h@6Nij4On2l$amIR7XT z6o4_t*jOK9u5V=OZiK;Eo0#EDvEN}ZI1I+>hkk|sFv21NLINrOV|+n45g5wI(MHPx NK)^d6Ew?2m{S7{3-jM(R literal 1525 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YUGAe*UGslHL)bWC?r2W2bKZ?GV)9Ei!<^I z6r6+26g0v!^HTE5i#0*f1M#g(YEfocYKmJ?ey##Ie62FE*l%iVVs7B*Xz1(;ae;+_ zi<=|R$7Y5W29}nt#!67VDddEiKF~4xpu~ZcSYSfH6bNF%lRl6G&pfGlz#LNq%oJQ}m~YIrlPb7$_kr80L}=9bAbcRt?hd;Dcd>W2gU z*5Bt<$CaNGoFYB>hrD7>au3tt%;rP0WL?@XRp-3TG&b0O{%w`(?Pc>eGMr#n&5@ld zct?yyk?G8=*0x{VSHCcYojTj=rw}xITNWqB3;WC6?-Dt$EIDDXxxntKLx<(9P4P~T zlH|3Lxo7+j_`1I__x6J${l{*2eSEvD>Cb1TN9x-a|Ge!|yN&T}sNku$o+Z3xPIf8> zw>}iey7}Nld+Hgx(+eYw7u2tBY~hHJJsx2)Vee|CNiV+4?Ov9;c3G0NnG*BtfYaAD zK2G63_0lNjZhYeHH_lhfRie&ti+}xS67+nMfE7!KU6{dNr({7Hfk1~(A@)hife~y+ zuPBS|+VsS7!`ZMYJAG28_HKGzBE%SW?)mG-H*7REDsa~9n)Xp{V)}ub#fc14zIN;` z@09I)_`I3Za`657MVTMGX>&7!OnfWGXdY(p4e$aWqdgG2dF|qV%6Lx<78vktJlWm`tZavts j-OxuzH0GInJ&OcG-W`+Hy>A;q1v`VMtDnm{r-UW|lt45; diff --git a/toxygen/smileys/default/264C.png b/toxygen/smileys/default/264C.png index 6354aa459752f41af678dfa4b9e53efa62d31595..7a4428602dbfa61ef27d7b8cf897baf750409ac3 100644 GIT binary patch delta 1411 zcmZ`%doxYQMaUi>y*a)*y(+DfLL7=k zg&=EGz#fIjl&gx@X+!v=1ADdENU zO53FijGhI#U{O#cgfuEYD0%UJ_NDia6batF{u`34TvKqVUEElnd~C6w&4(OFJ0-I% zvoEi<&o<9Uc@pKivL(C~vegv%Qr0^!>wl}-P_;z2iMe9P=yc8W=);fpAXi&kTLyjm z5}+L#(fxcruKVA^_?$^|b8})(@yvFjs0M=6!o1vFFKCQV8leJ4CwUWLb1;3K{a>`@ z8Qugy{kznFGju;nk_nY|Eix)5!i1hejW9`$r2>G-Eea$QPN21$ma}mLEe(D=t~kLf zFfSz?5!ESMO}^b%7j3@nKFh5neM?DX;6=gJ`7!+F6&fF^^J=gZtTz6sAus!pf z!}pfqR(kizlujbY-VsV&8}}#~nf`#sR(HG;IE*SktQS!hd|qyTw!r7T{I5w$hk`9+ zHF@NU*_rY;2Y?yv+r?d9%CA>5cWm^ZHqr3aG!xE03LxGHmGj44EYBnvI^O@~hI9N* z%@M7NH(^Mo0_L4*WH^rCTQS&HM0HEsAWV$yh|jAI2lrC4j!V)MClPs1;-T;8JW-#M zCd?ejZf6Y5qMk7<94J!#_p?i9O9L}rU~Tn`V-P5Ae0A(VSzrQv4X4qMjT_XV5wo*T znCJ#kSv>m;U;7G_&??C7J#mZRhPD<4Jy!1S)?2c9deFZ*HD;Pouknh7xXQX)l(oC> z5#?e2Ic`PIo)RsZeVMD*IYSi3Q~;mZ^|X{F-O5)(Ox`2pb#`f5K_wEV-?zSG_jLY&cLrJ0xY# zpQlwDD_)d|R2)4{{Sm!E@vY0qX}k@+VHclC4P#ssvW|o-;ffkO_?Uv42vk+Dz{wsq z5L+KY=JdR-3l%PDwp$ionF!4(vK}6DzO)KUg=56Iyhz#2WwFu5No~{K8cB)hg_m_x zzTeu^OrsV4c+_uj&+^Ss4C43hpMusYXGdbDLV6$13bKzg-{JAY7w31H$-cMXu{#}) z#mTQwb#t=N9(PVwIYDOj(}`#3;S_oV_7W`uO27_nXJ>!Ya2Sm};(&Fqvqqz_ mXtdVUV#a?OVy}fqMW+0JLsEtZzQEK%2LKY$$Fc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YNUr!*UGslHL)bWC?r2W2bKZ?GV)9Ei!<^I z6r6+26g0v!^HTE5i#0*f1M#g(YEfocYKmJ?ey##Ie62FE*l%iVVs7B*Xz1(;ae;+_ zi<=|R$7Y5W29}nt#!67VDddEiKF~4xpu~ZcSYSfH6bNF%lRl6G&pfGlz#LNq%o@DK zJ7+O4Fz)koaSW-rwPf;XeP&0Qdu!{={s_gx;LGh(_+-i?4tMAU76}eXDuJ_C*ljk1){(Z*%{B{>{(?97R za|->sf>$(zT;wZi+-UkQcb&n83*5UG_-GcJs0leHT;X5pzwz37@%1~b>zo=z-}osQ zpOSGl;Wa;WIfys+t5^%eT4To_?XL>!7yVzuxKzY%1+!LCv}ekE-jovNjb~Y|IBxY( zH=b;j<`CoX&bI&3&X1oMSGpa_-#f?r?EBW!+Ka2V376ir)6Vt3&|7k4!?`Jb`kOj= z-c-HjQu`jUsCJd;%xdQ6+|w&;YFqa2|M*+SLd@Zh0_W~Hg^PPSg?JZM-FbI(LiKNl zM+U5_OCo+}t>@P{{-I#);dSCCmi_hH1C9{v6_2XIQythJp$8sgB z{tKoT+g3$2tvhMbFbC6<>i_ z?=@F9`s8M&#%r9Lrg!>MkNi=?O+szk);9jz#ddS ePNm^bkpzZb0bQlFe|^0`C8VdTpUXO@geCy3So_xi diff --git a/toxygen/smileys/default/264D.png b/toxygen/smileys/default/264D.png index 19cd5dcb152dc4c1925d1b9956eb7a261ed5ac7b..f45f5f0e44ee4d15a76066030305acb7c6b7198c 100644 GIT binary patch delta 1312 zcmcb_b((8}WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!Deg;~3uJ+>Z zFTd~1x%2Pef1vf3x~`t7KL6{}FQ7YK9)0;_#}lAy9;|=x_R_ok^{GdI?)q@!{qBVQ zK-cX~+W&a>9W&u`}s8sV~*$6 zKc4BqcBGm8(3j7@`G1M+U(}j1`Q6dUx@zAxyg7C9#-^R2x0f2e|NF6`B<;lNOpi_8 z*VH^`tBc+cTfO9H`0;}}`(Dkvy5Z154nD(r@9A?V=ftIaO|r<)J&^NIrgQ|60Y{>VUn;-0$>-#Ub=<~*3vy=_j1@{V`TDl^Lk^E%W|DmjQg;l8P)W%+GF z?u1VljLX-2zTa`%AiDh_m+m*$f>gjt0(8IKq92$>~LXKa=8u5Dk`SS)Wh?mhS2I#F!HAI_9{T zcG_N7;<(lpRDZ;V;d}W^4t<5&QkOJ4G+mh%h-=Gi3%}}j?x?Bz{8)~K*6-`fM$3h+!}TAErr+2@%s^ zbRg-G%!&v_!Lz$~H2Nh(jb13*30g8=cIa!HlHtky=o5QUt@8fHiAOnS{NtBgaPh9> z(Th7QEGNC+=DGN^hey3cP_4wt8%t;KNp5-kX`0Pzv8WiQb;n(qIOcYoeXJ(=;X_mH zmK3YfPg(T~8}wvc%=#3&giibXIKmv{Z|F2n95KzUo%Swp&L$Oe-BT+e=nPuD4%sh5eX>tx@?)cpNXAByFv~D{0@e_i2I6T>WEi3+iL@%VYbM{uZ1%GU2Rk z?$4Y*tm6C{Vk}zeNkDh1mbgZgq$HN4S|t~y0x1R~10y3{19M$NlMq7#D`QhD6EkfC s11kdqJL@ZaC>nC}Q!>*kv1s6i>hMp?*)&m6or{6N)78&qol`;+09^KNH~;_u literal 1362 zcmbVMZA{!`9Pc)Q$iQIcqQeAgx9C*vdhOBPUF9}PuQx8+0fUPWiG*@}4m#)yZP^_R zS#|@RGr=rlnQoYD#=t~f_7cGaH==QnCDUxW7@it`Xap(P8qTM(pan4o63D<5(3sq$7);EIE$}jMZf)Zj zx?o~9da*@ORlzcpQ+0q+cBd`uAPAJE?L%kC72YU$)|=}WT2PS+iU6s*= z_%hN;{+;Pq8GoUmnj>=+%k#^@-jNkJm6zPvBXD*+3l;@r^enTEhCq;%R{ct;Si>SMlDL;y~eBYjy>?+Cygs z-buK|d6SC&F_Sixv#!7I_L;e>pMEp8zjwW>aqo_zh-?3MwNGc~JaMgcee(tH$k6qV z=Q6J!OusO|{+Tr}Ik>tmZ%TiS6}NQ$7P$7;!|KQ2di*D}cxY|q&r-+k=gq?7Ojg~_ zJ!5;c!9$&2WmI1b@14FnH`({a&6GnovTnYSGxy;I_v@u6+AGiAe_UqbpB;aR*Jo#L zySiJSJeI>xyWh?0Z%*l0H#_ovSAXboQB_ylzDtSPV)+W&eO^3Rx+)c^es1RXBlN_q P^_L6qCBD-|wef!ddOPew diff --git a/toxygen/smileys/default/264E.png b/toxygen/smileys/default/264E.png index e000b39c9e48a8a4a7a81ac0748ba9c73bc20557..80fa2eb527198722551d6ca417cc9564776fcad6 100644 GIT binary patch delta 1264 zcmdnXHHT}0WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D{qbzyGoU;E|NoD0NiWmby*JDq`nsirC zT%n*SKP@vSRiUJ^AXT9vw}64cqIYVjY_iyRxddke*BouzE|_E8YnH`Ff;U?K6i3XT*}v^8yUI>avqAj-*)qz zP__ui3AeTGa+}Vz9u-r0#Sj!#jB+!8nI)rc;6^1a~nx=)1Ax=wJ0ac+}K=ek{kr-7n(V z`~6Bp*yqbVjB&HgamqLMmNm-oYF?ZqcI2t~lV6QBOmXK9e5pUd z(EIM3fcxVIPc-)3R_oOZcRHkcwS96=WY=xQ<}#Ukla}Xg>~cQw)xv{eq437aIV#L@ zaR&XF*VX>@ZLos%(MxC9HQTKg%gnnk^o#kbR@ba@eV$do z^f}4X#W6(Ua_lj8u_gx*my1T}j7M2KgeG245E8xnf7c(|4-ED94z6bU$N6ZXB3DC6 z&g3c!LD#G2X07^gyM5j3w#=L2w~cszF2<~+ZR9*3r~!LdIbg@GQD z%6*Tkg^Eu$8#&He78w`NZL`=faGI!Qz477A*?r;HnMz|#UsZLU-JQF;Y0qzyZ=Zf~ z+p((oY+Mj<9OyjN64!|6l9a@fRIB8oR3OD*WME{ZYhbQxXcA&*U}bD-Wn!sqU|?ln oaH4q0bEpo8hTQy=%(P0}8g}Z*L{C&y=VD;+boFyt=akR{07#%qkpKVy literal 1341 zcmbVMTTC2P7#@0;ZWS91&=b^>#8W^fmR zT&zVZ4{p>{?So)ytj5In;4Nt*#MX*p+B7vj*cvU3Ek?^y(3D!}dInhZf%L)2%$&=7 z-}hh6|IeZNx_vwHcjY4pvLjq0G{CXIezt9a?~?hf2!~=T*laawaVser0O4X<6rf>6 zYy%BIjCDQp4X8knjd8iD*=ml|vXZ9YqK&~*iVoQbQV~e$qSOv7Gz!{e)sJP4k71}B z^JDux5i+9lAR*Uu8=$efu1V@{mwYiSP>EKgSSX+XOGHyjhibAZKenvP!nwUoVCXW$ zYWHI+PBlmBQC>3u>cL%Q5=l`g!{C(L!!T4SN|O{#knm>8C^ze2Sh^fty)bCah{f3k zA+%}>uKZZSvUHXplF1~Vbm5xOMo>PV&(@&nGKeTMyHra|m8s_LoPq#M$&htR)>PD1 z6r)Dv0)ca8W{t4!Uh-koM6YuP+$lN!~}7S9|yzH>{eLs7fGbSB7?1 zxzruGEN=kO(u^ie>&R6=eL}M|Gok4y-^ieQ_seQbOPWsmcvhknfFVB%Vj)9Q(B=HH z@;dfCE_XS}aTFh1@xV(8Ar3~&P+rpK3%amXZtVZ$jDXG%c5(ctSaMsi0`2Kq>BGfZ z^8gigi~(CixKP-OAbCny;F?lDMo*key*RLQeAB77sumqX*;o5VxWA~67PB`rJbn1V zbJ28ny3%=gtS|d^B7P|vFZiS|{>%9((0WCRFWmZ#IC-pnr1rLtVs>K^uiH(l=#4KGAn0+bl z3CAEe)w8RjXsUE9yU0=3Gd~9$)6s&vwjIR3{&Q<0-_iH^+yHm6?zI(YrhjgFsf;H`(%DUgg6-9s1$pJv|S7iL^f2bNAepchlG2dGhv# pcvZezs!5ODbQT;NC_?hKAhnmT_1^PqX|er73kU0j535_B{|5&p-f{o{ diff --git a/toxygen/smileys/default/264F.png b/toxygen/smileys/default/264F.png index 82eb8eb5b23f1b787126268054ac36607dc390c7..ad41780286f85ecdc4901a2bca8fd0db9bf20a3f 100644 GIT binary patch literal 1287 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>&JTUv~Ly(S;LvXO3i@JeYcPZ{nd{ar?GK?cN-=V`K2P^#NOeiXrNOT!;uz5F!pY zC%d&=GafBt>C_UZZ77ytkN2U>Bq^x~T%Z-5q^tG$S9 z*ZmFmUmSh0FYO@E((4Oue0}=m#fg_d+n*eKx;<_;#Kx*!2AV*}i zD9TUE%t=)!sVqoUsK_l~V6f<&8tOP{jsee}*P@k8x+f=9@-=_d4YhpsGxMgg&gl&& z*;W~x*cA0@8t>nKKfCLWgl?Ubb7^nYrko^x?R9f{*3P({wA7KFl^f^zn^_!|pb+(8lOtU_yX5-zf_p+w9Ab^5oG4b%~E%n;HZ@&)@98HG#`%gKqOmR-S{8P4j0uM3f6iNBSn6 zk77A^OW|wuIp%`@6FNUIZd$u=PS>0u5r!&5rG)C0Wn6meRDZp3;<)Sj->RZy(PE1q zUpSr{y-E~z((e0l@N{hAf4!_jmt@~*Kbbr~)Ol-ao)pUs6Q_>p8Mh~RieJ5Wvsh5+ z^$lO`Bcdy{n3_|1E--D+Y+R5La<(G4r{`VwzvkWtVIOxTJlXD(HzB`~(Q(%N++#|O z#S4;Hl)mwoRZ3d=@V{Z^)s{<7kUDX3f{<>eV@#!UoZSTf12t^R{yVc}l$1AGNJ%eu z`FPhXv&2$Jh;OCcoRmFFEo8Yejz3$Q`buo`jg~z!(wqt11|T?BPDqa!13Hr)QYI_!hQg%=6-Zn8QDDMs^>lFzk+>Xt zs9o%!fdt!y^Y?BAX@~XLyWCo^;^RMlwTDl(+$nCXpJ#vShnzsq=O!k#{apz;Rr1QCKlQT237_Jc`WNeSrDNiH$NpatrE8e`AG5iKn;>0 t8-nxGO3D+9QW+dm@{>{(JaZG%Q-e|yQz{Ejrh-Z*22WQ%mvv4FO#pqeLhAqk literal 1290 zcmbVMUrZZy96!eVGc3+vBAtuwa+r)I>-}qcr56g6>vhyvO4`QZgO2OnujK;whr7cb zU<70lram;7u%~JEpbtJ6lo;aQ6;K1yxR8wpjmzSL55!Cth}o90*53g|AIKiO+}-c@ z``-8S`ToCiU7e?DYo4w_5TrKN5lO=J3FmqAVfa3_Sr*`O#NxZHl$N#ff&q}Qq-6ja zQ-mBy0zn!YSOLulQk|94-Bx$}1y7u>7&q4BZyN@CHFb@TkFT(h^3O1&imY|BDVpoQCcd_&X zu`Fi*!P1PhruA1kpsQE2G_zOJQ7*-xji+T*((>jBr#w5+Mt~u|3Z$r^Dd={7S$QA( zey=Y;hQk!c?|9HeMxtRDF+(+x!65I&cCpg`$r%BiA)My;PqS1+umhd#UhBid-tYhw zPK*IZBQQ1c4uVv%u}C;=|C0G?)t(%$D<1szy|yjaRQcVp_rrfvvs>lc8eW)rZZ%UF zF0`DOxingiO|K8nw#`T@+_-jF#GdJ@CP^H($GqJjrF3)-G1fw6>)R3-j}>v!AZ6E!5t*cXUmX zT5#r-dUSK(lhw>`hpSx^%HqO{8|NRI`z&?M?S84e^t*eWzgbE&e%JWL3cdXKWm1m~ zOxz_B-(;Es>vdBr%NzBRR|^Xll*2{$LD$^i5&X*6-y>HadH&p$*vezM<1e43&JpKYk~cZ%_yk&oMZ-uMS+QNh3f diff --git a/toxygen/smileys/default/2650.png b/toxygen/smileys/default/2650.png index 2b4fa50f5622614259da6f00244606f27b520441..a4cf9d8f21bb46c9d50ac2073a4ae9fa4e24ac23 100644 GIT binary patch literal 1330 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>&JTUv~Ly(S;LvXO3i@JeYcPZ{nd{ar?GK?cN-=V`K2P^#NOeiXrNOT!;uz5F!pS zkVwPk3A(Z3BukCV9KNNR_5P zF#>YfOFVsD*`KlVajEjfylT{9U|_1Q3W+EQN-S3>D9TUE%t=)!sVqoUsK_l~V6f<& z8XCCiwgS(e>zY+8I-Hz+k6AunpIP_qN37!`i@x~ZQs-H8TEcFL&ZztU$JPE(_?8KG zmR5gE>F&I%v%AP>TTb3pv&=Km`|sw>mHd_^HSNu$t)7>jd9v<1s+IZi>PExv^}A9{ zdpIq}2Ch7ysmxdoDW^=c{xxga+v6bgy_a<8WE~)Z^CYO$`M&~JIW z{8PhC4gP7NTZ1!wk_}I3udA4}Mq~N!A0MB6c$Ay+#vyLWvCZj+e&h=?Nba=w!s)hW zfsVqFU)5qBj6seZ+pK1+O5l(?UmDHPQp&z@;#HMzD_jNE@CMaCv0>Og-KRx-LN4!9 zktb6YF{Zvy{;pBz5_+j%a$c*j#{!splbX!qTv7higL>{#-vV3MJC zp>;~j(TBQ~uf$ecw8VUGW)ZLwn0?&S=)ptQ&lx96UOc(_&xvtwA;XJh{lf;{s}D@7e|7wcy~+RA?SAf#;QhAmir$^?ZdsQKOX|rGT3%hYh*MaWk?Hhq9bC#!zV~E7%*u$MdjSdWK5AN5lK5=oGhnV)$ zjKvYxfA62Nth6`(8S_8QR}Z{H8FGJkpFTLji+cw)_RnmMe&mHwE8;j*+``SCa zOOF&5d0l#Lak)WY`f7nkYD?6tzQ^Ac`*QE`>h;^_U$IvSY1R#%3UsAviEBhjN@7W> zRdP`(kYX@0Ff!6LFxNFS2{AOVGB&j`HPtpSure?>TzQTcMMG|WN@iLmZVj7M%0S^D v39=zLKdq!Zu_%?nF(p4KRlzeiF+DXXH8G{K@MJ2eAY<@!^>bP0l+XkKDqdn@ literal 1380 zcmbVMZA{!`9Pf6*$bf)+;U$xmZXY1yuI=@%cWpTE=-rJo93y~%54usV1u9(IwPkmZ zO@)W1KHH2!Cc=) z5qJ}#H8_whr>cCVFe52EY&8)^KZfHlNt$qrl_c?8*o@(36azPD#4WUyq|G)sav{K) z!qw7cE>FZ3SUHe-O_OO94F-dzAYqb}IuxfUO4l%(jR0X(n?;Qc8AbI#RKdloenpTq zK@wqIk*$%MGzS7a-3lQf`+VENq8dpQNEsSpWfV7IXds}+wTV`>GX7sTc0{Wc%`%Uc z@v79M_`!M99*B}b?w)Q)2Lf;CBZ>eHg>7_6{-yvgYF?KE0bfjnkKN<8=uEfS?xb)R#ZXR$begShdz9-HRgD$> zeAKQ0>_)imr*dgV;aN>mDkP~fS^=f?k|wG3k_-9 zC{h64%r7nMVBbnuY?#xDGZ7E$n9Jh?5tFzbqbN6lM7Z4l$r%cqLG|MJPq9R|Kn3d4 z?a~K}?dIV{&@l>VjkWK({)V7k=e#awMd*i`;n~pZZ7HMAjKBT-dcqswv#0xGQHjZkN%P#X-@6ySxrkQdoFjK6 zyxykTJ8q}FI{a76;^V6;SoZiKZ0=&~nT4_C8TmNpC~iGH(1u-w4z-_nFV>Ls^$?st zl#<&1!ljh4>(q_(A72WOLgjmQV`LnWS?u_8f9?5hx}d$~lZ>$sUY<$2Ja;)woF1N7 zd2s*m$$XA`(tfY6vhoftKMZ}T_q}`(v{| zx`ka>8kt>Ci0L&7Nl?YWBBO?z6`T&r=8-UDQ0G44YQvyIL4uB7*0dTqwfPQk-!vJRh zlvMo>2YZZXO^!h?N2%i@6ahDl&q;aCKKqOnKhzuD|0v?Oho`#RLObsteZUNAYw~Y_ zAHxB`0vLazVD{U^-&N$ZaxqgZ$bBgn$>ncla8Y6T)cVTWqF_YZ8)jC z_4V~ntWS~-iKIgecO9nj`1vnn5;@#nE|zg~`NEpXrH@OBdBxbZ3E0rg{kLCdzR02X za&9h<&lr`7WU$8}DrYF;Sx+=;?T=O1??@S+lh5sn=y}!f>ht(KY=01Xy1cMF)i5O! ze_d6qNJpi;$t-v#xpawJ^8DKvt+YH8yeoZDpzmL6{rB+_i9}*=@$6=gnKqbLIs+Z@ zJb-=Do`->v8p+GYLrtWnrH*l2J**c6fO1B9a0n?d;sP=`B`GE@AsR`dBu693iOB%$ zExvjrpjn9AyfTJO-(az6)6PM>sL6Kq(?Vx9tCk_`9Q_ z;cgnXxZavb>DTis(|l()@9JW3iu$Fp`VJ z0-~+ewly0??h+)Twg?PZ#G^Um$LYur^SU*5hl){BCBhmhG<$+&V?C5^>Z6qf74bpI z=QzAreK(2K@{JtG)5NJT$B&+D^~lHp)pDj}XV;{jZ2s0pi>Wu=ZZ3)myWkUpwS>$t zbS|0ErddfkGFQFN0o5$yw-8R2-ZI9qvvoqJ@tiH+7vI&0DJL#?1mcT3vrr)pvSm1LZ5sa$Z5bIL{fa(XXhEK65)+gWJ}lA2vdv_2 zuXCu4Pj%NsxarNxoJ^t*W9Hhhn&bv0vm4vECG65j7KYg=cSsR|8U6@{MTNMC*&ql8Ugy3!b zus+RWz5WJj)8S2Tx93Gdy&^5f5j z$aZtk_kgW$MIY#KzaCRhO{E3l_Yp#lUWMWXbJg!0l_QI!6YkX%x9O@Yojhv7zE($V zcLYHy_H7^AiO|U^FPn5vFKt3&Z~J?fW*olKF4z8v5s^go?dgtnJuhVR9>4uM@2w-@ z1J~Lx|6<#u)0p2eT|Wz)y>6opY<(BWBZL$cL5g;YOo@gQu)*2bSmO3r;yvtw@i-@2 wdnY>wa~#eIhjaO1RO>&A#H6UW7|K72{|o$L5}HdCr)UO(*OVf literal 1390 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YNUcv*UGslHL)bWC?r2W2bKZ?GV)9Ei!<^I z6r6+26g0v!^HTE5i#0*f1M#g(YEfocYKmJ?ey##Ie62FE*l%iVVs7B*Xz1(;ae;+_ zi<=|R$7Y5W29}nt#!67VDddEiKF~4xpu~ZcSYSfH6bNF%lRl6G&pfGlz#LNq%o@zw zCM@M(Xoi7&ly1L7Lm-(j-uh3>w-(L!Ey(?_0n)1(Z2k;)8 zxqAM7#-$=whgK`a*lzvsWw*l5$q&}rtht(cVxBm2plDXer1ilKt%rXfwmz~}!h*@d z>P6S{G8({-3*x=hdcHELl3-BnD1Wx z=)14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>&JTUv~Ly(S;LvXO3i@JeYcPZ{nd{ar?GK?cN-=V`K2P^#NOeiXrNOT!;uz5F!pY zWc8WXXWjsvabxa{CwrdkOF8&-_md~P9wWPI z0^6wupm)Seg8V>1f($&LJ$v@t>-iHN*GJoc;*3e&?k=*gnF}WZIqW5#zOL-g*!j3r z`C?u*YB4Y{)mDW>lmsP~D-;yvr)B1(DwI?fq$*V87BDba^iB#1vqvpAYZ4(d2 zlE)U)zFwPY_w7gQk|Tw(+y6?PcQJC((Xn4P|9<_2?LS0yRc4py&&x5~*q$2hcWm0( zwYT(oE8q7YUu^6g=j%Kvc9WUsEt{1N+apwU_gU$seGL7+rcIR1wV*R{*PA&hpOsdN zA9l@VOygc&vHfh>bc@9f4dz+4egFfP4;btb(D`&A?cxE;0OxayB`X>0PwZt`|BqgyV)hf9t6-Y4{85kMq8kp-EnuHh{SQ(pInVM=F7+4t?9Iiaai=rVn zKP5A*61Rp;DrFmi8YDqB1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H601(jC}p00i_ I>zopr0G*>rVgLXD literal 1274 zcmbVMZD<>19KV@$Euov$iCbmZ$Lko~G`W}LuDLTwd%Yw!*mNZgET6QO+>_*N@5Q_8 zl4gb0ZdRv$=r$Qcr5h;IPf`@dz(Q-QTgAyXMTFr%6oif8hC@1MF+SHO)emJKJnrs! zx!>>ie|i4@JKER#+*bD!ZVbb=#=9d)G&VU;!xr>zxm%IY&}>K3cE6spi;@X3LDn-6 z#8oK|lTeaJUj7OOG0c@yQfWJ#=;5=v>XjUfx1<_~jbXu#k|AXWp$#%HuV^9s_GfcA zP~;GPfJu-ELxcsTyKKV#a&IbI9?Wtw-Vp}D5|0E_XiK1^4rvx&3gN4|JeoV(1P)dq z_FxEKb1I$a1EOw1z<7P0EJ;y-WxbT2VOi=KK$8?rkmzPTl%Ho zv8pY!3gHFYHh6+47K`4Z&#Rkxg5o&N(V%G$LU^nZ&6Y|Y&1$VFM4**56~k6^4LFKY zMjy6AIP!EY1l33+)`c~znkbYqq9hpvdn@TXquu>&Av?D>Y(3A_*;h*vz8y z$hFqUD0dGw|-323Ss{YZ@DI1%aVOw#y&&JIpBEMU%7`@N;y4i^fDcR^!Gs%a*h(tl3qN-744j zKrSzuP_lJ1rRzhr3g|27wr&-41Bm@BXgi>2vR<_II>)mXZ3LRiK`6&eT?MQ8<&_QW zGd_PiDF~DpUGpG7Mq&brn56j0npvqV>)xr8yRT-Z z%2VOJC+Eg0@#8njGvTviV-;$iY`r3W8_qOd`{rg-*muRP|2W!|yZ_fR;cEEw%}?m1 zBi;0D@b|}uf^SuBwjB)jw0u3+G1eUM9BDrPL15=^o^sZLEn9Zx239Vf*|q25K)`CvT0O3J>u27D1l)a=!KQqA~I-Gubf*UO0Yk zd;WazmwoRM@af5g`WvqLg>xTWf4lDI7u~L7b*J8$K5=YE=TCK)#vdzOm;!>YZTEMb bjpLXLd-d$Bx$T0;Ie(*gv^Vl`*TBR-NawOS diff --git a/toxygen/smileys/default/2653.png b/toxygen/smileys/default/2653.png index 85c701aa06edb1d245790f41b59dd5ec0cba78b0..a4da18196e8728a576e1f3e9eb77bcab98f52710 100644 GIT binary patch delta 1333 zcmZ{iX;70_6oxM$3>t(g)?vp`p@2vX5K$shSpvisL`X2$K?M^iX~ckG%M`w_h=`&@ z6j7s$Xj`euRwRhB1_%g|u#`0@BrJ+Z1Cf9sHvKyMX@B(2ojG&fbKi5Gd*@E0CQ$h% zkTw9o&hd*7I3S}v13Uq!yrQR$(1COGDgWRA0N7sxz{vtY1xuW_03=`mm^%poz6by# zCa>;@J6wAmtb|hu--*Eshlj9PTZ9$rAoNt$R){|Vr?W>GMqCunl}y)m~WY% zZ+<`5IQOA#9v(JYFs7VVDy7R~*T>+&UCBM_RrT7(H4$43LufP_n4>+m^Ye;YrBDgu zUcyYTp!2=U(vONaOFcP#FrchfD&+a0#S|`plS-u3{-iQ?(_|FUedl6@5W@Zn~PHiGcYhYa~@>@S2TxsRI&v2s4A_fq2 zXLJovkTsr4@OcdYB8^R=u!19!(9HNVQ85e}nw7$&p_5ND0oa#!oqVKf482J`WY0$K z+PLwu%vdpWGflY~?kTOkJl4?eqNR85dZNxR#cv=ne|4Lq^=;Pfy9}qw^R%OZ?S-Nz zqnNyuKd%Vyy9ka42(E@#xe`a+OPlx<8_2N%?;5p8UU{6oMT<~zA*^nmM^$|F$VlM( zqgIsOugQE-;tk6Eg9lyd2bS^^Fy;$83X+V6Ei1jRSn%R|3%ABu52Y4aD=;nkpRB`> z4>Q;i-}~4I&1>{SE#G%r$kcnKu9CsY+LkuAaLcqpVF$wFCixG{&rW%5cICBFB2-nv zTaXE#3ofp3E45R&rajz^vc>>Fosx=ejTbYjIGf)$MyG#?-j;RpQD!MBdt&hz|J*@J z(q^-;fXJR>$mUV>vfnFhCe-DVtTi$i6zCafj{^0^TPo^F`*nBqR>lfZm0f|1r~wVGV4vxXWp7+ zlthx;E3ivDjkOJ<%_?L~4|(Y^Xg}{m)em&yK50K|;1)OeCthQNen?}rgThM6-e;<7 z>$f=W8pC>KXoxBG;8DASB?czdGI^nU!bP&VSDDZgvEfdRNM>y!muI9@Q}GE)bm$)E zl{pgkGEQu8!8=(LZ5b4AI=?IAos~NyZU@q@Ak$^Zi1y8jS?beC%xC3`u4j!H!@9

`!{AT>e}7#0fi|3U!|2uR7X_AYOG2BX&%iI8ehdc7&P-*TT$p zlAbs%XH)jy8lbz}DWmAJ2exAcT#7A;Yyru`i^MFbz1t{qoYF@ZF=`?Y4EO%kWPUiZ zUX1(j_{8v|v{R|rO@bF@Edr`K#{+6Zc`ej$%}Ct!z;M N5D9@kWnL#U{suW`dq4mH literal 1381 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YC- zElkbLfKlQM)9aa6T#}fVoC>oy6KF43uZx=-UcFY%MX8A;`9&f5`8lu@5Rj2yl3$#W zU!dR|Y^IT~doO%TiO^it=+6z~O6^iN$_XV-s@&M@K_vSBMKN z3|!nCfj%}fv@o!=bTw9j>P;ah%=Ced(FY|Cq{IRf0;WI^6Q1;e9C+qQ%>(9`B4E}~ z`+S?9fq`+Vr;B4q#jPciPkS*1N*w?1m%K7^;-geAgH^GLElWK7FYKynKRwnvZTc%TEq>aSel-O#09%;E;)9NbNRU!N^AJ*(x3hLv9I{C%-y@^Z2t+% zBu-Qiin3MM_WHnfk*6Ist#azies0O@nVjmX(C{d(^q@&%!>x3~UAzVBcSd{U{h8*kdv@|a)@G9HpKmhHDNMn6>9ksB%c!=i_Dt8JJ@!x1So|yKas}6N zlhh3&k5y+ZV>dl#W7?6lrsZ4=*ZmAL)8)bEJZ5b9*8H`~+g@v?mQ``)i$k*~PP-Lp z@#SyTpXm#;eQov4>)iHo#5v|ad3gV;(jL*D-x~K;-pM)sLD6&<+mqQT@;N-5X;+F% zH~h1WZ$Fsm@OJAx+XkKN@2Rt;T@@o)UUQ!mdK1T8wEgG1)jsJ#hPT)ra9B*V@^AjS za2fc^UHOaP)$YKCdf^ oWUt@mKYY`mdKI;Vst0BFqW;{X5v diff --git a/toxygen/smileys/default/2660.png b/toxygen/smileys/default/2660.png index 3ed03732716a4d582a1216e6c75da0cca950466c..e2a9757c53534d01bc9870ec21c7048c419dbc5b 100644 GIT binary patch delta 1172 zcmV;F1Z(@|3cv}F8Gi%-006c6H|hWY00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33M4xdLUc#~00M$~R9JLUVRs;Ka&Km7Y-J#Hd2nSQczvc<$U%dVV^ zEt^}lLmKnZw{7dpuvgQtWS6W>k~U8&@GVwJi?5D_S$x$at6@Yy5iDYsE(a&sy~ygt zu+Un7*}^HlZGX4JCFUp)kd0c8JY<~{FZYqjI}B`FtkBmAPPu5yi8zw$K1`DN5aEV= zk}LvLLA|goqLSkswjEX;^UNLNd>U~ZZtHMd;o_VwH$Tc_WC5Sds%n?gn8sw3S6p!? zSy4{+aq;2s;$vj&KwhNA{mO*#Y!d((xj6WVj(9;?K!1q&-?KmppbA8=)zPVn4#DF& zZwsMe-O<>{YBu)FMKo9&sQ$=#03L6e2p?#+HI!43Q;Gu6UW^FXo~)gMi)#*@T^d4n z(f=Ggl;|i9K0U&@jC$CFAjJ_s`f^v;zl)l%bcg#4`RLE8!CIZ01_5@B5aKyx8#HNE z9m<}I5Pw<6ZdyUENKp|OWWfk7aGPcj3Zd$PH^sOn6L9pOL>x`$=!~ zRl!~K_BhMAKMxYRKWVOR^Hh9UKYj&rP)yzYkaDmy%HjDhpP@ zrIzW}Gv9zU^RT_f#n!F^z%6%I?7J^9pnqEAvRp19SOAQ;AF}5?L_m3&>td~tu=d2` zke$&xC@`H|ZIRc6mdI61C%RB@GtLTW6e%oV%9MaNJ_gSYSVG?9Q}ZJ7r@jpe0#8W) zk`Q=N4yHRMKwu0@xAj0zw5t!-MIY%KJ<$W*76h3AU$QqA#NL#|E`RYX zIbKChet3c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y`gLD2*8txIZAW?5>ATTyf~%}X=dT(;tF&D z(BGC$My{3?7A9tf&Mp>47RE|Yy(#2`nLf}l`k=&tlvrRwz!V5#!jnFb1J69EdB7Y~ z1k4(`CoTiCCF3+t7srr_TT6l*y_o_9=EYtY)#VWsUXoyVBlP=&gG;KHFR1pA)#_OH zuj7S($=YI<=l>ikd$#%pe0t#NEOz+ts@=89+jeZ2?Z|Jp&;0YV+WfS89P3_Rs-GrO z7O*zV+`;@n>f(zV4m~=ekiIl%qU`aV`XXHJXL9~WE{M@v@4NhRdyU=x^_fzJo6r7J zU`*V}zH426lU!)%UG1q}^Y-7&30i%%>e#mn20iB;7?-SWm{+#pgVx4)0T%rYck_am zU#>ja{d8;Dp6jmzx8F{k7|T&txxYc`^-P~-68*d6wwswtWZ6Zw%Jm^eeuPPpGVXaZf~3B zqn2qSw_Zj@%x%vrX%&rGFIx3ZfAV6CnRop6GX~ookAy5-{p8Jf*_h=w&p&V3_t@6; zA1hnjddB<)UWo&lX0uBVrXP5H==}4o^Q}J%Mwm@g^t=?NBUXIycA_DV;hHd+dCM3044$rjF6*2UngA?*=@9?` diff --git a/toxygen/smileys/default/2663.png b/toxygen/smileys/default/2663.png index 4dd8e0b23b0be76a987a69835a0ff64d04ad2d69..43b0f134e26101249a98eb86aeffb82627bde64c 100644 GIT binary patch delta 1184 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 Y8EjUHAnOhrS+W4o{llmi{1S`*2C$J>RR910 literal 1444 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYpuE@>~i>^0MC_=2pLdqPB5H@clW)_uqg28~0pp;u^Qe z)9psn^FJMJVhOk1=vyQ7{fLy}>(z>8F@CqoI@7|L6C=g8Wx42{KYrL&^letS@B;2T zONuSLQqPwL+wgzAqIM-hG~ma9x20mg#eeDk{oUDfe{G85#m=&=`fM&rW*4KvPcH6| zZtKjj;Swo*;M;gBODXWem1b$3OS!vDnlGA#GBW*<=3!W`&~@$gGZ1zvt{q%QgTe~ HDWM4fExrr( diff --git a/toxygen/smileys/default/2666.png b/toxygen/smileys/default/2666.png index 0fa97e6e2a777ba4f822b46263fcf6db7736aa80..e123db3d869096fdebff9bdc1b12a3f41e4a8db3 100644 GIT binary patch literal 1460 zcmZ`(cTm$w6kb9C96d;c&=LWW773vVA~BH`>7k5(a0U`0NN7fya~dOxf+(OA59g`FfVq7D zI1~e*k#MQm&k+DAbzjc_7l^FG`XY0D?N;p?GtFAE?p?B8waG>iN$Y!HtlnTlsM@U7 zXhUeU)-A)LstmDr^p&s6gHlx~gd)Me?Y{nh=aHMUt~&2L2{3{%?@3q=GJ>%tFwS%} zcneG-!4wKiw^|J{goz}WZUY7OP`G_H&|rzK1P^S1DVFf4J4}e?( zOtyf6Z7{TS_w8Rj+G6c{Yz;G1YuLR?8Fi{hROOKKP^aGG31G&mD1`GWG3`4;m zF%wo`3^`(g0YpUyLx_hk<9$yy(zwlhrZ>`KVkdrhL`&nf+HZP;gC>&`+ZGn=Z=$bC zol5w2GP6vkvkTX-q0XQQ5lBUNxH*GU7+rUS$nqIpF0#|I%JQ2*3-dM!0F*F2U=N?k zN+BjB#YaWQa)|u21P(EkmjJ-FoPt1)+99Ii(7d`msxSlHBgz>pH+`Z*B^95WOw2Dz zln74v-jbkRK6_7{4gH$6F+Z$X!>7J@AZoDSyoht~RxMeZ9@u3z5RqHpOmwEf( z_>>bRE%O*JTX+5U4igQYJ=QI0IFmsk=nv)ky>+OTl?OvEFaf1Q24?Q3T*L>)XJt+H%>5Wh2Co)CcDPPJ-q|R?z`vA6#OvR<^|Yq zC!TLHoxs!yROj2c$JB|qjD5FCnK+!V=iu;0dPj3hLcxan z#vZulpX)D=GPZC<$NEUTJH$3_SHtA#Dg98#(V?HF?6jk^Nzo~mzF&-`&Lz1mjFuUh z505+Ta+<&57a&eSgma7C~ zCGMSVouv-Q#bI7%iu*m7tkv@rxWuPQ&T$}LnxOW((big5cJu|wfd?w81xoglYerf>w<0|ZFJ&} z@QBo>ejT|WbS3WMj-On=RW4Ks`rGUFg%e;VGHrkEyf;59ym0@?Ckd?H)N(kNCD%y);&eS5U;o(5hD@ zi%}0k*7rN_bnb3@L-^ccu=-_pd;4kXY6Y=X`e}HTM&UQK;)(b?)gzcQ-M>6Lo?}NVy7iO;vl0G0EUaVa}|A` F@Gq*{YNh}H literal 1506 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y*^NLFn^O93x_GSX@h3d7$tJli8C^fMpzbGU>KL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDokG#!67VDddEiKF~4xpu~ZcSYSfH6bNF%lRl6G&pfGlz#LNq z%o+mUD(*8dFbR3OIEGZ*S`zG?EgUFe``PQ5&5E}HlecU%*~s#U`;}94)1r>(j~hk0 zM0VwGyCxrC{>p!X!}V+HSTbENDkAS z-dDXputng<{H5Eu&&+)x9TH-y98>=0)9XiByKS~k{e1DGhs(vUKQAXfU9(D>d$YT9 zt*r2wxo`M0+tMHUg!HyAl5$(*<@0^k=S%my9DaW0OPjpcHtoXy8k1+X*GfH<_cHBZ zijJ4r^CKYco@Va+-IMD$y3FS)Sm&CkXt zlMCKA#$vFEhS%=q=YYg-;IdsOW|Y3KL4P4;W-mgq43hTe Uji27v%LJ9Wp00i_>zopr07%a?{{R30 diff --git a/toxygen/smileys/default/2668.png b/toxygen/smileys/default/2668.png index 244e9543ad1b798f36429ecf7f93139779752fcf..6e148ea6a8c6e06b35f50a19f1434d3ce6813467 100644 GIT binary patch literal 1559 zcmZ`(XHb(_6n%seq@zLvL9&FR6ceI^vIqnz34uTgy(S?<#Lx^&f&~zC5CueR2-p@I zR&-sEfI5f_RbWwp4bo&#f@Z-&S&(nP%<9hWk9~9Ax#!$Fciy}*?{Fa1M^nvE4FI4? zCVA4~s3V`MiZE}q)n>p!iAVG&0#H+|zRXgFJ(@$J`vZ_^0YG*h0Dr=%>~{d9;{cGx z0^ocG08~mvJDW>}d8k!xm01dfP9vnajMeIVa0`0!A*JH^Kt8UpYZ=b;j0kF_yl_V$9+< zEU=lgcoR4y-GutgV$KG*e+;Kctx!@Is|i!h*+}YaOyIu(C&xFTMmDI8n(9c5R3+WEv`Ws`>LmsJ_YG|bYt)B(#n9@QWee)1Q$5|U~q zIysFGd+8oW(z{+^%A0C4DZv~~8ymwL{AWfOL^Zo4U+cBm^tS5TG-LI~HJzxoubsw zklk&sKpCu?ziK(v$FD51j`a9Ab;?I{z-E-u+_}s=TjkXDapyYOP3v|8JM17L`d-&j z#nvN);X8gx-H#N}Kliw2wUU@8l$D-mJx6T15VRvtSng+!Q!0MjfL)bW2x>ZaTFwoq zuij;RYk!9bkz~U6T>yi_G^!W1OgTIHsOrG^s2`R3?|M{Ls**!`A8S#~RSI&uW9xdt zit8*$sV$b+kb_^v*Nh2KW3Jm>7s3jo5|^$Jv&D9K-WPWbS|r9Z2A51de|=bf&7zg| z+V93HZ-=eJFWOx>>=QQc+iTS{ea~8q>F3_=E{jCRAh46GSod@lLsQ;_T3Yo=_Ud0U zJeO@9_@LT!yYClJVS!h^exjZF=_@P3m6TGw(&y^&Jb|z&+Z-|^+d_S_~6Pny_*)_;_(~Egk z8x2(Cch+oSaobEi9N?>HksaYN66ye3@_-rFV=v9QCs?op29 zT`eb7CL_DNb8+2PD(sCJAzdNy(B-*;BIdby+8Afiy#{ARTQLf$esCwVk#-}vr3?Sd z@0D!=!ujqirBt!GgSc@e*3szmo+z3=q-Pwny6nMKcw^=QuTVi8OTcyJq;cT@@Hjl) z7Uy7Vx0Pv!bKYX_Y>%_X;hb@}65?3(e;WA7aS8FF|8E#I3e$%h4CE^?lH&y#tTZkl h#wD}4XfmJ0om2{F97x%$q zPzp?{@LN>U=S~&r@&JOhGk{XAkQpEt22fNEuU4Wcyb3@d7y%*iMrH77T#4dHGB9;X zMQe7W3C|~~DO;kXl9uwk4F|!BiVAs!LeAPtKp4ZYAP0iTL=72NY2|5$%*rK%7)XXQ z*v&TH%vymUBdur4d6iW3^mzytn@;zF*vd^MN=zB(plu*5hd_%Z7}umW$LBNuy75wN zuCUU^fcXr^mfHB1$4fOHfLZP^gm? zA#A3V<7umb3E4G^cBin=E3r6XXK0?a7qV3QaQmY5Md8KpFjjG5?BZyYe8BCJNTzMdB-MqE>w3Q+q=hHCW)S-i z6ur4ayJM*HbmD@hxH{wNzROG2#9eRit{dIw^6$;4`6RU=ObFaYPE~j9qAz@&oL3vx z(i_$I>Dw{kZb&kC{AjohjcjkYul2Uq-%Ixrur&JCsh+inu`l!n5KXK&Y zJH9Vna~99EQnKi*KNF*C{GN5Uq8ggp7sk&kTp#yj`aqrkm+06WrFv*E?xg9EM(Iwe z-|qDo5*u1`OQ58rJ#b{>ek66~sp42KjoBVwo&}L`27U?BGh>$RDfpbK}dh4o*1xyAOD8 z92FM!92c)q&4+3d|>16T95 zxsM;2*;MA{3{KU_m4ApTSTTRTdTGRR`Qiv_ zLq?V$Jd5e;WjDtKPC3s#7(UyXCJ6p7&Rx9p{;KMGXJaYf-J0}W1#@<kwW16RWI t>W-hQJDMLWAmngfrGH{=cFa0gm}CxLEX|!= diff --git a/toxygen/smileys/default/267B.png b/toxygen/smileys/default/267B.png index 39e485d47878ce11fbdb020a16f41ab3fc2c79cb..4c7b51d7743c1a0a8e1d500a76687f14ed1294a3 100644 GIT binary patch literal 1597 zcmZ{k3pA8z7{?#?d!ZeRQsWj!E8{YZku~Hpvxd#Y#-a>nD6GL0+6}|F8`oy#(nV@1 zx5|AZ;}#-BVkIOA73ol0q5WoePv_L0{m$?E-tT?RdEWp3|Gn?|k`FlA$w(?o0)PyO zXiY&zS^h09ggmu%w;n_WVLwX;OMvScQtMO^WRCVBQXBwI?gofq1H4B{G4lY&F#wBR z0QfTiUo#4SciIOa;BkQLZ1eHs$Gpdo`v|hefO8L$?*O|KVp}1h3r<{zq6y%w+19_> zdtny1y8qDVj_6p{k*j95jyAr7k-aB01edIv5~qHfF?)h(DuR72_j zUnp}F;@bFYA}VQvd_}}qQV$>>cIU2B&yfvqWl|jZd-eAfyS({lvpOMnrB7IuHzgI& zT(JK(Nw8}}%a}6%Thd0CfP*>y$d1Y}vF0ZoTU;Vu9}<$?*|vD1PeAlGX1$-s`G8~X z`0yAPwiNxYXQxmjYV*lxCbF=Ax4oS;B#EdHk%<_SdWQ@l1=V;6v6%t>d& z1~nYb2o0tm4Wyx&kqjC-B8UNi<8WP^>IyO98-6HHD`CM9({%l4^OEzPQF^$3*RHy% zCrVgC1A|4hX!&>{cqL$*_=D19)0nUK*0M^Ew2J(Pfd$ODpnW?tTBY9wDrYZJYVGeW z*@)ELr4P-e`uL!ZFw2fbv=xaP>-T1IQp+cOSLkE@=Dffe`2hEzFbNeA!HZ7Y_)%1w)CQ0S}cXTobvaq>^q80%}(34-Fmcq^i5>)BA>7_r1L- z_twaLh`~w@YxGzrXUK^J>Xcper1*SCyEN(%f^y63f3?0ccWp*j{RH}ZgRZp{cF3|B zsD*~y*HT1?yyzC;J{iZh#Wxi>pD z&)(l+AKsAIt7VaDJ2H35f7)w=p?bJDsF;i|qx&rvOuL2UUo03*_r5xGdd0}*u}jU8 zGL7?VyvIyMkysw3L*IM;d4PsVcQ`LbU(hyAd00KgA_282gdKE$^Ye(S#0BLSI%^VWfPNe0tY)?5Fc(8bx#O)G)3ri5DxPen(z$qy4v8*z8O zrE(^)L+7ay2}T1t71KnlDNa55`io~##Z|@=m#5D8nPxme7k$W?J63l-MQ@?2gkUT= za#!q%?QsK@$rjC3snJ_0`A76_+aSlYhh%N(tT^oB#S_Kh)Em?qBO|E4+$t^Aj$+OG zTdGo3uf-6_X{Bc($mBG(hIRaDX1R1+xkq4mlOQ=qO(-rq)T2-`sp6_aRi!}A;C*ke zK}RBSki|H@Cci49D7~#XEzLpElJ}K=fl^!~`yV_=|*G11jGa@NP- z4Y7Ev!5$0-kHLsezKZ*-At>1QC_VE38^Ys=d<7-G!ogrVGn^Vq154jvZyK5uMD?Rl XXjI?GkRBTHPX{2`I9gX)dBy$(z&oxQ literal 1521 zcmbVMZ%`Cv7+*9h2vP>IM8jo4l!Dvaz1!vPsq?!p5Q74r&R4~wr5hl^e=mRPPiljph$QWbtC)-1g`r!DXo!x!k=iTS` zd;agvwA8nQ1H%I$2nseQF&259?|oi=QGPFd_=J_0MUpX7vhul-n-u^=+4&p*n;mQ( zumILx@(TzaOInUbut@eT#+)lqmHE*T9$iK{&>)>Z%{I0xi$PCYWx(uTmC zU5~6MQj{q!8su|Hr2?>)rl#9U3v60DvU(Mq=+Vgp4j{3x$5H4Mbsjx3&8w65-eVMj zr&Xi^J@VA4%#<{k<^=!~3RS#KiD58FDlj!cl2{y!D={2Z$~PI0sdWUY!xP|{3z4k} z_FSEXG0oVLB|VZaNiH3Vy4`MtTczNIJQUMvwO$S!kC!###S*8)dg7g8l#hV{qD|mj z6307XFC&}77fE_V_Vj584p&ObEU{CZNtB#2)Wf<^Orb;_4sTr3+M;9u|GM!^Z85#X z1yBnR`69t4*CRK|2bOd9d_!JE*&E$Dfs>2E7Bal8$N`*^nb9Ni7loa(>u?Ms8I6H7 zQCiw43kF(Ans8b};ItAqB@jN2XJIK4*DyGxrc_!2!;H9wAPLH-O3>g&+N3pNKCIa( zO03fce0Dk6?hID-T&#{304woAI?osSIv_2dmv}LscfqukgjcNRoOa$V#(K;1G+G7- zTrsel1l|Ep=U2x)!@ia@kQhZX8iG=JC4b$odGz~4Zr}NyFw2_Bs|`njves4p-JPg75EH&~ zW#o;fh=u+K-a0pV^WLM&%m~*unRWcN!LXZ8qJQX7Z|Z5ivAv>sPw3~RpROC!3{t6Q zs!x2s@=#;_oUZa?iw~|DR__dtiQz^zG)~>|e=HR5Pqbc+?b~|0;7;&655)z5Tl&e4 zFzb%auA_QQpJG5g+A`99F{xsSwQ*ayP3-{R9?qIb8@l*~`z%$~c{GMBwDz58C-Pn;2dE-&6P|6p9k{AImIYpZT%jP=*kS&MH~_-#2gudHe8PtK|e z#X9fp`K))~+E3Ya^J2`6B_~~bmp7r&$Q$3hms_2OuDa5iTvZ;M&H0^B#Y%h-r1-F_ z#{Zmg{QlO>=vYVm#RpDPk}3o0-F31hVrq3yIruWGJ#J2P&ctKT^wFywaXC$`iy{w) z26S!e)}OU(tXQ#ltc=)r9X96_3*#?kh#Phjg@%q>{g>L$R(^bZ;zww2>}~%54-Pj{ diff --git a/toxygen/smileys/default/267F.png b/toxygen/smileys/default/267F.png index 8e0341d58b7c52ac087c4bdcba581f2f98772ab2..1a33390d37273b1c29ca2c1458e11fd817ae70bb 100644 GIT binary patch delta 1305 zcmZ`%Ygm$396vftMJ8VAkt`puQoBf^=2W7lA{iK_hG|omc*)CBQEB3(UQOrSA~ecG zvm0iXE2n8LFS#_U6^Zq%8`emH+NLGm5#PP8FWZNmFaPuZ{m<`m&U4z)chUTn&=&xJ z^+fC*q@ZFwnVtaLIBvAS)Ecd=+d00TY@%3)9fgF-FH(*S4|WKDzgNs#slB#ncFzrcY%K-7D{RxKSCz|ax_ z=|4jV`gy>Ie;tJ%DVDSzD_{w-`A6~L2z-A8Ms#5qMxXNbMM9%5u0c4 zwHrh;eQ&kvs%8+&xdj+xPT+)7-7*bgKcv#)glfGCb=PYR62+}X{T3S2>QzjDlrc)} zB;c!&bZ*rX&y)+F1AVx_n?^2Y!433VdZ2$VxqPqcc(2_Ud60}W z`dm7brmxU9M%$o63u(>(=;mayLb!qKbOI+e`9S=^7y>tg6GPx7LL30x%1;LQHoqfa zUZ<6~>k9Kx_fpDUFrV2gXNSz@X5EjRE#8!Ca8B5v=LbPBy?bHLCEHDyXQ~t{8BHl;8D%Zx_ z-vk|eIu_C4KMg}2@00%g?!GC%T=kK{t1;R=GcdAZ+0E*4zS8)?C5D6h2|HK&U14#t z;$zuP4j9&s&g(?`ux#U3%Qp3q2YcKgmjG;=%j)2E+~^Q>X2M`!Pd&d#)v$$eLp2&? zPN@p6bhh8#vg&r+@v<)mj9j7GDw87`l*f+#Wrszkjq8mdgRVtKql=({fQmN~TjGx1 ziwZx5>Mh6(ktCR9J?%9V8aKR2HSW>c?CHhxuUU<?he>>;x)SC3L^Ze&O_!L(Z@~xIfcYBFIFgd^QQ8G#yH$7=OSKo}ECu&=M_tSpg z{V<-gS-?F!B$6qv&9-!PqItW!xSzZ>Hu+Uj)!~9goe7T;@%-8BN5~(orF(m_a6?e| zgL}G1NhY+x*zw5ZkYdYnJztZx&!51sEw$NWTj{N2$DXe*NRP2FbuSGMWo@iUz=)-I z)tM9wWJ3zYbq#BzeqMKGYqUZ*FsaB)lj>+HJZ0m5@A8XGfZ25Ys)k&S8SCgRDM16W?jpi zL;rpxs|}YH!i{EgW5`jdF-QSUBqt{#$(881h2=;hJG+owoHvn3WD=>aLF4)#LSk}s c{DF-BC!hu=sih~Z?a%iRV8}E02BTID8Bsx#AVyRJ66_5N zjZ%#yh;4@_q!I{93dwa1MuV@C4ys|RU}CJXa0IX+sH8L&5rWN<0SBZ;S#crrw}uc{ z7F|d^>BD>xR%((f+BK=Ry{azQ-W+s_Na)uw)2uEZm~#bj*dU=+aX+s%niW?sPg$4T2~Fh$6jBF@#u=q8B6-I7ts`a>S5T z1vV9hfZA%f5a8)*2;qp&w=S&cYl#9WLt{b&#jO|`4x4eUpmn20`qzyO(Ry85L_%vM zU2WBZpdO)u1R3P+`G!m&@P>X}lR;607ETSeh9$-DaxMgXv5K-tV>Sm%d2qIvWj#EB z;|y=d-6TojIP0LCERoRIh+B-4PCLPIyq&kh7prGpS;Y4UPRU2G;Ge)OoX^Tpxh@lVT^(oV(Ccl0rjag3;Z zn)$He$|rB{$}XJvVc35=_3E!voeif?%%T0E!F#z(f6~ZXJt-CLL(`A@&XxI-znRTH z@bSItm1%I^;+^=y#o^I~&9^pX$-B1vGF7_i`oiYly*ppIG64zfNPK$^_SJ=FQyl|2 z$M+qY{rv9a^!()H;BQOn@!ieeWzC<+C_j>N^UKnaANM|4$nW~FBx@|5Q}y-jKd$Ac zMgLspi=kI9A(P{g4i4cxr)C5G{Zh#9|2wl^E!S~Td6sb@ zI`dc8Tzy7n(ib~CCvV&@d|_hrgI-}E^QErSg|@NY`^WOm-GtIz%UO-?fLL*~n?$!- w-t2f}IsYJ@4Ij0Pr|q1*TGo>mNJ`FyX3va0Xu16T0rOwx<*T>>xBs1g0D=3~6aWAK diff --git a/toxygen/smileys/default/2693.png b/toxygen/smileys/default/2693.png index 0a2095000eae696ceb71d6b9140db7f522a9eab6..f87253d8f447ccbd224b2fb06f3848faead5c48a 100644 GIT binary patch literal 1742 zcmbVNYc$*07C+K@RGV-y6BUmn5<-M9w58lqtTwGzB6LR6jzp4b>yZ#DnjsXyR7olE zXq9?YJUS&@TH`$wwNzX45PJ52KhAkhKV^IxS z+KtL?H4B##8W+&gE<)o|R8%37Uu4Y9(dQTGN93D?WjWk=YumHo(Dx?&kmXu@zVA;on_ zu^bcjBPQn#{`vz5kO8@7tZcK=5r{vo`Vj=kz%o*?A!76Il5s(zzhT$1Vq5WubbZS9 z=B#zWfbCDu30HoxET4dm50!&DfWJP0=jP(hUcm})STxMz&K4N`P>ae`Kuv%+nDAt% z%ob#9DMSe?8!}`6s25vokWl`Wpu`~I+Sb;Vr{uzS(D1?qG`}x%{(qK+bp5q7FLQl8 zFH1uz)qss~#B9!{vom+?MJJPWT!qH0ZtHv_u&@;Ab-@A~f87jlIN1OdQez!GpL+OV(-6}eqTVl2+SZtEB@QioMiOaCQl_|!M*YA(FnJ4cFNIjdRpL@;&GxYu{w zs;)EZt-GuD*J^U~_xDMaw6KdE`?=1_V#|-8BL~dK9y>CaiwwUUGnIX%*q0O38hFO1 z!%*dPOo1gnEWV}_jUX;Kur%Le-JTHj2G6g7%zM97_>&jmr`0fzlXGDhcWbTQ$N6=L zjL>(C|D?N53v^M`^#RjXt<*U8)m)iA@BFCOpI~NBo}Z@=UJv}d@+;3ZX#YN>(kLz- zo7U91H6x@~nmbS5=Ph5}{Aovp!DRB=m@CuW`A?WC?GLK=z2owVkf%Cu<2ffrrjjt5 zN4a|@c%y5_H7EZ`xl{Vd2Q7z_9vLnZLi#Cv*^Z6|x0>k(`4#<&)c#l;@?KF7z-GN) zNkC4WJ2s&{IBH&!Vb>ErK&!-uxA!xt&ago2A&(;kRqFgdwsmw%b$Vj9*KB!awA_5H z&38ojV0y?ze_@ZrLi5nlt&yagdq1A(y}G*2 zFZxY%5-0d!vTSVQ_&cXh?}&LhEY+ff?s?Tm@*#Q0XIQ>8TlCGw@M-9x>QcNs=!TQ< zp;Mk}YKGvMGFTG0oc%n)h&JO}Qbi}g9V41!UKV@*arf@>>^9KDIv*eZVZ$xrYyD@u zeT)T<+jMKw_w7F7@#URq3%MPemyZ!bVOpt~16|Etw2drpAK*<@^--%s8jbCa6oZW6 zx7GQRA?s_R-9=2p&~}E0?nvSZimtZvrIc*kqjI=>rpl@2AH{O`FvHF4*{O|EE&lNh^s-ne91?(ov-LCS%X@ zc;_uO+YvV%bo$g9^lpH z{O|o!h<_wRMzc?Ih5tLjpBvE(CG7rEAuyUNILD0P01tLFi-V>_GEZ@6941@HALc-x P3;+~wKd+{Pp{f4`WJ%|i literal 1692 zcmbVNdr;GM91k*4VJN4#0SA(c4nfy8X=!N-RcKRal@=(Rf^*f-1_;okqybtGPaZx{ zHxVX^6AuOT@I)uRA1AUCVagPp#{_jgHxTg@*$sM5S&HEHhvy&5CHXz_`Fy^Q-}ied zjnziEyZX2S0N}1xp*nW-a6FS;*th@S2*wWHj53jlr&5?)jK+ZoBbAJUY7>@<>u}6y z%lZiq0|4g~A|a7U)I>`Sl!=QuFkGw2%(4L>Y@yYR88UDNOvY0QQU<*^*93utQ3fp) zYG937fu|9wJQ|PB(HXZMIz)&kPjn#9?ae%4iYRCilqDyaO{FuYqT*% zszW2kY_TgDl*TY-DUX+%o6F4=a49;Khe#w6hX$X|VG$gQjbtz@hqTNdS3q%#fhNoh zL6M+C5lg1B85zWS`Yr^MS)+L`Oj^bg#iopB#mqc}3-e4SM_i+53!}sTb>o9*OM=ae z^K`g{%BBtMd8Eu9C$qWxxFH9S^+p;)6YQa2nJ8t*HsK_rMr9EDg=-{?QY9imlzf2_ z5i0pg1VO}+e1TXjfaO9|F60L*$2C60jSz;2Bq4CHL=k}^tco~7E|e=_m?cY45uzC9 zs!0ojkp_I+F2ULz;|e~?l`3c)V<9Pp@jLth$K>?Q1aQh6{uJeAr~tng<=$eQD}^7{69J4 zvCi-u#qpnF8Q)?n&@p{q`t0I;^WY@gF*Ms67<_L908D6CqY(+#?&$c?7|%G*1wGfQ zCkgr+`?*&i$&1$BEK}_%tqgeUG-H14wlsC}-jWFrgNDv9I8B*YdbDHMZ9~J_E$c2% zpFM4>Uv5qprXSK@zn3(tLQ)ahv%7Vuphv&UKJ2FTZ}D0~6k}COQj(5{4b$4b(Y0wR z1FkE}1IjKxDNB|3)`?$ZD*viN)sw}WYtOV(fBl_!xP8i{83Dgf2vonYP2K)T?sRZh z3o`S^_477{_df?43e7=3Ja4Vnxs|xq={|R>OI--5fv+?jK-cY0ynYebBfX05Ua#N3 z&$R}x4w*@~9@wAU*Zp?V)3+~v?(g05$HC)Ih4xP6xoxe>+a4x$Mum6mFctfFJLC4j z4sy@kmNT;&wJ&|v&0oAX!KFC4uCsP~*F!kl_xO8bK(W$3wE4ks#z=ZlrwB`m)?)CAx^5#}aMR8W@gH6+E zPP#o~*pfeS%hVNXh{C|nEIH3b0~+AqiET`}kM9xjCYOA0N;UHj&}OgAmcx}eIxAC!W&_W4$Cs*Sl{)aBmn zdJdQR`m1X0RPTX2V#0mC2mPvu@&!SjMItY3NZ@xizx0_s$j@naT+rFGYoNgf_`JcT z`5x_YckWPjW8O%ce_6p7`3Fu91RXWmZZ=IDndk+WJXhzxDLlE#@xM?jwdi5_%A$V& Dx;}@@ diff --git a/toxygen/smileys/default/26A0.png b/toxygen/smileys/default/26A0.png index da04fd67f315def52aa7bbfda6f5f6f14d5d803c..ec5d59f7a8ef319e0e98961f62a263cf1f13cd69 100644 GIT binary patch delta 1387 zcmZ{jc{J2}6u`e2>$DuFUI}F;Ye^;~56Q?dL&R9#XtVE}=1k+6NIzRdZ!{)Cc_~FS zh>@Zw)uS1c>|0DDJhF6-br#$2jlbUe=biJp=bn4dz4zRE&%JHPyGX`9NDcsCV6@Xi zst}=A7c2lZw~_1q@=_ZWh$p%L5T^$~Vk!WuQdi;+0Aerz%=iIdnGe8Ta^Y)tYXD@B zZf87k@a80k0Yuu+;PIdtUTEJ_)yj*7L4tG!7>2LVs~4pkQ>PMLrOS1FC39U@o9Xp7o_0X^@54}YgkYy$NEa1*fbH47Zx>sIV7X7X2XMOH9QJ=`IO7|d2W2x1ex(}Hi z-!ZXY;iY2iV9jYt`ODnGRBqi5JtH=&E$tfHRmBt6RP7RRPP}bcg|!@$owg>1L^|hV z&%Yj*&0}0^nDBXkLW*}EGkuq%S8mL1R8KRUO{Sb%E}D}u9*S?3nIME-a@0$PqXFu1 zr!B<6d*;X^s^ACmL|ay;(n5pj@f0+zu|L5HRUv;f*gIwLiZQ-AI&8)9%YHKS>d<%j zptUs0VM^N(usoKPILwNCItrEpT}k{o$h# z&81c&zj5?W-KvRPQd38e=x8&QV4nF6X8Ovw$HVxs?smoUYO5yA zeFEk(@{>U()8P#7y!1wE2=-V{wVpt~<{kO@1qBSr5n^{Y@d#eF!i3E>sZg@HU)Y#j z%+$2Hka*e$=jDZ?%e78<4s`Z*clWwYrK*s_XMfC0PeU_dSFqxdRfRN%)>a3-(hzHu z&GUvECAoEUdS!0OpC`545(V$OySUt;AuhM8CATOE+uY9o*wfR)L*i*`^~xqnl2wVK z^a~Yam0^^xze4sX!wp6LT_GFoU%V|~>^t7pj+vyo9e_%VmS1wDnufG{T`G=54f3Z3 zds_xlf~6XmU`$MmFei+TnRy<=SehQUJdQEMU@S41Z0z@E|IM(Bz8n-Da`pccMm2r* SWTW*}03hI8?W^qklK%ltUXiu{ literal 1482 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ|$Z+XbkeV zqp7p8v$>^{v8AJlsjI7t5>#&rIbo&`bc{YIaUdlYm=G`pf|&5659GizPih`8#}omx z#>C}$z&y(M%hSa%q~g|+VDAj!KoMKx$rBY#cn>xe&PrRfHsVi$__|%1u@RSgkGL+B zzCO!!ji-Q%=3S0mA?BA_zdyrwq&WQuF;kWLbG(#<|f|z@y=V> zZPKEW2L=D1-!*?9cQRS6@6*pHRxLS2M_%rb>1kZiqH7}-$r>2EOm>&?F;U5{pc@siuLN=SCb(tn7to*z&J}ThDs1CObEL7Eb-&!0_^5 zaA81CQIyaJ;Ykne>O!lT=LH{#TdCq;^;5&m(w8$V?&Wvpi4Prh7cgGq3b^aAGD1W_ z_F8$yTd7-0A06jaZ*P2Xo7&`UaYzk1Y-lh<+T{A6Q>_XxkwyPg*Db8e{6kEIf!L<8_w{Cmz@rc#D zeKBG`EmdKI;Vst0L~j70RR91 diff --git a/toxygen/smileys/default/26A1.png b/toxygen/smileys/default/26A1.png index aa730a7140f099995dd173072b65a914bcc23b2b..a753ef441ead7dd3e82650aa355cfc89a7f89d4a 100644 GIT binary patch literal 1368 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 diff --git a/toxygen/smileys/default/26AA.png b/toxygen/smileys/default/26AA.png index 5a7d5c3798e1cf2e62bbcf750119ffed1f03c148..d6dc51e369b8265b0b9e13311d67c9ba11be2e74 100644 GIT binary patch delta 1184 zcmV;R1Yi4t3(N_S8Gi%-006c6H|hWY00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33M4uGOci7R00M-1R9JLUVRs;Ka&Km7Y-J#Hd2nSQczk+AfWG?~li$Gs9j@!;)RHHc8q%slc~bB`v->7H08P_pF8y0Y$KgS-R|;Wb-1c z7sEnp0cH!Qcz?Iec9)o=KtMKXJ@SxsP8{wdlXn={9mJ@L#*L|2I^De>- z`6O8cr~vKdHKs8c^olF4 zBrE82A3xq5Uc8Tt9mtE+xQK&em&wmOumPzWBc zd0PmAb%(H#)fDzzix5~FO8t@Z0NmeYBD_FsYoJq*Qwjo*7b60;SJh6z#WhEtT?C=K z=zk6#o#-G3pB~{{1|K#dwBm>#y4(@_H>n9rcet;RPkmO6s@1t^5Ma{?A)cdcqfA;= zM`zDPh<~hOH?1hHNKp|OWWk79;4;l9C>m84JSj#-_eKA~MsO>5tQh$tlN;n);3C+x zhS(K<$8H9WW?cqqpd%~1MJH!KEyzi+Xl7AG&cHN~qah_)(n7fWfn49n2e1vQ2Qd8~ zGC-rdGm4|JadMFFwn?MQk&tjm_2DGrDaGlKA%8nIZ^^T6F^f&e_1H)Np_8zA>?8vo zUho_(nNFR&+FvA~vj*VHs2)y7P%1$XmZ8HeJ7}YCgFc9t&?%Z@$iRf#)ZmQ#)v}-Z zjlSA&mwtPm<=kHf3EiJGSGRd89@dXff$S7hHy@-N)fseneoN>VTvm$R8rjE^0003W zNqAE8Y}A5%jwigkxq1B)|Xw03~!qSaf7zbY(hY za%Ew3WdJfTGBPbNH!U$VR536*Gc`IiHY+eNIxsLkJJb#U001R)MObuXVRU6WZEs|0 yW_bWIFfuYNFgGnRG*mG#Ix{soGz&H>FfckWFi&P|vXLPt3IG5}MNUMnLSTaD>h%@? literal 1410 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y(9` zB4E~d8>O$pz`(f4)5S5Q;?|Vly;)3-5_f(p{(In`xbTLigo$R*OYS3wo>=-Miq;4Q zv!4ylIB;l6Q-5FBd&5i1_-m#_4+LLX9p19%aMXXN4AD zd?C3O60FMJMT>ROaZ+3jF&{=l*CHu1ehJ|_CqZ#*9 z?7dfFAR$tsTDUjvKjZZijNj#EF>UqQ8@Ippa=MDp#T}n5Q)exE@wH0L?tRIQl*0Jl z-IpfrzVW#3qt~icrzXCZ>vwn7i)@=wxHIO`REw}}8)L61bo2NNlv>FAZ!23>A(fR` zVDdFMwKf03g6_q~E4#c;oqE&GcJ*hCT>0+1;*vUI+)`IUT0Wae@c7*847X%|^6hu6 z@GT+fmArc%wZ~YnKM8VP`&aDK;fEij9G6`TQ19|>gP@NT2s6Bs>usa=sud1 zr63^Sw>)^+-l(-lQj7%W`nC54Y&qhc8K5_v-#jtfo>5Za6gzVbv#DMBg1!F#L=zZl W{8@ZIR@7bxm5QFOelF{r5}E);I`%XG diff --git a/toxygen/smileys/default/26AB.png b/toxygen/smileys/default/26AB.png index 4cf20983cfb321a23d67dda1bde341059b4b994a..d6037147cfc900f6c69f296745a243802e5a622f 100644 GIT binary patch delta 1191 zcmV;Y1X%l*3)2aZ8Gi%-006c6H|hWY00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33M4vYMMT~J00Mq`R9JLUVRs;Ka&Km7Y-J#Hd2nSQczW$z6swFSeN2&8jOG zW2@%2+Af`WAGh0WFvGr@hNZex?NYRRQGsu>m9+WhSXjk3-K!c#1Qfv{W*M?`k-In9 zycrf+4=`J}#DBNlZFh+|3It@MHX;w%=ETcAGkJ%B?G`KawSr4++G`<>_A>*jr)}e_rf)9j*SzIRW=~n+P8u+d9f=$f-mDpcf+o_Gi})!Ns+P&Q65T zRrEgx4<$N^gU^U?E?Ezo5Tuy#gO{tq{$13Br7PTL$Ok{0M%UK4X%OJ95kkC%Y@xz*-GPyv$1cn|6 z0*f#eqXv#vT@n@OK*s)2)`y2iE}UDXh2;oLi5!BAu!{w8^#gf*BOid}2u%Nn4A4+_ zMRPPZPL1M+-KEp%NGLd@MmkBpl{g(LXvgMlb$|9PX0Zu*eQhLw(1F;RJIR2D54?vY z%R$MT{e=QLa)5dn-IL^@q20ki@-W+uGNIe#r{PvfaUfU|COoF5kH}wb`$2E?Rl%L~ z_BhLVJP#7OK55Qw^Hh9UKYj(WQ%qg_kZN=%%i;Mip<83>o3FOmBT0GuI-S z<{_n`7zDrdLCa^$nwDJEWxxcDO8`i0BG@=MYOW+!TV9e-S#7c8+IDzAYs+Ik^lb37 z<#l9Ig#>)OXF08EtPnKQnuuh$PlZ&pe72Q%3lc$o5%G@~oYUY^SYT6$7gzp#cYo>s zOfA6YY8-pbNujVXOnB!h_&nnP7mzd3vMk$DOGiG+vdqlc0DDF6TfC3Hnt zbYx+4WjbSWWnpw>05UK#GA%GSEip7yF)%taH99mmD=;uRFff=*-?{(*03~!qSaf7z zbY(hiZ)9m^c>ppnGBPbNH!U$VR1z^TIx{soG&d_SFgh?W=@DlJks&7v002ovPDHLk FV1n)b0Ji`D literal 1431 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 nI6JCR@IMa3aC|T!?*BWqoYwULfE>e zk9I6z5D!2E1zN^}c+hePQo!*lRYW<&u_$O!GQ-hygJAoI;~(AG-QThA``-87@4eZ~ zVBtFJxejwlB$73MJy%4G3rvsY9OAuV#3&|)WjHSq55;7-R;)%y{!%O!0r;pm4iO<@ zY4Y|K#G6E#C6k9m;*o+4umnS?Vv`M3hpGrPiRA68Q;8)C1P5Y~IJuHT9 zIf5ym3siwfynKC%8VOAihDlNs61J4=>jQY}V1fWea511m6O9aa;w{Xj-k7s%20ybsP<3v)LvN2%-=c6iu=c7wafW&8is&E~1gB zDO4mhnL>9H>UbIHnH6u!NZk2#&{aOcRf(fWS}|;1(fQ zN-?cwjp=yaM$1Li@+3qWq{dKSD!;J&J^mRGmkklRc_sxA2>L^TbhZcNNyJY##X+9N zO8-yJXoMJ=sW|>qEHhg~1)8SsN}pJ~YaT>Nbc~v4je#>Z_lS-Q;dA}NboV!i6!D=E zPR1wQUA6s9xQpPH%T=^8F=;UN#-W34LVfAxii@WQJ1u-ElAAV2p_}85x&=+0bz>o& zuGj4+f%KlGE1Fqf7hE`Be}B{X=SQm)xp}Wy**iuqX`eck=^ks_jj8QMgKtrHcT7TA zwhyPVQC8g1E)*P(>Npdf5s-g$bzV%Ls>mVNV2vr_$9fF=>&Jp7nwu@Ft)KMQ-BcFe z-af2~d)f7i)+Tv=V4P2iNPoZNL{m=&QuLtvp5I>ds7LR}y;^;eqr+~!Zm`a=+9|W( zX@gbga$$cJG&^K|!_q!&-WG#Fz;U|5*VH_0qYOyNNre^pi4V#?b~m$4?|b~J`c+zl z?bk~81!YftY7XPHMwRW=Co&)#V`O1WCStLpN$ zPui@1GK=ytoD|#tkdq5c<`&rHoQibsEzk2TkULcFPFId>boK9>x4AHTd&T8NTJ&M1 z-3yECt!d(q)?OSt^xGOQ<4E?tpwsqOv=+q{d$*Qsk6l@r<+-+-S14+{v9r}Cs_ymR z6W^>QI~~i8EGBpK^>n(W)x5L|3r}sYimwE`>6SYStP8i9Z=+_fGad~08M3jGDcWSo zD0*d+tKIpin1E=#MM>D7jr^4-I_d{UPWdS}gh~k{$(3PvB&U@Xv`$bIU zTz%KsdcT*uea=QLG{3bZRj{dL*8phK9$no`p|tysqL1!Ym7ZL98Td6sa(xutQZQKN z(wk{7lAC|Z&)*3j;JU-NJ1>_%Ywf6qxgia%C#;>y6Kl?KSqJxA$|^OuW+{x`EM=C^ z;dkc^(Bz9%!^`e`HTYN9_(t2L+upG7f~e&2ICkyQ-)}r0rG`9xBWG+Z@(bF}+#KX` z8qf>D)S(uSuKcmyn>mcQwYJ(XUB9k)BukkCE}DCH`Mfy~j1%LdE&3f>j63uh(_G@ diff --git a/toxygen/smileys/default/26BE.png b/toxygen/smileys/default/26BE.png index ef49d5589db9bb752548e361c5ff335622687989..aa929b68f3677c625b8d9533858a16f9e038d4a2 100644 GIT binary patch literal 1815 zcmah~c{H2p8vhb%>r~Cq_DTV``1*A}t}|DlQ@vGiqO3Ev0sy z(rC*N#MYpeNuse95d0rmt!gMJuYr(LR{7}Z%Zwr-gnRiV5THDFQ!|sZixMB@`{3_D=6?k)BZKz# z4~t8Qy`PbS^d6-Es3%V|)>fH4QxoeT>88F3$?rdXiHo8z|Dv-QnE4q0+Lc1CExHw3F&vu}qR+&xC;miyoHDLt&JHb&Rj%+T!O^c1Js&F$Ty^!<|b zgK(XK59uf@scL;wR9WBLJK8nI8R0CH*x0go!YLM;=;&Dgf>cylS5ixP)iXjJo-DAm z?0d)WeaC08XZt2*`&-)cg9%SajpX{aVkf7T{?XS1V=aASP2J2|YJWvzTXuF~ab#3^ zGp+J9<7G4Lx7y~cl8X0|E)w)7CIn}DsLSBj$qVXz>H>K0jiyE3)*`Rz2E1280Mi~A zIwvcCR5zNjvLozIzo?}wqp0@H>F*5nbr46C%yhLswJyxl&ISpj9Qf)oWXKb2uUN_R z>U1>=FYSx_lS6OZPk|Rp>v-8tSRqF zqCwz?u$DvK3?F6&D!1ZhTG)3wH33FhM(!t1{JvT0=1To6S$&*#*3#%4IWmi0h;z4YDz_(uP$a z+!@+V<+VxviP3lCU4UE~2a!d;PnS}%!k(9!0<6s9)Hn}&Th6j3d_F*3#poo> z8g-%e%?;Vk38#goJk_9I9;b@n4qUlbfvr70=D%|s*IVb4kuOP7aknMlJE^&Ee2 zG=QtTxD@jhFGG;P9)>S!fLeG%BGT@L%GK!%{;^fqwFe#2Xaw2mA5z1Mr{nBV!Z05<>#x;S|-`Ct2|ttA~5NpT zoA}$P9W{n^N%JOx_0s-1b*Cv|4~Df)^QmLcSV3K?Y>PBU{e$V{0g0zKrojV;@%D@X zjKb`6{Z9C~T?jJ*e~V|n?;Pr8chgzvSuza%Y}kf~unNh#c@(rzvE;$d1v^2G~UJy+$l$H5ly~PF~me1wQ9aEbKKYAoLJ4X=& z6>_ekes&!j869C68yYFy=XA@U_W)T`8fEK~(8Uc$T z;7q;nIEaAr;Q8}Gv4NgAglz!E2j`5#U?YONanQ5^09z}2B+0@f?mzR4f`|YB literal 1712 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 literal 1639 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 diff --git a/toxygen/smileys/default/26D4.png b/toxygen/smileys/default/26D4.png index e28fadac42240880ba55a7482b605c05bedbaf1d..dcfb49e69eceff8f534d3574088482bb7e6ce4e8 100644 GIT binary patch literal 1536 zcmZ{kc`(~)7{^~%5EXYd%E}1pOyh`XC=Ici218Kov@{`!Iujb_vWr+`>uOYwL0f}r znYBnltJ-l7T~$F_ZCTX=?dqtyY5Uusnf+&Hcc1yZ?{}Ve<~#56zV9=y(BIEn9ch3B z0H{-sQs}U1?hX|Nc($%W!ra4PV5EkIFwJOmqu$ib&q8oSMnZ9M>(0)b zZfl9kmw4mW|r&6}7!!Q=0lm`tP5GidaEGyEbeV>LAy64|Ju)2XI5 zfKVP*QW{1eI@HvE_H@>|(A~2Xo5x&9(xp0~{#s5vbPbZux^QpdG%5%ynq#5e;hioeV z6bvts%ZZ9*WB6%2HYPQW2Y_=?dGIMwC4SGB4a-DD?2stakW&cJ%O4xMPjfhz*pn=U zV20_*U%h{?opJxE&bYJTNo|Tz0Cwl~2jW4dD6}&*b;bAIpi_MNOU>-gy8(2M^GT1M zVk>&u(rL#d)MIT78sqv6xz0iT=2}J_)TciLh1QUq(hpoYDBAj%GUIWZMmgd*CrDW6 zunwDZ5NQxgr|&APkf(l)CJ#Q((9oP3xB;WkG5M;vj(j$wf1W!3QJ$# zQ@0&Y)z2c!N;5j%UATrpPU_k@xYwCSSzQfN@2yJmB4OvuPIVn?LnnYV*}#sT@YVAa zK+!c=(6H9li;~OUejI>Nlpu>VxnjDyUQU1D63wIg`y0)L(FYe>^lh4xB-^%gs&_Ia zma_`|>&1Ml@P|I&UH^X6jyOhn-r)G4IFLM6zVTaCnK1h;!(jo(pTjn(233ALdAYIg zTh#=rqKS7MXS{Z@C^Tkgw5xl1!K0?MEw+px9$L)QaeY~7-m1zEB92GrwC^|IMzfz? z*=MPCH!--IObs+OwEMJeIHdI13|~1elb4pwboDontl7)}9ewlX(2Z#AF0B@_@I#9U>uwn4HY~s%Z*OmlC)(OM2HD|B4g?Z` zV2#I<@c7=e5%qr<;<&6RPTKz)T1}H%U;}!0hd?fepTbOJ12T&n&c;yVn2~Hco5@N` S7-GZUG5}OBKT4AaBj+#8rH-Qj literal 1542 zcmbVMeM}Q)7(cM$WFs(?A?V~d5I;sguI(MY(t_<>!EO{8)CrR)*Smurw0Grt*aC~2 zy6I%3Sz=(BE@C9QI2X)jm`)a%b&5tYfFDz0m`1ZnI(5dx23c@dq;7v0|Jdd3-uHR$ z`Td^n+fi0poDiQL4*)=dbrn%Atx1vRl{o30@#rZjEoq{qQmo)>MIR|JfQjZE3}|(e zbxb)!(#?(M7y|&zs%7n!Vx_G_Pw{RA8PQSr-5v=I0ER-phol-95p*zhEN6rs^z}m^ zOBHPE1*}C-6wU?JDp;*lNjEBowR!~AtMkB_3zDn} zbgjOeD4MY)Nk+&iiXOdE>GSy%K8=DG>Xa~sVG$0sS}tkGz0I6R`sJKAE6PAHUP@p+ zBFl4NgpqXcO`;K!Jbe~|+hemmC+55}iIP&L^phSXtWYW4?nqqI+Fr4o`PYpXYJ2U? z9!6Qtc=;xQlIl^L6$MMVd%2;AqU4Q!wZKY6AzcJdHMtp1v=T;0`l6s&TCav-l+c;c zA`^yNB*BcU&>}UiL)5rRU6hAJIbMW?VH_qfr87{vK-C(yvrlyc@{0f z2<%3NE)sY*IGtZT`vUuz$&A7#oX{aAO(bp;f@|^GT2)tGj+wI=6^|PGV9|EJNtM~Q2I(O^t#*uS_UGQ_kyRiM&++BJN4 z8}ESbw-G>9T=qeWGwq$5k7BsI`uzN3h5FGCY8S)m^*PIqV;6hb_o>1e+*^^hUWc#p!g1>?xxhyqxb0}0wRktF8e@yo9v}{65-<3Uk z_MpnK`QY-KcVtfV;RecdJZ48IaeHH5-@XkpX?;aj5O+M0nYtQ5A)hVY^ zQ$52&*8D8z@u|fVhO)zyzd5bbwdn*xj|AqfNGQP!GXCf((iS)~zFbVtW!GE{ewlqf zb7{x;wbAeAuRLfkOO;&>rZi-(>%DHC8Yuqz?fo@Z?*Fv@syT2zAtB+~y~n|ImKgqB zS5nWDCwsrC|2*+)&FNkHXs(jiu6nDc`atGAdAPN;F9iutoZnW=pP8jybfR_aY3+gT z+~M9%ckuTCe#5*A0}I8Dw1RJ$b%7 diff --git a/toxygen/smileys/default/26EA.png b/toxygen/smileys/default/26EA.png index 17727e0b3e3ba284b49f17b770b88e1ebe4f5e87..d606692d1d5ddf616984735419ac3b43435628fe 100644 GIT binary patch delta 1428 zcmZ`%dpOg382`>C*OV@9Co|=m@FNPLh{9ajkQL8yXSCHYjMC3#xtrPAsFde)dkTs1 zD3>VLZHE}?Oy-u{tF=1SNy=G&oywCf2KcDCQ{PBLCm!;aFLf3|60RSfETl}RU z7wh8V0zgBl%6hoGl%pa&us#5!8v&3>1z=4oWiA1b3<0nb27p5a0Gg!AR*WM62-FGh z0QZfJ4Q3FCGFOdG>XPVU8^rQ&>$V?%FM@oKHMQObpbv)e+JgB)pBAR7iqZi~lXJv)v`&BY4*!7J~{erQ% zw;wvWgMZB~7c^U>+0cV~zlL{jsUzCT|~%`wf&BE$r=<+ z!htm{UA&?3X{zsCkX_N-*;`m>%^!Q^r!8|rTNcpDKoOIODd#-t=^qiE(wC1mRV^tp zx?R`Q*xr4!uCbxLE7KV%ji-Oi@PjnQEo7|kuf%~KjGB8d7K@d~*5Bl;@kLSz5rIDL z2J+=~J)}f|?CImKu%Mu(Y$(@$7;giB44fW-BV)r;QKZCkF>!=w6giC)jiL}q0N9nV z0?`~1N>#MF!(N6>mFt&iHKGIA_7_h>a|w>VA0vXa>D1QGh|wYO;iX_lcNHFNG+AUE z?EN-;oW*R2PO51}M^1g8lPnF;gGT95<+VPH1Acq!%};t3J*1VKx-jNc6HYyIXXWdOHtLR=harHD3!#S z>}h{pc#u~}YAwx6&D$x?X^>4T(-A+TN{HJ*amcRE1;rE>+!4he;(bRDcU<*_E8GD0 z+%U<=gsTTG)!+bbu;<1Zb)}cNSeS&&{NCnGm1ht)&+Z_3se_Rsby#hpCf<1Q_0?NL z7D|5{62mFT>r0Ai2b@GL#S|fRxUd_3GjCB3E7*oFt9_mSc%>@vR(vv2$4+PylD~+b zFkzk?6O1QqcU-me5%}PbTLqf(wR|v?orC_jY6ToRdO$gy9+z)N(^^~OzuELn4c8bW zqhWm)T?|D+_hfD1VHIBr>Letq2X$)*V{jI`W5aZ~0{S-dxU$FVSs~M0F)~vdvZl4RsOb3qJcXtGZ7o3DzY`47w88KZ@6TIaS|FI$n{= zFP@*87lmpnO-|bcHKv&%M@mDp-tMTrriZ1i6q6`F(My_QMBz<_Fj+Qh3J@#u)&i`CQXejD$((jbL zjBSB8=bTuxyw{H%R?RTQ6Ijc8Ydx&-`k8(J)O1%tBPM#=zqy8F7-xrSx`3**9#LjV8( literal 1557 zcmbVMdr%a09KSS3Py$9J6O?TU5tG~7J#Jw)4!pa)gDVg5xRGnkJ1`YGiwTU!WaDxIqMI{Kit{MT9wrvVu7(@Qn%uV|@Q04W?&hqg~DyT(XB28HLU%6f=;Sqe~boLpyyH zN0|(TVx)_;*=4(BCg~73B^{7adO0^hD-;=7UN`M1V`Qk9DPegn{AX($46#lvT%fV2 zEN%l+%I5e*Cf}cHbNI^~gcHungfhG&AmA98hP+%kFOgm?Jj6?ab>J9*p&=2uObg#P z%5KSn41&l&8l^hLp+Zpz$CYTR2FKCq5T-&gLB^r)4BSc0GgK?FE3ECooUNIqVsy(zqu5M-bj$sw|CnH6{_z(^Mh zm9iEFmfm-Pb6YGAhwu2h?S9qO3?xG z;YtXCLF^u_C?E)|A!ms!ND5s}364sR;bk+Wg~5%|$vR04MR6)!kDGLa!3Z{bg9PP+_JtO_1YZSff#whU;``6s9Lo)QF|44CzF=!Ds>(MhaC2v1VS9Y2LvE^|CbKFAwxb6O)X$~r{Iy21L?W%EyalJJjQ7f1r8dD zFUdYcKY^(+9i=fC)D*BBMRkM;C-f*`qBLr?iZH^%Sm)!!3<1U_Am1qPN6 zN*`<MC=?@%W=d!Co>{$$%gLWTp=0ng_i0V*9`n|( za~(|IY~QV!~LhKej;p+zPR2u ze0x6r#BUwn+`JH(e@L0(S1oRDpJn^4FK^QCpC`91v}b0vJ=1%3&Xu+PSl8HH4KekR z>-R0*ef(_Hf{OULQ^vHm#H>QIu&*ngB>nQ$xrG~SCt~+6J-?t6UgDh}7rNw$qbqx- zZuq;XeK7Q1lc%MDpRuT8NA36@3+tGW6-O^$t%*j~$4{A7J>r!$^o?zG@<$g}VqNd; z%6{jyEiG%3M!t=z%!hkQW-b3N?k;7V&2Bwi5PAEUC2CySviaBCzS`a~yR%BF+9Uc} zE}XnK{hN*&>c|UU^7}%zhBfZwbiX!CmQUNha>hhuYG3kUMKc#ak4|tDE>D`*MAa9V zC;M}{JA3LczIr}%%bq`OBu?G<;>ge{^W~dKRh#!tTlrT^>-B40o!^$9Iz+3Zzqnx> znAm%z;#^o{V^3nwmcn(p3pzIbGovvqH#_;@!VmTR^}6cg+gx2eXQtnYDN>B;j~-j+ SbgT~isLjS)YNx*Ft$zWgghKKF diff --git a/toxygen/smileys/default/26F2.png b/toxygen/smileys/default/26F2.png index 720ad23862534ead6b97fc70121656b1579b7dc1..2a86834d046e8dfcb0410183d20b46feb5ebfa4b 100644 GIT binary patch delta 1556 zcmZ{keKgYx7{?#gJ&N>-8M@|Wt}x9@rj<~3S?2vElA1D-x<*Tp`k8VQ%ZwP&X-HBk zFSl~&VrEP8(q=0|%}Z$9;@+!Uw+^|xf8D?C^UwD@&pDs-{PF#s?~-=Y+I%#!5&*1l z>x0#@D$c{#1E7Jey%_zSddD2~4)O&^vjoVv1n^aT%1{BE#skb!0f;34D0+EI;4T1- zvH(g5Y2fv^bYvWom6GAH7ek{!m%G_+{di2+VAWa-7xf`y3tZR=RKLE#*FbfIBs8S!!gWKCltNEQ z?w}}7CbVfT#62kl778-;A!Rcl#yzh_;Pgh|7{LS6t|?WmZ03q!BBfO+oSuPSFi>O! zXLZH$VaPLtYsPp)P@X#=XrG+=_1!dcOazjfz(GTaF_fEh%M@_Y0BTJ@%mq;~Jm*4Z z5%iS++X%7^o=FFxz>w6Eci>?wSNt1C^noRqh7R#HgTO29+47 zPPo!s)6gdrDF&s=F~!6b@Dc>Q15maFcqUNe1=Td8%2S@a^dKH{Z$&y(c|n;8T;I~! zBLx;3@(dtHAFk>{E{ZRfw?NLJ+N5Z69bN46-jtb6Rtq78Y8A>Tm!n zm}w#5jG*W<7V`lozwjBhnpv#jGosfZ^5pp z3^YMsu4|p3?Kb~HUpa(3qe{?3-5b8YA&zc-+nYt$IG0`1ZRV~wsXb(7T|A%v#&oJS z?)&i1HJd-jKlT&oS@NeC3+Apr>ekzbg}PNEBB!F#Lps8!7!uKZS{T`p^8}p)shr}^ zHk(9E^N?+2(cvFvVLh;7&#aplbA!8HHZ`#+NJlSI7(B0CK{XFhu#Rtg&&jo|PYGDk zUs&~e_c60{o-u&>zRqWVvp<*pweEV|qa{fPKT4K$i8f;CI~)=hH(xdu@h<*=#{=)sjjLn$ zT6XkS!_}m!*-U~^tZIAj*1bAb@1c%OJ1Lo&zUGAZ+Wov!Phn(}myX?LJyi=uj`rQR zK~^C3kXWpjYm`_fJKu`&Nz?CFBKaele`noh>5NIl!uss!p`{0l3Prc1OWu3%GQSh)9S{$T!YIA{O1|{@P$pma@_FAIK`@0f)H5P~HkieK;fBFm zxp-1+Eu3t3nGLW_t%;V-cDqgO?M-*vId9#OV~X95b2WFg*}2ok(cIO`G0T*&t({kY zr}@^c<~#K~`48NRx~G+mcUx{he*B>2Zd*kItD-ids+wfpxV;h4@qR=g*fV$Ty;F!ip6Ixp=;UI9#}o1RpH@Bmx4QoW mPA100$EN;2U=o3vR|DvOKLjVna%_#Y0LUai&j)*`ng0M({I&1^ literal 1533 zcmbVMeM}Q)96m*-A}YFo0gbs`H7MF%@7f;pw1f8A3PKTTH|jLeqrK7_y$|oMw4xDZ zFeYpZBE~2hGdFa?*oH)m5w@`m(V6HZx=o`p&2;MKz_IBz=h$1MZhsj6*yZltk9(fy z_woLIw>3X+>6F;ySO|iqWM^5OV2qDE6UTyg-8Me~h9t%2R#pf;B}7XMWcCV044lo; z#f+1oz3bPVVwONqjE{A>6}Kao^az}Wj$kxlP6TWST9OeKX-^5Gz(q_k%bSpaop;T28Y%mQjDL?DwCKMWqB@7S&7H!MKWH5mxM_mz%dF9hq(YRlVKAwqDz8#WE(}` z5r|S^LLNKicI3m9ATh8(qf>jd7zPuB2Gbh|0$T**S`0_E;3m|Vo-`07o(7Lz2(Tu3 zeWcTBAGHNmCd98OB8j4*P)HNfX#}Yl#f(N{L<7gw0HK!G^9mhS^RgF#EJ?l z@Nh(tE)s%@2?3ry4uKOLjwiyrJenwwGBiw!D5lY(92bde1T8C0=3h6Sik4mLMFw>; zvJjL!;5>Y)C>iAL*@hxO;0?K4V!@%%0juB%atyCzTTKY~qVcj`Qmad+2s=ilQIy?= zW0={d$1DZ|fniiSVWjY=#?!b43x;dWc7siCqcF^dn=y-yvST*8IgQdAwT37+o0k=u z_b^erEU-Ju)jyL{IN6Kq)P5CUi#NK_u=pt<_?+;5yuqnp{}fAf z3shia`lR&1;z{!`Jm?q+v_|K-%ZDIntUB9jc7-oB)SoSR-aNaf|I8U*@PczET z@3uFkHn^tU|881*!mn^wQ&w9Fx<3USe{WGj%^NdK;h~!asTCQSY(Xvjh|XEo=ikn^ zy#5~77dX7@N>Z`s@6GAG1D(@^tD!l2m$zOopPDeXErs8H=U8Lv^m_lFSr`X(R(>uw zWON-Hu5b?fE^KQ*llY2Us_E}~F4=y1-MH#X?VZeEQq#e2+biF0U3Iaru;-ImZ9_A{ zdo~Vt<5*oqoCS%{# zP;*-H{i?YiUrRiAcgMAi)9$u+TGD6N72W=i%DU}x*%5(O-f5H&E@5fI0-IADE-I`w01R-bEk>Y#j zjvo4DKYLf)(d@lhy_23dkgn;sU0!JK?@^_w)|}`nAGB2eR@AEBh~yfYr@vH?Iq{xF z9;jdU4V_00&&oYdosN5xS6Y(*HCjPv0Jxx(zuDqNgX$8jVJd6cAly_)%7(L u>l#yazE*W7rtel?ul!LX+P`MZb5PSJ^~_ewksl&I^+U_rihlqx12;4P diff --git a/toxygen/smileys/default/26F3.png b/toxygen/smileys/default/26F3.png index 50e4a270cae32c26852ce63864c494bc62b12825..c51400b0c58d59709ae1f935db77f4ecc07f3b33 100644 GIT binary patch literal 1493 zcmZ`(X;70_6um(b2r4L4Swsvfs8uAP3W`ff1Wb@6gg`KACGdqs6A2-R5h{^=F~lSW zg+SRvVl7LcEQ5k50*Z)&wT7y=;f9vMwzzBhQ9JGQM`zAEckaD&=G?jO&C8+tZ=Pvl zV*&s(X;e=J8fQ%%<7p@_butS=gJBfej|`xdP5;2bqB$Xg%J2i&?+74C2Y82;Bu@bn zhyY_@0Pe>CECt1Ff$IS<8|l7GFTGwb41kJ$xSe6AM-UV*u4MKd0)0rxKX}%gJdlai zlgW?H3kQ?2ua+(QjEC*fSK~2{%*|1Zv~2H|!R^K%ly$XKly7L(p*&bYM0ucO5z44b z`Q?9srd{Pw-Sv0Z1>a;kAHgq zOI5Iuz2?JcaMT}JF%uh_Q7I;T%Vtl=#?GgriIIrr=VkQR$kfCE#~|UY01T4%GeboT zP9i}djE#)paS0-^fJ@lN7XYl0s)GG~zD1aEJ3iMAvxRcLH!gK}vVCRJhXIcR+k5$E zd&I7DDT}1I;sXct@{X6R8prnCGMiyb)>$!L`Qkx$7wc|Q&Wn$GPbbXQvE0}`(b^Nk zY$WJS$WG-Aox$|Hp2#FpuLUMpOZ7ZGu9&n7Zqejtj2J)8Lpt8yc}*X1SV)-n`@njU z+QRv@{L;D^Jh$5VnhFNdO$&%>y%s2Ib)?2!SmMme z_;^}_SJw(k?H7FD_0-nHUng|lv7%Jcn|aEb$W6oXvlrgd`7td6=&=L$@JkB?`Kiw`yRWZALTTE+gx`3 z;%tR$LvXfAptiM@pVhsp92sI^6Nhd+TygZ%dg{<5Eq<@M`oZOQFPB~05I6Q9jj1aM z%u*6qmu}AmX@7T=cI6pn3Uw)>6V!{{;IxZz=!) literal 1408 zcmbVMZA=?w9Pbc}*0C@dbu-&Ik8MF|d%bIWZEr25^x9EJk(I_)qZ`+|11`|J_U@oB zK@*kk-sqv7|XLOam-5Qi7MMxQ3UCG7Kau(IOX- zIe~}Lj8sTymTfRlx)ee<;`Oc&^U`vnK+2Ff6+uv~4he_TaV=U)vXA{&#;VqmKM`RO zA1evXA`R+MSCj#R+}+<%+7PHAo)I}v6sn07=;koX%O27OgD+Z!V+agIanf9lyB!v% z3oOc=I^2yp%|^_r!`voghGR9X(`?qEdQ4AZE)+#wn8jf*I5DHeXm%JZ7K0;$_3)BR z@id$9%K^X3SnR%7!YQ(pEQo$VXv%azb%P)aQiBkIoHaO99N>6Hh)GYT%d-?M$%T(50>mKc=J-#u zWVS#Drl%{d4;CwjhvmVE5y8e z<|C)2*?x=u3femTRsHwhzV-|8&h;C?+hc3DMu)rJEEpNq=GAVipM_^T&XIwmx(h$A z={%!K^qJRfyZUs=4(8N>7pgjP&yYFy+OjvZCwf%YoxK;S&dWuI;kn;N%d+}EJJx?I z7z+eE?Tv%K|JiJOObHMCu+VeycA@&t^o3E)I`vgc%Oi6`&uTZ^%ssfr_xE>M$6LD( zi6_KR>ipfQHZ>mD(t4%vd|NVFol>4XJ2!Hwug_Mw6-vHZ_hlJv&6z)zzp3Dhhf|}c zhxW~Nh$Rxb*Vi-uVz-WKzt+{=YcG4E!2NNcvuo|}yY`7&iH+N}uY6u*d;)Tc z8-3Rn#%gA+hjy7%f2FeSad9n76X9h60P+(4Kz08`2?{5$lC>g=nMdBXi78>Ko$vrg;)S+$>z?5Uy6Pop<};S4@o4EYS2#zZ9;8nC|SyM`kTr}o9{bEqZS1^<@^yKiw7kM09}RxVtoQi zI*U2C&Qo4dd2gwFgf18=qfFnhK^AZ?sXSy6vo@u_s4#ohx*MH|z%pw@YQ2%~fGBm4 zqTR?z61oO)&p_xYh{OhQ7b0$gG3Tf`m$+qWd`I8y3IVtTf!Zuk2Z4@4&@TgnB_KNh zC)$=@;6^ z8d$Rxo3B)zNbV>YL;Ep@{q}o@%&8YOq5%%_WifF|oc3m-T4>ZL1^~6ZypS*`C@#lT zn30x{oWeJSjtcpvxdI^oF4A+Mtcghy{z;~I2SXFPoL)8Z`0cND$JVv2Jg&{Q#mv$p z_Z1mw?i^Pvddx?zU!atAeUSzqO0jRQ4CUGl9UK+3M$=Z@Q`;wF3y+hx_+`l-HzvKS zQ+t+Rle{w8+$2uxWYC>#6vags>#H-jr#*#q9Q{M5UE(?>+%m=aF*Y zj$1|d&E9N@{#ry|Bjr`AF0Nxbx$JfqK4sl$sa7pmP1&03VH;#5ypvHU>r(D=3j?mb%K_v8qJ z>|&0WjzeQHPf~kb;cr)ZU9>gKmsdksrI_E8p6uLd9asjlwj>bV-0T4@toIfqVun+L zw_gOF>rwi^c^UJtq<0k_!5ch{=9oyO!4@Cla$Z0*z0KP4v+S~Zy~|$uRhWXv>MTik ziszx2?VC&?-8LoL;rX=2Y|^ca`MJ@L=9sU@r9Q))`4EN*ULf7MNGz(G!#9|TM5HEs zJCW=Ho5Rg7hfmaBr#z8A+mT2uQ87FpSdKmtTomv5YxwfsY_VLT6C_=Tr_p~Su4)x! ziSyX1grCvZDfjhegb>D~rGFTo+Dp)Y!(YBm)#z$!&K7VO`AAMWV>MWrs+=(a>Ne#` zlm4|lBp3%(+i497+i`iv7Xy~I>?N$J!)ditg0X#nuAVI4pl;mA(a`V?!LxJtOPiCL z>DB(pnA+8SRgUGlEu%^GR+W=RaW#9A>Ic=$2r|O#l-%2^BV*`2{s)CuXy;T1PyGv_ C%TVqB literal 1470 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 literal 1449 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 PF)(<#`njxgN@xNA$1WU0 literal 1423 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 UvhZXos61luboFyt=akR{071R~rvLx| literal 1258 zcmbVMU2NM_6gHV~r>a3kAl6hH^W3o?Y4LC3I3{W1+71mOZInhVyg;1zrZK61X1gS< z1ZCY+254xicw(9;h)D>cF)AS{K-y3WgNJQAj2C!d>W~lu3Bdp%R2bJyQ+TNMfMxss z_2Isg$hs|>J= zp-zB2P_^kJpMwN~Y^&;pqF2m5B2+AcP<)-tyb#5!^&VnH6POsbapBOLqg92x zoZj#SS4q6)dA2~3jYflLu!QAIkaRpA_c<6Q3@yU$wCSnMu;~tT7-Zm99NqSG%fx&} zwQNm!NgOI|hhW&*>?X14ZX^n(jBKhlNfQ)l7=B#q)~=Ta|H|0X+AU1mfXoBensO@e zJgNg7FwEWU9r=b(jWFiu@KDrA*{Vz#!1OY55{F+1P1gj9s)es$<~0~@<7 zR*)Q^dX`hLtjSIVAa9B4rtKlB>kEb223>^I_prswlz}E9C=v(-Y zvXK}iinOG(74Vdt7GcC3%~SEX0=2iX+W*NJ3B{0par~!PI$N*;{pn`u!^LLv026kM z16xCRxK%`uJNh%SSZID#)|RiN?k)w&W7sq3%B{@BtBZg3^@YyO#TT^V-#bs7?F$jd zvU_(>483+|ex+1J=f8ZR>*L50e@|;ScB~hD@`KmbPVG7V^-Jn|KZ^Ip_i0M~+9J1D zfAs0k7O(8PWZ(R5VE&5-f3KmbGfV03F^PI-S1Wk=xt^Z4bNd5&?-OTsz5dvZw-Vc0 zu>$OkoySqUEBEf;!s_Y4p4hebu|IYkePyT`AU7jbnt(8u;)K!Wi-SI__amr4a!Z@l<;dOrK|1AFf~ zKYjo?l$uEFXUUJA{r1z@o`b2iz=_bv#j&G&Xa(I)A!PY%_jQ~&>HmE)%BXy9xHR_< DXm^{- diff --git a/toxygen/smileys/default/2708.png b/toxygen/smileys/default/2708.png index 509fa7b206042a8a902dd4f34550ed0d85bafd0d..c053b6afb4a87b7dd4a1d2db8eedd382fa10c9b6 100644 GIT binary patch literal 1347 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>FK3#4-8>#p55lJ|Z%cjxJ`8;?6reLQ>T zDZ{GQbN63qJo5VJ^+%<9p3K{Sr8#$z9nd4{B|(0m!0ivFP$v z{lvh))KC=?Q4*9`u24{vpO%@Es!&o{kg8CTTfo3z(K|KNd(#{Po;$BaESoM~muZ*$ zpcfkX>_=wjPk}ZS{T*D=o?+g)H!qxjZ~r3NF4p>FtlO6iyPZGI>Q0%xd3O8xeJ4-t z>An5dY=+|e*x)3^WvfkM7q2|9?YGaZWX>FuPFM5b<2>z3H;(8yo8=Yt>v@+PcZ(N3 zqn`KkZTWqtAotFOj4STP6i=_Km>I5;Z$9B}QTx8Kg3aqzn?B;nbLl)%A^cGEqsdH` z6-_mYvt5oB@!pgT(zE@1V9)-!6}p?3@`%r1*?#$fdsUA{Lh_nbbzZ$Ir_7AwKbo;g zVXolU4`Lf0tySVv@Hcv1;d-cIy)}ap7v~jq$@^_wIX?lf>*^WcNbN!CxQ z2EXP#kniPn*)b{7_2RG2C_x{lO$*x9;wNlK(P`9Ragk|QIbpM+YV@Va7QY?l-9N#e zV99j(zvAJ@vh52kX0abP`uVUt@@0vtNlehNghKUqKR?}hv?SKF+=-hQn2{No;v7r&NS&VIZ7k@v5U+7o(%N^cA?o> zk#e*C%a1Zx#oh^B-gJrm*bS*j#m>zO|1$pADEQg*`f_2QQ&me`BT7;dOH!?pi&B9U zgOP!ek* qt)x7$D3!r6B|j-u!8128JvAsbF{QHbWGbj+WAJqKb6Mw<&;$TzUTrS` literal 1433 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYc8JQb68^ZK@<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3owACkt0MQwtYE0~aNz-V}1eOdseNeNf^+N-QuTUV}BEPM6*shdoYC9p$T+Zd+tJ;qR1A zm$TASmbxC&-6|p}XH(BClpnT*v>#n+wd|NPHC`Kjw*%(U5z%YJ^lY;N5c#Hmm@t#y69F;k|j0$*#A z?heDA|B|~}?lGBuR?5D|IE9ntk$i&ci;V(bLZYN|1RnnnSdgY$p4#&4asPg!4ujg~ zEtzb)8Elta^R(Z~wd5VcM#&53qYTd8eqWcidn=yHAA&PDo?dix(H6bSx1@!`YI-;oX%V=|tsV|%2Q@#_b_ zNsHi3)&&|lXP*4}^XphVBiqgU`jT<`D(AC^c(*U*NjPwiA!z!>Dn^-C`}cp}|M%#J zpQrod*H4P;Za(rnnRE8l8+#bInP-Q_NA@nOe>nTthkn8ExQJHY2@?~KN~hRqh{mxl vKUk0$(yD#`+Oh6UM*0PhoC_Hp8W zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0JKm{R7JiPQm&)3A5_aEsSuWB{vqCIM4 zpIrUnB}}r57Y}c~=_<1I$K%mOAa8Dzl+a?UW?Lhh!nS%#R$~rqEE=~K}ka-JYwkUYa}0Cyz^9TTAR#3sU7rro``=4P8HQmGumF399l-GO295#2Tz@tg z0$7f}u6T|GL6qd%LY71U6jjrW`;Vb(ssg5E+m8GAavj^YOz`|52%|Vj;wTIP--9&E z3shEhU6rWFvlN=PL;e5gQP(ywjA(i;CNvH}_Va5s_aspCdxNInxC5u14^fa9VNb6h zlmGw#C3HntbYx+4WjbSWWnpw>08TP6GBPbNH!U$VR536*Gc`IiH7hVMIxsK~)6SCs z001R)MObuXVRU6WZEs|0W_bWIFfuYNFgGnRG*mG#Ix{soG&L(QFgh?WevWo8ks&7v N002ovPDHLkV1oKvV?zJ{ literal 1332 zcmbVMZA{!`94~H6MCOJw3d@kDh#S$nzR_~69PF;`akp>`k5ewhFneu*2ClueWp|vJ z!zG}OZIa1M3^+Bx7~P?UEsHW0V+?`BOc$aN+}!qM^TT8=gXsrxJ_Qc_K=#3=?eo&# z@ArRw`f7b$&BolVxex?x4EXsF7&qHb&Ia%uokyqu+ImZ*SNE(BOJ z`&Mj$l^bcZtT>CJiA2JgAf39=isB5z*ct@k00@WKrCDOqp_wHa1zs^FLycRiuEDmV z7}Yy1Hv&9e4j~o~23LhOb0twAWoS~2qqq}8V=+6fCA4XUlz-h=6K#gO;tCp4Ouf^P zKs{PYGGvguyBo5Bz#DeAp@O1_9lS1e#uUv8@NNWramuR95;#utE-&r#Fq{AuUJj#u z1m~g%4kLUOR7PVhu9xs(9Lbk^NQT03fnZ2M@G=zU<$Nvy^Oa|~0nM~TO;R#;RbY38 zOWu{sa)u&Wx)Ij(j!XyCx9OH{w&`)0YoOsBjjAT=3A4;D&vLZ9VyOER*=Oi6cqzZE zx`us*_R_eA<6V@8wBzfuo|aAFK_G`@Rt_A&%z$pPLIPRB$s<>?pJ`Tra& zkdE5m<6GKpAKdv%*73Uc^Ve^^`1fV=k^F0iKf7?DrZ+v*-M4u*lDB8VU%KU|pGUl9 zn-*@}zx`aH%`7TOM~j<2biXz+nI7)HG#MrK*Sz=1Z}U`vG+} diff --git a/toxygen/smileys/default/270A.png b/toxygen/smileys/default/270A.png index 43d7ca87b7f52bfd03dab210c79ed2f64c68e043..51eefbb9ca5c7a0f8a0b7773cc8607f7cda45cb4 100644 GIT binary patch delta 1695 zcmZuvdo9)!$>MWh{woKyHctzWUv36Um z5F$c_dQ^&f6cLYZTZVXqRV0#lq~eiLOmzs6g#Ddy&YrX9>^YxvKlk4Az5jf^x5TjD zkYYyC1OS}1Ho5~Xt#BtdCjgpq^i_e{kVXV!J>38xA^{+$1F#EOWElYQC;(Oh0Km`z zI1y9Q@n;7BG$bCkyj*80Q}_4xhtmD|^f+iB0*!z{n5`m@^vLpVXjLpbt}00^PX0rF|F|CI+sz(^F8KRVze zj{_&DQR8{|@jM)#kpP)n|89`KZ`u*GP0|UfnJ(2#_pXq$HBv8s->jHwSCeMC)w8ep zMe!DF$mK6_3QFNn3+8@*&m)Eml6rT5if5%?g>w zkK)r}_yy5|;)I3zCsTC7WMQ1Jh#)ME7nUT9WCnliF5VHacSRl2#w-!@!E9BUg!Ol6 zL-tHnnzSiry^mD=OMlI$ff~gWTQ%La(3G>ytyb{rWUnY=l)p|E#tNS%ul1B~jWure zSF4~mQ|-#}=3QRxR$rC0DOXUEywXvmoN8ZxU7~#7xWlW3uDUt;a=oWqTK`zdZ&lBB zEBLJ|opcE+edAr3QqZOncW!c@3rmTj@`oF5%Sei`1{LHAIlCf`Qq-~0`%KJCS!m2& z>ni^Ambu(YU11l@R*)wPVyA!m-Qu}V-B1xO5Ik=~opD~2ejBvXySz5Pj1ArVX6{7fRTw_z68&}L_|znbXWu~6hTOi2}LB~V*s$vF7S5Bn?~t&eKbi< zGtgaFPU_=ZdtX^n{M={z>bo4Y(AS%0b=1(1ySP32{h|&ts#|vN`ew~l&Mwc7?sX#i z_g9tGwcN{5Z1K^zd#j|rr(cM0laW7o-H|0QGq-$UI>miKezyGvKfbU(_3Rp&Ma+45 zlhE(<{oy65*CXW&+hb7gm?T?KcCuqNt&m9k9C#=NUD_x(%&HfQB9CFv=IoY%Y70vh z3v$1$l=s5OtDC- zXM8=|4gRJCb!}2+GA49vj3#Q?JRVN-qE%AcN%9N_65=FzOV@wT$gHexJ!8T2%jXw% zJ|x~7`uR^a?UjGDh&_|b!I}WB!(Vi~6>Cxd^4G?P_)Bcklc;5@NAOmFB z7e0sV1A><&c6JN%OCPj1c6PM2va{T7@oo)Ur#r&l(R`5n%WVnrwj=*EM`)Z0|H1FX zT|YG151nX4AwMr<=QJ=$jsYGq*t1v!$;!dp>Y@b($rqa2d}n+XL8H*~SDASXT4`?4 z=_AGzZo<&O08gKL{%p&+JS{z}Nnt*XR&dwL$6Gvs)zl7&w8i6AKd$J*;dn-fnW^a_ z!|yM6oWDq9i1)`Vn3koF(7Z)QON literal 1692 zcmbVNX;2eq7!F_+v4{u?^;pg6@N zDqt&0hk`n4>y1$dnNAgH)sZrSgH__ilxh_#9@W9lpxq$Y{&4)!o!$M8eV*q%zW1G7 zwH z#^QntSR#WFV6y1+lwO@CHsDq!>ccP{Ry)mRFcOm-cGQrEQ9v%X(qfZTKz~?g|E|1TL;sKZq!7Mg?^AjMhn8z2x0$}t4X={Yh zB-SXDqqgXk1T<5WUCd$?6%{dyI82;a$$~^8kxv7L6KF&NS!||ZzDiY9f% zc8sOLNW73R(C1;AJ4U8+_ijTzAnlEKF=3$(1u41iVOfP4l>}}DM)E7Ryv07uks@rE3yG8>IRevh%N0VAR3=1}JfQ+&E5K2%@&DwE zMLWat6~}*yWo(PCK;QID>C=lh&4bzKjv?sQ_-n&(D}&+Rp;Ab7j$TT;C@(A{Qc^Tu z@YHPDvBz?xxGq~4zQLT3-ELdB$J*w|$=~wb5nG&KJ4!+D zU`26Y1L9m8FLx_z)4ewefqNO-B2$5B4Y5>8k5p6B4GUT+A;^@#OeB< zd9G>Rd6c4qs`-_HGR&TRj04!UV4uir=HUl`&Hz=y1>}#vP<9p+ONTJ@6kh zd%wKXwwIGrzNsghOf+BB2hxuKg>0fytZk9+NArv8zj2c7=pylZLY>9dzT&*F!w zPHI=JZ}~U^Pnr7NKsIRYcmeg6v;lCC%;TT$^3M{<0% diff --git a/toxygen/smileys/default/270B.png b/toxygen/smileys/default/270B.png index 984f829b756265b41e4c8b56906823c3adbc71f2..3f03100cef90d1f6cf766714a69caaf0688fc8dd 100644 GIT binary patch delta 1472 zcmZ{iXH?Ty7{+hH2*War1zMF7WvB>-B}zd=0YR1~;5u*&U;+UpA%tlmj1&rj>?u_? zf(RrK4a=ekC?E)71(ChM!BWVO{(J1{x1Mv)^FF`lJ`of!a(L}`Jm7{n^>Rv0?~Lev2Wrvt!)qVNR({Lug`;Q%no0zi?RTYu3Q0N8~K zwvHB^bZ6)cW{@ErD~;r&+~TDAOjJaF&%0w?dy|OcWl;m^Bq$lmq(DlWs*LMPB90Y= zO+1W(n0qgvFOm2r1_$|`cs!*4m;}Oq9>V@K(onX4Uy}Ft?M6@K)`Qy`=BpB3PvufW_Hxs`&n@}eBVeMm7rT>08S*N zJK1F9qIZoh?>QOSJG3vx=d<=FeD-vyYW;1c9BcKh2-6fNjP>yi{`@gsvcr%)B~#uc z$3Z*HPkMH4Wp1*D#MC8C2Q>UZsOYJzb3>Ip(swB=JN=YzJK`>RQ$NcH?z z+R?MeeJF|AHq>^@>&Pw*pOar3NT|bw=HZ4*2Zc#^#m4^5R9U?%4YM+#A0`Mcm%U#u zj{b3N_p}}T@)qaD?mVff))Cbs8SWMD869dK$_rnskX6M`(x%|)69HZViNr}UYP{2c zL%vQrx@9@v(@ue|SD-uwzt))E4x6!_quo5_pfv7}2tS945U3vPl~<6%eB(;9!yQrb zD}Fi#Q7BL2*;Dcodtq*zM}H2xYp38`l9vo0NR#fjU8_EsWUNnu^0~r?WC%3 z*rKQihLq7X>6B+HOCqG%V-Z>Djg8n`QM+JMI2Pq$c0Qv>!}m+HF-_&KaKGO`$EyuOk z*d~*Y6y=^f^h#1r%}AK$vrFS)V$6$i+F@21+%j^|PUhQR{M6#eEZV@{Cg^{%tPBVkMxlYO_U+mRk_^!s|^j;#Jk0Mi3_G2+>D$(alikD$#mL+NW=BF)> zYyN6ttZ~_1nNSs3fVt0%EB`r51egj{6csAQJRB!x0TFaGtWYRYEN0pU#?;u8lnCCB-WX)T@UYA{<;9{$4zd!b(7VM#4zl>z zBF@IK?&U9?;Ib8p1P^a_%1-Ni$|d?#Y|KU#^7#Q;p~#*S^0TgDPKFA(B3{tJJ&i|T zh<0#rx`5Ajl99@|#K z(2$8-tb>Q0a#|NcMp5D*LZwl<5EO+-Qib9KNurq$hM<@l0XL~caf%=*Y&JAU&Ombkm&E&J zUKF4hBU>nz$~qW$I(!75-)g->ECfaZ1wy6{v3@nGLexHA>|8_E0olg=>&9KJ1NP;9 zPHp1?VyWZ;`S47OgF)=>Efg~Z-cSo99wdb=p+#4zj}v4It%JcAm78}{45~#LOv9iA zgE1(IW|=TeR+a`a5VV27a3;=iH!Nzxjkpm*2n5eU0gIMIOf-WbfK8969_S8ZQPVxKlu_bXS(NOeSQ4**g176V&85fh#Ob0Kx#b97oEIq@gr8$W& zu(z5UyY?Oj*I7QEre zi!_?EseIG~ThjW+#bx2t=uQZR!w(&d);M%YQ;!B-JGNn-SZ+?U5&5|@m0H|%V}E3@ zqolC@=c8p?xK}Q17d?k(cXhWKiXiJoa{D%vns zsa@6c^@+cO-yiv9Lk(nj|EWu%_Vdk4GcxSfbJ6LvbL0FSPgHT$JHDi<=0?WWeiW^b zv=m{_^*Cz0uP>`c&yJ~RO5)Mm7sf^-#%)J0zE?(bAX1A>EN!)pV#*&1N(VP1Yd|<{tPpx%CLcLdNTZMrc ze(Mi!R`2<4@2JlH>t69(gT*nv?X!zp$VB zcl4VQtMZesL7PvWR???F-0kYm?IZPD#^giCCwS_$liuB03qMvcJ;kyTE=W6V%sDgX ztwjUQu;Kcy!AfuGv+60W%H;XTl8VZ!sjgX-Yx9oHUFl0{mH0^SuZ_k9+q$0F^XE;f z^ru~(@rI4Z!q4w1n{xAH^5G?CUug97VwVmsy6=(w#m$b~Cnu~b5tiEQ-AU^r@>j<{ kZWS^I(%b&(AHOOYQ5dmM?24g>A3zJ9hH58Qp0~!TA$p8QV diff --git a/toxygen/smileys/default/270C.png b/toxygen/smileys/default/270C.png index 7fe482fbb1810e2fc57093f7440cf6dbc06b3287..437fc211f97f07f97ee2d1a7e345383caf640b80 100644 GIT binary patch delta 1566 zcmZuv2{hDe6#r(jJd;v+4AC={Eu}ZM)KoM@^q4|aD8{~qn35JVM$AlT#KWW^yNo5G z#+F2rZ7|s;`yLTlse-|zc=_uPB#S1n#A9xY4U002-`mpBV0 zAvY`z3qTQlCVOfpfk?5H|6FJv6~s##R%w53WR81>Mde3#hw@C(n51=jdX^j!JJSi z4rG#`IK&F;O%58#qd?~$Xr|)d(%q*j55BLJsrRJ@LpuKfVIV!27vaQD@Q1Fr z;dT((qCI{-YrF5+5$gjLX^*}9L!2SjqzKxdeyn0nRXkYgd^X>Zy~t%PH0CTev6j1@ zPrrD$!e`ICdNlJgY55&{siQ>DR`|KKV74Z;BmT-{X&f}!7v7WA-ino;^1;XC&V&G{ zv+-j3Xi?P26PlpCc!C}KmhRD)awR(b-|c1-Lx*#z1DTChq)7elL{g&ihXoQ8I5oM(DG+hk%AfGm6jXN5Z{ZR#y4p650jrDo`Dm zqlu9LpbGCWhKMNP6wXlez366y(k9L*FB$+i>4xP6f`vmM%Fo}|)x+BbMF{e9L0!M> z2f)FoI4kq`UX=J6f6on}ePUt?b+l&t!D%=8pe&Q<{Nf(FKO7l~bw9Z~WL(f{zYzNJ zJ?^8=QQLS%&4{$lB@_3KdWKW6)zxWsZ+@<0@a73qTyB-kyiSCiw=H)>J>m5m&$6X+ zM|riWvIlZ(*tm%P;_evrLXC4|PDa?LD|GcJuj4|6^OIu^O;vN63Z{B}_}Mo*zW1bN z9 z3rRSuK>AsCHK+T-&j=-s8*4rs3NBa5BgjQc*2gw`|E_#@qI?6%+-8@)rJ`I79M0D- z2ErK#8jnG-(E63k2{@`8fY(% zpiFCdiB81ZS-YuV#~N4pE!H&J5WfAx{>!h^9mp2)-*cL*@avmBDRF!IvYRM+5n5i0 z!R|vtvjhig>*uDit+VM*1-QA7quT0rMrkUaJr4}1)j7TH@{1{S+_Z@^?P+CpaS~0O z;mvW!;L8XGXWwVB^RY^I^F}_&;10`iTaP&_05THofj9CAPs4zwW3JfkCYq`jZ=Xok zkG8K)2@r~3KOv%;Te0g8eD-^yF->i3A@e-h`nZZWR*R zAs;3amc%r~!_%(qcO2RFwIBu!^Vy>t=pk`N)|yIUR+7{56JpqO%?$>}IQ&+96k7G2 zjg;D!5vlFczipM3$`_HM(xM{H7@Pia+MI%L&&bMjb9U`^^ExhE;_n}@xUew|@hlaN zG30QRGF`^)%FQU#R+iF4B=Wo!f3mjH(ahZT{2w9WNsi}_ERoBqA)IejjbTB$Pd(mhN8eJRIvgu!j%f8MY eu0j865c|jRRPkET{hcZZ08Sa6HYhlbC;tV9l>q1f literal 1600 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYa<`tJD<|U`X?9Bw)3(;%lhF7nZb5UwyNq$jCetr%t1q5W|m*f{` zV)!5ir392`RoG{Y|Iz}IqIFJ$xObD0)K}>kk2Xf${x6JFiS8I1yX#Rq?d~3x;{BIhH<(`q|vGwem zzgOp&aInjxZzuX3zP!-xihp?6Hn@IcuCRseyglX`iZ^b*S1wP|w~_NvQLfQoGCzLu z_l>J3+5f9*Jm0i(R+!{FE|v$6c5t_NTE@KlEa%v=GBn^+`f~oz3meun%P zUh(eN_azPQRi8Zjd%u5l^#&iJWlq@pW7qlbL&(T|@Pzy!`XG zj&rTfuFRfof!AKzEYA7+>E|Ka52fyjSI->37r>FWmCJN?rMs}fEwu+rKlqenxOnK- zxLH^>yggc35X5nC-@h5QdAhURzLp!7E_k?>Pu;U=ItS0;MH@D?BrK9Y+8b&jUFyQt zw>V7m=7yJjoL790SEO&7H^lR_6`7a_rKEJh9ubzR= z%i*p|Nd?QYg@+yQThEqWaiVp*tmuWB^iSH}lV|D|uSmIMAR@W+jF?W<)SXY)-}tv} zgIDj~`y1J}2|h^6tNhqJ>E^DZo6adNUSyPaZ~r3gPuW&=F4HyE{;+4_VK86w`{l29 R**igHx~Hq3%Q~loCIG$KTAly^ diff --git a/toxygen/smileys/default/270F.png b/toxygen/smileys/default/270F.png index a86cf25c17863360eec8bd0dbb3cb5462e8e6cea..2aa3ee04112ff96b92076203677e79305a9c4fea 100644 GIT binary patch literal 1528 zcmZ`(2{7AP7=Lk=N@$f8MdNH#5qfCVDk>$8D56ViNh+aQ1RGUW2_l-HZo<+!$5G;{ zds!`t9@%kKu~pNc)wQ)KRaf?J%yegVX5YN`UGMw8_xs+(x`elaODakN0N^-lODBlS z?2diH5Jl@sJ3~yw$K2i=fV$-UTO?6PM|oL0*#i)+1pu7^zy@TZe*ho^1HeZj0LB~u zz6;E2b2I?}W`D_!fE6qS3SJp+_8JLVfj}UD{9oku?e(y@6i7(TDUOb3msfL3GH$-D zIu5x(MunC z@_%a+EU?G{Ll1DTtGs49qWJB%|KqUdX<;bWlw|cZ2mkaJ$LFQqb6qi`)i=H+*%QP> zgnS*fa(}jjuuXSPtnnAQMU8LUUM~*MKB{Ob;x_lb7<}LTrncbG6W*&QgX0aI{e@-a z6`9<;@><>>{HcYN;+h72i#dcmC9kBisk6MH<=t4AV9`%7pa2y(UUnYZD5$#iI;3Dg zQP3+Tm~j(yg0EaDK?hjqWd;$Qq1`(xy`2sL!qS}ZyN3fcvBoHsruFmNE+16s)2I!z zZ72TW@rfDjd6$j6g^xloArVoPF8;>K)Hz9n%t2W>l`F-cSJzX7#U-Q^l&HUZrjO`0T7A`C%Dp_NTH~}V5+yT9|c7V3#6cK z`v(GWCMnb9YFZvfYHLh8Sfse@EFyNvvvN6yUqQSfgZk@r5Gw};;t%($v|dP7cCWh8 zV3GHDRYA>Q%CbfEzFInPNzo^#m%DORud2=#`h2>*=x{Wk+YiayZx{1?cFrd}5l!@} zs*J=X`JzvBD6-r8V`$-t3C(mls`@*ytnt&7l}4h^BuvK9y1;b6nouIj7>T+kq*_2g zi)@;3av#ea_XvNmkFg5skfZp8R(V|Q>5L8CIate`4A$SU`VQ56HpfN_gfB6>*D5Cl zS{YZ?#^Yq#R(2X!7JAHrYvDlc)JNY9B+t3#MY==nsmz6I!3!`IQ zhT|BNN$(umW##-1YK)I^nRdIxUufXmTcNAk?#TkfL!&s=_ppTxEk7mwiEhGP>4QDj%Qno{xet@I4b}a0rtK zV!o~$KL6KsPjtZvadk~zMX@ZqI>&W6Fhg0S;2Fo5=*!ep7&3S`(v|vgdOC6IW&MNA zq|V8}?8u*a!Jlf(Exx4B-Dq;?y|A&Slak~VvT~umif0}FB}!r&*7qYj!@#zJq>H9kcbY^s6Y8|U0r!}rLvk@-5|6yfrH8Z~> zHnpNSy}?!wptFvi%Ma203_;eUVO?ot5{+W)6-SBy^4NejaFvcfN8J|3* zgTWYMFw(=k-S_=(fIpS&>mBz00g?A^=|cg?-4&dv-n39sFa?;Csn;kdoIlBj;zS{l U!-57V(02>~4vV+sS`cIY0qJd@egFUf literal 1581 zcmbVMeNfY87>~*vdV>=cHegB&#|Z~*nl=fvw6e5mp+K=-W z6Tc2W(DOhCo&($|9;fHVm~(ytZn!yR!fuM9C~h~|9h1Us%%uoye|Y|}T$1;F-aNnG z^PLK7MsmRHu-O0r1ejCEENPwNd;Dig?}(x2l(dA3#%ysJpDVg4fdL4b&tX6_N98eD z3`Li&?_jh5Fe8_>WsBLCbd81hTXa zT8UW{7N>#9XH&`qW?5N=&0bb$SJP0^5>V^WNCX@sQlN(`a=0`e9W>6Xk@mi07y`#t z#6lhP!l`VF6*TYy17dQO%&tHX5Xa>Ris3jC2Pzea5>`kzE<;cahHI4Z;KT(<)&x3N zlSP^)Y)O(1$`?ha28P{kx7@9g^FkhssMTs8hf*n%G-R$)he&y34p-zP1If7T0_zl6 z-U0d;sT{so)IpM`FGAp)7RwZ|!!?m8DP^#Sa>9sQ0dt%$u5oRbn8p0-#!Izbwo)ep zXE83mSg=d=$c>x?OSyZsA)lh;jb^F9N=2cHNZww|F%Hp8>LBThoMvf_5L64gV zwZSL}dV>NtDGdpj(x6b9;;~7NmtplLLJ_Y-P(r1~5X7iVKroavDKIronoJ4U6xQr; ziIl_6Oxk57yAxQ|E3q1bz)&JD*m%BZvIDI7yvV!qc_(OChJ(>7SqII#T`|7$yoi=$ z1h#~sO#;t>r|54LccTm$7xx1zVS7rp?x4A?qJp{UqFd$NbyTz6|H0FM|B`r0y7wNa3ndyPzLy zQGN9zq6>xhp{JhgbQjzr!}>NwcrWU_t-WgR+SJswZGD%cf;Z-EAubNs===9aMjNJ~ z3zARPrd59&-@WUjlT9Ih-QA5X7Y4$s7yd$wSh#7Wr<;53^;wF4{}u*2ZzPnQSySFk zd9}mh8@K0X&RKoJRMB(A+sG#Y3od~nyCT;`e}CsuPpf}f@1B~jg8ueE$8}lx*E3@` z-ej8_I<}L_%^NP2qlM>o(d9EQjE;5HD6a0~{vKH|9M)+~Z?=-r_mU4j2riOoD`x>M zXO27!xS#9{n}2v?)A1iJWDm}JexOoaofdVus(ce_&{Mxl12g zbNKTcs?nog4TP7(Jlz`CvFhRDdLVNI4ocndy5N8JcKe3am1Cv;fmK(g7k~EW!EJGm zs{Q7!>%WERBZ?Z%hNnGm3G5?pmO$zCV_V>@&z>!)$^J00H0H!P9h6uX`}B`fDZSUB zH-X)kyg!FB3U`nB+VS~~F?%bfkD_>J15t_22-c0nUn`R)>)72+;z~mh)3&H02W<|T z8)a6;o?SkYxL><}VaW3Op-y*rgGIYgwzxI$Q?f8Jul@0=6&$S|E5L&Xs*Ey#=)WHC(&<{aMd^KX9`#gKW~jxA`B(B2H}p diff --git a/toxygen/smileys/default/2712.png b/toxygen/smileys/default/2712.png index cc6c6ab2e1efca89e69b962dc12c4870acb64bdd..97c107202c8929f179ddd6fa7baff592c066262e 100644 GIT binary patch literal 1517 zcmZ`(c{J2p9KVBU#>kc|SqhCM>x?mxSDxKW8SCiqP%{`?S<=t5)O#~J_EO|Ii3%mz zPctH=q)9SNSq9Oll#-&*E7|7F`*r3Y?~ixSz2EQW^F80sz2Cc>o9pfAE|1Z~00894 zBo|-UEBzd3Y1kT@EBe7sCeo4O2mt$>+&UczbKNkKF9m=!LjYJA0Ib3x)(ikiH~>D; z0I(|oKt18oU7x)GAc)=`R9CTB4ClO=nf18FuN?jQWKO&y5=%hu@VEqki-Qv2)%@n^ z7cp!FGY=&C8()|S~5Axq1b3ss&ZO)S~`$;M`AXgGNO`an#2yOCiuOf6$EM({yVK2lS;gM&8 z&*=^6m(?{a7|m8W_;<;TS5;>o?@=Q$=1xOr)`Ue3&avC`2vn!9?+YVSq%14^y7=o` zN~_qsg&2)14Fv8-{GS};t#GaFr~;$dlL}rk^GPU{du951L17JA4m}}$Dc8oL>M6gC z#f@|-Ssz-nH&7at?i#AUJa*iIbhDB4J-c5z;lzzybOh%QS6fwk#wL)4=T5HnzTmZG zsg90k7C@1r{_eos^vMcAv<1|)vq|>4?Of4?_RfEFh84=bBpfy@U*~r>wA_!T(eR}M z#%4j)ywdTSIgyf?aK`YUO0UmLk9t`*8|<~+^6tX;Tj&LM~Q-Er)Y z-jrcj61}YMPn^6Oh&M7B)OubMMoeQoGA$reoSEh60*44klLJHK;1!3s_;)HducQma zdIAkCZ61ENCw<|idWhit{+_m~Rqr8u-#s00&an8mrF+NJDaN0pu&gQ-HUl~@ttiJ2 zJikKaHH$M(LDN9bP9R@rBopajmTEg>Pp8Cf-4i!})thSKNS)C) z*WUgRVj)T@tXeCZu4{drm;G(ZuOjH}=u=cJ?w6g~;|~lzstYvpN^X5hnS)}=la#(I3N@em#R?afo0U3bQ)9-NN zdMhg^7vz*PSv49|mtd}tfHNL@?C*;)(bw6csit7%?xtp<*Z^TSo=C7E5b&eTIbI}> z(1GS#R`}*tc1v?|0<_%<43)kcn#`(g(prSpQaWWgA076iq>o$jSB4Zvth7UyD&Bxe=;$-d^6JMups4PY5_J`*m%O&~!zQ24wpYJ>6 zCgVK*h`13v9*>`@N6p+C6?ldZ!VW&%>NL@E+x@pyzDnUJC#A_)XR0S>7&nbSyS3Q5-LO(vNsK?W3OFq&|& z1VsV?Mr#gLz^VkCr_V!hx(tRF#3VDAC@y6puhk`z2*o0&GZ5E+Hp80nf8BVgHe)Gt z;UY86Pz5x`)x$m|2jlA=A#r=;KWHb6;%niFG3q(gQXG)h$=Op zP7NViPS7A?P$xx{3MnF%>QWRzj+bE(L=1@)GPzm?DI^lD6p}$GqSK)eqLIqgT3ryE zN;0gK#PFb9g0nk_mAw)RBQ$PhDcV9&`N0k_LV#5p4h zG{=9MC3wVjU|{>A^*Q0i@ZcmjF*G+C*xIpGJYGm)Dyp`4+t>IP>n#~EtNIyT{Fm5{ z+QgQ)KSPpgZz9AlwSL`q4{oN7)B1-M?F)-IR+uq;^|nzyvTEcfC|G$T%^Hn?U7_i} z)%>XZbklf|0R%4}%yz_f>8p|-KYFL9?qSQ(%N>B=6uq`_Z(7{7hBt3OQangj^=~3n$+WMW)BrEYys` z!ih=Q8F=EQw#tvn8n!MZ9p><;E_^}A?67_2R{ zQt{cZJsYmIcciNuTR*s%8#yh#{=n#zx034lut}NM%;f8i7Iwv`*_>kCnE~h z)lazCd+wg7{{DMERNU*?f9mR{&^70)TW!x8So@*Au6dQk)lBWl2edN$4@H`B?7Q{X zw5MafS`ojy^Xu#o@pvm z6d0z(#k>8@%YnM>WhMP#fm`*7;5+O=!iUC-zxi+?`iB|2^)X_-Tx4Hxqg Wpdwc+wfk7$=boxHqF-p1`~Cq}v|C#M diff --git a/toxygen/smileys/default/2714.png b/toxygen/smileys/default/2714.png index b6753966f5f3dba453469e261e7f5d18b2195f7b..df3540fb9f19edd76cae3ab246b443d176ce1855 100644 GIT binary patch literal 1400 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>)L;FdF z`lAeG2N<$=d1msn;p+~^4a^Xitq18+CIOo z(thbrAY--Zl@R3L}F#M9T6{TVwSmnvJ3A*Tcb15;&HNJL3cV!1*=QGQxx zPO3slWkIS!MQ#BDgGKMuP|t053^?u-cgZ-iv3x4Lqnkbb`=&D=gBQrjY@hsROI$UB zi=l|o-#`DF_Z?lIsl4;_hi6B{gm*`lK26HGdME7mQseiJ%Xagood~}?A!Pch*sd~t ztqr-`CYjxrkzV~{S#{`PSw7bdosn&CVxGM7HOp`7y~cb-Oz&e}`R!OI_r`{dD{e;= zZ-?wTV?1fQ^ohKU&hgeet}NRt_J|{|qHD<~y+g7eMIu>NG}S0ZyDU9soKo&#`SlF* z_e#@8^U|WF@(c>~mLKi^A`y6C*``&uG)mWnMb*_@sNN*Ybh;%}e3^Y)#H&-pzu|4Znhxj(*HoUXLt7usc) zc`og9NR6kx9qXpx6?%3arn@G;{P1Vm1^s_gU8^Q(8ifMW#}J9jQ;!}MY6@Ur zz2JRf@tb$=-tqBV`u|^UzQv&)>+{bT>N#T%F+A?hh?>Iqx;yqUr`R0znY)%+^6gPC zKL4yju5o?M!@wnu8ZWCN7id*&bz@#B?ba-C$)rcD*CD6eXS4RVU(XUWf+j4PnA)oz ze0r8={)DGL4{e*SU9G!&fySw{*B0mU*6&yl)c*K$NtnIBpN~wR{e|vwWTiU+9jIF3 z8c~vxSdwa$T$Bo=7>o>zjC2jmbq!5I3=OP|O|47~wG9lc3=GW8Udf?o$jwj5OsmAL z!7V#z5m199$cEtjw370~qErUQl>DSr1<%~X^wgl##FWaylc}KMkipZ{&t;ucLK6Ts C>Mn=? literal 1262 zcmbVMO>Em#9CunN^kdy}LDg2&KD+~kk@zEtlUU6fCwAJ1rL)qo<;I-&CG{HnxwcEv zf@oTV*Z=|Bp_;a-d>lYrfWZ!vDwPdoLJW}Dg)a#S1&}x(pdg_F6XUsU3WsV3EZgt> zpZ~w#|J$eKiLt((13d&m^kv4y9A16y)8)nQq2F#Rc-ikr1!oeKotk1nLQqi=f{d~B+%42 zc|4q@vt|lTY2))2oSdJ?m*%HSF_j!W3`XiaCeWdyfVy5aY`z{RH+gxycaJF&Y^pfZ zadOM4LRJPTWI+%P1pOtNVF1Skm{6GGm13)F%EWK}387K^zYEbGS_etXVvl)B%r54IUZXqPO_bTniDmr*IA zStm|nPq#wQ&1`mu*s!+~g;Pe=6_a8DG^OiqT$|dqlY{@dv8%S7pEDtqgEpGAO1K{7 zgKaR*-JK1&ir5?esHNeeC{+=aW_4&d88J@cF9B6kd6r=~F`DGkLM$a=A(^7NG@FWs z*%ZyDBjGm3ZdfVGvTQUQkwQ|0VI(%ng`xr-rNhBUFc#s2Ha26}j$)Kx+pdP~ZezJS zV)>K>6$e>)gsSZhkf)G?>?ve|)FcOnk86gCYWDqZdA6bzp`|?v)wG3lu$f<8+tt1l z5n`dB6cIyi+-?|>z%nUGjMB*@_I(Sh{-2yt*cr-gj{h`E`v`ZSyWMGhEbI&qH1Nb& zcr>nDsD4Kfp23VLs!6|jTvuoX!tSGH#){O zZcc~K9a}u`zUS^2ye*jccu%v^_<}g~2yuz`EhJ~Ijh*WMiI^GiMTF~>MBn8nPkI-7 zoKH^l{}5=r`(14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>EvML?9E~-GRCw>VYbOwlVB^ zzZYn`d`XaBFaskmI2P6T4!Tfe?*0hKZ)dAqwXbg;^L z06FX>p1!W^&)E66R9W`eXP7WBFx6FsM3e+2mMat#<)>xlq$-qD7NjavTmvwydqDt2AqNpFdiq)7a{pym9T! z$DIwUB2*SRo3E_uU*}zN+$o;(+2oLYzwd4jI47#4ApQBEP03C5xOX#8x=Y;1i%DEB z8Wg>K>s2M6JeOHpPA=2Z{OI})vg6|1bGh@0W`f}RX|q+{Pdi}j zVZkunWtqa%NhLzp?@f5l|AhHN35(wU_A?209%S?tJ~k1v|FbUJQkGM+{fdYB*}$hS zFNmZ&OuhGQU!mCSfM?g;m^>^6ZWpOre)!O2s2a)d757J9WQxCx$rfqT%{MiCigQjL z*5fdfDlGKfGQrr3rCsk<|D{RI%Ad?{*IkMJx$c2qRrQ>wM)TM`ciLs1%lp0Z$EJO? zTqW9bcKuK=EmJ=|@o#j{KZZXYU9047P456E&MZ$C#}J9j$q5Sl21bn@YHkwqXSPo5 zZEWnFI#*Kbz(LLPCy%nKSR{Dus1&Ja=nzljkl(*ZX+OiHl}%1wUQSIM`!;lJ;t=3G z{9{p))*~UIM_P{-1u%MQW=gWLNoHz#&Jc>U6wp-nUbdeh+mY3MVfFumKwDKyTq8H00)|WTsW(*6=_k zQVgg;5@bVgep*R+Vo@rCV@iHfs)A>3VtQ&&YGO)d;mK4`dBWi7>gTe~DWM4fv$5|O literal 1207 zcmbVMO=uid9G`y0vbEYOwlx&;oTR3LJ73wE-I?whcD}L;-F4k8G>VqY&b&>A-I+I; znRGYt&>|EP5wsVjMWii)m$sKuL=A)#dT0-=#e+qK7AkrW@lcE@Ro6GkZuAg(aG06* z{%`)j-~W5&OU03$vECR#5IgfZwT#zY(X+h=zxy7v4ZJ)bYGdIjs)bF%heXywRS5Eq zF#*faux6&N!!$u`so9mWaIA1xGLb_Wkq*^zJd7rY^l-~F%sLD~6;9Z0hWzcTMH1Ln zhCIR-n1ZLkNjo>|!_nDd#hk61qD2nB0@5uB6F4w5K+9>kfz-;7tGp84N5?b?R#n1! zhFo)MtWW|9@*&`~q!*^ldM+kqaV5ql%`( z42eBm3&HUUg>_;#=p+iKjBXho%~A~QI8j`y+Cf-`|GKfEc2Jq|AYFz5n)Xdxk6M2h zjC1#RLy;o(Mtaq^aZ!whip*&Tx?x_;koXH_*_Onyte~c3LC=bch6Pz+1f5e-Jf|?6 zp5(h68(}ek;Z|?Q#;zJ`u zeg&aMw*yL(C`7>|@_;fbfP+VD*Fw$UrKmh>(W=n5k3mcKkpouqE7=>^*HT(CD<+1t zq?(B0PVq@i%W|xusVPR5vt$Qr{hyrC*cm!%j{h`E_Xu}jv|Vp~EUXU?bn(Racr@;w zKD~&?>gT+gt+c+Y{@HF1Z#i&cYFF;TwDdYC}Z%b3J0WAKpd1_N>|E2kD*FHpdSI(ZfveYZf`_FNY&Q$?Xez5!Vz3H@i zD)HvYcdyulrl)rpJ4~-n@IDc5!+6*O3cHSC)VOWe{9>+G28-m)`Fi|DN)^ z6sg@GTsWG#ej`@Q{j_-exc=-Dg9B$*+Do78&21$%^$fM{fDAoNnR_em*nwx F{sI5tlI#Eg diff --git a/toxygen/smileys/default/2728.png b/toxygen/smileys/default/2728.png index 82aad355cf28e1e29d776f8a0249a8b5bbf4b6e7..5e7c381ae1e1660e91ed02d811da759c183d309c 100644 GIT binary patch literal 1385 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>sSzTwiQ)fN+_Dfgml*z^Vfg=&;r}_X zT^AYtA7}Xgh~fWZhW}r|+V3#@|HAP9H^cvj4F4Z6{67G;aUSFUjST;9GW`GlUn+6w zEuc?LN`m}?8L~{If5}hhU${b;?bEe;vQ`#6a~Jbfl*z~)JHipLcTXU@-@`}2to}O= zG-bv#bnNzxvTJ^PBs~_WpE1eX-Nmz=^+^qo!(QU)>&pI&osUbEV{501JOcw$V^v5* zNl;?BLP1e}T4qkFLP=#oszOC>0Rw|Y@6=H5O?MP{?i6>)FuvwK{*fnb`YN5`KU<#s zw2)J;l+4O;aMIn{5>r?AKXBjC^`6=_&tINht$VD@xO8DwN><)oGdY*rd+z2<=Jo={GxMy&p$JxcEa`CD1{5@61GFt=_wgvQVy_;Li z7s<&|p=@@TKd$_sx2f@N+%qN*{GYZqp6U><=H|{m zv$6cR-qJZ4kM`QHz2LFcR5W~r#nsCT+$&i07c5p$D&y+);!@u8tSPY4)#CDJ_u0pk#s`;0-W_8AWP@vTExMWO^Sgzgsya*n34rknvQDbkkkUMm?cfyPoSV+0k+(UYt|msl%FQzD5rc8!M-z z*oJ({s$aN3#&QD3gvFkZ>dcmyMtOf?C~NAJd*k4|!HxN-e6MMj!-Tq$=}#VX`~mv( z_1c=P5B$D!d2-4l)9EkB>>o>zySorl!TlLZ<7r%j1hoYr+H-8Ez~ zFnvz*ba4!kxSX7jkdV^Apr%ptDC4UQURe8PTu0?o7ArF9NuXQiDx&Y!{&!O_Ip z(xU6?;>csDUDzSVDduUQo2tueDw@*Wvt~~3+O2z z_~gNoh3k$rFw8nPt6-a0$Az+sZ6B}3mNqO|GFxTJ`q%Fgm$NVV`p}9ehSzb5;31o2 zHehk{S&6sg@Rs`PKo_c(xJHzuB$lLFB^RXvDF!10BO_e{b6rD|5JLkiV^b?rBW(i% zD+2?zdmoxnH00)|WTsW()==K;VGqbP0l+XkKk8M^+ literal 1347 zcmbVMZA=?w9KWtx!=McI65d9}c}TJ;?e)2~*WRs_YkSvGT^WHYs0rb?dw>&qSMLs6 z$f7VD*619G$;>hnHuphI7BgkOBx=?Of}4I=j8Qj}#Sy>wNr{O%U53vAML&>z@N#$0 z%l&`9-~ai)+>7mPhssOqN-+#8PqgwKXsooJ5(j!W{I(*Zq1F_-%}%Y~%!)e1BC^&8 z@kCl2fE`ejN1wg|Ll|c7S5n<(ck(bRX=%4;*|>A*3_@dAs3n&Xr6FkIeQ-cg!^AHi zUnX!x4ijDeq$in)!9k^UOoyFgZ7FGNNMdB7 zF88w?e0*ICt-{2hX=YfG%x1IhtkavHYODteg^bLJ84|cXWIAn~Yt`B?JK(==Y-(+!Ml+D? zfQB}rODG@xjYTku-Mxh@L*xy6L|0H!#9>~OM$%9<6MUFJU)-`HvjSj%KzRk=7bpP$ z5R6maV9@J{`uV7z@(D$b&9D^1M5BR#&&LQ6fLJ14D#`@?v3QJ$`y)JE#3oe36jccp zbrqz$j`iLX%f@slnwp-{wBcd}v=3^gW(;Z>Jl09$`@0lX*0RO{D?Mwc<)N-T1Le4` zrSaAHvdSjPjV<%xBy{C{FbB4s4Z@;4~scP=q@Kh7`HI{EU|MskXY*$nQKbFl6of@0!{OWRT z&&8De_KYWV_OHc?;qlVxg`N2$HP2F2*w2Sr^?I(pfUgXYlU0*VLHSb6g^4?1?lHV_ zc5)9OghmRx?mHb>K3PA>PB+;ey}LUf>G^c~{2z~VH=E_pcEU5<4#xwfy&o-HeD#(q z<;^>mBTG)_Rqm6j*UH)_mS*hE8_wUIU9~&5_z(7$+2w1`->1*OraAlm6OEN(p`oVr zeU5b`k3KjTj+dNR9CtXbx9qL?rqI^oy#L_zPy4`8>E>kBBfW)VeFf>%tn=`5GziJI yrXS$oQ)k}YR`CJ1^}q#)@2adW%VFHO4SV?WC+~ju)}J3*zqN$W#=jHoJ@yZ5+Pdlh diff --git a/toxygen/smileys/default/2733.png b/toxygen/smileys/default/2733.png index d9b1f08143de666843b83638d1e5e7f29aedcdc0..334a066abf7d7c16c9049e3d415b1d0adeb9ba3c 100644 GIT binary patch literal 1388 zcmZ`(c~H|w6n-2b6q-O0K|E>*AT0_+5Riyeln7CY63`}Ugg}CL05SfM!vNtLB8VDM zkyg%BE7xqR0Xwcp3<>v)(^`zl@HU`ZJp}urKfcRwq!Zd(66cWAw zh$aDy2Lq7L1DNx^ZS?m*Hy&@>5=emofIa{@z@3D^XF{JuM$wW zXtX*YbwZLpQLGUiWLCUxL8a5jBpGAH8Ur%dx2oBUQ3=T0G-iH;{+TDx=?YB%jemKo zg=}R>^{ZBm$!-R8?}Bb7RQ_#m=~SC3dQx+QPSf1hzXw&RRE};M9r=_pT#MpbA)9@? zt*iBB^Uwr~Q2i)aB^GaBW4J;XeHlPIED%AaG$R!! z9Gbq6OX_gg;~|)4U1)zCRkzDLSrA=VlxB9|n=2I8^oVH}X10B0W%$8b^TWq^(o70* zC_1<7jkJ*xy<%!3DaiVd2b0Yi^jwiJT(;)f{Rq1~N~_a#i`pZ1vn%GG-dwYTZ=yF4 zDynWTx5NF$VVO!Z1(v&dt9kgrC3K5;@rgstYpILE9*`aeo1ETS=g~9ITk8TbOj+D( zjkA%Lvg@lNeT&=bIYx{QPa>Y+*!X)Qg&14+iBPm&XWG)rUuF$(^R{hPcGq1I--LTBhJnu9QZY+3OxwiBOubiEp zaD-^hic?n|J`zH3S0IgsOJ+p})-T7aN1oeVO|o@i%jdt6KTo~b&8UfvT)4V^5i8uZ z*1n5VI{0FuLI1mm2?_4b_A3&DWQPU*h7Th#7)s$#N*>!3S^CcCnr>C*Sf;-xv0+`a z_Of*;ZJjimyU+5Zi%V{KF4LbO6Msp4yBjmd*QM#?;i#%%>v931B5FjZ^7CZ5+VZwV zQ&Yj}yV(Kf1#g;a5weLBzQpr5B6He`6^7<)vagEl4Vl+DH{=UcY)F@aiuVu%|8!24 zL3{k((X_}4o3@b|TIADmt3{0Mbu&sl#~kyIZI30MDjbyij5wZCDWb+oaWUg{ZA=GM z+^M3MIm&~sTkqsfJ^Q-g==bBp8G(#^@d;THbq>?X%RJLFh@dY8nyKDifeYlUwqvxG zZN}+OtvZPXE3_{c;>{egC$NmMF6>GaYs~REVuR;;RyzCm(e0Tw^4Dof;i)`nw;|}DaC$T{&41~i*xa3ei7Y*PuW$Rtuh$ve0IG(<#k_Jzg$f5XpH8?w*_3ynfRR2UM&=5xV| g6BWWGP$SrTxpXd@6L+YCtN8+fN}+j|uMbZ706RH7_W%F@ literal 1416 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y{ieny<_3s|N%#)f2%rQm4tZ}VW zb^-$f;|@<3$B>F!OM<=inFB@sE$eVm&0REc#f1*{BOW>`U)ghVRvK+g$y5%L({|eR z(Rjshx)ZP%`!b$n>Y}Z@gxBMQ%y8OrV8yw6n z)=eMT??hGx2&gY*;yp3VVqepT-GArp%abme^2aaw_=bXKT>;iwuXXtB&s3Z{UC0^v zT7MpF1 z-)pG4$ds}qNkrN%nLiv+PLH9FMYPgc7wEY zCKqd`ykR9$JTFnO zSL?2T)JpN|3z?W%1*^D2uUXWKXJtt4_Pftk$1Q9UyUXNrWs290mcP@k72d1oGGGX1 WzMqoiBxVmP8$Dh9T-G@yGywo+Sn#j_ diff --git a/toxygen/smileys/default/2734.png b/toxygen/smileys/default/2734.png index f95730ed788b5099446420747548e8565616379c..93a995f5d5e1fa78fd1c0da90dda4e1102641fa5 100644 GIT binary patch literal 1297 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 diff --git a/toxygen/smileys/default/2744.png b/toxygen/smileys/default/2744.png index f88a35d19e2528a2a2dbfdf08a2577b3aaf1a96c..bf4d09ea3ced7be376adcbb268cf83f46b22d109 100644 GIT binary patch literal 2099 zcmb_dc{tQtA3qbNRF)Q7buYJ}y22zB2`f*MVPV2-k+Hv7WT=^MS4dDEb zIHL=ve!wr^4^`L@n1nbLLUQa%MNq?#7TheR>Etz!DT;O(x-!7e}7oF6!UK7X&a8L zTMFj zu;lwgAV04cgDs{1@U)lpo^#O-eF2**lz)wIP%<(Op2kn6AQ!sx% zdW8091ge8H&<;>={&vwkkhgjI4UPfl#8&KGFb}lBC@>J9yz}R;yc`ExZspIz4%~e+ z$YTyPz%=mTaSJZ#!!QCpAOpw{0eA?8110mgF|Z55f=w8KCx8Hdr_?F?fx~k z4tRJV_u- zexXpguZhIhTx$Jz*!i9XH`j%puCmN_+ zjx3SGr5X+KV1_8(=~7NisWfle@-mG$L#56TWn>B)nhZe!!Q#{josW(-IWq7dz|=R; z6&Mx}5|mw6ac@fy{3UN*Gdahk(A}jSogJ)guQH^Wo{kJ@4?9PM4&IG0z1Yz!z4oU2 z){DLb{)=R}nu7SYT1Ad{Y{F@|OI%lt%}=Gp70p{G6F9BH*fcpv?U%C(U!SC^DaeIK z z7-SPg4s57mQ^Ptf6=Tgu_2%z15ehl2;hCtbTa@f?*u{rRqv)BM*>0ppx$3XqboAKS zD|t>CI>#KFCG8szewG>XqionFd4MGZWq?L@E0_pZ#@CH^L|44oty8FdL- z`nFWb--!NnwEW4TN162(12h%aI*54jk$t|ct}}ZuRuv&UxS)Lm&59me{#E&JPgil` zi4rH4fF^Swo=nxQkyX_W8c^VlBS)1z@0DkVDJn8~HaiK#9f1lh`hpsJrzT{hg(FJF zF74KH*dF?q&*e>fpG!Xt;Xm`N)PIddt(5o(u`Kv#s8_FIdAaD%Vq492+M<1V+}OBR zWShqY;^^XwLn-fQ(?#6rJ$-)^jeNdsqH$PM=$(W9BHvs+D}7r`&n)!mHhmo}6Nxs0 z2w`#s9#`@4pIlwO=9Iu%!aC^ORf6j`oHig=7@jfOC?F&(fge{;WAKDOgLpa8U}GT{3?|-?qJOhmb|Br4{2B^sBYa+sn4n^+H$vYPf1A z^Bb~vaCCBZVZL*9b9eIaymlSkxVe+)b<4Za$M?3ZoS*+L6mVzvp1>ehQ}Ew+f0w@( zvKNKke-IWP@vu2EDmv^@Ozd@}5EtK)kf_+I^!Q2J(`Tf8==qD6?MceNzu&K%-0>jpzvVVA@u_)=&;5g`-|AcJtd{Dk4S1B zg}1Br!DBic-QMG6C-jc#BNU`&Eq8Kv!hJA8mZ^J=X+>u;)GeJEFd=1_{SOb*IHmvq literal 1833 zcmbVNX;2eq7>-i!K}BT{qOwG-sN~pOBry@Q*$v@}7*K)&H6#l}l5E&4CZrx1l@=9& zs;Ja!K)kCc9%xXf2xYvW9v}t9DtMP#Dqai&-6+`paQxAo-TjVzpXYtP_n6I&R0g{b z_Zd#1P~1Zlu!p?%;Lk#pBJlLmY+AyOr@ zkD}BOk$?=tQGmzb(6uZO1o(Ug$mQ|*U;w~ofovv=y!mvHE8_7*>_DLJqLJQkU6M!z z%lmwhD+w)yAj~2r(`vOctQ-c0Co@5zQ0U@dv+1M--I8G&c{O3^1lmH=-sY1eVaqFASYtCqh6Wh_E>b z$V1o&2!aARn zX?va0%o%RBsllr{=LB@kZo4o--ck2`eeUUL^O{erWTp3XP>-o!FB{CxUp#%C*8oRB zrFQ|()B3F8_`IQiEOir~sb08$+J*A_@O|lOwAN$bv zdHW{y!*90ENJ`%_k!cB8b98-Gx9ZO37uc?A#XAOVS|k3eqcj8PZegj5gqG0I4Pib#v?xs_i z>aVl4N^u^4$&ftbkf-7itj@3Stip=dP27!-MlFZ@P9E6Vw1N6d&jl-mY3gX53arROKIDN z>fboWwmv%SxiL-bJ#$^bf@zs$KgQ=1BbU`oTu&|B2g~L0F@@*fb*|3(7IU^T_KuwO zJ~3g}?Alv5mM8Ej(8V}<$L%3F{HEC;8gV_j^`cx!%$QPVY0h3y>7P}^3isj&LLHIa z745ArmglZHap{;N&l(;dPtCV)Zs3=7uikN4nn2m@SEG(C3cEfJNt*Sb+wJFz9m|fF z-_MXKOshD{8#*m=YSANycN0by{LHLuDL~#%oNHN=oFhWYtzm zC^dPraW+|xA+hJkW4uPTMuCjbwi1>iM613(=>H9!Rb7oZg22}qJR z^O0~9LBLHMDKZG?>5E^!ElR5K;p6%=jB2fXUlWVPlH$X^z%w=+WE&5~1J6hzyV(d@OxhQEp=FbJGI^+k#PDmd1E zX<>Z{8Q~GZ)&g0UI?IzR+3UX#NhlOf9H020`y; z&~zmqG7JYY_#etD&h8M8ciftf;i7E?#X80+cYD z{>S=Myg}4;R7z`uDcUc29QP%&VZ>fECXXi*_xILhW#j60`4u`vQ~9cHeWXiIdFmA; zSH+c#=NZTQQ!SqTEy!k$Fej50PdF}o%nW8H83a(8J2$fO3%PW*rIK5p>}1uaBYbJ^ zij@>;UUnBJohTz70((M%RDw@t2~iRCPP|9f5vUkP&TLM;hn7#nk{x#EGg*ZN z-?GM%(GLQz zqCYlr^XDAQy$LwegHzp&Pjo7^a!e+Teb?;o33&PeN2H6zN0ZH?W29_aQz?0zMQSZe zLtf+P(^?AEAlX=dTP{ectr0@XbcA?hF^u12)n!I1CSgiaI!s1KQst&XV|A(XSGpg*?D)~Wx@`1}(c}Cy zwhyIVRV|ZXvE4cT!Ot_~+T!ZjDEv(B=rucyJT3IwfE+p1wt3!7|GDM2H%nAr55_Fg z^sUP&=G7w9@C0*qSUmx6b$@xp^zPc#m4vm*LjTpp9Yp^Pu3wRx7#c4dU$E}9xr|JS zWjXn<=rk6?_H-lzabSisGc&_2 zNJx)9fDkk!9VlTztQcA(103jKfeZ{OlorhJWYFkw5$z1*GX)?ykqKPKfTVu__R?iZ literal 1493 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y@vTd0QD#|cid#{Bt^zoGtunFLZ)$8}Zs6!>=IHi?JgIrW98(0$8X5=x zPGewTV)b-!45_%aB-q8Dd~lw&CKD*3Nf-S=V)R zzAIV$zu`!$-kqIBdRIDTdG$JShzd&im>PGvEq{4KUE@4hqb%8tyQb7$V?x$nQ+ zySJWA{$sqhQ;(ot;60{0Uh>RdGgKDca0^?tsPsK=#qpZE*`{7)HeOK6IveZaN2d&@y7W41fjOyB?ne|$JRW$zlX`7 z^;1;l-a9*7*5Azz-}Yd&-v<`y0~yc6ijLY$`pH^SzmLyYy8XqonVe-s7nkbo+qk|w z?}U;{^&7BlZhYnn(o>$ ze~~p$INS^3<}WXQF34+fuSTO{(^dYc^79`m%dgl!Hvhx8X8G?7uhx^#r{^8ke^RJf zyZu-1n$_pS|HYa#w@XZ)EF>HKL)`jE=%bJJjBE@?QlxT~V?G*y3R+KBKbLh*2~7Zp CC@viU diff --git a/toxygen/smileys/default/274C.png b/toxygen/smileys/default/274C.png index 64036e1cd3cb3e934b39e77be9a00e6833e6b56a..2bf25262600a26a9121f670557465c3374063463 100644 GIT binary patch literal 1396 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>X7ZtDLX6#rXF{WBK+uEX-WADoZjI(`X`jvh4Ef} zWn;pzA+tr`{KajGlf$lFyjd))^xDE#`-rHf7E`m1#6niN%RG)3SL{kyCLwuO{4cxo z!LA=nd{XxU;^Fd>Eak7ak;lGQrgKu!fk(|L66Fk&?o`jEy|Jq z{;$tEmKr|ydFB0LhJObnX6-#Y@nEaY@25S2PRY+xqd6KMJgc;u$#t1?vo?pqxo56{ zXT<-WcM&v*t4fVrcVmz0F$opVPuKI$-AdPOJaZ#w^{QKW6{i+xoYMPI&?KA9%E7sK z*ShaHfgT);rhc=QdGh@|zKZMC#@ySNBdzDp=%2-Aa(370sXDSr1<%~X^wgl# W#FWaylc}JhkipZ{&t;ucLK6V~BS)nG literal 1169 zcmbVMO=#3u9FMxJ^Q^gtjYm({=k(qC`n^Gy9-`IA5N?pB;UlJC;W`2(#~4Kw zWLQ&Y9-=;mS=X?uHBu`dlTGX-jYuaMIvzzcOyA+qGt6;BKpljF+k`{xPctz*(F|?%F!{$f+ZC)o@bYx zs+9+Uf_(%s$yCDRAq1kBgz1bZ!h=BIp}_I<785WnXGB@Zg7(EyYrfTxE2`eMMR$32 zj1W)exMs7NY^IXfALXDVNfC!2B&bFrm~x2`Cfs0mhe1Vw>DwN$u?r$bqmCy@o~52H zhv0bS@(QsVv=c>B#)XE*!6eT)P88RYc0elVgBz=A2h}MLaTOHcN#CUPXzcEQY3@F4 zC{m=}$j5z~7R8uQu{r4=mz2~zOTQ#7+miWIP7!seWEDl%1PBXS8WuAd5ke&=N{Z0o zSPd)b1xYIkP)loB2sJ@cpsGlM0`*+FsAu^Ow&VuHa81;)Yg4;zEdNQYtoXP)WN6peXq8%8CE3HpAE5n0aIx#*SjjPf>&*@k# zmefKu{ImY9*ZB3whLQUBajpZ*Ix*rMi-FJ$6z}?wK>cJlH(=%oGkGGF%bHes7pZo&68~5+66CpF~L; KP_Gw9zWNXGF?w$R diff --git a/toxygen/smileys/default/274E.png b/toxygen/smileys/default/274E.png index 9a337afad77a141009799ecdcc2b716a21649f6e..8bbf629504cab2615284aa9384a3b5700bdb004e 100644 GIT binary patch literal 1324 zcmZ`(e^AnA7=JKJG$phWH8qTiJjElkGOHOXCKk*eWU!fqf?89Nf+0e&G!#2O}g39@b?tq4VeaSVk{0l1UB{3B;6lH;QSLg)Y|Hv&jy05b?A zeFq>S1H3;1;B^TAmzb{(-i2(agZ4A2@Eo8E;2A&%fDS+l@C2X^pc>!-z&(IUfC_+e zfE!>aUaUvbMFasC@lPVJP$lD%r(C1Lr#GchA<2_ z@$W$!7#Pq8>if;9y($DpMef_{hp+l-w-iZCgfu$UWWWStw!ySMYt=abBgsi7OBljQ z!Y3Z*M<0#j;f3Nv9zHof5y12O4+nxP^T_5OpF5v4CNwJ~_NilsV`g6VCGm(dt5>!4 z=F8Z>2Wl#duXdm6xc?x-T0vLiK383O(NO6^jA|VSi_dMWo@(mryv(^(#%XqA=dF(~ zWcGadi^BS_!}6+WC$zq$F7IIW>kvhU74KXdr*}Yn({hYvriX{$FI5PDAsxr zF;nv9o#t`btl}m!#>3dwbDoN!&-N?CxVL9g6s|^0O3iIjiK5RzWfA5$p>ya#jE#);O1Q&&B1#)2-1=mdsr37B z`x#?GM36N$a(>D+$EY+F8z&+ksKf4xm{4KSLzqZHIc=DoiD#`To!5$Cgnt>m7XR9= z#aZi2MHS%)MylLn*L!-eTx)lebsU=XHpylPSD9ROd>ih+JI*KeiKTb)Lzm%hDxIZn zkff!IR~`E~OkYNk(h0KUES1IOyUMJmg_&4(Q^FTc6#>DPHrzTiDKRe^o#EfLl%FB* zlysiDZTW{;gp2>Ywtv|Nj7``^*{^BaTx_H#iW*sj6{8w_lNBAUUB}66Bg7oB({|=q zk&aTr=l4|b`&Zu1KRvjRW9h2i>1%)f{Do`N33(oBdsEZ1%(V>nHzD~L+q+Q{KkgD` zvg3Rzn*5E>1?g>3MBCBP(^Ij{!uS1M^FFA89q3r?drv;(qsXdG>4?0|LYg0iY4hw*OOti#9;{!dyIyMBac4t%k2Dn>Ila(bDOi$>6TK;s zp1LroCKo_U+5Q`pn@Znj9+L8L<<5fpD5uATm@{O|U425m2c092f59~!vCh)%knbZB z+qYHc_bG-xD!WPK{L4SlKKz$dL#<~ literal 1319 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 W(^+x<&fB|!N+3^HKbLh*2~7Z}8MA%> diff --git a/toxygen/smileys/default/2754.png b/toxygen/smileys/default/2754.png index ce83bb601ba7849eac7d4bb09998c4bb3c11ecb2..c1afcb2c73e3efa88c5b0395d8a2689426bf7265 100644 GIT binary patch delta 1111 zcmV-d1gQJc34{rd8Gi%-006c6H|hWY00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33MB!|Rh+v300Mq`R9JLUVRs;Ka&Km7Y-J#Hd2nSQcz=OVHxj6WVig-g>Kz|7R?@=HGPz55`)~wWu3c=#3 zw}nu!;V5iGH4A&>A_}Y>RDZ;rfcu*!!V8LR9qBa0R3ZVC7c(68Cu;}i;##B6P6eSW z?|*h4o#;poHY41*WIk*{XvOp&b-7CH-$_kay25?BeAH*tU~Qd;1`h5TF2rlJZP28v zYIgSA1b@$(r)dMZB27ggkOeciz%Z>K6pgA2R*IR?ebIlA5zHEvD`x(P?OcZUfOs11~$l^wb8qJ89i=YEp6MuNeXlXjdh?-`t;;I4E651||`>^*PaA-|# z%jBYGwZv);Hx zQAhC=C#5U&3)SjQ_DpwF-~a#sC3HntbYx+4WjbSWWnpw>05UK#GA%GSEip7yF+wmp zGc`IiI4dwPIxsM91g}#7001R)MObuXVRU6WZEs|0W_bWIFfuYNFgGnRG*mG#Ix{so dG&n0TFgh?WA6Xagks&7v002ovPDHLkV1fbk- zu9Xm2nnCBn%IImgH63NJ%b>SGx3r_C&}^_W*30My%VF$LNJ?S57`#u3o$U~Mn2_}S z=zX5|d4E1#otb`n;Kc9=f*=O6lj%Gj2i@nm55LbmZ%cSMZHq;FRdNRn5UAEGgW@K%&U(Jtnu__D4BCD$hCs0Kz8#Mp#mNaaOjgA%GLTAbXybC(2) z94F_PTqtJe{35EmZ5QKnbgD}FdEF1-N2+~vt-)sOzc!uTaDCk@y_GZczKA+BX zeBo7`T(E6}r>J_p9;}Chx>==QEEaP)XgYu`0@jLVOHM$u&UP8n$SRwPVJo@@Tt=y+ z*X%fnl^%qk8oAsdv1WA=g;Pd3l0m^>h*DKIu6=9E&ZB>29BFM8Rt!Ytk)_wnGOkDE zY!{4k_hLt`Ay&hmHx*nIX(_FjYbw(0Y&uTjFF{$6`B0b>*bEe+f{+nu2$Nz2rWl5W zP~g~@KzBKg!U_zG#@Hw)Mnn!mk>a}aGBnaU+3XG~oM`}yUSBm6Vr zNHmm8LP5j|aNJmKCW#|v@hlb-!(<05|DT*uSPbPh$A6loyM;T@ogTJ6UK}1Cq~Q}| z;-m5D`@63YM2{z%P8OVBO09N#!h8J5o%$!2i<`G@9jk_KKFku!uMghn+o2MzzQzsD zvl4jce#QUN$33;HX9nj!|MI$L#}EGMEscP!E2I7O@9uukPB_s)ohVhoYrfJc5Zu^) z=3O3XTs!HPB;)xXxwYTM`LCz(xeD z)<@R&PH%qH+?swwjNZOCHMMmyb*X1q>)rWrW>`EmpU*saGx3ABx$>vk9Dd|&&R=$h c#=V5+B+>umLCx=L54nGttT>(iDs^$=AKEpP)c^nh diff --git a/toxygen/smileys/default/2755.png b/toxygen/smileys/default/2755.png index 74b34e077959d613bbfda3a70f862edce01ae2e2..80cc60eaec42cd7ae362f539858792fa975c4108 100644 GIT binary patch delta 1044 zcmaFIv6*9nWIYQ51H;x|=C6PhOS+@4BLl<6e(pbstU$g(vPY0F14ES>14Ba#1H&(% zP{RubhEf9thF1v;3|2E37{m+a>GW}UtJXvQ4*9`u24{vpO%@Es!&o{kg8BopIgAdV9`4@G;rG-1CGDxqOndaOof(x zv-Z!sd|q$AxY6T?v(n5xMJ4Ov+caZu7&A zx0%kU>3yuvUHw*XqkAJm{*|g9il=W_T)8}XyW|PELZ^P=n5@fd#U2%`tL|FzN$-&K zA5A090M?IAW}RNWrYYJ>=KMY*`?*5;Za$eXiWI7$Z3|#VJX*`uE&n{q}wVo6g;b`+V#^Z_e0Y*6+>C zyt<9^qg1N2$;&mfrrPq=AKdgTH_Bn1N3bAA${`*-10QV;*M+ZIrY5C@#9xt5VcFXD zv}dNh%}&RC%x#Y95jl|=(>hi?uJ-yOraE==@!V?_Nyil$-$~98vg9%D#X!=+>7Na#q^u z2|2u&a$}bH$%c0RHH%eFubd?Q+nejfoaP3$wS8(_UClp~`8}Ojgtixf4pCa zTXS?BSvyg1J1}|nd%8G=Xq=xraW8K@lOc~=ykCg{>*5WPJ`G3gdYon!ELxy=_vSK> zA|tLlo;AJy=hcn1er!MdO8e0vF4hw)R=j5&1)iw*TY7z5&Gq#D`lVGHRW~;uz8<|m zy#8!e*dN_{n;5)a6`0(%06I>!#5JNMC9x#cD!C{XNHG{07#ZmrnClvvgcurF8Jk*} zm}wgrSQ!}DSzqBp(U6;;l9^VCTZ4aE&L*G+NstY}`DrEPiA56)B)AwDJYD@<);T3K F0RZ08wF>|M literal 1134 zcmbVM&ui0A9M3ux6^9cM**y5j6cyGaFKyB$tj;b;wP0uI3hQC0Y4W;-EqO6{v)w!> zqTodE;6+6I@dqe)b>NSopeIjW#KR7PvWtqK2*diawbR4!U`X=5@8$dXe1B)IIDE8g zYwuQuVY>3i^b%dWqi6Fb`riAkY0{;K7!@*tYou-lh{@Wxia_2q$59EH_RN{rXnV_ic2p_|1z$rG&av45 zjm!?0t=UOSvDv}HV4$H=0v8bzG~6jKR2vy~g;%9=bj-70MTJae*j1-0g(A>!fPlm$ z;+6m*kYx@gC0U03Kop?J3-p%bFsVwiDyBf|VyU&juBjzG*Rn--8FqpYU*-9Fz0TDW z91g~Ls3=OrA&POT5f5iPVm9Jl*w<#zQD_B@PaN!lh|#R#X_8^7r>h~jexa~N?1imF z(UkEG)8`>4@U9!hwW1x868huDy4qoR#z%Y!g?Ku!Xgz9uZ7|KI+aW5de)HBQnHQBdm%AB3$^V!)NTuF{1dBc0Wt{=${0_zJD@m$2@WT) z53~^(92|8#8`r}_QF&IQ)luM_MRqR0E?CL0>a1g5N+eT4HVd`L0~&W)&~sTDu}mdJ zF%oPGYyVHqcR4GPd97BgFHGh0UeFSJwFf9vAUDjv*pIS>Tne3tnwJMmkTHmM)KzddJI0U#|7@;N`+7sM#PU z+@EK*q&mKy1Ky>%9ZBXxY~R?17qKH_+jjQ7z3}?T@~$ff784A3#@u!8X|o4*K8XGR Md1F|AICT2rZ%Aouz5oCK diff --git a/toxygen/smileys/default/2757.png b/toxygen/smileys/default/2757.png index 09323192b895bf849c6222f744a9db7f46fafd69..5ce95cbebeb2a378ca96fb21a233b2bcb1e52d0c 100644 GIT binary patch literal 1291 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a> z8UOb)`0u6l-$m`ey~2M>sei`8KXiFNYp}mnVtFCQ^jMPdfhYq)FI-|{-vex z=Rq@|GR7otcNc~ZR#^`qhrPtp*OmPlJ0F)S%O3j-69xvRhN_T=lAy$Lg@U5|w9K4T zg_6pGRE3J%0tN<)-l?I{+ny+J{7Dzx%)-pEYfec|eet=PbDx%9sC%-kSpM?7KDLQX zOq_rJ{%x;2x;{hk=921Q6BAzB=d8ckJj%!J6&9 zQgfr6oF>0=_B^KLT7F`I?DXwVW^TW6RQ1WPJsu1TnKxG6QDK&g+t4@3r`;>=EjQN- zX7&a*i#|1}Mn#RY<#KMf4!U)TEjoSXi-%3hMZu=UCl3n#T%KuvMf&fS2Yz46-aow~ z-`ldz`fOxw+0o@+-UcgJAH8;zUDLhH&BA8C>KFdi3SDi5wWc3{$+O4P#W6(Uvg?W4 zLQM(`u7M&VM;UZaZ`!iykap7l|HWrb%5LRX3p#adcdM(6%j)s`|KNAz?QJvp7nIyC zz1znmar*bZ^O6c2-wqqdFgZwwJ+=^2Z20`x#8;iKW6`CkXtxFnyYruGIvJfp&h7MK zn6xBr+EPD;*`^IsK2`1Z<_z#Meeb5(aP8N}n#{j*`9scf9WM&Kr3iGFYKdz^NlIc# zs#S7PDv)9@GB7gIH89sTGzl>@urfBaGBwaPFt9Q(nCSR$6^e%3{FKbJO57SA$V7?( xHAsSN2+mI{DNig)WpGT%PfAtr%uP&B4N6T+sVqF13M!!(JYD@<);T3K0RS@c6Tbie literal 1169 zcmbVMUue{J98XuhR%^%hVy%k(nUk@x-6g-=C3gvTt7($cV_q%SW0oPqT=Huh+T<6L zU%hJ?r79K+!tiPAVGn!RqxvX{lnoK~582aX52JN38H`yd194YwzqEJNhvI`F`ThNV zKHvXY8W}#(yZ7K;hGBY(C-pL&--^EO7##=Jn<^(Du z)1E#5J<2gm=eScDBV(mgiiO>z8ObE;u20bnlN+r2rZt5KsG)fY!rOZGkiA%HiM2rD{y?`U=Ku$W);tnJWDOz za>4aWrEOv_Yz2yjjH{bI2a`PKx{+U-(jh6M7iR1z9ad(2#FbHqX9A1nV?5pl)7ZUQ zQ6xyMQBDUAO^P|KV{67m9x3X1mfj?7$5!}MR+9=)%V=7`5Fk{IG#nB|2|_I^$(qpS z*a<6(d^V${1tV=_Av6Seh|d;;teS$Vnog-&8(Z{3VtN*8>vgE!78bq|t7ri-2@Wb4 zPq!;zWD*k`PGTQuqY`-cjN{q37QPpyXUkh11W0f)C_6? z^*hV!1(kX%(UmM4DYk{RUngc9HHM3dM=hbewI`X{hYBRn%c>5gm=PvyZA@hGs diff --git a/toxygen/smileys/default/2764.png b/toxygen/smileys/default/2764.png index db9de9e4f20534703757c3d900ff295345a22d9c..20b145d54521afaaa9936735ad31b2811484f420 100644 GIT binary patch literal 1616 zcma)6dpOf;9DkW1_f8F6O>K+jGKR6_*2b15#zv*nWIsz;mX>R3j3kAU%0pL5oRUlC z5>lrmr#jS;BwY@}A#$1PjPvU`f1N*0&v~Bj_x(KI_x*g{@AE#N_kB~ny*#uvR%!qM zwCOYo6Gpx7sg8h06LVc(7*u$!3|9ckGBoFdkZ_F-rZE`+#Nq%DB>^x8Pl*NqI6?qm zkOKhu5&#B*yjmYe0F+L9Z}X!rE-ohNgC9h)p)wr*5Rz9Q&f>H)@%lgD|Ieio%O#(8 zU`F>@e(=YZ*rDz@=zj>njRfOAF;(E&PUtL$2rW$*bjyghXuBC5yUA7|BYo- zfJMHh=*lB37DSsC5}g(jHY-GqLjq!ztNzRZ3mJRuYbTvC_VW3|Fu`##$@w=wTk66uyU}Zkb#wo}y61a;4GI!(*TSp$GuLxbWz+WTZt1t6Fj=O-;4dA{B zn4lu2{j}%yET4xAWmH6^DJWbI+U=C4wrkAz>rS{Sb=ZMo45-6{2@0YX4=PMR*#-a` z@#$j+>=Q5%GQD7LcfRYd^7858B=7~N+~#K|sr8Mc6H`skL&^}US8pyE^f$bZ%DEth zH0)rF(q2yw3OI>eLxT%7A)P^0>r>Ow!0H4JCTs*i`BMZLdg;=Bg=m%p- z^=~Y)T`q+t_B6h}vhi^yOp2)t^_0rq>JTTwH-Q_Hx33@a7)G zEHLx(8V&4J)UzYy7gdITb;&ur>|<e zAL$fVKf^b{oJN&xFU;cvHAzvQZmZ$C&WG|-Edzmw0^%%xeKR8vt!#w8~usiq`* zRJ)%d(kRqtPnw>_b#B1mu!RL%tp&Gk^0*-ok0<5!_DQ9lRs@>Ca(&dr^UDpg`=r@r zEd)YKOSZoso5f=HxBtgD2N+# S=s5&`^8nDPUX&6yPU7Ei(4gV~ literal 1583 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%uvD1M9IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD#PU%0_}#n6BP2AO_EVu8M)o`HUDF34YC)x{-2sR(CaRb3oXS&*t9 zlv?(FL9 z>gs4>Y-C{RY;0`lWCqjgnO9trn3tRivo{lHFI2A+UcFY%MX8A;`9&f5`8lu@5Rj2y zl3$#WU!dR|Y^rCd5T2Qrl3!j7iXMn>T~doO%TiO^it=+6z~O6^iN$_PLrZ51H&0?TGzu~GDjPAY2#IYv`QXpYH-<+RDTZXT+10H~&P{%;_VmM>j7R?})Y>XMoRc^AOL zrKjK0N0G;V-ud}5u%+$Llyib|TuMeV2ezqaQ++Q=l zePgiZk6JCm+mr5Ym3f)orz#@OJI#CcuQMI{U3hgg5_hrJJIJ+eW7>c0;n%7HU*WvP ztjjm?hUYI@b(+of;)B4Aug)zwWFp(YOWIe1v6s!pVzajX`OKLcq-8U?e}+td)v5Gs zXPA3p=HaH$?8Dcr3wrbnql6A^n=N}oL|SCOF7s+roi4VR)A}6uyMx$Yu6pSELx(%i zH^S|?for1q`38h^qwPf4`1!-JGASa?VLR6!@-{Wtj!ePYaeLm z@=CdV8rLSM zmp-09KXX3EtJfhnZbUxk`_o%L@s8vDpW7Lj8SL8Fvv=+K@Eufyd%F6$taD0e0s!XPASgue|l%JNFld4csS&*twkz2sPV9`4@G;rE%1D-$M zHLF;ZPfoJrZzIf9v15UMq>dRM_b4VH^8N!+!#6 z&8!(S=X*__G+AeT`@>bc7R(X+Ia&9CYIK^6!@QDbUr)Id#FsOqh)BKQm+b7@)NtVE z`A7xUEnK|<^Cz#AU#I*_JNx+;Mcl>tZ$W6X6F~v;^3n-WQ$NnDN7X5!2CzULPCdfHQ^llh1sZ_cQOZ z;0_fd)|FG1Oy2RW&8A*KzTq)P%s+m~gDc*(J(Do^Oa6HH{jO=3Ck7OH^p;$b37yC1 zTgVpjGWWf7`bN&wx?GipDa;#n%O^4O>q`Vr`nurB(f>}4KaV(iA8J0!COj+ITdvG; z!^Mv7>_x3j3tS3bEc*ND!Gn%JXSdbMe+}EGy<_q{x%8bq&mDu-`On&XyH0E0n*8}p zce$49*?IKdJz13Uf7%p#F~+VfYZW$tQmDD7i(?4K_2h&D+!Cw{c+^-mnr||kN+@Iq zUbE0afFWhWQbtFnh@QO+tJ)Sbo_a8cA+dTA3PZ8yHv_7?_*Al0)bKYRJt`$xN%nt-&oj UXwgJPbuI=5Pgg&ebxsLQ0FHyC*#H0l literal 1081 zcmbVLyKmD#7&oejpr`_*4$uY5g^E|=*omFkY6$U*MrzWKMrx!YQOCX{R%4&BuOzKN zg$_WcO7J%zHq?a$1|I4_HwGq_G9#f@LP!ju<(x-hs5)TTzWeUJ-|u^`7VQQL3cQlw?3s2MI7&ABqDTN6tjm|n`4 zjHP))Fsbx4a=ayC0vjNmY}pOR7h5T+!zd%yFxCkBiw@FTVaFfsGbq3}Jj+EEbjW~FufPSAqOhmk5NtP>+b4GXUZQZy z=$7u%Oe9L%b`V!b+ebz4*Np?U{nDZf=pyjpf@k1*R7d+@oV&vf1&Y`kamurBQS^oa zjRhMxD66C>d=oJ(Q)C&2SCSI1WdvEpf+RHLfNY zMrAXSph`j6m1I0`9C?Mu`_hg9RD;+{|I+ru-$KcEbI>taPY)=wZoR@|Wo4C2sr9AO-rGwL zTzIhhP`W-lFm`*cee%Pv&+A{GwSPWdKe02i^F8rA)TSE5s28tpSI=M0zj(b{+xWIJ zmCmjm)$SeNtc5q!*_G4$)8PB diff --git a/toxygen/smileys/default/2796.png b/toxygen/smileys/default/2796.png index ca89edf555bf12171765787bb82c426d32c205ca..0341ac45ac6c42939a205b0846433a180025b522 100644 GIT binary patch delta 947 zcmZqYc)&hEqMnJFfq@}uuJ|V)#ggvm>&U>cv7h@-A}f%ukn9oU%fL{j#=y|f!octg zDAe$RfuYoZf#FpG1B2BJ1_tqhIlBUFfD$|bJ|V9EfgmO2yNL;q-Wj!R6HuJ7B*-tA z!Qt7BG!Q4r+uem(kjDpP8+(bTuWLQ~Gj={M6{{3imwOBhOf^*@5hX#1W0dq*z%KA{i~uwj=SZve0DXu|My+BMrkKU$FV)e ze0P^UO0`p}+}-%*nC!P(iaSHHRn-#kYM;D61van%X&O|HJ&*C*UnoVrl_ zWyQJO>Y0agq}!x@oIbsrqktU^z*=z zv;TvdO4cxbK5Bm>C&cmiEw@{f1#(JyzKDC>kPAvF<6YS+B{OmVt#8ctFNJfKKf3)` zXZk-Ik4s-Yi_K;)f8_n^qh|dD+rZL~j#_WddAL8@uJV_OLAYy;q1OM)zyvDp>Eal| zaXmRffk(pGSyp03*xD=wCtZeYL8e2ewrT_cm8h1uMwFx^mZVxG7o`Fz1|tI_BV7Y? zT|<))Ljx;gQ!5iQZ36=<0|PtjD|{##a`RI%(<+ny@*)&m6or{6N)78&qol`;+ E0L1c-@Bjb+ literal 1031 zcmbVLO>5LZ7>*QFS_>kG=*1yLQE~INNj3>h+k8yyf?LaWp?hpLnca{U`lNH=013yEI72*y8_1N#BCi26rN1xFw!%TFeR-3dN_l*F@ zoL}m2MVwMJ!_3YXsUNH(0y?M{B_{jp!$%fGp~x4c07OuELrtmvL)K4MIy29q4L;ZCmUJ8~#tuci5ok+Pi)~KbNRxWwg2+f yr7N%B_o^M`>!)W^?vL*ao$B+OFZS1%g`G2uzc;ma=gr%n&_=LFr|Nno|d4`_T3|YGvVm33V&u3tp z#Q+qn%kN(fRKr*jPk4EfIOmWAR2!`~Ckl?*Djwt@~c7OaH6MtMBh=^(_7P?BVA8U0pek z%XZiE3f@RJkBZ9J9J+dKpzp)mwR+c*IdyWRx^vIU__Kr@RT7ll<$1NbBzBRt*e@@G z`Aa_CeZQ}@Xq^D#%^Cd97M`~ISaja!USh)T!q&ZE9=Ug~g>@V36wJJ&Hha~Y6U$#M zaR|M_eS68W-sBtaHh7)u=CS#{TRie|uIF)9J;kpjC+ip6IEsD9OiHWM^7ZoDv`4OO z^{b{LwH1oQ)@WI-S?o^Inz$v6Ia~yG;h%?1@}_pw`vu;7JgiI?VF_V;*`YjJ}VOqRo_FpY_ao= zcRK82aeI*@?DVkLiFwh(`@&OS9NFYzc7rMGv=3v>*=5QFo+(^Gnx6IQg>RWF>y+gi zehcpSBX8ue@?G0A33I>XkB8syns#|&K%qx($t9W4d3?N<37@}h``)|xMvKvYGa-jl zjW?!QpKNRw@6kM274Yd%{lWz@mI-@*u+Q#QGCe-c=1r3VYscxL%!LXXf-<{a%(Aht zP^#bhmVNKk^<3|kY(E~U_qcMxl=|0`pS{^Ozf1r1@#PNN53TIx4@xfIBy)4W>QnKD zC%fhaUwQoim^?K-T^vI=t|uoP;Fe%rz@x@0qcx+q@c_e#ghECpc9~TX%nNw7vHTHZ zVz^?+>?9HJ@Cs0qYKdz^NlIc#s#S7PDv)9@GB7gIH89sTGzl>@urfBaGBvQ)HZZU< pFqr80a21M<-29Zxv`TC`AQ~RXM2byRROez~@O1TaS?83{1OR6{oq+%V literal 1060 zcmbVLJ#W)M7&eMjP^*fLr2{ptKn0Q5=h{wUC4~4RG*Xk6G*TmVppJb>tkynbUx?eO zz(Bi!82JyV0}=xuB*enR0s~By7!Xqjgv3w?=X?}~ssonoyZ7#Wp7(oqseEI6bZV5M zsPW>wRw3&|^qm` zwQi;@CU=d_gfby;AU5dG*>D3n%rZS*ne3xumZ5tpcsjA=Z4WAy>4*z!s6RObcUAX4y`sljzJPkl$oM zI-QOK#> zj|B;ib(|=!o_2sM@UI)kY6sO#53&^)ptf(4dNgK+V3NC&4MmE?8+p;UNl}aq4Vi5R zy11xi8FG`bY)j?=khGK{74m6SCxW7KQh`@fBCm3MAt??yj>7^auS#kaPd5@rCIt3EU^@~a4K3_D<{g)jed z`k$P!#2Gefj(?hEctkod+8(t&5srojx@2N}G8#MhS(A)awW#H*;ph5cx0{Py+}^#b zTwje{y0vzA_RY@^d;5~V5n>?MFxcTzot0#|Uzb>?Yz2Et`y_hR@PJP)g&rgj_ ncJFhazU}`$xG+8PmfEIb)T(g)iW+`@9?fl0FKbVgwR?X6>F-ef diff --git a/toxygen/smileys/default/27A1.png b/toxygen/smileys/default/27A1.png index 36fad95c2133715e56dc4da0c338ecc449223b85..f0dd3577e374ab4d4302a21da4e735f95964db5d 100644 GIT binary patch literal 1257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>_UyUW^Cvv6kG28D8I!!-UF2DL1@;0t z>?NMQuI$g)`M6YhHa&U3#=yW-TNM&f5|mi3P*9YgmYI{PP*Pcts!)+zz`$V9J2f4lrtvM!kBf`Y&cDH4f zMaWS#!MD4-uT_WKU1Vier94A_($9D0_nmi6>uAV0WBvF-uHK$A?ke(m2IY_Ws^3nC zS+#Z2WP=@Y$zI)WR$UVCzvkMw>V$Z4_bjnK!}8;6dP*nu)%~lUWOim-;*NtVD)VRA zToQI+oUN4hZ21+HUe{&ehc9f(^H_eoB85NVq@5C?clA=#k|^UHVZ4hJoWqfxya7OCkdBd>XkfEUdOrA;?LyitB?Gfd!@xU ze24#Z{yMY8S+8wO3JT13UC_{pK9#ci=888vIp^ihS;qTlRao%R7QGh>S!YEzF3<>x zHZc0q^eXvpqTazZNjnxz-|nF|VSU5Ka?g?`xBsmcHEdcJwd*37jdBWai5Ku18S?F^ zW%6U5$+}Xpt5|;c&}8UsB(GKZd4Ir!J{bY2lZLV$J`d%YY*N@+ zXNGs^2ESCad3Hj|a+YbW%fDrBj#qi#*WD4k*Y;h}%k$kTvG+~VH&<%!OS^B!x=URz zwq^p?Rmqnp{!Ck9Kj~9fO}gzpQDE9E^K@|xk+>Xt;-y$)f=KJb+!G-oEk?B#GVMmY z|7Uk!%e?yh=jU~M8|&)~*0b0N94k#^?p)j1y0&#`)Y|SX8w1mJ->!e$(9vTh(`l9P z?301|Gd-6jcb<29@UJww`E|No-m8BaMh7lGdzAXwpzgYFTgS5br*x#AW}3*a@R_CI z33P#KiEBhjN@7W>RdP`(kYX@0Ff!6LFxNFS2{AOVGB&j`HPJROure@63VtQ&&YGO)d;mK4``NQDp>gTe~ HDWM4fn&=?~ literal 1226 zcmbVMUufKP9M7D#TfL%G*`^@NGL;T{Nq$K#ZL(h5yCmms^xASgSnRnHvwL(=;ygBWQXi-Eah})y$WJ8NE5p@nk_AvJ#IA5Gv>zM0${?gu6AIcsKNq*no zFW=AS`#&#^3_af7_Fx-95bfzBY8J2ehR?1R{NBG(*YWZ|pp6Bis1THO4-yFz^4|@q>XTi#i1!I{(*+4d}hdOk{b}^bDdiyJ`ZcM;{%)_GP#HnkaoTW(1 zj8n&A88+i8uw)&ndT_KllryRmhGbIxePnM%#soGDbh2VkI=)rpt^ z1moP@-cYEBy^)7K3l~M7RFN@lLnlb9aSDH-P0N&7zDE&LKpUEMiHLG@fc;&Hs}#20O!q&GDaRX&&JY48^V1$D6I;fexM+50A!y znYMTFSpA(=6S>Nz{NNK)$Di#ucjxk318b2N>L=!w606|!TKzhrYl{zG$k(d1zC(+% zv-P8|{!%^Nw>Vp0&+cg%*vnq|{5~bJPtZPczvOS#Z$7_c|DK=nM(6c!3%_0HD2^{v z_e7rScYp30E-(K&_-JZjm;Xr1^iiFn*H!tfrs(dhUbP ztMfmqtv~$!#y9)kyY^}1ufq|xWz|`GW%%MHs(;~ZeI0a3m6!F4H=e%w-AkSNT>F{& zA8)>mR?-WP4PVw@FaC+1ys14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>XPASgue|l%JNFld4csS&*twkz2sPV9`4@G$z%eTW@Rb)V&=^t6?3}|+bI1kV7_^{{KUrQqLr7t{hl1;6WaBjKUtHf<>54nUF*X(>bQ%;fVhKP{h-X(6DQ|85)9-Le0YIXS0 zqPq#M)h;;>aXWt2x;RyQ<80{Y;r;uuE5bF(q2TXx4FRU8R<{Y;R)jdWCVaf~dQ!lK zM2$7>rrg!+o2<2^?$UHHWbtwW(4tGT4~byBlhj%adQ{*Gp4*KFjJ_WSHRt`g&|AH$r=>^H^iaoR-QOh|J(3evPP=J-{gFVBHJ^~frIj00{U$N- z%U6_pJrkZB{@0uH(sC)2EmLY^vwWI%!5|P=NBp6$d4M z?#{LUs9QU|KzyHVx@GTm1<&u3pJkaXf23IZTlB-cn9|SATKDF9v_IRg@|Tf;t80~{ z+u_f^)H&7D#W6(Ua_ae`VoeMJtq%j=9P!%_o#QC7d-nHwspq>|@;}b6PBV82*7s!buQ%}<=1wYF^e?bqfTJrhnPXmBjv*yO8c zHgnNtORv0TSu2(;dXmU3dv&@-<;_zLy{2Av+iot}ZR=H6W-;&F^w)3ud>Jcj;@_YD zZm*e<`1>C_vz1`fZSVJUfeutHag8WRNi0dVN-jzTQVd20Mn<{@=DLO^A%+H4#->&# z=Gq1ZRt5$$eD3c;(U6;;l9^VCTf1tDnm{r-UW|`b33& literal 1274 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ}+o7WNhhZV(RMZq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztWi0)yO4o_QOncCF{I+wlFNqPhXVu-d~}i5+hy_MTIljc&COS0e{+hwQjlN~T-f|W z@{UVEksymWJA3=BgGZM-FD%ti;^69XHlKTYkIFy!I~VqGmz+4rf6BXhZ+SWAW*zRX zmd zJbuIe)DT~R+p6nVHHK_ykP3=<#PGWy_I-DH^5h%)uj!cuP7I3Oa&c1n_GH@=NmCzi zs4Lr-eN;^^ovN_W+5VuGT(Q)>16yY`T@{UZ={(``?98bOS4C@1i|o3$)B5)X&JtFB ziz(~+k8SL^y}N1NAxkTZ!>u}(R{EsIuHW?Z)Z@C1jjnqZwnv71cwT=$H|bv2_tcq- iJl{J-d@21Wp1>gesce&p=YoZx(#X@*&t;ucLK6UkCAHH4 diff --git a/toxygen/smileys/default/27BF.png b/toxygen/smileys/default/27BF.png index 1245f996e0036e617701a28878fd5efc4d40433c..798387c81f7ee9b8e819e2636445c76e7bbb1939 100644 GIT binary patch delta 1300 zcmaFBwVi8%WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!Dk3avN ze)ErE!w-fjUm4c@IR5(YhoApU_Wxw)_-MZW``I^t8Mc1E`|%&c=5O~u0agAr+xMNJ z_05Z~e;H=HXPEW=@#nt`bKWyFKY#z@FGKfBhOUqB=z2K+#@Fw^|1G=ym7)95n!Dd1Zci<9(Fb~wyCldD6ilAao;`c+_56v# zIT1}DmodrP-GwQQyCwz5VK4FYb!C6X&c~%=imIl#O5#SeWG`Gws^G9o3yJ_yEldwzutEG*q;8|Z_Q>TzF(HOQZLk8b5~_x zWBz8pTQ#LRYaXqB7s{*5)RiE(ndj!t6L)8?xyQqu%WV+7rAL;TGAwtV>|Oz0*BoB%4m)jUiOU>uc~}o;VQ63 zdda^NmWZXakNK%PmJid_lwkYVjtQ$+x>_%4S8LBS>Zsea!W<;Hr|&uz3q12M0=y14*QtergBP5lGJ$-{@}rN zA<-8O=X#RnM804OSaW#$;+1>{rkuDqK}a{#G3KwsyZk528)O<^{+}SIvun4LOmA=S z;vb3SH(!*fh{yyf&sBNvHHS}hOY-M!R;AsMIZp5D*Y~mrSP4WIt4n_P(C|CsWYLQ! zSN}ON%35+{oOos7qWCf8;X&t9d}dxUio1N423Op8&-7R&D?aWDaKB!xaOc}o_lzh_L4C6UYPhH38^tyNFR)I}r&;B(CB;;8Cc#-sG-iG94 zo_Wvb?>+Ee><^REp%+3&TaH}6ZL7kf;VYLpEqIpwWu1j0QtKzxt9@8o?l`+WiuH=N z-E27-!;9~p1pr;CTH+c}l9E`GYL#4+3Zxi}42+C)4a{{7O+pL}tc*>qOw6?n46FivTd%K%EcETBsZ8*wZxvO_=&$e)d zD28Q=V_|BXWK3gJBpR~~+!7^|Ib=bBX~ZZ-U}NaOxy3{cW=2iSd1p7d|JyAVESps(pw>uDYJ!zH0~wMUImjch zVXm5EL#hTa`c977f}%aOF0rLL&fyz^N>x2{BP2GzYxQO8{9vHp9@e ziW+smD^3NxK8TWJ4zign#vp>C5RRKrs}0A|5(q<3%#45=H=)f=y5G;D{3|OuN&)XEB@9vXRhWH zsaXz!dNdUz!60|HH>4{9Z-`wo4~oLZXern%aH8s=9WeM}3h^NVv6NA`3#Cda%Ee$P zN-|c|X|v%dN|oVu3QKaVhov#hf>=S%3vhmAzgrh#H|!m1t>B=3BUsOO^y^ zIllzIj(wZOT8fY)N-@9#5O*0uyGRf*4kWvsvB0a?(ErJq88~Cso8v#tk~{((sEcc@ z4>oJV!--&GWH1_zGlO#mL&}RDn)GY4eMc)FO(nBGnZNR2nlqhbWM>ic-kBW;GkW2n z{1InK&dJf7*}C0zo!uXG!-vRNtc1R=c%-N|(&a+8Jloaj5yy@XEv8=2IyclY|LV-l zT=~KJ{l}2+&_42&n-_a4cRcx3MtNc9&B>H2PeVfMbjO`nwUg1=55GRJU}6WnM|10m zFQP^7@4ep<%^Qv%*G7wN_hekka?)E+_G-nA?b;*XzfB+7_d!SQrTXyHX!FGvZm_A3 z|BNQS8Sy?}ckqww=G?7`H{1*7t9)T&=3i&Wr#`OzwtHVep6~9@PLDMVOu0{OXc?

3F5a_YR>(&FeC)^5w#2}6 z`b3W7+TsrnoS5k!9Lt=pfPeb+%_BRD#*H@2LWW*%|1$j2z+DT%sii&Vo@pw3El~VU ze$U~1BmH~anVSQjxA#sK?mE&F5d-ce^6=nf4z4`0t*`g;1?@~ diff --git a/toxygen/smileys/default/2934.png b/toxygen/smileys/default/2934.png index 7b52ecdf8975001462bd6eeacc575565cbc96cb6..3517e59421c1cab1a6694da7c19340b2a48c0bf5 100644 GIT binary patch literal 1288 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>32{wYaWQe(`S`_W zVi%r_o_8#2_MwOw`@*K|3YoYysCQ#P*INJfmA);@yqgw#HY@-thNuT}AtFFQh&aTM zr6(T(&6%|C=HGw+fTq=KxbWlmpa1{=|N8wGX!5%+KadSiow@Vd&)+~R5+-jscH{Bc zJI{c2)i2%m>-V3Mc{_oYUU~TP(*5TZ3wA;5T6*k~EzsHGB|&~5uOkD`XV0EJ_j>+> z$Mw-Rpg3cax4VntksMwzAcwug)7O>#89N`BD$5@G3=;+hriQAJh?1bha)pAT{ItxR zRE3htf>ecy+yVv$i{7cBv6pTu@YFmPv1RgbI&{3Ssq(dteD$AqkN)iFyIt3t&Lrlf z{g!J^UETkQ`#xS@qq_HXhuF>T)%W+bd6s>A^{_ktmg}~{-FMqK-gHarnr?}@o@2X7 zvu3vb$*47ry`jll*Un0IaR|*3y0~z5%3k-&rn8b4mp4^TJX5~^x7ewzE-Wd@b~D=7 z{XTTMHtiE1^X*>iH(Q;Ha<66k9eU#CGdb*L$WrC`Yg`*wtq|X~XxY@_8}BxpiV>HT z_+IaSY58p{+3+1LPcAF>e`1L|u)J&L&y^;g#S@=yHL!^cpJJT!FNM3}q?s|Ff_UtY zJw}3mzRNO5wz7RuRK6G>Ca~jQwU`HEkR!*(RHLm8iWN53_o@h(F&!~lJ@>|9F_jxu zOa7gxWXj8*DHP|ht$fPm3BhTM3&fR~zAevce;^fhGhN;3Qs@_Z_H=c&uIBRzJ1#`_ z?@-(FsK-BU9oO&jKjsE7`%ut@p(R*s!QlD)z&YrtxuDI#!S1cpg`N3K;)lBuXTI7fFT|J*&l z;x#7O=nm1nl4-+5vI`n;;XQxyj_$_(=()DI{R^FE@ z-`Vrrb<(>0S(|UyY42MjKcDHY&~!aJkEyTD6s7#1wj};sOxKLqk{K(2$+OMV#W6(U za_rgbLQM_=E*H(x8IQIoY)j%>z3%1z{by2dXla~3{kBr-!TiU5MeCR=E-S5XnJLOL z$>`ZkgRVncM0H&DsDy8n3|-^uq-?mjt=HA-)s3hnOB(08dd<3#&S&ElZPEF;HC!@R zV`<*Y?%&Q!tu92_pZ!^teevjnV3Riop?5B|CJ4@#Q&_${xt`Q|Ei6yC4 z$wjF^iowXh$Vk_~T-VSf#L&RX*wo6@K-<8;%D`Zv|6H_V+Po{#3Ck9VfKbLh*2~7aDb37*i literal 1261 zcmbVMZD`zN98cS=%&w)!5N4HP$cD3Zm*lyZ^e*-`(jP^hBr=h6BM4H?W%@{L*1NtlQLTD{i2Ix^8F@3Vx_-t!_J?w0G4HWgiSl zo|pW8zu*7u$#8eq(+zbG*I^je5ZNch(CBiWEw$+V=weAiLz6A`+C6&G&PgW3I7LrD zJd%}CFa{-M@X+V59m8spYP{F(jdrrKo+Ty6hRkOTgvPM;y?H~D`=N~|U`o|O#P8!1 z1gz0q!**G-7~Nv~U`0l*oC1U^5*fZe!<1|Et=H{%9A*3Yn>HoSZhNX=A| zY)lB3wa_X=q;1<^DJqxCkvT7^n<)wegF%PG<8dPkw>7BQQr@juJ1PtUv}98?Y*p8A zhfzxC19pf&p00(EHKNgVV$CWi3Z;z7O9lll+zslX_m?s>Og0@-uh^< zet4jUPK=3;#$amv7=~3fM+7dO{~^(N@X(t_woPuDdiSY2^~XzZ9X`$72IudTmUXf? zvh$lnu~6L8GBPw&x_NzJ^s&0p+>eD5U%6^WhD!HtUdyyTG3k<8wc4D!Dfwq<`gAdsS757#dm_7=8hT z8eT9klo~KFyh>nTu$sZZAYL$MSD+10Vq<_$h-=b{i;2t5$1gq;yYOW6ykk+b4@Jz_ z7dB;A$i%Hdy&D6%*7~=v^le$@-L%-VVF6Gv5Y+=U0QnFhpeRHFV#>{zpZ@><|LgbP z>(4*6@3;;$v}@=I{CW z>re55JwS^)*B<`+?;p_m+1pRg*>M_Tfq|*MDkP#LD6w3jpeR2rGbdG{ zq_QAYp(3|{fx)78YG~l5+X_5?o{QKrO>1E}UYPXx`pmj-KknAIs+>RnF+2Y8%!bwr zUb~r=-?y({D*q>Rwdea=i`~8*y_z3)NM(1$#}ChT=P6b1znd4!^6id8l%x$`x zG;3CGPcglA;MklwiDlO15>&&dgi}j)W7L|jPZ%9 z^!#wOrvJH23X3l^*rljwonm6TZlB&Lq9EELkl(zLt?j|bnD45M8{`zGMfxVy_eV`g zxUKLt`W$n?{|TB8SyO(u6=@Zn(qZ_L{`uFvNH%A-_KfL4- zw|XUby(LWM==(mF%!*k_=U-l_1$>ri5$u<_P zN6YuWp5_toibFgU#1H67Z^)RcTnlyfJl-3cFmKLAv)dXZ7{} z#JMijPh@slEE2}wscCacm7o9S!hALLZ9YreKWzBurr^GLdH;#~Q~ut_f4Q5t{MGj3 zv3h?iCoXwC``H__{G-#qz71Yb{piwB_LTv11N+Y8d;VfB|I}5WbNF{SFp0K#x;TbN zT#h|^T&T%G#O0z{I^$7;ZB{dkyP979xA&>=5uK7=TwQa9hMH7f$Vs_X@NZ_rzwMZ{E_h*wJg1h2)YYZJQmvUhUwpxBT?@;(^dR zOh!A7FYa{L>RDuv$?gC6>$EKwe?R$ZUBG_-%!RlA7+(BUy14aR;C-NzR7+eVN>UO_ zQmvAUQh^kMk%5nqk*cF8v WK2rSsL`8Kj1_n=8KbLh*2~7Y!<2fP# literal 1295 zcmbVMZ%o`|7_UQ7p+tg-*$tC*od!|3_U+yE?kdN5v^}|n19D&>M(0ZVax~EPuI+hu z#N-?^INTSE$-X&bqGlL_PCtMS197@BrymS4nIOd8SVKuRKA%WIf=XszKy${|jNoaA|V!z#E4BA=Agjhr|1|SjB zq$Es0Ny(=#!xjvy7*u=v?f!TNCmWhua&+7|Errk+))LO8BzYLx!~jgHdWc*-KTi^> z5+eJ2aXOyj;gH%sYQmn;&R%(RSPm*=xRq$hafm>JwnXH#5#8c)A##hCL$Y&Bk;ImY zJscvpo$8Nw6TD$U!sqrh%QOH4%esNr$FiV_U}(TlG`iVl;N^TQ$M}hzi$vB;Wspk< z(H&c87b1sjJH=5{Hk);4J#NEHQXm)%Ivfnsj5M09ylzXmX5BhmW)Pqyn`+8d4V`cp zr2!*jhe+hk5rG8pCV?uHyi{cLz(~ z70dA^lx)N7HH?vR2Xqe^wqXq!DT41|i6{D0T`{s&qf?&kXa#7hFGD428XB>cUryb_ zzR%-gfzB#{4{lS}7Z)Z*oDYm@IT9{Z5^F15b?^!SaC|6HT2 z+%9cARKXT+T*;1$msZD~o0_-@mJ|2mC&B@K<@<({k2cg-SGY4xrOo1@C$FZbD!y3u zrz@AcUthZKvz`}SujPW*ia+L$;;XHNWc%A+*83hGD~{L9d>p#C_Q%Fcvu(BKwO;_9 z3CP#}IyHko%pOesy-^D9KXEmCX8xN;stf7ccNTA5!rNX*y7s#cja--3R#&Z8^3sLh s>u29Px`4MIAH*N5W0rb|&I*9-!?DKcVqxZ2vd{TN#l%iwy6xE5KO(8ep8x;= diff --git a/toxygen/smileys/default/2B05.png b/toxygen/smileys/default/2B05.png index 8bacddafcefd223fb534194c9edb1553c5e8b9e0..368e2fa6f3cbb140b20088d04acac7c8780b56be 100644 GIT binary patch literal 1257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>ERoo;TImh2AUr?ZRg3`&w#cpI&cMO zQQh(bAHIA?w(b|xxgwxD#Y=+xKz>CAp3j~=d+zo836JZeZ9s9xByV>YrP$P%H9!t~ ziKnkC`!jYvE>+&6k1tGMU|_1R3W+EQN-S3>D9TUE%t=)!sVqoUsK_l~V6f<&8XCRr zi2}!;bkWT$%pAMsl=RdWpQ}0dY59e^C(DZEC(p?fcHvre;llm<^)JF>T)9uG-R)M7 z_IZ6Bo<2%XBU+aTzm}mF;zShW^tG25XT^s~; zlBVfdtKZyn;?>a&7or+&9@4y*yZNr_GYx?Wy;1!(DRYJWzfU}=&6Be|V&QqwIYk$z z2^XqdubrXtS?>_7RK$)7VmSV}T8#ot=y8n6bbk&CU-Lp27wEpI( ze4c(aVC^~IW`@~r93N$JrHwK}W#`(8D{kt$yTvJH;vyx36HS6q56sRAi1e=Fo+qNb z`1hj!3CcS}ZNhr~OgqW?N!4JR2BR(Sot`>YzZhoG7g9fZ-e@hbs5{|hz#Jd(mBWYG zQYvVXs&2>W{1abWYZ#^}8~l|wa#+0E_{hZ@7M5M_w|OqUJaIyS$Jd7{k2R0JoZx6} zmVYvM`;4P%Pk!C;U|7h!vGR=yvs~N<8QqzDbNAd57rEfhw@9FH14E~Cg@K^Woz(Vy zntcMX%Pu)r+<4@|(U;~gKe1lx_rm)xmh+as+WtH8+~2|p7ruHHm(5=O=<=^`niJ+G zy?WF<#d&TZ-}!vbzs!^My6Tp$2vz{5&T>x|#}J9ju_wZXnj8dNFD{=Uyg=fj8U}fi7AzZCsRS?4}+(x KpUXO@geCw5Wgtud literal 1220 zcmbVMPi)&%7k{XE+x zX^<$TQ&_psts)ZARM>S)({3Oktvw7e#Dti{0dWF^kPrui#y^Q^=DAG@hiV5b+wXhN z-|zSR_eyc<_`w5@9v}$fV1809;dLN-_Qmk~@jIc8m!Uwb24z$a8oCFGtchxn%-i}j zEJ58|IP)1CA&CCERjCHm!U@?xHmyfGbklY*njl8Tnyzll!ho#7Y0Jq_zkIwwk(QaE zPNoWM!ByalHQDlDxiwWWTC;{^Qe&g!NK?iHHVkyKY0o*n+{{p0yfT)fV}>HPRD#(I zwe3{3P$U)PK{7@2NrMG|6h#^cDNzJZlN<{;hQ+s-1cICrWiCy2FA7`p%(`4sb6s0_ zm!W2Yz?B)M(P+>Oo<`m@10+d`I5;keHIn{<6X?yP;~(uYsL(e&%MC2#kP)L^L-Rp~ z!k%u2V7rCF4zc5R6NOX8GkcEhG}ESm$GstKwJfW}P#j+aDE(Er7AL+sb*l5k&FcEn%F6k^7Y+>) z?LPIP2ai3VtgjtezIu^;$+O-{w%a$~{rh(OuYN#&S{~GRnkc{hNNi(h{3GU3VPY~y4i-DB14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>0W(* zPm5>S$43uWzrW@3t#0?-T^w&#OY5p`iMX7jyGgTV_4<=pIgPEZ$(#4iNOo}u)e*Y5 zZg*=_&s)N~J=Y?yfb-r-8-QpGmTh8`#8UlS&-(BS5n^WTiXfWe|N9ooV~7KzM}k} z9Jj02c4i7Z$m3ZUcw^g?lVO`L-YgbWx_!e}`bg;7Wn9gA5(`=Lwlg_iT(RxJSp%+J z{eRh`o1%}t?h3Rs+Uc;5$?b;6whd=5tUjQ`x4zNq%c?hzlI|FFB%k4n=KAOKTF`AU{%p?Zf}3H zK;bjT##AlamiAtCCd*keG86Y}{N8x~Qn>WHEBXDKV}5;B@Vu^m_Ga$9F8$ZX*E@Va zva(w~Xra|&nVJ75ec@idqpR)EtMkpkNWHH2ujVuUtNgd=S+#?JIV+#g)~I})D37CNi|4xCIu<9>@nXf(M@P_eQ3es15^u}pN@I>o4p zPsuSrN2r##MwFx^mZVxG7o`Fz1|tI_BV7Y?T|<))Ljx;gQ!7&wZ36=<1A|n)qdQSF zIJb?1}zuKJUyvX!>~|#SjwVxm;c<`iQW(Y)>P2a?a33~xK;2fiUTo0v+@v6 z8%hyop`y(k{{;47SfHTiCcKHv2(MZOsrWi%)vys7!}T`vp&bz@s?H#cKLDhpk!?5L<7 zh5c01w*kI z3xJqlIgx2|Y=@QO0^p<}AV(z$0GUZJk}T43Nf2p~807O}i`f(>{DJ7tYStN0s93fg*?f>MALe5ZrbNr`S+DE7ZeQ~Sx(PnFSpou2N zL8B2&Uw8z=?&?fSLazEve&mVcFV}WAc6{{4;Ck?6^OgCt!tdbXdUMSpmrftJo?n_> z>OZ(LH`jdhZV=#q(h8Fwzk9kksr>cDd}iOuOSf(g;P-?Au=`Yab?*zSJGxJOu6190 zyQcJ%R-Vxp1F@a8Z;#b`uY6Z07eBi{IMQt1RtB%0dF#pG<*QEbz8C3Ni`THlbMJI0 zZ|=NJ<3E&+ggOg7sXc+^*OvD>^WXn`Ojrh`PwUMaCuZQKL$5#7Uyj#rOlp6wt;H`V zK0Lqh(|}%uIAs*j;5Z%Y7Z>Yf2_qj m&VU2M*Vgx({Aw^bj|H%D;Z}|5`SNl9Pm`8MrTWn1iGKjXmZJ;+ diff --git a/toxygen/smileys/default/2B07.png b/toxygen/smileys/default/2B07.png index bc9532a7e3a02dfdb42658bf6556614f63566703..ed86d820c3c353a86f66767e89df5d7f4603b921 100644 GIT binary patch literal 1261 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>-K!zyYcFMy0y z^mS!_#?Hs3%B|B-x|D%|slF;Cq9iD>T%n*SKP@vSRiUJ^AXT9vw}64cqIYVj_og`p zJa>MJST;>eIcdjdd~A0}>deQ>Z=Y#5P^oOx;@Pxj+OE{eb#?zI?)$hZZ1TNQm)@-M*Zo(zk-erHHe{^XCR*IXv7*aW z$*N@Xn(r%ixs~dC@iT~5`F#8RzSbf=LB^Xi_@6C&ZTGQg{>(jx50pLTs$M%GX4Th8 zlMQwXUS8sA7W(pp`s+mwp;x$XFIv|6e#70wsh0Xi2Jig+FD<`qB^$n@<;i8;{!c8C z2bOot{JGM^vv|_es|I%>!>1T0#cg8UlQLV9eS&!Ip7@y@^J>{0+!iv{p702n!pd^) z|2)>9h7bX#J-*ANB$|#F-wtxF91W`a_xo z>)qr3nspDZeY7=kO23cZg!K)HS6gmW9y{hL6DK_Ng)ggR=r05F8vPlf4vQ5P?=f2V zrkE|+q&oM=RQ@O5-0T=m3tIe@H)`6roBN1i-kd{^+V5{&etF`ALXX~;OFrvSQ*VVAS}aBkX}cE7m4`XWn|*<0GyG%_g~wr)47tT=Hl zsIuk8CT-i+^q&@jCobN*?EXamRNb5HU#_-Uf4%)!XZlAQk4s-Yi_32NAD#a7arlDh zM^}#WuMC(Q+4tuEq%Yi^ja^j-lK8d&Q)h*zi(`ny<=7L)nVJFwoG(Uf3_fC2CbHrB z|NmysQ^gjfmkK|aFYm+E$lr2#(}t#->$RS#D2bWsc<^^yOzm0Xkxq!^40jEr;*%ykV-LJSS8j7_afO|%URtPBiN`Ht>H z(U6;;l9^VCTZ2e&EvP7x1lbUrpH@NHVJv;1bNim{1VMBr4=NeF?hKyxZTQ{0QrGa(lzG-7LK@9HqZ7n|yJu(Lih83f(pFTs8 zh909PA}Kay%dlu1EIV+#oX+OU(|J*+_79PRzJv)(=xL;H&RDMG$EbB)3CqDTLy_w$ z-gJ!GaB4C&M#{*6WQ68Jc@_Xt5NHsN2m*MJJ|$(8 zM8g)|#i*j^*%HH)N+r6))5s|>KorG*gX2P2BjnCmp5})vcVCl1fo|S0Y|lUz88B)& zH0#AE?CC}brkzS{5?gK~Q8;Ccuh|TsS;jPjxYo5@F9ZK|V@qu}J7+^C16?%h+~) z0OY73%3PCUE3Cq+EEwi_HLNHAsGP_pqAHsJB2c&}%c)Il(sDh`%EP8z1KVw2x!YnT z*@2peoGd~!%?=nVA`iJmWRvo^Kt3>GSUM`X{Xuy)qE(<{JOTBDgG_QgzmlKht72Tt{#;q@{cy+H zt-D@)=c6|5zG}UC(W5aYkh6qGoM}Q zN&oWHx3yoFW}mCS`~EY@N7A4McrbGL$z9j_eqNrxc-pwZbHBH=e?cs__Fe5Np0ASe gm2ooN3W$~t;)na+lWWWUW5Ew5siu{a!;d}v50ng}?EnA( diff --git a/toxygen/smileys/default/2B1B.png b/toxygen/smileys/default/2B1B.png index 6a833f522475ed5ad2c10adf3d788c328728ecf9..9f51c6bbf1091d89f2cefe6059ca4c701b461aff 100644 GIT binary patch delta 1070 zcmV+}1kwBA3EK#e8Gi%-006c6H|hWY00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33MB!|Rh+v300Mw|R9JLUVRs;Ka&Km7Y-J#Hd2nSQczl8hL3<#v-IF_~RdtLVU9Z=%9j@$mmFP4oE12afYKfnJ--~3mlCf;~hwXBP>KD3xn zR??!TO?U1@`*=Jag9!Gf;+A4uq;r;TSs1=mTas$4VPRogWiO%_5s(>+hF* zkx%A@fy$^CwuOa@Kf|`cN-<9~FwVOs>fyGA;|dp}e7WdZ9w7_(6cQ1=ltw8gy}W9I zEAxtShQ*I|gBR_Qu>pDE=GT!4;oc_zGBP&k|td>7zsqdzXdALfd~*mCj*~iRscznMMYDcfKwoQL$bG|I&tL#xxJB3z~u~_{tp?! zyt@m;5#Kn)iyyjiPnRPh&NQ{A0_ppk$}5nPKabTdvIx&{?u6eX#y`VOk&z$ERP*5$v)trX^R zC*95yX{a7_mX;PeNhq?NEZk!lz4*jnp+~5)QMfcV;N6!%Cxj#kfzp8D&Bw|5D+NRL z=o$plVVmk%ZWT|qZmeAB^@`%#)~i*k$bZ<_oTyl}3;P)f`@@BEnKqoy`sc3QweB{0Z4XMgdAU2>iCIF|zT4HU?a{us zyDh?pB@!i(AfO?9G#G(Imk`C6A%hQ2%o0&XG=S^1UCgS3|ph_mNRI5)PDjGqxaMEe`{!Ifl3}`odHEuA|L__Kn<^0!?q_>;>04afMoxeB=AKQ zI20$AoXV%txa7D1XTy<@PSG^ZabY^javZ$_XDFH>DRgroIx4W7z;xiXi$K;~V?fBr z$(k*+ixUM1ivmfO%jIx65_a4{lID5d=U|u+(g=B@Hqjr)vR zzf*#70(rU=f>lhVmWgezmMD}mvZ579I!uw4<;S(C?ZFKA*Nqjmz1(OKkQv}PC09rF z7}!z=quf2%kgtfm5uSHVR1|GkcJz`3Y^cg{0(}V^rXf&~n8YP%sY8;I3PaPP5~UL? z%h9wH<9LawbF73FSt`j$(WDYpc$!w2M2t(wQAUZ%e1hd-9d)c~dr-4=P`7I$yEUxx zK&&9SK!c8(bDZIN2c!!Qbi9I7#HB2UxA&U1;gr4YetDLnm4R!H03+!-7QUEY!Cb*U z8;N#MqDV`=2Pp0sB_~A`F^43cS0Y3WYy6*_k;oa+Z;t;oOZ^CSpf4`BKH4k~53te1 zxM(y2$L{rG*s67^EaocToz1F8U)|aK#{S@V<9P4ala&Pd^Ypue>1&UC_Q@gh!XI|m zr6&VtZq3Z3w+lB;{_yhV&$qtQF8umw;iuTzJ+r4^U)#*Zd}`KsX*T-hhxea*e{Row z=lq5%Th-Iza5V{Dyh$}rt$pV7RjT>Ys^q$+hSk-Bn-_YUtm@6!u~!1($nCZZ$KSf{ z<<15U+$yCG-RZW!FZH!*4X54)Eq`8_z?k*Soz{`th45)H392oxx D0JEfw diff --git a/toxygen/smileys/default/2B1C.png b/toxygen/smileys/default/2B1C.png index 94275fda83f655bc7fb37cad8094c2a62ec61fa9..25ce49a07e4a4abec3e695a762e4a8611aaaefb6 100644 GIT binary patch delta 1025 zcmbQlxt?Q!WIYQ51H;x|=C6PhOS+@4BLl<6e(pbstU$g(vPY0F14ES>14Ba#1H&(% zP{RubhEf9thF1v;3|2E37{m+a>T%n*SKP@vSRiUJ^AXTBFKDU5@!J>C+XzZoi1{^idMQodRIF>xN znE3VDOuKJCVhieGj_2-oGoIKhz`fgJ+5G$U7qtcG=&Whgd z%l6qUfBWr}w!O34mhK9X4JxS!Y|P*6cdMo}XU(J4d7-?|Q%-L8tJG|7C2fE(Xffq_q^Eth&*1p-a-fVyD)d`>t;{l(hcl`S~FIYQWm}?QD$i zq!f>2Z^=EeB-Yfr`hH9OiCN1tTX!h=x+Y9uHQsW-V6A{i?<($jBF2kBNJAO6Z zxqpKFK^e=k|ITh3UcBQfG~x5>eYE^;>V=nH8atMxS$Wz`-65-$ar|L!>GgWuutJWV z`}u?%UO42Oot$#uF!#L0s;3uD4*$)~TJl6;fn(ESq246LnxcjNHy7r2YP(F2eJLWh zZHHjf;|L0PT znCbU(ell_znlE{?EHMx0D%BF#h?11Vl2ohYqEsNoU}Ruqq-$WVYiJT;XkcY*YGq=s rZD3$!U@*hy{w@>^x%nxXX_dG&ycA4lo2aPH#lYa{>gTe~DWM4fB6Pr} literal 1170 zcmbVMUufM_7*DCzIoz0gm~|+gr3miNO>%N?(woq0mzzKC;%--O@79YrG)>O!4Q+B_ za@w2rVPKtt&Id&%6$?IWDu{Cr(rvJRtjc6;DC|K{*(eSMLu3w>&6mxSwzv9F_FzbI z{^a}pzTfwq@5@{Leb22~y>T_eFl$OXj0zpsrO&Dk`hMz&CuWTd;5upA5n?nK z4tB)hPPufLIab1RcCU0+t3$gQgt z!&>r1R|U2~NT~9Bv)SaDSq?|TJX91V`?ojCU)SUmS2zhUBc$?i&gaq z*#t*bj7M7)(BHrW#|<0;eLx1!40?f!oAKuKcow5IP~?px*NU(Y7V@ilEBKePxo$zz zpl(tHH151$SQ?F3rZYt`srE&z`+suAQ!#v69RDen))uY6bh=#nbg|q#D4-n^(bm{- zOo(V(&6NzTn*22L{@%6w!6W+*%y0c{{_gGhwR4-2KOd5vZO=DL)twt>$KO0^RyV(4 z9qsJ;aNXRA_V+{(es_8gcQLxU>-CxJli%K&-TnF2U&d#D`0K~*mpW$L_EX2}zcm|M z+P?YuSmFG~pO)5t@#~fs?>v2D_JxN#dp|MGPk+_BN&)lRubjE*9y!?Y(mPWh96pH$ zk4(3%ul;@N`pfP?MsK_O(WbXIyqoC14-1#4rrVx)^y_=)dcJ2Tj;oCaU%S2UPJZ75 aoy?)f4u_YnT|1EeB}!(W@n!Lqseb_B+J+MV diff --git a/toxygen/smileys/default/2B50.png b/toxygen/smileys/default/2B50.png index 358da2bce9ae40dc2c92a0eb19933eef897f11ae..d08be3443a940c197acfbef91f368ca8ecb46d26 100644 GIT binary patch delta 1407 zcmZ{idoe8vXWDgZDr zR@{dcsUQar2Y|{e^2`1hRBIl0_3;3R)&q!70$4(q;=cey-~cAb05;bFRKxP?_SynS zto3#$IU@+dD}u2*Fj@%11u%32kOn{+L0k{ZToBfQpmv2tHV8N{UkUsNz(VJA*$NXS zE1>=#5opSfAT;aWpt=7beh!F`kBsFY;$CF@n(z@S`w?+BBK(LRBN~WDMDGw$x9DS% zcxe{B93q}XgzewhAexFn7P*L^8DTBfi>KqiR>RVOxrldKJex0`uR*>wi09ej*+TI| zgs|U|{|5+r6@|S@{D&~dH5GIKpR;x0)e%7p{M86^)v)l2yx4tgjsu^IVX?UbA;TbeILnO7VC8h>eol5a2at@5CiyXZ{F$0z;im#as1!{` zR2W6`EG-PcIz7kNA?pK9fitNRo8azKw7f~bhF-v{>@c<0KlosTRy*geLtN*Ou;xe{ z3t9G!P_Ub$a(-T{0e*>bGkp@mZ2p zvCcM|@xFQxjek0{RQCP}`-0`fl#?s_tGN2iHhjh=s`SA5K)c~XnvDxyM=O+sOrIT8 z_S4w982&@G`&P5RN@HhSR5zwqQF5tPM9OUPVBc3Om0k9>)iy=73mE-6Q^C!v z=JJ9K*QiPxsaxCb3I8PqqtKkvV!<*nyZbn3-wpSRd23w*lymEn&*N|Q>68!49#+at zAh~rB4xih7b6199L$s6$lC0oe%DHXbl-7NHna`;h>rvX4*W;?)c96mhPSx-(uI1AE z8+EUKkH`cc?DLvO1gz~NvM1qfVwNf*j~P>zF|rB?Y_4=~&z9LAVY;JAT7pG3;E{LJ zUi6c0NfNP_TRs^H_nsQ#B<$c#cfO?9suS2V7C{fWhL87uvcuG=(2vAqY`mY%YY`i7 zWx>2>Y7ybZSSb2KYY0vbB(-kZr8n58_G_Gjm}wjiN5!ylAk~D9AX-KX7+L*X_D|Wb zFoG*S#mnIlTXMZw8W#6h443(#_f(T(EuoZ7Uery~Ypum{L@u_$TSe-&UTQ!(rAwAv zx;@W0H?o8tuxp(DV3*qErfgo>$*5Yt&sjRgy+*X~^4RKYebz@)GtLls^!mFCgO0Ki zO*7g%3wC~1=AOkj*{94#&&#;t7_6k#S`DW&gOkp!O>6FzZdCLN^%tqXaLI2Mp>NP- zIQub9_%kRr$HOUT0W+MLnK5p=G2ViN$Jv-$+E|(!;&3)N+^u`U?f)^*PMrt|jQW2= V=fdu^Of7vm0D`lpQ-vcr;csuTnoj@# literal 1461 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYa<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3owACkt0MQwtYE0~aNz-V}1eOdseNeNf^+N-QuTU=x;TbZ+*%Us?JXTB^6%Z5jAJf6g*SbBbzL=IC~=3c)ZmEa()%SH zv(q>zc1icO8(S~7?ht8FDhWT*%66nxH_1_J5x21X4X^0rHO7-q-g&*VvQ$Xe-10-= zyPd`J?%N)25y)_vvMXLFhv~M?%~lTnd#2J$W{Lggj!Qi=?#VXY`o*>8r)udx7N3uo zgd9>nTU#kSxtsUz*&X5u Z3~$U8`CdKSei~Gedb;|#taD0e0sznD7Y+ab diff --git a/toxygen/smileys/default/2B55.png b/toxygen/smileys/default/2B55.png index ff62f1b4eb8e977c9529d1c7eb7e2de6dcb85eee..bb71bcc545be569cb07d25b911eced7e53df516a 100644 GIT binary patch literal 1466 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>cZ1F@O}KA)sCe0-69Z1ZWO2 z0-6a&K*J#jXayXZi36>JAfTlX1hg8CfNnrWK=(kL1auX|VL-Q`A!2;=|Ns9_OD4Vp zhJ$-akRK>4k%36tr5_iXfP3qhBD@bUZv;?Ea?E;d~kXcPN* zBZxQnU&yCmk+#dfzMN|g5wuAGn!}jn?d}p>l`hK$iSDYv8(trt@f-qusu>ecb}D5*vHWAtJ(w^T{m<@F3X8|^3FFbzpd4b>5Q1( z$Gq~}F-d()j5k-DJ>h(<_R-UMGxr=mP}VK_E9&4I?nRd`t4uW)lsmEfgpz~k z74F+gTBh?7cOO1wdEUnGUAeN{%WcaZbBRAM+Vvw)PMP~hNLZ-#)MHa72gVg24NY;i zy!`q2?8DvL&)jI- zc2dZ(>gZilmee1`a?ncQtNS_59eY!i_B7;3Yb;k>F2vdp*TZ2kZE`y`_5VS)g6xVn5c9QAs4PN{svs|*oN{EYZ`(*{Y|%Pq#8G+HrR;)9$qzO)8$h4@+CiTy&7Q=>Pom*Rt#Tc_+%t@4Ty|^x@H| zYd~M9mbgZgq$HN4S|t~y0x1R~10y3{19M$NlMq7#D`QhDQzLBy11kdqwtF9%Q8eV{ zr(~v8;?_{!>|qboAPKS|I6tkVJh3R1!7(L2DOJHUH!(dmC^a#qvhZXos6b`#boFyt I=akR{01usKHvj+t literal 1307 zcmbVMTWs4@7u*%HyxX$r&Upq4QUq7Jd3q@^H; zC}I{SpeRj_ufSdmtIx>EVRJb86r0u*r)cRoi%K4$F|4=0m>1Kd&;%)%l~q6f`^PtN zAWMFH$P*=_c@E~}@RSY*r(((U)M(l#;r)G}x5y#_1)3r#Dr2g_7XA1(FNXy?asrkQ66qEIL}3ofUoX9>#Z^I04;?LZn1V^TH6qC+)~)fjkaq;)xO%9;u+ zMlq#Ln0_32x)Xwuk4E>1Rbw|%C}l)Z%oCK8BoxJpYg^ke6YyU*_SH6$lX*xaprK9Z zX;hEQu^JfV?%swhMdS^8QkPLt#4%n=Pbg3|BfK9+U!0OGv82n(F(HcU;kb}MQ&doJ zQv)6kLs6WU@o{vGV?V5iq`d+i;sv+RLs0_lC4;=r-RCp=fG)Qq#t29T3lHre@@{Jm3ZyaC}HsC9PmQYL#avS{~~1X()wsO#$2a zW##?syWBlwFi3F%@&Lu{CHYVgMa&?{=M!A`E>`+KIU|rWgw-7XX_nd%>Of1}YkjoY z8y={liP6z$R4&HRoZ6m@@WEv9hg7vo{5;SyavSfiD=UFBEpyX>N+VFut}b9-Y%Q#R zSsekF4wS3j6V--tB(U7kee3qdU-l2zzXM$5#+&x5-G?&G2S4kYw@q)()m3^y=9NFR z`f;-T4mVCF&)cpxU4G`ia#p#H{MKy0(NgXRRBbEk6M<#>+(~SSO)9qbA!@Ub z5NDc7eWjuAf7%#mFKrR^OAr4%{j9Gk7rxFta(CgS*|rU=l0AOp7xQ$-tMBx$MC(Rw zUaxL8GHXqx2cO$0e%f~K1^d{oYo|tDW{!4#-P*Qj&%M<+uak=pynW%v_ZMe!6!_%z zj)PAJmXE9!X0OC1E;qc_TE2Gp)T--^(uKfUzdn#U^VsaWeGPNJ+9=}wujUVj`Pk}t zJHM2a@a2CEqi4B`cIb_Lo1C76=D-CY>|xA&jf59G0zc>21sKV#?PQss(n zcQppeH&%s2lmsP~D-;yvr)B1(DwI?fq$*U@=N2$9SoBT}b=-Daf#*(fmmH(FQ`2LM zV{d-NO}DLcS1OD-o?Cyo`=El!rJe&XpMRHsIs5;rD@UaHS5GSqyw)xze99~8%9fYs z=4QzEA5RuY+_&!ybwjk%jo9YZoMrJP__e8ix)jNFndVjG4#{@2?3EL7@ zH7Z8zxmN0_kR#FYb%ol-blo`#FDLv`e#UU;zLDylhMcz^%axYB;cSTeQg9=F)!aFo zJwICQ7MS#I&;Oe{!xt%9-ry-um-#uN$Vs!U{`phAjrHoLiI-*HoqjrberU{AGk#u~ zoJV>$x~8u^t>UYqRh`#VpWb@0u_}DZ zymRkF{)EmD{s&*`PZT`68mrLv7Lf#7i@S&p_)`OI#yLQW8s2t&)pUffR$0fsv7} zr-8Yyp-G6Lft9hTm8r3|fq|8Q!Ge{)%~5pZ=BH$)RYG)N({S;NbN56=buI=5Pgg&e IbxsLQ0RCjFBme*a literal 1106 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 Oe{0kylSj3g{(k_voHVuo diff --git a/toxygen/smileys/default/D83CDC04.png b/toxygen/smileys/default/D83CDC04.png index 6521d64e4a87b150adb1a09ada1f105383b524cd..fb1f1f619fde164e89754907010c7802d417ce52 100644 GIT binary patch delta 1459 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 diff --git a/toxygen/smileys/default/D83CDD71.png b/toxygen/smileys/default/D83CDD71.png index 84f20f3131c73080f0c34f25b8e0fa53761292e7..13c53fc467ad5f204d7cfee5ce5ca026718b4ebc 100644 GIT binary patch literal 1262 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>CqW_UzenujfyATpw)%iZdp8ySvEDVR2*!a@b2eeO=j~ zvGZ}MSaf--eqvx?YOD&0C<#g|S12gTPs_|nRVb+}NL8rFEnr}<=$#tsIO&c8&z{d+ zHjLg*P0uTOb}rRAZey=&`1otpo|9=YrU{Byg0>%c`TczS%l7}Pt{jo(Up=if@LIc= z@F}mTD_dTko0}oue>_>>;JuJLK|S3i=Pv1TAKsqj9h7UDqm#4xjjM<5wmcgOZklVQBs!FRs-`p$wK?rQRh3DrONEXxA)LaR=8+U!hB zeB@+pT>4~kW#R&(SFO2;4<8lZFpJQ<*2#A4=lag79cv%%ICNAaUao4I`w1qyRu>oj zrGZ_p`s-&sIJsxhoWzfd?>=y?PRVhI+wrqF#p%a;VFpP_=`V$DYZmD!{P^`;X9Clb zCV?foGqMsmJ2>@5b^* z|Ek}?n@cL@$8va{oCmcfj6R4p0zJkn$M=>cR+K(Ud5Zy zL34L}YP(}Mfj^-$i2uQt`V$Oa-{}ds|9K#yU-#meZS1<19`CFc;o~~L-v&5X^X2OX zo5vqjTv8^Z(y&B$qpIH|CVu@H)!xs7)z|+L=enfM$IzR^v&A_{Jnq!w_6f2}pFK#M z>BVln!P4~H#WydFKb5E2e~tc?cR2Rf+sf&eD`Hh%{#&+l*V*Wg8}`+5Rrv2K`Ru5* z&ToqI=jciQ7}WoD?ftm&ixe<@R(ZNOhDcnFJ$+KB$w0*AVpzx=M#nkx*d8$~di3T0 z|1>M-4ti@mmW`{ChBrzsqH%&i1SAY8QW1Y%)DC z^Ngz=>uGn-q_VXU&UZY{yc5lCVgDFpADO%-MtDzbu#R2sN1l_PZhK7r$zQ@HUvckR zax2gkswJ)wB`Jv|saDBFsX&Us$iT=**T7uY&?Lmrz{=Rv%EVOLz`)ADfdBHj$tW6f z^HVa@DsgM5>nqa-YLEok5S*V@Ql40p%HWuipOmWLnVXoN8kCxtQdxL16;uc@c)I$z JtaD0e0s!ok5&i%G literal 1221 zcmbVMZD<>196v41YPX6L!FE)8+yoVx+)Hwo+|jH}?k+CQbTJJ!f@q%HJxz}G?m6$S zOIolmYMC9u!SKU|2t&|)+QbijQN~h;Y&v23O@wXW7rzJ&rdVoBe6DS(AId&>+}-nX zzu)iw^8Ej|o}GMQp#MleK@bD!<4O*XkNHnu0KZ4>cK{xSJhk9Wp&73V97rT|RDxvM z1Z9|mKwo(C8#qP~Ju^nW;1x12Ng6Ukz{iAYrj6MIF*aVafi??0vINV96{mjt>Ka8F zdYqc(Gfc*oVZ}JU=)kGP$-K5WtBE=_evBNeNm#&y9w2MxoaIWjIJK=S;kmy}Q{*

B3;)oQ334k4#Zv!W>a8XOnIh@iV*d7u`w+@oCu1-hDJ*q(tb(pLl} zH1EYJ?CDMjrk%;`30v-NqHxOS8n9_L#L%Yc$F+@iy&U}4jeXH>e!+%x4!UUG(eQc9 z9PN^E?jCH&2V!rela7H81V55rIk2^zl3x{mzZ!&7E-JnmF1Mmv1~$(ut}a5 zSXPb+qRe$Q_Tz?Q30~w?MvbT#uX3WuM3NCzjLHfhi->HOo3>mJSQ_lwHL%?su9A8n zSCSnFJmlmNn(J0Twt_t5R*+4~Qv&(iv|;I}>OSuu&rY-obc|C_PdUgWxAQ9*``G8h zktmZ$u(FChz;VYIC6&Mt3wS1qYM9#P>i;KaGxoB-50_4oOMzx5-MDpeYjCi0`qh=#Ila&xe(#gP z&VixMrN)8!=%M9y?uo!e&k{IeT|HC0wtTa}e0Fvf+zku|T3heju1wGOU7r26ksZIV z3a%f)o9(sR#QDv;%MH78YvsgK-}lB2#q5!d_SS{fjXS4T!JpE1*;aY9e)wR$e1~a8 z4=3&oTpk+y@)x#MeffHQ?Tx}~jYpd|e*Tq27uPqg-uo+hs^>@gt>lZ`*`f9qA27wK zo13q!uYLS(b2It=I@j~`3GUp7PrknL`{%8{?~nK1IDfGD_%oHI{-!|&CRQFIKC+5+ T)ceD;{*NWCPAXTD#kc14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>>|JN`1@2T^DHlqO{x6^X6DYoX&i@_jfo}NjsP_NtvH!n*B0EOqxs(Rb7vd#BexQ&*2A{hzq+WB6LNz1jUzH^syES8`5%^XTZ|@cUak-~27xT`G7Z+&s!F zW8<=$Q7OSc!r~`iy}=~ieJrio@OVeVsv9bcV#`lf^{x|>#eIom~wv#weccSFonch2Fm z_RwRyJdRuZxAm0HyIpYSfzTxXew$OmDU9=#(w;59q|)m;E&T9@l)C|b{U0aWK2ZIA z!lGf%i&M|1c0Sne%a9@>b)mh-#Wm95!LRp`3alQiibt*=yu#J?;A6~p)y55S3ezHe zllr43BxFzcrTm<6$Np5W3dWoF0{mS4X0kTK^>Eynw@S7tJ0fxZTn^9hi~nWj%$3mK zvzzX{bcWK@QyyJ489U+_thBAJ%wb_f0;BGf(ug=;E4`qQ)B59yV76M+v#k zRcmvz%zr>Gs-`~3Q((A;ICBF(T8J^xF%aw8b;o8(!$3kVCBIDUP6<#{zNUEP~Y!^Rs zbJEWXCujd}Z7O-va6v;YfKO%F2aTdn1}W_NGs8P{gI_Azuuq5@A^2Em%o--?mip+G4<~ckr(rJlzep53Q3=${5g739pl0ST`l?gqUV7L zwARzbF+}2W?6I3tP5~lp7vfF$JG@+H^(h8sE_nGjes%tPX}{N5jQ>lL!wgFfl)Um` zEskX7p4p{ZxOeLFPbNQ7_TGH=ZI2oYr;{Mt9x)}M?iT`|Z+VqOpUY+46F19KTXu+Dh$|t#pd(?JCP=n%rG-xg@tS z^M9WE|9=1Xd$upWtGVf+CIms6qr3S8TsPQH!#(ibzIa=N%VtYRSp!;`3qk60ax!J5Vtbjarnp60$6Z!*h(?gEp0X}xi@-uNATO&d_RG2R z7%EFFw$B$MVmb#3^6oJM42;E-*|B0aC}BO_Xjhqm1PZW3w5$xPCR1jy6V_j`CS~`jwnF2qCAViFK$Ve7{VLi=m^RAIW8i2NHQc) zWZ37UNsf{U{LU4Rjl-Xa)v`^aJxDF(=4?k*nzgV*7|U> zHatLu6Jx;97##dbMUcAfQ9hI`e|`GpUMJt*_SwNa^J(OPORwF1=GesZSNDE=_#0_! zf9jc3|E&#kUxcsD{ngOg+WE$jqn*p=N*DN-0_M~Y?1@(n~n+BI2Z#ucOzqI9wUraYl*_WC*Qkk>f$Zy{@=UjCKl=+ zS(^Reqn4QmzyEXdP|>_F`{w&C$umRw{f|EJ^OwM>x1Y`3KKjn${FB2!g0~$@RQlr>^!b~TWNp6{uf1sI6oaukN*R2iM7W7 diff --git a/toxygen/smileys/default/D83CDD7F.png b/toxygen/smileys/default/D83CDD7F.png index aa5dca17218733e95b33efc39c7b3c942438feef..c8f64329b072067a952cd72a676dde88a07f061c 100644 GIT binary patch literal 1269 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>-w z3|;>j+W#^%|7NKF!BG8;q5KO&@kfTd_Y7HY8PZ-eB)?=xd;wAnQ4i!oM1X=2ahM_h z|NsB=^Z%Bc{~4zL0~y%;w{ic!Gf)15Oz!^k^Ur^f`At9S_x?V3|1Z#@ci;Xkzxaot z<2zOxGd;S6fKC-J3GxGZ7a4dyd-m+P*YhVlu8+0>#Tk>l-Cd-a60R%(a@b2eeO=j~ zvGZ}M@*aJBVFCjKQ)5*~L`hI$xk5ovep+TuszOO+L8?MUZUF;>Meo$mz-hM?c>X*W zv1OXp!g9Q@>F4X}zi&QTH&0%3{o}UVKaSpT%5alvy!?Lt{!9G-R$X0EE*q%)`_U_N z{lrPJdnzhE`|dtrmbd-3K;zz>7lWL-OV3_Yo_%P0m3LS&ua1t?>fEC;%1o=0CU)@M zRe5c@YF(f#_g>*M;wNjq@6KO1$J>QL`m>|lu6M%v-%mbK=E*T%x8XY1$)!>HmYgSh zXQ=6JTIt0;e@!=2=tTXT?pb1Xhs)YSpY1tuV8?x1&-r<;OJW`hP4b^^b4hqZgTJ8h z=isbK&r~k2PTa9+ok#kSKPSaLOv*LlQ_#Ovv3CZ?yIOXJ#TOdv)_8<0VP!d2pT@{F zfy=35d*sSCu7i&)-+Mb9bX%|?WvlqN6|CGjvO)U`=QZ4J_uFnJtPBB-rRvEl#5{!CKq%9!Qo5ekkXYqpH zH|jU2?g+Ip>-lu-3DZxZ9Sb)%-fiIK?>TTVUR~=#E~CD>url`%748oPPi^}SWGZb| zoT~d$C#KRlPyPh^gDMuk|Lr^hW#x?)$9jWj{Q0o^TZxsB5#LI~xg~a~^Y|E!s!fe5 zyKB+ytMPX0M21Oxt-8hPmLEPerEg2IEBW-QUeobc2=g=UcNQ)am0I05Rm(1WbHV&G zN22D6FCubg^ElRAym#6C$@^1vFSdWV+h+as_G6vtA8kA?ef2CZyX}8;`q#I?3!)!g zIm*5=VD8MmH}@xfVQ1LhwTf+7YX>lW)_J-(hDcnFJrgP27$D*L(08U$NXi|Kb|bR~ z|NooaFE5;Ts(R}>u@C3@r%gS;eke5d#-Uv+dwUn$c=v4M1pHUMUo`QhT#0PlJdl&R0hYC{G?O`&)mfH)S%SFl*+=B Rsi3lm!PC{xWt~$(697g1DE$Bc literal 1222 zcmbVMO=u%k98b5pNIPXqg*A1BKBkuyO=jLCnPi5h?PNY|mZmLfVCzLP$-FdUJ0H$W zOCH|&q<(b zN%CSWLuYIemGy}g2hFZb<%%l{MNT8fM!`sx#{>rQR8Tb*O_#4G$wOWq%lii*rYD9BNmip5w6 z#RSWVOo!tnEXPJTB`R`qRE|I>GeSa9WI<5kFqMijsdxvQHeF9Oi>PB)$9CJ;#6z*X z=pfazoSbDXb~<3DYGj`{wmH+gHA&R!ghq6OdvDf=y?Idhbbug&FJ8L##*b&-9jd4LyX%b|g4k1b^1rTb zw}SMymvh74uRV3P@0a}F&HF9lnQ#+bxpQ!?yjg4XpP#_J#}@U)tv5XJTbmAe(Kept=oVduXhFd=dO>g2yY4g OpCv6%NjFpZ%l`nU3!L%* diff --git a/toxygen/smileys/default/D83CDD8E.png b/toxygen/smileys/default/D83CDD8E.png index 3e3a43e06a68b5cf1486211a0d2c52ef12fa2ad9..f615013d991661748906dbabf67163af5f63a54c 100644 GIT binary patch literal 1336 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>|Ci7C-(CGb(Ff?B|BvtgaM%0)?9t!i1Z20( zoV}+O=qd4%AU{yRAOp{5&z?Q^dj5pR_0cwxG_Fg`1!o?-O0xoSl^@gv2VR}f`%sqwT(*v$u zcV+UhQrXrs`^kaB+}CtY)~tAP_Wz`&k~5481(OzZHYEx5uQ_AGbL(J!8u!LjBioMl z9@$3AS*Eov|CHVyukw!9-4VRkwrb~>bY;)$bDym-+x^J<*GJ6@wuP@BHHVxy$Kn2L zyUJfCHLtEc6|#GG0#oNqPZ!4!iOaF4jx#kGh_GJFexGzuY95>Ewi`*DQXBsLcb_@; zLFCz_hI+rn-!B(5q}i{Vc}PLCH!+B<$FSe#SchBk?Z|?2e>=}xetdbRjL)g#XObpM z=ha(fazT09cE5EKy7*5amC;K!cfN>b={}9nmTLz?)L#@Yr3Rt5$OR{l0e(U6;;l9^VCTf@aK&fP!_k{}y`^V3So6N^$A j98>a>QWZRN6Vp?JQWH}u3s0tkiZcdJS3j3^P6c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDj~FY+`QU=xFHd3UmR` z-xdZgZjL}7n*mvtmafK1P`xSSgqc3jG5Vmyfs|NaLckOVV#1R?kOR*=sd>O0Qv}Q! z6ZLBR7#J9RJzX3_DsC;Atn1J0DA0EQZcKBwKuZ7%le3}w!X=s7Q9OzQB25Yc9ciJAIL#@PU2Sg@P*!48GV&q?J{>`)@NpIeBKKvGMlJ<;yeH_vtK~ z>3b|WgJqASyvW-yRn4A{ANTB0 diff --git a/toxygen/smileys/default/D83CDD91.png b/toxygen/smileys/default/D83CDD91.png index 2f37aaca03cb70a8cf7ea8d8c15b4fea3cb2ba1d..09b02e07aef9b3625a6022d73516c441f633b0b9 100644 GIT binary patch literal 1297 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>^Qx~#BFHY;SKML`^n!RvkRK=*kb&p3XV0E{ zJ%7UE`e++aoH5DU-9=$@wrvBD!(QU)>&pI&osUbEXVa4hYzz!cwN)VzB|(Yh3I#>^ zX_+~x3MG{VsR|Xj1q=)py;DOyH{Dg>xl`OF!h1q4E<2T= zcqPdD#LM^Z?O%r5xOzS6-7S88o5bz2S6VksT)8v!_D;dKf6I24DxFyE>2W1;Y2mbI zkrKO(W=(pu`tgEI>o={INRvo8rl|C8Pw=y|-0S0b*w?bo(4F%5w%P5u$@;8JdjwxU zWM5zWc%_|9<;=!6V(L#Sn>LAM&Ju1dzT4h0wJa^`gZk$~4yi}DZyq`%m3_j@BlC@L z>6y>Dowov_+aGa>F0YX);qQOIS~A7uh~L6Mso8>Yg$GaUQJVAa@xr?cU4MVL(YU9e z_v4qA4|&22a+cPggnKF+BOE&Z_QxGyUBcC=u)29ASKES*H@2%bZg{6)wrAE6xne;U zZ|7C_YvdY!Yx{D_E8Lbkr1?S9one8vG0!&lYkbFUx-LHF*Rn+W^?d1muM!saxpL3u zxZ9-63Ftap_n}Di#&<{G8zrs34Jx0fPdyPecRCxJ8K>|YsZ{CANm@o))pAX#lG`&~ zi)MBkI3M6nd%|?sWnzHHDxC<=ylxu19^2ud8fn^GY0joJMj z2lL)Xv4_MfJU%Es`0&8xV07PsOa*txqA5#Q3xBo6#Ghb)@RVcDKYqytfp@!}c*Mx` zKG>am=h8~m6qA*9Q!3UJm9VqE-Lc0t{a%vz^a-y%D>16bc3dr-%v15>fR&e#yjJB_ zeGLzJiwhm>JSj)yTQ6Gq0iHyU-UoC-{`e|7x1yhZ+%?%ypB{JxgG ze|kl}S0%Q5*5+Gpy8K_>Uf=LtBwE~FrT0xwLB>DtOZN9Scg;(737P~JwI{%ufaI7^dv~|2pZ@;$xp5%RHE$>R+^t)#9?!@|Kw^dv>m5 zIwm&neYLln&+4Ynp#p6OX9-`=5=h!CDwVocxl7~JpKA^>@4szyi<~{-Q-RN<%aN0= zL5+cP?6+m;IJcbyT`=e9`G9po>&XTq8|6H_V+Po{#(DF#nhKbLh*2~7aqrZCO` literal 1276 zcmbVMO>Em_7&c^U_v5ZbY*K@Tg=#&&I& zB-PY)gR+7$X+mhHNgD?knk#;s6Zjr9xB8=Qb%EsvWRw ze?NZS=Xw7=zd1VmLf5XRb|DDTl{zeB;Mn6nJ38RIch!;L;IqY?J*JoJiev(WQ}iOh zQe|lzWPqg9rf-2k1o4#AY|hT5kFc^{4oEI0P%RtK8bJn!s)i&_0vjuWaaD`s4?bVQ zF;$7=1-bzT023YQY^2kh#G2Jg6igXWl?;LkkVLuc#U zlwmzeeQjfyyGIMTK92%#`t6?7-slTC=m+@ZQGP)*^(xMwyp|w zTeeW{5wemH!yyT#`=${nxa>%=iKsaL@NMOeHAE4Q!iud z`DN8D^dq5gl;k*y7oi6*?ieW~IT$ewXAC2T@RqIee{x1ZX9%}B{?jb&E!ctXbhGv0 zV)O6-4W1Yi9*rNZ>HP@u*g#6)velo84|}Dr4(%*FUL3*ZI&L_r#+{3Qd%e!-m*-<| zE4k+W#rM2Ur_Z_6=&TQ*Cz_eP9fv$~T<+KNDEiqM6urNvr+LeRqGR(9d-Fc;wO2sl zkGnUYSX^q7^V3<{<9Rmf;m_~h_Sz4BF7z-x^OyNA>&|ccFzKV;_kOqiXA~WIARn`k z{`(gf`_MV=I`;H84a0dxyR-cLyQjZ!@A_j0~#q3NuA`j_Jn7YTL zLc{z8dB1bf=UsXJ>9*+|r5WCB$c)%$?eHd*o!S>jtue YL{_G+d~$Exa@_w>N*oqGOcYN211;aM8UO$Q diff --git a/toxygen/smileys/default/D83CDD92.png b/toxygen/smileys/default/D83CDD92.png index 6727be384f71e630bf1be1c21437dbce8ee2da0a..fefcd76e82da8e38264ce7ff1f8870a9aa504cf3 100644 GIT binary patch literal 1289 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>32{wYaWQe(`S`_W zVi%r_o_8#2_MwOw`@*K|3YoYysCQ#P*INJfmA);@yqgw#HY@-thNuT}AtFFQh&bGk z1xN4x`27cH)Z*j!{{R2~{KL0NyRSZb{}pKR%a7llzyJ2@_utrA2Y|Nx`u!(r+HRm- z&)$FO+j8dl`>#MtU%vl3XZMA9doN+yUNPOH66kF4k{~~j*O7tevuDqqdp&=`Fdh=jGd25mG|i53lkU^m>R1>B1(c1%M}WW^3yVNQWZ)n z3sMy-atjz3EPAJgI!?Q7z;U;@OU{vvyq{md87zJ(U}e9?1)f-6;`my8UX|%QL@s+|6s2{B+es5J}mbCt|q zyX36-@q=3XUdCNDIJ|&^&+znwx!rg6o_uz;;6hcy%|oj9-sbK0eiNv0KzgfS-3c@E zxK}Svwo9JKD|Cui-*IKxUa?07c@da6B6}isp za!}w-D`-rNa@61o6<;G_y!h9me+tSgOm)J~{CReg^^>ZB)?AfWAyXRF8=LF5xi2!e zk&T^qmT|=@?lNf`Lyzgo2C79OOI$poRMzcNTrYouJ;9FY@_$8UlU2K$WO|RS6#TPc zcZpRjC+qYp9;zopJ8v&kdYf@COg(>Nx7w4hS3MXOIwxh9O=9HNk8t;#=B%3kZMw*X zxA@$Wnal=Ey;I2pYtzc{JO3Toe-fmVESzHba4!kxEy=< zrdX4L2cK@;rPM9Nqp|KVzp-oa5t-vOpK9mbgZgq$HN4S|t~y z0x1R~10y3{19M$NlMq7#D`QhDQ&VjN11kfA!MSpWb4 literal 1277 zcmbVMZD<>19KUW$7;TEfscc2rc@?y}TL8q~a$TY%MFoaBTBaVM9hMIQ=rMPS!!}`COY+Ka_p&xVz`& ze*fR^|MuMbeLb(VHn%k+2-2GA=5uh|=RSMA@O|h{M})&cTNtqW^&z_?ngC&Cy#UaR zDh`7j5asdXS3xI&>>g6`1NK1nC?n}=Ky)#IvT8tU1nKN58=^D5&@j7TvV>q?-VWd;(cz!uT6I;vSrIgV}eGH~v0;~2UL zu}9+AmQw@SK9tizeJEEO49){6sBqNFiH_5g%j|m{bYy<(+m|s>lX&qOnHdO z@#(r2T*a}XZ5s@ZmrA8TDHzbrVVsP`VlD?o`60q@jcc}8_G?yqgMkN@WGaTO=o;!W ziUoblj$_c%tq@cro82bXta_qg%J8yi;ADWnRn?7a6K&Z!@UI&?qOJV60q`8K^f6O{ z^%!bz7{lB>TF3=LZB*01tR8)u%X`1B1ED=r71lzF9XqGK%5@_fuP`7TI zdZaDGnLxC4Gq3BTjSlE5>b7nbbpz%4Y4n9-iYDtNtHUkNRQ$A6lou?0KOoo=^2Tx=g6 zpurPk!lS_y*7FF`~2^o`@EPCUM_XX)c)&G(*jdh!$YFLJ)-JkSHPbI<)y zn61nvI_4%OoL4{mwKAWWn{YOAPkFDui21(xeCA*>bTjPx<()|QHR2FIz5c=KwS|x` z`0dCM>KbF5Ud$euT5;}lLBzh+)r-aCI*NQIRx{Oe=U49kz3dZS>fM#CT|s_oS(~4J zSbKAH@Dg$6v0Z4}$=8!#|MB34eiEv2?|N3IpWHZ6oc!In@{nkI+N)ox4HBpJ%hdyF zYiKR9d&b%LzL#*?XT59eDwxOo5o diff --git a/toxygen/smileys/default/D83CDD93.png b/toxygen/smileys/default/D83CDD93.png index 47a754e8c5718769510971c6ff46bb77865ff610..2294126fbc1e7c42a11109c8c9c484df4f7d39de 100644 GIT binary patch literal 1242 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>Fdh=jGd25m1U28h6w`$ zQ+-uPL`hI$xk5ovep+TuszOO+L8?MUZUF;>Meo$m=xwtNc>Y}1+QqVXc_d%w$N2Lv z)2H8`IQK~r=k~q4>JllD%4bis)&2kDYX2#G>!dqN-FqK(ci+F`w9{gD<&RgpO`h$& zpSS%S&+k{W#JCM}gSNN^Hs*hwy5*QyM267pcgy+&8C^GYMDBVM^F%Im+ls!{dqQW# z^?tr9zwcbsC&YMj#rHGL+blm8$s4#6py=uJ8}BxJnvwoa;`!aq+Xc7V9{Ke*Z1_~^l9S`v`0LDkuU^}kDez#Oq|>2I zxxs3&q3-GDT|3q!o97CIFImOGvnioPY)1@l)1tuAgOig|O5(rC=df(=IxecbKeec_ zhV#s+B{#2kKWMWr_#l5<`C_9)!^t_T(%8IjO_+J+r?LU>lN^te)4r&jyEoxF{}W~d z`No(3Cos;sn=9a6{5V4F{$IUbKX<2tidWkw_bi{Zyt%O~=U!Iu_Blt@o_yUpk->>Q zDff*EyIkCdIl7U2bNBp=*I3{$6JgY6)SXtT5&HJv;R&)&r`tG{%=F@an0Q)P(Q=k; zt;@f4Z`$h?zc;(H^1fWU<@e`~PS@4X-prlXrT_Z&a)+{0E4%q8Ewx%KGw;68FJ{L- zT`^j>1pWe3XNjkaV~E7%*rVY>hYSQ*E-I$yG@q0b5NlGs|Gz9_|C^_w;iez-Pn&PG zVeZNezIfK0Dzg3R%NM^sc)9v~@lyYJloTq8H00)|WTsW(*6=_kQVgg;5@bVg rep*R+Vo@rCV@iHfs)A>3VtQ&&YGO)d;mK4`fy3bG>gTe~DWM4faT6iG literal 1207 zcmbVMe`wrP98c*y%IKkp&{kxPsWOMVBrmy3@4{VY?j`4L?5@Z4U}Z2dcX@4No4hP} z^{&UT?G&ssSf^MJ#}v{1F;K?vpG`%D{WIw%gZYQ^5C0Gmbufm?6l>3y_OALz*&l`^ z@B3appYQK)UMUryj_!PTC&Mt&{C>Sm*9XFvB0q# z5Hg+NHk}$Tlvs#;#3pz-RuchW6@>@!grb1GtRw5atXC0L_SlY$Blm z0Fz1zN*x{Baiy#l2Qrk6xIrsnNZH{eW$8eZvN=fi9d6zYi0Rf)$F5E7wz<+hxhnLL zNw8nRc&5_(MZ(~0p$A?8t z1JIxz(72PLp3`W=3YAhRL+09C>;L3Tpw0+kbNr`SI!CkvLvgG1>1JzqkV_}Vr=tPS zJz1q=^-FBS^B-i+3}R`ve*ukZZQ`{>zo-ulmJp;zgVBjkg>N3}=93u`Y& zRu|syE*&^l%ss;__KDr)cLw0`9al#elhMmekI0;P{L34~3(x%-{z~#jQU5Ue?D2m9 DF|d=9 diff --git a/toxygen/smileys/default/D83CDD94.png b/toxygen/smileys/default/D83CDD94.png index 0b710e1dcec5b8aa943b078b896101ac31261bdd..5681f7a287dc43b55725061d07b75daa515e8efd 100644 GIT binary patch literal 1263 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 literal 1270 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 diff --git a/toxygen/smileys/default/D83CDD97.png b/toxygen/smileys/default/D83CDD97.png index 8ffef12076a3967001b060b9253dd0553ea4f59b..4c5e3dc9f06aa1f777447ef52fe56c7a61aed122 100644 GIT binary patch delta 1339 zcmcb{^^9wRWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DehM`D_??%#uRMvHdt}d*C$V!5UwHT?25jHQZ$ERF9{cp|=Y@x_ zuRnbkJL>?@1(DPDL`>U#=H5%7Q$BwEku`5$&b)oaix0l3|MYG0_VYm39lri}$GLmq z6E+{a@uY3tv5((=Bu(D|baLACZ8sjj-hb)-iJMQ5o!|3w-+Z9I#Y=+xKw*arJfA&# z_T20F6CT$`+koPXN#5=*@~pf9dx0GG5>H=O_Gj#TT&iqAhMW@h3=B+lRUr{2L5bxG z1x5L3nK`KnC6xuK3Kh8p3=9^%Q$rmm-7(9fYdeZ4!D-8lU?lfBZiRJ;Dp@{{g$X1v#~Rc0hSSeNLu$L%JY@2*v8 zspow+tf`!3HbMJR2n$oBK@<0#Wuh&M0!k117#gk8f5mUq6m_`d=!yJ!F$(caj|&8P z9yB>U5wxiIRH0JE%y3WYzC)8gkG%FX#vJzquO}KFos+hB#MC;+*-zjAo$mq;M?4Hu&)?YPeB$+M1;!S>&b5`3cq)Dz zu<_a?qgA9;RE{gzwz!N;vbLMRva6?)Tp$v>QKvc(_r%NQ&pM;Pd4Ik2HlYMqGGO zsnuf9Jmrg0x{7AS)cX2U0a|szpCc~*KE6EYl}uEaqFdHSzL{QO=s5O)S!ZMHYiRdP`(kYX@0 zFf!6LFxNFS2{AOVGB&j`HPkjRure?(H+vv~?d|0w*qQe*y&5XkgflZ8A;)iV}ZZk22zuw7E1-ts8D)Dl}%QN%I+Kk%_(7- zkE^^S!WttRlDbtF0z6#}L5TbP>%^k6mMD-iG|9$M+>D`upvASKt*8O+UpF??R)Rfo z4h?XM)GdcWJ)*mFV350~8`2bkH{`1_4~oKe(o(ov;6&9&yAbfj9N{A*W_3`67pE#I z%F9@A+{4)LTDzUVamqnBDNBxHBdm=etX8_x&e#}G3C7}Z);c^mZS~fA?X;63a#)|J zsH_;~a&~!OcMWTMDwd>Vj#VW&C`p~U4rq)?s-(oEI7~GWaCtK?Mx=zYS1ZqIv@|F4 zZ*vi^ED7*Reo1}<`*y3X67zU)iUA&gxE&bn^?-;8AUU0k6vyF=`a zZ|C&sv3u-CcW;{Dyun4sb;GZ{k9EmCY1wE9_I>UB4Y3w}77~Af#hWq`md~m?}$XJeH+3fVK zytC)459TGlTWU5LqJMlnQPO_?^2GGi`}a2wzEx`aYIY31^27L^>7sh{TDtHB9sKy` zQ+M=tGL9d81uyCiOTRQPpM#B9e*j?$3qRa?=ESiw_pN7UGuvZR!?0(*32w;4q0M?| W@k;ToG+Ua{{&YU3f&Q$vt^XfUZrk|) diff --git a/toxygen/smileys/default/D83CDD98.png b/toxygen/smileys/default/D83CDD98.png index 7288cbbaa506c63d92fe895fa272a5c97b4d78ce..4cd5f0cc50910947ada55677c2ac2225c3c1880b 100644 GIT binary patch literal 1366 zcmZ`(c~H|w6kZSvMN^T(08-=-&}bkef{>^B4{KY zC^i-Z#Kw9RZLy*vDyJ7Bf>sU@Fr3?ZNzAjJ-f;$X{FNOOa$T~SF8VZj)G zsC^A@H^F_mGk*3^kpnw!p;Dog57k3(bOaViXJP_iPdC)*W}^N7+X94H0ewAa`#1@Q zsei%`7elR9kA#d2&!rF6qP#(3XfTaEv?1&=YG5Rca5_ef`PkG2zUBQJ@f)+&i}d!Jjb%H7Lvvnv4oZ?3p+p58N@&kpQ) zGLcI?=yiX$Kr=GANK4onzOW77r^G44#&}&NyK15`mBK@E*?4hF75T; zZx({q)}{&;aS)3bII6d`8!ipNn_TmD-7iL{y?A~cg z?NCvkGCWCj9#iwYbK`Cd?cVE8x=5`z4vM`=47Y>kK2x+c+T-h}SbhMnUyN&v8|Jn* z9BA!Om=jW|U+4nLp0r|qOgVue>K2g#sxB>S?0DqkRWx3EGw6_W#eib;x5e)x2PLYb zpC~VMRm9y+wD^6CU~%@n!bjhJ+Lm2_^9v3=$aV90i!;?+&pB!xoT2c^e>gSlh_I-j zvXf2uHHW~=A6Tc=+Ppt`u6#;f|5$Wr>*lg&z44fj`pQ?tT*$!NcNepA#w@uj7ag-5 z<@@^8yJ+VaaEeaGWZ@9>YbqEtYNU1Nm(34ta}vPpf%bkpi5udG(4uQkjs0rL4Ph03#3cTL2X-zlUg zHuG31&An?Mg~}V54SS=X-^se*Z^t;YxVQ7Y_=-$AP8~cxajg1PB{FdwiFjvZl{pX^ zAAh(V0vm(nmJ{p+`%rBz5_+tJ&yygOBr$>lAQ4F<0&yk5b48>lkwW&OczG@(5-CLD zO7Btoe+3BB`KigW{|^XC<5Z#nHu?(O^kj*YCq@93pPq;~Foe7mgoE(-vWzxFKkNX5 L&ZM0QOvwEcMs-}w literal 1335 zcmbVMZA{!`94`VgFwD8c>>x%<+2+*UwY`_#wc&=P?a4WhvEv-zTPSxAT;=*=TXqM| zdBXTW$jk;V=wjvqW7AAV%(iGk%ubgjLNbjBLBleqCd5QcjErPk*uZ3bvi z7F$6Wh_Q}0CO`#(76)6S^$iNiQl$=luCgsPLbs0Ffws8zy zhL~-BY{jXDP%X--IzT-Rmpw|5BuY^Z((R!rawqB}NGDFfo3fK`#zQeqFS>eR(3&1= zVZwZ1)fQa&vAAh!42~z0Nk`J+Ib9aA377%*F?9nAy6fwc8(RLXqW{~$|@QWiR#Tdfnxb+eh#L7Di0L8BGf=Y?T}PKRLspGq}|p|7n)&7VJQ4y4Lz| zv37WX0#A$%kH*wF`bz}KX$|shBsFttz+YXp_pvMAT`XW{H@Q!7eD@*rQK7Mq-Mn?W z>Et`zeHZxRV*0`@GSANL4qXl9+ef?D*{0%)dHv5X2&WrwS6p9AkWap~W#iIP&kcHT zaoZO}j_We!DL;0!Id}5z9n@x??H%pCaz4EOs|*qPQy?=iu2FuP(VJe={bLvF^K1_aBvXI?~f$IIj&ZJRF&8A3Cvj*BJNs(3!y> zeF!t29`>ULH-0#h&e&>iBQv9!zw)(`F%VC@y5Zexg=MNSmCNr-haa)&xqq`IM+#<` z^uShP_G7T`avC&G|bHeqRS8R#z(~X5cU7sQ+TT{Co&y@7_f8f6J z-ZpRa(?i=Q7GDnkaQLOd&fjgb16|+T`uuoj!LK8GTw9LJefIuC)z|p*Z14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>H9CBnJpX7e){$kX!7fi-zM(32r>Wt=kHa^4puKe1hi=4 z=98bk{Xn+P)W~)d(4FEXL4F{=A_LE7&z?Q^dj5pR_0cwm7Dsj^X_+~x3MG{VsR|Xj1q=)py;DOSr`aT~+tR_{Hx@0Lx!5U8LawNr;9z9Ukdc>Q_sQ6|6 zVh4@|TrL~Ft_g8(P55}_wv)z<0ui%Cvs!J71X-`~hU`B&r{OodZ;QCdZL=kp1%f+S z0^-gT?68frwh+HARa>UT5nK8HR%OW|Wy>2q1B-BhpYKz{(P=bscIMN=x}4aZnF5GwV&vhzZ^cD zGQ64>CyBML&Oh;&`6t7jz5`$CPcZbpTc_au_`wyAx*M@(R<+AqgkHxV&D>=V^UO zudwxn(M-QbirvzRStixLI{w7o;{WS*KX*s)e%p6Na_75S*5%LId~=Uz{H5*wjc-I& zb@K=DmKn*;yD#*M`R<3Vd9GGF_<_l@-qXb~MB;Mnu}GmN0}+>tW%F1?+@lr;PFcX% z{`dV^o4*YeufoEa|2GwcaxyDKU3cZpsqxLJ)m7nCE!sZ)dRx$x6@2cMvKhXeze=WE zcbyfaq#s^snUO_QmvAUQh^kMk%5tsu7SC(p-G6Lft9hTm5I5wfq|8Q z!3>}KyHGUb=BH$)RpQq0QZStjs6i5BLvVgtNqJ&XDuZK6ep0G}XKrG8YEWuoN@d~6 RR8T?0;OXk;vd$@?2>_aVAvOR2 literal 1236 zcmbVMZD`zN9MAf4$hc{pqEpmm*@P9m%gf!Rm(XjQCOPk9S8vyYoqlRAd9Dp@UYb1h zu7}dAs8sYrH)|;qrYOS?zRRYKUG+oR2SbwQ z|2+Bse*gEpnjLs@&+dnJV;Ht4osx2B?eL$w0_eT}rl+9gAt)E%pjCop#Q~V0Sw(=S z4P_YQfTB%~UjW@0)>_i@1z5-&;Z@5BE51&+V%P|cVcoqITTw>=#EW29HxtChXXgoA z*Am3BScb~jA{fzARR;`K2l8rlROK|Hw+HX8@QABiTPD34Bup zjwXmLrwW-YE?N%2W8p|hrDz&w*)ScAu`K-r&QLT%Qs`zwbd-;=Jky0YE&^F|v=X0_ zk_}sEmmo$Uw0V*&m&@UDBy2guB+YT0&%rPuq!Dr_O{i2trhBl-AOTl(bQ|iHiTjL7 z(VBn>0(rU>f?;Pe+r*~ZNEAvLSy60~4pXFI_;GD&yD$g-bz?_uH$Q0uG6!61!ckE@ zN(Y-@l)F0{@)eOc{L_w(ilU53mO5bo6Q-pEfxd(_UE`@pTx659*d>ZdnW1Sxj?(?H z7)#S)oaIEO$*~hQnvAdv)z8UM8C61NxJZl@rFdMR1y*8Zu8B>XE>uhvH0|ohZUY;? zBbFB(pg_yXTh>^!1F|C)wA>NP#>GJvKYUC#HLL7)`sLY*RsxRx9MF=EW#F6n<@Fux z$0E@#N)TvKMjoKJTFNv{M3~9)QMlJi#_vG-tF82fxZW++KqSPkpN_80#`2_2n4@#TK4_gw{YG2sn@== zc;>zJM@lPmYwFkKi^VuxfBY4GXdfQGd>KD^@3m#mJG#^ns0-h|^z!H3tH-Y{zrPW@ zdCzL?wF4ite!A9m;-iJ%SJFRFEq6cj=#TCD&((*r_gzSHZ)Y1Ny(@X2xwf2t6t_a?K ml0KALsoy_ysV`W=TCs2DJ{kY{u`8$i|4dpQkUs7odhs7+1F5C} diff --git a/toxygen/smileys/default/D83CDD9A.png b/toxygen/smileys/default/D83CDD9A.png index 7c34f12e8a3ba93aedfb73f299b56caf5738b1b9..91e0db3a0fccdecb9fcd1ebc53e706a5120ccff1 100644 GIT binary patch literal 1402 zcmZ`(do&bi6#r%vW5lSVJSvS=$!3_*5>w>O5HXFYlRXVHN|70+8G|Hi6rnt(mSH72 zJ?Me;jM}gssn%mHC9Aws$RlKNcjoj@`^VmMzVDvOeFIu@ZtDHJ`5&>1p9 zW5*Dp{)~`1C#%Y1FG3is`+kN!8i90n+!u6*EYrK)gPPbl|)OM5#c8 z9E2g@BU$p-d};UyQp@pzvDYx!4~0SB4prIU(IQ=w zI*;xq+vvAO8y+4WaUL05W8YsU#WB7vj&`omCx~ompunTK+A9nzsNlEB(U+CyBkG@@pKb+V~#*GX3nVJPp>h-G9WYaqKY3^vexiHM2EiGHTu;Jv1KI z)KI;P;4@O+a+Wo{R=#YbSNAE=;OoEwbH%g|l>JB8USj1hEE1k!U-NLi$-T(9ag~BZ zVM;RhZ;NKrH-EjWsNJTUV<=45YrtKzq!n%a8c!G z?FVuE+sCemrKZ(-A<@qEu+`h85rZq({20~#mSZO|wx7aYM$m|b3K!Xj(z^3D=ysbr zEnl5MZ}zdA!!WNN=Vrxa87rY0jkTPJyL{YzruZy>tFDhdP|fyG+&4v1>qN=n^(MLN z9V4ehJiDJ<%6#}9Kd(9c^pBH_pbUvTW}-n=OC02q5lGpXZK1AHoYecP&CyQ(yW|s3 zaU@QqCoaLEG)Av($EfApmO#bR%9oWKge`;EFY79d^J1D)FhlOUd{xDxN@R)Rgy>Pm zxoFd{P{D__X38v6-Q|R|ip99PHACK1MZXE+nyF$dzUlD8{34o*Ic8ZDpgjob6NXTadv%3i1T&$Lj!`d}6?L1`p+`ZGSylZ?`q0WO}cy|N2VS7hIgsH?xqa-Hu@ln#3@RHur`K%sd ztl1_)KhJHmk(bWLiU4y?S4i#r!jM2mH8M(solxgeNd*g&RTWaP+j#cgJQkD3ruv1j zr4AXBjg3j<-6YBn3<{aL%YiOpW7_{A1O>AK{DuEda5zQ3Ath+aIvfo4 o=Lwi0Y-G&}_GJ@kLCoW9I-AK7a+}%G4-p}ny_?;An`1Hm0&h5#N&o-= literal 1362 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDt5IVs7B*Xz1(;bOF%c z76vYEjzAxq0a=!ou0Xd#^rny#X8J(K=z|gmQeuG#0aGA|2~YY!4m|Uu<^gj|5in~^ z+gNVJz`)q#>EaktacjwBe~sosiR1SAtEV28;!6`Nj5zCh?BKyki|aKrAF2G;D=MC_ zXo*MPEzgWR4@(Vp%MF5-Zx=jyf9Rh~*bZT|k}Kd*lOT{Hjr_dQ*& z-m1;kcfX**!F{D(;m={`!0ZMo(-Wa_fy9eCEVJGg-N@Hv(v>r$fFG}gBt z3ydgTzW2~_jtmYt&W&#UrVD2~cOIR)RjMbgqW9oUF`ey`pKt^*ZMWNCaU^)&oT-!K z58P6CBDFAAtjc`puNhmCe=9FoQOsxha(Qv!`^^h@uKoV!cRGf#FRh@g@%Y#3)g8^| zlXiS_4rPyd_xk4ry&KU>0t*GLr*Er~YA@+q9$nhhg%} V#VVX0ht`2gIZszVmvv4FO#p+)14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>$?W z|NsBvH)r9D0&}1P#7lzwK%PJbp3j~=d+zo836JZeZ9s9xByV>YnK>+u>_85CiKnkC z`!jYvE>+G$eIlI<3{3S^ArU1(iRB6fMfqu&IjIUIl?AB^6}bfr3>LjpL%lc6G2prL zTg0+yV#-N7KH~?mt2UkacscicyMan&qZZGmHPd#bPOhu_KXKp3Rbi9wZFTQ`)4lrs zo)*utkB=UnEx+sXt#0?-T^w)D_D&Ou%DJ2qof7;bEdONIn#NYwIhw2 zw>xF8`&H9b$qVyYKD!$2|NE|5wL13mi^}bS==R54(}}4nEpojF~g@tz2CKk4;Ig6IH6&(fM2q+Yg5C4pZ7OA za82NH>ezlTgxUE+#kt>;93uDyPOnkh=&tLrA$P(r<>!n$_NRLNV7R$&;he5HM%)a0 zB?JqqSCw(BOKhzzQ&Nd_{a;*AmbAd)M@bvE?AMNLC+)r;PoA#YP%mzpcvas6@#@4&8#?q8wdrTUqnl0x#6Q zvA-d7M=MU+?dORnOh379a2YvWX>*vmu;cJL*%dD9E3UaqTbZ*>Yw$@k-1VDb4x5p5 z(9KDHk4~`{RV&LkJmr}4k6-fOjk~OmB+UI1Ki+-6Yw6{Q6AC@PK3wux_j8HH0@>Ny zr>&Gu-^jUiubGg;rN$dZWs{isA0=lb^jY^FKQM>*MeR)sLvH?Z$az2`4h$Aw+TJd)m+0aIs+r;B5V#O2sCcf}eVL>wOKx-8NZH9CHt{cHOrYOZ?Cv|RPtly;%lZOesTPZKCH zH%ZwuC8GSx#yRCV=RX>U%4ghq|Kf^>y^^FnC}Q!>*kacd}V_OJ(PkObKfoS#-w oo>-L1;Fyx1l&avFo0y&&l$w}QS$HxPREjWoy85}Sb4q9e06t~{4FCWD literal 1169 zcmbVMPi)gx7JXZ?+jf}{SKvUQlh6*6HX%(Do7AGAAvnNco2F@n@Z2Wd4i$%4w%_-j zzu)is?@Pl&@9*35?jD9=_7w-UGF=Zu&+Zs~cmCcm=+Z&-F*1V3NzL#Ple2IYfudtf zpfWP7*|XoGK8D#bZdb<0Sn0H4Vuv##9WHb{ie{L;fzUI|DMUaOP1tUheR2B^3v4UP zj;2aN$y3p!Jy`eANPVbc)~8I_Vh8#`U#L(52N45=&a@jSVV2$CRj3>t^DNj>-#DiIv7-8HEx>^hx3QXVjh>cwkF&b4oL$WOO zbTb6UE0wm0-JqE$nle5#JRWia?>JFh8`=RWqkrAlRy(N7dWbKh0MGa)t;cv*3rutO zWX0fNEM*3%UqlPEW#oDkVXvW+Yh^ zTO8YAbuBMT8ClhnS^+{`%;*`KK1mexyqJ=-LJM1T17f%)YT31^-6l5wMy#Ux$ROCS zU_9OGfZ<6@a4?BIppHnOXVi8rTnl=m@@z({A>TfStb&gnu#sQI-o}0^kxUD@98`5Z z8O5Cuv_g(XEKy08^#t3*TK^|!JavYTn&Uss(mJ9Y7>Qf0Pd8h`gIqc>J{^sf@7jK$ zV|BNvgf;9UOwNx{MI+0pIF~}vGK|L7r8&-jrGP~m|I-x{-L^9U+nK)nwx8! zyz;buqkn0x@v3|<1`f}6oNAB5Zu7U!`@Q?qYmL=`Glf4sTj^O?`tm1n{;_wd>*m@! zLr)%E`R77)xw9{}wx}N3b!#ns?C|>Wp8d7E57~jQrAO`ecYnP6e5PmN$4_JDUoMw`ES^4$cv+ia0 QYV?yR>OPf@rsI5HYw(?%yc)63QJff52XPhdQ^UQndie=0WisI5G>e zWgeicwIp90Ep4WRV@6{sfv z2~@PTflB%j)&x}EfAT+{MlP4b>l%KC9y^~)_FL(TzYf*#Yhw#ni6cvO{_px|#tM3~ zr7$1C=pW)rtMm>ye~B(4XG-^)DPgPL+s)_>JJQ9=53En}mlu>CnnG<5UbxrEoUC*bSV&6Qi7zQzHdn3)wCaagy~ynDz^{sR!!piH#-18FTsoHv?iVDORA8zVT>^+#hbkBU~!%Vo>l+_09BAEyu^HT4NIb`P%Ls2}> zHQKT{v_C!W;3DIe5mZ$tm5@HvoraXyDUJe zu!EWrGCdsOYV%I&t{Taczu+a&P!7Koa`kQJsQRwfe)7()V4B=--;wXF#h1F^PIb)w z7mheK`#IN?I&d@0f+c8ZSa6%YyuT`>F-!v6QP91;>-ADu9j)|SNq+7`Fv1L{Z+}$J zsK%_t`L<#n_L4g&HB~IHy|At@8EK(jLc#0oLR#D9rD%BSM;5o^N*YYf!=IeQA9Ra1 z5y2@AihXiZCETsS-9|lkg}&*Fk%Tk#IoUq@e9cb#n$&0>NrcB zFZXzJhDTtJ21>_!JL}}Rin9AIh#g3gp=IAUR{P(c2!7dBJbGbFGo+gDc6MWXMK6rt zfZv?aAbpU2@i%)hf5+|hB195lsqMtPRXT_BX3baZY2bhG`3?GSBjz)fx zm0QRv>jSSpobC&A*Tu=W@`Y?Gwn28}^!m}wGDH#H=) zxtyfQWCop*{B8S;!@$oBlXH^t5i&|SX(wuvril4lA_Uf@3O$pr5aaHzv6Icf2Zq6l!a$~4m z27%6DKmypJZEbDP+iftoAPky-wIkS}t@@RpL@M4CHqf+t}ZA5 literal 1397 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYa}t%N=+=uFAB-e&w-_YfQQ=>r|34@w+Ji3KJEOo1RKJm~{D@XV8%2h1@= zz^oDFaa5aufpMv)i(^Q|ttFSeHCzHE4t%UWt|PMP@B#Lddmz$tg3iho)%d2@68l zR;GSG{Un+vfpz%_SL<5_oy1pHADko6E%HZkh=d#Wz Gp$PzKljjov diff --git a/toxygen/smileys/default/D83CDF01.png b/toxygen/smileys/default/D83CDF01.png index ac39b56c9bb63ee8f539c1a6819a01130c51870f..a834fd3e635635c3f66f8417b3ede08c3af7535e 100644 GIT binary patch delta 1574 zcmV+>2HE+*3(pLY8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0kcp{R7KL<;nCaSSKR5)+Te8I z>}}xeVczO>#?jB&-@x$rz3}(0?e(AP^N8i|cHQaJT~E^7`N8n`#_;#v)Yj(2!r!{M)wsB|?DV|v_r34;w(s__?e)#q+}rH% z>CesD-Qd^Z=Fr{Z%Foo{#KYL??$Px6%hub-^7zs0@ZH?r+S}gK&(gfw;>_FN%i`$9 z;_1WG+tt$6-hbEF)9CHW)Y`bz+_u->wb$Uc*x|w4$le5v(?_g z-{-^L<-ptH$kW)k+2W+k*pk1^snFZWWt+vBp=;I!D`#?aQ6#DCGW*5JL|<+s`5tkK-K+T+3C z=)>XZ!Qbew)7`Gp-Net;oW|3l%Gb5m;it~osL$HG-Q~XB=EKg@mc!7O!_l9~)}_ta zuG8JK)!(bo+nL4Ep~=>!&Dq1u(xc1Qqs`f-&e^EY+MvqTpvcw2%h9jY-mul)vew_R z)!xC%(0~8`|EHNMvj6}90b)x>M38t2jtl?*00(qQO+^Rh2M!7)22wEz7ytkQetJ|` zbW&k=AaHVTW@&6?Aar?fWgvKMZ~y=}jg?hdvYaps{MRXR1f$g)hr#$SRr$>E(=xO0 z7~Ybh3VUQ(Ep^L={{H?$zw%eLlC09BM)AqTZ+~u~T%6rJeD+Nm^VV;-TW5xSc1??g zm^LZeqNu>P`b=7532ZcpMK@E&h=3y4#4KGlF0w|IC8}eiw*>QvOaAxOw~$<;L_jv` zJ@b%dUwFEYOkQDNyX6jj?%-lon-$_ruE#Km@g~9z`4pK1sDgT7n?%KO4%-SVk9Fpb zaese1aT{*Sa9rRLTuu+)%VT5#A7fSZQ)x_NG0Ur=c#142r^op57VzS2WE?=AWX|WA z3FF;%0Av&r&?gy*in4%^>mRc~3Xlpyu+M|384SVWvF;OrupS^bv)YM0YLNtMgVb+1 z55Vmu6X6fY_J(pQaw=H>=*5VD?V;KsxPOFN_SuOLx{CgJ@a)7uaq#IGE@afhE`(Mb z@spRU!oDwRhovjrN60%ri>7MxerOP2)eIqF*|sT@&Z?uc_f3eb<1{T0SEQ;43^HMa z7I<6@f}&A%!b~wTx-a_=HiBElbjHYUnOq=W0t1K=31M7#AgUTdCTgPRMwl>iPJbkA z1$n>=Hah~-K+cAgY)Mbz>M42u%Nn4AAUugE*Q8$FlhDt28=Y2?du_A5Jo6 zC2t1`RJh_!K&9C-MHdA4LZbn@a~P(U9z7{Y4aM#vLdN)|Q> zyvr_ul2!8}K?h?UOyLdI2rnHR9Dk92D*K_|=%)>L>bLt{&i!$b(DhAocH3{or}gbq zARFb<#Rth!oly?&-xB%<_Y8`hAmU?60002$Nkl1%`N=eJe%E>DzDk-aoF{nyNNT{i6Xn$&H>*z{| z>!~v68yFfHo0yuJTUc6I+eqp&*xK1UI667IxVpJ}czSu;GWhuV`3E=$1_g(NgoXt= z`!Iw@L`Fr&#K!r?CnWkNC5JPlq^6~3WM*aO#d^6ciSv`KCGeh8CBU#*~#8 zFjSOOR#n&3*5=jKH#9aiRevxvx6~9hww9K*m9#gtc62r~bme!~M)dUd_64UE13?!< zKN|>Hq)$ literal 1472 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 Ef;c4@Jpcdz literal 1372 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 diff --git a/toxygen/smileys/default/D83CDF05.png b/toxygen/smileys/default/D83CDF05.png index 4ee1bf48211ebb5bf4dc5d53be7b97ce686dd8e1..8a9b1258cb1f61f53dcd63ff40fc827cbf9ec2b1 100644 GIT binary patch delta 1742 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 literal 1514 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YAtfpbJ3$ zHaBo_a&a;;HgYvKvoteNg6d5nC(QJLj?o7t4y42a69T3{5EGvCfgE_|NzDW1m?B`- zn4{BS%D})R<>}%WQgLg_B;S0YK#{ilKOd}Sc2Z{x&~_2tdTE;DhRj8?^=AEJ5}!3= z$H({|%y-IOuB(20>V=p2JCUs5?in+JQU%mlW-*1e_0QaSy?Ex1f}67^&;EYr^B&9R zK5n*aW?P&Ov~>UX)7NMDj!BNYJD;fDP~LHFkAy=C<6b_!^aNAE0RH*1lN*({>+rm7 z6FAKHMMET1!bV)~uovH2`G|e30TUkVTzKC>p{J>_j^Ep=>wbccV^QISgLQn{biKlu zn@`$1EApGRT-N)-!DTvSp3^NM*>`)JH+8SyuGpWr=HT34Y;5HUZe4ByS2j$Vb$Rw7 zg+ht^3l|M$CtYa~Qi@2uqPXzT=h}R!e45H5)x3p4m$Tey!?&y+9dqx^C5Oqa&Oscs%=l4ZB1Sbc0sJ@ut1hgYLF=C|C* zX`IXW-W*|MP08K6>8uD{qfB&a_ ZfT3uoal>qVMn_QT>*?y}vd$@?2>`8)B3S?c diff --git a/toxygen/smileys/default/D83CDF08.png b/toxygen/smileys/default/D83CDF08.png index 428c8428fbeeb6f201cbab2dbc7131c4f1aacc58..0f9f2812933e0e27c3c63fced8768f55bf7e2d9d 100644 GIT binary patch delta 1518 zcmZ{ic{J1u6vwYEyEfa1-V9TeFo-nhFd-^lSx#e(vgBo$7>Rf@q_Q`p*T_=J+H0Yg zA0OD}~bHM<327ng1r1`iVfK>6Zqu$Pg zqA8AW3TPbm=p>{MIV3-MM2Y{9>Wma&k%y{C0}PBzKo*aZRJL~B4tXH^jStk82}t3C z0S90J`UT@a7eH=5KRO60SNJ>znF4@*1rUV5TLvsVgM}Bb`66J5ppd;9O+eb9a~j|^ zz~m)($A`KZh#mzup0XYH6;BAaC!tKV6ng~ehH?=A#}@wXg|aCV)1lODycXQp=>G9( zs{MH1@F>)ZnQ3WABm6!NFn9>wjl)Sn8j2^h9eW(k56%_C9dYlF0P5cpDhH4PErD2K z*9blc!GmABbF{~o54G=KaQN_Se)uvO83tHj0!3JEFwz=6_MFXw)>))br$geE=Lem$ zXLN=q-N)e1Md+k1b`NkF_Z}miU@ysFz5)D4*mk359xcPmPhI^(B0Bl2P?CWTjEjHX zHOvp30`_VbYY@qH7XYp=fVAo|hS z$3_su*oCSrIib|4BZr(a&W3CHNeX!?#m(JW{;j;4(hk+0H*7k9G%d-SNbw9wK$Bx* z!Xu-?(3C`S82SQ<3}BOc!{<0tgjQIO&biVNcJ&#|NX5m)(Cke6JuR^o%&2JY`AF79s@?Wd)Y30H1@i?BRJXymZgR|? z-I0sjdh_hZ%@I~CvQe5lYcOmUTdd{Vrfuz~jPtkYX(TaQ((|OHI5nR#4)SU*6kT&2 zk49hYR!jLfS(wc%$uMYIIEXDu*c_c<_)_0NV@7#IfA@YKokU+^TPCoUN!=vI!!GjW z%(!NiU#nhZNLv^Aa7}Be=Bi;8Cj!h19=KM2yKo_axOhPSJlf3jbgO}H!7(q4z*gRSWeZricE@4>#2rTA@9|` zkXL6SxHW#IS2w6xt7=W}AGPkQ@QJ5<7Re6aJ%+NTv(z2KC%iOjSYKWHOYP7?+&n4OOYS zs=GJ^n%&}!Gt@Pg15zJQj2|0O{dZd|v+kWE$6_Z)*IjNJW2Oq`x?G>{?)gY}v{j3F z`A2kl4`yZ!?+WEx>k~_`GTRT-<^6WF+9T+rnL4}Zk}ok=sBlirbZ%zK$aahS1Nng4-IeKdkoep-8&pB#D%TFzmQzDKC_S-yTpqdgpXg9B& zbU5ZpOfPoyR#E8Qfvv0v3l-03IHA;U6c!fxcyouX_BK3?r)Z#7Rz9h!y#>EpbfZfQwiz?jHBillAV)|!&M{#{1;Qd%3W<2nYrcTWIT-(ojjU4@{1 z>eQR*ty``aczJfBuV75<4J?gq4fk!79@Jh^R?5yga`@F9^zHK2yHD;nS25L;`cQM* zbc%2J`w8#dDJ$1g1=!a}__8*kxGLFd2gR943H1%3gyB!ehDii0a26IOxP2z(d%ewZ ycuOn1m6b6LhsWW%68k0Wf5g84DJC>BJn{d5C)o4#`4~eb05@k3r$>a~w0{70da%g= literal 1482 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 Xs0ofYb3Y(+wZE^^QN*<8uY2tue6}_k diff --git a/toxygen/smileys/default/D83CDF0A.png b/toxygen/smileys/default/D83CDF0A.png index f844fdeb4aaf7d193d00c62946d149c401addab2..fb659245177b53c25cbfd1deba80771806527848 100644 GIT binary patch delta 1587 zcmZ`%c~la35dJA5LYSp_l?Q5=SsJh&X>NI8kz(GW<_4aj;t7dafkua=W|*n#iAR}P zo0MCY=8+fP7b@kkR^C=xc4@B5zW&-j_Pu#C-#2f*H#2YMJ#rM8rpkf<0Q3yxT*W~u z*otTcK;^YfKPXT!NBJLdAp&q(7XUT~fK_pdJqrK>1He2P0P`#Wlp}MWI$HoB@zBY^ z4ZpFm@%r>>`HD661^Mp8 z2uXP|%0XvS_=lLe2V=7!2qFY}h!l_nk@{6^riVg*0v8{)^Kq7gd;Cu-_5_zWT3lA+ z;CT4?GKQd&*4jR?{3$g4)((BTj4IWYULc-h;^WHr@+Tc!99)H}VGtWzRvX$P=v#bW zJv}qDwA?T|Phy?S7Pl{H84Rv#VfXf*yve^`Kj1~pjyPAk+l6y1z1-<$#a6B0?RsI* zt=!cCT@6eq{OMjiPEwct-R5 zLjCOA#s0yb_aD-P!cWb($&gJhj4L)ESapy_vi$T^{)VaLCm`v}WRI{+;gRxwi zpo2F$o+=#nD11P^P$(sR1f@@gZ*i1Wa#h&jiBNDl7LX(bwb8Jk%Io=%iXPY}^R%pK zCjJ~F>j?N}dm`lmIW|LH)fED@+`Q9Mn-ED%D>giS)+mtUc`?ubVt&vSUQF>LW@|@O zT}xC|JxhEuX2OS1@ojbKE{^}cfllqxM7Xr%oxjOar>_1v7H;VmD(@(QC>2WR&vbjs z!muT4YPNKLaixTx{b5^h8LECnOki{Z5f7V&ZAPGBXcMzk03^?zb|cYUD6y!>=!hU{ zSRjfX7a53R(jo!apOWED;E$qM$gzcOW|BiPtx=D=4Bfq^zr`hLs69^qG2$-G=^a?& z%*}k&Trnsp&vV?V5)dd3K9%LzTYw~3x!?=alZgYnQhxBIh-Hg+7(JbwtUBvFp~txd_fGS}wRr5mjWV_NsuIo8NlG4$>` z!3}R`YEEKn*r}D}tGkVERc22LooetNoB@A}8rUPIrUoocnfm>?1?9|RIJjml`B{)` zizivOtxwdlH|Z%N8piEdx3=S?Qf8j-<8VG(`VHL89`QH8H9X}FY95rUU-#8*loWiC zopk%=h65$aI>_*za`j~UGIWn4oVRCzWs>iJl!E2uPg~nrELs=7eozosjaZzpB9fjq zp8fsI+hAdZcMcH(i6tt?-+a`Cqq?eh=3gRv9X3VhSRRgYzthfA@pWb@R#g{Q5WL#Z z!HQ}D+qJuuAb<4aA4i>#*oB&WQ4~U6;KnewK!hzdplE(YGHa>JD87)lJm!T6XrJ3D0@XTz0{yLBmbnaCBTj2&*(tH{uJSZGH{8DzRMe^a4`G%4cRv)r{$+PsCF=?asG=FF8ho~Q#;L(OsmzPXNC{)YT2 zba2a+tfCuP0`Y^k5Nxg7R0jOXC>a%1)od@4Xv8dwrP9DoxSoF1hv;jQ4%4mgxLta$ z`0ni>T!tIO~tf?)R@ z>YOSIne8PqtB(5)y?}={qoJjN$SK7PccF0=W6IKLqIaaJcXV{L|K-qlpGY`1@M^d~ z79=$p+Dp^>#>G3K>39-7fI<&6_m2(~2Y|!ia0VDt1FVr77GwU4vAHpJ4+dk7!I+xN iYy3w*iwK|w#cBM%!Y@^PBrC@s`F0?<3~Y{E$3 zmVyn}iA5aFM5{eDjY%^sL(P;!fcta=9*2`fb2y9QJx<)5LomQPBHKPVa8YjB%+|viH4bKT_kKe@Mb;ZMiMF#v!E-Un>~;_&qsJ5=`$b@}TTmE+q?lYK z)hZFSh80w5kyHz-h#+HJB2ERWb^!EuKHq8b0jC%iN)fgsk&!23#Hua;sD|_7jGb`E`l*8Htj^ zDT!xzOJ$uEPdi@>4(3HM(0Bb;8{{LeTlYRb+&ITp(Og(m?4Fd}{3v4qS=S9uPi#Mu zS{$>Hyw)AN+i+pkG=L1LJo%3);#GN7+UqsO(3ITRo2FV{<~YN zX3YvKZB%YsyECP6W&g>Hmea!AuL_C3BWCQ&Dqn+kXO`^U)0O!e`8=}2zaqHN>XN#$ zt5v(UT>gyqJFA({a3@i40>HLMi5J=J!H%XBaMW7DnqwO;og$wd)rYd-fQcSR-F z?@)1b$)T>F_APN%e_C+=tUsW`XB!6U14V6PR|Ri|KMCbIMuH-=D2CFrYke8 z==ZiMF&*hcZSf_Bofhm=-lmB2BlqL3KYnoQ*`^)k(<-;xEYgICw1#ZudbnxLiI#=e zn_JK8ox@N|vMyxugxuhdo*6@Q1U&LRa8=GN^B?-;D(6{w0Kc~)r^WXN&})`sXH;v8 F{s9S&YQ6vf diff --git a/toxygen/smileys/default/D83CDF0B.png b/toxygen/smileys/default/D83CDF0B.png index 76fecab42f43383f4854571f5f4986446d755ea4..362aea0421250557092e43356e4f41c7ca12eb96 100644 GIT binary patch delta 1656 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 LtDnm{r-UW|N5NU% diff --git a/toxygen/smileys/default/D83CDF0C.png b/toxygen/smileys/default/D83CDF0C.png index 8f503806f118fa860151e0d22d7fffd116086f53..222ef58f1d9b64e258f3027f6629762b4c2ed712 100644 GIT binary patch delta 1691 zcmV;M24wlJ48skO8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0vu3GR7KL<;nCaS4k%m%3M&d4 zKm-jh8aQPL6gUJ6EesPjPHT}+TzqGLqR`sl4=Z94C{ra%e}4uNHxDCD1P(L`7d$XV zXB;bAMpuw;d81!~u}WZ%&)MG)Flz-CKnEH{7&d1jNq!9@PcB1gGeKxscA;o-pKO7v zE=GUO*xnR3b`L3A3?fts9!?P_RzqQjOk9g_g{@Oxi$YwNPEC* zHgpp-a!+%iMNXGmV5V7PrebNUUu&l=LV*}Kb}L+?%GBB#N}CfqgELi>CP|GhOp#4q zqGEBhRA8r5Xsk|Dr$J$@MQx`WKZVHC*dS7=Dp{p2On;kQdbm+;w_a_wVS&C%W2!@)>wDKVs^b;c)($Dy<>5{Vs^hUS+yfsv?5Zn9a6C&SF>_I%UZxV#y_9$tY;h!_Lzl8#UoLkB2u0xXvivX)*(-s5h+j& z9y%daq$_OD6*g%UEm&9APoG^DrO0oganr35&4^{`JClX z2hPLm@jgbYL_Q*OP8IKY+hvbVpwP`z--|Z-?rKA5_1#? zV58Q9hpcnr={}ge!oYTm75ZGkDHm-y5eK>M!z7t^5pLj$}TZiKc7w2@k`JNtw1$;8As(+nQV;Yl@UU9{pWJNmN$HlwDi}%6UfxJkK z`^kjyY!d*CTpaZYMZ6#_AjJGf7NCGqp%83!C{>{lJRbA55CrQEVT08a_E?J$SQ|?H z#(4nluQCxnAhtE6Q@|-j0+1IY0=7rh4#CAWN1vSpp{wYh2ais4BnO`!;ao;OY=1&% z#SuSrxk~K&q$Vs~;XXp%^;tElR_CTcfK4NWc#gJ>GHF#Eojn&JvX0%fqPQYOMPQHx zBWi)$G^3zsR9*0<7#ZCc{RbPtt>Cp{*wwCsm|qn|e1so(BrIrqmwLVwpM&Dm|9 zicjm?uRwN+sf!;{j_Qnbc>YW1AKxd6l7dvS5C8xG@<~KNR2b7^U_b|qOw7zItjuie z%p9Ct+&qj7ynOrufp&<-O$tkI6=^2?>**Up+`2|S~g+;|BrDf$6l~vU>wKa8x4D}6-P0cN>ZS5VM zon4*X^$b0|ef<+APMSOg45s!lOq)Jq=B(Ls=FXeHVBw<0OQta_U4OQG#fs%CSFK*N zcKN#X%a$^1*oXo)Fl<5t0KO_-bs7mM`v3p{C3HntbYx+4WjbSWWnpw>05UK#GA%GS zEip7yF)%taH99piD=;uRFfaz*@PhyV03~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$V lR536*Gc`IjGAl4JItDN>cY{bjks&7v002ovPDHLkV1lDLzXbpQ literal 1582 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YV@se5 zK>jv2aB*^RGBP%DH8!&}Gf{%-O(7@D^ns4i2PF=q!~zonra%xAp7ennc;-pX1Ll|_ zVAl8>;D3vOfho?@#WAGf)|5%7^DaAxwAm(C*L1#BDSfrY$v0E@R!~rY_zEGG(4^*; zE4D&US$}caaAwOjvTj(j%R$S-l~plnmBXwFvx^HK{m46S5Vq7y$9nb5i~r~6pPM<~ zdT)k-d8CZunaO*vzkAAQyWmbuM*mC|vv>AVu{GVXvl#-O4*OWPIc|B<45^Bfa=ia*>{u45 zz3g@f@#>$yxvNh&!X%{DQvSzAuR~$C7d@JCfKxJZ|5O%7q4vpn?kA;U%mLO4C zE!Lsa?z%3e_;r3${4n!C|1!k|(=F8Q zN`6q0yflBh#5XHJ9#`%kS!>d#>uQKs?KNhSNXa?WBlXX2)4BV{{q}DtRuEtP_p$kr z&0pTuI(a>Kar))vz2Q855q-<2PO|XYx#s|vP(#j>TNa%YmIyE?-~ajJUGA;s70=cm vKJkCP?~;`D-*x&g$;isp?+x7ZpVNRL>!VkNgTe~DWM4fn(s%J diff --git a/toxygen/smileys/default/D83CDF0D.png b/toxygen/smileys/default/D83CDF0D.png index ab306db61110231fe2939205c2c62fe03bfed7b1..b76776d332751756909dbf32b5f8851389260422 100644 GIT binary patch literal 1865 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 diff --git a/toxygen/smileys/default/D83CDF0E.png b/toxygen/smileys/default/D83CDF0E.png index 3ccaf4f1de6c12b3dd3181ea7b5b7c2d360e70e2..8a2185514f16ac655ed79603543ab544619adf2c 100644 GIT binary patch literal 1867 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 diff --git a/toxygen/smileys/default/D83CDF0F.png b/toxygen/smileys/default/D83CDF0F.png index 5d3be088632ce2ca288d6aed380edb2bd6be83f7..3cb44bed27c4ae98239f0cca616857dd0f616f2c 100644 GIT binary patch literal 1878 zcmZ`)dpOkF8vfk3>)uJbHu2CwyHLX%Y)%)Wq^WpvjlF5R%?LAwY0$_uq?j2p#y!_i zgD{a?N^%?R*lrmr_u)`p+fO!_c?x4ITyCVSNPUp`#S|JFcnOUcrT?$zOC>e(QscnPF2X?n{Zq>=cn|Xgy zp{N}aJNISrPv=Eks-{8$Upu?Up(q$)+Kf01R=XF(_W`TpU|K9hw5V}z0R8bLt~-P_ zlPc`sasxI!2rkz{bT)+61EbZ5O@%-nFxr6j0L=+OHQVDd4+5$nJ`pw4GWq|F0_i^Z zR{*K{$s(oez#*dXDj5tt$sxm zZ1~S3>GMd=&|1k|;b`teyIwm2wOf!e)4A;P<|}C)DVi3s^CmZ9N01YI1nR#*(F%9i z`OZ}Qt1bUoQ4V)*u6Cg}>Lqfl8-cPlV7{(@b?(u;-^iNJ@On|1P{?{M^6N*`+7RH0 zp!6?Dovaa@N*#*!dGVlbIfpYj6+MCk^drZ+5NJf8atjJqAZ0uOeXEM+P_E_4M9FG3iulgyeg5k~8%}s$IC1u;^prOByY!gP(24#t83Nf#<)+gbHl=^AZ zk)h#jtYqe8l;m_2J7@b-Q4L3jJG#*9J;OAEs1%|fiJ(cN2N5){kb?kzVRD?@2XiqB z5rPBZk|n=i-YdRQg^gVL)XdvyDNAm>o(;T8j&lI+cq>v!*{t{#@9bw+vLT%LzTy0# zzCev*TZhzECp1#5y{|`x+9W4r1=`b+i^V73V*QvpH3Ql|j@zh*NvgEz68(6WZy9s- z?bOvG&teK537e~*Bnnk^#Cdj<>_mO<>7WnUE$vU&`0C+HQ(jJ{dL7wr$$`qV?(wEV z2Os4^xwi>qBcG- zZb{A6EgYYaxY)AJkl?dl(ABds-x|bX7?$E9{Hfw%eNEw)53ZV0b#hFPf`2(+_gw1^ z7~e1MX<$Jj8s9j&zE5^UvFaPYQR4jvlN}c=9#VcT-&dX{6?5^;j-aa{%PMY`1|gwZ z@$ZWfvnvM;&+m=+^}77x=Nfrj($T0D>V?tPWYrp4;+EW|(05?z)e$8{@yZ-6ZGA#6 zdC;|9?<&dCXSw{qOG&e&iZf1K5&%ZS(oADHFsPJtYX{$KilI`Kd?uPg%v0&FEuaNs!4x&nx$DhPG z?iup-0P$10#2=kaLY1Vyn-b2LG}P4b9!^isNqpLrA}ueeFR9T_(w2C~Zcg!d&q}{R`u-Ii-8~jOj`X=69VX)`GJ=U6% z-{Mc%u4876MhZOorqsQJSgTH*Qo`IW4pm{r>7{Xf$Y({SO|L-}d09>&N&U9__|eCa zqMf?7IY$Uw<+zo)MJ%x|=cw=dmQCz#eYZQ$3b_xj^b8WPnbj_h{vlhcA&Sz{z4h-# zD5H=3R_6_VYak0Gn;g$x?e24LUSAq(rXXVK2C2Svw9iT!<`KevyGV%>ccYmMn{zh3 z45kGMSV~UBq}&4m=%>}8S-H@BJZS`TZz=%=Fv1ua8DLBe4FBn9h%v_+n;RSJVKC+x zOgp_#@qY+pijNB5&qikdV literal 1769 zcmbVNX;2eq7>?krI9Q?v0U483vQC;vNraeAdO93}&Y)1dCDt295=EwrWmXwjAd}6~>FjY0X&Z?!^j|mLscnqd zY(QCIs1ZxT)nq;5=8b~M+`ZqBU6J%g7>sMlqNp|?m^w*^>WM%^OeeoEHCm031A-93 zmqK!h0G5%06lOzm4$S9qU^YkY#~bB%7gi3*AUT`oi*OJb2+BATU)UFc*=$J02SFYe z8pQ_cjf6_CMn~;xNxLIh?t8I97)MnEhDTu7hS3fPNx%rqn1C4oI2;1JBei-BW->0c zmuEOy1jV(RP>meNbih!4h1z%6XM=tSTLSt@cnD`GZa(bGhq!Ek1ePKQLi#?8)%>5F zu}EiF_U8Ccvy2{*9cbUaZGBRBJ3OeKoEV%O4a3rciMK(pWd^Ql~g%aVG_EXNs(_SN`gr_SkYTaj1f zxL!mN{Na&uC%!`r)%J<%6d`x}gRa&DC#_pj)7Hhfx%$XFcaS@)iZP*MP_XZi!L2@} zVAp~dZ8<-hR@QkOc;l7On+#ANT^xU;^z_~~2aoK78x}pyPn)aX?QHoa;P@1?aHC7b z>n|Gcm`b0+iATSSKGE(o^Ki6H5V_Fhw*0}G_NIJi@1unc`*tcFL!%qhLj8-z=23IA zqjDeTJ3j5b+E-rsYe&EduPZz6T^>xTZ8)gev;>-~y`&85$;7^4%n5d{_KvT?#p_D5 z3Vx`25x?Tqe&y6gU(2Z`-lvT28n?-7CAroK$I9AjU)O$Sh##NfRCH-(r~9L5=~TUH z`XmK>bkBpD-Ia!*t})?Aw<_UqxD1?N1eTdz{;S@+57C zo(dGVbcR2l|EhI+)|alA18x_@rq8R4D-0mMPP@aObm6bpXO`_4v)y<)>-JT-)xg~` zcJXsYuT+`SFk$-N^Ieox4Vr?|VrlTi-Y3gLKe8lL1=;+Moe^z2#(^XAJXUwj60K%l z8FMX@0LqI}BBWKc1C;tT)-}aAzm`_wKogX8wO&Xs8k_p;R1SrCeg4NK+@{Rvnhz`8 z7JTnSzp)n7eBST%aB}NFiF1BtH@*9iuFd7(Vsn&XMh4e2EVA_RQV0Fu60e@;h5g*> zzMI`GXRYTawk`07chZXU;8<$Qi(2}W`a_Ka8|AdIx4I8pOewKB{G7Ncm!?>oRp(RT zxcB9Bi>z`Qa?8?cJC{1AF@wF>{o;dd3w!$RXAV^VxvxFugKA^e@~x@68-82wM?L8@ zZGt1KP&DQzCp@`DB+gsq*m>>nz%`Nk6w{`u*7DzbU#{5|Q?n;+w(17|6VK{#nQ>b) iwwXMeI(W^=<7kx3y6v~PS-G3-Kc7HZ5K=CUN%;ryo~-Qv diff --git a/toxygen/smileys/default/D83CDF10.png b/toxygen/smileys/default/D83CDF10.png index b5f35fb9755c80d6f8ce87e9847c12ec140af0ea..1b50e854d5fb48f59be653522d82c7faf8b27fe9 100644 GIT binary patch literal 1771 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 NLr+&fmvv4FO#n_2_B8+i diff --git a/toxygen/smileys/default/D83CDF11.png b/toxygen/smileys/default/D83CDF11.png index 078260d6dc99e81d1cecad0674e0162778184177..031d83f2404ec4f126d6e96372cda6152fb6e78b 100644 GIT binary patch delta 1713 zcmZ`%YgAKL8ofrGSYB#vi=`A(P|AQn2!)Ukfslj{;0BW1ggkiU9RdljB-{{!AtV7I zG6L$Ls7N~qimfQ3K}uT+NG(M1QMHKDad4PHSxOzTosO%_ZU4=WS?jFxt#i)a>)YQx zyWMBjr{!JCdH?`IBQ|l6;-MmgWB@q7-|Mwx19Ha5(%2vXm_h)c)(!x_AXl~j1ONjT z0G@~efOr@H{K}3Evq%82?&s`Gh}N+0D6$GQ)j7FE*^oRP%mrBm1+ofBl$Ypj<&_SL;}9at z+R$#UYm+KrkR?WxfgG7kU8OfQRa;w-S*xwBr3Qz*$js&;y_tw6CRHa(J(*xLTrsA%T+0;1hSJ)4^P>NF$aTgau_KdWWL8Iww!V6BY4t zwJffJOy!Vi5R)zD=a=yFOA8b*KVO^0mQqrA#H5V4-|fuI7N=+Bv$#r2PM#LZQL`W= zKH)dR5#N%joHS6B4JooX@-#3ng~m-vVUdzD5M$$aGe}8bGKHN?Wg~|F{zvp!Lh9Pu znx`MB6A1`b&t`G}lwjb=lK}xL$n6daT3QO4o<<3UF4xn=e?MJxxrD;$>ENZM;9$2~ zU>W%P;(+i(Xa9MGyiS@yr}mDz!hZ`TTF(G!=tZ=z=T1*i11$ysP-YXv*Rv%srmUh= zsn#ek`l>Po#;7d=fcWM^T*klyjQ2xbQ^2}n`@+zivb}GY?lq|~Fu---_2Dp^>Rn4 zTx?qurO=?`&Yew+7^|y@wpZ?8myw%1O?A; zerL0E4UYOND&3LNvg(HD^<6(caGk_R##_H%DGGVTccz`wgswKsyfO6EG_+!s&()Xt zoBOr^x18_(!KJd?@tvHd*;KvugW*ptaelnKjnd0u*#=fPuhiRi0`J_ka%Wd*NG)3P`(SKU?&`_gq8B$({=( z=ba(lzZUJNJxGzpCVX4lu<27<+v@1!JHNKoRk+vDPcO9X3!7SAy*mE+^4^$;g*=<@ zn{PcDGJE!P65=gEmW#{iFAw#<`E0`VO&)Whs`cR0ql=%$m0oFoxsWqj-T0Yvdt0gj z7tpcR_QK1=X?liM5>}9Wt}>`(XxnX>coenm$YFsufA)Jqmzfz!36^c;#OO)_u_xMR z#%@jBpS*j0-dX^3=ij*b?d+91*B;&)znzhG*h-$5`{tfy=FShX7;I?#4(OnjyvrR= z!UW;Mh#_&2iR5Eea!~Xi30Ls35f5?Wn3#+<>jw!R;-h|Wb)CD~JJdax*<>XTk9;x^ zc6qqx(%CORj)v;3;N_=`>KGmpaebC64b4>t{GyJG=#TXHe+Q2V?-SX> zre83t-oRZUd8g`PER0yNQ%~dT5mde%2DgYPo{}?x4Qk<uSqHbq?bUq{5sjQ{`u literal 1687 zcmbVNdr;GM9It{7@sNio4mk*c4H>0rQu;_og|2UX&5c+)QLl3 zDsaB0p5heQ#?Yx#rc~6sxsA!W;XqMA!N&~+MJLLQO=T&9+aI2PESKc>$mjFwtQ?!ii;c5e2}2&q0@-AanNolcoo7J6Y*c^? z#Yw^>tD4L;CloSddSSB8P?%>xjbPktAl5GD1S}*=0CvlA$|kofz!67L9SW zl#L}Q136~b%-J2~;&0^2)eK3nG^3;GU0S(YoVDk(gn6iS9xAW zi;;}^L(-^WXbUisU%B};_QhgcBZe`q+_-uqZjn@qONB}tLq&*Ggo2}72+Q0lt8zhmU`tPw4$ajZ2DRgFAw} z%iNFYkH(007WF$i;o#Vh)(#KdUp4sq_T9TXif?u&gLh7)9o?KWS47IpNt z)tYAvRZaMDwp-kza?7e~D|=5+-`tW3Ei+5TolD)Y!awwh+Z{)}qrTw9f}GlcA!wqy zD{k@X3%Te>e8@<>dwROrPGcy9+Q4(TzoC z7SR&to_6=Lpi9f9JuURw)2?zVJ}zmGZ_XR2o6;<7fIg@@XmU`?R(prU@kt!cA}ae9 zq|h9&YprhRx2yD8Y`Q0tQt1TtkNC!G+#>P;rE<=)K30wvSPF?Ts z*|4kn@dgQ;ac@gv`gQHam>-f4P~RV}2>+`8(5%m!Jau^)>#^~LRUs`r_s)5K@H^PF zjf=c)3-{d?1hp%hZUK+(MeUDRTI1O}|2R})e%9s})N!Li1(d2iknWb%#qL!nTlq-n zbv>{t%>DHI-Zcm#s~^y~u{B?8pBj0^u|en|4F2SlEIz&h{#3Uwcjv(an6F~*k-b-2 zTi0zDpS`zZ$260(GWB6!V3pR%@Qee*Gyab}OHC@OHZqo;B5 zz`V?D56Z%SR`&=C+Ih(>BJ=(Okf z2_Ek@)wT)9^Xy~BQ`!>%LBVp?S~MK3Z>KxkCQoVV{ zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0x?ibR7L;&+=q;tXl#2|TxwQX zV{~w0wWMt2(SZ8ur2qZecz%pkT4}Yfi1p!%_uha0^ul+2jDJ#9Wy-{#{Og+d-Fp7? z!BA9W=ijLN=ZyH=cVTCB_27s5=b24VU-9Id_StbnM^^OHVf^HL^3PX3LQ(O}RYOKq z^3PmYU2OByVff&Ogo>FxKuzw)OaJ`YaCL<_JW1=rNbA5q{O`4Ib%Z%SOXIjZ>%l|( z@3K@`XF)GghnIYUl5=^8o}QLXQ)6Rkd31V;dx4OLjGKFb zk#BW{eu9X|!khTrbWT!Za(Ib%eT;W|iGYTdc6^IvX@7WYaC)(?lKSO>R9R_aXnARF zfOUI|YjJ*fevNi~jc03mo0WI$-6At@85{|-fuxgR8CT0SYB>e zUT;NpR99(H zRAy~!UdFLs@X1U(KTJSGQ%Fr)P*i14Q)Ea@T|z}wOix&ujA7!%WAVyRI6X-`KTSYG zQb9#hc5G6|t5G*QM>ji2IXp-@KS*m~N4J+m|9}7ge_V`u0000$bW%=J00LRE>-yfV zSOOx<`R&XiBI)|}=_1Sd{>xhYTDShU>*oIX>*lw&TK-xB%gX{I>HX;=Bh32EBU!WP z`t8SBw@!+}00001VoOIvx}&j2+5i9m2XskIMF-{w4hkg;EP!>T0007gdQ@0+Qek%> zaDQ@dW@&6?Aar?fWgvKMZ~y=}jg?hFlAJIMy!#b-f{|p&ehy%KOI40}ep+H?U>G*p zKouBUR!iNINxy&oNeZ<}jRLVw5j8J<=mY0h>Z1qLk$Pk0O-XV4*OHJlXEsHUS}6V(w+COoqHu3l7r1~cgB$qDJPU-_;+6J z68m>jJ8ZhceY$+`GmER%_NIY@Re#(CTk*1WnWTwCW$&BdS)!Yu5rk`~uEtdgvJok` zCydDp6(bhKS7r1Oo*9k=U4^O1o9rW!8{|9SsBEAzs+t3*gj){O5>cs4ju$pS$IXaz z!dTvcQy_amvX`VfapeQK9mof;XL10i|3e0-S9gIp;*C>0`JpTKbU6|-4u7c;MjWH; zrvm~&lTt0aZoY~pr1okgfTR<#B|1rghYyUsB=f0~XZ;HW>5T)I+7C%rQTASU9uC=& zdU(b&hd2_P;qXa#wcy~4{7u=<^_IR{aF=>}oaHp02T8g=X|87bsrZ`x_!aP$eCg(g zR9u}%hxUI-`U55jip(I;@P9l2008z$L_t&-(_>)30vMT?SyUqH~+&E3P(%UeiTL=>c6%*WTyKOitDI7D1R5~xcmG%P$KATl5-Iwn>+PKJR& zHr^>ADlsWJB{eNwE<>JyK_N3MJ10ChFW;@8u&7v(fuW?dth^$;vVW>Ntfsb3NtuB` zrM{uDDZ9C)wXMCQvrCnML9M%|x2&&!!o*3Fr%auu&cL8Cea6gLv**m6H-EuG%|%)a z4B9%2mn>bje8tLDtJkd6)dRXr-(cPP4I4@}ZrW^UWDIt@iRqTD+qUo6X=ZK#^_{k* zm9>qnojpiB0{|qlNM|IPIefPO001R)MObuXVRU6WV{&C-bY%cCFfuYNFgGnRG*mG# zIx{soH8d+QFgh?WWe&x;0000bbVXQnWMOn=I&E)cX=ZrJ| zsPCOp$|C>?h9dx%&i2(XK@i~a=pcv7j#1vx@4Psri};}?~*hHK-6 zk&txU7P%5pbp&A)G8ksFnQms&F+70*3Iqb128-oOB79A$20~@=HJH366d=T;!BHcD zVg|sbs8VCegosLd`aT4`Q7-==Y%q-{icA^9qB1f-I+LN-+u|BSn}|r{UpGFAHYrk# z2qO|PVad3LtVg`p1ewg;rw!SFq&LEF93_jQN`f#=vK}!IA&`hlexYkotq@`fI08SI z3&F4$W`UqsB8B~UL4Hg~B7i|De?sG9TqaMz=CVOCED=aR5M~K@VkX4q@`8dS9FPZn z;D#7Xgvy{nChVf5-EpqqlU$($M^prcD=;i+q5~py7=f8|m=Taf@c^G_)S$)8runw= zypI+_a5M$cN^wjNjOAB|e#AbP3ro2mM3x(tjK$67^I<+y3_}7oi_aEN$GO`7lQRbC z48ztO|7n(qEwTe`(+{mrE!gARcMoTbKIt=_Z8fYf zdl2{dl4`}M`a*Aied!%*m%~Gk;pzwW)%j+-^o)U*Z8g_6ICn7r3|;GRo5Qp(c;ysL zkG|BDu<4p+fMM!+;=Id$q^{?3t?o;k+dqf8ZN`}@dV1%w$^#J#B+}}aMH!KSmm521Nlhh`)wDya-Qd~zv=;4G z#W5!%?6Rh9Wu+8G;3Z+YFGY>Hw1}K%&RWV%waD@9-rhA0-FVMAkuK|ZHgQIgba#%A zovc&N4!%D*Tk=D5rSw+TN%Zc)Ua!8G!ID6o9Y@wfB-Q27ytj@HbzPM#?+t9Q*OpJY ze7$2@oY7+ObzHB0QF~fX%$b#PbV^a+xo^H;ZSRVac=sM!smmNnc1po$WNFoO?01LUhh+;H~l?4oRM$&w!gW(?E29r zpX{%HYU9YST&!83PEhWlTo?{jy)D=cynfPJ=)W6AA9U5N8Z;!2JS$&Ql)FP|JzTLp zj5g)_Wg4eg`sI@9EhqN%|0W!H?z~)>(PwS3^wu!ay9YXVc?4q@<_`>g*5cVXn%i{6 s+9O++@wWC^_*pjp_SF75G<{(;C6t$5U7Hu`ZTqu?z+uqIpt!7m0J1r;pa1{> diff --git a/toxygen/smileys/default/D83CDF13.png b/toxygen/smileys/default/D83CDF13.png index 2c7289657da99f14f8a8453e54e697d3bd7c86a2..37c2f2463bcc442c7ce65c818934d49a050ece72 100644 GIT binary patch delta 1780 zcmZ{iX*}Br8pr=^r#k8=rH-~UD(Gxybkf!pG9`ovqDX`!NaDUi5<#4CQ&)A3tEP@J z9aV!mCaP48?UbgprMkMMpVlqn2tg!~Z1&Z@+E>r#c|OngIe)#Xzo?bzvHk`Cz;~wV zXt_BS21CIB;L)v<67pAa-yq1@3k3klCIFC;0{|j;=usGB>{kSH2~;Pf9dyx z0)XOW4>ycsesQhrXg0U#4l#(1@+7*T{BGw5uTC&{@3TbQX4%od>C8NPN6f(|mQ?U3 zEiuo=4#g7AB})avIfXS)7)m&s=z|Xzuhd+Jx$l3VS=u-YrpP<5eJmgd{S$|N6R;<7CuPV3KXC0o4^sPp=;gJi2qo+D3|Qz?UP=x zmUf&U~*4F+8 z%9DgA#!?gVQZjFMb+;exzn4kg$wXsPL6>-?SU!Y<6BbK|3W-W%Bp0P+mv{Gc3I*?_ z2NSZxmlFP?gZTpC9K-4-q@#6SUc+_^V%@J+%9__LQC4z48MxAIW z%f`+P;f6=!!UMyTqG*|+kx4DBjR*WOnNZ%ZU${~!-K>=?|94%;byt)J(Z`Px9-Vft zsb+hnRl@I(?)OSI8>E{RlEqXzINBXUMxld9!SRvQ%wo!8U zDb~UUAwLoe=|=GM3H9_2C6H)U6**go5<58xD@}MR^96 zl`{FCZ;IE7#EYqWGa-A^{A^%-gB9_Yu+@B=u zjH9=nJDWqCENu{}6sP%q2i`N=wLxo{Oon5PebXh^l^Mg!T`oaITo+foKZH{>eD%uE zaBa;{k;B2`+3fY@!}Vo08;|F36hUiipjYD{T-TCFID!9~*GH4{3W2T&_+#t7Gjd%5 zN(>~*QE6F8P1y)?le14=R%+bKJnlBclHU84W==6m)cZ6r6Buau zu-&it_d5i(zr}LeYz%u?CCO418ULUk`F6ZWnNb@*x%_Z}q)m+Ph-8j5oKU;MDZ5)V zJ2=K2r@r|E=+1Rb{m7b2sG4cA%dm_DNy_45pwm<3ReGZ_H2eCCeedoQO#k_1c3TU% zur;jr!90_vxvnBwPPTM}M$It7`V4V`z4`!TfP=hSyn zR-Q23#!EWEsx*J{^|{QxQZ9G0$54N|FlG2olgA@X^Hg)#w_k81=eORcOy35_*7+F_ z6-JniJ#D@w=OeJ1+N=G5sUQa=w*xTq`D?wR7f4xc%m9C&d%zH3{A2Doo)s zYnQHbmy&vinlr918MKsM(EEYQ+Z!DzO1Oq+pNzEwt~ux1xi2sR5*cYs85qCRDy)VFuGGg zqzo3|qzllht8u;`W*}CN8x&`uCig>^&s1UuQb`Kw3Q=bXHYrMoi(#+}4H=yUMGtGA z9GU36P9xG5b+%4KK)jrUR?h=Vl&NBjKV?wbB?lA%B zFtR5N`M?b1fP0K|42kiMM^XIK9GX;EpzJmlI?gqUkb(&FNxac<4wRirl;zwkH@Q(` zO%WB>bZhDAX>lZBuWM{#rCGT}XMV)B9%JOwYlb*oOW$KB{Cbm13NO9+HIf?=B@XK# z43QU1R8gJ8cRdzWln?zBu^Q!kOKfiZo(U%cS zW`tM=(L>}0xC*{{)f8-Ldc_QL1#ErI+}h0idob7<3|60-D3=re11Pb<5ur){A4o4v U`0@6Ii^>3ibaaQe+mo{X1NA9Zl>h($ literal 1717 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 Qth3!WVkkmb#*feX2U4=E*#H0l diff --git a/toxygen/smileys/default/D83CDF15.png b/toxygen/smileys/default/D83CDF15.png index ff5c8e0fb8ed0ae12252e01d0de0508f5fd94de7..8a915537b65d11085d46d4bc987fecb859ab9b60 100644 GIT binary patch delta 1781 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 diff --git a/toxygen/smileys/default/D83CDF17.png b/toxygen/smileys/default/D83CDF17.png index 97e3de6fd0899ae6dace004882d0933c77e79933..18e5714e3bc73f44a4ddc8558494a10d048bc976 100644 GIT binary patch delta 1796 zcmZ{kX*8P&7ssEZquwzyWoC?;E@u!+&!`GxOTrR+HXNQDEyJQVl%s z@oE?ut_JMmAr!_*yVG#3{?Cd*ecJ_=pHva$eEpy^TlGKS5Uy zVsiSKy^V+4Gnt^hGcJjHW_vmv<$}vC6z+Z!l7nNU6N!#4z8j-dTa>2_0=+swu(ijm zy$nS;`E37{A3$O%<&#UlVG$@V`SQJmJ||Vr$&?)VvS{I<8v^CIH3g2hd5^Xtd2uc_ z2#iV3V-t(=}F#fEmXpQexaN zB>DV>y$z9kwNoh@-r0I3YP-o7Tu9>PM=`lLJXN*d_FJxUwN|;+y|?{bDIee2nryp! zn|Hd5otj6F;}b~H4$i)+6}R7JDweC1+g(c8fMWge`h3r=J1uGaB1UX_WHgvY3W@jf z3AT5{?uv8wR;u<^o0QUC`No5l4^53tHEI0fsF-vg|8O*xf^_m(8zRYPPwg#Vl1VP^ zF4gTx@2`rRYkw6+$Ma~h8IcSwk-~KM3PL(~@BGPD{$04YRJrr1RI%2$vDjK!SwId; zAce${LRt7ghKqZ^=2$qW_{dW(l_>uHSvFs?Ca$Qsm>U$%@*_my0%&e%64D84V~<&R z?jxO`D?Vn(=W@1ZILlMu`I2-ZITlZ3+S>mMXzMj~_b;~WWA@5)R>A3LSC2p^S3JrI ztD?62)MI@Rzcm^B7wnvAEXRPWnxjV zt}OqmN)L&`V%&S{rgI*v5_OIj&O=?Sen7*}t`KXqrkb0TNeto;BDs2rU^1H-o50ZH zWF#{5(piZBU{`!G5G$J2`*J#^@CXF!D)^ATzYQCB36a&c%9o>t*r7CM@B`=v($n|#`<>&cb zvt`%xG$ynyesZcejy+-7bLfoeLLmn|Sd{=Von$_N%z7NYMFtH8ytfc7d`z_?-SEc| zD0KV*`m~n+kJ_%?xQ0Wzf~P4n6`wsqX9uD0eamZ6`@Fq`06sxMI=I4b!kyc>bpc-o zFrn24{p=QBHXXehA5=5sn_FkP{DA9gwEsfR@74GJ5SISe*fU9HVopA*aJX^QChVPK znBz93I53_!+V>EgnRETszx{Z*b=!@_Q{SSd7LCXLlNaw#9*t9*to$zYi+7Ps3-GaKV(4a&p^Aneyb703)E2!1DEyJ zqVeM97}wZaXs9PrD}-r+SXmKR7zs7K!xfs^b>>HIkD&}H9#cYU^0FwWUh=)v2cc_J zTc`hS*jODi3m)Iavn3H~+>xmd$17G*o+F!HDK=AnPsbO3yYAfaq`pX#5ShwEia_j$ z`MzHYyHDhS`&FM5PBPHsupD=vgiyX~ z6d~C=E;#dyAuNZ;B&GSD49_~vjl?INwSu{Uv6KuCMqnzA6?V#vR{kX{Cm{tJAHu^# zlevEEh`UE%IecaA*>Yn)A(ACKCv0Py!Yz*t3GLZ?g1jOf^Q4mSE}yeyE)>Sl+IObp z*6n zYzTmP>8*d~!0}<#DU3KCK^%G{hk>LeGgJYvgj-sg!EMbftcVtH5!I05KL?<^TWy literal 1702 zcmbVNX;2eq7~YBkatH=Tl#1*`gy2B3n~)Pz4A~9Dn#d6=h;&$z4MdV`+^i-bf>Z3M z2C4=008h*#vt+Z=N1mji&4-k?dO5>=}tTEfJ}92mCUWTDvrusqUk!L*q;1*YNY2D6m;=Y<9)XwXTS zYxycjWs&0H zgvyjM-#L}2iUs8aiGzGLFH8%;FenhP;c&h{0EdEH2hzL$L@{NHUP+l56lIZcI5wM&ZR4>CGMxj9#bSpBmm5YS!mL?l3bTiqtxKjA5ZtOI z4Hn8km_dgkmPTx%q)gh=cOjT8D%E>ovvn#_bjmn(%))`$5XWS4#5IYwQt|jdZhR1J z)nr+4PCRZUHj!HTJoHPZ$#m}i+mHiDdm~v*8t6m8j0mCKWWvpq5|J|LFKnGbCqcO4 zaIpyGBPc3ExiBn~D^QUjN(3QtF$yb$(;6S*@&pi{&KDPzi{&tkaz$L8To%p^SHN6h z6e@?Nxk|H@!pvHH+OC1NJH>_nlPi&vI7Shqh9Hd76%d<2P=qyuuz>Oe0mxcwFzX1L zHNLlH5LE98lpQ(WEu z$r*=shT|xXe-+F07F~gk>HE^B7w?+~H`5(M(yh_t=`RC-8Ml>)Ok?lLNl>l!kuAv8 z>+6btGLN$66$Z~KcvO1FZ7pThPR`?@`ucjjU)}6*R@6@lb75rBO~1pvUFs^f^QBsYL4#g9D<>aPl<qrY!;1i~-Xt{H!oyXEQ3#tCXS%Vqo|*Xa;6 z=KN&H#yaJFjnTBRq*y7c&Nm9&)wUNeGwT?Df88#r+*pkM67-54nsBpoZrRC_^KIFq zi0bANaDVDka^8*IExfNqEXc=m|Mr3zP#WWV6lwyGdFNKnM(0=67AyU*z>r#*DTo17 zu94qR%RKsG%65n(femwz)}_t!yZi$V__@wqmoyv@Tn}~ZVYs)o7{4u@HntBaZ|)6fz7(W;x$R2C_M+KU$@v3g`A$p9M^i${jJl?=q;|v{23X+4~`cag4$Bu>Z(7hUZdBaXc!+kq|Pnw z9lIYbY}oqXe1+~!)bpNw_L?Ib%Hs$0HKK$RR!L<=TSb2f-~^;p7DYE}IgO4#hZ0pI J)lsQm{0-ZKiyZ&} diff --git a/toxygen/smileys/default/D83CDF18.png b/toxygen/smileys/default/D83CDF18.png index 13f8d9cb7e5fbd27bac260cee09a81983e4dadc5..eb66d26724e810f3062319c8aa73f81734b20c43 100644 GIT binary patch delta 1781 zcmV zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0w_>SR7L;&-2eUB`s$?R(SWt2 zY;6VjWc%lt{_mk-XLa}3aP#JyO;KO;(_ltPSN!CCKtodT%Thl>QS#4R zLq=8j;D`0rWLRBn|NPnR$4fmxO@xY>{O`5vz(4E5ML9f4aCL?J@3QN`L*uwRIX+8o zb%gfYf6}l^wSSdAerrKcOhrLLO;cHCa&&cMYItXBdS+^RT3~O2g@|%^h;(|1b$g0= ze~)r_hhb-Tn3jusfsuiSmxPL$dVr8_bc0)7ZTR1J%*LaCgNbx{i+zKWeuR=$T4_yD zV));Bw6T|RcZ6$kfNpbwfQFWSgp+S|gL8R^ZghlVXn%K8R%rX?gze&)p`M0kYkO;P zfo5uXcz%stV{%|-c2ieq^3GQ5-;kJ+c3EC-Xl#9PcZh9sf^c?+WNLa`VscGWWBJ~1 z@85=_o_kYSWng4=YH)#NYkON^Z)9qHWN3L-Tx~~9T=mpm^xb*2ta)K%YEe~YTVQcw zW_Mg+a(`7?YFc1%QCDb8P+mbrRPf16&AV@LaAHJ9SXW(bS6ppZTx?EKV?sq!IzLR~ z#a^0?U{O?EN>5)%OGt~RBFp~y%Ub+exBj>5=KlHX=C`+6{#pXd%K{_m{plkk&HBtES;y`A>9bm#j+MOt z0004EOGiYv-2gjh00007bV*G`2j&M33MB!|Rh+v300Mt{R9JLUVRs;Ka&Km7Y-J#H zd4F(aAb4$X0020Rl~r4k+%O3I=M*`D3<#v-ct+M=s`8oRr@=cjUXM4~*j2WS5Dm@4 z^!w*8`Vm*tMsk>!i(B z$H6MT>Q>e8hCvY=VwNcz7rA?r)tljDcZrzbBn zklo^hK2LC|O-C)n;oS5TN##w12mC3r2v7y_LbixXjWuLpb#-Z45shlay_ zzM7zq@GLN-=qiYoXtFm?E|9N)1!f6&C^MK#j5I5W8t6^nOyEJ1zCi|wGS(eL9Dpg2 zqaY3>iKGoWy* zd){IfP00P(NC2S&u{Ar%fQJvPqa@3rl2`i+1@wu7NOcU!$fEAO9z49#4r65gCp`c(|)Ws`fkCU>g{8e^Lz{vx}G#=wS6i+rEk9i*(kOyen>U4li~3G zFQGqJcZ%R$6lc`{008z$M1Mh47}H~5zycVVm|0la*f}_%d@gPtUI#}fXBR$xFke7W zNZ8fQ-NVz%TSOG3UQArV$II8x-!C9AND`<^Dp)!sG%VcTKf*UKGD?PlK{i@0CN?fU zAu%aAB{fZ+fk7cXBQq;ICpSDGIWNCJk%6I5si?RlyRih)@ z7cE}0blLJ1x_UsD=^GfXT(x@5+I8zU7#V}zZenV-ant54Teq28Kz*leX=QC=YiAEq z&j0|LGe;J8hR8wy0AK(mbVXQnWMOn=I%9HWVRU5xGB7eSEigANF*H;$Fgi0eIy5*d zFfckWFm42|Qvd(}C3HntbYx+4WjbwdWNBu305UK#GA%GSEip7yF)%taH99mnD=;uR XFfbok7w?fFCkg-nNkvXXu0mjfcsMjr literal 1716 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 literal 1457 zcmbVMZA{d59IlQSIJ^ww3r@07is+oXm-gN-9C+8>!71hCUNM5I@E-Lks-(i0TdGjjchvEOJ=|&BXhV6QHLmKK-L3}?E~?{nzsL!KF{-e z`Tu^cN=;4>i?)dX01#UfF`F=k`Jdo*!aMTm0wD}<@#ZW(opJJBf+Yc?gUKa9tDA6< zHj;1@e{_?K1%Nl4R7Mt`g(vClj9W_hF;bt~Bd`G=Hs0qU?D-@Q=8`UoHb9U2u0SB= zFhKiNxE%MOWFD1R!jkDF$r<*Ne7nv8#m9lMKD{8|CV2w%xeI7c?=wJ)x_V*m-yp7bozAa;AQAx~;fiUV@JVPcI-r1&oSmgS zJjKwUUy;aV3V8!0c)ApV+k@jP!Zf#>C?RDsAK{U~Qn}3S_Q$n|=6DojVuNfY2& zX^tmoI~lM`33ivc@N2nxlqCtCVKW$}AW#9Rc?{2Rd5i}{)79WR`zhMNc)4Bv<5`Lp zBU!45bXZu%4KC(aPpx9#XfYXK1hr_inA{(?2~okQLZh@GN-d^QnW1H_@i%%TeSr(GhJ$JN` z^Ylix6^Zxmn5Y59?@G=bJZ|jGt329Q7P6;rs-^tY=9rstSAw#yrahi&Ik5%%>R7^) z_Oa*f#YcPzlYiC~Vff(R!L&%Kyi@kYzPHf<_G0VVq0shkI`40|v~x?tC*D_)w{+~e zklTd~BeA>Y^V@aerf1fg-(o^`TgSe0)y^jNhgRHKBi;81{?*mo^J&UNp2fj@`zVqq z?y8tLK3Q{-h;6EmIQ#zL{0H6Z&vnK_XPmmpu#v7|r)YCx6yDO*{bYPXk^N?5NmQl# z^7sK)#|VvfrZvZvXA*TsPv6V@Y3SWjpJs6U!a*DV*R4&h)vhS6F}5U@#BUrL zW^>q?_^$J%4I31?1n0}oBRWld&2&{oW= diff --git a/toxygen/smileys/default/D83CDF1A.png b/toxygen/smileys/default/D83CDF1A.png index 48cf54e89d70783ebc40ae9b9e206bd1eeaeb5d9..edfbed2759b5ef0548bd722bf61ed039da7e1d4c 100644 GIT binary patch literal 1801 zcmah~X;_n27XB2DZ~>V6s_GLlXtCXva>czgl^a#Sjnni(0H zLLej}VGK1BPe?>KQiIq(k_adLUVxB@2zmsW9ublJ2aq`kkeSGY-z`G^98i|LP+hC9 z?TDwrg=&Mbv8T4ayQc21)yA$WL#M8$LtaoFpU8=(GMR7zPh2XJmTAhHi%T1eH1$fA zkp<_oI0Yc21le+kTb#vHr!qt-=|X;P1y5X_l)_6+6+m1SoUH~SIW->Q4{4K-{AA%v?@Ru~1T3 zTw)v?93B`PRu)ws9URh>)#v5wMbe5gZA)pTDZj8bTTse`mFz4PJFBQrT~}GvUZHOj zOUikIVtOjC+SplB*Oi>gGc@!V8oIeW6+JaaUwc5RtWM7qr!s^oX#xhAr>$;JO34;T z%H;($)OfZ;p;r_c3X1FU^0o1aSrSDRUsMtu1F|^CMFnD+HaRsXArYn}z)5s2Ph18< ziVUWdLdl4ZW@P24lqv&1w^WcQ^wMBJlsX%sc3d1hx`#G{N?Pt-~t!5HaQCz#lsWQ6b(UvA3zRYul0t>|ChI;gADT;Eu4o25ECGT-Mp zlUBsx>`nx?ww-efWjw{A>^3?uxAh&E9(2a_%Si8=^HvHz$DkNtC!~!N#(3SzFdw4z zGoR~}bLWo}yVC8tM;>L^8YmJc+b+~x+Q4S)mSfD(wTz!uy_LuA@wz?tZTP+=x<9S` z^%tc;AJOi)3x6Z#@!#7HoEC<&$0v@3rPZ0YqAg{pl)JuPJ37@I{uI{NT`Rg)+>e@Z zG5Zl!7w_9FIjncohiM)D;;1OuXtf=+a<5}RZB&&F-jisqZzXz9Y-sr;*d?xK`)+NY z(6VT|VE;{r=JB!-r5!m=nf z0^vKDIi2oSn;B?Bv)3UCK!U=J7b4FzWd)JkMCK_ zvU5emU(L@Rst*2)o$0;PDGxSG5yD>wj$h}WS-1A-twUj0eQ*2PxHZxN*V!}MU)V~{ z>a+JjUq&HXT}{U0XzM3fnaEK+6I@K>k_6WZ}i^05$GR@AqBMhpFB2v?D&WzNYX4P`&lfq*U2wly^Lv@-ossR z+OomMX6UO4^Oct1oL3z~iw}H^Rw3yOaIVL`%H);)pue!ZC)DD%5P88f>1)QI?7)D2 z*Y_i|ArWtH-I#3_;5qK5SGZnTnNCig-pne%m+D8 X90alz;xP#Mp8$YNpx}>$XO{d1%{KG! literal 1748 zcmbVNX;2eq7|vLTmWm+K0!m>Us6{24-OWLW36N|O5=u})EXa5*$pS%=4cWy6@I=&N z3k7v7pmqeUDAWU|C?liPwt|Q{bXuxHwBmSx0$NZ}@#qG@_J`w-?(FV&?DIVD@xAYC z;rzI0cem+o32sIs!wsy+2taHX(E=u%!T`G|(@cu&64t1$h@LyQIV@ll zLM@lDUOSbfnGc|Z6$khrm#v3j7!U|Rn8z0g;7|a8V1xtFw}1`vM0|k=2?NG17H!RH zNEK=2$}wB?O2SH~D2s^0vDs{(jSCXiG!7gd9`4jY5H^iqlR0Jzv$M@)(71veC-qjN zg)$Omz^RC(5ZRQ3MSJ=>1d~Ohc_VBl#}Y-SjAO?v92kT+CX+L+Q8YnV^Q=btP_Rrnq0ctqW=buWu;?$K!DtXcTp=n@ z!e|(ZDisI}OBFm=#^(!Q7!?Y_QDj`>ZCs@sRfL7{VL2kB@d`x7MQ)Cn$nUCoBM(AOJ!Vjb;O3BWF2} z=XJDl+-h8b8o94mIbjMif);KsFY@$2PMJ<==?47!Vwm5I8w`B2E z34Q%ORN(hrH#=zej6xW6_)d7v$if`Ed$t5(-RJgyaD4UJjjr)NCoGjr)bXGsj0<_B z@9gbOtUo?ic4W)e8eNBE*g@77T)8r_!S_(x>F}nwww#59=R=M<0{gm-oy5B?BJ8Ey z=GAJi#;>P>ll)u0dUC31@0t3v{te>7S^RbdSGe^h zUfli=ERXq%Q|wdPhN}xbSd&X*_m!x6&KkNdH$QupAAi^*;r!%Tb!RFov+MzD4mD3B z%NGR%+<`8-Lo+Koj>>pD*nIgd&fl&C#aO+o zLasZ6?%cGl!S0pXha%iE22XYW1j5_my5 z?HC?Zqw48D2ki3fZk$;a@179~GV5YJ3wrJ1xE}<}i6`nBo_I$_u4?}EvgJBFi?R6< zlQX=D+1SdRD2?^}Jiw8Z!J#%MicsCO$(QTY3n3y&lpbd8TM*Ee2vBz^YyqZ4^~ zC9Q_uzYU)x_{2m&p1>Y?cip5#6+QGEy=Dkt3_rvZ_i>LSx+^@=A zq`Y<0Wlay3O+C^orxp#7+on8Lmk;OPxaQ&_WzUJQ-7hO@j+(uzvbr{CS6q}8HK|A>nb;&>^JG=#W?Of z|McoA`H`0+MTyP6-7r=Gc|K45dBtwz$;E>D+HYUEK@6yTse9GRo`uf;j9L*VKO|e0 F{|`*sn_>U} diff --git a/toxygen/smileys/default/D83CDF1B.png b/toxygen/smileys/default/D83CDF1B.png index 3f93634cce3a6672fab699a8c7b8f6243f9d2de4..42516ba4d612fae5ecd116e7afe7dcebf4f2f4a2 100644 GIT binary patch literal 1510 zcmZ`(doJ>Pk+x{;2_N~uW!0Az_y z1TyRuzD*$Y2mm`7fK3=;PXcfr3&3;$ z0Jz@)P-fh&b+-aQ%-hYy!x4g@50BOVZT(L)VB0~sCFq?P4hA{Mopj&{a^N2(m-CI! zR%K{JuUG3v;3ZT<_Ri8`0*2K#MZ@JgS@LT`$e&AdqPT2qRdavhtFKLB) z9uiy}tQS0-cW$K$_$aPH?h2 zn&|4ddt$eol$M#=ig^$K;_Nt&lhI^q42r>w2o4RSqoP?1Ix04t0f2etO;7jok6480 ztCDr14B|82{I}J?y!bVtALDjS*2jicqnv9B((isMzB`uCT`e>T*W$=T_=g#mrFxz? z((3g-yvh}~Y>K?z7LanuRMz?Y3Az-9M^@ zLbJ1IUT({8&ZzF>UbgDc_)~9RYr>dVc2359+c#-uxsu5zA#3R(wpXEISd^hj5;|5x zV5*2vmkY#j?r@_F5(8#WuO92#Q&azu5Gyt`;7)QNX-k266~*TV{46Dv2i0(O3idHQ zkcL(6q>h{ors*ZkI?O&huvOlFPHHR0-BSP4I1+tVq_5_ezwVF(I6op5)ygwXr%G=9 zF=~b`p9uBo_RT5Q#I`3Z*1rz&>2X7N9_m!WI9w{U_NihLXw0OG(lY*tnQ{E- zN9qMl_(g&wt|WH1O_xErOfg2Taz(b5Myzj zuUWX2KQUc3mTn+udx$qpe~>Rv&YYuJyh?DMRNkADZm*7F>B8T$kVvreQ0WT_fJZ+y zFqlfmi-*<5ZO&-iMu;6bC*gnzLTV}c@1szRqHK~uK z&h0l=w@9vBno=ze&XZy^+#`EkAe9=9OzA#hZAhMRDxetjclY71=?Dn=b%FM-rjbJ|OXLN1VCT literal 1501 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 KpUXO@geCy`DJALv diff --git a/toxygen/smileys/default/D83CDF1D.png b/toxygen/smileys/default/D83CDF1D.png index c9829492b5c321ab2251c5512a9006ecc0083106..3c2f76ab89d840251085fbf9c952092e85ea7f0c 100644 GIT binary patch literal 1833 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 literal 1650 zcmbVNX;2eq7*3U;NCyQ*@hG)RKn+5UO-Kll$RwLYq6C-_fmSnSNfxq&?1tTqgaA%K z#XwsW(-FK{#yaSLsCZKG0)?^OMOv^5wK903I4Xi7wHpN6AC5n|v%BB1&-1*;_r9|w ziSdhkyk~hc7z`hEtU^nVzV64%lYW2x>%m4v5LIWU1RWF|Zf0n`>a1JNR| zDL4BUB$~mPn2zdFsZ>pz#E4rsup7g%TdXvj!HABrTVZ1+LIG(=28v0U{f(!X0BVvl zQ-m6x#tI>3G}b{NNsf4((UED4GBINo0?~E}Enqr|>H5rA+40fZa@+sFe!KqTUT5kip&31dCOpc_)mKV~L_u#difwVW01OlZG1* zgd!nGrR0O4Oc?>ng+dVsLSj)A#2?pqAD5;nd0>Q6!I#r`C0`7xAf-$gDObrLrBI+4 z=c+N1f-xg9ZWpEPj&T(qOn43%>OP*g z(JBxEU5S`f1a1LF^D9B$V_&9{%RoM)iWDn&?zrWAAqWW~BUJndu_98aWR7u7|0ic$ z+8M69IQ~;C<6CqEx~K0-pI*Fc9t5L1hM-%c-|!@n!I-c^t&r*Lf2?syWvgA9KaULN-z~^n)tx|AcaBI{LvzJJX1_z;RMV!aa62b<(!$6| zj-E!nL9{Xbkk^ru>z6r74;BfY<}Gh&-)v|suN9lW^VZ!e-zPvrDr}+YUK4$Ipzv|518o2#e zyLZ?Ru;X!P^592%I)RlPS7YkUN5f^r#4_t|Pnx&1gw5YsfAVx=_PwadwFeKl<}IGx zeLm2UaHfB{fxBaANO)jL@uk-JHep!W8Q<%L1H%p12P=brY6`g7ajo*NY0i07V&|38 zHXr)~qicTfs#r^dck1PnQz(c8us!Hg?&SwQ|f$b zYyGh%_Oz0|X)Q0PF0YncQHs8AlW)*2`Qg>JOFaip&3W`jZ!FrmAVD&K1d-qp_*-`enMeRcWqeM80T;1`c7HlO`|O3;eaBlSfY=kmn7 zHZ{2Msq6+87W4br^Iyie4o^67cm*}o>-cy}`|iQ^y1tB`A9U2^*Q3365bgbuqCCDU zl-=qP!0o;6RlM!)vFFiquXK1^C~_L0;VZVX#GK~G52Jyh_K??p2FB6azl*;D%ah%I N0ktw-u~%*=`Um1nf?fas diff --git a/toxygen/smileys/default/D83CDF1F.png b/toxygen/smileys/default/D83CDF1F.png index a5aa9595977d922f0cbc35f178a13692bed55dbe..1350976d4164e1a1a4895ddafeebeb030910a98e 100644 GIT binary patch literal 1652 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 wL<$B3?LaV5;rh4F?Z1KqK@>ka`s49c2G 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 literal 1371 zcmbVMZA=?w96wS-hk50y3A-W$F4E>tXc?N+xk2I%@>2^`l0PUu~CcdUg( zx3Y;YxNHj%h|!_RLW0>Qk!ip!K&{I}2)HF;f(#8(jaT=2_A`RwH}gr1IphxjHUyQHg#x_LEb4H**eJ<%Wa;WS z0!uzSa>(MrJONg0lJ>T%Vs(2(jnLjK&_1N>dAKyh00F@j;!f2V4mDY z5qJ%vH`|eQr@Wp@m{n8}wwOtifZ;eyQD(fvLQ(j0FoEF&ih-Ll;U$cPVhAg|aUsB( z>T6)CIM;?Pu(Bgfx*lLqG#Ct;gQQte8&RC5>7)ihm;l10waGdkGRay|N`VtKL6ri! zq{whmk*`-;bvpt)T|a_9;PGq<%i2buK*-P#A3$+4hWh=W0 zPOvCvG0H`-HVeUGgv)A4X>7#>AghhCm2gD46~~!;;JRXQsA zT&m)S*W$}aTiB-wl5lVqmL)mhIgUGM7e$xjw2QNlBt|=t4X*G1#0&+_pvmI+PqC!7 zKm{hJo23sHo6RH2pkq|f8t)E15rv?%E;r|>3C-90yfZ^DR?J1`_Hd3LR+b8H?$4|m%ID!mn}LA zjvC^`VWIA)eJAf~!|ekj7e0>8eY7+G{HyV=0%8nT=1-j#M^ zafYksm)^fVot^*lY~-QcH>Q@mpS*KyI`PtKj_t;Bg3SK$we34IDt`VZXKCg_{LJ6u zor^D|KRi#}yc@9=CCH0BeSBYTCK-F{{O#IDFWwL3=DmNcVEH;C8{*`cuo^#|w)m}K zA%EiYOK%={XDIFPwXB@!>Ga&Xh%q`j2u&LoMvoscygdc%~eBjA|E(Bp7WGLalAs}qor^l1@?x4S2%b_0wD}M zL(zjT55=VNP&FVH&CZpA@HlWXfgX<<|MNt41b;6Js9YekQP|Iof(D4qgR|E_at3^& zQSi_sAS#5cGDs~!Ss)pa@;FB!42Z~t zpd?5;3*r(uTngz*kd>oF(i2ho{xSd3=L1^;vAGZ}L%SD~jkYc>7sSO7m5s9Fy) z$g82O1F|cjvJ+$#aJ&w9a$qDvU@Y1Sh7e@r?d5E*p|V5BTbQ!7vk;OVA{rHWUW>tv zdc6;H&YvrPFV1++51X4~GUw-+d*1fgZG7Hruxa>_xjuWiKTt{i~R*sBR zVzt&-?XopD)F3_?w?xp?!pU9)kZWxqB8rZPMDf&7>8#YZ+->9`LQLu@sR2PE8Y>wq zOo)q&=5w$jv5gkr1!57Ez zn+iLhyDi8!q`n?KrD#1T-_rJWkLBrPOsw3x$HHyhFtGAgFWqSs?mXJtmwsuN4-aLj!!cCZl8b9eTR8d0cIxR17DNp0iNGKIm%DT0jyqc)aTve^# z(+b~uJ#7;>y5Odj+v`A_QktZR339uFAycJx z!fKkN$XNDZQ}=v+ntEt&*IY(77#`y8@R}H%B;FXp<=ZnnZdvsoE6fYd9ooD_+kCi) zJK~|((>?D*gi3$btP@Gw)fT^MUyad8#xhxNil$*)c>2iR>rGvJ=fRDZ^8K~E28UlU z_GN^i-q!NM9|sqVMyBee*WbMnmnLQ$v=n9Z9r#&wIfeLbx>mFQrPy?$bI0~4V~g7= z!yXUimIxwSkk1HikO=Mp>-!>@pn;1t)@FCzSKaB&vh8~Ldi=i3w(E_m_qO8Bi`jQ` zGb$4|U0wafznAnBU5gjOKdNO7yN|EItC4C)3m!KLqfNJl1=?FU+Ss_@0z=gvG6HfH zKJTdB`=}&(pqk#Ly4s6c%ikU2vUyQFwZ%b~j^F|3cJZQ?%rkXK+rS|E=3T35&hP7r z>HRyen(lS8`UzJPIhHhU$Wj^ON|^qpSrtvS7=nAl)#lbF0WY?>C0VFB1Elr1em}zt zXAmY5X7;g zBgLN&c!}X^BLSvp2LHH7Q8Fum1Mcj&a1NFvU~xG#4vQ_0@8O^g1(GL~@Vy69@()fG BZ?ymb literal 1549 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 H_`-hygyFzT literal 1667 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 literal 1606 zcmbVMdr;GM9PbdASBF! zI~xvM>UW$z+>Z4cK4T00L%O z11?1k3WJSca?EincqVy8ywSA6Vp7v!bQBQj#6$usBT#_Tn#bBPrv@D1#l*e)7y^M2 z6~UqbpF5RmNB{_qX8=^L3^gfW7*MI?a2Tpm!Sew`0V9w?yj7ua7>24aBpevMK+zgc zXJJXCZq$}2X}}youwfA7a5&@+rJUolAy}9tM)Jn|QNL zFmo*6W~4H?d_e<>o<0x3YBLyK5VQ8tM2RVboRkfMVES@8=1KryftuG2MhKFIriQ&c37HNByw zwr_K}a>LlVJKwXn+xve1wC%$kKD)C+COq~pqjh0jt4o)C zB=gSvi7ZTi_yOI!*_2_wMOgk{-mft z**B$UPnJQ(Av({uYQwFuTiR37q&-)tt53R~#($Tun3tkF|82_oro0v#;8l5Z-Mrwt z17oM{7S|zvvck>A1+GsqNf{*r=G|==9Qd_FYjLF z6?o>_s?tK#pe$-RIK=svruwf5z{4Jv?DShb=fuXEiZ_Seb=~j09zIJih5CNmBN-!E XGm&`jLT7!3`_Hb|#*+u|<*WY&yklfo diff --git a/toxygen/smileys/default/D83CDF35.png b/toxygen/smileys/default/D83CDF35.png index d9f1ebe7e365f5f503971cbddb6eb6600f53fc8d..d72047eccdc8cd84209ab81f44ac9481059bf1bd 100644 GIT binary patch literal 1617 zcmZ{kc~H|w6vsD!LQxJ+6tDy$w=utPNQ4Oag`7YlgaBzkO-KR;5(qJJi9#`GfB-QC zIg|=U4FV!42wrFvkEp1H&VVCU@vhZYMI5m>^v5{U8UN_c?!I|D`~AH4c6YWknoYN{ zcC-cn*f1Fs4r1+Rhm|=pdtvQj5o3`GvtR(as^(1#=O8{>#Ne<1$oBxCNDaVeWU1&S z09zpdUI_pQt^>e9vHwgA5dc(GbQG70-AnRojqt4@`BaDb=tDhp>pUxo7;T74*;*$J z!D&~JgL=(^V*iCje)D(Wt+xB&YT^GnqQxDc;_At`M#@(L;;wF{A#$%;7y)7`XE`p~ zV1)CvBQ&S7wFqtLt{?;m9cJ*)CgqJNllnJd>tK2hH}h^fZXa28BNdSgt_Zda$yfap zhA^e36ra5$pS`f)@}~4bsn?!RubQy58>znaWZs2D)o@1o;O4K%^UiPZZXhF}MOOt` zL$ZuJQY4?0YhtpIh0z9YxSS*%OpQO!$2L*j4u>W5C&r$PN0N-{Eb4gjtF++ky!=%MK1EDrzXb+9h39o9S0UhL2OSJtlVAW9yz9ZD5HE6z$U{~mG*^FD- z;;0xto;_zg_`6}M{L}Owdp;kT{--Sb(gnEcB7c4rM=u|buWyl{1~x&Jx`Kl{wpbnQFGv7VRh!a z^dakqdASp5*h_6bJ3?(3Lm81Wy+Ebi!y(j}~Bhwg#>n2H@AA4>^L-uN!dE4~B#=%mdM_R(8s z)13G2P_Fk|&rkFX^Kyjsb<1r&SN50gvY}IrHrF)p0IF}La`8$!lW%Wyx&Ec106fPi zrozO8@>lU1OqL=*?~?uE*!~W6S!CLw1*ylczG>2iG;xNHCbmDe=M=73SyXM_)!=C= z+Ku`gqo0g6wS6fmRz5{vv2A#Ab&nV7x-(JT8k?<|X9{VzGW10+!OCKLo<7n{$KA7= zjPK_wjQQKfo(25uz&zbAy`3@d(5spl}p`hCgWA154j*=x6kdrw?kut&uWA1=4=#~6@T1xzxh*P-eA}6sv zjh(S^2OAc~*ka7>MqfYH51n}YzAPe;Hg{tuDQ+uE9@ltg?p=OpCuw`QDE7R^=!$8< z-T7vMrJ%gYC!o48{4G+ayNb$FiG?ajut+IE4Df^e{IF0E79YUHL&5%m!GVx31O-FT z9(cU<8-YAiEKSM#f59W?AC@A5<+BlDGgDMKLZt-2;>=_Tnkg5iN;ndsI4{d2LH>CF Mm{c~UgDfci7qP&6+W-In literal 1542 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 literal 1636 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uta=SBTujUjOw3(eEewICI2stbSvne- zni^WVI-6No!t{FP6_+IDC8xsd%>>yC(QAxXua$FAYGO%#QAmD%4lD%(WaO9R7iZ)b zC^!e3DQJXe=B4D97i)r|2jW|o)S}F?)D*X({9FZa_*!LRvESIp#nQ;d)!Y^20wY61 zM_TSXn3Q+ zymIHx=ktm`-J5GYL51`7+=I>2+ts6Xt4?Y!dvwCRdyB;C-4~xfvsveDcKavO_(>8fQy{L5;m&@_k zXupg4ziHpq2lJ)hTzfkAjEQvlOc%jN>5L+>?nYBvUcO)y+YuQ)6>?tryc-yIExi!-JX3bG;Lz9xOb2 zRl(G<`~Ky+7sf?8TH#j%|H$2y-CFR|=3s$J&w)cqTt=G>B_B;#efGc~9(KDI$E)w} zSkTq^?%gq-o;9=YuPzr5`1UpRxAyhh6JG@}e>!1Y@=HZcY`H*OXJa&v!nBC&;3LPm zWK$d#OSAo(Di>pN^>gHw!<-gxYKxaM8&90hGdFbN{PoATSMq&aV&Uz0`Pr7K^B-k< z%dF63WUIB6c8(GI{qdrG%Q3GHrAzr{elhsD)o4b<>!+-YPD=N*uXoM(k}CI*U)#!Z zitUcOXAa$bcPwOji+b*)g A#Q*>R diff --git a/toxygen/smileys/default/D83CDF38.png b/toxygen/smileys/default/D83CDF38.png index 3e76c6296d23980dc4d7cf680a90b95635e0afc1..ab096d3a15a317cc38056a6ef5144f67be28ca44 100644 GIT binary patch literal 1805 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�TI~>JYEbQ$cKXKl}3)_BFT8oF3xxU0d<+9s{EXPx?&Uy0+-R7|#T5Vb>j^m6$hUwO#q#crcMOx&9)2>hZdj*vV8YdrC>Qm+k3WMfR3|j2wxAo1|$) F{{S3Okf8to diff --git a/toxygen/smileys/default/D83CDF39.png b/toxygen/smileys/default/D83CDF39.png index aec58deb69f2ba81b71b1a512eb4e0604b4ce7ef..5c285fa9cc1601923b42f289e0d19c9deef974d0 100644 GIT binary patch delta 1428 zcmZ`(eKga182@f&cIP!O*%Zyj>|&Z>w6@xe7$Y=8q6rImD^pW9BRl1#m2`D0DsQ*j zin>aPyS(IfQc;uKP`XTc>(-gMgR+;g6DKF{Yl=kuKB?K~6uO8ru-R0jY+ zA{%dk0I`!D#0H@8xc*|CF2r&1{$W7?>?Z<{Cj;OU6v~?dAe#cfj1U0&X#gxUD_cVu z0KnFU@c6#V%gf#cK!N(kuAsq9E98H{MpK<8GX&%}Tj(~KBbqTt=`9mD#2e7yjtQ*A z2$n1aDMH{I59kK~^8g^H0-tQK@gUfg{ndxEL-x#<{?POLR$Jq1`d?KyED`gPtkPcKDo|W6YBqVgXRH4AO~|R8bnotq#Gc; z1w>W>Qu@~(EQA1>mMB*`nhK2-FAfWaHZX7jEp2a~&dca%XQ?z3o!aKV-l_UHOG~cm zr7?<>mG$XQXb%9kBhb$W6zW?0Lqt!^4f55S&@)2Ye|Ptoc@Y5cg8lqRaai0wT;`q( zQF3Y`PMnjOh?Asc0^lVt4`-LXq@a{@#SAtQp_OJaPGtv%AIr(LVvK*-uY4fldz6{f zRKw2XE-(6SR6lPzQBPIoG(Pyv}Ek-*ar65)4qfs!%Sjk4ohw^{*0UMYoff9jR*51JP+&MQ&(>q?UHqIO<~ohsj)^ zNOgt?=~KH40@H?f*&0nA{>9%h1OIYt^9gRAW1;1BEfDEGazA_ep@PS#EElYCg-L+xJH560@|V_MvOe`b zb35_EqO(<0#r(zm{O~TBM1EcFK3#vd{Lc16iM{Dtn?yAZJIRAZb6($?v0 z!TYfTeyr2o{lPHj3u{B3jt0S7K4PG{^uG9Yl6$ax^hm?gX|0_(BaBdP7hxZJ#8IMy zZf>0<2lvSsYvc$M`ikfqCk-TnQlYhdRB*X+a674L^J?6{nMeVrJL{qiBjKMjdy6D& zwvuNW(^F;VF2|iOD{#|#_UB&p{FL4qGxKiY)SQHACtiNKZQx_XY-!s;bV99H>jG|| zJNR(d=VsX#An_>9u;9!ppfkP1^?ul#-mq3D-7KD0B*?O7YqA8$nt|Lb535{{?-2b4nn=2|GJ909@Z-pR3-& GL;nI2RyAV) literal 1510 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY<`tJD<|U`X?9Bw)3(@QBhF7nZb5UwyNq$jCetr%t1q5W|m*f{` z=|Cf3sX^$Vi!ayBWj@M`SXuxN{8fcC{BoJKLQH5^G$UMMCB|s@2OFf9{^JRLgF!o|8b*gni*& ztT$)ee!cLalgc5z`8KA_E_p)J(wey4H++4^`%3!t47*ynixEP#S7S=QZ+O?w;C{Z< zBiP2UtD;7vw@TD&eX~R+i|fQ^@e0xPFQpf3`SSDAvtLnKb8Qc^J9D(R?*HZIoVC47 zDy!i+|H0;3iWIf$`;8{cot7yD_$w!lhTcZ0J*yAPc?5gKEuP#;^<@2WL zaoNJxDF=)`JT_jFz-(R^&@j_|kb diff --git a/toxygen/smileys/default/D83CDF3A.png b/toxygen/smileys/default/D83CDF3A.png index 1b83115dfa7cc65666fd18ba26ab80fea5712d75..6058a373664b3b0a4a5ebba9acf0264734d8ad15 100644 GIT binary patch literal 1971 zcmZ{l2~<;88pkgXL|Fogs0eCACjq!}7vn~AL#Vk_>tlpEHYP-QGiV59e1?!X z7oiW>l=x?a4igc28IBP76hdp0&vepvA*5RDznAGXOcCDPlRD%jP@0SRyZhtV_gtjJ`+#~1#&1I06{$@d;?{Q-#<5bZ($4xwLr^;pkk1; zf=mgA8X)x|P}xxH1DW375DXsi5MBn77Rc`b-)x`=fXxIs>4pCK1yd(*jey`HNVx#q zuOY1gGMgbp3XY+(#ygbirqeoV1GudpO`O1m1HM@x?FVTuBvykvcixCtqegnO61`e$ z3(nziuo8sLGofFAbO16Mz$XRFoNg?$tW_g{r8_W=Kz1_}^?~>bWL^e(7T7Yt!fnCC z@x93|AcsQyNhs-otd^OOVmK;?*fT)O0Y)wah=G;{6h3$fAiW;Sm2X0eYpJvj7gn8@ zKZI3)qz!W0LEHkV^$;li6d{9x?m14y!LysAT2}{DuSgBEd=?~zQU!2MR#W%1xda}* zVV3u!xrApkO&5XadnoFK!;L>Pvhw0>RSiSlQ7=DYT{htk=c=NZHL3YodiD%#TQ6GF_JstOb|2zBbFE>X&mg>(d=080A zp`s8!GOeFFZ6^1Byzp@T-NMKpb5hpBkFVQkngY8yOim@zm*P2vbSO>OKtn*Kd1*Y? zz-yY}x?E%Jumh2u!DI;-5ov^Eeo}N?0+%32Pv#PYykvwNq?LgoC;mauUWhSZt4`>j zn8wXb9{TX$**hOU(njT&-#}rXY^1UYXLBw4lcnp^$@2z1b8nxIuZfaZb^6gIdb_o~e@!`9W6 zh&G3?SW!9;HZXjgM-^SJjU(PI_BuhQ9LC3LC>gmH;pa?EQhlU8mRq`?=g`x=$YXPq zF9(XQmP|>yl1pcca%YU4kO@EazWKV@{8q8vhr4tF?zmC2uPsfqYC_ zml|1mWqA&_=6;f``Qv%|w>fLecxo@~6wECuBgKYzWlOxqPp@l7W6a%$E8kuEiegTd z9lfoccBe|$aBXYk2i>ryRez*0oDw?vGdo2yEk4Y~UkjGu->kBkvj6*zVN!LFj(&3uzqNt ze9q#6)!C=A({YN9`V=RNX?)#%!KIMcl;z)D!>`&sEMeWQ;Peh?O^nKZ8yRX?$jjmX zh~m;uO!|wIo8r`UtFl&ytJuyIziStmCgk?gf7&l|ByY}cd9BQ{dcRpxoY*CqAeZ`BBPu5$rQDaF_#dfU}FRW6HsGe@jbz>@eD!+bO zJP5jaD_7~SUhFM46%~Uue*66CYu6dBH!@aHL8_`77y-<{{Xwh)!66$rsUkHsLMS`z zV0c7i6r01{tcGwnlP%FPv2pQ#OW*}3n&ObU*=OcS$tnCpg2Sn4LJM`YY-@VP5m9DV zwpfyL)Y9s6q+y+#mtSzKu&A)OMEXUk4O+gfj9o6PI9_@D#K}|aDk558dz$^_nXkUC zt~pzKj&q)bG`D~A?S;DU>KiULHU(e0Y=`W3G=JaHdgW?cdq=0dYo`Np?C!bN+sEN> z`Uez)WG5}O>$*~TV`%th;;q|v?v6NXBbQO7@}BE%H+PS*`{NUyI>>8sa&iv^kH>pc zeSG)Q@B$&n^-ai{Ol%{}1YRrwCqlp_NAbB>KqMlGWKG;@ZEMH0C6c$>lkJICL?W3; zEO&cS|6d8bBu-p(`d=qJHa@ThOE6q?2uO++q($(#$c>W}$t6&E5iwi_H-eL%GQ`Dh PQiP~pex6P4;W>W-1-Ej< literal 1755 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 diff --git a/toxygen/smileys/default/D83CDF3C.png b/toxygen/smileys/default/D83CDF3C.png index cc737e74bd1908245cb619d235f7be7533de3d8f..18e70261f823106795b63662ccbaf7e1761719fe 100644 GIT binary patch literal 1685 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 Ki(QSaPx9XeO;@@A literal 1770 zcmbVNX;2eq7>)->#jENlB8*EYmy&EY7ui6-TtHMr0!kDW*3A+^*lb9a=1>boZACjB zsRcYL6|EpBBGe17I2Gk6ttfcl+3^++M5+@(v>OE5AC5n|v%BB1@AEwG_a3u(^X5j4 z9_cxfMx%{ZM9Y-aIL`UFxl-?G-R}@;m_o``WIT~dnh_&L6RU}37@)ur4W`5pwKcO5 z3!~A7rD_vYq$*~P5G8OX;h(ffIY)ZDoZ(t&;xQYWIn zcS;pA50DZ@4B#<243q^y0H4oCC8DR1q(KORX0w@T<}eAP283W3c5<-U49bFGvg%31!qA(12N`6T2{mdBq?XVF zPDW%IkwuE=l&9~HfE!|BJ`n3o1A(GK23imU2r*e8jyuoQZ*3x#*uQRk)Y_C_HDI6; zGZ9%vl*&h{?;x0p-KT||hLktLSfiFo3X&ls&@3F&lM0!LPJLmjwQ8Xpf+0DZBZqi$ zwj6>WK_r_a5O7!$o=n1HbLE2^AH#B_60w-i;tKeD2}H37APFpGL9hg7aYN*C!5~(l zH<5@Q#Rhe?lMYVo0YFg?{VMyfSd&v~+g>Nrn@Oq^i3y1F$vZDN$yi07Tn zPi`)K7JK~q5)jszpt{w=3l^1cm+6wy;&ENc*j$itd)0ya=fB+^_b_`!;KbSGx%9yB zg(Yq)D=tib8)#unnU)BUJkmqqq(`}*5>y18_0xPt|b*6 z0qf|I^9yFP_Xc)YFV>d?%G?a;J{~O@wW{JSpNkk0SVOnITEIAAfMW`E-<< zgG?yx1Zpy;IoyfPU5dFaUp!IV4-$t5$Z!7;QG3AKHng|i)&(Z`x4IoJ-(8v=b?q$j zyso95+ju^%dwRF=ZW{Z#l?CMDZ4oj2ywTJCMpBSBC;h5V%vb%sW>3kkK0lA1V2P-6 zM_eUtXu5NicsBAz!LzO%`DkgoBd_P!TE)Ul6lJm4l0feuYvN|PGmYeD58v^!5 znnsmPUR#PbRx?(nWJm53_sdYxr(hSTYU1W8+SBp0oE8C6~))wDN fF6Oqr`;0})5x2c~Wyv1n{QD^6b7lJ^DY^dub>_K} diff --git a/toxygen/smileys/default/D83CDF3E.png b/toxygen/smileys/default/D83CDF3E.png index ecbf4cd365b037a69b45e9f3a46016729ce604ff..e63cc585ab827e5ed0df1b9575f83c2300736f4f 100644 GIT binary patch literal 1888 zcmai#3pCqT9LH}w7E3%@qNpORXKU#asZwQ1L&YPt2t(T~NMwY1rIjFSk}A=lsHi@S z)r2US$Mm4ptyE~0ga*|yWkykb(CON4x_{5k+1abxJ)iskoqNy!cYlxjJ?G{GlJ@B! zHX;B3Iz%4=8M5o%Of7ZjYlqPbf{X^kBftZI##6{&A~c~jI?9I}06>}z0K7~9UP4>E z2LK$w0+RwMePN*I1IGw5?(z&Q><%-UUW z7@N!3oK0OX2nSpW;E=!(B1rZcs6vij^_cyh|LBoq>0yPkZX@JA((3$RRy6m6a9S0A z_li4onnH@27z&wCgw0HFzVAN-?fecDg;~W@%8c&z?HO*}u9TVeS0jgHTa=x_Gn0bF z`O+W8jt)rmp-j$KkrWNaz0F&pAD|G*I^*eyl&Rs^$wB&^7A%zKpHWon&d(K%UUi3( z|AVHB*nxV3-e#M#Qqo|(kwU89U!yZvZ>E%4Kn1=HsPKOU>c-oEy7a$5ZTt`3bogyR zo&Q&$>-ZnS) zqgk9pI+`7y2!L~*IM}ab9IZW`a&i;Q&$F{9$+WpIJ#6-8Y3GFbsqaqvpBr7{1rlpL zN#7h>^L79EQpjo9y-M%Zo5L0#cUoOxKYcRh6u;%bF<0|L8Y|7F4Bt;1C1c^Vpt~XE zr-wt;Ohxj@1GpYD-QY$d;mYBnBYCxclpgN{jTNH9B=;vwF*8R?D8Gu=J+ykFD}%5+ z^-hZ3qlT#L8FIb&Lgw|KjDw)F#98bn5`yA&t@OO=>nA2D_R;h@5o`SUCEJ*;IFl@- zzvnW&7w_hQYSUzo{p&4DKxnt)TRnSy z!x$KUvAlARRt16}R8*4j+cZR@?fc)=T;z8;)OIv5wue%g6)!QfuvV_J_=zAgeXoll zj&8$Sp4QD?>b`fgEz5A>A-RAx_bYRb5{ce*m(`9mdm&^BP}#>ue%#)8%(kUVvU~hY zAA`HWlDYEPr}wFj?Ml01Oia^QoL%~)hGHklz*#K`uX1Fz2;y!dy~n~%#-7f-*hE`BiHYgjR*=O>p4*u_a@{5yLjXmJs1 zgY4>Ng+;IJ{dw&RYkDRXlvLO*`SGCAjOW1z+P}`swHeMX%&)%vC=lg__QUK>IM}_p z_=U3%AXaylBo%F9v#;Z=&+hmA_?P#sead4ieAcO^H@2sm4Q+=np}fpfi`86%&?cUx z%2S~^A0EdqJH@y!Vz?hA8g;m(w>|sFlK8xAHuiFAchu@qP{o1ip? z_$M_dhw(4Evmt#xBN99)#&@EqR2mJn4yC_gJ)K4aFdJ`g+|v{KMdYDeDnJz!9X#J$N&edg98TZgu(5g;IJ-uM;Aw&Jr?VN#X9X=F#n4~d;%>d zn)7xCZzlO7WjS4m@ZHk#saMK7v6f(<5k{q?>f;*#m&+MIy*Nse<1C DheISp literal 1632 zcmbVMdr;GM952oTWg8J_Pgl>@ALigffv>21|gc3uLX) zhFn<|s-CjNuH;}o!X0)rt)p;BW~u~N!MRR{*DB_55ZaS@?dB4NRpk}rZF3`S6?7!{ym35vlg6{PTR zQ%n{HH|YtFU6Qps#Z^3$D^t+~&QP?5q6$3~kdaR@lqH`s1FCE>uw)HsGEg?lQupyZ zjus_ovY0TaY03yp=2u2O#lAoasiYD<%okxwcic*Z4=IIGwGiQ}6=GNgPH_#-lQSOc z49{I0|0@>H7F&Vt>66lD7f+gpFtHs&v#l|460GNNrtL{V6&ibg>5l^|W@paxxi6@_ z-e+wpKF?srz|Wj@CqxAcqN6GuRj;1TkcWebn(3jl-#ZuCd4z3$q25gt+*y;ot>jZJj49=m!Fo@4Qypvy{b_MMKaDqIjJ zNR0XOt*&bFI=X-9G>xF4dF$rJ@You;Ts|-ye(yNW>>9KMEq1yPXyL7yB_G}W!kLiR z|H(1o;C=70hCfbKuhGN+->3p#WR|oUhd$hP*rMzX+F{**PfvDQXX;MCPyo{X+iRKa z^o_Q@d@5}9&@VMdb>5|K+;O;i-W~tFX4O#B#NgG)4(s-yMb3vs>AkMX2Oo56OSF=P zj(D_dqxOQsXU3*``%1-0mzG@|_dSx_Ni=r9;|;4;ANgsz6&vhcaBO8A*)_C2be51z zT+tev9Y4G+_*Vb8{P>)v9p8bwsz2S`>a}`t;Qf$0Bl{1vtxe4OD#Ddq=kj-E#eJWx3@9pN&x{Hh{oXz=X%>V5^}9Eo4eq%y7MWk6ZYFm&ouA>nG|;Ar=4=Nmf-9BM zfcPUm(Kq{)tk=mo^}88Q?NAjbLGZu{{oiWVL$)? diff --git a/toxygen/smileys/default/D83CDF3F.png b/toxygen/smileys/default/D83CDF3F.png index dd5399e3b245990a66d5e1b0942b0dcd0ca70df5..789498ba451d8d66c5b9cf42dba095a8cf9b872d 100644 GIT binary patch delta 1614 zcmZvaXHb(_6oxNIKm*9E6airYDPjRaj5L*m5+MXah@l%z2th+JF@%nMp#~y>&=pW3 z9Rx;-4WojxY*eg(1$03X3l`KU3hR#h?XZ7#W@nzcXHI#~nS0MI*8HX+F@RP80IY4a zcEd(xznh;M0NusvKNtwuH)VQL`~XO`0zi-hz!Dr1yapfv1;88~0GFcxtmW4B2kZbq zsfO%J#sB>IGosp`T^E?t8M*%iHLQ$~+Qs6u?1`xMBNl8YiCiILx><(uG3bF2) zXlP#*aZjaBc!hUjJA++ErHHX^0(-cWb)2*lvcqMfyo62(rx@I}kf0I|SAjho#Ac!F zaxGv2C3sG22w992>_HxHKJEz?ZM z;+>(TUapY9o>oOLi8p}RkzpHCAC%C+NbHD6@7_--#=*m298#RtBzI#vyH^cfK<`4EQPk3myPGs2#~`8kd~BQ_%bD_{q`2%)VGo z3l!WidZclzsOV~X$3$i6&|%)GP?nUOFXLBT7x3D{oYFRy52x4M%B#ICEE!D8JAbJ4 zQEB;bMq+1V^+*o8p0f9-*WPL(y_zH*Oc9)A?-1DJUgR`CD6P9COzfmJ-Y;?$*uhT- zuZGBTu7p>(k53`(25Jd?FZCZNI-ur(F}B@TYbvMUT_97oYVzZ=uf+k+t{62dG)hbr zW$3#F#gzG(P9j}6`D7K(O*mncSyz=f&hc-UUcwE$L0r++96D#wCp_>McH=qG$Rror zt9C`m+paH!XDLMxy*xoa!q^*j)c6EHyxL2()ksSf8TJ4M0Ohn)h#JDDFp^BUJWdol zmSxIM;j&DVbn|fJ z=U-IMt_yw?lly`ax7m4B*;{+xu*l#F&BaKXPOB)D5kA(()tEF6GEl5Ho?rZ8G06=V z&LFEK>HV_-Crs79>)3C_R#>rpdt`=r+uw?KsfEM^K^64Qd_ecCnf{`!R3`8?gZcViO1?q6@GsS#4A@BBP< zqq-lqzJMCAr%KK^IBF_C5u0IiQU8YC;Rpub3D zm|Irwy`6hJ3a*e&#MGaH>&<9lP*x&Wfi5TYB6v_uWq1d%RtG|@{v_hhnlzw6zXGhbCcpz zHbMoB?o~0!>50$5d;W$~e!k{HN~s3rKDvju_?d2r6!{=-Y|?0t&c)B6w6|f-==U8d z^Wh6(pHg-nUZLt?t~%4h)7~u=ghKczR)upm!XrW4q#JS}A&z!QGrRNnrR;{XUUl!G z@y@}g*G6wnwsf~WUK`z72LTT{F*JsZ8mX4tJ^k_QLcaHT|N45SJ{cElREPkZ2$sJN z`D$_C>sa#fA^b=NpXI{jv0wwxC^Xsz=04^6YY_XZ;Pp2(qRC literal 1679 zcmbVNX;2eq7+%0i)X+F;r-AWUWAT`hWOri{HWIFEQc@%!1c?gjl58N7WJ9u$fFhz* zL{M<(p&X*sVp}O5)j>r?2B+4RqmJ6Kcpy}Z78P1X0U@Rv1lu2uKf1HK-}ip|KF@p4 z7R0DN@|o;682|tuWds&WuT$L*V

Y)9=FR#g~$6s5nwj*>NiYgz3m+0#ust6e54UFc{>J&;%xe!a;}0XtqflQqCx^gxmi20apvbFeno7;Kf3b2o3-d2uAo2eT%s8Vu?^BLB!zL#i6ZP zb$UrGrWmtDOHxiMMOh?#zTIx;*#$h(n!<-s6m@eT2$$C2+Oo_P?%Xn0p`SMnrgQgpLvBUd8%ea)Ko4CY7>WupF${$ZAw&$p zqH(O!Y@=|qmKe8dpzV%f1K*03$gBiTkybTH8pk^zCY7W}TPkS*WpN^KVS>S|Bki_D z?()2f79*^NOhTuyk|uC8zY@b6?4yVP3B!aknE<0bhv6_(Awt7pRDlTv0tl6J#<05o zlQTZ;4By=x|7n)-Bf10K+t;m63$KTVFw+xbrAK3?EvXa$JR6i)nA*{vJmPYNc+D*d zTC%CK@-uF6adF4XrpqjaU|K?C<9!XouxnqJ{{fKueY8@wthBVWu=Dy?f;A-%_A-1g zHnt-2q-FBBM0G`HTSExbKKo8bQ2;A5FSpL=q2)@48**K%bDU3Gt{WyDsoru)wji)4 zGIIMw!Ep%QFfy{c^X8-J9XI!^D1#FF9t?l6=-dWRW_H=x_OfsNu1(Vq-j^A>wxG zOy{kG-Te6x1h78dNiI?&x#71VKNW54It zRebfGR#y4LYS4e~h86Lut@)Leg?LY1QrxfaKWUQ}Y~Av#dmr%4!l}3RN8MmV%<|}} z1cMiNufI@R7F>M#7`y4b_W(OMXXxad+1uwjRlPe;F((9X%yHdsska|yy9BLX?Pufb zlv$oJt7jdGP{&-%&o35OJ?VoS1CM0=`A4gKmu9r+ntfU}YtTJ) z*iy=NF41mpY+IAtxMOug?sq4i{G!?>y*$wIM|W2SaQ692dC|1K8vll>@{X`R~u-7wgm0(?izk)-rJ|aq4_#Z}s)|%}!%q=*cw5 z0>t{bd|kh}UJMQ2C}kZbHjN~O&P=YY8ID|;Xskc<$)QtUo9)m=sOvmftuCD$$gJYGN8-@|I!-M`>vj&tas zm%ratj}-Rd`6YjPb^V;ZZii}Sjq!(y#}&+?q9XeR=OFwZ``zj6{RNB<^?k$%@2-~6 s=3S9Knpvtz3EP~Zw|7Mg7+w?DK*K{##*Ck8obF$QQm(>k!`E*92U`e_+W-In diff --git a/toxygen/smileys/default/D83CDF40.png b/toxygen/smileys/default/D83CDF40.png index 86ac7ed2ce7bfb2f88baf8eab6d209609c5e37fa..9699a95d2f31d6be7a265f1381bc07805b0ccb6f 100644 GIT binary patch literal 1752 zcmZ{l2{e>z7{?#VSjrM<4BZ-GEXAF&k8Oy?V5G5JHN_0Gq+%u}QMayLW$EZ@;i`m6 zl0=E&78NQw6h%a2WC>--mgRoZy{CIm_dCDudEWOu@AEwW_j{jnzT+;=BxxySDFBcr z+uKqRFSqbYEJ3W6uB@ptoHXQeL9}0j5b7&^QTk?t? zS9nW;W-I7+f^J8sGlWJ0v|2)^Ei{@Ed+0htG#GS&0VjB82^|(XLp!W{8PGw7K3nKB zgEyO?%c1E6F`1Gk#N2@Gyh|0^$7Dz--mv>GMN)!Sir{6`=&pBVUA3e_4kfu zI6R*iiYJ#1`x4uyvwHNhzZAyv$CR|P)h3&fUIGyxN0O}!_fPy*WFpQdQ|!dwi_1u9 zh`R55CITRO_UC4%^oqPI7hz1#j-qFec{gkcJlpv!P8Szk*#ES$Ij7`Ckt3(Q zdN5d}*2J6Al_hW+q~1QfEj*y`^zEtl#h#VMZP%6AN-HZg*qDN#q*CM8Wf;93vUpnY zqxPYq(x`Y*k@oC_Pn(OhL#|!bT+8MK$Ei#UDlQuh-jRz9dN^vY^Kspq&aFvyVLHdq zoVe^p&#r+)S7+1Fa`X9w;;SuZPN=x0)%bl7GhuinYWS@tm zMe#XVA`x*%?IqVzvtup^_W;GDkxZm5PfNgn4G6^W*u4_&6cM)l^+*rjp=IohZO=2LzJ!J%NaPH5qM7ve>eV@%TS z=lcyDKvi5;zuDgGR9y1SdY^%eOCGiL8gAh@agvaGW!95B))ry*vNiMUXo!)#V90=T z=e45uRqjR6*V?w|wOPuqpYTGCI`+y=-a2vRbEkp}(wh@xTWdF^246az-hq}|DZgrk z;!3$Cfl5jkOr=_8hK9PYPuHXH)hL{{U&>^Z?e>zas1S^wHV(y}5+*k`8i-Yn#WyB1 zL)oZRmuqte(sr^=m!-|*in^~tc^H0vknv7l`q`-$1va|=9w;NDpLSUsi5!vId_--R zuaOZdSfSx*PQSALXlj3p!!pNUl+1O(bW9a)?;r20V$OKbWKhnfMdOKy&WB_3h7!k( z%$!krB+Z@+?Uv}KIZ1`{=id}76_=E>X;Ln{>oh&kqJ9t?8y*{5UDI0KRA+^)Z(a77 z(GeY%PS=VImld_Kd5B$pjyO3cf&3xLcAGJiD5@36C(z*AdGeVIK8xTR$U+=!!f)E7 zi#OKYXyCRHPtZ3c7?`fd;|X~D%;5k4 literal 1601 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 diff --git a/toxygen/smileys/default/D83CDF42.png b/toxygen/smileys/default/D83CDF42.png index 640daa0835884a3a0d7019bd8df0e4ec45c34a57..d2b2b3150f7b480b04cd2734aca07cc39d316fb5 100644 GIT binary patch delta 1587 zcmZ`&Yg7^l7QU$XLYxv1pG1n5>#At^z}iggMGmGwo0S%BA~|J}(uh`rmK-0OgjR!N zrsCi`QA@F`l$a6C_alv?PTTZQbDOjzT&LM}nV<7x&bjB@``vrK@BaAi9kQCSlGqE4 z0bsM&7feGk;fB&f0a~u2ev3mIF!97b1|1;79UwCs;8#N{a~|Lv8DN185L^s^=aqIu zZ3jTeBEMwqURztMiUQ?HsAfVN2cBF;sn44mqE}!vkpG3Dc$SHLbk3;hsO9uG@H7{7 zHvxqAIN0`hbS(|j!39++ZXgri#KiP*p)VCwDOk;!_3bgB6@ZeBdCEgoN5Dvyok3j@ z1%^$ENEqXRg0`-1JGzPiH4!k71oEBhO&p9b(NQlwN@Qcl)$wK_6E-(GVzbwgc|0*VG^RO=^_(h4?f9|->lsL`S zyXtBjf0hzH*0_vav{)*{O{Kz0C2q0FW#X!>?tzECLFhD-4oejqe)$$ZAu!X4(Aor( zHtU!X`U)&j=-(bvlCM#XHho;zXO=QOp{-(^!Xa$<*P=ER8ATt_pj*5)DI zV?k<=LHr|p?=HwiI`1<8Gd_*J+icd%+S1LG;cH4K6mdy=*?5n47vAqxbdU1*C#P%h^T$-o+qsnu zIvU`tWEV=tj4 z?5cCjSL2PgFU=L_^W`7C)s8Pc{464otJHwJ{RNbGPkCv^toZDO0zvp)H#ELDNZQ{& zth0+!Ro|(fa`Ufd?a3I#B^M&!@`rQMa&U|GEyn3Zl;^HCnlwkq?d={dugwjVBeO@= z2uSMAh7oh2lH%$AJ+)czHpvBhdz+-1>IzT?Yj+ti? zdF%@B-lLAMQ@j=ag0DTye@b7wyUeb1rD%1$xuVr(i>&;&Aqy!|ZPzu2W`54+l-1kA zm7{IxP7XoQ{xP}FDw0}h+=+O-)?~P(#zSiooL?v0&(xgE*EF1TcHp4#e&zStJZd6PuiI45;9v9J~VNC9$k1{9v9svc_HFZ4{+K0UlZjfZeEs$LL_s6 zA1$JXR61cV&-R_-q*(K~uJA(p!GMk!3#62F!1c-DylI)``%HDCxKm`CBbj?eL}W*Z zgpKJ%R)T0Y;rd@Ux%=p)HY4{<_Z1*}VmH~bT94kb9v{^cLudBi)i|UM@;+eIDXss# z@>{pEe09%pw;56L@PpFN|8|jmzB&6P8$ZDY<2~-CEY*OzPa_n&sN*N&+D+;4$X-<>D~568??bAJ$|F&S4>oF4!ird-%IWS;>@CQOt ea(rS!I^oY5drmPb3pZ}E1fcDX*rlMdFa00e5V{`# literal 1614 zcmbVMX;2eq7>=lD5>YC65Ji^7yX4pn*&G-#WRnslh!8beQI=#$b|Bff*)#!Kr64Wh zQ8jp59r0?VSgK{z(PHILlnSlU%v3EFor;}~TGUQa+etS_Y=1cZ=+5qb-}~+RJny}0 zGc(?a;P5ys7Ar!RhGsEqWbhdt&b;HE1Tbcap*2Q2o6M)(7=^P`W-<>4bT-U_XW^K+ zXyqk*7K=4FpD-9{qdr|>B5eXJs3Y*$91NPpnw8>lV5S0`2J&zVVON5W&YS}Q!mI=r zOY|bWLycRBv|AkeQu z7bwA(P8s!?fSROmKq3(HO(F;a5JUhaNe~1|1Yi*a3q{P0@S!Ay1W~|JVBi87Ym_-( zk%ejpY%!7&w9>RgAr!jZZh>1YASsIwlFQ{m4jAS$8hmGwoyI(TyE7rgfZ|RQML1}J zv;#p#ERS^2N|5pNWe7HhUjK^N?i@%IlQN+Pa|j`UNNBSKf+-4Ch>|9k4Y$)eR0%R)1ZKjlfFTG$WvPf( zC0A=0Ayq9xw6Izxfz={dE0u&eUWZN9ND(Q7B%yGs6oNFcOeKQkQbY#JWJnUMg+f@J z-AQA16CSclFm?y9;x}RyY6{0_k}{BFVWkV>vaO~qX$6Z!Ky?{sLN?TYF+ zzV|0`T4kGse45rfK)^flf{w((qFof`FQ344gD^p_ZP zLRjU;r+3fqi2Q3s>HcH)k!=T-zSyV@>u_s^_S_wwIcap(a#z9l3+0YY`eOE;z+e$I z!#|cd*ws2A#{XM0G;OMGhxpLw8up~J54i1LztAj2wxVsH46ZHRyzYmGW^u~TJx6=f zVmEjW{CKbB38!m91t=?tYA&j;&S|XhZ7&^`ob&Ao-)645o7S({Ny_gp zOh3a~*j(2-BX*Z{-a(c(g%}_0df+Zf9+ueMVyaCz62Eot#SOikeKY62eW|M%TOC-w z3i~3~f3v*WMV<__2EyBV_l_NLY-Dz4V{XZgEpb)hw=LAu)7~$K4%*$o>wPE))Lz+h c*1wu9Vof|wZqN!c+Jb+0ohAc4mYQ4k51yu0{Qv*} diff --git a/toxygen/smileys/default/D83CDF43.png b/toxygen/smileys/default/D83CDF43.png index 94773f8e3875ccf2fe723dd76591aea17186917c..0be4af58f8139a02da88073dee04c3f8f5029ee7 100644 GIT binary patch literal 1780 zcmZ`)2~?9;7QSQwWK9?_Ay5ny0)Y@h*b&AO7L6E|h{_<81QHejg^($Wph*NZM3y3` zC{Pv;VpT@g8G;>DLJCSLvM9ocKou?H)S?CV!~E$uXJ*cs-t*qO_kQoY_nvp&edoy| z!nPXfk@Nt-ki9L4i)fr?>Y|a=!P$5xqAr{mKZ}eAv!qViF6|HpBf4~zYXfWXSQwi}V{{<2vI_*&gC=H^p zgmPm>gEhID3?&v&Y6QngAh!TH5fpeR!$O%ZD1N7LkzEA;e}XFn6A1*=gPj7*q=1)! zVS$F{PznZzKH7+DdgP;xkU#rMV*SNB1$)hFv7n^nxiGGy-%0^nvyO_^Hub`z-@Hsli=TR+kN&2Dk`t1>kFd zW`G)i3cTj{Wx#Di1vWy;4MM>b^LtuocF3-&?EXeESh4boaiInN=Ej+o5} z)>+aq(zDaC^YkeLKo#akMT@!o9CD`UKvHVDkSxy26q0i@G6B3xt9M2A{Ee*pA=xZW zriFqFMWB%T)A~frp7i}w zU!;y4JnUyxXZ~Uzsq$H6H+y`Eja3dhrmjq7F%*ANCVQM{sE}#>hNnnc$ITjSHB{|aKZF-l?{w3@Y=IZY4$%Xo^2kb7md}eZm}re zF2?B8SuMqe_@%QP2KifZd-b0^4B$j72MTKnUUcpgTp4YVN;v~Xxn&)t?y0*zsrEO% z(NVU)WFO0?7bxMiqQcC^iTO>REUe)CW?V6*1Hh%OrdeXKnt({^if zmI(7O<7CKbC+mgENz1i|nGuJKmYUQKW9UXHnQaPhVZTcDuKq zZldf@ZQJ*9x8M0q{iyrYp8bcm`E|AEGCL?Bia3$TLnI-I zFHC&Gm<@=wt|Gtv7vU<)ZZLPNV<$H948Z#PyHS^A*x`Br~ zhSz1W25z1X3JQ@CEtQc6E<(W%#;z z_)9SWp{_C;KPHTckVy literal 1665 zcmbVNX;2eq7>-&55iM8{)Uj??oFX*Y?B;Rnc=&VKt{@AJI(Y`!{T zZEFRN-PhY3Ar0G90_v!k}5KpcQtDPM?a?KoXu}A|&8%KQ@AZ$shq^ z1uCw}BFBxUkSq$1&Wh0Lvr_e_0bH>h2(pVA0y9qQ0J}Mju!`*xaGY1ntR2S?2#kx+ zsS@y&QE@6YASWpt5O8>GJr{-np^yXf1wtYG5rA-E1mZHUkPY+20-+cY0TU0%Xrm0t zVhyI8(8cT|ppm96VhFO?Y#bYpLsBUajH0N613}n~1lyWP&^kMtur8itz;LUcGFfO7 zNdOK;T@sl=OF+ibS1y<>D%ERZ!a5NsCS;IZXMtc27c!e2evM08X$}6b8E=%fYBMc3 zq`|FZ2Bl~6k-T^k%*5{PiX4KBHR4Fh#3V(RhLQRVGfvQINw2@p9KQHlhU9B;zPQGrs%Q%W&JCW2uFf(p4(W*~f2$rHf}Y!Vwv zSZN)h$0zlg7`+o%^sQL2oWgZ9Noh$kZL$K?Mv^A2M$!VvqlJK9tcfs?HtSMHdR}>p z;go3&ZctLB85obR*z^YdD8fUem_RP)VT|Q4EJc+_U>IfFGao=<$1O-&~~WU zl$y}(f1=XJFNi71m(GdX0Dt+z<%*elenZCo{CtA%v+A&KO?b~qOaG$arVu)JhtrhJ z@9g}{4!&y)>|9fm@^N#+fNx{#z@8@+*{8F&?DdcDT^Lv$*R?I)n~XMtN4&dw z;t%o5+X`ZD6YWJY9a=D~Yew$g`r(HjrDMmJ`qhi3AL!?lG!yuCv|@g6g!5N7!ny}! z!_DX0%Wr74VP|G|H=_;T><<_28P%S=^7EtGIN!s<;2oPYU8|iefxb6Q`qC#KE_`vV z`Kq_S+nfd1!gGmPOKv>yDfs=5$1Uqa3TnZ*^}yGq06e|=82dnXNY~Q(*2JPT)xh=d zx>RW+Bih_@zwT2>4L+yT^ig0SU`n_QZ1+frRaZuqoTaM5>#pD2{rNNV)0wMXs)}k? zJ7+&c2ijPlX-fNUeScoI|AabwYmD={PN(u?VVAe(6^bsf(;-T<@AzPB(lPDoambdGknp(SYuUqbL zy31Ho%RyO2xu)4I;JLfz8dX-P>pfrRom1g4+av!XyKRAIP;u0*WkcVhd8wZk(_jf) zTU%g63a@}IYopG!w(&OIE!c1>=~`4$&sfc~%EtbloY{@HXGJ`Rl3hbapZ2wlT=cpx z`yjO9=)OC3^WZz4a3udlc3-OG@dKmFsQc}tvei@f#e3+-BGH}m>=C!NRyk?T%)g@U z-1fjC=?%R4S6M;Vb}#XVuosaA)hZCKZQ_51Xd$U-1rY;Lx0Tx diff --git a/toxygen/smileys/default/D83CDF44.png b/toxygen/smileys/default/D83CDF44.png index f1114e758890ea7585251ceb30f86c0ee6f97343..73218b90f4be09b2b90a3a32874f00191bd6ed30 100644 GIT binary patch delta 1684 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 diff --git a/toxygen/smileys/default/D83CDF46.png b/toxygen/smileys/default/D83CDF46.png index a0ea6fce66bd53e770a75f0f4467706932f14149..cce496239fb81ef3a1b5daaa01733feba275b468 100644 GIT binary patch literal 1938 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 diff --git a/toxygen/smileys/default/D83CDF47.png b/toxygen/smileys/default/D83CDF47.png index ffe08febce892c7778a3b22bd2e6c5471d4df099..05ba90783a82a8a7c205da237959bb9742657395 100644 GIT binary patch delta 1640 zcmZ`)e^Aod7Qe8NNm=>a%~rFlG$qrJY!fWgHgw6wSrjauYl}ZnTge}-8<`cT^itDJ z_L*y=jX#UhQddVLz$Hxz%iRpMbQKL05&}zn8fn}2ecd;+``5d3=AQHU+;h&{d(WAB z`E~-kl25rG0RTL`*G9q$l@v?~2H@w*tKP?=VeA_JMFa(aGur{+@c~$bDc&>y-{Sx< zG65i#1K^lmDWUEI03rYCOBxA+AV9;wZu0)>|H8oeZ1ntY*j1ZvhTR*g@wdd%man6R>mJsrOh z8jaYyYFztxWT!4s@q!_L9xQsczkL+j*6-c)WXByF<(!%m6HNBE2q_8mTS+U>jAt$kjN69h5Wc**8e0QeVQp@+3r;#Q9>eN4y|B=l~)_~{!T zIzw+Yte@MmntZGpQ{>)wJ!-VHYJyvuCX>w9a$KI)ElwHtro~3Y$g-@ail(ijVs+~0 zsJS-hq<<#2(HrkjDVou9T%Tw0wmaEQmrne;F%3miY_)fhkcYA}V`>A|FCNBK)muhi z|8hYUmCs}M+06B`_R7X~|C_U+Pc7~~=2b6BO=%>RkL6dYwpTRVRtk;vLG-~(cgYmz zhvm}vD3uTX&3M(Z1WJdyi=&u7IN*KRf@0hg7F>OI%MW`zA#49?E@ud>D^iAuct5eP zW`Hc!)wf;_UEiq@CDi2V0|{=#M@Xbuo|N-({|>){XTxtK;!u}`Y9X>agm*Nt-67kA zwiSJH=Yx`f=-^af0)s@&p2BUyN64UE<1591(o-v&VvC6Z_7P3B5W)Y-2G2Etd76W5 zn=-sh;vRVHI&y*ghxDe4=xa^j=Ydv6QB1{KN*>_EuPhE3X{CHtalQ2|DW83};^uZG zRsE&Zc04s%cB@?-7gxGdf2D4X-QixWW_@1MldE65euIeK?VX(KSx>3yqLhEr}V1eWZPVg4xV{I<=!C42Gv&G=j{Rc-gHZ))WO?Zt`jy@lIq zv?Wf4dP2iK7pFYB;lyj_JHvdu{Ih=qJ<1s_3+T;^Z$>%>s;d7HByS9d{`9b*&dx@H+Ys?KWm@~>ApWfuATdXHY%|dO; z!sLwRIdC&|kR?$9UXL&3BS1My5|Vk`@VT;b1*e;M@2 ziz#j5ltwq{xQ>s(VQequOYUZ$F6=^WP`JBw%Fdm=&P480;m?0Sv&{!`RT6oE^)C}@*Miz;61#^@a zN~grswMcG=!XsDd*P9pr6>{x{zAOrTlb0Hu9h!86vv!!a-L-$lWFB}`g|lZj{E|~6 z__^R#0Yg*YOVR)u+`I7bI$+k|=~IUZR79LjutYE|yF!e?{OBpuC6h|OdX<4;Di zU=8p%Jl-3(*W1^R=8Gfl-b3{B_rl?bI9w{KdHEUmJK{uI!tunc{~xfK;rLpx#RL8X N$fWQ?O$V8|{{@bl8;k$| literal 1668 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztRY^;l*7Qlw8hiKF{I+wl*#@cB8d{m=I8(0VZF|J^-Aw)O{%g})(9-RAt1=QFqp+n zd|~&L#44jR&LSlztUTNGG-uA`>T2mQI6c8kTZP-$`>_4X6|djzs!uOZD@*%TrX75F z0sFj%yKDCUy!X5|K53R3n_Wpb!w#3)ou5wKWLz_`(!Q&{)3dH-;Zt_y`1W}$P4^Q2 zEDvgse$RgU%G;mmVwXHKxqhrPKYRR3X-n+;nve4|>wo-;V4qiY$&7hI3g?n*ZD$_U zu44;#-L8AKYSqGxd&~l7UVLK{#_?v`;W^z(pUQULy%%(8m*9cFb}V-FCHGnSHmOzC z&XRdp>g%qwqG|E;&O9X}@m1TIqnRh2f9A4rVu)4CkzLdh`AC%?^Aa?zNxWBtL*>>2d~bS z?%e9=nYwCU3?xGxy^J4dh9(?H+2HO_=g7!1m0c);*C>=pG39P<{Kw4;w*;=eWiHva zNGqXZ!A(_d`Ts(U(#szHdc4}vD7&m_QR32VUu}!aIUV7nOBv{U(jLq z-Kol^d8xRNQOUl~=k){*tz}#i%zee|)f1*j-4^{_bIUibyb~98NWhI>ebM(_d-~7C f#_kmUsd#|l=Nbd)V69uvLG^;CtDnm{r-UW|++S=n diff --git a/toxygen/smileys/default/D83CDF48.png b/toxygen/smileys/default/D83CDF48.png index dd86e85d2e0005e395f504141831ff4ab6acf5d7..3e50ffc92a80dbcce19a93131fa8383587f06a23 100644 GIT binary patch literal 1831 zcmZ`)doA@MKR%o~HJLs)m%p-Hv9u^yUAeQkC|Fr4lSy-A)3=BFnIq2`GU@HPx!n1A zu1v<6oy}Fqixw7m3-bk&4C#P5g z1GM%wa$8Ht)Bd=p&mu?1K+Lu2>8!!$al_A(d%7a3O8iB&#Lfp{y*<>vzKFiQsGs{{ zA9jRSm-z|ue5!?kjWvOy`b#zCegf{UJpM`IWr8n=qkH3aMfcSYH&_|un1dSCUWY1{ zwpJT1SQk3iZ9Fo%K-Jo>nlVWHsRyrW-&B3rlq@sF#v7!-Pm@~e8W2q=@x9UprlY5C zojta`t*Wv6?1zh(n_x$^?>)}DsqLuS&VdUk9fL(fl~6q$t35W%_*zh^X-Ncsx;H5S z9upfKM!iab(-|=ocw%G>0G!|y`g(|E2$(D`+Z3e*8K<54TSRe_P284a;%!AygR*0yoCL7AgIpS{(i~yP?NP$s7N^#S@5`fNczT!kDSMb);%JadD9cU z(xTgOGb!;l%?eSJuWmF%|FLBlUrgh6IS2F@8_vKG#E1P4W+UUYztmJ0dkMHVo4e%8 z<^1knLf#r$f9PO&M7o4qY#T-_87Xs#Qy+J!@C^e@uY4$97S*0EzI!ZMW<_8=itD`$rGJ@js&LQW^rz?E8g87 zVhKNb*v>+=V!ro)zKxZxejc~5zz?RatHF8?LtC{P4|2W6kgjRd4-*Z}<)NCiZ(#9oaG{rX_8yVDN=(fa_xA;ix%f&S6W-|iSM^{NLy(WN?YO^Gn)f8Ev99{pi8r|Z%*}eUr%WPHt=Z5 zjHA*MyuKA3?N29@=oFXGSPB>b2ZV!zEyCFr=}15#T#g-gIeyF+Fy7t4CpwItK#HXRD01}o6gVc5bcNzgA(0uh0Sfqg0RRS#bGv&c Gg!Lbc^iCQ8 literal 1665 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztg+hpP89F!Ey11{!GR*S@1Oe5oTavOtuWic&TSk=8Lv!@wP;-Q!b?P4 ztBJcb{s?1ummbSPy^@7`B1iJJaCblO6WG1MgKHbdwQEaqkInz{|8x3#{*}322ie=d z-+##W{N8VU>0OmvtLNmlFt>a?yWO8dkWK%7)#W6OHIHg03MlVU^?7XHuK%0Q@OIt7 znC%OS-_O;ZfI}Z{r>Uf$*oS6|F0f=*t&pSxtncXyZ7ws%dre~F%*Y$ssV<(6Z2B z>8xg%zQf;MHy%pqW{SP6u9ccA?jrvY7!Sz-$+0dA_F>(4Iyt$uEUK>f^De4Dq0h)fb;+AF>L zNM>ot+P>Bp#;A*D1a6+6_4~lIM*AJbPfA=S%5hJc5Rhb~((1vLBxTmKiuv@0%{ME* zKDgUH-)POwWovfM+*9fOw|4H`zopr00fM3vH$=8 diff --git a/toxygen/smileys/default/D83CDF49.png b/toxygen/smileys/default/D83CDF49.png index 45f804c9924dda3f537a0129d47b5f9d5bf6728a..1110ede39d3fb4bab9581c4d69b27cadac34b7fb 100644 GIT binary patch literal 1763 zcma)7doOaD4Ah~2@_+)V1_YfFwHb$jBAY7guD|%;vShp z4N<6t+`f1E=dN}CxNF_-T6=%K-@W!{fA@Fq&syI+ zKeDI3-cCIPLG(#p9uzeiejOXM)U&m%L4X>xqjA1C1ZlajagDi79iv!Y6ki09?n4k+ z7J~e#UXm>$NCFx`mLm`ZTZtgX!oPL-A4L#NZ9g9>0jyoBF_@S(_t+MEOdIaeT<2_U zYIF;p(}I6fkrJ&qzFM5lZgO4Zb0;g}BO8xGdgjX#5wpQ%p->cl{m6<;NUw8R2n%{r zcsf*W4*|5M+tQ#KOz?M@7L}Zkv**<1LmuZ${mT!yXYKYWvGOgpcF*2}%i68J*ndpl zT-!Bamq#W_z27~{)HTiI$eHbi`@WX>f10!_czC%FDytzmue?9$%cCBM%Y5QPg`~Xu zFI%AC0>ov2Bp327K~nDXw?n|-L43wr77sFtA+-R6sk0v@fJlRcR4A;3yb6#N0ayAu zEOPAiAe^KFg$0sxZ&_R@zw($$gY-fOiiA+^-RCz!lmOl#K<7aeA96}TRtQCx;j{$& zBf*(e&?Q*yy93TAAUFm@X^@=rh8qW|IS>>7g-ipSKiD4oapv!kmIe-<;2#0;X>j_l zKsp7U!SL--NJ);aBR+fe08&!G76)WHg!AD94T3n}5vcw_HrjXT|qO1>jFK20Oi+tcILfJU^nI?xf5dt3s^N{l-yfGT@%s-4 zlh=NBOAG7gE3=JR7V;9J=JhKt((46lfwxHSp4SmS{x~XN+_$(ZdH>sJB7bkPa{3Hi z`@`t@(k&s4vF+O@wzZFrMLD$h{Pcax1Vd%lC*SXw7c)k0ovB;iKH4(8t2d=Yb>~H@ z!PSG`Xbe{-{v@BIcB?!s1Wv`%9eoTJi%ZO;4l@w(ps$AGD#G}Xhz zjUoP3Lf9*5*OleswSG4<_b=IQcms~$#NLyqJT4ttxl*XAZG z@DJA+=l$d@_91qUyZ41`soJ>p!TQZZC1I#=4K3o4G*vtUk3VS081%(Tj;>3$}^^nUAe?=w9VOV{0TiMoe~ zoxuolzVFa#eTsQawB0OkY|hj!7Ngn=FV6M!X};qPq%6)o0z3{Yl0`+9jrHPRzT1!; z+j`TcN3-aq@o5H@xXaw`)~T}a=#zU&l))ca%B#(eQw=Xn6FXNH2&GMesot=zUukCeX~=UQ{&r)ra=b&r%$0fJ zBPpU2+z=`8nsu<7%>frHr$fqG#f=V+FimR|E#wr#pB6|D2n~9HvV7EoGM^lOI`yQ= zfArbR@Pwt+P*3gS>5<8?ZT1(>+M{gR~t)jiCspiXmEq8O))&Tul+2y>>b63mnoWK3u51DoAk@d&1pEEJ53+h5G z#e`sS6jRK`vP5h(AQ&_TV~cjOwX>(%p|OV@unvb0pwU<~y0+o7(|-~8f~Z(d(*Gt5 yuecYe2|K=K2oP|@iA)h2!9@uo*(eg98O^4!nNdk`18jBYK@bvw?9qge$oMzp(ii3c literal 1669 zcmbVNdr;GM9FM$29JAtvsN0OivAIdxv`uI_s$oKR4e1G4^ zWt~1|j=$ezKL7yywNY3sH}c$%uMhX0+V>pihDod@o;A?PtOI8VKxw9v2vBRqQ;1js zH)p1wBfyf(4U^6p~W#?>98^LDHOe8wrJ^au|_|!@%K-uy zWO*zWIjqI46nqQI+T=o^!{HD(Bm$aA5ke@6x;ez+P|hOMo=LH|GnBFik1$|_-NcYK zmZT}r&4?$_8LWcOdHUiAR+~=ul9;j&2Z{@s(23iGkU%7~THWUwvbM9a#J_I5(%Np! zv=PEs!cJ!}CN3Yz!6RTUcCQz58*<*r=QAXi6g&;1O&L~#Vzrop&wUY?NwZu7p^!!_ z(Lk_9tbrg%7Acm}rhq02^V&!Uvz*(9x(sbHL1?Vj_OWQ594OANt@WVKgGSd$G40n27 zoE9S(av5QcWN0fm6kj>{3Vmf51R-b`ioh~NBZeTQI!vR15os8zQlT zoHIgqar~!PMz**LbWdNFKDT(;JOssc48ygCUsRO|06f0aVoIa4bMvNo(S`-QHP2i2 zyIkYCJ5?Qk&!2fl{TJS45ucts4UB<1e0|yv76#o|-xIU=2VVZziPYjQk2pcp8zH>$ zIYKZBn5U{D!Oa&RT4uN+mJB3R>iyR#(~Hlc_WNJme=6D3lwVnpzsOM5+UlD$uwnEm z-rr5r8_r#vEJ%3rcxhYcm(RLyr5?XM68G;!vpimu*0H|y)qcYBWVQtrE08JKf>YNbAR)&Re- zY7MKUs-%IBbp@`@Mh~Y_w%1VU8TgrT+%F3Qvl9#JoA`N(ubUI*vL#o_&JTF!AFC?T z1^}f~x|*VXFV6Oww98l+(0L&3@cIQbttqI^C=c)&NNyk9OoDwIfRCFx`yC$55DNQQ zP|N9TD!hF~#UAJ2{0MC8qWCiJT6X%%DK=Trj|a19&&)dSlla@av_3~Y^!=?tA2Bh5 z{;;M~;WzmG1p5-+9%K|H#`oARqt_n>!k$V0EvmR)6wjXi6_+iE^|zjPTe zKEVkmKR?p{M^MJW?IDT5@Zr9Q#3|dCzsu`+t0ia5rLC?95tvt-xi;-g^{I!C^ICc@ zM<@3bOzSyPKlA3kJ6GFlF6q>PbH9n+k~Oi}P+WfEs-%EEm_doU9;o8RQ99wt>>_`dO@Y9|6W7_xf h>t-LNisr2H69G;930;Ef18=&23|dVLR-;PH`3GjAdVT-^ diff --git a/toxygen/smileys/default/D83CDF4A.png b/toxygen/smileys/default/D83CDF4A.png index 7b3689adea75855607d8b962cf7ea00fcbfb48b2..974715316c22fac991c74757b6e570a68cc2e6fc 100644 GIT binary patch literal 1936 zcmZ`)doE=GAzlBC}x49O!(R0_A` z*+aV0m2kVciTd&DdOv1{nemwU-fye5TKE3(+v{`o-e;e^_u1>5eb%{f%)&v`yiH96gFj;1o3jT-Vt4B`>6{vCI+%)jg3d|z~h-Sxg5kx;DGaaG55)mC8 zmx;V5!Q3u9r^9o-#hzV@Udb1Q4~JE#z-w^jzsP{zS3{#Ef>)XRr_q~T0lY3>ZqEtc zlR$Qr&T=-Gh?C}&qoL;%=(l`mAX6}q!S9XYxBKsOp5Q+A;C6X!+_7A*H(kjFD>R|a zN&|k2H`E>sJtPQPJa$@~x7v^KdI=kMtytHzm+}y^DS(*=wyV+n2CU$&1JqztX_uDX^9cc*XL9ngfDbT|vG6c9SWu)qb=4_(2Q{0h}=hSFx4@^+&U=cXa6)^H_XXgUdep#WAM*tjglq04bfRajRQ zmvg~X3|L47%b8#$2Q1}+c`BHQ0R!Fuf}jiK_nyKR=|I7IeeGqK{GYPeGn|z!kE`dr z?Az}y&oCFeeu)Z`n3)oF+S#;0%A{zk2;N&eX4?A01%_#Je|e<5|ftHmJV9TJ_I*?dYWGV1y4-TNJxy2j5$TppimP? z8mVy!09alsIgWc&iTUBy#(kyRR6#AyeyYM`Z}f&)j=#sMnrNp}pr8 zkrs=cC--~Jy8K>>ir?4$yjrIi9G5lZ==ih*g|8L`8(6WQcH3P~ML)c$sC*mQKKA)& z>JNrB;dh%dBri46RDP7{k8ahdl-rm=*YpQc)$OYy-Y{7-W>5-9Rg}N&9jS$VjyN)y z$we5{_#H%U2bEXQHKT35*viS9BQAK}=e*cUrc3;xbW^a_0oqf_#SL8_-4}1@YbX`c z=wT2O(*2i1gN~r7$!_Ao1gMP}5vE}|S7@q?XFib1E+E;l6P3oFeJ?VSl+!PPe06KU955FNQ(&J&biwXYWTj%%f4v6@TXGCZD+yg3ZurHYVyqn( zTku+rA&^mdd1W$hI@-L5wAQ!UIy&zb{kYym7kBUEl(f!cVbzq0#ZtVf=J0(Ng!mqL z@;jWgO?T+^tEa?t3qZA;+OOUgZ3k#hF#Mkq@&}_T3f^m>!+Y(-jq3Jh#rOW!pnq(Y@{4Jq`3s88#Y zui-x3jdODFLyd)p0wEU+Fi~MJ#>cHJJJk1dJf@~@G8ibv=Jm?ppvjMiZF;)X&x$Zq zko=usVMK0@T1CIgN#nqY57yYO&NFG-TL(N&5&y$5r4IhyICg88VMYF9$tp=3 zr8PSg@*rVkTHC!09nzBcE>>4`-i-YI-lqq|nOj4FkA9lybygQ7f!1eC#NvR~D6yl8?lca+UTunV040J*Az{N4Nw5IM7-M5J z#sY0*>Su((nwVkD%=9oAEC$m@eY5Ld0desWF_F~&AJD3m+yV!veBHq}K9Z6`Oe6t^ hi1;v)1}=^mMZ%Ma5!BPKNbola0M6OVsrg7~&fj${UXTC) literal 1690 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 VP@{rdXvEbBU~X(}RC>sR{Rd8?iwFP! literal 1644 zcmbVMdr;GM9IvQo6jLmb4w2~=sCK2jQ~&^7^Up&;N|$4wc>+uP$ngq zC(96JI5eb%np47%IVJjBV@a`5VFHt914(X_Be0Py0k~~eyAyRQ!69ChoBOsQ5EwFH zip4Z&Rma! zgtAE|y_7L>`7lTO!CdU#FXS`iyg@S=ic1P%#c1PF8);{?m=ff^2u+j;#bE`EizPTL z!^JoZ!-=V4Nn)Zzq?Tc7nOKVZIo^du6dDOGMx==dqJ}vZRjLHRV@>}jW)SBL z5jBq&P^ZOIx$avVvUSCiR3WdA6`8#~ zGro(duU+Kgmmku^2r4RTyfL$1ua2BF>Bhb0U1`l@+>d7lVruM!I|~ctgY(8J;vRyZ2Q8-Y;JUow~7L{FbAhAOAeMVQ}Gt?dvUL zA_X6H#Xe}&^rJ*Ae@KzNT=&(~_J#y_{^^O#SUy;R(5R0*L`t&i`Bu2x)b(mh={CAGAn%sXAz z(%vHMA0dcZYsKrI^y`r77wfH&SaD~q`#G5ZKXqx@=iaO|%J#jq;Bw9of zm?_$NcWQ-2*S?1LLGFC{W&Yt_) zA-K}~G++H2(iSjZ(wZ`GAfI3R#25&rceED#@v!T6@IUA5;z<;xpaiq|hWZ)sj!nPZ)2D@#v5lGi=??DB(^VR6CL(>l_DvW_6D zu`^&Da>fyJ`{LD&-8B=ROrM`ecvgP6>-eG0i_nNb-u1s99!%@S1-}2k7T043)di*h E0N6ESWB>pF diff --git a/toxygen/smileys/default/D83CDF4C.png b/toxygen/smileys/default/D83CDF4C.png index 700ff44f08ea670c25f0270a3cdfe03a817dd718..8843766054458d437f24844a7675ecc5f165fe2f 100644 GIT binary patch delta 1687 zcmZ{idpOi-8^>=W#}{c==&&^uiZE$L3@fZ-#TGMCX3ONXlf=ucW7~*oElWAHiAlYY zLYqTWYpp}kj#EyvLyi-JvBnrKiBR*`^S4-_w{@~_xHZ;`?~Juc_!2v)NgHL z$OFJW)6L$fYzlGkZ~&;h`5zfo0nM=iZeAV$@rD42*8o;fE0GWI4G!QBe*j`GfKCLn z_M|O9oE)wc(%esE=0PWe>?%Z1i#X#EkYQ#VXu?=cxaQ8FBY%xB3aqov$PWIh`arhgm@N5r|y+)(krx%Ihp zM4XQZQf9l{SNKWD!kyKL|4z1;NJqnvxeR2HiOgqq-1v zA+xODvC1R;h3e}A$B;2tYloFqK>7_=ImC$?m~Nm9ml3$-JC=J~5w0=9ffW|4w!wNY zEVk{MZagJu{hI%p!syKhVT0uw#|0TNK-eJb09iXM{RUIjz^!(e?+Wk9gYinNu*pi& z{<);pVzzEa%MZ|!0qA2()G@K>6}iy8JiS5bku)@3tl51*+PP#m(kNFR^hM}a+VX+&_s#u?%}L7rKF*1pjO4>Dgs~_@g!fm7c~YO5g8s#yBLI}$3+BTW5Xf< ztkQ3j{j!E}%Cc!K>m)VhqKQ}+&bdq z(|Kln`8s~b1;M}HPOBtG&Aw>$ADxX>o3zAg95Oy@-76oyVh+7q^Kz%CM0G%O|-}M zhE8|M)6!_w^!?YjoQ-PPSEbLKSr$!okDGoTjWzELsI#Tk{iXpoDLbX#{}B6yuf6he zh&dN?^txk(w1eb&so%i}G9bKD>!d%)Nq<z z#yf_8Ecid%W5w^lO=m4ss9a5@9T7)v9cV}%q|T@=(Zree z$%PlbZNTFq`f++^lV6-PoGWb=tZ5zy9W5q@YR5Nq=r5B4?v?4PA6EH{d{^X3t9F~Kd%F#1> zpVjLvZRW{W?7TXI+>+Zac(yO&ZA-}(S@kb!#F}eKh6Hc-Nb*A)P`H!hH!`d3MR63C zr)|oi8W&g7P?TsliG7_`VHL2X7TYBsBG;Xak0&!+MPFNO-mWFE zX__yvw$f)j7r55<#HhrK4;9{7ylnYNtq>nl^Q%dH^w<@Js*@>ZSKJ7t*&jsf=Knjw zJC}NoeMBz>{i5&Oog7HIeF6S~=Yy&Cicc>IFA7+gox2LoGFcePs9pUq3uAkwU|p`x z4RiUbLzA8iqb%3dT_IwKl=Y}vPCiNW4J6TzKYiuIEYfnaKVxX8XMAwKwE@{j;{dk& zmUebQ=H1)(vdfgUPqek0?>k^@w%^p`l#iUs6CaWfxu4DX{V?9Lq(oi;pO^24_Zu3t zA^uXEP+k3vE>VEb8RsAM*7*b9!iC z@V$){(UigyjH`l}K;SL1W^baq8PJ`5>48*w5HTP!2od7k(9-gk>; zq)!Mxgw0h# zubndLW`G)+AwdaWlw=aZFesJsVX;Iig(rfD5Jm(-?v^IOVpJkU5gFKbL7X+loP+AI zv_4zhN(ot6)`1EHE|-h%67gxqB7hYNML+{Vk~lutc|9? zfFhAi=d((P^YnEHc85;)MwoK;C5lU#z)d&=FkdLJ+XHd+qMfXs{LhWIqMe2U2Px2# zPCB14ap#c}A0%_Rd$*wgkn;xpl(BJ#LgZnzDc?>~tQJ#3+!wytW=3&X0po}Whb1_I z!!RsQLqu}9NT`-zY6&97gBow+!eX&J4O59_7_NXh6*(?K(qIuTR>*}iwL%!=YAGj6 zP$n{H*T&iHAYYC%&^id?X=PkP?ISI$7k6nGwpIF29D=-v>3_Q zJ}1p-3~dK{^NZTvVqb({3IxMtc)$Y$hE<40s1PGEE`Fg(CQ(3rT=Vn(Z zMpfpai*sj(9&FjC$Kus1r;V+szf@fIpw5gbSxl4hONrVy}e@SxAchRh3aD= zx7OZ5uiov#iEf)=_`NwG+fVyz+y83gmtKGFLS!-z--=IoI<&fJBAXDNz#DvMxNmV-rL_OEFzDrn<9B=g7dAAo z+PgMiO9>eDa&2=%mGRk==s&B{>!+=m7w;VEeVEX*JB63$Ei3T34s6?Wq;MA$pWHR@ z`Gf3}m8qkfZ=PMg<^ql-j4Mxwnt3q;?JjID&*&@_`z^<2>8i%G zRcIVO$@w^1O-9BwuMx!T{l;>&;rxKQxudL$?#WvA#_k$-{HIECemP?7-%tZ=|Dnvj zZr%J-d1G}~N-o@8c{J+8oTFh`x}|qUhP2hMjvmwMH9S;wj~_O#xOIuQq$#3k`hh~h zPSw#yPiaeblwnijox~~6Jp)rNU+jwMIA7mW@^nXNUE4?MB@{dP`_X&P8J_G!(7b5l z{&7I_OrGjaQAO(Y;%t(Ln%%FXt4??LTI|9j*Av+}Z85uxAD5o}`E**#@Ix}l(F0MX uzDPtYXJQvl+H`DodDD8gx1I0t4iEy5;v)Q=Vg9cJzkMy9j_pw|So$xligD%u diff --git a/toxygen/smileys/default/D83CDF4D.png b/toxygen/smileys/default/D83CDF4D.png index 9f1070eec79219d249c2ac62427da20a496f3e08..7e96d5fd7abfa13843d2e26c73975ba06951fb0a 100644 GIT binary patch literal 1699 zcmZ{ldpOit7{}kaF=uI{HhS7fL zO2p5D-v@1RdI08VJ(;xOpT27=&~>d;?k1k0Ed~By0zfConhu zSp7ZkvK$rU1d*-~O#{vr*kq1E+7J~GKPLgQ0FKS601-e101qG!KmvetIE-u#0Me8? zrwCC2Q2=6qXkbLcjRu&!1-(*rL=jg2_s&9pB}|XNEjj$r3tbYmx+qW_hrUASuYg-J z=*U%RPK1F{=*)r1X1G}nGlTH_cj!HZJIw=mB3#RX-U7H;1~+AJQx4awq4gNFq{5wQ z=n%sbIrNso=Uu zImfqqd9{dan$X)5xf%oNPIaZCpb>2OSXzg5!X2lMj}Ma;i(YV0q8e3^lJfo{W-KJ< zuGN>fB?`T%5B4ZD7N_R*>5EVUqoS_8*n&Is@yi4cKhfo*Kn6wa-{wkzbeyppQcw}n zJg6$qRJ7GCu~(e9wgBk(!*qY4H!Ipy5FW+}4rQAPV+Cx}7(TN34%r31cfcR zCgcU!p+l$o?!ReYbL8_;LEcM;vM7AJKhbncT&{bE=mW{eDevdk$ zOiCx+v^T7Oa5->Gs0+7ecIlNpmTzaOqWs&s;@je0TnM2z%%m;Qp2n}~<~8$A&=et2 zZ?CGA*=--Lx6^26awKtQ<+pvaj^$&sZ8dGrHPKxnSV{h!j~(+go?0_o*I-5J!ky0B z{1Qh^Y7ITM7m4k%gtX6Mr#}ZR2$tctRg`VJb^OM1KdHPz`HQd3iuxdXx^ad0dS{*F zbF)H|#8AVX9C5c$`euxtR-aDjYM74Hu+cVqcbAM-HBN7c&C`sD=_KuLG9tycbnI|0 zcM#~9Y@#TQsn58W0(X(JuEtvCzx zb}m>puDm|olel{P`ICMn?up>_`wt&fu&-WqV&CJiUy_ryy5PpAQd53&>9R1VQhK^O z<1%V%x#dOoTHmKoXe)!$@zNJnPfv_A&t|#ra@5(km+ZCDdgF5YwVOBZ+l8zArKRG0 zZEKZnyKIk^dBt8YDZ`iH6X16ar8_th!VL{M7|iG09R*xk1}c#%KlDC z%E(`}e2D?kz|gRysJK`f;4UTc+o}ufk|o(04W;eLXYwy(o-I+N*+ADK;vuQ*4)TbB zrI6|`3}gw}qyyn>!~l^%B-#+{ZPwY+*AYnT?MQY6YXX5pAV|oM>;L1x4+{+D#Qwj- zxX~^>#KCZG2A?pFFq#$426AB7ezqx%&kACDvsr<$hm>sO=Rn>VswbtNY1k z#c|xGo8lZhbb9WAc)Ja^n+%UTPT+=mddhIVICi%=AE->-&80}){_y-`xg@_wKA+F` z@%w&LpP#!lHfDAV006NTGj0>dmqJf;l=w~_eN2kOEP-$c1*}`}lN<$PyV!CHw0Ox1 z%0`i{nsq&t4gezEwA~>%tjjSc>y?usj6C4=iEIGSWd(esvx*YHa;k!6^w7=YKR_Vu z(nCcWtJ3N-QkAs1mZJ)4bM4OBDktiKvKE870454}DS-q7UJt`#0X;OXi;45lwgLjj zAwrcNdg7GBnhzRTjsi7umCUJxVNk1;!)lFI3#WsK5=InC@z%;0DgxFJ z2m!-zrU_AHW~!714Q|jNY9g%hEH126;wWNNqecS?iz+B?Knb-mJ5z{9^Po>}RN9rHaVNG{C5lKwuaZea6Mum4wQO8;NXag6sM}Ia7$vC_=^Y zpJEAbi4_={J}rH5@w9m;M(h|)Y>nd8rg8w7WV7Je_Q1sr1*PVu6*GhVV-L{#!{ykr zzJZmqClA?^X5fybAxZTaDq;SBb=r0)y>;qPZG4$P&M1}CXRKI(#}WR-C>wI>-tgDm z!8nc|90{&(;E}!?C)&sK?~LizUmDRT=d{Kh9@rSUCVHdhW=gmAoZZ;DwNQE$fR8@v zxYfVv_Pz&;&aR4m+?){6RbS@XTncZv*3;D?VRi;i2KM?+y;(D*uDCw$dR(CMciFm@ z#Lc$!wYvk$dykZS;Fy;_Z}8pVzPis2E-|N;6yeiHKiSlr6VoeAe&s~}niIPRKfES4 z>rzY&Kfl<0en#f{EAQ_bPMg=t?&{bk_3i8AcjKP^KaZp)wJq~z)DJlQ!CmJP>?=oz zE3t#hW2sa3(oqi^CiP7Fsd}3vxWD=O-rC;pGb-0fh&prpn>0oSBK~Z|^Bn>A%m-rgp4ppHn;9a{NrsRmoTW>4VOZ(kYui z9-VuB@#RNLV>`cmSV*ZVtg6SJlqiYeZd2mJAn>hvI4@%UAF;`wYF_t*g7(TRES1l%!Njvs}LxMEq3h`Rkc%sBJ=Y47d~K!O1Pf;0fOkdR;jfLK!i z{-OauxB-A#WMQY|$A8(QjPOWH31M*eE`<9U3JAUG@*Qe&2rbH3ga#!HLbc*f z1f=eEX{7o#$e9tLN=xQpr4hE>&w9@ei6oTNHCgFgS!o_h3IUlUqNKETNPJ%7)BEL_ zCjnl)=1Uwptge7v9k8bIL*~WhuwYnI1-rXo?d_#N#5@K6{N31ed* zHyQ>9LN)`k7?4R{wfqtiZJ_gM=tY46RLEw+AU1Syf)2!0qV+R#*^J;l2(P`M>vQ{-cM7CPvVL0Ie*brNtY(R)?D6fFgELS#fQ*`ljw) zvA*v6{d(g4dYii1uT^#pDB%Wh=y^HJUy8UzW%)%Vg?RBUMzMAWUt$aULs-ASL?5P+9!BTPySFMeDV75`-O{ReT^(9*_^yB?I9>gQ9d zAH{PsMgKJ?)YqxZOe;yCPR)xjlIUGJwynzxJ>9Qb>q2w_7vIhmR zGk!g@$s^`<>k#b^(Gss@f zUl+907&O0gUasYT?^!pwAbm7*);Bx0o^h;jUr-rc(EXG9&e{7?O(o~&?M-qU1T*zY zr%K*Mil*Z-nSzZwcBSf;@~p?MO)f8&1*MN41=3E>;>)D;jWJ$-=893P65csKtfxE# zIG)!ryO+~r_Q}6dWp37|r8 z2U{zO`Y4lz2%9-io1JB_1QOwK@-BP)EvkT0pP(qW@nY4D_O!Ah@RgGAT1$T0rG{I5 zeF2s<*TxoQp_6@(cf2i&MWJwQok9x_3jGo-tr_mt(b1SmNKP9y0c=AXjUU)c< z#Pd%+t1EP;yG6M<1pDxV9o&fQ>$`-}vCd)CBpW8#CMm}8+X9R*kIZ{1`RkrwrOt|2y2tRTU$z zI`5v)k#Dp)GOE;`*w-gR{W`g$=SO_kao+0K>@4Tj@Tp$r;Lp@9=j@STje`g4YQsX8 z7PYm)ZWdDgeg|VAKDR}ia|+1&)#nnuxhy)DO<+c|5d)5x9ywxcdeZptG0I_6!qMY| z<4285O$nx^MYqK#|0f|Vf)&i+|Nn$v*KIP91dUG>$PpZF96g#1tXL5YHr^qO9>{iQ W(^>qeAvW^U0pLI++16XrQvU@zZZ;GE literal 1666 zcmbVNX;2eq7>-y4(~9vJR6tl4?TBNJO~@u$jhJK?659Zigo0Kb!)AdMl8u`U3F=tE z13c?+R1~Y&PCHdW5%H){#EVv?99pHts4zvesMt{pg{j&N0__jSAMWh#ckKH-&-=Z{ z?B=+b=&{^DE{DSztJ9(ecI0^XtjFJHDK*v48(tKywTcW z%Cr+g1L2^YG|uKDIcx~b#_sJxUPIO!B$g)Gq?prD3U^uw8>2&&Ap1pNAuR|7DIiQN z!5}Fn#vlliM~NkJxkRLvqH3u)0vqCZ6IQN}$TVtMgdB#|5X+)g%QbR^A}UHFl87PA zFji-CFlHN04C#`r?jTn3RxF~S2{S{{CW=ZQs(`puilH2-lpW9*VPIAQX|qr+N4PgV zuTP5-G?_tIqG-wr48#{9-=MFOK@hBvDPXA_#>5b$(#SAO3P;EkYPCX+O29#^rtgYf?>l(fRU` z^Hpi3Q;)Z-)oud~Yi4zo0Y0JG5q{_QaS!D@4S#U3#V>mUPwbZ+!sT+e&QyK8=PhiH5Ll)%l^-F;KjFM2XcMihlefcRxU3e%g>;18ZiPSDT?HAcJ!Jm7*nriv_N&Lcb zXWD!IC~hG$>FT@X-vsA6AJ3TGu{pi?r}1~xnxn@zg81I{yBpS#hSIdj{FpVtqSL(9 znggQdr`+b&J<%Diz#AgwM6B?|DkjO*>Y!|7sfTpZE*vo(Y^z$Bd271;xE-hq2{fmyNju#%ERF#xqOMT-rqZnXxe;H1@ZK zW7E!z`HMTMeCwvl-E~W9kf?UM{Ju#Q9)6Cz)c(2XSwnez1)5^4yt#h$&R?_ppXd3H zD0^w*71fS%@H*%8Pa5OI8h`2Dt!^tg*xP-)GUv;@#rghWL7u&hR9#5dVj@CkM7KUU zoYWTLs=IxvEs}@EKAaGE3r_9QtX%&%`ch#j|Lk`!Zhdevh)n$G+^WMz6^WA&OoO92eJLk^*#mUiHSV&3;0EBIA zEL>1|oHGS@&{bLWOIK7J3B)>J0cx%vJ@Dg2ZJ7WY7YBeCMSz49fPK`Jum})=0azvg z7~}wmQwy4%jR3ffozCGbNsZu%1yViOT?Ol3I68_+ll*`9?9nedrdIV88umXB6XA3& zh?am#DL7O?Kn(}NBM!b*9DFJ`c$Nabh=Xe$2d8Wf=WcR9>wYecR{sooW^^c=2h&6_ zOZ;s8z4A$db_kq}2a_wHLE?ZaTEW04{&$RfyJ7T*ug6p4{9%v5wbD2HjgJ{gC8K^* z`%Ob@Wd*~Ysn7M^uxclf+6kn35~-O+>ZcCtr;)}9Rzqr+Ud4DI{h;*qVetr3Ku3z` z2&xp09^U@@#Ui0we`fWS`pXBYUlgL4u1G=O(KxR!%c zF(3#M!u`}8y>)AvizBBQ0V;9+{TmE1dUOQ;<&mxTy)7*E!5()59#?sPa$^0EHTSNh z$k5>71JQSHMY#Bl7UqR`1iqHFX#BL;*1A-0ttcuu@O-%p-H6NI-rAzQY%;kEHTY?^ z4wn4${Kte8guK%%HPPNph{1W#T>PSBs9_Xx&;^nVEt*P_xqOidpm+6Gylw3yMqnyD z{TnPf>pCOUy9GzeWh^AdQYRhP>Gy+$WlkBH{$4y2Kh$&(@&0s_DReOO zlnF~wWkV;lX@c-_Z#7Rz%)i-=uAL`DYx$$hIQyAprUy+kq93E2WZ$nrWGL<|)>=gQ zY-)F0naz%U$`ZS!VRz(msmaKfo{=5)ZAQ`g9pkE$#T7o?m1Zw_#|5j`Hzg=?t)0b+ znSek3{h*?AjSpk>nyZ@r#v{=By#BnX|n%v=rWyEaU^PkLmpz0^3h=@;Z)d6@?WG zq+ErXrBTOKb#dG3hc;VjfABtTqg(u$aN)ak3VW_tJu`rLFxmPe~{;>I@v zFKfQ|Y4-;=17ktq>#vm+?%`jJbsV9XGD2ggUD6eqd!sgRo2SODkol~OZ{@q*$O`5Q z#EeT?-<06IH1JF=Ia%}V?LHY1Orq?cFHHU+?I7*oPhQGe73H^Pe*Zh$gR(iAWH>s( zP+eHNVzanku3!27(d^15!&T;F`QmIcF}F}CZHFSr=H7BouhEs2I*+!hn5_jCCqWM& z0JoKun~qiFF-J8%Wde~o|1M(Z6O-ps&Mxnf)D%mL7ta+=^YZTIN|cqgwZ^r^u@A1a zv3a=5%O!|ON#=4NuGcSw>g#)Dr*fw9{;v{QA;BxlC#CZ9yrsM*-WcK}aE2|-u`#TDmeS_- zxdJWEtJt`{*Fp5a{sQgT`GTeJg@G}`kWV#rcvivwH`O1TiEDR6RT?C68r&cF){K5jX$AqJ77|9`^prg;XMAjzrVN+Hvt{K80pB~tuJGPW1}0!c0; WKVtNyUK0A@0oYnPT0Ar(B>e?|S4I>7 literal 1690 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 diff --git a/toxygen/smileys/default/D83CDF51.png b/toxygen/smileys/default/D83CDF51.png index 677749f19373c00008fb7f286e19d52e4fc3b764..df81f7215e2f7ff5d290454f4a95d5f8bb829940 100644 GIT binary patch literal 1999 zcmaKt2~d+s7JwTFml%XKBln?*pg;)W3K5ZnL=sViaI0KGf&!5Q0)j+9K@h^}fhVXO zVvu7%c?skQc~-B(}tt3F=$d)=?AbG@r!g9 zA%yU37K4Lnjju&T0jtfemiS-_7e(7lL#U}pZ9ZHPYa2(fIGYjLy9%LCj<8xN4@^Xo9abR;@8qw&bh+H)fG) z|G{>DtxC?Sn_k4{TkP(7k)#|mVi5kdhaJY`Kk8TRHW<$tRv1rRS7QvXGsU<|S&nhT z&H$sAr1K5Tjc;K7d;@drU%`UEKdo_7Z9q!C9>9n5=_EhXwN1)8QG1{Ij?HognL@;b!wM;Nc)V=!c;n;C>&R&V|OaaH|UjWzf_7DIpk+q`}R0 z80>_bZP4Ee{VmYf0ykPgS`0^1pt~O0s-eCV4#q<99%wiPS1O?6B6Lcjvl=?9Kvn_u z`5!aFprssY@}M*s#9@#Y3PmC)&jM*a)E7Xb1R4)PeKyqWn-zLPEjCREnPI^9p5wA0 zWh-PyLNOms2%$^_XA|MP2+D+0(JVNg2$`WE^!_c#b(~3D;Icu;fz&|A3;{_b6!D-q zYI-LV(nEpo^*NX^vEJf^8*y$ceIbGcv0EU)2a^3EIRN6lW+U9ba5v8R(g*feEGefhTq1(P;kf)qUk4Yv& z=K3!zS56XFjS^SfFw+76dpUz0*ky7=9M7*Q|5bDf$L5|KHyoa>{A-zs{n#`pKV``g0&_{6?q^`GDCInK=cS-t!mvZQQbj3TeB>CrYd_pH!VT}e)HvG!^w zSBjaAX-`BgwxZk)4|m2-=1Ty3DM(qw-po{3a8vlFG%s@F3W^3Ql> zSLWV63G5FTuhr#I2)(O#C!2*OGRL4U9fwgB(zdof**HHz)j(0c!uzylPMz!}x4cB$ zw*Qx9Yd>7g^x#vYmlytYp@y|4ZiTbPmJ(*hngOk5rf+oJa+%vLX;k@%Pc@oFcH2e- z2Xhmg@M%>y56L?m^LykewW%f#$24d&{i|%(rFS~jCEq?|Fd+G$-N@1&YEm#>q2<2s zfMy&^P31i3E7D!1UMel+n-WPlNzKGE`S1~1EaU3Z)HJGMJ?U63A^qu`5j5@cQew?mK3hcPP zuR}a@Ioh{=I!8%;N|)FdbA^ADEsIY2culq3e#=O$y;@fouS9&Q>7HMH_7Plx-D11O zW?ylB5XlPS{*wt}>RZ9K9jKB}bjFpseU^dEYvffF?~yrtx-> zPpp^qJ#u{YWxp7envfQZ$gY(#i@ZvqAbREccf?H30D7;f{Rk`G?`6`(18dI-E~Sps zC2j)?iN%Pnyd$Q3rGjC|_l2dFIi4Z!tO@tc)CCXEzn#q=n@xY;6NJWSx^I#QN+Rdc zbFNcaZK?4B#uwhxqJ<65`E5Sk>!#w%ha_1Iai=anO|!UG6?aztskyI~QI+naK~XZ4 z-ZyHI<>jhcgjHdgs>f!~e6>g9G~}kgV7aQIx=3v?{_F4Xr>Oh*`uPVe3DnRG()`O( zY(C+;Wm~stY3pp?790|)D~I&-!@_q&L~?miJ9lxT4GfV{Oe}wQoIn^GpCC$1+GC8C zC#UR974J(+&)9z;Gwa|AWRjh8C^t`%Ur<<7TyprxN@RNUSn2T-Cr_O|Q&xWV-1$|= ztfI22`a;b`Y3-%D`i4exwEFv|=F3;EwzOVtYwwVCu0a--UDvvMdawV`*MH;YfE5wR zTayNF-M(}8-q8IA!!~4jq@ZAHH}de&kB^@`9kq8*P(U~w&XMBuY;1huIdv`8K#Gda z>s;2m(&%ms<_33X7E)4Tu{U~b+U&W-%X=f6#d^@r^`n31bOt*LQxP*z6d5k!QX}HI zm_lR{nQTRJw6d}DwINY$?Wy);3lfP+A}LMGWd550KQ3|?FXew5?8#)U#|(77?%)&0 o6YU9)=OS8U+zzfWn;#y<<#59zQ-p(D?3YD|&Gck6(8CV=0SE6*FaQ7m literal 1731 zcmbVNX;2eq7!DL6B8ru0324{`%bjC47l{T)vOz-(hD(S6B*y{~l8wniB8V;6b`V=d z9jyl_atKum3KRs?O0C6=(xGbGDWZZE??D|}XKblL`0)s zzs`XyB@o`pHYBHEX^IsB9crX&oEW;@XvWzD!qRBFS)d+~%r{vDb|H0ASAfr*+YBl& z3BmG&)K^ZWDU^T&wIBe8&Z6maD-^GVP1dPI;VEO-HD(4#XEKaNXIztLE0&1->&6?= z*5vhOgpr6?(E^JOKacFdX)>O>w;OT-ac=}GEe8BhH2E;9D=;D^Oa=?7_!qj~pchC% zJ}8A)QjjBsq#y`J#zL&fNETDffyEq%EuGeQ6BiOoI1)aW&4Z;p5LXfNB)nLd84JQ9 zT!Af~=E_V~Ok>g^({>HG-6<~Xtz3b`f@m<*l8mDH(-ol1MKRQxi<$vR5*G+bHJJ3M z%^K=Fo>$Srh{aHd=wmIY5tz)c!0-n9EC}X9xZhHz2M`E~APJMthIn}VOc9U6r%rM8 z|0ib*+!=j!qj0JBE(@<$Xf#5nWgGI^qK`cq0=ab+o zw6b=t*wG=o(%H@-9g14LK`9R>!EPcE43j)7y2FUwPrOeKRI7VaeyScL_1+T0_46Ny zE6Q31f95I%BOPp6PLMU{mZMJ2;5+&++0GQ7881n7RQsQUZ{DR4Y~^imz|;M9`B3og zn=koI!_o4uHg@gabN}8}?_!OA1=VGZKdI1cW*IXpIygOYi=J=f9tLRV|R`ncQpNB~oh1{(m=ML5?C#8H@vUa9ge0IE10Jb+`RJs zyj6vZG#A}2MO3l~M~N1Xl)jzod=2hJ1&)NEguOA9#Hcz)h}x|18PV=KHD{t9+&;gPyE96 z_#z(aY?prOcZm_6;FG}4gyD-NM}F(hkECyqH)dDu@Oda(T=T$>6g1Z*!6&P2Nk#Yx zqv}H2vz9Ke(P6=f!`-djeap0Esx|YsTr26>zxw?$mk=B|JmAs)YLEJlzuHez16;u zO|>+rgj~{m-jYpPc71tM=;>XYz7P4;ip_FCw#I9NXKgZliM|Woxc^CG#+jiXO;Z}Q-XYm diff --git a/toxygen/smileys/default/D83CDF52.png b/toxygen/smileys/default/D83CDF52.png index 3069b8314e6347053f78968cae382213b39138c9..262cd7a7aa97e352f4e2fe233d7a1e75d6dc5632 100644 GIT binary patch literal 1725 zcmai#c~DbV6o)V2kwDlI77;`UA;>21SjrM{6bw5=*+B+O0*??9Bw`?e!5UF0C@2vS zTv){gNiFUXsZhZd6srho>INdBB8yT%1vGt(|JY7Do%!xPzjNlEId?fTw`c?1PZMv1 z2LNaW_|w8rOIRGL%IIumryY(Oi0u{P1;Cko>I+O2)F&nShlK!;vjPC|E&%4xkoXk< znJ@rj3;?KA02uI(w1#>Dpwzb^IKoGvP*BCH9(w`2``_^p=~)&46fZ99%r!RM3M@r{ zv8!s<_k;Fl+{@#QPrBLK=BkVNs|$2dQlYlwTlQjg{uj#kVC{ZvOoJo) z^t$XTp_P4`4&6@VNxjkActnd_$lTd)0?Sv2%nS2x8`}@Zu@z;t3dR-%Eq2&&<&?Ns zQPH5FvlOu3puW`|a|cB*bHU1M-qhm>f$}~wS>YWsXXP^aO*Hv_`LR$5ZRx_j}!>CCy*lI`}eNDNu)ndmWf-rhG zIMaSvJ~B1`AQ#^xC*z+Fe;j;2H*s&D(x%~n7xj4Pj}*Ntcgzw8l#AZ1RiAce{26el zDstM*eM%+uMbS*J!(i#hzRaheI|F-X=RUms^6^#w@mCAkRx??&u94*we7E@a1Uk zo=s&@qG!);HHZ8<3uo4+CZ%qE;+jx&@tw#MOB{JZp^Qn-W>nm3hIxl)$hX+Pc_Fn( zxTBQcUA&IwDLQ&%k6X>phFTAe7_Olu!Z_Ymt#~zvX6{PXsu5mi)=1i(teq{?%{Wqb zDkSR1e6sABHS1W(!KkkAY*wAqbUu&8ELokT-dLW~up?7($(wuV7RSA_t0}=G=eS=X z#x;=4svJ1k=yaEBFS5Dq&lY`2*&5S#fVisEa`K(Agz)9TazON0HKtoETDNhn>gTCY z5>$@QC%x&3z^!W$y-u?s@!ShbPG+%gUr^0Me45s<=p7UB$MXzI(`@f@3Y&Z}bq2P^ zr++$CedOF8-`aRpYvEJHxF@wwV$AcEZT&sBTUMvvzx{1SrM$iDA@k<)$(t|AYGWHx zx3#Zk{O#X7}?(uVK!J zJGm6M?n@o~sG;@GqWf5Rc^%6wH~aS2>kn}gB-X8_^CmiGf)8pkN`?!?FK8p-glYHZ zl3dKFGyl_?5&jYbbEB zsZS}&CJ8P34oFYXYbG4@%AIlDM&=mn&`~XIoOZcNhelux)vBW-`=VF=WA)+5o?i?< zH6v?g;y$Hz#?MW7+~l-j11{IPM4#JcV{&)jdZl!W2scwo$ygHg{Pv(54nx7as~Ju& zcc8s_BY@@=v9v#tk;F=7B8e}DMp$AnIdJ}caj$gT*d(D4n6``SO8KV?Pgcnw8Z9hv zqi@t+C8ZI4y^8Y42nlUqz|h6z!g(Tw+M1)GF^4TsC7LIan5u)XbrAT?-ac4mFmQe!91ba_)fX-CUjPCvW1xUhKl-l7uaIGx(ks`4Y4Ol6>FXa7Ub z)j$Y6m4(13Mv%l5Ak@SR1U0}3c5%#GxGid D^y0ED literal 1665 zcmbVNX;2eq7+wxFBB+2`@LX3~aUjQT*pRFQB-stgV2}WosHGB;4XluCOjZ*aM5P>R zX^RL2two?#I*w>XY{vuC+Jbmtp_fq4aZE)E3dL)xbc0~~!|_LVcK18>d7k(9-gkCO zd|Zr=*K{uc0DM$RJb@i2yB^M1_C04{NXHI=454KbX(MCTSxF$;K&O$Q%A`vt6G)w5 z-Rd?n0suUWRFam_suyE=+QiejFg%CJ!mFO==iBXeo?XbJt?7JNB9XW>5QNJjxVCj>M(5y~ZSzJIaMGr?QWl1y z&7ezBmqxE)q!8=r%MeT!wfdE?**20WHf4N=&ccUz0=~)Qifb5cV-m=J-FPk9mbA`7 z@)JlKy~e6%&%-!xl+5Pt&4ye+)*Ebzm0}M?mxa^%H73%`sBkI7e&HD?14h6Sm_URC zj1mX|!?0L^2*qNdK!)Nn6bU0nHD1S+3j~B16@-Q31PZe%5&&a2O6l-^cD|{mtlUqq0LtB$*I%~87;xlN5wq?*3P@X6PgOe$Id4LgsN1SxAa{N&MB)Lm$)*FTsZ z*q!?IP}a6pfjRQXAHp=3Q;n!#hVQAy<%IWm=>0uYkKS&IJBxYb4Rj2A<3GE9(0KY6 zQ=PkTU(AWN=QWOs=AJ73+Rg@lIh1?(45dj|OrZvr`ISuT7&v;j=}%c}!L$zsPkMLF z{%duA#yL|t&pL5veekU6zdFYp zY^T@A=%{x+(qdv0_4RRiGE~)0&G|#k8>t``} zZ6STt4K3l7{d=#JuDrhL+=RJTl{c5a<@d#X#U|(3*p4z$>+Y7jlb7T>Bjzr>HSau^ zSE@BFyjgb-Ek2mvJhOjNUy3jz?&9hj@iDD<6*;IaS!x+PK{St?$?YmI7SaGGFzt^Fb!Ks`>SYzmf(73Nk z3j+27libF0dwfFM2QLLJ$V>;a7O5ofy_n+Yy;R8iy}WK~Q}ZXAxqkjzKAipT;oSaj di=M35=mtFMj1TZ#c-zhOQ&16c_yJjJ-ajM}biDuo diff --git a/toxygen/smileys/default/D83CDF53.png b/toxygen/smileys/default/D83CDF53.png index eeb27c83d0958bdcfca33a61659aede52905d77e..54381310154c6f340035faf7bbe8fd2f456fa7ae 100644 GIT binary patch literal 1989 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 literal 1830 zcmbVNcTf{~6waZXiVc->EQ78qqUe#$ZVE{xVn~6YL7KpT3NcwAld>_pkbq)5fm1*@ z7|I~!EF7Fz5KloxLG)0{SVlokv7OjaniY_88ximRaQx$TcK28Iec$`u`@J`t=;i4) z*=mjzg+iGuaTofKqqX@N|2_Gh|7KW54zmeSAmNLJ5eAtarSKKlMih`}WZ|d}DpN#n zYCxSSlrdpSzd#~Tx|%D;G*p?{hHBL4NHm4w>|)f(4VAQC!IM>JnKk;(Oxp^A`flnrc ziU*E}3Y2;Q0!)trOe)<`PJ>~9#iGItCW{6C2p}{VfoSB-a)cRNCX0(W0iQ3B^rlyY zaeaj1&%VeN4~!rP9T$QO1_RYVr(*hW2uEc`mc!HAtD%t*z-0trS**bf0=t}6h;DEN=;vWiC?H=sQrjFX4=VjYG;~U=w zE(u$-VUXsv-vOO?K4(hT5f)?J;gmXW*T<~KB^lRO+Eq z9e)=K)k<1*m*;jyD|Y_cXdIZ`H?Z<{lsD62=t7pmrtaR1>$3E!oVf7eb@QL4^t0tt zUP-Z{CtDmY#Z3*p5ZoqxKb+G2_SkdP2XV#O{BnP`zAB+*T1v_lsk%uKY;-D5r+oq+~bll0@vbF{{K^vcv z+_srk?Dv9m@duVWUz2A9*w^^sbzzCruM+6vd%UFP|lEsx|+)BqC2?scY!=e*m+=?l%Ac diff --git a/toxygen/smileys/default/D83CDF54.png b/toxygen/smileys/default/D83CDF54.png index 8065a3e966ac4cbe745a589b4c64a70842728a27..f5dc18f50ba89f80ae03b8b68da8e78dbf0b0c6c 100644 GIT binary patch literal 1720 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 z z=m-Jr6or+xxKE8PZ+Wtxuh|S{0P#7?u6WFRjq75A>%d`f*LhWEw82tqXdfF4=Byt( z<^7rkdJie}qyb?nfF5hLh{)20?iZct>kTCUD(L>q;C;uliuo(F8TNhDu-Gqli8zNH zgRN0gQB&9W8B>3ysr@QsfbAu0cL6!FNOvfJCwdTF;S+F$wP;uyxuh8YDOQSKV4`nG zGK!u+3y+KmLnSilVW@+#bO3CQpCsc7-=O5~OsO)ZPO&m&Ue3x*oZ@eoTWTCXO{sly zHpc7Z15~&8lwf{w(riKb7ol51v8bJc(PuP>pSSe!ZzT=v!_wv$&BXAwWFha0DLGpO z8RSPDs6Js8jMTjPjlYYGSIpMwdu&3oGEMlZf7SYtn^+fK;4T zq)w_+jOGQQ5^0ST7h%64Z~tqBj+X0g=g!HL8P5dU<$ES_Guq{nO`l~GS6A{@@WsY^ zCHmJNb*WAZ-{H@bb)yeCTF~jebnT#u7+qpNxuO0m@l!SPjDOAP_p=iORTaShw zlBsgZOD{6?ST`!xOprJ^fEUWV6-AAX^4rFR74h^a?s-Nr?vioYNa2}Cy{$&^c76|# zK21ILw7OjU)8!;CaE6COm+DDxbin4Z3Qe*-gFc$Ae`O%A#?77I*){bZuS{4dV9UCU zB8KBd5q(b9?z-}#;=5J(Plt(%Wkc_qWs~_gpX}+FV-~#ngLuGdfY#FcVW8H@Dz}oO z<$L^t9&$*6o`;7fU~S0shgq&I=*qBnSlFt9 z+~<7pnSR?Qka0Qi5I&T&h2_k~?JlppUW4>t<9zuQ4eQ$WW=i48rZ>8A*^S4lOYWqX z7Z%ogn(0b|RV;&20rl3~e}1fMVB)@z#4y{?gfIwzIo8}9gSEzNw(#4GwcTQAYhh!I v#oA)AaWb`k*LHN`4?`?%Ut~D*he7SX4lN2|ra;?ZEdU;_-Y(aisp)?Kc!;h^ literal 1578 zcmbVMdr;GM951q=f;i~vscfeSj*4xQ(yC=0h_1{AIl~AJ@Wm0 zKHuN>k(JJ@%*6PVc$rL=XtxpB(wG!^#>7eQH|`8k(lA*h-J*;0i6Kg$WhO6IL<4q~ zDyFk(%3JZlX*yjdi}f)%ZqeVV0NBGFf^?n5R4?vW%*Ac%*J;4m*P%_G|o z2n?HuB}VY6Q*MV7uy6tm=u{e|M-9UOimG6(4n^Tv0HTHwNG;u{64v566i4*H$OTGj zg4c&<6V?$eX=MccqR8VA6bgk@A&rU?iXj-oum}f&C?yMJup%H*VPzng7G)slphsYM zk>LVBgpn%Z%0we5dHVDSEbnkUBMt;d0wskE3R64;tJDz7M$R>C9TcX=&9KCR5hzCJFifvTEr`Z~MX~lk zP^1DLI;zV^x+7T43$eIGped0PayYIuS^-W!CvrhQ#{(7@3e3o50$wf@oEb^a)6){P zz?9QotH80qaC~v*Ir=6&45OGHLv;p}L}1ut(UYVO)#@>`88Z+Xa0Ki9KQV(OXP`)N z{HIu=TT%r^rq4=WT0CnWIv{n7AhpKwq|OeRY*fCTFy(}MR@D~R>fW1la%iA$L&Hxy zT=%FmjJ)U#cfJ#;?{;hj|fHgp;545n_I5nuk=r+U11)&9T_*OuCtCR^TI@4+uV z5g$Gxj^!G+D~rcnO26^V{6^Kai(5{26ds;=s`zC^e4G5vnNug3&hr=L&9}QQKAvBc zh9?{^xKWstAU8K`JFs!Rt*U)YOJUn*yX&3qaLox>@1l9SO?}%)&S$$n;mTQgPh69| z>Rio+*3Pvn+qy%0CbZ{qu}2`nuB~ZNbZn`OukKK7 z?RhjBD0PH8LXYcGrnQ2-i#IP~o6nVaK0l&~%g(E6oToUPkkQ?>b@|upVh*N1(1MNq z^(9$zyKH&W7Ee96aqz>oI_H6R{P;js)8&M^Lz9K0il1BbaYZfd`mXnnumyiLTgTOu zCn{Et-&sBOkM3#tjaO<@#T|I)~dh(oEiT=j3V^8uagWDf2 ziAASE)v>KT{d?BkJ)Y@*yJll!>ZCH?soP6r=+rg$*$vw>X78Q8=<=^iri|L3I%{b4 z!a3h#ZLWpZahn#5B?jbIUBHU#yIy@oU2i@w>}<{#`|s&@-P#N})2yn5T;)XWr3*)2 pUywCEIkTRJUG6E+-WDh(PF8$NG1_LV_$u=Ewv$=JUUT8He*mk#Nx=XB diff --git a/toxygen/smileys/default/D83CDF56.png b/toxygen/smileys/default/D83CDF56.png index 2c9d393b88c4803e03c0eb513b84f82b724efd42..3cae88ec0e0b7268577f372c1a2e165e2e456ff3 100644 GIT binary patch literal 1981 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 diff --git a/toxygen/smileys/default/D83CDF57.png b/toxygen/smileys/default/D83CDF57.png index d21ea0d97d5f7ac29c6692aa8c8edc1dc22c77e3..fafc625fa7c28b802a9a7d72cba31d58f45c7c20 100644 GIT binary patch literal 1608 zcmZ`(do=9aZo*;3 zkoTkXqDjVU2qh{}+haN*=_JNxf1S?R)BdsdoO{2Y@8^C$_kPd)-t*1y@p4gC(pCZh zD7(25$&lUlb#9i2);;E`evpB)@t$}9nzOg8(>FmrD#VrS3BVa60Qe~YtU(n24FGYL z0KBCEfGYw38GW(K*A4(!u8#+Ww6U=P#q_sd9=IwTx?V48Di^m^cUI(H`!m0>IP<2k zq*lPMzmU>cc&?=+3*yUY#Jb$%hJw@r5A#A7;}Z8hsdl>GK&ht*|KLuDfk1_~S<(S* z$o&_856)O4p6&_}~S=Ae3(g{E{&Ng z{`p?KOR=;5y-*uTl-*c@>r@_Xs*o|69oQX-?>uhSAC7&H>ia04HkL-o!RvJgTlIz6 z49DRgr1_4gk&B%5Z_q97vM}N(+aVs|b~rANpeH07wFd01_cJbXF@T;6Du;r5)7O$a zamij#g?Y<`@7CI6OWe?f`!Bv+K}#o(Ar{`I?(&wEYQYAVsf2Yx$90SKrGj;ZaDms?K3X5Q(xTm9;s8f;A031A@e34IlO|p8dSkOb#aE55Kei4o zYItFE`^LRQ89lk9`3>;uXO6$z&t5-r1an^A|(R-3ThcJv61}il!&T$bRFVSvA_w#Pce#phS z!-+lk381=l||5>2BTj`Nzov@4?QZnGtm=>%aAdsZ5zA&ftKK}h6|Ay<} zQfA81J6m9&q&Q)$m66%T&siJsMZqs9rP4TgwF+spr3o1U6#-8lu89xMZg)*?ubkjm zd3VoOdZZw8=5?f$yA%v;G)qfG)*6m@trkATHYU6okWETjcgXkmu&S=-6sum}_EXH7 zcTj_xj7PRLJ3i-d+~IS8P2Iy!PVxwzCsM5-@(<=%HO@BS8GHoiJuW=3D(A4ne@;(&XX!(FwNNTIRK52EnnsU) z6)H9=+*8FhUhstGQQ`Feg%CD)8Yd=wE8|>T=S-g zx`|YiPCk}M2qwq63tN;ZL>qJa-PWeI#{SACGij^hK+6K2DYienUv$QPoc-HkFK_rI}#yb7~ z0_k?P0wmD-dV?Q_#pTgsnE=n=1T#@?k#shb%%n3;$MiFy_6~p>$&1)Ppe6qUnt`YT literal 1574 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY>^%$y8?rdYT-8e6)V zIhtFTx|kVQn!@yY<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3ow(%jO_)WXcw z$k5Qy+1%CD)Xc=((#grv!o^q#syBt4Fw+M*Mjw`EiY{##6H5wHum6@S{eCCyoJo5? z&z3LE{gM*T%>VzdG42vz+IP>s`pO!CON)fQK5R@**Grrn6t32n9e-PRy?(;BV#a8J zw{LUm7X=j=g$XVeQOh}gX&Y03?&+)>;_DZuUKENrEjeYH`-S;0cI?V*NPaqt{WqJ~ zy*(ACHaDy%Trpwo%iL;fQUB*-8pEkms=r=k?GJX=sGR%v$F`XHzxyJ&le=DT5n808 zXD7S$Klg{y?F)j(M`Cp$GyPYZCod0Owv)50a{(Ge)A}-eSLh6f$$QBi@gED7&>yN&8 z!7_d7oPg`QXWV|T_ItBg{XgBUJncIL^w^D!1a`6>*euvLEh#;;<%Cz>3hASVE%(nm zbK}m5S(`+~?-(5v7U08W(SBXQ_?{`eQUVEgzDAHJQ qlzB>vO(W~!#M;h@JGJ+-NHDw>kgsgGy-pBRa(lY^xvXj;+IuUlPIP&k>~cNW=&&~1@OX#U)oEe3X9nMx7JPGBz^$2KS0?!% zthGDc?0B`y{c5|z)pq;qlLD^xdR_1Hyw>Y>wa4{TtKI%mqf0He7aOe3RGD7s^}O8S zbg{|tQj^V{Jl|k- zzR~(Zqve?z)0zIFYZ4TW7VDgAv^rC3ej11>4UZS;@6XXWRcCRc%IH{`{_$e{lO;MU zqog*ZDxGPvJX)@QqSEAeh2haM{bQAer*b*yvC`mZmna|i<*u`Di!&}zg zQQXN@(!^3E$3y5)rqt$m(PBM@K0BuE;e1*4JjP~%4)snFc8+4!wxUKR0(ma{JEQp~ zIx^34W=*o>G&B*kv=One7B;sM)-~W!R_91G=a}KdY%9m0sLH9T!C_)1U}P$wslz29 z&1@~hz{$%XAk3hq$tkPACab{m|Nnmp+lqO>v|v{f21sKV#?PQsvm%sUi=Q=J9lK43W5;oWQ{3rj{laW>)6L#_{06 zlTDk;3rdQfJ$uOTx$*oD?X`Ft3?*4+0llC?0oiN_j<~DtO%-*uUn>Kc8UfG}aOl(@%HM4CB zW(*EZF*ggIUC#psmTHM>L`h0wNvc(HQ7VvPFfuSQ(lzif*EKW=F*L9;HnlP}(l#)# qGB9Af_n{d@M{a&fW?Cgw2Nn(G%^vm>71g;I7(8A5T-G@yGywn(H^C49 delta 830 zcmV-E1Ht@>3hV}u83+ad001BJ|6!BZ1Sfw34oO5oRCwBCQ#+5`R22U1^|d{-^YD0f zb`T;wl4wLA5)DE~L)$TY|K_vP;3GirS2y2$?8(g=qrQJ3jw5v9J|$SlO5^BZioYM^ca9FT>+XkxuRaFM zs?Iwt4qo5-;QX^se0t%zXU1oqI)mY`gU}Dq4g`ArKH@G5aJpC5~^&#C7Owkm(YLoaBnPM<-?2Xp+Y2z_==5brSRp(+Y^+(*+`IOo7Zj_x3WOpZux z2OhS-NHxm@+HsHK#W|7WhHfgbJU9gR8q~H~5hdO=tk&TCnBoS!?StnPL^}W@cZx*; zSv!czxM^0(H^xIkR|a3-lh}VvI7BGm(>9j4*9JzGxHRH;`z#|`62~VCq=laHOybtB z(-dWv0d%k=)pJAbuk55(lOA5cQFDfpz#Q3Gl@fpcRUlLD7SD~{nI`=1Z~G~VLX%sL zEOVrxr9_2>o?03>0^%fMmF1YuPOwikfx(V9uTb}!=3ec*cjLhDJ;{IYzzQ((+*krK z4e~(T*w~nzot=e+g_D!By}iA&voj(hLQ6}Fm6f%rsj0cSIVdQ|%*@Qf!lJRU zv7w>C$H&Le&`?H3#=*fsOiWBlN=jN|6|Nob?RCA_AJ4+CV4YxpU|K{rg9b9C`Tg;foh9-oJnU z>eZ{OSFgT#^XBvC&p&_ueERh1hYug#y?giN%a^xr-<~^n?%K6$A3uKl^y$;ruV2rf zKmYpm>+j#cfBW|B%a?DTK7GD-@7||RU;h99|NHm9FJHc&IdkUv_3MuxJvn{m*y1Jq zOP2O6THIAXcV6?{c^&f?bnV!=?&Zr@PoF;9zJ2?lLx*nPzWwsW+nKYv%q_X>9QeF^ z#q1pgY#sRZjTnndB0hfj_~_A-VJ)2ncf8xBtNE)obR@TUb_E#4E_a&Cj5s&LJbu%qzg)`i!7we%8Qf z4l4=r10_W;h=(!IXz7en4fYqotKVoPZ{-%fkluGoDSaus-uHI)BL<1f`mWD-=&)_` zKY9KOAFNqISKfbo@8Odtdy-k6r6~Lbn#7po?e0?S$Z30SqM}4Sdx@v7EBiBcJ}wn& zVW$!gpgg~)i(`ny<>Ukg=1NvqRX4RXv9K_X_()Y98>gwgvU&@|Bt>NxuRD0~h zo&9^^p|X3wtbN_1Cz|~JY;2N|`uXzl_Wv6WEO;;>z>a;u=ws zl30>zm0Xkxq!^40jEr;*%ykV-LJSS8j7_af%(V>+tPBih_}t%xq9HdwB{QuORf7z& Uj+cVzY(O0hp00i_>zopr0K0hg2mk;8 delta 875 zcmV-x1C;#g384p&865@y001BJ|6u?C010qNS#tmY3ljhU3ljkVnw%JsF)n`tCrLy> zRCwBil21saWdMht_x;|PZ)U!6Q*mlXVZ^4TA=GU}izpB)M5vHpVL?I%iZOqNYPHJo@o{c$ zZtkn@?(UnZR0^#%LI|w2WHK4F*0=im`~S{nvs=kzl6*dowAP}vCJaO7=jT7m<#M-g z+_-@d0;Lpj9Ft0=kWx}86eyKSb3qV1A4L&bYowGCfbsG1k4mM|C%Ie>r4)%o0x1=) z>!P&=1X4x0Sd58@q5w!Br6iw!fjck1 zK|bHj@bK{cm6eqr)zHw;RIylmT`5H}nPz)?lW)HL9M@H3+fsjAU4|HI@qM4S-@M0b zufD}a5Ypb>&dA8fr%G!*Af+UVB7}rEHndt1uImy8ptZzN0)ayuhnzGT7-InF?CiWF zgCNKVA<)_og%PpQNGFN0k|3}+PLd!rv;spA8niZqVSv_}X0w@*^?Ln>&CN|3jT4$p zpWr&cS_s0JDAa$nf{^A#K+`w)%@$!8;QLK{-)Db+e^1qFwcpp**MIdq@7>OhF8(>Y zq;YbM#FD%D3;+VldUD~k(ct03-)y;D{#YrcI6OT3U~O%E!^^bYHWn_L24f(Z zZqaHzsn!_q{l`2im)YLly4cv*c<1Qos3LlLddOz8R4PLiRV=>vei&Zdtu>BciDILq zFp)$$QEVJ*q0MVAKb!0M{`Bed?Q`U%LPR002ovPDHLkV1j%^ BtwaC- diff --git a/toxygen/smileys/default/D83CDF5A.png b/toxygen/smileys/default/D83CDF5A.png index 6cb3253961e7ba8903e122fbacb95212a4f370cb..e9e4f2e733021ba13c262d87a0096227c376e97a 100644 GIT binary patch delta 1701 zcmaJYDHQ=VL4}D1!cxFiqlus+R5q~(v>;nySd@mMRtSO$GmMp8 zst{;d0tkdH0TLywg&2Y$1V{uzNJ2;;hAoCAfo5j@jDO~yd){}K_q}`WeV4L_xhK;E zrwIT!4&Upo2CZ?o#$M~$IilPjBb`m}t zM<{yo=;ug$?gM;oN?Kx+y54_fmyUL@zx%`c(a)bImK8iwpO5cHLv;S#$Nv-Df0FOH zyixYl#>S>nshk#wSez-wI~JEOT$O*6NM(GHM6tfERI28sGSPyRIXW>u&0AVootzb} zeN?Qh$yZj_R^q)KCLh(D!G-pP@;R&Yr zbMI$`9G-B3i{rQVG0D_+>4IqR?I5Y8rKy!V!g=5QW>6$uo)U<<=*%(BWFKRouAxC9 znN!GDWJ|J9)@V;pcN?XxmRMUutZ61Sk;tUZ?oOFZ_NJpzD&{FS6)M%%_O@zgXIuR# zEkyhX z<3pWegEV4kHid}qAeVQw5{RWaFAOrZ)HC}U>*b-|nJbO6>jG+swk~ndyk>NlR}6G% zRU%d>=ydFEf!=93ipN7~Xc;;E%3f2+*)ID(gBZh6o>$$Sq5BTOZXX8#2Q+T}%@qyO zwGX+epM_vAx?>=75Pe;ket_hG6#$@>nJ_jN&oY8B2u-uOtsEFZN?)JgDjJwe=lcoa=B-4dFIOa1&qx3 zcUL)`6+7KkM!B=Pgkw8i$B=JRrylQGY&r;sH?yl*W0e)@(r=6x8ng~LJn1Ee{o-O+ zeKRXvf2vIKHRa^+A-zOc!M9!;9}>9d#MbDu_H(K6^NlVyc;C@oznq8(2qGGJ`L~c* zNB$ave%?&4u(Fm^IiI*G{GZbpp>85c5>l9_prA{fS(==Rvz zAkUC2VMS5IG+s5Oq-ftp4%`6df7SFX9y=$mMlaDmfXd@`2y4WNEupm4bUqq*q}3fgLh&g)8KxsZ*%u19pI17C5~8h zP8FOh^(33-FvF@A6ZhHrf3U3nJkG~)BqoJ=P$xDrT2b%?Opm%!bw1VW9dz>Mki0Eh zPhcHrdGc~jfvIS%ic`Ka&7BL+&3D}&=nSW*;vEz=}ggHfo8qw z1IEtbqb95=gem`zh^VCX>*I0GhF1DjS*YjV<`FdGf^#m#ZcWbjTIOz$vVfO<4|(#U z{^?gUzh>7!V5hc?b>C!Z+I6X}!~Hc6s%(<`CU_b5OZOMLcURQAo1U4!r2k7l*L2G> zR!B{8!HK1}^BH%{pu){Er!*;Hma+Nq#8B-}{4y%C(1Uz*tEEh-apN!l42A!0<)dEE z)r)A9&le-XfgL&p1>ap`PdI(`TfoT}f0v&juE)a7Zlr_ajMSGesA+^}jx>;dYM#F! zl9?OKo9{P0q9xGFc=-6y))oYVzJTdV*F_@IalL=^LXJT#En$SRit-F3A|xa%R3w-a z5)SM?U=*H@K)B`R=lS_u^&KA_%Nu`o$rj;eZ|7j&(o80i?HuhL+>nU=<^=3d@u_G1 z!d;*4)=aGli2Z)&;XiULJqjirF&<_=5L0GHBSyjp3hfg)J{au_C7?8WWzp$J5wi%1 z`yS)F2U=41TUB{&=}Q7JH1G#4!z)M!8+&9+eRCt(Gdv^&0Or!UgrsM!=IQ~!u$XJu zkRWWRb8uX!8h|ar))tO%gxlEp*dUyd_RjY9)(C_%0?~bUaL*@%=-7~mu)CiU4E~qV W2>q!c>+msM04`!Y&~@hmQ~m_}k6C&E literal 1722 zcmbVNX;2eq7>*Pv(m_k)lL0p3st3l^LT6G(;K(NDZoVOo6F1 zSsATZ1daBd7FQ;ciSlSzO&Ay|2ZmuYm?$=l77=ALsnmLm1k$i{+{mXtI?+f6a1Ecn zBuvhdn-ENgOR~*ae0Gddovl~%H1w!QAi@Sy0tSp!0XD-*qXo9{>0`PuHFsA)C- z)br`Dol2C)0tjKofG`F-M9l(0fWu*cp76b_7Ebjq4p zqlFcyc-$7X;?s2`X@Z$dtJTV|vKfRqoeA=IJckAZg;0nPOO}yT*+Pt#fC&W@v#8Cu ziNpyb;80Yh5t$^PPI>w|1cON~e_ zStg9Bz$`?jSxwbL8!$npa`$dS4j|8xkQR0hc41 z;7W}aQe{+Q6LxXR?l@QYPA-g?F%?Oel?1VJq61=e1W8zQgb6_6IY96d+^8X}mN|~{ zyp9&d%=nj>Mr*JeZL>F=py$q}X3o&}dG*QdFR{UE3Eg(|gAG@V7+= zwVn&?IRY+55LuEkrTc7gj@#Zn1=X(kA7yklZ9MK3obbDFR}s^DdK#)H2r26h2`Z3Rs$ z1h-|oyXwlS?G5QZS0p|uSNRR+M@J(L$DX|0S{~8X#W<9b;Ac(oSD!*8L7pXesH!w5 zx+Ax`_mEP4BSzX_*XFtIEvX;6{k)6FyfYfv;~VsySK;dN;!RjbsCSXe%iV^#cOO>u zSeL8_hdwxtraZY@<5yfc7}GJ-&JH;@#~03>a-e8K*Za=~t0wK13GQY|6t_Ohy*G9K z%p%)B-$`91(|UCI)*?w@QQE7P87_k8l1?sKk#w*?aUjoL&z;_xHht-iRlSMY*@~tN z{@0|Z*uzEl6TEeM=sw-W3_SAmmfUZFK>8VZd<&2~>yu>k>eBkPLo;TdQl!w9JxV*0 zv=J}u*Cp4^uL}0MV7!0f9-KQ53Et_w^Uj>>jhUB|j%DbZpEzB*pV?jJvZ4AWJ9`h* zK6o*;#QF5Dy!G};+js838Jqs_AUevAK4`67AzL%_WaLs=Z~o%M$b`w;G-}!@R^BMB z^n0c2jAuGMbhBYip{ut(HCZfgPtw=2{X2jC@N~WNX8Yq?_kNq2?dN`8>=&|Zy`i+O z1Q_mPmsNipW9czVoJzDFIVFqWipqGYqSJ}H*}dwD=vd7{1E+FcO?G$3{vUD=Hl-I87C-GKbw6~I*K~C=FG6tnQ-9bV*wpMC*XX&(!91E7&CLnUk?mJ6N2@G;-z20i)a%(6%V0ymr!n%E(13qV3ty2dD|~pP4!hOT(*OLg zh09CZw6#Hx&9e5!wa;H*y^049MZLuy!$13u^bt7{<7Tmcp|0V~$XEHfBBXM5TjPn) f7aLa%$FG}23-l?CQ^v6#JN_n8Q4D%en40qsutT3} diff --git a/toxygen/smileys/default/D83CDF5B.png b/toxygen/smileys/default/D83CDF5B.png index 0a79679a73aa5bfaec5104170afec7673a355e83..a6808e1a6e827c7e28362515c8c3012f5d248884 100644 GIT binary patch delta 1702 zcmZ`%c{tSj8vmMRL6%C_TGuGy3~m$0HaThvlQ1D?)EP(ir4*(i(_(5Kw?np>ENM)r zDRGFwSQ=%|#XTg+(Uc|YScWkeOXB?QbN}j(^L^gu`@Zkz^M2mvdB5McM7dj;sYTxb z05CFDJqtN_2;LPBKzZ8EO+N&bqXSNPxB_t90D#-^0Bk_eZ4m&`SO8{x0l;Mgpb^Ot zxZ46CH|OT!>9D=MecaXzD$B?#;5;j-ZG2N9Xkc-;i5WSWdCyuqx`urS1=gBnl=J0W zCMQ3?vw=s4BFH{;H8s~1w$v8BeNzirRmr0|-s9Lvzq>at!(cFW+U-ZIJ3C-7s5Kl8 zzjZaZvx(o?SV@cYN5J6_2pw$=wU7&*S;=v)DxY<=)@CNf1^();uBQ5b_HS?c-!PLi zvn#Sy>C*DY!QqF$#kHO>d)NPQdVX(cDAuQ6Iu*f9Fa;!<a;YA5$&9B>&(%h_?r0?^~qv6T<^t zUk3YIUY0}?)WdDzG<#$Q2~PL94hjPhkz~Lm>5TN$uP)7veCk<|h!&=V)v2LO(!Nx8 z6!)TSM@gDYA{rZdzc4epvN*lAB8EPDLTecVz28Zy2l0RVbC5kddJz_}z9o*8e=72D z`}V$X^Xv2&I|<#^rj0QDedBA{5e4Ibnlaht_^SvDC8Vz1IeJ*+C2xlx5vD*EY3JZq2{%+p=RLugDGtVn_KI} zn+X@_>7=#pmK-l~vN(9=#E162&!kT;)x={lb;<_@ywZ7vm8YjLgT>gJIWHX85vl*~ ztnVoG*WAt2N=k^85HlBsVwZx?h5pnS{#bhqe)Rz9&1Xl%0R?t zZ*veH$BTR9D=Z%V(zMGqcd2^sF3Wwbj->Q~fWr*m>ChE&w?ZwQUo?UUBol0uZ9dSA zz_P!6y_!YqkL^(54KMtLkr38_9dop-8XqhS1GtX7?mOxk#oj^hzjD!WwN{L-|d|m1x z6k&X!`dfaD^w*|G%QEZ#hS{p#r`+o03{DoURn*^7j6HlLdJ*3%B4(XjB71BB^B4DC z_Z~(3oD8CA{l6T1w{}rO)Wpz#U%0GSV3=EKtac2%k z=;~fLwtc%1Er&NiJ|1EA@aU3+_CitiKy9p_9+DN``gD9eqmRFw{71>$K6hmK=U1DI z<|m`~dn=^(Ix$hm#2L0wuyMDoU^1(}TBhQJEb1s#2$!|Y zL#n!R&94nDhf$oK?4vWop85VpexEv7}7EJq2u=_j3^zINOOV3ad04E2c Jed!5b#$T;P5m5jD literal 1708 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 A2mk;8 literal 1778 zcmbVNX;2eq7!C@RLj@EB9l@Q&iYPgDF{cD2*+ilZBBT;TlpG5Ll59*?6L3VM1;hiz z122xzQ9(sy3~F0ytivcHNGsw|50t?Jgkn%GaZtKZu>IlqqdU9%eebvL^St+LZdBx= z39i#zNhHz)Q3MiAtRD8q`8(o0qi0w~EK_lT6pz7@aHC9*lERgk90f!gSuz@p%9QDA zZlIwg(%2-GM2btrKX4S7h9a};P)r&ffhLhc7npQ1MH-3&ax__`<$|{AD(YjG|}c=|a6jZQ57Laa56B#KBG)g;qVAqtJE(b(hqq;0^X(SP0es+FvVI8s>MYJ7bLz=lqw|$h9CyQ z;xUBbY`%aHczhZ|2=iHVm`{U+O!_Ft*RU*@4e?lz0D%z%f&_3l4`PI~5IQ^$frUaQ za}+Dm8gQ9bfsWc$5q3wg$TzVZz8;m~m|lWm>d_8}O2Kfy|ehdSc9jnqRF{2^K zUY^gEmfleY2kq zJ}r7sRMcElE}o*8Le`uBBgBm>Qqu%SuepVLO;UA($H5am8dc-=0=XwO{%T@S>YHO4 zt!tOya;2+qTl9+D1&xV28q05dycv1^$f1UzzssMhnc!`6veocMp^a-R?D4ZcP|F9| zoS$vJS(qCeIrKtm9^FG=h_0aC0OUUSG z8L%z&Pn%ntJ(0boCh&Ovy*Z?(dG(QAyqQ&{V^-DJj#VD`>+rT0tJ@yWKeeZt>~&_6 zlf2M5a5z{u^o*AL;rgZDW90iD)(VzQi}u=o0C9tordfL(TGfu>9%aRCrz&h$+Sh-i z)Svb@CL|x!hXr(m)}JrE{od`vg;XcY-q_voYkio3{$?NHvce;L-V%k?%YmA7bX~dN zeDwiadwZzT*J-C?f5?kg#m0{Q)hjbw$M1W$L2c6boh+^O$gSI27bMvsN?9?{G8hyr zVfhZSR%Jemd4A9Os^btP_rh60q-|?J6MZaMlh|0yALtM(o6T`kbArDwaxeRP-8A5%pZLdsIct~?5ZHVyn%d215#5C_+wfS)7 zxQku#-cn27fO{sTry%avHN%hHFV8jF1amB7Q(tlyKX;yWC$Qkp-^?d%HNh|Phx>f) zG}P_9KfH0z z|EK=QjRP+V%8nJ^@2e_5?ZR=$%3DTSlG^}mYRiyX99dn*Oe}bFxgpDDieq<4*O= zqHkEgZsK!2H$Fr~p1UYuS+%uF!g!fHtJHrbhxxX4S6*lHiC?@y)ACJm*6B$0tw&wm olshh4W?xk7?0!??6zZ8sN`GQ>Ub+9oJ^NosB#1;Rc!`_;0RVQpumAu6 diff --git a/toxygen/smileys/default/D83CDF5D.png b/toxygen/smileys/default/D83CDF5D.png index f76f82acd9395696a8e24f85f1716618af9ce891..7b67c2f1ad28f29cd16e113870e076b13a02bda5 100644 GIT binary patch literal 1848 zcmb7Fc{JN;7yb#ghE}WA^2OE~wWKvlbw1lzlCg|N_+raY5~CHgqN+2kD8ta&s;x(l=(UR>-*#T<9pA2?{l8}-uu4iz2`aSCcC*h%1WzB z0{|e4MA)G~JaM!oMM1A;c-$QXv0xh)8vvlD%lsX14CJst1j+>f?w$nzQUU<{1%^m` z0EmJEz^Xq0SmXhKQbb|P)o%a*qUYv}wm&>POic&@(-x`lw>{*RSLB(l@YkL6!C}tu z7`LB2+RYrOu5XG<%kHWrOw)W;+nuB1lkY!#OiInZ_iIK}ToOq6-L8Tb%hleipr@8* zfY#nnzCb5(=)ZrV4V0YG{&o(l`O8s#c~(_n605zAL93;=9$9+A?fkTR2721ms=PW% zzL|-^(Pz#mrzJ!UvuLUD#4{?V!3emF^>xBj6YW@fvE&>Fcc9n5M3FhpxQ&4?|h>z+C;%MrUsXTd8zxm+doapukiS zTWSvx^d)YtPHinSHbR-j=^D9IDMoDo=U=i)q>2(}d>l?;jt^d9n3jN7za=TbwtPwCFnMv4p}SJNbK` zKkxP2vpAt`>r04=@JAZTh)5~v8c6T7awouxMXotH+5x{FQ%8VAf{1jnm*7drN$biO zkfO>#)yCdMV~D7LXjsIpaC}H84n~ZLz`^biA^^ZVHQU3ft`H8L+E=*zATx-0CP_tT z0e8$X5j%Szc(>^YXvo0lTFiM z-->{OrxrbaBWZMuKxmMRSIg8Q)KOR|cdA{b(e|_qm+KQv ziEDC{GHlRX^K2{QDc(ud2rfmbbkYz=m5^LcnQXgXN1>P>?<&_+%M7bzT5%zsb+~Sk zC1f!6B|_{|l!Uciaojlu_z$o#Z+ zOeXZa_huvdW5dDhSv1NT~?74=nobywaeUA4O1K;uP{Wv!lMl5C{d>Wbf`k-B!)N{ki)ilg>pcN?BQb#JXtAeeHjdK1=!@99J1+Y$WoOB`PocIUnuUCqPu0 zmpQ10)aZYoRADY!{ju>=syDQ_|D_ECaao~pS?97)_`viluj9_xzm0YE=&Iz*!p)T3 z#x=azfWomId>8PmPOwZJlvXP_=V&5h&@7PR@#m-IO082MQGq}$B;@1|tSzQFfmTJh^G z+(ZXx@7W6cd$Z7~KVtgE2zZ`iFVs25bL>*%?OnOFM%O{3ntrHre{a}^#&axo#*%jb zpx;{LYz3*E?BA9#M;-!T62P4@gmb7?$W5>fxnF(`nDvk1I}0|B@Qzj)CQZf1Dl zYxD)Ug^8(!smXab+yV|KQ})gNtAG$56oQZW-v#WC-#!EjRE}P7562Uu18(5}o1pM( fI2e);5R60N0)k?0GI8KI0|2DGt6hz)|AT)3fSqR< literal 1776 zcmbVNYgE%_7_Jvk6dWo#6x3J->XbGKZD}hN+NOm9<*r4%jL-%fAx&w6ZJEU*PL3kZ z;eFyZr=TG7a*&A^#KDe-gU%sNQDh*>L>$Ux;6xEzis1IcB(Lf`!_2SY|>`qZtf8f3qG{Ct(zjfUUuGB4+2s%S-^*h?pv_ z5>)D?m=+I8GhoqaVKM5oB(*@p^j`w_nT0d~fl(-6CX#hVp;^Qn;uX?++cBF745?5_ zBIaACVwDkqlr&%fm*wNF1|bOG@mLUt%i}?d02qW|Hb~z*Z-^u0@`SK2Fnlp-YX(iC zFba_m+oB~AQ%g~LA)9S7nOG(t7HL?+h6DnEjRS_gX$^0qMMt4#Z=KO&gaN^fY6GsP za8d`@7|{eWl@c*&Pv3?>=#|QM#5&_}qUe;d&8VIYu|PIK*y0+}Hd0a8zizx&+Zbcf zW9%r*NTwRpbUhM1M!-dVxF&n3(9;E7(pON28%fY4qwif%R!D5l8NCF ztU_m`P@NhZv5V7ohq1g5VuexzhEk*H%pq516mQbsExSTwp8D z+h`HYfUmKL-`fr@3GH^1fWDF1^H40w#6;*g=GS%oF{{Pd?8qZFo&_4|C2K| z?F`%29RF#Skt4bTZQFOPPYdsc2h-6LW1vT))ckiGJ#l~n5yzPCQM+*M)JW&5Hy8Vh zbMx?G{KN@DeSEuO3tnVC64ANBMI8CstDxY%O9eZlK;|5SIE%Xj{JJV9WZcRM6n&bN zb>-gLBD|>XETo&+@bpr-HnJ@7Wp}2v=ev5#;vKPjH*cu#88>g=i{69~et$)5qoM6Y zUDm4ehx-|Gk!4)s_@adqm#lm7i|3muop(31-Rpgz^vv%-POS=WS{ABa`)uIi=8VSL zisFN)HZFA8O=vH6D#yCiZ7{fb^z>}kUbOV2+X`=Ps{D-EF)^Y2P<&$z&{0+S&|ki= zf|H$n?YEl2&s@_R9Qvf5)bc0Q)wy0iHAnS%b}5e!$PTButL5Q^3a>O>?SRpnxZar_ z60n(7+4K28e$MUlvp0fU+X1h^U`tx9rb zc3gMZd`?+qwHI*Nf7`7VH=x+cR%#-_A7|KYSMxU6O0No!+Yf`n1< zNy(kYilCB=cE3@|uEXuUOV6#1zHxQYxxIGZH`wWW9ha;QJycpa=j?N(^Q%9<+Ouk9 zcj1|V`PJ;dP8H`uZAa~&T?^?|nLOg6#(PzZ7t{o*;_T+SOYE2zay?xhZ99!Ang`CH zZLRKgU}$B14?6Ydc!I@mecAQEIAwq5ruF&LHjSOsJ&^?Vd+k3o?u$s^oVD5Vahv0T zl^i}LKb`y#Jhu2Fa4Fwob?+kvq577&tihfw(&S$nXFY|^4ECuq9e-66weH?D?OIOoRKLQ@4Xb~A z+$=e{c<_^7;;h)pYad6$h0B= diff --git a/toxygen/smileys/default/D83CDF5E.png b/toxygen/smileys/default/D83CDF5E.png index 281ddda1950821d13c3047619ab7cbd33c91f77d..9a9950159e703e4fa43e1dae494c84a076110c0e 100644 GIT binary patch delta 1714 zcmV;j22J^v4cZNm8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0q;;uR7L;)|MSz4wO=B)VTnKX(+>dFU5s6(w0QRdM?0uFvEc~ z#f3K3pjXO&G{Ji@!hbWzi$2$-V8L}P!hbWsVI#(7D9dat&TcKtZY{}dE5v3fx?UmI zo@VIHn(^GW?tjjpBENO zrCHvWM&O-I(r#~+OPBC zzw+U|@Y=EMz9=X$p+n}bQRlE$)RaKr zv~lUSXMf_SN$0Ut<*!oEk2}+wP43X2@ZGlMt54jYN#eM5@7S#A(VyU=MB=MY)s#c) z&!6zzwCJ{I*p)!fi9i4U|D(~yh5!HnFmzH*Qvd(}0010)z32V->&cQleEPHwcZvJh z`qSo-vs8c_O&P#t<*OD3VcqIh0#etj7F5j+tA79h00Cl4M??UK1szBL000McNliru z<_8W6B?47k%>e)a0)2W^SaechcOY47~dl zd4iE-$vlS{@GVt2=K1LsGqWtiCL5>%Bgty1C7JU3=dbdESGQjDt|NL>pIrUsEljG5 zH-8VGebZHB8@JnSh(JEOQPM)2ty*o3Y6{!#bFm&vU}Mu*Y-{Ft!=WlRiRjQxs@|jP z65X-USAzK@mEV5%EtI-K1Vm##7>6#q;$@zZe7J$^#vS(BA+>c|TP?!iMx;oaY^t&% zo~n}qRgo`blbSZJA-h8=sTUb|?@uqXrhnOzh6_wW>gkq8dV(lm)5OgDl$w-OGQA## zyXt~;M&u=1z)H4^IDkCq%=;CI;JNMqh$u8rA2JdhX#s)y&ru)*lnR9qpOdK>48h{5 z_o;xe5g?XP?Zh5yL4s{Xso!Ew!0n?<1s@>WSESPsQ)L367jHORpH(|J*RaMuJAV;U zuDpNkoShg*4mN}CLNgz6EojB`PhPGP`*%`1Y`MaHx_t1n7^^n#LjwnQqYI73wq==Y zHp|Z5H^H;zG_jk|j_PLGP4IR@;Z3dW8WUs7LatnO^q`a!2}cZleU9V;`3e|5Ftm;- zCUyg&DZADZmFOKNkqZUIb^J|a*QVztnoFoSxKCos>)bJ*P zF5`KSl2_|B~_t3loa1vWY)S0002~Nklt3 zUESP0JiWYqeEr0L0^+Xzu79qs0f9lmA)#U6Kmmz}$S7CW=$P2J_=H4Bpnz0TK(ec= ze@bdvdPb%+P(UUtz%@JBH77SMFTX$*C?Ho@RP0((T2@|BSye3$az{T5qJKYcdh-lLuz-^C z%vr9p`{&G^r=kiLU{q7rn7?4*B26tu7Ki{NqqdIjVm*CEMxgfrX#zn^!q<;<0000b zbVXQnWMOn=I%9HWVRU5xGB7eSEigANF*H;$Fgi0eIy5;eFfckWFvxSH=KufzC3Hnt zbYx+4WjbwdWNBu3039+gGBPbNH!U$VR536*Gc`IiIV&(QIxsM~E*x8tAtwp|07*qo IM6N<$g2F31NB{r; literal 1686 zcmbVNYfuwc6b^`rQ7l?)6}4R!>HtczyO5A<36Sh2kYW%*IzT}sBnzyNY|JhuKv58x zF|>$+6?us0bm|+iidf&;D#iLnfeH>(Q0q7?j-pag=mx?1!|_M&?C!ni-t&Fud1q}= zkzp=ecP@v+aZ!fjYIb(Dzs?ib@25|Owd^#Dk;gGHR0?C!nhB0nPbCw8(x6Qx)Pz=_ zwY-@K;&8^LkeWCqPPGu#Q3jsYuEVn$Oe~ti2@0{Aw7PVH0g{PS(kKD@j-3JlQZE5x z1u95o!iY37JljmfWJhXr+37m59t;Tvf~+V@U?3PRU^QeIY1AqKM|e?oZQtgDz=#Ty zE&*RV6{m^T(gwpd95rZJ2O-;iCA^#)yJCfTBBGjK|mX&{V@5|@DN2~SVzQ3QsCxJV{c zNX3|(6=WDBR3Mm0fM5`!2o#KQybFto6_5fKU^s%~Ff2zzSfGgAllseWsT>!}$FNEx z&1j7}V$3ed+8xEp-it*sGofWDvxcHF#yTJ>jbbP|jWPjPj1cgSC5?K@Li^au^Ez6b zFq10?y~0cxfRX&79g-{Or2f~O92S>5`|C2L5>kQxC z9B(zt*cRJ?_T`(_XN5PHhcL1?#>`%gh+j|4(28$c z*de*yFtDez!uzA!i&d|(`%VoHrGDX~3F`~pzioD7j#qh8MN|TC7Y%Q4o#Hcda#DOx z{f*i8+DkSQC$2xYbj~g)sCfGF)}?ROm6oGl1+~$OkJOCoNJckq=;!(zyN>s_-Ffj- zHdq5)30`=h$iuq)RqH=rCNwUs={h!PADsjEj$fmrrWLPpERjLr_Pu$kHT_O24?T&k zWAbb7yGSidyS^)|Y6M#jwC{|j-EQSHo=NB|_8N2`3GSxy{J*j^w!%$vAMmmza1vV1 zk9J>Bx}MkYb2H-(TH-@8syHjBBD*$Ja;{ zng#BQ?x=9UxQ{M4eP34QI{Jl2a1?q;@I8W9m zCUsIw{tlllHj8TYgVhcA2TBnce$l~4Iz9A;a>h;LwLEfdZ}ri$Pll(baa6Wvq4Tc} zAC4fr#WAH56`zEkD|U3xdWpIAo>~^DdbqK2>f@tb0asQ8m3jJxIsdR5b4e_#RS#tP zK7a8?$>XLSQHuO$kl=GV@y`d|cUSf_mD{8q?L))Pt$nDuZR>*l1HlLbhP<=}`L6S5 zIhj|A{Nm7b<(jQI`6QFnv#o4!Sz+zf)8c`2LwioZHbeKT?t;Og-qST%5wS~(>8@5y sQB1*JKl*TA_kpwhdAsJ_E8&deo%0Rr-bP5=M^ diff --git a/toxygen/smileys/default/D83CDF5F.png b/toxygen/smileys/default/D83CDF5F.png index 0b4ca0418f6fb2102b068053c0c46247319a82ae..6b0d1cf55c53357f06da546fbd88f7388409faa1 100644 GIT binary patch literal 1743 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 diff --git a/toxygen/smileys/default/D83CDF60.png b/toxygen/smileys/default/D83CDF60.png index d25bedc83729621051d661073db694e83e1a86d4..27ab69ed348526ee5c2b0fb052df826f421384fb 100644 GIT binary patch literal 1715 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>X#eMf!tS{Oy`!A02!w{ zZGq%;SLe>e1Rx1Siz+H1B0x4KV{%p&P<&QiUPpW!&;iJRg^8)p(SDk%b5~eIua`Tr z6p$UFtkh_2F~!BH-_brrN9)5u-%rPazMcs9ax&oCrHIc*Jii{a`@G-y>mkcu*TO!Z z3Rs@!@_E1Yr#)JqcWZt<;q?7-*vs8M4NmrxLef6&HvDqH^!o+>Q&XJ#107}t#&66l z+S}9j<*4KLGrr$0g}&P7yFAA>Psd}DPt2O6?EP)suXlM|nB}~^&c4UrE>p*Gl7IM$ z_>Ao(RXe(RfZi!Fa7#v);ni8lRT$?nRyK55vU99-Jjft6sm4mP4 z-!}s;J`p+1xYIvg=Xy6W?Esp^nB?v5!qCAg>jC7jmw5WRvOi$Z}r zp80PWwQsr~VSM5m9=G0u<+~Z9Kw!oKyA&0zOH52(_q#U=1Zask)yVp+P2dpo->c8r z@|9s@PnJ;i8dlD2Z9(-%Y#6?m&lHMt*k-;YSTHz^X@R)EMb7zEvU9Q{66eq5@C?8A z|IM7a5*~bJbKg0++dZ0fq3f`H-4l)J!Si;VnV+xSfVJ@C-Li-%t_Wyq6RYF zP4!6vdmo%%ceT;$OIHU^DpTVz4#QJ3nEog({m5v+n_{+Plj__fQ~95K?X6+hrd;sL zKE)!rTwLV1g+=Sh^8MM~%as)$Pk43Mjv>mb1?We^mYZL*&K06}x^Yh=!#vQT`k~sg7~&udepF zWtJy_sq?X?i(`ny<>UkgCbcxNFtai~Hi-vMiV8}K${s#z{8aff=I$L^o4tE}D%LPM z%E#OPZ#b|ZL9s*Vp}_tN8yY@zoLG?|xS=7#Cnu)HDJiI^sB4CWhl7%-uC21MwzZ|D zdbp5^!qn;U0TCfFL9=4Q^mv6POkBHS&8k)FR{Hv_n>?AlQ$yp~)o@c2GgEc(Z%Pd)Ln0%gD?y`<$hge{|b^pm$YETq8l#m4QJX%X(0KfzXhfpOTqYiCcqw zr1*QF21$?&!TD(=<%vb942~)JNvR5+xryniL8*x;m4zo$fi(#OgQu&X%Q~loCIAY@ B*sA~l literal 1672 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztZ~Xd{s996(+*D;$B>F!IlH zb6w!1<4gPc6yrn`Kj_);JqTqz`lieHqmkg=pO2&8`297Cd|GrD37UsrKBm^_b$11gSmtY=75xel%pBcp}e*{n63) z_Vp>ZB26R>p7k2#Pj$BCpZD|NBj&Tu&KGCTs0nehn&Rkh(-5K}wd|AkZIyyK)*3$! zCCvT%Kv`0vQaoQy|M5?jvj^{WW~4KeZROhcY{ByE>mN?++^qX@&BqgU!C%fykombe zA?fG#zE*!ZF>%Rn-&!Ydut&vj{>1vmW67?~-uwG5zP>GX;7^ZYe}cTF~~$~IUX zw)sS9%#{-6mnpvtPCkfY2)}pMWZFZaxjtW6R8(q$HZKg@9;h+Bqq9-#DevkvnJ)rr z4ZXhS++t=j)SRQRe|C_>u4bc{UFW!X*EgiR0;R zn*PmuMeyV}Ma@}lLPw9?yOJsPvEYkOc1JH`?#A@T?tH3`jiie`?qnTRV`EbZY;fRS z8Pq6~^7ppHmfKff-n5o3nq((9E!R9hHB|f12HhuHAKeLSaMN3}S?J`{hc~}3o?7*{ z%qK0b-lOq_<%!v;PqNco&#b+Z@t0erF`hjkcVnTbUD5xS`|i~?uRKx^@|E|}S1InD lch)YC-)FZl?)7{IW(H4n6_3W|NiLwe!PC{xWt~$(69DkRd=dZv diff --git a/toxygen/smileys/default/D83CDF61.png b/toxygen/smileys/default/D83CDF61.png index f8a22802393b6194f5b5186bc091f4442b91cd2c..edb01f78df01254650c28dcd2488822e1d0c40c1 100644 GIT binary patch delta 1464 zcmZ`%c{J2(82)TyjmBOkQBz6g+_5ucYZNiEWg8t!#+K2ju{N5FC6Qs0BD-$OC2AyL za5J{SO;@1?V@YwvSmRi_=KlKgo^$Va-t)Zg^SXB}OQmrIWUe5bx8>U>_;XEAIWGwQ zwCunZ{5mJdn3_G+)Dm5}fPJ&#)ZQFjIWal;{rlSL>hj>=`_0X*A3t_@JnqKE`s&gg zmp2ku;WseQJ2A#hs0hmI#+M8_-|9nnSAns`&bH~>i~-~&mQeM_nBw#Iu!~&)cdtdt9lRHJ!CRUC#W3b-3P`@)_R0@pj0M) zkc=vKIed}+)?=u8BJy>O)DFVQ0aw-PvtpR3M#2B_8Fjyd9o|UY^f;4Ys%K=!un&;-F)O#b8YTLU#>xiRO zO$Boqlnflq{Zz{f+>D258eg`G9zR5%y)HKp-GFM3N^%G{y|Aryjv+>=Y`Ru-)@@&| z&htmp+Vy2cCDTu+c(%i9MwD=TZNbMA3Z02Op&S>V3dIwZ30q_1Px}XLEtyyV literal 1549 zcmbVMeQXnD7(c8|!^Z|XTr0?q4i?DRUGKKudX=tg?^>yBqv;k_h_GIJS9Wf_JFYj{ zO+GgFku5-`Awg7NOhp1yJ`>RZEm$WQ5-=KsVH}8z84aW3BTUv&uVY33ApGIw?%wx# z@A>_n?`yZ0zCLB*D-!_#m||N*I^=a~;&~xOerMi^Q}UvcEKaG6_euduWB?P*yBW~t zqgF8vhNA0Mf6pug0EL$=cS_EZV!em=K~zEq3i<>Y4FC&`L4opAGZN@#RxQa!-A=2dCA>LU3VFFBA9}i2{SZ8qTi|8q{OFdU>BX zhSlJhid1b-KXS@hVh0IcWI!#Hm+R3W2#8@2lCQ-uWC4h35ERzPH$R93Eda+a zwQNnKy?O^}9k(S*26dGr33?a~1OiYX58}mDFoNTFf&)c!WsO{a9Vb!2T+W}BWFQ&8 zM`Q(w;01{;wIIn``Xg=VdpD53x zXh}w7*D$nI@$fL5JTam?8k0}Ij|0Fn1{-NA4_X-I}qfXwBrn{+3;zHqXhLH0hj62 z?PAoZ$P)8s>DS!dytk_>+SysD+!-D@qls7LL`K7lAI9n7k>7rPyRYYB=+M@4efI`4 z4o@gPlijy2CJx>Fr9#yg8a;V1T9wiC`Jb67fwD^L;#XCsb-m~7istUP^zpRR!nLtC zL-)VczS}ia*$hR$?p{K#eQ-|~83

o1ZNT(QTHFmFbFIT|3h5`XBy94+b(Sx%60c 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 RZ>jIcu2t)Z{mMCu{{c@7RwV!c diff --git a/toxygen/smileys/default/D83CDF63.png b/toxygen/smileys/default/D83CDF63.png index 361eb8125a7b18fc2c09ce2d1c4462cc33f05795..9cb9907e3e3be9773d97621053a78465d6aebb39 100644 GIT binary patch delta 1595 zcmZ`%c{J2}6#uHc3XNV&_GT=DVw|5DOijo#V?q>$rbzZmWN8!=ei%K=m}h#HB;mA= zq9W@c9T`Km(#q5%Qc)pGP2u%R09b|qq@)AzLk6XM03Z$mU?KnjauEP((IrhD)&R&g z?%74BA_!8WrYMU>4OFuRszpP&Ndt9PecdmxMN_#6t@NvwO&k8j;a`1^o?4?iN>&TB z);Dibd1$O9#Ayx?4EhW-g+^K}ShOr2Y*HQAqCMp6a=)Rbn49(3!cYvy^g+58csb2j zbUOxJb*21b;a}%+GS0A*&t{*#T3lF`_h(^NdTwgs6CUyBF(d)u2EZ(qA~Z#P9KXc&I}tiMYLx75{KIaeTa z?%3wHJ7o?NnV#-mK=q_>?%T_AW?@r5gVL8f5;oA5KUfK#&de<=NuqDeFDAR$tVm+f8BEW> zli29ksIVgup)i)25FLt*kBkOjE4RS=z~f?rxz zPs`FQTq=X%6DyT2(WJtI_5O2J`Yd{x-_kgKR|SWpT$VKG9imk~9GMxAbYj#+F|sba z*0N%AqM*NnWtz%$3%*l%Zj_r+@xtJuBZ<$-t#XB#olXbWj_1+)mcIBJN1k3I5+*{* z2Trs_&B+#lj@|5WQ3K3P|{KfQ}9!bMi|yR#E3+!g557RLD}cVlvDxBK8e>#dwx zl5lQ*th+qA;w+EF8uKW{Dt@;xC+;XP#Boh;WsLBf%1eHi=HcJqe8aTuN}1!c-IVEj zUjQsJwiv(=Ht2-Pr7lQn>=X{8HFa%`yol;)or7PzT^%ezwrS^x#NDzX7>j)4BVvoDPAs`>3ZlL zh?s?E;P*E4^OTb}20tHP8o;)_Qg^uI0oO(3^@j03C8_B7mKs*9jir+YkEPXXX-0)d z))%ScIC1Z;JF-1vo#P*rPaZc^6YXtV9p7}&@r93H-E7DyiN`;gEkzp6Xc@Ra`TiY|`qp!&bftU2JB%ll!VG zHUufmKUjYyJL}nJ8=>q$1K5y`1Gx8N(h~~ zDKqg0@D5ldjipL literal 1608 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 literal 1773 zcmbVNX;2eq7>?Dk6w7d01v!?W1&2qtp@_#AlUwJ{L!7={l53x_j%rXHZMGE zm4m&9J&8nekV%n9Vs)}Tc5{ih&*-FrSln@O93F)w;He5DN)o8BEhr$m_kk%NPPKr>7b=ulh%m~}~d6Kv*_r+8svZ#$-ufhiSS z%O}5fDo!2_h%h4xu&4}*k`96Zn@t6oEH)cl0YG#RqS1+)O#zuOiw#2@VEQ5x){LqI zI1-Ue+ae@BIT6PVFpZX)no3P&P%)#L2J(138wUhY2n~uUO^++g6urrBh5fmAw8r?bU1rES6^(SO}|tF|dR z&4AJ(Q4^MIR1)<_@S6b>xqG)En@ilRtDFlDk1)#EaRPbR)lRT>oxfgl^< z3fU3?Pb4M;p@`0wKq4*+644=7}V1G05P6kPsnHV^#kr zXEeeYnyoqh(=0PbL$ z*K@r6&bHb&2vVSxj2;I6XMSH-SIiII3CylceDtNI=GNHIa(Bz4WouVk`tMGnoA&QX zjfnnr2U*Q=4?WfTv`A7M{6KxW&BZ6^S^IJ8JlaO(cJa3iOMSpkT@4?vImvJv*bUCF zkL0y@)%nGbcoi;aUZ0^BH;K!}9jw*medQfl!xQbCw#^>i7u3=AQ2~p`gHDGO>=7E8 zBZk?*2JNl@_@z1PxalReOH~(_(8mXY@PlQo_piMuk+q~9e%V+D zwEkF_k-#POyEmq2eWgM3TGpbud-po`1<&z=tn-@_?Ia_f?T*{)cQGwZONuymE&K;} zoY?YuOV5|F-RzO|bgqCjn6}WzvowI|ywL55p{3~)2K7nHr*MDI^9RzwjgFTyJMO^A z7aIOP7qRYQMfSt<6}l?Fpdr`eq*EUrbmi}>)HHVHr37$$pIPP=(t@}VL|#>uff*}zsfuXOgKEO*l%)qO*i{@t<{ zU%N6Vs+b$XBSO;c+Wq~dYnltP=mjV=`{nNMH`MyxHr9E+IIt?8k?yvU(tGy1c*}C1 zGdmNNf4r^RvpWNJ#SyO^7nIAYbH?GPmDSy=em|>b zQ{`y)9r}w(<=w-bSyyW^pN-{gYg^=eqN=gubjELat14VRTWwx_BZYVCv3=39j)S*- eeJ5f+pG6wkX$-D>*y(Kh0m;N+$VuVm?f(E-MX+N4 diff --git a/toxygen/smileys/default/D83CDF65.png b/toxygen/smileys/default/D83CDF65.png index a4809260ec8ef277329a6b5825eb4d0f1f47ec56..a8d746f0797bba85e550b3d8a3c4c9cb487a89a0 100644 GIT binary patch delta 1715 zcmZ`%c~Fzr9{o^wP*6k^g`zaRrzl03h{_TzDw0Y-5F!!KmLh@+s2~O{2qvvseOg)+ zMG&ZTL=muBK#Cf`P(mOC0wH0|LI`U%vcZ>yko&$)|LPxa=FFV)JNKMBbLakkTFG0P z??PG*LC`j@RYwuBJaZuY00hb&OAD27K>+0q74GgmfM%p@iKGK%6>3A!NTvVrkzU_IL(&VdUZvK`l`4bLxM(shnav2z!|;?u z3hVSs#wEa4g7+do5d)rb$?#V~r#Hen!=hmkFlWJQKKP9Xsz*Wf2%t}!3?{w7I5sg2 z>kVK`1?q)HozbMxgAe0ilB6}lM%ZAKDK#)sZkqy3333Xguvrc-DRiJ#2*esNyC73( z7LdR@5g1m0j}zdn&?H>|jblco9t_H*N{v=;1f4U0s{p_A0Y|P^>HuR#qcbSyVep0z zD|KeU92k@V{v4o;gAs)q)=QQ1B&|vhUhzQXFrZCoRJvKEmcbrma)!rehrDvp_K6gGDMa)W1 zMtyrB9fJJx9udOoO3^F$vb6!iW@sx%YOJ)P< z-u1~hDO=*6WAnrX_r~}^@BJxRdO78M_ce;(Vx9{;vfFE2;s+et>#l81O>f2SLh6?V zu|&U2{_04}lA`Af7IyGuU2x|4d9T(S@dIUx(d|L%Lz~Voo0Hw@e!NwAt#{JtUw@pK z>zlFnK9*S@RLW?Wb>AVxetR$w>uyn9PKqCPkE+f2;liy)iLK1N9N zL#vx#k$RVTloQTcF7k_?v^wl{lLfA}NwrAaS#v;MyB_!Z#j^{CSR2zx4gXawtIH)F z3~&l~v>lok|3v0JOT8^}OsT40vx(@mBokH9_Fqz+bMdYiYV)@Afb|R};Uw@*q4qn? z{b#c5DYiaU>-?!@?Cyghv0c8riu#loc=%@LXr;%1Wx(Be=<=!L6?~ zRY5ln3upm}eG35vc9UTu+>Z`4ln?3fDhC3-VKmC?g3Z3nB9|U~I3+CN3W4L7Ano)B z!QrwU&wSRYwKG87hZ2nYlE^PUtn6WL*uWH*ys2YLLbmx=9KZU&9IYXuTU{dGjn1BU zooJJF-QIEk?EY5^xD3&_Rj&slBT^$K{Yn=4zszm1t8NtbWT#P^6>)lz4e3hopPbD; zXMe4DbvZrQe37>*`quA*@WJxZnfP&YV*TR9olbqPWbUPCtc>NBUe6Oa2LZRHQ ze2lu|xp})-F(I4z%)xo>_RmS~EGlY~i{q#3+*Z3dZJb!`y0x8(@>=sWf41adspI`8 zh2EYWRMgY^#gEDgN*;d`8T+uRypviU{T2TBF?@Vf9O3Ju_?m8NIsQaU{5L}m!eK7I zzq*=EEq~2EHYnt>M+O8OUS*Zl+ZYNd$Uu9EEBLqjkz!1^^|taox%6&QT)pd=p~J<` zD$}5~=v_X)V6F;HS$6BZeZI@4bGvhDZ%sLR=BB{{Vb;*i8Tc literal 1678 zcmbVNX;2eq7!HRP5R_9Ct1bbm)R1Hsa&1kKWD|*qh7biUC?Q#3fz5{OLJ|-Q2q(x43)ea&EGGi@eu-FZP?GMKv-Pzsmd%u03=e=i3luH)6 zIQcj+7z`IhIHICgSL@?2k$%s9`9?!8(3 z=zIplE(ue|Qn8VXVJ(5PHC7$A88^^q24jAx*`U#;p%jpaCS!Ur^TmaFCV=V0%otuI zH_{+QQ?T%CBO0B(M6JzE(~5M=(9ePSW|$_xQAz`t@pL^2o5jpgUYOonk2y?WRE0_t zGv7KD8>s}Ogb@XJ?BF0R7X$%5pACla_waOs;L1ctyoJ`4$fv5QGtGwPCH z6(S$AMN4943Pl-U4##9Nu}#5j!kEkfMIw=v1A>BRjUdvZr!?juJ?S^jfS{z-h#4r1 z&;wRRO(Ky=iJ7#gZ$rQhk&*9+_2gKh=#+8H8UqJpb2&I}jcZh!q*Um?ZoF5UR9g%v zM}?9^rcq1RBgt5B(%xI+776HBq;kFt45p)yAj~nW?*HVBLp#H< zHphROW&DWlK(j!!;X(EE#2D$(DDCOE&0yGGRv=+&^P>aN5lf?^Tu1tc@0Z0K zJ>~hBqud{9PAx2SaXs_e(Zj>Ux<9+K>8wiNqlh~;iPuzQv#6?gf{#ZV!?y2W5KiQD z48n(zsaI|(J~=nId+72|QkS!LGdR-x)62;b6<=_8%gTFyxmC%EFbNIh&9XY7IIGSK zmkl58yt@)Resa#x`8nlreTsmoo-5o600&DA*I87%GDl5>d|8J-9lBW}>Dz1@zszZ< zRS9F=XB_Oh@>tauxSO|@P9%DZi~B-{pFjSsq+rUog05h@+1EG*I6Ea zwK1k$YxJ+YRu-ALCC8~!o?eN@Xmd9aXxvW>3M^G4!vkXy-R+0 z<=P2uUX7a?1Px+Mz`)nNm$v&`+&{k77Es`8H(61((#Fh@AmzU9pKkQ!yyU)d5t75B3qd2aO#EE_pp)%jqGYqXs&Ij{p#)^2$+x1#SX8E;#IPVJZw_|3DH z&qik5vrz%Z`|T{L{_9S!DU7mjCezlsva*_Ej|mc{9TIH6alOU8d*Mt+X~7=BzSKVo zTeo@+C>MDpgxLJ4ujH!Ct2)m+btNA<(d^bgbdS5KXW>cP*T67$Ik&G*cR13JRZAw!dN9lN z`7_7--s}jkm&zW+!Tv{8#{JvezT2X^lF|BuZdcw4Uf?a+-`(M{JNIO-N%G#A8#k{` z9lzt~>ecJXUo#$NXm8Z)Zf{?OCz}Q@qnX#!A8tr^(u$wLlS_8zF0c5iYsTZX-c5Hl otuvxU?q1$WhdcJRH`UuZGuncY_H&bW1X%wN3fU6mge0NhAFhjh8~^|S diff --git a/toxygen/smileys/default/D83CDF66.png b/toxygen/smileys/default/D83CDF66.png index 7d67e8e41d36e27ca383cddad7b0e6982d2a3851..7e6e8ab22368f74b20bd9de71751fc6422345cf4 100644 GIT binary patch delta 1485 zcmZ{ido;CLVz3wh%94M#^qyl&e)EttCKL;nUY zw0oY1C^Ay?+mm&k1uFa#1Kg2!Yh5{T-Ob4s{#@rKvy$_%^mAJlcc^-6tbw=CyEW0Y z+mF-#nz!(2huyzDN!#o#+4xwzM9*cnWPBa2;SN@EDFfx3{bjuQKcJ1p?a3BaZTv_H zd1qxDTAzp3=b$evh%>XZ(7oALy55n!(O)+BJh(d-v))1EkH&6~Wp0gSZ;xf|jND#h z5V~`%o0AQuUZ4KjTC#<62UbUjN)dEdv;U;N^VmKW^ zaI~`lal!}fVMmngfVHKF&WTEjYf6;QOo#v=Kn=&alU?!GR6@dneS8B*D&!j>B$exd zApn>qq@6pHK8zOQ59ovoDej}Ug%a4WRyHcyRwSIuFSHE@<;gpqjEhE0a(MHvIJX;u z>R6t3Uy3Yiw!1tsaPq;*mb`)zx@pjx;eFAOSM&1kn0izVQ4aLUz8#$>98Yhg6_gG3 zcN#S3=t|yYbz_Ui^4QX&q1`{++R{i{iQ=MCBkqW;wnlbKd2S{=3=ikvMaq><53*xE z^#=x4i9J@juNGMTf+0>Rb{5Ck)+S^9hN@$iBwx(b(;I88>gZX1j=G0k_Pi)mOqN8m@4%Pk8bk{b>LK#r!x8IQd3x9Wu1bqlk$!msG5X8Wv3g0$i&I zgnY8D#)(Mh0w1(c!hHQ{;g|$a_Qg+oGXp6dpy#?XcUd6lkgx7_1hZZ(Tk@%{AgIg0 zhurj8_B!8ufW2g6UsL{PRj3<5#f&A9h3vZCQk5f1>9xF3Bfd0_S^j-ioa@V>eIRrW z)&6`yp;`%h$v7R=ORV_OZ8J(N9TnV5K*=1(@9&HBKR+LzH?4)YJ1P=CVB>)a#oroi zoMb*g&ggmJcixy}?#Y3UhkWW@EP7SauUBbbWi0gT-C{VEVc! z2jNahYMh0V5sbwk`^2>=V-YL97`G}6V`1G0H zpT2=VX{mz^2B*;L?F9t2eD;eF8yN+qO^NzB#RV^O+Y$3&VZOd>*5X35u!KayJ*-n# zXH9OTGSX4cU&-TWgs5^HHSa;iuO*M({ZjZ)-G(}#B=S{7fNxnJ^(P`4LsR9uP`*FW z+@!9S_spAse=R1~`d+(5TU@+xS?<&+X9ujzNi&?radjg-57|}{QMRMKtK-PNi^P~c z_Okos9;TjZ6w-K}Pa<~A%*e^6QICNN1!bDPSRNi8T$60;PA1~XBn$5_5^R7W+RzY% z{tjhej59!67@1g@80(?YW)^5PN%DQoe-weiL|>m9|D%8fvS)7!-&fTZ2f)GB+2-X5 HuUmfs2rtpk literal 1521 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 X#=yE02AQT?;Q)BJdb^bG54`#}mT@$g literal 1614 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQgs4@=s|N%#)f2%rQm4 ztl_C2a*~08sm{~IF{I*F&t%VR5l4~Z@ulJVmyLzfd%O+0T?(C zh4&9s>X`UHK03O%L%@c^vf@H_w1Rl=BBALzX^P3|YLnlsTX!~m?iL9_U-9+3E7t$p zAO2N;mX3e#VZV9n_0?{5{oeJy^60C8;+Lm0!vF8Qt>(Y=TK&4U33m?cXerFn{=RL? z{@YsRa`Fq}7#GQRFJ5t*X+>_sX9@ll@+B2ZZ4<;#EnsCn`g7*f?~^C2Tkjxal(5$7 ziPfqpr;gT22-mHY`tde4vT4S{>5g3ji;ih&v~W&y*mbdD((5(M&DRgGXp~;(J=GYj z!L%`F!`+0>*}EOOMK!j_w-h9@J^HHlWzN1E$tJ;P`4o;d|9t+PeZ&3iTuY5Fjf-ad zTFT=f!!NEWvhLukH|`nNrY~5$Svcmr^qgmVCrduKdPTbS(~U$8(Pr7Z9@)xGFZqox z#Mxr-%Uo2`Y@@`{$3 zZQsCBIEmr$qdozl6^t%D{Y_J27-Aea94kM zOiW=$)!l7t8J#?$-w9q*+c_b*;*Pr$hi<`gH}8*AZnmVSUzO^9yfU{p`o;TqUY_1( z%%uWdABLXtS>pKY-vfo8`vuw_nr*+n_=j5A>Y1BfUy8ES=y|d=YwNGsy`!7ekI&Y+1ze%U?!m dKXV6D9OhE)jBB{kl*m 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 diff --git a/toxygen/smileys/default/D83CDF6A.png b/toxygen/smileys/default/D83CDF6A.png index a73950843d51048ba02c09bb80dc7a8c403f49d7..cd6c10ad1f4ad111a1cb14e4d66c4cacf382f884 100644 GIT binary patch delta 1985 zcmZ`&dpHwp8-GSjOT6iz9Euz&nudsZ6WU~$!)!%4Z>BNlg&a0gCWo|*42zaYVa`z- zMfDaT3pEUhlHT6V_125(oA3I*Ki~Vhp6kBv-*f+d&-Gl_@9tN7rG9=3LlFS56Rz$f zPpiTmv5o*V7p_?jQIhw1L>C+ufD~f@GEM`qD2Fl@062jF;Ll(HkX!&XV|gO)-vLnQ z@p8wbM>1?{Xvn$@+h6hkfKN892-IGw04T!%c~M{QQJ3K^N_Q3K`p(tUGJP8-#*CBmTE zf88bAnyYw)zUsu`=IoIM`kTQ^@5h90f8WA0}ESI?q38N_~EZz3`AXd7G8w zjf}G0G1|fTGTHu4+Bn(6kyORJ>dqT$rAu2grHzbKVHP3mpxJ}!tXI9f2{BtHVvV292%)n%{MK>q1pC8*>?4RXra^=>W`Lw7VYV&3{glULn$vI(>!Qc_A-!yZ zv@-bq8K<5!n|h+&y`){ExiqYXS%lY((ew2YT^aUI%Z?5fx;H1- z@x$zH(5!niYzLT5UGclvI1_(!V=i&;^U5Q=SvDga&$>vf1czPM$d*#J`xqPh_$;a| zb`LMev@6B>L8{?U;h`z6&x6wr<3--%9PDU5N>+rO;bLct(GO1=4HdYIoMSi-6`;pT zyv8{gX#wiq$=!0hWm)O6kgra8X@IODl4M(YlG*`{Z)gYm6 zhfs>^RNX%TzyMGh28)KyL)Bo0ND>+~Eng;j3f`B73rW(8jgKKys9}1vld)lX$x*QY z*c22K+*)`D)ybs|wx?L6J{{(k4`u|v`R}hceb!gS1YUyW8`gcLlz#fWRQRdjNJrq8 z27?rQrT>q4xO;8hKK07<5dulOLl%|eU@!I_W;m4#k468dvxQ_YW$ochPG_!<2hyh!A<3PrTv%rq0vhE=dSryLUJ)KcG`pAZR! zn?hm@a009~!Ec@C6HI1puVxDdt1Uf0rC&Unr`q|gM$>PEin*&~tpAnPZ?NBSLlY&G zL8iN6T}?^@)$azJa6}q>V#9-tjydZ%RM6&s5*=D|D=I>~q9%$}c)8W@S*DN4KnlU#1 zl>v4((kADMi!fY6=dW?w(#-eI7?#`O;Fm%d!vh*LmXdIG)SGwGMQNkq=7~|$ zOl7rq&YD^UZDHk9Yd7-y@|p4YvR-3}_msPM^>p;{&TH2Lh@pbrFCG%0&}xB;P3X*5 zTlSh4rooX{(8VMuEXJqhsYdaFu5lWyf-A`1PIRDgI}BnmBn=z}6aIVx`e~WrOjmr9 zw#w`mQ#G8FbTA=5YlOS}LEP|?W{B6p85Hb2Qe1P;hX)~~$d;Vp*E>sDK>IV=q3gsnZ-8$?}V z#k{OLZTInQ+=c$+t%ffb1-6Xxw>7hFiyuuTLa!4a4@Uguam8TiwqRKylA9Z;+_2ao zx2rY=<%r)r0{IQ{{^yE)6<}*r*Q%-iwhpGDxM~GhzhUDhtcT|2En3<-D}e4+PcLtr z51w#%o1T&e0Q%dH`1&0^<{uCk6l`D!1Ik7rp+r&`Is7<<5fMo>)&VFSl zP9!EJpWFpZQc}~>>5PobQ(33aWHU{{?wqr^dHDtB3eU6HoT6elFuPE~EiEhOU970Q z#J^m%2OtF1S8A?atF5aSHZ(Rhn*)oM*0$^I9XD=@#GPHYZd-!A-92~i-uvVJgWkS} z{R4wmzB@5`}#7-x;1{Y70w$2MhoV+5^?#6nyGG D`o4q` literal 1867 zcmbVNX;2eq7>@l zl!A_UfTH68)T$gR2-agPs1_IBfA{JWN-YyeSvWZ#k0ujP0;7r5_;QS{(4jGy5<}IAE6-y> z8f|E_HZ+_J51K1b;W`#-(P5c%1`1813H?k4R27Sn^yOHLRxe^atE*?wwQ3O~j1vR| z84ygP4O~rN^H&Fls#eFU_-clqFI{L7Py{-RMCm47oZcufi5UI70%~tLW;5vhDrBsP z@!6^Hpb$EO6BwPt@?oj~5TwH}3!KS;VbGfn0U*Q%s2gU2GX)%20CDL97lX1!sG|i+ zseHf|C5adsk~9d|?1Y2_R)P-;Ct}zjpU<~&KoFDCU>XzkBx+*njc$VsQp~6#v<6a( z>**FobU7YRiWroqpF_|Yf`Yyf>x~16qEf~-p$0a{0@ym8C9ZyLBdNsxb>pkr#?V9q z##UlRJf2Wd^@w&G1XH>Dwjqlm<&EHbLQ53|jg#W4cpauE6;cs{`odCc)dC0vVJS}n z%f);|MhOxGfaMUv<3I=i$+?_Cj;~>5hz|&XVzCsGN(^C^eP@}PCM$>d^;*u0naj5A=M8O#kvYu zdu7kQAzQ2B-CrfEUEi4>v_IxfpI&m+rX!*6LC^MAv+j1)#Q@o&;uPnl-jTiO5w&DV zb&|ulqf-j4k**BM`P!Vnyv@;()s^>CE)_hkrCZ-!Yt^3TLF72~jw?OitJsPz+CDyY zw>`&P*19>lC-Wx0_Wf;}Nj6v3cy;!L`On@HQ$N?mmA|!RY{Rg$YV#cj#5$JyEsK7IiuwS-SXtmtWwU+$}16jj|~ut*7dG& z5?*W!S@8QFt2~{3bD!<;hUL)84pmr5(v#ANht^GE`OZO>?VTZ^%pZ>2!aN-&JMn4~-d8Tl9*0bIA}Z38>l}7v)8bQ}WTiAp#x$*%aCA!ZullOu9Fa#Q6V4QkEdw7dmYQ;TRjTQ`4decmh|x}?rg zbI--A=NK?AcVSKH;-zlPV8xlzA*m|#k^8D=7s9?2d}}Qn>&=_8A~nkQw;jm_ z=j@!z;G{Rl%45a8kuH%LEiGn{yIqC&Iz81pRt0V%pj1rr++^ zC$*I?Jjua>xg#GC_uE6RRoLg=z2bnDMFIX1XV;7K-&#u>s-hka-_qlU8vWaf4*dEIm3_?_xArq+6V-+hl|@}4L676g3oIQwx< a$`BfUy`VmCynt!>y(whD(nFG{w0{7-mD}+E diff --git a/toxygen/smileys/default/D83CDF6B.png b/toxygen/smileys/default/D83CDF6B.png index b16a6e04ecb9ecbf3e84ff35701a1316ac3ed6ad..73ad91cafc1c98cd33a17aae6372976fc8661f8d 100644 GIT binary patch literal 1680 zcmZ{l2~g8V6o;2IApr$JE68cMX$hLpa2QYyITB1bLLhj6iAkU!N1#yz5d$KJAOb3f zSP^Og4Tb`Oij0L6m8dAxhzJSz|TiR zU0)plpg|&fQX#LkVyLg6+S+za5ai&I?i6%%_34M0Hjy~z|REW6V%0@ z2H*e|fY%HF2xS21a?iH~x&WYZGk{Fx#a>UI*MskL@8cjGwHl6ao zL!9t6krFmR4B#yVLgD55`d(XO2&gTns=9BX`xl~(T7LtbI?5eb|`bxA6+h;mCLhgYawa;(y|#32PJ!_GV6$-v-62WQYR2J zqt`b^?vr~sD*Zi`d#H+xSMag4~>HJo^B{zpJ+qWpaoRvYO#Y6F@1%e0G zhKmH8Vk>^y{+m$@dht6ODaQ1Lg~8iz%?8c&CC2MoWowNf&E7n!A9NA0GUCT&y@k02 z#m9TDbMsb2#Vae~9AwLrR>VoGUqfaG^mVRJ4(MA!rULs)6{U`w8Z<|&UZ;n)vJLF< z-E6iNw?>Zvt*EkZhmR-7K^PIC0Ldd!ypWSfO?3;6JLA2D0KoVuv``+EnS|yh#z#cO zaL~MDE(d)mmJ5K>$+N+MO+#4J^3>N`GghN6jXM>6cvhaauoT8Eo|;-}X^@l6S9OV+ zif0xd?|pBUMlleF)i`c$EgR^$Vx!9vO!mi~?riTFdgD;uI80&v+(t8^@(u;2$2a>8 zrXGl?9}~m{YnZ`KFxEAbpWowj-7hZBj5%GgV9*_B@2HG|dCS0h{}E0x#q44H^l~ADqlhP&$qk#qB2u zxe1$Uvp2rNHSzs9!7V{iMexJ(QBD`aoZ-69^a;+|9`wFnHI03FUn48^qug%0i9> zm-uSws$y)MTp|F6|8CVhqxvVL?}YdL6QF_TrW}Lk+cyH%3kFj|>$|R=+X|H{8=1 zjuVx3`%uLP9s;gOWcqhz5)R-WbtKo7b--oBUGXop*F`=HDu3x%V-(KBxrOi8B1w-9UAL$*Cy!ie1!5WlK05xuI zdKQ)uw>~(BC6U;tq#Qo{8Q(H0d)hx!i4CN$>E0)C#NzRG4mkS?b{sRRqnp8i9#AZ6 zf8^Zg^u+AgeH0k#!f6m`ts{^)3!YafkIm$92&_a7<|Uz6k(GUVffJ4@SnHFMQDb literal 1690 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 diff --git a/toxygen/smileys/default/D83CDF6D.png b/toxygen/smileys/default/D83CDF6D.png index 622f296172189c72ca86a0472bcfaf161349b6ba..90a201acf9704bc0358a28f514dacf49b4957831 100644 GIT binary patch literal 1584 zcmaKsc{J2(7{^}|W6LmzLN(T;Xb_?)hM922KBG7#GZ@sx491pdWLNgJ!stp1DOzqW zqeWyw24ki$jiszDMBTDo_t!t2bN{&aJ)igep65O9_dMr)-silTZZ7t6GHNma069ko zoCl-|Kd00tXw^5ACqg0~gmuOO(3mT`Mv;JGl)ruF$)K?`zu**+( zl@a+xhah%2ip{$Kh)GrU+)KNgYh52yZPGhMOgk?uT6Rl z-L#;8suQ^`DDFGb$r=WQlc0IXXViXRh66z2$SN012Te3^ZF6|(Aa3@0e;8lu{bCOQ zjHyAb$RdRtPcpP~`N#57XP9K)vYtA3 zc@zbwb$`_zMTtqsB9#>75u0W9&yDS=Nc-6mVtxdB9LSK^?f?an3`b|Y!0#AidQH(e`6%`vw2jD<CU&Nyk0Hld>W$3*-Wt*5JO=L)JjAGIPp!tv}*W*jwv8w5sVPU^`zBIDJjI zgkP+GVRg({Elg2o0PV)^@{sOGl^&5na4S!sd|}m}rT+r8?r&vQrH(0?9U5!L*)idD zc)ZyRMpd~_5}}u(h*U3_|Dj$k zChrZ;XG*s}H4;_N2G0a5m=f$(sCQ?z7hQRYH!W69J*$}eBe3rtb5O8UG=}qf2Ee+@I%h+6 zYrs3`TYK3&wb+Z%GBFs|(mSfK7p1H^pPv^@$uBC`5?4`haVBs%Jzc#oUn#+5Gcu-B zW0h65lon-W+G%KMdLKRM?(J#lscpCknO|IZ%q_2!NUBZEv`I2SAdlz(MT(odC-HUP z;J|=DAn51wECA4q*ZuY|*cb)fMwfy2VgyhaR4e}oDkQ)JZDL}GwlFl_PclYZnVMPc zH{XLsTcOd14-O*!WeBAOoC=KlZ-Z-K$UVrg?PmreEszmIiJ$^(0L_nzatx&eQ9Y=X VfVl9NROlNAz!C3)yJJf}{}0dcqP1;7PwoN+fEY^b3$wWgFXa;R+5@@S3hj<&? zoFbx=c?ak8$e7!3$^kgOB@#)vIR&?gV`Sh74H4h5_nwmC5F-$FA&t!uyd+0U^c0&-17;VQOWSCY z^3Od%Crc!Ka+q|xV7E-c9IQ)525e+Lmq$cPB*_zf9@3FV3qUrV%eZx5M|~p*Fq96= zKr9N2$4EPwlzAMTHZL{ZF)z=drND_3fMg#g61ZrA1bnW1H;?&rU^g!&&I8+W5a>1$ z@^s)cr|cFhU}QNOKx9g(Ljgemipn4rf}+qk09HV-Tp`}56jEUbiot52=K@7Fj>^Go zxT!}=T@2I~8MX0SHw_$A}O&MZ9Uo#@j;iH03Oeo9WW)378aX?5w= zy1GG?Jpb*|OYW=|X;^{juaXvB%jJ%fZTsfmiTX47wJi?{EmfAlnxr#VHP;$16kl&j^MQV+cf;Xkt2PPQI`XtU@2jSJRiP`c#750{ ze|pUi?HGA3VpU3ZL`6Oa>X6ui-UE7ph<;hT1y>izP>sD3q5Ov&~5eMs& z)Sr(r9h$mkvmIHk+?iMF??mF~Q+IlokAt#it)oNYch{=R583wiI@`9d(}}&3(R!V_ zd1}wwZ`@Z#?n&&eRnI7wh95p>i#YZur|-7hx$UJxo|yZAvE?Ts<3_IyeYI>>C1_uH zchs2H1FD8EcRPLdC00w!7^Bexl{KSEUIf$Zs!v0b<-XEP2%H?p~>qX z0!2(>%82;UtD(+&Ns6k*VxaBl;f{m-_eQll6K*`2TwQ%~&+zQ^Nwcm{1x*7(k;3Ar zi1guGmez9npH_If9xtw`G#+(qD4f=CX85{MkEETMItIS+5woAIxwfg_!m{OeH~o6) z`?(d12Tv#gMpBV*So{5V&qpVYED9;he7v!?w0h&$s>aIg#BWo3g-E0gfAe`cw-teZ Nx|vADYYcN1{R5>pQ{n&s diff --git a/toxygen/smileys/default/D83CDF6E.png b/toxygen/smileys/default/D83CDF6E.png index c534a4b426cad27040d8cb1b014aa6e1af6c4b6e..f3a454c092cd0ae6273be484c6f3c9abe135a16f 100644 GIT binary patch literal 1748 zcmb7FYc$kp7=PztNQqXlF{3F%Ss6ze*fK?!3_E`_5ZA#2Gcgw&R0 zY74cplP%=FjWEo*tz?+gx-2a#)PMiZe%cTFVbA%!zvq44=lwm;InVo?^QOBp9Mn{G zQ~>~LbVoZTY&ABP3JT_Zrt0pnK?hTvsQ}b`yLp|h1pD|vN2W6X7YqSNN&#RMK1!Mf z;5-q4_W=M{76PEnD-pYq0YLobdV*ySL6EsJd<(ejLc<8RG060%|Fb`5f{JxEulX+f zXOR$Q7+sAuFb_zYnx9m@sIPq0S`XvL`=w8-Z^iQ1sqtY*B(nJ1q|%&AC?pcjiAJLn zqJp}b9(Of9isPMDLZde-0idI;8SKw0$V_Z_bXU?+pMN!xDmZ!Du8~~ zfclR?eeO`7Kh*0D^}9mDuF&W{XFcTN+*CB{c#ttD3j{BV!BQK5MgSxO3r*nlphPhy z3$Z1+=zvfnNcak**a9IHTqJ|>k)99pisujSB=|Di_HMV@ij4PhmXAs2-%l(pyjxkC zgYzzw`sC};R`s%83fC_Wk^^>%JeeIpR_$t1wO-IR8w7T~2&?e)+ zC-UX>$%Y8CEtp-_o@aQWKKA=nl(h&Zhz2d%r;Q}1ogC~yni9?tc9aElXM1IZ@>W%X zMTV2XA^^yw3oK6olO2QS@gsuHoa5pJaXc=#W(pLPPL;%mC>jSEER+9izMrZ zUKg1}tsjZC&T6Oo%@}#p(o(|-(pU2~)80PLx}CliM-SE&4oXDE+JW~JvasBy2Fb{* zc@cZQf?ajMo<6e}skFOt5RI^oWtCu*UD3QEl-;5o{pp(98<9<6XAqcL%*@M+GcyI7s8S)d)=_{K`Bw4 z7Y0*$GIrX;{$w=s+J3fLaCvRZ$MN6+*B|C@U=VXUyIVpZhjrZXHM6?tpVCqMbia7h z?7j*k=baJmDi3d;zv(6m@ftLsseRRvXP9N0`pi0*e<@yROQ-=Et+K=9z?Tlcp1qA! ziX)(Xw9@18o+~J^{>>?s6X{sB{k_+;S_af7KhqLXPBus>v|Ha(5Odi=NB4W$f^?2F zEg)>iG7py$91Iv&j4V*9PsG1#JsM0I_1E2m7vSp@4AS@;lsu%9KRr3{*%pC2mXuxd z+=-Wc*4$B#Oy;zgygl_8xrdE53h=f~?8(PsbDr;8t9dTB@C7h^BPt?>{=ctp6KzYUbsSU#?y=SMTd(5yfARR59IQ8`^W zXpv}bh(R>1q_o_b%**S4{AcC~Dgtxs{!Gbe!@5wo-h6xFnh6_|(^tEg(It9cJS<*a zOl7)ZYT7Ck1+u#3tZrF>qJkEQNq;a`HumiECe6J|OUr^7I?0jlQbsyI{O8WY6|YJx zxwV-l$t4ZKiTguQitf_G7YnY{W?)gdqGEAsML}I%LDqw^X9!J{X6~(=n)IroqMt6` z%{S9@OiJ%;lXd$^x@E(N4h%4p+tiyK?`noSU?8yf6mZxAu4N#f3mZTpl1Qe+qo#+B zunrL|51UyYF*7C-Er~>RX?O0w1Yr@JGeL3xElA9YJ_rkTZd7oO2ol7w`CLHdM4aa0 c>0#_(E|bgV#6@;;;jab&x;?}0fo*{BZ;l=FegFUf literal 1741 zcmbVNX;2eq7*04^#8RS;RgTs)9)OTzHzXvH5Rz=70iqlMK~xCIhHN3(kSru3M^RC* z7M-D05CNgKh@w;xLD0$}f(1v#vsSCZcu;J)+7W*M-5}WhaQxAo-Tl7z+xL0idp19G z?P@1SS4RSY;3QiEh2ty9`Z#=mzn6?nDe=VxlSE??s0K4DjW9u|MiXGNOs7nQ!(pX* zOUf14pFptHAc|-#S{}kvp*otvh!krUuVM9-%THD9EV5jCH( zo+W3<4Psb}tVuV*5$S6cs`MlkS4|NFko_$@oInR-O0q?ltT*v2e9AO058qpl=@jy` z3YNsDym2a89!eIYMwrZ^F{vsB0Fc>i8sN)fvjHD6$N)e(1Hai+z?a8j^FTlH%tgVi z8PytII3%61g-d*j7Q+lYI^AqG)67g7YD}a9TrStj0fJOqgKFBM$CMVT-n4X<0fJ2` zBVxc1R8O`tDihFDj8DNmeG`JtAeX--)|+M$g{O>eQ5xs~jX~GxtZ_|io3L>BUpL;V zZBlG8!1Qp~gr*u*cs(>rXTf;x-fzgNh> z^0JoaO|%efMABfj)QIZH)A{8g@37ASxD1g*%;1P2&>A<_50r4lQnmzO`T?K_qRe2` z|0id3+!?yHIsVfuvqyLbTDNao9~a&Z53I*0#)ywbSzt*QfnYNsgM#-6+OnR_3{f~-{?g^9aZttD(X>q*=En26+C;c9sJGxCbMJW{^RHy z_SF}${-nj)*urvWNCuut>Ci<FOhXYs4jW2F;KN?X-Ok^?;+w<+rr}^K0X{3@45`X<}LHyl^?Rh3kQ}YGb`*^5* zc~nx{FK)TJ4i}6PZYmm<gE>Qdb;M>@2?9}_n$OB zXsp}2byHS}_r3c3?mN|u^~NZIKu}ibRWssV7Mn4mD0S}M>1g(upP%6P@eq|6DHzH4 z6X`#c=IsaYTI6z$-LNU3v$puyXIu9^8}jm)Hx$(RDr0IM3WzC-*7q4@M?g3!N0jZ~F(f Cex3yY diff --git a/toxygen/smileys/default/D83CDF6F.png b/toxygen/smileys/default/D83CDF6F.png index 3f03181e7585bcca9c391f2406c399710377972b..f64f24e6b7335130dc941dc74bc5961c9ecda0d6 100644 GIT binary patch literal 1882 zcma)7dpOitAOFrU4MU|;SzC-QSl4kKmvX<%sBz0`SYa@x-iR`}6(g5jmlZ`Mx2?xY z6lqs6vspxL5ix2=NE(;vP1dfga^7Fh`~LI(@jlP{ea<cN_HslB4JWH&Wkcr(qIA`nQp11Vv047b5gYGoZDvZ1 zL>Xi(HJV8t%TaF+gRT%5xS-T=3OYidKT%;UL3XObdGQj=G@TM>!K=F9Q63BzxwLSg zovXxWfq;V-uyJicFp#uU5DHC!zz@Y=_r~2nhNTEtL;v$sDx4cPtvD&D;Ivl9AkI~p)TgW716CMlE?@?=DT@F5l!(qJkD#*;u? zAccC&=SsGrVOumBgM_6k506iCU?mw)8K66WnygWK6nf%~o~5BqYa~uW?f>}H;EaCN zSih;a{*y8)#i3&Od=>sI+`f8a&*ukwQIFetvpbqhMZe%syDl1ZMM7UB{0@!yA)z07 z=7R>U(11G{3q|4rR0pWX1UzYK>r(I8HM*+iJ-ftUyLKfoU) zyH{=-^mE`<6imm#%z1d50B?C9`EkSY^?k4N)H*{!7!G4+VDcioE;#Yu$zWCL@u4-NPJ7& zMA|Rgc2O|{Q+0g27nAGBiXlgyi3mLr#wK&)BH85F@JIl&>?_~W@0C#G#}^dMlLwDq zw5P@vR_jE4I*_3v!?>(k)kf})S1yzP{AoRm+zXmQG8a*fX3j?_$MJTVNJR$rOD=MWRNY|?uE zb-_g9itfI%`7-HF2_d`gNiqxL9G9Y{M7lAvCyCSrzVF`tNvC|;kJK&xTio*KNm^Yy z@6o+JwJ0)m@7B8)v@pAPvP?>qxq|sRQ8fyDAiP#dj@N;~i5OXz({z!Cei>4ehPFPvF%4{N4G2!hn`>azd?3UzLJRS$w$+;p+=+ zpbjsq9gUL7aXwMo?81G62%!B z+DS|02z%W2yXXlZ#}6{_ZX}ZH>EH_4J0QAx>4^zP9sO3hV(Jo(nbWe&>xwLpm>N4rMe5|KCKg_C zb+&t?X3|5OzcVuzi05Pr-X$E;{XSRxoQzA|l-6)u`EA~sII?WxOJd@15H~j zxc<<4Tyi{aWNI*a_D1t%(qcss-^P@^=22VSx@9#h+9&Q)ywP{t=+HC{$)8umCSm{8`B+_Nbz(BTQ8-d;6ThmS?pl(A~_jaO9Y>($rP(bD@ySG%rCQHqp)^xNmdBSNaNsmX(1U4XD}Y}C?1z&{ti_P562 zd_0NfmRWRwAYJKNY-PVelWZ9^A~BB=_Z3_6TH74Y*x*(>4i)Hq*BsQbbfw0-&I3rA zT6c~;NPP5|QEVC^s=kd^4D=kopU~NVOc4EEByh`?-S<;%J!)yS{~>4B!OB~Ntpg*2 z!XYZf*x1n2z{H41VlsVw#$JqyEX^#;1&xLPt{;yK8r9X`!oKN#t|OBh!s4lnt*IQ~rw(9uaaPH12;BVp3?<7~!igCme|g o<;Jkiu)#JYB8W|ohWsP z;@v>hIR`~LF%e7^6C zJw7gqJ!`=%007vkXk5*#?#^e1EAw75^0$Urd}&20oj_*M`5KAK_Dvh<0mbZYD>OiLPh8mA7=W;8Qs00;}WnKfDiL4%n@w%#O#UR-R3K)p^1 zrHEn$v1U1uqmM46h=jtpL~Wr#E73vWE5R@u#t;|@S_9gQxh5-SlR^``7_)aC^C56T zg*Hf`H%_I-#)EQ_B0v#O$khs97(`JX93nzdI2c3(Fv1rwH_C-WFcFF&VsP?;7;BU+ z3sd9DNn4C0g>q=xjPd#T`T4wjA&;c8`LIMHadIFCm(k!_3rw`e#x+^}rxh(lYhP@}B}TxqH7Mry}DGwwls2MbYHqq&Ck;m}nI)g_tiqonD6_FpT1% zGE^BUkt-NMCKsSeL>?+axjl%hVCmfzIvQ)@Ovb!$X*uiJ_R$xZd>;0sxoCDm*gL)~i#;tnysrCH+eH z+3RZVsN6A6TS2%*Q7j?mBxslwK_~4r{jG%jp;+lR~K_UYNmN` zJR+c3#g=uwZa>6@3r8*q&+WZdd9O-LK9`Q&8*v*rQv1!IU-Z_xGe5`4IdrGz%kwb@ zgb{5G>beufxmztOF8}$(rB4@SHdds!wRm4tUJGycF4^Z7*3&aGdf#Pf>vx$J)$VnV zO4_95G2@HN7ml~x4qmMqcWkMvPd>Y_=y)N{wSU;%7gPoio;fcZ4Ku(Hml3)7aF1tz z?ab`{uJwanpV*!|7WKfz5dz?NH|tCfaKI3P&mHERmPm_FaarHWZLnn@`-tBY?}bIL z!%cTAD+X)6B*1|B4z~A(q==gjU5QQ8JC6p*d|cg6o~sSoc0m+Ax-`l$Ru%AgEb76z zv`*Qh(EbNeZ51^8DX>wzxWPpoIQ-P#+#hzj;YxgE`Aw*+Fpr}Xmh5cWb)~MQzre?~ zj9t+OAX>p**sEY#4Y@~`VQ#oxRO!33?5w+w*T(g%Q*%UDd}@O&UOC4aGd~v1UsQV4 zaR2Mbb;F$LtQcJNn>5IK>xsOgS7>r3w(pJ^?`9wLdv$(`CL_&1J+PWJooGRKv=lb9 zAN*h@JE5BQeVm79?z~N@n{VBzx$d^UYk%6Iqm8WnfB0JmUcOe@^^ju?H0pADd`0Bu zf&9{5kKtZ^ZdvoLiWKFq{(|zoSutUkvBPsNRvvI4!qzqw+!}I;9kg>m*OvX=I^TO+ zHfDE>?J*p(2c|vQj{M^2Ds`>uIMzStR?@PpQ^z}oQt@{aR{yo3=2H6&YCC&@YjgVJ zp;1qk{U@v*F@IDGm$8$!H{^Wodo#NU>x(Xhmq!l#eqx}bi8qpEaKF5Arp0BrZ$Z_M zK6Dfn&=R;iuxI#u>4jBoz9+eNf#9X4_H9W^Tss#oi>MTFc%v+1$3KTXf`$M9 diff --git a/toxygen/smileys/default/D83CDF70.png b/toxygen/smileys/default/D83CDF70.png index f930ce7a6d3a7911033c29e2dc0c44bf4661808b..4101f4009facae0704e7c061723217c9dfe2e32b 100644 GIT binary patch literal 1652 zcmZ`)dpOg382_RUnKac##c3(!5=~P{Iqq|p+`6D_j1;*v6?)R?FxR;xw?v6@nJARg z(lgtvZEjIU2*WbhY*|R1c7C1CKj(Qm&+~rY&*y!g=kvVZ%jbE%$*wN8YZW&u0sz+9 z+gZCo2>-qmt*oT(?jj7P zLswVwX=zkA^J#Ip#>}i(MWs$puLFmh>v%OUUJ%aC4~$MQI(b8aN#V=~!SozoI6XKf zTkQ2A@xlF!;=zAzCK?W#L#`si2GUo>wK{kKMnw5Cj2S|rWQ8mNz#7UW3DDblzYX=LzM{}Z1*Y$e$JBIBL2 zIvXT52WIqD=WGo{I0tczzc@QqOt~SVSM*jyP3pqM)!Fm?&0W<|9p#}jW9-R+=YrR@ z{MNETM&4U$T2KA&&|L}psiWOB!<>?S+D+cm#ID-d_VO^PREk7Q@u4?9Ky-6~>_9V% z%UFr{Wr<;B#%r-vz4whRdv;#j&uMPv^vyT_i=wVanPIMmD*xaSLlt;_GvpQ^gLkyG zz8M$ffPe~lqP???{Di!+qL!Sd_g{Gc$i~Ka94ETrE}_CB!vcdt2qp`@_72!tH>q-h`cbADVKG^&k`h*PB@18HtsrR+$O3~m zg{t08vw^&v)W@C|W}b5Wh9~AO7Pe-?ZTcUcS9lMra3m`g zb$qVQ7S(65*}THhyJS$G;5a*XzR+<}8xAKAq!|YcA@(e!3FdyO^S<5Hx5nVfR7HaE z^}=G!%BEzI`C%KROcm-h2YIyeF=(m97cAaL?)aUUI9LpO!YksPXec6Vwy!8;!jEv~nr6wjdT!XKSl$tAOBNXYq z*_Fy9KXK2XB`U-uiCl3JpYcTDdeq&Xt%ke!iv}BTa&@7oB2vD?4iIC=9KGx{$M^9$ zC+SayQ$KzS5_Tu23;Zz5BF72l&Uh~;DZR}TFLgPacwg^bd7Hc$1W7aGFT3LX-L0Mlb*BoL0jQ8Sv2}?`xjcNx<WAY*F_0=_XaBI?4DNb+$} zj0l9QAQl$ZDJZSa%e;9bH!H_K^O&oLo7YiKnDUynu#BWMd{TNcIrWI0lLP768PY~= zy?y$o+cA2F61S-`IBk2hck3GH==%dqt7Dt4E~V<+*_r80o6XZ+Qg>E#29l84lqU~g zl-5(8CzO;o*2Xbf?6Z0MjGi<^5kIL()+&+HoSizAvc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYACkt0MQwtYE0~aNz-V}1eOdseNeNf^+N-QuTUb&O0kYHmnXf z@a|(F&%Bwg0&M{)eK~IPMFLCqCt9ADC|y|FH8W?~_n>wD{=pnBGRh)MjtUQcI$kcG zJL~nWuP36*m!5DGFE;7jx$BTkv+)sbhdJeMkIl~dY|Lo3>tO~Lqg0femf-Tzb>EC4 znO_G^oFJ-Fp)Ya%(9bVYH6~G7o>`OBOjUMqObYcVn&r^6lS{4i-4+h!4+bA5-mBlC ze56TW)8oe5Dn%#D4o1m2l+HhTVCHjY!?(GN7J3`-0yXHT{YL2%$a)s%e*g%!P;}864&Y6yTi4mw)FCA>%TYTnRpn!MT-1>eW=17 PR6u*W`njxgN@xNA%m_sQ diff --git a/toxygen/smileys/default/D83CDF71.png b/toxygen/smileys/default/D83CDF71.png index 0db1d71108715d7c23aa408d1b2dd00fde6cf76e..dce933803b753b27d2c29392d6a589af1a68695d 100644 GIT binary patch delta 1375 zcmZuveKga182=*X&JflMr8}oAFKun%njvqSNJD#xU1Z+h-)3cAjtSim@)CZ`sn&~1 zlvKDlH)>9lQ1qr;adA?JW^BXU-TU7?=brPN=jC&r&vQQKbDqx{1sWy#JQVG87FfDozp1pwQz?e;&;* zkmh&N!P&*pg{paYZBu4fXQMaCiDx?KzU;LSr(=YJ=XEGY5)k z6W1aJl3gZlM<>|W<(QcE1>whPz29B*f0BJ<{9*iGcVcF9(4#lQmyHn1D8%WE%()*c za)rRn%|)?fMIu`et)5EHtl?e0`h8gNRMXR%Oa}n|Eq_oV)#AH`@52D>+b8~ZBdk0q! z3MW23PvO`$4%PD?bHUTy^r9ZMRf)O{k$p!Zc`DRxK066`3h#nFj*vi}s-c@=3&wG$ zkYO`0E%-iLJ!g&rQqD-D`*Ns3DX2ttLU>ec7>bjc7>44;B?3Sws$h6j*5VMKz9DV0 zOGEp1pHu66k>bU?1PkQS8O1g7j*3j)HtNndh=XNyYluZk#dgN4P89k>` z6i`Ba8UKsnDQ=4)aj^A@vR#ms0I?@RFQ`0RbD(!TVXfO*hMnMHGtw_je+q>CGx`bc!`v|=s{a{OgAVLj9Sh7$j@Y|ra z_TM}BT!@=yqk&UTTjPF^%2I2f>S{;|Q^#*n6J)V|D;dJ=+q&nBDXP#6l|7LTK(pEB znn$LF3hBlivM(nzh!aK(VTUOSID|WN=qLtfjlo;e@i?M|717e#9ET&~aL}>w_Wv;A f5<;WGQ~zcBFJV?ELao$jKO6vevM1@TGb{UVZCPbL literal 1504 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#mR0d>Fj3aYG`b3XyoLg1l5~DPMGNf9itCQ97u@;CIn1@ASOKN13B=_lbQ$2F-5?v zQT!-wDgy(PfTxRNNX4xs!RNDu9Yy}7Z917J6dY_WxMitIUVye%RNyYNN1g!-A3074 z;}1G0U-9{YFN@$8B@xH2q}9w|B>%hgoolx#k)43D>fxSWWhon)m$eiLc+H9(|KNA{4k((JM6nVV9FkU9sXy z-|AEH-4Z27)HYpTH*5dtsdj!$yDZh)1)ggJJM^AAr{S%XresvYvGH+xDU++;G2b)S zYgxXO8AZBxpKle&^G}nVb>*Lz(#f{|a}^?v7n~9wO*Ly3y~y$JoM*%p$16OC3uQFc z9jweKcz(@y!=WouTUW0Nw_SPt-5Gn1RV!|Vub6nWGiDuU&ize-b3QvNr`~=x_4VS@ zRmWbI+%oBk-@hPiZPK)NWttxAycF+UWb89LxKw2FY1Qkk8XL;?f39a_V_16h{QNBp RThu{iuBWS?%Q~loCIE*iFt`8! diff --git a/toxygen/smileys/default/D83CDF72.png b/toxygen/smileys/default/D83CDF72.png index 0ae27b185e085b290745120e8c6c41984176e089..3f26b4965e8e8fdce34249debcd5bd484be4937d 100644 GIT binary patch literal 1685 zcma)7doa{n96#18+ai^oS8Ym4>8=pDO@q4|+e6!0MyR{XZz*}jQYxiV;ZjjbZHP#U zvP&N8(TbI|Udwvyt~|&g<01T4>ut)%4z5ff=sY3uLWD|~)aLdcf@SZYmV|#aBT24NU|NnqljFO6?N*)Yg z$uH-^x~Q`DZ}%Tv-PA4+cf-PKZsRqz!dX6+RWVB|VUV3)npIGm{Eh*ilT-XTF(Vt! z9!pQn$Sr!9kPKr-*FWwjB+=8~rDQW-W-$|9Wkx=FcAQ9pmn!%Tx!mH)rp5+gM@v_4 zYv=bSQAcfa3zy$oClF<@nB;hhXDZ~GNx72iTiKlQtuw!;v!Irf+R)XHf z4v&pb%}8Z)qm#0(ehIf(*w)oMI65&Zm5oixeoRdFjf^*S^psZBHMDk!dj@(2M!E+i z!k+%R77>fXgWKhpr-OnkKTIRq{k(htkZPz^3Y&G-s&7@&)K=brIQE1$w6IWXvzy;Y z8W@?JnhczkQGj0!=J#Z z_N!_j^*}K(%Mbv>tt%ua8j*4d?H}Ohao(GXriJ)Z(U*Pv0Wgn=cW}w4V^E7T1}vrR z+_=*kYO@~(rgC{>(Y66a;o{D_YcO$He0Qj3EcsK7<`Qc<^^y+%)qL-;nIDCCrX*x` zg#KPvT=>=ZcO|JXCT^X`yT$7Q$-X@}`Oj%5#AbSxn~U+6H5v{KJocV1aeB*{r-S!n z%2wCvXI`5r_Dd0H>!LSJmYdUr+P_Xl_g1JwW}`W68kyN*k(ZW=nJ!KQ*~e>7kP_Fv ziMqCpF37v6F4Q67ORL#|pRUH5);%$CC9fu)e0r#94OW4^)1yB_kd_uVG>vg}Sf6IB zdxNQ*1@R&js~^>uoQ+*ED$xjwuvU~)aH-o3(S+rL$nY@76s>Q8j$TLZkS7ESBtnkX zuD!2BE3b|yuk_yIEMFIh@ZIWTaM_9xinGu$a@ZLr{ib$Ll6ygQU{h;#uDw-F{LF{M zN0B#s$;N~GOeVG35C*SrwDuV=Zs>okc(SJ>gJ%=E&b_Ta^jvqhFD->{>ypp)$c1EO zYDtC}k=_WMmbyH8_t4!cy0h4H=aCxg_pU+ygria6+f~jcZ!fYeQ#z1{`~?VY=aI(D z*46QY#<~HET>0sE(PNBrtyrsY$9}cUw6t9RAL=1Xme;|_s&Gwq-||C+qrqk-U(C&8 zxSF3N&UQA|oNcB}wt)^I7ABmHg!F<^KY`l{>UH(imNh9khWgx__tt0OxmU>2Z#Q;Z z&5>>=O`eIhz?4IGYU+DkLZXgt zOQG~=#h&6DF)mK1ZL2{UMB=%-GtUoge0ExRuqfm$%4>K|`3{bwr^msiQ~NeON1>dx z;~iSa1bdeZ^*o$SL0Jmp?c2ARMd=PFx9%d7&k0&P`NEd3%@-hV_GdQR&&|ujm*3`j zp6XLU;UR#NDhB=J@bGuIW<46ti3U+Mm`7&Aj76Os|e!r0Wp#B2`+ zV}ZdW9PeWNi{Rr2o%aa&Z^Dq4rwUBi_%nmOp9k#{C4dU7AU`)M8t+5#q!OtVDC8oS R3jY-V;Bj`?_txZFe*t5vIfnoM literal 1623 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLG8yO;GeeeCv{0lv$RV;#QQOs{jsPt4u8RTNqjzIJr6-SU9^F zf&6W0Zscm|>}KX_Xl!n1tc47D8_bjJ96#%5a`OH8F8?6i?-|1tOI6Ce1&hTOg`iDY+_}_vSo^F z)eoAhpWpdBzxcf+^Yv20goXP`3-srUKYu7~A)v@~*VI|%+lQ|?0SbN_Y8i9x@4LT! zd4o?&r^Z4-v57C)zWuH;TcIHsV_rS^hwl@P#0%SZ{Q0?GwBniis=KE5MT6LCclx}# z&vj3?_S4BtHDLlRpJk5AF84pYD^G>x;AMxsmp6KS`F8qAwdZfvLpLik*NcXnvsgR7 zps70K(sn`qWdhHXIapZUGw7Ud$duS1%eH2|YjAG$*LbBciBmO-A|h$w+V-3Cx%Cfp zhp6T&O9~afdtk`Z9$c_;arEryj<@F*Z-`=k?5!x~A98C}UGLARh}&|~Csmg|s%E*W z^rVV+Rpx`}fCm4|6MXvbpxbS4=kwxu+8q-AQFUxMqwf1=Ge|Y*F z2M3wP7yL=5QVkbsS_J%MUAI{I)(r+9xt+q7JyiQIY|Y7tl0I|i&+OSB7v7lby!dd9 z*;I#)=MTRdCP?((-T2pfmzZkSN~>$yp;J!Hj`Gfn{cc}v^Zn!L?@fY|Lgx!|8r~g0 zP!uy`%EI}4tkSaFn{KHIiuSraF0-0D^X|;%X{*jmzjk_>58qvr@>9h!%iD7&Gq7g2 zaqhf(ENy9)sp`wn^}Qi^5AHR)hhMsR=59%m{QM4ALC*G-y)vZ-S`*6_XmDi(32&{s zyCQE&{@oo`#q-zYzvHT47Tsl0Tlwb6m6ei#BI?Xc_OHHgHq`G5z% m4qrb%e_h3YHqIOMED{X<9}Sz?zMOIa74@F3elF{r5}E+9&0LTG diff --git a/toxygen/smileys/default/D83CDF73.png b/toxygen/smileys/default/D83CDF73.png index 5b7dcfa5e9bd808a6b5cfea7697af2e4144f2e32..4b8c2ef368151147e2fa2967ac6a1d9b9f4af31e 100644 GIT binary patch literal 1772 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 diff --git a/toxygen/smileys/default/D83CDF74.png b/toxygen/smileys/default/D83CDF74.png index a15bf5984e5d62aebacb7f71d202760833b60248..368e073cd7ab1f3febe5fcd0242eda420bdeaabb 100644 GIT binary patch delta 1304 zcmV+z1?T$U3d0JJ8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0MbxQR7L;)|E90BwY|d5($&4h z$gZ}#z{kp-sI8)|w5PMVnx(6EfroyHkbQ`acY}$Mo1uP-l7EVooq~^-rLnhykC%s( zoR_1jq_DQ5u(q41t(v8)oT#mvr>$~+gmr#{bbf?#euQ*>gO#AC+u`N5zQu%*nA6(c z+aLr;Ge0kzQ)S7zr~T8p~lYClb)r} z+TX3Vz2fNYxqrgQt+>DC?()XY)6&}B-Q?-w>+r6(zQoPazsSx1|No1tV5|TD03vi! zPE!DX0bkSwsCU@-;((Zd9On~CJL6-Dv)bDC^!fOe_KS`8qiZdk00001VoOIv0Eh)0 zNB{r;2XskIMF-{w4hkg;EP!>T0007gdQ@0+Qek%>aDQ@dW@&6?Aar?fWgvKMZ~y=} zjg?hVwwy2s{bvB**59S+ju-4gBkMKG$hrfYL}wjiwbO;&7{p2$HpqY=vLLR!l4K@ zG0TvRi+`-%WbtO$Xg$De;u7z++U62E3Is%>HX;sLcEw?yk-XhN_J|#N?BG(H_F9M| zxZx?1%9{vx#8YGvpbGMZY!a0k&ycN1`qqHXOu8R6DWfS_Hw`QRp6QsHB~NDUqWgC0f#gxcY%SzmZSC zat5aVLk4JccSUhD2B${y!&d2ZIT8vEseh41l20X0hblM&3b(q~EwmUyo{vES2%U(n zIY|aQyx=)nvYa}3vAf}Gk!o^qM*eEq zPyI$;ZMaLnJKWDuIw4KgGQ+y z8T6A*sht^&i$S^G7|gp#arqZm4}U8T^=CuC>n)-AX;IqWj)ZQ{rZ{~kx<5%S4qBh1 zGiLw*03~!qSaf7zbY(hYa%Ew3WdJfTGBPbNH!U$VR536*Gc`IjGb=DKIxsMxsrxqo z001R)MObuXVRU6WZEs|0W_bWIFfuYNFgGnRG*mG#Ix{soH8U$PFgh?W(F62Di;*EG O3IG5}MNUMnLSTYdSZ3P* literal 1376 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 literal 1778 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 V&wJcpm~GPs0E0U0S>Yb~>)+nE3rYY0 literal 1598 zcmbVMeNfY87_Z=|!zpz*#W5s?8{$|$lD6rGt*mX@*-5c0o%H5x4Q*<(nx-ZxEeyn) z-efvI5ZpNGobwPiKjxu=x5GL7JW=LQ&<#D5Q`u1A*iEOW=(ZH8+aI2PESKbcpEu9% z_k8CQOYU0}5?)E*@puzVb5Sd|@}tkVG2A=t&fgffOk)jpHjnbKA&ka(S~pdI111t% zf?ILSU9_wfSMzxB9>Qj4?dBYniz0adCv9Q3;q{kRgoN>`a zfF&qD5M{&)s35BWIZvO2KnBd_r^J3{BvD+-L}4r-f`nobNk-!u)@E2M{;wO))Mji& z0bFFo87fG-xO#ZfV_+_K&o>lRrDz*PEsb@6#Y?diy<#`e< ziqk|P?l#gC2@K~~MLfg40#b@~2EACJN8xDPN;zy$>Wzp2lE@)ghk_$m_y5V6h;v31 zZI1smOYDg2!07gA>vO`>;lcgf#L(Pm_zrpcdAvBk3Dw%dmp-+cy-C{S(&pKb$L0?@ z>av@v9@5Fx6QE%EO^I~Ii)r;v#G$G z*kmq@bk_8H_Lp7HiE|_%X)j9dTq&Ur7hPRpU9FmiitLMZqhgvdDdn@4>WV z^ppQx#;tsg@3%}`UZEH;5xBboX^y3wYh6~EVDn{@^=9)QJO-Aji zbK#FFq!pHR@6VfG9XY*ztFkQEaByZk*f3Pht2}Ek)K z`%`XJ@qxRGGTPNiqbHWv>Ex2s8#hkvSiSPt)Qvr3Uh6Dt?Y!SJG!Z#`NS|z*WLsNq z2RDd+-!`dm>#3Kz9L}AQy1L|>M@nB-^r{oGvw@DwZ(CpO&fB3)r0LYYwwwe@*RB?( zyLsY;3Eke5!JH7V89q{GZoJka9B2vN^L)35WcL0MiC;t?ncKenYOr#4@Ar?+*E{#i z>ppj8J0CTuq~43w6*sM|=LNfxr;s0BA1Yq9yyW3Pn{(6{-o`I8&Mr{*e-Zton+&<= Jx4Ok2{R4aGSKt5u diff --git a/toxygen/smileys/default/D83CDF77.png b/toxygen/smileys/default/D83CDF77.png index 12098c503eb103ec73c43c33bf2778050bdda5be..0ac8434ce4c16ac3e6caa4bd97062238a21133d3 100644 GIT binary patch delta 1568 zcmZuwX;9O55dHm0NCH@+ge!n1py3k2pKygkA%p-SByt2fv_b+wK!K6ctRRO} z)DY^yA%ddFAxb%U<4kAzq1`WU_RYMVH~V%+mklf{!;29B z02>Evga(k?Xlxn)mrL~)c-oqtm=F}s1|Y`@0ErxczciHO6##o+0N%s{;CTW7V_{Wi zm^T1WX=n(SL2?K5KqMm=vjT@?u+VyFl)tZeFcTHRLUUN?5W2Yu0byvOO)xda86zzy zKvxGP_OLS`uGPUCVNER&43E`oD1hPvd?Emq4chnnU6u!iC7`{+K!=pkn;&>S+uOwl zG>NFg$6`D4{AvX@*K%p+M7~q4Sv}H#>I9O;fs;SDeKh(EpJeL=qEZQ`1dbX9cHZC+ z4Nz^28d4oKyfzXEK@f!jti)7M7OcZ@Uv2FHQZ}1fQGss+LK$PO@oVh`5L#NWg}&Ve z5X=J{XS@y?r6FMkNR7c)gyKF96ffO5v2<7Qx?>-U<~Du3sJt$2>0V`Y#6~YCk|5im z;vD~YW8%b5{NiGUvMKpQqhPzlkt09`#(_jG?c+#ATf2C7s@Q>@hzLo5e7&jZ$%0Zz z`rJ@?o)l*BwKO|;)h#cR3gfp#(brRxqUi4jOVYQmZMd-QbzcDZX}+b9a>d`tMhB?f@-yI8wW{SqdFqo;Ar@I0cojhZKX5Vu1=NvMZ0iQ$Vo_C}&Xtg}+rml`Dw5Gy@?%E6`ug zAC29qY0x4jn}L3bUam{hvzP3t13+tE4mVm9&dVYScV#4{><|z|*+KzvZ<-JQYH@`) za&t=^j9J)b*eb^$)w{g=7ua`lhu_!t&CcX>l{!UQq5SBGBa4fR^j>w!gP=|LCtZ_? zeE0Jgw;XP&e7Hj?dYSepy`%5&*ZVG+jjTIl#?QuvuiTVOrDI8cWbRl~Bd>tA(wu+H z!pu0Pdq6!T9Lmo#gtyPctT4ed}8qAK1E_ zH3--{WLTq;2@9rgDCtEVbCXi<0rTgtb)G-Y0(+;_s*SMvJ04^)Md+631&Iyy``oM} zoK0l?^mLrG`5Doz>%pP*FD)^5OSrvXsw5BBvE|sZv{lbsK65R>Xb~PYzh*o7xjMFH zm?Iyp_%Ko&Nb;2B4`X(d@mS@GV5dLOj#1J)!o+e`)K%<{S^JHOFAVJ)zka=u=hqc* zKcG^yX=v~Xwy!10-67U_OpHr18n|TocFV8Huhs_D@=ouqsM9ah!(A~nKe_&zZx7P# zz?pO7rnh$F%i2+?IpajV4ErZ#F>M9aXSD6#ijZghOJqlFn0o3rX73mLHh^0zPb^~b z{nef8%4^1`t}?=v;QH~3P$9FJTfU{@>MBrT^a)okk#;3r1&w`Z0^SVD9l z^BA4v_)%MXPMVK29#|b1)TbIo{8EYxnPhl8@~E6iO4h~L)b6DHsq>{i#b?6oYui)K zhs@qv+1_OR&YqOv)cK~*kMiaXRRI>ApDpgFI0ZN6d*zEzcTr)X?XnKK<~*C3{xq)1 zctX6d)?u}NxhmI6Y@33Qp_VG749=4{+Dh{E!LzmIr>h5-8;3^R!%R%5B|F>q$YgZU z2P!W&FMqUo216&q2bI66s1!R-d$?bI9IKi(hFyz_i#7&@P!66mYHJSRFxjk;=9U7? z0AZb{73=&fNfk>5X`S|-K`8=B5%B#ZXoWL{&BYZ~s4 zv!apDm>GmPpD-sAEJ!f0Os}CB-PO1!i5StMM4m|CnXpTs0YHH%6bIPd!O4Z|1baHW xdb&EVgJDk?KHV_??d|^+q-7+gBxV1PK<^*L*c`p+h=sK-089qQ|Ds>K>~GrRW%d97 literal 1616 zcmbVNdrT8|96v3?M|>c{!gRy)Fh)9QdskZ9N?|Q`l_G_%)X?b^uDt`!(q6qDv=A~C zHp4^BhB}Q8bWYHzkQpZeny{(U-~$02M$ss~N+wuYaX1%YSEO!#82{Mi?tYK^d_LdD z@B7V_>rhh6E)7ag0R-!=O|ufMX?6DZCIwL@**0akn%Ej#Wsc3S<#Dc0rsq zhR9YJP|cVvZl#2BS=ONt3SBOjz$F&YOpXwi%jJFz1c~7gG0s9ei@Rg&&ZvL_N;)kJ z(;#A*Oe|7MP_+b!#R3{{<7zN9ifOP|i3mesPDO^QBpQ`Oq!Gc1@h{bZbRE4QI4-gnuNfR+H7FeT}ix-Jh2sFke{!h+? zoHIgyar~!P0$W@K`loM7pIf|X9@5Tr48yes+FPy%fbr|JsLJTRwY6$xQu?^C3nSfk z_7r%~#|=BRyX3smK3;w=Q(>OIK4fcn@Mn4pj%qq542H}Y>|7es8<{LRl)~HlUO2zq zxbR?G&-JGbUHO@tPp5hdy!K1`z}@!?7dwVqde=~$>mFIb~WJ9A&K`x## zn-Q-${!`V%N5oRk{g4@Dj_ozw29XHUF^x=w4S#1V$Us)6LqcyIeTz@$H0k8Dv_Eo3bw}hTj-_~^0P9u=p(ek5f z^Q1RImX}5*Z=060yuZSOl{K5e^j5F? zRqs4Sea75-!!OVDq=0|)XJ%@DfwNkpmz2jZ>0ExqIuyRT?&b#d#1i1b9@&+#iK>@VxN0-&m^;~C!h4yoXEm_Oh1>9-qiMFrgm zt(Zj7I^qzTxjQTE>PycWZ!xd-afHnC>V>SQ;o3s{`>2v6eR0t`{`AdrQe?-f2D=17 z5nA7|Qb$|uIlOIt(j-`0uc{BZ#$$ZzukTMlxA&Nc9nJSbeTPdby3IHF-z*FI`n#}` d#m_UxPXVHbrkzOdP4WLJ04 zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0OwFlR7L;)|GdM?;m3^g^Z(kt zMcKPR&BC6}!I#j`+0fA1?d|%^#<|JDv+V5n+1cgT*yGL3*MH2+*TKfl)!5zV=j+th z+`q=o)z$0b;`H+J|MTBk^YQWj~%?CkgP z@&3}%*}%ZY|NsAut{|=e000tnQchC<0002_`qb2oizE5?A#`((kCnDmYybcN0b)x> zL;#2d9Y_EG00(qQO+^Rh2M!7(I}<{5NB{r=dwNt@bbnG|cOY(6WcvC22YrjD zX(PGI^ybAD6T4Y;gmbb3}m-Bp|2BM zYSU2*aX2?UMN)Ya;Q@b&ECN(PypS!TQezF-9a7GH=81WK8}UfBH5FH=IG5AlM|cb` zpi@~@?GzeQZbo>`6{E-J57WSkZl9uH272^0MLsW z2FEjN2jk*eqs~r*(3SN+8;?qK1P7fN=3Fu!HX)Q^+7Di?0{eGRJ1kw{K21LOSv9iO z_kX5=fxCtY@fu|tY0|EmmAxN=WzB9{wPvCh%q)^$HP*R`(a=fQk%ocdRhCL9E=&+B zI6QlDfqVrVu!O2EKu3pW8x*ZOZh8~QmY%?h1Ym~`3%rzUkpnO#aulRQNm>wBKal$y z`2Z|OVER8~fJSv!BuArhY6L&+E`v^2LVv*}HB(7)RpNH2f;*sat9#yJ7fs0h)kpxL z1FrjQ^MRg*Q93%zcbmaVgBIL%hO)BOcNsMjA1-7o|Z8I31XH_9S5n=h8D zIEzGb?QBwM7LeKQ_F3Qpa)&(p1%G%T#8Hfs5a-L4kK5gU_|MlT-UPu7dK=_-P5=M^ zC3HntbYx+4WjbSWWnpw>05UK#GA%GSEip7yF)%taH99mmD=;uRFff=*-?{(*03~!q zSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536*Gc`IiH!CnOIxsNl5oZPf0Ffgl3IG5} KMNUMnLSTZAK5%IO literal 1403 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYp&T!C6$jV#>Ej9krK zj7%*p42_))jbM5`^NLFn^O93x_GSX@h3YlNtJli8C^fMpzbGU>KL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDoioYHVz2Y3Ata1ayIs zp`oLhv#YC{tD&2drLnnzBhc*-y(#2`nLf}l`k=&tlvrRwz!V5#!jnFb1J69EdB7Y~ z1k4&ocAWy|D#q2GE{-7;x8_Xp^=1kbX}fQ2e9mO$;)qCYUL_@_g<@~B%O;6oiTI2pIvvS z@nz(`95cCt(V-oa_S$?ryZ(gt%Q)*FOafb54`(Si&5C3YcJU~?V#uzzK>iDBhnxB~ z?@zN*@5MG;?-p26D01|xn~KrCyW$ShY$g8(=)`5d-m&|hyKkhUf{l2%i0bvveRHMD z51H7zDNVeiH8mJOato!8_j+ImRzFK2?p@+oJRWu&O^9Q?=p@PkCrP7_t3bX&*uOgs#;0}csqVigww O6^EX#elF{r5}E+xWA04= diff --git a/toxygen/smileys/default/D83CDF79.png b/toxygen/smileys/default/D83CDF79.png index ce34a5ff00f962efcf55db6094fc8ec65e45c8eb..5b94fda287c385e0f066be694ec9e0ef85610f14 100644 GIT binary patch delta 1556 zcmZ`%doob&yBKHvK}_ulipPf?X9rZQa&0HA}} zNJ5HuAl?ZNK*h}sUr$IN8cjJwbOIn^CjgAA0IVYxV;X?7SO7ly0DwyeU~5E=W=kf_x#H52(2x=kV0mKD$ z0BOVOLfBjfD;_{j5loHy2L+iB0V1=L;hp3kBr6GK-$KZ}L7QA2A*&yM>&1bbH&(E# zwY+Yr?Xf5w_WrQkfn1I;2mu=u@(I_QO?d;SA^m+JoT!8oRmr_? z;b1D{#WuFM4%}1~a;PxJ>-)tWD-46r(FX9o2W(G(sX>Bdeb|`{IYBT3^`?) z?dvX>9k=dLcp)N`Tw3er$Hr}?&rEB*d?^|nBGxfvp^EGk@pZ7b5rLwZcZiUr5}a%$ zCnaTNv?RJrj@|=6^g;yrD3y5P96BsKG~i^gKbm?z%pXlV69#}uLaLhsCku;uJG;d+ zs&7CgMp>Zm<}vx@WZc@OYnld+Wqu(Xalr6k@|M_ou7f^&i26s2KG(Eqd1bL&9(BJ5bx~ zyyahLhpf$?8?hqV$1#HX?Vqc4mC-WXHA9V=!{IRve}+|-J=`~j3bBc6p~=5jSTxEI ze-IU^#fkv&$bE{pzk&lJt}8Liw5sLM$AYb(bkK4LJ}NAfJCbQENoEVh#NDn+%DKFMEZ z%c1w@4aS-C)TMa}1>a+9LCv`gDOL24d;TjqQkS@;L4XPU_+2+MlQfqu3o|(@_jhpa zfSDmvCD@L;p(yfAbZ`8`!Sl6;=SB$gY-+TSx;7fvS29?qy3OSAX)co;JfxTWviLP$ zxRb`iIbHWuhELWMRZT-xENdI@(g6Y*LyPLqSAeZlJQmW zq=d@dyiLEz)sFb^cFh&(1;+E?zqkd8G0dp}yr|Un*eV=B_CTahHij>oUH8RXCr3ll zZ7$z5RHf9fp4PtdnWJsKog-bfaH`caCRm+%nXV{lKVE+OTW{#f>uYAKLm%{~7V8d8 zuNRuK=4-|r))#e|3B!y2ZXBJM|1dkHx=CpZYBTU%WcIQJvF#6nq#rQ+ z9D|QXTKN(=Tga9;t$i4CjlH_Ze%g)JO-p8_dAsNsY*6qayXfuK-{nU1xhjPbWsM;@j{wLMDpJ3;lw zQNsO^0`_3{?7?7-F@{EDLo9CZKHNU6E*6W!Vw3Rii~kci6Y6&|;QapwMpZl%k%01# W6G))}NqQ)1x37}xQQMiJ`SX>q|a0ewoQvL%A?eSsi%Y{&r5!`C^TFHSbMspnjZ)a?&%e=L{e_sHk-`96N% zZ(4G*)1xA%MFId2m9YS`@#Cc6BO1rQ<9~gM^TTVLxsc1JOE@pikbr@pi%BrUjhB)( z5+|xEz9LfqAiTt7FXRfXSvm*pmf}H-)aUl_Yye0}^LcPb8OedgWT}hNL&JTCA<#wW zp#qgvZuOW*r)xnqL*`d!+a1+q4wQh>Qo$6Tju&u~91i;27h|w9X}6l%OG$R z!jQrhSq5;P)h_}WN zB{~~s8MDQ&^pKO|JUW@o>-9>#3MtK$%3u^lgBl1T;SmzHisEpegkoog6flx?FfI@0 zqA4(_h!@il#SE{HErpv%V>^ zR(RyM^*hgB8MNIwly>%}-4s|^k<^4iVX8n|5x2Vc7Mru9|Mv3EdHd=Yf9um3GQ*CF z13lNJr@pT9pPt(=#E=N zC74?e9f^3)6W3Z6&c3xKcC(RzALSW4Vt^a%k)2aNi0d8c3n-M1W^q_{+@~)^_=iq8 z>dV76i;C7g`7!{yD$Z#f8@Fe6P1Iaw=I_h*wwJ6yUZr=n%u+t^HViL|pL^ekJ!|fXQ!#T1) zwmZCe{j$tCP2K~l{(j-=Mwhem zGjW`MZ};s@-%rn8gf~VftB>zVhi8nOG)%PmEB_>#I<^WX0A~+f8c1aJ9}E7{GtAl8 IZevly-`~?jv;Y7A diff --git a/toxygen/smileys/default/D83CDF7A.png b/toxygen/smileys/default/D83CDF7A.png index e5efdaefee84a1ae1b0d18455f461b459344a765..7f8a1f2f3a49e27966b260033223290a2f9af81c 100644 GIT binary patch delta 1601 zcmZ`%eKga182?T~%G)U~t#qR(DOp5X-14#;S5rySo5qHaQq7{9exu#YZrGNWFq+Xd zo2%t@lxdm8EF-caTdIq^*C#m_7{wEdv19 z+8Z6!06fwI;{m|K%$46mwKd!l=M#Vd;M!&YNMr!MXATH~=0(#^!S({ics(+OPmV^hsi6ZA~pc5mRMyeFO0Y$GwP^qE-wFtE&{P{}_ zdb+@LB-23A+ug`xjWB?^)m|-fUJ^KcE<;QgtR5PC-6@q+FzJ&_pqARHTeqo&c1u<3 zOBj@yQov?V#JNNn=e9z~Q_6$`ZGzrAJ=? zoiWc0jlC9x0RN)^nWy?POxyA_M~nS#pK`D1lojvMSGU<3myQmy&zz%X4i@Hjz#CAs z*ZXLQF2NUr)*aS0Tww!$yzksD0JLsg3k)U%gkH6byL>Ss>U_8*Au%r8G9fk&0N3>F zV}1-KQh#vVWKRkm!ngluXpoT0-G1e}TiS(N#d-J?9-oePUo2MbgSs;l@?99{Te}@x z@?N!Yx0&O3!+o)LvJ=;3KEokzDls<$&TuOO2=i>M*Aay^Unc~?t&xM=wM1JJkxc}m z>Jq)RK&I3;k2OFR2*maFbq(-LEm$8Xo$obVc!^SGvjK|U|BKkb{X$$hMsbqTA{g>z zNDG!f;Fq;o?>GIjvftXyxszqjx;&ewyRaB5j-fu^>gpwKIBm9X5El%~lX9cY6?Ykk zPtM0HHafjQ6d7GK+^T#LraNox-WRI(zUdR-x3$vmQ7F3w+T^!Toz`Z{V?OXxa-Vqb z<*OWo7>G8y7Na)dz0ePT-AC1CzG2nWFz=Mke~;9R8QA9Wa6+uSVaujQRzH3 zvC6oyXQ_6UG1Yws=g5WVS)OFip@(Zxke)?Uux&_qIr-gFovMwuj{9ApO_A=$ z*m@@N^M6Y6GUKEbn6b+CJ0z6khn8qtxUN^7yW@nkxolGE?RyelDYW`*^kFrhnfy^r zgROmehHjBADIfaIM+Y_DCZ||A2l&hehM@vQ`nGSzit698^%^#$!;MkebrD+-)6>aU zpe+2JW3%TWJy;SIcGCUK`?>XNGhE{~11BF`P`tIyvXK)y%hOy%pNe+6Zi1X9N3NAj znZ3EMEUIQci=lNbmNrl)kIt}!i!1OY*un3#X#AGJp~bVF5U4^>Q*W;6mTNw=-`CqC za7_;ms|kEq1f3olj*BD#!xMWFA#F6;CfuJxqH;IFIhQ=(z0SL8YvN5z2@S<|b8`mz z>9;WlpUdnM5fI^+dZI(2v<*V9c($4nh9dfA&v}%VjOwYv3 zG&d_J+uSrLkV;L4bGb3m<6|9Mp4i4(WEgXfOw!?aA@cK2ogC=jiz>is1)LdlZ%SHR zT`F<|Gx|=6?%qeJnmcU>=wJdqln{=>T@Kd(a6~#f+9P+{@7xu*6Nz$iMmZz5A(1E~ olI}6Y{YS9h($ literal 1630 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY`Cp8Nl><<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3owpg#ns#uhpAQ45_%46XcyO94PWH&8Rrt=%mWGyHk$LXzJ*9EmzvYp(pbF-I3mP zmwaEkb?tstzu|$KBg@NUyV@X8If}mKL_Y#m`s# z{^z-$&+fdBJ6IZPveD#!T7#Wu*J=?#uBZH4y$wAL3m;zEnkv(HCCl%ukF{P~)#ASw zoO36nz7jp^d~Vz7iHGXaQqq%KFP8mS(QqS1PEXxdHj_qF0a?_B6si&Hy ztCdOe*!;PN=e8V(`BmIp*24Bb=~Lv%O?=B#kG<>fv;FZz&_G*lNtss6&#rpKl}Fys zmXMU@|9kxJR)GkeyREmb^*WTxc&%XeJL_aRzj5O2rtZfVjS9azc-a2h(Y|s^Gq1DT uwMiR;#WqeT{7|%$skt@yIOiX+1P1@lZ0|nKlm7=Q@jYGrT-G@yGywpih;Oa{ diff --git a/toxygen/smileys/default/D83CDF7B.png b/toxygen/smileys/default/D83CDF7B.png index f690c80036e71042ccf0a7774bce17b5dd91df98..5fac44f5103bea8bf40873400989cdae68a01f1c 100644 GIT binary patch delta 1481 zcmZ`%c~H`M6#se?lx_#Bq?x96SlUmr7O89z#jMmaERSkEK+`lK$&fZtBxNNl^IU5y zue>mC#WTFG$ad9KG}Fpklh8b}V%ceCXZDYs`OG^$^Lg{;9Z%NIBkrP@3IKqeMrx;J zPSG1@ivytW4&sZuk}TKpJnCQz0Ao7;BA+{T|C~e*!~vhsyj{{rp*BS?+_} zlzSX*RPz1gfB4qw*|CvD$=v6~g~iEtL$14;D)S_>lgoXbFCKA3-Ul11OZfR|FI#HI z-t;doNLD`0_jWdSw^Ypt>t_F8t&9$hzv(M0$mJL3iRV6u=VlseN_eSpvKIBFnHAii zR_dl{W^49bS5@!5(*idj_yxRw-P@9KtBnFygt1FqIb#n`c2VS;$RIy6xvMm_GYG6U zQ&-x#(<%7D5M?0+bXR8eGJ&+wVYQwl$=EjsayZbd6DhM?+exk!JhCbgW@Q4z9iZw6^Vx zOeE)V+}aXwl(rm|B`P!(Kov0AQly9Z!`#fM8#}k>@QG<*8=3O_+x>ADs``i+ShNQkFlawQ8-6XUxb83SKQQ@xfPP3j93|;D)$hshO-y~wh12c}(}n6Sb%wPQ zXL_K9$1qisj=RigE^mnR(F=3&u)y!_hBsxRu?^QZ!NdfA_ST{q%}54AazLS9&u)Us zI_@vJq^mj}k$KcGVB6q`eT+@0AxFK)hUm-Fb=4gm^wv&VDqp`ubakMHMFm!@4T)$G z%VXmmdyG>|M!nd*=S-sZz`W_^)n+C1<9D{S#y&SuwP$L~)hY<|)U88U>jPj2SfR8^ zog$ZQv8`{6}3pui45X3kOOr3N7uEob{ji@&b`*Bir4rH!DY-bv8`lLpa{B zc&crk#7LY@@SxhC8h~!H1rPKuGrDNjYVYBC6t%qwpXDpcPb_b<;&$2~-cG4Vz7Wji zI#5a9)KJZ-mAmhyQK`O)NBrXL5))3oOeZi6ZJr*ZMZ(kg)p;L+x#!PWJiNnI`~kb( zb$&ZaEDpOGM>2Dicra5!Uufy*m%#KN?hA6l(1%X|#$7^wa;zRR0T(v7CZw~i4z-f& zbEHS}M(MEer^T7kLl>gi9}Wr~{pX@07v7!K(QJApRLdp=p|ysj1`7yXK~#A?Y9~`$ z_MZ(lmN=qDm#3SXhgVD-iR?K(Od^qhyzwp{l(Vy0@MY}_<^`dw)IG4ByXFyDN8#`_ z`?7Mfb1z-=^_iZSn(S!T;6NIiQTLOQf5#)+T_7hH=Yc*^KOTwYpfOmpDt=`J9?9UC z&>6w2%b!>9NEbAusHm(I$!132kXSUtwzec#wKQ5F8EB}nX@~xHgBCne8G_Q&S#m^o zi~`<;LGU^gq)K$xQV6GCz7T?QUr%(0E3Ew)`J_(iG2B1{7!G4@6q(lr%H}bkSvk>2 z?ldyi^9ot!02)H0ji8^5OiYO;5O&{wtf|><2*N^8t7%~R_kc*5989w=Vzy literal 1574 zcmbVMX;9Q=7+ygr2#k1)1?pfX)T15QY_gj@B3licEbQn4t|)X`9kRQD4Y0|Q1b6W& z%7{{Q#)7t96s?txSf~mWys8eUMLQ!N^(ZQGXg#LIgB5$yEV8yg9Dg*E5qhvB!gejS@NMoe$2?>_olb#Mz(hx0@nc_SyTXa)AEyL}cl?F^sDu=ev zl)b2+jZTos0<)R)Ofl0u2eWZbIpxF1Jx-UzmdO%kdR&w(j}`$dox`wtuc^t>f0>9!?#Hk}=u`5utq!6X4EEmDBTna>KCM-L9^V$$5V4S~QA zM9kBJFPzFWrvV0zrvX&1jJGLZ7*MO_unJYH;pqUPfDuR`-RgK)g`sK;(E!62C|Tp} z*_eee4%?Dede9+?E)0U)Znxa6lyiIz1nYDQfdl)Kj(@&P4puvDIr4u#4mI9s8UWKS8WE*dt5hVSL?Vi)NWaFbxVTYm)Tj`Zh9FT`QqkgCqgIO&N|iy4OCo-*i4{bO zwb6dNjAVD1t9&gNGw?Jea(p_+<@+lj&B2MB;NV<@j~3&a793 zB!`^~2n&0tdEVLhhjUSO1NZZ`&W{_|_?k3uH0qqR-mh=tdmy*Y)e=3>iFw@$QdA3x>JlW%cKoHSQ}_LTdiTxdxUZ4>`yFR1giEEh`Elb%4NMJU z`h$W`Mj2Rr2|Jrit~hb)e9Q93G1q2A9=?TpJ2HATSGqskqoKM|CYEiVkOx+-SbwTl zh%$~glu&=x%!vJ~a+@_)>j}BO$=!2ftNG*+bal$@5zXh`7(BD)Ud^~-{FnMvTLHB= z_LP#bYNsj)^A9{L9p)p?Lzm?zNQ^D&(hGHJr&`{ZhkB@RU@Wi^4!XRmhAbr zAGsRu*KTWPrrMen1$~oq)MbdJ{??TKPXlIju58IOe08-nx~l5v-vf@^n1Jrl2S9W# zC8)cee1GrWyW%*kZLk5aTX2SIYTj%4A=rK>Fmh#O>EY0YpB-HhNIGYMs~+Uutv0{? z`IZZt8YgvZd8p4iG^Qjj^7MD@b6wYi>J}J}HIGnYUEa5rS=L={@SZ)?kW)G)dY>UU ziCROUSMk>4nkt~LI;bFKptiMld7ORY;@urzlJz&XXHD3=amUh)^}zf_>+FVI*Sa%z kl|gIkt=^i}8J$5~S0QK`&DgXcg diff --git a/toxygen/smileys/default/D83CDF7C.png b/toxygen/smileys/default/D83CDF7C.png index 81e6102baaba699ea5584118f65e1f031fd2e814..765efa2a62b8be216a3471cb1408c2e7d38272d5 100644 GIT binary patch literal 1631 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 Fe*ty%Se5_) diff --git a/toxygen/smileys/default/D83CDF80.png b/toxygen/smileys/default/D83CDF80.png index 18bfbb4f7edca62ef5a694fd12db7d736ecc05f8..3e00c994b86772bd82c26ec3e705ce7ac4583286 100644 GIT binary patch literal 1656 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 v4<8jnwy5%Fcee$t&cB5UOICgprz`sh)O~zj diff --git a/toxygen/smileys/default/D83CDF82.png b/toxygen/smileys/default/D83CDF82.png index a9c0f5b8bcebd357a65225a3c56dc0f8f3510a68..5de5f4ebd03e9403c0f60ee79644d7a26f4bf949 100644 GIT binary patch delta 1607 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 literal 1724 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYpg#ns#uaZs#TIH{s5a zQbpmv(T!XV1CHlWhb1-ZckX)yVTcJ~#Dh?z`>TjPKqUiB7t+OV_v0K-#9mb93UZdY*`+ zfbIr;4F$WSOyTb>Vm3#We9aU)vF}OB?%&N90%X>v`s8Vynx-t|-LJ50o3Mez!&2iT zd46q%N81j_EbZ@Zf10E&lX|rv@00rkf8+0>9v0IkifoDA^l8EE$_UMwlG_|EO%jmh zl~^olx#D!-j*dlI5yjjS_9p;76x?gN&aEKDJ-{Ck=r?>W7H-|(R1ib~VY zQwn}{tr~of&igv(yJ?-?b>i8Az9{x1&zZlmcx?24xYz6;^OsKy`fX`f{fci3B>DRX zN|tg+-{9YWojImz?Un_xtP@R+u=&JG9@3vbjlE*ss`C>|j?|pnuyM+tX77&GUCN6$ zr|h13Me5bkgw*ur^&av4FWujNd^xj4zxjvbt(a8d*PBfL@Y?iTJl!gJlQAXqqTbU< zwH6o3r`+OWHd@boce}=AXFys-d8@3iY?VQ$!Ku$Z3;70J63KAQA(x3|)y{ z1CWXZ;7t?&xJm%bc{QCuo&c;d3G}0Sc|`zHI3R`r0v+rL1!@3x)4;9}fDeKcYV{(( z1pT>D%fIc+l$s(8|i9(gnecbPSK56w6LvVFZ~x z7A7N+2Y^R$IgQey#KK;r6j~E>wI3yWvd>&}Oj&XL056f2tPG3NW&oAD8%(L1(XiET>T_jZV=;%6{CbN7ye_eV-^p!zCq)?KxV zIFVco4$C7amRa-i zU3sQkQdqRMqC$__tjk;s?LW?-7 z=IZ$EX+Xf*zx7MSijhM{s$pWdUNhqfVRok(;TjkA?)ZasH@oGe4`8(YcCBu8X^+gh zMIy8_e~QXX+bEv9t`5s&*2{O+Sk?{Neqtbr_TT^ca(b{i8=uxtw9fJU zzFf)gqPl0Wy;&Dkj=7ZSJ^lvuuA)YUxxCOOH2#UAR-*hOYVQ=8TyXk2rb&oda#yXR z5B4l(_krTsCRmC0_+y?>VQ+OHoJc4`Ws>={dOWqiOT=&TnTp=P`e46VG+dxKTh8o} z>-~JHboz9E>!)K{xRmd-;-)I!W^{(xun~R&$xF+X3bess%&{p6@u93Vce~15E*x}U z=JXy={q}Gm)6;65GX3nY&F37tWE;A*Mz^+eg=Dwrvbp)UU)Ft7PAJS93D>>|JVwWu8tw57{E_V8l3%{+zMyQkA)Xt>-N2ve_Fa1Sg0o`|G2=MEM|z7}6QBD}>F>sD zVEcE4H9)=D3cTn7CPTo&#qe2>11?w>7bmQ{ld~(;8H?NQhI7L@V6iwX_7q`!br}B$ z5|fzh*v$VIytEE8g9MhV8A6g`1?dbv3lNw|(JTxlkrBrVW-*wV$^9(oqXmHC6+mht HMjibN2Z~pj literal 1502 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 literal 1771 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 Jv6|v1{0HS*u!;Zx literal 1602 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDgpvsI!5QqnWWK&;=lW zn;W<|xi}da8@U>rS(=$BLG`AP6K48A$LNC+2U22z2?0|ehzU>nKn^_fq~-y0Oc5|^ zNPbB!W?*0{@^olDA$1Ub!ZdPJDyyI$0lW&ts_1rVHQx58}&+-*nzUG{S#KD_PGtX(Ne>cf#sABlZ znz#I7a3;%qd6}sTBx;`@)%m>t!3NGrmR8XQMf!2YX5o7JYfB}wSM}6XzU1_O=kfm5 zkr+9~7C|4mkpG!%ecY~R%G*CJ;&+dCy=|)}fA{krw_R)V{-m52HTWUD?IiQTMXNFw zZs2v5GziYQ`{_}zdw8;9iuRffS#I;3HPGQMxAJtn8lYD8ODK2WPVcMt zD;}GEC>5Af#l5xU`qLX{i2Kh)vxg4ld5Ifv$A?We(>N+%VPVv9 zdKZ)A`uzgeEn4>9nlS&(O8GYud+t4($F}_2ahbim%>K)EO_=t2b$EtM)$|izZa7H_ zStjg0`0T{N<98#rhl=$vX4PN0YJcDN;DOW2HhP-tL}zVV8_<=op!SETM4qD;Q~UYV zW^z(xl3_B93&lco9=mIb7$3BWT-f$1{bK0rNSDM}YQNMHo>wT^< zyLf)jxOw6OTh2{IhS}fto}IEmHTB2+4VOMWeaGwfSn1-)+O^-~8QB;**9kl;JE{t* P8yGxY{an^LB{Ts5>62IX diff --git a/toxygen/smileys/default/D83CDF87.png b/toxygen/smileys/default/D83CDF87.png index 6fb75ec6dbfcac739584a7d7ea261ffa1f48b1af..1af4546ad31d8719c14d9b2a5f5c3a5ec18b7d9e 100644 GIT binary patch delta 1558 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 diff --git a/toxygen/smileys/default/D83CDF88.png b/toxygen/smileys/default/D83CDF88.png index ad51677127d391bd66c401a2b9ebf66250aac20e..f208b55a95347cbbfd741396be9814f304a8cfb1 100644 GIT binary patch literal 1308 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>$QG7@g8_0iu&IbVA1kXaZLBGc50a|HA+C*Z&O3 z{o#}PzjMz2$ddobwf|G<{@cd=w~PO87x&*j?!Qssf2-L44vGJrk{%d%|94LL@16DE zFXy9G=>L%7|B+>%9Hah6mq9E7IymRZKA?XXI)UON^+H1f$E#blSBf>zXj7i?^E({&4vK~MVdx@v7EBiBcJ}y8f};Gi%$!t(lFEWqg^Jt)1_q1XsiA?>ZY%Koxvp8oqQl84f1LB#`uATq zAAj$m61o0y?smoV2?A1?CJDd(eir}R5r5gOX!^sm-MVUJn_rzedSlhjwcEVY?;U?z zCVgf`yl?WdT_JCSwp0f;=5C(4<>#{-5l7AMu9m8@5ILxpCb_A2;%@VsdnC=G_zj|W z9NB$0x4hGoi^XNJnG)a4XNOMPDE};I-WmPo!gW5qt(ogvEt;g~1qW4%KkTdM(qRg5 z+&3fIW$7`A;_@>^>x}uoe9k;#dv=@2orFg*e!cQ94Hq@|riN|}&hptQ@zOMWi<0yu z$$9qoWW)BPAK`oAdM#tUhs1e1MuCali|r0}m@i>%iTE$W>g2F8fMc8K%&3C`I`dv{ z6X1~MOHy9t`DUY-Nq&@no~v zlOQumhg~)Qn>g1CVfMf`3oP?wcsUiGK2Tg`by{ww`5E06rI&XEXjk133R!USpy1Eh zsrIjW_i68#d{3_2vj4fO({=N+H*@ZFt-rb5zwwQTx48TgsjDYTD(bvn@X!9-)vzwr zUJ#f#yFFbTLnJQ8p1m#Q6d=I*K+;9?l;YgQOSU<6guMTsa8vy7_O&}_evLoFuw`OO z>XH*e0U>Ak{SHP3a~^xxsy;{H#l^4J{FVs&NOr$BtUklhqjGj$ zqiD#@PsvQH#I51t7w2xE21$?&!TD(=<%vb942~)JNvR5+xryniL8*x;m4zo$K}8mW Mr>mdKI;Vst0GpsDWB>pF literal 1352 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYVYvo*&npl!w6q28x14{t`8Tlpo#Toep z3eLf13L4>=c`5nj#hRe#f%w)XwJ5VJHN~wcKUV=9zE+u7>^CxYbTu$CF|>5EbapiY z`rFyX!qU;n!qv^x!o|?QMG2}mg`6F!bAtAII~9u9`n#MAm)vqSz_D>@?F18+wU(9~j%$5u`*JUe zT)B4tLV&j~uOAyxzkTl!?)JlbpO^DL|6c8Xu~hcj zE-u$7&(%kGE;`x>R3CZfptbN|Sl87DMk1N&vfo*MGtc{IaKUZ8IPcTR#dl(tU%sN& zw9@5X)`LxETW-C0`;xEXkh8;2*%MEBzxD7sKf7}ME$5wU%b1Sr(XD%6b~uMi_N)9f z%deVp(f=+xgqPUAE53SKGfgd|H>PdfE#VvMC%9~6s5!-?y8U*_OK#SS{W81~h1$WI z%)W6BxlFTa)izJx`J#_)v3UE*V_+JhHpzA*f+b*=r_N#;$ZMs#pfM2-FTLG9TD0X>-|S4fnkwG&%8Y;`yPSH NHcwYSmvv4FO#qeV;z0la diff --git a/toxygen/smileys/default/D83CDF89.png b/toxygen/smileys/default/D83CDF89.png index 5c4d5597dbe7b32b7196db9abf542622b8cdd130..aaf40713516259add564a21f1c7b5d4344783cf1 100644 GIT binary patch literal 1812 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 literal 1720 zcmbVNeNfYO7%sCQ$ec=7r&AyVrUKG7Nn6@Vfzq_nL6MbC@MFW!1SvF4X@V`?rpPSf zrYGA}oT8Hr(L-c|V@}k=2{`43I%GS%;pY5A&;#!9bbcU95#0Xp{A0N!zmGi6^M3r^ z_p&K9W%1N0Z%*NGI8$|rsDT})dmdjO_8mMtim}6NMw7v$QQ3?GvlAQ@PGu2*j>J|I z1_Hwi^V^9S4rfxf*_gp(=#v#DiWFcTjKE3SST=_f6X&#HraXcHvWV4Ys}dYMei8)C zxDrg4=tX*)n#eIHy6i-nE5&GX<(cF-7#9n~I2Eh_NiZ1TBrR52;Z%ZSx(asg*%pGp z7=+1Fg3p}F(5C`w%1!_h0TN{rK@cF73LvpWDuosTun2;MBKDR>L1KkOs(_<`@e5?F z+3{?J0o9J%VpmEqhhc0Aq0r%Q2poujvac3Ga=F~20mD%&B8o1wGMF>UN{3D;pagBQ zn{AAlvH~7OEQ=~&lpyQrvk*v|UjJO!N{=UsO_|V%*@TclBqT{sTw`dOF%bW{@j^6h zEVL0q13^;-b`yIZ*`X6;Hg_*KnYD3;jG?4e*5lrj~Ngq6{uN|5~`z|FWq1IZx` zjA$T<2G&3jB-6r(OooUOBxr&J7HcLnUc^;PB#25b6U)^q6~wBj5V=gIfl;YQ1ZmaL zvI(xvN;8<%L`>Kep=TJ64ohAhqq?@fc<)Fho z$MY;&l(3uE5V+P(k-%7f73LS%hY^)V1dAcLR<72-Y}{&8CRZiMG+K!ag+wSg&c**v z&V;NpLQir0r&uPo*b4MapO-$nc-}mOmF*Zi+ZwCBs`BP=Ub(G9RYvFUmNYUk4SPLy zuwpkUr|DgpI%4^!wPpK+)s1H}MH*A2{art;i%YY;xVnK-SBjDg#aYTr3Kxovt zw%8pxH=l6#1ovGo80eQey4M^VESOUge*3i_ zsNXa?I77J^tOcbd*wpT&+)7@?9dVCo`zN`2?dR`QCMP+T49fR!P^UFREd0o8k7cVg;`P|{n46Jh!Nv*+&OMh0Ke1Foc$Vun>`uN|cw>S#a zHUvH0SYMqPX3;8SBi<*7u z>|HyYzZSKm_P7me^>a?QRnoz(f_WvkY)1k^FW)Q`T-+<>AF8Pbqg^xpXkLySE_{5~ z-6LS2#%Uc7FMa(eGqG+hBFqeHFWPqE%zX8UWy13MW3`L${!@7o#YI=Hb?-B-)5IT9llb&QVqi!Hj1o!=d*_$=4m zyC2eR^@hIPmgoS2Doa)b1X2kfz8j_ox60&h@B72u$2*?ZtiN4~)mLp=6O*L^Hf*UF b^%HTnA6=C@bl}k}&%Z>cNkQupRu%sP$v1ws diff --git a/toxygen/smileys/default/D83CDF8B.png b/toxygen/smileys/default/D83CDF8B.png index 51c96fe3e8f09607c17025cc66cab93674aab221..fedb6536402d6c03fae448b78c99222a2e105b77 100644 GIT binary patch delta 1493 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 diff --git a/toxygen/smileys/default/D83CDF8C.png b/toxygen/smileys/default/D83CDF8C.png index f2f460b03dd42c3b23c074b564e5544235e01d67..3b62bba64cee142d813c011194ccd05aa51c4dfb 100644 GIT binary patch literal 1534 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>wW~I)Teo4|noX;gLPQ|s-XkZj-MV{V-(DcPdh^bn!zbagH*VhpvbSy70z?qO z+xH$`Id}Tjy$3+f+~sTAb{|;Znzwb&0U#Tq7)Wl}a|ptQIAbdk z7brprdj9IotGDl7zIprX<*U079)J4$<@cXIK>z;z`}gLZd%u7G`S|Jcod=IzzkC1R z%j@~8*RS8c|L^AZ`OB9-fBt^?`t9Gpe?Ncz{QTwX{l`z=z5n>&@zV!Sp51@+I->9LggiQ^XS73Ce*H8@`!It4V8G0EHAg`tC0)&t04 zFY)wsWq-!b$EC`x(@?sUfq|*IDkP#LD6w3jpeR2rGbdG{q_QAYp(3|{fx)78YN+?7 zI|dwgio0YS*;qam+DzN(9XidjPRd5!zW=`Z<5e43I@;J$e*ON<|FYcRPON{d$m+j_FJCS~QLdf)0v0Y{QS{riBCY!~}u&@5HC_418G@olh zM`T-0Ov+caZu7&AuNl*Xr{B1pHQW1zhJZlttqzNno1*@od!9UH+Y~+L!gEf&t(ogv zEt;g~DXrYoS>SHts>ZlVA^wC~;L43{IjvKI&)a;s_rG_?@|%;|xP9iFe)+Jvsz)Os zI5PC@6sce><-O0kmY--UX?xyp-Y%LixskbYlI6T~m7f3mm=%=7-D{f!^F6pZ7XFuD zb#ho4z_HD8=B$GPI&xLBIa+wxHws?$d$Uo@aYNaZU%Jm&?!=`k} z|6aij`A};M@#|8xWlAmU_SC<#vAQ@>CdX|@@yVJSEf>08+E?uKdh>fpS;VeGwLA8F z-kcGVd#|01FN|0Gk?Ae7Q%g2ym3^z@m}k0Na~J1Zjb&QClKH_Rajvh|1zyXDJfRxK zYkOjfQlFr6{Cwq|4*OWVp_9EIPBt9)4)ciCt6LhyX`m57m(H9`K@fAT4b`s z#XCwM{QiXJ;-9!9o+%srl{a!&yj#0#v7v?OB>8P#%P&t2DDdnxnG`d1hipTW;?uD2 zyIRGZPkb((z~IE5loD4jpE;IjXAUP_jg&c}ht_NQlFh9-k72 zkdU4=GR)02ISw&DMVsLU|6H_V+Po@G3Wnj7D>gTe~DWM4fcYMwC literal 1523 zcmbVMdrT8|9IqP^#uP-|d}Qc(xD7Mf-d*jry-=aOYoS1SltzI`@aP@w3D>LFgBFau zq7Dpmm<3#9%xEM-G<)cXOQ&&!Ipa2697Y@h>J-r|u5NBqqT+r<>h=fmkNt9Yzt{Kk z`M$3?H#==^^wMaVOg7h&PT9abFZ4!5g75NsPiQbLkxT_rzECFlXpxl}7@?GfEIjRG zZ7j`HZ#u&!%VaN>arOeKz?wxk1YSXhWE6hh1JE*Aa*E$WJIYxJDrKFVTMq}nItfD@ zqlee2t%%h_vMw&YMr8AAvh9wVa)*wAQ`SJqegY8itVBb8zQXM#{CaqbmjLU~u@Z)+ zM5J;({M4udYc50zA`7V%D!BteQ3%Hs=xQ~Nqe&2kpqLT?4=zVn6Kb5mG|=>bfi{sT zBW#p;S{K;qVV5L%2&K~J^C^5Pg&;bWs7|K~abTDnNXWg_Zi)8G-QI*S1I2nBBIl7f z!3~8N=~AIm(!;>gr!MdwtMwVN+dCa75Hh8o_9#&WqU8CIUsKXv$;SR`#*ETld$osE z+E}kpDLOzt$`Zn05WD9q3JC&hh#ZjvNuet!!BNSxZplLFVQ`~hIEKJb6sNRC+-%U1 zCa^J*2yVtmtr{Z{%&bv|IcCD5q>4gJh=IZ=6N;KJqe+Eo!N5$YL8C$pVXVdNm1wtv z4eRBA-f67vxmbb}Sy~cAyC77ADxh|xqMT9U#-e(N-tNk^J-6ROgnn2~~~vCRL8nGzVI3>C+JiY0sm zDloKsR{CJ`ta(^B=ok^SMg%psMJ9V8!9p4A{-F zUYhwkI%j}%W^WvAY5i#P`R(JW+J%doeU&k0;s(C=y}oz4PQ5aAyVJdKN6h-kVc*@{ zV{Bn=Qrn@F=yFwn&f383j2exBhj)K^7dg!_=6m{w ze@C^TSBD!jdZSa?;NvCdgOg ziaPVl_x$p(%QjJCZ;$wPV(+Zn=vGvO~T^qYxJ<_dZ z60e}vfmMfBo%`eC*WPT+(e>=&0@sq55~r(uVZF^Byw*9twfRH%qi2M#XejV(nf2$Dru`3Eyla|kFV@y}tXv}s(yq>B z{b}_JGe+Qr3bOgO5(TIwXA)8Uc>J6kq;gnGHktRescXnH%y!^SzZ6HRR=#SjtTvSEv9Vh6XS-)e*ju)H5C8= diff --git a/toxygen/smileys/default/D83CDF8D.png b/toxygen/smileys/default/D83CDF8D.png index b83bebb4e0fab5e37fc6f96bdb533e0eb60c6245..f73d236e7853516ed77630316900069ae5b4ac41 100644 GIT binary patch delta 1335 zcmZ`%c~H`M6#t61R-U9Q9;j)aMc&dO8l+}kh?+-v0=Y;c;)-tdQ!B}9lM0Wp)S}DE z6jDYzm}F4Q6FxM&f=iS zj=fVmdlMwaytM;xmr^nmt0tfr6A+=X+NSwgf|_8AH7)Lfz9i1NR5L~14I(fpr?6&GFaX>p;m;OdhsX85vTR$B}B1XNy zyAWa{V`G9#v@PqK8m>9ryi~6Hc<5>P$P-T-5+si!#*23K+g_&3F(4&#?I1@QDsJok zYEUORdhxWV)X^utm@npBscP4$-8}tz-a^;d&`HBUSAV@8(m;o+%Tr;3{g!%UqY_yT zJd+KJu$PsZ#3Ex;m}IyB7mSOGGse?-iyLVR2J7mMb@Oz@V6YfW^`5EDe;Mc*R;g)} Z-2Znh=uy?UmK(JI2qc8yoBR?B{{f(LMaTdE literal 1438 zcmbVMeM}o=7(WJ90wp0M;+SMyy2P+(dmn{s??z#?*Fwk23N)da3+vGy<%HfHcLyz? zVrC=|vT2A}=9HmRvn4JJCea~VSY*i@48=Lr5VHYJHZ_qNh$Dfmz6FZ@LH382yL;c~ zz32CPzOU0+T9lHsGYNvA6nimU4%XD@lc)mkti=cemJG$}Rw{%VCCErDROl0`S=b(6 z{A@YP_?jDTuz3&^U&FcFiraC3@(KYR6V=g$0wO>|P+mbuWW05(0#~zsjyGu*hDS6o z=QC+4a~-Hdw6L{Yaf`%Ow3NEMEp=YfrzyyX^FkCL2(StRhXVDyOodFEHC_trqsNE_ zUQ<0ytBD7XnNmP6$d6mEbwE)B3I z`D&OSK>s4 z6L>hv$W#lBib(@JT@N83IvktCyu6VpkTN91hzO=bkw73C*P6Dhl(YZ3v8A@`Y8F|f zoRx(}$qVXHvpWU`xqGsqs3P!&Iw*0VC`>&qcpC#Ouh?mm27J-^I3I;$7(pA&gsqUY zSb<=+po9|w8irWmiF^;XU7;4btBw0w~v=zgwxY0`6Yz8w;TXINS4oSqYc3xH( z-pj`9a=`8emV6?XvPdkW2$D+>>SGtsL{23>r=()^OUZF2Y&Gj?q8UdFz}eptr;94gUgcVfj|cl}{! z+qLi?1!-lg{pKvyrm>6DNUR;RmYV-V`)Ac-b7u9~zQgf1e*7injWdZ|t^0l_QontAuE}Xmn6ts3 otL7459-3=?^@UR7=n|B$16rEx?KoBz9E|=k?bcGd&+Iw+56A}xwEzGB diff --git a/toxygen/smileys/default/D83CDF8E.png b/toxygen/smileys/default/D83CDF8E.png index 734e849fe34127eae38719eba412c0a3a59034bb..a3dcad2245137dbb30a46df75a0d3845c6a14293 100644 GIT binary patch delta 1527 zcmZ`&eKga182|oeMk67zNiw!bUS>m=tC-Q{X2V({bT+STUJ}cOqbU;72<0^$LM78g zij-5~O1dXWsff*698&2Pjdp+co^#K+_mBHIpL0IvdCv1W=Xp8LsCt!pK31p%05G%M zun!hwB!xx+peavnm8}fNMxh=I8UV@L07%OK;5(d3dk?@dA^>wN031sI(2J{R_jLjQ z3h?n_x+@e4X-bU3Zzo*&Z$N(o&cLel59Zp-r|*;sZES32+smeJm)P6ekBf6;7mh1V zg=^AtZ?9JZZh7Q{c9`8J28;TTL-G4pm%x@pcXdjtphgk=g>%C z6_c-w$VVZ784G4Q5&_e}!Qq_X@KS$Mo*<%F82h>BiUsF}VuWMM>yYX}cZF%PkNOb3 zK=$KlkOB|H#|EjHgCOv_q|jedwCw3W=aWm)qc`m#!g zuT(D?B7j94@UHZZzY4r6fO#xL$tge~dbJ>_vr)Gelm~a?QlJyj;#)kc8PcR_899Z4 z6sDj7gEmk6&F6yfz1#qR!1`U8uRZf8K;X4kI*`H`C~%;m!m;ACX=*H<7m9@!tGt~{ zg*Rx!W6=M)6-4@jtoyi^Et~tKv$lHR$C`h}@-m-K{=O>zPOQ2#CXr?cnx2ey_I)mOinw<9lK3D9`3d}HQI^OHpne2|s2^|lnBhL5Tl z7~b9~{9*1a8?4bilq6W<7B=nj3cjjeTJ|=2Gru~<$uN(Clt*iCI_5VURGXqJ+!Nj0 z=CRb>qN=s-s;Rfof4Ww!zcB(=pD6}4v*hg9IUEj??qXu z9<47vQ@Nm3?~+@PHC$_FKAZbhSf`5=eIGIJb^fdtFU?gRVXMe{PxyKt6bGGk3deW1Jkb?}1ux zt0+;QTR#BacW+rIkK$gLoUg8GR>MtM$zMIDW{ajZ$;KBGzEXRX8U3pdginG)6|vmY zmi#+6ioB0+%D(ngUPn2(-&px>W^||*=}gVdZ|=PoZRM`Ce3Nq3owyMiQNSJ;HNoQo zh8-&PZ9NDyoj~>W`+o81tI85RBiTc1Kb!^gdUidhwkt}K*1Fu6(I+9+J17;qX;3nc z+%%$(<fX52A#vGh%oW{RG>_EU zm%EimP>Gye%v*aFxY9Uom7G#JWqU})v(1^)$d+Fd_>cWS3w8*l?JLyhCG=LRTBr zr@EfH4i>cgI2qcFLQ!yJww453zFK0IAEK?Rr@Tbyx3sYAC*Z{mF%4oo7E@CbvtThF z9UitI+|saRergh{V{oy;GN{5JcyvU(Ys5(xThvi>FSX-*>Ke0S?cRO_6Wc$8NIyw+ zeboQR#Q1XckQftU8!{o2$u@>)7ZwyW*cM_jEeytljM36En1H)){1A9j0?*x_7slp=JBG%G!vaV|63LRd)6&M4 yX+v}*+d0~ib`Xhtnp*nhNTi3t_@RwGGR|bvR}z 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 literal 1658 zcmbVNX;2eq7+w(}P%dQ@vC^)KPnbxu-3)y0_>1Hv(V>}R$ z!r)On5Va#%6$Go+vuZWi78wn7P;vxjk|jWgd9KB#bf};~UM0789Sb3FP=(D^ zK`)(3*QSCf&5)o}Am$rHFbv9M0$3uI$>2{wLL7b;ALoi#l+E>ID+fbsolnEV#RR{}2LbKTw*PynI)se5=c%!yWpKm3F zI?_hxF$S(4*`dQ=E_ZJ?d&)u9n5c zDo~sg)Tl@nhoEvPf{Kv1Na--go3LUeQjFs=6hkl^hH*q86H71!td5k6P!z}GhOrup zjU_Awa@a1#*&V{F--=bD3`wvwqo?V)!yS-nqFLHzqOBmBCIcg8Q5GX@w|(d;&&y~r zlA-32#yEyHgM;~1Qg5&?hZQ057@$uYO>>p8G2=OXcnKKSg|EOZBDEEWa{7a&srQ25E>C-IL z`hDz}Sl_&#Dv#N^Zp7QSukm2he*JUPKwoFnz_Ufg3xjt_GNC)xASbEN zH%9*ijyUd}(ffCiA2xG&_4nV_)~_Y^ZolU^FbVB$ zxV;m3*nCIT(LX*Iz2*Mc&+{wluCr?=91!%7I-M}Kwx#CC`BHUM@r)3j&O@=u7h6$m zI$PTNKt3KaII9Zh%v=~0z5?}hc96e4YRI_#G&HjSy?F(nx(4X-7Df3+cxQTbV4p&a zy?*2GXO?Wd9Jnqsc}e%m($)(HzP{j-c8EXLuVfUwNSahQaq;M)7@yLxcQQmFWfh|m z!I1lH^;&=R3aibres2 zcB*qWa4V@%BDt~k+S%|ODcg1{dYZzZ_Zsi>mZg@ZIoq#xEKQC+9a9i-^_aYOL|Oj9 zF@K?LQl6hj)D`#C8R}GjpYV~DqXQq@dGWlwptIYk<*zb4y&87n5Vmfu&yLoMnKk>b zcNZk|uRc66B%|CdY=OApVb;Q`S%G1-)1sPE7dYEb@k7f%`5%u?omk>r+_3`~2^0kd U#0cf{p1J-88ax@>t%Ww5CA|y>F7;}IPRd0 z&;XRi@a_{2LmFX+#TfzMe+GcyPyoI|t>6U!ypaGb5dhFg1VEUaT6;qi0GN@9As%yZ zaKJQ;0M^-nQ~;=TAes&W>wsk@a4r3nZE}CXg$7)T0V(en@TpWNfJq|2B?I#`K*$27 zX;A&Tyr$|My|SUDrm3~0x}kyIUfT?zZERvXucWfPuCctXxub8epu8HA5Hbo&vuNcx zZz`ZV_f16>tt^{Xp7C!+lnl2n{k{BcuXS-plJ*hAwGVBxC)qz{**}K1*nk>E2f2fo zvCYRzyD6*exaD0v);dV*1&Iv6TmY;M@M#kaZ-CZ05Z4Co7J(Fo;aiq%aPnT1CV@G$<+lHaBi7!j5ldQ?nskwJ;%;mN9wa$^L)SA`x zttI;4I&<{<-rmRrb#nCc%q){N*E8Bb^r?zg!*ow*?;onDe-AA)heQ{Fmaam<8AJEs z4IN{T^YsWsJf57|`5GGoBetwI5$4JaMMZt*4cHGkL-y-AxnBLq;SqKypRwS$1xtg> zFp|MFT@Z0t0t*Q)ioOwsYo1Hsh&*>?q?{B0RCtg--i(4H`XI=j9*)kg4hV`L*#Y6} zMg~Cr>5E$i38_fFv1Osl58~_@R|TGKDz7^v&E(npT;S`L6Z8#MVkic7h6Nq$W3;9f zEwE~fHkumrtUARUy z=eJcCYL;#ZoTeVh?4)*)G{6LT`v zvp+%b9>;KCgxt1r3;(8^IY<730BK8I`F4>qew?KLeDr#N*gY}FWuz5xI#v0S)(1OX zSV~uc%ge#UFx0@g;_7Eb*u1@JUmr8-=2e+L5$AC;S7nIxqa510%F#b{G4%)~MCea1 zA#NB=N0zmekH=9L=mH1CyMg5ZKet|CU%_NJ;0W_wWKuE{CS$Qv-Oq_7CP zCn3YbSoc_ukY}g$tpwEo!=CE{*JPtafEFV2fBa|@fw7~bLU z>6mcai}cvBZn56|s>!Tr6gW|<m}D>YK|eU6H+ci@(!!xUZ|Thw*Vh z&#W-3Fwdij+nWpAUlCrCm?il@^U6~&W)yoO#X-Z)(*YuYLZVPg$V*Bps(2No#)XR- u7g6VsNDU;C^Yg~T{}|jn?41#ge*f?Ad5G1Dlm6`p0QwkX-C`ZWgMR>{CZVJN literal 1526 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ?8_YO{!IKKaV^|`WVb#r!ZmQB?S{>52(d4XQn1dTnK zn@_X3sd+lGT=Nww6OMKH?;s#DWrCML#y^Ek4$8Z`6kJ@Lgo`_8Tc2M0_RQSPpUc!( zHvc`a@584(^Hmt-#o4MKvh(5^r1?J zhmP6(!scxIO?b|1I_c5n8rti-?qAyOIN!}RcfTAxWTRU(@A3Pjc;A9AU-DS)m@Uz8 ztt}P3XY!U+YR^I6{SGyP2Q*t0c3;>z%W<;Cfy07Jc-tn<;PT+Rygk++c>8H)qeyun z&7Py7JulO|r!ZK44!h)VsU^ki$tin}CG!$qxvN_&s9Y``H=DIdmMncjk8zj)J?XSJf;7{J2uD;~Eo1)*O^%O)lwx_*2bZPth8MEUn^4;D|XUgf@ zSYHrwBI}p3*UMD>H6in6wAVdVNxCR;ZSw2AQzrCX_-ULKc_98PZ_kxyJd0Z!wmdn& pSF8}r9_RP{+1{e+5AF?&4AVoNPI8o7!VM~kJzf1=);T3K0RTrKHk|+f diff --git a/toxygen/smileys/default/D83CDF91.png b/toxygen/smileys/default/D83CDF91.png index 2c748d0463dc9dc863664b674bf92fcf0c6f520b..a306b330edaa5ec8570671d1b780f0b3879af4d4 100644 GIT binary patch delta 1510 zcmV zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0f10UR7KL<;nCaS03UMn_Tv8j z(f(N!o6*7 zt6o`eE&czmwatxy19E&#&I(My?!gtgj6n1Vr;eYaEq z!&?Bs%FzG+{|C)1cK`qY0b)x>M1M%XapeF200(qQO+^Rh2M!7(IsHr(WB>pHe|l6{ zbW&k=Ab)UjZ)Rz1Wgv8UaAhEPZEyepIE|H6QMTM52>oXjU4j7wT}~#(zn;_2F25I@ z%%qvLeVv?9gUE#o68ic52YriQ(L%CG_u|PG6MI;8^T=fur{Rn z!{-6Gztco`0om3NPJvG;0sy_3VXz%pI~f<(9Cdafgzl{W*?3f4)Icvf@nv2Q zyes~3K^)M_8JGrg6r@B+S`e2%kn0=y1gvLZ`afhCi|Wouj>g5w5q!5z8eLjKLVuI$ zLrKOeMR&*mz~wD@<}G${3Aw&55x!A}H< zJ~??f(vDCQnn*MfR512~tstZl1|1xnk-wPsbG^}53+_^H&tA^`Xe4ysX|8IsE54*3 zp90w_wr)O1IkGdt;rT70U)p?%kbfKlOpyQp0NqJMK~xyiV_-lBj7$i?%)-jZz{ZXM z9Gom{3|!pYJiL7T0^C3#C?w3qAR;OzE+Hu;Eh7pBA`G%}@(PMd$|`bls%mm_ahM#d(l#%AWm#ugS9md3_L3|7`Qws!Uoj@C}jPJhnU)>aHI zu5Rugo?hNQAmHom!rppnGBPbNH!U$VR536*Gc`IiHY+eNIxsL#W^A&NAtwp| M07*qoM6N<$f`Kfh5C8xG literal 1536 zcmbVMdrT8|96#t*P(g7j%IfBMW=x&5y=!~49W9h=fiAWh=(L(eduR{+&bzMxc!RM?GNK0yWHLHao^A9 z`}sb8w{5vKH*xaolOYI7H0K%f!I%_z;$y+P;p1W)3@NQ2xbXRMsPi-e z({3HIQe}}_yaZKD=LI;bAYiq*0wpeu8_Cqe8GbDw@K8Jn`#q(MPwUqqvRsi2)o3&k4h%~J7HPf;h9~`LjBi1d!ASXB z9PQ<4mVqOTq?0Y0EZW@q!~9`?pqWo z&$DQa6i2V4+&LWUfyeW!rC;D51WL#;1Fp~j1(CSZ<;HAdHm1N#3S5ILkqNB(|Kv;t z#KrYs!Y5DtcS?yqkrg{<2S{=x5Y zyY3(Vz&fR7d#&|J_nC9621BbYgAc-Md$`!SQ>Nyr4$tlw-Lkv(qoAX0=-m1fw+H$* zpG<7o+utV$H&5-~b7@&s_4xO+`(U^p*a`=`HbdhI^J~sT`8A+R|TVITm(&dg7{2xWS&v z+^5%993B~N3x$-GcN-2lzSsSEExjQztG;i#o}TaBG}Kq$HMDP+I^|es=t@=V$laJ} zGk(kyi{md|>N($gdD7&!nllfocVDTx5g4fByLX&8(t6c%y2*|`{m%YyYjWjCOs-IS zC)o7vFDow0JNjksHQlO$D)jQ$Qz5q|_kN&9b3sU0k^sNrhT3mtWJ$)*7#$L#I%igQ zBrTZz&QJ4J>0d=t^=p`Q@M}S*`j5GrYZA7`i_TAbU$~xD_g7P8=UBLJS`0%Dr?<`> yY`)gjb+(~(igiO~3XBMXKxX(K#+olm;4{!Sf@rL&zxj*EKiO=u8o$kUZ2Sk>CQJYT diff --git a/toxygen/smileys/default/D83CDF92.png b/toxygen/smileys/default/D83CDF92.png index 485bd181713dd88b28bbc19e99aae7b080a039d1..557fcf4501ec7b68f78fe850e9025f3fc2b4e134 100644 GIT binary patch delta 1857 zcmZ{jc{JN=8pnUO%SfWNRT9}mA_x(YL=v@xkgBzoORZ5`l^!l>i|RyiQOlH~6m6%t zt);b7OD!#WO|4T~t16C~R9h`QsvB+Q*L%;*nSbV-=RD8z{ha4{{&>&#eF$`@LB)>oH78= zimU87au5JygWR3H?4?ra00j4EOuKl3&B}uoFc0IXBY0Z7jdKOVx}Ij;Otrd8=k-`P z_Sv|L`gxxBRrSER$43Lf(Hge>b-MMqtyiT1J(GD55L^|3mPckBN8-!K z=02+M3?1@mJUK`MJ%plwSC+1q4CyjQw5ek5&)dAfk9K~W);=$-JQvt*wWiz)SffR2 zPwC-ssdJ>%IYR0bvF#plO_R{0WzdSEyf$}}TKY=4fl~VrDL?d^bJ!^ud=PH*5J?%* zrmhpL-|LI)c8ncAo4M|>ygnR0)`2j3Vqmjj<+EiQ(5P>|tYNaMX(~Z5USZg8@m8CT z!O1An7B5i3Ix>c#-&7~G>a$z1)L9s50Y-YNVz{JgBvz-q;|B#pG`rPFbuerx481ru z-DdFP*%~m$BM|&3M0XrYn1T{#pn4npAW_MF>BD1 zeF*LqKOhPQU&M2s;yIOA!};OS+fZ~96x|BJ{EP1&34=e;VK3;g=5VYQeO3vPT1mCI z0oAUCYTtsQx>ydYeE$fT)<3a~X$)fuL!U&^Zj+d$2x9qu^i>G_Veg<=#P9(af} z8AebBk(4f!Nx30o{GdZIf>5S{dd~L?fogUj$c-4vqH8eduQYDJ4{P#-9g* z?u&L)Az9128~?bKK7D23v|Qi!UCi3RPXU|Ncbfbu%;P7ckxQ0Mr)wa)!&^XLn(rif!x9oh2Hc6H(u zOzJNGiV}qI4Eexd%fKEuWa0piX{x`Pmn(`^xfZBqduD!siE$RnD_+G&0vry;*#3RB z8JpA%*${JGe0zq@T}hEd&ei%|b&->+$o$KDRC_)x${Rg?ElY5eZ>~^b!#DLdcm#Jj zlBhZ1S#_Ixbi-*}nKer9Wcfkw^(V)trzk>!4}Kr&WL2ZiBZuU$wdaEdlM*vbpNN99 zc(y)f{nt~YaEdxa{iwpOz03gX0=L1GpKeO+GFt>^9DboAN&Kzn%qU^Eni{3fsyJ-i zV@c&`oFajeG;dsU+_sUgE0f`RVWPSSn&>OQ6g!jjnm(3lOtTf0aunV+LNsWPjH`jM zBOh_xrNiE#`Q0jK-V+|gOIGssL=&yozc`4AOTWfW^;Yh_!Qa@ekkm6{M?ycn^}$*E zx-Q8ouIA{Xu1VFt%FXoUTvoAeO9t(#=*Z)G!Dm7RfUJXFgw_EEB>@0w?&)c1>FtS+ zOOx;FN=p3lSwa4}Wk!ZQ4}lmSw(PriF6A3xPmb_gO?Vh$O*TC(-Og4tv^J!R*VHH} zMtD_Kg&upc>aDI;bZKsuK}WQEc+5ODqjj{kCd6+Gg*)&M+Ysg49H+x$kI04%P-s59 zKKjI)*V@R6a(};Pi}@DLa87T}+2q}?6?)E<^rW1qEX2@k)J){~Mmgi`WMy~4GsJn> zZHb9**YU+wqEo4{f4@kU&>TPani0KlN8J3&jKtV~!omn{bf@#AxFuoXo#ok2Z>{5| z=aB0Rrv|&Mizv&}OAqH&B>qmj>MDP8ofI$up7iHW=Ni+C!GB8>+WQK_LWJR*(D-n$ z05mF%W=u6VHl=%+QaNS}4xLS=QaM!WXhO})KNgJsvvD#uEGi=D|8I0tQ2vEFBxL|_ Mw0Gq-+6vPC4QN&oIsgCw literal 1767 zcmbVNX;2eq7>=ljOi@6nB3hR~Q5 zz*?tP5fIc7uS#`1s+OwgcKy?D@DvpAw zFp80QG$aK_B2reUv?=SgJRNmO5D;kPlLUH%fB~yM(O}|R1=JB)NAJf4jMf@q`$&6I2)U@OgF@*ZQ5ASNx28VM9L z05(QggC!9HD(UIR5cEccVw~7u8ch_LGKLj4GC(?$q1W5u8qqcpO5|TRKB;X|B^wcj z5;0*(xR$I(toIn0%-!b=*%V1{_$zUgEDD?`!L&(w#6ZX;0xJ21u0wTv2n4y301;Ov z%?q{wxV3@&`dF6u^eK0U(>_53+>*QsFpO zZZHwBL5qypMM=A(Sm?7@z8FVf0>f1pmN?b{VeuG&nc^`cAdcVy3s#{99cD54*~;@V zS_y)qW<)2$F+DJnUq1Q?`#gvR2_+n{m?a@S2SFiE#^s4Xo=n1Fv6wt5brh@nKRIKN z&M<7v@tIJYHAKPotvTYkbMUw3x`C$iXXEavB;6IYmiFtsN4&l!6X&2?%bAYInC1C1 zwj>AL!G4cUDl~Uq-*B<^P2bCW?>FBWUsLelcTTtaLq)lAkArJ>FR+xm+--H-}szYs2}+ zSVK&5=YFT>t%sgQuU7lbh>qss<}U^230JQm+sks%bF7sm_p(l2o_fwh5)U^CWlsP>|ZYnuFNuDwtIIo(y>aG*ISr< zFeZ5C*@~YgsjHryY}{AbP&<8@`_at2wJz04k6h2)gO;WP6@J$s%ZydL-t@wU@m@Cx z{P8;bR{<~Hvyaefn;w;aWjLBqH$R)%a^lj$U9D|5oFiR_T1A`XjeQlZiyLOmx>HHa zU0vLLjGMiqB>e8g>Ag9BZ|sa4IQ1$^y>3sAw?kJ%qbKAu!D($)g%5bVvpO{|Gf+(& zYp7$(4B6C`!++&5hNdOuw-rrIv)^A_C3Oh%gkO9S_2;h|;KHPPZ9lmtJ$=sCypY+aL*$yNUP$`~{gX}9w7pZNUjq89C(a-8DsS^X zLfMyC=HvG^<#-67n)7XWUdzQ<`HifM)&hqgJWuE~X&uJmf%-bFPXYh_1Ni&MXGLCi zD%6tacad*yo6eUl>R$RNwWF_NN>_H|v6dF$`P1z+qME8li~G!1vZV<=YR=(KU#$6t zZ&`U%epLH*!S=--PuRo5i%OisGg@Q^I|FKq4)heSKe@HUZI6Q1-z6=5cd7b5+TI-Z cA)hjlQhHezzD5~cY5Voar7I*SMC;Q30n9hM>Hq)$ diff --git a/toxygen/smileys/default/D83CDF93.png b/toxygen/smileys/default/D83CDF93.png index 5a601fe20bf89a57d9b01404fda712ee499a1ac3..8d6ae233851e4c74dada36614c6f9330e85a373f 100644 GIT binary patch delta 1368 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 Z2@Ekjd-)6W>bXHhsi&)-%Q~loCIIr=7BBz+ diff --git a/toxygen/smileys/default/D83CDFA0.png b/toxygen/smileys/default/D83CDFA0.png index 0ba4267a80dda8d20411694eece7ef58d19f7de6..7e28985bc3a34fa3418259f1d42f70bfde428970 100644 GIT binary patch delta 1428 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 HP6 zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0bfu|R7KL<;nCaS0BqZqv&_)i z-~e*m0CnACuczMSn}f%+htx_ju9wxzy*>2npVj zl-`q+y3^;v%h7+<`J9fWrz|vq)b_#3(EtDcbe=J-00001VoOIvbl+{LZ2$lO2XskI zMF-{w4hkg$B3u2J0007idQ@0+Qek%>aB^>EX>4U6ba`-PAb4$X0020Rl~qf!+%O2- zbBY{627d(daXe#dm#Qpt{B+CnaXe1)VpUl(AQ~DZ%g^sWh=6E3I^(cqSG>$eB!6!=klnb$Uputa=4&mY46f%8S=G%< zSHx3nGN2mrg={jn$~9znNOkB{JLdfzqU$hQhT#H})N&g7NKX+3Y^u6@oKjO6N~YJ+ zF^Vlnr{{QdOIYbPB2FMrHgmipQM}h301>4n>O)4dAuS+K|9cb&0i{AAH0Ht742EFw z)PKh$Agm{dWmG${$6AnJUs39hmjT#V*gHRhm|Ybr^^RFi?eF;erVv}?sQ>U*|sc` z&+enM_f7EZ<1{TOu32+42yDWPT401%6n_+r>JwIuS9up5Mp^U_AoM{~^N} zySt(|&Vy5#e2=@Wa=H=<1#dh%qfy5Qx5gCp`6%YN)P`D(+R z`t5O-YkyuOa(&aB&GuXICH?pn*d|n6{E#ZEGt$xhUn0K%w3Ld1MUpYp0002&NklY5e7MV1w|!g6;(AAWhHe5c{v6RO)YIr9bG+rT^&trElmvu14AQY z6BAQ2GgA{2VC6B-iC5E~a4pOBc8oSc-Hk{TBm%aE3yo}Q7J zm7SB7n*n5|G34a~K|x_rQDFgymB&zw1^~kEH^4J=^8o+=03~!qSaf7zbY(hYa%Ew3 zWdJfTGBPbNH!U$VR536*Ge0#tG&w6UFgh?W$aAFU0000bbVXQnWMOn=I&E)cX=Zr< uGB7eSEigANF*H;$Fgi0eIy5;eFfckWFu5)qTah6r3IG5}MNUMnLSTYAG`e*F literal 1581 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Yk zTwKh}jU8Rh-JD>0J@bl767!N%VfJPM?S<+!!mHQHxhOTUB)=#mKR*YS0s=DfOY(~| z@(UE4gUu8)!ZY(y^2>`gLD2*8txIZAW?5>ATTy7Vq3 ze5B^nm-PFnE&vG%gmk6_n))7r_!w)l!nOy(h zulb~1eDrR6JGakH3*j@%4gWB%`g@$^2+y1|qSI=se}&K7xqn%q};$cOm`fqQjq9PcRfUHeJ@M zW!7TQV%AEtDSdivt$6%b)d*kz?tedcKgC>_&p-KlL(SCJpYmn7PrQ^_)3e++KECE- zv~Sbwr}zKY=S$bwi2tp=^z_93_%swXTtjbTviuv+*d30@#Cq3@6Q~&BjeB<{x8AvcSWmzjJ{7S+X{viJS!xg zWIFmS+nZnazrIe{>Csm)OXi8u&l?tUxhyh2$Fx&WDF5I79gHCiA>a6A`2YW~oZ0wn z<WQk*XG~1XN)+N@Zoo{^b>Zyf=dkz$F0BZ4ZgVV=@gbt!KnwY u-~D~V*6G2f|MmaVZ7N zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0UA(DR7KL<;nCaSFOkdHwwZsA zx9EzF(AwZVm(FCA!|s28|4ul}OFqxp-%g*>Z=1&da%kc~Eq{1gTvlFT&e+~zsn>p^ z%l~s|<3liFWo%S-i`gX{&DY#?v)hZT&i{33=Rz-RX>nbDlm9?0%hub7zu@|hjORo# zg>-ymRa=O^-^$e5o66?qU{jufh*oHB-6b2y)7Y-o>#>WGUUGJmv&6^I*T3QLzToe2 zh>wPvqsGwI%75tc&Fl5a=kb4&nRs7f%hRO+@v+9sX?uQz zl9tmN6Wj9m|1KcI&eOfm&~=1{R%K^YWoLAQgtf=WQ*OP7_CEM8>xVpwP`z-;0a-?rH55_1#? z$VRP49x`tehx^Fn6$Z9Ptk7cxr(Cq$R=A12AXif}_dNhSfRpkCM}QOWTR+X5?( zdFGCBdm3>YZu4-Q;o_VwH$Tf`WC5Sds%n?gn19A(lviAFCz(-B_i^#+@Zxo3>_DER z#(iYMc(x4y8M!$02}Zo2EFi@E$1IQnq=FD^bud-I5IpvIn+Sw;2eFaWM(kOOAXpou ze#?0Pt}mGgA0XQr$|=YxMFF4}BLcQvwNr3$&CzEULg+5~=fR^B9mTv;J51SBL zaeu@QUhWF}zNihB?r`^zPkv?%)#|os5Ma>=A)cdcLnf`NqqDa~h^%8b%@9|ls0a)) zVT2aAO)~^Vqw0h=#mMNs=s(y9ZUwIuBfn*GgZv3FlZXxsVAa6U4Ck-_9f%%?7%k=p zc4Uh!4KF_f(?E`flxRr{;_@4EeIcKK%YPY|{tp?T(cKy1Xl$Gu#dlkz(d9@;IHdY; zlJS<}bjY9`o44dmx0uByV$_54$jCwm;LBB`f0;m`t5m^bKegVx<6^IZriDNSbyJs z1+r31-TaVps58pp?O#Iw0C5P4W zSlBIME7t#ihsceNniQwMMG~o&JL`7)@p!Sgap5yTj?SfZKx+UY|b@47nj3 zQ4rBsJduo`Fpw0MN@ub;3Xzbfv429bRHkW^2ZqHe)f!@1#6X=>G@30AaU7~cyTi-6 zz5ak7^8Bz3qw!=qn=h6tL0HR2Aa1t1{o!~zpOhk8q^tCP!!K}`9@5V{Jpa4^)AB9J zKk>Et0000bbVXQnWMOn=I%9HWVRU5xGB7eSEigANF*H;$Fgi0eIyEyZFgY+fFfgF0 z`!@gp03~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536*Gc`IjGb=DKIxsNN^h1l0 QAtwp|07*qoM6N<$f|9Fx>;M1& literal 1436 zcmbVMeM}p57(TX$xPWMknFz}5W*kiVao6@r3mueR>pCga(qxsWaBZ*jgzJ^NgC1;B zAZD9(2`nbcq60&sBSsk}8v#kyMHDc>FhZ8dz_Eo)a3HYZ7`TOc2NeB-><=$@_xrf# zdESrT``(Avg4YxFytoGdfCTel%qEP_h)>)u;oW@3iwZ*$XLN8yw1*4e3<2ofw37hM z6kbNy2;5y$`7@CX0J}Y;-N895`C1oENpTS)W zkaX*yqbiHs;x`ap@^CFf6x9~kUA5&djT_221ZMMEL4YDS9OS7AAFJhc(1xy7n2Xyo z2;6{ha66hMBf(2`e4sO2qF;>61K+2;k?Ah?vE;91nXi* zKS$C&P*lX7bQPzA1Wz|Zp!^ogwy=-gN|cZ?8ISvAuv9LiC^4=LG|SnDf8E#-&Dv}H zgv>^;bQR+g&cm}mN)~eWctavk@J3t6kiwzh6&UTRq6i;n#&nSIMd~KqTDcZLiF_0oROP=tbMM@gTX4zTIs@oYwm z5e!*PxJ?XAfgAbNk~`QJ0yW4HJ*p7B7vomTu{7pAU_xU`MvpT8 z*wd?1@o`0??l&UUW)7%ZbR zy!|=2luNdqeL(%~xz3X-q2Yv<}h>FF(e$GKvDA(Hb+FcJwZb+wa@k(=*w>@T6^ z1HUJ&t@C_IRz_1p!?S7YiSGVzsEulIxkt`TXJG zQyr_zGb7!q*Ty?bs^?mN92y#0nodmJ>*#OF$^9umH#oeo*b}_HYa%jL5g%-wyxd)S zL^c-*kM^nWv0Y2wDHO*a#=NpG{tM?o|BN)^eAMTf>pq#7y6!5qHh)~jRgNkU&$Z_D z_ovgZzU?0P^U|@m>R%qcI#xq@ikI$ZS^tu&&+BWRXudl?7N|6tl(AMg?W=~Ax>rr7 sp1gVb(~>$TQvXp>V(a(0wtKO0a^Q1%?)coaZcY4OnvDfmXI^RJKifDT)c^nh diff --git a/toxygen/smileys/default/D83CDFA3.png b/toxygen/smileys/default/D83CDFA3.png index 493f9f4df140f48c1c9ad8451c025a85fdef0cf3..215a5a46578e954a68151cc2ce21573cdd69ce0b 100644 GIT binary patch literal 1577 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 Y&v5UL-MRTDR+<0=_>jFXdqwg81f%S7l>h($ literal 1590 zcmbVMdrT8|952?@2W5d}G7Ed$YIGvf_O9>FQrlh&H59Rczy!;+z0wQzu3nF|pz=_I z8#tZL5p_#Ymk=|T>3oE!C~kF|6DPWf&c~+61{&fD=tdFf?TXax591%Z+}-c@{oVKT z`M&S{oa~IanMpHQELNN;6SgpGeB_zVVcu^%q!DJ}QHDY)mvB-(ghW|d2Vp}2lN%{R zEhyrsT>S&Oh{a+%vAja6(43{R6K(+#(GmFF9tO=~Eza1;A_%>MSc}S;6^C~@Vi&xUX@?XpWsz7`^d47 z4@{^~xKq?TY*+n1-$YcUgB9+O&g#aW1A)$!5Woe*9C6%clIWT$g z8Ed4&sj|SvNn4Dh=DR4$qY?^zKA*rR77%2a5L7Ca5e^7SV>Hsdl{kg?({S&cCpwxWE(KjIOB0+G<|j>I*g?WHW}zivEJ z+nZPEL4_97OH`0{rXJ2YQ81Ic=NpPBGTx|`k{DAIWED)y@yaKZ$kx zpPUI9XM~aF_)oJ$kC+aOY@fD1BRm}*6lW%eWJcrhzBU1iHN|OywR!#tKB^lO$G24u!NL1mUeh#N+kScBE>U>w3vo*NmSq{H-Q6P{*SLK`tFyH4 zcbk7~ASQ6ostH1SM~g=OEJ^%wT+MQPS=c{A9!%HHMxBG zLag|dYb84{?d_A8rm-PheW;a=X|TACE;ygEpdC7LjMl6PZ>KrN7W%KvX1XI~ezy4V z(S(GLcG&JrtzvI1T3qd{df407S=Lz#Ou?!=rW!Ot2d3G;%aK>$Cdq$ zyYlvQH-ffUXX+KFbGhvAAHVCpO4kWO2U|X)t?#Vto|SVe$>7fKLS`q=eG`PBivHwX zp|;A0^>xMImDKcpiNe!|!Mq?X$$Wqn@*@dx1 zQZmLf7$IaQy;Sn@{+-V0oOe#|oOjRX-rxD%bAR`DzrTCWjdOCa7UJK}4*(FdwXtx9 ztjM>{%K?q5>cTFN;XIGBM*&cES6~Ot1^KXZHqQ0{TvrBwa2tSaXo|1^z*RT^OI`pV z(gE0q%WgV`1b{8l$QsO3n`oDr&zIb2sg$ z$d7=txk~_P)WaqKeto&FO{Jy0)jeB~^2at26V^>hJ&nzgXJ)=jGQsYyyJNxNRdIlL2Gmv&j^#EgHVf{mM*Ynhz z;<9?$iy_7<#%OtcN9)tUVdm?2%DqNfU&p{OC9fnQJ+G*$xv;9asJf-C_Z4G&3Mv#N zoN}-DPEuxNV`q6i4XP4U|E1ZDRj34pcxMObN_mpzSIOM#B+^}C+`E0M@Ul9=DW9QtbsH~zOD0Pbyl#&TMlo>HPHwRWFyW=Q~N6h54B4x`OT_ zT#J86nlRbjs!6dNy~!_H5^Oj>)^Wh@51NMc+PD^SaW0uyhl@BJ6gRtZz;3{cckiJm zj(oR2fU5WlDqE)nME!({ZW2SlC0Xo|U^eC?*}>8-gO6|qIZb_18WdQGjtUEsQ)Gqg zju{$hlBN9+tf+(=UAp-aF}F4Y(?;8p$Ix_{wf?!}+-{HB5EC67b^qx<#Ou`iF*VGE zl`t{nxPkqO^5cT03^y?(%PH7Jt+}Ubw3@8uE^%1ZH}U{2-pX3UjHIvw zdXweuzo;du740f(g>cKD=d1ddQci3&9V42nzQ0TcNe*3<%SqC~!L&^E49A?7PiIwa zat+cGn{i8{7Hgg!o4Ay)4kPDoS&Md2L1y}KxAsKl)KvTqt2%dEJ*<9O ziY&*>GN!SF@Z$Ib(|QZW08a@Op&-L-Q(6=@|9Yp#AeW*EJx->r1SwvzpuZQVy zC%ylq-9%@9|H+7Ol=G1jR&EYwP*zsQY|nT`htrf}$uF`oD0N4257;xwAA7H>Y6&Pns9`+E<#sJ4Gu@Z;oOrOk^c(t55V~P zg#35FwMZKiC_w642A2RI{55nS7N9Tz-dLEeKl(h@8H>h*T<*g{KRp1pmJSw`W?m8h E01rg&y#N3J literal 1559 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ}+o7WNhhZV(RMZq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztWjlmZa)J9le?#jV@SoVCBeQK!j2;U$~NZ)9qcw^s6AoMCo5DW`7M5>MmZvwz&1)N-sz zZr2CH-}}rzpPl{vtjEOfnpG@y%J?ak6zie_(+2_?RiHjkjGebk| zkMc43OFip6Vx1Q9RVduK!fVIjJzsQw>^k+l`)|i_&3$V%wjKDt=62rRBnhisyZ9D< zUT}N!k6O(S+B1)^zL%VlmU`SNa^u6l$1A^l{dVB$KK}Q1HA;*NmA3~vwEt#^Iaj74 zBsHymT~b~hUv61-a^cFDw(a}p&$%@5q*y|JZtC>~9IFd#oRpnz3+Z&$o7`9XVE+7Z`gjXBs}>-vxt;YEM@`mvv4FO#nF?QM&*D diff --git a/toxygen/smileys/default/D83CDFA6.png b/toxygen/smileys/default/D83CDFA6.png index e3a45c1441ae009c5704fa73602921a9e2b3f934..8a0dceb8d88922701c56a184a2fb52b6815967ef 100644 GIT binary patch delta 1278 zcmZ3+wTf$kWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DO zpm86+{rvy`|EKT2K79QNH2UM$pFq=lwq5w}^+)fvi@n<~0&V*C^Y``(4|5kDzV-b5 z&FAld7XSVC@8s=g5bICed;)aAw;#W@p1zeaZOhFkuYK!b&UvQZu@mSI@sc1vP*@-X z&u7n`J@KXLLy3n z63Z0|it^Jkb5a#bDhpB-Dsl@L7%Y0HhDL9jZNT&Ay4Eh1#mgi4>N`KipMRM?{r<$c zPl`CV@8wmONQqQFd!nuG{~uTTPvKi9-C641`>4D7{vD^C7P~8dyxMK@Z14TN?dN!Y zznUe+ZI~Oh#Wk=o|LfE($HXEsgl4~6)+fm5x}hU-*PECpa-rK+^tIj-IwP+4^IiFU z=b}C##+xg?pK;!1`LRfTW=%_D{o85UU$!=NhvtTL81Ixjta5BOhxwD>6u|{tuUc~j zMNg;Sc(>uxjP!RB&+m5LF1X$Hm~HxlqFp}%<&?R9NUUBp-z!;aa%3Fu(a;oUOWR}p z-y6i=D?euVq?8@E-bdoQ8RLXN4~O|FDq5$QoX*vsW72fc;yAdEZ&{cDOaJpd`YiQ_ zsv2&b$r7r*qQrTPZRPzUxrX2EJ}u%C-ts;Tc@naT;X=0{8^@ej(H_gO*5Z-6et7HO-77e~!*`sYz+Y$Pd-dAROo0dMB%Kaz$_-YF z4RudH@7l2@**sSue90;fo=piYVmo4Zn-&F@)*qallu{D^O+JTZd)IMM<^8EejWwKm zIs|W=_7gR|)7bp~hFs^ojt$Ao%H}Ck8a5ngC=9e^y6kYSZAy-(|D!j&m46-V7)}e$ z_{T4~;O1SnLKE}8)Q@-HUwToZA|ex1YjLtdtI|p$DLj)1&Y}Z1X_=1+HlRY;kmgm|D>|Z^p#%(7p@}Ttot)TFW`@Sfxn^4cu_5c5NlQS!-(!L#j zr}N>wfxF~ohBY7cS2uXZ3HF)IJ-aOPSzmC|>PnY;O&KqDrT&QeAjx!py~3467IAAI zTW}rLlDM5yp5wogIe87+xz0ZIs>gG#E3(vbDoRZ-s-0usJY^H-QoZZ5y-NJ21nJ)S zeCvg6-B;@k?B~y1c(;#1H?vXkdDq7KYk;m)Epd$~Nl7e8wMs5Z1yT$~21Z7@2Ijhk zCLx9fR>r1QCKlQT237_Jc`WNec@&``H$NpatrE8e`AG5i6BX6D7#KWV{an^LB{Ts5 Dq1{rP literal 1318 zcmbVMO>Em_7zMaKmM2Q=-lA?>FWiqvVqG!TdZhwU(F)zDhnb(2_W_}n&yL$w2z z?eG0Qzt8jj`)IW7r9Hc!-i;v0o^T5vgX?4VQ@IPiYnF>5T&gXh(~9d!DH!v-04YWDVGC!c zF;tO#SO*m$B54kED=j$##B*)!Qm#j0WGvuE8?!7VP=O_)S+!R)*{ly+=VhU6ALAIh zu4480unng=BTW?W+JZYD)@@m77RNK0j4R`D=|&1C8HTYr-0nK4QD+WlmYA*6%zY&W9+;A$q%B3) zP@7Rq=>3)tgPv}Lpr#{{En>~wOcYERo)yzL=^}7dwc}dXHmw-=*Nttp&Gvybz+=GF z`wa=!Be|~xhPivNAzKl8!@g`Nuqa|LuS@+Z(5x`;!{8T}tjH|kY2fG($<=dQNN|&6 zQ1Ft?6h)IH*FZCzyTq{_mZYd=o)dV%E5J$!?q*8h7@7;w48?mGk}qMynrVre1WI-l zXm=CKJrK)s1`sXXXxH`LQU^r4bxSw9^)$-GY4rIHMU(Z6x!*3&MzlOIl-GeAGISMP z&o8TNW1sSP>xp2H%qwXdFd)cJkjer3lc zr*GkxyjA|O;Pfo^of^J6b)lH(l4beYjjoF2z~KFF3g?$!F98UC9@|kg7P}w|KguSoBPuTY2(KS@-pdci&tH~? zv8v#eH*U>8v+GW$bVogScWUI^Y-Q`w%NOS!K^J$7zaKwQ&5i8*?nF~-If?9WAWKW< Uf4+^yKehi{VWExxyt!-SA3*2OkN^Mx diff --git a/toxygen/smileys/default/D83CDFA7.png b/toxygen/smileys/default/D83CDFA7.png index e351eff983450b71b05f4d8a09b052e77ed00da7..3b364433632f003b9a5be7b054c4a2c027ceb89d 100644 GIT binary patch delta 1356 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 R{|no{Lncum2SxE&{{UZ^k#+z8 diff --git a/toxygen/smileys/default/D83CDFA9.png b/toxygen/smileys/default/D83CDFA9.png index 27f6c29afde103115b3321dc023b8a9b0f3f5be7..1337f64f071aae356172e883d1111eeb425b270b 100644 GIT binary patch delta 1576 zcmZWndo z0RSrx@D=k{S687kNFvY#^X=x!4NEi$Thr^*O{sx6YArJ`ozhL+$Ac6k_k_nS3!eudg_NMGj%S_5V zkeHDjo3bxLp$wD7I5@kk#b6)_D%~+ix=XTK?jJ0rP-#YLoQV^2JA>(l(bctcVEBnc zZ0zifP0g6@Y&`=5Uy;Pxj%u>md>fnR#`S=nY)7L9O2bLE)J=FxcMoqSivyXUY;h0< zimfSj=9X5FMdxkX5lB>kB+Mruh-71HNg`h(BS`<$3vd2!!rjo`od=+7=W|rvG9f zv{)OJ$p4SDL+hW7FD$6Xd+NhxW_o%wnu9_eH4}(lvWX=24MNZRqUlL!$Ci zq3V2sW?Xruvs_zmBq&0v4(4R8PSrvr+X^i!JvPUEsf3C6%h;()0fr;wOB6p`LXcwwf6~3S-a^_(^jv2W?PJ)kHac{p z>s&Z~H>|ZW;4r6(lCQHye4^+edILTTjOU)zaTCI~Oc&^|U_q8g@kA>nCp?=sW!`~` zWWl>vEeIi3nrfIPrk6cux~J%4Dyt1FOJ;eqmpPK;M30X# z{e_vezo4fse%UU4JeyJUpXHX zMC<6`BZ3@+RqG&Oz>->1Bd;JnfQxP?&8#goIwp8%Bf`U!=bqi2JNZNX+lm1(PKiWH zHO(uzEj7dSXP(}9uSPR7ka?YT6X#zxE*<&q(r9U~GapGJDmDf?%`c>-It6c3tn=-H zgW`JIPkjisFClA06+c839i>vZ%99mP0dyLjPNi+7Iyj0QXs!$=S0@Ks8qJkP+v+^A k`Co+iglJ_<#y<##|6zE>ifT?c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztno8rZYBc*lZK~@V@SoVCBf&jg&jryrInf8o{>8_imTJ!d*J~ zysyrWkU4H~{*lG=q?5~{R@4K`l?@>Szb$*S0DX!ib*;6*qnC5bO+(2H^J=8_uDVOSME~U(%1j%efhS{r}QSV zT>F=PyYz0`*8iLfMr+0j#2c!H=p=jm3Msn8cKBcq%aOH9EnYkMuUk6dq!vSO zlj$e-z#Fr?98|YWak!<)s;S1P{dTNO>_Y9;VYfx7MOaqJq;8oico%9L<(Sm+z4H8rksUb#o`j;~3}`A(aD z%=XCrsxLJ4f0adC$9-nD*ZFMcx34zXyi->+SZ{hf*YU!Nyu!{*6XxA#ne1xL98%W} zVGsHx=(D+bXUVJ=HVr*066q(NwY@0cz2^SwqG>@XjZzQo5`AsC52-r!P4Ilyb3t`W o=*^bcL(z+BPn`Qb=RcmdKI;Vst0Qzk)g8%>k diff --git a/toxygen/smileys/default/D83CDFAA.png b/toxygen/smileys/default/D83CDFAA.png index ccb34e0d9c019c1aa890cd127e4ba98b4525cadd..81fd66e2d21ed043f05142ddfd572bb9580ff574 100644 GIT binary patch delta 1698 zcmZXS2~d;Q7RNsV5@iVwWZxPr0g?!OtN}zYmLLeE8Uh8^U?LC&1x-b;Gr>%yXbS>D zwGU*`l(lM*x&1nw=}f10esj+`_niNoxpV)Q^t<%) zEV!Bw1UWeyL?S^efgC}Gp!-*KH{v!SHI^Adi-4dE2MEgi7J}B0qf7||eGNm_v7zxdDG10xbi*UNuYxnmH7A4F@Vg{y8D1mXMf)zYFRH+_V0p=60)ya4MX zEZ(b@HPlZxH}>ogociyz$)?7Uj;{Xxp_i|Q#!{J*-Hz{WmyOil7hTUo28MmHPw@sV zP8h*w+6Yg%PM8-&qaK1mx1(;SC92a_vwa)-$>&@CYEMmk1PRd~D`P^w1lR|Fpbmt4 z$U8bG=T-oV26$MPih|tH(aCpW#aoGDSfUu3lzj@@y=Z5& zjI&(xBuGokUwxPw7#Bb5AAa1~5B7V{eSx1KSr55v?Q_!Qj(M7wxma~uqWW!7?{R2} z`_>Jz{fM{qau}=*cU}F$VcyqT3LAC@IF)$X3r!%gwT1$}dC}WUxf2^_iP>jvfPA@M zjUx_m2ZckUB1Rf3XJD0sjioqVF;%U;N?KH(B}}Vn+eE4m{G@7s;H;Vxzg9UsDiJDH zxGMFMd_f^p&W(#rQ|kt#$_44bU5<$&xfzYzsBtW8Pml&cIe$P3G$%BIf}TQSba0!x zePV7w5Go^s9>bx|QA4gbrhvMPRJ@#+NmtespH{wY^Y2o$mhbX@%(r$2o0*0$1xo)iA^&ii&^zqjqx z4lL^TVlB$esOY;7GEB0P>W}bOWsTLc`XoY@jm}T0m$T8Q>FM+B^`utHLCqeA+WF+~ zlY3i;7on4F!EalXy*_QP3kycHgSHphd*qw%>LxHD|AT>+e&%sPwEIt;&j*c9eq$ zWV=cmJN&lWJ$_>YnfmTn*lIYh-H`d8jlSq-T6~?$*cpqO+|6X+^M)EMqt)=O;Dmw9 z*UXR+n?Fq|-kP!=64}B<^OA`pY(Y(y=4RJ(PKS=h?!YIQ<@hyg@kiL;KsxlL^k9_|7@4(Y2^=rN=0C=ECb;E!!%1hi@l+9x*_9@L)OY)%JH0 z^@$D{|D*P-{+!wy`AZfR^_`XJm9MfNmNyD6ZqJ?=EU=o~!Jtwyx#6-aHV=$U0(RbE z^J>j_KUC#N9L9;u{Pz}(_GgUk_k~{ZfQ|`1EudZBbUz3l)r@wvE$SKCD8v{ WF>W1ens4W*13{q_YS7)?4Bj7+yENYb literal 1669 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYpg#ns#uDXvP9EB~G_03K(YkrZKjX787=S_>vD^}*@CC@v2e>$^F{JC#sVt097 z+?t^hZ8>4Lb^gJ3XSiec))sD_x9)$xjr0vaOq(f z)BgE7;ifSsKYfY+tz4MmE>xMl64;;%IfnaZ2n$1W>}}RyZi1RFCCrD#`7I=l^5+ktq{OBMcF}%w}WY;g3sZ8#X_qd zGiy%Mc`pU$@!LMRQ1Wiy(v1BK84bq#|N2fFa58B;b>xVg?>L8Xkzhg{+qUb<20>is z6cU!M+u6Jt-F$D|)yGbKyc`h1TV*oqrEg#e#}P&&yMuCvS>m=SJ4%uYVKbb^GNel}&Gxe2lo)=n5%L(fDZ5)&B0-(UTipUi{%b75#SA z$KyFE51$6#UgsX?&9)&TUZUh_-el*898PDldO9bqWLcBOy+>1S>1jI&4FyTo2%R*G z6zOM4oEG=$)I-b|J$ zMn5fTR$bSHtUm`%Okba#dND-z zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0Blf9R7L;)|GBeZ>EeX5ty`_9 zSJBFH#lC6(`Ih$Xjl;ZWl#f(#Y(D7VhSkn{-`9co@sVL(Hh)!8FSxU5!n5N}nG^r$|$`psLIR2Od^KKrJt$Tn`b+Yo_#cB7<) zHe0pY8r2lG-RELGmcYiQvDntk@rFZHY!cC-n^e6=*MB9tW23JG^GPbd{q9>Rb%h9s z#(XdiU3SIGJR|vV1KEu`?6pH`>$bL9gu#tSkv7>>WkozyCk3h^U&tmkZCpckhg4E8 zGVtD?USv(PB@Gvtgw)e5kMsmlz@~|r`6)FisbqRR40qK9>5Rxrwt$sv8F2u4(wX-w z62Wub0e=utXrMo=NOYtH1nNIWfe_FtG(vpNO3kPcES`Fw3JNv?g=JKGVUN9Ffo(;r z-(pU{?W0WvA1Jo3NT(yF$^=keyy0+tcJ1I?!y0*ZDoDBV{y~ld>ce+b@qA9Ff1<_5 z0K4u{0Rx)Yt27nH90*Qp<#Qz%)H!#-IG=gg^usGR4wDA>K!jB80000bbVXQnWLaT! zWjbSWWnpw>05UK#GA%GSEip7yF)%taH99phD=;uRFfcbZ>K*_903~!qSaf7zbY(hi zZ)9m^c>ppnGBPbNH!U$VR536*Gc`IjF)J`IIxsLsYf_?-Atwp|07*qoM6N<$g7*RJ A)Bpeg literal 1315 zcmbVMZA=?w96yFsHi`oc8=%hH&H0Ayy_8;iCmqnc)-_UwrEwTkdi0*c1$uYh9kwur z>t3v|VHUq=Vm1@Pmbfgs%ot~I5<_CT^KR4`d&_+}-p1 zpZouQ|MweiX@0tNOZ656K}rLA`4C*o%%|7_-w$PS0$i#LUz^dY#EgWX0i;n>IsqDp z3tb=t1hN0+FF_rG6vm`*o6#0*VxvmjE|@y@WL$-41gWb}szS5}7-%Qxl4K8dYhn^Z zCDDWJcLs@|%7Jca?|=qc2b#mtfu1NMV)c8_x+Dt;;=mBlWV~0_*`xgVr=L z#)f$Rnk^`Kux`UpSsYI!6840{u4r92$uNw`K~XlSVbl9%LrB_WeOI1=2YOVKR6|l^ z)MOMol|I9RK~Gmhh^xWiIDum@DR|I zJ}nCC5!;mq!`$83kf{j0VfSegEQ-*}E7867XT50sL{gNyy3H@=iJgd?2K$BhpqF+ouk(3wuzKRw9PtI`Y3~n~Zf0`wK1Ut~& zuD3oE)`thkaAGt#8uJ&+M-Zf-HNZE9lhd8qT&})w_o3mGm-yXs;e31fA058ut3MdS zJ3ey^E-qb8rQi85l}_JhADaKI)w1nm%yY2fQ_Em(>D2XKKWQ(0&GLjbvu(%S$A7%M zJhF4=)uXARvp0UuY_%#s9RxG8VsvIs1Z8D&vB-Sn*!^Q?ANd{`ufBTv)w}w>+4nVZ zNnIr09?H%=ICcBhWYy{VZ`-nrGbl9 zulI*Y_U4q!*iK}#i|Omvh7Y{naFpCrcxvIE8$~Be8XBJS7dK@#tDnCZ=^g*#Lgb}; zU&x#}o+z_in?9G4igrJHzF>37ku%S{ky=FG8oM;J@ZJGyQ`3nEeWhmh%}td8qBR;P zDn?2wzq4=6;SXOa*`ED-8857OMgzJMn^wNjaJByPEFi?x2iHd6x&ox YAP-C>zAc}2jGMo$fUlW<-y1pn55hXkJ^%m! diff --git a/toxygen/smileys/default/D83CDFAC.png b/toxygen/smileys/default/D83CDFAC.png index 6ddf1db3a1df08bf16a8da40ae383a88b8ffb829..3effe3a9c4bfc1db323b11d64a3628ca37f5df00 100644 GIT binary patch delta 1464 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 nArLN3Pu2dXBQ7B_hIaPXyKWCd literal 1778 zcmbVNX;2eq7>=M6b+B?&w5Tl0Oo5RcyCDgQAd=k#(h5YNh}eTASx5}YhU`Lu1+5Gy z0)vVu>IjNZPFqkBbi9x%h*VHebgZa&AZ<|a7%ZcfZV+sLIR5C)?tb635SYqgfRCbUT^9RJsi4{DQ< zsd}6pj*~>PK|$9e-hB#8=kDW%EQ+)@qA-J+E((^25{hIkuA^kAKS+OJDb-353_*O< zSIn0N3K0n{h!GB73M0Ne7~#ND0dI=qLs*`a%Yg$C7==+G1W91ASjv$~5TQWIK_Qg? z9xKz46sA+)Q+Cy~-AOF;QLG3t;21?1A_*dKssrRIf+9#2p$Cu%KH#}Zty2<4(#ukw zchRD_L7jptr3OL^OypOj{(ya7NXQXO5RNZ`!j`y&0$3tMq3W? zVl2?*RYOv%bP=!k(dnlnu>ymS98z`3|ZQ9_Wy5>B_AI@()cB_cHtrr*9o<0H1cUZ-h7B~V$FRGR-$ttUKmDwCUoZQpc zo=iH{{%+;-7{AmE_mn+XTZ8$}G~z@0*sC-5uQqRsitar3q``fCJ3Qo@G_x_I_@+HC zQ$@ncwToK)PHD?ax0~;^b>)j*d*=-viEYU_qiZ`?emt|oxx;LmVB1oDWAomnTQlby z-LGabZ8|m~EfO%eMSlwrDtjvwc1ZD>%BBa&7dB>Gs%d04c@r_YuD$Cwq}2^nA50X) z9WyvZ*)4Bs%ee3Cs=91det!0e^VY&7b7jndHDA0mjyxRxq~*Rn_GEWnnJa?vCzzkjDjM>WYgu$AB2e1CdsWP*dAk zxA8knn1yKW+k3c-q&LQphPrtt-ZCFXIC#}=5SaF_1^R7zYVifN zQGkz=Vvft4fj)`N(&GZN`0t(7{(q_Kw-p49J!pe=-EFSTI9T5tJGwU7^JJ$`WwzVO i?c-HcmW*oF+j1B?|MW1Ynml_fe<7J96#YdUoAD0|AGZGh diff --git a/toxygen/smileys/default/D83CDFAE.png b/toxygen/smileys/default/D83CDFAE.png index a94e3a66356f21b02a9b9d1b50cfca2c8f135d61..c1256068eb07e797fd749f94a8be94aa21bb918d 100644 GIT binary patch literal 1749 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 diff --git a/toxygen/smileys/default/D83CDFAF.png b/toxygen/smileys/default/D83CDFAF.png index b8aa1e16ef9d1e47132e58b703c32daea5fe816f..3ba2c9c5612c481459fc10fb22fea681b30eb5da 100644 GIT binary patch literal 1722 zcmZ{l3s6%>6owZFPvxOjL8%f2gc1UQ3eglLyc30hfhbnc1am{BU_u&R+G2=~1qv8Q zMJ!B5kDiJLe$4+4Kf)rM<76^tgIkoW@eBfV`B(a^drb>2a*n00wO#S z0`mGmMjI>#v5zeUS9)SuCxN&NB)32V{{aYp1IZoW+{1VL<2*VVy>}0to<>zFl*e1M zbmhoCziq%4cOvGCD(>JdIm=21324#XK#V; zUcg>~%s~`I(^5C4!3{njpwrgQ!e5X2v5LuEtvh$gcdEC199&)X>gB7K)m0w{tG6t~ z>zT_}CmYOL7bca`5|}u?EK3;DZM>~)V@aS~E$onwr({G!S_KE%0;->v_PDlzo+bF1 zUV;ZeJ?U&vuz=2r!}9oC&gn=5D@foW*!U*xX}w;(^7_@w3YD+_GKA&%5jh z#mtR+Q+4rG5minVA4J}&W;>17J_0&L!MuhlQyW+G}lG!v-K+E2E<`7C9B zuLv`cO26Oe0;(@NT7E8!Jef)J^1Y@jJW)Hs`+_k&7E-ULnsTSq%&%5{78{1$*C(i1 zw|zPJQ+J#JIwrF#KW|l8U-;4B)fOw)yX6s4X501#r`he%8^}f~IL@SVQcQ<)VaoH1 zu|dSg5u$2SJ#GZjn`3f(3tv{8uWhNH$!qBB?WLR%bCaxax~raD#l`WocxMB3hWO~` zH_Xem@}hm24Qi|s>d{XCls=)=a8eG}z2=(sGVTZ!c4PJM^vMUZP*A?z8n%Q>2QRMKM`foYw&@3@O>I+Js$u1OHOx9a@Pxj`yrUnC6U;J?R_f>+%1C<%C&`ixFXmzwsor&rx8=na1@Vzz%gJ!W((!35rZzXA9EorjYWuX7;_aI;~ z$iQ5Y8df_n(rn4_ImsN~a;@HH)k_UvS}Ks@MHvDcNfUtAR%myjUKu#Si!%GbF$4k= zDzsGwK6gr|&H^yXNdh9ikf(Pf?LZ;Oh1+m>VnJ6Y@ke6^k2p@)Qwm@7H+Adl{{_Dm|wO!g` z2MK9N7v*;9nR*ywgJ33iuQn7=WV}IFI4w+3h(eswyKSVMR^c*``NB6?45$D>#JEHu zPM1qDB_k*>Se!1vBq9L@3(}KCL5`PU5v4?sgbS18La7)*lmdxVC{PMvTqMK=FfM_E zSe4yH6Lvirv}f%sgD-9UL<34!uF zj}|ALmLk%S?xbwMM1E1rOYBSK3Na$baEVAR48$!LVL~ifst`)$Ng|~Z2Pd(H|C2L_ zaRv%B$A6k7c*JyIVEdx=8R5n7kalKboXlwKojPJ-vDn*HxLoVKLGMs!XtQTu8uv^4 zI<>XspOinYK4YH29a!>s;|yMnna;~xWsVG;YT@v~(7cK{p_=zLhq1c~I@Pz^Q|Bn} zeJ)y*^KN72k^RG!BI&tp#nhU?TkCKbdfM#&sJnRZ)_TcOc>er`{OUHMH#A$`yeXlV zI|F=Y`q9d?P$Py7oUOPU+tKhaGKSrTx94v>W-2LB&u&##jcq8s#JMxT9+phs%6|5& zbm7h)f2{*|;4o)O(GbzP`J@1dz&md1=%)CqOH6Sv+F_{Z>DU*mxK+zL#*ml2V+k&|6 zzKi{hyDM6UI;mwdpUg@tW~;x54LPYhph;M0y??%G!y!)P*0_cT528~b*14ghcS?|` zfs$U?iv97+-kvggog1NT^7iv1=HA$UJdeB6*LE+l^>oW4Z+J;a0{YhIWAzDd?=*io zWPh?Dx@WNmv-(TV6pkKD9eFm)8}mN`zH8r>xS5B1ntL-$-n_WJz7{Olq&)`}6g_|c5rEj_q`xhO& zFYew~-DLV^eO}!GbF8ARV0A%c3!WO&#m&CF{?C4&WcA#b@h_X_q^@bLQYCj)zR`2! t<6A?&%^vfIZ?EGt^h_D6Av4EAB3Nbf)c#)4*u21BU!`1*A5-L){R5zIX=DHZ diff --git a/toxygen/smileys/default/D83CDFB0.png b/toxygen/smileys/default/D83CDFB0.png index a3c36cf36aec5174e08ea9b84b2fbad96dc424d8..f6ac7a2f34f4c273aaaca57d18ba47f67c60ac6c 100644 GIT binary patch delta 1174 zcmX@lb%=99XG5S$&hHZ7N+;KVNa`#^Nw-dVWwl_Alb#C3hYp;>|uDNsbODg&&Pp_`sf zGj=|%dR2zqL2Gmw7?|p+LLy3n63Z0|it^Jkb5a#bDhpB-Dsl@L7%Y0HhDL8&V!-hy zU37B`2g{?PoaBGc=KP%V)W7lXhgnoGW(z?j- z;;1a=@8qoZCy%0>)770`X#IYE(z&zmnSimp()mSvs{yliQYUBI**|!p3a($op z>D>86hFdG|n|N%+1Wte9h|dptH+W2LR{rFFyzYYZ-;e_Jy?4(S$@Mq4tn;5$e4|dwZf*X2rkfh| zG2-$|q%NN(0YM3OQLwGWlhWURw84E$_yfy*J-okuj*wUYk{8A#!}O z*29{e_d+lKDr`xny}zOOsq z7pW!qHaDQ=6VM^5C9V-ADTyViR>?)FK#IZ0z{p6~z+BhRB*f6b%GlJ(#9Z6Jz{nC}Q!>*kacg)fn9j&HQBj?Xfx*+&&t;ucLK6TTA^`RP literal 1359 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 diff --git a/toxygen/smileys/default/D83CDFB2.png b/toxygen/smileys/default/D83CDFB2.png index 8fc6c037a2ca1ed11ec387c77a6ae993684bb784..dc90beb64e38b3cdbeae4dcaabfc5a11e2dfc677 100644 GIT binary patch literal 1713 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 diff --git a/toxygen/smileys/default/D83CDFB3.png b/toxygen/smileys/default/D83CDFB3.png index c7459faceb77d0b2e8510144fb4eb593003e1aa6..953c7d9ff64db412d163987046e9161028a603e6 100644 GIT binary patch delta 1684 zcmZ{ic~H|y7RP_#3K&F^3^!;%jtGM!5E60-1VRW`zz|4SkwY#;PKhhWNKhF!3XXy3 z1TiQQHOjg|oaGR738*-TK*D8!D9mySj&gV{GBUrdsokpmW4pS({dxWRb@zMS=eNqY z3N85B005v}w?%@3P8v0g3IOu+u#H4r&_*ROSYZHA=m-EMr2w!FPL(VGz)=hUEG7T| zsR{thawKhgDF6UbMTD{gHa9oHH9!5M`{}@Nd(U9s@EE8)s!>oCPo8%?RSvf)Mjv#6 z3!V?D+k2GFUC;QSk4&X8n0rs26M>B16&p@z2*@7K}CdJR7f;TX;wAzM1g78Ocecadbr2jt_U;r}&y$DmYt#E`c zUr+D9jz3`j&AZW8(`r%G7>Bd;c7+}qP7Mr+PtBNFSX^2A^ssj*gdHOg{#aR4cj#yl zV{bIk$4?}#UR?grp;Y??h23aqn)!95Z)6PIjhmcK3l8%jkek{Ror4o=?>}A~JK1|KZAC(+vh?OZOd=^oZ>-)a9X z*2CS4g28*)Iy!+b+5>Y|fVoh4EGGC#et2KG{$+6RQuWn3EtOIm!W?agDj#GsHy$X~ z-fetZUQo8n%7ebD$Q11}_pRaEMn({NNZ!khMQI=wr3<+)G0p`ygB%D6qyYk58wMY= zpuC{40O%alXp4g$3u|Bw09yP)HkZdrJci0SlAU@eBL&4P$VoxvXXXHa*SU*Ps%8mB z|GRmYbpyEm+>oK5PuDVVtSO54mbu|A4mM`3UgF-qfmQPug^Yi%e*6aRrlfqe z+U2bs?3u+Cvuq=`SMEvBRr@?mpksO3Bt%$ICiUqQ5H9GJR&DNqT`!5g2G7WI_dI2r z|6nfD&KPg~m>x}*&&TO>j;j{WOOQE5EoYV?_x>i8oR$QC0~D^%6<-&}ceS>TD#yMz zZY%pbKXr{3n7eEN@8>%#B_dCL<59ePgrn{#8&7;~^vPtkXP{2iR4|FtZ%i*WZm^v= zDC)1j{pL8_?(o~25y%_}W(z>>H0j%|$Efxgu;IT-oW^wiykOB&Q9E&Og`Pa;#qTISB1Y2QTcb&U4AiYAbW<8qf@Lsv}wDO zd#k;UXlW&*uRMx=%_AbV7~QU5R!R{ve-jDh`+YCJnEs?LrR=aWK=JIF=a}wOzi3Yj zlZm?uCVN-|i6B>!)@X z7G0U8#6b=CFR$V_Ex)lTcXo7WjiGO56*D~BM6=6Z-LA#=A3j^Y;Y9u!{GwAqG%DMC zC@BHZa!fVFoYsyG#A8mw?vZm#unc2EubTS`b9;R~&2CD9Awoxs9_P&|hM-J!475%N z8!xy2MIaNmpc%pw)aOrM4EUfhXfK*I8r_Mq;9Ht*hg+D~Ag!ECc39i>p+v%}3zAC9 zOFv$zxOCCRsTU;{T^7ejaM`ge=lJL=*A%tfy)jWXF_Bi0oQ7c~xBhwUAX}vw95+?J zQr#XO+27Dzdsj6y@xmwZpVxb;|MVe})MUZq$(WbDbGt+L9M|@jGLw*cksf)lkPw%{ ze5-wA{~{=z4$xl{UEz0HkAT@a@B+BJRV=*KTPm(9r z1%n}BFa-SK_WudU%uYU(TJZk^=!aQ#V1UKHPl(J;n?!2KbdU1l=sBqpA+Fm9YCIH;L25`fl@rxJPs zH)pOoLqu>mV=bg1iAmCZAU9EV0q)fic*@0l>`tBT1+l|5k|;K1LJ#f~LIO}|w|nCn)^;&^;y*WDs_iml zItihka8c>BiLHkvXcWxm?$w68imW&CB{azv1-D_8Dcw#u7%isYv0nsc(kzD|2*IRE zM1#syYF1FHKtuzpq+(bF!WxNql;dSs3_?YyMuK86riLIjEJdVBH7Ju|O0@)($k0)& z*5P7shlv=qOR{!Hu(DTTZZ0ZYx2bhyU~QN+;mXQD0T$9(>zfcy9ZD7?wr&B9!$vVZCst5Tv7g0K*eKv&{os5 zapR%uX-C~h?BLW_DzT)#q5% z6W5^aDzZ?khef8{&frUZEYa0ZISpWK6mm1Hntx=7i$isf+t>R#4$R9Ibu3<cqha zqp1$rIKV9Lt_TUsNJzvB8bz(`=Pxek#MVq*TXt8I_Fem$18qH(yLKi&(zHZ1{r=nf zf?c;m(~meVTxxF!>xAC%F$RVg1k`ujQXa@TylGB~F1YE;jh?=?zOk0#nG2fRxhaz* z;ZIxU%l)%=$d6v_-?eBd`bJeo?e(}WXx|;(qwG&S5vv0?O#M4QA1 zAHGU33}%rf)BKx*N@Mb_NgqUIEJUb)8>8wB3!z2{l6bb4^jtVPzy z4VTZtX4#fE74B7s%N6$x4cEF`eUpMJf+e;4wrwx1tR!;g<{k^1KkfX@rqI7GPFvO2 zb@I;MN!*^qQ@j1zDy9xp%_;i^iQj)d$#{IZtxt9&CSDnjHgU9*3mIq2#DO{W#-B^R?ale{V9v=hD{M`x zu|Cl84$~z!-phY*$&`FrR!r7aR{og!#f6LUIVI7xm+zHY(A&2>CyKtGuFfwnYCFhl oF38`){bh*Ed-fJq68g+9g@ba7`}X%|eD3{4Xw|V;l`6r1NT3OaK?>zc6e>ZkRICFgkbo!%LWqD!P>xayC&JUEnAWe{O3P~;NiY7oMW5NVt~^r`!@v)_FC@a^n;eHxV-=Z$1)0064B zPN0flk?uTq0PddC*b>538W-*rzylzK0zi5u03TJO^l1PRnE=cR0B|Y+z%Z_?$=?|O z2*b}Oh`YVLt=jn)TXwc9R!p4b~{b)VzyC)X~(kB#>xC3c=h0XJSgm5$GgK zoQe4XLo5UW!RVt2<~U0{(ahLn2LcI)!H@`q8dMDigJIDa3XXs`H8;i>l5uz{k%HAn ztHTjU1kwm&sJR1$fWyDg($Uk_RfB$pDk{z{g&&Xspe70M4Fv5^vnzIgju;ZQcMk{T zz|Fi=LS5p`chtBiUU=-C?|Y^?%%2sQKEm>Mjg}$$Qoimd}P(h85&U zhe&@L^DkE9XAL7KFNo4MXAe?ien1Msho5_19Exkyp}b<2G#}40qmwQkshJ>=`oevp z3V3|ubx}FG;%q95eLHFlD@&k9i2J!t!<)Sq?vFKS{tmanZ%AIcxIi@4(JVXHo0KT* zrBJQ*@-T{ zBxm{8WOTJUYVWrvg@;bILJxXYxjO2!%njVi)IkrL^xPgTx&&Uxeq=)%_Vk6GIU0Pe z%NOpw|Me4XUoxUFV_#%rcfq+vir4X{ks-BuncpkdJ562;8{+HohByxH`Lhw<@fM_>J zZif$p2V|F~yPH+WI8$vNJBu)K(*%na{p#WLUbkDg;z9|gHWl}3--McD=@aNL+nxyO zn~-kP@eZ2LjtHMli@@N=@Ggiw|t_dUA?17 zl;*$qPDXQ2se%NyOCLntIQuq;bveHw5awJQF}~(p32Txy#+*pvTtM zwx=#$#)N1m!?|28oGflc8F2Z0KG&fU<(n9##nXzijwu0E`B!VNJchJkJc;2_eQ6-k zt|a}pteeI4c{SxOokHz=-!2=Q+G8KgujoR)j6Hh7;*je$_}(qY%`eXDaBxy)tOy+h zw@bL6=o4LbDAj`y45pPguDb0zzjg_W1xJW|M)Bp^FUwS?kR{w;iAX3BJB7!KRRnCA zwzk$xwl&K>h{bfWb8xbEq%)aLOy)7zozD;RKSNBcC@LcP{|y!g4Cf2*mM8$cxxSoQ I55bv#08WHJM*si- literal 1495 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 BgL41? literal 1559 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLGjMjE=kNwPKDW<3A7ig*BGx}E9aur#FG4?ko^1{SPBTp$S=t+&d4uN za1J(8&O0Qv}Q! z>MWaPGB7Z?d%8G=RNTr5w$2xJ6!`b8FkQIMMMB!EMZjs=AK{Ek4z4W@i`*7DG|3BU zOw3s9p&8IJ+eA=ER7{susMl5JqQcGv3VrgOwRLmeZ#@0x-pwBe=RC{HFW;BH|K`lp zt=FZFy?dW@R!W{xjq%*e(*Y{$FOKw|X5Kr>4(VRSUk7eVw*F2+hNT|HWL^j>RWr3Z`DY$7>fV_eB1gx#X_166*0aU#chbr7wE2!_v$B zcHSHxcmGE!Rn9TxC(oa-w=mkSZN2tj*WoHDjK4`iSs%Mp8Xx+;F UV!y-x8K9!t)78&qol`;+02;16WB>pF diff --git a/toxygen/smileys/default/D83CDFB6.png b/toxygen/smileys/default/D83CDFB6.png index 956bc4daa1ef04ba41d8242ee2e262a40f2cfcf1..685a06f9dbe3ff15244cd5fb6ce8e024bff59a8e 100644 GIT binary patch delta 1107 zcmeyv*}yqLvYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_Cf)AK(){eNZUe|Pu)iHU!0 zY(D7dJX2SH=k5KqsOX8jJPcpXx90dH0!7Z(E_N`ey06$*;-(=u~X73xbW z3sMy-atjz3EPAJgI!>Bnz_aJKXl2vHl#@2?2Y*akCG+`D$!y7KsW()tuOv)J*?MJ? z>#x6`!+#x&-#V-0s+{R2|8@U92&vsX+TDJBU#9TQUynb&P?~T)Z_1PlNxqx6o>;bF zt)2SnjVxwjy+^kt^@;L@ZuE57oj0kpca?X@@p`v>md~C>_iMlEPTA$c;*xw%vR!ZY zBLn$~H9H#L9F#44Ga+Wx)=85McE~v|aWxCg>`;Hb#3A$v_su2CTF*~dJK@xY?vgdX z?@r#5xQ*v9tD54^qLaq9jv^a0lhW!ueJ=$V?UicNe%(}3)iXbbQTwLb5ymH;>2dBV zP1Uy<1p+hb7ucn!XkB7r`Z|BH1BZqvm!cj2vNQvh?&dn{L4c80}4Kg=R${i?ZhvLh1b$8va<-`ihy&W}fheYRiuLS^|v z-;ARV=kK|r`tALavKuAszl(l8NjD73y~ochv&Kzasx|ktQT@#vGpp+REa%Pqgm-hk zU6Hr!D$8nVuKcXj>oX*x#fqK=yYhQUDLoW)j*nN~>G+RJqOZXBgH~2VVT7WB*2VM_ zo8RWnSXh2X^rOMPMPGU5uo+1Q-JIn2=#=!QKaDj^*Nh8($r(A^yvusjP;SnlC%f0* zx|FJt!n4w@r^F`JuYFPd493W;o3Gt;`4*lz?#Sd}B^Z6ov*^J?*7G?hYhFD$`rpa% z*O7xglem`mP2^5aJb5_q8|V84tp6P@Ikn}S(b9f!BB!9h?VsDt_`lQsZGWiu_uHJO zm*SgM?)uMKebY{q|K;uF4%=O>o@HOT!Ae>7=6=<5Nc)Z^`{qJ+15_RjMijPbUmIstKwM0ZNzHw^W5{a894T>gw_6!V5zq_Ya z|3X~(^IvfbFx|hRBs6^$(3B_91UtMqiS28RUWI61>{ID8mfoh3sL`h0wNvc(H zQ7VvPFfuSQ(ls#GH8cq^G_W!@wX-rc(l#)#GB9Af_n{d@M{a&fW?ChN4!DN$W)J&` Rit1bp44$rjF6*2Ung9p!-EIH? literal 1276 zcmbVMTWs4@7cBRqwFu~SMt>tPGm74gH*p01*)UlH-h?FYH)I4m`9Q!o&THC>P zm!yQGw15bp(mt^PVhABFnlyo!R-qwUXo2z6N$`L=3W5hNLTE!1&_0xKoHm7rY7bbp z&-su4@B8nr3=SN3wYIk+2;z#K5aVz?;5;pR;k)xrRf5Z5JDjo;#;~1}On~r;kp^f~ zlQJLt!3WX4JO>FhU`uFT8`CW=AHZt79Nas{IEL0# z?9l+WT3r2sZ!+gT3BbGe)+=k*w71}9mTbvP)>4K>_WLARy6TerIE3?i^(Q_b3{p`#9? zls3lg00uqX3PH=pVmrjTwVf!KGCVJ3aneKJn&!k+)3)q5_}7hHwXI|!3-CCwjB!(j z^%(A|gJJIOZpcxD-f)kYDlCdLCK~d%26Q_r1~B-=qo@i;kt8GfLrjEcg)kIC0>MNm z!B0~HK}CA$I>+u?{(tlX~8S>vLP?a`{;03#I~`@|H&B+oxz>v_)oLck6;Hn+nv^j!p`sj z9Zrl1M`P%9w}v1MJyDTQ=2z3})oQTu=*opV)iVFY(Ps~Fk;KySWShR0J~KP_PG+(B z##gVk-~Q3nxb)jizU}16U;M8A#Pr$4m5Xg{`#a7PALi$t{}S=hZ|^N`e6ski{P|jW z-st>PMP6z@*!WRtv-QnjdHwc*7fUwXvqJld{$%pL_by#}zx4QDGv7~7?dcjU?yG+D zLG|$EQ*)W0Z!jZSyXYc*?^tG$M zZ~pOXrCQqD!*;ulT?v-%dFs#ZSI+h$nW@Um-_`HdPH&XcKXe!OJy-eq#%E3EpK)oY zCYvry<5wya(y@B{1*+Ka^wrgy^Hu)iO0R)7UvKJoRlUDZSh$$UEHo^i%Uj0|dZ$_* bLi#5gkd#dNmt&8;?EH_S;Q{e-=*-kVVgt0c diff --git a/toxygen/smileys/default/D83CDFB7.png b/toxygen/smileys/default/D83CDFB7.png index 4fde005983511d9978060c84edf759f7fba13824..421da920ad2dd4930380d267280f7df6a6ab0392 100644 GIT binary patch literal 1606 zcmZ{k2{hDO9LFysrV)xTLS~FSN7UFdBb9Apl5H&Un6k{662~@acw&YL4fBv3dfDZu z46-E366#qZvb=1Ol;pJxQLnPR+i^~BIj4I*_x|qv-{11R|NsBo3y${IQm7p$0Du(P z#)<;%)^8n&fTNbKj59Q140Ahk0IE_YS9~_XHO9||Vh2E!CID*K}FBl!+PSIE$LAj_inqG?x(!TrSDEgVc^w8HHO zR81(TI1S4D0rdP141xF^#>i|&z}WAQ@UpI^=&tlbFqO5wTrJxWCfvvg({2ELE-^GW z=CBacQ3Z}06=6|GGaI`xfdc*q)UyFp^&Kd011J)5jRs16{~^~4P>tZ>e%R)kST(-)Mj$b9!}NKLp|BP_ zOqCihckJd%E|11ee^U5Jai!kxxe2@6+p;&GDpUYYU@77KUZ)ktKB|>h1MqOSQmCRo@l+2p@G=ADpM_pN72DM47J#n@T<`wa^wYCCDn^bM!LV`)Q zBT39kNTYU1>afq003aG2<>Jny_?*Rrga!Mb3Z!G0ks)*pD<}j20zcE$sV)bPTxG~p zEyP4aiNET_9w^PH#_T1`xOdc5&Z@|5o^CsjSzTF+Z+>TJK>RKUpwITkt2G zJa8}Yx$IQk*sSa?+mXVU8s$F&+sER(U?_j|Fd zYO6VeOUz11v_qC;e~iz%dv8s3NZR&n{oV|R7zMtd_iG7<=B`l zH92kRjL0Vlhi_J-A>9U*vSo=VN|AAqgQk*D$T@R~Ms!{DjV)w9Qo#h& zA~o_1BHRhAXGekPlQ~xdUvZ^AeXX?pt?!9M0xs6*ZD~|-rNTtXWL{HFM_GFRxzdIk zZ>bAuF)Juq@yMOe*x^q5g!Zp3+!&E^dtPF69@-lInk{52a~JfWpSPJriVgdOnPU=X zvk14HWc2`@J#FS?LE)W0&yh!6rPFXP+&z@gBL!y1u@t8Zd_IRmB_1KU@~KqZ&(6uY zxw#yU<$le5d-5!;jm=XLNHm(TESGUnY73u|Vn^VxIjx39x=(v1)Y}Jml45AIz@Vl& zC@?%MsJ<2M?b$muSy<4YE+L9W=kR#8M;_)-Sy;GntFfrMyEj(`<&HKrZE8Fhy}E)B zjgPamXm`g)%YcG@wWavP_wMj0yO<<*Ce4RQH}MOjLj&~j`ue(fV_iK%7d^a*fsu)k z!EQX>1dlJc{?+(D0YSmEQ~r@Z4tTs|nG6GzzEyAz_Gg~;38MpZTCgu2Lk{v`&?$5u VT4ZP!9bWDLkV*Dd6&Buce*>=0pqc;x literal 1497 zcmbVMeQXnD7{6_@33NIZ1{;fV9y&|hdLR8hSGKlyl}5TP-C#{nw`+Ud>S(Xr9kc`N zhvZp#=O+?cs6)1zmsIZP$beev6gd*euifuy_2)E zu-U^ju~wFGwr>2MEr*~im%Gj;+AM2G2j`J7DIZzD;{#|2Dz6Cm7)J{$!gjXF?bRW- z`(p^~cIuFNr3JJ2jBK-e^%kD3-BMNO*wW(AI+2Q1aCv|P1Rhpo;DBd?S0DpAWR{l% z^OPAy;8_>3MTguMWwX@4MviA;rA%Jtz;GN^sbsi9sZ!ykFoEF&ih)~IhAT*=iX_zV z+=YN>ywgQmDbrjmu+kyTqUa-0)bIDp{Bjw`H=(#zt4(nbL>cfX6I#6@6DacvE7A-U zD>!(!Pjqu$IK{}=xlN)D0ZQ+u;PF{34~V_OT%kb8&;a8@aT$hsJgK~9y#>+A{ww35 z-a=igk43Glz-{6kU_V?d(qK@#M+Ztd0yShc?*^N~Y@j&DCJ*Zs&6ExSUt~_VlcaGi zP7`t(SJDKH8WFC840x91p`937pUvFol|;H8@}~7}Q1@m^3C@X*8Pj zX{^~Rh>X|4rsKLn+&QfLkyz5mvy8~`bsV=L-2pYtoX82yoDVkEs$glo+w0`~!phY4 z+|QO`dG}`4Y2rB#JX>GV{gC)_g3=Nct)^272prcFMog<9)F6LMuU2Z2Ijr;l)C>h; z&{T8$r&-b_(1EGxgVqO&2Zx9Cf)m4oqp|2?$IB3uIp0j_>jD?-)5&B-)^p*jL%Z{= zcMm}uWqB2kUBW($g%a<5s$E<8TJ+w)z>O#SIvTe1UyEOkmWC43$hzptyy{kA{89tF z7xVwVeLJi_7zzc6k%6~ss|mHNJeIt1gzKNAlF^L8zR1MLfzc|-zPMv@rZ~2+v$JsO z@~h562igZG8~)t7d5N@cSN@Jr@Qs}FXv8Mj(UUp$y7bt(=#}7?vEJ36AKu@6CR*Ha zEMw3fE*v!NTNn)I{w$~$O>GYC|GQ)P&I{<%8A>PkWXip%rCjtAc=)$a(XMzo(+FsT+43p#}k7yfmz@#wsE6lw441k!B& zsS$g&>myBe@VE7n(u?)-S5_s)hI>AdoUiQdsXP~6C?^O4lU3VCvX}jubuvx7nG$G^Iw5^#vYSi&Y|V5P)oo~e F^B=~LFzNsR diff --git a/toxygen/smileys/default/D83CDFB8.png b/toxygen/smileys/default/D83CDFB8.png index 584ba696eaf0748d0fd627d1dbb19d899bfd1e94..649899dcae5d5bb653be9b7a95ecc4c742f26439 100644 GIT binary patch literal 1551 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 diff --git a/toxygen/smileys/default/D83CDFBA.png b/toxygen/smileys/default/D83CDFBA.png index 77ca90e4fa8338adfd476d5f0a794f1369315768..e19dd827e20c99a6b9d5f211796235e5d279d20e 100644 GIT binary patch literal 1583 zcmZ`(2|Sc(7=I08h8*PV+b0Sf@=@M41UvxY>HtK=0+)G*k5_%c{-1QFj^|tUqO79!OJlDSvz?LbN;FYkxj{Bg*3oP1r70_D;>sL)#t%V0Jnm{^}xUV|JbG^k%mNdW!Zlu zUqhf)%szBp?GJzV)c(x_FYfTU^^u4VPxU*Bt+@l9vqd035Hx2P%vEa)mGJucYIA-B z5>w~|d)^iS*gf*edfXbV>VV_NS9^CITwWuR`jqD))|RDL&u%j2+mct-!VYn7ZmBoWA{&2b)e969xr)Uv|L|hnOLQ+?Btiy2_aT|x+86`y;yM0mjo5aEv z@=Edb{pMrCOH$09E<2@7qNiN(C5EyaiR3Lds6=QxogY1|SmEeUrz4FiHtyr#uPz}y z&8%^j^HMug+%GLIIv~As!BWehQ*O&eOe{Q>v^5WR zH!E>O))*Sp?Xh^ys2n0BR*%^PFbpLl%B@sMrnVbXr(lb_sagzdO@&I9VD1TlsQ2SS zF&nORF~OnTql22tlWpq*%`x#;M)M5VS*MyUn&Cvdk4b4|tWBlfSe>Ky9Ijpc6d>W^ z=Eq^NJlF#F1p^E0Y7$gyr`1WR^S8;8GcD?DJkmi@%9BK|MB*G}Law;-BT5P{<~OnC zYBgkA(pYWB$mY>e2gc-tiU)T^f5#Fd;JB)K>?*FV2;-&-4L5;OMX2dW6a}@+Ay)Vp zCa|nZcd8v&E3eKUSvm@C<#f&_1dU)xnAaA<^9ZV-L5^!Oi(Xe?Xv-zaZgT! zCi98wrX$qmqFZllsKF{`!`ld>_$&Vt18M;jRGPbAK{({9jH^L^kH=dBl!hA0d9nA)u zC6*}1@g_qfmS5(GkzgW_6sgt-$wSgYf@@I%48|H4E|4(_GiUF~_ZpX=@!>T6Rf(ubxnenof!%5VPWfRbyz_wub`|*H-LP+u#v@D z)}DEXS;y&-fn|1)b2C$O#zto*=3zu}3EJuQj<(RaeY=idu(m&$qmei{IVmb|z|0_W zV`YOM76XgnH#dLGj)ryYNOJ1di)IEd3mtgv3En3w4X+Lt^g>Hfqg#>aJ`_6D)H{R< z8K95W*VjQ~boBNT_0XmUhNg!4+Gw;X8ZG#GDdt~-z#t!g-?0A|oQbhH2npnQ6`X^7 o>1QY*RDksf@}i<}ffPR~flBcS3vQ)Cw>SVeD|^dIbMm=A0mHzZrvLx| literal 1540 zcmbVMeM}Q)7(Zz2h%Jf{5pa=oLhA>vT3$yHgI6b{8%6g5d+&4vxvGG1%kTFn9UeuAnYwtw?B-3>~eSS`@Hx3 ze$V&S=jLpPox5Z%1VORc8wm?oJhnt|QydK$Y-}+NXFDl7 zZJ{Y!RmDX*1A;^)j{E|lz`R*&Wt}oAtRwR{T>uS18G4V4vX;>TTuj>?j1GBlrW1i3 zHXX7}VV0X+M!M9o(aX_!-kf}^x6G=sA^P=jhDQqsoU}l}9%nhjYdtz-hF1&r;bRnm zXHp1lxY zjkA?#Ed)7h3rISoR1jQR6m`4ZGIyGc> zT!MpT;4mXq%vK6I1bF&11gFbven!mjvxx#JLp_uW#bk2S=?up;qsGmtIg+E zxoFfv^K2z&1@$ONj(|b#UTi3=2)xm5;T)hSR5`&~E1fhWWD`0Be3984HZ6`}NBNfIQfAvK0Hc~%6Q z&F})nSm}se2e3Pf#a@Wj8abL0ST3Js%Of3-TgnP7U&^{*W1bRTwavlUSU0~qT%M=V z5;W)7LEA`wLIC-5XEI?_}{Ok9!|V-@a0bZ{oqJKF?8>(nC|O! zXPaZX?c;>4eCp4=6_eif(U*o>-L3xOcl`xXM}76ac6z*S#cR8)ZKYG=-mSVTHDZ19 z;B{7?*b#m26l{8YR_;F(r25vlAcZXd0Y_VjO-UXI==9lKd=yCfRoJ`=|TQt&qZ zy&Kh2D|?>Yzjz$&mr5^$4kZ2bWctF1IU&nD(SA?#zAdjd-E5?Mp@t64n$}HYhdbAH zEMvcQ7STm&i0gpR^Sb6yBfDOHS2RA~r;ht$xBb&}bwT2ROh0`gWn^j3n&V|-4~7@* zS^UY>U3u~L?|H|;nUgUk%lm>?chB#MKiN$zicbuMYVgUb?+)F5 z^b0>-SeQ8AKe)!fy`-W--VZIw6<6+n+k#1rIYW|DH4m<f-cE7cWy8W$&EXT3kW zZt0V2ld;te3qCq9Pr%;D*mdqw%NN%E=9H;9p=Hch?UU0g$(I|^eNi9xUE-G}i+Z8J za_NfKB}r4iy?yWRnoY7=&8lm)=i&oBpPyWnzZnWt`9qSxwcBD}*X8<*sfm?e|BxGR zABg6*HXn;US{N6o@`V-#{0q)^NzHW)P?Q)d>^Tivm;X8#{x4^na)_3!qTT-hqFFZx diff --git a/toxygen/smileys/default/D83CDFBB.png b/toxygen/smileys/default/D83CDFBB.png index 0f1b9a7ecdc2c307dad849d54d2971af7361b175..5b59ae95931cdf73ed105fbfc32b2af6096acdc0 100644 GIT binary patch literal 1689 zcmZ{l2~d+q6o5A|+$IX9LV}ivfT9I|2sbDM0wIQkKmsHpn2>{VDlTSZW4PnFC{}xXK@&xUG}muUl8*WBk3`eC?*_dO~~@P7)&UY#)t@kr@A00 zHB(W`m<3bh>#C9|s#n^;=)If^5)7SbtjkPQ$n&VHuS70a-nvzFu+L5qgZ_#Y|y4*w4s zj#x7!lIu$(lOw-g247K!+;i%8s`3~(oN;htcNhsj_p1sq)YE&N6}vQH1Z8-X&GG5zeY2p>q_q%vR2 zxuYzBF)>2=;+*zR0ih*$Qj5xR?LWs0a(EGtf`kn0F`0FNm1xpv2tgD%j6Ee}QC_fV za>V{?ZrjcABVwncE@&$Yb5j$WZI9m#`gQEIx5XmM8g->`4nzu#LI#^m=1<>*nUYEm zHybg-SURJ-9`}_2fYju(Im%eRiXc-+#c7!$f-+AgBB--u0Qha)6-TPL2bm9L7k^7K zMNP{l`Ch)T7`>pBB++mqSUEDC?-Z_5~ z%SE;5)C&^HjlbfL1vb=tFwA&0Z);og-HKv+cFonXM-=tUW%Vp>+%qhmC5cd9`zE>^ z{b=TOhZh*P`~3p53CE0}WzlwmI$-SXDzD>RvhPHOju*Z~x)b6eQTM8&4)e_M*9#KO z^=PxBj|QCHR+bPrfw7-PvXRdn(i}UqnNQ7|FEraAw8;kOP8~PU&h|DwC)ZJb&N& z2z9l32s@xxU|GAGwhUr7C)@~7?etzbSFt%R4DH#tX@3{=gR5C&Yz&wJYR5 z6I7U+Xx9yl$wjk>&#H6}T@qw?(E^zrN6)8XO1mG>KkU6fdfG&F;N<0`-p6_6eQ)U5 z@w-nB)84;zEvrp=I$XqF7>L7Mms)&6G;AyW!Gq-c7yLE*sbmt{u3y08=@Bk2O+ues zQ{j3chT+}mL_=D@7mKZ1v@Mlw`UWhq_4rJ3h>x>3ow28i zONy~@tgqQqUe(xXICQ|&BP?lQ;3D7XA})(&zqo$?!RER(Ob$Df_r1_yFz`}V#jc7^ z=8Gl#G!goE$%Uq42TnEJx>Z^=cpoA3FA^jbgb8-cA-1-bEajX(+}?QN@KEjG#eS>t z)uz(A(%vJIHNjKb^YstQ`XJL=#!cNVqm_5s-svw9#3N21Z`tC!-3|w)kLRom=O~4I zr6@q45Wxy~LY|&(kguDE7uy30AbJOQdo6{a00HY?4Yns>q literal 1655 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 K=d#Wzp$P!e4uaqS literal 1413 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ}+o7WNhhZV(RMZq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztf9{{OPPUzajU0`V@SoVq(A@v+cSqvm?g!cBFx6iK>H8V9h^XVo34F$Iuo$0ssS~rTExc`62I=Qf#SK%LLJmi{_ktj-DzVAg$ zEMLjJ`~Pq3HTceahr9n(>6eBpd2 diff --git a/toxygen/smileys/default/D83CDFBD.png b/toxygen/smileys/default/D83CDFBD.png index 5d2beac39003221155fdc85b83294acab2912a32..724799d6e599b6275a22875feef123951061e7c9 100644 GIT binary patch literal 1704 zcmZ`)2~g8#7JpEX2nJ}WAP6QB(RLLK@u);v35O&LL?B#N(S#6=NR}(9L_s1TM=C<2 zRZ)uv3MC+?fKa3g%25g{xCKP6U2T|l3GBx0+pRm*9Qg9{~bqC z7iuAX2};``Y7XjpAZUComD389FQK|?Esn^)4*4G){|Jg&Ai4}5y<7t%R6|4G8cW>r zAAC(0TvU6$kwew6*U&t)Pr&cv&@6$*A!r_jjtMB8rAZdUB?{=AhSv))un3(qAXdTm z>XR}RcjiMVlKOoTI;U8}3iweC0wwy*{9ngZ{u8TFQy=1I|2!}M)3rkd+3%ru4n~&! zWvf9Gt5E(C(mz7^Ec_;ep{0`(s{s-OqlUxIrAnWXRmf4p=>>SA@EB9U(?RH2fWal_!BzN4 z4QH3&_8dr*PGd^w7>Cyi=uyHAHSiZfG)ov&!kfh{lI6ej$)Wizv@79?dhNq= z=$(f*iX$Tm;FUwk9GqW)QKBCeq;*=|vG!MaZoZfJ=gh*Jrx)WcX9lv`1z}+h`NM}2yRse&jYS@H zCP%8T_cc}Q+`(*@`Ejl_*Dtrc?aJ9&n!mNp=IG6BoLnFJv0{<&NsG;uE8??$O`G*y z$}*1D&D;uJuq-lZI($kaM6Y0_y5qZRFTJo>v#Kr8t;CIK%5-THIykHEZi_y(Hmp74 zK+^O#&LX~vBsEU7OVfwckZpZW#6-Vd_ocztu`Or6$$X5iTfRILxO9vrP)4F+bpQIht5Y6W4^KPj{2`P4Tocr z}J z=JpRf7|so~Tka(}F7N30BO+IrrkM4|9=tFmOS_Us#98F3KT~}&wNH45`btZL^yV1F zndGzS6_ZY<2X`|oqzCuD9g&X8B!BddC8OSL-ehKa^ZJdPXU!wYT+LZiFZb{ehGB~n35_1xBC@|5sOc3Wl_rx2fq@+>=+S{6N zNjO|`-aY262TwKa%cI|~&v-^@`?9dx@K|>@j*zscvLZHy^;EN>wui6OzToo0syT+P zG1*-)FZ@kBb)%oiFB3RMBsei?v2Lf1{bCv5P?%R)^h-faeQ9D6A%H%5zm1kpy(u4H z*gI+Oxaa{tq!3-{o$c$kj1_*cp!5aUS zwe4OXTRd^kKH@&YPCTB7$FCcDm;7G@PGoR+2><^RPA8KOAq3O48)%Us+|!I07LbA? fgIHJ!hY`y1W-)^K(QPbbd;m~fsLmyh^rU|RjA{Ot literal 1605 zcmbVMeNYr-7+;|b5eD#^)L~uH47rcp`(js)kGs9Y!#l(S1+|pD+dXcB-97jAyaRIp z2XazRGK$Gk5=;|K{7xApCC$Lb8dA#;mCT|s!!HIjo2fnEtUnz8XlM8Re15;@_dd__ z?pxVe8Ij>Hh64Z)X_|*y#XdUlg^d>5xCeg|VizkI@`W7EA$SR%0@6v&Mu8@VD5R_u zL6)rSpppR~)IsOw3;7l^X6G0w5wMZ^7?+3!faFx4i?A0_0%)TOX;ur}X*mgjG^vI1 zR2I3#rKgi9EL%)S_&&wYBih)B61j!$;DSKft8p_jUgIv%I! zRM0Oi`W?aQo{7cuJVgi`pUZK@!3xNBasuaeaxPGxqXuW^(JaY%-LC}FGkjW{;^|cs zY2-Nu9EvYSKgB;wQJ;DFd@F!7Cfw+JY2d@d?X`LE!=0{s#WD4CYGSK1 zb>Ne#&bd9~7HN9V8f&;!1IusJ^-rs9#ebJI)y(#MUU2_--1FBySlllsqfg!D<|CGa z#Ke2`hAC$uroTb*M`*&i6(>X2Ob^2{z6HwnEPv3B{90Z0=!@<6*6#Rj`^EOvvXHPZ z&-Zl^?PcZWTd_9|jwK8;vVf@Zq|;mR=O}Mvlx@`Zvk_gRU5&|owbB0M^%0G`H`f<+ zCB;Pa&J9TamD@a%Js>=&00%0XHtkJ`^|WPuRu;c@r{qx4 z3A0?4GkMq6so=7vv_oxE7ZX9%n6Y$?(hwsjiG_IJc{6 zq);Tm(J9r zr7dYQ{yeK8@v;P~y3#($e|=hSvT|Q}-ImF^=KHtvBL`)3iL&RW0NtgJzNzkAcr@^1 NHyN_Qs)2w diff --git a/toxygen/smileys/default/D83CDFBE.png b/toxygen/smileys/default/D83CDFBE.png index 96e86051ca4b6c4f29f0a4db167ddf804019d850..24ae90ad45e890e04c3ef0bed16cd92a6bbe8e80 100644 GIT binary patch literal 1825 zcma)7c~H|?67GZ>xl|7E7*L28qCf6KQP4z)5JfH#LvYHPP@|JbVS`d)w4UES5M-uqs?WY?39 zs>+*{0YH`Lgm*((V{IwQpnkWpx;sj;K@KDb0B-uaKge=utRLv)Mgq9H6M)GA_=2XG zGXPNtz??sTZ4Q7=#Dk{OhXAm8t}Y&g)zwwBCZTcLf55EUg5rA3CKNFh+9)DRG{f>W zf^ycOAZIA~-jYS($-=n)0K}Lz;IFI!H5&@pg@9c!>_5F0{>v~}z&V1MA6QYqCTPu@ z_<(^uU@-r#_&*rkUMgSp(a6^NS@Y&ayZTwv;t`#K4xhAccqZQTljK;t#H&;4(=PG+ zNn$@Go#*tdPvXPlX?U?{*(JRokOWRiXUjy8z=t%ROUL-rsu`tDsc*L=@`Ln!#SCPQ zLKGKbUc$gaeTRtCC8148r^{v_>m9_r0LCl0GYXSZQPZ4btArqscuh&fWg<-42+&&q zJ(Gx5xH$-IbLlaTGLUQF?c6X& zsF5s$lwr6z09Sh;wexVJfa}Eu*HrL-0&T(;X1jW{02o~mC4kGXAczNkl|aqhMM{R+ zmZ#KeAafz04g$Dvz6!jH!G#Te@un`f!1XS;-3ONxz+VMa8{bWA^+0dOn(lTIl^gbK z?c9!@9kbYOpGL9hcd@9^2it8hi`m)Nzh72C+vtX1@t>03)FU`G86{1 zHiD`j6B>bzPrc*m%z1#QjEXgF;xj0nht*VLvMcso`QuP>SYlBwCBCvIgJSo!X#B{H z{`8o8mK;K&)d9=g;nx*LI)Rm3AJ;kj%&7BloXwdR;8?=rg#r+3l*8ei2e zETWtw^kKi|{hi3^y07@Orc7XHul0|0BZhk|`U;H;BbRa&R=$SvLsEM8SRdm*Kc{_U zG|(H9+f{Kn;wZrV4ZoYcB+McmD0SZfoc0**P4=)#?M$nc~z-C+V>>0uejkrGl|7ib@YE-VOKMK4$q7tRE%Fb*|K0jVyVmi>ct!IcUMx- zI6v>oYp!Lu?XR|&o8O?(H1Mr{=`lX(%5B}EKHOLG#-n>mzp=aX0#_LqlS&T0%kB!g zPD`!&{4%)Ezu$HH`G=PbNF$zgwZA$4T=kYzH{`O+$|EdQw%eTIZnjn08tlFMJhhxP z@k+jSN8%ahu-g)5(WVq@sjGk5m&ARpzJ;Z7nYG2yS_`1{7s`)^N;F#{3OXc#UC+6eJ9^_IJK* z8|)oT$dnOGPpm92F;+f){P_8k`g>7;|9d4BBYp`%q{j&zOizA$+b+W; zQ$&ixl|3pgN#fa3DKzRQGh}j7G^L}dt#vIs>+YR1iU^|U<>e{l3x@`WtjsMf)|nuR zjn5kDqv?z&M$qTw6(uu-y?jcLXC1th)#+YFDll}JxmZ= zGYeaDD`fmXc2`BN@NB!gf1REIuM*si- literal 1691 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 literal 1635 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#m$jA zBhdLSj;`irj>e`yZ#XJJ^`?*$X8J(K=z|gmQeuG#0aGA|2~YY!4m|Uu<^gj|5io14 zW-@DHU|^c;>EaktaVsY{)?;#@MBTaXzR&BnoHa4k)oyyFcCx#xY1fL?sSyeF3p8Es zX6zTfsKlYOQ0TwYl7%}jEa_gVDkj;oRNBMiUPy$SeQ{p#xjUEg?Q`$@#XB$mR?8m8 z_k7R!%5%l@KJQyF<bAy2|x0g|vKtQZz|Ajj%FRskL zAL%j)k4UP=vE+Rt(CiQC?#Tbu&|Rg5OS`m*^!T3=C|ICt(h)|7xHmuUQ{Z{C~O5NDIMi=!1?oQ?Zk*eKzfB(Z!uId?kGCRe; zv9S~vKiKrmNi3du>bKm?qzADr{VsbIn9u5Tikj zP?rNtuLb@EGaNsE-otyf;9H}K^iNIr#|bshre6=LsyqD+eY)5A>y@gl wZMz!T-^qC(*Sas@fvs)Gzf!}$;SG!oX>S83x^Le!161sLy85}Sb4q9e0Gt11NB{r; diff --git a/toxygen/smileys/default/D83CDFC0.png b/toxygen/smileys/default/D83CDFC0.png index 8bf69e29752a198823504f77ce4a7e5f77572406..a877641cc293b72320abe0e494ef80509fc5a8f8 100644 GIT binary patch literal 1926 zcmZ`)c~p~E7JvB?KoWu>7{jIw2qY{CUt|eOSOP%^J7FuEB#^~`m;eD1#Q_vrKq3yy zNW~F_mPJ#l;|PqnW2Y!0We|15m9i+{LUHU+!OX{;`J?kk@AmXVSQ~tBb-Lx7>5XqloQDT;9MPURj7y9l&nyGBmjHu0Vt~g;8(;| z_7Z?X7=U>J0N%|2kQK)-#7**oDj$+4kC}~gu4U$t)Irb0&ne95HHc*x|B;E!IKl>j?Y6#m5Wm-c* zYbb~aA?9xhjRHG1L8)8U12S#a6|N}+p?(|kyE;fgnl z*&np1cI}NZJ>sL+9Afsd*zP-l>l;n7t}Wx*p1_fP5xP#9t}*&omBW(^TmuVy!!_LJ zLuwA+S{+8MiDon;xYoupJ2Kffa{VS0t_!(apQamja6plpagi74nSxOnVxwzG*PW7R z_{{m5#2<@;w5d)90xaq{gjWCcJM4}k?e*C-C$lOGw9*}9>FPpSl3W(bxw=~J$5M}< z_Bgafn(b!d>U=N<{YVqZ=mY-bSGy@XHSN0Cr7OYqL!RYJ(Z>6!8?GmtHE{67Fs44x zbTA545kUTX66uv_cTZ%yq=?3dyKY69?NB1nRitW5L896Hxb3v1j;J zg)HJWBA_pVVL${Z@Q2Q9myyU?G&@7Qeo?t7}VFV(BJm zOGZFUQd|(zIML#q9N7w$86LtNQ;+G^zl(vA7a43YYhaAE0|R_59clK`z44zb`NCp~ zB3~|%?G{s%YK54h%2fd1scA~!)lb4kmzGItsIsZ#uyb`^{7my)C+eRT8ZFOW()2gL zMdJyMS4%%R`cFOhnzU1;TF!1b=NpmUW%1Rq)ILp8TP}NR9j9V>50N^r{Y!q+#2HxG z@-Lea=?}r;R&gBj4m?yF&Sk6GZy#cvxNOdNWFhc~)m*0cdVQ(IK$lD`Ul@!IJdcTv z_Pg_Z(S$YK&q-*}rr;_I2A8XvJR~VEqGzhhZ}rnnq!Uhnfpev}*hR+qRd@`4vh z+T}Shd{U~yxSVXawJ9>xgkiT`nmrJ!-r+g89MTeWsQ3NDa&}%_o+|L)8O1;~y*LyN zPvM0q>*?0ZWw=lhk#oPH+xUxL<2^v&ztNWFvK_}vtdu*N3+}3h`cG~e|HHd{^24hK9|?!I zycwQuXic`s*|9K1S^V3-NW_Eih#<1M$WJI*w`-sy!>S6Vb9$d-bQ?Jt>F=<3E zY<;^ey=t5_{i(bJnsrsI_>0PR6gt&!TT-|k_^kI`3WI*Cji-s9N}G6Tj4fe5?b!D; zuqGsQO%)0psG05=^{9=O+IHK8?t1A*oZgJ7fw?;|+T6$4iXCGQd_&uk3T^fAbIJPd zG}^lXs)2M`-m#V@pBd0A?(UPibS`Dv&RiK!oA}V9I(}t8R^?=UafKoiQ3p@0rSd-k z1E{9QsKQH)bSja@<;0tfX9*AzWlGLTh2ktx87ML~2%$>JNi5?ixb@KFlfm)f$sZn! zwze1y&CTF87-5YK@wmFG+Nn9QfKYX~T5~8mG$JgD7a2|#2?)1`?%ck6Yh>7k=IqEY zkq8J5bf@pSw5NNzzda-0wMi`4mAyxnoAdVN;{2OeuNTN!0zxaC_m63ZxX#E{X2JbM zFQSoAeZkyF##zPKlwJ5yx=XV4_tz+w^C#xra~^#A>}=lnEIsubb2E{sFZbEGf(LJW z_xO+Q^7XjCLFpu9Y5_ZC@aIaAP$~A#$`>O7Y=^gR zr^75d(=DC}d%L=OyRn>L*c*nMBc5ISzl2=5NG4JLe!?pgsXmfmzJ5ZST%s%%=8FMG gB+nF6c)3EUm@gKJ)OokW$Z!OJ7aYay2^1Xo4@KnF$p8QV literal 1785 zcmbVNc~BE~6pl9qiJ%N9*t!$JK_%I2PLhTol8q(>MS?&9k&rB~gw2LzF$rE%6)gx- zjfx7kRSRlWC?d)=fJadjtzxvI0)ht~pj1(4rP_^x?H`VRbZ2*e$G-3T-h03IW-~(; z%yYDNx2I4jjLUKx7h<1QIf9q0yaivJf24Q(MV6%t?La9|D!@4M`JA`F_5-SYUBT53-hv9h2a0P@U;RJ3-!gWBPf(Lj2`TGVCv30Tywq)7_yXI^2FhdCh`x4j24#+1M%F5sZs;J6y6XD)+3!X=|& zdHNo2d5%mGEVDGHSU9cEc6N7PY7*31ICtvVh6lak-^^|OH6`grdFo&jb;@j3O^adJcxgv;%iH+MV$R#;*3x?Qb=W%*C^E-Woyv~;(-JjW*}`;Xio;`hdQE`GQ{n>)L%O?8Cou(064bBC)<*=|zL zH&50Xy`41Tb5!51)Win+n5u#C_pkT}4E5HyIbtkfUB8lc zXWcIT#+6h}Y*W{vCA%8#zIvJ8R~S>ZxkzL^jhB1h5EtHUbC%j8k3va+|i!`)}8tHUwrRVa0zoY9(_=yBb{bw-gJT(hNd$HU7f zPkq~2=BN;K{9G5a5%#w}$}T*(tT4~>j6-2Xk6qn zuj6>sYW~Fu=EKmwDb9{3Ce1w5s;F{4@Tzre-HH?Y{A;g5`m1PRTLb&qxL@kQC32Tk{WcAHmrG diff --git a/toxygen/smileys/default/D83CDFC1.png b/toxygen/smileys/default/D83CDFC1.png index be1a59b60437e0a56e229646200196e68286a2c8..f0f5e2998a6ba79d2acdf2a71faec5c4e7c40a69 100644 GIT binary patch delta 1353 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 literal 1731 zcmbVNX;2eq7!F1%M?nNU5C@knc=&hCE4KF{->-|U|F z*wrphi=AjRnu{z7NuWkI+vD&S^d*D8|9WO zW{SWmr;_FI0E`nDz-4ekRV)YscsvFa#^v##5P;2s*i066^FpC8A(tm)hXd0WNLeG) z>B0m=I&F(ui9iiW8ih=z*=%N*ISic0U_t_cz^1`whf;`8Q?`LrT0#vbzZnGtGpPuz zk<{V_z^157!?Q>cNO?LHg5D^XzY;c>rV~Y_jA>CCnGl1;)az|=O`=U?0`{*PuSJ^_ z*+z_+fSK?tLPgah-EW3W zGfZ1^{HIxFwx|xYO<%P>wRm-SFavdB2>Mw>MvL&OTpO)^m*bzrS$-tFAD z+S?aM!*N&_)3N@r`?Hp-ztbl?)V`>r3{8ti?DMtzCuDSw9mu@d64Ws_^-J+a$1+J! zp68bRDThp5@}sA3bkuGiES)$>@7#WQpnUMi^T@y6xi=h9GFr0PdR>uNUY%esI(Diz za>sb$qq=nW!;V1?KRbSc`$xQx_cd766_9lGonWNxVQJKd`Jpj)8=?a1t19wFO~cZy zH^(YFacnT$c^RtX4BxD|T$;57_Sz+u|5j>mczlZU5@tA+SubzuTV=6s@B%W!n=jqD zPYjH2i+{hZGQzBXJ~Z)ltG{nrPgj($x~kY2&eaDLMsBW759j3VlPigI*A1fWcGXFA zH@oA_y}oyy*FZqemBxpopI4XVFFN$XIakBdIY&Pearchjx;FnD|EIOrudM2FFJ3XX zz`r=j?cn~C*z9(rBP+QZMT2TLhLjX?4}*jK{a03H)JC@#%VWRjs)>skitA`w<60)R zf=37H>oyJK+E;mv<~Q6b>zUZyQF3G0uXRcLJeTa)3fF@%ad8z^*4(F+LmeqcOm@*e z4T44Y?fp7WT&!>38XfYZB*3}xDQCHVV%M+B4)#ABUVxXY&eK)rWXamV-BEpAjq6*y z_tcd&TN~Z#3&)=H&*y&tm&WdV0Bbcm-7W<~8r|!Ex4FAf`W5uDdVcjICkKZMrHcFO z_8Jwz_6L?^6kXfmd)kyd_Hy=kQ;^YTA=`uOKC#3;;N1cgOC^E2n+SXSjKsNoe&X&YKs)YT;YFkOhCND=t`8r17SWmp6@^ z@k#0V_@hG$3S)-7-*afWu+#WJ=-kxwz1!-_Re|mWnmIQX_kC8Inldof68!deL%~4H z16R=V2e2`9S7v9RLj_@Vh_@`?>u-G7^+XA~6Ptpn{qZ10tY^sep<>3|BxKnSjVu z1X~JPL^O>7EFdUGP{R!(1rdUBlq$%SfK5O2M}PI1XWnD?d3I*s+1ck>H?;(2+;so| zjI&Ms6_~n$2M(DK_hy`I4z6AGa+jj!?(Zm}eb{kJ(6FMvvuUoh;>TCKl-5%;~wSQWq zemApHYDX}d$d-`lPAdfsto-Pbq_UAFYGeLTV7eA1b@;=i-?DGVx zN3=i>M{gU<;g|$2*J}CjC%uy-duB(;bG4kb70&WlWY?Y7jT<-M9!A#-YK5`+ zX^RFTn*y{KkJm1n$<=Gw{yL}D@=}!`NRXSmX!r6D!IaL#x>}1%ACv+_;k1XL-@@1d z2k%uE$C4Uey8j8`HTvDjIY#RCveV*6Cnfw{&lYCT2p+2KP>G$ImFoLSyI~(`wlWQ= z;I{b~pRZn|JrRO#f2)`&1l#YaROgPV?d`?wa)(sS2RNJ!_x`Nkqpk!fp?mn9%hbn4 z(tE|v+luDIG9jy$2KHn`4Z}CICUIb!@G4~n0>qG zK@QH=)|NyFrWSUMw3Y-MK{z$HsoXL(%Ja(eai(7LOp8$V!r5Gjs?Hv0VV+U9O41aF(!wN zXT`JO1h!zeY;neVI=i~lU9ot#t@y3(j#w-ni)Evpto4uw;V6W}1t6T4m>p{DT z!iK^y(~zLXS6Cv8(U1%Vk?~kW9O?#yg+IiI#Idjh6m^an7~9hG*Ri615dQFTcfZGd zKcCO{_kG;nin5Zdc}wR(5R~QF!1=(KlX~XB;B6Yy1u!hp_!_NJuGQj#DnibX91syt zR0xYcQ3y3|=@$zj$WR-ps?ln^rEE}+T7;C1B@tBs8iERo5{eM47d0dxh9i;#9XWjl zMIs>wT1|U#ui_HxA{&}iv9hVGD%eyXWI|}sI;1ec0)nWh2}mN^AjQ~(1D)n&!92B% zp~$p}R_{O`I921VKwPpaBD94v2XTTxY&HvFrENCi8HB_M62rl5GZR*pwz1?|WadJF zni{HQeVltn3#=Syou(-)hQ;G?OPsREY8WFJhDmXdq#0P4V~vs~B+OE5MVf&VV?i~d zXc1XLQj9`C-l{oJ;OT=SL=~_1A+Z#j2^0t!mJk$-u;5rUnmX6CbxiY#|GM!=>sVEz zB4R!$KWdZd^p zNI@~J8v(jASn9D@)}@MqCaYDl+>ow-iaJ@7V|B8ExGHVP>gtFTlH;*8sq{QJEhnmx zZDPo+%28xGzHHqQ3RtXI^}wsdtJr${0> zIdP-n(`v~T&Mg6}rdnV$Uo1>16^1;_i19JDBOY53Zak-UNfg@@Kkf?e@v zKiv;+pP1U~8UEq5-Ozk9{M0Ylz+M9ScK+awDa+JMxGob5(rK37EH>r2;H(weYxzklrJ-fH`7JR4s)X)0gSHu%r{+PR~b@h|I z-#fY&ex-}7a`-}bzPuP(sAOb)GO@pz8R|Z|e)49!@x7%jg~pfV!W{qou^&gV8~XkA zwDJ6>$CeItmcPrLxIT7RUv*|Gd3b#Co~gv}CBLy~XQ0Tm!#|$ck<}44jZd~;&i%X~ zqc^jytu5zDPxw?{-pGe1%Lm3S2iL6)g$h4;Mf>-+lRN~rYqcqMAM(f*?G{8Wqa zS-IsKOWWDi~e{ZnhV7m=snR3!t}ZHIiBRti^#|SD+z=T)RKv(DPIDwV*fX|m z8(~3Z7>sD4)&1{sTM~(+7?V~#crlx$-ACZsx(MB&j0Ng(7}i2l7yL^O9j@eR0@}V0 zjoLn6$G+zN*<2tp5eOx|(03UI{|zb)To{M0erWeSF(du`%@f`8TN%@_`{&q#g$U@R zKnW9iE?^(2@L+YJbLA9ohP!oa7qojwLADjD1kgPSs&SBKfXo-ZKp=k$ z6cC`82~}y(qD1$4o}93T?w=u!3`bApoc^yi>z`kghdf?cSes00SOs^VwJT z(;vj}s}|!`$lcdt-y$#Fme&o>zB#a{|M(%2dc9@NiWE_c>KG)12C<>c$W|JRM#f@p zSfKHe@n#c8(}~5Kasa~uNmQITGX8T!D9TJuOBW#Ge4zlzJ1hjCSJp*y2U{td=H6~8 zz_i}lR?63`tVwj=J^SkFDDmCdu~6W~aNGO#>h=1)M2DrP`#*X zozGMDy`lMxq78rQqy)F+uG`;)thrR&P0p@fDo;GyNA6Q%p9Wv82xK$zTCb~U`!zW0 zf0$7eos+}egTAF-V+_xm<9Wv9;YD0io#ysvZn@R-&sfn4ad%0}&_Y^5d*x}rWhKKi z0{7PypWX|T-s!GgwMSZxzSSQpksNc!4I$&oCXY+%piV1Qmu~e%w!`TFTzAz@oRu=s9-VN`6BTvOd-eUdd- z@T^?EPedzeK!SYCGXjPb{M9Qh!$H9TeHXGB)Kqy$18EQ|F6en^_}U^4XJwwvAoL;a zOwrM;zj%MU-Y%HwZ(kG4iHw>MV?2&;lRcET8D+hZ9%GEi%|;~{kN2gUZbqmR=oa{( z*4Oc_Ya%}jm(9v*R^uLOxUrzopswL*_C~VL_Ng0{7`X_E$tL1fS7ndoN&ck#UNVVf zM^F7`#C1~TJ7sMvD}8%+;l&ploiU~+_%WY}SFBi(kgfmqu9?Jjm(?6MbFIH|auIR9 zLzGVWrth*kGov0cydN^;p_lJCSM%hi{y?POpv>XRoCikOD)d0-xNKGw_J>417&;|e z$E;7{Cmu>M6Z9E)Iyej$IT$-&DMFdi&`^Hf>sLmFhOz*@0f!S$H0OC{aX5cVDJyKN zy;xq+)u@rF@N6cJtDD~)7#%kMgQc6a`KzeZ{B-Tc=*?XS<)ed_ zdGcPaFgP_X4^y0Z&>ClDYl*ixBj51w^rvx7jva01+EbE}1&=9l`yMYnve{y7b*lDc zU2Ig0S5)+!A7`d%JAA0>i-QAdwYOL-&dpi-gmdL8?a7$n!Ivm|G;^tOl34} zS`QYU^n0RlLOMFBqc|{5oD?q>_$7)2r~@yGmzO(*=I*&8%9G;fP4%NvT`3el3S}%` iYx+M0hclDXlJoz6K_4Nvw|YC#1b`bD&h81|%l-qAq_s-` literal 1689 zcmbVNdr;GM94{~sIu*{x=FpQEgcG%ogf`GztF%ok56h!e;Z(%XrVyb`Z38Xn01=Q0 zI#fI!PEQf^R3;NRaEjn$^JIdrp=>Hlhk`m`ZtCI5l%)u6e|Y|}T$11K`^)$9`Mxh- zM20V$!uDjdSga`_!I+v^J?xLG3-k7RHi|RLXS5=gj;8dq6*m(ssg6n@fDi+oNT>;1 zm%gE$5VKfLdQuZh$Eud2TFSu1?K)hW!Nj0hEb&sC3D+hQG>||fl12&m$C*|TAaxQj zMyTSeOmZTL49+kU(HY?yZAP+Iqyv{O0mL?xAutd$4%iGSMhj|_fMdKUv$r4fKwwOT zPL_agor+aO0&>bs079-mN6UvG071BrK!_mFVgTktFptmN2nQ0NLIi~afbk1tteJIs zRE;UeZ84GrOrmKM%HvtBR<6~bOPLdSkVqu5bHFf%(coCpjWllK7%kot3>ab2nn@E) zQbxeeh$m2~v;<^4eH((oq*A>jHd@9L#iWd9!%aMh%jX#k_PEBhEwq~W*NyjTTQun= zf~O`dRH|9a)I;w*0cLXdVMBIB#v3%kOfp5mQ!q-KY9Nes2qpoUFI*j|LtzL)us|82 zl#1jEMv%$*h!U0u3Sl`PRt5+sINpa93SfVsK!#v2CW0UZEP@e%5|Lw4Odx|H89adv zFBhG(x8|QwCrxzbN@0`+<;%FH^|*fpQGC$1Mtg6(YG3Q9%9y5G=#MajfqDBOv z1T+`4|) zD?L{0w$dvwao>@&swWAy{nJnN_nW)f`u$yG8R4?!M)#+N;j7=Tt81@HJa?e+UUgrj z{@C^SZ|041%N}3YIO@=l_W7k=k2z~@NM43%Zrt);q^tHEdEV9S|7gRN&Plr#l{%c4 z_)3cI{i-;=vwoVkqW$6Jm4Y}`Xz0RWo4EI*vkQNtm47>-?p1M(4;SEzB65@NgIUk# za3hM z^__aBNf*jiUn`Nul!vh{G|{k~rxtl6WLeM#b%&*a|x6Vbe&cXp?ZvN~Qrp^J*TQ^*Zl(H*xvO4nn$ zjpB;hLdembZvbo7)SU*2@0QW1;L)0IqyA~EomK|6@-s5b$aAkNZ z(CB*Cr)zzMyJzoT9${{sE*+(nn|@!rrl7e9Ds^_>8VYgBbFrFpeG#v#Sl6RER6)%{ z)VE;4{oKX_4?If-#ar9vjXcdbJU4ZE?NvxvxKrvlH0V$w3KE><*spc%-Mb&m+e1`5Jfn4LbfjFACV)7 Ah5!Hn diff --git a/toxygen/smileys/default/D83CDFC6.png b/toxygen/smileys/default/D83CDFC6.png index ea79487931dcb8abada6584461310f0a71d2f549..7ede172133be2e65f8af03f3a5d0a7307f83fcab 100644 GIT binary patch delta 1522 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 NMtwZ?X>`iQe*nnSX;J_H diff --git a/toxygen/smileys/default/D83CDFC7.png b/toxygen/smileys/default/D83CDFC7.png index 130932220c79c37981ada341d7b0d459ac9336a4..4579ae2c04d57c8510daebd0867b688fc627438d 100644 GIT binary patch delta 1610 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 Il*`xr14Phj00000 diff --git a/toxygen/smileys/default/D83CDFC8.png b/toxygen/smileys/default/D83CDFC8.png index 4540605b898edba9447b8c31ce4af229ea870f40..d804bf56734b504f5099ac514cd66da3b2fd50d0 100644 GIT binary patch delta 1812 zcmZ`&c{JPU8vfBlLTWbzqqfr4)>tx@>J+svnVSloXibBJgajqEg-I+8MU`5jO=_vF zDD7}2bBgJlR<)?AHE1yQDr!rrZc2N9_rJM+-0!^Sd!P3`&vVXu-tYO^6}l7(HJL{M z061x`=np1HgtMPB0Q`0faS#FpYrQbf3w{8QVhjMREC7&zE!HXkTtflC8UX+t$^k$l zme+xG1ORE9fBE9vzJ2>fx6rM;&c2hKTX*MPOI1xi)-TP*>>BD6Mp@|$1mXaJcp?xS z3dv7N0mN5L44FZisvyd=HNz;#UOIKs5$uwwAc-CsQ=P>wCzDvTtL5yRi`LdWCaZ?S zJqLk4x?krk0|i08|4-lzM)XMoCn)qR1nLfx#aURMhCmQf(%`uNJmyCrju0pqAm8tp zUKEi`2_@l~z8F2E=0ABL9vU8-URs@B-B?~-7dBS56EAwJC|6_7b%h5EGGmuVhc|b{ zqECC}{_4l=p~=|_Dv^H$(-(JT^jF$U zUJAIL_1d!DR9eHW!m-)KX9LWUnn&&9teC%VroHE|SE`ByW8>)o{v&DBPA-T4ypP+^ zGRBPFXejSx(Bgf(hX;q6g1qz4`qianH#rr5lKfYyO9Z3iEKrh4ZNhtIm}_0LI-bnT z8W|C6)N)>>M8$i1HwAeW+URDZv=dIMha6L!FJvxP6tQsrjd%}c4oaJ0f+QO#V^!sL z-uCw-MaFx34Gj(zqV;Z|bW%-_R6`ZKru@o>^_@?9^#!+v8Po@Ok4swet}vNoD^p=D zcWYZTzqUCnobTe@&9l*`7^nmywG`lxq=~JshX<0){)QZqyLk^y>c1~473$;R3gkd_ zJi!7+_wsXtt-uuIj38a-$Tpy587a7*>C6iuiF&bdG2s*{Nspc!OVUfC#R7oc&C&pD znGgj(c&~Og<0yP-(XQyz#AnIWVxp6mU_D7l`~^2Bq#Dtl+N%fd_{Hu;?|zK2S;VYQ30sqqWT6?amz(m$%3ihs}Wp}mXJFIZwX zc$sV&Oc(uB>G)gsJMw$rFH8+*R=j~D(9cojdZE*;YCOeLakoZT~k3km}NTIR97cyQsT^@I-nl-@>HsVa>`$i~}Nos7IF z`Fr=^)y=C{V2VZ#xtVWsVlD8BT&3FbolH*i^RK~US#_q<5xuZ;T=xm4;0Z-(u2tEk zi%qTZ>u9QPdvD+>nD(nS?#22B+_l=QCwJuv>}wwC<0p+Ov}}4yj0?_d4u;uaSUp#$_2KxxaSQYZ@a#9jwT(*yp{l!*cb-7?k>AjXx1|(M zCn^f`E!|WI~~XDwoDM-}<_#48GnJ{&s#L)DB_x{PA&^$I=%6u*z1_I)_PN z@Rl7bV6wrLHW7a$n>>9OQz<=ls@~#9PjpA|^7cX5kZx;5Zay=IFLnjJituuE#vw<; z2rR%jTs`oI1O0P**c(P%(%2U7?(KXMDZqBA#{>MSR4sVfECKh${b~DZfS$FL^)Wa9ndIK6v_dGDsrB0`X2!; dhDZrd{{Mox@A2wI`X}T7z{?He+Ui2c_z#$EAVmNG literal 1843 zcmbVNdsIw$93MjTAg?ybcFl-b^Q?K+NONb(V3KJvS)$v_t)`lL$DNy*Jhrk%FPh>| zd9*0Rc9NDHCuw7?$Ybep}mu``-kI?z31Nhd))8m^Zk6kzwh_l)W86L zdmCpP5{YClT_Tnfql4ucKaP02b`3(rFawu_;6Z2vZh$bD35uCt4%1%@v&;SEzq z2;^eFVPC{bNRGmBoq$d^7z{Ln2Mxs{=}bPKZ{c9EsDuSoACKUWk&5UkBMf3#ufjAs zT!SKjg%Juz<8UFFQ2IFptxhKULX7B#6GfzqZiIAnCXGSYYAtaMS?h5*{I86!TI&_@ zI+!kp^=KTXBF-a%G6E)Y_iaZOLqd&U1*Rbm1&S4;syHo-;8L-WOnjlKHEIEi$>fN6 zB95OgACwRa5y;^9u|OW11u|HET=odZ*RTu`$d-USiI^o}GMN&VNW$g&F&Utk!(eei z4tE4AMf5m?sNfO58p7`|R{Tw@0K{MjM==G8#*S1#U=)g@`Y2QffI%F+}0~Xx+Irk0*zOo}YeKFlYI-z=EPQ#+Kmj^p@<>3;gLtg<&x0u(Dh>CUQo` zVQQ8QqH0;x7xr{ZEKOE-+*}d9|9FiI*?(Z5`$=_yYxnQwE6r~%CV&z4+kX)3;cspy#z+^)aSWEVG}Uq;+}ls_ngD<2#@C_?>jColbU1 zs?{YWXSQr~Ia%8X8M~)B)g0_oh9xLtK7N`Xuj&=f{4paISx=FyGEI(3JKWjX4xBxk zq>9QZGff3MpdhN*oRP3MUn!XSHZQq6e?8LZy!X+X1Cyi2i(2_Pjpn37%aV?03fWe@ zi@Ln!rzRXLbtzD08m7J-czXApLd6?%`pzUXJu!b?i^Dc(&a-Qoye&@Y*o@MniJNub zd1pCR&~!d0>r_Qvd$24>&uB`1OsQ(?YGW~j3cOgd$vZ=zy(i4h{=pz`68qjb zrR?Lr;(Ogc^j4fr?mUg`sCixWrh0?!d!OgEd*a&tXSL_rAptV-l9_?HReE~-&7J1l zn9=uUN2~j4>+>2KRyD7h_-F3Qk_J+1xdHf82YjE-fmj}<)SCI*$6qMQbuVPMwmR=q zZ*8>>eR)E1{@1G2QzCC)dPvDB-#z#Cz+b)nKDX;%csfVf+RUzD+hg|^pD9uU&vkpxF8AGcjnds0i-4^QR-z$NkCfjsuEMiy$4=j2@)dn*>iNW2<8~*pFnGIb z!m`ValTB-B`EyN++2>mNui92;FRgYw6%c#1r1SFX&;earkK8N1S-C&*{KXFn*J9S_ zzSk9n(atxp38q)}1=lW>o!FgC3ph}hm8ShD;_*kD7E>67+{y3K48J{vrX!(svB?01MQP{SB E0K@#;nE(I) diff --git a/toxygen/smileys/default/D83CDFC9.png b/toxygen/smileys/default/D83CDFC9.png index f4048b83b7b9fa75b11f6ace18e7f036c6c7c9b7..fca8fbf7ccb8340ff51cd4fe71a10e22261bf6a8 100644 GIT binary patch literal 1852 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 diff --git a/toxygen/smileys/default/D83CDFCA.png b/toxygen/smileys/default/D83CDFCA.png index 45aa9fb71afedebf01f4e5b2e4da7a8c8268e9cf..4fdb629276023bda1ad3e792b78d84ccf4e00175 100644 GIT binary patch literal 1482 zcmZ`(do@#5lW=Jyw$eSh58&M z;baM8uqbx$Ucy>u`4{e;smd(jK=pg}jSrk#>s>6QmaBz6Cp$veTlp9F&gw*y@Xu!- zo~8?jYK5ch_4d;DHJM2B(9;7$!%$lXjddVsf$lbFZ-J&-;5Ea`0T`Nq!ExZX0*?n= zu23knj6xqmPL@cux8Aq}jiip%yE4`-eG23ST3?^!T%+f2v5j8S9}}*P`u}ZF*-OK{ zS#Dhryc4u9EDFXytt|}*kfFX}D}lIC9>*-qywrP#l#u{KC0UtcW-u~XWzn4{*Rugc z=rpo3)!sc49~>Iw)oh6_veI*OnY8I6#TF;Zd1o^9%0ePq z9u&EHx{V9jJf`2VJ^O4oJ-DZs_EGG#$-ShU6*V0-HP5JnQib!3;3HEuRrG5Cb0HA@ zGUxSl(yNPOs8@DA_(Psb#AXqZ>3lX_((*@HMPpS5?QIOM!v_bH`z{l8_Hfd{8HU(M zGMTfIOCPadV)gJN$K!h@9Vl>1PFt$1vs59~-}y{lo`l-*evV$pQ7JJ@o@vXF!`bt zwf7YG=GEFy#0>Sw@is@X#f=$pyOs|0Tqu3IG{!LO8!60gi_*&o9rNw*J9j)Lui-qF zx_9fTm~{Dy%t_ui4YiAKGdY}7Zr_}CdMhC&Md|);vm8T3||&!9BQ_Lnk_o#KwJR9)u)V3KP>y; zodL!NssQ6@d2$pKBghIb=c<)!%SuhG5YR9&j3yiqAsa!8OTr?C#t8eiGnG^!i>A zAZc7j6Eu$F1_wbJ45Zekqii~&kdABIi%a4(UQ}c+G}^6p9I99enxb&JsK`b-Xai2f zxlTSPGdv68b~$KwnM>Z6YqCp#kp;;m2u<+{sPqf65cCToYOmI#`86Ex5kkQYvE#WD zEd?ZQH}E(lA%HIB*Tmh!K0#V(Em4RY9Y#A%z_{&{!DzJ^Xot=~;aW<)%=P@AoMF%z zELI%=+5QM&83G8W1F5+DTbm;qTph&wTiFXU4#gC2s(9 zXwpLuOdHYLi9=%@ucWnxc(3w1_PFc6i|o62=JaVY@u_pFoKM?w zzBukB8+l*bz) z{mCig+_*BFk^vDfZ3H z-!P9S`c&;vSL4Lb8QwAiwSWz{p}&cj`7<>r7TxH1*({6WE%c6q}S-|KrEjUf5wd hkZ?0Um|*EhKzc8JP*>jl^Sk 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 F{udGro38)> literal 1649 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#mE;4-zJaBy5>#&rIbo&`bc{YIaUdlYm=G`pf|&5659GizPih`8#}omx z#!N@%eg+1ng`O^sAr-gQ1jTv;7mED9+h6v6nRIqu<}S}&D~p$8UcBB@72u@E!Kv#s zDQ-#3Me#2Y7pnzbomeCJJ5y{~0t5DbS-6mMR@)lg^tznvXUF&CowhD7-(5a?(L?6| zcE0b&E%$%6-f#b1V3Ee7_@*E0nZEYzN$Tp|>ZrwO^R}Xun-C|5*|r|3s2B0x4L-cnUu=H7zT-=0>AnTs8fM}OK0i-iby=et za$?&rr5BU;bBCGL`Z8+15q+B0#LsNCRVx429RrpI?)UZAcJWBESId-2TzskhT>Rqh zh%2WUI#Oi6vEH+s<*UvN!&*>o!+vvM|>g>j!1u94`J!Fj-$5 zusXa$Jo5RWyQ+=SYTV^%MR{yXmwna9=5`WKc(`hP%TovCo@Gt~Dy)Wj2h86c`tkYO z%1Q0|^^zxUShKpCT<+SUVg45%hTHCL;NZCZ=wAy znLKmt7QR1s@{#v@8&mDS#(Pbc3Y@#SU1DM2tAhVe#B;989G}+}WpK*mK+LR)E_jfFH(+Lk{h+97Jk@O&2X?lX4xDi4GFWvO3i;qisGN0bZmFN z@shLWP!h|W;7?{R^@5o1E%PYS(pYoIGyLT5g+C{}n&I#$a?13oIWa;@(y|_};eW3E ztx<`Sqfqd=_5F1}8a?@RpDvf!{!&o1Kj$>>#IVT9rB8W$r9ya`InJxUdbiWjMsjz% zo}bm3vg4_{7C+q`|H$;jlfGb6B}MLzkExAJ(Uunb)l;qeUiI@`2vJj7!ur5%-3dOE zpjS)JoH2M|@J@Ex%F|5u9xAQ~&0*rNmv-24zb&IIt8qQYepU$vo@@oj#pN>|fGPn` LS3j3^P6 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 H+gbWQbRJnm literal 1542 zcmbVMdr;GM9M6d`Uu8ab%2aZ}G4P;mk|rgw)oYV>c342@l;hMRZ39GT6Vo82IOhS* z$LTTnL&iK)`=SW42$N~l@Z)ZV^i^*lv zS;kqsrk&LRKv)iE%ak&$OGpRrQZhjurPn3MXaLYH@(PS2pOrv6o6EWNP=CuQ2;`i4 zXgOw8TLp^E;}-iwHp7=9l#&mMrO^JV_Erm8z(yNLhp`c`;W769f_DK#)XPBhgdrmKblM+Y=LFpjnSYOAVf(lluRW4UBt`TjIl+OO^##^;Lwqk)* zrL!KsP;|)k$cYJo<=nm7P*73!hD;SXxhPBl%{vNRtXs0sdPx4FbaGA-fnl6ZGU8?f zL78O1NU3o%LM34crAEwJEX46PENVhQh|vJUCL~FV8VSmX5vZ9)DM}N3<}-n6BYU#+!<&p5nkZr06t zR(r?g8y^_PZ?2jUbsX|O*`tjKbbt29U??e2m421H@vcQRcPtlWny2659jLYo$yYT! z{;Y!ySB@?6*Hp*u-M`?1y8q(3J16=t_RTkr+gmksxxjpH<+d}Q4=@7>M;ENS(&c-3 z?&8ykj%!O^1lI4b$<%i3J)w*FP_eT~ee>6@4lt^vs`qL5J$vif(%Qqzw9`8yZk+@o zqqpoq*EeqbCGZFvt8Z@h3ofuctgRvKyfe%HgRXN}>+kLzEQvq)QS>Bk%IfUzbGz*m z>%Xqup~yR3nv@Vj;+1v2^B3|hvB}|)@%7f}t8~@#?(Dm;9gFi9o*C?mBL=6QzjnR+ z!26dp!{E$GO^PW$FRW;6)MKXbIm@Rxw%+LNJ=>GpGUckK*nj)I{Q92byP(clZQef` z;=8I->A2ZDzG*%heRrs2<&5sZiJJq8_P>T!L^eFbOuyHae4Jc=$Qo6pNPJ!$!S&5- zzLazhO`Y#WcTIh@;fo(1YpXhX4rZ@m#(!JCECb!$R#RF2UDmpzn@({Xq%pGtsda(a zgxPS>4)L;IrZjSKR^P&mL}haNxy?wpV|bHKD$(1F@g7 z=Uh{3uTRfc*fw2$S+TZ#%y3Ov#ZY@iUH@-uBxz1n<$8mkVpq!j^pXdC`;Hlk!@gWvIZTm}hmP2sy}N-h;E{lKKGbi&75p<>OeypcV|M93 DS_w*h diff --git a/toxygen/smileys/default/D83CDFE4.png b/toxygen/smileys/default/D83CDFE4.png index 32ec6cc508579dc3ded6717f99d86c8491a64771..28cec2535dc40f802520e28bbc47ae45b86d1247 100644 GIT binary patch delta 1356 zcmZ`&dok1XM`qWlce2N5BAsD&hB4(&ga~FKlh${&-vVQFGu-446`E@h#Owu%*MK#^?M$7Drc%KD}`@HO}4{O~< z^CW%UmqwY>{J27nbarM`_-DU($BUhpR|n{D0TJNlj0frR+g)ISq!EZtNC{F^(F8F- zE))PDlXS`Z6wTBBA|{$jKOYflIa;7lKpT(wBx$hMevgR!$ z`^f79t0IPOwRoOreSw1@-2EdI`m(WpExE_*0XyPJTU=dlk3p`lt*(zXKU`obX**=V z8d8q8QXyOrP8f%+ab+e0I1m0>8q4oJIbDxFYJ=+96)dfb$)OKktMXIUyDy(_Ru~X@ zYuWrx$>GD6;r4Uv*zZ)=G=yn-{RXv1SlbJ!Be|LsIdM|D@tS$J%Bt~cxx0)+BOkoU zL;X4xPlHR6EJ|`_lS+N84iBP`^73v=Es9f!!Q=+FaQ7p+(rqVO2`9LsBwVXqRh*%Q z9b5a}szEl?PLGtq?9g_m7w_<6YpFYK+A1=gAp2D^(A zG~`ErI?>UK10Pt{8d`fAt+-z5UKw)7{jNuk`T5$}YK!%80u=L2D}5Q4zv!ZBZ9mm~ zJ#mbAxc9}OC2HPS$ImxwXlb9Q>s7-QBMn$J75xY_-MlB}vGoL(91A5xF+#SNn~uq0 zGw!*1^o3^hRfZO!gH(QVG`HA0ixH_)^ex_gLBL~9Ch)vYK=$xPKqJdZ&zNmmBvjdkDFbmI0Dklg*2_nG{9%<8U->(PoW zvay{q^)Evc*qEr&irT&(oSWjd`rA$>AKi$WJeF9pvh__j|CR61m)PvbUx+bQxA=2L zNuRd$lkT*v8@*f{a!GsD!Q24Ftr-l?jA7B&bA3p_J9z7etOlf7sX$$RHoI;`U>R`3 z-bbm9tG=>{-$fa=4{0wjlx3&qpn3x>Q#FaE98x)V)1f~wW<9u`{V2E{sh&|vt(=bH z>?NnGIqoKX5R5L>O?jd4PEsMY4Ut49BW;Y)b1CC#*=1qa%&V0LoS`n$jb!>jzGl9N zk=qyEHjZ|a=)a$i3pvv`Kb9+e>d!*rTFTef7Yqs#AYt(gOG(76yttOA%>MmA^(@jM zx*SPaaW^dT*J)oQPHC^LBTOv}M{wz?uCOK(n$sy-pg%1LdyX0eJFp+xzuycxXl7yQ zZ2@5qSYa(~_CgT$AOuCpRe$~F|2iVi2Zja5|6d0L8J^Z&$u`=p2mrx}gy%V)W&8~$ CgJp97 literal 1557 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 literal 1442 zcmbVMZ%i9y7;hOX2D*$TTjZbF^$v`T{|U$VNS8m>_a(gGth`#28+ z+5L1A<78-G`|+DhxlFdLk*#$}F2@nV%lTDwN=Fs)^8hWAm7795?QLNs$ip<7MPlFRjoJN;x+hS(?O^_L;wOmBhgUE-x?5!kP%+vCBQy) ztcIaA6{*DtuRG;(R6`^uFpy5ADfJ>K3gNg4)#`8@ErBou#ncG6@lsSv=x_ongElT0 zSQC7Wgp;yu*aDIfZk8mTP^*K%pem?QaYBI5eA;Fhv4TOj!oi#xREH3GIfaN)u;+l`~9i7*0e>*$^7famfB)% zJI|<{jL5YKUQmz5;xriK?#YHyiohGMHexD6xqI*ddxTbVA+u@zRQ)0uUoRZC$OGm2UgZ*e&cEFb!s=bR?;vfafS`HrQ~31vS_xokoKgtndcb_kVJx2F|Ec z&GDaRNgsg@Ol>z?9|)Vn!vw&@2w*fyE>d7#<=@yTb8YB*&wPn^waM+)k5%O9-)Xx) zRz3d5*UAA#HR=DSa zJKQpaOl_ZXAp?B=kpmNlucO2I$~zyLlKHALd1Gv!{=1yjNL6|NMfnBIQcs>|_N_7P zu>Omju<_}iwG%mG6`3)4AARso#o61Kmd-u>cE+uVS*WcuQgHNMGr-GBxQ3+S(D+Mx tRk6W%;`~bC=iR509ZQPh`iihTLn%8Ecic5SOr-wCGP|{kx@2*m{0ANS1*!l5 diff --git a/toxygen/smileys/default/D83CDFE7.png b/toxygen/smileys/default/D83CDFE7.png index 4527782380bc334964680b962698324615d8f316..612e467cbe2681e72c925b82c605df53aee07258 100644 GIT binary patch delta 1245 zcmZuvdr;B`9RET(8x`xWrMWf2Op_EPRKn+?X+AMiNzKP%NM&WHQIMYdsrbwXsO7b+ z>}4i41WiqAo~Dx6JIfxpfr>brnvb$FE=JdG*Y5WH=X0OW_xt(Y=X>|f#^hr}=Ewp7 zupxdGY5=2MG&&7HCS0Ur8XGvCA5jdrWUk2dVQz(4|!Us@z_v3Jz9{Ff6 z@K2IK{nYq(i$@W-s394c$L2Od9bCuU)D_Q?1QcpyOLhi8c+ zb83OOUnG%tK2(*|$rX>)OTu%2z{YIL?(H80>zL0IV0}2Z5JPq|Zb;C_`#&!~tyVkg zKdM>mJ=A2-Q9lLvc;S*Ucv}N8;rh|NO|&Lhj1`&@eFp`A&PWN1;D#`H_{3z+&iH+7 zJoj)S8_!Qj1fUijV{9$2AT8ECA*2{tDoS0jU%st-`MTle%R;|;#Ez*<;CJ9}eG~6Q znR!3z@UG*>S?fd1r-=c9%%p-75-X=Yakpj9#I3KD6a5TkWoLX%{Nr%jww*}=PC?}L zkSgEb&6TBjjVx}7OmK3Hq}1S zk60bBYJ^aqzJM z5V=vU8?_Umw`XtOeOpjtzPMx9WmQ?a^fbOaa-s{DN<%W!ylYj-xOAmYg^>f^ZQId4 zQ7992#>^5exhMH9Gb*L(ZctVa<5is?G(Uex-Dhr}@aQ}fO(=e=84*hnv&PQe<&%yY zL1p)|#=|P7g^9cz>E)wDrKaJ1@Lb!%lC<%Q#&?|e?*BqSU-4j3^x*O(*}-Wy%2;8S zg!2Nr8tI>+vK}=D+KU`AD#A0`m!h98{WeLLv5{M?T|lWCn%;$f@th9KP;Ziv^@;^|D->?YHHrX;Y-<#UD&@7Mk2sxbb%R zvW%4zCp=dp%@ZcSDR{=bhst5QCm-B>HS-r-u1!=A-I|{xNa@sr(=_q;T^pY_oO~VX zCih6`k)X6*!;=d9yl7!7`eJ%-nr4!~>w%1i>c+{vjEm%(q#yJ`1@?vqXMF(*5xXN> zr#$cx<;KpJqYqw=S+Xyho|hLz3nBGZ@1}`=kM-9QYHXC$F2(eP8t36P7bn#xRSr4w zsg!Gd+-cv0xhq%7%|oB#VkMq`NKl><3u{&V*uuf_@Z-6|-`}6L;MqmIHZtU~;(AAL zIt zDuLQn8Xa61lSZqF4KySqKsZYD1M5`e*sy@Q!HYpsc9k6VF z5C49@|MT}X4Q1Y$s~@mvY`be7ZWIHCS)T>M_0*|F>LwTbqrON z71qR0Z)AIO;%qUn_C)!bBp@0S)2`y;_-DXQMY)hAgb9WoZ&@G5F z62rEg$|RB~Z&&~g2ZDZ?AW4*_17s*n)8sLfB1j4+;7$9<5F4ghDuUK73|h04ESnN~ zYqsDjhUFc{WO2M$EC!0ffMMlul3^HEgQENp;kV1WBbEHR-Bwo+fGu09>8OT|x{A_} zG3LZD=;?L{nwd!K2RiWJ)H+oMl z%UeKl3@dFIg?a}h^M+&CdBa5c0UB*Tsp^VRv>$iNvmLDfEcFGT^je07ZsnI%cd;K1 zh9U&Vk-P{!fN@6&p_hXZ({RQxVi2owmH(4796E!$&GDaRsc*p!bf-J54;MR!2k7v` zSnz1H{r&VE1ZfDz1uk9sc}V%`?&ZmYUp38L>R$IuSIi5+HxB6a}R&DeG zgsi@Nu#2Fm!kYMY!>3;^J(Rq#Fh94l9BG{ToTyZJ(c|%99<+1XTOm68ysw;US^KVa z@Ywq1!uQMX-!ND1$Y&N_8i#-@zuZ_ zH}?@+n!RbBI8?dKzj1D9BGBF(k{4gi=LUa#N?zP=$n74Ya{Kgqhu4l4oWnG8z_{7X))B2(5 e^ITy9X+(b6_d(2${5tCXnBro;@L|v3>3;x1t+nd_ diff --git a/toxygen/smileys/default/D83CDFE8.png b/toxygen/smileys/default/D83CDFE8.png index a8586eef824297fac42dc8aec4e348c2e3a9a0cf..d6ef1f54128781052f5a9c4face1f92c4ae0cf1d 100644 GIT binary patch delta 1250 zcmV<81ReXc3yunq8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0IN_;R7L;){{U#QfH44oF#v!u z0FGKYdMg0HsF>^1!M~@M0BEu4(Z0Q6OTMI)=+3*bTSL90lz-*RxSmcpg+mv+p_1aq zw2DA1z^9n%(!jf)kl)0xcQhfoosQnXth}O=SRsHIs}i`ye6US;bjvbJHNeCLV&VxNMy!X(FNQxd@RZ zc9W|Ka%2_ZDulNRh!>VJXN1Hh3p@L$LW7jTM83tK>uV-A$hW}CIiY(h1rEgQPoOP< z>VN1p@StQ+C9qfA3qT7;u4mvB$Sz2BNvb!lydk#_VEkpPlTjV-a06nOZ+*d>`yN?!FZEl8hw z(7fd+ae+et-5dlg2kkBbKb4yJH^vg_;(r2bO(yMUAm8Vr~#lM6!)7Juxu z&4op?@tGsUh6&-&cKqeS+j(5Lv_D8bf@8piPnHc%cv3<-U%-hFBjzF^KadinNV!Pp zdIKlBD;XuJl%sgC0y#Zj;FMKWzw5fHO5&3>XuD6>wP>jSO8qd%VHo-!G%*%KaVxt{ z0000bbVXQnWMOn=I%9HWVRU5xGEFctGA%GSEip7yF)%taH99piD=;uRFfaz*@PhyV z03~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536*Gc`IjGAl4JIxsMIgGfJ-Atwp| M07*qoM6N<$f-}oED*ylh literal 1459 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLBNuZQ zM+*x>H&YV}CzxK(yyB9?yyR4vy_rCJp?Zz*>a}t%N=+=uFAB-e&w-_YfQ8b?Pn?g>Q=>r|34@w+Ji3KJEOo1RKJm~{D@XV8%2h1@= zz^ow|DpSkA!1&nH#WAGfR?Q@DZy85{}l;6A}$W`A6MWtVe( z@pX%~|HQa6;Y;1@N2-`UAEYo)W=Q5_7C2#a7PV-q@o)aVcNz+a0 zsf6wNLk7+L@3(CDnrvJy?QB!K)T8Wib_w^E^Tn4BnaD3JdZvHQy-Ox9|ILDh{}S(M z%KWuz^gEw0Z7EY;(4M?~3_f<>XXVXb5w*ZRD>Cv=9V0*MdEZ7uep|zti;E)6_wDFTAZD)K?h$;6r7Y5Z}sOKR5DN{}tDGTlMUv?Z>~&v%=*QR;n@` ze&%Cm*}Uv2Lm7J#-^%@G_ZeTX+bhSf{8hd2(WIczgq#hond-}Tib-q^Ip7<0d7FP| z*6TCh{8(145`7uqley)T_{Nr5&vU!u)HWvMJ+M%+FL`g#uN=72Rj_yYhxi6Y2IgDY U2PW*!=mr&|p00i_>zopr0F?U<_5c6? diff --git a/toxygen/smileys/default/D83CDFE9.png b/toxygen/smileys/default/D83CDFE9.png index 54bc6d108ca38187882e15f02e803dd1751cfe11..55f1ea24bd059d522774c45d05d38bf8acdc642a 100644 GIT binary patch delta 1358 zcmZuvc`(~~6#u&Gw&;{fRd+*a9ieO^weAp#IzlO}5Q(;Gr9_==qDj|Mwr)BNq0X{O z1PRl+w$3VbSF6sdqiCGFv}AvK{IfGV^X9$xdGCGR%;(MfEV(O@qD&D20BGvSxNal9RY~e0DwvdU=J!$7Xb)I0I-Y$0F?v45fY=(#RO_BJ3C;} zd_EssWt9N`Qh|Un!2l?s>Vr}Nd_61@sz9&=;Q#he@~=_;8G`2crjHA*dq$rI+wgQg z^2rW7W3;Y~*FC-j`975huO`m?IV0_-*ENwm9{~?)r$uXny;TeTWWsI z*X54Zwten;(_h)J@cb2D-)=%VPF)?Tc4W>gTxK-G!_v z3ahO%ziOhYd2V5;>v8o2tEHV;`L^=;Mf`Pp?4^#}it&oZxQx6j!Q}dk{Px_6y7c@P zjB-e*Wm&8_q+5aP>IB^`VjdGo+2(DJzcZZwUX`O_@^wy53~CGbpSQIJKZ_j0KtPOa z=ZF^jC?+GJF5dQwVgdkyv6N`6C)w36LX8v}LcDf^phk`&5!51sNdO>|Gu^$?7znBT zF|APH!Vy?}#~*s`yS?k10gcK(5!1x6Gw7PhA>!ii{k~h@G0QUpGF6QdFX$eSBpEV&^Kw725nMGvN3kaiqRgSALpR`R1b3&-aazwAJdmep8? z!?v<;rZ>Cms+#T?OUAsm4%)4Jo8SAN)_tDfX~ju7u5ySJ6R{huX}gqYu^ z1NUjY9Ss*210T|^D{CCq$))6SrRxZqy3XRd+1boBeNXdFPxt-VqeF5fF-D3ZsUmAz zO0ppWI+LPvfU*G(YZ6oF;H(0iqI}l{8~CXSLCYR9HV`S2NWd{|i-^{Tj_NgBpi{Ta zap@zfZ^Cp9THdBCpY0POeZeUB#o=OT=MFRMQB+MOZ8w*GX>?p&jU2ciH0PBd zu#i7AD(c~=AfH8ebZyWp9J_Pp zP*ynE%|oHb#mBwez~&x}ra4F@!BR(Cr5!e7L`&irB3fQ)d2#HsA9jUyg;xS`du9PtVz^3L;W#p@ zFM{G*JTPys$t~J*PSZafbuMRGM$;#_-`jqGZgMcQ9Gw%4@s>y#k7v5GV21gw2|7pa*TSv$nu04e9j0yVdHf zcyH#RYesok#W4JYQ}C>_ynCXs%f>-Jel;m}v-u6)SK9=hmSC&qo=`+38@b)KTs88r z+NnIqvno^$nl|RAQY`MTI(hp`UMlb$G|aUbODQXa9sBf<=$k{7r7e!86s}`t02>9GII%?LUm*kbrB%sQ+jDMTnqb%u-LBlmNgE?PUGb H5_k7cqxW-+ literal 1519 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 literal 1409 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 literal 1497 zcmbVMeN5D596p&tA&P=vkT9(0hM;i0_U;PpIPk8$nu=jvt`@{Ihnzrvpf6w!K z-rq;d(^B4>?H}f^QmJMqub|SwIM?&c^abyQe~i&!SfE(4l?*XYanTa1GBIK<3nlY( zKAXp8Ko(i_R<17&Zv_Tp#(Pp2zXYZAva$r$b{Rd9_J;%+_Md< zp>Y$Xz^I-Om2FFdNKs-TgGLu?*CGgnVH!klz%V2RLbV7AYr&1hB6`Ar5ojDVd8vUn ziOD0}8->*)DNT zg%btH!${|f#fni4lujIhciL=I#DY8-C=fE(O*>&kqlJ0ibFOh~SxIOAmGMezIkUvc z!s)Cm7E5-JkG#cRFo@mj9eE6a8X{HVKvL*JO0*aAte_-QMm6}NVK|0B5d@>+&6w4M zlNPWrlUmG*lJN$V)S}ingO}q~*m%9!YSHU;6lyjhhy{(uP!uOAlb$pf;wZhvi%k|} zg%<3r*DnYBPGZs5VhK`WX+@MWMX}IZ0cj3V5oL$ygvbmGipt^yMs&&1p7czdmSQDt z6U$g7k%z|POK`97kE1%&L>Wj@M*-ysV#2K$Zbon`WzgxgIM6{ge7|Ta@L`;9O=4^h*_aC`?--q03!52?0r-J9K-hMUgOwYmj zv#XRJ1`ZFm)C7~8w$ejKOR7s+t8*exUVV1?VN_Yp&fAT{E_8pKqiEgQD`PxyrYyzz zcwA*9IEu+Kl77v>I_r}J3$Gvs`m%pFw>)tu zRuU3?drxy^r5+o*Sg~bM-~w4aoSOHpyWj3>MqmLrqAnlwD&LVpB}18%wN(Amxs*p{A`mg LDbzXhhVB0V!`(1U diff --git a/toxygen/smileys/default/D83CDFEC.png b/toxygen/smileys/default/D83CDFEC.png index 7b0d510cec5b63a09fc680c7eed8938816100c8f..a2e4eb8ee4bc248e1ed0427b777d76bf6b362c0f 100644 GIT binary patch delta 1198 zcmV;f1X26w3fc*f8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0FzKmR7H!EnE;{8+-qXU)ZB}c znE;sI|0x3h|Npzg%H-|x0DisHD;T`X)4RjT0BEt}?C^PlhJVD&(c$Xvy28lT-s9is z?YY6nx4_2S=Ige;#oOcPwY|jG;pVfv!qnd5vb(|3+~Kjgz|Yy=ueiU=*W9kRzV7w- zt+u_$(%4sHYD!dEthKzx(bs{Lo?dNqn4qVtw7WwqkIDc500DGTPE!Ct=GbNc0004E zOGiWp7Ga3%0Dk}g2XskIMF-{w4hkg+!j2S)0007gdQ@0+Qek%>aB^>EX>4U6ba`-P zAb4$X0020Rl~qx)n=lOg*C~1gBgv9|9D?y*XZqRWcV#Dp6577P3>aC~uB5f0pWlDb zx44QHl1;i7PqvuY!?G(UW6S1NZI{Np_s8SWnPIP{VSmXkS(_wno>bsltdbUA9SgJg zs(V($8v#YIh*`SqoMiJNs~5vUYXN2pr}(zbc9)o=KmZ%H9z0~76Nmd?@(u&rBUb3K zf>SQqav~0L-G@ms?;_m5C&?l}732%sA}Tq?ux+sNm}l;I&!-W$;kFLP6)w){a`Q7i z1`GIPR)1Byq{cKRBfa8^JIRW4x{r%@hZpaIu>*OL8u!73@oWoy}EV^F0MIrb}0zmMgMc~P@*F_`1ApMHz2XG+ssXoKe=0EbJ1a0u3?^ z6UP>VuCGjPkZ*yjBmx4q1_C8r;Ko2(25O)efgWLJXg^p19X>kvCKTZWOanOtDIrOV z!hhutTLTN`KxU|>5aZBxQpJNXF2!RK|=Q@&41Nxo{ER{<5wU%#njCYDMxokIz0a+^a~0~ zib7-0Wdi^J0EtONK~xyiRm<5Df-n>V&`?$>#9c&GL_|P9`2Vld)^UcONphbqEFo-L z2n)^+T+j1;&vOwhlmMZWQ6y!Em|T)Eumbd~H_G<^>Nq=cPacpQ4GB{6G)s!?Y=6jb zA+NJsklzeB3i3Wnf%0J}Q9TV6>ZX0Qs(P9lT|c}BRXu%&ar$0WJ=6FB?i(0YDA1I3 z0000bbVXQnWMOn=I%9HWVRU5xGB7eSEigANF*H;$Fgi0eIyEvYFfckWFb3Z6g8%>k zC3HntbYx+4WjbwdWNBu305UK#G8ru}H!U$VR536*Gc`IjGAl4JIxsMIgGfJ-Atwp| M07*qoM6N<$f)J|{ZvX%Q literal 1384 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ}>1;bh)9S zqotdfrIVqhn~SNjv4N$l5>#&rIbo&`bc{YIaUdlYm=G`pf|&5659GizPih`8#}omx zhK8VKCj$fH3{MxwkcwM#0$yu32g=m({np|+-JV-^blNn}2+jKDNt5y-E!a0-U|!H4=BAw*OD9(a{@iyY^PH&p+e8cd?H;9Dr_MSvRdV|J zn(GrD+BKb)VKtWP+nY1(Uq&LoY69z&KH0QFXWxLYZw=pN>9ZF4oqzoEY3C{KZjY?0 zRq2~e1ExmJy?CJh?Q`i5AOAQm*>8Mx^694|}K&01o>Q{{3vkpJ-q$7?lfw@Rdx-u5kJo4bT!3Wo}i8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0L)NKR7KL<;nCaS!qD3D^YilZ z^5o>?#Ky_%>+ALP_3Z5I*4Ea*#mm9P$k5v0bgrOc_czOuT(fU3q_q0fS>$-TnI&e-1KCmHW!Sz)Qy zYO2)D*WB3O;{E;pbhF#b*4zIi6908*h`-><)Y_ZM=E&37uGZ_v(bvD>@W#;A&Fl5V z&()h%F~iQ&p?_gLuf4;=%+i)wHObT0!pqTcIU2#r(EtDcoSIue00001VoOIvLub$p zVgLXD2XskIMF-{w4hkeY6GC)I0007edQ@0+Qek%>aB^>EX>4U6ba`-PAb4$X0020R zl~qBKoG=W$>lJx|kz~m{hr#%ksvPtDw8S#QGHkLLYJV`uvRdkv4gLQ4i+;qfX(L%> zc=KYDi9M{kaxu1QZnJIDnYZzHJO(rDvuRkWOVut#yB8JsHk(PCFOG#(e9^6{VMIU? zEMk@+8y8u<$>Pnh(0YK`#3kNswaq2wC=ig1+K4=4*(aXvnaL{*Y>!x>&lOy1(_Ra4 zBsV-wQh#|9;f{QYOafFvy|7K9QsWu66;{r9=7Dj)8*xv!B^?*IIG4-CALTK!fKO#r zwM%JCIT__OS6oFFlrubkyg9sh%Zwe!lgx2FGhy8O4uFhY9Qp(!-cS}0V*Ya$NC8qo z2(~$yn!yk}j(M91gbfF=k=0J@S&JZ8JEVTgIe!7SmrR5gkZm31G~`sG0MLsO0sEoa zDY&@S=(7tUbQk^W;L(YW;@~qPoJ-cjCWKbZ_`%CvVgD{_how8*Bjg7^i-v0RzG)C( z)d(S8qisVbomI25_eF@TxtkVWB>7b0bf};mo43_nx0uByJZ0z1LAYE+@$Z%rAjw2gC<&^j8wRDql!;|gCTvZ2 z9Lk0(6VIbu_?jLBl#lRUN{Hz3%v(%^`1Q_POpN4HN`h}CKgB;PHG3ql0RR91C3Hnt zbYx+4WjbSWWnpw>05UK#GA%GSEk-dkR536*Gc`IiH!CnOIxsMpOy9Zy001R)MObuX zVRU6WZEs|0W_bWIFfuYNFgGnRG*mG#Ix{soG&d_SFgh?W=@DlJks&7v002ovPDHLk FV1hNbO_Kls literal 1348 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 diff --git a/toxygen/smileys/default/D83CDFEF.png b/toxygen/smileys/default/D83CDFEF.png index 1e446c8072065055eff4d9376d1152c52286a63e..22f4662977aee7463400f1417fa1d3d28b815122 100644 GIT binary patch delta 1300 zcmV+v1?&3z3bG228Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0MAfNR7KL<;nCaS0O0@7+Ta=D z|1*e<&)MHa=Kns6jyrmKF-tusDG<)s-e~FnW8CmNUsd4X-hVt^RAk%l&DY$4?*H!Y z>PSjH?d|Hz*4v!%|9`^BqobPn`S;4y+MCweZj+5bPCdxe*u?q&-`?BD(buu>;Q*jO z`uFo>V_~xH;l|L`0EIRHNeaZz)c}zI0Gj~b+tvUTf0_u5(_b1rRW+(1-8{2X^lB>&?IKvOdTs6 zir^5lblJGb5>@7?j)T4?m`_~t-51|NQb&n^Xw-YgA@i{8%#AY1dE5>CjwzTKx{^}6ML*h609pq{eKbj0Nh?>BD{cXUy)8lOeGTly;$LJ zeN^q>TtY4T>_iA%dH>pZc48no*z|N4GV);;LMsmc$;(w@|4wR$r7PTr%R4`_X4Tq$ zY2aYdbRl8cwpk`^s-v^_L-4HQHq9umNL3LCWWtJC;Bhq+6pgAAc8ZnJec6AI5zH#K z4Sy?tL~?0ngJJK!~A~{IV!O$~wNkSw1 z>EPgq{N1u2`i;KZaHoE|&vNdMK|f(cBS)Gv%@81&o1IpTp ziVY>fJ^%m!u1Q2eR2b7;#)%ezP!NFONfK_^rA6D7HKavFTD|`}x#DyV-^`i$0e}8c ztsfN7?Ha>;J`Ej~WxKsTLj7KsWCu8&=Lf?PF`kg*U^;u73&Ns~DTtCJ$w*l)6(o}s zLDh5veHpr@f{F2}fw2kJdb8b;sn2F_!NESF#?d~&>Fit@&iMqc=lQ`kjH2)w_@3uN ze7h&fBh9k(ktFw940%yjl^^(ItADC2a(MoE0cpY=r-ka4L;wH)C3HntbYx+4WjbSW zWnpw>05UK#GA%GSEip7yF)%taH99moD=;uRFfhn-q~`zt03~!qSaf7zbY(hiZ)9m^ zc>ppnGBPbNH!U$VR536*Gc`IiIV&(QIxsM~E*x6`000?uMObuGZ)S9NVUaHx3IG5} KMNUMnLSTY`iCdKb literal 1403 zcmbVMZA=?=7(dFu%1c7rqB7XL9jIf{yWX|E(vuF_yVliER-vrbxQuIir5EU3cfHbr zD=kD>e2a)C7#s_W+ftpwy%+@~B^ho}znM%)&;iY2j2c6X^1)51cRTzjF1R|j7WpAU>6rAVn%44myqW2 zHUfd`CSs=%+7RWoJ3x{b7*GdmR9-cTf;bMNS{;s~wIHTOF+?rhxC+%0I-J1t;N}HM z-UOeYXrZi|zND2A3W#EeK#*uO3P&|CFLWTN!C;U%Fia&`sKPy*NXJxMxGKj$F=4O3 zhD4U8<>#*3u{3~Ny>#(yY z#2_t9n2!iv={)>ZIk1$wdpnX1B{jrhft3!0?xJ{aB*<{0jWR;g7ud)82(_k;#H}c) zCrPUXLs64Oi<)&h97V}G+(2SEj_t6hPFF{o3_6R}LZPSyGu2tlW`jwuwUW5nN^N0n zTv((zFO&1jN`5!7+IwONQebG27o0rbm8$?pfEW32fDeIWD-P~=v7C>OhHK>G*@%{6 z1h$*;Sp_}_uIHCvxASkZkZR0?YYdVCIc~k0GE-(ugIP4V0oOvCSl|E286t^6z3gbr%P}{M; z{#K?kv(-DNU%YwcAoyqP2TFTo>fy7dlLcR8r#{ZE+|@6^d)I^&Z0L#OvHX#=>gu@i z1##aig^WKnxYTdckG9SqKC0AX=O>y}m*0M={Q6*G_(OHizP_oowi6{Ez2`9}XU@Gk zKiTYl{pYiZrtDgF#P3gzCtS_&)d#9B-6y4uu%qnEayD_J)x{n4c_wF~ zzn{BRae68Jnl4n`7Fm5hUmT0CW>NL&$rHEV7$qFeJ3sBI&%5J%u3*>wpK@5`Y%G5J z?#Ytk%D#B^)bJ_g<6loU7QDM?+;PBP-<=u#=Gz^ERj!+lT*_R|oWF(?R*ZQUi`~h! z_GQEMvd=Hw%GB;HefmCad3sE7z^@qGHRmcEoG%&nT)TMj*M@XrVLB4&dj=}YE0=}t zFK`0R!lEPX4s?cYZ^vTsUp(>du4mg86NA}3s+~#Y7l6<|&{#9B7`l=GD;jq%SQPNF z83lD=giFUV6T<@!tW!~yDIisndgr(0_lv6^<&!`@kT>_oLyz(OZ^^$yo28kWFt-o< E1K`2xJpcdz diff --git a/toxygen/smileys/default/D83CDFF0.png b/toxygen/smileys/default/D83CDFF0.png index 0db16f2ce4e7cad1780043a74ab18b3cbcd2a5d8..9eccbe590adc93de06e2f774c5c03aba165a32fe 100644 GIT binary patch delta 1269 zcmV zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0L)NKR7KL<;nCaSFOkb>gtGr+ zTmLp3>ntKg&e-0K!s45^)c!*^nYYxgfo;vMp>}O+Y=;8iSLCDkC>)Y1H(bvY%)~kkfz^9tT z&(-75$heq_#DA-w!_L#;&c(yb($l)H!pqUWqLjhP(EtDc4vx1h00001VoOIvLub$p zVgLXD2XskIMF-{w4hkg%ygz6(0007gdQ@0+Qek%>aB^>EX>4U6ba`-PAb4$X0020R zl~qmFn=lOA>l8hL(XV|R0(jT=y6o{gG9jUawqN5HY=2}~BaLK1KfnK=Z~3cQNfzl* zqxj_F54TV*&Tbywe3O-V>yO8yGeh27Q(_^es}x!{3$XCPzCWqHi?Sm8nOja9{bE4^Zs<=b*RllafV88xeR`W$M6C=#;WR< z(3r+%hF3!|ip&V7$N2FU(Bf@)96+9=<#>6*c&+Nss8YJ0Do@pG!b4vwyy}M!l#k}fL_cnxE@(M z8JAGYI=c`;ch>)GJS#B}9CUh`3mNgS3!xN;{p95?uzv@&!_pn@Ve-k(teLg8Um6%# zG)+iYmTjgA zPk(NZZ-HT$umv;t8;k)*4U$9!x=y(g2!Y+wH%O|aAL*x@foULTK}wdSCvoAI{Vx>Ig@a@vyHSRVwtsXa;7Gd!8Y~DHBydKJJxSIG4;>twk-wVu zQ@znw3+_^H&tA^`Xe4ysX|8H}SA0o7J_WK-Zryy4EVDDh;r&}ezW|UWig1-qTGs#o z0HjGoK~xyiUCh@Kf-n$;;YCHU5-X_01T5IFP!tpqegB6tyO;hq`DSNw0RGb|Sbw#q z*Bb~8j3(A>mQ6IST>+0AK(mbVXQnWMOn=I%9HWVRU5xGB7eSEigANF*H;$ zFgi0eIyEpWFfckWFp)vwssI20C3HntbYx+4WjbwdWNBu305UK#GA%GSEip7yF)%ta fH99pgD=;uRFfia8Sr(BYCkg-nNkvXXu0mjfVFEqV literal 1353 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y`gLD2*8txIZAW?5>ATTy1?qMy|#{Pnjq|^`?*$X8J(K=z|gmQeuG#0aGA|2~YY!4m|Uu<^gj|5io1C znF6Xv9q85EkX70(&qeq8$;cEY|d&k zPLFz`%68>L{H%7~NayPZU-H=BH~8~c_0!|nbANhPt;#LXT#z(@ja89P!Tsso&DKsq z4#&36muGoz_vimdTjlu&>TPuw=)`cXn8DdGwbhc>vQAuQ^@Zm@cvI|6jep*`W^1m} zTl{p|`FU|Cf&_T~i?~`G&VQHs=hv<8FSrz>Uu`%UtjLoBv|?HP|NpQ5*Bq_>ta5mN zyi<1i%fA(!Z{+{=zn{4DUt`XX@9`_QH$A+VyFmYDeSKor_ePDsznDbs>}uy%_GT2_ z{O9-o_g8+K7sS_H4QS2y@$IMf1e?E~**A#<7@lA=iC41b^yOM1A>x&>@6YK|jEQ&l z)(QG@iFo}_ka}?7koJ$StEZdm&k4D9#d-2CcFwSr_$HVCf3KQso)#L$Y;<8sLsw+O zt_iiAEc5T}aWa@wU6bI*k{-Opu)}5 L)z4*}Q$iB}^LFH< diff --git a/toxygen/smileys/default/D83DDC00.png b/toxygen/smileys/default/D83DDC00.png index f7982a4c6f3b1fb223a1227784529aa34aafc7ce..7ced00254fdaf5aac990525c23ffd0b481e788ba 100644 GIT binary patch literal 1731 zcmZ{lc~H~W7RN7$zlZ`NPs@WYhAJuO3kDFaK-dFGArZrBpJHN2FqW_xh#=G$Vg!pK zi%4WkD2OB>4Uj@W3`vM=l&fz z=Q7@tm63KnEd}OWO5!`<3sYH4m}Dj$2EH5ymhY1Hd!4wfPF(t4!bxXS{r8fpCTTUC z{O@^1!DafFq13QL?|@r{X{Z*#?G>^p^*5`e;&Pp)V_{PNWMNiOQ(4G5mU$RoS1r0# zFYjw>8SUws868@f7@rv(9Pa7tZEI<*mrKPJyoiAF%&2@$Y@N8gMP56gZk-y^O$`kU zs9RKZHE?@*oLK&eC{a;CZAD3Vc=&`)yPzMN*N@#B==)Y!bhA_l+pF*I8Sn2Y`sPA) zNwK`LTwO06({_*dY1$OhYdP6PIaxAM=`Dq1thejdcePS+nK1X8jKo;+)x7b(u6h0F zNN?vYd2MA;K5QCnX|tqyq`Q6YhmpDQkztLxNm7+_F1_$V=0IC>RT`_mt*Jo3>ujv! zMUi<&$=ynMMSf02!6n#@wY9Z=bqhMUco^=rNfmj~*f-)i&$A=Br?2nJ#|ifD z*(bcGk?gjlNgwH11y@r)%i+8DUHi{{yIYQY;5kE!G!WjAoV1)M z&bNBz5+VE4dD^OcbG(`T?C5dC(oU?NYW(BvXF#Ft$*ai9+OJyApFi7Vh(d#%8VOeM_=q1j87G;GC3XC=gUH`(bU$1*MhP)Sm`5!JtJDoCWCyv+ zPm31jtz)a-FWvOJaO#Scc+mC@^&dX=H&(L-%4lmTCofdHO<%hclg_v#Te-~?QAUDo zqP~sYL7WO}Z1_EJST;#))CVo|%WzB5-UA2gtzC^jVn*6U(QEk+x}b(n1rdR9SN_XQ zjKE7t2Saf_A_r?~GsBbej|VI8Xucx&d58XU zj7Fi;Vz}$f_JuY4f1jh3c9O9qe$1?d%C7| z<3@JCuqdQXR&$sqG|?d`&KbQqPn#&Hb(@TIrN(ps?6KHicXg3Duno3sTqHZ1%BH*0 zlIU;%P8cUAG-f~Aac`(2#?{%y)x~Ky2IGpsK(o&?|F3`*AI)T>{(S)_6YmKZSiOBA pB%Zl01$8_Ke-Qu_ZNCfE!F@4 literal 1685 zcmbVNdr;GM9IuRZ(7|{P73a)~PPs|jq)llvtM(CW4+{l~fDDWW6H9L$a3*v(d&&0s88WVd3vOq>GJ@eGqi!X9pEWdkO? zgq;Mdd1|X1H=2}rB%Y8Lqt)eQ>co2XqJ_W$J3g2Vxs!;jGMKoAfLIiLU*3c>jR!~-ENkG_Qwpa6k|2owoSTx{AJ zsW%|;il_-&^h&}uQj`_pa&vNWI5~U{L1u74u~_WZfS?E(5n;=pRI zn>N>qbK`LvkxlC8^Du->lIh&N+K?MadxON1Ci+mYECr#gvsJ>-No^rVwv2cE6_cCQTp`aMf2bmx?@PXHGEl{D;W%r?^Ft@)_$XOYpOChF1Y=Y zYvZiCp`$~Coce*TGOoXeqf^(9|+i(NyuFGX(4AwKnnl_m2J1*arXDV*AsFLA|#mVEVJ+ zhd@{d9Mf~fwLCV__h_5fM>jWrrVOoOhU?Eh$Sn(y_nbLg+sqY!9@$;(nfLndy)S_q zMsGADR#AmUyP~fA0)Fh?_T<6J*(I@4aZ^LVi6pOl`T&xhHE zBh6?v*48%XwO(EJ)*STXv|~F8+Y-I;ki+|cx08TC=WV;HC>Bkwb=Kyj)a}`8KCl_ zkDppSICI$N>5g*Q>GZfeQT-Y3&MGh4^n+_rh_g#sKWIZd^ZQd0Q!hO8I494V!*nkE z>Fb`p<_)`(I`AE7=apol)vLcGx@3sC@j%L4@?EKILI-8}WEmAy~b>;{JXQA4clNEtM%oN1*#hp+aL6M`dXn{s91mgV6v0 diff --git a/toxygen/smileys/default/D83DDC01.png b/toxygen/smileys/default/D83DDC01.png index 6d16b88de8605df06992cfddcf9f52af003ad7cc..0a276c7dd2a3cdb58bd050e2af5780af6587b4e4 100644 GIT binary patch delta 1565 zcmZuv2~d+q6kYSI@6i%yxDi``*z;NdilA0`t+%eTslpEJV~vZAeT>6Q6?(NrfAiZ)s;d%e`aD_ns;}U%^It&94{|j z5eTNnN0H&dfnnCb*bsYuc80&SG{PEOT3mp)G}qR^`UgfQjG?tWt%FDyOpFY5zj-}1 zHp=Pm8yn*AxQnlz)z?$X`a0irQ!8NujGJm`a0qA(h|7C zna0Z=eQrQ1%D%Kt(x_ibU($c~Df(jwfP~=4W34Ttb+tS$SG2MsSzTRfeIDK2vtp-K{oxT~7d+1fHR$b>qLa#-WToWb`! zP+rJs4m)-PK8z>H*ZcDuepyZhOLKx*yN8Q{_BhpkOptIu)x1I&Xa9GP__nO~Mc?F< zGM%cU=PX=CMEl6el}+|$6LJAVVfgG9>_yqybfX771Wki?$(=jm<_yvl4cuX(MDifG zC`~GH}cc* z4(k-UWAQuZ?o{aWrTY`Y`{sBi+n%*QmO~g_Xs;$jR-g4Db%UYT8w;)%H~1O&k`jDUBI(`(m*OHQqvRj`H<;G!~@MG)&@^B+=xBLjB;!=wSVl0Mk0t+sVs#6ZkW5gor1{6D_N(#PiO|XA(aR zB$A4D9_a5NPGzrBRUT;dAWJbh{Y}3O1kb2`oLO$}tnhjAe21QG{SWmT)r?vnlZ6T< zQ(}_PhbNe&n|1pspiWSWwCf3Gs_Cx1ic#q~jLQrpQ?o$fkO;&D2Br zYP(a)PR6+@Hn_^077{Sp`ufwlvdxb9mN)h>v=rS$yL{?8C$At_+U|Eo60(?q=F`)YbQN$!z1!sM)pM4 z8}iyUl(@w~J0L{Qr9t-yGK@(~xMLvQqL!s=bk7J&+T_0rzyE+O2C19BapfV`npHlu zoqlgBHnU4ue4t?A7|-~!M61p9L4(M!x6J#|OG#6z4Dhnk^VfYibO?UZb`NIM#XLZp zp7hL5@o){WG-?0J8fSUf5NGPXJ+#d6oUda-jAw*jXeb5KuU+!Ktwx|mwG5*sdxrZ3 z#H$C|Ynk7FR9KW%aQCc6KyI;$_HN&-OCAh_!Ac2!Pd)G?-QnzRihQe2z1qc@9Ks!3witIEogbRLsZ;-%9|Qthsid>8J9<|KnjenJxP&>{u;Jq4^l_;t{*E+NeW+b{$U zL&SU?^u(zQYZ^#$UK-RY)e4siK|l;sA{s4*A+tbKg`luXx-kW!A+#8QCV(RsBw6#i za|oN!JYq{)>7YDOxB!ogJW<|=cV1;Qx1tTrEybLcg z918{%DJNGb>LAI}Cn0!vtM#ccD~u#cN*U~@covd*DzWTZS=oxJQFS0i+LKh z(E?ZKbxG%uGc8D#a`${g0ifg!G0)3Lhe8z?Iai^FW<`rp2T5O)ZpKZh)QKc!M#uz` zG@DQa(VH}gL94|OgiOS65)EoRi;El72{^7dn=~c^f|yV}uF)rIQMJl|l13a)400{3 zAX2Q04%%fTyCYoFbGZcRr74l~+BvQuSOIByoX81z91oJ|82H*EhIMm3AwFeD5*mAm>QQn2*jPBG8&8qRE?U{7>;S65w83H)t+x_S3(%+i*^8D!JyC-&>KJ^9M zJ>dg0Rz$tg!)Q*LFRXo)Rqgv}@1uy!7QT~noSf(=xEk8k*q#SDyXq!wZ+2ZTW&3oS z)C(fFFKxH5htvsu?_Ki!*h(h$@f*SVs~0N5zWd?(uFjao?1AcwHE z=k9A|$lVFygDzw}EuU`bSPq;O%8<}K-942vKK30ybH=M$8Mk3-;ZSCBtX!@e3tvDU=VW$Bw4W_x`A;r$lIGMDu1V*pOlhw=Xb7m&wqEsL{jq!jbZ1%p=_UGNq4&d;3*W5@{p-A+Ipoqpd|Zk42U@?8iQDNu3(k zDNH;%h3r0Z*LL5KqAa&`_}EN4N@QAB)IhD`V19N#nBiMd;5#3eKVDNpOw)1^T<6JyPpau6J^0&DV}v< z+YY(t@FaQc-zv+cwsW1jGkv>LcMatEj1~nx6_DkbyJTsevJAqLR1XE;yYGzeK%So> zhXjp*6zblV0L24~!I2Ue_G@ z(3(D39y)cIvDkcSyfk>UnAV-_*U zDR&XuraRSR=32yf83UrcX3x)mn4A@~0Z+wZa!3Kl{=lNh|vV^@Bx_ zy}dw=iZKyNRJkMyLG_I)Qq2s@CU{K&08cqV58(!eCz`}3#6?EMa7?(#@f?$+*m!8Z zi=cQ9Su}x0Ow8%*NLNQxzew7VAj z?|BR6&)=SI%_JTF`0_=s^L|28%Kga}#Oq#xP@54m923o^y$gzy9+szN5LiujOIg|Y zJx1CCb>-~+SKk{AAMFoLUoyKm{ebs%eUHRTIf*qj%U67VOgo+Qx4qJsz;Zs>GvQWK z$)!xiXZ_7KqFX&`JP}omPG~Kp9A=2KW76o49}v4++gzpCp*K}7yUfihvnM*&CBZIl zj(l^o-%LQs4(>#Y6o{MMN$fR;q)s@biQl+OMFnDN4haf8m6kS{257C<-b4OsYF$}sB+7iWO?*2r9UNw8tnO?2_hi@vlH0ZBDNCMF?q zr?n8UO z9qG9Ieql;>-QAGXvWPj@D6w+8j%tHXYBz6BA!dykym<1{mc#>;<}*R(w_v)UA1Wq! z;pzH=ET#z$p+5kKsksKB9{$7-uc9p7P3;yIi*Ic=NfjN^uHH_WqT9sWSW{oeNPC;9 zm1X@E{Tq+27;V%tFw`|(w?VJ6u(aqB9788KI@-9JI#}(rbhZs!%g#H24-aSvBSnQK zXJuPFGiHnz?-_P%Job4lwndXa>-gT;K;O`y98=EoRF=za82ScFHh1sDrotgqelVDA91Infqh;6M)8qwVc& z(9Sk?7`h!A>)?pRI9Z|5STx#g=MeHgfY>;8RAe#)*zi9HzsMNTxlNX80FVe&uR2d= F`ae-Dvrzy5 literal 1576 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 mZD-S`^B>2FQ-Ptuu>kXb2G?Mgh literal 1599 zcmbVMeM}Q~7;f-m1=om%FLmNkM+Ztjt{+$0P@DGdD%~u0ZNaKlIod0n(7Sqf&=wcS z2o4yaF2YfRx@=k}ej7t{M0N%1_J{G0UGDDpaqsiI z&-?z~_nNE)%LNJZ6L>tHz?_fRxG^#M@Mm)GMUN&hZkWfSPS#F&*#Ji4JcEZS!2z=m zE5&U%<_T>Y!1H*#XaWk6m~%Mti+76XF53X;(Vb;3zrEjN#D zONGFs30tlcPKk0_tN={WIG~a!#BLb~0uUqtl`04Va{##vluKpY4T(XeRt0J0YT)@L z_2kuZ)*k zGmelSm)dZKs-)fAd3YDcz+CQL?I>!a3GPs^3WRc3`f!ppBRV1XMdBemTA3mj zhD;!=hG7#b2SEd>1dS>c1cGobq=Drzj+bFgC=8)0RE8>1BM72$4Qw)*3~B=;gL2_q zgEEFSlMIWIZan6f;QUTu5z{NNTA0Q$mZBXLRS~NIYZ=8-Oc~_|U^@h4ts+Pd6=1TX z$1@czg40A5?lIAn517oamUxMOE>Kt|H$Vyvryv@)T80=Aqg)|J6_5tvv`=9@|0ics zPK-2K9RDen*cMlT(dmoQ=N2!T2Pe6Xp}E%B_}Y8Bc)U2uj2Ilj%Li=h^6gk!&0rN2 zx$*2_ZE3;b>o;bilJV?AGi)dLpSQJ>IW3LtH@n(?Kr`P>t(jh zz3$y@_uQiI-d^6rRP7yYO9js~pMLGLdt1i-9%N3Ag_bR8J5!=)Ulee;h8t&H7Q{D( z&Ev`Z!JjrWm7=7$eFYN}{BN({>F7>rLN?B>79GV_8xCL8Umm;Bx4G^e(VSK!G5&1E zoNb0q8@$$KbiTgFwe{KTzV@+&x=~u-@-@9RH_O9rXU6TJkLI2-_3~$SM5w@(r+Wr+ zo($9;~;ap*y#q+;O+&iE+qzYC)lDZMQojxzZP!_E^w5;{B#y4HX?J>ii@% zCADpfa+_=Yhepk@`2zWyef(dvO!`QCXJ=FY{=tUX{iZL%mnw<}hQ}6#;Uzok&i&P1 zRBk=sC}}R5wLUT2d#wJep}86Q8G+;IZ_&;hxU;o!=$6h(KK@c#U1VAiZ*#L74)F zR$poy!SuVo{5~~1X~*)d3-!PIa{>Pwz3_&sPx8{Mg7+D8Kk`(Zj{n^1ZGJynx~2QV z?KSr@bQ_8nr#-Hj$XL3)E^>Uwfu08I-oxkakAIr9%b1mWr|bOaf}7*g(G|%{3loQS zC$t2JQD4!}tjN9xt%EhIMWPRC8@e9lT|Ii&{_#Na=}m>JGC@+@c&(-JATun^Xup_V zeCf|js#aa^co>fSQGTRH|Ldcc(|dbm=f@Jx>mpy*nnw7~_%hxS;3@~s8J07 diff --git a/toxygen/smileys/default/D83DDC05.png b/toxygen/smileys/default/D83DDC05.png index 94cb5f04986e6b9b7f1560b1ce3ef48133549229..5fcfd9efa2671ea706eac1e84e8afe5f8ef9f4d1 100644 GIT binary patch literal 1656 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 literal 1647 zcmbVNX;2eq7>;-p5sTtcIn*^+4~`_eNtPreLb97cDnU#_jbbayW+4HRO?DR&1dCwR zI?6z;R6I(pbW{eU)r#k|ij3_@2OMzZ&^i|J7L_UtG6S_61lu2uKf1HK-?7j0yvO&x zv)d9AVyF59`fxa$soFSH&yLePkM|_@J^#fkj2!|PwTUs1X^ab_2#(TBrV@bGhNTmF z0yF12FB5VOXMCE~Xktvdco|OGc$f#nbK4v&o5PVuyB!#wNiaYvk#4msxX;d==K@x< zg1btr6X+ZWVX?-orwGIP1S7sa6PKE~(aQk2TgD352nGY(wk$g>b1S$bx-xd|+2(VB z5eSp1;J$Xsq)P-4k|F>xPZ)s^i_2Q0 z%xN+`su{J#t`uAg!#HGozRTs}xr98DO6P-8snnwZK@lt>g3h%wm^;Ewhm0wp1dUTx z2V*7efJYHaC9@d?m-X~@2sVdK_eR)Gk0y#u8Q+aL_#jWfx7j>#ji6~pPyFk~ThX*J z*Fo_01WjgBIC~yxA!B4Vckedj0kYo6R!~;GLmH0SOp|nNQR^>qys<Z1_4!5@;(aFg#FK1G zUB(WrPe%Ah6q+>Xth$lSsSjvfi_?h*=kG492(u?N1+Ui3ONx0Kw0Pt3;D)C;u|9(* zlhVn$y2j~}YnD?hilP?Z#)dRFvpKi`^CLe!p)_2qKI1rDgoNjxA9uJbWV!##8C$3K ztK2m}5HF*6cB8)gaY^sssRX8w?AxCH`25w9=msjO-S6sL=?)&)(SeqgWt_`(UQBb-+RujaUC~(FBfk;=3g$C6m~sZ zmvU6QFuH9Wqmp-dZTfmLRMh5(c+~$4ZD|Z}Wkg zGa*BtNlw^qZHN{s3L6(y2G@c7!1@K#I^Wax=C#F4G8n@03w~NRqc^Zg=XU_oog`}9fpL7)C;-`t0{4XckNPb{uJ5Y^{DY>aKG z=0>)MrZ!5Zgojo1Og4V0ZeW~#akc3K<9Xd#Inh5*#-99F(^EK2y0m)mXo}~T!qKV| J&{|bW(LWM>a4rA< diff --git a/toxygen/smileys/default/D83DDC07.png b/toxygen/smileys/default/D83DDC07.png index 53b5530431e1ea6e73ae92640b43bf5b7743462e..69a3c7440081ab2d0f6bba7b0148f46e8a40cf8b 100644 GIT binary patch delta 1502 zcmZuvdoN0!|gJHA{?W`-Tl1*NHhT;DFpy2e3Uc|z;z4&Gob)ra{xe)p1<>R z1VAp#_e21Gb90kb_qMOLmO0ozH8DQ^l{q!e?x9olJvzCZNL$qGr6jvsdE(?}8>@m3N>=akHxF}p&oSB}QWV06M z<~BAqq*AG9d3i-BT$4y-YinY$SSl97T33ZanELb-2iCA66wJ-=hGM@tE(#u{P{`t*aV%*{W{vy z_OYw^1Kd((-CI~+YsKp?wUiH$q!4%#=JEbM{|;?SPGj=)V!)+-ULb^Pgi!SZHki*KuEklApuQ_jfuK=B@!Kfixi2zNhAT_@Zi@#zsfv}s%*$CQNge?`zK`b()kP0 z;faLEw3MAAA3mxoc@28jR~8nGK{QHjhIY31JH*MF97ao}xoLPeH;DM#rv_2`1U=XK zymz=seFQNZs=noaC#J&l%V;XX?=87WNn^%Z}E~-1DwU zdDLN#YErb9IG9Y5FTR7^VMfo^Zsn*?N+CBVxmn$u4Rzh-m-Po*=?sU%84EQYMct3c zB=?nXMiUloJhJl<`43Yj{LNfV;OBwWFu2*DpeT`+g4BtaQ@UxLE5?7lnS6 z0lN&_%*?l*p1C?8rAEJqtlOs#%P@||hr~w^;v=!)v5~L=HW(Wl3lGd83v1f|YYg_F w9oE*)9D~7PFzS81kN<-p#zb6c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YT~doO%TiO^it=+6z~O6^iN$`PN1Y9f9Lkk2Xf$jIAFarZqxTlL_NX4y|;QQH^14QgRlP@px4c5GJ^VK|AzY7o9{xNV1e;5DpXg80` zk^c+)7b*NOtNH9GFY>IpX$6PVd=|&p)~j9F=Fz*C{oJ}FbJLQ{Nw0mkT-|xDwqVlX zv$pZ){_ZR0Y!Ud=UA0xnbHhs2&}m|mHP@xQs(N_iWH3t)qh+4qL?5y0S-WPOPgBg6 zX5D;QEP=mA^}^P7y(eqr7Af&xP2*NsTwbfs#L*|vvg*Y3DCyU$XZOB8u}$~M`g-U0 z@iyXdy7v2Q?ilMGY7dOlOk#RcDd)63U-5eBM3oW&J-t8cD_j)r?rr|(c7IdA)<2wY z!)07n9DH>p#PiLE2fStn=T4#D$nE=nlP?004M#_cl`D=J9`k2|&-eg9xok%4^rS;WAJ*RIFFtK@Sb3rfr-~zkW8i_p%nsu-h2Ii>ow{Ke;QG2P^`cX zX&;l#*%REDRXb1j-jX`xx^U{*s<+o`5-j}WPfqICR~Hx9_S;#|{N5fJ!L(=RUaZpD z+$d&r{&43_ONEsl8Y%a;F>*_++239AwfHhOx5L{VF79g2zos?!CUHxjmFMS|J?9&8 zN?t!zy-3;oiJ$ZbUEQZbcjTqpWp{Aw6mfKsx&K~zUA3`n@4fiTU$_6b{O5iBjhrcc kOJ7(d#CI%cp3lU?P$GJ8#_oCYA3#O7r>mdKI;Vst0P!C}dH?_b diff --git a/toxygen/smileys/default/D83DDC08.png b/toxygen/smileys/default/D83DDC08.png index 17991b36cf8c32a78a52579100728119a00cdfc4..2c239f27b54f044a12152668d2b8de3920b30af9 100644 GIT binary patch delta 1585 zcmZ`%dpOg382|1x81oQXI@O4!rNVJZMQA!Gni36V)i6q%tdzN{hZHB`F_+5R z6Pe4{VjRX?8o7*IQku)g3Fp_-^PKaXKhF1gpYP}UzVGw-zMuDb-?s|=3JIE&bpQZ^ zy&KO!0Qa@9w*Y_{FaO<32J%stjyu}}5T*wJl?K2v6rxT55QGL`3J(A_6#xz5vnnSu z0ANXur*PIa%-8i5Wm99r?@EfqGm~GZ#`xSOR(Z+l^1|}`?CkXT_(vhDtYmgd)K;1^ z$)q)w6jHj<$wQr-Awfq)s9jfKg0O(rpK-nWk=Hbvu`HexF&{LiQzlq1I-<|C#hx2u z1dDP*>PudJU?$Zhk)V1%4XWCc84A7M$9V9$Y*tyZu$~D8x$oZm(8SE+6+RX)A_cA7 z#;S^@Hzhw=&CHx(O63!&c!t+lw>Ue!@O5&szx`+KpSv&7b*?mGiJP_hc|bhC8!5Qc z{}|sBi|dIw(;JKHraMEFiJ?BI#qR?R(rWrV^Y+(l!nfSO#p?L^a@y2$-!EkkSHz#B zO=%K#+7dfW%FU9r6-sJiqzx&9S(F=tg3gt%L(=+3QcmV_PmPpcCT509S&56RkC3_VPLP(vA{d zLv2q}wMf8QTU#^mJ6#4%&W7ad@MFmT9<}0@3*RUPGVN7q4;yfsVy1DQo1af-OfGDH z+gXvlORQMyX3nO@OA57}^HA=e7kpR-2akx%M{D1W{if~#{R6vr($?ypT94l+$U&0q z?5&aGNX7L!SZ`~qK>&V<2*bIOoV`LbM=gn-K^QYi5%4QX0Ok-71oEPhNR>X$2WhztCY>Y$qAvs;k^`4Ic9j z=o9A8bf!vH$!6FCqHbfIK3)IhY+mwu65XY9q+k@Uej%vcFV)Kt9+jHY(q@O*UmRwr zRjQ6JCq{>S2ncz2WIdexLeQ|$jq<7KWH?!jS=YDIVPYDdd%kqcN~P^jWMRJ*{Q7c1Z`-&(?6R$}{33^H7+s$!LPeC9s8&pOPy~lKBy9D7D z&EPHAYy4;Q&9BFM?v=Wo>2EKnWLjto9OnB+TnQ6nW`dU^+p=0Is2zn-i>Cf8w47^UwH zEvt1j_f_<{Xi}y09WFnSoE)`tX5>mye#ddgI%N`TfPcFO$d8Ps96fQQ^=8*F20R;xQN|6n}&TJ&P`acD{F?5r$soB5aU z=CGJkjN1s$qZDJ<9T)=Pqpkz9&_*Dl#2>z-CVrDdxkWr;VgpKL*Ms&0|P zLjRI3FCjVHJqEiC4Uh;KWEAcGJzFaq>t+VSh>_aRH^>y@gbF=-#U_<8#d53mR z>-#n?ucb!-(|mc((G_>z+1dT9r&AtM`Onuz42(Ag&&V?|dUXIMmnwK_0(Q;W$e-Nf zpFoX$>7kNP9O#25UV+J(Vh?S-Yk(Rac~4aYCu(%Mh!vKyP&>LLYgdxD7wIzg(v8ay z03)=K(O&eyy@tj(Lp1h)3Dy|12aU#}(MJz=DgK9WHPD;j6AFy9{*Q6mC!jb{TYo(O NcGeD7uPpI*{|5cK6IlQN literal 1595 zcmbVNdrT8|9Ipxld4=hspe&q6G(w=)yS7*Eq@(R!i=7rLpkU@Qj`j*C^zOVKw#64C zx@dHpG9?OTLB(z215KPby2waSd;rt=*oNZ|RO|+5hi+;x(_N9e{bBrLm%IBt?(_M4 zAHVN6x7C`P6B{)vN+y%VnirCJ(ik6nA|s@C@}n1&G$e|~A~BzHh;B-tWqLbTLIY+e zRZ8d4l-;}fBAq3ZO>r;d3Waw>?Cdz`$)mdUcRJv?PAr$wNIE@fCP81S`& z0AtsJg=&k!!V`2Ev#?5_^Q&?TY*pnp%noM13uJk4Nx(^q6yR}IurA!A1;=%9X&&5$ zKwumqmTSS6P8C_K0Ko|~pq48$Yzi0#P*e`9)F=w41Be1fAcb_J8L$dhqd1}gCN5C2 zCfFT#9%-7eC9SkznJDr&1i9UAxmzjcgi;8`Ff6EnAQ=)O!{uc~%9FvmQbGzO?Xn3B zFEShp1Qn?gu2R&3lBX|2aPk(*D`D0(ktiu;kcZ+SSgwGa&R|^QXqT8r|LewU(XIk7 zPeXaMi>nlD(s?*iLS!j-Z#EPJO5Wg01V%a(s)FQfl}?%!&7>BTzR2y29aktb3Dg7= z8iFtx5g67RRj@&=Mq!x9L@@#hX}pfBH<(a@MAb%>(E!6nBoihO0ydbGB#B@qgCfK= zvo4WhZFI;kBiWtcs@}-O34x|WPAK5Gickeu%Q%s9m2o^k8sM07O$FzW~Gi1q}J#v-*s6gn_O-t^#z{3eff))Okeu;bJl-#rj^DI zG0{%PwxAKjG0FE{rGTb z+l*Ur_q)aOiJeW(#oPDY^|iPH?f~ZZ-M!zL8CMc>`F4?iY67nt&gwtc(qn1I75dRT z*EBV&)_t3!iwP^0fAAElA(5Z%VYjD_ymiXcuy^mk`>uZfdOmZu4*s(}v7)#7I~7Kr zjc`ya6MSDeG=U4dynU;O9&R~y&|Qn(us8p3;XoKwSiNIDP8tit+NY#8)O=LZ@kh@7E#BNMIapiW0!?ded@=m=_sCiPlL_pV zp|%IS_xQ-%-CTIdw#IH$@p#_Xr>3UaChd+p*t>Sx?EUnaiu0ohmX;y&$hFI9TW?#R zc3lb-FEVP2<1a<#1;W#lntq$zeEJ+b%wjnv)o_D?9Ns8?9CQ(yxQH)Bdg^@zdU9 z-QE*DZOJqGOQVyz^uO+DU#>6q-#;?8!MyxIL)?PD4*52&yJ~-qo%A;LXV+3ITW6lO zqz=#Nx)D`VclzeppO=rUZ*B26%-@}+x-$4k8#&lBrZ_sVV$rivee>ZiL-U3^^&iik zWm0FQZtOT4Sp7@ig4k8xIJD2!-}$g}_;CG`p&n>bge(yoiEK8V3;qpcW@9dS+)!Ne E4|_CP$p8QV diff --git a/toxygen/smileys/default/D83DDC09.png b/toxygen/smileys/default/D83DDC09.png index 6ce569d56cac08b6307869f6ede03df31d3f9b14..77e389571c65afbca16bd96af54fba3b84008672 100644 GIT binary patch literal 1856 zcmZ`)dpOkj8h;s*aT~RZXx5P15(Z6))e7&V`MNc!<50wjM*HQ#M+kB zN;#!yqodtchbgxtEs>PAE4y*lh(k8}o$T|R=lpS==ly-Z-}imLpZD_lzR&ZMQuq1k zV2!Z=phF@1(9w=le(N!4wXxR=Mw=SjE6@v|`R`gQ40WUvV#)MCfGjHj@s|M0$RvIZ zkVXWUjs|e60x;yCY^Qw&psGO)2=QHAU4>{OoQpr~Wr_s2J3+h`a{X!H4t%ys(kVkNc-3OQV#&2Vh7czE(gl^i9fW)|-qjZ&ptwGj|8dXu| z^$92u2s|O#1Gw8kv`2X@+Z$als9{1qTPac+Wcxv)JH%~+bWbRvBcRD?HWUV-fNP=9 z3ZVp`;n!3s57#>xv#FM)jP`{!a3dkvA9E~ z63trEc1WXK^A1|IXJTq%Ak!PFqjb-4@Ygd)l7H^?`qhm1tXFTn znWJoW>AZUF6n=JPFz;hbe_gvGT>3giWLR5z;A-EBVa;IsSIo9kq3=d@2YyW`yy(&N z^2*V+fyv1Sk3um`IcI0yPQH7z_@9fjlh`+srKQQas>Y~#vx0f~H(iTe9h9X-v*7t; zO?~M~RO-^d8w#5i6kGDUrk3Xi3>+BG%TX~ZG5&r&AW_GYk)k1>1o~>c)X>FR>d2oB zmY_M#$qES*&>0y7eo9gthsPoagnSkuGm#JAdaNS!Uh7HXdePm#&2KcvNot=ND@Ib6 zNAJ!CJW9KmFn-X(ZwqeSI?2?lMbjyhOI*@aqX(B+8}Y|e%YYV{pEJID`utq2xAp8@5mawH}mQ zEa@GT34T~?u;z5~HXYRp*gf~+1UoCwCOWvQf2FMYyE(w zIfeb$&mxaKu7ab*pW4N=upLrWefyJNE{5`GedmMA|JrpOS2pv*>ex+{}FU%kD?=1(PcmoAk)B0N#Sz?Jbm2>z#@1lxPR zUZYt-;AMVSDDSkH>SY(caW{|5@^_QknRY8omeek=C+Bmz`L|{qY>Jcl*z(yMT>r`~ zX3t8dJIS|g6#|TK1MTc~$otZeD$9@%O^p5P^v+ZQFX z>63Y!(!wbl_iw}vL%fw+5BFTVmSS#_pYJkf*mq>6ZGQOZ_4#qd*2?#8Ap_!y-@;8Eh7v W#b64PdsyhV20-!M=hL)1I`2;cJM&!t literal 1802 zcmbVNX;2eq7*2~t20;-J27$7MN)aK+1`?7;gs@34(g22VO{KCXi$vILOcs;iQSl^< zV&$qBTNJNxifGV^H#&;eD;|g^u~Ov*(ZMo+9Ni$;{&4)!o!$Mu_uKb*-h0*%7Zc@Z zKhvH>A~}i{@Fm3RWPR*xiMLPhC`>FKxF8vi$I@{vtVT#YIkpr5L@2xrksz>Kzv4O) zMk3jyE2PPIviLiW3_~ffRfnQORRo$u3Jce%U|A-D151%*3MH4^bGDWYDCAsn5<^TA zs~{vpu^>l{#OK6FWjUELwwxS39|+TN2m%zrVL*pwDK#7&mpsPHA@?k`VucEXAWg^?@c0lE zGJzE-H8`x4Arp2Lgxzs0^jRziQX?>qsihc}HPHca85oXfGB6bY#WMl_B!yCrX*F}K z<@pdTA5kk-B66V`LxHjUaulDi&!z{{d3**01@j5dL6FB5GTD(JTgYbw2h-RB@;Fxh ze{x18oS|Bq<3G(ZaYS^Wb^EdP3E|`LAWC9l)Wm4S0HGtq#BoG?o>cb~FG4folf15$ zuflropSUen-an}D#4=OQ96m6~`$AK6%8G_ddtAKE6b!&LVcoR8=%ckd4StGaCtPMKIB}q z<@OM_qPu6_61Ng#>Es5}X^$BX=kD=8f# z@?`R6Q9jnTx^Gigcv)=}jkj)8=d#|o*vTF7FLRFwG~11|A1vF1*AyHaZGE)%)T2G2 zLvY&8SWU*F=RlFgfQq+(tmO*>Zk&E~W0w=oNs|wK$%=M1yn^QJ7+Od(YR@j;y{06q zJuP7FQ%##>{ljhRDpEHef@cSG*UobFzIg1Ydv(sV!dJF8Uq2`q8Y#_+1%% z)79ADIjQMX8MVeT3*<^nUohuwkrds7VsbWm)|98+YOOk-qnfMNmpd8EZ&#YHmp;2{ zZ)>iuuF?%|igF$r-f7QjvHW&QzpKT=JXqA(n@9`UcK!YK3#3fj%B&Hw#$+yz&9=U*+_d+pdU?m|FU7tV1rdg)7ZJEqf?%(-0twt1%@Wu>cwYex1T z>7$Ofg=W9aQETFgR|f8%%6k*L<0-qMFk$z#C#O5(reB$vRAAUoKmOGQJh6E3HlN#j z^E@KD^LNRQS+!SBAYIWjANa?;OTQkN%; z|EZ+Mt}&^99X2e)qPZzkofs}_BIL6R^L}-)_;Ca;-WZYdu4$xOO5>D`)aWkj-$^8h K;a5ebt^WsZM$xhW diff --git a/toxygen/smileys/default/D83DDC0A.png b/toxygen/smileys/default/D83DDC0A.png index a8e76cbacc1f495aea91f12cc66a2a580203bb40..bb83653cb4467b569d6c857730f2ead7bc25baf6 100644 GIT binary patch literal 1800 zcmZ`)doBD146)oD%)HJHf_OW|rTSj*}Wwcegv%09-Uv|3Z?EbOebMO5=?)UTg-h0pYoU0&5 zhHNpiFaiKx8@ zg{1QU=i*E)8#W8get^*DaKkK^Vcx^UV`HYcu0`%;!_uGkmiPz5p`0WG+X&DUbRtxt+C-p zaL|Fb74H2E`5pM5)sDA!TZ&6?%i%~R6MvfOeLTvqib}n}WvN67U72P%SPcpEG7N-a zD#}jcd+1VnT7MzElW$+_f1n|OH&DnQIAT}o7jQN{MU}_Dc{F3ND7-aEPvYTuB8+l@ zBe+={cZuh4lIZbOSYoFjtzW|YHY>b2Q9IRne@$#^pE%=25$$riyPO30D9Se+$z4V6 zo$}Pa0@~G##BQNqRSdq4!o60&=**(DbLgF!=+Y3o0(?XZ>p*K#_E1Tvgql5A60785 zj~yVMJLFSI77i8X-jbzu#tRkz2+oA-sX8hX;b{^2biTC%CC3R(EA;!o|?t5*+^7Tw!}mq$khnM!)oG=69m z(JZ#?f5t}#&d{kWn~Mp@R~(U3oim~79VI6aJqw(LdL~)-Y&~231935$eQIG~e*c{# zFRBY~t(#VJYYmoyi@vt*UDS8`$FE~@b9JUTFlq0=!Kaz!-p!U6B5Oz?l=~4)wXVPu z)uz!s^uRoM?VQiwZx3zm%W6$~9Ya@cr$?4qzrYMhD|)W1IeA_b*S~m5KDo#FJ9A9& zklwX|Ml1bFGoDXk3;NT#2{;{~y-Esh--Cj`T;;mWYE#OCzU?22(FvR?Xju<{XXt(v z(VfRSJs4WrXZuv(8+H4wZG>!#dZj{$Jc2X_^P{k`jdOivS%!Tbsz2C>*c<6T(-bAM zZ61&4>XY&WgA3ko9o*teHuhcGUDqXSMOc6AXrQI9pD!u7dC|~tym!KrM{PbcoK(tg z?&ifdDiYBu^>bR+95Q4y>^nsaY)5zRF6984&@sF8W=!w>PGRZxH$UIeQMQdV9(N@t zTFrPGGXBP9w&E^rS3lbKJ+k7qXO#+WANHumyD%`*TCLCU(SE#XqvLt?S>+!m#n_G0ye*s(=MsTFzPuPy!fQQMa*l2D-3>G4Xc*4b*=p|TrSR>yA|>;0xE z3(tGhwA;Z}VBjHEFMYUXI|x@2PV|f)ORLAK;wp=MZHhhK-jkevXZrl|jMZ?{Jyq3I z?l(SH-zF^E7p?D52a83^gx&`aVe$_G!ws#MUmtH<&WUb^+VeFjh!C^uaWVsv6c&fc zPUg%pq#*LB7D2g-`DHZ5)gXz!$II2%2IEAd7~P$#t|O5m!Y0)Z?mw*3bE>L|C&y4; z#f?pjyOCq5T*lPY6K$h(Gt-@CYwLcUU%<>;g4JWc>^$>hZ0yru><=&myexbo?e+WD zmKGC@sN1H?ONYGruU)_1cbN7J3|`OMz0#a8y??l9!=%E0q((I^Q`8`$c;|5ZHCTM* z_tVe04;%J#7x7ysR}*LawXA>AGd6Qc?0qun$DqO0ciYx}agO^!E<<8u5N9>&}g>F=-~eV_<|H3NBsW*JE%pPU&>rlfhypL@|oFeKu8g=*l-e`$z@a6%oK6f S7#sSY03Z<~gSrA3rT+pB%Ev+g literal 1712 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYa<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3owACkt0MQwtYE0~aNz-V}1eOdseNeNf^+N-QuTUe^PzEw|al| z{^EVbk}*7sg=CmDWE3_=TY49~sIQbgaXVp?nGE~xru8XY6HCGcc-U_JZ43_0TkQFu zvi{_@OBc63c{2UTD`xkH?-k-sCDc9_Nee#6cC#SXPA*Hftb5ZOk0pWIP6@cmEz5NJ zz9TVGb>FFQ1_}1&34iFG-gm|H?%aR76l&(oJo?Cs#e1X0qLm^=Gj+0R z>|V_3YdF4QmhZYYul-)%H|jPmy?r*yQL@V1Z0ELL#c#!QcFz`5^^i$6`*q%!sXN4q zxBb?`(9$il8jCOdG-O$NaIIX&9fv3GMS|OkC+(ZAeYCRm{G!&rU$>-Km>ECFu(xNe zFRNzlXi{I0wLN^tBLVL0z4K3`|5y6X*>fTBgcMIdpSb>n5H=a_rc_qbNm5@rHZe?H z{dl5Yzi?{abaT(wT_?D+$3@P)fB!5JG0!TMg7=0t>uBxgRl^jYrmAY*t@MsKJ!iGp7NZwZr$&t+g|1JCW>^sF7aHq zXi|{p(G%_cC!TD|;A~GcHk;S8&tEzI+Re9u`x>M+zV%4=USwXwz@4A|;4)7^$e$}$ z!fT?V4y33|D$o2>U^Dl?X7j~@O7V9kQ!k6YTAN>0sc}c}(TZyU{>FB_eacD8H|&sd zPRl#D@aK-1+9~TLjeU z#GE?Kdww5+|IN6t^|RPEGnxEpdp8$qm)Fi$|M9{<*-dD7?7!>TZ|vXP-W=MS^Pv9G bb_QmKXcv*(B}F$%L3N0ytDnm{r-UW|iRp;* diff --git a/toxygen/smileys/default/D83DDC0B.png b/toxygen/smileys/default/D83DDC0B.png index 9cc51718aefa5d777fda4690e63c38d9bf804d29..878e117e06a92df9b1f61cd372bfa8ead0049e40 100644 GIT binary patch literal 1699 zcmZ`)c{J2}6#rsI5<>Mz(|amQ+q_08CEm+2mNLblRXt;tBr-9Y86L*kSevXvl(jU* zZj2-`Aq%wyz9}nLmqK%bl8X}ypAaJ+cqNyw#yf}S-e7Sc?H-lq zelsVCT>@z;ivwv$7|A738T^0!;uVK>JKw=C6{@fTDKQWI5(@m2ihfTlqQ`MPnYj>u zDKtAEw+3n?jN3>~EKR9-^++grD(>eB#FP|XX2U>DOHXUph(Od=R3i+}ua7EjN~-F* z7@Sp5Eou}Gyc~ElH1V-bGQ!L#i7gYxRfs|!3IeigZu3Obfh#;)oXNrQMmBzfIAIQj5#8zrTJRo<4vZ>ekRXBP^%Pej3K zWz;mjPfCSfLUDLHFOE}_QP!AK(VWR^PAwA9W3_$=Scl~O;#`ce^E<&^TJZ>Q>|TYZ0?ozcL| zT{G-ksD)umCKkVT#UKTUC_YqE%H@QmIbg!KT0vk9#TtWNGqFZVUhC)vQwl^$`};ba zrC@PXO|q9K>54m1lR_sGHEAAX01n3_I-11wqvWb*B8^OBVbk75JKiO65HpMVuB7gF zpLMqM5t|vVt}L6UflFCGT|eldMcq7DEpc%%$S-t?E=cWjdrX<|@4v>cQuNnL(Whz}_3Y=nDo0sr=UxY(1+;H# z8B8a{>{^TJZAi_~CGU-LQn{wvB;5ozxLGl6%43`IAw<1}L%al!40e9MbM}I8ZDn9W z#3!lJf*tAM?BiO%S5JToJpF#y$?58so(Sct=_TIGq`9S6@#Ybc_6%-=>7M(`!{fV4 z0(B4K-pYS_J6-qUz8%l!n#SSEtCh+{;tD&Ri;iVwZ?@CA`!QmStEHafl@@i39c@#A zeKuEI$~qy`@MY~nBQ7ZT`^Sz+&#BT9Eo?Vs9CCIPZq9pTEWLHQQu@9N>>xAxz}-!! z$K`Ay5Q@9RaX-{|8H~-!%mJ>Y^Zc&_h6VM^MoawbPOAXPMZ@&i&|ovSoZDmE`nTYt zB&UChd%?A2E#2Qki_sn=Nj=)kxVc`Kt=*a#Z?Lcq{v{#=e&vX~$1pfFXY(VerwmqS zFH+XusSH!tE4XT{6x%d}T*w@ne5^>F;y*tpdQE4w&suvQy7OT4#QfJitaMYK&t@FE zbeo%Ro`x;~((_37z&rcYpxtbZPSs*}e4Qq!TZKVV6!)N5Kk@|aX^ttL@6ahTPPa^#VH z6fwy6%%+{P>62_p<)5&D`kb+Z&T@*e>|fEscJ3c({nd-Xf~-+h8CkRJj?(GK7S2>^ zZ`FLb1zuKm!^`md6G`0_Ut>ju*A94Rc<BztJ%W5u1&f0(e_?a$Jj3W2bg<$ zFT0gS34UuVbuX)(F`#O00bWk1jJ^yU3~Ef9f<8+j;3z~xycZENU_WaAek95Osi*It zhcY~H&`{rC4+>?7La}Xzs{WJU;YqkcqW^!wTb0YQP{QW#FW7sMC{&ym5ttA>U5J|2 c9=OXyED=YbUvDNtKL-HT=#%D!rq02C18;3NLI3~& literal 1649 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>#D)oX%Rua$FAYGO%#QAmD%4lD%(WaO9R7iZ)b zC^!e3DQJXe=B4D97i)r|2jW|o)S}F?)D*X({9FZa_*!LRvER_#+|td$#MuSp0-(Pg zU5zYU99_-L9F0xQUCkYppn6lt2{V15WAs6Z11YhsVgMHS;UVV zX_am+RF+=eo0xe;Bg-SDv2Y>h8=sQEUNsZ9mPL%g69gtK46wcsf9v1L@X1oWdM~{5 z?p2$ApHu$c_WMB--;gBPaJd+Dv#5QSR}@(0eB#}7SO5OawM_oszqL!uvJ+qNG5clj zn~a$L9$bInBLsFE+G{a)3IFW8t7!RL3m zUOX=;k}{21**jw;|BmTFnZ-6@=KN-+F>&$!rEQD3!w+Blm&jxm!}nWv{&{BClZQ=s z+CsUGZa>*`NiQN|hm28KXl3B!lL68*b*yD)n@fLh`_mZppJ8I2gKpM`9dkF_PW7po z*XsP_v(M#?e65QvWKMT|v8!>Cn%``m{=;2AZgH8KZf041x9#r1C<~dgl&Lbu)b~3z zaBWZNG-$td?nsDlwm8c@{Ykq_eB1LR3Iqd+ojFRc-ZrojY`*;T$f;LSK_5$HIKmR^ zH%QH2_FUd*;&uHq6F*eAgnrHIdv4K~9;l+?8v5H+&6`Q((d;>Y`PLurZ18r{eR=T4 zmF+oJyDR^FSBQNhZS}JB)cMsldH1}l5=AQtTqLJ0bG$5aDEV^0i6_##IOBAizASm3 z_x`4Jy|0pD=0w%g3u33eN}0{ETVu)+rh^LtbQ1Cm8=qcb+w^nKvaDafAMWE``@hFX zQL}bKFw^Jp!+P-5Im)Ly-Ad^(RkH7X9&OWMeSfX`A%x=;2mSCE)4m K=d#Wzp$PyrOK3Cz diff --git a/toxygen/smileys/default/D83DDC0C.png b/toxygen/smileys/default/D83DDC0C.png index 0d36155a51dea54d40ffc3ac4ce1392013fe03df..700a0dc6dca5adc21a979ce4fd8a9ec91abfac0b 100644 GIT binary patch literal 1883 zcmZ{lX;4#H7J#1}S&T6>Q3z{wWK}i;Lclc5PE49b2m#qmAPH+EfnW#-f@EaEI)ETs zD_x+-s%)|a6oEh_A}F-9i40grkQe0JIkD-=fHYYg9O%=m7x91^|%G1As5! zR{A0UB%%Rei3|YPG5|m_t9rd{0pPn(FL#pb>@WD43fxqw^TQ&$(fl7Xqco)$5ezNtWEc=NJ^Cy=u&#!oivwvP_51XnuImj`;MTXa(QE8y4)&(l` z(P481cB2^%lBVFDmnDk>%&r*2`cQ-*#YS4?`?S1S5ULO}D&c$_eFx`=AVvbHzh%(<mipA0-|Ls8VsxWe~l_0%l@~{xz#syk3EFRbU$fi?e(`hf7 zot{?cOkGwJEjGlW~>Fb-jV zC|GJwnA=GgQFMPqFt|`)6FgiMUMs+%`p}>(*W+M+bCOB^Y$An&V#deOqhe_&HkV05 zB{7%)V3Swk>+xG98YW#n^wY&csz@t~ELctn`M9CY=Xp6vZmsUe6g8=Tot=F2&VDgy zs{&Kxd&&A3rhIm&!5A6d_!=WI_=T%kFdB|_wyV5U+e=y@cBFV`FxtyIJ}=Drk075n zXwYpA4I0qZ+gOD&l_qmA&1Qu%kKl#pev)9PlNkUeS~qE|%@{7DuAPJYu6u5=HPemp zEaosge#l3!#9I07ybl#o4PE$0ETTjsX3P(N;)56>+qPV|N_W#RL$l1T3_g0VW0dG{trk4&co4zqsH8hfPqtZH{VnI`P-< zV-x7nxJ%^K#}XI(EK?8Tzrz3NsSct^`}+2LRl20lorlzuVDCrGlXQSu<$Qq5(loGr@*rJR)TOZ;JQ8U`n8Xh`$c)_dEs+NQR5>BA=y4p7 z6P_jJ97V74Oud4t4Ki(@i}<_?>j@)9-5V3@Fn7MzfumDJs{FAkVQ#`2txshi7K{K`Uh1u7S zf^n|E4AFZxs)fGUoP@WxlWX(7dDaeZ)lmH6{kG|^XE;vTah?ZmhJjtVMR0K>sXYlN zI{=|}@@dE9ae64!k>Rm>yuCbEmvXhT@{wVEz3a6!YI*sqKUB)p{STE0QqO)S9fo(3<{Jy`w`pMo07aKo>dcX8oF0mavzX#qMk&E zt7xJLvQ^52lq*A;1a~Bf9@EB~PpH{2{F;J0lOH}yT(*vtcKlvioH0HzH^>_QjUnN@ zk#?pO97IC8+Q;WoHQc{p8GTuXN=m?5w;v~9esvgp2Yt4yKbuNn z)3D+3G%x@bXbTHdw6&?ZCCMC(J#K}yI&O?cW6|j9`c3QqN?^oMqv+iKpD?xVoC7AP tf4jjaj?U&#;%R^*H7<;XA}}ZsG$M^c<+4OH@Q()o1XoX&7ANw>{{S76Ku`bx literal 1710 zcmbVNX;2eq7!C#0iGyH4LC3;2C~`=W-CSfP3dsfm4M#{N^<+sl5Fy#PSxkaL6+sm7 zz@vy@2a(Gfv1+k0MJXN>24Qrl6%Z_1OgfE5bBT&Xg=Pb)O2bGX8B5XXMT{rkH!%RMTEs}? z$=EW31WVOMW)fIJW{g6WnWhq|84DKxp=Ov8&|xGBn04uTBWxBiMs;CoZr^4xfKdpU zCStsGN-2v2BshTqJSHbt#Rfru&u4;M9-j}+10XgCvDnnj4+gm~j}JovVC-U0)(Eu* zmLm~kwy2eekxG&Vn8h-gOiUApi4!R-P$(4IH6SRMLIfKvdJ;7U>y5tS3J7LY5n2PO z#r1$)5lzN3ND+hb^lb<_gG}~LSZ^Fl6qPcT88xs#CYzJ|m07w$}fPbP^uf|Qr0Q>R0 zjTXTO?K(^yLEt)IG{3O+J@z>eB7_jBKx+2@0>N-d!WMEN0Tn+xT)-1D#<=SLlQS0O z49i{||0$O7Evf?T(|4s$E#5T`rl&fFpjxB7P;iV!`{-5_60R`!mhEkib&vOGX|lY` z(;a@9=WLZNyCu%a9gZxlj^8?2;J2pF%d<|_U3$NFk+LoNPD|co8_nHaFyW(u+JVjV zyy^73DGAGZOFH{Xe(rjBp(=Cl>dv-?hF?U#^%)zU#I`(heZ*Xw@3u8+@J#B#>t9T( zb;};~Z{D^A9bCB7hCA%0r6HAg>8z5)mt@KNCb>Pj#2MUl;=3cy^TT|m*3Iv`a-^#@ ztAo4ORx!tIma*aY+6PkQyy;wvxYg%=cl3rsHGiLNI&m&+NVeYkS91JP z5R^!7$jZMqJ5@Onvi))WG;`Uy+=*L?mRQSsG+`d**!OGU*BO>IQka#rtE?ynpvpl~ps8&JuKNeV-mDJQ4KcQEx+UK*%v%2l8H|?R04#Ek|EHr0eLhKKmrDW zJOpJeRoV{Jq7`V7q9an=wJch6>uX241zh%*>0kTD-kE#P`M!G|bIv_8 z*C34+*^*pI0BptK0vRDG=56y9ac=jsk0r!fB|nM}eAcjjMQ%x8&x~+c6mZ-J&{YCU z1f}~4D4+vR(tyA=z**ff6cYrjnUqGx30GHFy}Y~-PsV%RFzkSw3aLHf$VhcClMTC& zw*?X^QfY*l*^=*!2#4QvGbC;kToC5?8v~ggni$C5j7TcfZpd~shbr8Rvm6v}MXVk2 zHsPqN*{swXXSn91gsyZ!f)irwaEOBP9S}RP;~Xf&<}(9S_C z2aOE0bI}}tdViea;M`vHhvVBATovOa6YV^F7=q8jFcgVzWEhuWa6f7pNZ*W)-oec% zj6|VNh%-DG_n=@qQe2_)LjxN|7HXNOqNC((BB;u_}j}Qq^W9bo2 z0BemnN@)Ga+K%MSbJ(XLD2vkLaY-7PTE&kB8 zmp@KizI|LbBVb)u4NPz~+b9&{y&6v8jFaSFC;u^Tb>+Z6O^@w*?spyYa~!diPouwa;=u6wK_ zZmLts-tE_NE0kl|BzAdy&ep$HzF_m`s)SmjX{}<*<0O{aw7+(H7&GpRc21oiOt!GO;5aPGVP%V7S*32^+KlnVbvGBRU#;xhi)rfM%4TMW zezx{eIJrLT)o8-Ad5w3RyUfeTNn}M^D>~M+oOxEd-mlv9=&|RTE_c!=T(MP9Nt#FV zMQfkki>EgE?PKCjdhan$o&cW z{O?&Z+4P+~=}gu@|A<9k@(lNC(XW+E)#s@W%H^ec zZAR?7b%ySCu3mWTK(R=fsFma%Uad5=CXjz%H~C1~j~)w9Es=|@MZQYtrglH53!b}1 zRJKhcOwwe^HHyHDd<7xEq%)a*^Z-AGe;k7zxQ7+!AFz{752VxEBL6=0KMtzg%@~ literal 1584 zcmbVMdr;GM9IuGOq1^e{V5l1*UX>|*B-%%63(};OT8mw&&`m_rHrNcB#3a~)SWi$- zn4Ux2$xA0?p320X;~eK6Iv>-aw^LNcLv}h9_2iUI+<7`Zmm+oh!}E{jlKg((U%sEu z_niuB#)6pWDbXU4D8`hI*@SgM=o$Nl@J<+djtfgXZ*cILbUyFHSwfWNrkw;}^5QE9 z8-crvSGE&ck!W;2X?O4r%OZ`7_Db=Pj@0jE1hhz`P4zRltAOAEC$WO0bl~83$3TE| z>%eS!Q+76TS##lzYZMc)d>60u?z%; zRrmrO_`)fN#R{M_O8^S#9EnQ~L4Z;zh2|=hN+=nCwnrqJ)&m{5T_nq;i?p8;WaKo8xW7zizxzo3j@) zgv>^8bP?+k>XAP^3>I?tdP5;a!5htDmK2JD7h<%l$V*VX3DbeX7pa?cYhVacVrsq8 zn1-MRLC~Xer4dHe3K*5cMwKGW@hYrbtx%`w^(Y2o2m~2m#9%=6MmY+r(&PvN8^Tx< z#ql`hBEoh_!R`oF|5~gDWeJ?8SvyS^hC9IOp?R9~&54ChxvzQVp5Lgab_Dpw2MhvG(5umM4hN&_@U1;Kg@9KpK(PtIh5GqO-~ z{HIyMM?wdNwl7;>5MB-sK?xJX3Zr3vFaN1X6p?4b((L};(rxB+JO0+0wO#jjHVpK| zchvs*!M8Q>_8E;#M=-KAw#+bYc3gkZV8pN%EN4u|!F@|UH7BM=myeD9DAEghCQgcv z?b+0XAm{7axOrVuw+A`d?Hj8bhMotmA`2i(_0ANcy9Vehjahc7c4@IC$aXZCq|XL3 zTU~#|-`+F;@usP5YaO{W zm%gJ|e!99dau=l`RaEQVTvtlsCl8hkefae8H=Vt`QMRK)>!+OYPlP=RF5o9vPCoiVvit0zbEB4lcZ&40zN(T& zS~GWuPsCa`YEw4Xtt^wYT&`>~x0W|JH`e4V(#qBxnlbm{B5q}3bKlk7M=$o<)K}E! zDj3!3Jwwyhu8Stp&DO6)NMifXeRnsA8_jPIGT(ERj#ZmB@YN^C=d*sZq-e_=Riy{a z^GzkHnP5+FYz47UADrJ!r;2Aes~&FNv^nWv=T>~*WKVOVHBa1mw&2J%wCeCdWd9Wj tulO~$_HfSj^X=KCXEQ3g&ZI_+7hRv-{ds+1ATjh0HyJXpgZjL(e*i%@PwW5y diff --git a/toxygen/smileys/default/D83DDC0E.png b/toxygen/smileys/default/D83DDC0E.png index 9080dd0e2a17f6f61f127f2773302e6f48f98dd3..b5774b484c8921a13d322cb2106e1b6c9e68eb08 100644 GIT binary patch delta 1485 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 Z`fnWb@CKa;ZPOH1{sYQhTsQy# diff --git a/toxygen/smileys/default/D83DDC0F.png b/toxygen/smileys/default/D83DDC0F.png index e74447a809bf82e658f89cbe0c6174c01415cdf1..69c405b08e7d1ee7efb04ff4c9468a8a0886a5d5 100644 GIT binary patch delta 1518 zcmZ{idoD>X!@7)( znLRE;<+d*2ET))H4btWsT{PpISc>hK?HryC<*&d$b;zf6o50M(}4Cf&hRgVo-cRkD*9VnlB z)iL*~eRQC->p@Ok6?0s~TbO!OTge#gE}kB5emCDgHQw~3o72Egc{y6UFw;9ZQd`8P zhc8TZi$%QN40d-)PEQ6~z=1dsae`t9P7=;D%xbn0KF5UO;sigDUf=pef^1YvdD*A0 zgFD;QpMJ>JknH+#q;|2ktn;12EA1f>G5_t=c*Oj*{ZdI0vXx`);|AMh0*GhuAOo?F z05|d!`?I$4^YY3_JtYBs_O%=UTdpO#c~D$PS5eWixm0YPpwtH!@wphGbC zg|~F4Cu>kb|012Qn?@f~0p`mQEg zDZNCpI^IBYfj_F z%?7v);R!3r3MDx5NI7oX2~sfo8oz$X$7=gA#ob|4QQLrv;**<*07Uu=LKKV4j6o~5 zdzmw#a+H`yqrWNZ^&U&1`huF&a@6`*R8QA6Ze^*Ro7ag#N5q@}go zoW5K0p51cyg++?CBI0y7mpxn4L#ay9KG*c@x2UpSz1g)U0w`0j$B}boRE~ggy4Kt)JX!4&dXbrd_|Y}%`asZ-(>`G;SV}ykGFBt8VU3d z2q6UrhJq~#$`^Kp9FlViRChHoVu6Q+&5*IaY|hQfJNL8Es$%h&mxTl)S(a`yw>aL~ zWNvvJseqJX6|yoin_HTbsMHo31Q8WX2oCm18>{Oy>gw7$l_(@&`x1rp{*4WJDs}7n zhEPZoVhs!o56dIcsnpc8n|y=AJS?WFIs%Wkqfzk)d<2ird><+rz6E7xWjTwU2Qao2 z_i$%tZ{zoA{cLj#hee}LxN919Ru!-d3oC~hs+hjP{(hYG4vgu5&;%RQi3F0}`lkKX zcDC>>^(eL;lps$MCDim}6*TQ#WIbg$d3AXZk$`V}Zd0EB7}4>-gIc eNe&7Nq5Z#Mr|WeDTSp%WfW55~zUBx2l)nI%lfzd4 literal 1648 zcmbVNeM}Q~7_JlvA`Ha2Ai_KjmjSaMcm1L#9cz27G*V=R2pDF#wg(hwuU@aVMMO~< z>Tt#d2LcX6AwnE76ek};HgTER1Z7P807Y3Mq8oGADT~UkNZtN0{;|v5{XXt_p7-PT zzPa6r39J2lf_)eahMy`9)zD*r>+$xY-^-_FFnU-@DN?BVcU&nn$2Lu#M;f6E*GZ&EuLjCNx;d@vmjv5 zOTZL?nyWU;@oYm}K8YvgCnW3gb9G`p82dgDV@GHKBTiv}-I!;xAa)5jtBcTc*ER

ge;x40n_1+`ZnA3rKr|tR)Tfpxj^=S?0#qh|`3kqjtGG&;GMcYI`65)ohiH{(5nnFjiIiLzm5PL@ z)Xi0yEEHza;cmMI+U^{e_gXF@Cvl7-$Yg@Zb5}rOHbD`VY{Cr4lY~G-iov8OY?esZ z@w|u@#Yw{^T(2YvBQTp^#PACHJQx+js6wQ0c>qI@6qa+vd{{)s&y|V#72nd)Xa(TB|%KJ0tf*v@AQ<>s`GmrLuNO;E3)^vCI!JzI*=C+|-UcFIu{tu5j zuZM^F>aij8e=pYQvQ@yMByGDLNZ zc$n9{B^RoFBd{{k~{|r3V)HLr`;c_H=fZkmt)R;v8w|db;q+*p21$NaMZI zrwi(TuC9wvr5E&F8!26RVQ*2|qOP_L$Bz5$7;WM*g(CfvMeB>q_W4mU&C0dKC3u;0 zL0J?lt+?0w>V5NK^zh0`swX~iXVLTW>9MmDlf3qzoWUJ~F-3J_r^=IB^_6#j94}UX zwXW~Z<9*(zA|H*^_@8l%A=NQAGMi7NkMF+UbULGLD&=d}Jn0Fe? zNE0saX$>)-*2b+!$_Q!~4X)YTVk^7WX;{Cdzw7Nn;?~tC| z>i#b;hjmyVAM6;?MHb{nWd}PJ9jJTq{nAU;i#r=0$l~|R`^Bfq-?M^wq;ZEP-a{_0 z!7381OCnacJu5WDZfxq7R@eCD8B0$*gm*s=T~RfEQzz>% diff --git a/toxygen/smileys/default/D83DDC10.png b/toxygen/smileys/default/D83DDC10.png index 070c460413346d2eb72c377872f995c382affdf7..871bcada7fec580a34e3c682e9b9251d6e219862 100644 GIT binary patch literal 1513 zcmZ{k2~g8V6o)q)f*^>Bpr8=6q-iM{ZW=)3Oj3+W2$)(#Kmt;T;fO-;0tA#GcTq06 z6l)N?!0 zWR+in>VztUZmdc@3JGm#N+TRxdkXM6=5=9kP;uvT+9BOsB9TEdt(0L)-f}Kv$k^Gx%Gj9+{6OZeQZ-d zY4UsLc6?ik=?3-68lruuPb{f*gko(lE- z3uzYKKaC-=i5a!SMIeDO2aj^(90xrzG#ENLQ z5ji+jP%q@_k9}cdL9uML88rj8aW4sjyXK((Y)Ml|oO}s;hO=WQMqd`Mvunj>=8A?R z?PQEva>7v~hwClz2nEa-_4a2HRXP-F)j#m)x+KzcbK|UO0PB&#zEa&nnODfVJ^Qd` zHJKyxUkrczCK{3?y} zWwRwPaK|(5pV_^F_vfvd_8w%amSVDo=n1@z5p-t65;md z06MR4WW7yv>&YkM%3MNq{<9oI^+kN?(YqE?J-^0#D(VH@9eT60Y^dGayJh;)$VuxQ z)oE&oWq^bmHcn&18U%SWr{?T+L(ZypW_Es-=bdfxJTF#QiTR~vRhcxe!}$VNPhpE- zS30z>bWi#Up_Ay-oJcf39~v2bB8&_<3|teB18W#063?E KjurOav3~*V%)+h! literal 1547 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDj~9?C5G>WMXLPX6fu| z1oXGFi-o15lZC6BsfCN7fr}DUZwfhKrVn(CJ}7Y@B^H zS=PQW@4oK#zCUW0PtV$QA?FN_onleP<*c2P7G7z}3f=!O&&lpo$(rSn@{c~>v-_}T z_UzZ;B2R7BiCZ5n(40N_gc@N?66~Mu-YKI?hyKSeOIU!wow)6#qS3ArOZ1Yh#!q7D5Wkmh0SCapwCmgHU zxhAbp?tnp7#uss^qGg+ZEo56c@i^bS9~|m@I;WZXJ~J#8kw~{%Z7%)cER$l2Tz-O9 z(Za)JDkd8XolJ$j+5PMbn(h1coHye14E9>EeL1(znG?o~%hlF~vDIi_T=MLzlVjAW z=b!7=PhTG?CfjIs@sfkcVOO6VEA?+h8|OB%-#RX`sBebd6817y35L!+%KT@`yf%P} NXHQo@mvv4FO#oGtMe_gv diff --git a/toxygen/smileys/default/D83DDC11.png b/toxygen/smileys/default/D83DDC11.png index 6f143a6784cad0c085552c0ca9fa3bb49d3fffe0..7f92df675b5b331becacb62743e90d871d0655f8 100644 GIT binary patch delta 1479 zcmZ|Pc`(~q902eq&Jy&nb*$C6qT8ZzBvjL#N=cCxb=B0dDCwvoORA`D;!fETcP))J zjZ(Jis-xnnTM6ry260qnTUz&7_BYeN_K%(U%zWSX&3xy5-!X3nzAQyoqzD55PU^_o zLqX)G=@nA|%95q_JVl`#al^vy3IO3q0H`ql>_Ai01pw}#0QkoP0Fy@m$dhv4T*U$) zbk6p&gZaV1LB*?d_UnxATI;eQP1Wg*Rq6FrX>}FJkg5_Iq^vOVFXp|@)-nnd3Lb|* zN?tsGK!f7^SV$o=8uC1k%6fbs@-*ijBriMcJ4ROMchE|WHCdd_;<>5T)uo}?$(G)Z zqS}ho*~zBGg{~z*_snF|5T|x$XLE0Fd-coclAvq#%h&*?c6_jEdvl#ip;9+D*9Ln_ znrpJRw$`nztoC-cEG;dewwkIR?QE^{Cp!5P9oyR*yW5*kbM4LfeO)i-_)VPdm+j50 zzOJGV{N}}lPEKc0OMTwlR8wV1@)Va{{_^28ufDP*X>6c^#h^}()#T?<#|A3vtFzy= z6(q*`B*gf1w?1!epycsJYM`w^N5|(n=|V@0B->d-x8XeDNFuxJ@8h<0zRvQur}zlo z`>D!`NGeW55F2yk!<-)P{fyq#MW{l^i*U&d&_sW-fD&=CnR{c|Ek@D)E z;n?hYIJ=AJicS^!yB~46k+RP`vFyjSVCb{M%DP%roZM;^T+r3yPJ*6Kp;(n@#g%bG zjp6)TSYm#MXDs^pg6+spGAhlZ(E^1j@q6 z!-pSoX~y2;a`9pKm1(i^OS%2CB3azZQJiwKv`vbGyLpW|LoE$B($9FO6%LJE+=xlH z@57b`9N>9_w=G*lk(>`JUl}Uul4A}|j&-c|^lpn+wKG4LBOXt0&klRG`QblbV0b5e zq{S>x83D7onL-=XjI^(i>RR42$_0s&KS(xXwCoKOqS^?7$0AFo5f~&0bkk+GuRXP)5;syhOIemukm?q5>L;4`So0~HCaPE=g5+){t1j~ zrPq$E`_j?fyLLZnPfRzsF70V6Ip$OogvD*7($(1Opv7NMWL-&U^Z# zL=>0xxnZS^4k`JD_OrX~4)w;0dWpu5S_IGoBZUY`64clSOav+Z4)>X_*KPtqnM;$^ zG7R4}KxqaUvdjqA4_}+2f0Ug&R+<|rIG|5!zo}yyE6Eq&S6UbY0{x?saYGjBU2YEq zwxR}4NX0G_xI{wd>Tf9&fYb8wo750iaW75I)Gn}g|9nQSKU-KM?nvaEad3Qmv^WRm zA?Djtq{#N*fA+UU+{N*NgGl)OfN`5|_$mJyLgQjZZigSe{~N3JPk;Mmdc%E5?|j>d z2>LAtt-XU9!pt~`7@@;Q+>k8D5AbA`jP>`bST5hRg_92vo6va#@ps*nvC znRX&E$yx>zQWXK`=_NNr3jYc5?R ztmdYioc-W{U4|SV2Jb#45EOO8orS=&9<_Jv)ke@a2{g&(&SY;-vX9A)ARj0IeU!ew z4$4SJ&(J{+WnzFfL8DKjP$no;OBhFA`ac!^f!@Bvu>Y?p*9a*~QPq+Jz|!2>tOVx~ F^)Hr*)Lj4o literal 1634 zcmbVMdr%a096u2l5jC8_H|BC+B6_#CdoOO|g!6WhQ;+ANL}4s zGz1EoB9dSlSjm8;B|1!CSTG=%&M1~4!IWaDNRW^a)gA)%hvOgZ>~DXs@8|Pu7p@Xt;XdW*(#9_hpsRRoo6Dg!w$$#9?#0N-&lD}Q5 z6=^MM!bFDeW{B9`5jy?uRK3E$57__&I}nb5B3K-7P-$i>;!yG@c@b{yJO=r|qzId; zKo*Kbmv+a`s@# z)dR-saZC1Kfxqidx-JZlN<@F&Cs*Cc$M;6dG2Jzctu9PlUmcH~OEeX{mXL!m4?3uw z9pgu3^RoC!4#t+TZXZD;zK z?_Gqgxw|yR`lro;0C;h3zh_*q&jR12Whx0=>{EMhXwR5uVyf%I^9=#lK0MsKX6@1R zxSN?JM^bpL>4BQQua{t1E5}_cO1Gpc20Fg-qawvF3&txhdyg)n&$QQeCwlG+n+@H| zKlJWt7sbWkd2U2f5uSCU;O)WH*x>cCmXTYxCo<Co&xARFi9{$|7<11s}8 zjd|`q`8`crJKdEV0>9L?-P9c1yX<&ox#OLz<_o!Pp0DKYD6VbkbX}&KRUs?*?7#+p zbYXT=?}=mX?HTG#)klQ^rSV1ItqbgEEi4MV|71lM>w6%a-}_S1&YjJ5-~JRSk=1{a zQvUEn2_gIZQ?J(J`CBL=xQ;oS9v2!Nd#r8fhj9OSnuMGycgLhj-dD~Rh5J`oj@8uH z+$CzFU#;l7R-6R;F2?+zy=vb@#hBiUEHSOBsvWKeuN@n`XbcM{K+)sDPg+vVZ>>uazGv~FWk&Of0Qa^e60 diff --git a/toxygen/smileys/default/D83DDC12.png b/toxygen/smileys/default/D83DDC12.png index a584b4a700d91213926c70379133ea6aa5724c13..30c9e4fe91117692e63c488a8808ed6860f56750 100644 GIT binary patch literal 1731 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 b>^M#^haH=i)XstabO5M60pzRR(TDy4BO=*n literal 1650 zcmbVNX;2eq7)}w8qX?o!rJ~zar#hNrHwVdPPcNh{0da3Yr3t+*kRV1P6t-E5JA1D6^=z-*L* z$zrWQYgG{$<|qeEBsyYr21lkrW(1cl1tRPSD?kzq4%o>oiw&{M!7*Kgox8XBATS1D zGUebKr}Wx5Kt<66Am$0V1_1;C5(y6yi6s(f2>=Tqm@i;&2^SI}VhI9=1LGISTBD67 zBmq^A+hSL8FoR*N2%n#qm&eN!@+dl;56NUQw+0MzSp?UXZ((pd*J7JDp@0%L18uf4 zX37G%74bAGhmnJEhGX4dXFSNL8oqM`|$p=cdNWldB-Tn5EZwhYP&s1hZ>!eq0>NafiUxsT^f zv?xKFa|xrGrbu8czlixA_JuGigHbFTb9(?okOEc-WFk16jbES$7t6qLuJQlmjL$m5 zcNfQhie+Mptw8tmZRxX%x6MOX*p8vu))-XM7>DCgp+OZo`)wvMHOd)3{dL=-N@po= z*UpDugmgsObbb{zuLOhIg+Zyi%dS7K44gXs+58*PMbFBwZ-2N6EIKAgK?_$Oc(M70 zWS?+N{1hB4KOms--q2Y~S#baLj*bO&B_FMw85UOXDEAVf*=;mshJ~H$&e}Wb za?xjhaJJ}+v$u2|@3l{EYHA99<`|7jN(!m1tt~lnByfA@j?ydYuhLeHjt+Wy_-tnJD~&D=0q*V)*t{%$Hk5L;02#pTPz(f97vN1wUr?`-v~>e$xSruE#U zIDJ%iGSbz!HWJ4_2@MUs@O@R);+%LdFE6Wf_MN%2XWtK7!sjQu`peIs4_~3!5EGIX z_x#uWXPQg<`ul&b9BJyUp9HQC@oe0C@Sy*LRp3!-aAR*zZ2rTiWaU-=>{8RWBeVWA z+?rFZ4Y;Gae>3=bvuDyDYJt!?!{iKIP-uR-Y1j~zP@KJV%ANEBi%%%FU75CBe7r1q zeZjDA56O+0ai;OhI*&cohmjI1cf{|N3)@k*qWqUG#YpztW0I2vCp@b4^_}8^o`Sx* zt%nY5tAU~mZ;_FwyyZ{)S{}#V?PIt*?e*sF`MKxUM8rm?9&=o+=sm-IAdTW zxa-q6^;5v~^pZPI=p6A|s?mh2&p6AKaRq9+f zhBW{HwhsD7kfxdD6XF9vL&-K(A_n15(&2~@0DiOvfRP8l1|l)u0dSTCz#I(#4;BD~ ztn$`yPXJIXYH(!0*47pYGV9U>4~#Ldh}cFGO$0o&+4;~y_f9>xfoxHqUs!yN$>p{^ zu78LqzOF8CsE;?dr5zz&BnPCHm;bAQ^W;%=0jK%tZz)=h%})@D=$|p?NrRClx8*Z^ zW~#;Ct%m@_*2Mj<2YF0P#7ZIjIE0fy_(eWCdi zym=!K^z!o2OMig%8K9g5gH=E*7Poisg}1=aUC^4XA(2SBdjt}KM$Z@E@yXQw{{F4q zIv>on>K`K)Zy*wD~gZee1`9Qpg-bAegEwte^7wgbr>O7dQeCJb{eavZH11C z7oq?_!#_sG(jyYHp{&!VQZr7ZK=kujDNs&k769(WrBMklhe^1LGX`F{I=HDxcjoF4 z`+#yiAxqW*UmLzuoMvfkVaOzk2bK=Li&NdE+>FY1bE2?c^X}OZlIo@@!`2n&O-s6y zNPY*(@@rZn7bBW3gkL_{$Zq;5Yl-b5jNUVeHZpi-m1_L^4>u%b_G3DP`&d>;ylgaj zI9_%YjT*aKEcat>N~vLCW2w*uzOL8FtlE$Z4*UstL~aoDHY9JqYA+d2#eDFk6n<0e zm26j2KGUZ$uRg{1k}xCVt7Y+y`UiPQIjD->d&h2xSj(i&;}3F*>~U#1>YOv_ADHI* zuiLOE_jx9&)^-8&NAAlzuIBi~dKavWKVfV;yz|Rv6q_T?M!C=DN379_Jf9OeoE;bX z1HPz-ch#!p`%ultrSEWh9=rP< zC3~Lmwe4ymwvAwvOV#IMTX{dRFD^HxCufe8X=z34?vT0mQeM_H9*b69!ZTeTXAIsh zto-fw!%rozXB;c@LpuvyJZ+Dr)aJ=L(G9sUug=D;r@7-!DNVmZ`{&$^d`D_r^&Mx(Iu{S+39tE;PgJ4Zmr~m*`J@EWU_lI+Heu)|1stNom=>0drzkJ0){Tw&CV)&nQ-^!9eG_IoH{+l2z&o&y->Mx?y*Pswv8+eXvHr8&X*!Fa!sQBB;5`E-E6+!+!kw)Sq8qi41+?8M$ zP0=!<{ULTExsa#986#ukeIdcZJ_q~za7&8?+Outl!ab+dyB2y;1))$rX#_%yTeg0f z(abWTUnZ%ihzn)0N!o>=9^Y3vEA6AP~22-_Dk@S$hN> zdrHf1&Y4S1Cg#eRm5CNYsFYn=F*#vqJX=PqD!b*xqf%odF$YckSo5R;G zkzbQMoLxO!_Sun0?j9sk&zYK;|4C$?O3p|<|9=ul;E|(Q+zksGZ2*D-Lj523(sKU< DNMH1= literal 1668 zcmbVNdr;GM91m2H;hapFhk`Q%hak{4DSc%WNt<9hDPlnZxkEw|prdU{8%hB|MZr_1 z#{^Lnd_QjIDT;cs^TmccWHLCW%pC~G?R1Dw&ewFHOA*-q@cd)BB)>;KpU?O4`+hH* zVq+Fh^O)lS0Khbj8rN~7r}J@la%DTOrtXlW(D;4{ACCQC*aqJGTW8I0|92 z6wo`T61B0Qim}q5m@f*|3t50wH%xLg6q}EI|<|ICeptH7k{l z>hP#BTii+kWwNXV6$o;3bNRU8Cxb}0abAlFfhSjrkGq?kn?!n zMT^r`gPo?Ltc(d9%`a+rk9`q>%MqNA5>5{g7{(BlP#%Uzx%h>cR4j+axYYm2nSgUf z;4F^+6wCM)SAovyMCo&jiRPirT*p|s)=0PxH2{F?84ZrbJGzV&eM`KSc{R7!elI%R z-S!COcTL~EGPa`##HM#t2$VYNjpO9TpWRh~gZnFIq}`lR5O@G|_g_7=IAqJV6-Lez;9Pt=@Awe2~%TFR3j?bl%O3#X2UsVfI!wZbb3f@SMv^3xiujW$tt(&)Q z$gt+e`*t0Zk}ucI@+3Yrc9-hDy8>pFe-+?&@ma-@$a`pKPsBr)g~#kQ3CnYa@0Drb z+Gtthk;1>{?w=~u&h#j8rRtBJtWnpc=N#-k&%1^+yD6y1^=h|IE(eBuVgsq+Go7wiNU$z%*=qEO@czw%5#a>^8@85zygM)Eco?{RerJG7mRgOs={!+iw={2j8vulA+Ck z*Br1J7$Ng_(<`$o$jf*wz>keT1Qg_x3?IptNpHyEN?#jO$Y=b7n1-X|fQ!e{9 zuJcXxb{i;xM55-1Maos{@BhdG=Q`tFM=(`>Ywo(@uiYZApV0z)s>?)4wdpyhcaNy! zdc79nq@!+WX^rxB_xV5B{m;F-n(RQ?{io8T_EM~-HLoeW`KR4gn$MO6pS<%?kjGz} z3EXI4UnSg?`7G!0)U-!mQah74P#6$X;BQY(Ty|KN+~>V!?}8Us?LC|4wU{mr`n0cd ZaRV~*^*b6XG&h|81q~5{pHQY2{{vKka*zN3 diff --git a/toxygen/smileys/default/D83DDC15.png b/toxygen/smileys/default/D83DDC15.png index d9fc6220d38787d426454ab769cd587b4a23bae1..ee2f83ff7f6d8ad69a70cd91af28303fc6328a6f 100644 GIT binary patch delta 1602 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 diff --git a/toxygen/smileys/default/D83DDC17.png b/toxygen/smileys/default/D83DDC17.png index 0043f3cd1b5cb4a95c4b6d235a9e00c4511f9769..42f14de3d6e9511657e8d3c6de65e64816f540ea 100644 GIT binary patch literal 1743 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>w)EiX zCGqLrmOf@0)60@pw-!t;i0;e?&hfXi*OB)&Q7en`Slw2*wYPj-XVJpC^x9a@KnqPr z9r+L&?ajUA`=-@z>95$-TQZ zo0I&iqTKTX?UOytVx5e_?e(hSyyw-V&8|p#`TEtV!~0^Ljq`%+OTwJ9{cRH6Orsq1 zgRQm9)TF!8gEy>LRF@gOdE>h8KYmP`Jc)^cA=cR_(q7NsLc`ls6&Mi-t|r+rLHiCJ zH8av%vvKRDZM!ri1p=+LJdIUMl*E7`3=CLaHWn5pMs^lvSs_jbZCNK>d0QT%n*SKP@vSRiUJ^AXT9vw}64cqIYVj=cG9X0()MI z)-|mNwQ0}0a8}Un*UxhzdkuJ!`vX~oRt2SATeRrUzn`oBJh;3@bZ_?gX{O6A>}Xx? z$;;W_V`&!ax~*>a-CZ2to*TTYSao~V)oW{(UC1>Q&)%uRx9eHb?C69x$50uei|b~G z>`lL{8vP()4NJGD*8azG(ORjk90|!6PdZPJ{SYMYb1!W}`D4!Nxf5QU4O{Q(vF`be zpy|G$VINjnX$FY?OUe%lelAsA{^rz#?vgXVt-IDZ=Sp^QhXlRKJaO2Tk^9At8If~h zCL6VstY4ZK68pgK{Kpg1E=)WP+B8 zQ zBdsN_o21X3%M`S3O?~d1sWS@rtmZxoYPT1P3g|j)_qgckjs0_PC7u?2cln93UAJNO z=eo><2YDt7U+P3JJDIjQ@aAvDh;47|-=-$s@PQjZU;d6(&4^zajpF3X`C z0kXy=+^Y__@${Xz@AG@(y;J4f??UqXEak7aXC4mfjQ33xlWmp%}qi;#7NA{&@?%riAgZ9 znA=-BTbeD6LqK@@*3IPwB}HiojLe+k;pXN1{`&dyY+(WurhNMPnZJOahl?*Uftm5; zg$)}%bevf6Vnzg8S(#+y&K*BmSiT=wa^uOA5VlIzqg%f8oLTc`&YeAfnhq(kEqyfU zz@<%}x=yWnH7jcVE-SXDzuJy1dp7Obwr_k2+c$SNF5bO-`}+DrM?6a>TQ9fpoqO!S zN~79eJu^3_ou6r)|F5aFTgUvB{QuOw+8u0@%KOtk{}y`^V3So6N^$A98>a>QWZRN6Vp?JQWH}u3s0s3 QYZ(RxPgg&ebxsLQ0OSLWtpET3 literal 1606 zcmbVMeNfYO81C4~6vP2K6p;}Fy1GG|q#w4GPSPfoDg`XuQH2dco3;_!)HXoDos5YJ zP7#OGb?8k_H=Nu6x4YeD#ZB>Z)A@z$=;=@=R5q#ZP_#G2r3h?)c>b|mlHW(3=XpPV z@0;4p^d(^-vqLx>PMA6s)3RfD;F%oEzT<|T66_Go;5sIYHZo;|o#dbfx{w6a7Q#eo zNy1RR`V5)O;k<05vUQA3lP1&C79J76@SGMK%jR&B7dvf)zKCRiLefN8<=|cS4Pwtd96`1&nm) z?Uaq7Xe$s!`J`2HO(`Y7yt&Xuou_@y_2^$~gL41oP5Z5T$!Dz{U-FPnA zkzH;h`C8IJm)P~}c^Koz$!zXkY$yO^y^&?uDfUo^VvN?8SV$|Q#^fOTg=e4)G8~q| zI3mDd5su(63`GD31RW%lH;sfr05n>9dQ8<{_DiT)pPIFN&WNwH zqw&x=_rQj4HAQu#u`(sTTC7?>1;r|x7Eak53D$7y&G)q#!{OLn^t|7*Ope`HhE<-` zq$X}2==x&FH?qhyyht$lrW>7Cd|JH1!ByinNIDPNAX4o}?@ zbiQvX*)hKyd^it?dN>bU-Pyj$?T&PrbHm$uO_!1UwiVY?GM1RHe#Naab8B|jS`PI7 zd9-n>bjGj3(}%VG7(YT3-#)(Fw5;J(zv3mb^T&+?(P`(_u0J&xwBmXtn4d8`*x_z7 zy)rv5B+c0A9V#HGXvfebi5cy1PeXohd~;U9RrRnB)px{@m#Y(hz9$xg-4S@Z^!+QB zW>;6Z=y$LGcCSZ&X>YCTk5hg_&|57nP-3W9&sol$uG_cNxY!*Ye|3Js658nL_*{B8 zwsOUjrU$C_yu7NW+^fr%)fSQ-NlIGopYg+ z+wyfE7;j$)oBpY_U`zjwGX<-Id-@-r7^v#XJS?ADdtm#1(fl6Sd%hr_sr6{@tt!RH zi8^w3>AaYz9^KQ6YnwfL(!4W2jL%h`tGN@GB5s_N_xk6#Jl z`fE#T?LB<1DnuAyIw=Tj_xbS3EhDQ z=VtHZKH6L#)et#&pn-1gtg6pIO}U|Sgm1KLUx#DsR1HCW1-BHd9&|tPCt*==then#qxM?YO2V$0c*QyyZN2uxONYEvNBn7^P`ZG-bu4KYd(qX T)49Cg2mb78JRNIQ6nyXxRd- zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33MB!|Rh+v300Mn_R9JLUVRs;Ka&Km7Y-J#Hd2nSQcz)6Xuy7o5qYnY4Y4=R||Zg$t7O^ZSqV&7UeY@y5fdWnGl@p~Zx< zk`^^>x^pMm$K&xBM36TXmlWe7owIby!qBbSl2ls_2MgOOdlAJ9gUmQY#DjOryjkVd zs^Xw_2h)YJe}C(yyRq*jM}Rk?!~NiOZXD|2$=eKM4?iKt3Cg8PFFBDrH!MV4XlJGl ze=;u&R7SjzEi7Do4cP`M#Xix%Jl~qAhuRv7D^!f~WzaJ`f)~&!BqDkVjZ$oSc+~_W z^NMhW#Y4M6i}vu?fV^ykj&Fc9lrO=8cvaDt^H)gL|w;Qmfi!UxFKj&Le`${qmd#SDY}m9>*`HYr|b7edmV z^*uHiE z5|up9ACxPvGP%Ild!`T1H~)EC84-5K`h|#>d6iA9WadH(u!9z zLx%c>E>Oi$!l@H|hMm_=a894nPGC;k$ z3z8##af$~&bmN{bEg_>xjZorPW$zA!(E~EoGJo^tySRkZj*A44bRxFIO;X_D18XnI ze5&MC|3X2!aDWzpKq+E}g@Xr*1fU0r_o~K|saXaYa!c^L1qWy3FQ)xmZ|SQAcd55$ zFQ@TpBT-9b*Jft7L0^XBv-TaV>XD7m;`7cSo0MgltmG$DJvj6}AcS%G+R4C77 z%74F0lyLxn@z48mULJRfL)2c7NJHb5(csYH(9jZuQ)B;xOOZ=UAk`N1YYC@nv>_dC zD1;=XNKsG`J4oO6?tFc}U$3EGL(j83!;?Hd>y>k6kLTQW#cAw%Z#JEB%ZeMx!{=vi zp72?Dd?6iHL6BMMSCcV?wWn!pvLQE~)_-L7?8(%OCz%bzW+k57x8=O9T+m0NW5}3K zYT~I%sC8sgKU~8cuH!W_xiFZZMv<*U7?sI@66W4#fvozf>>oYiQV4z5L{Bd~_(j zoav^-y9XcReI|=xZEeFQ3*xNnBSOs4!H{aaeoLw4*K(wg@FFvUb> z)257xf5T6^PqP``a6)$g001R)MO;{PWMOn=I%9HWVRU5xGB7eSEigANF*H;$Fgi0e zIy5*dFfckWFm42|Qvd(}C3HntbYx+4WjbwdWNBu305UK#GA%GSEip7yF)%taH99mn bD=;uRFfbok7w?fFCkg-nNkvXXu0mjft%*dR literal 1571 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 BaFqZ6 diff --git a/toxygen/smileys/default/D83DDC1A.png b/toxygen/smileys/default/D83DDC1A.png index 635ccfac616774a193157cd14e4cc0b720daf0dc..ae8a07b9dbdb9e5f314f3ba4c975884d8334628c 100644 GIT binary patch delta 1823 zcmZvac{JNu8^?dzDAi6!XF5tpJ9NK*peU;p&^YR1W6>8s3qRtIsNB-&w1~;=lgt~d!FZ>``q(w`~iO?Spl&h z0DueXKiYr+;%#7I005|@LpyGgV6ODY^qvI(M5+J)@+AOlfk(&%00@Huz@jSv=w<=H z@t~Y$>l*;D&(7+utV18ef`RxodHt@VSr5;ou`nw zOa_=D*5>EH!2a?D)PjWgo~rWkb}YylG05)#a$#aTsH4poP^WP1pt5>!L)hkNd>6=! z9vrCP{$5XN!qB@rK?a}l-wO#3t@)6P&ihcFkw&PmWf2F!8X5xMz0T#%l1Ik6ItA>x zO+KG7I6!J{WRFpnC+QR2IME7Uyt}(67Vm6r&5zSY+px34L)4D8%|(uQV{LC!w70#z z#^Vw&4K!TG9BFWuzbxkS#DW#^#`@mQwn!jgQOI~y%?P$xK%;Ce%<*WqAh9PQ)v1|HI1hnmHAt&USg ztgqV~wwSlPKK+$XpLiY;7~<-Zg?wI-nuIGZ?5nF8Yi!_>2G_>NzOk6X30eob-p}!V zabi5~QvtQ1j@gbKK-aYuKJ8}N?vC&VnPBdQj-bQ<3nMAE)Df9;5bQ0VtDu{sBW)ev_uRsjf=o>|Y0}x^Gq`F_GsFonG$rE3Po&UGq+h0Lh=1uC|TkU&!fdaoT zraVn-1gfVScHg;4-bo8XeZnDZH%`G`C~Q!#BGjm#d@~EC;$@<4Br~f}U;mo#8DVmOC3f;kTDbf;40Zc2)keza zRymd_?CJX=;bq*(Z^s-VB{7k1PKKZG@(p?wIje;ILlN4n4=^WPSWxig;`ra@G-{E5 zB0TNTHjnS>D9mZuKQy}b?#c}WRuB7h=d z-uK{@aZP1EMJARh$ickQ9}klK)zQK|sZ}}#T}b^+=R8vk`*~W@!^uSF2c7f_z#1zK zxjTWr6BJyTU-z>z{FmLK&O335EasKdISBKyE)~c{uN~%GSqtX~}Xm)wo& z$y*9lIo@aAfz-fND!=tvusd2y;-)O97TJUr*y8X!bzX{uUyc zrY)3kNbFcc^hwJtR{132@uk)~H7#)6lLsYVQ%ukITgQz`1f$HFs1um19+la3bP8}= z%H5B5cK<0C=n=Dw7pdCYha};%02=pW!nJzaFtNocFS_@k6K^$y9(R+WHPV=KI@eDl zP+XJN5qrsSGJQsEUU0*7VXH`|1wphCc$B7d6Bv|@>0$FfEX^#;GvB!*T$3~U z`v?P;gnr`S?-$fkCCgG&FJDwwtuCo3t17K5&-8Y+^6~NaCHX!M@Z&EmFW&cXwOZtG zmp(#+$+H)2iY-3oAHHhn$*!0PWs|bo^E{>tddl>pC>d(b4beRVcIC7RXN*hk-znC2 zLc8!5*%<$+hG)Gb=Lh-{#sRsAsi+Me9l>PgcQr>I-Z~I#j92;W#WqQN;Na`10RSNl zmH}4p+wOt?a1L(d0QYc%d+I(4@dN{)3Dwk8hia>9T(#AJ>R!>()q<)*p}J6LiotZ* l|1<;ydOY@u_b7gle*k_0-ah~U literal 1720 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>#D)$5E`ua$FAYGO%#QAmD%4lD%(WaO9R7iZ)b zC^!e3DQJXe=B4D97i)r|2jW|o)S}F?)D*X({9FZa_*!LRvER_#+|td$#M#9J=mMa> z9bJtqTpV4^%^ZzQ&0WnMm7sc4$O$ujpkwqwi32IIz=VJ)5X6KheIN&(c~bL$Ii?7h zHBNc?ykTHqdg1Bf7*cU-N|1N9aH5Fq{hG->YBNtBzEv`7$+2tK8Y0#tu&!;2+PI_O z;kx>Rts1W`xr%FXJ-RkEkHt}Nb;pVf*BFki-CClnGq&C`@xA=rWTxMFMX!^?P>tzmr0c#HvM)jvwdu$j^K`;fVoP{Qfsghd-Qpl+<+azgWl=ZRt&C&95sb zTxoi^bKRO$BMvszGos5kWL8_Nm~tJHh&g`n%yz>S8#D4? zeVyLkm;1ke`MkREoxQC_#57k2!LB`mdW@9{9{X)M_-rlOlS57jwYi_4=Ndfg+T`q9 zTPOAB#q7KEuX4O=n|i|Y`ZjCb&0L*LhKx*}-)FMVKXLY_-3k#D7CN_Q_b>~diejBGnyd0pyYheCmf zw`$8%DaHrErMnv4MfS*5^fgz1ODYbQ`r2vLvP5C);lvwdIlT!63Z9#dKXXf`MmBjT zv_3n}$~ZBD=VS%b&1F0$tK`)9vwkzqH?cma^Zxbg=4*Ez`SsU}EB?3}e{5fKaFhGv zVj1y;9E=*4Q_m^5{ZU^5_MF^#TPB*C>f86%-Lkh1c8ay>+p%iVKD*9s zb7h|hC~xBMZM&+q*v35i_ATKz=hrF;9W9=7JGk}G#?Bf!v6&H*cb9r|@2aXgpvpP# z?SVB@4C80iI4-t76}CMvs#l=(Qu(_#8~bu^_p!5F%afQoBhYY$)1}Qf*UZnk;>HuZ i->=dJm>^KbLh*2~7YL7>%(2 diff --git a/toxygen/smileys/default/D83DDC1B.png b/toxygen/smileys/default/D83DDC1B.png index dccb76eae5d93b7861e6ca88e0822d21aa1efaac..412d5fec992587f342daf20f35c8cde0e8871eab 100644 GIT binary patch literal 1611 zcma)6dpy)x6hDSxyy@|Ys3sE?ADQt8wFSC^aWe5b$^1#y3dzt zDk)47N&R(9#ldMaFgyTw3Rnb)8T+9AvHUNI9DeSE*xG%&SssY;VdW}pQ<4yP!r&4; z=Lg)gKzNe|jp0x+4i872{f~=Z_lEc?NEbkC2^{4~Uu@cK!T_6iARYxnf4JBMBg;O? z*R~mWfyX7t>xJq`xc0!>*5mi z1nLLS@AEq*$XbIl#d6s^RM&^qDZQ0K_aV2K^U4+%-S>SPD?2Hi6~3;C;_F9E#5KvY zJ41$w=-KbIU57%o@6K7C{X|Epxd!$;?I*Nd$LuEv3i2+E+!SmWt?n6;SjzExusT{_ z&`;68Zv9vo-O1NEEZp2REFWAya{bP>k;<8)c_t^+C>n$@y9AQCtCm;=HlS0BQtfXW zmsRBa%p5-&WRmoT| z*@a0p{zKXh9b}$8QSzdX@2PJ860A2T;q)8S@>cqV-)@K>3j1I#$uYOM@$NZB;%(m( z!_uPpJkxDy=v(2oTjkJw6R6NaEY@6rzzH~3 zR~;Nv&}tCh8b3T!d{EuyYJik9mWv&n-eoEh@s8C6n{gM%Q?934ay&>KoL|yPaVjE_ zV{EUqT&v#LQg2T!Mbe>$>lBxJ0=HN%_~X6tN@UxWmAUBiO9_W}ag+NlKmFvlp!wy) z-hw>pu?1^>rQ5Bx)%x^5V7D;0*w>T`*|5!xqGl@pwAc|9!bz=ckd?{8Pwmc<=0jJ zR1?qPqToqA?mLb``wpZGO)iPf4Q6s##Gn`!B0wLnudj-C!83}@jc3NNz&bc8kcA~jFhf{W7Be_8x{HPU O@Bn0zo86B#4Bnr}vvg1Z literal 1556 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uta_b{EG$h-Of1aIoD6}cShyIwSQ=TF zy12L+0mIP{rq?sCxFj(zITdDaCdgihUSqs^t(=Qe6HD@oLh|!-U@0IVBfliSI3vG6 z!8zDWK_fgfFD1XcSQ8XI5Z}6_7G;*DrnnX5=PH21*D4c>{f?G~hGv%LmS(0FWHfYNX4z5;8>5~M2Wg{{qr^l=J}nGN^Vi+Wt$iz;#M!_Dp5FV!t_9w zkc0)=i?sE3z2HCcV$tck1xC9ybyv($Z9SY6b=rQK=d~EUyCUU9Z{NT9!k5N#D~#Xf z`P0WW&wrl#yzl(+h*K_44yXTLzvq2q5_h0)cuM4RR{xZ^H+@s;`4=Bw;c9$U;p!(_ zzKUxJMl z_l#}Ko7f~cZ5oc|xF{{WcOom1v+nt9XYD-}xBdsLU7+$suV>O!%>%~FJo{uD{X%$t z&oa@q)J;j+<-9AY{#xm^(`tfG8Ej=s*Hzlb$RE4#`b(qR9e3q*$;G|ykLzN}l_u>` zY5B0J;oJr}mZn3(SG=VIBUc?rGRj$aEbf_P+sUPgzP77WJQj6D&2&*?a-l~gt$9b?p}8~A+U;nDU02sANh=I498RMb}-)B RJP}k*d%F6$taD0e0s!s5Hx>W@ diff --git a/toxygen/smileys/default/D83DDC1C.png b/toxygen/smileys/default/D83DDC1C.png index 73d740e3024cd902a292a3fb56b4194cc8486154..fd285ed40db47b2bf3970fa09d7d00ce363307c9 100644 GIT binary patch literal 1738 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>)8Ej8COf>;f)9qs^#8-UJ$AfRL52XLFldvobbs-qP3Cr!1ow8XS_AlImb$dTv7WguecS_ym0oEfYOcT}{3E zDEmV#{$(N7Y6^;4N{V^DCi|Ow4>bAiYxK?XGT2({uYXY=dLv?dpmD@sf=A@cryQs8;>O`2!?C8yj zvXt$O((8%Ti?op4(U*H@azUJ(Qc0lkj;_qM1gC6I-AUO#m7x|DAr={K+Q1m`a=&N} zjNrhMAU{xAKmxy?9w_9@|HYY{Ub*|On(>RfCK^|lzM8P;a5q=V?9xlmRzFS@jW1oDbz@-wOaF}2 zQ^#E2-AnnJR*|86Am^b_`R?1#glxE2A{Lt+=8G&p=53?Z451J7GrCt?v1l%1pE~)x&Bpit-E4w#d=H8$oqM|F$3wZpTqi=p zLalcmn{uXL{mvxO$cN{?{Q3B7!XsVXG6nrh$$NDY|Mbf;i0!oaBrN!mZF_^jpYve{ zSe9^gCcKth*~j7d=;ZcRfekM?aw4{}f1AhgqL%oGe=L@|*{FF3kv) z@^Jdc3H*H0S8Il|L5ktK~{roDf?x zAuC1w?!&*)PW24jkzJd()P$!16X$~3ya(W8H z2RV5~O=)Ztn%$lcM+4805%$YTB zwpY z>N}cl@a%DT@XYSw;o?ct%{zF-9(?`y^)o-i<_g9-U(1bNCe9APpinJwjVMV;EJ?LW zE=mPb3`Pb8U}fi7AzZCsTnH3ncpo!$K%`@Zje@BQAJ z-5$1Tg|p*aM-GSM92AIZ+0n)ROr6ZW-+w%;X9qV1i)JFI1SVZ?Avj8$iYI^|qdt+) z5_;U0dX@0!a3&^@k;hSD z7CZsbqUteU>`DP9F^m}z3e(fm1?ge|Wl0o5a=F~j0mFROf=}B_jNZyO(H`RrC_x)6 zq?sWp6JTf5$5UyH0%VnrhF~;nG_Q$G^jMTU<`sFKQ%1&^An2#q-cN?mSE!?Z^A-i3|5O}5?>TkLM)41s`QhizG{h7E`?MQ z`8YPnL^FDmff)BovVO;~;sMloN^t^kH0C9D$3C9p3WzewpTWwnoD@&A)E zAuC2`FOL5d%lH;sf%fU^(q|X1n};y59b;izLm}yN<8UUl1fj}E>#r#Vv4Ih(^BQV< zPB!;4m2FKchPgtfEg>{W5Ss7WU{u&(LQ>8#@LDhFXT}p0u4HIr_TL9o$d{w#qIcsE*x;5x7e$BqFVR$ADL~-zgb@H zO4y>OZsq3JKWM(6bJW}ANcMK_l%b7HAuShLJ@FSj+cUSg%ERTZYt-(qPAz)x1IuoZ z$31@LO!oO(u5ErXWfgfTQI|e{=#AEK+xv5N7wki3>on(e-&K?>KX9K+TQI+4lc%Y8 zaMP@H%?t6S{Y=eDb48XkIk&!bS82zCN9Ap``~k&UrxufQ@ER;UYnCNAr@66EDnC+u z_*UMQuLSKr{*8?X<`g<6Her!vP7f1I(2f*S@~-lkGDE5->JYD0ONvVBkIt|8xwGL$ z&F{8E>yO=z?%ylB2m922f!7{>!KhaNJ^`iW;t-x|W~LO>Z{$1dA&wV?bvg~jd9?rw zTBqTCi;lT?Wj?-mtm@+8mm3BdWY?ss-;%EcKqfBTEu@qX^iXyH~e*;b&zN zE~h4Q!JMeyuEzTO-TBJ<#Jq-@yG0XlxRrS|y?06A-r-pD(<{?X?k`$Vy(I6>P3EX3 zytnw?+Ot=p=8&{++Tf7W54`O=aewFpHeG%DsWDxDFWTtgT#$cCx`7vPEz$dY^|fAq zS8jIC%#Sv_SXg4bIeZ(qU6Px-_40H>#I_dvNk3CN>?q<~|72eXWvlhD|1yHGRcK|v H$D98FJXVSL diff --git a/toxygen/smileys/default/D83DDC1D.png b/toxygen/smileys/default/D83DDC1D.png index 1b49267cc76bec96b5f8e26840924cd00f9f7391..c74c7a7fe0be61e2f2d014ef64e51cc529f2d673 100644 GIT binary patch literal 1899 zcmZ`)c|6N4MtG!*U#tuG4GFeKHq!Kz2|ek_uO;txu2V2XJaWTrX&Uc zkhCJ2ks&Spy@?1zy(U`99#SwL(+j2m)TW8=QsK~C*^5ZN002uJfY<~81du6q5r8lZ z0G~Yoz-I#>AC%ktGXVf0TRUrt`TqVsw1yRNf@mQ|AP~->L6|*AbOYs~pgasTH#c{5 zbab}X{u6D_tNsaYRUU+f>H-L9X=$06nfY1C#W{D&^Y3|jdftj<{5w6Cb1xq9f<~kJ zdORNIJ4N_AhWI+BCNLqWtEkRg+hsrj!sBOV6)k&si`qBF>Y>dR4SEBCcC@4`}+FU*Vhle ze(~{rAFrpiubn$P(a-B`;cz&-?uJQT*Kl9c^Cyp{M|u}N@a855SS(gyCcCpPr<@bt z)l@R{d%LraQ&;tgU#GNF)}^<=eM{(T8Ba0ql2yo)RD-A#nzCWPKL! zXMmBBk*O(RXCq)d70guwapVC=!o0x#JFqnXw6(QyIILhCx-qcb3ie-u)fONvE&a2F z!u~7pi3=n|VXyCl)l%@S7NiD)!Z;8V6x5mspjz6@x|0FXT$yB>@8$VlNr@*thGK99JwC&w;XS#lV*Abj0* zRFQqt_eC&0m_yEbsbIVgnT?H52-W<~|kl_LX#0KnbNbTH*kU__=uZyh&9z}A9~z8(*I z{Q7vCR4%S&wy|y1T1|MSLzpVq-a|&;b|19h_N^>*s~@^{F>Ky5V0hAdqC}4|yBaCs z-QzQuPI*}2b}Re`t*3zo_YK^SA}jwca&hPsOH#pgY5&VWhs(O(&6zIP0Cg#()`=?| z(xRrc^xg4f9Bu4yQk2D7xbT>&&0_EkQsB7%$<&XDWkfB8$|TA*!zxy*N1kg%x$^a- z%gOm6J*ldlHq@5B3oj@xCA5jKYn_II2WVARxRJenqL+-guF5*6_pj`(Eb?p!d-?aW5l{&U9@0=)O{|ZV7W`3cBBFU%tiwRVUQgjhH;Hj5?&-9$LD6`M@ra7Z z`&2T!D>rtc#X3PgJ@D9)eiudMz`*Up2X`v3XndNvRQy&aVc^l$k49-WOf@72#dp;` zVQ{kEQy_JjFS8RtHqxG&e&Kp<+gZQDVEG~9prfSx40hb0ucYou_-2eq9+oj!gcUcY z(lf4C*1mNkNE(VN`9%)2_K`*3+=P3=lSYVF(;0EjN*xh5S#p-_PRHWTGGnDjLh21U z4`I(l(|xUos1e0%nX^jdqEqPP$`d{7N48UBvpC<8 zuH>iIbo$J(3J-+r)vzZnzQLkV=X7LD7R>_QaA@tF;+ya8%@6h7-n%J;4~?zbf}IUb zN{UDJ3wa>?ey=D1!k@+v@DPnIV-2Lw+>@-mA;w?Bi7kIkgl4TrO3F0n>pRs&YaQpM z*~SIPR&b=qcR$nct!&bwyoTUb6W_3`04h_Mm6<6;VbIIt4tg1&ksMfBTU!8uRQbxFu# z@(A?L^-Fr*{W-=ye8M3MIfQ-Pwz!P!cmA|vjxbySt?g8Oxh{N4AbG?vu#t-p z8yy`F9Mh$LY5dbDoIf78+}^&pIQNz7v;1NY*?5#0ChT{1u77cQX+C=N)yUiY!@lBP zCd#TVc9?5KO{C971tNgGR8>$^EiQ`FMu3a~o68?4;WE%uJ;gM?$fQx3bi7vx9a2CS zqpOR?7^2VUQ_f)UdRV-^;b{y8kHKVH|Iz$Egg^$(&pYb>6P8du2#BEg-Ge>Dn;Ah3 kp#xJI!;`LT6-f1=lj&4iRB$gH`T+s3GPg0SIqz}fKYt88qW}N^ literal 1842 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 literal 1603 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYxAZ!`CVki~UB%j;;nqCWe-7md>t5 zKz}>CSXeqbS-85HTDTY*0NoDJn?g>Q=>r|34@w+Ji3KJEOo1RKJm~{D@XV8%2h1@= zz^tLnWhBbLz*Ow%;uunKt0nlpw@9GKzjt?bTff_t>$P&X(2eG;3q_Myj&i;97MQee zO4K(I*M)BH-ImyWV_vlVALHUUF2xV3cUn&g9rDpx-OjQyHuhB1_jYUR&D)F9%afuz z^lEB~&;2}S{{7C*!++}Ry|Q}*cK*-MYN}u3U2A*1W0%*hM?q|ToF93W9A_l-tj(A; zEqG1R)aAcsEG}OVx<^H;<%Io=zL@p=Y_n&5e^<=j)a$DiR=M@?saV73Z%?KOsT{c} z9BY`xw`GeQi@40R%!hw3S+6;sRH&J{A?Jxbn{n2elo{a=ZO|1ws$9UCt})`*1W~O)QJG>b$&L?1}u(NxcVH zPX+(gc$qhS*5aq_H!PQQ&zEEK)O`8K=hX4(PtV?4aCVNN^?P+WwYitV<&vH>a9_JR zS4OWlag8<0kF0LdN1c-ut?josAFg9u{l`GIIXF}*>-hH!12-wdTlfE6ckKPP^{_CTq(^~?iqzurH6(>5FZm)@$E6PhO<>0Hz_V{UMv(7bTAcgxnqoOJklb9&p# z_O}V2Wj66uPKs+SFY9diA<=WV!pD9D}J36^(AON}RR~*}F04p)7yA^4!3@2X7Xrp1yHr zPNalH>F1Q?7d|K-4A`*btk%9~&t@05$tc`4URutxW8Lenc8+}eU3dLo6(38H4cI#` zweYAy_A-Onn)@Ga&^ Ukkg-ua-d?})78&qol`;+0G4oLFaQ7m diff --git a/toxygen/smileys/default/D83DDC1F.png b/toxygen/smileys/default/D83DDC1F.png index 52f30a8112e09e8bd1da3e996959b7f1e42babf8..ca88daffb9f375adb853f2f8474105676d4a5fd4 100644 GIT binary patch literal 1672 zcmZ{l3pCVO7{EWu7`=?tdbEY4JW_vTN%quO<827zUHK0~nq)9CRLCni21`$@D5bno z5yljHj!jb22q}sr>yc63GxqQ7Zu>ZA@A=*P-TU3|JKuL-=bm&VTP|OsyaWK2TU(hs zVUn;=@r$rlQ&-j*6N>|g_C$ai=}YHODQv9jYvp7Q5Va8?HUVG;Q^gJdgd+fhJ^&`? z02COxoZY4X5p+a~lLNM(3Z8&OaFv*L|2R>YO6q90qnjc%At8d8$V!KHJz)(l%V% zH(T|7=6d(^rH;wiM?#1Emk?JE2MQ94IG==*<1^yl#-}Qub>HosF72AU)IRYGeisLVzK#VLjz;*-iJersf0z@GsB|}%2aGBhoFAhUH{{OAlo8)J zS9}!h!Wjl$de_|)N;1P-C8)NR=8!D4)(RU)Gp+4O(*4pamS{*cQ*!G7Bx9mn+?h^j zgeoI6*gxNrzwIh1$vi}d-QTel7E7MX$*IJCCfH_Eidbf;)zYEA32K} z)Di&zhBTxFWE_kTZj|D3$x^J^fex>=^D(kSIbVx+lOh3#oHI)Am_5b;3$&D6DY7#ll zTp5?*=Am6sgP>lG?*v(E`gZgn_9kk}(fW&b3rcps)1qIHn)m9^f^-I4P3NNx?*)b zyvPHm!BU@klNE0uG~!2>8_gVQ+%q^Fd3Kz&X9F{a?=6_{8f!l`yNYL_#+gM}(orH-=g#p>h$j4qJm&_U+uQ=N}@2v(}oAjcv&9$!J9al{~gb;u1e6^LSSW z&b8M}B|IqfBjGNedRh4;wYhXCw~IR$XGX)Vrc_;gpZVgZFy+_c!EDcP9Gf$f^yo_e znYP;#E9`ZHmgCmGX(=DcdwW+bBfsfJ#lR;6q(WIyS;-+!KRjDT2ag~X634HduT8wX zI{BeR`*pD8>xLO_-Z_n}NP|goXZoQ`nu%{H4HKY`=(&P`vv+({=b8Yhr%f4ptKOdIoO{Wfriq6=od_-sagl20W>EX>K7T( RM8m!Z0BaK2yxhzu?ss|k>)rqW literal 1571 zcmbVMdrT8|9IuF^q8OD4mauX=WD{q5kG@YjQ+n6Y#4^?rt(!8gy@Pi2di8p&1#}Z1 zOoa(JiSscr#K;;&=Qd(w7#z6iB0AkPio?yhOs1(1bO;VMc17y;hw+bH?(X-v&*$@f z{J!7Yn3wx%LVQZRNF+)y=ipXhoE~|^al$+8!6+#V5}vU0HnxZlkQ^;CP^^On%nVsf zTWOLiTk$QOB@#_6a_8H5yJZpPWEnXb!N`M*Phg8gSqp$WH}m8$rUmuj39toEk~3pwHlcZpfG|$uyCtoh!Rt&F;oMLU!Y)( zqlz#qZW^~Gtn{FZ=Y1Fi1p)ziKp|(jVhGXcbP){{l?e!$zs$>%L7CT|9#z0;zms$O zcsJ_>B8sGgE#>u~;OXNK7@x)RMA+*ePn3`{C`kGsL=Hm?6NzgK?dPrZzivDg?awds z(U6t)v!$FP$Pnh!IZ5dv|61OMK!nq!J}NW z*Uyt)Cmprx7VM6570={iMvf+Vmdj^ZPqYH^TrAJ}U91l<+SI_@C2lXp2K5Mm3KZ9&IH4gT9-s(fK#j0YiE4!SVS`4c1IM}4|H&C7I0HqB z<3Gg`-4ZG=GJR6|!s1Eu&|aZqIH5I?=j(?>q8OVQH{=JeocM5gj*XmsaIfO(42P-XDl3m#s|Cle4CmI71bwSOtwu-{(WTU`TNb84`*av zm-Vj;#=f50Fw=MI<&hcok7^y_S*>xf9w@3@mU2o?&3!LH5;{Unnyzj5t8Qzby*RGh zG+}9+a!!vUCR{LO`YXSdRG@%Uiv)tWYG-gA=uN3*KZ8{x)Z0rSl6a#)Eof83H!#llpNNHBw?4rgL}%}J+4YYOth>BLEN>7kcC5Y_ zir=wxd)3``?u*dv-hLx|uBB#l~A^T{FAWSzOvzDH3e=|3l4Q-3c<0-M)=Tv^2? zwtpZqrtRx^l$5k>6_&8}jOWBz<~Lu;dQh6!_5CGt>{=-F+3ZCzeHn2Zm*5pE>ZTR6 zw>&)9pe0g))Kck79sAqjTcydnk4d_QhZbE6^l5Uh*PmWKdZQKBAFjQZcqZ18)SfYE ebbuYnI4**x_7pVk{Nd}!Z`(}d;s>(}*Zc$5$42r1 diff --git a/toxygen/smileys/default/D83DDC20.png b/toxygen/smileys/default/D83DDC20.png index 2b1e6446fed8b86dc919271ead48509a27b14fd8..465e11ac5ddd46d8a7848140fff060e2105539b6 100644 GIT binary patch literal 1820 zcmZ{l3pmtSAIE>h(jc3*B+7_sEkh&BNF}UmE-{A4R&!NaGG%P^5JS?4%TkD?`b!3jFc@g~p&TIonIS23_ zSxT7zh{FNM$pDT80Q$_5$AP;6RE-0CN$x;q0{H}xqks|#6ov|gx{h+#h(wz3u9sjil+4jFgE$6PVEV2Rv5&Wcy$bJG5UjaD<0YeS>1eO|zU`zG;1Y+PX zAb$P=V(&jetge7~UIEVL!1onUR0GM`!0&*zLEv{2MBPyEBZ#L1hu(m21bU_{2VTQ3 z?U01kE7T@(mtFsq|oHw@5#OMzs7dRUaez7pvmVVUkz4nMj%v8$S zABUU=#YrDI8_De(oA$8f#D( z4Gex~7E+=~XGozeBIPuic`7C%GKzs_B`_K2_-H18LuOvEcditt&7WJJpqieSm=$%Y zmh^kUc!f&emQ1eqwurn(>egN;_6u zD~;BpJS}d#C(J1~5X?;jxE_Vu^7(1bnLLnCQ-RGjkkKeI4B%(cRD zZJH#TIoJMhTle8(Ou9!=b>u@~W95xadxWOw!QWGA08^LjM<@#nd_Zl28hg|c6F zj+l(35`C9$wFZ-y)*R2UI`DA+b={Z0^Dt*SbI_qEQsm!i^uL18V{*QuN4kN)jJ!^T?m z61Cf?FFP$38$Tz=G{4c?iORigW_7NMF#3_52xF$qcYEn69&whrrYBF@rIvm-9_YW{ zRb*=4bs?5C5*Kj(!9KsYe37-a$$XEys@G0UPQq2~_yV#KCQLVvFF9gnUTW_gZxYLQ z(8GY$ne(36HKvJoyD07jqO>X#6+4t4G0k+A&~=tuJ3ElNODr9+s(6>^)iUoBib3kwJhC4ECY7;-o;EVx>9vpOg! zJp905NAGX_FD2c))|)+@ed2+EE@{tR%5ZsekkUX~yTZTNi8jXViyiy5xSo7#?W&BQ zn#)8!`PSi&6Gi)iY-6=P^Y{1n;RJJfxQA;0AvxdGoyFGDoZ5_!VzDHn+EiO+TFPaP zG~zVwaxq&&b@%S4{041M>ver*_)w`p?vG))hqCAt7Q>Ntih%^!f!nde3P-TAu_M{w z9Bu6#?QJY^I7b|gfR}IiuY~9rdSpbx|4;DvmRN=)7%ZO<6cfQZO*zE?7kUhpf%cB3 a9Ayw06netRW(M+Q0eHLnyYXDfseb~5-WT-% literal 1657 zcmbVNX;2eq7>;NuN-csShg#P~tr$qMn}Z}0G|6sE&;$eoirQgGmSlls!|sv<>Qs@b zib_?$gBi70aS-hwbgTl7#~^K0tO#1`fmfv}Y8COqW3U?}wm%$ybZ2+JW1r`FkMDhF zi{lNmM~(;`!QpU5>a<7#JC1TcKHltm;^Q|MJA^Q3Dw9ZNGER)bIZ6|mfde`#X2uh6 z%#@#d0*~f!hGr5;sZ6SVuG~mkd6*l+b6M>yo5P8o>9S+SY@7iya5G_3fDaEI0Rh6K z08_+zf!?mhErhm!!V?P&NydU~qs#=(i~*uua#p~KGZ^5q=GbVtO92k(%GtSln-2m5 z5GGpzzI7^99}lQW3J1hIVWd$2L4ZWUgG6G91d0G)0R-~}>@A6eL~^l24o3ll7sy(p zOqucoL^EiMT`52d!`S6~zSHUCIfXouGV>vsOy<^r;Ybz{N$1-b%oS;)!#oNIP8%u0 z&Jd&xa4TXNWFDgcSx?`FV72S@?}TmiV4~QR@m-jm5Ag(itJNLX0GehJ@PFNSFPcuu zx8wW-oF?-qBYPg1VIDG@yAK<316gn6aTLKG3YLSA#yl%-V|0iDWWVrCgh`GKByV7SD@0tgOs?j0JB-f>?jz!u;EcoM|H(xnU zcUGLPSYSxpRQsY3-yY~FlZNZ+XBAZrD{GnYO=;7dUA0Z!;2m(;;bQIF@M-FG0li_C zAl;>iW3RQiA-A#d?*{rjeXj7+rn^Vi(pTwi&t+||dIX|{EpHZ%ue?5SO;yE{bzcNb zp43~i54qV_vckG)R&6P_?h?8=VyT7WeO2Fa(7PAQ7Whpi7gd)=)!iC-mX_m>4q%vT`hp;sqXe0O!Ft_?F~`Jb6LWPWve>+uFnJ#LDsxL)R;%!SHcZoC=2 zui39^xT90>xG&(lmFIT`0zd9Pzv}T=-_L^X7ZJB^ANy@|L@&4?&fZPo+8o>B!~M^m z6$NzN%UZujh)Ek(&DO+Lj>gir9zUA zHgi{MdG_s-e75KJO!5=a{4gfEJofWE++lE{6 z&du9p65Kz`@ZLWzwE;J5XG1WAO#7)LI?n;s{m*wa5`}g07zkc zj{3Up?Cd}qoH6WM$JbPeh)~Xm{1p-$x70WjMLm%&-|W~ z5mdq?k{k~}WNCJ6Lj|k!!pYNN?wN7;d`5VAULq@r2+4}hMRE&MZnBf1JH$XqgRC{^ z?)ykcMK<1mWUC$JtefSfSL~}_=xbO_KFlxk?5{jZMeoh=)4RhmFFIxn=m2m|n4H7v z^>MH-T8K?-EIU4+nMR_@Mm7q^0u`! zbC};+o*mCgM>et@AGFKoz&kpr8N#;abAJ^8G7rLw1;y1#ly|p!YiL+DB-p`D# zF1``8Rjn*ejS4y%%IM67qV!io_XS;*yyjA9fS+kcv!NlHQ1RZ-PGICG61uWeH)d|? zPQ*4*!jPaD_uvdkxAKVSc8grO2#&tJzRXab2{JH+TvDN)?k?cM?tLB*kfmb1TxDlv z73B=%4>3}z0FXX=+Si|oC#Av@k|+_;v1B-vo}NI4r^O`zV3&J^h%26e!?x$tevvL_ z$vjF7{V@9P=J?yx?=IJkuCxZ)AGUFhauYI!B<0;grQnemVb?}Y0WER;dI^$m7VL2U zQ~mY+*OazE&CI_#p5vW!;;zNz#L7)AAOdo&8yceOS~Zg$RD9HPT$)Qqc+L~~E?j*V20krjPV&rjb(C@nqbbPw!qmdjP9RN@uXnU9iRm#~=Y zXc9k0V`EHSW6Y*`Gxr0Sbb48YL^slopwu}Qd(i1CES9P9a=&s_Oz(c!t)Fg^rd0}X&&?Y{->6>3!-N)zD?emo`NmC2tIC1uYJ?%7 zrNxyZ6-6A5P9%&D>Q5a2F@fp}7Ju{%Ky4XNUHz%yBq|vdmPCdEus~W^Adt2Qb1PqS zB+AknWo>1OM52&Lvf{&z{~6*a;n5N4-!`ZbXkUL;?XMLGln82Sf!SU;0I;s!F7?hK G%>MvoTzmWg literal 1487 zcmbVMZA=qq96y0|lT{@%5o)$^95S67z1|%yUZp^<*J?8hjF*5i8Q1oJ3$%CM9kyVc zY;2&$539^g6NkD*laa-kY?&lL^na)FhgZefwFxde%R&ip6CDE z|M&a9-=3_D4ND?dMnVv@#JZ8r25VICc|if*u@ln_SX8pPK+fSyWDg^^^bX#oW`a6uuS)=E2uexw3XG$im0>$u%DD~5{gze) z=9~s3Uu)CY1QT1vZLAjAoa&5RM|HV_bRub~aEezC2wbepz+P8{The0)~bm_hX%u7olcD~g6O&Hf;II01+E)h%8Yjt`&8J@ck zU`=$E=(B0doGl<3kTO{o^eF1_c+?(3&5NZdMv`QZ1IH7AMxs>ZmKkrNTZ#`c(5&PT zIYH)lHymVS?0ltcK!B&SA-DvaZJyXI%_Rz?4D~Vsim5fI%N2}kMq84z*?-+wP+Q8a z5?C~wmH0~00qRi_9|D8iJ=;)F5qP7|6gf~7rh?`jl`hsTTWJFVzNnp?Q;%bqj$TLU zEJo5~1_EW$=q$KtofbD~a7(f_#IX>TNFu0YbCQY1DH6lXIAy^|qgg{y780Y@CF3Ei z)h)@4+rftHa=`8!mU<>uZxUHX=EYo|uLyNORv9nzQW-D6rW_r-CZBUVd5`pZuspNT z(yYjBXPp+2cfm9H)pHBjCvgHd(pr;=pn>NYW+W{-lEO#}ttAKzX-4L-&i|7$6gYzh zo8v#t5;_7M7~IacJ`m=IhjoLA5y5CQMIJu_L5rrXv@zG)YkxdFofe)@v*&$EGp0Dz z7GvDvPf1_<((bD@*CTePLE{l{WAh)qx0j_??SNYrUEEG%H9ns&E~}y4cW6(QYIStW zhx?<}2fq9)R`u|}x#!fWPp*uO+?b3wdh7YY{b5Uyrs{IdJJ7Ym7dp;sZ5#GBY_9qI zzyR8lKPpnn&QEdWim&%6%WpO2L`6IDvU--=n}vx>Yga|rZasJK&KG|b6&~CSmyT4{ zk=PbxcmI2=u=FQ;)W^e2vo!T$;QJk6%C(PMV=@|3E{zS|MeCC9r@jKI;Md}A!c8ZB zzG%NU*m*P|zU!3vcV(b{^=VaKASb5rOj^4zc}G&LC?XU`zFnihxNV9Pc@r_i%1x?_ z*y{shlYuzJ+uKs>cGma5TKrv7_P{4suSl8)nM}`khW*ilq1f6+Q%bGxt(CQVPm+N@ z1BI%HgjK4i;k8qvA2k#f`KyMW)>`ikb*H_om^|6{WAP8mbkRLHVd(7cY+|Eh+S@ui z&$eCe=o0Q;V-$JB8+E0VJl5wI{~ov$-JNOh@2qdByu6ejPIpEmjGXWnt5ca{U+ope z#mfFO#BaUp3IDF~!vi68tS

yZvPH xerrGaZtJ13yHlqZr+38)39Bv~J8}pLQ$oAyzuD@@%S;dcVy)&3`Xp8S!9OklCnf*@ diff --git a/toxygen/smileys/default/D83DDC22.png b/toxygen/smileys/default/D83DDC22.png index 2314d9f9928a76c0dcf5735e8f420d5c56e44ab1..9db8e981e9fadc9ed6024da07b8cb2a4fa18c1f0 100644 GIT binary patch delta 1588 zcmZ`%c~sI_6n-LZY1%YRp=qfjXr}d(GKp{jMZpyW6irPMFfx-|P?-scv?!OvG;qR3 zYub!Rv>cbly|S^f(@2}69W6(xY#ZCNoZp%MX3m-S$9KQ`-S58h?zwNsX3VC-UTO*e z@bI={zyX;~j3NS{KV&_}F@tmGG)im~0AdUPvLXOJ!jjAgKpq6ZlT-lkY5jZqOP zg#%Ffqcv2wnow9}9%o}t(pJ_|KxIIDH62t1zy$UzkYhoqU;fj*F@xJc(P~FIHsgN2 zgA}qY@U#$lAoJW|o}gk9vhh*HVK53#atfNW;MZ*2;YZ$0@)P*N96tC=oJgMuS79Dk zNzafr@MwyNe@g#_C^?0xq`_+2v@Se6{M2Mk*>AEC30~a1i*TBeeWOs&D`Z?slXZ%8 zkLz)b@#Kri>4PHC9Vw%OzkR4sIjjIx{y-Cm(b6d0d><_{{>l!2eL~#8POtM!pum7K zPoS3KtWJl4qXF(0Vi9#A?s|s(xhQaGy{kSFs5XF7PoVGs#crT{4Jcg=ln^-V1NNh_ zDpGI*gWi_H>g6R42$HU6@C-Te8YZEHXqoG7o$pa~bN}A1!ca+Ygq$qWXB78{YYe6E zb`%V!ad@Dx+}LP%;o;NMQ__j4nYFqXFIxzdspQ1>bG*Oi<~BXLYZ~(`?)HbDZ+Dv; zo_}s;>OKWMduQMAvBMKR^!Yima|v1XmvwXeO`dnk0&yb?D)nF9MC531sW*n6VP~&5<*l`_+f_ zyqt3_5or%TJ$hxlp}ug+=Wue<2I>gmYafgl~NsCH;fn5uv3!%gPe%Rx}M2_k9l)cJz#XD>CZ$GS-Y_yV_=tU8~|gAh+&` zsU*uh70Yr;&P*E9cJ_H4%9|<|Ny=ImHs_&q(5`5e>c9eJb1%o+wP%F(#BRt`C{-PRf||~|Xb{n>5JBQwy`1k@&nA{1_S_>-%h_{$gi8L>jZd zatnvsD1XsHJuz5aA*8`2hE7+0J0`!@%+czsI9dd=!de*O0H zU|ZLk#qC`J9e*~Czz>v|H8H%#<|?w8=|z3BTS#4?zv7@$ab)>>_=~ntLx@b&urw{z z*;1`eP}7j?G5>os>g|+#F>yV<$afEM>%$(Gdd|dG ziz`!2&JLV0s_(VFFY0#_*jLE3N!j7s6UCwYI8ix)5=8XzTiaFFc&7EIi;-4tbxA4o z1Xi-C`6bUFliTfL-@Gz&Xq$m6P30Q4Z|58C2snn!%#6ERkqeA@#^opn`=ja~YVz4^ zgqc>Gx#QW4lVhzWS(`it5v;*AlqaQ{Uz`Y!fzBnfbGe*c9zHFb2M2(Kuvl-%&)a7` y(+9$@^Tqo@t04#vK}zC8!@n9bv$z8O%EJHe7 literal 1614 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 literal 1680 zcmbVNc~sMO951Yj14Sm6Oob4pC`!|$ccdykkUkC(iX!r~p$#QKo0hua@M|5#p<-}U``zIRd(7rS(d z>-(-84rhucQWejR9*$?yMD`sp@CsuGKSrIzB#>#06{B!YxPeT?0gVYW;_*0U$X(Zo zhjBRXq!HRACMhNw)srS3=8)mpOcoZ+;e;)=SulMj&H$;nkub}_{^nK?APjPFr6`6U zV^QMiM5LX<6YQ~Cy**PeGk}X10bw?jB{1O(2G~qlW*W80!4Y1RojbN65Ev0*GUec? zQAsgzfRdzeK*SS->iIAXh{ZfuC=!d|1pvZ_5s1&;;!s$Kio_@)0md$n)kYc8(0EnE zm@alD2h$nGfQa zh^3O*j2vVw9d*HEiHUhlY^KKo#fA*BVHODH@gbAR;n#>X&BWvXn(;pu!jE=Gl(??aummZ zie-F@tw6{0b?LK<*Uf{Q*^Z&u);Q$SQN!Ulw`o-2T3hd~(pymp*z~Vkjj@M+*%Om+ zMm+eX3Dtb&t*zjKTmS@jHRYD#Tu2v?T+gBOF^8=nxh zaqDV5jm=I$4})8l-76wJLY_|;-Vmv);dEb!PuT#R7${7JkADv3%C}1=<#i;#6xZ3N z1m5SI^=kWSqqlu}(Kh$3?$3J`pPSr}vwKeO_7|C6-1babI%}=CquTw?D%$<_`6kce zmbu`I(Dk-*LKbInIC3nT=O#Lom2PC-w_01f{!`A^Q~4 znH{kYjN86B7|^<HOgPhm8L=Y{?boX5WxYWcd1z)1B3o zgx#K2R(^QJu~X3v*Q#r4BYvNH_YY~^%;JkrSD#-f&M^*Fwyw)~R8U+FD*~$X0n%FW kWKBK$74Z-Adlj|OiIY*NcFWj8H#z7%k0B)B3H z*@CDHtxI`{6hY+S2ox}YV1+0IN)1#ogjYkX0s(ISv@_1^?(CiK-ZSUkbLMyF%(-)g z{(jyDdgginU_hsNGBInsGI3hiN^md=z>M~B4_^;}AFixfWa(f&K8(in1xWrF;QR%E z1#IiQ93YMapa=z^<^rte->7Ho0MIn?-xuhmE+x!=Z};xDt*X#!_#Qk-f{u7-j|FV= zzrbQXfCYcxw_ErROkTIZxOs*l|CiaTY*SgL@qh@1g$4}=0YjRKt7d{EMYGHgemc9t zcRnkMe~Dtce}Sq0FW6<4>IkUsGcMs}K=hKNZZuMr>!N;rG`bT_v5-oDW@FHZ3wi;w zWw`l9K>gZigo-A=M2dq*MtNTkPHvt{t$>~b`o$i}_oEpOnhr%{G&F37`e30M1R=20 z2B;U1bORc7yCI6m&pCxAg3udZBy&PiL-Y*hTA=+X^d$i51~l{$(Vk4Sr=Zun(XTs^ z1W*^K>Hyua8%OSW0-4#*#{uYT`BT|lQ3Nkzc<3Whwys?Ojp5h4oK2r|gWuq9k)_U5dc{q5mc5Hrr z%w@Uu+hAFj=J|2!mE%7HD-YoaHWVyTc1SUUs&Cb*RZincZkilNK9ph(kdvM%QB zT2o0n5Jvp1*Ksg+LrICyX`f-H_@#ro@MUkp#%T%fO2e1!Dl2-wYqvD=k!eV8?fw+5 zu=J7E6`b(H7PGV_TO7n?iDQ zR4NY#2MrAjO4s1Xj*cYCn$@4WQMWfdsINPno=&DzrVCFeCw;1(n_HOI)!s`ZSJHHJ zE?!8RpPRdI+S~JUI;B$V`M9-R(#K;<73pPjVu=1o2LWb8&1fFwyhz(yB4vg1uK;f!84cnc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY(%jO_)WXcw z$k5Qy+1%CD)Xc=((#grv!o^q#syBt4Fw+M*MjwT$#g!BR>@5%H0U0kvBvsP`4f%6`|V_Pdr%cMF4 z4+kuF{%c zCZoQ(^|bgGm+4CwJRRp-L~+?4{x0|{hk4b`;>b@njpj>Rj~+oOJv9_s8D1 z*v_5EbJ4NWk*j1ne?2O5?%q#KE|WF4?$}(|yl}#T4GYd@{wjMIew63j>!1f=X=hId zSzTDq_>CoQ&A9`ixi;s@G+Y?lU1r-YKVxn0%Jn7L>z}Np>z7ya1q{*~EG-ul2o--{ zw&&LV{rx{?o>=onmQ6S(bT?nfzFDbzH}q(4T4G&peB<2o57~RWue=p9y8OHD?Och| z8)nAd)o(p+cl)aC2csKTs`=){?3D<;b4<+V+SZ@E9(Nh0&D&bTn7xDl$&M8Vudj7p zpgOtmP3qp)51u)0?u*qjd$HmjlkGHV+2==0>J17!Q|e{*<$R9Q4_eT6KIH7nX$MaC z%17AD3bQ%7=4Q^}53C|)r)7CqXZ-tT_EB22EiEvO*E2o-g?0znb4%UCj_+*_f@>^XJBTyFxl?ei`z#!LB+YJ LtDnm{r-UW|{ufrg diff --git a/toxygen/smileys/default/D83DDC25.png b/toxygen/smileys/default/D83DDC25.png index 6e05fed6633d98a2859ce5b7d66236a53d19a2e0..f72eb69c0688f8710a74391cb1315bff9f8691f0 100644 GIT binary patch literal 1622 zcmZ{keN@v`7{{L>YzuOMDe|U{ih-l)R1nXy0OM^T$cqA&$|l=9gJ44^g381-jdvBy zNKB_9>QV{`Autp|+~nOcBA74{1Lu%6P*7mEAJyrc{^*>~z0dR9bHC3$&wcK>Sz)1p z^DP}L0bo8e$S(r3)^me651ZX+Z$)Cpe3x&CFF^S*s~NTh7L((GB0>Q6Qvfog0I#v7 zj1hpnbO0R-fKddnSa_x;d_4dzD{SLte}s^_2;|9dmJ5a3VDb{>UyL&6ztNv|!gx|> z+n-D7JxciP2(hgI{-!z|Ffh6H*aB_7wIJ3J%?@r#2A3p6cQ-@`CL%JwrOhzM{Uf+V@VtbPkP&c4QYthq4G`a%~teL$F z6V-s)0QI}0hs#l~6H)_e0b?a-Pr)RX#HAznUea=ftW4c7Oe}HHSt?snFfO+av#JZy$#)7*DAU;VEkQ zLGoa=v;UWi7mciaR?-@Mubx_`i&E2nkr7F&nos8CXnt1eT7?|}8NW5S^p7SL2kXPh zcN{D2#floitu9qzmAxV~{EEz9H{j?oq1AHJo-2II-x_#`a|b60QG19Kra` zhfU3IH|^1bL2>uQIaZ@%gV_c>dTz1JM}C}m^X$gGbo0F7I+}Sy=@&hQc-NOjIpi2Z z#O$LzW=UfnHamQ7&X=mRky+rgl4s}Cer-u^PC_2z1Dg%e-RSf6)J_{G5wB>EGPOq< zmd*E0{mD{4;5+(*=*YCkEA1||;?sd*JpQ=@r7_`TVhOW0KI)~Ic*?VuuRfi%gI*C- zr;Zt>27K(w4s^h0M{lK`ucrH6(;DWp#7fM=KZ9)bZQmr>{>Hh!0wF zF80(+k=2UOqrXg7NyRwsz3c?H?F%kqk8_ae=eyZX6~_Wx)Y_DkwIuvH?a%|Y%1b+V z)XHGzVDDI5WNfgQYH6Xp-+F!Oj#B_xTXnO%va({BP+o6lM#>Lu%U>a3;hfnv(O&!B z+O%ny0w*$h6wy9dsm&U4{=Y&s1C)v5Sjqj;XCqT@{X}Bxgw*OE`rm zd%7Aun8_48k9Tz<*RQtan*NMtJ)g(J+uD9JBWbuWxbt4YgVkGYZDqkjL(T5g@8t4? z_(cnyNn7NRQQ=#*M1CJBF?i}69X##respludCJ6Bf9+#t=%ZS^}#SE;buU<`~d(l>TY+glYtnp-cu5qW+8Faer>a^E?1c?F;pO^Ok zg3gJLb1;GZ+zF8ao@gI?Hy3<4f}LD4Gm*WE8^L9B(vp>2?6U`8`iJ_JZD2|M1dE&M ANB{r; literal 1629 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY1J+j=IrcbY6jEmnO9trn3tRivo{lHFI2BFUcFY%MX8A;`9&f5`8lu@5Rj2yl3$#W zU!dR|Y^IT~doO%TiO^it=+6z~O6^iN$_bb60aGLnjwYb4zDe zBcQ*{%v>xCjLd=lFtBtqHB^G?O(7@D^ns4i2PF=q!~zonra%xAp7ennc;-pX1Ll|_ zVAiO!i8W_nVCwaBaSW-rl@o036PzevTOWO@)o#T!p@|<>tjt(3QNq`xP(kI$!6OG9 z+1cec?=Ex^W z)89XF{+i@r>+ty?g2UHi4bmU|c0X^X2ZS{f5LOX2GF_X>C2{k<3S|Bhxn zmik3{%lprqkJN3Zr>XsWdfUrb%5B~9O@+;m1OmeNEJQm31g7$~^>5t0^p7lO$=w$l zuIo6zdBw`bwqau9hwlgFI%aPSURvY+WaACCkQZ@$iLymnEI(f~ww}xoo)9qeKxfdu zKCVw~c0Z~w$s7~eVl&~~$LQe1B+I)M`xi6ab>RMMd3jGN$8V;q5gmGmPCN^|vDed! zk8!o3bXNA?<-$Mb7wNVe9@6&_mVf8S)<3WH{%-b}6T?5qestdNCA z%Cj5S42tYSj-+QB+HBu&?nLPES#8Vz{8+Pmv$B~uOHbh>OOKrg+3r5$xLGqPq9?9( zZ_{kARF%@>k}{lc34JO7~X?#WkY+vjS%-(r#VOyk9q4;y!670%Cj`ETB*faMEB{&tB!U!BD; zeLm|H@$lJC)xs^BT)To_sFyrFk-OPFY{OQ6Cbql|kFK}pjvI+*qFIy0UBzR|XSi=aV|SP9*3{oc2eRJJ%DB8qV#T+K%cH)$`(Rx8dH;Vm uH6upj8*V|>g4N#N`>wg>+}8asn7|-(UyikK>)BJF0^ifs&t;ucLK6U@32Bi4 diff --git a/toxygen/smileys/default/D83DDC26.png b/toxygen/smileys/default/D83DDC26.png index e53f6433551abfc007b27a1592b9020512910cfe..033be848bc70933edb735b7131ba50df836b7667 100644 GIT binary patch delta 1684 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 diff --git a/toxygen/smileys/default/D83DDC27.png b/toxygen/smileys/default/D83DDC27.png index 779766ce8f63158bbe73401e0946b13aadd0bd4e..f48cc2f1d9c4aa87e94cadbd03d444b2a6631ea0 100644 GIT binary patch literal 1716 zcmZ{lc~H}58pq!NCLx4CfU1B<mh1ECj>;`cG$%*>^tgbG$Rpdwl1ac~3@! z2OFcTPyhg9P6#s+@}_@Gq&{TvZYEKXGe}~DF#xz;WwasK0_C`bkjO9q@|^%EIsw2s zv{dvKfNUZFDn0--835a|&fY%Y2LNn3Vjq{axw)x&1w6fd{|h3S@+YoD(w`7qJs?gN z+d|e6@62ZJ#@Rbn6kx1vah)|_o0YY>#a4)mB9IaUFs4{282a9;ubyCNWCX>9(FR-K za0n=FNdtIGeFFmn{eK%UJ-z=0x*L-d8pAsj`vpJj0GAfPI1n3`k()0qD9X$`mXMmk zjr$@Xh{f4|AS>_qiBr`TwepgRD(SJW1j*tEZcInpRxcl#Fg4@Jz_ayrt$I;&@4=(v zCEq3=`Eo`9#vTJYGr09yJ0wNl(}7e;_CbDp|6TZGFJOm;PfbrZoNbmhG*x{kFFf|? zPzPY`-M6mQx_bIKyHWcGpL64QbaqH_j?MK-K=r4ut*r^-Q+bDz)oS(Lh)CL=07pDw zx3kNuu`#Vy>+VUm+_BTy&f-E-^T&@Lbvm6^yD>XI@8Lz+<=|w3HJj=P+8j?=?vL~F zr7migmX=pEOUtT-_t!eQaP|(LY~OyP2zlol?ClcF=`*#;@n00LCY~#lj|QIe4)Y<| z;c$dVoO$8$)hnI1PuJBW3=JW98+FZas1JOkk>OCwIMmO~UT}(8`M_sx==K{RL~Z%_|RY`DBEHa0woA3 zCya%dLts#j$gYzPwgBiA1$DRP$&(yxGjKhaw5!F;u<1_T13_WV!fPHcP5m3!^tMLV zwZw0>rz%fn&DX5`X!v#cy8xA>PI<4k;YVqyR=Re1u;oQpHpY0zIE7a>(v;rGiB$+! z#|;J>URsFtYwN;A4E$1UN?tROMn~KpJ&*PCt3yjU^ZzGfqwZfksAch%FBz#R`Apus< z#(28so}4+5;eF0HuhWJ$*xsb)C+KQQ-I;AK{`rUd*tMSY0CnZv(3|GJ>`bCo%`*Z= zb`Ok-&zF0=8*8WMpcK6(t?XQq&G}8Ub^RKn@?-wLn`T43Il*BvxZ7VQ@PER{WS{ka zHmaODjtMskbxE^!&tS8O7rq@Cel~gk#S?jztt$@q_Kj-p{nWeJ1$zgynd#!nk_i%x z!dyogn{P2JD-CAqF}Tuw;ol=GG9(qn&vO9FIxs>)KF{HHk*mjFXx^$-lOlgo%>Wj$Ueb5ii|S?ooTcL z>fjC`^iW4B>!4I9kcwyt5)tG8iAW;35q;b|y||u48ks_)kX(pF8j%Q}RG0imL3*Yz zCGqJ0FUTzk34jVL|2QEkGf|o=kca?7m>Dm^anc1zqDYZIc=X5<5%iS+z+r_muLbgp F{{?nA#by8i literal 1674 zcmbVNX;2eq7!IgNRW1)aO6?|88>~5YLpDbVN3scoF#$qUFfy_v3#1|0WV4unVnJJ4 z3bXAC5n|v%BB-e)~Spd(Rfc zsw2H7%$h)>(Y%yVh=y8yU5}>+_5QeT2&I;pq&$I)$5TlMN?^1I1D=8bN;8^AX1h%HKzFw#lVD&}w;4hP%8W8*{`2Na1!E)ED{Q5r0JmW4!}EQ>vGlmWr)dctTW zjkpDHF`_AWCMjW1p1ujeY*ne=5?kyeiK0@*aiUfZ$mVj)W>;Lp+ICWd{p-d%we8w0 zE5^}ac07~NQ}sv<90gOkd%q!h^K{F6spJ~P{q!N)Zs4r}T(IAFE5JrSj zSP>zT$tgi9*bw4d#Im7Z&mm2$6xH91_By0#Wef5LbvGh=4bW zRa)#MYSCk(c8!$X5iImxtXM{1D2WqV9M2f-fY@}L#O>+06_CZl0CTa?V!$2tAXjbS%4 zW%)ITDC>ybslJfXNs6a$kk4v<7OAWNDt1V}UH!Cb=j;^|mw0a4Rf7PAbz|vFWx>~| z)_-0)UL-61q&LpHq}ONV3l4q8q-vO7?9(3dI}>fn*|;Hpw#nwFi%l3? zdET>FQRrNn`yE!YU@pBd?7ZzYtIwuhQNO40`W8BHTBBS3yn1Ef9G_FV@>_ea(d+7N zwl^gfNjB#yYAX!CH0Wnmy*yWZ>`!Z4!=mugzbdoWxOw#t#wFd#jjlqTE5_X_jF0t+<)*m=7-uX)0hpN7w(ia%=4sg zKQoop>brJMTU69!-I;sOu3yX=zg3r1`%+?=-XGr5vfu6GAAM`ro=D9GqUl?ER9}^^ z5AA4}qG2?aADXs=?&*#^IDJzuv3L;rTK6$MKL+l;d-J!Pxn&L;nA3G*>)9gzhorbg z=>5z$x@g1v_MlaGvo^73rS(BVqT7cr^aL@lYu}}?Qbt#(DDQ4K?N-!`fN6dyA^fB* z;X{eeqk-c+UtUrVT=}&1fIoJ9;hni9%t&FVVn;yCuO-Yx@BG%@)+s5hgMMY53$$Nk zmTJzz3vcJ27%aNcm^Gh$Ca|z>ZzU%u_I`U&R_!?S`$%R$UYKu7_%crD(QWeJ0FVB* zz=D*aBX~rO_TYKzAUQbh(c`Pf&OP)Rxc#cA_M?q8eVU)^=;dKSr}rIR($meIApfAM n@ag)}?E_4opkmXNSCf-z&5*XztzcrV>+hhHtC2m@ diff --git a/toxygen/smileys/default/D83DDC28.png b/toxygen/smileys/default/D83DDC28.png index cb6821ce0f6b9b44294bad3efa5e4061df111460..4a113e2d1d17553e61363ecc4e53de804f27d20a 100644 GIT binary patch literal 1584 zcma)6c{J2(82;IgC4|UwJ2hw`x6xFT_1b1+31f_Vqanl0h+`HrW0^@|T5Ju2NXb%4 zwlHSW5M>=pG?vSRWJ*#>H)%Pf=Ki{Wb^o~M+;iUdd!F}wzvp|;dB5*`87SYw8fr#r z003xsdLYmsYW;8(1#mToZ4LlIG1A4y1ps(Cn>KJtU<`@yK>GjyYZm~doCSb&FqQHO z0OH^PuoMOWc0~Z7Prcge?*IUDL8v1bBom8WnjC%hs2?OhTPl$VT5?1Lm;?zcB;@Dn z|KyzyAD#}NN0_i~St3SnABoc>CZ@5&@-&i-%*7CHsb6z|+E9&g$)nzm2 znK3a}*eSxYl99W7$^F*H5AIEOw)2XM=7vOb!=kC~`!k|W>2P1;l}r8n`qY!Grvp9x zcbi}^n22}V&)eJA+e`XTg#9CsMx#}7IgeTdkGt!kDUM%SC60DjzcjV7x?F6^Vg?k@H&<<*990dbqhNllw z`K7X^8dSmL*tbgnAfL>_9A%(!2@oocLX3(bKp0Fa0g_0f0)P#tAn;h>3|!?~q+SSH zUFF7tP3|WNeSK=-3p@Wp--=-TRZkNT%Pc-E+dw)!mk4s75Lm0>SsAl+t+aj3(d5~K zhC?%Lv*dikk7T2ZOXxU{(Pv1dreV9N)rnYMWBfN-036_^xM~!5CPo#US z6mGTW>c8fN7Q#y-lv9wF*&7H%TPF;nDC6tF<*Hpx9<$tQOkKH&A4tZEF?RC5{Jxy( z_|inaPNDiV-&XE!j+M5iO0$M=JS;#}Su6A{%BD2P-dtZ|6c?|i5)vf~W@4^)+x8Ek z7W2M0s$SC#NbXq}ac~fo>4*pL7Arc* zrz#Nj9`wsGmStkX0c^0b8TE6hdEsDzR|{rOl6h)M&EV=CR)v4VN>bpta&q%@3AsVC zESUDOX20Y+8Dxv{UsFe9W00%KV&PhN3wEewO03kiFcIB8^)7y9m=$+QK^R}#KC>gc zuRFB5z0X$N`Br35yC(2m>U4fhi6OBpuCF||1P%epKpth_aSVc81dRX!uphpEKMZ~d zw$BQ)4{mqB+Rn=MS2)}b4)3SmSp1iOM8QW9ng1TqFTaz~Py(0|f9J2LMl`FQVQxjQux<$;~DJ literal 1657 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYUsqak2Sa?CG|)P zhf7>^Y3h^`HSOXy2^5aqFtPO*@1r<#y(=>!O4EawI=OT|X^Tx_dRv{V`SQDExyF(} z@q15F-rXsF_vGB%oo%=0&i!%NhbLWPmqgU5jS7dk+ZL{_HRqa|ba?N(cXiwDrE*N^ zI;y@iXPf_~)4G1!rOi$rb6x#1bj3%DOD97v=ydV3F8(OQy**G)c7gf(Jf~Tm%hZ=A zlv@Xe$Md%z=G$50&{a5Vnv02$qtUvg%o@8y5k`5}$9 zU3k^3iLyr9B6K!%D6t%Dh|z1`n$o2_|Ln&ZM|FyLvyDQ0PY0~KaJ5)(!RuWo!=sy* z)t)WTkmCqdlW+~&m+};Bqg$fr2AR15Cn zPuu%DnDC)DNj9eS_5{)3PAii@ooBHeEeAGw)tUHwW$ z=(M)Xli(hUOBo-JFg^Vcxk6%DEDtl!+O7YrGSC0p`=Mg*oQTM%L*F;%N6hJ8cj=75 zmrV<~LgN~B?i9a$R9G~Rk7t{qA#X(X%-=4}hkqQ{@Ah3^oBQ?$`ESt;j12ASqD&i% RF8%;j2A-~dF6*2UngCcEa;*RW diff --git a/toxygen/smileys/default/D83DDC2A.png b/toxygen/smileys/default/D83DDC2A.png index a44d2e12a96a5170206d1b0b436a32d2e3d90c16..c3c5aef6f2dc1ba41d1e2388d1445255ffa38289 100644 GIT binary patch literal 1737 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 S8MO_&{~l7YTvRVe$^8dT6O%mv diff --git a/toxygen/smileys/default/D83DDC2B.png b/toxygen/smileys/default/D83DDC2B.png index f09e1a33a50b047310b44075f90696a613bc0fcf..0f40d31579bdb43ff144c9dd5bd186044f1778e1 100644 GIT binary patch delta 1519 zcmZ{i4KUPs7{`B>Wo?pFNLN-RO7zdmdAV&^?RqEHdduErUl78URqJj460P(?UY5A= z(mO((sI;P5wX6015_t_NQc=ql;r@3wH*<3{_snPJndh1L&hy%=bV(_FBPeE0qnGH%z)!S^KxI=fy@PR}&d+!9n>e^C4AJcoXQ=6Lq_p}MM@k5g?D zm^LSm9=epmX~}jDAX|k|9iy1!0C%evzH0?+$7fl4+L{M?5i4NZo3Zwp;rsm@Oxp_R z!c6bfAd)Z-4j3vA=LM2DbbAk?MJ&T%rjENTPMHzJOY;0bJm8eY+4o$bm&Mv6OH~o7 zEFw)AvB|D~o(n)Fqu*Zdxw_C)y}Vp=ZS;*v)AuEb+4A!IT*IRSF1cCq^HS-^aLwT$ zxk71yY~h4qCnA!eJJ}IYfve(}NT9%m9b8BX6AH>$9jqUZWDEd`8$}Xa1juXZL!2Eo+Cm~}1!U(>k;tBF{9g+B&C@0Sg(OgaZ*j$W297th^QSEIw z=X!YgvesHCr=u+MxGMmhl?NVrR-rO1{U@ z)2WdUl(?s#&!8-`J-ZFz;=R|PC!rM@q`)L&JS?!d%W+EEEXtH>@O00;t(9%qrf$bg zTpNK|h0$J_MpLaz(=jy#&QXxu`J90rDB4rP5l;2G*cOt?Eq(IRL?kL)D|o9Q;fUW_ z*HZ^{P@pcC#)=R4z@$<`yZVwq{eV3~$2<)lWM% ze|#~XS?IehG``*hx_vlTrP96T;*JlczI}_@K0N}Lsvc))(k0U8@@JObdo{yVw|78p zf+n(JW3pmDR=KiZ05>cnd-tLjCLe{KTm5rUNYQn(&9JQ8cI{IqWHh-Nj^x&cERlP< zs!lM08o&RemI&7Mw~*~z(UvtSeYn*^Q~ieR%u_Yr$qrR)UYEU&*z?RsH}t+DR$1(j zO{$Y<>ApQEUU$bdB12})9v?dgzK2fhygK&XI)(dFDs!BzOdoU3h%<;ux2>ESL9c%6 zE011uoq4FM5HucfeTf<&9ZY=K)nAqKg=4B(`^m(+x(?Rg@-U(h)u%@^)4HnJCuVV0 z_-$Io%BgkRa(@d!UbNYPNT6tRGW-Eb&FjbG$CbTYahVrSp3Udww2FF#!mgA<2|T}a zf=Dv(q@(H8vu8amLyy~?a|uL>MBLZd-P}Cb_|$`HqsmOm%DItJvW~jG&cLxCj zO-@a|-=vXv33TRfkLQj4o`y`O!zTH#4>D=&087TP0K@?!$jHb5GBem~LfH#h8k<_0 t8tX%lB?J|fESmkJ_)8EL!3++J{(nKcbpP3l+PYW(97wLj3LF2#KLDTVwYUHP literal 1613 zcmbVMeNfYO7_QI_?XH67L`B6cAj6G*B>nCvTABcz6iY#2Z0bTAXr)PN8f-yeh^NBD zo2X-+#X0phy+hD>3VtvIZs>-hB04ujIj889+W|V}2dGPty8Yq#$8t%2A92hTBfn9iW-ObV67xG);$Xf0GG4(JIi8#m#Y zC2z&gcr=IOpJhu;XVMLcYBNO$F)v2wCLAoA!-<~fc3|cloB=ZNY#XTo`%jz%0h>hw zrpXLqgF}Z~Z3+1_o|2!GYR=Cwt1RHW7$DlMW(5eG!2ma5C!K1y1{~E@vvcpZ2n0qU zOpXS8;Z(Z825C8uhr#etm~@UMicOiwjX6Y+P%I({Z(O5jCu73@b>o$2XKJ1U z7nyJ;l}nr1^T-PGk=fk6-jEl_dZS)Q+t@?F>u4NfC_0s*?7j*xS}BHdS}6yhOOXSSX*SYAxtw#o$MYgu z1gC8)aZ4Oc5x{7E)wWmIm%xY$MoJe?KyQ|!^EMfABna{iBx%Jn& z6Yre*W#*KW*=>jX^ldzF)AbUxW6i=w?$ETyp^d@l=3n=`6Pdg?aC&kO_H0)quQl;f z``tRvRnm2bBwc;3z15xxLu&&cJ}rDy5cBXvU_5`QYIRXa!LzVi9Xdxz(-A}bgoyF^ zx*NR*=G0tW%AM12OqKBW0`9WKRocqhp_T956))Nn8o#zBr^T>_Z;St+eBv_5lTp~a zk$+`UMqx*3MOQ+1AuRtWh1*bjv$^JF_cu_&a8;7k#bnQnp7Z^9V9zZc_2(%yq_NQ}2JbPrL8_>hHH#w3U{g zP0G}?zW?qQvZCYj8@4w3r_~sex7Ql!FK6`hOawzWEHBv6Syt&>BJTaYX~YPfqt}@N zA_`acRe_h!$LyXr!in?*4tp}{Ldt7y+#DdP2yS|F)#X)fevkIUxlM#>mj1qYK@_*B z8qseC%3yo>thWY7!j}x5HeU!xIM%Y`TX$#;|Lxs_;YSnW)*P>YGB>I<+_2~2);BJR zA9sxR^!Mm^eb&0zTPps&T6$95K(Te{ADT<&W|t-6ly4d9J-EU}C-< z1VlGl~L&NJb%86czj&?03~yH~Vv&iRIhA8Gh1qv>|19 bLGd`ww#J}~XFld0^#0)WXcAHz`)SEPu0&^j diff --git a/toxygen/smileys/default/D83DDC2C.png b/toxygen/smileys/default/D83DDC2C.png index 2c855eb24c45ef806f625fa741dda51b3e168d94..d107ff66b32f37054478c6b3917545b62c89d626 100644 GIT binary patch literal 1654 zcmZ|Q2{hDO7y$6kHZ(CUln70Vr^FjV5fx@EDa4Sdm%)t6HW=B9`D~doldXLJLh-rcfNbhckj99JLlf>)|RFcXjwD>NSK@9 zZIHP7%McSnW)-znwnz|mH#}qrP?orIktBlTSXVQfLjWPl01+_&^T<-fdw>8O0M8jf z?>c}qHM9D#K7ha->w`qX^71moCI5(?1V4zr4Btdt1VniJS0Qvj1V(&C3x}UkgW(&` z0QlZXKlmBh8-C{E0Y5lFf$tr21B4^#i&zFEXG8HrsPBTJDoD?V`VJU;uRiukb8;5O z`0#QVvI?P~0>)+n_zS?}!_Y_IPQoxUPe3LUx`$wJ!YMQRaQaQ?9DvR@@aG`(3_%|c z+BtAF7rMF7-Ur5}P{V>JENEauYcF&S!t-HZ-iO>$xP2E&A4ADQxKjlcPvPzpD6D{* zc4*~5MH7U^K*0mZErAE^#bFosN9%j4ZJ1Btb$DTR_)WKRJX3sXGAi^!WHi73td5J7ZQ z6oXb0Qgr&~8h~JE2+@INLkh%F{d_2=JjqyEFqMoA@}dIlzLaWb!RW_{z4lL#Geilq zefuBWx3Igadsd;cZ^$>4VP42#2u#gpoLycd?Bl+?m$XCkQE*vHqk{eMstr!s72X*| z?R`P|3ehs3y=7B)HUX)>7fP>d))~uVxj%cleealkPpNT^n7T&Wl}i_Pym6bMRG##R z=xtPb<5k8|3X&2lHSvm(*POg)tgkj_EyUU4{oXg)!mIj)kzA966@&N&A4E4GC<_$S zp6F6O%=ATh38W@>>X%MhZTZdC)3Q!Rv16os_JcwLx|wKi_pq`lrJY(*<+3jAZO6yO zsnIK3HMKKXE4HirRb18>1}+?vn>!yL6z#NMY{^~KSP6opp_595w@)$jF;$OWM!1f-9qIx(dnQh(CLW;e z7EN2jJI3CmvpL0umsr`aJL8jo#-X|)oEg^S{-W7&AOnS7FR_lR-KxXRJFcx7`b3zO z{#wg}g|~WjbnC$B%uiB*0vp!>t9IgGNS`Tj%w z_il}BQo2sml03jPH$zq4h}@Qxi&Z+nR;2PJs?K3=MvRCbT#gg(y8Tze@JTI8iFa{o zRQ>$yQ#{s)6CVLPvhJqTkj8ydTroJHCdu?I{798YLym`4cv7TUt)6>%>CE( z<#P?g7o?g^>Z<3kJ85xSf@xzTI#MKNh%31^nPF?w%_iU@ z!^$qU7mbf+_CH#Ts#o+yp;okT8SL`ugw_IHdYYf+%^$Xr=V7GFb9z5e* z7Ql>$%FErzToZZ#M08N~xrW$US znuZop1E;rBTTe?@6^GNq;hO#P-uXn`a@G8nq~ fxR9~tUL<$24VmN??At;{J_7)Af+fD#$T{j?A$7ZP literal 1570 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 Si)n@T=N^w5k=?ON3jPIpbYHFj diff --git a/toxygen/smileys/default/D83DDC2F.png b/toxygen/smileys/default/D83DDC2F.png index 35983295524d58a817aa4f90361f11f284e80539..086a5b6440a177f3ad17f9b9311d38689d55d8cc 100644 GIT binary patch literal 1753 zcmZ`)YgCiR7CoSVg#ZD$5&I$;cz@ir_MAQEthHz6tl6{X zybt}bp}wU)02orpLW?;nl3_9K3qu$G`*V# z_1RF93D?utU~O(1LEjMRy#by4h5U%MB8sc2;9zIFV~YtILjsx4`c>)ymFFI}AFh<4 z$j?0eKKR^__wiI_{HE^{WZJu@(%^BWo?2$AxxWqRfW;b6S3vn`Xf1_z%73+%fFunx zUE7emFjWa1Vt8H!q9o|7vOy*eUF5@P#oC#t<$w%)C>0r_t##gXl!}d}P^Uy6*6d5p8px504~kwwG>txfk-41 zNfe|PkbXe=;N2aVtA>h1Se647zYghwiX<5Lxp=xcYOEADQL4Mz1hN#bi89^k=4i%I z(vQ;b22~GK_uIPeHFvb!`O=@7& zRF)O?&_lWm9nj0&9A`C*n!t(Aprd{X=>45JrP0iw-0L4`KfiEB@YPDu55J79jJ{o7 zp549dDqmQv`(R(gl74hcqctkj3O#z52st{|WHiy^Q)nc;Nj)QdJCAS@5nWh2{R?Ig zpTSPHiA#v(MnrRL_^ELmo0OP10QdYNe_DST9y2&&wl7m0-nsc4t8peRM6+ytKFjBz zTK3w2AirU;8uY09H_unc{wO6}^3UG=F`?v1Yn6j}*!4+*dRuv_m9QfW|LOj+>~9;G zzcT95d^1kgmel<|<{s2$K2&AN#hJC(axLWX;=VG+VT_(sub37*HWZ)^4i#!^MfV<> z6LFrq_ZD=jG2ue@Z#q8Mc`HKprMtq=Tg)6L)}QqjQv~*N=5h)%@`mj(+lfKmKZ}!(RQO3lu4Z4#`fCZ zfc0Wb;Z3|}HK={?7euLTCbGB7j8T#w=3rfqza^Mx6E1v?e`boR^04q=QNmWxSQ~lCj^43oO^RqrW z`>w=YPa6(3zbtw={-IVDJwuvWfJsSd@i-!v*r(PsWeIjVAIKA=N$$4(Y@(-J-yD&D z+^sdnRl>6{Qal}O@byS;#y3(D2KA{ih3Lh^^@OolpP zEOdlGaCURKeY37kPH=In8Rd@D+^S8E<0qUtl@NdPcI^avBq=FzX>M^|qnUp_m$Wd> z8CfvifYCR4J+E(QppTh*$-`nX!ki1a7tiPC2``YbrYtO-dW24)i5?!j6UYErx6eqry$)XCsVvCBR zB2PxdM8(F=oS2HfbeZGxYI=6+*O{;JHD^>W{yV!Us@a9I#QGvzU4t}v* nelk0O176{=p&T1Z44cPcaMzX70-=)~%Stjs?FGVc;7 literal 1797 zcmbVNdsNeQ6z;(%AVZGMGu*`3B8ro?N#BvmDYQV90=89ffKb|$wveVYsjVooAxt~Y zoO2*BWatEk_}ZLL6af_x2jXOkipm6af{G$@AWT`R;P!{dKbDi^_sIRe@80{n_rx9< z9^&RQ-GxG-xJg6Ba&nyPcqWb~-!uD$Rpj7FNTLY^rX{Q@GeQw*utWrqqN*fBj;J)N zQX3F|3T2E|uZ$+5Wnr)yL+L7q4c&&CNHm4wA7C@7)X4||BqB+AqmcUecr_K!YlPG& zj*KZY1tB_pXqp*Oq=hThX~}AVh8nOC@VCJv0g4bRz=oz6ao8rLzT$<+xnr9_1zwpD z$wKODr=n$%KoDj|01lnyr)Gj6z~$0GHiydv=K~NEgcwZn=K6tbn8Sr39x!rINj0-Z z3(Lj9BU?Rx91gqGRSH1}G2+92^kjM_TydtBi!o=4ZrdqYPpMSDW=F zLXQ~%2cs$xvk*cm>FMhbP?JpdhS-RYB#KNK!=^GZKsu9wqK>#;S>uEp`PYrNTI0%9 zCWIkJaLi&>ljotOje^PCy<5m(NO}WDnDykLs8Yn3+JYiRLMj$g$uD$`UIR-&0Vsi3 z5|AT-Bp?X#gCQ25&te90#DN@$Eg9u_8!jF|N+5lyfeLxETMh4pXKhr|L%Ea6EU9zY-{f`XU=HpC<2XNq_n0d)kc`9C>h zkj^k1#qpnF8Qmf)&@p{e`sCtG^B_jDW6Wf0L^Vc`T{VUy6^oR%>zZ%S6oqB#*^DDq z577FfF|CJgI<+k-Yt4Ueop!uO-h9Kv+Wc*+pL;jth6estZlB4^_j@XuvZ0pYlWCth zsgU8U_9?e-z=H}fkId~e@Y9r-E_nWf%ESHg=BgONK1b01-Rhn`|6x4*Wr%P7FI$Cs z2&bwJu(#AUe6D2Df%9J1YKECHD}3GO(bntSB^3d2*uhB8J?h@zZyVY(I$Lv&DR-0{ z%2f^e92@HSwVQjZZIE&AlMZ+7PAJaMP`W=g?t}Q1xi`kfHlI4SIDzeP{Yu(;eDMAN z(ms&wly`5g8@$K4w({Bd44EeFa-zKenmzum!2-pbU*t&dqq*_5G(UfrNC+BM_=9-b1yIHD)vtG{KDsI zY(MUft9OY*6jiy0JbpOo*5no&*0?F`0wgRx6(1-1qepgBIQxnYkCxZou|%Kis~D$S<{Zrtrdf8ZhcW`($lOUxVZUD>d>Nuo0*r|b<>i*Nh35uP`+R`4U_+iR~fEHn9*zr3aYad+;)7!!Z z^KQ@ZyjAygXIrVXP`AV%2xcxc39eqCeMgx#?@ zt-7QOmoulYDZkj>`gqx4EFG9qzPI;r`t}cL+8>W9w*kB}2j`zmAKnxv{Hp42xS~6_ zPZ_yO@4eZ1q5q%C0(-y3Ut5olo7!P(4&zpR>Dncp{TR^YfqL=Q2?1_%TT(;o^OP37P)@ DX+X(~ diff --git a/toxygen/smileys/default/D83DDC30.png b/toxygen/smileys/default/D83DDC30.png index 3249366c3ad7592fca404fa2cd7daa4d6adf280f..e926a23df3760d58449155793e5a74ca377f5f88 100644 GIT binary patch literal 1634 zcmZ`(c~H_>9DkTs9+jw>RxXyAnz|%tMx|@XD5KyJmbFHaAlZ6lm_fRdN0YgTWQuvD zXlmt^Vt8M8;c9G$X;flL>FI z08AYN0Fw!TL3mDs_ig}Sp(ITw<6=M%W+eosEbNDMz6 zdoq+sJ#mH{kavl_jGT+f|3+5gMF_n33(H7j#X-0n6I;wpgFHFRm}?i4Awagv&dcUM z{Fft^QaPc1FJAHM&fuc@sv z7)W#MzK=036uqzPQUzky=f1eTGtEIx{8sQY}%DIbP!Y#F$-%GYd?w z2QDkL*A$F6#op&E`IehTO?nr87#rJB>t!PeHE#K2!)DCZJ}pPdqa*L?YHknlslj@m z4_!>J>0VoR0-ilnoMNbo4Np4M@XG&%v9It(O0o>zZ(vjZzH!#XqR@r4V`|Tr)DR8Q zR7cusltZIJJ7)O#ao=qHF?vOh16+P2Vf~UtwYrze{8+G#exIFW#zA31o5>t+)ve_V zwY#(}+ZE^!I>sHE6YENEO2mGV|RII9O2 zd)mVF$uJW|UNcM6O&{Lgva0w1Qkh-y%s`2}=a*onUDBSHn^q29Ahv$o4c>FmI`)XC z>qR%`Zc;$L0ZL0q8(h`BWuRI@NVu22SjnbsQH_fyc%C-dY<$M#%Gm9^CV!Q8v-xIL zM@Aj-jvu6Gfl%VQVnq~Q7oI`Q4tn4lNqpC&+=n4OEVhR?9}DDG3H&*}DVKQ4NklEZ z%IbCeI^?Kd`=~Tc^ToDf>pBYU9v{uWUC?q+KR2^uJ@wTgAB}T<@|V%(bNih|uuRp6 zwbv;)CY%-~o>p6c`K7eIjTM8tQ}(8=Z$|x8N50sw3D*34+#=sJz_)r=X1z&=G^(?h(()@sSynoy3Kgv3!=}snlLVmOy?Y1MKxMwk<`yHayuSRkFMl zxDPMMcfX;ual-~~>(zGw&G(};XNX}OlH`ljZKB~}&ZEGC(YY-#8k7>fBhjf=0+eX< zqcz{D1AQrVXxgmCPiclgz?Et_*!do1p65$yWW literal 1609 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 NknmwaodN9P{{dGQosIwi literal 1601 zcmbVMdr;GM9Ip=qu{fB_*Bpz1`C!mADNRY%qHWSbl_D!BC^sc-QW~L6O;h@C&c|*j zIz7-+aZH8HA=S+}JoQ93$GPcbCu4_B!8uV+r;KCV`D8j>is1H#=O4=@`Tf4Xd_SM> zJLTC~=~0mrBP9|^lre)ei)(b~i5MZiU%&l?5|;^rK3~Y;Y=W2K8HtwWEDT_DP(_TH zp=ke-(~Mdo8D?X1^M!m32LYDW zfO$%j+~gt{JDU;UnVdjYt~F3>#b|Iw8ld*!B7uVuD8T0^ak_Dz2JGj>#eL`)0)c)N zp;!Yxbt>PK4G3oL?)X6ll5tyk)+Ml< z69_R<7S1DRK+)5uAvjzn(=%eHdmvF_${-)*f?$~(ayUY9^=rEYGxM(-&((J4`dtiU zX55^Iw~F)=7G(diZ_ zr|faT*+VuWZOA`bK5x=K;)pnv^>^Hb6)rKPzK&fvCi@sFdfB3*?J38rQw|kd z_6N(^?-yhTqF<>F^wvZKTHjxFuTy_)Y1@>T&=!Oq=|y(?84{0pO-C0)tXvwOgoS~(6TVo>XcDLS2Bj$d++FRNYp0_rG z{;a~hN8NQlb?V{vr0XvFvF2jQn(kkMs+*oT@@jbNbctu}=~p4WQFNNO27ApK{vkfPxhpaI!N^`5nE&l1SIOec&NrK{-HO?;v9Za0pE~^U ziG?qbdE=y0;$!QYVjhq9F|*8YM!t+6SJB)wKUnu=RNuQ9wzxxO_Oo*e>mkXeA@%js zJiYQ){o@nD76ro2s^?whxPxzh0bUV=Bfo-M_MU?ivxfKdtT<1lcb`b=^b#Mvvm&8t za>wVcnBb1K+LPnyBWr}9l;5x3J#-efOH&njX!7RV(mGXKnq!Xfw_Q-SJcEmD(7 zcmD8ILBa1(YLCI7Nzf)xnbS~gYlcVjxs5EJly!+ znpkOxI%Dg|^R+)$S9Vnx7k|nrf(?~>`rfds7Bw8)e4B3Jj*W_%f2wk;o*es|b|X=L zCGKoTgt;$oc|uzjZ1lA}F5N!8zP2anic~wfVp8h^s^EaPe*H~o$Oy^xu7{UBgqtidynYM5C?eFcr{k=CkyEA*`V1Va3 zeN%k^Sm*6U4n)y#b?dG{wGDoK5DHqy-Td7E9{*slOw&efT%=c^KS1&}fRu{>OQ4of8;D~^S{JV4_F#`w)|91Q0 z&ofH@f63wf6_DKyjlV&W7@o;jJr6rq_4RuAbr7=JAXfk-FCmKu*#fxR43$IBfCl%# z?WVuzIc&SZ%j;@dI?h!J^UG>;N^3Zk z&r54t%Ie!txl}EH!Y(N2fXZRWXoD<16o?>)e^`PXks{E60Dm0MD_SEPs*`%C zPfV$5S_5Zh=9MNfvuUA^C0CXYXtWK_t*j)wyczeyv*pY06zXZ0YQ&VYwt zaZ-?q-_^pY!U82YuOB$&KlXCgk7Ny?)8Vfw`qFQBRsOj^q zdxO>yx0qb`DhI^A z8lLQ+Cx!=e=4WlI!pliHdgLTglH&y4P5X_p>0ZqsOHU7Br1n@n|o4b=m-ru~7B8Y3iC?E@It$FDdDn z^CzWSoLS{{2ls8qSc#=~_Zd9kKl8||`_Zh4QffxUk;mLAudm}T+I7l?FE2Nhe)xM@ zt%SioceYW+nkF~(LZ){J|BRg1XH8xsX<>%zl zO|W#i%-F;v-#9liVr?M6-~rvpryMpZ+1tVp&as32haVZ16_!-X&ANn3h5&M#eqbyjSy zj*j-Fi<(%_&m?}A=gVzHv$SSXjxeKWOuBPq0v!c#AUHVS2_NI_iNW>+XUE;n#N9gy z1ZM(ay;ykbAB5PrsF-Nhe-qAqpSTMpU{`+#ii>8Rp(W74Eh;X8j`NPC9j6D_0K6#ym3x5EsP$mul literal 1619 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYw!3T z)o%vtN(5Bq37>YEcOr4Q?O)NV+9MW;Ccjg6w$$BNbN!XvG2!9`&MnK|vJ4U6AoOeo#qVp7PqJXJlV)98fOfru$= z0{}WWUIza;=+*<+Q-J-;qsAyeBoedOFY0**~>`b=P!nuBh2kjDdUd z#4cQ`k$(6-@`P0Cx77y}SI-SFT_PfScky+fxhI-AKkEfaa)usfJNe*qiGo|ANG8|Y z#>gYrgjTHGd4Aywsco8bbdu}p?wOb@y429?&XmC>ayI(80(<|)jiOEqFI2}onYez@ zW7fk@9x0!Cp!bKj;WlSM7DMvW@NK8xl~>8$Si+dO>DLwI_+7%6ms`$xwD+LY%HOq$ z@A<`-R^4zle`CfNws^AGjB9RU(v}LFSKj?n^w4Cwd)|?oTNEFAEctrw*`)`r`G0%P zXRj`_w#!DF>MdCJjVEl<;tdgg0hwj9 j4!L^-`A0t8&cMtdx>YA>(beGTpkm(B)z4*}Q$iB}2s>q2 diff --git a/toxygen/smileys/default/D83DDC34.png b/toxygen/smileys/default/D83DDC34.png index 0be777d7b83280914d309b561e30a8a8f4620b12..a0b1b67b05af81d444d62542876b793db7206983 100644 GIT binary patch delta 1575 zcmV+?2H5%M4A2aa8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0iIAyR7I_iO|6eju#ir&lv1*k zQLK(kvy)M@mQs0PF&z>Gt&dJ!Oea+~6PbHKj&(mY76O!ZJb#pVLNpWtp_6#6rIfa+ zkiopRr=pr;Oe0}SB5G47i*P!ah-i(8g@k=_f@d_Xk4<1pBWqJAeq}SAfkds3O|6ek zl5Il|1OOcd0El2eM>rg8RxLg;78wx*gIYbRjZ)36X342zy`5UHl2E9MN|<#;hHg12 z3jn5xPhDMIv44|Lq=!gxTP+<10jP^n%&cX`rC=y1D6Ec5fo3xi2LqjuddsV1!lGWb zm{hNkPgOx41p)vl3jpBY-@vDEz@J>Tl~S&dPNs=Tn}0-^eL^}a5E=#nFfcL1ww|qx zO{|VhsEbRVf<}#VJ9l0#m3l#sbvzXf1kcUP|NsBOwSS$RfJByiL703&i*Pw~Tq{#Q z8g5o8uaQuQZ8+cF-R0!uy12JzP$gtdBT+pXfoL?clvJ*eO{IrPlzKslhJ<`$F|?Le zrHDzkmR6vHMv{0yX;LPqicFt^N0fO$k99sL}bF+ zxCsCN00(qQO+^Rh2M!7)0nAmLy8r+JfO=F|bW&k=AaHVTW@&6?Aar?fWgvKMZ~y=} zjg?hFmfJ80-E)c_K?VfUah%B7^}Q~8{C;qf@qaj;b{fA}5+FW&kW9aS{-Pi8YuZTe zGQD}R#l&t_UAY)rHMee?4CZa#Znwz{c{dG7b*Va}=`8b&x2 z!6s&zvT>2SH(9+IHrfaXVs`$-wzEO z+%;T?*J#@)lXlhY?0pkFYo4YR#T98P0)Z?TQ42h#6$M43>VlbKWOQHjA7lixhJR_t z$ZwHcAYTHr3PBYILE(-AM=M+gD$oNFtDcgEzvXJl*b2J)08EJ-4Jpx*7R1#zf^Mk^Ho~3_4v21((!JBgt8b+o6JX9Nt#TRgH@T9SnUGNTTW##3ctu z8!LUr*$s!^Rxhxcy@{Q;t*ih8M( zpl<*G0L@86K~xyiV_<*+MkZ!e76v4s#>%G7j^J`|YG|rzal!fA+B&*=s(<>bJiHJ- zpMjx~v5Bdfxdp!fh%abqXk}%ss%m3vXCVaS3)?$5IytGTsye&4T39eJh`70XczUUV zfwzw@gQ%Z>KwuD<8ypfE7S13R5g7&KM90L&#V4pHCNU(Zq@<>$r)OklW#{Co+T<~a z=NA+fNk|q;NtcwCl~+_U$bZOI$;m5#Y*(zV@v5yv@{Lk_T~K`kl7Mn!Q*%ozl7LEE zdq-y{0AZ3Wzj05UK#GA%GSEip7yF)%taH99mn zD=;uRFfeWeuTua303~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR1z^TIx{soG&n0T ZFgh?WA6Xagks&7v002ovPDHLkV1mX!qHh2I literal 1639 zcmbVMdr;GM9Ips$z%d`wO#~$byt7CjN!vgoT`6gif@M%pa1${!p;Spz+h|*k(-Rp2 zKG>$GaAnN*akIV{FdcACCwjhc&fx<@ML>okoWt|noVXOh?GMjCmP_(`1OSU6SS03dL;y)K8G^x)z}N+H)+}TW zrc=d@+2U4mFqdV`m`G%^*@QNUkg*s<5Q?I14Hy=12!YjMVhOvzWDOlxP*GOB#b{=Y zj0tcn64}gZRt|EWz7BylYqf8LP1dnQaVZno39|?iibXW-j%yTcWp&iQZoCz3O>>wj zk&d!5t1Wu&JaR(E$z1NGm|HBFgVjYOJKDGK@ll9#wGtx&P1Fu zB6o58r&z|fxC(Sn-;_SLc+)(TiR%~(*BT1C@kbudW2Z)?OtZIdPR)!@C1$K`y@F5= z*WS(^`$Gt5PyJNz9_748h-m$CWkaO3;rYDH6$3*@B98xu7sy1iG-b=q} zT9fNh=n5gjY@T z8-~f#69U?w5A?IYi@cUR^;(+r`O%g~%IZaa8-FgI-gt9CrMjqdMe(J_#Z+xw%5iJA z^xlct$s~I7dQGTP5OaaNrx^OF?SsB9hdC&EFkx<4VSg|`szO@ecv*jH>pZ!V*;2T< z3daP21^oT5N}Qsa_>tB`d$Vc|-|&e}r;$zDE}uKNA@Sh+ z58GU|K#5D@A2dHa{9?#~>!FjDm7C-`S@Q;Ro5XeD=#lzoj;~HBN3OPPk;T?|SNBy` zc*Md#`b-hOuzb4A<%RzM)h2D% diff --git a/toxygen/smileys/default/D83DDC35.png b/toxygen/smileys/default/D83DDC35.png index 5ccdc028d40d81ce95acc0397596ba4ce60ba9f9..4873b38419cea5f9913589f1cc441a7be144e054 100644 GIT binary patch literal 1713 zcmZ{l2~bn#7RNuSN>~IG1g-WNw+bR5-~y$9pb!&CVh8~(AmoPJ#Gru?!dewdV%QQO z${tW`QAnulVR0NI2vv%fEo_1qmx5AMHmzE1-gSKQ-n=*S?)>gKbIy1F_kZW!bLY#T zkPodg{L&Bru!?~9q9WPs(~0^5iPm)*8HkZxZL=~cq&{D58sJXp)o)SakEa%;+>sdB?q zxr}T1P;WIyT@YI%B6XHWjkgK4H`4lRlRC;ysPkhL$)p>_@N}2t-B@X0VBoV~q`!Ab zZWOVql1are&I2`xI_;0hi~9JW{Ozc`wB}CODw9^ibyCZMTZ=e# zd7Ohz+m8^wsmqHfk9I9gq6{^QrtaiT_Y@ARL`6x5&n1vYn?(ZIbmjHRQ2|8imNG87W*KBM+FD zKX!Q*Df9&dlDGbpzKP*>gN|&A3;^^_CDD!ws8Bqb$B$xhPQqwGA`eC11hl}0cygkM{6LOoY#_g6ZPd~e&duj16)AyF@E7GT!LAg~h zah5fdpH&|a^G9>^__hYDffM5ZDuCDFL7o*SZlv1dHP~yLvU%|hyh0Ryy~oTs{Fzh z#r2Fi4_9x?WlD6|9ZNq&y?EBdrt(4}V^Swgs2D2|+Y(EAICERwa29onpOtYwQk#5P zXiS%N!%|9daYsm@Og!a zm_Dx=KY%{zk}j=^W8|n%sj=mnO%6M>Z!qT27Y$r=nYhSj8%T1v^(Z0mg*GJQ#L`%X z*|mkg+0*?Q*-z=aZSz7@6T|w+L}iPuH(jW z#ZA>L_Nz4QcZ>V31~v8>u`IifNn>jmdnUFWuYk%E^DnO{F*5HoH0hOvpu_aGIvqGt zz0#*$87>?))kx4!+wB?c?z{FhjUFoHFTdiIEzUbmFnTYoNaA8=Ur}EVQgBijo3A{iYCIW4A25}^AyQZ3khSJnBNE% zB9bqqWN&2F7ZObZB$mL|ZScvVF;6p}NvLbIOBm#EtDtX1$;OQ`bi#1eUTUIp zT1uxEH(&araOz~UI@A97?Mjb*hC^NY3S6w?SCi`POGd7=%%ij2OC!jbt{`}M($)=z z(}5l)@*kn0?q@J&@*iX}KaakNj)8%W5Ind8(HUzj>l>QuRjT^tHLF<+hS}Q2#(189 z9~Z|L)U}wKvvYaTu`z$nEiEi8@#14%7cg@d=3g(GqEN=hs5w(Z6B#SlVAY~&PI|gT zl8(yB%$C8qS<_fnld<)qW%H2D6fBmid-n|}s>s?eDgC2;%*x1?DR$15X zva<<e*LA~_uHi4h7LhUwwqgPNIVvokMe`-hkD z*Ny(*(b7_@R@WXe?PO_z-f5wM`3^F6k6!(HXV06Xof=K2@!jQM^bOpNY^k$nUJf;ZW#<^VnQpV|8hc>n+a literal 1696 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 literal 1697 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 Mnc+_tnRrJ31DgB--~a#s literal 1685 zcmbVNX;2eq7>)=?MGA;jF3TDVqL5>EbFM@s*$s&jgph%VrDI4ISs~f5S&~4h71Ro1 z>tIEvW6`PQ5)jl{I~5hAwcc^+Q7^30K^<$STxu08c7tI1!|_LVcK18>d7k(9-gmY< zIVr(!a_D3hi{+C9E&wEo6zZLy(SScl4h>Kjo~`YR))=D#VvGN4aQuY2D0##ghj^gZEI%(gh|Fu z7i#z#s{+p>Qf5B0QeM;ov%8E=jHAfl{f|tpPzC2EnlvT4;llW3kO1S3q%_ks_=# zL0SN}q9Kbcpk-{v)0ZKbts2cMVT)}nQB2BsPJ@*Pa``;7*&Wv?+D2>ff8BU3+NLYC z;yf*GBMT@aa~|2V$H`3Y-fYMXWV}I^QUr4-hCGxs7MO7htwv>R<_p(Em=FwjV7PYY8K1mgc^<4Id9aYW~x#R!K+<|Z1=j7 zTrZ?EH&$xiyXP}YJ}8n$p6b&M%*&r%802#cO{ttwRTPM-GuNYYCl^G{iw?9xWgcL#{!l50#}kUh6Yaw< z1|K^Es&^`jZMK+qHj@FGjFWG5HpwgQa*TqZb4hcMcV*iBt&=ACKeIo2lKnU{Gaz+t z+FAY#d+G6t%k@v}Thr5iG=^6VF}fZD0P?iXL`GfB%Sve=Mv*8eOTNf7slpjoG^8Vt2!0YhPMkXo(_r zdTMdhm3#A=j&xI1IlZO3MeTM2vJBwj7zGKD(hdwr{XJdJ0`TeE3 zDSm(NetX1Y*7r4Vuu_U_9~hoprVP5HC9c$odphz&$5)--@PX=#$)6i^U;Hs7kv!z4 zw{5H2bt(32=)l0Duun}s(9@%`8ubl1J8-*a_eS&WXAvG%HTCsnmv1Hzg1x3+&NMhL t_D4`ru=>=yH$U0%oj>PVO8X!=!JBoUN7g#5dMa`MDAZUI+9F?3`VSQAcaZ=9 diff --git a/toxygen/smileys/default/D83DDC38.png b/toxygen/smileys/default/D83DDC38.png index 2141d1b62bb057a21c17694d907d51c66062472c..d6469483e7916b0d02009c922f54c271ece5739d 100644 GIT binary patch literal 1640 zcmZ{lc~H|w6vrO{C4e9xO1LDF1P?@GgaOg0Kmr0urD2hbIVpT#gs4#MGlAZf71BMT7>x6;9Z_{fFyGOVFtiwBqSUM z5D)<-f&lD_0ra`0*FClYC>6TXyr~ek26$`Xh6lv12Cg|on*$3AL8h>8r6T?x5Y2ZG z<##cnM~EaS*pF8bfm0Cn7olc=5VA@E8~ZoN{@+2W{F4)#p@#-iI^d-m)BnGbZh!!uvF=L#uAXmql)w1h`Y=w`yNOt|0#3G1O`C)7|C zO|>wfnE_Yn&_shu3KZ{v^vzI2hH?iu>j)JTIO7OqJ3(v@h1(!&3!K^xrS@>n39`4q zMOVnS0sb0n;7T~W9?oosHU>QMgXcle!!!#pfoKB6fV zIci-r7m+Km-JB^$vxl|@B91EGg-%s{qpGD&(3Ffm$pt_iO7dp%Jy{2_TwY9QL?j2x zPvmm22~kL{ZBC(2VDAeeI{B?0Ic*8Ldek;=wqLaU_fLV`lz{tD9Zs9`GPE@`I%lT8 zEd9Kq<6O?z2B}zoFKseZR+x3!C+@eF?x3MJ@8iWixd)}W-bM9}nOYmyJ&Edf5nnu+ z<&!KsU(Y&fuM@yNz1B#-?NEVCJY1OrSB|(%zK<5Wm3m-r8#NoGJh*P!-X|@%X7C_7 z)^VRrYu~x56(ii}q|E9m^G@Y&dVVTDrzNb~OR2eM*tE5}^li_w>2EPzI9b1UK*dM^ zBi(d(Q@fDC@oDx7rzK*Bt$nl2EILKyJBEz?+iUJ?jZu@bi&keZTvon~(|1lu(}_WA zpku$?(B~@EIhe_lNU}THR^@M7w9#TM_T7dudGLm`Ad_9Q#~!P--jq`0!bl@@?VVa( zCE*V}MB8$|tr-1hhJtvrj0=qm0}0-hINR|a;@kX>m((isq$wYG%jF&E&sp!(=OU)= zJg(1czO};;(|Vv-`=Uonp85NFzkx3VW`8ohp`SLBCZh6ugUcE)nj6(t_Y&RPA9|`u zj;OI09ShsNA$Mb{k9ysj55kqRgKm@JmIBTAGs8g)FMcuO!Z+X1I z^uc^iGMvT{=S@l3RDmzvd33lBo$@?zahbn^eL+bbzHQx8llrr&deMT%drL=jct4Wy z!Y8Tnl8xQMW19UQ`LEr(8|IIo?0C7|S|wGZ;irWDQ`&xm(vw_Va`>kBr|uS^50(HN0!TWey%AD8uE z#TnUj8tqX}51qC?L&U@|sT9lejg3)}k>?vHyC_V9F2baVu}N8JS)j2=;Cn)s0ey|WXC3c-Znsw&fi*`p(Dvcuh~k=K~9Wx56V zUY^q4Ua$ND5@|U`?@Ch~Z+c2YeQKJgk(F79IpyQ(`pZL$brucv2ll&p7aGX>`m84= zj10vkd;P8ZWO9srV8FWhs(>#LTy3^WHD0V`1wfc2u^&XOokO}N@Tp9G2#e3L3+8bU z10*7eWJ$EK++^jwiD99BqTY&Qpa{sCO549W$EptL^$#~pYV literal 1558 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDj~9?C5G>WMXLPX6fu| z1oXGFi-o15lZC6BsfCN7fr}DUZwfhKrVn(CJ}7Y@B^H7|+ zGWQJodT}4;BB9#%aq5%Teyv^U!vEGY;_IV5vtJ&Ljhp^;+e5Dd+$?+Y_{@w9sxH}= z$yPJ2zbp6u!o-vZj|;Qyzwoq8+qJeqoiX^p>cFKcnO;(Ia@(4IAKI|{0S~ku;HAcXy6gMM`rthA3t{R#@uC{np!sB{hQ(L@~cU?sZ)MVb)0J@tCwf%uk>w3 z3A0D@F)isG%gk)|o2H6romyL)##xj-VP^B2SFFa(r;jY&pm!+$@74UsztZY6ycHhQ z7yn?^w=%m{Aoh8>)2nprjU7FGXH&u=ob1CC6svogp1bs|nj3Iol6t-CjWbEzD-)V- zwqCoE)90hb_|j@czjL;2h`a7W%S6|G|C{tbFIg@&b4~ZmSIf=_$O~xMoRaCg@H?gH z#0iP)WnK$*Jlr+w&Qq-q=kIZRw<=ZNcWU-$IjcF|GuzrTKKX5^Z(RLT$NK(?NBp~% zTn;;sDfZD};zqt5@~hhfLoHw1On*J)FfewTvJde_afRm0ew<R05{{c}Q2 zw7ff7k`h^1-m?40Wb4bDKIVGNx_n45vHya=je4&6E!z(TC;pQ^xG=lebN$El49pDD W#aE7HX;-fVmDHZDelF{r5}E*vQ%9`; diff --git a/toxygen/smileys/default/D83DDC39.png b/toxygen/smileys/default/D83DDC39.png index 775d8571c4f2016e7f2011b0f2b47770bccb3258..cd2027c4e01d8cc273572af6ee43b362c98ac369 100644 GIT binary patch literal 1844 zcmZ{l2{hE}8^_w3}Hm67&F7zXU2r=yNrFtPOg0&+{s>| zRM!%NEFohH*R|ZDhPS;3CaSsSAb06R%|2geWRNW7sX1_1w)0E9*YumzVwmjJkp0$|wz z0CXk*N65JkEG_`RBYVXZt5*`kfFOv?X2Ze%!3t0OPF)cFJ1xFZ3&GfSN?4~$AWR?L z2WG(Dv%~WSY^z>=4--dniXgedgpfSGQBhKQkC~RsNXF6r{OV6>#jC+dvyPN$7weU~b~7ZaSv++uIO(5^lF#`yJN0c_<#q4q%xQ}8 zha?+_Q~QAHk6Veeg~|v}eGv5E?rIE%8{oK8+p?IP z!U-oWcRhjFp%B{-YL0@6aZt9|Fh%cGZTZ~5@chfsRZi#Uj#8-13Cg<)WnmU0%?4}B z7GF)g85~YYNqI9gy4lEpax9<{r^z&0Pet)EcV=mV`(|(igTc&k2BB;VD9Zw3Q6YBV zXuRuG_lw2R@%iB~1qFpS1H({09!k`QGHkcYeAWsACp&xQ`iEvYFBe9}m&PaG_CNnp z=KY1PyBUvJd01VWo<07oi`&DQ?H!mK7^;Z$`*UovgqhJ?Rr9#6aiskj_XWq-7|reK zZLX?L3=AnrNa<^C?XGJsic5mEA*9|i0m}$Yvo!mvxXYY>`gF84wl>w98yi~Qe|g{B zqOoQC(L{pNe;_}5WqJEao6_nsuJzAPi;qrR)Z~|mURsE{Bpcs6>><6sUZxpQJn0GTX5WYMti7{k{I0Qv`gah`x?egPdu5$Zny`CbnA6SJI;B^W2<9 z(-mco=kcjPCZ{#w^_qGnd-{fVr;h2vgKHMpe7i>}qqe*P7{`&mVU?%(f}$!fk`v-4 zi+ZF+C<6iEn;r7)U;ef;>^Zf6CSl9)5`oKq=Hp-8F_Np?jmS1LGP{y1L_upfdd9Aw z>^Qc2@ETuvxF5UeE`32Nz;|^cRZXmS`*rL#)~;+$C~bx+wIi4`%TJ599x*Ra!Qi58 zF00U4g3iwZ{e(&D>YAd+GcT#F7jUiHq993!XVXZ(P*xFHyi<3CjHu9&9eYiv4EvFE zVWCZ2@F>D@sOsJrUYX&rX819uUxK`CC^5@R2rxK*=wg}(_~01*WVd(XNps}&(eYUE z2lsay)>+TA$ODot%7j0jDo0x8eqv@+J>ESwENWuYY|a!5zS_rXc;wbU4dCeWj(xV$dkr@VVCd&vE5{W|eTB!s@Cg)VUeU0d@j;F=mHb*@17Vx^_(Mn+=v!8o zGpM5|M#uzbn3bI_m|;j+87)@MuQ`POnon-f^it51paz^fptYpM>NBIx%<`@%c2mpe zM<5+U&>WI^@2oQ^TRs%UjOks@^WmeXYLM0=_B<82sdH#mS~{gY>l6Obps(=XSg)jq zxt;H23s!O#S&h^5T5gkln0snroD75L<8;0iGFcKUilj#tht=+db$ZlDR~!4o5JDXB zb{7C%KK?xduwsGW9)3Rf`Q0mo{`G>e2m&D@d_fZ}ycg`dcu7lJM-+h&yR56He^DIl zH!w6ZHaTEwdH`c)e#K%xkg&AET3@xXwY9l+-R_2^1UPu}mc0Yc5s!Do5r|GV4}rr+ zoJlUOZtm`G9-dy#M-BtT50YdG)tlya+sD^WQVIc%O5gDh2n-Ag4h#$l4Lf!m$b?5k zMny-*+>MTorN_l1!H)@vNtEQ|6h?9~Gc_$;7M#e)%*>)>=j7hY`z=2+;{=c^C@d;2 z$tf)@D=982ERY8$e=4u2tYTGH*Hl&3mY+Hacocp^XG}H6B%!^S=<6tk-XEQ=Y^3Ay3n@r4ev6A{tL6!VajSR8^Hw8cHf>u_`FE+Bx(& zmD4B`8imqOUq1dn4IW+u7pFV_-JtJgSqL{se%-;!%ZcWTqY{BO!OM||H1fbX6D^52 V!W~LC5&p^mFw!&Ato-aC^MJa?9j zwUaf8M6&VslY|f>+4NYN6K|K-Mwl2T;L-?OhAMGAj3FeU0*yuhe>EI~gdngYarp(r zlSCS>RE0+15rK2~a#T%&O*S-xT1%iwBu{UH7M8~$I1r7*s5An~i|Sempi&4Z;p{*# zP%B1aRenhrB1@VZDo=`&^Ar?sFTm5lCkWIC4g&^tyhg`22q^u$d}40erc;1^6Fg2p z84wi_7z~I}3<20QCRGkX5WwNkAQqd$fjj^P2r=j&adW5;i_hlp88d;wi$ZwA6iR-G z#COmau@X>Xaa_x%)Af2iP0ysESPUKF@pvW<27^jiP<4qK95zrjI=3MP38Ir@DlM); zHGqi`jz$x30fkUH5Q17882Fi3qZ>>Vkutgg*3uywNLQ;(arIm4@DSu*8DF&4g(hkd zdI+LJ6EHb(9!j?%Fp;~jJ2Dv(YWP8ziZ~QFUV_RK)QAT6mk21t7n(w);7cJMBxNwA z5L?QSLJ-9DWiYv1CMaS{L~I62I>hlMELX&sDT2f-o>(Y^2o@n*BJt%)eL)6K1WCn` zA*{bfhr=2>G7c~;T%}Q z@eD*OK`_+{MB$5}YM?*AeAO5HGnhgt$Y4P{U!GXXAmSEFxICeVP|xN{Adt{LfK~jT zoY4s}bW?Hsr&xxzhzc}KKbJnS_}n~*hUgfKXpM~aTB5OrFY=cNLk+h>e+-Ow4kI@h zt3UL(_4K-}i2Oe1VUd$?OQ@BVTAVX>UWQNRw?1YxZTgbnDU#pI8jjhIPT#(7Z_Dhk z3ZDIPOKD@7eYsCrdWN6>qN#b}N4v4H!w%autcX9@^(neFV|Y;SlV<1Uw7X3;4XJ(i zM_f+Y{4VR}ED!S3oEP23gsgW(k6lBI^(hUC_VJX5^()&QuP?KSJ9P2%(da(&_r6QK z-afC~S<`d+y|^;f?(K;3$Bq0(TH?|rk50LFObfEQI2v5A9vstDN#0f>uJ#5Bj3Jkh-4Z5Mi+gm=F14EPt<0-V z?4q~b%hG`P*3<2MVrGxLTG3KiR7Z-7TfNI~=|*bADQ|La;=Sj&ue6E!fW5`M9!;g`KI%Dj$D>}8nD=ZBG)7AL9Ac$<|ArneFwKY z<-(?)EGO|gG4Dzj&r4z`H~0@*0G(2?+H<=DY4O=8-smU49Pgx>FX?hIK09%F(^lJ7 z{e_2R_haN4C-cwlcCPJ(t##j}?Wm3!AHZ9)?q}DJch|yx3o~NE$7eg)GfP_wKVm7F zVI?0T!;R;Ck~`pityxy1Winawt&^-*_T@*n;GE5W={GQvWDebXGqdNX#_uj)dG1(X z_v&Nq`DgatfERE|i?2SxWp&-dfDw;NZzkpijB8d*%J$9%1%LKnpQQ5-nbo?nI`fq4 z3-7hj-GQ|fGxzP6(?-4C^Qo?LG0S7Zt~F5^tv!}bY@3Zy^y7z}BBG!7Y`wDWloy!O zH_V3YdBfhwU0zj!-C7+RQDbrIP)TiAPF1lJXi~Oi5|0$}85! zk&=^>;m791jI08kwPSi0E~}{itsw1b z->X&fVx*)9Fa5abfrSOLa@{#Qv$wedqa{dkwbeB}T{zFi^!xIc&XpVxEzSG~PW;WD diff --git a/toxygen/smileys/default/D83DDC3A.png b/toxygen/smileys/default/D83DDC3A.png index a2bbc5b63d46b92834f8d23a6008e509ba81a348..7dcd9f7e4b7962d97e7720f207d9f1028738bc74 100644 GIT binary patch literal 1676 zcmZ{ldo;Mt%@oQQ4YfH&W?IR!DJcoP%uu1Sp(c+h z(HI6}JQ|Ov=qZ!+h-xH@TE;GwSa*M&_Bdz%*mFMjejn$4zxR8--+RvG?DW~Ht%=bD z0BBRl9#ll?ep~9w$i8O%Qa?nf>?e8?0T5l(nrEmYHg+GG>J7lLRRAQN2H-2=N*V>= zFb;t6U;tdM0$><@t=@MN07|@_UNjPf@a4d}t`x9{ET8^{`>)wsllt=XH@#278G2HV zAa3pY&?EiObGN$r`FDi%?YFC%7vWb(5emxc5RlA^boRdCm5LGbvFz1%XG7~#gvA4h zlAV8NkwyOpftS4h#az*HMcxucp2l2}?%dqmP-`wMw}cH_;JqEN!2_1Bl(*%=r<{4g z23V{KB~I|3H>`Dr#oCis)vLmR_#lua0a&a9YwTpnK#`*+l0JDqHZfaZKG&Ks^T!a< zsP3~UDBb|Y>$}f_uWcu03rt&|bpJgd`jP<_9&zEH?*=93V4cGqNn7{8(9~ZuUmkEj zbHUe40QnkFVmF%s+Mc|e6vsfG60BScMeE>o@QDi))iGk}!-*XGgW-e!UX_O3CCywY$_uT8yy?P3OmTevg4zf*rO5A z063q&;!n(eg;O6q#5E%>QR#}g*4{rOwK+bY7@alc%sYaS2H>%-2^n5ZCl?CMr^5Tm z6L@L7=zd$|uDx|edmJQ@dFYXd*O8JAbi9)`<#34$e^gP3dd0e4Jv-hTaGyR{ieWh~ zZ&}64sf@{Na0-+fJNB#N_SCjD3`QK%FjYkr`sV4K6EqAld6@~d$sY|iPK!@%k8n9) zapCQ)V)D8u%T2mFGD%76+6^m7v;zW*hAodu_VccOF4ntfzeg5*hI5FZoydQfigLW- z-|bUkdlTOkE;J(T;@fRLd6ytK`Z(3JJ#E^koNV-5q>QyR+6w_2a0xZD2W@JTEo9 zmKJ{Ab)%j;{dLn3v+JW|9Y?!?x=`1H?!VlhHoyC`)u&1Rk%0PpC+ZUvRR?HgljoM` zOkix9BHz zN%9!XP{IuE^))$v%5TMNSe!Atewn6CNsedMon?|O`p(t^<3WG)MrYn;=lY4Vl zcO;!N6ckxKF1SRW(^8%0D4QIbB)=1Kr(0x2+M2^YE!$$FJr-Df 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 literal 1685 zcmbVNc~BE~6ke24L^)Jc@M2li3X)?t7YW1=$OcmzND~wsYU`3+k^srZ&87)B(t#FH zk+D*TsY=mdMr$1u!76P~pdy}FohsO(AV;fMtqQG;rM7m%Vf%;UAKlsA-?8ue-uK?` zz1c0AkxG=)uGPjTg^CbeI1QCJb7sPm@ z@GOZ|p&a+ctfY{UrmYeV$Ki0W9XvKk891<5EcS392#c{`+43wj=44rH5fcmw!ltK8 zR@y{bKo299N!n>C#3&sL!E9BlUl3bt6!D$ikJX1xqG!Ek0GN*l0uo7L&0(tq~2~OEVN1?g_tjF+=NR|SPY{G z4}}FNg2FHyqeOTyF+6U9K#?Fo_~-=3%diO|M1b(Pe31g>!widDi3;T+C0E4bin&5m zJb_hNY&2%k6BB+-jNfrA@0D0dB1K>{Nu`ox?nDJ>j3iCkjHDGz)Cs|*X(kI!I&6`i z;~9%qK~Sdm30z5$W^gpW64Oik^ALp?QJ^B!qX2`SBlu2g~MH#>snn)0}I0Rbkly z{G=Q(A*tRSUb&Ft8u8J5k=nB9mI_^SxVT`Zc)=VKSwSszFP6dXja~fq+u@?uQ8pxq zoubq2sM>YV0&YBTHvhCGUewOIxUscvrVnX1o;r+8udwVLsqM{MSPAxD%I6s!M0J?`jX$7uMT{r{-#;WsocTwb+Kh>f6`L3oC25$*!MU zkg~5Mwlv;bKm4?!E8aHw)QTNIH_$z)yRjzZ`wI*7dbe*k>&f|*Q3LHa8+Q5+6_nIg z+*xMF$fN8ZTf35HmlV$k{^d;Bjmr@WhkGggv<>3WKhI?sR@{_D&k4-+n(fGFTRyUT zo6pG|uFYY~w@+mkhA!d`_TnA!OE?j4L~eWbc;HZz#@^E3p$Y7{{5&q@8s@j8Q$GF6 vS%C29;;n184()nE{Z1cTRrtJ2Hfb7Ack(?T%H(^+^P^Cq$%-Qh>x%vXuZ((p diff --git a/toxygen/smileys/default/D83DDC3C.png b/toxygen/smileys/default/D83DDC3C.png index 58ecebc7cb1104a682b04040701880e41911fef8..c18d7289d168f9b43b1cafb692bd1082af46f781 100644 GIT binary patch literal 1735 zcmZ{leK6bE8pjWkh?i6*n^L_h*+m;%ZHiD@yQnN_1WjmlX>US8srQDZwp~@z4P7fz zFV%>bD(a<#dT&(~-LPU8T`elK*Sl0nQM9?FS$s34l## zD`6IZXd(dfOaN?e0iY9++j8<407`uxuAbzbogGN?f5Yo6TOcaWEV!SZTgEuU{2vVr z4$rw$1#zpW8she!2#c$8i^cyzR4o2CrDD;a+2h{ZgCp$1g3XxTo*AgQFuBixr7P1qB6qdU{{l+gH}LojT=x;J^Vq9?xKS z_6?3`Yikn-g!uS)G#dS|Ln?o#(9zMMLP2xi+}w0K$-rPRBeKcWwGVlDc`z7^Xl^U~Ll9+1+36Ld9m6|NY{pW65FH3v<|s7270<0HpAn|-yv8uRy7%IPT&)_HMDpu0#q zUHOH@E8RS;@Lk5T0Y6oRq1_Nm?VqiSKrHD>&*$;1o8U!+z<{C5dmbr^o3 zik6ijY+mgCkhHfVQAGnk@wq^ns52XbC~^8JF@r$4@#J^w$?dD(DQ8&_w{Yh6MJ=X+ zqW&YKTwH_XM0UCd@$T2^l)MwJs^^_5k^@Sj!AFX^hxoI$|32VRa@AOa`DOSXK&SH=<_J|Df9 z^*$DbY00-dU31w)|9Rs-u7+SMg*bYu{O-|5JJeaNPmfbdW3LeMpAeDsK| z&C!;omSznAhuIz5m)n}~WVn6Q8A-s)7iMti*{R)Fy*D$anpm_N$^xmirbt1i@icdH znCQ>aQhrJ%Eh|!Pq^j{SI{ti~yNjjU2{#IbX6{DiWi$D%ze;+0`j!cz5Q% z458udpn$l)HZ&j9^MMSyf1JPw58!^wisAqVcDNr0PYGoOa_AfuJ1(-D1N|-lP{?jh JRgTQWe*pss7$5)u literal 1780 zcmbVNYfuws6wUac%EJdDDqz`(S^*`=ZnA+yqK51SVtEEo1VlozkO0ZX%_;#c3@M;6 zP)ngIBh;!W9YiWbDl;MoDhLG}LBN(76r8~-BB-b+sNEpg{&4!EJG=Wm_MUU@z2CjF zSpj}tR_1o*G#brH;*ChDaf#_!U`D+iUwn{J!zvODC4=x7GEqigG@$~I#sG<07K=$S znIdUV3+6$i&5cn7hmxVbK72W@X2?u7482-Qp=mUaEqbj?9*>bgG#0DU2*;_> zA)xPoeObO*5vEjmClgpuvR|+~IbP0F(6?*`JoJ2uK#h?yK(9{F==gd8eVUg~%}v`( zIxuZQ#tY~loeK3007N)}0WgE@DrbQpz~wSP4$S3(8v%#~LQEEQb6r6WALjBQH(=(X zQ)+}FhA&0LGg{P2Kv$Bamd|7+CMGfx*$kYBWr92&&%^;iu9StVE=fbm^sXA+npp+} z)5!^ymQ>*yz{Dtv#`lr}I_2rd5Y$><-%rFE-AtmWlri-(EfZw0m}<2tu4!u>DaHPA zXVdX~j26KN)jmuiCU7+{onOA{Gx}@@;Xw%MhMGKpKu`#YSUe8oM#aw(y1_j9 z3|8@fa>k^bVVa8LU&S)JMOC0_`lb^mX!w7CODTLNjT>7C}MFc%X0LNzon(sgXd?u#wO1$ zwKO~E9qzF^(xoJQK?2jPRy{H+d1Rx6?UdDadm@pDye={LdqBvg-O35N_04P|MYMBW6+8EC z_$B-8!`3wsjJELo`!|`JKMxEK%5}*8BQ&xFepur_e*f|LNqDHdtz%P#?fA}28+4*ysOHaA;jk+fJSo>7jrp^oJg1U%FA(&rs#ADKWe7x4s5VcjU zcAb-UwcdOxrT)Htu)qJyvCz(lfv~sRB5o$$ZR4KHxMxPsTkv*rGBC38%{|Kkyv)Yo z^%<%2SZKvSSY*oB@Gl25)&%zS^fW1U#+=)Jv}yp?*dA`4FBYd>=&T+-cFL)|x;o@~ z%Iac8iB*d=9Civ7RdYC;sJ`#)lozedw$wJ?zLsmil&nUy+EMdS^ diff --git a/toxygen/smileys/default/D83DDC3D.png b/toxygen/smileys/default/D83DDC3D.png index 3863e975e1f0c7b923ed60418c42aafdbff0464d..0044223ffcb45d4a66949806c701e42b4cec0ed7 100644 GIT binary patch delta 1502 zcma)4doM5XaeG>l;k6Ec`$H6AlaTQQo4Je!z`>G2zn)ueS8ozR1Z z@@PyNqsOpCd8CYxHi?~d(Y>bdq4Nw&mZ@Gt^zGY7nwl| z008z_BRZ0F0c!-T<&42fzda08$wMrrdKa z;a&isqQbUDP+%lll$tu(|HMd@6dJ9PBGamimsQmby(dZ$bn}n5B^|oKOh-`9PM31h zZyw5%??|eNO}?@{0Rd7-*!!w>?5W)rFQv1tgfV5IyAfK?Nqc#z>3dr*+^>d*|A0qd z!~LVvH@e`b{!4a0-azFhBoSA7j`8-Q>1!O-mic=*-C#j9}lV{KE%%YurA z&D&r8T7EfT8+`XM+}1yPspY(1L?qgx+?O^}tB62bRFLUY=jAhEId?UVX=4BBjC9{x z{FGS!wm|%)R8r&_nqlQpNTf~_hzmVKR8aAxuv~pabdo@Qb);D2`qSu9kt!u;I=@0& zR{v^0bmDn(?sWcnZJAV+oTH3QSMPx|?4u*GnW~r!Rcxkuj{rGplKNdUa(zc0Ed()& zSls(&=5>$Ab@|mRCWeE9`UiOrl+#~Tqa`J4J`pu4g2DEh!O6d(^{YO8Lwp0M-N9P| zfNv(Ejtfq zCf_IOI@_y3o2D$momF(+z+P|GE!05`)fLG6$IJH#Yi-vi`X1_B;+@L9M1AtJutV`Y zsJQ2>{uNp6JsaQU-xiP9I1u{8SaI6aX}z!WDV<6A57vA5cQ)@_u}RI0MxE}FB$_@c zD>j=XY?E5Ks>~{k_Cib7YX*1gP1$%4#TmT3H3?+*xua<^WjPZ*ka(hF4nHky+RRwqQ;$FMKuzGz=0x60cVoLclz zupnEIL?VmDt(J>_x2Q{?n(`VFvro+}4L+N(A!5d$%-GWq?{SjyY+_X!?CXP4nw4pd z%*5g9<@X=%fzFxt8SP+ID+BFU(yV{63AL^9U*0QdzdV__*y&8sUk?L8C7qFUpcrMe z)?-Uh3Ej5;Yw5Cq+LhwRcP2jPDF?WnS+1xuvU!ul+GV9jKi*!v@y=w0i-$Gj`r$SI z!p9Y=YE4Dcx$dN*f%%Kg`5)+?#jW#(tP2eFTu~xx9i)CfAL|E!Kl%C9mk&SCLq4=8 zD1aQX@)47PS%h4x-$e0AE5e$kLRX;G0H6O&q@@kuRtfi`YEszqs! zW@SOX4)>g+v&ncHw+&uaZg{+#m6wBCKt7p7vU7KGxAXC_bGLN23(q4*Ij#4!^(0`i z1aE6^dPqqYImXg)<2s_V^QKx%aQJaiR#y0SN+flczyB_3B>jX)Bnw3uw>f0S%BxVw zTDCk&G>;X>W0RO^Y$O3Z4v)tM;fPolw+I&;$(2BIBRb-6Bpj}6>sZUb6e)XI37pJ- a6#pf7`$PO9k>xr=0D>qX0S$hP?7snhy}!2r literal 1592 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 literal 1289 zcmbVMdraJP953!6yTNpeTioVGQx>YY)JuZoNlvv~@FtC#La+Q1GG8^V^T@EybMQf3yGi;j!A%!I$_K4-;!2 zOO0OmkoqdM@l<9S^GtADnWyYX@+vjzymmB1oS*Sq&ptQz%-rCm`FksFkA1Q*GyB6| zPdoQ)DL*&RRDHuyLS3%B@%Fc`=I>c`a_r(C^)IY@q(R)8V-sIIm|oaD+I=ct_QZ(; zLq_fIdSddI@xG1Dvn?P0oa}~IZ{%06zxE}%h3xOh^|ih`^wZ4L*TODhpgpnX`h5SH zg-vUslXGX$gP&e$elur08Qrn3ci+qB<&O@Hw5hi?_tW(|uS_}G@<$&CSJwM$HjfWC zji+|r_vibq=PK4;-rcrNdS~eN!6U+Z*LOs>O|DKh$I6Y{mapHkWbMX9tJcq7 zv1ablm9rKun?8TZ)Hw?$&ze7R#@zq^{{xjlP(4sJ1OfE`(S+%$J5K( z*&gT)WoZ^J7A7TWX+CylWeH}Wvs~?U6vY{1>q8xZ4vY_T(N3w7yxm=RzimnU3gobtc>21sKV#?PQnBdrR{g}lz*Jjb6%tVrlvu7% zP?VpRnUkteQdy9yP?1}}z+llkHPn049RrR##a%Lv&$Q)_8*eLHdCKUxfAjOl6?2}X zh9sYC$jF%BW556Zg8ZMaZ+X5mTIBxu?2N#r7HvtP3k>+N>C1^VLp}Lz*$kc-7n+ z$-0vgVgq0G&)_bop5FO^X;ZDMm8{`bABHbUZHK=`Zf!2+-1=s-uwu#6zvs_w7G^$q zFm1{138&g)Io3w}n>>B>k$-cq@Og*t@So0KCsu!U)!E+*8=H48n=_|8v^QHW<;yxNL!>0-xA zbFXpDa#@@4>{ZpAxG7rwtsW^XKcf4T4MdB~mZW%B38l-Q_$>XCLB~Demwie@TDf%B z;>rS@$?|p6>!`*g^;4O_LD>sCZpUrE@<`y*A1e=rh0Ghn)K50Hi?7+N zdVAqy@&DYcB~KjGmrQwDsv`Ykw}|r8Izo)+vhrzm~Zk4mKIMs;;`?!JdZnTWLkpF%yZAS1cqOiZ;646F$}Re;n46+b}T z5wRV3ff(%BJq2tCk{yzoe45YmI2TYNMlo$;Ohlp(jUYBhA}R(tfQI@(yCmDO>6521 zR0`U$7TQC2qAn1Y>Uves*jwKe=B#APNku=i`Lsp_Df;8a-ycq*&=4gg9SgKwwh|~9GCUzhaneZOkx2SnOV%;X3;uOuP3u@wPZZ!@ z5L3F;0L({da|R4!cXy$*A@qjZsY);@VkfTzx*|Z<+`JuwUyMO1$O$A%3MNV*X~86r zBx$WRQC2HOIBDKVo6JInV=XLU6|9WeX|^(qi-asJX=a=jCqY>#f}&|UgLTU>O_T#5 zqbot(6)bgEEay^zs3~fbqI6~|pdqYiN-V5IQCA~_ZfTL^pc0Q&q|>u}S{|rUHwf0M zN(5bsFDI>`Ut=LjhPAK^ZDj-#N!GY5f4Q>i{3I*(k= zO+A3{C#%04C7bEW)Z(2BStTD|Jp0Sh#~y7xc<-Llmlk#@!o;MU2-$;}dQPpHUU5P(yOKv?*_1CmM_1dMm{Yib9-L8Le)}(JY zK0QBpbMD-U7V8g$;dOR&`s}$ z^4*~+`<5GrkDa2j@3WNlpE|tF_0_A$bu9C?uG?rzqFnun^An}{)X&tfSt$|Ey- z!r15Ix>pX#h#}9`d;8Z))KK_E;XZ55@HF<0Z){h!ZtupcS^k`?sZA$JY7gC;Yqn(F ZQ;ZB$RLu+zj!mS0b#9@a|G?Sy!aohj*$V&w diff --git a/toxygen/smileys/default/D83DDC42.png b/toxygen/smileys/default/D83DDC42.png index baeb7b17059969fe8bde64b961876c49ae3df602..990bff9d474bfd52a3e751929e45d4f9a69cfa04 100644 GIT binary patch delta 1516 zcmZ`%c{J2(82(IT$U(x5RF+HXMq^N!at|`1D9ez0a~Z}=m&Q^}k+@?SyRl>!5yqZ1 zWRRsphB(&jQ`xu3SjNnt`|Eb@IrpD?&U@bPd7kq<-*?_6LnKKgR)%~40HCTSW(6Jm zcl6El0jP*MwCgAUae17vr5OM|DggLX0N_HB{{jFH5dbV<0YIey@RMgc-QqF;e1Dpo zV$k2decNNcNwYfBMX~FPCQN4ee5Uy`U-%9_B|-`mGewl{P=_CQwm299J?)_s8BH z`1dCWLt-zL0O_ozbaqD`x2tf6k8t0`l)G^~_}9?bCO4OH$h^0wP^SL%|#Hf1nr0j$={wKwVP&VuQZ@YVX% z?RMJo>m;bpH|^sNL-RnAEN^@tolfx_Z>i3Pw4cL=-p4mESX&#L^^LbIU|p@IyNCl* zIq}H)o<-M%NujGFyB%i|-Nbw#7oXF0@>N3+CUD#sVuBZ~jW6!mz9C>2ceyD*0qpm!7c=OU-EQVygt*Az!{{?HU!R}b38qtaVb?v+|IaY|yXW^7dgDa>DP zvD|VcN#WvPJY!sB`@}tim?L(s123hgUzTR45I_d392TM%|l`{9I7v|1Mno^B^3kc_#h%&-n0 z=S%9#omlF)ZQMt>e3Hs9juNOLoRazaHSl4AvyL^(i7dpIfs=ngk0+N%UTGaY8)V!p zc`y7FVW+?XF3XWtD2Pt`Rf(+qCEq8fOnQRGeC|coVGpAX*cZC3-6kU5t-j0GdnDY~ zEm&yusQ>thj}lyg2EB-A*Msxo(1JFZ80uq=4&bm@tg|!05s#by!PJ+)P_MU#+0w@tZDU%)73GObbE}^hXTRW-V28!Ex3)IK;3P3& zvUCtUER+%weAUnh9crw{|1-{MdMYP7TY^ZFhxH@FO&AuZ3tcMB@5;>fYa*+rs%E9I zqE?trll5y1>yY{c6XkQtZCVaeD$UJVHnC4F3wr#4tj=k4TbeQKcV!xW3}pTavRQ=@uS^yN`Dy;Qk literal 1565 zcmbVMdr;GM91oMR$)VtDDw{JGiaJJ{CTY^gSzI8gtw(v3PTAPhkft_kn{;Wk1=Qga zb*MXb^MRtCW8z~lUx!nCY|6Rw6!GTaPIQNNxVgh^Xmz-qI+p@<`@{2(<&yj!`F=j1 z@9+D_>VmxK!xB>y0RR|gvr-OaOo=`(Cn)cU_nt7yFkYsMgP2n^XN7V zf&C`3*9;9fRctQ+b3};)aSf(swFm+dga*;!1c79Ns1`wCt#T7;L}$VY6RHOXFGx|7 zTyB$tvJ7e|D>LMg<$wu>L!po+glR-+9*h``#wZ7hsuc@$uuPDduv!SF#TY0q$Vz-b z=0yRFGBTy&eAx^so(>$rAF$h>5(~k>Kq(=E!%P51G+Nm2kDjaFIw(81f8BVdbXv( zPNOt}AO;JH84Q><8>g~yR7b}+o`ub};JBVPYH_Vjk0>l8f$OqyEsp6>njo_EF|17p z%8bBrFcCKXW+CH zC-Dn7mqil&V1Il~{4?~8G=UR1tUbzzcR8MLNTs)kW>zQRKKq53#GQKD)2J zuzbp@nJLN8(r^U)Q$PAZ!TlaGK6%^mK4__a-|LwfJ!JmucoI%bACbPMpmEct&Ryr+ z%~$JwoYZn#|Ehz{{Wf{!1ox@lwhFOpM&W4E`E?{?@hcOroA)$0HimvWo4==aTk^5@ z&vT63oU%}!c&AqM4XWL#L`hP~$^4RG?9ohVWt`1%@$IY)vx^@7_V?E7UE_9@ z;fIcZ^uffct6E3G!HR=c)hJI>W7>|HHR)GYkVCH}Ia-2~@7FJ}pPl>tj+z;tzBPWe z@KOd++Lj=-sx~9*tK(A^x8|a}RhM!Obfwl>syzSYi|=}_e6V}rU(~PM@)ugWPgu~)2WofrUaSMWe4{y1N+e)FTi(e7{j#0=_$;nBoLXLj6bUhIsY?o5gI z6jd6$nz>b}eZYi^Y3qeCe89JIW@BXSy57h7$}abzj!4y(TIRJo*ZLe)EkSB~Z~UE- a$~eGf7+Sx*=I-(658FoPQBB!%m;M7_a6f_o diff --git a/toxygen/smileys/default/D83DDC43.png b/toxygen/smileys/default/D83DDC43.png index 71af1e29f0953ed4164a91d4c6ad65ee3321f6b2..72b0103ec6ce90a51d12121ea1d9706aa6f41d8a 100644 GIT binary patch delta 1363 zcmZWndpOf;9DlKFhvlfFP@U4n{Wh1xln9+oRuW}(3S%pm7#5SV*;DSt6e*O;17Ha%(MJKegaTmP4*+y70Gj0d`s4ed z#;k|Cm+RWv+UoF&spcXmE)DV^Km>xU?V+@l^`RtwsQ7B-6;|Frc5bsV6c$XIqMTq}EvezYZ?G7+DvX0(Y`I2(h6D5|o=2e}Mj5Xx8zK6H3>qo9vf@qG^z_CJN^ufN;>5&+ z|IGNz`uY_va__D-H^`zuIPwS01thQg9Rn#i1&ecqOW z-5Fe=tG%?i3cuP}6{64UOy1t5ui3=A9vKxqD;?a2zsrqyE~Y74jnsy?nY^N8KMb+n zG#dQ?8)uSmL$79(G_K|E0n5uJ3R-bfLSo9)yU`HF;w#gp)u}TW_VHc(t#izH0mrS#g+55u_RT6yq_8Go`suNf|w z-}IRx@3#a!X&aP9=LghUb^AGQj$Z4@o;$d;+YL#B!3&s@Lq^-9citup9y`ZYT#oUu zH#z&+0p}pcTi4o53nd-uogJVttr5 zx4y12Ic#8tR5!NW9~YT$<(G6xp{3Q!utEVcgJ3f`JjzNzS5>`z^infHqmdqhRy?uu zyptO(hov7epRm7)>B}d7MYyjeAp`K@#E}a&H;K}gX-@AL=7KHjupzeV^nEquE7sxM z1L3?>y^+PY7*M2CbFyHB-vXE5vnD;-)7Gvlq9f^_W>6JyD;KyK-#Ob!&(H1L%#95e z!J8SR7p{oPINT0q)4h4;M&T*dDX;L1T3UYFtWMFlTarglAEcU(wN*RXbd;v;$Rb7? zZ5v+G{jOT}F*T8+Zul4ULmy&YoV>L90{oyA=m}TQJFINr7f2F%i2R7oh{sVEDnlYU zyJ1?qC;fFiZ6=ILv8KeTD5j`1ukMQEn12>fy53A z(^L67HRXtpPvx1@*wi#980*o+&;+x(+L}8I1jgPLT~QTcYhN#LgaxiYE2hUsEl6)) zM+n-Dsjj8P~w3guwmul66u#qhu&Qtba6KMlu7p&aH;eH8$(u18(i Ihx{1-0O=Zy(*OVf literal 1451 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 P{iW-PB>X$=vf_ULqXk|@ diff --git a/toxygen/smileys/default/D83DDC45.png b/toxygen/smileys/default/D83DDC45.png index 75aec779d6e2cbede4fd0a0a28b8df9ed5266253..63ec09ebcf5407e8ea9863d52627584daadc5953 100644 GIT binary patch literal 1632 zcmaJ>YfzI%6x{%+JcUpMq@W0e+6VHH5MELYNQOiaF$4@kXo5+UXn2HxL3sqN@_s2+ zR7fQRfd~iz1Of@7QUi>L(oU_QBUT+`s-i|J2y8$5tNqcQxx44yJ9~ETo!!}j@URdw zV>@F2fEkq%Oheh?<1o}m=Z$XW5hxk#3)~S1Kzo_V5*v%gj(aJz9RM751wisW0E=i! zG6O&w0>CT_0KX~#Y?7;0yZiyb919PnlhFkwo3>OELu&kYp7ElzZTqgDwqHZ%qxt{A zCZms){1aarnOFHx1y1gXPRJQ=YU?(p*wwQoBKo%y7JTWUp8q zkubta8(|$n9n`Ky9n{7OQ3u^K*wXG|P?WEkwUq%DgVwy-x}HRTbhvCJPSi+>tRjY# zxCJzkB1f31lfr^UY5P(|!@E4`i>%5gDFx%q)R)<(=TFoumdY2-wv2EO!xjzvu>;m9 z;rTXLsd$}x_HAwztX9HC)h&Jw?6?6H{jfm=>w95s*V}w4Z0v(=gJb-hTM5~)a|kwT zbj7l_MYZsf23{V3m#@AnmFr6CVEeV1av9W&L52EVnOs*|2kTYv+T8{D6{s48wO9Tu zlh2pRp{yGYj4dj9XHJ}j@}A|2hNbe0uu1j2NILpm`Y0<6LRkF#>NB)$L_`bwc%^@% zSHGvLkWyzU->Q65xqa<6rEFzIHoY>vv@}~M-}wH0)WYI!k1icyAoN!)D!{~V4+)lV z)zbZFuu4STL0+lh7VE;*9CrJa5tq|d;gt!(~+g@hX6EwAzxRx?M1cXxo#$hJTj5Y$bf6?) zGcIWN!-4d5H8YgeUWBP$PQbyS{X@F-{jo0dujJ_r)wL{D=3ED#-tsE%lhrTnHr+VT zDX66O@zduuMpEL=2?fza&Ei#LUTPnUEIx6{pv2SE^Rkj2l*&HJ7@YP!Y4T({YmKYp zCCOHC)bqmY#+^@bJ8n*-?PA;{EUvpl@2tyvimS5pwWG;g-109aX_xgEz&9bR#Vs74 zqL-~3OMe<{s#xmmGxcK{VbA;=>O34-Fo~J-kx$LWb&o%`5au|1E*a0gckYW}NuEt} zoy%zLsoft;u)2a{6lBFznG!8AH(!Te{^rOOwY=MBdHeCq2>KaO_njIE}WTJ&ZmDdF})lUlmj+jEWA!@=y5`y=L>3Z zAl-Ii@1sW?l3sPJ>6CvlMUrPI0`P#-tZT{R$72^UaHZw>p4vwbo!?l#Aw){_6nyM)HhIXUewh1oRzbqJn%yeQA5qNCo!jFkaJ>MWJ4##Q zm|0nBX=?z%nIfq;e!9fL@E#*tVh@fKyJxR=t!hIXcM*{pA`V-`_1i1tq6F{=9`A;D zyLouhJrF+v(T_;@8bSOJga?0sAsm(9sY8RnwjJ^)m5Sa53)>*!yU55}he literal 1609 zcmbVMdr;GM953h)iw{mvr{^OV@P*JeO<&2VjJ7E)lTu1O>$Y>oHk2N=DM_FKL|23X z)7z|06x723x~ZFb&I#)Im~N;jhs?t%kMk^w;Kp>*zyL;0X`o-g6(nz`0=tgISnNGK=xx6v0cjk*f(n@5Jqd ziNI|IxtEDV9&b#R!)#$JX-iQn=@j4rjDU5zIW~`%n8dnqYYxExs|mZqr34?IItv00 zn-W|uO%tWL)kL;KS4a_=g$A>=Fvp76z@#NWB8zeYPJ+P!*15(-qpT7f(M7p=U|R?R zBM>G>3BGj7l4b7AJ6s1iHY*;B116jtMGg2--?H z+>C>C0RcsPHJQgKLC({cAvoP>X|IG`^k|~ElnGhfErbLjq0%P!N`uG|H74fbmdP{f3`}2@KDT()JcNtu7>a9+27TvK9xt>&uSqtuonK_WLtZyV53k)_ z-kai2toiwv`7KPh*OWYUifY1&IB);C$?y8dUGs)i`u2Yv72#hXg7Upb^YFOPz2IgB zSmf9u^X_x4%=ho02VFj%Ks%rhPdQ@{@5-Bs;C_T`D0K=SI8yt@l)fQQ8o^GD_V14hKy|bTeo%;Ls?jL7XeLOJt&CY3$R=2*`JmJTcEl;*i`*UZ{?`vCs zUs(6!f{NswPKL;e0! zO84i-DO`H&vr_5e74@fUtpppAUMyS$RKb|!?e4iWeYOo zlwe?BYN!f{C<#g|S12gTPs_|nRVb+}NL8q>$Sq)Cu;`r{>Nx3+0mq)tT{ezvET4*P zrj>e!_EpwN**yMQwdZ77Nb<>sjEo6B`|JNLlK+@}%Ogzk-kuvNN&KtV#q_M5bvtKv z$+KJ=x&DJZKXarey$aguees$bCLm!5SeCyU0Htj?P8Fo0$DjMNjyT*LA=RT$@N zkxMWutnVz3&MoGYWo23vC@t8ov-^?3c@Mig4&ORttKK{an*A-RLwUzT%}J+bbCe$m zP7zwb^{F*SXsT59i7*e#uVcPem zO-H@+g&Q|KR4`j~tJC(n634Z^p#4YZG~9Nd$)T@s+v-wg$4VEb1>(js-@>o@ojbbK zZGJ4rLhJYO*8P4ZD(tfb-^Ccon<;Kd`o$mjPUv@ekhRX;hx=meeny(CGJD_7#uvsb z{>b!}*{LO)v&z2htLL0&>VJ8WDYtE6+QgdG8_$LIv`uE|HQ_N?zbVA2II$_>;6u|% z(od`tb_6qVNnF;nIh^P?J$gYJ%ZuU@X-D~7(u5Z9*q)P8x@K&kT4b`s#XCwM{QiXJ z{7;x?Jp94p^S_yzcOY7O>Zgba_6%M>1={?3`M zVP1Vxd*?E19nd_#RAK3gC2JOOZJizM8|&=l73>}DF21V8-9Mg-gNLVg0n?siY6^^+ z!NxN-);tqaVDzq=(a^9!)Z;)DN8aWQy=(XF+c(!X#^K1pix*DbyvX{{B2ZjeSvg-k zP=T94Pn|FMk=3EqKv$`jxJHzuB$lLFB^RXvDF!10BO_e{b6rD|5JLkiV^b?rLu~^C oD+2>_vsZE`8glbfGSez?YjDdBS~O8nor{6N)78&qol`;+04d8!rvLx| literal 1388 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYV)!5ir392`RoG{Y|Iz}IqIFJ$xObD0)K}>kk2Xf$}h z_sX%G-#&%a)Sun5>F)1S3lbNvySVYghc_{ri#u}|*Q^%pxgC6}p6QRC1R-ef#IRQ&&uWeCNsIrSmqfG`iP#<>EVEy~_GUBKH<8DYDvJSrA=1)0E45di84Y zzYAXm{Ii^$bT_xT_Mie!Z2LTMh5elYsgjpgH@Mr0sZH>>t3Sb6dREAR11T933Qn`h z6jWJ1eD!tl-RJJ_0~lhRl@o8v3A`#)J1D;@Nv>A-ap>O#JH;;O@t>5D=*zuxWRb?Y zZvTGO*I(}zHKj!-NVp|46iyVFv|YSN&->by70DS(mYq6vwLf`ETv7MT*A-Ex(q1wA zERou^?xQ_Z2e)1I^oW3#^cky|B7(KruRmu#Z18zob->~oGb)|3-OV*FnRIP>r#Rue zQc0+DFhil@gri*&v$h@G6??yQr-I`50(s7mIe!Hb7_wteZ=S=Q8VM@@JYD@<);T3K F0RWHF`zZhb diff --git a/toxygen/smileys/default/D83DDC47.png b/toxygen/smileys/default/D83DDC47.png index 029274eec1169c2e9e02f0890f5daf390c91339e..3fc07307a299c3703c6c5fc9551ca6bb6fc50d79 100644 GIT binary patch delta 1300 zcmcb|wUKLrWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!Dth|t~nRZmv6++SS#^W=(O7uWy3wDI?) z4R3Z#`Ezr}-@AK%UfKL$N!_QD%m3cn_j2!?FDI7$xx44b^=;2K_Wi!K>(7lHk5)8& zKECwttz9oRPxx_V%ag6s{@mH~``Xsu7uNkaxg6-AH+yG4**f+2?cG1ltonFh-kbWJ z(;lsEeY$z#gY~`7Hcxo4w(IG}zI)3XzhB;TV|FRfL*-h&dw~8ED+%%oW~djiQhl=a z;uXb}J~tmX96YUZe_?`xpww?*=rAxQdAqwXbg;^L06FX>p1!W^&)E66RQY0FHEJ<1 zFf~+#M3e+2mMat#<)>xlq$-qD7NjcFSL7BjFj(|X4UN4v*MOrYT{PB-g{jc;-s;NN zmbGs_Sqs1K|9b1&YD+2Jmd3^ffByV)-uE%<-jR3LHm7!K>f{Ap-rC~U+hb`K9lE`4 z_uXAu*Pa)wyRzlgYSH-3I~=zk)wx&XnX_hEcm7poW1d-wlUJ4A-g3isw(d

15Ri zpQ!pDvfpo9(hycUAl>h0pL3H{|Len(w|TDR-FdiPHY)4$R%}^eSI_!{{|L@8 zS|Ii4=&h8zsqZ6pCx^DjY)!s5UvU@T)>I9>08icJ3;REa#5pWqwfnfH$yCLZ3SHU7 zH<}8n`n)NGj#?BpVi!Fm)vZm)HIGJ&fgQm`tALKvWT6BYG>7a*le-c?0q{M+dDtu zX;V{AoBVuZr&V2lpXIcj-{nW8DpiNpq&;5!sdi7H$s84rHHixO| z(_tUWyfqv=0?h2Ju3?4N{DwE zzCmdZ^M*h2Mm-yMX-|4=V=?vM_Z_#jmQM?~u;f?3rjKDaWLGU@7pc!JJukh~qQz=I zpOC{7ha6dT&4zaVJ)2cdFPt>}2RCcc6NZNyo^M!aa5t|<$fn0Afl2MtA@wx*MDrO+ ztZFJL1qCkuvTpHzRg2f%5xm#7YUj6f<%zHTiojvr}Tz!mgRs2r$+$I!32jD<%pC?l6{kGgVe1pkeVQ<&{i+=loVM)z|3N z7^pF1`tVB}PITP|be3v~YeY#(Vo9o1a#1RfVlXl=GSW3L*EKW=F*L9;HnlP})iyA& zGB7w?d5#xFLvDUbW?Cg~4VzTTHUc$Bf@}!RPb(=;EShK_!NtJf>FVdQ&MBb@0QbsM A{r~^~ literal 1374 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYV)!5ir392`RoG{Y|Iz}IqIFJ$xObD0)K}>kk2Xf$Qcf+svx(KwmC{E2xi<^;FoOe2R?_{CnvjZ(8lg-kzxToVh$dg z7jg*5uuFJ;^^jF>SgUpmXT-&s2)$hMDb{Yo?{@pDoGCRiN z?3`S=I8cGZ=&44-uFv~UzO=k0|L4|iE}=q^x2G()E+oIPlI~;h6F40Hvcw_$R>R}4 z#69bOJ5{J{_1GA<;?A9KauU1vcHLo%6$+f@I92qw@~-1MeJ6c%cxv_CrSeASyQ`+W zTee$iEaH&w;tAT!xAl?pwC%zZX7^6N^t4jAcZZ12lFw@4=VBQbYPMFbtXQ<=h_Z{U zvaqO*@(e$jp6AWF&b#eRxT>YwV)xn?Sc}iBp1r_BeSYtUgZUrrnYzC4WqH4OX3u%$ m-KU$!H($>C=lLMGfsx^g&*u1hWly9)C7!3NpUXO@geCy4zUPVn diff --git a/toxygen/smileys/default/D83DDC48.png b/toxygen/smileys/default/D83DDC48.png index 7a68d8c878a4a6b02c46e21185528e338e716e5a..c961655f6d8bf959a4eb5015ca2fe4260d62b3cd 100644 GIT binary patch delta 1274 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 Xbc1>=ZB_9)4-^0-B85=9i;?>;0OVWc literal 1339 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYV)!5ir392`RoG{Y|Iz}IqIFJ$xObD0)K}>kk2Xf$~hrY;e+XZTyH$2#?fr^)V(+UntoCj?7go68<5 zoa)-JV)4-my?Adiuk0ip#=hqlbMH8Ev>Lw(+v%Bbc-xxy4rX$T>hE8PIS`m+yZ+>= z;>AMkOI9g7*}N%a<%{1I$2{5>zLx4uEsEL|z_wmP!Rf%`jG1ay+poW4V_Ir&B=K!m zs8(B(!XyqSR{ssNH~lnY$lr9Wc=5dNZx4l^l5044phUL$__wD`X1C%_*Sr%sWsn@a zVdA^r{2J1|iN4D=Sjes4E2s3*WX0r(N7ed_dhWBYyWgt+VCm&`yXHP%?V4$5eRX~m zt0T`8%f4pA)EC?3W2#T{J4};ROS=8-zu)6_24;q+=-QXuAJXbT#hIt8pUXO@geCyr C*wz;S diff --git a/toxygen/smileys/default/D83DDC4A.png b/toxygen/smileys/default/D83DDC4A.png index 0026ab1284fb35af4651f92d44c386126959e26b..a4f5a838de725436abb048d5a1d12de5798a25bd 100644 GIT binary patch delta 1584 zcmZ`%X;4#F7=4MDfFc&OLs3USMFa#Qi>!slg2=v)xKzUumI@}pL<4W|VMI|jBa4X; zl}%+4Wye5J1XMsV2qciO2uLAp0TE<*LHatK>9jvO^UZh8`Q|%!=H7D?6h(?mHE06> z00;E9dLtkgf_KFOK-sO$GXIT;)(AZ2QLF?2XgC0vBmjUV8vsC81xrU4n}$>^BginNH2@hf1lod{S3kvJDx2Is^;4YrA;}~#_Xy3tVQTU$x>Iv zs))NbP`@_#Le|e)5>}0tr7ZBDE_GMH;@Y*r`fqK8G7)#8^7hyFMY6s+Sj=5|S2)*j z5ALa+eaTrCR4(&NWTNM?-kRAu_GES1YG>KkxA|~S&BE(ESx+@AtQvckI8l|h*!p<6 z{mE)qxwJlWFeheBTq`MqQf6P?o8e`SmnN+WDp$M8;O>gilK8Q*l*#J!$(oEMe#v}e zc3jbUhGH# z<8uK6i9=2GSq+k$Db>pj3E6C58uGqmi=x$5NWT-&2~bt3RPLyG&hX=vuJLKEY}ICD zMEXvpX{yTbt?}4Lr}0U)YW47#0dfK=z}dkLwT{*wLg9h)gB(c;4htu0P@~914SEC_0L+s!e4X!baEkr#b_>SGo?Y>8=&Ysk_g}mr zl>x{<^~BhwnuVX0jo-Fo*``Kb!W zCj}Kw+F5Y#r^5I*h(VOF; z@!D~uPpF~W6?<)8-7t&W7{`RZp$#;AkI9KRF6{*Q9fn-jjd5GBWy1ouX$w^dUrNni zZ6BBGx9Rl3p+rJux2x`^XrNx=U;48AqR|I~9kh#@`TZx>#dM$Cz2=f6+&Je8lY-ZI zv^MmIK`Df-YPw_9&uZ1SO|%n^t#vds==w8$%M0J@YLpm6oXsMfm^abSy(>}p`Ovsa zASW+$i?)Kw`OpJrG=+?jBk>Rs6@T3QJaabI)7QN6pm#c}xy*xyPM?@)PqL7t>d;QM z<-KxdhldQMws1r5uZ}ci>E(>1?kH@w zD56*$_^edWP350DMv)J*QBwJ(?WSLQT2Moi+~)(W(M6-LzaPP`G()crQtpl>DocKe zR1UtbuE1unmUA7FRIX{1pIl(>VB4k5QZEv}j`L<}kIs@=yKltHA8!e2 z&Z0EGC#YN|SrwyO) zDJ~|3hIcy9-!znRU$^weUAdyXg&+?AH)r$*w8+vTEq1w51IHJ!dV)dTAG;Z;c%8X+}5rSY5y@qkb=U3qY%T+|8ZPSxoCJ>Q+pEtINH0} JJv~BT{0YHw8%zKI literal 1546 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY{Sitmp<`tJD<|U`X?9Bw)3(@Q3hF7nZb5UwyNq$jCetr%t1q5W|m*f{` zV)!5ir392`RoG{Y|Iz}IqIFJ$xObD0)K}>kk2Xf$>#d#KSf14g({q*Y8tF=LU8AY0T z`i@;_6-b!Y$~^PT+-E{3+Ybi+KmN|!cf-wHTgvutt77B1%yLY3!I=ZI6CT;PKYP#o zJq8#v{vOw3-IVuF|cK6$RrE;ahbKx zdsg@*J+~-2y>?=n=cCuNH>>>fE*4^Oc*bJkSF^~OscWmj&Z~?J?2#h7O!g9aJex>rH!TI zun(JNS;B?ajYo98N_|hf@|W}P-Te6{4h82WaNqy1sami1$nU~gHVJ+&cZQa&*O=nq zD=p&M8KrgERCq#HLG4G?Uq;^MPjqs5CEde$58qk6<<{Cw;p}a?e1UFeT#B+ktY3Wk zxMF|9cAo7!&c_=})lZ*g(p8n1{=PI&gkODW0QcHo@&OiVb=(FF?<`oZod`{v11g<8 MUHx3vIVCg!02Yrn`~Uy| diff --git a/toxygen/smileys/default/D83DDC4B.png b/toxygen/smileys/default/D83DDC4B.png index 87d5bfe457f42676c749f459852db600f87632fa..66590dde584e74d636f8c005af4fe5758d8d9f82 100644 GIT binary patch literal 1764 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 MG!}=}$WuQ32cR;6`~Uy| diff --git a/toxygen/smileys/default/D83DDC4C.png b/toxygen/smileys/default/D83DDC4C.png index 60b7abcee179abd5bdfa7f031af37b87b32ab774..5445e2ff3934a310ad089a23e662c0047dfc11ef 100644 GIT binary patch delta 1577 zcmZ8hdof zJEp;`hNOp)@fb56V?FYImG>~Rzt$gncF(!zdq1Dg{eHgR?>+aN`*wT#_M|-oH~>H! zxx)_HBz#P)O#moQ{7&p932}7~OIvFIA`SsSi~(Q`l8EB~grWeLbOivN0e}kbQO&PL z0KgJ$&STA+W1OMIOAXutP~`mX!lih&-S^p2+Npk9pp)t^%)2{O6i3+N-1|PloZkdl zx42}F4yr#Sb|eQt$J4A#2ty^>ll;0eLV9W8Tlp<$y2O{TcAIe&`k>;uYSP2?+bP0r3W_?Vw}4k-28_AnH}oH&kXIM z-4;9wgY=IH-%5;CC66=Hh6_kjwV4yu>4Q&+qa|^(_1WXl1UbJga0>e#g?)aLyU<)H z>Z)AnsF-GFiv-NEm#LFA=~K+KkrMJ~X~Jw>)^c0fYG>t_##}M4YO$qgy{}f>M_{dY zRfz;ISNV*M{<^j9>Y4g%Q5R#8Q~0GZXT7&}jn5dO#|{_8F1MA=G(2AARf+kG>DtU0 zR@SxR7IDuFHh+QwYc`}yT3jr9#*@4(E&E;bFc zwZ@w11G7t}sOm}4#;1ZK6_IQrR1@Zou`r8q=Q`blh&0~H+FW{6T0u@rg7u59J^ z?(x?Oj$us%j1f{~1Mfiwj!#LY_uxBC)tam?CS&`}!r9px6ktDa)R$7wc{2KzbXrQQ zQTbeu=KdcU`2%u_W5G^hT%wz#=V5vw&DrCeHLhjZew#^8@}V%j3s|9lYOFO zM`#@RP);~i^}7B0S-TO<)AX}lYyJ8*IKBTr)NP!(F|G$kUQCmzQrn$!@U( z&ioAfeHT0ij6OUj&PvoqHVwp!TPhDs-K)ETdlnJN3C?acG3BR>>M$<8QCXU~tiD%r zYA6n?d1^voZgbZf{PbJ$FU6HEm3hOQy&k!(I zV;b{34aN^$C(QcRr=1WMhw!z#{1;cy8E{=Xq~Oo! z+>{p-`rdeXKwHwHN}5$kWgJ_P++D|2qcM9F?hvALIbAGQVI}V(Loz8=%}3BLDyZ literal 1607 zcmbVMdr;GM9PbKsDxi2UM42wei4UZ0(s$PC`iQeiJ1h)Qwn=FNjnJf|!B*TvIETXd z-~d6uQ)G(DlsU(=&Z{C*PpP8S9rHar?-bo8C?ec+uoS7=AD(}hOY(c<^Z9%qzwb9i z@%m3fMo%2gVzENB^EC!$4D~%DM=#&Jb{Hv)r8JXi7@)NpGcW^Y zG_TA)hsjy2fOI@DnNHTlAtutwHTp1Ir`5)=SuFWnr_E@}#AqN5%fJZ**mdj#2;gP~ zxLBm)>uhSwg3r&Tu!LNFqA54iBr}6^=Kyji!U$L~+6XwUS%e*ND!?IKgqi!cc_1(Z zp)(cWOQ(``@qn77FhIl=#G3dJ1c=34NGKAEq1gb;hhQF`xy7-N5D|$HSON@RAY+X( zry~ZX55URkPJd$ z0Sbvw7=<86Iu8~|r2@W6q)~}rA?nw79apMS3mK$PqCu4qqoNcEMM{{@M+H(?C{T&~ zTrFXzjf4sF+r=5X!(71|xrmy=j5J9ll4O>@0^%(sP1-G_4NxbDff*H8NO(N>HB%Fa#-KHD4x#B~1K$r9>nHhq>mr$r+Dv zhUY7e{}qdWi>W~0^i}CIi&xEq5lqKWOlw5#p2TLc*bc2mndrP;ZdhmuTNLgV^wd|K zH%FH(D_ebe^FOIE1^J+O>DshguF`^6Ki>pKpuj~d??h@Otf z@v43*sSmT333o(S;n7#B9*_8LYf`4WyTk41l5Jb{X!q^k-~UFDR$A$AIg!adux4-fI~IrEO1IOyt7n z_LgIt!rPC#n>+~l5OFU3NTV;W&xKRbk*V*2BQb2RE*%UNX1#!1SJ! znrhF3pwK)=*R@kOZ>-**r(O}+7d-8mp@PG~Cd_CZ(e@W-^106ka+h4ZzXsBU{-N%$ z6;<{cvR#F>dYVVh$c{cy?JB-FK^}ae#9MLJyN=2jAG>y9b7N=oXtt|hr}D$Sk2|>c zE9yqh`7xxi5U)zvTzvHDemwGHukg~gur1!ynur8bU3iwOlL+>7p4|9F#nQw1w_X%4 z$PV~5?r(KSeq%-YC~n-{J7*)+S2O!j>mGg4$ulOQXP{(P<&>xtwmaFiOu+dwqv2ph zWOq()`~h!SSk&XI__uX=*^(n0ib8aqoB(k_&?wHK*4E6m@@uj)d#bkd2YopvSbISS zIKK{%w!G7RhgBSUu(hVPz8yH)v=wT)JpZ%vjS9Z7SM{W)W%|?ByXEH8aTc;+&4Svd z?fFj^#9e{+*^?&@-T-GV7b_xntdcw^k&i13J%Xj2YDi4-oPH6r?Nng<`N1*$+-BBn W_LE(=r?GXuU%M97YZ_Il1^)tn#A9Xv diff --git a/toxygen/smileys/default/D83DDC4D.png b/toxygen/smileys/default/D83DDC4D.png index 2f816aa5d020e3f2acea0495a64513268d6a6f49..3a8e5121e17a01b237b1ecde6185465e09911d15 100644 GIT binary patch delta 1528 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 literal 1590 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY>#D(QE96SFe?GQEFmIeo;t%ehw@J1Z3ow@F_Whh{dE?sGk0O-r{`KhaEUk4ud&})F^XZSudo*}CH%2r)wzzrGH~9XYhSV3P z74N){8_nPLmCvuSX=ZiB*UJYdFYgzQYY5Y6Qk7weWb5ookZ_2OJhooGIOC;{vtQ|f zjXG;;XX|#aJ^7cBQE>7}N5`(lrxshjeMwN?<0iXn*}-zz95JpH7ujp>^Lu>B%CmC{ zoze1PVnx6oewlW$HHpckk|!G#cd;E5oF`NfP}F^Q=b6{1O2hqnKeY8<%`SI!{G=zs~&g-aBXR`QGpM^Ibmoo^$W_o|_vO z;H?KUg8=~aFeg2OAgljn>U;%hTl?+7kkLAi@<#!1FJD)U+XVTRQ741^0iaj|KxF{1 z3Wca207!xZFoOjE!2!USRMvjl9RLkcpkE03GaY<}2BiO5`WR=|vux(F5D@skGAJ^X zM|fRGl2@h;voHR40fYyc#248SuM6S_a&Zt5a{sJ>QnJ!yDJK!)zPWWp!oH~B)1aW@ zHci2&LjaOVUM3WWz!*1Ww2%aWK_+3kh5ct1(i7EJ-Z$k#U?4a0#ntdHAP+xLoj%>n zh8CHup^e;(`_NV(ElW{$SIu{EJG1RYE8^RuMTrYfxU(I_YI&!suWq)ZWclSUZ+MyG6={vTizcx9SG@H2FKE9T{F=%&%+pa7^TZX&y|Z~UkSVZ@k= zfm_b$w2v_2%GIhzFtaTl(QD}+?~he5T zdGuG+r%6!90F6jrZ%>WSn)VZrvzd(XM{j<=c^hn>>$;y29stcuN=O(v2$y0>N{lC7 zh#^>#FOvwCsj(ygT$lx+DE2s9r&D#!9i^?MyyRY(A{JuyMNit@jt7OpBXzF$<)*s;xltj6kd4?A2@kz z9Q)D0?UPhk<22u*?@j8y}*}X zvc8%{hwr=$8}HN$k}=E!##WEsUps?GA-Yx1O1rPM=1FPpV)OYN#^RhC*qfLju`yZ9 zZl>zbR@_4B*c!ovPBy^~CUjA>+*%XYjf(MX*_n11tzZ9jO~!Rsjn@351`)^yNxWhG z;9M=_VT#LKgMeFhJz9?&u2bc&=SwDaAc zqx*HOk$HsGL)7Cy6_`|t>B&N$KU)Fkx7Gvn^yoYBtA||JPE*#tw zzvEimZVO8p)SW9BPgID>a1<7cjU-}m1fWTD(oWu{9iV+E&LZNNMMh-~wNl?Lt1*Yq zscU1D@(cq#fAlJ(p7$EgVa>3r{#-oN9|z8RECw!R9ML~0sh9lwo^ zjb8XLrwL0c&C)WlKiHi{SzER7E5j4?c46_&_gYHb34;9Zwp)czSwq4hu}oCLG61!( zV@jrjEA-~Telj|YjK`4)h^RyYWWZ7Q(WCZoXZs_LAxGc{hi?&%PT#=c2sr$hn`GPn z5Mtx;7l@btnsAC3a~~p@e%T>7o=8r?B@zG%A0J7u#Khvx6M_gh{N)5O0s2?~z@P&> J?|NYA{{ewXL6HCe literal 1701 zcmbVNX;2eq7!DqQFcDBIkr7!JM06m@X0w50l4>9u2_*qSOaX@9nQgJPg z88&?0h)Y?l=_!ORj*e4@OZ23Xi`i_rR->6gvsls#ik z6FOZ6jyn~njs!wT3I`A_9H8ez5Fiq9At53XLH>Y%4+(gD<`xA&LJ1<02m*nL3uM$N zLyAPJP)=wuD;bzZ(`E^emz|x>&4#%omCA#}VzG@wAP8VA0xTO$G-eGjS-dA16u3oC z5oVeoO@NINOC~dE8OV4#egvagt)3z_StbI-gp6m!%shz8=NXN*bB$SBXf6J)8y~c` z=r)*fo)))|nUtQ%M~e3(n2FuTg=~h5Hc((>hcNxz5@}7k8%5jhc7$ za+6OO9A6#5|GS8m}2$wB!`%7GD?_ zySMb0`1Ab-fj-LdQeNo|ylr2>oRR(E(qVAvjcv26+ga`$r(X6&*CJSuaa?o4-?=-v zAot$vFMY2jwG2EW?p5}A9*_NQR5{W++{`VxIlSxlMRShqDZ8a=d@O68CGEYYjBLHT z>&4?^kw3+sPe`}}9CGVEo8|A2T0iIX#WFO#H-3$-%x<0UriNn8sl6h05*_F4b64V1 z6293TeRa;er1^@!7%M%rHUZ|Wzc1d?SI~2v?@0kt#BO!-X#VMi4y)9jw8Vazd?8Yk zwA+_+GbPdP3RS47Y;#`1+lp~?8J#;@o+S*Q9G=ao^!mah)^xYpmx z$ZPuBLvBAmwIZuv>9tEcO)fv4aSZnM`g-lU`~zWH{nLlQ@~_;Qgh+FYU)kLg3tO+e zKIaqBv8l?Tu_I_kt>^r0I*!+8TbxQyOJ!xvb-8&jc`MznM_jgV9T<38HGfC9Q&4@r z`V($LEZBd}h19fH<&HZ0O+)@*-@mz0GrO&msB5HweVW9hJ=eNMBkb&19Zh}}9>=I+ P+mAzqY82JMN%{W(`PzmO diff --git a/toxygen/smileys/default/D83DDC50.png b/toxygen/smileys/default/D83DDC50.png index da64391506240b4a0dc3747899bfb01eb9b1c4b2..8809893542cdad2564b98e6fa139b8a164bb6a3b 100644 GIT binary patch literal 1591 zcmZ`(dpHzW6u-3AE7eMw+GIksCU!hVC7<1q*R0sQ%A=UUUKY4-vwapu>jOF71s$e5F>qUo$LXK(gq+V4uCIE6*B_>4Gq97 z9stuE08}Y?EskaYNL@N}+Qn*fbF($luKk*0cY@~to5H(|f12nukQ>%@)8olCM?pTV zE!v?i+FpwJJ7L4MjOT0$)bu3z z@RA6FRW}89E(;6j&zQua+Qb+6G*R7cX!fq=)=(`&kVhL}hrX@31#O1w7%%UJ4{;Np zrTV`vjd@u>7uDZ}bmV^WXhX`|s`xjRH~wtRf$-J6NEif8{ZSUnPYW34CPIVpN15*% zQlRjvFk*;%t2dnl_tnQD!F)uuiIDbZUV@r|?BLghk$q_a0yYK0|Lpy&K&Xf5!DwU6 z=^vWXmPM^gZ<;^9y8r2A-Cx4m@rUUWUeWUKzoeqTG4vZdL!oxXN|K=o35&ojMp!7rz4iC*14Zzmp+P@638S* zkocpoBurj^wGD=s^0BkA1h-_=Y$1@P;_R(tr(}0199BFUL*oD-9TDZ?Ms*_4k(4m9 zKPiZaq()MR$cSJH0LPOvU9qeYv|Q`lp2+Ll<;2qwJ#X|}PfV>zdW;&pn8$|Pi0&53 zN=jXmD?;x{*@BR^nF}_5mzzKO^4v4WMU{Ne{GZY)zDY>4Xjkl&@Zyqq6VC_3`v+B` z9}9U=M`Lf;`_|X6Cluo=k82g3I#v}_3!X>bD*S*?UMo~H zts*JiEWq+_NXQn>RhqH1SC%!sHcr+RR`Lf zTFebZ+3H_GnouH|#L{cZZo#^V#)niY7>IXf9E`I-TSIv}S+-7?N*F13J=+= z8=8ys?rZ$!#nHHvlcM6R{)oM-unQ!CXt{Eg)xM!XMV)V1J%s7e9!=ZucKQmG*BF95 znB6JxS~lS_r~YI@;C8=RNQ4=WFX~>vtl5I;^MJ zrHj(3?a?Q7bF1DkJ0hc)uW@i~a}61U8-+?0YFdI5rkIpOKR> zKhI+2PR&j&bdE_T^K)2t(-zX%nfViQ)54CKeW`hOvegkTsoCcc^FleQo2_Fgjuhxd z!nsEt*R+i>&@j|ZD)_}zS365xBW9MmT}sLrzNQ|H@S8}i3EaV9s+Aknk3c1w`i2o9 z1BPfrLp}5{Jp*GG1GK3T#uQ_WLZeO5XyOjuqi-C7$$ljN$p3dxalX3ssqfob!I|t& mr4z!40P9EgAtG_XgaD!wk>D2@+DnALIRH2-2TQI6{`$Xk59^%( literal 1564 zcmbVMX>b!|7!Kj6ltIB(W<;>tWk3XyJE3`aN|D3nn*r9po%{BUP>zw3G4 z=e@rD+&X*a$mFrfGMQ|oIft}Kf$8eSKv0x^#-5ks`V$n-AW!2o7I z?PhEY?TVCkGSg+U!6mG{KrFE25>DQ)q~jQ6*dLJCGTHR3aDaAt84++WZkE%5u_Hf$ z0PE6$`IrT^1dNP_&4~(3UUatI8TC4~E--5bFg;922K0pmd^iu`Yr_veW8ykO^fU!nu79$w^w9zFmV^Drd>lWvS4T@B}t*(YT(d9In%+rt;z^JaNd=A_P0ZDi4id+j7- zpW4!NaZhn?QlY$plP}59kLpWmf3K+Balm?H^)g#NHzl>Y+F+e>T-p8hlI6?m4p=sY zqU$zpaH~{Xhue#P=sY&KYT@n6A79cw{5@t{UfT+o+R9JdR2Gk4QC?D2-t%Yc-L_Ms zy{EvQMRIX&<~85Tp1H|Db5XX+Sg1+cv}{VlQSQM|w;ep!duNEXY1oz%_`M|G@XDR9 z)jvG;=BnHUO<&ijBc_V+m&09uJZL`bz)Ih##olkxZf;xFjemdP@Z!?66_~eRsyL*< zd9SxEw+MLn==2@ax-Z=)m9fUCcH90hw&Z8dJm4%|AAPkAo-;#CU)8bDCqxbw?m)f! zGd1SN9l8_O?oH~#8WsfbH9Kn8`mCwVt5UppHMVnd!}fEf*^M`Y>zVoL-Cw;C{WP3% zenV%^2d=R_PsYX4W`Q^-9y;h*dFAj8-;f>aQh<$TC)RhdT}LOBlY79as+_I9aWxL2 z`j$npFkBS8RlAu&y4z3BQ%%b}*;kQ$=8}HR#A{_2m(FW(HbvzbolkDgpZxfc{OnX@ z+t$>^D(|kCHV^X6J7eYNcp3x4L26owcOAD>%M!FFeVA? bRkX?mH4n~_*VUbm|F6x|Y;v#R!-{_Z^Jht2 diff --git a/toxygen/smileys/default/D83DDC51.png b/toxygen/smileys/default/D83DDC51.png index 0eeaeec65ac979442538dbff019264244776bb0a..d05d576c9f95ea8bd686b21eba058a2802361d76 100644 GIT binary patch literal 1832 zcmaKtc~Dc=7ssCIxstk^nb~=He*&K=TM*%<;TVsW&0>)+$7`#6KB*6iIbqN4I zfm5uv0B{xo05jnLfGh+6D7~0>^e_M@?Zo*JJh}_vT?M;4Ip%_&VC&s|UDqwX&~Xh0 zva#BpZ~6r=c_Y6i#|Q*01o`5CX-!#Bkd6GW{nlLL#w%OF@oT3k%dqjvw#JNYpwpba z{duOrJpW!9_RxH@$Kwm?^~tIm)G<}QrY2GO|MRXR98@mriWsf-0M#x`HHvG!K%J7O z0gD5)rQx`#XSfZiGc8A_8-izA!a!9Gm-Qt)_x?M!pI$SUN0SGBwFM=0a!mgC%~AZw z;Y~exZJf0>!Fp340qPw7Ewrb=Xyh)ex6oL`B`t}PK8_@h+=KNL8V^4@A?KI86S79? zPBmql%!!IV_NS~1TR#n^&xvk~)Pj@7Gu`LG&y=+VEf2P@3=0>=xv~b#pB2Te_^|1+ zqS=;!g^B7Fk#KpSeY(MKz6o6$5>|x^nW(U-aQ6}T1POePJ#_V)ZNW*nPT)b6VBj}h zVYgiN@ZEf$qWp>-;U!*h$rFt5ru9+>M$wL*s>E{Ou&XfG$mS)&>igmh;GBA z^==Ab`1(-D`p~h(p6|x04hsuR8!l>q$Gg71-dR^C1A7R=z+*qR8vx{f$jo>lNLgcF z%Fb1h>-D$vzicy+yQ~^tyJNQYjht3AJ6{klpIlv)%H=C-EAmIHOtl-U{T}8iPsiuy z#-3#$H|1Ee#NbYwNWNa^8noJ}Q80ioy!|~OQi!(tZslE}s|8?tCM6L<72SOis-jlk#?lpQLrqze;NjvYNmWg~A6GmCE)*PiK z55+iEU(_&PiRj* z%+{=B?Fp2=>8?9WAnhLfQ4=*;MGHhV@yx5nhcL1#gZ%Ij+>FjI`t%ge{W0Z^I<0Z% zj|6j|1nQ)DhW)_g$Lj1YLadrq@Nk2RsUKrAdY3}xxn8!{3 zE#yx}=9j%XyjM%JTo3+oUz8E$>T!#oUA3QcC9x;a)qVO@Qm=i^*-Dc08w(k|8DOS| zXb95PNGNULtuV9iH#N%%Zgz!woU#4Q7RN}Er9}x+iH<#edhV@kX4Zl9HumkiU-}&5`XyF@r3%FdLnl94p+kauSNTYS^asD9 z{Rz+1F8-O8o(-?DJ;`MpZ?BoM>F(ZcYg=)w^2Af>|9A=`mowZtA~Bp;Ra>1%eB=Yuu~ z1QLM|B$Q15qac<>j;1pIz2Na)<4~|*`{y?V(x{9?Vmt*vk!d7~nRhHPl7goY$;`M8 R3V3}1fVT%0UFja4_BYb>TwnkI literal 1711 zcmbVNX;2eq7|!4UGJ+z9(BiNzHd4s7n*)*(G085FmQW0F#88eUSs;*PL$Vnc=&hCE4KF{+W-}}z4 zNr;c}aR0!aMx%MiV-Y1a&UQX-)2VmR@F+$N0VJA4CK8#X4Kw33iI&K~0l5Lo!j(9t zb>yALBWbiLnL1SxnWTslX$S)gb7EL_gNb6(XpvEN6Q;?RLM<4z2#BN^I#bG2dJJVSc!Esj?)`?GK*}4@=Vl#sD3~50G*$y{B;|-0q`t7UI;{wWgb)gI zQHYPiCE2Nk)N%?%45FF=f|4+`? zlrwB+ar~!PCbp;wbWSHrpIS^d4{oG7#!R(_JVBgAqq+PpM zW4LBZbzY!#Pfgt3hAUsJ_LWyuDCHrmt{xEh8cK68AAdx0)tpo!Z*h_RC1ksBq-gtZOB^rjBxVM|pQ~Sl;qGmz!IC za+5ocF#R|RZR|lqc55`rZwo*nxv$XP~p6-{7K%bkn zqQI7G6?C9T&~>||UT{1WTPAOR{*YA83O&&CJPckkKg`{Fx{$kHMGtyRs4xv;VC+0SJQ6Q=GhUUzHy9ZM*Bn7 z%@(#IBVkrowL=MQs_oKV9ib$W=@fgjN$UKp{nuIRoVDKXeV+GS-}AlSAJ4bG9B)rI zt&RE{0YHo7PVmKW+xpT_!Lo_PRtg3iqMSXP0a_)SKGRjP+K}Py>j9982jHIt_=L6i zZvhe!fOj+i2PuFqr}DOsBY<*-_X#TT>({TICR?T+*1)%b5iFQT4TXMIB}}_ZT&K%C zLJ`ec6kv+=V_k7sR1=IOd75Q<<1eLnV4O|FOTw&Wd@9B-rcq*Ca1Fw+gyZ{bGq8Lh zjq4QaYHTOjp~q74HQ$b9Y+E`7fqbBt>i`jex;~V4*ua;#2P0T`n{F#(kwFN5-N)Bl zCoA09)o-u6v+i!E_2&PKoNz};m`yX!=|LX#c|CWuDY+*?#3dU0h}7>F zqjz%yCgmB5&Vu*%i{?5D-`p)2syY2r0^zD4KpNx9A>atvN`+PK?c%UjflqI7_|T7$ zPo%Uik>8aw#~ZQ(T7`iZ63D?pI&cxH<>B{g*jM;o^{FQ=2|T3bftJbJWa-o!IW&Qv zHR@>aO}r9Rz~Mq&>6M7lZt08ea#3v{$N>7GzlwKN5G0^l+oq_&ZvZ0z?EpmpEP%@Z zlK^u7J=;lpc~*Ci2N^Ol zGy(hoPzjI?nD=boy%A#`b>aDXuG<+jBCdV-^5ye$iqm*0c_my;@8d^1LCxUPKc6jp z9QmRu;aYtDP_*)g=G`YMs^ZnTU3kf}&z)tmY~vQ_==BuZGuKpQ*L1g`e^>A}bd5arAq4)cF=Z?kd?jwRFr0R=3-)?X}MI8B-mw zI$hZ{)jp9(uHR?$I{)2$Qjf{+Jd@mmhGYvlyRsW!y!0=3AZ2xxwifM9Uha?<_NyCT z{Zgh~$I!~pHr&rS>+1Z&iKmgT+*C}ItBwyWj|Hj?c9-ZlMU6^&!&$37>{(gnk=gg$ zQgX%~-0Y{i!|el3g^}<;AqzOPbMz%9vMokO~s9$!WT>vYOhlFsJ=dx&d}cTfNd-`26a!8PZplYiSUg-9uu5;EYklvPDe=fJRO$qO!wiEqJvV-9nuk=K>ZRu;zGZ7 zWl$2xGIJea$jLKdZczbAUTk*w*9|FnwR1T|c&~DYxKDZI^2jIUvW5rM3LEy9%yXh$ zmO-~!E4d{ysvdR3|7uYC>9zCX3R}yuw!{ht72(4rgCj}rtFA|V_<2ozOH7Ova%yh3 zL)z5a*4O)=U2wc8>*??4sAkiWKlMLA0IU&fYYW8A!s-y!3UN4S<8a8<3_%Ix~gahhm>4 N0Ey^HXm+7x{s#>pGFkut literal 1816 zcmbVNX;2eq7!D%fi8vqvqO1WC6tc;NY>pZPvI(SeB_O8=Az8=@$;Qn>0*(iWQ_-q; zfft2xD0ri`c7%Eqk5XC%w0Hqt9l;xnip7I&6l{Mu{^-u`e&74;`#kSGTNoK0JbsMJ z7#fW>UK)bPsnyQ>jIyTQZl*y6wK$XFI5G-PBMk}yr3I?+R1}bE6zQlORj6_nH=%QA zG|MzBI*yDB3l%7F4MSnpVHhr%rOcm0u4$k0HY>Ts}~rB^bfoOYHvOU>A(jSGDArJ z=u})-Bp|{G6yPw}zDg#G1;8+a<;Q_xmJa|iSrEviZrGRQC*Z&Whzkr|bjli`N)yNt z$&f8d64KQqsS|*p!C+t**bJOV2U&bR-^>9)zLbWqK1WL`jJ{gE`!EB7>XihhBQabH zm>CtRcor$7Q=WbdL8A)``$VkO4<(998E91KKo)}uYBc7!K4|MnIr^^~pVih!=jc#S zj_UC&LP^yl&3zb5cL5vsi(A3Cs^-@g)d{&1Uk&^dYS3|Ktp$ zoB_?v@t4C-$+ z?MTXQFAeRSm(8u8Qkfc%!C90)*~4*S?c_+A+qu_u@!6N`;DN1xmz{0fGuu_(X)8nElHho@Y;U}m^W~b04l?hm$UjYXD(9E) zJ%8`FDX(2~bAOy3?ai}#$n`JFukg3LyuUnns(5FEiwInphw`ud@sP}P)_;F)Z{eOSrP{mJI8fy_e&OU@M+&~aj?~q-C$>y~-M~zr zeq+R}Jl~^NT&24QoU9j>B?^yk~ zBGK{rjU zqw&d1|E(9_aro8Xnu5l-6IIUT1Nm~7+8;b~7hx|#7g&F12de9{Q)f(%deh98Oi5j@ zTP)sHG~-T+kE6o9Rl+OH3OU(SdFtiV6gOfTE95ab;!-nh#+FeJPrvYpAuQwFO{NE% zqkm~Qv||T-b-@&c>}vhmr2%up8Un{Lb^V39-D1)fURj?gc~z{Sn+BtbCvAAse<1nL z&6d`aW9Dx>cYfZ?(><<6aBWwo-{oabJo~tZ-^J7%hhkno&u)y)3Qw_-jx0tmWohH| z$9}F{x1nwPi)u+r+FM#do?Gzo+YY7+9ZrU#MxoDc+5Nd^z5!(U-K!2xYA(s{+L=@O z(Z8~&fJwNzC>|LM5MP7DS3vsOPu&_JX Z!kPxbbMkCwXY4fptEA#^14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>(43Wx-}yn;ZL{9rOCsj?)yxj3siJE_ve&d1f>H!r2SET^?Rw=FxRYI5h&C9}3K zo3njZ|JwA}>WsMR*%Q_+pSxpX`=X?<%D&nSy*2A}H7%0Es~s!?g4|OLv~3KvZ5=HG zfo?vf-J=Qgf?P?EA1D~sJ`sfGj={MRo2rM5sMfYm};v+B1(c1%M}WW^3yVNQWZ)n3sMy- zatjz3EPAJgI!?Q7z;U;@OU{vvVyzK{her8EcdKI+Q`{FxS)@^6QEBZNIBpBEr$(mg^$3$@l1K8|9w` z%r|w*USzXRzqRGMlgvS0|A`^@Tz4qXQ|e|2ouHr6z2Ztm+Z#U3#HIAU7Ug_=Pn|)j zsPKxq#K*QR4FbQ;2P&{k;c88Yp1e|&=isB2+glxO6bMKM`X=>n8p?Yn|q~YsrH@ar`hdiyIs9zX;M&dy+AQ;aqeQDU8~a4&-;E@W9fU*H^bd9H1kx1 zZ(xP!+Tg`&E^eCOZDaaHA-K!JjiJ;P~-g5Z-Z|6zBcvtl3MTt35C;4}KFTXr7 zpunToWKxY*p;gF1$$%YKBe!2j;;5|U6>@mtkaO14=)ptQ^A{&Qy>N2&-&Q88qK25n z={zP&O#V-9kNoD&snDa&&*UOtuz_Q{s-=Ze{pL5v>lVK^yR-7XT)Jidb4RD^>Su4} z%?_?$jb!KD7y89~*0yWL6K9oqz_eNK>Eak7aXC3bLDpdN2BSla z+T6y>($?%w4U^|~H_jGh{g5D-;KUfgQt|Z3lOI1=Sy)+r{A}=vV^g%OWOY?dO-vLG zH7(_}6l4pu^wrIkjWsm1t!2)2wk%{*<5W&g77h;d3>HpKHs(}gQ`5{W=Ju9UQFVdQ&MBb@0Eceug8%>k literal 1325 zcmbVMZA=?w9KVWeGKs{Mxd!vKvT`M&d8fZcjhjLsW-~zon?hacj zAwaUpd?ho>^n;=fV$=j=n^A-emcasv8nc)z+lM(cW=213gfPWa>T}SdAILswiOH=1 z4aWI)&;a;YZ}&2&M3B5TF%s3Ip;}sy<5u3Vu_ogRL?cLLRZ`)FPN1W$pk0)_*sYm4 z3>9NutjQU&g%lQah&3q{G^XkzLaI~n#IUNvXl0Uy1aYAAXfoa`(yZSm4@ZfxTi zx@Dqwda-R$(NGv=Wfh=KtGz<75d=z6R>I+=DB>VW+6WT2!JDce9JG_7Nf(;EFz8K< zwb2b+AnOaRyjX{>D>RNL5(#UpW=R8i4I zSwam)zE$qgy%?IWWxK-5nW*Pz_zLim)hr7bgonaUkhI&Wph>)|eQhNrIp_w~q=i9@Y;R zKGsGBNY?EnSsNK}IddGlVL2yBc>)d>NBUtU{G{8)`8;-pb5acJXC3|=HYjO2F9{&$ zSA>4ESn94=npFX>%W6cHyK)^6?vQm^>yQ**;lc~yka^U!&uX@V#x_bH5-v`d+TfQ`{ zK8&sZf^975?k(7}RK4DJ+HHCQzruvW z!-ECCeu9tA5$TO@7Z68FY0b$qvo9Q*c$3I0!k30J=E)c5j!m}|mz%%4XLWq7ebP3Y zO256bcyZJ``Xfn}9XeYw*7Cs8(CLr=Ap24m=WnMk6n&l^zu(2Erw0Bs+bsj`dg=Py zSY~5w@6;7}lq{?K;@y=?@7dq@{;U4YJAZ4JN+$Oo>f21ezw*=i&*qoknmGU0&0BK# gx!Fbcjp+x`d1R`qfBHyyN;Q69L4O@L;cFTC2jGp-vj6}9 diff --git a/toxygen/smileys/default/D83DDC54.png b/toxygen/smileys/default/D83DDC54.png index fa76d908e2b9b737e4102fbf95a0dffe2ad688b3..b095f9cbca1e8ee1c689f6857ec9e1be68c6ec3b 100644 GIT binary patch delta 1564 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 diff --git a/toxygen/smileys/default/D83DDC55.png b/toxygen/smileys/default/D83DDC55.png index 23d1ebce769f39d8ca47684cc0bc2450c4d0822e..84a5d628f012618f46db6078ee9c4e0192aabfe1 100644 GIT binary patch delta 1632 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 diff --git a/toxygen/smileys/default/D83DDC56.png b/toxygen/smileys/default/D83DDC56.png index 3d3656b8a41c906daed9e1795a482a0c6cf1b2db..6e6cdf4d577bb6a2ca9cc17d3e7232e6a3101c48 100644 GIT binary patch delta 1313 zcmV++1>XAB3d{ zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0O3$fR7L;)|1(F4FhYSYKzuDd zcPu`5FhYPbNr^K?j59}zGDU|lLxL|qdn-J2Ge?IrM~E#ydVe%ajyzeVGfIy-Q=PlJ zyD&wGGe?a)SEDpbjW$l0GD(m-RG>IgoiRs_HB6K=OOiE8j4(rkGD(j&PnkANmoY|- zIaHr7LV_Q2 zLxnIxgFRWLFn>dYKU$|cQ=U0gpgUHgFG7PcM1?(Cr#@S!FF}AWLV+_%i!45PEIxNM zNs9mf|ID;cQ2+n{5Oh*bQvmMCqJERU?BlS2OcL;#2d9Y_EG z00(qQO+^Rh2M!7)46@|l1^@s8etJ|`bW&k=AaHVTW`AjHWgv8UaAhEPZEyepIE|H6 zTaw%`2>j<1If4ubq~mxj>n~OL%<j9#9G&24E`HOzUQ?!t5(!F@H z#l#+#T{#(BHn(cKH0HfO9*@q9{c0LJ*(Ga}q|K8Gbc;+svsZa>E zIxs9K#(0|T3e z34ie%w2d-pRUOQphhSMpHwBwOM=@0?${0;icoof4qO3t#>`TlFILI`%I9d$6zIt+l zd<$FzO=}no#tQhVFv(a4YM|qwE`x>;VJy#e7SRRlJ^|A}4nj&$(jsyB4Y__GpMd2I zO#g=r&|r5)aWonyNATS?X>_>~5-zDelz(K*Qrr%(hbHBcXWc@ICggfF5JNR2b8B&BqdgKoA7bF-H_IVL&l2JBlC%1QpEs|373Xm-S-b`c-u`0Du^Y#^Q-& zN`!ADxm2!HYl_66-e|Vec1PCkJq|vBz{W!e#}AzT!&!Mf2EKb?AQY}QxIc=* zyF39isTLC@+k86!001R)MObuXVRU6WV{&C-bY%cCFfuYNFgGnRG*mG#Ix{soH8d+Q zFgh?WWe&x;0000bbVXQnWMOn=I&E)cX=Zr5D=;uR XFfbHTeFc#rCkg-nNkvXXu0mjf3&I!0 literal 1366 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLoy6KF3~uQOh~R?bDKi6!|(A^G_^uoMuGkzbNuoRMFk z;2dnGpb?&#my%yztO<%9h;Lm|i!#enQ{0O3a}~hhYn6$`eoG@`BS#lgXJ=yfD>Bh<|1H-LbO?CaylRrGpYQXM;p6S5qh+iG06J86 zvLB)`>bY_mA}y_T_aI6m+}X<+pgu?YOXzaM#~ya~^8!f30dUU%{DXwJ(*Vcu0Iz8P zq!NI&(PeGEKLeol`t0(jeEaqdV#C#gcS0@CCc=dRsLY3yH<+I^frT}oD#eHL@WgwszA{S4HqFl9eS>VsuOx!ATJflb3xt+Hyhzb9dy=1 zMIPj&LYo8{FF~sWnnh4s34d1x6hNwNb4=xe%{C_S=X$r86NKHc5*=}6u!$wMrfK76D*xqCzEDou?Gv< z$7g27+Y5@m57>@iP=`F-$iQ1}>W(;?Y^oPU^M&RrEemu&unGl$PE7O)X8##jUDlYWS1BXS0UN+&`|EiEIMe*StO8PaBKE zF*}Ld_RdmT#OZpgDAJ65=~2P-c^u!N$MtVin0|5KSmZg+X-f=7@R&!WKQcJWaeW`R z?9oqg)AgyI%*RK~vM183-K|(Zj_d8tqjIejYa6KkkxH{5v#I-bx>=te@J^IkACYz6 zY7IV+bfuq%-c}eeVk074w!L?>Hi5s{Jv?D&hodB6kY}pMSI(}>!*`z2bRyeiFOn%8 zX4cpsnvh+lYBL<#)0hW8}cfhogDk zzwWYF?ZhF-Lz9oceI6Wqbmom8rtQOG_{*DQw-~9>jJ@oqZmetkejboppQ-cZ?eOA-7l!kgu2sQ|g}vs=GwuR^j?)kLtlfpJ=MUURYKrGZjy)vK)W?q~*?E znD-}5%(-_&(yhCNC|zx3s&MMo^WH^2blZLI^8SzwhW<@K4XEL}AFfG~T{3H7PGk)2Kwt zZEAmex2~dgzZ`?o^9|X;T7U4DEy)BuDL281<`eB65tu-}M09s@v9%)MMt9f{caog_ z(3aT3l7ctqF~t{6i%Ngkq_ZZM8gDmzTcNx?G@u$RUaqAW*koz7ar@eu8nH-wkbu0e zk67cW`N!P|BMUYqkWCL|Gf0PH7>EK}ysfP@-oct+?@z#!?6#8Z9XH|eBs^Xbd+FtW z08AGBNEqk;0j)MBK?uM|y~7?>7&|^Rh5^oW)*%L#$_x!>_%TB1oMQtFc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLGjMjE=kNwPKDW<3A7ig*9fm(E9aur#FG4?ko^1{SPBTp$S=t+&d4uN za1J(8&Atv#|@%<%Wii zmTqR2PKK6lE~duD29~Z$P`xSSgqc3jG5Vmyfs|NaLckOVV#1R?kOR*=sd>O0Qv}Q! zHy!ib85kH}d%8G=RNU&Bbk<)uP~>R+;VGHcUQ6YB5;TuZW#rlvn3VMB)L!fGcQ+-K zwQo*o+3<<~gM8st-aMb2h|Cz1Su;AMe>*Oh7rAwd@B7~CEUL<7lV&=&gS6n}sFQ%67 z*fM|0y^jm7T%9hY=lX29n&<`()$iTe{TfyGb}WrIYS#a4@~8bVXY9>G!9tzBlQbua z|9zS1asJ?$ufBi3$@Aw(-kdkj$)IWSx>vD#UUWM+wFFg56}KKJPWmWX)Sa|M;Cxlv z8}o#~ZwIEDxBhH*m@+%g(P6@z)?|6M{l3T7Zaca2dcr;JjGT@GVt#M7{i}}VnA;Z_ zbK7k3m*^MUS0sphn{wp4u76?)J#txk%IS+O#=`w9d&h>33Mj z#kn1DPM)Lc-wUvqqw{MsD^X!OnoiFOidBe*i%5Jh*oy^QR zpHze@OE-EPe#mz9R`pGs;;-W>~rFU0SChof|KSh#Ei{{vOY3`bDXGa^S`i{-i%-)<)UdgfSXsHZp^)T~hH^=_Pi;j~ zxqX!`v=S0ZG?yloTj}z3wcoe?`u_UyZbNz;!OYq2LXO4Qi8Vt`zQcoEC5;&0Et`D@aJj(C6PCs>533a zT>?+oFct$@mQZXDB@VxX94n~uQ?FtX#mw0k=+JQtzK&Q+hhz2u@ zf$WfWceP!>pw09^-k7-rG| z9S3>dtos4*m<1>k-bO>@YS_OH1io<4ANWkz=L(%W;AbkdvEcD`_%48ECOqP2fbm)i9(83V1M(8ZgJ$_O7*013(H znhmPp8&1Y6>aZ<-fa)B6Rjq0XRGG=pK*ykytyjl@&x#fjX8TUn&Ygg6xz5ykbnMrY zr{T~ki{4~Py*GR*r=VNw+9NP12T++7dbn-)-1--(mgpQ6Db`cIyf?{w__P~pq{}m~ zPebNE<1Io3e<+-iS}(B$;KN*=&xYUbgOPM#0eNt6Xs9o)DoDYVf;`<^AOmCQt{`eW z4~DDSsG5elv9hm!n-YL>VmxyT&nGa3%-tIm5+1=J^WwN1a_nv{fZb7{FXLH1W%kSH zOd}^%m5+N$di%cIT$b>wUGja8VeQP$+b(!#9FE+|?|8pvME}$%VbK6-Vk=N?YW=JJO7S_S4G7q zDnC^8viXkI_pvY7k-})nRPI>s=1JYJNGl+{Zv}yL;aowNIOhXlyJLl@@xfxMu=?Q# zKiWVL_U^ltMpC?A!`vKgNq$@6jxsa(b3-sXZ~SrXx~mJ9lDQ~m`H-^DA(ybnsTcKd zPO|z1`GPBDqJ zbsl;2F;E=F4IT16F z@1@L7xKCv{Ynu@9XYfh?RJRz|oa+5{nrLT~%pup1j~eSOsysmMh#Hy2HAsGqas#t^ z0`d$(WlYsUS){n6@?eUmNYhxq=5D`|PUpG4u`=7KGjsMGu*N%%I7S$j5}NKQM;EYH zGC$(^rya8m%4>SGaCHLx_9m_I)L0XNYZHIl9AD&oK`dC#G&! zFpI?weEnKR(i(ar;{=DYS*)QU0s%iNd-Fzn9#QrR)VW7I-Pe13n)YawJbL(v4;TVL zcwFeq>|u9XQ?Fl3R;Dd!i2tFQwywqGuaG69j!Iafr}-}ZZL1R0+)Rm~<$@!bVttoG zjp-F6@236-&)YiIkSUVoc8jPuGcBXx`R_&B~fawKJzsXcS7MO4aW-|4*y+;8N+#O8FQ6 COTEr<284GDTX2mpW(Z4#c&t)adrFo1hc{bRtyEfK8R$m;2A)@fo$KuOS9B&fBSmXPVB zi73c(lX3tUm2J&1vPRu})J)riCZCSbWwUc=0FWoR>?U&#$%0wr5-X+P_cfj5gH}Sp zH%N72ogE`B)}%s))E6damBF56WFAM_?ma6X>$#QV!Im;1BYm+}?LA;)8=K zY>tBe!YQLJ6~t(U1f@cWz$}I!5J7}ctQ0|@*&r;2V3C-+5djp7N)Z&E0}fq$&Kg5x zqv^P2$QCCl_!gG6qau;h=@dF8LYi43f@Ct8j{}AUoQA+rK(Qv5fO13)GvK7d%vkNL zm8L)+qbZBdXBB+T(-$Gw>^j{DG36La6qhoQ%VZZpLb1qZ^Tjo&?O@Z%f8BVgwj-m! zPKwe=2c6HDxq4(r4THJdz1omZk@E&kVXRzHOu0C1&bN^itHl+3?u(GH5-1Eo2p*?G zG)ft!<^&ZcMl>)MCxtOFteGPn=6D%asf>dWOgRUKRdEochGi;MoH|zH14|_sJc89y z4%S4O$zi)z&h8Kvc_kLb7}CVjOa@Ko4tGGRg=T4og|>s39sy?_U`kXK_JS4?U48x5^+ntUo0Pu5ZabqcHP+;OOP9WzWL0DuSJ*@e4I@T`>9hLa3^Xmn)3+KH@Dc z2|7N|H{n3^gEyx9+9QD`Pp$2`wfn)Q5MpKP`88N-%Rq1Ie*e|Q)Wx<v$F}(8JAbJ5TCax%^6HXW zx&p@4CPw5h9DU`0f0n`3-Rlgv%AEIZ?T!ixS+;xKBBFfz=a)993xSAj-iMNk4_8hs zFB*4!g5WvtP3+W@K&UoIfAD;s|Ae#;iYHF{^8U|Q`L4DVd8aBbxL+&1b^C?`oPXlX zw99?@Jo%q4*1BofqpD986jJPD|qC#%g76KR6cMUb6l@L*fFE zsp*b81wOcWX61>bpE{avr<~4xTC{85`)NeO*^u8S1xH`qbMJ^vbZd*DVm2~R)}GcR zFRt#5IIMUs6h14w-13E8bnKh6Gu74g^1oNjn||qHT|;7?z+G|2Pda91j*i#H3kM#> VbUfb$mL2l_+qLRs{IDu>^*@&KU*-S+ diff --git a/toxygen/smileys/default/D83DDC59.png b/toxygen/smileys/default/D83DDC59.png index 4d2cfdecae183203309e7c644a59aed03888fa00..8ebca9a1bf123ec014cd99902b0fcf85f9a65f03 100644 GIT binary patch delta 1462 zcmZ`%X;2eZ5Pm{JfCPhp2gG6m%B9c{xsQY(;Shry;ZUpuxdvMxVp<4gCZHT8kxR=B z4go3^5eX4FBZp8qrg$KTa!3eLsT!ejD2VB^{na0x+4*+Aef#as?z{}lEILn{s|Wxv zGR68qhlrxmsQ?Ib(Vs$*kmJMLedz!s832%y2EYbPrMv>*dm;dTG6AqF24FXX}6Wq@1Qav-bH7(DQ7|K!Q?*1Rs4hHV)Ug+yW9>epg>MxO0P#uZ1=d$MrC zxN1tjT5i)Mw-d?9O>*bPIpdlc9l@HR@H0^;B?!i~D&aElNP#TNL3m6G+t-T=56@Rn zP9u9K?o4oa?fu$$^$qRq+I-cLu1ewRh{YOXx+KhUw}J|;cuK3z_pR*uu0Z|K(SV&3 z6Z$fl9>Y(G-%WtMww>{Ga{)ggzjB8J#rB}nPz$K-ss~hqGiiqbP)JE)1hIWX6Y#9~ zxX9@5BJgYuD*~Sw!({p!OB z}r5T%`M3t8#KKNvr+{?(}^ zN&lu$$*i=muIzeP26e}&Glc})?l!jq%Q|V*I^uo0r)*2c4R#+g+A*8G;Z6ykMt;4z zI5>$JiRBy#v2G0-i%qwwz?tXvC2C5xb9sjp7}QRiV3+>*Y>~YeXQ%|Vu=OHqlM!4y zr&=%@uk%ScSAt|;@*g`cFsFxR1RgQx)hkE!eOli2a4sfkuhZ$8mx-L-5AW=OgR08B zp>`IpF(r@t*9&n9+I^=+&y0be8ip!0UQc(LP*yKH{kBi{d_Lk^w>nDl)s#tldYan6 zS`N!3*+d(>>c?E%DrvrWPNlhCq)A{$Y(4l*q@mbDjjCzO)5dnFA3gW{_~{0P%>-}z zL*Gcqbn=H2mZI@BH$}w+x~4{9L|OFXYjy{J>_QB4?LtQDtTe^U$ow|ZsR#Z!ydp#v z1EY1TX^)_lC<*G3JiCIP_hddzPw6*OchGv0;P(hQG%QmaH#Y62S>DU7k1puewW`rg zjn<2z9xbdfJhzrQH8X%>+A5673HO2mS_rnK*!!dpl7vO}SrsJx?6CtSH!e^2ARH@M z&5qnS<9_}5wIp{SP!w8)Tvo*5>P7p91(yTmXe4`{X<#2cl5-{B8d2yF9=`S8rh?uE}v zBuk%71tAahc*Vne(y;K4c6hi@8GA;#{+DPPz254`sK!~D7Yi$s$r{!v$iZ3@r1(|o zV!z_7y7IO(h1nfhwtQN0o_D#i%Dx)X2+5YC?jv9bT?fP3u1?dvLo!m;v=8~BqE7qO0+gLw_unP?G9VoSsp$}B-#;)mDe|||09Tr d3nxTJa{gcOXyph$pJ1p8fCtUXrNNnb;cxeMiHZOK literal 1542 zcmbVMdr;GM9PftX7>aLC5gj4iDei3Z=))#bux(N;TEMmk-G;-^1_&ihO@b}lbPSOV zamU?W>XhRY)E&;96Xmuk_?UY*>tKjX5jQsQd@#pA=Nz1}6sg-Eo_{QtRA_e%_LXUv^b>NsTF3p465D1Jx zM7Iun3`jLB3iH)duhl@ z3tSQJl+GhJK17ys_jE%+pyUmn#xv5PP=y5NEMjSoXeM-^^hM!fTsVnf2#G35L`|Y3 zf*@KGs?=(gut7~2)ToLKX*`K*(yI*`*r>vcdIFJD^rQjSqJ-8!5KFWglQzUPdjyg4 zIO&jGMzTB3RX&xA8+n=%Io{53g`o;a&*wx=$mhI((WU_sG8vDH^9hTB$MYy!g65g^ zw9CYEEHIW|oOyzMRH-LnRE1zB%t)eA+(tr+=?z-aq}CD$On~ED*Z;{GBsl{Gi{n4V z655g~FgSf&`qJWY^UxltV|b}G7Bo_S%Vf`L%!J&aZnvhDl_Zo*htSc|MS*kmVbfN(H6|}R-q2G%f1Z)*s_JJV4)30ff8#jN z(ezT`yZZ*4b4=zfvttv7T54ObkNna+uq68s8`J&yoX?7UZ$QJl)ECR6E)1WXl`wPa zz^>bYKj&>NIQ^E;uM1DAs5zMyeSG!9a}Ta8>$%^%F*cL$EsygBoNaPtPhZOYQ}v)( znGlHj^}X^%*1ow+_~3)d8T)>(NolyXbFlKrI~jYbS1uf=U9WAhrQAL1*X%tL|J`&( z7Y|RmvnmOwYGaM|NoT=@i{lDyR(aL#1n+{aU)2Ll&4yjJ8>W%l+gf*S>qwt^L^tEZ zicUvhR^yqsyRpE-Q# z>JLp!-)d){cJ|&Gm0#{~ly6-5+q99Ml0!%9PJP|^{lfEYt-Y6DBb&Ei z*RU{E<>unS4Fz`H_K20hglC#!POPnac@4K?`1$Uvm=5LLJ>7#iTpN9Dc}8zpqN)Gt z&&mB3+tRY?TRUG_+EJPl2WR(0j|vTgDHE^b0`%t1hY_&Mtd08O)WoEg;Gfw{rV^hR H)|UMPW@}6h diff --git a/toxygen/smileys/default/D83DDC5A.png b/toxygen/smileys/default/D83DDC5A.png index f72b865b1a16f85c94438d162a5da659fe5dbd43..b065c3b01f5edb2b9affe71b7a22292f69688ab0 100644 GIT binary patch delta 1615 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 diff --git a/toxygen/smileys/default/D83DDC5B.png b/toxygen/smileys/default/D83DDC5B.png index c5ea2ddfe42b11333623be9832efeec161ace2e4..4fb19777aac43aa44c8c329d6c98cf461d7dbb61 100644 GIT binary patch delta 1720 zcmZ`%c~H}768&_zR0MVk5k+@UxzrJZoRiIr1W5vNxLk^O5eVT}#XvGZI7~po4N5`? zArL|2NDv5wfC~(Ah@uP#3W@^`k(EQmSxsPoAbj)NnyRhZKep>tb-%9auCCXwUt7NY z<}Sup004_4x;{u!3&nb10cgCj?F&&I;l{x(-W~wNn*qQ~1we(gnDYSqhyvh!AOPqh z0CrMJ+Pp9TY+CudyDttx$YQa2h6XqV1xSHJSy=^}!_CaeD=WXl!B}3^pS(*Vlg9{N(~2s2LZs6omB|^m#^d;We#t;_?Y(TltAygQSl-0B z^a=N*3HQWboMQh2bo98*mHz@cl&sWfLmEz zBbCY|3`t{kV?$AGO;)*3D3v^X`F!f%??{m?nWaGI+BUvhP7o z8|1dZ!VbvmfQ9YwM(aB=gQFz#U`id#zX$n0!IJy1vrd5hGYSx zR=`9N%xHwH2FR?1F_kd11O{-HajEMkGZ`Bm+0f?(Jj;d`xsX@{gZYrigXar2PUml& z%2^_$e*7U}5zAaTk)m==UBk21UDMZHGu8-cYXsH`E_D%`{PIXR^7v^GHG=4FNAvbX zyySTQ(3qoaN!5l++Sp*8S1c-mwLh({e$ozKUXFP5^Xu0$3WaKIGI)crPBadzADvYw z>n>sH?rcL=YzlHCID-uJy)FpRq`7+FG-ow+wwgF(xtS^e*qjjW>reA0#u`(iuad(r zhZxi7ln~>%D--}6a=Ct3_9RNHLz#uaYN#owm;)aQ#e0-rY{JMdK72MaYJXC=X=Iq` zexI>%`TlmT6m)&}| zn+?s-ln1`Y>q|s;#W5#y4|4M_Qu{8DPM~|0)31qr3HSxai=^DO9b5kO${g&Ejv6&L z{G8Uz)Kfk6GFB_t$_hNk5$(|Ko))#T9LFhoMqVC4H)L8VvWn5GR_5{zFS8!a{+2x2 z?w~{YfHB`^q_ZZC(43nNsCep`I?P>eC?%Mq+1CfPVlDZ)0=)pMbP zVjf$iAC+UMQtf%Ht}vze>|4pkY8gazR|f56)FgLisiC{}f9-DA`RRc;?Kds=%stx= zj<5|LjM=|k`f3$?O9)gQCo%D{XY>idln9gK3znsx1gGwOw$EIm{ibO(Db{6_T4`i? zpPoXs+k`osJYLKpb@gh#7-7#0KNhUBHmiQd4#i>vY-?Zr6@z)AQ~H#)FWvmELH=7U z#*F5L$K8KfZ4WuVXq43Y#&i95hXFc9_v^MF!iUI8-$d7`YwArtHI~<0Y{i^+pT6*h zzu_2V(f8i(@I>sn?COm5HGCc%2sY#K*4Ad$W_S(5K$4Aps9h-DAdnbB3Q683X{7r5 z2b`rwN1a%~blA1T{H~MdIh6RtxRaU9D7$&sP-{zjCJL32mD!#U7f)YXSy@rJx}3Cf z8CXeBSJO~WPfcSHoS!_g>L;w?A3b>3XKrF|pcd-}Am-%AVYn$Z_ykL&>VEompo#!hss#rA=But#+jNXe?h0~=--Tv_WW4R>1N4}rW_w#-H zQfZi%IVxs+3;+P5va@g_JH`bbP9*zI>3eEshjENHm&u{>nG!Qi0BS3hM}XN5a{*x_ z%vR5uZwUnegyq{zxlFEpuG~U7_~w8O-|cX+XaG>C+)lH_PB35|QD7sL(C@8Y2(($1 z&?2#3sCQySp)ISFCUQ#WnJlGti_8kCW`PQ~oF#A&j2U!0ib$8-t%QboP8XvnJQGBOFd`7LH<|{g%f+Z1k$@u?#CoHx z`Enz!8}Y@ilu#kVIOPICNl6L6M8v1)0s$DCUBdb0+=rpI2?hvhOJ$Uk@#1}3$0xy zkCPA>2^Y1Nwy^ccpBx0UxqG>zfFY|!KA*O+MKKrQlx3}hAen4j39(=JR-09hz%YtS zHKHwTMil!7v0y)EbQ%(aJ$8jJhle296GO8{W0fW)4FE#j*|^%|_A}L&>|@p3^PN9jBvC!; z-|r3gW#QrPt~k!Qzx;S?OH^XKVqFvX1Xy}nrB-lHcs@lp@QPC3;hoKSUHxNLealpR z@t5r@ceMYhZ#iUlMDeJfJ)M^ZkK(hV?}$XAt9=DiFZJ{8>0Ei8hCA=|9{JFlySKLX z$+X(jyaLZjzxUSMN4^i%tP4%>SZS7gs_l;9w&ySql zkihLt;#{t1xlgM&rih0}mYp2vRb})q?$>cQcZ6{AGABuEqc9(otXfhYW~u+BdE!8G zZ~PMP^#*S5XbtHIGpxV0?_527v&8qlcs9PbqjA}x%UdNoH+L?*arf*{f`(UJaWMAL z-m+`GH7V`T!Ho?CA?ce6(?2n8^M=R2J+`<`y|3Hh`(xtx6zhOu@sp<784+jOEZ#bM zbXhfiinF{<{9YF)f%Md@a~jx5p5HIjaysqggsvH@g~z4>5-;b z&+*aMdw%=1+Ebpmx^a85c6;T73%k4f8EYNsiO5EVR1s`R+=7Udq;!ouR>1SGRe!lB3XzZ4_ z3dqykRQ1`Oh&kXJWubj!f@KgmJmyN*;>eJvysuXHI1ET~_E&1K4!>8eFdmmnK%G9 SMP>y4?b+IS_+ibe4gUZDGgaCE diff --git a/toxygen/smileys/default/D83DDC5C.png b/toxygen/smileys/default/D83DDC5C.png index 4fad011bad1753b2f6bb25e1d4775b89cf01fc43..2dc62a91cb329067ac2f2341dbcf79b1131d654c 100644 GIT binary patch literal 1842 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>sK-jxPIrXmPQ(E|C4{;9`h4Q0mIkb_lz` zT^p_*Vjx@%9&w;b?22vp>wM%hEct+_+DjIQQq6fQ++>nWc#54RV~x1-9mFH_I5Ta< zLbTaaEJb|OSfY*jJ(O91D8i60+<+I1_<(F&2~n3!3LT_6LbY%9`rhpExzXdZ zu+Vowq0jnqk1}VOYG2hWonDtaye@TkUTXK;TjjJW+j>{I+2b2(h=e6Yr8XSw~RLYvJ+)|<;*{WVzYJmqWM6_YJRS7%!+&oo<=Ww|ED zVr7>3vUJnMsU{1OjAkbo%uF;(FcS*UWKS>=1cp^lh*o=`T9dCzjhAADn?jMZOrE_Y zFg#N%MH9`1<4uKt2}6LTPaT+mf=hz@KxvJEA?eA#&$D^5pMS}Wf4O9(z{mbI^UBXy zWF1@=;=3p1!W^&)E66RJnB;N|yqYX>CMeo$mz-@C3IR2)K#yYVu6&ueP z@8oFOs(e-6PW{0*KjDYWDbem0ymnJL7``O69sGCd*2x_^uC3V2tyq%&_x!QNLb3-P z(_1X3_q6+RES>SKdHU`H|75T5d6)0dpX^^J*1P8H@1>26p@&;4dN=i&yj(MD>R$P# z)U&s@WSn*JpAo6DKWj~~=;DWJiSANtUEz^h3wIbeu6gj`)k)q@x*wMKME>D@zasxd zR>+SpUzR0`70&!9x-{aD{|^Jl4L*V!FAqv0`Gjwam^6?Ttq@R7^}PL|6_r9qx&g z@bNjUtkk@CQSzeX$4!sVo{`iM5}G`zIXLL@rOU=fn>Xo7t|=<|{K@z;H&-`Tmv@)< zTFIPKr?eZ^oLaT&)vO1%tfJX=ANkdG>%pyK%brcUw(T2VHCy|+b?@fgo44;Dr?9&3 zJfo`<6K{QyG)_M|&$#^Bo}b)edQp2?4radG^i(@+UDV#Rx4XVdo8=XyB@`5v7JvWn z<=t`Q|Ei6yC4$wjF^iowXh$Vk_~T-VSf#L&RX z*wo6@MBBi?%D^C%@90hx4Y~O#nQ4`{HHZY)g6cC#kPX54X(i=}MX3yqDfvmM3ZA)% b>8U}fi7AzZCsTnH83Ti-tDnm{r-UW|C!Pu9 literal 1687 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 MF`h1UPQi(P0~7c2i2wiq literal 1487 zcmbVMeNYr-9KI$}L1@s3Ow8LBWYpkx_rADYIUVkHue-^!6OMs!CRp~CV}-lDc9-rB z!twiaGE7Mgw4%vIhJwN3V-V2`shm(j$58{PBo9LkOQ)Ey=sn=9KOFyPXLjGO=Xrjg z@9wazdO0R)ew0imi?Nu=OfbfV-dQuiSAK7T2E+3bl`CcOMUs~mSXqXHFJvK$i*~Y^ zEbZ{W@hzJ!lRa6)*>j~_>q>&*T?#scQTSXQz?R93x+s{ z9?sENQLD$u7IWrufz2vkWoODu7~BD`SPrH82q55MB^vU%O5Gyi)5DXx1ek}m5g3|; zNF{oB%Bftd4Knfq3uzQ+DuZe96aoToh*t#;6ooD&d8W+8x+M##hrx-$!8r&D!!b&!qA(4m zq%aKAnUpG>PK6pYq(P%pQ(=uqakZq{q{MMGWl-rb3@G6S+(c4ZC234kY16b}uEi}% zw3}hWb~#{onyY#&moN$}E%Ab#=S#yKU@PV&UM%K4kTFZ6z@Qw??clv)s612AlB~eJ z$vR8|?}8@tOK^{{uhL>F5IU{`-iPAOP^(NhNoA0l47E;;so`m^wnZ z^G{V5*k<1$o4)Lm7udRcOtB3Ox5;HYA`&8xH1AnID;~aVNy^*){sBWwf~}j~%X)*A z6;9Rm_zr87e8Jh8iuMmiqbu(J@N|oqUV0DxgZWYZ)q}wBj6Tj_IFQntwYjRP@u!%; z{Vn3Q*Z;EI9sBvW8=W1SYPi$Os*o|+g=aCx$1es(TCHesFi73q z>y$4%OyqUO3A>=(vCk}^2xsJ)=&!#?rZ)$EHSG+RCv>j-Zfv|{=DEWMuKHWw9jo$h z-G46s!xrar4e{sUZF}%9QgTl&-nh@+UpLrS+*+|~fb*|QiJ{*P^xvZLqBh->)-PVr zrn55HUf)>#4U0Rw<-za06^@!NbamtPp%HI=%A6*;#yign=T^&y8oqBhwkLMmEyfJX z7vdCR)Fo}W5TAJQqYHJX4xLFheH>A1vgx0iH}lZ!%LPqj?WeIOtak3uz}kwWtsBlZ zD?5I9r{HMKi^i(_xWx5qhSr{V@B9&|@7|J;lc(SEv<=grU8yS+PMFb-=4+kW!+)9+ z%S+De8@^AzkYTt|f9cs72jN&=T2$V?(O$H>_E_bjYh7idaOJ?wSN8k21lILe-f66R wX*2SP>u6hk`|`o-*b*r?a&Tfi?eF*k*_ic#ug7dY9{R;vs8!_WhJua%0OA%WUjP6A diff --git a/toxygen/smileys/default/D83DDC5E.png b/toxygen/smileys/default/D83DDC5E.png index a3cf22a3ee02ae184ac68b414acc1e5a67a4ef48..260ffafb148e0ef3c24d791e6e2d7cd2a67d5056 100644 GIT binary patch literal 1736 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>53ZKp+?#hvl8{Rb%9b%u_0N({y+v06?!_SI@xMZyXoF>eg1cLs zvYhQS#f0Q|c>;7a)P)6g#f9|+xJ6i)oz#`ph4}O&MUDA5P5C%ttW9OOIqWo)ErfWC z`8a}24di&Z`g2lsx!G8mm;wxSbi{-X&!1Z07#O73Sl!i?Qfw`#gG)tNFvv)^ z$=9X9$)ds4rZdF5E7CvQ!Z6Oxyf-fm=p7GDmHz193BleCj^>R{76sOZ!3LUn!M=vP z9Fayk)d8LpVnb`}&1xOY5=?Z9Li~W?OAR#{a?!@Tj)=eNKAz2}frGcXR#opt5& z->Yi7y0U?NMQuI$g)`M6ZsRn08>7#NtUt3o15f)dLW3X1a6 zGILTDN-7Id6)JKI7#J*ir-piOx?{j`r?^YTk&We3p^b9(^i?{=e@rs$`i^_X9Sh#Z z)A8)`i7%gj^ZydtzpVF(-r?Ee(LQg|u1@XV7+M^iJN?|A{@ZWOW+=w{ItRtBD$~5R zcg2BiH@$Av6z8mYH2YmBufK)JLA5l=O~n&;o8R0cX%@wA5WVBb?z_3=oU*J;ivpzu z+jD|H8lCsJm#47%C|~W{2SKyHMRh3ec&It))NYRQBf%+x3%EYD<_J!e%0BUKLeZ<4 z?DIb79kD&T&E!tPqZmJN`%B3#jI&Rz%36M9Mv&x7)9@Hqvn9qh^?R(>RGjaSJ<+|U zaIaG0o!=}DE_!|cTwM1k>MFGS`>r9t6xHgsVQu6}QPHNO!TH<^6AlO7=*SZLcB5Nk zL)n&J%FkHt+|O0ov*5DJZyJpcvP@?V?(2=;WKl!Zz7{ z!5cVBM1rR->shWTwSG})))Z3>?wvnBlz8i_`k4eiQ3TxTR! zSjsi7D-@q=(qj6Lr^a=P>6`uoQ%*Q~aC$|FtgBO4pZ|o}K)&(i{|Sd9c`RlvHncFE zBwse&Z@Kcq2NR?+Rd#9>S~0LXpIUovU#pn&iPzZ^7@XPF;7qN=~Jdn4G#%B*}#(WbXE2f(X7|6q@`wyI5M5_I(1f8Yt`D^ zx3Y7y5*X7;d)MXpuAO&pUtMkogTRXyuU`7kusC@@KDhi2NpaKSRg63kT3C} z;Kaa-h8qh%axzF88ZG<3+#HneRZCnWN>UO_QmvAUQh^kMk%5tsu7SC(p-G6Lft9hT zm8qe&fq|8Qfw|c$ITQ`K`6-!cmAEyyWd|(+YLEok5S*V@Ql40p%HWuipOmWLnVXoN a8kCxtQdxL16XM#aNSamdM zVMxjh+8MQ}R0bo5SWjPvV6sF-y&*PR#}mb-jAzqYcrcgGGnwphjcHq%IO1P7Ce^lT zvMmHJj<8Z0w2rNZ-e&^L=I-r=?24>6vKX3Vi=y4ApmZ4~!px`@a)|xH#YtQyfMG-t z5{{@)DW+tFaEyJ_*=0uj3%@UMQbQ(<3tC<8YqUc8Yl~h#Ur5q8q$nYS=OcY z^1O~#LC|C-fvafB1diobMowZMmPn+k5D}&ju^zB-qe?-D0#!;7R4A4xB`7qG#s5#v zc&sx#dvpA!StgFy4zzFIv_31m86LvSP7KYC#@3%|Z~&OvpjMz7+wJll9WirJ_lE2J zr{?EQ3!c?6FUg56j&;g|+ZR;9rLZ?#Rmk%^Smfe45^sy`-nMtahN6ZUzMr^Np!{Q1 zTiRy1`xn!ul^v$jYwI#rCI#J=_>Jg$AG8c#EfMAmN;sp}L8tszbN|>=fSgOSG-kL0 z&hv%OzZbqp-Bnv!J2DN3(lHg+68XF2*5soYr+5z;AiY4FGa{67+m? zrN5z0oPX&-hYSDc?5`cZvyI#;NV>5wab?M1#R=h%etD=%S7)!eqzrf4BVF`!dSKC+ zCGJAw+QRxpeNR@^PRE{BR4DHxH#KizmTdZU@YCbom8g-AMkcLZ>==*-K)Gq0z@_~& zhJ0J1RtHoKIOO%+x#QK@v}M%rQu}8L9#nVy-8^iy{$hE)*CB`Gh|LLyYwpfyj6N9g zi*xjxBdtK-K;XhBsph7-tk*aU)QrU=Dv)~q0woLUo@rk@AUiO z&od%r3ps=_|FHY{-`|IGbK`eYlGj_V|c<8lp!y-T|;-b7UJ~1LEV%q5E?XU^l;#+ zj2@M)J;1}M!rj&W=k6Dm-^BQ8;(5CwyXNEjVpxG^XJU6w-wA)q6XC_!O%W|gBxz(Eu| zz#z!6C`1_qA|@aP2m^!=vC|8Nn0lN$${m1LxjLIs+aTQn>x)JK@RKzFH!}d( zfJ`^%0Z2jsz{dc9EC9fmP(%y*4geL|>60N|U%q^S+Z^;ihlftKcC%A6b0C%4);;u* zd%)2dlBeeuTK?!MtFD3MKgliV+~xEKPbti0>DrU(+Q#PAG2Z;-+(I$=Rb_4CyRKgT zlCZRL28ODysUu^q$DlQ+LoMH`+j`8LeM3QCMdP^IlqWUFYBdJ zYN_Ei#eL-oO_N>|>2C9d>HKUxg*ZaTiFH^WTlwz^)x zPRM25_qM?_A~v@eY~ixH*7lwHyNnDktgGspmE@Q7s-8@=3V)$D{uEkjWw#=%#7Hr%*sywA$dD~F8G44YkyMqGj zv)@+gD!OrP&q<=C25FBl;_zRN!cRLWeIkYWitA{#heAS$eeT9Vvq)0sdwOg2XOCtW zz8r-lTi175Cl41@VhrYODs54wW%i(3}A_awP`u#3op;JA@RoL`$;4ToPae17Jh_uEC=HIf6qu)?cPLrc z|Di#?u_8cgK0=MB?Ro^6mx0>yG56-?8hO&pIN4Gweq}$U92Unr^jgVs-mv{_|FbLm zs<1CTLwM>jdE}Vk`GMS1se)bABt3MFFb4Tuid8}0lC<%bF|?&)GN*(y4j z0lu@dGsYE$Muv}kQ`IX1a4|j11GePqOFz!IsY!^mrT| z>zX($a7kP;5t#2bH{UIIQWP06J~r38hFO+MR>iUvJ@L=Y61F0Gg%V?;h&UwnDh^`cFyioGJA|{H!;ugN1k%w7>Ev(# zfj}Y<&PVxs{tJLlh`AV>^8W$em(XPp!1QZ`;DlJ>^{A^j;2x6@jkEB>N5$dLxTu(v TE8RHgCj!9FE6}sX1C#bIsYiJ! literal 1733 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 Q+~A+uXh_5N>)zk^4_yCP3;+NC diff --git a/toxygen/smileys/default/D83DDC61.png b/toxygen/smileys/default/D83DDC61.png index e565a42268fea0fb10b3f33d99f4e4ed37291456..c55056b719ffcd675277158b0c45bd96b41366ac 100644 GIT binary patch delta 1443 zcmZ8fc{J2}6#v=|hNj)}k|m?a@WxUec|{C4wlGSzOekxHrouZ6gX}d~#>g^ikTuKL zWhBN-Vvwvu&#}Y|lQq4bUf%DW{&?rT@A;m4@8_P+=iYnn_d6^#AVrcVhyVc8we~wf zPV|wUD_Y-(_S)%~Xghb{`1 z0P$Fb!JL{%W}eS`dO~wfXjgd(qnKEq9R}V0s_-td$53T>O^ORG*|WVMgdF1rHRvXL zLe|gqf{lqL6hKEwMv_$+&|47j147o_LcN0$@H*%EkBou3Oi{FLYbLfm+q<{$CNs|) zs@z2&dyLYP#v2p)W6ivghS~1?)rq#rmh`^Ld&9MHT*i|bMoMY2JC)!x|AxjJssGTK z+C&NLpyF0W8~SR}#=6S{bMKZW*_|cC>5dF$b;=a8WMP;--B(-vzz$kQXGy}uYZ{kX z+*e2HE%G0#ikxQTj5nrqS0r+pav0ADF@x3Ng2|VQ1C^uncsA7!+F|a1v<5V@G2YSc zpJ!?PT!UhM!4kVRT@(4N4Yi0W9(jB%8Xhk9X)5SDM>-1b8epu8n(2{Jzku85kdQYl z;Ou;DzXRW0R`7!KLN}}}%|Vjb@kgHmoHa<}Jg zhwfJi5w6zN|_T&DcU_UFVAPh3sBT<~7g#%o!h3LskA9L2H)j=Br>*=)>8~mYi?>1la0` zA*v6;s89GJhrkimD021_-F3Ga@RCp*jG#)yr-QoOQM=VTS*zL?exKbqVl7b($?HP( zQE0>ChK)$DJ(nJ{LJv%wJrqz*Kdgd3`q#>Ad5=lxy1cx0F&`^SJfa!B8Rk6EnL35# z!3baEH@gO(O*TgSt}lTbmsY`QaDy^A6(y@t2TueFN->8Q)L)$TkPo@4?}-kkI*er@ zS2JkveY)AncT!Eqmu$32KgS*`qHfVq1m~Qp*Z{;T4LRB zORA*>2Gb!5T{f8*m@(@avBucAUvz{alm&u$P1Thpr2}$Dkm83(_nj_d9O07tq#h-u z2}_ohRg?$%1)y0yz06aHlm2=Hl#UK>na?|hcwSsua?9r@q9}!umS$@+&`(DqtE(h` z69d-HGVv{PHC@nbYIw8@-Uo~KHS!Mgg&fdD>FR2s477CgF*+zCy)#B<&S;`gMkrKw m1ZUrW1i>Lb{x>83FM$86s7KtZd#rK_27ndX&iutiFXG>6!<#q& literal 1489 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 literal 1448 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLFXG23*BMTEt3r9;c zb5}#4prg4FOs{8NaYu_h>bAii};Ey^rQO>ryA&s6}2uT>@%`z;JD4V+w^4J@2pj6nXj zG&gd!bapdyH8eIiG;(rLg6d5nC(QJLj?o7t4y42a69T3{5EGvCfgE_|NzDW1m?B`- zU=;JIW?*2v;pyTSQgLg_ghow>ryTcGQ{-O?== z5*lWjuFrV!BUEc@!fersdBQgu*SGGyUtJ)<@cbnU+nhfa6aNK2v`8ypIG))0_27l8 z&NrqC2Y7u<-L-vn9z(SUzfxd}#XM%-Zh<`4$R+OzVi}yyB~{OT>t&!WGO6<@~p8TDD-BLlk{-ApKiI1-)_jFx+?yTsU z6e%Yzd%DtTuUmt&YpS0fi&b%kL-Lv@qiYG8Ad9IomqM_~pDmy)0 L{an^LB{Ts5>M9gm diff --git a/toxygen/smileys/default/D83DDC63.png b/toxygen/smileys/default/D83DDC63.png index 171c4c6f47785a0f6b9439b996cf3e5331144237..b9a69c7278539f426db94c9a14e81649f4ad2e2a 100644 GIT binary patch literal 1569 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>D7OKN-Fh?t=G z|NrYwRaId4#*_s4fuaHhEUkVkt$WnZK99A}eeq`edxzmkhg|KhEk-nZ0>-`}ut*PwnZw{nl)T;{4d)B*kT`O=1_XblCpe=TTo z=M#0w&b|!|6MoO%+raH1Ds<3JJlI&G=~(f7b*4mRhn%Xd!qsP7h1O^X{e4!&<<~w;M`Yf1CV#kbd#Ztv&Hl5<9YIP1vAw+h_8*RiQV3D@I(~<=Y*lT6O4*($-Bi z>z2#NnsZuTG7Q*rDyiksA(o9zhc~N>p9nv&f?HW)MuO?S5DZhpz3{TvPHG?Jo^d!2Z~sh{dZ=|2-~h`IZOPw(T|7akuR?ZNzJ;V zp}KPUw7UmcY!?)6x6R+#?S10&YbC}Oz9Uh0RoLakD$Y&HY4?izqc3tnozLi|XK|U# zSs{mxlg??S{?%;v6=w?+UC&w3Cb^5nZ;OS6QoZrp<5i2_i``jyPj2s=Ndx%umGmc)1EGlArhBuk5)!A2FkQO z6yH2=vD2+79UiyLb5C8;@?N@^t@xbB{r}Qmq^}qJ|7gP?z~#Qo@LBh{Gw06g|8RTI zud8=-`rl*@6`j*wY$v=<=}swGzIj%li0bZX(aRmrmhYBcm0g@&?H;=D?YF9Gshjo| zHd->-7yNU6hp4A-tBu=pAu)z4(8wc_BXFgZ!TKe8%ue=B`E_dI|6{__trKGoFz=lRdi@I~Qq&OEK9 zKyRs*xJHzuB$lLFB^RXvDF!10BO_e{b6rD|5JLkiV^b>=3vB}fD+7Z(mi3?_0ihu` zKP5A*61N8VNb&bT4U!-mg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h0?Ta%22WQ% Jmvv4FO#qSmv}6DP literal 1313 zcmbVMZA{!`9Ph$>0U=-{PL>!-S;$cCdQb0qyH-x-YlaL zRcMuCvLJQsnUu>BB%?)%H`~0q+39EP-El<*rK$UF)O{yK5MJI#UGA|8f=NQM( zWfi+Uh^;u)7_CKF-IP(%?RAMB08qc*4Sb~E56V!&0|?v$Z@&xpXwpv;+tAeugVs!` zg^qFIRapDGjaai*69rR-Cj|osZV#@iPF%~{mK~G-bz@y^E8bC3h;!N2P9AM0DwT4 z@CE{2Pl)6~B;n)J9P45Iq>u4>Ss%qR6o4#DfMrgwyQw9 zt61+nu{3MSf~}izUGGSDKy90D>sFg?plrP#-P)jNlAf?io${rA$))eQ!G!wxLGbhF`)n-CIcMsaM&tV`ae0tp)S%VW}!qF%T?0gA-gLr^39t zQlA~le`0c|`peFNcTaIv&e018LkyIPwuJT^-p9T3=C$j;1it^YWu~C-YX8r@Z}<0i zE&N(FeDv#rU9X=!OAtvP7(6>Y7t0kkb$#~ZX>=`)!jEfA6WVZ{G}I(UtK;^dgNO67PD`@Z+zsD%0=o(UG_}oTNN9IcMbJ? z*z`aHHZ@jRm^$85Hu}ThYuPW|z3}Zo)d}{y*@=>@tzylc{RPjBTr4qWB=pj0@oI77 zMBKP?_!aMN(cx|5e{On3A77YQr2R%75O$w9KfU+k z{de}ByeL&>&Zmye-LB1@?0Km6c<^2vf{D=ug6n6B5J`D1bDgYe(CA8kTh VxJ73bs_!{Jtq5PkjfR@`{R0ZW(F*_o diff --git a/toxygen/smileys/default/D83DDC64.png b/toxygen/smileys/default/D83DDC64.png index ebd2d98d7d43a1033c08ef06a85a91d2ca7bc484..8661c68e325f561e10a98442a04ba594949a005a 100644 GIT binary patch literal 1272 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>p1!W^&)E66RCzW%dBDcNz*JWi z5>XPASgue|l%JNFld4csS&*twkz2sPV9`4@)O*?-1D-ptMJ$^prku3n>;0(rK6U2f zhnGMe)sgY zJ+s1_9!I3DS>tTS}yJi2Rk zJJ!j)k-;p>w&H}9X#3BEPY>BPxxcxP%)UHk%TpJbC%gD3rrdMg(da+PwLz;xe3NTP z(Hh0u3QrfNzpFS`ubg){N48DdXU^rP&(bS+#5SB%5Kw<5DNZ8?R1;xV9yHymZ^iBcfcz zEaL2udD()T*V=;i7s@sKcAv?i@3GCaCHR1*D}#sryn-8Xq1Kk-*JWzUJUQ-G{=ZdO zvPjwD#+QQUMvoGOowWJ>Pn^E{z(3h5ElaKMOh27o7uLJ#s-;Q6hRhuT=PzyxR9m}h z@#g1h71s=XFZyP<8-{A0s+hItgVwC&Zjp;q6{kmteo?qAP}n2!>({9#Y(K>;IMaA9 zbEPLL{V^yLKBbcaWi((gKTD?X{>5psAj<%W&g$%c0E zHH%d*ubdqI+neh}FrU_;BRgI*F|%}SSj2ET@LNWc#ne^Dtgl(Uc;wY`J@T9Jr}EQv zugrht9gh9=wsKl=y{t+}{iT?*&sJ7=+_z)h6uctO&ckw*@u7!5br;${deAi|>{N0X zFmYCRx;TbNTy8yfQ>>9eg!O`RTZV(y5|j2RFaO)0vn{XOP_=Qs*@x>LImb(7qq_~) zN^QJ$Xu;XYiYT9qYQcSC!CdQr1QrY713237_JseDIwqG-s?PsvQH#H~RjxE53@NrG$$&QB{T oPb^Aha7@WhN>%X8O-xS>N=;0uEIgSCDvlUDUHx3vIVCg!0L~30IsgCw literal 1203 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 literal 1338 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLVOG86fBTE+-S4$@& zS5q?sOG|T81DIaVyyB9?yyR4vy_rCJp?Zz+>a}t%N=+=uFAB-e&w-_YfQ8b?Pn?g>Q=>r|34@w+Ji3KJEOo1RKJm~{D@XV8%2h1@= zz^t*eEomzQ17nt_i(^Q|ttFE;dNUh}94mghLCY!B#UU7OitsAUr-U3{$O>Jh!(pR3IT z=4i(j$81@YuIOi&zV@&0YoQbYDU*XKha8q4V@;KP_ObOu!<0H}zO7ePENz{i32#ix zIZ*i_ywTQHI8|oj#UJKgN^E?+w`4R`4BpS{|Y-BZHeHAD;_4cI2 z0kmadKKNf(Id8f2>+ARW8JHQk&s?7%a_U_Rs5J9*^>bP0l+XkKx2@L| diff --git a/toxygen/smileys/default/D83DDC66.png b/toxygen/smileys/default/D83DDC66.png index 00b77bc29d212f95438caae5cfaf1b0ca19c7c80..ae329b3750a7d3ca8e11374c9d2232f0b3ea4334 100644 GIT binary patch literal 1856 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 diff --git a/toxygen/smileys/default/D83DDC67.png b/toxygen/smileys/default/D83DDC67.png index 162941f5c98c0746b5e695868e5f3bacbce7003f..8d73e2a7b81e158add93f56b72d0befcea39de5a 100644 GIT binary patch literal 1764 zcmZ{l2~d;Q7RPTw7KE@EC=db|KnROSfTA^&7OOwJK@0IG?6eU7i!~AfV~GSX zgkLd60vJOCpy0JRo+Q|+K8b@%_2q`csIr*7nW4&z5Jg7FZ`w#}e2+N990?+Ajk<-w z@B)wco$kXyfACuphX8*NWv(xFG8a2pB$DtVMx;m{e*mf0>==}s2u&K8+F zEWL4PcMMA4NK7M}v8~^WBpOB$4O47P4v`5$M`Dn%PW(2b2!a6vzlDN91sH8%nHz9y zO@fSZfkrqg4sC-%!pAAk;+ALesCwvN6P!I-E6CnVRW5->+o8wZ(0m{CWB{7)hcrFV zSZ7~Fb`Y7MX+GBfuYcTVEK;?Wy&UX-W|Yul#fyP1{vz?eNvLk!!P;PA-iqpU+J#PYtaNeF@F0mPUJLFBd*2=KgTA4C*@v-EV`2+lQKS zq2bS={^qAmxtm|rKtpn9s153G+3c=;+9X{(mv=mxLAN3n#RvVjIX}>NOTspz2gSUb z`LWfBw3|m_CyG-^C?tHpRXE}d+y}cP;h}KLcz^Ncn_GKPXMU>3Aljm06g{urzMNcm zG*-~w!N)DHm=vvVUS50M8nv^0dRno1cvxy z7qR*}cIbN_#54ndJeJ3bk%Y5zNa74pYMOvYl1RloQm#-8zy~E&kzv(piuT5&V;+iN zIMAP-^1OZZyD$4!aiN`w{pybwOyeKmgF1qZOf41A8 z>F9VQm)NBi7SU<9!^V8;KaUfZ9MVz0%IvTTPUeiq2o5jiD$hIZI<+(sBA;n_j@NXI zM$6u9uUb9!y-VdSUz8t*W>&cK_7h%X*^wKczUZ@jJ8gX}D?Z*uIk)dZK)pcTY11gL zKt`6iax3a~U9rCXH>XxpLhh>^*UpPmrgPGxB5($>U%DXQQ&NlI4EK>SWuB zOtSCkN7oCg1cY=T9jl#m#`hf6APmC}o}+QMQ^%0V@0wcZ+79pOSkuT69%eFC#A*bB zZA;(LGJVSXp$w%i4)0bq6sQ@sI*Ih_?E#l;FAk#Uu8raww6<*V6?VzdY?})SQfPXQ zms_cY0HZmPN_!GFQoP*s?*zNIs~55#Wt{j?@@|gr7A2f1#OS};XKG#|yyre8s6Kl~ zB;(&pOWA$ia$@Le{oL|NWqq95)4s3zM8eILB_Bse{AsiGb&I>`H+GbU@iJvdVPJ2P znSV3>j_v6!>#0Su!!b^hh7)&RXeZ!n^;!ji0P~5JzmC44=8zmEFrQ{KO`N)S{AkwubKJQCLKqWW{|Ew+rH=ZYnf+!rxjKDO*fNC zOCQPn%6U-TS$Ed2ppY?j{<9+-XN6G|3DtS(*%>UB@pQF%b42v)wqDLkSAXoz1!-yJ z^NK~m^`>IiW%VU`m+yoM+uA{`J^f;~{0+9Mt2m!KBrSm(RWH!bhh;X-Nrp#CDx1gQ z@Ytz*fY1-!>5-`$9IWennE0_329rpO_sY-r!ZOJD`3yb{gE?jz?WMar$KWGZkuG_g zDFWk@w9UN%1&KU6uGXBXd9;`PWIK8f2IFtXtGXL;5!rKf*=aDS7$^5g zmA4MpdGqf-maZJ~;2)$MygxbiaCBmPa&mmaZKO^#kjxb*0oK0nBA6Ntxa>WA6bHXr zJBfdcgv*xjsGJNQ?0`GP-Q9(<+r{mDmK%lY;YszRI8!K83Z=|vUjBaqp@^H7D*bE0 zoJFh&EFk={Lxd<*lEcp60Uxd?g+~e$viZDl9-AvozsrNa9smOUL;bG!Cd+;Xdb`_} literal 1794 zcmbVNX;2eq7>=ljP%3R{RjlnAL@sme<{;S;n2=2nHGm{AmP8`i2P{J*#Oh#qY^;g*=R@OiVwsN%Wq1e{jT_~9l*CtJQ790gk;kACRIW^l zzlwU3NYkU$QW-82uY?tthAOw{P)!;gfhLi>eN8&KA{NDgC^SZ`<&pc(Rg(d=l1C0@ ziRogU09C1%CF{|UqxUqBS^AF(9Zxp;zm0 zHKqkDjPfWf0q2nkPbWgq=)~eT#9G5-qKK5yOmZC!q|#{`jU}$v+6G*L{_DnDwGGlF z9ZHj+1}s6ZAnFnAJOw6l_ijTLMZz06NUtV}B9B8bMS=#^;sFScOnjj#)k+uwK{mqi zV~hA)fshdV1a!6t5^z|MfDVbgSW_Ht!-AlIA>tq`1VT6 zDQtk&fXlTCbjq%pusexmz7q=z^r#%i^im9qo9cjI6^3I56{Z6OA#A`cRIOEFMuWSh zJQL9(s9v3jDn)us1H8^JtbU7q4#=hZ2?cbH0D&xVbG;xTS0G{wL53Fy`4PTPV3q$T zXEeeYnx#4Z(=1a*L8sjK)oyp)JJ3K>-L~YHH652~$Z!9mBgj z7{<=8l~DztxUs<>NnL$BG0$~rA!o%ZaFBl4d#ibTpMsl3dP zuk2^I4wwbK3@hgjmvuc}Xme>6*V=C8+=}r`oe~W%x*eLOYS`)SxK6@!NUvFO;ozlv z72B&^Xe|S$LQgPdb}`$BZr7!^ovC!UzPQ&DI^LIapz?j8oYCDXdDL=)BeHv1J$-k_ zbtmq~Et`R$s*~?~bmuSj2wSo|Yv%Q$#QueYCFUf@;vB0?+j+Zd$7(rCRvN15o6BT~ z@UXZ2H?`KQ-R3W;IK4Su(|*#)E@j*}{?d)ytsaO5G7HscTTXF#+Ul^S!`HWMv3GaLSobT!>G4ll!8l?lAyuwQ zhI04R)D+#!DK^{Qy6QfP50tmmZ!^NaRlLquPwto!Z_O37wo`g8+|Qjk5a=Bg=-2S% za4h_y9+H`pzT0o$ICsJvr<+ts(;vN&M`Y3k8;Ku__*$Yq+O+J)1~lkzs>deV`uXZ%j8gGFy(-vzO-|x2m(A zHSl!JgBkW;#@09A-=|t_qO1xm)0ULL)J5E`(ZQ4TXNL~W8IRb1msWpR?77p_?wa=d zg^0gZZZ8*7%|oT%c3m4RPQw!D>8H~>&m^2&(<+PD*{EKYw>UihURBTyhuO}og2u5; zKZcsbGg_0U1u-sve?-}}cKqdFXKJPD17pOViWJwZ0$1rFLp-PAO2U<=>rb5ic|lY5 zIYxfsrf4JbFz2Xn>)|aoR&x4=yA;3ad{uYGTz?^b{4myRmSpEx{z?JDK%~qsGUFdr C$;Mv* diff --git a/toxygen/smileys/default/D83DDC68.png b/toxygen/smileys/default/D83DDC68.png index 37dfd2adee9cf487ac09a38a4e8c342cb5ff88fb..1ae9332e798693b8541efc97c82bf19131940c13 100644 GIT binary patch literal 1863 zcmaJ?2~d+s7XG=B`v{l=A)`dO5)#5031@&HhsYray33Jp3^HKItOBBlj6#HAfI;Ix zb^xUW1mzSI5Cjo{fKEto3;`j^ArVvrS2^*QU?~y+NNfPuol^ebh$8^Vk^+2#J+yRmHMF!V>CTmOrz#o*GL-p0fHwa( z)YLWq17#Ie2nveI5abmUG}V;l3*aBdi!=Yn%1;OqeSqFp+8Q*b?% z&q*Jw&hPkp?m%tP3bzFmKV9Lr4%8J5-pUvJn(?Ua>im=-Av|E|^;G{|&N{Ds?Rgt? zORwtk7J2=W8R3#}Y@Oc)PWFJJ-p{H_+pLMJ$(!BRz>yk|-zw$SgR@VhqxIm# zJ&;?o`H(HRmeO+}{LB3!NlQ^;5XGH{xfJU~vBo3@dq`SKUY0R&M*5IcuxNc@`S(+t zzio7u3OShtA#Os>`Ke#e3|{`LxFY>SIpW}9)o0vv@-!``PR-K|+m_?ob6#3y z^fkD__%!0kjW1%cU3&m->^sBhvn69)1(VCR?50=8QwQCAJ7LR5f-54rizdS4l>DLv zqkKYtg|Y@=LO!W9|&hIg};>cy{df7AGlufGctQ;-F%U;i^R1H{* zzVt}S6+Y_737>eGr}dy&+V-*ju}jKxL;{ZXq#u_PSpK=>+U;KH9=?W8Xvg6a&2vXb z>)W<@y}c*Kb8_e}V}EI_9UGq3eAGNP*;+SBv1A{kojZ1C!OUyGFPmbf$O=7{A8?%A z*f8_q-H^hn9-iy4OZI4_yGQlx54^+tw=!FO1|M)mlQ+=UYRzj_K zKTRsF%7p#cyLSF5FuG)G?Da_;rs%wdWFM1R@6 z3(Z7N%=o8lS_ACHNIEM{Tmjd*@`?zIvF4p)%JHq^dh?AYBkfPouhGrKakx8!6mX1mL}_1}cw?BBQdMpaDoYq73{Zb>m6P74Yg=Mk-1 znp+r*Ppfk@#2Y;ol`AZtLnFiHX1D9~O5s0;eEY3r357(J{UQku4-vrmHbUV+Qs;w= z2c(0-No#9FdfUdv0pD`#v|)aN?#8Xeb*p#@m+g@Fxtw+X)4XL^2Q{?pf_L)A;_8vB zcf*<(f+ftAbvB&6u0BzJLP=3#2VW!-8yn%sBvEVH$khg7I|9+%h;M9eMwXp?)s*^T zg}ncXiv~4Iz~_qk`b1pVV?{uncXZ{VRo6l2`C^zJVN6;SlTM0Z&>;s{;jOH&csne? zI+%bb5p75|1amx|gvZ<2E*bt0At{-b5S#Yr1kc34Du{sCIUy)HmU%jgK?huE$Vh+;NM{{wM1qCSL#m literal 1797 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 R_|o);l8S>wWxmT+{{xf7(Ln$J diff --git a/toxygen/smileys/default/D83DDC6A.png b/toxygen/smileys/default/D83DDC6A.png index 1009ac89b6d37b61627c4bf75c1809e2cfab76e0..711c23f0ae682c042fef33fcecd24ba924a34a64 100644 GIT binary patch literal 1834 zcmZ{lX;9Ni8pb;z5C{PYavu^-0f{CYfv_M13CI-@!lft)CgBQKHk>6_1eZ%iWy>g}qhy1MJ>dJBB%?g|(! z3;;mEW525(tV%zptQ5SO61Mrnf{LK}PyuK@BfrIxhHLz>{eC_Gq?-VcoeRJwyp{b9 zfFvRSi%bCQD*#YWsO~)I007d~*PB6u;4vpsz$yPv;tg^Cjhs9dCKA651}%e@m62g; zV_{6Slr8b9!Rm4_Cxh&Z4?ED6WngN#&M^D1(zP6?zo4L{U;wEc*i^Ww;k?u^BxxiG zg*25ywra`Y4NcTFwJnHNHWWt-l9ir;k&vKqeV<9IH{m|R>bASt34%tTozd;Qn1FqD z7KVBZDjBb~?N_o+U5K+NKfao>qsBqMin3#%kOPT3;M+m3x+OKa(CiR2H}rMvZhK{6CxgGdFG)GB|RSBaG`9iaz?t;IYqYxiR zx?8pekc08-UFv{7WJfwOcfA;r^jGfO4pEGN=Y8PWdx^{HFVErA-3HjS@3sbj)?*#n zSzUAUW^%abAJMl`jy2cR%pGNHSnC67XP@Zd`=#aQFDH8+4BqP>nwXqgT3TMW#KVyp zGo4I?vqcj4(cu`P(kC|l7>`6IEwv3GAB zpKtvTeX7jzA6m$O&4_R>cUO=vt-BvC&;k!18hReBh%rWt22^*!`OHdZ1PlCFDfk3_ zJU5ER!3)w7IQZ1q1OV(#l?5CwohHg|EvQh0SlPF4Qr#xP8rCY`T%%gG96e(@DGq4E z=FdM7efe`?&n7PDnH&Gy>{t=OB)MgAWI)`|(b(n{AsJdp)*danU9W#*cR-iQ%25V$ zif0_#Lm>x#^7+tEKZC$yik%%YPPv!4xyOCNM zVWby_?;23dSefxVzkVp+uzl97bfil*rQe^8tHG@0@o;5YF{0HH{$eFs@;R#CajeaH znmHFrw{?1Y%oTBdq#^p+^9n!B_clS(H1bs|hdmPA!0w2C#kaJH_OP?Wiy26~fx3Iv zWdlixnvx4d!Q^HK*5Y_Efzh-)=BMXVE~erH74qe(JjZnd%q`u<%pwZn+Zi%$uP<+D z|26%3Wx1N})Ca7rpdQ+#q(NJD_I2I1v+)g9pfi*n+{a9OggBBJ^ei_XIZ78NYC%oD%;)x zGX8>IxKnuHYxKLba8ji_T&WC=XUCY%Xzx*rTfAkR>f+F8hwkHBNUZRr+n`B3*X91a zT6y3E*#LXFEqPt>G(CLo(e9?W)g8*%(u+qWlD!xGp7vyB{y^kdVt=Fd>DTtL_-nI$ zxU+o+G1$To-zzqxl4;tOI$%QHWH|~}MLjV8( literal 1763 zcmbVNX;2eq7>=z7Qb0heawuz{fCZB5hHMTKg5 zDU|nD#(bVQO3|{g)vRRRZ^EO0r&{|DYeHK`M56z?@m6hJbfy-i zN1{3`16PpsNOGG3lev4hA(JBM4KEB=kwrn$#h4;PjcN#)SU@Ge(3C1A4+240%n`!U z0Io&93lcFPDVsIL@ir`*1G1#t09Xu(xgaQkgd~o`VnZND%8)<->?y2F zqazTF0-ds}BJECMS?|R1L^z5N7#@vb=~EpLo`MmWE(Oy9q9_>fidAWpm|nNYRG!z- zicws(3ROyRObtxrm#2D*eGbTF2qhv0MIgfJo&9rks=xy0YeOP&w<$LvV+x3E={6 zZiy+$3nPB1uKcK_5joLphc<feXO^GG%IKj?J$tFE%@o%YzI|Qmn}}b+)=teVHs{; zf#Sed_jRd2VyxIid9CZ5zQUzg+9oE!_=(P zx0p?K!z{0-xbgam3N!Ou;NYR6?%M@(qWAC;Y6`r=Do&<*j=1nQHE?BGc{^iA$X7iz z6&{tE#g<=}jcxqKYi6#uM@7Q0Q51fr0WCmmUfA8dT^JnbC*J_hxGP(J85r$cx~@2X zK*rv}qdSf+U^|ta@@6)jY-lZ?wIZci-V1muTt0|&s4CE1yE$etP!h~uEr{p0$2nhf zR()B2_J!*-^l)RX(6eEYpHcqX&Z6f%8H$ndISCYQX50nt StZ^&TZ$~Bx6;}%rvi|{qSe)Mg diff --git a/toxygen/smileys/default/D83DDC6B.png b/toxygen/smileys/default/D83DDC6B.png index be243b4c72311dc5e25a6f773f0ccfc0228416c3..11e9b499cba6f8b98f2bb22a36125b8dcbedf0d6 100644 GIT binary patch delta 1559 zcmZ`%c{G%39Dc=&v0PO4t;s$ye1x%#eVrIthIG={W{9!Oj3J!zVMfYMw`d_$)Q}d1 zbc>?fu0@u@4P~wFO;_1{_v>`dJ?H*$-}9XFd!P55=lq`M_j_A^>69)}fkgoT21MBa zw1`DJQJny|S}e61jzKv-($k*`K)N0PnF0Wo(WlI50Fog9X2SrmsQ^HUeY)L`41fsJ z*PG@ROVDEIXzazS1gXmVD#}h*B~Dc)6f1B3UbT1aRvBys5Cq9_*Pybw zL4uRF8)Q40#vAFB_}G;BlG%_h%Sh{yfIf4Ejp}~_A({UKAT=)uIf#Sxl90P{HDjIW zxyGljw&bl`KZMe?)`G?Jtc6+z+6=v*6m9$ib~X!C$FKHwKk=u7LjqAZ&QK@S+^`sS zwK_;53hc}{MBOYh#M-8HgP(*Z;F;>@`Fo5spJ~oaMAPu8&IjUH$k@Z&++5^s$CZQe zbrJ60+m9myw~)~Xk$M`HAfddm+n^pHe{sJ zf_eVo`|)gUVpL2V6VFRyGx4eMY_xyju|ThD!w_ybDNo%AEB1;*)@GFpjgnT)Q|*f$ zQ0VS~XJro`!iFy|t%)DZ5AAj5md`xPC+ej%&py6CD7=|G#DJm1zf*2eqfYPdwYq2= zm@BVAr&SL%9<>bBP-<8d?g{LY=4-We8wFSza~Jd)NL_AfB2iN1gF_>h6(-Ysz7{|a1G~+nH!JWcubXbdW7?s5M5QV z0BdVrY^fWUgcYW^s;ju-F$`~8gRs&$a!0seAjCAU5f`1lo|?38?zpCHiPDTIy$V`? zAOng+)R!dOf7@z=7p(3#8QtKY<9g*AL&)E{k37*LhdU(`(bZ5n7-^gxrs4T5_qjyd z?OxZx%s4e%{oVRaBAN83kX6-gTVavQ5c?y_G?sorPd?l&vC!TkrQ4gKEp*(cn%yCN$a zlz%ai*c78|KxbYQjNcPPVsfK*8kd-4amvG*yPno&9&f7clzKg1r*E$bNq@4H3cfUG zf*%9k@DX(k!LLlQyHEDRy1PAivaw=aR^|8FcEhr>bL?0#htI}*A7V{4qj*Bh2}mV|5QMedee=sNU9 zoa+{2Vvou9y}nMfNG_Z&C@7fsrecZl-y1&}Y3%*1UhP;^WzWy5@k^jLaqfhR;FMJ% zc14Baq{7LQ9QiCDE-qT1W{RswQc!@iXo2(p17+1{R`{}IF|GGe0A a{$G$$v?0Z$bgRB302DVL*Cyw%?0*2a@uS56 literal 1719 zcmbVNeNfY87_TB)Q4ky;B1$L|aSClxT1iJmXj3T2hiz4cill9TjJ7FhumzQ&8%zYO zs5rgh7gJDiaw6(f90+Xc4sa^sjvZs_pojv3U)vbxQUtd@y#29UlIMNjJip)beaVZB z4zss&w_`9E_R?^%jFyhphdG&kdp>)O(qcA+#8Gl0i87)j#t2apN(_){(PT`9q3VpZ z8(0v7VUvU_;;6W&e!6rSbO2sHZi6!GYA#31rEepWaLe^?t z6emhA!cy??OcIl4Mk`dAsVZ2_3SJ5XnFKU}7NbzWq}AvQ0+Wz6$}6DP)?+pc7*(NC zg{(JD#YM#eB7(#Kop$9~AKH$3=*Qp7kVWG7= zZ=w}rB)$PtOGrWsjOJH>zr#Mn4M8|i00>K95dzV1i^PF&NN6A;;RVvJi&_5aBk zn|6k6ZH|96%lHx9f!5{Q)~7dbhX>Qq6GPIY@mu7yCk)0!i&Pw20`_XFFN4BvrxVL{rznl<=&A!VN>R{z&A4vz4~Q%Z51JPpE(^YSyl1v!wqKV z9WhLg$`yIJwzp=COh^+gWA56oX|h;aolYISbE4D1p|ChY*2A6Sh&-)1d91SwY&N%{ zZj+o#D(=bE;RP9WXY7)E9u`Mz@|-Q-w)^U0+sJmG5oL)If8RWqc4T3Eg_qE#;d-Z9 zqU;W>77k?Jw8YGN`TH)H&m6-#8Pgx(KP6n86_HT5x^F{*T~4o?7ogOe_Mpmcud>6; z#}|qp_*GmxxA$@IFxj>5Ui~-a9xE-qiJL9C8Tq!u#?ym8oYernrA;+6_cps8-wkE! z-R|z~+DBz&W9#^KhQe#q)cp&#zrHS;*?pimIq&i#U`NSCmxL=Un~JL2z%p>JxvmX`tfNN%)HZb)C z?J1Q7hm$&xBf!D+NJRV+Zy{Ks2EjTs!2?9tsgvMN8P!fgYSf^LqAzBP$*qe zm$q}-guLVHmwwlp&s&$Mh<~ra*ZbN0%E!`%jxQ{SUT*rdL-%>p7Il|>J)*c(065nz zj%8;IYpAwHyAfh}^*L3{_oW@ZahL4Ac7GaG-}!{YoWHdw>5rWg&G&1B!wzWmj+O-U zM6`WEdUv+3yVG2x*1WW^s^@9zgOgly$HAdfxt%$+XI3VV9~8y2>oyK`Gk9kVCGHx> zgN-+8Q`>iUrfb)@4b1_1d&=M$cRd%G8-nY84ZhmKly80(bAB#+hJQau|N)9|o<;(R*2cB~m0>d^$97>)#HlmM;JR diff --git a/toxygen/smileys/default/D83DDC6C.png b/toxygen/smileys/default/D83DDC6C.png index 9a262f1d9f7d192f5b693fee0beaf9996b880e49..4fa7870632b14aa2d5429291d3f98b7cb37dff00 100644 GIT binary patch delta 1509 zcmZ`%X-tz@6n&IUun0KppjKoToR1a?c34`VEGo!W6j>}wm5M;Y0tKZXvI?U@i;BoD z0|GjXWmP~>S%d&?1W8n8KvXEyq-+IQ?0m+|B=cilZtlJ3CFkbc^WGRb51p?GO9B9l z%oKf)B6XVNP6B|Jw|z528o`(_S8sO!k_`Yz%>ZB>xk{Y{ARYo>jtT(bCIIS;l6Ef} z03;519`Pj$suSj`;^%HN`IT|}a>i^~%uH#ts4NJ!21Wt5#bR-$BaU;_CdtY)9%qzB zG>1afn>YAu4r&4SOGlG-6tpA;h zj79=jHLbzmYkln3`nU}qd$Tonqd5y{w%RVQRYz`haKGoa%ZLIb&hFQ<&{AhvnB+NH zW!ONmX8I|3Qxa^fkKd!?g~z%zwH*a-w=;?h$o*7MN$zFegTCqu0$bHR&$g@MMr?;@4$WlIKBo4A1t$h z_y!PHfb~lN5eKk$fV)6=0f-+WFcUy<<(Fgt%!(0#vn=q#C|g@w#4C8F;|1cl1=HK( z`{Vp`q}$dvpYQvTyXU&+wzfnG+v^jjFlV1lXL)bht;-4%ZkAJ|Uv5t2mcVHYR) zh?mQL2#{sExszo-%kGfZmwS3uB?kZ)#ZLA;&h!pR#4zGwPSMZOFw7(d4a16J0APLP zI>n3o4%)Ugqhgze-uBzHHD_hKh%H)6nbez)@BD(lm7|xz5`rSOc#~}ZVZ531o|e26 z`fJX_z4kcs)-%x)c+KH=ofFXonv2m|+&S-f*H=?y>GlxjM5Gd zV`(L~g_9Gtuqs|w$v=)am?n4G*ABGVY~{^hvBffjykrgcei znQmQYjxg1ptR_*92_8`Ms^et|b{NkbTzqJ^Iu3pH^qU?1eJ#$V zrQ0g^&!5!JJH(WdE!F%Sh~16fdoA>>VsHa$Bhm90)1WCkci3*t-?H(|YR$!q9a(Y< zS~P9?>xNs`ADv92jYOYlIdxlo*p%C$H6Pd)F{@YN+CyA0IjpauZg5Fer_A;tu?yu) zsibT6q)JEdP1*$y`0hRu1KdXL`E*6J+`9Z&3aD|qns&{|ZTp8g?Km;f4h$*Z{HCZ` z;WeQ;_$s1A3x0C6W2j=Zz*Fct;>!)Z&e{d?3_kg-cJ>+H%HO!l0S>aEkqKN0MeTc4 zdP9}mV;kG0mvhoxML3%)JBgQ`IA(O;*)>Rhx$6XpXj7|-W6Dt~2A?Q@EvDPQ(N1Zb z^jHzGhLY^|>}+S#pP$*dyTKPaqM9>ZO4Gj&eXQA1-YLvA1%F63h6kR3@ z?cC4D$$k6?lno7)B$d@ckh6`h zLQ~fQvY$Sae4H5`!lV(x;%FgEPa42NSgaXjWoB;SYYq``mIO=eeh4B!kj$UL^#3rT dV#4XClK!8NnC@!N*=r~dfE(GvslkDo_BUPgk0Ag6 literal 1621 zcmbVMX;2eq7|s~AG?t2^qNtc9-W`+8ZVt1N07-Tusd6Mlg;uOfvOs`j<7Q(L94doa z3Eo;!s8*+-R?Ud@!YN2;#agf+mFid(3V2Y)3Oax%YBxx1e>nc=&hCE4KF{+W-}|m7 zK6YLpX99=CVg+iWQ9UyT`JRB`%$wWu3TK888cU=T$W+>iQv^$CB2x%JYr)e9J%O9t z%WH{n7K@!~HYC!CF$?5I(jvfp7=g=TW7sTKc%;jQ8#4(SNFmb9Rt2xS;s+jJHYs?E zB{9Mno0>>BM`u$+LUyddn4M`vOuWblAlxNq1S|xN11?LJ)h>4_cmuj}X71YtdB6aK z&Q$PTJCzs{52#6s03-qt-zbD2Kq?hLVu@4=%?4m01cO56mhvI7Tq2djbAZ8%$5^9G zsd7E48MMW$6ufkrw#h-z>2wO5A^}OIfe?ZqJ`EV=GYG!jZKZJ+-)f)YS3n87kuuw8 zGie2Uig*g?pcOpE)7K$bY%wu!gst|$L@_A?UAPT|1VYea@x?WOw$pmzUpL;0wj10w z0@M?B(m@%S^GKcICo{Qww;>;p@kSm;nVCbuvry9Lun<;Siz;}`7lFxal4B48VXz2; zBp8fA5G2#UBAHAiR7p^k1Qug{jkj@yh)SiwL}Em(WT+S{Q%fKvCWK`g84RIPji0Nv z+G*TsB>Z;GjNL)5=$%}-nj&zTqzojP<*$JFbdo0R>7)%%CrE*ri_KOO>9o)C9nb4% zQGznBAWRyHv;YJ7m7CvU9~SvM6+?&yQDZO@w;GipN|g-LNMtA^M0tZ;)BnjC$T$P~ zisL`U;@@H_&^LWk`pn`@^AJ|1V<@IIwpVVAWwC~=)uKv+>w2lq5}kmLS9JHaBzLbk z7?M%UohtF{+jha&mRLdR@_&F}k4OnbE^ITgShEvNUUyGjETa zW=T5Qnf3YobDcN4tlbs+kJY?%wYKffwrES3cyluO`4Lup>{;1_9V!rkJG0vXYzrUt`EiA2* zhw-(V^d!%SXRF=I!#kTUraXR7T5huHMjeMmts}#%VcuG{l6#Rld^n}I;sj8-M!0O^ zy6KJ)857H@U8UniJyl$sy*^_0^?7ZM4NHu7S5h;uiV@_ zV_jL(fwpU#K08zC3~(ss28R9#a~qQ`>$M?s8~0_*3B%(e@{cq{_4kY-1r-gdA)y`R z9%s*mef2CCzjZI`ukxs0;!d%vE|)a!|ILvxY)@{r_*C$&mYlG4x6mAAWB=F(o3Pcj z&r23O-*SEtf5OC-&Arnvn*p%SJ8olnwdc6!miYa(1vef}ZX;@PAEEhW2Pf-(`Y!n1 z*vBYXU;L!{_PEh|7OIm@w_iB2>wNBu9>K?Je-wHXuCD5OFFd%|a_3M3&OUK;c&6_B z%MTYkIxT64>bkMCaN1q=5m6tweQa~p*^S-~NYU@*Op@jYa$6VI%x#Nbvp$czsE?!n kd9UW$j^xoLAuj`jtjU$5i>fvfBYi)5Ef$L&RV~Z=2aCpWQ2+n{ diff --git a/toxygen/smileys/default/D83DDC6D.png b/toxygen/smileys/default/D83DDC6D.png index 217da2327bfbf64c1a4c60ed57583b5527d1a6c5..caf627ae0f72e1a61550045310f75cadde9105fb 100644 GIT binary patch literal 1664 zcmZ`)c~Fx_6kjNZz(+(4=b**`6i{k_C=y7yl0XDfFBjzqP^(q8rfDxKYyN1@7KUqE;O0AH4xEwBwy8@D$kG8}-+^#J7L12B)e za>f8i!viqE0)Th|0BgaS_8q^>qo@ep&EeE~YG4f`q?So*3?r-Ql-z)= zQs>R}J1Fl{q1m7p;UA=R!@n`td;t1+!^53(&BAwIX8p+xSpUZ9mN=Fa1tIuIyLRZO=bg#`a+=u5`dnUUs93Z$=?1{=13E}9?!oouMNb|b0 zsWE}7YgfUaI^f8Sz3W$3G6Dw`oIAn33^QA}J8)hR3-jvcD+G`m1H-q&eF-o)2o~Lj z)xW^pA7F9o5({b&Oya`4et3TTU90q47pGg^gu5RV4@FnONIGQhM7h9Ee}pw!m=F)o zj=pamB}*ca(AzeAvs;Ni8(ADlUwYwxm*JK9HiN%~rc=|oxHx+_^$}KMi2~Qwg<}e? zurvZzb=RIj&DkgZJPFTtj)Y=jqNAbwG@3*&AvBn5w)Us)F4Qm<)50mnT4Ri<6LRVK zh!Ft#M>3=1#F6ZDoIuFuB_?xmVu^r@%SaIbuuXA1CjRaa4mtc1OVU^68{9aMD|r>R z_RzxXrwdn0hO*2(_44m*-?dI%{Up1dzYuet_J&wX)%6VWnb#dVGNvZArw^8=45waM zr!NuJ)#qEiI`w(*R%z;6RZFkk)i^@zkldSR6W!yqVs$(1!J+8*I^u_!8%}IiUYqUU z*Z78#bG)>9S!3;MzrL^D!1pZsCF47$mt#dz=W|DKYBx5qJUoWD(AVx9VL27pb}n($ z?vhWv$8Cy{cJaw)zc;nfdn{eo%@LljsOmBs!@7I=N!KT?@%Y-VpgfJT%|ZKdTaGIE z-ZL;=t9bT8Hlle0CY=dg%RN_Gvh|L>)Ev^Y_O~pu;Cn`y+vfJp3|HjmEBq722df@B zSchFqi&z|;F&KVKa7r#qezQz{UEL4LnEm*N)h(-w_s6j-D_1Hnw7{RYZFIM=7aI*o zZ)J3Esllp`XkWNq%r?IKUE{`Kbzg8+ti%44Yyw{3b?T1urrmcr1Ie+vEE84AH;see zJ7#{nsadr1YSqRPc}4!J&uyHUtK3X_q$UYLnA=qh+#dbY*XxkvN67v3JkHS!Nk)`; zPMM(8(XQcs%|1EVKIa~p7vLIt_vF>-qr;DH8na0HY8X`D9-FS`-CG!CvoP7A6DOJ2 zh9`wNRi5vE9f2j`Lfw4$zC8}dbeSQ5R@kkYQ`%=dWDO|$3%4^DH@k$~un)UrYvqG+ z;GdN&-?64Oj}h^>*yUVr!7hA|b<5Fg?{(S7bwQmCUsw${Jzmqv=>4j^{uNymJJj(s zbbiX^j@#7O)4b@Lw~$~RpEhsE+$rbvl8V*n-~5e64v4n7x0f|C`a(4N%-RCG@}Q;F z3NDL<6^UjAGuVpBY2B3WIETYh3TI|!mzOCqh$+VO7%zc^F)=qQEkep=@@1t(N;Z!r zE0HNaRmfC^QI|F$EmI~kn#^RXPdH7R7WTCu$)<9bCKvhk?_Cqe6J48=ktExbpZxsE z$q75}Ym#f;bElA9hL5BaDX~3szQ?I&zQG77p#I8z!R=j7wYNNOY2V00v{tH^!I;=B zRu3+xU4CGtMb2z(dpr2H?Vt{0o@S0IIfF3h3f#!^VFUeJ!w7>hFn}{?xM&xJz{Sy0*!#GVTsB8?ppT1=1psIi KI{88%EB7y~;qw9j literal 1737 zcmbVNX;c$u6iopYEfhtp(4#mF&|`%pGucd1Vww%162!0tvDHa3z=+AjWI_UU0jUZI zDvAP?LIteFDw`=hK_5Nv;V{Lwix^DT4learXWnY7@b z#g5ZHm_{Oz90U9%a$=lree5R_?|F}3!o=W)OBHws7LA)=Jwg(yF(m>7Xy6z`j=<`K zwZ9_1B$9138mhn*vOt~+(@ zGEk-yBeAG|q8X<1i_M0706GZKX&`a4DG-y#V)N(=f$@t>SktSc zd2)&0xGiGEC&%Krjz^=JOeU&{LB;ejG>FUPS~ci&3W1;)60|sMrf3Zw6ABW?6md%Gbkkno1LRF4ve02& zpb2h()_}uW6*6HLCG3uK8E@tC#CinAF?}e8t(mBR;8+aD46&FF5QngV`4OmAjhPG| zTaV{ev=T&*#v^J!J*EN1^2$U$m3k~ z|H&DRaE4|rj{g+P#1>J3*6HigCl;@p2hkE8qbFKp9XVzziDWYpAQ6U|ua(H7{9Trj z_>3A;Tjl3RkWNdxo}zi|@tL<*>|99cbw{}B`5>6OAyMvf5?D~=B=AWAo9_!(ZJDut zu%hVOiVO=mab$Vn@dtg{rm~>BjhxQBU{UHK9yr`#Xv+V@M9p!lHM$y&{q{MrbpnnH z#aWuBo0BEK*Ltuyw`#9jDkJ|KE|`-#E6rnVgN(6ch1 z&Tiz~=3gEUxH{Y6Lk^E#RPQ`!V5a}k!EH}`@>Nr3sL5iNaAe7B*TtR-sshmWatD0p zw;!Keelv};@#u=-9x9%3|1fWR^(3ulO?2;J^kUnI#w1KoaB{S$?0T(xK4WH=Pm*g& zdrSJ4tl?k_{aJ}IzBR8j(o%lgvvV-AxH>bYyiPP{ch)ISTlw>aJFjSK6Rsz3Id;UX zd)AUymA-4esDHRK`A=D1={|iF`lxw7|Hs+iZ`|8ker?-jZ^!u6f1Mun{Nh4o3e(9( z)9`NJp8AlozQ|pLIrn5h%1=pM%}0}J*lJ6pW3S@YfCp%!kPG{2k_Pem8C@Sr%FYBj zomG0*H_ih1+IqD3Amt%AG)WU)ybZY+zN1z<_44IMwhvndY8_&I>pEWebF21;H({as z+3ufWb_&iEUI?W|u!$9A_DfK7?{3)8b~}Z&uE}BF>Te1%?(Bel1j{Qrv#ak^xQx0V zy2@(ox#bhi-Rh_WGrGH-8oMvt>N(rpr}X>b*wfyZ*$r(4=MJ=c!#9$0Qww+L0z12W z7atWp_-(sSqa~~%%f?Q8ZSz&H=!j#N8h>s+?X~i5HgZ1roxhE9vk$K{epgpy%#7}#kE&1f%i(m3N#*PiSEQP|J}Ts+b*s)}aU`0N7 zC9N-eWF;g1ihIS=rGeD?!n$HXNYBd)8>WJ!`%}Udm0|w-tUs3kX^^B$v})5oLmjfH diff --git a/toxygen/smileys/default/D83DDC6E.png b/toxygen/smileys/default/D83DDC6E.png index f389f634c990fe2f3f3b045f389c11282ec5b14e..b321f2174fdec607c811dee479655b5dd4ea9109 100644 GIT binary patch literal 1786 zcmZ{ldps0a8^@m!BO$oXYizPl_8VzPFIFwpon2Fct1!E=LjdJ2KY`;Rc`7hQy zx^)HZe;liz{UfnyxY#s2kc+{$78t+0ZGq)E>yLBR5c&)#0`RB=`&4i%0pGhoZHADJ z=1TeHk=@O=y9KQi><=|Nf5`G8v0)#w+ElPG2hfAP{3(vEUS>Ea zy`y-^o{=%y1cO~V8j1=?=`CBN4rnO48EcPKCI6HYTf`2nNsD||6w_P6s>_ZYui-RZ zPki^H>x`Gv+vlARZ>5WW?-EaRj0^~y3KCn`KFg1|bA!LmiH7HfIyVOIi^rab$De+9 zSTK8sBktshpYz52_r$^)@o1}fq;-bR5oXXf9v6wbtHix`<^_3!6_*wS`QpAiqVjn0 z(-L83)W-(};@%%c<#BHc9Op-BlYF_%JY!Kh`7Zmi zXTm@$e+*?Pi$z;-CtbpY!n)Ed^ zx9OqM5VyMdA%%n7XiRr#U312fLp2xuTP#~H^k?qr%I}TRE<)cN0b7D$_NKJXI9Xje zX!D%9BjTQ&&hRuX1Zn@_Lf;4uRcm=Qs}@;5<1hX65pRh3{Xx;@fL@zymq78o$&^P0 zyvQ(@iukFnjFS66M?6rIf;-+cy1Pv8yacPj(%& z$soL$u0N|BIJ9-2zrA2Uuguf^EmdWI=wdZlb?Q*{VHYi?yNN%3f$RD@_sAdLR3a5I zRcX`=?3*{v5xLCdrSeQA4%n%7gU zl2F=DiczxY4Q-6A{g_(c!0=Ji^uGk%ghCyjD0hDD!1srvEIgiSk(-AG9&kJKrv5YR z2t3t#)AM-+RT{4L@>7UxZncZ=;I6vT^*bfPFH!wlm)(ho`Tm z)J=&pzl;fPtq^`w8^tsG8%sYgpm?A2r{~Q`9lx%x>gSdP%}-zPsI>Ino|7cEfp|5U z&eeO_EqOB$ibNdNL-QpyFfcHPL7^L?X)_`kBM77zhB{PLcjI&J+nn?x7)a#a&|N;3 z7iAqB4hCzSG(Yc;v{uqN@f}k;z(zZ%pZx@th)fpczWCRNt6g0`&T@xy0})69me;)Y zL;LNi>#v$v6f6PRnb}+WN9ky0=4fe4?H2_CQe}YCRxz13IG8tC(GYDEg_IMfck}0q z-#sdx=dUBbl}jbeRZkbZ7OZ9qUKdQ0iU>&WhjoiL$A8(ByVQDYO!YloOLK>w%S}wo zyOlX(2C2x3s4~maeec~k(_3cFTr7Rtc>RX$O;^{l$AJ?S_-z_TygVte#q^V|Q=vgX zJq@88N{d0h5x3{_^K5NSAy_07N_qS7#T<`5R*m408Yg literal 1790 zcmbVNYfuwc6pk>FNWr0sR*}lOmI?w%b|E2&1W9%i2@)O&C`A;OWD`=7Y{;%8ph!U- zkSdH;>G+^jG>XDl2CV`PFRfFb15|CLh=UbGr%?V_npT* zXLd_M+%ivhA9n_W;i-s5RrKuT9IUDI+wak9jGpFFvJ^^9=qMXz#TgPUk%~sgs|cOj~yDK;X)9==d+9$y5918*;o zwr17pL@HGN))p;^!7Pfhh&UXZ&BnHcvI(o60||vfCkG6N&>A6Ro|(ezA!c&nBm;_* z8mqxV83;4rWW+LwwUij7J)H=_WKk;L5u3@kiK0`+v11kv#O88LCTCo4v`I>Z|KrAc zwaKJB3(isDB(c`2q0d9Na1u=C?!OH=6=`oo@m2$UD3}o?G;2+`nNpx)kp9Bf8nhx9 zg7~OF%9l%oh>RAb2$wI15djZIxUf8&H_7onEFXcSQdq`CVN?h~GFU1{1u_XD;6pOL zTp*WBVijhR!ps_c(yoEFJAp;z|A`eLRve=UYZ5^iCo3Q!i=YTHi?9HQnhyl5G?=x7 zjSO@i&qTB+ZZ+iMTDg@l0dMjvGQ7vW01|SgGK4EYP}mu_FdUW%5jkH5g@!}06b0X6 zwf`q)9NHOY<*@^9KucHW_MtBX%m$^zrW8hO z_D9{?3XB_{_5=Yj`_2b=jk6c}MjUDt7FOvi=SMR1Pk4gkrT1JegB^Qszcz2w>-?y~ zaSYCEWbARxP^bocGkUXsxIV|5=h5+1wc7&Mgd??wil`k~txc&LezA0StCp{Mq_}~4 zG0I(cBxW>_rON`k-B;7A}k>y;_f_9;FtCQ*kWZzb z&(NL7rydI~G}XJL<<3j1VI`i}uAUteF+~WMr*G;`aTyoX`775m*Wjb(A z?Edh8J0%CB6m`)vAD_z!uHTe)Y8KfTsGnI;Lbz>RF#16MbeB0IXG!k zPxgB@u(vTGt@-0~^G^D7JzvtgA|-JfJ8*NqV@-L}qUNf)**9k}Lz0_w{kwWY6Hn#^ z1s^SzHRp1*^Qd@T{94JZ`hdP9CJ7b`rQTzsQP+%c*RaEKFN~Kga}50EyZvre&Ca<# zYEbgDSQig9lx;PtJxbMM5cBNq4_7x;(D%CU@>7>awDFV!&s6bo(;a8#4UY?Fpy{@c ziiGj!?uJ1qJP^-w?e_|X|ZLo tjt4C4gU;pOhV>v>U>Z1fzWr4@a}L9IFlMAp-dgAUBPnEY=rL(V@!!F1rA`0< diff --git a/toxygen/smileys/default/D83DDC6F.png b/toxygen/smileys/default/D83DDC6F.png index 6d1645b49261cae40129606e59e6d30417292f5d..95750849c57d0f7556716326c3f7ccde5cf16938 100644 GIT binary patch delta 1443 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 diff --git a/toxygen/smileys/default/D83DDC70.png b/toxygen/smileys/default/D83DDC70.png index 131117046692c1ff1ab1436faa0c2213e733c7ed..2125032737adf214e770274461d8c2fcc679b936 100644 GIT binary patch delta 1841 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 oZ$o{;#)>&zA(3%z=8*}rX=~0Nc+WUwgmxMk!d zBL>q|8+(helXPwr)Yu!KYTHo6H`}SVnU;En(#XQA3a8L#S z03H3GNRqbinw7H^0Pt=r?D@$`vQhal)t5=?@sBseD-!KWknsmS_dMCISZ@2lEm_~ZY`I#3N{Cn3Hs zB=s@S ztV>4>QG)g;=a&(L|DrX_={uUzPqgId^_5$Wab2gL`%DWdQ_Yt}Ph5KLnY{orG<(af z#X`z#*R{Fs;JNP5xmTgH9YGU(ug)U0un;49<~{NJ@}z(w7W#=>{9iYEH{UU8E5|n% zn%Af43W_ZT>fHvoZnPYv-^HL;)LHpufB;(50JX2-u{+Q`7J@#%sBdv>}mC)<#h zi>XP~7nZoxX6W&9(G9mjv!)`8<;pmyy8;@2_GP4Qwkh@Heez?vCN$r*)y?cJb*}+yv1dozGQ`OrU}dh>-Rfg8-ie3s)Y2hd@gTQ&VPe3ZTKYVN zxX#O49c^2i7q3nB?oJB#CWRl~POgr$e&Xddr8uv$lA(4c2o2Xm*nOJ{6zqz^fDPcTcYd29U^&8#b(W-iwLAk^(>gY*!5W;eG7ZEg!_??ZSUpnEDm4U0Scch+6zHMC!ypKf7w|E0oXB zD7{+)KiIbTu&E}6yP$!t0H?FeCr%zH&T|7hnJF{17i5tdGuW{ZMva~HZ^t4{*Kf0r zGaGX3%F7O%@qE+zi+b6rgCbgU*6f-^fbU55+WENOoz~wve&R06|7&4uX!xY-vv{Pc zsh5^7H;f-q=opm0zMiCnBM?b6rRjX<^wL9bv~>jBRnFmp;7dJb9^jAW4qA9+&08%N z_R;jjAI|zJa7T_tW$w`7c;zhinJo9tcXvz%5^xAw_BbNSaVfZG19R!nV`R^^!j9~~ z_TlGyNJGmm3FE(IH63%K2sT8lev#ay$_xk9JZ2nFgsLh z=C~i1NI9;5>8CP*_$sKQ6RE~$e$L(s^=Npw)Yc(fABj)K>%CO+P>LC&Z>4dT<{5i) zPmy2i6Pp%@-+npYAA(aXh)x<~4*o^jJoD{~%7%w^?553FHL=QOx3zLlD$P4Pw)YPM zHAz_aJK9>2VInGpLJ9B>@-y+LzMo(62c)j6QFmNKpL$H`s-_Rpg&IEe`;Wc%m)Gnv(i&LEcQU literal 1800 zcmbVNX;2eq7!F2+A_&xo$9OCYqKxL)U_!DIA;~5XB^U`P6c3hUBP%2ulSLA&Kmip| zVDJI~E#A~pK}S)%vEYFyov{wpC@Ko0h=a6V1;uU@Y=1cZ=+5qb-}~+RJnucbK@}0| z<}$&BMx(hY!jMR6^{_wABdGV3zdoSUGM<#hkZM9lrlKZ{Ce{)O7@*Lj%dtod)mm3x z#e!%wx(<(uA!C%`u!hjHP`eJxqBl}#8Z9W;Vnj7b7zrd`%W;DU>^XfN1aPegT*Omy zltw9*h=-+_Fm+l)lqM}nBh-SybATWVOcCfY5(O;!WP=&Dh`^7$FtxWIvq9ja3YjDV zKRFenQ~^@LgaJHO08_((Ab`(jL9=*#J~R{Hav(07L*0BPGz;eOVQwHWc!88PlU4^u zBJx37lq3QZNzw?j*{P|itkeJ&VOq|HghHX6gUe-78ceg*K%y3=!R#}{fM8~g2{)2B zVF2umXabQ!ia^TKPa)`yO66x_gLyDfRLa;E)X0Wd9JXF>kL#nhnT*8#b>oZL<|wNX zV@G0UBE_Vk>Y?))0#mvBx*@wFQG*5>vWru82eHDhVqvKXLrKCEMG(nD9iU1iNWz>*7y+r84@_T#8?;2K+23BC zPthWn315Y2PM{>r=w#;lT{l#F(hjfLG-)sEHFO5OI{{&SG_3(&#wGfUafN z)p532#eT|}3zO!mkMD`DcdE9?9LIRNPkiZA{;O5saxU>%_8q4MFZVxiudK**cF8*7 zBtDjvfW+C#_bw=_#igaXQokL(=9SmOWJ_vv?yJ|_4@|0lpC2FV)qOIZe-Rgg62U&_ zs(D^kgb{ldEnOjNRb$sIvMX^pR!jHdzS$cjHIBJ=cROF)8FDtmx*>huB!>OUNOG$J9m5PeWRNzLPxX za&7ITPVbBvtBxHG-M{csOdtJHWRI$Jqo%)2Jw4o2V|n5|C%?*PT-gjVkbAc!p6s{D*@4lSscZYRWI1uY8sjIqLx7ph02#tyCxYKuF4QJsjOEcD1?8L0EgNv0y=$l7x920*-(bhK6(qfPf3fO?PhH_~U(Y1i z_M)|S&A3SlpmcuJcf0IHM KfgF~^XZ-`kX|ewR diff --git a/toxygen/smileys/default/D83DDC72.png b/toxygen/smileys/default/D83DDC72.png index 86dc3256695f6c8be00e4b5a5a06585924b39c50..23686f7425833ebea40b95b887c5c9a2a2833f6b 100644 GIT binary patch delta 1653 zcmZ{leKga19LK+79&1>$Msm}`M0r?}v?yaMYfZILDe~KxNtV$(-|mF8%8WeLN){r` z({&=IB1FwYgs4s`Po?OAtDDO2uD|Xd_uO+{=ks}gKIgpu`FuX-d?rzOs5E1tIsm}J zTA!&(NG^#^0-#E==m$$fRh#*{Gw1-sngbA@1i-wi75^512m%0;5CFtn0C4=v_4}Lv zK-BEr?MZ=Q_%H^rF@U!W!8HHHF=K?EA&A5PRb(#(Y{Oq76bFtQ{SrYqAmrjeun`d9 zL9j7k7;!@(luXuS43s3kug!#;^FCLnz^(bP z@_KWk@9eEqwxiWn42rk})glT=KR}LlwiT)ds9Kv&kAJsny;cH-2NM{_wSSy$3z`Y! z^VZFOvye((eg8Z+HN^cpXyJ#~)Z~7t)z1Wkcp=rXV4}p?hN1Ey*tDHwAkr{(R~1?U zDxIP=uBE4oM|5~yRsf*(N37>T0fQA~#t#n-JQ~6=6U6X2X3;!85CTVWme=9lJc9N~ zF4or-sTQ^+xZ^N)esJt-{BZ01&|F1VE>io9l3I9mSopX$`jW#1dh%vlhl_*3{aNR$ zyiQ1YIb98s2w$y@qt!>P{r`0ivQz2s0`DaZ<4pX-bcJiBuTKH-qG*CNqdPila_YGrgLMt;A zBZHdv4T>f$KGfm%K~q70?d;IGeeXH>VE*7hx`*3t{1TU#4fk2X$f;2upJSgsFbwKv z7Pu2l%%)1XHlnfDbo$9y!M5`|o{o<^Zfv^#+OKgnY*2@~k~1-Yg6oRsT94d({j3gjg3c6=aedB84o8(O`hBK?KgFhX4u-LxcF zegb)nROf=4O%^SVK9M_#G-=T&^FQt0)tK2%a@W~)$gunmv+mGQOy!kR@8Go8R)fAh z%f*Sz)!$fBLA{TTMoypm>WAKEpUc!t&(!u8gvwf13P-<1qyg9B`vXo?O(!dY{;q6H z2JSme=dF)=q0xjQ|IQ}GkmsLzk{Y*(I^A`Rn81vDYS?D9!_k3PDL-0ku72C$hktQ8 zRHC@tHnU3hT{<2d`Qn`IyS56KNZtNU{Tn-?>`b>U;Aaspso_QvIZxubv0tsqT+MzCzVf*j zk*KJTt5*=WdYa`iy_g@T-hX`W^>pv)*c=j*7MDkamv8wkXWn~3&~>j?xRfp8pI@#{_eYv6?j91V>5f5QrI T+{g5lYjpvjQas4zu28~183WaB literal 1770 zcmbVNX;2eq7*0j1TpbaZ09IIn0xCH+Bq50jm~0Zj66CT35H%zVi6q&WED&ftKt<$I znL45lqo|c@6j28cDqe-k1Qh`ft}2Jx+7VDhEoV0fwm%$ybZ2+J@BQ|Dp7);J6B-g= zW9e*(!{Ka#0tMmNy1?|z{~CL{4o^w3#R(Nk&}O_l0k98ZqK!$6Q)nh1x(QhD0u zMwo-cnI$MAC8$Ik%$6Z)lGLO_GN`o}8i(Wf8?;hc5{v@zaH3MfB@EVHBLGS{mk>o4 zQ^Z;bRwx70b#O#_NTe)1Nyd^B{QUrqfsGNUVN?nj)GCdhZQv57dD+*(=@2mK-B2nXBh;rUZzuO zQ6-`QOpMZaBpKxrFi$^*pw^1TpNKX3nM7eJBO9bzGDxD3)oN2*)7pA89RAmh&uZ%< z)3h)-9M&VrIvG}v1ov4mmb)(-GAUx-u-E96SW%=Z0U}FQ!x}V5z$IW`NOGl|O$9-Q zfXQcwcq~YW34Dmc5K$o}oeEK?B47F}$LFv-8pBt>fP4j10Sg3$R3=kIXYpt}hy{VZ zG>AEi4btdQsYV9R+Erq9XRwSfV%d-mmZFF*5fu`-J*2jcT!vkxuiP2%Bf!Zl9V-x2bB;Z9FI#dzS3cE0S zF8Pqhp@b{<$SKzS^7Zl_?nt5T2gjo7KC4XFj2a%*(CpdDkNC!^RhE z^UkztlModSYENRc>TuS{&4o?OzT%9sGW=c6z||MUV*|zSi{NOlcEIpvaXlYk*eaWea41WN4uXg@( zt3#ZvnY+23eM9@UVQ2E`B~Q=!*TN~xe$gP`)$WaznPcGuCF-uAru^_jC&wjIE9>#0 zb-(9V)-FvMI*41lKJulPb-RCb< zJ+OV+%?uw9IR#iW3>%K#=9evp{7&>V*5L1M8cXSV^ZIPsRy@bgw}9>Q82_t+KJVwk z-932WfYaLk9Os;9yIpw}$kOT+>nI!!+j#Z1*u$o!zvb&TBUqVX28q}0K6RyQyf?-+ z&bTNnio63q*O2CF8ROFNmgXVbET}8!-Gb%}zx1aawW7bXxTN?-)LFrLJ(9FC#yJ*W zLM=NMY`s0j%w@D(IGSc|lTOVD=49^Q3mL{wZu1_0nfu3lRd;oXXH^ou^_d&TG4Hx{ zr9v6rT%GPPbPGqbnW(z;;qBU8#`U8hagD&v9N6Egz~M@60G6 zFL=oh%q#z?W?#0b$MZ=+=id%@&hvf@v1}V*dj_8JxJY@LGs%rxv|Mz*v(={P-TEsV z^LS^5cxU1z56|bu>>(UDa=hB@z-qCJHl#Uupl7j>Hkh9;#1)Uy(S^<6{_LJx!=-^+ zb0b#R`&cKs^!4=tlRNIbaP6K9G97@o$r2JF=kCQObBUF*zgcjwNaFU!o6UNB4^KO=Ba8zkN5SYcLfq zb_@%c*y-MVq@#TmGh2er8F8~Pb~tMXC%4qLnBUwpRo771-PL+O#JKgQGk@PI=z7G> jXRjWOZRjPBExV4J>JPJpO52{B{yss%5WyM##>{^JY6!1W diff --git a/toxygen/smileys/default/D83DDC73.png b/toxygen/smileys/default/D83DDC73.png index c5aada50627fa48f769702f447ef80c0d3c7cf7c..8d81068052f5798588d1f793f06af146d3f5f377 100644 GIT binary patch literal 1769 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 diff --git a/toxygen/smileys/default/D83DDC75.png b/toxygen/smileys/default/D83DDC75.png index 1c70b19ea4b953a4557d21fe7658e60f45faab45..78898d7dac61cf9e99d21e084dff8be679928fb2 100644 GIT binary patch literal 1725 zcmaJ?dr*_d7T+KSq^&4Okrbv;uM}>F8k9#tpkNXu6(}a4@(}XiOHkxd2#o<_VocE# zM8ZoU(P9dsC>4+rUM&S;hj@XaBxx#KgcLD}D6b1w4Dj90cBV7^qxa0&-}#;0J-cVm z?#zlKBM!V{gRubsyh92lkP*A-O|xEypqqy+1u>Saknj)y8ZW)ILR*jcSVky09Dp2G z00bfcUL#!s0|41L08CT>{I3Aum?3LF6aWBfFfuHP2*Yq-U?7P^ii(Pg`CVu<8k5PS zQmKEq2r?K9I-QP?*w|QP6cL0VZswc5m}9>SGM_`EAefk#n3fogV0?W1RTdt>xVShu zE94JymAMZgLRvVYf0FA^(4Q(#3jF_yWCB4Dl7mPjDladW$z-LarTO{!B_$;SfuOLk zP%f9(*4Eb4)Lg!NxvHwFprC-m;S?1WiN)fyv^0rC!sT*#Jf2V}%*x72PENjEo(Dha zWwY7Y+1V@>t4+dL(saP4A^63kMRkXwkb_j?yYt7tuPGgDmYek|ct+iF{zO7Tg0{DX zPWe=?d-${B2K;;oInGpnr*T|o()O*6DOX0ijbqxW{`Qq0D&e7ec(`fie)UL8*-K?L z{HV$NLxrh65ALsp2X4U+>tRhJ+My`A>n;by_euL(%7f07d z3!S`;CLh~LgMH2_zi(i1e#WePr5=35|BwBPrNh7JkKDSs#rVuFaA7X^{mQA8i7PI} zFj<2vgQ6ciKmZrkyM!W+6`K@Jw3@QoXtUF@^OIyR04(@9QAgQi8V8$^na(_Z62h{% z84&hVY6buwUo4A`X{y0lud*Dz7g<~CGdq7;$&ThP&6@?BSuPKsQnnxKoQJRO>Vk*v zwpRKSN9XzO@~O}b)P3m4xIN`F;(C?)o?;{whYynGRkugIAUEe8I-7d0qIp@bJ9^*o z-*uR=3^!qT|L&yUw%_)R>2&gmwZ%zWm5Tb7Hc_Dk>fz0c7x3lB=DdCmO-RFb+~3H1 zIZl=?#a`IaI^kaYKx)lVQj+YdZ05f_X;+3x={p?ix7fdKX7#u-aG=~Po~nQ2?^f>#*fr*SxD!%toFq-Pel5XW<5>k0J}O)z5IeVdU`1<; zE!L>|eLHkhchm(o-TLbaab&}U+eyNAj!U|Hw}vm8UwcehjwYmTKV7BMN+R%KJMDtG zb9udMhR5A)t=jQlZ(-R_SF@gV6AolHI86-hT%g%oI1KaEPyg9CQ}UQ*wE6GEe8Shn zZ&DV5yfPY2v~Ic^GLl>}Sf`ri*$1RVP#QL{w@}J%6e!#=cjei?xTQqCn<yhpNuCCZ#T&+O2*|9bnYZ5l(wjb3w?XbpAm(pTrt zFeYCjuXB+^2#MM{%%B3)epjaJe)Lu<4Pq!-8lh+7lNJkY-05i-=c~>xvSX)~1*gxT zj`(tHb3gdZ)^=BV$Tl>Z_WozNiJ$N{^SeNkV6gko`iyIlbKy~~ajTZO0 zsSH!M01=Ib95hYyUp=TZRnEsoJ3v)WXy2NZYtJ<1IwuFs8l9*g=hWNJ3?9f+@@Hg1hylBCyLWrwd_6q9qC9c_d%XR<{656t{BgLxtm5E-H+aX(i<$e#iLi5Nlnb{|#vKg?1y*#H0l literal 1692 zcmbVNX;2eq7*6G=g4A#*h*Y-$EpjB;&7CG<*i9gm5QK1ub|55+M96N~tRx(@6|4+` zt;Hfo>nKu3t>UzbGqluNJPL>~s6|jF(gH1_4&s5@pGo&7Gxc8a=((0!mXh*hG9J$$Em{Jl$@F+S3Mh@*O=ujd z)#v53qY@g;A;X|aCX-dsVjXT|YVA5qtIIw#o#fnTE)G0*%j)(dhECbRsiYWpkN@@YCF&i_Bty0DeubA4~k68?0 zMup6hGF~{9tcnF-oIn8{lM||AgCM}?GeIto&j&*Qhz&w4Hg)quL9Up`7efMI_F_=h z2z`b)4pGe7q9iFJlO#=I7RzF>FfAM=PHbX0YRaZMyNRtBem90%Tez6il+QmI}NW9HdJQ7L0twI&wGWV4J$dt5WxW-<=_*NsBeRlof}X@hyaosz-+Z9GJ@8>kZizDR0DT!ax;8n~mVQEk+b0m57u>4P$vsn3Sv0|7&wIohxa6Egi17b6A5;te!CIF7-1A$2fOpja4 ztL^1^5iNoehFnyyAaElvlV7pn750Uoh#fA6*+LkB>~V_(kX!^S_;QdV0HJV%F^kpz zpPaEMXIS>;_)oLU9Z?-<-@a^pN_aUuC`L^TL5;@OhitAi+Puq3M5eLcB;((Y_FU&B zy%rO8ZG}^Rol+j6*~nci2#G#`^+m>}7cKLsVmc0WRR(+SaJmzfG#0S3W#F7oWVvgc z|Ek&}3ldWf8jEYZ`VHTOP2y+nYn1`dDg;k$c_UvHj1_Rgn{B_exX*X&N)FlkX2z)- zsX>+eofii;272X}z&pC#t7~d<)-`FlzK2P%ZEgJ(dSgmxV9~?ehQzXjV?Ey6*C(f> zL{0mz9d_T-{`l(9gEpyD+I@H8h$bkos$+M+f`CiCHPO!f{r!d%bwyhNbL}v*xP|^? zatbRs-||ge&*pckI=0;_PTAI(G1PNsLaa%U3;m{*SYb;V+rOe%w0^JcxW|OcNtc`3 z4+hf9Mib8u{@P^2#)?Y!#5Zj`aSLJO?Be33Tqe6?l*~UHlP2Z|q_+8Te<>mKj4=|N+!n7`(&_EE=1-)OZujj>yCyC>_ou`oE$sid-$ zj(>dmP~DQp$4}N(e7MY~qTyiuqR*`!yP_&{c@l-E@^-jhpdC&VxJ?T!eCUyHn{)XMdg$q-_h%Y{Hd%_{COK(Xy`GLb`v@g`-$Ek=b#!Bm_`EJdPYGLL0sq$8* zzT%2juj`%FMY)OY_TM&hrNOQzB>jN_{^D)_6x zH*C(E<$YzX4ShQi`i-4qbnHp@{?hlp_wcFzcJR?(A;q_3)#a}9vz{-07JOCx`>IZ5Eu{M7&!EnJWIAWuGVT8}Em3ahsEe}ybZ9iCJO()to>ue^ D-|U3| diff --git a/toxygen/smileys/default/D83DDC76.png b/toxygen/smileys/default/D83DDC76.png index 3b23af4310c119916ccd90cc7509b20f82bd703a..72f543c6075bc05ef5229770e2df55db9656706f 100644 GIT binary patch delta 1603 zcmZ{kc{tQt7{?DW!Zfm_3HOd_+G)sAR8rPt8(S(%BW1}FgHqQR#@L4BDoaSA#v~CD z$wc<3(X=p4h=xhHOev(!{oU(6_x^Ry^L?K8dCxhY@B2LOd!8dgPoUGWlw|;bO`39! zNRT*XVq*e8WC5P)z30OMW&7-j>Y5|UT%up0oe zB>O{7W-n5`1W(F)Q#?7z9?uiqd*YAu#JN2QvuzHsc^qiX4mjB8XN8<1dYQJ3p9}h~ z7*UX`OG1Eun7=WEY^HlXjdATs59o|`Za?pc=x(@O+gakx0JGL`d&GCiddVFruv7yn zv!wn^Fj`8TX}Lbtk|V4>DJTV^2bqxn2nxF3Soa*a;N3Nph*Fn1UB!T}YvD-8$1113 z-vKX6{L`aoIMNPZ*9_-Jz^At$uK|AO{65e++u!t+*9=EGA@8B6G6g=n4g2chNE;OP zz)u`F+y?vVKG$Wwy`S~`ZU(=;;0KQl1yA|)`A-s#_f=&H`7eB3tc5%dzy2D0$A)~i zkWbx;E9v|%c)ca(iqZAG{!@;J-j#7Sl5WH6aYs(<}4s2&}nAC}@v9P=d zK41;!(_t5LB9{uQu2zS6dT3y)Lp}N!{u9jD*_w+GLalw18_wR3axja(U;@{Hq8#~G0lox3n2k{&_;F_N`~X>3XrG({3AvXzaQ z)Tk6j1~1*5rj!JL_-|28hsng_k+_i1;FEr*eQ@M+AwIaXfgu3wOug)UG=q;rFODhg z5*H?29Km*bvP3x}xA4!qqxKA(@MfW}(2n>^Q;tQ$Md#=iZi!0c=Py&Qax32*a%Zl| z$$d*wmAer*5L9a=F;7xZ2zS9A^Esrf-mp#+NBbG3v|OxYzedNE6aHccD$b7I zQC_L9-eT@#RM~z+qDjYoyf#}gIM+x@t(h+bipqK2UB z`Jga6R0fm`T(CX9U>36M9 zv5ffIC|!WJ<8H}~56Vv3#nydYm)v_MqwswibyS(8>8o{UrR>h#7NOJ&l8hhfX`DBd zyeXQ>1zoF`jGH6widZj&D6?Xjw{06HmmgEuwI-toubId-_#jv|54tUeM3fGYI_08d zF+AkE-gxp^QC>MM+1mf6{1~fkvXEg^NPP1s=z6P0u^>Dh5HT(O1?!jnvbQ4bN?C%f z$K-aUOglBx%A+^2FN-AYM~5_C3_itBBKAENzr9{!A-~pzBblM5c8Trw)ti0!4}E^= z;4hK8ddAF%<-xgC{n1++i-XzX0|%K}(Uxmob&`EyR;F>6b8==$spvZLK)+d;nmDcI zdV2wJ^W|P(*#_LudjWS$Wk?`aiOtT*k&H*9QyDgB8jY%kTD3~v!Er~JtTkopjsOSw zRjW`g+GkXc8l9K2*mz1s^^CR)N?+R5AX3h5d+@Jvo(8T~`l!u*#xUk_;m}ZFx;@3% zZ!-$pUooB7!4wLa*@@ExH!S^d-Gez=OYw)yjuzTW-TA{P$<{B7?y`wIenVL|gKQ>= z!V4B-n{NzfWDMVEj=kW6M^Pe_U%j{|O77}P7TqH{D@UL(7`<86crK4uKfWbg4}(G3 z+S;z@S)jMI(W5k1;1Z~^%d5l5vtP*J)h4G=LAuC?d|o#_pXAkrGyzXGJ4`-toa|%h z9qNMwphM8n(IgmXYU?^_6AX9g8S3e5B@he=1k}KE90F+lM+63+@H=_#|B1*rOXEu# Q8)X2nGP5-;-|rRsCwbHi_5c6? literal 1650 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 BQiA{h literal 1733 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 M>FVdQ&MBb@0N${b@Bjb+ diff --git a/toxygen/smileys/default/D83DDC79.png b/toxygen/smileys/default/D83DDC79.png index 5b3e0096730ad078d99a541b39ba0d35cca26b4a..b5b0ea23a620b67a8eaf12186337b6ba1fa60a5a 100644 GIT binary patch delta 1639 zcmZ|Pdod}LS<#1Rr|L0MN{6u$!+N04;@@iYTNq#B@oJUf*V-3|K zY24p}TwoCk7P1zDUB9)%$kMn=&&L#RUN28PlR?Qdy9guV^n7& zR7b=#+Unh*;IpvkP7?O1y>X|dK6~AoetXliB&`=z%VrC#FH$X83wiAmY!zvJ1P&p! z)gN;qF1TA=v%$bH93wY}jw@f)LJ;VuYJeV+W!|OA(N&$+HjHfKre8zj?9@B?R70cR zO^i&Z-l1yr{mNrn!mA&ZXA#W3UhV~%Na+z}pdq07dT9Krp|y^v7Id&50f1Vnh!G+P zj5>x-;vI>JO^C(|gh|o(&$vkdIOdlHg_S=cqyIRJfxK zv~NegpOvN#cB^;IGZ$#EP;vAn;h5oI^}DGFhgSc@fkYGM_ghHI+Y?)#MCPQX_@2nF zXc`ja*Qh;Npu`puuMQIQfAKM<7@dsNldhMw_3di68Z^eG`I67IO^+^iW?w4MMGV#z zp4wd={u^Z5?Ct#2HRZ1H>S$Mgd()%ODQz2xx|6lJ_Wn;R8hb?HD>XGQ1GS^1#F@S)#)i$3jYlf~<> zd&su5(i2yKtkbNjpyI$5AhPLxzS=O_btCJ*8`%k+_N5Qkm*;z2c~=ePTQ4m|VX35Y zcxu|R1v0ibH>M;v-2Xkdq8h@q7Kwy5MSKe;~a%~_u$~< z!8BA2bs(oSfwpgAeuMCXt24N}vEqZa;d$9FITIWcqq|eN&!R66m)%dI<=d8s;)OdZ z#6DL&PnX^bAUp(Ia5EUw3 z+jDE%BcYr#%F(6lttB)%I+{V!`5)7(7q>sX^33Ger-d^f)W;6}vV`w3hlX1BWh9h- zou^$Aebmx;?0e#h#-ovwmnUL<>XrQo)7;${2E(jK<%o#_>cKJS{#!o`boKQ@y?tE+ zsOS7;4ks=)er`7I6;JYeszS7i%JKYROU~o!t3;qY-5>hysrg0Xs?%?Zo0m2P$V^OSZMOCOcT$*fDI# xP8545J4X_k>_jFfu4z{N3;$+tkFaB7g#VwgITX8CY-+v^0GfxNdlNM>^G`Wv#oqt` literal 1780 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 literal 1708 zcmbVNdr;GM950VC4&hc25H_Z0&P;L*a9sRe492YbkL@zVH5^#dON98 znc{&cdMZN{yz@?n>;M%eo=yR8I3I{InVaA?9X?P79BwIs+aKQkST4!$`}^hl`F!7( zt!bJhAFp697K`PhN=CHI%62}U9?W~e^S>}=38K*~T1Oh`0*oS9i8!ez0F@cbCbR^G z7jC#r#IaazMpH%>ouytTHIQa5=G5WZ%~l4@V#USVt(YN~paDIRZL-KX!>w%`z=X>< znIbh$ZB-CCrsN`u&=qMi3`MyH3C@XM0>s&+41t-TF~Dxlv)H6|8E1-D%E-=RkONGq z(77_sYp1f*X@G*H2tdRY#29!G1c=34NGKAEp=f~5gZLnixy3P%P%08j`LV$C#bK;b zxKXM_l+(7Dos5%1(^e@678De43j|z}$_62cMB?P&^J5r|7+ax*#_TZ`TlfqELf8zH z$x53@3*cnL^khCQ<1n7S4#8|ytKSe?Y}1KiQU=;FD+qCUpxNw5gk-D#}gKe18;MPM{ZWsqdvOb4XpkThw_A+3NyCk7U0nk+b3V2g5= z=XJCQL76@#a3w{WfvNmTO>eQU;474|Vwi`D84vgnB$3A|5WZM0Mq^QbS zsx6wK)a+Ndc!<`AX;%c9R(5mhxtkLfch*$vXZx=~mRAP!qwZ_z^nk>8q`VUIi%6fn zx2bP#ojN=Ix9!c%Aw%>B6XRDYn-M&7W_aw!;lisEqtGvtDZJd{PmvC2six#eW5fAl z?_ci>+wJ1*#!gGA*r90u{l(;;dG(!S^9_w7kQGt2$L+)Zn}S>9y1h{%FUu-_@L* zSQ(naI_aK%?ja0Dt(EW7)#@#^j*6Xw89^ug3AVN_?6G8Nc{Juo4=PhchpkrDR+7>( zhkGqH50af#1@CxK%X>~0FcCif(q zYOV8V_1==B>gYH-P#EcFIucm35!qiNgH5pGZpA_%Y1HSZ-dD5kOXj$&J6-LlG(K8) zc+-&+!R744r<2@>tj7tV2b34JT`j9UTMj&tMgsUCFmZd{qNQwV`>q{J0?JY*cM6`X z#i?VvPJoFyIV@l4h{~nvkm9MY(7a%NTVROc`z3IH-4C6u<>l>?OB?JLx2mtPTgHMS zR&^y;UKyCP+3N0k_`w$!ioKe^UboY{JN>usth#(En)>?G`pz!2M!u>0jDOX$cEQg{ zCVgC}-K{Jp@tcw!PJPzPVD>RTcb98DvmXtF^tKiSHSTHBx1@!f3;!e!?T|hHYB+TC zzIy)J%%1Tp#-ejdrT4SucZR@Q=L<)}$G>dc(zb2;gQ_t5ou&q_(n0Go{5|B??{vAM z=%NvRxo^_Ev7+_9eT{bfXyoS$Zj|2J*rtdY`l$5cnCGIG&?XO$$FAOkD~#6{@_G;H z&~L*NJ^YF_FFLy~>F46?-6}6pBtPqs3f~!@GB6SK*-Ovvk;qxBPu&AqQR&OCRgC0Q P&i{uB)gUM2>o@-m-upi1IrqKyy!WP?i<5@B zt~vlfgW&8ygskR9S5<;WQ;V&qAOj1uKVc6*<*iNYe#(%K@^>bl03dcJ0CDjEtU^n1 zlK@0w0GRd#z?usHl9v7OqzwQHZEnZi9sj8KQ!bbD%A4}b8gokPlCw(K+`@!(-i?&J zYsooR*mvSsTqx!nps;U%f+5`CJpi5>gw(;scf!pdOpAKb$xwZPL7i& zmM$-^Hau?Y=<5GCGdC?=kcTtn#V_UIH)f@aQ<8->aCr2|i_WL5f4vuu&3#@F2*-*m8eVtza_{o5CS}Fn%7&6ZDw+t^ z?Gr(l{WG2DvhgmywhPVo6k2yIg$Q3O{AV-BxIN_)wR@tJyL(#*#Qvm=)My$IRo#+>h zqJ@W&gQz4Fok1g^VnS#DSh3SRj#UXTs)C3FJ$pFpZI}%@IJ49|V%;*vHt8k7r)<|T zxWkdF0jn!Am5Z$6AavuaMez%5t)^ciC(cs)1P?=S+mss50>(c)cn~LM8)0 zAEh*C5j7dCk~u{0J*@ZSge%8=#32TiYs3N`$$JA>CB0rpqHd&}>axK~L(v97bwwg| zt&icl>&~|j%G6!_(j21yQBuKxH#VWnIWX3?`KPj&S4{mj)}qb>XB}y!63Phabnyxz z)OnNYUHLES@o~hvn$%xzPy4EByZ&hbds=k$9rJgSuU;&am&#EYB0}+;6o;_2tJ5gx z(L8k%X1AAYrPo!epUnzp+y0C=e0oGaV`Ke>qemq-M6_Rc7vG&QbttCs=!}psTS33J zMqe2X>?`?AxK%?>u-}_GKzVPP8r3&5b5YACIJd|nSUdK~Yoiqp{~W%KhKZVPkPgL% zbT3}~6jpiVS%fjNEwg1C;tA&toX11akScz+@JOyX<AVYn4bDQS$Nm#y3G+Vm>FC*6%k=(J4nPH)f$Nk=;FOCmQ}Z256% zz+O>RpLw5=vN>kC#rn4@J!?koV>dgeEk@Xq*k(?ZVa%vGkf72F>^?kY)YxAZsD&+! zk22T$sti}zkA`T)m(B|&Z1Rw5{}oyWR?9pu5Dr~Zt~yz==aTdO($uNBzxUof^SUA- zHD~$KMq!f^9PHh7di;HXf`N~%t&f2wi*s2`by>MAggOg@T|SP7lgZ>C&#O3@m;@qE z`;p0bI0B)4@?f-y$yV3Bp;&DM0v@JDFf%-J$V493f-)ln{Qw{IZA^B{xATf|2w+}F zQ@nbXew^yvtfgsXV%%zMhBf6I7Z(&2D%s=7o>ir~O8kfTD3@~vn&VAK3I&I&s0`Y! zy*)E6Bf}~)J-8B!tEdh>?e6I@(A(SJcaT!O4@aAvJaAxteNA0$O*DOa3R=*mM=mXX z{j%gbIW?6_X=!UASq06f-RmiG%{vg;F;2LixLJU2-C0ByYio-$igvboCT>oo0MIaO zo1Pz?+X1Cw2i?(&9^gkOS^I~RAOmn19L@rB&;o1ej>TB-+iz`YwHt%6#$e9D9&FTy ze+VI=0YPNO|0fv!ij+cxA2t+Dg_7yfe&Hlw9}s$$gd&9a1(Jv)zW_#9I|=$x03bNJ KIFujuz4AZEsXDR% literal 1687 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 literal 1661 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 A-~a#s diff --git a/toxygen/smileys/default/D83DDC7E.png b/toxygen/smileys/default/D83DDC7E.png index 854cae372dfc93b336b965528bde44c4f24b773d..8151fb6e358aff2b517b5cdbde80f3319e4a174b 100644 GIT binary patch delta 1190 zcmcc1b%}F=WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D*BnhM<%>K)c5XS_nZBl zul97j+|vfs#^CJk?cm}8RAcSvY;I!*RAy*qQNO?AosOZ=*;(IqH@{NX(%xSG{Pc`3 z3d$;*svb+q$Q_&XVO`06Au;ildAEUv^708R&AP$H&bcu43JVJ>6Eic=bVf$T|Ns9- z=4S!zVBjhV@&ow=3W^s9zsKpXVaBO*|Y+9$QTOdVOZy zw;!`Ixm!0S*GiOz-FJDJ5Jo4z2+VddoIfi z-Izyr-)-A@M2?l|#)`8K6kqTAXymUZpSPi`TlkmN!KG%hTLsv6zTBxY)m%{SL2!!T z0$6 ziLUyBWzYM~+ePzF-e}z8(f|Bt%SZb%27{u)C+Z>}+cq=^{GK1#z!cQ#vf*pUDrdzA zoA0@b9A>hI*jC5hNY-;okoEW~{hZ~F-6fZb#v5h=e$2~-7#-qz3M}SDT3d);mntn2 zYFW3Z{;iGHMM0UIv>nAKD{iy|biK4M*~#_hzT?~a4JEC=OMYHRzj`9-zCN$a9n+YO z>l(K`CYN1Z^X9LBP4>NIq1kFx2Tv&d3`?H3xk4&WsBFnY$B$axsv_cSI@%^S#ycJM zF^Nrg`VgzaBlGocXi4Ma)bwV_3keqwa+mEoB3RI{;K2#^1g|F=9-Wi62&~(u_?`a= z^9Gs5m;Wa)O4VPDohZ}W9jyE#vs|(yi<8wiYuUsnYh21#G|2_*xN4ifvD^E^-_nT; zPV9-<_f(kW;tcvH>9l*rz10`Gpw6pwQ?a;2rdOz=AaoPYw=K#$Ju3ZEXSq4Al}Pjs za0zZ!{$zf+ZjJe`yn}OJy}fw4(oUK)wtUv+oA*THSLe@Xx~WmWP0!B5bkpRAAO7hs zv@crJm8W}V$^l?1?eTPR43W4T+ukkIq#(lD-_^h2$cJ8sl$rnkw;%MKy=<;^`gf)W zbDAv{*-Zb@`NGMiT`=4!WYMRN0K-?myvijuP27|z?Jw=M==JV}_a*-{7fLNJxO`lB z%B-znQJ2qnDHRb#Ap00i_>zopr0J3@d>Hq)$ literal 1373 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 JJ}ODy_z#Z_hsOW_ diff --git a/toxygen/smileys/default/D83DDC81.png b/toxygen/smileys/default/D83DDC81.png index ec18497ccd013b61c03ab25ef51583698e922d70..f8a8ea5bc8c9b06f9cb7832532748bf1171799d7 100644 GIT binary patch delta 1739 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 diff --git a/toxygen/smileys/default/D83DDC82.png b/toxygen/smileys/default/D83DDC82.png index 4591862612d84deb380da7092fa6e2d8107e7d3a..94dcdec528aca493861b02fc45dc802dc1ae149f 100644 GIT binary patch literal 1673 zcmaJ?doe(yc!n;pbq;IL*` z0054~q;VnE{jxA{XtyD)41ox-lNv|`p!(#BWib-U3DHb$AOI;u05T2(uml}tOaYKY z2H-al0Pe*A7$=-*4E6v3)*Qs<(KQ+k?th{44`9CnjYgwTD8yI5;c%_5fWcspS0>l_ z5O+hl%)4CX1;u%(=f4AT!{c@BNREDN4lg1qTr3OSE@H5^;dOPP>unvJ7=b}TiQJdP zCfeF-Yinb$SfV|N#$t0LBExn_1R`lzq&PTC=;P;aZDWT*YopL;%k{QYI%^w0f-l?= zB8=kj!o9aLEUbt~6bktTsI}`XxA<%g2<8Qa3jKpZDBe`_wH62j5{^Kau3oc|;z1AK z1O$h&xqP~RpzG!>My6(PI6?~!*EcY7batWn2l}zMdHK>E$S!*N22jOV9o!b`Ru3x8 z(#BQ?hl9$8`T;dVCK06uQF=O+J+)^wqpb&mDe`FXVBHz@VDC)VO-)~&M%|%#(J<51 zI9PYKC0+2j<+Mh5LDO?xGuWh2UR-V|Zb=V+bVB~Qxk#B4lN&}+=EN@FD(=kL*?J^u zvNB_~_V`rg!M3A2poe>z=y3~bI#I#pgaAZT&B*+xv0p_f%W9b0N8w@kWbAYB4e808YXAzV8$o+w>>BEDHDrt+FWX*PHyTK z@QZY^j-r(6g(o}I=yP!`Q<03hO0SzskA=lN)Z(;c~&q%jYvl{8^b2$hzODQS6tLspfat@1G1(TRQ<4ji@S*bz`mPiwbA+HYJ z46Nn=-QC=oPXsB>_s5p54BWkWbmOP~?A%24pSvfgANV9*H&qyyE@thf+FwflkXWd? zeQH4b$nagS`p~&nL0z)9YeK@N#_n%pghfhu?ez2keGg&TrPc_elzR?Ui_aRm3>gnu znSNVPr7vhg>i6PI)txa){)%yXQR{h=J!WLpNUg2h`a8c~%Bs#$!b;pLeU+f?HDfft zJ%%X@aL?Wy-~JK(0VX(kN3F?)b-atS$(bE>C3-Jk-A#$qHay+Ow0z80|K2@y#XtUs z;mTHvg2kj)z;CJ7fviZzeKc}98V zg4?$amgm&x)fZJ~Uj|H{?#`imJsksW&%0hyb*%{FQ{&UGs$NgL8+&7o(3|-*JO6Rz zbsR%C=U} z1q!KHA#;yTltBbGkT+}~kvEZ?U3t!AcbARsuAA-2WOp(-4sq?v6!=>Z8!z1@PyTPg y+Hm9dkihhdLrA<_u}_>R15|0eL`Go6ig(JmGO;vyk5UHB2LM=f4(+P1DDy8qY@A5| literal 1614 zcmbVMdr;GM9Iv1#4g^7`^K^~T$wT`{+tfC;V%wxrp@3}>xJ{4J1e@z4wGFhusk8IN z7*iQOZ!+9XPp0DWn4TiY9H89h0H27uxo*hLw*$vEbu2~d_J`*m%O&|e^7(we&tHnu zQWl4gpEjPwVufoGQ5`cz`k!$j%$wamWMqc7Xe^V~Q@OO;=pSe=I(QJ5&$HS5aSfwgw2t`KjaQ;wh5`q{ z(-AHz&uL=nkvl6uW^(s>Lw+FR4U+04nW7l2C}qmC5q4UG%0cD}7bkH9gQO595MmIF z2`~tPBx-?BA`$YHFsg(FA}pZsDy~?q#9)a+BvmPd5Thc2Ftr+GiXv31P=Qb!;A-qH z+GsZs0lOq)cZ4f^Ef-Na2_sE84HRV!bU>P!qA8b|asVp57>Lav?KtIj#rezgB3hJi zlKBL#c2YKAIKK$_3i|?~0^PbRcTzvdN&u>1g(GcMsOBFtv-TGnWnASr(Cn*N9_Z`zkEL(C( zq^{R#ci?q(b;312isNj}>vryZc(!Ok|NX=Kw)Zn+Z*Pg*JLgm2qv*fJLEfb9*@vSl zqALvXlZ)G;tl8J|W1g=iv$nP4$OaryZM?ZTp9UVSE+6{)>$c;(So@^P?S+dQ^&kIy zsk_<7KJp~}%r9y2rNO)Boopyl*1R*i=kulAk4J};#f61Um^scHdZ+Qq4TX78J9*wJ z3ff;uFFG(P=*XYN?`@hO@*GE}v#Dv1PMp{ZUuy9!n)q~7&-yVl2ad&+R8=3eCpVW_ z4=KV#NxeylUR_y>w;5hvGdp(ArmphHpB#fz%fpkEOV}6sf&F2rG3!H*-`C$OimDjs z^jNkp3vMN|j(&5fq2+q%4r0T-rA;YmI3AXo^v98q)5V3$qcSWoO)u-JPM3_m$#>j^ zdvNv{eJ8d&%{ps-kbS({xb6laF8tDoF9%ne?%*QXtr z@`Lwo)rzM^{V7xDuC|LgRrU3?wPC%I%bKZh8Golul cvxY`bW%c_ube>EhHNB{r; diff --git a/toxygen/smileys/default/D83DDC83.png b/toxygen/smileys/default/D83DDC83.png index cae7c04bf26a8730b6cba89d305dad438dc56f84..4294502885d92e03e988411ea2fa851635eef473 100644 GIT binary patch delta 1514 zcmZ`%cT`h%6#l&=FYoaZLZECUVuUzaI0-`oS~Zclqd=g7P=*W_2*!X&t1mK&fFK|U zI1t1mBM6}mzy(wpf&+;n$f%661j|wsQ2XqE{iEmH^Sk%E=ljmN-@QKtJ&m4hfRh0L zOU*STM562why?(You!Ae0Nhh&KRsnE>d8T)6GH z8UV=OVVkq)>({TBsGw65+|G=hEsB}Dkv@I-*Z1e7vsaGK6~`U(-tjg)@Wc7T_6yN> z83)I+f+sUW@UgQ&^A+c3t|cgS$RlThhEfCPN)u;G5(eOJ2n8Zoo(?jNFY;rf5h5M(&_dX6V=?9qp&V z_b}3*G~0(W#GU=PM!VR%oV#!yLNrX6tjm&BLx4YduhmTO}@U|G@p*M~@YY!(WTCnOevY*NYX=wYJRHvwP$?~k zE;PJ;f6wBVc?N%NN#8B6JiV>DCr>~=v!6aA#}d3$8>AV-PZG>IVb@0*Yukz{Lp%Jq z!BcqH9dW?u5mj5Eg-fiqv8QuRNAZ~zUdD*xW0AYd19}vj|L_ON3Jd8BN3rsOzz7PU zWrUaC39?v}w@$utztwzlsNUTdsx##Vqg}nx_qT@>iZ_}+?CG5DOl>@mYJ8OVY35+P zwcYIHHmz-Ud^03ZSM&bfv6xkO>uN8iGbqIk=h4$Wuxz=a=%Ubfi%;|`4_6`Z4s6)YnayAQg<|3jFk1f z+&;?F6DS#1W*-bvI=lC=@5%#>(EOTwElBS9n9I+0Tcxk}$}lvH9bHoVwLRP!zr2_~ z^q4!CI-yj{)m9xJm+G`tMZNYJ#%A_S);udpxq08qP`~0pCbQJ>RtjgV$gO>Dk!$N+ zv7&X`bYzxj@>-qUPM+aGEz=gR!`((Fwb~f94vg_E+rz{Q+WIO+d#^HKf4aVL!H*8A zD7Ymu|4mmf+cZ!v7KfD#m`ExH&hmEICmqn-CPGT%^a&1gm`}rN_kowq{Y=W8#xmZnhUV>#Kad0VzOY zrTY`r)casf-P0Pizqpo`Qny`)tR`K_Zl{=#r_q|@8z4cnJXv||;>iMik98ixmhKwI zcukIp`QoOfgZ-oZYz+yXn^W*`Xk=jEU5}boRElGFQ%6Tf{Z0dmH-ihA`r5^j4w_nq zx-3IoNY5ehRD4ogd}5SZL1Fz8Q;u2w-fD`qMAc%|VtM_>4TG$8q;}iNqo3dykzMWW z2yf_El-ByZR4v;LeNNwTs6f=m`t(6nG)A~ElZjkqem*jPI~QN6KaqhYF}^u>r8&>q zna8!Wvaz$V{+`RV<8u8OwUz%-1O@x;3po1!0&U4LIAzfi41mp|?e--?@5p}uy#;v7 literal 1579 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYNpbn%4?9;}6@6{# z>ihV!Gyh5ji?})ZXHMDo?d@dW|MCuJvEH(?j5`FT=W701*cU3WXIJ^^v-7UrPYv$m zO;Zw$-5Fz@cuPrts#Q>b!?dQw{p<6mw*6~NGoCzi4&%xb#TheOb*D^dR(`%X%$DiX z0p5o9In#pb4td=@f9{UVB9^AxreYDkCMPr)?HwJRx)vxZhTLtoWIF!s>)gC!Z)Zg( zE9>u4X1*QcV>8dLJ3rpL?RjU{oEz?n2O1h$4@@!@;F|WJ;T2QR z-|$0Od$ZTNHOqM&JQA^}rv1#3%8vm;Q({`P%r@TH!%>hWUNARMsLMksz~bONonO+= zmjt~?m=WpXz_;4U<=h&}%L~JO-!ierajpI%)oWO@qSJi;5*G1eu05MQj)rHLi>YQT zd2-IEcU>UIse(pcWd(!7E(Jp8e=9$&y5DuIe`Pgg&ebxsLQ0JdOIyZ`_I diff --git a/toxygen/smileys/default/D83DDC84.png b/toxygen/smileys/default/D83DDC84.png index 514f9b08e09e6140197c91ddeb15e6ae6dcf765a..a4c603637de2ac8aa0929ad868a729b9365551ee 100644 GIT binary patch delta 1331 zcmV-31Bk8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0Rm7=R7L;)|F}d;{ZnfHO=jjw zTDnC{xI|0;RdvEaM&(Ui$Uj2=TzAVoK>v4>k8(Yaay^f7J%7BXd$^)_|7wW;Pig;g zmH%IS|6z;&agP6MiT_k@|6hstEj<5SdH?iqI{&T1&N@KOJwN}Jw9h#| z|9+zXYnA_Cjiz%$t%6Ltl2h2al+mz^;?1Av*}US-r1Rpr{{8p;`SIS#pVYpR)xL}9 z%75C!j>@N3>3_;}|MuPa@7BwzTg|R!%d>#Ut7GQGZP>ns$fHo{$#MVk%lGHP$fQum zqEEYlB!TBaV;i>QpqYmH+?%0b)x>L;#2d9e+pw000McNliru<_8W6B?BT` z{g(g$0)To{SaechcOY4GjK^_T z=Pgxv%<uhkWDJyouedXUh^(SlptfYYB}S8FOs5s&4&u zyLDB_=YMcYYFhQximzGRVB2CAZ@DBkR?Ee=>VY>LnqiZwb=$PqJ)14r0vlr`SWH^@ z9rxJMs1qU}8jsF6Y}pkr^AXA04P-a&@YfD4wfS0$D1+-cL{@b((-rX)n+&Lid?B06 zt#S?79a0^7)sFZ64AFI%EyHkuNoqOW`bbX^1%GU+x_g{bQyEI8*V1tpTaZrAap{(@ z(rrYXK%Q*o{fb2KTz3FOl$NLu8OesUfI$84Q6L1A3Wd;^2U9Z`g2huGlYp?EAeK?> z#2#xwf_+7)KVlw$+q+DH50D)z(rJjPFagkuHyp0dsvVqLdS#!T2$3uAe|FAJOe6=J zPJef4BOeVDwBqnjUak`RcTzj7T;V=lKKNOjRh##rfrGo#g=J;ivP?d^kIvpV!LyHP zVmF~3E!?fS;vJ5{yLsI;uFjT)+_-A!L6sp=ju`s-9LWXp6)<3`pi6-StQk*-ONUFM z0tE&NDQwN1V}TI>ItawNnI3^Ct?qTB7DK4>YmfjU2V(1(qyQd1ux3k^Lnkls7YgLa z0n*@n7iXd)E$~4e9%Y9v#}``xp0JvbJ~=}9LD%5mi2TK}ANx(d+Hj|Sdz|IkpMM95 zT%R;&v;9%&0Y)Zf z7FGr%z$Ppr!j3G!Au1*!!iglnB`zT;#f>DuBP}B<$BQH&FRh@cq>Ln>0u)eHLlaO( z7tlZ#&_oFE@oQ=8=;~<+@IeIx^?wZvjf_o9&4i!=<`$M#);6|w_6|@1M<-_&S2uSL zPcLKvZy$t!ua}>HKwyx6a0rkC01H7AlYugO{{R30C3HntbYx+4WjbSWWnpw>05UK# zGA%GSEip7yF)%taH99moD=;uRFfhn-q~`zt03~!qSaf7zbY(hiZ)9m^c^&{VFfuYN pFgGnRG*mG#Ix{soG&w6UFgh?Wxh@=Aks&7v002ovPDHLkV1gClUT**Z literal 1393 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 literal 1584 zcmbVMdrT8|96wYTf>lvekU7p{ff8)5cWtlq)(U+}39ZOmw-K)GmDWD4+#R&S!ZxQ8 z5sjId8{%`s!CZzqV*nRl8Sxn%k7-~dL0OzcSDf0+Y>Ms*)a?)BAG_S$?{VMH=ll77 zf8XzI%{Q&$#Vm~h0Dx!6(H3yy{Lm8}#l4dszaY3Fk=2#4g_Mo;5i|*?tdyAq4Q`^6 zEFcN%hN{b?0sv;(97SbpnK2KyP;Ma+vJv{-UJeZaiY&jEusBH;G?SGMj}m(D{aFZf zSd~zz)F?7~HKg5}E$%}LI&^8Q# zlP0WF2~CMAGv&0Q%=kp1DVj)FW!iZch4{@NVfU^)V8$2xG7kHSIFoTw4EVRSR zIw%hqVkFE|HLHX;rBfley+-3pVh=N&C@y8NpYXznPz1Z(p|~ck8Mc7@SH>%?nW7C| z5-uPas+zWN=V40;gSp(j-ciVqQ-iOj9o(T1E-huLc9R~~pjATL7opW*#YN&w4W>sl zGL1&BLlH!!lOSrT6hjbACMMUQVUAZ}#X3x;MfGBxM5jRz9hxacbSgxei6Ig#is&R^ ztii*ugvUaL{W>_m(^&MiSX@Js1WVCH6y*w6K)#(~DaKBDK}{hB@=F~aE9GO-L&q}} zt(K%6HKbKfQ*LlFzqsQS{<%OkB2;ocAMT!a%Duzz1>fS>&_%MJfQjN=;ukDBPqxK?*1(5E*v+Quu~S-YfrPs z^hSJJLM_bi7&+I!lwFt`i1^)>xZ?esv;`f10;lR13!T=dmny0s_SQW!^}Ag2nyTxA zxBGzX0Nl<`>m%w;TP|kx)HvG$#UoWOjN1lEBy{DoRio*L%Ic5mcReP8?3v-ft)uOW zV=H^xgx^i1-29T?S_O5E=FJh*%&j#U`hsn?$msVJ6Rr$l7dBGg6qtGG&CY`*xxRN4 z9%gw)cJt-dl=1xxuWrb^PHic9yY=)Eo73>{NMxRE^~IgGf$oRWM}q^ud^mJJcKO{L z)%82Kl9N_7mkugE|CqTxYkA9oHNOs}?Ku|NaP}yzAKo^1)nZk!5WkIYePSI)%HStV83ZHa=UZEM#7k0zFVok2!#DU&I9O%2&CdEm9Eo}ciT zmdLK%Z&Va2+H*;M%i+kz`0-BQNcvAL{$pvrEhps2Ze&#Rv|qdryybe)fgI5#BzB=iqA=uFxUb;ZVi0A%Pw?EnA( diff --git a/toxygen/smileys/default/D83DDC86.png b/toxygen/smileys/default/D83DDC86.png index 45b22d15759b16b0ca02da58e6d43651783d2eed..ebdd6abbcde506515b3b2ca1935051edfb519589 100644 GIT binary patch delta 1753 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 W!Hm1`j0JsW0N_dTajSI+Nd5=g$~;a0 literal 1804 zcmbVNX;c$u6itD$$zpA_h$!PIngUrSz+@?5OEN411_C82RmWr?5i%2!!32*8Rupg- zsERx8RTM=;5vbHXM^w~m#f26TP+I|uRp^ zuJ$w<%`rSo97~NO&5x}$^>*)mhf>2>QX(g1L=tI0br>yJNzB85a1EM_#bT&3Bh7^Q z(`c4S>Nq(mkBSs12n`c8+c1q9Erq7h`~!_zRFQ&_z&tEjjSIof>N6mqRtmuczbJN; zR)ne4VGDJbY+-bqVquDcuLJ`F0Dq%^BG6zY3K%u1xL#lsf&;t)YHr?Ufxv(XnIZ%S zMaiS2fQZmxfFIL`pJP|L^>%1DW!uUXtYsL?}>5!P@<@mv5cse1u@wyjm8|;fVG~C#r~D?L2G?nh8APR zVtOK7r=ZRwY0@y5%H78unGGp5g6TRnbtq`6m{6o^Fq{k*3qk4&Q>j)8I1mJjc_AxSIQ(FUFY@7084PCoa3S$9 zHXPTJD6YVU{i-RyLs*}WVg(`{hLVIXjv!KpD?q9uNJ6h7w17wk1M~znt|Sclugu3Y z7_Au7sTW{ML`P_Vf&2>8AMnqE`0Nmgh|Lp;Ip( zcTyq_3g)_HjI_$XX^!qEF6S~TzJ1DQz1h^{eEqQ9C&Zr*Ue~m5@2+VVwpZ1+vo=Iq z%(b=5;o6NtiyDwqy0k}GefPKbmj%qn-{#!uR0;Fqop1eEeaBm$ziwlvvVf`9b+%~w|{W_#33v%2)n=Uq-qLg7Pay~ZqUk#E{(SN1rYu5H-e_BG^T z$a@t1!i~HC>08AAb=SLS1n`<+OmvDM$AnpJ2s)2}Yo`{szNUAajD0(1n(2fxaJFdY zU5Q(#DwN*Ewa%(p@`FvwNTW4=@IbxC#IkYs=^YwVOzLNgkj&py?%<*zdUD5t%FT;+ z*szF)k>k-Wt-acX_T%m|1;nZ@Js?d|%^s(af0AL$EZ^A~pFJ%~7U% z1?gH9=jzsI=;+JMoUvZ*t)ujh#CQPW9O_rbF&u=W9z744=44&+};_`WVV|MEm z2j`!VRRQ5SPm0`LsC@6g>Than-(Lrgw$V*HaNwZi!t$u>(+-#IUiLq5-clzO@p%vT z8eg_|Uf??h)-oI2Qf#d|AADNS{iLUV+)Hw?K4dOyQXvVSfvoRVU*f z_NH$X(Pxcqc{*Y|{Z;jipBrqSIW1rMI<2zRHPKtvAYKn-ufJ2GV&}9q`g z9eDQl2&=aIiLxWE=T2KEl zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0M$@TR7L;)|6yuwijtITcY217 zkfyAydV_^wYj1COdskv)d4Phrzrl-`oxjG&|NsA~v9oxBhJT8Ym1l5rYIAm6WoX=n zhU^Lh|B;3Nx2XTtytlvbFiGo#jL6}ZmH&!)|MKbI_ zwqN5sTSkb1VI{!Fz--|X?{>G{CFUp)kd4}i zJY<^_FMs#UEcIuj4a?&Syk;+8dFY2dCe79kqzYx&mZp&FWxg_2l66oT(3+R z_dWrTk&B~0!H74M1%#OYJqx6OR-qAWYcjQhA%A#0=WQVnHXOu8R+HFcFM?q0X!S?V z3An%8M0f$&)=^GFP9+Key?7#Ee|GH_iA%MgMc~P@2hWlJ&3&L5dka zc)2R<-$hMWy25>ieDJerbZwoR1_ACGA;fFQHrk}CYASm!LS)U|6l#JT#Z;v!<7tY< ztAA*nGs+s0gg-$0ViWTE+DHJQ1Fih=} z&$ed^P`2g>Nhc{au+TMKl7l1i7u$Z+8+}!9C%rw+avslvgsxATv)eosU)GOLf$S7h z7ayb=-N|xzeoN>V%g~CLo#CV!0001cNkls3y3;W{hvIzztUwOq9p<`Y z0000bbVXQnWMOn=I%9HWVRU5xGEFctGA%GSEip7yF)%taH99mkD=;uRFfb3(&XWKD z03~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536*Gc`IiH7hVMIxsMPj&?7RAtwp| M07*qoM6N<$f)!0KdjJ3c literal 1487 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQs|N%#)f2%rQm4 ztTByi<{btG#{Zr!jv*DdS|-PO1Q$vit5-KWI&w(8EgvPqk#-YzO$zSH1a>hs3` zXHS2&vw8pjphuWs{8t}OrTlXUJcd!}UW zlvkYdXSJ@Vx6NCQ3%mBRZ`?QS!xs13jx$$0zbDGv`^|zaRchAxhHRm(E<-u%3Ca^5 zFL=0N-m_Lep7XmCSI94UQgm2TNqO;}ziL_BEWg<}Ep96cwdGnb{Lgz>ebC9Jv%4Y*80*Ma<(y&X!)0XTA@wTZ>-_`}e;ko%vvBaL2Cnt+Exh>vvQ{xoc!N zPCPxCf1VG^hsg6npU+&Hef0c`L%h#(uV!sk@;&=x%FDyY_oo(B-zl+sapJyP|AsD0tObQGPp00i_>zopr0CV#t A`Tzg` diff --git a/toxygen/smileys/default/D83DDC89.png b/toxygen/smileys/default/D83DDC89.png index c2151a2198d03a69f456ec4dd631de70ef8a9156..ba2b6246bd0e21e5802e331a609ca3e4eeb2435c 100644 GIT binary patch delta 1404 zcmaKqeKga19LK*KGi|IXp@v3QBy94Wi`~$chlY8$)#)1ZRC$QGZ0Qae!zPqG6tQK7 zuBSW|T@saw5tU4O!V#&fx~G_%`@4VMKkhx}bH1PRet*BO&-tA9`94P1p!2tKl>h+7 zW*hyWrW`@`A_LHT4!y=eKpG!P@$~|LvlD>S3;@zAkC*%qgja&i|TA%`ew31+OmqjORIRC*UU-RS zu%k`|QE)|?pP74x!RSFLLCm>J;_TweQfX6m(rGsw?st?DnMC2UJ%s#F|Zd{Sg(oJum+gX06h;z9*#V@wMn_DyI(dw z+1}b!RZ%UiYpxO1UccE_+u98!ArMQ$!WPl@mIXuR{y)uqi^}G%*y-}IzOOvFgc z=|az;-{ix%QKy&=IleE&HhXJg!1K-rbN)K*L!5%S`cQF8V@tgb$Bub-r=fng-)xud zv0GbhCbfdwX~Nhp`3Kz-1T0oK!M95;M_YUr%q*!SbW9jD>e1T%2bJ!bU z5;(p7-todb1!J>g%09x0c@#hJWqaa{gYREby(Z#$3f78Lw)^(sD_JU-51Y&nw%zhF@8*^n zimtA3ChG;;w8pgRP2skve*3yp`;6aWq~oklTJ?4r<3A%e;Ad$;L+6>jcaV1t2064( z$GIkC`=|IpZl2{m;SK}Xh;E{4z^)~{He$ZXFlB&OH_t#n?R75C?>VR-$IjNL?|Zj& zs(38v!(_JN{)4o{y@RI4tE(m^ih7rCYMyn+VQJ*s z3)^T;v|LF5C9*!XSw6KUS?}+BHq}>{Mt*`a=w-$XWdJobU{Q~a zkeSLl@X)>lmRk^u$zX*!gvN(K4OkK_EzO9wW+W>*iRiG`+QFJ+N+dcEiMBQi`u_r= dW0_Im?EePbPxy)o40fRapt^axHvAZz{x?&-XH@_I literal 1490 zcmbVMdrTX396#vBl);quTolh@+`!T6UFmZaXnR-M#WF06fk5PF54b^l-St=t*?7!7 zf}-MUQ!ucwAefP1*dMqILs(EGfw4d$1eX$UEQX;|HUbPr{S_$s2iYH9?(X-v&*$@f z{J!6-Nli%(^xx+XK~SJxha188qUU+e54>M}~D( zHY@qZ#0H_krlE>xT{%mom8Ybe%8N~MGe0o_j&~}6fQ8~n*l8)T+7(VEe_dAr=ALbY z53fVGVkLjWsZ2vEtYKIR77IkNCLxN#5{Uqf6H6rMK^POF7$O9>Bo>WRh$RY43U6L~ zV2w2wD2%vv(-v4M`9&OOQy_@L;Se}P0){O_P`O<0(ZH}+fQYr1Svk@fYqdvt6>!RK zVrd&kGgjE6Nai!8oRSYb-3Y;AGZ?mnt@h1Cfs`Rm(uSY{A!4z3;#x=BIV1J28{4Ao z>18$wF;aG>lr@3#D2VcsLGGSy$O8o4D2}i+I25u3XH2CQ%F5|+B_Dhdm}#?uK;VHBD{;J!Z$V>R{)M!eM zF+S<#-oB*BI^StJsr$@V*~U+LC;Fh)!?~qlp{Hi<=hjUVNniW)F*?@g&LLNl|GicY zt_WWKZK1N;uRYK7S@!VllT1tP;8|DJ2d;`I^Hfx-Ogu9DG+4J*cG`y*k}%ZS?%p## zV2^o+*4^Lz$9T-+nFn26Nka{J$MN3J& ziHu$IzM8SxodK8I&Q5)!zdqv|`o+6VW1lX$58at{=N@&rlb80oC&Thqd*1(WA-r>< zaNjL=R-K`wF+TaDrb~-en&Urply-GIU3TWbxqGrJ7^J;ogF@ zHjnqlU{>ecSX0kv-;a32x6#oD=J(EGbJ=TCh6npIs{OU$7hgzRn=D&y7e`BH#`{~k>j(EQ`dupc-e+et+T~tbiTG=E`FfAE zBe!sA>Cac*tE)Z8Yi%BpUTHqe%c)mEk$w+d-JvV1)yr;aLT?W?KR@_!qq67GLO*h0 z;_m0IOC$bK!tjfy*B<}9)X|h!UbQN#u(}r8bM9WPo|;RmkF5MMb#}*%%Z&{e9MU>L zam-Nm^MUCh@X=j;I|88_5PnrH?h9@}s;==yAs;^|Fy_~Yw%bqSp1-V~NWrhE^UnMO D`6VvY diff --git a/toxygen/smileys/default/D83DDC8A.png b/toxygen/smileys/default/D83DDC8A.png index 1ee73302e9f65248ca9c4e0e4fec5ed053c2e74e..950a9fb87a94fa1167ebc8e893b08cd067cb3d9e 100644 GIT binary patch literal 1718 zcmZ`)c~H~W7CsZ9ygT>a?|$dbch0$I&dg2q^kArC zzQ6#0x{EX23q`HZOJx(fTM#vwC@6$e-KhXoS=jYpMU)dloxR)v;>-ZzlK@sxQ~WD{ z7%PA`K>&7q06k7Y!$Asw+@zz@`nnzZeumjDSQ~-H{aRw7nYaUF{m^KS8)<Td*pG+sV*y9hOCKGXw75MDzOhaH2rLMYxd)j|Km}`iCnhKynPE#>%C{ z$fJkLXCOBor2hWM*|SJyCK4aN=H~VnnJlBx5C;b&Fc8VjMe_1KF`3;soEVEm(5a?| zkVr^={)RWR)xbc6#m-?c2$6_nWFS|sZg_rwk4o*r4O}Ry( zG&nj6_4E)YSbGAi&td&t>dNc%*?y~XDz%wT7wYJaTPRI7z`_7XM?f|Sl2>WvG+K*; zL&q;5tb%6`U~Mkzqc}l8r&sZJH{XQk58=ZA>Pma%P1a05qd0>iD6nZNf$;~h&<`I) zurOjd{lw{dCgtu`>)ImdsDR-{m~Mj?k6@qn~VcKSNIR7bZvif z{rPDZKeWat=55_r#odsz8nbThcU$~F*rppq1oTW1^V&L&)JuK8?SE8^C*St3b5EbB z8OiXu58#uDYY&B|>5DIvPV9ca@TUgxp3Fbp;{4maW#wXLGgZ=X_FWbGQW+}wU@LQd^!f2K8uNV24Eztbx1?PL+U5{+lIaZPT|)^>tFTV>0K*MINvyQfqgUL zN3+6-A!=Ejw{c(}jA{FAXyS{! zV;!Rklz$`1w#5d#t_Gj>Y_L@I#bJ%vnQ|_^yNzI}b0=G}32g zzbq1)ZqO1=PsO*J^|Bu9kSlfB#@`4$iPN*`sg(b+51Wf?^(~S&+Pd|>_kWa>JQFu2 z+P|KQ$~$^4&?NfIP(iOG;Dha&#In-;5AFwtJLP@OjFoe#>v96Qv=mihx+U75A{RQ< zTYo4ts1+``^?X$xp|a0D`FzRc8m#%Dc%i6w;?a;W-@vSOdiw3G(Iig44XZPW)lgkt z(!^$^h?7-R=#HIjtFq2^a}!LWVp4WCGbDsLG`NdYlSGv_zA?JDo+neh;D~PznIdSb zKF%JjUc?kRvvYH7oas)r4P~`Wf|knSDi@82OHH&Bu19$Rrx@XR#%*S%FW+B(*0Q0x z#r*aG!;(8>UiZ_(L*YH%;X(cVPX~twL_>wPr~1ZuN*C4CoI1?P!e(@@_zsh6PfD(- zf$M8>8a74834Nd`*U^v53g)uyLZjIz0Lh9(B3kVxTHAPATiNZhwX@l4X=P<+Wz}_} zboPG)$0AvW!+8H+Q2+JT091hg9Dx}b#*Gb*W&@QK8Nw#G919L-d$EIAyr{=)^fLi) MarB@IXhDg81LGRyApigX literal 1666 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 Mq9n0!qbRlTAMf>o5dZ)H diff --git a/toxygen/smileys/default/D83DDC8C.png b/toxygen/smileys/default/D83DDC8C.png index 9a0a3eb00ba5f801b3a27d3f28c63fdaa29519a0..ed941522e98510326f37d35f2ff41c7c1d3e213c 100644 GIT binary patch delta 1372 zcmZWndo+}J82%Y!iD61vl*_1;mC5JUFzMnlm}MxY5Vgpy3r%(+9W_))tUFscMGh_5 zY{OC`mq;#=Tn960ri@Dr!x=MS^zFBQ^v9k(=Q-zjpZC1aIq&P4Zs@Gik|`C3IxEH004;h0MMirv5wmU zAg6ZBjY{em9EV}}b>DC)^952PXzw4b_`9*JrU3yWWwH%qpsjCYh%+(39$#4zGg^8E z*VbhNV;}lP#ui2f`^o!ziTZ;4l*!!We&_uQBLj_H@1<+&E7H~XqujSnW-CtHU|-#L zHoEZ1?Gjh*ZfDaM?e8RCS4Cn;>vlQV3j+t~!ofH=P!IOghJ7{KbWnAz-C~J!PPE)@ zw^w2YUbxvg0M@_do$$*pf-o}nPf_U7$2Mvfyk@GvqQk?r zr-z9tyhzhHz^uwY#Vr1KgwdZ7$BNEwNSo=8i&`r5Ga`f$un({2YdJ?Nx#*!=bi_5nX}lGIH_ld zdU;%bHrZtTjccRrDkMLK_n4VO6XsZ_cDYYG<+`L1ww&R6bG3-%dHXNG7b}%b2Q3`WB^irYr>b?y5&YT{b zGY_DNIW>@Bk6I-=J=$f0oBS4L%0qc3I{wwyK7>gsUgAnqj*cpAE<7WAt@mpddhRZ@ zdDTCj<6NMmkK=gX9^`wy1xHOowfrCY9@P5%*jwVkE%x3c%?BRjX(=((h`V|dskZa> zM(xapFZ~rHi^b7Ctk&xVaiW^=(1;0BRSegP;^LDZQBDpUuAJZ4Y;xg4Iq6UJLxG`L zYfOK(v2zEdHJy&xR?XBZc@EiC@kH}o3ddP{g3n-C)c9PJQkc_RW+07}*)`V3Q#3{Gjq6dm}oompUQl}Q=vr&*oeT?M$Ftj+(6ZS039u5uRQ$yuPl{ z>Ga~tk^{5HJ+W=Q`Sv$}+I_c*`g8j{@}t#cM?0$4;OSRknhD3W08cYytM-_|?!u>y z=B~a1<8QG3QtzzT7P#+M#B~;{lH&>6>Ndq1sFwvXsFtY9#4NY6`)>JNf}Bl6wZxzl zymzIJH{%Im)bkmL^V)n$=w8p($0(F^wYfNk+unaSl`2p+xQw3bJvlDKc6{d3^Xm$& zI=e~3PwUq=mWGB>cixKp!!+$j*F z^dh?((n&t_;6Qo^@oZEGLckm{H^)N+XS|su)eIt9SP`u(_CXL4f{H8F3IB0KTnauH c8vEVxe?!06AvMcjuQC8+lDp$m`+%GO0Qeq}D*ylh literal 1460 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 literal 1442 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDj~9?C5G>WMXLPX6fu| z1oXGFi-o15lZC6BsfCN7fr}DUZwfhKrVn(CJ}7Y@B^Hy-$L*TqbL=#%fI5?NP}r z$5_!ZMN|1Hr_xeoKdt4RQ`(eRJGgp8)D;9boKKMczw`9=dy{1kofMJG%y-}Zx%Ttf zGwYmHAJw0ctxZoTZru4tNB?>3DSu-d?PVG|RXVdCyUlm|J8k}P*?rsD->^E{g)mj| z9DG>pQrUPZYnuQ1c^jh6INWRgqEpd#_R{nd>zbPFu2_Hg*RyzWw%K>4tSS3lm(Fc| z@nnOf>tTbc3qC!arV@#@+h;sG{Y$7W{N}gl4L0hmS~`n21sR7q>bGnQGidlc?^W2E zgC@U2Vm-fdXIi{-@;0(<)MFR>VSP`2_vB@=TNIan3cIw}sP_9=eZSj!OSj6L>E5Xt z;MWkHYV$H8;M>H|b!o!yb{Xeen-VIw?(mgzKhegQrIrvL8FcRM z*4jdOnfQepeLYm{duFF!^Eh+1_QQUba*usx3m@D}{i9MKba2L#mdKI;Vst E0O)25C;$Ke diff --git a/toxygen/smileys/default/D83DDC8F.png b/toxygen/smileys/default/D83DDC8F.png index af81a7fbbf11182fef5fcfb2f77a0269d9c7d54c..8837f68d755d9b329c1c1d3ed3a37fadf3a2d73e 100644 GIT binary patch delta 1741 zcmZ`&3ozUH7XEt+qV1{;=|gBO)gTl}iAR)rtVf$fDSC+@eW}N$1Y3VnwbgP-YCBGbAbGiCUUXLgz)(&&()Lpgw+(}7a&xPPzWE=B(6C4YMv7cBZ3OV2B$Pi} zCU+M7x8SS!CMi)#Hr7paVwT|!vwbFw{QRZjZ{OX?7^5E3ddi~pZ~~EE z|H3c$QgFT-oak8AEe5X5AZ-X_4TJPy!0ZJ^4R=YH<|DWk7yT+SwnW=$3P$rla5sqR z1OMm-7LTUYc#-OEWHQ;u$H&*#*U{0j+G<-ZX=k0?mja?zrRBCtW%`sF57;~ch7Yrp zLR=guM~@!$^z?Ldb8~ifzGIh4n2+yphM9m&q#-rmW{i9(?` zI5-IHb>5bI3nZM3#(fcnf*UHe}L4*|d1$C-LVued}-fWYQO#oh7U7MPNVxByFIt`h9u& zqTm|nz6}Pdz)&3+t_Onx&{6!6%7Ag|kjSbqU-ivpVHXdZpmM1I=E-lqunktDK&W)HY zzj$^uco7*8&!+nPVJakc_rAN7TD`SZ^K$+Dqc;<+?>|fCX4h9&R_8x`g1frJF)`wQ zucf0$leH$0Zn?0p-CUrnN_*UqM1}3)?W!`XqN!%68N85Yf;cTXi5|eFG7@kxanX@y z&PU+bi7^p4PE-s8k+SprjyD$(R5w}Lr<@h#d*gOn!}fP?pGq+&O$R({uAkklg_`QP zg4>jBU4H!I$o*VAY$-}??Csin;#8|nkaf$2LgBy!hrIW)?#ByQ-U9VvUYP)+J#VvE z@tpPSsfokli7`SaUscnhKPUS-elq;GNYd$Zsgf?k$5D;X4SzG*YW})1 z6Qz95t!QXkF@Kz0dxccAtHhvMl)0=1U-OcuQ$#u5WmF~_mdGENbNKW`)Sk`pZaS>Z zsv_n0OIaKZb3W~)-}^7Hv>85C{w1z31jc7d6e$GJkrFQk!@?p7_gj=e5xQP?# zBT`x>I8?JwA01B!d{HmPhS!O35Q;`k2Ie}nB#qDK$Y$sNm-7=yl%fd&UmZ2 z8u79Bg)q12aR^o?z{>0DE8_|Gf5ndp-)(NkpPQ^adG4#Ejsb&pKW4E&2TerTi3!I< zeN@zsS5RR}-}(Dq$caufBwypcGUkX%)knL$qix~I24xw+BJ)hobQ^oEiWTZPW9**E zyN|0&!L1TMq5=k=tiU}CycvmC9vgdB(eZq4o|$PW-y@sO3(lL@^czx8YiXM^jgLjO z>S0hgwn6i(x21GSVbQoZu4YEER%r`w=2jlS;ftWh4F`#i3!_$&7<`~9Q8oz6TWnN#!$m@!_}Uu`T2s^ z57yGxTrw(Vz8}56qTzAi?SSyzgQDOJk&N3>7N&dh@H&s%!IhCS#P>cr<08XM%mv1H zb3u4jjnGV3EsO}OQ8=`qi^Ue+3JTONxT!r6NWF#Kr8914`>-uJ>E_Li&qlCUkyUFk zqi^)g2y1{I#_%h#W+rWn9?MUVg|vpP z-}BSJ-ogfsx$we>*)fn-Y{bS4g>|4R`S9eyS<@&70g!M*+Qjk)?pY7pe%>f=)99LoJ2 Dj?XEl literal 1746 zcmbVNc~BE~6i!7L6!1m?D=Z6G(U4>}7YSet*#M~oG(-hYLb4$#Bpb7fi2-LolmR@b zW3d6sXhlFRf{u991InR@TB)TYD#~~ZI3kQ9j9AkRg6$uUe{^Sef5*P>``&xM_h$2! z#YDPI_L@wgP~0R@h>RSk*&k;o^6m55rXUATLL5)TVQRvpFrpNp3QIx(iB6G>%20)B z^Tt**ltOV(Yvu7oyfm7x#B?-;-G*k?8AvpR5-Kno6v`Bo0FuyTtv(EVb-o@1w5l+0 z69a&F(IgTSZ> zkrD=ebShrD3=m;P6kyYsK}rS$0UQnuVzD_Khzh_A2&Oa0n-c`F_-qaz<^p3ENU9lC zYQ7AK7}FwGVW5T}417A>WHQlAOd4iPrb9d)&&~nEL8L_xzFAKw%t3nGZ=3-^aivjf zAhehsurn%>urwkJBt88Yg3ch7ej?W6V~HYDMmH-Abcn{F>vZp*WUiRFdbR_8SM2x%;w^-H`N#A8XW-hoVSDFlCw!)e{mV3?#qMR9Y2Z4Dlc_ z%oIaxF)W54C^!OU1_v`4;cO(F4YS1K9G}CA5C+VGU=|ko4mJLF`?O^_f$x%qBt+|nqyV%CSg zvgIwFrv~=ytq|{e;@5KJ@bM)xR@WY_$^~*~JKmh{ym~P6?nLn^XOm1P^JjtwK`Lun z13a&VnyUHHm8J(z&4O~S*F>I`$F#&XsvEa#Mb0j< z`qz3Voe>pfJQRGzFL}{hFo%`i-*+>`$)e}o2Akj2l?_$ar?g+ZJrG@&(X{4~ZG>CM zSnKEA8UKLhl>bum>n0>KqAN4!{e|!aCtF{RR1e8MRo!U&JqvKIww|sP ztnZFABraNb%bk*f0%}T`WLrgc_nBDzfaT(vwX`|U{k(RqY(4n1ZxO>c3iNM6-sZFo zcQ2Z7E-U}sY9Pti!#ncF#oK(+Wyg~{6&_B}?c4k%-@EN~z1H}y*X#Puksf_`jb(dR zX8yB1Pv6z%`TSA;cn%UFkU-t%rmPRyLHsF+eK`0Gf4)4umv!%Hp><#+krr^m%H3{= zF5aS?SUA+xQ`PrNWx2!o7t=q?6Xy0mdX{j(_QpLCpSiQhN$Dc+73sOtaiVp=%vDLmoRun#c@7mKA*O0O`+WnWZrzKxf=iES;!;S~| z9_@bGis@cUJCC$BtQee8zqhT}Gq%%W@Yj|2`b<1oQ(Eroa_2@yFV%N;Lu0eaIZ(Qy zl+%@RJ1s%MxwBlJFzHELdSNgtz^Q~9J}V%rd5TwHaeR=l(s@<++%gmY>qVZfrm4aC z!-m$zh2L}*xm3HyzjDZ7bC#?XWlK|gYixZp7eDc7+ERDXB67I;-Tl9J=1JX-Nozf4 z)KC&{X7&W;E}i6l>)-_H9`>-oNj?KPANemyw8Lkm~Tn Gt^WW~=$pF$ diff --git a/toxygen/smileys/default/D83DDC90.png b/toxygen/smileys/default/D83DDC90.png index 41d16a32fd0b114376eb06f728a333a0d8ec2f7d..2ee105444cc4ccf2f09ea9ed0d1ba6a7def02bcf 100644 GIT binary patch literal 1785 zcmZ`)dpOkD8-HDzk*%!zW%tLZOcL{qF)fW5%ovwp$quzeOPDchml{kBk=qbMa+zOg z5J|Bpa@!V#CPK9~q)oLvh-ZtH%a9r5_nkj~zvua#=bZDN_q^wG-p_l^`O}=;$o*m?cpIW)Lfx5O0Ozm($!P#`WGeY@ zfHQc24<`XgR{`|7C2il40hCsJzV&l~D|9HPLInkiXi!dp8WJ=SKum)|I%GM_{IdjQ zj!;2{6cUJ>;or`XM+Kn+)Y>h##LNa4{bhSjT6TEE^vC5JE>Qo?3lk39A`cseN>g>9 z)Df}f1^KL;(_M+tK^!603n#uq7C-?FE;~a$btT@YgBT12)Ru342<*AI)WG>;W&&}y zTtZzcvy-N3BfwT%h-GX3u^^9BkY6*S) zF?+M#f4!70EwqH&cAr?Klhz4i)-mNdmY<68@@nf(XN(d4(~JvmExAJmA#lT9ny4i` ztBo)uW_uwFrA<*Q4V;xyhA7h-GRSLv8S-J_M(@w7rH7CJmpjkPC5h`*tjz|0d9CeA zl0I@Z(yj}ReBN3`eimS}0Do#d3gG-KETU!HN zvEE2#9Op5;6-o3zB{iYbk>d0~(2481FL2_&B#(CSmj+YfN5-)qk0fpwIdpbX#ydA2 zqi}*(ou3nui_)IQV|_Q)6BZfCMmqs8@=-0-LEbq3Yy38 zazW3acd47HwFvi`04OJ&^E<|42JHh{R8^4qw;9pHRJu^K zeeBs660x2}B+BZwBvd7OV*caA_6hs55X*}AC707}BvDR%>KL!2?_;5p+U!aM@nDCSdkwRd_LHgsc_fLeI?5MFH^Wi2~Uh+C|eWNTl>?#sR-sZPGQwF*kh_*-P#R)SU&3gawVif9Vly<~O_$<2s zr}b=1<6$G+w%pbH1iGDMTb=28bJFUnNL$xNucyXZt~S2IIkir5bP*fB{0=44yJ~2x z8+NjBtw!SDkM&70J|NY7?!0T2zkeX{;&TU;DyOrwMqef+YGr3@5~eKq{f?5)8lyRJ zf(pCI9AC;>^?_Vos}=!QZRqiJ)7(90_FE;f|6j8+Orq&ZMLraG3-1gv)XA23zQ>HV zNPp8)eW>bd^RAzDKIXN;n%~=m4JeDr?W0|J87U9@ zVlN5z*Z(G-e;YTWcjYLi@YtJI13`DWgCX|uXt#Zs@womQw{Uf*=8a1O#~eEFCQDIK zG8tMGwe69r!P+)eXWApl9k zHJ)U1kaUo60FNi(@u;x{!G8fFPqV_q;{HE?FK~B60EP+=zNf=@{NQLdP+6x#*ce7+ aFo(@#2eaa$`q{{T1i*0drq|IZg0RVAa5s7jNS zeian}fOQh4j3eX1mh#nvmZ7rPFictSBn$=xCO$==MM)KC(x%}Cz9|Trcr@&qhY(GXaoSPh80qg($uwXmo_A7x%>AM z{g3b;@BDCd&URm9Nn_*K+~;P$`SBuALv8-^gjEY6XrQeOdG0K7^~^r9qr?HKnG2=f zEW73tgLfo7E5*b28oD#i8prN+R6v-*b}+kXbN_ht#8cszcgin$Ii)LUpWEj|mKAlz zwz&-@7H(ozp1qrqbGTRmqz1OTxXrwOV|{FAcl60qBQLwlGw2kf3$Af_3}M^BcH9nJC49bzE)k`4tbX^kZMlP;gS7ihi4Q! z%xt(-*t^ZiPK^{^o`W6r+_=}gTv!V`{h_7Jw(f=lKY6!gWw}jAd#PX4qJ6pN9}po0 z7ZV!ARR=^q)o!(ov$hTevfR@OY7C1ymto$aRzu%p*0yclc@OAS&Ktg9S7~$-3RhP+ z)}Orb(c0#HF0(bRoL@Vw3zL_^C(^x&D)Rd;C3%&tXkB>Vh+urYD*K7xMTEAReS2l6 z=EZKhOO4&e%jd<*e$G8OW3=z|u&gaw>SbN9R_E_}DZP|d|IoKE)+)14Tq=h<@5^fH z&!qKX6JE7lv~nG7>-IC?p*mXox&a6Kq7(aplBZud_XxlB^9W?cHruMs9X**d@gAOQ z$_)2ia{Hxb!icVBbJOB#FGbcruGSln{!gU}Xpxtg)x{7y5t<<#iz zY=`lI)&7o)x9*?~cU4`iL)We_INmu|IXIi02zDfW=eeN3%rmt*)r!4cY``=tO|aL3 zIS*q7jhti0ed>oYqwnhc?Z)x?9KiD{YjuBC@1Mh~ZaT`V1pI#b>Er90hkt4nbj-`V zK2i7A?f$^zrk%D~gKkI&1wrBxU$%J1ay2ROb~mRb+Y2h p?H=PVKK$*mZJ%>q%(z%6%e*jo;nf3qx diff --git a/toxygen/smileys/default/D83DDC91.png b/toxygen/smileys/default/D83DDC91.png index 2654b929a4c4123bf25a3e9b36c88c8d3cbcb9d9..e8638cc41d8f4afca7fccea348652d6d1218668b 100644 GIT binary patch delta 1785 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 F>fZ!$)Fl7_ diff --git a/toxygen/smileys/default/D83DDC92.png b/toxygen/smileys/default/D83DDC92.png index 9146473e922c774522754a29545a46a68f4b58b8..621b28bd6739c032cc2300cac6e836d39e577dbb 100644 GIT binary patch delta 1571 zcmZWn2{6=q9RIQIE0sz{uU$uos-fb$&rksRyor~ z`&Q#Q6YH254<*M_ZffIY$k?}U-pqURdNZGy?|eV=`OJ6b^Z8cD56Cf&Q(*uAZR7zD z;SdY8cCiMaDOYw6CobIKcn5bE0OC#okU#@qS4br+0dN}vU?m6uR1pBj$faC2Gyozo zmo9o@m_9%t5D<`Z#Hdad2>w4BrAR-JRH0gkzgZ%r5MyunkD@4oaQ`~4Fg!TH5D1YU zaybN26yDfH7JoA>-GfR5=o*0&TY#w-Sk+C)QUq!x0=?=DrOX*_-@>m$YdvE^R~RIh zAL2Lmf|r%vaVx65lJvaL|9qS-;1-n3{?b=M%}dWnwMkci&jaSW{ZSEOu=q}93NKFm&XVi@}bjK$|GSJ0+RZThi@iR@GSyt4X z{>RT}*j+R%3nUfwmN(?k5mSwAuE#|$9fyJ3JV0lX+zNZj>L%-2{Se|r9l31!sh0fm zo`}LAfnx|e>Dj#mIu)bR^yw~{I9gslkgCc`Hj=vI-fs( zmKodoSl_WbB-DrmJKNh*MFgVAY2hXr?dXD$T#!_d(G=&I`c?uUN{RFIjdsVyz{!y} zL&746@aR}F5q_6M2Ea7Cz{~&M6eP8`sAQHTC-rFluHLGF*UkWc)ZxsP#@+`Juf6cn zii1^o%iF_Ox6~;Z*}hXB=vw|4*Km^s%qC(~ZHqI0`ujYUzcj$`8D$U(>s;=cxSoE7 z^me2-<|O;Yk6F~t$)~BAcIyS+!9I`88>7#aHv@Kx#~O+KK{HxOD&4N$UiCS)2WP3a zLxxSNW0!6vdFa23RLhy=9>^n1V>pO14La~*7!c@BKKfp0vF^VxJjE{)*iEHQqU3yv zOsjca)Zr~gVj+ zVqU?KaN2&9l9WijjrzpO6HiP^5P#&ojka%$l1XJrip92?Or3l^g&qqwPLk9jE=7bp zRKrdf`~R67&r%HjHBFOo*W}pE|j{ey^16^A>H~uach< z5bV`%%|iUHZ1`nYrBD7V5<)B1q%A9PV3nPk3)jje;u1@Z@%1pjTN+)cuA_!!iD7W6 zBtla=%#E{`8eqk6ZI72=2`)%nduCo`rjpE5-?{PNEH^+}y3q5aosqZBjWj(D>`)>4 z`_apCh;cjv*i?5e7&=OzBc3TZu?{nRzB@}VdC1p%afnQ`ev~5b&3pR|ZawAXh0kry zAy);{)+4?j-)SXT@w3~UIkGclF=o{5ag5FOxxulB>CCjPbq~0C%F04=ewDfUiL#kP zyD;CROoDPp7)SV_-yChNJ(Wi_`**B5$*R5l?NIgY2_yTV4DF9^iv7)c7gRBb-XhJK zik-!E-sa2gRlc29bK`3tf64PA=~92dI#OqYs$L!uSGB*UA|sz>M-xcO4pQgdT+^5m zP0!pq_QoJfdfz)e@_}Ac`{w$IhpJlriTnf~-`Uuf*2=?gZ}_QgZDHeBEc_XER8t$J zb5E45eD(4bzd(B9VH1;L+sN>rLt_kz;cK;$D-|`%_PtuwpIH4wA;QfO zog~)GN=)3weq!vv!LotS%^vEDE9-my)B3(*JjzR}wcrZRJN=J`)#g?oJmoZO&U0?0 zP((g-p4rYIx(kFEXhvgvqY1cZA_^Z#6b@hn85tp=b4bIpo`w+Wj4{d>(uE)t1m#)J k)%?dmx=9ENiT%-_@ULT5)$eE?LQ4h!M-0}s!6qo_PYBr0X#fBK literal 1697 zcmbVNdrT8|9Iq$~;snYN5LtFB5Ky4^=tFu^OM7jlV6iPE79q&d9<8cABj;n!$(_Ijy)Zm!NEV~_<* zs!+K?)*Gj^N)>>SHUi+WIng=@h51z|P>n$51bCbjL9hWOWwcWT>H z@~s4@A?#$HO-I)wJ7gM6=kEQ6T#B?e=n9*WE(&gvlDa%IVWAXKA&dUP)*JOG0>gZ1 zjD#;23osciNHB;mN3a+kfjjVU04X=$t4^JlOhr^49k!h4kD9ru^2AIlkhOH zU>d8i*eTqiBc|;dX}eQc$$POVW+QNlw55=wX}SYc29hG}2GR;($$TJUrO~1%9rlH; z^1O*wO4y9+2)*1!nt{puqQ-aF7a$x&EahPsM@oAR!(xG)FOa|jxs=D@Kmr+S3akG= zIRj~DKv#48r&*?t=niyk-?lz2yd56GLQjm19*y47rkxCiTfagoPH|q*YjWdvr~@~) zT|2qqr{+I@_umx|T9cij*;?aY)f+mu<(K`UGsRN%AEKgJMeVH5x}I05UUnzQRicw> z4XlXFODOlRz99P|>C3B<@oVUy`cmUe@4o!LiMGCp@5h5{<^)7Bf@g_5f;=oAh5BBO z%^7X~buf$xJ+66J>W8K%)oeC=I#{&R;oaVH;ba;s8%mxsg(?q>4R z@=j#b_Dg}l^xy2uS{$Y>v8<${vWKhnQx=PSc44EEooQKRYHRSB+dmHnJsP@u%F|XH z8G)`#4Y$@^=^hs*`5sHG$YjqhbhKVa7gQc;-SVW#^x^W>V&G|9z@BZZb^g!V&+V^1 zAvT^rzT41xbNBuDBb-;^(1gxw%@((ZrJ=*E&4EX^ooXE1;#KcgaVtF_N4rTb_Ujmn zU3KEl!qw~1#8ls~lH!sFcVtdvL)YyWs&fac*o)%&2+=$rMMKBhh~VJ^m6^i_qkM7k z4P4P(B>vXReP8L2X@TeRyTi>RwQe>ZyKAVrW ziKfo4nD=TqJ|&Dkccx&c_foLrtY&t_X@7Uk(2EeHBgAl6nD7e1UNwGxY(3bYe(3ZP z{J_kjkd-$CP1@M<`<-Du)e$$h9xiK&1W=8lo+(Q`cJ*1y#T84mPk+3y27CUo70g#9 zGgk~uEMe60nRBia4P{Jd{*zl^l)43~pLc!pKF=4-Ovb}S>9@jSlJB{G8VXsGv|f@~ F{105_faCxG diff --git a/toxygen/smileys/default/D83DDC93.png b/toxygen/smileys/default/D83DDC93.png index cf1b001f67008dd77b52d2f0edbcae4f155de348..b6443f83baaf179591f2e74f5c8fa7534141c620 100644 GIT binary patch literal 749 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>IY! z6`#a=--~wtH(CAPa{UL*T5Lr##;Tu&Fr5Q)pl z2?{(CGbLs!>ZB=5ZftCw+Sn_rbl}{v1E&rhIC$?~|6S1SKmyiig`XJP9j*IZc* zW(MCz{t0IPE&x*ygKCLuL`h0wNvc(HQ7VvPFfuSQ(ls#GH8cq^G_W!@wK6r-HZZU< jFfccJC5NIRH$NpatrE8ex9p%rKn)C@u6{1-oD!M<;$a%D delta 513 zcmV+c0{;E&1*rs(85#xv001BJ|6u?C00v@9M??Vs0RI60puMM)ldb_Cf8GNc2s1Cu zO%nwG00Fj1L_t(I%f-{bOVn{3$MMH~Q#m8Ghzb(zYm{MF5FMCPNq;~zv`94AkD-Q$ za%zwWS{kCErZ`=4Y3s)y5Dto5LBDK>^t>$6g6_1sujX|Pop2nT(bRi;^Lf5r?_YfQ z-w}gU7(a+kyu|B7I-2_vf1C>Ao6)htCeqR4AQi?3(340}RFg}PO99kLSIA*XLpAzZlUX>A!VG|~>Bax2oR05~|wzNtn zR-O#&TZ8L3S=z@r6mc6NlDOC_Sy<83R1~;;U}Jcpt$Uy4d~VN!f05Dqf>`WGcUMzT z+hFSHi}Pcjr@r7yHG?y4?K^2)t7c1+qpe%lFJ(RmnwwdOv~{!_8<{#?ZV*%$Y_J?- zu|P3KeOz7GLb1qFfqd~dQx=sSjLv6f4mSq1WS6L`p-?2hNH(%C_a4T}1(hB2KboDG z&rVK~%ahHM`%X5JTl+ZU;q0q#xK#BDrAu_;ZdXI=-tF6VQT*v`=8f@%%rl(mm3+Rc zLCK^JH}14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>Ionopq*;oN8Wv(*GP!1>pPt&~>q`1~t_*ta-eIMx!x*Q+Vwf>Zw{w_27nQwT|NBu^$&W{}Z zZz-DZL*t+nAR2cSX zGu*di*rm;|QJvxc|Nm7Vms$Yh&#olMFPI_f#`Eod`L`1qb3OlbIsIvpf9tyU2-led zA8aB&e|xGl?bwz3CqvRUt`K5oo3B#3lr!+vw720BKQp(8&MeZB(R;EOXcA+Rx4X;a zhs^i40Xdun9+AZi4BWyX%*Zfnjs#GUy~NYkmHinzAD1e(PDANZpwJ~x7sn8Z%gG4} zOa?}Vn>R3Jnx&QT3CzEI?%u(Ro!yPIkKa5xb$VlC|NXOf4`23n2sm;3`1SMgGaOYE z3^XiULVRpYbZnH2yqX-1tklf(;=HUB4K*!QjY8WT&2(**jlKQi6mKi5n;ZF0b2Qw) zV8e@Fv80ab2?SWKQ9kzKMP2$%far&Pd5@{m?gU>#k|MtN1H( z7xFMfrx<8l7qeXl^qp#nYeY#(Vo9o1a#1RfVlXl=GSW3L*EKW=F*L9;HnlP}(Kax! kGB8NxJGv7^LvDUbW?Cg~4I;s{pe)Pa>FVdQ&MBb@0LuVh4gdfE delta 703 zcmV;w0zm!n2jvBj85#xv001BJ|6u?C00v@9M??Vs0RI60puMM)lY9amf8GNc2rVlf zVd%pE00MGJL_t(I%f*vVNRx3K#y{_$y>q_P+0^CCHAA6L)0iSfL|{pTMHwARFLe*> z5(thcSP&gLR45gZr@%uUqM!)8M4~1onGqNoNU5978B^!p_x=4kSRfTar@p5zJP*&| z!}GxZKIP{V3(y8!1bo0Kf6x<+^LT^a0dxXs;7T-3FM#$#yj&IZ+&I)2F4|XD=G|6g zRtl+|u9IuY{aQ8wLuR+mk!j2{Vo&*6m=cThnY2`l%1-} zI9Bf0>uWRr{n6LsJV@&*0Q5(h&&T zL0V#+zGDGsT9Zjjf7oq>@^u>pfs}$mkAf;?-b|W<=Uz{?G??a@N}nh&R+n)W<`GK^ zB$hIar+BLacgE($o>Ffb>DZ`-4#4M+X%-XKrC5NIn^&gGbLU)G84GDU0F0*qxTp0m zGg_+=$z;X$tu7lWBqP&V-h8$CfLp+m{Xx8W!UiD$csU2}f08@{E@%KvCXZ%U3C*v{ zU52n3nsLZDv;u9BLRxDI*30x|1qS9|JjDZ`JsKygY^-H0Kr3*2cQM6-33`AiFwtIt zf8rYqCzu7!N8|MUR^<&LCBS{438)5|cW$D4Y>}tH*=U@^AGh#Jhz4{6CBO-wBO2%W lKQAp7;94v|8P(rB-vRpX>4#=z9m414Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a> zb!^$|IC4le>X+`!|Bfrpi`M-ynDyUg)qk(e|2;SUw_kIEyY9cztp7U8--&eH6K?wN zv-!94lE03N|GRCtC(``ia{2$Lz5kOB{ZHKY(|O7Nxc&bFxBs_Y`QLQK|KMH!lMesS zI`%*L;Q!ct|Gl^TH(Bvfyyw69+W*d5{(J8D?*>%4;lJ*RPvX7rMZ5o-tp0Dg{)1@G zUEYTO+Dq^7Hr(W?2L=nT?~WC~;L|P%@&ksn?aSM_&+M3Rrm{A_PMs7A)W(?P?e4iWj$>X zv50|zsjey{q9iD>T%n*SKP@vSRiUJ^AXT9vw}64cqIYVj@3q?o9CwPl>sP5gAenxE8RGv6Ktke^j&Xt`C&I{ z#isK|yM(WZ{Wj+Hub$Pu}KD*?wc;dfAw$>le8SS-C zVO*sUe`3{&EfLOT?Nfr!+Zew8?`Cr;$M>+P+PSADKPJj4bKNir3$&C}rYHQ6HRZO0m8{`Z4~8#EZHGUuxpi>oj%_bC z3oDj9{d@l0Vqw{n2h-ZDr=Mx}B;JnBNX@_fD`U zSTbGyugGk&YxhD6DfZ>ApDw>MdQqY(5))+Hs}io7*RHmMnd|$u>$+PkTK4SW6LNUr zkaK!+%7Me&*Dg+adg0{izpYGGMGYpl@@`%am~gr+i|+mPG4x(zRYuJzt{elR9C~5vN#~NWgTe~DWM4fH#ldn literal 1450 zcmbVMeM}p57(Uhwgp>>;n*}tS2l8RoyWW+S-r0_R+!Y%6Xi6h&!P#qj&bcHc9q}a}u?4mHI1C^bfK>yxiUI zuMd=Gy#019I=u z6$JKs4M>xwPE{AOa6W%cufR3-);m4D?HAtE(PYv zZ47}oAX2*l*>tM8&JJ66frB-uTIo^YIIPv8c!fr*#Sg%Q3MViXxV1{Wg3@Ryq7vS^ z5MWL4wo(q-x@8Nj42VyXLKKE|cXy-RYLpk+Fq|aGqy|AK0YWME1SK}C42nf51)38* zfHBWZit1WI%wYn;`^3b#>dqL2)ZlAZ1vX4PiK{!UBP0TpMUna&Z5;u_Ib^ z_Jlah!HImA-~s2+T9hJ#+&$V*5(vDZ8U#N$6t;urJzW7VDA{NO0=}SLzn5Zg5@!fC zgKHRq!Esz?CDb~dT4mPIW(`rnq%?No5|w6}W^@&#g|y;8MTg_GiLSs+S`%r(t)>*$ z78E5m=;2a!{lM-PSN%vXWf3@5;sqzqccd!7?&Bq1^zk9s(x`>Yn*2d8-z}CWk7qMl zniKr5a$c*z2jGqTQvMz6GnGuG88_2pC23&@5VuxK>r_M~h(kr1G&r)w_5PonVZa$I zSsecDgnKg3k^+Cq)sk8YrV>5p?cIH9H ztftYb6LNXgAR6C$h9ni==XwV_Cb9msxy!>dH91X+NJ}XG-RaV=kJe7f<%RvY>U`SB z`YughBJ*~_6^q|3uURf{NsqMHzxe&mXAOnFEPGtB*wY_oMyvW|?d!3X%$bw#G+i;@ zdg`?UkyDQ=`Y7Wc4)F+Sc($i&axH=X&?minb8)r$$-$M*X~l)tuS33km(20^y#A9z z@cIqxjnbi(jKz2XPZ=A=jKfQj#g`}N54#ae!olkw9=l(-w-ZafT8q6m5Y%#5ysQ7Ff6OXenqJEmlQf ziz9A|h|Wxtftb-!$QW!Em5JK%m|$_bF`X`BlT4O9m}3LZenQeETbA7K`_4Hz=SyyG zZhfXaHITKAMF>fed2j$ek((_MBDvd zaj}g{xGO;E^U&bOmls!g#T!Nv6MXtaM)(q*uO)MbiPLYVkNQ0S0uy0%$n1pn5@e1$ zk#%3j96^=~yW1BIk#E8)N7z-+_QN=cvLP5hgU*e@%P1RwMU5*tcr|^Dvlqg%Fx-Hx z8dYO>eH4{9QE?N>yQSC+f5UgX^`#EwxKn+3iDI~UGYa#3e(;cwY!8(f9JjixKsROqa z5lwI=;{Gwr58>WBaKvMu8hQ_MuVKe!NP8e`N8}mAwtf*{{i})~!-ym&UiuWuK1jMD zxNx6sf08am!Ud%FAgL4F^9XOe>2LWq=-`8(gI`hAh;6w`cOX<`3ZtKsZ&t{a#PIO9 z>)Cs5OcyhbmYR;sW@i+A*Pr}4Gti@Fdfdxacv@Luv)@mu^Ei&SAm)iV|Y{}8Hjp6mU=t?yS2yFvQ*dVG8_go`<8{rul&4x``wJ9_tNkaxc z+GrdfjBlXt7-g31H0!nIe38zSPY)vC2?TMxjdA=06`v=H7m5-#ZQ$`lJYMl~=i<-& cCs0(PHxyX@KM<}7{)r9{sYIUQNX|X*H(rXmZvX%Q delta 724 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 diff --git a/toxygen/smileys/default/D83DDC98.png b/toxygen/smileys/default/D83DDC98.png index 2cee5aa57398da8d857c1d3f1bcae06b7be7e174..1459cdd8835dc7da1fbf237c8205be4779693301 100644 GIT binary patch literal 1738 zcmZ`)3sBQ%68{o;2RN`QkE%^kTFS$Q@DPHh2@pvNC?p^VY6&Dz9w87wj64NfKm-g3 ziV--yASFdAK>>L~35Lfh5NZ*GDsYv9J6jdBqIB=iIMbP%8)x?0{r0!}o88&jna%S# z;Gw5us)G>HV=~-Xm^D~`S{pEJVe=O@X4E2R-ZX?7iU@Bx>X=UqW3aptI{67gnWqt2 z#h}augpx=IEruXOxrmTa+~ro^JqY2ZeZ2hW*H9<`ppqx33f{?)ppL8f2n>Ipe2eGR z;KJTTud7Ej=*Pb=^Z#-7?Kkhhl;k_8XdYF(VRXZvX*SJk>z!1t{fS^LxwQR(x!C97 zhq>S*2Tm7w*H4(`PFfUzZvz}|gMGEro4>xRp9-8_h#!K4VTd1uunvgm2H_Ay_QTy2h*W}T1X9N!WfV@1KzP@rD+ z_JFetXma2xAo(7ojY0Z2WQ;@7Fr2st`~f)9363%_$LK8(s(`p*5RI+`H8#;IAZ7r9 zJAu{!wzWXKIIfcd4%dO%0*AUGq#w8gw_GdH*~g1V`oOy8Cqmk^UM7%bKyLzeCj|en z7}Sf-&Mob4{#pOjkVZ0WKM&MK_@V`yfU*4I`oox@)^pIMkwyrkNQadDxZ9_~26+st5&W+Ky@uKA0mzK%J;Iy0#n zA$-P3zraKmCz%*06of~{@Q8_`I3Do?KMpHjQ0h-Bn<8mVC!E<%(@-0WzpR{G>DZO> zHa)KRnN#I)(~dwgac}B5uda+gu730^dXn*!+#zaE^u-=JdfPbIp*dEf`wM?2ws}BT zv|EpvR7;UA{B{*T6<%@k_2NXJB4Ao(8t$~I=acYqdAzu7_u&o`$4Mzlguf#B?u5FfADNb!U_3te%%eS&U`IM-S3m;vpVOR>x_81%#(=#oVMsm7eRNcpI zZiBTEl^0*t7#7(FtKz=SOK{dpm)^<6JC^$Yc%as{nmiU=Z%jWVwfk#EyK~)%q1JkqiZ1q_1L71*loO0D(LYO3@~BB|xq%xuAPRJ0iPTOXv<(GBW0 zRHx@Vy)x){0!%yb>!Ua`Q?ceHzWo4Ys0 z^()2lN9{+^CK91@^l3-^=}66F6CcJ&&D>3G-Hu7Hsfkz0q57rS*8LBP-F-e^9@1r~NMXDCs@(|U`-{+?R#+na5=l%v|} z7skDI4EX0w;T}0jA1)qi9+@=|W^CJ;IdW>^(x(HNIYxDVHy^rE67YtgzMPlM(7s^E z!pD>)eNL5jaXlGt=S&Bl8+{d$m5lRf-Kps@FDP4^5G+rBXaFJ3k$~ zOzYi8Bw0A^w70hCv)W_EDyCUlQk*ojLg!{b*qBx#HQ^SsS*kHRGFc^;CzuJVjkzzL z6Lj*&LeF@9F*Imp>1;ADlSM}@EiQd%Dv?Pf!Sg1+Zw{K8bfLJow6>;PO4(d$Y<%8- zb@fxPx^jEb;CQ6Uznoehz^)p-fB)egwi`RT%yD8LrL|G=Wca?MS`gwyn04DR_aMRh zckhUDTU*-NupstsUmur}pQnu?8>!h}p5`mD3799+0~5KNL>?tf$ioaGlgMNn(rz1D zdp}ze#m<4^U~fetQAi}7zT)=(5cmRaWVq=4giY+U^*dv-UV$wLPfX?rd5Fdpgz|_? bJ|}|5;&Hg5_yHbvU=U)`54hL6hGhL00YxP3 literal 1673 zcmbVNX;2eq7!Eih1rdV_P%08?qY{sDq7D z3yP>57U~6}6V&3}iVjqewld&d+Yu10tyl3tJ9rhXc7w$BhvSd#?Cy8$^E~hIz3=R5 zRpP7wzX^UU7ArtL8&xyoX!qmm!@MW=ztSPZt<=f-dyCJV!6vEmXO7M(tupn-HE(`XiP9yB&{03$Bq zB*O~6!XhOM#@YE+LX)4U)#qpH5uB3{55zfyjDU%tb%4W^W2S@-5ob_W$jsf_JPt4j zp|eGt*G{DF)Ik7u{rxps(4S~Gbdf*@`UfgqAWL{fQXTIYx~Q=uLOl%Vuh zqlGq-X27keODAo#h{JgLIs}tNp?D)~riK#5q>Sg#S$H6q&oi0aaSfs=T21`x##_;p zHqS!v)C5J^ta|1=GD1CMCU@^PFzFe-rsQJ6>LZCnI_Q78(ILZp&dkWoPdGDHkZWl{u{NW}ao z4_9ucXq{P4cRO6X|PswK%BPX(w9Bu!EV(gH{|(Li{z(TtOJD#Cp{ zucJi?t8p2D%dDgc7|gHG_!j#RB7-C{8DGM9j|o5!fe;8~V&`L!6qRCP&JY*>KRM$u z&hXsD@t&u#818R_r9bQ%`-jLdfslwEbP{|i%HR3SLiB8qY7QS4+ z`&75NzeZOl3L3~)x3x7zMxHpH1_*JGl!Qv zr;c2@b$`0|Bje7t;?-=(XK%S}4JUh!|C+v=4?2D7*@;B^a5t)DMug3y%Kco5&r}*CTe|WeTDCyuqQ06Pg9!OdU@g4 z4rRrOo1w$bq}j@g*5+20ytk<8TD!gdf(TAbPH6CTEc{V`OiB|UEAZd)NdZ79xjp&0 zScC24+Wq3iJlo=~Vev^s^9olx)I<~HlW9w*BQy4xq1DcPXL?7qs*a3#`dwmE`~t7k z@PUJ;?yZ|zrCND(DlZP~?SHuPdePOfbLJ{{jtCxEy6L>%g%_QC8uRJRSa9|b7WDDS zvTwd^i>Z_JU%J@$+wFm?F@K*;F5ZVf0LRn+JgvrKHQ{-LSoS(ex1 zO)Fjm@?MoVTh3*q8IB}=HV_z*>=kGZTm{cB^grA^noeswUEiYo>s3PV(eJnVD*|F( l;+n&EOP!0GstTKjk7GHXPuq_W5%=6b2RW9A?vpHC`47k*cEJDu diff --git a/toxygen/smileys/default/D83DDC99.png b/toxygen/smileys/default/D83DDC99.png index e9ab2c133f92c2bbb991205f7bb1496f856c769f..dc7c44967022429b66bc9d56b8d41f0c6e855d4b 100644 GIT binary patch literal 910 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 P00000NkvXXu0mjf$;1n| diff --git a/toxygen/smileys/default/D83DDC9B.png b/toxygen/smileys/default/D83DDC9B.png index 77174a53dd0e46852a68db77aea6c7f6bda8742e..ce1b87781fefc633e1ccff071c4e71c6cf581298 100644 GIT binary patch literal 922 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 Ad;kCd delta 787 zcmV+u1MK{#2%iR!8Gi-<001BJ|6u?C00v@9M??Vs0RI60puMM)00009a7bBm000XU z000XU0RWnu7ytkO2XskIMF-vk8VLg*H4uMo00084Nklv3F!h-)Pq6fi{N7oL|;OK9_m4&5Efz=dVdH6gCHLi>`4fMs4O%Z zR$98vOX}QzPUmgT?cakJBJjBb2M&DagO87M;D1Bd7VR`#2buvp-~+rKyf3$AwZIL) z4Mc(aK${1zCTtlVmKGM(yUL0&TaBopDBp+17!Cv*wUj2h+(oU8z_Nb!r zD9_1qfGBKI7D?$g2&H$Df1wy-X@KBVAIJg-!sgcmaDN!B0LMj(dr*ud2H9AO!MX-X zjbOMFa~bGNdeX9tj7OhBT!XbFEGIz&F$I7uTiie5;hJfEI3+D%@g`G#Qc{9HkMY=Hc}+Q z`?0(7(SP6WW@S$=07+Gk)MoDSv~*mPb;{)P=!l^z7K{>nJxXFe4w{6m)PW&aVdOzB z#~jU=?pb!ilUPk4fBYsv2Qq@z@9!D@MS_E*z+7Y}^7bU_*-`SUBubio({tx8HT_LY zEk_uNPB6AO`vR!jNn&p;1zL)}{Ay>Rt;Ejfn|}hrh?9M{-mo4^5qf--%upqRs(%`& z_uzfCeT@iku>=2NgouVnd|@3iV@fs69b)u}p4rxOC^Hods{Ss(`S&t_v@Na_(-#_; z`>OiX*oqpqPWoJ31uKLIuG8pUCZ<@+G!Ag761?4=G}aM0FQ?T-y?a` Rv)^PEs(LXMI^96jpG^-X$U!? zAyY84f{n#d;CN9?#(+R1jiV?GI=pL0_z$n!oj~7y3$Cut?!NE$`Fx(|`#hzwf^dq9 zuM0wm!jIs_6WPP=PMe5G57;6k5;=nt%|WO|?YeqmGtv8rBI2VFD%gooQ3*nih*8lD zLU|ZMv#AKNwFvoS*Itj?j}U1k_SFO)0f2(ue@tR*KxvP)4UE$KC0R-U?5)BC96eC0 zfl>(`6{}mz*NGtShUYE<^}HPxs(Y-O4vDNs8*fpRI+Dmh&M^F?X~)0W34-PgowQEy zt=_2DFX^Re-Xw+0Ye2tb2NUN~D%Sx->~+Xl@GS*THF#IQ1CrS&5+p-Z`_2#EtY4?Ah8ofJs|0aWD^|t>Y>k> zCX#r+pf;2Nk8zHP2n4bZQ&@@BtFepc$UzLE z*bM$xh*u>RAE&5*(+a|yaI6;&wE@-$dvp_Ss$oh#JX5i{>jFe}LfBQXq>YnlH~}wdfA(&-|c5cq%9FEP+yOPNcn@^XVay}K#bzwM_eRQTYDk74{8|eGC z|HTOA;NZ~vMrYbin(m8+`rAx8-G7%{uQxPiq)WsPq*7cedoXQzIOF}K_txr}-?ve5 zYSsCwqX~&ee)?gI{}fZJ#kD_%1O>BL9me*ynqx`%Nep^kex7Z~TD2Ubm1Z$ztpOiQ zY8poyV(NlL(@o>@>3c6@*gp14cWXjbJ!W3n>&GW2diL7pj<+ht@W7KLDTRgAfwpCi z_0s2c-Ora(&}$z15A9L^JVY!?Q}B`$=_eFowkTIjB!pu)9)N`eFoF^o7@Nsrv#{M5 k#>TJ;&V9qb2w5l7WfJB86Q;H&dlLl2=LxvYucQ|L32asNOKqZNtH@N$$}-6)Au{R^7@E6?I|K;@ zdCNnWpo>uFJa~y_g?I=A`!YmR@tYc{TdA8mN6wFZ_}{ndpa;8Dw_bP-&*6FCh35zV z`-I&l*4!ro1wcNKiOapOf4#Ny9B>xs2Oa>Af%Cu!P%rERQ-Ee-v3k}58Law=*p}&; zFjJVLM^||gzr#V-yWa5iOBHeP$y!z>ruZ;x?vZ27eOf}%UdEgmOs&6WGyD-LW)b4T zp4Esltt%-^ATDf3wlI`y~^`A;;)s zrA{h>Bn?P1xWlmM#$~YrjLD2TqwRA;9l)>&V>ZwSGyzdS>a|U6sq7Z zV0K}iVef+VM(vgN^6>bV89@-bZe6UD$kE}o*{*i?4;F*quPQ&IPfE)K#|kfp``B=~ zvG{)afk$$VzOHf2t|jQhOuU(h{{0ios?C z+(B4TV95)MZrF_Q!)8UTlGHkTD2X}62Q&$eJI|$RO2(E$o@DdrrK^AT@nKt%y&M<$7V`?x)|TY{GaMcvh~}=j9$e0--22yOjaYybZW# zpzY0HSukNtHU?Tt_hi=sjQAE_0k;jbdHxBeoe}|P0VJTwKwIojH2wn0ehifnsR&2_ O0000)g#gq~?daSM(XoMuOl;v6yTBcKI`AByw zDa&-+EZxoAvQk?gsgw3F6w`K!Fr^Y*MO5_ek8NjX_m4d@_ntZ5J?DJiy>sVeZ>I&D zU@fr#0268mnT~if{W4mF>|f(eBN2yA^bhw3peA>Tj%kR*xV<6tZ~#&r0T5*ZFpEH< z4*(n@05BQ{0I3)N3;xA>5u1^NuI*t_6v$OU&In9YLe^Wzd;??UaJLLbKZo?EI@%LW zXv;)UN%JtM>J>|?XXz|AB}1mPY^xu zjuPHdAgVqGKb*hwevID><&w4xY5{?jGlU`>sTEvL{q-c;k(4`t^0X=ql=%yevr5 z(wiLHY=8gAxLSGtfbI8>1`oubHOio^#`ro;9lmybTGd^a$E(KHt2Of2E>~NrlV|om zSxoIv*{Ef2%`-3YjBUx-@oAp|ZnjZ-YlE9<%y4t(G1kQL#dTIGecUVso#(vCIA7%& zwwuK$G?`(=m{;VaGWWdvg}-dTddC`-t@S(IwEX~oPu4r^g}k&>MZ{&?60Nzbn{S~b zXG3j+<*Bb!IfB$Pr5{l){lTp$1;whA;V6bY16)}T@ckGPRbYs&PJe2!UgTtEGZb{g zl7!A3zUN|WqB_w^tm^Nxvq-wllg{FYQT6FP1!{4^+$n*Rpko90)N)!hkbftTZ{qy2 zztU#JX;#)$`J3NC^~h2S(&%y-dc8#Zu``+pH|}pPcYdAN9-O|zj>}tXiLSQ_+-KfV zsOj#ugJwEO!JD|*$jNs&3HMXV~?Xn`-MgW zw%vY8(pr>*WO+sfIXQ|X2y|zcT0flp?qXU7!L*Tsi_8n9lB^g*c>|aU)J{p`2+Tu; z9WyV?6>OUffv5seYh>d>2&FWII1Br5j4M(9fS`C%PPvILbzVI!DkKi^VB zql1vLQ^;Zp*`&P!HsS!0KqTS`UU*mcC|3f>&4c9O>P#Sz2m~+B(Y606;3cy-2}l3G yAc#x9iWIETGejmQ2ve8>Ht=U9$Fp%%9y5_mXERwx4?bb*XBwbVXyodEIPsqgk$a#3 literal 1322 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y`gLD2*8txIZAW?5>ATTyIHi?JgIrW98(0$ z8aZvzVGIn6QJyZ2Ar-fl1fTXg>>y%0nPtnASxQ{5f(|Zy;T9ke!>N1AU!Y}?huFVv zZf&RUdaQSMymng^zraaxwVU^ZTOztkXZDoOI<{q5mOL|iUfMg$?~-af(vM|YCn!a* z7_zM4Y;%nSFvps*> zu6ZmyuCI28sb24xV0^Iq>O8-V=fA}IN2o4LIuyaQE$z_hrru{eIoH2)`_^D--MX(* z-EUf>iSCO~$*(alzq=Ogy#0M?)i0L0(+*lzxBXabzGhRc^{f3Kv?`^(S4``%NzCIg g>Sb!b^g}3tA^CF0iSzopr0HjsS%>V!Z diff --git a/toxygen/smileys/default/D83DDCA0.png b/toxygen/smileys/default/D83DDCA0.png index fc2c29f95a531a1886709ee3ed55fc947bb27346..f596e319ac744bef1b2e209db63767fcccc29d22 100644 GIT binary patch literal 1688 zcmZ`)X;70#7=B~4Acz#?2%s2H5K%OwiX;U!2q-s%AVIVxKw?2rh`|D)0;VDcFb#Nt zM-@gX6jVU2cp!MiGSzY>aws7jf^tX_1(SUJ7-u@uAKiKOy=I?%W{=s;3iRKur(>=I z0D4|K+^MMlcCKqm=pnP0>2^B z!O8>d1HjA`R%{1L3OfEPP*yTum~=*%kW-hCUHi3o_gOC*e4S+Hk>Z^xMgB~_PeOrD zd;uD`@5w|#q8&xy;B&|>ENyk@aWp{XmeGzZqou&wpgEw5)}C|LjR}Mjt^z#Fuvc9YG( zO_GC-hV+28L$NR_8<8s;$v)w(%Vv%$%(c*add1wu3E!MHovT-8i z0?a#>LZfr2jGyuG?1QYB{Y*S3Ii876ii-zu&N&-G5p)w43CE0&X82Nzhhk(9&voOY+=Zq9qz($fx&sqSQ~q ziEDBj>$IHYRr-5w4j6B~kXoGn;i<85msLHj+uPrb?RwIS_rA0;J)qMgy|l!%#=}qd zDnr<8-@Lvx;VN<^+JZ5rz1zT05OCh*#gf}weGR^w4vrC=%p2?=><$afZ;Mhz>${6K z~yu|$5|L1obfkSeV#YX$=p*n-<;>3RBVkB#X2dS zb)Cdc*|Rz5ec19ee=>vh+yYO65fh175o7>`acuB{6 zQfXn##z(z+CpO(By4re*nkrIDU;S3hFg)NmGTS1XH)AN~c*U>I3oK7EA^e0jwv%BN zFWAkMUdQQK4zPl7`i(L8V7|8(*&?)$->vl#Q@1p|4Y9)JkFLYzCJY#vyir$#lb0l*1QZ++yBPo2 zSsRc(o>jrqr}lF^NyLXX@AR9OpXT#Q1n46M(_Zcr8m=uW05Cht12rG zQK{_8s!H~ByV-^f)4Dpm(>~tbJRRK^)zxO=M9!aU?8{2a%Ix>WDmY>@XJ?XPY}CTs z!eVr6ku!;etE;Qm%PA-b-&NS1on2pFk4s8QZ!ErFRQ0FPV=3b;Hi$LU>uSQnn=Hp} z!O?exb#JA^r{RrLM%q|wbon+M4>~8B!C{i4*i6&`kw7Fm5H>kDZlF05$lq-wZ**Kk zAdm@!O-`eh{~^R3jE-R?|3ATVAN4v)Fq=~dKFH$y%wW$wxd$Vec&|9dUM7{vh)zxr RG12b=z{|tmy}~Ub;~(C_1iSzM literal 1669 zcmbVNdr;GM9IrB`u<<&@LvS-x9VpT^=>rleu1#86Sh1EenC{TnrjXG#T@!4ndQP2+ zh;Z}4a0(mZfr7(LWuP8D$7?<&4?*XNbEoJXo6M;L6lEz=w?91pST4!$_xk4u|8bR$)4J4RAk`-eli19**GbGL2EBG0BvPao~1>BQsLV2|#VZGYB1l z8*@HwC1N=oFB7RxW70GqAO_09!`(VOr^UviIh@!9P8)8>A{byfkwIFe-2M}%xBzLC za#O_`fyRasnWV~PCz4%BdV?#=02{dr;(%Bu!V*{r1_zuLvz11iQtl`(!tUM2d@eAm z!emLgFPuu#XaSV66M&c}j4}v75RgcCphzr{fb#%I0785LdrP7~5h9i#P&6=haan72 zqY2Sr$}wB4B;{r@j1A%Q9S#T2A>>i^3_b|Mu$u#dqF9Y6I>*Z3&L}HA)5Cxfw82i= z7?QFAZbp1LmCZ=GtfwzRu-G)3m&8_jEKzLA_)gr$2YCX%#o~@@RGVgW#J_I5Qk&N2 z*a*Ikps8%TfvtyWrU%UC?)8S;imW%tLOaP81vg`qA=^S&88s&5vR`;c(uhDHD8XXn z5~U1A6|5jf1rjBM#)u(Q04bxz9*$RGMIa`O7Gp{blEWaVfMhTvM&%F$!!m_ZA{2SB zYAem)Rs-R&OR{#yu!`4W5!6oL3}x3-l-bh(+DwX}=uFB6pve*-B9*inDF;2rU7i=w zVuYPsK^T>G$^wk$7a?C^ABKdG3=^ZM5Mwhkm?LhbTW$UxT%i$rc?8MmF(V%0T=Qy14wQ5YJcmA?!`x4cZMZwJ@!Xqa{ z_Z#~b*Hl}wdJ^&kJ5mx7PlN?2w#8xX)%}b7Crk+HO!jXWyo^55dY=qdhG8FtOjQN! zIzV2n*k~pp9c;t?sUGLf1zHTJCw5`!i2aA z5~=zdeicVDM2_OEL*6O1@c~8UacaTJzR>Eqj;^Ax>!-YB7e6y{&L7{b_-e~BzbWlu zKbYGP34A@+``Prp(^XSvSA2M{;NabNG8Ri4)1yTN&6V}zs>x-59BlXXn{QUNr}zvv z{yCIqYuUZ#n?sc&JAufH^47mAZuQd5tA8{3_iEnV@~E4_23Eb{AIlBF#%XBilj)%W z5_n#qt7y{Grr7S^cMnXAI$ZT@cFe}+u(_3uMSf}FF^N^+INVe@ISIdyYFiDFa!NiSv3-rKDmG6T$T%Shp#`Ik4$z~|5@ zNo&cxdRO42&NhT5rk-z!unjfkHI>f&Hge)HzsoCckFub2Q`P?9uRl*&c$x?cI@3Ei zIPHKGcHxSjS51)CM##s>wAgCR7pHC^OvKNheb4=4P%DzK8u_wy{{XJce}w=5 diff --git a/toxygen/smileys/default/D83DDCA1.png b/toxygen/smileys/default/D83DDCA1.png index 57a5d7f5671244f1ee177a3fd8c1c698c2edca06..d2cb0f25362b153e815cb09920ea971838e32611 100644 GIT binary patch delta 1393 zcmZ{ieK6E{9LK-wDGx`qDZ0wfLo_a0TZ9ZTR!b?Y^-#!qOlX%|k;7sos`W6XRpE5p zc`D~=J(M9~FPF6r$wQv9WRYj(Vfo&gZszWvn|aNA9_I6&&u8BAc`cJAlcr1)1ppk? z*-a1zF@JMga{$UyWdtN~VU2j>9c%%J)C3?l0e}r*D|QNiFbn|Go&XqU0s#}d>xMhd|WWyg0YKQmHpM*C21@uAH8UD1hmMS6cS z0XJ7uh%jM4*x_~hzci)*Ayq~6oRSLRj(JY#)_fCO>s#Q2&bI}^jWM`62{*^#=GgpO zvS79vuJ=R1XDFC~YyDfFYv30SoJ)ZtQgG!jILFvp>VT_VD>PmeTxBl}rNE_z)rlIo z%z{&K^WFY)UH)(?9xhjnw@~18)Y^OJiIxyJov^^Mgnata@Go=Sn*jy8H9Wsz>?rZ@T-xT`9Cg?GjF2Xq^Vt;lhr~j!nxe#Iz@N;&^ zxwIN9*fx5YwdczU`c)Go6lk~LHS z5Q&a-bfG$s!VyX+*)Jf-7opyy_#zR(6aWkv8ASKqER1BdzoNT^m`I4xwf4(gzBhA3 zrXw{hXfrI7ZHc(mrs2(KjWCzI7&w48P#&)D^YJPnI_t8XIKkEK#>=L?VTan&xWB0u z=@MV={p{iBIT)lF++b+nw21p!oTw~^D@%4woOSbA*1UVF*=h#mhh|39aBeU=?GfYv zOCjV<=uwYJ<+edtpTOD9_j0T?0r`C}sJPoc^Je5!(YkakJ2yMwolZ~l8Xy*tP7{t?>|;Vz3g3a8Vw9(mWR zkxVvGu}N+RSEa1#RdtYr?XO4WMQm>0E~f((-wg>qh8mSj9DuSZ2s zYjj%|JtppR`7F&?Rxbk&R=g6Mx1SBP)z$V+Rm!MRKW%Y4^3aFcn*0(@Guv<5N)fl<< z>_^4CF%5HdSy4{Ov5Zc)OvH&2!Qe@(%PEy+$Hw0;R-nbbHtQlv_1r@yr7DtA##3wV zC@Ibuc58Xs@UG&MA66lyF$kB;LziX6@nIV3z>vHBK?a?mzD{~Vec>i0&g{Xfw-J*E zQKljIx_5ckpSG&<*2B_(g_c)fj)v%AkFu19#|kcZ%pTv{`AAfQ=Q@nnp)sI7~tbn4;^H8&3tr03)Ul#i3iDy~KeZ+X#M0)e7@lvOK0>?|cQ zViMxF6C~8u&XdNPR24{g5u5Zgxd|Gt@((Kh+AiiN3S0t|Mp_H9w}dwzq2k8OV>KA~@{kNp9IF~(pdh8O7H6#oc<$vy#oH~(J{PRE<2 Rt7=IDV2!(AS!&@K|2Ie4m2Ln4 literal 1474 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY(%jO_)WXcw z$k5Qy+1%CD)Xc=((#grv!o^q#syBt4Fw+M*Mjw-Dv*aRHbR# zp?6F%959x_g$&Vdp#h&%sbu#VS=i8Z6 z{zShx&|mp`pLu_xhl!HY<5PPt9IxDclE+Xg*`WW|ajnh1iTf8!d%yYCmQ0^XCijIb zUvtGrzxnuvbMe(@ule%rj*AwjHm#VWSXAt@t!uq;N?nI=`nqfLIIqSo|9j=_i;LWM zUVS*K>{Bgz?b5-2%8TpN;;NVIbFeO`iBFeTc-HWS;luNrdi(UwDPAd@U~H?-#I54w zTCwZD(TA%0tM~c^OH6WldZ202XKU3pIs%f7Ken>GNEh+i9KK~+fL7$n156XmBY+DHu`kjji_1#>@TnYg8*AEppy|L2_Hf^oh~Y z4Cm(+rCI(EtPE9omnU`E`p=z$uG?GzEb+bVP3h%QpM6=M=Lg1ltr2_U{Na|L7S|(# z8^#se?#{R_-P=3eR$!w`w2|$J_~!qAJ#+-!y04ZrCyVReh<)zX*6z1>w>8&GLAIp7 zr)~!yeEM3rK>OW0{|EQ~w+7yemdKI;Vst0JN|rk^lez diff --git a/toxygen/smileys/default/D83DDCA2.png b/toxygen/smileys/default/D83DDCA2.png index cff291f92667baeea3c9e18540ee501b34579e56..e232809e214daaa6913ab5d59ddb519b3ac6b27a 100644 GIT binary patch literal 1573 zcmZ{k3pCVO9LKLmVa&**GLtaF3Hp-Fj8W_D157;o%WnP?KykT=l*`*dw=J5f9H44JvV2kzYhwr z4gml_(P`dH*y}D&O*PnB*{%wNoqD8)p9cWf3$&Ho74RAxL1X#>kYoWsS|$MV@K%}( zfI~z8#y9{_$^bBoKid%C3V_PCoxUt81dnv#zwGgWA_Ab4Sm^r$|BxC7!R*)qG2NC0 z49f!&a-rlH$kzp8kiR0H0|mN6A#{jNf+BZAiTjr=tl4zP#}R^~J|yT{8WgmBnLXb? zf?&vt2zl8ri~XTXF8C!E3n-cm?c1XuVHG6Ye5l_iviT=x0xV`Df~KgX4;xlbc{#v# z+J`(H79iVTIBIDy&0+lq*+xS+DyM85GSit13m6Zg!9wn0HO}^zc{>eYk#9{9Ux4)} z!?n*yqaTZcfi=VrgbwkS8}zn^+^ivrB}6t;IG9Y^V#c;wzq3XUnd`zQ!#8Kl9_oOv zm@Q)Z2ZGKs+FkBSx!4ZTNe-EDds3kcF)MgA-*;MBj(k6hn;Ai7ao*OOd;T@ zW%ueuMt*+Mmtd(f1ef1C_o_r$(MjG5r@JXfQtS|;k4m+Xohk$3t8g8aaE6aJ$X;Pg zgBKbix*t_zL_-HbK(!7%Ed)R{If)f4VsaC(@xnMk)OUQWC^4RoJrWZSfXj)JU5p#A zvB=j4bJwddJRkfvi4>Gxu{o#|wH#evP~7;(u9cZP-*-QdkItIPjm}3q$wQNSq>{Vy zU5qlGp`D3jzRr*L@lIcDp1Y(Wfa#g%)b-M=-1E--nX7GUU4lvEzP!!b(X3XS-iHRp z%Vbu66-6h>yv(lX-pu`vm9H7zY0e`RsWP&2*jP*P9gH;Am3z&gh;b`Os89JlBD-E% zw-L3(3te54djuEKQy;I>Ys|zfm>CZ#lZwbaw=3^@5b)>25;H9)R4LTX&K;HXb>+xw z#|FC^aA!@1PHw$GSeS50Ym{}&zRMZtvjDCLRL>8XoZO$&nik*wlBR9727#00`_@Wz zN#_Gljea1CGc)=}>xVEy#bla+?#n6kHD~dj#23d2bFyMKf-pan4(HcYRf}}SC zJ)f6+b~CaxP;2(M)XkP<`>+jaT>f&j+nv6+rADi~F8;iQsiODzu8_#?rl$UVoE&fU@CXQ(XaTMh}n$4_OkH~%znp&F-u8+{~YW>{5$K$D-= zSKrSNJS3H^#_E>H)~pYkGv?%qF^kVG5BUs`3h6Wgp(4lHRQldX0oVmia;PxsI4b{7 zw9zHv+;~EaTtwipnJHHCGX5JKD%XZ#do%Vm)u3ZO8lP6}75+$u*DzUIR$QcL8t#IM z#G+0WGeheAKGiI-Fv4DZ^mN6F!PE0eJbaw=Y!{LPs zfSSN!EwQVk?QwH^YioP+<2J;waGE$V)%vilwY6W%+Aiu$z#xRiJA<$ zqg2|_?&zN0D6Z01%MdmqeBB9QYiL&pH7)8SeNuXLP!-wN-7>(CHb#)Qs3VJz>D2)@ z?0uW?y_&Q$7t5N}I|FUdXq(!rF&tI$?Zc;$GdlT5|Hqr^>SD+Jk@+U)kYR6-+a#Bx zVVhs=s%gCrA7@y{o-f literal 1598 zcmbVMeNfY87|%e3F<`(ZDCb-QU?i3?Wt>95d-^N{yTf33PE0$;62+%X14`S3ttc0v>=$z-OCLH6nSs3`7Fg3g($@tOPQUdIX$ir@Lz;h%8J1dgy1q2 zqE(`54KJwFVnhq8Wl~rzhPCO^AjgZaYE&ggPzc3gl?;M3unbqqa51b-#~~FggODIr zPdhn+wva))6mNG7D|;yxQ?n$&F>DUQlm6l;>Hr zILT7wq*co@c5pPm81(}CC@g`MxKyo{;JoJ$q(rp{s)A50E|o~cs75e`wf>)+iFjv3 zf#vjmU$4h(Fcw>~dCA0CqCCx+!mi;6FMlJD%BzqVi@PNbi@Pt+vqW^@bxiYJn~-_veA5+B>NjhUGD)vpa;cxUB? zjLVtk@(ZWwts7@`ZC89F`Odoa8=O-kWRB`Chc^2AD~IE{QEB6!zEk~ z>t0pF`I5oJtAtIFe75#%WV=Z`ZqE~gXQ|~?U1mks{+)%p>Z~3VU1e;qpE+(r_17se zDPg*}q1H9r;dwn}(dXwLh|l2Spyq~_;@FJ(ixX;NV!Hq6j4RlZ?^#S9x;F0Hj_A|H z@^V#A&Ahz9yhXr_*28YZU&ZdZc>gm_Z0>^|OvIZ`{z6-pKRji+ z98op3t!v6_VUs>SccFTca7ldR(9$#eeK{+P2rQqq%|9G220Ya%D^ri2I~4e%>orE) It14LY4@d7?F#rGn diff --git a/toxygen/smileys/default/D83DDCA3.png b/toxygen/smileys/default/D83DDCA3.png index 2b943e95242fa44ecd70234e84dcd9074e262f80..2480754a307212e50b915cbc9931ab9fae377aa0 100644 GIT binary patch delta 1611 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 diff --git a/toxygen/smileys/default/D83DDCA4.png b/toxygen/smileys/default/D83DDCA4.png index d25ffffc9478d184108e119ad10a0e2fd3e13ea5..04fa05fd2fb852f7e2c54c7d3a3170d6a08fc5d6 100644 GIT binary patch literal 1267 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>S*zucT^G}9#-x*eZWmxo?VeSWpneP~;yk(g9hN18E|NkImP?ZqH5cLo(5M4lx z5Cqf?M?iBRMnO!Q@)oD15M>kJ0PUI-&TIm7q62`9N_*XoNoO>c|Nimu*@s8DDQ_I+ zEjd=2a%jhPSq90S7QcjhDjXvnI(~lFnZOj)>bBwP!4OvG4;ANbyC|%&?&w`pmUMnE zN7L4YSLH4F54^pqc8L4LwzeX!BCkM(7e|E|Z*R_C{@~Fp=X7zW7ka<0A9U{Qd-UM4 z@|@Ev+I=~KWIi@e-+ky`>=i!m@*U?V@YkjFuDe=$S>eDcK_{E`oW;ITq3xT#yLPNe zHgBD!;BFYId8%U8qK{g$zDuU^o;;*ysC7Z%vO!^v;fr6hPV#=zHONx!?6|>D5cnWz z^CR7W#zn#mEXk}AQ#koUZMdlHufR50UpXa?|u{*Rfz0n!e||ZrVo9opREg3MEQr{c}?e9Ogczc(Nqu zQ%?QDhI z-7nr2PAjgLQz@yx6m$1m@W%~xHC!o+XYBf^@btwwjrQ;PoPQaM4tM1pUm5%pm_Ca< zT^vIsE|(q)7i%&QVLhK6cHD8^dNHOOcYS$raD3LW5J%u7bx=4L8gHQmT!r5ENW< z3YMU0&%OH#%urNM%_&!KrTCU&Ava-0I*F$16EsC-hMK-<)gcB|IOTX*`p*l?G;r)J zJs}p^qOZegXJkHrN;6CE@G4{<9Wyl8QNi^r zz3Wt^I0keSKp-ZBxW#fDkfa2c6eWp!74R&_Gc38KIG0pJN#RqVbJ4_FVAqtAk?+_d zQkI^^*jE^)(P$(ZLIMR-3@6KS#KH4%q7e`0JZv`OUU6|bLm_cTkt|`dKT>3b%@;#R(K><(F15= z6qFH~>2|=_G{PvHMn2HTCGgUOFKEc;~y7CpI;3^1!9dbJf96e|YQEo!2VA^IFf^ zP%HVtMtl3`rOj7<+GsDLd;M0w_uSV1S4IjaC%3lGe7k-mHc3rAn||-A_w`57-=UBj KHNMd%+xG#*ID&xy diff --git a/toxygen/smileys/default/D83DDCA5.png b/toxygen/smileys/default/D83DDCA5.png index 4db5a0eabef1d249e0b1165cd9f0171b729f0985..7fbed7d8b661eebbe40ff441e82cf65c5005eb7c 100644 GIT binary patch literal 1838 zcmZ{l2~d+)5XT=0G?55$h#XpqKnMtdkPu8TYN%Y{5Q5P(d+5 zFELlaI6|2;geKw<@~A|}RCeM#pMwyN5E3Zxh9WuS$sju!(j_2`gTzRP;sf6gHn4!{ z0#5c|V+AYC!Pp4&^fZ?j?_(ivf`z{g_QAKw|J#Y>`cJR|77_ooNr`w}k|+d9f;6=) zmWNe{5$mH_F?>B=vy%-G8(!}*35#GtFyd`qE5by*4c!hl*l5JQjqP7ftO@^sb>mI2 z&Mbm8;jjeuN#Ls(D2;=(IH-w*BXMvlR-@v$04fDA76;8y@M9^t=!SS(#zWg8Z?)2kwr7k+H6GP&6GJgC0>co=e z$LB9BC9JI;7vhX|Kb#+IjIt}gh|8MeTDHxeOSb(+I@iwJ>#sX&?fsZrVAp?fh-lct z4b%?Py8OT&%cms_@bx+qA)YS94g@(b$eS=q(AOp7!nWPi!ltz_M-V9wP1t26OHY*~ zC5x@(*)p-!?i3k9tg?^8_>K2y#Q9!_9Bp%hj%#Vc$p*&4)RUIdBb<%b8upZJw>CE* zk-i_9?TQ;AJ0!Ps zwdXh$?hQ&*eqEKgZ&TPRqgyAd1+Bil@Tj$r0IzCUrcxo*BGPL$osNA;g5dacG+TX%F9nZ{i-=5OK8QuyYxKiX6}{d8k6pg*=ql(VcsLf zi0k~|F=YB9SRvvsUsK{rX zrCY-RRnh48IA!7G3|rHVh&GRvtM03CrG{qO(jyLh#&LbUD{Yj4;v#Yly;0vtzl~hc zcj(43xl!-Xp|NGZ;1!mIov<2zbZ{_a)x-9rz`W-%z3CQ9Og2etD@M{@Bs5^-LPZNX8zlA3xLzs%)s6M6FF5mO-Pj2IJ~+>=JHxT?@$^0L5umvwZXM#dTL z{Ax)YbpD;1`@eGy}SL*&Aa4TnX^y(*Rkj7(`C+Xe^Lo3U6dPV1LlmFy2@PN;{t`gqD$2OEYjydbS`j=CVK-$38L(@>tgID Ph7iwtv)9@8c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYACkt0MQwtYE0~aNz-V}1eOdseNeNf^+N-QuTUZUu@fUM$kuv7w8@DR9d!!J2H*(#s1pw`I&rp4|6;iLi{* ziB|pRcdVcP`98;3_^{Pg@7ArsceJcuiUW zQ|VL|WFu;Rgt^dfNr%VjFJA<&ue;Y&>1NNJ;@wwOp%d zx&LVw^M|4+Cd(P`TR+d^^m$yhGDZGo5`Xg}t*zHG!$hh&GW3GE-u+-!j1`;B-OB&r z!_HeD_Jpmut~x>Yr{|`au=#8YXV$(6Z1ui%V83Kf>#RL(0=3M%ow`SC<2|bSw4ZnN z_e826fA?}igjcUn__C5@r@!V72UuRSFOgsQy?nw--GpD4Q}$Sk%r{>1&G+5*4PG(| zv#d`EevO&jmO862rr6grCFW2%lX6$*R3V<#sfYjfb5&*f@oq4D_c)rxj`iUp3o}+u zdzl(;|B$QQ0c$M2EIP2ublX+U>n^K5KbZH~%Kq{{VLAOx-@nf^E^b_BD1X@Z{ERQ# z&vKcVZaa}Dyx6bk*wsm?-!*3)4c{25ESFSjbmVx#My|uF;(ey3TFx(uzd5Jv=nh-% z!(7)xg*W8Kz7Ba9r&t)wBN1j0;k)4Q&b5~|{pX+ipGAUU+Orear?5Of4Jw;GUHx3v IIVCg!0EE9gG5`Po diff --git a/toxygen/smileys/default/D83DDCA6.png b/toxygen/smileys/default/D83DDCA6.png index 758ce6d254f2cb7679813ffc28dc3a5c153b2c15..d4b4ddebe2695b8ff2d39a1c229f4bc76ccf008e 100644 GIT binary patch literal 1681 zcmZ`)eK?d^6h9H|PF5e=Vx=09tiq56QxAbED51UIJ_rx@xKP(GuL^CVF6MjI3ef&cb>L4gSP zQYe(ZhxuYCln~qH?_r)84L0BV08wtCSW7PuLxVzFQ$6>g65U!*K*g-~7&j!^BbCUsaiIu`wJK09)7nxg6i6Un@;+HJ15I;!t#Z9q#qxU!Xi;dJ zv`$}sspyq1yq-tq)QCcj@TU-XxB&I@Q27QP&ZZ8kT7{aSIppy)0#7v1p@Nc0Xqbl1 zxjnr~J%I|^=i%-Q7tra=11Mxi8_yFyszH-UKwx_f2bhWyoJr z4K&Q(nnK3q$OGX5)X75zkj6I%+*d-?TX>{4`~!hr1lpDR1*+m<6*S49Qw>dTf0ZEP zGaA;jcTk9GiJ%6(;Yp{bnidhle~s3wp-}+>1nOkaItMjU1VL!Ur5yAHd$9a{0w7wg zeje;Mi=TWRZ{DZW99;FwbG46t{=PZ)C6WEAeyQ!0rE4-@XI#pCn<`x?7HdW_&$YK@ zNhCC-!AM)n?1rz$8%xn{OAhV!^1L{zJJNuHWh}~G(z1zVhL}xDTip*k0qAfzfmD`1 zJsukq8yy~bl!;}rW0=^4s2Bj33zvg-*XH63hTg4lP0nI;yBTilYzbuMjNcj*q~R>t zNjA9bq1#my_cnw)k`Po*nI;r`C4bl#9ok{pk|2{#T#L(!8amc!uERc2Sd?P)hIiWw zpBgoHrJ~dH1=T5dFvE2i0v}qfTGveJ|1R)AApy_XoMWG@6vvNvl&MsEI*>vb0hbLEYwa+>q}EK^BOy&^MTTX2?seV&E|HV% z*3%!^)M06vcZ{8~S*n942?W0R6rT6leGg(H4y<~b&h9p<&W_@k(L(RYPO~>A$gjit zRDI!tA3RA^+D%Qo{VN^sRFCw=oU*e9?PFz%P3ClqOe&{-vz)rz>IK1jVRXiFXqf3e zzpM71MJ^`KQFj+drcX-@7DFXH_4_z)aP)V@PfCbcUHU#tv15j z1RwdaihtE&=QgfEfZp#N=I^9`Co3~dl!~JKz#aMbRouSrV=2boHOUya=Huoq&R%0q zIRlv~2XfDC*&q=2oWk09jm;(su*`VlHIH0eQrlOwed~M6!hAf_^ci{5W`<>rsoq*I zI$3D+rL}lF8B0m~^@=OS(Crkh=ZYZBCv?I73-Ti~6Zo6gJCLB{Sy9YR42 z1Nph=U}9N@&?DnQIQn4@P&|o&CeOlX=wQUg#vMO-LO~0sGeH+0=W8Fm;h;Oh-BtS0 zvIxHz@?mO%r=_v3Gc_g6*@j?+w{uJSDd`|=-DrL3>ZROlo|PceZX?EM)u+F82_8Sq zqILCLyrgrU{G{t~_ih~S?+IgTYe#HSN5R*HBG?sHz;NyRMBAw6e5m3KaA zX@zQ|O1C0QR9ABycRYJt?eYZCI?=i%1CzEc7s7OC=tFK|k*F*Noy8=C#WGO`jyOk0 zTb#44gHxabj<9VzVLNUM4oAS@vWTPn{}7_08Ij@a|0ldM`^E?*e6Cdphz@7P(_@)H hWJDigVkuGd2&O-i&R`$!W}^QI01C;+v(kf>{4d5#8G8T# literal 1577 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|9zrHQ45 ztEr2ttD~!tp`oL*o2!MHlZAncv!$z{n>ox3Yxx&em2**QVo82cNPd0}ECmE);3jg8EWEtQ~pQ^*N3eV}9XL5Tw?vA~3YDGVLUpU+CCUkLQh71Z=_bXjenqm(15yU3L_NMlP@ z@NAB#)&v1jkt@p<@$)QESfryAB_iRv`+yU-bGTyE<(_jE^6wv)&C64py@2n|_jlIc z=g2?*bH4V_-1#9lIXUz%J~`46edXpNy@MCS6|dwjIDI+&{wB=- z?qZhu`qEu*atb4!M=i%rBMpABj@D;Wz79`gSA6XzlEJNJl1xaeoO*^IO1X5O72%PS}rpK{}jw2^uE=Zp)rg(HDKMSkC6Y@o?f( z$?z13iNU8I|GxVxvp9dV+PbM(F_FLjPFw%yZTyN<%@|2_O<9#KO-?0K4`2V!9P!is mNX&6F+aI6!l#kbO8ZdbLzw+nbr0t!c!rRl;&t;ucLK6Uz>r6cW diff --git a/toxygen/smileys/default/D83DDCA7.png b/toxygen/smileys/default/D83DDCA7.png index 74c1d2b20f74af4c7e206dde8a6d9c51a529b129..1602702dfd1b1bcec05785be28ecca3b3592d694 100644 GIT binary patch delta 1300 zcmZ|OYfutc6bJB|N#P@Fvxnv*N-WcuMapu?d?kXLx`d=us|%}W8YHV&p(aWxWT}Z- zWb`sOcV&kZ%QkgaN6pYMT}6DY`D~LiA3e-8y^AyTVL$B5Z|2Oocm5yFnK>%MD#MHB z{G|W@JIu-eUD1#5@bdtmG|T7{1EK3tp(KhQ0P$M@NIna|tnMj!6o5D^059nP;3WX8 z=SV9KxadCG$i7qpgb+v}$p1nz4=&9v%AEn?_n&3G1sRiz(#Li9K=^7=>L?IAUz9w& zC~4rc#Ao1(@W-(51w8oYr-!d${xEzz2_4ElmW;usDOfrN!`l~XCt$-Q1b5yE24TfG z#MI9P_riivcv}l^j>7MIphMl9ry72(hF+cUP!IHex&ZiX5c~y+Eek+4MeBsb4(O(a z;MWOC^ZXB;x_RJ}*SS}8^KIl(`~$#G4)fb@nSVTEJU+qJ>kT~E*fjI}g*az*huTEc zKNls^*R2LD`Eftd3y2U_Bwe$d>*GgQ{?Bp~Lt9Wu`3(br-syNMjZ0yiL~&xG!}*ac zCW_1BFj1%28~~g%@(%7-NU_F$&#n1JkR8@&DKsArE>@_f+Sx5aEShH?RYOu7zEMtc ztA?ch8ChV?v-`x1vGcP2sPriKP(6FpwjNtJyS$Pt-^t?fNpI}4-O#**D_jAEr^ z5JJoTkPI6Li@VST!6zHzv-rhFsE@s)_1_Y3{qy4qc5&yBqK2v+CVh8?n68_7l__Hl zR?|J39#W6lZe?)xrjgE%6}PSWPD4m3R<2hN{ERv;_HD|$wO)JbY30_)I+jJ6g|Fuu zt(X`uu%idmt3KuC7v2hscQOvrtkAwQbL(CjsK^p6Ri2kug=8bg*?c`zL908Tmi-IK z2&snF2v2ueD{?B18bwH@PpRj`au4oI+zE`VsVZaEm`=!)Xcya(idH zNYPoMag{4ECMD6pH@t4+y>ie#d6#o&nLYW!45G;L#pRwQ5wzh9nGI)y5?v@3KdsGM zIr2Uu5ol~Uvuw`rR>!cW`*7xY$U>#2%%r6}Ksec&WU))SU$Sk~#T%1z z$@EQ7Z_>b#S^Gi%!~|80w#7p%-`d>G@_%&s-EhFogo2ktXQ*A*vNKiDKC_5ugY=_O zmXCDD&Ig%3ULI7l_E35<@Loo(saFZ?YwTOnYa>E~=n?zY4Unmn5%;0km~Aq<*x1eb zr>GrjCn|3sF~rpm!^ZfzhDbnlru_Q#>+($C>rU>kX;pUBQ;}OxtnlNENXGFk#-^)E zaSzH%cRg5}Y9Q+F-s6n#>OEJdk49gU2J@xY3ef}8-#$?`H8zoU&=DX-vf@2)p-7`U z9$PMf#tma|nfTBcrmg@S7Kg)NcVirPQXR4Q9ZvY2yB)AtJQgeQ?XUPxfE^tc8P5Cv ZfcC41Wx9ZQwyljJ06qkNFS#dO@GlUXT!H`q literal 1356 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLlg5CnFPc6PRAlyyB9?yyR4vy_rCJp?Zz+>a}t%N=+=uFAB-e&w-_YfQMR)?&xIZXaVxK zqq(WElZ%0ck%5Vev!#o<5>#&rIbo&`bc{YIaUdlYm=G`pf|&5659GizPih`8#}omx zM&Il&nG6h!HJ&bxAr-f#1jlMOCra#-t2Xbvwk*we@mh_j$Ltz?PuLawWD6Rm9BC9< zDq~gnph4c@BlC&%62%Xc)jv&ql6)zrdfxTY{c|?Fy<2wu>ebTt_r>?WfBtxATkaI* zvbUAy@eAhgd$-(jY?`7}_11IFLZ2&BjT6pX3YoOeu)Cmh66=*RJEq0QepzG*+BpE`QRkpSO5z+@D3-TmQ}5_l}oUXUUES`xsg*{!RYpXpy0qThd?6xOV>9 zId{3{AL87ttj>OT`3cLtmC=91zw1gASJcdPG`Hp#WZtjy>xGu`KgU+4D&FP#&#PC4 zo22n3#c1fv`f2j_>Z0<`|I>rC&Fd~18M?W2Mkmd`vElmT_oCcar0z5EFr?o&bWHsB RBv4t&;OXk;vd$@?2>`fn<0t?C diff --git a/toxygen/smileys/default/D83DDCA8.png b/toxygen/smileys/default/D83DDCA8.png index f8039e1a1e6ebc7ca78d636f9533b4fc9f12bbb8..c1d6de3c760b40c6e630829bf5759b3d2bd1c446 100644 GIT binary patch literal 1862 zcmZ`)3pAVA7XCdds#@AkJ&N9_XibrcM@z$9^}J{?-jAh^1fe?BpbXJg86BFamb9qH zm>G;aawTg}LKyX|G+rerQjZEjiBK{1$m1T%T3wyH);;U{|9#H+_x|?xpS{;QL=ShT zgG$GhAP73>;_ToBtm^KgCEk#o>ba;_vb$eMU1k}`S=3*?YtMjc^5CI$aTZ`%-;b2m*ZKz7*GL#$sPdh z-UN`78%Fn>AJ^=D1-ZTi$ooG?ETRMd&tia{&j1WS+h+hapz}^~Q(WE)|L8OjwU-nq z2eS8+1MvV+WLi+d<9!PIzm#3n&ZYLPG`?N`sf}CN%OBuvzgv+E&hOBA`9zwCR3&QR z{58HJo)k#BrZ(C*!e82EdSr(doR_(ybPcNt2I2*%@I}-x&%tz_WnzX!Yp@+Lw z);UM2>U&ZiBkKWjAp-Jw@&S_m!yLq#7(2ZmdPzz zKmQqhIO7k|i;)c$Puja6A=kXAU@f&wZL_%BwQrV5m8|lvc?xr$NM%W-((BeR3vh7$ zv0m=G-S@ZQH?%YT{YmvhFu5QfKb1uGP!ncnTSb8vE|Vsv>cfoi-GbBYI_v9)koX?z zg3LpK!8M_<&O%xB4nK>ikiGJWZet8TcJTMQvsl0@6MV_Zp#fd5h5-v$tc#l?j0ZcU zq`$A*GN1%BZ*rnfK&%%w9v&5g4~w{ogU1r0aPWl4Cnmqck?m{ z0y>9kWzL1?DebaawKZFMb)S{@tWd;4X8?U z81Yv?1|q=X*|srHrvCmj;;L-*J$v*qlN?t_7@XG553EhSy}x$3=BRG$ocO!>F=EOr zhRwdhWTszHq*{tjf8$RSTUG48rnH;}qWSwpz(G zr`{-ib0ADj6Q8?O#qx|M|B_8g;rXiRnX8|Acuc!kufe8SR-x>uhcZp0*|&g%Ob7}y zXOY^iim&T1gCqm)5nYznFwAl0SXM-1!jW)1lP|Xg898CLFN7wC#cPpD2~kE*1xb0C z?*4YI`ornjk9=QqTIKtc{0rkQ86VQ6zqOTB)OlDNP1a6GXhjEAYoiGpFFkC2wqypM z?j>-q*CqD}t0^_h2FTHf)O-rz*W+QMM;JCRnA0piS3HUrPNZ3FaJa~MNt5%!cKP|j z-k>%8dpln--76TI@Ib%tvi9{LyZev`B2mQ2kIg`<%D6b#`e?92FgCD97&zPjVRZ7$ zDWvY}mW~cYCqn1MVa>z(a-n7>sBYG4Ged{2j#r(AC^?zbV7rnd88Kcl7jG1ho>K}C zsU?iGa=*lXT@3T9t9y(zG)ygSPQkk0x|(E*^^7KFn`qfb1QHx^zHx4mS3K|8Gl*Z? zhj`z<^j**$r*Qvxr<+eSt?cpFqz{NsPkoz@j!fh{%I9e-tL6HQ)P(itTj3=?{FtG} zi83`9zcD)YO(5gu7AfaHepZ$-G80v=h#Uf+vC*k@`SQd&eT@Wb>p4Qxxm)8+of&yB zMUBnP&9coV`Yld#d%B9e#-?q{+x`o1lz}C}+*KAj>fyW$zgPlp7f3(WF(5V+8;e7S z#NdE|Oi`w$#wZJ86LTLE6x!@8`Yg&2g+ilH*|zT~e+!7jhem`E{(r!@7 rjSq{B$Hw3w+faNk4(<|(4aa%mu%U$L*En#wL6D21y93P*^TU4uXBwly literal 1727 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 diff --git a/toxygen/smileys/default/D83DDCAA.png b/toxygen/smileys/default/D83DDCAA.png index 5a1e68d29a1622c4360b45db74c2805965548533..50a329b0f4142e197986a74f6312602d7de632d1 100644 GIT binary patch delta 1531 zcmZ`%eKgZ+9Dgub&7n@^rJL?#sT0+h$dJ56%SwxT2*sEpG45tl@f%GSU76c$B}pSO z?|IiIl9Ien=4DN;OG&sxUN^Vw!pXYqP&+{yza@9GykO}}` z-vQ0Da6w$OBiaG*EET2jSA{t`z=cEv;Hn`2EH(hEu*8}GfQ|)V(hmUqeE_tn51*g0 z20)4B=IZX$#`b|@=n-=rh|)sV0h|XPMt#VNUZ*{`gVw=lejK!px{}GmylW%5P(PQt zuKb%}y)(%lrX4rW_oQAL$e_a*-X_U`B$*5;@7id7yohsI^jqkR`>9v|b?}A$?1!pK55UhKO;PsL@Uk zozy<5X>8h~w#VG^HUP@kuey8ENd62oH8MOXIFy2>MN=tg<`pUcM^f&3cylFKnpxYTUv) z!*(5&8yd}BMy%tj(>aAC>DuV}3PejCL(0z-yscGO^ApiPzCC->=v0a{!PK;z3%%VxL03!Iz%lt zE<0AJTFEp@Re5`--BRPUP8VH`<*Xm4z!>yw(b^pOy+WI+#JAr)@R=O4!J(TuBX>lH zA`*$EQRJ>Vmw38U!G-Gw8>bD3Y;Df8QPUSl#;1VC+Bo%+uAAF-FnJ?-|O-$p%69o(Qdlzo5oV`+{>=)^s49vGc2{=OyA$DI^8hSqp*C* zp{$6Wp;blr#!h#d?1=O&+^J`=(Mn|ECVWR`k2WEm#lI@t8_sC}=7g;}cM!?#GN@mO z)C{L?cIKgyFx#xl*dEL!-TRv?QKWK=O;UyS*ow*uxM_9KZ`C$Xr)|<4<2*m=xxrR( z(8%|ky~CXyxtOYuK|R-Ql^qPpJXcW5w*FCjBbv5bJZWetlf~1yfmR-KqY#hPxf9(S zYPis{zm7A;YwAL{ww^p{VsISlucM;2ToW~DV`E#?eQ2viyT8s|fwXG1aHUiA9( zEtW7{E*w=YKND8Jg7yl^Yx-l&+a3aiPRWTk{GWut|Fimpqn*1>Z-5_AK5iHgv;osd zCWpL9Hf{_G^AAoiZVbB;&=zOh7~GQ3P8q3YjM!}%q0>ekKFya^tPUQz+PYpC6FAONo_i7d_oif}I4vzLyAr}NrSTDt+ubrM>Q>_0{7W8>S?63s zeT3fjOfo5w5VFJVn2mI|0TyRuWo5e8;&ILD5cOTNZpU$80%H^wW>lX7%`26DD zzFrHg8MzSwLM#nbgI6o~p*eZd0{v+ed_W`xF2EFPYI*=`alpjP-2{ur9mXFv rHNs-?SnP&j*^U1gu7n2$2SxwCfpNpdHcfx;CIC)2opLO(_ly4rMse|V literal 1526 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uta^b8ObpEpObtxT41uOty11A*8ycIq zT9}#|Il031dgc|EB<3Zj!tBii*$dWd=7v|Vm2**QVo82cNPd0}ECmE)q+=({Bt6OlqDkjv*DdrcBzKf7wCen0);l*_`LM%VzOQU214K<8JQdJrCDO^Nwbg5-hf~mT&o?*Hi~S~qrY<;}Jon9a z+x`2i-_Lt&CU^hXl)NS0Q`6^oaNV8zmR~3CVEqyQqi*}^o_+mu^fYUzzLVdwdyhGM znZ+i%3AzMMFe(x7tg8@ln8U^~iD&uMBlG9qPuxDcKaFY0GcR+?dB@l1&wg*!dpA_Z zxqW+qX6(wih5tC#{9x@{DWRZw$~M&e;rFV*9j4k2=cb*yF5JacZ_@MV-u^#(G(7b` ztFMk-9JYPSoPV89-)%VPJO5y)YW5O~dCn3nfjOHC8%6hB&HlP)j`_iNtS<^S}W7a_!r( zU*glMjOGOz?Ob}I+GVvv!y@r{v$>b^YA!y0w9(kb;6<}IOU6?!qobdiE4U}E((5?$ ze?slM!mkCk+zX#K+AnNKkL5a(rKEn!u;Iie#z2Y7{&VA#W)}Pj z*vmo>+R8og}Lf`|1l&sP2I`obl=(GK9X_mg(79x8ix1SMx#dc*o)A`#=-#u7uFIAg% rdHteIht1aQynAg{_sy^24U7yE=k4D7eB#+wP)Y3R>gTe~DWM4fa~(SF diff --git a/toxygen/smileys/default/D83DDCAB.png b/toxygen/smileys/default/D83DDCAB.png index 999a66701611fb4fdf93fc4ff6381b661b8ae6a4..ab3e93e0413c268755a5c6a5c46e77d6c4d6a1ae 100644 GIT binary patch literal 1705 zcmZ`)2{e>@6#tI~WlJUGd9SH6_Au7Gu|*_L*2YN`GnkQOY>nNkDTJme+n@!Vcv4Ey zDbZ%j?&Z{A?8+V?q`~O_K2J}}Iqy67`|i2Fdw=)-?)U%B`4a7HEJTH5ga81dmIuuo z5HG%ZZ`^=96;(GMMjYD9#M%Ua;!DCj506i9fB?O|60T6-%;5`ul zd?o-=f!E6I4FEu0wL3&GhZDJQJPVFohC?j)E*TCmVP7KbIS0F9Vdq)Mp~LAyICT?p z&OlBKjDrB-`P|VShIC- zsu&>lZ@4P*zw9@`2=b|F54To-Rex6^QoP7H&Z{z5r6B8cGmwHAq(jH4uWNQC!fxhz z|F2uGf(+P`^zBvjrmWJ}aHJkdAxK}^LZjh)r2?;?3O^+92B=>q&%w`G@LdX*P2jQ} zVQ&I#Iyze>0f*CI4+D;-z|kvkhyllQ;BY4Fy$Hv$;n+n8Va?qEMdbW~Xbv{3NB!fa z(zr5Qu&>frRO!^jE8|%qktAM zduv{bjJ2XRq~43PPMYEoBezA}A(=#e15Y^#Q+6EtC+-v{=i=T-O&CQF`b$Qq&(yfjISeEaLWy!{8rs!}Fqp^6kvj^zamJ1c{I_?z*30WqqvI#VZVf%JuXt+wtp19Rm9m=Mrdv06U7A6= z?z@?Ic~MDQsjOipQu;s}|Jw`oIesYB&ewxrHczXrXBP7GK53_t`iK zC6%O%HI25(r{K`c(dV}Ou}n}r@1wAgDZJHB#bIUSB%l9IZ)GRCpW7Cjcv}{V?%Ird zZ1f-$S43ilgznBeb`*B$sc4AH(ga%KC8saYjAvFY>lK`1et|4;#`7#zi}_?NUy+9dgbd6W>X<+Wi2-k{ibsJ=^taXT2cN zkS_!77f>vGvg`L+#v5tg%#)&F_EzO9?1OSGrHxaq5{B3R2#D%?%&=0No7j!YlTDyT zq{@an4P?n3-OPH*byHN#(Ib{vYUy7rMpvM#Dcq>8iS!hM(|<-7d9I&-mzv9WH-!t!<%*oAUfmA(~20ITxctQK(K{adG$pE)JcK7T7dd zhkTuVmS!dd={FwY5?dv=Z1*5~x>HDGkI?~7@r(h{sRM?z(^ znt)EoZ%L7&)~ZzT9EOR7)D1V={)!}FY=s9}=M|@hErO-v`g^KgpJzxlmp&@yA1{3n zHZbe~8uTUcXPO6tLxYCBh?F1|Wi=($U_qhq@W=?Q`}dc)>8y;!qbGwt&MqE7<3Nk> z9g*8YVyM~0{e{)i^pSWAvvzj7x%r%M4ZBX0-usJGPHSssX4dtCnLE)uNi;IvBbbahpo!DeRK@L8)zBtr z;P6^Hcx_!391f4e8R@gdejrc-JiRFq|4*=`_>~}p9jg-#2T*8Xq+l{I@eFV$V=Sp8 aFR}xf5>bC64^(K3}p2P#SEOy z(WQ)&p*_`=olGhO%`IUIU9yW>gSiDxO@}dRKj#H(2ue-&duew$Bg4f^Da%__!_93f znDtmxYYmi^^5RSxn-vh5f!60jA{{7qXoAy8PQ_~BZj8HvzH23 z6FnuEld#X)0xOHEOqRWvM&tAO)IObB5KA?P*=!DLplC8cBumx2O#730DL$e=Fp^tj zy)rBCa9EKp7OG^63V1pb0_UZuXTrQRn<$Vnji2^v5VcmrapAb8(URSq>64jB2 zfka6JK}>d3XEN!uHUnWZpn5W*@jPydO^aH!cD)(5;s{VN<95P^+E6?tg+#3;BErq) zC7I^kOvEk=?9Os^FXUpl$k4JN779W|qyijef-Fd7f)~aMjPRRlS>7Y~q}Ac$nTeKQ zM0O+Nv5Nu+Pv;k7pJN}@SxGIbN6dCJPNE=goG_WKHWO(#m;_?SVQ7n)6yVVJ-9zgbw6o#n4MS>WAz4D-BcJ z18Y)OPSCoNTydxukIAwwUU=YYgF9rK|GP32dOzo>Z&CL~1$HVX(Y_ep*6zHX;c$fx z+oV3Wbv)3z@k~G0wRK17y-&tlx+hxp^z?V$RrD^IGZkaO>$i{9TgO+=TXDbWbk^=D zXnDrSodq>_|9bbSxxFTopwRR*|2=<4^yHW~NVV51q8ITE-A!Y=V^1D)HeI+f@8G8D zkG!U#;cw2lmVO%eEHS1ne^;@o;`ShXWBJw-@p<{nL;IBX_~4~{(_qHL(ut%b>iof? z=+UEVaC+OOkzdk&UVkEBd$Q`cUn}Xoj>8XImp0uTe@{0w(crpzB;C={%RXq`O0`ZV z_S-g0_VnilHviG?rwSG-7Al9dRA~JN7waZFPL)#~b={Xf=Ys?5GWn{gTYV?b?K<1s znQkFg-O*d-(%*Hpc#h}n{3-sOYl=dB#@ojBz1P~bql$`-TaEh~8=t-rx_G`1t}G@V zosavt_Q;NNN5z&ab1F8hD-LX)_hWNY!egmU{dH%MOFY(jI<;-+{-F!se)-{}ho!H) zHR_%mS$StfX}pb{VPX@u=k7axIj*FUFYq~}rdM~)9ZYFeu27{?qYcaQ_SdX#s2y&& zeAd@}B_^#lGsQoPDS@dp_-=a zG1okV$kSFHPTDY*#&idgA?3pfl%~raZw=< zy{qHB9+Aw>FDyda2t=c^vJb5tL;2jc2EKS~Vs2Kp@LsksH9J2#J}VMSp7CEp0X%K$ zdEGBvO#5Pf-u6a|qffD+Q~Fxfj+PG#%jnOWUAqrOWK4FX zVD+NPr`n&33u_|#1971XQME+s45M3IVB(RqQPYQv=`a4n(pOWxNKxqJ!Be@cUClQp zS(-#F=@sqaIcP9pR8)rINo|Np_@Q`kRzwx=oQ|7Y@9H!ZT*^q%`24i#k)>wk+NAJq znv-Fu_C7z?GzXU9oO(=3zUX*>?1uL%+W56-riPrrwOD=Zs#Qk!a8itl5r*XHF)P_d z+HobOV-u%UnemsDmt~(^RUwYABixjiCCYbL1LKHrK2v{-98twuD{b(CZCGo#D&es{ zNmIpcH2NGWzH|~}S^Vaj%?s@{z42~LaW<>p+Bp%O8lv>xTug3;2~)Nzx0nSZ$3{=+ z2wW0{w-@&t-E^i~A@6RTI@zUVxii;)3hh&}eYuKcZR|Z_>tU<#w0PLN^e{f5_KLE6 z+Zhc?7qcYu?la#6YDe&iN-A6ZrSUCai{6^#PB%xZv@7uvkMtu16KC;nVdtMn;GdMY zw7XR#JLc33t9r5*4-iqK*tC{ZPq4;ou5F9K=8Xc_o%MUyyKC>lUMPFkYN_JFt<5 z>`l8hmfBYI^)ehE&e{xnus3!oOwput3J5QmU+_RWowmAZZXjkP4BG6q(q`_SX}QSx zTW{d!w>zF?OUu)Qk$HPlX1(Q=%W|XC>!TB+Nn{=Tq?&AH{pZz>(aD#yT#E{z3mtMi z$h0GG=??Jo^9%H+Qap~G3ZhW`0|DGJN|lHW(bNpV5>=xt5ePLGZ<|;pdmSBnrC1wp z7d3=QfXxPy84ZP^nUOZw1ehSsIT@YRK;cTcPs$|JVQyu46$>6??X?4e46&yj)<@w? z_lRhIVNtuy8IhGhhi1Qrf$%{mGB>+g|6+@X3)S+a-N~^Xf~zFNeS_V{m$HYKp}X_F#9LlXv6rW)^r0 tQzIM>kHaB`<}Urm5fU1Bh8q2^<9`f{ON9MtJN1c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYjTrCZOrZ~D9ySNz} zIvHA+I61i)n!)sX<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3owfNt!Vd*p{^RB$^K(0l9 zS^rIJ;uehJ(b{r%r7r72?YkkbPAzSH=VPTZ^NdfL+RT3IdBwLoE_GEU*uAg3_j_J7 z-|0;}8fN$9Hh4dMH1}puooT+5uVO{7;jv8K;*?1q+7j;V>%{j+SS_CWwE8nwipqwc z?-txyadz*@Dcsvjog0=J3iLeG3VyVLfio_1mZOYng7v?=<5F9uy+3EYX7At5>)BKJ zzgDGgOn5cHnKAac#-WMn={5(29z`5tVf|Ccb9dd|`&-|rCeAwMwKOKAz%c(nPbX6o z^Yb_6jGBetU7Re!LKzL6ug42oI@gCdTsXsdLO3Vm!18P7n_5=rxGnlIcY$KhA7X4AM?E}SdBk4aqKIX{ZV$Ouu{UU-L{$~Ifs{z z+lT%2q6sGao~w#}pHTQc^?2c?i`#7_cxtbzX`XMsY}v6$B*wn!TqpD1#_r;YYq;O! zTl1)|eP|G|CU)M^lB=tN<$Y{bSM{kI?#Sa%x8+~6kJ-`S=JEfzrl<6`+l8$&Th*K$ z{_Mb4rvmqdtET$R(pn^Qhxd?YOS6ch(DL0J+vnLt6tuire{8kMBKPBB7W4YU9?KPl zPB>5(#yGR}|GpHX>b@rH)%@a9JXIBfccxe?TPzopr0B55@3IG5A diff --git a/toxygen/smileys/default/D83DDCAD.png b/toxygen/smileys/default/D83DDCAD.png index f23fd2b12320e9e39f1fa39c2d8fcf1c4c2fd5ae..9f2dbd614e873c0581a98f2a2a49b93b8f722dab 100644 GIT binary patch delta 1593 zcmZ`(doZBs05K)a+s+pt|S&wShYdvCH zugD`YgfWsb45ktC*xwj4<1ywphB3p;*xzn{>^XbR-gD3Qb-$m_J)e8;xm+!dR*C_6 zH2}Z_qk9T*sLS?l_5f5SuT%J{BRSg7*~1Nhn|lGE!~w8`lqdoK!m$8M`2b*>4#4KH zZ0bon0Myb?xDp&!R#s}7KfPx{tRX&~FKv+_drOosH&a%Zm4gN_jz;@nwW(q zL|yFh+~P(ul|InW{^ezDNADo7zV&k@wX2)WZRr>+t^L^AISTRNoFZyN`_PBpu~!Yu zruRc_U1Npiw2mH5N@jUcRU2Zf-u7fXuSJ5)f;R{w68w_?8nMVs&8k2+k&Vk+DU!i+a`y1_i>fw8Klk&fu)g&Za%^MT^)Ez$<$#OX-6>e}=4y_TcP zM+y1Hp#tfPLkvZwI$V$e6Dku7a}YYUi^q@R!9(@!&PcL`bj;0h4R4K(<{r@CQHB9v z)$N-EFOr9EBs%PR=%v75e>5pN%pV;U5(a=x(lbx{v|%!KZKEW?&Rzr6cf+nW{J7`O z(RU2(#usijerPYLBHj%M(0egbN()wUYzmSB$lSnU8$ z-%=A5Re&S6>SjdJ@^f--rPD@@+y&QSkB3nE9kz9AGcm=Dk6=x5g6k@Red9nD{`Ypn zm~{~cCm5?GDjdg}%_C&zbG*&(X5H7}pyv#(eQBSlB zb})HYv$JtnT`J0p@S@UhXWW{zxf%r}AknU8X? z)nw;8Y%jj46WuKqZ>W**(;3tJI;`(m8pM9Gm3zo1J=3|=#jvC(R1idv)x;E>q(ART z>T@}4w*dQKF8jD}O`IR4)u79pb}j01H@wQeJGqjgX2?F5x-BX3l#$Fgv5XY>jk;S< zVH&pt9|>&C_r0O=F@<$;!^w##J=^tX$&$SWFnl-sDe<7^TrfGG^8Gc633^KG)bT0| zZpi@i=%n-P^*F=sYOR)R8=5F*TXt%5hV;hU-pF;c9-++h-Q#R$K@-cHEiI3lX;Nwu z3-m@5DM3VII;6bdKnBH>g!`gD;QPn}?L3CJCv0K)`2aPiy_Ytf#zp90$?;DTv*JI0 z7`)G9CtU!$(SliMazZ#h&EKhKg3{O1(UiuoO4eI{QbWT++=DGT5``~B-79=bBG|<3 z+`eI}F&({Cq|SnJQ%sDRc`4jvRN6*^ryd?kR$qRmqTs0k!P2t2>IT}oS>Wxdp<%ty zsEDOJENXrJkfX2dmiGg>#-;P4nQ3Xsm7wXG@FV6dHmf2uAUNo1V2BmCuATJQV2i73 z7X$O{%d!y4s#;-$mPyMfZT)>+&?dBLJ#A|0f(Y9~a`Yk*eM$bde%JjG2Q0CcmKf{- zjKzL}1=iNe+IGK<85V1c#a=~K{QcAa3)meJN({Ud{r`xaXEuvccNuE}aLmyiU+&-& F`xl8WSLpx% literal 1636 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY-niw0q8N>8?<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3owpg#ns#ui|=G{w`!F{I+woM6uk5l4}~d(U1nF0{I99N8jqv6IE+gHnZn;2upm6+L$S zsuPwKUmhH6?vm)RtO($@eDbJa(u#}@w!(w0YmVsdPCItP_T{{#%d6GyPL4bE>Y~dO zl|}qQRp?4on&|jD3_J2}lEJr(2EDPhsm^k~o9|z7I z6pcPqJfY%4+oRTu>R6LIrwpbX`X#yA>X2EaSX1j!n}Sfs$1zDiuH8UK(h%da?TY^eCHH)$076 zzhAtav}|_7mZV*FdlZVcbsSsnvhn}XBa)X+H!Yiadh^O2%b%`ni*pXTC1h|_^tCS2 zb=rI1z3f8yueKYvB24r?B-ZQ7v&lQJ)!vpX?y{3%Ri1;vi{|s6PfE^uykqvqV>ebx z&13CSGVk%bta9bLi-Jf}qMoMZ9sz6bV3sW*8~5$+s<&-qQGCR{X=C4}oE;urlcr1w zW%fC}QsRM8Q<%2Z?m#prX@3&68xM5e5b3&BsOW%($4%Vg#Q4=1_)^G~0vHoyX(4)?`dD% z`WFkm^5Q93f9CuC3G@7y3C&R7xW4*>{13eY43b}G_v&r-Yz3A3p00i_>zopr0GLK> A$^ZZW diff --git a/toxygen/smileys/default/D83DDCAE.png b/toxygen/smileys/default/D83DDCAE.png index b9af846b2aeefe429c364ccbd8088fe00e67c057..f28bd1270e3738a6443a6c6ac2edc74fb30201ae 100644 GIT binary patch literal 1534 zcmbtUX;71A5Z)XnW2ezoK;@Nsz zw6OIvkTL_#=Oya22WhDO~xFN(MLmQUSs}fk)bUB+Bo2?$ZtpC zx7jJr<=_pWIrQr1Qm{y95B;Ep16XpO&v&W>`kBxGup3}FCTIwO#<}n!Pw#uy@6pNK zNlKq@_gHqK?`dB%2QE|LF$xc`%sG0WUhGzKyh@fYztAbAyA>>#jMgQ<`(mM=2Ym>* zq*PbD_$I6D6Q9tQOe9IWMv!Mv~)3<3sF|5w{xYL7|BQb{Y(z&?_c6B87H2%Ue9dl^E*w z2aihlgh%Y)LMt0?(_u`;bjTSV8GWp3jx$0y5G`(HfjvrOS0K(n_%$#2PeZsj66xiG zMa8p)P_Rb1lOoN*)MnB|mmFehFBh=u&_@gj304`yESS{AhkgVcd_pG*&T!-o8y<0? zgAUF_5vB*|&`SZg0vX`p9Wt6r8)S~++ZBv%tnehYTS&ID!IKeVrh_j%)64^`=T0g0 z0UIt;U`T))!F8x)bvM6k>0oyvgwZ0O`cX8<^5beEjb#RZ#$W|Pt|;;fUoZB)R>8lu2p z6sMn$9}<$f1%y5>SSa8Sk*pjTEJ?V4JynUe@Nk{X38riUf;(RFd&z8!P=|u;2tzPh z$FryQ2uUpnJQF}S4{w&Tyi>!v_(W?M%OxdSc=%xz>QbUzDBKm`n*-q%k8qCz?f&pb z@w8TdxXXr%0r>0KK>B>kR%GL_Js39?T2^o^n0&o-k(5*? zkuN3Y8N}Pzq@v>)rX0MOrP9^cvKO1Q22Ptub)%}5udl4%RQ?_%39{Xe8tM@nzv!qYSWr`*1lW2j=g@v#`iXDhAmqW4qCVE{SUbH+XHwzcEX2z zf$*bUfx8>Hd-ev2#QR|XfnfCD$Df28LJuF2NM%Ri*zptS$xjayY$^ppY=@V_V)EBL9*fbz@XV;9kSUSP8W(|ffV=f zh-b#AckIQ>R1_^+SaUP3m@+X|p{BHAQ$@L{QfFLMi5bvpv|6<$K^+}i7_HI8#OdO+ pDvd^`(U2@ZWB>2J04u61mMpIQZ@}8R%p?o|S!ubc7gEZ${sR*z{Ad6G delta 957 zcmV;u148`%3xf!d83+ad001BJ|6!9s1Q?MtD}MutNklW1mao`MpA}E?DiA2O`Owg!NgJ@##1t0KHo=o&@i7_Tb6Cd5a1Ht+2tr zXz*qVAves?Vxy*_9SK7SaSF{0fV_jH=>>xa0)^6PL$ysU8-efIB{zhEwc8(~nZhai zd@^5h=fZaVCxLoRK&JqtFbvbVa&=Fm;eYhzHTO6H}sDEiWp7b-w$+(n~$vDxBSm_t5|6KEMvrb+apu;<;IV|?&#o<w*=9fMz#9X%*}_SS4-7>wD>S+80nnvn;{r;HLTsb z4XyO9>eToc}J?B@&qklV-R+QB% z;<$fJ;$6GsH=52bcDJm}ZBTR~F@#OCF?eNFlW`&++O8=$!0^bD=Z+3TJKg-Yywp~_Gu|EHs}vHM4e zt%v~-9?TN%unG;OHaiYSC4YjFQFecjrV$bFwm@6OU^0~14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>AJ94lp8sAqflwpf`aY(U1eW0cbMN5THt6d~(FsYy(F8 zgpwe?UXH~tb6xN~B=-)60Vk1ink zZ{GTj;zvv%$|&HU*ZEcu`LfyJ!+sgHm(BJc_WRsDp_OzJM6Re6v52m#Hh8ORHUHk1 zcb4Z4-0q6D5Eb{RSaA6HwLJR;cm6)uH>Ik!7<&(ldaB6P*?uVSB9 zkkRJ5>RRsQr0+S~Qn+(^rY0C>KCHfz?7i5pqank_>hXru@I7bTRpj#&b{DqqFUweY z_Ii|yWXyTRO{-?lT-UMuMv8;!6YiTSc~i4byqoaq!s;@c@Bam2+ix22h|gFu?eg0G zFCu{lmaW=-T+?*w!jvjq+47C7FTco4&*Rj->vn|m$;q^P>pevNzvgS`xFqy9(RGue zu0p_nORkoNu!VwBpI2Pz;X3$eWxr>@hItw~i*IpPzhUKmqaF2E>@&k1yIU$1jvJF* zt#}PZ)fzrM;%tAtIh+0Yw78R-gB_oo`hES}$s;aB8(x-7H~VzafKjx){@PQmjrHnV z51$f!clqgJyY9`|pY2Qv3d(nFShGa;_LI%O6Ta=^oLB23e3Qj0YDaYOQDd3j^3S`n zW9Ck2y|W|h!Gco)n(jP%?A(hQYdGJ;vHs&(!K!9e$8}0@>Wc$P6O8%JeP-cg5L9pc z%AvzNv&AIHC}PL2Hk*0{`G)1n8~(@}Ih=gg^~{2OxzSI#^5~}}Dk3sLwGuZYMCb8c zEnpMLEqyP&Rl;d*d^3xHrO38EPp$_KS+j#S$;{YuB;HG5ewKlNI<0+6^5-+% zZdujMACy||X0@x{`w9Q%U0q!ZBDr~hiSxOqi(`ny<>Ukh1~;`dF^$Pnd*?PcHWo99 zoAXaNe){+U`#^^Pj|i8L9>+P2j!r>dQEp*VS~Y7ITl2DM+xVE9 z*U2^SYg{;SBPVn7%9%S^nVXl2GBg+wlqDZDI)SF50B-} z%FE0B&+BVwuyBbm78ZJDMpm*;S}&osdR2DT>$EH84VAsVXN{wyZr`%Dvfl0R=Ut@j z-rBzfM;RX7G@Ryf>;4L$msLw#BT7;dOH!?pi&B9UgOP!ek*DSr1<%~X^wgl# X#FWaylc~V^iGjh>)z4*}Q$iB}R2K!T literal 1502 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ8b?Pn?g>Q=>r|34@w+Ji3KJEOo1RKJm~{D@XV8%2h1@= zz^uU*ZFrl3fr-!4#WAGfR?p>lkKjO=10Rd?Ums-m-gKtGr%2FPAk1)zz<+_n1txC` zzg*~w`{G)o)e$#IuIA0jIH$}Y{;xvI8mDDCMMvo5=&oW761ijO;jVxC=HA-#Z+G~x zl|C%4x3BwNe(u~&+v^1~?ti|QF3V;Aep=$#oC=Bl3N5p5TmRj)nA@?V^tR+fyROWh zi{i$ooBi5nZG80XlH5U!M-q$WoMnH8-f^1YIr)g$;k$=cChz5$-4qolZ1+fy?dYBC z+0W;H`KC0c`Rp%J)gM}BEe*eU&^DJ#Gyai| znL(#qPUaL@59OAdpQbJCQA+K4o`1j7>12PYjVnjJ6!%h%AcKu>u0_rMee!U`ufOW2 zE?M3Y?A+p;61wZ-kB4=i45lR{HbjXRGIf7BKV#9+_o>Si)V}IRNi4YMIp@Hlim!=n z6@r2~4{Y1+)Tuw%-Oy4cy{=(BzobRSV~?MatZjF%6!cdHxjlRR_3iVMAFIC$@f}=0 z^+U(KCu#kGAFe*{h_$}HZ~ZE#jUNwi9#SoN^kCZ0x>dZd_DugLuJ!T6qL|gcrp4;+ zpSx&}W5$8nJ&tY*)uUftk)CR|?9aYie_IxFC@q-2ep}FbKK^5?H}9P7Bp>_z*T(ei zQAHwes~URsf2?7*4dcG@_S!Rx@}@iMUQKSPTw(m5;rxb}-4oCMf6e`0^#Fs;SDpI) S(#1NU64%q!&t;ucLK6V`eLHyo diff --git a/toxygen/smileys/default/D83DDCB0.png b/toxygen/smileys/default/D83DDCB0.png index 4b7d9ad23e5fb71f9972068e81080002085e2c21..6fe6259889a4d274ed27f0e3ab000b8ae598879e 100644 GIT binary patch literal 1705 zcmZ{ldr;Ep8pmJRC`Za{W>xHp<#k(KR7Ax~TCf75vVf+>n1XnPKt+u#1`Vyzh6OXXZQaJoA3%eRHXP z-aGVm>j40EkcfC1qz0Qy_Zw)pve`+8WJ{EnuNMG~1&Fn9I8>w9M4B%EXDk2^p9f$C zYKf-+NJ0ZJ69xe83IN7}vi6_c0MMDBQW%7djg9l6h>eYsYZrt6Csp~3O>+Iel0~k{ zB3FxSDl-UGiDrMLJl-6lm}g!TYXMOZwMiallT4O5MEXe!h%?Mhl7lTF;sd{u$#c3a zC7kDB|Jqb{p6+cIwf-8?Ug9^eDd}yFo_(7Au8Z@}a>{$PxTBQzw2s(z@kr}sKWLB| zk<~x4f9Sr1d6;kCCbPd&fYtm?QJ1)_OhhNL&l?rJ<07V1VDY9kWU-I8K7IPj z!iC__n6(di%g>Tt-41M!VCK7GRy3)7Dlso1juFD<3%TmYlKEbKO{!f_1!-wOu=Z9G z%3=*Y&7;tm-A{5NqS(tXQXUi@(I^;ysuNaTr)6cwrldyV$&9;7$;$h@rIF;pD)MLp zZFw+$z^a$<5=x|qE@DjKdPcU|M1S@ z)cmKm*75u8qu+^|o@v)t|I?X($Oyd92sw3FKHhkc1K&r4ifuxYFJar{Hhn!aTm%8H z0RWbMh7lyBg{Pnd3H-xX^G!^*(ve z!Jvw^ zM&uKwu;*8HF7$Jp?z}8~?2WuN)^vcwel0z4K#5pa4^f znQ%0}mbS$<_emC`rJCNhoaL5|Iv`%sWv=fux%zG{>oszxAA8~chxW_rB5FKICLAma z(CDM2$G+=)pLESIn>sn{1tYrXjxLzFXth$86HWVZqIo5e>Jf8~MUT`ttBgEjha}S< zINr~!TncZX?ojt+N`9Pz`Rus(X#D%t#__I{J#$0+f)1u?@u2mvQ_l!bX?U`ZUjZA_ z^EEzDt(u9zHKqG>9hl3^t6rYYF@@4OM8bP9kZN`qfJGVwVw3g#Y_}pOunw(?=B7yr zLc1{X{Kd#3;vO6c-?z8CEOX-QR*P_BltDO)CDjrhFvniHyFD;BtAx=)H6C}<)^#L< z20F!8Coc6Xv^kGzP$mj7S4iP9&mLvb67u#2d3re7xF2#j>}u)cuk-!YD<`Qz3??lk zFtl=u5mkOGuU=kTU&oJT=pbsaj;+ltw^M}4Np%wVQ0kf06-!DPM%2aFp%70tPdh~R zmbl0Dz7&NRWXH@-%}nn>?lvjCVh=svmpvVPr?4Ig(J=)&h z2JK{Hhhf;EaSo0+4E8V@jYFe7T!-}kX5jH7Vj@NV-{2p~ZGsFYn4$A>VWnwpr$wYFMFkCQfPhVEo31T~n-iR$ zyD2)+8{!1Uhv$SHGETsAQ-=;YH_+)+HuWY>J(X=BGN-r{soNi(e=L{e_mSs$-jCn= zUbdtpFPIz{5y)b(ChK)*Dl<;?J`*_1J96MT#th-K#zY&b9NLB1ahA$LW#fR}isj;| zIA-yz{SH^KSpGRgnu#_U79nQJ%E!DIzT0YJ*esSJ(QU)b1vm|41pC`gfB<1p zg6R^2&|p*Jd4#UejvEV;)69hhX1N7SOaK&agb}dfGzPe>Ye)y;R)V9t2s8I?3qW8L zLKi5(7fzWBDS(=?`p?ClmLa;!{+)^GSMkG=Mo(GIwAY;vL z$w5+4?U*fQr3CY6+J*=OE|-h%67eZ}t^ktDC#?h-PhW&!wHXXAg-OR)qL`Ek+?Y)O@r44b)f?9++Cit{|GM!?v?I-9 z!v(3hgL2x<%z5NQ`N&M}UT???WV}HZ+X?1Sur(-Uc3N?g)}u<0`NFpl7DNNdAq_0j zKoSkCfgnhxg+(%%NSGu+lO(WMD$?qo_5$3P{PLXv&dC*#Nas3d~6-NDJk1%=I46 zi)c~YPP~g-w06o0jOG_1USS^=sWd`Z49T@}wFYM5R--bxDoLi%N@OS`M8Pqx<^SYN zz&IoD7RP^z#ka*&pm+MR^qIxW=D|s(W9&?8RNrkXX0iP0^{6V%eYK=CQ)k3xo_YRT z-L+d?O}t#>9PgC8Ta`J-@{2{~+;wT=y0h`%VPH#q2#-q*g3-o>cZypZOIq)NP}&bO zGpcv1Zg-ZG@a6JVXPP}j&P#2oqtb&jMijp*5=Z(66#WY~CUMaNwGTcCi>9aj92F6h z9}^2k0EZ+Ohn81*BB0sP!z*0-{k2l=Un`_(=H(#6IVE#+y&whp< z=%`g>Tr<6KH(VFWX7>gS!^EnX=4a0(jmg1=kVY()3NEn(gN&3pA}X zwUl06QCV~7tHOcE;?s9lJoR&?S1sFpzw1h1%eu*}c4g9goJ&il1#WHnvF7pgW@m*l z)(SFT^!ztWb;q|NG@RCv^RtHtpM$JlSn|3}Uh{Kr;u+>`!_Fum|l93JQ z_AiY!HP*wM@%V)Ab+*qv!6WOZTwZ>7?^-=Ky?)0@AvUzAH*R=??8vfO?vi*8FZAHE zonbT16JHNx%}gd2oH56hR)<>fMr=q|{=usCSqE3YeL>9TZdQE3Rvu5>JZ?(axor=Z zy>;@}nzMT+%%Y}F+Sy$)0WCx`n>hhf>KbqU@ma(B(|MWK-|n8MtBg8av@4{}<=7L( zZqM@TkvY3{tsytKoDbjOZLgBDw>)k)E4zZJo^dpv^xq~8a6g<8k^kq~+JZNqaz5^u z6_wXs?VsCp>R9Kd45ED8mHUIIyO1ZFYM|l#NrQ_;6}_?S{=V*O#{%H8@g4JGekv=% zq~;3^MUT*|go%CVx2U|6_SAqM;pycoZCAeG?d(mjOsM?3<-n)l^(QSXe^yUX{LSv$ Rbi4Owpw}d$N0L@=_y-LjXB_|l diff --git a/toxygen/smileys/default/D83DDCB1.png b/toxygen/smileys/default/D83DDCB1.png index fea93466aaa2d80c078698819ee79a4b4c046898..ae95bc24d35d0918a244469a0ef7d3bc08681019 100644 GIT binary patch literal 1650 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>%1PlZ?!VEn~ z&|!w#|NsAI{g+P%#z$63kRK@OP=MlVJ9*FZKfdhOvj?*7zwr}dlqk4v|2pRW-EKSf z7@6{2?~b*}%HL3k@E|7M;n3%mDo!~I`>o^@Stm1`l_)dlmfX??NMQuI$g)`M6XWb_cD|Wnf^csS1fG z2}&$iC@9KL%gjktD5)$+Rj9}dvEg>z2a`np%I`VXIm z{kq57;vY}H;n>lbIN{~<@A6-C?R=$A#?&>X8s}`0OW0P>`M+%Y zUDazE0uy>~b=aiL=GOmy=7}uZw&*t(uJh?_&0ODVF-dx!+R8oM1@3mPVvMU6#N7}J zT=|jbwrtqB(r1mo|Cm0Smlti7S5T<8yj%XM;id-v)X=TLnLaxuo|=ZoxSCBdwz1!1 zy{F=QkL-!=HHCYP690VTZ0L!R`OE3HXQ7V5kzem46`@ft1dOg^&Ub+MZGiRlMA9A_k+ z;jrM@8`hcc9kSrSwglVHjjYTQBCAr^x#b%*Cpu3MnzTh>U7f=E{3pyCWEx-opTH>f zYF$R(u?JT)>TbkZ&FyPxa?V;Z`AKl+<%LQHZQHNj+#YdM;L|TDm4*wAH)c&vIdE9s zrd{Q<Zc$6)LR(8F21Yl&eR5(h@I@^3v~LzI{Et$;QCKQ&Y zd^z*x&W=SI7iZ+&=8{PM%-!wXqr1LFsO{?3uRq+BBEPcFj=sHgZb(#UxpAcF?3>Kb z|8!Ru{bv93L8JY_{+fq7Ch$9HPo5myJ7?;7Q{G*VEySnqdbgKd@p|ccC%frQfjjr@ z+-uCU_)&A^{{%7l?q$zsH&qBZ+3eqQsZ0KqIQL7vtlH}5AkV6nxJHzuB$lLFB^RXv zDF!10BO_e{b6rD|5JLkiV^b>=b8Q0yD+7ZWKKFN_Xvob^$xN%nt>L9$IvY@fB*=!~ s{Irtt#G+IN$CUh}R0Yr6#Prml)Wnp^!jq}MihzN^)78&qol`;+01cw+&Hw-a literal 1342 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL=c`5nj#hRe#f%w)XwJ5VJHN~wcKUV=9zE+u7?02kk2Xf$G7fpWMIrQOb4^PIvS2Tg{Ku zw(iu-(x2k%*H@_Ew%R{}g?e@8fF!y)(b3)$N~YF6@&Zr=gz2 zvdS!~ut`wH;;-iv;X)DKM>6jNDvsLSS{m!bRr$p$UWoPg9D~9s&TPIL-kQBh;`S1m z=p^@8W5LmVmmM?WG?ucTw>YL8)*|hCM(KH(+M*L*9)c)-z}(N?6g@xj-X2{~^s z%-z5jD{*W^PDtrtu8fbXxJq&kWy^(aZ07R2*unYy@TNb#@(Z1XZ~2^#aALMAc1OHeFOJW> zT;3`fd2Y&t+5Mmr&C}J-Wt~$(69BP< B+s^<1 diff --git a/toxygen/smileys/default/D83DDCB2.png b/toxygen/smileys/default/D83DDCB2.png index 4e83e7707f168df15c76e0ba1739e3d10f4c110f..6b2c85a6078e2c51cf3ef28823007f5a843b1693 100644 GIT binary patch literal 1384 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>H=O_Gj#TT&gU4>@!Rl7?^6RLLy3n63Z0|it^Jkb5a#b zDhpB-Dsl@L7%Y0HhWcK+t-y1qxJ!=F+v(8p!ls*F_v%&uIpaoNqNl;9s> z@sqFKVB+pRmR4GNb*LsiRS$E35-*>+UoKsa&kp6tmrewCT z{`ZL|t$A|H*KN4Yb#iHxwk7A}*M>e(DdAJwYBuR{g}CnBpqmwC(e{>abx-BQzUP0w ztK{y;JzUuA?O_-DNyC2vYfaaTne)9So2W&`@gAO9>TGFy%>P@%^f$_n89pu2ULUU3 z^xuqeLSV)M`xF(eQ%p?H>(4W3E(pzN`C&C9>Y#wlywCeo1gu4XLQV z6F)QDxu5G*!FcmtfS;@1OxA`t8^H~>yURG%CAL~w3r!?p~+})-FDFL?iOX)Wux9UQ%ig#hTZhpBbYN&on#fve*Niu7V5Mf%i%m2+ifr z3{GV98$eVU)U z(=o-X?UVZgpS=taNq3kQm3H5v+gIatwlbrNY=>^Ly5)xtO@^v7`MvJ_(HB{w%)Umv ze8bHL&c|=C@dpd!luS^qIN&r*L-0fEx5Zly9u)k!I@kV%Zf$6R`d-^}MK8}ctHjPX zN#9X0-oydiVQ!fhBS#42L_8Jv#4Vl*-^^Jon_Y zPpykMAM6QvmM6-$VNKTByg&v;sk33bFN-oMEPeBp{qV&IrkwRBOVXI9%+lU|G>CCa z+*?^jJ8$bQ^S;m$X=DZ3%wHXY8_P_R(r0~o(S~bfG=rq+5*NBpo#FA92 zKbPtq5k30!hEwN)KabnX@CAQOMTSAp&YJ}*(wNDzW^`*Ak zxCIGTwTQuBlubMh3HHLYN-$|EbQhrABnFze4PVz1UoUTzN8k4PBF8alMRiKJRQQ-OoYa;&Mw!K-AEUP^e7R*m%3-ZK}%qn=-L!i^;5P=3A3Aoy@ZgXxNU*+Y{-a967u&Uw= z#ql+#ikV)(8x{l+A{3B=6a^TDpu!P`q5OajQZyMvHxr=3T!i81D5zdIvSukIE+?d_ zwvZIZ%Z_7mBw49ch)RestU;1uS=QsA=>XCQ*yFk*xdGkYR$~yLEnBMTsD=(aMrptp zb>cYkbS(tU%w*Pyb-S7IP{B5$~dEfp0-8Ws$BRD-&c7UDSiLMW=j(GBQ=hVuwhmdsZdZ5!(xo0M4A;MVLnB(G|fbrNH|%;rghtqbQ#v{s>p5?%ij{q z@fMUE!zviYaIFJ+%Z6jvWy1t~o&nqYR9!JDc86DGMT`uSmpoZj6}|mUUU4XS!ze91HJ8f>my-(c%Y6Z z#zLd9=iCv1VVj!LLZaX<9r=!gKYRV&)4p%67CT-&&G{Z#+V#a#fPL$=Q|A{BUOY2(;bQ;ZogLr*{Q23( z-1G-0#{M9)7q0e=+#b4;?RZyC75BBATe)^%ZvQ*(;@pdO-rwtQ%|ChcfS*{Nnprvo zPR@c8`Sz{DyUz;u-1TGg)Xlk9=6PZz`^49up6~Y6%}%$zxBT?xm9cpGwu7f3m$>n4V`*O8RDXSXcJjmKoinFyywLQ?%lBQ`+TSu#*Dm`nx6gcAzWiN-adWZl zjbl5S=YLJO>C!X5Oh0;b?@PJ1ukIN6`0#VPmLFKae)uVA?OCAPygyD_%nENNAD{dO DUuCCB diff --git a/toxygen/smileys/default/D83DDCB3.png b/toxygen/smileys/default/D83DDCB3.png index 6141cec482eaa3e2735c3c598f58881ee8a947e4..6976b53acbbb17fac088e76f12175fb83b2710bd 100644 GIT binary patch delta 1250 zcmV<81ReXW3XTep8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0L)NKR7L;)|K!S%yq;^ko@?aF zk>SUV|MAEF?zjKrjE8w%larW{k(M7Zhw7qVB5r&|MA!V@YVk1 zvHs+(+{T#EwSNBU!{pMg;?S+r!l2c}qTt7q-oJbM)Pl{idi?0Y>F@jR^!xSs{ow2Q z+2{A}^ZVK6_w@Mv|NsA$m6iCwnXx=)!&I!hG__dBCD>_u{?q%!}^BcD$ZzVRArW00004bW%=J0K>z? zT%^pW00001VoOIv0Eh)0NB{r;2XskIMF-{w4hkg>TaB^>E zX>4U6ba`-PAb4$X0020Rl~rBRoInVCpHt)rBQP)?$A4X&w^ZdZ$4@uzk8w@%MXMYI zhK2^l^7H$T@{L!wUi7XbdQ_iW{pKx9s*5)dpMBF+WE;2JZHPcVyHV0Yo2^=HjcN+p z?sKsoOJHNuSZr(NSm96=n?!WzCROjzb&2lS=qtf|lFDzt`xZ)FAp)W?AB;nnUGXx{ zNZxKByMJ+qy>>`#-PTr%Ft`yZ(k7d#tca)Tq(D{V3)!Tmjqi}%A(hmN46OUpi>zt3 zq~QXSkb1i1k)9w5*fcRSKcyxml}xXP;jX$Moe_D-7O;{nBMu->I`e)-B6zMl03r$v z)Q5~jM_NFj{`V*l0!oEKh|kH?42EFw)caIG*nbER%cyo@kF_AdwxZM@F(=^mE>pn= z$o3WKbi`Dd0O-XEhwHOy2j?2r*k>m~%9Zy&J7*^bl7r2lyU@%>Tnkz;{gaog#QvSs z4qL8ppDrK#EXJzM`_RC_-RMH2v29rk)U1MTw zS%1itZyh}-B}Kv!LtmdGxj?=G28Yz25S zvZIhqEI-&t4vxrQEc>zF%2yli)NhZoT*mVtDc2{>*=#=*U(%0Xfo_GWiyu;Bbut~< z|0U%YYubuyz&V)C0001kNklxC90`QxX~ z0000bbVXQnWMOn=I%9HWVRU5xGEFctGA%GSEip7yF)%taH99plD=;uRFfhY;&J6$n z03~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536*Gc`IjH7hVMIxsM@BzCcpAtwp| M07*qoM6N<$g3UTfpa1{> literal 1325 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 diff --git a/toxygen/smileys/default/D83DDCB5.png b/toxygen/smileys/default/D83DDCB5.png index d27fb53f6cb11c9b70524b8d909896bf79da633f..92f8cafd1afd0a46be98af353db19c18a03a2b22 100644 GIT binary patch delta 1613 zcmV-T2D15+4E79=8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0mx8HR7L;)|8Z_#kBfMVhjfjH zb%cO#pOt&m!k*N^pQ4t0gn@9Glz*C(e~5%}hl6m4g>#C8bAO43c8P^{i->xQhLN=ZXrT~%6HQCL<`Q&Uf5S3-_=P>^>}V^>06T~@xgowcx* zx3QPAu9d>Ej{D-T!Lg0Fv6!^4mcF>1b8TUXg>#^qgPoUxla70_oq77&qx##UvYvXE zk$shpe4m+vkAI7JbZ%jWfN#6Cn7Ff-tEG&;x}CtShWgo~`Prkut%bU`oXg0l#lWGD ziFK!;jm5vCdUt4keQnCdtH-mA_ST=muZYmjvChx0%FCxvHR=j>f^Uv$3GDu%NuTt*n@L@5_&?mUXhRq}SBK+1SOOnU1`+sk^qS$iua_ zv8A7#lz+FOe*M;${nnVZpM0O3leDj)#J{uu|Nq4>M@|3$03~!%PE!DMtEQ%;wZ+A? zq{_<0#l*zK#KgqJ#KgtL$#blxrlq&Y$hWNc?_HPx0004EOGiWihy@);00007bV*G` z2j&M33MC6HfOVw+00Mw|R9JLUVRs;Ka&Km7Y=31Sba`-PAb4$X0020Rl~rBR+b{@x z=M+7H3<#v-IFa?%nLhUTU2uL}$8EpHGm(rC3(HETpWlDbxA-+}BzKwKyx3x5H><8( zjIEkmw@n7~HgC7vWQM()hNZex9a407QGsu>PTG8RY^>s|ZdDB<0*YW0vrO5z$laT) z-hT`mZ3LJtT;kpCwz|u>*OL9?vTi#{bv>kbjYjqd&ojHq<~hT5o|q~>R<>S z&vjb}giQyrk=0J@u@^zG4z&6s=LFo|Z6dsYY#S)2A*T`rfL@FUIG$ZQ1Q*vDIy(_U zSJD3*Je24t4n8x&xnwYc`*%@0EM4I~Lq7OfHM-XKLxTW!jS%8BWPclN z(yp4y-Zvq#=4o2dT#=?CFvx-ty})By(NG9g7t9nRLw%t?*a&V7(~glpGPyv$0){}D z5V=VliJ}!xKre|J=v`xmAJ$t!mb4WOFFyiPB8MO)Bxylh{Xp(-2Ph+I7sxlt1Q(zn=Fhe>Fsfs^L$<;bbZsD-S%7YW&QXR$VRbr@j( zR2b7^U?3eZGBGo=u(GjpaB?v-Gcz$VaI31RscUFzX=!WdsOjqQF!1Ud7#bOyn3|be zSXx=z@GT}b zBqk+`Fo>q4rln_OW@TmP+Z9&; z001R)MObuXVRU6WV{&C-bY%cCFfuYNFgGnRG*mG#Ix{soH8d+QFgQ9eFl7$Kxc~qF zC3HntbYx+4WjbwdWNBu305UK#GA%GSEip7yF)%taH99pkD=;uRFfbHTeFc#rCkg-n LNkvXXu0mjfQOOY< literal 1555 zcmbVMeNfYO7%r%MsV6FW$~cXIshp4|p)E~p<=UiZodqmKE6Pb|QpiD?kVIP?H&j5y zQ>SBk;(-%)?$oK9o@^84nWRQlxHwc>b|mlHW(3=XpPV z@0+TOw7H`rq9ar))oANH(yok={wFL%c~5=vl2V4rvc)N9@^0BniHyob^LY$l<*0nd z&QNqo@dd`9QVnyn4yWw2Ex=to2T^_uN4tK+!}6ktmgTWXhN5kzxZ1BqO;*R*+fV z1NarGJbs021QkzTg}@0m+iPKuG?XYMWojQKs1XQObDTe}L9`^>nSb4QBU*Bl2#ng! zNc;-XrJRR5Hb7Q#_jW^mpyCalF0#s@P(>u~TEQ_M*-9EgK>zf0=_`xZ&BJ(-p1EJIZ{NiqC%XWRnel%M@DfN+4d+JzI^Ob|`qwan1%fqR= zb4IvLkIO3U(d+Ae=T0KWPB{Bmv9k=BHr$LG)AU>6{G>E9WD#S~4@+0=-9G&^ z(A*ZTyXh$0P#twUrXE`jXgXV0+ef90+0u^T^?OEGE)}=!x0=uYR58yT_2q93k()uX zv((VG-?Szp>_T7J@p}8_?LWS2*fU{cW&Ea@3#;jk=P!0dh2DoksEXmi_xt!`v41pl ztSnmMc%R9KHi=v?nBJgli+?-hX=Q>B*YO z`Q=b?ZBK1W+~sRK>ZO(?N1t5oDqJ4&Tz~iH*K|~kw?tAYySbz@IR3N diff --git a/toxygen/smileys/default/D83DDCB6.png b/toxygen/smileys/default/D83DDCB6.png index b4d6405a3c8df591c0ddafef679e40cb551688b8..d47427b50c5a09b3daeaf504ff090ced7bd544be 100644 GIT binary patch delta 1690 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 diff --git a/toxygen/smileys/default/D83DDCB8.png b/toxygen/smileys/default/D83DDCB8.png index 20240f82e594ee3beeef39d5d2e7b96352ab949e..10b4518b884e5452d5dc5d4284fdf92aa04595ba 100644 GIT binary patch delta 1849 zcmZ`(c{tnI8vaqlR@&OCbMMGlORE#cGDYvuOEj&0Yjw0uD%LhAL3R5DsVJ(p+RCNX zU?fT=wZ)PMiBL--YK>)x*lCq=TT?f^+w;sHbN)E*`+d*(zUO@J^PV%LG^3QE2_66d z;JC4}rx>Jy?40cYfbv9P4<#**q5kJQoB`mDJ^x591*E!x;fcnMH*NIN z>1ldT52=OK2M!O{iw~8(ZevoDMIttnS(Z<#6AGE-xtOM=XZ>%gs%tVjJBgE%tyx)- zATEH*qda*MAto&y-OcR2GJkTCTzp_`+%*-Iga1$dhZI&f6uoMAkebun z)yEwbkZGNq!I8P8)zyvn)z#T0C4}MOhOfi)@n;uTM9ZsGYJot|DprNfu9}%)u(`uy z)3dD&`ThO1Q^NM~MUY9OmAs_3F{l|ctelay^yT^X*3sIi8gOZe!{^r#iK!#4k2gmu zHuz=Z?HL;@tfA4yUnzNF0n5tpRR!UTy~W#Ozsye2rl#7+q~z4Ju$3hiwIp`2ubAJG zHpC?pi3vkP^*kQ6urQ&ouXcU8i&7dh-43{_Ng59Dx2H8nXiGwnoTT5)mG>N1;L zcx$>fp^o^F)YOrd7S`18luj>OU+<$*^D8UUIUF*#m;5Tzn?mmz|4leKC(6x@?&+y` z{km{&u47}PkHL6JYs`I4ZRr^rTif^`TwE#0iLI|Kq6m73YjA6r;mFU*gn&^esJ(f$2>5Z(1&EV>KU!$s`QmF+q`4ceDVa85~e z7au(9L;tjA{14C{GK(TQV0npo+2mtu;@l)@g>xEF+y&yHJ)B#yq5jG8s>c7{&S;2iE zERWL4E(%kzufh#-r>DWii+&CY-TITM20uHk297<=rUXQh>M!_DeO?1&R(fkM@@vs~ zWalgkSEF~vK8Vzs^oPFp-ku?$>}?O9yq(tlYbu;_b`4<{lr#Ng6FC6NNuIaNdm%jLtDTw=+?GX6Dmw-8=d|KY63ZLG9WPN z2X(?aFn>)y;a9|85`lN`D5}5fCRfFre;ncjzr#|C%~D&!YH4gJeLn^9hiqz_uN$ba z!Dj$cIJ@ZT)u}KjdA1isOUMVS9c%Y-BpMk>J?UyO#wSEOq7vVpT%~BZBQe3--z-Qp z(#P#@+fk~&E0%OIJYpMXqK&+ZT|4CG7qz(Rb%>VoCGMTu3sC3u?w0I==_X1gLDg}u z@{RZPX#;cAlKjr$&8sEeWjCV3j!BK0olRcIz`RC~mfb%3;W<_@o|STus-90@r14ZA zU*y>Y<(TU#GI%@=vuJ3DVc#{?6c4hdLN=n;ExhlZoAp&W# zdYY-`xA7rR0tnU94RLSWOY*zMfy~UyI_(n_xo)xWC^HL}bYU}Qa1pDk`xlS#0>n7cIm;B_GnDX)bOL+oW$Mvo6 zE)RFk%!OF>{;Ff0t%e58C%XRk6wNdJWyG!ltK&&bOv z%8Nw5%`3mBWU3xUC2xqraT7VdNE73G(v@h?VITh}Ti?~N#0v8%t-z?tHr|)IARH*R1lySop{aQ%y@Sx)OZE3y zJ>H~=k>JqkD-914EfUk$+0M@zZcdh041kQKUzFqjn#mhUu8`$K^)CLA3S829HDONWDGGQTsB P2LObFi~Xy!$b^3Z^Yoh* literal 1876 zcmbVNX;2eq7)}8Np(;nQfCp<(YgCf#h6J*jLXu4gAcsgXB8~~kMgk_gB#Q}=N{b*M zh#V?-QMFbDMM01mM2Cuswe>DwVH6Kk#0pA5kfPF!g6$8-AKlsA?|Z*}pXa@2v%*6B zC)&E%QYe&(0TLvfTFc|0t79G>AW`JBS*USMyo}`ASK1oZ+jhJ>UQ zPN*?0U}luZV2K2eN_zS}1PvY>{DD}jA59dQGKNu(GeA0%q0yM*dZ(=?!qI=-_^7r% zA_+$s;iw)<)G5e%s658NWbQs~$gD_u1BdF=WKra65KNJ%LA68x!lRO3=t{K`hCq;m z_y{;+K36Cr1%Z&s5ko>BHY8+1VsG{s$H%Z7CQFPky&wb$BV@Vg?u)P zs!6+}Sn!irSg1qg1g49?ur*^H5EhFOm_8Q60pU^(Fn5_+tHca? znz=mhqeW1idM&CH>o5)QF2At)Blfuv3*sYep^$};o`WEtE9P(oAXkjASu7@3L>ml!}=fiJHI){9(1+AQV zsxC6VybN+BWUk`CgRDU-?qL3ELN@7qTU)NQ-_$+m>|9nPTJq}t)jh+V6`CSO{p3>> zwL^*XJUr_1GsNdg2bz0%R-EJm9h&am&dJ~OCSQpG>`iF$?O@urN7x=X@qyhl2%do_ zf8FVN;IY8Ls>mZxH?<X3>CG=T4NE0)a<{~F zn~RHGgg%a*HmR@gZ_hmb@^qf;3-oW?=$s0i?HslaoSi*zYhA*~fPHRZrdzJ1e|B-g zn@xxJIu+0=k4Kl*o*akQMVso*xbB}TICLZGK(d+iM2)!8HQd;Tuvdu0UogSxoX0ylPA)|A{7Kw6vcJ`KlIgT)#dp3@}p(7&mn7@N#M5U5H z91?Dye>wBI-YqYy+kGhL$^5KW2@6VU!LA*M!eerVV%n|ah1`1@$?O)8&HFR-VXH8; z!OMQ&&cR3pEwgq(wbijlO{+j8Cx%m0>k?$stoTvENUS&Q>|fD%F5~y&JriGvXzL1g zPk3GpmxOqnnBwnB^RBOT1v;_|I&NBHWkY8oGj9BRa!R)7d|^X%ini;r;cok;yzU1z zzO-`Rh3;#lIi|FR`EQyA)`e|t>FH_VX}U9>|Ju5HxUKnYsG@T5`h|X7y6=r$DIHHk k>(gUxy@h>!MQ@#Clxm9M@yr*c14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>oV`A#hwid{->lMP;BgfBm^|%e{e7a z0Zsew?hZsiGa<%8Oa>YbK|m|u2xu3?I-rdZOM&*n5zqlx5ztkb2|DW1})EuNh_`P`n~ zrg>}ciUZqj`rN81)LHYWJ1>+s-a_P{QkvwZ!iiUB>)hkvUdukiHzp}>b*|pR#SRW8 zS;~(lq=w5plRSBwH>E#f;c+g#t(ogvEt;g~DXrYoS>SHts>ZlVA^wC~;7Y@`oYpD9 z=WRaRz3+DCa*pR=R<(0ai#{9Mx`}L9xn@6|ByrM4gv29C(z_0s@9XK>Zxel7C2OCQ?9rex^Zrt!t!EDj3PTT8B z9M}4S>W|E6xV_t_h4aL=z7nY{87~6^Li~8=6<)g?)NKEVV^Dd zF2+dSOmR!nFaEf9Lchy{taa`_+!tf_Gty+0+56dSd||xek4$fwom#RvtL)o8j(Mi# z!6m}C0)0Yf8SPu9v8U@=?BX>SH%;*Fk*b=Y=_(V=Q(N2ngzYDv1cUIy*21dr2T7YB zab9tlUwo{?OHuoyTgfEdDc6 zPKAU*bt2y?f{4bBMcQE6+dX|5sTd9%kz5l9TxHs5zb>y1Vcmh-AVow*x5Q)pN zC$0(|au9I2sMf;5`3?-8{Qut)yNxMyPWw7*=0C z^Y;1up8p-Hsd^WB2 zVk?LMG1}dE1(YF(twpY@H|xWdoTMoZ(b91wlo&)KNLhIzCQ9wV#F{~?ta|V}7bb8_ zj(YI@?yxf)<3XET(W8Ufo~k;jr(I&Bc=;ZzEWtqn1(+h1P&!nDOL*`lUJmZ9V*2fm+S%gtek|Lb&W{OD{=Vmyn1Y5aqXibl{ za1kN6Vhc(hyv;OY96`k6aYvkXXnHF_vMg(HP*gG0C^ouPQ%n@AhCRa|07KH{m?>*2 zW-*G*T9@g;p{L6sD6w#Ol~^@a5(QI6B*Yj&I-G=}SaB_B8)gLj>&BYeMqPId5D{Q# zUAhG8(PGblVeTGp$WnyfaMijDiz0Rin$)EL)eH$99DZ>`~jE(-EoDt9&!fKBHG)v|PcA&Lg zZG9-L4i8Y_#OQD|dUjQRh#*Z7Z>tQEj)a%+eQ`Sa{|6@!)BgYf diff --git a/toxygen/smileys/default/D83DDCBA.png b/toxygen/smileys/default/D83DDCBA.png index 4a9e28013eaf08fbd6be276e4726d3467962b3e5..a3ba77d7eb2e53387a40df157df860b5b3bf027d 100644 GIT binary patch literal 1588 zcmZ`(c{J2p9KS>y%Y=j}G?gt{CXprCo{6!gF)5U0VrJ~?Fb!c0VMJsogOGhG8JahP z5z3M+EmO8s9K~oo^zuk~KXp#$y+7VP-~0XE<@3GY@8^E+xeOO)TUn{CQUCy1w4DtG zvU2NPas#Aw_HJ^8j2Owv$qE2=rt~Ua9GWA%?J!ONL~8*+O9Eg8TB6MX5Qzj}9tVJF z9stUrg)PU;01)wYam1q5*47-bjN!pfR%XJ3u4XUylcv^GV`F6U<*uak`{_BoRkxn< zJ6?=W2qD;S2|nKnjelucYFkno=N_Nk-_r`6^BeOMz>lI2oFf_4@4W?!=QcmD+**b+oLH{jKCXif6?tr2|$rP0KEiElwz1|NU5|_le-SpT$ zlD)9_Wnl1SAf;twe0o;+F)W79;STinJ-?cNhZKA*lv+=rwY#5Vo$$&&>s#s@(L|1E zf@nfXR+Lt?@z^{LuYuiGU3<6aT3R8ijK$1lWu?|tb%Y>JkCxp(JFrUW+;i8zndSx(K>15)2#Ei}eE!a3=S zD-Ec>f9S22FDV2L@wvrx_8*X!$T{ IlNl%`DM%{l(Nn3#js`O zjWGA9UOm3y(i}a#EssycAG^>H*4ekGinOQyf$?|^RYU-YrfYLk;bhxrkV8>j?^ji zthHj!+O#BQF^snY~!m#ju+?e}HvPpG>2K)1U*+FYSrkTpAUR8&$el{1cpnektnXpX_`y;k=tKGKFofB2OA<5uO^b~Cdc*ja(6 zy$I{GK?3bT6C>QCyHkF-)!Sk&~rq1gutBoOPjLa zkZaXmwrhTosAs38@T@C~p{F^4wz0yhJoU!mW?s)sznwBP^rnfL$3;jxI5_C>Xf#f< ztV@T|K7?;jFe$`Oh|}s46SEY-2SnIvJ2;RI$`gsiELt@u?^0bsYYK~(RIX@eIX?b$ zV4`A?oNTei7BJ*-{}+xQXC4 zvDreQvSJQgWh*>UTxE1@EJ1a%n;Z@RuV6iD>z3>yh^8hPb&BkRClgG)!wHZ9`bd5K zy-1_I2Mn+WkfsL>O$`n7kVsP`k^t*&{*OaYh>stU`u`4!uCePsI($8XYY34{!G{xo hl~2ey0s)Wq9b2s6*+Zm=I>n>XL#Rg`t%DXT&?#m`@atb{V&)`7{?K%`$Gb zSTEE<dw^DWM5NNxIQgVl_$3+~g!M(Jf^OYy^XYZd;MvDRs-ZL%dRU?>&aN;E)Pq zm2qD>m7~u9l@v{YVu6Tn62dTuq5?QhjG}NXhzMZ>60$eShvTGTREord!xxveMw>0t zEKEIYit7Muox_P@QwF(l2Lua*kj>_eYe?J4WD)+h8JGPcFq38}0>dblpg`4f ziBiQ13Z)QLBgzCZq7)+Pc(IS;byye^qiV4N!w^ga!zx69C=s4Sq;j>G!c89UbH)5qqn!p)~Hd0iPuLCj)DTZ}tctj;ps!#OrKxiQuLBQ~!u%yJ2G@ng!9H{%pT~Y(ZyiT{<$W=VZ;m-Q^-mTot*`(<|6DWp_mf+3LA25;V8pYaBm0 zmw1}kcWcCv=~KfTNBTbJtQu?i@o8{y$eu`$u!OF!yjGEW=G4jXUms5zoPMRzGh==f zq;R&)daG#e#24FR*Y;o`ZJX}o1SoXts`pJuJ;dKy%DZ=b;Np!HnSF*mxds<^R9Aau zUC>91`$r!5x@v^ucEQ5-#hjacM11Pj_@2C4AaeVOgG98nD}>k7RQb3Sh&rImJ06e| zxcy|q{axv@7LT>KX~MXbwy>1AiTmd@0?++SntHb*Hz{bjZrkJf*(VI4p2PQN0i{?+ zuwd`Z&(1}}{oXQHw~4=KZ8w}bT2oPSBYJ;icI!fU%~yNgZ9zBI+zO4jz`5nmdFFqq zvhEMSv!uq-e)`&9C6|{!dV2I&{(|3j{EdqJ=AF7zlXh`hHyx`+cdV`1shy->pZBNw z`&{+Z)aK^Sjz!r$Wo0|Y8+9XVpPaRKM+EdfI9f&``qAUt*IY$}^y>PQHS%NS8uwV< z{!>BFK+K%d=e0%Wl2?@wf|*GL=MMz4QltlYzfQ*O64&Ps0j&a`=R@GsfdC`22TQAIPeHnyRyoDjXGl zQHo`M{LPmBApRpfeC81&Jx#V^FIo32a`$}uA$TuvD3FXe_`#UB+n|WX+efQ zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0JKm{R7L;)|B;)b$kW=;)!3u0 zw9D4q#mUUZ%FM{p*Ui$_z{Sg>t+c<#&z#)U}MIy^oSRe>ctN-Hca%hufS_W9D<-{a}+y2H!w z@9)^(L;#2d9Y_EG z00(qQO+^Rh2M!7)22wEz7ytkQfqGO}bW&k=AaHVTW@&6?Aar?fWgvKMZ~y=}jg?hF zlAJIMy!#b-f{|p&ehvfpmZ}`{{B)ZcmSxyvGgJ*A%YSM~-4^Nh&tK^Wf7NQzMSIl9 zKDqkCOPFLAFCN}}(^X{akH@2nz~0Utiti~MJSTttaiaG8GsESP@ z+I5qxOH`esIyU-BFrOsz-51|Nxt554Y|K0J(0N}t+(#xKVPJc3haEd4muhRtNtj%Z zVbUU-s(-A=C+nm@Rn!aHq^5<>ur094SQqKI@Bf};9d7e*oZ%9ZFPA*a6J!COB4*~7 z(j<*VmRG}YWt~w@kMWZ&;3eD0IDkB9<8@>r_+NJbWE2|UhaHKEvVg$#&siV^Pz55y z+vrq7hv4yA_bH)aJDG$p*C*}kHjikvbFKzngV!1cx2 zDY%9e>g>{xbQk^W;8bFuIQVpi3mx@{Yl0Lw8uzwe|!=^jjSIB37W+Q8DKQsui z7(-|j%9b=)GaJg@HzBf((?m5vj_PJwO>lPu@upVx6%(UmVOKs?Xi(BH36B_bea++s z`F|D|3QaayAu8n$x1rjBO7w>327Dn^uvBEB<0KgOoq^LprXZD)bT+QMA-50Y6L3BQ zr~g9+DAip+j`83W7TbSR1$kfoNLH?HCkN;?h`K+>tPWt=1j z9$xTFN#>K1Xa7qJ(x)D9%77eHPx?_7gn!IF9JE6Yih@zPlI@1V)z~MnhPgI+a7O+v z?I*pZ?+Wgsx92RE{u(6de$rgs_NjPS-#!JpB`)23kP6uu<;eamNq+#yl8T5$lqw7W z004nWL_t&-(_PO;62dSLMA1grU`);#lXDg}nEM}$8A(GGyiHgAT?7BJ1rZjBhksJp zsZ?vEjz-gIwOI$05UK#GA%GSEip7yF)%taH9k5uF)J`IIxsLdHtHS#001R)MObuXVRU6WZEs|0 xW_bWIFfuYNFgGnRG*mG#Ix{soH8CqNFgh?WMr%@{ks&7v002ovPDHLkV1k6VFuDK$ literal 1332 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>#D)oXxPua$FAYGO%#QAmD%4lD%(WaO9R7iZ)b zC^!e3DQJXe=B4D97i)r|2jW|o)S}F?)D*X({9FZa_*!LRvERVZ+{M7r(8STy#R%+g z3uhAxGh+iwM`tG&7Z)=nsNNKE!b~6N7=2LUKuRnyAz%swG2uxc$bn~`)I4C0DFSAV zpYm)83=E7Zo-U3d6}OfIpZ8{Rl&Iaz=bDh#DX!_IbaKUuW&b@zN?zZQzr50UyTXf0 zhPzzdKVIQC*}CDztu;C6<#$8Rlx+_TlDHkq&#SX}&->c^do}mXkCev#Nx!(-X=UDR zzZ{W`7dI$ezt{FqE7eW=o4|bzpLZ4qBDoEOmG(tic?Mn0c%=M>h3kng=hOB3cO<@8 z@O~Go>ZiA}*OYMhMc-q8F^lWhs#z;s_OjmEvrqi3z=PoQ-i_-TT0igTZ|-BU_!pa$ zCHtjy<{u+DCOuA{ca{NH*EO(4#PRJYWf2cgj&XSAI%lVi0F$9=yJ4)HrHj>nk9`I2 zmx(>I;q*W6l|O&JZ^g0G{YDuVI4=iJytw#|toyntJW5yXX2!4Z`qiKL|AR-zlIfS7 zxVy?-Fa5uN`>vqOmoT;2vJGXIZ$x?Oay30H+@$O4($}ubBl9Frd;j0<>laUcXxb^z q_@ZFmHG_Nyr`I#DE@k|ue1JhybIPSR&RIJ_<(Q|dpUXO@geCw^f7Y)6 diff --git a/toxygen/smileys/default/D83DDCBC.png b/toxygen/smileys/default/D83DDCBC.png index 4f7011c0caad8e7afa432b7e1d03d4e50cb2fee7..ec6ce624304fc99064cac33e31e74a9eaa8793f4 100644 GIT binary patch delta 1222 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 diff --git a/toxygen/smileys/default/D83DDCBE.png b/toxygen/smileys/default/D83DDCBE.png index de1a1c04588417f948a3fd7a8e02f7d6ba249ddb..6137dffc2dcbac041e1747dfc5fa1672e2e62f8e 100644 GIT binary patch delta 1350 zcmV-M1-bgP3-Jn&8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0QOK!R7L;)|Fz}$XRX^;oY7pW z+E<*>Pn^D+vVx)K$+4{uiy@Y##OW9(AC?&$I##7=RKFv5roGEfyKzo(zLt8 z!^qCn+1@;q&j5YHOQ_t?)7Zep%*D#j)!5uQlFa~m!9}6ig2U-!vfu!BzdoAO0CT)J zlh5}+I{g??RQK(*^)Bs(t0AaHK|Np*qV0Zuk00(qZPE!B?^+EMQ^-7PB0ssI2 z0b)x>L;#2d9Y_EG00(qQO+^Rh2M!7)3BryPi2wiset&vYSaechcOYfRivxEo);&NyiS+mP zANm!)rj2Bk;mwOpCU&#x%Ej2Kxy`moXWqu`b{ovF&!%CiE>*h}?Os&i+iWInzBm?E z@kO_)hJPmlieM454B5EI>P;4JhK1Gx%qA}JZmVrBF-L)bY}7{NAV<6*l^SE%R#-XbnFpTx-H3a-E$O(x#krg= zzL&?y0zQ>h)lQ``hi8a&h!081aU( zfDrQ^vp@=H6&k@dCsQ*Rg2!XtCIVr@L2P8T6MO7M5Ud@oe#mI*NnOh;S}h51SCAnDK*`tHQo7YKNsO+(*be zKYxox*XDiGAi$~-LcE4-qfI)irn2`%h^)DrLQRmPn5r~oJWbJf6|MV>vW8?~ml!o@ zkSR?ZTMW8BXL5mj3G6Zvn!tz|8C1AIwP6M-P!xCkHSh2OXynuY9lN_7fhmzgkP?!# zAg;b4&oATyupWWw|BwM1>aJ*x#>S~p{C}`jI-QP$f$XO2S?Jqr$4=pqJyO;zStF&CC1qd!|F7{5Gw%%7 zw{w3u0RLj~1jDdj$zUh|sWgL_;kYbfE?)>opjaZI%=2Z$O0^_Jp(c_b)$0;sqbZ6l zXbU8CWVwsj>kC2~1`0VHjz(jaoqtRfWdJjEPAyb*2HNt62D)LaD8tafw5&B{Sti(y zv)S%Gwj0N>Veh&J%60eP`Nz}w@^L;LeGjf+-zb~R`{Vhdo{#$*2yiR3L~j#60000b zbVXQnWMOn=I%9HWVRU5xGB7eSEigANF*H;$Fgi0eIyEvYFfckWFb3Z6gE0UA03~!q zSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536*Gc`IjGAl4JIxsMIgGfJ-Atwp|07*qo IM6N<$g5c|UuK)l5 literal 1460 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 literal 1788 zcmbVNX;2eq7*0I^nE|9|AyRP-o=k2+E{K@Z1d;$DDn*Mf$wFdCHf9$HP@&~2+TuY$ zL{X%2SX2zG;rOFFyZe3bx9{`3_w43yjjd1+ z4mUMUuZ}{a6mqTxHj+WR4%uonV`v;MDA;NSH3kR;Vxf4wiAU(KYa{@AEswA|KtWZQ zg^*4!O|?LgsYM3Bk*NAS)LmFhVE@Sd9rLglpvy-tcmPdZoF3;QKy(8 ziV8yDM2iNiN1X2@7|Y#<4cQejZ@6I=JysMjK?G|OjgSeIiFgF;3t6kza_KZ0Q^Xc9 zC47!hj0plEl_{YM*#UGRl`dfgOme&r%M$nt*i?Z)L>FU~5Yq*037d_P1W1??nuJfC z#L7$v3Ys*~q+LB`cLK}*AeJk%Kp+ZR)G(Yd*#Y4?7=;lXYzBmpOkl}sy-5ovBTMb& zc^j<=vgnf_t;7Nwfj9Z(>fd9Z#$s_KY=%(8z&ya>=8NfU5ns$=^8Eu?Viun;fz|$> zoKY}mDE8*~PqR!OVI64SzH5CqWSTfyN!nP z=%0Q?+j2@qM2_4M>9tf-ze%#vvyq+ny!~bB1DCV4W$A55NPYP2;Pj0beHNbv%fl3C zd1ll0)^-!;{%6Jql9rxVk&EL-Nttj)edQzLOI!22hD7ubd#kF^4V}W?m+9H^`{kYM zZ$h)=9?I%0N4N3%yOy-FeamRxI|`dzj&-`$545+Bw8i{nv-N&=BFJS&#Iz2_>{}5z z%de{Yj^&2)o{ufg4f#Yxba{O4>dUV3rqKf)+9I)3K2IHgbk`J;I@}Y#>(!K`qAmLi zmq*OE(y%&}U%7n7@^IpT)L~ws1&>xFRCi|=q0jiU zyI8f(x1H}997?5G-jX5pjD}OI;B<}(UtX0^IJl?dpwDiHZ5qanxm}|!x;=@*MP&K8 z;$ZdpOZl}?2V!@Wj`t0BT=g_`u4}kacp+~t(mtTw_+x&R?V=)cb5fpr&BE6`>!{U9 zqc-GtXqIx8j6Qy^Y{k$%;t}cbE?j9r&XN4SODVYOw!L1X&5CQqe?2iUcHgDRS^Xp5 zd*l%N@IS1*b+TGO>`npCwGmRuu= v2@4UsyJcNxKdk^`pY?V7hu*Y}d#=aDW@rDMblT^p{eL7AD@BI|>oflW=fk~P diff --git a/toxygen/smileys/default/D83DDCC0.png b/toxygen/smileys/default/D83DDCC0.png index da3cd5d7c5ac32f01c95e92465645d556f9f51d1..5a76a4c4abb42ac04f95cd29fd5dc7705676afc5 100644 GIT binary patch delta 1853 zcmZ{jd0f(Y8pnT`niW!3ZaR&dHcm5{PUxC5vT4~wlWo~#*4UJ@9ctOAsgXw)I_s=Q zd5#C+kyxf6DhPsdsHlhs3YvlkDw2@erWlH%;CE>M*#Gvg=k@t~p3n2w`-z87!85Hf z8zBh#+G*Pb!!W+(b=C`lC>dLI*G&xC_U0+yvk>%PKLo`mLeQ$gil2s{`v?e{y#_%@ zEClV2C}f^L0zpPsemvuc0szEE{25%puuZES!Y{HHW zlBfD<-Rzg`^cQ$49z){4>~AHu(TelQpIlsN4X$DQ-EdPIt+c3&*uko#)RaovD?6xv zNSbj1MlPR$?VuNsDhceWf|61~Lsjvxkc;PZ;$OaIQ}7j4FPK#YP1F)<9j=m$Wz`h+ za!LJkJgYLFR-J2zSk;AG>WemN0hgT1A!V~Ll zskEbDt`Cg2fj+`Q4^}N9tj%_VrMF8nB8FTts2dqv=c*UDK-L2Kt3C?y)Z#MzOeN@jq8Ml<>w0Ezo%&n>2H+>>8nvCRd>W^b z&EYURxKyw(02YTdswuuuNZ}8a@n^AoJ(10C?GgxwCwN^wojfL3odrvB?fl4CccqwL z-pj9U<#T8a^<9Ib-GieI%!YN9cy+pESx(nX(BFw(O^aU9=+yQ;35`yZiwF}!!iN#Y zqO1|KG{sw<>1}IaF`8PN1^rd*wtPM);V+W-N_w04Ka<^SDye=+0aibNMH#Q9zJ^Ms zP^)=NicB{^ou4coUd|VQLau6kc?PUZf@L|-C|2J00eOcQv^OqG2vhH7Mw-FL0sZ^- zkHZyuF%d``HS;ZEJ&z@8e2W=mfrU3fJ+St!Nh^L0hU&pYlUPhvN*Xl^o<`1`6PM5Q z;-_263~+7VZh6%Jp$FR6$H1#Yx@2WVobF(nqt|0Mo3hxZxj6@Qx`P=R5{Xu;RZ9jk zG97g~$K2dc*?*S~W)7_`4rDsc5=)8v|a=LNW1UvXpHVYw|8b~-XVE+Ey^!S zU^i;3p!7Ciia#;84x{Cl^klSGW(k{054GL~lDxMyf1!x=>0(AzVAh`q|xK2{A29h*tirA z@xC1-^Rg%EIB2fM#p0LD&|l5Uo6C1?u;o}?Cwma15#Ln1$3~wv3%CrVu^2?3#pmb; z*n8Fs zLi!D&Z;msrRONtsms)EYWS5V%jK~a0{_bZO+w);m!lTVsjX9SRHnzMsYIHcG9RBM@ zj%=H4vpxG@NAesu`3ocF17pnVgwGRmN?xJlANNE(w%Z%8FgpX1V4`a=CnH@y`|1bP zm|F-Mxnl>M;F%LB@GXR2jxe?VCQj^hsW0*Bi!|fyNPD*o9~arAiMk{ZiHNH^Znk}9U1;iL4Bz-v<$n^M-f`4vE#dCsg_fcfO!U$2hNJKu zOwW+sJ8Hj2rzbp3YA-kjJj#snyOr3gzH%Hul?kb-YvYf)48X#?Pk8xRy}5Y}GTI;f zmrHKZa351MnOsS!qsBy_<%+kP7EFwb0KT0#kIp954)$NG{p78T(l;?qj+DIl?5qMEORf` z!lDKb*WF(-fA%cxY>!h_*hkow4>mS7MFG#A1&n!kcyLl-ryuKg?)SkR3F?|6ym>9e z5(FhKziJ8_)XGEt^|u$Y%ewO_T`CAQ{?D=Xcb0Hf0uYQl#ZpxMjgapo;F47Sb2Giq@t@{5~0D==q!1Wt}sq_AV5P%-MK z+p^VH9}M67B^u?A4!n*ILf(uDG7QKC;o{LxSV}f8$(mXl<6=S6d*+8|8DN`uMfq{si0z{AvII literal 1792 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 PD%L$+{an^LB{Ts524Ghk diff --git a/toxygen/smileys/default/D83DDCC2.png b/toxygen/smileys/default/D83DDCC2.png index 4b727ddaf991cecf5f8c6a1e91035d251a9a3078..aae823bde6e6dbd19b1078e86d24d80f314c37b4 100644 GIT binary patch literal 1874 zcma)7Yc!kL8vdyJrH0XJr=3n_MyMJiM9a|D<+!EVx~GPz3PKHvG{mi{XqD1&Ep;hE z2!f=EBoY#GBN9m~38I~;s$PzBrdl0lhMAhL>#XzR{5WgPUhjUN^}KsM@7`0`zJ`Mny9zmzWe0Fwr28Y?gY5BvsMWy<&P_Ftr zA&@B-m!2!tPuM&$nZY4ZS!6b!&Jj?UT(H{!jonWjAyOD%9-y&6+Dl=Qs3Twy`pMvt z{xj~h6T#I!3bUh!lwEM`zvaJ+Lb-BWEMxJfsiXW}un(2RosuypCI=ZD=D3J9I?+TR zwGaop$n*w$Z_PbId1E_%kb0LuYV7Q*YwxLS!r!QC$*-tSFRDQ26h~#`Rk!t&HMA90 zHD;AoB^TU`&b)g5asjTirl!4@NE@SZr-#NSiF7_?Y-)(fNh_*|OwWtUF1c3S(%N^Q zIw~3yJs2LB46;Ps!xQb~vDN`r^8jmr$tO@4K^L*#U%4q&E^@@PBZ3(!SKQ0sche@i zsN4?9SlbXAf1lIZJA{Z$>!))jrWGSRDSbjh9-SnyguM(Nfi~X96x4PQ&!RCh)uVCo zEO+W5TOecbq*RW$?9RQ*#kXa1j|5UBU#j3s6gSf~>XlN`@Tg=~EtJnq%IAbLs&VlH=J?bQgWE%9pfLGk0;%}n zfuH=YjNK`gxo>WXQ0lcx3*pRay{0IQ%9!d3_Rg-_@46Wej z-=}m_v!L^(K@gq7<&GH0KMANWc4=y;U$c_GHv8E0BNHQiw5d6=7zhx(zdFiKK;GYH%bEo=;5$@a_|0loo zBWnj2JAF6Bs?VZ%mh9}eZ`97IUqoF!e(1c`rn>TuStKcmzE^EdQN4^mdS{%6rz{|) z8Gc^jUzt4|Y>q$QxoW1OJUB3sUn9j`xME1KH`P!z;^sOeuU&Y4&^0IW2`2CMo|=GR zB*tv-d|V@Ks{4rXMtrL|vAh7)tfal!Pv3*wUr>E}`~4bXQ7_7Osi>+de`G~{uiZdk z?f~b~tdd;R(Z>AxOi#BHqCmPwT}tONu-*k9>NK0U`bZ-v_+re-c*`S;nwo!d&s{Tv z9WnCCKXiH~M$goq8>nBh0Z>TaTW=mWG)pn=d{4{)F0DC0M+;NZF81KNr8*w=ul*OI zL*1bBWUtoqdI+3)r={hTkyB}-4BOH9S;m>F<*C5v*Pm?TLfukW{40d~b?UyJV{6Si z`p7tgN-s6$^s#1R^xCZ|wdfMQ3F4OtAFyJwQE$4DSwEBo-zVF)9dSCG)L0T_#Nr?^ z#re7wDyW9(cIJ0F-=!d5wGh3BL^vhhyy0_XSYu+{rHv8s_yp%e*j?}D_KD7 zK0dm1w?=9WwSRA6Fnf0w?d3t|$R=3IvluboVN-vt9UTPw(yU~`rQayjHler}zHTvD zFc-DexAb*x-o59u7jzLU_oPA8c5ils`$=sUly&K(|8>RwpQqm2A-KTmqw@ARcTLdk z8s-RZ&#--pxEOTY%cYgrn57p_pFc|kG~M!a{j3u;JU_QM=Zy39_0=&6guKDL+x+G2 z#=FGW1Py&HEgkLBk{j3kybW&Mbj;TB@tG8egw~c;(4MaDGKa3j_yqLJl^4$~%=hkT zs4Iup)t?JPhR>TT=Ty#4#}F__CkBlk&cG?LS((2&ZJcs+{ADBXVzY})+IroJ>?dAt zNQIH2Ti*Ch@@7lfWoum5OtbyW6glQ8UU;kFtJaZ}<#iid*~V8R7$1tXrX3P2y=9_gE`wd zIE6XDT;WGukHTzWFjp9?+Vg4O{|Hjk;*%0`{=Z=9;J150!Tt|Bgr+56aVQKL@QhE3 fMMDs&s6=!K8Wo?DAx48A9RMJF&Uz1e#T5MoN9BeJ literal 1645 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 IM6N<$g4b+vwEzGB literal 1331 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ}+o7WNhhZV(RMZq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztYL6AU=0HUW3s1?rMJt_$; zO3BWvlhPKiyv|iFI%5vgsq*4I0>9!WC~weZmQX3qyu0bk9wsl&j+_^vXBM5%_PplJ zbm*zTp#`t2E=c!G5;OJrvutaQtbD$?uEI^_fH)nF#$5{&(;nydvVGdhR9sp;zsli* zPwa8l>->$K)BeS91>bk%Z!(xvdiRP$LSH{$VPCa^$cOn}tbYDht#Vs(t@GbK6EjN| zJ8t|X;asuBmxaX_qFz~9-+L~s_2IC9S%@gx=d2&XfQE diff --git a/toxygen/smileys/default/D83DDCC4.png b/toxygen/smileys/default/D83DDCC4.png index 33665a175d1cb86a2020902b0e7f24a56fbb3b3e..ef8e394982205a50e91990e7218c578ec2d37042 100644 GIT binary patch delta 1246 zcmbQk)x|YIvYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_CzOP{rmOz-{-I2U%meVl&$~!@Bi~R?>~P2_Wa!^pgN!tFW$Zfs{Z%y-|?$= z51zmNn3)t9OSl-hBGz z!`l* z!(QU)>&pI&osUbEUDeF8kAZ=yx+)~1Bq*_5p`a)~Ei)%op`@}PRiPrcfPulHcWS8P zq&o&2dp>vBII^*PDz;I6J#CfD=RYPH^=a2$e^iscoqCev;)MpE{q_GA$rqaDs+3FK z+jAo+$$a&?n4Yz>ZfET-c~*NTZ~H->pIK6qUIlISzWC0Sb=TRjOV4H}CyU-MS)V15 z7{D@nM(T-kuJ7)pd``QOVS6Cwq0s%Z?RQnLX$U;%y~SalGMiif>z5~oc~Z9DSh!v` zChKxsJ$Ipnxs7X3rT9a>`Ys)&5XU_ebQc9B^WBsUI#>Fvv2b7Sj^($ebxDWJIsNjP zdj*gFg5b!|w<1!(dTteGMaw7hKIA#yUEVJC{^dsI%891$rmOdq&u92>;^@Tr2}h(4 zF*&WN|Hh=J5bD8ldzR1I1P-b5rO_NMyzCnvURC)vqq|-q!aC^hvtp)q@|j9C4LPql zmLFU$#MltmV{l`Bq_u?jb*a)arIvMT>T_+hE(*$&xXma&S#hH!pexh9Vkg&|`;NX5 zyAIXPs`;=vGbMMfFEevFuka(&Q)ZWztj-Gic8BHkth*vtTZ%-2r!MQcDAE~z(JFk3 zsh3z$XQVT)(bA6k#YY3Kr~B(o&~H?=iAucqp^Vd%?bW0ejeMMLCIyckH^nUMtYh0E z*Qhyhv4X1CrKTOf+V<2b$T!>;obiueazWr-(UTW5=13jn-|fBp^2C4wkKT|;bEaCz zHZ-a|4NJe5BEak7aoP7org)=+1lxs;?<3WkJ+@t1 zH6^I)|NqT%(m&06T(_r(w_eJ(K1{k-eSuH(TdTC<%!60`!jA=cXdk>9C%^Gt#s<-* z_iP-s`3D|WSjCsLWX5);D&@E<_w^RowpQpJ;?Gn0AedO99@x<_ncXwVWtOeyG^J0E zT)Sif!}5cscAU3Aw$Ia4*0)Ifl>*RFswJ)wB`Jv|saDBFsX&Us$iT=**VDjU*U%)y z(7?*r)XLOQ+rYrez`)$>l^lwW-29Zxv`UB$Y#Q9MgBDFxROez~@O1TaS?83{1OQj{ BUb+AP literal 1308 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ4|I$^C~+Vq7MKt)1%jCHqz~l4Gf!$BFvk=D zvqsmdI8z1&MjuZX$B>F!b0(ejKI|ZHG=AGk`K4Qew3s6jw?01mgY{HTUa(VYxhAB_B75jr}OXcQAtR+poZZO4`cR=ufIxmY+@HY>D&U0#Gv&%WrWeu$ zdsf>z%u4Gxr#DAU!1`;J=v24jYmCxOie(di26;Vv^5TPfX=8~z+lMd%pB-<$1%|NC z)m=WHafc{RvVXo9)0)0#jK{1Lny(0Sb zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0J>02R7L;)|Kw6q>s(yvSy|yv zPU%-x^JHV}TUzK*Pw`z_>QqwXNk;8cQR`7p=1fZAK|ktCM}OEfGTu2i+~@88|Nq(K z>;C@!)#B*<{r&s={NLc>iH4EV-{$%H`}XFn=@gpKO$@Xgua@bvcV@$_V2YNe;Gt*^Ao*52&#^XKmIR8m@8TVmhl>BiLC=h4EGOvBOH>woa`VYZ%00Mk^R9JLUVRs;Ka&Km7Y-J#Hd2nSQcx`Y1062}6Ra=(Z zFbMr;6N-pB2B8_bZerXi^=Rl5}JUQ}S)Y$a{JIW|`DP4}vXCmf1k6SEB2 zxyap{Y~BnTtp}JbT;kpCw!4Im0s+yejfg|Gsd$-ZBp+@dyTuND?ch?I_F9M|xZx?1 z%DV`6#D7y{5ugh4g=`U(8f(bzkaFso2cGk8#68WnG~8g~Tuv81(qlvco64$cr_`8I z8R<1wTtzmdGdzF1JFIxmh#km_tZ}^}Vch!!KtwK%{zOH*AuS+;{&N%v0j)wK*w(Dn ziVDHvskentu;D0dL^TV0>_rql_+5xNEo& zuR+^rldh`C?70b^HK!@q1UibTN>j$u6pdHWIyK4~l!aVk)xbff6mg6gczurK0{Ien z1%I=H?1_aR%owXBQ3JgJ#xdoH3dVR3BMBMEEph~=L=HkqP|~7s^$mIbKt2HT5t#lD z8KA-LisonxPL1S;-KEp%NGLd@MjA=3N}LXW$B=TXvu~lr5c2vOB!JMNur(*ifQJ{X zgOcUIg1<5!XZAcC_`JZ)|ZGV5ZKy!tlK?^~^z#`n1ticiayKO)4jlN5` z6W<lT4P7=__oM0Ao;>w}KSYJboN zLtr={vN{aYvTe&GvId+{ivxGu;$kvwnP4_=nZffJV~cppnGBPbNH!U$VR536*Gc`IjH7hVMIxsM@BzCcp QAtwp|07*qoM6N<$f+Vt5=>Px# literal 1383 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL} zBMV~_V{;Q1W0+pgyyB9?yyR4vy_rCJp?Zz*>a}t%N=+=uFAB-e&w-_YfQIHi?JgIrW98(0$ z8bb9m{xL8xPWN4j^K9?_C$$QhtZT|dT@w2pNLYpIaF5lF# zNG{kdduHC8to)^u9<>>+dsn(!q0Mo^$t4fJR;|*Q&3?z=*wxAh%`N-b-$^Rn*%6s+ z@^P)2MDX;evoX=U%QszqFh`k#>5IR?g4b1-5`6Zm*L50PXTE$jMIo^Gg|We(qZzST zp63f)9wiC=C|b1q`}=|*58cEb=O@Cx3R_!>*z|bXe0?^zE2< zmN{l!WzkY0SAAtND<)RysbbBn7}?|7lHa|496TparHXC(imUHtJv?-WC+5_@ tuPu_34*kvczSDT) zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0H{z*R7LLf`Tzg_e0hOl^SU1;%LTYpJINyWv@O+-psS7Gf_ zQGrHDQA$vPMoCptTFruqU@9jr+vV#?MouImDA?oaA08uk zK8O+k000DZQh!cU002vXO{Q~n00001VoOIv0sjIm-T(jq2XskIMF-{w4hkg%ygz6( z0007idQ@0+Qek%>aB^>EX>4U6ba`-PAb4$X0020Rl~qxe+b{_H=M+7H3<%`oIFa?& z_xjo6cR)$j>v-F3oM+1jF))l|`u+14{fJ-FMsk*h}?Os&i+iWFmzBv|F@lE%th9?4wU=gzn*}2Hwn{3_;3#|v3EnMQ= z?zX$c90dZhQ5%tmY;)q}o|$}vf$bJ6^tFOZZQ5%gj^u`?NhuX_MSv=(7q&%I zYK&pK!+**-&phy)cO&lUwx#0+7w2-i_)#7s3;0x4RXdf&l#@|jbH!C;Lpj6q$GgLe z_srOVyvQ2YD-*`OPXJ`(;^;tgd1A?814ffUdxG=gnSrdBWnkLSEC1j2@c*vM)U zd+bFJtR1a>%Q*q}k2VorK(=+1(~wh%0zfaG2!Gh0T{{F9*BUxI5kgndzYZQsbQA}l z5#e029yTFJG2;g>SB3q%s0m9~xX+Leem0G+t#i{Lz+EGRcn#S`n{-u8WzR*3tht*) zO^~CQsx)OhP0@H2t#d|ML$a_-j2bk^lqQZX23?;sxj?=IZcsMRBBp^wC>9}WX0rr3 z@_&Gc6MqGi!WQopdEyPCIsj85hae>+X+d0lLta0S55UV2nEnqLprP)H=4fo38pRL0 zOQ+M3P;f|%bdnsEI323s3@F^{>|4xY6Y~1nNC2S&u{C#+0S_-2ha}5E$(#L!0{XOrZXw!tZGQi7q&UET-lbydShD?ky2{A(@JYyei5-Z#vG1E zV@~Jt+Y@n8xg^Q;R^FIMv&#LuEK7;Js64(aikx`9Y)|6-j~~pB8=KMo(Zm1%03~!q zSaf7zbY(hYa%Ew3WdJfTGBPbNH%BcoG*mG#Ix{soH83kMFgh?WkwM|A0000bbVXQn zWMOn=I&E)cX=Zr=%<^(B7-~$qa27Ps$NCoxSJTC?Jv12)g z&Z|hZdThZdm%SJ@^8!HCcy^XsK@ccO;)F^~lEfxdsUVbc1-!{DLPe=bN~u8?FAQ1} zSPxY~TNiCXNsrY?lAn^x1Azb@$i{i0T2AP6x)_I2nFTen#1JPj!7NT(A7`L}=oY+w z$;)$SjFGA08zemjJzWUF=eOIJh&gdFQ7~olAmf)4xI*sp#p0UR7NrvKuN%v1i_VZA z$V-68HwbQ656}8I80PN5hGL4)8>&d~!lE#BH1BTk0Zy{fdJKNSSuaZ|34)}xCemuu znJrK-nH8i}Y1XQhW`)wKQO7x!!)i&ZSxc%-w9-Nlghgpo(I&IbsF`K7$e*e5g^omHzc(<yVUwnSQ;L{!HE&zXb5=sN(6amz(yOL!S6deEgvEobtEJH?r<-#W!E@zv3( zvUekY@7*_a_jXhB)~4Vy?Zl63`a$2DU0wZi7cLg%WM518aPahZN70SfU;es2v+>+t z#-3G%=-96rU-_O&8eUnEI8IYP|8lQ4$#UY450Zv%>OF0vIG+AZL;bh-fWxr^53`Z_ zeXkvym_!_o-w$SHq;yS9Mw8F1IxbwSy%Ks}NN7EaoO-3tLOYHkhf`B~#=)nD=wrx$ zP7d~twNAKKOn-3vP_xcx27*6Evc(_@v0;ehNd S#+1y)eu*|qA>D7PeCZ!Ds{79X diff --git a/toxygen/smileys/default/D83DDCC7.png b/toxygen/smileys/default/D83DDCC7.png index e1b35a1d8a765859e7b311ad3a91680d467ff614..f9519fd17ea1f213068ee8e4d96a3f7d10c0c87f 100644 GIT binary patch delta 1341 zcmZ`%c~H|w6kZ}n4kT#PA`mc~O2ILJTq!LCh$5Pdfgm101B64wCWSZx&KO8V6hXN} zfkvwWPC+S$QX(K=AVlOy3`it`909^53gw7^>Cbki|8?ihd;8wo?|t9yEFYyrW*c(0 z004IHK?guVGnz;y0#JP&wGpNT<+uo6e=-2c)&Qhj1mF`?N>KuE77xG-9RR{D01Q~a zKBc-tjnN~AX(Y8;4Q;VlEaVeY(;y29p~L=JQ01j`pj$Q!S=k@8bV7WOGc(U2Ng4Oiz|Z@Gs=aLuWL?4;}CU zm$giMp+tv$kWA8<)X_s)X*PPq?Bf6c<0jLNvHio&;aKs^s8ex~ICc^%5|?;}1%PYT zjX-LR91q`^!hFxwg)7Eg3zi28FRZMEu*T}I$)5=wtF!b`FU_UB3m(do8%55!ftOtN zIv2_W<+cV96_d_#YhIG^^^OR<;D_Q%rB7+|{*TX7Q<=4ek5yx`W9=M+p>m`5@ZDT; zmjh$myu#&pqoMN&G`_gLt>IA}zs*PsRzo$;Nw-^nK6<;bQP8q-*(7;x#J_mu#AOTq z=&9V!V)!{l0Au@a$eFY=+iw{4Rz0eUuXIGeUFBH3pROwzhZ#)8^Vg1>TAmyyRK!zr*fC85X1~ak3_!h+&j{I6+4}?j#Me4CCb4@^j6qIBeS;C zBIm)9+SqT;zq=?Zl44@H*mhc}&G=6l!it!opnNAwR6|?9FWQB#+U&!{%T9U5itp$x zcr)LxQ;$|qSii(Z=%*ktxS(K-`_ru&ySC{`!Pz@0HaXte+jAe|Dlg!AnF<${wtLF7 zI(wU4UkFEn&1(Ifx=|HK2)TBV>m6H?rwF(r)v%%a^81^Z)fr|A)eg%P@fBH79-~Eo zy6v4iQKD%7?4`kT^R$R~cBsH;C@zL?ZQFp0%I|t{Ek#=(wS=8yZ!O;E=4!#Io{w&l z1Zka<$d~MWYY9AdeIXP3UpL~O#E`!cTknldQRH!VHr zL!|AHl0`atTu+RqxS8G_N~K~njCcM_Y8$5V)oGe<^+T=mx&X)w|L`EwFwLaAY?jC- zZ#6JuIPkj5?jp=2_CM~}uODHLMw?249jwjKHm6B8{zmU3HzDUG(aN!XGg;9OC2_=ct7N9|D-RRpN(uAu47%lJ zI-*9sMM2$lbc;-X1X66pCLLol!q}07i1iQt5{#p4Nh od=7E6{6E0%FNiZt#;K^J|3{2qkAIV6whIZsK@!EM(u>ah8wtj1GXMYp literal 1465 zcmbVMdrTX39KWW58*Ln{csj>1E;A{qcv^d6S5mr(*(M3p#PI{BYNqjJze z>g2g3in-{XFf8E$oo3~Z5 z6qHTzOa*5b>rosR0E@YMvLU~s=#BOW=MalRI5EavK~pXv6Vu7XFACB@Y7rPlv1B7^ zHfT(^C>Tvj)Qp&t)rd)nn3L22j-9Xu1i=$A+<+k%3d1;J#Brlph2h2|wFZM#<^VR+ z#S4VXP6g~bM7vv9%@eU&6GssO!`T?d8R!6ODI+j^DZ_%M92DG_>u`~bo8Rv*&t|k3 z#W^Y|(#$b5xRGD2V+Z@mutsUbP0C~whWO*wBq6xQWJYl~F$qSDn0yOM{-2ydqBD@c zIsVfufg`a4{oC!<7lrNNpsnn8D`xsc6;b(@SNup4B~&*hBWqF2 zrd`~9w7sdR=_7A%#6-JY)?on#y58>We5)aHer+X5d2@PVV)3brLp;kO&R4jY_y>jF zN%ronIx=j!=uB_1O#fo=aFOK78V7!IvLNGBLwCz*!(puT-rUek6goC>{L88-a9{k5 z`G?=^4R=Q*r)Cw5xNb~b8)@rmNk7=y@$`jTck0&1=9d?`e)rY;7Q+?=-SwB^;>OvP zuRa*;nkm?|P|hnq{{p!`S^L`iGU)hl_+Z^imM&R#ezyM7!XG!!_00}9jt=zq_qXl7 z#M~LFTU%Pzooj79r5x`W2(5YfNcUf{5B5yib04kx!rGm+QQly0<+nZW`5LNgj>=?< zxAJ3N*XwGQ9>P|M)G9ITJ@MV6A9{byIc7g}Ant3G^=$sqFIPK%^m?Tqj@ZCwgTf1e XvLOjLe|z|Q|IaiN&%(|d3mg9dJp~^k diff --git a/toxygen/smileys/default/D83DDCC8.png b/toxygen/smileys/default/D83DDCC8.png index ddaa706fdd1e5116397fa1cae18c0c30eedad0fd..22100cbf9c5d30ae5bae8c08a7249d4d62c9612b 100644 GIT binary patch delta 1363 zcmV-Z1+4nK3;qg_8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0SHh`R7Iw-ztYk^h;Rv%$&k^Y+&2`R?-d zF@OE?(*kRRPAD8>QGSD z-tF&+isg2F(|_mpt+~SI?(^!CmEc}u>4AjcO;YN>#o;tH)ZFXQ=Ju<%zs}|L*C{K~ z)8g82chwOQ*s8UuwY}o&@#LeZ&g1mX;`8I6rKYsJ@bvfQ@%pB-y8r+G^y`or00001 zbW%=JmRXAK7XSbN0b)x>L|1(Z*&hG^00(qQO+^Rh2Y(I~J;oVx%30(^Q@Saech zcOYj<1J%S7fdM8~s=2MUOK0B4?RFc?u&<_JsV-H! z6zyJA;D6g}C2hVr7FO|1_o{{`0*YV}vkcj}$laT4-V6(^2be8f;@$4HyTlv?0MO12xVY|c1InO-s zoOdJc>9(cg1{ddYy7*BZBMbOcR#iKd#*~v$UVn4NRb)ds!}G_x!;AOK*nzyr8rLfm z#=TDfWaQ%LPcY&QWdR}PKWBjy&?+>7ZB3?DFa(e1ye$O6hJ)D1Y7%?wMG&kVt$xcn z0r!tK5ne#Hb(GVPQ;7mVFP;e4pItiy7uOm(I}t)x(Z3EJN^}$lpAq3)vK}@eNHOCF zFMn5s{ky0MOINthkPm(~jjpY8(;&cIBZPPj*+!dmRZV5jMTo4qn?g;HqnN5RWjsyM zconU4Mp;9$uuF^@G{}@Djx7dVpEJ2Yz67q)5D1t89Q?zLfx09rP&Br}3VaR4YkH-B zmAIE9FeP#bQbLjz#ML+C^#l0;T#mr>e}BjT4Ru#EM`PpED1O*oI-QP$f413Hq}=N4Tg+k;^7`6H0HFi1HFuH$4=)&pB+Eg`oBf3X`ozJ4-Z_div=l!1H+h(C z&lZ3u%@LB;;Hm}|;kG0PN96Ce{h&AcuHa63dz|Gwo(BnCpEPH;c`ClFZ=V9$DSxIe zK1emXljZRIme3zCgo;+uXbwvN006m3L_t&-(^bTYR)RnPM&X|%QB)|U5>zl5Tbx$M za-G}~(h@7t_kWb;m=@mioeSw;F9|VOK8%PVt|*B=NhJ<7^pihsBbmyqfOsS05UK#GA%GSEip7yF)%taH9k5tI4dwP zIxsM91g}#7001R)MObuXVRU6WZEs|0W_bWIFfuYNFgGnRG*mG#Ix{soG&n0TFgh?W VA6Xagks&7v002ovPDHLkV1g$_s{{Z5 literal 1468 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ4|I$^C~+Vq7MKt)1%jCHqz~l4Gf!$BFvk=D zv&OBv*R&WI7~gohIEGZ*niG89J0wu#-?M$!E#6zEO>y?%^1N`U)k5Hg)1pO772G`P z4`}Q%ob_~qfRl9LL$|fP(pxp6uU~3S@V+RZ>9}c&Wa$Cd}w0u5pEHdgyoV-6PhP(JeEyQho>6x_^d?m@W|xSx z&)>Ibg<+Du;5|u)ZHsx2?e?^ow)Xmgdci1v$EbHlq;lO=er+s$Yn9wp@<&wZtZzVn z)I@EKD=l7Ur|C)9#Vzz(vMO$ClY{&2e-dmti%l}wOzXaF>FNJ0_iX=y)kfb^)3%9E e@80;IiHAXSeQ#YlYk(-IJoR+-b6Mw<&;$UOyBJ&m diff --git a/toxygen/smileys/default/D83DDCC9.png b/toxygen/smileys/default/D83DDCC9.png index 7b956c69fbc761074ae794c9ea34a741e450704e..ff5eca463bd495a577d510d11c597e965c13c0c1 100644 GIT binary patch delta 1357 zcmV-T1+x0K3-}6<8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0R>P@R7IwB)N|9=3B|EtsfzRJ<}`um;8 z`dg{`jK2Cwp!&+;`!ALG)$0A;@BO^U(DwNH_4oQgn)F_#^+lWX)ad((yY_Cd_ucON zxW>-g?EAml_&t;E(B=Bo>G~>;?y%DMx5LZu_4nE9`tS7juh8`^istL__Orps?(_E6 z>iKc0=YFv1vVXqD)am%k`uDrzQ@AT8>_pQn3B7fB9?(?m=!qMjTO_0`w zuHLJ+zs}|L!QAlxX~U_tz2fWf&g1l(y4|L+00Mn_R9JLUVRs;K za&Km7Y-J#Hd2nSQcx`Y1062}6RZ*7PFbMqT6g`3r2;}29k@eU2`q|@mKuOl?c-w89 z#Il4K7zUYs|NKQi;?uN|++}$4VvC90th#bBwrXyx?b4a|al73HGv-&*n5iyRyAe*-Yj#tp333!um8OilDGINm zb*?CDNEY)FqXrE!`JFt>vS$e( z3C$6L1}Tv|uuwH!l7l1icgudz8+})BC%rw+avslvgsxATv)McqU$bwo0@*2+E`MG~ zHL8>8@O+ohAEVQXhR`Wed;kCdwMj%lR2b7$#D`YGFaSp3pIH#fjL3{^q6mZ`vq~$Z zETPJH|5q_b_C4RZu-Wgj!QpheZaCmpJYGe4@G0_=8-6tqRMk*8qWKZUkDS+)wS>SYKe_y{apN78*F#G{f#YfxC?(0LM`#L zs!)&s001R)MObuXVRU6WV{&C-bY%cCFfuYNFgGnRG*mG#Ix{soG&w6UFgQ9eFvxSH z=KufzC3HntbYx+4WjbwdWNBu305UK#GA%GSEip7yF)%taH99moD=;uRFfh3;99xkg PCkg-nNkvXXu0mjfZ4#s} literal 1462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#m$jA zqpOjHi=(T#nWM3(xvROO5>#&rIbo&`bc{YIaUdlYm=G`pf|&5659GizPih`8#}omx zMvmaTjSLKo&pcfmLn>~~3AXi!4wSHc|J2M(D!0o^;i6Vk*@i6v?264vg#m&Ko0anX z9%<})m9qT8Nk{L4Cod{_2-bUD<&u<yZ7pBMh#Z7fM8uX%$QN>^9i*Izqk1WMoNe?0e7A)KeJrcjxeu%t8b`3IqkX8NBbw%q$S=i%k;B4WblY>h5XPS~wG z-ncLs%vE?+8>`CUMVrp-syp()oS4q?hx0MM;Agg7oCp%o7u5&=b-~P zKE33fyQgQBivF21Stp_^{htU*sg(tWKW*lylD+C1@M^1RhQaoa>UE7P`xw~l-YkmP z>Qt4+rnFmPr@%@*hquYXleO#TEDeg=dP?T^%GE1&{s^d+=*@qev6jDS&#!omz`_?Y zf4(kwqWE9G{lkO5uP1!Vxh#;m%GEAiO4C3e5_T8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0MbxQR7IwiGb4;L|NnNuQP=Ky3<(TKE0oopotVt%4 ztpxqqA8|Bkh3`DPd9}sZZl*3&jZDpLwVgZBK5n<$AcB2035z?(#k0y1L~5f9#}^4%M6-V7712be8X7g1p8X!RTC1l&K` zRPcggTSqz#oN6S1^5Tww{n@ocaCXtq*{LAqD*D&KLy3;$;4>nebLKEmyeDkdOLo5?x#8qCtSWL2*Scwx~w zB7Y8K&;S4cy-7qtR2b7$!iN&UFaQSNpPH?rDl|rl(8h`tBY#$` z_kSkNImdS&63IUaSgBO{r-e*5oBNqT-u`kN+s>oly2Z~@$#n}TdtT+!_dTzSDj~HR zd8^k6sUo1X(V$KG+GJ;Q>>I)ua(+tlz-Z8m0Dq^^?t7p$KyetPFnA9Hs*3M*W2xC?ssF( zz~c{|3#cBMrv5tP0000bbVXQnWMOn=I%9HWVRU5xGB7eSEigANF*H;$Fgi0eIyEvY zFfckWFb3Z6g8%>kC3HntbYx+4WjbwdWNBu305UK#GA%GSEip7yF)%taH4Qp7GAl4J YIxsMIgGfJ-Atwp|07*qoM6N<$f+FQ*#Q*>R literal 1368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ4|I$^C~+Vq7MKt)1%jCHqz~l4Gf!$BFvk=D zv&I*F4_gKX#tu&x$B>F!bAqk?4;u*VGf{li?d74%!5AbSaZ}=_#ZZ$dTdp!*yfMsqj%MqG@0Kz-p|SUjKA+Rq^PjSx`SRS$ b{)}u4g;h5DH_i>!0F`x~u6{1-oD!M<(-zzE diff --git a/toxygen/smileys/default/D83DDCCB.png b/toxygen/smileys/default/D83DDCCB.png index 2d0720dd997e668aee7c02fb7c4a6d42e9078bf2..ee9495434b2c2da371027ecae98f83e44191f22e 100644 GIT binary patch delta 1378 zcmV-o1)cia3ylkq8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0Siz}R7L;)|IyOZn`%6ILl}BP z7}C?!+uYs1h*HCgRp8>|$enh)hEem!miNw|*Vx(Z@9*vH?tk#mt@Fl}zJ^ikwSMNO zY1`i5@$&TT@A2g4>Fu_EyMj*Qp=JF2{jIdRxxmM`e@fk)VEz66w0lL^msFe#= z-{Qs3)XLA(@bU7QS|+q?GWq)Z@$&QN>+Q>yMC z000GaQchC<0K>z?EnKXO00001VoOIv0Eh)0NB{r;2Y+-)O+^Rh2M!7)3BryPi2wis zeR@<_bW&k=AaHVTW@&6?Aar?fWgvKMZ~y=}jg?hVmfJ80{O1%sf(!`c<2aG^*Z2C_ z<99$w*6VoNZJcMz2r)2>WcvN{7yXD|(?)Wa;mwOJCU&#x%Ej2KxvjQKXWqx{b{ovF zucl$CE`L?K6zyJA;M;5^ZN51cR`E^us)i>5ieM454B5HJ-J5LQ3=6FXm@Qo5-R`!# z#2f_zvQZn6hir4=<(`>*gn{iAEA+L3OKsX~A&%sRr%5XBBHWQrkwt(is28?HRBDW2 zyTi&k&phy)cO&lUwx#0+7w2-i_)#7s3;0x4Rew8`#*~v$UUS7&WJ5W_^T)fxi}%df zfxO5X*DDjoy-xsSAyQm3ESGdoR4}La{uB~&^Ai!NCgm?|vMw@h1O=ZtTh^)DrLQRmP zn5r~oJWbJf6|HkdSwph0ON<&c$do3IEe2hmGr2&%1QuBc;;6s~b%z@RbxBm9z#3bj zVhkAqEb;=G$kQUzBQPa$2vS0l7R1#zR#OY84!3wuJ`xdj=-M5`tg=MA6Si6dOgw z?m+AU#lmhe-v3El9ftd7XU-BrOvq-p+c4yp*+Eg1lct>*hZ*2=n3^2 zKy0>tZ@bg&aZM4>9}GumEJ^ZY&<9MF*&Hn-dAU-i0Cm0DqMf`y9M>w~biQ2C?fw{D z&L@EOd>K78faU!$dRxgC0xKrb45|ra0000bbVXQnWMOn=I%9HWVRU5xGEFctGA%GS zEip7yF)%taH99pjD=;uRFfgF0`!@gp03~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$V kR536*Gc`IjGb=DKIxsNN^h1l0Atwp|07*qoM6N<$f`Gu8pa1{> literal 1499 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ4|I$^C~+Vq7MKt)1%jCHqz~l4Gf!$BFvk=D zv&L>2jv*DddVlbr0LKhnB@so@sX>1Ny@SKo-dN&kY;F9yI(XB`MN9UjK0JHw zd+q1F-^MdVyxiJCtir4sfmI%n?8o?rCSx$sBE&oetScg*WQzjE`56yt`zf)ys2 z99naat(?iuv5!?ETgU00*@8G5?a%WBH@wWv&0VzZuk5=gl{@_8RYYDzHaF&5CzQWq zWM?P zk4SqJmqFXcRnZOszD^vE7kpazOWe_G7U$V}+j4uvKeL^lf3{Hr~_T1rTtYt)xWE-mMG+3{WZ z>|LF&a}G@3rXj1et2*FrmX5~K)7w%X);mph_)z^_^?5yu1cQ#ZD*vkx#VerV*3;F` JWt~$(699j38yx@u diff --git a/toxygen/smileys/default/D83DDCCC.png b/toxygen/smileys/default/D83DDCCC.png index 9735ecaaa41c5d7ca9ce753e9defbf52ef2b4e15..c880d4bfb754a4794686f30540b2cddf9cbe3a69 100644 GIT binary patch literal 1532 zcmZ`(dpML?7(avg=92rk-*)OjC6_TfZc{`tk^6N;j|^r8>oPRPrB&95(I!bmxs|)* zwuK}~D?F`4%DqXHmXfxR>$hM0v-`)M=bZO_f4}$no%25LInPOTayTM}SHc4T#B8iB zT@Ww%ePM+WyGKXD6>%ag3wsLy>eF|u)6obf`dho$0}!VPKtd7#s|c0w9)Ktk0JFXT zP;&r~XXmvz9{>Pl?_}p@1tTLNp%Q|c2!!YA>ae5Zl!Zk(8jZ|HYHBc(IZvTfK^O$K zV{ttS3eT{(zAcC_HE47z28RSU(P)9JTm>54yd^}KHVE^lhUO#e_iUe%a?=*yPoZ={ z5W@5u7*?XuMG48hx};+n z`d13scAKA9Q(K`>2C+E!;6a$phCMu9OG#}gtHNdnzv=420Rb?dzeFJPLJ%Aj1fM?L zPbR~Wk?_Tfu#?lKjLbNMfs>M89`Cu45lkRVW3X_1JnZM+M<56=*g-_4t+OaD`3SYdYKQm6S;36MlCNOgXwFWcGhrealIhk~@Q|z8*=daIi1M>N zVhQ+YRci!bI5zfHn0FX)yaul0f=ns^Lchehd2(Fn(L{D+I5Q}OLFB};8N`@SHUP$H zS?bH5 ziL|Td_>HokacN45Cqo*m!Zx_~qTIV@*hhoC7GIpJJvQz;<|h|^SKZL#NoUjSue(*Q z(le`~#%>RrUeg&7Quf#IwDbC=wmdvgLD)r9N`Gn`G9s(@MXD!7xjSrpvR2hA_{$wR zXK3gozj~Hgw(reeLq#XCl|zQP-fxu}_Loqg&%f{ zS~e~%BVV?wEuGS8B<2@Pm3wy_ZIX%*ifzap+9%x^KY@~T_PSvfpsqY1B(zvqY>wR{ zi@&ZfQN|>R{8nzJiK3fYXcv#Bn!ZdFSv%+Qpz{D|N+;ud%(>Opg!u7n{5#RF5!tcg64L_wo z;`0vj*;%1a$SWVrsHX%XeP#}Xd$^B*lVQEGeQcRmy>m4YQH|QukKGSTREcK%&A(pT zmvNkU0wwGx?Q0@AJaOR^&-RB3%fhsnwEov8A{^Vx96cXyVzq^H#ezb5h!Klov76>* zU`)z$QzG|iOuMtn%)?5%wyp^eleDD7oi9={(%dIG70HTDw_n^38+;=-xI>%Fk@O8s zL1?q<15@VRr5R$zoXttYvGO;zt4n+9>?>Nn5#2plCBs7=Yr6JLra~B>}y{Yx^H06iEv6>i8)YbveyRxYDt2CcR=2?Kt_aj%@UN4#^y?!EKI5u_Ny3alLgpn` z7XwL;;Na3yR^VLwN=w`-lzS|>gVdH$kK>&W;$|ye4iBT_$X!~y(Azduq4V< zRq}ngCcM4!KS448q?Ieuk6hu7BZX^ltUNgZbPj{+AIU%*Ad|>s9g>lbKE+L+ME%i# zYCzscB2h^s=+y%6KZ4NkfFNe<{|lmd)&~)R;`b9=! W4~UKEVIW^J05(<*mNkcb6aN9hIfmH) literal 1567 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 literal 1339 zcmbVMZA{!`96!c{iNm2R5QmOZ3}aEQeR|h&t#^*Q>m3|&jFmH>ez9D8P`S2eTXqMF zI0hNcV@HO?V#y3tupAt-%x6m)eCOZ!n}@?AhOgeJRU<~6*F=k3P#Z)f zAoGnPC-Oq;%U49F#gZD4LiI*{u!;$*vXwV6)`T2`Y>UOYClTYr&7y%ch>eosLg$A^ zQA844XdM~EgE6nzBvrI&Vr^S>DBRW@rUi7*(}**{Kml1acqAc56`e`A&=p+<&dqHM zMOGk2vkP5ysyP|sui1ht7usYPF$Tlp@whc^v#MGn256c#H3*^vB1-gD#o!Yqie8vhU`0KwNijoG z6~t8J8`KuVg+fnPLy%*^;JUD)uO$km3`_7a3|MhYmd&_U(7M5i|GKduS`W3xM2r)4 zwM7fV^N18C$uM{KHe>>!H_Tp5f``IKSvA}ui;59oT`2rw6(oW20UG!Sn-7pa!Uq6w z_z9cCVZ%Kn>mdodFR8I1mvXagDP^a}X4AgyuJ#e`p;KJn;7KlP;LzYmSJuT? z_RyB)+;6uXX@7k<=jao;KNU@Ge&uR+(Oj3~Ru_4*>h772k$2zBdCZ|t58sVNZeE(j zgW2eZe9j52qH604^vq`;&3)28Ua%wNiFQtQ}Q2OT1GIlK5@J zZ9O|*$}WDpqpb7MmiJDk$TFVSSa$rK17}I@PMVl~JH=Tz(8&dFT=?N!!Lil}wD^34H!nd<580EegBuZ>^Fmhw9VVq$h~D&uHDv{3eKjj14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>!oU~~__+#2Cna_VpW=l>>y^&&krD0;q z)+>{Ie*OI%{_9};)>$Q2I)<^4+}k z#Ig-*?bKIqWHA%#J-RKaPhDnJ(&Ub^yDG1xLZ`3j>%GVEY;wxJ->;%IN;^3^j_on# zi!D2vXs7hEfca*#^_v@tJ43Qn)e{SB+dR5+R$UVCzvkJv>V)_vPhalq6V^^Rb)mb& z=KF5-%)>d7ZQMRipPo)iuWHgr2tIP=&!kyP7N%60@`PVwefdSg{w7n{rrr+b6O+&F z>GNRuzKvO7(FKRv6CNQ;SXo}#yEh61Xo)!0$oZU2;1JW_>(ANpm0@E~mio6CZlN6B zrFGqv40-aYUKNZt?@jP?^_j`p5NGpn!`@Y8Me7n?pFT4Wod0VK_|U(PM&`>Vr!+mlto0Zo(b3OX{noT=9GNf$2l+8M|d~q z+ZB1suCn->i`=_%YMr6sHk~J@LS6a2q?8_ZEL^{S;vI$iOwIpU&Tz_U`|xP`6|fS)EwZ$TGXkd0Bu^K|5Q)pV z=iP-G6hxRW+;IFWmUH9x-}NiBS&p4a`D`|mp-krbXJ*+$96iT5gD31$o%htM>ynVr zRNb%rCTAv1`|stx?1Y?2&wQR*ZXeDb-GnyHqMv>jcR9XmFZ;a5s_-%6xt(l>R_{2* z2DDAJ#5JNMC9x#cD!C{XNHG{07#ZmrnClvvgcurF8Jk*}SZW&>SQ!|cC|>d$MMG|W zN@iLmZVfy2WTJr@BtbR==ckpFCl;kLIHu$$r7C#lCZ?wbr6#6S7M@H66)6m!u6{1- HoD!MEm#9CuSvb)(VBs%jJ%ym~wEvBV#Vo!DyHI<~WhEhS1LH3$xK?3c!B>}PD3 zIOReROc@s#OsL|3wy6>q1ml80RAQh=2=R3Ti4%%|69NI^Pzf=f+oW))cEGaz-v9ak z`~AOtskD4HcJ!X31VO|KOG+89li@QyhTjkTaZAI?jIUPx6;$_InhOcZKs5*ombM1V zP&3+3eFosfN& zscNwVWaL7?C6aNSW*ETp2`0tyJaY!HG{aIfzWF$l5;k|pt%gkc&Y%P_o>$?0}=aT{}(L-#(_I%CO;n1#$?GCWX+hRr8g_@7t z3PR0c2b3DfM_vOtKwja&gR7=(pqBSgSf2f873i8zLnH4Z3+&}rG!L;aNI9O7WF^B% z$uMq-laq2<$R!0S#i^=74zR}m$r**6p~B|)PqPeAqBeh4sSJ8fS@vVuT_iVLu z_vm&fNME@{boFbmpO9m#z_@u1e4`$JCcYr zx941K*Mgq8Tc#}DvwHjKy(jN9PF&GDZ`=C47v`_q*FJq+dCRQ-|Npl<%P|C6FH{ob z2lRsc{`>b8?%%(EKmkZScyPd2wYw51%b4Wt?xK2_@gzTx!(QU)>&pI&osUbEWsiM* zh6w`$Q$tlqL`hI$xk5ovep+TuszOO+L8?MUZUF;>Meo#5$4PSxc=r4jt!$c@a?+;# z;E!pmWIq2XnJqaj^+t;Em4peYTd!oz{_^|z`Y+Asyp|nj55=zq=_A6cU4|Xg-&15 z*LsiR+2oXczh6abJQot2&|8@YSF7UnSIZH?8z? zwu@9}U9~9ghMK8vO$9LSf^_0##T@dp?Xp;VPo72K6jPsS!o=v}`((5@b z{BS{PY=B>V|HsL?4^(fTuxPM(dFJ&Amx6q21|u%sFYP@pu8|H8{yf*2z!cT$)^Wah zr6kY6$ENRtnG$UsbT;4WwEf=1679VDevw?mZTHziaSq$ervy(7PGejkuFSM8{JP&k z(=`F<;!c-hzpQT#?rePY;IgD%$ra^woIx@lPo9oV{I8eQUhiAJWBmmGIPyFm2Thg4H15_B5&DMmc`Oi`B|y+4Gq1-%AN+dvZ?Y;F6ef= ze%&W;LVly-GZPWr3ugZg98G)b#-J6D)6~|wv%p!wBVf{pTkiJE%Nu|B}&_R<~})an0ub)$(o=~kLnjH)IMT(y5a7EDyPl8y{i1~x7GJFi~V8V=hT)n zCp7GU;ERdepUQ97U5fs-?txxa^}MH-;@LfS*=MfH`@OOv;=Ud0E>*v~HT4s?b{n6X z_&0jeKZZ@yx;hvHmOla}(pXOy#}J9jYfrfgH7E$UTuc-ZR$$}lhRvv<7_J@Y$pi%ZwZv!^qzD>&utp8g?m)t|^8?q>oSrarvF)Um@` zn{EAx-AZqle*4S7G>c2RaN*->phfYjC9V-ADTyViR>?)FK#IZ0z{p6~z+BhRB*f6b z%GlJ(#8TV9z{gTe~DWM4fb{qJ< literal 1336 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 diff --git a/toxygen/smileys/default/D83DDCD1.png b/toxygen/smileys/default/D83DDCD1.png index c0a4b779bff1430eb6d157a9664efe882d707955..5b4e246b4271c8f57ca3958251ac6a305cee9cb4 100644 GIT binary patch delta 1269 zcmV zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0JKm{R7L;){{U*$q_eu<>+#_0 z@!sk0-s$h8v%28x@!sk0{r>;`{{Hm#_v-NT+~@88|NrRi?|=IG`{?iT-RSP$>hRg* z>;C@!$JO1z&)4Pd@!jX{)#B*<{r&s={Py?wzQxPE#LCLi)zaVQ`TG0z`T78A)z94H z_V@X@$IsB%+q}xq(%Ib2+2HW>_U!TW%GTcO@$={I@y68K!OhgOzsJVV)wRRR%hTAy z(b?;+eph00001VoOIv0Eh)0NB{r; z2XskIMF-{w4hkg%ygz6(0007idQ@0+Qek%>aB^>EX>4U6ba`-PAb4$X0020Rl~r4k z+#n46*C}!YEC~sY<1ywhRr$>E(}EeV*W*n#c9j7k)PH(3GX4Jfi+;pYw2*Aly?C<4 z#2%JiIT>3vw`#jI=Dj~2kIoEvH4RC2$=W1o^P~dZVwJS`>NuFiSKYH3Mi>;qA!g~a zbCS)AtX>QUtp%7ZoMLU8?JluLfdFsRdiWvh+&I*SCm&`Yd&CJnPH@UaTTaB`T=yZ8 z%)1CT_n`f~}5B zRb&VnuYFqx3G0r;hF3GO=UPO9wV~8+J`ceCqkl|<1(IzI;S~6kA^@ZpBMi1z)lSC6 zH3!cw386ddUmFi5I)Z~v4|6Uf9yTFRaoCT%+y(aUpe8Kc;l4~h^RsGHt}s#26OnxgP3n&*zP24x|am=$o4X^1$k7v53)LZ`&$agz*q zSYRHMOeZF<_LmgUryNv-iR-RNJVHcwk$=d;a+Ezt5M8NitTET&E}leK&a$78zgzYb z-{`x9yYTJV%elWA3Eg*^tJ>^}hxF}NAUnm@%?~L@bw)Tm|0VPX=FWrfWO)aXL!LZpcL{@<&!dGLSvd(Jt}hKOuDL0yWfX@A;c zIh}Q&Q)p$F>kT0iESoXrP-DAO6^t=L-1R(fe>k2v)d>OcgR?B4Gz=AhC}y(wa=qPk zfFym$(j@S03n0s%BC!y;9H1~?qIX$+U;8m{d@Pv~8}J7{4U zQ?pG8_^}|m>5|Q5aS}J;79nxQiAuIq@Qdl9%tR)M(@7Wt*#{qCZ;`tFVf6(WM(fCCU0|IPf2KbN-meUfb6{DZ+6q;-RIElgnb*(($;U&E z5Ld#xcrH-6HO8;eXp)KrPoY}qTu=F>ke-X{=%b-9Kx;H>EYUFMFXdIp$Cn7A8UFqB zSr`%mW_Tm+G&sWy9~82yWZqqs>+x5W`pE!nc@ih;2g$P2AuEGfd&4e%miXjGY6FPJig%cE(1x;Qs zuqFqJDHm&>v;`zH98}dXg&>hgL?1EgCAkDaNs^3nV3-bQ=#)xP<)S)KS)5>CdBraa zVO5YsD9*_Fq;k~^15Y1^5DGh;Pl!cjGEpF9NR$gBsNR5tLh-mJv=!CG|Lew6wG~fg zm`7Z^B9+U2P>%~O za-yG4*cE`?Ni6eBEXBw?r%JL%lFAYtupua^k`k1{5aTAGik4k0mc0UG8O<&rMCPMW!839dNlDlWQ6e9lUgTeaXU} zrHfu4Z0+sn>lnQ^KKkpX14#ao;dS0-+sd?#o{x%d9slN}cjxsJml|)RAM2Xcmo|&; z{Q7GB9A^5I*^3X1JZdUfXX@U$ulf4h7h)L=MZKIi!}q4#T@Ac+}l9nyEdLGu*$A?ukk7G+t?1O1Zm}O&8~l99p&dgSMZpFl|mt z%fZ9Db`LuoKeH((^4~gK_h-ueQ^A{Ur-#x%9?SUU4F3LL`$O@Cmz$5pY*+4~#_X}% z!)L0B!~J-6wQ%Q+ZPi=fWtT>-CEc`qYh!7`vN3gL3bgsbz@7`)+lejbbN+C0ZT)vo zhHHkRQ#Uo7PudoFvD4AC>!9`c^)1cIK1*s1JRGjrW0bpcKJDvY*1+D-UOifXHd=k( z_w1nedsd9k(f%E)A8=M`KNMC*>*)Q#ycuU__O^F^;ceY}!Cf=OI=0etaXe+drax!& TR;stl82^PjY`JWcwP?pb85kP* diff --git a/toxygen/smileys/default/D83DDCD2.png b/toxygen/smileys/default/D83DDCD2.png index 400cf7b6b9d2f50b66c821fc8541daec0f01b941..9f5585bdc847724802ee0d7cb8acf2a0de3d7ba8 100644 GIT binary patch delta 1172 zcmZ{kYcN~~6vq!rykkU!$aF|UP1$BGLN+BKD^?SkV2zbWL$D-aU94D}ZLU{DG?gW> ziq$mJdb1ua4S67*YZ%$YN-)u6>QMb!Yn zny}Xomrugoyxjom9_suZtd4v05UQ^?0LKa-DGlHQo=RE-h$jNbg8;}s031LB%|2h@ zg|rLjXda8d0fu3C+z`RmAS}NCMGtJhU)-8bADf!n8URcVkAJSJekE$kGQk!>*$=2v z4A?t=Y#ug6fXxCn4VzuC(GHl*8=HWQR=~yqdjpDBpm+}01O*#}^(Mgj0TaP`Bdp=b z(GwrLVWAjS>v!>eJ`jTsq{i|+k1vIi`^w^Qt8(qECq)HQB~0OF56jEjW6z&^^tCNf z9owY%VgOYxhel`l1}B&!vC)j{kzwYnL?q0d9fbfmf1m4rWq5+9$z3pVNzv7m%{kX? zj^(>5w@9Vk%lsJ zZ}+V5!}VSA)$y3RRY@zRhdC(}B}Z*+3*aY63|(o23;sRT`7ak8E;wQMHO zbZE&$4A&Z}5%x|$DP!1h)8aEJ&)A?0bJ0#q@_lM>P!V)P<8&=s9%5)Pwy%R!Q6vc= zP-|TO$7aez{K-=JyzWB9^y2PL+4iIsP3 zeTe90qmRkvzoG9zB!I7+15>LwO;_Dg)oa8Myt*ba^wXUN*2->wGw9Zn@(yXTEn3aMv?rl7I zdry}TA=BD*t>4POak>$s6@TZ-k))nv37P2&dyh(fwG=;nl((&;9xH35GZqC34X@y+ z)O+F5zfNiBC77!T+A>eHC7T>Nl-)vChO~$R%vRn+KMWN9bDS#U3=8^|<=Z)TxZ08X zD!z0@skx)3ON$=~*;i*$GK(aClrQ548tX-IqnV8Oi^mO9iMkY@?+Pxc9=&5lU6cO*MH*bs?iA~8(Av*|w#QPH8-8HxYjVC47h?rSmK WCHO@%SlI+KZ2&Ki^Aw?bP|CmZ{2*fh literal 1247 zcmbVMO>Em#9Cwx~R7oo~ZDOUt6y+7f>xabnY~@kiVSEnP_?t-COs_@yjV57Z??E(@?{6n-mV!4p_F| z`#=AGzyG&S4-XA=2D^h8hIM8Km*ZG(~Np+aeTqQq%L|3190$|C{;xGG*XCQXO0rie9O9__tjlEBwg z+=&#i?$l^*7?+?8@OUH|E>Sd%vuuQp#aWi#i!&6>kQBPvFdgILEYBqH=0zZDwpQlz zveLALq!cmkx)x88l}aU2iAJD3M$#O|c^nKAMjBzKX1Z!MY&yGI3^H&^wr;sPG;xnn zEy5``MIcYtLolpdZiCo#nu$UwBde-K(h-U@3@@%VZO6@nf8E$r+bPs6K<0r1r|c4{ zM|oEZjB@vAL!Kh?hCg8Is3_{B3`UZc_IfGf=EW8GpbedOw&4-c&F2}UVva2v!vJ=S&>EPZ}9{Zi-2ACDdi zyb)UMFrRHC^qSWt~5o18Rdoi`+`j$Okguc|SEnd9zYU9Cd|=)HCShyC6!Cz~0P-xXgx@efv^sUiRX diff --git a/toxygen/smileys/default/D83DDCD3.png b/toxygen/smileys/default/D83DDCD3.png index 930e01fea2238d143597b82569236d37464a606b..d045646a6cf14d52a8c61c4586b5f44a5fee065d 100644 GIT binary patch delta 1471 zcmZ{ic}&xH7{=a2yzNSxrK_TfE;qg0=4`oEztd1D3z9@b)h zh^UAVqW|vhE&{0_FFR+KkAJM!*H$|p@yyJnQ|N_p$!W34sU(O7mvCbel2b?*BcjjP zA9jbk`(?Gw9aX|M+4yuxc{Q(ESlismF5{QiHs*c9vUNOsA&15l)D;wQhQ_93BNIiu zN)klpRX0Eda6Xf5W$Vz~CGM3D%6AKdt(S*J%`9!&#r14Sn0p~;l&)C^Jz8lsa^(6mf40In5vCkfRnc#WCg^yx6N zq%Oab#GN2M7rqc9mW?Z<1heQm0s?IqT->$l1uyb>QazAiI0)1oCy2NaZIVg!kFkIoyBMNo%qRO!NCp zQ;&HYt&0+vH@>I)U;lV65BTlXTMv)MxZOkgZg>T-bQY|fiWq}I^Aa7{rY*l+Md<+R zdMdFXk$?3T@)a+7_C_poC7@Zw&SG2^C^qe_SM8XIStB#3n@?hWadfJcN*sOnXS##E z;2`PvlzLy`qZ02M@7i;(1ifA*Yz{#s+t4S;-LB;^x{0Cdl>A} zv0lq%MK@l5(5agnyqj>pWYaJubk5iI%Bv^nD6VUO#*AkGg;l8f7^`Bz(KI7EBJYev z3~V7E<_UC;X&lkIrkq*B`%5LHq=j885~Z!Z)}(*&_5>@s!Y|qe9k;WNw z>{~2jd+)8|eN!LNr>Ww5YoE{h!XV~5-raYaD>(UoJ|g(}5cl6tPDnVPl*Ca`Mkgfs zfM@|YaKIf#nY-_Eao4sp@e1_z_KpaQ@gs)B5`81pFlZdk)Y{xq)*%r|yVUd|qN0`- z6cuopQ~|THarCZT z*d1FP#Hmv*dD^&eyKIwlUPH{;*?I;uxFXOuFyafoMz1BmTkVcFs5t(>+qGwYB@DIBv7Yl7-zYlZ62BEf~yBn9H0zB^L6J zfzBsFDQ?M>6uAHgyn_P{?}D>;BHH8K9G%^q9j)c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQs|N%#)f2%rQm4 ztZ`7(OpSqoDbv%%F{I*FPmpiEP@ssdv8)}N#)AOe07((ijV9Mz6vKS)>?%lnbZY+v z`$XaCdOhCecS{#ao0n!@Ix;2bwuxHS%B8-!Ts-o+Ts*E4Gd}KhH|DzgMXmgvb^f!t zmfz={mTY6JbE)7<=u^qNx378gk?H3%`_>)eoR=;8wD8)D_=@y*ma7yh-!F7Lc$fWq zqM%!VX841tDt}hV9jx|aI$k(oPu!mqdC6Cvs=fEF_+R#LepBB1<2NRqRLR<(&C#wr z;i-U`x$%aGwFMnYSzD!+Jqq5?&F3-cddLBeD%;-1k}A<6jh3ptY-?95)+w_&zkism z)@&50dfKSVi!rP_b;5$xrII^LZF!d;ZcF!1XV0qp^g}Q@e*a8&qlpKWKYg0m4& z@?@&f?e(?ynp?iwKGwH*>js6Ysy zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0CG@FR7L;)|9o09w2V;2oLP@+ zJhO{W`0wc7#iGunUg*4R=euhE+kEG_XaCrF<+o-3)^`8@|9|APV*k=|%GBB7vtR$u zZQ-z7|I2CLu2=rZWZkP&{ls6}s8RaBTiT>e_`O%zqDuF=QrDnJ^|w#JjzHF)MV8B+ z%K!iX0(4SNQviRqDWR1B0004EOGiWihy@);00007bV*G`2j&M33M4xdLUc#~00Me? zR9JLUVRs;Ka({1TX>4U6ba`-PAb4$X0020Rl~qf!+%O2-bBY{61_bhPJeIXfRhBt^ z8ayA5$1iyq)mSnh8X6?i@1MWuNBo*Ll2wK`FE*Lj&8jOGW2@#i+a{fP8@JnSFhf3@ zhNQYw?NYRRQGso>T zsXt;)!0lBg!VAc@j&vGgDvU zri|GXg;&wKYm_xw7IKMILk}{gh~tQ%ug{TOAYTFlgtP()Ee{jUe>e@)KyQc$heM}? z6BKL$I-bzZ@4%GE(U1}?X+d25K%QU72jJ-lO#g=r(CF@p;%FS48p#h^rPJw3D7d6X z8h=TyO56@ra0e7_b+21!aR_;S9wdO!f!LZS$$*C!tfM8%p_3Q;3kCGXL6li{4&sz! zZpp)=>@k9fIai9chE=Qx8!S0EB7e8+hkm2)Hr%P-?sqwl$3;TdH_h2>zZIX-k57SY z6sn64QjO|lI=p{N=nu9_ii`F*6|MjP0Do>tL_t&-(?!bd7D8YUMd48@B=ee#Bq1O7 ze~US_mh=4FYl}pO<1UuX=}Z@dB&7r)yHbXb-zY~YR5kpl76_%8h5II$l?au){}1;H zp?*+}&^)O@XkXMKbZ_bq`hj|c@k1lRG}DBzth9)H0c*Yx+gK$+p#T5?C3HntbXsI# zbY(hYa%Ew3WdJfTGBPbNH!U$VR536*Gc`IiH!CnOIxsMpOy9Zy001R)MObuXVRU6W zZEs|0W_bWIFfuYNFgGnRG*mG#Ix{soG&d_SFgh?W=@DlJks&7v002ovPDHLkV1oJq B^rQd) literal 1220 zcmbVMO>Em#9QPKeU^D~4RN5WLlgG3nHStFqH#W^$e zr(C91(-;#%JZBjT?J7C#< z@BRJ%{r=xRJv{VObl2Wp1VKdeIi-l#hr;K+HvEqL5vX|SLYYxCVwX`>bs-_@_BaH2 zQ=NcCsOq)puV6nxM9M~K6pa=R3z}^v)KDkkn+`@3ME`*AsM;h%U>r^uR+_whX@LZW zo+ghZ3v|JeVa3SJx^QH6sHDwKYP?Pk90L8mfC)^9RN$LamM8dWa)VdE`%p}gU_%8> zrpZmGMhnA0wp|F4iJrJdGYsIk1k;=3IHnu0G{aIfzPUKlDQGFArc5)8YeU;ZMfk59+iH8InggjK^z0c|!}Tcd zYk_g@?rtbl#NG(cx&|(aI;Gg!j0r82SJEW@lF$uZU>Sx}QWBRHc{zg*l1y`1R!${Z znP#(n$ri_USd5S*T45Dd$}&ubO{J8iDAPTCVzN)n@LSltJ+do){O8 z#?{ViPvWuqGp~px|A+BL5DduXN7J93JbQHh9C0ARtXyCFvi;tX-92;Pb%QHE4u7(^ zzt$G{aPVpG(Kn;q;PbD&wfgz@9p|HdaPyJ*x#zmC5c%oH+NNte&PVG(5PR=|&h7`ib4=j$OX>`==|1JJtu6oW+IZ!NujJQ>zQhl{TW#2u|G-p^p-K>SsD$dc5nm z*t=`XFWijQU)8&oh_St3fBU-jOfJafPIun_%g>i$2j6~p=Y>O!^4BLW?%3H*{1iF) T;@S=hv~Lo@5Ya>`G|rS*)>e# zTsW1Xo`Hce$=lt9p@UV{11QH{;_2(k{*0ZEOO;tE`tNk0>e{N1h?1bha)pAT{ItxR zRE3htf>ecy+yVv$i{7cBzSnLWaNH^Gl5=EZ`BXUP#I3J;^{W5yY1prOyeRY=1tjtW8r$)n5@fj+=VRWHm*UH;t%^Ox^$RA9QRGoT@>_Ka+CFvIaSXZ zOYimWSblR_TfMZ;oYOBKxmWdQBm_r>zMUc!tfjp7S=Z_lZ6$5b`^(!!-%D;}uAFGP zE?lLjdOkzPiKBt@6OKq-_NADAVh=Z_AH;Z2^?bndv#e3wKd=9$P%hv<0`O5 zcIm$kOUCczGdc7XZd+Z->{#i-v_M>2=3DwzzH>*ny3L=Bqeo<-G6w@fNqR!=Qaw%Sd#|53^=PU1Y*vE9uReaJ1E_bQ&x#v5% zM2=icSn`9rpDub<_$X*M>e<-}7i=ym^$U0T^RB{2pS=D96KK7si(`ny zW!KZM#Tp$LTpt?h30_>HDCN7z#bwX`-G#@BYV5y9MR&}b@^^mYj|qqO3pPFP&1`z` zJ9w5N%fUHf9EmGJo@i`!=}2B1-ghm#rC#Oc1u;e6-k*BM-Uw_ubL-B=vi<-Ob}OYR zsjliRyX-ErF3en4;+%2Y+E!rKJ5PPZd)2L?jIqxS{sy{5wZt`|BqgyV)hf9t6-Y4{ z85kMq8kp-EnuHh{SQ(pInOJHY7+4t?oG4!M97RKJeoAIqC4-R+E*(4dWTGc3s&g?g Nc)I$ztaD0e0stON<4XVl literal 1260 zcmbVMTWl0n7#>Qo4WR}!DQHkmhBcAO?p(Gz-5GXU=hBre-L`I*wAz?-I&-!iyK`}7 z+L<Gj*#UU5#N_U3$BeMf2L=t9<~Co96$P(e#Mh+Us_kdDoL|i1Dg906|)p z2VoY<>ex%?VFJTe=Cxd}+nZ_U6jP7LflS2LEriCfM2m09$}n`n036heB>u~Z860S8 z67Pv+$c!bzA#MA(4ZFuXa?1Fy!m4;nGf4OxBG92L179x~4(BKF1zrxVgJS{*3nK1t z5??f`H`4_~(}o}xiG~%Dq5#80sKyw>P+I{_QZzxLmkCpiT#Vu9CQyEGq|H|ITvkez zb)j7nA97ubBM8s)B3?9N+JgkevTVRX(_thLcE$`>_QQs=xxyepN3k`_)l35fjPigv z;wEuq>7om|mB}m-8%{Y;C}e~$TLcv$30)8TT99_!Ed1AuWu=|mm<5R}bj%T3LHWpU zu7FYO?yo2iMAmSfwuX`-7bH^|(V^j{r6i7SBC4ixG(|B|Twqc>E4HGIAd*aq7UMBm zB6*@vX#ugD0Z;a5hH84wmLNTg-b&EcMxmOrO&u)6m(!Nf&+v_+ zK(ee5m*~K6o)_XQ&r3ohD@X#%;AO1(e_}=;V~C(Q{!=WKBUFLGa;fyuW~q6gfjY)U ztx>Ztx)sA#Y)ngh&c8an|I3ale$5vrZlyxhL)ZJK-{>Dm96UGm^$+@wPn^70pIEiN zb0a(Z;*HZQcUQ~N=y&zrwmVemxx?o}w~x)?GXc=R>u#PxsAKgZF4~ z17JU2*<1DYj>)M+^Nxm1&40E%R;;-U-z#HE%Ls3jROo L)(+`op?~rpuxzaB diff --git a/toxygen/smileys/default/D83DDCD6.png b/toxygen/smileys/default/D83DDCD6.png index be0ef9ca1af4d24f8b5a213dd9afae8c24370dde..9d187a4ea8f5da9b8f2577650928e5c88375e401 100644 GIT binary patch delta 1303 zcmZ`%dsNbQ6#t4Z!fk3Uk z0D!$NreV5dkbn)r0`REFWIftYhizj5LPG$!>IeWm8vw1YMOOiE84bWp6aW|&07!C0 z`0Nm&S^ylE(Gmy&DE-%O=P}e^T@!MOw zp0xhPehNyB#~(VcodX{6S+!T_6`hIcV6C;|6NV@&9V^uo$=1%m( z+UOovLb-hs8!sbrO17P|GHF?>Pmk!{qK9kFA+*m{#4hb>Kz4h7uNQApccrzDmU5d_ z;{|p;wmueHx#iTVK)Kb3OXP^B5hrO3V#U|fMm^a+U z4o0nhsmB+-7_54bgS=;$&7y4xVHVj=5D9OFz(Xw; zYFr~38CApC_nk-mh3Od)1x>GcB(o?8ZpZfF4mvj0`;1V`cWG#nkh_k;Gr{4x?(Evc zj@&8F8jc-7;^1evBX);lp^MSCRd>FzJ*TJd1&0yMD4X`Tx#f$zK3_&~5E0c2tuo|N zTUO(BQ2T>rW-0R?PQJJ^Im6C|4w4pkgLD;G^6=rc^-% z<`+U;(^g#KX^%GXjvv|Vr8+G+mQtc;dH{9P9>VrCWSoIv-T+>gX@Otz!Jzd7t9WUG z2_oMOMiP2^q0VMY-M=IIeu|bBU9G7?yua7_2ciGZOHA>{KuSt^odVLXI58{F&nU7= zYmV-=Vvldu8q3wW)hojdNZo_}6zGe^qoB9}0+RMalo?%4!kUzQRaT8p-13a$6tuR{ znG+UiGvD{BVrld<{zU^U)58Dl%pw2cas9RUAhY`^b<$hDcTdQo_ZC@%@6^2X4J-TE z(;$dFQh4-j!3~Wh2!fd+t=HN<*zs||&ec1IY%V|`opUBX#^;rc9mn3BP+R)siXB8P z8k&n}-ZVROFFWpG>S2?_Qn&pgymWlkrrgX*_BLvosN2?oiaSeD@oE1rNX>!;oG7;2g8(29cha}X HJ1X-pR@G?~ literal 1424 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 Iol`;+0O7ssiU0rr literal 1240 zcmbVMOKjU@95=PPmOezK2~C+QYPprhBoaT8IJMOfY=1QiETyCo&0&JN_LtUc{g~}8 zX(zg=2cQ!|>Sf}Oc|s{k?(hm|9UN06u_NM7 zCCOc*@|j^mwp~cXBhj!%GYr9T5hfPrIOaKmr5To@(aVLIm=Na#HbK-M5@~bwf{+DD zT^HIV$w}XL1d1vai;-e9V!IO*!}EN=!Lnf_5%y*+UoC|#ueZSfpr^Tp;~Ta`1dQsq zJ>w@yWa+L8rjyC+5nEn8P$*ha z=xu;e>>jTu5Jc7pueb(EiaHH!ZN`L_p9V=1-9&Ul7g&bjK)=K(A}~Y!pi+| zR;F1c5pQtpheaqZCQC8EN(#davXaDe3N0t1Tp}(4nQ36tmglRM1{-<}q_>WZJ{Bv; zE>wNn&Dr*JqXLE}ZQu4LZHJIYIO5q+!_w`dcQi=PuD1ZX#%ZW4u5A)K@fD1H^m8Jz zoaT9{AFzSnqA2zAq6nlIF9GBSS;y-CCuS5fh6;+~KgH5GLKPS+_evja_L>J;sAF8z z8h0*y_Y8(T(V7NguJprrwN^{v2cs`1aa_i`qE|kte)U3Y_w35G%Ebq%-gmxkdU|Se zd;KVWdF=1iki+%fXfC|AUK6`7UXVekvVIW%@@K5?Q1#FDQ=eTSmWDRp9$V<#{A(it zsy$0vwU0uT`x`y<{Zy>_+iLmR$FHV;YB?|T;q&70w_bYx$e;Cy3da;oxqs8)a`Sl zZ+uf;Yf^~qttWrKe&;Gs?xo(%Z!W6r>-hF^sPfdwzRL&b56_fSx6hpghbxCx+m}0< j->Xq8p-TD4LTA?)*19pT#Ma8y;9rv-90H$8V+;QPQB15P diff --git a/toxygen/smileys/default/D83DDCD8.png b/toxygen/smileys/default/D83DDCD8.png index 7cb1ac91998c8cd7ff6d8ccf7446a2ebd3f35cd6..bec3da5e0ecd339e8dfabd3e25dc4c98aadbb744 100644 GIT binary patch delta 1199 zcmZvae^Al~7{*^vG=FOfDw)Al$Q*`7sA#1QR%Dt#a9M6G^0#JzeASuS7s--krnXvU zmX)riDFud>TF#NF(Gouc$fW~3-vPj?0dN-7b;g7k zgZk)AJdVL&z+g>`2RRBzJT?FFS+RU*b47|@A2S##hd|upyNU@%?gDyqg|9yf z6@wtU0BASG`(r5Yhm-f9xQpaTg+qN%be-r<2B``PJMb<o92Vrl!-C{>L&;tcm zEv#%{{|(4*!CTnUe*G20Ml$MhjcGmzRLV^7Bc8oIR--yHZF< zfgpwSRki>iu;O@pgrbgaJu&MPB&kjwbSraxMs?x%w(tC;u*n$_{0?pB9cPe70*nj&>{=*gc`X zymVWpUP*{CdnA&!JWsvg^P4x*HQMHFIP+wphNuW`Wc}H^RR53dgWMXL3Kf)aGlOi3 z8!t++%=ll|L^U;8dktsJxbzVkheaWsUMF7?qgI5`Emz-pXffArjC;<9%9A)O8$Ucn zJh$_p>h+j%zbNAJ^jH%K&Cx8}zc+Y`opWUdIl-KJK*!+A&=JiFIZ9Qk?%;L9nufLI zZCUz#=~GiWguj)W*glx{ruRj$W61l!$YjT7>FV7-;YESOm)hJ%*I3?G*M$#%2s=)W zb@zMq&TSAkho{3K5K7uI+4SEo9XPALBhT9=^cuBqFk3&p zvVCD7;Oz=^6k1d`p?O^I{PpLPjI_3e&x*L~_Tkzs;+@wu-q9UK5W|UqmA$oxsvS(5 z`i=8Mx$H2W%P{BeETJS6IBGv%hX;*hV&r)?yW7wuyq#0D?#;GD>6v2-^(FIFsvZZ) z(~1nq)cd$vvT1J}VG;j(-t=-MTbk6m<;X}@splB_Gl3jg@z(eD3wy=T;|Y0QWwety z>!V{S9BOIj+F{(e3>orC`grgf`%WEqCHYC=0X*glZSQ+y7cYd9fF$#g6joABic!Fy z?(a{d2hjW&JU=>X)oK+|~3E|i$jcM%@Y?q{^ zTH2vWOb8+11&IeJ5+Lya+8Y{3DVLW@yYy|+q-llmFeqY0Xi!0vaNH(^hiVU4w$J&G z|L^g_qXBD29cKRxa=7Gvk5*ZBh?($f{jNXbc-oR?E6E3w*o)W-Lb}?tWP(aLW{l z)3FSlDJ!64jV-!hdT}CWEY2FdNhF`bN2>xNuz|1RReR3ygsMpF@(O4l98(0otK!d! z#GX_6%oMIb7vM265;kat!8wj(qA`wRPU0-huoR7MF3d!Q7$>lCynPYKnrjw?teR@u zLXt?7e7`JERHafOD-ja9GZe$~e89o7VWbiE793x%h8=IX!=M7saILa$K?e^Q^#Yvt zMFM%c7lK{RWcG<2ubn8AGODVVDTbse+YaK|)%N@>_}7gCwY}Uz8Bkf^!Fks}^(YQ^ zz$kb3Hxwu$Z-hzLLPgQ%RA|iG!12?nNT4sIX_*4cFr1o@xs=2!8WLoM=2EPZh_MRI zrsA;<$3a+)mKh}$O{lD#Vi=8;l$feSwRj}1X|k3|bg*g1^L57n9lI8?+s4N4ixm_X z=st9F5YBZvV5$Ut=#^j@SEf09aYpgI22ES)3Nfx&ja^^veYJitK{W zAKcp*NWAyRkB1^B{%+w{tv6FYjPLyQ#`m>P2EXa9HJj(hwtqV^vsUXGm|MG5udg0y zU5IWbKYgHfwzYDq@#@C4UkleqKI#JvCB8YBd;Iw7$169NCzGo`Z=BiQ`V-~`ml_jm zy(MzzUhkFe&xefbBgbFAxqPdB_QO*nCye;!?T3F^+377}m%H!Q-@0@D{nGPzS5sGS zPxif&cE3)(d*!=JnAq3+Hv0C~xfVT8UmCu;JXGHtU&caM*K@D@H2dV8CxYKeTANTm IlV3RZ4?WbL4FCWD diff --git a/toxygen/smileys/default/D83DDCD9.png b/toxygen/smileys/default/D83DDCD9.png index ecf7d465fc09da212edddf7e851fae11f7d9f2a1..7004cc8d3438202560bb631451d95905fc5e4145 100644 GIT binary patch delta 1198 zcmV;f1X26q3EBye8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0H07yR7L;){{aF60RjWqcM7Cg z0Qm3d=(B<~IX?M{7T|mgF*ZB@ogny%6fQG1|C=56h!QF;GJpS>7`1X7_Jk<|BwOyo-y-)01pxq|BV3gegFvz4gZP& z@p}LT1_u9#0PlJL0|W&Bh5+t(00II6|AYYUcK`ta0q%7G*J}X(|Nnp7Dw+TQ00MMU zPE!DXwke^N0Dk}g0b)x>L;#2d9Y_EG00(qQO+^Rh2M!7(I%Gve-T(jsdU{k?bW&k= zAaHVTW@&6?Aar?fWgvKMZ~y=}jg?hR*4r=$+;fT^K?Vf!ah%xN^}Q~8{LaYvaUHk) z8ogLDAO;2`%g^sWAymP=w|wOo9w9+=_K44YJ~+or|t*=)%c*cdCpV$#C*xW|@8oe%-hcyz{L z%dU8tk4WBbAiHsgzjkP;&DUB)8C=gHvZ|Yzu861DWI#3K3)y6Dm21fEkm}H@cFg-T zMAu=q41dD~CaL8#^pTz-3fNS2_c*1dGL%fOrDGIZkWSC>(=B18+lV-UJlV|gibU~T zcK}3`mZ%RI$%eFmK>hDgAOw^Oh0vG>Q!^NX#Zw=XfUuq*mQn4*9&15@eMPB1Vjh6o zyG()?kR2=1X^5#X0nm#X4%cVZ4$dvTvd>P0$bXghKRah9CX$0qr@OR~kA?|aarh@M zSBd>QsU23XaGx$8{4CC@&HJH&gS*p(Wo6s4Og_7h&fYh{vyaonZbCa+xLb3@9FD@f zdEGUx&X$GTxN7J@l_63dG4%C0k_+T3U_h7v%kej&z)YP^iAwZ_h=LNDLNWmkGos|W z9e;skAhRKjEg2-Pejv|p5K;)#c>{-%H(_8ZI#oNP;g20VPs=fcso?X z9Z8Uv5bFFoNC1%ov2~oJ03KejW=obsCol0A3gpOv#b5yWqmnq~nA_mtS#}N* zF^`pF?X==buptHqN8~S-{n&5v)rLFu+kfLO*Z#anlT4P07c<3E)mhBAsS7hL`1y*yK%;H zd_VVETZo>N{hxvOki)+bg6EBCg5dj8njr+ioE8XSs8r-AwM2+xtyhlLYlI}((0>*o zO^x|3#_SNX%-Y%(l48{d*MJ<{@JjW#11uo^GsfTGy46(k5`P?RjL$w2z?eF`4 z{{Mdew_ob-d%U%IPcwobt+`&Q0N1wQX=;S;j$6JAmv%QZ=nj}Aw<6mB;Z<`OpgCP0 z0R?oa&gi*K!+9xelb3`0;F!SBO%-=E zg>5-CnD0kL(*`Jw$D>M&BvFRJ$pp4%C2j11W~C}@Jbvv?Gb`xSvKIHs3_EkIunK~dr`ybtTRZ!QEbg}HPb)?MtRsA zcT*VjbSnhi%ICL<4X2hUm@>kXErP^jgsum1ZE8Di0sQO6j@nLf!U9AAIOe#mztL9>I~xiVp*P%NTZ2WB$0So3*MZ^Yq!b3f;Hsu_6iG5tQed(?D`ub|h%qKhiAkCg zV^p@Au5;{!6-7S5#|2TMge*yBsAMuLWJNJUcE>XUO(yHuoZ+~#p@6zw4ce_?g*#$7 z(FU?>+C|eGt9L+u*>p{(Y+9%|z@Yn&YKCf7oC865wxX4Qt(^dB);4u?Gryd+gMEfi zh(e5Ig``9Uar3;8WO-f^609HzEQ8gs>i@|Z0i7X&=J-#u)Q?~X2HWk{hr;&o00T~p z4M*d%eQ*DPAfeqki7$FT4sZB=I{ZNViBvc&hTGy#6JP|r>vGT9=WU-{Pj}9J+wkD% zO7*V;;SUckebJRXegAI_@kiHv^z!SAd!_c98{c;~u3r1DDYCJ;9_jk@k#90@hIY63 zOJg5|rv2)RH>-c|`|5e$ztXaDYyG|-kU9P*^7(bM$KCN0)-XPA=0Yy)O)%{09cE Bu%Z9} diff --git a/toxygen/smileys/default/D83DDCDA.png b/toxygen/smileys/default/D83DDCDA.png index 2ebfaf0aa43dce9e8d7e636170948274b0806139..66f8c39dde3434913a608018f14eed87fff566c3 100644 GIT binary patch literal 1652 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 PzF)i9n1EIx^A`UDpk->v diff --git a/toxygen/smileys/default/D83DDCDC.png b/toxygen/smileys/default/D83DDCDC.png index 056647b697eb980694f199aadd7499010edbabce..64d1bfbd4a9bac3e61b5886e584855a81b94a57a 100644 GIT binary patch delta 1139 zcmV-(1dRKU3ZMy)8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0B=xCR7L;)|Kq!s$eMJ>nsn*K zqv^$@Wzn$>Wv+>fj%%6J9pL@xhb=$9p*R6)otc>QroPXxRq4UtM_}RPQ!KCuf zulLxx-oK&m&8+j)w(QBM@6fT@xtr<6rR&VC=fk1s$f(w|mEyXV;=7su|Np_gSn>b> z00eYWPE!Ei-{6z%FFybP00Cl4M??UK1szBL000McNliru<_8W6B?BT`{g(g$0)Ki` zSaechcOY*R~9Z;#0`!_s?JHhySY7#2XK*mUU6qhZYmcN?O#k>CT;KACJdl5JBEl zTvCjSbk5Q(3&XZ*OHyq$Y%FZ6>_rq$IAq2qA|AX`=6}s9uT~WswL6$Dl>J>d-HlzB z90AdY4v&M^sW{9dk`FhKJ$#28J1Cbbz2rn5+^`UFp`DpJ;>o-)P#O6`wy<#VHDnv4 z6ndh8=e%p89%gG8t}rpmmy4e15u$)iAra9_YLr6h=~WY4nOCGUEPk{btZ0vj4af^O zzm7-<_kTVC5RtK=KVA{5NDBzA{~QHEK&#LQ(iW9!s1Ph(dR-C7{ zN2}jr4#54RO$je3wsxdb5mWXAP+mOYu)n%?a?U2jd3GsCy7T_Eb7!I|GJj-dk4w7_#(p=5vsdz}= zJ_WodSKWM&igzc{q4_OIe*nbqijUU0;(xmU003@DL_t&-(@oDs7Jx7i1kpi)d+;Cu z?te$p$eYyi?}{8>Z~+m8QOpSgZjy4D0m_T=ugC#a%~gO#wr^6(<^}G7t{?vT4lqvK z2$hG0YI@x;dHbt}k4B*M`mf=eP=OkG6OR zCgvn0WNsQ_*uJQ9TO1l=hRc>sXN>5~$f8IDUxoy?ObyX!$kgbjTjo=^LqCvxuxa}| z|EK@o@BiL?&C!P9jrVUv5TrQVD8%4eVm})S;k)v;+ag@HSiv@{MNe31(Ex~F*1G^2 zR>f`*1EM_8cM;SgNI^o0w^?nG-JGPWF45L;Wz-ZzBS>w1CM8Nez(Tt~x1#y5<@Y|o zP(}7(t#pKlq5@i?{>82Tm+=WsEN#O*%87JxHXojOaXzs$GHA7Bt zF(H(*1tlMrw5$||m64GfvIimS+%vDH%%2QgjWq z8O1KW-|}J5)722vR3x%SteLq)!Ia?{F@=*Z0#{W#t`%+5ih+OKSXbMO52OGd1E$_@ zNU$D>ZFw-v-JK2DiqIRb$xvWX#9l#{`cJ>L+3fx!+Hf?pn`5%pag~_gOr~MxmmBD@O$W*0L}XI*sx|=q9%d7T?N|B zVcmDca=ZaVOE==W-ka}$=A>@vW>Qa~d<%nCwJMsdr_G&qc~+wpfT27ETEcqkYf%bN-^`Wpf zJV1jJW5Cfk)o{5TK^*?D;E!j%8GA7jJx`PkPbtr2cf=M(Pdo1OUoCrU^a}Dbi(Drs0xzSYPR`d#{%zj?QBz z4@BNR`SkV0z@DFqH@>2h;p(A+YD=@w=tZFLV+&zn=T?^uetUXWNg>^1Js}6wI~@ zkHwQ4hS0fc5hddVFK}vjE+Rk(zpMpls9{jtBy_m{iXer3J0UYi9qMj EKa4)Y=>Px# diff --git a/toxygen/smileys/default/D83DDCDD.png b/toxygen/smileys/default/D83DDCDD.png index 35e9942d63364fd57d492129d8ccc68456a5633d..418fd8caff7fffd5827ad1d67619f10f62515e48 100644 GIT binary patch delta 1517 zcmZ`(do&Yz9RJx+S1Z!ZqpR*pN*);%QE4Nu8+AP@;fM_BlF<^1G4d+!S9wlJwQ54C zOI+{ATJo6Z%xjIA$JmTEV|V>^|GJ;^J>T#7e9rlN&-a|~_k8nUxv(@<925YcsVna& zDbl_uI}`vFsoO-JG7=8=vUacoAp8&jQLz9BB~la%fIA2P*d73wJO)4+Q&3}n8UU$? zKW&{X#1brHMC3Cf@>xn=yCkv+L`jZL$R?z|icLsLjE}BLah#l69%nDDZ*B@VHgQW! zylzqtowc~JDefN{NXaQ_u4rR-Ge>6@B;DHk=`CH~(z2gWzEKOFziTBnGh4=nm~)#F ztnR$75<;%H9*Ac>=zZb2PhTYE68a}*gzF+QmAcws zDj88c>DrrX&@;wd!wH3}KLl%hj<~~;R|MvWrr$;xt7`%te{F@kAnvf>y)*9_rcp*F zdHmJJuT)O0mADSfJU8YoFOY}E7kPX(hsWk{N2~9NTUEs4_FNK*y}<75r_U|%rWckN zKH64OO+V7)jyG)fy7n;INv zj4@fwJ;TiTrLk#t92QN=@Tx3{?d)u(P=_f4BR#`o)X~XN#!O@P5QDui#hPQVISpL{ zp>J+rB}>|lb-3_zpMY<^+4w_AXs$G+y>2bxQrFL0odXFndw-HGSQ|TwEx73|JLC?Y z+GU27Xb@bulQY)A^DZ0{9O&cc?+wR>VZ7m?0T=*ICTCx=&!Hi(5j0-==)OWy4|ghd+nCn1B|bEgFOKNDg#PU# zEg%7DT5I2HmjBizAQ^X4j<$HaV&8cWuRdr0zotW*O0*0US>1NTv1fCN^opJFbOHd{0HY=%`hUelwmg(Awz%W}dQ)a{FV&BkLbzhA75 z%Jx?c0%5&R2DKF%Bj{2J_BRNo=z}J`5D35c8FH)UZn^!YhaHcp;9DIzj#8fLNS$XR ziDw4z(zGOpe_9KoXjV_;+>zDg)-Tk{$kIr?BFrb#*C7~^XL3Zyfl{|H@rdy${WLXy zS^5JX(u%-hDxYnVgjZI|u5smFq29?2Fxg;IfA7ll>Q(_sZv8=O;@cbP{)kE#aoart~N5{nEER(qa zomv-$As{j$C?p1NZl*im+yoV&AUo6Td-N-UmVZgKPu~fTF8!WGeZTUE@@BqZ^k5p& zJrd<noU(Fa5EdT%j literal 1562 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 literal 1438 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL17kB|m|oAk;*!L?>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#mgwvE1l5~DPMGNf9itCQ97u@;CIn1@ASOKN13B=_lbQ$2F-5?v zA-y44gMoqZoTrOpNX4xsm(F{OB+4BB`29`)%+l-e+PmK^nx;0}b-M#clfuh@_EURG zxQ{mJ&27??5)7RFLjFa}m7U!ja#8(NCz_KSUDmGIv2ERMvF=IdeCDhBoVl!H`ynZ9 z-{&*mXFmU3qx){Z`{S8D9&v3M2`wM?&bC;{Syintch8FFf{GfG6DFO@Tani!Ykcte zBNqP$mFYJ=Cth#Lk6*Cdc=-)mMGn=1tzYE6q&;-8fAi)=>+b-LLph0!pRCr@PB+y& zZx+x0FAs>O0pzPCVE5;+XlABlAm8n=JRkFKLXRxd7H{$0G&!rXTYDDh+KQZmZKU1wNo>>J!Z(}56 z)o#|dMm(C5v~JRw&zk?6p014Sj@JJ<|6_CmBZGlXWYe8>Y$c!))6><@Wt~$(697Yi B8R!52 diff --git a/toxygen/smileys/default/D83DDCDF.png b/toxygen/smileys/default/D83DDCDF.png index 8d932d2c1b56708c14e6edde39f54c1c25348287..4d557ffba0c57ef6e3d8c7d446ca0d87f913dae6 100644 GIT binary patch delta 1227 zcmaFB^__EqWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D(hN;^s>uI!(+c}C~F_WH?n3->Gt2?^2A(3r5Uf9|0< zo}Qi!4Gq($Pq(qL(bd)6yLaz@4Q_@93{_QCKxY7*c;fin3qTihmjw9*Gt_HO`90;& zpD94<-nJ9-f#Qrw-tI089jvk*Kn{C}r>`sfGj={MRc58=ztb5QnChxRB1(c1%M}WW z^3yVNQWZ)n3sMy-atjz3EPAJgdTyFyz;pMth-K5oj6P@cis`F#ivQSj%5nHkn!jvS zu1ci;;mHf<-><*0{ZHvzmGXMYi;I6gd1SslAn#nBoZW9V?ayX;+ixed)s*>mw$500 zsib!q)9rW0nfbW3F7?lH(mrX2({&T7G4Q*UXn+18jW7mKZOp+rzr1Vs!`e ziOFYv?omqov)-CPiHq}#y0(yPCZpol{qBtd7qm>8YS($4T_K<|uWCLAt90X`w2t`F2A|>+h1EE6y*vxwTSW%3??Mt_d5Kb!_#Dw^FITr=0Y5zJ8z(+-c#?v&YW8sIi8Xr-_U4^oFcS zyxI}7n;Ggu7C2lNY)+iIrD0~%gcgv+I=T@>kcQ?Hhb&<-O@x zn4Q4(a9&FzXPAh*hfHMsge5m8rercz28J!)WobTr)4Eei?b}}8H1K;|y36odx$c>o zos(P7EByGSpe^2T-k!tEi#2dxz&_Pm4<5J+IxQ8GEwKIXU331f-EZc|h2rVHA#H3x zSE-h`MwFx^mZVxG7o`Fz1|tI_BV7Y?T|<))Ljx;gQ!5ioZ36=<12cmY#Y>)}=*Z1a d$xN%nsl#BWo=o&aMRhI)22WQ%mvv4FO#oEY9DM)) literal 1376 zcmbVMZ%`Cv7+;7{hLTVVO?23np)9%EKin<1J9i+rdlzRM!QCNyi)zS>Y()U2si_K8efVJAGy}>LCkp(Mex=D4XpyzqGZAT=0 zRJ0lkHt$rqvl!)M6`)3aw$4kCBudkI(qN=%G7F^$lEMji(>l_?8fliwK^HCzT2p;~ z*2UWwY(dF_1vE`zaXcIj>%-Z4S*^xNhGC){6s3b2x=@3p2@#zX%7`)WAmmj=MH6KS zjWP;Va=m83pr`X81Qn-qkyr{XBnqYsj|d7*>Ipm;jK(#m9nxIjUpJQ24tW|BfV)6Q zu2;RV9{!9N80PN&hN6nl8}>O>ghdf*dD&YZ1d`_9Eg1Zw_lZ81B1xJzS!sJd!`Yx< zySd4AbCN| zt_bZeU`_YMvYZM8O;$a!TpR0v;()Blp@6KQoSQ~BmWh&24u_tKmS;X%9;o6T;Ipf8 z5S_~}D=uN*L^6cc#t|ltr=oE)Ih2jz?6i%{&LJr)k1b$*|0icSbOw($$A6k7b_6>x zx?OC2C@c;Skl@6qa5Sz1umwTlUUl&Kp2*mMd-&P-l9ZX9xnsLNTNQZx$_*Q3{e)ZD z)qXg6ME6Es*T9SRNp!sL=36iLW}BNEHeD+vdOSP+KJ!kkH2qq|(dkzn=8%xEYynResZSz#8}D;M<7@wwf~oblL|WUu+SN=R)|1bb;(w z_V2j8u5H)8AJ?o*Oh6CE+cP#~H0}R)hoNce|Un|6;k;dG{o9yv~6gdEeli{(99=??VF)QD$2KJ5$lKtnW@% k$s^!o=@YXvY5fUn5gHw}q|zT5qyIdIt%!fm`ux6s0CQ&NQ2+n{ diff --git a/toxygen/smileys/default/D83DDCE0.png b/toxygen/smileys/default/D83DDCE0.png index 781669ead21fc55c98705b8356d2ffc290768dd3..f3cfa403e760cece373d994ba903700022aea3af 100644 GIT binary patch delta 1282 zcmZ`%eKga19RAv@b(U&du~2Ju!!&L~ZP`(pmrCJk%{jEXC@)FOSt>8*wi!)%8SBMc z-78iY8KZI2xLa7`tSF>w#7$~Mi9}89{_cPG{&Amwp7T7P^L);8KIb!ylA`h~xo`jg z(Q)-*h)kpCesln2xhs~!5RfLE*w63-AlViGUOE6@A&aL6;4B${$xr}jg#h5D|KI zs~3GOqeE)(tqhgoj?tiRXvmezMUUF=-mMhAdZ`)FwY?qdGAxWPE&lcHt@cTmbY`l% ztR$^XdHdyHtFooE_5QCj+$n93w))eD?)i^TE6R9snXpovCYR+xg_;DxRETg14F7+w z1Ht7^q3QIT89gdnJAfv;)e%$?5A7k}d9!+uC06MX!6DA3;crjR%0cGv|6B{mMjE|08D0%iNXGU6EX_+tfn;l)<0)=skCFwAWV z7(`+6a=wJ(o2LW?DUq|7WoM|;x&f4=MmKm0V@us!aj&y{D`{3jvkx$zKOV>ERkO9j zPNZm@W#WgQw$+#Gt_W8g^_ZN<-E-{O*ZLe401rH|qoV~AYSjvNq+MPVN z39aG!<&8J5*8RqbHyLAVn~= zi?7G4>bN+0TRv$hN`rLIlEuumMx#gL%u1gUS&f~M4b0E2%SDdd^K1G|U8`RUbe{sn5 z3eUqgO#+j3hP@;%rBqv+W17byG>-doM#FgQ1NHc8ip3Bjv1F${IKVrFb*{lZG4bB6 ztno{X8HW^@Io%m$5zLAA`W~lSolF`rAM#=6HXK#Y5$p%-9{(BZZXVLW{sT6Q^252` zR=*E-tv4R*+L*dL^US)Kk)NRl75aG5nd^E_gc4R=YU2XmJsw!tmQWbu8Jd6ycj5EQ zn0o{w%ob(40|{wT?@KVTpd=xPc~Njw9jS|C@0p54lHhg)nN`Kjg7W;va-r0Ux*A^l zTWe}5X6-tw8K)WR8->M1tf1ne;3GjrMJV#}Bf1xZs??g5X)S5uHQ4xYG}Z!hRscWb zWERVX0ad*f|C7Mn1L|gz;2o3@9+nV6JHd&72so3SogK+kN2l#fCo;`t2W^KFiA<)E l$yCZD{y%|Oc6fAT(*Fnc#xTl6o9s{k_;~w!Nj*aOe*-gYL9PG* literal 1389 zcmbVMeM}o=7_UPoW3VinW)5+j$5ha@y}QzD?`VhZT`OgjK|$8ck8$l?=>gX}u2*s-BB3g zSu=b{k?t)M~@wuqLe2h;o${B?uzUfnjQ(p${X$nhSbmUQ&25 z0L2*@j~G(SFz|FG1VM5-*N6kb)kJ}mX(NoJMKy?45aMwyYX=n<_pcl4Y6r`s5~p=> zK`|tIK|Op~2{6dr?G42hfj8uSnFmE-YH86M61afkpv^G&qG5TK#84EcjaJ-lAt)OV ztQ3OVG0LdNCj(rDCg9}~ zc6nfT6-(U~OHwk&D56|0inWOjDDjJm81#!0M3v!C_5nV?is4{RygVz>(wxlKbF5t! z1!y_HB)^V*BT67v8-*Bw2l2QG17;&AJ8naD1`q%ZuVUH%lQS)FMjLOA|1?YD2y|e4 zyVm+ZSQ{QL047ETqp?^~oS{+I~EPpaY0SE+jOZX^eP8=JcP?9gIs>!0pt9N)bz?Z$rOD-W|mv_88leX8$5>bY(k za^Hrxeod-y&pfr~3e=r?v*g z!=$;fDT(Reb!pp{102%?^OT* diff --git a/toxygen/smileys/default/D83DDCE1.png b/toxygen/smileys/default/D83DDCE1.png index c2a3bc9a825d99df1c84abe7b1d2c99fa7c96c83..b690973784d31932b57084d23f816721c45becf4 100644 GIT binary patch literal 1813 zcmZ`)X;f3^7X73|0usShAXLRh5yVn#;7QepORx|?3J4;o4+S&{qEHYiN?R=ko+7>~ zh>9h(KAs~WR6$cFHIjJ-GmsDxf&>B~0~tUjvDamP_

#_C4o(d*6M}I(MDB?uA`( zkqD>dP5=NzbX0gElwIFn@K2!V7vQoRO7@vyv0(r_zPw~U0|uQ@hocf>0rZJei zi~-djOCRbk{;NmN|CXo%iAbS_A|!Do@(zXove1QMhCmE4U3@W3ASToJPr5iyDa@w! zE{J4{|rE-OG6;&YSisX+vX%HYggqD|_4@E1D z#}>=DlD?-@Zhb4|V?giZ$qQyHmBV}`d&v{AdnDv8wyp%JQ3!j~vC(Rkf7zF23N7&lI+hDWtV^UUn5!DJRIl+PF( zno!a$Q;rx+`U$H*+NT~F?WFUdF`*q06{ixRT}dlQjC;RU@sb&Fa-ntX&Ss~gZO%K? zwmJ{^1?%}plNK}c#+5g9Yks;c>{Y!ZF+s?m+=O^w(rc0VqM7^tytl!t+U6P4wQXYk zh-tyZx3y8c33r)7p22c*)$=!bTv?mMTcS{!$BF$D+)R|6hy~HIE*+fb@D;b zhq^1E?L7TM(%yo^jFYIm{9Hm-HXc<_l!r&1%E<#@^Do!-gk9-J!#l0zAz_Q{Ehj>} z$FJ9~w9aolLXeJ|eOGjFt}pvgb7~osjqT|qI^=*yhm`oMZ zP`HBk=#P6DbsP3{xET&6sTJ8iInTFvB%5R3)SugASDwDC#aZ6QQj)S&&Py5{ZUm&h zRv5`0w3RTs=7bfM=d(||9{&B;pGwafJp64>i7`2S$5vm`oVy?Wg=tNHWqf5s@fQz} zm&^aHqWwot9F zm4oyj3Hs23xL@Y3IS$F!fuQ{3wsoH7bIrxB)>|#1PDk9Aza;za_V*+*PpCZXGN@tM zxZn%hrq;lvmy%x$|5iK~NrHclQeZe)th(?;|3T*3>xE5T#EZueH*dmg8%EO{TzoL) zCBm}24f|Y3u6J&jOR5{X77mC@@A~&=vK+RPu)Wf@ELU~YvK^i0vTw(>*WYW_t_hd+ z$JY9?629G26QsI#q_bSQ<@UqX`(!>?#B$uuPnjMiO~PMOQE7JG@LI>~SJGg_h$zob z-Thcti+nBZT2VBsZp34KWcp5xD=s|s%d&=C*0_UZ(cViG+s`b3_&#N0FjPt$W?l z{YDdUN~$M_*wovGeR)6q%gxpCw$I*+Ef*d|Rh4*KPAblHuQ;8Ox9-_CLH;UXNoiAj zO^YJLJx%&&+F8BZZshhm%a!c>QwuSpE=4irx$ZZe-+v31=_j*V7lfRm8T?JLwF*>$ey2(#8Szo zjb8_oY66M&JZ`|3|J<+W+luLUNcpZ=U?M*@j2Xsnp^snjs>n5|H@7L+MI3RKXlrIG zfTd;%YPQ{1@J|~%XGF+T02YGWuwVZaSNCW9!c{!?^jW(z{>e=i7r3+2Qb)v+%fKld z8^NRn1)@Uxb&`gm3VYDuzP;zJYf2zAy-z{J-U3`k0UmQW9}guEgboS{KyL~N+?W)I z#{44~6CCW1Mq|)uYLNi(KZcxKTo$3|{~Mau7CwdypS|y}JC{&!G9w=k!f?5V@TlmV bj7)qYJ_A>DoQ;S6RRBas#DzEQNGJXUDM87@ literal 1720 zcmbVNX;2eq7+z6OoI>TQ#nN$EC=!JvySY;$kZiyxK@6Z&skkH?2$*c#Y)n9_4k(nv zVXVXhEew~8hzA8DLR(wVR!Wh!VCi_3)&nY91g};d=?1~}hvSd#?C$ry-@ecD-m?X9 zvC(d`=gkHHz)i6PRnhAl$1}@?e$T%@j?s%RB~PN_2`y#8NF0dN5UDt*&|_)13db~A z8#?hY0GO`TB_vTv${4Yl&@(ZI4%4bP(r5q(3%43EbtX=Msd$>sAc02O&Oo3}BY{@& zlx(F@hNtV6WRrM&c5H$=J5w#vK;etQFsqm*(Bl*aTJ`GG=t7_n5sOi3Wx(-$G=jY{Q9VuNWiQFO{!R?NtPnQWF`?}%$c+eE4Gf8BVcwkaXY zh_h6sSb!sCn&;{P8dO1JRb~Rr88&eLoYZZ^HBls?2Pg6?*1f{M5Eikeav57FLlH;ZA^{>7$)fmjm?MA@DGE(uHUB4P zEZP~CqdET5EK^5x2RgPdTb~wQ4i9diCx)a)Bj`Ik2mqXh6li3E^-}5XW$9ikyz|EQ zYsL-+_SW>)*p+i)5>z#G}8U`%j+@ zwxLXY+P&6OB`NO7Us`?+a(%VzAkR6)ljweeizm*wvcykK}2B&ad6C*<4+o9r7z_ z4{D6~s^sSwj=h;{iSDQzb*yOjx@*m02BL~`oPSo#DQJIl_=hh9m z_T1&alNSVTCtY-dwq~e(>%uGbN{5Hs`B0CT5JbxooKzsJ2uynY;G6*w7gj zK*fMO(9<=qy)ypv4qNnr9ghrudDN74p~S^$O?rOQ3GQd`7t@O2;G2kdV#l3lNY!t^ zdCx%DT3X)J!;85BmM1uebUi6udUlQW)}B3fd(AGE_lku!aCi01svLWa*G5C<_TS-A zMnNE>w)xtxOWlew*soOTKi<$<|BjO3E3Yjc;fMmgHrxvr0}LoNUVVE;cXxSxZhuF$4i3Ij_I$sZ zi>4pjYdKQJ*Q}0NUYF6BbY6Cmdb`iReDH^T4bG=q;(XX4PuJ>`Z+lv;-z+#I?Nxdn z9tMy0G!=C%BNLJrWmGmuIlfL?=ARU1w=9fszOp9ez&7uqi2lUl%?HVA%ayqqjh|k( z_S|?-;&EYTv+r18&L{O9U7Mp+ diff --git a/toxygen/smileys/default/D83DDCE2.png b/toxygen/smileys/default/D83DDCE2.png index 4c3be3e309e3f212ed881a6d7e2502e60d247ab1..0ff4ad06ece58d873ea3c4c1870f938e5138fc82 100644 GIT binary patch delta 1687 zcmZ`%c~lbk7XG;xBIHbImJ2?YkxIqXESx4VE74#?MKa64tt3%%$ORRUG)+WNvn(x{ zal>8orKLUBv2rOC&1MRGR%W>%lbK~^KIXso{&?rybMF1_z280OJNGNl>(Ub#u{8hy zHnwnIRnk0*#bW^w=jwi?YN;?H#KQ*<0Mi-(P8tATR4vXs04|~cn4BUzAs!=G(?V_vZcsZ5bp1SLA z?CI2#iu`A?iK>nXB}lKjwkKPPnTA!V=%L*G9UQ9;F5-R?vLW75wb0Xarb}@2Nq6M0 z=bQk$ZwwaiBYURW7&|0E@TmPIa2uzIuDuG76KvqR}BHS6mI!W*$~Uq4t@jt#UG zEACRKZy{cmm`w>SC(CTw;vi3NSv|Xs5J%~(t>xF2Q;YM@b+mBnZ_r+f1E1fv_;>Xl zS)+6Rb?X9uP)r5QEQ6J0{;DFizXH$y#q~-Qc4hM7Un5?U3i~pers5UrzPX9ec}&H?WZq1&8LV7OS4OJ&d+Av zQC|9pJ|yitQDri393_)u+5Z}Il&eTN{$1Y&RcF+Ky^gu7hwCPXs(=myhd-h-qXUCj zL7S(iv;k0OG5t<3e5mn=nAqs>h{!MmBQYinkwA|DU_ZZ*-rF^2g*AJUNdp zi)u4hUt2iprAu$6-Epo#26ssMnR`;<#Sel59Uq!(CcV$ggvrb24z}BknwWj@M3t5= z$(5byQgSATHmoy05*B~IwuRwiu-#%6 z=x>a<&6ruOagRA25B#?7D1sYZ$!X128YI;w`R7lRJt3rBtzBRf2v*uR4-Zq? zM;4Bq(k(Aq9T*~oY(&vAY{_>eSK%y!*W)uOqlg}Pky{zD)wtksc=f`%MQH0Oyx;fJ zqNqx_X1oh@a{T$CgM+6*+#BBv?TT{T-QR`tzgQVQB6oYpAod>Hx+6DTqUDu}FznRa zJDcWiN=-2?6WTj-EMjn>5FaG&hYJdgM3#jaN3rSDH@MM&Xz}vP?S_Pq4}Hq^Yh%~E z#W+4xnp`6@xfu)>NniZr!W3@`W7%4Au4;&F$@v;^`-Do%dF%jhBb^&N4fHOgJe%f@ zEo?uKn2>GK`N+jn?MC*3P2~3v+3@!KOBPFp4_V`Zt2I*uX41l&y|;Jb?oCEKp{_N2 zZf1RjFm^Lee--SgDo{N7W{>OwxAe2vh2eb?l|v_R?pVL=VB79Iz23ghv|)5r3@F)#P6yAalkir4dR&9>N>BemED6afqZ+pMRX zo0*-TS6D!%c>87LT;cP6_BifIz~Q~f;oifKh6kSvJRY*Q*|W8poh`t9t-ugndl;IVg-K@MOtWMC`^xAhl}0PZkrRwi zDkBUX5*wyUU@vO#UR#ult%H-F0}AcvjCOX~jY6SOs4!Unz5gN5qeCOY6aPP9tFL;B TR-X9}5CCvT2<{DT6z+cj;9>{3 literal 1683 zcmbVNX;2eq7!G&16!8KFbqOLz&9MtPOOPZRBq}KbnF50cOR^yml3kL81VjZWd@R#4CuMd}SygxV@rygEflwF)W}wa^WM?GMKv-Pzsmd%u03=e=i(V&u!* zUFNw^C=_>D6dX&g9@dBINWK@o7)Qv(n~)|DahRGgAqJEpQei15Ak!hKXe^4TvNNus zVHAp;TB}GP5~5cMl$efzSald?9ZsStl(3~{98spB1dxKJYV|_ev+rAIfL0}>#j~SX z(YOTFXrporXk3n5q0C8B@>R5@;Xs&KKoaOs0s+jrbiGkv7Sbko1?1j(%%lMmDny!) z_Rgt<=omnP8Bl=D2%#%kAP8_c3^0_<;ebm3hy_AS7I|~%V5or25kOpE@}iN}3@WuC z7LJ^>MM^@Nh9GbOlW8)U7^V;gW=Lg%d_LdG0YP+9gKo^$6Ns6vH~LL6z^GAa(Bgy^ z(*ssUBn8VPgf!CAcOmHT=;-&vdgElG$dob72+jlNNW`N5y7576qaqte znX#x5%QPs-dZ_)Tz+~<|Zpf-gdL#JEpe2ifq{EmpQ-|sa87!ocUl=N_N&tZ%2j+=6 zks`iCN(y2LixUY+cx*_*f+D%>DUJ_e#Zs}9#TBt(2xfzz6cR^@Af5Q+Bna-AQcdN3jBl0YwPRpun*7sSb$IU<78=U^pO&;{bv2TD=N08H23l zc^55=8njucD$;=IfQkGHv>&j~1NkhmRKns(V8|Lbp9@L(l1Ppe4B>*17^Y2PRsSbv zOwt*qwK@LNEK^5h2U@r9Tb~r(4-cv*C&oaI#=e*97!-=_1sN<-nC~3k-Mb<#!|O`R zvZe-lQ^%gOI3ej(H-=sd+^r90)gKIG*1K*$zdT3UUpebSrMQ23;5K0MN=qeFy|phX z%BxJC=(oR}VCsK5vw!$ePfykzh8tLO#OWM&>4NaE`;SM*sCUujHg3p{M|F7Z8vB-) zi!QxlG2F*ac;*$Kp2s+uihVj5p$fl6+i5A<8&ILF8rWn9Jeu=N~R*Yk_@H!W`~EAq>el3wn( z^W=V_#xw3_*IluHY)E&B?`o%P$K=tJ{M_;Rn)b9^wX=fb;+4pTq=L<>+HETwN4!kW zN-bsC+{G=94~yGoxK%$;t!7v5^FTNbCr?Z_7EWup)r5P?jus`uXNfJxe!JuwUz2hS zIu*6`-gRfqw9gMI&Nvk2i(s`AMeTDq!nP*ZwWfH(-mYQO>*oFITjx3tW`8@r(Ux+} zu?YS$sG!{W;G*aSBeU&g5VhwlwWnh+h=(_4TrAC|c66TS4v1sR9CR61J@fibTa=)@ zw6uQP@Z4P`yI15lc8U&B3w%pk=Wh4#m*Uq)lPjkEV5!)$DyV?lW(xS~bZ=wC?S#Th zhl+jrp>(f5*1I-z*=uJwB+6`>OH{f2^IG5RbY52GlnNBbT_~vYF7I7iUiRQ}b7rf@ z(BawCzvUVz(cVFQBgOygD;-`NtG@WrWz&`j2RrRm4^evzt7{u{khPuHp z+sjYkt0VL4_{`g^qOr#t%1%|X+X4u8-HN8na+~m?(S{M;uS4E5XI|}W{|=qsHxwkQ zi_vitAFf&C8^3J{QC4$iIA=XFOH^X@$zD*g+Gg4AAj~%l4nC1*dADX?uOqz>u*9PmBV%7?jY@fB^vH zF-~YVF)A#dl~UqpWV(tV2I*j1ye$BA8M0r<5HUwkoZRpL95e(VE)jswVkzzo0Bj@x z6C?mo1pxfUD89Pe3IK_ER~*sd`|?_hick8#JHlCyY6kVum#mDod)8L;i#=?Ia(@}A zHd_6kIt#CpN7WIgnEax%UG=+CP3mGaek7+eEW3G|8;)q7p(*^dR~Hhl#TpAUt*V#? zKgG6sN(0C8C%s+fn(qsW{3L%W;!91EWeN@?;{gW;Vt0b1-4YxONF^@k@LAilh;rFv zqcf4)6P>}a-4cne5R|q0gDheBDl_bHNd#Ml0mP0Z;q!c>eWVnSu{XIq=mp^THJ4aDs5*3PWG* zF3!mN>s;9`n6#4gcrYCyE+i3vbwq;{$nQ>KvVw)dJFIxM0uD2fHHl+a03aEAkm$j3 zBS#?^%&?#k8Wq8cVNem#bOr#HyxcwbffGo%r|(p(6S4xkw34N-y*uPPzj)H@Q)yzO zf151Sb0Dp}F6;F0QK7}RB8$4_k2KE!7}vXxWm>vhkCNW&-hXj8w5b}lq~(moU%25l zvze=T*!#{4)BM}5`>!mzTO)N#)YSxDO?d9J`XRn)Ez&)qz5Lizc!nu2!cMw&=FJ$n z$7Y7P2KSsyY7Rek(|wy|?AadTRf2m8*2$h0ME z^x$ZtTqk6`V4U?<&l)1TNMtWqR-D~vSm=lC+(WZPS$DcLCFX~6!X;xa6%KM!U)-1k zq@27rtBm>bArkR(H|qi9GK}|Efn!Awai#XXPX*GDcd4!Xe!oYQ4IU)_`xC3~N-+*K zVWtxTI*+q7EL9ETW&_YIABP(38Q7Y>ApwG^`t{x|!EH2i`mJ34mf8ErcooOP1LJ?x z9bMbd)~z~eK2~PKtv@h#BSc|jQjYAo&bzTIc%f8uL{WF?5uT_#wr@1FSCwL^G4+al zI-I>rE%GIS1KpE^xzb+p&OBRo%$O8cu|o^4m7+Sj`1<6z9Wyc}2|g`VaCc>$(JF5K_ap+`L z`sA?EC&=8=ae>iDk(YQlJ1}Tl;_8PKQVU4K-7&$XDF8(hmGXLI%x)!PZhCe`?*d6p zK}AVTQB^q^=H#>s(u#x+80SZ3RD^DKroNsww`&xe{g9QhBZC_)%4!xx72-pqUiC}&q*DIE1-^R zsloZL@&+2`)9l6F+yD|XDkGg7 zh>k>Q08rMqE_Zfq4pjUi1D1maE0D~hq9{zN7{Es4#*L;(3sbXAL^CAHd^2jZxiJ!n zLLy59OBVk!(8B^lf@1#LaQ}lnS8PyUu0RM2VnvaeRA3tz7C=Q{=;UCk8}SRoN%HJgt;?@>GZH|65G&5I&GU4%Fzx>71VP!v;lIpNk{`N=p5)2 z@YJD#IvjZDoZ!ThP3K@I3Y!CTqEp9C4^+_Ghl<~C7mncD>E~K)Vf`{ZOK;vLDDA4F4vnUHi zI`VTmsCWPf%%rX9Lb@p#x3exO>C=&VU7UypfcV8;j@b3W7$!yJ3JgOQfGCWhGFZGZ2_nZ87>>q)V;3Y^;~kl} zg)ofS5+ya1EeISglX*NIsb`^-<+EgnN~Q8~pr}ODkht?1f%HlkcZ{Ebpxkzz<^-B$ zKp!KS!R88TNc8kY2rkZKdP&T<#}Xx`Oy(sy86t&cE|)K^QEj(iq5gH_mD+A=K1azc zl$*`v?P5JLWBgz-cds|(Qxv_y-{WbqDCAm#wdcAhMlcd;Nc z(~E*u2V(|Qr&ORi7&XKx{2Z^sYU40DDko3^)e;DzM>RTwN~hFoR2oAZp;amUSR>;W zNXAb2?b4#%F)Z?0EUx1zQeb&2%dYizKuR_%u|;u~P7AA4S|x${;?`)iN|i=KXyq!c=sO0DVIBV`XEM*bEm#r@fFNcR>#EId>(Ks1l@d7{qZ6q{S@6FG4uTI>(bVl2OuNp6V)}8MC z{L!KA=ZTW7^vQ@fa+gNVX=!>0HBAft2o6>Zv~Jx9mq?pRm}lkVFYc?nviTScMNY48 zYCnZJJ3shl@aFqHw_NHY%M;~O*Y~!KaLUUEM)GexF4NWk&ZiePbfCh}nmv7eGg@PY zWP`bxNqaknj}ftXAA{E&aErpnmo<`V}ZW-pqu{mvPr ztE)9^W_=pJNj*_FoE7&VDO%)9Iml>&b}qknye#SK8%}-Iy8is16K-##=EDm7s{M<3 z6*hZA&Rn8;fAer%O||-gP}yRgQxq_3%Y|RNLYLhfFv^kPi-kYzj~%q_DLG%9*Z67B znM;$CDgvet%!Iox*?K$&J8ENx;_uh57&%=O^>++*uj;3xo=^I>pIf;AB(I;lF9&yo zjdNCP>R*ypwUiy)_GH;FSNc}Hg-=k_66;Hfj-Kl1K4^=YGC!(4Btu?XSk6XYO$`L^ z_SkYB*6j&@axF3~Z%t}aDD&*isOU4bYbMXFFLij^rElA2w-g=Dna16=m-RMlHVi8` zS~hq0zLOP|gUWX%T`z2=j14=3c5VC3JA!_{_`}W0gmVq2B6jueh0i8}Q@1V=unCmq z&fU^|P=vXlt_AusEErmJsHSl!9O`+rZg|`bdtiKH{}v8BVFmAmhrrdB3RiWOSs$AE z8p^|~4h1bdKT&SZnq6MDI<$F!nN}yEws zVB1deUUJ*v;BiknN2>q&V`6?mSo_Mhr^P@ZP`vrt#*EV6D}6t6quxv$*V;Dz0}`oK Ahyn2}M- zfQ(!N^=WCf4o=qyX8oA5&b5eo;S5(ODq_oA3q%jQWE{beXk2=^QF^rly3K)#`Oqs7 z)G`V+ilCA{=q?w^c>QH)T=IQpHi9WmXqY?yW-k8&^jrv44M63ep!+@0ai$n{0o%;u zEaq~dA|71JmzI`b%Ysn}>^gQH z*A36nh)O)XJo0}pWID1ISzB9YZcIP(fo*O|PwM9G*cMy`_s9i1AH;!l1w$veP^1!E z@QNdfD#)!iWIRp?z^bG~Pai6g6pxCDjSh{7q@bt?F%%RnDh2@SD>uFTD{|257JAI8 zRvWRhd{q#$C8N{Kwl5ik*vb^X@S-L<>rZxwxoGD7vB?YdBQ6ss_W3ii8wYi)0-eLT zE!iPVuQ>4w{?jK1?9x1loW~{JXW9qX@O7B9Hsi7~za4d6svBAw@*gxwKSr{2 zc`xD=BuP4nIVI)kn3uU&ve%wnf$FYh zEc(way3bgBWRfn#zm!VyCHPmc!e$yK z&T87uTf2}oKZg&TXxDxpV<~JG7oUtH>ckE`K7YXsTWA~HFWZ}?BytXN^K+|3Bq>xI zK3Q`sE>FWyWZ>thWv{~fn&%ntUC(C?1`$z6E{%~XCoI|90lvD=lB!-FIUaxNu3`~p zZezUvu%!m_qUR>Z-QKAjIbn=bd3S9zcTjD7!Nv(($CAX>1UcK9AeTc7`5U)poe?4* z`OvE(jkA%Z_Z-E(X<__FSXy|!szHbGJFVZ83K#@2Vy-%B|5ukf=a-tFHZlX|PhD$~ z74FTEcziQHJFmqv@eDk3&8&J?7kklvOA-*iyOf;l85jr+dYu(YG$df~%?_{7tD{vN}>=(UGSlfqNY@`xB3JrFSJ2*BmGX_!hQ?@bLyA({v?~RhW(+p! z8rmKUcHLJ4>Dh6TEDmhW)$f2E7*p{+R5FQ5u?dN#zzJZ`7>osakHt<)&z)$ST~;=h zySJm!HfXfnk6e}i5Tc^V5upkHPjC&5d5 literal 1466 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLC_|yVm9RtDo<={rIlG@M9HY>(6QX z-k&`az4l^8!h$txBU7$>#w*YN{!#oQo6wqdULWLMGQDKKD$` ze0%>9i$8&{tY#G4jWdY)Xm?fbOX-Jaj~~d|)SE39lAb+(%e_CBUPNALG~sviTmGW> zLga&Kr%x@fd9VA}X~6;xsX6v*j&8o86|w43>tl`ocQ2YgwU_-qf#+2J$=cN%I|TYy z>9Fm;b}w-2E6x3%?<}@*?hth?&@Z-`Y9O@zytd4|yLrD;_g%=_@%T@ADaYZ=+Ff!j z!Xk1~b8hXgI_k9XhxF&k<`epumDMKrGN$oxSQG^IU*T<7l$@M*=8)&h=`xR}p0kWT znWj@yYH?1R@!TASsT15dlp4M)op;00xIV}DiBYD9j3&>qA9JV7Jo4=ClC0w2n;lMG zpSXzk>PqHb?ltFCqi#N2rg|r(nB9cQFL(Xx-n8|zEB^OB+^svo`{j=6;Mc1s?htEa z$Y!0ss9S2@9ixexAMN;Hd8&@rN8FYr&_`cMplyw|9tLx^ml~ZD?kIsuQ%_evmvv4FO#t?d9kT!c diff --git a/toxygen/smileys/default/D83DDCE5.png b/toxygen/smileys/default/D83DDCE5.png index 6a731d10b07cb3a4316ac085b9cd26f2f91365c0..3e3c17254c8470ce7b3e070d3b9a6976d47cb643 100644 GIT binary patch delta 1428 zcmZ{idpy&77{`BeiCofUVC=#aVNG@}!u*FbFJ_xh-{cQqfH* z!gQfY+p%)oYPKQUIGcqyrqW0*zw;}9oj=Y$&+~eo*XRBEzTVI4%hIgVEYJg$0RS+w z)bWR#N;J+J2LLtsnsZ^Q@QjY|@bdaP!@QOMD07+N?7!3sg`(gmlC6zSz zIsgCyz26J(20>7d^kaG}#Fq?r4GeKbgPnbnwx0eLzL3N1VRi6dwsK#*d0Wrvc*<^_ zwy}XT;9_nux|m)B78w|W2m~lN81nUnTwE3z0be+}*XyH^QHw-DU_vR?6Ys2cijc@w z=~t%s+aZP`u~fv|;nUPV&F`0$a}_?fW%s)jJn^7xRG!0@=XF9?c~F%QdL@Hez+s3j zgC2{Z5X@ZA&%Bq~b#$;OHe-E#}y23ayavmqRA& z+m#B`z@ z;OA7Ws7G(_)%YE*NH*_ly#MOid#}E<6S0q^Iq?D7DJn>oX_Q{=i9*3OOuv>vrCPCf zuvC0N7|eD*OO55b%M9D%?L^j>^09%ZnwL2ylOA}VvTQCCeF~f>kOrXK=jB3ro@Q`K+Xrbh(^TYYu zGIfbpYm_PU?NvsrUV{UOg2J5JJ6T!Vic~YI!A>Rf(tsEhVTo5qsQS9iFT7RQ=hJ&z zj%}C{<)8z19Gbt9s1y(W8n>QFjU6*9H!NSNN(~XMsBUUV*4R{KOHMY-klzB=IuwnV zEpswr*ApCLoeRQW>x9TPW>b83V8oJ_&Q3EyHVs=RGM69@~S;FS28CVICs0M zY7|)Xs+uab>ysGgBfIQ2ebDl{fT9%@+)+KD(55WxazeD}6)))Mm6pk~hhk7A`W&6# z?(!myky;N5_tAY!?zyaPr?M;Mr!_5>8NPjkQ&PX4RY{?1rKYkHJ!(sen7v2LO0Csm zl9tOyBGRFvN>FEANSuyAm&@lroVNUq_1~r89*6AEl}$XGe`j#|AW65;F?xG`54vpD zGcie6F0J`KWnPfWCQsrjxYse6)5N9oh#rURqTRx+C*dzz@9B!e|H2Ds5v-(zBF}M+ zl2cZ#&pIczd>WoIyFirW=ULZono46JoJc4&JsMgDs(I3z@M-&z3T@TUEL-E6TYei6 z3@*7iEa=KoUftolO6@Bo&tov_auR~s4-O~~?fK$xb5HrZD3QK)YHj_TPuYlo_84D< zKdx|R2&%{pXV$7@gn(F7?gkzXG*wZcI}14pPx?I!^+R8{Ol1yAj5*!v!c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#mPG?$x4i3sEMqmd~kO=+KVILr7?J9kFjes^MiKKe-i&WV^7N#;vZ6Rt4oGF8Z|*pa){c0t7E340SBtf^YY~QkE)Jyr7&T8MqZ?dPXVarmv$hZ^#w@3s%R!fo5T-5VXd)KrBzt*~} zKeIia^;93%T(>tttX$$1vwF*K6hHJTocTd?1*=SS^Zs|YrnK?@=3$@a;1yo@dDExq z{XMT$1QKK~h=y+2V6<2<^+-UW#VS8;AJKZ1jK?b$EdNkz=5XmvLh$~wz5Ra=S}Q!R k(yh_|bKw7}d`32gBU?YP_jnxH1S(WLUHx3vIVCg!0OS80T>t<8 diff --git a/toxygen/smileys/default/D83DDCE6.png b/toxygen/smileys/default/D83DDCE6.png index 4d3f70114211c0311f1b1ec7c01313edfe638796..f0872310566cc68f0355b5608eebcc2fd7fb7975 100644 GIT binary patch delta 1819 zcmV+$2juvi4Z;qP8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0#Z;+R7JRiRrToByogx5h*`~< zYxU^X_2|~opmxulaN4YZ*{XleoNmgNXz0$f@7u-p=hgG#&41sqh0vaH&6;e^n{Cvh zci*vw;O4CPP~Lr zuXjVRc}TW`Q?`Oqwtr5>lxMGaMz(-a;IoME(zNf#qVdL`@57wuyOQX-k?zK#^x3}l z=hXJ+)%D%Q^ncK=^2ea?#hvQDmDZ%BAzjqVve1^UJ05*t_-R()H-p_T$d= z*17Y_rSiw2>AjTAmuK3jdg{52@Wh?=&#d;(tMtpI^2ww0)wlKJ&+5yq+@*ENjalip zis`qD?ZB4v%cu9yuJp{O^vR?3%%}6qsO-k19&dJwu$Jr zi0ZnI@x`Cyw20N5XVH{i;?K9;yq(L9R>Fo**{65swTI`lhUc_}=Cg(7vxMcdgXOV; z(Uo4%lU>e{Tid*x%#~ush*7?ROxL4xSf8wux;;ww*t$N_BdF8o}&yrls zk6Fr$Re#5cQpAQ&!Glb@eo57yY~ZVR;Hq}tsde6{bKa+N-KBBdq;TQ2i_4B!$ct3P zhfu+Jv4+EiPr!mqzkf-*enz-^ zMY4BAzJ^rHlwH@HW7wQz+O2`YgigMGN4k7QvwwI)%8^^unPJwNVb-X8z<^4-en+)= zM9Gj^(xZ5{dPV>L|HgEg#{d8TFLY8)Qvd(}038M~7;)0gUk4Al{{HBK87+?K`K32M zy3$BCvHs>_0xzol^Lq>_qx+c|%@|D-00001VoOIv#bXOAcK`qY2XskIMF-{w4hkg+ z!henwi2wisetJ|`bW&k=AaHVTW@&6?Aar?fWgvKMZ~y=}jg?hPmYgsQ-1`(cf{|p& z9EZkum-n*F@l$2G8HQ$(3A}EDEUTo_hxGgBuk?dgwVHI*9yPL0uKw^6CfUV{htIxg z6WRLX@#rFu&u)~I&|*!qwaBK>tv)BKv3~>(7LCQWVvZFCRdGl}yKa(ojjBsj$3brd z^GPz_ef2Grdx;3}#=O%HUG|MbeR%R=2C@fF*l|K~sn$wPLg#u6krvrhrNN)9lLA!{ zFJzON7Cu9^LMmflq+{LxJxLpC%TQdP5|S^sJi`-s0i7ad=9kbUjZKDE!*FL^5PwdO zamg0Yl5Kb#K%R8weRv}HZ#w`y3Jv6kj6_9PK;ZsoFW>@Fg+z$YgQ*z|LF2XWQvzW< zKrFr5i9P3n1Y1L@-+Uf`+eexbUO=`tgj3;DW&ofUD-5<*)=tJXtgy~5grqy`UmIs7 z27-f5r@7D(kGLk3;;>I%?gIOFP=7mYy2E{$eDbpxvo`Nd0|TqkghpZ6GEFv{jmq8+ z!Lp5RVl|;0)y=e;V0A;{O|9%ZCdQJ5T=`T{gOY|w*kY*bt0yE5u`BY5v(z>g_qprN0JA zx}P*xwS6ic(zj26Zi!nrAAh96?2K?^|CXdbs&I;gj2xv=00034Nkl05UK#GA%GSEip7yF)%taH99piD=;uRFfaz*@PhyV03~!q zSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536*Gc`IjGAl4JItDN>cY{bjks&7v002ov JPDHLkV1k08l|29e literal 1692 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLrkJOs{8NaYu_h>bAii};Ey^rQO>ryA&s6}2uT>@%`z;-v%nTi!T`Wu;jY0l) zG<7z1Hn(&#wsbTxb#--7g6d5nC(QJLj?o7t4y42a69T3{5EGvCfgE_|NzDW1m?B`- znB;dnkb!~ejHioZNX4y^$)~fg1c)5lKYPhZo8M9^H2S#3LsuL*d`|@+P>eJ8O+21kw`n!kq*SBx)?_zh)`}6bqc7OkOhi-g4ry?CSXXYvo!Jf40 zs^eVMU5A(U>!;tA?k$vK(Qnhc^JzOb&*2Nn$Mycd+W6`3`ZY1{XZ~HT?`)#&v-SEy zO-9WlhU?cGJl*;2@che@g`3*@`RDDAQ#8Es=E>Q_%cpu$tE<*6xqOq+_&WRM4dPMX z84@L@+-}(vu4t|6p(*e~&StypjkV8a_uZ9A=RTjm`tEN&hxeBbYA^(N=2~6+%$%}Y z$o>n*7PgmH7O=P<-x~PKkpFA~*FtA`CjMPhJ9{TGh%A|v+HpxHq?WVGaKV0#y%iGk zCN{^$GL@T8Qqwy1=630(Qi+#3XL96wLgq=>Ni;?*=X|;CLHm@OCzJ|iZFrqpAn3;6 z$+9)lBGmcH36sMut_#-r$h|(@oe^5!TrK-$u0*e@4UZ;c`$7+$P^M0Xk4~!F4$1YwDJL^==*va*Qf>S@_M4b_SZPHTt$YW!^CjPKn;hi@%jxeM^4a z#ITTGZpTTpq}z8U`OJ%)b$n*agTj;1k201_O6tifWo_LcK6A>}pZTgYBe$5Y>MGPS z<>8pb5z4Y)em8e(1M{r&PgiD@eyz>i*~Pn?)lpPa$kjHWFzl79M5v&Xn;oMSqh$K$ zSyQc+U;N6rtMc08S3jcGeqFaGC|vrj-=2I|-|k(qUpw6o1uT&5=(-df9=!9Na@)*V z7q{`THtsVxEcLOqY|FI`@!{8`E|smczxbwEpzplwtY5NL+RbTJpQjkACEq=#yejk8 zoV1APOe>q}rO2?+IG7>m54hy!}|uD#0*!>aBbH_Q}$q`oh!I&t;uc GLK6UbYJfEW diff --git a/toxygen/smileys/default/D83DDCE7.png b/toxygen/smileys/default/D83DDCE7.png index 5bd245493b8155f97f5788ad8cb1e9647bb2ee0f..6855487a6e8cd5742aedbd6f7087e9a0ef419a74 100644 GIT binary patch delta 1300 zcmV+v1?&3u3cL!C8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0MbxQR7L;)|HsqX$J5%y(b%cA zy`{6erL()o)7t#}{r~^}!_U^#+uqXG+V%DHM1%jp%+vb&{D0Ef-Otw9^7HdQfB%@a z`@PA~_xk(w_xaG++|Je6@A2|Edi#{K__@W-_W1em^!Coy+RW70F?98iuk`Eh^0vat z^!E4b@bcK;00(qZ zPE!DP;NakQ;M9JMF8}}l0b)x>L;#2d9Y_EG00(qQO+^Rh2M!7)22wEz7ytkQdU{k? zbW&k=AaHVTW@&6?Aar?fWgvKMZ~y=}jg?hflAJIM{D0Reas(sEk~t1D;J;MmGsjQ2 znVDr7HrYTG7)e&^(K7P;=P&uet9cW$YjJdp29L1vbV?u$Z*)JMOWiQ71$| zG#;IC*nhGsUgjf`4>yqAxWivNwAAKnEusvr=MY)d%}iIsQ*1Jz8uEo~GPlY#WOqn) z=v6!3`!ht>VYUpz1tzKGbn7EMMHH~9>h5t$O=T#VUQ5SaY(Y9b$E91sO1BYl0(r8T z_bU>`bKLP|x_s1UadvIqhXxMrP8XJyv}K!ob|1{%H^H-yX(F3IM+FZAY{w!hP=H*GiWzY= z@VNpTkz(!{tsa48Ad`?rN(Kv8-;n1IQ0Lbm0Ynakt$$;Z0(khqnv^UDCNJ@q63C|-u+7l9j%qv- zHGQ?6H)POotJlJ!QT-QgnKRw2-*1%#YsvJ$J6<8plK3EaJ}6r zLysrlVDx^w!YiYBQM^9|m(%P8B7ZBZs`_#~B@>||{c5C6^VvUjvKgvjNZ^V9001R) zMObuXVRU6WV{&C-bY%cCFfuYNFgGnRG*mG#Ix{soH8CqNFgh?WH#X`X0000bbVXQn zWMOn=I&E)cX=ZrTz#wn literal 1398 zcmbVMeM}o=96nbyb6a&Y!vqjGj$lC7-Usc`o)p&hdeE>!2e?+Ypx5h_9<;rBz0y)8 zCNslm24gS?lelbb;-*LsAp671-Mt_8 zJkRg*y;q!7mUeH+bifAFm6>iw_Gdw5fF!7yM$P)S)l%yP}V414%SA!I_XkAIB7 zg4cw&^bX7sw(@?VA|dg$iK;p-(aagWNLeXd5~qMbke69F9t?ycRNRCt>QZ2?Y@-Og z2$7pj$dXfThZDAn5)bROMH&vnahN2vxK2-!_!gMJa011^O=@r*r6(z(7+$^*U`_J+ zs2ZACwgpxu#4pQX3PoeFm^N0V6{SWLHyVwK20>^5LKA5X$!uH`isYpfXgplL{J(CjijLH^hIzDx zkBCu;1NHFbrN|(6cQ>Q}fj3mOB!Hr@0b1muK|UngX%hmzXuX1$B5<6f4Hl9y8?835 zuvjsYA*=>HVZ{ihSfA2Zjf)wJtqh|pq5&4iZG^>EWHnk0v`vqb7(){&u00fy*$~I4 z>wm=6e z)0Nf-idGh zdfTM`LYeLq(8U!fX!oj&3B?myeLmq?&!`w%xq8eqa8*flROJ(3RYQsxO5NvqSS6su!4%H8YpfI>}7thZ56VcW!gW z1T>mh)3^EBu_rq^W#@2o>#O?2FZoWn{r6`V(Q?qH5M*NzNd3>XcD*t_rll#VwxW#y!go+2LlDBwC;zd#!ddk^k<#c@&avy#W$P>yS}2o zr?GR)_08Uxp_Dy;=5mRcWq9oM-!3^+x}-MQsV;AsQI|K(VG~{I`9rR4RcEnMYWG!F zZFjPwLDf=ywSO0y8ND$#UD-YyjVH+IHv@MTF3snifo#5p@qy!qgUX-KZmXh4EYI%# E2X%VxY5)KL diff --git a/toxygen/smileys/default/D83DDCE8.png b/toxygen/smileys/default/D83DDCE8.png index 446ff9796fc65e3f261b86905b5ffc0eeab4dd10..8b185e7f52fa9e72829458847948ec67aa809111 100644 GIT binary patch delta 1323 zcmah{c~H`M6#q(+rM0Fdna4)vnNg^Cgsw-TD;nC8noU}m_ZCS;qv;x2nU$H8+k~sq z;Z16$MVex!+g)Oc2%-if=9PGZDC++9kNsn3|JXO5d7t^bH=mjJ-n>3dz9xMeZ4Cgx z5~Y0zBJ~&#f(HOka7AdSYx&f* zxTNY7_H+5dnb}#+`wqq8(!4_1A?R!G7EVsjG`wz?FDR$x9`CUOUk)n2<^9pO7w?rWVOrMl6vk;+pE@>Es0{WI8l6J_xejv zU+sk7&p5SJ7-|3#8dN;NOXHiy51MA`O>VIm0AQ)ffgx04cp{RL5FZ_PDhf$GPl-Y% zk!chFoU-o(;ctu38=7YI&conoVBScm8(;Ckh8y4*nS!Y`&kG4Mho{Bv-pEp|C?BcB zkB{z6?$P;)6;OR)7Gd7~IzN-!Ji4nkTQuQ-L*DDRjC=4oxQgvqS4$Pk}IAz)c7@3tE769+82iQn+G4Olk#Frx1UBfPG9Eu z`;hzj!CB~f9ji|Bd{*Vvvaw?KAgPwHxHLQEos8}~=Czi;{57jGj$JH)<bTAp$r7aJXT5t1XLD^Y&Ox3;SVryp4re{-yNd)-9sL#a%jwqF|PL zgPlU}g*iL*)Oa%sHHpacE{#SAjL*GiPA))7$hn1D? zP5q?Qn^KrkH|YdU&n1ZBY zwsZ}}8FqKobFHPYzr$!yj4^jpRiE4!wqEZp&u8c7u%QVssZSgw(F>`weo394 zV0r2zK0z#jrx0s@WHc=t_dV#^okqUW&OnqJo#bL&MD3XwjI%KU!>W2~C-IR>J7*F_ z7sN-EdnEq9rSuKiGPmLzn&aA|m#P+SH>YY+tpme;qZ>5lj=oz#Sx3T2EVhN7RzXM4WaSR$1@57R&-u1d$fMd zp}WY`9x_~x=UH6aNo|uB`nB%)bD-?L#Rau1O5|FTd@mHVfEm>*gc=!6jlxDGL_q{> z(YCfIv?Iy}6KI3R+Sz06(bi}*7LC5;Az}T?K#q@$i$4F40r6jl*ysr2)(#6z0PtS^ Kxa$3+i+=&|f_&=$ literal 1372 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 d7_zq;lWtFUqXqjtqazLMS$plTq(e= z5MM2L1U}GEHzL>|D4|O9m1?E#=P17%((^GLeKZsXXb7_9MZ>(mM$q6&p<0ye$ZsDG zA+Q**Bb&@_)E#DppqSsN2;Rm*pTDujPX>^@XJA{D0t6vJ_t zK;T&wt;UYbIaTT|hFMt=V6)z+^P?CB6NDZ!nF#`05926?8&Gf)I?P0w2@1Es^A`fF zDS;}=!#U?|0m+U8H7!gT43S7gA2I4>rP_dzBpKttaUIamsST3GM|G0ACeFYKs$UVq znkY+fjFGRD>oq$9Je><66n488h$VGCQ6Oc8C?7UpdejgK#p0UPRyB|CuN#YMtGn9|Bvs=j zzYw=80=x5A<3q6&s|dU%D?VASjdwtCP}XEMD2HLzOTf9CL@6Lg)OE4)%tgxyiuj@s za4K>Lp3N^ME@B^eZ9%OTnjvUwEN$ zR${Qvk*aH)X?n0>bX7|R&7Dm`f@2@RBilJHl+fM&n8({!J~5X0UQ1^Uw{-cH!w09k z~{yZ2M; zl6&hfBjvdjO!8!)nR8tVlDh7Z!UCK9Hz6I_MN&+ zK$o7qm{%mXIiDUZaUR^7(?nrM4-CWInck7~^lQI%pe6RwizgC&9eDQX&o5l996sSK zfr^Tg+V5xeyzZ~-d*%M{$2a?4%AFj72kx{SY4~${|6f}^J*Ix6o_1BHn689~)hmB? zOyol&pRDM=y>@2VFppP+hj(2KrYi6D-e@+ycDJJI__Y^^3wQcIs={g>*_MPt5>J`Z>1pj<{8|K}SXzh?X!bbN-&DX5x*yI-Mt9!~edYsEAk4L4rk;6LDjSOO-Q(h@8TOF87je}1R97dp_iCCrBWzXby43*axLAf4 zpU#RR^1{RX4|6$0ehiO8j1J)eSf=NBQmcE(*xrviR>$#J>CioK=hjc0^FtN5S;@T% z(u$d$MMS~H6EE66*@`R{YG*D*>9=40A|15|W6=*jh?yHJtTYtg>kHk5k$0!(tmxr( z1O+oZx*|(X&v>Er3ZMHPFm2S=^b}GZQ$p!81_#f6$Vji#ZrN}xe)fLY#W%|hiG-=g zmi+GJ+osZ_rWu|Y9wI4_H zp|sosOKopZ_+=`X(S%I&no`fe6LUp*1@3FSY-V;Y*WqGtFRp%mVi5NjEf(xccvh4s zzVE8CZDaLKO2yi85r*c|JHR^{IWF;jN{ETvOsSDC1dOnKiB?_sT8-BDXH|&;SxH3A z?y-LA_^n~wtW54+($G~EV{69aS=7D?EF+28cYZU8>&LX^SEO>$`W>x*7D^^gJuloZ zHec^7a;S_Sm6Yb{sAuX%AI*@W=W)7B%V1nW=??N@h2~k3d+IwO_RX?N-=}zzUlS#x z-YA<&F*3SvGI({v)=p>Anfzt)mEvR41M`~)+~VU}!dJD~((7yE#9UB~dFP#_|P)ED{g!g&3v+kJB0t%Hxlr%JJ>HMTkSfOLEbg4Sp z-W%`UzFm04eNtUZtD!Xut4d9!)i2_kW7k!bl}{c=F^IpcH9frqUyAOws)?bYuz81Q zUKkb%?pti_nvU2KfM_z}J9+WhEI!BDFPwuIFd>_mkjNIK?PiSaWNTA%YjYE0GTE9; zR(QLJzd*t5fYIYep*D}Y1iu-Gw2nmNdS O3_x{qbF6e=9{(5I7Lhmr literal 1444 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 II;Vst01;6!KmY&$ diff --git a/toxygen/smileys/default/D83DDCED.png b/toxygen/smileys/default/D83DDCED.png index 20847402f1a822a93b4c1f8ea6611bc6dadc3ba3..0e50de2d7387a24f14fda60eb8d94c091a1c3690 100644 GIT binary patch delta 1412 zcmZ`%do+}J82-`9rIDHniJB1-OJj^n<5FWxs39|rH7?^a7aEs*X)+BmW(bwbNR&pb zTII3|wGQd9l9Y0(lxk8gg*s7cNg2NVI=lbuAN!v7ocBEMd7g8g_aa|$b%kubco_h| z+)8I3Qhtggd6NLB&s(7kS3@v{<4N}hAkhqf)N}y8Bdyd)01o2-cpnA;@hkv(P{|FN zGXN@hUn+y*`2hySe}STpApbqc83UOwfaD?IRDjS@7={5W2Y5vpY_<78_$O&XF+yES z_D9&;oXLn_g9RQ$=7RE12*aA-;7Hnq+wNj&xlLH!&^Q6NO~AFoOQ7NN9=boB#rC2z zZJk_g*?g<1MeLLkJelLf9^yd>?3j2@n!oMNUEmQ6x?#`-JCSxliIO-`Y7jRX+*$(H zmJny;J$nUWNftN$2x$HaDraF|UvEZ0Vn!a6C;(a#}WPw)%K-niAqV>bnDWW z2d>67NLz)y$HQH7=6S^j1gHzVyeaDA>MONOwL8c9WdNwABr=!+dbkh+#m7bPV|W;W z2;yOm#6kc#<`nwT8WcDU<-}SSDN5tbm}AlGU~$UayqE=*H03H<@7UIBXC332KJNRt zV=_p2&i<5NhQl`d;%6PzmU^6;aeIZ?dC`WvE)K54Ri06CgE2+FoCEeCK zT02aai{}EfE;fzuPj$;RgaiAy=wG$IAB#m78uZoG*2RA>R-ayITm2Okgoj93hnj%#!F2)sy`Pjb<5{CtmI;`>_^#RN^qJQ}z<4uyb}w#JJ5~j& zZ1b0(aTX|Ptk0zgTzXbV+!{&U2*$qp#c>yLpRvZ%T$*^GBvp|_P1lpf8ch)f7#CFf zPdcIJI8m41j0Tb;_1q?{7s?7U(MCPk2zTO{51HN?^aslJ`z#K8AS$!M>E-6P1!WXY z)R$JiEbNIVHiX=0V|Lze-}41y?$ejvnQ5s3;qGa% zS+3P`R;Xn&sa5R&U4vieCG&BYC}a(07QO^R+2QtRO+CLJtT}TLMLljPJuUl9?cnS( zQOf4B)VC-qoQdl96?Q@dd!L-~%-Toq;!V~X(l@7>lp5QR~Cri>H5Pi_24c4aH#tq^7jYjQOn|o16_fI-9yX=IuD6!MBQ4??3|^ z4NLOdh;s$6CTpZElT)IiFODnGH+Jx^gfCRhH>4zMaW6tc$+9Tw7RAV7kXsvQl%2?W z9lBZ{`OqpacM?PYF|U_`zU8}$^6<_O5~j>Lj29$)4jY)Kq}Ys5L&Sqdc7N*VNZK&| z<~_PK+O#UWsK+tq^;79 zkt4+Ws|>9N6-Md@HpcZ0%3EB~JzG4qUFIVZ+ol2vQ@{-u@Q9pv9#Q}vhsRst9IR{z y3>zHL){barXNkiRakvgqul9cgv2k2}gy{bT4JHZo`PeO50C-V++^@KWN&f~>dSQD2 literal 1435 zcmbVMeN5D57_T2=;KTuvfy)+ZF^d@Q`f+!?yUKaHYwz?D4+SoQ2?0v4&<3xqZQ%}_ z(;%0@2s*!FbVf;LreVq!U1Fkcq5*;kld&vuPNQtubTMHwYy#7{y#>zg591$e+P?4e z_WAvu@3w9zSd+To^#u?Fr3ThAMPN-cp5!F(&Kduk18iMlOF@@!7c^$6eYedO~{CeRM z0*hf6Qeq2YL51c8ac!f@7dIA!xW-z}8AjZz;rti{2qL`B!m-F^Nuy#eWSW-(d*c{I z;As`T)`iSCRT|s?)3VCLHnYXVVK@$xq#3u`ND^NO6BtgQ7`RCjZl!D_McCol3jx;D za5YuL_-Aba$%P2Iu23i%jYiE;i&<7{P~7Qs8XN>+0vaZ*LDJcnNz!r?3=FSvs;KCq zEWrjNTP4@&E(CZw6GB7@2A>g2+H9gg%Fr0Aptu=BBM~F6X>Cm};{SEyx!PK&LE+IN zUX$xo4%DMMHvtB@d$A!y5qLwbS4B`1b~7V$brD|D1B?p+U(8`KOc6LvG7c~4_c&=E z5WF--`U%=$BWR59+ieMs=V2WfhS9Xe>T&pOIPN1nRzPJT3BSeXB<+knfelES&Pp7g zuqy()vslXuu@tTHtS+k|S>BxJfDMAI%bFl7FkMW-%S%KlEJw9GqdYUwGQ29*^I^X# zN8suFQsQ&$d#xTj=CFIbq{m^z?XlY|UIudzn3bWOhVL`j@c+pf3Y>7}=JdRbSW}|Dmt_L4A7v$9D(XyXJG%i-$@$_gjaq4*%Xz z)0uZ=-Dk1UD_ifW*Ig4AmyFf#9<8Vv{|nAof7iW}{z;tsMWd-^`|>TBLnHG#CX=&! zRy+<&9Z}?59RvxQAz463q?&RBPy(w_v_@230N}00zj-NdL>55A! z0)i&*YwBN=UYWh9wR_H>ciH8ft+73i&Ru$U>XYnB;rku8cO8%pR5a%xSrh2}b6*jb zgFfoR%jMf&ebASY^^50L^EYRjubhe>8b2$1=k7l0>S;}080aor-8R+s(r+*SI9YN7 q`{ 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 literal 1624 zcmbVMZBWx@9FOJbn1iY3rWBZFb)syvO;TIh9u-Q{YInR1I;^(|hBiQ@ZEBO!;#}+b zN}W!fxauk5%OL^unSP8Y-J8plp~02V^=T9|u)NtPsz_ zFW7wSz?c28X+8`SHh z>?MLUD9tR#i}Z_ES@7&t>1yq&Vy#%mPFe^i+9foB5vMTFZZw!kiCxMb<(1I;;4zO4 zj;c__QuYg{G-;WjoUq`afXnA-As7aQLN2^OAQZy!AOgV%529}&2VNi%2qj1YICimV zYZhIBBnwrJ*`g&WyNII95+2WHvvF;FE@3I;!D6vE$bldnT7yHDnkdZ9F_Ck|8Bm>&lT;S|uNyDb zCe@{8oR@`@gw>*@>rpUg98Bl#)rNwKv^SCri=HkDWH5N>RumksL*Waf=fWg;=f>DqwyBjHICKF|6+Y z-PF38IQX9YhoW5uIy#p${kWnnXx@GI=Y>CAc{XV@WfBr8ito&)s0-Z z{W~>qx8wP0#@TA;@W5s8{U#Ux)P?tIS&qO+OW*x#+KpW;EJV`)Cr%2DZa>dpPG7pT zb6xC=*vd1Z9l6kPS2P@#p>*AH+DI15QGd4guO@OUf8Lid{nf)gz)tOEU%=VvZ*Sbu z)v|x);E4akxrgg2-5mev_BC;udn=l)zFzm-*}2lIb)WT%+c(+5y_Yt6wAe@QYQH%S z`o6kRXWi2(YIVF3_eCDV)mS^2)zedxcQoJB!^&-}tqeb9o|O}leBk`Q20x!!W&`|!PfvK(&#Nq&jt|{6 j9mvnztPSmcEerzo_C&;_N)M(7|Me zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0Jl&~R7L;)|Jmf~z0B0I!_3#> z=+)ro@%8xFF@K**x%6I zz)!fh9;`#dfF&wb-tY7Ez|7R(=j^}E)yLJ` z>hJTs$k5&8>b%R-#M9d4?C{&;=(@?$;OXtNzsK0(=C{Vsz|Pj$(k!jwZzW7 z%+$2P%B{G+wtvFPxW~|#4(Y%E000DZQchC<0DypHsD1GO0004EOGiWihy@);00007 zbV*G`2j&M33MB>0I9Q?p00Mq`R9JLUVRs;Ka&Km7Y-J#Hd2nSQcx`Y1062}6Rok|l zFbI9;EBXl$2*l5EjCt!?eeCCVgL82lPkTD9(FlaiWq%Xt_s?JSBYsUA$tuH}7n@A% zX4RF8u~l=MZIjNtjoa-um?57{LsDI;b}8DusKB<_Oxk>LY^>soZdDCWI26GqW*M?^ zk=2_l-V7V92bfJ<;@wu;TtY{IfN0c4#39SBc$#Mt*C=aH7IKMI0|%K>#4%#v^*NFYisMM3k{WS2SW(eFLUM4nj&$(xPzn19^TSAAqMLF#R7g zK!e>C&CwW~8p#h^rPJw1C^)1>8cD87oDNlR1{7{}?^|dwggie72_SSRY|TkB;Nb=9 zpkz5Pd9lBgfZl4*a=r`NTeF)jGDGV3wtqwC`4&`wC#)tB$fjsN%9S-ZB7e8-2fopF z33uY#{VeD4I7sOFq&b`Ir{Yuk@hOmvLUr*$s?nWHhxcy@{Q*DzidPKo;@toM0Ek$@2eymu=0kc|N>n<^caRO|K9KX($Mq6@RTZ z^o~OB_CO5tL##pi16V_aH5$Wq%rD2DOySNH?tB5yr-BUiy(L8uN~=;BtT%|_M3711 zXuCtYSEPp{XvP@J%7~$-GxE#zC%@fMD;}Klr%X}2>Tm4V7~y}XeY*ev03~!qSaf7z zbY(hYa%Ew3WdJfTGBPbNH!U$VR6{W^Ix{soH83kMFgh?WkwM|A0000bbVXQnWMOn= zI&E)cX=Zrc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztkL_8F@%AE(aY1tF{I+wlE7H+!wv#5XFE-r0s_7q>`%y!=Kks^__zF0y?Tc}*HJN% zRx#a=%lf@uV>lP!k!CaYlqDBt-CKVyKSOh!f z!#!00zD2Wlg{b-gBUbI~g6l7r`mRkoTeCT48(Z(ssakQ5GIUl3u6~yE zC}rnKL8mtzZ#mxnyRiQcCzBNG{mkT(vC21midL`7{*dsaPvmVpBO61?P4=aR^Zj3g ON-IxSKbLh*2~7YTyTFkE diff --git a/toxygen/smileys/default/D83DDCF1.png b/toxygen/smileys/default/D83DDCF1.png index cc722ad2bf332a2696f28a654065bd1fb5d727c9..3571e613c5091556c4b259532463d66abd68a09f 100644 GIT binary patch delta 1590 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 literal 1281 zcmbVMZD<>19KWtzI%)!?U)pq(-maTCSaSDVl1p+nZIa8y2AVD;fm8(PCHJH~+k5fu zYSIce>wLw{mkqQqbs&nkfg&gr6qa?JqcE8IVKVnY=@+NywxVMjjn?Pdr23)kgU8)H z|L3{?@ArSdS5hO7Ioi70Fbs1fhGJ>7wp-608+!Ntu_2(vY4W3HMlG5pL5EmGRPzu| z$ig^GLqVK*dIk1lSaVUzj+&#%VYZ;kZo$%Vmt_s1F|2>EtO&X=T;8V4t&p;;YfW=FDx*~vmsBnAiY{xXXQWM~R_S)Ni1wj3fhd08Y|$0UJo zs+f}@V#}$~WD4h09pXN>$5o&Jz!}C3ygr5jhj5w#G)bYGaRD#uV_4dc*DnHD)5Rj2 zj>YS?&@Mzwn5M>(WT{kgmppD&A16UD7_>NO+J!V+#*AVLWtU>~H5g*hDCm-AN~(fe zj6zn{eB5&Bkx`c`%OvTi~v!TwMADyArZn z$1)GZvYZYDQ`NJoI@Rca)P!oP#)PWjT!z6P$w`W+mW+c|dA6dBL0x(Vig8_)@y-0Q z(hl~09~RW5xfIGZHyNTFvpFW@#Lu4z$GW)<>J| z;eiU87#)p9&$GXyiE9FhSR`BiAzwR3ojB4_ZCQRVT(iHtak6qcatBamcI&W>vhBq_xHIqUTFuea4O`@%^U-}>?>a6H+vh6B22;)9&j-G#Oz&>* zet6*9j+2gy3pVXGpuSpMu2xrGZ@)75+RBUhv94OUtr=fmZ?)O>9l-y3v&sGahcBd# zUbeNa_U>;E|CV{x*|jw8q25`zg1ybA;0O$G-m5K32P7kCrpRc}K@fAD^FhZse#pPIW$6`4fOzVW?@fGkiI` h>$$e&6`ToIny~oQFRuS|KWF{ZumnF6`y@Ix{|^Ljt@r={ diff --git a/toxygen/smileys/default/D83DDCF5.png b/toxygen/smileys/default/D83DDCF5.png index 136b78a942efcdc8332b0036deeb0a291a912809..30fd19c97babfa8333db3d184682149fc9833344 100644 GIT binary patch literal 1665 zcmZ{l2~ZPP7{^}%0ptilK@vm>LI4B707;M-FgM1SfFWR%V+bUu2!bJqFnEBmNCXk( zQn8-ML_r0q&L9E;MR3Fe4=`L7Xtip+NCg$$F52nX>2&9}`_23Ief$4^@9oa!hl+!( z%pJ@D09JzKTnQX8GpCsmeDxq%hrz*k9ZSRlpsvVbT8@Ip`1s`#5dc|k0PNHN@DWb! zd#MG-nyH>e0x4D@l6F5`UhC?zx-`X zN_!L|Do=>rlDJ_;YZYy;JW{Dt&M1n=hgXLT3=BXJq}S`Cqoa4Nl|n)i?Bt1%0tI_P z1zGW7tE5ut&!VNUw=4mB?Fzw7uJ1>-n>IK|C=|kO`OIZZCKKvxHZYvq0*Rr_AlUp; zDuqg=@~D1|w1ux}4p2uEi%NmpiSZ;5iNsVdmuF<#25$#CaS`0zF~S@tCnqv~K9s#- zjA(hy!{&v%2^%}x*48%40W;`o_O17aPR4Y!S$B80N~H=wo571PIuDs|V?;AGwn7=h zOF`W6>nnJ}C~Ap#W^cb9#C?5Jk4#EFSIjcG+EiNNIbGEE;lo7VgQERj)1`3g>n+dy z{T~evyp)>vHE!7egGhMW=Umgi97?Js!B=N zkfg+`GgFoLt;wkXEGaAv7uFBrEeBuN1|Z8c#y8$#FA2lT0=BOuJ1r|3aUVO2usBiH zh&t2K0^PkZWk+&7qF(%Cl26?cFryD;i$ew}J$h&j==Z>_7FW1`nl8HdzWHXJR@;iV;IGfQLE(f_&U#lY;`1kY z&~<*QXGKK8bXhm*9pTMMeysJIJ&`^}`#QZsjjrwSa4Bav6c?i!9jb$h9vNK7R^#iJ z=a)6$y<437?B5||MXiP%U7+yQZA|dklW~vc0k=Z&YIYmpVcx-lHQE8^U0WQ74^(fe z({j50%-j#yrg0NX1SX88*af6R=%Y<~d)vFFXi!-1$YYISVw{kck6A3OC%0T@z%00o zcU^Kga2xlF?q%NgQ^#n7x{+5I0h2d)>^}dV$H~LmkQW2h%q`lgz19+RIzNEXLNHh( zy0kTuE9X_guPQ>oWl6F3<6{95E%x5DX@MAxIg(RcTwXL{Y&nH8#^G=k8jYyu7Rif5 zy4ACir>VeJ@M>$4ldiXR>Q@vTVnwyx9=?8WmRV(V)R1cj&UGk|F?6kITXuG3c6Rm< z0e5?9~b@TaP!?M6Jos;Z>ci zKdaZB>C`n|u4nJwof95jd%pQZolaMGqFEeWTAEWR;)I>USvEAJ_xEG_bC|0`Mq2MK zzk}^C0sh0wRO>utdiYx?q4aYvEu*$ literal 1584 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YHGxhOTUB)=#mKR*YS0s=DfOY(~| z@(UE4gUu8)!ZY(y^2>`gLD2*8txIZAW?5>ATTyIHi?JgIrW98(0$ z8gqK5PGVqSO7L`X45_%aWU_yTu%pPa{WtG^@n3V$UPMrLsj1s4(Q1K zAu6c<(lKzukyow(9rwD#1y>5%cyTJpaoKKR=ht%K*Sfmm;IW`p8Bw!yH{E>mChwa0 z>}6)NX7ksbHuzloo;UxV(em}u^+(H;d}P#aP5ZX2!|)N~$Hb*GEY-h-T`N73`$$G{ zVcC=p2}6e$Pg(Zv)_xZFNMT0Hl5*yGrfb}zGD~7}r}h0kT3I4e$Iy48k@FG9!I{Nd zymRiTo^xJx_`q!xww~ zu9v=w{H|WhLWyY!QHo68z3t{qi~g``OV=*LrfTl@8`ZyGHsy2)xwbgWw#B_dlQCcY zL)7+1OCU`r>u2V<~yIiPwzoF4dm`5uq)~4_EF`c_= z{bHB6_ziVu&gmEA_!J+?sr6djl6zr}@|1aYG8;mB64=t;T5|9kp1YD;I(JFEv-l+c zRlXJJo+na_OrJkEvux{*hq{X*%i29{lBXZpY3s?;e0xXm{tkxo$`d_yw0WpJQgV7z zdr~|x;Jc^3gjn3S+RY)Rr_{ba4{UsBI8kS+cKL)Q*Iz#nd8qmQ=Xs6uIVu0Y8!w&x xU$FDyJ9Q_Q-7B1GRF6%sdwz22qx(!e47*mDDpp+zoe3(#Jzf1=);T3K0RZh0O)LNa diff --git a/toxygen/smileys/default/D83DDCF6.png b/toxygen/smileys/default/D83DDCF6.png index 68a63e0fd88f2ee956f8ddc1060b3dfcd53eeedb..c0be3ad1a16902715b2d4143358f4190cf4b6bb7 100644 GIT binary patch literal 1197 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>$?W z|NsBvH)r9D0&}1P#7lzwK%PJbp3j~=d+zo836JZeZ9s9xByV>YnK>+u>_85CiKnkC z`!jYvE>+&6k1tGMU|_1R3W+EQN-S3>D9TUE%t=)!sVqoUsK_l~V6f<&8tT32jsnk} z*CLio(^^=Lf0Wql9XidjF3LdOzW=`Z<5i5@3#aX3UABM!{|oDXi0Z1$F43QtW4Lj4 zYPjFAX=m5o((A2!zy0=FjhbP5uGV{D;v+}_92vyyER%U4*L%*+S6J&JV&>6Yw z&773aN~^^WyJa(`aWAjfezt6S#9{{rvn<oW$|{qgB{{iSX(0g&tMH| z2oY#n%ePERqUm_@T`{J_yN)?2rk%D~f-Kq1SMn|S5ByaXe#n{O=eVvkqMneW(0n?)*^a ztycWJOgU1DM~b)Po>&rETJr5q%LyyL%Up%7U0nwhxZN@ukDE9Ln5@#9q3P!K-Sz)L zw*u`MVLgAQon-x_S|F;{a=`Pb@{8*-^;QCGRW0jJsHa{0-zVI^xo749b@|3j1$Sq! zElXGnfAa3!H-Y~^5zDgw&TJVa=F{vTWL2Z^ zOU@NH9=UP!rTNQGtk?Rz@cxVCyydU9|BgNPw{YTxufD})v$sFG{Og^G zWB2Y4`lsi2+cZB~Dyn^Nqv`yJrqCm|c(3{#%JR0ISh#nwx)Ov-Klz8chIJ;`%(3VtQ&&YGO)d;mK4`8N%S{>gTe~DWM4fgx>Uz literal 1173 zcmbVMO>EOv9QWF-VpS`R3Q_1b;h7#lBDSC7xV6=^b^S4dNU73D%|JqR?3c!B?dRIB zOPT{si-ef8L&l^^TtNHKjuS_wP67mIgNei7w&M;Hh{MpPX-Is8=Qb%jR2*j6e*gFU z|NZ{o{%mUUKDFU0vjT;P0kImi^c$;AvFC6)S|4>YyT|GoCdE zbI@{@7w^Ckis~%6`2sFvk1ICvSS!|Ht6o6R6g4td4J>;eV!8qBRskI>javHV*(TNh}akk_3*>0uKa^C%2RUqLPvnVTf*B46znEB_*euEn8%l zX37``3ddC{6}FOOQ8>o|S(alCK}ZmdM6~Q{Z6Gm`QYK&SkKcZ_xGIQdpmv> zdy+4nbvm;*etGcW4YAObk}(OE$Ym}!LN_( R{%JM-QDlrs{rid8wO7oxfh_<4 diff --git a/toxygen/smileys/default/D83DDCF7.png b/toxygen/smileys/default/D83DDCF7.png index d38227ef048cf13dbdcfadc4a3799b545b595e0f..b02f891d373e727948697a194e1730923ef4da9e 100644 GIT binary patch delta 1471 zcmV;w1wi`j3+W4x8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0d!DIR7L;)|A&W%_4W13%gfZ% z)cpMXy1KfeuC}48v+3^gaH%F))(*4x_P<$vPo?VhNx*x=@De~_fJ zz01|yz{$|S$F@NTuei6u%#WF&thmFd zw7;FIv)A9_VrFSBF*z?dTrM#>XlrlP-Qk_7v#Peijg_6BuDPA9xSg!HnW(dvp{Lm1 z;%#zvL`O_-a(39?45jhB;~qSoHxkeHu=j+vRL zw1$qBu)M~txx=ox!>+o-uDQgmxWbT|q;PtGXmNLOdV!LhrLVfgv%bfJiMbSas!MY}MRm+oUsZCb}8DusKB?`Oxk>LY^>soZdDC;1Qfw0W`7y7ago)VEZz(otp}J*T;knU+g!p% zfq-n(M&u#OZaCaClUEq5Jz|F*JGj)Qy%yp~Zg?(9yd5kRJQ(0B*QW{elqrB#dtH^?KhUbqrhZk>|u>*OM zIe)Gr6UKkv0g#c4qd&ojH0Ps zJ6ipga{_L!HW6Mxwsn-#kW+~QKrikH*q>cH1sB&EI=c`;chSEN9!hi+2cHq)T(TZE zAxJUf2QPPp{ky0gmhNz$AwT$8G`cqLhkphER*ewiHDnuY(pfc?y>CKf&C?WWf*i$E zr77cXipHyG-7U%*lEu2jQ-cPXa*5-JLD$zzZjf(*RSH314m^lnC%C0*6E)BqT!5r; zmMyK3Tg9I6ZD(LgoEpUsTcy)wB!3hP zsgX{SPbH>970iIbt?qpbD-I#g$3X%JortY@k_>ow!E;EmoRqxSUnrnY9K@7CbAtF7 zE>T_8_&dA?w`$=!~UBO-S_A$$OJO>HgPnxUSJ{6DE zw@-m=6sDUGQjP9pIlO;M=ntrQihq1?Jj(b0006Z~L_t&-(_>&D3>cZ1VJsF_HUib~2#DynMg8k$<#I=TuB zdin+i28Kq)CZ=ZQ7M50e4AwTbwzhUs_709t&MvNQ)(q|*o}Qjw-afv5{(k|1LBZ|} zA)#Sm;SrHh(J`@c@d=3`3`xl;DJiLG=@~A8nOUjXNenr;dHDr}Ma3nhW#xGll{pMm zXn?R^0TCT5UptJ^t^fc4C3HntbYx+4WjbSWWnpw>05UK#GA%GSEip7yF)%taH99pk zD=;uRFfe5f#kl|g03~!qSSoa6VRU6WZEs|0W_bWIFfuYNFgGnRG*mG#Ix{soH8d+Q ZFgh?W6jOZ#ks&7v002ovPDHLkV1i?Zx}5+3 literal 1516 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQFDg_;^Jba1l5~DPMGNf9itCQ97u@;CIn1@ASOKN13B=_lbQ$2F-5?v zF)c47mVtpu#?!?yq~g|*VDD__P%a*0qRD74)ym|Y~nX_iObQ{PrS#5YLcBf(Co=Cxs_c9W4o-jOn zpT0tr^>X< z@MN#fzQD$o7-c54WK#0gvICq}v+bWv+OsxG^yC#+iTgYr)>#Sfw9d`-zf{RO@4A>o z-&E&y4O&s_ZWkVJm1pvfpZ4*=#pETIJm1KzQl7i7p?W6Ir=O`mO!lU&X6*~DOzp3F zDVMqL&Ye}t+h-iVyVFwT%iKku4lO@_W1B6{ag&c*7O~b%t`^$0Bha&F+L3tv!@mSh zJuS7V|0u*4+U=HXQBonJ-q-KEOeQom;AX^ofi-`A{JZYy{esis{hICBp%3q>{r$fG c<8}sShEKDMl!IR~o&c4zopr0KFkFA^-pY diff --git a/toxygen/smileys/default/D83DDCF9.png b/toxygen/smileys/default/D83DDCF9.png index 6cb3b360893b464b340a2af267cdf8727cdd2a7b..3ce230562f607329fd7f44a71277fdb09a255d20 100644 GIT binary patch delta 1499 zcmZWnc{J2}6#p6%9z)3XB+m%-#=}@rhEA5IOpKnjov}+ZCB_z+u`gMMrfJI9N?9@` zG#O(T#@M4QQ_>_Pw5WJ<@_N7b&pYp(ch5cN-p@Vv^Z9=7_j~UnQW3PDDUtwy-9}s8 zp#TrI!`lJyFi-Bg?-mH__&d7c0k~uU05t^wF;t>10uYG?V2J<#h6#Xr7^}(E1^@}4 z-<>_MKYskUUeV-pF&pYsQc;7PRnyAp7_M*bg>q(P6Pw%DDVS*EKlAblg(?N5k14FG zme80S2%Nh_kIP`?6xKnW#e4us{M%5*<~5Cd#q74|l*A?5(YCnf?_-1{ zR&c+JDLOkqyt$^nnc*{bDE#{C!7D}vQT0oL?#XH{o8uHb)4LM2eW3QiUEeBWPoBz( zx5uoIs84FOQuF$RU#C>3?Sf?yli^K;tWELEzLAKEqb}i;XTpX0;_Y~o}W&P z5WOwF5ucK{qGtHzQIZpB`W!NMF0BHG`k?zF|7bpzimFuSU_C;!eR*91t6`gutmvJq z?>KkfNU1M`H*L6T@08o{@ba|2)K)PT8Q@^mwbsk)qFh-Ym`rTuYfcz$>uEU( z0_H@EN6{krKndjhf#5`y5tKy|34hlM4bNL>HiCDiB~S80mE)GNiZwgf3H??Q>v@(+ z-nN`b`WVeU6J~2tEHhbh#u~0Zp^33pvOhN3rl5U8i#3=tqGPn_t_C+I_+n6ro_lk9V#Sy;sb}V88zHOdpzwnv)Uri20t33%8od6 z!lAC#DO=NmPdCUlk~&jj&b4iY_ncIQcQ(xGztE_3x&_0k&*_es%8Z9qYv6+F{Pr#? zF_o?0zanvRqNVMh#fkCY{9%IlKmawZ$?)dwhZiZKDhHO0i(C%dcOdsX#f5u_$hMl3 zN36jYUPM!;t&uC$>k<9t< zlyan;qSEmcVS$!bew*-H^2{PH$@<;OMaEi;wjTNNQ`XD*s*SqV%d!C#%%%;UXNxY3 zt~JUjdkSa_$)dsclZnuZ3UCMQJT%7q3GK2PzSLCJ6V#j{hum$(?cIZ2h7a0%hf6EV z5inR1sycK@)4mgN^W-$fw)9@b1wx3Pu7FP<`i02CgXYy3UeEnR*$7Kc)Hj7(o!Pl% z5vqefJHPt+-P;vOsn&!)3lPrE5{Y({4kSTQOH)It!Q4jI428j93@nXNW-vX)5*m}v z(kd#r$)GcVvg0t{Ss=(78tTIf24wsRK|}o}jwpC?c~U$LM7X-TnQG{%s+yXkopAm9 zG`qx_Yzu&WXC9&*g9?Yn(kEk&lLLIoff)brKqvq+w3(R^dauzTQwtAMG{$@nW{-s- r8jV4t0~NZt|6!0W1e^_u{fF^Cf{Od)E&0e@vH;+)E(h=16O#S{xF)R6 literal 1582 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ4|I$^C~+Vq7MKt)1%jCHqz~l4Gf!$BFvk=D zv&Nazu4@<=nBqKL978H@EeZ0@5DpZvoqR?mbFo>cWOmY}Wu8$Jb{S}wDt2+ZF40<) zuraArQ83H3L@`fi#od?3dS^GWitCBE#AI{_ObBujSj{^1QdI8jEz8cP8_icad*;Wz z#cTI_2)r+tvGegS*CJ@mPJ~{Z>^{=n+K7NoCw$0m%F*@n?nUtho8ud?~>2Cgfq>zVa z@9GO@reF5X2|$a?*g%`-gJ5@xWSlezu*G{fGUTPm(c zbF`X2u2f0!bheu7Xf|EGboZ-u-|y}G-!XlYrG<>CK?(C`mmjy;Hbm)iT&ot~%wdR> ze>|Dfr)F-n$`|id8~b|qy}T*HVG1oLgsd>vGp^&vKuG*8T7=z+kRWuDw3V*3g z6mD7eWcwGt-G{FocXv|E`Lgc#>W!>6b#^>O2?bZz9=vw##e8)w=9xV{Kcc4aCjZ{~ zZSVCTvblwQX6N_RC%&^Qk<#_8+hWEZpeH@G_i*%wOeMzH>zTI>-``!^DJZ+#Zi1TZ zq%{e3itUqS-RpMMGk&ZPUvyUYL&d9!(?0olCtvkwOkke9>V%ZY)`o`@uk)Df+O4lN zan8{P2aJyep8dpT&-R1KQDE_cfT*OMN~d{DlrE+{St>K@P~x;;-m9A!zpT=DRcmK< z_2rt>BZrqO^Q7y4dGz+Uh|}!yg(o_D7SEc>8@BdNU4?!5Pq+C$Q>*xT>+}!tK8k+ek>uP;hUQ{@E)xw9F-}oTYb$>t4V2$UEHLq261`J5+6Ji1vf$uQ%=5 z5fJBM>?mjCAhRJz>&3fIk6*sMbo1`nD>u(vxv@Gx{bj4o3O}_;)8@M>@GtdIiHu1E zdRI*}s1@ibzLFq6P#`X_u;iW6#o2vW`3jp2*At)!W0JSK3*&z#-FrX|dx@v7EBiBc zJ}y;;-9c+~>lqlB8mdAfN`ey06$*;-(=u~X6-p`#QWYw43m6zIdZ&guPMTxDv*)#F zWz)ozlQ!)Ke@t5?^ZCz~BRr>#ayCgWGdQs+Dr+n6-+w>5>yE6?RE|uo_?$Gk({|0S zBExN2d7E=D&D{R6!iLTG&F0LARo++iTz89${xF;EIa|N4HMG9w;@VY-E)Ie^Nz)Xi z#UuBgd~|lhg{p>|M^x|SZau5|OhaHo@2w7-l$+{&-_JZz=E>=gSa_UEZ)@iIR*Ol} z^VC-E=`3)!b5&zpwIJ?>THwl$Jh!>DpWA#su;YI3j^(!}^>N3{IsNoedS#E!gW$-} zw?R_Der}a#MZ+huKIJ(tU!TvXf6pkH{gX;@jJsM-^?k+(6T27NAM7xn!rC(9{~XpJ zht(H2wpq@cbx^=2?z5R7hcsW3^6I%aiuIck-e!D_KF7JEc1!06rcG-X&uN?ECBjf; zsFYA&`c@<+Ci}%3Cyu+W|E(%CE(XffxV03Yth&*naI{B$&mGZk?-!Lt>^fXu+gkH! zb4E$-J#${BHByR4vQL=ZTCzDyt2(~r#H`(#S6D2&R(b^=%ji0`I^%Bo6jK%M-A96( z*=AlkvhnDG*V$_3C$=BZD`si>Ai7($D%A6;npS|2<$pur_a`nWoaB5UsxqfRbE5ME zp-Ed5*6mX~FMmQk!IJs%e??}KE3p@4dh2_G7yn3HU9wAtlePVdhx*CoLd%_9#cu3b z`>wV-GRJ9Ne=m!Gm0)zSy5xrsP0uH!SZY>o+#ldD-=ZU!?d(BCskh;GvOg7UkyT}9 z=CyI%o<7&X{lgL#cW=IPC+_F|_RRkhdocFPH=EOyd-#NQ*=MfH`@OOv;=Ud0ra+&& zH50PxOVm$4{2T35&(JU0m3MW$*kfQK-Rw9S^@<8Rf1QRD82jNa_Wh*5l3s12V-NR_fs!X zt*26_6$0oaMXZBc+Oaub=Kc zxL?k7)6VnXe{()~w#i=hwyM%nt2L@Xf2fwYMwFx^mZVxG7o`Fz1|tI_BV7Y?T|<)) zLjx;gQ!5i23vB}fD+7Z(mi3_Ai_npqpOTqYiA4uggM6g;`-zI`Tnr4Ju6{1-oD!M< Do9IP{ literal 1492 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLylcOS(cjOR+OKs01jWPOf2>r7@E5nI2xKbnz|T) z{cYiFVqs=%VCm@Ws|N%#)f2%rQm4 ztkJPaUyXr*iN({!F{I+wl%V_GB7qXNkMGPZOcPb_x?3BN^)^wy#YIyi&_$y|L+Q}> z6Nla^PPn&ix`W66h6Qg9e!80>SUZ7h+lB*5Tulo&jItEVZztqjPfdM(u4Mo1Tjx#) zRqD&%e_Hc=&hNYOr`oldIr=28nY(K~-4}beQC>JR=7IROjZfZ+2Z+hp@=B(NKfmod zODs^Za50~Mmx1dN_j5hdnrbp9oQa7SeC_xq&*pzcURKA&uHXM2Y+f8*zEm!5ud4Z< zNe^*c3RU)}+ zm6x!P7vJ-Wzt2RCX8xYBqs%xb;IqY=FyWtv!jB{$-}vom@MXTFiz}W8OgE|LjXB?( z8Wk|JN&Tkk7FVl;7giFNU+><%A}=&h%X*S?%q0_^9sJr!PS+*P>i)KD4$P@4yuw=8 z;XUh^;12fNn-1`_^_hL0zy19bOQojQ9|G7PaWMJFcqDE9<0}vm{4wpQYf}95Ct|PJ zbZ@gQ%S@`#;jPX$zt$aE^FNT2H8Dh)ukdaHyXi-jwQY_2?kUf>Q!l=C7Pq{0oTHC# z@{Q!%O4&h+S>^}sEaobCzVZK`&LtO{>w8a_i^*)ja?#qHl@x+DLx^m9w6%elF{r5}E+z CGa}Fc diff --git a/toxygen/smileys/default/D83DDCFB.png b/toxygen/smileys/default/D83DDCFB.png index 173b13ced0039f4f420060680167db3905bc8285..0c1e4410abf2875298e04261f363c9bf73dd1f3b 100644 GIT binary patch delta 1350 zcmV-M1-bgt3-Jn&8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0QOK!R7L;)|FnrfsIj)9tg(TQ zm#(+Gh?SjVa(Q8HcDBC5qph-Nd4OGNakag|w!OlEj+S3+bbnfBZnB3zj$|*6WG?^! z07fwpK`Rdc006RwKd*&8>Bf@i#Ez_jJ>9Qlh-o?8u43G;W!}!Q-{a%n&a$a~JKCvV zFCY!p#Hf;;gll^?d5KJto`u%KrKNj0)uUR&$jESjL7l8{eULy=W*4D%H_@I}v$(iV zU>{|A9b0h-Lw{5ZopUwJm{PQ&h-`OcI7bM!p@o@lGs%-q$CFU0mwlC*nyHt5m1;7? zj!Ju3EUUA*l4mf%iAG^+ZjNLxWJMhR|Nj$}HN^k`02y>rPE!B?0%^EB%Y+3PrrSCO zc=MVK!^6ZNu)u&QC_;|_0004EOGiWihy@);00007bbm=rMF-{w4hkgzyHczz0007d zdQ@0+Qek%>aB^>EX>4U6ba`-PAb4$X0020Rl~qBun=lOA>l8hLkz~mnhXCI7y)JwF zj^vO~LfhYv2S%1PBWY~t=l37C4#nY_cm_J|ewTEV3@-D)8Ya^2G;m3I-|z^BM0Ko#T*+axMA#;~oh za?Ue%JjdOLw{%<5ae<3-xm^5AkHG>yl~vU)sedu$WTe+zaTQsRPWSxr?(pJ089R_C znd5pfVcfR?07fp3{zOH*AuS-p{O>G40j)wK*ygO%j0(Zyn74^gu+%Mgj<(3R`n08SwCeaY(Y9l)TtqNQon&N|DmD6Rz27X zaJ72lQ?u1>cbYX0x;(*p{lRcF=6|~&hy;@+)7e}S1&|ejsf*=mttv8T8-i_jy1v_Q zG%yYXGmod!**q9ppnGBPbNH!U$VR536*Gc`IiI4dwPIxsLFSr_k-Atwp|07*qo IM6N<$f*Ac$hX4Qo literal 1490 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ#&rIbo&`bc{YIaUdlYm=G`pf|&5659GizPih`8#}omx z#{TczCm0x*m^@t^Ln>}9nRMPeBv9n|{_mBmT|+#SCq@{XXm=X0$7bluaH^gtm>95S zrE9>uBd*d5UGhc(q zAInzo+3dSheD3+&_kSIiOwkZ8cRw`i+6N04}X^@|8n|ty0^b` zXS$g9@zY-?Ubvdz>6HEE{@imZY{%~J$<=vMl`NYgN@M!scl1$h zTOyBP8k^ty*^!6kn|fk0D^pkZ)puVHi3rNid)u_-NBl&W#WOm1UWagW{`ti7uJ_!c z)oaBLpSPc);lXN>&+ZZG&}byu`t_wChl)&?@SP?D$+ko96AU*xo?WHP;re(-*=v@( z`!C%z#M)nSY8^{7)zdMke%La5*-HLnk0hABo?Q_Asl$g)Y46GPS$y6*|B9dM`dxlG z$E^D&D>G{`-~IX}8-De!x>L6M*i`0s77tzvn``})Iy^5nx5Iz=gbMyEzx)>_RYqNv znO2lG|KKkd^Sd)=A4$B=C@m9W+3&HasFaJTulT7$)6HI4-?(t!U%M7weX-)SeZc&% zVvnaYZZ);k_PVmaWnUfp)$_;3SvA+9OTNCom3Z=Naq0ihLdV}e=Z~+heR_89ytQ*X zmu{Ut@#dZG`+{qKm$Ur6dOON)y@*_&o3X7us|15|or{*ls&j^*Qr6Se&t;ucLK6T> C-YmBO diff --git a/toxygen/smileys/default/D83DDCFC.png b/toxygen/smileys/default/D83DDCFC.png index 7c71a81435028bea6abb5a8ed4f149d7b287733c..d7fcb26a608b031651b852a01026977bd0b2daff 100644 GIT binary patch delta 1128 zcmZqWn#wsrvYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_CQ)L(kO0 z!OYx2MNMBr%gEg))ZD_+!#l{#(&6Np>tWH^^>$9duixJJ^!aX7Z0@^H|K5D~QeW2;XcN#%iCtOSfi7S! z3GxeOxc?yL&i^tXmodrP-G!lpRn`N@VK4FYb!C6X&c~(7tQ7rsIs*e!T~$a#Nl;>a zxk5ovep+TuszOO+L8?MUZUF;>Meo$m$W2QPIR2)K?rq^ZTym=DcM8{{I)&@0evaDYVU8-)!cMv#YellUF}8yRA02QhNLCTmxsj*=gQXSZsyLsl8pO0=t92LL2O6p#LMw9OuqxzJ`N~_Hy_eh#W@f*zEab)}2Z97}~ zm>4gv=iUyhmJ~!tSGdweKDT&HfhEp}gav=A=`zIm(X&rwA?J`qY{uG*v45 zM3{%=*E7uT|7ISsJv+@LCgD+xpZWdE$u5kuPp!&Yeq~0GLKhKCwC zE?dRF-RRcXP`2fl?lYD#sc3+W3C*t;CmH-yeR8oA0_f`?H-%eZhvT9RlkeZ)Z$C zx9a4ZT49@Pzu*m=B_hF7m-TGdlv=;2^twiNfLKvyC^P%apd*G!558JX;(p?t(4*QY z!>W>P#TfW7C|RI^OT;BO*xLI@OS7UwVw<*-6;rShyP~Q_X5fxrZ8rNS@E@pQS@z$V zEn~y(MV3;{!OA}qSC?4H)^oD9U-3{r8QOVyA=ezyywdm48HO!3_UxPrB^o(rJ&hhb zWPZIM#nPa1qrR7eeAb1cecYSY-#lBEe_-$13zc=Oq7OJ%d$MfOywzWn%G-CsKKFNH z{+HlGbAOcCpRTmw6WV2$c`ohu${$nq{pCto91#0wLRN|T<%fUco&Gb-X6mZ1<+dxx z0VdISPZ!4!iOXxxpB8dZU~qeQU$?buQuez&|Fw?)dsX&^+$iT=**T7uY&?Lmrz{=Rv%EVIJz`)AD m;6(9~=O{XI^HVa@DsgMrsV5UXQBj?Xfx*+&&t;ucLK6U-LfH=h literal 1285 zcmbVMZEO=|96wwfID;%fM+SynZi;Nu-b;7a-enbgcdhHBvuT@b8b0iHecB$|-Icp5 z?S_gk#7v2ws&f$n62cb+Vo)5Q5eb2b&ev7PnvBFH8d8F|j2K)b^SP~^epvY6`)_zu1!*|2%yabne%im>1^@Np`41oCJdJjN@ zs+0r~AjSK4UIwiQQk76*T~=3U8z<|kQ?hlOIW-N@2-3PWmzLySV4*!Asc2s8=BMW{ zREc}BPC7({(gH{+Z36~~4s^uifnJ%7V_Tm8DdM#~oltr_tI z7ZC$xTTt?1Da%T8IG)XBomrPtHerRW-J zGfF*r#`0p&)5Q?fbSSh$teNFR!Ia@SDUFj(0#{W#t_5w=ihzIJSXSGN^``+I0j8cY zWLS?xV+9OzcXva!BJ_rP#!z5Uq&`uXGb+%mpy1kAmGU(<`MT_fMv&k;cVzeSKlwBYmFmx4N$S<1w|;Z2y6U~MS-seti5_nW$x}NY8{s={`2Ox50*O5zT^K!qV%xB)wWXUI29KA% z6*mph>ONyk>R3TYqfm#|=L>yl};24t!SCR7LD7{kd`K z>eTq<$$M}7-Vg=Ua2lAAR-W qvEfYJp<6GiW6AKk`FYo!8UiU8hYq~6se0W0F$Mh{;t9U{m45*DBE|Fo diff --git a/toxygen/smileys/default/D83DDD00.png b/toxygen/smileys/default/D83DDD00.png index 03465b5131dd23efe0709a85bc80aa14274691b0..08fd8656fdace647eb9272c7a92d3ee466ed44ea 100644 GIT binary patch delta 1313 zcmaFQb%tw#WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D3cofE^Ih+|I3eG zU0corEnau(?)p=ACv3X_bivvax7MA!J!#vyHOFs#`|+z|!%3i<*3=)rS-fEX{C$^z zu3K~T`n)|CfNuQy{b$q413;JNOxpr+S*mj}D1^jIg8V=FpGQ{Lf{(eba92&Wu>)eO1x5U0n2s+3O`| z!;c@++V?8#s)5o14l~2kQ+m7a>^=GFZ2g9dZVfjNsor~=H`{wnpn`(*R>{f}yY%RQAbroG}G}5d7RTNizoF-{KLHy;%Jw}Ot z`ehj;>vvjw;dI-xP)Fg&uj@J!n3gmN?ZtF;rx#kk-Z#6eTXi*`?f6vYSq`j7ZeV}Dz{YhZsV<=xn*@)`uo`ek)KW03WP6N z$-%QJp+&61N1MfU@vEkpNhu}qU*vOGwzp*-KOsLeMj@Ul{3XjbrpkjeEG};C4o zT(V@B5SQM{nSPTt3)OFTcIC6!v-aJtqgs55->*9|amaS8t(?SDQE|{FYLkpr_#^>$P{WN%Y3MvG^`M z+IcACmh9yoM-HV|To<3Fesz)Ac6P13#HS?}Z}=TZocyvUb74QeJW8)uP~E2OC7#SED=^B{p8k&R{8dw>dTA7$> t8yHv_7}!}~;X~1oo1c=IR*9*B2d2Y6Eoak2MRhI)22WQ%mvv4FO#lcvV7~wW literal 1391 zcmbVMZ%i9y7;ixdE}b%#XwfLQV~bm`y}Pz|(33*TwY80uRcHcp&T+jSa6)^>-LW2x z3dqKU@quL#mTVDCCYbmqFh96Njbq@{L^FxT5$8a%h}$BfL12SIeGe%5f$W2qyL+Ga zz32CP{$Gc`^qKA39^IzVXtsMj&N8rOs84ztcxTTgXs~3-u1dL_ua={<$Z8xRKFC7e zFkQozv2>`Z;cK=)qe-pi0+n*5uY_dyu#r}Ej7nGlXpN?zND*kJj+LPxTf;?c@a!j_ z!w?s;!4xQ zRg+~-_l7O7v%$5pERYBijYf@8vym5T5X@?|svIVh0caScrie@{hKQ7tWN@+)BXWYw z@exR6q=S5;Y=eQP>mh^%pKp^mB5foJqzqAL0l|zY5)P|zt!Ybg8T+prTWU*zCV@rD zScz{G8BmYvoFo|J?%sw}Mc@s&U*teh=z1s5G=|xT>~-2;@WmM7LL_R=rwBJj7~uK(WC!?bd6}1Lc>$uz321Kx7YXrEX`fo2^=O@}$Q@xr zZjlc|YxyO)t?ZjEd8osIQ7)H7jXNK8x*Z^50!UV?%M5Q|L;ojd2yg~bo8v#tk~{(( zsEV7d4>p^_!$!cwh+s6nIGYRRRomxvIs(d1!II}2PPS%Vyl?bPdtCQ=;#5nYV;&of zCl+~QchByzV0Wy$a9>Yzb0YJD1Eno(Q)9=oziLO(mF30W*2))d^$)*=47b5)qIUek zo{k>j3~@-gG)Umn=Vd;sH^E*Et7NvzlO#a+2cxSA?xw+X4 z-4J%(-)=A8GdMjz@CRD(>fFg)x>~)*_^tQEu%|uSWn=ua?!iZXeE9C$O5;q&LEV>p zA#=0yqnfH<=;(>Wa-xqOYt7Z2xiWb?rqyR8zDsGC8G20j?1P8t3(L13A6dGQho$T| zeMeulq+hvKvHB#j%aEI1b@RG>cIx-fdjGT^9Y_t@@5Zm|9-4fmcn72H@BeLd`q(l* uks}?xsPnW|>mJB7eP0fhrec~Do#x;|>Ptu78y!%8fnHas^TXn**Zu*%!uo&! diff --git a/toxygen/smileys/default/D83DDD01.png b/toxygen/smileys/default/D83DDD01.png index bc521ef6a3405cb962887efa946e71ec230ea40f..3f8c7bff89bbf167bfc0f5c22d11e22edd48423f 100644 GIT binary patch delta 1344 zcmeyu^_pvfWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DeIKS$8IOg zKJfm_kJ@#oXYIT8{QWneBY+xWr|x%*<($|LXUKYx#zvVGODo6p~W z1-dbI%C?@(r+)tWGk4ELpnEG8@4Nf_-L$QzfexRz`S`Eje}K*}o44cLr*9A!XKC}z z1o~dQB*+gGg2=%0*|TTQy`DeeaecH6D9)JV?d~G`nz?Wyki%Z$>Fdh=jGd3GUX}H< zMZ_Wo2Bx~Ikcg6?#Bzm#qWrYXoK%I9%7RpdirfMQ28-UQp@ExjEAaezE@I2%;neil z;^3dsW%F(8!j(>bF8*`tdWBko;+9M64!rz+KK^C<|5;m(l+U}SlO1?%c9`%fuc)hA zUY~2un7{pYuF%1Ip?89MSM!`R(&0Y5J+t2bQjT%Xnw;5hLPeCBjFN=~{qJdhzAKe~ zO+>oXVuskMf9uNc_SWiJGHnu^JC8j-`{7f$nKdnqZ=YE|+1gx`Yn0{pp*~CE`hGR3huc~ovxzURsd^D8r^x*yL`cwfs+*?R z-aTne_3EG7ik2PiFKZTmJ8>i9&VujHr*b~5*I`IW$+>V`a%0=3h6k_bZ+76Cz~waI zTf(Yl#R{A2TU7+ic#l+tb>CPl=C^^zjS@ z(`L`FcGiNE)-9@+y<>ebK&EC{yK!HYhvh<1<$a%vLT}XX?~dB=lIy$Vr-SF!)?Ab!@&p75V-ISiRSM_S= z>iiRbnSV0G^c{HlzkUMatGls^eLoJajQexNy1!(XkdfX>Nk5s*Z?-$LzO~r1R_|_& zX#0d;uN4?oWINUtP7p*XQ^|= zjX!P$ePRCc6ZdQXUU>gfx%9g$`Te`+Jo~KVdENZ%&D^_P`mb*s{Y`u=~T?@FVRRZ~lq)|B_eo)OgpqWMMYY$*Lu; z5hW>!C8<`)MX5lF!N|bKNY}tz*U%)y(7?*r)XLOA+rYrez+j@|!&MLs+E5+2`6-!c amAG|0kckwVsHo1xz~JfX=d#Wzp$P!CI(a|< literal 1396 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y`gLD2*8txIZAW?5>ATTy#&rIbo&`bc{YIaUdlYm=G`pf|&5659GizPih`8#}omx zhQM{XvkVN3OFUg1Ln>}1`9u{?Y!o`ca%7&vAD)i?lN;tcel^_i=l}P7y$RD)E+$Q= znqa57tiJxg7n32g-5v1-M;GWE=+oo7cKpkG`3cSp+y$Eqr|e#7d_#jH;l#!UpGL-T z(?T|P-aMLjk(H#ZUU%?8GXD$&};HoreieR%EldXA6(>;E@M#zh73TnzKFu>Je{ zynS0Fd!CU^<^QRS%h+=c*lQg34d?RFx2|hX{C;&gkNv-Y&Dyi%l8;{4{@SQ{4THJ! zEP2P&jm$aanmx&4~}fp2i$Y^ev7Y;QQi~ynu5C|7B)B_W%F>r7=8A zcUXS*R&l3eSVwcB+a6tJADNb$690^TygF^v;Pmuqd%i|dkKk5l#>cfN zUccMX*gI7+J>tym>dwU1N7)VZlx=vH{H&Dh+T$xBanVWs&6EHCCFS<~Ik4gLo!GY6 zaQTJD_;g&>+gF}7sC@0&=EEP%7ABU~*3cK~qUt%NWW)1EwQbAS+Z%gTe~DWM4f Dr3B?( diff --git a/toxygen/smileys/default/D83DDD02.png b/toxygen/smileys/default/D83DDD02.png index 41ac49297bf9958696f8d713709c4839c7fdef51..373200a1a02c23307690a734d4e02f463c844fd7 100644 GIT binary patch delta 1364 zcmZ8fdpOg382_>@MIBP++A*i2+|7_nmn9QxZ8YNuWgF|Fn6h(Bx5?#@BxPm4CDAsd$vFY zMZj)~5B+)4_O-!vMB$wfnilN7l zb07kgFF_ExR9NaVqxhP7uLtU-R;yL>3x!X_P}Ab41H)7Dg+<8JXYY1FGs@e_;g<5b4Y%-E#-6ZT-E|h4o@I;P} z)|l2e+Lmwu_K*tznrBW>LRe&40xIq}^T6Sw3=}Idj)7vw!~x)b;YtwUju^F49G_{X zLD-Qz%FgPmuz7BVwRbYdUL4vuTBD&;lGlVFaX4!H!}%X7HVm>u8gHDxGuXfj2<|Se zBqmJbR1XGXQ*@U_*|yvlPG%04T!*1D<6;6*AX;n zPqyDU=z4Bz^e~)|E@FCUNfs|z4~l(> zV7A0m$!gNaI8?7Dncbbb@2&C4UC_9D;FLjn&igMYjqlfG*dM#Jmd$=F4#SvxCcbsF z@-ue_F436q(wyEj-Az)9&Fl{SHd2K*yLWzPT=oHf^IVRNs${6!-*j5UR5tU4qu=4* zxxMI%8O-$OO!ikLDWwEM^YW1ioQKOp%a7*PHkpEV#Gz*Bi#FhKUKFD@`#RqRYdRVE z4419_2-}q_^VTza9k9w?WR;3>=PEhIJWIQfGlr_;Ftu@)h$sT|TS~gFUj*Y0$vn5Y z^we;;p;2VpeSME-Gg-gT3Jm4~7iL|Ey6(63{K|Rk!MyiAf`8HiSJl5Jdy$HIx~AML zTUb`Yy=?uWT<%rPB+H!>9CRw?U!TAi$ZTzHx`j>xyCVeTLa3ovuOb#ZI5Tx#uSi$9 zzcpiHwp#~V3a4+eZp$c3Gk_MbWch@!qG&9JXXJ4P6aWT|!PukS?44XFPH0bOtgELh s+7^xWM5A-PhHL)Ah+#$@K9Kl-#y#YSxa@IEA5-41H@UawH3NPq|o#U1UHUZB19daMNz zsEy1;Gg;=gWGds+HVtD_bS9#K3=v%2xF$B!`t zURRM?O~{5*^^Pi-7DNu#qUs{23ddoRL~)IlB=JI+P~ilo0ykNNYm8dbNa*0r3jx+d zm)lrvwQbr0I}_rOB)<{ELZJ{EQlo;{g5d^(AtuixR=BK8G069rO+g;_s_qbkhnjmNdF9h9oMf8E$tJ6PM{ z=dfxnD71@CP!D%*0t|BZa6@rL;EnMakq1R#+pL1K-OKqTyVZn%FQ|)m8C7aMP1gSK|z$*E1Tc4aXV6qSadr1VdWXG-J@%s07yT z3reid$tCRa!0ski_fV{n7CBZD#9BdUOLRb$N05Y|NASaR4G9<2@jjOj3hs%QXCqoG zC-R3lmrWGB@OplY{5JNrYK=}sQ8>*ont0rLmDNUph)EzB42&At#Jc`Z&M@E%7H^LK zG)v+LbYNWEYJITT8XnFECPoCKk)HJWO$d^W+pSb>_{Zjo1A$|a$H!AXAK1U9cr|vs z_YCzXerYYXBB1>z^QN2oJNrxaobBm}l_%lJB|q&NoE$71KZ@?SKDBf&yPt+0^?!iB zIr^@8ra*r1%lCULBCxuO$ zn+{Yfrk^d>o9?fEYwl}E^AW`zu@kbhX>H@?f+2P@8nD{r>F)?@9gid?Nq3jpL2&7{@z!AH2M6Ex)%mynNMLQUC!RSRDk+q zWJmP!kev44%k`0(3Q9HlrJ@&jC@B% zQ_MPg_zh+By$|IRznv4Z@&&`>{Li=QEcVmKrbqb&HStH1{8Q+zQG`4`rWR}9 d%48f$Qb6`nXbF9BW-R`Xv@?~~Vavgn{{i`$_JRNa diff --git a/toxygen/smileys/default/D83DDD03.png b/toxygen/smileys/default/D83DDD03.png index 6f24b204f7ccb705a9a9c4270cbbf29555360c33..fc4963b714552c55f128c88d6aca6e939ed5f8c1 100644 GIT binary patch literal 1562 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 JX9ma7e*hAd7zzLY diff --git a/toxygen/smileys/default/D83DDD05.png b/toxygen/smileys/default/D83DDD05.png index 0fd3e11c5214f876b7b1d811f6564a31a9e63a72..e6d24623e220ef650985c427e922bf8966cb8e73 100644 GIT binary patch delta 1209 zcmdnQ^^|jhWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DT;%`l z-2YE{KnTbNiahEDp<5;O|Bo8~KWy~>eh&y;EB?Pr0EB?-yIoLpHu3*ff&Uk>K?ulr z*8Bgo`2Sle|8JN4KV|y=QP2NFBLDXb|KB47(se24|K0BYoB4q*QGT|-3+OV%k|4ie z2Dwe5n-^>nTOiv1W}#?b|C=}cq9FGCdZ4J>W--xC)9r%S0aY_5dAqwXbg;^L06FX> zp1!W^&)E66RGC6!&N4DEFx6CrM3e+2mMat#<)>xlq$-qD7Njavz-<=~U>q^^=1;JD&+$o~c**MR_4>kj&4Mr$dwf=Vi6) zhVM8(g}+Yh@s($_mlY1)o#Av@GrI3&+UA9CY6XpM-|!6)ezGEQ*;$tF20Zt!)O?>I zp~}7c(@MwYpf=APM;E*nJ~Wr=yBj-6!-dKXv)w zM4{Q+f7+Q>ZRAX?%T;NZ!o1P0EQo<$zharnYsZt`e_ES9H8z`lD|el3x9sKxiOPx| z*?ltkQ&FX}qb^ zY9s&uuNKf$ko(yER$0}nVfU(4-V;+;OL<+VNv!B~TV~47c7A%^jAaY=uPv^Bak(t^ zw{6t@!*UG=m$?Wr_1!qT@JP||OsTuY&Cl5rMOq!-82&iR8ELefXVabon-=^@pPqgF z>$mCB`zEp9>c3gd4|IoWiEBhjN@7W>RdP`(kYX@0Ff!6LFxNFS2{AOVGB&j`vCuX! rure^nV_6R>X%HGza`RI%(<*W6kdG99KT%Pgi-Ez@)z4*}Q$iB}1Wh}? literal 1330 zcmbVMZAjc^98Y`Ol&Y=WOVKfC78&T+U0$x2y9Cdd%ga3Ishr1j4D@ojs6j8ulGr0oovLO*PDlrq0;)l%Cl3|&8#6-s3j=E};74k<0PyX=X2ZXcu{7Lq*A z|H=RN`@d(RwfX(BeQ)lAAgC9IZa>AZfcN1IO8`ry$#s}*YPXpbbO~yVt1$@< zCWIa-ED7T&aWx&=6ExpV9=ePcq?{)FUf?WNq#` z0?Tnf((VaiA&rrG<)$HBY8z^fh(moM6-Vmp;Mx=o2ojPhz^O#PV$dl+vdK$>y?u-# z@TQ8{=SQ}j>Ik*MjH*kp$K`g47>>gv>B5hAND_Y!CNP{pF>sSk{0QwKX~GL{UkI?K z$Ghn;%WvBPk{{_cO^rs;WHRYWx?QT?gW?oL*&GDn1R73bP%(v+Q!%Ov46I~`x~!S9 zs=zj*5K{+CKLR}63L&9|LOaBYv7IQ8GBhP1|xW{r@*l*Ho%h%$?z<}QaH{L6cOM#Du8p1JVWvfQ@{ol!xR)zD%h26 zyIWZ9g;<)=CBannh^qD%I-s>zHC3Zm)nKNLgsa?U1oY|Q$Ipwz@@F3`erb)d`P^zlf41T(on6(qXQ!-De0=!M z)3TYevyO=immD#+!Z$CRoVEV$y1o8nWKV9ibKJ`3$Hp&Q$`y|mT|58v!|tc~`RSF# z`|nOSOa~S5*Xcj^e)m(w>yusLnNuUy@A&cI-*O9ONB3V|do7KOH_Y9=mR{KyO`V^? zvJb|V(+BR(R(5{d6U~0+8tr|U@wI$&a%yFRiQaL&5z7|ee*3M{*cA>!{@BP&eSfjG c{D*_^0#tow**kWyX21Ox3v$iu>qp2=YGHU`@Q%3z4v?0J>N94J8_rn9$5&2 zc9C516j&=EQ$_-o#u!CUSV;ysxj8{l^)%)a+n`ZXIg*!4S?w(K!w@rPc zqgV2{dhfh>Rog^G%f3=9rqpqMdH z4AiCYCKq4x$~=1m^G zoUgxi@jc;#T^VszI2bw6e7FA6r{z)Wa|HhRhVf*Xg@@W0%%f4>;7~<*>f#ZN_tlcS zt+LwqfmiKScz)MY6~^z*AFltVS67aWE<8oqSYk~&zca%-9-8G`oU5__PIOvG+%pn! zmR_Y!p*^l0G&m4}zxha5ZfZ8qwKE~BZ#P?UgQsy}EFbi?fyCPOWX5k={D9X<+1au6 zGrhM;Wm_%HaYt*7(8Xa@DI#8dX|eJ~E=CDW`&P&C7ISu$hhG*fK^4Z9SbYl>-EgtQ zzq-9jB{O9?N+r2mE&07u@7A~p50p_qdo8x=1vw>!w^dArxHm5PpOiX?8uNbC@8QXi zDAiyYo-OgTHynO&thiXl#CufW?vCDZ)8)Buea`~%tlZtg4Plp;KZpoP!Fd1Pou?ST zdwO7&-H&P-FLb`+!Pm)s5y)L&D_hW(jCebvsn-1wNe$XNB16L03N_Jk4IjBK97>}1c!qlMYiIv$lRpwnpDtdwqftbj9X=-R zYO6FBz2A0uIVtFPjb7f9_3w?1OKj%q1ZM4H(KmI&b5c4+L>X~+)Lfl8ZuBCX_?~BG_)A}INs;BVlrz|peP-iO`H}otxCGf zb5~Lm+WhO9W3vW7R`2x4MDkOkJl0)w^2m|IfKv39sac873gSgaiuTgu(E{!c)7WI%9W%>M`U{^^nd2WTNT qct!>?S@dWIrK=1Ac6li<)s#hLP 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 literal 1902 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 literal 1915 zcmbVNeOME99G_tzC`#xQY|1r|hYYse*bX)sc-t{V93o6uVa2hX+k)-JcEVsNg74VS zdk} zndw5IP+TJyAhG1^X8$HQlB3sy5tN)fiEtSahc72GP(4P06?ifRL~2ka7K@>Z%(NCP zghFvxu9nIOnPd@Pj%ygGU4~)Q=twk$5)x+Aq4E@r0Fp7KS}ULr{MbMT)CvJTfh%E2 zbRtZpUXZ27;>yzDp_AJ5isk%R zL_DgC+zIF^g3$4q%#4f-Mn)h5*DIMIkH@ofu-O5mM1UbvOQ6O8t-*VY0l^G%y;?`8 zaV=nHM3eCqgn&+3`pN~3P9k|ttTl`ViVPXki0YUigT>Tn?0&tJHW0DcKW4m9+91u; zVa!;}fUnTY$$TvL9s`rH`)@^dLDCxjV!fJ73Yv=G@)a6POGF|9I(frTs1YQ8>8~idc}CEehtcMJ%>Bh&#sdCM-(~v)Evu2w@AwAQ;XTLQr6MP`H?b*l)n_ zF>IvPK%iPVHl|li>K(-j{}am>=`obR^->&99jkyC6;9v=6|MtBaS-5_pw=qz48!~O z^t|#G!Sw2MOd;0e8sKGo`RX_5hhUCK$l~#Y!3f*#Hw+7dc`%F!IXodEDGtU&wnb?K9v*Uf`z$&S&Jt+5r_eUt3Cibw>O8ZV`8H!VtBN^8y; zeiVB3a9P>iyKC~=ecd*+IklTV`wlxb&Rv$dbn4FiOGeVF8=jro^>~BNiyF(Wx~5cd zVe8tat9jk-hfBs^Ook`q#`gc3zV3`Hlj^eaN^j4d+rN87J@nbTd-wJ$!=^zK&GzVL zS=1NN85W-j693ts<|CyJAHwx|rOI#oNR?=#Yt!s!znomw5t#C|yF5*0n0T^J;099i zfNi940TWIZHn>hP_*GHA=oxs?ob*{bqf+YU7oluM*p_eUbDH$l*7Wv1)eo_m7li9? zn(j@09=ayEa-;H{guT!SZzrdy8snt#p@~DE*X)R`vGtX=v?Tmuop}mkobmTA@VrBcsHiq8|4R3z^&QH#=?6w;Evki}6Nw*BW)-y-$5+5g)_fgT zb!T881LQnEb;s3py3Y$|v=N?GQeL;N;nDSBj|$Z68)xacp1zsOJVx~En`-cR{gAbnCpu|wNM`NAh+Gd2`N;^c$O>}5TlC(a?1D+|+xAT9^(ycUuAhHqV~g>vi3h5L2|paPbrl6Q zXNTsfIhLjc`Hp+euHH%otP5*uWEZgVDz*I3(E%SvL*228%F{cdzhUK{af&a?y?j}) zzd9`b#?W8B({J4DTsN0$N%1Ufq0HqQL-wo*vR*z-E8KJ~wPY}mc%KurhXP+s%JDLX zFi+GsJ5+buB7U^C{rT8yc;r%V@7eL1j2%d$_tx^cv+3VG2I b_SPf{G`L1o+IcS8{yU5ek4CD5NqK(*W<>&j diff --git a/toxygen/smileys/default/D83DDD0B.png b/toxygen/smileys/default/D83DDD0B.png index fd3e3d2ac45462cabbdd52d34c4d28487982cad3..c637732deb8fb41d35c8ff64fefa9f1dc7d9f989 100644 GIT binary patch delta 1172 zcmdnTwS{wnWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!Den@NIi>Rv~TpR>-lBOwY zi|?$Sd~|l=!mkXQJH7YZz8f}u#$pEtvn=IL6H;g2xNAH)T;fLFMyKzhFRx zZG3|&MIZLnbonraIPTrxyC^7`=eBIvxyoma&&$jo&CBz)(km#`Tiz}I)NoUSe`@H~ z;7lJ&$)~2_F|J}$>Wyvc_gL+zINKw8V)B{7w=)gi)iOJ{>Gl0%QN9--CXn>cmU~G< z*h0aaVk6UL#R{9}xk>_NGDqCPq&FUqV>x*1;;a5S!UeTkJ3laOTDy2o+Z-H{ zREn4Iak1ug=V!(!)HA+m+0kUcn$?=5`fu8b#(SS?5AAp8V6|qbYjAN``-{z|Q>JOk zge9znUzscG6y+P1D;xZkH*#>kTQ7R{;tdPaF8Q+Qe#?~?KA0f&QpHlM&?@AhWWbKA zYx51eeNTMeH<7`KJt_O03bS0C0iWlz=1Jnexmmv)b3FYw)}i`rp4kUM8=G5wmgl!y zRvPuF3Eo(iVSmGuukXbD{NL-$f317qS5-adsm(lg&e-`T>D%9i+*vC>pD9vvnVwy} zhv}}#Pd|LqU1-1XP*>y4io+JbBwFa{;us=vIXQuW(M>H)ER0R%z_D`&PqIEYEc*Q6 z)5pylI5@Z*m$tgP96e%bsj9NHV8w)K6Q@po9ug1|G_8$e1p}*vrDY?NmH=bu4zAFS z1}P5*S(OEBf)^UNGaOh<9x}}mVAS2QFiL>2K6eLK?x#6?4Eq=JWochppaZmBwZt`| zBqgyV)hf9t6-Y4{85kMq8kp-EnuHh{SQ(pInVM=F7+4t?9Iiaai=rVnKP5A*61Rp; gDrFmi8YDqB1m~xfluy)?;$mR%boFyt=akR{05A~gd;kCd literal 1342 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 literal 1570 zcmbVMdrT8|96yCHhGK~L#*Fg-gFt(|ws-U@7294bowPt{q+=WLXb-5gz1q9lYnhKU z4AdA>U-*EzNxbO#w=CppkPIA<$-3 zKt-rtq<5*Q3R{lfO%?hLCX2t)f?J{N6<`)8=LsAXOM;xk?quYg0-EHN^ZU>-41tp> zY^4Hv>6BTY52|T51)@TUz#>8r5W|Fs6vZ$k0~Cu8F)ZS5On^w`C?*$Yf>Rg7TXS2> z-PzB4nBvXu6)3W)dgWe5(JUjK^N$xJ1RPZ`XSE*KGtV22|V*Q7SX8mWKXc&#>L^0_G3 zNHMg>ZQ<)tmJ|l_xqGvrkRtDmJkM?8i$dB7+Tw9gPF6=KApVQcYO~742!atZ6{c0< zY7H-_)FMnPR?ASaS|rwHqG68LVWlcWtVCr}r36P2L?gyCaYC(;XvI=QrV^=CVXV%{ zu%y#Mh3(pSyHi-n8?kb=n<81-ZK7#=xC8PlXqILwXcwq1#K6=do6|~rnPs8!yo{Eh z+_nvrRqLi5;ADQ~w%6FlF%^a=)r1UHNC?(@G-IfH9@= z#u2Q++SYciZTW?r(&YYk-{~aD@=YTX_d1s}G7oAyxP;q}_iIFSzep7~jhpy2p%s~X zM(?TjRG#27-#Tb2Y&jRoRBfzsJWP2JXnB*H;C<@d~<{yLUl7 zvDKa0uP*|l8OdF&^BGNJJH@J!!sjd+Thxco`OYj#z(D1-m`UQGC!#f zGA2ktY+VsO*4GVB%pboXs2VyyGSMPz@!a-Y+d~4ecy3){4rn#Rr$y}!2Icp8xLOJ zy{&s=^1YT*Gd2%?yWKqW@L7E0_4_xUz%@&Hk3HCz9Q$WhV#C7q8Ks_n!+U+D>X@4H z#$dw!-G;9Y9KCh>=-u|qf3Pyo(UgcEcQijVwXYoSU49rs^;LDxN7@^OpYJKI`|an{ z{#|b$sHc2}g+E>S;p(a-GujUQvX|18{1vgGBBP<_!P)0r@|OI7;!JJrm8ZJu^T$z4 eawp;bmp}wy?87>{qP}Pi{k3%(193{VHt-M7K0~Ph diff --git a/toxygen/smileys/default/D83DDD0D.png b/toxygen/smileys/default/D83DDD0D.png index 2bdf40b3373a5b728630f1dbc44d099a0916f022..4f7cb05c18ea0df251720c7572945e9a207da16a 100644 GIT binary patch literal 1583 zcmZ`(c~H|w6yAgc6p%xNa7c+>Mg9*10aWpaq)2hpq{7lk%ELcIvPjx0U+HR0M2Ou-a`^+5rAY2 z083;5oQeQIF-zP1_X7a)^z$aUudlB|F_R0=KVxEUX?$j}y1oVH<&8utily_Tk{L!a zyR)-XCX=nKtU&b1EV!QVrr&eIMDl`Z+uz`(O%)>{-F`+-29WL<0A2xU}&WCq3~XxU}9$O z?drSF=;(a}LHJlSvmleddQG9lZr0iYEgQ1CT@OA3fkZ-pL<9nE?LjG5PCV_~%7lA+uLg^a>hC~n#hv*t?4GfJ$=^GviC;J8j>F62w_y<5|m?sv9 zplC9SNPw=zsBn4>x5Op7q8BL16^)C<+$>(9lvr7GI`bNFdd(G!l@hU7;&kyPEEdZ} zFOp+Z)7k$dn4ZkzZ9eTanv#i!87$*O)P?~-`Y{WFROjv*PVife#PoeTtf)&YM$iaiGKBnff8|# zN6XGcm7I(bmh}yPlZrli&654gcOU%Dbeszy4p-M5K0Y8-9M=i&n9COjS)(M?N|Vgk zvp&`K@xxKP_!g>4{KW!t;&As$YVTE#G7Z?{RMiI*0Y9~P5H~r2O<7Po@nVQrQrDbq z)0P)N4RqKWuIXD}0u$x#raBy}2|KjPAyq~sPyKxG5UY$Xuni;UhY(tSxmB4Bw-1~+ zK&YznHXA-FYs<05#msiFyn~+;2~!` zV&4Q;ah1EIRlJhxWE1}&k7m@WSVc#Cr0IQtP>Y$txICqBpQ|mi?Ijg~M>!5#`Gb7J z($I9XK>{+*Y9-QnyOvge7kwyvOz7EzAeH_VTv*flSJ{Y~)sZ&eLdM(R((vUEL!Dhc zvhIfbmiWu;cm2;Zwf27~HE-C+YGSs|Utl}xugoWpwmDg<+)Wg9x)fLvxw@&D+*$bA zX5EV@$3<*H5iN{~uXMW;R+5M*tsOo8OzshTQK@%75V)CdyL3fWho9y8a^;~C*%>Za z-==WoIX{+rjJe~o(W@T=37)Q9+w5-V4Fpch{9xZsCnT>JH0!#)sB?j5Z&eOmFh$Dc zC+#eQ)x0C5yUlWWcG!{|$G&sOth-;^)A*vJIKAA8eOuj%k1&XOGL`*tqQ&SBbBWor z^9)AJUG9R5@#yw}r|}21BY*R~2OmFOonu6#vFT3j>t3_!!@(B{RTZv1hMuMn@8&|% z9gZfG$x(C)jY5y&!0c1h2)hVsDfSlSCFcrxk(_egkz4~~wCPS`05`rFr2$Lah_{T> ziwnl@OUve*x|wgY!NPI}7C|D3MqIEyhqR2Z-?~##Q(0MAQ&ET1#9(Y~?DyF1wYRb5 zj2-=k5=Tp*#ZjV<#;9v1GFeF}Nh~HF$7+tDQpx1g@Ye5Vx3DuzVHv!UrY*i1xf~R! z*ZF|S@VS#+g3-Z)Xbie@(80pd${Asyp+Aq(_)1ex+t5(U0HqFgRiTB-d&63xeVMV` zgIH7wi{=!aM1vf#!PwYXV;rpa*pl{Ooc7u|**aKZFisduk+-D%KM4s8Y8*Z7{}W`I wF^W)v-lqxy3_6QVNumK4DkF-9#wSo>Xha%?nwHp4gMM)U@a_aRzAO3E--4Z_Jpcdz literal 1574 zcmbVMZA=qq96vzbUu9++Cd%!I1kBodDLvW?7294bsD;uZ;>M&$`v5oEUb!Ann4wP1 z7Ij9mxBD5DihjUMvpT^KL^ow|8kK;GFGyyPHHg*H?uyjygYm;IclSL1=l;Ln z|NZtFD>kI3tW1F*D80OlHi>Ii*14wP-8US4GV48pl zFt*Ot5l{j_agA(stx#*&Oj$XHl!@p_Jr0+MhM=B|orYgda^X+uia!6hC_Bya$Mfjthp(@l9)$RaN#?jy%20xzlv zEh^--Q?&*otmSwB%cU}j6~l3uB&B$foFws&VFJSm6ccY!f)`P8k|K)Xr3(?Q@wP_F zMC+GqiINIw76ccCq8%L_(hixFTd2tp!iNZg%Hf$>P3?z|`i4cu0qbqOrz zgd>bh1J@>~5Yf}uAvjzH!y96!dnr+3%1{sELUAdEIvkO>7PZ}i3HqVjh+m{O)>t05&B|zl;dq!5-(P26c|kt8a@W#oITnB#%507+|8T|)|yGUU@PmiaUJf$NO@jIO9P&5 z2R1#=IpD?oQtaF8tBbT6Oey+L6Op*pYK=mvR@0gyrH0lhNn{CY`#(8DMQ6}RbNr`S zqDNu}Mz(KSUliU94{(YT!;7QwB5_A01jW{r)9Px^?S^1DTpE`&=AErsUA6@p_#kT& zG@A;KCk(IJmwhoa#|7nQwa(mXIq36-j;?c0U!OmIc#oz{Sy-8set-9{`tP%8mh-by zeHX(%@4>6R{?P9jwWQ zKH1kJJsSSJ|JZYNUESD<&wjn!gI3m`@BY-dbz-O1T$TUcP^vE!e7?hf|H<;?-oqaIM^i$~Z?TRnvZd^Ud)qA>M2InRwyk2|-Cc|aV zW_ej_>Lz>fA11J&rzx=TYJL-|)Wtk6J~_S+noAvmYDhBSqpGSe2WLus7aqmF-!B{V z1)hz5Vlwgm9<)BkGMdu9IxjCSGqZT;^3550C>S^w_jQc#jyD`?yFc#>_dEnZv9lre z+Ejet@#Ox&neH8dkSe9QH~t0&-8wrz#TrY{?7I)QBhq|+PtSh;&|_{Qe)}(7R{uT! z&E8|Fk0oPgordXigIrDAp8%Q*>TqaT`zk79W^<1cHEqoIGv8pw^`{GZbGpA1do~X+zOu%HaGsh1f`aY1m za#f~$Pkcktk(h5h=?t@Njdabc(h~BX&+A%NHksBN10S%jzY;$@w*E@o@Ok5r#I+gU zHpLt{t>0{V>EkTVyv*`}%)9Gt%XA83_B5Jb@YTS!{@>?-(&w6sL4CA%Ot34`k9 VG9HXuw!Db^xyyAG^a+h+*FS$^P{9BI diff --git a/toxygen/smileys/default/D83DDD0E.png b/toxygen/smileys/default/D83DDD0E.png index d9a8b8ac5bc26a657375d78d61165b9d622677dd..fe0ace1365f1521a48bb7a7063404b5a32777452 100644 GIT binary patch literal 1588 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 diff --git a/toxygen/smileys/default/D83DDD10.png b/toxygen/smileys/default/D83DDD10.png index 42104282a601af888213bfa0b2edfa1aacfb17dc..e94d395ce471f836c166f9fdd9a4893f17f9e211 100644 GIT binary patch delta 1581 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 diff --git a/toxygen/smileys/default/D83DDD11.png b/toxygen/smileys/default/D83DDD11.png index c60bcad4d976965c1246fc34dffb6d275ee58710..37ac8b5cc151b7f21a1786ab4fd15450053287ae 100644 GIT binary patch literal 1574 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 diff --git a/toxygen/smileys/default/D83DDD13.png b/toxygen/smileys/default/D83DDD13.png index bfc3b9b4a70c0d23a546ed992bb8d765c02459b2..a0f831149f8a4b9766da89863cd8104be99ddfc9 100644 GIT binary patch delta 1279 zcmZ{ieKga19LK*brse5)8mZe1J&^iQn!BjTv|XhviW9P!L>OkprLAk#4m}tZMbDhA z=2?u1-P^T@GHp|ADGwRNz2#wb>sG(}JEwo@k9*GRb3W(u@cx|7=bX=bj0jU;$3_7F zR=Sz(M%;pfzB_yYs5`glGtB^raWQ1-4gj*21Hj<|@CiwACIQHR0C*D(0I>uBETc>k z?2Qy2?Ajhef?>E;+Vwrv_hjD?wcJM#G)cdwyrGqi*f%Cxe(fou_VlYqC#SU2?_~1+ z7ZX$M-A}4oJBBBwI(k)4G{cgvKBZ<@t{xUPNpFj#*K5SxPlw7x4G&bBOTt=Y!dN2< zo>jpAXkON(!hcEO$p(0`{<$a-o@j(e1kcLp@bDdYphR&s3RYi-`*<*0eo9^v_Ia=Z zRu;jE0%QZ^h*iFw7m%B5nAD)%07ZZNL0_+?B4V%fmbIX57Q1+k%`wYfrA9jR4g_xV z2RwtNWF$Z{DLY7LE!q_0Y$6-$D*!-0J1ZoNNu?dZG15}v4jrcBn3)VZ?r0JN0IxF_ zLL;AEVM9jQau*5LXu)XOJ%YAsV!p2D?e;Ww<+G;!x_G>~p`nFR{&us15Y?ZcXnil_ z=cMXNi=l^WqP(+pk)>TPQpEeP$FnnTm2+(pFISN^oJi7nhM#e(t&Kn2giSxq5ayBa z>Wt#b4?+nkWA%IhwBKn?^S1pIwV20=f3%T3)|ccGrFAS6+jk{L&}ys;0~`0GSr|Tx z;oa@3b}^ZWjkFTxWjpLs-DQ{#E~VOK@z18V`%vuTG|=;Ci^82^Z-u45UMW6KD^NL^ zI4ec8=7walptCvFEbQSXyeV$2WcC;de^k7X!}CMw92|rBHs(Uu2gBu1ZhflS9E(qIvX?qJbDUa;@k2PmWr>W@*p)cY5`04p;I@JS;(*?MSVUh{dR;oycL~ zogy>ghIG~e+|L6{$)cqnd5M0mrtqtAmpb+ePny#DZ{>LIwZ~zN8Oe*in(ZUwt(x3= zVnu_K(=A33#NL za?NWA%PT1Lk#YJOqeXLfJL>9t6a6+|6Z8LUSGR#*4%we7++01@WS!!JHmR%;DGkpyMq=o zC*m}?>@5ppE||ENY#NNXj0j5>w~?S^P7>#IvdtNWF$=8AP-YC1Wj+TK{Xq7?%iTTC z|GEF~_kX|l%gajErmatdAZV>?J6j1>t@>!zfOp>96AmmnilbWbi1kW@lX!^si*-Eg z3UCd4CC~ZW!y+Rz6mlFk2hDNy%is=n#AfU#zs4Xj%{J(Cjsx5ol zLp)l^%VMkK1NErSkAp$(o^D801l~|PBmoqKYhguSYk&_bF4l&CFM7Y=r*I4-SSv#~ zY0~Zh0%JE2PTX!a<8}k?w3y=@t6>?F!-5%242v@k40GVL!)kHTCfZ6{FsI4rjALCv zS>b{{K5kb4c9*gAQ?Zm?;yFc>yrS3=?|||~Q4!@vF$CK^1YB4p1pQ(}epxNgQnV~D z32nUJDTx7iF~5|sihY7M*%<>#GFBE>wZ~C$_(siPb5`r* zuu=SFVb|8V_pY|)#@fPfjvc?nw&b5)Kb<}Wh0kSu_+#wPy)}~yGre@=+3dX310A+Y za~Z}R-D&S0(tJKYmHEQSPoI%>Sz^`69_K=_II%VMcC0jG^qb+Mf1Rr_R`TQ>Ea}P7aEWRB zVQxxY^Tst)c5$dreB;-W4s?Gw@zLPu#P_=ni7^tx@=~(1XZH2~aOeIee&NiE(6L^q z>9wNinhEPE`x)!F<=CFCelz>UaE5Yy%9P^GS@*}fPVaO@=b%-W;25F~UYI?5MU%e& zQpW2W{?>RS&=^_K*_Ju^?aiUg?*4)Y*FOHe_@@s{#TOIPp~3sNMm$Z_i25gVIm*}p Hrnc)J&r0ja diff --git a/toxygen/smileys/default/D83DDD14.png b/toxygen/smileys/default/D83DDD14.png index 937d445a87e9c2ea0c17ae312bdf5c981be8570a..fefc17baccb3f487dfe680737defde17128d2d99 100644 GIT binary patch literal 1761 zcmZ`)c~H~W7QXBd76BKY$U_uVlu`o-Xr#r21PG7_2sOAMAqhwU1CkgJ)QGqQQ8sB3 zwCony1f(uNC?KtZ23dn~gC-yXParHoiNRlg4DBE9&AdC`{mwmi&Nt`Ixo75Ze2E_V z2vY{VNF}E80TmVp!`}zdngd6}tP-iN5aTRo>f$r-XJ?Z*=nQQyAOy$*} zk_!6HFXMj#R$^Ad25xMG@iJ%23dG#)eOX&OxbDR?kmd)j_<)}egS@{d$rI|O%s!<;@6O2TT}Siv=4;Is1>en7 zZ~4?<2ZPMolx~>5t*ZPD zseE>1uF-$2P(Tv|h#7w0c_giPKxnyXq3b$Tj=!3!PCv0@9w^TYGvV#!?+G~Ft1 z_L7xqDH?Y|gP}{$gBC@ZQ}_Mk&3I_KO;SyoeR&5ODUjFM%~h_2x{1)pk4t}eDqitF z^=8a9dO)Kkv*Pl}mJ>5gfpZU?7n)FCI?2#`9whIF7RMp^Yegqj-sC}B{9mz(IH zg&#e)yJ*5t&)esr>pp}?=jJ0xcklSp($}S_sXwJ+v0)Ie{>#^?iI??UlA+g!k+^m4 zH(;$wsJ91J1yZ#ggdHsgo`BOD)iOX>?OlywcmklBcshW@@F%k@VouV-BBE#(jD#4P zMSOG&0K2c{1fsLvBDDo`sV-j`H~U54eS1szYy?@`YmKW_NTkn6dg z^!Qw-EZimMolbmlTe*!1rDAj-Iy)y}L;6z+lG1$h36WXW;MF6#ieTJi_KwyKhpf$K zR{Y2#`>J2&(b}Kj*3s<~9n5}pY>AQ@4?Pf%8rvh(>3-j`lc&&q$#Ie|9 z@92gBjM)qQF55BzM}|0;>Z2Nf7Km?RZ$B|j)IGK5y-;oLtCQ4GG#YTLPq*yzhP7XM36~*Ty|_y+p6;vh(G2^zmA?ge>!6S;|Z>3&f8|+xH`-`ec<&Bl%5i zUC*_~tpnL*2A2wEvff}VqIO@=uRaypRj-}Q#FY;v5nCoL2`Q^GqHP{W$jY#wz{ElC zZgu6)X;&k!p&AV_tbt}H^PDkl1C*t(p6U{XvHs)Nje@rt3)2OsNDXag_(`)4k<_Yb zuBPTX{nb*(XHLB(M;&bvUDcoGK8zU1<>VpjJkN?qA9lF65t6Z{nyCkab`(BBk`K2^ zMA;ig_J+t@jiS7z+cg=EJCa^E_26BaQAu~kElarvCf=;)8Sg>(7g^a}si3znsV%C1 z6CR>)rLx_b2dzAYrTyAx#hY!89>SorP0Ot6cl7+byoDtB;gjIv8g9my*i3-g78*Hd zrDpzp&GoErO1<(XcdqH)W)LIVG%@4uTN5K|;e&aLYr!HTr0I(~JuhE3S>raX@Dchi zOGoJQ?~)t)-_&vE;vJ0}rgt5@-(+LnNN-ksWsP-qEirx75fcw_yEl$ErRHZST)u@n za{-S<2mGs#5(3mtn46z4>}=PTILO#WB({!rd{rx(%h9oJ5ekLjl*sVuKgULdPxaP> zkjWBOEHmztY<_k$Tm?~c?;hgwXT_qHx~7J1jjD!NtnP+sdu+M6xQG&Zk*)gi?&qTjxoGzvh{0$-d=M^>a5R|cb_pG)NaNRqb>%s* z29+ARCbk2i-2*+MzGL^OW#jR;Hl|AKL&a7|dS semTPddKiO6K1l;;Dm|2DfsZDK)BI^!W4W2sy;BiFkT}(*wU!h$m6951J literal 1637 zcmbVMdr;GM9B<2bSVd8sPdsA5yLm#>r0>v9+9pWVB9=OOn;tD~Dy1~Z(qIcZ6b=wn zrZR!Cp$NB4J(b~{C(Pk=a>K`bFr13#a1*xa1Z8us=Q(95Qnx=m|5z@`?~%{v^L_lj z-&CfjEsEsN;&V8hNPQ}5WXGt$6A{L~V;_uI*`E=jVHgSlL9f><^ooTfy&8m+N@YL;h7(ytqNjjhtiD9T6BkrKagU95 zQjC)%fPkVko6Kj_0@l-KA-E}n;khv38A}wKGSFwGKu9P8-R?kKqi7Ff#Q$~Ug=mkd zfWko|?jiGO8+#s(xFDI$-OCLHfUGyjGTO-=iZu@p2nEFinViE6TU`js1xlQ z&MAM%O55^tPsYUxKLHj`Ru{z(4t{$XIGl<_VCDU=@5nS1*=i{&$Q{Vy3Ny#YKQVCa zzxG*N1+#WhQA9I`Sg{d?|;N1I($ z8-*wOPFItrRgoG#x_BP&Xp-@YWp+VnrS8W5B;L>=?AGZgk=@7b9ev#Wtt*L}{ablW z4$;-pfz8@+-c#R>q;&L^mz%R_$CvKi!xhDQrX}5-k#Vnl%Jkx@*yN&d)q@)k9N}%^ z&zK1t%j8!kb=)?E*mUS^Y((9;_``RDW*(e%cBEUjV>7 zyu}{{-~<7HaRvaSA^Fnf8<9Hyrb_Zj8+uN4JbTh4n_a6qmf z$npZ0LP6>G;G_qL`xfw=!3i=D?ON5l?u&X4)H5L^g#wb@z*QEg3SWsetd$5i_LpX67j+aA&UT?Y1Pw%f!mwTbX$C1v#63RzPh+LShTDvS$bF6C$fipy&=gDlehM1 z?z(>Z)$?N`73yTo{>FxCjzBbbb=o#fEq1P9&&+k)pLMp7@l{{fBXF@R_Jib0E#GpUZg*pQJmFmHVGOQ&nl}Y9~_%$3{u~5{MblZ zc)^6;7eU%CXl@MB_RWhDROZ;(OsAQKz{v+k;gJexAj=zw4}lJ$uJ|y>^8;u7z-eEQ z?F}mIfx-ZDkpM=Tvf)v~W~ht|4S`ML#`E`C@fVm3rVZ2Qf?-b5=bavj#@DXd?{&wS zjP6Di-&M7Djx8-MFHKE-Sbp z(%VRd1%gNOc1H{&4A3~;&7n)@VJjy86iDa!GZQSh@v#w6M>!U}BreAyF@_6()9J#X z{qg|9=L! z?YJYJSrFEcn5YhO%x@e#cE?61>3D@$VBAmaH|||`yE{~Fc7(y|pvT~c6CamvcgX&s zCrtjjV%)5|w!<-fZu5n~)Iax}tD~%@uwI*H$^vHW%<~&wk$-$=kUW`pusR0!6_dN? z6g6j}T5ee?&Cvh&me{?X<$KC4!ihrm<#ZCTh^3B1==!FRi zwQhDfa%7^39Diesw}CWk)Fb!~yWH|TC>-*MespEWfaE#Cxu!Nc#sGsIIBi2j_X|1l z2nSN~vGm9<6-~!nzo{;cvVYi*$#A@7XTP;sm~rr4^{5NY%?fL)(e5tLAKJknR#_1} zo3Z+}%&m#Sw0#=%H64+iSyP4)VK4IZh>;V9?I4!pE;SVz4t@043-F$AG$)^Oo2P8H zor&Y3f0<70vA%Rhkn2`$xMgEatD<~c8-M4m);hl&%X1&itoUNxT8>wNuAw>oCQe=c zOE9FbH}*75-rAZyg1M4HaM@JK8S&oSN6)vd=$3x(ri^!84(~m?B>c6HD@jc*dEa>% zL95#v)fS-+Orbfa>eYQ!ZIepP=r$dDx_DlEIDPcz6P8Ib%!=7*6qi5~1&hJtFc@q$ zD>9PB2D%15Hf7)&q|0iPyGe<2B@g+A+aKZIyd58&QjNX0My@xSozbcdk!#S#r_fmf z!Kn;Siidj+`IE@DHf{!$O>6o3YB*!n%>OjGt+4L;k2=@&}XJ<`tw6-M%+7d{+ z>`BDkI|u|4f$;RWc;sINF|q8Zh@}5taL)!C3Ky8J+z=2O!AoGqbASswHk@NYi(y7` Y{5ec^Qd|oM{*eHnx%*ILu8j1*0PQ74=>Px# literal 1703 zcmbVNX;2eq7>*zq52`3Sf>qYwfq^8Og~TKh=pG5A5SoTmG9zuXBnw2?Y)m#1prA&f zRuB~Mps3aAP>bkT?bI@uTD+x-fL2DViVrTOdUcY_NRfQptHXDaRtC*t#l$(SxG|fcfpj9%Y>{*BH67sqW|N$g zDpG@LD@tUU6Y_0DN`A7=n4fKwnmBQ>K#Wty5Re3o15Pr>VwXAPoIze0v-chgIKZF^ zoh|3Qa!Rk(04QZ601-cmX9R^pKrH49qeWt|FcN@3AtV5qTg($i%S2)sG#?ncIE*!$ zDMO~kltZ={NzTclX{$^i$j!~==SJ}_drSQYLWXR)LTY3P{o$*PynY))N1^@kVXC zF3(B`w1k~<*o;g)GQx(zOzz%p$g9YBBm2N+W{QI6V3g585*Av8$vMmyzR7HoK|-Mz zlOSRxEJYQJfS{mQ384}Zgo2QAzG#@^O;`*OgCHV7FbGiyg$hU_j+!q)6-pGvqCp4) zhp{S)oyILjV%V;ku{(r?--?x?HUg(9n~tJ#hC4u$MbVT!i?RY}iWmq_HCs$nu6>@j zJg=g~2%C8+VN%*C5*W;{%=`xXVmKN_K&cdwV30R%7)B&g7{-ujDS{zVF=q&C`ad}n zFwO|P&GDaR89rh<(7S!z`i$^;cnAwKF*as2@WP9~u~+yXcff3Azu1KE8F< z{jek0QMYGDRe1->I~HGue~Z;D46O`37e6i`a6ODE;wE2;4=t=Q6;->Gp)UB-$uJx4 z*Kr~)Y`&=a>`2Cz#xGmlhvZOyTiSlfsJ^c3drjxBCGFX_&Bq#Y?i@*bE;vfq+|}y! z&zE?r$W&3o$oOy_np1qGkUnw9YE56I*83)1IQ?s{t=(PxM_)^J;x`AE1{9Kgiod!R zuLSGskIvb9@M&zjC)TfijU--F-<`Jnq%>ywT6UTH$_6`J;O<%u7V`fr-W%r_pQhUU zLq6Wdd#|ljBl54RUeyS!yw&S|e)*Q@@mDhjibK!1Z%Z~NIh@;Gwns)9u1WnHY4-a~fu76&mo&JvIe2#>x1szhPTuDFo;)7)s2|n1V(-+B6a6y2 zomAfMWA$GTpSfv!q`x67z?@w)?R3S|{E#iPr^GMG(;*?q0|}j0(tL;2qQx6NsW2Gl zo<*m97qnTab4}V9aH2OTC8WIf+6{Ha$^Kjq#9d*Cn=o}n^7lx!mgNqNT(t6OPH@lf zBf_b|p9cUBRIZu)%zuq&blKQLyUJeFblpcsPI0%@sKGfi{CD{P8~;wNG@O3aT-Ryr zY4Kg$LuNgF=Qt%_CvAUtc$=e>D&m&5mi@FZGs;-iy<_&Q1fl*^R*e7L<^d>r>pQW^ zh)U6^Y8PC4wKk%_$SwVGUeAl8{Cmez@0^&jlg;+&ULA(-ur$I~si351EAH=ZAIrg# z-t{|Bz;kX8e-1CmFK!45>HrsNN+#x4>N@VOy)+)se8ZR)cJ|%u3GKQ8`C@e zp0tiH>4=$RIq+yw#HFg62GjE(kE&(h$+AVgIf2KTQXVHRAHi~U`D7*U`Ds8NzOe#u)V2TP-O)QP#oFYLOEt8C-wsuM}sH)P@jvl(4 z(QP_ZhmO`#oC?*_q0*vc{Ujl=L}Ce!PUf5ApUxli&VBFR-+gy^@7{ObrBGs^u8x@w z006r9ApdZP8~i&F+o07RrGElquw-9?F97^;b;l+}3&O~Rpl|{JV02vtou+;!yO0R7T^8x^k_|W4doLa4hY}9^!Y9etRgMozR&fYDWKX3rj46w%_ z0?o}K$ZF|Or=%!cUAML%xk7Wfh=Pb{4fir;+rB$$>>&o7SiMBvQaC=cXIWUK2N zqXH3>a$d0{l`E%Y3v3?vfg|8aWa2sb2uCnH!JiaMM>xX1;Zdl(N#Hd{=)~U4a3a*P zJ+kB7$gcglHw65q)BN?~l*PTzWS9>I*i9yc^)$ydz^_&wc1E2O}ZzYU>lWa`$3 zF*eZA*4UMQGXx5x5g#1r50q$G1VI4K#1n9E30zOdR%^gL>Q?~J%*`Q1F~cdDNP0Rg zDJ7MPWM6)pM7!~PEJ-k# zYMa*SVRBMIP(3dCTCa52nID`samKP-RP+$fGu9e~daO|nUfZjKY<}$#=BBw@U9P~Qd@czD}uh0~wP_`l8Zu%`0P7Z&Neh z@0AsqG45}1tG1UFV`Fpw9@Xc3bfjCRzDKqe&bB0n8sbFTA9rMTMDF1VL`{wlIh0-! z`5l=Y^+G$;$HnT91J(|6@LNk~V(?!3yLB~HwMNylN|Wk)_u-hMfo@!HYZph)z({?W zjs=&~F}SJezTYN1id5fNAll{4HG=WGhx^A_9|k$BS0nvzCyERKEY32TNW960Zeh#B zMKKd8Oe!`ZoeDAF5c<#|6xt2t>`HP*V_gno52GE?Xe=6C=_`EvABQwrVoFl>|2qiG yqfH=(J^yAnK}%w0Qqrk_Zz3(8io~Z;lBwZTN@Dg8Z>Z3B4FGr?(f^4bIsYGPcITr2 literal 1568 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL_U`)6gIl8*Kxj7n{ z8@d^rn7bI6!Ss6O6_+IDC8xsd%>>#D)oYAbua$FAYGO%#QAmD%4lD%(WaO9R7iZ)b zC^!e3DQJXe=B4D97i)r|2jW|o)S}F?)D*X({9FZa_*!LRv)|m@(#^ue*#+c!puZhm zjVxRoUCqrLjZMv6%^j7XdQ->=Gku_A^g)RODY3wWfGH5fgeQF<2cCIS^ME;~2$(fq zPFQHfz`zvX>EaktajR#tceZe#$no<&YLideTuM>Pl*_deKH9ovQ9ytJhwrpSM|)qF zE#?-S*fjsZ-a3|}Ti0p5IOZpCW?6Kgz^s&IXAdvQ{JdkKXWEPkHIszP8hbya@Ay9N z{{MT$?<6I9-3k;_H-EhNtE#qYZX{|?|E%S+nF7ibFOpu88yvFmR50{?I z#O7Z@hnP>Z&02Qw_@;o>@=i8VZF(=i8unG#%q#f2A=9KUYGxa+_D^MTOqO_W@%&x_hgSg8 z_HEO0ogXgX3RE@OCwPyg>vyQ|&R$QhJVEobMQ2}>aPYK?m2fZ~e9zvs$YI{(2e+n* ze2`&(Yt|UgdbmU;{cW9DtVMA1+qwBt&*tu)5*0LAkX7c+hL0uE+?GdXZMks8fRCre zm_t1M+rc9)-fAK9o+tW!aF&0Vwwm`@U8-8mx`}FP-Q0zt0u>gPFO_5T<{nxa8C$(h z_@YXy+AbNcRqn^v{y#MNVc~oZ)rEJ$`t5F36~6wveD0sc%g^WUDh`nlwds8r_|D#S d=LdO4Hiop9Zh>~nQ8l14+tbz0Wt~$(69C>QLY@Es diff --git a/toxygen/smileys/default/D83DDD17.png b/toxygen/smileys/default/D83DDD17.png index fbab54d69833c41c53a84184e1371d771705e8f9..d7df173df5d2d1767a5aa7a9e3ab8807051d2b6f 100644 GIT binary patch delta 1653 zcmZ`%dsxzG7XC~?6V0uiMjhK{utFVO9dbk+yNP*$Ev+G%(VA^A5VHr<3~zWrQLxM# zpoS@GN@}Qx5a=M9A}MHD;(auoF}F4Anc8YyH3#?i-~P4VKj%B=eCK`7dCw=asj(?_ zV3`8|Ts>^hLIxIt3&R1>QG(oxfp|* zihFGmRd0v7A0kur57kJOOm15M&#!69WEJqLnq{)?euz6MCts~Kq;rZO-1`qS zg_ZSLw*-ZE>Y-L-Klin$wXDLj>nu+CjZ!GsIWRIlGe0^xBfQsgnZbnKR7vE*=5{EX zkdd3rz6pT{Y8&rJIubJ2qBdn&-TkYntP9bx#lqTJsVX(E5E`Ra%2GkSYaWe2xLiS% z;^B~Xcyw&)7f7+BoEuO?*>9MfTbx;3(mgSj)k`3eAQVa*1G+$z(1@3>r^z$}_3fRF z%HBFzmsF#Jgn>jA)HXxwu&C)c53MbOK@5jB95S^2rrwcmn#!;Gu=EApBtaZvt=G^c z<_)VflYxvVT#H{D`(t`P@_Apf@mpm0>J$ti-NKoMVBw^86D6*d7-pq4QA11;bNKXX zI083H8=SS=KfO8jk-U))i!hijys1STU3YI*K2)&6H0F+)QH)o zlc7Q22K+C0$gyA$!dSr;FD-Uiq2VKb5%mD<$Yzov7{sU)R6=4rEtVdQVx%QRqf+A% z0Prg+KZjGzVJzoge|D5@ZMpoykEeW{{`a+Y5%14K*S6+<`h=F79l~T+XKinlXT6hr zFIZ597QGR)ENb!y$^TmVP0{{K&{S6UbG7BN!V!&r5HU_?dC0$_w>&jm`D;#5Fr`B& zq;O80qwg7$worRQC(#m0gD-pIVCCDa|6t41L9igofdcl2F$?Jj+1n~JRQ5hQuA1e1;3{d%s&+a};+firdzE2f1 zxoZ^LDtj89Cw71P)8QBw1V1;dsrwnPq|5oi66@XRi_$%xQizUpwKh|o@lIji;uC+u z+#;m`S)^4(SJHhPMLlus7{_tl)xg7&s#t9QDpy-|e%9N0u!8gB25Bsp&&B&)T6ul_ zpq>79pYSHkZm%fppt&cvJQtAaP+!PL^jYuT?yPf#BkA;@^>-2PWw$f6hcEYkO?Id12P31K&ztQ{3^ZMx zwf&=_cP^nRE4}(3###%4`dI8JZSFp+>*VocaJ93<^BA5p>F`Rw=0RWq!{GvcvD-g6 z&?g-y1mQ^b;}kM1}T^2!B2fSs;xIqR$uN~PETc~eOlk#uz>l%;knk9J8dXr%aY5*g$2b?u${WW zoHsX5<4^sK21AO)Y?#p~@UnN>Q5iXFZT;=fgx?F#0o+WI9vF+>PXM5YBtpTnA4 z1MsfrfcMdb&v*ck0h$pU!JtMlqWvj}(U1XN7%wjmjIYNbZ_*)*|2J5FEXEy!@yB3z mxZfm@BL6Fhi>JoY(*A$JulpiD=Q+7p0YC^250VCw+5ZK!WEW2W literal 1712 zcmbVNeM}Q)7%zUHh{}u^isCrSHZe-CAGEZU3T=-Hk%ATkG)lR)S2&@)(jK&61SLVT zp+hH|Y$`GXX4xEqe1k$Zr;S5o8%|I}AP7|vI|VJ3mhOtc_J{G0UGDCEpZA{M@A=Ms zwj*-evL$PlkVvFu;bD-3SU+$+i(QGg&zJ)y7B4(B9*@S9xE|IbB)$TZAwakaPC_II ztjJ8ei3E{I^Oa~!JRUEO;L0%-1$OFC3@QzQCXs@I4H{UUg5ZD*NkY{;vbnjH44?`g zIgTl&iZwzc84b(QBGFlqG4iYwIY&Vb-U%H)1|E5umrLxO#~>M) zR>4zv z+Paub4FXCK9hR<@6ZKH~&Vq^Dz2A^ik?@8arA3LNz^M=>PgfyoJRIVYi7ylds^HRT zG#11buta>0Fq9AkLMlr{7qXdjA(bu)WX^KD3rl5+*a37}0H48O(P*J`K_F8EQ3C^n z90(GKLg%pIY8?)%<;bjEl(0L4WxN;56>1R}$FwmRmO9%3JCZRR(~r`67L6~2*i1gd88@FPWC#N}0tSa4zzhwA z$TL{Q|H&ChI0HJH<3G(Zdqi}gb351igfKTeh?qxxoS)KFkuFb4!fcb4bL)M52UocsarA7>vF<^UGE32k<1PiBV7v9@pruS_ z>ga9IPreo{k6gY&9$l~)4S!)->X_P7N$qzT8wPJ>+Fy*6b#5s9Dbw;=e5<9mC#SbH z?W^xfFQ9AO3!c2_%W>3D2mTCN^}W^Jb?)Tt5>D-K*CpwR<~o&i;mOm#OY~Enrj8%I z9E^!XX5!27-KbBC{S0okHFkQsHuYSL5EuXPSLNlZs|qDD^fMKxFJ(IhyUZ&q_BCp) zeL1ctb1r(1l_LtuZE0ngn`h*R-n-v(rKg+i?Js=dC%1B&W-U9!VM+8UwR!1htz&a+ER3XS6r3Pv%KE? zsG;0>&z_GS80qfrZsK&`Tm9>GhzB1T8*_gS!%C&WHj(!5?4*YsOkkgKy%V(RV`Bqs za;fh-8nLBfZ&S^1o2l@G_n8L~xdOqd8YVLjffhy|+F3hzC$H`9dD)kf8RiEWB?Sd3 zB24~jbMe5@>NpkmwWUuwVD$LtX?>B&L2~oFTj%Gp@A9tL*o47rMvvBo6V{4qoo?~dZf zmE-YpSSfpRtKGQiFlEJKbKAJ3&rxXvrKb}b4O>;CpO3~ASC%HzMbm>vsXMrI0V(8=3yJ+h V{Bhd&uKmt`NO))@R3S*r{Rhc~zybgO diff --git a/toxygen/smileys/default/D83DDD18.png b/toxygen/smileys/default/D83DDD18.png index 978796588977d5116aac5ecb217b157b2df95c68..da9708d9c6b4aed636a5c760f9908b5a6849b154 100644 GIT binary patch delta 1272 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 literal 1286 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 Gp$P!=S+WZN diff --git a/toxygen/smileys/default/D83DDD1B.png b/toxygen/smileys/default/D83DDD1B.png index 956d7d7983e1ac1efd8fa6915a2170b9e8fba132..9ebeaacd7d659edc7f67d22b762277e17a1804fa 100644 GIT binary patch literal 1499 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>YvBmZk669-|NsB6 zTe}|vgUq)i$PW~{D8T;tGh4>;-z5TD*7I*&!?SWJSK;qpdA84=*~lFH?y-2H@Z=?z zSzgG#oS4VPB7F0O+{?U)izkRIW4SDI@TW)QT1!woGA4PuyF|{~^hE;5VK4FYb!C6X z&c~(7v9(i0o`Hd>zA7Z5Bq*_5p`a)~Ei)%op`@}PRiPrcfPulHcWS8jv^fSmcV3HF zHeI|f(|)XC`u9y|J_Z}z<#9GXxulXeVp?d>%A6P9&&PjJt`F5dsb!lbGC$_eL$Omw zZ=OAzet(H!{@;%kC22R(uWyOy$eg@Lw~MQ0_4y^H(+)UIjwmrMwGj+p>7KFl^fA|K zD>k{5>U^1&U{`o@H>JNUHd<7OK-u@8n}q5G#C zGf~^TN2+V}iKe2e9{GGm{d;c57@v4P+tcSG@qHV!!r}=I^^+#9(BS5<{BOy1?;S?>x(cidQWp>mO0)lrV$tagip(`1uhNG*8zK0QI|grKK3JJ^JHdMs5;pgGX32dQ@Qfh zzt^+hb-!F-I(=8^uADuWmrAg5&w8Vr;JEs(P%6)!%TMq6?|ZcOpVySb5qOpUY+46Fc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL9(!7jt6^Gnih_yyB9?yyR4vy_rCJp?Zz+>a}t%N=+=uFAB-e&w-_YfQ}+o7WNhhZV(RMZq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztfBqtksbpBW1y#tV@SoVDVGhsnGHn_e7sXAf55Z4DE`pFlLtKoVr7oxDfRUQiHfre zidb_r<>iHW?Qe<_aFyZ8vTs_j=z#(UU!2f?&G%XB;@a8jR4SLOe;@a*>FKYbUyXH({>XUwY=ZyeruMZ(A=bJ-Z`NkI|7cfO#2|ox^msd z8G+5cmU9y9Ox&hlY>+x2DR$S&$m;K;XLl0gPw{a5yJ~xlU(4c+-Q=s8+w2^;d+k#9 zCo!7uT(+d1b4pC$9rc45iQ&0+2M;K3SsCV0(3(F}xHH=4wAJn3o4HIy6U6yFE*zV8 zWUiJ{{`|*z2Sbfo3u{jJZrP(XJL02t{FP?GJ(YLmzMac?EVlI!%dF{+buot~m;AnK z{dkqtp|HdzhjO7g$)eUq@!#fHAKSbA{k{xE`N@aWj(yPiyz7YZm%D2!t7osRXO&>k X{O&w!hwfZaP@(1N>gTe~DWM4fT29BV diff --git a/toxygen/smileys/default/D83DDD1C.png b/toxygen/smileys/default/D83DDD1C.png index 72d88f59d47d7c66d4b69f439f4fe32bcd4905a1..bb52bb6b9dea7a1cf123d0d472428a72543b9677 100644 GIT binary patch literal 1465 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>BYJup2YlP^9 zXa|}A)QgFLrr|(fwfUCE!??2wj{f+JGeh1EmW8YZ6I_&=-_w`1= zEucj4+m^KNSq|Tt?rs2@#+c;o?h^5K>ybVnhrPtp*OmPlJ0F)SS9H6pF#`irLsdvb zNl;?BLP1e}T4qkFLP=#oszOC>0Rw|Y@6=GoZMO|L?i6>)IkK@_DwH{W>+4><>OXuz zk9PFk{&!4&TaN(qVUK0=@7G`0{zFt(Wp;`Fyd1-g?Wy5@$EKZKdrPmk@_qmD#m0x@ zd=F2G-DKu@%Vy<)?GdWF`>e#$K8Ai@)27bUl_0peQhjumJ zIFTh({mxZj4e#22Cn_1c?OW~l6` zdEEk$$Md*M9s8121x@*A;`%t$m=_d*$ql2jKKnG zX3b3Evqi1=uQ=#0ta_l|u=2p#4Xyd}9vd6*J`q^bqN3YzTK>e>-WsN9#s+`ojhb3_ z8P8h0v9L(we(q$q>B@=^C%iiBc}(Nz%L%OM4v!|K-%k>oJ>m7ni40EaNuO_cvf1oO zkeTG8uDbr4IM<6ajHfnan6HRC(BhWd+`jlN_dQ4Me+wAqC3qZzYe&gHp zy%)n-%P-#ktn>VzmB)*JOLp!$8~kxYT@6>t@)^5+Dm;C0PNV(#_Le%vJ@#FFhx0Bx z1t!mho-U3d5|?X_Toq~x5OKJeyx^Yqjo2GEx>#@AWo7#RU;UBri{|XK{093y`_AVs zTWv3}rSWf?C|C32f2vI9+Cxp)T3$-}3$X0)IXT6!;6KB8evkA`9cMRc%s;KXvmr%e z+Nm9fLJNCWCogRj&DeG;W%=pJHQSx{Rs9v`X;bzRQ1udG^^)}unQ`vfm!zGGuAk;z zdoh}~>^F0<&D@!rC!UM>V!r8-V)2sq6F{Y+YKdz^NlIc#s#S7PDv)9@GB7gIH89sT zGzl>@urfBaGBwsVFt9Q(Sg`W9If{nd{FKbJO57SQesS&wYLEok5S*V@Ql40p%HWui ipOmWLnVXoN8kCxtQdxL16#3Xl{LfG_JOgFM6XR0u6T7}RE~oqSqK z6m`K}6zg(bizkSDK9A>V+_bX<$#I;=K~a7v;dgosS1$Ms$5&#IfTP%&?1 z+2ckrXz7{@x|K?;6B|x3P%vafLAD4I4-mTU`L!zTxM}dO85>GFnO+MJY2cVWwgU6f z=_`R@?Cz|{6NJ|AjkX4pB6mxs(xU^zO-fM=-r%aH@)Sw3QaHvY1Wt^@O-u~12}%qz zlo+5AA*RH!5mtzW#YBJ&3N#la$v71zgE1k*F&xDP7&w-&NyBkvLjfhd8q{0F(s#u2 zq77u%v@@pJU8;blF4HxgF4IEAW)`h$(G1nhJG;E}ta&Q|TRQ^Ogl+2RYJ7QZ1N|Hu zV@W}j!i+$BehZ99iy)YHe@uAbv%|{^SEZ^yE2DeM=Vwy&EjVWRjt^A)Ds?|r zcX^_HVe92BV*`Iz_Vt(laaZ*=p1$4q#$aypx22DVpWi*X`G?QXemk~uW-dEW_073? zS1-%zkN1~F-aN7Q#i4h}OQ#N8j~{0ntlGBRksT}d`@6Pi-3? z{fi!M*DwAuGJ5RBa{pIgtY-7d*>Ps*Hh%w(ujlXX`KjWO7nYjsHmB`9ac1`6Y}3@1 zCA)Tf?3McI*%5*nIx(?O|G}5nYHrRwT~QYKWU=q=cdHre{&uF#E> z{P&N3VGS#jryukU4=$cxymxy3(^>oUA@}!JFT6i9we_8mD@QLqliL2>p<|nnDm--k T*64Jj_X|qK8>9=dXJ7gUq5ibf diff --git a/toxygen/smileys/default/D83DDD1D.png b/toxygen/smileys/default/D83DDD1D.png index 940a84d56916172df6484504fc42fd5518a4dfcb..75acdb0cc31639409c0c71bc015906796cc192f6 100644 GIT binary patch literal 1424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>3b&j$a_HGXX?yjzy~G%oUNSO8QHK|oy)1k?-B z4m1IRfaU=8A|s$Vmu z<29gW#w2fd7cV)Ut;c{I_7YEDSN3P@d|ayRs%Dmb3=B-QRUr{2L5bxG1x5L3nK`Kn zC6xuK3Kh8p3=9^%Q$u~P%{Jh<^IF8VX=2JrnRb>vud8Ff{g`X`b79N2_=lRSHx=A>25FxYdX55_3@75(~emxGHv?!;>_V` zwU3_K8CAA7zC9-TFKXdZv-oVm)FRvVldJO5b}7wIn#_1LKtE^l&d_~_%i2Sa@5wq| z{Gb0~=?>k)g^k`>mUn+?_)lc5S+%Qrj#secWS#X(AM0&fFlXN5OS%u2-acW`Xmj(- z@2Q=S>hCg62#i`}pQ0jtiizoY{dp$M1)&)&$82WIIw)W>@3SE%$7{}{nOFVZY!p|y z5x(V@@^i)=dvB{qG`xuj^mF!WWo?MF5iF=Jdn<6~l6GmClFPb9|1B-1&d}hqoBQyR zo2``Eghw6zaqGB#mj_y}iFs(}Z1*E_PVDUOzRe8f=^UrMZf^3m+s&W)zFXkt@w`Q| z6x_MXv)5I$E&iAn_gyl4c}k-BKGTeV)EQHvd3M>Q7d6(f_DCr9m`_-k|7Szw!s10t ztdHFS6{nv(o_eNt-Jsz*;qu&nxbczQ~D* z%vYpy%va2dxb&bo#^$zNB2Ux@?-PzEC-iSVrEO7FP~h?}>2`nB(dmTW&Bx$bY} zgcDyqi_32NAD#a7ad5)yf>)24LvGFu?0a`#=qKy)V_hu=3rkJ`)8;l$7sn8Z%dy99 zMl}UUxIOfDIAI%>70GCpnU&d`=IO-aR{G!k%0F}2pLVYk7_U9NX!vid$M)@Sw@VeA z@SL@5`8Syz#~$dM-t?I}N_?JR-J}WEoNu4{yl19a&1#LVv>KMM$Z2O;EN(P+xqLL} zvkEP(yvmqteQaHRo_n%!xiH(g{TB1j{djZl%;7oC9k;d!^lx49dU-md=KiHMk*A(^ zxp`@PU3}&BuiU?Wi$%8lyIM85Mnzg`?SF>VV&XoDtBP`fK2R-jjVMV;EJ?LWE=mPb z3`Pb8bg=d#Wzp$Pz8hk5M) literal 1244 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ}+o7WNhhZV(RJ&bUQ?E3OQk>4|I$^C~+Vq7MKt)1%jCHqz~l4Gf!$BFvk=D zvqr-qnO+74MjlTW$B>F!OD@}cA9j#9{xL?n&4-oM+Yw4l?r=84+W zbGJg`A96+iIFx60U`NVEjw|CeH%8_y&G(qvB6^v7 z_^Q9Eh_CTX6MWpTbJdmqygwdtUWsRl@{^u^;i#_N(--Tzk5#nh-p`g?JXJ&LxvkHI zqr!fvd**HK4&{Jj&1B&=YQ1e=sQdjOY6Y)!N6US5BB6{z2#W zBi-#gRQ0EDNjwx#FWNKnd%BgIyy&~9?OxY>8WH`njxgN@xNA Deuk*y diff --git a/toxygen/smileys/default/D83DDD1E.png b/toxygen/smileys/default/D83DDD1E.png index 4577ba883900b6459c440e674a02ac599ec8473a..81a8f84f44e5b3100d3e1b8c4d62cb5344a6e175 100644 GIT binary patch literal 1653 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 diff --git a/toxygen/smileys/default/D83DDD1F.png b/toxygen/smileys/default/D83DDD1F.png index 9533fa00c8cf72813a00ff1ff6c82e727e63099b..c709d36f1984a12db17ecfe24f08d0ee854b5845 100644 GIT binary patch delta 1304 zcmX@Wb(m{{WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D(4&^`2FYVvkzG-&jO9!arw#dJFha9o;q^t<@U=@fHpmO_qBTcS)hI2e*R9Ie;8fpk-a2sQK~(?7{q>h0038+Gzy9LA=Rbb_ zzWm^2?1c4jH|nfjB?t7Jcu9~SD1eZG=d)+eo_jri!sGgA8&I4v$=lsUW)6!ZJCMU( z;_2(k{*0ZEOO-9ikW+$zfvK@7B%&lJv0R~`C_gPTCsm=OvLIEVBDcPPfx)78YN+qE z+X_5)io4_(RTLjSwmA6aSKM^ly7Yp&nB%$okLfpZES%`Z>}S9KzsLO_nb#)WD|P99 zbM$I{++mg76<*h*^L*i5AN}JR$ssWx7ewzE-Wd@b~D=7{XTTMHtiE1^X*>iH(Q;H za<66k9eU#CGdb*L$WrC`Yg`*wtq|X~XxY@_8}BxpiV>HT_#W?nY58p{+3+1LPcAF> ze`1L|u)J&L&y^;g#S@=yHL!^cpJJR;m%?3f(#)7oK|J=y9wWg&-((piTiJd&yDeI% zQ?F3)_q)ynrl?l84*kh1S$PgV-uX_oaf6`3w8*zdP#(;i$ih&4Fnt|opW{6N^miK4tMTB|N-7ktna7JYF{^2~_?D=Ht) zTy{{?)Z{(G95o}>l~a~X-tnt(&z_0=2dY^7{kn_t>+}}aUvlc*nWB{X=}Y2gYIQX@FJ1c)m`LY%x;TbN)L)K0_Ee}TK%(X0 z*4-QX=9lWQxQK`{-~PQ{?fd+XXU@Ih{-Zs0w)|6}fXv#}E*JiZwg#4x6d7lvpAy?Bb4`2uQHEIuJifYYxiy?E_*on z$G^1K@6FEeFildpA>X&$*0_5@y$a9y>960OJM-|PcK@r-(Q6qN|NE68anyYIIkga@ zMYno@E>$gYjVMV;EJ?LWE=mPb3`PbUvB{QuOw+1)n?4U&x71g;I7(8A5T-G@yGywp7EM&s~ literal 1344 zcmbVMZ%i9y7(b}{Bk964%NUc$d7H`{?e)F3S9((5xVu8liUe4wd_b=4TR5S24!?_qcvuI2-j?6DQ7lxKN7e1)*gJeP`ZoyN0$D|(mS&5CF#)`fuHtVHy(2%=CI@kF#o)|iBcSmkAqY#fsW zzN(^kdx$ls0{%vvQ&fmMt#(Vu1^`aeR^V{bG$_X@8=y!Vx@ik=Fix7GT=@D$AZuzk z!Zh*Tbz5lXA-Z%u#*kz@9=FErRz>Y3f!pmiI4H`3G%Q-btcwYYtnJG&@K6h>QcRZ= z88;Zkpwg#%2;}Kn2+^3|zdxn`sBNJkb1XddvjT&*SYHNBE{MU_5wY8T17$lpZ zru3;HRFBBM92n*9-i8cC z0Ic8uwN57u09Qe~IV#7o8CKv~R|T;1fI-;ej%m7!{4iJ6D|Z7?v0F@oY=tYVc57?}?!Yr|z46t7geOmVIq7&CY{~rR;)Y zO^xsWCYVa5st$|~3}l~r^;UAC>fM3t3NbLP^?<#1V`@7VR_*VE?7wmI{cx7ky-ve&B3 zwi3@Twpnb?vvqALapvY_b9!>QWA=sl`-iQm;~zXcG9>=KEu-1MBQibuUFqb|U-;hX zy4J7$xKp^>bSC&_PdNf)vA^f5GmoDcV2|kYHAPEb%;k^GE~GCNe|&22^guT9lPJQ) zm0t@@7m^pPiMuzq4R3cH%)EH+TJgx1Pl|s$vU2Qt{$r_h3Y42`7UzB_GPjSuY%ZDl z^EP}B`}yqqowbFBZv+CpzdbRG7d@G|SWa$zpuJ)A@a1#C>HM?G%=zrD8;^Q-cRZJD w@U~+^MX8#l1=p6-V1NDBOS{IttuYT`CM;_ysda0ygT_D2Cp7RM)^;5I2O%BWLI3~& diff --git a/toxygen/smileys/default/D83DDD20.png b/toxygen/smileys/default/D83DDD20.png index 74e29fab0c2300aae0e5043264e515460081147f..0fdbe290545829e35c3e5392f655102ed771173f 100644 GIT binary patch delta 1392 zcmZ`%X;70_6n!iL%A&GGgVu;3LPdkhBGS^N37R4ZAs~ooP(ZQlh8VQT7Z$-r7I6V; zK!b!$Wfze}WuHJnkg!Rp62JtIMUs>%OX%03Kl-O{-prkQ?mOq6d*{7XXRF`Z3Ml~q z_Ly&UgM;z~j1vZcvJ~}Y-*s?~I)9Al1i;nZ05IbKScDc;#`ohLl=KOe%9>6`e>Zbc)M!jL9J| zIq+lnLNEc&*B}^OE3B!+Jnvs!U0spO>)%bniVZ`Pe|(Zg7I(qY{yA;)3txi^I^?UX zuLi_$r|c#Hr&$nQ*uJtNhr5n^lF*BP|GK3*wFBi^KDFi3Hsz_C9gRw^W_=pMsip;AdPYTiZ3MwojG$1g@A4Q1@^GDHw!vL^J zy65g#K8#iyzRcQ&QBjtJ+oe@Hx~CmxAnkV8r+hHUK{`6zh*$AjTw2<=!aB=$C~yBP zYT2z*>lOL&Tu|R|fPi~2bc|Z78Nh=s@NbdQAN#PV`^~z8t?pQz-KP5F%Oh|1CXHA_ zo94YgTJ0;pdZ&dgan?Q-4UlnY{LJK4D0R%E#z@eN%R9R(r7PgEM~Aqs5L`6Jh88v7xRo0t2H zF48T;f!3*)!;1-v7pGV!=!Z_0DMj5r+{)K!t(XI!c`d0A$;}3hir8sLrnnE~eoR@A zdAyUXhD@Qe)#5Z&%Y=Q-(-sI!DV4j}DOMgn6KGh+4Em&&-;l4Zz&z7|9^Bz|WD8;d z8A&yaD}D-v*qOcEyp7@jWn6Bf4aT{!0_|zl8>W{yRL`15dPbeJ;-X*hd$x*-ho;V` zWxG`mx8jn=0z)m$vNjMCfxplC0i_c#uGsX}U z^*8R30~BNP=9RVN@4kuF!d)lEUeR09Y`HiQbx74{B4HQWI6@cV;s~cUo&0c1jF2dJ zrqs(I9!Q!hK9=k-)~m)CG|O^6iES{pBoZRrN2vvz?VW>+O#&Z8zx{1^Ni+-fGm?gE}osWgb5gvoHL*-sOR|bXXbA?P(71A8jGv zFr+PfpN;_!ti`75LQO+6GrVbh14meZS#Pd?V_gz58C$;~@50W4*H56%p`V$n5ntPh z->d2-d+2GC6Wt5jx46#OP_#3pi-fGpqxqHH2AiNqo3rqm7@Qb^<)Zu#%5sfNOHL6( zu!Yp4kwa#uYro6wnJ5v&^bc4U365;nkIn8Epha@we4rmMLyS02C7Zt-Vm_r`tFlrO zx6U73p5#@x^;Y}P?AiQ*s*Uy{`}cFgkt=<|?^(I8b0aKj0hMf8K^Dri;E9YWcu#MN zpD)GV_I!jt9KZqefdl4fYjcZ3Bn!0dK}%c9L*Js&wrI4!R!jB2j5R`Vs9#_})c+H7 V-RSF5cbaSffPi<#6=TUUe*b=gA;M(WeN$-Eqe)D%oM(;Sti>Q7adTa0-b&!`{3p7p8xaQ z|M&a9-?9C9uWsD%{00buHd^PWQYsWOF2PT7G-RR}2Mt@j zq?<0FNvihnB)u1c*1J4Kr9!DKSMOxJax$tT4|rLChM>L1082WnXaRQ6Zja9(n;W_y zgFTc%R-&?@HkP0(J^Si7y09*<$XQqA)KM~H7Q8p02LxVPAmM~#S zL6BG^~;xTz>}427F&sg`tqsQ4So>1R9xqtxq5WnLd6;jKNIv zPR_#$9>xbp8A%6IBN$}B)0Ggstj+eA*vGFX3Zx7PkSv19QN-(w#)aP5eJLGGSxD5?m&(dTm>P!zJ-%s6Yjv`?^_4KnaWPI)Ljs?ZYZ z9E{KqM2-c=Fq1`zWvf(b3?sB^9f8L<*1~FVEvCvbsVqthsDuU2R%*0bi%F|fqb6Ky zQpT`WA1{zTCmplv0d`lh$|qv=1V@ts!xb@1b*uySS26;_S28S26sqA}B_1Ee`1#$@ z@~lK_ra8}>G?l|KUU)gbde0j6RSKmBHJLEN0z3e5Yf*EK2}G<0l1^t)$X2n`|H&Bw zoI#?^@t`Q!L>@Bo&Qobu9G!KYS-Vfw*GEQG8A&{Lt?mt6$y~U^KD?{xpKjXf zL>K2G_04006^be2{MMVvqELNg2@m%Vx1nRhnu@S8R}w$_T#w_EX|`+RK=H}Ag9fo8 zt}G0VO2e1Y&Mo#}|HJg4=)P$d?%#E({iBS_4UJu0U8z@ckH>`)-#)k$SzLdBTCeQs zIPo4?pdFvQ(UvV4>N6$|ocUQQx;lGADlw#{OZekIG$)Es2`rkfYE@i2YaC-AU3ZI$ zvGQi>wVLJ^O5mlkZbwq6esS^J{yPtz5e2s=+a>RxyJJk4Y}oQk(~tI3eeE!D?u`6s zno%=0u(=||H8(WQ-o2UFe7&}uz0Mfx-NDc2@UinPOy^hk5@$~fI}OeAI~h#+<$U`{ zd0BVrvz4Vwql@+{V6*t$Bvv8);?LHZM;*PAJ@VVm ziJrl2zm){L3kQ1@t@jR({sm9PwX!wr|`*iN@BU1UMO_sUP#qTF3 vlHhymKJG{eN((%GM(AJN(Vwi diff --git a/toxygen/smileys/default/D83DDD21.png b/toxygen/smileys/default/D83DDD21.png index c77b49a46379947957135688b69392b951823909..4cc424d0a179e0f59421916e1d4224eab7056cf6 100644 GIT binary patch delta 1340 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 Sp1r62>sUvV{qy>c*Z%?2tlm@r diff --git a/toxygen/smileys/default/D83DDD22.png b/toxygen/smileys/default/D83DDD22.png index 841012e2d7a3b7266c278769cf09394a7beaea77..d86193a95298184e210d7bccbbb0af4bacdfbdbb 100644 GIT binary patch delta 1300 zcmcb^wTEkhWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!Dz292)F6Vqt}1`{o8%p1!W^ z&)E66RGF2c|4wIMV5+YQi6{w5ELSKf%1_J8NmVGREJ#(T$Sq)Cu;`sy9~!;wi2}!; za?#B!%pAMsl=RdWpQ}0dY59S=C(DZEFVE{^o5;lF`Rn&@|6fn{YngsLc6e24?3Ay| zj<#%?Bxahst62Tk@wa8tE)&c}r;9vRn!CB=^q~uDZ%qn`=4I!;tyL{}l&ATOh0BrV z@>6>vpI%+{Xh9kK<wrG ze2l?;rSRU+sA|pt7ibdGX-OP)Q zsVvpzE0i3$s!dM2XA>B$!k?!)eo9r_k`iXf~yx?CUUFG9Xw~l z^7eswHTT3+BiSR)&twl4n`Hm$_!E4K|Le56sDkBHyT2Dn^}9`4XMHv@x9sThFK>en zRDW9A%^y@)rX(}#{-iJbtDL$zPUddh2TY=KJzX3_BreCEaTjZH5U6LlV3f{yw1IcV z0TVSBS6Rcm{{z0Qarj>y8@ih9UuKp5!t92N{>DZdbg~^}wmrTP6S-rZwMyT`3Zti| z4kj--^trarCaovrjJOtegi+A(9bFNBwH!G%E;}36ad%$DbU}@F1B-d*j;wC#5R6^) zT32aasK-+|udh>cy(%Tcr)G=eu0bsbf^;R?&!NX3oPG-Oz`J50#e$_wV}e?%+^)r0g@?$n9O7ZG z$VPZS&xX7AUgDh)l+!K*f?CkmNN}=fV$(XNgct*82y(g;F_!D(H8{ja1j(hJ{qn3H z7Q!z5Zkx~Si&1=s(9omstvyWvuBVf8g!S%P*qI;zfyiqtoDiduN+ewR60?9> zuc&C9F8!)gL0>aW$qEnKOco<&Mo}2YO{mp|<7hREnNbWegBv%ZR>Fo8m>pib^uU@D zZYTV-XU!Jax%3^H79$WO9*>*i7L%+*5Y*vtq&YCm2sDgpx1_NNqof)#3^cEDiV)KT zS%TAyY)J0XTzcT?Y6xP?=UXS1)U`x`lpzT=hM*=hB8us_Rk&;RSjhT3YNJH{h^ zUX{BP4%DOFkO70--P=%F5qLv9tq7nfY?PL{E|HfsFYVHUFQ%{%Cd`%^3iqItouWJp zhN2{6MeA)g97U-b+(BU(j*YMmlJroR&B9n2PzeSj&8VFrJ-D5!p=dkFWUyXI)mVw+ zGj;`FcMXf(6H8DE&uX#~kmYEm1DZQzO;$VP7)-U|@Z-A$DJ;j;s&si)qosL8*vE%G ziY&q_`6Yx6?At6>yO|_WiUA&gxNFR`hXfJhKyo-3i+&9o{y#ZGfHO$CIsVfunIq7F zX>q;v!DfAUcnM650!9P<q|YAjph`5oHcDhe(2BoCZFwe^d`LnL|akFaB1M?hf8-B zOQqeRTdzxp%h^p@{$*^hAtU;n&< zF>*g2dp=dwkaxJQbSb$|p8cKf6Q0p6-%VW}pPe@(U(G)?ctH$2qI*B^wytq!rLO)l zky+@0`5n(3ZTHvzzu0AojulMKCF(9dF=MW)eC<{+ zk|#RQe~kY0mabbf&c&j diff --git a/toxygen/smileys/default/D83DDD23.png b/toxygen/smileys/default/D83DDD23.png index 8320fa3299a53274e200a1ec8a78c8acc98ad395..5684e655505eb929fdcd25ae275bda4856d1991a 100644 GIT binary patch literal 1535 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>wM=H0Z|vta>5F+@GG7KkpO zMj!%egdm`HOawF#7Xn&@9|4^~5CPqZ3jy7a2VsT-#QPAx0|Uy?S>PiuKqE_n{6Jxk z0<^A6C+z9;o%;L8x8{z!X~v=1pR+<%3$E&TXZ1?ts+jRR!84&&uU7B>oOL(!%qpeq zPy1H`W!|MJtrEQ=X7$SW%-xQ6E~`VbKOHrQ*akG2G0EHAg`tC0)&t04FY)wsWq-!b z$EC`!J7|qA0|QfaRY*ihP-3}4K~a8MW=^U?No7H*LPc%?1A|5H)KJH5w+%S%6nDuv zvax(BoO9yV*S&hxfA}m4WsdLv+Z(4NsmLbL^78xn_%F(JzS1XS+syUNX5KivN?Sa6 z^)s{ET5~I9x8Ke+aJHM>wq#ex+n_DgfsJ{aXKwlV$RgsXc78`B8QYf&eh-NbDs1d3w&bmAIsSpkYHH|JzlDZR z(^{^_7#E@zp6Q-xClMNcw-5dk4cR2bSA& zX3mj}^qb102saulm_vEe3 zg@tPa)0ekgnEG{nue+*MQ}a2FVm(Rz*#cgMZ{6e8G5szNkk*NPXy-N~v;667 zZ11Ghj$}{Cy|83+R@k>Pj%lX(mzj!OXLUI!2&WY^CPq1GaD}R`5iwr;YtcUi1X~tJjwb={D9v9&fB8O0;2!-$ThAD{&ls8KmVb!!b8_Ldzgcj+#S89EOCAGiv80c zCp(7g$_9VsjT{#5(mruyXMtQLHJeXW@Vv({- z-?mwHO7-)0}b{?iTP8L-Bo_0b0 z#oew&M)AL;fhlvpr;B5V#O2td)!_#d7><9u|E6*oZ|{>y*)Ce!r+U2$S?1(nn!I?= zfANct-&pk7)z|DlP`|4+xy1Z9gwpPyjU(XrY5LGCky;t>V0<{ummnx-p{E~(?< zXxboR#&qy3R|0v9bq* zOnE(f(&d$R?%ka(ujQ~cEdP&L?7FbspMQM0!+Pc3>0e(bRKDiF^3d8q%HM9;{7pLb zwWn|S9F`Z(lw~hDb9yq+E2<@~5hW>!C8<`)MX5lF!N|bKNY}tz*U%)y(7?*r)XKzM z+rYrez+i^Y{aq*;a`RI%(<*Umcqy392Gk%4vLQG>t)x7$D3!r6B|j-u!8128JvAsb XF{QHbWGb*!W?=Ai^>bP0l+XkKHtm@- literal 1278 zcmbVMZA{!`9PdH|CA$n|g6xCVo@QRMyI$|5ch_*^N?+W`j*;Wyp_$~$^*OGAzStJ8 zV3rsLS;+JQL&Jw9o1)8z*$`tEaZ9EK%v@p=nFeDp!^lV^j){=@Hlt60LqCvxuxb1J zpQr!d@BiL`rp6~7<$KBz1aZUUjR&!=S&;de=Y8yb~ zvd|8aKoGm1zY1y*q$DS$TFjQjK`N`t9>Lb}Sh5Du2vS>TX+pLGm}nblmy{@W>(Yl9 zDv41n9ZYx=8UymufgT++_cW%mJssJIh}G>!Yb^>A$iNg(OYT$*%8Fv^ycCq}V;n=* zRm_ekw&7Guq6uYG9iTyv-<|am1WJ+~A`m1=Vjt@B53dakDf~Vm3sCqk2L?RKJ!{>8D4Y$#)n1bb23}=yn1x8kvG*eO) z)Mgag)GjlMK~FbAkhMf&i&!x>69rR-TY`oY9xpD-c3kV)hM5Hay0NXck?Pg}o&<*4 zrDtJ1a?TY(?k|bx44SCL@fm$gwRp#Ipp)@mzp|mEe4Imw!G z0~9zhIvfq<>HZP~+2M(^bjtent(m%p$=E|zR*o+?eyf^e$C-5Xqcfq*&E<+WE+fyC zKRCR2&!|~p4pq^bD?AZinty%bv6BteWmguL@9ei+D@`YtK0fxdc6I3bU-zB6uvF*# z=CXC?JTw3GFU#ep&V=RjZ%^;3{OtE@4XpJV7I^lHp%~w5%=VS?Wvx@s417L*c=w5U z{KY~@&UCfjIO?1_^T)+k(bE@lHNk_^t9$E*(oc=wzBbkSWo7@v$*Hlih4<)Fv+o`| zviA63d-bjSrJl-3?1P`^#hJi&HGlq4;W#Y3RC4an@JFtF-#hMqVD3%V+0|DnUmhH} zc{{di{OHajU#+0OPX3tb+nb)9j=euIaJ=@Ds^reW?m}$j`bhuqJ7+U%rEPO4y)b$r jYZ82!^V8PRT{}vVwLWiZ_uP$(_8%$EHL_#%ncjZ@azDU% diff --git a/toxygen/smileys/default/D83DDD24.png b/toxygen/smileys/default/D83DDD24.png index eeb06665554c4fada2fc4f2e52b8086adbd59318..37b4083706f1cf95b04309bb4b3fe9932f7071c2 100644 GIT binary patch literal 1305 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>-iHN*GJoc;*3e&?k;jgTngGi4tt5GuPggAc0Mju&O?17 zoeT_2HB})IB|(Yh3I#>^X_+~x3MG{VsR|Xj1q=)py;DPDuia7Ls7)7*ZDMMQ^iw;w zect8cdi%xa&i7G^|7~j7$u^NYM&--z=lZ_{!>@X6^8Ehl^XL@vbUsUs)tuFc^EZSrB`o3P*nyY(PB(^vT<|IwivsS;k=hUmK8!kpQ zL_QO_zwPEbp==S34{mGQS{RP1hp>IW`E=TlySf%|= zMf=c8JH30dQG59NWluy-+cAHh!M$9j1Ips;_7eryb8vHP{P&JEh9Oj;DOYl4)C9#0 zn`^qlLf2$FdDo{U^~bn0ZEbwTKcD@axlC+%F+Dx;H`r@c1&Bb zIoRpNsbANREf$hJ=(xPya=IY@Yyq#r8~uA?Siikrpr#Z1(9Y-H=gnCgww-ZiW}e-~ z`B5lU+T`UTE3N9hrc~MGnXEgNLR}Rm^a#d0Fw<2KXv1Q zC~Be!orEVLg983ri*`K zjOah`rTzp%?<+k8_s0*WaO}IS*6XLMxX|wdm@o8Q+s_YJxaf+MA&Z$u1 zkt3;ovZ0;-%wmMyik#@p5A z`6HSem^9lwT^vIsF2^2h=W8+$VZHGB9#fZaCF>QVHH*RuUjP3;l~b{?itmH}<#g6q z=AO!#fd`#Jj|PP{PmPS+Y4wKZ=1#U5kE@QHD3;xJFDFv?4!_eQ5jMO2vN_uozO~O_ zIeL6@$2q4hiu#^jlP*2c%KzT}=E2HWkCwiAxb{`@7fosY1iyW)D=q&prgnC} zQ!>*kacd}V_OJ(PkObKfoS#-wo>-L1;Fyx1l&avFo0y&&l$w}QS$HxPR9-Q7y85}S Ib4q9e0NeOHCjbBd literal 1302 zcmbVLZD<>19KX^oD{G2XhE2q}>t(nFP3|ShB{`a;*So|IL&z09MkeA zp|TXgjs#Lfs>p!}dAOp3vC2qBtW1hw35!M1fieRL6krNyS(#D|rX0Z?n&hr@Lait<1Wk1?Z~LfNAlt`-9i3{jVhrmU%G zol(eX(`E#No^FMp6jP~fV%2CS3Z@J%3q_pt61bw&<7#LdW*YqK#*W%XW~K=6G%&Pj zU4-?>yINqFyL%g|D?)FWr*s(>MVR6>aasYYndBoFyzxr1#1Os^M<+-w$Z?4{MUrgX zPYwkFG)ZzHI?Pcmj-9ZfAwNZixp>?khn0v^EJ+0UAWOx4!33KK54EsK)i4EB1TDKV zwA;k;_rx-s4g^!vGnzKl>VVM+&D4wut%!1CG)bWK4U z`DNrC><4`QAi=U^!+RKah~N_}jF^T}I2`w3O|0~Pa)v`^@OpFnr&(G@umkJjcI(5< z_V54|PK*vmZLpUZU+u4??VtWsBfqmQR2|DgtlepQR{zT3 zuvH!Q_jWlipYc1RcW!-+KDM^n6Eo%(iz8=*KhCY&tJWRygZn5EnLobrMf(-U;@gW3>h#CXk9zkBoz&CP z#I<7!*EiO`*(8wjKL$h3J1q+!33s*!v+dordoyNRdbvp2mQG%uTI_mMjjh*y%dc&o zy&vl|*z+eJxioG&k-WAs`{m)j&o)MXF4zwD#+2RJZgKF+Rcr4W=IT1Q-`%$K*6_P+ x*4^j<7e2evwbAj$X$e{N`e?jK;{wTS=# diff --git a/toxygen/smileys/default/D83DDD25.png b/toxygen/smileys/default/D83DDD25.png index f4db0d90ba56b5d41e06e5cf52c9b4fd49f186e5..3625517860821b331a40369a428ec0f33a1c10ff 100644 GIT binary patch literal 1832 zcmZ{l2~?9;7RO&g1VmYsmTgd?2(lOwHd)33!9<7<)472?hFFlU3)zNn%m}VngK`f{ z?12lL;qeYAr@+6caLEFCnV|d%Xs3fB1e7e82!M&5FpjMW0wo)o zT%nH+6AT!q!nhYq#=vAEC~RQN8M?k$+UHmHFk%UcEuiGV+Y)$J22)uuMuX7}ps)s+ z3$!`Hup>+a!|WNDZ-WIXysv|)3>f!@Q7Sxjhnr-0xf2wr@b)&$zlMb&n3KRvIgE+n z-+@p`hSSDSZ3nGiLf>f^s)FHq7_5Nqe7GM9*U3w}1kiV?@FUhYEOC(EQj7i16HDyk zX#YLiqfGAZui0$1c8}&0cJr>n=hjvd!l~Gob_aNS4kdJ_DXUAm-x}7X%N|ZEK1`0a z)%?L0nVrrG z5eIQoiK3*0=$L(cqBu>&Cmx6sA>@3#kWD=+qo}`5{(3zXPnbz8ZhiHx$tL4NmZ)Ib z`BI8eQ-~eWE%TIb^P%5LO{T|QF_d;q8MU#IPQt3N{K{`-kr!6Wq(U5QBk@P`-QRR1_KBe&ha1rOjq{v-^ITanBK2Q%1ym1ldpBvAkeOC zHPs4DZE4?TIxkY`C8dlA)rxxd*LxcO#PmPy&`&!+ytI*>ZPluO)5?m}bL#bmH(mDr z9gET&uk)2Lw;w$Eu~^kz5=FyZ`1wZc^{#V4hNJs;=Wn!@e0#~g-+0$O{v)knE1&$` zoAQ5siPJfqcg5|eoYbo;jy^kd0>9xh%Yq;)@Fj87_57RIc&?PXkJg}@Q9;+KQ29P9 z&jr_b(r!(-$1TmP(lx=k1P^kwv+>g#>x%P)d2WAQGZ# zF8%eThGwPGbiy^-iDPF3j8#Lg{xUOjI1`;UrF=9=a?VpZF(fc9)@$>>+kRPE7$9PF zIuwKz-d=ZpK+B>}7T)-5NM~lo)f;`MQcLnLz-g01ynN1X-(3A(Ut>g9jlHd1Xz|Zq z64bA$ZN900`G|q`u^Gwpnm|>d)BRF;7>ii7Ytsw#m)z;EF0 zSQEHIox{Q7Z4=E`GR!S?zR5joxccgLPd@LB#}-w+G`$scz3*#8{Lk5XL=J}?#eVTD zKPO)z>FMHj^Z6X9^yF4be(v!+Cp$liEkBC0*U87B(R-&KqoICh-7W6kfq@$4)7Fm1 zPueMq-5~!l#3k@zqSOAr!6a0F5;GVsRR~Uq o7N>HP_=w6&h~yI)aooN9AU>Cume|I}eqRVN=zg>+PtK9w0jRfL^#A|> literal 1534 zcmbVMeNfYO7_K7fA*gqvh3ys3B0UC13~W!-j4^3;21A1&!gKA z2#lFXl}7NfQzcX(U=c(X(5tm7rv`=r6jj4IJ&M9F1BeDjAdP&ZDp-f>Q5?wv#xGD- z6B!q7C#>UI^2!K$B*}+EP#_Rc2efKIbVD$PVNnhQQOOo6e+@6uK^5Rd;0hYULQq0A?E$#fs#W81!*4yt2L0<8$H*UwO_Kc|GM#1Yrmt$ z$3k}2FI0<8IUlb1F|ZuFXA4CQWpD7MA}1$>UO@=XYA?%6Ho^$XU(^i8;3SN}B%&o@ zJ&BMo3>&P7)?m2wF49hGSVZf}g86gZNjTRwIF|3XE zOEm9fW4fHIJC4;p6N_6!mX-w3AqXpC6;S9AB*E_ye1N401u}{`o)H56m!j!;d|HAP zxs@zq6$LLa7GIouioPiehEXgFL-hufL}1ut$s$QTs>{O6X3Rin!Er3}e_{s7&Op)P z_)oFKw&V(oPM?&%ym-<)EH8JAD7S`f=mojsCaP_O$r0>t{BX(AqSunGopqP%ra3Z_ zmuV_G)-8@7>Qc9fB2hRB<#2DrD;`mWP`a`dNf41>_ENK125hB2Ljzv*uK&b*E7=?XHAM zhbNWd;1}g~cJ?ZpEn#!OR+@cel-aqZAy@bfJr1ZA6z*SpFg^ZwG921{eO}sEr{juS zO%;FU*YmFzcfG$)>9G$|nSJNKQ_clFt0(C)=D8YryIOtwQYr@)eLznL9h&uH_-Ax) zz59YY&y^T@@t0eX6Or5v%O0s_#~p}UNY{J3Yg6AYecrL=#!$<}3E@lL+li<4G#%?% z=yGq~EqLqur=GrFQ&Y(Pn!UaI<4>DXr>J_;m3?KLX<2&BgC)(!)-UL8%c;0}r2pao udjAcQwBBhAe{^^G-&DATnbfy@!eqs(n7H-iA60eHKeCN1All7kYyJTme>y<` diff --git a/toxygen/smileys/default/D83DDD26.png b/toxygen/smileys/default/D83DDD26.png index 78acca90a186533d3110d64f342692e4d34a1727..ec4ca85286e4c4a277bda987fd93407715d0fba7 100644 GIT binary patch literal 1555 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>(CnvS`pldo$)3`7eW=^Pl}G-**-Qh`fQLeK2g#Z8m_V{?NNi^7wOT!NAc8~T9u zMx+*dgr-`$1og~X?HQKVGkZX}{t|9|@b|JR#$uab)zyXwncJ=%Zw{F;EAhP6OP+m!_Q0Yips)4M9hz190( z?>`{7|Coo;f#Yf%9v6OmROe!h_;dWv+s~f2e_QgT-!WYBw@Qa?<=+Wx5iCH}l-wdE)NuoO?XdYuRV$#yq$0nXV$Ar?C4dU+vomL9@R_btvz6s5$A>ZjSOJ!6||ZxIVS!2u_vCKJjis z(W{y4^FHStu|2!ZZ{uTtWj-z*L;dVT*~T=yvIDzyA77U5uA>!f_++L|j}Tn8Vm+};|n;h{#3iz#>Y z8&{DC>!`nC#Z2$ybCvcixN&WwpR-HrL2AY&TG#}k4xge*BU9X-1bT{VC5mH8)wj(fr{`;-Q@-P*Gj z8(Nr7k}sR?w_JJQg9%cZDm%3btwMGj{;PscXnDrvxHzn-q0x1RZe+9ar}Eo%52S0S7pU*E zJy#@mzPV+c|E$e7ZA9ZQZTD|{BeJTSKdARzij~cN)i3Ou*LL+q-sAWIOq{ztT^vIs zE+;26Fsr4Bg_*Ib*zKOXmzjOe=@Um@zIgKHQ0s)}Y@cH9R#YU`*p>79hwIldI%>q9 zxO?*QZSM#N7m*Xf!qX>w`B+-U`f2HsR0)Zcr+@x1Gd0KTeX_8o^}&P-8$NV+R5A(* z%y%@@vQj%^Y3AoCc_=-g)8E6V%hxY3G?@Lfq)E`_OU6co>zjC2jmbq!5I3=OP|O|48#v<(cb3=C5Fj_yR!kei>9nO2EggGg{K zsKAy4*$|wcR#Ki=l*-_klAn~S;F+74o*I;zm{M7IG8I@-Gcb6%`njxgN@xNA+Lv!S literal 1597 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 E1GiT5B>(^b literal 1611 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL2U16PRAlyyB9?yyR4vy_rCJp?Zz+>a}t%N=+=uFAB-e&w-_YfQAMqsygtM@C2D(@XLX zj%_%wf>Y(yDenscrc%jUJ~pZSV-Xf_>h#&_CY9W?a_d@=l&XcSNmuSgZ;;)8F@eF+ z;$&pa^yjwE_ptA-ILv#yrB&tR@si-+_>BQAPn@)R-dTmocfL(v_N{)CYZhiz)90e{ zLOzO4Qz+or|~27xa}`pP$tmusnzx9UK=sWA+*JFsuFf`ry@E!*z2v zf9m6te*9m8ZSOw8RiO=G*I3rqykF!$|FiDi>go;O8x>}recIo#NMpgD&Bp>$_-6G# zkBWHKY|kQcwIOOPL$6C*MeRq2ie2yDbTT^rie4FVZ7I*{>(OW3nvZ9h85&n>YW4w54@8^3z z)(8~$&au;6^VM_JE;9~OPq_nyk{PB_0Vkd&Y?Vm3^Vv$SS2bq(xj8~+d7iVX=N{8omNS+H

u4gJl;fqZMsAK1V5vuw$4apnJ5*R%~?)Uf=o+m z8i3qrHQ8;sE;B;Sf$SJ{*a;z3_fMKWw`cMUpwZj*9-KON(c(Cpvf75#srFm5 zo%a;G9j){_Q}1)RCE!xCKQL6xHK$z$TI5s`RXDfPFfPG@Rh zoVJ9ufqBU$4z`W^dAatU6y`s1MN;(AT}7GOPqkDYKW1cNW@YE%6%>_}mC1c{Ag?g% z8_+n$ByV>Yh7ML)4K9Noa2B!L|kcg6?#Bzm#qWrYXoK%I9 z%7RpdirfMQ28-UQp@GwG8*tSBc`jnx#KZCAvBku%*T4U|`MBJ{^V;i*+qrYnPcs=B z^rZay`&s;NNBm{CqUjIMcI&E@ZGLs?=#5o7*KYGpzjyp?ne>?%@xIB+c7?nR+EN|Z zn7?`ImY>feB95BhT`e__L!-&{j8V#SrSkkumBu<-9@9Acak~DdtP(pp6&v7J6AErRSV+I=q?IM?%8RbX8HCE z^Z82iNAvQct?~*A^_F+bKQ-Lc;GY`0H8|5Jv**(+?R84hrzGdu-;<5olYWfviR-l; zaWf6x)iNn4i@Vo13Fd2Xb0q$s!y4qU`U1zcdefOv2L){Al|^&3@Um~5cva=wjc$bq z>!`oeikaTY=PK=K$a$@?TyeP&YeSrk;0F8MWgL3zOiRj?TGlP9m$jMlLW9pv`OwZ3 z+Z?9}Nyqr()^YtV4-}rZu3)~R{GS}D*y!iW92<7KaeS28)NA%~)vQzVx&(RT?hTH{O_KeX^llyvKRc(*;k? z{&QlieU#w%UWQeQtI7YLI{)UkH=9y4-8`&TI2<~#Xo0S!g;M?Yx9oc_ma~>$y8T$^ z`9~X%7r&NR?mip*(e(E>%?Wc4m3(wub#bmv`}6HB{}?7jbT!EA3Q7bf(H>71#}J9j z$q5XMYH4C&Y$^wi9IHRqaOio#BbTSvyLTvY7wp_wU7*ZeQC0c*gVGMWeRcn8{xC8% z889w9puonKI58p6a7IT3$Ak$Jc&1Kr+Zg3$=Hn;l>f&e^<0gwY#ii0 zyRlJ_v7}`2moF1pC3txj_ex0g`udzbqpP!aO>WNHH?lH}wVMPQw&tev0!usw)e_f; zl9a@fRIB8oR3OD*WME{ZYhbQxXcA&*U}bD-Woo2tU|?lnz;^FLGm3`X{FKbJO57UC Wn?39&DynlaFnGH9xvXEV;li(`O!Mk+G3UCuLER$3bB(8>+F9cW< zSvy%oo0e?>NrzNRlAAOcKryXW8|A=p8PJe<{4R;{$y}Z-F$S9R*hJnf z@q!DEGBQ;{ouos6r%NF?-4@FVvCFfZD3CJL$GA~Uu0Wm6Xk3fh9;t}?*Ns)RJtclO zhZb=jp-!}cdf2zbz#w<`HxyL_-jLfx9u$SCr3G7^lXFRCT8Drya+YUF9K#4&V<1d= zEoB6Pfl?4AoYJUpN`afysu;&=SOun`DYZV6#*Ls7M%eky5D)T1-R4ux6J> zVq7*ZW|s$cm$AzGVo6Hm7)cOI1fe$80fp6qBzUR?H%t{1@YYh^#R^`}Q_=D)MN4xc zzn5c8qTqxV^Goup*vHgrtx1zf(NXU~+Jp;xNZMztPU#XDDz6jW)-B znk9AwIxxCjX?-B93=ii56C;Ar@VBT}?v=SuwXJAYSNQk2?qqIbld<@~g|a=H z3M= zAva}qdg5N7hr1>mVr?8z+`e9OQghMtMs`+F;$AcI4afaHwUl yas7izJ*IqkpeGnf3e`5fg;yQk)esp>TvrJhpC9?bZ?&C`{xi)+E8ST diff --git a/toxygen/smileys/default/D83DDD29.png b/toxygen/smileys/default/D83DDD29.png index 7eaa1d62af283de190bd76d526e1841de7070669..59c3282a45f2710990a47ce6093501a68d3f984d 100644 GIT binary patch literal 1503 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 literal 1384 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY-7!m{~d+yTSB&<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3owACkt0MQwtYE0~aNz-V}1eOdseNeNf^+N-QuTUEaktacj=ygWiVZ?A}Qtu0#+{LtpTj{u1(AuzE6{+5| zSjMnDar*B+|IAU5;nV;0^UqH01CD(HGg+Gtes^2Eu+(bq9H4;F*Q&d7eALo*-wmsG z_PDV!ZF8ictKg)QDjZD*B-#qwr#8#K;%?9nx%S$0O_+Aa@yD}I+>B=|w2;xdn5E9xNw|;rz|4jiJHt$)@Cb-|} z^wFBi)RvgAGp1>g2KSri0+Mf7%>?gs^Bga1@J>^1KKS6MBahIxkexB}9=#F&@zekD zM~P*ZSK9K)z0>@*emmbpg^qk>&r5flN@DlR_PSN=zrX$X!Fy+~as1f8o^R52s6yrr t*TwcbK2KLamvv4FO#m61=pFz7 diff --git a/toxygen/smileys/default/D83DDD2B.png b/toxygen/smileys/default/D83DDD2B.png index 40cd7f9d4dc4fd479b09fd57932d4d2532cf061e..d54b05bd86e381eac43012df768c1b55900d6972 100644 GIT binary patch delta 1360 zcmeC<{>?Q(vYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_C z`_2QGuH8C&@yd=p2Z2tRGH=kgkfw|>hGpz9`1o!ingDLOGN zIWvFJ>dhK@wy8N~HlDF|K1mjCQC2?5w!XRI~8s#{pN z#~C?<=-CDUT>}gT(dNURz~Is-3GxGl?3d7c>vW?W4qV>vAphdP688Nr2NV+iH3&4k zuh0DXL;iHz^kav9-Z5OeF&C(qG0EHAg`tC0)&t04FY)wsWq-!b$EC^^WXLJOz`#^r z6%tVrlvu7%P?VpRnUkteQdy9yP?1}}z+llkHPrXoZ3B)w#a(iaY%G@w=bX6pb+2Ca zA3h8Fb&t2jFBbQ9>u6+5{PpuMdu@08Ww-jG=`YWA>#CJ)es${Tja55KZ>JdKe=ghY zn|5M%rpGGptBS7e;-W9iUN1Qte*B=;zE@#a4Gu5h;4?furMLUe-jlD+7F=*^xOqtR z-rKy{-fsdG4oGj6tUIwQf6tZ6lebHr$SZV;SKo1E*9KmLP1Z~1{5~W6`Jc(7iCNiN>jDb(mLKf@AQ9)VeAB91nkLKc^n945?KV;L zq0f2wd_LWKmy+2(DcK%7Eqtu_4a0{MM<>otI3j(B$?47icdRiEt0r)4o99z%z|#AC zpDxRxWz9D}WT}6f(X9}{9`yHNF;m`q6Tvu#ZM-d)9|U(YIMnN>NywaEBiqxR-D_nn z=oEhKe~yjTMM0U8v>C-GD{iy|bY`ib-_1WU zM)V!{Qh$P>_tiR&zGFvM3jW!$o5yNvOOu*uuxC-|BI^)GevLb?=54=mRQ1WfT^g2t6O4zx{4r*7+U%jGbce(KvYajFC$a7F)%DduULV{J zIu#YT{7bsk|J5yCcSrDE+bYZR>By-BYhHHRo$OY*(HFZ7F5 zL$7P6hU-Q-U=r=|ba4!kxSX8ez}~~r;OyWW>>VBL9_~MXCaY3H0tbs*TiY?UWovTY zI4~L)b8~Zhb8Ba7OGoof;PTV6uD7bP0l+XkK4Ld2( literal 1422 zcmbVMe@q)?7`}~)ZetlUV~jE7Qe;yHz23pK+({{I?1X-L0Gco}cvKgZ;G20Rg7%-YZWtqSV#jiloKgj;@a(Ca4 zd!Fb0@xAZ0l@!0ad*{KO2!iakirw#aJ%RYHD)49Xgc%bg9?PKs9>bI}|>4FxyQJqeMXOTJ`o{kb)oWQj3)@9&XG^wKrJ-TtJ zp*5Lz(WQ)a!xmf_)K!Y&r7^6ou1-^zsgYzihEo(3(IAKnh{*8Oiwft@5Phjp1qS$> zvfxz&Nkk)xoI|Qr3~K1X`nxqGsq2oQQhza|UtP`DaKa@Kl)sMr{T8h+960#CCzg|kE^ zi<2zD;y9jdB{H+KGqq-tF_T0V8`ao~o6S-tGo{a>ER+?8DimSPrYu&ii8PZWi8E2I zP4p?8=mb%_0<^oq&3q!4w#a}}B)MFYYN8cTQY9&puS)WwmNFgsVuc{`Ql0Om$nmU4 z%K%w;8}L?H@}O(^rG+i*vwBu<#?1_+rz|W1TI&srKW(VN=;Pl<2X9pKjtcf$d8D|Cq zQ{OIJ$a{eB_lH%iicQKL3XOYL{z!A$oDzFRf6tP;zdosX~h`CRznoRoGvv9-0O@;cJ-GBVkS z#`Y)WHa+gFox3%#CQOh%^zLd3^Fj(6jDtlcm4JhC){}@uOP|xyC(J8 fmlr2*tK#iQdF<$+v>$?pBY#L6Tg-fFwx9k7Pn8T3 diff --git a/toxygen/smileys/default/D83DDD2C.png b/toxygen/smileys/default/D83DDD2C.png index 0147271348cd0b5c8bd95dcd2491eb7656da26d2..abf56d79e496532d62e7c122c880f6659ae70fc4 100644 GIT binary patch delta 1378 zcmZ`(doy`IgBJv20$HbT#N+rzM*8cYFU;D?NbIN=5zYtqjL#A15~#03t8|M8^Rjhf~oD09=6pc;^LxZ4Ln2f_b&B zcJRg5GbgF!jg1ZFl?YQ))9ia?FbL8h8eW*>0!)nWH*hYIkgTZPUuPwPg*y z+-_y1P<|9YwL&Oey|jYYn8VwvuM_5krFM~fmpR-=1oexntDHNeba$AGpnva30tw0n zPH>>gc6K4F&Z}x7jn$ss+`Ah9g{TNBjqTop|#`Zv|6B+ns=u;(LZz8#|* zNxL#fXAV#LO%z)NiI>CG{kr^z(x};YFaTJPz1Azi`)nxAjIT~rY|$_9K?v&Wx|67`!kPN!2v zq-vuA!%X0wX2HnhzoI!*5jj0&{=34L!i<&zs)}b6bS0rRYq`6OkYm(y?sdf-+#aWg zHM6EdbpFh`xX-)&0!4PbfRXY(Aui;@QD7Jv^2Nk-RwDqh=S!nwjP`)3^$pgG6McN` zgO?(HQ&ZlV(n6s|diOs|PzZFaz|&7PExy6Dpb}XIQtLLVA%eT@H0OxyVP!rqDNv)p z14#@MZOH6$7MwJ3Mw2y+V@%>*x;(XZ()jC#;;XdU)~K56BmOTJ#_P2-^dIP%w*?O1 z#)}_c#zsyl_2&(3W-1u8Q}6E=2PAe>{P5;bj~@4zi}c|pLdoWkTie5_5 zIl__AWM#s?`th!}>4!uR1{Z$T<^{v!slLr`XiAWOsLMFV~1T=aNu zTwFerBO{aAdF_P?IvM7bZa7(jzP3K0zOVyW7x=?5gASWnT2n0{TdPC1hp-1A$QFWBL?2`S!(at5 d0{pH4?EewMVx92mdrXi3IFnD2Dv4e<{s!Jxc?tjk literal 1530 zcmbVMdr;GM9FNx55Kr`Qli@TM-Jw&OM`>sy1=^<3ZoCfaK<32I1{xtrO@l3n3eI__ zo>QmiI6POz+}#v5$N4~~!wEicCmY-Jl=+zA14NzY*iKNP`xU6$AD(|Km*n@z=kxhK ze_5ZFGH+bS>md*XjkC_D)4>=Pc*cwZ?`hqB77P)JB}>VW@)Qp%bCA&?<#Mo9VDq_j zj&&3-JIlpEkSdS2Whq(7Nt9g@)NBBw_6jb*hM>3vuZy)8a0;Bu<@2Hed3f|V0`m?7 zlBrF`l3ixb$?{;5qJQi z6c~^}r?QgMV6!B1uvV>!wqrOBlcXAt(UK%S2PQC_KrwKW(Rd7{B`G2n9=Z@-7N*f`|r)Xm_!wu-<6VJw2#Eb8frLyA)m$ z;eaBWD-|gQ1b8|ag5XL{el9G!hY|%+hI&~SimNeH5CU-xpxsJ3_pci-M7wRpE)GrS z+)|Nj2ldFC9wdX@z1&a$2)vMZal%9+LtMxI$r%cqK?BY4pJoYe zfes8zpSM0(JU={~2u_R)j>eS};nyK(_*+)mX!HJ-+w1oysBpNxw$k`srmv&x_Qun3 zix$;1lsXoRalf!b>QH^h&=T|A%FMh zbyt6ET${?gTmJW7r)DRVtH-F`ULEC3T+FGm_E#U9bFpT;FXF(3?G-?_KZDNdnKJ*= zk-of&7Pw`6&N22<-Li2_U6W={?in$n{`iXSC;PW~AeCxnSlx|?jhSt`sMUAx-Sa&3 z`;IJs>i_kVef{@3yZRHnf8JlMQhktEqV ztA`&%9xuCI{p{TN-?!F1s;wb~`qOtm?2h^f-;d$n<#t@Rwc2)!UV80`aC)t%uCkS$ zS<8^Wb4 zeO)IOY`im-*%1~#k?8C0K%35N_FXIMNlLryV%ODV?yIgi*V6KSPsdNw)@7nOZ>$-K zG?VKp4~dc1SJpR~8XL7+`xk%JnNr!{{30ZB?&XD5{@xjcCDT7py>q>8X&#u%xfxofUl0tuNTC(OJ)uu%Y diff --git a/toxygen/smileys/default/D83DDD2D.png b/toxygen/smileys/default/D83DDD2D.png index 450c039fd209197f98cf0cdf2379b1fc1f8d2410..be1709a5b061371bc83c390b0516ea52d2df489f 100644 GIT binary patch delta 1428 zcmZ`(do-+@j=`Tq_mL_WpYRaL)V3d(P)P&v`!Qc|OnOe9yN}`I&NtHYg7OfH6kx z806rA)(+MHPZVCV*umCXa3jp{$0NBL5+30c* z0Oa(X?I}bk!G?4+kP&_5Wh2)IM!q1H|VPmW4({`iF$wd;AxKGw0X}ib^5Oh)S^j zN#(!r3uaVQKUxFH-U-?RT8QlAD(vXwJ*a8x9i03;Gc-CjIx!_4{*ZW$8^(@49}|Z} zp;}toH*C_LTUb0~Lz_qofYj4~-m z+$CMIWLOsJ5k6+6!8UyS5U6!RgI1E>5k#P^) zm`}EQY{c_Zfpp~jj*@J>;k{G=6ezOD4n)NXMOBm`%Hs+V2LLcInsSomN@b&&;bEtP zLuqJM6qAOIq%#3ve;SzW=2BOHRgwHKiG~{pxfZHQKdW)8GmCwx#SY+pPGMGC1zN4OVBiwaq3s_TzZb zjhMvv*;nQ>OG$5g>GVg)%*`AldL6GNC8o*|MIk<`aR?cRR2$SgE{x^GN63EYpJ~K= zYItE-USt}Twf!~B(xI)*se&wY$PqU)-MpMTu1bU(7q}RlD%uJEHJYaX(}xWNXzEO< zX(y-G#l2VsJKfQUhp065;!K{lO4G+D^T*_<283Pp1IdTJC%^|&jz1s288=8OKy4xL ztBorSDqCQL-N)gc#a;*C;+i9cg-DYVUC&h=4HZ%@b^12ExkvI;F3(#Owz^SerXg1_ zCmK34)GluH7f-|nslR8aly(_=@P()ADc_|U`W}wpdJ){Bsvy|BX*%gu!Kp#E{Wb1o zuPAY;wMno>x)v+8V+^*8(lVQX#Z&FntE{V&MEj8h1nLV~5ZCl~?R)!US)T7K<#KIr zqqDgz4e@Zsq}rH#&6fC9**2V(qHepHs-yp6^L%4Y56AheT^g(V>D_~R`J*a#x(A2N z=lnn+7u2W8 zPo7ou5;m<{E}&aY-;X)L-2UuPN4R$9rE<^Wy@8KcDKYLNmWaj;46m3B9YN;!YbnMA z1V(yN3cN3-20~A?h-_<3(dwbMi&S4agPOKoO%)9S!Ny#EgW!Nnw%HEtiJYWY2ZRGf z1Y=m#=y{U9;VGl}?<-je>J?0^uFHK*Y>Be;GI>rP?M{jSDy8U7NW0$*oKkoND zzvuVy-nOEGS0s~XPlg~!qF+iH!8kqgB*cUF+(D57!z@8-7K*tF!A0>jlyBoKG_1E% zm9&wjZ1r#S(m4;tLC{sq(gvO`ZYVHW8>+1;n{-Jooa4p;ft?m8*ln+2ow!>q9plBpJhCm9 z!eb^vwOaZ}l-W=O6C6*&N?As_Re>NdhRKk{N(@65!Kea3BwSSiQ#A_Jbp=m zH{Mo(8%f=`FR)Tes|3M;%jGVYOXkXuaeSp5QK?iB4irrX7U|A!?QQYLp(4ml!I$nEw>Tw~Tw!ASorxP z*q8171=%%ObNJRwZ|I>|=uaE*_xAqv(GhNY+J)A6x0-8PO3mRIkEkBq6h4sS3X2_s zdp$}$uhWA3x zF6g-!2+p$6?ZnVa-~?fFE=FqdNy0E zkgeLMIsXj2tEKf0vVLgUmVp1j(Vt!{dAH0r{DZIkSnA;;P2s6+$;-cf-E%?dGPIuL zhrahHHD4aOIX8D*(WQ$SOV^H$JZP?zm)Cn{-$n)p91|1GS5{sBbln5Vt_GbD*Oipx znbK}*Oe70qZ+gpy`kOa2CZ;b;KK{jxz^0%3KAY*of|rw)m$&-noZj%}nrTyFI-bv~ znRJTVV4kq`?U+w?`*z%4(U#n3Ib=zDq5b8)uKU*p#ZVxKr&7O%hJy|8{#fJG_E~Lz zn&8e&XU>njALu0GABMu>a3~l%V@7|qBkn!!ddEP>&nWTzslpk}~a`i33N6 z>N8*>W%*Z2JAj# diff --git a/toxygen/smileys/default/D83DDD2E.png b/toxygen/smileys/default/D83DDD2E.png index 2c056bfc29833a354005d14b5c5181aab78837eb..b6d25a8cf37e73782955118b17f562d2a5e6d888 100644 GIT binary patch delta 1824 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 diff --git a/toxygen/smileys/default/D83DDD31.png b/toxygen/smileys/default/D83DDD31.png index d115de5bd925d84c8af65f734060a67b5a767e16..35dc231ee5a52869cbed769a9c9c521c79f8a1ee 100644 GIT binary patch delta 1471 zcmZ{ic{tR09LK-8CTG}5iV!1~sm2jwRK_umFqNaVx?H0aOAJOrrj6vx5mM8l z9F;BiAh{CbXyi7SF`5~SYkzI~?6d#u^L(Dq=l%YCKdNG9(j3j+YO_ib~A zipY6$M{@v5??|qBib7t^+t$eufGc|eh>it70JWm00SH9_!1MqB^8kPyK^d=390Fhq z^0UAWL&72Qiq97%R6PE5|{4w%Lc-068_UmmUy=l@ z^o`{wi+!XuZVtQ4f3C$-!2X@p;lmq^WHz}i&t7MDc`)mb^C!Zl-&u1R1ZKTCv%zwa z?mSbAo^L-qOT#c5(Ci-UOdWdS)sJ)S$oUV*zqg`3QmV{s_+hw63A!q-oZKoP17JdQ z!aD;IHD;F!Xdb@%#QJx}Ao34z=UNa*U_{JCrR{dA? zYIPrpW{Y9O<<+5w+2UnyV+Gcn4G~^XFtlMdRvJnsf?<9zG1CJVSy6htBgJSf)hbdUuK&E zeFeWHNx+D z94k)aCk5*Sw;};|^mg<*{iS7)wC+dx&1B;C_1O^@uN?HU)OE>BUi_sP`1xNKgI)XI zCJuP^Y-bsa|CZg`|FS}Lmq?Y8>BJbWsxpZ^ueYNn{Bb7b%3~%Gc0iBCM^27@}9>r8~v! zT+20r9@60XP89rTt5WbP_6Qi1L$9mqV~bfalZxB$o}7C2K+nukT2+1%vD>8e&F+Lz z8JZKh?Xxj;RQ71(ZbWpxa48`R0aMwl=uBD38SmY+Fc&nKXHJP2P;A!=F^KOYI zd2m_rJlyt*xcO8V%x0Wjv7S@OJyV;>s}2^jBS0#txLN?(5;%6Olip^F|0V{i~-XrhZk qVNfVqNdDA6fX;U!AkfFpH{$<^>ix>6?(Wf&0Kg81x1w5j#QX&)j?G^H literal 1598 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ4|I$^C~+Vq7MKt)1%jCHqz~l4Gf!$BFvk=D zv&O$E)d>s?OnIIzjv*DddV;+(f*nQnzBfPVJ=sM5(Xt3#&yXDR+^r?5i;`ULbg+hp zl>~~uS|n}GwaD9i*N5gN!(D}myD#=G;COq8?Vx;^_|Jx}rh{s{2cM|w=Rd1_f5UaH zXJLZHnZ3{F?KCcSD~uJt)q7{J?ygn49y@6mpA?Q{x8m6vYcSvM+pLZIeqK0m!`XOS zW#6^AEK|11io8?jFVDX_mrJFrtt6gfP892^pW#ch%HE~?sd)Esm#oe`ubyo!-vh(X zf14n9hhf!2*0vj0FUyziNEiF;{F;kr&dKySZKW?hZ!Vd6eHw#{@SI;lHI?)9w$JKq zo>9L!%ppFrn*Wf>^LH$h<~{OL?oCpBe|rb7K%;rKvV?w0Fz2$&Q_N599eliJg6xx^ z&b^KM7OG!7$m~AT-s1PmvL?Zs>%<(l)G;cq)jap7QSy-z)MKRnGVtUfbavwLo+aq$o-6E`J_RmE9diJ?1#S6Dj-_&I8a6`0H z`}<~x%15I2Q@cXcSq(n=-ndqg`El>s$}5*|?ml^8KHn>i4LTFqj%xbbT-?$ZFjwTj z=GadaqIY>VYl?lz`P5Qtp5pXeBVd|G^P?%|%5QXlc5O1b)IR%T; zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33MCF)>VYZ%00Mk^R9JLUVRs;Ka&Km7Y-J#Hd2nSQcz%iD0iLVNqw5##x$YVfYrSNs6zIjmo~treYWokQtkZxbsGt?_PNI zV%TUcz-*x$?|*i;%{kUmARrsD9(nLO7k2lN$tw(Ox7Z>34$4}jX-*W$bsr{H+L);! zpUevbl~FHj3kz$^VY|ahW1XmDod1@n4YzeTu5fY6r-vTp5wd`fl8D%;G)iMJ$}28- zGOsA7`}oo3@S<&G>_A?)#keWgt4Zupiy&AUr2fcx0B$dt5?(;IHI!43Q;q^aFGd7xPt^{=*+rwzPK2bZ z=wAnqPIMFppB~|yM?GYm(265|@N!kyzl)l%=?eE5^1;t4p<11X1_AC8A=owAHe`}2 zGCF&1LVsi#r)h<_%te@C-~}VJz+<8i6pbniW`dE>ebIlgCAbw#D@OXra)ElGa>k70^@QUZRf0002hNklB6z?&}>NmYeVRg?iJUVY$v z!}|(H3YyNXWAsk{&QG589(04nop&eZmVc^Hh2q+a>0r@eX(`LT!M^Mf(?MZ1$`-95 zl2#datF;1T2U^g`<8J(6t#H4URgkdb;x{8y5sM^nwvZvn|nMWmf=miPr2 zcT*(PObL|$001R)MObuXVRU6WV{&C-bY%cCFfuYNFgGnRG*mG#Ix{soH8m?RFgQ9e zFvEGy4FCWDC3HntbYx+4WjbwdWNBu305UK#GA%GSEip7yF)%taH99plD=;uRFfg(t TcCnEmCkg-nNkvXXu0mjf^CaNP literal 1327 zcmbVMZA{!`94{jyWIL3o3E8Hu7R8Z3d%c(5UBii`?a4U~*?|s?=*soEy9V0c+QRh+ zP6u;3SqM&@)9JP>i!+Pz0S!#Xd;t@(1T*Ih$%c+?m&M>Bs7NAPmiZJo^aI%ko3_vY zdHVnT{_ovWQ~gBVn)PcC1j!3la$&gUTTgBdd~ck+Ex=`+!PgtLYSc&y8bBCPZ3Jjg z7GfX_1TnS!3@Ag8?5I>%Z`6mX=!h!gf~A9-asr|eq^#Ub2$3dWpp76VDSm9`)K?fP ziGFOWJ7f#SJ4t6GSqZ#FI{3)nWwc^Z6_ehocl~lB%iq8ow|0U5iJ zWp@#4zay4rH6R$OR;Q}XnGUFltA?t_)db4cQs}0wk|L@}z1S+xVzeC4q*fpXG*w0y z@=HrA*mpZ!9y`O3EDt?^aeM7tfPoQHQ1bbBC$@wY|4+^c=nP>s$A6k7a|Aok5|>*a zZkC4!C~#slI2yA%HavOn{Al~+%Nri*j(|%u$v2L^ZW}pr zF8*zk_x`@#w=w(B&&rRkyav4BlCXB9hZ(y*_vC9MJ5QXr;W|*1ZOh3Xzq0q?++Bn* z(Leu5)}Lj^&rfkb+%1l-TU)Yg&rh9i=Df1+qvChKncZmagX2T>CFbF(BHJ)>r2n~3 z-}qky@z(jI=212 k`K_m8=1ouQt? zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33M4vYMMT~J00Mt{R9JLUVRs;Ka&Km7Y-J#Hd2nSQczyXrvi|)9;_Z(hpwMYSO#*sF8hf^_!P4 z$u3?zy!o!1$lhMdD~HL$Q~thN_(j0mWTMIzdDm#p`w zx<+*@^o?M?NPp(L-+d3|Tp|LpG4ISn*E#WYADO(uz;@#bd#;dNs%<4FVRAi&NsH{N zvLTH_nV(9NG$vVI z4a1#vML9jjC40b2_K|S_dC|uEnTgS1tG-SV5)&3 zcs%BPNg%8Th-Frj*rOIC*fvQ0mh%AIUos`UfNbATPDM_c1%O_R2)I2|I|SFT!ah3@ zlCGkE9h{vQC=NcI;X+3};+oKkBR+Y#D(v4yP1tmW`w02L&uUby%}s* zE)d7KaSDs?e%CFXj)aUust+d}PnoAf0qxklwSP?AIEzgv?YWTvk`Ba{v6CElc)>GU zG9Nm5^}kS%K5+o(!9hl;BwWG3;9<%ReKU^`Tc*-Oc-VL%kfYhtN91qI{_3~%-G)2$ z+s9ch{c(__>yzf}Hc!Q;_3cxjd*al^2dPk#C6Ek&A;VmwE?wZ(|Mc>#U8ic=v=fHoax001R)MObuXVRU6WV{&C- zbY%cCFfuYNFgGnRG*mG#Ix{soG&d_SFgh?Wm`vZg0000bbVXQnWMOn=I&E)cX&`2K v05UK#GA%GSEip7yF)%taH99mmD=;uRFfi#6X9kfWCkg-nNkvXXu0mjfqAlM} literal 1249 zcmbVMZD`zN98a&bl-i9}Cz~ReWgSlGC3)_W+$Ho@(j>Q@?7H*r-1@gJfR4-Q5@6<-HVZ9i%_diLYpH31Qr&{y%AIb?Z4EkUgD3P=u)DFR

9GK3ZsYsC=p(iSdop(K$dwa8B^-ml;!!VrNO#gBe1)UzLEFKl9Ea~aVpzIe^_~pMvmUJsUE?LFCtcgb*YYbETi9o# zOpF#qASuWL6nC7KlOl?kLlVy`QDOtD|DT*u$QdeVj{h`E{RnknAa1rk+H4LFw9v%3 zXf(>bGcy?0(2>k6>ad9?cHeA}|7Z+#hJM%bH@Me# z9*!2CUUEL0nw$iM#p=E03lkGpTgCR4r>5QGqvyL%e`+oyf1Uh${=ub7#3$u*KP{7| zz6l+?h}|fCw6fnlgY|V@UwQX6x^4A0KApS6v`JMuJ^Sq!@v$Assij?)%-myF&rN^X zuwXBo9Y4dgHSSGaIQ`-CXO)vDZcp~Vm$*LLH1iPIa_86~<(bSZ^J?SbpAWxrY;3NG zh2Cn){IdNf-@H8EM(_B(DRZrI)Vw!#Yu`Is`v)ImqjSsb#jdH!=V0dv?}5tE%RQ^d zOPKQ9`6YLI*YtN^SLc2_?7jK(-CK)y_g=xC?P!g@J^s~1@Y6{t{qp<0FO2>Jj|8d6 diff --git a/toxygen/smileys/default/D83DDD34.png b/toxygen/smileys/default/D83DDD34.png index a1b4491ec1c6a0bf11b4d2f4430329147e695aba..7b308fb098e2aa4500a0578fd88385d5ff4e867d 100644 GIT binary patch literal 1541 zcmZ{k3pCVO9LKN6Wypjg=Sb2=`HX@H^n28aCC}ajh53`0Qp;dX+hN3)XoUMb+ zE?E*l0DV-@K|;CALs1ZJ$v?^&;9@I?|1L_|2_Bq@A;qe_HxH7 zYAFH$@Jt4cjYXAD9Vdf54NaAOu^=1e!g2wiHfO^s7h*XvoWW)RkYoVB>2v^Au+r%{ z0FF@rnCAdsF9twEaJk9H0RSm?Z%;pZJA??K`w(_O*ahJ@gpVMcgm4N%Z0et2YcP5= zG}Z^$`gJfBM)w-M5cWaX58>b%!w_POtT6^5#`^m)i**;w=x;E`|AJy;QDr4+YeVhr zsJsFR1WQyZ%F06d`A8%}MMX#~MumkaGjo|rT_lrHLIO%mMEe5~mxDN=C@=uwm5{R& z+Ra2R&dAONS(qV;DY7(2+5|+@M%%X`BYk9Lf~>KkF|s8iXB%WgMkXZWu@jk+5Sxa4 z>C49YD9B?KOAoM6sMjjl0NI%#9~uf_A@5z-0lse?+K8RCwUF)g>9ediqYGn8qjLEl z#0qlFcM9^2S98Xem*0#@a&k>p3$W7KlyUC(%3Fy^=wT;$OeGcO;ZBo6(xy{bA}?gJ z=s;$k8XMe~TlL;;VOz!v~ucfP6FNg7)b;-5vDIi9Yw50c9BH^0yo~cOUqnY#;FF`Lo$g5*Icv0_C-&vgPQeK2= ziQ-H~v}%FQ(e8Lg;)3Pz?0VZ78_8(tE!R$Jh?dj6@C2zVeU*nrgW|5p^nl=@OI5h? zKbMvS2Va{z#1qZfSMNG--}0r~wrRMZOjR|*ZNSx{W(|SVHP_F_=Q)+BdIudA`Fbj& zx*cZ%^2U#nQ#pLbyR!o_uM*17-2gw2s(7XB*)9$461X=U>*FoR*QxUhuDHp4sQz^Y z&0R3~oZHXqxNnfo)_I>MYHs_eD@p%u$3%ILyweYEqw{yHC^wFXRR(+-E*ex0(^8J|6(ApWSVS>iLP*)w#@ zj+*+i!Q<*rdAY9`?@IN<3ulgXm*|QbXNwxgJ4v%EO$<=Fxi)b^U*@wdVt3~EzNDJ> z8j5p$QZu}coH13sEoD0h1c$zxY3h<{x5tphQdFRzC6jG)>+=0UN$QY9XOKtZJGdIgx$|>Ce6iinL9gOR|GuEB!@qKwo>8P3*by9@ z()IvPYx=sL!eBI=NJw%{5tYfN z-Lxu$$iCpR(@0C9!;oUw)uoq`K$7DXq|@ve-ae5Wf7b3ZY5M+qXdzUbO43oYcPnG01}( literal 1453 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDokG#!67VDddEiKF~4xpu~ZcSYSfH6bNF%lRl6G&pfGlz#LNq z%o-NI@Afb-Fy8faaSW-rH6_^A`$>R^?aA3GlLB2_Sa+AMTEVSlt<{u~{E+X4#_fpy z)}+Vs8#hO6*&d}UcGQXcZsJGbSb=m`*{zYTe^$&%Vd|^S+tj>R;kAs#6V;v1_f${* zyv6je!}|A&J4!F-JX3Y8QXN1DYjggE8)g1zJJ#~ z9&MMk8+K^aaIfn=oBz2#W~C1Z-HZW=)AJ8}T=7-e%V|*w`?k<| zj^~)SieEZtBp1WJ_3Bf}JNLZVwWE$1*nKLyFZm|;y|Nr)>?ceCT{7J)~SMqb2 zr~TCPTARW2w$O8m^j!YGtL|3*ZZ3L${IBwy)N}I|*GNf;_%1uK@yGHsS>95IrLt`= z4>GUIn{d~5N(yVc=O>9Jw!d=%T3o6{gbYq{lq*`whdr^F%)az!J*xzR)$BQ}s-}wG PpaRs>)z4*}Q$iB}+t3Q? diff --git a/toxygen/smileys/default/D83DDD35.png b/toxygen/smileys/default/D83DDD35.png index e7460124d833586e65c149f6c86699417adb3f18..67cf643cdbbfb8f35596614c365a242d46bb15fa 100644 GIT binary patch literal 1516 zcmZ`(do{040&FxeUgC-wg(J2!TY$>6#WTK&n2$f~z@PzyZvx;80KQ@&{#$@k1b`7A z0LvTzZBD^Gmz@A|B{wRpbkM~Kb(?!mm!}rWRQ!@E+bMQ(#=J{dI>2AG7uyp zID;S#fdIkD&%i?vjUWa=JQkmVWGwX=!v2J?SCEb15`t6xkdGjA016NkVv|AO55vW2 z$U|^p3M<#x@ppWzvLr5pF1v((+Yje^re7?~oXuIZ@P2Jprr}Il&zCO~J>6;PMl)Gh zXg+0_-urd3+vvw*HaLtW=eO6MEQjQchcE}nrP3(4VVs(>p6bI_o#_DbvEfWlF2gqz z&j|_g4>-Zbb9o##J}i&}V3C^T=5=8JuQJoKIYdFIzq*x})4+T~Y;{`vT^EJL@LtiHmjRLmo=;E^m9xOj`xq%#}}VpD);nyTFUMh zhd!AWWkgoTj%M$etU8nI_TD{-Sgqlz+mf#o`oNW?aYcDN{)C2RJBbzQz#GZM4bQ%R z5S3Tf#fhhKwyQ=LwCU-k+Ex0~3W|F%(a*Y8Q5cr5flJp8*zTP{7>dH} zBCn5~V^OYdFvb_pom{RYS7x`dw_nKI9q;q0`)@mgUZv$`Kld$;7^P@=?l>^3HK8zI z-AA6@Rav*O@LZ1LCjPizLi5~cpSVVFV0@IRes6rPYJ%m5-`<18{NxOZN`{VMe+0I7sp{{s=UZ9B9$J7^1(LM?%wv`<%cW~2CVu@3{eX4bI zl&_A&W%>D6n0&#l6bt>Ngb6QQ?KY3bdk$3lg``f61)Od?y_@VrU7MuLYiUYSA87T& zm)iT}wNV|Yj^SDzL+SmvaXE|gjgv8Loa|AvZT1ttdv3Cs*-}XNuoLT%u4O!Olk|)E zY0X}NV_wICm1^qLmhO7%?3u4GiA`K0ym;YF$A^(-Rz_nvCxJfkm5hoEg51pRW zs(t9uF0kcO)htSz1U(&pZ5%i6kAA%>>Jmr4-s(fh+9%0^@}NACG}^GfIH*BhYT%}A zquc)B5jPB7>RczeeeQ}4uP`5bpJFQ6mbs#f*7Am7=K_>JqOR{Xf0o4EJh=JUpkBCS zDn@aON^`+jYo4OIoh~EyV0pS@wPG0g2BU;1J!oC_;^?9!k%jfnMp=f>L&~NDJ3p-t zD@7cg)QER``i_*9HZ#4Ys=MX1PXXGJUDNg-iTLQ@GbqULf_hEqap&V**_ z!)21ABXj1#WXl%$#H+tyhtcCwJh?1iF5B{O2pcnCN-#AwCYT#<-NxKXuq2W!NhCu8 z!ID5|=5?rkM+gjJ1^Dy+pYW^h=}L^SQl{V<VE69(Hrv~~SVKdmiEMBmL QjeWrYR0^G3Zs!yC53_!mL;wH) literal 1444 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y`gLD2*8txIZAW?5>ATTyf~%}X=dT(;tF&D z(BGC$My{3?7A9tf&Mp>47RE|Yy(#2`nLf}l`k=&tlvrRwz!V5#!jnFb1J69EdB7Y~ z1k4)SC*)6MU|_uB>EaktacfHOd2eY)iCW{?GrUw8ZwKw^=v7ghCRHW8C#s{VYkJ4@ zd5;bTY0Ud0UG-A8J*n#D%Bw++D>}Bi@;ABHD|Yf`GVRTLq`0N+M#}GfcX?x5r#)%j zZoK*PUh~7|?}KMluovgDoeh}$p50QV)9_K_XKVi)fy`&~b`(8)_&{fxwZ)#M`{wg2 zj21nQ+MI0n@BSn@Av%6-vD9aWmn!d$_4u@Ub3Hh9F#k|V?9qa6vn{{N zNn6-^1UVcP+`&KPLR^H5=yYk8!rhyHN~~=Ux3JP+`X;?aTVZPGE;Xjg8CN-Kw%-!o zAtL=^(X(`B`}1$2w=~DA3f>TEG(VC4I;=EW)jQ8FSl#?A(~IOsru!}@nv{7@h!<#%auq|RqJiJjRtPFSHtlfZ*6LZ8CH1=M&prxJ^O6#P&(^TC{Dhi=Ct~LnOpIX1hQk2$&U`J0^gc6MqN=Zj(McPW4MktPi zkeG=Otr#I)?!`979)#ow9xmPv0I*VwR?E;z5&C?Ow^oRtI~KZmYiALNxFCv=oI%hL z26v()0tAiwp*0vzBB*0PohKB?LoR|+B9xjzHi8lp8Kg-jf(t|$q)9r0Vxo-G5EKz* zoQj~(RK_U?GQRN2B7AxW;-~OQ3?If}@;;1r!PEm-n1Z zcu@hvS3x`g{Q~IAhQ2K5&4eeXV5}2f)x(`oXz_#F2cYdBv>$?wAZTYoTOhRfLT3mx zFra!TRO%YNOudJEk0hI$GJcR-^rG*Y4d8)))_4o7&9DqW5^@^r3riA0{Kn-{`& z<7e?RGct&0@bAR<>FI95JQF_}v8I`=p&v$Ju1FSiCE4ynwhc~8UY{@-jGNRZ`5Hn zb9142WKQ$QMx~y8Li$$?{TcU?*&lJ3X8P=4F!tJk#wmM)-Z;EbZ$}>2?mH(Pxtj8W zFLfBb;st*H)M3 z=jO+g*|U2ORz8SjcP~e1p-s1`D_TFt+XU>*{xlFkP;6AmXWmGwR>>Uuu)if^uRmX7 zbAW5mK&R4)rpwQxcB_jd6|U*u6Grv>jC+h7r7vNCwKRxDtUazcHP$QM*|{?z)qlfrjcNFck8hFq!y_x^&JRs#NSGX4D!n;C;sKi73He6`C((Ot1hJ(}Q;yILN}+HK$E}{XVB0rT%1|5ND@)|&%yVlK^9|>v_gUv;Z{w}^ESM=$(>t%n zSbj{T5g$7q^W7ErZSqlHYFXMd(O}QZ^t^R_Z{LuuEUm4rZ;ef17XDr~72Xs^+>8*; zDlDfQv)O8ygr$FtrPJx5x-1(E2IptZS9dp$!n+^({}zeAC;kD8mZyFI literal 1490 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 Tk;63PGX;Q5bhc}<3Apk%y{w0z literal 1531 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y0J@bl767!N%VfJPM?S<-f#jDrKxhOTUB)=#mKR*YS0s=DfOY(~| z@(UE4gUu8)!ZY(y^2>`gLD2*8txIZAW?5>ATTyf~%}X=dT(0&)S+ z-#!67VDddEiKF~4xpu~ZcSYSfH6bNF%lRl6G&pfGlz#LNq z%o}##yXG1@f~q(9rs*1CULv& zDw*&6<5$`NH=Y@nwJmnB{MTK+R&!SP!hnB&eWuE6c%$=ZS-IA8$(gz8>y6`tU7pow z$yn$8sd&D|S3c^-Hbt?UJ)W_e%zSU74)fkG`F1tplR>3yC`a>U^&7WltLNR$ZR_k| z(^mD2c-UQ})?077R>)%2|GtCi^Pj8#eX0CqiTb(&Wm{iYmCt^d7tnk&G=Y8px}N#V zo+OEGcjeOvQE}MEvFCiILB+DO7v!T#3d7Y8?-bWHcxg8y>bs=y(zJxy?lpnTYOYe9 zGf&6}dCQvIk)51WQ0H5(jrFTryYpFNJ}+NqcYbol&pk^6bxqbC s`&Z6%zopr0GxX<3;+NC diff --git a/toxygen/smileys/default/D83DDD38.png b/toxygen/smileys/default/D83DDD38.png index 136df51696e29a997bc46207769b1d58134e81e0..dc39083fb134a5f611b163d3b88bcd6c4edfe8ff 100644 GIT binary patch delta 1153 zcmZ`&YcQK>6n-mIq9vQQN)-uhsJ2Lhbs4uHm9}Doq?EcRjILYFXBwA^OI+5aN$uJy z(Q2yg4AxTHT9<7RS{Y_LLu;aGyG1CLklpSm#k*gB?vI`G%z5Tqo;l~oIg7Y9ocNFc z0{}Q?iw{9bJCVku0nj1ZvlXj@#-w;gFcW}mD**C^0BoYE`~?6qDF7_R0N_>&fKj@n zC&(Qw6tDu=bO_<*4Y1S2*{T6>tOAafZ+#D-ycET@7(iJLl;y%E7kJMVmIEj`4oiUm z-n5270B_iQK>>hO2fz5`S8#3wPCtQj&*4-noM?dK5;&Lzd!k@ZH0+ImgM26@!v{q8 zzyvngz@JaTpM22qnd5m1bQ@zNI4}edeLWQtl)ywj`xYdzTr3Hzllm9^$z2%-g^H-#bZgC ztXu7S^yU>p>YBCO(%W!o&y?kH*GX}T_%B7Gy7q&zOv#EUozXM=fSBasMS^J^gxb>F z;1h3BPl&oa%y?)iQO{1ly)*oIO;OOtsM35CYEt(D^KO?<-&OKyVcuoiqNsiymX4)j zX@pHLY1AmXipA|}Ml#=Ne~!v>U#mX*>4rO4H)0C$N0lanHfuI?z_3p+PR36fJR5fM zZ)n)(6FTYTY+kc~H4wOYjCEJ?6+%|IEKfCeynVH(t{o}^fr-J?m*6y} zyu*%39J3k^{J_I&so}IXMi2qZ9oTC}32?yD?<^J8#3it!UlO_6j|zFiV`;Z8lA?EE zggB3rnCH8tmnM0QyR&uPT|CkvMBiQ&fiO*?cl+Nno0Qws9ECRfG#+(5fS7hEqKN)4<9qP{|DwX~3GACNDKrgNB_ z;@kK(e{WW#ZzK!I*%q-(g2jOx`tXo^Jy{!AE45qhm#&~+4#)^S4B^HiJh%7^9!h{p zp;B!r&bFr<*{3LO4o+^4&L=1oHwvXT;E$gF2vX9xNeNm1FIXfb?!Inetp~svdZ1sM HPfX#zB>4t6 literal 1257 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 amAG}(^_A&QR8;3;VDNPHb6Mw<&;$VD+qJ6z literal 1177 zcmbVMU2M}<6n5!A2&IA-pwMZxj8>o`wy%@8w$)G)+kuFsRA~m`1?Jkfjn&xKwQrZS znh@IdFpxmN6B18UynxUoHcdrLq9E8qtMWQAe#D=c5R)qY5X7d_~qFSvV0Z3;Z zV+iJ0XSs4N#?Jgrx@C{ zlFWfbj?Z~2EZKX;eb_(VTQJ9mP1$04cGBHdg%CIp8+6qvyMam=vK(>=P|gY z0VcWouuy17yixY~HaQfdtRZvMfiBK!Nrrr3E!$G~n52pspmwQhMi&4`>2Z)wBt!sI zNt9Kg!Lb|`08L2qsk9!~(*Wp#l#)`iDr;RrBBQC9L<5_318le^Z0OoVw~i%pAHXWA z4-Jg`0z&0R1@x5=MnMUAwAwGy+YZ>Sg(|`J@OT!Z)u3;G39XEe9C{(YioJ}!o>pVB zC}lLActGNo(}FHbQd|V8CJ9oEsbj7GlQWJu!-d81pJHijkqQi_OQla1OU(ma(lI`1 zjrV^<*ORuI&T6Sb_1?f-t=7~0>CurFZ8s=^+PHIe=F*9MpL{!V?Qv_{xz1Bc^X-oP zQ%4?l{I+sy@!;g#v!`=wFHAlSn)cuP@$%&+ZtQBUR{81u+KJRx6D?P7%*-_1em(#0 z+Fb3?=daqeZ{F;hJ$8rcz}yv{FW57CJB7Pv0sq}`{%C1k`?f<_elz;v%Jvl(hhNU0 z{5W!{GZHD6Bax{ifPZlPmKQvmZ{H<5e;=Ou^GWtJ-7 zl@*BYv9bbH{^#PV|L^PeKPvDG7w7+~y#Mq1|GV4$Z?AkOA@V;i@V~J(&_v_By!Sv0 z_)CKPf*H0beOGScyvG%=b(`u(?S#4s`9LYgByV>Yh7ML)4Y_KqRxddke*B=$zE=~k8XQ{4!Dr|_y?OGRxRkF+ z78$w+avqAj-*)?zsZ&GfsC}G`+T)rt(?w z5bsBoNR|~%H5<2ec=cIcD_isFM*2I6=lcb>CvM|AEUI?y<);tIwr(ODPOe#1=GmK> z<5qQ4)PIub%My8eJDISWo(|a)-B&8&CK~*^&Envq$5-1VsPDnevGG61e#Rh2j%`*m zRwZzVov)4NXenjiD0o%n+ZtDaHM~LfM{F4SxBIk+PsrtcDRN|r661w#LB89;TPGJ6 zo()VFcY1N|_x!o;s#cSlefi7vl;zD7r#$)~zvqtXxA#lbbnZU1i?#a^X&SP6bLNAD z>jjGQ7UwvttzETv({sg&YZraB>yL zM^DI4yrXcR$yiJJji!hioSjeP$Ta63tO>MbzUxOTU!3#qeUtFbmD>Bl?Cn^K0&?!wOek7m zeCgp&-G%Y9t-5-GQX<~~Q>m+`i(`ny<>Z9=1%e4r+8NwtFbHpMSTJD$gE4mu=jsg| zD>@9co%p49&)6Yh%|1zUj?g5bIU3K3JPQ{`9XWiuIn7j_^&A_6%|E74ouYkQKoeC< zTq8J=d#Wzp$Py3d$Py? literal 1179 zcmbVMU2M}<6n3bTAVe6CD`?C!LV&c`zD{CntDz*e1JRbQN<*o{MC;f$t(V%@*tf*3 zCKYXHQulyB@QAi)4`V+S&}qED10Ws{LfWLMn)V}YYzX!+Y1KgEM+36!HtF_I@qlIf z{`h>~ch0%zc&5K^cXG{!H55f9OMA2m8P`Y8>Nt77e6MAYp&RQ%cmUOL)9@jcw@?kz zCC3a?BXUp2Bg>W|M8ES>%s$K$hi*Ll9ELA{9)!*a%Z@u(`va!NBxw58KG4BSxc! zCUA}+o-T#pc;)gku^Y4#MN-CvhQ|Sx=Nu=BYtcHu75JYUD_RHDNe^-r7@!H?B!dkYa@M%dEi$L9>szqG@Ag^aYA)6He zP$f}Tg$~C`SV7Ecd?rgEdI11kkOd$~c_GaMF(ajc*1?wC02{6eJGwT}ZDZwUVinbg z21b4rq47=y^p7Eof-&UL>VQaZ+h@BLY6jb*<5`MUgT8$TT16i@^kRM$dj);Hpr&O} zDr!3MfW$2q1YMS-j0jXs5~MWK##+ylGmbdJMaA*IV(Dy=3XG=9rB4>i%>!N1F+OPx z?$6&ZkhZ!}((={t$J*motG8>*;e+#3wd?4)IVV>6Ih;r5AN_qJaXNPGhwo1wp5Oc0 z;NZIW=*D>bfE$1Bo8j&k?{6}sr~f>7;iFSWZ=W4*9#4)G?oeA6=6D`YLu$k4UtGC- zvzGWI|MoHILVa!5F6z^BPrh5|ebD{&fsv8$(fcv&-G{sWS~vT_s>#?(!*!Spx4v?> zHT~wz>GjFq%z?!Ho$SmXv$OkK8yl^}w?BEGU1?l)1_$@jPgj3@U7(IE%zgRRtrLk~ fjbkTctJYE<_B{OM^wpWKqTfYH@6*l{M&9`s=R%4P diff --git a/toxygen/smileys/default/D83DDD3C.png b/toxygen/smileys/default/D83DDD3C.png index c5f37cb71b93776dc5b9f90c46a1192350a81471..5e58b6fd713e4a1049e875a5e63d47b5d02485d0 100644 GIT binary patch delta 1247 zcmaFO+08XUvYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_Ct=ABvc< zFKo)LkcnG^dN&4it@UqT>D#i*yJ@j!!vdgUAgTvy0P-P1Kv9SU+?4o*C->iYk+A3# z(6~8=Z~y=Qf60k^Ky%X;9{cqD*VmuFvlbr*+OhZQlS4P20d1;Yb^PzYfB*jfuUmT( zXlvM{ZMR>%zyIt1pbx}Lg8V?CfDAmJJ$v@t z>-iHN*GJoc;*3e&?k)urpFcs|CBD9UvFC%u5|Kq@t<4Q zE7TGcw_I9x;N|!8@h{u|&)RaNeBL#k?7(ZY!-P+HMP1$U`do9y{Oz}Mg$~{ey%W^C zn&+I64)@{hnf{k@jC0oH%zhIpqReEJEG+1MPxJF#sr+jq(xnzN#7_NNSAMs*R@ahg zli=KW?D^RbpUTawX=!}>%=*dJ`sSisqb$ECCw;kv!*9*ZRG5FJwei#n@ok3=U46dc zZsO7i^;r_v_p3=g+}8S-O?+`p)r&wmMeZLVLV~ta-89Aa?n!G>|J+uz>}Y>kv-sPI z8yR;Ne1ATb^I^RXLrO}{h2xSN+cq^kcs+l!1J?vDrwQK@Ry8YD*j(SLB4EaQq$;ew z`^I81zYRPdf2S2QypuOl-P4dGtFc^h`5V@TxE>CRd8=fbHhX@xvlg7RZc)AL9qWq$ zGBwNEjr*!REEkF@@B3U7dSicg)P|Q_-z7gCJg>GcD!!eK@!ccE8>_ajJ*^U}VpW~% zl+t;5i(||)wKE0<2RU>YvS?8~b@2EVtt^wR6ZjwNXpg=aS1=M!x#tcZ@?^;3&a@#}R*CJ!rt=*KFWA3ikg z+@4}r@@ZLp{X&J>M+_Gp99*;~!8z;0hY7N&Y0q6w1zEMUpUr1^p7HgI;LqKgoO_977~7$DX(@)MOyydNKJuQ`g?uYV!<}h=2e0@9~_|xTAjFx10y_pMMwH$IkM1 zi$ZgwcK1RYeY#( zVo9o1a#1RfVlXl=GV;_$BO;l9pVqoxe^>bP0l+XkKD8)P| literal 1259 zcmbVMO>Em_7ZjoD*~(_GfjaM{7&C9MG22-0_=RFJb1z(zA*T-Br4z0Yo7 zsH#MSvjHl^F)T;-qRk6~)=VYG zCB<0N7Frv>nBur#m5Ng+yXkSht#qf+-_Pa)F?5lF&3Ku5GksC&9mN?1{EgGX+2-fn`ja zSy+!;cZ&>j_i#fF5PHMCWU8X0m1tBKU6vay+ zYLH=AiW0(XM4($5dvQY$L^3=rg~S*|Ni-iB3=Ik-FR&uZgu_&e8`mvc*0Z2xSA}+) zT%Kyn30i7Y7=J-#uw6g1C;wMLtzp&zyc`>aDpW^?fTB2e#T48}C#u@qbaZt;XL5UR`?T zhfK9x?eAHdoo&4M_V49d|MG0(0iC|pHgF;R+^za%qsxsmXq)L8{pIen{($EIwq0F zk9mdHuJ*av=|=BdD!C>su4A`&NIPCs!Z6;Y)Y=Z&uS+zNoL< zUd$ki=ku2zTRJ&<=%ybpxYj?!@Cs8qy0Yop`1JjcuOmNS@=qi_Kis8%Fn8ERCPaG(Z3Am4OSM46?5i8NK$1-Fvp9;{1N%(vbMk HV0!)^NfxI@ diff --git a/toxygen/smileys/default/D83DDD3D.png b/toxygen/smileys/default/D83DDD3D.png index d887596ddc65488a9bc2f668d6444e6ed4b40c88..8c6b23f890a057c2a46eba2925c73b31f0702def 100644 GIT binary patch literal 1290 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>HFM!aAcwug)7O>#89N`BD$5@G3=;+hruwRoh?1bh za)pAT{ItxRRE3htf>ecy+yVv$i{7cBftzj{aQt~LV%x;SvE;GE#;@0B)_waC8!+kl z^B-H+fAmpNK5|HJ(#!AX?MXXZPA!3p#~g+|MUB z_k{+Zo${fbDYiLI8Ap5M_r!>Pd%s9cC-$M8v)zwK(~#GnFDM*{Rc@*1-Nrj}%j&fB z^S&KxDrbdtXkQ9pVT&|y;L! zP>4yvqHJN)3(~jFZ{|%_z7Vu2j{Tks$8AU6>Co(b}kDDXcCTHc1g3wTdAtR#L_~kKKz^W{)^?( z@2=$c@1Aq~v(m!Xv!A^&%Rf5(>)T)j>!X*Bvaf8jisU<=&-s_pbbZ&M{dp^ufvK~> z)5S4F;&SYX>tam?A}$v5E6`|-C#5QQ<|d}62BjvZR2H601r<;Xp00i_>zopr0HQW8%m4rY literal 1279 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 aFfckWFn5DUKan9P3IG5}MNUMnLSTY@$9n?+ literal 1389 zcmbVMZ%i9y96nYV6WX`|VFOuq9Ls_@dcA9VT^l;M-t|Tki%n?)OU&fjzNIJhuHGGM zAw0~H|cVlXvS={a4yk$=| z7egRpSAcfcxOVQ5Tn8fqUX|p4B(-HKz!#NNNr_5vlxd*Qibg>UO9|yE?ReIrWq~Xl z1z}E>V(4mq9l|E|VW5nOq$#r5RYvC%w0gdHQp)(Ffl)F4RKyB?MfB!3QmsVp@N*st-|(&X8Lshd?9K83oG@%D-T(aJ?uRqu zLvm`eZ%;+mgZpXet#mq-r+iv=@|~LpzZ>ne_Vb{$)Bj`1WN-Ia5A}Be_c^^h7wo<9 zI?8==b2pPy_+%=jyM|usyL-om7vG+))pHy@WZPA|gX%*1^7r^FE?=*{(tK-XDV6`8i!I?~8jg%jvpQ#MRQl$_|?(MiB~4{JKs3AtLCK&X)vb|2~0nxuiVFR=)kqHK|WGGaiv9n zm(x#|>Zk9aQzt*DrH2YxgO8XPp52K%yB{QOeU>h|kp6I1n(IauQfGJQFjRLbJ2!Q< raAh=H)SnlaPA>hBz15G@_GBR=e~f}(FLr*T{Qy1gdiEn%^Ra&bySeTF diff --git a/toxygen/smileys/default/D83DDDFD.png b/toxygen/smileys/default/D83DDDFD.png index 899fe6ec53892aafe5781218b2f4d9e45ea3619a..e189cd1db3fe2048c8adead920c91906f7b9b704 100644 GIT binary patch delta 1649 zcmZ{idoDoI_G`=-M^mmJm-A=dCq58wc?9HU1TQ!967G; zu^kFO5}kY6iCT{{-;pg z41GfyNoh(hmz>)rY0Gl)?BYSBmJgXMTFLGo{KJ&7(k2(*= z<#-M*pS(XbGA+ycmppy-^KM2JR{-eE&63DG`@0l5v z&eLAVLnQx!)I#9ggETr+)x*6u(W~E~u@wqRA(gJeZ9#rPK8Pf9YK>ixRSL2CE@i%v1C0fN5eOvShNMim z5(hnYg7xkab}xtDP&QCR)w;3w(0JWz!FQNF zON5&YgRLb2*UF$n!y!uQVZyTBvp@UpurWdNSCs|hqn5LU%SOLVo4byFlx90rv;=nz z2>F$}ka|%z%8GSi#-+)Oi<=5<)ggPF~-^sx6+Y4 zk&Y^8j$B{Sp3rn2{qr44ELMR*ThT)KONn8mjxUpVWDVXAeq~Gjht|Cx1`KQ(Q64nV}W5ogegDSI+@_#Wik zx_a#xJzo6ai1XrZHCs#9c?dgrsSRA$M?8b zVR)fo`+$QnWS@&M0sb+8_^a0gw*zok9PT*Q`nZ_|)eMU_x5Qgm9m8VrSghTtX|?|m g{|CY&1A>DR{uyu$3VX;jI=maeiR?mZwf9T^8%6Hz&Hw-a literal 1608 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YJ@bl767!N%VfJPM?S<-f!mHQHxhOTUB)=#mKR*YS0s=DfOY(~| z@(UE4gUu8)!ZY(y^2>`gLD2*8txIZAW?5>ATTy3)m5W_>ERwj$&(V6( z^+Kt@l^1TiR=#k1v&(6h!K%=Z3;nvz>e@?Zp7Kh+ouXZI?yuZ-%j-A4f0Y%xxmI6a z{{HFTd;aqqzvVqp)pAfm?paol_RQbL=ZbsXmWG5C#K>1AWoG29yCi3F}E%bd4wBqZ-#x25H%g&s@5qWynmZzs>-_m$pG&K( zBsOf_)>^*B!KU)j4@RcLl?$16y_PcCuv9hZ=aDx@)<5T;P^drWecR9Z8Q!Vq@}}?G zB)cm?w7Yf940oPG5%V6uT^IQC*r%x*6F)!go)xur=Asm-uUx)+t*fp6+%UrpXZ({Rx!*BSvp~|xJyYUcCI=5lT+)I;>?x=gSXRToxiPi6K3R&eq|`H#vIAcR(HJfHv849ntIHh zd|k={-A)(P9)DBYnsvB+{pD#|qP}g*G8L*;|K>?Ce)8?X=S^QtUp31rq#bz7x-!W` z?DqXzm#$t^Yd9Ij&FJ!bT?|t!v&+sW3zAQM6O!>0IGs?qaQ*8LMu!7D3$`BSob+r# z6!#jg0J*U6#LSOn`R`|mbBeNL{R#G1-teJ6z^LCri81QS{_q7yKm2&I>&0L921bVK XYgI3<{#|(&RJMD%`njxgN@xNAdXr*% diff --git a/toxygen/smileys/default/D83DDDFE.png b/toxygen/smileys/default/D83DDDFE.png index bd3ca85e1d9d0fa337065c9cedb2edcb572dce28..d8fbe064d9c9a3567cad986807dc7785093172cf 100644 GIT binary patch delta 1239 zcmV;|1StEF3WExe8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0K-sBR7KL<;nCaS0E6Dp+TZ|$ z-w$@HB4%y?fz!{~-vEZ-8FryAN&qNZLC)CT0EpoxUs5AuV1EFE&dt}{0E^-!W@!M1 z(971_0FLA+UrQ`rL(0_J0FmV>X=5a9c*xV(1(W6;fw3@D7bkRt$I;gbmgpjTnl4TS zFi{XFbAiUt))blPDr#jeQy(l|KOcs(#Lv|hoa-%LLMV5GEnh(+h_u7b(;1)aDTAXf zQ57SKwZqKP7=NMdDt3Ls%h4U9?!n5?|Ns9_3`Erc0004EOGiXN{@GmE00007bV*G` z2j&M33MC1`jueRi00Mw|R9JLUVRs;Ka&Km7Y-J#Hd2nSQcx`Y1062}6Ra=srFbw?H zDRKm(r+pk|jQ>)V&m2E3Gc(IFY_fqW46>}2x@AMZe}Dd>A9+=+BzGB6qxjE%l5>;@$VPo+9{xk6tnxLDP0g*cNNF->B;iEu|g zMHT_7pnqQ27E!T$hwTn4&w1v7aeq2-pKj}PT;UR2PB%ZwV`KpzV^#H2X-so6%d4Td zi>xST#JqS5c=0wf4j?bm@_uE)c=jCt8HEJ&Nk*cgEFk3k=PZx{q=FFaZ8Fus5Immq zz7Pl-0b(<&o!FxmNw9WE{g(3t+&*L?e1L54D1WCSr;-JLUW^FXpQ;^#OQ>a^od}_; z=wAoVP7D+WpON80Wh+n5F(k%&@mJ`;{NZ-Ir>_P@on`saz3Np?_6L zt=aEKZton9 z^7tMC#@Ok6xw2G%Fp6&XNAwdS=A1wIUyS6Hk>H*40rGSn@&raeaR2}SC3HntbYx+4 zWjbSWWnpw>05UK#GA%GSEip7yF+wmpGc`IjGb=DKIxsMxsrxqo001R)MObuXVRU6W zZEs|0W_bWIFfuYNFgGnRG*mG#Ix{soH8U$PFgh?W(ey)$ks&7v002ovPDHLkV1mim B8{+@~ literal 1297 zcmbVMTWs4@7W#%wUef{U_juiDf+SIz_K-e5O_OaXMUWIuz?*iHKGsjOR6AO^FlbGe zGHgPKRBXXj5X+ioj>YkOKJUqUJ(}K!lMKVy8WiP*2)8k$nqtAN8m_8>0F1OQ=S*2s zQCm?=X@h1EgPyL2pyXn)HDT4LBnqYsFNisu^bokB*m13(4Ko4$bz@z$ksQhaJOK=C zP*212$hfLxn7g|hvVqVWc9$;0LlFlAEj_3J)r<;341V!QvcwYJ4xWyXd^^ua!W2n{ z!akDo`)QKoJ7|Wdsv7HYy%6gqDL(8A3nUq)7>eT=e<;j#@EsfzA*$S{YM7#$235N< zv|HhFcjdCY4n$MalbSYAt$=t|Gc_Ztm-3&Td4Inov{-B?~{KE||YR>eyDAvx>F18;eJi2gt)?^PTaHF4vxE z>(;q%f8Bha^~t4h>DZLB-1H&lIidx2Jt6=2)yOE)I8pKw;vlkc`S^2>*ZlD|R)P$HuYW&YU#W2G{7agPCuMJ6wC3n=U+d zaQs#5@`gr-|8^RrrUkuaW_} zLvK$Xm(I?9=6vh)#d1?|`N8p}_jliXdiJ$urm1ugSsK}#IkNckjcX5`uqIm%Ty4A4 zmb$+oG~xVX-m=P5^rpb~GmG;#wLgD3=^X3wc(0afmulX<_Yr(y?&FuXUt4Nb58-5J y`O~`5FRpy_^0`8Ni%)LbT@<9}?}<=dr5fa&zu%nQ4_<7re^SwKw{V`@claMc(8VtR diff --git a/toxygen/smileys/default/D83DDDFF.png b/toxygen/smileys/default/D83DDDFF.png index bb6cad653ba498f68d302fc5e0dc54cc29710bae..89148f14652a951b50b814f0fdae0945fb07b06d 100644 GIT binary patch delta 1208 zcmV;p1V{VZ3*-rq8Gi%-006c6H|hWY00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00Lr5M??VshmXv^00007bV*G` z2j&M33M4lg^f+Sx00Mh@R9JLUVRs;Ka&Km7Y-J#Hd2nSQczaC~uB5f0pWlDbx44QHl1;i7PqvuY!?G(U zW6S1NZI{Np_s8SWnPIP{VaYC8nSQqav~0L-G@ms?;_m5 zC&?l}732%sA}Tq?ux+sNm}l;I&!-W$;kFLP6)w){a`Q7i1`GIPR#m&C#xy1)z2b^H z$%=HkkBfJQ7w?0y19_1e_rZkmY!d*CTpaz0ig-aoy}EV^F0MIrb}0zm zMgMc~P@*F_`1Adm+h~(k z)uHUU2!D}v?50o?pMHz2XG+ssXoKe=0EbJ1a0u3?^6UP>VuCGjPkZ*x20^xxT zikPko+!ze%Km`gchM5E#iiRsBwoIPjCud+9$RS7xNm>*xe<0U4@*7y{8JPYL8K9x= zjOJ);oE*t_+oaLuNJu!O`f!plN^v@5!5NUan8haKdTb?8voJ}?eR zrjwFa`%4MvqXsfk1&v5b1f%6>dx#)TrK<6bu@1Vp61t|d?Puh#w*90x`l{eAdV8Mb z++PO?-Jdj9w|Oca){kF->=aWsKcpPp8R_u+m(VZIFN%~^MX%xj00BTrL_t(2&s~tQ zN`FFO7>1w!oRe888kW{jl8a#xGz2;+NLz!0VMC`|h^`=Sps}F~h>8Y-S{kGa2wF;l zh)M{DNKRs>)bjruEcNt_@AvS251&x<#*B>}1Q!H0mCxs`^F09Z2LynPQYz+0!{UZZ zyWjyZ`U*IS-SLEE69X~Q$s9ngm`vaEjekx9+2~|iZ=KyaTj3Uk1cF4-oek!o zQHT@AheF`Si)J|-#6=dN literal 1499 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 A?f?J) diff --git a/toxygen/smileys/default/D83DDE01.png b/toxygen/smileys/default/D83DDE01.png index deee5ea5135cf39b34bf2771606830544b07cd1a..acf0f884264961dffd8520869e4d477eb8b388eb 100644 GIT binary patch literal 1649 zcmZ`)dpMM76h9c35n@_hrihBoWomLuYAdsZ$FgH6QZzHnh@Dx~l-Ng07r7*3G>p_d zB&Hjsi^LR@wiH4sy2*?}w$wy7t9|!1f9?LU?{mI$e&?Lu?|Yy3JMZ&+S@b|3V?(kb z02urEdNFV`oq7y(uwP7>9*hGa#xuYZpeAqHBuf|PR#Co;0Du%*fV6!86S$N%46uU= zFv0}zkO0g|I9eaH3P4Lt|0%>9;k6%72B3q0G67|4Fa|k*a{ra)1Ip60Fafu)$A)Qz zfW#UtCSWW9RHOkIi!_!8s7zB40xH%RW@1`}#$$}-IF$k_*BA!4axDP$6{ELu^hS>Q zBvWtb&qwd%=)D}NvA>3t1?ad&)xQ^wR-t!Q=*92ot^*n=LL(A%cQKLydK!iXQo2tD zZg+>?YQ_UuK+hREdJ=+a0X@bbG(VJ>L4$(L#^Oyz=<&~}2~d{<>X={2vqU2ml{_-K z1v0)RY6Nrx&2u^lM@H2`XX(X&t<2Zc;P=K!_B2_9UllhrpWhTe>}b#&s7+SbuA z_-6FN=|XfHDiYy(o$TV7B2)uI%^cZwkaFOZKzpc}iyH0Fa2^^xiiUI16+oSUI-u`t z**Kn@lYkL8HDtW(5A}R`q<8q6gxDY#A9k3W%wVk7FfB zPT*MW;wJ#O=Ze=y+&@M&NR2U%pb@kaSH!h#ikawr^*QbH?G*lB58EUL`*h}!UJreq zKTeTW=0+DN+j9bJYr}WEV)Gt8<36o$eis$dqcHl)Ba22Dm;Jj&y>)Bbx%UTiqN2>V z2+qYxlypLggwt}*+b#aWE@$2)OQu_5R{T?56463e`*_e?vjdI?R2*r({Q272TPCS* z`xzyl!ZR1vDeXje8w`?e21k=h4L|JXk;LR*THJhBd~DMhQ@g38Tz~QsYT}Y2Mc(-A zpr*=A#b_UOr(Z#W0FQ0Aa=P2*ljIn0W94gVt)(AEV97(HK`ApX8lRDG* z(cnO5lcG{dldpTK7vOP*8#BM8f8&@7CA?sm=$!L1GIvqk!dq- zW|JcqUiG}LyY6Csm`AqHUeinADlXw{VL7{E?K;QKAeMe}R@WUXR)f?uwSXtTJ^!W2yGuB5!589t9D-J(yl64>+F z3!|1?3GIICozazCQRYs(APla_E zOChx`Q!32TNk*dlpsr;#iE4FX4Y4aIOJv0GQ>*W+HSn@it54lGa*BODr+6b%Se!4l zZM7;oR!~y3g&r2NnQ^3~fYMP~8nz))%j~4%(%=v#bHnM=)u(j#)=${maP?+##2L42 zsat=gujjIuY&lf`0>%P+~ofkTv)WT w1`Fm)?GPN#6(q5?alkV=p3SlH<{9 literal 1607 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 literal 1774 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 HHzVyYD-a#u literal 1699 zcmbVNX;2eq7|wWdS_f$bWnhh3fi}lxNk}58&9O*;0D&lhin1h|u#jZKW-$=~+c;RE z!dTH#qV+&gT9m2p(P+N%7+EqkPIo^ZQ>b@{>lH!`fh4RZQ*a$=MF^B8*5X+xAU7g41hP;)W)jgKHJzjbm{vqj zXcfM1W0iL1-MLzp<#m{z~eE&2riEYeh6^bAcw`KZXN@S5OR4!P9!jP(J5<$ zRwqoBMUUB{Rw8;fNt%T$mep!yS|KJ*=vkmZAaHANI1CEGu&gqXh>c;g%pF&dp%x8+ znMn*c0d7Sk3(qG-bjs7w5R7Jp;1RHG;N~$>_gsyb*0t ztumvmWYmJ^6B_C~baThaRPNqx$PJ{t5hfBCbts5IhHLVTsEL%zM0Dy4Q;TVZFem_F z4g`Z-m;-|#$dBegd_KgMaAguMCjuVVcoUZoN+A&DMnuAF2&7c_Y%v@Si8)e~XH#WFZlg1|7GHQFh0;&|A4eDS;v+PN;C)FkS&k**J+?vT-vYP2mBdshCNNTPA#N4eVn zlQS0O49i^{|0$O7Evf?D)7Pa>EnYVdYN9%Zpjsoe%OjRXo6sefiB-018!A7EQ6bY? z*62k}=gdQ294l>jXsW99Esyo-^eRs;kcWoad!37qw)EEY?)HY0#e;rBSESRGvg&ul zcE=IFV>w^dp$D4uJsD34hedl3sQFBAZ)>&dRYxIY*<8_c2d(d)+-aK zHBb7AL*BjMGwtB=^~?Q>dK#b3%L&fN?%RI-RnIX;gFsc1K9qIhj_E{q4+uP;@QlZeU-QSo$cf$c**_X zKihIj6H6X&*WytJzdZV!Bk0@ZYK ze|QyNRkJuCWEkk{cBJjq>&{M7E;E4Xr=Dg1JP2ojQ1FgfyVN_HPkra_H^?fMKl*q^ z`vCvSxh;E&D|VL;eSc(NsxxcOSHPsN|lZC)g6QTI@Gm}9^`q*&6Zw%*oA9;J|0OPHTH`{%jvY^E1bVJ`#)Ryd0nnC zqPx3iW$@G5Dp$B(OX1Qfx?3+Me!VdyyqVeFZ*fgpw`_*r2UU4Q5Wc&lmDhOeVopr_sno#hkDG!( zQ(RX^UV36c<7E3!zo{npo7H>$HqUoCUL-*Dj(g|75zI+_sVMej)A}8BzH-=eq5HQX LhZAHCl1%$QgTa#7 diff --git a/toxygen/smileys/default/D83DDE04.png b/toxygen/smileys/default/D83DDE04.png index 435b0ca2aac301dfe3ee5285537cebb078888e00..eaddfd3deaa8b89b22e2f1957f06f5cebf5897a5 100644 GIT binary patch literal 1676 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 z {=TP1Z=-pM5uCq5}mJlq`DvUc`8!Z9JJwWNiwLAN^a(h<$-G51ztD6NBf4pWv zKi#yGo&Ky?=dGyiMo-ge{Z#SL5LI1!Q`!w-&g#U)R?7Fa8!5uN7N33YUyB<}Yv|b$ z`v-CObiNUHm|#Oy3n!2`5({cT@cGYa%agS&pEB<}jVeEpJTmE6WPtY{vktBk67O6w zZc!iJ!7^~VVmQz#Nc`gQ%AjH<<3yC(UUzIrfkC6PzU>PN0Cm2;udQvoKHnsFZJNDZ zqo?PiZVS(b24P~M==fd_Vwln4!wzA|S|`+~INTQZZNUcQsQ9>81HRtcKW7)_PC=yc zKdf-kPDs>xXZz>k$@)<5%Ol`$`EQFx?|i|pSAI%LZeoj$?q|O)vJ08Z+mXA?YsS}4bBXP8 zAWU&{8hfY)dIvco@Za;}S1zV|g7l0a_ghA$hOgNic1Z~W*MkBsN5G>5#qclzJIOnD zT99{JSXr~I$dp|+6q}tyGMPdq<3?vw{wE)6;T25yB9PM1v+EfJz4?A(05A$x81) z{3w*!iQ1?bA|@nQ1fx1SWYwXYbOsVlq4=#a86Y?nA%FxVNvjvrUNkk+0Ifz$+rSNB zh8Sc>vNk9iL)6)!QE+xDEY#4}_yc|>5lNs!2naCg()75`S6ByXNC$PsaQA{HNbd(lX1m?lva zDObL=MV`d8WP&h=7!0G)NH?yfx@22AGe!-lMiq&K24OiLC8N|U2-h7QpaD!G_OexYl$8j%7Nf(jN} z0df^A1qgxyC5tT(u$fY>T*_r}6f+#}#VQ1RnUKZdE0{cxWRWtZ9GOfa0p)B`u9VMU zReGF&^e{4GS8KI9jb(ojE0SRdM4(s{il)tUKzK4rpm;KB0Ay+&uyTV|uR)FYDr53 z@nlPmvwE*Nw!^$~_e5pYWe5BITuFGY*U1wL?I2r;VqwXl1Jr>n)Q@+}Nj%cH^uTQo zY6MT+V;dVCZ5}_mf3WLPAE$D5(&OhBb06pIuh6=!vs}D?O$vIgR-AHc6xuu9_KFKT zIkag+{M~UMPr>=GQ~}g`C5K`?Pc&V=H*3&+%<td8Et>V30+zBug4?27njVd5CoixD*8IV|KOyY-aTiFJ-523MN7SZN9KBZ zb8umyPomg1-!y5lY0Es^{jz3o>^y$Bt#_MgVnMU0tMfP2=A819ojaJ`Jg4-aC%0UJ zUp;8(3yrfi^Dq2B?^%wAn|ga83o%};q4qN#SM`9K^N;vsDkrLCqJO=&x=Z%##Mp@$$ zhym}3UdK-j3UvDR+}dSc{yS=iZQNf$CAZ$V`qs6?FH&D{xi-655KE77qsly9oW0?6 z<(j9&>|z**{pLcE`I)+gI9Xil*H5|n(|P~AiKgML!;txfKMV6z4h7^_tS?sipfP(V zGHbVQIl92}^z!cmt$z)bB2<1( H8o%=&Xm*JG diff --git a/toxygen/smileys/default/D83DDE06.png b/toxygen/smileys/default/D83DDE06.png index f3f1c7e671c1bde11ad59cb2cfa3b0cc92d0486f..99739e2719fded2491f96d5abfb7814714dfd24e 100644 GIT binary patch literal 1734 zcmZ`)X;f3!7T$pphB5?E5O~T^jVuE+C^M89LK*@B0YRk_NPx(k1}N4(L6OM;5(NP( z1qUp%pooOnDuzL%w9o(|6x$#XCu4Yu0){`lDyH4>lK7W!uzSBhy&{;s|0Tuo~@d_Btox6=ndu{px-W{YjV}Er_o{~T53cuPNIHNc^vWQ#%MHGismm5 zH83kUMAUDFZULGI+nA}VO4wZxtTuHg6ipxhr6nXUQ1x1p*`}->J!hbMfW{A?i3s$n z@=Bymk`dI)HUgdQ4{9E5tV>HOmps0V6Oj7q`*eGjM$=5NAE zFAGg2p+OxqsEejj|Llogssr>0P$!^1C`qAIeBoch(!UW8H#tzsGN{N5xR^>o&48Xl zMLg+dQO1)lp=?IF@qTQ+l1=wN7M2|&pRAGEA8;b5@jBh?yW3S z)>PZ&q~pQED1{Tp8DYF2W`=PJH#vrrz&7S(rm&5_NK65+KVK3;Yn>#kOnzSYFL(S0 z!>P{E@t5n&KVP?wbGgt<{j6ax4e#Z7nzgpNq5Rd^Tbz$Np1ziK>*{odr_Dqqj7$pu zXti3JPVdx+>Feb9*3m}jg-M?=A}1Oy+8*6uSpTl>%aF(Fd8W_A=AP-6+`owRc8`4~ z6b5uH-y~*CM0ilLv*IK^Vw2w)qJjN_cEi4;7SE$j_a!xlEyUWBueVZXB694+!7S}6 zwYO&yv`Y-)`xcA2^X1C2Ra>dW52Mycn9?Kur;X;Uu4U2LA#K6O{A$lFSCD-8+ve1N z%*)8zwxr*pn91Kcy-juc0Uf+vO1}*tOEow+ljZLz*@Odox1MU;6TCzHk+in`jN7vm zf?*&%s;EunRGMe4Yn1=PjR<0wa^F3TIcqV)6!+6vea-gR5zWEwHlJ^PBBxXP@rO{qT)Jq9J>@sp9G< zc8~7JIEUI|s`UoU1xIa0I|_$V1XcXpY*|R@r*W2k1d_cog`0gsbznAOPUvOQ6*8!_ z{pmR$y)%U0m0H7W?goj8`QK*cD3W(`t#5eI4iOHz7XR0970fA-YpyK&vx`k;7kVDm z^4D*7Ch&w?)GTZHK7#r!?TXIQVs8DNr0Q;ymz$2hP_5Wv6Cl888HG`3rJj45SM=0o z`*5Ew@^=Pl*xEPIe697Lw9<|V@++;gn#Z@~SeCI}O;5HhfK#b81 z4YiFS8ei1TA9t|-&$5R4`DOc#`|iUF&OOAM7@17BrZiNv;D&RFH$3b_zTMU?i;W}@ zcqu$?I!+<>pk0Ou4$ssI_&C|jKu)n{sZoWbgMa9=X7p~>@ z)W}Ll@8zxt4{`AN;}-^-7AvQwDi@muHN*As_>S*3Rdx2zE1fygoJ|*pD%#ApwHm*x zwqLE?)em0VI^$j$7!+FW5WlhzzCVO9vfbD%!ZVn5nBu7?#>4ep6qP66^crAybvMsD zjK^Z~*pAU$HWpwXdEY(@vYmyMHN%SRxZlRn+KxmfJCey|elLW76C@_HI5C<3ESS@d x#bW~f_g@H3j^Sl6xomJ}B}cK1sfo;3b`YD%%1nL2#vT^{s<*#alSkyK{{gvkCT;)# literal 1721 zcmbVNX;2eq7>=R{C`Y5BC>@p{2uP0IB?$=x3^|%0LBtfOR&hxd5<;>eyPAMf2S%+y zMJ1(Wlp;k0QFKOOgbEc=wDlN;iaG-$B2^G6rA`%f#L^9i?GMKv-Pzsm*ynlP<9pv( zYkb^it}aVlC=`mTTqaH+M-ThsTm7QK;VQz#Kp79*n4p#+eMrfUrX+DKCi4bZ9u zv?R8Ir7((6jaHUpLf7WRDOEW-6;DlziUcAod{RJ<5(r??XBu$6ML?U-<&$&!Hj@TS zKnR_HHtAHdA|4Q7CKO;ZV7iJ0f&hoZ07Kav4j2MJED&O{$eTk4L-}kDAL0U27mc)L zQm63~#L_8SWSY%ph8bpHrgSFAtbUfQYAQrj-_nTG_qqxeX zH4<9P0N53gRBSULppl+VhM+ep6z_!%_*A0Elrb%ckqI(bOugP7*900T63~C$_#hfr zW*bpv0*YgsO)By{()^~$WbQt0$POgE;jcDn$wNUh#h7Zd9yJhhv4BQ?VW_ogz69if z5(t)nYzZU*K`=}T!C_%AE1E5iW<#NpX^jtYqd7bwR|17{B`g>uRfHlDD1>2A7>6x^ zr7YGoS8l)w#Gpc_?P^K8Q(X9?T)xPJA_QhqVp!&M1;lGG0>d?!5fH8A0D(zbgBmmA zLH6UBj8=@Av|CWM)P(7QiTv`lAF$8k3SnV12awl|A?2CMpacA4`!r5q9dGd45mChH`)TX3zFAvRZ`mkna;Sx>l7VwI|_vZWxxodww z`^A6^mMyl=b;ebhqgk`6*!|S4j(H=uR=RUaE9YL@D`c{=_8nXD8k$vmVBaS*WQ`F& zxHY{R^8+4HcN8rvp*G~7Z1cF7bnfcnkx1gK$~^N-c!Ny(riEM4+r2g1C-Y?~-*=#E z`%Un=RD}c&IH%9Kx2&(It|oVE_xXZg+NEY^H(2iQX?=H8R`t_>`InD+GXtZ+7p+EH zUe;rmjg{2E<&9OWSS4Ny9P!wmayn(3qfO7c8>dD5``amnW#N0EzrXAZ>j3jV?}VzX z(n8x+SJyR(J&H#EqPBNmJHeiV&))Vk{;YZiz7w=uiJ;a zPl63&T3ywXqpnFe=C&Pka%{bj*r{uyURrcdH8!eqUmY`4QYPB}#r~PLF}Baq@WRJ` zAeW!IpU;68F#XnnXcjZ*xt5Wr+3)dfm+#^$0 zZ}$q&HRtVq(3_q$5OBWy?ZW!IO>McaDhIP4GrX&Io{Y5~4EVmNr6^cx?R?354$;H|75WxyF(N diff --git a/toxygen/smileys/default/D83DDE07.png b/toxygen/smileys/default/D83DDE07.png index 00ddb6e1eb687703768d1205ae1582e368a71529..12dee1b1ca3b89fd97aa42c1d19695e41af710ff 100644 GIT binary patch literal 1761 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 BlZpTU diff --git a/toxygen/smileys/default/D83DDE08.png b/toxygen/smileys/default/D83DDE08.png index b775c51514c65fcce8f7310daf71fe52738f1180..aa09cf9a60c36f8bc04d6dcedd672db21003266a 100644 GIT binary patch delta 1817 zcmZ`&c{tQ-8-C|EGsaFGLdaN?tuba{tRuttELp}jnk0s!i0p))A^WamUvr31#8IJ9 zIJWOpBzvT~BBzf|xjyMQ`F`g+e|>*^&vjq#^W4w#-p~8b{SJ!{iWkbkXaInwu7oR+ zFoARjIslyoBKy8rgya2~E)D?1YXFdZ6@YI@D|rKeC;|YR=K!FU0U#4r_0ZV_07%=( zp2avgI52|j2`I)1lm!XJ)I^zSqb%r<8wq8r3RM3F($Pa%5m8JEWQUwE(ltR_Cz4Yv zagOF_S31ryfy6U{80vte1`K3@E&^mD6Y@q@TR|RZv z23A)FR+3b*qQx>j1nP_kL7 z-Vs8)&6T;l#pN6D%1Y(y<%Mniy z=TnyW&Z7k)bs4zQ%EPU9$PY(-eVHdH%26Fc8) zHy9(H9w~ISiP-tiu&EC{o1dS4*_qne;%_Zvy!zScQKC#vy8N|UW}SoVUmp96w%c~+ zYL@-wm7O1|V8m1=kuHvZ`S0)8h9$M~=f2KL?CyQ|zPY!{=Li0%{k>prZ|}?8OhKW} z{(gx-U%$ZhKmRNf(k`6;wlkx%>TZ4#5u?uA*;s*0tRfR3xJX+E2JSshTv&~IbjQ#S z@n}*!%QMo&HwGUT9vTo7%*98>g~42WY)BXYhIu7!^x|cLVE>nF6S@FqI>JQtw;Qbr zU-n6{0kgmTt|5PWq8K&x9ABPvu+K1m6ST$4}8`VN<-1yW0Ed^9}?`_U|j(EIkJsMyr^;jdIyz6pNAJuC>j4G%RMB&Z-E` z@?7{MRmn>><$_B1C4uRn%oZ!CPR2kXKk%ja(~%zQ+G@di?QI|90>?--T&3LB8KM$J zx%>P#iL)Jt|A=uh*5Ry~am>DYky~eXPoZ%!Vlr>Yi7B!YS@{%ehUbQtL(R z-|_-CkP{|>aPpId}*KQ zULRhtRBMisOWvkGt^dGoLt^xDcJ{hK_bTF6d0Q(w3zmI>l0q#(HcY0Cwe3Ib?2iI@2glP+&i@Uk(~b@b zK=GK1E6dHz-QC^I&DGpx7obP!*e{MkFk3knM`rrYq z92{~fH0*MCL}XNSOl+JoP>H{ikeCD~^HNf;rX{4S0=10Htn8dz9xpFHJUgpE9cUC5 z6<@nv!sC^cL|iMraU5u#D9y?)E3c@itP0Pr*3twh?V8%Oy84EOn~gs;HEW{)q@#PQ zrS*1Od&ix-oq9SDKx0l4^tK_yk a_x}Z$r2OcUMasv80kCB_T6I{SOZgA-S>Ol& literal 1860 zcmbVNX;2eq7>uGg^DLg%ZTDRR`Gx;MKMKDYKu~YZa8dzIR5C)xBDIYKF@p4XHzT+cf(J^ z<8U}Pd4xoX%|ypL&Kdjq{4oGwlQ$|=p)p7jYJp5JPN+c=VL+~jl3^tbX)@PbfkSY( zF-h836{?C_$X6qJ3gnQXSoKB>jl+e6S&fi74Mu@PI9Y2Dkh;!ZBmr8DfE34yqD2|S zaEdk}%LK<{Es9lVrKx!uQdlSuV&!85dKiTOt6pa?^Q{8XATJ+VJC3O&U{C~26Oe|C zQbj2MF=B!N7KIV4rqSsDn@yoJS!^~v2monxkV?Y_JDASov)Fu)0}MYTOq)rQ#8*mW z!@97YfRuuwMn08lu~;Y;1_d!CQ|UY&&%ptL!I(s_In#hb)?kBq)(C?HHmglqBdSFV zfP)cAMAA_K3A1#_1-&sU>LaniJRB%2WK=6;q|zxgs$TE#Yf##ZD&c?4_@uNsHq!`G zm9QB}H>t6FB+VKDW3l_ZB8MPm4L{nX#gYQ)B#1g)4;xUqL_os+P&8T%UrOiEr65B} zXGuXRolfVsx(;|Li-5gU|oI1(v{`ArwG#bO?VA>?3DpkaOwVKx6J zW>m}=s-rmmQ!FD#SOq$kA4?zGd~6=rfOU)sYYkcuB@XL2EK|Z*>jOV!q;C3>$%3x) zd*0M*RdNL1H3ZG)pHA#<@1SU-HYv9f8U?j{eD%?)i(&pk**ghvYr-mS7uKFxT$<@u7yjE z>)iDqC#z$rGZdR^TnOlLAKt2L%68d5p3J|)3;e=gIBAq&rg{D=DEzMG?T&>T^y(+2 zhxazSZ=4vKT{h!f7p}LU*Sd9E(=q=Q{aLO@Ph6-xdXv(+_TkG>%Zd+g$=fP3)UNNG z)!$H(Id7t^(AvN4PGRToF8-72$=h-F>+|8|s*9&=mR0?@8W`{;zn1F%T23yHtN0nYNi zHT=c<$1e9Eba*u`g`;=05XeR_`pZ2v`f`Mo7frr2D3Kv1L(2w!dbx~(-;*H#nH zWj}s2)$@dFxRcN2*Qqm(R>ySMyG*55zMDFGWyMpwPnv2)uX|wA-S^JR+9s5C`pZM3 zz45LQk2^xAYtG-8@K*ZbR|TOwf++Eu?KN=OZs&L3!~c?gysyQxsv#|7rCv0qS^TPf zPXjt3f?!tn`joEwLnr`ERBd<=S9m#BeRP{Kw@r3L6f$=n4ZAq>- T#nUO@aj(gxizF4I)f@f+OxDY_ diff --git a/toxygen/smileys/default/D83DDE09.png b/toxygen/smileys/default/D83DDE09.png index 5eccad2d1f675ec94939585175ff054a26a9cd45..510fd1eea27f9699cec9a1336deb4764040120f2 100644 GIT binary patch literal 1703 zcmZ`)dr*^C7C)phh*rR*6{~a_1O+6}NK=&5g&;&8AxOh38A?rnfLMW$hNsgCh(cm0 z;T1tG?^+QGYpqdA6lzN&2%;?of>d;DQ-dH>86K9Mvmcq+>Hf2K&OPUM&i(zqJNGg7 zlZS-{Y*@Et9RO?y3iRg@(R#J7eTDGOZdTz$ASd~9eE}|&u3r(YA#!SbAdd@>y8|Gv z5a1#=HDk{JllMzgU_Jzq4n2-}`Ent($2tZV> zbMZk9&S-I3iw~+-zwodOXSFz|#Xkvu9UoO-qe=Df_?^0#Q-08L#2#nA!e?LMykI(cr+SH(0FC6Rn#mr!ydqUt~m4--i87e6!-xC2uE3vM1zmL;F^Msf7D<% z;7w@Ha_Pu&#oq~SJ^q@4-vM?3-U6IV87G$30gK;&JP1$+cn7c_erpCf2Zk?(<0ZgB zz@K2E9zOMf;$XPj6pZ>kH~=^d_^SiXl;gA#_5Z}HfOi3hfH=Jz(Fqsgm_1~?(A673 zw4I-gE{#ws|6Og-rLC%TU0Ep|U0!}MI$WxvtyEFEw5B0f)!57VVcOuzw>5;sERGZ4 z55;TV3?veBSrC_PZZNl8x7}~}HDK|HQFXM@_s1j*fV)79&RhA&H6lv@s543FLwX@ zA@9zmLAT1Ym(Lar-RSGrmfWB5thTR<4Ue-w-^z$>YU$DZ6*3-gafBgf?+(|kE!sBr z1I^DfRa)xKk?Hmyv>9+p?Ofv<>FhZNj5{vMF4bQZ?9UHmk- zq@}XvL#y8V3BF8+sYhjCFyxJn~L#5=JcWb7^ zy+Qp+=C;CiE$VYJyuo=vjzMI@jKI%;hw@>m5)-*e%FXZp-F0<;9lFy?b z z>!!E-T)TA8x!%S+zmS}~E7bXq3=4VG)@iw#LJmwPbNCUQFlo4*U0FAOA-?-w^wwv? zX{BA~bT76wJUweTqM1~@ZPazJJcEDfwd(ttS!Fqk|9H7ZRjiN>Ij19WF_(kJl(fK!q! zRg!VpHc@E3F=KIV{`rF0D+hONq@?>`UQfM{y4GUD`bC>0RdKOW@tM5jMd_&nfguW& zP!$py#N`a#8W`xm(>Hkgbb@LpfoZh<0h)WC>maQnQRUKc;lhQsHpsuIkP0ZBQ;f|Q zTDYz1q_D8IR`cU0e>`tsve!^{e4!rkq#l}mGCh_0eq5X|Dk|A%oSB(XjPVgjNMOtN&=KTtrU39#Fj+xw8%gzYjmUzQ+}iJ9?I zF%iIn?&0A^-|e=G5wVNTeCJ&zV~;DH&ZN^*$sMaN!ry|Fw1niuoc}L)OJF-o2;Nwo rAv`TnmL-yk!8ai-PD~9-5haOvVo^d)dY_p1djJHnL;c(L2@C!M#jqBU literal 1630 zcmbVMX;2eq7>-b_B92$p{kC0QMhV0U$B{4$ObcEI=yCEwi8#arHlwv__ zD^jl-b!xS05h+R)j}eP_fQm+}6>IGv-clTE1 zWPEQ(SX3B~#~Y&8p()%rEbt5*z`e&kc!6`n+bm{bQ)wIP!5NaLA?OSe&^z!EX@H(=cZk1Qf@6lCp^T0O92YMiE zmYUz|l*M2Iv@}BkQn5s2g&5aMo`2#!}tWhz7t^j&<;8bjDr zDQHrkEpDae+gaA70zr?*BlbwdG?NL!N~JQOfgmCdA#%@mvba~|bVmmjP||H>C>Kl7 zP9UI&XVAHT z6w*!SGFI+9Y|%k7m%G;+3II88RMQxWI}|({rLDOR(#h&kHJ|$;CMZIM!AclIBp58k z5DbQ4MG_)WC?rs#6it*O@mNsfRa~VKQfLunyc~lhFsGuyAPm(eVp2$>ffY(5$kjXD zEbg?DLAw-Zw~s4%Emx&wNSvh^Gfig)E5Kx@S=w!8`sLGR)-9te37D9$BJ+ER-Ax}MlAxS7Y!CxVjH($sdHCP z6cYBe<4%_@J=jfOhE(SZ-B;?`FTL2`-qrCUs+@WYe{@_nX4MViyMwcfs)b3G^3==q zA3o4}>Gg%fjV2l$^ouTh{K?6{tpZ&W@6W%R#y@H9x1sUWlbWjH^Y7nG*Uu?TXLoMM ze;hyf)_~B9B8jy6@#*lxyJOdGT!)qT@{bH}y}EgE{NVh;?yc%H<+o=VTEZ;dWUaEw z@H8g7_)`13z63n#2YAL2vUU3n(WF-6nZ~%!pIx4A$@SIj5p*8Bc1Ttk`^lSY)(S6d zNp2G?*>!WwALTI<)fb4+t(nnK-CybKzDLXN8U;H9U;H`(Kd@@FO%h7%@|7-(%>kS0 z%Jy!GXuw_De=;Q$KV5cZ(9c!jYWl{E#&rME39_b*OQdBH+oJOuI%~&;So25M+ZNo{ zbUti}GDw$A`>d!r#DANvpCcUg+am1ywg(*zZMF`7bAO9w(!6^G3%@ju+*I(+^|QX2 zEs+cRuQ>`AeJnz&9?s6=U)hjcys|(Y(Jk+aLiT0t8n&Hj(o79ocdmZFfBcS%cS}p3 z#cX=6w>$6^YEPS;+hw6mRhzeXKr!7a(-`gkG zTnMYqF|m6hJ$Gvc?omV}Sw7J1oEd&p=O0~m+;HSk$+25%!TOma=jPpBaTvT>TXx5{ z>eIMLTe`_VtV(cpWoFUX)Dnf!94BMfe>D4K*NC>=dC3Lpm_wWA9)rN=!gB)sMg5}Y sk^Q9^_v%hm92alPEI)rg_F42{UP(p!{;_Fe_<=vZ9y6jpCeA7P2Q&L`9{>OV diff --git a/toxygen/smileys/default/D83DDE0A.png b/toxygen/smileys/default/D83DDE0A.png index 2885494415a446e885283915cee2ee132649c1fa..c24689c5b08253477a17c0e075f0c1e0be4d3f38 100644 GIT binary patch literal 1763 zcmZ`)X;c$g7Ji^?ih_Wm2vQE4!`4YyS_9oJn;p0&&7v$CTWG~FW3)vUF$6*rl{G4x2#qo$C~&*rocS?7=AEzZ{qA?~tvc`3ty7l* zXuccPnXE$yZJ<)T>A2TlxwO~dc9(-519xj9J^Vco5@)PmWNF|rc8o&zN9c?-LV_O= z`h-h@afIRs2u+3|L@q$cIJ&qg&>bPQiGckTIvBYG zG6sxagNbr@TLy27VKN^ku7QjWH}yaf3olX>KZTsyjg<9A;Pv^Qs$gYRDD*@_gFQ%( zz~f^grk>CfWgj+zK@JR_%nH`e4KW1iVQ6rG8XKr0L7i*W32PX?TgkBm84+ZpN{&@E zt`w-R98|tK$gY5d48u9l^)<-80$J+HmhtVJK~S+1n)X1eJ9K-3d@tl6NJkYR=ta62 zG?Spm9eT(hA@ygNSB2t-V+M8BORa)C@#ZO1blMdq`=Q1}4B7-BM(|)aJoN>Y5T;6D zrW&UH0jeZ;>H~KW$n2rd43r3dLvSBKD}pXuMEFZy$cnGWr-8($(^dv&W!a|p&HtvE zvs9pSUy_?+x0unlu<)Tzk&$V?n5%hTvYN5WRDPOM*h>?fNAV7|a6eyfl%ioq!2>Nm z)!#>JRBPiptM&GRxC(^S&z=c7#HX|3vFKP{L==aO@e`xjSOPa1q1|b@OuyUx1nvG) zmrXr1*S?Q&Z*1^m=I!~LiL-gelP?mtG1DyfIy+M;hrT%{&zN`NTGsG+M>!;M5i{7a z7j6<5+ zRGraXjp*k~epB3r0V}?dcFMjwU(?&Vk9*uL67QX#+F~=8c)^d`)az*~jXRJPH0YDy z>aZXvt}(+`JcV z6p~5%xHvm~OZAn-Y3u;$Ny5H|^Fx~GPq8ryo2|3LI|BVrA9v}if1BiatFuj^D-4_M zdg+bTx}~I5Yl@$Iw^rIzrZ6Yk3@_Unvew+@dXYB}b17&pT=?i+V8g{zF?I4t8_)6f z&wbCz4b9r5mC0`TPm}$%>k5`H=GXkXd{rT+u;0nV*QjGt!Lr`AQu)B<$MU)&XR+q+XL?Q2L95eGI+lzp`fxgVi%rBO>cW`xm8D5w(;ln z8gngD-5QCLl~&d?kM&m5Dj&TH^*o1o9hW2ivQ%;%A5&ebw@1*{o?~H1 z{q#nfqvOeqr!}>QN8UJ*T!}7Ds#nJ7gGQ%=eO+xG*Pi?C#{_};!_oh2OsDM~9v|j7 zK5WHoM^%%f(<2iTBhzmt9jx22=~r*Z%=Nb#8JX&*tBi4nc;Th&E{E| zyQElQPO<3kc_mqexp`Mb#g=VYZgx@r_W_534j&2-<%imKVvK_&p@AXHBMfFpR_IZ? z4lMF$K4Yj$sp#*1k;jO1l$Dk$2j$Ou2YPy5_6^DFs~;v7eD_w;)bo$nt}StdLnQ6Leht^s2@&ie)2@pDRcw$T&8~^hl NMD?M0*L#Lt_yfU2Ql$U@ literal 1681 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 GlKNlDeI#Q5 literal 1633 zcmbVMX;2eq7*3&OP?516R1Q0A!l^dLX34dZLXM3Di9!e_qqSv87P5t8!|qB#&{nBv z(Rx5s6s@RJ5vf`oi`9xEhX>y0RAlUU02Q^ZgJ?alb_2xrhvSd#?Cy8$^E~hIz3-N% zPoFw^WcWxHi#1xCqSiCx81EApz`P?Ly+WBGj7Cg!CXq`!Q4(V*EJO|lXl-a7rpHiA zabY`_$YS~D;sz6K(xt&>!p1|r7@o`KVAw2HVv@^&n)5Ll$ieb(yPWgqp(YN1TjU(0 zNGH%aRG1Y{ag$i4d%D5w&Ns^}oTQI|L>J5m*f1IeT($x`1-s;&L0y=cd$;)Z80l3$4b)4c=%Q=jvLm}84I^AnwJ2jjrCS`mV>fnPs0pDix#x;nhXg&6?8*fBYhGGZC z*JBhhmoziyksIYBGr4=aAuo{e2A)CU%%PwKYQj9%hS_PYTFzm<@GQ6mMnD;e2q6R% zAwmQML8(RvNu`iLDN-v%!UV*p@g}ZXCR0cfgb5Nv0D+8(LL?PwAb}bL#ZrYz0r|LE zJ4K^*Gv>34Gj@l$&|A5%io{TwAPodj;H!XiD?t;Km2d#6Ofe8|#O)TsNlo+~&rq~# zjKqsDi-sg@z+ir1{0;VH5(T7CiiH}9M2!fUxIv{@rIJCALc(N0z{EYowfvu)@fm0M z-s1RAvG}%_3iM82mp-$2-8`6`=@^n}jkM4_kj3(=(5e*%*LB*XOEF}GocNN+)rVYp zW?iam37gz@k-Qi?X<6;DknY%}9P74)8Kbm;MlNSN64nqHGip|-#!?x2>`b6C{Yq`l zK+6Cy9xJo|AuAl*0wB5EbvBv}D!PzBBYTlsc|56I8{lpYs4eop zTqTgUWCb^#WVaW7-E`vjqRW?Vl%4+c-KLtBAMDK&0(xk8bVQ|ojbH0R0#NfBITvx_Y-VyYwF;lW7J3 diff --git a/toxygen/smileys/default/D83DDE0C.png b/toxygen/smileys/default/D83DDE0C.png index b8a367a5699ceb836ecdacec4719e589bd1bbefc..a1a9721743a590856a1589ead18231dafa0f827c 100644 GIT binary patch literal 1677 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 Hl+XkKu*q{~ diff --git a/toxygen/smileys/default/D83DDE0D.png b/toxygen/smileys/default/D83DDE0D.png index 6fdf5c6e0a20f848ccbcbdf72dea097be9f93eaa..0ec4145828bd5362270ce56486cf413cd05e1a41 100644 GIT binary patch literal 1746 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 Va;G1wxE=Vf>$N$W-Ks?u{{T&QTYLZj diff --git a/toxygen/smileys/default/D83DDE10.png b/toxygen/smileys/default/D83DDE10.png index f5b9b1251e3bdcb896c06a20aa209a96c6eab5a1..21b43ea6ef8d12387f097593990999899c0ac58d 100644 GIT binary patch literal 1597 zcmZ`(YdDl?7=BP_Ob+W*2}A3UL1Rdwc1=5C%Bg8`Sn4v&47MDqadwqRYaD0BSxE<* zN+?+hrK7M!ax6)SP_5DlsdwMz*Y1yfujhN7`+lDL{=Vz`-uLybrI57XA z$FyUiz7Sia{I@#NA!V_)6DD$ehKqyTxSS%vKt|u^Yd(N_Uhn%7>X==2(SHpM7pPd)Fr9r%>5KG^&wQcGXh8Jbhm#VhylIbC$C_KWX@ zdq3C=SZO>iTJK@Qx~r$6UC-!^_DHe^uzD! zg3Ou(`>)g6NQ-I+8OkM>nU*Gt(#nR*5)(7**~@!(4Fs(5^7DBxl~te`9#?m6Wbxfk z>XZCCtU_o@Q&;Q{>{qzeXE->C`UCWF&63 z(-HR5eWb2lwBK{~*?O_Ui>-cFOzBqbWum7+@@IMU4*8TyYnsx*<_Y3_g73a)#Ty19 zhLeFoaq@26miJn7aKUk8=3ooIS;}#LdeXy!g|`irV|)XaFLPCfRa9=>FWYU(yB`m) z`_1AowMljfY0P~fFZ;!f+X|mbIU6Z4@0xN>tlXWDXQ^%2nsZpN$)Az5r{PSqyT*Wt z8C#TN?00kc)2bQI#|K7y=1iWs#>WkfQIBgfu3itm&-+p_D?SiU;FDQ9ik}eQ1wqyzzjK$RM(&e0P zOQC(eX_7CG!|||Q#&OupYqK;q9)B@DmPFeXnt4iE<}#DUR#8*aFt)I~Xk4y#M>B~Q zf9O0_K}NN_sD3lUIoR7-aO8{zSyg70a?q=ZsY$JgWr+h&t@<--ms-v&eq{r`gBPE} z=5uWW_Hl6mDuqflqpUNtuwq$IY%SN?TCFEjD7F+z%l^`r|1pGwae{d<|8KZzzS0L9 umVbZ3_AnklioK5uG)|a5m&6ER2XdKQHYa9pD;NLM0T>Rh^vX?siT?mSDA^GJ literal 1549 zcmbVMX>b!|7!H&}imjz_Dl!8sTdD{qdo`P8ZBlYyj!+l`D>^WPQ>`!=aa4p3^-J5JKNxtpqDHTcKYG~wwfi=Nd zNadK!BeuXw3%exAMUQicQ>AA%`SgyX_-4WUIThyB-$r=mrBnU6(s zSdlLk9N;_(6C-4hyJs5;1A#YGrr-vL!gx%)qm*O4lEtKj!55{|?WAZ-gV8uaV=5Y_ zF$_yF<3vgdff`gMg9=ZkBN|WR8dC_`Y|tmGX*3xFDte62o6TwiVM5h!WQ4chn~ zQhRw*^J2@Sx{0mT2Nv8@WEJ+j- zd4J{c!A?GSZN=u;hCt}=2ZNV7`yR-WyJP)t48-;6 ze7=IJ)vwMtBYW$Y8P}42PD-CPM*hv3s^f`h(}Y>SEvf$0WFN4uIHOCv+uYc?eT--9 zhV-q~y?Z+bW_EN=nApKI+UoW!-G7U?V2kP+A5(EBKVj|^yJ|^o+C02U1Ww&bT`Zk_?d@?k)sqad~U&WqB zUGpwA&Y7flMDIJ4S}Q&B99Y-Vpr26tE?4P)<)x|TZfyJFb1AkvX`B3*1KTW3u8Q*P z$lf>U;%vLn;F*8>12}D2?|__B%DFmOuMX)xEUhX={X=*Kyn1Mg1#5qhy+sUyZuZd?zpbgSOB% K(=o$}s(%35ggB}I diff --git a/toxygen/smileys/default/D83DDE11.png b/toxygen/smileys/default/D83DDE11.png index c050655281e6a23a411de6bfaca26af7ba3c0c4c..e6946b045416067955a73ec842edad9a7d12b89b 100644 GIT binary patch literal 1612 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 diff --git a/toxygen/smileys/default/D83DDE12.png b/toxygen/smileys/default/D83DDE12.png index bfd07f9f9c3180a73fa1b57c0cf604e0097d6072..bd3e0a219b8c45196fa120d644117e9ba4d78784 100644 GIT binary patch literal 1724 zcmZ`)dpO%yA3xQ&v@T_Dr>K&8*R3yYmj*4dc|@g{ONr2^x}=p@kwi31P(^EQHKIkL zO%T-6pq8peiq$MxS=6;Q#I5VQUaUh@*Gq8TAAh~?AMbfS=X-rV=bZ2N`99Cd5AgRi z+-|WQ0AT3t^4zKy6&VC>~w`Fb}|Q0W8>*h+PO^ z(SNyI0P{Ce3n^fM174Tey)LtzuJoU+^dAtM84yr<1o1x!VwL>U{rs>+-ai*BY+yOq zKVz}A_Go~-SsbJN zZngocJKs!-Zc>1Z3T{RLS+>h)BTGG20T-K>9+!`Z8LDof`h{w3ZB^A>tsc&U^A+mB z+^HVX^2_P9Rn7AAiN0$3((_3OA=0R!MkaikIw)o?&(ADA8`rFA*4Ed55;EZANvI08|3_5WoSTz5{NAgESWa zB>+AGa0G~G;HfMSKI8t;5hRKMuphwZ2~f2Yjsd7LgwGjJ3g8fcgFw|ZL_*3q!Y2A} zjK=1&{qDDSzv^6D<8ABjmq}TSSAVV}MQPst_PDs@z-pOJN9ShJu4Mef%Hspv2*W^x zw&jeEr^m?)+b66MN0;sGN6>wyYqWizw$h0#0zfN0l|*I}Dc_(I5}CAf@l-UMlR!nM z&=UY~Dk=-cm#Z*()8`AU@H(gw){WlDH(iI)Rx=Vx)lR}>i!L%2?V4WV`{3fb$XXpY z>9v6E;z$&7Mo75KEX+~LxNh)_KEsgq8E}podMDk&)xB5}X{l&}yYJ`6$%>QHVhftn z&L2Oe@$ay>9Y;gE%#Td!VZz$w9niZ^yA;Zp8?BZyQXC9l}Nva`MkR8)no!%+oqj z7Ch#E>u^1GB<{8uA*9a!%Zr`PO)1K}J-vl1W-VT3gOaUin_Zq4vQ3y?`g%3cQrq3_ zR)F3;p>GlfRoF0Nszn*a$9uH>Bdv*G+_bemqtJPav>0m=;dalkZqzs^ALaHr&B=05 zyyr$?T#>7biTlZ!2gz1~7hXn3?5AYWuJP_)D^{#KPw)GCu`Zc>zvsgJ^rhBVQuAW2 zj?sd}A$i(haz9VWCja=v zxm5q(c2Vsg{gQn2rQ7I!y%~o9`^s|cF_fEbl*h11ts?RdJ>PswLvt5X{om(zCqg$@ zpfusF?=Y_(p8i-d?BwKFT9sUZZ?LPGE3G{DiOiPe0xWZ{g-!oyddkv$q2&_F7-t;z z`V;e|rR=1;(a!AU*o!rpuB;(S8(N_KvZk9A?k?hIl!p^*qil#fe8|y0#(GoA43Bl% zMDg13S$ccWgD)2ng@c8cZ!;@fCo=4QsAE=XP3$M_vaYij>tm_4T2_Ia!3}+SbO72C1~=L1XK&qMWSY z@Y=IIa__63o#5kYjs*$IvWmie!eUPSx?&zW%4WYApD;7aa+~cQ>K&JN@09BRO@@!S z5Oeh4VF%J-49?yW=ZLYxU~m`=7ym^3KLk26 z`W%h({|R%Jp{5AIeB*{7CXM|KC6Nm7(abYcv^Sj+LnTrv(HvGE75P#C@Fw_s+;)%3 F{tuYNCfoo3 literal 1715 zcmbVNX;2eq7*0#Ut5^}RDi+s>tyXgE=H4M_P5}vE2pF_gmSjU#NH!#!CV(A;(NT#- zD~M=C6p(3UQ1C=W5ygWFB2o%k9Xt@yYPAJR@j$!bu>IlqqdU9%9s4}bdwlOZyD=(a zk>~j7<0%x1XK092Nsg0T&sY!gJ+tpMLJrdinVN{kb%YHuqZE-APe6fC1Cod;QAC@X z(trk0C~i7Tr6$yha9D#I=!gqLw;N0(n?ea%U^gL}B$NOW&_v8Aq&_)SO9e2kkh+wk zU@A-!RF8$EnbGL92$d!+Nh8ow7X$-Ac9;|}pacTg4ar6eY!_08bYXJt+GbFJAqbHq zqz*f!Rzv|3+>8PoI*X=Zf*`=<(m^(d%LM}ghzUXrCV6vdARFdzVTcEeTvXDUS*wGU zQu&B2awVkd3Bm+37&e=YZe!7Lb0Py22m~$-2%?b)nkCgpAaWiZ{YW%SfWglrijxi2>4?41>WH*AUu5DA9l2cq`hXN;RPj zC2GN~W(|2By4j;-GI#Gb#K*JQO5ZifgO})JTL%g;eqjU5ja987Kf{5K9Jf zWRMI5LB1Sf@%b#Km?IT)Ahv8&<854~1Y&XpGB!`fWP_v%pUso=C44c&60qbfxnz_Z zYP1lDQG<@!#Ynp&T-H0eu*8fa1a4O0c=BikMCow?x9D*bAc^JzbC+U9EpD^Sa~;oc zv{KZJeT8b}X50V_D6mumK0gEN#ky&7paSwB~|0ib* z(iw)UIQ~;Cqg!MJx~6YRpIp3Y9@I#7jG1f=>#c`X6w3SeL!}~>{bpWnT!;$sxs+k+ z=C*FSS{+%|4egk{Y9MM#X=c=sy|G&uOQhGtp?SFIsrsqP$4mcL>_><&WP-cQ&$mrd zUyVGtUOF*{U#@6Jjfpztq2s{=o$1ZC=GPf#EN3iv#|i_NE&4e+GgC742K~v_`tBKS z_4dH(mRGxXyLVdmG)@@X60p{x=F4MBqWl2E9A_`55DY74R8JRbU|C`Ky%#u!sCymBarccm^S?wNB* zEnaf8IzL;Hu5PW|oHyf?w0*LAYeI45e*YJ}+oU?xE&s?B8xI%j&UMvPtWLsLeY5TN zlg_!FA*&+Bd36H!zs5~k`?@Xpe33q24P)2l^D&FVVrNC4c`vQ#iwmG|_4P??T~R|y z8?rJsyDmO}X7kPIUFPxP(!#y~<{ua4r&~K_M0_ZZS^H(40jt9s;c|JUt}LXh;J{}S z{2gA7Ca;N(PX<3lW4nIIUkeWKQ8wyls<@$%JP+ zOhp@f0teP!8dKHi;d@k-!E3)gC_h?JaBkPP>$_*px%VvG+V5~rT-u*>>#=9Y_;PSx zPq8BB&R|C!JXl!fY&`UqOVBbd$>fh69=&E{JkfpJ?ax3sm^8g zRC|5w+-g1sUj4IvUjD)!w~muI&%OMi_p6fI2c0$Pn+ItLjbksmr{tJ?2eYOwMkfXa z1#EBXaujBS9k`dz{!I!MH+cLo2IqKdLW$ zcP-3ZP@J_VUfB%>o-b|rd_&mHxzA5zWamcgQ{At;Y{(@7(#sxnNP1UKZ{D${^kw^V a+F6Rjds2@=Z28;u8wr&~NDqkPv;F}V1)x#@ diff --git a/toxygen/smileys/default/D83DDE13.png b/toxygen/smileys/default/D83DDE13.png index 9812eea77bb6aa1efb70f9042b8965a658cf2cff..d9be4e9b7f9e0512df45b5147d11f6f42551b237 100644 GIT binary patch literal 1720 zcmZ`)c~sNK7QR5(Us|5{5GnLUBh&)H2tjCsJ`xfjWvRp<$Rm)2pin7WSXHz@T~H)c zBSFBLQ1BGWCIti%p^6Kjw6X~ml}%(-koE`>I`0Sn^^Z5_&b{}$_xonfnVECu>^?tF zyn&ek0N`0(3^o>x)~B8p=68}l+>Zs^A(}4@pf=ZVm9LFqg3ycY3y@|7a54)(jg?ML z0VI+EW_SQp8Nim=0FADFJ{%^(#IP|kW0>q2-F zFa8wD19Wx+3T~`-Pk}@1_v-|VECqBP)(MvZ$pGbVBv}64hUEg1Z&W0J3N{R5Vzkl? zkL8#ggI58SY#5eEAzJkeseIA%0yM8gb4v7F#!~d&dc9&Q>uYr^hNKHH1s3&D{#UMhKd38L1iMWjh|T^ zKU19qs1eY!LugopUE{wNIJdF;rij^o>rVq<+q}nxC(8rYwSTS2^nH{%6PIjObDylN zEIsMVmDsLIby}1W5O{ks;Ec9~7Y202EMKPXr0xd>mfBs; zK?(rP^fXS8n9WZn#Ky-&L>>_l#Hq0&LP~TjfOC#C!28B9S#LNY+nlC@dmHCM{3^fP zHer==`11?*S)o5!d+RXVGsD#@Yr4nJU5_L-buCQZH8E~EnD|m~q<{Fp(^7{w<4G;} zhzEha5>9?OH#=#Ubxm}`cY9u#e^jwtb~ND5KTdBSY`1k!vWtIbRp@lbV^$-=Fn_tB zW!_F+;^sz)B92L-twSfbc09$0HxA0HEkdGtLk;6_PA*J#>Xy+9DcQD~9NH}jNx>H4 zhb-8Bu1jq$FFb~Ok9gm~TXnyrEa!bpG#KO@2-sVC?^35%b(3K8kfeLzSW0rx;?;ld zm25dERBq=h-{agEb{Et*;&X|K5gi@ftfx^*-!?c99;NMGlQI*%&1jE#y9ejuppcZo zo8S4T=R^IQtzC>|7#hg)bN8{_1S;8R&3`JAV(qFN)pHCzZYruY zu!%W40xiEjZGBVAppiOj5^PQn4b#;phzSjoEc=mBt=YpJ@lz9z{-SGGeCF0+!P5Tl zym4n7m#!#CzszN4_Qhppl(1+GIJsfOQ0{{;(tG)fsS14ZOyhlS_jqdFZ-4qG>?v#< z^Hwj~NQ&u+i|*zA#XhHBwPvMjJdDgfkE7_%VUP1Yi$UX<_6m7Co=Es=WMXV=a)gfo zoFLTr^ZgD9hTi+rHE5EP5@~%-WwAwT~8tbz< z!w>AGQd3h-^U>howD)TDe}mJS158b20sYN&pZF9}QaiKezD^8?(6KVX`&Rzg8%!3D6J)6qO~}kOi`vZZ;6GmZ`Qn zB2E>bpjEMEN)ZJabf&2Ft%Z?_!!Q;*)rJaM(OU5_IOxzDAhtgoe{^Se@4aWw_nmXT zd+zMU)Z~Rh{0Kge#|zRe(ip%P;(Y?ffp^rCAp#8HoHm0?V=P<|!IC_snaLp`osGyP z4J2VM{^xE3dFG{A&NpWnE;hYWioUQBod+`Li7J7Rnc8q#56 zDLY3oG~{I@au_G4fI+4sCD`nG{cB>{F)9?03~>>51QiGoo6TF-u(yLVkpHIfMsJ6) z*iIq_(!n@c6W9++j1LU7dwU?SBgh6%VJWaFM1h7eIc+4(=`;!$d=Z!_Gp;f=C<}Cl;!t8kJNe(fT;vgiTN@wU`={U|OLR1uStIOr@4cfEg1?;-nHE zR!2KHf;N%9xD<#xiWR>Vi>q0Z;274(Fa^F2NVPH?uOiQfZw(|ykp}VhnTCsHQ%*HMyFFd0m z{IcJz?r)d0pQWCaMH#D!SKnu>>Acj{7lSV|EOW@#bTp8v0lfW5${Ahvip8OdpG{fq z_dg^yPG=*h_PuBI-&+~}rz|(q0P}S=RD=caL;pFkj7KP<@)$_TSMKp8ll=$`Pv|W!Iwk}xu zi~BZW=sjRh8hEvtxZlBVpM>;_cHi@xy7Dj)5;(ul?HN~Iu1X3EyR*Q!Hhk!l^|xIe zO_weQvUa4d3=3Vom7i4`!yN*Wgu4q9HJ{l9qOA<(}UrwwQ PdH)4EZL;R5DtqleZC-CE diff --git a/toxygen/smileys/default/D83DDE14.png b/toxygen/smileys/default/D83DDE14.png index e2ff195acc6f0dd485d4531dc98fc28ac0383100..c73602b6c2dd24989acd245aec07eeccc29579f9 100644 GIT binary patch literal 1661 zcmZ`)c~BE~6n-F=$R(F|jV*Wqc0~|{ibO$)l`Dix)JZUbCLSCvN3fDK0s@i*5dv7e z5RW2WSVbhZ9MuYPMXn;EhC`@`zOI?+O#4T7-oE#J?|t9y%)Z~uru%xYHPoN3 z4*-S?Pj@C&O+Mdx6R14j&e)$SS`l1Hwq;ZZm#c5kSnO*>ia{0}5xWLk@UM-N^zhPzR-?WGB>_!jzq&hXIePg923Nx)(kxz}HIbSK_n$ z&rfLIk8hQzQsMxWuj7jYSfqBf@4+``@$FfBF2Z^n?90Qxe5{|3rvTf6v5jm!?orIq z!Op}Yw$8&M4}8MHD^M@}Vy`Qd2qyRbCc-B{_0pMG1^ArA{&U#57w<2^-vMs|cBbL4 zfHj~90@Y9O_7vm>LO}=&l>$}+Rzla${;3Rzu!Yoh@TzhH-h%E+%oGOnTnWVUfGsnz zCk86TIozJEW|+9J=g9naONRH{cdS&znsTWssok6BO_ zjj(UmxfRZa}D_jnMlTVX;Zl;J;aB7AjhC>d2;~ub0&&%=B@Q#@L3?7HxDp`Fy45zI;6P zwN?DI`^VpRbvTv#?zp?-3)c^GZHAWaC~srm{M3KY#WL*Lx@O0N`efz3;uVT2`hj3_ zNo)f#U$VNvp4&Nhw71MXe&(=a_4gf$w@IURR=gZN)tcSKv&;G=d5OY7j?S*~Z;qWe zds_NRAYFE7hF@ryGF_!FI=EhwMOQxh(fvk@^twx2MOVXwp-*JT#I!?u8dbx|eI4EZ#JC z?K$GHWb4o~2O_G~VGbwcO2OPi0cXlaS6?W7;I(gqbz)7!m+!(W&IQP1-V%3bos6K6 zOy7<97tXe;`s=g@Er=@fRCcUnQ+|E&(G~~4+Y^S(NmWp@U|&DKV9xED%&!QG7|UWV zBhfS>^45^llIugK>4d5Op1GxVSl?mVb3O*{Jdqsblymo9N3{$~qrn2BjzOk8xTX3*q0I(|;^#y($1(o_TQR6EUh zCPT)02KsTv(Z&;v^bOuk*&(v^s?JNYtst5-6k`j zRZgEeT6b?A^$Dy=k6@C^A$iVQVt7;md&1t{j&QPD?7&(~I4^N@c678M2xo$*72Y-c zk70Wxmme29mJk zW}PP^c)X#hlqr!-)JJ1x+9n{p7=g>?;MhD~#7viiFlUl1kV2+Wb_MwG$O#ajEDA6| zq8I8NYBHUQ%40}lUX00{muW^V;LI67gbU*YY$QtnE}PZv#9RunPZ#6n-faj3`XFqk z0_=AxQ6CGaX@&$O0ukRVgkeA`6~JPNR0>ZA5Fv~}LhhFGVKF9=Vu%bFxIoStV@bsf zxOTu6w^D%VEbG7^C?_XJkRuY%Od15EDC*Tf5I%?CJ9F(U;o{q!69yG<(rIQW2TRd* zz^h24(Alg4Wx+Hs3TqIN29{_L z4GhC_Eh3W3MM9MXS4j}DW>Di*ToI1TVG$yhX@p{!Q&B0!YFwid!de-sK~dQtS7&#! zgxyRI+NC(V16e|tTm}~g?(DcwuL%vkb+=$nr?tfMq-RWWon=0B zHa;owqv1PtI|4FRd{chE`|A&um7DhMO)KhDht-8V-Ly4nc+-Z)BNZnn7q#lZBGeR? z@@}Gh-^P}PHJisTsdNgJGQZ%Ddvg;2pfq`mwKs4@OzWx90X_x(sSW6=4xcv#XE3I| zX1`zJ?Wsxlqov1^LaqGaW%Dj?yqt;ey&p2S{)~Mi5;$gi+1UrC62iCYJEtdQ%9Dop z&L5W>d=Fzo;QCbcQF&K#^_n+Y8}EcfRX@8KUh{KS@O1g?l&<$9-M`+cDsTSEU0X?b z#Aw*MtM2Z%kD7l%K7Z0Uv7&SYb$#^wfbAWf?M>vQ?|0N5G1calIES-TH!PyV#LM;fg@e$d^*j_c0dcXv-@-Dl?(D?dD42d%v}@KFeQi@CD?A7;V9iKTw?7hj9tGj|&le`VB`sGc?c4GX_)zIP_5 m<)N|0PWXCG+$`{Uk2kI6t+tgjez@oT)$24d_#V|KOaBEArEHP_ diff --git a/toxygen/smileys/default/D83DDE15.png b/toxygen/smileys/default/D83DDE15.png index c1dcf86702d50b2943eb7827d8436ebddc68da80..6d16ea37352ce0332bb9911797578bef067b504c 100644 GIT binary patch literal 1655 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 diff --git a/toxygen/smileys/default/D83DDE16.png b/toxygen/smileys/default/D83DDE16.png index e61bc89224d97774f94b4333460cedc35316745a..a0ae46a2c8e454aa04f3dc16a5a42325f2688995 100644 GIT binary patch literal 1717 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 diff --git a/toxygen/smileys/default/D83DDE18.png b/toxygen/smileys/default/D83DDE18.png index b4b985e6e78c2fb5089fcea895f83f7c913c21cc..04f349e59e607e2ed5661ffe97d24afd527fa97f 100644 GIT binary patch literal 1758 zcmZ`(YgAKL7T%;WAV>mq29aWk!$Ty15g|1U)qsFRA?4*5VYERLvN8ih2oZufprFV@ zUg05#RTPI5DWYP*@+k7C1SCKO6%a+MMx~aQO2y*rURZ0^n)xwjoxAt9zw>?Po^{V& zHzR!wzFt8@Hd=i2J|d!>BQ2OnWU+_82f&qFgN5*AL`~oC8{`jg&=%nE zQGoXZb@&Cq0VcqN0KlyTV5RhQW1u^L&Z_{w5N{;LF~Adm>40f~nM;wtS%BI9RptX` z{6|165i(*DvSPrZB|9M?a0%eaB|u;$v}^**1w02oSTB+_D*^KWOQF*e%BX;Zh0y;C zCKKVb(Go*!MKEUoE0!h!2&Tm!AC}@T&G@PrAC?fi;iG(C_#vaDtg^)|KwQt57@O2kXoWveGQ~}=ScjbHGR6S~si7}4uLTxs_{C2oH{DMFSZvg(tzCL%9PE3-60=QktGw5UZ+A8%Eyn`!_=lrju!l?WnVF$2KoW zU$)wpD0Jmqe{Wr_E>+)pQ|1nvSpd?ZgCTr*P`+q$}ZX& zraaADvGD6U8M#8mrl!t(d2zP%iOME|UmRYb32+G4k4{0#;yld{|U0HEcOqXvO*yZEsx1<1?j)j<$_j#i_5PUSz!tHSm^o z#AXz+A|}+T*-nmVDC^sdW3F0$*p=;)wdQ8OCNqWxqqtqmoOo|%2kP8CO{W7*jGGF5 zY_bFQ#;yz>1pTGQZ>lCQfBR`%Mbbiu9@*LhGKJteFdzX|* zUS$N6oO|*?C8<3vby)qkwR2-xbiP~YhvHb$KP;o{6*?n#19@9{uKLhoW_-D|F;2hF z$=t2UC}{G0@@n_O7k1RmR_$!@=fc0ze>h>;Jod-Asq=Bevg5z$&p#FCzZh+{G$-?I zJTL!U%^J0?^R-VLKdM%$B*q$_FU&I7<1Siumg=!}Te~5xlhGMUcUr+Wl`1q(Df0#b zi&1wKM_2Ab4%#u+EBJ}S66(^Q>I5jZR!mbCR$aH2=_$7=O*>zHGP`i(5J|gHI=@9o zHaNgIWfdcJ$+WDOtv|^)_mk56Whc8j=FfIds9z7sr^5I#dXmjl6HZSCUsE8w(P5xn z(|s^#c3<+j^3!aNB*%HTZHDK%5l&qYWh9Q5Ri5-trl5S;Z5(nrR_gQbr2Dj%qAC5! z^9|4U@SYsVFZ``cJ5fKL*)?%rb-2N#vW%wmiN9(w+G_KR)_Nl|?JE14hGFMbwGN~rwy!4FhtS@~1^m*HPT>q?m zL7rc~y0@pVTcf$P-p;|kSeR!|U`NMW8pm3@i@i08m}fAAH{O&+RMxdOmz?OKQ8&{jtjLE7qQ4M|SV5Nxs=z zx>2k-!xm4vA26ht{>9(%m-+7uOr1{E)>Kq?wcpSqzo(@6+_a@s(pjKs^ykN1Clh~r zu#tQ7<-%~e$Zfw&L?m!#Iy*ZuKXcsR60(8mw$ati#g)Nix-prw!M=k3F+@cRB@qe# z-;k6Ox1KPpT6{xrbc8%UTqXh!Ve~!`of{P{76pmIg$Xg;qQzqX*PG{c$y1Q>JEB@D A1poj5 literal 1659 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uta>e7-OLT0P0U>lObvmiIGP!mSsIyG zxH!2P8#x=9!t{FP6_+IDC8xsd%>>yC(QAxXua$FAYGO%#QAmD%4lD%(WaO9R7iZ)b zC^!e3DQJXe=B4D97i)r|2jW|o)S}F?)D*X({9FZa_*!LRvES0%(b&<+%*f5$+{M)h z=x;+OGiPT@V`E2iU?>;>{SDEZLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 ztidg{V?F}|(`ru_$B>F!Ig=wjLJCEW)t9GT@;W=UWrc=rkb9@Xs_7oZ3q_|KVei$D z*eF*sfx~N!oYq@K(@q7?l`Qf?r`spB#Ax+6=uSSimQ#Gvg(C0cR|@Cc-dW5({RRL1 zz0c=a-jCe(U(z)+G;A)DDi^a@kIdBXW!v*7+QnSqJhfv{neNeN+8W-RTq634A{+a- zUFM6t;{3qR{`UFG{0s5i*TnUnRcLS)t6Y3j#MGO!Xj*`fMTnA%vBrxFeOhf|=2v<1 zHz&5RiCL(flw~V#=uC1l`NVr`cGQl^vm8R>)OOUp4p3M1xK}s(!Glr`wKxmcC#x*jSktwt8s+~+x3{c_jIcR6*b?R!; zTno#+U6-SR{hdp>6VvJ+^&c0IQJE8|(-q}pZr!P16B*UR{F_})^x8rRk#o;fI{D`P zto`0-*mSS_>Xxf^YRbMfW>r3G6nnk~C7oldzje^^M1cOTUyJ?kUj1BjROI$`exYEY zlNuEawvibz!n1m$(yXT6>b)ngDZaq6^#0jXPJ&huEOVzTh3&kgGk5U=gOf#H4E2>O zbkEN&vU4|O5s=E&vc8tbq8y{3L) zN^;67=6f-p7j0x(?znB$9L-9r|DKN=OJ5o%P3GU7TWx&g?X5QdVvUWn%{P@z(a>$2 zD(Us}rQo?OQY&w!XVl$XQJ*i`!pPAhh1cO`VU*I z>(pB{?|ypst4DL58kxE7{d#^D+legclT%9E?^rMTy5&HF&Fj=VMrx@~M4zu`U}lK0 W`kJxn!pj&?b>Qjh=d#Wzp$Pzod1#IR diff --git a/toxygen/smileys/default/D83DDE19.png b/toxygen/smileys/default/D83DDE19.png index 6981b2bc4ab2c106cb74a8de4b1c0ebb73d8f2e3..be3d55c5fa745f34cab2bc532f7a739839441ba6 100644 GIT binary patch literal 1631 zcmZ`(do6Qk0uGF1W^0>7mI`^2r?)~GQwa?z)-uwH0XPt9?>!fdUbI~B`5dlDh zL3j4Vku*Ei=3u|tWPukBDtoC+DnM1%ylIvy&dI@aPbR>@p8*n*0H$y$K@Jd20T>Ph zu*(Nn9C4<>!vR1kVcS-38p0zPPzs=9fYJbEC@=<@fQ0`_vjL?mT9|-a*ki-AlYqnu zEhb=`4=7IoFg~HMEI{WJ6%n8Ug<&S9l`1^OSdP;|K<5>P0j@MLQC|W2Btah~s4su^ z3j^8cvjmMw&=~ejXfOvAD^vqV(MTowT#5SQ(IaCtoQH<<(WBMqBB0lP=*_{GWo`#- zq4SF8i;GNI#WvLKjcNdOVc?tXtDfQnIXgAP0rOFpJ8A~>+!#I6zYw|t4VPXB)k6>9 zV%Q3F8_*p<{XEov0JQ*W1SExqSolx^=qjMcfL?f|F#tUT^b{&WL0aI1ssObC`V%j- z`xq0@6F@Ja<`BHQvlrC@YRB8zfu3pOvnP6=w!)WW!Si&Ry-z&W9+yqNBIM5Gt2au- zxyIAeS+Z~6CS)(OMC+!-ghq*?Wi0BP`upX&w&{ifEKv$_b#aC?)#Y@Y%;hndw7Ku* zY7$pz+<((90#J@S=?ljww(;0b!&EtcF>VS{!GELK)+^fm9iOUKNE=e*YTpVb+ zO6BA|QeBTrRVg1e*IXYF}ee@Wfdx6VB;YbsRla^*yObIY!~$$Vjr#wp(Rwx@-s z2mB{2!*=AzRo7<@1qh?B1X-Rj+E>CV5{OH+PtDW0m!Vf^SnqgCImO;k=P;vxqiVHRndwHlRc9-YALlAx(Ab-i#EZO8cECaKCj%I|bf`n-B&eX=GdvByzq<)OJQzfdB0HH$ex zB(3Pk=mW{hvF2KY$OZ0Z;re^b%r@!AUxfvcg!Ja30a5$4z-OZN(SbBabwaDOOrGCa zH#u3?nItcBW)LowOiqT7Rf=;bC%w6Zf*U-FRH~BAt)3s{-xVmznP+f)Xz9X*6XVpP zA`*RQ%~}e@c+sLfpmO~gjL2;5^>Z?-w|EF>G#Yz5O^{t~F>j7e#i@qP(TAB~yKbgj z&^6YXV@TTfK|cI`nS4oNCm1F=HUzl;`Vnt^6_4h}3t{otcEM3>9KeENVPQhCF)_FF zHmBIFx3aUcG^SAOC=|Bl{rdk1!oowi9Krt!7J40-ef*csuHY5U;l;9|*gy>l4`P!U bVXVDuPc|z=5ZTJcuRZ{S=H`6GDKPOL4V3K? literal 1561 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYV%q>R42!82hIeh9jm-^?&rG)CWmD# zzVE*Of6o8^yT8x*9NG6cAZc%yuvu<}h3ad)=BG2%&+qubaQEC7x4iz=t%?`2bH$wG zuWq?q6Sg9KL790*#Z%!L$>txYZ8YV&ACwAuiLp+3liIU(&Vv)oifsoDm72Q8ndGOk zsaZW>4>X!1_ObM~O2tn9y_51C&+;+;<8ri#FG#<6wC4815562lF&4E|4NvbVy70MK|OAxBOBzwc5)k1n4#Gu_LEEHl3U|8ZhI3? zL^JZJJZ0J?!nF5h7>vqxH|v0!%}FN7}dMkwhjLTk7y z=^a8p5fPdVK#0smNRL(in05%E4O8BpbXUOF=S6s$0s{>&`2yaGVCfzFQwrA+q$9Y2 zAR9p@`Y*kSAVY?64<5obE_hlof-D4hvnAXg54R9ZX2D_;EH=U-4<<4e zc;q|h328@=u*ehU%3-#6f#(2?8r5ev4c9YawhQL&!E^>pCl5D7*PK;_2Np2K2XQ9! zMuUV6BNt&b0!E|YO$?0j;DIGHm_jWH$}F=z(K$<$?~9<<8hS~xpEpaeW z3Zf&>dwBJR;ra{Wo4<^pd=E4_g1`~F4?(~4qXdn)n!V8I1f{#606`7{-X3^LhTk1P zd7n$@$Au#^T(2%g!4&>s5s!ypC{1S2t7R0Q|&%~*hh z0K*7|(1S!IZI6Lg1ldrBU@%Ul79~=6@24vY84a~ zn19WctgbFe26FQ(z7{IA3S<%Uyy3Ow0gJR|_mg>}lj<1khXGc-JcfwALR4D3=Q3nBaLLf_LxZ!ooQVY+AJhIERi z6qY0x*Ivzh_Mnn>bcw0$8EqU6O3D9493;y1y+gO9!_Gx0XEiwk6rNL>X$_ ziX4q}zff84b$!TLYh-y#Q-7zY{3p7kv5dMDESDn@@1<2gQ|cm#^*#s5age>yhnYX7ogwLW_4LiS}zS#C{VeE%?ats-`~458@U&YAg5>7 z#Rla**qFko9AH|Tj<3*er|9KH%D=bipc`!v-7rw^YCf<0cK%jhn5py2!q1HrZl+{T z>Yr+HJtZ0jdiR6YH2nG6U*dcYMm&1{^W(&i%^q~#Vw#fr2W_ii+t&wP8lN#fm@uZ$ z%c%|5{nL3LH^X7fX6R97AB$5taXne88)SLT%2QL-{*YTta-w3^Z*?pMo9(o(CMqe% zkB(&B4$c;D{e1nXAe_@kLc} zMn{=&+}(34U0X3{PFq{J+&o`i+HLr-?HNa?>{iL#_eE>9jN!O)dNr!OVr?P1Se)o@ zJhOJ&p=_aL^61*i-f`a5*~!!zT7_rY3X`%b-`1a*PVi{a!Y5Ug;^IWt7Y7F*Id!U4 zRMZZty3)q+nJK#i*0%fYq?8vDquZ8yw=5nh>12O=ifjbth)AzT)!OLl<>$;Mw{!}f>ckM8X5ckJ^#@A19w?AGPs zOD4MdxH1@wiK?19;Ip_)EeC=Cfq(_^_yPg65P&%l%;wOyfC=#=e1QZO0>c+bTQlKF z5;dY6wneX`U?o31AelnRl8^cNPc!zCyrhESM` zLVOfPAqWyFVXjET<;eMnoDcKR5si0o1ssI05G!~>l*5N;6)~SLR6?kX%i{^SLP#{i zRT<0_X3!ENc6GGfVJ`Q*T#3R&U=(T6kYw6O1uRb?Dbk!m8UaP50GJ=6GvK7f9N;{j zp=c4pq+3hiN)xFE2J&A}CD94ao%xg_z5g3F$0w=(vZt`2Wcn zn|6lnERO#a%g7d8fzIjM(x(@1n};ya9b=+fgTc8$cc)9c3Xy57t(yF}P|cbtRU0h3 zgO0T1>}6K0=nyozylD8O2nlRfc-o{EgT;7jgMS zJ~-{+SYffY`>V~}q%C*d{#<2ZS^kzz)u{E?zO4x?MeGNE$sRu`^vfK_{jRDj*+li+ z+^9R6$&Vw)RL=)%x^ER9yk)Cmwf>w?)>gUk&aonN`wrfp_gi*IX2&1%7xGGWMm5z8 zJe?=Y8DmR0c&dIv^tC_Y7RFsHI@fs&l)dcbspJskdW!&4j%Dw7oH>wZuhB7@Fp$tZPcVk?ft6XjcU* zT%c0&d`Fi5lupHqOK5=qvN;*GH>VW}Cod3Q*^|9euIM}23_mLW=qD|_>t1r&g8oe~ z{B&EvfP2X5glFY-c-Ls#3VVmg^~B6YDx_64Hfumv9p2@;KC?$u@IrGwpsKWu%71zN zv*V}t*&fx3aim$vVax)@sU7K=TT(C2Jeh{RD$x-8VtUqnyehVg6?p$@L6UIKu8>-H zucC+TI?f{>IsU}e{G%5)%&Qb^%BlDUzIvzX3%{gkhW*wHIpY?hA+bL4qk%HEn^bcH zvx$~udM&L{Z#4xcR6gei+x;qc$2>j!`nw`?&&=(c(?xC`xeiqY@1}#tIehDi>{qXX zw!GHOXZIWpaIAdTRU9;~=~%>C(PTFzvnk-QV)5PS*I?o|`%=s2-YZ?R>i@32S1*j4 z-3U+IcCPaY`_=RVqhA+um3h$_UVf>4`9-B{zuU~H!Ujq21NZKuwX3G~y7hgUJ&JM2 W)qB~6{?#4Me~1bVN4}S@+Vl^D!IMe= diff --git a/toxygen/smileys/default/D83DDE1B.png b/toxygen/smileys/default/D83DDE1B.png index 5466a03f3a8063cb0300311ae2a932455a175151..0a52738cbe70e216d58108d967a29eb86df1fe64 100644 GIT binary patch literal 1593 zcmZ`(eKgcr7=DK_B!*~7+02mY7*j)1Ngtz`%&bP5hUi1{VF*pEMpCQHRJ#&kBt%*t zW&1!nmOiEr9jrU2QD9u1H9s-mJDE(iV1L&ZE;|gBFhy%AB z1(atn;s(qM0OcD9<|;#E0V*|AvH=wu0(auJlLo{b+p$b_K#^O=5zDJQ)K`e!s?o3- z^%acU`*YAcHTp}9-ebIqG`Xk@PzkQ@L%J&Tt_uCR7u}|z*ZJsm0lGaKRRZb>MbCGk zrvg+D=t&TIx(+>Ajcx#XKtuPOlmVdN0{R0`3!v+OZo;w6ZC zi7J|rEE!2<_y>E>FR8~T!I1L<#}5s_?Qr+!F4B>)f(cjDc}Hk^eOCAA=!fpktZa9E z9_fnOutdv#{;l8LH@&w9I|$+ce-@;fe9ytcRL z5+yz%E;4$nlp;@#mr{1h;sF+B<^?lV&lqOv59z*4BjU(*UzayW&p3Y2d+m(udh>qf zw6nKWga`L2g?o^my-Yjwl&kZ+y1O<;ydbuEV@6eBkGRNUK-Rad;g-dyc&cikL(X}i zVVl(V+I8s8N+hSOJslsE^cy0zM0aC4(~W%p8FcFtSf_O&O? z|5#P@#hXo+ib=~lBrHNv%bA$U&H}!}P~y4_XIe*LiB%k#*5@fUO~`X?q1m+T9)`5m z<~1jTO^Z8>{05i#E3AXuCM#v!Q+5pEp~^lt#rYwMciGc}OS+uRx)l8D&Bdub{)J{v zlpa#YP}*sh(XaON91ll5BDXjH&OUM6td#a{^Wv;P`3%z{2i-ubK;9e%nRL&_>N7)x;Mx;qobs4X2Wtmox9jO#jV+5Y@e|dD6~KT=h5gS2l-_i^EsyrSE|n1G_0^) zNPE^3-f&-QJu>3Mfl}jI`f3%y$|=-8;OL53HJ|Lw2W?F!o^$J{;dFUkdR+O06t;uQ z>olon3VI!k{QE_7zfKx0k}mr+Eu}#dIQHq{o#dr+Z7yGnZjbz0^;vao%siqzXp=sB zy9>TD$fnq3D_i(o$Jfa;%92T zUac$^eCKLMnn?Qk=k(;ps_dETDUM2|O4+^XB__Eut|PJ`6_d_KH{kG05T~q9c?4-*9P; w;|6SS7~f%ST%eywVzP=58hYMJ^%m! literal 1578 zcmbVMc~BE~6pk%AAfVEr)&q5Cwc`QF9=XVBbV4?PPy;bWiMAd~vPo9RZrEK+po}Pr zcPmyA(V~OZI<-~AR{zj;5Q?->l-l5dwG*8&IMx#wt<eC~Xj!+dJ6j~5f>;cW;6 z`b_vdHP|1N)o22=3`+uXiBxQZ5d=^uB#2C|P#`G)3L_{43%5dy$S}DALzTe51q$9+ zy8|=h`T<|UN)5Vr-h)Aq*Xxydr4oj9LWoMG3Ui>SSg;UtMKn+N#56ZC!hn;Ujio$1 z#n3>Qk;r8Vc{M0R+AqQFF&bYH)7(I!gp@%(!UG`^7;?MAy85g+-c0@*#%rxPOOc0! z%p}JYvNqv791|m8A$M)Cdbp{s7x2(cpVni>t(1^CR6HQB_gn-Au>d(O4T45R1a(AiU`&~ zb38%Y$cSG`@H>E&z7dOQS(4xx*1|CPkqR)m7@px=j0e!pRRB|RDB8|=xvAmf=~s)B zEVZ1p>siJP^yL?$UgKY-)JQdH3RJID;yP5&jif2GT9s6)5u_lnpt~Py|35i{gkYd> zar~!PB3nWQhNrJeUs$|q9+DP1h80>P6Ur@%wawe6&rY0^xVE8VTvy|V zaMM~GH%4!&udjz&T&sppOHTeIw|3Bk^13K^L9~^SC1{(Q&Yo>Un5TYF!A+*aC_+A^wpenS9XuV_lz8hj8sRs#MM+W{-j zKRX_}u%v9o!HkuI6zrbP`I;@--eE`gZkuqUC%Sp>?Vlf+*5u&Zzif+%PPt01Ih@pa zdeNXS_O&QaZC;$eh)vQ@2O5)isHSxvsK~j&u3HxSVAV0~{S~pmxxtRkzdxd)o&?LL z4U3U3ReD<8`?hy|`ol%|@%_dVgHI+Nfw0hT8&Z1i6`X0;ADHy@_4rM}r*-o##_gWm zw4dS@q{oIbJJE_4qcXVjuc^-=Np=8Ecf{BNmi0y~p=cUV*s^00pI0R6K#^@T}KBeSfa`|j1R7hUGqV6cTeoGcNd@Bbtrw(9n%Ph`ug3~ zenU;|%G37R`h;uD6zg|^srDT=E5;X?;|z1^t^}^M{n{?d37jb!`FqU4BQ;&+$B&QX zI-2hTjYBr>|07zytEIR`a^2wHw(zR=m*9)@dFIK^DHonBTc6mrYam6I$x+fC=6cZ+*#a(4%mm*T!Uap}if_m&(jzVcqBX;U0FeE%cWq49f{ u%H}ysm0)&5W|#AXQ?m2vwY$$Jo)+!-<|cU9IhGFpy$!l7{7Bl;vVQ=Cb6L0m diff --git a/toxygen/smileys/default/D83DDE1C.png b/toxygen/smileys/default/D83DDE1C.png index 6796924ae3ea42f34540b1e7e0b55c8510f23e18..628cea55565a94efb4f672fcf12c9f31a007bac0 100644 GIT binary patch literal 1693 zcmZ`)dpOitAO8{=mu6|xXlxl|Z-aNFi>&oL)1sO6k{vVB=FX&+L3SdP9TS#r4CW?@ z5?if%o64}NN!z--E7y#bA=2!m2JJ54>~B2J`@Da>=lP!R_w)Il^EtQYJm=*4`R-k3 zL^J|`b#$5s1B<5Gid%#Ej~(9g$AVro)rSgDpJ%)ju8(n2B#q$%koqA2FB4!9OYx=w zj!^(qVF0cY0762^-LKpLbYy=2W_lv5WC6+sbQVy~D!}|1K<5CR{x1~(%3Z}+25UME zb3(GVvj~1K-iHahFGe5%+`kx$WjdrmNCGorI}B^BVAb*fm9Nr#K+;vi#*_fMy6Q13 zTm`&M=ujL=|3FJye9)*Ay^(@-YE=Ekkc)NQ3znq%1_20hMC;Q)s3dy{^{u zi~H-6yRLAC$`7Dl$w(zeD#`4VTt!={W?HelyrgI=LsftjfoLN2w;S|wnaH3Rwu=9)IkFNNHeaNg9}pjtp3fF1$r z1_2Xd93j>T3IhPu1L^`zN2JgXlph<5{b9B<0`uL z5hoR~;~Bq)9Kn|8%w_m$?*hQ5=Li2DSX;C#F>ICzZ% zHp^C9$@vp2O8b(zfn`jj6XCtr19J2?(=cMdrTcgqOc~&f>~vW{T{-}|X{pRWE+agd zl#m#I=twM^#7#+Hla6x|09?)qS;0>f6x@jy8$Rco;GR#o2;L4z-T(YM*e^Zk5$ET2 zXMsshmVVUY!m`21Goig+iDG5f8HW#(8dbkO9J+p|`j&U}=%ZIjmYw;vmDblBS zg)qYuv38u+-S_~F&tKQG&GDWIIR~=ULClhiK<@|s3G0I%SKGHVd{$ACI+by3 z#q#LBMu&B~Brf9K2YK@rU$s>>Tp#{qMynBVZKw1` zp0RCYyx2czRo0d34S69yCJbF-eda-@2#m^8O(bVt>Z-r}r~x&~Tn|IiD^ z(rh~4i@0TbZ3A=9x8WIq_msN}jGudl^<)*XfRiS3&dorZ3D# zbg~X!JZ9{Woj*88(#gcj2THcC*%DHhC$j(EcU1ka4ZjK2R48d=X~+S~MQm}og?slUN$@m%Y0LREU&$fR2-!7%vQ_UOhGa%+XvCEcf=FbP%_ z#Qw+J9oUn*OYQH zGE8rF!NI=zM^0>PwOXxzo2SdipZD!1jg70O9OZJ7{kZbwq@muZduw{@%4L%XoeXN2 z=?CkHL>0N4WVUgO*(Njd%@*%l608X1$0T9Cq^P8zSR^VHNTpJ{E>hve!V97cVv#7+ zFOV5%-$B~XFw$pFnokO6O^jImw6ZS+RQjFDTQ#jZIF)5gOp(w+!YVF4__cyp)^g;as+BWFi|2u#K{9n*(K+gVT1V6UEhehwFA1GKJzwp~UIc zYF~r@2{`dlM-HX@e}UBj!fQ;hNjrmo{2^{~cp@99QSlLM5}gws&1SH}qf(AOW@CQ_ N0NvBqqh?PS{~t|^A_4#a literal 1663 zcmbVNX;9Q=7+wJ>XDigPidPICICYO?@5qXK3YJ9#ifd;?!fs$AY_cW+R&=yORTL?9 zQR(266%~ZyQE4qzEGnp_pn^~x6{&&>!bq!tR=VWFBRcAemSJlxs$%z+`4U8XQv0~1XqGU+IVag-)90RzG;Xc88Mp{8|f z>#<-C$2Ad;F)#*wIASC%Jk+VfvstVxn!^bWu~|`LGR6Q2SQ1Vsz{jW0f&gw(fGfm$ zf!?ad%y?)9g+*sX#27P@jdBwhvIq#aAuNFfV^F|mNg-&&rT~X|5mt5{^Fd%(g-KR` zBTgCgk${?{FhI-`a*YBA0wfY1Boa#`&;kG!Krmmx-V!b(Lc|gTmI9*}$XcUJiAa=2 zH)@OBDL^yBSP?!yJw2V5F65C^5+9Pw=&L1Hz8U`4ryVb77}Y= zEd)U_9W0c|gaVaVqY}d+?HI?Kuxh1BEfonxQmsG;u`EipSSM4dWlC0t1afE$8%EF! zN*J**yEtoi6f1ly7Ex0e%8*nHNv4c-K%|*uNZL$V0d=$l2wH&?CNiC#?<~(qv>J@U z(=d~cA}zphei8f)_T^HgP^pr@I;m8{dJD-Ql|-$U3x!H4n*{+I_XyVXe{#lWo#8v1 z<3G(ZcEom|Q+(a}?B@0GU<5lc6gwJe2P(=q9G7pxG|CuT>xNw`Lw7HqShwl!<>H>4 zOFy4{;C~S7F1xDcH*anJjJrs)tolUj0khxo#AmViL=VqT@>I{FsvoF5JjYMcR=cev za9+vh2Rq>%h_P`)VH5O$$9vPxJZ@Y6YG{4qsiDJV`=g|%!9}^nAN+J_ZP4t6tBd{w)=W3wK1N=>;c>Ld)pm-%`Qc{sUJ7$8cAaXT z?O4@&GVt7j{d2qbe%*ht^Aysa>gh9(9n$O9UjWotN&>vKy+69!E4A(Z^sOb6dw;Kc z}y|bBinM5infHWFIllXxf>|-0S(Q;_vfFGQGJ641Y3}L$=Nv#cWx)f$4>0> z%k19c8J<6`(lCR|^T}@NZvo27>$Kb5GF1D^r&U#%(q4ADmCuCXqQN}3Mf%B^Pow7r zmh1^zGsQD*$lh}QW=nhRiM0hurRO_#)MffNQ{tR>@vQo*rfn$L>1O}-LOc-eAT#2r4r2{)|aeft<7170&ym#GSc?mc? Wu3v>HPSWN4C4^}sG{;o&ng0OJ1A&A9 diff --git a/toxygen/smileys/default/D83DDE1D.png b/toxygen/smileys/default/D83DDE1D.png index aa3d784f4f62deb92986430608de3ed06a51b421..a646c978cda27e599669c6304ae6e03f25f96c38 100644 GIT binary patch literal 1693 zcmZ`)eKgc*8@>~3jG0N65f(pRElo736w%^E?D}d9Yvr3EALDChg*UCN94X7tFrs`E zwceHD%~r;$-4=<;lF#x{Ev-mc-n6*)$2t4Q{_#HNey;nv?&rSFbI$WT=RBv}Tn^Hy zYE%HAJ382M$gHq7DH};NFxuo!Ch0J1XKR4#Ir7VaGNdMg9XQSa$y)(Z(*fR-rPNmd zU$Fru0|3m$04lL1w+~qWY^Zm0;@TlOP6B2EW~>KEP6K8GX8kRNfTz|uSs|B5B!g@# z02Be90VEqpE(R=ICnV2Xj~u}A^-3;a(Rz@bWLxDrlAN@YGF25rqt=j=P0l!6gwqn7 zlHjm-%|0r`8413X;4FzvIF^qUfEUR6H~6LoXKHZdYiwuXWFbz9vE2YK0}gqe57+Fi zcE#sO_#*kqcP=={#Rk9uHyja_g{zFjUJM_&)fgEliXF-UVy~Y=iP(SnNu} z9uw@@yZ9)zEC{d`@B!dhEItB!4A=_z5QJQaG=`|1knah29k3H-9|VisV9Y?ifCF% z*>PD0RnKh2NjU^S>Uc8OOTY;{O2o#;goa1)2tiUTk4WUl0_@8cdHDW1#HJjZP_j&; zQC_~-CtT<)IL=TO>94+3 z)9_i?aO-q}dPi<;mG*bW9*s)Ve%yeeD1H8qW(?0+=l@k_C^PJ0Gr!1~@_G5j*)%5O zo8FBbOyk+;qeH$n2T!CP>9N13J;arC?8^H=y)8P<#{c7+JylK2=alHgtV?soKWWS= zeIb1%?U?pq+sYQry8V|*lE+VcwW=QHT5m+(E8bVw{d3MATMpf+s;eF}+*8H1PwrH9 z71DpFJ<^(Cu!eU9$yAr59pULnQlnlwT-L5qluPydKDNKqWB)#tKDCqto%E{` z97qyW=$iGPiJiz>x+Yhy-2L_2cu{|I{<&*{lYa`9M#H+V44UttfAn;ZPiEhdA%py@ z)dK_b3Ko&WRnC#jeTy_s%XPp_NGQ`7raPEAk7uwI|y%YJa&b~R#P(~7-6|E7Dh zWvE%s1X2Fl@k`TU_gVdwcS>z8<+tAvPp-IB{XBozrWtbHX2lEU6BEhDx#DPR&E3)+ z8~{>GPt$}1h9{HA-<)Z7>*fNTmVIX7R#Kw1RLQZeRiWcdWncK+Y{vMjNQ4M{8N~1YteY(J();ND& z!3zH5*9Zc`SUZO&#N*x$qWhk+jx5+0iL{|&C8 yQFWw2b!`Xtm{7sdz<3^5hr|T&2uFTk7>~mX3`vT+&m;c~07p9)+p9JKX@3D;oe8o4 literal 1659 zcmbVNX;2eq7!Fz~o?ME6Rn`q!QAo0zi)09pWD}BN1Vc~;DeWe^K!EI~o0SA)iaH=D zMICf13Z+6VT1!3aRZ)h~Q4w`SN>XLrA2pXYgx?|o-C zCN7JcJ|%bxi^ZC*)gXFi^l?94p3FPM^$KN%AX=3|Cy^Pn9i?!V93zc5ptYcxxE@Eb z{Eu4kC>CpChS`upr|6bSO{9f`x-lGw#mcZ*tf&}=6*cAHG+@Lt&4dhma=IA=%$N*Z zDb(?FRwbTg))Y{9Qo%BVsUXKB!N8bkAj%0uDYKvLPz$}`!O1WIS-OjP|IV6?Ig(MP*TLXq87(|3EpP*4k1Yu*3DImDbM47F$ znIr(WB5EX8(=w3pbR-0eRi}F`OxQ*f#iWeuK&@Pe!{b^k?zo20Hd>GW>&Ccfn<3wd zbM?55Tuqsn^T=S2k(u1R*^nE^cq2`q%*>&nxd>@mZNUjzi^xFc3kNe}QWYeDR4`u! z301HPf}lt>%#V!Z^AtivA%q30F^%!Kh)^t4sFVV+iYJ5^6}ea~QSe|`tV9rn2$GL+ zwS3z(~6-%zZo~ z(IPly&ciV^MOuL2{7TK^*q4aqe7QmdtHokO1v7C&3XxJN;q&EUCJQ_!?h!8be{#lU zoZ-5Q<3GhRw#8JSd-}TcnZ@ho!3m~gD5f>moN#Stu_k=2MdSv@owRMMG=}9fH#9q| z7VLt9&7ue8?)W6KJ*0+Z+0m3yMt z`Vw&iOzug_5()FeGEY3O(Y8-4w>`hp_v&WXtD!A*+l^gLLFHs{&eSzMXD)nRT{k-< zAvAlD;r*k>7IMmTpcO&vv z`djV&eoMdF5d6#H8KC20Mrp}lQ|#SI--QNQpDpt~uL%Uu4y zp9$IhYx>l~ODtGTSaGX*(BK{N5HbjpLjBh#7jKjJ=2yMEwz%ut&H9f!a%;KbcwI&J z!`n_?`n!7q0{f8E^0HL*+;pwu^dAFFReyIaAK*N=QXBoEFyIVF%+`SePv#vJUE=QW z89X1Fk{d)DuO@+DZ^4eTIj-IFX zbpz$0jYnP_yO-vzou)_k!TPfL&q8;eH|`7f*wNPXplSYJw{#iL%?O1sN)0Y!Op;zY<7#^PQ|_y$%=b{-p%2&y^0P^ zfJ$zk?U~eNI#+x(&DX!R0BMYxzoJ&4D(=Wm4R{&UuxUnNHxc%1+T}}UtGk21rM*}} zHW6>x72@nDYAL!|t53ZdwZCGOvHhL;9Pjr2V~>8SI9WQ;xGRq83#O7U*6^Mtto2}h XnjW-;#(Gb>e+OFCGUR~b!}b3F@s@J( diff --git a/toxygen/smileys/default/D83DDE1E.png b/toxygen/smileys/default/D83DDE1E.png index f2845ef688f64bbdcd53147ac5897f5f49611d1c..908d40629f133a6173134c986dca5bad2c2f2330 100644 GIT binary patch literal 1614 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 literal 1625 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 CTI%%x literal 1625 zcmbVMeNfYO7%pxK!ZAH3a3F^~+jwv@lai`ODDok;rZs$#z;HhJG%&+NWDOk5ZJpWiO$?qf2^SmFw z_f1tsdP-<;R4|Xn3)Q7!25y}0dxEBL@7becI5$MH+FUk^Dq`I@P4HA^s*nJ5cD$G{ z5V*Ny)%V0g9`CIp(wNKU>eCb^$}Yrx7@^1RBV}dhOm&SYiJ^CO}f#v#%e;%;G!g8p+~_9*a;Q~Ja(IdQFxT#xUPbm`?f_O zFb-j@N^rudTzv+hp=bh-2_*uP7=i!<5kgWKfrSk|c!iQI0t&@B;Cbg>9RQPiga!vYQ=U`iY;?h!Z`zFz?&7!yr8 zS(0)9K1IBcaEvyHl@!CG22cCW=d$$b&mYkWehL+kJ73qZ!sf{OiVR(TuUg zNr(&tL%C=ZcOFH2Kbgzjn+^GZoHvT4G|3$bZo?>(%T7309i{}iFG4eER%js<(!vrg zB-6rL2!axmVM$`5M68x!Y8fom`ZZq5)oA5Xm0T*zf}(0UDn&3UCPfhx#{68J zgJE%piSXMceRe0fk~eY{8k)daiZ)V|&0hf-7K)`93*`hfSqKo9Lpscqn~C=w&qTBs zL6fTqb23fYf${t*$k*6Mlm7AjbG;1n0dTORXR*%^jx=XQ>7X+XMS>W!RV#> zRi|~U;x?q!S=zy{4M7p#?EKbpxdo}v*Bp$KSIlXTYtOSB3rUF=2j^7}tE=k5TQas} z70IH)8tQ)Acp(uPRXpxK6?q_JsGAv4j+I_qFjVT5>VIlm{q?(n0Zq~M?$2D?fwZl2 zjsSu?yRQr&6?yMja+XaR1}Y`S=x_^vaK}d{YS)%E2JF1QbjH5rLAxvBcE$D;YzU|( zn-4E3OUf8%*%4n`Thmq1I`nHGzs;F??)E$Ldz!bYLr+ZkeFmC4y(||d?oiJXC`oij z@a}^VX_e{C=Vv`}j#huLcbk34Kwf=F{JpDT-r3d}2}f^CZ{bb8bhLbNQF379?3gn- zoe}x#9z>;C%$xcK-j;>B>a(-UioLq}vQtx@4=%yrWb%Tr+WY7-a~J;cY2@d#towm8 z*ch`Xv;uYAixu{QSiT9r^M|$k!Js$xQOt|3o&)bkqaSWPdwe9bTVx|jFHK7kHPv+{ zJ&6pc3~3CHvNqp1Xpe8tbL5Lpmy;VGU%lR2HP$?obVv|+bMVXSVR9@Q-+oP%{ny3i z!z~@>1giYDbtR1v4SkLLyUiU}?k)9p@%cY{wTt0_?wpTT7}j>a80%iS>zQPQ_x}D# z(?sz*bmrEPoBE!8mB&i&cNjApwiGP0<^7r18&la8yX@9swm;@1expCd^1%L8g7nj* zlJ?3vI|TU~ED-EeJt;l=Q+ zwk^ebo~Om!J+g;!%ro_8AFXei`Ox*lTxpH=VO@Vg1GD+obN$r4?Ctixr-IGH>%S1s l@5STM#Qu9t=QoYb?cs%{540Yt?Q{En_Bw4kc353d{tuj0V`BgS diff --git a/toxygen/smileys/default/D83DDE21.png b/toxygen/smileys/default/D83DDE21.png index 4d891fab0c96646639122b9d5fcb3bb18e51a37a..4e176b44013a3392be61a3e0f29a7b57b6b5e917 100644 GIT binary patch literal 1726 zcmZ`)Yg7|P7QT5vLI^Kyg;Lb48l*rVyd_HMJp7X9r~xgcFvu9@Aut%&&-**XQnbD zJlKq8O9KEip@1jB)^c@HNmx6uETXU>rv``u06J?-m!(D+Gh_mZ2%y*=ptJ&D2~(vr z00nG-XGs9u1_0}vQ$3Ns0E9acVbMVdp-Ud5%O0ffy+{U6(p4|gu3m)U6;IN4UaKV5 z7~;@BIb^J{j~OgD6ANYrIMBxdoO#omFvKMcWAPyj`Vjhk2sb%IEZ4k=W7}cO4}SRF zAK(%OxWpUY#C{**{hjcD57@yFM+bd~cXkjacrd{uUN2lQ1by4i}(l%OmZ^!GZ{eGDm`pX>lsB|%Lpq!GX613D}m z+2c{Rfqq@=jI@#HsN{NtbD13-RR~Zhpd2RB@AW5ZP(!$Bwg#L~j3bE&{I4i^!ULxXvs zF&qBZIAB2hRj-kf9ormO0iz}28?fLZ0R|XfRZ?0Qw z*j?~rvT|&)C(FIzQjy`Iy~opso?FJgdZur_p`|ImC>WV;lgZxSThNx#Fcm@mqCqk6 zub>x?q<5d2qOJWY&vsc*V`ZdtY->F&k%cJE zj>o%IwF^><{jU177Y=IH_h}bwIs`WJog~I4>)?`dt89TOwFzyfRg_AaES1|&K1-sm z6Mx4dUq5$fLX)xeO`MhyM+sT6$|vrdcenEM?>0Mo*D|V-?C+YwvAfl}{**RT=l8Ky z;I2pLxi9xlt(`LEa(@4(6BW0sW_J``33>il_}>ev!@sE(pQMhp?0IZqW;f{-uNhDK zk3*e$?D_MOwZ55Oo{Py`SA1<~(_)P5beoOYRukKFo3up5*A>&($(`jl^0!#`)%V(; ze&RN%(!Jvc+|ICV`9KtvcBbY}CLeP~G^DL|U25_i&wq9Ih5L+(`Ch!lL$_rYbKeoi%Ou*Ro^jT>mx6#@p;jw4x#g*7kg*y%!bw#$g929u9*3G_r=i0QF?pM{~u{8Z5?#siP(GFwr%Uar+yRY>raqU zg10*4s4b<*mhb217UUl$?$3=hHn9-9a$dgCC+Qb{e*KFDl}@M8&3`t1T`SkqOlvep ztEww?TCs*|QJ1U_4+{wi7m7l~qW-=xTAfrs95V3l{^3DahauKLzsm`k-t{AvQ%6gC z>o;Gwvs~Mvk|Lr*TE&CeX-Z}5fH*TYR-P1_o-T4t%bcHScbRz>D@{p?jG3Dqo}X)E zcV>*p3u4w8+sB%T{`OJSc~PTV)F;99$1BUss0#~bbluU{B7Y@VuI1G=$JuA$V_>R+ z;#6{}O2L)oDX;-|w!1rv?agxYjCNylJ-oP{949uL%VzH*cdWh)e-vb8%hOYe{(r&7 y-PZHC;Dgl_qOwy|`=xmb2#{wdD;UBoX{th^kjjg4`xW?80T2d-^S%j8D*qqe_SCrm literal 1712 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 V53hPjAI)?B3^Z7R`U_=d(Leu9bMycJ diff --git a/toxygen/smileys/default/D83DDE23.png b/toxygen/smileys/default/D83DDE23.png index 3cd5062af77a5583ae19af024cba6497aeab51c0..bab140cb3a7cc0774e4d6efd0f0e2337b7e310df 100644 GIT binary patch literal 1677 zcmZ`)dpMM76h8(tQIpG-T9HwsG=>JHQd>JB*Flo(lQx5yK{P|-UP~&uO-2!lQZ7+R zR?EhS)K)$zqf#!ptg_Q(OKfdCJnz2duiZcPea?5z@0|1dz3=lq=lh;F&C}hHs;RFD z090oudoLWdXP-H$*qfSb?Z81zz~-<4YO>}|bJcN93v}}00K~5VNID8Ig-b~afG8Hg zTYmuCe1Juw!iMde094L;x_LVwyp91%0h9?S6;QeoV~_zz@~<=-P@1xZ3AlwlHcUGW zC|9Y)1dQ_mwp0p)ncygnbhlK7}qSRYOP6hbr{33JoSA!VJC5Q?x`>hUzO?M2Ij&mw-62 z?D%G=4AoQIOG6{^gv1Ed0eYQ=#+uQ)ax{DZ_4%USU8n_++zhoFln2A9ouI&*--9v% zpk_d=fbIk83n>lO*>4G#LUmDp2)YfZ5zqs8eI8OAprx&BS0hjQ5T>ZKppU+5R&YmrwGtPKu_U%3``FlyA}ng9#DS}k^_3l$79J7 z=~d(TS&6;eXGe_}93(oX~Zj8BjsDFPwTWvB_S2xP%jbB;9!j$mZUz{d7 zB0PK6_3WHyZD2K`t)n$iIsS5qa9|##ijr^c9Vm?HE84n?q#{q1q#miSj4cq*jrP+T zf76I7D!$BV=(f}8A6rn<^~lZellPB{D>4rn@AmZ#r1ltkxqK&Y*@UV*J!;LV>+;t> zds6Hef7H~!guX1=MdHF(-ZYxLJ#MS*oe9UrJLz9CUmTW=>5nYb7;g3e`&aK39^QAC zbdl^7TO1E-cQV!_hjqAARcJHM#qZYCv@tj)so-<7no>!wj#mZ)+*@W!IO$`zS{l~{k{hHSZY9|@DN`>c11FjVoo1OGuwuys( zawUug{nB%mms>gD&s5FXp6$K(NuWQFY@+A8yIVxt&`#;3-i(WtWOCd(GGiydvPu1< zQN7bX-Q6~)>!OqUzgIC%pg68%iNtk__yO8FQ4vuGV$?}7TDv?fb;uE`g+_a~B+|wv zK5LBW3Z!%9Y0lT6&efW$Mb((End2|bOwCL`ktWH=kfdd$yGetjE}5<_?#_}7SB|Sk zdb^Y_ZF@)%kJ>s2qNDvWLFDkHW~NLg!(tVaxysy($&?B_J>3}96--Jpkv>j$P$%=t zF25y+GrheH1o4((LMH3+axYJ%+$%31O_^fs|RGQw{)lT=xO7X91B6jc<2XVzb+rUU34q(NyvNC6_Gq+shZOO8=UTeFS zWyWIJvRK*d;miLKgbRa0_;LR)7&7=l4-3AXJzc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYu_h>bAii};Ey^rQO>ryA&s6}2uT>@%`z_5KjUAoLjNHu4U0jWT z{x)?JgIrW98(0$ z8nO;5oeT_2Q#@T9Ln>~?O!oE&36wZq|J_Q|5*aef=*Fl%L;u z|L4E|?<-9X8+1gMMlo8&3Ej~AT^0O&y5b@mk<6!SKBgVq7UmqSu;TJ0mJ270YAU=~ zc1d3{YuGifl3o7R!TVxQ`LDNpSR!y%S4d++Z<*0ezrf85cNZjfMzzUIx$7feBHPL^ zx8@Nkd2ZENU#|8K9pSsX z9sJF|pX;3dp<<3k?1JZoYHnKUWNi1HvB$!r;-anlb4_{6d7&ax!<{~O$K-aO z>c3pNx>51J#nI|(fiqN7=N(=xdG^ahZ@byAT1!_~{N~yp@04-mRCM>#`8q3aZg+Sj zHKUeq{rLk5f6atW9@Nv@cfaML_nn<~XO6JE(QQ1_JMDXe$-_Eni@5>ML>DbFSrVUaJi$>f4^MozU|XhPO=(%Gg{Jje<6NKUyKg3yC~7$irNkY~DCeGk-#;*L z>-@^1QaPy^Dj(Jy&02e@J*4{Q65)xQzg6uyr`c3-sY|Uosr@4I!phB;Y%BI;q$eqr zWVi{-&Do>mf92$&GZt*Y;#X7uZ0fXl@^1E>l^yIUa_TqNNQyr>elGP|QA^HdgJ)IR zt^2BPTTOpAS%WLtB~eh5SvlrlddQ1q%O{-qCL#OG^~9--^#ZH%uFSY}d%sG4sRMM_wR011pzDA#0A&G6hyR8*0j2G6Ou>#Y zivdf!1*iZ}CLk;U<3)f9_Xx&w_mB;!Vo%9|qDL!NzJHIkz!2NGI#|I+Wq|JNp$rBp zf_J+=a7HhS(UJ&BMd)Qwe;8al4K@Gx%k1MUv?8kE8}^68Zq-7KY*{kW9CwF{N6Yn< zT>NNr{Lb<)ny*H^B(zY77E94`Gg^O&)_c)n3Fg!QnXlu-sYe| zWAqr%uO#&LCYrmA#u=y+P#27f0?;Es9e{cOwL|SyQer|%j1|OK!`H%Vbyts}MnKP? ziU%3>{V$%CqJDfCk{5SOdG#u!EUV*xj27 z++23Y+qri0~JsK9M2}x%7>i{?DHC!=M=tDd!8N1-;O--ghF7x&ZYgm6P1Uo1WnK^ z@h?T=T;Dp6FU%fa><@17wplwp8mZTpGjR7vjhRQsfh8XXYm!e4{@L2{;tKVBZ7N=! z+LZQXs+7rHBW75M9Tw&MvzmwPC zM_deaNMWrEPg0D>Ll0P1+TgCt>f`R|F%r5saqa>(Z)T^x4-<$`lpd?j?!0jy(Yo6ZNcVu1C@^^;&pYb z$1RxG$1YEn)BQ)JQjV5wP}yB-kmhG>znk%O&CfkDQ}YzFEdFHCc%NY(!A|sa6$cqu z^|U_pys&;h*^Zi!lymw0nSNDb%6@bxqT!9RVSR~qhXUbmn7_m+& z451_9F9p#`Cv{Y+9ahDb^MW=_oLS9Hhuk{NR)vRQw6>9?*1@U$t1=rJRJgN)$_x}f z@oBno%cR=HNBUR8@`!zB+7^>_jmT+%-I2v~GKMJE4CtJv0Ao#X2^o zs&tASLwktB0(rkf7cDKL4*4mozJ5I;85y!755Jw7WwR7lW3w`yo%;Kn`m;GLspR;Ir>D+R@ZFl4dAaUxLlTL3uKD-(f6?xakO&EfGbNH$18svfH5@J@-SPah zUNtq`>bKfeq+_JI+MvM2g^zVeBobGf`?jRS>#NC$$x}97CAVj|_@bGP_A7DQ!t&PY zPRFveujzJSUETD&YNuk`fGGx5sT}eoKg^r^MfH_@#@VtkB<2Y3r`MLWr{2BQQk$-> zr3Lj;T6+Ep(Gq*Eh61XmfXf!}Y&hXO%)trri4&$|E7Ri^jN@b*^S{|xSdqwN8#1}b z=~esx6atDf5E(lzcN;U-|gTY;wOk^hx5RW8{*3&(1Y3jJU1Sj8yohNhkY*q MbgB!b!Jd`)AFH@Ke*gdg literal 1716 zcmbVNX;2eq7>-n}q9UMGv{u)MiiI4Tkc0#lCZ~x=&=6|WPRAv?zzWI6WHAAyjvSGr z1i8FWIjz!aKu`y*nz2ZcvEVgTMAS0XQ9DztNQ4S#HypM<9Dn%D?ta(%Jnuc5y+*lm zhWmVX3WYL5A(yDg+0*q-nM{5cy?hIk(>y||A)@eD!VDWxiV(qLP(Y!B<4_d}BgtD@ z(GUvdlUPixAv6)I1zKE3hg~vsi_SozDU^^fiviXqpac+u#$kGh`uryc6~GXPx{edU zj4+7NcubyRM59uaYHdn_mXA=wLV*yAfF#hN1PoYoiF%X30#QeJ1?1Xw%%B1zB18g2 z9W_c5u?7(1Mik)C*)%N^1OYCW4hC|#TrdD&F+moCNj_W}7%1Ry1uPygHmIaFBN8i6 zNn~TX$Q?wDCkTUp!7!W6bTgZd8{-%tpU-!3uvj!wf@VtA6R?G*H!T@wkf0{55i<}N zt_NI;6nGBuI<=2R`iBO^cnekp}lRDXe zGE}GuPcmxBe8esp2a~b;up*ZrX^kM#h>=Nw6D7DdNr&nQg#@CKH*^F;1X7R>N?B|v z$dR(7AP5G@SnQx6HdDlrh&Zf3={U#xure8o$Cq#dc~T}9Bw2)FabS>`96X6w$_x^Y zV-^=JVJRw^s;<98soj=;5|G;uXz1o-}qh3qzH+uWYkDW>5A0BU`g{ zS9FuZpbfaRrHPi&;H$cPKBU5)u9#Qel-hQ^*Jmg{X1Yo0Jtw?!v29-CFIVUb@0jZ= zd!Xtax@G2su){T5d)n<2zUi9jIh3JAJu-7c5cfFRkuP(;Dp#(x+gs!Mzuq3xdE%S$s8jw`U8+Qmv^}BrZf8YgOs%egTs}@o^|2!-b<(44B_iz7xjgfv_FvAZRZd| z^Qxiq%mB6^fV%Dcq4F1ls`^hoh8&Co+NTr2^@USR()83vX@AZQb+f4_muUd+b=Q7Q zNV!|=9~=nUax2Z%ZOz9LH4fi>iGz4{R{L+!iDk~6-$8pPRmZ#YYi2}m8Pt2++uAa# zFE_9E*QFbiqV@NehbfV?{h1tMc%Q<$QFaH`He9YPJf~nBD{$noRN*x%B9Bxd?R8}n z=$uU63*h#p#$x9{p<~0%6HwA!XZzeT8g0i{vo;IVe66VVz}52$>!TJJ?}>tU zYLBnq6WL12jUC2h`c>r6%UZBN43th8=AJo z^y>nzTOHB;&0PiM);;($|J0%mXOlmqxbxb3Wy%I#Q252KcO(<7S>4~#3-xav4|{E* X)Rl;Aeamy|UH=hBut6u*Rq7_6nXvZf}xS(#@2$R=B495Wl^*OY3vnjglG&17=MkSHb;noLtf zKeR|C3~Kwaj3231%Sb{}iK4b+vlOdz#@_vM_MiP@-+Axe&%N({?tAatd(O)UVEdRD zEi?iE6Bd&ZL};^_MVdp%U%jz9nQGK0k&PXLrhX%)|^9*a%Imhj_+U%aG zsqvn!V~T9;iq|<$)duc^{bj_qz2ecwiLMpLv#CTgfDV6y4+9SATQdn`u7u_1J@?gI zvXL$6YQ{1f0NuR_oXwITUK~Xn6CD;AC8S99h=r7Rkr?0`dCtb~m8a>(+8(oGOMeyd`Qp~0 zgZ;vi&Yb&VmfTUf^{U!BS?1N!H(6g|bx`qpkEbrl+GuC2a&i2JMmM`&vhQ~7O530O zUGjJ?h0lY#lgDdP8CeG1QagFk%Bq~7Q@sOa0XOPKe1@X0P2Y9>NPEss-23#Ud%=9e zZT-&SSEe^5vikSwUktf`2qcu@rw!Cp>gi(s(cyGXt5)L!E0wi55nl2D#R=q88}5(*sYj*bp=SBI~iIbYM= zoLt;o9PR0JH#*(m+1P>q5Jb_TkzsrOpAdI|xsD)wHgiI7beJTL7b67E&}hDp!V>Yq Zg+W4I=$_rb35j0;faT3*sMd33{{XiA{U`tc literal 1619 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYE%uLK(4a^LIrZ|}z8@d@A zx|%q;S{Rv|8^QE?<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3owQ=>r|34@w+Ji3KJEOo1RKJm~{D@XV8%2h1@= zz^qZaINyqafvMTk#WAGfR?FmQkB~r-w*9t!$!@dCjJ>tEPs;?$Z&K)C;?+8}pk42M znoF|EkAMhIuba*qLT*b#E;v7!-Fn6Ovw^6f#MPD+y^E%K&YbeJ)-Ap4^&I00=Zu5( z-*W$EzpZ~;I_26d%dnM~HUv7bu&4N#eQzoee6(}HyzX74x4!Rk@?~EU!lxJ_cvkWf zKlfJwox3c@J_e|lm^+@nEFgTv^FR`(iJkBPh1;1*jW*w|a9rf%;%>{FDN*N~`#`g) z?v77UKofh;vgWy2VG>WcI(#Sf?spPsIdq|Z&E6TN8+P+do2)%YOS$oqd4W*(^tb0Y z%yt~ge10``-`5kmvu`)^OwOF_!Ft5|LmZ*SYg{jA2UXD{P>?`+xWe5YHK zW0q!TD7RjDwykuB^!`n%%6)Sl>xHd*x1~he`@cgy*Lta}kCp5vZ6~>WyOw{!ardR| zA1Ajpe^p&nDBg2Yo7e2bk4(4Dg;|AcN8~3P^0wXY=$Y;Ds^L=kA;vexLZ`LYd_48E z_4+bjt|-Zk-o+(*-=6cFTi~+M{ATVv)pcDP=j0sbUgyg_W9z>~O;_)nnqBpb%jH(( z-eh?fsh)yW?;l?Ie7((+^TkrP)VXy5u|ZXj{;c1qaO&|Qjo*s@P4^#A|NCRM&h6tz zu1r4I;ZeoJckb)c4f<_11TQID=kZN z1-FzRSo-_=nHK*n@0)vq)rx%PKWN`l<&>_iS)gLx)78&qol`;+09W~JZvX%Q diff --git a/toxygen/smileys/default/D83DDE26.png b/toxygen/smileys/default/D83DDE26.png index 48641e6b36ccfd01f85a3fe0550483d86d6ab92b..bd494b639a889523e6cce156359007a501fded91 100644 GIT binary patch literal 1607 zcmZ`(c~H|w6n>8gi5`rLT zw4%jhl(ryV5O1i8L_rZnkV6@YN&yWbXmtA{Go5Mw=+4{szW4U~c4v=S$y!FBIo=iz z0Or9#exX>job1NmVV>-4!Nh`o3_XMnP$e_fatyGX6crR20+8+qkhKrsGgitP07#($ zyk!G;l>*F2RMxIr2B5cX?P``k!rFd7`GCZLascJ(JcjcCN&l4=0+Regz=kjyW*Dsm zP_a&q5inc|=#UOzSfMi+pmJSB4ya6L7!#va=sbooIhGy+bW&#+z$({5P*)lHON9ni zsH=2xhMqz+tU@Cy^d9rqP;U`Bp;PtjMMITnxDs{mMvWBo_7HkoiWTrYwx(nzJDBEmN7Ga5Q0J;sRcNgm2 zjcx&|19T72Z-5>^bqd6|0(uBP^I)3`XlIr2ZfefRZhys0q+O3K~+P z0Rg%Us2R{Bz>b;lA?yKmA8vwB#^lvuZ9&1a@ixPQUrSBuRK*7=TCMEa#DwNqt4zL7 zTWnaT(hX7M&p(g0E>vr4%P@&vANV+erDNN0r$ zLOE%q#N>o%ejJY^5GC?R!uUi0Pib-3s_IUXS?7-2S$gmCWUp#iw-~UAY0L*CLjR z_g+3LEQ_&ESR#KJc|euA_Nn9MZRN(>g}LGUm{SG91ynOuz;(Iv!t<^N1_|>kj%{Tt zw@_N2#I3qx?#TE!`gq=Q->JW^vm$y|1UM8IM(nt?QMA(Qj;8!2$3G)r&?iDD{WQ&_ zx5EkMCa1PKS-;Pz$+G<1ebUF6JPl93(Bu5vCCl698kZD=?ygs6 zeW_b*U@DKF{bq4JYo1ehJSI4yLvmbcIU{2U{?Im1BQelE0^c)FFYFU&jqE9b?QG-oaRqa`%C&sgL;}${z_rXuU~pD z$(#I|aHdtz=3L{sujV?z%BOKpxVngY^A1k4^?v$5pZc3GeQ8op_3<9l1rprd6cY(| zCbxa5`@JJY)Nn@5O=2y((J;wk4b^jG z%rZtk^8Ta7Vq{b^YC7`CbhMByH^UQ5%uQutV_81lq@aW=&y|S9QYmD6l5D7IQjuJ& zZ^gCgQz(jxrZ7X9OshTl_98=rhLM_@(fp>S{Lz}4M1KQA$BOfV%1)J5tIAdmE_84- zjOG=ezkMQ-M4sOi8WtVR=7ceyk)>pcjQq5@HIm0+>!$~94BW7$+vl$3Kt=%cJzOqY zbhfu2+q#3mKU~1&2zXvm$viB88_mtlnYP5)bur78=C#P(%Y8A0M)RW4c!Y+l{}IF| zaQV@q{})VUW=?+cHj^tb6QTuaoMax*xe1XxQgA#ch8N1?a79V?dD!m`5bV$J`_-47 F{Wmw+)K&lh literal 1559 zcmbVMX;2eq7>>YLz`+7zt)*r@%B^BJENZpDTGO4=gEHVt?1ZUq&PQTW^fK+9x_$v%N{xEKk_Ve(kc2;V<^9EMoh2+z`_ zsnUcL#>u7@dzh@^^lV45%VD75DxPh~zTD zeMZ^SGNBaS!$2AZmf%n!2&C025Vc0DMJ7Y23PF`B@M;qfHLlU(s1E9XV4%%I=i)ZP z(yt5bjIdLZ1YD`~dc6uSrrMVv(W5;!qF$Urcn!@~*^%X3hG zk;>s0NJbb~+UJ5>NK1P`%!&Ph0wGiSC_#xRR7$ry;8(A-DA|~Q&3LJ_m|Y|=N*g2c z3p@^xkKFhm7{uQ^|ltd6j zZ$UA=9#fe$gjs{C$soteum()8CRIAM4h#rjF_BuWnb2tTYPH2|(ddF$Dr9x*tVJz49YLbNZ^W!kNikrU3Alo&fZu&s`v1gC35-z&isL`U5P1Cn+ z88&p*FHmU7o?nkIF15V2ZE#h11R;RFAmYcaP}E1qS_J`{kM8_FEIGB6s6aa8PfVS6 z#Gg8C)P>d6Jx6;kKT7P`q-+`1DBsv!9HAmsUVMDN$Nk3DEdNGn+3eh+i{2BBontq2 ze;Sq=+ZDU8bjhaDhR}JH82@u)NNrc$5Z~AnF8Hi2e6qeNB04hANUP%)iFgAu1#5b==A78qs!a4;Uy8!uC~^vQp1_|^+@EX>e%tkdwv*nuWIw! z_K_K3m%Q5x?=~DMSg$$yaK<<0b&fZYsF-JgZ@em zJzw#AoLF)wzIl*>YdM)*xbXbktDEqzpAAgviVI!0xm@m!tb?0lD#;s}AKuRxu_}C6 zWrLyqkM+?%R(x4@vbbviS-f|Ka6xnZ@0!yMNrzpn{vBL`rFPx#Y64aR-f^ua~~F+;(MU_5O3Vx6kjRyA#?+)>h_G^J4}V>JPYF zbu~|p$+M_av9&%69h%L`t@}E#tyF%y{Stn#l{y%>~;YoS?=;wOu zyM#RjqaW|LoR=Ki1b7ng;~DHJ!Z*KTS2FgN;YGkUAAGhZ%}aZyr#3zh!ZvTL0eozO zk7gVUo^)`N5&i{u2b3F)l$(t4I$$ke#}4fH9d7_u1KtHZ4_F7PcnDhr_z;c=Ai@@8 zEWirD2Y`)`ze!il28Ame2%doTfbZk+QxU$G;3dEYz(+t#;Prwx#MEH1#Mf(Vs$*;3 z{??%*rZ|PyGV}s(nIM z2^u2KnX*k9%!-${TLh}ZZW+GecAK8(nBpj`VA<{;VY#Yzniw-NWeQcRlsWThcYSlF z{*BJlm-Z>jJ2)a%yw%_a8K*oUZ)8?C`Nb=NrAQ(A@JExepDu~BP`I@BQQ6@lS%jQX zZMFN88rza2CA+4JTrbCQ86sx*?SUCdS7r@H`|8Yp*}>Sz=&ua-k7U>yOc$Bk-io*o zna2%l>Fw<@c8+L2!HzIVuC2Gc@@K`vfXwtmWVyY*{dtu?Gmf7YlzlV8%JJ@{ni#Lg z?KOV#@2)Q8-&Q;bH!mq9kx~Yn(=B~jB<{_(%e4F~-Bzx;aGc%QqtmZ(WoXrz6@E7| zdstF!BRv)yyHzpcBKKrnu1_CFZlALuKhdJBW^G4cR@EMp0>!&lHi>$8()zE~W_{&1 zoYtv|oRTW8A5VR_3@nU3nUhz)PPCo*`1lclcMm;)+f(jrmlu$dH=3zwQ5_kPUwal) zup>qN*16b7_vLl_HI-cf+qugb+A6BJ zO{2vmh~tgQe`Ec?x}9g9%v~`Z9eP)kvn7 z9jcJ--zS%Ob38matM<#e*_#z!o*o|V?&LHHO*!)>?T~U0+3v0Bo60z`SS9}2FRo0@ z6lqh~rMoqnwAzxA+BA)ZJey6ie%qPTsOpzW`|mgAY!+Bkgi~8O%X-Xdr_Rm`^y3S8 z{D9VmGwx@iIcJ)h8-)TskDTo0w#NHajZbTa0Ts*~m_kiNkO}b(W=q&Xl2E=x;1nVj z5C!b$c6JNsjtdsq`!Aw9Enebe?`T7(JJIQd90tl> literal 1600 zcmbVMYfuwc6y8Coij%4kQ4xnVXhkYXcJq!z%wv&YgBU`I;;5Ts0}Dwu?k*%CGxaee zBEH(-6cnkCiaMpzRza&+t&UjiP%I8Y9jMdsUA5Z!0EI$tfY|0!^J4QD&7#U63qSE0v)LFMJzmQ(}k$!B7!~ zNH7#b5JaAYisW*UP$SW4B&Zk*aJ&htMubv{PA!&ULKy;B`h21^TD2OCKy=HxJgttYfYQE%TC6=pS5X+t?YVi{N{5e zVM^@2$;kcAgsRHA!N0%z6S-0|>Tcg3H~R;B23i#z@@?8}`4y4HRWr70Us0Zpw&Ix= zM1_LPs^*=Y1sj&mxF-w=Vwe1&cp)BB#7*(6?XDh#KHG>}VzpIkc#X09JLA@c^i8c< zugs;!P6^s@_gWOk(bdmwbgZWuSCwAiEq>JnC3b%-&L3sH%~sEEeNb{Dq?c1!J8z=r z%crLB?M!R!6M)UAa&1mjQ9 zS*Iga-Az|-6p&Y@ui!*qP1siUGWE=?!~2tcy<46o9qBUHu01{`+B@moB0I74$ifEs z(P@qa?dL69nUY{hXPh^}@qB=Be%mpA#RX#diW70OOCt}J%qjSKw`HI;;+Ckf$7d|T zu(Sqydg#XLb59P-RL0Sql+sg7`~JVU2j++LAazvSH2Ca|(J?V!9qVG|HRS!+=c>K< z`ThFf@VRv6)aRenEbrHIjT!r*UM8HlX{OJdzT4DkE}M8w8djSgHm?2h&fMF3?_}-D zc>MeFmS(aytf9W7F86lDGDT{e{L!yRf^}hOYa$*X&bDLU5r78t$pyo4F?&%Y6J|(@!5Z+n-Ob-_??pZFzKYAXEt5YW?k@ujQe_ P|4-Lrsk)<@RqOu&;l5jk diff --git a/toxygen/smileys/default/D83DDE28.png b/toxygen/smileys/default/D83DDE28.png index 76ccea9a572feeeb43b8035edeb83d25def17fe0..75eafd296c08a6c576252dd4007826872f5cc5ab 100644 GIT binary patch literal 1726 zcmZ`(c~sNa5}u$SPz79&*X6ZIF@i)OEKy4l2(mtwDg?x8A6Y@x1Pma)Bc)(l0g>g& zqCy~oW#38#DvPYLM3#WaDiR6^6c7YjWy$Ri{_7vT=eslC%$YfJ=H59Ib=J{(pW;V~ z0I<*2#=;5HD%)3n7bZ2em7OuQ+mq@*1t^RAV3Q$>*?5+XlLNrDqX6MI0M;>A_zXZ0 z8DNeMV3Z1=#!GvA&KLmKcGjL|dG{;m%JO;kz_&fquO-6|A%uA_Jfs1N1QZMCI}kc= zdoT!Gp(c!69Y)4TXND(+u5?CsIs@GVbPG@dpkzR)fKqk{#uIlC2S~W%i3fCd2UsK) z_HajHJZXmr0A=j}BT3ukL;}`8uIEZeG+L-Z^EIpOQ7>{lMd_@OdcpF{$kx{8NPXtg ziwAG|g`*9^x3i;fUk?vI5iGqP#=7V(y4qb7vON28>Ge=!y6?v3#+Foyy+%_8w^6{| z+}zyQSZ_-A5v5&{jEWbQmnEaUqMU$^oPdd!eG?<%m7wm| zX0(*0_I9_TdRVTtYR=?TrTRB#&{rze(NlO^L2S-owq$b96F`kHpAAXp!N&kRbYLE9 zvjI>wpf>2a4NMaB7#Siet@8^#@ohjQfJCrV0zUe1?KH6Tz)cG{`rxe(s1<}B5Nieh zw*#Rkpfb2_262`UZw2UOGWtCoEvBJLKs|uE;rR8-*q?#{-^p=%F0f(cjkBdLPnIuiln0>6B^lZx!xYYIbtX_2KOG_gj0e$GG=d1*Xh)#%Lc6 zE}!dY8O;ARXoOAXt^`**c(he}+2q^x6D)sJQyo0zBu1AprrF?Huw8e5%&e3 z=hZ|lnHgIi#^vMP+YX=0EBj$y=~#{+lsT4RW!&rQ(4=~_Ys&Cl!l&wrgEUu{FCRX= z(_vHcEA!BWgs#=i<>}H1ZS`QhZZE6mbB1xNQuxhFpR6fZ)eyAtH>7`Rs>uB3d-KvY zc77zm&3LUsMgG1LQ$0U@kfj|#|F`Cv^)rR4T9L(7+|Y@5$20cC15leP;TabM^K^+=@!--M#_l^F}`z|y;tF5(K~pgHL$G(M$O_N~UFre4*>zNyFzmn`q=riMJ;0FUtB zEWa%KVVCmqKOL3|L+`m3U6_$Ih?%??8&u3ROgnNpn~@os^zgvF4-PgfDs?AZ!n@1p zc4SM2#yiBXH08f`*hR^jjW6I^>c&W3(J!b_XCxAN`>0R$9TO)WZtho}lvPK$Mfb5L`t7cpvy#X?oHf}mHoB;!WP1$)W`;eyUHkECE}EI z-K_1B&uEnVp{6*~hI8U)t2f9d{#KYymhLh3!Fyk+I? zSLOWV^kSf(JLX0)_g?u>7^yD%vkaLbO`6(g*&p?{^RJ@h9^Dkz=wO>?vDmcXWNj^| zw14%n=yW>kxRIV+p$~cWNkMEr@sS68}*5SURO2fh$2ork`2pJNIWPidrv6SMBoMow;3_Aga?+cZ-c@kP0X zh55yKMMVY0C546Yta$EaUvGaeKOZ0N>}7A?v-GpHx%s(8T;$@IBhgaMb*fIPaIk@H z{%wXnX;(q?Tz0#2J^k$wifSelJpN(&d74+>m7l!5y~-nYyIlz4udVLe8x(8i3p|0t zty+Hyw()_;w{+vP8GMcrE0BXJ&?oEbYm-lE>lxDY$VLVfBZ>iuOg18uGxIi1{w=|e z%l7gJ{pSSn>X{fU;lu3;&Rh?E2qTaKR5q8%!Q1*VJULDr20JvMowL1wz}C{yqS%}s F@fQedCiVaT literal 1700 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 BH`f3F literal 1694 zcmbVNX;2eq7*2(8$_)w%D!N9LqvY6}WF>0IZX`+&Au8f{EXe{ZY&Il|35;4xLBVQ0 zP(&*tN11}Qv}jQQh1gMy2Ws)CC^9WA6_kR4GAh+>IBb77{^-u`e#bt~^B&*(&gMo% zE_896;>chyT$JH(G(Ec8o{z`V?-~7XQF`#9kXR~)OrT7t0b|ItWIP5a2{aLl#!zi~ zsuc@mFzga=bu1ODihwjE!A5Nuwwch=Yz8BAzFCiIk}(R1#}aX!l-XO?zyxrul(|Bv z;;Qs=EC~6g+vYHVVF;Bh zWsW!%tBL~TqyYnjY(7iF1wlY0VuJ#qNCXB0JTA!NaOqpb0tJvz1o6bc=*6V18MFyd zG^`l4MX#jHB#P2Q9FECkVw?DE(vZjjB@&5EgU4gh2$nHjN1Pen8y3Kk`S3pC=m<92v-2oDiXO^ER!RG5J3nmgu$>e zu2N^DP@M)Fvy0PqN4fkDav`|^Ln+drCdrhs3W!P~Dbkok>H&F-2nbk#>$IfF7-&15 zk!WGefN#LG3Ij<1!}*2q_t_WA__8n&Pazh=2#<~%3=_%a5@yH*-$FBZViHTd~wHOX}%S;kGpC=IxS95qIv8beN|2dYh082F9<8o zdSP|9cfr~0I&tN0H$`LN<5S;nJU*{0DxzkiP}I4_$4{S{=@6`W>H*Cyex_$z4^Fkb zZL0AcTpr|_(EsOMAQ-amSQ&IR)oFdfY`+PX#htUETx)0gGT+p$$2r>_mi9E*{UK@k z?IqOs+JAr9nHAq0=HKYMvv9K26~sI$@{!6ln|Ifs6F2$pJlx*kR$oMXazAg|-Inmy zSE|}){yP?D2B2AK6-S&eIzH|#eREl;TfWgxGJD+Mi_k}Z2W+t%A|wyaQQHsf{Q7ts zHxElj)}gId?$s8v63Ds)d!JC(ENDE{*I(V7zrX=Kp&m>qe0t-#x;)ps#xnJlOMlAl z#^*(o1hre6`ku(AowID7)Qn@BYj6MXHh#Res%t~|v}+}eM;6}bu1vG;)BL_Rqv^;o z){FkO?q9)y`;#{rz<}(uiPujvQopLMI$M<=8g#V2tnmf1)v45B&avy@Q;Tyg_W7;4 zvdS;0y;YO_izi%@7kGVZ_4&!`FTP!S^{t?}9g#CP?OSE98qi1jwjsxd8rH2pFDNO| zIQR;uv5Z~xTEc~3MD&;WQ(|^qzF9ob>iJ;e>4)B|A1^*a zne_o#y`e`BMD%2rNBMnPoOGqEC(3Hvksr8-sP3FLy&xx3R(Y{B--I8mk1KDBD~~5S zB!hi%Cia1og-@a)eJ0#E4^5u|p{}4yy^~RCQ=y3mtJOd1@w*4`bNF@AS I*t*Pr0C2L982|tP diff --git a/toxygen/smileys/default/D83DDE2A.png b/toxygen/smileys/default/D83DDE2A.png index 0bb276b0a2a014c05594d88978025143bce54473..c98fe7f23b07ded5c971cd2b1a464acf2c730e5a 100644 GIT binary patch literal 1722 zcmZ`)do+}37=OsU85`}>vA{=X;;u^Lu{3_dDPBzURC-+q~TIIs_d6 zz%%HZnOI)7_-HM`W^Sh7)ob)0wkFMq#gnIh*7C;0S=G= z##jL4GJqA)zg+Wn22g3+w$+!0u#o{-E`Txt<$}42%Oc1Ilm{q(k@>eQ1eEiyX0e6w zSYWbJKqY{V0>T6sE(0X_0x&H85<)q5+3Bmz_pkaA~HkBLvipqK!M8bR0& z2V{#&VdM!wRbK)F7<1hd4VI$`IeI5YgJsxiXs8fP%F&b@DY5xF8ZJU7zqp3d(fe97 zS&LpAMjcjYOoGPBP>1=u0;mpG#lEpa?1)|^RdeA~0Q6M{q0vj|%~90913mNas|i9c z1ZW@}bz7kOfZ74w19TTqC!hf?8caj2Q0fm;=V9_Jlx&CcoiHs0)B>mpUN`t;G9cU< zGJk~erU29iZ)D6228=XrM;8J0tU@E%X#50vghxHbNU;(JAV2sVeF>nlx*>c6RcvAp6r9K8Qp3j6KgG*@@(7Fd?nIW5QfxIpFMRUh!}OCLp~!lE zp(I+PNr^S9WV0eGQQyaz9&7k@6&JIqgm}1bhHP~sI#y`%8J;xF5lvm4HCmT)OpO6l z4<-5T;4|6r#ORnPZp6k7fTT4Lo4bmlq4tR9Y_d;MZ5=zm zA;G2K9wSJ4tPqzyq7aY%`M2?m&51Mluh%ImRs?Q+$L%f2tM`i?Z)|4`j!eW=bc*5? zB40_hTduCHWnJWBM#aT~e7}_5+G=*Do8HeMr4}n!TumgDk^C-v8WD(&bGg z5-u6?+OAmzjTpA~{X07E*e1yt(GtKU4%epK#2x`MmlP%Tq&JG$-Th3I~>GcNnh1#CHN$wgG zmMxD?zML~92($ds>9M@p7MTy_ZO5lyk83VBYH#6YZjy9nd20D? zMc$H{+n5#Wj9=^Vx!EiC6J2ceuic1v$o*71Cq9&-6IQit zcZz5)f30G8A&}Y((^I)8%;#+kvBUY9v6?L6r9NDDVLxkV?7em8ZQ|;&;n6qR>VqMxaW7qN z9Qv%n8k$`3*5n~k@9S0i1Oo%zBbDFjQS@=*Lho+7dY)3rtJm(%VTf_ec8l}Mh*71| zu-w(s;~2$k)7p);&A-*vopZ8nHVrJLTxgKWPs;7h&gjtnBpvI`&F}W6x<>9k=oVTe zR$Y=F6YH^WUtn@_pnJG~yijLJdUZf7H7fY<;o!+5IYnkQ>q)Xbd-i&klOvz?SOh#H zUrWMCrBd8Sjm#}~%{0#7T)zcUn0xcktX2W`C)V(3JNRL2K8G9{!@&~RlkDwnNE>bJ zHu&0+$m<=*4)#_g5}8EO7@SJ~k0CNDEP|W(|AzQ<`VW}FaPbX3QCxmJJB9<4u&59Y ckrBxb=P)_!utZ)v2fI)J7&NcV7pN@3KPKH4ng9R* literal 1685 zcmbVNdr;GM9Iq(&IvgV4oTsD(cYwBO(l(`qR@NTCyDkFNv!jRenc~L2eV-0v>@bJXCNgo9naROuoTD;PsW_msVrjS@ z$4t3v+VD^gXQG)fBr=K7QF0??6<|(`z+ttqYz`-MvBQQLGjIk-#nT8i;sJz7 z!Ap=t3!`ls+(JaGrSZ76F$Uw>45Q4%TdV{^9dcH{iZdACux65WxkJGl(Ur4v=QhX# zMj%Xvf;Z|^VstE^p=cbC2t<6N5P|>%5kO)Iff<6f6^^j9FHkWOS&4$9@r*2$Nh3$sjE( z(n1m~tc4&bI1CmA2aAMi396RBV(qxbo49I;7)9Y=u~aKWAXY`DLZn&*MkKISrk0|? z<6IqSXE4%;kJ}|!yJK9@Te)%#jbjW&8z?GsyaHk^6hql9lnv0tAwWO^L7J!>d!X}p zMx#Y>n#jgYVKik0M)E5s-e6xQRf$w;1P+r*Q7z2I4XF{0MkW%eq-++1Y}}(<)BnjC z$T|Z$i{n4VGQP!DpmX}V^x4Jh=D|s}V`#QDx^@yXIUJXBI#gwF+|DfdFv76htEfrw zP0@buPZ?+LlGK^froEK!xcXFzw@<}V81HpJ>aS-$(@#vMxk zGGjUMnXxA6VNGRu>YXn3$m5KdII`W{H+pGRkykNuQ)xbKcv-{XGt zyNX#W&PVlXH&$>1;wQKSEciS$K67?AQuY<+WZnRO;JW68A6T-|`;40V9E#k%;nn`M z2}!2X=|WepqMpc-ZS$q+#ruqpH;090deg^i{WX<^r88?cT-I2&0*zCb{2XGvz3B$j=SRu`H+H+kiYhX1 zetBNU5p!C{M{D?5?&7<7s`akO3Ssz^c;dk=N_jr@qEfAIRsJ2ATS!%1N-hnMyqIxh z>B+WJWTPe{*}b^qO09UV;8^AK)$LPfUA*t(0v5fxgkEmCW9Nm(@%rr8gGb?bk4uqX>Wb_`b@_w8=t*YY!@H9gE}t;#>}2=Di%z8M z+kd6u>W%H`QK>cG0u}iKbNZ$0miac<-Dw?aF5IO1^!Hey2L@BblNf&5z9+49nJHD{w?(xbS7k^c-wr;R}ms#mZ72X66xEdT%j diff --git a/toxygen/smileys/default/D83DDE2B.png b/toxygen/smileys/default/D83DDE2B.png index 0b459a2721eebb5e2d7788aebc8ae53e3a7fa93b..fc972fb0cf7cb913125f90f4cab195d2fe804137 100644 GIT binary patch literal 1741 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 diff --git a/toxygen/smileys/default/D83DDE2D.png b/toxygen/smileys/default/D83DDE2D.png index aac29ca701f45b2b5104b835fb938d5d5beff633..2c7f81def793c97d2ad017c664aaf7bc18719d46 100644 GIT binary patch literal 1785 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 diff --git a/toxygen/smileys/default/D83DDE2E.png b/toxygen/smileys/default/D83DDE2E.png index f6df656ef48fec1c16a0785e028f0c8f844d8e5f..f9d55701c23587c672e4b688421312b6c19f8c1c 100644 GIT binary patch literal 1597 zcmZ`(X;f2J5FSuM5)px;SQblgr2-)!1f;PVc7z~=fN^U>NTQ+?f)Nt58W1oblmLQ& ziWRp?4{miUgre3JB%mOR-~zU|E3Oqqr`P;yfApStGvCa7-@E6&x##6ZMg)(g+tLAG zY-mUTpG1q#4|NprPOjskNf^DH%i{tZEgUlvXF_sDdOgp1%XIbHee25KA;9L&wvTo0$A{`v{rVBAytbl>wF- zw1hx#Ip8(}Ah_6Ig@C&a6)j+e!3dMk_8C0EL{8Ewz&!>dfK(cIc)J3h>hOsUZIIf~ zQ-CJ{dm{1X!g7&arN|C@7T_7cYtGo}fX#sSvT;C-7XhyVHUgdlJPTM4cp308T-Xe& zW&vIT)Wa$d$ngU?8}KM#3*dEV+b1aHL+f5Xb^zX)jZX^k6kr=*E0C{SrA#s-WAIS$ zBR)@zteHP{z3ZfG{a8+`*OhK{9vLa@8XkVt)lsN*87ZaI>kKVtZTI`v9WE^+brnQn zBn}G>0FB9{5R#ZGLV1CvgQn*6X(r7+!fF8H4e0`*f*+U0NKQ$Ttz0c-C{)Q(hB7f3 zz^9-zChS-ri`us)&z?&e{Zh^;I}sLBHhY6D$D#0&o62Tkfn=ViC!_uOuEqz!BbDsp zm>h36c13qfjq}9#Uk2H|(|4-uPW2|T{J+_rv$sz0lz)6(R7T>j6~~7Me1sP!-m0;U z^%;NGQD$>YUUXx-%WX5$TGMjg(t%sCy-WRqGt?_Og3dcOA5vB>pX%nVy)}^a&4T2E zymhYUE&GPXhyKOO`g-6i4_c(jupc{je!(1x^WGilgBfeld!R}dX3J#BnFlsMf9PJV z=1F5tL??tF%zs_!Ts3*jb(^ZWx9wwf((~qjh7KyLiddgMn5#8Q?cdOXbdIi!tWR_3 zS!(`ni`8p9yf!%sIj`B4R6BF=l$xF0@vfPor%rEzuhLF8GzBP)dOC|D{KKbNL4&aW z$lRLLWac5S_fG<-%T#z%rKp}VyE{Mme(~@T+U^PM8;4R#yBkYx#yzC}-TzYGS{-#l zT{`!z-4CbwBR-}!Hmf*HtGeG;>SZ5l-)-Hno-WxFxoo{QK`^7e|Kk$Bjc}wo zPmR4M1LSf(4h`T6zG#mZfiXMPJR*Xb%4U>UYJO8G3-Wp4E&0mfcU<3a1P*v?LN z*~!$*zIjHD>r8`Zu#y!gC(6VYmTTo}*JK-~xm!}?;}*ENB{{gcIWXS7u~@DhdNqzp zHKPxi(dks`+Y*u1j5c;mp(Z~sFJEImrl3fo%`5OTmR~pXctHzcd|Sq4y4zEWi%Ux8 zDXKY}GR5-~5)u}tr&F}^qXZ&RY}3^yz5Yr|OS4|zBoNC)QL$I@^=?isE|&}Sf*4tx zNE{R0<&-y*nLG1(TZdRG7D*(Mw$5ue+P&M{N6Vo*p7U{6>%}wV*wYk&LWLwwA@z+< zk&*x&EDsM?mba_Br@)=%JIl-0i{;E>`Lb9=-2R&X2ojSdD`l$x7u>a9WK9G%pLd8( ok}1;SQl!9@B#EVr(8RdqQob}!qLN>cl7BitXkbLZAwN;(KlL=oG5`Po literal 1552 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 literal 1572 zcmbVMdrT8|95477g34It19kH}Gox|s_3rvSsjTg_P(y1eb>L*`(H`_#dROkQw%}ui zAQOphic>drO>`KI$WjW59H>IEIgak47_fZo2|?`@{IhF2B3q>-+h9 z-&bR`WQ|Fllq?d7#+b5kn=ro~dy_^A->K2R31Ld-4F!B2Q_KelmK157Oc4o~yhI6U zBME1D**P*pBpOys*$emra}MTUyb>ZNBMEwa0$L=>$PD@jhlk{WBC>>{HQ>GGFbGgi z4VW)COU*t#>7ueLSTe7|Vs}({9BL<+ITy$XVgiAeGh2f-34|U)XCMZ~g&1WegDMTsGIQ(9c=1sT;6mBF=%840ZHEcjo11vWn z1{j7_Mg&!*R>c5a)OqR;EIXG94^a8l*~CVA08CMxy~YYEdPODpc|~)#~`%3Yw0&1JiO#m#@N;=94uIQ~tM_em=IBdn8HK$4z+OAFbbwrvON>lmPE^g1$KN2U* zKGoff9!QK>ge=d)I4e!4TT{r=+*ELR@{ zm%ddadsbD${di}j*lvq7A6iw}?1q{SKAo5l9ah}Ndg{U(OJ|%udzb!YuD2)n*&ClP z-FM{Rv9z9yXQBFnQ3>H&pKuMX8T0YUfORyq;k2r6+QYTEyIMmZCeQ3Xv*&H-dUfz% z=<5ej=48jsm5V)^UH;_M=+14^J90LT-=Etv$JF^nV3^ncI1FHQhML zpYpN%u>`euf789_*r`z4WbW|PmJMyk8}A*RQuIT|*y_&n%TE6pGM~*Vp9+ZAnB8fG z{h_QCOKwM6zq*}LpPriS&Vf?h_g4SWH||?WJAv?6rFQS}ee0eYQp8XCTYBt$vo3c= ziwYZBlkQdxKVbh(XF8E%+fd=T0rtwW8^T@B{pZK!|FyvO<(AEPkx4(xE*Aou63?Lx zZO7i9`BU$%Cz+NDdt9sTYAmCAE=(AGgV1!4K({R{jHH*G*gi diff --git a/toxygen/smileys/default/D83DDE30.png b/toxygen/smileys/default/D83DDE30.png index 8f50d8dfafb6008f6f119c3168a6ebb81bcf0958..b86ff4bbbe3630b2ee220388812f8aa3f54eb076 100644 GIT binary patch delta 1661 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 nrG=A%DPTel@StEV{YhRF0)<4T2X#@VEf6d67MM literal 1795 zcmbVNX;2eq7!D#-6fG!K5v^;Wpdd+hb6{c;B$q@I)DR?9v@Xd4QIbtJ8weD&sI3T6 z1P28wAjPR*6#*fll%go0#k1lGCR9kKA8VM+USS9yyFD zr^yXMBw8DofFU6X!Af<)W;I(w_U8j!Glvk+Avg?}buoG)$IK(Y)#VU#>o%1PyoKPK zdE`;2RPt4T5XBIHNum3yX&?x&SQL=KWU;`-07L^JDvh{Vz956cWN{!rVC*6j)-X*Z zMCKD#&KDtr`&IOCWrW@p>FK`|6GE;|e0gsK&Gg zT#M=ft0EkMns6SO@N_f;ok1>tC#*M)C5lKH)eIY`AcaQN>8x?RMH_Ji@~<23MH`jz z2860WjHn4y6Xy}>K29cb_hCa;AmI&XC8i|~1&$G+YLgDp<5CfiOnjkev>J{WWP@Ue zE(V!mNDPAD5(z|KvV=|(Fhv3;#1M~bypJ2eqKoMQ2E$KGV}XRq5;g>h*aC1yO93h6lIEpDzG-kX4Rz;&YYK%q=fG~swEDY1?HE5i1 zk@a{+qZJ{THWtxHFjNP;%`Zp$9{X&+0D6Fc1xft;L}G}D8x*jFLN=Wq;74SEM#MeJ z)%>5FQ3+?L*5de2v5apK6=g&8U;9LB07#P6$L2G_hUlV$y}mb zh9Cc0Kf%4MuQWRsv0GoRQY~B!E(jGCXUw`0Z9Z8zu>Z#r{>2^XRq1Irib2KAUC$3+ zsI;Vowmg}VloIaoD9wcSRn&Oa914F?kmq{Mv9hOd-$B`M|IpRAs|VTsQM2vW__wWI zBRoM#p)T?c>mW7EOZl|x=wFFhGu!;_@xAAI{RNt)l`R8_&5^FZH;YrJ9Kpo z-pPBOwLP7FpC1NHX>r~)vW0QJZ|C{;x~Ks&w~(iL)lDzoup(CHlQT1y8@to9Y2ay( z53RA|<(=NvJaXc9yl?#^SLEcBF7?Nia9vJ?$XZKm9!WGv}(FKQdqVmX$Qx#5o_k+hJ5?U&_gCU_9H|J#sp+ zZ^$9Ps3+Mj-?Vv8;Qg~?KUa?=VN)x7S6eW}ZZ7|}9`=mdB-3$ev5l9j^r`vMtr;oT z;pF?_qz86Mjkg~TTxl>ihd35A&8R;du`J&v6JFc@(s-q^tPZoChXZcz={uEWg{5%n zW$KnI(oJsdvXff-&}nnnB?Y~f{LUMl4%ZtVRk$!NrcZDr4J1e}K5m*}{l$@_;$Ts! IVB_|G0QBU-0{{R3 diff --git a/toxygen/smileys/default/D83DDE32.png b/toxygen/smileys/default/D83DDE32.png index 48c87eab50d5e5244f8d09d9022bbff8ed9cea28..f401d0e226ee6188d68c89a2d90117b92f8a8d0f 100644 GIT binary patch literal 1678 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 XE$%8@i=f; diff --git a/toxygen/smileys/default/D83DDE33.png b/toxygen/smileys/default/D83DDE33.png index cf5089212d66d49a99e800f58b17169ca27cf292..cecc347897abc212265928b5c44247118fd77eba 100644 GIT binary patch literal 1805 zcmZ`)dpOitA3yF^FJY5$3Awb^rJct8vKBkTj9UzaH_9->V#Y{H(?xD=B%~6rv46bh`JC_VJfG*B^Lf7KIeCEr-l{6P zDgXdg0v=05y!zHumP4$$_0C|#$;Wy4djQaUb%%tcfaFFoc%nZ5XZ8V*kpsXdq?9oa zz)3U!3sC?#vjEUezHu+e6#!YKK)(=N*+Bpy6x1d5*HYonRQNCp4pzfHChV_(BMor+ zSNJ1FadJT+fhI= zcnL0b!nd6pPcm;sfSF7H$MfKFCtT@-tB8F7$1jUB!B|cBKpkzgDQ$JM6fQTejFgPt zJte58j@N`u@TyklC!|uz1h0BkmL+-gSODIKLr ze>D2S$39RP3Wq`<-)p_m?{PW#4-O9g2N6UT76RC44txCIP$U$DK^{_Fymb4$rG8F<&M=YswTThMbJ1vvoTHHMuSIGF{PAHazW$kl*-*6{gv@HZFO zX91%1!jQW;rV<0T+Ul=iQTY6SzhP%*af)l)_Tx!!7e6B!Y<_$`a<#}x!c@H9srWA` z>kG@Gu}|wGR{eBx0z#CD_VvcfKv`=Mk|?DT{BcUJmDE(s)FKu#Es(ZLKNAv`N+hKj zC8yA1fH^CcA1r6b3(E_t(5?9N(XcrSuWmw? zm#$)OgB8<#zn{8g!s)_U*$ov`P8GgZ)x1dYp-?^0`nXMb^)a4pm~B{s7hkv8FY2QV zOVdgDYY#$x*Qmqn#>I1>!~`uh#JFcqX$=S+9qq0ew)wD!nSsfK0^4g5%#J;`r`60S z7ewttFV)v1u6R)MJt?uuRL;PyNzJE3?z;A7t^9h8q7A3$egIxWwQx~Wb6~x7>6$L? zT@$k@2S?1}Wj$E(o;7Tf_Bwp}dM4o9eev(E2HIalif!XsrH9Y?h|?8Xg8scf9dCE< z`kR%{-_%^-*prUaW_Q+E9=Q})d`a;%Kbc*{RhoVZ*rV?*I0j_>Z3=bG&qAI=EJ zQ4XE-r$sW(XB%e|V}efv`z9Va>m3(-BD5Wo?Q@WRj1F`1uJ8LOrxP5aGcv*sSgdf1 zdCeIg-n`|1C^RN&gf;AtCXy)Z*e$4-aPZE)m>CqDD12$=Tk5p`Sj*|+wtbdzCUu2( zM3E&zMSGb-y(AK_Q} literal 1759 zcmbVNX;2eq7>IlqqdU9%9s4}bdwlOZyM0+m z;53I%94HjZG-;4HlpJT+o+CkRC?Zj5L@E9nT!jKsoiZ8?MU@)M zx~r%!g)%7$Q-l-YvS3(^>*z`whHlmwNH&Gy>t{A7)v+i6sL*IkFQj&#ucQK)Mo3-3 zkuhb4091UbThC!}H_mHa~2U>Xk z5RijF2nd3F3B=;_SxgZ}EaE_HWL)D-TsFw#i6DPA4`FgZQbizuID8SG$A%CnfDcN> zxl+A}Q0mp_xLu62JH}XMZ5f_s1cwz)1;|4|CfB*rD<tB%`}d9vN{$JsxX z&a8Q+j-D~|R?*<5#xwr%h0+!eSpysE`8D6;^JkYXbX;6tKb$<=(b}7{<5k~{T?3a_ zDK;VnfdidAQJKKp>FAns&r{k;PbT{0cuq{;xi2iC1nF4$Frj+$tmFe_arRStswlkr z!4U7s3)XBoZF#?*)?@Ciy6CXEHKy{fUCnd-lCB-}t7@L&kh>gO^3m5!WG!eJ^eB}` zKkdt_Xgc2CbGE@!ain)`M;Bdqq%qSq@9;UMBtCoj#SK@p+J>j>uBja~=YQ9tN`ePp zlwe-My7VRMU&$i6S5{Z6m5*BB#=kPr{-xhGLwg| z)TW1gFD>p;Y_M{RR-7p)T6NobNI&<=qJS>7#m;&Z#hoio+J~J!$m@Ihq|?2`vh+>^ zUr0Z9JI93+c38WI|EQ%f@{k|TGk%$-`1j{$H#*8a?>V`+?k&iOj4_7aF9)-Ovob=D z7R<5ap3qJYnpkuq@u`6A?QuOq9h&*BOIo2SwtT;1`qqiu4Pjf+uDQl%UJY*VJcLv! zw6e`<_1k)e?y@Y0;+tcgYp-w3+h|FpHTLGKrZKaQ-TTAstMV*w_-2{UqYHBWUXRH~ z>3vkqgWBLbPWhOR^_I4mu!#8XyVK)loBxE|UY76i*2pGox6ZrUM2}qf?T#O`ZL0j@ z2dOxCc|r5M(3vHtfZi0BZ7~mOIy{qq?r#WMj35|3v?SOop~}ah^Gj7O{5Zwv%ClyC zQ|(D9*mr5KPkDE;_uo}R`|T@uL2%|eX<)lE%DO=*w!ZSVOZG}|X|-MpIm0>7BwEcd zpTCB9ym(JE{F(QhhE!&?{M2LR-MlZ-4rn^IXq=*twas@eU#w#(*4stQh;;dYigWc@ z_aQ<5Lpb~J*)-g43HJa|S32OFkkC7cn(n(JxNe{@hqd=cWngwY-7z=Jet0UA@?nbf UC8M$Pxb2rCMMA_SqR5ng07`_h(f|Me diff --git a/toxygen/smileys/default/D83DDE34.png b/toxygen/smileys/default/D83DDE34.png index 3f4f1d6f91b2d88148a8280ea1d3eba989dedaf7..b5fb2d7584d6910cb6b4f67b843b1372ac6e82d0 100644 GIT binary patch literal 1654 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 Ppd#JV)z4*}Q$iB}0fl4g diff --git a/toxygen/smileys/default/D83DDE35.png b/toxygen/smileys/default/D83DDE35.png index 24391dbc180a586c752fd27aee447def12b74cbe..41cacc7375aa7ea06e199382d92c0c15c626a67b 100644 GIT binary patch literal 1685 zcmZ`)do+}37=K7^ok+`ZS#6_53H31yCehd#%_KsUQ>qym*O?4WF4gXiF@s!^%Y@KH zN=fBXYBQC}t6B-Xq>#8y9OD zX#&7v2A%4Q=OuHW`T~4jWv#sxPb#4lPYOVF)}mR~LYx!!(tSMv4w?f9L;y3mBv1ex zzyL;>0OSGyeO~b`A7=oia_`N4G=vW^pbS8%fRX{F0fbD?0i31-%A8Za(`-OTzI*1D za2*fab{vpoeh1uu;{rhW^8m*=^N|JU%)D|8P~m*wPTY2Ge#S9gj?E{Ia5>tRlh)J+efT zEuZ>Npwav(nfOzG0eZ9wl>_Pzm}(D?cY*u4F6fybssq&LjRx55r^9 zA=(MXuL8OX!p*?60I?UK7C?_7!WIs%heOV=&mK@UpbkJ?aBRm9xtkz|0lB;Nkqm|( zuyVJl=J+ibey|(ez&%PxAx4T+yhX!NasGHOiF{wLxwg-Z)kk~2bgSllEzqc!O7bjc zXR~^yrzd-4S;wqqC93t(`6bI^y)#oXt8|BE{6>IMkjF+Uq%K@W$B7!B;Ym|_r>3Q8 z>hyK9UNC_2uLu1C_`a+-0xu>iB%H%0@Dq4!LOhoT;Fu}#-`RB%Q~xzoZ)*j9DKiqk{f|dM>4kzfi$vXW$>BH9@RhNf|C%lyrwcA_zQxBc64!_yZ_(!0sYu8??QqiM|hbBcC0axyyXCG#oIWOq_sr+JBgZeNRee7Cb_ zdN*fER7jh+)J)!PW8}5et_$0+n|)Cwh9wN}c@Syp&>GhOT%K;DwtLcG82!n5Z=z5m z(%6`aQN|yB6B@j}On9eJu+&N2g2G6@FV`NL&OEGqoQQp0Zo46AK(C|I(a}C6@?eEm=c$ zu|nikdM|pHTPY)nQ(xwuy_REU#mdejRJ{!Q$8b8G>pGcvXVc9^gFnSNWewBZK09>C z$8PHF>B`!ZIHp@swA?rILooU9tcODXmQtE1z4Y8k{BC|@P$_-}PxmrGIabSS&6<5$ zv8w7XU%e%gZ0v~+@80M$nJNz3tP;hN7gNuJJ^xra(VYEq78QGLiZ5q#)-Bh$sc=Ky6t9EmBOfyGb^O229uhDTZQ*hzE>IvcL-2T{nvailoIE zD&hf%v{*%~;!zQ#9z{Tq@urNoIJ9;YwNP6;IMmK`tle~&fMF7=oQ8dzN|Kpji*xCY>dHqYBQCB13C+qh8u9q zoU`g8K99$nlu8&A*+l&UrHQf#F$YFyv(Oxy$D0>!qcKwk&H^cT8bPY~Pnz5K0AW_~ z7fbb^o{qrNiRfGgkI#)YnsPHt3Nt@E447wAasn2d#Q>Wnle8*rD*lMBlAAlWMSNfc z!e*%WqfRC2drkoHfRr zsx+XHW45@Jil5H1v{EF>&dwHQi-i=ECV~_Sg+l{|1sp=1TV3|aw1rdl-Q6r#Ku9nH92q;ECiDI0q zBdsh(n(%SE1ZQ`QD}F0i8NuKfOEE@@${eqNxO9r8tmzaDM8qS&?8O9Wrn0Rej^i1P z7R4E2HExb%C<`!>UnTJ-`!cmytwG>OnGDs!T-=ZbiHJ~$#cCOs1(1t-lxzM!ITLZt zh#bZ7pJEx`;wsQFeO>z8;&t=jB-b$v*Ba|m8ohbEiMw^E+GzX9yd@^Oe4+Pw`<)G& z^ef|+nkpuT6mGuY6WJ|v`+k3VfKTJq3!1gndgIjMcOFfsGnVD;a-E6B`3W~vYp3tg zn*Dsc`BurGbo~(Bvg5O~ZRKI|7X#sauiE7;k*OoKQTNXIm zZ%q~ZT-ule230<@d-QyGIsZ^eXk&NGx%}R!Hv0bUMfb|Ts+eo>?(f#nsoQ@a`oX6$IF;W=)Xh~3Ha-mPzoPGJ9Wc^|GE%>hZF!F5Y<-Ftu zEUbI-Z+I;7eBFVk%kQ|g+41e*T4!#nqTt|3dFz|SAfjX&UAd*qAReVciccr||p+#Gb< Q+3WZX=(MqDon}SBKkfW+4*&oF diff --git a/toxygen/smileys/default/D83DDE36.png b/toxygen/smileys/default/D83DDE36.png index 46b30afa0d9afe25c6f514b9a0468deb2659a89a..a81be46a7c7941f32a07119d8c101e4bb9844e60 100644 GIT binary patch literal 1579 zcmZ`(do+}37=H`~Nl|-{l95%8+e{N;iZzEI>#Q(Z z0RTE4EQUX>y33cQD$W)*s{?RRJ4E-U12pGrFY(kdCmm$@djlNZ43M4)@DaDtX940V z0I#?Jv~mE0=nI{Db^@sE^Ydae5nd+&odT2tNCN1L5@V1JDED8q5KxwKgavqnGY%{( z15~Q?VgbhGfX*rb#zo4=2Xsl4+7&0}b(( zR}yW|FMw_Ux&>%Th^C{^R0Qe;Bm*P|)CA}*p!!nhntV=nvDo}h}7_}Z; z>a4&Pl~7N221wM6S(s>uJ-nG3FEv)JFee1=8&m-xq#k8+#QwY^r07_Y;BX|LBuR!+QXO&@`*pYZIhQ$ql?};RwfvAbEqVjlmf3CX`ic2 z-bPHY=BV9C&3(6{g7$Y9hS;}7onQG#{@7EqMAy!p4iUtW^%%o^*t{M7n*~w_wt>&;0!xX#%J7*0Ii%`K#gV zhBv2)tLBVsZ%14cmUzg*6W;e|RoeTG)Y)rya|MMdKR4eG%sEx8mTR}tuBnCX7RSrr z^geSc(tcqzGaKt)KYw62)JVPe_3?8L79>ns)pLpFXKt{$t67~H#XQzd%JAH)8~n83 zlg2w0PTtLhG&$tXHCp7|>8&kXx+Ysm+pnd5cGSzPKQL=jr6-2f@u)lMQlIII&omZX zQf;>PEmB!c=D2&7?b_1x-eAS-S`FhMZR*|vsk+IUPj(|iEuP^w*F^dG!N5h+=uBJ1 zKq}+2U_Aft-A{$>0XLlIVk?KTGaE(cu8yZ#^+-kE6UHsr1}0LYNB!bNR5onVLp~!3 z;#v>j!}-*ML1!C|9OMGQAz_t|k8OejsaQ|ak(8L5Z)V{X?c)!tfy0N)-ylix&z-37cf*eU84_Syf9qK$Y`% zn;!Q{BC7Y*M>@*=*<5bWz5c-y8BaEt3yAvs(hHd>6qbBXpn%5>4Gb8wkl0wCu^Ap7 z4dsV&)d)i4{m#c!jd$s;hhupzNe90Eay@=kb1{=64&#aWw1csHT!2cUQf(;PZMNF6 zw^C@^>}hsRRul@2LXpQwfBTO?C<;3)Ncw+6hwX+CY*@GagaDC1e1sRv2YQ$&luz;y b@(%I+`Mj{Cn0`L~g9CUleHe{y+>Cz!q-eg* literal 1509 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 literal 1679 zcmbVNdr;GM9Ip(KflRath@i2$sX!lT=_^NdZBwwUl>$`|$7$N8&_R=uL|e+l33F6* z+c`nhgDH~(Pn|j+cT+%+6GXsGMN~xRHWg&vbb?MDAWM0;{o(D8<&yj!`FuX#=kKyv zw=BlbC)kIk}0jb<|#QA->)#F&9nfB{XjU^3R@>N*x+G09j- zLM=~gQ=;jXrA`7(bS~2yof$@{iM3=g5aobq0V_%&fWw-Jk&r{i8qtO5xqF+-0!AQI zhKx1pRI*kFC~*P>gd73e$OAz@B;tUPLXilJ0QfwR&*jm#hz&+ULJ`Cl17jD9wnmuD zPy!r1W{Y0QSm_jHgScF~-OjNKI5?5U1*KA{TZ7MM(+D=1jZuh$jgj-l6=0M!5*8a} z!7;$Ch#2s-l#E4tIvRr2rq#X{#>lZm(JA9P5E~cd@VHj1JFXElNhP5Fxba3bsn52d z+ys=w*Ahm$9_D%DWIA{MZO9Fzy@BEg3tbc>6UL2ettdvRVHu15!ZBG)kP4K7D!xDk z3RQd+2!fJmzCa=o@DxH=A>>D@#x>r=jg00?lnP;_Sj7{9w2E9T1SN72T@IxP7AU}R zt{NjL1T&)Jb}h8sF|Oc0xsZ}T5eg^tIG#D)0lIXY!pU^p1}GCnz``U8X2R{{B6oR4 zqlHnzvJN#x6Sx%^$uDGigMF!3E|4ok{AjTlR`Kb$L4`=ElnMlLF`We-9rq~L^nY^3 zrJdoro8w>2GQLH3pnLkd_36dy!-HbztgMzq564Q&a2^{hHsJ6Q?I{F3rRF1hH&$><|twB zmi7n_MST?Owg2k&j8CR0%^M|N6M-E; z!lCmnd=O4{GKl0wJDE)r3UfomyJ`&OU2R3XFB^wjTZi|>VV#|!JqyU?KHZPb4z{#? z+F5goEc>F;92-y^pR)YPl*;}U%v5V=(egv}wH2PnsxL}npNIYR{(!%1Rp#mqruHZ2 zS`UN{fK}@YbUiK%)z{TdX8-#0*i#ax5k#HmWdrd9_zjyF`FoT_tKoAdPi)YZs9pymN?*d;l`4ZJ+hDE zyLLw0&nYPCeWbxWHEnaXUuCLOm&C_cHdS0<*5n3OIG^R##?(4><=-~GM64(4PSnFn z7_G+6SKOS4%v#^#Y92z1*qfhv2DTj?FdY{}?d0w5^}Z^6b-_9GUfrsc>{%;T7|Sn7 tl7p+_&SUnrrTHRy_9~ul;!{~MR$@-7vPILbv)T(9hVa1xfzX8edged?3 diff --git a/toxygen/smileys/default/D83DDE38.png b/toxygen/smileys/default/D83DDE38.png index 882d0aca33f074310a180cf3d3c3305caad0737f..ffad9c5e1c300c31d99e1629f848d2531412ccf7 100644 GIT binary patch literal 1740 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 H$$9?({Y!_P diff --git a/toxygen/smileys/default/D83DDE39.png b/toxygen/smileys/default/D83DDE39.png index c31174452d65e1959799d9c089ccc5466172807b..828b83284cfe4a60482a2c256b01b87bf750cb97 100644 GIT binary patch literal 1801 zcmZ{lc~H~W8io%@2ugy&rBKmwK@0VY&;$Wxk%AB)K*AP42eAoB;6~xHg|!v|Q$d8V zBa2E!%B5CNP?iX?2g9ZyC_9y}4n|bFuXXZU~Za9-| zvHR5a0RXT>J8M@&4@j2W9%MhJyWb5F89z&sB>)wv^4l~NQftudTuA^#YXcC=0bmPh z#m)l|h6CU|6#$c5091o+KXWkyKw8DwkwOrsV#Qqfdx(cx_vt#2lz+mNNBV!U&PDx? zls)Sb0D>r4!um}Kq9jntu|<%M;(^FokkkYcpM&sBkWdf8vwHLQPh^0#1PSAr0Gai7 zAP0aG1#lvNhv&JJ3U=zw?bdp36&Y?;=qzW0osK}b@)Evpfs2(}KkCCKueK$u@_}P4Hti6c$0@Jt!=M!uxQk zaUk=|P_EfnvByMnQoN)5Wjwg%h@OAWf-{Nls(j+?QJ3(5L&i+DamR`&$W7sLSsq$;oFN76~Zqyb2K71PATm7-O@uu#N1#+5ICN zqXTwRSbl}5!`&#c_37NE8*dtR0oVzMHIwBt;!`);T2^=KSK!G<6UhO@o8SI3-xrkl zgCZ8V#Q>>3N3+7o3Z)~Z3$W_LcG~!+|4T7QDKCt%seN>RaBx7hF5cbU70yj})ITbY zCxYZM3SSi5yMazDs(Yf>mJa+4fuBC0;{iVdY~2R13M>?ZU;IFZBY;oARz83bnhlf* zkm%squ4GBd0Qfh{_n3H_{8i2RJJIQr*_p5V*FLR_);2blmqK=esfN#L@KV;fT^}}t zIp+2%XGRf=l#he0b%g=_t|YiOpVBZAOKFMWV^fMjN;_B=akRHW8`M0Pp69p#aqME5f;R^!pPBpZJ#bVJT95B zwev}JC-K{Un~+s5J@-20#fy}8qyAa} z4aO=SDt9JHQ5BzHhIxYV0{;n&d>J}d_rlcpQgvP1VU%>G%c1lH|In%5?&e+L z#4W1mtX0K31dLqP$nmD$JJ5l5F6s(+f6 zRhY$p5sp47BzD5Ogd$uyVP;v4?rMi)(9ex`9wdW5zb)|b9 zJZoR2?T;}^DK87{*J$!S%8A4d+Z3?fNz7M-#D~0sv!t~9xepAOW~9Ez&&on=&RbPJ zc1Tt;5M*g{b!EK^Px34pWb`VPrdXKRhf3U43+$R~R}t&QcbUCw(pS0(CAL)inFGa~ zFLe`t2^0v?t5WB0*L02}6MU`wAKI2JSHZE}apiMP5`oH%QtW=Dwgs_)f%vF5?l;Lcd7r`|!Wk>6*T9(pXJrFc-~gq`%4 z&?Y~_T=QzpBKmn>1S;b7==6!X6*If}tzzT49=8?d_rEI($oQ}o%LjdTP9)6LaCX5+ zaZ+Y`j?_COq;f=SOUmc{bSjl<$e@EgMpoe(9fD?78^1?RX6!=yZL?&IW4(5X@0D$7 z>Fq1NCA0O8n0S>OkGDqE;o;RrZ#{C>&th5>xa605<^Neyzk6}Qyx|C@C}vkG&azok zQ}doWZBo$(GnMYrhpz};S=oDo*zvEQl`yP@gL&tNjd{GNP1FyB1r{cQuk{o2x6iV( z-QO?`tHLrct|Dv-iPU4!J23EiM7u|$f1+>r&3OOl;5W3X;tn^)!I=_rfjNEl;c)2e z%YsJgFMP4tHrgvyf_pVfNOglb@aXRCC^=pO*qG9(AZ29dI%E{ppVnn*TtRE#T!uYIFplx zCWd$&9L@xXJ7x4<{r?OBfee;!)ZZIyF1r>XhJ%s`Zh^k+2wDgeSTX{Am>R?Ynjh1Z WNn=C>cQBFv2>>F2Y+Zhq8vh3_5L8zH literal 1755 zcmbVNX;2eq7>;9=!{T5;3shtYT8~0b5|T)uNjB6d!5AV54zY%0frTU+lMM;S5UElS zK^><~6z~86t&UoZidaXGRy4I#P(cccm11;MM1>Jhgl-UQe>nc=&hCE4zR&YM-+Rn% z6~%nx;qK>-!{IyxD?l+edO9E1nbz9?UY#v)0mNuh^u92JrX0RoL884^PZ zRmMg;6pq79Pl6>fR3?mODG?1p;j|%`H98E9!-YqhbqZw~gaV0BGOT6e`+u&-1F(vX zm(qkJp^gWoz$-HKP;6$5M46eUWUBCy5kR<^g%M~VQ~{VZYOR4~X5%M#S=ijUO~eBe zCTJQP|4x)lC<1th9s+0tYKW3VCIfUjfgDPs)5*aAg+!(hN!U#fA&0VPbQUEHn7r_q zH@zx}B?hA=ePJs$J_SW}EF#flG7(Hv0-{eQl9^1VlY>GD!7M@y8Cq0f4$&HdrWioT zpwz=U6h^dwlTnd~7*RGJQ~K@*8l6!1o>*&`3=|eJqFJFMk_jZDM&mr!gtY+`L;uS7 zptV7gp@WEG$bcC2N-Q5qK~rEXb{}`-G{n@fR_kFbDGD`+D2*COiwZzC9{WO2!73Jo zOs0bjEqMoV%Q3`@0h7?2x@M7t}5-F@z zAtu98XL{b97KHS0I;4uyBN|{LzAX3y{^?{2DU?CwQJ9!wOg)E34de6aJdi`>P@}jM z{3KTOe_}?Ngu@tII9AQaI&PK# z@3r1nEVkt~+vTdRqsIrgxi740k>KMt!@bkEpTcLtpX2S-#hWSu0~d9G%A=gYX|Fk& z;F?yKiiO3GrkAhrTX2wHUfI_2*iS528c-FVD(~!z?%J1rBKho_hRsNKua!Ae_9k+u zj4CZnlM6EaJ!9PZtLw+lbkAXJsm+VIexFz8TE$KKsrs_5EARHru`4gK`!1CvUDtZG zXXV^8S%bap?O(?mNjcic+?TgUPxXJ+^vPGn$)=2&;ksH z-qkU5NEJ7~QMzu{EZbj}(+R`kd3yrylpfyet5)(AzU%I&Hl`kFT-;hvbAC(iSk~a+ zrQXqHEi_UvEXoja8|EF@A?p!jW-6Y)mKXJStaq@QQxk`u75=7|j{JG<;S#YI!?H^B z>$x4xtF4vH9?I7JFc{{|w?&SZrxl)wN}6`D!aZWEWX>10vb7sd)sG4H$A4S4tJ~M_ z{@~~LJWF*~iSBb?MjI?ErIDL3+2cf4J~h_aj23}cT6Ol zopa*Wp@_Ee*!;(LqL!ibo)F!3cu~aPmgANgJ~;J*wFQBR R_H5_xM!=5&PjEMU_Ydi^!2|#R diff --git a/toxygen/smileys/default/D83DDE3A.png b/toxygen/smileys/default/D83DDE3A.png index a18fa7d81059416b343706ed868e1bd9655f1a5f..8022c4dc609e7c8363bd3f9d6a0630f45a1e03c9 100644 GIT binary patch literal 1711 zcmZ{lc~H~W7ROHz8ilYI6e<-TvJ{F9(V#_&1c(}ifMJ)a1QL)Igkl6l%dSA#6iP&( zXn;p?fe<1)#X_+XDHM!KElSy3Fc6Rcp=`H5=FPP8{^-o--ZSUS{ho7X?zwZ*-RK8Z zmGzYYK$UjLiGf*-^|f&Wwwqb1yJJQ%nBqbK5ag(=FqJUhFz^t=1t8W0ARz@{2@55> z0XRzpcfQvk3zqPY2}EdZ{=&Dn!0&ry|Ut1M(I^%bhiF2TC{KeAP@_}^d${vm6_ z!a9I4pSjLL#yX?9FrWSpZ?j+`3;GMyr@sLVEdN|8{0&y?-@qC`Lq%w)aJ9)>nggpX zK5LEMOQqyL1s2o!uqp~i?|RYO4m4G}bc2L`^jm3jLz9B!j3PAMfTjg#x)#keqRHyz zRyw-phn}2AQa+lhq8@ff5+0IVM^ksuL>YQ>6}|q}$kYOjr=d5cSpVo{8XAd4FTR+# z5{vFOy}cZRMxvzkmeY+pSG#P{0PWok*G{_UY^mGogMHElOZ1R{`T+F;`UOxopof4& z8mJM_1CTdEW*9BwMoB*S5qv(^@VZIfv#~o-u5Dlgpwhc1c6p?KNnCHEENN)gNbsu9Sph7fbPOlAs~b_wa<@W z*KEaM(AOsd;8aMEnS6zA+ORM@zjs%D-XrmxYfVhvjrMcmKoKr#f7T@ba&**!Dy26-l zV}an<&NfeV+mSv(kw?{F#eM0sP?7F$TP)fG9(BkYA8-$Rr-AE_;rz_(u8z5ubI9(w zebks*{8S&Kh+mgRYAkVNdG6VDT+>w`O8BB{bFJvP`M~8D+n?~fr(&dXjpM1$NQBJ= z{V%kO4{_R*U;G`E@(vyaPuS2;haq%vu7Y*kkV&+UHS5knCK zQf*YNbW9RvM>nNPgkR!cb5Dqb?v(L|%LBclPN&G43C>mP*?uBUCpG@*OLIZIY28l` zb)@2#Wp)|DFJ*D3hKHpbrZ3H}UKA{EpZr?ec6qN0OZ&~Kk<&dozeemH>XB4`b(WzU zId&^C+1#<*Heh)E(_EGDBmRpTmF6=FPG`z^pSh(E%^VXbY|_**a>!gvm2F+r^m&i8 zDWsfL6EUxPZq42f1~q-w)b(z#qN!(!@!ymF{4m<@8BNWfDW2liJ0qu^mb?GfCu+eAS#!jDBK#*_FJJuE)8OK5~hiH}5@Y%1tS`MmjuP z$<2ySQ$KwD^1Z=@4S$Tcr0jb)Wz}7`AD{lYYoYL&ozBTe4X%k{4o~jmZ`STJGI(*% zcowgc{mo6cWtl!Ura*I|cx({|`uqEnS%I*@+A+#d)YTr+dB10);`7gcF1F1iIJcTq z1YKBBP*}MTRPn0SnUJK*^?GVu9~D(^{nU%Q;6Uh5%cl4C?yNp7?d|=rm-~)X5sFBf@9okxXxq0pk zcX4i>SbNtFy>gPU_DrNK!Elf1K8urul!_ZQm5t?h@3fZI-m0vz=TfShP5AZIZ9Kuv zCS!huX9N9mo(a9cmg@4|wZw~5NB1Ls2fe*r z{r<2|zNloDpbSj|4kX2{&h>|G%%S>lSWFJvCNPqX86Xi!BunBRORL=;Rz#a!WSiZ4 zEr>)LBJs2$|NjMH;jECLbN?>b=DT?stDv`D!96^P6U~fd1BDeHz&50XF@xC*Hj{Pk SD-j#}MF412x>L1-f6`yZtRx!% literal 1644 zcmbVMeM}Q)7;hnTs3U?y1~|ug__a}b?X^8*VSi3x%o0MYvS>Y-OFJ=&U@36YNC3%J%uMJA z3@`e)gOIUU-nkY-7M-O@k(x*=5A*2oTvj`SX0c>(E<0u_AZWlym@PIr_{Ui{2v~4A zm?_rqHFhPDXGtoi2z{~EU@9&!NpLVO7Ld8541txPF~DWrXmdzia&Veg%IrPI5C}}G z&;@evrBhj&G(bsG1R&-KBTamP06-9)KqN*G0T+Pz0vO^mHxeljNyUg1jsj*bkg-PL zxl$dfp0UM9axjmk?NSJGI-NYHkVjHxNFb3&JRC3_$!J76iflCIinKYxXBkk!VWKQ{ z+Ctg@4~h+74Pr{OiVRwH<~c zI|1nk2U$p&n0n-f&w`oUz1fgQk?}^FN?DkqU>i}=RA?n^bTTRjnJ+xtf=gk6070V@ z5Vb<0R53z=l8>liWwaPp@?mw9c$VXJSRo%3D-elDf(jX=3XWEaRT7C>sS-tt5EUYt z#U|SvG-fjqvvw_v-5IR#jaaFYA~2ey3?#X6wgb}gNSbuyk#<0-M}U=?78_1F9Upkg z^D52%WLc-0+=s~7Aj$h1ogzNPzs||DnyAYgbJZL0S0HV`2Wcn z#5e#S@DMg;Vkl-bvUX$#vshk-l2L`h)my#0Z*^dLV7vQd ziPL$#tLOQe)pxRUv-5WajT_zG-g+-ZsXzDLP~Vb-`NWciBRPs@$R`L*I`VB`1!vCk z^5vg*XPmkB;(DRk`RvwBOZwgVpGtE(<&$-eD}UFeBb<5I&VvJj)7C9hVp~n(0B3Af zrH?waVdvY&7Ed>`Yy7-R$2Js(_1~nQB=+H{qt>B_FZIxnyVvh3?cj&b2Vma&TMbDl(bwH#_UJmuw|jPRN4+2R`BdGwn8Gg;Tx+gAu~XHSpZo%G%P{)qC)T zdiK5`eQc5VJ>PA?I~ph8bMNS!2S2xg+qqunZsQDb4vj+5z z!KI1PWMR*WU+|{%#oXL+uTuQ^cbAO^JFku17;v|Tu{o@u0#xT2e-!9pJ}Ejdj0LdI9^h~mz&sW@ z{11Q>GQcDsfL07(nOI&Q>IHy%u#p+=|1Qtuoy=G*)BCy9Y+?K-GGi?GAF$c~a!N?`k~rsMh;q)7`^F6lS=eTF@KISl0mJr zgU5UPha}8~FLj<)CZU;XG^2QSE_o=0HIz)RvxVWq)MiHtYx_WEg8yDX4D) z>JRMOy{tEW`D9)ax>Yxsm)IL;h572|KA5ve62$Uf^kR{z zk*#NLu-)$f`2G9Bw@AVSdr67O;+UOrB9bIMQAA3MPXzEhCEv^%EF%+som%Rhc}A!r zX6rUi?T=KyuWiu>R9usbWL@`T-?^}Mkjdv5|{I#tDhV>uS@sH`Eg=q`ZgET#8ziyXxL^yk9fcyE%vbm{4ZX5PQ>(#}{1F zJrQI)I`%nl`9zjMN89&qW;}IHk)~sOrGCu{?)%+G3m;3b`xmI@i1t_OOMc6h=Cbs| z>39Cddcj$H?3L+NS^9u%;-Fbi%59gHq>SKQYa{0!LeA((uhj1^|8~=|eCNMj9CNHW zS6(@GiKh&%J(Gk_SCl+*GQWRd7rECTB?XnCp`q>uL15 zo}LXJ<~=L-j)(EDceH0e%BmS^dS>9PQNmoL@!M3PVydsYX_@^j?c`TY+$`Lwe5L zjidn6w2_lH^&_kjtbQ7QI-t%eAX7?~+cIB|t!vG`5YiD9rwJARzPY(#R_*!V$8}~) z>U?%~CulCeR~(cY#hJ4srKcSm+&X)-Je#|4EiL!ZS@X6SEHhCM!#CWjFPhKiy9z|0 z!!h?DsXDp^ovQl=x)J3u#9vPmm|ia|3LB*#KS~=5EjV6G0)Eh=CRJ@JlbNbhYdqMQ z1i$XDZX`^9Uisa~v`^E)PfCIfF2mP1quJW}Jc-6E@OAdGp-^lbX*7LCd#+pS{nfVi z_I8&pT=>Dx{)gCB*Fa&6XhQh>#q;N}BFm)~LF7P-qJly>H=M(f7s(65gIxn3^$*JZ zd?Vve5st(ipwpu_?FkOvvzte!HxQ3B`US*t_lAhYAyM4e?5ZV46uG-lR!+WsNTs`I z(w&+j(l-&YT;f*_|JFA*YeJyPsaD0-_d2XP0Wz-ykZY?mR%1_LC-L7R5eg(CT6D4q zGe99zC@$nRF0St3u4I}UmF7-$B9m!kvRPku{{IN##loF2>HlAFP?q{NCRnzx!zOWz nWUnAu1inJ?b`dEkUJxq^6A6UrNh%RmPXL1a*^C>0{LDW9BYFZJ literal 1668 zcmbVNX;2eq7>-&GXgEaWrd?ODVo5fekPV3-B)f?L3@{{)QLDp}EU<=TW3rHdlqw#0 z)Rs~Ov9%&lW$I9E1)*A@L$M5MMNn(Gs-ok8N1$lMT1z(wwm%$ybZ7TF-{*Pn*=>pO zi``uPU0E!aTZ|G*VrEb0H*E?tzCAR8Gm{@J*V1Y#mA2s)f+f{cDFhH>#M6i*0@r7+ zZ6zXDtVyY)MoVi|OC&nV$i80 z;0vR)szd;#ECe9r3OG6*3>|N*nl?)y$Y!%~Z2~T3NrPaqSnTBB^Er$J$C_=TaXZIkoiomW5mudrG}9zy z0-TI^3YAGmf{djvTriqds+Ytj>sX+ekU@6b48dF;WHdVc8kM%vNyNWqyi(e#$u<*E z5@Dq>EjlJ2sdL7`Ozd8-$SKHJBT2B3Oj7U+jM8Nq2@@TIMS{!?S5N9Cd>BTsa2cYI zicvYUk)b?9!AHY|e3Zvmh=k)Dufk&CazrRogo!Z$48wB1R4(OVLKKrJU=hk(jALU= zRvI_yh;h9nqjwA|cr8|fS_qt`EE695j)d(q2#m&8LcT&j0`qxc;R2K|7GqAorKmt8mm?@96-WgN86Oiy4ZMxBY>$6`&aiNT~AdslAJDy16tZ5eTNu1&LE z+^3Se8V)Fn_6S45s%N@nU2oax(>twv*X8P7%yakqfYx8OB{8{|?G;H`M$_;{s{t&PED;gs_>Y)Ujc*suO|7_sku&?*j)$8w7#HHN59CumNEh;$D5L>_U z;L)108~XyZRY9LR_8-kx&x=jnb_g6=rHw9q_RWc)5URQLX4H*2v$vEhPQS~W+!@;I zy7}|Mu=;APD!TayE_fc4a4dg~M`84y*e?G;DWWq5Ex6%5Db}G;50=N02Dj5Y8~eU> zd8g~MdhcV(UPL}r+1|c<3RO3CdvwQM4;?XohF?JZ(!u_7K;XVZwc$Z4FTJO{Qi|T9VfoZiIg_MYXaC}a0DxU)Sb2IQ$+3cArrKm2T1;oeqjoTyp1 zak8U>H=!bl8#p~+V6&v|cwy6?Ke}?^B2(pr6J752gTL&sZEs8dP!`;mu{Sub$31Fy ziLQvV{;!_gH3M7YPn8!07gi$L5}nexqba`osLw#bAe=0W*zsmZ9@4HO%K~lZ>mIi~ zs-D|l8K{}-_e5WDWVL4DrN=*2%x;7vALCu$4QD*<;AH(-JT&EG!?FWspBA?rzJ34R e_FhY#E01M3$Du_Z-E(vP7h>e`*w?b;{C@yoC45u> diff --git a/toxygen/smileys/default/D83DDE3C.png b/toxygen/smileys/default/D83DDE3C.png index f924c45f29dc9873f1d3a9f2cb76ea9535cdcc94..cb088d106287c6e8997b22252620cc22327dc94f 100644 GIT binary patch literal 1696 zcmZ{ldr;Fy5XUzO6oCWD^MPi z@DPZ#psnI43dl<-lB$(}IPw-1Din}skVk?d;rh#TrvBBL&)v@M+;?_wXXh@3$=F5G zG1dV965Z2-h1nJAwTyu6n{1c+U`FFG)r$&HmZ`hQ)x>;qkSEIvAkG3H=^VfU7D^fc zI8Ff=;{do`0x*h{-1l|^z|}GLuxay|r1=b8WrpU9g5|S6g4+F;3|%b#AJ~D+)DW;(&n!7=RuZ;pNG3s3)o7{= zO_ihfH8(=ZpXwOsVF2nph2(dTqP_1yYWDZgTX6)9U+Jp|5ii+0^SaJ437DGJ? z07pXjo8}i8metC)itP?L;^Qm(pv$0&EEE!a-8m0+u=MeHBWpP&)qTj5~qi0 zaDm_L@(}kE{;I+pEg{{DrZudkr(=%yJudp`LFU-Qz+8OR@f7-bhAzy}>iflKA-tf})O2_hy%B{0$gO5nT&hoq6 zdL_4CzY5j=!r7GeD%dAy9mp4-5Hx_o>iz4-`W6^(aY&iDtRF+dquHylY0qgrb zw>Co$T%iSj_QpFq{3VRmboV8^WzmQp*x(uIuc4 zEs|yiv@eWT9MyVf+|^j&Lv6~LzjIlfa%Pa@FtWvQR=*o>{aa$!kabz2W#yw>n>W{+ zcO|R}>K<0TXpr`fxL?hx3KO^RWfKX8ZUH}Alxqvw&g~Irudg*0gobxtT5Y9U8{H|T zT3H#aw|l=}BGa5PziVvb(i7(4{AHAIG^MBCN zCJ!-R=32_mTrj#tm&I{r1`@5PAK8R=*;J!e<(}OqyRwEWCJhaLJ?!bGOOa+IZq3c! zu`#iFQmZ~^%0T|O`swDpX_K#p;@TuF%90PF`UD-J4f|mHMdJtJ(Z(fPt3>#Fe`=l; zy+%Fc^K=g?dv$vdhr@9S3Ny{>gHwP>vZfV3lb-(e4;}`Mx#rR?V zs=7VIv$=j<+p1!e%9yHcU4DA*#6L0^&$pCD$k+c|y5&&c4pU-r{pls#8LCveu?V+x zx;~IN!0p%1^7d|R_4dxv@3%7`mPJi#Yp0~FkbG&>OwO0!HA+H4My;&YtjQMZFp=y;^KjuFd++1bRA;OO z=0cjkFqkXkxduh^Fa!1!dwW}ov#p&Y+m7Pu;N1DBH<}03U_l^{Ob_QC=COF(;Mgb`5BoL%=ro2$sXOQFKSRb8 A^#A|> literal 1654 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#mT2ff;%Mw>?B-;o1l5~DPMGNf9itCQ97u@;CIn1@ASOKN13B=_lbQ$2F-5?v z;eS)}76Sv*GEW!BkcwMNCdGS*1d1Hne^>H*|GQuM*Mk&eb*DP5;hCbrp%mfStx+?% zWsS}o+nVzlE7rE?EN;IcZmLt$v87!>+mP4O$n;{EqVCz7XG>zkkI&s_%_pe1x8&jR zImPd6pVvLVW7xXaPhxeO$t3P$<%>(_-BEn*F_~k=LRZI6o!86Wv~7?+5vcr6UFlka zby@6)WZ{(L*d6(X_4fHgyB42u`@ihO)X1eS zWp6*OINfXzxk9tf_|5YBw*({4w0ud9{SlR}Ow;QEp z>s?!EN6&&A`^E>-s*wd(&HmD=jBHo*OXrpH}4M0)}-u z9+}mFfqQuWy-TkMitoMjN-AZ#$ZMuQwtFk8_6xe}#Wh#QYYIp*@7u>*%zsLa_ol~5 z_ON}0$z1Oa>Au($w&1k5!|8XDdk?72XS$&kaN71yjc1_H22K7mXOp-)nypw@IBr^? zzGd6CSNErBYfAr`AgdA?!FSO~qEWo;#HIJgueEu9+}pgW&32dl>#YZ#{&bmI$8opA zK;mkP03iDnRmqqDQ^sPUqT&?`EMpdyH+Q z=U&`hsq$O+an+=$7nan@%=Fy(+B5lY!+XZ7LF-i63XJPktz$cPWcO>`9;Sy3cG{lj z>@NRa*0Soy)1Un-6=SyTD6mlLbv2qXG48bI@lR6cR1Zb^TxYI%c{TG~kmKx&BM_0K?xK3fbG! Rm>+;@0#8>zmvv4FO#oYsZax42 diff --git a/toxygen/smileys/default/D83DDE3D.png b/toxygen/smileys/default/D83DDE3D.png index bf8c96266903f057b2d751ff76a4ab007252717d..ca2a4cca35def0c3e16abb85467a0ba217c9e96f 100644 GIT binary patch literal 1782 zcmZ{l2~d++5`Z5d!$5v6he5)iL`E4Mj1_{MVpdofh>#S9TjU53j*$pb0pwg@L;)oz z3I&KLW79-$rLx7+RYH+E?FuUEQy{x?fel>Kr$! zle(I|8UR3@?Cd~8rIvhut%RQY@S5(Z*cNU_u>*jUkJ+RvqdG3cnMMI1$ryn2a{z3h zru07mh$8?n69j-wF#tPa${roJ1wdiV&DFzkJzssDje!tK*~)K9HCN69x#cg|81(Aj zpbq~bPf02VpsZbzBfThREfS&6w0>=-$4a+&-V6C)>#>il#e1XL{0qlqu@GztqTzB6oauz~jT@Cj z_|#9@;@Xma_yP&MC}Y4yE?nfm#YVW)@}ew^n{Kn-d zR>9c{xYYi(A*m_N8V;tysk?CLg;Z3Ne{xp@9u%D10VlHI)E)GVP?QHnS<@|1^TJZu zT-PeU1^&ic?c&Z0OXgc+py(WI^Md>U*q01NDR3xix%vd3>bZQ&4~o)YZzAmShwWaH z3WL>~MzHY^eC_~yY~X7HCb-5}17qNUj0+;Ct9*0ej8hJ1l$$?!|!a>(tFc03Yf>z9H-bvZ1hx6jasS zM4cflpK7*5JHvuSqsp5IT@jIC`_?z9KX9evYgT53SNr-`SES2J^$+~KzaJkp3NnAh zHC1pZel_=b^K%i&*H|(2XC@k^5bWaQpcsZp2}KnZ7MbFxGNq!Swijuf82<_Y#ndDZ z9~OwN?N~pl#0(R-3r|;C~X^WNJ z2sz-q{?md7Z|^WBKCK%{^4g~;bEl8^6%}`ibzQ-}_jSgNgt*+KW~WEk z+I(zac(?xUv?to~@@Pv};Njl}rcU9jk@`6-DH}Oz2*?JoDy$ zyzP2&c70bA_B_Uw_G-G%DJgefko(~1$7`i8-H1<$*!SOOvXa;pr&Ucxf8wi72*Ls$ zI_s>*sT_5P%#k%1i7xAE-GxWg&ZT=*Y3r}t9k`^{A+F6QyET;WJ8*LdlRT%4nKL=D zrM&`1#+cg=xXt-oq5j=9OSQ7~Z0CoKxuFi;ZEwk(hZavF3;z@vY-IhO^~mMBFA>G* zxm|yojV*lNeszdGskS~XIbQKh)RbtbZ`E?x-^Z)3h$#`J5|aP9?KP<;CrWWAex!~k z#C_r@pj5opNft@}<^(C#03+6q}KvJU2)=s&N!ckv`6gFs}Y{!a)7Gr|{{g z+%^Z2m+7!`ePFO>9s1Lhh86${pgDT?u}ZtUWpgv{XIO zSrglxKi>G38MpXdlb-#Jz+bj$S?e^j$!g>Dj09SvL2)ZT@EGoe{!%M8T)n`6yg&R# zEoq3v*|UnMS{ya&U_&j#Yoy)(;LJO<3A|Mk#KRvCyp!2FPi)+>zCM3P?6R@Ezkzc1 z(Px}zeE6-Ke{vnt6nX=5^Tj$XT(nO$$PRWMdi^0mK|y9A;Xoi5il7tiR@UvGplw8P6=-wW8)hXXU*bQnV~H>*@}CKtpKl z`I;YFF& z8K?k60+EO(SmI3&d6*Jx%*<`f519}MHUt7g;~DS20+`XEkztAdKS0MlRX&rj@(k|L oVXOptECbkuMh7!+WF|eFL1WNE6VC`3=r96+>_~N}w-3tv7ovV&r2qf` literal 1716 zcmbVNX;2eq7>-gEh+WU@(|Ug-lJ)ZuWPw6aAg@dIY1VnUp-5iX^pEGG-zeQk;w>0Hq#_Bh&wt7 z1Th#6T3u8$6|Gt-){uHOW|v`G^hO%ZU<7?_F=Cnof&yZRIGsTP4m6zx0Ua&@R|r&G zl@TT4b&6CI5t$kqrAbZDh;Z=h#XyinOcUq{3Iiq=77Mc2$diK z-y0RJ3I|ZqL;wOd&tJoZAOJzw5MO{G&{qJ=gqKwoe=?Md+lu1DPhK=iRF$_V7Oc;!W zNJXfe-UOpuBm_o<0vP4OApwGMj*nsGD8lE9Wc1)c5G04CG67#G=gH(UC_v6bk#Vfj zV5TsGh8WkYqxFtqd7s3JQ4@htq$!Fd6UQqcJf5UTb3ADT&`1PWxI$;Z$z-#iJw5Ne zl@TW0cLW|{BK5#%e8swt=tm%!%NO!cSR|6!{g$G<0J$7NWm2A$7ZMDEV_5wE#Ee55 z!?72~e~M-Nh^|2U@W-KZ99R$?fXpOIYuZ!1Jw8eRO8Ao0^6A z1y7<$FXMmX>ARDN_5sP{?ufw89*Luhwcxy1eOCwnKJO^Q(%Ujlx;6 z*E05M1B0A>4_1K3kO=TvTG#I8IbA=`&OPFDeshVpr}u;Do(_rO9;%6l0DTyB|0&d- z>+0$42)_1Mxhx{PJ}7t`^ z52c)4I3?n?$!R|^LtL^5;vLOA^XupQ<4A%x)nho{-46e;y{^l6!o#ue`VymSZV$_G zPVn(=W8BdkkDJqq>QRL@rS%D;>RDkiknfJBZ)Elk`B~pG{Z<~XU*+btZsQp^c(7H) z+f&dIFy;HzjG(n{`~He~G}KUB_V|>uIs{fH`envWSgyUbD!SgSaJu+m9SdJ@X`Azn z`7Y`gnWD*xrS7Y{!=#;$ZpYPKuvnY(GHsiq@hQ)S<)x=sS#O*Zw%0nXxmR`Hdc)~# zL^TdrOLke0b=2I=+Tpd|1Io={g+6pFVFWMgW^93a#a)e;XPAFzp=R2C{F|DUu{V3w z7j3sD+$K)s4cBA6w(I4V;`s7k+^|ix#W7_+P3?|uCJo1XbV%w zdiy)l_#EHyyl3D>r%zR5^--TT6m7gB+4`8ns>ok-V~F^ diff --git a/toxygen/smileys/default/D83DDE3E.png b/toxygen/smileys/default/D83DDE3E.png index e02931ced8461071969d686a2b314b6d58d22bb8..840ced03f220f3f22bf2aacad250447c3eee5ada 100644 GIT binary patch literal 1727 zcmZ{ldoYx19LIlaMaQn#9I+@%?czw*{gTR-ZLLdcUE1xUHoGf}EVjuap^H@ND0PTY zQWPSj?V?g=6>aG#&XLlpB$rO7bX9H7d3Ve-(;q$aeV^Yv@67k}{mt)vX5QSTOIZ4P zW_kdi&-VA@VAW9bOvYoot%E@*RwuAdkR9}aS;RklYxc`(Qr{jM8uUG z2D%W8RB7nRW^A_T$AT#Vu_OE5uFu>Tlz_GZb|(GkL<4x9bk%&a1hY zqUBJ;}|q<|<|{a-Y+X zdZ4wKU-ljva|4?nii-npFQZV*n@F;@h7gNaMeQupo*DHE_ZhDe-;X3D&hqdH=E#hm z>`mVlq!q@vd?xr9CyMw`&;Eta+m__3=7}M4>U5Lyo_{D??~a-BS}Gf=dQZ4klHSR668{zatrwcgGVd5Mwa%X*_HlLRYSUiw}KZQY~IbXg<8 zN@}7W?Jx6VCB7z;I;f+3qwHT@GF~Sw@433Wmp?q^qw!e#-D90iGL`G(GvaNAbyg48 zALyt$^>Q=GI`&n?QlkOeir<3CA)&63%+dMfHCDVH>SBE}L4eWCX-uaP!vWIgozOH-7BnzO#*7IMqwr&WcTVI$}QdH7i}~kPBUH>Fm3x z&-hMfkEh!%%X0yh%KW7g|2*k_i-VritNMwyk47HL3tMRe<30XRW>oLCX+O@u(KKh4 zlHI;rypD$>JzP-IXYYKfOQ|plC!&37Rr!bBGa2~rocg%O=i2@`scEIp zeIj*#={w@OT&@oDsYy7Z^}x1b!+d|YiwB;+{^n4&Ky0upYQyHIO6+2OV*4?|Oz%gn z1suc5Mu$(bp%;@T`?Z#=R!~><`NrwcXf(FnCWDX|#{^4}NJP__HjT_%vjAnb9PKwe z3Iu$0N&0$jiXWFN5ENuvyV&E4IZ~pj;zp~DZS@(aGu1R2)h5d~AV7TBY%V@0HC0Dj zAQA~4ib6i$56R69h3L+0PBbq-Y(?K-MILCWxe)_(?)w h2olx_DeQQDw2&j@3uKAyLhP>sz-BJ-t6jJ@`+rCg0*wFw literal 1583 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 literal 1679 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLG8yO;GeeeCv{0lv$RV;#QQOs{jsPt4u8Rn;9Azm{=G)8(CVqfc)*~ zY;5l8YUb?XXzXb0=47M<)tf?2nCSx@qYp|PNQng|1WbV-COqi_Iq=Mrng`4=MZl~f zQO^v_r%d}jT^vIyZcUjK?GX|v()Pdj*%{yCZkH#@`5ZqpYs!k8t~jQPa$I{H?k(Wp z-4e9R!O_KzmCdUn`P3x^u7xJmC%9Hk*DG;dyh(G7VYW!{m18rrp2@hUKil$c=0c%Y z3wiI~eEp|-?bhq}vWtU?d6bgn3#YB7tkPx+dWy11s-+ooN9%+YX$JXk+0%V*Y*O z#vLrFyI)UPwR`p^v6%Xs52t_G+xhNW(T)6zYS&}}*31<4cf9s~#qzt48Q*IcWafG` z*u-pBILLH&gWpP@T{S#RQxy}R&HMM#gKfzIxla$wju!rE6I=FWZDRF{Z~C(T{1csw zteaE<_+vlZnw%x;Zg8IagzpkN?gqP4ed=$Oz0TbFW49`==aj>bAj!H{Jw2K4HuE-z z@0mL7$y~D?M-+Cn|1L~8?E0tp55pao2~v;e9#P2uwD6qRjlKn5ywB&bu^;CaSfpOi z*cTHk*>=;Xxobi1<7rIYstrm07lPfsyIgqwzQm)#&NXXG$F|}F;;ZVs{Z=t;_MgwG z{vj}&*}1`_&#vpOpxrU)$yv**Z~NPYg*VR*{k~kTed`kqfvqhzbG@#*HqBIe5xZwC zyE&gv*M{mxC36|$W}n&n&LC{U`Q|O_@89TsDs=QnmC{s$KX#T8+v*l|SD4I6@L6a% qTQ$JuT1e#w`{V~TYmEMjBrsIw>vzr0xs?p6B0OFFT-G@yGywoQOnp)S diff --git a/toxygen/smileys/default/D83DDE40.png b/toxygen/smileys/default/D83DDE40.png index 9a72002d22fec0687c56c8d48e073fe9fba4a8b8..01df29d9e07646425d50d74b82367c78e88bb750 100644 GIT binary patch literal 1792 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 P8-FwYK7sriUQE_MVI8y3 diff --git a/toxygen/smileys/default/D83DDE45.png b/toxygen/smileys/default/D83DDE45.png index 503fd3225c7a2fcc07f6cf287b8b57bef697aa2d..f9da41a5a88857a4861fde0d41d99b97e053fd01 100644 GIT binary patch delta 1717 zcmZ{iX;jkr8pr?QhKQMtiROk2m8l81HQ0g*DViH#U@obIj$T|!NX-hhIxdr`xs=Y7 zsd<&gsVR!wsXEOp7Z-|YrWGdG~_A+ z0C2!gn*tV<7&l)x0Qgsd=J!Zda7Hq`$i4t@aUTF=Tmyh_;3`83030*`EJpwU?hXJT zl74LoJOThp@_>_6q5=$Y?>S4|zk_A2^FLzDv_T?`;9T&XHh_Up^`L956^fZoUmZ0t zFVzl7?!3_3K;!i(G2#Gev#F{!eS?d)!8dbWQa;>xy4OOon(5Y}SzUyjQbre~9-13&AQ6Ux)Z>W-N zi;(p^5|mud#vlAiUpc{0B@4By$iWaiCPq*Bd2aHH+R}-(dWCdIAsZfVtN$%G^`etS zDiTW5R2pjjJ&MBhp6YM^?{xbYQHS97`(e8w!ElJA=c)AFxNKaYSQ=1B2V~IbW!nJcv-1n!dKc}~Eq_)!a_9}iJ*tsfP zifWKV@YMP|pT{+jJC?1(VQ};Eqdnxv@lv^$3 zR>|U?fWJ?-ceMp=f@PEaKu0OD#DCTsjD`UKK+-Q# z>1=Xj3Nq<@VstEviDaiGF_Ee9NdRy-zch%%pFzUk{i*jOWFn{FL&D&ppy0(XX<4Sm z&x^lL1*ut8)y-$hmbT3{?J6H~OBW6ujng|5`h8KqUmzOlrp?x3R(CG6a-w@_lO^<= z*y3utJQ(Y@#}AyEI!+W%v0XSfk`83~4vsRHEVk?UL=J1SEaGKS-?Q1v+9VScq&jfb zFq=hL!q- z;XCe}QS@8%OoVQzpZ)Ps20_V)5wt~#JLyp_8xLqBIzJd=9BET;NHx<@FGK`yTqbsX zY0}8Zafj^m67u>6QKp0%f_P<{Uw_Sdqu@R1G=gQpnw{Eq22%LRAT z=i~`LL}o^%d*Jq9N>6eDi2$1YQS|I;vc|Io5f90h?7SW>&dpqi7#j=g?adB@QjO(} zhiJJwt~?DN+Q0p87kpz?goP|sWnOLlr>2I_bo4LmAd|s+zsv>q#Ckht-V{G1mttC~ z*AB)}Y8ptvy{MQy#%K3h3|p#&S6VjXUqaLjEFh0DypC?mzG-f5h16yuIdpfa(jTMH z8uxvk5qo<{UL@~}`oqTxCtcQ+=r=nyxxGnc|8aKo+?b>9o838-??-d*M2%+`-3ab` z+o_U*pO0%y)0k%S@_|`9L}VR= zIBqqta)X^J_mLtVBl5CGN93!ZJ=aMDH>$xHBLa-5=tw4GX>K7ZDg#KcWc)yJNY?Z* z4M$hL?w=M;48QLj=_@z0Mb3$r7gy$Gl8+y3pj`dk>l@loIBb`OmL@_?lR!8@?0F>= z?6=yN3p7_piTkX1u`=JMD@K32C8rHq~>sH$QLr-#N^S;p6Cd4+_^O zeYAu(#(v5+G`})3R(XuE*dR)VZ{EM>@T+U0JnK3|f5_qgul-UNu7XW~- z<@V89mK_V;k75()?6Z+naKbrZY|v;N8tvk|Z2EtK g_{6iZ$mq1cSDg3baf&c&j literal 1783 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLGjMjE=kNwPKDW<3A7ig*BP%~E9aur#FG4?ko^1{SPBTp$S=t+&d4uN za1J(8&nKn^_fq~-y0Oc5|^ zn154<2j)0+PZ!6Kid#9su|C3)61JCL@}J?ktm(Ya)!TES#t-)^Id^*vk7bx#WKT}I zqi!<0L(OG#V8G+%j|U|dOUqx$xVHbu-4%fs)$4_tI;H9pBO^)-IYgp0yG33_WGsAp z=E{209F>a6lXn__o?HCB{M}9;tGUOn+z8e){<1>(jq($rSsbf5gdUncWP14MX}&Q1797XPL?^9J2yy3NO1ZI&3Z4O_;iD*X6*meuJk#xR6=vVud!h)RND9d z2d7t0*mkZB(UlwAm^)JrESj)gL1gY4)l*9|nK*sFFD(_V|80Lr=d{I-=b~S>ZGG(d zIG1JDuEaD+5e}I(O7mB)o%8TuT6JEO8<(_nU2Ekw_H#;-Q$OzwIk@2USL>3kr3?0c zRQvKlq3Za=eWLSDY|HO3i_dVqKRGDq?;4*M8f*S>hQGhl?5NWu)vL77<=~}1j^66m ztweXvfyDFe29`bl z=k06Iun4%lLHBv_hTl_@PcQtnrZY-uE?K#D)+XNq z54jh++P?p@o0Glw&+nTKug_&Ng{qaE4XO;geDCS+z&Jy3nd29~iT1RuU|r8WYuTdp z%cYOY$se^pHDSH>(ycziAJQf+s)Zj#lT7zuMcI znK@POT>hWL8nbU^`NOwupYw#X_nbRXsPK!H!9eqpE&S>aBg+2^))qG&Y^w x81c>M=Chd-TxLXhI4w#^` z5>;?_!C_)lB>(_hT{)k-@?O@atT$ce7spW55L9)<~rd z`|ysZuGbfxEB~{7-JJHNx46hwZw24L_hMw2DAu8L>d^Xq9;OW_?F^(6->Lm+4JX#lIrN)6VQiB^`#(3dZ_I9O?=R&zq5 z)^cW%1Y}ifeaUc-N9MPZ^RF#G;hNujt6OT4O=!UmLV;vf?goR_yP! z#gF)bi=pKpLODo_3te7f0-ZP-KzGN69wh- zzS5$CyB(`f=GNwTU}YSvj<3y)3P#)gWW@Mo@!&5mSQ-@!bLLreKUr~5{0OAqi&NEM zI-7yJH{K1`gXcW(V)Wf`1GvYDQ_};v_d&sIm5Gnn9%vvXuqTroX?r~4w8bEk1`ohN zl_owQX9}e9K;`0m5Zg;y)M>ZqOetlvxAdQ$k{K42AIJvm$KT-*aD5RJ&W;s?&9P|@ zD`JN-@!;kQP{!Yi`{O!uS}`uxErx zv*}+={r>sew{v)Pcyp86je9t~z4Ll!`~J77T23_SM?;anKlLv$GR=(Dhp7fapU8P< zd$b4;HGV222%4iS2JsRhyGPqH6oVE@qA7_u9~w3|UV}o7CWc26G-wGFf(D&TfuNuA ziaqg#Y^3zp&jwU+PJQx0VlKcI-#=($Y+^QU3P-kGHbfRSm-rPL^uyl`ZC+fM+4C$R z@zaHz)+a9h4Jxd1JU0(9eLR}`So=y}MLN3DQ_@>GAS3v3vtsxkmd6cu3@fsiWHKlC z=#e6e#-z`qO%}s;sv<h573!N3FtsbS(%acubQ{CwL%OhL) zMR?;t>&&Q&*auvIQbTT%Cf>&tgld8lfRLgS9q0g zaLn-6+>LUnw_`>#tm}$oS)&Eh^N5c_C*FE%r?2ou}Uv05e22t8x0` zzHW7vN>_Miqn$;9XHk*p&i$8~F>*)5YT=ZepGCM#(;t#jBUK&hHS1J%rv~&bQLLNd zC{ssOlH(5FJeL3#5@p z%3#ZMmxu1!;raO;U3v+-ZdJEKt74ahW}mA&J$Tw*OMAL4KekyVG2ba$-%dIiPG&5N zZtd&-ghdXQ9kgzhB2ITY-;hkS&r+frdmY)`c-5okVx1Pnahc@6DCy8R^DKV{@lnKw zIUoS}A}UXA=`%*2leBH#OmWzZ^z73Y>f~1%N7t%z&8o4pzem;ZJzEfkDeI^}E zpD&56lm1LxU7q`Hi`p7Njc>`q3g)vBLw^o)UbE;)7^8vxUQ%YE&ezmwItcsacO~B* zL<@Rl;$Ly^`kwiekbiBx=w)PARrZf-k8h`P_ci9I7usDkJ+uIH1bcsdo1M}4&h(Hl zt$Q8OD4f!4NDw5dOC&2OkcqmI`ayHSvxJogf``Ew@9%7E$Vlhqy1Gol6L(NaY5`Kt z`;8)0@m7}*C)KKhdBG#>^%Dj)?F>=BdV*n(y#rG>WP!hI7k1Og_( zvlGWKPjhi4`zN1m))+}0!Il&br+Cz_5A8`9Ja9}$(r#12xzsqYl`3cyjF+@L3JMK+*zDg<99y`! zZsFqFK8nwghe(wkKXe3G)e7HNhlcT?g$C0ImLXI^um_C*nIcV1jgc0{CgwO3q@~$0 tOLGe&B+?RzEOU9*^FI!9bZ9s+;r|=f{y2Uo=%D+wbM8E(5 literal 1858 zcmbVLX;c$e6pjtzLZmK$2v)`*ivr2akOUHp9Fj>S+8~=ksR$t%NQ}vZ$sh?@0Yy=) zOQkMF5ET$lEmlRgBM2&0@l-@qswnP~Y86B(s0eg|VEe=4kItEy_wIZ5`|iFeIB5Qa z@eboD6v_liAQDQh6V2aPOY-m3Jt!lWX@n?}2*cuuWEqZ90u)#r3P?1v1T++tDU68? z=v)eg8m|hEBqF6>^W>O@Av5bR3>qDYrcmbk8+0qVq(gBr%Pmf?r zL8(rNDpi3gI2x7`6fRFu%ee}=|2$x>fkzT(P(lV6G%K`vo`Fvv;^mQh^D&bS45<)m zKK+AJkEmGpsl?0*VF`3E9$&6%g28JgvAug9|=73=@Qo~Dc)Dkj-msamQ!hoQ9Ij+(X zDohKQ8D();62YgFo_+{HqmxQM5^MFti6T?RG{|&JhygM+8gpDj+Ik`s{nw38YU{&| zI+PiT>aiqTPSzvdc?3-6?(>GsiljHZMYxJAifjdf$&)mwmXIKPI(frTs1!UHf;fn; zfFlmz3Pq$K5P}>rEc9i=LJ$`FutzvPh2=t^AOJ+f2#f?kkO=1b2-&_|AC?eg3)mtb zcmyla>Is=vj*i$>k#>i%qR(P^LL8M57#@ycD@HmXScwssUWw@dVHgK+k5Fk9Sh9Y$ zxjY}DMNnL|5><$COalz%m#6xKeP4(R3PeKCSBSvoxVb*Ch$|FxM3A=+1Pc)QFjnz@ za>gW`VVaxcKg}|7M0TKg`?2*&;p6b2T5@7=ax^R-b%&D^hf9!va6_viJ5}k3+Fz*c zY0gjrqI% zmc^v6s_kF~%?q)-R)m&n)~`;y@psow+hu38PH1q)Geb@HtD2Oa{TBx+p1yxv!&tVx z(bFaLOlRMihNcQrm4|a;R}fvgvLR_y!y|r6Ym4A~s)J*H%vv9(_s@4`5&gwS{;;Jj zd+v)=3G*_PM#`HslV zCmGo$z)y2HZd^5E%#4?T_ItTjSOG1jez2-xk7vTbbZtU@=^1rQZVtuGQy%}8VI!Si z@;Gh&u+i)%}3{ zKCONOCEk?1XFxwmy}Pdx4z`-~@sG`!Xu;Nbp($hJN=R5Yh>;hr`jD)*J$7=r! zX-V46AEr)R)de0ZZU^~BYHDu3$P2)SN8O%S=e6 zQSarAaZU*dUHsVA*0U`0*S52zUpWC~MNv`Nla|-lkdEQI@$~FXU|DIQ9Koi zsN))S7Ka(OUiTW8PcE7q+;EF_sZQ}z^&uwJqIdMcAKfnAnVwm(U0kv*7M0m~beCS= z@Z|QstaVNC9pjLaQ&-egtn9_`HTPXWRonM5G})WX&1omBU1}_Q{JsP#|1-&?^$myd{B%&bXs33OjKli%e(*OVf diff --git a/toxygen/smileys/default/D83DDE47.png b/toxygen/smileys/default/D83DDE47.png index 355c9d286a4eb87997f100e7045d8faf42858be6..a4918205c90d8c90bc02f109cfc1381605b89748 100644 GIT binary patch literal 1661 zcmbuAdpHw%7{`Cuj8Rz^#E4wdilz+7ku5Wm>&)EQu(2{@Ep5usmRyqDy08?HOA@89 zaH1QX(nUPE9-Lfqx-7b=4mNv;~pM@30~o0+fjkqaaW>y8DWAxG^m8dRPeM7WUL0Z23jAVmbgcllGw3;+kw z0L)SWusaKYR-9PkZ4ZD#jh7o4FO$gtFZchj_UF*}S0FW4%h{m03Z@K2sKLT^ZHmKN z0OpytM1R1@UnU}FnXpU{BwU6f_yXfj(k1u7hLP@5EdJqUCjN}4W zh4BIjk1I2qFQoT0oRj^2|LKk5-zqZ)AKni4aUFVkf4aMVx~qQZ$=#vuHjs8JJt_I6 zIAvy}Z}!=*vR5s#SCW_F)a;a_AiW8kXa_}oASk<-;X9DSoU6}xQpTHa$QsDme?Hm| zgcpFa5pbr@wq@S++BhnqEWk6Ui0Urczn9|Xhm0<8svh2UYYr6lg4i;U(K&K0cUDpy zz{>>IvA~8!N{r_bQW)88*GE-nTys?}FkNn?H(9<2Q|pEZE^DEBLb z?kTl8zL3R1Sck9X-fP}Jn5ObC># zO$`F)Zm)5n4dQ)QWtXwzx|eE0#wbh)Er>Q59HrmrPGw%{I?73#&apYJ#Xi@d#LwGV7iD@v)~;k=q(w+RieQ_msWdEEs+Ffu zN)a&!wqt~bJo|puMh$D^J!i_UhKHe$j=|@%P2x3ihi_&`6|Y~tBu*2%t_6wlggZ+! zgNDt`{m%w6G$ffze5qI2CEJA)D${wN>s7It>v!{7P;Y;fCHPA^lSHlaH=?<$(c{Vr zlx&R&Or$RBMF6I-jg5mzvYom-m#F0YOK(WIiYPi^IhBx zoimB^HWJ+_--vvmA6ptoBwsAAaQu2e{rJ@3CC{#KMO7Hp^|eO1$k;sn3cG7w?HjPy zOMN3drJyrg7k%=<*h@3PE7W-p0i~Jg+Y9Zs{?FJLCzC<~)Z4 z&vX~bahuvko^teagrYjnq8md3l*3i_1;qig(-sac7)k!5bjiFlIc7MM$yL3sD*v5h zL?h6qiyg1|$-rVqG8z2;HjE(yH06f%%N2as4DKNshYqlj>~OjPkwuH5ljyWa Uetb7wz7qf-;ys+|aMYuJ12xF99RL6T literal 1659 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 diff --git a/toxygen/smileys/default/D83DDE49.png b/toxygen/smileys/default/D83DDE49.png index 320c7fa07dae7a82507e76ad1807d112d3b0117f..e63475f8dc1db0a12e6cbdb569011bb041108b17 100644 GIT binary patch delta 1688 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 literal 1750 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uY!$Zr%L;si9buBuP(&nJlVfE-xZxgqD z%Y+=ZJFgyuwMl6?;%9SwDS7PW%U3hERo_~->fNgF zsnMouc{DBVRNuFLekXiS`Oca8e2?qryY29?aJ_LmK3XA0-AC}+wn?t`9NVs?bJ(xW z3Y!?pdNOxq#>JO67E7PKma+Nf4wt7aiSPC@CLav@=(926S%`0|g!sY2KWdt4N}5Xr z`qiIief`R#JGVMrNv!pwG0$H4@Vo50W}Q4-q#6<07ICIIPer+T*UJN?mZwxscy(s) zTX|M2N&Y(P5%r_%lIv$m^t=pmZe2BfqJEZ&*u|`^7qhk~HP@`D?|9@Ux?{aA6$>zX46>sZA>VLjsS@Kr%i_GjE zu?ZWzc2^vKXK{9WX~r(G&c~e=%a$)!e(^J=rMz^<^wXLrBrLbwQgd3Haq}Cm|0nz8 zdEAZS4D*ifH{W+`{`P+d`S1VfVrFICAN!NVS^jPRHy1~T2fGsF9Bx%^%)UK&p75Nw zwIP?B4_i;1G%4ceZ%)5=(hg|}9C6p%g5-|8y?-(%Uh-`j`_-ddF~{?pw;bB2Jn8I# zpu2sh@5*x4>@f)p+9Y9T!k4|b?`@P^&n?e=?dke)2V+((2wHg{>27}PO55|*U-&rg zuaEwAEGboR=ax1XtKM7P&rirp9E>(PIfY%xxs#K_QgOP;btm2PGyG+1CL3)0vrIDM zr_iM;#vr%DpB}qw^wo0sO%uG%!^8L9^#te6THcED%Y}22`U>v7wluJlaF*Df%4@Kz z`oi8|@gI%ypU!>VIWg~ba9d)^$1fjkmvmb%;NwaD`7rj!m;XhX>@yxGJbcN#>ljb^ ztlsZZ?@lfMZ5}^+VQcV@JpogA_o`RV3KLLN7x=-m=jeugVc)ZooMYDJ%{U^xa%Of~ zioloUg|9AMEbutjX6e~i-4VOsOUnL>zJl-knf_S+t@`$;yuOvW^*@UQ!|N#(ZYF7o Q{-B!1)78&qol`;+0Eoz_nE(I) diff --git a/toxygen/smileys/default/D83DDE4B.png b/toxygen/smileys/default/D83DDE4B.png index e9f865578b0ca16124d10ee3d6c48f0e916efe5c..90e1e1c5921d017130f99a2be5d6c762ffeebc00 100644 GIT binary patch delta 1847 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 kpU6uQrVH8|ZxJZC3s)0wMi==_bo{j diff --git a/toxygen/smileys/default/D83DDE4C.png b/toxygen/smileys/default/D83DDE4C.png index ccb621a3c5a26ecc4a514f4e777f7f82c6c932c9..a6e70814b41c0b35d25d67edfb29e39263802422 100644 GIT binary patch literal 1557 zcmb_cdo|6c!3glhP+BY<veuTz9ID*zFK5t-p$a$q zO4*||3lO0dvj-}ey(L>cMe|k3tT*M{@p|rL6K}G4r6Fs!ES5W3!|E$#^_8qPQToXq zQ$>-SAu8wf%dPGLMrZ!|s~kpG0ei4wZ>)A_pqw*E<&4!WR3+0Zli0)6+{tFvP$iw3 zNT(*T2dV6lnmKCXTtx!CJbtMz1M1RZZ5q_H?_C@cpysL(o$SBd1JG=9!M5l}aE+LU zrT5e?k~OmX4*B^%Qoev4&m&-3dA!~Gahp8esBdJPkiZ}ko(p*dJZ&t@02wB42?=4M zm7TfpqOh2Vf)3AC3J1V}$Z(7+(Fq%hAOr<^`}*S%#4rLLaW{YffOc}0i(~bB6nt+< z(jfW>e4ehI#vFh0fVJa>PG4HusiSV%D%g-?+(;|zf$N`<5A06%y2V~pt*1=9d3j#O zt8B4;QYAO+tA_UhD6jjP$yuvTbv8pA=CJxVmxmW~=pItjM5>?cELt#*f~WPHZ%pB~ zy|r%`wENFWe7e){QIU04Ts(P_tc}Y#sk)ss-zoa`)@_sPN{yp0TBYaUx9+M~Ex3{N z=FU~a(J+o@z_Bd3;9ovg^)(IXqxvI|8@}~LvqHz0g2H#wP9sGoFqd8Qih440(%gf) zopGkRo!h^+bSAKqCZb-^@!q&cQaDEb;-yKm}ln0mB5Nbm*UROkjKG)7xa zrufoU=f?2LsDw!Ns3o%ZO8f>IW{^)Pb8eXER-9*sqyZCQ&zn;U2T!}BQqnKPSx>F2 zN)Gyn;TBKHTNDZ+f*Il~4)4-5Z@6M%CK_^n-SDd=LOc#>&+c6c^U}0(qjRf4xK4@> z^4XewuvU)SX5B99Txzc8+T2eV(_GuQr3TVIn2t!u8=z+`LeF#E%FGxeJEBa(U+wUa zjh*G5n23Q1eY75Db&v(TNW(hEe%$9jopEQ5gvcuy;fl&%v+TVev&dj@KZ?m%-s!p% z?ybj`I7UfQxCZq3pw+ zj23S1U5!(p3(0mq+ucD@Dbz=a9Dg}elZYP-1JjMR7D9OSR*0h3wOtE2%ZTj4BE-ED zv75@S7x)LGp5RaIFA5_!l?&O|<>}D56^Z7qL>!ig*Yyg*LkXy()YX+y7nRjCFls2> zZ#8u_w9cbYx+v6ldZS|hG6V$Te7(c|+u-2sUkw>feA&S{(3=>F4Z;IsT%ac&VHJS& Z!8_rxxUk#3c<8SNz{=d-tir@2`Y*qO_L~3z literal 1629 zcmbVMc~BE~6b=H42Wnd_L8~n5g@Yv7&0&%uVzRq|CHgFkM8X5@7VW!-+S-( z-fl=X#D|WVGKR)v2=C`Wo6xFhTy3!R2Bi~oaSu8 zNSG~oA2kuNJYJyHZc1m<^@&QHath3T8-d43b7&qfHqJwv@f?B$vWRRusRH|tp9BHB zMFplw^&&lu5H@>4K0_?YH<=xaXLts(xU?mAVqAG7YxcyDt{KA&DB;2x zJI&fD67Vycv#4BF1#+Ig48ck3^{qHG$QRV zR!6#6Gl>(!x^_-?2rGUgR*5i#nWY#LMLC8mAlXK-l*>lZ0J1~|M5WnD3*~mr^&ij6 zXiy zp}#o(Q!K+4M?#bNpf7I3z9 z!joMA561@0&_u2O)A05oyY9V(d5D_P5s~8M>nPVcn#N+HfyTu=vxED_)^B2_TK5OYiC~@w@&R_v@3jpsQaRC;EXuqQA$ZCRTyV#lkR-s6_urK9Dgk&s4ypb zD*W_Db9a987Gv5+6MVRuzqegRZ_>gn#%G->4Ri|6=lZOydm z@)zNU_>Q`Lkq2q!(zfc3l=ny1;&+<%q~Y?OiSA!wn&j)Y+UE7W>xk+JzWMavWWMa@ z;I1+C=Oc_Y))PMj1(uZE3jQQCVbkiO?=Ezl_l^*?e!so3ZrbI2VAQM=)eLROFcV)z z?3>ddjjtm|y%kbab#%p_Eg{eUe$Wt>)1TS=pMl&`TeWLKbgcrWw)k-p(lz%QjZjBFKd5RZO7d%LB)@x3!$Bsff%sIX-S7`=51p q9+%?vVQCqSb&khi+SLqbO)#H#q%^EW`FR!T|KaN}16r$DQS=YD>}To# diff --git a/toxygen/smileys/default/D83DDE4D.png b/toxygen/smileys/default/D83DDE4D.png index fc9fb7ec1e8085366397da50228f61262d964c3b..2954a6e5fe35ed2942a760b76ab8b5172745da20 100644 GIT binary patch delta 1626 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 F{sY?IVN?JB diff --git a/toxygen/smileys/default/D83DDE4E.png b/toxygen/smileys/default/D83DDE4E.png index 2fd7c71ea8048e9afb07722c8de1543ef2dda749..8032b6fb18584fd2d04d63dfcdc84e4d101c3023 100644 GIT binary patch literal 1710 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 J@8ya4{{XT(ihuwB diff --git a/toxygen/smileys/default/D83DDE4F.png b/toxygen/smileys/default/D83DDE4F.png index 204545d5987c0b6f0d991fc746827bc1dfac3304..d597f2c346463740671c4fca28810b9d8612ee2e 100644 GIT binary patch literal 1567 zcmZ`(eKga182`@Oq-(k=3tPrD$qn1sXi`mRlTCicO_9y~Hj2hE$YlUOBx=g(#D{UQ%f&j<>wrBXx9to$hhY{o|hVe811<^LftmJl~gdo&%dX8%<5D zOaK6;ET%7n_(k8Au|BdpI-7+c&OpRqGXSW{UHFA>h;V{{39$iCH~^r`1Yj1Clpg?) zkpLLt0YEPS0GoKMHh2vHDC*`wuHTm?fD0*jk{QvQwH?9phA6E$!g<?}fcXHwxQwV@L0t7_UD)c(Z8Y(b>V8 zqS3n2iN|NBnlH?>U)8Eay35(Koi$Ih;r=Ss=dRi(2L#s1&EpTxJkH|TBsbg0w0K2l zQ%TxX!`Y#l;-(TMKJu)MydAITN>-Fg6{R-Y&s!%o;9_g=+(LZ#aisg#uV2GZG12ti9(Y(Mk>8e>946KShe0O%^9_gbBM75&`hcDGUuR>L&c~<^2+Gy`s#_TwVRZrLpNNkH@l)#oSii z=*hxnFk;KJ$|qwd?>3pu)lO^VcC{e7)I zLDRZEwSg%uGc>47Q|Z~B;yPx|91CpW8CyJ)ois7FMnzk`;0#w;*mr~-o^>8Dc(t~P zJFZuq>PN?uLmhxZ`=EFBV%PneEb7p(FSBN1?pm@FIS&_a)U#K(_T5&(!>OR{mFA!2 zkXL+7U25%~@hi(N(8l%|VMhJ2Ryo!1k$BoBcpuX*%+(*I<*ia7=%=FBWlapfD5veDj>(S11^ zoNkZEbWWJk=1Um{c@o0)ewO5Iuc08Idimj7Gf|F?Cp}AYidl1HiaXJ4rM?Bv&UKGw zqbd+z$;_~T@^Luz-SEFpziyHn%GbbtEMEq9*>eF8pd7Xdw>cO&^7yde{bk)%9Zw{HQHK(fCda$H zIwmI)lNm2APk!{Gdzy?_Wu&D0)Y)mGIT+46bGtg9^!29sP#fi`3&y3gx(&+T%$DqO z!Hn*bkaqShUE-~DTC-$tKO=$&FAb#zVO_C7)X=3c(a1<<%61mpV56`$;smy-EYAub zxh}03VAIw`dM_#tenV*OUg7B3@Yt*VUY=TgzroXe)f&4tdy1XYs`mD5wJqI&Mtk}y zy|Iq6mPT{Jh5;zX(6 z7HJG$3eyD=7;%6?qEMVk9?oQUE}2AkqtdD5pGhP-iR3{WTK=De1aVB9Fy;Rf{C7ec tB*F4qhY+z)D(6dJz=#n?!vt0WUj#!iKPDxq5k{sq04zU_Z>0|}{U1K^toi@| literal 1590 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 R-aZWgTpT?eYV3kj{syQ2Bn1Ef literal 1679 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 Be-!`# literal 1571 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 diff --git a/toxygen/smileys/default/D83DDE83.png b/toxygen/smileys/default/D83DDE83.png index 0c6dc18179c8d63cee4ba01153930bc064b59f9f..25ae099316d67c2d8e89281017d1901cb976df7c 100644 GIT binary patch delta 1196 zcmey%d4qF;WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!Di+Mq1)2l280d(gxgG0(&JrsL@&kE|fkD_J zzJF$em8@EE{rU|{9(8<8(a%4|z);V?nB?v5!uX#__a0Ccdx@v7EBiBcJ}wn&VW$!g z1_q|us*s41pu}>8f};Gi%$!t(lFEWqg^Jt)1_q1XsiD!6<{0q&xvp8nqI`0aC7l1z? zILBy#)Th=QqwS|GQ_4L`UxZp8`zyWZwLx@S{X?e7SBk2>PMoL2bt9zoyT#IFMpJy& zFMX`LO=0f5#}{=Im)`EMXtcR`=DTR;quuWqIy7`I%7>{)Ut(hVI)AYPM~0}>Lc@Nq zvnvF2=KY$%!uq=NkkP8W8;-|qIIwTg)qF$#18*1mHnDceYG2CiSZTmGMO;~C-T8I0 zbE2zM&-<~~Pt<-NZ>=sYeDdJ3%dxjkxF2Q-k@*)nef5!lbFc7ehwt#8-v3MI+`6;B z7dAHUzTBFsntM}i{$2LX-+e#4d2BM#cfsP;-KOC^4>R`6z8?Gd)e@a2$0C{8L)$zm zj~sYitRj9Q{J?}&68jWRus~2$ql5W4+iJHg#bsTla}!$;-Cn4~CUYHdjC8Lcy~c z)@v<l|Px^u6r`OW_m%r`aautMQ_h{yR6HfwfSb9_P!8zN-cM@ z+V$W23I7buuEy}gjwZkaTJ7oL7$R{w^~`O)CIbQ2i^)@!6kK<9ByWva%k=7hz5GO% z&#=Gq1ZRt5$$eD3c;(U6;; fl9^Ts)gX>V$4kLG($0*H$p~2doS%Bz02x)FD+|ayQOPviBWd#J>8D&Uc9@l z-JDrR!W>ElL{TINSxksuP(GMwoMs=G#(hu|z8FM97zRIR9EPDrkonx!PCqPs@N#$0 z^MCID`~BbVa93*Y=DNr05CqwrXqVD(t#_Y?z3{#L*0KzjtxhcGbQ?vdBwGOC6(bMO zgeLcbG?10y7w17Mf^00RnVgeLc5nql3&^fcpsbk?jUcUUWm7Kn0|(86UR94^i!-wr zswxpIODBn>DS|$=eZ&IYBdJVbq`$x_SliQRYng)t8gOK^tPSWkSB_vSyd2!S$2f+r zs5t!*Y}Kh;vI`Xr3!ro$=r0f?i84%p4ABfj?nWtsq;LY>jGqi~G{aGQ(CURjYnD>v z(o(!?3rZ2J&v8r+$4jMBpcD)kRxeJnEbDSmlpkvN?P1-K%YNN%t}#f!E?BDRsD_TZ zjB?%>bRrn^bTtIcOeWWeb-S7R9n>ZODa7}aLTG6(hH2Bwzb+zrxunF)qu#G{h z0P9g~u7P3h?r+Fdgx+wSmI{j^4@gE~Py@P?kRllT5>QlyqezmG!U7ZLSuq9$K_r+s zC5CBAB&hfvy2h~{R-!^8!vJU)kmZ705`Q_Ag z>@(qzC=e_wgeA(2o9Bfv%kz>DVg*TH8LWy`{!h+u=nU>Q$A6lob_6@n-LADh6xN0Z z=x|~zcQj6pet;kze?sCjLc%kQKi8+-u+bJpPNSaB;tH`lrj2`Mrs| zpy%3+o?bJ1JWkzWj^Uk+&)r8;j}|7g2Gw|#$a z`7dK@6JEFF^D}{^!{ZB4MY-89z5B5jCLRb*&Tn|<3tvaW5$V+Jv&YA;EpDI49=%Fk z*fwL8c3nI%GZdBgHTcJTbFbunoRbRCPd~hUl&dth^ry}|+L9elKJ>|{FTXzTJ96OQ fXyYMIwBkX29i19EclwQ2-5*jSmXh8VdJg^rZ>_YH diff --git a/toxygen/smileys/default/D83DDE84.png b/toxygen/smileys/default/D83DDE84.png index 829cb73bd2337f9fcd2268a09947d0137875da35..025cf05563b2353addcce18a06434de8fd492771 100644 GIT binary patch delta 1592 zcmZ`%e>l^59RF;7W}_dZVff;5s;g()B|o}IXNl+>*6C8Wj9lGZVmS+aos;X7j`c%H zU6$ieeyz-pCCA9@)EHrA3^8WaaEq|r_j&HG`{SPH^*rzA^?qKj=Xw8l-NwqXIaaV9 z0KjGY+EAqE$M^*K08sraX61wd!Xes$kRSlgI|GoG0l){Om8JmT907nCDgd6h0N4~) z(D3DF0HAomfnj8wPS?c;aIrY{xBj3jqAkfn&u*Rj3oW6vf78*+AmJ`TJ=i^nrm{AW zRdVWa-5%KHT>JTg>`2!0X{Cx?UUk2;;$Hwt4v4sgihm9gg@D-$Ql$WvgS2rXM{1Z< z2>g<_U!6>-P!+b%KOcUtnqM*du^jlFb%-e}>QGO-T|q|CYPGAYtINyFi;IhnVKiK1 zes$;M($bPfqnTe=c&Ap+zIi(ZPtRnRCRB?Sv|627t)5oSj*Lyb8kBZ*_wof@yw>Mj zUTa=`=if`K6H|)bfuT0Ri~1&hW$n{4PJMA@?SqG$A;sd#%8E=r-ro75u|>cay!flT zzi&`BG%_(Zshm>2Q@&kzGpA7~X9tJn!}8ao6Vu~UO8INW$oP~@4!<5685f_qc*1*x;Bn(a1e`|XPJntB&NafDZ>d?QZd`ipEm(o! z62n{uB11(U^z#Lm3~Ub|#E9V^L^hfEROlR9bS$$Wne~heMm1y%<&dXwl(jAmW3)S&3>JZ&Iw#nV> zu$s&*%iL&2j=OXulO7TExASGURv#tmql;v2i z+ovO%WTYJ~Tu)Cpd>Y<=k*XFA`MM0mS?ppyPPHNO)^a@ei$c@V^u7k`!@HlajslU*?M~=xE!vu-~`PTw(G!e_D+`cMxAZhsF{m&M#dFI z8Z$4LZP=>_h6nPmMK!l#G(LiO>RjYE*VDXun?(WHQ6m39k5@O(B|Y9_*Kz-syOEVA zHtudg39a$DutcLbqA1DOtyW#gn@@`Ph>_*s$Uh{6w>dvhht6R5^hJSrT&tTDjPFG=7w)ndR$0CMtID?DQJ=i@@ zd8sL@D|%=&4tG)Sr}WQL_O-S+IX+<1BCT+~V`JtUuCYQx#66#qI5jw&FDD^>ehy=D zISW1*Ane?R!}0l>x8?#-E{oD<9NgLrQI3A~&4_5Aw^!QJ+aqa*`efaMeZ%6Re(%EQ zu_D1WRAi~g@}(~vtdYewGsxkLs1uB6Pg;C5QUHlSB5fzQZ71#wBN9A6b@g;5Y$Fgn o34~l9dD(vqu}){APMu`_zhe}C#4^|6V-o=U$%lL^_EFRS0o{NIrvLx| literal 1561 zcmbVMeM}Q)81Fz)5I6il)J#0jiTKf8@7iA5(^~JNj5~eSS`@Hx3 ze$V$+o$PPH5R2MKr)4Fc7$h^MRM(~$Xb`)4H_2#3qzPh;A2D@2>I6e1uSF$hj=k*A3cUZ zU`Rz=YXF})<*?)dB*!y=Mxm0slrRjSr~+1NP!ygIAW9g4l+umLVKt^fF+>XtU!Y`- zcY81!l{IWjk_=#>CCUpi05SeUpHQ;E!e{W z2C^{%SH!!ddU$5Wz*6pBZYZiKd4pMbRw@d;j^bQJKE^MaDFY~dQMg$*hQKgN=?F9n z*OMkmAV?*eg^)T8LMo9gttQ6tA}p*{QYsu*QwTx9unECQQfoq0CQ7SCNgYYVux7s? z(tZ~cv&%|$hq0=cVlk3uXp!UX9Jemk0eOX-$O(m903es4K<0AR@8*KSoM?HTMN2U} zTg}?7e@!)7+SC8a*x4TQb=C=(kdCk*zNO$o5&QtKM33JJ6FK0le91o()sqGtS@f zBzV$v@#%^i14)Cm1=AF5IW6B?4E+q2P>@p4+m-QELBeL)y>RuRxgT|(#*4qyX%4iN zzFxX5r{?b-L;XPJv2POUjeBdjJ>~rgiMP7_pZ-sjkBZW7s*HIK1-{k& zt$oVb)hRnIb0?b04j$afcy_&kPaN6ry{x#M?5V20Uipxcn-8>}L2_@F^gXbKbG{Z$ zXV0}*Cw-URma(cKt!A-f{JWm!gXZF-4GY@#UkE2Yshd!L{>UuLsmm$M?i&}yCtpy! z8+JeZOnskP<_8dundu@nRz1-eK=lb}u6CbZXMQ4R7DK0x63XpT*Dkpf7QY z^}@{Ebrr4eL~cDit{m9h4o)&QU3hTy9MExl>j7-)sD0-*Mu z*bi?Z9#{57VKD&2XabO!0>F1(D{&2gn=k-AlK?Pf0w7Cy!getSfDh^Hh_g97JOp;s zp~(*eqf;aAXG9`$PRBA~iBEtVZFFkp?fCTI`1FnZUh{;un4&&2dM|?BMYD~p^I>!v z1(w@WpMp!Fz&-&ur}vLey_=Z+f6>_cxrL?e?Va7-UEq@rV)8+F4)96^m?%I@UR?bQ zNNMtE$8iioW-^H zl0hzaduL~7o3^uqxH}1n>D!-7BIZuWCVm9eH~aNqi&tE(QDm!K%CJ;OAClI=nP1?{ zf9mL1#Mn(=fKQsir}P8rWpR};xlK*29qg9&?Cf3xz5277HD@(zb+ziCS}!tl3mz~F zvX}+euU9LdVn7t`LKHHT6c_*BrL_dz9wR0VqoQA*3+UAI@8D(FJqVHJ zWi3a=I`PheTMuT@@js8mZsPVGer|0dzwH|p?w(FwjpNPm`8e1iLAnqG#UmnAG{#0` zMMOeW12kiwK>^^8jlsE7vEGr&l<=^CV6wk5HJakD92G(Vz=V~5DI)WLjxiBH2 zQ}j-FMQhFKwVL*Z;)!$mTJyUjWl54UGgnK;q^|h$pE);2X|!mL2(R*Hm^aImwru~b zzX8|JR3r<}-U_f-@UZOO8Dh6M{KG+ityzX_j4hW~yr{iCmJl>g(q|6`l|pCy9^m1z z9KoR+)@?FBgf{W^W#0VP)Sf(#86!>Genge$O^UPqofJFqme;mi;e9cZwnufMbOPI%zF!qzj9|Rsc{MOG> zF!AWyxe%5qJr@fp2w6QUZ|EsMNtSve%1&kb9evl!2|MU`#U*wU|J`LS!wZs2TM7-Y zzKGX3J(}x954}}!{!(erhM#HOp+mpYGU84N!fX@Ed(S2`(h4Ux=1Xc9ma|H{jYlA` z_6jt;c2zZ95q=n-TeOgkhW`my*K&~en4Mad)9^=?9x^fC`m&eH+jeJJ)%L#b5I-`R z?DKm7nJlriz3JiZfyWaF1N^l{0s=~HjOY1^iZIxb_hu%h7p&RxqqwUR(`5zUh1OrcOvLGDpeNOgf^T4oIjM|Grw8@aXzZUCr3(`WCVd|`qQ$f zYQWjjT+Kj?4_I45pEFfOwsz>% literal 1474 zcmbVMeM}Q)7(XF=D0RBbV1Z@a4#7EX@1ykK8cON)S}6f^l?_w`j`pw}ZLeMrTRJwx z528zqWFOf`rt@QL*~r|?7Dgr$YEe)cY^L>Xlmv3CYEPWXSLCej>j0LPKr6)NNyfg1bS+Jyu#wu|O-yr%}frIomzMg~4 zE_Nqp;aFSquFG5j1jRQvDyqb)vQmwecPUv(N9lKY02+b{bbb$OZRA9_p4;hgYmv#b z{Rr%^X^~2G8CK>oaCS#=K;X6n$}6maMk{4QbcJw%UjqnSoXEm{m(%Ul__fF^uLkU; zV-$gBRm4UuGUrrP*=E?l3mmLgsuWfX$6=CG;<;*)#5ceMh7%|TZc>5gYSg5L$b;uE z1XvSn4H^q$nzsccEn*i%j|N43KA+O3Qu4x16sIUk;vfhG&`@}r-6HE(xV>3128Q!m z1&2p;@NQUQWb64RQHuai=R$CK%E}gq-QM{`fs~GI>xammcST>$)y;AW^mj{(0N9RB2_Ay!~v9|Vpy}= zE3$4Y7qjb-?9O5HABxo&1dbJXp@Qd~u@2a5=SALY=RL4t3kg3}>2TY4pLd;9p1Eil zPH^nzY$kzs!L#|*I2N%_=I0t{jG}1ZffTo1Pv=v5JwxYGG(%G)GLNNminn_5IzuA_)^m8cf<6qY+ zZcJ{=2*OWZ83<3TjSt;iA(KrO;#jgrRdA;yshd6Dwe>5e>+bFDBagoF!7|yExR#>V zi@I7P1O|nifp=7)OXzn&d3TCzgBn4rU#K32*9XJDOs{C~uRXWK6&V_Ut`8hvbD^&L z>BGsYAF6s=CLXyo*<m4e6p=`ey33pMVDAlE(U5>(WoAt&^4fcvMDBg{LhWpRMV! zv^E{DIf%SgoVZK=%#M_vlY#MdlaFnZKX>Ntp-&XOkvG~Nm~~g79IU}~am3!zk>0Un z?Gur0@2x{A z)Yr+Uy%f2>JTI)n;PJ$vgG=}KMZ?ZhXU^BA;_dG_KXa?FnSF^@4-fB^ZOTBTbGi%Wsi^ClGV0GvO^GgI=uSrPWiF^ g7e|XqQj?M~DER*9+UFBqO_lztW@9;Xny%aX56}1+w*UYD diff --git a/toxygen/smileys/default/D83DDE86.png b/toxygen/smileys/default/D83DDE86.png index d757d24ee4ba45d9698f233abe8884b2b8018b81..8fc95218c87ccb952532741439f65c4d9c0d1cad 100644 GIT binary patch delta 1570 zcmZWpeKga182@^S2)&59U3FJcvUG=BC6pFXkx;oxZH>BGYSv_C`pu=LkhjdDylj-q zu;13pLaJ@bYk7a$Rw-f0ih1kR{q@g1=boPPInQ&R=RBYDoaghL^Kc9s4N`xBv;Y8> z*7N<4pnbyC%M}1PW!_i(93)2(Jbb+XpsfLb5f8v;q{J8oAOZuxI~)LxYyg&!u2=4J z0ziZ2v&Y|Uc6PP^Z+$zTm2-<%1mCNuddw5ma4T+S7xCBy#r*pZdG{W$3vMN(XWfKF zm(sGbi%OvDMJ2c3yT$O`!ZJZ&xgd?5o0^@+ymFP9l6fIHjULC0j!!sCJC7iO&NxeF zApUBo2Lfc$&k#J2!qpOfmAI)(rWhJ|r5t@R{Qkw@NO%9>la|ipW<{86jQVn7=)**E zlj7Q=rjw9deQD@xvho{NDhG8#pg}2Z>FAL64~(cr$0t?lse84JwJlwJ1Iob*4)+~Ts#yrQ_o6naE>X)FPe7tYJPntYiS1CglF(J>4}21NN2nod2)h0qjV z?;poB=1k9UjLmxc%rqZq1|5+~RjQ9aOrfAh&+Y1rIJa)nO20D?%IA=z)kwd_vE4g& zfP^{AJ&;6~;_2n4JE&`AY4#@WAx*qvLT82ff~( zc0R;ni(i{U_CdS<>ZoHAg!>~#?#aqZx4SU?eA`PZ4$zxl>&Q%n)J6*!4%|?)f4)-4a2)+^RcOv9hz^;3ySRO)m&y_SzaOVHdL6i&7>Q?|XE! zr0VH9`^gtkTB@NR;Y)+nFZ&HC|- z0|g70sf#<3^dG;mmg|xzB&uCD zl1P!2d|sioY(V%0H&vsT8?(o_SKBdAbm(lYomq0Ex{`SK#?=dQ(aBf&4GY%oG^lpj zNsguK9bSHA&952yoLG29?d{Kb?q0U#1qv!M8`F6C@eHa{BS6PiFSIb2PntB0`o7Ht z?A?j`v^AKKt}tm1H}Vb{cUIkPPp%v!jn6~T9Q+{I>F<%zY1}6L5$f$9HzMh7g8u4B)-NIYtY+nqYi-1tu@+c}J66E|$xi(o=pjN8v)k2t*>D zNF;<=%w4oVAdDoD_y9T~w>wMLi=D8N)xiw9dv9IwFgqZzhb@}X-t3;w zsNR0em%Qubu0R@xlCgL}#&2x1}|@ zqL^1wZuvX9)8_=Ph3BdhXaM>bIyeV#tR%?V78JJtN-&-h;z%HeAOYB7Y;CPE4%Rjs z{cSLgcJ_|;_Gk>o5rdILJv00tA?!?W=<%qZ2!{VL1m@@9w3Tc10q}J5-ci0C_xHbH Cc?9eL literal 1698 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 R@}Nx$0FB~DuGL2`GJ?sDg literal 1600 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 A6#xJL literal 1531 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#mFDUT41?MfkA)-L^~#L+d+ zM&#(NW{sv7_6y@oKX7{p0sC<_Tyt$&1d}E!Ex7MwswHTw4<=n1*!2$^ScFJmiRq zYkTe(!6m;?VXBf!f5(Iqicg*%kh)@eN%LdHciV2g-wHaPSFDH%uJichu!u#LDgCm} z;`Qn5F4G#fQoP?TJotabbfrO-7^ zYc;%%Z1O2_;d}IHrm%))>J6Cy^^11DnF`bM)RuI13*YAZ@@>sWKfNW1QP=Vrqz}o7 zo_gs1>-ICRIrDzX_@^5EWzt{6d4h43`Nc5Fd3HUGL0yMd_1(DJves>{(pKmUW zIrFXk|Fp8Z&nBO2+n;TJ@Xi$Jr9zi=Qq!J9*|U7y+b^D&`>}ZKeT`r1VkD&6X0t6^ s@ywoKN6Th!)ux<%4QuP($20LT>?yurFfVLw1*kaoboFyt=akR{0Q?CwbN~PV diff --git a/toxygen/smileys/default/D83DDE89.png b/toxygen/smileys/default/D83DDE89.png index 0de6bd45b35961c5ff54476df83563f28dbfa28b..fff5d09248e0216bd1e5c3b31360fceaef0fc80f 100644 GIT binary patch delta 1508 zcmZ`%dpOg382=I$3f*)uwXik9*%)S9?&P)^ZHshc=D2JuEvDGdX5|vO=2Ehg%W=y+ z5yc4AkrYpQB~)Y_~M4pgBwPTaYS@QEYcVDgY-j03=BPSbaSTZ)w}g~R2?LXyI=s@Mc^T%rVq@ldijH8V567-Nca*z4`+;T@A8 zhGk$eDkH#Ia-N=Bm7ZH9&B&!O4}f3$^`^gr%3EQ40qjE~zt+8bTsyBePLzC7ng-iC zP{swlo-}5FG0s8(DHQq!MtB=Cj!1T=?_(VfwzYG#q1gMd0^RA1oRV^(B-PEE&gLCs z^J0XOl$=ZDL^}teUDxg3-r9^!LQLLMIgV&-|K zNA4tU&5akE;+0jCz!Gv|eRR!l_z@o)&M86*rr4i}QB4XnUF#`Qd_&uRjhyaZ7;ojI z8JkA=j z329K*S^PF#m`+t=ICp1LXIa-%=Co?GZbsbhRHVEi-75&{kQ=p?Y)`DHN>#RIPVJ%9 z)T}{|MZOiLo4HrL{`$6{X_V(u;!w(+{gm$VN6!LC#hO76cd(4~MwOM9WaSR4!!~WZ zjL-5MuGTRvT`5vgeV^86QG2uL_TeUZciE$8ytaJ4oGRJ8k<6+X)7clKjQxp4JSaOv zCboa}EWM;*uz$KImWILPCR3}ruI6N&QLiR`Yc=1W_MKd#&uZYy%y%pWlpMPF<+3=r zYwR=!&6|KQvMEIC7w5$}_Juo`2M_zEF6YId={>D{2c{gnnftQkc)YMTiSgwBF zM)@tx<%$qnTzpzjzJuK(l-s6kP0uXjILt7KGI<|i5!P{h?h@;Fb*=vD-gSR=@$46= z-Ci9VX12#&Uw-Xs@YrT}g4?s{$Y{HdnCJ9p08!IIHq@W$L4QQ_oJ`L<9TM^#{?J(u z7e|P(Wz6hOc%YK+mZWEe6S=w7S2K{OX6x@WjJ&ooT-K@aLNfaliUYLcM@Fg;@(_ce zdq@47g=r&uZxk@u{@=x@oxMp=CL6WUF1T-}c^TGIW$~-j*7aO%t*6q+{bJ3RW%;An zqxX{Jp9(2pu(61!p)kzxu564RtqF0`nSkzuO&qk74Mns=tK!wiKP+etq&U@ZH@K*y z)vdS7Odds4e&)^yh=uIPE><+*U7LPkcUOwmt~r~)`NGGsb@8b8E0cdAI>=KahZ|?+ zJG428IMZ8U->iy7KB7UxEbPea*(3dfrslHs@xQ=3qXn-1f{-8qhs2KIz{Lkx;4Cc6 za5iRm0t1gDSz3{-aJz9h5)PN+_^kFn3|@3dcxe3p6P_XibaQs@(g482mFCjmbX4*W DQ_+oW literal 1681 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLG8yO;GeeeCv{0lv$RV;#QQOs{jsPt4u8Rn^~ARI~iD7I$5|F zf&A_0=wxB(=;-2PV(H}KWND@Z)tf?2nCSx@qYp|PNQng|1WbV-COqi_Iq=Mrng`4= zMZl~fwc5~%fr06ur;B4q#jTPcUmsydk$>BEPubS+c3bPDAg`aA5c5Qj z(`R$?92W`D6zT~svdwZYZZvlI$9GZ1Z*fEXyH>B66ZG=f+2(!JIsK09^wXjM7RLv^ z=MtApozqcybpHF}0mh&G4iy*QPuPCD+_L>JqZf*tEcQmuM9_fqLF)DmpbavZ;rb^E9#?O+tuKYj2w5-9|_2`bu z*XRDbiX2tjb^L&T&*3?_t#&sV;!Zq{j6C+}d*l;&Tej7v{#JJ)%H{mq;@!V9N~Z+| zRIsS$PBoCaoN#V~!r`2W8(s)on#uHWNl8{|uHCC2=lCo#HdbGEeZ$&UI#;Bms+_wL z*f?*mS{0bJy5acdT_5#lJU$ety>-{7JElLjtbaVEV8_Cpte)&9LJ#AUKU7vpHUI5* zye##9>ao|#TuY0B7A%tLT4fVkTb_35rqz}24${lLm?W1^=~(+g?T~cciypE0AC|OV zKKGF?YWdcbwCL>eq@!&T%Sx;}zI9?`!j&CV1+Uv#R<+&yM5KY+I&X%d4o3 zYd`;Z>u($Rix;l&=n1{-DTq;bon*Az?kdZs6>Oag6+~pV*P2dZS{dN*zQjpQ`Hfm| z)x6VNRZ{uZunX|C9$;%;u+OJgJ6cE2<<8~@+bUd?_I&AGa^caEo&$x#jrUeA*tF-> woxSegtM#^Q*>>=4+3mxM4?7RN|09~f@YSCs?MdF9I8Y_w>FVdQ&MBb@06-;tegFUf diff --git a/toxygen/smileys/default/D83DDE8A.png b/toxygen/smileys/default/D83DDE8A.png index 46637f4d79ae8c420d5d2a0d92012e0dac31bfba..84a5df955a8ecb143783780964d6ee0bff66da72 100644 GIT binary patch delta 1428 zcmX@lvyXd%WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!Dx(2D)1u=;!_0>&nwJjY$1Z32;b^=jNb2|{J=@>Y; z`#5^~6xB3B4D}6(bZ(j&Flo78XcWV;BhvT&GAupJHD#5@zH9uGS5>uiMlH$#TI?4Z zscB#Ybcw8zilMo+frYh4KyX-GlBJ!axsAPlShPn#$iz8|E1Ejod;*f|a|@Gmiz=Gh zC(d1LVdqfM)V6rT)(!iPTG%=285*f-Xsc=KY8n_TYiR2j7**6a1%^k(rKE+&#s`Fi zRn|AJ-ncb3Ijyw1zNxdPx~a9iu5tagJ@w7)`Q_DpQ)f<^Ij?Wpte(j;dZx^nICEZJ zSyf$2N3Xcb7huRpmIV0$1HB%|WC;Bb`t;G4uba1MedWJ%mwC_LsnZ%?rd$NdFeZ7s zyD)UH%6b4f>?NMQuI$g)`M6Y>LSxP{GB7aJSA|5B1SOU$6cpvBW#*(RlvEa^Dpcea zFfds3P7RITHrs&b&vmU`EXpS*Me=d}SpWRX=F{(cJd1X=+`e~A{f0;VCb#~L6YtyC zpXC2H%Y5VRuQ`!PT;0)EN^}MePbV=*`!hYReD@CS7A7Atwo)-JjNPc3? zj>b2~WdGe-xYR5@TQIf2w*ADaytMjVO7oH?GhUsbpE7x8=sLyS&a0f)-!goEULdyp zrV)?$j3v`9UvRJL(ntutdiU{^S*HSw_D<^y%yG1`J?_7~Won-CBbHAo#>cgVjuzi# z6bOu3WWUD4YzZsND|`1wfeTtDPRVjpX9X%o*nBnQ|v-j<6eDD0kr%m0GI=iM!{>(k|gPZ#=yO=fhiM_iP zW7xjcs%*F z%yRbI?T@^Febe0FyJFW5h0wC}C5wM=clpQg&2LbIZm-k@Z@Odh$q&^pL9zu`e?6GQ2$_3k&JhpUdt&F{UydXw;WjR!C~+4 z>{Dyf$`G9=rzJ|JX05%q!Zf;yr>$)H?boHVYajFbJUbX5(|hFOk(^&lxiil#`I%%m zJ8 zMwFx^mZVxG7o`Fz1|tI_BV7Y?T|<))Ljx;gQ!5h-Z36=<1A{!4^`Jr&p&>UvB{QuO aw+8u0@%Ic9mDIQx7(8A5T-G@yGywpj#8{^Q literal 1615 zcmbVMdr;GM9FHJ4@iDL-isxnwWAd=CKp*KS(lo7BTdbu*QKy79K$IpmjkKUn9@Hh0O>90AP}C0cPUHaR2jGDEFS#Ka6uj6sxhY>2xmZ!WjZk+2|Ys)YrFo(z^DJ9f<`g;f@ZAvIp zVh|Y|Y9gOpP|OhN#c5`1@fs^)gXX1x$!-NFU?*4{blVFkr^2m-MsyY2+`lb^z!3<$ zMhU%i%3?5rYMLQHi6D`06^X^5R4NcBNu*NoTo4wCVWEh-rF?OcLLybbGH~>QIBSe8 zS7E}mqqew}63S;;he9ZHxm*HQqJU=dgkl6i{2DOK=Ma2n5yj$eKIM!KC}4!s%8(A0 zq$$v^i09DjSS7@H`Z5H&!(ezNOgTpr#idN>#vMYjKqR!={c(+;ovexY*NxYro#rA3 zAv6(AdL3is>X92AAal8Uvmrl_^G30lA-SU91sH8zXD29DhbbZMi@-+O6tGw<#pI|| zt3uQoZh@*rQZ1~OOJKDK*2*LSjn{EyvLum4rqN(9suqhiFp5dEC?XZhwJ0ou6XgM} zj&ia%WhDZ3NzU#l7keXDp=Jo2r5Q6#7X&)Mm`}5`GoN;V>U1d>n@LhO+U1P%m*-`) z7{QQ*giXuPc5o!W3i37frSc>-Dnbxcj=}!8RVq}Ds8kr5grFD~1T@OE{hyo(IcJ3a z=J-#u1h%*i^iN;4KDT&vcnFF+F${M!ev1115C8;i(qSsI`}XD?i}buDQ$54v;dhoj zzc4jyX+?5{CAHwd_)xVboEIa>?>Gfxnbg)r5`mtbGdm@trBHY!yt4PwYE#>nTT+e7 zVyhbYHBF|$+U>4Tva!a!!`bcYZy&hlTkp9mpPV(wMD%1vZaMbLVELdqOaI3eO?hbj zy$HrobNcwo+Mb?0dp5VfA5~f`xbA4}7uWTi8A>Qn-tKBnNd53&R3m(|@ps>?(hU)9 zLow$EYTKXeTb9nwY4+Us?dpWEr#utCc>d~AUdjE%XX{pJOOCLUm@It23sij3*Vr(A z{qicXj=zaL_jstJ6?pK1>^M-m!5&h1wzIXQMdjx3qioZXvNz2#y@eXy@~f@gK~$;%1{HhJT)=khmB(cb-d z(z{o?_VoE0_Fv}~%d8w0~itw-y+3>y<)VS=K?_QG*l=MJ?6H$2O|h9D{NRX_0RFF{*lOe$--?!t}KjB|I6 zb$%PSGvTYZXH7KZjk!g2W$a9BF&(u%eRyqR&BBl=+s00Latg?{!J>)f`AMFZmh$?T z9YxIi-OFWO%6QPCe^_KytGoO9cs09sSKr(f1b&uAS5`DM1J$uXrAs>=<=y$)z4FA| zU$0j^@pk%r$YUm_s%bh=tc?2vyxvef+upDIix>Ve89R0Crwh+ND!f=gau}rdMPxP* W=lwIN`ex};|1VyrNyEHocG*9T{b2+E diff --git a/toxygen/smileys/default/D83DDE8B.png b/toxygen/smileys/default/D83DDE8B.png index c25512bb263bfb0484739f10e42d52e214b929d5..53c33593186ac05e7485489f5dbda23b45c89ab4 100644 GIT binary patch delta 1213 zcmZ3=^^$XfWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D3|=BH=cWJOUG;xo-T(cyKm&m;06J)rP?aUnePSg+ zejqXPASgue|l%JNFld4csS&*twkz2sPV9`4@GP4EDt+2~=5vQF)OKFf%58jhfHCUV&%f-or&njI?>yZhcUE0||IYf>qT;g8 zA8wjQ&fH$O`!1XDlg-&)I!8*TF3UC%y-_ybvs$OIS8d~qv{w~}7jl>xo}SP<`_AIi zLU%V_yw38uGrI2g-R%>u1S%;=eVtf%Vv}~Vfl7w= zTq;~`eAO6NDa4;p3)~pvT-H9t`@GGEd-e1E_Ds7O)W+@8bo=Eq_o^O^gyc1=>b!ba zPMI0UfAnsOqm}ZjN4g0|qg`bj=B1qbEz0?L=R1ZD4V8fTDJoW%n4DhK_cJLj2+?5q z9piI0fkVuHuRqHnw}u-%S?b>+x>X{=r~FcW#&qXis!~luPW(hacOPA0hP@SsAO2c% z>)_7%mZ)zxI|X)q`j`IvW@qD*1mjDw9#0SJwS?*XKY2Pd>AzoA`%>*Y%TKf0cc)!_ zR-5tQ!Mc}Dhc@MI_OV(Wmi~VBgUIYzn`U3=d$j7()Q@GZ4@0k%^=?W#?WDK!)eFaE zLWO-2zkbP_Wc?&+AS2yq>*r#lKBI7<>Z+?!Y8|Ic+*Rt;8JMR|$nbWlW?ANRu5C(= zr~jij%$0S@`x{g3m@fZUJRDiJeWAsy=H!`wKI~poVkIQSw=&QzX!W$^&79{Nj@|vX zYpIy?iO<;+7@XLX)~cOsZ0BFIS@rh9$>RUHSxcTU1m9$}I{TR|xUXDaZeHsAuGxIP z>qN4BSH}pNRTLJu)c;Gm+5guqK6gj(e%o_Ja_5^{*7?uce6vn#-xB%#OgA+`#qCv2 zzY4M1^WXc0_%@lYe!GCly1)e5?CIhdB5^tO{6(RI1_G@QH*+rRx)c^5=){rx@n8P( z6gR7nXX`g^7PsEaa513g$P5lSWiPcUYA$cM=X_r2kzV|+wA<&@*R*==y*-J6Z>v_R zhOktx-O43fU~+851&c>Di_ZPTtydHdX8W&L@!uJ`^1t@HYSJYC{} zXeBe(MyU>qb8mn-fI+pyHKHUXu_Vl&Jb7#dg^n_8I~Xd4(< r85m4-e7Fj(K?9^CH$NpatrEA62Qraj6BX6D7#KWV{an^LB{Ts5e(?ys literal 1317 zcmbVMZA{!`950kG#KU1U%5;&gkcf`EUdvs%Hg~|ay&IQ!$PN^A3(#wyg9h3jZP^{f z7q&#)#cgiJM5B=z-=bgeWgq-7lQYvP2AzqE^JQi(EJzF!U$zAlTp2`eQU013)^9AFVu z>;*9(%EN=70Y8GE38lT$>WtpUO1kP2Z5>xyO+qw+_#4wnQR)X476-kG7QlZ!nZ+?h z4&WW$s5_eEL7&n*Vt}@hmUd~RUt(muu>td^SxBG)OT^OZfM&Ak0KUr0!o7V=;Ml5) z)gQpuoa&6WV!Unu%lY5K8FGS+ z3E_2HPzvCEmX%})B9%(HQgtrf=p{&oVQdbHazYKKIjmV?+NqgUc?JQPlA$CmMb|K! zQH<+DRse^du7#i`qtOjw&0J3uOc{|DlLYB<6RK**wW@7eG4QV&n`)cw!%0BIfT<4| z60Ap}Di4OayS^b?5qiVkZz!-R;((w_Ln_d$h!DWx7niKaEJc#E;N$3Ukl{m6;CMG3 zrg)#1;@woZ-kayx49kVQTsX)D1&ZTIGDLA)$QKg4q{q$Idl`=}kBw-iC2A7L+f|_5 zbu4pTEXx}}v~;6g*9Y<)(AuY4y4j~EF}{t)YC056)>Gy^c6rvK6@a1a19I5VRctlC ztg?xH+UMapH^Xo~fwJQc200%S3<{iw;RKGM@pY{He{x1ZX9&AF{?jb^BiMoVcBA#7 zurWM9gA-%G(b%O;T|kh62O>hSJ^g)rIhSihUu~#DZ*4|@AHe1drzxxjoxU5( zd%tQ=O-S>-K|(99{Ucr%z2C?I|W7dim`0`y&UAHJ?~Publa@(ed_OSgzO(zB-A2Ia6Je90lQzw9{>OV diff --git a/toxygen/smileys/default/D83DDE8C.png b/toxygen/smileys/default/D83DDE8C.png index 95e047a2ea11a1e750b908383491f983bf628c18..2630cd1293ba6eaa89bff5c6c451a80d40af5018 100644 GIT binary patch delta 1245 zcmX@c)yXwMvYwfNfk8u;KNv`{q&xaLGB9lH=l+w(3gjy!dj$D1FjT2AFf_CH-y`0CgRGi0ris zdtN*~QUCwftyR}Ahn#${-x%@hM9u#>`v0dW{-3J!e|`Ax-!4w~O_yEiQ)oC!cF#w2fd7lsa2Sq~tGy~NYkmHinzAD1f6rY8^B z7#Nr;t3o15f)dLW3X1a6GILTDN-7Id6)JKI7#J*ir-nvPx}(7J=edX_)3lbB{>PGM z;#Yl|`FOc>T7BAHx%^Frp={nxYu%eKzn{PV62INmtV_GUt(aI__;vPlgOhr3HZ@;k zXP>y2zdc&Kv8HtKk`}S-S!-R*T64deM)vA%*qD*CO}zL_!HOVx%6(ax*yC&@MZcAqU|7m$k#eNlSE|oC;-SSLL1fp}wQ> z`g0Gx3HpuUx|tL9`T=Ot=prxUj798 z0}H0*|DD-1ZpL1ad3;dQXW#GGXscLG$>|~TZdPG4w>t;kVT{b0S-V)QJ>c4PM}EsyZA*pbHtoZ=GLD8GVv{$u zP^#blmVMvS_d@TEY(E~W_qTH5lGn4J<(TCko&M!*_=4(1uO79B+?YGD?@YbtFJ?xm zuC)cWUk?HkXP2joV~E7%ev_^MHCJwsOaeEbT}ySEJ{jBYT{5ir=X~* zsoCLBAH;LX$jHbe?S#>ZlZzP^G&C(*)HG?+rcGUwR?V8V%Bs0-*|cqa>*np-H*a0z zv~3Fon>Ti@T-iBuXKU+H(T>!!2Tr79ojSw8mLSHg%x$bKZOyK(E^f}RFVC&qcAr7@ zhtR#VD<;(eouyjh8c~vxSdwa$T$Bo=7>o>zjC2h=%ykV-LJSS8j7_afO|%URtPBiN m`Ht>H(UF^4>j3)!;7#6%f}IL{A8T?Dc@Ll)qC3LN@@?1N3) z=cT{j@BjMrWJCSI;%z&(K@e2zJH$7FvBZ1|tl(XKKP!Nt%%}?*O={Fg3c3V&L^UkI zzL?M|HA;fm_390&27>aUav*2~{fC)|8nX!|#+Hg{fDJ)4?vy4(+9d-HORcivLgufg z5m**oNHgij{2C{<$%ndisj0g@5b17@&?4eK0N11#AP|!b0Zzp_lsJ=eA*;F!n48-u z0!n|DGINI2@EGt4BS)|?qEoYA*$i^3jx-2 zG0HUZ-gR4G{rUpS&0Yp{2OEH90l@i~TQ{bg|M3*%~ zRu$M(6vAq!;X;6?YazrmzkfqmiLWOLqzp|78j9O6G!`@CT1Cf=M(JNSHbut+U7Cb8 zN^!MQkAQkacjd?+caJt?0)aQo5nTpF5juD^(ixK!!^gW2@Wm#|B17Of#XDKb>!GfP$E*! zt_9%r#N)-ey@5)nxn`vpj3j@{%qmB+;v@ zF?cn5nJ&ghxN#hIRq zb9U_L;{7)>x2NW{qQ@q$+{r$i7a7cTnG4S!W~ zYBUtG*lqdtr6-y#_Lo|I5<;n?@AYM-_MBk7UaK|niq$F*Igj z9Vnmpv!%Z%b2`6lq<8U$BNnSw35AxU`8&TUx|Y5@9v)pOz)z4*hX=R(@@}mwy;UqN zMU!LMPiB_~JnY2d9ruQwyj=F>>{#i6*{{D{$j+A!<^_YLTLQzSHxsYby)ga9 zb0w3DJmF@oS zQtdU%m8bS)w+|ls@a}Ktq3<79X5h=0`d&ULC#(e+^j!LVE%U7Rl=)}#)z$MKv!VWf E0KE?0NB{r; diff --git a/toxygen/smileys/default/D83DDE8D.png b/toxygen/smileys/default/D83DDE8D.png index eeffc287bceec2ac29b11e00610504afc8524da5..a923db8cc030eba3fd9d88af71ffd3aaefa95cdc 100644 GIT binary patch delta 1535 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 TRaTVip8IE@kfjhUcvi_jRA_D< diff --git a/toxygen/smileys/default/D83DDE8E.png b/toxygen/smileys/default/D83DDE8E.png index cce5a5b689fbb467e38db4b662311aede0b9b220..d75ab6639a0a32fec54b2ae5c10b22a15e6dd828 100644 GIT binary patch delta 1316 zcmey)b)IX2WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DsS7j?1*q%jL!_@=*z@A?iRTZFt-5|WAsUiJ8WFXIwckBYAUZ>Fe#c*RD>g<9qJoyLw;LN7HWU zn&>l+bf+IVl(q9mnC%BQ2Vbx%;juoqAH$r@4C9%$Hp?UA}A~&Uq>5Wk9 zd%ulcu0PzybDULg<`QqowWfrO}7Hw@)l&s;u8M z^Sie2(c-&|69S_a*{7&TpJHNqyFa~A->`Ci z6J7i7L?uJsdoxZuhq5&q%N3U&Vsg;G$Fak1cNs_C%c)k@f=b~R|I5tr@)2R5f36^N zu^g|kN8u55{qWYmyI07Dtt*(XDE~+2+`6;%zn3{S>^5#mU7B-KZT{W%P2XKR-Xxm` z`c7Efy4y5-&*6&|=IeJKy{Z{`M%tZMDnv5)lrCHBeB+%C`ecm4N%BEG$+zNCkWnjR6 n`P^g_4Y~O#nQ4`18u*cP)b*9=PgGRrVqoxe^>bP0l+XkKJJ~tg literal 1399 zcmbVMZA{!`9Phz)>;=RqbGU%kZV+F__1e2D*UCZK>z#MeJ9)XtC9*;9t_L05wX}sh zaE>v>fiZ)a&4H0Hw?s^qu|@rWaq_lJvqT|gGh{Ex9Er{>V~&|2la1N}hkhXYVAJ+_ z{!jnE-~YWG#okxaHf-4d06?1CMVG4Uq%JBEaT~PAOQ?J8 z7>2+_6}i?1Eji`)6oYnNWI>C;lovE22#8?@#B9MZWIKo&5fnD6H2Gz*2K)`S;7Bj?5241X&5gf-g4iwE(HS(mUsLaIkqSDp`1ID)=6hcJscvgs`(sH8Il)-UEfDwZcjzqM$7PTe0l>OI@RkbBw zlfc5Iti(5nLA4&?tqHK2yGI++6jg7C*F{b(3R6e(!G;JMmEE)rQok5NT!=ss1f#7K z<|J{uLlr2y5p$w;s|B?iQD?p-!Lb^aq)Z6ubl7Q>vLlECwNef%?XY5azS&HnG?Ku& zqms--gKWYsr`lb{Qjf$Ec9CUdUi9&NU7`buYj~NLYIp&(mto-Zqgvb~Nm+4{q$x8_(G-qB%h=HW$r-FV18dFk zpJquMsU4_oS6W{cR)&X-suLrsqhVoF8~~u}chjUVemU?!QHqjt_I9*VBl}(*`{`WT zcNcY2FM{EJGy8WA-7T8mziq7ZZ@%NP&cpCE z#E8~ZfG_7f-25Y3b^lWBNLO5W?wxLH`k5A|^Sw#9&~!Sk?7u!aG2H%nU`=ik7S`A6 zp3>K^KSCTibaLk&rpmw8ld`?#8k*Htvc}uJYiLvI=}-2hP7FN=%>9)PekJ z|62>+b@7lXKImntzIv(fj&h}^P@kNgb82d1`E5k#IcU7vG`A(|M%Bjdp3IDlvzbGE zx1A;5Gyaj0sqDIm!JDpoCcQpKa4je^vu&|cC$?clCy9#IqvQEMlsxg#!Lso_+_u@J z%+F5O)_=X}ScZApMEy#7@1GbrR+;3RObIEvDYGzF{Bf@?DHWLh^4p+zv~OJd6}lZ> JdVs3h_YWe|`E&pP diff --git a/toxygen/smileys/default/D83DDE8F.png b/toxygen/smileys/default/D83DDE8F.png index be13deb5224e780baf9d1e9af2a17f3fafef01e7..df2c9ca5f33b6a318d5ff2e3ff9c4ba9259aa150 100644 GIT binary patch delta 1189 zcmY*Xe>l?#7=L%MjS3a&lE-E&75Z9ZH9E$|t|?9`Cx@b1cSYUqd;eX}^S;mf^Yeb*_j%rW(|ab0&!x)%z{$mY zFKUbu86gaSw&Tk`#2TY9LBI|R0mvf*$m9TvXi7E>kc9xu@B!$x0BbXTxU$C!EtGy4 z%wZV}hRV~;Q&y%^B%A5YB-DA~;rxQ;3=$3GYT&@-|0JOuq8lOR93)(7xbU+S)r%dy z`kiiu#C?XZ`3B)vdN0J_Pcg9E3{2$rCzI$*>$!1y1J+DmP*jBMK@a`4hu!E;|{;)Jbb9rak$*!VXn8& z_3>zrn*dV4#03Q~;i$1K8*Rpk14CH2DV&+9Bfhi9!4BXvSssTc4vWnpWQfv*NvR10 zac)KeL6VjMu)VZ8oKZ1`;8kymy%<=d`v^Ii{E(ck1|+D(5OjBxphcU`?{u5JE@We0|Z{lwX83zQ-D7i0XF z)}H##f}k{dGO=vp-192VI8#EXw=aQk#nvh7Jr`b;^>_+*=uU3D^)acRbmxJT8WsCA zDwO7ZU%EAF3)^z_h0k>I!Rj zIY6G^mUipSpVRz=Q2S?-twcbt@<@ne9*=*jG8;7nO zT)33Xxj26WYxZ)zYk$gshvw{l_v)+Z`H6S5mdW`W)SSjm0xIiCc*n~vn)MZGRexjw z_N1uz*-?h>IonmS^{{7E>9!TYs={ci-N%a7Pw(U$XFqLzW@6uIPcM@4x-88Z6c|sY1{u;7x{X@d8s1laQ!EoUi%VK$9*iCqnbS)r;zf{C#zGQ>k4!YTevutB*%K1iyHmCx2pvJN;|m3!0MgRqlZ3gS0xbTI$SGiZmlHRd0tB)`nMz-N{=bqq{J#JI literal 1310 zcmbVMZD`zN98bCSHu`3_skp6fAye^Xce&(x=_NEPdAYNPy|!F$ZHN1Cx#YQCXp`KM zw0BoWy=q+)8eJ~_> zp8u2o@ArSt(XNii>#BEDBM4F#X%k~`ZE&8=Zus8)dr^YRc01H(cN-}?E13Y{6e9`H zh$i)e7?70wp-Z3zL0l;{-e>nk+Zox=ypp5i&1o5kMv#`)Tt<=yfQ=?WzpAs?(zy>X zR8?54HxTtjGXhAfZ6hY=9_fh7BLgz6V6FSmmK*~KG+;|;P8-xMCdXo{ybRns$2f+r zs@MZ8w&qk{v_19f+Mk_iZA{WnHORf})tuAKtq09ID(SG31%Qc?wr_dc2=1$d*Z|5q> zocH4W=U+eh{wq&DF*!E&>I-Av%yk|)vvAsf_-y{MgTKu$?5|BDvxV#rXPcj^{QR?- z#uJS%jV|B(*mH8*@%gKDGnr4WPjvn*ySfh5-q`!2`_ay)ho2prh`KJl-0;)S(>se_ zzD6__BxH8t-FIrITHj19-kVG$k|43Nyy=abtZVk`sT;L@Ncg@&)u+@1aqHH>xtW#T zo$0y#ZtAO3#p%O6TeO*DRRfD$Wo&Y?;e4Uyt;DutlUr|}d0^A>QZah2o?LnF3;$LP zX`6oWNVSV793A5e^Hs~c*bf)KHMjJ*cCk-g*?d8)!g}yu&9|?86sayYFZ9aD)tX+- zMz0|HFK*Toa6cHzyBq4hTbR7t{d{|See=>jC7~$e$^mru@d~8Ejf@Uo9KU_N6*zya MNT@?R%_pAu2Vh#j^8f$< diff --git a/toxygen/smileys/default/D83DDE90.png b/toxygen/smileys/default/D83DDE90.png index 8407d415e4fe21c1ef8bb652dc560659ed1ac0ec..44dc513fef03904635788efcfc02737feac14237 100644 GIT binary patch delta 1428 zcmZ`%doiXvT)(!@%83g@f6E*K!K~VIjhQVrS?dqGFodY?)&oA~-U2x(b zp{1kuyFD;AIW#dfJTWym&KY2T8DMh;*__US(eWufJ-L;^3Ms7u4Eg*-Y-SdGpMg#2M==^R4kS7#y+QA+m zi>Ww*1qAQpjD$=>_DH}))O-aw03b}bh4#X``A0%=5#d3>p@C5ROUXO5 zNL}@Mhi+C9jgnw8UWf?i++gkwYN1>~=`_AM-KB~YtL#$>PBD7jrQtbtMg^gqh>@j9 z4OO?Cd_i?pQL5F-)w{4V!mA?{rtTB!`Zq1vuHSFjpdjN;bqqJ#e3gDT$z#EjjHs4z z*Z4pYCqy>7V`UyotdK%wIz#@eSMDVHP9K zXN8O2TeGrF=VJq)ri00N1p~gng0Ota4zmkL# z!f4LJXf4_g=OrrN5Js_fxa#rzW3FFUlsw(<8P&llrcU1?mjzI)Z4t zcDGp61Z|3nyQl9eTboE+dl^rbDwXj5^!UcH)Oy2=`{|ikt2%kLhpVKgL|&`JeA`JE zdc-w0bl^{TX&*I9DVurQ93Y6g|UW)l?WpDhg$HEh_Zu>PWs@6Zbtq$Ng`fV?$Ud1(FA zIBfJ@cp5XWnXmR_o-!R;Ii9HZZRC};F{-agy0yX_p~BvQtwPFl65|bGiz(n2WjNW} zpbzw z#d1uP%2^F7x8SRt*b2rg3!-NNCu&a)k$ny-{?yU2-s{+X4e!ISb7g%pw!3@nbzPYv z=&FHvH$&$ikY1c)8m9fV4r21e-)0Xx6lgGNzs0+u#qXPA{tf(Q**v{ioatZTRr7)e zs>Fw)tmN9-OeuKMPza4+p68M?!ddgiTAeK*Vl}(7>U?3K4HzEjg~$5i1I;c)1cC`L zfEyU-!cBDb4bl2=GlY?up{X_;ZU%?9Tq~UULl72@4Gy~b{{qHwWnWOB`0EAtjAN=2 O0N{jlv43fciTw}6Gptzv literal 1463 zcmbVMZA=qq96xAQ6mh;}nPqI*?Lfc`dM|AcdeW7)*Gd=5OToYr6^`}_9bB*89k!qh zA(O~t(K)jbBO5cmEZf4{K24)AP!w_E2aR)^A-X7;!vVo1P=W3#P__@m54+sm^ZcLt z|9=1XYs$^uwl?~;Xb6JVTC-?7Sl5T1RdVotb1KMyB~~&QN%_1>@-YGn8Jv763tKs+ zjJ30jvwF`Un*l*Gm%E@yDza^-96YCFLOM!6=LKj8%Fz40jH7~;;8M2C?a?9Q7kUub z?bIQK8XIQwn%Hu8R*k^s*JKwsYAPIBC!&7`&hS%!fMX>F_H&gUk@D-11zrm5L&qor zFQ`ZrI%LtQB3mwO;sqAgC{?Kr498)TRN`t4N#ZFmf#C#-ftyUl)s%*$h%|WVLVz{F z>7wkkWyuzhbV#`*c_|e2`Fu*BO34dlD6Z9NLmUK=3N%v1YLCSDQ$1pQn1N~)5Q=tug$hh>=Bm|1yY9k883<}F_hy%aV=7COx}6k(<0PGKBrOK5 z$qWRe2_r3pDP2RDFv60i33IH3P1hJK#x&eQ6GkJBn~8Llnlu|U>U2V*R?{j|7;E*2 z660~OVY_Z%cL{5LA(k=;EF763dz`#a+!89!Vze|X zxc9P7i@xiZIX`lSxGb&vD$KwUAmPuC>q_l?v0EktMA2|Ktn>&Y+>@ z_)oKhk3a{8w#%&#gyrF3Jz!!4Fd9vldyYU*#Hf`v6!?EBoec){vdssY>W#e(H||wW ze3%@S^y@2oHhh95x?FNgo8nq6T!`$vzUtNB=$g4vk))0ohG?A&qZ>1Qo1 ziXWpVBaU9$HeCGH&fd$HZwJ1tw~pVtkQ39~+)QhOzmImrlx%`H-~Q*1roPZ@-P_lH zy5sCeUwwV%W3OV2lI)qeNBIZeT-?_}C!Be(@%75@Vy^8!DKG!_ZCmTb{RhXUt{%xd zsT>&YpLllD)*#1r|4=*L({c!tZC$gbUoWRmOiw?Glj+xOQe%d$17|lR+#0#s+r4*4 zC3ig@jY&zlQPQNZn;Gtb%`fGZ5U?)oj>a1f@(qc^cJt zmmg8h2Iiuexb@$6cVmYl?;Kjay5asz;QqeGt2xCOHW)lH;2jO@Xq=cx#x99-ge)m( zYk!BnPVvEP;H3e5eWkMP=hi<96@Bri6v%;TGIt$%>u|I9dHcMcf2@|J z*1N-{(=*T0ev51CWXsCfmb5o^>@wEKWkYu=3Key;vwz1VCk{f{r+RinR}#7ork_6_ z`)Q)J&t7_cMs;p9@{hWaOSP$gRdB~6tK+ulg0hDrPpH7=%&OY9PWeN1d1dZrtr3wi Y(D}UE@8!)K$3wqTt2vu)HVWXrhRwT7@wMt&E>PRW~t6yP zrxE4u>khz+td&b)noy38@Con*AjtxNE2#i1LZvHm0M20nm?Q(>k_P~a_JBjg0iYIo z@BqnErBcORWY{~p%Cxirskf%St)AP(ZfJu7!H9&@$c2Ks{$X}QJGXzRiN}YK-_bvw zr8PA7C)=`Z`;XR-aVlF|Rd6s!d=DZ=21Z5)C1Y%EG&)``7$tEfV)=Au-`n$o$@|l)`A?r!zK&#`lsxh&Othe4c-X1%wxjff zSgr`PPXgZb$4?8x6CXy#Cx^x-WjZ=zE%Ae+<7gx zBuQsae+8=w@5R_x!D4mv3aaYcSg%@my@D64%GR!)g5q-5-R_iAw7Z2RcOI1#l$PHw zE`#JfWB8jG(r6<+!0-EyF&Jyh81SBS_hp2vc;el@@espJXk&vT^0D$}qf!W<7QWxx z1KiNu=mRBiI>FZyJ^|O)*{s(h<7We)euv5_ZZsA&2H8Wo**ng)PV z#+@Ky^#iQda@5*mdo|S0I%np;e)y)_<~7Pr7SdMzEGg3q{c8=P=#iMge_507@Ou!) zal6AkVbjyCsEG0jhasB{^P){z-cihC2R*{MG7h1q>y}Q;{jQ=eR9manEBW#MSq2l2 zj0wMMjpFLMj`t6x55(Fkv^)_cFBnPjTQzvXCj=GoJN z2e(dT?5;)ZO%SQ3a4tPLMlne>@!Vu#DnISLTOxb^l$f9w!fCq1PM%{iPLNI;Ux(=j z>!GAO-#T;@v7*l}2sOI%OD#Ft)0h$~UxV1dHODkB z`>PeO^n;1`QoowBVHvJOyuP$B7R1+Uz6mtSP1kzy@Tz6bW0TECdQ)l^ui{?k_}9n5 zN>q3zORh+ho-le~$W34zr9S5QVAcoh$}m5)xdgwq^Gf4inVf6uu4liKq196Tnguul z43|vaXnokPpiUTyDYS<>4eG9Qy5XIaXR``-zi)_!^1;rSCFnwX z2_EjG^=~4`KrQmH^Ewy>{jBgwYBQA@7)%Tz1|AyV_l3jfoSmx9(f`=vzX?-MC7(E< zv7%L1cNk|;QWEJkK?Z9ingPx6F9VNKqb)aEbd%(2^jeU-}e&d&5d zEBpGUjF`i9OG|abDUFROrik*qt30!q?u!~MF#+ar=Q2)d&w#P0ojGfNB{r; literal 1528 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 a10sOFmgkwbsFo?AzqMYQNgUJUm;VEX)ln?~ diff --git a/toxygen/smileys/default/D83DDE93.png b/toxygen/smileys/default/D83DDE93.png index b2c68471d207e4cbe3b1df786dcdffafe416b5cd..beb4e21de0509dacd755227ae099fb2e02af27c3 100644 GIT binary patch delta 1489 zcmZux3pCVO7{4>#jK?Uil$|L;s2R^OiKg-xp%OyGG=_*ZG%-pILL%eQDGZxMD3y1# z-eOuZ7}}zjR~g1L(u!%D{kQh)Ii0iL`Of{F-@U)@eBbZhb8Z$wAd{}f5CH(_7|0)o zKs12hNC2QJP39{}4B{xVowFkV7j^;=6A!>Tv=uWAzfmB66bcoURkHF5MtHMAuEp5Y-01Y&`1JgHNh#nmSovF9--}m`9o_8S!HT+ONC_ol z1E7`je5ks)qqeEz?72vYKB#Z0YU;Sj%HI%7&MwX`uPiQo5+)=XneF{Ab@icXeh6*- zJsYPRU4>Yz(9I3d`N02OdlzSBe&OZY(bnHNtzDd&rj9R+H9_ga%AWp#)pfzzx?o-K z6&jqsxIDeE+%q^dHNV80`zUmD9Qkf#ev~&0IkR&-%FM{@?dvzjSwNjk%=e`-(|b6* zJv|(v!;y%n3&q7H2W*IiMMdZ7QATEXeXLPYQE@>*5iOGLb;`$*aF7xlwr~GIPcQGy z$_y1b1*DRSs)vUsIVcpmz$C)?PRP$5nzPgQH~A+R3}pTOU(1p52pOr?BYZ7|O;eM` zCRX_?t-LAwNsq0l?Pzt+=@0Yg=y{MHcKWdGA&?}t%?=_GG@_%m1W!UvT1%Gwo|_H; z{Ne={cbYRP5=9LU^=AYIQ&6-hDg{Lkp#p$Qz3EEG%)=m>W|gAiGW@zP%UxMAa5d+x z1->%U@2Tt-FKg?nZqRxp``5R%l*^s2wduiw&Gc$^r^YRBbG4JE^+Aosyk;wnv_8Z| z6WPNN-LEwRKiz?kp1xW8WyU?5ed1lsmJvyvIBU)zWlA$wh2hbxs_ZF)If-M}htxH8 zZWV(M-~Bv>p3TF(mAH|HsoZxuK|>EWS!kd=ff~A$t@&y96WQoYeYgvurP81N&YG=7_)Rqr*WCWWzzd^2awG*p&(%(#;i|-Dm=4kT!dYhNR!4D+kr{8I_%B;v!!Dbtb+kraa`|}iYr$Yw{^Fd+TyY@ z#F;Vo?E!Ii#2t97Sz_-jH97N1TbCzEC$)?=gLeF47fR(>&w4Mwb@s<3d)(U2Q9Sv%0<=`$U~ zr#fstBVDXrG0Q@rTr(W+m6j^1fLAh2`^B%VL*gmAo94V6#L$IZDkE4s4=*&dp^M&q zX**;7T4Q_C@q5e$PI%1j^7wLout06CF~o|W+M@S3W#o2%`RWkiz^Zodi;&yNS3l36 zK<#D!(JQZO8)<{L5NfSRJR>D%7SlgM-v{6_h=&L+s?W*3ZDP0;SsW?YMge1NXiD;n zm!gK{vREt_Qrj`_W=?EEf`_xcr;Uxx zOMADc&!lxw+7-SbxivXUCU^+SVp~gPUO-#(p@cPE4$NBv6Nm=+Q4ULfz1jDlB z^5quGZ!lP_C7~TnC_s?Lk;sf=16b?;sS1eM~YCV#%$<<+op?-n>QU6o%Z$Y(YL{$b_TN(hOwbP;U I1HN&81N)wotpET3 literal 1559 zcmbVMdrT8|96xAKrw}mspt9)g@KFuzJ!(%{snGUXVOtdnXnh2^wg=pxclCPE;#6MB z&}`Ez8Pf~}S{IVB3{Lm!0D+qkY1r>B=e#<<9vS1+fpz#8Mc^?J zu}qIVH_C3!fla)?z#5fW>BMjxCP@{(SVNL{DokKFfnwk#mH1+vhSU*D;PD3m+5}gz z&PG|rb%C88DHTPJ4n=)FpUS6J@j?lTYqi=i2SF%-gwk8ZiL_tIc~c?`6ytRYtVd*d z4h}QY4!%OvBf!$kDGeJ02(yGSp9dP+WzfZg<$PF=?-8WBxVcrPAK~Di4F& z7%yKTI6*#&QzBpxyH_g;3j%9&D+LxLg)XOfXN8;L#B55BfE$&Ib?FEkC#f_eX)$O` zX0S1uFw#Pp(lmq#BP>fa5ssH(muR#kMwv;9FdA{(Oc=EmD$PI|2@MvW$Otx@^NKX* zWFmT5pm!Xrc_mh75*S+Kg?yebk5oWTDKGNgQr-ib@<>>*n&n))&$}?3p6A|DjKEeh zE{njs;j#GY*q7+n5^BOgX-pDGct~K{hyejz!)@K9RDen z$PuW(@bX3JgUyTPVK~q+0%#5MQnn3(qL*h=hJ1gIV?>fNWbEZeg9_usxpE}Tydlr*?V!x^?+?Edbo5`z zjqS;-NbTOgj7i&gXKwx1CuiFoLqEsF9w%(E@egk^PagNp?0_mO8%kCd@4?dEl0}avYACnEETNS<6xEqSDf%rS!(^`Y; za~6e$9+&MnxUDf(vAK2cpBdAnRoAbeQJ?k6j!V$So{X%Tyrz9o(Xr5?v+|z!S^8Ds OUv;*51@*bHsO}%d=TJHT diff --git a/toxygen/smileys/default/D83DDE94.png b/toxygen/smileys/default/D83DDE94.png index 3cacb88d620399bf012c43e0ddde59ca6bb46676..b6efd4a47f5029242c9e0ac6cca7236efaa12572 100644 GIT binary patch delta 1556 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 diff --git a/toxygen/smileys/default/D83DDE95.png b/toxygen/smileys/default/D83DDE95.png index 7facea0af5e2560fee58d51da688f97831e4d269..d2e3f987cfda07b7cdce20429ea6af399b96c74c 100644 GIT binary patch delta 1435 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 literal 1502 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}LOrJ<{l zv8$n}tC6drp`nGFk+Fq^v4NARi<7C5i7U(uYGdM<`tJD<|U`X?9Bw)3)O3kSFe?GQEFmIeo;t%ehw@J1Z3owACkt0MQwtYE0~aNz-V}1eOdseNeNf^+N-QuTUU}Rq{=fJ6#N5hj`VT5<&atnakT54{ zi@~)Md6JiV?#$sx;XQq83U_Ot((0Gp5fV1<&0oLn(2G}^HA`tzuKZ7%iysUEKl`0( zIn2MrZHm?@zl^G)*fk5|_m)auzP;^t_tQ%IbsURl#VAB^f2`Z%o7MXFwwPPft)sWt z9)Fp+qH*;{3%R{@+f{zvx+(cawus)9eiur?Vb&i#-4K6kqDR z0+to4*=Ie@S>80&l|8I#i}Q^G4i6KU)~;N>;7T%&oMlVc${*97@3sDrm*wEFGO9Ja zSXIWZ{Gj!OgUPW8Z|!C>*zyPODEi!YJ@xrVxs+{rN6uW=FSxrYJT{P3Nyd6U@55Vn zf7`ux(SOG{OGC(ZzUf&$#T{JR)27Po@u^>9A^mN)0pIM&I-h)mU0FRVE%_XH4MHk2 zWAv(--~EZsoVossOn{{0v#kd2pXlE!eYQ8iP0jxQXX_h+#tU@5TmBbGU{IQIYH|xt R$7fK9>*?y}vd$@?2>=~MDHQ+! diff --git a/toxygen/smileys/default/D83DDE98.png b/toxygen/smileys/default/D83DDE98.png index 1324bd25027cc95e273fe4a35d134fc49e4a4c40..7c80a77898438e60e1678804615642dabfd5281a 100644 GIT binary patch delta 1561 zcmZ`%c{mgZ6n{*jsg#Yzk<2)@vT27*Ny&(*F^;f9C^h74O*5v!kTK4ba?HAwWYb#Y zXslF@jB?aki3lS(T4#q28jYFRY4?x)_K$tv``+(+$M28#ey?;*%bIiz4gvtS8m;?5 z1}g%r9IOCtrLA7}R*>cBlSCH>fXHtFViEwp$VxHO0HIibSqgx8E&z&t<(~6C0J&Tz zdon>Pm1>QMAx$cq1kM!1z5VI|_aUGe*i*osgp@@f7eHX;SO42M|G>X7aTan#a6<~^ zQpgcO;tXUx4-w4wE=qb9CEbgXCkv9!1qpNbJyZ#3qn~qLOR`=|vgah3a}w^HBz0DD zdQ1q_!@#bGJEMn3MeY+~w+S(MT*t$omk}j7j2a zC-EV&3D`}*x($xGaO77=7=o*BNHq)e*3XGgh4D{?w1=M^c?(K~Z{eH(j+EMzPC-t z4`D5EgmY6m%jj+Pkj{hy35)>)uM0LygQX=S=}o)gJr zgN;lV$FEWY#}d!yTjEDJBeQcu>$T7rO_lR6FYl3cL=eKYBo!k3)xIw6j#&k5HUX&CVF*{3O(;8ZQH#`79a9p9rc;bgCZP z8eE;9XtAh^ys2Fw8NCuCs&`00sS3<{f=0=@a+w^Ip8R6Kb7g*O?YS;Tg`)Clj`C&_ zQ(v)AW$c)V&Mpkmc;b*(8p|?vCt+jnt!vKgzz|p2da71NqpN7I5OfRqUMc2Kwb^K3Y8$_lyIft* zH1}Gikn@bV<&nn)K@sw8OiM*qQ=(c@`t#>j@=DssqaXD(=I7%gh`ddi4TC30_;$@v zUpnkV9x++bcGIX6^2z>|cO`|g{~WNju_CMYo}{3a)HO6b9zHbC zH!vtJ_E6Wzi;=%+!ecN#Ad^XtO@b+MVQQ|p(6tUmRBbQibSSyn6kAkO#O2(&P@1;~ zwE_Mm+O;Ca_Z8m3h(b+hd|Kq6el_{(4A%DOHtM7 z9+}P^xFZxMBnX8Mi(jc;!=LhuJm~Kh7Up>Rc=T~O1mcXhbBJw_M??g{&ov|zvlDT~ zf$GKHr*3Mx3x`nf^yK1KJ8`a6RHSGi@%AhICA*^KtT5i*7!_x9h6ZF<<0d8j%of>} z226rG)7P8nXMU35Cj&6SnwS`2cN-a-l8v$EJ8|Zwd%nYB&9T^yGsRQ?Ay8?)LH-f{ YPgrqx$EKrP%oZeoEy2;I@&G07FQC)4CjbBd literal 1577 zcmbVMdr;GM9Is4qGT8v-9NvUjCqto+q%_b-$J(S7#46C;KpU?OE z`#$nX^0LMLezW}q0)c;G0+Gs(0iNeoU;Z67JVx?E2&XY}ddAE-NEQfG6q5mcQ#RnB43J4%mBK$xoEAbf zr4**gbyA%T2U&DNJ`42u%M7OcY?Fc##=i^2IWbtg)evn>%q z;}9-eDSYOXQI`zi3=1H+SSB(_VHiSDF&r&NQ8)@hq%b0p@;54iqcJ&(Au-UzCFHHK zlo?AUv=g@Yl~S0+aW+gMaX1`ehfK_{nG#r`PzeYb`wk6 zIGV9S9z`;P$>o$n-qUACu-J6E=fYO|M4*i^X%UakQOF1^>G7LbToB zvH?jdurs-=iO+|5o|nwW?&XF&K;9c{IZN|NA#(`ElxqQ2E|E|Q`7dIMrZ5DCQ6g52 zYE=qc!!Ojh6xAYltQ^6mh&D#<)p!wi94aHU1fo{Kum({fD5An;YPCWgizB$!%T2V} zInrtZUb{4JcY>>VDHp?8KynOgV3-_l1te!N9AnR7Y!I$Tp@=lvN-+-m0#ACLot6MB zod+l_%UGcC_+s=6>?;r%q9WutE+cr)VOXWmq6#&v&=PW)OsdcbC%DxAiJ63VM&c=s z{}hXNi?2Y>^m*y?i|5S)tbE6?d~0kdIus!g_^e1IR0ij@n(~!NhLo9SH$Cps9A0q# zhay?(f)%F{k^?9I{B{j7tGzV8E}-DR{`)g1(^4%ki2wUiPFti{dNF>^mbv#y| zv9ETpE83ylpL=2HxY**K8d>~}ZGO+5TtQ+-tIM$cf2 zVu!bsCUBDm2dQR<>-vo^qMoewADFYUtBfHEDs&?bH0@H~YG&7t?{^HHU#RPDw3S9~ zSWD)a_B~v>{`8$Qr`{SJ0e2fN&Y2RY>;Kq&t})z|@*t$8FZaytI_hHY;U$OKM(MDj z;-?4K2W;C{-_M$KQ}cULIs%+!-Se+b8|$95B=T`^+6>cV--i0_v1K)FQ9-TS9tHn6 z#jn2l&(@GNtF98AwO<>NX(iuoSbp)sa42jpUga~gs9oRDSE4>zxz5m55$p;#2UK5n zMOTFf?=DXJZtD}V?rD+vm!Z1$TcdgR+bSwbZg$p&?BAU3?)a_fX40y_1iI8+*@gru z1N)Mz(v?FY|Q+K^9!zZ9h8|dGGT|cSGb?HIJr_#Qffzvb`y} qYAt|_kvENfUFYKK`@@VI{iK5H&RgGXi>9JHKkr1%GNMVH{?R`>aat|_ diff --git a/toxygen/smileys/default/D83DDE99.png b/toxygen/smileys/default/D83DDE99.png index 9b0d95cb1fb7f92a983105284ab65233c6f78d7d..6a965bbc82e4016593dc0901fd30cd11c425e4f8 100644 GIT binary patch literal 1546 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 aP>hB80xi!brD*Zk&)1%n&Fr(ftNsD2Xe~hi diff --git a/toxygen/smileys/default/D83DDE9A.png b/toxygen/smileys/default/D83DDE9A.png index ef64e61b10af6c4e8d65995d3d588ef0674a7dc4..93a79872f54457a3e8997667790ae5ae2bdc5ae1 100644 GIT binary patch delta 1342 zcmbQo{gP{fWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DhUIb9r(FdspqU$>>Ztk+Uc45zp$0x3AHvIiO_u6*z|G#b>n!@mQFT;|n zS3^!dIJt=7<9X%NOBmMOyW9Kn@7_L!r<)iiUAfM1@x!sX46l!?o|w=4|ANiV4u)IH z8NTW8e>W0+tjTwH2Gg_sYDZ_W{V9arFduIUSX-gA3UBi^@f>Y+kE7;hUu&}IP zV?SwTl$4zh^t9u|C(S@#36})C+X!NCd3Ou#vwRW-iI5C|LoVfkmmyM_2bJmEzPQSh1N_j%doESb2`~CkH= zRoPvlKQG5{dB{{g}t3{tmaGZZ7bs^S4~uIs%B4Ka&|ITPIqYirAMoilf~k{ zp2(7T7{D@n#?sRVJxNGqS!K*rNc8Uikxk z@2vl8y^_0hbD_G8~i_n>2e5{j8g!f!{VDFCv zosViW+FgTenJ+u=Ie7(KT2%0pdFQ@~_YVY}Kk%jgM16tSn{yd`#}1yHShGD=Dt28< zi}xyiWy#GV+nvSkFm`&+tL4_^Q@noNk%?n2*X-k-Mh_AjD+7$?&e(G+-pk>+l!C1r zYuZiYTe43tMErV`;^7j%ChtR3;kks`P3CPDQA^2FoiDmba4!kxZHa_QoJ!h zqV=JEik`ET#l(d@scOy(&$#~hzrXFURd}9F`1`AI+01{`HJl)+)nIVU zLn}~dN~+{yuc@mXV*?F>q^EG54dY&1*8Zx=&FqWZ(&lUOJZ*~&>rES9o-ugV{q**! zEjq=#v)8_T^X!C8<`)MX5lF!N|bKNY}tz*U%)y(7?*r)XLOA+rYre qz+j@|!&NX1nqVEd`6-!cmAG|0kckwVsHo1xz~JfX=d#Wzp$Py%jz|Xp literal 1438 zcmbVMZA{!`94{V2$BRxfUKg;Gjg1J`Ys(edaI(Ahj-Bp+027Q^pxhOxaM#)^-ti&~ zaG1?u!oHBOxEXXKaSJ5IWOF0zK-q>4A!=N3i)@798`;>V1Lfs>4mk7!*$11p&+~u! z|NZ{&J(8D`9TprL3_(zsc^{h()@{D$nIQ0f;gOdEON?SHRtm&&#l^`yq_>G>JZ!de z6?{I=*=pY$;WHsfRW1}3E5(-mv{kgLIiHT&ZI=KVf-{-w(Z zEZB5Nk=CNINDN;o?5mUcg1VeSYh9IlXs7 z$+mJjpEa%90+J4?R1}FuQJ2f5b`fe(u0S!0qI?`Uo(eQlowW{ybEi6-iGBu_cUomZ zQUuWf`xv=0u}0A$z|*x5?2^T@LF{m@Ckmttb#oGmsWqtG?u%=FNKN#fh$%cH2z#BSO7C=$BYF4z?*m;LyW_1YoqP7V(8pkk_%`lKA zJ;fM-U|=+)31>32IHSQ$=~_R>W>|xfCDJH^p2ZCY3^U^TG%8Jx;YO`iLl`g(>&KcM zPK9$=dB0r&*j>j`PsGxU%yWt;7m8xFzXS3rMMZR0iW1Bekno-&!C@0!&SYPC)}m#3 zS$K=LnPkxpujZE)HnC6P1g>Yb3`4NMa}3i{CXzB>l!?_6goZLA>sZ_W$r%cqL4D2f zpJwqNfe!R-H(DPE8^gmpz{JR4G+unKAq9d0zA>}e?DR_iMy>_wLC?Ys$7^_-4V zwRSyQoHz4#L|aH-I?>M!9FD9A4PA;d#!T;I25)7L6%qYW-eJ!W(l;diu|vfyKl~u{ z!@`3-2_th;@v7)V)x`X<(EAyqF~{FO1)V*6fUuUneX+gy!DpvOyi;gX%=fdi3Bv7h zeOtUbsjdEJlvoPbm7XW_W#xI~5B%Pu8>1uFJfGegIq~54le)1LY^gKQ+;DfQZeVEG zcyw_1>ESIiJ9@CDyRX(CJC`&4-GSGxbpKY@)>{AA9WXr8a(v-@U&$-IC7(;tUAfWE zCwS&wKbSpv^4$|X&s|$leJSkiZ}0eaHgsDl6ij!QG7-aBBPZQwKv%K4>_1 zo8io>b?a7|SlFytw-P7|N1D1oa~SG@7zk7~v<=OyfJj|OUsJ~bqKKg?gken$LqjA( ztPMkv_r043Upzi>?ZO_098ZSMc!oSLpauVb-C~HcWXN{yefgIm(~Y4uh9TR7;o=8| zP;-W4hyNFB{{OoD|JU6Yk54kBIWsgy{{H^#_s>r+pPXd~GG>UedQ^Y!;=_9v7#!6Z z?DVuPEPzhYGc-xbE&zH(dY$`6AYZB^$PW}co~+jk-k*MP`q^`@rx8-}3W=|tM@7DP z68%o68Ys({21sKV#?PQf1g3v__YKfvL7CB%&lJv0R~`C_gPT zCsm=OvLIEVBDcPPfx)78YN+?7I|dwgio0YS*;pCd2oS%Xagoo!Fh}vC8|ZZ&zMi z$b#EzCY!~}vakNJsyg&AN3)W}5gq3;y`t}GtIZEP-ex)@rnhlh{qDDV8{Hcj@~^o4 zP<(x-V&!u6eB%jsAGPfBD_H5fJ*wmI&WDvulV-J~?<;m;56ErOo zM^mZuD*GD01AphL9b!tk-But~qb11j;wVS+ZR2eA$B&{GpYv#3;SHb zXE8={W{OjiZt=&hWBOelAUthd!F)COKWn5SUvJJ#c(5+fX^-1Yw^>_Or=^~E-cY~h zrf;CHhPz>C<|zr^i4~%2i`^p^rzlRJVR}U&xXU7nr?mFl6Q-YB7785SSR|H(imx!d zpq90ue2U-k7}MDp#2PK>ga4i!9X7Mbf4Sh&wj3%|9P<5AeFlibf{y?dnD^3>#3vOb5^c-q&oZVJwc{WBpeMg8!@pZW{qPZxGgsnH5q15BV(JY5_^ zBreZAf4rWrDL|n0;p44~65e;kaVuzWtWXd;Rr|l)$e{4)IsKo-lWy{U*#5IgXFq$X zYsi8<&uW_A_Pq&QnYHU!qD)8Qtjt$x?WHQ}S8}7bTh1{qd{DLTeXQ4Y>5UKA5+&|L zeo+vd$LpE3?}oMCBn$pbHJ542jy`GJ+A={>TaDjm;_bP4_3P~=`i@P0>GQMl6|;Vx z)T9<0uCG8Bs+PD$l%yne occEy=%}>cptHiD0rC>T6P=h4MhTw@>a$LZ??ds>U&MBb@0Qs^j8UO$Q literal 1398 zcmbVMZA=?w96u~+<_d~_U}a?NJXqWmdN1XU-i1=yyLL3Qh(H+ggW=j9?FsFbyJKyU zm$igsY%F4;%RVULhb>!XHb!=Eml&TEGVv)LQ!Eq#Xb38|#Ui|~MNna{5D>#wWcsrU z2rT-o$Zpbuc_OS36stO9p{}F4-q+FMqy31j0xpj+fFLBOJRA$Ph7~4eMV5IPuumPM z2)wMKwpfuBry4yqFe}LdOq$FkJ`BfUiZbD)Bt_xHFoEF&ih-Ld!AlvEVu&($^+JF( z+2736a;{ZdK(Zo1RgEwx8jVIxQL{;s11L_@bc%x@N`OX*5)Z3NzfFHbM{iwft6ZFvW!Y&-C;%L z!#*KxR|IxfvGilH3@Zz~D#`Vd)SB*qnxLdgN>GZxY#jw}-z|pyQdHTID$h!^oFI#P z1;0y{Lhy2a8F3x^G+`#}9Lcg~4tS2^cG^YJ4xDyzq}hzoPGlA9|35iHfiq~TIsVfu z=_AmAsqI?p17U4=gfN&G8H~pHZ!9MvDC3x$v)9Ld_AY9gO}A}df5K6DCZl-1DDNbz znT_I9L*%xpTW2h|(9|?PyXDixyKm;`Ohdn2zx?gQ$A=Cc+)}*s$GuH@PglUD?|x0M zZ+qq!{o=!kFGg-Yc<$O+ja)ERVAzKZi(lt-3?1SRTs7=ZMk~M6dLsEVO9tJzJNHEE zE^*KCozq0)3zrCNF}WF%6K@*}Y=jvj-i`k`lz7JjU3_%<;eo{LIpdh&gZ!);#GQcw zK3B_|x3t~W9=w;hZQC^n_1@_W4iLKR_?4Oa+X{R8`?7Vt6;JDWOA9uA9y2_7(bjV| z@pl|fYNHrNbbCLFw(*uXx(vFpvhp)C_ny~{`$kV>1wPqYF?o&LlUvmA%Hf5du2%k< znOl`5b(@3QjVF}TBVD@54NFp!5juY=5d5mCShSP*0wUf&di>nb z?9`BTVqd3w5PmU#!7zNJ=sQTI2m3B{x`w@j2nzR@wr7rZExg+K=zbpy$7^11`0zMV~g;45G=yq0fryNbme*miJ B@UH*> diff --git a/toxygen/smileys/default/D83DDE9C.png b/toxygen/smileys/default/D83DDE9C.png index 3fa33303366aa6921ca8c99c4da1bc88823d0703..bbc00b865c5663692c15964d18f62b369cd4f9f9 100644 GIT binary patch literal 1628 zcmaJ>eLT}!7(W(EE6aKzbE|nP<)yWR=5_9dVViA4WteSV%4{(&D}`E1Q&OLln7q{O zJxS7yRVFk-7bRsaN=s4FEv3Z$_3_vJL+arXQhnLQtG=-7*mj(%wSN&FRiq1FF40V9 z=NsyUBjEvzVDA80VcEH=x`x8?^G~keG&iyRH?bW>_9-bbWfvDX;OzQX-T(acJE(*wnwJ?;*{uduxF4=JQ$IY|zpnn?^EbmaN|i=C ze5awQ>)G=NPE1G7Q&%sNAB~}aRtJat0-47PiyK-y@=q2+y>K9({si5EqliKM)m{KH zQZKKr&dK1HJho^u^4oRI<9_-F{YFHFdP1ogpV*tFxQl2;EU)`)k1=1dUpICJLhC;&~X6SjfYq z@r3|5AIcA=^z>o%)k(5t1h{V3c6sl!FSqS?P9m*6Dqht@yjO%8WoNN1yC05v3~dRc zn6|K_PHSokdfPAB7;`RZd-x{~iI?B(<6}8@ikbtGFVvDd+j9`;~ekqaQ!Hs_E!ASSD0%PCV%i&>mxpB z+t1t-{5}ZHM4wh@Wpyf9qV!Eu-MyTzZM|6v-@`xh=e)`&0xDHgXGvtEX)DaDH{&5} zRcUan?iSiv{E|KQyA$SSRSsYrs|p*J?L_GgOE+c=$}@k|Q6$EBeACuA)diwXsWDAg zeTwCxYy1R_cR_iDbWZ=~dThuV>2A?*i|ZEel^4}KKfD6*BbC1Jc!jXUZ29D6G;27i z8&l7+kfa$r^)9Nubhhe^Ik87gDO;hU2Lu=5PMwWa$a>=FJ$bfTD{lidw{=yn9h_CN z?w02rnA9`hq4BPY7$|8hKEJ-X{6K4*pfa_WV$(n70%Mx*$5>g$5yF0Yeh>`K61u^< zr_C%v&)1c2K%1WP9=rFUs`IU{weuNoJUQ1-jtmyU7bPT*+g4 zp!X_|yTMv~<8Gj*53=(5Y3IQu0kJ{*3RjLj%`_y){oKS#XOo;uhT$=NhgA{rKj?zb z!OG&#K8n!g)qaqz+v|2VC79Jg40G50BdIC#~ zwWVzoJI-o#v;nMb-b=@tlyPJ(bwP)HMZ$-pZS$6$3fE{f?ss-i>163lF}mgvlu9$D zvWz`|IzsP64d5_Y1-*ZM(;aO*GkAt5*d#snsQUB4{{FVhBXYG|VY>l7F}Y{5p}ah5 z^k`SrCxk!`-qd<=tCbP_b>Xwx@3m6&V)*0cv(f8~;K_&3qqd`H0}9-Zzm(5%+qG-f z?-m@3g-kf`^IfCN4v}O*by|r?p&~9@#KUtEd5{7)EDmRfb+TLQ5VRJHU$-9b;An%z z;;~qFmq*Bd0r&!LT(tPV0kr7&DhOcmD*{sxElOo4@&JJ=h~i<$e0B_v!DDm9+go_h Pmk$7$L?u>uMeg|r_Hecy literal 1658 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ8b?Pn?g>Q=>r|34@w+Ji3KJEOo1RKJm~{D@XV8%2h1@= zz^w7*>JL>02BuY>E{-7;x0VD&dxQjv#6CY~Q2s2-YJ1@sow*l1d^HN>mVFIO^OT=D zmH!CGB@X*v8Xwe+E@_-NvBj>*VWEpw4_DKPUjaR5HZD5Re`L#?kEZ+5uAHe{^}X!< zTEn@|*7J7nJ#Ur%e)D&$_767w{|$ehVpUABe71ID52 zOX8<#VY)L+N>+)lnXeZ97x z=3##Q=4Zd;mhgQ)eABf1`(){dCAtcCbC_0NO`3g>XKk9UTTa-PZCf2zhVXXYo%Olp z6u;DarSAoL`z%8LCF{CPig~GMr}nI?X<1-+IQ#4CaVq(jk{7HOH{H5b;3vn{U7lAK zY+k(A_IFC8q)xt-i>4LxtPN{^t;)VP)z-CZ~(#_o(k;`i3)Yx1&oJpQSA ZfZ_57hRPLBo1cPe15Z~!mvv4FO#mcrc<%rJ diff --git a/toxygen/smileys/default/D83DDE9D.png b/toxygen/smileys/default/D83DDE9D.png index 7c4a4129867d51870c4c82a582bb1928dfcc9337..255da8223493eac15d6305b8c5e4af5af104d37d 100644 GIT binary patch delta 1388 zcmV-y1(W)%3zrL!8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0T)nAR7LLd_a{Pz9U>h8zS)Xmh{=IZR;h12i zyS{39oF_tt*xcaN+1<6dzL%Szn4F-QouZnaqnn?jo1dhdprxLor^?OI;^pV$=IGSe z+``Gv|NsB`kA2qw000tnQchC<0000YzcA-P@l^O|{C{iysraLaO`iY&00Cl4M??b= z3{3I>000McNliru<_8W6BshvoCGr3O0(^Q@SaechcOY*@?uw65+E8HB+Jk5Kja&q=1uHw zJzJKT!hhlxHC;<+)X11)vsHELx7)3&LOzF6Qq!uhR(#Fk2HO_1c*`ZRv05&^RS%4C zXogLy)@{>b_iVOg3v7&)U@>XoI_|NhQ71$|G#;IC*s?2L<|C4~8^~_l;jbN9YV)-g zQ3ls@h^*>nrYqtpHW^S2`9e0CTjd#2#xwf_+7)KVlw$+q+DH1;~yS=`_Stm;mU-2#4#l zYJUgkmR{LsCqm@P`=6b&6BEh7rqf;8$VbBjtvLLXm#f77ozxC1SGZ4?4}KPB)#m-s zz`@<=!m_e$Stg&|M`!Pw;MvD%VmF~3E!?fSVhl&&-MsD^S7*yYZk#prpvn*_j~M#; z9LWXp6)*t=%>iHLSX7wlKqD&Ai3KU1ihnhtLu3l$36|J)1eSr!hBUThkhuDRJin0- zzDQK;%Gd z9VaP(hXv+r$#UrACH_Kz963P#4VjzszQHNS*ai>JvhyVo^O!m2PAkp?8)9&9M1THb z*^m7uUv0Qkzdi19?azxuu5X&N*?ud&q#wTm+k~o%A5vv?MmoCxOXL@$35tcvg4x>u z006Q{L_t&-(@ls44#Geb1wUQfy|_c6z!ulyQlw~c?tc#!NMI)~lgtD7pw>8DPPfP7 zb$?r{V}1TWFcfClPwNelXe^#crhn2I?%hU{pUdWWLZ0UVRAx(|hy{#=l2FDNz*?!+ z>WxPIO|#Vku(dl~vDXttG6I0z(H~I2a5SDwXY(H*NsHxb{Rd=O-fVZf{o$x6r}Oy& z40F9QlmMaI4N)S;{f;P+y}CY literal 1453 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 CCKmDl diff --git a/toxygen/smileys/default/D83DDE9F.png b/toxygen/smileys/default/D83DDE9F.png index f391cdf3313bae22c56f1516da89ff77ff9da8b5..af0cff5c3450cf0482615cff8ffc1348f0a9b17a 100644 GIT binary patch delta 1217 zcmZ{ieK6Ds7{{M=)v{_OA-!%nPG>hOa-y8Q$Qdse)=O{fvR?M=X7M{DT9(^$D|uP( z%xSY0TPk{R#7YAIlxb3fEP3Xt9$@mPI05# zF?1k4zT^k;yw36!@%x%voBa=?wNltFJgfM2%qCOk|7Wi522!fFlNPXR?0 zfd7K*GR%r#P7E!{P|bqo%g~nw6S**51b^`UK|#qvo?=5$RyD-k6}?w3SN9Tghu;0Q zy6A+u7=I@RJB(6=^cWRb#L>kbJB!8R3{*VNJ;VU0#K%&75O;bsffErH#AF2$kc*r^ zLJXS&V40TV>6TkW-n>3~Fm{7}SJqLy+K1m?FV}axzT#L%q>r;>F#0_^ndTNtYjY+a z@A@eWqi%^vPR{gjZfaqTf%%V&2eo68#^v!>Bc60|8?%Zz>}`A}C_E`F_4Ms$Sw21_ zUAY7+QVrqTOGem({wu5ohC1;|4$YJ~eQ6@VX{LB9W-JDorT5spjo4QZmz?;jUHj9S zl4}-zp_+ja$A9UPTnK7BAodqxhEJKWgfh>o^rkjRfbUK2j7)w4*>j@Drh(73D< zJUSacNNAbVYpru{UpJ}C=wn#P$ zBT^}&bIyk{OcGNI;O{l z=T0{4duJ%uo^T9HuRph@AY?8GXJ0mm1ttsQ6)hIiL-(;&b=IZ3Cz}nHu!+Nhi_*L+ zUg(3SINDLDI|e-;%xO|p;Wl{Nfr{2L>{I_=$dlP<6ONrb&rd=n^ zRNcR=$^Ya4k*%v<9alW4YP3DjOGn>Z-Dq;f*r;ZW#%SE)Mj=5?vxjO~6&^b3oA#WFNfTJ+J@Y z@Be;pb+*6gb{=#B0Nn95F^T3H>#K60@u}a-5}G__Yq!~@^_qD}haj5PdLS0hN_{X1 zrS$ON9Bcx>-m9d#&F;iYoUCPik|pCSWOE1&KvQ!eC&~TL#Cl+#qDJsvKb^ucB^|+! z1`}i=C%}x-Hlo9>k@l24(l4`Vy!i#JslXwEEHovokR4DBt`NaDcsaDTjtLyw5Hb5B z_@+_aiB3$=bch9gey>bY6vi+M9H(G-O@LnTn=j7NgXo z4Ve)fS-R;$HkU|j6RXBnpisz&f|MgDA4z1hmR}pvhM9!_nz5s_ks8iHA_)y`NS9GQ zdh04+6uXBjvILPeT!*fpq(}pzCJ$wyYR1I~j&6KuCC$+k#fV{^iA7nV6>WHdWMZ@s z4$=Zi$3nph$4*$%Px*r(KP}R{NKvgc?-%%3IMB*a;Q%WNbOjq%4O3EOSkbE>y<1r7 zp;%7Pp=4@$O49}^70{W{OwGt>IZWtcu!f_Gn%45hvsQXGy%nLZyav-TUCUw{@#T~q z^fTdrz>_S?heg`*I~wJ~Y&0tJ0hSkemch5M>HiZm0vSVC#qpnFsT`pSw3geYk2c%Q z169;9I%fCTF36wCV@aNC<_BS2B4SGO+W~^!U!R&J`XXEv% zxdYB$o~W{2ww->4USGU5`IpOaU)>uhKB7L>StsXhc6+GVer54pSNZKjWQ~2vQTqED zxOx4MbMLA>c)Rc5nf0mqJ%?-6>8VqhnBWGr^Tqevp{43C!TDP=psl?A)%5gy@#)EN zclA~I+;mGy9NN{f!2&zc%!S3Ndl$Rgeu>M@} L)^_nCe|+p8^ZUVH diff --git a/toxygen/smileys/default/D83DDEA0.png b/toxygen/smileys/default/D83DDEA0.png index 6987fd55fd39794ead0b9f04eaf0b42faca0c757..82936e90da0ed809c7ace18c4afe67ee21454bc9 100644 GIT binary patch delta 1351 zcmZ`%c~H`M6#s@NYUoCIQ+bqF9w;FyCW#^InRildc%-i9 zWTd4nWMZ4rR$E(^PMK0`d1UEoYin*R``bTvXZFpTdGGW2%xC7kH*a2lS+CqepaB4I zz!-)>Mf12H#Seh1W%^%Yv>!QGtv)&$u!uzu26Edeasb2OwF0UmFjiHmJEZ5>b!C_;a&bPlFT2bib$9Xj;On6|@ zAH|V-6@v`8X_e_M<*r`bJCSa|aFaXYQ2Nk)K4JN_cDL33)p?Ql1^;ruxxp1{yG~in zToJE`qD}X|RZf|X_#yv2q8-tnpp>ceXYmijF*g**>yj;}n_CND9^n(asO{|r_9KaN zydsQ%*gCU1Czd{85hT8KNz)&T2ulMCpuhq1*d_vL^#lf6sc-VtJz-*b$+ax_=QbmB z-P#3Bt@*;+ZXNwnWu#PbtMOTitD)jU6GdpY*(aiT#3(EV=HL?Hb-4bBk5}LC!F9EI zb_XU0(y1s^WiF*veyyS`58meWrN{X|;a>uu_OBzl*!9jfD+eVba6-chNUUKO?b=@#ABj=N0+zGt$AkC30;+nTaAr zl%jeAT+D_~BIpgPA_t&po+?~w#16w>w#)*wMvVv;>c+USmbX)2Ym+mji&p0w2x#5% zM(>BkVKw|R`DKnJT+6@1aGRC<_8e`ad&7y&ELGc({nO8B6~He4_UOqf7>_qtcMh$C za#=cWe7Dt7(+Sk3PQDT!V>KZj^?p2KV7XwO)oPL<)NoB;6V{W*4f$1z#tV1)l<2W_ zYb0k^{KL3E>3ouEw^lDa+{{MMd~e|_m#Y@vwTa9qiboER9HAGLk_diu^O5l4F=L^x zk*1m9=2S$%QO4$NC~UEX$&y;2^jQp$CG>W-#QEt3#jXaG4kI1wh%HalOz*ItriEnt zs21`kXHmApduc`_-Quax*4HmQ(J5a>Z0L&LSmJ+V`!7k?Ll(J@1+x}t&9&UErNby{ zi@U@H4g19TcD{tvGc>X8tMJD2@KL{U`d`0E-o(b39h8k~q|J~(t`RSDa(s$5KydiH zuAHHrzvD8a+LGpCv62N1Z_f>2aARY*O#Cqp6Dq(B>*j{RdSYC0bXP3ieFq-r`3)9} o$6_z+Q@P3hZv;zhmyJk71u+~?GNK0Yuf&P-(SC< z&-YzlZq9N|*t{?R05q0N#-^^3!Dr?S^}X=#hpf8HRWb^cJh52uuo4HD9AXg%T3qY~ z&c?BhvW;C_8URcy=JN}b0&6yD7hPI5sH63|+$tIX(w2GMti6O&z#?t~FBqYblcyn& zcNn2HIxAvz)0~sfESI>v@|=8od5PWNfR?3$X>5~mK7WKuNzO)mh;Qp z9Bkucag$_M>ruQY1XgqRbVET!)f;l9#H&SNOBvC=$;AnZg)u_v7p;SLkQj;*jGiLQ zCIg+J3KWeHW{lSBFdD(kDY_8Hldw3VCy-<`g~2EWMKdrHO(W)H%8cvu6w1(e2x}2! zg%#{v$S$wiox~DP#geqdv5F|=i(+Z019F|BBFawD4bpi8xMU44I7E+}7%b1DXc=SyNrVxXH(lb~vZj*`98%!pK!VMHd83-ax8gH>nXU~~MZ zSwcr@2L`u~TVE9(4-Y4(6C{(p z{2)6$p`klfq%yO<4218hm*M-vr$&tLWiGx2)HPde)%j!TYr^2v;CVr4VU^BGzvZYSKsYF$ZN=HC&IHI?jN|* zy>IO{|D5#(KWdDRGPh5K|4K<5{blTp%unU)oUyB|U$p-oJ$8P}Oq9QsI;UmJ%8z4G b$72hDJqriA2VGJ}@b_!U$YI*3!peUDXUjB) diff --git a/toxygen/smileys/default/D83DDEA1.png b/toxygen/smileys/default/D83DDEA1.png index e6abeb1f4d9f8374ad6bbc9bb12177ebd128aa12..9149416ee9f62b4e12271bdd2173d610a4ceb528 100644 GIT binary patch delta 1428 zcmZ`%do_Q0l>=uFpR%)kLe14 z3d)xr;O^z+1wm~P1S!dX%G=x9&CSij!-L6WDv6Da4GxF%_4SpYP^h_VFq;b$--Gc~ z@cx|c`+S=Z`8G;2mI6#oP0?tym6a8RMq80zvA#O~rZz!L_3MtEC%cL9@FNVquqp`z+ zj*9{$;R2bITb{+H28Q~C@<~4IBfe~A*m3&NW94VKrI~?_?hIEN)1E{jc=1I!eM$o#3LTXpbv~Ix00{uQ#eUxZ#6NZ|6d7LKIyG|BU~FqlFfe$cb8>Zk zs_V2<>XLG-a?Hz<4CBL7Bb7o!Ol7!hOls(8nW^$L`_yS=R zQk)zgg-nW#2f(SYl*Mcs!Dwzw=@YWFH77ne$(IH%r7y22B&8Q!4|g}D6-MtjLcrl2 z3+q$1tL!%Rh*H7u}E`K4mYS+S9A@)uWA@UR@ahl~auz8p~$ZOH=R5P#n zxHxS>aMy3F`8+S8F`_Ilg?e@S3rToFx6d7E>31gYRTm=dudz+@phOKKS&+MSp;$

wcTbsT4{r_9x01j7)lc_r)yqK8#O<621~XeRFtrNZfGT8>F`|%J1x4HUBq8e7zu~B3cb7!pM2eQS zC2tWAfkBhTjlEdnkYdL}QRtPPiImaU(w7v=oqlOMhu-Y)&W@3(xCbd_JkU1P&l>Vo z5zdI9Zid$QJGGRSNN4eao}`+1NF+f}bnsKH?da}db>04^FPYlA=2LHFqV8(xbd8Va zY$%%ct}z~r#NSF!=}VaFC+#1+I4NxLd04(U=O2FAH}OwWZQV19=U;KT8!CMsdF31P zAtlQHtf!Jm0Xtttg!6e(5kz(2tZ`r8mEcj=*2h^dQn4Gb+Xtf5Ze$*4AC?%k6Aj22 z&2Fj_M1X2v^ z94!kRIEYaRa!JuZds;*r$})bs{A+eu_N{V9dRzBke^~FBEfQgl&^Okr-C>}&M`e3X zL0(aAVZIMVs)@17&B@EPph_sTTrfljnq)(bvPL;vdApg|oge>V8X@x`Zj#kNLT O08rh%$@NFVv;GFB_G>x- literal 1513 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQ#&rIbo&`bc{YIaUdlYm=G`pf|&5659GizPih`8#}omx z#^389Hy9Y0Bt2amLn>}f3HHyp93b;AE=g!wkN}sh$7>B(jc3jyh6x?hJM=rIGf!;OIep&Zd3N99E1{utmzUPqH##>S zcW10!lC?F;`TEyo0*`hq%Y5mgRM>drU5l$mYloTfOdfBw$&1&$ZrXEyMcC?nyOFu2}IQ?i^Zq}=P`eu?G_fkWS*fNEGGEUllH6uzE1@IBn%{=YhL?KLU?y?l7OERH<>uD+?b zyU^g+L#y+F^~TK`H?w+h$$s`r{N?ld$=V5eJc6F{4`*7k`38R6{5Ltl+H97Si4^Ph z+>Dhj8Pe?nUlzP)?E0U&*>Tn?rA6);+Rl^e{<1eW%w*v^h?fUF6N@_KoAsi>8XdE*my3c9^0TzN}`Z-!Z+VUnLEq z1-|+xJPyD4P4CSNm*qa6Kc$M@h*;X4HeveKo$=a>!sqe+o!Rzc%ZbQtC-av=y&LCm z`LJTeG~V~CIUnENXF5Y-k1n_Q&!XS6wnTY3uRJotWYM9`u@7r zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0MbxQR7KL<;nCaS0By0*+Tb5| zw$It$KZLw4E-q41QqI`kT9n1j*W7oe&t_(3%hubJx7e-4+ke&8*ZKPT|NsBW)Y`Dg z;!^q3=@$q6}V*dXA{QUg;`}@e#*vZ=K*h$8~@W-pE z-QM5$`1r=q*7ElH_T%Nkw6x{t=lc5k#Lv|L%FlD7qrV0N)d&LN4Flv21LF+?!_LzH zthE4}r2vAB0Do?HXF)=+0|2xG0JZ}FwF3ad%+dghn!?M`0AzZ>%FyQwHN|k@^Tf}zk}Lg=?eF0@{yla zgSEb28W^~1m=Lc~wn3A2)vWCO5G-rnrWNFhG!=nB7R=xR!?c1>6sj&*DP~6XMg2iW zP=9M!cFg?d$p!KyFi;3pikkyt&uBRx{Q*<8}|DVm=ZY(Qlca+ z5?9}l`v>v?n2*5pf5-rh>aHM1^ukZ;}BIFIY!OmO~}4_LmgUr+*wIu^QcoGf4EwJr7UX#nE6RaQq zo$%1i!4dhpv>)n?zFTmodVBP8p3g=?*PZ69ws*ys^zBn18^zYe2dM@-84mB?68Zz+ zRf?3UuL)!T0056kL_t&-(_O=f5<*Y_M&Vx)Qd+K-TWQlq3oX)))ce0uu1quf&VL-> zAD7GEz~?h~2!)g=u?5J*lqj)fD5XR>r9iDP)moh-RA`M@v(=^@+NC|JLGKTSqw$Xp zV=|r17lhDn229IZuGSlpGGRN8v)%0vu6tx`c&GE_db>aTzz<>`!e>T^=#>%S{pSO< z5FQ1?3e0H$001R)MObuXVRU6WV^nfwVRU5xGB7eSEigANF*H;$Fgi0eIyEsXFfckW zFgG^p9smFUC3HntbYx+4WjbwdWNBu305UK#GA%GSEip7yF)%taH99phD=;uRFfc}I TQlgO|Ckg-nNkvXXu0mjfqMJ)x literal 1314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YAtfpbJ3$ zHaBo_a&a;;HgYvKvoteNg6d5nC(QJLj?o7t4y42a69T3{5EGvCfgE_|NzDW1m?B`- z=xCP~V_;wm@^oLzgUC^y!)g_ql)n_lqxajA{_x&1S$kv9>?uuzjSWUddASWz3%3+}r}sFF9FA zubA=l{{R1*nl#KjJ{e|C?F&6JdM zU{_IB|Nr;5xW~VrpV>W_M2+X|sA--U8?j*PliOWKU)lJ~$@$^*^2s`F1?eL!7M8cw z|3z&2^Yi6x`}g&U72l*gCfPnV@LZ78mMp%m_SZM=j(xUuRT-zJEk8g1|F5sF?fDo& zcs`ljpSoH=y8HhA?-Qs0Jn&_H`_ze&LVYXzewDuv(uk0hvrVq}-}vxn_dWCV`y*~w z%iTKuX?7#$npuLO{P+LY_q3mn*>tzQ>BN%48yA^GF7uen3(TG-b4HSX-Y$orPi!0p Z3~%#Ji*7osCJ8FFJYD@<);T3K0RXFW&mI5( diff --git a/toxygen/smileys/default/D83DDEA3.png b/toxygen/smileys/default/D83DDEA3.png index 4f29601c3c4eda0f7d94e20711a1cfce4b50254b..b91a9fbe5414bb76887c348b6666326a24b0e9e7 100644 GIT binary patch delta 1365 zcmZ8fYcv!H6uzb;<9S+9QVq>mVVD_@26a+K85)(mT54&GS7Z_sYMb#WW6BcM$|KE; zW#n-N%P<}dn~;^5vq>#_c#g+GS(;U|v;TJYo_o%BzI*R??mgeRwHhpq?Cs1A0APmI zIi-Y+VTUM(0BW){)`L}*97T2Wq5veB07m82r2T;*I zam?FQp^T;sz5uXn31d%^!RiTAbpnAloP+ z+@{jpJM+nC&+n8g^NQ}gXe%fyIQ_i)>hrEElPTmcRMIMTtHRk_adN-nG)ckZ3^9Ba z2lp;n>OuV+@CIN;QORWAoHo%MYO3UCCPMuzyj1wTomzM;hPnnA_zc}k(7gz~vXQ9) z#cUr>QVCs)&@vBAW1|xTP+9>^bI>>k<>aOBc2v%J93=r;b*Gz>sKt>fu4cvi?$;FSn1gHQN1rUbtfOV@wY+dr%B)X2l6lL)KEOwG zt$9RjK7NUav8$3tb`Duy5Jux~iUPN&RZP4OSdk#pLy0AkBlcKEf2c=;q}^X#KUcr4 z>p+98ENwIUDc?prfYI4|m9+eBQ|*r9YqM$hUSF+pz03X8N?_Z});5CFuIXT|bnRu@Uw@BL_Uo%QZ35^D_w?tXtXC+RQ!S zU^J=F`9aj=M-FM}@dB{Tumih&(K`kah?Qc#^XBlahFpA>d!Inp@_{^Wqv~u%2jNkx zu10?V}_@xX-+Do ztofJQSvS-L#PvEWzl<+VBUyNS56n6DOlwur#4Hk(=gG)ro|&_b*nCmbJYB9 z(-#9BBl#9D>9(VNv(*>lz4T*0R41pJyObE=7v)Ahs>>L9{!OX1p$a75!vZr*$HwbqnPbjig+!=nhT@J*%J*$mx z%*!meZ*ye7c0a9NWC(6JJq)k7GA8KMm8~11S>0S={4p=kv3`}CGa(treDy+n_D*%? zyl{|q+@VJEwfh@BCTkil?1(<@awsSPgw`aT}MHIuy){ z-h0UZ+$*FA#A_%YMpy~|FA?c0y!wVyAZkC~s(jHRGU<@_HYqg-r7dLF@Q`c|Ar~pU zL?;jkChBx@tg!|Tf6-%UaT!mrNljQEB-GZ%E+aX$sE23?N>IsrR9ej~|GA1^!7MGa zeuA<*7Ze^6#iK<=MMZ^#T0IL2j-W+TD`;Uc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQs|N%#)f2%rQm4 ztZ{g|nFj*{<0DTO$B>F!OD3Pq77i3STAx2FK}dVXku6uQx!!7u4;2xL{n5Bc?3L4< z4sJ)5>3K1tx9lBUG}pPf1Q;mjJ=#=vBuV^k1dCzFYS-Hly9=@=IjQURPQLPb_JVDh z$IB*iw?DjV{XOR1&gYtf3#S>ZFpS|`H}{>#+|DZ}r~aLIW*5&n(XdB{!kpQ5o$va) zX8nV(c)ot=mlJ=SYg~MsPkzhW`P=udl=f~t_b&9mmbu{7$KQ8LWV*0$(~+1y_bj8R{2#ogdo&+(|2!nf(RSc@^E$svx{hWx^(XsQ&Rlx&sM47y z8$U&IeLgDj<#|hcW@3~|<=9y@!W2$9Ylhy2x)822XI;FDj)tzaR4Xk&s6SO_^(z9DkSXM0X z?wo5%u_oruJN_JB_2)Lo$gX~A`(G@9 Yp?byh&UcIz;h<8~)78&qol`;+0HE+C&j0`b diff --git a/toxygen/smileys/default/D83DDEA4.png b/toxygen/smileys/default/D83DDEA4.png index e4ab4aa2695cdce37d3dbb57f26595b91bf51c10..1f1861962420d49dbd40e7819cff82d101be822f 100644 GIT binary patch delta 1388 zcmZ`%doa{{6#o(_i^Z~48&tWAjf^H{D&Tihby#zjIX zs;gpIH`b^rj3&5$R= zN$M=oiwHo=Rm8VYX)(u!yZd?pkZcG*8Uui@;#L|TfD1SPmO=m^lmMU>TmIC?9smiG zbVmQ^bVmM!D8PyWiGCRIV$#@$9G-A7 z(gxm(f>ZFTf1mm^9JW0cDdNV#yad(qCZl12=>@1n9x*L12M1ex960k48j6JH)8WNT zcqs#3N{0nd?W1&1P>V*(>nbA#&`9_pMKqH-Rp&faWxn3$|K$yJdot=!`y4ZVnsrJbilM3b@f zq*yXGF(wuOJhSK|v3LTviyNP(P24F3jl`9|7~f=>U;6fI?C(PStprV0fTeTF7S^*1 z5>@F4qsHAoShA8@dV8p+PCro(vTmc6$t}c8QQP|El5FKYF4Plh_-j>?T*@C0zbxSc zdi}W#niO?(w=U&oYg}QwZ6HfSVSE=Z=y6XwHzq#UK~^fU+U6ND@H6v9a`HM9y0bN? zoHMrD{Lx}v7QVdfmR@~F?pm6oo2oR#8$s!A}*s_hP6O#O#dj5c(cD$4|_8>MVt4SM+^uVnZt7N<{HQ{#iJH0G6($AzuVPm6*tFL80vg$ z)C?4C-%d|&&)zN6Bx52*noEmVIvM1l3G{0Q4Sk}h_VPdIT>qC(S0Ur5>-*=F?`CU~ zjwV9zt6LZin8X|iOk-wRfRRJMSreaq)FAfUdgy_?17kF z!q8x+!$6=^B0_3Z`fyeHUp~fLXH+EIAl@lH&Z(j7^JqRw|7{;%A}ffyq1yhKF?mL9 zHOD^wG)V8uRQnQ7ukk<@RsDi$Mz5`g}n%hm) zckh@Cvaxy%=R?ksqQfF7)X@3}gF!4MCOqow>NJ0Dd3K2+_|T0tpIMw=;R)XBC@QL> z_vrPru?A)+O;v3r4HYdE#t4IKb@2`+`gj~7x}EeotzURQULwf>HQ(K(St-8CfbJGR zCxz1e$%JqkIh0NYmN-jGGn}oN#X&y{9Kp((U~O%R!x3<}XGz2IKPkwBnDe9vO49!e WTK6Wj6zUqu0^s51?b_@dlJPG>rEmTK literal 1413 zcmbVMZ%o`|7(ar{NkI1jQ50D%%LWdv*K${`9XD*RcW}TlyaOR-nw4JRE^uvW3wN-= zCOVO9%aSdI82@aZxCsP;%0RPhEaH$!7FptS;B+n%2xNo=G-C+U_kcq`kbSUe`@YZH z=l6U5x49rcD?0L-NR37lZOdjHVBHdYHg5v&t@D4gV2M*LC8|>_Q+=$=Ybcjk%0o6U zTh2Rp)>T_|h0oAv!pl5`C2EO1kK{zJmJRA?{ay*6HJXe}zr=DCyb6`_a&;9P;es=FLm7S&5O{f&h5X)1K_UGnc!ie)``|GG zLn|t3g$Z7Ds>EIZnMIk04BE6*PKTlpj%!i90mso42-Be$q60Ucit0%NPGafM+64n^ zva5`AFxE9&Kr+E@Rh38t@%emOUz%2w%Mp|yh#&`sr2>sqrB+Z`f2yD)h8P%L;bf1b zdPD&VGP0#&wQ7Qar>h}&CA)o{SWwmy1yY9iSqVY4I>hS@#oeIsTA8>tZ1CMYT^ zaD2$F2iRT1>K}?F%`(rbqFgA7m7xwOaEq#_xJ3yvJ8>wf*dw?^pOPFb&uX*`FMDcu zmsJ+M&`N$u&j$8!quxyG2!b{;STJsiqKyPaF|?kb8JfW1HLUCZ#Yxj_2Je(y3CinREY-w_&w`z2IT}r?nNi73xx>IW%>#6F#)SMHu?b-=jywcE@ zmiYRSz~LjY&n?VdnEZ8qB7r~3W(G#jE{S1tv%6nBUd$)%jmcQ7(EPbHbE+~n;`@fb z{N2|&sP{ikG8rCxrna@8eRR(gchOxthDQ5)?l)!CfAodBe&XsIh2=vXyAC+($+)Tb z?tvQVqGX70TAn5^Rvi4L|GHtW;pT_uZ=tWu_C4OGo|=xXO~~JNbo{M?u7N*>4}~`z zUb?eipOLJ4JFu*L6??i9`QV+_8=ss*(#b`nwe+feUv5_WVp_xtzX`+fKfTss%e#5I zH~hj_U|HTewcyO_IVK%IJLI^!y)Exfa61CciLbsn{oaTWH8T{I(&XjtOy{I_Jl4DU zyIVJsJLl@#hQ7X=M-};=Pd{_wOy0{oyO>hrZF0-tvBqyMeSR9Y%v28ixT&&hV33@+ zEO$8ew@ejTFYnC!GU@ug+^3Q$M;G^qe~!b)mx>0vp@jQWPabTj86Rm~ocwvgc_>WN W6m_`0VLr1n_+zwL@|iArU*kW5J_#HE diff --git a/toxygen/smileys/default/D83DDEA5.png b/toxygen/smileys/default/D83DDEA5.png index d3d6899b8eeef1a4b7ff881f055741f52e0c217c..f6dab293d738705d3c52ab6a1a07120d245df84e 100644 GIT binary patch delta 1342 zcmZ{ic~FyQ5XRpSjwUgwARx#wfkMPU><57;feI*hOcVkNSf?QbA_8(WAfPmSM2rvv zgph!OM-j!7!&dMd0p$`<4kJ*4#W5ZMT5(it6ej)JPWxYHemgt6GtcZkJKKiSVk@kL zXaIl<?jF#| zAHd>m=rjybX=h~#dUbItFHtX%G;w(@WLG<)Bbbwexg79PwvGbn`~CHI!KZG2J&UEc zveGk|*1|P5zMK3&9v$>@9M`%?lfdjnP{s$fCMNSZTrCz$^Y#%-%D^f-Ff#&HEA5C5 zR;zKV2q-ch?}0{>FxYarf)x>AV{4B$HA9>rHy~Cn7$6SJ1(7T^z~qnW%vDuN&9X+J zG=GB4AN}OlN#`oA{npWR=jUv{hP#N&l?{IK0==ItuwIH z>C*dkHoE0^)k`y~+a=Ri_ycC_7Ea|VqB-xt9of_|i?`;rE6yK2=ukU&C5td=7ipKTD$5j#U(*Mb z@z*-pBzZY|AwKI|GqXMElx zpFww*S!g})N7aA_5>vuBf1L1;a;K8tSk$NZ*nC1REGf}&#&A>XUBCwWs!~le>>` z0*(oNo{F%*fGDf61kPz=4v(8;ib7c?k%Eb*6wIit-x?S?oo$$mv9_8>tdBy@k!##bAf*+;&P1!vh_|eG~T|5328XjXpRof#E4(&y$Bwo_3D( zKb`DWNo!%aclzGgz*O&e@6;o|juX`|T#F%XOvdm<2;2I5Nu<&6q%;5&i7t9PVli`2@UV)+?$Ub3}B(TY6dLy zQC`N$P;^uMAd>?CG7nqf5?zjB+|BtkR8&V3^6?TH0CMs|Jms!sM6jCivVOCA=2D*; zWNEXy(%^s`yp^e84>k)-d2>mHySdhl(dxWhFeijd1U^Qjz>u%bAHYLq^*S#u?W4z9 zHMp)K)|%BDPPrUqpp_FC(4f&}xgi(^QB(u#4JZn~03r~KXd&rFvtT`LKykzfZeD81 znm~JSCu!fbB}ry=jVSWCRvQckH9?(*6TDg&!>}j^f@Db=S%D_MNQJWef!#3%k_os4 zmKRyh4@McOYOX;vt0hl2Lh$hp#}=_au$d?+W!ezMYhewf_4%T4t!oEFC-bix+iC|Y zns`R*WCC1+;Fjv)*&PE*xqGyssG{TzUMjFsQK&kSb2s=HzgS3`)zTLY&C)mm!zgJY zP`d@Q+9ZLnLZ}_Fnhb~)LhMFEjAJ{j(TM6HlNBKm!VbeW!~~giqz=`aOb`JPHY|oM z^an)B?`C3lS;_7u7Jeiaw+aj;azX{i)x|oXtcDZ0Kn=%()^ZfgtYrN(7Yyu+mS-bc zk`dTOhPDfw4_wbL&TeBLwdkz`gkgkP;;De{!akoY6*` z<3G(3JCZsuy4`AhN!S`5#xG5bAdSZOQo0=g;(jY6Eft}ksv~P_d9uB2$J+^LHomv* z*!O9T{Ep8E@FY;(0D8h7FC_1}BcFUGe@vA&K9{~{z|=bIc@jK4d+%1|OCSDt&ST=M zRN?c7`XEtbeOl9xl(Q!ta-=Qkt`~Q-sG8zrHc=_@M3Ti^Z)iYdc+Eh&wwMhKi0BRC*EU*Ci;j zxN@Qga&}M5KUE4PO&X_I-B?ix?MhV+_Of^5&yK3{cyM)f<>mXs%TFkh-?S-Hu1r^d zGyZ)4?9)?kWuCaCP@MW)NjRoE7F*80hCuTRi2O>+;OtyDA^vo3Z(QqYq(!66xYj>% z@uT*M>j&bG4jyPvZGq;8&Hb}W23hz$TS9*X%MK!!S0Y{1_1oVnzPz8P01rdSb@K}k zyj5o!y4t^cW1lP>sa5o~uF_Mt2ZqmfT^$&?(4ICl<8{0F<+;wDv@^V{v%{hpm7n|} zuXXi?uD2drFp4$PcP_p?@#i03DO9;F@}fA|yItLJ&py|ei$v}w(`EJb6fX+Y!_`xZ zc-f(JQ)7Np&nMjeWMGE^__<(+FV1d; PqkqyuTM2oNsOtC!yaf}J diff --git a/toxygen/smileys/default/D83DDEA6.png b/toxygen/smileys/default/D83DDEA6.png index 4f32bd12d62a19bf126aa926952e281be1282dac..a5c108f9d5b49083ae040becba6a032eea5e1a34 100644 GIT binary patch delta 1382 zcmZ{iYgE#A7{`AYh=QV_4i!;AMVeVCq7BnwlcptP-m>LRqJmjEouN+CQ1gP}oiG^{ znV6T%EQ@YdT6xJ;PR+{YHJeK@Jx5m^w8sAD^lC45&gVJLd7kHd&vQ9X3%U|rVI^7z z0HC^E_FdhSuYNuQDbcP>$ zJDy11?Cz;RA{WqT1qwyBr_|J6Alg&hah7yA+?Hf7t8KI;lASlX)|KzqjTKa#skgBu zQk`9Nk61dIXL01Fcuh?9f?dJk|;Il z;QehoGI>WJ2WO238Y$4Ef!WHks%lfL8OYcIdZR!F^H|YwLoyy<5zu1LNk*>MHbHBd z34^&Iz)Ut{G&Gzwtktd#XBiJ-MY0)qY;GP@hehxF-VcZo1b-+%3IjN7=<+Wu%t?RDJ)t`(Y}du4NsgX(#M3?p-b@xY8uW>ClL|$FSwpYJBLfsji5K z<>V8D$7lPA2!=i(wy$R|A7LXm#)zKw?cca|b8=`8esL&ipgnR`Lk#%n^kgLATMLu9 z-b<|Oej`szn%bE5gi)R29Ig;?JWVq&#)N*fIl#|1)N(j+;Duf;uw+eF?WR7OpnKIe zR+RYlQP)1(8a$JG?B94zj%piv(bE&5aR$2Q zWAD;uNx<}KS;NYo2M{p-S2rgX%Q$*LwY@9vv+^4!2?W7JV9oY_KEO;{aDVhS=0fvyadi)Mr7`IYCd1vCMq|=wd`$Pv|0oiZV&erF Y|1U5P$wpL?oKOG+u(^J%+oJOR1-DpB761SM literal 1452 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLXP92kyyB9?yyR4vy_rCJp?Zz+>a}t%N=+=uFAB-e&w-_YfQ}+o7WNhhZV(RMZq6F2OLQa_J10ACeN*qXu1ttVcfgmP4=>s|N%#)f2%rQm4 zta0MbR&E9c#yg%ajv*DdrUd(XNIOc@t_~C0vNV)mDQ?w|;PCZLdv7=!dO5C@+ruH( z?HY1nk%H8g=}smOd!(YtTX{5pH%u+*tz zbssABygzu`{P3D23AQtzGf&R;GjUjtRco!x*;Qt=9*p??ZvELwA15aESZO8hh>_5#XH3uM z*K0a{^iY3pfqFyny$zf8wCp*QUQsK=)YcZ@9upAKbG}>dWi-3NG_(_ zI(X%ll!z*0-T!@Gl9?x+X)2udN5PP@?V#b$ADn83)B`RVFS%wpd0#5?GUH!ed}3EG zUVD4sS=s)Sg_^Fvt}*s>77M-3sHtO+`L-fh0*X7@) zl^^(Tc46hJ>YsvkIxZ9De40?O`}y~b1vhiexGqe1l;if|$Ly4aZ_8$XSp1QxX4NXE z%O*cwy%deFYhQ2Wowe0N^q=3J@&}cXfvvAsYag&>f5X-o*ncHSzveaD9RB(4Ki~J2 z$8MO3M5}{d2blMusKEVrK*&DIWus NpPsIMF6*2UngGii5v%|J diff --git a/toxygen/smileys/default/D83DDEA7.png b/toxygen/smileys/default/D83DDEA7.png index 041e7ba4795033509271982f529c21b503030114..dab4659f02e275e91ee172f8a9185ddadccc2ec8 100644 GIT binary patch delta 1254 zcmX@kHHmA2WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DX*jcrXe zZ*Rr6HrBkG%dla7L}Eg2X+g%zvu=Ox*mWfc`1(Yo#0EdxulxJF;_`l{v+WFmg2KVx zb`Ljl{W!!hr(Va(!sX67ehqm+MPZgxbqr7Y7%IFOSXfw&R5D2Nv2@jUbpai?apT6j zckixTx$@ezYd{}J_TJS6x|6pg$PXAU%Fdh=jGd25l`rO1qZR`LQ(aX^L`hI$xk5ovep+TuszOO+L8?MUZUF;>Meo#5 z$4PSxc=r4jt!$c@Qh(B>UGjr&sO7VtFU5>E>l{{-UU5j_bk@qNW>3GLkAKR(K5y@f zSobe0>~{V-8$BaLJYUZ4_p;R|%<{J17HF(3TfDT@EPCrLsjDLU?mkblop#{RvW+*c zoiS8epe5G%;)3t0dy8KS-Q9R0yy>UP)cbYcb*B`!a-29;W56H#t#f*HTKy+Mwr$ek zH?mu=T+7Yf$}w4bqFU~zm8ZIDR{3#-xbEHHn|134-)-5jla&)23+t;Un%ZG z+;@Gu;VIX5$xjE*C%w6~Ctr$XhqQBs$*F0%n{Mh%-K+1Ea{8`|sbk-(v|GLgyB2<2 zcI{ier10XD#PEHk83A6iTB3P&*`*gX)^OT5-{hLfwBA{z#QUn6R=@;bcfa7Yr4l;> zRo+hBrd;5e!WE?Hsb2V&xALEZ9m99!f}i#&4MBH#&l={MYtp-5rdQ@^4I51HRjsZX)hsQkD;Ai%EhKrq``N5>rd zof#(&uYRlGQ21hkVp%mCZ`PBOTr%Z+eJAec{&viNy1U8x<=et(rz_-Cp8i^5x%=(% zN2h;%)4s5_;MJq{D>vrqxPPnn{K>p#MpxAn@9=D33T^asaSV~Toc!SFV{ri&C5e_6 zSC^wl9<~THw=AijvZY6-XTs!!3E=@DL1B%;pLkYfy^@+0Rp2z+(LLP%{s93u2}$D_ zGiJ?s2OxM9=A z&4mR;MMaE^=GNx?4CxQ0b-TNkn*m*;TH+c}l9E`GYL#4+niyD;%3x$*WTb0gu4`x# zVrXDxY-(j{s%>CkWngf)@*FRUj@FVdQ&MBb@04tXi ABme*a literal 1351 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xLa}t%N=+=uFAB-e&w-_YfQFDg_;^G2yJ49~^Ibo&`bc{YIaUdlYm=G`pf|&5659GizPih`8#}omx z#=6%keGCkY<(@8%Ar-goOxo|wlqhk0fBE-Ssw-Z4rp@l=W4(BR|Io^X3a#7P+7?X8 z=s2)erbivlW83f7mA5Jo98oA zHvGZk#qI?hqA8ojR4y~ITrspS&OcQV)o$i}Crn}HBgI&Td-J%aDW~qJn7XmfS*&|n z;!frhdHdLZvOOr?uVlCSz~TLmf2cGZo?}_Pf^p7m-AgAs8CAmaZa!w`D{8)R`pZH6 zI;l<1+qf@U@Uyr$<(E0c3oVcq3~=HwO`bk`L7jSLv7T7Bq6AOS{PXQuTe(~VEhMJc zoz6B8;Zl_0+rB&U?};EUMNc8!sa_$kXPHm3JN>x-tGrl&jQWaIU1^&+*Ba-)4wxRd zYmLJVq1ig8(|LS0-#=^cKH=4#*8K874Ur%n>rfrB^E0>BaT+lADKRdXu=CPBP|@b; L>gTe~DWM4fDn;0e diff --git a/toxygen/smileys/default/D83DDEA8.png b/toxygen/smileys/default/D83DDEA8.png index b67f8109df52e960d3e577ea1c19365da1b126a0..3e7d2ac0a7dfa8ef9ae4dbdb0af7eb2d9b6a6ad4 100644 GIT binary patch literal 1872 zcmZ`4do}=&dKY3V21vrqLAn3-o5R{kR`y>TILPJ8+epRz@4f>MAMP6(O4QYNb#xA}Sa9>E z)GIF!h{fRDJCK$J2n1ki3RtXtG#bRm!x!-OtrXdfyKojba$zAD9R(X3KqLYa6JT-@ zwEpdF(9|TYBqRjX)k*Wt z&4K-Wu($|XT0nI44v`2-N&uHDwG;>do4rA&ze6A|GXX>&1&57E6S~3AHrml$F0kMZx0rghU!M zCM1G!J2m}gQt~y%b%N_<7~RML(YKQFi%U~jIpk|$L@!@h3AeSsx;lfyjUy2M(be6C zyBiq+TU*PiP!3h%xxyg*%gutdO9qa5xlOaK7RFvKR(8LKRq-10@c^m zKhxjS`%=K?d-4amdZv2E$M~7ukE%Jmjn(OCPL@1(r1saY_05^4q{OyHlTLPVhv3t} zVt-jKxBGNZS7*#vrE|={hzDxs6C`bZB19DaPK}5FG0z!Dq$T(?0b{slnKY_=g2g zFQ?-}-uEoda34(!*PPT0s19z{ouPpow5QwYHV&ud-3;1Lm7OyAaUoPb zZ;~Uh)frN`^uTQf--0gD&w$*!G`V=UYnguSy{{}stk;S!p+$?_%8v-m`KUIDP4<2H zIOE!yqJLpO1&)T+w`9;Bn^~WkIpOm>ctCXy&u{U~#}vmOLZLO!-zw9e`MWX_-n(NC zGIh4-3I5aZbfxZ(mf^~!H2YxC-^p1dR6LjEZf1I){U>uXExpo^vlyS56Lld((ydp zh6|3m9VdTS%fMUhn`AzQl-!5AFs>bNzd;|ppqsOn85vZn#*=xZZYgJ^N66^m9huw8 z+C%)jg}ew-GRdW)bYTdsP4W+aB;R^Y*&CW;vB$ zci=yjeWUzWB~eB5%Par>7@IS?MPNk#QZ@T}XZ>VOk^jw`;>vC;E8O2n%9dfAdl7p~g> z=vS8d1Xl?T3lUS6p5B-j=_V2E6ZYW2_|w@4#msM9mY_joWpz)x*rjK| zoJz0^{)%N*7Gr+cw;_Uv{Rd%j*1$2(#Fy>=sfHKI|2bE-=qmAwyLVHVH{Cm)A}cEk z-D<=oE%iV}0byBn#1^vIO|l_@6q9TssKFQ_;8{Ylfh8mxlO+ivwxSk~ zdX+kAN2CafN0p-`d?Be#03IhIdRfrli+VuE)!Reg{2%siv2Slr)iwBXyKbZN?cQEt!l1F$Q%CuEf>a z%w?DHNCso5j!-31iSjuD4QXJiT{=v=!9=4OjL0avNv+Z26p)Ok5Jn-`Q+Wmi2(1uI z;L2HYlLSvCqL-WTxaF}b&2qhluLYw%1|sbOn!tclYQSztH(CUCA^3_{K<{105D2_d zq4YxVwNr_51t1~KIKXAH!!#@y22hjTp>V1Bp=L&PE&mLg)d2$M-s2@1=kl5h@h z5F2B(P->$FAGAx*b_cNhcVYz+Gp?pcvx+3s2RlHKN>ZdHl{5j8I1~s;AdFhlW|{6P z&+BM0+)P+;t;|dsfLHky5O1;1N7#r6<4PoKjP@LcMSK~`7sGrR#$~fvd?`49)&8HH zL9{cFt2zGDEQ3dM2fDUzTAvo)3=eLkC&o;VhWch~8a;867)+$HHx=jAk^b{XT|86S zXT5K4&`&$zL(WR(8N@#G$9<(p-KuCi;3XZGRvf6{9}{;KDzm!xb-BODZCUvAW{B8p zcr-9?YS=-)<1RgG<_E&}~mjA+h9CNc^4XDPDogcZLQ=UH_SE{&W74 z#E`-p=|#$k8HqX8z6pMt0wc5Hj}3ReM3zja(UP-lI zN_1|@Tip`Z^Fi;->Inh&JbNf-_>Pm_d4KRokJ)!;m0@-JD=Ie@Mj`f!N0!eIcxUu( zEIqxa)4gtb)$CT!Dpt^*%pfv9Ja$K)FE{UsGdFkkt^H+|sR0C{Z!E?a6|i1Lb~cU( zD)^>jay?Wf9J6s#;QE@qd+&8;nEjzoPS?KpVd)`W7r!6s_w{@Jpbzp@#y%j2crdbj V;zu?Q^WwOEQ!&z5?5H>?=N}y!$Hf2u diff --git a/toxygen/smileys/default/D83DDEA9.png b/toxygen/smileys/default/D83DDEA9.png index a0127dbaa3f89861f775a098345d9de24274f673..08f1c5dedb491fdc225cc9e7125731ea5b952790 100644 GIT binary patch delta 1209 zcmaFC`IK{lWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!Di zJ5X|Vf6@QU=>J7Y=k^xl5y{r56IwIl1lr^SD7%M;r(PHag#zB%dG#`q)aV-K&3IlLy~(CYAmD?@<} zy=JAM19W%2SV@pyFoWs8HJZQY$^GaTdH!>P=%4v=|JG`msUAo=0aU`621sKV#?PQn5;Lb-BmDz|>F`5>XPASgue|l%JNFld4csS&*twkz2sPV9`4@ zH1^UQ2acNOB9@0ZczTWp9B;F^>$5@=GBT>%e*OH*UfUgi*=^Iw z56_OO2}iHpRb;p=D{u4LOEbT3T=zPmt=5d!yL9EPr5WEBHQh)v&is6IL~o4V)nfo;dQYS%sp zn*A-RWAToMnUhY{@8&4K5}YEmfa_OluFzDe^BZzE6#bgX{_gmG!S6iLMg@)?#XDpE zWb_BJCTXo+6&sc8mA>VAOrdDxg>xVNe0M(6Z1xw^Yf zlV0|sgl(A%pQD$@rKTOf8g2GZ^gmF=w(P&N+opi>qXi~re!b7yb1%ItQW22}Qf^k6 z?=^=nw17<{@9T`{ijAC=b-5}H7dmguvOd|+E^gDVa@zA`_up0~tKx=z$@St}>Z6Kh z@hbDn$tAa+|E#;D!KY%D%OvF<`4Uz3^e6g#f7h7*TJ=D$s(Q{-n|bVc#>5-ARr_ttUfUGaU1uF?2N3;&mTS=WGyNA`h%5a;+YHL zLSl{^k2;8)m2JzYVN)()OfF#*E@2EVVKgpbELJgN*t(L9)lwo-6KJ7oiEBhjN@7W> zRdP`(kYX@0Ff!6LFxNFS2{AOVGB&j`G1E3Mure^Pv%bQIqCq7$KP5A*61NWjw46;7 S71g;I7(8A5T-G@yGywqpL;-C8 literal 1256 zcmbVMZA{!`951_t!4z_fW^U7MwJeKHu5ZWnuH|m?uDz2(56N+cON?ZteGV%0Wo_{a z!{nR^q7hB@l4+dF62EAi&SjI0=w=LjfDdR$Tx3MU48ae^WvIlFWf?w&JM;tD2b;Ff z^MCsP{r>NLv$^S+s?9q#BM4HJY!K3L^}0_*IehQ_YgvTLb~~Q6TlAcr7fpafWxXAs zNmc9sX&}nIhbBN6K|DDnleM#{=Qv4ManaSm9o2wn1PLE-3{mO=Hrft46fJ_?`sy1D zRpbcP8c2~Tg9n{TL!Sv+`kFFQUzfzn*n$0M*x?|73TzQ|)NakSOkn7W zirp2#R-MYGno(Xi0UE&lK8d6#lwokHF2FEUElQIVO_1Nukwry|(kh9(jAH5nWVD1$YL+c(5-8bKpxq+2 z?!H)#H-TvDW=7Y$OC8YMsoT2MsT(NY!k|yIDw?e4ttZ{`tVSyUQ+Wl*2~$_mmHcwb zI`%^pOUB|n8R7-njhhY9ah6XoampX0=$L>NvGV`P83COk+~)XCvy_ft2fEv})`!B{ z@Bj@?j0s0$VPSCqLCUrzg=of^89REuaqMaD!17P?gMa*farn|yqqlaf%{$?7Zf~5) zzV$A8wJ`MA4S)9KU%su}%9wv+Cof-r7O$VHR`xtPxwCa?bhfsB_Pe_m4(9GI-njSn zv9^mZMV2RhnLTej_4E9uQ;XWjZuLwwfNU5HFWnq};N(DP|JSE?-8}dGgA0SN)q3_m zbmqM~Td&`;XU>JDul#WM#jYWwW~aQP;RWTx4ZXXv)5`d-BU=!9xB(e%e)YsV#~zzo zY8;xI{HQB7oPT1)?T)8}b#8Y^@ zzkhH2r=wF+A$RTU`NG3eVg#u?RLvqsy^oZQx3*Ov`|gc+PWxZi%gT{!@1Ol~`|;5a P+<#9p-Xwe;J9y+DVKT7Z diff --git a/toxygen/smileys/default/D83DDEAA.png b/toxygen/smileys/default/D83DDEAA.png index 808f73cf781d1221b8ca8f550586f803f16df1b2..d186b37ef0c72b0e699238d126ceeebbd796f9da 100644 GIT binary patch delta 1300 zcmcc2wT)|nWIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D2ThVXZ>V?&!$X+14Sk`n(f!+Sp2_W|6^m(!(NXo^;T=M&G#3Z-tTg|SY@&% z)AUZ8)3gxfx#7xZOAS`1o7`%4*q*9(EJx#Hq4vr&_QD2c_c(vAY zd5qHL1f|3E8OqC(^)FYMeOjJ%AXQ;mlHSD%<9$gotpS=#6Lo$!10`w7cNsu2f zjFck;x2^$l8I!!-T^Kr8Wj%l#_7YEDSN3P@d|aw*L57?X3=B*)RUr{2L5bxG1x5L3 znK`KnC6xuK3Kh8p3=9^%Q$rmm-7(p$@2=G*V9(XeEwsSaqRWiipghp zWaTKkIA1>W^85Mtm+k*oT{$Anzj|6};I(!!;Zt5wSGK%7H#bAR|9G;%!FwThf_l13 z&Rx>wKD<54J1EyQM<-|X8&{EXmXu>^N^*OGpPkiOA0{fj)p~~Bl)tg&cYAAfEtxh6 z%Fbh7U;MD3zTe00(1C9sWQ*P`h*|Yzl5l0w;X_AS%hFz*Q2%_$A@vIP?L&u-&QH9Z zurxwlYRCC{-z$f6d5??gx&17UXWeIAH3(~$F4W4Y4uH>?eDcLX==-&MwSPVH!EnWD;F*Z)>Kv@Zt8 z)U>r5_f>gVI(7-(uiUBq=Dw@%jgt1?TtA9{#Ps@A7ZjEnOq4f#@oUye-cPay31a+5)}@PS z7I0r>TeWz5Ln5d49pejyKi$sFZwyx0t#DIyrC&^?bDsPO^#n`i<^P@C48qGDi;wX8 zeSFlOcjt1dN(#?PK|dM)o6DUO&ouaKJ+tqp7N6qQ#|n%pb31ekCy7*49Nb**Z8U$% zo>%c29_f5ZrxsXBEQ*`*px*33hV>~{W({d&?t8umA_bH<`=n&%Ox%C-+va@Y4%_GwFz6PxP#(PuR>A`7q~xn0iCQ`;u=wsl30>zm0Xkxq!^40jEr;*%ykV- zLJSS8j7_af4YUmmtPBh$IzC*5q9HdwB{QuOw}uBYkzzm%k{}y`^CxP{axpM?y85}S Ib4q9e0EvSv1^@s6 literal 1363 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#mT=pY0!pxeM2q-3`Cg(nm{~-QD zdyP)cnw*?g6D_kkv80Y^j#o1JPCQL3zdKX)l-lgWt&hKz-P>24f3LzzbHUfL4Y_9j zV|LC}?!Wi)UE>s{Z~gfS^JP8v{J52K_+Hq{@4x#UI5eg&OGsTf!K2{cjf5Qe#tZE! z?NjE2ygw-K7b(6t;=r?4Ldh+Y8FsS>iKTCvaQ?@(FJVVa)g0IxkIaxd_{pPHBtvk9 zaiT%XTU7^_fS|@p3({^}Ovn_hsa3wt;e4a6N#dve$#@@JVV@#c z<2EV#PP3!f60gEzD;k;Jo^(t6G`(3@;q>Akhx+^u@G&~Etdr!n<+3^7{3>sObjq^@ zK{pG`K0N)_W$1L=qkP4}bAjvP<5iYDkb2ti diff --git a/toxygen/smileys/default/D83DDEAB.png b/toxygen/smileys/default/D83DDEAB.png index 092f7f7de137388b261fa3faf1b85266800cc849..8d0205520708afe3ea28d70ff1d0dfd7ac28cc8e 100644 GIT binary patch literal 1869 zcmZ{lc~sL^7RP@eYa)@&B@C1hDvOXbC>a(p8f7he2*_Bffh?>cgd_zN5m~}2p`pQ< zvN$*>VhRGv$W}xU43@o-MPVwHDF})pVNrzFAOCoq&dfWX_wKp(oO|!N=X~#ZIetEF zS_lIK1VLIJ?#>k0YHweS9q?;`+Z6yCm2k2*8G`CX$Pb~au#cg;Q@kPQtQiEQW`8<&C?*r@vjiHH?FkB-wA@0@qS)Z7tq-WdV9g-BPo9AGb}%vm`uadk z%?GJ;t@7GxMTO$Zm8J6Xg`-FRn>i8*X2!{B+TMPWNc;n1hYo$pFdqMrJ}aw_^jKJY zq{GzoBdr+Br!*QG{((9)TDp7B4L!Yo=;-`UUV3_NN+jUHgY5yky0*k(1&;@Yhe2Z_ zsI3Lv-Ai$C+vDH7337860|TEQIkHHlg3Qe66DL4h8z?COWo6G@UDsHwX=mr<^mHH= zgMxgcnYBq6cm8-=cnD=#vB|#d^~_7_4N~E@-q()kem!eA^c6R@5T842dhKSwdcAm-8qBAJCSc;?IjqPSCWfCB z$HXME;~>cXk|dBUo+N0rE$2Fs)l^1!PQ?Pxz=EU{gUFtRmBga{rl=TG=M#1^#m4Lx z?{)57&14iUb{FByR_k6q>K$)r4t#pe=Jn=MBA+!bjSOz^95wSOG2gwuM_So^@?np4 z`+WK1IJaXuF=55{!{!rhvia`1;Zvy-otFP%-=5k(YN1_t>6FNcQHIRgkj)O{ z89f?)Q6IBAgCsY0{(hmvYDJC+tTk^I9Z|0ocxNlzQ3^3(gW*I?-OX-(eJGZ`;XIwixc~WM6 zR4R-TptO`@3``#I?yzN*fdc;OWQNS~x@C*zD~sy~T+j&t8n><{)mlW~v3{xRt9nFc z>t$#vY;R}9rza!x3(QsSw<+7?{c7B0!1}wg>V$qU1v6-ZPH__MPtVXJXw5{p=M>+; z;P07??V8c6E4}yv6e<}^m6;UuJe#p-e`V?stQm_ys6c zDL4M!=M7b`R`orc$<%#6(`k@0DMg7)HVM^HBhVPSD{ohqR#ukRbm?IX8jYp1OGBZF zK%o$tJGH|@Su}};7E+X*pPO5txeFzym|ET$LwdU$&d%K9j@ z0O}*3et(u5)QnC{Ofu7J1pMx|aUsD>&JhGs0`{oT)Z3Fb3G`AkQ8JQFt1LBhd< zID#F{#+GVBa6CkGwEeRcf#66WkPgdH|B=AvFd|t=|3AT(71IDGptm~&a9I3=(0C?9 iW^lro7!P)6IFrH*Wh8NXneZP6K^`tX&b7yBX}<%x;YRNO literal 1798 zcmbVNYfuwc6ix*JqbNFHQBl@l3nC=hge+u-fO!IvAcRnYP+?6LSs>YvED#DdRe>nc=&hFlO?w;>E@7ev~ zVL|rP)l>?FVjmJLl9Q{0<*~CN->xra5OP^Xh?PVno=9XOdX&Of<2z6wM1$-^al!RSHS~JJ6k&mPh-erJV*~Y91|` zBV)>RLNo~r&eo%m*{N90&rv02UKuF_`2Hd4oPM2ZC9?z}!V6t?AW? zuv{dWvqefgS`tC%Uv)Tqij{fV$JGBjp zEFH>_qXs-(uOjP_=r#`~bN7Bj7DdtGi-M$za8>EFqI6@#V~OybB9~EU_=2~r}72;>VzejGmA5;vbCWD9+{0ydZL!x4)`v^lK$ z|KyB8I>WFu$A6k-{)p^A%l2*Slfv8KLAB(>=*iJ=y?m{SoH!^%#8(*mrIE40rHFIh z^ji77&bn1q$>B({T@A2OboxUZ)RtiaDw;M`Mw{wZ-YQd{tNXQ>e#}u!+$?c4jR?-J zS+tR=$;ue=ANRl5y!+M6WZrJu+=q`Jy>1)M864)NP4$F~l|5+9+4pqhBaJ(6`elC* zEcxq%Iw4^fqpH-!(UD?f1(~1NQcJGZ1nypEDs?X~nF3EwrVq8}^6!j_>4%&>Cyg$4 zd-J75E>bDQ$w|pR>N2$?T)E{dd_}f1B^o zg|oQnr@~?sm6aXWt2=(7(y9a9nw!?YjxH+~#K$jFL=_;`{KgyAg=xAQo!&lB`KAl~ z`OmMdO~{(sDymr0zP(-Y@rtHWjx@Wd@1&`1dJCd*(+%1E9p-Lo`X-{>>8JZvW^>1w zS7RgacyU#C&2*kFyY;|*?{10Ye(&8@7LQl{a%#ibnhQ>24aL_J8E2wA6LQu}H^e_& z<7)e4_yf=Wn{5Ft%ML3;LxGlUdA$jjVplBjyrOe1P*9uW>h}y! zB_*ZQ%8r@^{4yx6t$4hm;(Mv5li3g#+jDKHk-9qgRIk^<_~QQZr8W_wUGA^Op1yiH zuwVV#bcuYCOorTk#2@dgJy_dEFGB7ZVRK`s*WRXsabFG$eCl!nIyCUAGr8|r8a&V= zy*j-oalO=QMZ@-oS1zP@)Yg?XBF`L=!JdZNzP9s#vTvv<{7j*#z8=1GN!c(243ns8UI7!ZLJ|W|6W8&*eA!qA;&7 zF!=#r-_B#qm`z^R=4Us!U*GFIS;Z@*58fU7iX*rt-*PzJ-rhrtbp{-d$W|>p5T5hs zR+nt`Xki?f+d2NMv>e+>Ow1&wMNej-aUm05;gJ z+zrXHcq|SJKvS{y*GM(Un?`%%aR3~&1c1%}Kmj586aWWM08B>!fH@1mT2e)Opeq0{ zmjFM4r}E#bXQp+XAXQ~2jE#*o93u-akYChgl~IW@nM@=SHL%j2T%xiM?HL^%rHRGj zMpl~Ways`&aA^`@a&l5Gmvibaj#OusBom;4^YimuUT;Uw;MDE16Ujjeg<^Pk7~(g( zAUM!lnbE@7b224(Zf0_Grn(5H_VX(6Ot(jDK!oV4}=U7MfZ|Qk-Bs-vc-h^ z{*B3}H>O+X1sZzQU+zNlVNt$b9w1*0=?xim8pQ|ass2uVh1NQkRqhmssbn1_gi!F2 z>82!dYFuJ+tSKdv6lY3vL(Jjf}`r zQme|QU~jr$k&49+YZvpwg*(V+#6wJ5ODbo2K=8D#t)ay?ezaFYHM-{%jvKFTrx7Ih zn}-6k_j69&TzJ;qfxb3*13AbG{J^uvkLLT_^V5TLkDNJ5o1^~IYai^ z3b>Mgov<>H<=vp`${!HB)T*@#BEnA8>g!;~t#9RSdrHzZBI2To+ca{x7Q?Aqtj3l~ zEW6<`VO~zlx17=dAEWYkOZT4hT3bbI&MGrX>|)Y*U_sH?Mf>gw-=4cPO9tNW3vVze zLb+mJ_hWsjaN@`XoJG?6EC&>61Z767O18kY*Tuy?@V!yLQ0~}JHl=3IoM`O9?6ET> zez)#1Dxh-!uPMmY?A2QFpz;^fa23wRWqi;0OSSbjZN#hSfCLb-dZ(dF0h;A@u}QJa zufSN%I-Mrb&NSXL|O^rRq}M`)_+uQYA7tk>^5|VK4^oF2bN$UjE;zjqN~QO35LTh9i1+mw_u!LW}j2dV<7>XP@6W{qR_T>4g@|4&CsYD{8W=KptmYxrF+ T9$sQ*r3HYGr@u#|dqmEk4`kHv literal 1558 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 diff --git a/toxygen/smileys/default/D83DDEAE.png b/toxygen/smileys/default/D83DDEAE.png index 7c37d2e1925fd6cfec9e7fac38ad52faddfceda6..89e1dbff6ecab061a1744d1ad3f0e9b2b27363e8 100644 GIT binary patch literal 1334 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>ESDgjjum^&6v3ZXzzhb_oi(0|Qe-RY*ihP-3}4K~a8MW=^U?No7H*LPc%?1A|5H)X?Z{PZT)* zl#6a=VdmI1r=+L8_*~7oPspyUFPeAA}4LPn&(efZFt{3Yr{*f?}DGso!43y zHG4K2quED+oTIm|rA|^4nrkbrxamk!d?wZ!C#5QQ<|d}62BjvZR2H601r=rtp00i_>zopr0B-w6+5i9m literal 1358 zcmbVMZA=?w9KVjPuno4xxn(dX$HAsVdM|xB+Dkhqch}KKv4JK-lTEI7Pw5H0D|ZJi z7rF9YT5F@d4$ zDn`2x+iK91E>pk*hG?|P@2Xmr;DbkD%4I=c7lXAZKIrwi)QTA=;nn%Ynl{e znz+EGEx7Yxal=S51d&Rm@RS2rwKjtCcsyASyWIvgY&CX)dZZ@_h$f(` zU785%5i8GuVeX!8D60s)VP4T>SQMdyQ^l?XP>dkw!{8TOk|l<8xLG!v-dJ;$*fmUdQq+#JvGP99c*x7Stk{s767teteb9Ce-?HmK-^pok!6 zSB7>svFuZ^466abP_>AvcH}xB99IohkE=Z z;_r>k0e1Lu#hKBs3TE$JP!BDf?%TWH@>%}F(dgsV)yWF0EK_-ub|-jqNo2Pa}jzKmJj+OIp=s~7ja%Z&B+hfjTfD1Kw2>}Wwub-y)I zHWS%}fAQN~!qPA9v`D_H%-ULjn&|$n^V#E9Oz73|(ywE+3$tc>QSs!hmAb|9JnMB+ z+;qk3EsPzTCWn`&9OmxHygTSxO&mEhhvGAZFC7b44J`d|f+&6@y))BsMD`E0kGxLJ zL<>F)eA6&>zU#r_%NE5rU&5u8S&HQ;8T^=`X?q~>2nDL&)wLL=}rF$%Ip5mu| zKAKMqHs3uveLEblSlaQ?2j0U6(|*U0c~|99rS+m|nI1s$km$MA7dk6{Je>W*1^Gtq Ja$W1&{{Sa2&Hw-a diff --git a/toxygen/smileys/default/D83DDEAF.png b/toxygen/smileys/default/D83DDEAF.png index cc94e6898aaac808d0089f9acbfc4b5bb2c40c92..c1febe47fe823baa36bad024675e3f77e93e8e0f 100644 GIT binary patch literal 1693 zcmZ`)4OEj=7`_7>I0TtYVaU%A2fB^*4fzHPEH*agoZE2B1ahW8C&v)N*-|v0C=Y8LMzjOD8 zNrg59CjtP#MzoGEht_WXu`-AAD(}VN&{%BcO1J>j7h9Xu3*ed(yG||vV239FIr#w0 zLRZcR0NW7&MpXcC$^mdltvtIi2mqWRY=c68MR2e;{jqRgOooiPn-4pi-6P}fE(P=c ze*(t*6EOZ2WC$c;V#Yi|y)do^7UY7l9Uy*V5g~r1TSH8F;UQjqA4hci!SngOnlnxnUkZVqcJftVzC$|9~%5d zaB%P^VM0v02HUH}o=#R(RgH{{z={G&@Zze#+mAF=As zD0JrHEpDU*#cnGzFfvTO@4WuL>#*O6`o#QqMnZk&Ouz5>uKnu-k8dwcK5SoR-yFI9 zK}_PU{OcR2Avi21UmbeZ>1Dq zyCz5Y!n8>;v5PvUw&&5~O@j{%C(k*c>PLiW^3(HI4M(EBsKDc%IJq0QH70d@sYC;J zGI?=V?lkhYlpbAybmS4E27T)6NJR$g&q*HDzg1x>k27eH?juua=YpqHgl_)>$(W*I zrAfobnL0h11@xY-^JBj5>0-WSkRPTK?ZXI()CVXhGIWp(w6I|U)H(u}c zM+@C_WkpgU@4P#DL-h2x@lhBzkeyN!e!9Hf^ZxHYuLDQS>(R`Qaf@HeI&g?lAdMbh zOuFV^3v41?7{Nn!(PMiG4z7KP+s6!lL~?ENC+1b(zA3!FTR(R#<@wh(nWLvKM0VZF zF1hwXl%cCQHgsurYIVtHF{49!6d|863WiVTzmK!}v zGLw^CrM7kzxuvCL#dj?h%`C7WlgZ`z`I0M_y%}}2SFUUjN+ewN^LO zG#{&Ol_>T4+#*Rxc#XrN#>VZvy^g)P!JB1wzrVKbs-qDPy6=V*bPwWwfqyDhD~Qm> zskItTY`O*-;Dh-1cq4w^EMEl+;jBS9D2t9D90c)WkGlSgkdzXa5TE`3gwSpBDo7yD rD}<-SYctj98o-T9iP2C*N$RZ{xkeqAoz|&=cLo3=fs}uOr^@>SKfA}x literal 1621 zcmbVMYfuwc6pk;7BAqlK3RYbg2SvO1VV^N+fiAP4Xl#gkX=ZGwpNjs z4vz{&=;){|Mh8c&3RY?@EdiY>wzk!ZjQd%o|y zx6+W35Iikn8jHmW)+M4wu(EwmU;uc}`_qeoC7jV@GU=3+abgafrLs`jIHa>-Ik*wW zEF1H`#p774Nmjy?$z2z!@kT&ml-9JaoDPh6sxi zUM|YQo>`r3LyKA`7ku5 z!fa5&&z;KD8z431z#$1w#5D^L1d>X5h*%<(B1<5l01@&9;FfX`u|gtM2xFn~3kKF4 z7OTRDYR7GXof6Jv7`uYccRHOsr-(;6a`=c`F86T=gMUsney= z$jGJgs0H`7eb#@(nKN{Au;<{hkEl715l@eI)BPK?wQ+b=|Iyy0UGVGddv~vX^@b8% ziqywg`*XWHZ|GXtcLRgBCp4T&6^ERlr$!WV8`p6pSrKJH8GHFuc-*7%v{h|0J!?|? z27d2sFimF*Un{CCnS3?tV`vK+d_01jLB<=_Rn~NFKL4!E9@^eVJUk?6Unpt5nb4Ye zG}(}{t7&D|F|lNlM-aNtFi?YJuRqn$(ZDn$J2HxzhKsf^_3pHU`APbc>PA)9VCS4O zv!+_#X=Ps=%s06g-gzhuxiY(qIR2M$ZM3$rI%=+mjG1u39a;P!;LW!3`sleek?cSy zY&%w57EJm#$5iz7-K;okOFMP02fw4Bq;h6Fn(A z|1Pb2SH8?}hmMS>n)nRsTOZQ%I5l~-EA7{r-)ODj}eA?MB zZZXmGMn4sG*LqLRt`U@l{JfyKJn+QUbL~~ko`Dr7&vtRHw3ODC(x-2BoKqaAjF}Q} zLt4;uaA)tnd@`70>kGU8+fNl8#Z{)o`)fa3`u=XD_Pa)tbmfmxXJ)NCtlNcFu$N~L z{q74YSMl#x>Mw`n5zc2p!?6zxOrF-c9CiG4h@k#Zfy2G%xV)!XN ew`Yczlf~NcNcs2!-XO>Kqt|It&?E6#TmAt<5MdMm diff --git a/toxygen/smileys/default/D83DDEB0.png b/toxygen/smileys/default/D83DDEB0.png index 3790c67f8850d7e57bf42c841775e159a9033b5f..5a814ceeefc1fdaa5384c0a41f646cb513dce3b5 100644 GIT binary patch literal 1322 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>-w z3|;>j+W#^%|7NKF!BG8;q5KO&@kfTd_Y7HY8PZ-eB)?=xd;wAnQ4i!oM1X=2aj+r( z|NlSv#Tk>l-Cd+g)1MdtIqW5#zOL-g*!j3rtWsQE?lCYh zHCBa0lmsP~D-;yvr)B1(DwI?fq$*V87BDba^iB=+-gHNS=gw;pOQvZpEXOzY-dw76 zTE<>?M*ri&Nz)6jHnawK-(|Xd{=NN+=s#UzlVW#?`)`|ZBkktY$s1FPSC@Gt+x^;> zJ6G~smejO2leT(ZdgjTx@2FPh$EzC+yVqBxnD%gJH2I#n^z?zsZt=*y5@u1{2GJHr zcfT#$aYTxhX;a|qiS28OA3n7+s%&q}?Uwy@>!7B1uBD)R(cN}WBYCAe6Z|K+HfXI7 z-{u-pbSCk3;?s@i?iik{SI#?}E8C|XGw1TthwhalK8{(je)z_g@EAg9zI^q_#vyw}?V zI9~H5DX;c?6WOg0A-&|^iAn~!{Fxm33b&b4Ux^6&e7T1)Znim28Ak=}D|TwXx$o*5vFmVcTg`{fSu4VxFLZ2(Rc@*1+r~R{ z%c{O}^JWV~9?y(YT;~xeD3NlIL-)gsG>$H}tE|RHPrTUo!u|$F9_!_1I1X5M|FU#v{r zU2C*|$-V`q&l#RBjv*44W6xX{YBCUExv;gI@o1T@;8rDZq#x~JQamy($XaL- zm*&%Xe4i?;ygh@iJ=mo;@po|h^VpjE`>Uduz3xs4Dqm_U$kPAgy<~=NOQHyW;EJsm ztDk7wZt`|BqgyV)hf9t6-Y4{85kMq8kp-EnuHh{ zSQ(pInV4xC7+4t?*jZoUL(!0%pOTqYiCcqzTFxe*21$?&!TD(=<%vb942~)JNvR5+ cxryniL8*x;m4zo$K?N9tr>mdKI;Vst04$(AEdT%j literal 1310 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 F`%im+*f#(G literal 1637 zcmbVMX;2eq7!Gn6IUFIPl2KfO1ES>ET#!h>WRpOYKuqG87O`xS4GFNjVY4&=&!V&j z6^a)Yt3^Cp>M@-<#lomXaR%`O(Lu3;j9Rg^sMzULt9FCL_J`w-?(FV&?E5^=dwuKG z8Jk6fQ*c2y87c z?L^XOv_PA~Xd+G8Y^eoDIk3-$<3XJinnp{@@Hk;hF+u`nq|kxMSi`NSSb)PSV=WPB zd0Hog*d19F1Y)SjF;l5|SBT({fJaSJ#&QOE@)5{Zw4&u3E>Y*#r(!X7r}iuW@p5toHMrxEY7WIW=by%f5K`JCxgHQ?t zsa1Rsl&gfGLL?G{Ae1VWKzu*PyRZT!kFS;}6e^)g0)i@jszM-Ci$I=S2||1!U+l+f zFc%497R0aXpmfKul-zr;Qiwoc5+{r}UgGZnogF7}mmPNkkUUnCHw@Z@q3Qc)gIai{W>YB?3Ln3_o>DgkQ@YyCet<5JFW zea-QoX7O)P9q60BZGCF-_V6GWbz%tWXw0xRw$fLQ(s>P&Nnrhu0BdWJ5RLyV&BVE88>g;T4bJeh50gjZhK>H)&9fwGZFjri??jg zwrgs3d1G2Omp%)dXZ|rD|3)M|XyA{EJ5M~59zKFr&GC+lHDqNbO(&|7^h4UrxP%=u zv@4jcn`R%&*{i=DGBGwU?|kmst&bf&`p@rn1U+Yjoa|t<94WL61l?{@OpUY${k}Bt zep7Q`%YrzOzU%Dnw&3!x7pj2N9eysSo4;^Ih@9plcOs2Ql z4BaAI@0O)v&DR?;lklA}^LvCYY+CK$q0PY=U&$}^3U&zi1wA+Rt{J+rn3JFIL0j@j zK#cZsOCXvSwns;YAZV!P`HFPm_1>o5S7D;-!52b5t=k&$BH6a)v-K6%2t}>OSnBCg zM}~)}Fbr#`sj2yXCAKr+WQm*pwEls<*@=ZSb`6BY_N`_;xg%rMWi>ccH+s^rry_A} z)xL>O>&XS|7rT#sTXb($G=JDExMql$UpisV+?Be-MBT5G4<(jtD0|g&-jLV#T6W-x xw)jhH@kfAld~{d-`U!)*-LBYWLx1P3381y3jLxH{ryuwI_%*5=14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>`T@CkAK|NlQw4MXoM z5D5f8hEEfO3FQJs@FS2qMg~SEhNc?~T#``5Ion`{FoI+m7>epz8M@{Bul+4wahH46nRL4ow?B2YmQi@NucF$hvb63LG z0WDxm@^*J&=wOxg0CLz%Jbhi+pRx0Csd7cPyBY(XTvrtmQ4*9`u24{vpO%@Es!&o{ zkg8CTTfo3z(K|Kt?zK4v9DAN?{^AhiIC8u&y!d)?)y?DYeeTG=zV+>O)in(PHi@QB zpMUfJJQn|1t!(=|U2mVn!tPn?R86u{&TPE7YS-7>Z?A`KtH?f)>^V%?D%Lm(c8Zy6rc4=A8Tz>m^<(B^5{ce+XV}mDmTr%t}S%5 z_AaA9U{sTQn2PizCZ@0Z-5UiQL_03ba9^4h!Xf5g>(kWq)g@8#YTONFah(n87yMFw z#<*i|rqG@RH#REGZJWU*>`=M!(8FITZy(&br2YF%h``QI|I(koIpR{Z;bqBmvriWd z9JTrWuQ?r>^xrS5eX91I<)__i#niJu*JV6-@UA2=^5(WBC)2-Qy!l(L;#=XY8@?It z+~wKpA`Uz5nSDL>@vEH36TSYtwkwQWPARwTvvV(Mtl{k0C%$8)Tk}ns9lUz49aO8D zw=}9Xt9{(=9`d68%|+!6JSk>NHmWwau9iRXS^6hKj(fsS`;>-FyR}ag%FQ|Tr20=8zWjjM!dH))Lr$LaaQ~j)`G<+QuWQ+bf75`ejbWXqi(`ny<>Z6| z3_T|poEtcG4xDLHJ84!NRZ~SnO$`-Gd3|+rWlas)Vr^@got2Z_6oqYrjf<6& z*|@#6v!$c0tJ&Sv)5XK1t=VGD&CB_V{oVXDnh$!PynX!o`S}wCPbF}0@Nn^Q@^bU{ zcl2=e@%1`>l@?%N@amBCO{#Kp2fAFf#5JNMC9x#cD!C{XNHG{07#ZmrnClvvgcurF z8Jk*}8fzOESQ!{BSozx=MMG|WN@iLmZVeZ|ICldzNP=t#&QB{TPb^Aha7@WhN>%X8 dO-xS>N=;0uEIgSCEPojoJYD@<);T3K0RXprNa+9o literal 1429 zcmbVMeN5D57%$TxfuJHA4*Xax6WHMH+H%)kJ5G1ka`Ti!I4*%rOzhqDpgDRy+A=swh)X6Jjv(R(j4{Ummtk~6NOYMx!=}z;Y>P~@4@iU&5;qf>dk;9aKZt*$n zawWWj=R8&ASNJRlTILm;MQV{fhjvS%mJ8`AW zV8LTT3JrGD9$@%VVOzD#7gXmu-PJxfq%PGm^~125qLsjba!~5L!%cAP6i2#!(E{q2MMon4UHeG)}?`7Xqxw z9xv@+tqZn*WI{?+H9+fhm6es+%1o^!m*_BxqCy-vt^pbvrOK~zL5*KY4l}U4;+BPg zDoB1f#K^g%3e|)FPv=7r19tl&v0qt86iAsa$OUwm7S)MjD6TneMRoB1y0N6T;;ahr zItQ;v6|x)D!GK77)G#03t=@= zOg0cK3`$sW#%RD96t|LwFvn6@y#X~)ETuOatp*Is#*LJbWXz;F6ENWR0CpF!nNP&hjLdVYBs(RkEZhNkrIIQsrBVQ93J5r@Q1E-CO67%6dFG>Kd0BX! z_gG~~gy-^03rpCy=*=W*B+VAWYz)P1CJmVu7B%9io@FSOL>91~|C2Kva7Gtuj{h`E z_y}}hXuH_@Kv*0e-VY{52BYz#??fX6MO?PAW@m8pv|~s5Tlvq{jy$v;EhcmD&A+TI zy5e#X4QrB!XhU@R{tez@>m<{EHt=efE#YL@p?+6gZ+ptSO~u5K6Nv*;Gp*&;(k|?S zwyByCrETW(-}a7hEuHxpOzM_HeO=`K_?gFEZ_kCdBjaDT>8*ESzm)bmZx^OF4j$e< z_FC)u0DC4oDQ#$K_lAq&qcsN}Obi-&P4{ABOhdHy@8kTkKZm|ft^4A9RF!=Cuc-aG z?`ziGq1q24=Nj@Gd)L3W^HxnU!(?l2y;0iaOCCx*HZ}RJI-cCH(zS8b?29h4<*N+j zqwAAp9X}u0d?R&gX3U<@U(+$X@qUK(=8T(e+#M7j)U6yErmj76kh#4f!@3}9&uNN-yDsfCLOhW zTl#mS)=^*NbW=(|;2O{Edw%&zTkQ|C8P(>&cT#&U-&-DWsqmXKpH{rIg14R8q{&1WzbsOu$&t-(^0dPQWV)0o3T*{a>AvS==n2lckd;k)#0PIc%;1itM z{S1Iu5&+Lb0iY`Yu#Q&N_%Q)M-1PMpa-g+zD2N6HP$3`Ei-r1s&eMBpi|DrkQ~B?K z=X?)5{~JCeSP1bPrtq+XSc@U%e8|Nb=CjjGm@)h`n74L@FyFopTYPhYjs5|4{4akO zCUogu&p#R-x_z^3%FEB?pg2S-l}lK@hZeoe<&?;7T6zsF0^a zhc^uxc`FnO)6>&$@)>Y>30${ztGO*~G9+BynUz*jQc_!6+uhyGQmK}fmJ|x5y1KeM=g=#cIp6x$g+|iNjZrAna&u$2djoUzQfKPB z7$MCJkZ=$6LeEsfJ581Q3#Jam*9(`f9q%^Go>Z6^)~a)|@e>)^j~_?0_cJmbCUW4^ z*H`#V-KWv}4$|bYVpxO-<9V{#A1f3Zcwi*w@;OF>M&_s`v^3t*cL3-mCJ5Kc{lz;l z(J@isvIr?g9v>~m#6?B}Ks%HZDC&NQG2XeuN|b~^iUinOl_xqIiFMY>lit`3pJSO8 zW*BSYPxppT*4>*91*%eA7X%SroGx}HV6$^7ie#O?Gdfmmt$ctCi_6!mJB=UEPyTPz%E`K)cfYGKx_39n*2Z90%`vCe6N;kYJslA$um zD~(1IhbM{$0=JCZt=C{*CzZrKNH|uI%HDK`|Hfh4@gU2i)hG&59qK_K2~nstj~oNM z#{1cd;gwCZT5-8%l%4FDJ&7g`5hh!sADu$@K#KZEPoyxYslJV?aiKsZA(wcFM)kQ&p`$)*) zF92zkR(xeBY3i6FxonourgLK`&@&ehw{Be^FrSgPCoAh{MnBSY+yH57YpY03=C`#H z90-KgHg8U{!bZUa3wEV%cZ3DDQw{-9;qo2g7%5;$qQayYZlrjV)L$x= U#BXbs!v7NhxEuky)GajWFW?F4hyVZp literal 1647 zcmbVNX;2eq7!H&|Dka7dTclRjMO#tGW^-&vO28!tLL3Yic-fy3d1=#te2cIEpXZXo-P7 zS(Yz7hsSX^ujCQNEGA2zqA*i70p`;YxNLS7&Edo)xa^p@2xkBjo==cUaPU|w2oM$} zm?_l@^>!6rKqR|qJj0!8G`ow;atoLc55&0?EP)MYFu-NAk`9GS36Ah8SlM?Bfxw6g zQ=|l+JC&tR2UHY|15$xF#w>(k06_$>M2aAAG$0bfB1p*INDM4dND+ld28>=HYmK(# zDGX}us4aG<1Pd6(u7IH8;$lIuSU}PF5G4<%WEUtT&1@nqZ59S=E%;X~RiIr&faO7lDPaD1_oz6{3Yz zGL=fJ5y3F3k-&*kDFVZ)SVXQ8jd8pPE0M*@5=FvTjYK1dVU0+Ru*pLc#UcqRj#Z=M zSRLtLFw%^V*(F%JqgdHXu?iK9V+=(bDatz50qF%4Lpcg4JD|!yfZ3S@X`zZ8QNHp# zk5-M-#4_BXr70UQl3xY!0{c?2L?%R0Sfyb-VB?Mzs!s4AlkC)bhEe$~EOiI0oIk8bHVgS0bAb{Ml8{6p`&$L|42|c3jYmCdBH`Ab#O#a@0ic)!xLF=*7<~y^v zfu5r4q%8W4;0*=8qUglWPdc_4HcL}>K4`oWehbT9n(^?(Ow1xpSh?avt~n??dwS#CMNJE%Q{0nQ*n`#&C9K|2^VDcPlU@?; zcDwDrYeRV5?ZW}VYo}FR54d)Gm8YXWQKNa)>-F-x2JFO~ifgog=zxA@Qt*8Dd+=~~ z*)Q>si?@vrZoaI~efpFa=<&-tFtwqfq419X8jd30KWu+bC-h+6R}M~&x3+Y!FzDip z6;_fu?>Fc4B)>})S3l&7@W$4rx;nqlXTiE&GHF*w*{7M6z;}P&KKTf$y0}a4 zJ@j#3rO0@p+PFmg0N38wamit*2muf>pN0~g-)D;z*c35b= z-hRXQWYtC@?&sPs78^9FRlOxs+NwK`(A#V7d!BKH9Py(WRmaZu-ShndbedH4-o)Is F{{Ry2XF>n~ diff --git a/toxygen/smileys/default/D83DDEB4.png b/toxygen/smileys/default/D83DDEB4.png index 5b8e42404f8360e90784789839a63e8fd2a41575..2b63d916f3aec08ef47139e8ba4df28e50a49a83 100644 GIT binary patch literal 1685 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 RcwnCn0Nz+X&ueH}>R*SS{-FQ> literal 1766 zcmbVNX;2eq7*3}uhlLhvJwP!A2M;94E@Vjp0ZBH&0MT$Mf+!(bNFd3^%|gQEfq0+@ zC{!#AS{0>G3DVYDLB}atC<0oDh;oQ16$J|xY{d(@L9qSd_@g_!`+e`X@AJI(Y=(G^ zkCmmZC5c3`60U{=iS;wnV?Kj;FB%z_5X*d=ABqQ|(YQ{6AtY}Z8ikOBDoG3yh)86K zTj~)H66xb;MQ|t{D)MJbQ598U(xK{A8Ujrsd3fnH5@{TQlcSIrg_=Vds=iDiD`XtX zdWMKD((n+uVs#RR1SPErmL|nXSu%>3C)q>KCJ0mrE+OkxO0|})=TIhi*~H#-Orww| zRPZ%#06}L$!&#fC#wB`Zwbo&Z0YBgC)d6BTilbUNsv+}&Ao8;H3bhQ? zX_uSI^EO%-!4wIIOn{*(@t~&Rw>lX%kOBxqu zcdha~o19^tkXRQhoS-^(Y>{Bf|; zqx@=e)9~&nXJd{b_O?PyS0A7k36;&-FPCf4&T50$dRkDl{uA2oA6-eqq{NtBSNBc zEeaR&k*{X{A>S_E`1^2&XgA2JTXgLFpOWJi1N&Ofj>fmd?oK~J*JW89f7OxN?lpJk zYaH4xoXr9kL>U|G+cO2xjd@$nMC?1; zwQb9csCZDY%Js_=U7mYrrvvw%48W|NcyqaU>&o8OaTSM}M8@P{*GnZY>7%c<)(-py zo+|svjD{&TO&=P)rRk}-{38BNprarvG2f8#(qb4+t8$64nWi5&@Wtx{HK|_lxqPLc z*1a@f!%oWPmBFc^Jdh&`FTHJx68w)YGGk-*ph-{ zE!{Ww1{A^mSJDl5XogLNHmJSRSN@pTjdU zm=DKu`!O?zHp9p@w-SrIIn4{_MV{J_lJ4;2&i&RIX>Riu*z#vhk02i?@HIOZ!vFNm d!MZJj=5&&%gfAV;D#x+do{<*b$I5!iMQrqQ(hWLvN zDF+y0&VW$(F%Syg&k(SaK|7nlXETG>I-oT`kZvLR|MZGgH8H=pw*T7FcB$O?5gYr@ zWd%~xix{t z0i9p?p6@h}rCJi?2Mm$4x33dF|NQdm=bHxsFDpV0_T4`D>gV5m@9o<6&2Mkm9QOIJ zP34yfIY14JN#5=*3>~bp9zYIziKnkC`!jYvE>+gk77>dW7?|p+LLy3n63Z0|it^Jk zb5a#bDhpB-Dsl_z85k^jr-piOx?{j`r?^YTk&We2p^b9(^i?{=e@rUu`i^_Xt7{3@laoc`OIK&jco@Jkdq(Q1W3KP+rF>1hk)eDb=b_O3vhDR}RZB!TI^1&I zWH!C)nqKMlsf_uiZrO`$_UX4ut~kjY^!1+@a?hngVV+VqL+Awkl8(H z-b3R3MrH*iarfFL!F&&Hj)nj0ZMj++f))zo*v@>Fz#-+x)mbW zz5WUoGv&QE60~#J#=GRQKyW9EgMONY%=uNabGoy8?W_fz!tecmV`Fu3qD)TOj^dLw zH(D-qy|k~`>GkHm=i3b>t-nitK1jd1BI-Urugo3On2zfiw>2i0U48SWR@f$cUj4Gr zY_Y0?XOw=1CeK|h!K=%}dyBHZ7TbtToE;@U#PcYV7V$)=&F;A^*$OHtVmq zAL~8;xyR$huVt3A-)?{8{p+LlgkGo8kIt(C=lZmNujl;Bv}S)-huH6j;=mMI;_2cT zB5^r6fuWvREln(pE#<(mb*CDY4y;^uu2t#4+Jz^Zl@6Rsc!$Y6@w2MI~Z)SjNyMiHk2N3NWS;fp+QDCpj)C`Qi%$Jzn| zPl_on$VoqbVnq_W5CenA5sso7?vSfM2dI{~MwFx^mZXMRB^RXvX$B($BO_e{b6rD| z5JLkiV^b?r18oBXD+7axjt^HM>#)krPsvQH#I50hOr+REMRhI)22WQ%mvv4FO#mD( B4}$;z literal 1312 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 E2cBzXF8}}l diff --git a/toxygen/smileys/default/D83DDEB8.png b/toxygen/smileys/default/D83DDEB8.png index ff2620417963251f3fc677fc879e7744cc7d9d4a..479c4eef9c75a1816232076c3ac4192182a1321c 100644 GIT binary patch delta 1746 zcmZWoc~H~m7X6|kR3)`iBcfFHAQ((|tPz2b#EOvAKmd_oWs^-7V;~8lKSJ0J?7G(>DAd8SREDzbGKwjRPdGlVUcg~$N=gypa|2a1qzOI~c5ZVp^ z&_{jgETq}!dXWu(T#S|?feC2HyVZNPNujXLz@~NGuAgi%Qj})pYd;JGIO)p&ztz2&HSR= zs2}p8+Mnh_g}g{UkJ-|g-`12o zHTjE zgAEC6+xRH!_k9hbf%@1;ESHHLdRI3L_1DEPOgRyzf_JsU{SAx&GxlZkp?9@w(?74z z6tDilmwhT)lNPQ?a;~$zuQ9)u&Be?9nfdt--pXW}Y&Mz2ILD;B$)u68>FCv|^p%N3 z*>vodgno^2cC_19CJqh{A~QlMGAU>EW76^%v?XCJi~KxX?X0Y<+^M+v0f-jhO$ZA( zwKaKZWgJ=@VQx)^ZI01yT(!wh#$zy;Ag>dQppzlKC+^=NZjMs7CPSA#Fc*h|H$G4{ zM5nXk%!RFXNTe>>2>E+0PESwo2Hj*y6ui)fUw?18{@%PO8RbUS(b76}HwC4prsnOY zyV!3x-(&W9z`(^p?H2n8m6hBb_hv<_>{t0BDMCe2 zQ89>yn0u+Z+5_gg_s_ln%df#q7m#*>nNA>+$x2$YZ^)y8XE>4noudB~Oj{z>(vgVe z(9-!Iw8c}?)2BB+@Ljranv`Pzz&0p~dXC}b#ncF=hg}N1?5DwChx=);Lc#&CNWJ4i$q}NJHpE|_ zjNPO3^Mpm!^5E+F&;--geqfp<4B7A=Qo^9QzU$ED;(^Vi?|VfTaPUG~1zJFEsgg!aowDXpbCKS3|aGXdg(lZcQ@3tq_+hD^zfbq3(^XKsu}MX`QeP z+s&yQ(=e;fZ~vx7DK>)J7iNhZ-O^MUQ}Bg^33N5O0R>1pl469;HTdu=6>t5Kb`pd2 z!PcKt)H_=J2^PEui6o_p+)rFy`SUH$zPMt;VgD{goAQL9>!E5p>VxCGGt-S0+R9z1&cb}!2(>+~OQh8X{=JwBkme#Tab%E5mhkL*E zndsBMA@DTAs(+!7*moNFTgko>yuz%Yse&x>MigZH8LSP9gSQV1+wZh$8nrxMeHhEG zX+CTFg9R`tTh&J=Z`;4zihl{e4mljwY;-JkW(md#N@-YNf1I1J}zZqC&7f@>sl^78P6KB%p?Tlm6_n!?S? z&wsbkKoc2iRnNe9ui&QOQsiQDNOPfN4CM6BD6q!yzg^41L z!=o_t@j!tgOhrWNybruUfjLoeo4~{qr^BmD5#0Xp@sH(6^1a`e_xF46CmA8Zvz&*z z52a8j&e9;UoLomZp1}jjw`Z4KMK0qAiIND#w1i28BNUMai$(ycUKNAL5tSx6;T+;e zq4d}46iPxVn+vHiJzeF{p_}yv5>27_`I`+YbsRzf(MXIA6;dA^wow6{Mo10g$e1z% zjKu1KEI1Ns309~racY5v>OT|kGeabS9wAhKSs#xYA+wO$!wZqJkkE=)N>H69fS+mkzQyTrM~RU@<`!gGt_88pwt?T!_U3dM_$z4cBNPxj3-b z7P%8rV+q0lF&HM3iEi?xV|WY$6bJ+k4i<|>YS4_yD4{abP@`8LgBUTYah-wCVJP5W zR7GQngpf*l`Yr^$K_+`oj2e3rMW&2lRv8!|oypMa9dY$&8wok`uNxoKHY$=02t$q- zu|!-=)85;hpX;c!6^=5qxwtB>PDSOG^8AZBww30oonK?y5B!WO|y5$G#n@z^2|?88b? zBcVdoNS|FDX}1^4{3sTJaYRL6xB|oC`#K;b79%iYEM@@UP%hvTrb9KD$vDkXo_EoT z5nPvqXaaFe5A@_0(tW@_$Cu4xibNnRAw3}D<}<~CA~IqwDG3A;UurK_^M7*2Ae~`2 zn&Uss(sx95phJA$`sC*Q@E|BTF*rFIU3P0VIdPCwEK-Xe2}`*MOh4>gyEgve(j_XfZPN8V;e{WSL()fC6G#D~ztu4&KRR@7A(Jbrv$ zS&wVCCRC1Dw=-05De~f`lzDCLnU+@5@U2~Mj`nXe6!=wry4_ZPVd~i8n)n-gn^4ik z%9F7LM-2)0_B1BuSv5pn`V2KcGctzJaJVI^*zl&VI;*>+w(0)p&IL%u`5Wzi<2PuA z$t{0)do`mS6Uy&xe(jO!vFpL=z^)11f(G;YGqmQ3EwdG@6BEA5^Y*z}YuQ{pe|wLFNoZc9kcZhq#4>@`It<+Y_4Zt2Xs4$Xu`VL3Yq2gjX7;EY_{#Jad$Tl?J|F} zmOFUl!(&&g$_hAe`i(LDygQWFjtpCVR#ms9>|5zPZ2rjN;>lAY@Po&zR^<-ayUHW& zroEqceR{JrRhc6KGDX)KO~s4T-A7-y^1o^A?_y2nWp8-pACTa^>3YR|r_2M3d^*}k z!2K+HTAFL!D*QXd5iAifR=je%lL)#5o~=OVJ8LYb(ASHe9@@V6MBal=g>@fjdeFG7 zi&vdA|NC|qq0hl?uxZrkNH1E)kUL>BShF(Qz_j1^l^6ZeJ*u?(lW+Mc%lVmmM=@7U zj*5{aJvln}cK$BEj?{G5rO9q{*1hfiddn->sqE6~AlV$x@VnJTspiX{6xm$7750tg zXMkDH>9KL>49`i^>2phr1II?y*DDHB4PiBpjI`WotCMyeJmmf;D|?V|%BceGGqyEr z_&8|oj*}@{il$oam|bFNtK8B4W$Q3?pp(@u9x_h*27Fe!Y%r7Z_=gw4H_ykqIQ~vj MNwBy)VDXB70AqK|mH+?% diff --git a/toxygen/smileys/default/D83DDEB9.png b/toxygen/smileys/default/D83DDEB9.png index 34f6afe5fc151c346adc030c46342c066695d3d6..ed294c0bee0c619e69dbbbb433d11c31421cfcde 100644 GIT binary patch literal 1285 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>-w z3|;>j+W#^%|7NKF!BG8;q5KO&@kfTd_Y7HY8PZ-eB)?=xd;wAnQ4i!oM1X=2@d^K@ zpZfpx7s#mJfBpjj(7=f&{(l3CK#ZOAd&jMRAj2DfRP6fw{L_D+EmvOt+jQ+OL+5ui z>;C-x|Ks;RpcUm?zkT`f4`{`<9e4Kt-7H=bprs+DQ_7@z*@=Hv2I^%kL9`wsE2MPBo|wRPeA`}G&L z@8}Yn!^jLNqZ;N_b46dSsyA_rj0wK0K;TdE*ebdC896DY($&wDq}K3;)IYId_-^jo z!f$e0>XhaQO;^SYeK(dI{cC;)kD9ih)8k#Z`$avUoL7km`+T{FF>bawP7@w=$k*=F zeskZ|H)7Y}+SZzH=~olhp7U*HnC-^#QDzhGtSzhfQqQ|~tVuRss}R0q1qaWj1Qzif zzQ!D_0i_3LC#9^=|H3YPaP5PwNl*HH@+Q1*U_7pJ;bn)lU}@4xqYJBz<_OJ`lzes~ zr$dOhH|P8TlL^ifgeGmti23KRF8_&efgSVZ|BA{HE3BJkdV41?{*gD^WS0ylXM2de zpV7UbIeZPr)t*jL&o}J$J@NI12gAbPS^sQ)<9m1;Lwa__zT>aAY)Gzt>b<2f!BN7aq~Ooh zqbEBa8T9R_@|dJ@wrFX{yroxaGuC!*TC`Y;`xWE49==XRJs=^t4oDd00SV4`Zx+rI z3f75p`@61ky77+H{>g98?N7U)C^zNW)-<4_R7+eVN>UO_QmvAUQh^kMk%5tsu7SC( zp-G6Lft9hTm5Hggfq|8Q0srN5lTkF}=BH$)RpQoA*H@+w)F276Aviy+q&%@GmBBG3 hKPgqgGdD3kH7GSPrLyp3DyVE?@O1TaS?83{1OWM`GPwW% literal 1269 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDj~FY+`QU=xFE+askla z76vYEjzAxq0a=!ouEt7Gy(#2`nLf}l`k=&tlvrRwz!V5#!jnFb1J69EdB7Y~1k4&o zuWaIDU|>}Bba4!+xV2=`em^Ehf%g4-ci#|5=I(6mZxp-sBKn4em6xZE#?p-e!kJG@ z&m6tv#-03>`9{ZeDO<~;`fHJvhZ#=`KAEIHd-l(>Z{FpH6|RhYUfjZ*sPIBm;z{Wx zg~j?;Sh~Kv5->adI!EvNY%7uHx2oEAu+HPYZy2&p?4E<=l1&ji>Y5gc+_$;?xnASk zrP7!XIZreBdrbQuZrZYZD%_w}V!)gk8UiyjE;$qDb`e|h7G zCjS}EqPSVoZ*r}#R9&2M>d)*QC&jgWUY^qYf zMV|S6=x1Bm5J()PgK+1?|&uf_gC?u$K{>f8=kfvSktxmL4efP)>mBmAABvu d^6FV67=A@B725EN=MJbK@^tlcS?83{1OQn+y!`+G diff --git a/toxygen/smileys/default/D83DDEBA.png b/toxygen/smileys/default/D83DDEBA.png index 42f346a3ca82956f6503805bbfb29561230d9e59..2ec1130b1db8ea307aae0d9732954e9271353f4b 100644 GIT binary patch literal 1309 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>2iynRdfBNJ9 zs@p&t|DXN%|J+BQz5gfO1zP?8^KXdl|Nj2_|NAe{#^1~D{r~<4Xyf$K=aYe65HAVx z0|f&z@O<{{*>kVwPk3A(Z3BukCV9KN$i8MSoCxHwmw5WRvOigT4~_5b}`{oUQt)JygWBIL%#ob zvcSQ6A$Nj$x=YSo(&aw9Jb)iFBYx`N zy4mlJ?VBdUc(a4=eDn341v}i;hq!1xBx0 za}ysvD!ySBp?R&7?by%tomD&5KHPEWs7Ab8)in1LOm?j$%PZrX@`ROLS*sC2+{CFO3!A zkY-D=UG4cMl3P8(YDwK`ONQ_KGlk+Dwv|l@o*4X%$wA+Z<&FMTzk@fIRLqa%@GQT$ zpYPq=3=KZJ!-pl^ZI{fs&?R`ka;Ns2`>wt>O4@&O{d|%x8ou_db2G#23XW}$a$g&H z3U{8HH(Ma`k;z(t={vcMdG;iSTNwKE))9hlC>R&y7VsHBY zowo0G!SuT7drwW`#65TI&s>-HIm~8F{(Po;llAY`Oz2uI{Pe<~X-n*%fA3nfG=-9Xo=mbgZgq$HN4S|t~y0x1R~10y3{19M$NlMq7#D`QhD6EkfC11kdq zJL@ZaC>nC}Q!>*kacl5T%h?3fAPKS|I6tkVJh3R1!7(L2DOJHUH!(dmC^a#qvhZXo QsLW#UboFyt=akR{02#er+W-In literal 1318 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Y;JjGP<|EG=MqJ@bl767!N%VfJPM?S<-f#jDrKxhOTUB)=#mKR*YS0s=DfOY(~| z@(UE4gUu8)!ZY(y^2>`gLD2*8txIZAW?5>ATTyO0Qv}Q! znMuyy85kJDJY5_^DsC;AwA+i>QKapEWO$Op;*O?`0*YZ}zZj%M_AC+9VeK|)`oXa+ z>L*WFSyFPM0f$h$gY{Y$T^}VaFOey4&h-6PyLdJ&y>&uk|IWF;EuHdh7f()|m>Q(A zi?@r{mSgYFC$W1GQYXYez)R#$eMrmb^Kf6_VQd$ux9m9|MNSP^~O%q zv;@tU3nZ8PZa#Hyme-Yg(W$m?R`G1pm?vv|efImSmy0Y~PB|UCactk8H}`)ge^lDl zYn0@%W%W!2^}t;d{_{+Fsu8M`;VQf7(|xW_FBq5A2Yo9GoO3cYBV&Hg7Q@E=l;$UO aED{XmsTb_|8fv9MC6}kGpUXO@geCxCR?B4o diff --git a/toxygen/smileys/default/D83DDEBB.png b/toxygen/smileys/default/D83DDEBB.png index 730020ed7a66ca6cb7e93e013143b78c119b93b5..d2130519f38b582cb2e4a5ddcfba027fd33d7699 100644 GIT binary patch delta 1275 zcmX@YwVZ2$WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DA+AX)E+#HJAHVoa?81}L^NvN$J`^!y zU)YpgArrR-^==I4TI=7w(zj)qchh3eh6O;yKvWOZ0OUi2fT9oypx}-xFMj;~3pDG` z-+%xA|Nrs(575x9m!Cq+z5U|jmJ5%*|M~;8;P1bGfByajT9Y~dAke0Z4_*N+yYckx zt*39N?>IMO=Xs#TUw-`h_Tv}C`rm*409JaEa_*81_u*|3zCksmIU(Od%S4^|+t1u^IWo8W)Sk+RSGyAzZ)MorTD0eO+V0{t zOC20cl58J6DAn6@#$6@fd`9&VzRI@?*3`SsdgdY+^WE^Mm$k9!k;#>b3yeM;y>sZ0 zRPveJ8A~_%UAcIExoXzo9N9K)pE;kFUYsbW$aO=bvm-WWv5A=S-bYQ-DqXGa9`7$} z7k#Uo#Qy1!=`C%cLz{gWT%NqSpf2&TX-k8^>;3MH0t%un3agt}agR3cXj+`?# z=YgGlPZBO4p8NSkdylYJ;Sc_}cS66*gM`iQKD3LK`?E%9)z!+32MLo44z3H_rZ_od zV&KhUVVlzyzS>7p^$zuL#$9mcHW6-Jpb-*jAoQi>Rrjy@!!b>f2RAHQ-tUn&A-{2I zwnbOd^+pz%Kc5ZN6kSpdh$o0!928z{5%OS8L51IeOa*txqA5#Q3x74n+&`iIpoY!w ze>=~G6?v@|LhSC!Kl7xwm+TZW(pzcQQ_)j&hmY%4VMVv!-6YZW39nu&FsjV$xLP=g zr=sGZg_qHMt;((Y>jNg}$rz-Rs<3uCS6pmvSFBc%3GdJeeyLb9A;8)6Y2@YfC+knu zt=j(OYSZp3w+p8S*T<>6{I_i9th3P{O@DvXd|-R@(ph#-=eZO4&gOgmV$Jx~l_MEe zT?^4<6Sd8Gq$YYo2VhA*yeG0@~r3HrSX>qmrThz8C`nksYK+ol~*ks=RFs!H1XT0 z(e)`(Z0l*sGgn{VzBOk*(;xJxSrxwM0f-xipYS( z_nF0{j$cU-PAHKi`T|6O_9qY zdY_wGF{(S*NpgycNQc#49|I7Q3}b~24u*l-NgIGR8sL|yhYhTQVQr1%>O)~|st{+x zUjM2tymM2%x}IQZI+aRUQ+BJO_R!Gfav2;pTRoPj*9K*sPuI&@ZIQu?w3sR;bWxE> zgOQIa1G<~Smae!UC4#{1k<`Xou0$P#`zm}ynJ&gV}V_j)2GMGSg7-`CY z8pHXB*A~Gzc6U}}2x4p4U8;za!uNZX*nos&J>Ydy_{J)T0t@U;j`2gTk>mV68-yNT z18j3R7zjBhZj`gq(d=DHD`Wk#V6Fys;pYeFxoL-v~0M60I6tMwW(|I|Dih4z? zcNGinh-Eny@w%c$6s5md0iC^yu4uhVg5y&JRZpTum#v}C-C|`*kcB-TrQuTTEz;~tuuG!j&h#FElOF@bHG!)rW51{BrWf7wwj4&T|Y7{c@^M`Qk&h=L^%BnGgKW9JJh*Y(9PA?KdW>E;n+~ zx^t1?vwPc*@K>gK%Fc&cT360< zpYIt6Slok8RVFL)KCX0rW5sl2yE}Vgvh1r@2fjOd`6K6ZQo3p;W~#~N-d+4*Wd6a& zKEJeJs(HWj`%~HQy+6+t3M1w}=kl4d+$eYAYCiw_WMu00kB*nNO&36aQ?lmasb4EH wrqI~&^39)RE#GX6ZrU2Y)J2BO3ld?n5VLT{#Uax<$@s4Yd?D|LZTnyN2aZ|LWB>pF diff --git a/toxygen/smileys/default/D83DDEBC.png b/toxygen/smileys/default/D83DDEBC.png index 2e16d90365207b68f77ffa3b7d44ab9eae04c5f2..b473c5fad3ddfa79686adb79c2e658b9561cc345 100644 GIT binary patch literal 1314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>W&| z{?GFHKi%#B6#M@ZEdKWx{_oWM->Ue(QR06c-~Vdn{}l}X%NRh4A?kr#hzL*+A`UlX zd(;1~??6TY0noHBZ~rfe`+sf6|L>pv|M>j>;)egzT|icBEc^fY_5Z`u{-0R}vhMpw zkd1Tv|9^P{viHN&|5vyDe|!$)fV)TjzrOQ-qRsynh5siP|9^ZI*-6r3XY+u55ibey z1BC}N@O<{{*>kVwPk3A(Z3BukCV9KN$Q5xZXahOyC7!;n?9bTwxKw#IJ$b;!z`#^j z6%tVrlvu7%P?VpRnUkteQdy9yP?1}}z+llkHPn049RrR##a%LvY%HG&ZIrX8uhJ?0 zW0GOlcib~>jh44t2b)ylub+R}YrB_cD(^h~;n`6!;oXs?Pm^-4-U++C)cF15vfaFC zC&DjJ2${YrwyR8E>q73fNoMzD*jN8pRvmhnr&-D3h>mlaUeR~8+2)5GUo)n0PyexP z_glS>G?2A+ifE6L!R^P zQ^sQ9h^JCk06mcYSnY0JV=IHbHp?*`Df!;dPS8t{W?Ar7% z{c&Jt--89lhvs-6JFM0cOfa&3<% zn@vrEtZF3R)IB%jgB*^_r2P3H>nkF{{xM3v(&Lt3PN&$S(M9{8VaTKLV#eWr?YTWJ)&8rRi}Q}mud%FT zb}X<65@K4}Y0#r#rP_Hek=rR|-S*#y`ws_i`Ka~kLF|>I4US@Va| zx*bO^e*`*AwZt`|BqgyV)hf9t6-Y4{85kMq8kp-EnuHh{SQ(pInVM)D7+4t?r1Blz ziJ~DlKP5A*61N7C;95}mB?+=2I6tkVJh3R1!7(L2DOJHUH!(dmC^a#qvhZXosNiDo MboFyt=akR{0N`dxr~m)} literal 1298 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;Ya}t%N=+=uFAB-e&w-_YfQ*eUmQl^)4wf0?#-njo;>|TSZYt(KB)->)7+^;SYTQq;d_rE8nuDDV)Z$qX9 zU)ZG!a;@n<#coEw@c*MfW#=?U|6P2i=KFp6DCbpV7uRx~XHV{d11u)(wIWNFpAN3F zT_YXATFv&JS9dbUjGqm=0}8fXOo|K;2w>mTr26}h@&N`V>oSIkYR#IUQp(fS&t;uc GLK6V@|Gm)w diff --git a/toxygen/smileys/default/D83DDEBD.png b/toxygen/smileys/default/D83DDEBD.png index 4bae582241773ae1634b430fb23bbfd98f3b24ec..911fac4add2ef69f5b3729089711252824332c6b 100644 GIT binary patch delta 1388 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 diff --git a/toxygen/smileys/default/D83DDEBF.png b/toxygen/smileys/default/D83DDEBF.png index 90e601a5f162fadeccb2e4480ec41fd4315b6d07..093ca87bab91a4b2370c34546701c30388e71a2a 100644 GIT binary patch literal 1838 zcmcgteKgZ+9DlZjkfj%jxGkDUtEH0MbP2JTm=&Vhut|uRyriggok z0GMU~uqr60!D%-DFqxx0(RzMqRW2I33LqnX2C)-T8fa;&49S<7CK3*=~fcV0p1g8ZA1JGja8+<%!r zN~lUo#;qTnAGv!ACpkUqN9U&;>c6PGqL0V>Y4rawlQMGe*EF$Pdm7ugO`P7gp1x*I z@9omc_8#u-;jt=%#8H&2>(rXe#!aN8#%@010#ZosXu4G&Oz@r zBb7FFaJhZF>FH_l#7CJ-HaGWeYFg6L`DkKtYE&p{Xz3aij13O+`v!(tjcu8??4G&LWj7!bDS)_Y*qoEOrpb;FtPm|&GDOrAuRFsI~iR9$n8%Fse z47ED%+SEI7Aj>6=eM=|~SdvwAc1o;Q5_0;n@52g#NbI@8W-_KRZTM}GhF7fh9l}eG z4*ILdQW_-@CWs%cLLSL{3&9X5=@HJj&SNp@CwWWi7MJsern#qq${D;)yzFZNss(A1G@X;%d z{ygfwb$ZJuEuGSaOi@C30$!eI`5*-^JoE)Qq)pI{?7tcsefh0v51m}syuRAwvZusiy3Zk8aAGX0 zZ5W!)&p0a!;%c53iA0J;P}QI9m)FzVx6gQ{M4a`!VfJ!|9mb3bF_vuY>8 zHYTo?9ml9;L_ci9&6&es7VF5VUCvg84%vIW^mGvyGwNdRrn7nk231UI&rQV2lr$Gd zB7uG=bGJ+A%Q-o7*p5&YJO-^op171qQ{MtpdMg&KS*@v&osoq(LVcj6Pa+#^@TCNJ z`T0y%coyh7Hg^rR_j5;DdEGBK1HEhY`I@TwD_6ePTCS=73yfxlu=FO>Y8civoRO0N|`0tjhP2F8l?i C7`ZI~ literal 1624 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 literal 1405 zcmbVMZA=qq9KTuv3b<(4BB+z|_yF6a_tN(IB7MDUp~;G@bb~D;@o2Abf$Np)u@<&L zI|jjNP?xbmQ2e4__Tn^V%VbVohNv5C%d)tjPDdQ$gCwmZa~d7p(?Z!k5I^j4chAfH ze!u_gbNeeR$~I+f%Yq(d;qW^sMHh=FrG$Mg6r6N&Tl|sUydL! z=QSYJYA5ClP;3KN-X^kDZ53`$TcbzkMNCGxG^_^#JS#D9m~Zk2^!uh;@B1JWQ#0X>R_LLo&+sSw0^6xZo=NezN10f>@dt6yTmCH`PxT7hPR9+3-3 zoZyF(icFo*A{h|i>2e5s!0B8S_6JuI1yY8FnE;9_FqG$$ai!2f$;JNb#+vA$yEVX~ zE;cB%h#pW6UtyXIa`$*cNg(h>zgOfyQJ5xL@U-x(Uvkg}1bk6=Ij^3;agx?rNxMZy z*}%d|VWgd)v}%IF2)jm|)>w;+sdaW-W2I<s(2`0_XS3-vHkE}`DQ#)4!yl9w zzlTlR<$&E4uJy58JteY?B#3T7Xi9fLWrH9I!3H4!Q&l8fT+R8tLMZr5vOLSt(yYk6 z#(M3dz{9Eh>bbS-;~E;X;3|unCQ@;0DOJjU3uUEgnnYH(-v5&`6gYz>o8v#tlHLLx zn4GS*K3J?C9@Y;|j0lcK?!`BTAV~I!gSNQC*XkZ65+=Fv^qnbno^nS|5jAtpeE!F2 z`arZR8vY@3!f2jaKhbE(h^*gFnD^R?bIKjEGvg`HHzAO1| zt+q1lnO)ggo%6(1MV~NK`gyDP@b_PayH#lDX4!!DCykje%Nw1VI&Zyp_2l@e506@>`$UQwZVa>`um5@63VQO#DfE~v0I6p6VUqwruL>Py7uGUbJZg}xiI z#p#aI*%y@1pVOJ!A?3ymzIAu!{)(ACZa?&%yE&3=J}U3+GIcCr@T|wY`_1`D_2$7X z+q=KXUw2n?cxTVq!|{a|2baEo`KxoX+8x~%5msYHb3_L5m;QJ+Zg>Gp{tz9u3i_0F H-&_9x=z9B5 diff --git a/toxygen/smileys/default/D83DDEC1.png b/toxygen/smileys/default/D83DDEC1.png index 84976f7b2cda002aaabdcc4d28db1b2e4a13c2e6..9ac0098f174b3be2212b823799b78161e28ef69a 100644 GIT binary patch delta 1303 zcmcb>b%<+%WIZzj1A~Sxe=v|@Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DW(94wjDaPe(%v$I}R+{ zvIi)FjGjM#eh&N;`h+M_3rKYjY}{p;r+K=keN*RP*ny?OyOXXnu~$1YyG zdgtD=m#^NvfBzPQK0J8x?ELlH+v*RU+I{lE(F@m(UA%te!nK`8&jRgen=ozms!c%O z%*21sKV#?PQn40xDgg>H)m4Q=lmsP~D-;yv*QaIXq$-qD7Njav zl`zq_AKJlD3%Go5j%;3bPsI0BLfB*gL zt~;_mQ#o>J_2;C?owjRs6&Y^J%G>;Qli~ZnA1g}IZlqt{5Hfw$Y>~TrRvg%V)8|%A zq0X8|-S0wqm6^H{1UHx5+Ab|~#HVx8-$@+5+p)bQ z`ch$Ick#}cKRW#rS!=XbuZs0bK6O%K{mw^Pn-3e;mrLg`! z8-r0%;TLs@k8PV81U}E-?7)>FDs|9IJ=j>H>3H@%HKxS7jyWl&9k$<vwq|^R#yb@rv{R=t#xRKEBMcAym1(rGodS+w77s-_(BBjx{@HJNZsnJY$tf zYR|HaJzduf7q3x_yrDWz%H+fpwLV?8*m&cej{lf;2t4AP!W;9UBhdL_qtpvtu^hz? zfnOYziGEJkPYAz0cVNm1MGsETD1q?%6Q1)w5#BJb@#X&sj8d=GW%M08wlc70d#Kqi z8BW&rka~ahlgovcJB!X?Y>k$!?T*ZGn%CdUB48yLU92wo;X~8&2`QGEl^gd5ILx=0 zalzVsF)!;u_5aQ77r!lL{n@fak=yam0i`?3o;>LJ^LLwk)#CSD?-p%;9(nF>>4Xb^ zJ&Q}Fmp@9Z{Vj6B&#?5fK?Amu3^}r-L$w!MWS#Vz0d{y0EcbZvOW-C;px(S8h;$dRMUd#*NA9K=b8Q0yD+7ZWKKFN_Xvob^$xN%n Zt>L90Q##v3MRhI)22WQ%mvv4FO#lQZecJ#4 literal 1360 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij|R}p_z+? zlbM^NrL!xL>O)SYT3dzsUfu(?ejQo=P;*9(P z1?ONh1`yp;U%Vogx=Kz!?xT9jFqn&MWJpQ`{4U#m z7$-9@FgAF)IEGZ*nljni`*48B@$+X7Z@JN>Z?&R%_5%eDKdYSaf)g29UiWu!?dL-m!mGHK6ru=&L(v|n`Hcn3#jiuGI zmVGSTZ#->9^KUg-c8`O_lgo~J{M>YF`wWejahLx*;VjPBr2EBKZ-Me@&d=NrU)^U~ zq#^hDwHV7nHP1_Izh>IAoaomLTeaQDPAyjKq(rIR!Ou7LZ&SFr?QxR0d#3Bj^G}Kl zY^JbAiZrKsyg6~`pu5lImS2nex+`5i)yhwJwEN-H+A{_@;%=KHJ$O&V9zQ3_d$}q6 zFw6DQX1+NGRyK3bp7!?5jBg7*eM@9kT=M(fhy96f`7{z@H|nmOru}h_>k^UrRr_z4 zZfc2eT79)iBK*XY!?Df3mxw3k$Zf3fy%5&J{Iy4R+BxpIp9O7>6)$?P&&bA5D|IJq TOYZ6nP#Ndx>gTe~DWM4f0@~*` diff --git a/toxygen/smileys/default/D83DDEC2.png b/toxygen/smileys/default/D83DDEC2.png index 9149a022e9d86a0704cb228f8a4da57926cadf00..2e8a9b7854858126511a9881263dd2568be1e429 100644 GIT binary patch literal 1366 zcmZ`(X;4#F6uw3_3B-(3%2JRhpaKF9iVBpXkx&w4F(D#uH6aNQ$|gK^6#|T@gdM3k zqF@IVkSZ>X7^6&-jG#z^fCFM^(U<_z$`X_%dFf02V}E#N?mct9d(L;hd*{wMwI?jt z+|zBz&bl>E*Is$bDvBXd!05{698m1u{6If(gC;&Md0LU)_UW?0b>qywR~qFE;#2>zPpL)45YEic zFCudb2(Csgd#l}Ye*Vogf-1z-%uUQ9pi)h2nEzbu4c|dIdQD517N&oy&yAu~Dpk8w z3bUZsLKI&Sy*rrn4Tdq$hz7=X_mD=`HCpVFGW2ZaiPi6++KmcdFBQ5{^8m)3i%clL_xgyM3VegB0rqe_hFDy>$_ zKOwyyw{EyGcZMxECEt86)k~O=G?>Bd$?cFQowuHix2c+{`knGzNih^jUAQxYMaS^j@OyUgugvjF|dvjNh8}ra8br`z&wj>y7H{ zf(ZAQsq1kn(dtU}Xi&SP-cbPd;yu`cCPh@BkobgEf0uP3+#xA2(XQB)Uf0i^{@(wj zrL4QPCBw9ooA5)H)WV^=I7L)${~qTy%72Vg1jUzJ*;o>L4or7RCE@H&>_g1XsXR5t zJ>LZ{i44B%;$=|!_<>tdQF?InqpN3!94spti49B7R4dv$;?|1^!!Ph*yl`jCjvUWN zjNG;i$o-n{2dTqz_|5OyLtVQpoLw-y&9xH4lU$>Vo9KtzXgwv9f~CTehRC+o z+QnzpScNOYs?`8C52ViW3OmdOc6elMvvpt5C|CgS)axhX#tfS-YQ-8SZe_dXDhi>nd8v~+G@;V?GVDc;mQww*scP%&j?vWVOG!yEbwv$}qDAeF_0+E?b0 zmv4-YUop&Vxs&UmLgE}@Ikz|dmTSPJ^o#VHm&(ZwtcywxFuU=q(gt_ zB?^g1x9UIKJGkZ{^i|QARDM3rXFiYF=Z&`Yl7?`NC6VSA!4fpWLt9XH_>BpekI~7w13b zk6G6VZ#_~HclSUwo=SG!HZq!&=X2rb#u7lx zT_MO1f&#u9!2PcvF`3Pc$^LRdcuc}gw7^>5AtE^j&Sa)?0Fj*>%^^?{nXw!ihsn-9 S+Rf1qJ3t8vBVF6cDEJq$oo569 literal 1380 zcmbVMeN5bB7(XT=I|qZf5yhF6GMSs)wY`sWZSEvT+w*REc-#>IaZ2g+pg`MOTXqF? zgOMl$MdwUr7)D$+k&F?dn9WRcpmC0D!)=nF^J~r!$4quHCPtGPd=EJE53)aO+P=^G z_WAvu@9qzjKbmFCHyR9vET5MSf;A`kWM+W(=HC}su-vCH)mli7Xfamd4eqd9&qF?u zZQz4E8*bY*#XAj#J0e15wN~waoaAKD%qDfrx)=p$gTYyW+RH1s_GXRQANjIlUu9@;@r?h=A3!DxF~3hR?pjg>e) zWmf=pm$BGwu_UGNtR^d!vfPsDfIy?H$!enjz9+{ z#g*0vo0Z|=B``4x7>(KO!XZ9D&zf^Fnw|in|Z=k!e z;^LVn-G{qhz6_szZ!phIpM$Scmp*02GoQ|2I&*TzPl(5x=7-`nJ;N7%eT5^r4NdE_ z)x-;l?}%Tfo%{Sf3&nkt(>Zm64D{MhopoJ1d^^`7>n$_)j9h-!HF7fTs}nc!YeGZm z4=Y{0dnTIJnE&p$P&;Nk^7+u4eIrHL;hOZfIBsg@XltoEaD1z>=@=?q`ncfTuh|z5 z-rw_JQBC`v*)8if_v-pk<|k>>g%uxe?X1JhCr64hU!4!k`PLwJ?tKdH`m6fpc<0pK z^Jiys2MArWDf71e!Fx&Xp$J&dB4h;M@Ja%OA&rrpoPHDE|)Y}^u+&gX^zKY!{-tI^rJsnJxd9Urt mna`xU)0l}bufBc_Ny{+A*TwsObi}_%{`Y)LIXzldx9=aa#pBNa diff --git a/toxygen/smileys/default/D83DDEC3.png b/toxygen/smileys/default/D83DDEC3.png index affea8ec1465af00fd0f3b4e7ac8d0bac6428243..d76d3e2f8b9c9847883fcd895b5dc10a6bc264fc 100644 GIT binary patch literal 1329 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>-w z3|;>j+W#^%|7NKF!BG8;q5KO&@kfTd_Y7HY8PZ-eB)?=xd;wAnQ4i!oM1X=2aj+r% z$NvKX#Hd%F|GoV34``tGia*ak{f8Rc@O}T?zh8d-3t9DR+l@aUTWY@Uz4Pbc`+v_q z{{o=E~AbTr6?YaG%q4o2Hr+-c7etrDmAIK%8A9mgR{ovg{hQ?2h3%-8-@ekQ; zl}i&J0zD;O666O87-Zo2?Af#DUeBNKxIWqj6lYBGc6X6xO1QEF$YC$>^mS!_#?Hs3 z%CqUo12zT*rkbjdh?1bha)pAT{ItxRRE3htf>ecy+yVv$i{7cB-qYq7@Z9+=V%c=@ zx=cIUp3~Kl&wgm0yvyTkd~(UlcE?GfL7qlWzn_nP>i%cemP6X*`Ej{M+uB!hhbjjb z&(3|`oAJK?_+nv4yV)%+b3@J6ZK@P)%iX-w0v0jli**AM3$GYmSh|Kh5 zydOj6Nv!CpQJftS@mO+`Xpom};Bl*WVHw_=Re8l{SX@2rdj1PX-~l&Jmp7B7riQpx zon;Mw%KEb8y!=}}?OVo4?4OPl$GEGs{5E3{n7Dbd-N6p?C9EwM>id`!7ldfA+??Zc zHi1J-f3GiVODX$C!K(`2V!9O~%%=R(ea3Rf{#3{ZrcJrdR=kE+Js7?uwH|&Ox^;4K z*~J%|xfP$D`+fb~Vqw{n2bZ;5KR?sn!yRS#mtDWS^RM+v#^`qi@p|+B=m>?*K7QfA zL9K^Pv5RvT`xtenr1$%N(6RL8?mB&^MUrXWhDBmKd_-7X7oTcTKj`&h-wXQ_TxX=@ z(${syI?%NFM?JSuRh zhDXTZRAs>wiv`N59u4UcT}M8ZY4p0irH@4s`Tp#6dW zN+(kr>Sx{Kcp;Twa=1q#Dn2(Tl&Jb z7#dg^n_8KgXd4(<85pGU9o>ndAvZrIGp!Q029e-eP;n*+vLQG>t)x7$D3!r6B|j-u e!8128JvAsbF{QHbWGbi}WAJqKb6Mw<&;$S-EK7|5 literal 1317 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDj~FY+`QU=xFE+askla z76vYEjzAxq0a=!ouEt7Gy(#2`nLf}l`k=&tlvrRwz!V5#!jnFb1J69EdB7Y~1k4(S zhZb#QU|Zr`{xhuKaR^8Yn!)4WKVwV8|{1fg4b+r{+-XC%$d9=KaV4`ZMLdP zVW-wZzJS-Y3NjlcTXt7egc*6T@6K?Ld*ODwYr3gZ{e-OF zO34o<%=TGxf-yH@htULWgDw0Dl`r@B-_Y7QB_LDrpm)WL&xtl#s}eV!@rVhT(8K97 zZ`xd;3hRWuQiku+_I9*o-P-)Vr*pG@PM>B6zqT1?QA3~BGo$r~4!qsN@4T03;$tPb zQ0Cz0^H2Ev7Ubq$y{?b_KCckVW2=kGvqfDlu_~-je!Q&Z_e_SSx=pX7syMWNuIj82 zVOqQI@9U`Gxi%i&JNK_Ypk7Iv)(li|dAj#$8!Jx diff --git a/toxygen/smileys/default/D83DDEC4.png b/toxygen/smileys/default/D83DDEC4.png index 1ba41912163d1c14d3123d0820eb5b3a7b615ec6..2bb6658e017c22e3de6e5a8359eb5327875a449d 100644 GIT binary patch literal 1244 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>2qoV5t7aQ2vFX_#;E!dxosH3~8?!l3y|;z5pqPs0VT(B0xciIM|T> zWB-8wV${he|4%>pk7?kM`@i947Hs|s)b4)enibGJ;w3?TAU`1k&u7n`J@)gQPad!_FfdhDg+!DDC6+4`6y>L7=Asy}hjP_J-%t|uPUKi7|cING*-8pA|@3@=SEcxlEiOSifFV$Sl^|da% z-8P{tUY33Kk5$#dhk2TnERN_nm+2LK*IQkF*zq>g8LjCR+jqa!+vwiNkblMPhvMry z6|0u3>l;tF`>4gvzaTSqdsxTeoewo99ox;3|6#d@=mM@st!uixx=mBcJuJV^WPkS4 zxYauZUAI11J+aF4G?1|8668`=Y z=X043D2ub(PZWI5!OgMp|4r^4467ViZcmySH9;}M=G$5!Av2jy-t}oo{V^_0rOa3J z?bILqTh#fXDdl!sfmMx`Aj73@Nw)3m+3b%W-CA_ck8`2+>v-vYuM!saxq{DPjO5G| zrzG9#-xI_7?fn8Zo!EzVK6OvhuO{TqoX*DfPD<@a^pxBSOS-edzS*#xp0z!bb*ECS ztHOjX$(V#0W&&MKSG%SrrG>;_kxyaS+V`|+=6#!;4*Qt;Hk-UvO?&h}zf`Ze4QLv2E@_%S%rc4xfE!`?7e#LvfCU$tM&ya3^;D zUN4%IqJPY^Ow-nG`Zwt-?dk6$^5gBqq@LTpJH`lfk!p!+L`h0wNvc(HQ7VvPFfuSQ z(ls#GH8cq^G_W!@wK6r)HZZU>PC33UJf literal 1251 zcmbVMTWs4@7q~8y zqMGR@v9?*oeA*5*=T8Q=n8a(WY@c`>4p&=e98b}OGn4<&hS<|{aWVcv>TDB8vB2wT?GkmO=QHPt|D3>(;AGo!m9M zuEkxY?No+V(%;s=jHz6Jkg@a`Z0Gwe$AQEL5@H|dafF>z)GeHnxqYO*O@b*O@Yo=Ub z^Fq393$2pGq-`54Nmi@XP&FLV%?T1D5(!s>rh^C(wB|Hhss%M`cSk{hR@qbxThTS# zRg_Blteqr~ryC)tMmD=CtXb_up_Gv|$sj?9B30FmYaMObdHAmzTcWMPoB_!^wDeiC zjOtO@-65mgJ=l;7MBcD3nF=b3G%e`mSruw_Mo1FqOGs8^mI}vtCJp!)&!RcBY)LD_j$H-WZF3V3 z(-=h;QSba?>V4ovR<|Jy5-r3R)D7RDwNZvuHx(YWtA=L zN5hdA#c_Zakq0R5I3=Vx6fuKliG&y?+FbenUX6vKH=HY=F zIx!|X8VBL^-#P26x+u7KD^`lhFf5cfibdvi6Q1r;3y|;#rOZP1N z-I9Fg?_;ZPEe!h>w+(NvH)ni{&D+I*B+GZ2k(>3sxie42cHO|9SiIsi&aRvr@mH>= zmXFqNe(X^OJ*ipexf6Zg4#08hV5>KDt-03K1S^%pM_Tu~0xw6f$79Qj=T?2KH^a|# zJH&T;d@XV8^`EvCKIk(pU@O^x_kL++pSOn`KmT6ef;a2Eb*lG%N?5pUcnVLS?fInZ zjsE`gKje@1_pM56^)owuyLkP_#*J%!-?Kma9p^5i{q^KAYOm&nV9eBV4&dU)rDJtN2bO6qa|Z(yezN10bX&&Ax|PDUIRJ{=rC{0~#>q|yKY diff --git a/toxygen/smileys/default/D83DDEC5.png b/toxygen/smileys/default/D83DDEC5.png index cd438cb8e3470d5b4395163c6c85dc2bb592b8b8..19f966af4f011c17125cbe4ea9a065d5faa14a8b 100644 GIT binary patch delta 1328 zcmZ`&X;70_6n#)J7|;qNf(w|K5O9QP2s@FoXe>wxkOpD2LXe#Vh!6_Kz!$=%$Qnfj zgiKw~Vqs(-ETVu^p$Jt5bVO1>8VHMmEWrfw^<(_g{^e*FWIwnVyly8%#FsIwlTh19rE4~91Yd=db{OaPP!B%B5yi44FD3jn9<02p#_ zJoGz^GzNUXXVQQe21PK)he0k3ayCGQ3P5Y%{3=LZ28oLx?jwkq2NAO%R1QL>Hy?{( zXp?}{Umyhi(kOz(J@E2p_;T;&xD_Q__&dC+gpuf(+?-zg9A5dnso_~h#O(sT0GRCKuo1Q zRIuw;5o-qG#njeSP_X#L(8a+!FOlU8c?|mIf&LwyGMQ}Z_|&8>ZK4^$HO_c_cNEva zb`~N9is$J~L&;D((55I;JI4Y5G}HOaKprC`8OKeCkBH`k<9O$|;kcAohzo#2eu=+d z{RmEXeG22O>ESMV@6fTHR3&jv3pmiTl(_!(bqa}vZ+unzM6MVJmgm4_jZc#+hb`EC z!{sLodXJwRYMkxQh?2C7#OilQ{ogV2@9XwcEOfeBqJ3U*tU14O%IEws1Wih)fu4`_ z;L_-x_+iZ6B!}y}deu;E@@RglrK4uOUPUD;o<07zNvR++eO^YLY|2Q8rNq!Dd%SfF z0^XKJ<0O4q>Am+1n>#y)rXGxj+zB)*GkX1)v}e3I>-|>4TLS*Hp9x-Pjj6BaTypH- zu19SVBD1CJgsSespE3;Ri|aI6xr(WhZR3yy2n*x<`D}Mxc0D8?HF&Ok zOVtzC-h_ z#Jvl^>5FsIwE80 z6Y`S-E)deV)rKxB{e+6z%sj7{Y=dVOs(D{G>W+BmzL$M)H~zNT$pd(z&aiI~wM>G@ zimQD&bm{UpnA<-4z#eS*HB-7nV9LVIda71hmV-tuPy1sdFQDerjGXW(IoxIWlrpZX zLOFl-+MpnaC|6cF{#J8pF+5d;S*R4qCo-6)E6j6-TZkJP(%9S^H;v|zUkw^PkGe69 zpuy99vy4tMX3H3MlH}2P%6Ht?V&`$Pjg5^R11p6~Jy2A#z&bKA652ObImmB~adaI? z8Xcb=dKBd{_JY6P=R#E3kmh0bs@0MjJFZ%y#mQBLEKawPj_8T3tWMwIuZ6k#^~p9} ze6z@1WlhvW`=wu@dKMcW5EozZH;0-6Fe7*p_K<(ZaB!JEm3Z5rM#AcA=`qgxh2RaP zB|_0}U8hp+tMYlq|uMoy0b+80QmM; Af&c&j literal 1337 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+nA0*tB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij{$}g|nHP zp|iQOvzx1tp`oLziJ_CJshOdnvxS+Zvk}Y;YKL?fq0y6ST@{2R_ z3lyA#%@j1kGxJjN%ZoKZ(F5_VOKMSOS!#+~QGTuhIDD-#vDj~FY+`QU=xFHd3UmR` z-xdZgZjL}7n*mvtmafK1P`xSSgqc3jG5Vmyfs|NaLckOVV#1R?kOR*=sd>O0Qv}Q! z&v!k|Wnf^;^mK6yskpTy*w&lbQJ}VZmIIgeYK0pSH*RRUT=8Q$I$zM!fm1B3X@R5S z)YgrkAG6=M8QEf@spIm7MVhs{r6Xx6+n(K#>EGX(?e03#-+27onR7PJ-nKi0KC*vi zC?=uX_J=j9P+nM|Pco8CWR>NEr5tbk-9DP1I94lgV`^X9b)kX+P7(D6pH;K>XE5!Y ztk30Q?_^$F!YXpC(K#+$dhdCM1wyYl#oSq*wO5y~sX6l7pFnfx*${FZV=6;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2!1y@8C&V?q`g(lv)!4$Tq1o5}{$=?6oAu{UhOb{4J~A-8XJB~E z!0?=b;VA<{bm8Ug2i|Pi|9Zpjw}1ck|M}DN=TF=3-;KY2*Zllh_9MUGdsf!hl$81} zv9S*s7?y2*zIgq!d8=Oh`BVD)cmC&(`5)h9e*Yfx?OVuKU*FHJuAgjeKZS+eWnhRc zyu4-K>-lS*&RqWV%+Bu1n_4fduRXM`=IdAQ&u%UsZEQZ6nSKoLzsv(d0@Kg0+xcSl zibvCyJe;`T!Ox!wH`bMWiSa(QDCy%zoA>6%?{swDsHnbma=OgG;F)-O^|t5JmOPj^ z|9;Qxd$-Q#?^#~^G0ErYg`kZyv(FsxeP^KkT1DlRoZK60>$AY{jy}G8)6dZ*=Ys_%fO?H*FEZ)b-Qiq zt=isOU%q&J`s8qWPx$fe{%_xEy?(9yQd;_jsK_$`ftQAc#~B#3-S^I0^>F60`%@O) z?U{A=%a{01pCUee3i|ND``tUYw{IQZSXsT$)ji6mykB?tKP)JxrR7_k#Qc9YE zkxxcePF_J#Nm)fzO62iEBhjN@7W>RdP`(kYX@0Ff!6LFxNFS2{AOVGB&j`G1WFOure^< uzkF^oiiX_$l+3hB+#2fo%JhL6BtbR==ckpFPqdTb0wyL`KbLh*2~7a2NSayz delta 582 zcmV-M0=fOs2ZIHW83+OZ008-bnr@LH6O*F@B!2=GNkl`uhL*`R${l0x&ekCIEy$6a^QN152>~z6?zb z1b_Lu>O1p+i7`B6t^3)y2dJ<#?4I?d4x-E}Az98Z`2`TmzkmP!{`(J9{pauh-+zJD z{JOYLhW+QSzkh#x`Y&wke*WLjpZ~u9`2XuCyC4Yba z{-vaM@9)w8IKSO9tgOURS13-WQ0C>!I U_c3~yp8x;=07*qoM6N<$f(+3!761SM diff --git a/toxygen/smileys/default/ae.png b/toxygen/smileys/default/ae.png old mode 100755 new mode 100644 index ef3a1ecfccdfe9cf7e9fc086a2c6c010f7977ed8..670f615519861a8bb5b780322c5dd00360443ffc GIT binary patch delta 718 zcmZ{gT}V>_6vxjHbh=VvlKGGt%fy=VZf(fshHmUS=lqy%)fG0~oLg=GU`N zkctV$$O)z^%T!uZ0nmL4pzjJmgj)4|1L!~iei#5mqX22GW3$y_0GdKwu0>-21O;6N za8ejRJAfU)3Sa`z1JnX&04gBX(?dF)WLKAC$+9=eXD5kaM3vg-^R2F~l5OpTMiZ^7ik7LoUa!yZ zUs+ipT3YZjC61y8u7d*{AZoMWN@b*|XnA=#OtltEL@p1DM0`GFG=+GbJUN!C>U)=Tj*J0dQkOM6VBHSXg^btyZg4s*;itnM@{;NQ6S6Kp^1pcx*P? z58wsZ2lx%J3lNLN0xHZ1E-Karl|tV9o`w`s2NN913N&FSAvr0V{&MhSCN;XJluNbr zb#w_m+7GF)Fs$D=zA>JtZ)I3~k>gaYK zS!kR#oWHxd?eIwI?q?R;UhfqnsW;@NeYrTX^z@MH;wW>Fo#p8re>cXyMVm-{BG~D8 zaA?Rfxn`-$Dg3zW|Lopw65si-b3Gefw7vTDD{%G8_g>G|o>%=m@E00_XLywd;x!cYJV3F^?pRv9>{z4s}F<))i2Ml@v)doR(F$s$_=`kD~( z z1AmpRbYA%%2Vi7me5m>mAb?naPGtnD20MY__n-f`0mzg8{s9CK*hzmtW)s%H$oS{a zAAkS?JLxaT`2W9u;n%*AtVs| rg#n;F00G4K6&$BCuPl&6grlz#Cw5X`)Uj~NXtgJs77``$vqQ*(1& zU0qdG)t}PR-}(8!GBSTACH;tw{uUClt~S_py{p|S+psNR489DKl9JP=O{=V`Dk&-X zm7o9PHj(%%y{p#brvck(|m5td_(|`^B46Y2q!org#O{%D@EG{n2%gg(fk@54? zsZZ^#AIrD}Jl z@3w6Ibn@g!8=D20RyqrHROYBUt#o2A28IJ;XJ=<_Zf<;h{P)PnFPqkXICd|v|cMKPYPESohrgVQNVD4A%h}A zd3kwZQBihw_LsQ$PZ1HHf`UGHd%tsYd+Xq^AkV>Uk(uEwz5h5>H=O_Gj#T zT&m1U(SN4{g${bUIEHXsPfkckNl8gcOH55oPJaI2$)ltP&mKN}`dC~*L`Y0fRCxM? zl!PY}Crz6;b#iz>NKjZ{XmI+43sbILns)i()yw7vCPrq4sRqW|H*DFoZR6I>x^mO%g_ICSw_;{sxc=~pGoIi2q)VY&q zPwQ*wBza8i)6mn@)$U)tV$G^`E7vA_tjy2w$UO_QmvAUQiFgrgOP!ek*P@*WX`1|NdM&doe%&u>b*3^~+bU{`~&^=g%LYy8pj^{r~yv z|9`Ka|9}4c_w(m__W!pfKK=atLxM{ZXbC_7vHbh@@9*EgK-ECyzkh)Y{?EYhFEQc& z-#`EN9{7Lq7*OE*@9)2U|NQmmC(vO40VtezBLF}U2%?OI(TG)&JC>!~;SYmN-)zb6 zgc(7A-^Q(#z0vHb9!CJ#T#ii{@&pjekAH99{(#N?1vKR651@PgadP}IGh_JvgOP*n z|BoMke*F9X=lc(!B-jRk0Al(2;|I_=K;=Kd0O-rV_wO=^Oa9~L`E&gSnG!{UyMJ0GXDMz3==+a21Q=s-+zAp`S%CtX@CG?WIzQEfnf@cV>t2v#*#WX hrhyzT27mwq0Q*I0Q;uGDzyJUM00>D%PDHLkV1oDgD5U@Z diff --git a/toxygen/smileys/default/ag.png b/toxygen/smileys/default/ag.png old mode 100755 new mode 100644 index 556d5504dc28d89be22ec1883f12e8d8c07d5f41..421ae035a7fa8e832265c40d713096c300313d52 GIT binary patch delta 858 zcmX@lGJ}1BBnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34fpKSmPl&4}Pz3|Sb_RyNY7nTbtSm1tuP85RX(&9qJNL}a%$oAl z)YOESn3#z0@ZjL!L_fbq28O2$4Ckt=QsU#Iqatq~4B0q0@YxBUCr91u*UfOczu(r% z(%jI{&_G9Lqo?O;28R1U%PT7v1C5A`ii!y7sSkX0%KP_y_h0v%UY)S1&o=`q*VfUk zQd7I_;ekKKu`@2kO+0C|=8t>w&iUF-Xn zMh`**wlXlhZLWKnp7qwipnixpgN7~+A4RwUDh%%Tm}YAdGX7V z{1^FI&$H8>r6=7^OZ)of&6juYZX_h!5AnV0>weqa@hStuc?O2l3=Ah37>+V99A;qH z%fRse|NpSM)uq75V@&dPcbPPe<-Gz!Jp%)KiKnkC`!jYvE>*smSB+Yr*!6UA4B@z* zoRHwc%HwlJXHCu4U#>8;CqCpRzdp54B>e~dhRjgAKu7bZTe z7n~S)(QsqoM;^{j&y$)fGha&1jJ#>Nv+^eoYpd%~)upMDGoOl14ZUi*m8bM8Z?ErJ z-L<)IWeew;#%|@gYrD7hudtex1q;iQ=hv@2V`dOw)OVFzBYg|#2h|eSh?11Vl2ohY zqEsNoU}Ruqq-$WVYiJT;XkcY*YGrDwZD46&Wngf)@*FRUj@4Vc(yZ?|y&({^!r{-@pI-;rsntVbdmn001!n&i@21 z000*N|CZ$C`2GF={{4Vw`_|n384dgX{rfT@`!PuSTYCEU`1=3*`+*%D`Tzg|05Jg0 z|9=Ad0012R|3Cfx{Qds_{`;}M{W&fA6&L#!8T&ji`k=7+|Ni>=`}|7!`YQYT5C8xI zh~*yx1DgQ1_jkSr_QgN{Gn)!7=zkRT=l(yS{y%^Jd$>%yrhnC7U!MzC7BKSs{`cq49|i`1 z0Ad1q9Oyrwx8%8gxgMM?DZn5l#waDq07Nok3^KyZe!KfLdAbs1hRqQ z&!7Ll|Nj2-+v@Kln{WI0BO?F9z`G?&e*L`u>(B3hzyC7){R31C5I{^o40O-`KYG7_ z2K@c?`}dZ0KQ3N`sQz*7>htdIzd(b30~P!Rh9S^N00G4E2jqvpOk(1Uzkh>u{&_uj zu2fsweTd z5*Ds}GvWmRNZ<1;3Vm;f^j$$Fiw^Ml2LN^T0Gpvx-6B8@0@+|vKxI5{IC$S{ za6b)<+*%(T(c6~r37giuBsF?Y8tzFY{j*iQ?$RE&_4_PMM>(>rCAGBQbO3!QBru?g>QEtPJ>~@US zYG3o|gAYzW-wdulY~9}x0$2>8N#2W*>TeC4(HiS6d|W%sQJK+>QI=|QZ?o;$T43W| zX~3&zU$6sEAn}sy+AsX?Tm`zBgIY&kCzjK0r>Q10n{DX|bJ0zoczi)P>dU)6&upCp zkY>Qn&M&We4(Vp{Ixwc%z8AHUn=EPayQya_^dT?RZXW#|sO#&n?& zE}_uL;lA)Kk+CoSlqM#IR<|h`>FkI(0gor-i;9W`3%@>k==;q(U$S-U>)WHZy|JTB z+0xvKs?L`%nQRs(Cogwy+U=a3`Po&1AjG5;DPdRgu+emNXz=>AlFCZS@!G1I>h&k9 zPuKnmtdvAVyc)UXmDi%am9I87HYv^t<*_5s6|;N1 zqUY3F8IZzKR58)Ip-`b%3Hg9TSR^SX362+s1|%U#NeRgA1OkywKuF2ENW|251R)`a krMi3JKR|hf@QCQ-{{uG?-_Az~J0nAukQo_<&v;+`3!-MSasU7T delta 582 zcmV-M0=fOp2ZIHW83+OZ008-bnr@LH6O*6=B!2=GNkl_WA(-_y7R<0st`p&i@3?_yOpF zEVcLzN9G3t_xPt7Fj)KGp8{cjMDP0k|NH*_{rvv={s8^||NZ~}n_QLxh-LYgZEWYh z2Y@q26ceiU4uAOk0Q&y^2Jx)`@sR}!3JUL@{Qdv{0Q&-n z#ZQu<^YHymcfRod|I7UM@6?qi_R9-ci+(%!>+i3>|A9(a#LIu(EBeQ4^Y=ec$9I4L zVr0-;)YxkI@WYSafB*gY2Soq>{QdX)50L!-^ZQ=~BYAcfMxfxwAAf&8e6b;`5`Q3o zm>7Qk`t$$aFNo^D|9}4hss8l`6gdC>|G#nJ(bMn$o_%Myar)uEe?S|40R#{W!*5{F zF#sk1LeO83+5i3kbppfm|L^a=pPhgB7ii(vUw;Kzfqno8AQqqvK=Z_T+5UoUfH(;l zK4`|6(ajj6LQLHJ!z~LjSlUuy&`V1}@n=sCv#|TPmpy&?&iPJq&iNwS zt}=#pXTAXdC8B@c9R6^J{{wruP7Tnx4d9l{Iqik9)1FHF>mUwXG@9V)s+d1J?ka297-MX&XA|Otd#oy6iTy*{aiBvFfQBtw$kW z(&3L)(zwHX86d=fC*5t&eD2%srUSLLCX>mi)!5W39LHr+$%XQgMU`Z}Ot3`gF9L+q z;MwrNdPmDfyKB&H^I0t#t+t|~0>dzgSYi;NEq;{$7#BokoW<&@p8z(#I-c@6pL!au z*lTs%I9A7 zx)N@Za@pdfHBK0pt(rg0l?w(%Oe2p~%A(5RNSWX}fcpS90j2=P0nP!O2H4u#@{gUY zJ4F9Ht-ex$jUIWJA)yzBSE8 za+xIJ6qWOK_Pe{@*|}q6D0nI(YkK;(2dt0sKfdt8j}y&JoTit}Pg3h^d)|A0_nFbL zZ>)&9+7gS*M~elhaO%2$@ delta 539 zcmV+$0_6R*2iOFV83+OZ008-bnr@LH6O)GnB!2k2B`5R)Rsa9{_us#NEPwybdH*$plZol? z-??wTWO6hA{qu+M&#!;Ke*@8#>5BjYh=lGRLu zzkX(OGXMMa``@o$|Ni_2x(y(Jn3#TnHT?el|IhC;U%pHK`}6Df@2@|9J^lXU=Z~Kv zfBzi*^!4A*U;lv`e*Fge3Lt=(82$q_{C@%J{{Q=r3d3K9-@i+^8GnL7B`=e}zhCPA zfyV#*3$pe%(D48P1PURbV;Fw_`TO^`7UN$9_Wx_&ePj9k>(8&ByFUC&VP|Cj^B-t7 z(2+p1fPMf7AfSsFfU1G~KR0@KuvavJpcdz07*qoLJrOR;Ep6_9dD)SxOl(sVs{XL{gZg?Oof@cK5co#kGSwTUsnA zwLpji99F)J_>s6|4~sG=3KS@c6`aO7)Xfev=LUj`V(I5?doN@4Wp8^r$^ZO*4=3mE zXSkD(u~#wxU}w(KD#v>)+q;@Qq9TB*%>ak%0pf|(;imv*4!}GLz&io3{_9gi#W?^; zzX}T_c~p8jm6k@aSh3VplF1|)3=$v=5Crf8_y86Fo&zj(cGB(bbXyzkaL_F+bW;;` zMo@%{T_Wo6NQ`$(-ef?;8W zA}p0g2x1n1TCswvuBHwgAaOi`ei2rbgrw4tSQOIhX8_i$IqPuTvsT@xth{bAO%TL2 zgJDdkyR25bRjMJmd=NqUg~E#hL9bRj0-#~qm|ZO`cPy6cM&me9?$PTL^eBdnD3wDp z*?or-6-k;C}A+Vnwuw$#)llYi_SfZJ-d9}wom=8k|_kjok0E4 z5cTLnE=h1;@1ir`*R$*8`5nGN`IH=j-$49EUF;`YbiA7MRFY$+$YmnzHUx)D11>x; zhy^aG{Cy?9KB=!4@fLvpDERBZcL)}&@Z1E?2zX+E87=&c!6PL+lEXs;rix)|FW9^S z@FK9hy!=iI>xabilhlIz+%Dhm(nJ{`xuT#bFL^F`bxQhb#k_rMA{r~m-6>h=)u1Sj z<7yqI)jav@@#E<^44s~xomv0Jy0ve;na1io)}}_=Ivnk-QlUijc`+jPPU4f(t-sw* z@|(k<)Yp$TvkMDTcLRP$0s@NkwXWUXvS-GwQN4$bM=Gw zzdNT9IMdsG)8S= z0GGq%W^lG=WPU8kxMdN;y_2d; znlKwO1^Nq5`vuP4R{*gvFfjoY|9|zG0VMa2;U82Qgns{qYWw}0;rDMw35h=p3;+Sd z0)KSR|9}4(ASQtkLN$m1CV&5705X362095KfLKJhW;OjWdGY7lzrX*$DuLu*pcFC# zNdEr)SL!$SY=)lz0mSm_*B_u)|AM4J|T5aY*Q28M48|9&xm41$orFoZD}AlyHoNE`-n dxflQf3;-DBhs~zWSEc{}002ovPDHLkV1gk7#lipp diff --git a/toxygen/smileys/default/an.png b/toxygen/smileys/default/an.png old mode 100755 new mode 100644 index 633e4b89fded98256a8d142dfb60a8058f7e6b67..796943f8469186566ebf04c5bc45c789377c8250 GIT binary patch delta 748 zcmZvYZ7|aT7{H%h9gaBS5+AlXrbGB|G~G;QwlNzU#;NN%GA`YDNlWpesd=j;J``$1 zNojd!GIHJJ>W+5P^zMDu6w-Rk;Ch3jz3(4M1rD@Gg-mQmFtKKA$UK?(FPrZ|`ga=m0hW zwChK#uC6XGFE1`GE-Wk@9v&VX9PF3e*e@)!7Zlj@@@#uY?C$PbtyYW0GCx1RkCK{d z=W@ligoHgh-E1~%G#WdHvlmC-rBY2MlUl6?C@(AfmX)Ot2tM+8az1~Il{hjnxiqd= zBu9OSijswdbP)(0czkE;#k{XEbpXv6GpO zYmRQt1kCpg=>%FDW;5ahPU_rjz_0@I^G{`x%iL1J92|;@uvqMs3wJSbemNeg#13c- z5AzEmxUS69&5)2v>r!c&-kCP_s{W*8=;8b4ikj|c@`k?lse7Fs67k@Jp4Rc|H%~_& z_qKhKy!Mm~Ka#zesD0Z!_N4!%n3nAxUy^w3U1Les9fnXK;&Mf2<1g2_&nc``(_I^< z9co&HHC`Q)1Bur!Rj~3!nJ4(LMIGFdN`FuOvahTNAp`JhwefSg=>7x|GfO1Q5#>>G zOY_hGB!t8vg(6X*#7F@Vp+uacM4k^p5DJ169q&*7$0#fo7UY*78UGh>7rnQeh-aJu M*vu40A3gj2U)&v24*&oF delta 426 zcmV;b0agC12IvEj83+OZ008-bnr@LH6O;V{B!2;VNkl(}33zy1Lc&_o7?KL7y)bkd){fBym< z_v`mxpbeRs`+WRVot-5A{r$IX$CJFg1AjoXfO^2j{bgYI0T4ipZ{NP<=jVsI9s;bb zp*{wB=E4QI2Y{ZwbLS2~0I~c7g**d_2B12KYNSwtgbL6h009Kj09Or-JXu-B-@k#9 z3_ubn$OsG=AcD#Q1Q1BWt5>g(=ES4z)+>ez|hdb!0-zw z)bN6Vq11qZ;Z*_ygVhWM2JwP9y8>+(7#Jf1d_r8$X=F?d@M$TR%58)_<M73%6A5)(dT zM1K3I{pEz|w@=z{?d{)MS-mkac^?q4R9*dZbMuF!qz`d%A0i{a-Y|UU@BZbW;adxf zH-?6nWky81g;*A6uhaCUaKwzjsk zw6w6WSi4rgzTR->cD-khb#im{4GawQ_4Rdib#-)fOiWA^6&010l^q-$%+1Zs%*;$p zO`3Gmy?r|m6iSf|38OCpExiA7?Zr+U5d>3cq4!u_7YEDSN3P@d|axW zhx$Z1fkL}IT^vI=t|uoXq$DIHr6s1OCO?1haiT z(Wzp>_0uO#og5wz5)>Agn(&1A)72}cCR;_emX>_|A~}QS2ybtX?-!r5Q_kw@tX(5{ zgEhBDCHL(cS(&qQb>_y#NZz?)YqNJxZOz|5%uLNq&Q6kt4k;@wUX+~l_))Wv&}61b zl0iYri!WVbW-@Lv7M?tbsgWu2rscdiL4}KV8XOilWMtH5#KqOsIXXHf8gVc%ywKDz zY^>FC0eVEW#5JNMC9x#cD!C{XNHG{07#ZmrnClvvgcurF8Jk*}8fhCCSQ!|w-TTmt mq9HdwB{QuOw}$d&4||{nNstW_HRZV&7(8A5T-G@yGywpy>mykJ delta 378 zcmV-=0fqj&2CM^+83+OZ008-bnr@Q|11En0PDw;TRCwB4QL#z`K@i zC`7khBJqe#HK%0p8{d4vzG6;-vS9=^8b>%8u;L2FlK$@p9DE79KGz><7*fhc>u zUQrYq>Ly``qftGdN9Sku^u&#y_NHc2{!pQmW!Y-AimW#P2%xIuU|`w@?s+0_uc0Y{ zH)D+KcDuBpuIpw}@Bv(2;L9rm5}u|hZQzDH&v$u^{mW1fHl6vqFIuo7IBIh=-MAI1kEiy^rbnX|)bSGo-=L=>FNx%JW(MP&-MGG4R@ z1~!(tVrPAsgE&nLBXhV8jY|jtvN)AtX{qfB*OnGq3s&fTf4{rgwH$z)1Yq5Yh|OF6qVv?Wmv`7Pbxxo9}XkcbGgUm3xcG{sqe#NM_zx&gx&*jZ1^` z(#?7C=ZH8E7W>2EjgW9X$X`YHUWEI8hQ0JRV|DmxyN?1tENMohfv|KL5iib)jv3J& z5Nv+otdH;Za-ANw-OX5Cbju{wI6-!MC~zS@HoLS$i&0WskVOa)TnOWQ2<3uF_DPaK zmg$-2R3Jh6pAkrc>>KmO<8e_G1vz+u=XnmhOGl58)`f%g$}NhbNRlK70>^Q$*UNDn zyJR8fbwv1mDtZvZFq|Mf9uLQJEXy)UCmDf>@ILbS0E(hhQ&S^>Se)b|7#BvlP?8O< zFtZ6N@Qi%Ch*x6VJ<1vDx!*)GuEd)e0%SYa%-fdSY46sGwAh3qp`XMHW^}}`EYd36pF;w zy|U)jwX0vtykPt8oCCIiU5-8uN}LpR~3& zRiCSD*na5a<`*?Jdrp*n{^=<-TwY&NTh)}NF;!Yh>-L}27a|?U>6CX_Z`STL8)36aWoR(T2`C|@a(f=MDKCFxMLwh|C{z_f s8z4vpK|SiZpZ-JC9W~b0H2q8bFA#dYYHd$`ZUz7)+Op!C@9EqB0iOBw3IG5A delta 444 zcmV;t0Ym<&2l@k$83+OZ008-bnr@LH6O(%bB!2;nNkl@6W$~KmGtkKnTS6^N)>*0U&@_KK)@xRfMSi|NlSZzyCl6!@qz3|NdwE z`;YlA1JmDs|G``k%02c4Ab?nY{9*Y0kKz6A{~)FR89)+1OaA}=^AC(bTqs+Zosr?! zKYxG#VyTyXqbVi~)bk%`%YTqIAOso-0$@Ib2_#t=8AP?;00a<=I`2PWHU_X_uv#Gf zeed5dn}LQhaeMyfaQhDfjEoFs!VCZb#02p!R18A@*z@n-{r`XO{r|T8KQaLM?H@n@ zu^_8vfaqYeWBBm~7*78=oRQTt0v-16AAdjqv0VH3PmA~eufHH2Fz34d=W_W6lK%hi zD^&Hre++Ca|Bt@;3lKmoS+|8snHgSw2ZjR!JV5@!L+Kwhlz>4e!O75hL=Yf=SeX9( zV`N|wWrqgnUtq|BbTR@Ne?Z|2bT&1nefJ9*kv~8PsQMpBEhQ+< m!RQYd0pk-G|7;8Z0R{l}0dVJShCyHe0000F8w&^mBSt&)g4i{uJ8v|TRQI=*4r7tJLl(iq!QcGK5 zY^jV`N`;kD+X@sgk=I}oTvP-X9|NVGI5k?KKwH3lw6#yZcHPhYSaOq_bIv_EIm!Kg z^K9Jdlr1;_IFNH%a4`YMZ2;RVbz`(FfK5x( zf?`TqS~M{+ipOI(90m}ZFhCH%56~e3+&I8hfP{o-Z*R1xH)=LVIy)mqV??jVv|3D~ z!P-^mb=9S1b*odY^7Qq{0yDpx8WlY~QK>W{7DuX(2%nGf%YDZ3QEP?NUcqzKO1$Gk zYxkzEnVT-PAD6H`H+M%6B%%?_{881sB&u}E#GZb0VBHxA1f~ZxgA!^}A@MgugI?Q~ zfX4$t zuhYVFP1BsW^3lg?gF&xSVGKr?P7f6n1Syn2_Wm{D2mKebxpS1PhX->OPl;T%3A=p? zbxfE{CZ!UqtKT?rB8(uRii#kY8!Rmil$7`nQ&$c2fjbQ0ldout$B(W4I$$vT1n?Zd z1MoM%8o&yGQwFdMumJEB;0b^+_8JO+c7SL!+O12CUBM<+{wKM{cdbo(V;~O6E2P9N z#k~~&_WR2G9qt%4nU{N@c*`7{$>eZ2EM6Ix&1Ns#?DIU^)5V4DuWZ};TH>oot*2X> zdCV4-MqNTJE;>v*M4!83WihR?GnTu5TJ{l0#J%a68NVlBxyO-&P5a zps3gFTi@))HgGT0o%}dC=i0uDDb%P@u!se$@f3I zb#wQw^L^ixueefQZ`Y>WXD}W<_>eq4CaDRB-YL*cUinCT(|Xi$5Xc0=%a&>N oe+x`vC4CHe2QD?E(NX0M7pb008`#mjTMk|M~g(9sAcr0)M(A z3E2Yt@(3_52ue-_9wznQasm76|NZ(06G*Va3jzq}>A$~zvHbiA)WCZ6r|!9*I=_E> zHU4wR?a4N|op~&rY+Sactf4W#S(w>b{xUH=00BsdNCg{U_Z>ye2-09hTXw=%QK6Y009Kl@EsPfe_#w`E+nQ8F*C&1F)&CmFaQJ? Y00fr$(G24oApigX07*qoM6N<$g1kl^mH+?% diff --git a/toxygen/smileys/default/at.png b/toxygen/smileys/default/at.png old mode 100755 new mode 100644 index 0f15f34f2883c4b4360fc871d7105309f1533282..a420c864113a18534f92d15e16cbd772fbdb2f81 GIT binary patch delta 800 zcmZ{i+fUK~6vt1o(rL4cnR9byYt>vTKM10j&Lq%CWT2qvFn@~RWl4glLE6vCHa8yB zZtKCC4?ecC%xt>#)E`im@Xm}l<4*}Rf0o0EHvU?^M%q0N3hD-*FZU=w|U;$_V z5&#EC0K@>c0Dc4P+_)k5{ldV2;Pdf49=^Mq>+Iy5P8LNOyPdADr)q0Sqw&{YWk6_d z#ss@vXlURq7T#>;3gd+M59qb5O^^-p5r){Wf_K{X_}%ak|blX*!K4J z#>NJY;dmqxAqZk?Yb%N4;c%Gc*v-w&_4V~+G8qbmSgDkonqq@Nc65{(8KH-Vslh?= zx}WIl!#$p)+nwm@io0BKha(XPga9~~i_vPCvNBq&rW6XYxR@v?z>gnK=H(@Fa^jhp zvHcmb^z^t?%C@vJO;{6cv(XkSWipXfRb)j4q0!)_rOET>6G~-VE{_)$egpUcunG_s z*RZp*l*rdt2Ts7AZb`wJv2XB_r(wEY>_L5?ix+ZMh}&s{@7I75QkZ ztHtT~`MnMO9*L}lf2?L7IDF{H!K}=73>_bTaPQH?!~69nOHG~G>M(u%@OkOW$K_8d zL?J&bQ#z3OnVHwG-n^Wf9q93S`#pQoY>gKww{deV@9Dm^M_;ZSco$rpAA5214t4=G z^<3((UDvi*e1@yJlHNg0YmN7ES7V^URo8FKl~lQ}+$?V~d-RoCBatwlaVgh zvU1L2wOXO?$*K2tX|4E(aKfjv`Py21PPN196cfl1xx5Hb70DD98KOR;R4Y`c5k!q3 nj}4)Pe}L|5ZJjRv|AFYi_PrvIBRWi+%TEK)>x|mx8ua!bLf>-C delta 340 zcmV-a0jvJl29pDj83+OZ008-bnr@LH6O$?fB!2-VNkl8RSooaXC46r*1+)p|C6Ucr6AS6 ze?v$p`UNC^{r~j~$Yzw41=;`*Kr9TVrvEjy7(~T^Rsf9wsRmi{2ZZ2^KYxDx`o$(L z{(o=#c7On4eEIUFsHiB|mj4W3hz5X4AtX09_uIE`0Rjl(B&aGl13LiA0t65XLJ?N; zk=6ePhRZ*I0Al>Dug_Rm`2Y251|$~)1M2@@6mI}!8O6olw6y^Q5X--d7nzS8`+x5q z12kBmVFD!~j6c5_fMKno0(1^Q0I>i=iaqJ~9|lQDP=EqOfpWkQ1hM}xKuECh5Cl{V m5I~GyQ6myh976y=fB^sxd{5DM{ diff --git a/toxygen/smileys/default/au.png b/toxygen/smileys/default/au.png old mode 100755 new mode 100644 index a01389a745d51e16b01a9dc0a707572564a17625..f847827bfd7f262f976fa3f60f0ecb7342ab21b2 GIT binary patch delta 934 zcmZ{iSx}n=6on5WhNY#pSSzAJmz2_q{gV=wu!WKo0tpcIeG6a{mJpgqq1vAUC1nlR zq=7&Jgs?}Zk`X9q9qA%P98oiF6o+D^6vr9+;EOXI{9k?W>CUG9tFF$G64H8iWn*de(TH_5M_~bizjkuk>QFzet$AElx>;QGL@wKqRsK*S z&8rxVmyYoZO(mB;w#&>bdM3I;pG_t&+3+i^vX%|O>*_Y z>Sm1C#LXMvWEp}}_5O)EhEU^kPVE)mdW6#wncWL`02%V8xis0t+QuJGp8lmDUk=Ua zWvBMAL^n=EskylZo%xCW(LB9X@jO82z~S37?av-u8XxGzbRsj33KCATa18+c!8Q z6k*|N;dPDnhOWB@`rbZI zgRx&&IxuJ&G7k?w?hD-GU}~dNndk~$x}vB+QOp)s6;lIDg30tH0(=>M$qa&hhQ;>t rKSdC1f(Q$?x&FscE-R9h)cmi(fru|Do9<$GJ5v?#;R-A0iMH5WL;^%+%)n{Qmd)`&&&d1Ox%z>ihEY{`vU@3=8@G0rvO$i3mOL`~mv-`W+b$Mmr&io5dg<5v!7q0*L95jt`Tz zK8Kd(Utv)OSp_aLv){6ccV+Z`{Q2+asKUU&aO3`Kpz6wW8wp`<28M3{0mSqnB#A*- zc*8%1=RD#sSOwMznKA3=e&iEzwo{cA=YK6sviSbvcZ8P~D+{Bml_CSf4}bswF#yj0 z11td=>kSyd-}w9X_}}#cxYqI8^aBw7_pQd{B_b57x7F$E^z85V5f2Ecwbb0u!vX*? z0M7pd%ibw1L@xF4^xpdk-uedw{QBtY^v~$~{r~^Q&Ex6q^#A|=S4%6PuF(ky1%KYr zz5<8|6mWn3{r>YW$X=;7#r)UrzkmMzWBB*?_pd+y{{8#^_y5nIe|qK|`}pbGzrTNh ziU9%$sNvVIKS1ODn;S^WN%94_Y5e^4``7P3VDuLltkM#E<*8ObfB*ga_dierKmf4- zH2_T#;^*9XRi|MwriiW1GWl*NAj`nzW5HK3;f0*H}; y!518zKY%)s&>v*3s$<#h{Y?xE|A3(k5MTfb_#31aclg8r00000Dy!50Qvv`0D$NK0Cg|` z0P0`>06Lfe02gqax=}m;00I6`OjJc7k;fH_#Tk*w5sAeQhrI%)`sh;m=6_B2ZgTLQrvDjj+)?TaBS*g=hrqNNO&rG4s zN1n?=o5?u&k#Y5D18_~>Ex=3MvXSoY&o_2W?V z;!E@4N%G%D^4~)8-9X*10Dk~)zW`yk09&;HRj~k3tpH1_07a(&KcfIUp#V3X05O>W zEtdc(l>jA^03(qAEtUWO|MBVZ`2YX_0b)x>M5!ka1_S^A00(qQO+^Rh2M!7(IEqUp z@&Et;#z{m$R0!7ry8uN0000330|W&I2M7rY3k(eo4*(Dm5)%{^7JnBQ85$cL9UdP5 zAQB-WBP1mzCnzZ@D=aN80532iF)}kWH8wXmIXXK$JpevGKtVG?HA6QzL^?%AM@Rrk zN=r;lPESx#Qd3k_R##X6Sz23MU0z>cVPa!sWoBn+0BLG#Y;A6DaB*^Tbai%jcmR2N zdwhL05UK#GA%GSEip7yF)%taH99mkD=;uRFfb3( z&XWKD03~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536*GY&O6G&L(QFgh?WevWo8 RkufI<002ovPDHLkV1oK>q$L0V delta 462 zcmV;<0WtpU2aE)e83+OZ008-bnr@LH6O+CJB!2;(Nkl z;qSkHfB*jnk{~UA|A86*fB*eIVdgP_0Ak_eU<0cD{o;k^(w>d;pa1{< z0DsAUAoBO$f4@PDf8sps4FCQC1Q64wUw^h*hy4HZXO3U)@85qwD*pci8U|DWQu_BV zi2MUWKoOv;00M{w=;|*lY_o%kegGx^{`&*A{SSok_usF-P)oof|A86+0*K|;FQAM6 zfB*FtoEPp>ib}{^cs0NDu`~Mf@Xk-mw=Yh=vY5)iz z#^1LY7=^3;|9Qj!bUfJo2ok~l3zTJKk$-b#7eD~9uuj_A9HjN+(@%&)!DjyhTk;Po z0(L#nOY;02t84550*LYZRR$J`B498AO=19sCn!k444??ZAFw_DKsGS^0fy=8OE(7q z0*H|ze?P;gAE3AdMkFXY!Hi$9XofPt2pFSWEC2xp0BF*x^8r=#1ONa407*qoM6N<$ Ef^h8ILI3~& diff --git a/toxygen/smileys/default/ax.png b/toxygen/smileys/default/ax.png old mode 100755 new mode 100644 index 1eea80a7b739bea4a249dd10a3457010525f60da..d8075a5130ff53c3b062d84656a9b5161a3d14b1 GIT binary patch delta 952 zcmbQv`jvfxBnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34fr&l9C&X1fbFX~zZu#WBvWa`&AJqLT%l~`5D0lEm2Itibwo4g| z7cywiW>B2UAl}cw-O0e&u9~@f-rh&^_B@`m>(Tc|i~dgQ|8r~FE((A7mDVb&6#mJb@B-YhHCl5z4P`w>RfZTZS}3Mx4Zw;mj1p_Q!)Q+)}+fR6VJwV zp9*U`?%#OWqxwKx4^Vxna_aW^d+v9xxzV=j>eoB1zsvJ~ohvJyeLQ2*$)x_H(Orjv zTlV|Z?{=%)9@lk%fg$zhZsk7@=l;Ig{`*Gb@A}Ktzj88u9?Ac9KI`+z*aKrf#Lf`!9TaB{JK>A<3h>L(|O-xBfsrR_^f!=cTG>-`(|de!Z6t=eH< zy2ZL^vw8jo1_pNqZfiXhrPtp*OmPlJ0F*dMVGhgC!o+VPZ!4!j_ZuO8C?DYRWmWO zu(GjpaB^|;@bcC3GYAL@35$q|iAzXINz2H}$ulS@Dk-a|s;O&eYH91}>gh8W7#bOy zn3|beSXx=z*xK1MI5;{vySTc!dw6l&Jb7#dg^n_8KeY8x0>85rF{7=Ha?`1Xh42ap7#FTWW6{R0RfmVXTY>Z8;d{xkmj_n$+t zl%eu1qc(d-g5KBf%)kHt{{83Quit-v{Q;tXKY#z7ox2$z001!n&i@4Z_yPd`0PFDj z{eS=c{`u+u=j#9V2j%Sc^797z|N8p+{rLO+`TP9&`u!FW2mk>30#G;xDFDDQ2vg|) z-@5Zw?JVTrAxNs~5&|AZ;5uWfQ`vUsMGm`>aGWoI7=P|oW>%~E|Kke=_~}$sotXvW)>C001!n&i@4Zo(Kgh2LAU5{{8_0 z`vCru^Z#B4{R{#B`U3v_0{#60|NH~~`~?2}1p^BU_zwUAh=ps`l)hBsr*FRe{(tkg z(P|Ov-|zo97$SFM{Q2_-=+_^=K;$owzyHdMa_?Er01$v7dbt4r1c4w5k(2#*>JMBM zH<=3FSnfJP+AMw(0SkXyanM^KyHf$e;>dz>*B3x63?DuLrG@xee*a-$ViRT%{mH=2 zAtAv2jg#TmKepe0|AM394=}v`{6qc8!0-bgfEXE!=QF(f#_$0Ym*?Bcg^C&ez>%07*qoM6N<$g59t?1^@s6 diff --git a/toxygen/smileys/default/az.png b/toxygen/smileys/default/az.png old mode 100755 new mode 100644 index 4ee9fe5ced2610a60ff99e6cc4fbe80d7d53c624..0eaf6b2aacceaed26490733e525d73050a958b93 GIT binary patch delta 940 zcmZvaZBP>g7{~u>76c_3m0IR&NW|MVKx7SNxCs*m>;)W-wT%t7A%ksh;D9wyY=Y%P zGDRU%Aq5K)dAUKTuq%`r7@=dZ0SzeiuCwm^ROit?^rgE#J@?%6{ORRA0->CW_U?2gHB>?pduv*jH&!GW?zq8W$Of-z3 z&JK$mhlfxz2-6^pHW>P#y#e)gD6T)gR|k3sLzXk1Fgli{JKX19CkT=V08+l;B|CoX54&33R-C-bFE zz!0!--0CqH+*<8)aq)az{Zp^ksZ>63IR2b+Ep~K_Wn}!wVvRDH6B5ZOV6py~r&R0S zubxq;Tq4Quw}$>08=Ey&JnHFn>Gb0%dmOu%KkT4S?8|Ee27q5I7MIejbjoD+#p0iZ z!inshdtB})gK;-0X@o`_69_E6XP8iEuVwVAcHWRvuS;S(g;7_t!`ia8w6WJ;Vy(Hr z3_hO})JzLJn-JZeg9M-y(wo(yZ%qQ5k>6*?>ei-psds;)O71L3{3<`8T^x5sNNE#9 zw`NCNm^ zcGB1Q=~-;1->-fv144+Kh0lCu<9il^&i8i`2EbVMZ4(I35m!9_f}j730f8^QjFmw_ zudI4C__f#HFt1*-_RY7};qCPsHg4Ly<(-hNq3?#h7aoDgsAv+I5)&J@Eq;3f^?e!= zckE1}C+}h~cc-MX_N1kQospTv;qv&|IeP`U`-FL5i#`xbr1>&=L7_saD%y`?HK(Lh zqtzV{<$-6AdRDKRxx?=`(0Ld#<_V z^YdR^xOntZYulHXyK1)gtfIGc`)&%+GX;9NR9`@q=?Z)W6cU9JNs5mo$MVS}YD^q8 tHX)ouqLN5=4z&LEA3$9yR}~un2MGL^utlvCHjqOD02VWY(U~kc^bZWOZ+ZX# delta 527 zcmV+q0`UFm2h9YK83+OZ008-bnr@LH6O+3GB!2A=l`1OZ@g$W>lSQx(isdiNaDgF;c|Ns8~4^;5?-@iW~#y=qY z_g^6S@Atp|zyJQ+io` zKz|K?|H*Q*F|hFf1Q6q4MnzQ${~M_P_y6C& zm>3uU0*DFd0+8#0#Gk)^|NLQBRAyCBe1F{2!)0X1E~mh$tM?Zy4kZ5pl`t>>1Q1XU z10y5^e*Izm{rBgUYd`PamrP50zh>=^yLZ2yIK}w$46afN={{8s{G6UqGKmY!!@c+B`kO3foSQtJr{Qmt%l1=3IUvSWXwEg+b z`Uh+s*!>{S0fYMQ-=BXO-Z20K5Fz9*Le diff --git a/toxygen/smileys/default/ba.png b/toxygen/smileys/default/ba.png old mode 100755 new mode 100644 index c77499249c9c54700885c84465bc9039a433a2c9..96619d2103a8e1b39ef3c7651f38f18d685be7e7 GIT binary patch delta 898 zcmZ`%TTGJ&7(HYc7&7K2n_(}yEL9mOKWzb1aZb_-6Q!S=_Kbn=RADpj6DBW zXj639a{vyfY;0+MYny7PG(Cp{oZbc0eg>?Pr&=#iNdR*qfbkWuz4Y7Pvr~W&BQI0H z0<@Gg>yRmMjL#4B;!zhi?qaPM!5#!OSk}PTMP_IK5^kPpn*FWMH~V2`jY|SoOwdJXT6S@2nJGamVTtCz3J)!idJLVtG1g~~>u9yXD7S7su+Nvov zXgs)LNDho7#ATY;GAr;MFrd8X&8zeX%iUjmGv}IGX>V%_7>@?WQdUNj{KK)k(hN+| zIM7*j!7EU>c*V|7TfCEQ|CN>&|H!)mJ+Z8#>{O+W9?CNV?ZEx=`We2$l~wG_kWU_w z*p=!jt7EBAtzOj9{QB7CAV}Eb-qE$l5U4@AUbOQgCv0wXa)L;&P}nzq|(X|j??*;c&@piM&&SC7HVs1eE&p0 zyC1ccrlau;z-8c1dCfzn*mfw-%E%?v=6&3;7`Ewcrr|`DO>cUlJauZZKXOaFHl8yI zGy`|Vr`^Y^T*7jP;G~^bWY3UWk4Pp`Ke8PB&|F^Uu)Ck0Z)k|i7y%lAUZ4lK26U)^ zHf6)`=H}-9abq7@T!5S9ko_0-1Mb7P8qh5RC)z`PRHnm*3(kv4-UC!kRa^BBA#y9JYqhn&d$&gIY zsY%(iL<7C&_OB9scarM%FWs8zQu-&<>Z-CzX=&i;(lh_!vhS6!i15glUY3S6)_-~a zVuSiZR(j@9E}J7wAFdzN4L%qc(#G$JduLyaY)@?0k3au(>-xuTt?YnQ#&fe3}|M=hz|9f)g9n^bW*7;V50ZIi+SdlNTkTIm? zG7>-~sMNhg^4ge&GC5rq)x$^%FQT($nG$-s12iPnw J^H!?p+`pq0pCbSO delta 531 zcmV+u0_^?32hjwO83+OZ008-bnr@LH6O)nyB!28@QQ);SZ1r5CAa%&i@1e0034=4(smxl$xo&#>}`~m>`|Ns2|{`*sHD*OEZvX{*Q2&94G|NrINK3=`^Gt9$n#jbZ@ zW`CQR8GkY`{`&t1s2YfV{r~st=KUYCl59Xr00M{!Xv@F<41fPHoWA^R$>xvtF5yd$ zxc~X{7o-}f=ig7DY9RXc``_>1K-c{N2q2&ahQEIq{`~z1RCDs;w*~7zIJ!sAKj8J} z&!7K)enC|K{|nUc|Mwq|27mwp*#K1f8-IxY{0EwG^xW6ktKYkNC(PXA|MTbH|3E(g zHT?he>(^hP2|x`10R+S!(qDgo>#0ow5E_urqt|Ls5Vb;6=|K0%qiTOz>@`~BzlzrSD`00Ic80qEL)V0@)O zbHF4R?LPLUd*0iC@Pf7t@jx4Z4hFg$XgWXuv4Cv&^IuYw6=>rh5Mlr_K+=Ey-FxzT z(Z-M2Srs#v3#!Pm|NRHj01!Zo3=BINK(YA;5|J1TAQG$WRBA83#lY|k7^DCJ1_0{I VU_i98ucQC~002ovPDHLkV1hm{6%GIZ diff --git a/toxygen/smileys/default/bb.png b/toxygen/smileys/default/bb.png old mode 100755 new mode 100644 index 0df19c71d20d7fdc06e1cba01028983439b2bdae..bf17f851831fe1e1d72ca1a56514d1f4b9597ea7 GIT binary patch delta 928 zcmX@fa-MyHBnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34f$?#GPlzi6b0Y&o{onHpfA=!{-pTOuFvItwKu!4!46zIhkqivM z3=F>2Q%_Y+K3U#>`rqr4zt0N)KFIxZKl9I{)Zfn%VygBZyRU_tx)w}q~JZ@zCL9w1A{38Lv>N#vEZ_u{)Icdvv&M`=(cmd zLsN-$eWBU9DF)weYODGzmG_!2|J_AE}e)kdo!tI{9TV2z({d(Z=^M+MT zo@r64{SgKApK0hi2;_Q@NpVmAD^WAgo)-nVm_U(YCi zxghuXilngZ9DbYW+?G@I!Eal|aXmSK zfyqrxEln)Utc=f3FOQ8)PA<-_j?qyuQ83W3kkgZmO*2z6(z24(RW(&K)U=eB%~w}4 zSGKY~*0z?}Svgra*qE)DJJ(w~TRPghn%!MJT|Au4yqw=(KVLrH{(r-P1rH_!v~Bp% zabm@b88>$PXgRXv$&`?`Enj-hta&r%&YnL_hZa4W6vXyz)2FUet6t5zwd+^gv1QN1 z+OBQ;)^~2*yLtEa{o@o?_nmjhp!C+2mzUGe%rUJ#cc-%cbM!JFMus9KQB!2vcvhw=X( zX68RkEWcx_{Qvy>{rfKnZ<;U}Ab?oFCjWo(>f7JH|Ns7C{Qv6#!~ehk|NQ>{|M!2! z-+%vq{=WU_!>_-;fvROCxPdkR1P~M0Xa=C_KR`47{sXG+pY!Yfqu=)*{%)W0>;M0s zzkdJy_507SKYxDz`3KYh5I`&-cY{^``2$3Z|NsB`^ZWO@y}$qd{r%_nFQBC$4Is{6 zhz5WF0@(mI8^j0N`~Tl>LzQ1Ye}a&q>VGdF_t&q#U`v3;{RL_O2q2(_KYxM7|Ni&u z&);8v{sC=pu>1uCpTGR{u!TDbgupiZ0SF+F2B5h>)j$et!>|9$zXW)GF#&zd3bp~H z^e@nOpt(RB00IaYN`L_f4><1@i6>jVUUfrWMKHg!0-X68mIsuzyM~jXL}tr%)I~r N002ovPDHLkV1lAb6%+sf diff --git a/toxygen/smileys/default/bd.png b/toxygen/smileys/default/bd.png old mode 100755 new mode 100644 index 076a8bf87c0cedcce47099c6b74b59f2c9d1dbce..4f0390c51d4d3c5e7bf9942ac547e3c7063bd343 GIT binary patch delta 800 zcmeyte1mO*BnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34fw4BgC&ZP(8K{|o!J2`=oPoiZfkBsnL7jm?k%2*yfkBvoftP`S z6=()S^@i%ob(Q67%S%?56s#=FU7nk{Br|nkYT~?vnAtHAGs1(X1^Z3*tFPWr6+b^d zW^PR6?8qtgA@^effBE?Q^l-oJ@6?iGYu;jR*l4IzqvP4{88JKZS76{DZ|~pk?!O$J ze%jc4wJ@tmHq@@sR;yBT>2YDOWhh%)7BMR#WO~S%V3U`IXL{Zw*IEA{Z>O&zC>QCK+3w+n!%JIe?`9M zM9&kEu0O4L7}p!6ElzXkbFuHTw`#MhOEtM_qxVHi^RtTbd0jc-9AW+}KJN5-Zv9&Q z#Q90Fb7LcBMubcY4VWC@J;B?p*UhQR$+pebvc=N0$<(OcNWVs3r&{O#|Nq{Xa~1=` zmodrP-KEZchhs62!(QU)>&pI&osUbEV{501JWyz}r;B3<$Mxg{1|~PPG_f$VGCn`O zJUKSDxH!8yMn}a&!9c@8PS5%U4O6!CteLZ?Y0;!jU8`nU@iQyW?`fN}Y}&TIb@TQ$ zE)?W%-q^Wv=FVxYOQ&w_T|0L#Ge7g>#gjL8x2~SOwRd;>^6BRM&Hd{)&)=WzkPuMd zkr7eB!tb0iCBDQbM`nWje1Um0jvPKPZO)`w)8V@*RQ|7e*gY0D=Z6C3J^dnKxY6= zhJP4r^W=BhrN2zSe*ORRhvC<6xi7y8U;g@`{d>LSuiyWGR{aKQ00-R5+27mwp+3@G@Um)ktzdstU{yZx7K!6s4ME?Cz`UeyS10%2o kfB<4-kVJ_{f&d`E0KleSy24>=IRF3v07*qoM6N<$g4w6m$^ZZW diff --git a/toxygen/smileys/default/be.png b/toxygen/smileys/default/be.png old mode 100755 new mode 100644 index d86ebc800a673e6cf357c33d00edef93e2df0787..4c2c9da219aa2f6a76f6b97f8189e383cbd4fdd0 GIT binary patch delta 824 zcmZvYTS!xJ9LIk)I%=jBYPBLYbd#Fq*>aPP+MMo~$GJ`0(YBg8FB@&;wK6cL2?)k>^dZSZy?Fh(vTKjPnWwmzT%u^@D?ZY&M(KYBklH zI8FkpM6n!&5H8HAxZGTJ)he#M{0l%|UmsOZktE5o5GcdFF{M0Tz|H0|OP8`5%?kjJ$73`a^?E%)5aDnsLK_5O9na@;Tn@{~ znas@al8kU#8dDM~c@EIg(b?MCO4H2@(}<7>p;|$x=J`r)b`z`BG8zq2R5Stb;Um2L z3~wgk^#}Mo1(R>!CBRL9`T6a=hv0;+}=%n%3;f{jwSi%kMI0+BWW4lV++^?OJ_x?9Sz<jF z6w1cWKYe)e-jz_5>(q|^x}7U!HG2%wt6_J4opav;Sxx8G!BD+#`$(;)C1B}o z+bpXwTpMT_puCPC>27WtN`HTC@q-~g{kS+`_}xU; zpj#TC!8Mv{Vqn6NWR5(uHIQHq+_&-8VU$@s2{41>Zk1;%6r$4POTm(Ro K7USJ&*TLW8lu+0J delta 386 zcmV-|0e$}V2EhZ683+OZ008-bnr@LH6O%{-B!2-@Nkld?-Rp6afW{k2m}y7EI_&H>T0kG5a8hY%fR=Sf#DD1|3AzOf0%fG|Bw0e|M&0z ze?a7yO`8A$hy`r!|0hqL{sn`7|9&$t{Qdv`Hx&K-{r~o_|G$0#RWr)U0&M^YAQrIM z|9^o>{{Rutr2jzGzyAOK1J>{hgns>oXkhsL8>j&wfIu2Rsv+R_AB5T1GyoL?1Q1BW zpFe+p|Nf1n;TJ-~Kd@?uhChFRHUI<=#0HS!U%!8$YJib28yLY(0tg_G2B7i3e*FSj z@aq@44gY_`jRywQ69xu=0D?FP=vyd&x@#M*niZ_!4^YEjkV_aC00IbN!yidWNw7Em zGBN^v_8UwBWq}_3!=UsBs1$;LP67xZMzol|eV5@E3j@%1|NbxtaWMRQ$M8ZQBo5Tg g#=vlgfdL@E0IynQ@B52}^Z)<=07*qoM6N<$g7r_cZ2$lO diff --git a/toxygen/smileys/default/bf.png b/toxygen/smileys/default/bf.png old mode 100755 new mode 100644 index ab5ce8fe1237a18d6809a5570024eb108cb14a3e..b7de4593087c4114b16ce15958a1f994988afe5e GIT binary patch delta 871 zcmZXST})F47=~Y983s1TY#BX{SsWBYTj*)&&pa(FbUnpm3$(OI86_Slg@9xDQ!&8I zF>Y6zCZIC^FlK;kI3^pbGdSm#ne0N{&3+;q>w+~Z;*6q}0{vTevt-%To9}zS=i>eH zE~K1E?ITvD04UQ%)BD%+xE%MU>I$GIAK3REu#&L$%>lbfV4el2V?fsS!HYH>ko2w9 z;-KR|6bJ*$KnMr|Pk;sBA@Bf*_x8rRyJLIy#Jt|<_8rlVj!0Wu#N!FG?25~^TwA+j zw}&b#@Beez6Wh9#i@98}y1J;t5w+PO7E8Fie8pf`)|D)2G$FZsQ7R2F%sn7tHZSXR zOB(f}L=w!;Uo;x;0+8G~1Ptv6mdTN|fomI+4W#VCR z{&1e~w2&Ai_{aGfM>C2?6_5Zs(DGf2@e8AVLa!WC7L9U61tSHyALXt)y-qMl@Q(9d zIJ!E0AYC>hg9zA$#>-6&ml~W?PRE5BtIuklG@Cv*8O|9>#!EJiZB%`tQk+pphoo2! zv;vJlJx~MK05d=XdO!qoSGiKt0U=5ixZsmgt8ufl&o2CWJ5#9~IPcr-B?Q5zp67mf{@{TM%lYfq3%!K&v+<6%9{lx6 z_Nl=J&gK83M4Yslq7AU@)+!fgQ zr>g_Y(?2j za#x}~5P4~*x4F^lq1-z?34wxCC<;kUp-HZE$VsYLMJZpCkR(NtZ$C3({}0j8+1%FB Z{V(x9Kv<2eJn|8JzSMj`+J delta 435 zcmV;k0Zjg-2k`@t83+OZ008-bnr@LH6O(rXB!2;eNkl8RSooaXC46r*1+)p|C6Ucr6AS6 ze?v$p`UNC^{r~j~$Yzw41=;`*KrCRh|D&h|N<&Nqsrv=e1J=Os`!`SnKmf4-F(Xhl zP=CdrKMcQt819e>gHP`g_ z|Np;&&;0(C^$RHS>oePi4@?5Z{{qbdYWNF848Irv0*D2O zfB*X<$t?*s=I z*YIz{I0D;!n`z8s95EV(4Eh1R?v_q-L!-J@p}3+TE)$|2k>HY`=wgw&PmM|(xc6b* zXkG1at$t8nb+bx!U8U$#5Dr4vD-wJ!;D5_6xKN#Ps(<6h_@dX z)*mU}V&~U<%A?!4lZrlfIe!eC(ZdPb*E2HJ*5xG1pm4?@>aRM@Wgr$9ss}jfiEqnL)XJ&n`uPESkcb%6wy0@FQ`L>P!@zZtoa-MhQGxJ2*+(Q~=?y(UYucWTR|5JR>*0jfR=>HYmy3dw&N>*pH N6`(e2`>Pwi_!qKxaOD61 delta 399 zcmV;A0dW402hIbK83+OZ008-bnr@LH6O&;BB!2;5NklUV68QJ;AB03^ zz`>t?{{RAr1t`kJ#RVk)|Njrs1E&A~hf_%4#fujJ0mK4R^dFh=9~)p~WCU6Q5I`(< zKHZUFlKK1pFG7al|34rEgMTmxM666Kx4zv12p~p=W`;7CvPYjE{rUF?h<^Y34MKl^ z|9|=mB!B(-`wPhZ^#?@C^U5EbbPyl_g=3Hc01Sh${{P9=HB%4|34*<3-m=C?^;L%m ziR{xQv2*hU;8+Y&0Dxf-W~KjsusWFAC4@j0#9_j;X5z6SjRhH>d}sd(7FPhVFdSi! z*Zj@;_Sc`kfByUdk|3A-`STmZ_yb~qxOhP0|NM~=`E%+Z13&<={M^pKEc@r*J)m(Q zQ$Vf&I|NEX7=L~<{Q1kM^0$tG0U&@_fYHPB`wxSpBv1}0%J2uI6XdQxe;6Pn5dHlN tK|sX-0mKN5=YL3u0hNm;1q1*A1^_(JWriwij5+`S002ovPDHLkV1m^px4!@Y diff --git a/toxygen/smileys/default/bh.png b/toxygen/smileys/default/bh.png old mode 100755 new mode 100644 index ea8ce68761bbd8ee06f80c487c24d4493abfb52d..f3a88fdb791139051828b8ce0ab471b6381cea07 GIT binary patch delta 773 zcmZ{hT}V>_7=}L`Xej8+zh>nWo#Ox5auc@svoU95YMTxxG)mJ=W;iu0D58lDy@}8= zQ(Gz$fnr`nbYTU8lZ?c?)2L8qQ>XK1XFq4>Y-e_EyXd|7_}=$>E?&N`G)ve3;~WhD zL_#uhFg4bg(sZ?^3gAHoK(`5iprY=5fV&95K?4AP1R$})Jg*c2gxpkAs>)FmJ)52; zX*3eR4}b%B09*i0fD?ctfU~o+AQ=em?*;q%$c|33tu3I}2eev3qruf`Uu`W`U3~}; zq_nnX47Tb?wVG5a1C^EjiV8w3_7xRjY_>Ny7n8~C)E!BZhlixW5Rl6Wkr)>Wy*W9a zj0~?tvIlT_dP=UXk=HeTsgw|jaDmXr<9S#tcS?$vlpGGfPD_-P;Q|4MAZ{kp zm6YTb2y9dVP6MqnweEXhFfj!2yn8_{N5>z8;gy zF{#um7JG_|U0g28;h?$3+}{9pyWQn-Ii1dv{{C%%nam$c)Eh!>$txt0%yr8GwS;%c zs>;K6!=oZt@Udn#kFq|?BqG(h^`-_0m1gL4x`aC3y{PK^urPzckbI-$QHUtDF-FPm zoa{E4UJt#0JZt{cGxhXc1g+O(GR=(5_nF@ePZ>WvnM>|1Q7kMd7MC|Hs-G*rLem@c zdTm?N?UvT|-L3sUE0gVdt9-?AO;eA{ZVoiOPa;vCxytL<`9!YaKnp23KJ zOOL&nI599XbZK?K%4Cd=4UZ3wK7aM>rTN8og`!@iT3KSvP3>vUk55cQ!!Ze zxJXrDb(Pn5H8*x?`Av7U)Bp~`;p8DjdF({x2$u z;qPAv_a7MjfiWNmAb=Qw8hCkmAu9g=`^)h9HN(95|6jdk`1|MopFa$L{`~v?{r~UZ zKsJ#4vS|}Q0I@)o{{Ii6E?;4oHS_=5cMQLNffRw!zpr2agSm{dvOpUE0*DEr8VGcC z|9>|&F^G#pRQ&(-8^nMkpk@XJfB<6o_wOIv5C1P;X6Wwu|KUB3;TaRet^Wm==GH=00G1TRQlt`4~RYg orcL`-S;>GJweXmR0Du4k0C($DZM}~l%m4rY07*qoM6N<$g4@Zw!~g&Q diff --git a/toxygen/smileys/default/bi.png b/toxygen/smileys/default/bi.png old mode 100755 new mode 100644 index 5cc2e30cfc47452d5bef949628e955a522d59e50..18d51e20f251f7ec67a690dd91a333f33a331d4c GIT binary patch delta 900 zcmZ{hYfPI36oyYlQ(!WfsEzTDOT^(2{mMnOSu|+LC~U|^*=z_<_Vr`5w2qDffuwd9 zrocej4X7~LvMh=+F5|K-YzbHxqXgJj%09a?3akTKN};s$4p{3?e|Y|!ocH8S-Z#&! z*M`Z`cLD@uL709{`-I1n~M_=ZpX)2!M$VK>HbhQuND%V}}4p zn)D;gBp*Te@C6MyfB?`^D4OtaA~ci;3YsC2aDXX*34jp*%iXc{fq|&D{XU=1p;WF` zR+^ie%}q_l>T2VqOUAl7LrsmI&zG$C^}F3}m&*lWpwyFUpVnv`bAS8C?p+p%mV`pj ztj_&>UeDvrXJxrf)=A5B_E0utz=y5-T4r1Lj|*GpuJ7*d?(FOsP4oKYS&zr_BmTqZ zU7ra@g@8iDwa|;|%3@`4{v&LC$G*M2y+TaAoE|f~Y>az{QzZQFERM++i+Y05?ENNOOjAs?=3puh;2xIvftW-M+OY z*(y}hb z0O|oQ14zUG7XcJ)opOczLHolm{rpL%QV-L^8hb-N@*iXo<7g6jg}gU7B4qTXO6r$J z81zJDpax~LIj8dTFx0TPq+faQVi{eegoVBN#@qYe3YSY8t~ALo>D80z%;P7HWoBqp z4b$qs@>Il30umP&^C`8li5h$V4o7lF-FL6&ZeLkZL+RO4VM+1n%}u+_X?4UlIzt0r z58fMu1@5V@s;&8{y6*Bf$wyMs7+7*DuAF?ugg+Udn)oELpA&iTZR(8-_ovqfv$9Di=pk+*k!%895wq3MTnBV z6>;)J7!5s(`4_|>F)`6dLNqmwNkwR}@wE8(CX+7 zEPo3#Iq$wbBBJMTk#;k4<3e#ePFE z;{?TciOx=-NpIdg`t|#dcz^$YFqm+of7AU<7UGr+0t^5F#PsJM(2)${Jd)SmT+KY5 z@!|h>po)L5-!r~>3uJux|0`&Jz~QGy6n_L1|1;FIh5?0X>RX6HFQ!w?2OV%+JmL`!6UofFS@705Jg0{{rX;{Qm#_{Qvy$ z_3!uo_ahB2;H;eP-`4Nu-`%2-_%v{r>#`3;+U%2}l8TKYagCntxY% zvCR^`|NrIU(gox-_$A~OGmE+Y|KH=ZUtLJ;#+&QE|NH{l01yB%0M7pe0PXYaj-$4I; zJAUH*vL(#IBEWd~{qf_!-@lo-xKIB7`SbJ9L!tO2ZVxwL*zjfZ0R#}^!TSegK8T$& z)cnN2z{bGv2OPCP@)wu^VMCrNb;`P#}dL zU8cqjj2qe1W%@_6UsE^izy?;(L}S>N7{%xkjSs0z0m^ zw=f1w0vCWcf!C1DBArGu2~h;kBOXUIip@>@y$QU5v3p~IyMd9#kifTb9 z6GeJ`9f8GwW8Ptl*qJ%Te#dT`>q;hT6A7&#sCd3a5cF$n7+xHt5Aq1_8CyF^$QM{y(GuAwm zXawQ=+DFgOhLC}trkkgX35`Z9FBeKn`GW_!qN2D`87nB@wAvNPJchon`s#1#^q=Wp z{IounpC2d5-z0qm-Y>nnTRQ3!>gdg*wKpi?&>{Z70d8M#UreEh=H*7^@>qE}eCS

1k-mf-PE(V9BIavamYN(tecX_Gk${ zTx-jJ{;}G-{BUJuPkuqc)6YD$Tk+ndD^B*xyOZx+wzf0ou8vO2gYVqz($Y`g|M-K| ztRwf8%pKGYB^Nx9MVIl0_9{G@NwF=TL#eRd$@9y<`DA#~TDj*4xJWNy_R#!4= zf~wF^mDMVOpa^1d@VW%*-+=1B#A%=VlsEAIiPTuF);iA z(SNHL{{6#-7#IKoh=t(`L$zx)Nb&!FK*Y?*4A%OYk?{{R^B*Rr-~a#r`TrkEe)+Nq zAb?mHSb)<1pL~7#_y6C&|NgQvvi@WE`~Uw(up$Wh1xCLa8D)W%00a;V1IVubK&5~F z|9}1S?>7S@!~g$((Ud|+28Q224FCZIGXF0FQ1zcbfB*dX_lJRr;s3wCNa|2D02KoS zAbE^!0Dxf-mZ69ApkCb5fP^9ydGO$cm6w`kGj(t#`M{tFlLo%j*4%mm2&CaJSn02S zzkrtfWBLcO;r%bLy5Gnoqrh)qC;;&xt|r&LVX^X&cH*iF_3KEeRq3_M?M+rzf@ESauh;&BcI z1DXjl$w&)|>o9zoXo%=>o-I@u<6(on0Kq0~Y+n``U*-{qOho;rF}$-1CD8 z&D6x_03bK#ABt^#s^v|5>kb*fk+%S9>Hz+VOEocoN)%v03BdmXApOAkTibI0R!x@_ zDx`zD$`kq)0D~n>Seq^c*kyP_Hn3aQ1JDa_9iSV)4sa2m6M(hG5bbx&dV`afZ%lmC zHQ9SNM*2G19n*RusL@O-m6oYJdnQUt19G{)sOT#Il1h!9X?aw4EMhi=4fM9?ucFc|4NAAhWW(7R&UNc*?hw`e+JTf57n4~JFr(G_B$9EFh@{h9nVHTF8wS^| zy-%e&Q&WdlC;v#HI1yx8rJmZmce1Q3pxEgzDe)E*jOFEd#Ntt*klei4#o-JynL`LN zl$CW1pd-Ft8$c_74WJgF5nyFyCEqwx9RGq<>Vka9*KOB2C~=Tr#$?ij`Gn-8jHKQ5 zQdXS$36tb168*SRS+3TqGbjx|UAlY`q3hrO zfT+;#H$1+e3N8G)@cS>nJ!(S_dcv9VPMIzQeB-^szFkMJ&n*$e{O}7v#GY~1pO_a= zEKtze*h7KhJaZ9-8#inf3MH}5}z{l;>=w(9=_ V?&Wu_HpbgY0GPB$az(5>{x4gkhEV_j delta 550 zcmV+>0@?k^2jc{g83+OZ008-bnr@LH6O)?*B!2<*NklxI?1O3{QLg<`~Lj={`>p?{QUp>`^K`e0*GbKzjX}PwK$LOWB%~*|M%}K zf`0ec>{rvp=0007r<-ya}8lN9Z{CV*A z#jk(A|M&xadF9vJU%!Fw1{yi-`E=I~Yu;= z{{98V13&9}TPT8IimY@%0-bY; z#JIpRUu2pXpD@KJqGJm)!&K-dxLRu0vG%sTy?wFL^*&l!+v5Gtzx@6=Ip4`IIp4Q_ z*QL~9{-!$tpsXa3KC*+WeYm-DzZ9TP0C4;yK#V;d_X2by0Dd`u_#=Sroo6r9mjI+J z)jivQG8SNrz?gtC0%d^EpAftXK|KWKU_}SM%Ru}J_zbMCugA&HQ_fsIwPKrh^H+=w1izvH2#8?avpU20?A}D{*O0ytCzAj2L7rG<5jc`rLj`dRvfx1 zA|3VEBn%A>CK8EwJZ@UXZjO#H4k>M~B(37x7SZZ;A!(CZzh|EZjYgwTD1OuHZ(vxI zq8#t^SWk4hY-?mB5)OwM(tO)m7qZouzlN@^qcW{bR$E(JT~)nw@~C<6nBHIrxSZI~ zG3%S148zbgO;HrhFa$wRW}rPi*Jck&sw60imX=CP+Ky6@s7i5@ zhl~&nbVs}g)&kOyz4H2Og?^&KI`E2B)3`rpPgYhIpU>y;!N?6Ulir$<_509${hG~fL{d1G+U>SquNibgo zt$7+uQ!qIQ=ap+sY<(%p>WcD=@J|YcT~gH&DVpj|%}mSA(BapHS!-5OUe>V5+$@*3 zD3!`qg+kTrvAdjZg`FVq?f3BR-L_>b_ru{cr#Gg}v$~?+_2^?c=e`*qD{O=4=;+}a z;tPGTjc@P!og?_F)xAx7_=D&E-t)62oO!B-BUpTV;?>oMK5eu;d1)Y-@&>2u@{Z3G zjx8hghWsCruMdPccMnPQIb-cU{>RT-o|x{9ayGn$>axF75nJCsU=mGr3ph;1d(wrx z?~Vi>{NyF;-WdU>Kfyh}vu3oU{mN3i@#R0xiXMO{w;=10``^8TjnkB^Mh~f56l#^Y zxl6?kC_oAd@{ppu{M`-th`3NB7VX}JAYuejW$LC`;y>WXYc1`qz5fsJWc?dP@^_{I NNYI+{iKpc!{sjYRzls0= delta 578 zcmV-I0=@md2mb_+83+OZ008-bnr@LH6O)hwB!2=CNklEvh<)_Q6L#QMiDDO;6kc_==+V*Ywz*} zS1YsfP+B-9)SAyPfLI^~Gx0<-fUWuW_tn3T6Muer_3P6oPQhRQe*b1*1gif1|KBf0 zR(~0wJpcj3%*DZBWo-@72DFWlk(-gj@!ngb-$#D&C_i9e{QvLYw|{?r|NHlgiHY~? z^IHG`05Jg0{{(k=dF}7->+9=cXlNG}79t%T{rC3_t(pA#`tSX#4jKOh|NsB|;rr`( z{{a5__y7Wk5omBxQSrlv5B>nb??0VZ8h^~pET6ohRD=aTEuH%B*H4L}*8gC@$i(#V z!v}x>V*Grafl;91&h7h^9iM<2e*b2fYOkEV?IhQ)-<|II?}IW~xw&-|6o49-n3&$b ze-97kI>;M$dBLMjHb+g#b_UZci`uqF)Gk-BL zV`XJEH8lc=@#94XcF6*u28Mq>7?^+mXZZEwVk=?z1!R8%#WWKa&t|6#ayi2)$M01|L91<%3V QX#fBK07*qoM6N<$f;b*6`Tzg` diff --git a/toxygen/smileys/default/bo.png b/toxygen/smileys/default/bo.png old mode 100755 new mode 100644 index ce7ba522aa7e948d581478432643c230eed1a658..06eb7a736fb6bf5e9b14222f5c817a32d26dd830 GIT binary patch delta 895 zcmZ{ieNfW{7{|ZFwW(YdN5)Y{0>;P|x&a%q3EYIuP1$g~h}?1vH=G=-p$=4J{U}Jd zUT*G?k`ttfhAHvVGKutxiqaASK{pT-PsvR*7%;|wZOwoBufLwV=lOi@x#ynmobOfg z@t_sH063dne)`Y=B1Fzl69T0apu7@T^p46cKrsz?ECX04fxx2rF7al-XJ|)C2G_I3 z*X0kFA6zTp^1%||1RQ`Jm<4RW6z~-AG}O9Jop9Ib-Jk1RCq8xQKU&sREq_wER81^a zmO0-)yr3<1lok<_UbMc}T}`+@EOS?sx`+~2*@5N5>ZRhs#r;aBO1_XUbttmuWf_i= zLaTR;3QOHOr}2=pAIQ1Fdc6QI5wXpt$ z`NvhW6NNJm_su-Wv)#{~9)D-*Zr0PW^e3aDNn%7`9Zt5G5+B`)vyNo`*#v7Z?3PN~ z1I0hjptL76VO&Eo3@Ji3lK3t_vTGd(Vu9OhfiNfU!H4+m%#v#6&5 zH9hE`DDAno`E675j=4%a2e`x zb?R{HHq$oVEgol(!yaHW`sma8x#4tZD1CigI3qHCT{M*)5xcR`@J0Li&a>aPTsia2g|08ZYdvi+bbR&wxl3PP zZ8Bc`p}F<4&OrS9tDvSirGcsa?MAlUuGONVBPP%|6L!pdO}JFU-KA0FYVug}19{$p zC|XohBrP_Q9+N?*v7#BQm{=-}#-h1`YTC#dc-O~z!fF8 IZjpWPFNoHJ#Q*>R delta 438 zcmV;n0ZIP72lNAw83+OZ008-bnr@LH6O)evB!2;hNkld@>fdBvi07*qoM6N<$f?=r88~^|S diff --git a/toxygen/smileys/default/br.png b/toxygen/smileys/default/br.png old mode 100755 new mode 100644 index 9b1a5538b264a295021f4f717d4299bb8ed98d98..f9da237f954c49aa0627eee8480e727b9baed8fe GIT binary patch delta 868 zcmY+9ZA?=K7=;h8-Ks=o17_F<+8SP9qW+MT7MVi`lnn2^RHDIXJ~3>_h6F$0pZr+Z?f_g?3@FE}q1ON|6L(61~eX@o9mTkJy)G*Ot8@G+knlI0qs=ux_ju`br zdggqE_A4!0>MTVv?34DYQKN2H$GDjC!SYau4hG)|1a|xVGU^K||MWIVmqg$Y(ET*H zsBl#b+|`8Kw9WRX!Wxj(`VFSprT>JzxA-}Z9P%Vj)vH1*8m}}mqkH@zO(?kQ#`L+H zGl2(l9@q81h5MJxb%S1S#^Pd1I(sZ#(W5{#KAo-$g-U|KqF}JFylrk6AbX^O#xH zr&9DPWS`4&ojE>VmiMW0@n5;elep=~ri8-@@oc>4l<2(?lYYp+3^LjQZJD#IxW9Pl z_kz(G8FiYHc1!cR^282_utO*~AwVXu5m*awfGDi2tUPM+iNg{R?-nZLj+rR-!KHBG zSd?mt^N_P{P0H$P$KyT;zb2J(sbA80` z*m^Cdy?^%9tqV_&P7ltX`K@)rA47QCub+F^^~)vi@gIku^xlsl&bs~e&cKZ{RZm=G zB<{SKC3k$#_HtMy#X{||=qoJ-vF;;7_#m6i&dwx7nfx5hc0MWIED#H_Ge}ZQlEnRm hw&w_Qjowss@c+c~fURvueg{8o4WOiU%7oZ_CUitt4 delta 531 zcmV+u0_^>w2hjwO83+OZ008-bnr@LH6O(iUB!2h4dMmPRvVEF(49}xWg|M%~|zcyOmSQ!3nInVUx-yb0N&!6AF|NZ&> z_xGyWRR945((s?*|Fcie|3c*c{aG0N_5Xj)|NnnGOT6`7%>3)m?>~Qk|N8q&hF=C~ z1AjmOfi?X94^$0Q>Z$uRRO)Y4)uapmB!2(-ul(c1=C+V!kAF`)$PCo;`_FHns{jIs z3Fu;wy-Z(c2YwAz{&V4aXk?bejFO*Op&u`@iH=1Vn?&l3xcd|7PH400>t7N8A({(OUi z42UN0XYS(sx^IHR|KHO8fB*i^eoJpM%kO_IKp+443knrr2mu5TNW*e0%P3|R#`UBSY6(JGpq9V(U?r)6QrZL}fnfnlU8>fG z5VB!c=f)I#aI8Ty9kPbNMht_Zwmq-hQSPAB)}lg|EMY>Sg%Q?0?qN?~^8I`HzOVM& zV_l7p-VFdbg6(~#kK*)4lm(}Y0NRrPE?xoHj7S%k0a_4%KdJ%v-vJzIx;a`V0N7h2KtwnG@?cC+LV65>_9-zeb#R*$s`2jp9?{@A0l}hMCv+zEa*sF zeH{OK>cASFzSsrz_4Q)0SX5Cl+Ls-s_@U{vwI>O$C-<*9QJ(?SYPGPcD*K`SA%DS_ zHa+(#L4ShJe(ad}@Co(aqv^OQ;;l)1-|x;;2L&BHiYvx5Bq{UxFor3mX^GpNM^U*X zndNe25(E#&QKvK2Zcnz`;nF2Id>E`&xP2RL-GZJTFq^?-g6?kU>Vl3AFc_e-b7yB~ zVf>f{Ko7fWB>BSYraDe$WG|LRTqI(>V!au|j<$9tCPb)lu`maVrj^yzwKZzBLZwnJ z`CkNHJy-b`5A8qj&bx0PjQjTLwXd(6RDQGhvr_DIMOnF&cFmBp3P0thGLSShoy$8u z{PQml)c&DC>rh*h25o5_Y-%~jiaD=8->%&XZD#%&V(p3A+kvbY+l|J2VV>yGWYm=H zXd=48W?O#vv43|s{`0?jD(7M^WR7UVdyOI+yI=M4;5X&57u-B+OYn^^i|~}68vgvC zH}=eeSCBRK;EV0Jq~h-dS~~_D3vxMG+5hh$*&2~dQ>)OZ_{wHg1WEu!P&5U}Na3bo vT!ep|$LFCO1mPn{)Uy@Ce-aIiwRLLk{|DL(1=%L<(HH=+s91O}xBBuwUHX)I delta 464 zcmV;>0Wbcp2aW`g83+OZ008-bnr@LH6O(`gB!2;*Nkla_$N-WI z48MWozkk2~{|BN?$BqF65DUn#yLX=4z56#LzuV{ie)#0qf2Lnh z)j(~3VeG$ufPMl9Aczeh3x56n#l!R`HGkx{tmLo%zo4cf+raqmH&6pW0D)`(n*Hn7 zuRlDzsX@Q&O@ICW^9yV$k{+OYz`^$C2LnI=v4EZQ=eL;H?}UT~S%qI;fBgamA}C~F zzxhtx^{0000s7+j~oGsjT<*(#L(U`*%O~KPobUUR zlP~uCElHnr?P>rxP{gP9zmZ|vVmMq;2GF(*pz{lWq!AU+dt3y*)o<7O^! zK92XYE?p9H^O1op^Y3uwxR%e3SR0$G0#&Zwq(}Z*yw6xufjkgAVMO10`oY1V%0{5cmQ? zL@lq=CpSjB_mKd+w~3oQ%Fe0Sd1ZjGKXLj)*x!MfDF-?moF4a|QP4&y)sz|SD)a#` zBfz;&ur3wlzdD0YIHo4CnHm3sk!AA^=DX8wxBJP&L#is1YD}YTHkliM9tI9KC5dJa z`lsja-FQR(yvK|7Rx#$SQOjo9Af=2Mk;ZkAbUQIA0m>DPJ7D z80l9pcNa6K_eZ<-(wzmAO-6O>qS|vX(fnJWB@h8vS`XouAe;(M0lWY!D=XdYCR?Q# z_$qzLA;qBo<4wPagT$gLQ%d}jbt&1B?83e0#MOP3;(&H7YS8J7dcEFMS8veQFL(&t zq$9ju(zhWkGyRp9*Jlj&8T&3=>c2QpRj#c#rmobWQ^r1g4fWfvrz}kL#TU+gRhCuy z=pmV#^V5&!kHU+uz4}e5E$i~ohc&sFYk$$$@74~UTb)(?+~-~I6p!EE`sVd(2E7lL zrl${F&{RC0-Ypxx)zVB;(v+0mo|1R}da!fHjUO6MgeYln)5fb`fAGF@ZlByf+TO-y zCAXIj<+t__+JVk^L&9pc!bk%W9=?)xU3@3oqO7(U>n!zhL$jt{>>xvAvRq_OZeG4N x50URKkQe0bL=ZWG>?sUx{*S`kWNa|C{=eX0W5u=pyzMCfD&-Ny&&9f~e*td9#^(S4 delta 570 zcmV-A0>%BZ2loV!83+OZ008-bnr@LH6O)7kB!2=4Nklkb}761SBl;Qthkm~=x85sWl=hXNol=0)o z|9@XUzB0Wn`TXRU=!%=n%Ci4h82|!^32gR%glZtk^6USnb00o@{`LDW>%U)&BK_|- zy-75RkKVi&O=|xC z<=6keK+72b0*D2q;Xlai|Ns9mF#h@fzl^Fz{EQ@?my>m zhTngHY5@X><=-Em5B~fI+Q9gWf%(r{!Lonfe~7bv`1|AcyYHO5zhC{?*7*O!um9Y? z8UOwUY5)izrhlNY`Td{y*LR+vyDXjW-iUDi{q+0o6TYcY@@Ln5<&}Bo*75)IZ-0hA zfBygd^M`=}Ab^;F!3;F`KZoYKk9r*UJ}J#iW#r)ecyQi-hM)g0Oc!aflAGrKU*y;S zKfnJ2y~@A<5I`(UJPiMt{`^1wR`PB?1N-0qiZXn+wy?7OVw8|)VEWC#2aMD|pfLOk z3Pzxl00M~d^Lz$|42FL{8UBfaA|n+T$N#>9Lk5N0EHnV>JO#92LJ#707*qo IM6N<$g3w4oBLDyZ diff --git a/toxygen/smileys/default/bv.png b/toxygen/smileys/default/bv.png old mode 100755 new mode 100644 index 160b6b5b79db15e623fa55e5774e5d160b933180..00997d1830528b7ebbd4dc3b3698e0e09a9bf0ae GIT binary patch delta 800 zcmZ{iNlX&~6owy)q7aE%gb;BdQe1#KEro`P8&1KdND34Y6`d|KY#|jW5^J<5iiwE{ zDhQ&)gWz&8b;IBi5AJ#}XxO1lr_&BlO0g`3mc_Yx@Lm4A|9{`RyjSVlzq}@N#Zmy+ zpEw_IJ)C3MtUj1=2tXeRP)hFeg@w8{`*>-IQ(NpTDj3Vd9cpa!#Bux4 z?BT4;A$i&-0E6qCeq4F-{3B*GBi){sYL})Aqe(-1cQayCZ- zCMPEuhFO~V^G554zQtO0&!TNI7d4WEq28pb9muWe%emI0xY{kR?vhn?;I+K~E#5h= z_O5wiV#4KeSu7T3aiLu%Exb+IL_(UslZxI!ZH*wqHw}h`4uk~tuU$>X30?w>x zD1OUCBRnc61{IX zwJU)V#gRgRi5Km3I$^^rhr{s>;0-_T5Ju%?*R?rt((Z z&1|_M<8XSG8k2XCzcrn|I@>!kJ4J|4h1o3X_;`L?>?`AoXRS|LapUs}ZjU_l7u4-yFwKrBE}!P;sd`Tzg_o&+gIMn>VH!vBB&F#P%RAB2AY{|zGl z0mN&`Tk_-7w>=n{RejQzkfh&Kn{rf10?_b{tFTZibx5v&dxav z5I~H7|Ney-|DWN1$%1FyagzUW0464;_kZu-0|XGuJ!WQ++^j$M?h9<#^c(D?w|3V5 zet~?<0P^l{s5gH9VNq7Pdioqd0I>kQ`5zo)K&2pO{r~^(Cq%_BkRV7Wi182TkUzk{ zW&j8vu!cW>m?S|i5#<8~8ncWn(_c`)Gk}Ai`OhDa>c4;gfgKMJK#W&mQK<0#y(^&J siMJQbt_chOfyFT-f*D{W5C8-i0Q8<(lBH~=r2qf`07*qoM6N<$f+=Lyv;Y7A diff --git a/toxygen/smileys/default/bw.png b/toxygen/smileys/default/bw.png old mode 100755 new mode 100644 index fcb103941523e24b03726fbbd88ef213dd476577..51d5ec45c3f58c3451dbffe5c7e9f2d56a3e54fb GIT binary patch delta 833 zcmZ{fZA?-N7{?#iHM_P|CTiN2v_W^tnggrz2md3Or}wbxOAv|Nh{E3H*p zzTB-^Ys=dGaBp?J3r=&pPoI>|Nq;b-{VEp z;*RVkivU1eOf2nOn{HuhWjnS3w5$U-*$NP&qmv&1nh}6cY5>8n09l8x|5H^AkaVrG zTq$M0pfZN2v_WF&6C&k*BI!Q@9>?Kt93Eou0EK%Y_&or>eMuXndVN%nkLvP~SG{D% zEO}{`JU>IUO($BX5~n=z6O*x)3Ea}`jv8DT-29wANOgND%QSg>ifoxA&2I_g1fh2) z4!Gh?&Y0GL?;4Bl9K~v0MGiVKxb`s+2#_R65JV!8h{xlxSPaMUXf%ppSR@ibQPk;l z(lTbVxvs8GsZ>^0R4NpT($Z3~SS%D47Zw&G2*R;&IBYhX$z+N|B7?zLU0q#KQK8tX zC@Cos3Wa$bPHt{)PEL+qua|F?OQlkwP{8N&^Yiob^72?LR(5tagTe6o{o!yp6bc1{ zL7UA61Nh!?`s3JcJ6``fS~G%G*&^jHQQ3>I^pybQuQ%QW<3r#*4= z8{FuQ>c_GD4y?%@(Y{92BjI0Oh3ah~72UIXU;u?aKiu-ejnB|M51l@^{2ngOK>IYb zd*F-*E_f)4TC>Tzp0-ZfqbQTK9cav~rM}yz{-!TESSEnU;~>+tGEUN7K=LRaL3nQCn40?HYBAjcZ1o zcDvnGglyy$a`^>C{p$OF{rTv@!~Ua(O$PIkX5)bFet01qe1rv>4G|-{d?7jYhnD2z zcx&s~GiA}T(#<=0lGUrWpwFKT4-Hv{3s`OJb?m#h@ANh48XM-lb6jthOqQ9M(c9J2 zty0$dY6~22L-n)jo_*Bqm>ioJUj|kdXT)tA?xRiECTX2Xr!h4Mw1x(H01x5uHXubC zxEqyRL{PvN@Oc~r5g^FosRipdz#+YEAG7h;_lcucnW&TdQ!0R5x?S?e7WL_`=wXqH delta 380 zcmV-?0fYYk2D<~083+OZ008-bnr@LH6O&N`B!2--Nkllc`SQ9x2&UjE?0 zg8%`<0`lvhzd$}*14J4{IhY2@0~G@V5J82%q9CTJ ziw|DsW)@w{=9Uq%fpT%pVrICd@O{58|1aP1 zo#%Nc1tmKG7<2{xshaYzuz!C?JMd;baO`a$%Sp!`0wV;lMgim*;JM+m-*|LD(KWZj zYi4Ph?d@e(R#>0_@1%eC2ZW^+bMrd6tx zT)y4VK-bkJ1cI%KilkcofNQVPW)oYRFLgSun62}A!wjjLZqi(mD<&JH6LotpRM(sn zRDM>m`^?kiB_(9YL}TgEk=4Yi{;HmgkWKU9rpB2@@wB-1a&6UQ)$WUe@(blpeO|^t z%a>2du@9-_R&+Sfb-l~`?E&{SxBY9o<*LOPF}BRNG|x7xW>m^)rS!5C640yBr3Xtm z2>1XGz>R93C+-e0`3QV~z2Za%>o zuG>roZ^_U76g6-tQ1sY~>Nm=qjYq1A124ZYUSz20d&Z+U`h-n9SS;vn9(&v@@;`e( zHL|N+7Ay`_9u)Sv%q!oyzhC&}=II;DzZ>s&+*|zZho9~){Bh&{>9=m({`J<6i+3t- zrv6TD1&DAWPA5kBB~O-?#Y=YaKce3G;Jp*?e)zs?zoX4*Yq#H{7M2(9-oCRWe_qDj57FNO}k_R10QP_X~-r;i@4uRV%x#5T_pirU&82z@25K6fQD$5j!9 z%w3^@zL1~nf8Ea=C<&!fMrdS;Ca;1ZTlc9*l|n)gBtdA@YgPXtf&$-XQ^zy1N`!HmDZ{s0C4{ZZoo zbF7U4Ab^00a=@*XJ+4Ns2#sd@b6v u%9P<30|@J>SnWvbu{@ z?zd(Y05GCndsS@mk$uMd@@p2r=`8?q1%L-%$!7t?2!NRbK(7WM=hU>ZqW}oQNeL-T z3;+di0GI&g0jvOXxW|8@SUs;)+Z0!91@$h)WlLG@jOu5-w9F`~v>%u2vI=y3=_FUI z1!x8uD!;xYxa8qgVX~tY822)ERWtmXsYh z6_$=GD_wt}4~)yx=0%IaGOb-4Yj(E8NK7=LWjlW^{xp8ff3O41Z}6m-V`(Ms!kb#n z@{>iYp}p_+-3N^mn!!G_Bd4J^B-6T;V~UknKnBksc=3mNELG*M?e8%xX)z3I{q<%` zT`B5BN72FJ-lnbGxqu_HjFWiIClKfm*J1}$uh>PdqB}z)o~L$)y|J~ks_w3CMn8Z) zlHB@pbDDWWs&QvF4(KuXJ928=Y^5uq5KTV6(1zWAZW@>}Oik!@9h#12r|g%AtZ8zx zAyR~^`+)2?_M8>)Y`66&0pcCOzTrk=YU2xVQE+0KnGMXlR87rA(@?Y%cjeo%zi%K5_xMl<}0O$ZF00sei z0saP%J^mjV4gpqHR_;YloyUJk$cWz?Qr( zk+No(#{oj@7J`4E$7vXfr%!QX$H_IW;OZ_u}7VrqofsgL&U$o4G4(lZ-4XF$2#Nn8!Sr zm&wiJ(|ITOcmXOxrG_98A(YS*3PPua(Zi5n1fe5H1w(h^9fK%4Qz($W-!QrUkZ%Pg PXdOTtGcl%VH|N}21fZx_ delta 539 zcmV+$0_6R{2iOFV83+OZ008-bnr@LH6O)qzB!2fYA>yn++!T z4~BpMAb=Pdo;zWU%&tTdH;#^3o|FboB21`ppP*BG3|`!vO*a zsNvt=e_;3gWsp&PA@E<2jYs6ilRy6-zkiUBR}x?ll~8{E`|rPBKv(?*X#fZyCWc@C z{sR5<_y4cI|6V*1`27FF_aEOsv5P)rP<`|1?N5fU@1FAh{`LR&Z;+>f8UO-_h2a-S z^>45NXRgW^dmj1ogT>ZR8L0p6`#YccULV|N4feyoKmULp{s$01EWj`ah1y?+Uw?lX zzW!mF&}ZWna*mJv{hvP!9~c-{G&?+d3k*|`>c4*(fguDCKr9TeUNL?XmgNN62ny_9 z%q*9dsq*uG0lM?$8#aOe|M~g806qB+=*cfYKimTdAja275ebRrZww4yzzhyBVnB*! dAOHw304Q`BFs$O$^#A|>07*qoLmyg!XMUkCGI9?Cm73aqU%2#Z_k@pC zRbMY$es}Kd+vK!Yv2iaB9DK8P-y<8F$8Nq)6VvxIFuc__|2}Wgm$vQ?#l^pW|9+W~ z^R}eyU0&(SjJ&6bDYso*K7Rc8z}WPGt;>_>gx&Q(4eI(|XU_Z7*!C_r@7J$iFT1L`7~W zYu|D6SGqYQc9!nV*ZZj}kWnehYz;NZtl{06~ zoIZW}=+UD`jvU#)fB!-ThX4QnKiw9a28>Eal|aXmR9Atfn|kufaHtcl-F0cE>||zww5`}SvgslE!eo2+gm$ZI@-FL-CaFhoGskEoZnwR zUq0Ucf5U+V4+PpS)NlB}@$kfo7c*|`_|bA?NlM$4D_g$sEIqU4&72>1_WWsbYFqSZ zQjqGjO`o{7-8r@D)vTztUBBAgT&HbnSXQy^*|b^L%-YKO`p&I;H*em(ef$1#Zq3bQ zXnCP`bh~pMFgY`*mbgZgq$HN4S|t~y0x1R~10y3{19M$NlMq7#D`R_8D^n9~0|P4q ogH*nwJ5hAx=BH$)Rif)aXb=gmWu0iK&c(ps>FVdQ&MBb@0OK5`X#fBK delta 567 zcmV-70?7TI2lNDx83+OZ008-bnr@LH6O(WQB!2=1Nklk{|_X8{rdGxSeOmSU|{&ezyJ_HEdL%o;Vdr%N-%$Z$Hm3<9}Iqd{K)u^ zfmwj>|G$5%EUcngSuE^4|Nj2HKdBcWfLIuKIDlsTd-L}1yH^m^fByadGHv?5AAh;4 zN`IIc8A1AQ-~Pubz$7IBv;-i4SQvovfB*ge{fFVtAE4aNE7yPi`^Eg=5yP(^Ur(Q5 z`TLjM%#`8#_rHuB|9}1hItd_vSpNO`#qjs<|KGp=|NIHm@MXqK79K7}ZvOwQ9Df%s z{`~lX*xI%KzW-!k{Lc96Cr|@G0I@Lq_rW0O%xu0Al*{BASVF?5X-+WU%;w=g3!Nz3>+N%4GrJ->|$i+5HdGsI1qtx!Y(8NWc_1cU}9nd3bHCHF$;)+yvo1;5I~H# zff~S3_=$mm_sW$YKYqX?7{d7d`ylrtM@KfWG9Um5FaQ>@GTkwFz_$PZ002ovPDHLk FV1hh#EQtUB diff --git a/toxygen/smileys/default/catalonia.png b/toxygen/smileys/default/catalonia.png index 5041e308e3a0f57f5ef9651b574d49d30e5dc635..0ae1406ff04129bf8093dd607a945d01105ddaf2 100644 GIT binary patch delta 843 zcmeBU{>3&yvYwfNfx*${FE@~4Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!D2qg_}lONZ@+WD{mQEU`YZkG&*U#Z5-+bSH^?3Ki`ORm$$J=FI#7O_Ej`>#+`L{IWZ(-n{JfGiL?!VHUekR)fh%x^j zVfZar=Ua^VdsV^jrwu;;4E_8g@Y8pnkKf%td~UiO0xeMXZ0h}zTw*bwrKFVL*1&zCuO%)l;e4<~^4=!#+v`Pct`)kr zivQwLuK)l4hdoQW4U9#`ByV?@PMJitGYs_%4D2PIzOL-g*!j3rEdTn5rT`Ue@pN$v z;kcfhz`*3DmL?WvR>tS2mnX*-XJ=Q(=%|<|7-(3?>8Y8?CK(wSX<5nYs+uYqYFf(c z%ch&FaN*+B%jO0qMrMYl$p+gQ7`?T#rK7E@+1=II(!~u5N{hdL`10x7$J7s>*&7?` z4=i{v;lhRw9Ug5fG88&Gd%OKTe7yWTeUm-TpEz;i%&BuH&z{!T(9zOM)X>$|UcX|^ zs&y;ZuJ+H($Vqy@V(GtY=hCfbx7)5=ypogQfT?c$#kAJ>Ko6*vxJHzuB$lLFB^RXv zDF!10BO_e{b6rD|5JLkiV^b>=Gi?I{D+4nFJL@ZaC^~ZUQ!>*kaq2MePs`afQBj?X Ofx*+&&t;ucLK6VRX@cSa delta 348 zcmV-i0i*u<295)e83+OZ008-bnr@Rx11En0FiAu~RCwA{kxfejK@5hIRf?spRf@%r zgQvPss1($j7k`Gfe^k5_v7mSn_2fYhRojCrv z-`h^9Eb+W?{1yY{CG> zH?d==2|u`THj_wKNP*E@-AJFu_4CuPlzi+EHgtS14B3iLofpaP^TvYgEIqzEdzr&14AeSgEj+$ zDg%Qo!{)QS|G(9Je$-OFbz;fp{(`MNxtlvPH?*d%Yf4;G-?+8x(dE>}&AIh{3-cMo z8UDX)ieJ>+zO(G#yU38am61yeL+9ta&&_h0on|{T(R^xb!lFQ(zF^OJSq%IP46)+> zKGv>1Qnd3x_TRT5kqe5wXXU@T68h+Tpv{cL$r}O<`@^;Sg4MeHYSzXxa56CXORhQ7 z_3v&(%DRH^g}IL}hMYecWIZEc#V+4l^(P&BR=KNo`^C(1x^>FGXiI<2+QO*i`QeLl zg63zux#aINH_mNFw9o9ILkH|sJALKbJ)~M(@|Q&b!-*k~A%An1|DpoV`8oaz(tPJ6 zKRoMo>WGKkq%f_nAQhnEHV?@rmy7$<%_c@Ocr#>eZ1-G{<2pCpVP>++j5z0MQP0lQ z+nqmbBiZCEQg1I%Yi-;g!Qje}wyxP_PP+ZfB&%t0CX*v=CWlz`1?cs7%eA^oG&u{` z+xbm0;;ypL?+s{+zbq?3=EPC|AC-rcD(}7AB;)f?k>}xsxa@b2eeO=j~vGZ}M zGM)*Yvl}RM(9^{+gyVX00t1tqTAEmxSs9<7UY;BqTbx}TqoZP?V4z_kr>ABn8=GXL zWhJYtYN}|cX(_L-E?cf_tZgl`vvRUDe0babtCHM_fdx_G#GIlo>#TbjRq zzI?p>|Aqq#9!$8fp`fkf#EKU)ZtVEca%9PqDOa`>w0-G0v*yj5JA3{tdD3)f(W8R4 zNtZT#>N>UR)vQ~)ezhH2R?wEVaoV+Q-}=t2dpGaizJHt#d1Qols(tkEpJ!xhV^bp~ zz|7DVYVbwX!~z%u45}rr5hW>!C8<`)MX5nRn!(7x$Vk_~T-VSf#L&RX*wo6zLfgQ= p%D^CxWj!#FBk9P^PsvQH#H~R-QvCfyLv=1-ws7@xS?83{1OT3QOThpD delta 564 zcmV-40?Yle2k``u83+OZ008-bnr@LH6O)4jB!2<}Nklreg5($&u7Mdp7ytr@1*mLuL6?u_$1iW#S}ybc{rB$=)8D`U{{H#<=MRwl_g8`Q zd!pR?`42eGzGK={HW46zKpK2xz9y_;)e!&GZh19qy#g!8k3YZuefszN_rE{C|NXx9 z=YP-rw_nb@VUiUP2igD-Kuin_BL6vl`zgIQV*mR8|8G|2U!T7In`-)Ow$bxne}9LG zfB5z5*XcJ*K(;aeSD>o^0st`p&i@1e@&c~g4N@7~{}B9V!5sbp`j|K8<@Nq-&kt=P z>@pAZ`T7Q#;RiPl^j;kSgdzX}05Jg0|9=Dk1n0)+1;F7M?)v`y`~3U<{0Ra3ebf&N z0Q;oj1sWy&ncoKb{`$V^0u$T`0M!5jhzS@hfB*k}{Pq9)AAf)R`}OJ`u*?kuYbTW`TgLqkV&=E#U5U#HfK6edEdx0*XeY}ZiQOlUy;v-wRk1TJ<{4%n4 zXmpP^yxa5hX~02aDO9**Bq><{q!c1bB}#OLuaihNZr=2MN7i2-vfOtBnXm;aKq^T{`Abh*|8?ulMGC~4z z@*ck(r#dHhllj$z%YTC?fmX(jLlzbd26_9p%3QaX>l{XP3p&Rhgao3P32|hUclb}_ zH4Ww7d7*K7^c`NDbP<7yml9;1fvUXb;at}XjWDpGv0vXdh)UrxFp;#()a^6ge{HyD zd&%7$(zg%l+B~{ePdCu3$EMy&oPUD4Zq#(4+Knm~s>D8Okd=~ngGu_l^*I%)=QdH>e^XS>WvjhEu16hoAMOvmpk-@QFDodm<9VQZesYg%+mQxwha{`I z)N0UKjY@8xQ3RwhX=%%utmTSyt%6am$WW$drZS9@Vb-kj#QhW4V>awE9r}M@vuU^Z YRgnK}C^Xws7sLP>^`@L#>)7Le01R1Uga7~l delta 479 zcmV<50U-Xm29N}h83+OZ008-bnr@Q`11En0vPnciRCwBSkv~k6K^VrL@A}nJf)*T1 z3L#8Y;~xSzAPEDBL&Hq5jf=)VENWO?jf9E6D@-hqG{&JMepOP?gpvvgmH^SjfyuJkPqeYl28iLgD=0{>PL{}DOH6^h-;vOHUI=`GVG8cpu&hr5;GVtM1=ERjLWh@3Er zW;5S(XFu|VP5P!C7G81FES&lKzJ`B#h`5LlS`7Lu%FTRf7L#kXu3+&X?knkeR=P8a zKBWsnL4+X8^Pe|hzMLz)oN|Vu;Q-Wu?Msztb^608^aZFOB*Z0%0DFDWfVuMO;j5`^ zqWS^m4?xnUfgqAMkPd1fVCXvf<@oi)CxfC!GwxeZzP)9k2Y*l-=TKZQOb$yT)@ltL z*3hN=Bkr^{q3VIt5JsDLw}?!{);eE$it&-LOfg;;Q5>y?tYj#_Kc2q3jK&XpxVS$!Mzz`g(*@Uq& z_Ap|mU=fQJR62Az<4B7(JeFmw%N$~7X$4lx7^U)BNVZEK_x84Q{kjkPe>pklm*0nz z|Kc;h#7K2UrRpm7I4%jW>07;0GtIDrDJmH}XTfESJ%h7U;q zHq90lDCN)cW|)~PBO^4rWQCr&vPH5)LzmV8{*IZ+3y?MsDStqs8(#VywoL=e!OUFt zdb^`h?b_P8i+4kv=8*1oC{kA!uBmD3C$wLD6Kw4dXpR2HE4~kVabVi$;$?%GT3h?% z)2pQR8rf_ho4Sc`K>?MUTi+fy_LaZtv`_UVUfTMPMu!8~yxx_KjSY(02!|u#a5yBD zkt9hFL@*c(1Oh&v-|r7ttyZts8;KsrrD4=ENCB2lNKv)$ft_QBaR<7ZCYKc)Xo&#Ys@dr*JBzHqcqF|3eV0V=1m6h)TrP3};dE;` ztS(kYKOd4nGYYoVs-K?b{OHa4!Gn!@1jF-h1k%#{Nl9k+D+4a%&NTb>RQ#>SjC(F# z);X|RfudBQP$@|sA<4r8Q4|d32lwv}$Yg&1KA-Q+^>rbR^KqPq%(~UNIRNu_aEUI4F->jEkqI zFm|_`=#QPOiX5qO>$JM8tU^^)Ua4OC)9YEBf4Wf3VsR3-J@-8OVteQLu8s@3>Y^fL z@jHhOmrOr$PEOe$JE~Dsz~_rZc2t<5wG0fJ?v)P+1%u|j?;qFD@SajbDdKkTlYBqg z{Bb0_nf6)hTQX#9Ea`P)pIWuxVJGO$mlPwDlNo({vuU9rA>o5{oIyw0+Vb-rjPK%p zd+S*3U#l-}M=o7@_vjOs7#kUGYNSG`1r>Uy;N-Zo#!)?C5H{J|r`f4As8v!TwD{|t zg|UxPH1Z>wigHaQrf#T=4dA1E{vLGio^*jS9mO()m{70_MKKhujQ@G)KSEu7MNQSo Y{}(tV&09LsQ|SN-`P(^nWM!@Y0+lGOkN^Mx delta 553 zcmV+^0@nS>2j&Ej83+OZ008-bnr@LH6O)?*B!2<;Nklt4!EOKi{E9pk`vd>|{r~;`|NH#^{r~=nZrlQh<=`_ZF38C6g#jRd7@s_ODJ3Nev=9gw z8UKSQhX4P)z5oCE{eQ>y|1j|H-+x9%#`EXT0R#{W1H*5i5@u$gs{agN2m-%;fixfi zpeaCQpFe*F2q2cU!)f`}`5%6M06O*Wzkk1fng0F#_a_FZ;rH)3x99u;@_vDQ@#o(k zL3Y8CZ6yE!#IkMsHdRH{2VWli`uFS4-#@?q{r>&;S2WQ0U%#fEp7!hSuit-v1EqkZ z62H=y4PO8Ph=t(~!|(sUff7J7fJ%S;1F=Ew`TZNJ8c4GI{sRnM=06N<3;+SdQh$=f z5T*R*>+j#dp!@glHz>6KgEjp6)$jNl7(D;}{sKw;|I5Srw=;(UAb?l`I2d#V|NQ?i z`TsxA_rMVS4PwatX87~RNfOBZ14h6=1R*h727mx!JamYGiHU)elYyO`ftwE`{tpu@YRM15$Uq00000NkvXXu0mjf31%fK diff --git a/toxygen/smileys/default/cg.png b/toxygen/smileys/default/cg.png old mode 100755 new mode 100644 index a859792ef32a02b41503b5ab5f216191af397e02..5ff3986d801e1b9251f2fc2d5d3b70714f8ef597 GIT binary patch delta 800 zcmZ`#TS$`u82!AoX~P>@Hp3gbdD-TEr~hPATk1CZo14?kn&o9_&NRGFZHmnzqM!$T z>meirLrfGihzhe1$qSV|l!}_yZrV_H`!5o<9((BNaQM#QobTNAT%mO_y*vOQBkeb% zJ2KE5QGO<03DCv{=)46$xlWxw0GbhiZ9RbaK0t8&gBN*e0B&vS9IXuA0!#qB0T=^# z4)6@{kHL<20k&E1{b{$|Nw8qhzSVBpGPQ2D8aIsf8x1w9HMOf%GuDcO13g6*k>q)T z(BMO=9RPFtR$J+6Y0;OW!sSBkk~VKKPrZ;czL!l=nIxG`5GgpG=zPeF}#y(?x+cJuh5Ja-o-`^$>tO6KT3_U-jIG*4fi*s|tTnz`%V!-+czd%ICuf4DOUsR53~FNYY$D5Ndo@fotT>%uGjmx?Lo)p{P|LuyVO8 z0Lo8bnE=Z;tOFb#9t!B1Yp!g!GDWtu-yUblV)hW z=F9bZPj|M#aQvLG#aES3OOuC%m7KcnmJxX=ATP1O8>7k1aaGJhk?DwxRdK8UZT6wb zOPETLiC4o4EiOp!@W`UEqsm2rnxv-CVupfq^<)<9Vrn}*JEk(Ym~lR()i0M%Gt|+; zODiRZov$PCwXQkj_THVT{ww1ryd=Veo*QUnz*O#873QN-e3-gdIW_Zs`rRx)mK&cC z$D8GvhxPrQ;^Bb{`4e-TLH4WOUUxtbVtKpzoDZ%?SVo!7c=?jCLR{Wh;Q|mK0s#+6 z;Uy+(6A^I|Dn?Nbf`}1h!t~zfA45&um5fKB`VB91=ii?+(ckX8zrd=& z8g~5t-|*}I50LQ;KzRlRfB=Hn08;wv-+!+pkw2b%5QD+wp5Om#e*ORY`~NSH21c-x z00Ic40ciZMzrSKc{`m95lmbbh>WW|gzd%(pfSn8sYJdO&1?!)`e?oO& diff --git a/toxygen/smileys/default/ch.png b/toxygen/smileys/default/ch.png old mode 100755 new mode 100644 index 242ec01aaf5ad351cb978a4eb650ad801a438b09..da989f3c95604088267f51fda1c782d0f9717027 GIT binary patch delta 551 zcmaFQ^qggaBnLAC1H-(cc9DsS!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6@fgaX7#y#PvS|!(Rr5-wX^t85q7ZF#NBs{#RM~ud3>AY3bkG+&`I_ ze^OJwg2b!-#mE1PiTU^M-`~G~|M>X)c6a}sknov-;a_CrpFe+oIXeA{j;{X*H15ye z-@kwV`t|E45JX46XJGgN1kurNfl@_9zyJLH4g>)KuNfHrWM%#K^!)AW`dwfDo0ish zclVb-!L+nrE-t_9?Z0ViepOcf=H&Do=)T0HU$L=2BO-oAME(em`0nQR)z&)whSH@#p&CyY#}JO|q9?+| znhZo-FG_nQIX>)^INIjcB`STR@ZV;oIqUcGEl9RHyoKTRGp8-L=B{J@@@t*D$%l5v z19t@{emOo_pr_ho>gJzGl8u~`c*C<?;g3@e*W1Le`*rV~E9dKv zHGAz}ZvPQl&}0y#b^7$HcxL4jJYHoT(tm&sQ7v(eC`m~yNwrEYN(E93Mg~Skx(4RD zh9)6~23E$VR;DJ}1_o9J2B~~UccN&>%}>cptHh;&&A>n;xR!OIp*j}>gQu&X%Q~lo FCIFHk>m>jH delta 304 zcmV-00nh&D1n&Zn83+OZ001V=;Bk>56O%atB!2+`Nklo>#i-#`@r0mSeZXjx_DzkmP!0>PiZK!bn( z{(t@J*RP*I5FP!VfdL?Zz_u_l{s#l70Wbh@FF*i+ZTay7EC7bcD!zUNssIQe79eH> zs_^w?`19xg)~!%Ub910m7-eOFvH$@DQUUZ8$mKxS!NIRzKm>6qKmdVMF#Y}m^cYA{ zd;-uKhMzxxx*5RQfU*Dq#P}5{`2L}WCK&_)1Q-CWsmG6}iO4?z) diff --git a/toxygen/smileys/default/ci.png b/toxygen/smileys/default/ci.png old mode 100755 new mode 100644 index 3f2c62eb4d7dc192036af889b593b782dbe8abac..631d1fbdb168a664e9bfeb55a040143c9a863f2e GIT binary patch delta 769 zcmZ`$ZBUB=9RDRJr|4xRpgu7c4x zZ!{X`=H_N+XJ=++&Lf!FZn|>i%!|FuvmB-)%Wk*ZY_@29H2*a}WF$oLPJ-)TI`1_EjLIL6#FI?m%+R&rrV{|lVs{~!otD=cbLo3 zaMlj4-8{M(7eTAlp!*A{wq!?(#PLXMu~;}m9QGjF|D}JFE(+)1r~uWs-_b0#H^!RH zX7($#PrpyNR)A{Qj)Ce{+M7l8rdZp3p~+-o4Y0iXS9$h&h7E^V3*mU4XYbl=YtOW` zrbp@`1zJIvCM-k~f|Ib6z_btZq#DL-%tVt|J!*g|IGxUbOV3Wa zCUMJ4*dZ!?|7mKw>+n!X62%@rJXWpXx@psIbh@NQi6~CC+(63Z3PqkWH=iV@C#Sys z_?kcYeLB#`-*2^VKmeDmNAPWc>Ak@Eq;wQv`QVf5fqq^_uIez|N8y!w=BCX&?6WKrBG#F*5vR`2XiW&+#(Y zfBu4_kb#krkpTvjIFx{1{QD0WWZeg@6*PFd$(Sw{xR>~Pu0rNOxj49W})_6!W> z3=D=04B89~stgSB3=ERy=8ew|pSpYb-mTSJZY)`MW!H%p=dLVSbE=?zVR6gq`uv8K z*|p2kE0-jfE{HFj$G{+XSz7YN)M;;8o1YdJJef!41z?_{p{r>(F6Z*Cv?VNeMZQA7e1;?+f-ae&vu}fI9N8Ci`$X>h9F3W&6 z6Yu7L)G5Hgh?I|Q>)xDPzO*HGW>eTz;0RO@1XR#!eIbMfjssSPhVH|XY71js_d#}mVH2> z-JULvAsp9}6B1IA(h^gXpFeo==-I=kkJAMNM1;fyMTMtNm@;YF#Ho|h0|G*V!U98s zuV1)w>DtAsm(vXlOpMG7O^vs2*s|%`){R?}HB%*}rP;?1jfFWsEMQ=mSY#M<@oGps z&>yNLt`Q|Ei6yC4$wjF^iowXh$Vk_~T-VSf#M#im%GlJ()KJ^Nz{bP0l+XkKh)`*U delta 524 zcmV+n0`vW%2g(GH83+OZ008-bnr@LH6O(iUB!2{w?e;1_%H#0M7peoaC4S(iIQz?fLHS{`vU_5fAwL z|9>?1`m6F71P2ZL`~Ca;{jHc~NM4g07!Uve_yUM&hnUSj6-^dZ)!#y*|D@#D^bNmD z$X(}A`VCb2`_Ia$$-jR82C4>Xo6{KsbP_-SF+DJF`pL$*;0gaz7NKVhyoiy0L>N`;RRU= zG3@U@5D5(&chF91emYa2&TnUFw?^TpPVhum zGNP3{)?gS0S^(|Z&StSvKKoGmdg75F!%x0^}gzfF2Mk8i2>C|c$ zfUCaVSzPQW;5oQlGCP|%#kR9pxPnf%#l=~pqOkCAG&B?m48+pXwg4Oe&bm4Wk4I)@ z5p1?SH5H*!Q6_T(fN)oGImzMRNlCWYSZib?78ZukXcm7z3x$Hv>9`vgOUUYKLMS8z z750)6oX^Me@@%=e*33*SJsoAS5GE5zNLT{}SOHJ~3xFBG1YiU(0O$d90Gj{@2M6Lk+}op@xWXo>xJkyYxhiuDFd0l{BI872a;hMi!A@bZQ(5s02Ajd~SI_nR16;0` bUXr)|AGme5h2E1K;{(9s7IDUMMYsO~eM)iR delta 387 zcmV-}0et?%2Eqf783+OZ008-bnr@LH6O$GLB!2-^Nkly`n126Zkdy=)^OxZdNGBuMNCpTAM1TK65Ku8d05N_AhAt9f hK;@!I0RcdO0RV=LXa+eyIlTY?002ovPDHLkV1j2ztA+po diff --git a/toxygen/smileys/default/cm.png b/toxygen/smileys/default/cm.png old mode 100755 new mode 100644 index f65c5bd5a79e2885060515be55f03a3ea4a15d95..6a1341257d62fd6f2a259a307f0d09a3af680290 GIT binary patch delta 895 zcmZ{hTTGh;6vzLIMhGaou(`~Ku?04T0{!TEi63RXqy@Sb+OEYF)2%JNFi=L>I!MPu z7cbGN%%L+9HSxt5=Vk83MTE^{s4$kuye%YuLWAF4eDmq#mM7V3RE72Xw1UoWqZEGzpYkvRg zF?TxcOr;JbleP%Y*Do&%qo-UqT+Zvx#v6?*N9-A^boQP%ja-1$2Mdx^@W5i(}wp+fn zEH@ZeP1WfiHL35FNu``u6D+%ji7>lZR)v>w{D%{^Z*1lvvvJ6{WUfw+s8iFG$$N5s zfZ#|G!xYf;Qw&=3HfI3x4CdXv_O)*ze!pe$hdIv>qWf=L~ z9MFx{o2`ax2K^Pi`l>3bCzDbsFBY>!MGR3uQISnFEt#1Cx)As-pu4P7T~d*k$OV;@ zFE8hci`l|LX6H^?By7x2B=Qo8T#6E-9iHn=her-Oh6T8WSfhnE8u{vKPOD|Bsu;tw zG%cej2}PAoPYY23&43qhVFFkNgbzjm0Yo@2GCPa)_4Pva?t4P=8=8zYx)1thnzsl| zR>)AN&sxaJ%PAs#dfh{P%%IcQv+sJ{ZlBNB6!3Wb-st_m{#@|fpP!rCzG>^0CpT}) zfB)S1cLMJ7z3;ty$YMYEoVCGrck0)sso(GX_KSkB%O!HFLbh+vJvDIc%E0AF|JANw zNOH2Xqu(7o!T-ZhT!N)H2J;@z$@64Ck^A=RZ@qE$OwXGeO|_;2fm%bI@u%_Ix6V#Z z)Yaae6uX}(Eh#T96Mg;Jm!Ds}@YNS$_tB#-wjOI~dm$csu=MwZ_=-37G12k!Clj_$ z>p$G@4QO1h0`%>d8VCQ_HY5P;ITF%4LOxH(PkN90g#rm7k(3dNvI?oaf*|+FNU1_Z p5F|nLnP!F`I{q`XwfkBE;r}-*Zf<_8x8ms>z@V?!UDUc?`WFjtiw*z) delta 463 zcmV;=0Wki(2aN=f83+OZ008-bnr@LH6O)evB!2;)NklRy7#=a6i)T}*Uq4o1{i>T+Ei z1!zE>j|1c*Q?v605DU;$28RDnzCQi?|L(|98e;E17s>tTLaMG-~Y>h{r`!g0jL-tfS4HmF#P%V_xIo5zyJLPk|2W%e*G8w^Q2O)d4+B5|fi(R2`v>GCpb`eK zqF=wDsv!Xd)$kXL7ytr@1sH3;|NW8VmIV3%=sG5;-wZsz8G(9%rU6NwKMah2U^uG( z0-Xd9K#UBf3=E%u7J?B2!^wpVO32X+L=U0S3;_TE1^^JqWP=C!P~`vs002ovPDHLk FV1lhV*)sqD diff --git a/toxygen/smileys/default/cn.png b/toxygen/smileys/default/cn.png old mode 100755 new mode 100644 index 89144146219e6fbec7eaa89e1bf4b073d299569e..a73f73bfb47e665bae2dbc65573e16ab7205ab5b GIT binary patch delta 828 zcmZ{fTTGJ&9K{bY8Awe#8WV z2bcofz=9fMz~8_q@C^6^_#Jo(#GNM-fs^sGJ*$J=A7>ovzkR)AZ(C|^UbI?cG#xb> zBlY$3dVRR2W(061f+sfCjwa@fi^d}>-%{_#YU2Y{;fjhmrE*qL9Fj@{0)an2f0iVN zfj7N9pJK*;ZXAhH)Z06?>!X@wXT@BZGNe$1q>>pP&zGAURH+^UuY0;;21Bf#iudZ) z|I)nfD-SEnLUKh=BJmT1FDJ*7l@%x{x$gwVnWgO=3wnJ-tBuxa;;!=9($bJz?&I-1 z`}R#`X3ogvw}F>-dsM59RFYw}IwY3`#bQ67@8fd4+1b-fjl*$gWOyZ#L13w+B|?&O zB_$!5EGQCB5!X%NLvI>Ouh1#H<{BRAXkS}prCz+?=6E%gqkrKfGpC4J^!iWgCk delta 410 zcmV;L0cHOB2G|3T83+OZ008-bnr@LH6O&8>B!2;FNkls%5I`(ImPCCuZ!E*-75`Wj|FN6?6?pph^M*eRzyAFH^XLEX-~YfQ zgnez(CV&6}Y2c0f&n)(oH}3D-s=xof{bn=y%c}nS%f{azb-#e*uYbRO0og3FvOr4! z0)GhPkS}Zg|NH#+>$bmtfBa_T{rmsVufH!rO2O)W0!b+P{TrwOAb?mv&i(NBb_upTT2B2bq0Af6j6p_e`KS+`N4~BqY3J_obI<9jt)-^W000000NkvXXt^-0~ Ef&=@_LI3~& diff --git a/toxygen/smileys/default/co.png b/toxygen/smileys/default/co.png old mode 100755 new mode 100644 index a118ff4a146fbd40ce865ea3c93b9d20ab3f14a0..075ff3908eb59323cb12a9f04c903ca41a7f2c52 GIT binary patch delta 902 zcmZ`%TTD|206mOkRUAgZ2_2H*12iD)mQs+mGQd_TErga96_}Jh*m%i=iUQl}$dV<~ z51!G@8k3o5Hpdn;W|_<^lNqw9ZP!xzE~Tv$#LkULfzpHXsO9kaXPdae^6Ce<=&d&p92;i9*pkD?O-&ft^Gl3{eB~QqH@jZNZ z;k$#yF)VzCg;98K;pt7duVeNaX0E_~3DzN)hp^%44STx7?z3T+BIG(9a<&C$nu2!u zx?LQw)~%V3t(vNoE5-`Lg-Em~JX0Tb)P-#)LRMkO!Vj8w>&Bx2L&@6Ip;cY}ik9M6 zA6PMP^q(SV;;!kLtGYbDI&=9+x?j)Id<@?h!k(_M<9Ns}4B0A!R_?m_NWfUUW?-&P z<*n!lzczDOo%Zr^>azZAjS`5PJoCA?TG=-%P&&(d9i?aOhdXVB?UsTTGqur}Eivpr zsozthOR3fro|*=(Ve{na5wGAQC$DFww98)7VJkdkIoM>T%1zk~)BEeDcAwCth_p$3 zbwRTMxQeLB=6)~doRi%-Q`By!x7c#uwY()UWt=qbt(ktadWtO2ZLd&jx0PwAGJ_HD zz1KQ_gzGJ4KV=ko@)$0H=E@|T=^2h)Y4+q)>yDk4xJ1*|1P3K=6tDw-%49F<>;I^& z^@&8!`TRv5Z-K*^XR*9RMRN?sTz{tWyC z+y};ijg5`u)}*q?1)?O}qpYj1jxUWx2uZ;yXOregTVv8u5$OSkRV>_Ol#0bN znM~5yAeVZb9@pHgT94yZ zB89@R{4(}6CHdmTa^922SqDbHX>0X8PuWRUsotrx+YT}B|M=ktpKujO?T$bGA`FCQPf_+>eYf#AP(hhk9 zkVE9;WD)sUlw2W&pi^md8ZDC`=mas*t&aUqp{-rk)Y$$1h1+|(#s(<+VgL@ig7s~Q GxbGh=AC{B= delta 421 zcmV;W0b2gV2jc^f83+OZ008-bnr@LH6O)z$B!2;QNkllc8jOEGB*gaW3f?%sPpp7);jV>)`0 z)<}QEP+s5ZT=5Va3sdFhQmgMA_#KdVj z{m=M#3zyqRwfa;~j*j|n8NG&nPhYQ_l6C!>H5JW$nue7Fcb;|0we{J^!xojpU>mjS{K0$W6F`hV|Nm!Z04oO)UF|Nia&KcK|F z|9}5r=r5QAIuC3Skj==&{cH6~fB+Q6K@k8T2n0YJ3)p~`^rMAeiNPT#pPe0tXo+02 zibzBS3Lqf;$J4#~?PT7nPXMt1J;(rbFHjWdMu-NmG+Z5s{PpYK@83*76B!r)0tlo5 z>?xq}zd#y5N`WeVBbyB+frxCZP$ zfZ_yX!N31NaiFfH?PUM~1k{iYi$!>J!nueD2D5=A6F`6g02YCH(;6J=?f?J)00>D% JPDHLkV1j(qtYrWI diff --git a/toxygen/smileys/default/cs.png b/toxygen/smileys/default/cs.png old mode 100755 new mode 100644 index 8254790ca72f98d9e79d94bdfcb8839b1fd434ad..45b4710935e4192cb0369c78fb54140001a4efd8 GIT binary patch delta 830 zcmZ{hTTD|29L7J;hylZNxDg(3n??;8%4uzfG%!Oi)XF6-Em&NuEp3m|*{&_USPC3a zh-@SZ-sXc3nvg6p5l}IBNszcr#E2K%qA_vPF|BM_45vLkJv}X~uY1_{^5y@1`91ve zjW0{C7${h|3_xXhDrfMsJnB=+&iY!QZ4=Pd10>Q_*E_&R0DqYP$yp%Zb?&C79LTt! zuF)xgLEu~9C~ySm20DOX8UWmY1K1B(03%=qdPAZ6!QgZtaL??_d7bK+uH8>{Xcm;cbv{iyM2P9 z?gF9I{QTnLVk)g{@m}oQ_o|%b6B2k8UGd-OOhxsH= z)AZcb6kk-7Jb#|=@8|pa5u&yvtdJY-MUCY0iBn(@ImfE4wXZfl&}DAZcZ4DoL-S5fWKbmsKQ-D+KZqozUo?C~$N5kZR4Y3 zSIE)J*REX+xV#>}+t#YDX6eD_-Z>Hg|ZHrMcdk1{q*J}3KBo>b?Z4eSd;TEEFi$J6k5Rz?TiD>)h1R)^^=kgKle*nif c))q2|!v7^cY07^;DA=40s1$YbVX5ixzw9$&?*IS* delta 376 zcmV-;0f+wl2Dbx{83+OZ008-bnr@LH6O&E@B!2-(Nkl92*9D3+W-Kj)qguzCNUJoyY%3RL&|H<0}M8>j+={{H^;_t)>gzkUHl{>sYoGBA7t2p|?yQyooB zA%9U(u7Cgk{{@--A8b6x2B1zb1BygNIks=N00WJ{ z7y^0)%zz1kWJN^&bany+5X-;o*O`wU`(c0Y9s@9dfaupR5P~q^B7c4}{Q1kEq5^a? zKmf4-1C{Cb9|lQDpd3gPDEIp}BZ&Qn0YZX}hajM0fB<6r3W*39VnF4hNdW;sfB^t= WjAt5u>xW+e0000A@u)LHtzkKR6HcYp4WC0}y#eJ8(@oP5JC z7#Yp41wRh}3A+Ppbnk3XzAZ`NrvX&G2cW72@X{;Q6MzZ?;He0Jbp~K_$+y=I>;?#W zz)KaNi~qvikUR`~dtpy6?COG;PT2k(M6|)X=ip5}Y;J+|S_siVP#rKds{pR!)%q@D z!_ZRw;Gebqf2jI?ukKl_>iX?y=da4L_IY{R-GZ}oQvHm$^$vi>4e;z(l|w9kHg<#Z z1t_22L-|OLk08ASLE*Ru!$}uLINkX2BeN5Ls;s24vYdx916M`@fq>ud_xXHYua}}I zkHPb%BMDmGSiPhs- zS_8ldJAmI{@Y83mT*dZtEc^DgbLH`h8?nVdBKcP%r6b{@OK)Xed|fycUvdrLh!wz7 zTk9Pi#nMu(DB8m>J=A32HyXJ0lkA#_B-O3GpO43Xb~CQxr;INr0FIFb1q55k_I7n* z80L1nT`t$s(vk(hCzDxF6d&lvNeaU~Zj5lbai`1eaJp=Er_JWD+8irZ`@Mxl5@1Cl zEp3}C{AMoq%&er=5Tx^RGU069P*K+)>zYWJS!Mrj}@o0FV${_x!BdD<`(>@<||n6WlA{Kcsc zgdKErlbw_x2wsqgL^;_qu~cHd|M0KJ%a0!9u3Pu&E1TAD+@No6J^i)rtCn0IPryHT z;KPgs)7{zmnR_!5Qyj80S{ciX*>QR3(#4S_8!=IKY%8r&+l#>q3MTi}^J1qF6 z(6u2iuhFWT8c&JUCpGCQsr$L?G|rs~!{oGBZ8UNW49oT{+ae>k$+kp9ZN1Rb-#6Gh z@Vz|mlf3M_;{2jQ`;(QYw!f{Gc7$0ko_!dR(S19;omoD=va8%&Jbf+bR_5- z>3_l%s8Eq3R!CWrGATVE8i|gMLgJ#BI|WRHwIhZVgKR|*7J}#!mizt#6v=b)WtIOA W+`@8ADF5c2Oo zAmh)Ue}Dh|`~Bzt?|=V*BJ*?h00a=@X9foD>Z(uAp7SLo{I;-RVE7Nz_WwTv6oaXM zV1JsCk@5Y&@1^xa7gnxhi`TP6#zkgpq4S)Xql;C1uxCanGAPs+Z>|pxz>G#yh zKY#z)%d4~7Dq8#>1H@Di`UA27=wk^H4h9ApfB<5A%fP^umG%Gr{qKMN@Fv9l4+;B^ z4KOeO{lNI<%^QFK0z2u?pAUcjaAlnV?Z6oL4PpER#lzpf|Ns8`|Mx$`zUDaq0mOKkfx%RCCNxrk r(fR)m&{Ht<3rzk4lMG;z2_V1#=BZQczjkGC00000NkvXXu0mjf<3*NtsO*AUBA%`#!gOO)Qct}w}4&$8yNOiOx~UWirJ z%k`pFb9A`la6aG*#!;zV)}HOzvuDrC?5htx{qFbn;dejXR5iPJk)JOB2-ndwb%FDc zfYi9{TL8{415gzJxV@I@H^3P#z<4r%paNiFc8zh{Isl*h3Gs3Zgz{uqHF?qer`7ZiwCw01=$}*70THKj>BDKkN zun|XKu|?6w9DB?hX<^d!bV?JQ)JW~FBX`ugw^kFID_r7Ie1p!pw+@H-W14;@vxTXB z@wWEGYs!&Z@$6-Z>7}fikd(VbrMN(g?Y!%R*`ts|tNWPxS8rS>f^>h&ZU2HNxI!`f*@Qj7mnjjrxU|4hr?mF+Z`A- zJUl!BfM2-asys^m$+64jXhMQbDxDOIZ5o+#B*~g5vhaE19M0H^6_%Ko_W&r0n$2dA z5@$N7o%h_gO+yZ=LZSaGEWtDGf1Dgo%4>Yy)|`)qPk` z)C|3@dHxEYJX>ZOx@8=aRbtX|M{KEGq_b}<%W{Z8_iWlman?0sH{h8s5Rx+ z>#91gho~$2$}|t|G<4s5e0NaYcsop8*iu;CfAwK`@2$3b$j0P3o3i5$C+3RMeXax3D^#(xSyC$iFxWSskdW6VF3rQV5;!9}riXpAl#eKmdU>{QU<~4Yb@` z>VG_t7pK2&&RM@-e;M}NH3lmE{TGgaPW=ZEKukb|V1pT~WX?wEZhH7ZBzjHrgZE;| zhN~RpkNpB$_3IA^0rdd=01!ZofB*hvWMupg^1x#TMw$P(0V5OB#}6L>0*K`UGc#9p zHKW{n=1`s=Y4`sB0UGuD|F2(+4xFrs{C_`ZJpZ%hEyxqUe>2I+z1p=4Ab=Qu|NafJ z0q7Y##sg)6%HF(r0}wzgtO5T@)0iK;`)?;ASEPspG8hn13kLxPlzjncs#>m28Q(v3~Lz}Rx>axXJA;$z_5^kVJ-v1YzBtu z3=ESQ82T9)x`8IGuWr3ueRz81-OBR2y`eK$V-dd$Rykr!e^E{6nO4D`9+<9^QF>5P-@DO>Xs=7z@%r#{f} zJmbmG$WU2%Hzww4MC6r_^aFw0v%N2PJDqm2Ib~yh!tCff!^3a254}-4_12~KGSKAB zWo5S?KK!1Xd_BPbqPOq4%kO*^KXaU4|J3IAdozQh2HJ?k|MiB%#N5b4KG%%d}?J? zf6CPKWK&andwZ>l%0YRr$&zcd1a=A-A2wph2C813mUi9K^PH2@87r%kCML)A^p0t2 zo>EpmAt$#_LUNC=>_+}tFYfK!`UmwHGJ)zBCMI4fE5CLB{^yX8^8o?pyuHu3xt(%y zI$>*j+|u%>sp(-Oqr>{KlXSM3F{A?3FRe$7v|a{=P6kj+G%_$$Gl0S&mjPt3BLgGD z|NsBzyO($YbulJ+ySpquuw5IJkl0H+eO=j~vGZ}MSPMIqcmRctdAc};a9n5H&EWDM zsG5n1iJ66!jh%y&i<^g+fsbE6P)JxrR7_k#QmS5BMwUTNUO`bwSw&S%T|-k#TSu2c zPEX&!(8yTL#MI2(!qUo`!N%6k-oeqy*~Qh(-NVz%o59D|&p#kAC^#fE%p*J^GK#?` zIwm$QJ|QtFIVC(bEj@!FGb=kMH!r`Su&B5st+cG1p`x;?x~8_SzM-+HxuvzOouQ+% ztGm9Zx37P~#7UEH?(ag;kei>9nO2Ei0|!XQ WOTl!uiH7Q23=E#GelF{r5}E*dZc%dp delta 547 zcmV+;0^I%P2jB#d83+OZ008-bnr@LH6O*_DB!2<&NklhJSzm{r&U*@8AD_{s7TGAo~3q#Qq&9`e*60 z-Y{{Q;@?^mDN?|=V){r~^ll=rtu5CcE} zF#**BRkyY(adR>Mc_H{~T5^XRV~oVFSgBv#s=t2z{grn0SJIVV|Ni~D^5u8)5(a<( z0%_pmV<{@S{pr)+Nt0E6|KyV7{&xHKuYdVZe*q2t1$I)V%&*fQf6aaR2k0b#00L@Y zNJ)_ZdXt6aUw;1WU%!4nf69|1{%eNjPqsh*e*OCiRQ>ngug(WR=luq100;mv0M7pd z002|=_6O|j`(IxeDJcoY#_zAGJ@f1c+sz_-Y;613A;THb!ua$2{`?RH59iY70)Ge? ztjp8V3?4uJ{`2R*A3y$rLgW`Pj95Uf`TY%KEX&tF2{Zp#9aP~`xwzsYKmY+l>DTYy ze|7bEGBYGgOYeZf92}f~{xJOh%?XYR_P<*z|32{e8@UANB!B<{+OU*?;Tuw9B1h~m lFbRS diff --git a/toxygen/smileys/default/cy.png b/toxygen/smileys/default/cy.png old mode 100755 new mode 100644 index 5b1ad6c07886e6963db439afff55d7056e3c5cd4..6e234ccb41648e795ad449f214c875a87cadc53e GIT binary patch delta 623 zcmZ3(yquP{m)O=e%!47ey!r$x6ik4-~RjY=$~6zzpsS-I_voJtkaLH?%y9z|MvC6 zmoHzgU%&qO^XEUmKK;I%^6Rq0&&&2dZbp57I_1mPuUD^L{r%_nk6%A`-{1J^+43JB zubz8yVCjvupFVxMeEIVCpWo+QUD|$f;LihB%r4mlf2zscp5hcCIdO_C7!;n?9bTwxKu2LmJ}~RgqBNWtV2=_%Sbf-In-7 zGkIC4Mp3=y1kI@tkIc#&6sBumjoN!LXKUwMFGuUUarFi=y`Ang96YDgF5j%#xTRBk zjT6JEYqQ_x)mtdJ$MGES`CPIuCcpfNMu%gf*fY8F;zkZC4oBs_?To2#*Lcz_f3|Rc zoy7kZf%@|fB1>)*`t<=Ft6Jh3QIe8al4_M)lnSI6{EQ5YjC2jmbq!5I3=OP|O|48! swG9lc3=H@$pPP)LBR4-KGp&*Vr-r(|GX05$>Rb#Ap00i_>zopr0BPhI;A4WVP82*P-nR{;Wu@%{VvyjHxt$PRn{_4)3{2i#?xggJzfH2_2D?%lfp z0mS(B?OQ%RK4fFCg)B$_h=Cda0tn=XckkXoV(||c;sTJs1PCwyl&D<=pf zH(hEeM4>PD*r_yt@&JGvcK{st>V^@Z7y&SI0MG{jUPY3(8LMCX>Nnz%Wds)w<`%YTuc^D1UElZEbGiwYjmevA(|Ua5&c1 z)>crRh+wF3>oRIatp?mh)vzhWIijtOTZnJ8av7A6~%IK}@n!VgS*bO8^reMJV_1pc@+U?J$Gg?ig%}l9?E@~Qki(>q`GhPT5zD6FGF48W}Ppq8u2HA5QQ^1ERvH@s#jDF~y$| z8s`@g9CI+5mJr+OD}L1e{LRy@wzhYVg&i-3gPA?vASv;0Vo@AL_yhg|cDA4(PryUD zS9rJp9ih`hk?7Fy$jopAjbNaRh+qUk5u~McJ^CM@@KV8fe%b$lG5Zk_4v_ykoW8^t Tl-9<3I|HP!Pb9r!ac=zq=t^~8 delta 414 zcmV;P0b%~q2HXRX83+OZ008-bnr@LH6O$wZB!2;JNkl-E`@+U6d-KI#14SN)p8x;B1e`(wFJ8O=2p}dP;Qs$-^@T4- zu78B6fGa~Y9%3Ot0I>iWfB*gG`ulU^m4AQ#{j<{&_>TiXGynt;%O9Y-fQJ45&;9r3 zx_^s*|NQ;upe=w)!=FEY00M~R*B`KN{{H{__dn;~pDWJ(72m#ENt6x9|M%w)kYxA^ zmizmcQ9ytpFApGqfHnXP1qlN6{QmpT_re;Pa~ppC`u+dcufM-R41d3VF@W^|NuX;$h=BnhfLIuQfvf3rPl&6JkF$@bm5;lHkC%;`o0Ws3nZ3QCy}iD>yVb5;8+Ytj zzir#v&6`(l*sy%<+Qq9@&0n@`_M%18=gploYgXU1X`K@%ws?BlG&MKZ)zwv1Rn?c4 zmgeW@XJ%w3B_%~iM~8%j`1<;~y1Lrg+V=L=r+M0(+Od9qUES=es_7*qlk@ZYGBP?7 z6Pu%>>w<$Te7p-?U2<$}Qq4@4c2@0kG5x!B&ELw(KgGqr^YVUWWc*A_{1FxPEhzYl zxA$ikmyb3!AB;`k>FR!NDO+i;zt>*>PyMEqzjO0`rKSB$NcbKZ`7J2uiQ1o z&RqL?{cX$je}7!`^I69CCy8GlMSXb?{OPXu$J?&&Z`!`SZvOg;{>zJ+FU~7HJ1hJC zLGJ7p{cQ{Nf4!ai^YyHsuc!QYIr01R?ypbVzdmXD@~HmvgQ`#W%0AvL{%|M%{q5}c zw=(Be>aXk5-`1_arB#1(hyI#+{Z;k)D|_`W%>&% z^(PnW|NsBrU;ecZFd;A|dAqyJf3bD)Tp)+N#M9T6{TVwSmnyeTL+MhW&^}KW#}JO| z$q5N5Nok3x$?c46e13jXws!kpSn)1dNu2oRokz& zW6PdRySD9H-???~=H0Vv`^PD)?mN$@^wt+iz6KL%!& zUw{7n`u*qszuycD{}^xpfB<6o{rh`yaXFCn4-EeP{qy%Pg!}^qzkmPv3#NYmUbk*F zKmf5YF);yEpE!95Bmoxw1q44q5DtF)_yNTJ0Zu@b00G1z%fKM;@1HDK^`BqAKstW@ z1b-r+uHQd?{QCI=sNwhbA3*&Qe}GP900=;09NYi^fzZgLpfaD*B|G)o(*g*2n|NlT8e?YeU`b@+0 zl>s1tSbn@^*fVd^KcF^dhM(U-G5nv8f$0asKVU#GGyMGpVz4qX{QJScz|8RN7Xv_m Z0RVS&h{+hOUswPD002ovPDHLkV1gag?BDl6vhwP3xrOrwzBn$ow3eQfnG`rq0tzTI%=&eQa6&tgi?xQ8z@?5bT6f1 zhENz_2WoUF}#Syj&f9KrzRiUDM&0bbvK?nc2z zfH<#uhem#9BKCnLw$&6<)-po!8no4fHh+O~j*!wr(X=XjeMNZ9-XKpG;Ov>_Y3F0H zSP+j5K8)G!($-(;&O7L0KXv{#_1#VK?9YVp%umra=aS_b4yxxgO^;8~-`}DwPSol| zC$CdY4$|C9oal}ow=J2h_?MR=O?DiVvvUE|<3v<*F7S1u3hx~xCi_I>!tN0sFj zAJ5Ocm@Au^{rh=4Ry8_#F{Q9`YD(1}2L(^WwJUG_{%_!Q26>U8R9j@w%XA0yT7w?M zm{^>KWv69GG#QvIQ!0~+H(;0y!(M&7z;yK=!2lKeO3TZR{6BEGCVz8F#`;76g?wjT JcW&{qe*k?Cu&n?9 delta 510 zcmVtUk zFfjc4^@ri#KZZYl7=Hcv_vg>QAAf)%|9<@fGk*PJV_^UYAjbdy|9|-@pHV|NsB{kKr$n@%KN73uffb`~na_EI<`J91LbE|M|EWPTgks{Nq0p0|Q9) z-+%vqz>5C-g{l_hVFa245I`UeKudt6h7^Mc@BgDW81BCO4-x~L`sXhc{R3+I%fRsK zA3y*x{R6rHsA1>M|6jifb2E4w{z{xB{(t=a3noG6Cl@D!zz%=_0z1jb=>Lls|LyI; z4*9p~!=|~PXFivI{rms#U;lpn{s$)i|5jpGV(>Zz5I`(ICouwz_4EYleDw3t^jFgl zd^`Y@MyUP|B7uSYm+24CNdN%^(!k8j4D|QjPkX1ln)33;%U_JYkjw_^1jYnV!)M<= z|AD?{00xA@mOzuzwkTF#rS*%d{8M zimw$jd|+SzhW{Th`UfU|ff@f`f(&4ig8?AG0DRM4BWbVY!~g&Q07*qoM6N<$g27Y+ Ao&W#< diff --git a/toxygen/smileys/default/dk.png b/toxygen/smileys/default/dk.png old mode 100755 new mode 100644 index e2993d3c59ae78855f777c158a6aae6c1fb5c843..72af9e3290085fb4bba497b5e526646ec18ca45f GIT binary patch delta 800 zcmZ{hTS!v@7{@;wWGI+9D@T-;&IhM-wz6X{3JT$ED(b^TlOSId zFZIwv)JwfYcOinPh=>SLSqeRr?tXJ;=bY`hn|6Hcp{I|J|M&ZU-=8nWavh%K9AN=~ zLY6C>JIi)fST41<0*qAyOxy-YGtxu=UEhsIl4aHTRPYub@WqI`b1wl=EKxBy5L&ER-ktBom? zJ2F{RB98F+As#PST^&)WRv4?{Vai}2ueN2g*)&1m3Pr3@wj<5G2gwg}B^cMMYRH z{{fK6WYVcr%9%>-?(WhwolGVZi3CMaBuP?FXbxbbb z4TU0-hEPpSu&OFhUhd;?yrrd{;^Kfn@Rd2yZpT%sm{dv!gQ3dGz?n0C4#&r4dyW=) z3JU}0&wl|ROlG31D{e60sPj4I40~Mx{WHY*;YNYPFi}CY#0o$FuF* zGGQLC*L||MwB*FGQT7q;GSg?H$*y>ozcKdwy}<`g85)s%u?>)C=sG zi0`TwE z?(rjw*r%gI03D==EIIJdzO==miF$DyOKv0KB>iG32ACU@0Qjw5{AW{T*_%tK= k2e{R1?Y58pA6TWDXBdEUutC>5*Lb=BK%>^Go++`(zlsHGhX4Qo delta 433 zcmV;i0Z#ta2JZuq83+OZ008-bnr@LH6O$(cB!2;cNkllaWpldLSzbbtUX z&3{1-06+`@K>h!ZNvLM`<}kPiIA3?K?Zl!VJuS0ABN12uI2v;s000mK68GQM4oDR3 z?|C6;zBc4LR82Q1eETXSa+3nD0Ad8%4|Ml`Fn}2U{~ypshW{9Vk%{T!hYtV&#KHiV zV*o?2zW>+&Bgm+K00G4EikX==E9>w5`+p!;{rbhVYuEpeAHVqe{rmh0;vR?xfU+z~ zN>9$60|+3n4Um8Xx)n%5Ec^A7;pb1NAW*{}VBr1*dKKsgfB*t{`v31gOp=mdWB!5y znVA_Ff(&22FhYX=&mX40VAX#?vH$_Z_!Jz4kcb2#vA1vkJbLuT&=4*NNe7TOL`SU01U{>n>)Wp3BF}q_Tc1F}Q1TthC z&P+R)mb5=9ZeQH4NfAFfLSE#BerrzJ(CX^6!O3==t;H&f;BCPS-VCV+(_S_C)SnG- zTJbVrZ}jtG^VM~h=Bvz&Rv7tj_GfTq`1_FI->3F}?{fdX&HDQ$?Pq83^wVw+e)-s3 z4K`c+q-=-H=R&iuj~u@|wEp@e{P}K%n)>Rp__&CusF1Kw{}%Lp4M3vEq=OA^zL^y|;L+Z+3c8XgYC={>NOcqAluoljP^;Dk;ual$|4M zx2oQb!H|KWoFV^kzTY-KugzYr8(kgNIjpQU|D135Ak64vwv)^p8Hrhv)+?vIsXH@96(C^RU{CwTMe*NmY&c$JkgY8ONs})uj%Pkl*85n#SUhZdj z`t&J-B?E&o1A{)p|NsBby}#)Xj5Ee0Z+Dl;51H%lZv*n!OFVsD*`KlVaj7!w4qBrN z6x!qI;uyklJvo7a$xSUyEX=Ho&rdH;j*TrY&aRHpQ87_4(6ErxQ*%MXmYy|p_B1V; zw5e;=tX*u(%JbDGtY5lz@#^LI2^lG85|Xmg@)9#sbCa{v-#>Wqxm#-H%aY{f$MMy?SN=!~rQdCx0TKxQk2{We5nKWzKyoocX&Ye7a`uTu>h>)0| zsIa)enRA0fV}nm0hzq}e;l`Camu_9Vck$-cyN4Od=7r?TXa8^MoSao%#gN3QyLeac zMJu33R7+eVN>UO_QmvAUQh^kMk%5tsu7SC(p-G6LfuohNsg;Sjwt<0_fx!%)`@2wd hgQu&X%Q~loCIA-$Z14a8 delta 559 zcmV+~0?_@S2kZop83+OZ008-bnr@LH6O(cSB!2<^NklsKr9fWpMHDx@84gb5Ku$Rm0!OP{3-p-_^Un5jp^|u z?SH>OJ-`3_mgbQL+5iwhOdtm^{QZlh+Tg>NcGo{qR#UR9ewx4glzHy^uRp(j{rmOj z?;oHBfB*n70M7pb{ow!s5r+W$=Kufw0RQ~_o$a1N^GPoTT-yiw&XGVBSbe$r0&xrf z|M~#~9P-zx0st`p&i?@b004Y@cH`sX`+xlW{QLWo8w3mk?PM#R`7vCXoc9I$`|}l z&&0&^{{4G^0AggQW+?V9e)##}@4ukXNICz*^xfyR&c70N{%&;to4xMGA#Ua-qksQE z4=C~}9%?@X5I{`800we?{rv^h@aymIiQ4~;axo?CW_tgUqkg+IL^V(f7_5JRp8UfA z5I`(IkN^JvTY^&p7(_swz_40l{oBugGcWzc@~-vEcKXW$l>)(kP)Gtb00a;tLoP~0 xLf9{h7{nbJo<4ml9U={tg(Lz71{i<<0{~5(ER4N4%!2>`002ovPDHLkV1nyn9iadK diff --git a/toxygen/smileys/default/do.png b/toxygen/smileys/default/do.png old mode 100755 new mode 100644 index 5a04932d87963bcb063497b1179cee12f407e18a..21342596412a2796848b2511a0ca532f92b9ebb4 GIT binary patch delta 849 zcmZ`%YfMrB82!u%J#EcZYp$Xdx-#uz3hPRd3fawkC5oveO3<_sX;N7$temyV)UwSV zmLs+G@{uDa`KYa051Zv?NvqZi1bL{Fd$~Nd`>$X9`nK;oXFKOR#}`;juUj=g5CG!2 z{zdicLevcD?tLi$r@{a#s{lOIsnQIfLI5lh0JITc<|g8?(lo3>|X%J06qbX0DJ%#0(c8>vrXU7roYjuuh)##Hh;cyYm{{ReIz+4li4K_ zTYkP(B$~*~RGU*%Uja0<>d#*Pc)IpOMa{74^82!jL%7T5CGd%a1T%NLiNi6nScc7; zO&Ioq5*XfcqeXwRX1Mg?kmADI8Pe;)XUtr#iNiKTMEnj58|U+%QLy!zTFqE#^}AyA zYp2WOb`mBw+lU~4C=8SNi@`APcztRBzu!MQJL~iLyk4)z<8iy)E|-fWNvG3E5Co3n z4u=CL2&2*X9bj6iw56q4_h1%*z`S#(DGqhv4#G)h9PUdwq}wg+;TLFmdEI$s0|4T$ z08=F;Rtz)qcqSAbkBK4e)+vWGu|OLq>daC00jT#+%Ht;sNcB z+-_9V$rg5GDRcl?_QE2YSZvM7u?Vxw>FK8AWOC9jIr&_y8q6*0&sO#c6}pU)UVzMr zPa|J`m;j^z1#@$Avgptf>JtG)$w~a0(8a&0kbqu}r3mQb^x%ce;C!{^+$L%_fbrvn z^T(tTn71oTz9O4_JUBJ#$Ql~$z>c#4@#_kgWw1&YU>l2Rv{n1JRRjr`vgKK^swF9r zG{x#|;-Kwk0u$FuRUw&MkI@z-i-RK091hKkJ55gtKeUo2;)qV1Semv)xwe2eSNWV# z{j=_4*Yu?~_sos@UbU<0<%ViQ!|1dYzxw`>zSmaO^o>jF@y%mA>$=z5s(sXdr~C4` zfv0zSItDeYhxc`DPud4A1lYL6?ZX|Dw_E!kTnmO@x=5>BF&#xYVU`PW$`2Nn%TQ^V zj2aM)L`O#=oJdxTkcFUIW6{{y2n0b9te7Gkl{EvYFi2fjv00G1TR5z){^gjc`|NjjC{{H{>@Bg2F|G3x~7+JVP zQ&a!{{{8>YA0YV;Oy1qJ2_S$#8h}b)z5DSOqWbSYAmi`<|1AIh{=R$n|F7TwfB*XT z>wgzeHM6WN&<2126ppbC05A-~q$vMCOd&N)3^rn&3WaiZo>@dBxpL5=L>h@#UjVT% z{Q39iA58Ths0J3s|NohoLF&Lt!8(8c18V>XAjZFc|1vT%{s#lF^Kb%2CZ>-cJ^%y| z<6Q;@;r#qR4;}z*|Nr|B$h_ab1b6QI%YVcYQ+fnw+wVVr!Q?*~aqgvk3jhL$3Fwdi zP)q-SJO@NSf#}CCu-kyDe}h&3`2%zcC~N@&hy|np5^x~1e?#02_A)Sppk@OV{{e*x zNCQ9sfi?X8!z3xm2nuqb$A1GEj7-c-jEs_ETz^5K3<`;V|3D-#>Hq?W@hWOWLMKSI os~10TiK#2rAPGYl3;+QJ0K~F)-%m+9R{#J207*qoM6N<$f>3MD$^ZZW diff --git a/toxygen/smileys/default/dz.png b/toxygen/smileys/default/dz.png old mode 100755 new mode 100644 index 335c2391d39090d6b40a409870a74326665589c2..f49fb5839c1b846d0fd882bdd4026924a6d4cb96 GIT binary patch delta 838 zcmZ{fTTGGx9L4`cWkj{;qLnYXW;M7Iz+y485RJN_Ws5< zM64BUW~Stj0V=ivh^qktK_s345F!9}8UV2wAWYEuJbNzyrX?$bLW(FS6H1ftCHt`4 zL^u|WWhabU%r=|NYPDJ{7PHxGGMS7<<4XQMWq+rlzq9ex-TIfeYx{0q>{Uzp+Wfx) zzk>StGY|;O&(DkI=H_N+XGcax6#e(kJ!&fNQV8$M`S)Ze+AmyIirqs)-pNU~+dVx! z?Q}Y)rly96hY9=xXs~lvCTpoOohh-Ea>nISufgD+{HvLn8NFVQE8bK0qWx5`)=^v3 zQS-TyKVHh=sLt9)4CAtDkJIUJIQ)M9plEOq!`>}@*2e3Q9&f+EXsfacIDI9w%$s8K z(VWpEWMg)+%jNQVy*iyv(tDkIOG3XPE>xe>^61~FS(FCB#HWw8O4RRi`FuW)$D`G1 zAwNRYeT}Xb7pN*IO+ulhNMDd;qNZ8UlYWiHXSZuK8aNzQ(WS^$iqMP(9;vQqzl2sM z$eZeW=9`#+LpZdvo_9xj{Fa2F78j~2sY;a6TH)|{1_lNood5{{F#uZt!T|^XO91fj z>fM`#g@q3?b&vptk$xz-rkE325DXz`Dwz~w3t6>15?PE)brYb@@CqZofAlkKk4?Xt-6lmH7G^nY8W_ zHMhQ&o05`|mYGUU|0H_2cVh5fh_YAI|dx??j@cBPffGvWd(xcF-UWiHnOt5@TZH zDX|D~cLFg!F&aUL2-2MK?fJihNMZ?}Rm3U(|KbOMyR;#8+j4+ZQf6}3L0Z)x%}kVz delta 520 zcmV+j0{8ud2gU@D83+OZ008-bnr@LH6O&d0B!2_=Odh`e&fLK_VSpGBofBEShNa^1{zyAIC{ri{l$6tS>ZvK;$ z{C~&F3N#g>nv07INCE^93jWH3lKe}28-VENu!{qxMpmw(A&OiWD7%*>38j1aE_1Q65zUq65@`T6Jf z&)*tP8K=}V(zkrH>P6Fxyl7IjH1qdLJ2B7PI0-f~x*N5C)J02rvLJ=S|6+us5Ut0000< KMNUMnLSTY?ZV1Bw diff --git a/toxygen/smileys/default/ec.png b/toxygen/smileys/default/ec.png old mode 100755 new mode 100644 index 0caa0b1e785295d003869330fc4e073dce07e7f6..d6b42d6eb0970c1172faf978e78945292d6b020f GIT binary patch delta 890 zcmZ{hYfO^|7=>R(mKvDLfGK5@#LGG|wDPsJ0lRuBP=V2!QrZbjV(Enj0dcj66q|x* z@PbM~v{SZBKx1?>PTi&m5_Dcxbcm%ELHK%smI58xE4_*J&;IS#$$3wn$)60rRcu$>Ofw)$t9{nJf8 z>v8YY(RoY5yt&>psqjqHxQ*2cm!Wd8jePt1)nIM!w|^MCBeib5mW{aaXSRxkvU>*WPotB&t7lwcaRE zUK5?T$~$(Yu;EgE{Y6e~XIaZN06O41_ID2NIQdUO;}h-?b4IEek| zD0J`0a}{tMT$Y`cZD(wGlacy5ZNuE=_5V`IwiKd`oHR>XGYDV+7y}pucnP2b&;n=x zo&xj(JOX$C5NfKp53sPX&{;)dg_aNFXJ@3JRiWbOkikf^m~@O8^Kk_3!~T@K_Ry&} zE1kw!q!aLXLZOgfTqF`$^bY z>JOx)-oLkMWkh~fXA z|NsAh$bUe=KOb&%0|XEY0|OBKXZZ6PrUE1ZLjS-@fh5E4|NnsK4%s(0a|A0FHhM?*o#se9QEZ;b|7ytr@MWm#z zEz$bb`!9d~{{Q>$@1MW_!07MqKOpw+zkh)g(B$8L|49h*Ov!x=5I`*NZ%IAyqRr+BnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34fiW_`C&ZP(c|U{Weg^Bk4CcETjCV5VZe>v4%%HfDL2@mF@Ja^W zr3|c#85rhQZ+KF8#DWM#EhH4({A`pzFyD3 zFgt$!{g}D;B4^(XnQ=RC>Mfs1H{AQLJ9S^R?YLsza>=mqf=m>0 zwY8P0sj0rczLu7jii(P)q@;+5$kV4!|NQy$`}gl(zkdDv`SZt*AK$-!|Mu-M`0?Y%A3uIPc<|uEhYvq}`n-St{`>du-@bkOCJ+&6 zk1tFB3T^UqaSY+Oo}9qIHXIIDQsF)}iXjsVUskxwGOV64) zdzuza+SIjb)-EDk%oH7XjK z$ei={>he$XU_8aldO&IA$B&sjFC}M2-n86V`IEJ^mGfj~CPP_(X32*Wtc*bCtCqM% zl%ynUG$Ys>cJ%IbD>>fFI>0)5I`*dfL;bGC8z-$^#1??2&4h7 z8Xkc_!x&HikozB^0U&@t8Xi1&0I~-Rfu8vX1`r0sqyJz4$OZ^70Mssi?0DOmRsaA1 M07*qoM6N<$f`HhcB>(^b diff --git a/toxygen/smileys/default/eg.png b/toxygen/smileys/default/eg.png old mode 100755 new mode 100644 index 8a3f7a10b5757b006948ea4436fb242d02dc9a4e..50e4c7e940fc7d5cdd0db3eba8354d61a5aef054 GIT binary patch delta 800 zcmZ{g>rYYv7{*^~OQ%gM%5rP2W;I=?2LhQVFF~M@mr#$2g?Z$13@3$j)Pw>m+h}Y~ z&9V=DaLo^`H6ycT+WH4=+m?3efE3bchCE+Q`@?dVzvSR zHzyp|eUN52AUjoF0-!wtVC(=0L?YuNKnnu!69+)=1MF2l7%j^Ih`J^!#sp4)Wq>6B z2Y?;G2Jju=E5K)fPXMz3ex1(O-tKE_^OB@T-RxGWD5a88C|o$cE|IKOS34^zoTa6c z06v}8;I6B4OC(fH4TWK@vaz8YmsYpd(z4pz>{O{%luD;wKMfELhc`Ah!l7^|6bb}>Q}#aU zo6i5s?f3hAKA+d?^>{oEgToOF20gZ$tJd4=Ur3j=&Fyx3yk5KA9t;Hofq?smVQJ2^ z`0?i27Y#*GHk&OoGqb67?%2I6apxWbtq;e0dwcG6 z-@Q{U!pf`4Dk~b^y_@Qq!u5iWQ5`jg7w2 zUD@*cyftroV&cr3LH$P5b?ynh<#No)3*sw$IYt&YNv`sw<(k-&r=^W&wFN{~Yw5-F zdTy$y-|}c=VED=FhlZiYqeq4lQ;pkzIYF;X{{d8&3eiRjMR9}6H9l=268 G_>Din23!FE delta 402 zcmV;D0d4-&2GIkM83+OZ008-bnr@LH6O$$bB!2;8NkllKv9q$Ao=?@NZl_Whl ze}DdFw*AJmpY`AGUqG{&{(vlG6cuILz8xTd82|qL%gD$GvgiMQ1~B~h@9&S-^B8_V zVU?(0_|Nhm27t;wefk6tKui$tLdC%Jua{kaet!D*_vf!?E&q`L(9{3^0R#{enrblk z^ZU>LAHVtk{r`=k{y)(2e*gi*sIRYISbJFb`t@s|+x~#T@85q?0$%~OOZ2$oTa(!A_+P!=CeuKd;Fn};XB$x#>1gJ$tMdjkfivR%x3Ra+zl9G}@ wDWE9G0x$qu^&4X6Ul;%?1_&UM5kPRkm4;{4ZQ5E6V9o|ObOeB;T8A6}CKiB81AtxxShlylUr_>(&|6tn zC8mJ80631fw6vJb=BB15lgZT3u-9lb*4NkT^?FQ$)z;S5s8quh74HAYgF2lK!!Wg4 zU0q$RR4Nsf3c0*oDwPU_LY&X%^NNZ-FqsjV%n4w#*?c~q$K#opm~gw@E|<&cbUGXk zf*{5SVsvEW1A`ID&JK%2;{btJV0wCbic)+k9*<8xeM(v^vDVgTG#Uv6LUejCD=Q=r z_+q}vSFcE|Ha0jIJ#Ziri3C@!3}j>kdAu3=00a01Knb7#kONSk z5diQ2xB&S8xd7_`=m1RU1DKhaIc=3bqKpYzxm09XFn^$pN|NwKS-CigNJ>p!i|WLp zVXD?E6O~rY9jw!AoO2RE%F3QIZta>_-d_8qOIX34wa#Z{m z{f2&Q$$8~F?tN2eK60x31@pGiv*L<+i2tZ%Dh)a{xg9(2zj;orKyMVQ8g*Es9U;qdp79dq$ zUk@bz{rmS941kP3e}4b@17iRF{re9X0)<+>f9Kt}5g>qAz&igwefkV2{Ra+y|M>;f z0wloT->+YDzkQRGk@>{H01!Yd_wU~q5)uMRg7l#RpjH2Yg1>)%@%EPe^$W=O$G`v( zKz}SEQX<@}+z{0aVEF&cng1tG{{Qgde|b4j(L+m1*`Ggw3`hKmn*~pz%QT=MPXRKmaj;?1ifa0xhloNlE|LuK&--1mwx?-uoV+ z`qwW8u#*4+i1F_GyFwg7fByafIr{%Uh<{F?DIl9c@c;kcznFC_1^)bDR8o3#?i@e> zu_!kvrv;?l`*`m+Fbx0w{sjz?zrYas4I=;k2Kn^=um4TI&zAlb{`(i`cz^(60UGlA z{~t*nNuczfe}DggwE@wee}6zEFsT3k`TOs0(2TzuJO2Wm1Q0-s4A~3}-xwHvfhW-) o7z3I82aJGfIT&_cW&j8<0J$Jf4V^lKbpQYW07*qoM6N<$f|8Nfga7~l diff --git a/toxygen/smileys/default/england.png b/toxygen/smileys/default/england.png old mode 100755 new mode 100644 index 3a7311d5617df952329b1e293fdfddc64e95ca72..0cd6e96e8f7c47516c56d984cbb96840e618a01d GIT binary patch delta 727 zcmZ{gTS$`u7=}O5)JzOA%q;OhmU+nEWlD2+n2tHra$~bl1COB>nz`9*P*YP8hN+q6 zg(jj=l8B)fnb@EMRCA3#ncK9fbIm!|wEa7{qxRo+(RcH`FE0<@%lF4~z`N(PuO|Rx zlHA97BLey16*-K20N#0k<~9Ha9yM3 z8qGYw9Dr_hRj1QwwOWlvqgJa`D%JY>`r+Z>!NGycX4`CN*r=;>S}abd)8TN~?RJ~Z zw!goxR4UzWx6^9nuP_+${CqoxS*=!uLgBDjWYip+$z;KWOop;pd$Y5@$H#X@M|C43 zx}hO$fB!O-DkYQE0>P$0u+iDM*50mcX;Jm{y#x>fY;w6ji;7lRg)4NrJTGsFL_$+i zBm{yuE>093{Vg&QC6l`VzNV)eCMLG|0|Q%6`?h*|esy(ic62PIrlQHoYdoHk$6Muc z6>PS=zFyhZHVj}e7+fybzR@T_5cBS?*=#nMOc;hqBoe(|Z#5dlF)^54FA|BQ_*E*^ zWH4ZuNhT6K1Ly@ng+dfXC1UaG#mQLw$sXmo)NKD2{tUeWcRn0iKE-FtC%`YtGqTu? zBQQ|bC-+Hvw*@9tI)$6Ke{XcJ*rT=s^ka!5-T6hC~?RC$85X?Hs9d9_z_@P@i%?ACCC zEdD{3+_pZp+pX0vI1zu-k+9UT;G;J#&f?{xIFy^5$}&zBv7(_0UqC@zW-kWnY(nhxHs{F9{`PVEqjVw+WHsmSZ+B0 delta 434 zcmV;j0Zsmv2Ji!r83+OZ008-bnr@LH6O-ryB!2;dNklv>|NQv_5I`(wsv#s$_5WYLfl3&Vj0ZdT zAAdjqu{=mgk&BQ2`{4u7JfPVOfB*jb`SaheU;lsqem!FbP!&)dB=zSHi;&QT++2VF zVtKcEwV;yHug8!7|Nis;&u@@RfW`yKU%%e1S@RFf0Q%?uuV3s63eOlA00M}G%if;b z$cRl|{@=g941Yj|0d4rWbt_OQuZs)R27jQ2zkgXpLV!Z diff --git a/toxygen/smileys/default/er.png b/toxygen/smileys/default/er.png old mode 100755 new mode 100644 index 13065ae99ccace42df97be8b594049f9f40dcc4f..4d302a63ff0d5bcd09a72b2a83774b07e9b926c6 GIT binary patch delta 934 zcmZ{geNfW{9LAre1VWN!MPfB62FBZdgTY`7CTjyW*kmwXmXp1(;no`O@6-JdB?No{ z6ETFs-tO?Ykc%QBOZ4zGvFnaA?26+IV`RhKbQ?IXqy5_-{q?=?_j#VX@8>z;v*_Ek z!*3ISG|oQ(-67k_T_u^hVxTDuXg&&fyi)TrP)h(0Q~>)N5LD6st&{^$Tshe?!L=<= z#v{N8v;*BhC(sIzfElO4wQLZO~oS zsjsL=#rKM$?+WuK@+9LDNMQwB=7Y`y8V^~#UcJxWQ23)hx3^6q8_DF2@lwW8SeICg zVMf$YlyFQ4G2C5Eo?FFFewII(D)7umJzt4k8KV1{{BP8$hb@W1^Rbabkzp6Z_+RoN z#Gl@8=IUHsa`%P&wHs2qT)ax&&af-_wq)+(gd_*S{5?MA;@;h56(RY9TyU{IU+pj! ztX-9^UeC6rXS%0&_WG3Ngd}TRoHaW7ZyL=>r85m3@ z?q8B^`&oa{=?^0!Y@wmQg~yDt6wRWZ^14~z<~`u?Dbw05Qsf*-ci-IOoM1can07jS zfzFypRk!8#7HMZv%B)P2O>Xg)GPM9WN)2|gz*Wm#tz|E9In$itvju0=#-Gxwmy@d> za*S4v{$7%Hq0qbtTz087E6S2tt!21s$kH)yI%(4%U(p?VplMxJwJa&fCHbNI1x<^j ztnm+EVid?n8U+K`dIvu9z#ZT=Fb!M-egGynHa32h_lwcx#zdP@Zw8B zFYgH6`O2%Wk)ZAhdHs!0T3Gn*HzOjWqGLd%6O7n6W_$uGF^QebNkJ-iPa5wnz92m# zQz**H7K19;ny0Dbeam59kf=81b&Dth}PKs=B7OuD+qM z$&7=CEM)WHmLo@7-)lS8-qDHF<0np@>gpy>zkjCZZ11@b`f$E~;KPpwFBm`m#QbUJ zXMKH+;lvfGetgc$M5_@L)o9f h-hTq7a;>4X@qYt81XUhn?`DMi144m>{{>Ig@-G|aWeorT delta 592 zcmXw$-%C?*9ELxhZD*sFBlBYZNRcuxM4Bx#(Ku$Bm=QvPij-;IBoT#O6v4imK^bZ$ z20}u1l@~#w5Mp3wGD}R;^uq9@j%lVTxh&_|Ip1F?;UDnwzR&Y6np;dyVk`mxXzph> z+L8{mzcn02LSj+=>C!t&P0rYeqwryMON_59OB5r;2UU(ubMqIw)d~?q;pZMt_q+9M zr_Z@AWq#mu(HJ-LiK-tGVhxkBU*?_>8M!zfxHf&n4K>=J*Zc8z>(3=;@+0X}2UmJn zKMklAnshJ2HE@-NIs@t(Np`Ow)$i|}?aeI)q0v;^v?5KrC0>Cz4|jCpzQIvRX=i!@ zsW135Q8eREsYTKP4s~v*K`wh@L4b4^=SBu#DkOS8j@VCx6tdMqEVp&tPA#)IdmU#@ zkTnw-kEKINc-etrYd5x|0*RD-qvkTg8K%Do-^5_Y!nue(&5nVnV_dU|Fx)eyJX+GUkpJxtqZwb}E~E|v&j#9BBiVLli)6PY?yd^~8h zVHRN|G@dd>!^8@d87LJf<5>ViXuNgE;ENK6GpygaP~{09022k{YhRFy#S6T{9M0bh7{g z01vR+tPx(UUc{{@_2-XM#1fl)YV0tPM#pbHXCQPhRkNRq-1K2 zY({(b_>j$pDk=nvMKGBnMMb<`AJ%BNy!?<-$x5Zc%uE)?e~7dI2ze2*Ayh&CvQ7<| zL$rbC_2ICF+qCXJ?3 z8KwZCgMv>mGw(WQYR%Jq{p72T=@AW0ucW9Hn%O))E?(ImiR|D4gZ^`-X|sv>Ir_)H zjb*cg!SxKYhN6;5GJ&RZ$Hsu?Arh(QdHl`$dsE$pZ+EwMy}a@LS3XHDp(xq#FhpFg zuvQx`DBx78P)-iJVM8!IomuT$O|Mu%B`1^d@qxvQ0``bC%MO z(a0JMY++$gqha#%=}ns`g@TmH$jr>I0FwaY0Am0nVo0;I`i0~x@r6;<2EF$5URUBS zv5EHJ#k%N;=tVJUF}qLc<%qvVKs$F)cW71zk_(w-4M#jgImLx1o zT)I5ImS}6cbm2<-<%?FMWoMbG)M5Peq5tDx??9gt%aP?u6%Ooa$Kyv`oloT0V`Wo= zXI{+4m(N~2Z#i-zR5i5ab=*WNiaM@-yXuyGq-trqsW(pA*%(-O2Ja?Xx7Vjg4*Hr4 zU1d!r`>I;ic2gssE-_Sib6&^l8RSooaXC46r*1+)p|C6Ucr6AS6 ze?v$p`UNC^{r~j~$Yzw41=;`*Kr9S@8U8Z@-SroUzJ2)j{lkA=v0u#WfBygf{r~?j zFnFgM06>5N00~-0yuXXJv;Y7A07*qoM6N<$f{ygO ALjV8( diff --git a/toxygen/smileys/default/et.png b/toxygen/smileys/default/et.png old mode 100755 new mode 100644 index 2e893fa056c3d27448b6b9b6579486439ac6e490..ebc5f347d33f7a5dc4e0a78629ee3433ecffb3d7 GIT binary patch delta 909 zcmZ{hT}+b)6o%jWS4KBb3v-oCVT!l`($-ewD_z&p0<{!NDYLLLDW%ndNQIA9ent_H zDKeoIYC&aLa2JMP8c3E6wp}dCGG!OrGGj91%wT|SSq%LF`v?1Ocd@IJ_nh}RZ%)on zTjuD4teDLJ#JN$1BZX;CHWaFrK97B+*F4^B{Ip9y zX4ld@)Fx7Tqj2_=-eIUu6E6kwPd(aJ<_jq^+~Rngx&gVyPngg z%dno1_cg$Xf!P7;q^)s+wA`_nZ(D3L^Q~V$waqToP5-VQt|%E&7de!LSLOO!)ks7% ziuHBt`non6)kGtz!5@BY|7LmSk#lrz>a6|qKRdpfTU|{L1XBI}9qS4Yw z3MLe z#xvja#eJWT<@F}`{YMrS5C$SWwPCY7Y?OucB7cR*Hu3QE)Plh?UpH?5`MD(3CuVtb z6TC;a`^vaafFK2!Od+W>BoYM);suWvIK=reUw2PZzOrYZkHzvbnJe*eD-4D=D{Bo1 z>#IYRl|ijGs407)QmrWzYx()B5=lTP^ylRG_&?h`3{sI05?9YH@ zU=dgV{s1;MHVA`5Onrn_ttgO=eKIx9pah+iD`oU0dR**&0aHR2QmyahQn4oHA#E1Y z4a!ZUZQ8tL>o$7KOR92C8`p2X@REfy%P{< zmE{%h>ncAuS*6ie8){%QojPrp)pow6wXOZb4twW?u5J=N7kfXt zbh+*1e@(HdNT7LpuVBTY-;&`;MtB-+Z>#{s(0I-jq85Ab?mHey}n8W_bv7 z|NkFAvw@aK@N+YWG5`bsF#yj00sZ{|0ReUZ0OJ4u`~d&_lgxzd_7grGtKje;)ax=3 z2IqJ>VE_O6|Nr{|0Uz@6!2*a0?AgCSK7R-?{Qmlj4Xj%6&%b|8@=M=*7XYgUBL<)t z13&t|0Tt9W6SMGPS*Eu4GjN(12q5y05Jg0{{i~|00D$* z|K;BR{QUp?`uh0*gHf3A2MqiE{|NK%gr8Id|MdU+`vCm>0UaXjtIh(5JbmRxZpvGhP( z;hvcMo#7cyl)Sm779%q@6^Z^+d2+SB3b`@+-rgrw{UN!c2hxS_UT){_er zRkc$+L+82$Pjd;F=;YV$9ktlZF4sF~a#-5#(3D*vDcggSw>-H}^(h=e!J)ii+?>4J{T(G; zTpej0oxR=N{vKPtdwP|4`FZ*#KkzX>&GD7*^qEuVE}lGlT0h}|mX4O5o~Ew$`Uxx7 ztXj7+^}(}e>sIGyRqPJyB7ei-Ez@ L)z4*}Q$iB}zr0Sm delta 430 zcmV;f0a5;j2j2sb83+OZ008-bnr@R-11En0fk{L`RCwAfQ9VloK@i=G#~Cpi1uG%= zg()PM%3ok52wK@&Bq9is$|9u*78Zh_MDZsG7IyvvkpvMmL}O(kQNbhzlHAUWZ}uGS zcJJQKn|W{6TiYmkCMLpSh&3!T2@{JYvLfUVOA*NA8Dsm6(W;G)h@VnU3>oD8jvh6e3K zPIm7e>SNnS*-9;OQG787(bx0zJf<>KD8U1oWM;m1j+b1Lcw%w~bJ=u9977>vV(JDC z#YT2>tIw_kyAl@0^2;|-G6F>3dDnkz1zAehv-oxj$tCgm?Fx>f1miq}ut_%5hx(P* zk<-FGhw2_pE{pYYHLjHmLo!wnFclr2uv^LI4>Q?xv$XfPIN#dbPiYsFhn@jQ5+B{W zo!>-q)UY~ZY$UahWZ`TQVuzFXC7qz<{9 diff --git a/toxygen/smileys/default/fam.png b/toxygen/smileys/default/fam.png old mode 100755 new mode 100644 index cf50c759eb28b5962720aa1ce0617a29003e477d..e2cdcb72c0967d02fb320fa105b6f164c4e6bd4a GIT binary patch delta 909 zcmZ{iYgCg37{?#7-g!A$9F7sElV*j>W9T+8bRf3LfSp}v45o!~>D?G>!NP4$81YVo zN#?OA>rg3acE5PK*)i%wNY=P%Hj{xHOv(kC5!>Fqd#`JL>_eZP^Zft6|A*&)p2N3} z@LtMTya)g!`Ruap)oIv$Xi1q0pk)n!s{?>aTCQ0Dy8z%?4S=`@ApO}ReVTlLl;C5^ zav2OJ;B1724J@o>A(MtxB%~9NdLjbHZ@}>dI64on&%@qO!WBwjA+C9r<67Qj9Vb}Z zF~-u%nBSz0JwEDrAK7^+z8AYlw7q&A>Vg~^0-1m(4}3Jyn`%sosT!3 zBTS<)gO_M|@hV_fIdqzV?_OEOh1g*T!`()t^f?M&Of;7 z09P4c%cfaIdw?!-QSyD{HY{G?BsT9EiJ`-IwFistJda!LmjU|OJu_UTpVj#pWq_eo z`>97;iA|1Jwsi=v@=ESpe2(ODQ%1ASczOm^da-vn;2F$5H1~x)SG?-I7K#0 zGfE#-G*0FD$asDWvEC9B?!q5#jPlKq`;BM9nt=s%|AN93HWo6zzn%GbV4xWmr_wIvja=C|vIeqwqNnpGM%r1$ciRKAyV)a2;R{;7>@_3Gh3B zX&g*mKreuH2oi~e$ICBGK0?Y39xG`a9>a+Qo1;w@kLU|yQkR+5wk<%N>+ z#p7rV+)}ir?k?RUyO*oiH?BmGs)Du@$?Dovjj(Bne60yV3Njww+cTBs~{r1euzkU%uF#OTAHzxZi zcai(O&})~zjvYF8^6%cM@7J`~1qZ!%8v0m>c$1^LTt~jT*v*Jus@}Nwc zY==!>Ycq(^U54ZUkw7HM669tHbIOGRarQ=WPVPE^Kr9gSD5v}W6KJsLO?A!xADBxu SBHcp%QUJM3U3gMj)BZ0_5xblK delta 470 zcmV;{0V)2=2b2Vm83+OZ008-bnr@LH6O)|-B!2;>NklOTf}FCMD80)oDIYC+&q4vMR0s0&DpH|L=c>-#`S^3sJ`mRPz_88i@Y=|MmYr z*an6_AO0`^1P}|uzkh%JGXi;k8UFtPIt#4m|G)nb@R%209T*1JE>}LxEO-Gynt; z)JZ^3{RRdn$R)r3{{-3qbK7r7_<)=Yv;;^21P~~A|NLVH8vpA*!!Jg#)8S46`};4* zZjcB51NHo400HjxI1_mi127V4kW@d)pe}N$eL_p4ehJQfS3_lG2 zn3xy<0*H}e<5zGT{$l`X|MfOh{2x#T3AJ4Z#yf}sRPx~m13-WQ0AagjsxN}!%K!iX M07*qoM6N<$g4EvAq5uE@ diff --git a/toxygen/smileys/default/fi.png b/toxygen/smileys/default/fi.png old mode 100755 new mode 100644 index 14ec091b802cf24ebd9f8825f81cd2f6e360b46d..0c0af9408f9fddeaab7844c822e12f0af2571b94 GIT binary patch delta 800 zcmZ{hYfMrB7>2)e%T}#SC2_0G%4*G6N0eG*nFTA#ODbBnF2t=>I-Agqbf9EbTB2!} zRcmF=Kh;*&ni_NDTC*fCKqmyeorsKB13X*~9PRY8AH9Fxeebtt``&$1k(Rldgn5ww zkeN;{Xj~DiOcd?T&jwJe0Z?56AgGn<2f!Hw;HLn9eFq@^^!={9bbvXEeOY|2-RZSE zaoC`Rb=|`~1_mZ<-R0>pqzwRae_?FS$8(LSqxvSr68ji`8g>@=NYT)1LDNj%;Xq z*#NslU!*buG(fVpsuh#munFs|N3M(%D=(P~tI)hkQ+Eo5`QX;l&zny6GbNw) zRSW~v!n#h?<4M8QkwO)kTVcvp7O{jB335m8@`Vi2IlGWI(d3j2eE zFF^2BDE{|E-0Pk8c)V`6r&DLJVJ@zG2*;_=>+yKpZugrWrQSB-lwKbZUi*6F@>hU| z0CxfIz3D-F`_P^brk-9yckfWAZs4_6-~K{pHP$6jADScFyNk1!-UViAh?4WNxl!Mv z7R^uEOlHxZh zTGQB~K5cJ{qsO&0Hy83vMpoMES7+q-bi%@g*Xy&5S^9p)`e#o|OI-9{%a=X4fB2Bi zx;0(T+LYx=? delta 427 zcmV;c0aX6m2I&Kk83+OZ008-bnr@LH6O%0iB!2;WNklM{c^4u(Jf7#RLR!5=XB;nNp@0AgWcV2}{u0V@Ch|9@+l#=rmn{{CgqP!a%g zA%e)@+MQnj0mSkTY&b;v*_)q0Q~&<`_a7TzWMusH=N~`-vHSz7_y;xY_g^6U-=9DK zfqx{>1Z33=|Nk+7H2?$y(;z&PFFXXRA5t=4x{1SIibD) zVqy6A2V^D4P_SySA|L?j2ip1XFA)9%V}FnWV5k8E5aaFp@8x88|A8GH*KrqUDiE!$ zSNVtPbD$@Io<4s50YCt;FtPk&W(0D80t^tFkA#i8>8kc203M%J^#Y)T05H!7pj-n8Eo}cOGY-Jdnvu?;K9p2c z=PCg5k&xKa_;>(LEF-M#%BI_{_F?sl(PHT z(1SU{?>AMCss@+q^vlRey;nZsIX;XP54j6;D9{%Sc)aDZVxhj)EGstUi{~ok&&%Y0 z%E|?5uR!bJ_hUSbJF5p}bh+}hD5T8QZlV3H8?kk$W~ysUry6Ws>Zgt8dny#!Di60G zdo0R(!aHcuatutCK`Mg;iy9@rA41v_OR^wREK7gb;Hf*M#oLAMVoUnHF@6Z zn3!<_*$mYyXCJH->6Rq}Uh$7!QJ;t3i}AYMS)C|d?Hc44PNq_Si{1I_uDD?dA_t-g zD!h%O%Mz_ur19{3Fs?7EMj0wsy2|;pacTY^hh@?Ji_rm5MkvL^{T`vl!|ihK>plB@s^|MzTH3}H;mX=YCA+2L5d=JZ|N|0YzUJMy7LFnqjykYpNoIn$?IL>bUWCc z4yM|k@x2{^t*0lBD8{ z$cKCcC!^7+xLI6qV3^-HyZx?D8lxp8umYa?-G!u8U;3pr{x83R3wSj!XzgpkufrQ} zz7_KJJMV_RhhMk;{S6;%giV`2-0~3;7QXf4h)=eC8u?ii5QwDh(J`NsckHCZ#>IcJ z3%*Q9+?_;CPD!PGm6lH5vllWl8B7*?U)Fxk0WR-g_95UOJ|Yl`a&nJ~^YXuz6iA`4 zs91PRR&uoCZY(6MLqx$fkcWVkRyrFEFyuj uJ%$p!V;g}$ArK1uHO&7MBvPR`r~Lm58#$p?XeNaR0??>?l5QvRYyJhNXr})F delta 549 zcmV+=0^0rE2jT>f83+OZ008-bnr@LH6O*q4B!2<)Nkl2C05Lr?a%1G+Sb3M_-f!*)-&mJ@lxC7w zeD@!u;s2li|9<}wjr{Zf&o8mqKR`Cn4}Smw05Jg0{{$2&D75Ge#R4Yy85;rq{l4A< z;Uy|AHT?VU|Ni~|0E3VP4*%#^Q5C1H05SpqRqz6ck)iQ#w==`D&k&z6KwJ#;;_rX| zzy1H8y|YU8#nr!`zx-!meywNcawHQVfLMV3`1_v`90&}5z@GaJ4ik`*829nDYkCgk57yYhu#1L5DU=EKYy9QM#40}LIxP7)d&B3DgAf;{d>X1|7Tw^0D~DI zfLMU>@|%H4keA^v#7X~w+JJ0Eu=7CNvo9D<=I#0S^8ep|48Z6E2p~p=%&#DsUtk1^ nR|W+49})u;*$hAc5MTfRI7W=Skq!^C00000NkvXXu0mjf=GYVV diff --git a/toxygen/smileys/default/fk.png b/toxygen/smileys/default/fk.png old mode 100755 new mode 100644 index ceaeb27decb3f138ab5b385491c092557b79da92..0b2c8e1f550f8e6f5b7cb7378562ab1a9076bfc5 GIT binary patch delta 940 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 diff --git a/toxygen/smileys/default/fo.png b/toxygen/smileys/default/fo.png old mode 100755 new mode 100644 index cbceb809eb9b96d5d8ae231a53c4f4a98f0fcba9..b48a3f905d410563211d243cc080f99a445f269d GIT binary patch delta 717 zcmcb`+`~3Ol7pFnfx*${FZV=6;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z-STR6XN>$^JfOZ+qZ%0*l)gn|NiaUx36Ene);m{^XJc>K7IQ5 z@#BXNAO8RU|KGoV(`q%Z!+-TE}QW_qUlXY-K&7=moMYI z-AZ0K7Cf`gerlHf*dY0lcH9Hi$a@N*ckOc@GB9kt$-wZgvG`SL@XI9s7qQ;YBi){b zI6U>Ye(YuT$kpJ1gZ4dZ)w^c$w+*Ep_*-uV`k|irdj90M{#7qM%U`$_JvU8zte^Bq zEAD|xT!~21zGwi*7{rc6bS1(__eDVBw-6sX#fntnF-tI0u zjhh3LLH4tkc>21sKV#?PQn40xD)9h{t?_hm4B@!0+7~Kz$U($eKB#8GwjH~HAbR7Y zZU1Er9RB6`O%i6%;hTP3oue^8Wts(xAql_7x#8#jE zm)?GoQ*Zn6*Pq#&78a;Tb*oRB;FZ2$dOy{IMqYp3dnyhSNT> zz8(ha;(!iUEpd$~Nl7e8wMs5Z1yT$~21Z7@2IjhkCLx9fR>r1QCg$1(237_JGkosv k0&CEL>B!Ab$xN%nt>dL&I@?4;buI=5Pgg&ebxsLQ0A77gtpET3 delta 412 zcmV;N0b~A*2HFFV83+OZ008-bnr@LH6O-NoB!2;HNklkk7~4Gp@cTe>K9lq4nVLku^?EbZ= zY{LrndK%?eIKUMzfZPIrpIDTu1-M86K;r;Nl>jd3ytbpe0dUuk9E>1eC?Cow8&E2h zBO@cj!^4AvgMEE{{r&yc6=1dpJ%9#41<*BQln)qQ_3LM6XJ=++rl+STCns$-+ib_i5AqTsRu z-~x9@{>jiJSBW|iv)Qatsccy()^xh%9MzhW`3>Ow?SaldwCS}<)PdAW$4n*@R)>{o zwOVUB&60B3M2|LKJ~s@&Eb5hg)QQ*~KWe07PbH&PtJTcJ;wVGx5hE*k2!M9&L)$xT zP5YQYqO2AxEEbE27OAHmMx#RwX$idmr`X*s-D*LrvP!JrHGWmA)d+&1(G=~mKs1H= z0pNI+yjhM^y--v<`!YU0u8X3m!}sVIC%XYcGNcV1+6L=A)n8mo144mC-B79!X<geepF7j~0td}7{0TB+LX>l;`fEQy`)b>kD4{c3bhsVdp?#aQi z0|GY0FF#7mSQvuGZ)Uol*p+QZ_DozE>3?wvKHSGObuHDds_>C4ueyxLDXO_IxhCM1 zi%TBWKa_IZIo#&rx(6>sJYN3e8^Vf~Wt;{+dtY3spgq4NGo6wgvokP=9>H2%=(fhi znO|DjRm!`wn(NiD01+m6&>4x_5QME(x%A-3cc&bSZ-#}szJKq%rLOjDiXL6_n)^H~ z40qt|CRwGE(`oDxPZl|bMUQ7Cl4zNUn1CK_rm2`IG#8y$J*ofq?&R j;rs)no~JVz+5ZC^{}Ndo%HA^KMn`NI@}VHn{a# z_wV1IKYs$rKY#u(GBPT;82@4uuZ@Bjb* zLEQg9?mr;O+S2#`&+mV~e*ORb`)x%NKmdU>F#Kou{O$MOzaSM~zkdA>20%8@c%VIe z&wrl$`}Onxub&LUB0x(30+2WcIRJn#2ut|?gWYu1Cf+!-K%B8#df?1WA}#uZ8oj7u z>$I1i0Al&`=O0k%-@icgAIJnM0xA6maSq6BK-ECw|NZ*S`0Lj%1_pot6puj;05Ax` zF!=umqj7^frO?t|3^&I1kxUq9yECc+jDP&0+zosI!~#?gbjk0(K=L0@-h-@h6E`~`**Kmf6P1A5}mKQ2yYAn(r~CP?@|;*k*yn8hW5hD!kny diff --git a/toxygen/smileys/default/ga.png b/toxygen/smileys/default/ga.png old mode 100755 new mode 100644 index 0e0d434363abd6766f9e8a8c8c9ad7275d23702a..14df032f76a806dca6f3874934bd0556e5ae4f12 GIT binary patch delta 898 zcmZ`$ZBWt&9DbNLXmz^nZ1ddKk|}bEu0J^7LXbfsBT-{O$s|z}I_5EQpd1om+G@4a z^xC?*%iF9q&F1D?BVSW8OQyB0Hod)EAG+4o#{`i#9s9IT`}Dit?|J#%bI;!i)0B?X zO$h)*{MeRGYO*`6=ACy-fTm2K`6v*ML(P8y4FvF{3gDduQVyOUQ}6-O(jK`=f>z)N z-~yb09k2oxpbjtqIzR)c0R@~sr(@n>ziqeOvK_oxZ@ytRc}<3CL-kaZc3fLNx=-m* zmX4IVA%k~ zGg|$1P34rTVnVTZv{>Ph%16Y~A-;HkBj{&x`xva=%(CG^ND+%+eZ6sgy*w6E#iDzn z(c(y?C>$1rLj1KgPS71BR#zE;0L|}D3j~CZAII6&SWFp>mW9J_heG*&|Mr!YSNwte zM~~uOmPk|F7~HYCnjHvatgNIyeY(}}=RAA}2zxrNEZfiCv3|N}>GZwd ze%p9*PJe8+veo-u^R(JEsc?*!G>*xv*J_V@0k;`hp4JNw4zw-nPyAB((S1$JJ=NhK z6-{^Ljf+z2_q#0f1$DQ0ra6vrR()szIF9t;=Fk6db^Ycz_iMwM<@(c0=8m6DZTAc( zf6^cOL3i|SMayEjdqI83r*!(_53uc9Bwt2i4=5Kw>IS-ibHHanC(z~wKEc10x;8d8 zUQ5}Z6Yrm-FP0Xbub=SJ;|s+mE0IwCq$F*oC25}=$&R09WQ8Ktrlp!HJX4fue0eXY zF{$*;4_+seD=P~QlSI_&7ZuF<=VV#rB2%hnr{h@(&G3?%-H=#9&NXk_FK{L7O0P*) z=2^EClNs*1S1ah8OSH^oh zr@GzVS=s3xc^Ahuw=kv|HU>#BX!Cv#Zryzo)d0;VDYw;D+YG##4-N4F7QtfW65L$o zTPh~O%VYD{Yz9H_2;zq0PGUU&e*&|m)?{@2zhEe%aj=s~-wenkrG?*!t6Kg6zrtzx delta 427 zcmV;c0aX6L2k8Tl83+OZ008-bnr@LH6O)nyB!2;WNkl)$URLzYLD;Rgdi020R_1pp9+VQrnm|9`j2 zRDX8KDUbvfW>k)EZ(2O=d>QH$KN3zEi7S9u{+2K>GX4ds`2QcM=+A$K-~a!^(JwH9 zFn%*K{{Cdb01!YdV9)*qi~a#?`wdg{8%Z^Y!NB%mff%02G`f VXkVsquWSGS002ovPDHLkV1oaSy$%2X diff --git a/toxygen/smileys/default/gb.png b/toxygen/smileys/default/gb.png index ff701e19f6d2c0658fb23b1d94124cba4ce60851..032b04d3ff86fd597a6daefafaa492070fbfa12a 100644 GIT binary patch delta 846 zcmcc4@|SIbWIZzj1B0W@Uv419lJ4m1$iT3%pZiZDE0C{{>=ES4z)+>ez|hdb!0-zw z)bN6Vq11qZ;Z*_ygVhWM2JwP9y8>+(7#QaT_=LDlnz?Vs)i>4CkEJvN(c#3#12^x! zU7OsvCZWzVdz(kv4%?UwQH?w21m;)IJ-TGYss9^S)&KkV|MtT#i#FVzzy8kuDLw!D zS|=^M+&lNe$rF$Nb=Ca)`*Xsk3rQ^p_ndt9f9dl72lxK}|G#b8)&HrP{}U2R`YwH0 zwB~zv_q4+;F#U;?;-y?*D13{~r_cKRn`pZsz|NFD~49>*%?>sOgBH?!^D0 zq5u8-&Rl!BCb9Pa~P|UVUL7x2b2&`KYuVSFXPQ|Mtz#4i>615K{`c?i_x6gT2W}TO9L#Gt{5QnocaSsC=J2#_p>eCXAHMbX z&yUv$zS%`fs^%S-;*#u~xkylF4v)rU4%G?guf1rMwrCMIIeq0NgLpB6XaR$8u83h> z@1E$b!R5K1HWTxgOXQ$`AfAHeTn@6vn@dZDu zf4K6+(id+ZzdrszUO+-bW|4%{G6^|BNm1Fu0@BMR=1*8LW6GRKv!>0PIP>U)xszv4 zk5`C@2#E=b3X2Oo84wy9936iD!i_6;F5S9z?;*kvFiY7aLW!_ TG*MBVi-Ez@)z4*}Q$iB})l-rE delta 551 zcmV+?0@(fi2G<0T83+OZ008-bnr@R!11En0`AI}URCwBAoHTQvnvLG!L$_YOVPIhR z`}g19iWuE1ZBzdL{i|D>U(k2*|8J(R-+sudaynhucHbwAMTnor{mwqO^w7JHzaBsT z{O^B8RYf5+LvDs&KmRKVd78=o{`1#HTiEo_OolaGleS)G+IQ#sUI`b*(Pd|Ni;==l7pKC$GL>N^3mYH}67p`p&D@J}|s{`zs{@==A?zzXNRmd4}Qt zGiRqmb9Ttc&5KOgY;M=g^y9}LabdQlGu`Fa^naW?FIg}hsOsO(pFqbj{QrM%?XX;A z?>6ywi{QJWc6RR5SukoX!@{)aQ<2Q35p1=S9GcamOGipiz9V4qQ6cfMZ zu%@l(oH?1N>$4MWS)RXsJIyU^y{i4Wr3Zii`Tpnc-^4K0ZO1?S|MSQF`kR#-_cHwZ pyL#&(5jnxi?yg&v(*Xhu01XqwK?>zdet`f0002ovPDHLkV1hS)Fx>zE diff --git a/toxygen/smileys/default/gd.png b/toxygen/smileys/default/gd.png old mode 100755 new mode 100644 index 9ab57f5489bb9ebb6450cb27f4efe0cfb466144e..96ddfd99ca0f8331c61cc7556f68078d760a0f3f GIT binary patch delta 898 zcmey%a)5n;BnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34f$@5PPl)S(28O?#41bFm{sI~048MyReljq8k7f9p&+wUn;UfdX zdj^KL3=H#kRsVYeM3w)Zmj8QH^7m2U-$%KB9%TNxoBI21!mqnAKW~M9KM`DCyTI=y z14HMLs)oz)SMSICdm8!oX~^Hlfqx$Q{C?p6>w(kH`?f#sn4i347`0y~YK`Y}28ND9 zl`#imqW473IT7;mdf=bi-oNj-|GMq;^P0`mb7q~p3=NlQt1VNDSmx5UzwE}{h>%?& z0b2v&cY3cs<@Te%<43mh%EM-!YYem(Ypd2TR8?4}5WK{`Wqe}BFE6!N%73BtQwE0mz4_g{{nIyjx^8q0*yOO_h}E|g zo3C-^v-fIQELD-8BQH5yQe=UM_dK)53=F>+7^-(>dG7Rd-Q?Q7*Z%G~s~?w5zF*V- zc17#!xq6lBN97CFNs7-D5tt+3KHKO%FfbVy%66uuoNzgO#s23Vs~>kvzTedQc3t!9 z73D8i>hNeLUO_QmvAUQh^kM zk%5tsu7SC(p-G6Lft9hTm8prgfq|8QK`P(Tok0I1H00)|WTsW()*upG%R13eor{6N M)78&qol`;+00ReiMF0Q* delta 576 zcmV-G0>Ayh2mJ()83+OZ008-bnr@LH6O)nyB!2=ANkl7h{~Z4KJ3#Qa=Q1XS-;5h0 zel2+U>(Te$zyAID&HDTIKad810Ad2W{eQ1C!|(3%j6gG=|NJezk>PLT=RYx@rPeY$ z{PFwO|6f22zyALE^Y;(XbbtT?`Qi7!KQr$AxuXdb{lm@p`-jH`p5KhW|NQ$6)Bq4bASeC#^Lrn|uRjcboOphP3I3L5{q?^H==I-GzkbS+vvHfQF|Ciw}$ZW>{KsGSueuLtV@sB;{Ut5{K1$X}b zVqgFWAjYo@h*0Dy!50Qvv`0D$NK0Cg|` z0P0`>06Lfe02gqax=}m;00DYXOjJed>+9+1>F4L??E?Vq0086Tlhi1vqr{QUg;`}_Lp>h`Ip_uk$2_xID&(|`HF zzUhjB_`bUR{r&WDZS!kp`uh6$`T6s>wclr6@TH*7&(ZnL%JXe#@@8T9`1tX^yY=<; z_V)J8&CT^~XZpRm`L3+_u&wy2r}w9%_MV&aWnl4OTl12R@|czJm6Gw8l<$s>@t&L6 z9T)e1d-P*o^Iux>T2}H^QSnnw@PA!c?^;ytML+C4HtRSt=`$_pE-33vM$G^K@mp2- zv$6N1p!cMq^_`mZnwRsDjqh7l?p9FmgMjaehVF-g?udlzfPL+ekJAGH<>lq`ySMIH zRP9nt^z`)e^YiDEi}LdF!^6Y#t*GL4ZSbd}?Nm?fO-Sa6gVRw)>VvWkQ%0|P(5nyjp_x`v{rgqF6BE`y%F zfuWJHiK&^ng{76XjV%L05UK#GA%GSEip7y zF)%taH99pgD=;uRFffrp;i>=t03~!qSaf7zbY(hiZ)9m^c>ppnGBPbNH!U$VR536* hGc`IjFe@-HIxsNc90FMukufI<002ovPDHLkV1m6;pQ8W( delta 532 zcmV+v0_*+Z2GRtO83+OZ008-bnr@LH6O%IoB!2;K=s|G$6#bNnb!1Cx>Qe>QfY2m>?Izi&U81o&Rm*8l_%%a6%3 znS?}u4*37)&;Q?l7=Q-<`}?1Z>;K z6Eg-CRR%`Je?NbMZTR-*)JdRzCObz4Hn#u&{xCB#{r&NiNl=Jw`!0Y005Jg0{{ZFX z<^25o`}_OzySMuK`uX|!`1ttlSyb&(P4)Hl^z`)e^YiDEi}LdF^Yin=!@~lI34Id4 zKQ^s>ziZpKwJRVV{r?-`>Ce+9yxF(=Q+E^C)BgYhhy{p&EOsL^{x@&f&8#4y%qT9w z@arcdBO{Q_;~&cM{X46tA0*-c0*LYc{rk{}1Vad$O2mrDH0t^7s WwoY>q5s*s&0000yJiE^4r-PMhMH#{U6=}DSe6=PwIwPYTC+4VZINh= zn3`xAQ&iAIvr|*&Y9%A>s+{N8MNpyfXf~LxmN*x;!&<1;5-QcO9Y^l1Gr`gTB7y>5UxiajG>+@9n380SF6>- z!^1;ELjwZ?y}iACeSJ0;usDDLKnI`!=o&OB`;D*q4AaxoQ&Urulau4)<954!y6pB8 zzi^_6H(ro40>JGWFg3l=zwFk%P-;vjlhJ527z`+iVi=~==_amZ*|SfLWirREUQ_{` z;}0r78J{aPk`BaTv1l|Jdv=;FgK0fYx8-Dg12}u9zoQp>-l>taBQ^37v)PQ-;bnTg z-j>0zrX4pk<1H6Y4*{?XKPo<;5^npCYWc`h*>{`GW?|!ToG~HN#5p+#z_|9l<*mND zZA2tfS4mY?tJTbiHP9ol_z>fn+^y866!(<7nD2UzByU2Ov08Uf(`iBj%T!OK!^ZR^j7>*bNA!%? zZh(EMGM^Jt&-nWF5w)%dL<)tXxw*Nqp`os>PAnD+g~FMcnfu-%cYG~Ea_E7;yrnIY z-}vFc2@az=XdMK?RI4MrMXo1lx49#=NA&gJl)b)(dSkcR4DSQO31vz>IbrGB0;IN zsG{zXoVT9GZ@g9euvH=uPo5vOiovQoAq2v@6deM6w$n{c{yGc?5Q-Mg(@YHLoV8L*X|{1>62 zgrL`(6y-~n9LFDV=THxGm`R)z3L`587w{r^d3lm{dXl|k$Rx^kABy+Rtt1kKL@Gb1 jZutjDKg(pZF8vR1`b(hBsl*cUMkjn2YIvZ;KQaFgt^ALz delta 483 zcmV<90UZA62B8Fy83+OZ008-bnr@LH6O%gwB!2<3Nkla# z_wV1IKYs$rKY#u(GBPT;82@4uuZ@Bjb* zLEQg9?mr;O+S2#`&+mV~e*ORb`)x%NKmdU>F#Kou{O$MOzaSM~zkdA>20%8@c%VIe z&wrl$`}Onxub&LUB0x(30+2WcIRJn#2ut|?gWYu1Cf+!-K%B8#df?1WA}#uZ8oj7u z>$I1i0Al&`=O0k%-@icgAIJnM0xA6maSq6BK-ECw|NZ*S`0Lj%1_pot6puj;05Ax` zF!=umqj7^frO?t|3^&I1kxUq9yECc+jDP&0+zosI!~#?gbjk0(K=L0@-h-@h6E`~`**Kmf6P1A5}mKQ2yYAn(r~CP?@|;*k*yn8hW5hD!kny diff --git a/toxygen/smileys/default/gh.png b/toxygen/smileys/default/gh.png old mode 100755 new mode 100644 index 4e2f8965914ddd3bd6be97674d2e40a9a3f7d26f..57561cff31158adb2f02f1fed7cda65cf9e8e19c GIT binary patch delta 868 zcmY*WZA@DQ6g>+2u> z$LtsO?+*zckI>r6yWM<41LttCwY3S8Y13fbC@YI=G%=MbZnOObaCUp5TEq<-Wu-Bt zlF7@9na#_h6&`}{o*=aFyqo8o9B2P0YCUzPdU(w6_gZN@PQ+qLhRLPrY=)^^UKYu{ zJn!PTS6J3MK3=siWUr{HDIFOlXj&1C?x2~9r6qA`56jtD){;n6ZPMghem(VudVQ^s zVRE8T`TBY$O_we%0=?+E(`B2nS*9&jQ|~yX)L8Sy(PTEq&(Xw)C-d^`siRfdJZX zceLGXYx%Cl{cV$T#%Z6nTfefJu9%DwWBH|W{pWh^1+8jA1qI**+JIKT1&DFhiU9*g zpd2Uxihu$j7s+Juc;Nh1G2@gDd#!ooero8CZ^SMwXsffNJxJRo$xXT26uu!sw{7NX z_txLN6t-BisSba;&$~R|NzE@TJh;EKn3a*a{rP8~lfFp}4vr3gIy5%Y>~uH2+R)^s z=AV3YZ%MDaHhS%Fkde_(KlRCpI>*miE#Kco_gO-+HXM4~` zdJp-;0xhA{77=Ad8lAgFLy#qUQumUQAV`8Z@Whn(z{eEbJ)Hr6-{Xq^3lyzcYeSlR O31G9-o3EItWB&s3Xkst` delta 428 zcmV;d0aN~>2kHZm83+OZ008-bnr@LH6O(iUB!2;XNkl8RSooaXC46r*1+)p|C6Ucr6AS6 ze?v$p`UNC^{r~j~$Yzw41=;`*KrCRh|D&h|N(0S0h$aKjWi;+@CNF zzZm}i25JBZAQt8_hOB_!_dovn^Y72^zkk1e{r&yx->=_}zkmPzZM69pko@%*L;_`% z`Tv}4WB>>t7KT3zfBpiM0)6xwA`MdU?>CGD3IfdnYWNF848Irv0*D2OfB*X<$t?*s z=I)psd>X}nCO~w>0EQ3v&-4pMLRwA(8u@k{r~*o_xXlezplUCCn*`to)Q<_;HL;C#1 z>c9@(B5n9K>eI%ZIyc~7sSD~ZEL@2nuO~eE1_lP;-CL?^cR}1ueh?;S3*}{Ec{x*3 zTP~L8mIEE3(<70%NZLg{4PnMGm<+~w1Ysh`r~0`=|0Jq!l~q>M|35Htx^B3GcE%Tg M%g#w3Ock~K0h&j4>i_@% delta 400 zcmV;B0dM}W2G0YK83+OZ008-bnr@LH6O#f1B!2;6Nkl}VU!Q7P|1&Wm)PrpR2q2b!5Hle5FfeebsWZ)5spT9vc*+3O2}Iw&|Nr{sqj~#t?#-->|9Jkhu>Ak?8z|23?=Pc-^xJ6* z00M~R-|OE@*RL}C{r&&BrOD#`fmE#t2bKvNiggH(g0o;?Rz1Q0-s uUx8jhLO}3`f#D4UgAl6tKUiu22rvMXy;6)TH@U0;0000k0aWpz3WTUW<)bkGh*AP`txT@A5pXk>&%5LT^bs;g<0imFtR%gZ>9JDtvu%f+6m zr7J5bxtuI637X9qhOMluu`DFQF=Y4P*7?$xwI7Y`3MXPtgYcjBPJH3B9U)zv`-*Fv$F}#r@4h{Y$UZ> z5~&Ls3`BP~q0t1&%kk1utfZhV#;-_e6`jt#EU3=BaIm5HdN^N|(GmCR;tS&+@vQE9RM}EbD5>XI(r{wz zAxF%C{bFJKz68-hkMRERM66?YdSvSEtab9v%;>%G`$+AlTI8JOY<;~>ea79G(52C8 zBLMl9I(eZYYJoc-&7{`Lg`nOo>D$k=W+x h!hZyvU2W&}SN|{gf>jJyb21_Us+1=cFUp&4{RMhxa|8eY delta 408 zcmV;J0cZZt2G#?R83+OZ008-bnr@LH6O$nWB!2;DNkl;J1)(QE)Z z34b7fn4oTd_U!+mLkz$F{Qvdq|L@-*^6S_C%a&nk00990)`(*=-xesBS%qG0|gf?f~#iu^M40u$sYy=fB<3vYG4L(e*R>bI1yxzvGIRq z=Kn8V{GT-G|FL6G4S#?d{xbai3-l~N0D&|x{r` zsj!ad4WR%8nf{2Qu~Ej?Yu?^j0PNoinA!j`D47-jJqviI1bE#5y|H(+Bohd+7w1bv z6TpwaHQ-xd1h@ix4O{@u0YktSz+6kqoWbz8xp`Knn{8~Gsjr`|t+lCCHl^~BT>hZE ze6qCkeqmwX^UeLnDTTr&m)m5rM-s_{l9I{%{Cl~%e`II>p2`0;J^c=weLFe%u2|dy z{53xxps0Y4^pjpHI9`v><0jp1(&h3JgvaS{I~^{E-DzJX9QIoi6LgKgXFPE5tAJ_9 z-#X|woTl`j`2f0U@${2vk|I6i^VBY{i|`OmxAEW1gniYyx?*2hT3uRN zSz26KT(&QdT^oZI>hILT3&O17EZz_=Z7_{7z(}+v#+`_Z>5X}%CpyX!wW%vIqBDYN zWg-EnL)F#l%JE9+h*WZ=toTxK-o?C}^EuhW+5EHoj4v~|gIvx4hk1&bY)wWipn(wz zKn9cnMSvLC4)B2tfD15zBp?oJYilzzmK(v>2&vA?6%H^)b_6Aa>BI%1u%}^>8{ADDV&xgPOOcV zWf+X>Me(Z5Wn4Y&#e$Uj*DCp~q1)qXqDoWWqebK;)xBErW=mLZf`(q6-b~w6#MEw4 zWUZOLKRMHJ=X|5N{pZ1j?#XXFZP!oDb=|$BGkO5^ zwrzBB;Uht?QM^th)$OU$sdzPeRY3rU#o?r|(o@)J5;lvM%H^fKk;G#0SgeM1=Su%$ esMqePRU7`_uti2+KFUtm7`(fvP&k~eJn#<T*NuMRflk?JZcca9S7Obsrp|}CN0Y1kR}%p8b}UsyPp7J z0c-pJ>&a7)(!YO!a{vDR1T|}fB<6Qlw#oF`Oo_sVk+2% zKTsoq3?TP6gz@)3Ki_`_=6?VI#CZSdeSc|j>3_iRfw>w5`~yLT{~*Z3#B}ET8Gry{ zWXNYo3rM^7>E7?Zzk%r2-(NrkVSq`X;4fgf{`vP;ML^|Z>qUS7Vgben&^eOal0a#o z=%2sf5Cy6D10w%|odX2GAo%x};THoy05LM8Gcf#M0J1@ Z7y#Y=Y!OAVAkR%8KI1aPRKoIBz0zezkipolzu|5i|d^4J7I%7I4Jx5Qge&A>JbDt&pXm-^HP%3#&CTL=kFetABm7ewSIe^G;;O#*Z1Kj}#@gvxP>==&gzdE8a`6cTu%^={ zmaR&qo>*AftchvVXVm5?GYk*_ao;|Hrg^)ayQg3^46}KDxmbN#U2v+PdeVpra0(L& z@lGdawWgP9SawS)RlzW2YimVwiHN4~bm6*F`FU^TRh_Rw8CXE*?%_K+IKQ9swWSj+ zEbC!ecPdrSFt&x2t)?lHal%*`uS6*r;BR0RP#UBfjlbw`z-tG)lGeQ%9&`;>M%d^tb$cHus^E)-h- zjCnA>Y0L8jQB<`1$(OSnrQMITQU@NS38JD;6WBWZfJ+;Ew9Rz*VS?DL2{an|UD{0@ zwxikG$_RIFVWYSDP~Ikw{^e~i}yrd|MpVyl`p3k z&irzNkH){yMen@%;|Hk|-&{z%_4`NipOZbfe#`LP#7jjsC5<-h@`v|yhlAGMgF$7$ zNE(d>(qf=$d=zP|Hd$>ZQcsdrk{omVG5sG$|G=KU(D45q{~Bf<53C=f%5wqQ)#SWT H-*xOC!3k}5 delta 418 zcmV;T0bTx_2jByc83+OZ008-bnr@LH6O(QOB!2;NNklSc)_w5hAe*OCeR4vOR z3xBi+Ab?oFX8(t({tZ$6>;L~hU=2XVuU~(E|N0Bk07O6y00G1TbT=bV^`Afg|NLS2 z{ReI~M8m&-NE-fuGynt;*hzmtW+Q3%1=j#1fvO=I{`~y|)Bq4bU?=?r84r{KY4``% z041R|`~%zYhXEjfz)k`h|LYgXRlk0r*?;iwKh!;c{{8v;?+;J|KmdU>FoTT$%>dN) z8|qPHZ9omcVEyy&FAxEp1Q0+V4NSlPFi1**^!)h?fo0`3BLfZIR<#e(X4d3;7bIwgO5Odgw%M~_OeBjV7ga5%;v z`kL!M%!LNIo@j&(GE3JsHa2p(TsE7PWm%FWQ4|G1;CY_oIF@Da{`SMh3&_QXb0b~3 zh%ejk%7)tI9)}cc5j&d%x0&B-&|bFtiNpET-`A{}DLDfW;; zz{>kAoZG~9R51<%-J+wS)>68wL7|s6BbOS@ioNn;m)Nabw+KNiPUp?nCvhBMW$49iijB47MHouqtbQ|R|Dv3yd{LO$A zbV>n*)hf6x{9Y61tYXPZ##&BWqMA(Ihm`T%_3GECjw&U*wcA%1hM{RXlgUsNl}@Ms zJT;9pV*J4<7ygRtJ;;6@VfG)Oz1^980jk}fc6ioF*Sf9aabsI*k0Vuwc|aju0ImU7 zfxm%2fGfa7;4*#=9e=F+IM1`EuJ)DLr=!(srBjE^jVAT7`uY5Am4!9odS&#hxwe|z zl<>M-KA+Fi>2~|QEB7AUUv}Snm`oPG_|n#ww-gmlk00~+$EPMHCnw`^hmEwiHhxTL1$ z@};-lJbA+FN!~Bq8vm}uZlAwdrag1|%TR_Y$UibxVtG(>ad~=b(m}Sik{U8`YjL4M zSFS&Q_T1b+xL*?3GK@^;wd4Ex++zrh>wD+O9Y ztKC6V?9k{)qlPe)=?%Jy4+z3Q5Hk%+7oJdb_xb{zga21NDOlaI|Je!6JNbaw)KvRp HjcfQnx+<0F delta 426 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 Hu0mjfkh2@e diff --git a/toxygen/smileys/default/gt.png b/toxygen/smileys/default/gt.png old mode 100755 new mode 100644 index c43a70d36424b66f1627216ad988cd23a4be9285..cec68211264566d531025204bc40ac5305a12841 GIT binary patch delta 834 zcmZ`$YfMvT7=9Vcig=mGup^p?LA*qdIW09vCpv+q5I}*p(?Y~TFE|OhNWqD0txUOT zm5Yi7(r^jJ_(L;{F;V}he@wu1&}C$q#0zT03bp5)@7&Mn&fopmlQ((uyicAydGjp9 zdsW@33Go21cbBxZXMM6K&0bJm0?@n>zHU@`da3}^QX)d%74u|LG=R={;-?Q&v2LcTVSQIiMfJLA()_y;B_MYtdUFHQ@ zl9Z7|QACVzWR!Oeb2Wa}Q#HsyO8D4K`RJHjGc2>bBnSe}^HIbZd~Du%W_Jf|z0N?= z{JDGb(HnB@s9fcj7*66i?lZ;i>11-+>FjgJa+QW9A+SoVcV*Y8Y#){?1|*slS(c3u zOm-W+^EaeBLzVU+h!259@{G&&A*ssa7cKoF!!V0cTGxuSUTXU!+=V3u)qB;7#EiH}%iu%h^q??*uQ|apW4GBHjv80B({2yFdi&=6 zYv-%Ekb31et5zqaq$KxuU+#6;-uCxg=~E|JO3mdzlvzx*w&_3qoc=40XhhWVWxflk z#l_Djv2ABsw$`nw{O+{sE90hG^#M)V%jdICgA@O>Kb{HfNy8JKeZ6AI@}-wL935R3 zJALO>MDnUWPp_#w`0aG2_U?FtSA6kEY<)7KYkSi>Jx(NM4HY;ojb$ZR-l3e6d3IAn zQC;O}V?}*E28++OjeU`YWh;MVxD5y0j%v44Z$IHw7HDy;7Q?eJjn1sW^_e^Lx}95b pT#w_&zPw}lkD>m!WBre=Q~&Q+XH`dfG@B9u3Jv@6Z|B-t{{<0vqzV84 delta 431 zcmV;g0Z{&c2kirp83+OZ008-bnr@LH6O&Q{B!2;aNkl=>AO9FQ7ytr@{8{`KJ4gZ0HP(J{D2s9NSfWR7{ z27t^!x8NVhN&g{E`Ueb#e*ggla?+pwjDH~2e;9y7#Uo@GQ9f-vN#lyky=g*&iVDRSCA6I)Za4Pr*F&F|M ZzyQ?%bIstw)YE-?Z_aEN>XoQY$n?C~q0A3nB zyFFzV3Q%1O5MizA?*LX5;3o#a?E#o)>>U-Q0mSv>2@Cl2OrR4W8V6y3 zAb=l$0+&#Tg5aS+XS_T-GhdJ0s*B*`4^u+94QKCI!b%)EELi&TjUjS9z z4AIGu``Z{xD{X3u-mmaHsO-nd5OyHEyF4VZ21TYoNp0{OK*uS@+)5jp=<_wCXVm@t z+3N>box~Cp83RI{UtJTJ0Ei-BYh%nUw9!SEHpD*bO#L?-NA3-M!8KxYKx7CAwf-!P zPhRE!!omQ}Cm3@xZ6IASZGCL7Gcuvqy?^@h-IEs+DokXcviADYRa7D2{{-ObqxFrk zef3e?5mk`kae1j)^`pf({6bnR)KZx&sh~DtUYX7J0idoorm#o$5aGuXHEA7wGnVez zQJIPRcnWG`p;uz2-U2v|#BfJMZjTfaWLRbkiOc*C#l`ROZ35Nrv?A|@!pT(W?p&0&3GNLv@wI0H)3k5&42m;0nvN^GX`jov(icN}19YRaSgC1C#-r^wFA zXo!x_e#9;b7C{a_;cG(TtR>UOwrJL}(j7qtuVBV2B?i+n$)z~X;YilmY>Bz?@vB$s z7Zq)`$MKTnb41A|Pj&5J*GTjE-Z95O#}(HK#CdeEYqWmo(Dl}Vi+y)%lALqgmm7zV z+^Rd(eXZrf@jHm~WarhUbH{EsoIZTxg8Nec7R0<2%QJ4*NXs)+1r}lHLL~b@o-tjK zU$$f4E<%_|;v6K~ma8wa^W-8EAt;ema1bqDEw<+9_Ec?C?lfm>7a*@IKA){mWK1TR zOs9j>p5-GOiUq~&DN8JTsYNBXsJY5AH9H^$O-WgWu3weBwjdehu1V#lqAO99i=yqk iPiOxL=nSe7ykgP+6CW4KIPJ;HW&sHJxfvJJF~?s{5qX{f delta 447 zcmV;w0YLu!2K@t&83+OZ008-bnr@LH6O&H^B!2;qNkl=hrJ-xK=d0#0&M^~2_S%&fR6tAp8=>2$p8C?0q7F5H=q9h`}Oes zIT^OczkdG%sRkpU6i@>|0I@Lq1-k@j0La}yZU2}V|JX_T{{Q!%>EEtDfBydd!vuBJ zUq+w?fB<6o2X+#W53~WS;s2M<|9d72Yk$Ze@c8ooH#@^Gpjp46{st-r2p}dP{{0W8 z8suDtuU~&k2_EpZoO$|_%&*^!zrm`3vVVbU7#IKo2;^yycfprUio2fr`ZcFaVtj4r@lBYM^?cVkm$B pV*HE|seh2z5UD>5h`0v`FaYD0Namu_b7TMj002ovPDHLkV1j=8-Bkbp diff --git a/toxygen/smileys/default/gw.png b/toxygen/smileys/default/gw.png old mode 100755 new mode 100644 index b37bcf06bf20520555542c58534333e92022d929..9d3af7c5726a0a14e87dcf55139f4ef9a1161de0 GIT binary patch delta 884 zcmZ{heNfW{7{|Y4{f2UZHVUT~(_@3fefsY{ z%^MJ;E(4&eVABq+=lj>SZG5B#*pmzN^#c?a^<4)#N#JG+pgIazD-#O&cjn7mbqP zuy|={T{x`0@-m*=wY$k?^H{BPmF2`G$UK(0a&V=5N(L$79O>~;dVO5wSLwz}OM)f&6Ztt~ zIoYGxt49RfvV$P9v$s6-M2c>G z>#BO`&gEu;$Zr=kmiOFa5WDYhP;}l)5L;>*i#r}Ph+Ue7y~`fXA&hN&n|ybg#ic3S z``FqhwVyEd@=SS;W^FF(&ag_n!e+v#P4=A|{bk_N^!!sZ!RxC7v+u^v^?y9^`}Cp9 zZ!JChWl`Yo;j8`cGyBeu{r2Lwr|%r73Y@w*^ut*y@afdtk?-F=5J?Yw9s1|^wGR@B z<@~&w(h+uA)n|6l0dzfpTonzsTl8*25T%9efq0#J)-!~g&Q delta 454 zcmV;%0XhD%2ZRKW83+OZ008-bnr@LH6O)7kB!2;xNklw;V$N29bx6a@H9Df-Y{`~*{2TcA3GT`Kk zZJPiB2&{qO|NkdX8UFqSqW^z?Gcf!GtAL|lVDy`jQ5I+qKmf6T&Hn%Y?_Wls>fgT^ z{(t`kOaDPu3LzO7egicC1P~L@so($pE&B84-tXW4fB*T<@C!*P8u|_N13&_Ujig z%>MrZne-E^2&e+2=O0w(pMQV;g1rn7Kp+jwK&3$Mg8Tq7>=zT*RETPz&Oabi{`~t3 zL=0aT00Ic4f$8@j21!YfoN z$W1!E9G!q2Xa%ga9%uxNz*ay9Q~;&80=OLA6IajOoxAzdy=(g}I~u+)8^?@>F@yf1 zzS5~JA1$|QE|jW1Q7O(VAjdoq+-Y-Zw|sb1chB1z3{6bV{xCi@UNKsmGb9zA6!A{+ z*n{kh6B)8$87PFU)^K_Gjk2ASYxgQm$A7e55nB))% z2|_@)r6sJKy?ccaQ zAIt0IdOXKx?tgmq&S%XRYgOlpk&OTyvaT*5lU=8%Z3_E_kuibaFZB5;?t7fqrd_tr zE&M@hac@dOcS6#qjxd(>Bq#PHF6xPo zI~2znU?CF|z{eKrfv(mm_xImTf79sNkw07_JtNIIlOsAU;-BPm2f3W%oQ(bqtcE=) zhAtL32vh>}5OM(#zz4E`H2@d$^YbH)?Z42wh^Uh1k^^N<+XWg#nPdf2)MQkAEF<#k z^_qVA^`(rIsH1<37zIf;8S%h^2O}dNii(bjjeB_ELc}jhc;wN<#Ysz+E=zuFc}gnM zR;*n0xc!MI8O+sBt$8{rJAzmo`QH#9aio6Iei9ah+OzSR1% zy=_-}$L>9^yt=m&`}V(f;Pp4U9Nh=sJk(>~`&KXBe&^l8M^aP!gzvq7)Y{p9?CcF5 z5p(%OGM$NGqBN%JN|RooYtqvP9D>8iBzT$ZwQ4pY$l?mPL^?qT2*M$`b?QF`V|}%u Z#`^z;UsKAKz``?8m^0st`p&i@1~008~!=*5f8!$AZ=RT^?GQ0n#v-hHX_wM`p`3(jQ-pJkpi19B_Ls=QaqenpdzyJQTkKy-G#ot9$ zzXkYz|NqbV|9{rcKZ1|HFwVQV;>njctAG1f0|XFQLs9YnhYx@n7=8nt{k!tlFMj9W zg~h*_8Grr%57Y)E#sB}G`}GgEM28GO0I>i){{Jt;d%u4F{?q*Xx0d%Wmk5Yzw*UW2 zfBhAH_LXJcjs6E;UNIbD00;mv0M7pe{r~_0`}_a;`riEfk_q}xd;2~(0{{Og`+xpn z=Jf-j*|xvs&fD?;nQ0z*qtZAeJi(3@-$QZ}^B;r-Z)z_N)8kn%f`m{`&RtAHyGc zK8Zj7K(X-`2>$>5`|mF>SOEfvQ8AR0;q?y&hDi(z=NK5?AjR}A)R_JUCV3bD0t^7} W2R0!FY6>3!00004gpB0orD=5D+^YkWgcE zB-=8^`4Aq*h1N z>M+B^{r=ea__9jn+gs)$NoQ^@my_eB=^5Z{XJ<&O4Xae|=H}v$9>r$OYm-w^r*qY8 z_DQ9jQ0U0Ya+R0=2}C+h29?TSO-;ab@KAhoH0JZIUH&zC{l@CV#7b|kXU`t{jve-l z3};EnG!SlU3mjDV<#OM?eQTqm(ZA+a%}-Y>7Pm;Wn4WG+P2~y;CxC!L;VUl>*VIHU zmenUSEB*b;R;ynswI(Oq#p1`jj=|ukX)i^2rP4@kt(PPn*{1A>QfbS~w5Ft3w`_i$ zl(d+a_Yep+HU*9z^=q|DjLOGDtE$`;6)u^~Sysjs7jp##Hj(H7umH>fv%om8zP>JT z%--Yk+t5^XfbNSgB=J9iz^FJZ7jS~jiP_R`tAt^G^_zm;&m_z@v#d^M(ls?18uiW2 zH*f4V9c$w_t79=OIrZahA8kz;VcEgKpN0p9&TDF!BkDTU#>8Od%#AO>N!%%Gdn>z#Jak(Rn`>&T#ZY-C8os8#dNU%n{1G9)P^ z$4*F2cHFMKMMbB7zcO-Xk}s*|)60GBPbzsoLZe)3)HNFQRC9-(9}p8_@ou7MHz{FA vg32$TBn7zyK@r53XXf+o5n6Y)>5dz^{-4qNLucLqxhoM+$gAm_GWN{B)4gwz delta 465 zcmV;?0WSW62ag1h83+OZ008-bnr@LH6O&W}B!2;+NklJC8tnlA|Ao}~4;m_ayfB!K2{rms-ZxH(P2TnfTvg#G|cV&mukucrs%9y|cFgq4Q}q8em8*am00000NkvXX Hu0mjfTfF3* diff --git a/toxygen/smileys/default/hm.png b/toxygen/smileys/default/hm.png old mode 100755 new mode 100644 index a01389a745d51e16b01a9dc0a707572564a17625..67c11496d6f7152aff04de33491f0cd47046bbf0 GIT binary patch delta 934 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 R0pb7v002ovPDHLkV1hF<^r8R& diff --git a/toxygen/smileys/default/hr.png b/toxygen/smileys/default/hr.png old mode 100755 new mode 100644 index 696b515460ddb670acb7e9de4438aaf21fc5fb77..8cf606426f548b26ecced7c503e584c0f144ab5d GIT binary patch delta 813 zcmZXRSxi$26o!9vETYA@qP(~~6jbQgqHROPrP!(!G+t6gu3vNNB zLhHVwPMSCvN5p7cV$=jg@7R7 z2fP3a&_F-X3-kbAfRNG184R4!7&H|7i;8^PD1Tw0zo5XU*Lz9Qqt&vRnV)}*6?1FW za9XX2%gPFBG+S*Qg>~=OU2e~R^3Izw%OjJqVlk7L$g0&J0S`e0($f5|UilQMUc22b zlQIbjbX**xRJy|31cMyM1p`5rWqk^TM=I^>>th&(rfIj^?Q*#Sfq-xBT(++6TW@cF zf4`fiold7e;18F_B#|&vr}p&pI2;aSKHgvJ+;FvhZB?7@LhI_YZ;6r{?6hgAlx^YR zhIxlx&N)y&v(&!A+6YvDc(|v=v9{`6?uFK@Q!N^6Gf~l$Uf#IiaKr3<_3~1?bocXR zm9GIa((ar7?J{2P+;QzaWoz5?ciV=`t$$toUw`gD@{Flv)ybyp6K|GV8a0)#iDM1G zQK;@faU01uG5tD({~+Fm30DwXg;D1beHu~6G0+kUg;w6vmW96+k(;(axohOzYA)PF zQtHL3$nMBtLj=4Gan!i5bziMiXa=|FNDP=ik<87{(d%3td1OadS9f=()8rUCeDuhe z5o2`Z@Z;r`$E;>cMdngX#&SYWcJTU|-biNLs$&R@LI_L zUUPG|H2Z38QH+%tc7)I4H7|J)ll^CHb)l8Xw|?c1KexJl_Jf^Vb;K^kqe&NY_?;un z#DfO$jV-;3sDtWfQ}{Mh!N=H>S#Oi?7`%f@RZj){D|+V`%d#fPt?hJl&J=+Fru~zX zA1$(0hEEYtsufgT4yBjtw(7%zBvDe5P$UzIXK2JC`E-d~B2EyA7{`!ndW^w!wK&@J$tWG6&e+N=Ea2x;t delta 462 zcmV;<0WtpT28;xd83+OZ008-bnr@LH6O%myB!2;(Nkl<0|OxA@1MWlzkmOI;leLR$De1;{4g{7^y$;L zZ{NOt{rct07a;lk`E!5(f@%OM1_PkhC8`eE9egqWbga&p;hdpFRZ$Adm)# z>fe8W4*Ct02B|hR1*-n0rS<;(dx&bFRewOnE>DToOAr=`VGYFTXwo_NHjuEs?qN?S`Of)0eBXB*R3q&? zv6TuSmb2mA5I{HVlzt>i0*c=O%Bz7jk5%pjbPV7z39v5%eg$o}k~sjyDohmfo)0lm zLrj!FBm9jZx8L?nX$9Jw+MQ?WZKZWKea&RyXA_$0adpL*vTQ_NY?TxaXMHl5 zu6iI=n3ZRSf$P*@Q%RleRK-}HC!n`x>xR=+mK25gu&h6~cnD}kfT{l4OyRjn&DjZc z)wr^JEVpz-T4X)09X?txD9*PCPnreN{`eDpq(S!pXz}*#ym)2QP;DuxFc+2epVs%O z3-9G??&hlQNEN0NGGhkWotF8#DE-&Gfhs(g{G~MBQu4tkiR^_0)_iQl9LEqk8y)0g z`aAa!j!++a@OBrMc?WO+D?0g7D)&k9!9@XkA>NbTGsoFA8y)CkzU2%j9J}cDpl$Y@ z-p<$vBVY%X3ncFREceOurJU47Qn+|buy7=PUKBTX=)kOCpDQ8C85807AZ!Yl07ig^ zfag{QGtdw80e6AT&CR|}N{6Qc6p0{#d-a7bx96LnY57S!+6--*SMZk9%zG5isGHA? z6>lAql4K4^O61uxsblhyZDuN)d}N>Y+wQaD)mMD!m(Dj`kdsXf&5cKdVo_RhYRb@k zi{$>y5XG%qU3tm!VqI;LA(80q4G;VF8&&@N+?#J)ZOh6eMn?~De)+kqbmJLyOI78E zam4N0fp52eDV0py=sOxOq=<-tfxXNhe>kmKc6)n$UL*LJ=;_%VdgE(l-t6PoUu(T` zEQ7FG_p`fhl@zb9g(QZxux>U7=z1&)b8heDeafe80|SGDX=s1Qa>;4FUtRJn6s+Z? zYjd)-GPYDB^9Yy>CNrE79UjRNM>5!Zqu8wXcQY7l2IJIoohknTRO%dsT>t-o|53li RhR6^vfX_R`{VtBI{1=0+g9HEo delta 425 zcmV;a0apIS2j>Hj83+OZ008-bnr@LH6O)$%B!2;UNkl@lJ^yeSLk3S#=NC=4jfk_5{03^mi5da|&1HpY@6|Ajue?c$IP*>0Ac}Z`v0Hd)tg^H)qf#!P^JHVL&)F%e*Xb6#6?+wmH-40 z3xCk6|Ns8~1)2?0{RgZLWF&|Ls)lL+iU2hL1Q5&LKMX(>AUTM^NU9+S#0FXN@8@rz z^Zx+^5DWL07wmsTIe-5EX@IBzTJ`52%kO`z5F362$-h7b*KaNch6exv#P}EJiR%3S zk01R1^NZmZ(C**=fB*Xb3rzn04HN{CV1G7~oYMPUrvL(oi@=Bg2q3VA-+vgz zB^g0R{{8poHzPzdP!LG|1^I&s7T7@N0|XG`V{jb)gGJ^)NR*=rLPQ_{Aiw|(gA#|z TkRhOP00000NkvXXu0mjfRpz*m diff --git a/toxygen/smileys/default/hu.png b/toxygen/smileys/default/hu.png old mode 100755 new mode 100644 index 7baafe44ddcaec29ad9f187f759a7fa3a1a5df00..09361e49f6cc4dd89f56ee6c80f7dd7ee4761564 GIT binary patch delta 764 zcmZ{gX-v`q7{(t~LZz9_E^@5J&%EBuOgE0}oi(yc*@uO`_Ram&&Sj z+r4G3Ykp|ex@#S)R<4GcX@&>d=9Y?zfc9_owNLM!=Y4;B_dK^}JfPPzXUza0O$-D( zqQY&<3N~)p1XM-?duo75YPH7=?BW4~20(HWSX_4YzBUn{ZBI?nDc%EbfLFkC;4$zJ zxCh(?nt@vsQ&lx!wYn=R+!l+wth}$dxX*0vF&bS4!;h@2?u?ACw6t#;P1B#t%HG^u zTTfP2k6!Q6>AJPr?v#`+m8w%F>qty$7m3;k!WkFWL6VKYceT1xO8JTHBB3)j_Dgj1 zSGoK$5C{aOrltb^fZy-;`FxX;lU}cPVq#)^e0*$d%;WKlj*j;C_m7N>jCwr1y}dra zZ+Ljv<#J&&ikgc^+eK2*pjdwa4doA;I6J+z2eY{$o-Mb8s-A~g0SkaHT8U| z6{ujkWs*0@lTRme>Nzn-VwN9V&f3pfQo{_lhtI1H4Xz62)^Q<4-i`e1>)DytGIdwB zrd~=_pI0l-DP^Z*Nhg!U$Hju90)8Do?r`q7h8iAY~#YM0y@1U%$A#-vms5b-=@l_lXg8>-}Uiz z>x<9tI-a$>ZCSeNLg~!@udVHb`)5Skb8l#RZHVRr=Vr+et0AXz-J|L$nmt7NH0t6I zE3bRLL;H!iQ*ICWHTwYhCZ^Fmz&xdID;L{C?peJ-`xGzPSB-W9ApF9<(h}yRLW6Bl zWNxz*=2?uAf}KWcfY0Oe<9VWZB0)#+Bx?i`0TIjNNq9VwaFF$n_zM)57Me}g{{zY$ TTP`_>m2&`6p^@K|8TS4L^`9j7 delta 369 zcmV-%0gnE=2CxH=83+OZ008-bnr@LH6O#=CB!2-yNklR7>lcvADk}@L2Oxl0z-IqPRt=N}nF>}4Rt?nl z3xA^j7Xt%805Sgk`Oh6_*t7#)BAfCKR_NCQ9sF)~;)0QG>-4-5tn{bxV}009O7BIIob0)D(_ P00000NkvXXu0mjfsNo6vxU8tP)*Wp|&JnU6`%Przx|NHS7zY0IWS7#wrZ| zw{y{e7VF9wYDShSKTVO9ET^4Ol`}s8thTinFJHnrrFc10UrN_8bG7sgbxw*ZGf9z3 zk)I{YC^5@%Q3`s-EPzQ+&2@f!=a`wcKbx{WowQCpvP_PzPK=tzhgL?U#z8SI5gNoj zI$;++K0J3UWM^l`?RIaww>{e0+S=UQ+}PM~xm-@CbA5e%ZEelraMd>Hr2Mz}x3K?86BetrS|_q%$#g?I1uoa@TL2!R2a)(};HSVY=MgKB8d($ zeSIR)kD#Lu#|Fm5!_o)+L!*+B5h5gqN8k9mK%MrS9?4HX&&ODNE|pWy^%#&5GC3BZ z#FFBR(ntt3E`gdribfDBf>08T;d_YcTI>q%`v1gUAfuA;SWJo{0%WJ>(`Hi9TYmxY C$X*5j delta 367 zcmV-#0g(Qq2Cf5;83+OZ008-bnr@LH6O;7|Y=e#y>zg27mx!0Xgp5*S}vr{r~fq0csvl>92pk z!P<5VDfB!NI2#TFQ z3lKnzfB%Al=06ZHfFW+c#KiRe{d<4_Vp#e2?;j&0BS<|1KGhIepf3OdhzY0xzfv@9 z009KD0caOQ4+9JU?PNd!KrToYs2CuCKpKAh`0)<{7{Cw*fM^8>FaZBzcJ7)&_ptx~ N002ovPDHLkV1mA>ubTh> diff --git a/toxygen/smileys/default/ie.png b/toxygen/smileys/default/ie.png old mode 100755 new mode 100644 index 26baa31e182ddd14106e67de1ac092a7da8e4899..fd87d4bc00eb5d8a0ca749daea91e3d6a95fa230 GIT binary patch delta 825 zcmZvXYe-W86vs~mYHF67WvdSwG=ogqmL|-=%3X3ybGd6xRGOQcXxKuxWX`%i(h>$~ znh)?p)Tg4NC=<=z4_}GdOQMI5HK*Hr-RCy9>_VS{J{`{a|9%JlQ|p+3Mqbc50628O zAJVjgi$!ZvGt&VoVgRZx0C?G`Y8JpK0C1=P#O(l)XF7Y29ROH!Ri1_-&;-x`a1o#u zY<8R7ZnxQNR;zVt;v2OCh$vX*!6Fytw*%CHdB|)UFqQY0`+PpX|F_@oU-7!V9l4(N z6Yf?8(|i0t4}VvI#_s(T6RQIZ~2*& z$1Z^eX*v=(6Brh}Gl_fHkjkEVrIJHv(6~mWDkxCv@-eMeW1svrJu{`9wAv##MR2x+ zb2yv1ZH+BzRZDBr)#k|XOgWl){8*NvP{nEd{QlEgyvt~y$PJ;qc8euFZKSHQEJPN2 zYI{n6E+@$xcqDpa@avn;BR^_id>sDPKfZB**%%ZW9JXE?6v8xIvaH2#Xw=qeE{AvJ ziXs#H_@UZod3W2@`CU@q=c{>nUA^mCsBoF);jL zc$)H*iGc~kgMdFjM4VLr{bTs|3ye4z00M}G;R{2xYc*8G|9|45;*5+y4gY}vDER;1 zpZ^sx|Ns96G5-GklDi2YfLItxSVgb4YXfjasA0Ybs`#&c5^XvcLUqDM3{{9AP00d^^H2e48-+%sM)d02u=%hct8G!N(3;+QH((o5-_OE}x zfO;@2_y=+i*h!3FCjkTyNW*WSt$#tPe}Dh|1q>*3JwPWh{06xi=mZ7^fB*tJ38eHl z1Yz;RAFzghe?f?W0U&@_fU)-b-ycbCNsu@G{AB_~0s|u>BLf65u`q}!fx`u)0q7)v z0Age)WnlOOiepfG|6tf&vR#Tx3KYdae*wui4`jT<|ACVM1ONmW07}DhsSFaY8Vmpc N002ovPDHLkV1iNEp+bA3l6||Ni~kw{Ks+e*NmztCufd zW_N!%bL;s|Y|Yt^6j6@QwS{;psAt7iVs%DF#EXMSg3IRF3u z|N4Lb{{8*?_s^d{zkmP!_3PKqpFe;6`0@Sw_ix|6ojrTDYr*f1`MA!#e z&e`~UyZS%i=`t<4G!Gn!czBklQ{`Tq9m;L+qfBg7y z@7}!(s`p!#{cc$Dt9HT9%6UIaXZ)I2_8BHXp*5Z^ zjv*Y^Rr{U_H3dkx*bCP0j((W5ATHWK@OM-~`KN#H6<_?bwzf_eJJ8X&=**U*SzB+) zJ}B9FM{fOjd5*S26Ek%9CL~C-r3y~>S!!IP#{J=W%)Nv&;s=%#Zwy%%l3gmt9dU(Q zY@yrctM5uSDtt{@5OL)>+u@59MxrW8XAbmO95#=Cu+*hv3zL*;eO;};NPX*y4fk@& ze;@u>vM(n8`|;1L`>WsQI8L$OJatuOyjHPP|Mz>oX+W2&mbgZgq$HN4S|t~y0x1R~ z10y3{19M$NlMq7#D`QhD6H9Fa11kfA6U9rOqiD#@PsvQH#IAt@q+_R^O!P!UbuI=5 MPgg&ebxsLQ04@h-egFUf delta 368 zcmV-$0gwLZ1+N2;83+OZ008-bnr@LH6O+0EB!2-xNkl2Ck7Py`yU7x{{90h1(N^&{{0VR|M~m>50L!_$o~K5&;Rc9 z8vp^s2(&>=ObnzJ3?PjE*Z?CV5b{XcgblKTHZfByjpAQrd=h&?HO zAa>`R|6Hv9XB30N3RxDY7$AV4en1PH(pe;U$RIR8JPi;)Eb=b@S{i|Y%>WE+APJ16 zKYtni{$&J`Ai=+&m;rKu@$m0I!-|@J00G2!`}S?*h{R+d1E5NP00RIi2ze53>3%K% O0000MP1OiNem;)L<<#>N|kO@Y_?XR(9)ERj?yh(X2rVJr7WzC zk_NX0+2RI6G6pM134$=tZH&1f@qrWB1KX8R4CUqAe6(L%==Z2!qW_xQs;3GhnX)qMT%30xSaHt_~120vq0KzEqF{#9e=D zXOT4aH5hJ&!3=@92x<^3=Mm{c=yL>J_`4l{p2PhX#O4mMb5_=WkexL%vnJ+OBRxaV z(*|l<7o93yxvgIIC?jr#?e|YPw2GZ6V|}IU9St*8#CQv6&rZsnAD!H`a!b5?BP%kI z9v*)#GAaA50nB73d*8_})iaBA^ukekzSc9*=a!?HBPQZz%+bnAISPxi%i-qm7JD{8*E}thX$!YC2FqL5Pd{N zZ-v&SRduN3t@7N{xj79vFQ3d-IaNpo2-vUKt1nkuFIp{MSxf^aW51F3f-v+L%DcH}8Ey_0s$66wkl2k;mJ; z`_;p7lH@&)6r`IHWLtPVB1w~NeNdWWc)U2%@{pXDW_)^=xH^9Ov-(7(@W9#~ysi76 z(rRBhWaH>}ZGuWxjai<4rOmX??+_LvPK{d)#(Aw!UnktWDWOMkH_ yT_6%>iZX>;1p<*kaMnd-{p1`g1&zJsVvR4Yya)fUw{4w ztNss>0tLxGfB<5GmnRH!zkmAi4;z=EiU8PJWb_XpfEXEy89D+wo_>7# z=ii?{e}Dh}`{(z+-+zGQ-`_wH5c&5H2>tsb!!NVEaXCN$u`v8$fC$0WA(a061K0WI z-(O%bF#KWw2p|?92J%I?MIgpNNiYJc0D?dN|A46fU=9Bm00M}SA(;WF2WSt7gd(Fq jNFsk=2$T>Q00ImE`aDe*M75wS00000NkvXXu0mjfs&CGt diff --git a/toxygen/smileys/default/io.png b/toxygen/smileys/default/io.png old mode 100755 new mode 100644 index 3e74b6a316477b90cce8b5f2111f911b1c640950..0f338e80a1092b47f6ede5195b14af23bbd9e819 GIT binary patch delta 943 zcmZ8fdsNZ~6uzzuADeEgt+iLRGUv<ruR&otesGfvxDy5E00+h6zG$9KKEhns=ClC- zLV};WR`1-5>|h>>B>@!e0Vq8M@E6)D{S|CX_NZ;^8gAP#`MUCBc!^Ro8@~)ju|3jl$Q^=k*i=NeA{`;EPE^d*O zt1@%-%$$CDW^a*{$rsb}uP{!x=K&4k0ad}$0{oOn?!5KF#q5E@oYvs&f&j7&R^$a+*g7Mo#AP@+4^?xm%5Q|T(M zC`HMMI^Gz`9%IZyIXeh~D(FvW0W824*$iLkUyM+&@F_12R zuuQIQYWk5zYtau`=Pau-<&FJW@eXU?Iy#h6_IyLs(YrtEuc2F-qC2Mg|#|Ve1Lp_WB?ifDsVIa5j70Jet^LB z_4QwN-J3;i;*cI0ftP!_Z`z0^7#@*C!2FDHbMbK9TgsQBrBNb2B+kj~pac9mI{m_i zjgFfzPR=f_FTUjF4llp*>gFx4y^d^sW80f=z5UL++hNDf_jc{}*yH)W*9YEveLnmM zu)co#`~yA?{3HnXY4HBf4#2^X&@en9JmL`XaAXvT91TZeVkvR))PzJ@QgTY_Q93Zv z(wQuFMrKxa&ava1TrTA0pD5t*Pk#PIVbPag6_+63Tv}ExICc8#GiNK#o&Tm1sRH4J z>YCb%moC@UH(Y5HHC=_~7L=g4RdP+ry)LV2Ys=CHks|xmYV;-#9wCv(O5?F{%oA)h zfFIV+&j%amFJcR&*sJ&2a*Ml83+OZ008-bnr@LH6O+CJB!2=VNklK;Nk@(BnJ=+sJG@H9u6243gztO;QFr-3LWwA`z8=W=;rkT05Jg0{{)&C#sk(H z27mVR1oia^`T7CwI=ckcau@Q71iA1L?!*;{7^Nm*&$?GLJd|NjPb3=0E0KfCb4#nAu(05Jg0{{-+t zQ26`)!^rI-{P_e371QGlF%9qW_x;Sb;D7u4{{8+^89l2!(f7eF`p>#9os)n9h)L$d zop=AaG)$G*)Kwn;fDVBb{QOZ0?CTIDAi2wlrF#yj01b=65 zGEGV#-r(^F2m>G;4$IEy6cq^^7YfG9=O7*p;o|cTD=go3$Rv>nUgjt|H+%w!W#+^q zQj)w+o_+=z2D0Hd$W)-~!Ca74A~COQ+P}a5!aRH4m37Oz0RjLq0M7pbU1hU&Z#U@c z^#}(88yXDJ)9Vux2^bX!&C%){8a@o>=<^B)1^vB#?Jsv177N?k;{piihf+us!bl+c jhr#^=MnDDwK!5=NY6}umT#AI-00000NkvXXu0mjf(>@@d diff --git a/toxygen/smileys/default/iq.png b/toxygen/smileys/default/iq.png old mode 100755 new mode 100644 index 878a351403a9a33fd9ae3af1ffd54739545f364d..97219aed2186695c272f476cf7b97f6fca207562 GIT binary patch delta 789 zcmZo>X=I-u$-&IPz~E@}mwTe3aJ@pZN02WALzNl>LqiJ#!!Mvz!wUw6QUeBtR|yOZ zRx=nF#0%!^3bbKhU|bd86XN=xf#EL$!*2$Lp9~D&85q7YFnnfU_{hNUo`K;l1H=F7 z>VK7$|H{k%m6ZG~DEyn7`zJH=PipG#goIx)F+an@e*_1A_w#!VQd#vcK0fSUOw7N? z$iE>We***m`1t&GcmL(&^wY-nhq?K8L&I-6I^R7#UjkK|n*Lj|w6`TooMspqGC`t<3`moJYWKi1dRU$9_7Pj^pmS8qpaM@v&neQkYhRc%FC zMR8GaL0&<2R(4KCPI7W`Ree`sfGj={M6^kx!)lWd7^`7-Ejv*Y^lM@)2+!PX$ z(h^gXpFc=Rc<|`avxiR~iwlSdi3y4drzcF9FlExTiBl(s2ZRKL1%@UET)%MT(zT0M zFPj^f7?~NGCL3(uuw~P>jaxUD7nBs06_zq{?WnG(s{H-q*H3m9Hr9578ME8lTHRgT zT#p|)c2r$O%}{0el4VQN>r>KFpFer_R9r;NP(*b4lxb7LL&8F@U%7TwotafiYWA$? zsN1)!t#&rL?rkei>9nN|tXAcb8=U0<30L_>8h1_n=8KbLh*2~7ao C+f?`f delta 408 zcmV;J0cZY!2ZIEV83+OZ008-bnr@LH6O&T|B!2;wNkl8RSooaXC46r*1+)p|C6Ucr6AS6 ze?v$p`UNC^{r~j~$Yzw41=;`*fTB6L0RRMnAb1a3Al9INYNs|4I3?NdWCv8aHe`Ze zI25v&!6f5$jpNVh2O?$m1OPDr&i|7M0Wp692;wA$U5|F$_;B;f&rf1p(qbG!Cte(9 zVPa-y5lGpbZn4ZFhzcT9$vfdqmuG@jz&@ z)$3@uH`Q7(D3+OJ<`+OL7cX8scI?=_d-r~W!7ng?FhC@j_23_Elm(4bHf>Cn=$1?p1$SC*QrWPy9jhH3&~h0A?4g&XY8kMyrobq$ z5wag9tISKXY2wJt+>9OLQs_XkX&hThHgOF@REDKgyb*eOdV0=j!TL{q|9tu0_j`VP zc{`K(lg~W2c@qF>e*AIgj;)Rt^!v+-fz}s+&Qk!Fh&q1-S_t4b9U%D#5H_Col;#71 z@sa~t1)RWXpcgm^bO9#-d*U8w0xZB0Ko8UcB+!GlJ8kwUyKTy5{npy}O_Swoi}{Ax zIB6s&bTyuushbCdPes{isoJgHKctio$oIPT=6#Wu z`FZ9mpJk{9RVaqr@4mfqdv0}ZVrgRbui4SX(ffb+1{a3zuH1F~Jm3v_FV9_`TAJ$j z^-nKP7xWjz{2Ei^X_~=0@D?&`5N3jd3tcBod)%IvfrMgF&9>xpU{(3m2ju z594%F6cwiFKp?=!c#h*(hKcs}GTq&g&_D9~{d`YPERl|n^FALpHpb4(M2CkN?{wtK z724|!U%Euyx)r*3F?91L<#H{Y>|%kK*~}FcaZ)LppC8S2j`U8DVJ(D&+2tLgTYX5B8~ds!=+!A7mW}7LE%%|QlEYLnXMn5Is4uRr0(qd zeU&BJvWn7j&FsDVGp2jr`M%F3vcx%}>`eF7p=+kA1H;1ugSJL%^YNyZV>SvY4<5|7 zwc7dUBM!D>UGPLsPNeG->5UzqlCO7+wk637TI+##YT66y%bMSKtGe|unW^Hqx}mN^ zN|rV$&4=ud%8cbL#g^I=GQH-Q%KrA@*JMRUDs2Z39({A;)akyLewd#ZC>liJz{fu( z9>*@LqRMI@twxEy*_ap*6Jl`&k((jPl50hTBr{u*_3Fz6At8u{B$wttgypEgY_k80 Y_#g26A>pc1l$HW06>9mHg}Uy)0WnCHE&u=k delta 450 zcmV;z0X_bs2Y>{S83+OZ008-bnr@LH6O(lVB!2;tNklTuF);jr zk;v#5jAUT|2p|@QFAUYL)gZ+{!0_+yzrTO}{r&s@&!2w~^!xYUKY#v%$lrf|Z4F7-s`Ty{t|Ns8~x3>Pz!S){pfXY67`UDU_Oc38f#US*GW&hv2 z{?Eqpf6;>f$N=n5fB<4bR}BO)G5?=F{eOSr#Q(%Z6!riA12q5y5OXa%JA;bK|1)P8 ze*XG@@#6oVKS89P{(lx0pgc$$P~^l3hVS1Qu3Y)DYX?98G5%y=V9w0^ckdp z10gsAiOm4?s3wj|NLe6^PA!KZ$=RN4+De*qQ8GZ=quMR spkjakV*Cn@SvX=q<)TRe0YHEO0P*5TBsVw+>Hq)$07*qoM6N<$g2dV5*#H0l diff --git a/toxygen/smileys/default/is.png b/toxygen/smileys/default/is.png old mode 100755 new mode 100644 index b8f6d0f06675a9570c2c6e696ee51282097c3876..5236627e29d75fc45c657313be4686859e7c2480 GIT binary patch delta 811 zcmbQj@{($r+*~(dGN@tudoN_XE;)$&Oqp9786FLquFr?OZuDDz< z^>ps!ldof<K;l!(mus?tP zL^dA?t=}I|v&XGuyF3*XMb?hx;>Urzdtck1fm}8XMi$)w!pkaYsez zp|jIw28Q2%en0nc`~3Oyr%#_AKYq+0*;_T|Y;4=1h^GBdog7_?w%O-xw#wRQnzr5` zX{}DeDvg*G?!`cTo(zJWWiw9)*X@05V{MtW!6a>+e$ra)xK-*g%atRRIu`)dxH51z z7fq`_nK${w^RUp!roEx{y926s`c~}lD&6W;7Tx~is% zhMKaK@=E%;>E_DD+N#zvJ1Zx%x!Kvd2?rY&b9-xNOGisvv#C|HyQ`;*hntu4`|Ib+ z$FsS)<=Pw9chop6$Q5ud@W_}^xg*sjC8WeBC#HtUIVmV&lWSLKN>)^rTUuBdpL1S) z+`bvEjiHHwg`Sy_WtFVVCn~3gUNzlX`jwZ{QdE>-?{@XbGL6(Sphr|oTq8NklU{H>20Dzz%fcl9MY$GujqEXsHVM}-SX zf2-xM4s{hykP}!$Xp8x`g@iqg4NJaUd$B+Mm%>4cD_iu*Z zzrl=O|9^qF|9<`Y547mdFIIVlOMCYL1b-0728i(>^MN+_dJApbjMWAvCZ_lA-va~? z3$x6Gs%D4BuYkV%yQsqY-!G6Ke5&>VxnK`~5YP)iPs@vQ@9doh5I`)zkpA=cA4D3c z6y$*a|G^%GXaKtOFGRy1kW>Bu1P}|uFQC*vF+t8hU;!2>DIh5&!UZ-36m+2Q0Y8Ny z!{7hFaQ_DoK#UB`_3(&%`@ZIips?2K*P^0DV3B{Y2>u5n82|zd09rw2loeyjtN;K2 M07*qoM6N<$f(z#4ApigX diff --git a/toxygen/smileys/default/it.png b/toxygen/smileys/default/it.png old mode 100755 new mode 100644 index 89692f74f051cd43503744c3dab65c8ba773b7e2..a2c0f0202770f378a37225f041835df8f2d4892b GIT binary patch delta 745 zcmZvYT})B|6vvPG8J3Y~rZ3tW_8`s+rfz7%4{m`Zs9Xih4-~}ZG)z&Zws6|#R!z&w zht^XMu1%LqGtElEskChMl9n10p=PUq3tT|a?n5taZD%_>=l}nm{dXSv_Xl-Hhxh|P zR)#OM=MYUBC+8LB0kp*fbX)}Rl2XSyKr;fcAp>Cd0Yo+2A1}xN@Dt~s#8Bt~xCU?q zpbIP(i`i^8nM_8bad~-padFWJfCJb7egpg@Z(V}+H|?#nt(qCl_V%{VN0x7EYio0J z(_32Vkw~;|v6v7Fe*o!gt9VsW`l7UCyu|DEdORL?S{lJ*x)= zq^DcROj=M)sYIh9{xBcaK1Q8RCmtPbkBD%jrkY8)9cuNp=n$GYz;QSnb}AKTv5e$i z1IkGycZkawO&dyFj-9`v$^{Wz`;5IBkothki)%-j(cr`eT zLf`%RW(4_k7~pfWu#kDVOr}s&DJ!exYb)Qru9__Jl_)ATBK*MqNE+>4ckivcm9pMD zwzSAP7M{5S66M{z zszP-mC8zdO+i_)ai|}lD`2|^SE(Lyd9=`6;5esB;x*CLY`{WTtVD#D zmdHGcF%foh3Oj|BfFNuH3HoB~`iH1jDb6Tc{}TTJG+jJS5Az5GfR75fgW0mqKj6+h A?f?J) delta 357 zcmV-r0h<1(2BZU!83+OZ008-bnr@LH6O;M^B!2-mNklBoF);jL zc$)O|A1aViRs9E+gdl(bVqy5gQ0-a`QV#_G|4B+p{{Ii;{r?ZAVq*UP{{8>YA0YW< z(h?BnhaF^2Z;XuMpq34 zK!4R>4Gh1312q5y5Xk($41fOst^5Z<|1klQ2B2bq0Ac}p6lnJEzrX+dC8Pl;&%gi> zKp+i&!Dj#Z_Y0^8j|N7tlK=vU38>-s|KGpi(h`wP1Uu<<}w|6yPN2q3VNKuUi@ z5OxiJfEs{4{R{L1Kmf4-W9|39Ka$*%AWm=m`3sIh21Z6k1~^bs0x1O}ppyUsh>@X` zf#DM?GhQNnVc00000NkvXXu0mjf D5C@+V diff --git a/toxygen/smileys/default/jm.png b/toxygen/smileys/default/jm.png old mode 100755 new mode 100644 index 7be119e03d203695325568174b72522124bb2f12..37ae2baec6c9590981ff7dab210c86ee09fb3fa6 GIT binary patch delta 893 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 delta 357 zcmV-r0h<251*8Lz83+OZ008-bnr@LH6O)kwB!2-mNklUV68QJ;AB03^ zz`>t?{{RAr1t`kJ#RVk)|Njrs1E&A~hf_%4#fujJ0mK4R^dG_4x9|VqBmaN@`fq0T z-^&ZGnvszaXbC_7v7oD7yY~OWh5!Hj`Ty(p|HFs>KYj8)IR&Bt;zED`VuHIKB=q*} z|9{o1|Ns8||JN@lTD;`{R1ZWk|EGa3dAO8ObDh3E3Cb;oH_5X#1|NHy@|M?558b}5Q z|Cf`4hZv9q2p|@?lb|{i68>{>{ol0~`Plzi}L3MR?Wo2b~d3kARX;D#8ZfnX_k`#eNUI_GuRs31IFjDL%)p?}z@W>(V9HSc|NsAM$A9GkW0f(<+uda*<2ADt zKn{C}r>`sfGj={MRlb;4jaopV&7LlfAsp9}6Am!+^!S|7S(EcdW=_l`MUc6EA; zj*5wbfrf>go|L#;+HBd<(bm=M z?&|gF;^F4y{QmlE`SS7h{~Hc0crf9@h7TPc`fV#-%($`RN6V2VPo`Yi@}AfB)8feAoK( z-@hL}|Nr_0bQ_Dj+^xMk00K}f2RQ&hFc1JYLgN3=lsJ)M+p3uRWC5ydAM*!ly4H1h zsiEFvIU^1)HH_GYz!QMsIYt5i1c4ZML4U`}|G(2(1f)y1ler`_1c?eLPCGR$Vn^aH z{mP%ZBm3bCAjYfL_bVxw07C}q)c^lJym-Vgc^=cBzdy6%1tjG_VjwU4XJKYJaAYMw z001!n&i?@bc>rumZP@eK`~Ca-{`&g<{|EjFGu!(C`un{i`|bVg`u_I({rmj>{bVZ- zDawe+0*Hm-2hg9t6*%O6gGB%Q{s+|d`@S}aA@TQ*=s%wS{~3P&`}6ldP|H7{Vt@c* xWXNX#QVf3>7=FRfA0);f7~>z9PjSO8Ed zellCrSYA$T@uCudbv%HUEdV3*sHGoZ4FWJ=13(V|%xFAvy-Wd+u+zBEtc?KR0AYY2 zKmec*pcmjPz!!jz0K@Co$332Sb93C~q8gj1`g+pojM?o`n~kWfBq}O~OeWl5c>Bk> zj;gNqQk9jY)fzLKW7aa#y^w4!B8t^RiUK?@FDw#;`20Z(dkqjPFB?9kAMqEByi!wb z3cOGj%E<}xcwvp^B|zNmCM}kz(MWC3e}AZ>+EmnGMf|Rea3Vo2H^AY9l*%63wrX|E zWQvxS5)1V4-MZoKLejfEKe|aqb|8_h^g3>U&F-H$Gnk+M1b}L2AQoT|tv0s0aQHSF z+b@mo5yq}%Q#Kqe#W>Ie*rD3wDhlsqb-9tg?KY~ok?6uo zw29vmlP7Q|-uwD|7u~vvX;kJUf2|G1=yW`XWBOv5JK11hco_o0%G9OvHYTXjYNnKl zni!1nSe7%zD))@ja_!T~r3~FlPIc9MlYU8&wW9QMi?8Q-@%=ZQuO5H6yU73O-Ti&O zuBV^w^mM(w_xj1lj+fom69P*tnt*dKlVgdVD zX8MlKb0PW`u1jliEw6Fe(b`pZx_}rFi*u2@T#>{qLeM!WYwr9D6g}=?)_e72bMlX8gmTE<|1vNq0ns0zZx{dq2xP;5 zh!=kY&G_~A;V*`-zy9<8Vi5ZI|I4p`Pk(+wRRb*nI+%e0Ab`Mr`28DV0K>0ef6o18 z)c(a}@caMY-+!07o-xp%{gFT9Nrd;0b?Jy0e)=!wlzLv2xCTKn`ND# z1%#dzd0fYJWtoP+aeZ)HXGep5!NwRews34?i5lV9s5_5-|Mo|Jz4yHD^W6Ks&-;G4 z$xP`;jM)evH~Y!v&X>08lZbp_5%6{jU^ox_LzWC7pot3HmjSF#fF1Rp|0u`?Hq4h4 ziaFRa0?IHp4g)^{KLA5OKhOt!2V4if0(yZSP(}<_7W7>UEuFsRcAxIjd}G`1nh!kH z?|I(TyNNTiCr;0lYhC(MjT873l>Q5sJ$3I)*R(jRnjK1=UEVNxtj;E>v4~VAe&u+< zF(XGhnj^kbUSk5@zXSN2o4rSm&Xtw9#o}3k(8c3<2*NFs+q1Ln8JRX3&6=8ONl9_A z*;h%jPUn$IJz}w2P!c$2@KltqdOcyU*QMv?c{O#meQB2DWOGuIBPVAB@HI5LMWVZW z{;W_K4TVCzy{o}sI2a6ccdz(-Ryuub*DmAEowls3c9N{oxcU5<0`8nt@^>T>2`#Rz ztwmQ>9{c^P;qXL8x-l`)n2=y&GD*koRaf8Ta%b}1hy()BhYzAreUy}hmzKie@S{2R z*z?;*35|Q_(biq<%_J>nKuB<|}P%xi$B=sh9b*WMz*(RhAbsi5 z1LO{5yH?;FK*BTY>+7HOO_RTX4VAn+c28)q>lz6tT5b`C5~Re%zLNQ*aGQcm{mNzM ziep@aOr}sMDphhNu{iJd1@Fn{0}Bf~;uD_R{_M7`S353$tm^2z(j_So3y%m&MXre{ zyJ}+6=}4zC=z1pY;DMp<2X3n-`uYa@PuFW3bf=mcqss%T<(0@^tB;?Fc{(=ksV$q^ zFDRDV+CFOi@FG9Iu%MV*#A{a=jplJ{tI1;8zx%bl`}U*}yKjAceam|?JXGtZ8i%lZcw&DU5bHGA>mZ;V5m+%J%(AX&?i zY87&=l0`Hq$pAW)PEVtzr_mT<8kKe6AdA6FrBYc`>g!okasLsV)F`S{&Hpdpt7>m| R(q4@PxSV44&BL;H{{@oZj>Z50 delta 487 zcmVmLII{9$1D z3uHsk9~gN_@AqGZzkmMy`2%79|NZ;l z>WN1H0*Hly;S0zAU(a6s1S$e5{PXueM8z*4`!`56P#qBc`tw&>oP&Yk4L|^~9RBI6 z_3>1S zGXDMzbOE=49-9EaM0J&9T`emwH;=g~P!Ocy*DnU30tNVGcpOK zXK;IXGH`G*aB%Pig@_cF{AXeP^XnG~{eSuMgMk4cfLOpb{9yn&_7^iJ9}^!RpQ{U# zfFRIFK2J|30Rbj9R;ck{8vp``2^e4?e*uO5{*~(O0}3&+vi=7G78XW!_WukFa!Zyl z0)6%e6kb5*00a;t1H;0y7L$ihz5#XpVPN|G`!6_H{{8}ze?SNrZoj|(26|pWmQZ`& z>Qw*%1auPLr{C_fB*v8u$BQBdC-VNL&z}= dK}-Mv1^|pSGp$vcitPXZ002ovPDHLkV1kZ2-Uk2x diff --git a/toxygen/smileys/default/ki.png b/toxygen/smileys/default/ki.png old mode 100755 new mode 100644 index 2dcce4b33ffe1f40d490cb1a2e03efe22ea56155..83b15b89cd2b16624c6c7ef7c1b45e177d437f74 GIT binary patch delta 928 zcmbQhdV_s}BnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34f$>d%Pl)SX28P=V4A&VL&NDEaWnehXz;K*_;V=WkeFlb)oi!gj zsz0<=zHcsl(^~TBZr#VbRWE8XUl%67%8P%I75zLl$AFIb6CJ&wT@7t(73U}Sj!0?!X;Zu9noAiiRsUfeDgI>n?ygU^C>R$MZJ3-HP zdpveFeCVim&r0D2yWb`F|G0kn+q=78pM`#U9Ps|m zzAxuaKef~TboS)OE9YNCyRY?ht*>7C;pxlnB@b`h{qXMg-M+auYNy|R{`}#SC->rO zE+p5SdUEbkQtcVP{F9Ha+%neB?47xL;?f7#?ta{S@OeS^wF@`ipS%7xx#41B?fDZI zUd~=~H>j}wC3O; ztItGNoH};)*_<`Ef(wtGzWjnyxjZ;?<>Y1e51e`3z2I*9+}k^kzSw^FS>2TDRTHmX zxc++mo<{|p7gz1PzjFKitQKHg`7^MWyGGAvV5m3sooehom4TtSo`E4Zxo~E5=41wj zcm{^(;P_rf`Cs0goG3pB{em*v^23Wv$8TaK0iObJh?c#Iz~suM8QBqHoihm zPt8opNXtrASJl*d(NH!%(^Ab$USHi@*;w0JW@lx#WZ_`rVs3BkZ0Tt0YIb*Zwsi4u z^KyQF{e1a&`~M9G6xtq4xUk_v$B7j$X585EqveQ7+mk6*wtVS1v*yj5JA3{#9a3t0 zH0jc&PhF>0y_$7v*RQr?YHiP^UEB7p@7%g~^X~2Y$0@9C=rhl#^j7^BN#pdh^Nh>y z{bA)6XJojoVfmdKI;Vst03^4snE(I) delta 595 zcmV-Z0<8Vm2apAj83+OZ008-bnr@LH6O*a~B!2=TNklW#1EW7Maex3~0jd+}tN}6q|NsB@-~WGq{xbb%i{Sdt@aOyd_sm~^{r~&t-=9DK zfB*dd`}gey8vz1{1+4A=pXW~*{{Q>`=kNbNe;EG!`96sWNHIP8^84@af4_hK|NZOV z&wn4xQnEm+00M{!r2OxHhJR4iK=k+TZ>A77)*#lue}Db^^$Tb=ko*TE|NI8J3LpSO zb8Z9x2m%4nC$a{*Gqe90@enzQH`ta%tA$7d{Sv5iP&~x@8dMF~(;dfh&fyCHF#yj0 z0oRR-9sTeJMNR(n^YxvS3U`G57YhIQcz^x<`~CTD1@aB?a9|COp8p31_lJ1J?*0M* zF#yj00e^d?acV@HzxYi#4;KFIpu+p_`U7fE2L1j0dzkVB0Q!Ma2!5~n?DGATWf4(Z zj{=CXcjj&u9#vNh!Mjhs?LGBA!B-L(Qd3tx`S=bZdLoSeQ@9kdIP zK`Q}(vzrWQi4N6#B|VTM064uBpymvKH&E4>0aOTpMFPNX2Z;FPuiot40F?f$L%H0{ z4ls>?T@SViSQ>}LG59D@!Vti5_@V02>7TpH{vIfF;A)brBgtxlQ2Tr;ueSonl^%}*!(?u^1jAGo z9e^0jPV=O7678uRFbxm{;q&>tUN4U09*+mZFt^+7aybKHGMQSI!Zfacb2@vbdPJQf zi^Gy}BO~oxc1 zf??nG>0L2X$y@Zy$cY_{X@2s3fYPoKv%{=2>%^`5?w7Nz*-2($!h8O9Lq^ODXUp`S z$jMZOp0#0Clats5Ah~+p5+9z7= z3#GCHL?FFgQQq-beFLw&j+b5Wl>URsE@P5*ci|gA<#a)U;t5{az1m`VxH&rd!{r&EPH$~XVC_csbBx@coIvtkXE zw@nc#PF1hw#>&I;SgLhY!M1p1)Q@|rSAO@kG*q;+k}3${$7#wo9oe;EX1Z^4%&gh? z>AhwCv*F{M^}_;zf)&2Adk1wV4MF9Tf5e{DGHmRs_pT#`(Dj zMtE4;#nwv&NN#@K=+MY;N>VcG;e+n3QwVxeg*n}>%=T{;_KTfSQBhFyD^h1Tv7Z^3 z5sPvUqq3sIsGKc5AumGZkccEE#v>{5%%ofX`z5ts04u+dQvd(} delta 515 zcmV+e0{s1_2f+l883+OZ008-bnr@LH6O(xZB!2BiPm00G3zpv&-?|D(OUgSnJ>_lCl>%fEwf{o?rj`w!D^2F70ujK3L} zelY;qOn+E}_}^TA4iG?48!Gmd%f1q|`&f2cUgVY>$N>x>e*)bEaumqN{}`A#Icy(E zKL7|I7KUF8zkhM_{RscVVRJ{C;m66AKQQ^9;qPCNUp7w`0|+2S zhEQ>aCuCMzvGI5^nP&#%7vR8{=` z_?Ue$k$WOTc7+6P5A@mMQ+CGv)Mcl{D2My^A2>L8xY+BYCVC#vWvDz+8M8MgVozk? z*1+K1-n(wQm*hKz`q{L%_r7}drl+I&{64jB2VI^sFqEAri`WwpvNI%LYe4ZS?~~Wv zQsN!0t!>QA%sib9dO9kf9ajAksPMJk-oEHqaqza_;NAYa?s^|P;uaO+U~O&fiW+!OvMZ+R}5(-jr~RAyyrYU=B$|M;lZ*KUF}u7=@7rX}ugc0_d3~#L&-ZC(}W?*>9!0?cP z;s5{tr|#SE0-eQ}&CQX|-b#iz>NKjZ{Xz=xf zgiBX0O}l*Y>Sc2S6C*Q2)8quB?Hjgi+O~1)=JJA)qB6tegtF4&?;pN=+V=h9*U$VM zJoQNlynLLz-2ELrU45Os-Toda9$tQ)zTW3goH@nzskrE~#3yd9?k;by)2Fl_X{}zB z{Yhd+_Ul*DQnP18N8P?TuF);jr zkwEkdL^A3y%;RB5jD#uy2p~p=R)%WVYLMdp|A6T4|G$6!{r&sz4-jy0{0W!*y>H>4 zCr^LBeEaA3@86p?Z2|}&76ule;{Q*+J_RcM`|t1Xf4~3!{rmgxZ!L-6A?&}WSN*GN zU4M|6n8EV@iK3G1@mvOg0Ac|;0BAB$=^v2UK$U-)*nZ`y{Mj|-clZ5YMwWrx+&n%W z#?EH<)R}*Ov}XVaAdvZg8Gx$){P_!VhO+4IFy7y@TYg`^19ZZ#FMAfVGQNqjUc=Y- z`~SP&K*az7!~*md(9yqt{bpeOlcn*8=YRR{iM78TJ_oA)EyDk+Am_Ig#~OyTU;jV; zW>EYC3=V(*VgUvP&}b>4KQThTuJ3o;wq@JX*S~)K`Yj>yYg)~3p<}=PPx<}-*RTJ- ze>4964b%V-Kr9SQ3?T}?S)Tp6a=_KcFY5I>h9CP@ad7;em-m~o@YnyBP}K~7K!2_V z1~otcvCMT~IK1G`Ahix5vjvZRlTJ%f#=I{ThAax)F)bIzW0qE1eKtBKk5F-$0 zrYHUR^P7Y1Hw)9>#`xc?M}9Nx_zg4 h5sei=06>5N02Wg%AY2GAegFUf00>D%PDHLkV1k$g6(j%v diff --git a/toxygen/smileys/default/kp.png b/toxygen/smileys/default/kp.png old mode 100755 new mode 100644 index d3d509aa874809a323ea99f3b37ece8a02201f77..50dfa1e40d16e237b277bb23843ecd425a8e087c GIT binary patch delta 903 zcmZ{iUrbsD7{x#BI!$q_7CK9pPMx5c#Pos)5m}80Q8sbvu(Rnl<*%WOvqhj`NkiAQ zFHM(f|D>AKEg9L8F4@wqYnOIgvelT{Hm+J~olaCh;9lVJA1)U}#_JyTbaK9v-^2NG zZoM>9G)6Dp0RXMSEM>fkYN=Fp^vVD}s{uHD79gFEPNM)u1i&{4AeaCsKYsmwj}V~1 z-rXseuutWf#$45C_Vtl$`Ea&uC`(agb`Fqmlz_ejycYv$1lmF%4gI;CbBA*7;Pz&4 z+lg;G0@-!{)*7~DM>DHvdc{Nj?M_*iT*TC`{s)0(NPQDk}pmEeim6 z9?*|w?5mk)&*FMLX|s_&UsR#MDk{+Z`~A($a{$Sak@U<=^4!_fg9nN8=Shbn+|hv_ zJQ%2}^D`JIo$jU4FdmOMb}X*br5`^gT}~23qZ&D_a z1ieBBtXr_t`9r@EA zQgN%icu6%ls8*|$T7^dC-Pm+`9GVT6x4e|LXU}V8d#TsPu3XZNjbFX|LASiO_i#_& z`%ClBmGdta7Hxcl%i*ytjSaut{8@SP?yWnwPaZeuO(%?_L^!StCnJg2?&6Zd$QNy* z>3zRVSHEffMx}H)sb{E{FMVG4)(ss-pFFbMbk9ugJSQocVScM|@4ec$*m%d7c;!o3 z$(+(z|HCkTVCp0L+rOX6QuoyqPVsfoD>rQ4J)LO!BzgTFELJvrsP%kL@#Nbu4U&F? zT4B%#RC-NbfP-*2^$5S7&6TqeK|`Z}%V!~o06``?*YE!a7#>mUwC4W@u(F|-$Jw>J O0Hl&G@sFaxGyejP3$yM3 delta 499 zcmVleeXKS1`s-yjYWVq*XZAjX3?|Eq|BmH+?$pMl}ue<<@G2!j~^8UFwK2jTud zcH;{`05N`KU}W#_`}g7%!(Wh@3@l9lzkC7d`Tghr-#;Mo_wRpye*gdVn@LLY`JCAR z0e{2-lwy4KiiwGVIVgltUY_yi&tI!o|Jl8p;n#15-@icU*KbCk6Mz3^`1ON{fdL?Z znEnBs@%JyYzyH5mxBfr4|7*t%=AfYeY;6C2{Q{!DKY#uG1wwy-+}}Xm3;+QH(!lWN zFN3HElfM4XRjZk-tp4-xFo=l!{|(Xrbbrr(uudTP|IeR43=9AP!~}9K(5)|C{@%Qa zB_QC>_U#PczyEvw{NK-?AVoiaLREup04fFuAfO)@7=d;zS;Cf>_+L_jMNREzZSDUz zZy3R51Dy`>0MK|QAYxzu2p|@qE&q6U{ylp1t-S*jP{82(@|A&$>pv^&Kd?G*C|m(! z=g&VuL12Ud1Q5%qv){F(SwMje4Rc7SgQEg01QGuSl4IC$>NP+BF*3)0{rdqHk-&Hb p8uAN?3yps)Mm}i)w+DC?Bcp;9Z9ShmY8T*nU)!?t!V^~IW-++nR&#VIA^V_ zTyw@UyOreGhhp20!4+x;CkW{SlH1qDVN$3{%^^Yb{4 z%jNQyuU`3PC~sw@Co$0-7dv<9!tC_)%jRaGP^ea`XJ%$l)3C*2aZnW5(&~wicXPRu z(Fe>?(XN`i#N;H7;S@y~j9BaaPnKBz=z*i6oOg#ZHC^!K+ayUkDGI?*f*>d-+0;3G zLZTMrcC#-IZxatTyufS|77W7>)IbY{;ihMe4(D89{clb_#?CfmR}WbxozI(|3Iu|V zjt;FsKW?|1Os3-E;;gJJy&mZ`*fawZ4hN~z^{1t!NhFd!T6)Ce@n|%#MyXU1gw<>| zD-?>BmXngml!&|sC1sE9yeX-A zQTMj=Vf8(jx#(JDQ?07}Nln`grQ)5eJ~%l>^S$%imm&4QXS`fHcte)r*Esy+Bhrg$ zYP5#_`!arv?vuXH*o|uUuTQSnSs^I}b#VKRcOaB0iez4F0 delta 530 zcmV+t0`2|K2G9hM83+OZ008-bnr@LH6O$qXB!2<0Vxn$dUT@yKdH&)BP#Mq$ zfB<3y`hl076R7Rgt5;mSyo`Gea7>-}|M%}Nf0%y${3VbO@hKwm+s&C%B zIc?fBpuar~I8`lFo-hkBeE;FB!{+KFy>H+C`uh5lCr?8B01!Yd|G=Td#l;2WAA3A> zSW`>0Hcai33HzVF|8*7E@7=q(dd+%aVPOFQ0iXh)lK=t;d13To8APi%WTUcmOr1tE+?Kl>s1t7@xm=iyV<~is28K!2n_Z`2$uB!~g*X0EICb UH!sB2Bme*a07*qoM6N<$g86O)S^xk5 diff --git a/toxygen/smileys/default/kw.png b/toxygen/smileys/default/kw.png old mode 100755 new mode 100644 index 96546da328ab142ab0c7370511cbdbeb9a20efaf..66ae3a4f5d8f743b1b50a95ff6bcac6a37dede5d GIT binary patch delta 839 zcmZ`%TTD|206j`l3?xoPMShHn5o6?WTZ@g2#L!Xk%A+m4%#ze_OCJW56rq;X#?%q* zrZW@JC^3GKjsZ$nXr+S0EIOxK<_i^^3|qiHU{Gl3i&De-xu1QVoY&Vm?xc&!Eg9>Q z02CbjoZ9+jnqi0hNO>u6HWN610f;56^IqT#0nE|>w;f2Yzda;62(0-@SVHj=0bBxF zfp36wfG!aR8UZzM5>NtFKqXKCh@k&guYau9J=ZloYtlT`)IT|0_qeX+QH}B!rR*Us zeIPYR29JsD;xbzqNa*!vt7@vG-O|e8N@|c2*+nJyN{XySJPR+sH=onPVRkXIe#|2K zNEAS)(@CXLNmZr!g?V^-cyMyi?y%cF+WIH@E#p7;e(3Fa-_tePWgWK)x&>OTRw9v5 z6gB0XipS$CD=V>BYC&V1On63)3vp=BuO42`G1*A(Sd=G)f%+)Et$=MJ9qru-3y(a3nr7Vqhrx* z9^>;>Dpe_2_~g)`D3|MFv*&Yi=JxFQXXnnD?c2Q>h73B#lFLb=Xt#O{QS8C2WIo~W>~D5++1%~*6+YL;0GMQ1TY4?16~6! zf#<;L>S|HS_w2-2YpRQj3a^|>9r-Fjk~L&0KiQqUK4ljkl;6!ZB(wptuz*@OCZ}md zwL&FR%013cQ|?KZoOY&f*t}`$#w}?#Tdud=XjRfJ*NzFP@~=e|;;~n6M@D7zt9NfW z1e>*=nYYhwvGuEHOW!?fQ@y4^d-_acq&}jc7XxAcMgQk`(zb1D&owv8;v23C8Ai+E zch9=K?e7iuqZfWPecH@0zO@Cf{CRio_TR(t=0_dX3}f3!f8g>TJ+rqy48@GUWipKJ z8>0j6aQKH8W}mOKGZSr$96`F4nK&JGY4{S2LZ(r2$)=bDcV8Zt s&Dl#3T!LsXaSZ*Zkg4R>sTDOU-TyacHlAR#GIyr{B)_cC%%hwC1v>V3Hvj+t delta 424 zcmV;Z0ayNo2j&Bi83+OZ008-bnr@LH6O&g1B!2;TNklA7Jzc zhW^1AzhDT=1qi^Q91{Tm0x=Mzph6DzUI#|7m*jw<*=qayMYY)&AaY_7>|;NX^2Lhw z+E)O9G%$Sm|76+IzYc%@3jYO){{8p&H%J9gDG2@g`|H=gUqFT|k1Ws$009Kj@bBOM zzki?q{@M07EI*V_lnFTrie&N{Qmvp&tIlLU`0Um z=MNK@1V#oE&=Qcfe}NtV2p~qFhT9AbVhjxLz>y1yUj`ThBEkR@1Ok8n0|1?9Wf3W+ S_=P(F0000UB^B_l7pFnfx*${FZV=6;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z<4^qC&YDYVSR7?LI&B=a$Wy=eSZedItc~D;cqO0d#TenYKcTnOqr--SJF*D0s z;))w1^}PE$Z{jE>B8#s03*TvRP-BUAZS4we! zwQny2gJ*vII{%cpo(Z#Eqh~loOt$v#HTCG$_w3YlX*+uU-r=)%51zig|I{sWzn-kR zwVny}vs`1QJA_ZR3YuWz*Q@2&zF}s?sWnB(CB6v-o(C4BZkk)qz+jzLwaO)Cx_$U$ z6W?Avk4{aOR%P2J1LyjzqVnwv^VZDCi%rY7_G@EcFitF89$T~|B6m?p=DdK^Io=5~ z-QuP>MNYO2>9_Rmw+`ww^J~|4=+JR)Wnj?#|NnnIN4`8L&KZ-u-CbssU1YNZa@b2e zeO=j~vGZ}MSpM}9O#uq+_H=O!;kcfhkdTs;mYACS{K1n)&mKN~oGu_BA|xg#Dm;C{ zlu6SjPMw?{5D*d+78n|Q{lb+?*DhYYoNi!XVq|7$YP@~JmQC9>Zrz+-P*74-R#;m6 z{lk|}_1`{z-JJe`pM!^skCT_XzoVzCuXAg9N4LL+kC&gPulM;AXHK2lnEv3z+0*(O zI$qj(n!4KSS8PdNv1;8)-8HN8Gjg)>GIO)Hr@wgd=GD8GZ(q+hD7^GUGkEz;V;^=) z|G4{dYz!y98@^0$T?&eP)e_f;l9a@fRIB8oR3PQgU}Ruqq-$WVYiJT;XkcY*YGq=k qZD3$!U|?r`g%3qXZhlH;S|x4`{%JXzCK{@90aJmipUXO@geCxr0&6D# delta 582 zcmV-M0=fOJ2ZIHW83+OZ008-bnr@LH6O(`gB!2=GNkl z0vPTM7r@r=_W1eT@B6&W=GF86>F4SF{R8;*`|kt0=OR|!6(_;end<2qJ7u6-;BTi|Gs|p$**5LzZn1hWBL~s*!b_?|9?PW(N)p131|aA0DrMC zFaTZq|JR?tzz_k3%CFylfBk3p&G_&4kAH0upIGlsnR((iP%gnhrrKZ1ZxaJR00A}p z0W19r^eE6_xHcQe}03EIDeDj;sc-w|1W-iV*JYt3?&AD0Agfd(1pb)G6|By3IGBO00XZj U&R34u761SM07*qoM6N<$f`(%umH+?% diff --git a/toxygen/smileys/default/kz.png b/toxygen/smileys/default/kz.png old mode 100755 new mode 100644 index 45a8c887424cff6eb0471f5a1535139b965e241e..aa8118a8e571cd5b62f514a799bf75890445f796 GIT binary patch delta 928 zcmZ{idsNc}6vr>2jz?lerBs$yP^Q#2VbGWvx}k_DZi66*=g<+(G1$y4^2f6&2)IOM zC6RbKhLoP{@JEqxju_$C`@w!~jPaPFAcxq4z1Y_JuYdaMp6|W)bH3+$&waQ?i#Z^uaGa81TZ+ZurRwKRV#14f>bi+7et@vRc|uOMj=uFPimt z%=pjE_*jJaCcG54H@SimLstv2{sP1=t zXTR<{rgphN?aEU+v*jztbxsgJLM?srNil!IUt{#u7`zMf7QX>LSEa%l)^RZ*dQ?y%I6A+a;m4(OPJ z+rFilCW%qRHmo+H)5!SLRo#u>G-q5Y!ki-Qds%Di*bfIJ_Owx3vBCkZ_qEf#EW>Ig z>gA0;ZqwaUYERUvlww6+gKV&R?B0NcMH#iFj#vw1b`Z{Lrg}!umWpw2sSa_U)@X04 zM1Lq*8hK8yw9PC@?j1#T-L>XPZP09n0r%phfHCf2Xgx(5cfQJ%t#FpermsuuZ%H_( z{=AhkVkO;OPQJ68GiHTG^MZ*dHFoh0!Hc>%dKXjU1d%nz;E45`ytTc%g z5R1U#!eeU^< zn_^yg@ukf#1H7_j>#MK59vk<@o7>*nj_imBc>5hJ4!<)Yk&u-9E-{4!Yj?f3`~B2C zd&!ivef!f7WWd49Lx;1DP-*n+oDXvIKFkM9!AFHf#U-U>jPj$$m=zyGSmkln33e5y zx`xYRp8Vt#@CBdNes&tEt8Zv*YW`e!1|rV3w4Q6*(cW=h^u>kFi(L>YULSP{`SPo; zGx9HYf78?TZ5$?{94`WVFjFj-lFMb3aLb8h)#bqgcq|?tk4=ilCD3tL;?6{3B5pet uOT=Q62=l>A{{dKRM#WM7{{v~2)ZQQv^REMqElSxO0gz5PM82`7xZxk%qrn&e delta 555 zcmV+`0@VG~2j~Ql83+OZ008-bnr@LH6O*O`B!2<=Nkl>FpW)wshQC1d|NgQ3 z|MBx5^A84=zyJON(VzeSfB*kK;n){|0AgWaV0gwLyXbc?(|@pW|3Pvx|85%m-~aZX z;D6r#K41Q^gEaj6FV4=$@arEy05LIu!2gi{Gync$`1$|;-~a#q|NCw7f0xa__4fbP zn*ZDO{onuJ|3JnA^#F|o2q31v4F4ql-&_7K9cVD)|KCjiesKSPtM&iLe}Bp>hOODMyLi*`~U7D02QY{02pl*zx~qz}R^IpI-rF5aVC4aX<-x0AgfV`?dWKUnSE! tcw7Pl8_NEJ6w^S%e=smGF#rS@02IGzT5}(9_SgUb002ovPDHLkV1h00H8=nO diff --git a/toxygen/smileys/default/la.png b/toxygen/smileys/default/la.png old mode 100755 new mode 100644 index e28acd018a21b62d2cc4b76eec7bbe1f714dfc6c..302427f7fee71c9c8e3a33cd64392b070db2f41b GIT binary patch delta 881 zcmdnYvYvf{BnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34f$>CuPl)SZ28Q1Z3_lqdzB4dsg+5f9NkwI~9>-@&{VrJb>?PdXJ+agl+cn?ZVa)4WHS?Ke|fZp7AJ zEtzoZ$B!T1zkg3}xojDK!Yc8EY3#9p(sK+9?F`~O>t{bmZoL`bcs-)}YE13bH}Aha ze(}j8=ZtCm3FFw~`jJO{3eGYxG&2Zpt)6i|uHkxA?X|GVtKLNyQ(Lc}y!`1oU$L=2BO-o;gnkbQ`0nlf)y?gTlhbEg+fSC3pG-|Z85w=h z*Z-iSbCZGLIRnE}28PEB3=bI??lUmlW?=aL|Nm2$RrPm(CNU;?ySq$(mM6U)$YC$> z^mS!_#?Hs3V)@rcGzBQM-_yl0gyVX00t2I)np#?#SeThv86O*)pPycyT%27UqoZP? zV4xveA*ZKirevgLC9A7ys%WSwTPd%vZmw*sZ7s92a0y_$7v*RM7=wrR_rO}n=3Ti>~L@8;dx_m7h&;d!U;9FxjZ zS5{tL<_k>0Ki3;XPLlNt1^Pv`#5JNMC9x#cD!C{%u_Pds!N|bKNY}tz*U%)y(7?*r v)XKz6+rYrez`)M>3LlD&-29Zxv`X9>{L^waO*B;JVqoxe^>bP0l+XkKhPHb< delta 501 zcmV@YHsEW(t$G58rGVK#Sk&y( z%lH5P{`&_)e}Db_`{(!HzkmMz|NDpG@4vr){s7UR-+%Zegz6bK0R#Xs0M7pelOk{( z5`QD-{rUX<|NZ>{`TG464+L38761SLpR(uO=J){s0Q&s>`~3bJ6bIb^kphT`;m^O{ ze;I!LWBmP>@yDNkj7*H>@iN-VTtHNrD96Ue^ySz8pMMxZ=pRTgKmaiT)&Bj@@b3@E z4ZnZ>72sjw<>mS3fzyE*#V|elQ z_ouIaAR69${QdC7&p!+dKmYvy1q_1U|A86+0*D3ZK%n=2{|4Ft3a9tq|5Q$UnBpe} z47bUvpS}F>hlvrW1n703LqGuw5I~H+4E8DYneU#w{tFHnpw+*AK-}>6@9%$1|6W0Y zV2OXia-szrY5)R=Wd*}Q)I*}{TFC4!@uAE{{8;{ALuY}@B{L0RjvFow!1JmS(s=00000NkvXXu0mjf&BFzn diff --git a/toxygen/smileys/default/lb.png b/toxygen/smileys/default/lb.png old mode 100755 new mode 100644 index d0d452bf868e0cd6417f518f1dbe695f191ef392..55a5e5b44345981491d168b753e640a886bdcf72 GIT binary patch delta 838 zcmZ{hYfMrB6vvM}E|#m-yVlb+w3l_!&_cUqa1kUh(ZaA4K{O*XlXB_`Yvii+p{Loq znQN_O?^e^w%yN~zpJop-KoJ!2-U~VIMWgklFMT@upa1Xo;q2@hWMu817&s6BVq<(k z2ZBc%rfWoUDL`=uK&cVH>#s^}09#N1y9$6;0Wf~k$(qC%fPm90mM7yTfNua_06qhJ z0C)%R2H+Jy6Tnk|#{j=}mw3vGJjO!z4n4I!pDfNL3Ui18ZBL%crCa07R&;5Sy3%F! zesZ6|Kow}I{0uTTjaaYj$y()7Cw8Yvok~$xGT)Ja*(D1*@CZkGLLGqBZ0_^<`WVJX z(>|JMceU-RF>bq2^4t1qY( z=M;jYICco*mkZ>ll6okLAc)%9T9e5{)3n#?B|T(wM+u}jT z1lhQ8Y8tjZAT~H-WFjYj2!e=bke*FBNXr3&^Ck3<_Ec2g{k%2AcJU{?3F)%SP0GW)8AOS|E|BMVf@9w$u;_{gn z=Xc!M`TPGrpcyd4_~+k0fB<6p_wOIfbfEeFfl7B>-|_DIyI;S5Zoa(f#*3@};Q;9G zfBygi2&4h78pxV*WHQhn|Nk-k{`>pa-+$lj`#S!>)&B?j>kmKxvA{JzyfM*l;?vL1 z(^jVdwWXRQn97;`{r8)Zff?coAPEpajMo_$q^olNK6(HQ3Q&Oj{{8RQPhj}``Sp|W z4>-hrgM;hu9~OC~6WdP#1P}`_F8+V}#wZ{N3}z-^u>SeY2nxsFOus?F&j1N!P%zB= x{r~kF&<212V!VhNk^hh)7|vyYkw5?tU;q$PMCOG5K70TG002ovPDHLkV1oB8>RSK+ diff --git a/toxygen/smileys/default/lc.png b/toxygen/smileys/default/lc.png index a47d065541b0d998da832e1981b479097a9b36aa..291f1c5017dac12f687855c0e9308d05edf46432 100644 GIT binary patch delta 916 zcmeBR+0H&evYwfNfx*${FE@~4Nq6*hWMJ6X&;2Kn706de_6YK2V5m}KU}$JzVE6?T zYIwoGP-?)y@G60U!DKNcMRK5@(U-H(3me)ywv z-Pg%Gzw~eY+_ULZ=Y~&h>pp5FJqd06HEHMf30uGSZT#M|;al1A@2Bs)ym;qD&b%*` z3qP04{a85jWB#-cjVpnQANOwj-o4>luRpyU9qp}c4t)BS*!?MQ>W7SpA5#0@S1)?675~V$=I8vw z-#gZQZCv)XcGtHpr@vTRTbr4gZ8-WqW7Vg$-Ve!r?-M%TmCpq#e&}8GbMB$<&8xmt z&G|C-?T?)7`KRWaURb0b8q(eU>~nm_`?&VE#WUY&#Xs;U`!Valx2Ba}8kW|7DV+DY zedp&XCFbAX>VJEy)t{|WvGRRz%e%;?w*}MRYQ^1~zWZy#^3OGkz9dfnvgz>K?`QPB zUD5n{QTfYpxwZQqx;DIvY0ny{(jl*+eN3Z zmu$b>w9K6OE_=$`yvc8JCcV*$yX#i+)j0EOy-vy}t)#bFNsqMR?`p;0(~7^N6?<1J z;r9Rk{}UfMaRK9;G0EHAWk%UWHaj4Py~NYkmHinzAD4&XcT zDM?9biHWJn&mTN_^z32kgU8Rs1w@3z1Vx3XPna@kTH=JK(8a0>ld$HHa9Rax@KT#YMh=Rc4gy+Et|G&+`75EproiQv7mHw!S@eeK7IT6^)o*Q z4_6uoXDL5-e@9POUuQ3OkH1HXhnJtHulM;AXHK0v*?a!9enNu2j+UOLuJ-yBYgVmW zxmG)2g?N5OPF7xKZua{ZZ(hB7nfl`O^LYk^lX`Y~*ME4T$r`-emz_ayrok3IyJy#c zK2j}ljVMV;EJ?LWE=mPb3`Pb&rL?rkei>9 knO2EgLtS5)K2U=s$cEtjw33Os(!enGboFyt=akR{0N=UGxc~qF delta 471 zcmV;|0Vw{r2Z#ia83+OZ008-bnr@SZ11En0s!2paRCwAnQ9Wx^K@`0+Z#P*tMlc}+ z3r#=>#-NSZgwz{Qh@GSmETWyIzrrS!pkkqDDw`CEYY|A1Dxfxke?UP9EWUlaZ{K@! zXYTRdfWyFioqIm+xW_M=V(hYvbO?~5ZHkEt37xh?rY1@PqM%`P+i2w@_wXJJO#pvL zABRJ_dE4vh`#0&%AV#EwB$Nan4@Rx2mQfB0kw`Xyv&yZ9e2VZZzc@Prt3ouQxp_wf zk8^H?>a9wbrCaU!)BX4T4=>jqWRt?sQKu>}RMsn{Ix)0BgvOPubm;S8zkhVR+D1{k z6jcO))s;FX&k8k*(8{_-=bHQblZSsHR2=dj&GZu0HE{w5XamfWvRWmKrkP;*j{oxR z)9#b-=2^41`}X>V)8?od5le3cs;Km)$W(cpedQY$UftRXEBASQ@%7*&j-#QO&9jE} zEdL1-<&9hGhMnySe*%6tPalQ53q=u$&pWFa_0%dW?Eo%cqKdIROTDEXOs+#ZC8S6* zyg9dhbmh>q%K%B1!S$Y(gHkgHo3!FeBKw>F2Iy~mI1)`+ zf=b9&mp7@quK&8?@M98TbP-v4V(9>zfR2#RyW%?f^=E(d*L&~#JkLGv-TS;<>n7># zOivF0yx6}Pww*rY4%NY|G@vdBu(SYwQIKU0FcH9l9N=CAwiqsbl^F}rW+kaP0{Gdn z%?`goFbA>uGByrieGfdkfi9pEumPulR-hRm!L&E_PW630;<)+AveSC?hP~s8!`ShY zq5XFGnIB8qejwGY-^-i7%O}6hZ5RX0jlK8g@0gaRDpr0e{_}ytIdLSXemvvot(59- z1p1MLGROYn>oLmV%-T_EN3!SMz^5OrI#sI=i=N*vINLjrQZ*W1=HP0tv6aI_;ZT}! z1kl%YPZhLG9Bv%TJ3g9OKb~4MF03Bo=||&AM>wR`!BSlhEgar08xmGh?8;zWN)tg*izAVOxJDJbeZJm4f*HFbK7-Ur#0y(6)Dyl^#y=254<4tFO(V2i$p6j z!5`9uWeNA0n7t&7_?;KB7#rZ`_&;Sa7s7qrsWFqlLtwd~bg@X}mIPTcGoOt`wWXY z9p>W<_I?n=m=Q({0lxxEM&+WRz+Il_E|ES_NfzYdxqRVVZlWtY&Xuw6H!*uADfCO= zK5!TKnYz#fFb>=Tz6NdrH-Kxv+S=M3<22YWc-0#lH@)_{kMHI;w!F#Qy6r8$?V$PZ z*!lLZfWV;LdxAsuhK7Npg+~yPtf-*qJ?t3HJNvls*dH5rAU=VYn8X(dlMjlJB2G<9 z&yZwhW#>o_<>tK$S^nXI!XmjssZwjS#U(nFmX%i=(HknOs*R?jHMM4x*3~!Yj~yp1 zjZMuhC#FqPOc6LfvIP|8N4OH9_ zXcXib)e57AtE$pa1uTNa3L`jSkBL2rfbN53X|lBdE};bz1ZP3no{R csOJFlUx#eH)>vaZu+<9?3DWtWCCZ!r0YjKvX8-^I delta 475 zcmV<10VMv>2blzr83+OZ008-bnr@LH6O*F@B!2;`NkljjhIVLa&sCpBc#BIrC8Q+H=JRZ-oB*`TytN z|382J{rUS3Nd5*hR<>*e2p|@)+5f%NHnOw)VEXgRTK2XQA8*O!*x$eZ{r(G7_xCqg z^?&c*f2GAZfi?gH5J>sI{|q~>>T zZ2$qp!tjsj_dmuxH+6SkQv+K1^Dpafp!@&+{r#8W&u@@le*a|zk$)I|{ss9HAb?mj zKkZQc#wYOgBhb-*|NQ;?=l9<~e?U(E{eSm2hzZmRm?Y2uW+tU5hOYnt#PW@SffMXnkm^6bfW8Bfe}Lrge}BNL zfh36ghxzZHHw+8_0mK3dqdy>nK}sQpfld7fH5+URQ1jovOn*V~0uVqf|G*j;#Y7~T zfPMt}`uA_fzaY&_V1F=xqT|oMe<0NiU=08P#CRJVf&XA611c8{0RcdO0RVmbVF4YZ RFtPvu002ovPDHLkV1mDg^_l3rPl)T!o8doihJ3#f{QYX+x2yhNFZ+DG!)*$AJ5u- zIA#6*g!!AJ#`()L3YKU5TbTB5QOeKzMXwK}AFV0hky^Gbp=fhV?uPKp^}(szQ{w6` zPV~DoQ!8Updi>n@f3sr#O^^I>FXLco;fbc~Em1jZ12We5rK}H%UEvnFGu?k(dSfC$nANqH)c4UZgN-`;I-I3_;j_# z&l@4h^OGZHMf{x<@a=ZOriiRn-WhN9I(@rp@wR@y=~_SkYctID=UOeW@;h0f^8I>H z+}!w(X~BOc`Tyzj{(3WdrB~9otLEpsoZs#zC_cYy1+HI>uj}?C|B3_LoztACD=%KPJ7v!giK{>(N5FZ&!Rmr-yn?@ch+o z|M_abN{{G;*7Z@l)7_6(TVI=|w>#bR?jrd)hE_AQ9S`S82Tloe>2dklWcBH?_om2@ z9cjLE4E+~axX(9no~>&?*T`Z=n*L%(nlv>rGMG=bHrH95s4N`7sz2R@$_|Nf5y(orOL9$KEnhkbkNhq zF@)oKasmUBn_8M!m{}R0pI)9E8(W-R9iyXSqF|t5A*ZKiCL5b%q-7PH`F}JriTefs`t#viKyL!5KxOq9hKbwBOe7t>qXTyO74<=mL z@PUJE+KCk}X585EqvgnwCsVF$DQW9Dvu4JdId}H_X*#s%(WFa8Y}-C{y*ah&)vQ~) zezhH2_DqcJ+qG@q`p&I;H}BrQf1JYVzG)1W`KPXAUQRzV$F%y~oyyM)S##8kzApMC zX9x6-YKdz^NlIc#s#S7PDv)9@GB7gIH89sTGzl>@urfBaGBwaPFt9Q(nCSR$6^e%3 e{FKbJO57SA$V7@wG*steVDNPHb6Mw<&;$T)*q2xU delta 566 zcmV-60?GZ!2lE7w83+OZ008-bnr@LH6O)?*B!2=0Nkls>{QLd;{Qvy?2od=jE$|Bn+yV%s;pgxF-+%vL`1==#{{LsWwv~&W_sgHJ zjDJ7B{Q;WF%<}W+mp@!0-yWP|m)B$h2q2(_|NnsK|6hiGKudoEUGn9{-`6*oEmQ72 zy~rUb^YQPmzn@?Idwh=T>wA{p3V#3shzY3p@87?_|NZ&@_cy~|pmRZP`1kJ@JI^@8AFa{s9OeCV!ysfU5ue`3=0a^f54YJ|SFOUsj4S&A>zrT-D)!_vL!!MBZr$1M= z@PmE-7a)LGfMNIhA5aNc^`Bq=|NZ*=ixo&eyTbnIg#f?w=cgChm8@U=`S}+ZXn#Q0 z0|XG*hQEJ){Q-fF-@sW*{oALjD*1!K)|1+`v{>kzB77xGVdv4*M00G4K z>6{}{M1q19i2i}R1dJ0<90Q#Jk{@OG2aJ7SqyYpN0GL#BQd3{Sp8x;=07*qoM6N<$ Ef)6+>O8@`> diff --git a/toxygen/smileys/default/lr.png b/toxygen/smileys/default/lr.png old mode 100755 new mode 100644 index 89a5bc7e70711575c1ee3b83cc2be7f0e1fb29c5..24db5a944e3fdc6e76a0c0c1a038b313782dc8b9 GIT binary patch delta 731 zcmZvYX-Lxn7{(uy{vM&yonNF?F3pt9JaVb!v8l~@jHyj!9&__tYD_~vG|ejsDk;q) zOAS#GwU|dH)`t+Ykg{cSc@DKa2w^&>^XgyUg5ZIN_xL@$yk88fi3gU(h5)?WcMgbb5KGqNwE6?(Xi+4ym2(?d`3tExlg9xw*Nqv7yuHmQ_mG%8E8s z(mbD&IhQ4w%}iEHL^DaT)1v69xQK~p?sx=eI$hjBCdw6c^@HN%#`X1etyVj)7y%** zOL|h$n#4(s!i0v|j3jkh!c0=klrU;CCVV_H^m|00itDfBcu$B0O~BT#<+Zi7)z#HC ztyVoeym+s8PMS6=%ScqG31<>XEOJs1HqH-Gg|Wv%{JwF#NBun%zHVbNVfBD+c~PU$ zjDH+jD3+;Hg)?HoR6Kv;&wj9q$5sZjM%i9p{X7&t7e_8TeZE9j@`Gyu?az6QW>qyf zAO~837N8M$4%7fuz~0`Tk&7(rCi!uLc($Lfq>n%wSr|zJI837-M&_p01eb1df^0Ph z_%nRC|zR9mIds*62)mzk9-ep$M23cTVMR#p~!D&PJL;2m-n!b{z z$36Eup2^@-_rf^Z)8@TrWxn+pr}OTo9M3VRE)Jd)dMU-y#)60NvO`B6eL%LLIx?3f z4!^?94l;*bidDJ7V$*><+FHuICQXQwiWtHhBGTYUb9A()x!Tj6cyt=$f-}RJZpWa} mNV&Q!S^Yyu(i4)!IsYa80<2VSLmB<7DGb&SGP&v}5Ph=hemk|NH@>e?ap0@Be>+?BD-@jNiXmBqT3P zoPPxnKp+i!_T03wQQEla((gZiC0cs^;{c3|jAkj>009Kl@aN){C);;k0UG-E&);Xo z*&wq)roznr3pD=Ezu&)DC1p;}S_BY4jKAN$W)>8Nm;toyKW@Ot#Ps&Y4S)b*xg;zq z)7SR<*)x!NAa?^@4{|ZkY%l|8FPQu14}7b%%*lnT0Ro8e`|FpiU>ooo50nM^;l+(> z00G2uNmN|Et^ViJCtzbBmi+n$ax~Zmh<`xjUv@dUW6L)J1Q6rb=TF&0#L)bJVH-#Q xh@W4*3=lvp=Om^7pho8()Tl*f00BUN0RV2&XAPI9!jb?0002ovPDHLkV1kUF%)|fy diff --git a/toxygen/smileys/default/ls.png b/toxygen/smileys/default/ls.png old mode 100755 new mode 100644 index 33fdef101f74e38e2422bb85dc8a31bbf1da326b..e4e79668bb6c34f8b607b00d1f4e0b74543853a2 GIT binary patch delta 849 zcmeyu($79Yl7pFnfx*${FZV=6;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z_=>FC&cyb+qbV@zkd1h<@4vypFVy1`0?ZW_wV1md-wM3+j~!* zWpM0$czOHxPp`hde|rD?#y@|4|NZ;#@87?FfUMuYfBpLP^XJdLeYe8qonT;0{rUZ4 z{l#6qXErzAzp&};r!Vd^j_J1V@|=E{fjJXs$>qJ%4lK;xw;K!rr_02>4C)Mz?_RyMXUf$BvrlhsJ+`Xy$G6WPzkE$!e%i46fOgw1)w-<;y;1fn zJsF%Ceti45cW%ntXV-yFxcKmu!<54Y?Yp&Fcd6FbZI+*DDm7ctdaX5sHN(S8+b`{% zdTH;pD|@GY|M|0U?FEaz1N!ZIv|9J5RL_u~tt&ZCQe?J>*$OiTQ- z;s5{t>ryHo0%Mdh$=ltfSDNS1RUn7G#M9T6{TVwSmx|@zdLPjgpx9nd7sn8e>&XcW zOm1#!YH4C&W@UVSdTeZYa&dNbjE;(miGqQKg`8|`o|>7Gj*bH1VSaIr6CFJxT|<3k zk6gcS<y}k`+N9!`FZ*#CkP68ot|g^&B)2h%gfBoe*Yrp?JK=^oa_u2_iJUToeW(L^oeSTYeY#(Vo9o1 za#1RfVlXl=GSW3L*EKW=F*L9;HnlP_(>5@$GBB{SzQV@gTe~DWM4f8-#fl delta 567 zcmV-70?7T32lNDx83+OZ008-bnr@LH6O&;BB!2=1Nkle%Z^E+rjEqc3%0S@D&)-qYExxCp=yQ1PA~B|JUd6PoU1p;D7M>`uh6#`T84x`VPnZ1o8m*00Ic)hnIJb z2})@&Ffcy9zK_dBscOsh;}1W42b%hi@z=MzzZItbVt(`c?{6Sr_{9Ja05Jg0{{;U2 z{u(Dh87M#j|Niat`CX#Q;PCtV{{s8^`T7%_`xw;v0`&X(`~3d={r>y_`~U(7q@0a|P-z;x_{re5{!k>RY4S#_Mr~x2=KpI|u`8M(3 z{Rbcai1Tv(;rRQTXZ3H6n|~Mue*fkE^Y_o6|9}1h!T-O1ff{}>00aD0JP={aA97n{!T{+??e4 zf``aws3E}s(o)t#&+ep{A`2v0a^QFjP;&}c4Ma6=pqv0aT7Y*R*!DsFjRPq_kX@On z7Ojn9Z5S(q@CN2SyaV{V4}bOHSudXTVBsd7bYiv(>$9~!PmOQpgm0$W=dSX(%GaDn zSEox?93{)PqU9;A*ZPjvl5Lv#9{v&d=V~|HB^xgNhBJSCI&a;Pzz~$_hZ&+a#{uKn@hsYhz7SvmPoW*HMtjHa4L_MpgiQz`3^iGL8MwWaY}Q@G7s!gPgTG%@Ix=#dvAg)Ktxfohc9 zJErPVDLNJ6c5!N3D!+vv*TkS*q;0R?&cDV76NWy6;<`fGAr-ZY_^munGlzMV8P^mS zeL0%eNTYsD4Zjf1y~=F?%*T%m=SYVRi5_GL?`QG`BuV}0%sxTfoxRb$Y+6r(iF%6> z-W{{GPsFMO?7*U4zmSnJFBH!4c(X}Ko`eK)Qy`d5PIhoOHWtgqWKQkg-3dGb#(_~_7#IQu z0n-4`58MHIfm^^$pbPN({YPfTN&~+TlrP(t(U>=KyD=chMyXsxc9O$3(?dEwj-~~y zyV8s_b;x*uR{P=$G5XgIaVx_1C%hj)66fW;bs{Kjr|y*liAOg{V@V~{!^u@IiReYI z9^{n2ERQ9{m%foJ_|O#mc2ogH#Xc4)BQf6Fk;AVh@7qA^eokc<4>qui=S|%!pF4vwaEWty zaffRu(1>mnX^e$BV-c^QtSGR+B3P_=Vo!Wxk~)##u{k^ri$M@Pf*@I**8T@5H58WU ktNtITtd$4@0QKJsDudowsVh61`ub)-D%zjXnyx+h4>q@mrvLx| delta 446 zcmV;v0YU!H2mAw&83+OZ008-bnr@LH6O*9>B!2;pNklpql^x|1$jl$IS4T`TswL|9}3&5m4d(-#`Y#zu%uO zuLKAn78WK321cNQXN({XAQcS%|3ajJ>cHsN|9`)Mh>=wqXf8kiv0VDXAoc6d?_WSO z{(nM@{P*uS*kF*NfB%2|gE0R6VrBnz{uKj20I}%rVJLI`{^-k>KY#!H`S<7dzuzGA z_xCRl`Rmu;Um!M^l;`6=xPSp5fLIuQF#P%V7sv;y25A5(1xW+7{Q_w~Xakza@Pz>& zfLMUe`uqRSpZ|Y=>VQIE8-4+ehiC(l5P$an-#`RZ3=lvpYg-v~|Ns8}5U2*E3uxBA zKM-YLl@RhTQ1Ks5`9GU3G5`b+ix~q0|C>L5UxJJPO8@`$>p#c>Fas{~=lB0Ve_1&H zdN42m1Q5$Fpp$@ROG*Nz8UFkQ$pIN4_8%b02qb~-{ri{k@83Uv|NdZL00i3J#DNGHX8-{P03{-KL-EqxnE(I)07*qoM6N<$g8R_b-2eap diff --git a/toxygen/smileys/default/lu.png b/toxygen/smileys/default/lu.png old mode 100755 new mode 100644 index 4cabba98ae70837922beadc41453b5f848f03854..37544b4582230356dea64b8da4f7aef1d98a59e4 GIT binary patch delta 851 zcmZ{hYfMuI7=}MYQv+_yG1r*IIe##+EF`B@Xe>s<+6q;0Tv~64^o}6d3N1)gjwlc# z5h=n7!a#{^I-TMwpe#b>Wic8v=iJ;6ibXe>nnpRN=YHGyyC3`hy!qbueUdNvX5s^i z_N5Es0U&FQkQi8zjDBLzDa-@#e+qD@34oQOL;nEy5P(+}0Btvb($hCyum&J*G(Xp5 zkThx1XUl}tkAxLUVOf%(Oym|XU=o2^00aOIFbD7kAPOL*E|pIAiv2y}*FmwTQ|Rsx zx=!#N$N3Y-xYlO&=wYTAMd?OA)lm0ZZZA)kdb`9HRBSpRHu{BwwSvEzuXA%Xm8`pr zsj$dTfNHtJ9l6D# zH<|sD%)T46??=jWovIomD@F)cC}zKcTQ1`zm$2fC7_1A&H4{*egKrMpZ=gI1&M1_= zhT@lCo`s^nA@4aDXCV72=)#Z{24h%~BwdNERQ_(9ZDY1Rko3;PDS1(#USpm?@j~%# zLW&|KV|APCg^cF9!S>FMudMA| zC%5FA3O5&+i=I4u{OAws!{2|?Agk0Fs`Qm(!&iS8wGNMjLUkT*b?shX4fQtmc3#4t znKzl~wG)d*$+ia1#3WhvNi^8G1Z!&a}u>UC=E z)lOMJji}XWh$c<7%A`WHE7P^r28{QLKh z;nyFAKRt(tW#8ZZ*sqhEbDq5CJ@3G_ zF2?C#?`;4Qg&V$Q?*y3N)+VRz1&)LPrJn<<^r+Mc6cNA_2?#C#K?N0UN+IB7OI4^O zWxz?`OW-r$7;qFY(d$4VU;sV>v_KY+4SWIgl$2Oartae6heqSC1%>wwhOWH4pDC(? zB=2c7cQZ1~t?B8_DJk>?Jtapvv$H!ins&9iO{Kb{RJJG-O;Ty2NMsSl*Yf!_Y<6{I zWTQ+b2OgMAZ5quTwYo*6YE~+5$>let()vWv4WaNSery$+eVNI;6c$z|7RLiFtJULh z{A0JzkB`rfjm?dX&5n-FJbgO-JBNlQ2L~tm`&}a=){7T6Ha0dlH`mwK z*VfinS65e7R+g8SmzI_m7Z*JqkK65DSXgj-JQEWWuHIg^!!d8S&pv-X^X%En$B$h$ zn{#;BF*GzWFz}+U&p9&ku(Glk_~pQXTQXULL{guSP%98z=W?&HSXUy<5tX}lSM1#R zeQ@ykz`(Qq{#6_f6X?|I8^vNvLP8CfTOA#JH9Y+Cu3eWxLVgSmz7P;_Zo6N(ukTeB zD<9~{$-S4AbvHA!Rkg1sBu1K)V_^&J7{PnUmpE{7~XdnO}QmG+@>U%IXTk3l^- zFfcsCC%BwgHZP{Np*h#w(9(Fjskp#+=+nZY!%K7S+_}Yh`pXO3yN3ppoHX(@N33zNKK?n$fA3GKDAHa~W%hQ|wA4ocwcBPEH#|Mx} KQp6S!dHi3&f`xMc delta 402 zcmV;D0d4-m2hjtN83+OZ008-bnr@LH6O)t!B!2;8NklqL{Q`2iWo3bu0|XEY&^>?t{r`=u8Ym4k z6@RQ0gn$gN2Dab7KQS->1Q6rPlP7uPF+h2 zyjU04o?EB^8WREcIp7x?xxWK-1n|QGP&a|NQ@3B}r9f1NF5jTQE#NwE4d?*cfeXNS z;4IJt)C1Lk6RnG_p2b%8qPu0GrD?vYVXmR>OFfBr1JM6ak&6pF2}JQECVp3Pua^0(pvIh0G_ThA2_Li0C8u zJ$&9H9z8^Z-R&}J!Zk(XMOnTqYLF8436p!1xsSLB4-zu_Goc3BP&Hen9n~@cM(&rV z^`{A+33oo>Cv{xV=E~R$5L3%&6fxgo)~)8p@Wts@ zSj*4IX~W7Ty9H6o#EMPD;xp^CdtE0vobBZYTB2y4b%UO)Ud1GFIF<3H^oA7*zGI_N zRJT^c;iQw(@cYlf zYh$lJO)W*TxWw-Luf%D4q#j;s@|I9X+uAGb>ovLgc@?>gMjg4>7M^uoifBXA;nznj zg57(P_a>zXdb=M#?0N9Sj*Nml@0Y+`@?O%2FCJadNhgH$WENY5U#lpsJ#4LYQ1;Uf zR**`hrlt^*6f(^~64X8sB_ag`K@o&R{A2q+fV1jwWkthZK=24Y}BSCr>k2wdZRU}iRx)_ZDKCdsiP+ccTa0|rF|efkO@kcR&Z z|DXMM_V?dkplXN;h$0C31tkCe0y3mIrGYj81Q19A$l!l}A?5)YAoKqH`2|$;AFSsu z2!H+h_Y0^1=qi8!VgWgY;V;Au5YFg8g4-`_wR00IcC0c`wlknw+DNZ2$-$7NDlT|NlUo0ap!i1F~k2r~my1Y5)izkcK~h{{rRU9);Krw*=%9 zuq;py&^Z7B1lI8X4~o-~jQ<6)85pKOHccoDfr1S$dA z4+_2iU=9Bm00M}Sp%En_p(KRy4~Ywjas~!~00RJjD3J5{9=O5)0000O6unTkr5d;+*=|zMlOHD-$U1F`?%xPuo z!L{`VWUZy^V>w$-ZPse*>!tEzeJ>lI7cOw^`q)EHXXpHWzn$|t=iFtz-ap4lV*!9d zmdKbd&GnYpkGD1hOjH0&Uj(2SX!<+AC<5@406=d56r5RnWRw9U4HgrHM zMHs`r0nlS(w8fHWF$9{_Yom(QA(^jULLx|r%MF&4gyeF1cqCym#amk9P1=}R{i?Td z`CR=ykL0-p33`i*1BHdb`uZ=7t---q6CP8ksK&-{1G;7xKk4TCYpO!!)cws)F2de|C02DE!P2oK6bEA|er4TN|#d43(7yi;DcY zxxRy0zKje%pC7aJP$y4DEtaU++ZHhxNUfGstHVlVsG%Wv^k_gV_6vo69`7T-CbOvB z-DlXtS>`ZFJz5PmQTO(ei%C-5c(W??Yid?HH)*|X@i9|+fMW`C+Iu?z$?6iK$7yre zzispq8(UjnH@|EbWak~q&td1DBIf3xXE>bm{2?0Wi0KOexPegVKWZleVy%|6xIjN;+Bir z@s=+5*ladg6mdU+`Trp1GNu3m;gdVD6B!GH3E@YfS^ajsHm~$~=I^E;8c{wK?09Wa;B_(m;FE=JXZ~y=R delta 369 zcmV-%0gnFB2CxH=83+OZ008-bnr@LH6O$tYB!2-yNkl8RSooaXC46r*1+)p|C6Ucr6AS6 ze?v$p`UNC^{r~j~$Yzw41=;`*KrCRh|D&k>_3IzlJO+@uUm!hT4Gh1312q5y5DO49 z0)JHlRs8wG@Eb_}{{H{>S-s!?{`@vR^^5K2FR(pO4M5WY0*DFZqCZG(`2G8?)UU4` zzrJw%x*-cw4MBhY08Ix7Ah47E{sH^x7s!U+ztwL3`tbkv-#@=Jum1jzWCJ7ENdN%^ zb`n?!9|`7w|69Q z0YD~YQpR((S+WhsPgVn%1OUU=0I1k&cpcy(0`Nl*Ab9|g)&KadRtmt8)u?rfC;$Tx z0U!Zz01O}q;0M?Q*Z_!{&Fs(+Yc#Sb%JiRS`ugbZZn~o*qSsT+&18K&QCo{^G`_!1 z6WiKqVVj%T#zsb`W3*aYt&UVyQWX`X^e7<~<04TwKOa}Aya04n6(x)5QbJrB&db9D zg0Nif0bmCP*oFq?L`_7gq+~Mkh?FQT#SsMK^F!I$SXmhpAJ1B?jLAeB2Wb?I^!HOe zJ!DrGVKCq=En!Pj6INFj(rSZhb*Qt`4dC1!~IzmTcLs1k-k_18EI35m%F$@cZ zLcw4#5C}M(POsPNa5y|3kKgaN*=%mNI}ix2t*x!DuCA=CEH5v+T&|_1rNza?`T2Q= z(_y#UqtPg5PEZr;nbTHPX_24dx7|yRU5TiwT9NoYF+F*|cxUL4AtpUn$z{4Emts^A zH&f30kYF?lTfTffm14WG-4(SAk8rP&%%D#(mgpIhaaTOz9?dJ(zO(^DorA zu$T8K6N~EjMWImulci2HwQA)%pol?!5x|L4@z1mT`SC#>FjzIlQ3%% z{N9t6nG=i5M->fd`)Ra8Vz|%|TTqA;777uuP*kiFA(EmJNpVRYf=Cc#>(ZPRpx_^( d?_7I#=g|L&%QpuK#zhB`0aOZ&e6m7+^$+{BP~HFl delta 317 zcmV-D0mA-|2K)k$83+OZ008-bnr@LH6O-WrB!2-8Nkl8RSooaXC46r*1+)p|C6Ucr6AS6 ze?v$p`UNC^{r~j~$Yzw41=;`*KrCRh|D&h|N(0SV{CZ_AxuLA@S%fEmBAbhajaRP`eP%%INfiyrk1T_G`pFe*90tjjY zTn_{=GBPrt03a7C3lKmc4KH52_yY$2z$O4<5`+z6Fn}PC0rUe<3LwA$<*i?PrQ71I P00000NkvXXu0mjfa^`)2 diff --git a/toxygen/smileys/default/md.png b/toxygen/smileys/default/md.png old mode 100755 new mode 100644 index 4e92c189044b7ec02a5b7a3a9460e1d01b354801..1bc8b47064b71b56123a85e2330e60d0ef10c3f9 GIT binary patch delta 897 zcmdnSvY&l|BnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34f$>U!Plzi6=WYhp-GA>h{C&*u_cFupiwr++Gkm|vaGZhRC zK&8_kWj5VStG|<6bNlbRxWAtRH!h4>KQH{(E8m|lJ?;lM-}AP+>t^{dA!IYqb?mzv zr#_0Wy!H2O^z$2j7xx9NoECKNg42%|PWQZR@3>jqb~L^p9k3Col6iMh&F%Qgn^C1V z{=5lWJ|+B7q|d_;k9pl5-=EpsakaSRV05G2TK8VK_gbK8hMm!JP3@g0$`*q-w zi9zocxV@d{IIG?H+Y__vW*T>c-ByB>ZLR3LA6arewD4MB&Xr#;y%+TQIL4p1i8?#I z+4k#W!yDE**Ui+g87kfOcUlfozqzFAZb-p3|C}p6>6d=KaQ*SjVOE>d^d_6Hj|{#% ztk=G7s(wXB_LjH(Vvy?f`R#YT(l5CsU-dJeK?XK<%o5(q&Dl8y?mR zLCRKVH{VXLznNTpBd+4wkC##3Uxa>t9`Nm{&(|lmUmm;M_H(@DZFj@fVm?SQ(Ai5F z7=ZfbGBC`1dz|6*NrsmP8J_QFc)FLN{_$>*3w? zOFVsD*`KlVajCMNwuo2+6guqb;uyklJvo7a$xSUyEX=Ho&rdH;j*TrY&aRHpQ87_4 z(6ErxQ)BlGRl;RW#JJl-HL{H#gE!HrBS5*;zSRIM}$Djl0fUyFObw+Pa$E zT|Heq+?=hP-#=SFUq0UcKSTQT0}B$`CS2I?q2t7g7c*|`_|bA?NlM$4D_g$woLTc` z&YeAfjx-%oYI`*4%cV_gK6Ra1^=ekrtzA`ZZO4{9n|5v6x4v`h-p$)}&#vtsr?9&3 zJfqTEUnGswMX!GU%E%BOY5Xkn!`TZA3<(UXC9V-ADTyViR>?)FK#IZ0z{p6~z+BhR zB*f6b%GlJ()Ii(7z{V&2 delta 504 zcmVc@b4ePKQ0h?L7m|rNCrv*1Q1Y?p`~9PNdEl?l4caH{{R0!1MhFf|9_ePe`fm6 zDqryD|DWIg|Ni;+`_J_`%K-uiq=Df-!_(Kl|Ni|CMF0OhW?=oiWgYjwzrVeGzWx9I z`+w(y-~WGu5Q~)bF9rsH0Ad2#1T-6>`p@704FCVWdiqyE_S>%?zn?t&`v3p0|G$6z zgQ7n`R{;bN6VP4%{xkdmsRoGvjXbc2Q*ib#i+$e?>}LD_@7KRyzyJOE_4n5=pu>QE z00u!g^X8F}1){Re7)$$tdW05k^dB!~up00Q~}s2C{n4`j*je?S#~ z{xAmb?*xiOYZd_YLsf%RfSd#nKtK&3#_vD>fd>5rYWV*vBJ%gIpT&Rw{ECbJ0<`D< zFHk6fwEg)Dv;iQ1KsNmT%K)|(q!Xlq>)Bq4bj0_Ab!7&I$ uZ;z+{0|xaEa7=^IAy-fwLl6)E1Q-Btx@1*Za@a2CEqi4B`cIb_LonFfeWi@CkAK|IqFKeYgMjT>sy7{eR2({|)E= z*PQ-car}MJ@z({%Z|Cekowob`*8Y!^`EMulA5P}qoXqRLIGKNPGXLOY{?5tljg#3c zN3$1>W}o&L{C{o#i-qeu3)eRmt}iTHA6dBGvv9p(;d;fw^_+$C2{Y$IX3qP}oKGFi z-tW}^|I+>|3)g2Bt`96+?^w8A%rSnk#QX^Z+hb$6IXgWT;(b=Dftr zc>!pvqv^wS+W+s{ykOyc%EI|_kHfPa_LtYV#9C)uWad22%yE{vp5un2>AkgD|8HBp zbuxRg$NFBm_TxPcH&WE@%`rN|%yEjDn9b|MHqQ@QJv(6ac(3KdJr?(OTHM`XetWz5 z&8?<4fM~PnwauniH<|wb|G&Pax@s~oVi}XX-Cg>>GBy7Ma@b2eeO=j~vGZ}Ma%}BX zkp~K$@^otS2m&Ya-7iVWzSI6k6m?#)%Sjfrd8K;>k8EIL` z>Z+P58fsd~#%m|(tD7quYg^0gteh+y%w}A-V9Ua>KU1&E2bK z?`~f{eS80U`R00tfbxWZ0*{P{3YU}+6X%|S9GQUf8mFY7BCo6{D`z*g3kA2=yqR-n z&y+t+hm_iq3>Q3fxa;t8Qq!ePMQvSPTUTYV-=DSO)}ce2cI~QaYdf~=*|clhzV)43 z_io<3eRd3S9}T<9ihn!|LDQNEEKF;yJ2H6aT7}2kja3$|CW~*5VQ||WwWQON zw1k1cLtsJt>^*+1v@Rzujw*K_{d{_(z39T|DuNY)BlJ)eU#fIk>z%!#jj|`tjrA4< zsNV`|OQ?%;scpV5U#rr55NPkvUosg(&S%-rkgZn@#IcZ!!@Wk=4>SV%+0Ev*SlW`6 sNdh@d#A;IbLBuJ$rZ_!9=}&+G06}UB6-m#2x&QzG07*qoM6N<$f+)1UYybcN diff --git a/toxygen/smileys/default/mg.png b/toxygen/smileys/default/mg.png old mode 100755 new mode 100644 index d2715b3d0e11b3a92c4f33cfad6b4f3488d0310d..65e7f277ee5c7263e99c748e8e441d854e20c00c GIT binary patch delta 820 zcmZ9JeMnOa7{(u4O-azYu8$QsrETgq-8sjdEw|a)*hgbJ(k$(J<(yd9$Hl&(MQITP z6e!l^xXZq`cvy#nF>jM*II zTsFbFcE+t%#%PS`^ij1sLJ-pu$@i?RX@emg4zny9tlb(YU+s75-q_Wz&BU-l{!%M> zL5rS~%U(Hk*SQQ{HqL4NgDPFGmhPdIeM(u6Ong@?x-AlR2n9C<>OM7W2!%on!<7G9 zuIf@LZz*LynYdjnx*-y_3I$gM3$83E?4&><7!1Z@vC{jc1)T+XzPuG}D>83nE^QTN zT+K+ol9qZYRq0bA9}^Q3?niFN-wtb^*J|oD6?YdGbrsRKX{v)#v@4K<@$vDov9Zz7 z(Lf;J_xrJxD-19I#Xu2I2q=L(fJWFKzQc8vw9{cTr;0`fpKwcZgT2&}{2@7Qz9i+J zvtki64WA;ZLFEOP2|EUu(G*B3i!XZmz1@v@!7J ztzWlNTU6L0-`w9n@ZYh+M~}p#Oe~q0yQzPGKkrQA=_Z@mYI!zD4Q`w}ZzxABmE7`m zdOK>}9@71QKlCj4{=>u)!Qw@=mz$~POO+KH){hLo9)6`!d1#sk#iLxEifr4?Wsx*k zsv6uo8fvuFf7WmV6hTq>geIR(^a4^r6L`v3JChQEIp|NLS8^M~p8@Be@P{D+ZWHf;h3AQp&u zP}M-j)29s2pMz9D&@UkQ>;JD`KsKYSEYJpk0AfK=4P*dK0!o8Ouu_of-w+K9zkdTY z0DlA!3((yN)j;^`52o304M1f80R+|X9}Iwu*X+N)@cjDs2c+lU?_dAGBv9wyKfl@e z{~Tjr001}1@4rADVDj%Tpgtwu-=}^u00a=o2DtG+^6#Ht zKYxRyQB?o^{pa7Gzs!FaUNHa!5W-0i5^ruEj0EcZ_2(Z@!(Sj`_`?7YKu{atdc--! z|NZ?3^a0RFpoTwxf#LP%FA)6y`xmI;7Xv^5F+P6$_#YVj`SS-zs<)^!JOM{369dpZ k5c&ls|A9#!27mwq03@(uAKja*cmMzZ07*qoM6N<$f&>+{>Hq)$ diff --git a/toxygen/smileys/default/mh.png b/toxygen/smileys/default/mh.png old mode 100755 new mode 100644 index fb523a8c39d40401b9abcfb144a73cbb2d76b286..67cc066d2b9e2a9316fe562af918b56680b4cd0e GIT binary patch delta 903 zcmZ{iYfO^|6o%gp_f<5*Og30UCK^+T(Qc*Xq6MY}O6|&3%B15W77Df0r9Iq}4pE7@Mjd+wFevXt}{Ukz45i=zyy1>`11@k*c=yPCD{SE)1Q{TkDXo&fi#m z`t0iXuOAqk`_o%qi)oC?xdem)3e_jW0#$#Cto;I6@y}(=+U(%W+@i5vckf+!*G@qb z?Xc;MgwKPyrUSV?Af&kdJC*w4T}z$&w(Z8PpVh|ki1%A}3z~Ku@y*4M#PdO1)AqQ= zDDjuR_c+?F%zp2*4c=8Z-b`2b3gumVS!cYo{jj7hrl2i4_fn+T$~tBR0{IDwZF|cB zA;1F;0S9#e8(;uo0BvJqqrW0z!lwX5BIGAph@lnVT|m_cQh3xwYQPpcWh%=&E+(8ie?^IK`yH32C5w7+Z?3ED&O?*)MGLJ5OJu8xY zC51thz9|;x#Z^iD5_T5`X0mnV#8%-xMMzan%o!>_tR(nYG*PiF?d>X6=*Pa_D zE-wZVo|ys9**mru%hvf}uYX`zc(T`{r&#@{Qmp= z{rUX>`uhMT1msi<#Tj-00tlo*LWs-5M)CiDhAVg8T)y}A@1K8v{sNVP(6`^roPR&X z@P8Z#;5fx9BL69t0U&^Y8ve1fF$1+RGBU8SF+)rRTJr5TBhX+o)+>qpJCu1p{#SAS zZ=3QjbPhlO05Jg0{{#T~{M_a8tGL(@4F<^6===Qr`uzX+`vVjI>RSuFND|!tCprH% zUjG&-`Tqfgkh}r_F#yj01OWa1_W1j+!++lU{Qvy?{rC9+{Qdqz1jSwx$`var|2mid z5-H;A`Ln9h(%kH%hI9f5=p>+mf4_hK`~COd_wP)S{~yN+p0G9G{cl$HUC`wCwRhVW zA31R9(Tmq#loW)3!37XNU=2V^elxNB`WnD{A;$18yHWH#CcRxJU++4+b2=wV6fB*t&`1SqAe>J8@2_|pU49rjc(%!V=&EZpf-oO9)`#(^{edH`|>*fx6UnnMSq{cUe{ zngs#EK?ndR#v0f)&;SL*fhe#EgcZ0KhTwxhO$onD@iTtj=i|LzZgh+bjii1XObxi% zo*u^Kq7Us0Bv;$1aLe5~2y>JCm;3oYUHnWZ=X7!oN9tNLyVArMjkK;VIb1{is7NeV zQf|v_fcN`(mx~W{a@U>Qq9f(7va?OhtdTbAl0gl%tVncM#LLPOCewzfQVo7_f*5JpT4IBraJIY1dQyZ;P>BeS5V z>DpvcL(LJ1U`6~>S?uSc=&gcStag1JoEF>#Pim}{?Xs{AGvn9OC&=U%YU;W?;VF;1 zOJh|<(MoB=kRLmuSp!%gH8jLpEbM@pxf*Do&ydM_73GsBCd%Vgr7?d|^s+Rf&yVEh zMwQCn#qHw*%vBTfYXj{klPVQeU7h%NNBonLSYcsQDvfmIZGM}xc|JSRB&Td(|LJ9Z z=%zg#v~zFLZl|nP>ifpT4P(5%E~eE+)#`{<8Cj7>)+$5d%i;(`Z3qMRfV;r$Rcw5P zb>I&mlz~tPf*|a7&(~;R3Occr7-wF%T`I|{aM)$;K7`Savy#qFaOcUwmmK_c;ZQwP0cPXiCSGXIzLVs0@l8+%Uu3&{J2WB>pF delta 603 zcmXYv-Aj{k9L2xSHp@U0Dr_r!sf5HXT81bTZHon3p;l6PrIV1}@WLAxR?ntnCQ-gn zTNY_o3Q3DDx(MsRMno@bMi-JWpVaiCs1-}welO2yQGdWW=W`BcdSx)9*OqA_qLgp2 zrEk;PfiZKDYAUI6yXw5tyPr?2^4ZsAJRJL6mnVh}^ld)uzUw{J?H){O?dM+4EjG25 zuD+18&<=VB3weop+o_chGL*PMQbI^#LQ#adS(q4y5XjQQJ8Yf=x4Y7OALN);%kH^goLTUqC@gTO^#EawMKG zLq@+Mh(6qodj#7>?4uj(x!*_-rj%Af$1E*q6 z+U|O==JbA1D!qZ5Zg}K9yV9F3RJTjc%7}wDThObjnRHvHu1(2?hw zX{=ZZcy`*mtmz^PL)*ze)a2y-G{v_+0)56=l4I! zxtH`=a$j~z5`gkDIj#T2On$U0KQ3|W5Pj?)3y=Yl4y;`D`T

uBBZRy>J~vk|nL5AQZy&eU`bTT6m#hy3MQn*iOC^aAe^>K4Io!H+d}-;t@`N9r zZ#=bQwU)~kYKRK)3NuC`dPyo0#f`$$dM-OVoR!5J46_REMccJD>LRt}f+@%;tXL4% z^LKN?85tZ&PAi}r?yueEi)PcH$#~AV5cU^K(k4+X5QLX_{sr!Kb~rP0IbF4!mKH85 zf(I>Mw(gqP%+6N&e|krAzi&wo}Oul2xgBsZEC{oie&+ zF_+URQ%;6)xmw*VEb9#1pZoj4410ezur?!W^_pj&%lxqK)cdDfm{T8|Y_QSwJL`7X zr+&HftD9NfH+Ado?S?u%afcucT9zSoV?&pRN3}yEy5a79#Vq6N^mTOYmxcsCKP-vM zg7Zmw%G0T;r=CeWahN}T^!O3>@Uir5Rn^;6C#o#(9j&q6WF~*S{__t%-6-0i*|@2o zP<@j*d-nXf!GQ~(d-lB1_GW>n-MQ!S!o$T!!KKg{qNBdDsp)wPyhfZRl3YIJpFA(+ z@VZ*OtkK!YDg`7#l7&Q7Nujow))GdY-l*593BpJapA5uH{*!3;xc0hx{!RQ(;1>Vt Sv3~8#sepy5F%MQSZ~X(v7K9N1 delta 412 zcmV;N0b~BB2igOW83+OZ008-bnr@LH6O(xZB!2;HNklBoF);jL zcwE3B#Rca51EVtx|ExhWa0C!QEDT>5s$HwWD*pZZ|4)GZF9QP*{b6MM!_54LN&ff$ zm_Pr2|Nj36M1I+{2_S%27+8Rc|3CTq^zXmFAn@-u1M}bi|9?Z#uiyV~|N8&y7f>~$ ztbZ)f27mx!VE|eEAE^2d5dHlPWd8sE>;L~hU=6?i|N8~f1J=Os`!`SnKmdWv|I6_A z575egAcQa*n+Bj_fB<3vdK75(@4vtQ{Do`yh0yRHtQw*LD9^wE5I`Uef5B$|`u7W{ z2T22%M6!Vq>?D8y0%`dD|M#!IAl1MB{(t(1X2ZYVaN~il{=>ik5I|rjft3D+Ah2p= z8^9X=05t%83Jh)rfB<3v#@g?He4J$7y)MTgz;wd8t^dtW|4WR+-sSv;wg#%X5Hp4i{%<>#41$|GxkK`)%L1za#W}*cryk zPyomgEk>T*5^G7Q+@r|@IFs91#kmk0KWm|0gfMccAT3V8Nu(~#TpwOwYAQ=>gixrRnTY* zps2sJ)K^ksp$ZGh{QMsPR;$Zq!=|RF$w{iO@0VI_SLcog6bipY;uB?4d_Ku$6HF$l zRDP#VY?`sz7VLK4#01%Ub6%yI$W@H_bLdSG#pe-eY2K6+LMojFIMO)N+%h{jw9wr% zU!FJ@=K*;Mh3BChc_|N6K{#fV`H&3QCMUo&SC{B^!~CkpHAn~ z7EuKSq)J7~<%C4y&Cd2@Wx2UroWsGHOgq3AfTg7+*DFggU08@-rI1=r4J=9NA>6FY zlZCs&qau>SH0)IyY3q(snxkDgUWuXxgWh1OFjmffc7AnzF``b3V?%UY%=)$KVlSNO zXggnlcART3DbQ+))kTHlZ{EK%q3_?0jq;I94v#&C?7ZD)K>KeE_YVymsWsR04%Iab z7|;fC!Gjan3T!bLx{PtO#kyPa_)+54uFgfh-tm?3F(#(Htx&TiIOtg&M!aMuh=tqu ztds|r`?j|Jw9XKLAqU?*3=Wv3yC073Y?x%hv8>Ka3psque6#9u;w TK+}1ay)6PjDa)7kN>J+`qsv@- delta 421 zcmV;W0b2g)2IB*e83+OZ008-bnr@LH6O%gwB!2;QNkl^B&`&!7J=^2?@8009Klz`)ANq^8O)E6eupAH(jzJB!i)2Bb*-v4F% z#qjUU$NF!%uihy|#L860E`a7CEf{s1-nW%&CS=m&rR0%>6S z{f9wP5^T(0hCd*kj9?=fAS4j|{R=@r#Q*`s_!TuG8L-7JTnY#P0t^5%>{b$6?+*1@ P00000NkvXXu0mjfTP4R` diff --git a/toxygen/smileys/default/mn.png b/toxygen/smileys/default/mn.png old mode 100755 new mode 100644 index 9396355db45a8ee040c790782209868acaad4b85..c976ecd6eacd2d7393424c3e7aa90c5ded7818b1 GIT binary patch delta 849 zcmaFE+|NEil7pFnfx*${FZV=6;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z_=>FC&cwX1H)gSB8Hb>`Zoi^PX>nfUl|xaGcbGvvKScNGBEtF zuKvHZ`d@wJzw+|h(ys*t|8jHxW@i3LP5qOa@cmaz%+K)fAHl)j{ru`*gH%`jI~V^i zJLX?xWJ2htz`(ygK7ZWZe>*w-agfv_x>wKM-M)P8-s#&H_phIC&z$U#5Kzz&n-Njb;-69>_KoEBE*mls#(m>5{-nHgEh=IojpTI!n{TkD)0TK9sv48wZt`|BqgyV)hf9t6-Y4{85kMq8kp-EnuHh{SQ(pInV4xC7+4t? p*jZoUV*n;|gpS<&l+3hB+&cWzayCsgROez~@O1TaS?83{1OTRyPB;Jn delta 430 zcmV;f0a5;s2kZlo83+OZ008-bnr@LH6O&;BB!2;ZNkl`J++5f+PL6rRiQNLgu1_potVgX{t{eKx$|NOuHhk@zO zpC3@OA=>```VZI207MK7009KDfg$woe}>=xgMa@Q`TZMV99$1f=+7UZ=>P!)^22|| zKmS$!{Ac;a@cR$YT8L7JY6c`57{N{g2!9~3ll}n%;THqLAD{$K>gP{{s~8|Q038Di zr9XfEFfafF5J&^_-#`DK{bpbXYWt1m9;k*tKn;Hx{{96z2Oxkz8klDOVfZ5Xp9Scr zzf#gnTp;fOBLU<|#y?Dqf0RHAHuiv2Ach!FvrS|y+S&!pg8lQ$uk&%v`8bccXRkz^ zNr-+1K<>e(jE-00ElJwK!^Oaf6yW5Cz~2$-> z)e~x!Pic{Rl6`_<(W<;rnIm0wmlRpbv8%f|9ZcecPTVB6Sf18J0p zf&}JSvwBLcnk@GP%9K7y?YFg-8}CX+a!42ds~G{SpOw;=A|4ilMAK}OYEq@}Daw3O z^@JqfQ{Y?~4n7HyE-KeaWLoU3(8C@A&d$eY~Yc-5l$9Ko?vc@vH7#Uw?ARRD5 z3hxo=h*aW{P%eselG%>zOj{;zkjLrgr1hmC6;J{)AREX8xRD4Mz+n(>k55%AnC}AFTfR^Zhmb z>MsxW#4&fj{NhXT)-z{6Kc}J3c6BPHio<2HBl4wtfdvD-F#m%*ARrEK_-r26>(bF4 zkL7Fk*hq7|sj;cy_^~H69S;2&{^PIUuIT4uVt2m4sBUWuwSU@v>a)|Og++%-sA6s5 z-PyUDcRKFezLl{r?bX-z>uDBic<{2_X&Z7FkG@s=w&rNvn~xuEtZ)AI==TeR+q|HA zuIXZMps33o>gKmzpF3AxgVq`LJ5LiCUVuijiBy?%8k1hAZPZ5q9D>71Cj{y20}3`F v(e}+1vuEt>gf5Uv}jnf_M)EIywOT^AR`u%?ajKXH7 delta 526 zcmV+p0`dL72h0SJ83+OZ008-bnr@LH6O)kxB!2TuF);jr zk-uOJ2pdAOFaQJ)3&R(NYS(Iz;{X4E=+Cjb5S_s>6&>Le+qufKnBv&bF%YWVBlABO)w3uGC7{rLq%Ks`WL0R#{e z(EPs)fB*akI^_5N|9=)g|N8#t9~WuY61}otf4Tqs`!(tD7m$X(zkdG%X#fZyCZKPC zW&?Et`7z=QS^~DI8Yx-=nhgK{7whGvYkyX~|G`i$&ji%)=Pytx13&-(F#yj00{}h% zHX=6e`R@Au`pNJ5Yb~?Y009Kj0JIC@Bp~_y_jiv_ z#ZW13DP2L`ZI8B{e|=%?y*0qV{R0japkjakVgbe)FpMR+C4s6zqJMsGe7N!7zkfg@ zfBgLcB$Wh|K;aDxbfAV`3;+Sd$WY3_@Cg=?zhII211Y9|Aw@qA13-WQ0KY*#(RYzS Qxc~qF07*qoM6N<$f~~*(oB#j- diff --git a/toxygen/smileys/default/mp.png b/toxygen/smileys/default/mp.png old mode 100755 new mode 100644 index 298d588b14b9b19e04c26ab36266ace317b81d59..013e1836a9837946017dec6511a07f1a35242c94 GIT binary patch delta 885 zcmZ{iZ%`5j0L7o#pz>DNd7I~L&UK!8o@arQ4zfC%q#ohIbm}?(l5)xvLWQ9t&>^X% zpcDU(h61KJZz4JY|DdK3GqVe=ZM8aA&UIVvHg5C0n&ay}?9;pV-u*tkx3YVLsPf(8 z2_Po=l~>atZ|S@2_#`G!5d_HOz_z=RZ2;vY;CT)}YX^Mz-4<3f;PLI*Gs$#>RUuS@ zz)A#&;8zNt3pl_9kps_MAQMOhSO7!S=TI8#4LZBlIA>d&`g3Fa<%a3eLu-q{ENrp} z8qFp3V?~!nqySyjcT23E5;jjNd%n5t9JpnctxtEjrU$my)b}0rqDDhf*)=9#pD!N< zP9jXI)Y}D0>sJ$_uYS{MMh&t~t7+aLkdHpISFJzRpW*A{Ki1~Th5#x;B$stJ`3>fu zoIT6S>eEGLY1_>B{90sMuSDCs`S?rV7 z<&8RB?dD2DbY2%VyCbE%4+#0JYqp@qlvg#Z=r+H&tNLY0H8?rbZZYlMq&%?w5I7;`x36 z+xuKZsTY9}>c@R6<5(pzE|}vZO^p%}4t#Kg=bwFYM`ru-iE8<4zM?jM^V&xHgYhl- z4c*fgCxx);y0SRBueYyl>GMvC*x@X^$LI=5+>MG>^7ZdPb^jcJW%RVN^SNdDGnEIg@=cc zqQc0OWHN~sag;`(hLT7$63Jew`}sdaG4Fg~f%yLc^WnEMn#jTXfCPGC?9i#4>VE;~ Cp`2X)eQ1zd`|Kz0p8A$%R`bTZnZKVTj3KfeK z`2Ky*5B&e@&)?sFfZ#7s13&;VG5q=W=RZg_5SU54z5Mv&imk%`|FW?#NF2HHZpKX| zPKHOnfBpIO`|qDW|9~0*0*DFdzkh#$Hh=sBTKbps?|&9m6K!50el`bVF+V4{Q>XY@ zS^xj~3$)=MP@aL|4?qBcZTJiF5YT|npMboW#3IbJ?-ZlI5Vjx-c~e&(*k)>8ry_ilh%o3swtfD|+*1@2TvJMJD%Wq+|6)Nq=*TE<&?FZ%0?{yD*y1DnBb8=2j@%pEv zo7>6j03hbj%7&7U-c@|aVBj1f4j1>vb3}WAO;Ye$f>_xX#mIqNCb$74+@Jajkka9 zwK-;oZPS;^4F#ng%gf7hNkVE5Z<-zPPyftKOHC1`q7i(YFM9_U)-K`vDs`v_1)tvp_<8m_(@7tWPNk<06QQ^a!S@%Uh}ed$az|(VG3$|J|8u?@*w9YT5_dQrEhEL$>ik@7yfCRbEoY!&XeFCJ{Qlx75)iC_OH*onY=N0Q#$#b4aHZLMSh>;+O;3A zA7%2HU$R+oM@$~C>1B7HvA5^U$IS5kd&Bqm4(_X0KY3jDNT9CG$^824H*?;cY|-r0 zv|FhD8^dO>!GEd{@jz^?F|j2m9-}z+1ZuL0JYiBsijHq-#0*IM`)1j-;jgOnn z!A#cMNm)W((B4Ve-c*)>f#D|~rZ{^O4ipMU-bgNM&P{`mQajg9%ivrm#DT>t+4XJGgP5I`&pzyAFG^Do#{ zRa%^vg_(hwneqG2-w+MozW@IH=Wj)-86P+6=Wo9l8G*Kb0|+3-*{jc+=}JF+`H7W< z>HmKQUT#*P1{)(8poaY?9|3*x^4(W97JsHcfB#8~@y=g&3Lt&p)7FK7ajr?EF)1E>;l%P6n8x{{H>< z=ik2@e2BtU=x0KDxB&bkx{@cVi#2jT^&l@b&`R+>mjN<7Hn8v>U>N8(uf^ z@{&+v36iLnOyxFWTOzn1BJT8RXl7xh-sPQ?nu)fooF`pJ``532J$KK?pXYg|o}NqU zV2S9e~(P^CwSoNK@(xAcs z&;#@W?*My%w}3Z+PM{rd0xf_IsK>4gyWI2c&SPef31O2UNn^)$n49GE*&bB4oXG+ zB4R&r;{m5-j>YU}t~<6)Ix2-6&ht)9L_&j&^T+ZpQ$%a z84Zyd?Sz&(L#e~+l2A!;pjhY^@=x)(J}y|mHlP7`9jF0RKnWm*2W&u!m6eq_Uwk+* z{wgz7t~?UER+~5jl3Zk!D(OyAS_Z|}Z$5A^Ks)l^iz zR!!Pyin<*A>cZFamo64o6mawGud?$wVgJ}@C=mR}x&4i1`}QVBOYH9bds~0|^}&*J zi0}?u*FL_*)(cOK?5X-t_oJ*m3!AceOMCtCM0Wtor82s#jV_zS(q>Bl_ynJyPl)q* y0t1hb6bU6lft}R1!F+iKNuJ|7ytrLDDO4^fG`k4Q)D%!Vi5LVGFo)L@R3|SIb%g{{m-XfJ_dV3 zK|)9mN-AW{F|Pn(VPFF){{Q6bQ=rnnK(YTo1%HE7fB*mgd%@M;K<=+UzkV_P0?Nqp z$bT}hFaQJ)$SFXR|Na9R%*Fm2sO{I6U*Sr>Bb0vs`uywH-=91jzd+y*NDt6q00G1V zbl+cwzkmM!`SbO+m+YTk|9<^u`R%^_m*bXSzgT|#{QE0P`4>nhQ2ZZA13&;V0loO= z-(MgHX#3gMzn3`u{`KzHucyC$Kl}CT<$tfW?!V8y1*!h^_t&4le}JX~1P}|z2B81` z{`tlJ>-5b(kClJ#jQ=(L+V4OAer0L?e*Ep%meap~34q)Ku>l}}KpH?k0~!Df2i{*9 z>wkOb|H{?<&G7$s*QH067o?Ksxz@X^f#DrEc7MTQ6qx}R{|Am~AqIc|0|4oIXk8DoGX($u002ovPDHLkV1gZ2_;3IK diff --git a/toxygen/smileys/default/ms.png b/toxygen/smileys/default/ms.png old mode 100755 new mode 100644 index d4cbb433d8f9fe49f06585dc46ee15593e3e621c..ab6f7fb9c557cfc4b3247890de9760172ab4e2a1 GIT binary patch delta 911 zcmaFHa+ZC9BnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34f$?#GPl)T`?Aqqs#SG$8>m}WLWt|zAo70tJH^!DSFr+guBr`C? zGB89lFoZHN_%kqgGB7ynt2W=?dHDY2dpA~XyD@L&^=(HUoxU=6$%XRzJtbAU>N84r zrW9;X$y}F^zBW8%0|SHYSyAz4lc&6Dtb0;a_$VX&MOE2@(u!+2g~^G_gJV`zPI**1 z@j+ql{oKyGfeC9F7)%=#{U2`H^={sr2i0{Cnp@s2oPW8Ydv)oQsF>w`VM|ITJm_0| zX5zx58Ev|S6H=kR{b)+fO*b7b8|ZH*3UXL zacN;({dK?4g$xYJ3=FxYjR(S0Hw7hb@QYdN8L`SGc)6Y5QcJHz)~@rHOinmD&t+b3 z#O$t6H~;#@3=Fai44H*>`+^cS_{OaD2w&wKwA{jTk+I8sJ^R^)HZ$k-gwLJkrK~s6 z!5e70WM0)?Ti+!X9t(|K=IPqc*07qPV?IsYXtJDEpM}Rf1_n_EhWO0VU8zMo5^}f3 zWNwZ~-4L9x&OdIoSL8~!&}B}6OPqWbJ9sZ}@}6tvI=7yIL6CtVl7YdTfx(c0L7Ra= z6&T_F|NrMbF&QYqz?kIi?y?~6^1cN?4tt5GuPggAc0MjuW~J!A(}6;VJY5_^IIbrr zB%~yzC8j1nfAHkdvxiR~rwa&(2#E=b3QwOfWzw{XQzxef1cU^I1%?J+zi{Q!wTo9T zryCfU7@5@@ni_B4uw~P>jaxUT7Zj8fl@*p2fB*31)3=XbKc{nW@Nn^Q@^bfg^mO%g z_I9Uxc=&kvdHQ;vKXK;Nxszv4r)y~FXz6MC>T9oGv1Zk}m1~n9tYQBoEj4>qbkyyv zTh>;RyLMGq{r;uyGsmRz)RoN3>1R@Auolhx^Tx9H`O)X+-{;M-6lSm#HM>3|>BV`V ze^g6cBT7;dOH!?pi&B9UgOP!ek*Nv(F(r+TJM zd@O&8pE;x}-14%L@L&9if#DAW!!HJge_#@d00ICp0M7pbFC~Mplt2I)8~NAR0QB?! z7#j-}5WCs@+S}~?`~LL%|NQ&@0QvX){{Q#=1F4*i0st`p&i@3S@RSGI5d_G)1@ZI# z_J8&U5)b(M0y+2j#`QMv`vWF8IQ;$o{QUp?{QmLR&;S7V0*Gk~kHv3!H5OU9Kb*Y( zq-ELEH9vC;U*%N({+r?VuV26a{F4-VqagC^H&EN}zd$De1Q633j~FHafsLPJA96}R zV-#A)X~v?X^MO08uQWGzhcv?EW1DzS7_*-PyVVhzS^WKx2OY2BN>efPd&0 zko*T>eE#*Pcctaccf$WUKXojz`Sk}FzJCA$hy`fN-#`C>1_L!f^ne(DpbVD3|2mi1 z06hQ{`SBO%q(1-w1k~^cC?O%n4J3gQe?U6_{rLwb|A0goe*gdX|1T56-!H%Z0~G@V r5F-OaBrHa;qJKyXpfU!400RK!cM?V|HPH&&00000NkvXXu0mjf#ETW_ diff --git a/toxygen/smileys/default/mt.png b/toxygen/smileys/default/mt.png old mode 100755 new mode 100644 index 00af94871de66cd0fbf0ca8e46dc436d66e2f713..0d1f30c5e369fe25ce78e1231e3312199fdf0c95 GIT binary patch delta 751 zcmZ`$T})B|6h7wsNtf9)o31H3!BW)AO>q2}!@-D&r1)3UDTdH|NC}~452CG_TQ9k_ z9-0qQvpD& z;cD@Hp*8x9QOGb z01AKrzyV?aQGhT&h=({%Q`G9pN|Ixf$t1@mIUHvl4%TXA%x1c`m+I~&+uI4FF$my} zMWgF$tIy`=KP)YA9LHi9V=~cAO_ZvEtgj~|l7v`H==Hw=k}Uhy>t$J%i9~3Ffl{f+ zdRd~X3KxkI8cl#V5sPsw$1n^X4pTZEDU%Thf(wOlfdFr8q-mPk-6hrPcu7gDs3@*f z`gzChaF}5kilWF+h)^mMm6dpTd91Jy%g>L?WZM8FNfN<737HJb&5fbxCLfXCB#6Y$ z4vrx4;$kc>FM2R1nw^c+))Jvm!eGD~8sc(!Oe)1t6cdYm0Ks4|5D57F{_XAUt*tGe z&$qF$@nvCQ3*4}`x91w_6YzIWIoGVyjC5uWO!6SZsc+F{{LIM75~h~*aCdm>v0kI@ z$oyBUCJ@mqz@POog5!eQI`%JYLi^tuTMWdRCsn=E+0$N3ay7brLOeh z11*C7<{n-5Y3=6ok5eyZ?)5Kg7T4Z?dueiSzWe%mYI3sQ*u1#5zFh3RY^J0t7WLPfbq_-59F zdLEn~v{?f`isX#{x@UHFALw?=XR5kxKI9_^o!T?b!2zdLVR2ab21XP?(Q2f=T2j{` xK@=yXiaL1}f+!HgJL=(o`j6tm$l#D|4E`&4QggItT2he(pw}8Tb4})}e*u1xUC#gj delta 356 zcmV-q0h|7=2BZU!83+OZ008-bnr@LH6OscYe*rd0L_t(|+G70p@gu{(e+*1a3`pSL zn>Rel%Kv~23J#021by!|Nj{nSXllS6)}JQ%=G*B|382J!^kh2 zHUR_>%fCN=?%cok>e;hDzkll+8vX|z@%pm6{J1lGU+RRw2&0J3U`hChFRN&x~0qyee|#eA3sBpVpPP67xZ7VO6V`wv$K zHy#*Je;6150tn$G6b=9Xp&0)MsNpZePT#*kH2?v`f~*=Iag5B&3^Fndyu3hzLFn&a z2m+c75I`Us-n@D94+enNf~Z%o__l08i(?1?2rvLmwOi|Xk;8TX0000T6w#C@C>h|o*0zQlfYU=<$v8r7Hs;IzUT7Z2Sh&45jDmXyMqFkyH z-UIFc)4&Aa0o;HK7y$Z#9-s@D1^%>Jmk3!bOEoo5&E`c@=m?qT5vAj}`$}plR8kQb(W#&2gs$Pz~nWt^N3)Dm;F3?!rIGcO) zw#sx%eq>5gGA`1M@-*&U`NPcYL8?veOqBGJMcrHZUEG2pKn#+^-r}^JX{|clTxq*v zJbvkr<+I|dPc^1<`9~V|ANput@hO@1q*!g$9s7icv$jU4A{g8o2#Ny%q2K?(%a?4Q zkMZIK<@xjXR#$g;y>Tlmq~+x|yB07kGh3RzC7u5!rtw~M!`;ZMvy_dEfDVOYMFpOM zTz9T&C|f=#m-I_Sy&`@$Kf{rc-kHwmV9?v?lvYa8=SheKG(aAp1c-kVBzFTDupu2t z1(JaTfDFU|n*kEm*Vm^HZ~B*aNRcE{(3bGx(t?&&6t@3rTEG(gvl$UOKO-RIo@x*+nUN&*ZtD&a|+~ZPUlG%3{8IR--HK{@5X0reQ delta 434 zcmV;j0ZsnR2k--s83+OZ008-bnr@LH6O*3eF_}kY% ze}Dh|0cioM0U8HZ{QKW;AQ#B^1vKHWoG|Ymh9>|4IE<4N06-W9fN4+=|Nq*iiwn$Q z2C&jbT&$=TD3oCSlHOGFpRx(;BY;>KBo9alsWSh04>T2o{{H<9q`@}){__Va_~#GM z1}QP2gACgN0*LYNU!Z^U|NnpRpWzq7|9?OK|Ns8~{})!0iAm}G`%?e`!~zT(Mn;ej zFx*fTp{NGS0&QXd2q2cUeGC!mf8PK6{qG+rX#W2D1w#KpjDH~RZy5Kl5XaxreGC8r z#4>*s149Hbs3pNc@)sJMAod>!2@Er!Y7qGU_hSzOKmai^#4`XL4@G~lG5)|9z#9l> cVgLv*0L0&Apbu9U8UO$Q07*qoM6N<$f)|s?fB*mh diff --git a/toxygen/smileys/default/mv.png b/toxygen/smileys/default/mv.png old mode 100755 new mode 100644 index 5073d9ec47c3b98e18bd3cd8499433d463ab8e67..44c2b5f432fed7dd5ca3d7a27a8202de665da72d GIT binary patch delta 873 zcmZvYYfM{p7{=daOF%G8oMV}nI0=kR3O(&8xWcl4g|(CxS_-X}uK%9H=|j{;aRfK>#*zXGsp@Y*-B zCV(y9b#*9O82}m}0uTZS0Qdkr0B(SX0Dl5F0Tuyrc6-id%Z-d|P*iqsC~Goh1_sh3 znMNqx*SDrr#=E=YQt1N#N6tQ)(Q4!5QHJPSJ*uMBib$_CEN>4<+JeI7z>AIkCLYaV zF(T0%z=qAXrq!IG&)5^Mjjo=y(AMer@ZMc5TEW8(cpYRcV0i<@l>~mC>(}xfL0FbquOez(p!*(lu$`~}x zBxaVgPK`%{dvNWFR^?HvoIT!WpN=**-UG;(%?WwusnEzEZ8e09XX%OMjB_q^|0;b= zs;z^?<@^- zx!(h%_4=eh5INEuYH18M)%zPTFXwp=Ywt=`xx4(ShtHI`c9;5!i$iR7N{6p$G%1xT zrRYg^btT2(L|a>|rG+_kh&gzW=JBFjuAj~JvshmPECAdE_!eMubCaqi^FzWGTzo_@ z?c3_`+|IiKN+fA52p5zTRlG1Eu1x1o_e6qbMWK^Gs2|61y-r6G5pU2R)+4Xa7Ygip z?1{(AN_Xyfcd{Q%zWvVB2@NtwDjBZ!taX>SRJ>X!#i*c&3y6I%wwj>h%rO-4`mgV~p-YtU zIUgLCZ&tu9VePgtqC%Vhr;=(_QT;lKSP{XNHa4`M>9>0!PjbVHY m+rBtI{U5?)?jO+G{w4kg7`r&kp5jy$0f<_qf}1Vq^?v{-mx0az delta 480 zcmV<60U!RS2c86w83+OZ008-bnr@LH6O(xZB!2<0NklWzhA$B4g=Z%5I`U&fgJtk_g|1x{{H;?U6oxK z=&at?J**6@V8K5Oe}Dh~1#%?NbbtT?*?#~O1iJP2|3AMO{{CZRxc2)7!+(Z6p#n=T ztDpb>{AB#|ivi>-2CxkP0R;BLZ?JVh+Zp~c{rPwM=Z($3{_1e*9r(EK-@jk~{{98o z08|6E0U&@NPWr?A{a117c~Ibh-1_U^uM@x^Y6gYU&wpUMS-$-OhB#0$Kmf7)0ZG~L zhfz|J3l!vk{{h|g8*1KvNF)FmfB*gx`wIj>#Q*`sco!Cne~@DI9}*WCF)=Uz1Q-Bm Wy-P6Xdy?V+00006* diff --git a/toxygen/smileys/default/mw.png b/toxygen/smileys/default/mw.png old mode 100755 new mode 100644 index 13886e9f8bf65186eb96071d4399fbe077ec92a3..675d2c253edc025cdf77776f04f112e02f53b59a GIT binary patch delta 864 zcmZ9JdrVRR7{x!c(xBJUifa!oAEiL|f`EB}A;hE0MTNLdNJtQ|X=A>w6;i{w+RSRp z-Wk<2dz;%Y>3MpOiT>M#SJPH z9~g5*WyowE&CVXxlOtN~a7M;2sr08`BKd(~!PwY=n3$kc`VJseS~{X9Ml_mVQv9b_ z`~yXYQc?yJ5(W^YKRP-nk@PYPd|E6#Aw*kHUNaBk0oDSz z0PFy{044y2sE%O_AO}bXNCiLv_yEZ;Ha1q$T)33!5LQ6y)c(ExwffoPOlcxh6Y)M` zIxBI~gXNZHM(QBcN-FZ1%K_oqq@u|jhtuhHeR$pX_Whf8y>o#T$(pjgu6`T2Qcpa% z-xW5gZ+7&)y*bp=9%}ESP^JV7KvkuU^V! z2z}H4<>uzVrqF2kgjcn-D^y5lXZ+&h&8~vpw^6h9>^84Ly1P?_moHUT4D_>E+v`a^ z($&RHId^tl$>&e=Vh%Uvtwy?EypZD8ulcG+hL;!uBJTv~lk(X;lRwrl9TI7cg)Vf` zZroMjW)283p^%TI@dYAEfZ?eMF)qfq7=~k5gYwPUe-fV3!r~(D|B1hW*YmA&8U#yN N0E8w>-KKKX{sB<%Gp_&u delta 467 zcmV;^0WAKW2ayDj83+OZ008-bnr@LH6O(WQB!2;;Nklgdz{}>o1 zGhhJ#0T_s5B7k8S2!aznYJ*O!gdmL}m`wikwu;?nWae!9*wOvcUvnT}OACHUFjdtR zKrCQw|DQa0`uFc&*S~*te*d2S_ix?r-weNhxBmVe{Ok9L-@k7D0%?(zl?5sV2p}dP z=70b9U+C{&yT5;pfB#46>ywfWS`r^B2emYGC;N zhw;yEHn21ZK^Q<1%m5k+3<-b$0%`dF7k^?r(6B#Xr9c%(s$nG12A~Fj0AgW|W~lf0 z{piE5KY#!H{`(6^0LkAV^7kJg<1dK&2k6AVf8+%JY`(|<5I`&pQVh?z|2zgsfY7gh zzkY$$fyjSAE>y!Gpg;NlGN>>B1P}|u51>Q-NODMmjQsca&tI^Yfhzv}0g-<}p*#T$ z@xTB70_A{;0Ro7Tp_GB)6VTrb48LIL4-(@KjPValaxnk|7yyo=UE40l)`S26002ov JPDHLkV1lqF*nj{4 diff --git a/toxygen/smileys/default/mx.png b/toxygen/smileys/default/mx.png old mode 100755 new mode 100644 index 5bc58ab3e3552b74d990d28a0f500e9eb6209dfe..0c11c5a3717a6661f5e7a30728904d4743bc019a GIT binary patch delta 891 zcmdnTvXgy+BnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34f$>~`Plzi+JU2rkKSM0{%hxYozkdDl<;&;KpFh5T|11N;Nd|`F z3=BtsM#S?r?wQfJXJ-AbX_eb2{{Hjl@85ra|Ni~+=g;r&-(TbuyvWLbmY)4IDXo4# z14HBP8R=`AQ&u-bFRS|X8w7s+`t|ecw?|$94?X=JxOhH}OWMo8kg~clVP$RjlCqz_ zf8DyU=f=5RU%!3*@#V_{SFZ=o9{22B9!16O1lk?TUbAyb!m7HMWmQ27i+=q2dH?E> zW9xgGn&YZPs+Px?D zK7aUdQ@`Hyrk=@lErYwBzUzTTdotv$@9>+K=P@hOW=g`BZ(qNE|NiaUx36Ene);m{ z)4O*!wDhlQ>R(gWx#Q-&8mQixA#-g@>Z-NZLBqn>jzJ{U0q7sq64!{5sFcK#RIB8oR3OD*WME{Z zYhbQxXcA&*U}bD-WooQ#U|?lnuwdnHbC?dWhTQy=%(P0}8ZLfu?w)9<&IOE7S3j3^ HP6f+W4FCTB`}+sTzBO+aKmdWw{?G92)2qM#{(k@c3#5+mKLY&w|LfiZ5cT^v zvwxH%&<212VgcC<)DN^BXvn`m|G{S8yK(^N9$5`*W}w>Nzrc?B!}RAj&|v@p#KiFL z&)@$*g@6D2{reke@BjagZ=QH_|HA5tt#{7v1Ul>guV4Ru|Ni&u_rKqNfHnXG5DUYv zUm)ZE{sF26>H%tC6A=5t#K$G4Eg-G_AAe~5pFdy+`~fn6HUI<=69bU(4`enF0gVD` z*mw5mQw||P1N~j64*ml=32ejvKOhA3FF*hRH82334zb}E(9;b6olKqoeR&JC&DqKW zsQ>S;-~Yi*`VUmZzyJ_HKqoN(oe#F*_b;Fy{{0sc5RLSVg$B~EU;jZt#{dkM|7m~! z0W|;w5J?*nr;q2L`{se0gST%LG;e_5cGwfB^vdqg3!rX#^<%00003 zMlJ-H+rH_lag4ErHUKaKU<8<+19%2t0ayeu1DF6F15o6(i7}(&>5$j@UH?T>d%HUk zPCTcmG)1jNBCFx>N+_gD1%rWwg>#+N6#4B>6Sr{Nc?#1}-Y)z6XEil8x2(x0laV#G zgjkG~m!mu$(R4UdNRd~sfAe5zp@;H*Oj2yiobxGt)BB`TXV&@61#bq8Q%UfFN zVY6KY1S`7tPg3GBuAnDWj5}8~ar@UUu7X2}E1uxw+2lY{$NR_C0&7yLY>|+zEh`)43K3tp(|%U_wRj=lexf#mTIv;|Du7)GyqLoRV$?7w5^*NS$W+ZLmBGVDgj?k zwRz*XaiIiZvx=ETg_F8F_gn5fnE0{rZs&1rL+6JlI!>h&ifEilMia4Z^q20^w{F^^ zRBXHW`GqgOI{#&lw6aE0SEZ_~HvRU;?|;qx`M7|7ATQ@&vul5T?)c3gZrvUk8`T^+ z_JR7y+_CnfN5k~{%gg?Qc{%=OG8h;{PCP*mG!EAeGEe=zTnhbdg>BP`3zA4tKuk6sxkm9gvH87O7fX(ITPU&7IBISa}k7tAlFCICI2zBYg*q|fAs$bbG)Lb RkNM_ifGS~~;C8v<%)fm8))D{! delta 509 zcmV;LEdT0096o0M7pec5bo&08qxs4>c~v3<~xD{{2cp=WxAN>|$dG z0Dt-a`~3g=`u_X-|N8m^92?xIsRD?F0R;ZNc*&!t#Kgn#=i7JY|NsB~{>>mTdm0Dx zpa1{C#)An)W@eTA0)PNwiNI_f1h_u_DTrb&+z}xv;{WjFGl*KdyBpBOpV{{sbqy1^u?y!_QYdjJB6 z@%QtmjJ$mR|Ndq8&j58S#L+;5A!dU~pd2F$%bUA*0Ro8So}{#Be*T{a4?rgU`DOz$ z4B{qm$bh&I!M}gm6csNWJqi#&jNk9yXAu;H8UqV;1|SKK0WgCBEc^QAO@IJm`Ss%2 zk0($50Rb>TfC$F;17@gJfn*>Rb3li=$YL!r-ZvIB6d3+6C|E|xf&(i*; zHKkfplIoHYYZKyY;uSwDAje!$;j_%0=ISKPEsg4?N_j&WTN5k!MIyK^i25mteT~ih zfyubSkY1NUiI+y>%Uww>ji#s7HZVHS|E#}nq|fox(KFoBJ=EPb*ws1E+1}S~AF|gr z)lL9SrTd@WNpxhZX1A-Gm6~i*tSU|tClkoTJQ1JG=Z0}v!7S!_rb4VBO^g7&_X_sp z?RDPUF_R#h7K?B4`CKlS#bOZz5k#lcXf!I7>hJI0M;bY37;nz7m+7WNG4%qOnkSB6 z@k5y0AO>q~Ad?zE_gfLT)OR(D9qj^U3bUuTNT<~%k?T?Yprg6ByQ$aS=;*Zdv^Ut> z?%JQ+?)>joN9(WlwuZVNO*1;(lw3G@#e^}rU++asM8kW zv@uL=C74)-u?rZj!N_F{U&Qcv3{_#^4Ej#NVL`VUT@~mkLmOG8Xe-Cu++2&fYAfl- zElnm7es5izU?m5NK`IwfdMREWG*3ox{v|THD-}ko+#kEuK}f!V+k%CQURg|Wf7QeD zwb#AgfcKI&eN0Q2`7VFU&ws_+@2sT4dsV>dHEU^s>(*}wqBDXw!aIauhK7Ylu-NZL za=5%Gytj!TE!Zp+i6t@8EwWfS6kFqz+u~K~?FosR_dig}k(9h+XG&^XdWKe)xhpGM zj@^6od-vt!=6z_$-(T>Nu@Jk9K0a{JR9teX^zaeW(Xvm<@oB|p$Bvs%SWce${PY)J zR-VCEU!ScycfR_hyd-zb_+8XPgHtnMXVr-&|o~uJZt-B8BjpAmz~i&c1vC delta 522 zcmV+l0`>jx2gn4F83+OZ008-bnr@LH6O+LMB!24-^) z6p+AM5&R3m@ab0ou`n<&D7^U}zw+sP%fF`-|NaIl`1cnm_xm3h{r&yx->+YPe*qb? z+<&q_8vp_bq~ZU+|9p&pvv&RstuJJil>ZL~|9}umff)b({bONaxh{1bAb?oDF)-ZV z`m5;k=ljw>l6BpeezVN}{rlPPKR~N~|N8X{NCKH4>bI<%?516t00M{!=o3jd#=5m) zyzyNB8Nca#eH--U&Fe3(f#?+keR}os(|=1Kdi(V)&^Z7B!~zVP)m#5|#D06XgM;JU z-xGgXXZ~UM^&5nKgBXk;@(++?{{4pyr~x2=7=JP_Fjr;$zi^*{`_I3I+K&RffByYr z0J|FG@c;i9K;&N_o0*mK=B4cb0R-|i!<#?qAfU(3R`iCJ-^1tkFh97?!Kytqsfg~uZelsxs0U-vU_+N&ffB(H< z00<{9 diff --git a/toxygen/smileys/default/na.png b/toxygen/smileys/default/na.png old mode 100755 new mode 100644 index 63358c67df905515b49cf50cd766834dea8c18ce..4bf47fdfd923b2368ebcf5ce03b7351ae2e7d5f8 GIT binary patch delta 907 zcmZ`%eNfW{0DYdZG&4ZrXU}I3a;Q2SP}{>{9&y-C2~cYvcO0I$zFIt%CsV4ec_UjtjzhOX2oV4Wo;F-;8A z0+m2Da2z-WlmVrH1~^$;H*W5JxCAT$4&d{$p{j~I=W9C4KI_wJ`_<|`rLwo6py#sj z@%;SC^2oSHx?k^>NLQ6>1uB2>~H&`yw@?NaQ&WBrn{;u8JeDUE{&T#DSO$nC}JaauasPUNN>e1o#L|8=oYmJ*5TuhpvA+`$fG^ zx)&cCljdj6qbOM}B~wV{c{A2&+tW$8)jG8$C}i_%ue=&uf9Cwz3sUNA-MP$^w7nUr z=@RqEuVV^oY;1M|dC2DQ)p@TY2KDGMKigR`8nPz2^ z-voxv^Z;@|9j3+G;8c34sLc$he^ImGO{#}?7V7?1ch0xY`OeyLyxaf zs8+mBE0b#F{Jdhh4?q$m$s{6~tcWxg!Dn;$5xn;Zf=>_wnrk!v2`E*v0!8Wn2Rh$> TBfFNh!{0Z&I62lNqRRgTJ(`Y^ delta 586 zcmV-Q0=50g2Zsfa83+OZ008-bnr@LH6O)?*B!2=KNklFf;m`2zg> z{{8*_{QUhgHW*J!90}&%29b>a^7Q}v`}DH10tjd-8yoBI1g}*b?B6@)xIB6&_WRFo zPJd43=mbqObq>D1hX4By|NHgp|L;GHwRPtz;{gH)sDXiz;U6QzZ)V2dKoY20MNK$A zSA+NcQ;z)f|Igq2|MmO7w9@DL!aL%O?v>U60R+_W>(dw3rWqbwY+vs`7YYhj_p}$} zTRP+a`c40T|N0L!J1pWwjPJac>kgmX!+-FE0U&^Y8vgwL^Y6(^UJ((A&TbvS-|sof z(*NIk3Uu**ZlSMT#kZAtD$eEJetAPtKoJ-a009KD;qTwSHcrxUA<{hC)-o<$`2XiG zkTaZIUuQ&a`F65@U+0%!Uw#8!`|md}*Z=|usDZ1nNMDHgJ7;^<|MOQs1~YSht$!_k zVyxV9z4O$A!@n4Q0hRvw_XikqKpOx8h~@6}1jz>%7`vMPfB6Mctz-PEDE=htFQn-H1B-ng27mwq Y06^~*nfQtQz5oCK07*qoM6N<$f`Fo@n zn;Cf5GqA4Z_deXe`DOpAM>E%*oU!&)=aPrbtDe@ccv7?Uapj^%rSl&a&bgmE^KO0S zv^xw8EBiOUICOsMy+;KfKSure6Y}O=^!z<5lBV2?n{X$p_jYLK&49M+-c46iCf{aY zSXwAs`{=^FuU`}Y{fk_(B&4h?@XsIbpFcg0t!P_PxO!pk%6XZ~W~VNmk+@)4`C3&5 z)4vStfA=;2{u%N2FHmv7ym{Wgf4lwq<@o)W%a3}q$nOTh-?aU|s(O7aHyrXBoM)$fbE=VvL`FV+c9LF!rm z?5zI%)BpD$&)KtGfBv-k@x%1%L91_CAzxMezsP%imUR6j;`qfZ<}omM%^9{|sl0qW zAUMe0*SFqk;zX0LU-iq@IIGn9$ya(xmb!`*ISS<48n;9O6*Cwzq%CV%aXdIC&cVaO z%H7>0Bvik0g@ba9pIoJe-!Cpp}eC z-tI1Q3iljR2Xfd;Jbhi+pRx0CsWR*iTB8dT+U4ou7{YNqIe~%6O)X6<%&d&hPcKi7 zEzZuaj?qyuQ83W3kkeB$lZ{O>(z24(RW(&K)U=e>SC=hUHrBS5*;zSRIM}$D+gqD0 zyIwllx|-cxJzYH9yqw=(pDkZL-u{2XfdvmHT-flT+D@%{HS5-{Uv0;hJ)3rI+c&PZzH{r|&AYenAE&Uo z?>wW@DAvPYB#qP0u5(~uaQ$s2IK73x8|V$y64!{5sFcK#RIB8oR3OD*WME{ZYhbQx zXcA&*U}bD-Wn!*vU|?lnFvI8mE|?CmhTQy=%(P0}8eR&fvrRNq=VD;+boFyt=akR{ E0MFlV@&Et; delta 529 zcmV+s0`C2}2hRkM83+OZ008-bnr@LH6O)SrB!2!C`~j1{ z{(u-j@)wx>144or00G3p&+wxmLk*<*`%f;G=l}mRe*7-=i;d;azyE*#g3uoj`Ugh; zr)Ql72q2b^3>*v$|BYqV$_U>U=6}V_{p-Ug_RZ&f58p8R^9Q8p_n&{i|NaA#l7ehN z+kXK9h^6q-reIk`VbwF7oIjQ=`+e!s-?p|7QML=z-`u-*QuXhje}Dh{g&@AKyayR3 z00aOr0M7pejN^&`?tK0P_y7O=($f4WDEt5a`vC#_F&@4$j?@48`v3d<|N8s?`}+YO z8$kfe0#F>sHUNMy48Q{FH!M8N)Pwp9Y=2Ef5S!fr)7;(Y&LShYR!Z5%Q9^|N^j)~X z6F@A$uwXdN_Rrx@bj+{czkzoDPD}*K|NeV{l@Vz0uRmZLKqMp39}Elt0mQ;={(HOh zKbd=sCyxBtvHcfN)6bv3Qc`|5zUEsO{0nI5uRni&|NZml?;mB+zh_S|00a;V(0YRP zm$;o2nGYWR{q-Br1HTR*`Tas%aP1ZD-+v*hf%g7^cpez|00G3p@Ph$pz}A~ue~f?g zeE#<5_n+_FOlM#Il@|gk1qTw)^nZUL2&e%dfEXE+8Gw3#_JBwr`ptkJ00bBSj2l76 TkI2;|00000NkvXXu0mjfL$(e2 diff --git a/toxygen/smileys/default/ne.png b/toxygen/smileys/default/ne.png old mode 100755 new mode 100644 index d85f424f38da0678471ef4b3dc697675118bc7e0..bc088df11da9e15d29e3ff3eae45c5a26aaf2cfb GIT binary patch delta 892 zcmZ{hTTGh;7=~ZQ5^yA&t|}v%DOn~VgY`f=q^?<3+A%B;7)2mLY$>hT#T?zr<~Ebg zX6Xe{$AHy>8w%+h2v){Hl)w_F^Kh|<&{E_upo7wNoLbucJ^cS)|3q*7Zr*&~m*?hv zT6c~lG^QlR12}$^O1hEC(!Z`cd8!b&{04CK8W3S#SKUB82k|na zsi13icIb8>5TI$AVuqwhl8oBMBK^j&RUgtn8XyQF7!3OTeuu-cy}cccMk8b9@JDZj z>W>ijjQ&8tZnt~A-Y6N3L?YooheO>T5*BUn_g z^K%>XGwbuyYjY0IwB2pDt=cAB6XVO{4!5J@w+@_zW=tcoN{;m$6L#r^{5$-^orlxE zN!2Z_#`%EdU7S^7o-AUJz*k7`d3TUCcUsF9=UGZaue7LN5uvpeFJJEMSY!!=o zMLFF$f-V8?4v%eNXLM#DopA@K0ToaUlmjIIBRG#y4v2spAPZmvnLrx!5Fi$dP54^9 zjQO}4S)QcLyzu^BW=YWH7fKUc3A=Y4+)-BbD2_S(lrK3hPjsjhirShQjZ&>zU0im$ zmR3{>=W6ovFYbN$g_l^LH?}mHl#Mr=%Zufw-YF?7b&T64rj+CMNk=w^&pW~uWDWNX z_G|j?4Gj%k)Ys|GUC`DKXotOut-+!5bz8omHxTa=QStkBtiNg)HROtae(*7wNSriw zxy{o*gs%N!S#Gfn`adZ!_O9KU8zl`7I-N}u{l12`jo*0MW=0~$U%z&lroIn;R%Enp zv_HO2KgUWdZ0FU__*1gt&gb%0EH}MOM>wdHR=lgLRqE72)j2g2;Bk1oOip$tmoMjX ygjoV1|II@jj*!FoVCQ$G{}KNJXU^81)?E63;I+!+%^TdbT|mCHNMaQ$4F3Sc1HBUf delta 475 zcmV<10VMvq2blzr83+OZ008-bnr@LH6O)VsB!2;`Nklbp`Rq`9_JORYQ@Qs1t_y7OzpE7{d{RN6L`~m9u4N~+6jQ;-m|L52L z-+#XuMPz|W00M~d>&=s#l8OvKbwEpiHvESG2>tK>|GyyO9|$utvpzk42q1u17-V!9 zI61+pfvQ08|BZwH@16Yr=jVTUlmA*S|6zcEk@1g`6+i&7`~&$EYA%%V^7j8zTmS$3 z{hyirzos*?2B2bq00L=%yMh7Y5+V8jx_`F+fB*b1uJ#`ZFoK-+422<6rO$*F;i`S<7F=LcY~09F2DXJJ40(-`_tV6+|HP z=ieU?`S;JC|3Jlmf#LlZ7^oZ!00G3vP|CpY0UEnd^aqLY2gZOX;9~#?FaXg6WgJ14 R34s6r002ovPDHLkV1hAE@+JTP diff --git a/toxygen/smileys/default/nf.png b/toxygen/smileys/default/nf.png old mode 100755 new mode 100644 index f9bcdda12ca7b07b3d16bd88c759db2c82c88884..a02fcb80ec8bb98a8d033a1d462bd1b87c7a308c GIT binary patch delta 865 zcmcb`GM{~dBnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34fpKSmPlzjnGsEn?vtPe|{rU6fj~_q2e*60U`}a>@KJDJx%^=Lc zz{|kE3N(YEdP8;Py2|U%umAq@`|sa>AHRKk^!Cx8KYxDw{C@V{*@zk8!PA2MCi~S_ zZ>Wl&A0IO}=G=pGzkdJv`}c3%n!3_urN4gt`u6?Xfolg08x3`8bUgb#85|iRW=Ed6 zf9Ch^-#>r-OrDb*J0te%_pjf7eBXCL)qH0h*=RK(?d?(J+=Sp zey=Vs=Qiio^{wB(f8Tw1w`zr|LaBm%r#*u;Ls9*z;^67Q{!{%=-aT2hqRO$=(W>4m zYktaSnFe)#lZ&!s&^^+x(N`a0D*43Z3$D=V+xyn6TU z-P0FOA3lBf`t|Fl&!3K+82kVK|FT7&j{)O`G0EHAWzsa3_X-U43=Hfgp1!W^&)E66 zR4lr@RX+h0?DKSS4B@z*oWQ{3rj{laW>&`Mrek-1bN4nc7Upl> z+`W4C?rrVMr*H3HKfj)#p5HlPLUe&gMnr{6N=S)MjtswZjZ;BVP?1+wRFzv=PM8_L zvtM3ZonvBPp=V}frE97vzjLW?ZftFHY;th1cXo6&JHNB~h055wwtH*;3afo$U}R9N z(%!YO(p(bg71a{gh?11Vl2ohYqEsNoU}Ruqq-$WVYiJT;Xkg`FY-(jCkWnjR6 m`P^g_9l7}_nQ4`1I*>Hf^_A&QG*steVDNPHb6Mw<&;$S!B7Q*t delta 541 zcmV+&0^Yy@}(m>8MA z{QUg=&vz+aDbBwfPMS^;t>M4_{rUa(_om!U009Kjz`*eT$@`~&|NZ^`^ZWn*{}~wn z_kV8d{r>BFYe?(AfB!%K_3?Y%L>Z^l>!7{V2qIffI$#ez5f4~?wt{_jNwA= zk=tvV8m;04hxH1ClNsp|jaD0d_yUND;Sa;#|9^ps{`~n3bQJ?5!?D}Pp1*tk?A@~o zJ0>vvX8@W7)C|%C)&LMdARGSt{R`y$`G5E4KLZHvzP$VU@9#f<{RG+rbRp0bkZpf{ z1C0b~00-24qP4iqXt4FCbe0r@~|NaXDR00000y=$T6)@QT?|u-1X0`g zvt?5wzoHC<5nzsh^AAZ8oSV)PH?z%dX_{?8!3a`lp`5lr>9Ci2_2vEE_qlpTSI$P& zuwzyN*tR9isNKl4q)EU+w`*S@jr^qnbetX(RLd^5^I0r+ibq4j%VgZc1y4 zv{wo#jyx$>+tjLiD#abeY;ZOh47x3|Zf+ZwbB(j1WkYs%HnIT=${v@g`c?9~a^fa2 z6PO7E0+Tb7oU0u6H*8ijYxDKZ5ChdX_~;;Ui`d#L_0nFy-#_7r^=b>qO+%wK>Vy?Xu$7p0^bXYQw4wRwvfv#k*L}SyCeTCvK zv8YqTZ|5T|A_CYC>;crUJM1o(%jtAF9F8IT5Hf&|u)Mtdu;)NuMB56TVu$RE?vGCo zM?$n&t|X#gN5{o-8GLI+XXG^?mu(|soRn6p*XxX>28tSg@yas#`laE;*!cQ*R^qyZ zq$FljO+%eg+fdtBzdJsmkSzFQSE1Ue)h5Z%Njagr6Hb|%=hRA_PAQGhuveJc{NTEQIDVB&cc>;k%AZYtOl=U8P fs7!A%R=o=(zEAu~=bWn*ro{qsB2RW>yY|#uBoF);jL zxSw`kfKA}vzkeWsfB)Y7c&FZ`4ipE;{$gNYVE_mq7KSej)vnbL>3{$Ki3^FdGjT95 z{09P{Ap39jn1Gl+48MQ>`}6zn?@co|0R#{WP!mx4$=9cU|NR95kl9TC;Q%Oj`{V6j z|9^h{{`Xs!M;2%gKmf4-G0FZKmdWv|I6_A&;LJv z{(%shYM_N64Szv401;3FKmf4-Jp?oxsQb@fum%PU8~*%)I0>i#s2CuCKpOsn%?64B zg>X9QKhy?*00Lh)xZD#`v33$e}5!T1H%pEp8vmrCIZa`Dh3E3u#-Sae?t&f z8~y+_{0D^!P%%INu>fNY7{-#^k|1yV`OC=22n+@Y2?Wec%u4)9fB*jlA)tm|3;+Sd z$WY3_@Cg*hp!oj5aJ=ET5StJrrvLo;^Xkhh=L~01G&6wJ@Gt-b7y#3Jb|6swwhhhz O0000leb^}1juW89THP|F5 zlpr601%UxUE`n?xGFV7uKtq9wfQ?fSH;Lbs%KF7aed58}qN7)|{ep_Rg@P`@;^qzQ zT<%Y7*42Qnjlp5%ID9MN{gT-&8rsFY>tfC|l-Y)~SA^tNe(Oa})y!^c46LhXqH5{X zRtA=g#hjNeKN4&H6i@ykRzE;T@1yVU2}k+~{LZAamo2#!DDGy8I_Uy8h4<31+lLSm zMG*-|5Cr~RBRyJ8j{FdKdx~Kg8mDQBqDYeT`FuRja}%{Ue((6pb$}T=P7nl1QC_c? z7kG~2*td26jZ}`m#ObkPe!t)2@kE^ETmBZRAMi)-^Huk_iaz%6ZKkZp=j`^{y2zpq zvY?%?-te2Q`JFuk>>LIyTi7XVYvI(*+*jvVMSUQ;7H1+{^r{n7_%U+HkHi;M{som& z^U9}UU4DRJNMWp-?HHk{y%%>*3-fT7-tpRQ|T?(&qApUF%edW}Q8G&o_q?jOoSO3UU;xudSIS z+l%tbl?JU%lb^j_rM6@^QugMSsZ2ZeCl&6BSIGuSn{$V9HujaFu;(vUd<0BWMCw@-VHrFU1-7h`s;s~kAcqp$N2lh-~a!<|KkS< z0!4tf{$&v7VQgFY1|Wb~F7EnwTAKO(J7734fICJBB9ummWsfaGsrB!ILr`~#A}X!*+sjHN%Ic>4zsK#UCjUm3o@A}10k o_JaXEb|F#z8>F0pl>s2Y0536OyW^N_tpET307*qoM6N<$f)cjf*#H0l diff --git a/toxygen/smileys/default/nl.png b/toxygen/smileys/default/nl.png old mode 100755 new mode 100644 index fe44791e32b790949b0317ab3c258864b9024ebe..00df16514cfe14432efbd62ee88daf8792c0b2bf GIT binary patch delta 822 zcmZXRX-rcI6on6?0i%iNAmaQ{iE)_-`dUG>mZ+7AsE`&b2rfJ*ZCQ$~SSeEVK?4#3uH{V|)+~L+= zMvVZlRLqWPjE$nEDpzEt164DC>RKSkpQ=9q+X!Hg1Vl%H7=3e>TnvOAm#tPvLjViV zKmhOo9>4_*0Z!mE5HdAz+jn!uTFy|z>M6FvODDg#aGgB!{M*+u>}DqjZq>8rAi z%hOWahGH&9!zz@ld=s-emrl(JO5_1?x<4t^Cy;pK#J;rbkGwisz#TcunHxFk0BhRI zR@O6yI$B>7)b0!vRrw1me0k;G+|8b>5^ovxo)0uM#Bm(U7sE0PL(_CH7z_jge!t)6 z^Lf2qkH_P7yL+kLUZ>OPa=F|dPft(JXQz{9=;7gE#2zr7c$Cxdr=sq*%zSg@u5PKR zYl*RA(N@QNon26Bi_^5uQeK`$UR-8$0O^RRQ6IXWQgvf-#dXovjs%@!PKo_j^_6J_ zEt5B0nv{DXD(hTi=9w|FQ*(=3fkgCckR(&Q~el_Z5HRy2ND$5!Br1sxo2|{> ze${c!s5g{Xl;`Umwl<6F&Fkbz;`o-|`*!Fwk>OMF7Kf^Do$Rwdw*6m!|3Z=F=!;H& z?cKBgHc?O8-1{D0)>@ihcQfWYr~3~+u@CQga7k+!Joc(9Xt{TO;KSi(?cV0!G z#Pf2H{}s~>(shQye1l4)ELZUjLP98vCz9d?2?_xrnwKa_STLI)L*B!2-{Nkl%w4 ze}91F-@gjtJbOE500a;V!?RECzy5mt@oxu64y57Vuirp*e}92BKuDm5KmY#maI!Km zyaNaz7KUFSb&|rI5R-tG{01YisX+4YpMU@V{{8p&-#=jZ0i6U8K#ag>V_^6NMt_hP ke~?7}fTI{fG64h_0Bg!;fV%q?(EtDd07*qoM6N<$g8jR>tpET3 diff --git a/toxygen/smileys/default/no.png b/toxygen/smileys/default/no.png old mode 100755 new mode 100644 index 160b6b5b79db15e623fa55e5774e5d160b933180..d76758bd3791e6e9c0018a03e61a622f462fcde7 GIT binary patch delta 800 zcmZ{iSxgfF5QYa)L5zqNA$Sv85V^J&)e0Wif=z)GC?ZB++wImXq>2S%2^2*!G0~tL zMU?oUc)XZ;LC_E%yrL!=G@Jr1+ie#qwOGno4)@gu=jG4*|9tZ@Q?ayvd3A`dHvsI9 zpI>=Blx5tC9n44tFoXeATm+b9qKaXFA_QOr1rRp?1fRbBGCdx^t3;8alnwzbE-tRf z>6o9N9{~6P& z0=at^DH0L_-e63W)9HjwtqzBy9pDu}8-T~Zh+@v;rIsg4ycU;44>FC9UY08LvHAG> zu|@0MJ2A#;mr3?3eY-J~DUDjI!ZC~{DAJ}v39HpYvjPIw2CZ4gGF5BP>KjH|jjFz` zCR?G*IFz1+p^9$eyQb?~S4U@Nmk1H4QJV#ei{lFUt>))XTb{Jw=4T~HvA(eQOwrk? zF}G_{g=*veOjLgI`1CC0nU^oSRn>60>Qb^KN&4aKd#?FicmwAMoAcn_{pMpDEzUR7 zR3n{31qTMLGu3MAa3dd@vh&g(X_Z;ItYA}9(<-Pu&mp(#8ksi&j$V3LuTkl7F;<8( z0z8Dri$+AzT!E5{i1|XXKp2T2Vgy4-eSO&N*VEX~cq-m1J HiRgvDAW>x3 delta 450 zcmV;z0X_cQ27m;R83+OZ008-bnr@LH6O%3jB!2;tNklU_l7u4-yFwKrBE}!P;sd`Tzg_o&+gIMn>VH!vBB&F#P%RAB2AY{|zGl z0mN&`Tk_-7w>=n{RejQzkfh&Kn{rf10?_b{tFTZibx5v&dxav z5I~H7|Ney-|DWN1$%1FyagzUW0464;_kZu-0|XGuJ!WQ++^j$M?h9<#^c(D?w|3V5 zet~?<0P^l{s5gH9VNq7Pdioqd0I>kQ`5zo)K&2pO{r~^(Cq%_BkRV7Wi182TkUzk{ zW&j8vu!cW>m?S|i5#<8~8ncWn(_c`)Gk}Ai`OhDa>c4;gfgKMJK#W&mQK<0#y(^&J siMJQbt_chOfyFT-f*D{W5C8-i0Q8<(lBH~=r2qf`07*qoM6N<$f+=Lyv;Y7A diff --git a/toxygen/smileys/default/np.png b/toxygen/smileys/default/np.png old mode 100755 new mode 100644 index aeb058b7ea8b5d88519dadc69cfe7cdba77a587f..48b9d27c68e6bba0dc1899cd36909bf8f0b226f2 GIT binary patch delta 553 zcmdnZ{E}sYBnLAC1H-KLaLtK|!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6@fgaVEeg#P$FG|3;c82Q0E?<_>+AgIs*I4t|2L=WM z^__2k20eMAr@H#jhYvsR-u-#|_OEBpw3KFkTDR`^>({?uy}HZ5aDjnALax4EUB3Aq z1H&B#1|63zvbxiRW$T4xDg>p9lw@*lFfhF7=y=uCBq&`fClUIpw)W?P2S4xMzwhnM zCl)IuYWF-h_vf8EPhw*?Gca%qxQhvy9cN&85+43IDCiIa!$Agy)eHN&Nd^Q|B7VO*>>|Ew-oMu3ugx9B}CjouNSrX(I%<%8u-+!O~efy)gLfsN5$e85q z?!wT)D(eB{u$OrHy0SlG=i^f4i+R)BlGT-sO*K_C)O3SYDX*_?uB?D}eYk)8g##Bj zP8gXPni_B4uq7#B!?ulEH*YF0C}Cod4&Zpd*;da1Xqjq>YeY#(Vo9o1a#1RfVlXl= zGSW3L*EKW=F*L9;HnlP})iyA&GB7w?d5#xFLvDUbW?Ch74ICgHn^ej+PBc{KVqoxe L^>bP0l+XkKEz-a} delta 380 zcmV-?0fYYO1iJ%}83+ad006C7ryG$W6O%gvB!2--NklyffB<6p^YJe&%q%J2@E@iKAb=Rv<(vO+e^|@#56B5{*~0MgEw-hSQVq@c)m4g8m-@vJV3BY9Qv|wE~+65I~IFf}TM7H&Eyo(CB|)%mAiAaR9Ln aAix0dk*M#^32Bo60000oHve4?q0Tm0qOXj&1%y!I~VUsb{EP0|~e4lUWTm}YV2I2VX znY-d!R!29kjHp`{Qnkdde4$tIeAoOr4%suT)2EsyO)`k<(~9c$D44~-z{?;TT{nAM zQp>Wqro~Zpi-IfX`;^ReFPP<&JKeTEW3qY51mlDr-RMq@@K(3{X$%a^_qWP^ygTd7 zwU!r`8(v(jeR;9!#rd-5XN#Vl&V6z+^YQW2hes109FDnnFyhh4g58Y_46K30Q#Oay zEb3peKcsqrTft0+>}l3%lT4HQ4dQyVqB_;WS``DEWPIyw(k1`{n1R7JuYcp@)kl8+ zuK#`e@f+v7S?0-;j1nj6#`J1LbSVY5$@(`-csB^U*P10w$m(8~)Uxu)i+4MYU$ah| zY!u(G8`Gl^(Ww&BDi_cs>0K}4Q6u15Ws=a#z+j)+z9zACW!aR?sqL$RD;M~e&GRan zsGE^ou84Dm6w^DeLm^Mi#M;{y?pz6 zzCq!oCz@}AH%lz@oqy1{c-pk9Z!9Ad7#NIZnk>w>P05=M^p0wYYeY#(Vo9o1a#1Rf zVlXl=GSW3L*EKW=F*L9;HnlRb&^9ozGBC(vSs#p|AvZrIGp!Q02Kh+w_Y)1(xfmEc MUHx3vIVCg!0BUn&!2kdN delta 465 zcmV;?0WSW-2ag1h83+OZ008-bnr@LH6O)w#B!2;+NklU{xSUe z!|>-1i2VDD0VMGY3H<>UitA{$OPI`~Tk`F#7!u%=q{3H<BYX7n!%ts-f`8ob`_HexAU6PA^5@T=-(XiMND3aA#Q+dM zObkE|{r+dGuapyP)1Kk;_aEc;AAi38{{Q3WzaPK;{rLI!`_F&he}T|1aOeXB5HOT} z0bP3j=F^vNKi_@w>c@|tzhQw0H2V+OA7D=a0npP7009Kl@BZ0<00000NkvXX Hu0mjfzY66L diff --git a/toxygen/smileys/default/nu.png b/toxygen/smileys/default/nu.png old mode 100755 new mode 100644 index c3ce4aedda9bea0553b43c8d3d849eba6b3d2cd1..abae7f15a7a8ff096f90ce78b52acd6eaa59aa15 GIT binary patch delta 872 zcmV-u1DE`~1f~a&83+Ra002Qe{R@#H6MrCMNK#Dz0D2|>0Dy!50Qvv`0D$NK0Cg|` z0P0`>06Lfe02gqax=}m;00F;HOjJd4bJzWMZ-ay1e~an;Q$=rg-)C{x`AtJMF|qI2 zRrcar^xs(W+*R<|Qt#JL?bS_Reev1i{Qub3<>B@I`h3>Y^8Loey~_7-g!S_8SAY5T zUHJ4`_VQTu@Ky5dQtH!5?O$j6)7Jj#gZ`_j{?lpwm5==2aqyz3;#_F->sa&eR`2Rh z?&(eP>`>^?M)65a{Ls_>=z#u~l>N(K{eph`+G+2VoaR?r@#$0S=1TDDP3X=Q3(IOzh@K>*Po7=Sk?zLRMDF{-mIg zi`jyd>-A7fe}&$Iddc5eS8imq@8(YH%ro zSM%;u@$6CW>P_zGOzGi6=iou?-$CZwJ?7jz>fk=#!Z!HyTlVo+_VHEp?^E*ZPx9+c z@8?PG=11)0MC#!`>EArx!8GsIRPWYL?A1)`(@E;lM(EB%=*>aq%0A@CJK@DR-@!HC zz%&2<|3&NA-T(jq0b)x>M1PcjXG#?S000McNliru<_8W6B?BT`{g(g$0KZ8@K~xCW zV_;xpVrF4wW9Q)H;^yJy<7W^M6cQE@6%&_`l#-T_m6K;sP*hS@QB_md(A1LC*3s2t z&^ItNGBz8>Y;5i99UPsUU0mJVJv_a<8GL;G`~w1mf`3Co-95s>BO)21 zqGMv?;u8{+l0CvwQq$5IGBUG({A{vZ|V)NUEl` zuD+qMskx<~wW_V1p@WCFv#YzOx37P~#Og`ylL0a!GgTxFqrw0H03~!qSaf7zbY(hY za%Ew3WdJfTGBPbNH%BcoG*mG#Ix{soG&w6UFgh?W$aAFU0000bbVXQnWMOn=I&E)c yX=Zr`YAmfGQdO|6}<3m*Mxbp8x>>F#yj01HK{e7&9dS6|D`% z?SB9P*Av3x|1!rE&h#?LnhHkw{r~*^{QCR*`uzO+0s#T~@%jQVIL0UdfG`kK@&C_s zu!x|7h1#pPCnTlzmg&NiNa#&thH30mZ8HIH7K^xX0jCv$QA zuN|fgd%lXr+|Rto9m~nW3Nah3`X3V$&>epP0*LYR?PyMU4xkf(Rs#(HDgZhI6dwQn zfO!zre}PO^7N#FhegXs#3j@P{h%s>05J!Uo5!v-1RSds>{{{#kmcRdiE@OiUL1$D$ zLmg->Bt$_Z1IQ2{0T4hy8yG;*!ORTu6EiUAfDZl7%*X)rClf0R*hxTTV0VC6{{RAr z1!&yQXFotjf(!?_7~*IKm}CBeq8X|QNCE^H0Oh85p&xu9oB#j-07*qoM6N<$f=I^g AYXATM diff --git a/toxygen/smileys/default/nz.png b/toxygen/smileys/default/nz.png old mode 100755 new mode 100644 index 10d6306d17429012904035e4097bf93a8d205971..c92a8a9fc7a4730e8ac2919bb92850a22b88db51 GIT binary patch delta 928 zcmZ{hTTGh;6vzL#u`*z6b1>Gq!Di`{aciNaqZfhFuTV;X(!#h{pe-#@q+C`QE#Oxe zx9-wWC@U@HQick|ZEnL0m>3l|_h7Qb%z(Uzh|{Tw*{jKdpN~HHbaKx5{ZDf8+ugfK z8=;5n0gx?x5#~C5ggm9sD^UQpGl0Dx_?ueUe*@ZC!0)vHe*%bZ@vka{K+vDX1!`ig zwYN(@4%CxTf`MoeP-=)(-KntxHGmSJjuAj6kO7F2q8oRVv0` z*M0sqlkFoz`+aTeU2?w5S8KkdGAtHq7l8Q3bo#IE@jnLoezCOeG@71u+kU#(xo&Qg zSKSns&0Vh#FTA(a*S|gFc>clS_YT*>g%Qd5IiYff zS1>tTMkeOmaHu--Dg!`lqt^TL*WY@E?tFP^e64S6jPyVJ;oje7(96d-YYR)TyfrjCS~W1>Ut<)7^?Drf@Wt=d_B=f?_`q z1!$Yel_Kq;tonVTVm_;MR#4>U=6h3!iL+VmIKfD4x|7bn5}E8smwSPO0I4zAKFLxC z_(jv4{K*u;6DM%Bi%f^&2h(yrzY6 zXh1~*xj-6_1TcV;lx^TJ5C+gF4@FnIu~svL5sqB)b&-(w@D+`)DD-`$NOR4@BVLp~Ej9iHtfLeT;tml@ljp z5PRzMtBg428S=H)_! zv9d&^E-gD>UQt=~rUunDwOXCN&S0#6tHIRxb`#9aT3t(P+l6{dgQ>maVkfM&uI`?9 zNPBPJrT&4zpKP0!2*hUaEqZ1a|%96j-j z`iW^F$}M$Ti-E6iGf)Motkl#b7B7j-QL|b6G%laZNno+~ES4eS!ODLEP0e);M(h6v XB2_)XRDkZ{GC~0vQ7E}BuI>8=*!n(* delta 578 zcmV-I0=@m(2mb_+83+OZ008-bnr@LH6O*h1B!2=CNklA_3H5V5SZl@9UBk;0Q>>~F#yj00jE58tx; zoDUfVAO7ZA^g)(IQ}Y8G-;ZB^s|2r!aDOr^ek~Tl^+5IiTTk{UKn(x^05Jg0{{txV z5$FjO#M18f_xaxJ`@PBK)9C#b2>sCM{7nhtD*x-Z?-9-O3KRbIw*MpJ*t-IVW$|5> zd}e_^d<-Z4@c#Mz_c#Bao3DRge)jj*@Bir!w0`~m^ZWN7pxb7Dmsa8gISC+un16u& z0EWh|zyCn0fB*gU>)+4cK-d3+r~^s;{u{${A1M3x&p)6BfB<3vYWVZ#@9$rKe*;Od zB9OXYU=qmw^JjwiMZtexfAao3_}37q0U&^Y8omJoSW=t^C=JvJ40w=EAOoo34=^77 z&StgqW`A<hNf;s5{u diff --git a/toxygen/smileys/default/om.png b/toxygen/smileys/default/om.png old mode 100755 new mode 100644 index 2ffba7e8c43f160bb0d9634fb9e6cb4093741340..53b7a3253cf31223c878f9980681541bf2add75f GIT binary patch delta 821 zcmZ9JTTGGx7=}M*L)1c>tfC9isimOugM#ZvLmZHZ3MM9Q**qXiS4rk!brG7alZUd_ z)lq2rKQEi#f621`T2RX*=#bI=H}*HR_jV%pUZAHS5<9pZo1ug+%A`EV`IbVbUGZ4 z_4ReT-EK4*9VU}z)9JKhm@y~EX0uIAP5tcdb~_w1a{1cY8irx!%F53Q#k5q0NhEra zNXO@obK|uf&M1Nm)9IhW!Ultav^-uDz*~T?WoKqGGpD6eLy80wiS&tyIxcsd!x=-6 z5juT{N*&m{cZ|z@4PYuQHKd63LZLyEn5gHXIyQTP!O+rZqoJY0RO%3gGDs%h0_X+! zQc_|Nizj(Jvnro(df?T zh_;B3mXN^az&(#B0S^NF?vZ@&`Z8J>z=Wp&#{7br+}vq}Mq!X=$==8$uOy;gQBqG* z!qbEUPY$rV*sOL|WNRcuK_@^fKr_H&fcva14safgRQNtWWNuW@ztY1 za!7EgewLey7q4=0n$YW;7s)5g`+Xz%D*k#Q7VzantS}EeMEg`K&8{PyN>X|>fp9AD z*unE2{NUjHof*v2-UNa;qL{+x6SI#TZLF!uKE~hqG5cngDo>V?CC|jhbmMyMKl0ev{JeYj z*P};R)jCjkTyBhU{jWo18sX8-x~ z|9|(Nzkh!J|NR@N^WQHZ-f z!>?ZqK&!y&{zFIzP0wn(b{R37FH0BS}Z;-(ZzkW0R`o;JgO8$qifBy!0 z03d+C8bA*C^$+NzU%!${e*gOW>-S%fvwt8AptFE%pj(ys{+w-R00b01!YB8~(6LN`mb9^H+uoto1KY#UGHtAbWu-|Ni|4 zRQ{LY4+B5|F|K6*k|6XCOeTghF#H0uzJZ1Q!MG4<1_pot0|312d3ZSl6ukfd00{s| KMNUMnLSTa7#=`~x diff --git a/toxygen/smileys/default/pa.png b/toxygen/smileys/default/pa.png old mode 100755 new mode 100644 index 9b2ee9a780955566cc7dc2f59ce175f32d3731a0..6b0c7175fa5922c2926a8e358ba470b533342c34 GIT binary patch delta 813 zcmZXRYe-W86vxl9d?iH$MMa{}mbqp3PTgXymbR7G$JAUeDw&Vj11zmo7Uh;!Qc0wg z6s%fLdJ1VsSy}dSwREku*2^M9%jWfZU%NV+cE9u?`tgUuIfvhe!>JnU4L&|CGzb8a zl!5Uz(UVP41u4rj0QQIg%m)Dc{L1_jU+Rp%{ zZgQmN@$kWW{x0G-50jZiB`1^0g@jz*kD|ClLSUE&pyktWgKL@P2ewRGj(p$%6D^DlF`7kz82tnLhxd79=p&gb*7 zEXyzqP16)bIh{^eL7JL|)?e_gImhZOOnM!iT1WO*SL2nH4u=CW$vusOtIx4H76v;> zYH9~ayS*2~eo9fIt<7$?Lz;=$b(`B>&uzWo+kA~Jxr~LcdBn%Ee|R# z)HWQsK0PQ%^{Dxf)pDh-=~zSk*%m>{;x}1Zm+ix|cdx%Zd3&Vw(~I{VUt66oKR$Q2 zcRqEQTkW6QJPuV95;K2Rf<&265iNZsSBPYBk+#f~h>2P delta 457 zcmV;)0XF{Y28RTY83+OZ008-bnr@LH6O%myB!2;!Nkl z3&h}OpZ>2o|M$z!|8ZtaQhbd6|NsAc|G~e{pZ@>){qOg$|G$0#RWr%T0<8iFAdm*2 zw0|TogRT_Am!FIf)j$AI2PS_*H2ec11_potVu8EfUYUvEKf`}G`1lE^`X5*+Sm$r% z-@k!200a<=Rv$}~=bu-f|Ns5_|IfewfBrH6Nrpdvs+^pde*a>f6qSrfAZn~Z?Fa+2||AuS%0f4|DysXCZ<2Z!2kio#PI9ipMMN6bs+TP zCy@OQuI|^28NVh>fOr)c+yDUt)Bscc_x~?2`4{NnKTy?wuV4SYckl1@>;7K73grIz z^9LY+Sb$Cj8YjlX2y{I#6#jr524ph=0nk)tO-;_89#C}3%R|BjAb=PdT;Bp+#WL{Y zAH%OdAO@8E@A`pXK=b~A0Z_vqa1sClfB*vkKY3jWdXz1j00000NkvXXu0mjfEk4@s diff --git a/toxygen/smileys/default/pe.png b/toxygen/smileys/default/pe.png old mode 100755 new mode 100644 index 62a04977fb2b29b96d01ffef3b88b6bf2ff05862..2e9d19bc0ce19b5f78216f806ef96136d5370162 GIT binary patch delta 699 zcmZutT}V>_6h0>9a5&8kOi5gk>tPeO`Lno=#_TTL)Xm()88d&TR#F*jP5gn0?ZFZ$ zgFv);2?~NViwJ$tN<>1Y6pFsY7-OxeQ?|Rmd++XD*T*3E;Bff9@0|0U@l@jYlU!CR z04U`Yd*Te&&No*#)By|#0EQg^1Vat~0k|&)*lhzqCINEpK3}*Z2S^ds*BVqw0E&Un zM|r)Z$Ai1wF_$X>fB^&n4gmrH$-zOozn|{wqiLF=7*L5sf+R`8WWt-8;(C2dtKB=2 z4cO_{R@!8uo0|!Oz;Qe-lf@*Gs7Mr3tNqLxgCQP|M}@+OKoCVy9{_E$(MBVstB=KE zk;1|-pC3^uXv;lHuTSW72~ABj8Vwf|V0n4riV80?p|_XRs7aM75{Y2Bxgj1eTwd-0 zAOR9RJ)~W&CKQTrI2_8#!lY6c0L~EI-2{r_2!df)h|3LTa)NAjsJJ-B*xEaYmKNM- z#0`yDSy@Ob4T(g*n9v&=drqf+ZOym3>Rnm!EHAs6e97cv$7VJ2(v*&xD%5OREPBf{ zX;yWeD(!a~C;eQ7om*)=61-NUN&{;u!1j^ zJ}q&E<(0~(nrByEZTU0c@~iK+<{C@R`f2cD|rqMd&#y$Witl|Ra7TY_lz zr~#S!x^9^tQgT)r=0{qbTf5_etap~_)q_pPomqS41NUfwS?4&g3cd}^ot}NaJsr?y zx7#ngT%X%tGR^$VNLlQ&%UcgOnot%64=?54$eEbZFeevURYq%PyR{23Te_G6Qn6H8 zB$gFPN(~Y*Qc{ML%7kJuA{I~9x)%O1-0tnX+1-C+$os#;fAac?3Ca0%0JTbs&Q!LI F`~@3DINksN delta 334 zcmV-U0kQu21&src83+OZ008-bnr@LH6O+sWB!2-PNklwu{sT)v5I_L2fD~3&1Bw6t{xJfTii`gTnem_T|9@r%2B!c2|Hs7q|NZ;_pFcqI z%ce~L0R+~-@c;jlrwo7p0?~hna%6D(_WxhMfT|f~Wq~#T1P}|@?Eg^Jzkj2v1_GdJ zuzv=I-@kzx00M{wh#7&Zfj0j6!|?mhe@uX+0cadR0D+zK=P$&S1T_5l12i2VfWS`r z`v>f&U-&gJf}I2qKwu{UjsNuvF#Z0+ASnqp<}V}$85tQ7(Ko521PMbR0xAXwAjYpqu?ULc^XH)v4D=!d g*xEv+zyKh?00KQ>ggb7|CjbBd07*qoM6N<$f{nV88vpyt8L3;L*jOk4>f?ciL%<9%YH$GisK9$Uz&Zs)RiFM{lmUda737P#gTMgr z40sAW2L1#70UiQ>0(XHsfJ>`&)z!VPt#xWNj_TbGwc4gq*_6srx!fX?nYV0tS5#yY z3aigA*`|z4V_Mo98f_>kX_&{mMC^4sKM@KB zgMmPhh(Q1N3+;rt(eM9zdV1RD^G!`nc_$~Qyk5KA?sPi880`}usx8|#I=V$JXQj_) z@_Gr&WFQdmc)DCe75zF@d0%UzV^}&de$?afOnSXmtMy~xBcU;`yZ74N&cj_DjU8=Q z?pY_s+-|qUVsSVeGcz-OzyFzey#3Y_{f$Te8eJxn=`X-uRW-uln6k2rEY@2FV<;u% zbwa{mZ0yUJn1Sf%=aG^93nQMA$*(AsZeX;$!d#*&F-s&P;^N_g0wbSq%*}nnX8)g= z`8qxQ6_fdrPJcn63?wCW0=I#iz%}3ka1J;N7=T`&2bi6mC0)=yAU+pT!O!C~FB#l! zCyFo)Prwbcg+c`D%YIHRb!ju$J?#`VtrRxud zWUnk=ETZlHffUb6P(@4A>*jIeluL@4`$!7}f@JlwlFS34IkAe!qEymefq=Y>pSWY` z)^!SPm{#A=Y%sK6KX$SEPG8fJb5TvdH5+cXTKczcyx~;3@Ol#-Kp8J2C>Hf8>Kq8hE{<67LBrSfth^#3UsC()U}z~c%zm)Y{h&+o%<7ytkO delta 436 zcmV;l0Zab(2J!=t83+OZ008-bnr@LH6O%~;B!2;fNkltzR{k(S%7!IJo`SpwQ*DqHv1IWD(Vf+CG>tA*im7^Cg0t65! zSpWY1!z3xm@cTF8pT9sk#^1l0ftq2F0Azq7=Pw8V6$1ni<8cOtU$BV$gT(lQ8Xeyd e2GGX<0R{lV0YhR0$T2Sf0000iXC&U$~_&Ec^e+GuX3=F>+7=AJ^d}m~;9?>FMd}>N;o691{~0eSLi`EiDxll@NLPvyzgZMCwI8 z2?%^PGP=*ekd~I_;^MMl!-k%o9z8uhO-)T@Wo0=zISENgMPcFT{Cpp{xj*UaCnhGv z#>P&VFd;cPIUpdw$;rvq*4EO}(#XgtTVMZ%j?P^MhJ0WI{r_M2UV{S|6O2jT?k+t! z+?Skz9QG1VUsv{L?0j6Rd@-*Ywd#Rln>}3|LpZJ{CnTgKC9yGyg_)J{`RV1c$;H{# zF*+(H3I-Y$a(ZfJvdKwCT2`{Us-}vDnwIkV>axk@%EsE(GCM0L3kMq)bGxz0d244& zM_X63yQ`;*hnp{G*xkN-`u6_y^XnZF0t!4dnEfLvT$ZGUl=$St)Ho#t8PzlUdu2sM zRk@{wmHFkx)j29M`v(?!X1YaIx~7Jf`sT_8EZDbjRdP`(kYX@0Ff!6LFxNFS2{AOVGB&j`HPtpSure?> mTzQTcMMJJ~eoAIqC2k#?RLV9^G*steVDNPHb6Mw<&;$T>lPhxo delta 531 zcmV+u0_^>Q2hjwO83+OZ008-bnr@LH6O&W}B!20fVKL!SX0Ad1a`1cQJ=-<7ptZmH9SAnX3{Q_yQ`~BbL*ME+m z|K)!Dcl`DLKS&U$0U&@t8vg$M`{&P}#F!W*N5{%vzvln`{r&f^{||ospYrSf)?fc0 z{rZ3A*Z(w-2B2bq00L=XVPVP4%$zZO`hSMC>(bKFRyR2_x$>m{QLJ!pqFO-W_G}!zmbRFVkrgR6)y*=q zEo80}wy8u(NKEFOgboUUh}ojkWeA%h=rHc#Ysy0j^tPAM-rL*VfBUub=j5F8eP7OZ z&Z*}wCAVj%JO@CfjHh+Hk?GC1ZrNT594rP}KL%plsC5=-A%J-^Anyd$IJ-t_lt9vS zwZWwA?L7(jfR6w#@FC#g&_FZL2si*Mum^Y>Fani{glu79s=NEH$8*i?9(B2XaXNo) zY`o%d{A9OZvY3DL?%sW|cIO47ak#2#5U7Ym<`Rhn!?1zC!@YZdFqwvHYK9Glp^Az@ zwYtAt>6giRgu?Im{Ifb;Kj$J6iLflivVXGd%;e-uQ`1$wo&yi4ReegOUno4s<9$IEbeHOsOW z;&CIx>|vN|G#!n_8qWGoxTK#ri6igv4(}^EWX*HGlhfRyXyeXCQK5MJHp94S#z)i7 zVk8+o?pu*aA_Bp3NeNY07|zQJWoMIFSz)R45fBQ67{(i<=_#6i6uUVdwb!qR#bicC zNF=%k1cS2-!^F5a-y79vBO5m^uU}6U6ohkg$;`~)s`Ox58YvJgF6Pklmt)5|qP4YA z(~gzu>WE$+*}QpKt)|M$!)0Y5iG&o1NIw5BZpG8O5T^_*0t>)X?uB!}4Dc9u0Fa9x z3~@%1>hu+w{DFq@R<1~P=_<9!Pmx7IDi=;wH>F`8g zFfi0__x26Aov!@{np&D8ePoziwk%PhRVgo}roZ@d+Of9JKl{Sc<~zD=i(zY(u2O$v z^w+WLmeJqF1#j|8K3&pp9bY@fE9p6ZwBg;S^QEunoIW+acGs>y?keOLM-CmNW9!7* zx~>wQv0IL*Z*O##H5|C!70`WEoP;|CGJo!~IU6iMkxN_avRPbqxplvtOArwv(FQ`c zK`1r}33;hRE|&2KLQW8!hI=EN|NjV$@7o;pp8qGzt={)Shj3jgpwm`q{A%-&e*yX3 BlIZ{d delta 476 zcmV<20VDpv2bu(s83+OZ008-bnr@LH6O)nyB!2;{Nkli<%=m@O00_Wgywd;x!cYJV6Os_hii*mq2oTWsA+TJo*<N~0_Y@w0Al+0 z4@f;`V3_{@|8#9F=K6XQ9UY*azdwKd1Cc)=8bAo50U&@_7#Kb?Fg*PK|H1$NTnr5N zxw#FRn^&xvtbK0f`#-;cuKxpc)t_H~fPW0JKcWX2P6GrGPy_e>|1$sotNdpWWB9N9 z|NGDXi_%nt;+>^~C8^jj&9fdL?ZSb#19YlvZB5cq%X{{Jbn|DQkiAM9&n)ePWJ z0|qTX00A|8Wnd8b|6iS9!{-09X8ix~9$6if@dv2kF9R_67#IKo2&lp6|GVW3OGPjI zKY5%%K!AaV8yKjJU?UkIBoO`m3qe3%0|XEY+Zz2JsrLV1kqnP*OauV{0R{j!AyxZd SDUH_v0000do(E1q=idL<2z;OcbC;{FDAo+0jtx7%+GbGz5 z7x`EG?tmMg1E+u!z{kKb;0T}tJ^)m}dq54=*VmWB%f`D#!-(PV$PxXI`ldTg+B;g6 zOQ~=w4h+{+4OLa#s4zl;wY9ZiD5$=zmJiD-9hEYNO!xgE@t{~>6Xf^iv#+q3W~O9N zf<0JTS*mx{ORq~^f4Qs=tpcl{*j{XZXfGQm%laxyXcwXoft5h5v-ZsBnJIGWeW!9) z&#t;}>mJNK=>4NNqcfxMY9ZbQxlBePk;T6ktBu3eVwHnBM>HTJ%mn=#hQguP zZQY%EF}0wt0J-o5$#6LAU-rv}WJUc&Cx1S%5L~cKSm<5!ZJpcFFQjko+?;ei2`u;m zzSY&$g_jG`>t(z?9`_2@{>V-)ksZHwoc-mjb;4S6t%ll8&9!jAfH&X`g^VFGNQwqU z+#YU@C5LgDZW=S)e|CS;GpTp#S(jJ|rUcey7BVsKpRae;%WlZT1L6{E3E#@kGv{&4 z9Ol-M>4yfaT`pI|Q_0t$dUARE{TY=zX$J|3MmMlIbdeC4G>6^tHn21&V5 z6!$#t)%Z+GwPHLbn*AUV7Rxt2(kP>9P&cZyn%Sq%rk_tWDxbRDGs&;Lk+|vgBsEP7i)J-s+rAWoB5F=M z^Wct@=rfrH(E&q)%An zs{jIsi2(+HrUEqpC9EW^)CAOaJl^^9-%lBC8BHP081)#ChQGgoHUI<=3(zGHNK?l?T5I`*d7!XeS_3t;(89+Ny{rf-2_Dy#;t-rq>7-&C#|72!mRuNG7`~NS{27mx! xeE$CVKQQ?5=Z9*ED#II$2u6;0E(U-A0|1B0K#Zi3ZifH>002ovPDHLkV1l}%1or>{ diff --git a/toxygen/smileys/default/pl.png b/toxygen/smileys/default/pl.png old mode 100755 new mode 100644 index d413d010b5b097c4e0a4604eba86dad79567ed16..ed09b835fa125a63ec29d5f9cf469bebfdaf564c GIT binary patch delta 714 zcmZ{gTS$`u6vvOVRFur7*hV!qFQuvbHl5|t>E@Or!Ytpu?ta_ETgr?u(f2YZQo_zP5TIX41RFn{Lc>#f1GzCZdRBO3joRr zHp!8dYR|OPx9I>bLuv8i@EOY^2016Py+k@M`m{>78Zglbedq zisIsoMukFgB}S>tGVg1ZjNgY@aVB^gu4|=8X5?Yc1EJvRq)^b;*)XFtDqFSPnhsrW zO?zWcUDr*e(f#86>gVN8Z@(_Bz5cRtXVL!d+v4hnSHh(W;}e!K;`grb^M%Wkj~OPG z=Mqd$*Qj1DWgniOv-Ep;-c$kq_O14|_K#~N=tzl+&*#I+tmx?E!m@+=H`k^y*apnD zLAmAJAU8mYNTo%{@ghm7UV_L=WO5mD3_;`w;!t`X{S)&48fQiaPFpYj-}rv8JKZ5U Pk_e#HG^wAcOw)e=l<`6I delta 311 zcmV-70m%M|2KEAw83+OZ008-bnr@LH6O-ElB!2-2Nkl)sG|SKjQ`jGBO~L# zKYsuMh=oYi5GkOO00M{w=vGF!YB0p30jL8YfLIv#`5F59{y%&6|IZ(WKY#wi(eK~? ze}4lRzrl>(AX#bYe+w4^1Q6Is4VEzI_51&?Um!LT$@u9L&^Z7B z1a=Zw2xL66B2?87%l|Mi00a<712fnG3~)u5+Wr7F{AKw27w8;-00L=X`u&GNQW9*; zUxq&*os3{386YGO{rw9;K*az7#P}5)xgc=FfXYRa0s?>l0|3zVT&TyiU7-K~002ov JPDHLkV1jDgif#Y^ diff --git a/toxygen/smileys/default/pm.png b/toxygen/smileys/default/pm.png old mode 100755 new mode 100644 index ba91d2c7a0de26e554979f6351d42a1a4e22de3b..507dd9f968ac6a225b3be9bc334e63d060974db1 GIT binary patch delta 943 zcmZ9LeNfW{9L664fiN*LGc!_CoJiz0-X}DMTfA*-Y-4N#lCc3!SiEhFm*~d;O?*kP zHxLALM9}4?qCxT|R$#_ev=lr}ZgaBVvd+T0t~>6Ee*e|=*Z02P=Xw5k?mpj3Yku`< z+3f2HfD`-I+SctGh;8a5u>h#q1=KeIE0k341`KrIVJ^UG12&a*UQ3S!JpLB(rMw_D zmQA`}>WVfB*8wMi!$2co1ZsgoARj0JRDc>d0OYtyH%XFzOidcAZW@f!ME&^v+cgHm zwZ++rswxXnTho7Yq&zcgP$(G&1iukQ4!iyF7lR7rxKuuRXIkUj{G-EV9eav-R>iS7|rnU0HEcffH>8k8w^It`4 zHI+(bFOWEAvyn@#M-Rz+PPa%n#1{?gh~m!XMiXgcleU=amjsXIL&hx$MR}zY7Ma88 zwA&qS_tHHFDG*#deVkA#&-Jz!E-yqnzYCwRCOT(mBcH~ZD&%7qBmnOl+uVJ}66tm= z*j*|7i#IOIC#~X{VO~RRX~!w$A&p*@U1rqfjP?bm@;iXIpMl3m31V5R`cbBdPq-o$ zcQ4*zIcJ!&W6@KC(buh!-wy5m^0OT(MQ#|=0x(Em!CYW(;LZxN_pvWWMqj8dDVwrJ zjr7F~n=?rFActm7WbPQ`> zY#d%=$8!?)b9t{PCMBn&@-?nOi;S^or<-h4ryxB~uGgsa`7CvLJ~hCkGnt|Es8B|@ltE{O xMX(~6A#^&6PWN8?o%)LZ1au`DZ9(<_1OERejEza`RtC+F0-Kk{wIt*=`~w|(aGwAG delta 628 zcmV-)0*n3Z2eAc^83+OZ008-bnr@LH6O+CJB!2=!Nkl_hx(kH9mJM zNBtEcIy)gnMH!=&-~E`A8`2YX_`~rx{;oS`3$G61p zUY_{!-0$E2{{CbX;QPSJ^3BEStf z=kMRY{{3V4_y6y|pKjL2-@ap&7JJ6U@%_*5-~a#p`ugS1-#@>_1YZE_s`uj-PS>0Dqza z05Jg0{{iU$011eH|KZ}{?E`M?P!bU9jCh*`0{HIn2KDv;`uzS22=e&#`<|^m(%DYU z&VvE~F#yj00{#F1=^N+o8RP%=`1}3-uj@&4@T0{{K`3kvf9 z0`>p^`T~dr7#V+mGjVdsuu2HZ30<)K`@?wanO*;K^Y1?ynHYcl`o-|`Cj&Fn&mX@S znV5eAV-O&K7@spRd;mw|0S1P226vzt;K=?37K23aKd=%uaP)Hl1Q-D6nhawM!_iUz O0000>@Pl#)B;_2@*XT}%aug={6HZ|Qk_I7>j>ch$H(w4^=7|IzK zav2!X85oio7-AV1!WkHX1M{DsyZ-(EyLZ>mz4&wE>b3JPK0J7_ZvWSrOJ3wP9jec) zJ(O0xJ*jM4OwsO$yzLAO-fzJ}g>&Fs0EqZBt14 za{r8(?Y;9XLl=hRtz%$ty_#PC{prhp7cTyrG5v3G@!v)BUv1odchQ!0iLHJqE6dl! zMU|EGO`crZ6koNn6zFmP!sq8MfByII!Ktm+f7DN%zIF5C+w)e>-te}(>6}B{V$aOB ztm44LVvn@OsMx&u3=Gzh!8>2=*w-=Z*`gWe&*ZhVOnNxG@6?g3%F42xHeo9rqh>@l zB&1Gh3#g3nOz$LjUq$VbJ z!6fg}(16tX=?o0|#qCEza<==YZ*hxT?-;elCUm7);4(e0WqbBJ_*aB`mj=5R1+3lz zj16rDhT{C@LtaT6-Qw0cMy|0AS!ouq%+P1CrrVO5&J^Q-(ltAk4LxgXdTJRM)U)aj zq*d=rD&HMfvNI}wduYyL zuw-B`W?<0$|NsB>ubLl#$$>G++udcM=9NA(Acwug)7O>#89N`BD$k}T57>Z0hdf;z zLpZJ{CnTgKr6r~&KY#G#(X)q7AEyflhzN-ZiV9DkFlExTiBl)12Lyx!g$0HNU%znW z(zT0MFQ*$A)SDQY8JZez->_xVwvAgirxz5I6qOa07JvWn<m75?zq!ev#%t!; zoR-@P^p9$ZYeY#(Vo9o1a#1RfVlXl=GSW3L*EKW=F*L9;HnlP}(Kax!GB8NxJGv7^ rLvDUbW?Cg~4I;s{tUwKtARB`7(@M${C)!JLF)(<#`njxgN@xNAcFmKZ delta 596 zcmV-a0;~Pd2ayGk83+OZ008-bnr@LH6O*C?B!2=UNkl?M_t@b2 zB?|4@_Y)cs0KVT1>*e#+-rD>70R8^;{QLj<{002{{HT(Y0st`p&i@0&F#a7wFahS} z6o1ph0nN=5{q6tn@E_^c{lU1l^AY*>?eh2PFCO>E*7Xbx0@lgN0st`p&i?}F01o{F z1pxvB0QdR->g@pe`}O_>?Enew{R9F2_ajBO3l7@{2K78Gz9u`z008^~05Jg0{{!Vd zs4FN77!dx<`5YAi^1B#F4D8&$5I)oT6@T9A=YXy`1}&@7Y_^+HR|Qk{q_O?0Qv#| zF#yj01gEXY8a77e=F-Lc0sQlFjx$x?`?!_CjK*az7!~(PhC?PJw z_2&2Z31t0000@u+|pGG7NtIrXxW_G zSY+5`8%xGQ7G3axh?~Yux41!wV_39+eW*wUMPz_|Kkn@e2;UIb57Ty zUe56q@h<}4=P`-qHkCy#z3$26*2BNpM`M;il|8-JNR)HHp#VRHYziF)G$ zgJHa;=DtohrqP)0snvJ3ZF4AKF&m_C{5E8MMWZ!a5x+aJ^u|%TCD)n+8P_S z#>_2Dg^Cf2qi1wyZj{$LO(ms{q9U7EJX%mN!slD_^6X`0-vj-9F`8z^?nV{GQBeWC zaU<29e`F{-$4U?bsj1gfQu>pV`&OCq}CLg;VPE1TactpCAmc91s z8?U^+yzTf`Uw&i$nsl|57Aa)P;u5+2*66U!TJ5rrY#=hzv$NAlX4F&~t9j&}YPKPrNKa2AHl%Sg63v#Ab?mH7}OqBzY$?LtiyDImGS5ApMU@V`S<_N?|*-Rj6V?c z@Aq#Y0y0jlTL%z8j0_C_YpWR^J^8P|^Qy3#i&upG|G)qLfuca-KMXK2fw=$vy}xw} zAb)_EKsNsS&&$bh^PO0375Ag3EPt6;7?_!X=sz3*^#j#0Gyh{@0SF+Vlm3Z`F-Ar( z?BDbM+y#c5od5B%>y$nn=KcNZ|L>nr$AC#T8JS}%RsaMLPy>UR34^BAe{Qb-hmSD) z`~82~jewZHH;sOtVgB{~2TgY$oP_kUTr|4az~ zLIKcRMn=YG*RBBs5XcWd{xAdGslxlLv4@9Wnj0kl??1?35aS<6Em#jw1H-p(09OG7 z(!l!rdxGS4Q-l#1opAV~lO0Jcn~ zCnl58Xl!a~G8hc?^$mKxUZ>M(wb}}`TBTAID-?x=g%XJ*U~1kgFP~GXG)kphE|KWqF85o9d_EsR5MHm>ivQ+;h*ey}eBU_-bkh9A7odvKAK?J!Gz~_6misby?UHCYThUc9b*DLB^1z(WLm1 z1m+NvVP(V)&@;y~Aq92-Hfk$-2TLpGE6d)MmAow}nkkaLl8UFrc`x#?Nuj_l;E(gs zXK4D z>a$rM-nTvK8GKMKjU5=E!TPy4>J>KSJ$Y-q0V_6CRT#9Wrcp}{NJUapQxI+n>#UrG vpaM1r<*-j92#O%X;{^8~Ltj^=t2X{;{3R?o&)>1IPSF5xOd_1jQMdg8le8oR delta 410 zcmV;L0cHN62G|3T83+OZ008-bnr@LH6O-}+B!2;FNklS?EfB*jd^9M{o!0+F`VI+{)v}qGS0I>iyi7@<+dh&GN-@nWM{{8s-H&FR+ zIQRvEKmcUQ%E|&20|XFA!~cK(8UFrt`ukV$_iqIc@Baz0|FHo^M#hH^9|8mr%Riuw zZ-3tyLE8Q){QmQQ=eGY$|NqCtU}^v=1iBd@fS3^8`SJ7r@8ADFe*C{^(f_{w|JZZ@ zy#o+HED**2KY#i6{~yEeU;qEJ{Pzp|pOucqNnlF=0*DFdB%s0ne*I+l{{11(um4^i zSXF}z{SS2YKY#!N`GNV*AE2Jys=p_Auz&sjyZ+afmA`@E^!N9#zrTL}`}5}?i2di^ zUuAx!qb)}P0tlqxJ=>o(;gaW+StWtN|L+e-^*^8rV0`@f`~MG64=|wr{{Q>;-(QA5 z3;+Sd*vY_99>~BT#K7O`eVgLXD diff --git a/toxygen/smileys/default/pt.png b/toxygen/smileys/default/pt.png old mode 100755 new mode 100644 index ece79801506ecf8c42397349b4fa2cfe8176b999..98dddc43985d90a50e4d711481dda80f08be3e88 GIT binary patch delta 861 zcmZvYYfM`O6vzM5MOf-TxmnEAfh6!$CFWHBwU#!l@l)1$%#JzY`H)!v@y--H*n)t=UPbcU6f4`HQ zGrDmkX)rVSX#f?PHEC#@ESTfkWvKyr@_<7}fP@$wngQt5>a637u|5Q@*TL3kpTNpg^GM z1;BpXetW8E;fMOgb~E=zHG8^h@vMkxmes1bT+Zj^#r66*ATSna{HXE!ur2zgnZI*m z_WF%Sfjvu?%Xy7TAP7D;m&?iFwORxX@Qrp@hpj(_P4fZcD@5F7UlChcCeY5vGjCKrFtO3 zWoY@dg$~iykYVh~tG|DvS@=RdcZ!U7^J8ggEJ^+$GD{osKUr=SoXir7}j6 zv4VnWacMEI4afv!fE3t_d%*ho`d^)?hsAV*(^#b+?2-&TD>g}fx+Z8ynoUZPWdHv4l?XnE&%4BcP>L(>XJ|# z{%(Xau_Ax~Vqy5iP@P&0QUL`2|NUkB%b@s|;qM>DKYy72{9*e2`~ROm|6$~pO`8A$ zh=qX(sQCYr_fNT){~8JZ)e-*vC-Co|*T318|Nj5}_y1qN|Ns8=|JN^|YDQUEpbY>4 z#D4-d`~Tm6e+y0i^8ft9w&d^c@4x@M{QeU6o2&a5SoLp+28Q3iff@h;hzaPfzyJUK z`Sa()*WVml|FC5@f6DCmby|dx{r4Y+U;lrCjsFF;1ZX-y05LKAV)*m-@9)3ASr~qQ z|N84E6XypBwVzC^|CoOMW(6riX!!F7XnzAh0I>iy{QU=H{yFpdm#Xt$joH;A+;ZOo zZvvGFzXRLw8|oxRu#*4+h=t(~1JL+ie}C<~_eWUk_b0C>0&nlL-TKXZ>=)BlglYz0 zC;{EgzyJ_HARBi3E diff --git a/toxygen/smileys/default/pw.png b/toxygen/smileys/default/pw.png old mode 100755 new mode 100644 index 6178b254a5dd2d91eeaa2a2adf124b6dba0af27f..e7b5f90a7e2e9c1dca37152ef0529cc372d28aea GIT binary patch delta 880 zcmZ{hYfMuI9L4{JM-6PKz;tDcC>a>7hT9h%6+$3wutT2hHkcS&+G3a?Vpn7pP2EI| znivt4A8A zga!^y1jWD+2EJk75Cawpsspf(fJz*8W1wGxor|DbgyUSTo3lJ-Ylc{}i>-38m4i&h zJ=%Dm*4?E_2gu^SfK~1E=iMeCe}30Jw)8Gr($Ajy^Vi37N8B&1M}NPQ)lH_~49IML zVJES<-KTO8kUM7?VoUF^#eM9JktUX2%h91M9Y>LpI=4gC;TLugg6qDdR$TF;53*NcgcQD;Y zP0p@a85_NN)q63GXU?6ypwmt@o^SZPKvVQt;iu|ne?2MFKKb|Wr^7NtB1#cT#ZJ4y z?&!VsQ;*YGQ)M>o-&g(BZmre#+KYxq`2A#n7aATWw%64iI~o#}kZ`S4Up6_xkB)9Q ztN!p{LecO)dXcEd!8e&OG&17U$(&Dqa|<@vy7(Wv_I$Kw{?!L@%@=okVH+9Y%jJCo z{j~?E7u1IBEfR}c12e0TcRPobmf)I1v$EK1(3y>Ly~!A~d=Mcb(H2CuMJUk-5xH0@ qmr4W(B1aHo)Wg0vh|0Z&igL@_fmrpSuzKN!2mqBbPtl#F{q}#F^QO4~ delta 488 zcmV3;-$M3|UCQ1={*+(s0b|b9?bJ$G=j_h?gk=25ZO~ z6jE_fd-)MSEDTH_)la_u|NEZ-sM<*Mq@C0r#{YkIKe5~U$oBXD|G)l$5Ky%&4;Tryc4F(7xpnnYvfB%8J_4POFm*0wT4S)afzxu}i{s$+> z4}Zbl0NL;lAb^;F7~-?<@j!Ed9t8*> z7N8&g{AC9D=^w+KtJ!xys(8w*2l{j41CNz=0)ak;1S=#yfbItfAQpxn|9<~tl2PPl z0821jed~7NjVmbNe>2GQfz<(n`!6FfW`H3HbPhlOF*1~YW%z^?mp?$FQ0_mZXa=zv e82A_f0t^7C1v;)o-u|+NG zW<3NQut~OM;leDNQJf5SkO?LpqPR>M17Wb1Lx(LC*x&xg{>R!~8GUcQi|={9o9~^> z?9Og~Vs$2forTJ}j;C^bxz^p4djRj#K6uqu*F& zDoFtfAOQlv11vxT34jDvfK*G1>hY+}&8pk2INwz2>*cyS*>0CC7SU`Ls;iS#ReX8* z(gV_~9z5t%&1SWxMlqR`iVC@`Oe!rEi;9H8?Mbbc*J!xSn|Zx{F-^0qDoLs!D9NP4 z^D@UtEGsgsz%WUg<|&Gc$61o(5{YOiMADL~s!BStBFnNkJxzDEB|iCppPb}*p5r){ zWf_JUow!H(5>h(C?OSICz0M;yxSK;XO*1SzJUX{R3X&{|qA1+G6KnOvJT7i*grcb7 zk(rf5`i{)7BF%^tEySrLnc&GdPm=8NeP(HeiY+G=WAW%>d})~)9=(eVBS%j!IsB2@ z_vR0H=WE^yn;PegZ_F58^M!UffFo>*u;zTj$$ zHhdI${r&mF{;V%!xS?A0DcF41AYd61O|ccfqvi$a0&PZ z_!{^sl}hy-4;G{!oKb5iE$-~fB6HybvfcVUCE1beht_V&8rX680Whd9-f3DrVYOh@ zu3Ss4-}g4If8^oEHawd1s-?aCWM@~$XD6zSrpkR4d#%QaTVrFl z$4CAe)e;5yTQzoKi}qUY<-V(zu6*0rOWno7^S)-L^w+yUyBZPY>i1 z`T2Q-HcwMv(h#~WJ6_NgY8RSooaXC46r*1+)p|C6Ucr6AS6 ze?v$p`UNC^{r~j~$Yzw41=;`*Kr9Sjzy4=qWBB(EtnWVvLjePV^Y0&s&&2fa%^QFK zVt@JYkBy(1nSq%Zss!k?M~{APS@-YvZ#JJG201x|0gQ}qez5@r5EI0wa6>`h%cteq z@9Mue|Ns2{KQdro`19u49*F+^0|+3-w;#WA zv9bOIX#?r_2Xj8qX@CF21pWab0}C_LlYi$g0Ro7TLGWU8ht%Vjzk$~M{=)z!|AR?5 z zAShK>r=GiC05LK!p9Mw}I1-Ub2;&b@6#s#w0Du4k00Na${#~ri&;S4c07*qoLS5SURG5G21N!2X$A&i1_oXR23DYE28RFt z|Nr~Ti@vGUgzOd zz6NIK7aoF>C?xLABrvw6F+YG+GFTUWEYs~4w>>#>EiDl40tm-GAU=gY_2*Xy-0 zux4j(Kd>NS{e%k}K6H5KwP`44n{R(HP(F3p|Ao>N=@CSr`0ZAcg8K44y0AfN`{qNtu@Y+U@ zT7QrVkfPsU#_wMs1hfID1|Wb~;Esc6TU=EJGWFLlunkbN!Q9_}L0)432p}e)sSwo= z4bd$f5a~a^fBpd(3|0LbOac`E1P}{Q!+)sq|NsB}pI=e{a>g%&)4?|U{s;0mNCQ9s zG5v!Z57O~Jp}QY!#kkT70RS!(0IH-~DSr z**X{2T53>KD8MBTfc&cfKd>lY1CT=iK$8GSl>n}p+>XfI0J!TBhoi~o%15%wh7=0L z*x1*wEIeX?>-BCQ}*=2EAUd)9JKYEsCOQwR-Y$wl(+k_bl4?s~6+| z=Qty>5Bg@AQurJ(nM_Kh(wduQ$)uUjP%L@bUjWYD9(q22HosH~yOBD{xY20D>aa46 zMq|mOn$r@Dv^dj+Got{EqJHUnt&r9ItyVJrMEuoav6vWG9IH=^FfdPz08p>J>v*H7 z?HcEc6*VHc*=#maV|0{oH13c-<77X;aaM0zuZrKUs20h&O`laN6@nmWT#zOz08OL3 z12~c^Ymp(7LDoClTijmOOMx3 zV_pI5O%?l|5P8Klt`DzwJ;;|zrS0wQElo`g4GjW;fXCy_&d%Q3&UeSw!kr8`7WvU|$17~h;l_)ssm^i`C3)rD2kD>Y?Ac2Vtp@ijiT zTvSrk@KC~D&*rog*FWeKa=8VMZU`#cmarRntbIwP{H}tM?98B3M|TDU(xRCQ3)if4 zb>@{;_LOq(tl)Sw+986}4K#X+4}!2%O4mLd`R=4+@y(D!Zg1auZmF+3ou)%qzT`X$ z3BetBy-8Z>m1_<0exM-vGoA77HMk0*gZA`q%; kfBO9cq@SfR=okM7IQ}JuYyrh(#Ep*FFyyd6;r^t8KV>J8R{#J2 delta 483 zcmV<90UZA62B8Fy83+OZ008-bnr@LH6O%gwB!2<3Nkla# z_wV1IKYs$rKY#u(GBPT;82@4uuZ@Bjb* zLEQg9?mr;O+S2#`&+mV~e*ORb`)x%NKmdU>F#Kou{O$MOzaSM~zkdA>20%8@c%VIe z&wrl$`}Onxub&LUB0x(30+2WcIRJn#2ut|?gWYu1Cf+!-K%B8#df?1WA}#uZ8oj7u z>$I1i0Al&`=O0k%-@icgAIJnM0xA6maSq6BK-ECw|NZ*S`0Lj%1_pot6puj;05Ax` zF!=umqj7^frO?t|3^&I1kxUq9yECc+jDP&0+zosI!~#?gbjk0(K=L0@-h-@h6E`~`**Kmf6P1A5}mKQ2yYAn(r~CP?@|;*k*yn8hW5hD!kny diff --git a/toxygen/smileys/default/ro.png b/toxygen/smileys/default/ro.png old mode 100755 new mode 100644 index 57e74a6510dd6a4b29668db181cb94727d1eb4b7..6d38ac70b2abfe902f34339d75885f9215534083 GIT binary patch delta 886 zcmZ{fYfO`O6o(ISu?UK2QBn=UHeu5id0Se#6%3cONUapGt>A*O&=y7IQY;9QrEG4C z;)0+=ye0c)bc(pCGiu0U+@eIIMiEpj6x&h?V%zuje|rnQe)PjWos)B(-*a-j3$Amz zdC?01AU`J(+w(csnqny0S`6@g8bHU-06})uF%8g+0nF$DWTyZU8_(S?%K?abP`X8{ z0RDYQx(7-BLDG=d2FL+u5~v9v*!~4j00e*^zzZ*uKA%cUqvdk<$J=*X`WzJeXOK)Ox0{72vsnc7YJk{gwfM{ zGC7%8wTjHncCp4KgAG0QlGfKs%PWO>495!t0U5(c5XzwG_0-f_g2%%X63DD92McrV z?CG*AEiV;~qj~#Y`TcneBc`aeq$?SZk0(T;36=rt&6>_}dBaHV-WRgnFMU24LK|p$ z9YuXfl4}SD59f0IOXK{pv3N!Xz_qGhZ95K)RajqZ+if_mLr8^CAx#%h)JD?%Eg_NM zA`vbS*gmRn01N3h?l>+@w9{@EVna}_L delta 433 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 W!>+o2O=n(@* z^nW+Q??3;4{|2F7ztBl}d5{wT0tn=!KYtj2e7FXPG(_DmAmbNQ00M{w7(GnC|1d~O0_A|B3}9z5 zg4iIRfk+?%h8-9I6$1ni<5!@&kPrhZ7flKX00ImEKZ#`?*f?Pp00000NkvXXu0mjf DZ9<)r diff --git a/toxygen/smileys/default/rw.png b/toxygen/smileys/default/rw.png old mode 100755 new mode 100644 index 535649178a885355c836b5c838d096ec3ce8d365..33f99b9c7169f469a683199025889c5a6bbdb94c GIT binary patch delta 902 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 BA!`5t diff --git a/toxygen/smileys/default/sc.png b/toxygen/smileys/default/sc.png old mode 100755 new mode 100644 index 39ee37184e09c39dd05425db127288def220abb7..a222766bed4113a321c7d8b3f3344227c053f315 GIT binary patch delta 901 zcmZ`$T})dA82!+h5>Td_NPN;g6vR?{TPVnF=hmhj)B*$ASbI%q&3IHl4R~+26)Azi!vAr4KopOK^9|F+)>cldD8v*zm13*6mD05u;zD)^G zkZ5f(Y5{@(CjkNg??ur#DFvFB%SjX^+-`hoDwoS;v)N20v#_ubx)5d< z9TU>hd-hN$ns00*0|Ug}1diit8*A~!_~gAw@cZ~Gp1bq!(!(0cV9H+{CR3{fK|H{- zu~ckiZp8VcQyEs4hDs%uC9n$~E08b~o%DPCR3x&J{^OUmczAxyKkYyG-9h!Zy8erL z`4zeBimYm+3M!z#dAr|KEE*KTAV4QT8~=5H-2e)JdVp#G3HU$^PzfN1t*tHZQ2cW~ zPr+e*gXWU+##99l1TI~(R*({u6bTud36Ny z&0ha{;@j)rOup@K$=rQTx79&zniek9GANU0;51_5PY= zd39wNan3DP8oq40_=+_(b`QD>tIsqk+ogW}WWHK>QCzjS2 wr3hLrM{8adAqa{f$F^NF{72Ap+}73M`G3K4Za0I&F()T#bG356`5VHvj+t delta 547 zcmV+;0^I$>2jB#d83+OZ008-bnr@LH6O)w#B!2<&Nkl3wo zojh4@Vglm)`^WI_6T?3#hJQa9{xKjBKmf5YFnp=5b_J_oHZZtjYFhID|6c}%Ka79= zFt7Z>6#4uApFjV9L&-0jHUR_>Py-PCfAZuz6Vo4a^Mn8YzhYqc3sTYd`~TkG|9}1d z|9|_}|6jj=B8;-KKpOx8hy`TyKZd`5nT(9?%F2RO|M~TQ@}K{Ez)C@?e?v4d{QeEp z01!YxcLP=bXJGj0<#nFn|DXSVfvSK1-v%}nh7PTKf^DES%3a- z{0(&mE9-xI`~SYa|Ba0Pg8}3J|3IYx0R-~2y5`a34}4eo0t5gt0M7pe008tqLe&4H`v9o>{}dSiNJ#%nPX7=I_J9BV zpBQ00ICp0M7pe1p&4t*Es*)LH~F%@-Qgi2lUy9Z{Qw340*JBIm;WEvpA~jeR~c?ITw-7V2J8o5K!9VF l3C#Ef;{r(@27mwq08;Ngw!U83!VCZa002ovPDHLkV1gF99_s)A diff --git a/toxygen/smileys/default/scotland.png b/toxygen/smileys/default/scotland.png old mode 100755 new mode 100644 index a0e57b4122acbf0beea91277639f92c010afe103..44ef46d95d8ea9d640a1de17c728bc28af03f6d6 GIT binary patch delta 853 zcmZ`$X-v}x9Q~nz5*>yOoVsKlF?E>-{EMJj!5KPA*Azz!t&pHuYbhSn*mdPvs4Y~h z;8tjh#VC}FOoLY~hhSAG(6VvKpp*i0mD;gF(3svWJz)L15Bv1;-pk9&%d3u*q3X6p zuLpqiw9rP$t~mZq=GQsL0Djy9Ah-7VU5b=ndMa5ez z7SioGNPGaq4mjBZG&NAVfz$znHo&*R?q&eVr#!96WYQZ9JoVpeYisU6uv9hAZJJ># zO&64=vky(I);UJ)#EE7j|GnGm^ZC49?~$JeA@QD6Yh7JkZ5m#pRE=awCkeHG;7WC3 zjm6{foNk=~D1~n|<#%44km{!wU7=8D+36z)M{)cStIa+5>g}HMy%~bvAw~uTJtdvy z{6-^HI3|2y2?m3KK)~ zVEf5#LD!ta;jr871X;L`L2I6J`BtckX>#QdNj{m`FoCNdKO!Fcf~`rZ8GdJX0OOI_ zY(@*U09PTh7Ixf*I2pv$0_r+MREPfqU;*up!C-JoqOXKcjW|UjWW?TRkJ%q4C@%3B z9%V&s+Q3JD(7s@f3TwT@jC5-B43kc$7oI9&u$YVH1&j5~KTPwM*x2}wKK^iXTti*G zR4!rF$@0i#GBxKog(j0zW+n$k2GR(*v_uW+ku{A)w4XGAw=pO&QtF6(|>s5f-Fe##O(JfE5x zX#DF%uxg;iVv**Ek_w(XYDVra-}>{JU)0i)86~=Fl^eS};&G+&z2bH6mmr!-xTLSl zyA_^-=Hm0Yg$ynW%jB@a07*zv(gEb~0ZcL#gJ2J)U{jL!AqW;hSjn3P)&EIcxLA0$ ZsQmvE@wvQ63Ffm607U#Z8A=?z>R-?Jn!^A9 delta 588 zcmV-S0<-;-2Z;rc83+OZ008-bnr@LH6O&~FB!2=MNkl|?EKgre_9Y~P+`u#gV05R6?eB9(F1Jv{F&)=r4 zkN>{?@kfyDF9S&NA0T-C{kH(yq%cJ;X2$>j|GP~)2M|Eahb~(P>j;_2@~|>7x~L00 z`G528^zj#OfBk#=;m4ahA8c(TnmuJ$7#V=73pYJnm$nihfS4IPvJFHy7an}3AbvwG z2^cq?*}Le*F9qITY|MXt|6}<52Oxl$KsGS^|MT_Fult|W-DI~E=pbu2U29|zr1b0h zyI-Gw{{{uwAAkS?IqCPiAHUvyt4`1`ljQ;``uyv!bMx8XfBx;OGvZ-koENINM}J=^ zvHRR#R$y2%0R#{e!?73qqMRGc4Irv#9Dm_de&QDs<4)-!`Wq1BO{`^~%^Ec!FKY#!I{`K$AqtAb&xVEQjvM~aU zXW+}<4G=(#z?lB`@1N`9$G2;bFg37pFi7z*eE9*2W)S)ZQuY501H%V~P=_n^`am8) afB^soA1O=IqiMAO0000N3xZElI`^%0#eqS1tLJfu_xD=PzXxnC+Z zy9sa$Iwmiif7KGHQLa{1MKLT|ULGka2^STGM517>Fu>>g*=!%3?nhARU|0s+6-@Avt9Ua!~V@k~xm!c}PfdR9Itm%WooUQ3ium4Q%jWo5E|bZm(P-)E>8Yuy+o)6ug_4|{L?)A2EEaJp z=%K04P+eQCP$)30qO7b;B9Rmp7K%h7p-`BUlf&cjI2;b0P6r{Zt*u!!gMU66Qzt1Z z=xWq%JwYV#MzmBMZ;ww(%-HbCW+f4&UbG+|OZcI}&A3{vUbjxW_4xj_Bq@{Blv;D> zVoZKo?WS^e%X*YX(ow5&OmX52&6Wz@xs9a^l745kuru~Zx{4ytK1-Hlk@St*Paf=u zFUmZfQps=UCzE6>!>&_#=7hZ6NLSi{&zW5Mj`8m1ZgtnqJ2!6Mk`TcPILN$=j6bfLR}qV67{v;s zy4I)>s9H5d01m?8Fc3b2&BfS=Ae$%PakCIafFPQrFN6O8y+N(hn*IaHe~HcV&bYhm P%tQcGEG_64;a7eGP$X4u delta 430 zcmV;f0a5<$2J8cn83+OZ008-bnr@LH6O%v#B!2;ZNkloq98p$6%h0bNdEf&>lcvC zD1R&akAVRofIu4l{rdkz;B`qLf44(3(|^YQ|Ns3*fPZj;ftmT={rdm`#KZt}@&CVn ze*JmE`!e@n)nm4=|M~ck5I+dOY00M}C!Ips}02dHo Y07@-RI+GI9V*mgE07*qoM6N<$f~il}iU0rr diff --git a/toxygen/smileys/default/se.png b/toxygen/smileys/default/se.png old mode 100755 new mode 100644 index 1994653dac1fc1c6ee3c9fcb35c8af97f16eefc7..995f96516109f3940c7311905a25df348808b559 GIT binary patch delta 855 zcmZ`$eNf8*7=A-r>X0ijq(*CMt^L;8(mu+vHM^8ju^pw%a-$S2%;Hz-aH7Qd==7mk zDcr0sQMAy}^>OJ!NrlSSas0vh*z#L`|N6sU?>+DHKF>YxJ>~N*5zmtx<^q6_k2w{S zT~JTO#x0QmN4)`Z@&T+kmGc!K3jz3+1i-xtur#BzIff5lXN=wyCx#8V*b~570c!zZ zWdS`CsA)h}0Z9Qa39vv8_OSqwT4Sfi&|&*{*fyrd#tx+2Y*XoUCT@ER4?l9qt&lDT)9eJu`Hw1 zCADPH-YZUfE-{Yj0m#V}ri8LbvXUmtVEpu(=&AmQ@N>qnGX}xQJHh$2>=QKsXf-2S z@2AxPc>Jh@A7${p1TQccF9v`PKn0-8&dy3yd8_fY-L43UsAytNo&^U)wKP&p{EQM8 zI#TSr*2!=SXpxG9aSj8DB$yNKo#M8Yb7*0d|Nd13LSkabaXTS7d2tN$zfuJJE zgEjVIigHOjJ8MBCfgU1H)7ZNw2#&?i-LOXCLI{s#X)@iNB53D{c*AeBL_ok6V}k delta 480 zcmV<60U!RA2c86w83+OZ008-bnr@LH6O(5HB!2<0Nkl<5|9k)S=ifj@fBpg${rdCw z7k`lb`>(7pHv_{jfB<3vn*8q{15ov!pR5qo|Ns4BVf^v!57RHOo?l?}`yWs<(7C?= z0*LYNK?X*pGKOD3v;Q#s|MUOfZxH$qCjb2cBQWFtZ$@_cPkRpn1P}|u9|n-AAQdnT z|9?TXAyk7H4FCQBl>h_~&`AvcLF)bhMSp=pNE(nxum~g2=YJRg0*H}8zc{Nb;QpJB zz@XUb-V3(sm-*U~-+%sq{P2V6*B@Z`{QL9gud*25+15z_0mQ=a}0S=&`owuI)U%t!#e-GdO^~f(L`_rN$0f5I1 z#|>Oe)}+fzYbpTlX99FR0yv;oT~>e&1i+30a9#nVwvWwMa{(f5h{`1b8vq7?$Hd@~ zk!AoBfDvE^zyP2J*aFZ2XaNqFmyakaFgHi__6BBWsmV#Qt&M1Hbt@EZ6y0xZbk@~5 z#9|FzS-A>ux5q88 z*4KZhn-N4f91ev-hoM7y4h{~2!C)W|@caD~MfrR_uh;AGcu0~oYPCUvFle=Ymy4ju z>Z;G_BuS5+COGb~*$7O7xy)v#$%yal*bD|tuQzXPnRGhi#)e^S?fCe3<>%rw{gR00 zD<%BSP5oXmEuz%I3PF@5Dls-Aj@i{QNn2AwK2H*@lcQ)$v$92|P{^^}y+0PC4Ba!E zQctHOojY?jd9;6cP>Bu?j11L@BsI0wSH-$tn)POMeRFL?%NFJ5X4kS=c{$h7=?}9X z=cYf++-+B@?%e8-qwPA6+e=b}&vPm|J~mMnoe_q!plcOw3A`8%!%n1dt3!z=wmuSc(m>Lrae4KEwt4!lW#v9+x86T8V5Kn9SZ z7SyX-WNHORu2Rqd*+_PF7Q)VAFR0v~yM=Kufz delta 405 zcmV;G0c!ri2Gj$P83+OZ008-bnr@LH6O$7IB!2;BNklm505vgdYyUoe{Lh{}EFK;|7A#FaP8D^M5;ba1<8<4f|GC2UPm+H&ER#kRGsxe?Y{*01!YR zC;dKnkj2`X)zkCW_8s5b+Wvn23^5y|0jv~A0#!5q{{4%A0U&@_fL8pya^=_l{r?#l z8F+XY{;)G~as30ko(ZTFY%s&0Ka9WsFn&r(0u=)U5aaKUADMV~{sRF67~%#@OiXX@ z-vx01#jRs=QBDqgfh#00000NkvXXu0mjfzQMPg diff --git a/toxygen/smileys/default/sh.png b/toxygen/smileys/default/sh.png old mode 100755 new mode 100644 index 4b1d2a29107be96413eb86e64a75ac7a3ba5793d..34f77a71b266504e4ca298b47a877f82050352c0 GIT binary patch delta 928 zcmZo=y}>>~l7pFnfx*${FZV=6;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2!1yM>C&cxsu4Q%WVMFir57|WzGKiXbudOZLdQjY$fuWg!p%SQ* zfgzuPA(w$6oq-{dfg#m9eErXrtIu9~`F-Z>k5guzJ^AR39VB+QvOIB^#bN@qi{e$|_rnNioy>9F2@7il0w$a>g#mfhtlFcqmR;GA(qtRB%G7WDyynE7xhKwC zdD1fdm_yhaV8}8sv=+7;56#@+nY`0Aew$m&Mw{TZrv7Wq{8k!zuAE$x-&c_lGubM2 zVybO$Jy3%+14Dg5W?cNDn9K+XJ2dy#lU1{LCOxtCNchZ)Geyf;?&h}wzEdy3C zFqkqhROi$la*f&O5Wdb7sLW%Tmh)m&yM+o?^QFxeiW|)n)|+nTyNrPW7$e2$RR@ww z_r(|Pjsk`5_JGtaK8c&$W7j)JuCobRY2m-3-qdHAvDXp?25kn0Xak@V5>u0(KX~%!*~6!g(**=Xgv10zg{M!LGHKexsgu(K0z!hq0z-qZU$}DV zTK&bVm(vXlOpMG7O^vs2*s^Kc#;u#v3kph#$_h)1zkm4h>D$MzpVK)wc)0jDdAa*L zdb;{Ld%M#;Jbb+TJbk^-pEz^s+{v@2(={}7wET26b+y;8ShH%~%C*TW@-^~uv~u$@ zbF<&Sc=PIA;)}Ph=NlAWdZHP;{HX7I!{Tc>Y<>&sKR-Kjb-uZM-N8e<=DGf42xBlW z6Z;o)0_Y>v64!{5l*E!$tK_0oAjM#0U}U6gV6JOu5@KjzWo&9?VySIlU}a!%qIk)3 m6b-rgDVb@NxHat5lK}=cgJg;8L~}(h1_n=8KbLh*2~7Y-OMZa> delta 584 zcmYMyT}V@50LJlmXJ;PShVuh)WyQ*rEixxkIYCV`Y7SB)FrqSw&=Qg$$k2AS&>A9` zO)wG_MG6$Ai5CThh|9I4FcC@~f2c-vjCdF7%bgq8kYU5z7A+x{s+sosrp@IB}yL>f&^5mn*>qXso8pJ%TQ!072 z+MKONQu_%f6A!Y%Cn_;j4m&E)oUY)IYgB|}@u)F{x2rgAym3jcy^wZs%b6haJ$2oc!|gMF#;k*=N{$9^DtvXa#? ze+0rtsFY)plLKscWlI(f;!uQXu6!OJ>&F=R1bOS5n*fMAjImwph_KvP;kC`U#5Gyc zNX`YCkl@nfhoh7iqZkt?`L`7l6@Db*|E_$X(Y-f!-m^Iyh*{VGZ=LJInlrkEcLYWW zAj|Wr*FRfe+Tbg18zB$?#Msiw-SZyLi?SZ`VtAVUkvRqHj$CRGf^EmrW`@T3``GQe z(=gFg*O^OiQ0$8v<0Tr)Q%j^!uO+Bjgk8eM#_~ADl=ceCy;b9sh`7O(^To8iT2Z{~X;e_1yjl%}y7i)FVN5m;8- zq0q{e1$q&3rHr3&6*%Sn3K+GvWN&}9Tl=0^J&MBz_=*0IsuilHFqd%;FiLRRV4NSS SsjA;@1{hDBEpZfIdGHsc>NAi4 diff --git a/toxygen/smileys/default/si.png b/toxygen/smileys/default/si.png old mode 100755 new mode 100644 index bb1476ff5fe8e0d3af4fc6bd11e513d95fd9cccd..0e218b672f5974236babf7f14badade61149d9a5 GIT binary patch delta 868 zcmY+BZ&1<)9LB$#H|=!iGR;P9YG+uA;!gsdKUBm3MGG;=sQd$>q%Fns&^gbBTeer* zoo0WuKW1spX|t}Kx2zXkIV^P=S;m@kqDDjvIRt+E*YR?_dhU6i`@DGWS@*jU(1zdS z2LOyTZ&3TrP(_p^=imW=rWk;hRsff;YIz4xj{v+E0ic}#+iI`eFGvHxJ>=~duxvJ) z#bPm=%_ft{XfzrO2EAUd)9JKY?b6E1#>U3_`UZx1FuU99UH5vu9*@WEcDr0Ir_kidk-tBV#bnLQvheqz}Ba- zbN9K;_i`IYnDSrgwRcia4pS})ocm1Rp$x2C3AsMIwwc`ZJE0l)@e0WbrY0E_?z06l;XKnt(}umlkQxZ}L(%AQ|(hklZYD$7rn zosiautCXT@$(P4Xns;yhU4H*|bxEODYK$ueUF7CZ!#>-xJv<@=uiX53=#FjI+B(l& zIeY0scYBwz?P|y6i={lFpy=Sy!jgg`N?tMl*x^OhpD+KO{p(f9>*o`yxrG<=YBgQF z+&94=L}@f!(-xAmFPGEp+d+`Cgz|E+yaJWfRrmzR2$`IKq$DI!1pAT@bT1X9QiuqG nB1lEBa`XeDT2@{q{rdmJhd>zr)JN?}y8{6@EG~06OZ44;Z&iYF delta 448 zcmV;x0YCnt2mS+)83+OZ008-bnr@LH6O(iUB!2;rNklUV68QJ;AB03^ zz`>t?{{RAr1t`kJ#r6L`!;c?-85sX_aWI25{s#gmg#^BQ`2r9?ED(eHr#yb~>OJ$n zzpahB3`|V_u>nR#MxZ4C0mR5Gcz8mG(&x{AqC$k{EPnOn%in+h8UFnF_viP&-+%rC z8GpZl==VP$LqeQ==EOq)0mSn6{g=m2e$<)X`O9Zpu5&$Np~P9Kn=gZ0kf<1I-4aUtk1c`~{K>fBu3( z0U&^Y8d(1RW7^4V^6LZG89>!QBmXe{`hUyt`!B<1wAH(l|OhCl|0mRbE5aax{ z{?n%yKxh8_1#&W!1Ull^pT9u21Gzw{-+%c8B_}gX2M8dR5{B2z-!J|9c8}rrZ=m7- zfBga>2m>zi=Qq$nj6YP285jTphy@ruOh5-pN&@9T&IHQ+{>=zt|6zcTK=k)71StU( q0|XEwFrNP*AqG?~niLQK1Q-Bbh+HusP;e{&0000l8ptmOg#Ko>| zy&J?d?pL2qNd_>40F>PTSf-`234kI5z=i?{>Hz{SHold|0(g|jk`$tGfc5qDEg9Xb ztE(dbLjVH+eE>ZG-2guTz5#pza9zE!wzgK&VV^F%uaf}l1EnGGk6+ubFUGzCasWU6{N{huV zmDp&{srZTGvDA?mGAf)13$Z2$-vg99CRbKg+-~>s^77Ks(&FOc!otG*{QTV9-0bWu zMNyuqKkl{J49%9ykIcHJ@e2*O)>t>HsvgOz8p^mcpt#*HtL&3h^kCJ40L|_dx2|z~ zW@g6aa+%F$XFQs79-PE zeu$4!My502pWm{_-W_neE6zdC%FBM&0n4Wdb zD^YFCpSg;!Zm<509Mk4a4=d|$Ro+Y#CWtN70v<0MK?DfWRr-C~KY;?BCQn=X|G>-OV!8&n%MJDc5R0V37xC!z EzeD_G82|tP delta 450 zcmV;z0X_cQ27m;R83+OZ008-bnr@LH6O%3jB!2;tNklU_l7u4-yFwKrBE}!P;sd`Tzg_o&+gIMn>VH!vBB&F#P%RAB2AY{|zGl z0mN&`Tk_-7w>=n{RejQzkfh&Kn{rf10?_b{tFTZibx5v&dxav z5I~H7|Ney-|DWN1$%1FyagzUW0464;_kZu-0|XGuJ!WQ++^j$M?h9<#^c(D?w|3V5 zet~?<0P^l{s5gH9VNq7Pdioqd0I>kQ`5zo)K&2pO{r~^(Cq%_BkRV7Wi182TkUzk{ zW&j8vu!cW>m?S|i5#<8~8ncWn(_c`)Gk}Ai`OhDa>c4;gfgKMJK#W&mQK<0#y(^&J siMJQbt_chOfyFT-f*D{W5C8-i0Q8<(lBH~=r2qf`07*qoM6N<$f+=Lyv;Y7A diff --git a/toxygen/smileys/default/sk.png b/toxygen/smileys/default/sk.png old mode 100755 new mode 100644 index 7ccbc8274ad8f76f28960b83f2bba2a619029d87..1d389f7114c220cb6c144cc5ebd923233ba40835 GIT binary patch delta 880 zcmZ{fYfMuI6vq!jfJufiWHWq0y9$c(xUImXK(WvwwUkmVkG7#{DRf~1Dl4d{T*DAW zV}VEjbu2?S<2GR%Xx!B40G;A^*7B0IwK8O^)>_K#rH>1{U;SY6OHOkB|6g)Ww@;T} zW3azB0I(8?fF?>1x=ES7mk&@C25_<#z(qzU{{^T(0REE$Fxmh%9Q^8zFcHA(Nmho4 zLtr?8VT9f8Cb8L_RvT`$ViqfAw%8ZUw)q9?+?-{0Zgq8aWo3mRNC=O|<954UE|=5k zTwY$raolX0b$orr(b$4w4b4mEnwHKrI2?|7y)juledG2UyZZQwQb8CD#Hm`BMA~@C zbf)c}Q_U}HnqE|Ej8$j#m8WOwTaDm-%bPk-d-=7kL`@705|-DVmnJ+^` z=M-fxXJz3$9-fxwNJ+txl9uA*?Xj_oboydMgdP9`umjittN<1OGr$7CJir{lEO{tE zXF(fEHo&WZx10T; zJ>7BpC$#Tj{e>{WMMh~=sX(v}63ny>q3xq;@|93ECr@3dP!}6>dAPfXyCg=j+F-i{<7ORfFUl6EDx|?WQ)&g)jne466Qe~1<)5dEL=|9@t%ATs#!>J2~uu|SOfK7TR8 zr!W8i|NH;vFHnS|F!w(OU}R+c^XCsh001!n&i@2LWaMpL6-Urv25| zlz*P(`2GX^{Qmv@{`~#_{QUm>{r(dW1b1xK0st`p&i@3&%J4-(6Giy=|7vRh4-Wrb zUHx`??d0*<@CX3>{QLa=`~Cm?`~Ld;{u~zu0R89!i0SVi2B1U!{r&fU#+3gbKmA|6 zl!@v8=U+^J{{8>`mjUR&KfnL~{sUtC1%C$(KmY;#zy@^s$|;6^G4QkpFdoCw|+c$ z`X9(OzrhIXrr&?q6=e4BT?!CDELIE*%O`E*{00001TxpuL z)DE(;!=xKai=qSRzU**r+45M^WkE&GsXNp@>aU>ppNIGO!SlXvlmBqQ!O%(m0Ag2; z1{&uDQ*#w@DTzQn6VR0a9=fIL2Q&omEe+ru1!nH3y_38W@H?5bRwBYfpar-G+y-s| zH-JXq8gL0{0M0`1(CZvJs*ftL7ijJIn$MaXTaKz*mDQQ0>{QCFX`4T8mVJ=n6t>wi zDYA!5?@CYYO5OBnlhi6r=}1YmB*vNJ1SSFhJwN7c%zDdu90$LH-|)Sm`fIhZ-&omS zS>dQC?JG6(8j3y_71|25-P*h^jrx;1+nQb6U4&-1T;8#-Bm(9=F>) zGBPqeJUlcs2_rW?QN(aCrcPN9vp*qxfG#!03u7gdD2pNueqtt*4B5CeZ|b_JfB=;$ zEOpf$zu0;5X~``0ZXdzwRelp9J;>$;%xn$z2^_#th{>ie$i=TW7&mJ{cVGx zzNY!Wl@s^$XRB^h1?n#u>bbQ)9Y0!nGBdwV{8F&OON@Bjj0ph~_m%85tPzSuPl-oQ z9xq(L45u2-ov+`TK`Orxy^g+_(?ez)sXkmIm5|a6OH3wn`!dc_?yaVq%{zM~Qh&S< zN7R{<%Nex&@LZ8BH$$GQp*^$JuNES!JB6v%Sdv|wtU$gsX!GXhf4!=J>j@uWTcz)I-UjUHB z^Ha)0W*FuxleVS;90~&{DFtAJs^k}d9s&4`0!XU>g7?=v+!7BUs@S|CU5?S?7$t~z zlDvc99T+=?(RL?gbCA|C!eYnFHmAwv93LO|dc8a^i03$tWm$${XqxtTJQPKlEMqL| z5dwqT?fx=sXBdzD?SA`UK1q_F%{HFn%N`sXc~@ud%KqA(V;ZQ`*Q;GF*U&c`9OldV zy+ua;KsT4)$=!JK;ql!)<$WC|AKh=QRh@5a-c(PfTyw3fA<#P{L|Ne=fA%?F(9Pv_ zvf6f5-=OI;=GGW>7556RR%kN}=aS?#gzO5wJ$ z&?ilq>e`P<7l!0@gsd80c^S{TMZiKc?4wJ2Ic*!GYGu$SI;+uhv7bsTytX>8?o6*s zUQ4X5#^WzzD=M*V4LF3DK}(hNuty#-#YS4+O&4@{_O=_SJ@+Vei(An|<}|vpZxfj} ziHrt3tsaMYCQy>FlYlG?(wva$fJ6t##$cr#;%uylID;!&}`U#L&UjzN(Sbtfmt(kFdZqNqxx+O5$j)CQ&P$Ip>b1^Ux! zwOA~{(`L>L2$~)^L+l5%k553w{FaMVvAzqcq83DmM_#WFM-UNxCgs(lJGsBN;rm4-YXDPp<0^yQLWox+K~5X<1j1+rMn%+r^re{6BLgb-TV7t?D`I6&;iHg2AmD$(xOh%>dGta*fMof`gqAhv@xQ6*qp$z~ delta 440 zcmV;p0Z0Dv2KEDx83+OZ008-bnr@LH6O%&&B!2;jNklPEol7!5I{@}e;EG$`~Mq){{FvoV+so&6Zek;JYV*``O13q z$!wq?2!V|I&+z9TKmf4-@!$W9Km&lr1Aob{965V`@w@!1|NH01GVbcn-y`{`_VB12O}o;mcn&E#d#IYwdsj`tKF2_~7pU-@l=*{R{N$F9v`BVqy3N zj1eXgZU&eo3^(2|DB4B<&Aa~QzZ4I{pZ~ycV+7js7aW@a0mR6V{1vDNXb*@4At44R i7cTM#hQPrI5MThdL{FjJ-C>{r0000PM;>g-9V393(UCQOl0HE9?rk~1D#IgfBKXd{d%>(E-4!}#Rj#mIj5P*3<0NM?(;o!NU8WTX; zudZFV4L$>iL24Srryx#4Y6`eVz)S)KkOWu+SOEA7p#4sJ^p|K`qD>IPqIgIYTLiLM zAc860&-;8_T^;LoGgVcw*Kg6I4L>*dMtt63uOQ%}SR)9#QmIOwui#iSTT;U4baX)h zgJE+3(L~hulg~5k#fR{eAdwDM!Z;Jl7-@}$%FCmz)>$dUF^D80o?#C@h`R<{e9Eyv z+t1%EKQm#PO&S>*q10+JJDZAGEYJ>zZ@0P!-PHrtj(!Kv+sB@l)m|&z*<31H4a7b&Cocqw|=Tcv@#T{NKfGhlYg)hC7|Ex6z)^H@lDYF;0ykBoVre5~JWjIg zaiMecyJuhj(Mx=B=g0Z84^8nQK7RY+%Wv*oqrV)#{P6Fr_>+-;Rw(kFXHCKRzPYH` zY=)wo;x+4@T^*I;sKYi-cwb{UxEl=|4oVgDh+bcaloaZ=;W`8@HlPNB20>5+8HNkl|9=0!{p6{xSen|M~Ox z&!2yPfTn)={d@hr-#`EU4p90fz>J~+=oo+i0%`d3@9*!wzk#ZO2y`e*gdd>+dh1p5K3e{b%?M^h35L$j5(y9{BegZamP{e;6150tlo5XfV)8zyJLP zl0aXAJq=pF diff --git a/toxygen/smileys/default/so.png b/toxygen/smileys/default/so.png old mode 100755 new mode 100644 index 4a1ea4b29b3f541f047dead7c202fd3b566575a9..f1a1dfc953cf8d2a75cec3d30a6f9e0453dc48a9 GIT binary patch delta 900 zcmZ`$ZA=pf7=DS{il{JP8`-~^Y{SW#T?@sn;}Is5uHi2c(*30D1)AxdK4k3J_J=b~Q5rAZR{gr(C+R z(I0utA6Cn*JHoztn1yNI=g!T-7*NImVS=We-@{(o4SmW~Ok2Mvc^0 z{odSOGP{c?Hj$9x2(R@wO>y7Yz|0 zwQs0p%dZ65*seRi%lDai2mQB$GTrmGPLr9PM7p5^PdV?w266bz&O5@MzRs0deZ>}^ z#?1U`qn$Xl>h_M`Cv&=q)SvJj=RHYnZbcssTc4~kuqUQit<|Tw$f!-szCpTSih8u{ zU3^KNyhbFoTQUM7B+F$8=b$vM}y z7FTYU8w3k*kW!D(*buE4pz?aX@-8y1gOL4*C$)P%Kj%tlaemzF+}-H{{sNSc)nl~Y zN@*=#jfqqblS(5|Fo5st_2l)q_grvice=7VTo7#oJq|g`khu)o9U!s8wkPn(62vaT zM+*kv+u#Emh-`kpUu_rN3j9B)AZ2@En|5p>IskY&S*n!xlouNEt{9UhJ_t~gvP6kI zcpg6BjT;_HzQjbn!Y=muXx@YN6eWoja)PzEY+2jYxM^TS63~Bi*VxZu;BF}p=&pw z!S!_~kDoqv>O^|-`m`NsGG%hgpLMry-ncz;>yPx!n?BsE+7uqcj~VJ2=rgYE9qcbD z!ipkF&K9dR`&XX5eEyGP`9)zX=pIbTglD>Uw#QdrznNr7B$MDs=%Q4bkaRK zEMKP-qh-p#0U;t3@{uikL7ZHGh+{?KxcC?Z5hKW_TjxUm6VQ}kYE{Mm2hvr=rba<@ O2!KqQmT3G!@%6u)Cd{({ delta 465 zcmV;?0WSW+2ag1h83+OZ008-bnr@LH6O)t!B!2;+Nkl)@Y5GlqQMDuunjyol#xediysJBw><; z@YgD??7^ov!I=9BAQl!Dkm^@ofvQ1Dff9fJGW`7yk_NJYs)3B(V1_skBTxlE05Jg- z{eK540-4GHHiqHD&;NFE|D6>6zx($8#~-kUKOh9u19TNY0I~dG`1hBA5y<}g_y4cI z3}({*g*gAq^Z#dL_#dnH|JmpNJMaAe2etv|3x;0|00G4G>kr86zYquf`+xk=e@^EA zQat}9xc>97{@;He<|?3qzo3u+2q2a}Ab$;DSAre(?f3ul&;Kh50E6KFxo7|1fBVnE z1oO{7kdpuch~@8}f55G0}wzgKwJL& zWd_*`)3EIF|6hMVcAj|(^2sl#@gSEo{9ynHAQpxn|A2ul!p#7)1QJ(_5EZ~6{yGB+ z4`8T(91L_4Kmai^lzwIS1dB)zVnDEwqZt%O3=BLB009O7w{3XsxPZv600000NkvXX Hu0mjffivOl diff --git a/toxygen/smileys/default/sr.png b/toxygen/smileys/default/sr.png old mode 100755 new mode 100644 index 5eff9271d28cf8bf1cb85378600c4fa4997faa33..d6be029e9b07e2d7324163168ac4afefda211782 GIT binary patch delta 912 zcmZ`%TTD|282-!{7?v#CtP;!?SOSvS;ZC8KQP6TbK&j9|OF@}P=>;8|1$to;NY%;A zmL)@nmt4tZm+1V0=l;WM~(wxoI3IZ@DadLGoU>Oq_lr|y`})zFmKvz zA@Lb-0_X!i1dal|Ko{Tv+&~-P1Z+SfupjssJ$HM%f9d{U>acIh*Kxbkee2M>H``o4 zw6;vzo4#w>bA4~XGErw5Goc16!kQF|h*3$1ib5nl0x$8L#IqvD3M|7jG)Gfxc#WYb zEw!If^JrJ=wq)Qg@qEV=;=WA-Y-BoN^1yfwYzOyd`MB{qD zKi1F?wOFD?Q^a79ek&6L4~m4j0{)JgBM4S5XEHPC^mIn2i*%G0%{OKZ!E&z7F7E(TXa!GA)Ft4lvWybB+k-@BS8TN-cdtG~9#JYlIG zHC0|QkRxS9my35^(&b&uS6$HLo>vlIE07J;0Y<<8kO)8r>;yD`0+0h)KrYtT*U8ha zH{xB`&|)Yr9c-GO9E=}HUVRmr6ij+CIWysg?tn55-PD&BSvJnuo0}aDM~lnmw1*x& z{yX@W^U-`L1_YNsWZ0z)1TLyEVb5}I`iDU`+v;Z?#)bVh=4Ljtx)BT zk6itB%r-K5rK{cB>F@A)xMwt#n9TE{kd%=4{E6e;AN7@&<~%49$UC=g|CE)ntuOQZ z(6C~t)!}l69tRgvWLq|$9XO*HsJ~#f*0s&vpPkVt)v9sg>ey(P*YD$KmNA}t`yeGt z^bdxItH0P~4}SB|GwlBLD@_{Sd`;;NPM8Pcfik^hgV$m6I<@vrXM7=t$jQkjG}#KJ yML}qDRa#}9oFKFWG36PG*Xlop_uYPyWI#_=mtHPzKK3u8E}iQD delta 451 zcmV;!0X+WB2Z02T83+OZ008-bnr@LH6O*6=B!2;uNkl3l0iqYp?U?N;d=CfhsX1l$6NA ziW^S=F~0rzk&l)8KM*j0AOpjHFq;7^00sZRBqI~!!*`DW0*DbL*VV)D{5iwlzd%EQ z7JvK)BCiks|Nj2Jn&Usvrr*E+|N0GNFiJ^%T(TG-fLMSIW_kWKq{Jk z|L6Yw|Nrm*vcLXk{sLvE&`kgV#PZ?mYhE_Ke~^%1fCkF%KfgdUESUZQqvk&Y zGb8h>R}TRKh>@X`A=NwW_djq*!$KVxY diff --git a/toxygen/smileys/default/st.png b/toxygen/smileys/default/st.png old mode 100755 new mode 100644 index 2978557b19d7d4283aa9a00ca78dcdd2580edc7a..0786db054472c97b9638a8602eeb07a3f2049732 GIT binary patch delta 937 zcmZvaZBWw%9LK*XH3B6IiZErE41xjsZ)`)xc-Y#5bHFw>Vyh8@F$`)v4rrhaQ43fm>;tV~Hbi>^+&&R+PU>w4F9$D|F$L;vx(LE~k2z52cQeLpY0_x(%3RW~r9;sZfz$~=vDGLYy6 z&I9Lw)4(aLY<$itL&t~&aRaHNl zOy3)9hI@MbL~-$V>cTq($j3H7cC^mcl$K8E_4jnzNsZ>NN;#ey2~HQ2TnLmzN_+l6(6RHUVFCHEEPBcG|=qGH31|o_m=+x6E)aB|Y}UIXzUT zC)zn5`Il>-bDq`r1J;2*TPi1NMdELBa&Pi7Z?HMnQwe)w(&%B@NKEW-^nt;Ms4L+S z{b5`?18JB69@@%Frb_e^TJ7B;^&O?+mi*Y)5{W}7bYx|ZW$?b_aP2JiHHM8jnv4Y8 z2#neY6ay+i8n6S%0x|#wkOCwE9BglIKX7sn29iPLa;eBZHBR3ViDHrEiz!Y@cqlFC zVbQ>j9`|J;fhOelph<`Vdj{>=wLAEUCn+IMg@*0f8;+;WMyn`>(8>Khs@P0cOuSXcF18gF`W&4v&1+_4(+vFaB_+ zPhY6%N20~Wzzr4Z>UOtywe@X|Ou_7OTk@Cc$N_@??lfVsA! cs?z%ZK*$b3)kQ7zFcU%nnYcjY5E?rE1q+{KX8-^I delta 522 zcmV+l0`>jo2gn4F83+OZ008-bnr@LH6O*_DB!2=x{`~^7feaw|@9%GBrr%pd7ytr@ z3Fu&;D>$G0;d%9&ckUlP_FsJLznOpkI<);49~%fU|NOOg<1c>pU%YI;<@gu?0tjRS z1JJ?0e*b^_hvDg;|Nnpg`SWY#%HJzjfqxiYzP}eP__b)suV24@heiGQ^M?T-fIu4l z|NR3(T)!CPK^mBtegy^nW@r25;}3F7RP=97&R-$nzZw4j208~IfLMT-@%Qf^!oU6q z{`r^j>)%h1B1Vqi-od{={s4J^f%8{j)b9^J{;)9rdGL+_Ab>y`p7Z{>#rnHj=X3Y( z-Ow-rh7W}N4Rj%x0Sr22{=cVs7ytr@Mzu2hD1LP13-WQ0A6iLl{wL$TmS$7 M07*qoM6N<$g4DYSS^xk5 diff --git a/toxygen/smileys/default/sv.png b/toxygen/smileys/default/sv.png old mode 100755 new mode 100644 index 24987990b733244b23f8e03059f4924804662c75..7b533d1f72099a11bcff153377ea2737903ca558 GIT binary patch delta 852 zcmZ`$X-tv<7=9gVIBT7j9c#<2W38DV9@XerrevhDP&BrzkjpGhqz@A#FuP`@iPp+> z+O=Ava(1j*HMdHOG`T?}5#&fCi%SsDzJB(jU+>=M*t7R}eYhrknlu^!<0_|od6#IIso1Qygq-&Re#HM_NJ@u`pC&EBegBVl1om}1!q;G1Fg67 z>V`^BS|$5xEdVdi-EyG~-}&d9<#mn&r|iYYZS0z%y&~(bs=)%(Oyvz^mzpw*jSP_) zp#7cPXK{x5EkWa8u+JRO4~%KQ`PKbiRi9UB@b>CFJsS5H^{>y$QHB0@bE`2DiA+pP zM8c79I2;OvzH5cw)HQ)XU~Fv6FY)_*KA+d?bvPVdO1JbeyI!`pL;l0#@pyeco6Qyq zhsMXpgTcV7?$#Ib>zLf_c3Z91*PXx3HZ0q(HTg7uvpovEyIVIZ*Nn>5qo0&NyLw$8 zdq&=O4}Vd+PPeE4FRzw53xyWCz?@exKq)hAE9ob3CC1G~hV_NoHFOPurcR(LS5g$@ zVjV!Mu+b@Ku=CE?%1&8Jj$1gAL566cP-vnH`t$k5-10t3sUds6p1e;Fa075y?au(( z0Av6U0UiL{1-LyqIZ59nzK15^VEUhD6vRQn8*ZlOV3^onTE-e&oVdnC;?_Zc+y_H)~NjbPjoK{4wAeSt_WeQ20 zd_hjx?xX3fyhB;5(ie3Jm9E9tcAGZ7;mBmrd3$yA!`SzU*d@zQ5*=k1p&TNM&%p*H zAxTLI$l3%#3XOmelT(Q)sjCo#h#)gm$vw^gILgY|C0ybE9pZ+{L@8lK3;>x#$#}Gd GQTqq=E~yd# delta 439 zcmV;o0Z9Im2lWGx83+OZ008-bnr@LH6O&{EB!2;iNkl`ivbG${bTs` zkKxZhkO+|R>ko+h{SPR?!0-n`0t65XP_DmP45Ao}{{8vO@b};UzyJRI{mby@&;LJv z|Nr^(55kyHcO4*rSb$3Y{$qIY=FdN%qQ7A2zaYl%KOl8L5{&-+{{2r`o^?wCPw(T=d z0R#{W*boLuQ5K-Y-#`Drq5l6jNChzLng9NUhBYYA|AAr)Ab=Pdn2$32Wq?N{j3E32 hWB)^9fOG%^7yzVLY)$_d2*CgV002ovPDHLkV1j!!*D?SA diff --git a/toxygen/smileys/default/sy.png b/toxygen/smileys/default/sy.png old mode 100755 new mode 100644 index f5ce30dcb79b443ebc1615fe4889cc26e2d762b1..dfecd393782c7da7fbd6d555d2c8c09c4b632528 GIT binary patch delta 726 zcmZ{gNlX&~6owyV6(Xor3mgz-3$ehow1Ex~LhWM1k`@LjP(n*-*@B{}!NQ@00A4`_ z5hWZH2p9EYB6#rNf|?kLP%3xTZ$-ty-((Nd z*l1xi8m6|E#xPo?@|TrSa=A||CJT#*ygVj3{eFs~d_EsZ zk_16`yROl%%Aj_=E&Dn;jPy8xs?glar&4N2Osp*fT_$6y?&$oygeeG*-3dYe;Z3 zr;wCjQFgr+Sh8eolTl~T|K4oYZfkY9(2-XO2r z+vn$d@33sW<^oc^fDr9%SMYk2B=Sif5g8T{es^GKP%e>47vKAGiNy~Ysp)C6&t|4u znm-yt!mR+Rcc~{|exBh3zfLnr)uv{h$$;u_8G-_QgwM}H^0IgWjEA7PLR64{89`74 k5nc~_QS}eed84__X#PKOp_bzu;blYvD5MpV=VERDA5eZl9{>OV delta 359 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 JaleQ<_%A^(QZxVn delta 465 zcmV;?0WSWR2ag1h83+OZ008-bnr@LH6O(BJB!2;+NklYR~`w3=bdt|NR??9{ha&_xGP)zk$Tx z-+#aUN=xzq?EwhD!Wi2C0Dv&)|Bp?Zs+hPi0R-LQn75%cY-O8sAPsw;Q!9X27=Hcx z_y6DDzyCn0!4P6RP{Xg^fBpb%_yyDhbQs7i|IO|5GdGk5nhp>^ARB;YiwSf60a@}Ns2Rxp`v>aFUpl}4 zt>1nl&{JdTf>TG&JOa8OAb@}|6^;~_nCK5U(&20YfB*vk<=b2r3~8l1PL+_+d)T5 z)WBkfSoS)w)COvJCw$o@J9%50_v2ZMfp@an(@ zNe0yF2B)<4dPemCzp|e&_OVMZvGl*h)?bsrt_IcCwxQE)Xte=_Vx1&K{UUxhpXefz z&a+wPSj@A`ou_w-x<%k1Xt4yent)2Rrch`dyfO!;_;OOw#W+n5Gw){x*%f1Q@F7Rw zcWn3%1Rf{@KKZ&=vUXqi+LQKbinBVIwBm|;InG=jV=Uc`S-SnsvdfoBEBUjoV@Bzkr@YQ`DqyTiHI4AjBoEH~UN)(;gG z-q7Y>Q{`SIKXoYNeVNkBVsUSJdJm7+osxQiLv-y$BA^BG01}V@Vn6_JfnF%<3)!e$bXf$PF6+@TaxLM9Jy{F|JrqSNcNv=u%pjh0z z>Ek`d=zN+Y^-!dpWLLkh7g_0936(o&ib9>hvMrBO%Pf_IT83k3qlUqD)r8~4*LQjz zpP9SyW$Uq97yRE(54`+l{L14bhu6_t3h7Wu)nOqBsA8W!h#ec zB_){lgHE?kJmylpEHDy@1=>PNQ41fRD34Ubx`}_aD-~ayu$zMS9@854GcG*@h z27iD6Vgi}{>lcH)J%gU!|J)p)>i@rg^Kt&>WCWsLoQyxg_7khf}QajOoBWFj0A9O{QdV27<_*(tYH8MAV!8_28K_t qi2Q{VslUK64;T3dCV3bD0t^6gZbLB}^V`G#0000xY1A1M6?up{vc$)LId#e^;IhnACFusRr6CfCiT7Cl>2;g@b5FQ4W*Y%8+tO6GF$=55z z^FRa$0Rg}VcmNmR0R9601m;^?qaU|KSvG1mN1As<8XCezW4N|9R8tew=>jzEuc+{; z)qljA^YhVYl#N6p;cz$<3I&6~Kp^1v`+Yv2*Xx~|n-hr#FZ4g^?wL7!bh_)ysm?<- zOUGpUf&2T~?(Y5c_MX;TY|Dh%Jl52Fvu*z!fCY9pHjb;+SCz`ka_W*yHYAZ;6p7B0 zWS>CLOAudkxhFWBV;LFUt5=@|asjiEHI6A1S14*kwr*G=8O+b`C&_aH!5M-$oyk4L z;T%s-|0*^0C`rZu9#C&ET&JiZ>AC^2_(FbuA4&G|`KPn9PiAHwU$LSmJ^e^ZN>_5S zRUkM8a1s)abXslZkG^l(IZR`_OD1zlrA~>& zxn|8HU=DZzJOgHdCx9JDYaejO7G1!|))u_kUA;FgX5viJB5~aFxTGbV#Bp|Z(h|Fk zN()5F_=j2=FG(_JrfREB`{HpW{rKtA=TDx^E`K%o^*3Hywp4GiSgcm-!Gm2LhdNbq zrJRwMmy{_UPEOfoewu!;lE`BS0gs=1W#H1#<-w8RraBW-*Z5&Wz3*?g)9diKUWtF1 ziBDRbnDE7C?QIA4eZHS!3)eG+#nK|#{abf$-}~XmJK68P$5=YM7Ikl_DBW1Td2{)V z>!lmYHYqeJ`{d74zuJD8xrfHI-kodpswT={Hj`L3&DBrXHm1hj;F!c}Q{`5ZPN+5N zVuBnZCnt+onZ?Uf@(5urU&znn5`>T-bV=ji{0n6LM|@zYtgdSQe delta 389 zcmV;00eb$72gC!A83+OZ008-bnr@LH6OB!2-`NkltgkhrooX_w;ZyVr9nx@O{obFOrTJ<7(49h&^)&vFx`69jmV*LLfWCw(VGyY=( zjEsz*K79fRAVx>mjX9Y*@813V`wwWx|G)qK{r&Ur50LQ(2tn+>fB*gY{qN7ef5JkX zWq+kx00M|*|L!xUh9Zw1eg5+YB=P&tzu&+A0@1Hu|G?y5I4LjB$H1@!Ab@~=_y=(( z$dN$T{`vpw7sT2BfO>ub89?$EL;)x)00Ic8;m6;*ldXL$Yz5{poPB} z7`^}m5DPc2nu@9r4=+1782$pC1a=$HjBbBmHi+>D>=Q{z?o+4C00M~d=g*(a%*_A( z{lgVF5CD_~hSJ-&Zvg^`xc{r8$pjy5S5fJGQ)! zDMQpkOjMkdNJ2z^1g6ZhHN`8+pmOJLZqxQ>|Mb`6y~hV0?~lC#Bk^;z2Y~oktBW+y zLwLU6+VvEG>JR`?1Hgf;5`70K#{=xK0gw&=pOT)pX|VuyEJiYu_6}eMU>aZwU;>si~a+ z!kwDx^}IYai>1!VQ8AfoX=$s;$;*j}OB9MSmb?%hJx?Iag@-TF>8${Iz5ej<&}y|B z*J@3>FVWp${c;&<_+>O24F-csrLtPA==xLB4+Xl_YuxNcHER88old7)Sy|C)wHAv7 zRX;JC%?Aeu#?3CnM!QzHSgzCSmzI|H_xH^fvr#2BEO7Lnb9KvA8jVJ&RGLgC6h$Ez zisVJqCTd(`9MXV9iJ~IzM}$>{1(gS3OR>H@KaX1;CvUpB6uLy#MA|Nd0O0iI=XT|0 zw`Vh3G8j@u(zB$5#|e~&6mkPOrap!wBoS+gk=4S;a6vfu0GtGHfTN=$x!i)-c)R?h zL~2>Q6M4>N9QgDUn!`5-H|Jp258Lw+TQp9m#xosP3fOQgAta>8JM(gdTWZ7|3EPs`t9YbMA)68RH&4i=O_L+T{^DtctWn%Q)IRlmaB^gH@|<{DCKmS#fe|P9 zef#IfQ@);OPoMF|y8C$rc=-GD%}P32dR}z3^-4QiWzF5~eX|Tkc8ll)n7czWK6&m{ z+U5uIY1#Zj4xgKe6x`$56o_~t5r>b)5u%s`JaUPIkVqHtcm$7sFPL-vmq2)T3yX^c X{|Ck|RE$Xpq0RtwS}OH5ga#K*q2CzkUI^Y_hUI8vp``#q8@>PEJmUlm7qz4(5Af|u+{y|g&!S9Ft%(8imf-XR*|Ns8{XO&`^x>w|gKB?0?dX^0jc|nV6XF-@gwKKrC?M;S5IEQU;Lr z{|rcuf%_03fEXE68PaOf?!CJA`_J#+e}Dh_^9zW8>|cMuBuMbrpTEEV{QavUu5z*U zB0vDKF#KTn{r8U~zvOSQC{PZ_04n|S?+=9h^A{8^An@-m&<212Vqg$wVBlw9U>j#3 c5k0M)cOinNOzWdHyG07*qoM6N<$f?&wxVgLXD diff --git a/toxygen/smileys/default/tk.png b/toxygen/smileys/default/tk.png old mode 100755 new mode 100644 index 67b8c8cb5191080a1cf33125cfd05efe0b9a76e0..050fd6344eeecb6736c9c916134dd77c5149a008 GIT binary patch delta 897 zcmZ`%TTGJ&7(K)-FmQ^)&1_FDna&HWKjq?5%#f|!+LdCnI&KPXICO3?*;wE9w5OAN=bYqx=llM7?WS|t zyDnP*1Reg{P4~WsX19bH$^q1Sf!qS%m4nKC45SmlDhbfO19qj<^zjY@PBw0EI0J6O zczqa7LqI>!4KxAuKr7G=Gy@GlEw&72onKa+Thi$6b@sF^JUH{>vGVzH%KU8hmG=5X z&A1?WIxOB6BDZP)eN>K}%zm)4`d3!*!03p|VAR*OoU3itnoVS5qv~_f4Nuk>OELxM zRJx_F)$@YX*{BTLleIrZnU=0Djqz&E^`5lrz42o|MyOAh?q!(h$IU<~FqBbb7o^;) zZ>lb-Xo^U({<88i=$IvdZz-r%d|r6DJsWAVgKaG904+cD&Pc= z3gkI*eFLaAH#eUSK04?)zEd>&3wrhXOr+x(;H+eE7|zSi+gv~RxQM-H!!a6S(SyQW z?n+2fDwRgZh-H!$`(pIMa*X}a($c~%_nmLOv*T@#%Chs(BEs&H z_9>fXdg^EEA;OpH@9pO!HyXP%SFd$+cV?z2(=t-iPspD=d$IBI_dlL*ciHN??Tt6x z zQbv=c$s7PGL8W>ThrGOf!@UWbj~~s~?;t_Y2x7Z+QtxQsKSN@&G&V-{|AtfA42sVC PLsx*s2%)!qO6LCys=kQy delta 577 zcmV-H0>1sf2mS<*83+OZ008-bnr@LH6O)kxB!2=BNkls@vYEgFhy`Q>1H+S-zZn1jY0s0Jvhax*-{*r@IS}SX5 ztEsImyF9ejW-q?Axz(G|OEp(s%6S70=NuG>t*5r0zJ34y|MzYG@8SlW(nsI49src` zNb>DnRByIze{}^wdoDoNC4g|;>Y4*+LjZnT0HhB9Xr~6p403>kdsTX)#zlwNW{3eq z09FA=02}}V2mq`AECDRUPXN3b?Tn0dM!Vai=gvjl?#L-u#OYjXZeFw7S1p#X*-X~e z5j8blys~m0prfPx;m|SiYkll zz9hQQ3QginVoDMl-dDfi#icP+tu$`(*({butCh0On|@GyHl?zlmBKo@_dR@Anof=6LFGwT)S*W1f-Y;Dwq)MmlR7eVUm`Wquf^KJI&PckoWq%(JIW$a15@ zKI{ECGfhh?r|r0ZciqN`2~Pg#^Oh6yznKi(z(e}s;_gpB`acU(ue5wT-r7n5Q#uFv zRW{ZU@4V$z|0A+Yw58jEJxKtlrc(V%Wx4nVT+Buy delta 452 zcmV;#0XzQp27&~T83+OZ008-bnr@LH6O%~;B!2;vNklYyO<0*jY=VN1rQ?x1H+jbuf7A2+upj>Wf7$Q%RxoV1$N0e}Fas1Q1BW)@@rKJb!plT3V&1{wp~87X#2eeZT&%{{gn)CtL%> zNdN%^^24c9r5J&cyv!PCR+@JY`m;akam0nU_Yft7}^vgDFGVimo#`=?9Y&PNu~B z_N3-7Y`XFE?&U|f`nSzyY-NT zES+T~mS$#N6Ud;-kh7riTxRs#`!h9*^h z>GNvu%$jsDGvsP(odHlqiMwi{lR~boRHl_^nyEmNA#Z}NLAf`BI79NR%Jbzpm+MQ< zm!<~vW%_iby0<4dwM5%BhFjHznpFoHSNa*0dFzzAPh?r;B3<$MxidgoKo&w8Yfp=MSDd zdiL;PV#0%`kHrNXAnf<{aBP;Xui&rnDC8Q*0 z&zLpyvq3~eROIa&w{BWnSk1KBz2lR?j-AyNRh7Sg{QCKWorR6{vq4KsTdTWEy_@Ut zBga^esjGZ4P*GjJWZBa6l(f|6N1i`>`q`jBL_|z9eae(+Q^P~TLa%=?xN_~Pxrv$S z_AT4CmY0;30#y`#|MKml0t3UkOIjI0o99~teW6<78c~vxSdwa$T$Bo=7>o>zjC2jm zbq!5I3=OP|O|49gv<(cb3=G)reK2Z9(UF^f2hjwO83+OZ008-bnr@LH6O&^DB!2`}_R;`~CZ|bD08&38d>k!>0#NnSMTeb>^G^C(osa zH-GrKc-yzk3^8;6`RCW|mk(||e|YQJz2ATT0M!Bn5EH{chJXM6v9kUD`20H?>%TMi zuO{1voVt6d+$)WVk%^O){nwx0zkdJv^%rQ#Z=eQ%0Ac}Z`19}YkI%oqe*ekA`2Wve zhFzDBOo*xZ{rB(cQ`_&qdGhP`Z=jyvAb$k10U&@_fPVV-?=LIs-)~?3@pAq9_2+dh1^x213 zegn-0x#SPnhCe`W12q5y5Xc4~la2K+$FJWUT>pRl`2}&nFJR#OfocG`=kK4t|9t)e zH2?$<6EKW_|NFydXl}yBz@ec13+jO1V4c5y{r&@S4=`~5`~_(M2q1_Je?H&6#UiHo z>%(&?Nr^uo=K_uV4OaB;57=xV`123s2Y>)#WZ(xy@oG&c*4je^rDI2`=RQ@qUF~k=_xm?HI?6OO&`KqRqGUz}rPKLE%ZUkoY%J__a;2rL-OfCD zLU&%Fj@FXt=|pNOsnPfVg!+2HVBj%~_jtnf^;~Z+>-RIQt#n%(_3GunlL`*fCD0j~a6ij_ufKE!v6qsKbUZfwPr33tKLd&(pS~O7HgH zxn1D0n5`&M>-#o0o1B=muDi4AYK0jc!OHbh@4b^sd6r@V8yiPkdR}fFwth=~cWct? zjRx0=t<|<4-&cy8NF7>(!(8pKs7$9VVgVT8RSooaXC46r*1+)p|C6Ucr6AQA z84PlA{{;m81Hs|L|BoO4zkmP#U%!B=8D(XGHUI<=3)t-cP}Om944*&$pE2YA?c4ul zWq<$s`TduX{%>gT|L_r@28Q3iff@h;hzaCwplYy<4FCT9KXmB7hQ|Nt)Bg(#gN*m^ z{Qvt8$Of<_K(zn?!~}BDpT8hi0uj&>pxItt|NHv>A2{&e(C|MmFGw7s;m;qS=>P!) z((wQ9AF!W(0sZ}7M&|#9_5TYB{zpfHY=8Lh0j>e!B!B<{ISC@f@Efen#^(Q-v;RXw zL4Gwe`+w;YBz!<_{`2P#0|P(+u>dtNLxh0ZfG(-1_+MD~UrY=XO53*m-?QiczJ34y z05yO@1?U`r00L=X`u&GNQW9*;Uxs<}fI1n$MlwK1B_&800ufL#Kmaj*MU6;2aT*K( b009O7`YJe!xmtCc00000NkvXXu0mjf4g$>w diff --git a/toxygen/smileys/default/to.png b/toxygen/smileys/default/to.png old mode 100755 new mode 100644 index f89b8ba755f5609dc761384fe0656f73c854031e..63949b1b9a4895e6f8b780cd6759d6cff0b24f68 GIT binary patch delta 763 zcmZ{hYe-W86vxjJrgLG2QB1-NouN*5xAwqowg=nnHlJxZYUb7DO2I6(q&~E1^(Cb; zA|ghQWf3MNDPj^)Voxebq%f_B-Y;~IySw|WOMUKe;Bfxu|APbn{~qAV8DP#Du{356mmlMyKTjdxB>aTvxK{4pq4MCB7XkvBiIm zPfC<7+T?mB;H4qdYOYFn<;Fl@&+;tatl5`Tn!igZOxw2ZpuoJkZfS*Lj|k1a;2Z84 z@ZatVoV8Eq!Q+J=desHF9=H>IZ z_g*}fOmyto+qMIX4ib^pP&9~#J46YK6FrzngeQuR`xY`T1td;)eJ3*-J3Qg*`*>=4 zr*LFA((`oSr+@A?(=|RFs22`~M=z^--W+lSy1TmtSLK;Et{KkX=l_?s+nnuNw%4^c z=-jOh`~W4QROTVtJcX)Cf#}w%b*h3~1koYLqqaUi+<(Mhpt)sxqi6sBfm``2xArR5 OBm$teQtNdK*7XP7z*0^C delta 363 zcmV-x0hIo_2C4&)83+OZ008-bnr@LH6O#-BB!2-sNkldh6SO{nUiXv3iAlW}aH!uJM5J&?vI8?|O;EFJ{{Q+wD3kpUC27mwpX<+*Ohe1*j zY|LMVKOmiqU?UkIBoO`m3qe4|00G4K6*VFma76GwSW*B8FaZ7?PT&5VWuE{5002ov JPDHLkV1krMnZf`7 diff --git a/toxygen/smileys/default/tox.png b/toxygen/smileys/default/tox.png old mode 100755 new mode 100644 index 1c551f722e34ff9d228a7779f332d6d85fb01224..ad5e1d5dcc988405f153623f8d6987c8c7363973 GIT binary patch literal 942 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ET614BbI1H;e%K>8&EL#Y7+!>a@a2CEqi4C48d;*Yv9Ffhgh_=LFr|NmcASrv$s z6_usLr6k2A73CEnB1#HMg$0EbAw|bq&zKX;Y>_xbx@D2Xc4r+zI5`+1P#j@bT~8zXARM zeZ77E{`~_=&6zz1DE|HXcOci@&E3nx3n&N#fdPR)Et=|@aWQeJ$*BqP3EG<45Hlep z(6iE#(ozyqKoTONrJ)6M22dH$BWfyYKtUNP87_7%VBi21164|hNdN-`=zTe9IiMBk z2aQ^RagtIJ`9kft-cKqPauSN^n+}um{Tz=yh9O8BTHP@|Q@&3N|KJcqL zJ-jhFt?;A2!>^N1!>9c{dg$+}McZ8#Z(KOTqiMs_*pO>0LW1VK4Dx;W(A(#F=by`G zFaB0M`{C`ouiw7iH(hn-=Rcr*j7i?^E({&4vK~MVXMsm#F#`kJ8xUrcE445M3bL1Y z`ns||W9Q>i}%WB5^r6A%TTSEln)UtW2Q0kx}Kq!I_7So;`e;UHpN+ zsDOx&pKv30M^~q}hnMH+6NgxyJP~aXnK~sjM3E`$mDI~wQMarlcUAc)FxEDoIn%Qy z$9eIbGiQ8~7c_8AIA>~Vy!`~LkxnC1n1j{%s0+u;_)lzL=DNZBvWDGVQ++~%ama!L zN3WjUE8wYc_weP@*6QMV6COW)t@`kJJcA&E)GRpx36UofF)W5AEE(Au5fv3KDgoWL z0Sa1HYG!(Nii(DMW@?%<85z!fH>>@+r*bjSSE?nh5hW>!C8<`)MX5lF0hpAHbPddP z4NXD}4Xlh!txS!z4GgRd3>K{XZH}TLH$NpatrE9}i(j0(ff^V*UHx3vIVCg!0FD`0 AD*ylh delta 3304 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 TYO?wj`6&QaQ>F2~0YCQ#_s?^3 delta 430 zcmV;f0a5<;2J8cn83+OZ008-bnr@LH6O%{-B!2;ZNkl5`({s2t}2q2J#|9}60 z{Pg58FvR|AYlEDjruP5RC6KEgJ^H_IAAdvx#7O`F!~(>OU`rVmFaFQR_rItJ~Cb4Rx)Pm-E9hMB8v9Tr( zHw#@B0)<$tO=#yH+L7&H!>&z7RyHVX>mIk!)`!uq`S2!vTID zHZ|3-+u55p>-+mNGcyK*L95mJ{r(EMyGvibToQ|m?d>IvrV*TEEtdMoNIj8Yve{rT zs8*{n3?m3{nW9Sb^F@)UaNhsrn$gH;G*y+V5{=Rn<#xL}IyyumQe?B)@*5lZ>FENB{>!0WFQd~{ zes+F&Th<3);^ zn3zx~6ex<~IG)?wd8M~?EZEyb?HqgU~z zu~F;kGiIA5_2;9%pZxV0UrH$vIf@;@6tdL4FptpJ4C4PAi*s_5ftq~ls!_pRf?!SkSSF1_qq{8g&==S-r`>6CF2vn$LEdw&v;2# V?|u7hRNC1JVAK!mz8`SS{Rc8>fD8Zt delta 556 zcmV+{0@MAU2k8Wm83+OZ008-bnr@LH6O(ZRB!2<>Nkl7Z$8sdHeS5fB*g|DJi8y zMl$T%_kZV3hChG)|Ni|SiTtu@6F>klH@thtXm4-e;c@oTrCYad0kvJfbH^?$?Ei-k z|9=l1`v3aP|JSeozj_5kj66I)?%V+g05Jg0{{w&k0RJ5w0P^!XSy|WJ-R9=z(aMV97z{4m zyy=pj4$^bx3|zy{hYtY)h`Ao9!QA5iyMK58uUrA@u}@3{Dt+|m(Zh!i&t1Fjk(&$B zbLtdG13&+d$BzL52&93*%nYRA_b-Nvmw-~az7PB8NG ze}DQEAb^-_ff|gB|G#|;3KO8!=gC)}n?m0PM`}+X`2t6W!=rsew uO9qDL3=B_MSsx!f$bbZH@7@g%U=;wNV>M8umU^-P00008LC&V?AfgzoNA(??8o`E5jfgzlMA&`N=n}NX{XeI-LH3P%q zSO$Fth6BCz`?_oQbyn?ZE8o>py0f8RTV3wvs;rG=sh8KMJ~^AZGAm+VUwFMb1H=FS z|Ns5__xJDLKY#xG{{8#cuU|iZ{`~Rd$M^5wzkU1m5(vKjEZqAjdBe^4b=RX-UkzJ% zC1}}Y-^CX_7MypUd)9W==@(}W`nS(xQ11TstnA-|h(CY-9(nLCdd-#4m6rpTUG!Oe z-hIJ2r@3b>rk|`>*&VR>9D{88=5w1D9$xgM{=-+FCFgyYoOfGr-f`|(n^~vLr=2vK za{S(LosP{j7{nX@K5zZ|ddi8@bZh^i-C&II?O#|J?r%Hof*$hY3WWl zs?mGMZQdyc;rc(HPdtCNJ$B>mfTb6`7F}?if6igf8MA384JRMhnQ-*UarxXujn1=A zFz{A?{rdIGm-;WCKY#x8>C?xLAMfA4zo|TJeR0y-{Mc1lkt@%(iu0Zy<+iuQ z;qE5u!+jRhe9WhKnM`&w1V)o71A`_5gCYZiGy}s#MTUdP43P{BtiWhuV0f`8706&< zO!9Vjk!DJ`vINLsFY)wsWq-!b$EC`#wNphND74ShrQR`w<9c!e1CyItnpl`w8K0kC zo*WxnT%27UqoZP?V4z_kr>7=cW~QX1WMrgeC9A7ys%UB`Tc)IGDX*_?u57GrEwi(7 zGFzl@uyHZBw|2I4G_!RzyE|K?db*{hc({2vzrTLIe7rr||Aqq#9!$8f;X}uX6)$Go zuweW7qvc5bk|$HHbZq(3b7svO88%l-)(t;*R{msdZDn=kd2>{CY3ftasi9X*x0Zh8 z?d5TJZu-$=`<886%S*~k&CHk>Zfa<=dm8A!1Nud^#5JNMC9x#cD!C{XNHG{07#Zmr znClvvgcurF8Jk*}8fhCCSQ!|w-TTlC*PsE?k(-~AnO2EgM|rb{{X|1`E(QiqS3j3^ HP6Xr#+9@Z< zv8!||KmalR{{5Tr$Jy7P-W3ZXQJuGwuKjf1TeKMhhW&3a`RC7H5cyk9>z9Jw??Y#I z{$Aeq|KGpAz<|r$bsivq7~PQ~6@q?%$udO-11E+Va~alvBl|xrH2?${04D2GQ02+J Q82|tP07*qoM6N<$g1NQuQvd(} diff --git a/toxygen/smileys/default/tw.png b/toxygen/smileys/default/tw.png old mode 100755 new mode 100644 index f31c654c99c023dbed9a7070103c4542326c4464..3e751fd8e3b20e1ad1edf581232ad1f91745002a GIT binary patch delta 813 zcmZXPdq`6O6vmG|)Y43|w3#`@X81_&*6zgXY;A1n&779nCRj}8RtBYp7MePBNt9YM z(_Hze?15IAj}qT`P6Za;V_$lpYeO`e>p|HzjwyZ2t zBC)2W5n{1w9-u`#L6VD0OG{eqcvj7OX_f9*LaI$5Ah~hY=xBn;v_wP@IR2A*jWJMt zbE2{F3rRXi(yq~bl2q%`%10OYNhFtRjpkUw!_6Tf7NKyK3J3M7Iws_`ABvm46gGa6 zRO!+xM#ZP!&c()CIUE8(%nXJ}6&Psd@nFln^0u$CvtwCmom4ftzwBK~$(udJuk{?v zM5i15{7kX2(*TRqju_Z(iy@-+gt&S$(JZ@r9oD=T$cv`nu0`X=Btix0@c-KWKZV z?rc0aP7JWkc=~Vf^9k^xZQANRPxtN6ywY55-Ozru*<>>pwKcS!mSO|-?1Bu?jWT!I`Y$uALNH~IE2-3ix>i!2fmY#7(64_C zKmI&?A1=Yb@DBq41P}{QSxc=BkTeozXJ%qL^7PB!e++;B{uQ`T!2IVc)9>H^|NQw6 zBfo6g1Q0+h5TixD{&Nl$XJuu4F#F5lH{bvK`S<7EgMYt2{r~m*|L3jqy4QG}`* zBnxx_0|P(+fiy6KgNy;L2vgf1poYJ|zy|sOAb>y`n126Zkdy=)^OxZdNGBuMNCpTA wM1TK65Ku8d05N_=jYtM;u?v?10)PMm07+A3;O32|umAu607*qoM6N<$f*;qo5&!@I diff --git a/toxygen/smileys/default/tz.png b/toxygen/smileys/default/tz.png old mode 100755 new mode 100644 index c00ff7961424da8dabee61bfb53158c537e935e1..e1cde1be1b235a6095a3d2ca465875c9568d44cb GIT binary patch delta 904 zcmZ{hUrbX66vhu*HW+Nv37d2AVS7+xLWR_h zRCJ3noKR5#wJ(HiFolVVh{ynktWX{DZ`~x&cBQ3dL$L)-mMpA~d)d>;`M&RWk}v1P zYooCZ+u~jYfXuXi5^NtOT0hieNpk^??F2af1;F1?>v$00C<3rd0^rR6Tg$H8CDH(5 zp5*7q1z-cH1vmw85}*d48o&%-1h7EyFbobv4*+}#mT`-D%v|NDGW~2SdvL_?lc8j= zq;6U_7b2gnsOrxa$Ye5!L}Im!A1fX#R{f|dyjLi{D<^sg;!aMNN9gnM`g_y)d^U=r z6bi*`64lzl>@ceaRLW?wPfpw-j1KWkQ0QIYRa?^b?q#tVDb0;Lp9D$!7*skcmG_kL zUb(a{zimOh6x=`I%*@VCW3gBWf~X2oMx5A0AiLE%eIA6v<)am{9+_b@Z)RESUD#iH zDr4V14iiProk{uK&z>^BWr*#0_0T^nWpGv5;?ny+7O?U1Ck5TtRFg_U-r`uP9WH zVxrQBh@UBsb7@FYtJSLY2X&ey&*C3}fZpTv`+ZwCCvAD}op%$P8W#2B`EwU-7a9xl z<rV5vCMlSy4#`SCoP$xPTsSd>P-Hj{o40k z9i3N=pK3j&hf7NgpFIoywfr<>2>y8p@Y4GuqQ>uX?5yh1hlMwn0G@t~9;!bY*%pd6 z+F=qXP1=Jd9j+TuF);jr zkwEl~fq{o1-i;yQ4}+sKR0%);u`qmLsCKOeDgOTti2nZn`{&=^zyJOKfs)9d0I}a9 zEPoa({2d(p=lAd5n>K9%2p|>)7NFw)Prg0{D*gNK@9%%V|Nj2V!2CN_d^>boB3^zoi8Kq<^dY;r;S^(fnUqxBvd}1L(S6Nio0OBSn5P zur{75eDTwY;mZer0Ac|K5zvcXYJWWWe!YMGr>ODwiiQnSFzw(R!iEBw8d}cZOQq0p|BESFr z{{uommxCe)Ab?o(RT)xK{%~+eN=p8=v-s;3!Oh0;x9>vExwpDpT>ll>|Nr^V@b@p{ z-~YgnWB@t|Ab=QQk<8H8$lw*n&@OR~VeP!@41a`xatsh-A&h_jKylB*01#jRb>t}$ T_^kUq00000NkvXXu0mjfHbOOF diff --git a/toxygen/smileys/default/ua.png b/toxygen/smileys/default/ua.png old mode 100755 new mode 100644 index 09563a21941f2a94c937d43aceda1aa546246302..100319bf3b4c1328ed52a39c09c028532a11a60d GIT binary patch delta 869 zcmZ9JYfMuI9L4`gY*8X}K_qBI2m&IH-WCd>fC%=2T`iAMv4)3WDOgZuwjX@MRJ}*HHjASar<+bRYn48vs;i0ixS}7^%z$2pO-~sz>3Q z02~p(E;Xti3K8Mn&xqUp&C(I~aKnm3p2``iW5YlYmYioDvN#Q9mqH3<=snzT`4j)X(Pi zG1-@Bv&0MxWP=JgdI;v~uvV z?#jb00}nPk?r-e9TjP8L<=+c0K@eJbzMSJV9EY;(CWcYbw34D^B)Neg*5UYCzdr`U zR^WK{D;IEH;Q2iqr(xMbn$E(o<$k}y>jK6T%$r$OO;Pp?l1w3p1RRgWut=XT!siRe zFqzj0)N|mtT`XJ2Fb0O%M$;7(rKPA6k}M{O0vylv`;{1`z_2vWAP~>M{{(!0!@_-- z{|n~tz}ytfPQvspcry;K#^I$4yu;veLLd+@oxCv@yg6i-wp49DV{iE^D(IwEjSiLm zBaK*^7@pSEaXuK0YScyguqVa_SX7eKys|p`U_@EQzI~F|`udGug%rg%ep)GSU!qBo zNb1coJ92G{(L__!Hf6^$Nvtm2l51Oo))ho`hHg$UN}409G7p4nQ}?c}$vZ49O_sDo zZ_hdONx974x^`z_plc%T*o#y520k2j_spDqdW}AMd)=|uXC8jI>b8azCS-mWpO+l% zzI#Wu{)B8pMoPMIeSgnymmC-SdRupYu_wb~HtpWKF!S#1``P)qb4dGx*N>}V=z@IJ z>7-4D;E+TsYOppnT1_hBep66DfhZK|NKU#uORbk9s!XLynU#tlDg-e_Ob-8tu(URr cn{6Kv{{vQ3buR9fCocuipk-=Dal_aD0+s`#{{R30 delta 383 zcmV-_0f7Fa2fhQ483+OZ008-bnr@LH6O(lVB!2-=NklfrxRp$%%|mt@T$`8RHtNB=VjOP;0(EL20q zlMsGO*4#$`u`n=!R6qIpAE@;2KL()0-@gpM|AV9N z^oIc;fIu3U{sVPN!bCwj85w_rBM(CUfvN@u`#%PN0Al=l6BNJy{y<3vI2*!XKyX1E d1_pot0{}>eZs_4h4jTXf002ovPDHLkV1hL)oVWl0 diff --git a/toxygen/smileys/default/ug.png b/toxygen/smileys/default/ug.png old mode 100755 new mode 100644 index 33f4affadee432c0d4f499fd7e04736a29c48b06..659f629007fd9a6a15ba86c59eb4e54b70ff3030 GIT binary patch delta 870 zcmZ9JYfMuI9L4`^ZdJUJO;A&|QJfT`<#F3V2?Ye2-m+4#fW?5K)>4Q(q>6=Em|h`B z)ToiU>0CO9Mz`pOW}A|T)(0vgL3|@RD=HCzsk68D-PgKLMn9dLob&r|a`sL+EZ8HQ z`XK;hrh2Eh%?-sPOTRA21!#x{*wO;P2cj+G0Cgz9qya$jBfy+>w(k5?fS|s-96c5& zY}4k;;&@$SW9`O`H5NLPxS7QeEzYPb7Zl9 zE18F>^st;7l9G2N5gSTUAvRO_B#Bl6_+FUXf@|cz8!j z$}9l0*{sv)G}#&q!!j~5>~=fLvO^fw)zuXh6(tghghF9hSeRTc|LrdrepGq$j%M%{ zHgF@oe^AjkkkH#7f4Ohr#onm%mqqp-;o0+H-S$r|U6pOv1RMt*PdU%yDxOd0I0efl zFiadxFQBM-BpKmy2?-+9=?rl?rxQf{$Ote1?yW|?c?E|TagBxSH+hUj%b3-4wUVkx zBg>Lq#zev(BTC|lr`|6$T!5WBa|U95uYUQtt>jVaSp@mk z^Fgz+=gULJXEVPmxGnn=sU?DUWcM#TRyi5|)3S$(Zlr<#xK-U7{Y&}S?0rjz0(TWM-f#L^9 z@@Xsg(N^q=_(<9FSw9rsLlRHbQ8RYsUXSfu`|69IS2&O~`zCK_bdJrGV(q_M;cz%W z-4fS1KKgxgV1U?y62 fvDR#=D7U^P-UrlG1&7+C^Me62*kaXjrJ?yh3gL** delta 469 zcmV;`0V@8a2a^Pl83+OZ008-bnr@LH6O(oWB!2;=Nkl21)^yg48km{|lD>^Zy@M>Aznf#=l<- z|9^iou>NK{&HxZVEKJ4>jAefr9{u_M=MTegAo-i2ynN}MJ5kZm42KRel$J95{>|{~ z*Z<$Y8RdVo9Ap3pAeIsa2H!t_KmPs=bot-kKnMR0GBy2s>sC=wQFmwO;@`jj{`v(% zzkl=p`BTQg01!Yd{Xp;j{qx|@pI^U!0e?OA`}c3LW5;4*VysrKJaFhx_s^d|kzc=l z0ZFCbzh5&j00a=@>dg$gnpr=--vmP={l0ah^{`_SCdOrH^ zZ$_X;{{jVpB7Z=Pe}6$z|Ns75$^Z~Rj9-C%Ktc?tTr?>l00=Mu9v()G=aTOw00000 LNkvXXu0mjf5)bAV diff --git a/toxygen/smileys/default/um.png b/toxygen/smileys/default/um.png old mode 100755 new mode 100644 index c1dd9654b0705371876d3e3d06f950be02de2a73..2f425ad5d1664ce6ec6234b1016b4bd765c244f3 GIT binary patch delta 815 zcmZ9Jc}$W47{*^PKQouDEL&+=mP9J~fhVLkP$&{L%Zs$qJUYxX%{#o;J*~C%N1Lug z%35n>7HDOzR#py5t3hg@6Zk$36~!x+_^;Lb&%5V&_S?H>Z~GEGQonAg6#y^;7wj4~ zI|w$V?AadL=_r<0u%sG0$|(#SetWG92E#aIKPh<%}%KP#;@#+6MTs& zmmDbVi7f0M_V!kg$pb`UKY$G2J3ueM;^LzClYU`wLDa3ApPz60fX~g%wZ2nNPft%x zO-)Wt8jZ%Wu`xkWQPIOr?7?eI%PaN0juBz|aI<*m_6vm}HPsLsrwik0SuBjf(1!DR z0Ql9tqL2FfuhiGtei;k~old9K;(EPaC=`miwfA4D?uv%4whflG4rsYtoXN!KbPbKB zqEIjvOAHWKE}5B`F_}zPo(+zVk7o(xxB$m73{$Js7>?K0*0#OJn#IF+M9SOG6_=k4 zmbUh1+>s|;muUh5)Ksd<({q?i{<(GQ&%nS&0Et90J3BiuF)=zis?lghMn?KABbW1d zJY!?yXm++CF;N#8spW96pdgK(pW4eyZUB4+=mdBI&u{GN*ZB2}C!Ez*=!puxAj$&`H* z);kg2T9axvzWII2(!?#F9iN|?oS()>$xE{=prJGx1@)&;eWIx-!`qkPi+Z3a14SE{ k%9ekC%Bav8_J0Y5GvBd+x@{Q%mmLxMh?P|J2RaRK7ytkO delta 509 zcmVi7Ttz54h6XL$JkA3s3o=g)t?e>01U3h&zo z5I`(I-#Hr#t~u}$WXZu-Kyv1Rm&rE5wSU`QGA~^U2>^!w{~3WKGxO=$vjGB#ELF_O3@CTd)Q21-UsL7>pWEO%PRM;|;df6h7i|KvNTH^4_U&I^!muXki*B*4Oqjg5_tjt&nG4-E|oLcYf{_WE^aaxJ&U!UmY#p!^BW%EEd*)@XQ@ zieI@>I8)n&pxf;p92}HZ(tUk>fZB4;<#G)S4D|Q+fAI--?_NFs7^|yUm6ah6G17`& zP}U9%_d(1ZtXA_%CBI??9~sGohvSB-n2%UG-QC?S^maEj*?n(1 z1&YFDWjHSnr>EoOWW07Q9~;XrTgF91uwh|rNC+dRf0Mrz8=1wBp_BcR1y7F(>gxN@D2_S|sVTXEUr-^KqNkRYgl%6_9h@0^a3Mhy ztgn-#EH0Xvt*o3j&9Iyx)|b!QskTkkMHy#ri?;?584{(!WLu!ylXOg~jYOe@UXr7> z#p%rQ%`peU^Vc0EN>bFaxU}ffIa2M8gu^qo5Miq-7VX{Ow?A*`Y5Z_&G_YY)=T+xi z*@BkZwxhdisf|md`CF^t{#`ZtlI_)*2Mg*X+Sa`6+b4=oRhI9x>5SW~L0ZKtTcXt) zbUNd`;=0KMgskY7@OW#e$P#7I(Bqc|3Qo&fBv6k zsDEa7_krQlM}{w-8NPjEjPks3<_JIl0d3e1G#F@-eci9GU%y&a{(S%by?*fzmXaTz zK7IP|;lsOk@7}z5^Y?G`moJ}Ao;(N;Kr9UZn1POWZ)W`W@4ssc%b(v24lSHl8Giox zdYUV43eaGNFJJ!u`0?-8FV3W{KUdEI1b+}ia`FzKyKSp}e*E~+tmNnG*RPoipFV#4 zn8AN5#OznEUcG$z^7-@U*Z=+9vSkH80I>iAWWfQ(KYth;TiE~o`7hD(?eEVUvT1)A z9{uC<_6C~G@bf1S0U4s%;WzHy00H%6~9r z!xDf1Vv$}jfzcy`Y3siKFJCfz_yBY<2>tl+AL#mDzy8A+Kod(6_@CSd2q4B=w=T=e zt9@o=<@xpF|9=LcasN@lpFh8U{19Sbc=Gq}KcPtt00G3p-BtebKPVPIK_U_sqdyoJ mJ~J?U1Eb$yE<_d}zyKPcTfX`*{S7Ms00000Dy!50Qvv`0D$NK0Cg|` z0P0`>06Lfe02gqax=}m;00HArOjJej@$v8P@$c{N?d|RB>+9+1>F4L?<>lq$+$yL@b&5M^uNi~>+9+9@s{-TS@ZK~ z^Y!+t%j~Jj>!!%+qsHl=!{?pB=9$0bm%Zbp#pW4jv-R}m?(ci;?RWL_;P3SNVxHYq zmf290*h`VtMvc@#iqkxX(Kmz7RDYD!0058c?eF~j{`U6y_4WAr`~3L%`tI`h?DF>O z@%8HP^y%;O=k4<5?D6F5@aXOGx53L@m(#r0@vY76s>|%B$?B!Y>7vBwpTg&y!R491 z8Kec+U;$EEGS(@8am)TE~*h-MrMU2!yiGR{NhR`;G z&oO_@EPTr;dCEwP(EtE{*Wc&z_WSSi`Rwuc>+tsK@Ac^J^ylpJ=HU9q}*nq+hd;DUz^xlnbudA)KrwyP?6J4kkLzy(BI+b+u!5a z-Qm~U-__UM(be0})z{6@)qly))yK`z!^zLX$<4vX&A-FRy~D}>|Nnl8^br6600Cl4 zM?|1r)4rep000McNliru<_8W6B@M;n(2xKC0LDo~K~xCW0lNT1{{R30009620s{jC z1qKHQ2?_uJ3k(eo4-gR&6BHE|7Z@1;0~#9~9UdPbAtECrB_<>%0DmYc94ad;EiNxG zF)}kWH8ubMH#j*uJ3Ku;KR`i3LqtUYMn_0VN=r;lPESx#Qd3k_09IF6Sz23MU0z>c zVPa!sWdLSpXlZI|Y;A6DaB*^TbaeoBcX&*BdV73*Qd53^fPsPlgJ*<=hKGoWii?bm zj*pOa0FjcDl$Dm3n17j?o1C4VpPo-EG-G8L0h9m$03~!qSaf7zbY(hYa%Ew3WdJfT zGBPbNH!U$VR536*Gc`IjH7hVMIxsN9dCm<0001R)MObuXVRU6WZEs|0W_bWIFfuYN zFgGnRG*mG#Ix{soH8m?RFgh?WvLtq~0000PbVXQnQ*UN;cac6C3IG5}MNUMnLSTaN C*6xh} delta 470 zcmV;{0V)2`2b2Vm83+OZ008-bnr@LH6O*F@B!2;>Nkl0RRGs1t`JB_UG^4O^l5H*jNMpFbXU^_4dclzyBEi1M%O#|9<}kGXDPok^kD- zj{pP^nIDV=yr>UAb`|Ab?o@{rmIp-*+aa zzkmP#|7Bpf!ubEs&THR*ih!nq(BFR`<3Vou&&$DFy67H20I{&Kaj-B4{QULw|Gz&R zY?;6Qvu?lq@$-*AzyJOJ4Rp^RkdwgVKUqPx=QHmB1Q6rvx9;{n4f5E=|`yc3-e}Ddhdg&_cGw6N4XQgMFMb;fS?rkJk#1Div?EPOFpC! z1FA=l2JH5K($njKbl|(@=E0($JDQpX00}@Z!~-z^8wdkJolZ|pO<#8Qi>$1c(b4I_ z!O7v_sjjXuin6K5I(=$tjq>;dxx88`y#svt_U)9z;j&m>4-HLrc8(0bakRG&+wBuV z;f=W13O>J-%{GUJn=T<-bzTb#wyGBatmAf zOPIvCTXa)&G6eYWVPR=$X^{^9qTlcL`Fviz*SoN=Fh4)<@p$Iu=G<<#%jN3muuo5W zW@l$zZg*Q-&(yTr=l40C&eql*Iq>96Zk0q`DNer`|E*P!Qp%N;L>?;Mmsqq*cy-&? z1*{lDFfTulFmt4|mpB4#gO=uxx5f*~1l$tt{^I?6i}vm;+_|-2>lTBa6_g*eAvb`T z!;CaWA`z;(jI`P`&g-T z_IlD_?6%Dmyt+S8`sTTQ{_?L?8iTp-v8%AFcG5cZ!k^b(sWFTe_cl7Oyng5?|Fgwg zY0wxttH-bRKb^bwd)>d9Kp^E65gpy{ET`p%>cmQ&Mx~I)Q|z#%xC2!bEM<|^2P vAc`m8al;5gKoC^GZ_WP$ocn%<=Cn5Z(}-Ys&KeVY&wBc^#Zpn7kj(oJR(onR delta 453 zcmV;$0XqJH2ZIEV83+OZ008-bnr@LH6O&T|B!2;wNklufM;3|NZsr4^XwNEI-g5fB<4iWtjhPj`qjLFGTICZ;&7)qy&}Za?Wi62q2cV{tUwI&+ff@2v+j@*PmbDfB^^3 zpWna!f%E|l0EUK&@PCGL3;+Sd0*sK~fB#7GO8y3m{`m(n92WW@dx64#|Nj5`_b%UG vpeF$Wh>^h_7L~{m3Lzn}3wq;idz_H~3ji&%4)oM%t)EfYj769Y70J-m9xxaHW zfad2N+sjOZS5J7d2#*%`WZ`bW{s6?Sp~whDQ6xzc1cBo?hG7VTMB{NKbzY&)DwE9$ z`L#lxmdP`U!YGO&%d#X%qA0oqLEw2l5{W1iA1lO1aDcL9>c7EJ-fQ{~9BBhzJy=(?&7$%d+ zB%@Ix9;0bGolbkb-bf#a_;Df8z+?41TFW6dY^;ijJ2{46SvH-{g!>@W4Wr!u1X;WZu3$-ft0 zt9bTWukMq7G{b9}id?@fyKarnYuV}372K(%n`V7!cCT!&#ut6JUpM;dy@pxhjL%F1 zc|ST3{WrSu!1`b6*%!`~cn0$F^{+RLbaBnY9h288w2|ArgM(Y4{O&7yX~gzWy(O>C zw6D%?t#cS{M;+>bLPKF;zF}j2!Md^ngK_P8|05Jg_#sD@3Z1&&(|Ni{{ z_Zx)%v%Gr!Lg4MrwLCKeVBMpmF700M~R56I7d7??$Yu2fi$Kdck h1`*_FhG+l?FaT4QMlLtV%FO@(002ovPDHLkV1h{Y>h1sl diff --git a/toxygen/smileys/default/vc.png b/toxygen/smileys/default/vc.png old mode 100755 new mode 100644 index 8fa17b0612bd318a649571fbc4f68e4512c65c5b..d737c4b058f4cecc3583e12b5c0acf18db345f79 GIT binary patch delta 928 zcmZ{jZ%~p49LIm%EK0lPl5)7)uuH5&$HOCFqMoUsl7*F6c(o2eo{3%O=8>h|hRs&D zH!Bw|`m?SU%dG3KaY|;LD_6U9S@RD$L#+_WLJ%H!KzMS`+g|kQcfa5F^Lg=o@#~2g zp&el?{0IPYGRdgs6*0PaRpFM60QHFgjRyc`si@Hbum=H{QUC}I0I}bky0tkIV4iE! z2ALS(0l+!cOP5g>jmeU063BwOI-6QOq*M-iy!gBRlbIqUeeU(x^H; zwY9Hy?(Fw^g&8)gBFpnWlorldj!-_v0pM5DCxDzP-HMieUpvWxw0KBM{g`5Jf(JzSo!Q^JU?9y2HgsjVR}LPV$*#_>AIB09OHy z>HrQqd%@WQZ@S@i7fhOA!UV6{;RThh0Qel{=H@Wh{t-%zuBvk)?B)9QP;MsvPzSROgW}rvl*YpF-{sz9N#WSRYzSww783h6dAk0)EF$^rmUAwssGJ`+dcRwHlAUZE?Tue4inGm;aR6Yv;&2D`y7U zAmV$$HBue92(n<*-SnQY fyShSy)qPml+f<$L;%S@Adf#DYv0R#{WP<3^+A_s@`-+%vqN!7BmfB*mgFHn&2{~uwnmbUuWO^W@Lcc0CWT}bN~VfVgpF^Z=k^c z-&*{?64ZXRo%{Xw|F0B{-^zTye*OCmaSy~v009Kj@cTEzZ?FK+xn`ojOhkTdxcB?R zuiyO^znB?*fBFSd{patWzkfkC00a;ySb^>Vngb-k!SajY|1WR3-{1d$R6|67u5ADF z@9!UAko;i)2q2IKpnD`F*nmD^Vv=NF;ALR=9j*+t?hhlwZ=ftQ(;sF2zo0Px3(^1( zK#U9wTfuSo1St3RXdzGt61(td29okc;Kc9`6k`AZ1^`ccTJ_GYkqrO<002ovPDHLk FV1jM|4oLt2 diff --git a/toxygen/smileys/default/ve.png b/toxygen/smileys/default/ve.png old mode 100755 new mode 100644 index 00c90f9aff09fb1b6d697c6a5680df01f37cad60..629fe460a621422ef4458a3744d16d9524196c3a GIT binary patch delta 907 zcmZ{iZ%k7M6vdB!77WeM*=|y{2^%X$DDtRn2`z+B3a->@p>=i!5}=f&C}Hc04uq5l zaY4pXPzITuW-erjbAE733_7#8xn!aQOJOXm8jFj|JGqUpoY z=%GkNMp4D#Z~;l?heCXkRIJ@Y+=W=IJr=8>X(dG!g~Q@dC?^=q3WemW;|Z4gr%ylA z>0DZ^OMP@+sdTQbk$ykr^MyU0O@grJSaPO#yvdA=$0a2WU=aAxWLnl}o*qrOE1hFw z>r&~gSnRObJZ`r?J^fKy+JsbU2L^z_ zuVV2O&{qcO&F1Tu6(ghNmxfEMLz00{g?;^b=ReLq*ZWp?cez!DIT z$G`08ktK?3X;2=LU%7hgD^_AjGAmT&NuH#XWFCv(-8PmuJy6I?wL9kYb#;vmP4)E# z{p#YgWzW)zesRw(&hA%UdYL_9y)^XM;IQ6$T&=Bsr|Oty?#aybZ}wUJlLBJ9e_%%R ziRyccYVvVzD*xLXEvLMHz5d!4w%Quk)8f>kdp~q_dKr4#)}ET4GTG0mKV^2l>1D26 z(>J)6*}E?eajUBx(+36L-TKfJ@UxQpFK{a>A5G+Be{=n0^TztCX_r6eo;We@luGX2 zIolD9>=UYN4~X`O8KdLtsnNodA=~)m#VplUH1kr#FYY8yRV0Xb=5n36vEFQu=uL*i zfPfGPa*2Z6e4#d%TxtYooEL{r>|TP1g&9`TLRqMfnl= JOI zfBzUj;2#M6gCijC518=>ga87F#bph{rqA`9MSs^`;|3)4S_3BTf>{`>s@ zo}L{E2?GEB|Mm6!N=n`P`~Ld+{`>p?A0G$7!M*~B3FM_eAU;q6NCH*w+4I@c^Ny?Q z?QPpWZQiU1R1HBuWkA0H1P}`l|NYDG=YP-tU%&q^m=97tZQ83}zy1SN^!2^``Sb6t z-O5&$Hz6hh6$1niqcekbc2@q|`)~gOHU9eZ_xImF5c2n*zd%z#B+vnW|NQwYq9js! zt_mQ4Shg`7W&Wf6@Ao|hup$4!hC>-}kw3qI?qK|@62ia$5I`(IYnXojVUUyr$~yr? zfgT6yWCXGQFhEEk0y-LufQkVEi18~lW}%1ym5U|?1ONdB0Dtsabd%ROzW@LL07*qo IM6N<$f`5qO^Z)<= diff --git a/toxygen/smileys/default/vg.png b/toxygen/smileys/default/vg.png old mode 100755 new mode 100644 index 415690798657a5921fd007b8ae85a5e5d414e7fa..b250b1f53139ce5cb8b6037f4dbc6e89ae5fe11a GIT binary patch delta 928 zcmZ{iZ&1?(7{|ZlpCJV9hwl3lAd>6bzPKq-4W(x=kX%nbiL@VqhGIj)vM>8=lOhJe8120 z=f)Mr#XaF00OV)?z11DR)4A6uD=7q;lYzEQ;4gaB_8hPiz?vQq^a0T)JhR2wK-dfA zAvL*J)!L}@0|O0P~d;DTMr=EWC)zyyqxzqhiPDgLg^`Dl<@ANc`iA0l` znL%yW5yz-?;=V`Wl#4`D0Pkp={ao|BO10oPcB4%@D3yjPjPvgLF(Kba5CL^(>EMDl zu;i097YKzxfCV&KtzRk>Gt|Kuk__eL1%<+K9&e1r@-vx!l}o90Q~sslf~Ne;OlJT{ zwOH;^REQ*}^F%>`Ai(9)=qQuvOHA}Vx?kn;DqI7~hl@HMmySINu$s+_85yG~DZ>c~ zL$R^m;Q{O9q&5)Hc&?aPEFTbWFwE<8vl`83YV~cUG9;JZl1hVO@nlZUcvjXJk9U*9 z8E3IZ($a>UKpbEK%79`(4$v3n18iU)kO0K2udlCtyYvVB7hx6h0?A1ELi1g^Vc4ib zlJOfODk3TJ(_f!#rbiDcNxnM#ao8Lr>oon+hK-vxGs3q-Y>j+5Y8zhJzGLUE-LFQ! zw&(RX-i(Qj!`}F}5)z$B$@|_;VWy^~XRtu9Is3W1O#VAr0%3Met_XSg;sX*=S|Fni z$`2J5DWEJaQK?H0mubq69M$UHJqEp@!dPiCS5?gpS8_LGgLnwncq zw>sc#%az&euJ(@3GiN{eu~@r8@-OFfr+uQ;#v_2XLPbrYwde=qLVpQSw|*+`A8(qJ$KW%~i7r!G4~A$4w;&0YR9e9{iij fe}Y~H3rw<~(iZ{>NR zp%Q4oeh{F{R&9f?s}O89DU%aSVuj?IGB)X7egnuCBs>L`p95oCsWJ8E1Ct^?iASr7g#FU`E(GCnKr4S(k z0A^h}m(${niPB=KE8Jg4h?1~*FhTmVqV^}R^R3gJXQPXuYL#*~EF$Ozhp>{+w`m(y z=E{JBOQWGTBae6b&{DYNQt*gG_=_@U5XB6!SGAw6W$fO7O%kW-o1OdkJEQYwm|Te- zAOuC1&U0Dy!50Qvv`0D$NK0Cg|` z0P0`>06Lfe02gqax=}m;00FR2OjJej@$v8P@$c{N?d|RB>+9+1>F4L?<>lq$4?~eWb{`c;l_wShe{QUUvr1jeptE zmGtnV?dWIi=Xvz*kM{AR{r&aw>~r<+N&5Qo_xI%S=WOfWRp8k|-Q8NhzisX9@B91w z^XY}?;a}|KRQ2mti+|vejK7G3t$1wS-PM8a=rPXDE92KX=jYAS)7N!%&-eHI)Xu%; z<9_DdTJz^jd1%XIP~Bch-C9G{F^^675) z_VV0ubM4R1*kE4i**??Nqnn%A+}!G_so=f6&6Snc!olsy$nX37`$>{e>)%-U_wd)k_=vrXjXmdU?TyP87K&3|Iu--7Ga z9@yCD^78V}&&~S!`tb4Tyw0ZP>f`6zLgCjX+|_NIoZ-B@y7BSw%gf98`T6(v`1kks z_4V-X?$*M>=()M&zP{%2^7Gx@-PzgM*Vot6)6>z>(a+D%&&|#M|NpU(QV;+D00Cl4 zM?{bH0)zPg000McNliru=6?qc3MCF)>VYZ%006v6L_t&t*JEG+0Y)ZfW)@a9b`DN1 z25uf+K7Ii~Az=|wF>wh=DFy~<8Cf}b1w|!g6;(BL4NV3uZ5>@beFH-yV-r&|a|=rb zD{C8DJ9`I5CubK|H+K(D1}|?P5fNWM|A4@t;E>R;aE6G;sOXs3xPSPB#H8eu)UppnGBPbNH!U$VR536* gGc`IjH7hVMIxsM@BzCcpF((QD07*qoM6N<$g252txc~qF delta 555 zcmV+`0@VGK2j~Ql83+OZ008-bnr@LH6O(2GB!2<=Nkl+B05Jg0{{#R40{i~@^zVxR{}KlW76Jnl2LSi`{?!8m*8cul{|7+&@QC{O>Gk^U z{eJ`)=I#>$05Jg0{{;N}{t6CR|M}we`P%>f+X)5&j)Fc04NUjFd+P9@fS%;@9^E|H1b5 z`Vk8^2@Yw;#}(}C_yUN9iGkt%y>Cxm>VInJ*#kZL?QKzgpZb&PM!~q4*Ax5MSpU5G zUUA}>nWLlNpWokro(2dYCOHL;H*bCm3bOuZ`0qO{)xBUG4|Ns978vo&u$MS1jK}McWZ|(VX`-7;l0)MZN z0^heeZ<$X@NpS-e+`W4nAb?mPK@QQtDzZ#VdM^{Jt(?r7UlNah|I^;TH(o)(!qk)# zsDP1?5g0-M0R++jQ4J(HIRvaVgMiF`-0Ob+e0JrEkgV)qCMF$5MrNQOBxV5ui1GRJ t=V)>H2WSr1S_lKg1`2{jfB+!C02JtU9}|fdCR+di002ovPDHLkV1l397=-`; diff --git a/toxygen/smileys/default/vn.png b/toxygen/smileys/default/vn.png old mode 100755 new mode 100644 index ec7cd48a3468a511e27c49a69194b0ef5564e615..76c3aa74f7fef10a748293dabfa6257f6ced76cc GIT binary patch delta 800 zcmZ{hTS$`u6vvMZH1d+EW%EIe)X+`$ZEln1bZ%|4&*|LsQ)hVj<}RV6rq0=noJGA! zxNh_iLGLCM8PY@Ha~2dyA!uGuyO6QVci%qydhDU6!#V%oAI|@rRnc1VtoX190QA~K zYUosUu+VkBr3qlN1Yr6m04qq--vGuDfFC%3<{?1tAMzq;MH&EhC7cNM>eKq4+8QUN37j(zQzJTm@NLN}N6&L-M0% z#F6am@X?HLYHCCllyS~Jw!52gI2f#rw%I6)g)*B-gMp~2iJd(gRjDEhMMNrH1K1Rf zc5q;pdECO2lKL$5#wp#ReoSamd}xy?Wj7_`NRh^EGOi1wMbxOr4u5vxaL8PU_j#Qj z*WQL3-`LvP-Q3yE&CJO{20s>*z4RO)R9Sok05w^S^9KCJAIghREdT%j delta 412 zcmV;N0b~B%2HFFV83+OZ008-bnr@LH6O%6kB!2;HNkl8RSooaXC46r*1+)p|C6Ucr6AS6 ze}hQ%-~a#p{(t88|6jlV|Niy=*Ds(rqpU2@27mx!0h|3FMKw^`{m*~i-~W&O0;~QF z(SN}3`!`SnKmf4-F(Xhl(8fQ1fG+vp^85d{-~aW0|NsB{f9tRRg1`PJA~XO^2M8dr zlm7gLxRT-bpa1p0|1bLmQVm3Azy5pw{{IQ;B%q-{(*XjA3FM@|f53hMJK)W)|CK-u zK=ku}!>|93eu7kk5yVLV0R(o^Z;+)RSAT&`0=XpP_y2dl|1babf5mUO@jyfWFfafF z5J&?vILLqofK;;po$>4cGq5`A-~Ty&|Nrvq{~w?RP^bW%0}wzU4NSlPFi1**jRE=| zq?1t=WF*L!oPQYj{y@SIh=7U#0*LV|YDD6RV+a5UFaRDIQd*2M6c7La002ovP6b4+ GLSTXkXv<9i diff --git a/toxygen/smileys/default/vu.png b/toxygen/smileys/default/vu.png old mode 100755 new mode 100644 index b3397bc63d718b344e604266259134e653925c9d..c92506efd34697a32f5d08ed2a348b2517814cef GIT binary patch delta 896 zcmcb^vX6a&BnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34f$>U!Pl&6oG{e=^44e!MQyCckGBEsRVED99y{F|HmCo}U;YU=NVgkLc+Kf}Z8e*_1A z_w#$rz))LTHKjZL<>kbhsK`U1A%6n{|M>X)c6a~fny*~j~rBT$*m4|B6`+S*^$)V{g6JZE6Idc5q-wTN5CLOSaM zo}3Nv^mKD|cC@#%v9>fbH8#-K)7GljP*qV@01BSnUtE$O{PMEDpO1HAx!a5LPESuc zJw9Rc=$QF~BS!ZQ>D)b_aeJ@QgX2z{OBpsS%iq7=|N0@%#&Xw(C*168tt`wynnr1r_=8@{(sHMNZd? z2%HiyzG%cSnIS79t)a}t2B>b9iQZ*B&9j=yr9q<1BaJc1+uddM`y1iF z#K6E_;_2(k{*0ZEOO-3S-PITAD7BwJjhWhJYtYN}|cX(_L-ZZ2D#Y^-f9v$LpjvM_J3aWOYroVRv%y>zs7 zHM_fdx_G#G99vvDzrTLIe7ybth64*8ObB4Rx8Xy_i4`wq+}QD><;aqh1zl6FoY?ZE z=ggWnbMEZ<)8xc2BDLNTg7hg%mR8RUbVzEq9i4;B-JXpC>2OC7#SED=^B{p z8k&R{8dw>dTA3Pa8yHv_7%W)%+Z?C^p&>UvB{QuOw}y*foVzC)s&g?gc)I$ztaD0e F0sxpeQ#1eo delta 543 zcmV+)0^t3=2iydZ83+OZ008-bnr@LH6O)hwB!2(9 zYYOuiLy{!DUio#M6xAY-`JW$$&0;d>G_C+*x%`yj(wFKdcUV@=`*r#6KfS+y|Nr^R z@b?enpFhlh{xJRi{r}IO|1k2)rcD3=#KOS9aP8jzw{ISVMlwxq|MBVfZ|gsQK`J2V z7k`la_5ar|Ae&KE_8$WSKmaiTHT?Vg|MaE5OE>+VAUAUr;?j z4FCZIvLVX%uQ>mocOU*NTK^}%njy93_oP3+fBpUa=kKpye|`ZOzyE?r`M5e+>Ws0ZFER{}})OWB&K|FDn=G<#h}I z0R+;wZq0D&~@JO2Cr zyxbp9-#@^r{{cym_y7F+{pTMrr2jH}VE_mqkOnQ4KS8c?rHj8w1`GVw2ATbv z4P+!x1Cadl4;ajU{{lVn|1ZNY27mx!VPs%1=A0*`&%j{IzyKtV1A`f8DFaXr#DK7Y hhyhG;FaQJ?03^jxn@>ueQ2+n{00>D%PDHLkV1nud7X$zR diff --git a/toxygen/smileys/default/wales.png b/toxygen/smileys/default/wales.png old mode 100755 new mode 100644 index e0d7cee1107205e017c840042272f12476ee0aa0..bc0200b3f10c466705696ab5969688319f7ac2fb GIT binary patch delta 928 zcmeBSy~sX6l7pFnfx*${FZV=6;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2!1ye{C&cyn^XE^WK7ah+!Q;n|A3l8e;K74?_wHT4ee?G1+c$6C zyng-qzyJUK{`>do=8cy#C*Ce8d3EN*-@kwV{Q2{!ukGdL_3w@xe0Truj~_q2fBSaz z>ec%H|NlSVz5VgBg%{J}?uCTB-Ld6rsMl2|r;9eWXU)yOfB$|XHS+bDlNT>u{PXAc zu`$E>xy<|sHWl3DC1ZL?4!VWky=Auy&GdJgAy9?E*veI z+y8Gkuppsr!i5bVI!>&3G2_OLA1z0gq_j=BvgJ$9nKf_b+}ZP|>CmF2wn>*Zed;>3 z>eZ}UyMDDDTb9-~?b^0)edpG_n|E*DKTctF-?SMTN{|Rt5%n uEbD_&H00)|WTsW()*v4#{vN175@bVgep*TSL^~-i1_n=8KbLh*2~7YBzno+M delta 591 zcmV-V0qRzV>KX&Ej_X`t47_wNG)5J&?f1LMoRJN~?W_3rFh-gn=a-@E~;z8UGq@b<_1 zUw_;G{QjkN;J}02MB(&;Kdc-80mKB<@b~YZKMV}g5wYSaSzrGC<#lj=zkTD+H$ND} zdANT6R9Lq3!_gzJFJEF25d}I4Ab>y`n3$OVE?Nk3?w(!me*OA#G|l@>c;{AF#yj00SSWuj7ECu_2N*B?HBiGiv z{_O|&^9uYY*7OMb^I~rU=@{`~v>`m8SU8}=+_&K?;Aat4IY0>Ud(t<=Qz6p2g84$`Ck|S0*H|z9~h+!48OqW4+i5OjQbBv dGB5xH7y#DV8xUxE>AC;_002ovPDHLkV1l3cGLHZN diff --git a/toxygen/smileys/default/wf.png b/toxygen/smileys/default/wf.png old mode 100755 new mode 100644 index 9f9558734f0482439b2292a01f768639a287ac25..879d5783822e39ec2663c256513c6ee4d7cf23d2 GIT binary patch delta 835 zcmZ3*(#SqRl7pFnfx*${FZV=6;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z&Ja=C&ZP3Ve0??|JO4xtYu(0@9DXkfnhlV!(s-8`3wxR85pKB zFiZxjU|^`OzWndszq{q-H%m%hu3mjJKkr6n=C#z+D+vjgV`46aho27)KI`XK-^##n zB{K3tNXU~JGcNl1opX0TU!G2;iR?IaZ}U7`ufMMtZw!69MaQ!x^LfM zGqZ9Ah8tN~r|s=eT3H=8F*&NQe^^WF(YkeeRaFlg85J`yTu)0o>*8`$Pw%j%<^g5p zeR6WUBIeag-4gp<>8 zTic_SmWNGE4;dL9(9ua}U|7n)(9gio%fQeHj4=j=Tm}aH%g>(!!-6r%+ueonKa=h~ zAcwug)7O>#89N`BDqD~trvy-FpQnps2*>s0g!+Vpl$4~j#H7^Z=MSDddX|{*Aoa;( zaRCt_F+ow`=@X_faeescdHTeuliC_unyXhZv0nM{)_vsZE`8glcMQZmyjaqDo)4q7zPP@Ri`!PC{xWt~$(69C;> BS?~Y= delta 492 zcmVkoRae`A_59Z>FaPuRuYkEZmzwJTU%&tT`Sb7h z@Be@P{Qv#?FPOb!(&$?_UrCGJgGL;^k4Qujka#`tk7LjnovNAkb9+0R(gv zNCCu>f4~1QF);xde}DY=^Xr#I=|`5hhp|NZyp>({@(egUPqbabS$vw!cFl}V(h3%IyE=;;C401!Yd_xA4R)Y1F? z;K9G&zkoJ?90Ozkef;Ye&?S$1dL&X)Uo2R_ZD7Cvb`n4Uu>djH#lJvd0wf{q-@k#< zHQ9Dh3E3mR}$n{;)|(f`aG|&<8M`;NS;Y3uOHL3qd~^7ytr@aVZ1CH>9}yhs6E` i6Zr>Ya4;|c1Q-C8K|OJTbltB20000gn0JZ{M}bV=`tN>z zuR*HU?cRMYGjmCD^1?ZD4hIHK4fp8_a_|1-ZHS1Ny>9E3 z*^5tkdrx+Ao8sfx=5F2m!_4fvfx*{$Rn;#F3SaH*tBO{{1y8QXUmEJw8{pRE?a=0C z-Q;Li|3zN@vy{{qYwM>B41a+rKmU)v|8FnP->$B|92|aHTm3LK{jRV7O-t*mippn6 z$xk98U(C!NGcf#SVEB`j_1n|)m%aT@E2|$SCZ7ZZJ{uX`2a2bq{c>^nuBZ1+vtIM7 zvho)>xlh8vANlz{a&v#u*Z-ZE^eZ;@XGFx$kkB6i0pGp7zq`48b8`A>Yx~90^0TSw zCnKXzIy!e57#=b({Qv)->BT*ueGH6A-tI0fNf)9DfgJV{PhVH|XY71js;s9iA{GIK zc6hothHzX@PGDeiQ%e&IGb`is)2q*uV`Gc6t7CLjOcV?>Eaddm%w%JejI^v|byZCj z4K*$0_0?r_%ax6_tz~vrP8Kc=HZJC7^VZImj<&95cUMmj77sV)W=kvQ_t(#tkGEI2 zZ?9m`p=hCEqGO|Eq-CXc&0fz=aiXH8rK*jouI!=nC(a}#oIc0+srd6J?nhkp-Cf?B zByM=0KBcX-dR2DT>sQiVQnMuvoO?X++9nRKoO4-OSxgMek8AI8S?&Z3e+JbO*NBpo z#FA92gTe~DWM4f&9^RQ delta 414 zcmV;P0b%~{2HXRX83+OZ008-bnr@LH6O%y$B!2;JNkl>E69J zA<-5ee*C|6>(%ey48MQ<`3*E1sOaY}WDP*a00a=wNxxYB{9yb20|^O$p9gN=c4-lEL#8b+t>Fc4{v!Lofz`0Dd=8H&A%$2-#>q` zEZ*V@)W?|Q?e4&WR0- z4D%ah@~7-O-vG2-wZt`|BqgyV)hf9t6-Y4{85kMq8kp-EnuHh{SQ(pInVM=F7+4t? j9Iiaai=rVnKP5A*61Rp;DrFmi8W=oX{an^LB{Ts5UoQjD delta 3038 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 VB*fJcLb3n=002ovPDHLkV1oNl`pN(R diff --git a/toxygen/smileys/default/za.png b/toxygen/smileys/default/za.png old mode 100755 new mode 100644 index 57c58e2119f402072640ca758657798b621f3fb1..ad4d0ebfd367c68050bc49509e465fb128be4caa GIT binary patch delta 940 zcmZvZdsNc}6vw}0G=dV15-|gnkW?7IO~%Fs0)qjIhl7o$1IAI9^Y+O-)ypbc{kV@Aj zl6A3oO(a?s3RNp{am#%EQe@-;plyQb?~2TU0%32yaH#Oxg`byOTh_C(*Fr*8+3Xb` z-(@;|i9%U)b6etaP3BDj!)Jx_#^cgKxj?6kx+5KV_`PvqcGWPpGBCK@e{V^zU(|j2 zwCC>Pz|ilKY^74!b|v|@>EztLta#mt;BIlCs!JTEm4QwIzq36wbo>m49 zyrX+XR*{tUhNS?OX$I7w#y}7rv}EZdQaB1D7Zr=lQq`IJorvf z)|VKqO=GEr^v* z{WXm?L8X4>;W6gsHl{Mi1PLI9eKV{6);Q1uGy_dQBX9wz1ge%)oi>l+6S9&&RB*@K`^4|~$Q z=nQWr%f}bwBYyq??7$#Sa7ZZk&9HEgBO;ITqN4eNnAo`Zgv4VY3zL#lM5$@%8RE>W zP&ORYMDa(0Vo~t;e%zGyvrwiVFuTWJ~TvA$Aex~AVCEiz6ovZ%fd`)fLg^Tqc ze$>#2OHG%XuUu_uZEL@F{l<;To89*r15U^M8RnZ@6r6ZPMyf*0OqYwz0xChJx)XGF zil=}=Fb~t1G~y6JFbSeI;MdOo08&|oBs2ei0Goe_Uv?+#sHM1C16)pIkcKTP`3G)v BbFu&c delta 581 zcmV-L0=oU_2Z9BV83+OZ008-bnr@LH6O+3GB!2=FNkl> z=-wkNM~^Z5{r&&XABI1F{{IFs;N+K0n*aic#pniuS@`GLGhZG)e0l1{<#LBa)o))| zj(?s0|KJ`--LK#OfB*Xb>lcvCC@cGqfdL=@05Jg0{{a910Lj6AAt&mC*9zzO1pWQ@ zb1`Tn1`iV6=KuBe|Nj2}{{8>`{{Q~|0SOB6tE&PCq@k>=Oh@B|gDu0v2b@p;u)h2C z^Y4EK4rY$O|Ni{`3qc?^$?%A8m^2X}fPYwk8vgwGyK(E+*YBCj{JwYI=Xm{(>&KsO zpFh9&_4oI$zrTR0fBpLn)W-hzC&LQ{fB*t%`1|*dn-i0>{lD1{*dF|3{qg7Ppa1{3 zxP{sN{sWo)_s<`ohJSx}xI`Fu7ytqYq`}{vQA3xp<0ki|&n&-weVuGm&&|aB=6^2t zw=aMH{ssHuAJ7;7c(_?))Y1R~h-H2=gSr_@?n%BkU;i+${w_92*u9)}^V;_>UOxjG z3l5}zKr4VqMTPU?#a#da#PZLJq2?kt7ZbCEyl9w||BiW_XU=`$=3|iO;sm+l4=@7$ z0qyzw7f1qK^A8|^7#T8wzGe`&ur96>n2;U(iUH^^hJRr63&!{ZMhsw*4Isb(;6g56 T8;_<#00000NkvXXu0mjfZzVB& diff --git a/toxygen/smileys/default/zm.png b/toxygen/smileys/default/zm.png old mode 100755 new mode 100644 index c25b07beef894408ae11c3be294d6e0eeb28c0bb..38d8a3c2c82f69aa248a2be5ab51fced9cbbccaf GIT binary patch delta 857 zcmeyuJe_@lBnLAC1B0W@U+#&D!u1Nt9znhg3{`3j3=J&|48MR<4KElNN(~qoUL`Ov zSj}Ky5HFasE6|34fpK$yPlzjXBQrxi14AtXLnTld14A(bLoNeDCIdq<14AqWLnH%3 zFav`x!_-q#r<|NJ@x;Xb|b2+qLu%aW z*wiWnxU`$Sbx^0tkeyuiE9&MSI0)Ihbr6fh(w%R{t zVCXp9k-H@~b$v?0nuO?8(c#O(gO&vOE%JM&sq{`#Qd2}kR7Buyv-N$TvH1*{8|yPu z)~7_Tj1F5C=DX0>bH3*j6`8lf!YcfH0{q->>&+YXHPr5@t=w5zy1le;OJVM&+{_J` zsq0dc)+D`fvUzW6YG-0(VyyqV&h#$OM0W-TTLuP81_on>qYMn!fg%hH=NTCO|NrkU z9hM5@FeZ7syG*Fy>f-@&*h@Tp>s{HOvGZ}Ma%}BXkq3(H@pN$v;kcfhz`*3DmL?Wv zR>tS2mnX-@78hq%$LOe-C>Us1$myxMpkYhTnmK!#7ERjJwQANbw&rEiw)M?fH*a6# z!igI@SI)F#7T({wbluc_TNn1Oox8Vrv2gQd?g{T-J$v`?<Oqnxj*0gyOXHK1ac!Kt%>GMMZW<-U=1Vx3#O+0)cE;2MW zI6D0Pg&SAyT)K7b979=LyIXpAxqp6qe7!Y;=O0~eC95ZPK#!=FxJHzuB$lLFB^RXv zDF!10BO_e{b6rD|5JLkiV^b?rBW(i%3o8QywtF9%QFP?yr(~v8;?M!qP~PleKhaQ~ Pi-Ez@)z4*}Q$iB}b+JSe delta 438 zcmV;n0ZIOs2lNAw83+OZ008-bnr@LH6O(BJB!2;hNkl10tjRS&?yMjfBygt2HEouXg1LC zzyJLD1w#LR1MLR}1V8|>0KE@nffPa91AkTr*Y@xCFQA8i0};b-pud0`00IcC0pvxf zOMb(Yg0%hn^Z(Z`Ao>jiKy^R`3=9AP1kwOfjie5u6hZ=R0BQIG)bN)9q@DpFfLMSY z2TF)=i@+@T1NS;GJXqvDGYS5b`u|q|5=9IQ00G3vkc=9m@OTA_96ie*#K52ljvuS@ g3=Dh>3;+QJ0L`y=OWn2X{r~^~07*qoM6N<$g0rR54*&oF diff --git a/toxygen/smileys/default/zw.png b/toxygen/smileys/default/zw.png old mode 100755 new mode 100644 index 53c97259b9b3e31c2f612e78344d035281682fa7..e8e51b7d560d877bde7d36f7a02c27cd6aa829a5 GIT binary patch delta 934 zcmZ{iZB)|*6viJy8bJ~Tr9*Q}83q&f-xynLqru!5YcOF<83GE7StS(!f3+#&r8X5r~xoQ{(Y6#%(GC>>4WQ_(2uY4v)vPudXJ0Q>3#{0|9PxJG!PU#o z-j;yBz`wA`HPG+%dNU*vp-{-<@d$!QWHK2H28~9GiHRX^=A3d)+Ui`Fx4CEBJ%w3A zg@W7a)LXf%@3IrW*%NespcI4rlzKRzIo?AIrQXSiwu>T%wVd;S$31=SjB?&Bn49Bzy~(ql1dn^2duGjyi|TSlIh_&C z>2Q~mb$1w3uIc9firybfPY)G!f3G=lJI{7Y*>N+g^;^l&8{(Gh!scuIrmwh%zhuJ% z$Cv{_WPl_onZKO;2p}Cu15$t_AOTrXy+V)m^N9S?ax=uvAeX{$VQ>WkU z>3y&7%=_7VuaSofgtUAf*-w!6nU;qF7`S<(x|35&+uYZ4j|NjjlS;T)|pU40ZKrEjo zRWbQ1@P?oGFzcPCjLiCzcU8ZC{rmejNW<@6e}U*1kPT$~=Kk_~83O}A05QHjx|ikd zd7h}_|NsB_wl9j+&d<}^`}fbEzkmP!1%Cuk3PdX^Djq$06d-_DShP(3UOE6$eTbjQ z%6nCU|76i0U>kn@1C#&%fZ2b3bBO=m!@vL#KrH_l{_&;O{R0M%{`*A*QSQuNSb%=` z_xm5i@BfUy{xSak2jnvT{?GJ>jqQ&L0|P(+fjrH?!1(*u_ia0qlPA9a&jJpUe`%1w zfrS#oZ-)PW85#c?wK4z%5aajn-+%o6IeUANgf=Y5fsp_rfB*US=igs=oB+f6Z`GRf z00G4K@cBb|DS3uC28J)NNc{ziY-Bbh?s*sh0t^7^U09U=?80OK0000 Date: Tue, 27 Sep 2022 12:58:40 +0000 Subject: [PATCH 151/217] update gifs --- toxygen/smileys/ksk/angry.png | Bin 883 -> 967 bytes toxygen/smileys/ksk/angry2.png | Bin 932 -> 1032 bytes toxygen/smileys/ksk/angry3.png | Bin 917 -> 999 bytes toxygen/smileys/ksk/blink.png | Bin 891 -> 1002 bytes toxygen/smileys/ksk/bluestar.png | Bin 809 -> 914 bytes toxygen/smileys/ksk/calm.png | Bin 893 -> 992 bytes toxygen/smileys/ksk/cool.png | Bin 914 -> 1010 bytes toxygen/smileys/ksk/cool2.png | Bin 956 -> 1024 bytes toxygen/smileys/ksk/cry.png | Bin 956 -> 1037 bytes toxygen/smileys/ksk/dead.png | Bin 913 -> 996 bytes toxygen/smileys/ksk/evil.png | Bin 888 -> 992 bytes toxygen/smileys/ksk/evil2.png | Bin 929 -> 1010 bytes toxygen/smileys/ksk/flower.png | Bin 935 -> 1055 bytes toxygen/smileys/ksk/getlost.png | Bin 921 -> 1033 bytes toxygen/smileys/ksk/greenstar.png | Bin 822 -> 917 bytes toxygen/smileys/ksk/grin.png | Bin 920 -> 967 bytes toxygen/smileys/ksk/heart.png | Bin 829 -> 1026 bytes toxygen/smileys/ksk/kiss.png | Bin 996 -> 1206 bytes toxygen/smileys/ksk/leaf.png | Bin 913 -> 1054 bytes toxygen/smileys/ksk/lol.png | Bin 957 -> 1004 bytes toxygen/smileys/ksk/none.png | Bin 882 -> 987 bytes toxygen/smileys/ksk/none2.png | Bin 890 -> 1008 bytes toxygen/smileys/ksk/notes.png | Bin 751 -> 1027 bytes toxygen/smileys/ksk/oops.png | Bin 1045 -> 1253 bytes toxygen/smileys/ksk/pawn.png | Bin 989 -> 1221 bytes toxygen/smileys/ksk/pleased.png | Bin 937 -> 1017 bytes toxygen/smileys/ksk/redstar.png | Bin 782 -> 914 bytes toxygen/smileys/ksk/sad.png | Bin 914 -> 1002 bytes toxygen/smileys/ksk/scared.png | Bin 897 -> 958 bytes toxygen/smileys/ksk/shocked.png | Bin 967 -> 1022 bytes toxygen/smileys/ksk/smile.png | Bin 885 -> 995 bytes toxygen/smileys/ksk/smile2.png | Bin 886 -> 995 bytes toxygen/smileys/ksk/tongue.png | Bin 918 -> 991 bytes toxygen/smileys/ksk/unwell.png | Bin 888 -> 996 bytes toxygen/smileys/ksk/yellowstar.png | Bin 792 -> 914 bytes toxygen/smileys/ksk/zzz.png | Bin 990 -> 1020 bytes toxygen/smileys/smileys.py | 9 +++++++-- toxygen/smileys/starwars/ackbar.png | Bin 1109 -> 1267 bytes toxygen/smileys/starwars/boba.png | Bin 1040 -> 1202 bytes toxygen/smileys/starwars/c3p0.png | Bin 1069 -> 1417 bytes toxygen/smileys/starwars/chewie.png | Bin 1230 -> 1380 bytes toxygen/smileys/starwars/confused.png | Bin 1030 -> 1259 bytes toxygen/smileys/starwars/darthmaul.png | Bin 1108 -> 1376 bytes toxygen/smileys/starwars/darthsidious.png | Bin 1454 -> 1735 bytes toxygen/smileys/starwars/darthvader.png | Bin 1281 -> 1532 bytes toxygen/smileys/starwars/deathstar.png | Bin 1073 -> 1412 bytes toxygen/smileys/starwars/dualsith.png | Bin 1343 -> 1500 bytes toxygen/smileys/starwars/grin.png | Bin 1067 -> 1328 bytes toxygen/smileys/starwars/happy.png | Bin 1060 -> 1310 bytes toxygen/smileys/starwars/jango.png | Bin 1069 -> 1286 bytes toxygen/smileys/starwars/jarjarbinks.png | Bin 1469 -> 1644 bytes toxygen/smileys/starwars/jedi.png | Bin 1743 -> 1904 bytes toxygen/smileys/starwars/jedi2.png | Bin 1648 -> 1816 bytes toxygen/smileys/starwars/leia.png | Bin 1022 -> 1278 bytes toxygen/smileys/starwars/mad.png | Bin 1036 -> 1342 bytes toxygen/smileys/starwars/masteryoda.png | Bin 1754 -> 1928 bytes toxygen/smileys/starwars/r2d2.png | Bin 958 -> 1064 bytes toxygen/smileys/starwars/sad.png | Bin 1032 -> 1257 bytes toxygen/smileys/starwars/samjackson.png | Bin 1440 -> 1591 bytes toxygen/smileys/starwars/shocked.png | Bin 1029 -> 1280 bytes toxygen/smileys/starwars/sith.png | Bin 1374 -> 1520 bytes toxygen/smileys/starwars/smile.png | Bin 1024 -> 1262 bytes toxygen/smileys/starwars/stormtrooper.png | Bin 1044 -> 1225 bytes toxygen/smileys/starwars/tape.png | Bin 1100 -> 1330 bytes toxygen/smileys/starwars/tongue.png | Bin 1035 -> 1300 bytes toxygen/smileys/starwars/wink.png | Bin 1045 -> 1267 bytes toxygen/smileys/starwars/x-wing.png | Bin 1834 -> 2022 bytes toxygen/smileys/starwars/y-wing.png | Bin 2413 -> 2680 bytes 68 files changed, 7 insertions(+), 2 deletions(-) diff --git a/toxygen/smileys/ksk/angry.png b/toxygen/smileys/ksk/angry.png index 12a51f53034b6068904e516805f735a5104bf648..2659bf2490e99b072d519bcf5fae26c1acd9d752 100644 GIT binary patch delta 905 zcmey&cAR~JBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z*rmL6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b1FZMZmYQy`tcmMzU^#1Mbv!~W&rJ1c*Tv}J1zGLgW zzrXJP|MTeopL_fFEU2wcS-PMgBgJ&z*3AF)pXYvfzHmx^_Vh_bFCTB5Grg$4GwJQ) z>Hj}X-M1yZtI_iR6OaGTLjJ!<-MBc&&qLkGR>{jndBZ}l|IecSKk@nh*tM(C>f{=U z|CbmxP2nm|V!Y?`jG7|Eh5BQt3mx>-TO-o;fvr&(^BFo0Io$3f#NVr@7Ae z?D2^oUv2vTV?XDl$vQxf8u@qbkO`B(p|$*tM* z?*$q4swywPRm&N5^z*m7^ZfV>Ow$r|egHKyCV9KN6x?9QDX9nY*h@TpUD=wXli6;XliVpl+6CwQb%X)n%tbXZ)9cW zS~xProYPyE_fBqJ+&#N}cWiig_SF4jY;-)RxG?dd;Kab1KRiqq4Gj%97JlUHbg+sF z=xDUmw6wHTHMOiaW%W?!>^A6Fwlw{KL)EMq7R<$IVP$+QDe0+R9!4Bbo;?+JaHxx0 zH^qWEKQYiSWWkOV0Z}cDj?0#aXaz(mX{#SOppYLJ78e*98W|Xuws64-{*qObib{QR zV{?7CcCFeWb;I1|mDH@Zs9RRMlxzb2e|p{dLi$Dbi^eO=432^sc1>p)mOBHztXkq4 zQIe8al4_M)lnSI6j0}v7bPddP4NXD}4Xlh!txQd|4GgRd3=UVG<3-Vso1c=IR*74~ XCY7>{6Ajh57#KWV{an^LB{Ts5H#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;usuL8H%nu7sVNg=mWe^Y)W%%&^CBwbDml;;A zoX7C`_0#FUe*LXmvf|Pkm_C32VuEVcVqsx^>*VN^&(0wYRQQg8j~6KQ?*oGXKQqIp zPk)~n*f=B@oSp15d3e}gM~3MdzzhHgAQmW{6XI{o;Ap48@bL=^!;%%J81CP_!VnZ_ z&#-yRL59ztJ}?wym@)|Q|6`C8`OYwR$?*!Ho&+HN0T4jUK-+CpN9s|Rh|8RMGZ3%|6mxQx`%30D%m6{e?+IT7c~h!_5aj8J@mkzzL+q z7#P$Q*ciSrs4)Eg{ow~th!r4!Kn5JV^;R{?O#UIm(+`gsoHGC5G+^Ic76x@5Nrqbw zRTzX1XW6b91|o(851p?^RI22KeEyZ|H*R0mRj`_9{gK#@-X0mOXv{PRb$ zGMoxR;(7-En8X+q)%|elzyIJG!;`n73=5X-xqtHX(`i7VYXAWROGuMftlplWuW!ik ze_J&Js5P1!5tF|4iZxcOSDbT)eoQ;qKjc>w$VM1MwGt0Dpq#1%DrP zWo|CE7EKL>2ro|;1{GyR20lJsU^4#9aOduQhHcyTFdRB^_4dWfFBiXh{dE&i%Pk=O z4iG?$Xs+b&_D~Jt(2et^00a;t z7Oye`F*gtk12I1+s-UUhBOwsK1>&zj{1>JeAiw}Fg91Q`wul7)0000LqiJ#!!Mvz!wUw6QUeBtR|yOZ zRx=nF#0%!^3bbKhVB8$w6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b1FZMZmYQy`tcmMzU^#1Mbv!~W&rJ1c*Tv}J1zGLgW zzrXJP|MTeopL_fFEU2wcS-PMgBgJ&z*3AF)pXYvfzHmx^_Vh_bFCTB5Grg$4GwJQ) z>Hj}X-M1yZtI_iR6OaGTLjJ!<-MBc&&qLkGR>{jndBZ}l|IecSKk@nh*tM(C>f{=U z|CbmxP2nm|-(vrNlkxvehCkOB_AX?o z&tcdxm-W{zHm-Y|Ns9lE57js7+i)WL4LsuPOF~?O_(O4WiV|;J%>#M^U_279~FQ8 z)&FX8YxewmK}Nl*%FA!naz-8f{O#^MKRyG~v_zdBK;4W<-tI0FD!BT1fE@M`PhVH| zXY71js(dl88nu8zA3R+gLnJOIConL%siirmskyOfBqW*|nHicIneuDHlbaWJ&u-rx8y=oLb^jO}9SKj2U`Yla1Laax#>NXqjRFApP*C(oW9 zZ*Xu-3@ltX#ezBCGc&T%H8r%la-g9R8kOuf8aMf&L=) zAby6n!wxJxfsFk?->a6mMwFx^mZVxG7o`Fz1|tI_BV7Y?T|<))Ljx;gQ!7(bZ36=< q1B1hr=XjwSv>-Zi^HVa@Dsk)Bq*AtVqM#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;usuL8H%nu7sVNg=mWe^Y)W%%&^CBwbDml;;A zoX7C`_0#FUe*LXmvf|Pkm_C32VuEVcVqsx^>*VN^&(0wYRQQg8j~6KQ?*oGXKQqIp zPk)~n*f=B@oSp15d3e}gM~3MdzzhHgAQmW{6XI{o;Ap48@bL=^!;%%J81CP_!VnZ_ z&#-yRL59ztJ}?wym@)|Q|6`C8`OYwR$?*!Ho&+HN0T4jUK-+CpN9s|Rh|8RMGZ3%|6mxQx`%30D)Za`U{f^0|Ofy0|P4qUi=UkdH-10 z7(nWO|Ni>}Bnl8fAOjBGdaKI7z$3}PzyWj+I|By(@QZOw(WZu4js9A`{L!7i(kF|x(TS|77%|22p~qZ z%*EmDp&G=?%bv~2!J@~?%2Lb&%sk)UfB63V&fRwhuU>z>0c!JeP%;K-00Oy00000NkvXXu0mjf9$1Tz diff --git a/toxygen/smileys/ksk/angry3.png b/toxygen/smileys/ksk/angry3.png index a68d15dc5f46be85e1233a24dbea28f0415c53ad..9b9ebc08be9355b93f5377f4838b77eb016aff74 100644 GIT binary patch delta 938 zcmbQr{+xY+BnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z}O$)6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b1FZMZmYQy`tcmMzU^#1Mbv!~W&rJ1c*Tv}J1zGLgW zzrXJP|MTeopL_fFEU2wcS-PMgBgJ&z*3AF)pXYvfzHmx^_Vh_bFCTB5Grg$4GwJQ) z>Hj}X-M1yZtI_iR6OaGTLjJ!<-MBc&&qLkGR>{jndBZ}l|IecSKk@nh*tM(C>f{=U z|CbmxP2nm|8iO_^;B3cI1 zR&dxvFfTpC|55ShU;VEpw`R}37i83{s=WMGEoaox&)@FO^W!t9H%&{_`2p0;nB?v5 z(vox`su0LwFY)wsWq-!b$EC^_^Qut`DD>IW#W6(Ua&iI#lbc$abDEkPn?^#SsgaqX zsj+!dGW%ys9i6pna&z9kk(HTi;m8zoPH$b_JGpsr_w4rFvEkv_Q}>Ut(ea?-!o-Jy z69a4h@GxC8G&I~;U-*%;)4?h#prg@J)6&vX)zs3I)kB@L+n{6F()0rkRkLPTFc+tV z6%}c5sHmr>dU+Ueq&$D}>}Z2ST%O;yDHhE6b&iRFg`S?Cg@K8V`?hc}H~VBpR=TE! zrn*)}u3R%mfVp{(YpHK;Y;A0=@7C5PCHo62y-xA=`kwXe<<+WR^(ZUh{)ZLW8B$r9 zuU~Y1aQJ(Az51g31^SEFgZLSQS?m(8*WB9*3;@*<*NBpo#FA92CkWngf)@*FRUhTQy=%(P0Z8aTl^HmQ_toM@=d#lYa{>gTe~ HDWM4ffHZWC delta 858 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 KMNUMnLSTZfZGuSv diff --git a/toxygen/smileys/ksk/bluestar.png b/toxygen/smileys/ksk/bluestar.png index 3f848056a6969b623b35928e68bb18f579ba6f9e..21f37cafc5a0000ec517ff768c23db60a658ff91 100644 GIT binary patch literal 914 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl45bDP46hOx7_4S6Fo+k-*%fHRz`z(5;1l8sRKTbk4@KJ1 zP_C*Ul%W-Yq5!B;E0R$sjuE0j9pomEUhNo0B@YHZIS^MffdU|-22u|MqB@K!K8$h>U=EaMnz_j}t>^QP|67hd zy!YzARnoMT$Nx(>mZ$|!(NEZ7nSaf>_M1!Nf48RpZuQ@63a=R_Z_x^!BI#6;*tn&5 z-ha)U|B{LSB@+Ipwrt7iJ#Jg^pC#}=gWrEv!({jJYqBZ-IZd6FHD`^ibEC^v&BgragBMdaz~d=BJO^ ziYh98{&*`ZBlCuprLxkY#$7#KJlx#8oZnwRUtTeh%`V>le}jVn$A|CSLV~FR!u))k zyv&UkCM0lrYO=At{LtaCq3c9Pfn?-_r$vDm4L4rgXjmBdiK{Ci{Fu6mikhmL$`NhN z=C`4qexAPG{@%WxVXZsDbd`;@t<|lyjl*M^br#H=9=~Ae+TaQEmvfh{nYXWTq2Tt7 zohxUGxEz>s`;d@FWK_XyV_-UMvex{tXOZN7pwCoGTq83VtQ&&YGO)d;mK5BMqyy^boFyt=akR{09COl1^@s6 delta 749 zcmV?|MQRk|3NgPs$UIIm<5|! zfB?c}!1*o59{#`g>i>Tb&8QV|A1Ew@O)WqGu|PE_0x4rCHnTJ}H~7jZ$8h>CNK%f$ z(tpBE;Wz{14hAOH{S1tZr-7npf#Cu&lmQ@s7(trFMMUrM2`Mme@+vTJ@hdTK@hLDc zvT#BIi4jQr{m$_1(*uU@pY8$i1BUl+A27Uld`t8H?M=4;0tn=SG*1Ty1|eB*hHd8< z7+!s1VEF$L-JzU7i!~VpxHTEPtQimUVLijg`^xBZ`?oh+fy^5q%^)#=076b2a*|@g43-)Y zjgtHfV6S}p$-waZBgCKsK>1JH5`SF(zdZj5(*zJe$VpjIRZfJ#Oqqc}Mf5wvhHaY} z0@BODG>8upSC$cGU|{8v1*&C*X#xl!WEb!Y@o_QCTe*>8=87E*pPyd6{Qv!(ZCy7n zX-!|g%{M*5gF!@qn}LCu73ekwHkc-W0AfT*!kQ6941d1r{(pM%1jDyiw^o2c_kj2_ zgQ$)YBfml}!>=#D|35mg5GZyTh(Cbz00a;tT8aTVjTh>*FF^bQNe&n?48X`__y;zW ff#Eku5Fo$+b+z9|vCMGn00000NkvXXu0mjf%=}K0 diff --git a/toxygen/smileys/ksk/calm.png b/toxygen/smileys/ksk/calm.png index cc04aa288b983a0b738b087c990ec1f5cdcf6a1a..da1999002060cbe91cc13a8433252f9e8ed6422a 100644 GIT binary patch delta 931 zcmey%_JDnYBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z}O$)6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b%tPMVUYQy`tcmMzU^#1Mbv!~WI)&^yznQd5I{rlUw z|G#hl|MT$wpGW`y-24Cg>hEtSHmt75NHN`4zcus!=ehsCuKxdR$N%pK{{J}i|NHL$ zU)TTtvgrS(sr$C1cQso6f8z1~S;+qvssCS>{eRQ=|4q~X*H!;tX8(T{_5X>_|HrOf zjaDbuNc_LVP#n+j{|aAmBG3QpGQ|m;|1UAUKP>qFG(&L=!|zkl|Bo^LKgRI>jQ#&J z>?e1o{J){_|8hM`S4+VESB3xYsre9s=A`0>@I|3CKs|Noy)CAbzC6ow^1e!&b*tDgu>m?olS zFl_~gO$77OL;N2VfBx0~YI1A#{Ch!0y{gK~Z`E=}9sT_6?mRy}gL>1nM4cZ%-Hb`z z?k+7!7orM*9QG1VUsv{L?0j6Rd@-*YwSYozJY5_^BrYc>Ffh5Pr8%dmxv^;^B$^tT z8JZezPe^9}Y^k$$O>WNHH?lHw=U6&2#ptcednY$9?w;Mgx;r*JJbV5zHaZ?uT$uP! zaAM%anm;^Dh8qhveyr#0^gQWcJb8BXXp=)sTwdQ43+DVf$Hc%wPfyRnz(mJ=TR51TeKI2}T~k9-T`MD3 zu9+ji+`K2X)HgS_Ha6FH>(VAA`wJ_*PVx5op7rhJ)mrr^E8+f!74_K}Qdya=UvzzN z_=`it0u_!+|I+wF1ZTiOWpxoU}PL`h0wNvc(HQ7VvPFfuSQ(ls#GH8cq^ zG_W!@wK6r;HZZU#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mEz{1MQz`)1`mHxxP@c%o* zpMT$<8MbcSz_4TIUWSjKei$xab@>8FFF*h>g9u|osm8ECQ&SITZ3ZUBZww6oz5?+V zpvI3341Zq(&3Mkh^#3`7hB5~OHzyOrh09L_FJF1N1!Ouv05Jn?w^5O2n-=Z)oq^%^ zBOrbT6n+7re?0`^I}8jzuQM?GxB|pi8GjVT-!R;L!l=W@%y#we{rA@a0tn>%r6<=& z{QrN6;eT;F1DO7Qh3|iHBG3Q-*Jb_}CvXDAm{8cfMz4*^}k4-h~sK*C;4{3p;B z28Ny-28NO@kf%N|^yCAnFJBmXvSDHj5HX-Ska|uoR(&8(2q1t!28i;oa{-{KR_Y? z0R%GO;H|f+3=BMy3=AAB3?);5BJ7wLEDn?hsROBh_x=Ycf&B&uAdmsm51t053!y*; z25tq0o@#c6k_l*p{GHDfq@Gs#~a9S_#6wvf~9-zpFI6^8qk1i00G1T4W7v> zR&P)E{MDJk!zGb{>E8o}o^D`T`2P!%BAFx@{xK;q?AUXKVe7WN40rFoTYnGKa~Tv* z009Kg3;sUp%G_LREt(n%5ni4y3@XZs419dN44*!IX1H_rKEt-{dl(KKxqAEJ<(G?J zz5cohsO1(Ae+LL4MzqYu;q9Rs#LLT`&B?){$I8mg1I#?%-+%c2{LbBX2d`d#y#Z?T zb07xA0s}w*p&J0pAKXAJ3@b`$(8vR&0AR*ocnie9jKc62rWhc=0H?bdfw3KhGXMYp M07*qoM6N<$g17;J?*IS* diff --git a/toxygen/smileys/ksk/cool.png b/toxygen/smileys/ksk/cool.png index b223eb3d9c703ffa4e9c6ac88cdfdae78badf886..891ed338611efde34b1186941ab0ae3699c00033 100644 GIT binary patch delta 949 zcmbQl{)v5pBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z&Ja=C&U$~AT7xl870LT#zg1=MIys=fn;=q9z>0svoeGc9;^*y zB*X&E1nEslFak2-qxFGgy|0(LhMEY_2samyY9K>dQ4q-R_fd}u({*)H5)jSQ~uy)Q0zO@BaVy>HXW=XHTtbtPRRaGuyDb`uDeU z|9{{9|L5WVKac+Zx%dC~)!*MvY*<~9kz%^9erx9c&vXBOUH$*tj{n~e{Qq(2|M%Vh zzpnrPWzqjnQ}=C2?`pLC|HMNtF|%=cW^`$Sbwr3>QlWK3P;^O5`sfGj={MRlb;4jaopVPo6H0ArhC96BwA>)Y6>O z)ZExK5)w^~%nVJ9w!W9-ck_ z7#kfADlSZXC|Ex+@M6s$9wx($g&#RPJx^+WEZk_=|D|7u_!!uP`&5K5KukU>iRud{j$ZBT7;dOH!?p zi&B9UgOP!ek*fx*+&&t;ucLK6U827uB4 delta 855 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 Xkv|~~UfO~y00000NkvXXu0mjfMAnul diff --git a/toxygen/smileys/ksk/cry.png b/toxygen/smileys/ksk/cry.png index bf422fb7b9a08442507863d56849f818d6d9aa14..fea2481fe3f161efa03d64ae8a78388d084b52f6 100644 GIT binary patch delta 976 zcmdnP-pesTl7pFnfk8u;KX{^|aJ@pZN02WALzNl>LqiJ#!!Mvz!wUw6QUeBtR|yOZ zRx=nF#0%!^3bbKhVB8$w6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b%tPMVUYQy`tcmMzU^#1Mbv!~WI)&^yznQd5I{rlUw z&+jk(|MT$wpGW`y-243g^zUycHmt75NHN`4zcus!=ebYs%xb8PdHrPL{~w1wz1rAN z9r55=-~Ufj_iah&KG+ziIscrs@Cds(Ytn8;W)B9dr8s*tM(C z>f{=U562i9G8wLKWGGHxD^BG3e_f_HfwMTC@!ocZ*M}J19~S(7n&I_shT<58->0Pi zA7lJ~jG?%KvT~EMimsM`|E~&7D(X$j zEC1hQ{C|_-|1I|a_f!t;uK)j`_5TZ(|Bt1LQ(22sS+;Jd{QG6;|4*&|Kji*@pWa;O zd-nLmkFPfU|FQr7|Nr{`#MpqrWmppA7tG+a`ianlX(Cz%(^k}T*hDZdJ;eV}@#kOt zuO_!<&%YOB)T^qz{8lYz)X~r1?#}b$GcZj{)cFC_&6wov?lPf*tB(iBVK4FYb!C6X z&c~(77xSu73n=u})5S4F;&O5V1CyItnsb_(8=FQ#qN$OYp{eoqgk<*5mO5+Kv!?WifW256i#f6Cv1t$hxtog&kWVo^LBWI`Q zNzIjonTAbHJZD}?&XkOdRG2bzqMptng;{eab2C=%w7i-0i08~tR>@XLg@BNd3HpjB z6#`;{qC(i1o{Fj-4SK}0XKLtG)2*eYrCY!9hEBbt#KU9M>w8vrZGG-q-Lt;En~I!x zcs@;gD?2y#ZtPsyx6`<~{x~ex()|AA+t=@3zpYr&wYstP@Zxm~GgmBKyIA`W^H=@- z^E&o5?(2~Auw_^t>ZI;1Y6!|2swJ)wB`Jv|saDBFsX&Us$iT=**T7uY&?Lmrz{=Rv x%G6Zbz`)AD;Be(RUIqqzBptc=DVb@NxOHq&Dcd;FP@Ri`!PC{xWt~$(695@_lvMx# delta 897 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 diff --git a/toxygen/smileys/ksk/evil.png b/toxygen/smileys/ksk/evil.png index a0483e909ef8068ff82236a148180aacbff3c442..140a259e99c2e39a66ff622fa1e75301b4f0fd47 100644 GIT binary patch delta 931 zcmeyt_JDnYBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z}OSu6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b1FZMZmYQy`tcmMzU^#1Mbv!~W&rJ1c*Tv}J1zGLgW zzrXJP|MTeopL_fFEU2wcS-PMgBgJ&z*3AF)pXYvfzHmx^_Vh_bFCTB5Grg$4GwJQ) z>Hj}X-M1yZtI_iR6OaGTLjJ!<-MBc&&qLkGR>{jndBZ}l|IecSKk@nh*tM(C>f{=U z|CbmxP2nm|8iO_^;B3cI1R&dxv zFfTpC|55ShU;VEpw`R}37i83{s=WMGEoaox&)@FO^W!rxO-roT`2p0;nB?v5()?%3 z`Aa|!dx@v7EBiBcJ}ygcRplbiGQjjYUE3rD7yb9(FY-pS32yJxrWjtvjbp1OaGjgAKu7bZRwoETX1 zhllB+p`qc%!jGKwoeow}0UeE&nwFN9s-~8vtRCu|-3A@YmZl$YsG2pyg1I;?EX#;x zNm{CxhY?50^C!m~9P<3OO|f9kkE?S`3@j`x3`}&ao4197x!K1vGqTb()wMD*vvbWH z0p{jCuA!yAxv{yvrBhp*lbM_CE?Kdh+F&XCH=eEp*9gTvp` z>(v+KFVJ7a9>mY^<+mL_xA@u%K+mg|xJHzuB$lLFB^RXvDF!10BO_e{b6rD|5JLki xV^b?rQ*8qSD+7bWmFIX-H00)|WTsW(*04#1schp!Lv=0&22WQ%mvv4FO#q6%Z~g!P delta 829 zcmV-D1H$~^2lxh%83+ad001BJ|6!3K6O*z7B!2@6Nkl#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;usuL8H%nu7sVNg=mWe^Y)W%%&^CBwbDml;;A zoX7C`_0#FUe*LXmvf|Pkm_C32VuEVcVqsx^>*VN^&(0wYRQQg8j~6KQ?*oGXKQqIp zPk)~n*f=B@oSp15d3e}gM~3MdzzhHgAQmW{6XI{o;Ap48@bL=^!;%%J81CP_!VnZ_ z&#-yRL59ztJ}?wym@)|Q|6`C8`OYwR$?*!Ho&+HN0T4jUK-+CpN9s|Rh|8RMGZ3%|6mxQx`%30D)Za`U{f^0|OfyL&-ECj}yy9K7{bm4Sgrl7WF4$Yp21jY0B2bs+Wc-v0n4u-^ay1TtXy!PCHW zAr#2aQ?JBOGJzSWm=R4gBUl_H4}VezQh)o-+k-%vPXGY~PKilzhVv6*{Ss_V*?>{C zpP{7g1TZ*$Lc;{8gpr-0rwJG+9F7c!Pyb?Q>0EMu*Pgp2K*_xT0R&4(lUJR?w*G)hz zw}ALNKmakKWiAeH57i)EUiNHG4i-IDR%RYx=K22q!}sTR?!G&C_4?}#P@A8Fk}*gF zKmef|00O{JWZ(v3VIby*MkXRC6?_EZw?O0svoeGc9;^*y zB*X&E1nEslFak2-qxFGgy|0(LhMEY_2samyY9K>dQ4q-R_fd}u({*)H5)HXW=XHTumN;6xrxU{Z1eaF^$ ze}CQo|L4*FKlk?USx{S@vUEW~MvCdat(pJpKhOQ}eBqS-?CFz=UOwJ9XL?b8XVTlp z)Bk^(x^GK*SEJ?sCm#Qwh5Ub!x^Z!kpNG1Wt&*3E@`i<8|DQ$uf8z81v1?bO)yXvy z|1U9Yn!;6_$a8#!SaAa9rpb)&4-5W3&9JGDp*V)&_bKWB#~3FSGAwOiIKPnL?GA>& zM;P9pvHyRjo}HZy+fGGi3*bV z&%m&CL*?D86ZW{MUFKo?!NAa5=X>_}#E-8w{r|E5|Ns9hgjufw%`z+r@(X5gTKz<5 z!ZZ;rgJ~-`Y$BMK9^(I~`17y+SCd<_=idu5>Qz-Fdh=jGd25l`rO1qZUx;tEY=&h{WaO1O_HIwKV56H8(bm zghW##Gec8j^Q2_<&z3qmYuDuFynQ1pGuOhADdwEsy1aLC^WyH=?Ym>c!?UOEA7i8A zLB)lM4+SR%*4O;uVY+B&Xt=TPBWI_BRa8Jnqot;$rKPH=r75e2I%l^*$FimADQT(C zQ_@q@mMrh=G&Xqt?5VheLtWgu7L{g)#6ZK41v^#*c+F|tzGTaq1$!24TC``unk_C- zEsc)LR_$80Xxp}JiA*xTGO}n=3+O#y$ z&_dG<`4v}8O?bC%DJ^lWSn%)Zb>WNV7sM|zuViP~v&;UUiqKkMK44HSag8WRNi0dV zN-jzTQVd20Mn<{@=DLO^A%+H4#->)LrrHJuRt5%#E6?$wXvob^$xN$6Xb?fyu}P(D T<3vMsE(QiqS3j3^P6#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;usuL8H%nu7sVNg=mWe^Y)W%%&^CBwbDml;;A zoX7C`_0#FUe*LXmvf|Pkm_C32VuEVcVqsx^>*VN^&(0wYRQQg8j~6KQ?*oGXKQqIp zPk)~n*f=B@oSp15d3e}gM~3MdzzhHgAQmW{6XI{o;Ap48@bL=^!;%%J81CP_!VnZ_ z&#-yRL59ztJ}?wym@)|Q|6`C8`OYwR$?*!Ho&+HN0T4jUK-+CpN9s|Rh|8RMGZ3%|6mxQx`%Y;PD!r~YL4`HKN3U}pmw5Y5K$g+Ybk_wNrsfI_SQ0R-~G!CP-t85npZ8H_X; z8SnwHJWw4-{k!)+Knd(OKmdUZn16ooG%#HV1u}TM2!Rz^0PzAK{s5&dpay{Df$Bi& zZ{K-)5GeEsAb^<9o`3#GR)$kSNL-;NP?lD zAfDm!U0#L-OZVJAdHU%zpzt+-0AdE2Y^X2!_{Ob!>4G9E3}GQ24F7K3V}JPe>N5lX zufGg8c-R=2?UWgUx(XSt-~G<8e&a5N(`TN}fB)gfHlWs*009K|w7-wKGB+1ni>8J` zgqNoagNm{u10NqRFd2VlxO4YD!?x{v7!Dn|di&z#my2J${<;aM(Oj#Q*^Y0H3@M=pKB%<^TWy07*qoM6N<$f=S<$G5`Po diff --git a/toxygen/smileys/ksk/flower.png b/toxygen/smileys/ksk/flower.png index ca5396106f0f3cc38ddcf57f778c702f9968ee5c..5463fdab72e105dfa29c9a3becd7497f816441df 100644 GIT binary patch literal 1055 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl45bDP46hOx7_4S6Fo+k-*%fHRz`%Gqz$e5NsGt@DRWdN- zg9Xcg(nzxL3=Ab;t`!4=B}}S@fgu~l&Vex!z>FdW1|u*-nt>safx(}F0Vu8wbPkvc zmI2ugG%X7(Wx~Lq$G`w|I|Kj)C1LD(X6BuRg$F7sCaI}4b94W>f4_-?W1_P1mf+ym zix;Q8AAgyC{ADm6Ow>`1qR&3gK>2-#3(+p=R*T~z`-r2}nc@?6a`xw%*S_`F%OW}>R^qxIaE|MNm;p> zi|fzv<4x@BYyABF|NqYvuzDLXxGYM7{DK*nc}k_VCQQ|7>0uYG%jxQ1;|&x1`DX=p zaxv?#-3EGzQhXEEul1X-UPDAKYUQ4kKrSJkJbo4~tz#?=KrVl2bPO2%w6mq7 zt*f>5@(iZ8Pd|U~;>nvwulB|_u%8ew`TpU{r*9v>e*DbOQ6kRNwSkSby``kXIW$B%@CgeOf3K5|4=LP90@@+D)V&6_qC6@C5`ymX1A#En!guI?`HF7MN) zj;1_q@Oq-HwR%-{R`#mbucSppmP$%U&z=<>b^DgJ)$U!hrKUQ(dlFRk;^o`d^9=%B zLwQrUnYaI1ax!?C-)tLC=V0zfVY%G0cVAZh5@U-pI4>b3Is1g>EU7|cg@&0sC0iI7 z&gPk%Ykqi(4d`dp64!{5l*E!$tK_0oAjM#0U}U6gV6JOu5@KjzWo&9?YN~BuU}a!% zxbhq?iiX_$l+3hB+!{8ilx+lRkObKfoS#-wo>-L1;Fyx1l&avFo0y&&l$w}QS$HxP Rm}?mrJYD@<);T3K0RV5}Satva delta 876 zcmV-y1C#up2&V^-83+ad001BJ|6!3K6O-`+B!2@rNklGoOJ$2P!U&ZU8_4F`@A@9ZO3YMAg); zvT<|gJLlywaB^^DDJd&o28sVEtVMo>g5I{^&n?-@R4#<4*<@Rlc$l6*4F>@LnBB%%m9D@Vgz#7m|0nWT8D)(e0}_w;nsx< z3>FdI44WREXL!i;l|lX=H-n#|3B&Ov8yVD1Oc=O<1|MC%oMG4R-+ady7(N3806_r0 z{{zAR0R27w{;Txk<7+w$3;_QX2LP%M-+usFbp!xsa|r+uCIkSW#N7Zf0S*8!00;oZ z#>N1$_4QZB007_s008&`2pqEZ3=GxZfBZ0d{^0}jSzs`wG%_$4m^@@)VtmCQCH;m$ zNtK6T>Gp37@6SJAc>nqHUqJ?j|EC!k)&sTv1_&T#pcj&Lot)bI?d+K4-@IYiB7gah zAuYoWXxCc?mOuX)SpEq!hzQy-EZy*!!RHGXgGW#h6SI(zj`;oi?@lo=oB;?RkQdIY z+`lh&;o?PuSB#9zKZMyCWXwDm#AHku{;_B<{AaOaxN$>*Vc)tF3|}69VK{g4(EsF9d+;Nxdr>B>wO* zc*Mpsh)78>Ut(Zb2GsTnh<^bD5GWRy8JU>=IA&*qW8ucZgA7_xJ`C$`9cOsM@|}U< z3nPOphcv_QV^?%+#s>SNZt-_Xh@C0t3TZZ3YHEpf*8}UVs1sd!dLqiJ#!!Mvz!wUw6QUeBtR|yOZ zRx=nF#0%!^3bbKhVB8kq6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b%tPMVUYQy`tcmMzU^#1Mbv!~WI)&^yznQd5I{rlUw z|G#hl|MT$wpGW`y-24Cg>hEtSHmt75NHN`4zcus!=ehsCuKxdR$N%pK{{J}i|NHL$ zU)TTtvgrS(sr$C1cQso6f8z1~S;+qvssCS>{eRQ=|4q~X*H!;tX8(T{_5X>_|HrOf zjaDbuNc_LVP#n)toWNF`$n*cYOmPBdaXjPu!-D@$GZe=#{5~c9{}|){V+`-l*#AGn z{$LZs-&5=-ch;x;zoGE|GRym;3=3NsTJxCRo|Nlq3Hbl2@c%uP|F_ux-(>uMli|-b zhP?|J>T?)&%w_#`OZm|5`u`tV|G#kg|5)nNHSUfI);V45uP;h$-B9`W%hLa!TK|8@ zy>}t1xz6|O@rfT_ZTkOX|NsC0FE9!}0EU=hNswPKgVX9KLKCKmXw@4`Tft!y!MyYk z|3}52fAznb+?qZAUXW3*s`Bz%wVY8$KYzPB&yUZ*G%Zo*2T(U-lDE6dM1J0Vvw$4- z5>H=O_Gj#TT&n!qZ#V7%3cd4maSV~ToSeYGxmnfBv+cqqi>ao!q>*dv^Qk?%43~?D@yo=y*_ZVd6uDIM-SJrQI4D;(~ zbiB7|@#fXLm+xM^`EZcqMEMsXs!#9j`^WjWcCXf|M_CE?Kdi{kkjlz@{i5rG!{5{E z)feS2&|kzJ#Lw_b*fx*+&&t;ucLK6VB)}uH8 delta 862 zcmV-k1EKth2$=_v83+ad001BJ|6!3K6O-EmB!2@dNkl#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mEz{1MQz`)1`mHxxP@c%o* zpMT$<8MbcSz_4TIUWSjKei$xab@>8FFF*h>g9u|osm8ECQ&SITZ3ZUBZww6oz5?+V zpvI3341Zq(&3Mkh^#3`7hB5~OHzyOrh09L_FJF1N1!Ouv05Jn?w^5O2n-=Z)oq^%^ zBOrbT6n+7re?0`^I}8jzuQM?GxB|pi8GjVT-!R;L!l=W@%y#we{rA@a0*D0^VD{Sg z85rKZU?}N@d5jH=d&&eDN;=;HUGf7c_#Z6QQ_R3%uPwoF_L6WGkh>iqfItS=tBL;v z+5%+#1uDn~DfK%Njl0D%k;2Z4G& z0R#~9+4IjI$;xml2#M<%$g7wzFfjiE;vWp3fBa*ZxCj`O|5zE~!bBPL49ysr`GXh^ zpJQQIuyoJ;lc%3f0~&A*Ab?n)Av1Z!>g@@izdAE`xFj+#{d>T`%lm>MEPvuHgQ9{k z0|S!;!#^ekh8=sZFl^nnm*MW+ck6+AE(7rwfB=H$1%DrPWo|CE7EKL>2ro|;1{GyR z20lJshEJb9Gu*j*pJChfJq(AAT)lnq^2^1qUVq&L)N%`mzXJpiBU?>~Hhen;o-yMtG+zuo|~`8g1SVu1l5fY1$q45bDP46hOx7_4S6Fo+k-*%fHRz`z(8;1l8sRKOF@2u0D1 z5H6P=l))VVWx&;NM>6unF+vn@1;J!v7&$!{S>%Aa8Gu??RiO+vT@V8Zm?c3Z&=595 zkX`K7P>me6j6f-%UJeIFphhlVMrJXPN+4j#hYSZgFy(VyIpsU9}}+ z*Z-br|NZ;^JN5l{tl5&?cf4%z|M-so25tX!1(Vs@uEk9J?<$eSHTQqw?Ei6d{>RG~ z2(J0xviX1i?*B983xq71wU7PZeD43=yZ?VNCLNcPTRC&5aiQS$ zjh!oJintt@bNi5xM`TpNY-3FVdQ&MBb@0JD}O^#A|> delta 762 zcmV`xU;l$>Ey#)CT;SsC^-FfyD5syX{*;eU{!3;+Sd2+}MiDtS*%L4-kCL4ZM4 zNeD;_GH`H0T+9fBKfnKFc>3T2!{dAJf%JQZhYwycJihfz^V_EXw*UeNq$(}M!H>bg zTZQ2c!(oOW44)XjGkgSMB*4kQAfU;}Ab+9B%Mi)n#c*ireTKe!(@KCc`2YdL42-7R zCoi9s)s`~Q5|sJN@PXk0!*7PKz`*~8g8wo6WDsFcV0dtXk)fq~;_crT|1bUbk>NH# z05JpO_#>+pRg5&7yhpQbAO*< z6VL@O00M{+=1O2lNAn7CE@_<_4z%qr*zb%CObq{ly#GMR1T?lfdnLoW!{0Xky3Ei9 z)N%!gzX1dgJZ>4;84j?su`}>9D1-Uz4BS9>Z~?>Q8_@AzA!2MC41eEz+X`gf0BHt^ z0R#|o;*gURmST`%&=d|B*Sxw_gsIU|NjZo1Q0;TNm)@* zM3q6FL5G3=yEMa+%_|u^%RRs}hz}B%6INni~0tg^v7w`*k2{O#wID=vK zju{N^uYS7x`_BJu9hcg*W^9}4o9dClAj~bo08C8$K(%ZzO#lJJh#cL(fMYEB|CK@a z=gI#k{=Qk07*qoM6N<$f=q5@v;Y7A diff --git a/toxygen/smileys/ksk/grin.png b/toxygen/smileys/ksk/grin.png index ed79b5909b5340d5ba0ae6aa1486cf716347edb9..b35bf24279c9ceb1778cb5e775907967539ac1ce 100644 GIT binary patch delta 905 zcmbQiew=-RBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z}OJr6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b%tPMVUYQy`tcmMzU^#1Mbv!~WI)&^yznQd5I{rlUw z_pi_X|MT$wpGW`y+Hmt75NHN`4zcus!=eaj8Oe)R~zIA!Y{~w2LUYcK= z=YQ*B*Z)sb_iahm?olSFl_~gO$77OL;N2VfBx0~ zYI1A#{Ch!0y{gK~Z`E=}9sT_6?mRy}1JkrbogYBWj7i?^F1fqw*%;OWdF&;gzOL-g z*!j3r`L*9}+yfMP>ZT$m9-dFro{CNly&5`I^yxINu0IaT zwKT6^xpwvX)oUwObggczJ-m3`!ps#**DlsR#Qar%|GbWUjr%&}JZu?iTQck}IaMoV z0R5|4;u=wsl30>zm0Xkxq!^40jEr;*%ykV-LJSS8j7_afO|=aStPBheSDxcV(U6;; dl9^VCTf-)mvW*iB)wviLJYD@<);T3K0RWzacCi2e delta 861 zcmV-j1ET!L2bc$t83+ad001BJ|6!3K6O)+(B!2@cNkl#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mE@a5}&pk@)M^dF!huNZi^ z8GjhIZr#AJW9MFmkDq=RE?;%|0!S}F05O9IV?(LNus~B&4`*$LJ9q9f^iNvCpeV=1 zAT0Qw;nt1w4E+=4FeuCaXRtC6Vc_OuVz_YmiQwfcFSme92M7Q`0KWeNW@0luBp(Kr zTS@l-@$KCJoS?%1IWrFcyuHT&Cm!?wpnsyX068%Q0KB`z05vG+045ym0NvyPECB-s z)!pCk*8+$I6kzt+_Zb-8y^Keh_xC5mp*g$^qRJ`^zkh%D0Tf~d2p}dPaq!k# zRR&&BQHIZN&okfyAbF5FkotG;e}8}y*l&OU0vRy<;ORFE!U~=YZ=WB+YXC?dqzX}ffJIUzU1TEcORq+i^u@|_MPF+ub05EU}9in=V$oz;eQUpvqy&+ z9zDFt@a)A$hV45KFq}T~bpHDfKemC)1qdK`Uhwx(SLWtoYf)8^jW97WW#HrG2gV2^ zFy#L;eER&6;mn!S498B~xP9^R%f+u=f87MsatjpU00G2^=28xC57i)EUiNHG4i-ID zR%RYx=K22q!}sTR?!G&C^+x*Z4L~)Qf%rKPgJOXJAb=RLgcZnJz^ua{48)+s$qEW> nApQsnM45bDP46hOx7_4S6Fo+k-*%fHRz`(dOz$e5NsGt@bssKuW z#q(ecxJWfjBooGPfic*@j4B2OFEFE=fguFUC}v<#2Q$hT7@WY2QU(S$Fat;cWj$eR zAe|2u@qmc{owwNC>CVRG|G&QYaP$1(w#GI=!NYA0A8wrc|K-`8jmx$~1^&OX@Bj5f z|8Jl8fA8%7hnIfcI`jX*h5vU>{l9VK|J4Kkuk76%5%6(h&Hvd=|K~LSU)247#iai$ zC;wmC|9^h_ueOT+MT!3l;(oQ3KgiGeAL#zy-R8fW_5T332e}z*>}>xB`~O#0`mZGW zUrq6UfX{kIhXxjww}plOH8lS7@cifG{I90=uCQ>vfx-Wp%Ks(?|5fDwE6e>i()(Xo zw%FY4Xm;lRj>i8%9{+>f|F_hhEXZqMWxYFN^8W=>{#R!FuSowtcjDs(vs-z2AFo~Z zfA5?B?5(2Rg;XI>p8A@7mlWEA#*V|8DVHxxgSY zD+%%oW(eH%uuGA7+FX|E9@Z!CB)Ww{oWMb>J;J z>?**RbYX_G(3|2XXMiR!CV9KNOq#~>UIEBqFY)wsWq-!b$EC`z{dVIXpwN9!7sn8Z z%gG4|2`NU&X^E+cX30q@3`}Na#oXT7+0xP0)$GN5er#-d?&|5{;pXN1{`&dy@p*D= zarX898xAaZFd?A6zTV!hj;(RS2M!62ei4x*fxfQ3&enz*J1PVN**3NuS(3ux;pr

d3B;~I z{0N9w0`YXHSU3>N0r75-STh5|dYC?d00L=b2I6Up-JSm5*|_}w|F19pf4F)6|KYaA z|80VTK)T`oha2bq|Nrs~D8KAKNL>X3!(5Ua_Hf5(7geKmdVE$+R`sW?(pe1mYw{ zMg|5p4j^V{U&mg? z4DaNWfHnXF@YQ<;hUafEF-V+&mEpajD#OLYM;Hz>Fl+z{yafm#=0m`+wfgt(+kcxM zKU@t0d>I%%03F8$^dS!?(3e~g%*V~ZAT7zj;OoP%_RwL5osSQgXfEZ!kD`sF&=VfKR5j(k$L4Sz@D17q@1H;dsK-U1hrK-j7gqe$BZfX|8^54HF z++<*w0n~CAi2nct5F>IJl`$|x^7Ha8i(4?0K}GrV`G1eB%zsaCFo83f04Oyes|5%koJl61fguTK&;<{Of1N=o z7Mo^(0K#V)NR9`HfpN$HOri{5fEXj00t6TU#d74it*uTW00000NkvXXu0mjf(Z5@% diff --git a/toxygen/smileys/ksk/kiss.png b/toxygen/smileys/ksk/kiss.png index 5ba0beafe9c78e0fda155af8876773c4bc3f2617..4764d69eae3d6b95849d6ee9ebed0255c015cb30 100644 GIT binary patch literal 1206 zcmZ`(drXs86hHlnJc|_>0#%f#APAL@LS=Mp0@4a%c@!8Z5Buglg|<;ABm$d>>=9(C z^MRR|=^TuQggRd01m`HREXay+P!?d+xr}r?6#>sa^`GsJ-Q0VA=lt&Z-B)t%nS{7~ zj`l2j0C0?njuKJ0?$xlRQ`U#gOrpRlJ3<%%(9{>Qrt&Ejd&;6kLVypw0Zx4c@PeXF z{Q;mM0sa&N@XiCc9i((GG&!%j8+O9V_g1y_` zIH@8(%E=aU#OVP{8Yr_!d3u050ojKF#3_C(0t(ao^-mjsdZzpt-4_tsAG+zs2X zH2Sq3=G4r#rPW~bBCo;3-Cw)i{>FHk6Fscjher2r7%k`66OM=P&n=p+B-&Xqx02kz zqPi6ZwH!?vyDzG1|HBCj71*d_gnS#b&02derr-3OGnHyT{=#Yue|` z@u>;&1W{J=(ZLC?*e}_KZx)wT*0Cx&?$;Ufha1!HNG7iv>VtL9-CH&4g;XHbpPE|E{srhMY zxkRnt$%+*e0Egso*klMhFgQ7o#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mEz{1MQz`)1`mHxxP@c%o* zpMT$<8MbcSz_4TIUWSjKei$xab@>8FFF*h>g9u|osm8ECQ&SITZ3ZUBZww6oz5?+V zpvI3341Zq(&3Mkh^#3`7hB5~OHzyOrh09L_FJF1N1!Ouv05Jn?w^5O2n-=Zyoq^%k zBOrdpP*Ss;Vb`t$3~tse3?-E_f$Y5uZhsa)!@gf-P!xZ|aQ88z4kI($)w}oKUk3;v zkn@+GTrKhc|9J*5E{+A#m-+txzb5tn|8<%F|E~)F|9_F`e{mcGT+PW<692=4v=0GY zzz+~WER6sDJE#f$U|{$RGKfnI`1WL031P~M3zrV+C zy#X4)CIYmb8KS8{8f@{mLMUAhp?|^hKy|m?$uqov|MNLBBg1ci0AdpR@oVnED<6Qt z;mp9mAq7;x#=zD33?;R3^*;lPgXQ_17!F?j!0_8Gq8q!1=2uwGYT>=u<8I!a%MPuhn->3mJ^SzUVnT(otuf_ zGC%;aFmN;c6#Vmd%8ETl!$1EtXYjBH0lM@B0|O_}RqQ~+7=dB)OPJx`PjQAFhpsbh zJ#>QM@x%8Unf^0ec=U(i6F>klf|M}GF))=V$!fmk=V??{lMV86bzo3YS7G227G(JJ z=?lZ1n|Bzt?byq3_{5F7H-E3aUiRVb*A3kN7;XSP_6;C_7@^V3$RN(ZnEZu-`;(0q5fB)h8^LzK+9lmqp^+sSwT;l!D@azdNU4rxi1P~)~V1W#B zWME*v$iTo2lobXhFFv3!D=R3?1CtjpwY+`LzyQo741ZyI00ImER})4>k$(zIdjJ3c M07*qoM6N<$f=;Qp+5i9m diff --git a/toxygen/smileys/ksk/leaf.png b/toxygen/smileys/ksk/leaf.png index c04beddfdf9867f70a357ca7b9721cebf54fa029..75988965e80268273affb5d28d55cefb7710e389 100644 GIT binary patch literal 1054 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl45bDP46hOx7_4S6Fo+k-*%fHRz`%Gez$e5NsNnzq|88l< zK;)DNRK@^gxFi_@Nt*~AAjvPr4J7TNb#)vRAhO!_@<29F&^6V_I!xO!ULPV3R1ajh zBpZn-fZXXAr>|})17rXJP>NTG6UYGS0wRbwkfH0Ss9_}w6t|Dj(|1uaa8ripHTF`K z(GX$hVg*XcYKa1E5mypq?YgweBg1sv@i|G&ei#0) zn{~9>ztH;l?*&P%zFyg;3x6cc{vVXGz$SmI-OT^~$x|JRCdS3KI{TCuC!Q0^{m+>C zgfrzXckX|t@Z}2L`6jtjgZ=YuHa+Q^|39+eu44F7#hl+vIe!^5-m?ZxR?RxCJnviF zgzcpPg*I!RHBJ8SQu<#Y`xATKf9A{=Jh`t0d;Xg*yI<>>Wfo9nmU>7j>jhulf0q3J ztQG%7+y5HY-n7nN?%|$g6t!3_`?_r9fAPLA&U^lM@BTEYbzP=!u4QPgRmX*p4gbql z|IeNKJG^vhNM=t^!{W^1sj(BcR$TnG`SP!AhhMC$Tb#D@<+Q@7Q6VJ`LB;lyw^n;+ zTL9y+SfjQO7&>+(L4Ls0VK3p&XldtCL7z!o-=FS%(-x9&!474N|KQa)OJ3abZOJ4u2ZXCDOIw%E?`(4zo{1+`Wlw!unO$Ze6=~@#fXLmshW2ESjAb|G%YkvYLP9#Y@*FGVOFOeYHg?cJIqy ze#SgJM-S>58<`oJ8gJiFQnaaQ$DGiZJF^ZRyKwB3*c)p%69$GFGb6*S{Yh>>pQ@I) zMwFx^mZVxG7o`Fz1|tI_BV7Y?T|<))Ljx;gQ!7(5Z36=<1A|W!uS`JEkei>9nO2Eg zL-Uy&P|lGA*$|wcR#Ki=l*-_klAn~S;F+74o*I;zm{M7IG8LF<85lfW{an^LB{Ts5 DBqD|_ delta 854 zcmV-c1F8I;2$2Vn83+ad001BJ|6!3K6O-@*B!2@VNkl0V3@mIcNBG6KdC?632q1I=UcCPFfSdxn3CPZ@rE{;MXY%gpdk z{Q<+X%byvZTz!9CTv714ft#|^-Lubcg7g9e5Hm=#j<)u8My}s!zyJRz`StDJ$#)7T zW!_7kXZS1nn&Fq=3x@ZCR~g>l{m=00F@Fz-gbM!zRc(bxZc(NW?c$r9Ui!+qD>!m6RBM z|NQF-l;8mf06_r0{{!94=lLBh6z$c{+)6brH~_3E8p*O zhcW$PV_;|FVBis9V-WkQ%V5MF$bTTurqA%<(I19q4_`4{-hV&$*Y`h{fZDzR1P}`} z7Nmhx5x+Qlk#VpTgN8oPj||@#KK*&caAV_NhTG4NGl+|eGkpH^h2hqj$GxB5eZK)z z{}rScAb?n)4#@QOcP(;owFW!;!^_VMyLazpuy(U&xboo$!;^>i8IEi{zkl%e&p$72 z9)Ge9WHUHqfdC+Y7-25p>@87>OeEam{{f8G{Zln}OTF#xg;;@90kS#Ma zGsEevH{@PD`0x>Gz#ouafB=H0ZDTK0#h3Tqi@bjHk^Sel-@k#Wj!RBQEc(uwXWQOA z`+U_o!4TwZhBr?>vYy#;>pu@nA3y*hBSxr~Sdfx9I}r1MXcHeb7A;$OR`d)75I~Io g|Nm#81ONmW05wY=GpZcyW&i*H07*qoM6N<$g1A?lW&i*H diff --git a/toxygen/smileys/ksk/lol.png b/toxygen/smileys/ksk/lol.png index 801baabac30113d0b6dcd1736a1a4edd88698c68..9d42addeb934ac0dad54884fd9dd67ab1f9c42ff 100644 GIT binary patch delta 943 zcmdnX{)T;mBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z&InoC&U$~AT7xl870LT#zg1=MIys=fn;=q9z>0svoeGc9;^*y zB*X&E1nEslFak2-qxFGgy|0(LhMEY_2samyY9K>dQ4q-R_fd}u({*)H5)jSQ~uy)Q0zO@BaVy>HXW=XHTtbtPRRaGuyDb`uDeU z?_Zz&|L5WVKac+Zx%d9{iQnH&Y*<~9kz%^9erx9c&vS2Hm{gn}eCzU(|341hyfnW! z&;QoNuK%B=?%R^y)oA(uiN}W<{tYF@o0bGMRX9DrR@P8vyJ@j^Q>ot9+n)a)yLL5N zom?aF{}RLNLk$0~@V!1J@&CHa>mx${FEPD8EcpL4!|zkl|Bo?FDr8vNz;J#c!`mGU ze~&P{KV$#@Og%e08^{|ccc%Qmp4cGus(R1y^= z@t=WV>xRm^S10UoQM=5;_Je_;xz6|O@rfT_ZTkOX|NsC07oYhR4m8WKB*-tA!D;mq zp$XGOv<#-L;IN5cUV4cCqvFrM`d>|M&7OZR$f#FUdHJnc&ibgMpTFIm=f`JYnwF>o zG?IZa$=ltf{r64o(?AY;iKnkC`!jYvE>(W*w;T5Wh2DF*IEF}EPEKH8a#KrlPE&JZ z(@01(H8L|aHQt_(%>LO@XYHEYoVRaeW#-PYbYzOrTbK7vZeH9yyM1+cYD=Ye|VS-H*PHa$l2+6vT)-@!zL%5Gny+oCMf9~QkXSu-o%+x=T4qG zb>_r*YHH^W%$Yqso`LaawM*|ukEppNRPuTcQ>laL}T)K8q^kL(#>GA$9_Z{M0_?O5r{OWcH{PyK73()hbC9V-ADTyViR>?)F zK#IZ0z{p6~z+BhRB*f6b%GlJ()J)sJz{Nkl#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mE@a5}&pk@)M^dF!huNZi^ z8GjhIZr#AJW9MFmkDq=RE?;%|0!S}F05O9IV?(LNus~B&4`*$LJ9q9f^iNvCpeV=1 zAT0Qw;nt1w4E+=4FeuCaXRtC6Vc_OuVz_YmiQwfcFSme92M7Q`0KWeNW@0luBp(Kr zTS@l-@$KCJoS?%1IWrFcyuHT&Cm!?wpnsyX068%Q0KB`z05vG+045ym0NvyPECB-s z)!pCk*8+$I6kzt+_Zb-8ysYna3y~Bu27ew&1|v;I27CZ44^#(I|L*+{Py+i65I`UUrXM^F zOcz3d4Bjq6V1*Vyya0$lKxqr80bqHcI*|I?citWZ3Vi|yAm+2@pFfh7;ZzV3*E6uO z)@FG9@*YF@^)C$ZKv@nTHUPO3h`E9!7zzsF87|-DWmvFu&;669pH2e`Uw;D#AaFu5 z)R%mGq~$Df_vKEM_rkli>*adLm|S;(}h7rS&@N{kC)-or_T&`?%rqEwtWx7 zp(9ssU%dQs@vGNgHvzTW0&7J$KmehK8i%)sY7j3kdp0KriykX0GY>HHe1HGp`|~?@ z-yOVq{q+W*n#(}^9Ed@&zyJ_H*n%D8EnwDR5C&p?Xb}7cVqhv@cnic|f%q>>F+hL; Y09e%yI|g7^4FCWD07*qoM6N<$f>bn;(*OVf diff --git a/toxygen/smileys/ksk/none.png b/toxygen/smileys/ksk/none.png index 99487f56b3cde6187a0a3ec0fe4c33b4e6e882d9..03d421f1c19d62edeb5f4d3813438cafbc5c38a4 100644 GIT binary patch delta 928 zcmeywcAI^IBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z}OSu6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b%tPMVUYQy`tcmMzU^#1Mbv!~WI)&^yznQd5I{rlUw z|G#hl|MT$wpGW`y-24Cg>hEtSHmt75NHN`4zcus!=ehsCuKxdR$N%pK{{J}i|NHL$ zU)TTtvgrS(sr$C1cQso6f8z1~S;+qvshgGr6=zxff74W)Wxi>#_y1>6|DX8$f9%@T zXmxUp#Q#eSo2GCTC-VHiE>oPqxoI-v`@@3&Pcv-lVz1mv)nc>21sKV#?PQsvivyKxUt=#{67V~E7%AV&?lv$uF=fiMso^1EQ>IUycH$JL zp}~phu+ZyQu3fzr5+13g@rilvswFY^FWk6tBVhd^t#xeQc>8y?En7IPep}x<4*o0q z|5PdcV*903rSO%(Zi`)`T9i{R(BG;ht`Q|Ei6yC4$wjF^iowXh$Vk_~T-VSf#L&RX y*wo6@OxwV~%D~{$#48g}H00)|WTsW(*3f(=M+c~3qKOI@1B0ilpUXO@geCwu+k+JV delta 823 zcmV-71IYZ_2l57x83+ad001BJ|6!3K6O*k2B!2@0Nkl#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mEz{1MQz`)1`mHxxP@c%o* zpMT$<8MbcSz_4TIUWSjKei$xab@>8FFF*h>g9u|osm8ECQ&SITZ3ZUBZww6oz5?+V zpvI3341Zq(&3Mkh^#3`7hB5~OHzyOrh09L_FJF1N1!Ouv05Jn?w^5O2n-=Z)oq^%^ zBOrdpuzBkdhS_sBGPqf@FqBlzWcc~z27iOP^b-b#A6FR^#osX8eZr{2$jo;2?)~@I z0Ro5x6kzt+_Zb-8y+DM_2QCJN?@SB~uYm@Bdkf_MU|@LjA1-aLEx~a1l5iG~xg8*Y zKnB>WiT`9^_=yy>zkuHO4Wz$(0S5t?jU*3J&&kEA4`d1f1Q5soQ66@njlVwtnST&l zd@Udtq_HOps_Qo*+(GJDn3;Hh96o>m0=kpo^%o`;1_m}@sIxK<#z1u-^}m1r{Q(jI z2q2IF2XDPqWnkcuWMJR`I*6SCHwMWA)q&K%d;bHJzaKoeMGfd=?89DiJMpW*hMw+Dg3p8x^~ESVH%IRDV@`v3nwwEqA9 z!sY+}$5Q|QKjZ;oP6!R+gT#UIAa!mo%J+cUf`FJGAb?n)7EE5TdV9j>ug(k}E{P0G z{~iDn5-?i-|AM4QCJBarObQG;_FQ4ux@|AR-MjDB1NB@6#S=gP!SjN@kAJ!{Hy2xr zriMaFVbzit9*xdp`E0Ro5- z&8r;V9;!jSyzJSW94vaQtjs*X%=7*Ihwsnt+h;$fpf*1TVo)qF00a;t1113F z4{jh92Kf;hd7xAP%oq&7WFW}^%qR?hVTu6)3;=JP64(d;GiU$+002ovPDHLkV1nDL Bdo=(6 diff --git a/toxygen/smileys/ksk/none2.png b/toxygen/smileys/ksk/none2.png index 352e102153d3a91a04aef89a1367820e1831e7b9..0fc9cf1788ecf47784a465b24178835fe3a183da 100644 GIT binary patch delta 947 zcmeyx_JMtZBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z&Ja=C&U$~AT7xl870LT#zg1=MIys=fn;=q9z>0svoeGc9;^*y zB*X&E1nEslFak2-qxFGgy|0(LhMEY_2samyY9K>dQ4q-R_fd}u({*)H5)jSQ~uy)Q0zO@BaVy>HXW=XHTtbtPRRaGuyDb`uDeU z|9{{9|L5WVKac+Zx%dC~)!*MvY*<~9kz%^9erx9c&vXBOUH$*tj{n~e{Qq(2|M%Vh zzpnrPWzqjnQ}=C2?`pLC|HR|}vylHUQa3FLD$cU{|E8%p%Y4&f@Bh!D{y*{g|Jb#w z(dy(HiT{@vHcjCwPUQK2U8XpJbJJwT_lE`lpJv$9$50%@@cWeX|6`2*k1@PIWB>mQ z`^lXt|8FS#zg*8!9M90z67c_3;s1Lo#YxQnZ!-SB$xxicaATB;{{Ptj|Nnp647qv0U@Qz-Sj#xc6aH>P7{j)a@b2eeO=j~vGZ}M@@v1{xCbco!PCVtMB;LC0t1tq zTAFj3nj4!&LZYdWnW3ri_Jm~i&z3rC*W~8BeIqL~caEhaQ;goaymxZ*;_lh)tGi>v z!?WifW256i#f6Cv1?wjUUaa}U!(_N|fGH1 z9m~>F4mr5(nqk3Q99G7~lalV~VZ`y|$uq{%%OdIRs{N9wZt`|BqgyV z)hf9t6-Y4{85kMq8kp-EnuHh{SQ(pInVM-E7+4t?e42P=0*Z#*{FKbJN@NW}s5+X@ V#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mEz{1MQz`)1`mHxxP@c%o* zpMT$<8MbcSz_4TIUWSjKei$xab@>8FFF*h>g9u|osm8ECQ&SITZ3ZUBZww6oz5?+V zpvI3341Zq(&3Mkh^#3`7hB5~OHzyOrh09L_FJF1N1!Ouv05Jn?w^5O2n-=Z)oq^%^ zBOrdpuzBkdhS_sBGPqf@FqBlzWcc~z27iOP^b-b#A6FR^#osX8eZr{2$jo;2?)~@I z0Ro5x6kzt+_Zb-8y+DM_2QCJN?@SB~uYm@Bdkf_MU|@LjA1-aLEx~a1l5iG~xg8*Y zKnB>WiT`9^_=yy>zkuHO4Wz$(0S5t?jU*3J&&kEA4`d1f1Q5soQ66@njlVwtnST&l zd@Udtq_HOps_Qo*+(GJDn3;Hh96o>m0=kpo^%o`;1_m}@sIxK<#z1u-^}m1r{Q(jI z2q2IF2XDPqWnkcuWMJR`I*6SC1(!^LU@#vf4^#(I|L*+{Py+i65I`UUrXM^FOcz3d z3=G@~3?&nQG0V;f(L4d@g(?tcgnx(wTln9dl0Di6F>lgeU%hvIRDV@`v3nw zwEqA9!sUN)8qfdYRM!9hA94aQ&;S3ArNH7Kd5}6c7v+0EZ9zcH4-h~s(2$wDV)gcf z&tIJxJX{hPdU_mz2?-dj|9?SJB$EWgKPCl+9eb`YY~8k(;qKjc>w$VMgMZ=)Ab{X` z!QV$+nVXBPMN>l|!pqZzK}A`Sfsc=u;nSzj40rC{XV|uV55u7&S8rdu{BrTD*IzdQ zwcG;Y?*IYBh?coHyggKdczM~gIXPJLSXr5QfSKp}`w!or-?{tl;MMD|H$ZKE4#c2X zU;qdpbOT`dgBys2L4Jfr9w;ak05b*yFj+DHGYZ3Bm|}nc0{|h55RXP*<~INU002ov JPDHLkV1g0>erx~$ diff --git a/toxygen/smileys/ksk/notes.png b/toxygen/smileys/ksk/notes.png index 7389846b05d859936b7be29363ec5183e4de8c84..6c07260034fb648838bf111cb533b595718f99d8 100644 GIT binary patch literal 1027 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 delta 987 zcmV<110?+A36%(t83+ad001BJ|6!3K6O*?DB!2^^Nkl#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mE@a5}&pk@)M^dF!huNZi^ z8GjhIZr#AJW9MFmkDq=RE?;%|0!S}F05O9IV?(LNus~B&4`*$LJ9q9f^iNvCpeV=1 zAT0Qw;nt1w4E+=4FeuCaXRtC6Vc_OuVz_YmiQwfcFSme92M7Q`0KWeNXkaouBOeEr zTS)c*@$K9IoS?!0IWiCcyuHQ%Cm!_xpnsyX068!P0KB}!05vA(045vl0NvyPECT`t z*4*Fk*8+&;KQO@THSRGmynM;P59DNfFffRS-eIs*;9?MAxXqC5`;9?__XC5a%s&RM zPnQ@NJ~A-atBEt5yC|F`z`(E_Ab?mH{{6F8Wz-;L|$>hTnf62K<5sFB{Z=FF^br zh=Gn`_{+@jg-ev-&-dTII2jo@0e=FBiTC&4^S54$0yXh6F#KdU0*9B&Vo>-v9-|`F$?79qx-*Yi6KXc>x;~Q^gvNJK90|+1%V1Rv4 z`2A<ex2df^RHW( z{xh8B`pNJSAOHYC0KWeNfPe4+{x$yc7l4t+#30VVGXFON{~;Z@z~4e# z$!uI~TC8j=JimVb{r=(o_ZN@uzCHTv_S?-|j0_h9|1vx|3yE9?fJ6X-=f?k#U}K!j zz`(j37*y977zBYTfKkoB3iQMuVAA*m#BUUUk$M1}oFD)ozyPW)K;glkaZLaK002ov JPDHLkV1ngh*X#fQ diff --git a/toxygen/smileys/ksk/pawn.png b/toxygen/smileys/ksk/pawn.png index 566d43f75687e16e743a69155e17bd3a173165e0..cce0cad7723e610e05770d2c894c72e170ba59fc 100644 GIT binary patch literal 1221 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl45bDP46hOx7_4S6Fo+k-*%fHRz`*1j;1l8sRFIZrj1|R2 z>!&6f*;>m3WdnRQ;3_@bRN!o&l$M65j+U6WhiY=XA&~1}tKjRUmKbXYQRD5QmXcr; z8mI*{3L+8`paoE)^Auh< z9}06F7cXr7|L^Dje?Jy4X!`a2$^XA^|Ns3oc|z`k`{(}u{rLayyEQ92^RumQUfJ;f z&x`+m-}iN;etdWL|DTute?4q#3O&8I`1i~Ht*f%y8vTEKyzuhzq3m?a(?=Km|9Si5 zfeAHbPWf3@KkirkzZdiWK}u_lbAFaZcDh+ss%cNN*S^&O|L>>VKAQRHLi_*QN&l}p zteUKsm15LUrPo)d_y4rS|8uIpZ)f#)M*MxX`2Q`R{(6mx4XU3GvHU;H@Om$MZISkx z$%_B48vcKfe(hL8ajsQYo&Mxz)!*kC|6gMGc${@km-7F6PM;pnuCMYc%r^XVp7sAt z#{V~&E^QaCDKRO`vEH}N=>H@B|Bog9Kav0cSnU53xz;+{_fJ~?e<=R{spjYs@~#VF?ELI!zOnAZ%c*jR~%?oei-|C+wHSF=hr{#{iB@rSbz2k6&AZ=ZhVvL zO@*1B%;9HD7m%Cs?alQa7d}5($|FQ)=b=kD3xajjIZm#YwRWC19 zNr@$=PHAhcUbQqkEA`bY>6E7uPo!thiWZ5wEi(0%wbkw^p%NjxtE+zhQumo-QhDl1 z=H;tbBqdDJ&df2bJ~zj*`1#rR4FSR3jgHaj7ays5Pt%Lsl=G7-*0y$mEt_`u+Niy0 zZ&#JR=I!nAZD3j^ZI*XuOXcZfYj2-9qwAoTdTU?u^L2NtzrDMipUGObYisY?xqF)z zPu|=;QTs@7P2RhAT5|K=X~j9nG6XXDJ&fL1^92|}swJ)wB`Jv|saDBFsX&Us$iT=* z*T7uY&?Lmrz{=Rv%G6BTz`)AD;M2q_6Hqkd=BH$)RpQprd?rT+s6i5BLvVgtNqJ&X mDuZK6ep0G}XKrG8YEWuoN@d~6RAAY{z~JfX=d#Wzp$P!6di@3f delta 931 zcmV;U16=&Y3Ec;f83+ad001BJ|6!3K6O)$&B!2^MNkl_yf1&A|& zc>1h)XDfkXLQuXXRBS7<9)JL1M&=kBN|if0g)|ftPv-da@tqAH?*}nvW=2=VmySlUu`$eg^!Vc!m>z%tVnB1j!?$nl{{R2)+y5WmKm0Gub^O10VKb2b6NrEO zUw^!y>3@K)MiWpeJ4_Ei05KtH<_A)uyxc&!|KAzd*_arVlyw+v?Yx2fA3*$#fs>P+ z;m6NE%0PA8Fns_4#01o655y}tIXON6`KRvRJp)wy8_4|2@cY*f20?y)hyi~;Gu*m) zhT+kpkN0%6!~|gm00aO*0KWeNGcg*8Wq(^hS6)>(00jjJAiu)G05&r<0PgJL0I;#9 z0390_02&z<0LsU*0Hmk0;zB+pO&b~xZY(Mk-p|qK(gFZM0KWeLW@0n*l6^-E2mj&# z00IaAhMeC3>hARb{Qdp_)z<2_Dkv36A|4e0!N=U(abYSXK{ONq+~x%UhM36CpMRsz zKLQ9826tY4>=z1#5r?;i#aTRzWUzyCfydG0} z+w1@b1^NJKPTT+q3jP291ONaE3;h5yCGY^w-4FmWFBkyx1V;dyro8~j%HOx>>GZh* zh!Gl?EUr$1~avnecF(DbS z_{7;C4B!8|Fg$%{%y8m77k|U*P0tz5ToGXS^vj;%)eg66S)8}6gg@rgJ`1#nCzkd7u@xg-+2Tz@SIsvHs3J`w> z=>rHLMzjnC$|NE{ECl6$1LEIMaSkZ|70B-}7XSnp02zBp=_eHd_Y434002ovPDHLk FV1kBVsPF&) diff --git a/toxygen/smileys/ksk/pleased.png b/toxygen/smileys/ksk/pleased.png index 8a265eae1e5296fc8e39ae973a2d1c4f4bf0cde3..2c7e60d7cc68eeee4fbfac1db9379114a9ea6f79 100644 GIT binary patch delta 956 zcmZ3<{*!%zBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z_=>FC&U$~AT7xl870LT#zg1=MIys=fn;=q9z>0svoeGc9;^*y zB*X&E1nEslFak2-qxFGgy|0(LhMEY_2samyY9K>dQ4q-R_fd}u({*)H5)jSQ~uy)Q0zO@BaVy>HXW=XHTtbtPRRaGuyDb`uDeU z?_Zz&|L5WVKac+Zx%d9{iQnH&Y*<~9kz%^9erx9c&vS2Hm{gn}eCzU(|341hyfnW! z&;QoNuK%B=?%R^y)oA(uiN}W<{tYF@o0bGMRX9DrR@P8vyJ@j^Q>ot9+n)a)yLL5N zom?aF{}RLNLk$0~@V!1J@&CHa>mx${FEPD8EcpL4!|zkl|Bo^LKgRI>jQ#&J>~Hrn zoZOl6|AxZ<%k?a`w=mRaGJHPC($y01|5f4tdn*5LvH!ox`2Qxu!~G2PISjXVvmM%9 z|Nlel{}(R*A4~mz$n*UcXG0;|#a*IXH&p)pvh@F_*8d-J|G!Uvdq1_g&iCx`i638W z`u}79|Ns9@o&W9thLT}PkY6x^)9NQe6Q+r18BANjVH3f;^br3?#rmIr^}m|jnmzws zkWsIy^731?oKZ(Vf4e)+kI%p~Em7wOP&Z?ex4TQPG|!`}Kn{C}r>`sfGj={MRetTa z8}|T(UU|AWhDcmaPGDeiQ%iGBQ*&d}NJum_GBY$a-ky-m{@GGz?V8-2w{K)+=FYKn zWQx&Sm-kL?UfeyqeRcJBY771c$f?~ZY=!B+39(*aN|b9 zCMTXVnkzXbDCrzhm^E!4BlFA5q(?kwW=00Qd~;=u52q(vGFK^(KiWCQMklZspq5`58H@)~=j3RX8bO!uFiJ%-rnv8TGlD+d=}K zuv>+C?%ce3_wwDVgIz;oc;86NmGb&J>*mVF3d*gh|M}JB3-1@#FAlF57oM@dGOz!A z1km5AC9V-ADTyViR>?)FK#IZ0z{p6~z+BhRB*f6b%GlJ()J)sJz{mdKI;Vst08_V#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mE@a5}&pk@)M^dF!huNZi^ z8GjhIZr#AJW9MFmkDq=RE?;%|0!S}F05O9IV?(LNus~B&4`*$LJ9q9f^iNvCpeV=1 zAT0Qw;nt1w4E+=4FeuCaXRtC6Vc_OuVz_YmiQwfcFSme92M7Q`0KWeNW@0luBp(Kr zTS@l-@$KCJoS?%1IWrFcyuHT&Cm!?wpnsyX068%Q0KB`z05vG+045ym0NvyPECB-s z)!pCk*8+$I6kzt+_Zb-8yT+F%77EU`wnFT)q&Li{{8m{NCY5&Kn5JV^;VUEfk%>ofdl9ukdx8yotF#@lhy!z z^p6Fs4y69w`yZeL_8TC8Kn6@dcz+t0E`$OZ7`PRHh5&W2Gr~1LddtADWCsI7Ob{Cb zkEjAz9Z3D{J8usHReS;nAm+2@pFfh7;ZzV3*E5h;F=1d}{s+WB*D?QR`00x8VV6!o-Pb3%8Cqpe7p>wK7D4mbN4>Ow(WZu4js9A`{L!7i(kF|x(TS|7AV32 z0*DbUjySwMRD*bV*|Rx0SXcB|S($l&ndkfa58t2Px%=+m)$6Y}Ky7{w#GqJU00>F+hL;0JU@iZ%cWQY5)KL07*qoM6N<$ Eg7N~A6951J diff --git a/toxygen/smileys/ksk/redstar.png b/toxygen/smileys/ksk/redstar.png index eb634bca5a64e2b69a2a7c01a42513cc0baa758c..33bcdf17d6bdb5512e5442dfe56a67ffb88fe274 100644 GIT binary patch literal 914 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl45bDP46hOx7_4S6Fo+k-*%fHRz`z(5;1l8sR1nX|2tm<| zj1aCLj1d82z|}-DGR847LKFnSWMdc^Js26~fSMT?fLc^x3|%k-NJxSiKtl|{c3HzT z+A=Z%rGR=J7#V>YeHj_WU|Mt-7<|CuFaZ#~si~>Q)%E}9&kwh3`G4=;G%KtBD_53C zNK8>v+oG?3&C>Flv-5u!m;Y{V-`w1;+1P9`HlCuTRU#?5B{A`Taq)jm&Hs{;|0N{0 zq^2Iv$@y<<`=5p7KLbNDE9*6P_y4l8S)82z?d|{D*!;J%`|sdTz|a3bBjbN*>Hnst zW(|%13m5)hx9}fSD=kE(ChP3ZJ)v3WWhg)v-tpowb+4|8N%(zndEc2s!crq__ zY?@s(KiyH@WZ@iJRuPVQ{wXVfrZ6UXySo&wXODRVi<=1|@aSu@F zoTrOph{WaO1O{d|H8(bq135 zD;;Xw)zihp&CSdC{q^(Z6%*O);_d%8I0$fj_|7dPm?|L5&&SEj+<0L^0;i`Y8{5kd z9UdFHPIMGVMoxHI6nN2aAx)Q>VsjH}{sj8_Q(bjB!8|vxj>Fe$9?duuV zx+6?i*;w0J-CEl?JeFB!!OZFL3zn`8o-ltocj=mW`x+MtZr|9sa;AvOfjPGi33)_D z70fmUrqd>C%@2DPN$v;wOtr){q9i4;B-JXpC>2OC7#SED=^B{p8k&R{8dw>dTA7+@ z8yHv_7<`&|Wde$Z-29Zxv`X9>n$P3_Qy7CJ$cEtjw370~qErUQl>DSr1<%~X^wgl# X#FWaylc~Ur!oc9^>gTe~DWM4fw!#_+ delta 722 zcmV;@0xkWL2aX1i83+ad001BJ|6!3K6O&{EB!2>+NklFViAb=P_nnlIM?NaO=ej%}xLR-vS6AkPFfrJUtn_g@7*HwvB<| z)hh;u|Nl{($qB@oKm#=e1Q-~+yco8A`hUc5Z_b<&pm07w0D+t@7sJT7`gl&x|NpkO z|NpbFfV5*`kT_5tq|Sqp@fOf)2|xn`00M{^7{?#A7#ZJOIe$K0RZWdS^v4ed2A~%h zcz94TKR?5DDJh1jYt}Ga`1^M~FaS0I4R`?%K#VYF0z*1lh=XIvR5LRMhQEKoet%~G zg#*a`fB%4y$ik3)@+8CI@836GW?*OoYPkZ$-v9y#9=E`ha)6DE4d_`-hyX7y1b_Yv z!9c)v{yf8*@87oqnKwY1L1F*_gq%3!ge4^z7%VLz8bNH3SAe0<@ccR05a9y{7$iP@ z;(Gr7|4*1EfB-^H%8DYYs=!oW#((f#MTKGWh7Anm0Rdnd#0QBBD=RYqjg$qdWrb-1 z2q0t^@N)s9a^t*t3_E7dWVrhI^W{7L|8Kk0)upv<`gC89^mGPp5fPA1evofqng9Zb z5jnnr0a*l0f4V3C|33jt4YzYAgA#{y#`dz@B>MX1Bij~$?%T>lD&U}1OWmJ00}VIX?lJcUH||907*qoM6N<$ Ef;RO#c>n+a diff --git a/toxygen/smileys/ksk/sad.png b/toxygen/smileys/ksk/sad.png index 3cafa05d8c0b540e447683af7de7c93605e54b32..0a33174bc842fd8897561b66e9aab1d0c65325eb 100644 GIT binary patch delta 941 zcmbQl{)&BqBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z&I(uC&U$~AT7xl870LT#zg1=MIys=fn;=q9z>0svoeGc9;^*y zB*X&E1nEslFak2-qxFGgy|0(LhMEY_2samyY9K>dQ4q-R_fd}u({*)H5)jSQ~uy)Q0zO@BaVy>HXW=XHTtbtPRRaGuyDb`uDeU z|9{{9|L5WVKac+Zx%dC~)!*MvY*<~9kz%^9erx9c&vXBOUH$*tj{n~e{Qq(2|M%Vh zzpnrPWzqjnQ}=C2?`pLC|HR|}vylHUQa3FLD$cU{|E8%p%Y4&f@Bh!D{y*{g|Jb#w z(dy(HiT{@vHcjCwPUQK2U8XpJbJJwT_lE`lpJv$9$50%@@cWeX|6`2*k1@PIWB>mQ z`^lXt|8FS#zg*8!9M90z67c_3VR4GU|6A<;Z!-SB$#7_Q{r?ZG|6jQLe=PO?A&pI&osUbEU;FLGJwTz)o-U3d5|@(`7?|AD(wx)O+}Jb{ z5>1WF3{8!CO7Bp8(EpTb1WU1V)WMKy_1_4ch7EL-5nbqo<08<8yycS zE=+tVI5F^I%^w~n!;SSDHx_>6?DTZ7atr8aw6s)JRW;RB)fM$n=k7M>SeBl0$iZ#b z3=8Jsure<%DUKyeQqny=j5wY=dG_>plS5rxUf&c8=6uJ*z(UW=%uLV1z{JKa9L&u= zk(I8gp{1dzu9Y*_%n@L2-jnK^8(ZsK8=Kp^v`NYS!b-1Gy}oC4&({0)YOQ*dm2m&V zitG%jtjyOhx;{AkJ-uFiQT_t`MeITR3~32=UyVN>;{^tQYKdz^NlIc#s#S7PDv)9@ zGB7gIH89sTGzl>@urfBaGBwjSFt9Q(_%!j#1QZRq`6-!cm6#fMU^<%5#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mEz{1MQz`)1`mHxxP@c%o* zpMT$<8MbcSz_4TIUWSjKei$xab@>8FFF*h>g9u|osm8ECQ&SITZ3ZUBZww6oz5?+V zpvI3341Zq(&3Mkh^#3`7hB5~OHzyOrh09L_FJF1N1!Ouv05Jn?w^5O2n-=Z)oq^%^ zBOrdpuzBkdhS_sBGPqf@FqBlzWcc~z27iOP^b-b#A6FR^#osX8eZr{2$jo;2?)~@I z0Ro5x6kzt+_Zb-8y+DM_2QCJN?@SB~uYm@Bdkf_MU|@LjA1-aLEx~a1l5iG~xg8*Y zKnB>WiT`9^_=yy>zkuHO4Wz$(0S5t?jU*3J&&kEA4`d1f1Q5soQ66@njlVwtnST&l zd@Udtq_HOps_Qo*+(GJDn3;Hh96o>m0vYi73zLep02?r7{zI%OnaF?vdh%iV*%-bs zs4)Eg4e}QQD?k8&3^;h}t!k8+{6nBEPZ&yi{xI~EVH!}<3pAtzsGL=j;nqV{hIj7| zgA&+pfB<3wrLcpifwl_;0&V7D=zpnZXJB9l#y^lkU`DVwNFJmPr2h7uw+DeTp8x`g z`Rw`Uk7Q*y6@69zZ0Xokb*SQr*8 z-E;rs>8I0x23!LOAQq?#C$Ct&J>m0LX9f?KL1b@RnCIyBa zd#*5S-L{wE?%j9mfqE{3;t3#t;CaE{M_rkli>*adLm|S;(}h7rS&@N{kC)-or_T&` z?%rqEwtWx7p(9ssU%dQs@vGNgHvzTW0^;ug0mO(FRvg|QszJQG?Ae?gEPAZ0%sjx% z^ZosY@6YeteRuHc_17DqHb6fIVo)qF00a;tW_UAz@&`8%3j;AfD5{{T0GKftfXR{p hm{A!1!W07p7yv|W2}I)!gp>dP002ovPDHLkV1n}ThV}pe diff --git a/toxygen/smileys/ksk/scared.png b/toxygen/smileys/ksk/scared.png index b9395c360057c463bd5eca350e14bcdd35c0c1d5..1b5c55c58311b86c5fdf703e19697405a44c8a9c 100644 GIT binary patch delta 896 zcmZo<-^V^dl7pFnfk8u;KX{^|aJ@pZN02WALzNl>LqiJ#!!Mvz!wUw6QUeBtR|yOZ zRx=nF#0%!^3bbKhU@Q;t32_A~NJ}zCMoDpoF%fz|k;pJzAQ>H@2T|kZtPEj<2WtZv z39&#kL3&dXjDU>zXni1A@9U+mp(X+}!p#Mw8pu#q6a+H-ebl4EbX}d4#6-D)TyGE6 zh)|uxSc8NZ1E7?)rYKMl4uFO>)&`$Fwc-8SyZ`@vdjIzJ*;DHpYlE`V%ofcpJb85K zw#{?SoZ9mG#f3AcHf`HHyLL5Nom?aF{}RKQ6$~%#t^fD; z_lx^m&#V!Dac}ec!-D@$GrZo-aAq;XwuubCPf7nj#`ymj!}~M#|Ie`BSi`V)8pFw* zDgSRM?485>|9}1eu9krRuL}1rl|Hn)e($E_nN!pEY^~b6IeG7CbUU|xEN|D)p1zxrQIZq1&5FUY7@ zReAZXTF$7WpTFIm=f`JYnwF^Z1E`rX$=lr}Pl-QJ9LQlW@vQfCWq-!b$EC`z{dVIX zpx7f%7sn8Z%gG50Om1pv&S`3HY#IrPrbcFlrpDV7lG#67>a1OpoAdUKtjydwmX1s@ zdh7Du$<2#-n~9XozrK|oGWQdCx0T3l9CQczFh$N`!8 z6J|`A!*Y!C;~^!UMfI91GYuxpQc^p2V9vaWGg%pDO1>1D#KUyca%ZK$gxQmIRnHxm z9Us6Us-bgYx>#bu1Wm0rE)nsQr#KA_PDD?eGHq)3l<8BaMQCY!VxGHdNlavDY;bJo z%0*i1*uL@h?`m7NaN4%MbsYRx_W!9;`o;E3sY>B1!(|)WS(%?XPXWCfuUg_7QIe8a zl4_M)lnSI6j0}v7bPddP4NXD}4Xlh!txV0d4GgRd3_eY~G6ARqp&>UvB{QuOw}$33 VIXV*!)wviLJYD@<);T3K0RRYTWOe`m delta 838 zcmV-M1G)UZ2Z0BW83+ad001BJ|6!3K6O)hwB!2@FNkl#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuotgVqsx^Xk}?@VPWCS@cPXshF`yaFtD-wWw`&~ zE`P(TmoFIfbhR0HdDu)}zItA!sv^Ac%GFmdKzacJ2yB3{p;Tj7psA^hqyU4QvI~R0 zkt+i;6BEPZ2gey?lZ#a!$P)qxAb*en zqCD(e3=F?NFo3WngHz@ooCfTh z%fg_}Bgt^_lH3V>^DFFF@J$l*um4lbRiVTz`!ZNfER$|f$Bi&Z{K-)5Ge8q zAb^<9o`3#GR)$kSNLR?w*G)hzw}ALNKmakKxst=%Lp6w(mpz-4gGG;(m6->adA`5@@csFnyYCKOz5aRw z)aK_v42lH?fB<5|;#E-o;4cPZV6tN12SpVqw1M~|5WfZDuR#14rWhc=0HDYHSiOaQ QjsO4v07*qoM6N<$f|-eqVE_OC diff --git a/toxygen/smileys/ksk/shocked.png b/toxygen/smileys/ksk/shocked.png index 8e137f6fbf7204b6a87cde0102d32648640d7d7f..83e0850498e7e064b6532412fe2567ddd901c26f 100644 GIT binary patch delta 961 zcmX@k{*QfvBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z&J0!C&U$~AT7xl870LT#zg1=MIys=fn;=q9z>0svoeGc9;^*y zB*X&E1nEslFak2-qxFGgy|0(LhMEY_2samyY9K>dQ4q-R_fd}u({*)H5)jSQ~uy)Q0zO@BaVy>HXW=XHTtbtPRRaGg~yb@Z`~@ z+cwWRb85@$7Z=W)+O%!+jFU$fESj5_kz%^9erx8LL$xoSUH$j(?~7-*&K#ff;@Qm? zPcNT2RK9OZdRL?6|0f>Xme~CJ_XlX%ww1Bl7MuNl?Aq06b#jfw|4R&KRxrG{xBlPX z-!JZOJ+nsq#l6k%4-5W3&G33R!+;^o&5L_? z&u-rx8y=oLb^jO}9UBkUJIDx09XozrK|oGWQdCx0T3l9CQczFh$N`!86J|`A!*Y!C z;~^!UMVc!!Gha&1lzf?)xl%Lf5zm>(o1B(AD}Pq*wB)=QDKv?PiM7@BsOr+COI44$ zwz392;@Ok>RCH>nL*uGbU7w6H1=-kUzM6Gw*RQr;yKc?OinLT@W2-!UY+3!YY1gJb zTju7?>RPuTc#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuotgVqsx^Xk}?@VPWCS@cPXshF`yaFtD-wWw`&~ zE`P(TmoFIfbhR0HdDu)}zItA!sv^Ac%GFmdKzacJ2yB3{p;Tj7psA^hqyU4QvI~R0 zkt+i;6BEPZ2gey?lZ#a!$P)qxAb*en zqCD(e3=F?NFo95E=}AGcthGvoJI90C{`> z0mK9(UVmXyVPIfmV_;xqV7LK{^%KuL7_>ZAF%&QU_B1`}f};AQ6B7 z0vT}d)>~Bu1|CTU1`Za6Gq)KSEPq@J7_^N;8Q3{^!8C{u5(mkH)PdB$d;bGu)u01I^Fp;vF0b zAQBfYFdrl|00sy30LskS00RRA01puz0NK>E0L;gq05mcW02Cir0K?7%0DqvRyx+;o z<(2>d0M-HsoRAFlB_H3obuXQfRffUb%7=lQ>pR2ci#r*%Zk);R_t$j>Qxi1?6|De< z?fafEtlzkc;q;lO^WT5?u??v8B|red9qaF-uFTEF)}pDQ5aH$N!l0t8$iT?=x)MzK7w^k*l{aUUh!C_|@yLn}Aw?@x<^QAb`+)%i-;z8pO-Xp3TX@qQ}b0 z%md6k-`{`u{`}6}cL%Rtf4u>y<}wgJ2VzhxFaQJ)x&g5K!41T~WW~S_jlADL{1J%X i0`XTM{tHtK5MTiQJP{fA_-rfy00008N diff --git a/toxygen/smileys/ksk/smile.png b/toxygen/smileys/ksk/smile.png index 3f08d94550da0daa46c12a20dfda28868a435440..a431ca76258c95674b77543baf4b5180b007ac38 100644 GIT binary patch delta 934 zcmey$_LzNwBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z}O$)6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b%tPMVUYQy`tcmMzU^#1Mbv!~WI)&^yznQd5I{rlUw z|G#hl|MT$wpGW`y-24Cg>hEtSHmt75NHN`4zcus!=ehsCuKxdR$N%pK{{J}i|NHL$ zU)TTtvgrS(sr$C1cQso6f8z1~S;+qvshgGr6=zxff74W)Wxi>#_y1>6|DX8$f9%@T zXmxUp#Q#eSo2GCTC-VHiE>oPqxoI-v`@@3&Pcv-lVr8tTC|4qjKHyIA?uK)j`_5TZ(;xwM(RMz5DmaQ8q z|9)Be|5NM#54r!}r#IL6o;^PCBPZ!4!iOb0e3`}lnY0hbCZfqI}iKa$o zhNi~b6O!3KTk5P`lbiGQjjYVvIhKx0F?#Fr-pS32yJxqr?v4!)&z^sbjgAKu7bZRw zoEUhq<_`~(;l_;{3+q2}c6vHkxdn7IT3V{As+#Jm>WX@(b9WnbEK5&0ziW1oL}dd7+C1(=~)<<=(ukS2XnJeW@M#nYG|r! zW#q~=a|D>1_oSBk=El~>=K5}3+N5NEVWrn8-d^9czP-F!s~%+~)ZhQGB0EDWEA#b> zt`81>Pp?;Bl)pfK5ql6ngGZX(;uy|7CP2@tmbgZgq$HN4S|t~y0x1R~10y3{19M$N zlMq7#D`QhDQ!{M?11kfAPZO_9K+%w!pOTqYiBkisLGzg$or#9(Tnr4Ju6{1-oD!M< DjUs+F delta 826 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 FV1hR`eZ&9& diff --git a/toxygen/smileys/ksk/tongue.png b/toxygen/smileys/ksk/tongue.png index 840d743399e2189b24b682a59503745927f878f7..bf6c37e58705c6d1c4b243f8d041bcb85466f1ce 100644 GIT binary patch delta 930 zcmbQnexH4UBnLAC1A~SxfAB;_;d+H+k04(LhAK4%hK3dfhF?Ibh8GMBr3MTPuM!v- ztY$DUh!@P+6==i2z}OSu6XFU~kd|bOjFRFEV02S>AE^8iHULpx!xYC z5urMXu?7h-20$roO;MmA8~_b%tPMVUYQy`tcmMzU^#1Mbv!~WI)&^yznQd5I{rlUw z|G#hl|MT$wpGW`y-24Cg>hEtSHmt75NHN`4zcus!=ehsCuKxdR$N%pK{{J}i|NHL$ zU)TTtvgrS(sr$C1cQso6f8z1~S;+qvshgGr6=zxff74W)Wxi>#_y1>6|DX8$f9%@T zXmxUp#Q#eSo2GCTC-VHiE>oPqxoI-v`@@3&Pcv-lV_}#E-8w{r|E5|Ns91&-XV0!@;m5$S;_|Y4sDK3DZQh45qE% zu!&$^dWip{;?KYOUrlb!o_{aMs8>~a`K?;csH301-JR#hXJDF^Sg-Q~sGBj#+uf!4 z&zAF-fE@M`PhVH|XY71js{Gn-H|_xnz4LT&43W5;oWQ{3rk3WMrsl?`k&tL=WM*h; zygebA{j;Uc+BLa3Z{Ntu%$;ND$P}ZuF7KV(ytsRI`|9r4@bK*U$Jpq2P;p`6L&1rG z7i<3TFd1&#xUujfXJ@^qgOyuAN28^ss;a7~uBxu6hdOt+LC3Q6ltT_~yJlE07l)Na zaj`5(P51OL;&}4p*;8kSxV*k87R>pMiGhWlnUR^Eg@K8UTR51TeJWj3MFSSB+O#Mp z%&()-@!qCg%eJjsvu@k6TS1N!!C8<`)MX5lF!N|bKNY}tz*U%)y z(7?*r)XLOM+rYrez~IxwD-%#Or|Iu`?jr>mdKI;Vst0KZOv A!T#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mEz{1MQz`)1`mHxxP@c%o* zpMT$<8MbcSz_4TIUWSjKei$xab@>8FFF*h>g9u|osm8ECQ&SITZ3ZUBZww6oz5?+V zpvI3341Zq(&3Mkh^#3`7hB5~OHzyOrh09L_FJF1N1!Ouv05Jn?w^5O2n-=Z)oq^%^ zBOrdpuzBkdhS_sBGPqf@FqBlzWcc~z27iOP^b-b#A6FR^#osX8eZr{2$jo;2?)~@I z0Ro5x6kzt+_Zb-8y+DM_2QCJN?@SB~uYm@Bdkf_MU|@LjA1-aLEx~a1l5iG~xg8*Y zKnB>WiT`9^_=yy>zkuHO4Wz$(0S5t?jU*3J&&kEA4`d1f1Q5soQ66@njlVwtnST&l zd@Udtq_HOps_Qo*+(GJDn3;Hh96o>m0=kpo^%o`;1_m}ZhLUMO9xHAPmItZ>ssH`^ z?+=hDKmdVUaPZb!)hILhhYUUCPZ&yi{xIMIJ>@{|zSho2Y z!`}Tj*S~rDZ4W>IF#(h3ksG%jE;xLV4;UngKwsGcoym`CHHZ%q2g-xg?KyDy{*7C2 zwgY9}0R#{@AsOmRKE845UOFSI41=+UtJhyQ0kzx$;_m zJiyHJ{r!jU&+puQckt@<*F+nDYAyrub07xA0s}w*F=7d9khi#jSQv=;p^*qm1;C8K l08Ew)z>LE17p52>zyKi45Muy3pmhKM002ovPDHLkV1j}ol>`6) diff --git a/toxygen/smileys/ksk/unwell.png b/toxygen/smileys/ksk/unwell.png index c093a9ab1084d945ba752a6dce15f4d0843dc8c9..5bca7213e9d15665c933e8ed58c72f1e3f8688d6 100644 GIT binary patch delta 935 zcmeyt_Jn0svoeGc9;^*y zB*X&E1nEslFak2-qxFGgy|0(LhMEY_2samyY9K>dQ4q-R_fd}u({*)H5)jSQ~uy)Q0zO@BaVy>HXW=XHTtbtPRRaGuyDb`uDeU z|9{{9|L5WVKac+Zx%dC~)!*MvY*<~9kz%^9erx9c&vXBOUH$*tj{n~e{Qq(2|M%Vh zzpnrPWzqjnQ}=C2?`pLC|HR|}vylHUQa3FLD$cU{|E8%p%Y4&f@Bh!D{y*{g|Jb#w z(dy(HiT{@vHcjCwPUQK2U8XpJbJJwT_lE`lpJw=dO8WmX#{b6{-k-7me}=s{hT-JS zl>avr{$FM(j<09vY6y?Z!Pop|_qcjv*44lM@)2+|<&X)70G9G!ha` zjm!*9jkhNxvwybKS-U1T=j|I=nYnW;9hqYE*5$pEn-_P_ZeQIU8y=oL{}>w`4=OH9 zd?+|E@M6s$9wx($^&2-9e&p=*bg*&@=xDUGWM!Px>MG$9eECwMf^MkKoJLD!V^wW# z4Q>@9V-*!imQBS)$;qD^49v{cqrK)dn(tq*Va1LmTbArtu|Xj|s-@9!*_u6zHm%yV zYSW@UK4E?xjgI#=E!(zk-@<+CwjB#{oGAYyMD^*hWzVKPTjnMjT4-9oA;02^sR{4a zEu|%{6$}17y)Jyw{DSyJ=9TOWe!uLFcQ33_1$tez#5JNMC9x#cD!C{XNHG{07#Zmr znClvvgcurF8Jk*}nrRytSQ!|6ns{XbiiX_$l+3hBTpHL644Tj6=u9+J=VD;+boFyt I=akR{00>rzdH?_b delta 829 zcmV-D1H$~|2lxh%83+ad001BJ|6!3K6O*#!f&g6NoK= zSQ?0*0`YMmo<3{d*?s8h0Ro5-Nuv;us*{#h$oKJ$Wl&PqWe^Y)W%%&^CBwbDml?Kf zUc&J3;r;2qe*LXmvf|Pkm_C32VuWheVqsyv>gMXo;O!mEz{1MQz`)1`mHxxP@c%o* zpMT$<8MbcSz_4TIUWSjKei$xab@>8FFF*h>g9u|osm8ECQ&SITZ3ZUBZww6oz5?+V zpvI3341Zq(&3Mkh^#3`7hB5~OHzyOrh09L_FJF1N1!Ouv05Jn?w^5O2n-=Z)oq^%^ zBOrdpuzBkdhS_sBGPqf@FqBlzWcc~z27iOP^b-b#A6FR^#osX8eZr{2$jo;2?)~@I z0Ro5x6kzt+_Zb-8y+DM_2QCJN?@SB~uYm@Bdkf_MU|@LjA1-aLEx~a1l5iG~xg8*Y zKnB>WiT`9^_=(W(YyJ(0fs8L-7<#gyf=Kcp^_*O+`aq@-KmdUZ5anS9+W7kekbn7~ zfiPfX0I6qTX5s;I_y7V3=uU>$Uzk)F7}$WJ&dR`0G7W-3Y#2rn1F8e5|NZ;#50D5z z0D%lRc5VNqzPQY4cE!#^ekh8=sZFl^nnmtp7b+kL<^FaxOd9zX!W^M8WBkGe89 z7h8*_hC+mwrwfCMvLXW?A1}kFPoEj?+`Z4RZTlXELr1ROzIgfN;#aS~ZUSn#1;pP0 z0*DbUjySwMRD*bV*|Rx0SoBy~nR$Sj=llB)-=E*P`|jY?>#sKe)m#SR=RgdK1qOfs zV#E^BpkU($VquUUp^^6+h$ev&=G}JZ800000NkvXX Hu0mjf?#O)b diff --git a/toxygen/smileys/ksk/yellowstar.png b/toxygen/smileys/ksk/yellowstar.png index f8a2672bd4e6c06f234cd7d6dbfc70ca5a37d44d..5e00805090f90d28c9dc1bf8da37685b95dc5718 100644 GIT binary patch literal 914 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl45bDP46hOx7_4S6Fo+k-*%fHRz`z(5;1l8sR1hD^2tm<7 zj1aD$8hjke~DKq;VJ2P;OPMqgJ(F%ghTAka}~@Nr>uumq`ugH3CidRkon|NZ>% z&X)hb?@jBm`u}ZZNtVQvPPHw|^{*YX{PxuO|2voe@7%sUb-Q-lX3KKpDVw+g{Qr&S|JRcLUrTIRlX`q#&j0te|6j5Ef5ng-$$IUi`~TOnSqYr~-`f9wYxDoD z-T$`^1!?^MKVSo;4{Q*))p|1S&we_Qwe=kfo)E>7?F{r~Iww~v=Ltu8OiRh-uA zy>M3ihLxom@qGXP|GzxRlL_c(tCAqUU-`qYFN(S7g7vq-+QW4gJ}-8-0E8i z0*VzUh3F1yJ&v8qrAz&Ikv1K9P|8BRsc<5O!9VjDO}GU^9aac zFY)wsWq-!b$EC`z{dVIXpwKx_7sn8Z%gG50%x-FKY$6A8jvnedlfHS|#gdbB^QBhM> zQ#qoo+59%t)6dh_+uz&QGpuz-n69$1wzayowsCkYv(AE<)8iK`T^l@M{&McpHS_j0 zE)?9pv2*225tjpVZXXizh>R+jZ46AOP1c$p_AHXz5A>O8iEBhjN@7W>RdP`(kYX@0 zFf!6LFxNFS2{AOVGB&j`HPbdQure_CH1Wy=6b-rgDVb@NxHUAN$-L1;Fyx1l&avFo0y&&l$w}QS$HxPm{AxQJYD@<);T3K0RRY+J5vAv delta 732 zcmV<20wevB2bczs83+ad001BJ|6!3K6O&{EB!2>`NklCgfB<3yX%-a~zo#J2$Dkm`$)F_9#h?Jh zoScmCVEX=@f#JcUZw&Vydi zl9Boh)OZsp{t1Y`py40DU{q%~aaM$(yL;yCi`V`y{rH*THb4L|U%bZfQA?Ha&6UgN z<5gAF7(|7D3K_Y8mGhDwT#V~c+8ios3|E|CPh+z}Z)E58&1oJ3EgdbzHkbe-z zlBvCB3=sE&k_aQjI}HE8;hkN4lHu^l?;9^)XJ`WoTmj;5009J#TXuGa18i(S&oXO5 z1Q>ZC`2S}J1_Cy=^9*m^ecuXX-T-L^i2(!ggy<)(=P&>NglPf@Ab;eftSBO;3be+I;rnkDhRvHcFq9VufN2mPBrYtX z%)rITAPZE>3eyA-K*%oO=i(M(*tmWk!;Wn;8Lr;=eEH6U|JyEI?$X+}WxB72S2_bX zuLuJxD+52sH!w{A0mO)$EP(-8^z}P~?#c82PrQ1=a0@7O4~ReOs52-j$VD>de)-Dq z`@r%43xQ&nf%pSR4?qAhq9tvR(|Dm?gGN3aZ~!q6G)h3B`Wqw&5MTi9HQCAif%(Y* O0000I;o diff --git a/toxygen/smileys/ksk/zzz.png b/toxygen/smileys/ksk/zzz.png index dff4463a26764b4661dd82e58b1b0e5a53cb4f36..0d17073b24189d8879001bb3ccd53e8a3968d453 100644 GIT binary patch delta 959 zcmcb|{)c^nBnLAC1H757#dm_7=8hz8eT9klo~KFyh>nT zu$sZZAYL$MSD+081LLFspAgso|No~Y86%^lIK!9-J)lTrm@bfvj?e=d0%W*3D?=FJ z!P-DZLM+fska$Xh5s(odtq&yYeZAB*)I@+rxVeB-0~yMSf+PW$5vr3IYmg9Q0F=_!6a@+*1E9H$wZUgkZFv9o?*D(E-oL$l_SCw@+Muj7vkj}O ze}6mo|M%_xe;)q-^XUJdd;fo5{r&C4hSe1r^(m(Nwr2kSJoo?C)&IZk`2YRD{~w3` zf8YK8>-zs+7XAM;b>Eisu13rMPdxrV3;F*d_5bU#|8E-qziImay6XSS?ElZA{y*{g z|Jb#w(dy(HiT{@visKpnU*RiGIif*G7vKM|TR zO+?FJ+6oSv2~=gsj;OGz?IN=rHta{5qKPEuA{UgF7PeQJjjQ*)EE)89XM@#M{+POYO) zUp;&G@a5CDk6%AOf3~?^=diSZgouogl$e~Lq^RuSUEYPUxjf*uHAz+SU0h>esJcyD=!h z<(oKj^Q5BW;9~FM;Kg05c1T5Od|Hv6A(ff=`bF0Vg9XfWwt}_HwSu-GmlFVdQ&MBb@0Ilzy>;M1& delta 932 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 Ro$det002ovPDHLkV1gtf>)8MR diff --git a/toxygen/smileys/starwars/c3p0.png b/toxygen/smileys/starwars/c3p0.png index a37df94e835c8fa8af7720d266f17549ed27eaf5..be5adeab2e3d5b249ad0b0fdc97dc81cccaf66ba 100644 GIT binary patch literal 1417 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 suD|Hk8|S6pS diff --git a/toxygen/smileys/starwars/confused.png b/toxygen/smileys/starwars/confused.png index 54afd60e8d9673fb2c66f86029dd43851abbbd32..5cc2fd147fd9b344b62359236bce8f81d994f362 100644 GIT binary patch literal 1259 zcmcIkc`)2}6#r61NFsHnh%7BSL9DeFYsGHLvTR%Lh13$%LT(A?<6@VKC0NhFgU9|@$hic7)FE4MlokMu%(YZWyz5wZEi5l&MT~`G)rxq&g;C6;! zs%CX{RoI)1^k6+%;;BSY)7NYKiM7QyThGrdSK6Q>--_BVtYfk^57m<-ZxfL&cj4Hd z!pSnYgRxXa5qF;z4c*z=+JZX#(cxgY*?MbfU~6p}ZXoh2NSm*i;%*msC<8%|va&KK zCnteG0Ck*4ht9#xR>hX?uISxVV@~r9zvlP`#yO%nxa| zL*JG`5QNS>L)s|tAagiLN;2XBw^;5hbNBZ4(9uk!!xrslz%S11N;dX(AcV_AdmYgs zH>CTtWWW*S*+F##v@c4`3q)G2B>i-RONLwMlA#o&)fs8ChI!P@CIZ@RCvLYEciNyl zTZC&R?xJkenercKi`%V4FGvf2Xu?fqNHbyTp<#KHYUy>A#V5wxjAQ%?Bd7sCm#Yd@ z8+T=DH{3kBQvQ{&%BVU{y)XNCPnOQ(NR`36xY=Blgfy*`2PrQuM`q zj>=4ylJKGKW|5xo0dDrE!z(!&f;=r@fev;*0~Zg*W2dIWVY+z&@MTITl%#twXLtL6 z)^JbX{j9V&?)tJ$rmR|a!dg_DBGxGt@0Hh2;)P1ad)JZY2vUEBzn6~#nG{;PF7*V__(B>Dr$;zJHXxlmrE7M;Nx!T(laO9g63Pl;FY}ic zHCSv7b+x>7^W6LQ?)iH8_;}}yjgEVdTM|i;=IIs=4hoBr2A3nDNAj)0 zhKqYV4N|zHVIzjF1-h@flxTJ3eA_GI+MmhBY&1bj{lB*b%<19k;(A(2rj@D5#{R8| zU9Y{LOjKTMNa_?VYU$J7t4-F%_lDpmo5TNE>bH@UaOXPOq9?A&Pd>}Jq^X=lAxtNr z>vm6*XTG_{xGa%MNvkYxENm<{Hnl30s?3OHn8-XY!ZxnYaq#1?0y%7Qa1}I-?xgGyA-KN=C5BZqQfBF8)`TxTf-om}kLBcfQX~JPblCXb8 zxI*}zaE4G3hNbuTj+3&7L^z!!%oxLya$=|iHD81j(-C@3(ydpKfj=+D{e}KRE1-w=Z>7;{w!%vNn-#1s^X7Sx+evm62 zDfMJzI0Nq@$UYGX`SM3lH_k&ED1$o<1ZD-!V;_UZ3^0Ea+O-qtJv3Ub&Hwy9nZG77 zD-vEVJn|?)7^M-FcqRnd(Zpb%=qxobh>T`u zXE8A`0nhV9vGjK<`YJN((kT+@7|=7=FS0utl^IJ&V5$tXu0>=)5WsO9Xi7lS^*5Np z!88rh*7dtO@xOhYm>_?9h*+2*OFmePLO7%}FJNkF3UhOFP*y*Ol%xtmhrS5} zOJ^{7?72vn&`4bQjO0BK{&WlloST8&G-NdfIiX|w(yLJV$H84{y7N2a3^mwmQ%tHz zZo&C!5=!qVf;x>DGf9?)Z#M;*DMq8>ZCZwuNkvnq zZ3K;TRGvaaDg|}mHY}lD6l7<&)~aPySGy-Y48XU8doHfX(sx|y@9mklH)`VZtspyX z-dKGjyRTPe!m4vt*KAx~s3QzHj*Jc>o4Fh8m&-LAET)mmY7{d>V`JA)7A-VeKE{7X3ef15f4o@5&_JASl4EJr#)HKaG8&_bUDQ|Bh07`0&*eLd zE_UmDuD?*u^cuKnxhQ12%l_@gR#e{ogK;EuIg(k`x`0^rCyC76`)Yih%4SBgdD^{Q zCGR~v3}KlKw5&}*^a2sp_P%H085t*olZ2UcI<4w@{PrHoax_J(zVFk(w$W@hMUPGq z-XWs)|2>XEaf0w6IZP9t5tB_2&D;>z-+5_IcY=4Fe+3u-6E1YAAW-`j00000NkvXX Hu0mjfJ+Ijk diff --git a/toxygen/smileys/starwars/darthmaul.png b/toxygen/smileys/starwars/darthmaul.png index e536a7e91f7c062e4d2b89cc4a7ca15a4d9ac25f..57e8ca167873908e96254258709d4bebbf225e86 100644 GIT binary patch literal 1376 zcmeAS@N?(olHy`uVBq!ia0vp^RzNJw!3-q79eei#NU@|l`Z_W&Z0zU$lgJ9>DSy+IG4Gh>hIDm+glM4+&#DPk< zxp{yjPzewLbpg!)GJqt|0YJ1cCuV(BW{{0(lmzp39gqkzT96;VzBbp*L~ouN!w#@4 z)Wr-8MjV_#HX{=gGcz+A3+wdS0zr26FjqSjE^eUJHj47o>Iz)N#btSUl6+lxxw$OF z#iE3S#Q6EW#6-Pq%p63;RQLtF1O?q?rGSp%<>bl{7K!5*2;$=}5Eii#6;Blr%@PuJ zQ86D0sWdT(Fh2e)QL$igNjFh38392*VUbi};RF%U6k*{c5s`RdVF5mVMM2?k zAz^JPSycsPJ7EzG4o*)XS6)#`Mm|Vb#7Iz>mzPgQNJL6nPF7CQfR|50T25L>grAp> zm6eT+okL7aqO_!F<+6pz$%(RZ3Ic+{z!(>okW^Gw6%-OS)zB#R^Uu&x36|mvlw=PO zXT2C_H)+zO6)RS>wYBl^@G7gQOGrxF+S>L8*aK5RZb-y?Z!Po=?G63#}JFt$q5H|XUvFpSu+Zd5!9lIA9v3xdO`A7SaQe)N zQ|C^eI9tuFE9~Q*KTU@gJ(_fB(WXybHk?6Qyu!jluV1-#)%=Q?nQ6G!#6_XImTq0U zck$-cyB9CtzTUM>WbLNE!fJlAY*Jrsc{y3l@5OXw*VQ$rduDD93QAkPFd*Lf^HqyT8A=yZ!%xgTC!sHlAQ^?9&Tduspxu$BB!{$NlHnl-|0) zF`FxyZL)F1!JEa;^<(yw{Jb;Oct?X~^GwUIgv89!6R%%weQll>bvq$EvHS9Rp0aOz zI(?dbel`|QBpKRVmOPR+zYHp}R7+eVN>UO_QmvAUQh^kMk%5tsu7SC(p-G6Lft9hT zm8qGwfq|8Q!KaB=CZK4@%}>cptHiCL`Am)uP=h4MhT#0PlJdl&R0hYC{G?O`&)mfH a)S%SFl*+=BslZ~7fx*+&&t;ucLK6VnTb+~u delta 1064 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 delta 1403 zcmV->1%&#?4Xz82B$Js2B!2}^Nkl6T!Z$eaDbQmD$jEIsL4Fqw~U6|X5Z0oFZ z8yj@n>E-n5^St&nT`~G5PfjoId4A93|30sTe_s+?ZEdaBYPELda(}rOve|4^JRV0T zlfmNRB6M9B8)aFBBFj0K%em+3wdiU3DMOYc%NK0L%IEXK`E|fxFsM2lj%R7ZD+F_- z(`m4!QYm4}%gX}JOHmY<&1PYh-ijl0SLR=);O0u;{|^m?LTf1cZwMA*$E7giGiS*! ziwGqNCX-1(`AI?Ow101xrfKKs?`$df+YPXBoN^hHzaJ#WA$FWdB)}zQ;G{M!~8C|A5-!EBKQNz`gVsC419U-bZr$3%$4^J2rMmy)6QHady1d02-N-H4wCi z_TP8BG1Z{Tc&BR9&MjtF1^ztUbB|P(B?W16x!vwQauQ@{A%A)Si;;5>6fK#+a_`eSZo^3!)OM zyr(8A=Tk;08Nx7jOlri;%*=|O8fUe^Wx-?qG`2UlK&_}k|CulG;S~jE6Dq$;>P^Z2 z1r3mGWf+%|f&84Bni75EG6$;scw+y4tZ!<-B55jR4AAF^8i6)hOu!U8N_08IvuF53&L)*BRnyIy4exDyLu7p>Tbi* z+%&pRp1`L|Zp^0g$jS-i3yP*T^{<#btnL!1)$jM8B5|~Hrd(d*=*UtY)qyI?+J=C3 z1-laCxPOHL$Wz}3!x5ZNZba|JNg*mWh7}Y&PJY|SqTG$^%IooXY+kRom4(B7GZe{v z+i-IeDy0;5#>a50hvePZh>?L246oUU{$L}nUR^|Jy$Qo3mx>dLt7Is;d=;V9)z#Z* zvz4PXrV*P}%AsBR9e3(~p=nzyMt}Gj7wu~ixqtTnv}_)MBo>QdUrQ4X+~0v6u4|-13H`GfjBkDz`uf{pp<|S$ z0NJ@BD{v6dY_eb?92VJfd)NrLn#z2nRJ{h2Rr(Aai>8Hz1#usBZn+CjzVSAqHZLNB z7k_cV5yT($t*ETI0an#QLC|ogK`=QLf!pZ<-yLoe+i#;|qNgR`AeHzi*}RcpvAmR> znX~k)sz8O;hk?lmQZWr zl~P#~3}%f`xgVrKGFrG_b5bG8m)hFeUVk4N8j>N=rL34Jh$uV`8DwdH0ejhn73$euFNw}z-mnSCC-E&N6pm30Y zc(Q6m1^&4L1H?H**=nMv^ykO(^YcE+6w}kw2!|)dV5q675kQI%)Mt5oGK}*V`qDQw z)=SIFnv^7kYw=j_&_|!p9LN+Wj&W8EuyJGvJVnqx{`qeK1^^XH<-NHI(Wn3b002ov JPDHLkV1f-Ku#*4) diff --git a/toxygen/smileys/starwars/darthvader.png b/toxygen/smileys/starwars/darthvader.png index a0b01e48aad8e190ecb8e061e6127de49c188769..66c14090c0bb35ce1c4c55a504d851f15ca55aaa 100644 GIT binary patch delta 1479 zcmV;&1vvVF3j7O@B!2{FK}|sb0I`n?{9y$E001CkNK#Dz0D2|>0Dy!50Qvv`0D$NK z0Cg|`0P0`>06Lfe02gqax=}m;000JJOGiWi{{a60|De66lK=n&?ny*JR7l5-m0N67 zMHt6_b2+;`d$v8>3q`B+!cx&fkx&vffT2ktC}@-rFeWHILVrRq`eH(S^TEVm^o1x- zM&m6J!AtZ3jEQ2!S_`5gcgjUBZMN-}?e5Yu=Zp`qwUpZOC6oEinJ@p}eBaEM5mHK& zQfRG9BdN81(s7)nTI)8gwNgqYl~Qx1ltfWP7>38gFw7Za3dWejK@c1;#(X(AI9Om3 zv)11BPfP$mtADlLskQbU#~Is<@B0KnKomtKFku*CjKLT~5Cj-w28=OVu3fwK>79V} zIDaAVKA-?x*JbwXH22P$g%EwsIp`xl5R;5Dz|zJJZti`zNB``y>Zrw}VSfBpF< zJv}`n5+2%dIM>@dEx5L}w&c1cXJ0F7Un?&GXJ$RIX|q(C^Sj@D57;`EaTGZZRXnT=?p}E?{fN5!-dk8Ankx zW1`sQOMjQ>@9Ucu3LvDUs=AugwZ4Y8n)$Y}W$`Mnu91uTKf40F+;PNyG(LU|EQF|- zC>Dkx*RNm4aU9CZ%812c1VKQtSY&i`6vuJKVqGFuxkI$o2WuK0-zd+0^Fd+oX!o1I z>m5gIZld;-U~4T>O2RNCm&>7)!k7>#1zP`iFMpT2MK%r{B)#l$a_2pH?wb$x057ii zgYy8lH`A@)#);5OCNuV(NF+!mlSEO-;9wSj)`VsA-Im2|4lbAYwe z9Zd&5Rnd*(}x7HC#MPK7M(gt?aY}@e}Df%;OD8pr9NIdaiLHsa^`F=U0vN=zI>JY^Ox8zPx8bp!Cj5_ z@I~hlULC4s*HuN{3ILA%eU!Sox=Ho^FMs1@$z+mZ@kT30Q3L{v30U*UA|7Rcw)hpC z8TFhzb%hV}DZU#_-PpMl7-Ik!9v&u&EWi@rjp@Kj2+>hrU(bmXCkTRod_Ip>iqCiM zK;M(*pJRV;`t7ZJe8upyoJp=Y@L$QK#27Pn%d^=mz;Y?2ilQi-s?@EGO-+7TS$`Q( z6frU~LVJ5Vv(st5?Kuj;D1AMhZ0U8_JD|wsMrdhiA(2R6jKNw6f?!PW%;{59R#o{x z>y)*YQoi!!Q%~E*rY76m+-$YhHXe`LSS)6hQg(UUgSMi=x2aU61#Bu+X;Z06``q)J z?1~jDZ7Nl1l~UICeOq2$Ze7>4zJKpqDdj6AhPC##A%T@$2M_Yhh7D}nwvC#a8j{H* zT5FQYa(+F2hT-8X2`>pi!b>tVG|1k)duU3hd2Z7ScwTZO2!g96P(GgrST=K6*vtF^X3C$Lfo@uTN?BY&>zTE}s$ zl(N9hXLhoT5khPiLUhOD@n1a8`vLeGhy$uIJ?HoX^A}jaPBu5D=bYunb1H$|!1q$h zUxX0dLWu1**Cqx33q>X-TgLX2mjD0&C3HntbYx+4WjbSWWnpw>05UK#GA%GSEip7y zF)%taH99pmD=;uRFfj6w)H09&001R)MObuXVRU6WZEs|0W_bWIFfuYNFgGnRG*mG# hIx{soH8v|SFgh?WgUoC!ks&7v002ovPDHLkV1gzRwq*bS delta 1229 zcmV;;1Ty>l3xNudB$LhsB!2{>Nkl43gNecDOQSqBnp#b);1c^_O%rV?9TBh#vNNTXHO$6r zFw5^bfBETjoHD)1mwW%aobR4j!9)PL6!Xj!2nRPx?r zGKoYYF+m8%nFCkF*o$*ir_kTLiQRr&A zCAc2TF%cK08(Umh!0O6MR?;n%qQGp1X?>;i(+XV9ch_C6YoVsmp-oPQMGBwExy)Y@W4N)Mfq-}~jOh<9x06I%L+ zNP|yQbtYmi1AkUBUmzF^ik`>g37V6MWhS))>L^b62I1I$2*G(RllIZ{yUn*TFWSiv zp;TgfIw|FA`&*;YK&R7DN)qsT{o-7WMnK1I0d=*nKt`7u9~nf?xbOae2d&vsBM~cz z*qI(?tyYWN+*~ol>>>Xw^dz84Yd&62E}&_*9(r>TCVxh*I9ejZF8dz*FmMYW`mE?( zRv;|M#D6p6|BRuyxHzM|CwaV|^T%r4DlJMnD6}ygd37INg9o*_OHdWsFg3k|3t>2!MhIWbGQ^FyQ6J*;QyeLgSx`}(|qPi-dR{{$ESMJ6U&h7nb?00000NkvXXu0mjfYN}8F diff --git a/toxygen/smileys/starwars/deathstar.png b/toxygen/smileys/starwars/deathstar.png index 383e73004672589f271e61e8b3ef13826972115f..3ee623c6d5c5176cbc3bd5c34e80af8f09baa5ff 100644 GIT binary patch literal 1412 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$Ysfq`jpfKP~PR#p~J&Hw-Z zD=I30Yzl$5w|8P2SXFg(Q)}y_=`-dmUAB1rhE+RuE#0=W zciN25&`^J0-`MD=-0bYy+S=0c@}knxDYNHHojZTZyajVsuU&WW@Z@<5!o$Pu?Cc!u z?OmLlWMyQ;#l##P9f5YIW@h$Go-$+MqQ054JEzUqc;tBBj9Ip}w)PGVdIknEGBW1o z=CZP~nORxMscFp}owFA&scmhm@9eJY?v0F$ba8R9x3^Et$k5c*&dbXS2@bAnY??M_ zPGNcFw1tZrd-^sUI?_LLR!B&Qhlhu=vvYJ@TxoT+vWkkIk56D=U_nt)XK!y-UVhJ% zscZM_U$lOckB^U=o12xDRaRbJb7yCIR#s?mP+VMGTSsSpVNqF4ZDU8*vdvppZr=$E zQeXgjd3iZFI2aij85$aznwr|#*toj5`1|^#rKT1X7FN~NG<9?|w0BHfxOm0Z?Yg?U z*4EYr1_oZ9p1!_5u`$tEnVHGSDQTIR9ld>Xmn~09O7ivfb#ijDw6yf}@bLHZEh;ET zOG`@!##46AW1O&Lcx&{RW0b?^ZHa0aaEh{@aJv|*5*Cr+=Ha0e9 zW@a|l))8T0A;Cd~1qB^lUCGJG?(Xiv!NFQuT3((W-ku)ep`n=>8D-_=z{Fs0XXoSP z6%`R48xvz;VPR%wYG-SknwlCN9SuyGhfDSp0Mn>WNswPKkY-S6Jp8#%k!SLspPbT{ zPM(?8+fs4h?ej0Ue*SXqx^$LJ?!sA7p4r`U)nV&(mjRVCCV9KNFr{(VqyRbWC7!;n z?9bTwxK#PI-)`Ikly3BNaSV~ToDB3#LP|<;apJQ?6_&(U0?EyV!GP&6_zt?)F3@ISt^W3I^ZR@C2|?cvmbUYG8yB3CnHjY2-;0gC$EVM; zsC>2Mr(~ohTiV0T)#vA)@3P3Z=ywa%Y7aAJAF`{erE_mdoo{5)-u zP(V*$6&4 zy0iSx7#UiwHT|6(+b9N%AJr1qh?11Vl2ohYqEsNoU}Ruqq-$WVYiJT;XkcY*YGrDs zZD3$!VDM?;l?f;sa`RI%(<*UmXg-sp1Joc1vLQG>t)x7$D3!r6B|j-u!8128JvAsb XF{QHbWGb*|WMJ@g^>bP0l+XkKgbqvw delta 1028 zcmV+f1pE7h3$X~083+ad0044OHq4V#1t)(4(@8`@RCwBKl*?-yR}{v-(MU5RX)L+2 z92u*vBiV_aI1gjtdeL+fQ}80R*=11zg_1&S?+T^RZK12SV6Yci1=@e0i_k@(q{XCl z-I&OfrUf@CgkV{+toLg)BlSC%sF=_Thk4!eJLfy+%+*#_Rxntbo15y&cT>FFq+Nfd z&CzUHo%V?KJ?(4SQ`x(*vGJe3A14+e;`BM~b~GB*!$U(bf~6PvrB*Iq z`TLLGKjoe~w2w7SYl&~a--qu6D@l9MX}8z>K24X8%|-+5W)m%5J-3TsIE2@xrts#q zca6ylm)5xdJ`+g3oLP|eU7=98=(K;^a3VvD1PsSws8y>dm&-_>okpkKL0>DwV#cZ2 zSs0e}#^c|9{g%MD=-(Q^z9<%p7Y`2);nOrUn92Pge}dyUI8UePbLWuVeTeDvv#=u( zRF98QEEF(v=`y;GliA;U^cfi+iOdx3!+bukboU9%b2Age=~N2)`}@#%-*bOGC0F

0Iz}N1H6+Fo&>2{ioVh$_kaI&|%i`?!`;|3W@N3tvlWrR8N`N%VVVZ2Y{c==5xlO8 z0Qco{ITQ|Z3T#@Yz^47JRx2iuKp=o#uctEkWot{-PXYu@lbbErp&%^Y=MQrThr_V! zFnawS%B*iJ9v4{5&%!tM_V&WV%pk%}32cb1uTYN27e^IHde1B*K#hMj(~SVqQ)g5y zTXp$4aO|I$nD{f1NMtx;Y9s_8vV)v_*LBs5d7cNGP?0(sABD?jjhX-RRwC+Sh9PSBjbO1E=Krw1o<3^M)CaFv#xF1xAmo^rLs^dZ9xDhB?>^AA~lmh zJq9vZE0?9{a9q?d^E?0NFW1-C@910X+`_`b5A}Ne9m-7zNR|LZnrs&6pyQIclpz?j z7|=tz-QMN<_jY!6JY5`l=hf2}~eTPmwCC%hPbf^LkLnlWFdTLZM}z zgEjf44~~y)Hd~pTocxA>Q?Xd=0!KoZF%V#1$ukr0Dy!50Qvv`0D$NK z0Cg|`0P0`>06Lfe02gqax=}m;000JJOGiWi{{a60|De66lK=n&&PhZ;R9M5+m|JKR zR~*KFXXfnAZj#MqHzv`nhIp-)MU7RdA(pmS3n>UKC1UYG6n~-6LTRCop$&ynihXQL zp;%h`&{BM8i&mtCTC_EiXjmE(lTB=l=Dx{hFPWX$nbU`OiKw8}4diCpFGdQqF>`8Q$SP(W9y)1;XvN%%$U;Q!A{Fp-*P(7a@ zODymj^!)gf6=_jMzToA#rAwc%W!MQE7>qmV>7efQfYj;#rV$4N&EvyKj}fka8r_~j z2oQRb(eHOK(bt1F7{;C+rDSOZWvgoe;!i%`9QYmX;QwwmbI|S$->=kfC#Hg;G{j(A zH-CYZb%ZwW1R&9Uk!xpvqwl_CDbD@S@CR{Vh_-a>C9<kfd_5n~*Q^L%R#dKa@MW*^?f&+iEVs$xQ z-!z!h2%vO%CB0|57~S30;L(2l#rM_c7k{20vjfC{KV_v#zq&$AzW*>?W)9L8q^#Mi znY;hEYZtfEy|b92Y&XkURdr%wT6T^!8uXWX+s+Wg{#0(;0SQM;Cg_<2b<62?4np-tbb16 zvcmBb=3ozcYK<--9P#75WUViVtp}uW+vO^mYqjD zUZy+b%0h@XU|0g}ap8Lhc<`!v>%sa%m;<5lQDkq&?$XfIkt>Dkg8u$i(!&#InocU6 zVKT6uXS}VLIf-A|3@3b;?UylpK7XuimK&LX^~c1Tr!#j*gXB6mCY7qQt3L6uIoYxa1_D?$Fi|zvZNBzOpJ|NIXm|$uzxRkG@Qsc zILyOg#w|sg#WH43a&|GhbE^V|nf;ud2ELw;fB3oj56HR~C17(ZO8@`>C3HntbYx+4 zWjbSWWnpw>05UK#GA%GSEip7yF)%taH99pmD=;uRFfj6w)Q|uG03~!qSaf7zbY(hi zZ)9m^c>ppnGBPbNH!U$VR1z^TIx{soH8v|SFgh?WgUoC!ks&7v002ovPDHLkV1is! BoPz)W delta 1292 zcmV+n1@rpc3%?4GB$KWMB!2|qNklEZ9*HUHXF!g4u?DE+%xyP=icw$JIpjqtba!teur%%b)4i$ zl6@p)BpH%FNiL9l6L{YYVI2!AHh2PUf&7r~*k4Z!S% z1y8_TqFI%{Y1ZVYCu?hgXDe(1M>YnO*40>Xp5BPyn;az^ zm`J3S9Q_VwPWN>z#rZl*qg!^HRpM>C{Enk!{{{0Us zF}_qZw!hNQu*M<%y8ES>gf6GNyhzy-c!a+0rpYha2Y(x#*0aC!5%5}$#>m?7(ujTA zf%xP&M6VxODh5wOH7Z+du1l#ZknL40JiTpMP?G@oE1WVkXi-kmQn3W6IApn+xyWZ>D8hjv4iqw(9pP z(^n5*>|i;{@?+S-Oql8LN}@4T%tvPsR)TP4l7B#6B_IBdLoNp`pT$Jz1}8Lb?LNUG z-Mn6Spgt`xCdcLTn>Qa8qOA112EIwh>IVvgI8nER(xOwg4U33NR}Y>mXl234U@M~| zPP6WUdt?IT)!tj3P03i8iegakAsY{ZH=59R<5xI2cDwz;-(gJXyFfAERI>R|qp}z5 z!GHR62f3*d*;vQth;_UjOfcu0q*rIve?QFM-@Nk8#~@4$9jWjwp1NMT%j*jDL5{@1 zaXd0|77Om(cvS48ED3N48X`$2l)-C|oKC3uJZ@&)>TmHKXEc2(xn(YLI?1HqgZ63p zxLG+Mk=bRxzi5O)tZ&`w&bU40`=t^Wk$;&97io^ayT_`M$6_J@C7b(;s!yc9^v@Oh zR>u`HI03M(rH`kRsgLKwQB}*){asd2$mU_@3%0hF!D4J4k(pVQfS)A)_Vob1EgM= zc>nhe`o(5UE3N z0oYFlz?2C9?E(NkdoMR`js##@VRC#*OwadVYHA9WFDD)!LJiVwW1UBkS5?@X8pGXG zeRUk#TZ}$0!wk2up-V_lp`n^>l(OJjilueucyl_c-j2UMkLxa>?R=XQ1@}G)e5&{;62$$<9h3Gm(?_Y)Q1}GP{;q|Vo&0c?lKfb zB@#(&Y^>RAhFiAd-RVf~?!)WhI?XhsTYuEZnBu zhAKDUgAy2q@z?j!=1Alzx2M>}rb$NR;f@i-PM8Ja6`~*@m#px-`plZRO?~cs#jufJ&~#P zv$myM%`b5rx4*52q<*?H-a{w6tTiN9xfUI~gjp1-&E!cM&Hc#(mh`(_@8K;OIR$QRZ`)jFbhM>`dSyoS#Z9 zq^9P3y1B7ftkW}B`qY=)tdLgR^ziZtW*$GaV{5{f(_KUBYG>H!S#l?>@qokaCl*WX zU*#_>k<%%pZ)e2?27bLbBcjMXki9a8$8z)Bhy+}8Z;%3r|9ikC|x zbGoknn*XjPW@*4)8|Cn!%F7o@<)s&vEc5nGo(%$e^P?DaCQhtig(zm5h|d!VXxaM& z1b`57NJtPlJctsSLLt-EhS9>Pfn+j`Ooo2xJ^dfT?mYf3Vd4K1e(cCyLl73epRgrQ oC@SFX695K3FH7Ld+0Dxla0NVm;Wupp;u`~i6O+iSiOwwf7eH67jsO4v delta 1022 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 diff --git a/toxygen/smileys/starwars/jango.png b/toxygen/smileys/starwars/jango.png index 5275e7d516f6ce0e9e7bd57fab0f9d79b578ac78..dc731be4a9413156291a58adeea76a2615c675fe 100644 GIT binary patch literal 1286 zcmeAS@N?(olHy`uVBq!ia0vp^!ayv-!3-oLy1%&rDVB6cUq=Rpjs4tz5?O(Kg=CK) zUj~LMH3o);76yi2K%s^g3=E|P3=FRl7#OT(FffQ0%-I!a!@$5~72p%%x@5_c|Ns93 zl>r&VgXzrJ^y*EF#LC6Y9OQ+Cxhw#@Eiy?pRXTzou;B>c$xx zAjUSq7!Ypw?DKxJu0YsFj~=~u@7~w1Ux6+;a^%SN?b~nMxbgJq(+?j${QUWI+qP|+ zH*Y?B_Uz4@H=CN8^7He9f`Y=s!;OuN4GauKMMZ&**VWYx2??pLuHLk1(}4pAo;-Q- z=+UF~>(}e(=u}Nwnc2TJank;rc_)(>TuEMjH+R#sh;?tGHh+v-|0a6QmDIki#eFL| z!&mUdtrbt-Diu9LIc>gX<~)t)X1%0N?Z_$>j|AP=2KA&V;%S?i#kCokSa`w~@rKS7 z3YsYDQ7P$JEM^fRqUX%7sK>~}EN0{(ZXGG*Q!WwIAsjN9H*ArHf0=AppJeJf#iD&W z^~cS-FFDP)?Y;1!=iECoiAxki+RP)W3=$^jRqS-1e#LdldHv$`#xbpq$t`Z_?Y2b= zou*uM>O1DpbGdm=#euYwpr@2M-=xy7j0<<;o@7j&;shk=VSXZTj-9TeqIMe%~a2&Z!%ZY9=mQ zwru&9EnD_pdaR$;xAokk#Y>l;I(2Hvv4>hwbvkj)vkpDjy?b}#mfLE9h3X+?73**B z+OZr-UD*jOFVsD*`KlVajEiazumY8D4px+ z;us=vIXQuWF-Yd;g5#F?E+s3V%%L__2Y~Q?f<2IqRqz3|pMP(a{ zN{g?5FZjHrXyZqrhY12-zkU4rnV*A)i;t6s)fj-IZ*&fac+51+1n@4imI zPLcEu58q-J#+5I51SMxi-n86V`LnODlC{Q`PN zp5ESG-?LXubsaWdoAqkjw_|ReZF~wZ-n@Fp*!A_>*XD{K9$__~IVpyfr$QFo*z(di z?c6L|$61Nz?o@u()e+ZW>C%l!$uzA0C?naX>OGzD=(P0=U27vaMWVNPO>@lqDz)Io ztx|`LrLP;4vdvByYiMZg%-eEji=@W3)YVVj!c|>U6Mn2*u`2iWK4y(=#n%X8O-xS>N=;0uEIgSCESwk^JYD@<);T3K0RS?zJ+A-& delta 1024 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 Y0EY$ky}m25fdBvi07*qoM6N<$f(#p{^Z)<= diff --git a/toxygen/smileys/starwars/jedi.png b/toxygen/smileys/starwars/jedi.png index f8ff638d111b04cd28b7a1ab998bb11bca2938d8..6bc31736f0d8453e0d565ceb91f2cd56c2ea985e 100644 GIT binary patch delta 1854 zcmV-E2f_Hy4e$<-B!2{FK}|sb0I`n?{9y$E001CkNK#Dz0D2|>0Dy!50Qvv`0D$NK z0Cg|`0P0`>06Lfe02gqax=}m;000JJOGiWi{{a60|De66lK=n)Vo5|nR7l62mVInh zXC22s&pGGbb9-Of+xLzZkO7vh+=4Q|ft~AQ$efdms4&FL1%G3NxUjilG)p$aKV*r< zjbz9)YzCEtxu9_ygUuIU*g*@F(L!4aEmELw+_&C)&OPtPKW^KA{>L}@{q;S0KIeR& z=l460;2D1wa37EdE_S{&mgY9Oo{#Bxn6^j3cG0b=a?nke7Y;UYzCp|bz6U%4EC!;J z>zxBW2L6!P;eQYEKN5A@oNvDUbmqVZU7`G2ZQ(t-B@P08oiB~)w(DU!9)|5vuj&b(s*zXg7p*x?tZ;XLCn|G`Ra z@22I2rU^svyAzR1@0}051{?u~jyyYL>$Zz--UNNqu&t9axvx;oq2o$AISQn748B3T^YcFX(E=RfRW>#r}SAIJg^V1IG6} z)#put*PdHc0-UN{x;hb-#&P1y5q6ef}JM;`6oU+^3eN?kx@3PhL%`6$M*Lzu#SzdGOw0 zQ@1>tZ@syzJn@ysODk2(+1qjIH{n*Vf&2hct`iZh#5YE9hr2Kq?#8WNgIm3tVCJ6$ z<25t%(V>#s)?a)iIxmmn`(V2s%U`Vv01d#SEr0WJ&p+05Yk@2*-tVp)Z5ycE<$B`1 z19dB$`;C_0G{uY78)wm;FSn;)Zb!(?>Y5XGsWBVBR_(Ka= zAph{$7qJ|Vg6*;4fsD6qaVqo9nev`%DfQ@yD`jJb?Jrz9d!%}9)vd2;rqZ%3rY~Q# zq<_3I6wvTu4vvXoHHHi*GKznzWvsx^Y>j#hn+9ay|nOHWbN0p_iTe;0!ai> z6oo^F4zYgydTiSUeDHO^N0G%Ho+9}mAb)aCEx;N{GhOngOU`u26>M^b!^C85Uv)ME z98c?^pB?R=^WDF6HytAq?`~j!)-)S0=T}wFbq`1h5QwS;v-ZQDc_6hxxEImsbSia(d43MX&UZv~1^3$$vPG z>$9=JV@+cj;A8c?hL`#)a(N}bW#VQ8p_P9`1j@kGG1{I+if9m2>3{ccmXb4^>1j0u zE?6#xQv~;=L5@XeZMkd(Vv#<-`|b2f$0`5PjEDL2t%umvk>S9}Yiyfuvp~rr$7X_^ zK?n~-7VTf_P-nE_Cfh-z5OOJwHh%$-QgVikHaS2FmP^6*Fl?6#BQ~f(lvo%g79|i3 zBZpLkmmjc`?MqbmIvXP$tZG`tZ#paZ{{9PWedGyNCd!GLo%n4pqK^$DDJt^$cIp-{ zKrM^mTLxA-1JI{T6pC>dY!}1wFdUDbaR(7DL5W5P#3Cq>FtVylblvoGTO*Ez+owGIONUZ zY1eI+o}`Bqi4cgF5-cwzP#Q&66@1Ub(KUd*$ty#aXPEeA0bjq4pFPZcc|YlNn%vC` zgeOi>b^k0d2EZ5~KBt0EWq%d=XbM+1NMF5a0sED_S=`_5VH1Ku_dB#{X<(SX4XxHVNt@rnTJh}wirqH__^UbM!YQhjA(U~CVK&1 zPweooDZ1ryam+@P6k}Fm5dx7gvKjyZu4Urn^H|v|Rwj*=PJ_w|*?-Yn%OqG*R$Va- zXy61jqMac_BHKGav?^YFHQgXRG{z^j&r{;1(z8nb@k(Oy$HLv{e1oV54#whT%i@(4 z2qC6D(=ZsjIZ8NEf)Ik-ghoeT9ha1qQ;#&g`t#>5Dt7}0Xuj^>INu;1&SkPYbD8Xx z($dmkC{%)?Cppn sGBPbNH!U$VR536*Gc`IjHY+eNIxsMU%xo-?Atwp|07*qoM6N<$g4S(oUjP6A delta 1695 zcmV;Q24MN{4$lpcB$Gl1B!32RNkl9o@sb@~tEj82sqDYk=luw@48&`wnpwJjixkN`mv z$O{shKpvayJNNG0`|R)BWHZwKXV1(xckcO}bHDTZevflGJZUchaDOXdmT;;2SFt3% zGEB=x-ms9@E#!0)idKLDC2!)n!*#e&&8?uNj}h)6tR@uAfA2iuQ^GsZU)v-2ufbLB zG#-BY@zkLYdpxsmb@=uv8h4oH2j~!rZdwkp%80caiDe?b|4fvT>?UlFG;J1qRZ+N0 zmVtM9VCgEf_z0A-FMp9Z`>sZ7enfh z*9+tUde)Z(tS#Ht%G=t%sYqQ{MZ9r5@Y$sa_g=Df5T^G%I)7jlh&P^Z@Y3Aril+5Z zA3qHv)C^8f!zFRxt6J%+g2QV~b;67f!3;zpWP6}#HpaV7oF!8$Q$Xl`Z-@5g=l<&? zch|`)(&Sx~E6OMl8fnbS* z{L)=8s)6b)^>C;D0e7f;kuo*rt!R7c6R!L6 z6l|N+q4+kxTtD%E8SXRRhpT2i%t`Vzv=LV2#sxD!{Rv#L1AiF)^dpu7wZe~|ejb{^GTFlB z+f!Ei>O|_@v*EtcgmmoWmExGH+pC(EPL?e%ow--ei>+${N^?VFxW?n6SO#tfA87*5 z^K%eA;F71n3o5MSRbc5h@Sf5IJLe98|MvlKp=u!)yFSUYOlFD1Vwq)74eZ_Aue|ce z#q)WC|9{P~q4FQR^~K5qqe-9f?d5~3j{7%$FMZQaz#Rq8xgm-ojvP6HO`A4B*L8A; zWV99MDGJ;THj%ekmcf5h1ruTe&q!w-VguQn?vQ8Zv1{GA6fHWDR6M^pHni+Vf9b6| z4!;}E^e@JN+)tsJHAL=$X>(9j6_RLD+^>KWZGUi*1b((3rfJUE*t^_M^B%a0Saeat z9X>TOS3*SYC_pIGG*gAudEc~&j>~J^AtAZ!M8k{yldZe(r+5g4X=6*Zg$*^uj;C7R zYp@c3g%GBM)ABG=96T+51m_~VsX*;`9K2s9)g%nP_cu*so))@QftW!x=72!2vyNus;v~gSqL>DJgd&p42f-tOvu20&cxR(z zUc(lD7uMCS!*9DIc;eti?6~J4v_!)w%71sm?sy(bY=V+df^eY|m8)wZ6$eNW>U>ES zO$>;c+??F3Z>v_bIaHLAY5)!C5hJA?5v$FStida48g^5nGh9e$Q%X9?$cPY39WTYnT6 zA`J!M4hP{17CFSWWx-HnwrPL-%9v)UdD!_JCH6Jg>5s8eIEZ92iR}1A_%f$adK=wW z>ac^*vIsmSrBJ346nYiOtK+QB4~mSKdD}aYr$lf&@pTns_L9sz2g@*MlMF4B!B2`$ zjOzPk)TBvSYs}6Knc&Kr)JjhKx1)PWPzX>CsEFIVoc?c z?jJ@`X=v_f%-gXTKCgHdUMrDY;{E%J(fNx7UuV!A&Th5Cfl%?9P)UTcn}1ud<*BY? z<5TeYz0Q0#BcsdJjzO`du%zzgUp{k5#Md14*X-+bfV;D)^lrlTU@++Rc)U)>a|4Uk3mH diff --git a/toxygen/smileys/starwars/jedi2.png b/toxygen/smileys/starwars/jedi2.png index 3550eb8e62af2036fc55570f34a7416b56b9e8fa..3f3a39de0d7ae415bb839646a134cc9ef25866c1 100644 GIT binary patch delta 1765 zcmV0Dy!50Qvv`0D$NK z0Cg|`0P0`>06Lfe02gqax=}m;000JJOGiWi{{a60|De66lK=n)3Q0skR7l62mTPPj z*A>Tq_s;Co>({OyG$l+bz`8nNKXIR(dbY=^kBkR7&Ib7pviL6pr-0c# zszqyEO=8Qjff#VQedEq_T+R|SZASQcLJ@)N*b2e|Ku`ChvR6>`X{^EpJkKzB=EON* zYr4;rt$%~x-{hSQZeI7%XU28$bVe1DO~zWjhPq`jBOlF)AQ@ns%d(VPrs5T?4P95K zf7hJ;`tF8Qgi7~M2|AuaweEt2QDos0L?VT8r%^X9;I4fc)w&DSx|4A37~!V2drH%p zSo`)D{$V}+y=B}VN}04w6wj;tNC|!78kLJh)PKrO9c^Ex|+VwH0{&s|H^ zyXw%Fx9ygyY@xsM98yZ8l$eIK$dOmDk{#%o3j}SCBbs}wx*Z2QL5v}s(a(BkKK$jM zttX%B##JTCs={jG`d;F>OO!6B&5)&`{wO2Z!2 zQrhjO&Ig#^j9BHz{~gZ1=14zSRgSK`)Zjhrt!PFh1k%#jw$0(ghuODpAHMGc222Ss z*p|3Mv!ww6bzf=+*d;v`pp2=yt}dgXmVd+!H<<%Yl!W^EP`LEunb7P%jn@2O@NJG& zJ|l<>V~1=s3fFas*a{X#5Vk=?BFKdyl&Wcw2(mbYh(rk`LIA78>nAtVhR5g2NEuMZ z)UGv|+#sVJo#D8zmmZiZ3>7!r$MK0zF=!rK{sY!`zW@fjp_kCvzhgD0FkS_f6MsY> z_!A=32&#m8>KSBR5yBC!{Pk^5y!z3G#c6eC0vfFinfxG9f)oO237!}*b1U}lXf{nz z`U}_iWOkgEx>mNe+)vB=BvM4Hl^6(ec~m7zGl^4Wj?` zD@rpnNIQb{X+KLg^bl#R$9OKDlLNSHD}8HLn_Bgn@!a5TV7Umkqy+pU#T&D|;b<6B zc8SkRwkO(I+5T0g%HJjHLHy+Nw6}KC@r`Ey$Xz$)L+# z)k_s(b0e(CAEZ1tiWCM@xPNhnTb5y}xPpL>h0lZb;)VQfBl|%@!s8w8-naOG5buokx6y}SZ2|dQJ{lKk9)_?r^7eBlx?}`i@ z*wuL**jC8p4`xQjyxh!9-1#CwsX|!Tw70ck35nxIIqE;6PZw6>I1Y~Ej604q1|W`{;a001R) zMObuXVRU6WV{&C-bWmjgGB7eSEigANF*H;$Fgi0eIyE*cFfckWF!GVqkN^MxC3Hnt zbYx+4WjbwdWNBu305UK#GA%GSEip7yF)%taH99pmD=;uRFffD6Y%Gx>Ckg-nNkvXX Hu0mjfucS+e delta 1599 zcmV-F2Eh544)6?+B$Mg}B!31HNklpe+R{P-%;8p$lbq z%ig-(-PxHrXZ&Y&DWGq7ny>%N`Df?*?`PNphZ-6I%pi;t{@UnkNq^$1Wm*O_GY?%F zkP?uV1!36|5cF@mD~`8zou5jNK2ErcFpp4Pdf#!vUc#2vx2z%jYq7@;H?LmnY5cZd zjEx^N;-3=5R@(0)BXYVdAWy`m6b`AJAT1iPaDTT=@(y8RaQ?%JzrGdj>Sl147t+w6 z$A^#$cO!B1JDr~SoPY2t8IhkLC(jqQsnuW1RfDrM2pl!y{e2?+0qJ%V!s}VBG2tzd z7Nv#T4|La)erMI(Rjt96Hb5-{D{sJmmXU?b3?bEj42kaDowT!#47hNayn^JMGNwLq zt6Ke3i&EhkHdE*_)BEBuJ|W# z$}E)Ko=hrB3YCj4>+{C}y_$em6@9319_n6Rwb&8Dw5 zWV)9b%vv%{H5N{tvS_+TH6hCvfCuM;b6$k%1xx+`u4s_Svp~~R;NF@EJ#z=Z&us<^ zG${Glg$U1@s`i%R~po^3c#_#39DAF4}*q%P4k4IWic-Uq-`3(VsIAMb?_ zC7per?WKJ$T;+ClF`aEWG_N!~uC$SGuC3CwM1MHdgTeX+x8IP>nE7H0|S!bvc*W47lBRa`wX<>l~O7*3sXUSJb*&Y;kRs?d#T z4C;|fZ619R7WwMX+Ejj$WH08_Jc(M@{qW^_Ab)!sxmb*f&;#W}CnnEpfv2*9 zB0`fdX=f9Cs*smwwN>(75^>K}#C(F-mVY#|c4aS)wz<7-Sa}WpF^;9>AxsP1f=K>p z#Hoq=hu%P_wh8t3JmIuh>U0-Ix(=do(HtsL1=+}`y}gTzQpk%%RwTALl&idT(GOID ziz=Je7x96xF0}#;5*^k!wN*5O%19Q;WD=Q?9x|u{H8bhF(gqy_nuG9G*B}>7P=D$* zB+ri6ef|wMiBLEYI6u~NCCo&ss!EaAmTFP%TUIsX7;q*Gnb(WR72rz@U~0M@`2VNXfD7=d8wNebVQj(>6?t~sMr=yn^&{bPyZE#|O zHJ}r9>^Q<2N4j?qYT#ZEHoKazUUv zNXD}XU4i;KHWG!u%;)T9#9sR-58L7^xnH81uX_sNdTOs6IY!vyEUf>~7M|973-#2QOqGBnHW$win< xh}i8^`jYwTo%l=jCEfOKK7KD@|Lz|F1^}=GMyBod(ait=002ovPDHLkV1hb}4wnD` diff --git a/toxygen/smileys/starwars/leia.png b/toxygen/smileys/starwars/leia.png index 2e407295bb286524ed34e73d6246316cff098335..885087d497893357a10a1b9c5690b250cee1060f 100644 GIT binary patch literal 1278 zcmeAS@N?(olHy`uVBq!ia0vp^qChOn!3-olCT!aYq*&4&eH|GXHuiJ>Nn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4Fdy{bAV5X>;M1%?X_iq zsH;3K($yHkCXzH)lhBbDGEp#og} z(h2#+aW3a}be-7Laddt2+3g)?w|Ac1(F1050F|wrRvGPK3bYhxj*X`D{NBO@Un^Tp zX=@EBOLa*LHHl83U))WB4s$ox2z51FJ}GBnU5t~FlBueAagyJjH51S8>N~%?|M;f1 z+ALoyEoldR#fkNqd)G`lwR7g>{WC5fn00C2)C;>O&g;$qI@nS}vOFzl>*CIh3)+`V zsXV=N*7ak{t{+`;bj##L6N`>-?h17=FjNu+`e<@v=BYi)x2&9V`Q)y<=eFHCwei}S z-CI`9x_xH-)TU$;6)~XSEz~7fd{_K1m!cyndpi+dNI-#s^_yXC^EgQvFlTWd<2 zsfz0=2>aXUtef3<;o!(2`Vql{wl^*JNbnEN~ zm-ausb^hwHt#{6BPVlubRu(f=ktm4uymM~nqpQbG?OuNQ$fo-j_uf9e(c4_Dt32`I z!4*fh%-*xA|JJEZR}U}Pyr9NbOJ>vjmJ>VXUOTe5I^8!d$mYoAsk>)2b-CO2`Z^w6 z(RFZ5>&YE6&+VIkY<1_^Efdb|oN#8(ob$UUp5NI6Og|~2m7t_(T@vIM%)sw4F+*|A zw=*_cPl_a7rEOF(m>Ko<^!KL~oS*-QhGgdWeG^Lixqg4+x)16HKH4*vem@{|t3hbt z`+GTpr-Cm7jbTjkc6VXuV3qX%a@b2eeO=j~vGZ}M@@v1{xCbchYO;5oBsa6iirkV z%W2DwpV>5@*Tm6FGx6M=b5BlGeiqk>*U^DSmXUe5MrY~f!pm3AMsLgOzPWC7JBM5?^I=)*^0$3= zlUt;Y98;_Q&idBemAj11xR~2pTY{Zox^7B|oA%4ez@Szwag8WRNi0dVN-jzTQVd20 zMn<{@=DLO^A%+H4#->)LX4(b@Rt5&2CSI9a>QWZRN6Vp?JQWH}u3s0s33nc~yPgg&ebxsLQ0LU5-sQ>@~ delta 977 zcmV;?11|jj3H}F=83+ad004hZMvIfp1Sfw3ph-kQRCwBqlv`{QM-+zt-Sv9CcAOYH zR_vJI00{|Eidrr*1wj!A30lNOyoIL<5qLyMs1GTKMy=FR(IU}@mUmR1P*e~NstQ3w zf}+MI+`=7)lAt)?2;<9oy_cSugwzx;SUw)_%sJnjGw0tCY}-bq&#Lx@8X7+SY{P%* z^@;e6q0!N?Gkw?pc~~jN@ebG_vJNse8jWCZaOmaQ01dkd-v6dp@h!kM8g5V#+b!Ex zP0L2Uph1>p&@@DE{5#(# z(_bjFph1Bp0#y5I&=3t{R-_tBV=aGBJu07P>*gf*FoB!O0YZ7BXz@eJAi)Bdty~eq z`cKy&P;HW@09LoAX*k_`5r6i@VH4aTiV#KND4p-lSD;Hiy5I-_{nO+%~-W^5%*^&D2W0` zFdl=><{cA+sRI!ZstfYJ?iqg#IMCIFo}S;~@zui{Xu#QXf6?4N*GkrffWwrVItruE|2!&smZ5#eloX^rBJfi4R=E zy?e?3vVAcD(q@c&ziByoZ&*|K-< zE_l4v@Kkva2-H%EBF}%20Zl{W8y)}kB>MXk&nYZMu-lbh(`}@V^QTVsLQy1Yj)i1m z5GT40;4MQ5DE; z8AhpuW5-UN|9jxp=Wn`Og-Vxn;>`DcUAHbd+_lY;oukH>yg5DjeBrBQo4-8pwm9Kk zH-EhMJ3qPoNSfLR+nAuK9~Q3r;-^Y6{|Ybw_x88~O{l&?00000NkvXXu0mjfI~d|c diff --git a/toxygen/smileys/starwars/mad.png b/toxygen/smileys/starwars/mad.png index 61097834fedc51a7b173f75719ec27cd76d28744..0380c2eb98878764287af4d4bfb41a08e2a9b0a7 100644 GIT binary patch literal 1342 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 QAtwp|07*qoM6N<$f~!=Dp#T5? delta 1706 zcmV;b237fp584foB$HYPB!32cNklr}C6Kvgls%EzQiAh9yhq6xuNR1m7P3RJX7ke`e{0|G^&tLUcvLkLF4sx~cg z>1Wc!aq1+t6W?q5`hMTbdrd1P2o(o7((7~X@j2&x&inD4mvLPe8-HyVFJAPz&MQ-Y zej#7D`DDFO-7}ZJK0tE?hLK8Upy@IUqYj@hh=ACMa%B-KE7jFVB!1$^;Zr~T*E8Ay%B~rI zndDa_*#~V9|JZYgx_`@}-#suqxD6bgkk8*jAzy^58mQM~YJ|0xyR#)Z(toFtYtZ;dByWbisnu$_bLZ)k2&UW`7`o!b=e}DhHH-E>!{y=ctj?brBgZhW2 zQgh4CbZ7b^UExjpR;AJr*TEdaF;|`UgCmhr@1P`=CjtS63j}z`avk&e3OeF`1VcO| zNy9>+K^8M02rO<~tHLyOghC9$!3cr1#JTzDr|S*r^x1RceUCr3zx3gScTt#Le5D~Z zPa3*;R8{oD4S%`m4@CnPT9*6Lp|KZi%l;n2vL9P5$>6gNqK^p}-nt8_sh}`B)5Hyb zldT1U$z(FfaTImQ)hLpmeHE} zHJ{)c)ir~YSL<+SjX&T=v?BzLXA2z9o#H*$(O^hd4S#E4W|4QN3GoLEN>A00*&D;? z_NRk|O}Bz8H!9F5=R}4lWhFGp;mx{&{OuZ+?$*%%a0>l{U8pSAaCLG4sZ0df-Z)hK zE`hQMr~}8fQLWbc1EK)l$B)h5E<#$Vue(&=aMp<@qYt&*y7O4av^(uP$8qdImSg>O zNokp)F@L9HWwL=4YXM#TX$%dFq7b|ZjsnZ7*r=^I5cmkC6x~?lDH$v@8Yb$phD>)F z@pux7qM)gGmiEKY?Up4S+l8j;n9k*))YpYH2oy3sNsVEcFVg4Rt!!mK)yy$0Gu)Z( zI8T3;?k_g$Hs;S(ap%lDCa?Yp1Km5(w`UaGQ-5Kweg^K6jgu$dfu)DBT2T-Pg>n45 zKSQuC0?Rgj{KNP0;)^d6y|5^eMVE?IXatrFKw!;vz+#E8ylHFZHUZm3pYN=r5(2m? z9MJc6Z%!Sgg|E_7t`$3!enL|K`;my5KU~Mvx%22r4I=)TG^CGLP?Vk!V!(;slmsLeCm-dkyrZhcH{Z0bx;qagBkgx&+3L?Sqd}BBY=)%b2`$t!0DO zUeh8s8(1z%SeRM5dyleyzU#?H)PJX+e}53qjGf#(LEVJ)E{b$`w;1qIya4(w8yPc> ziatl~NFcU7j@iU*G%grO@Li}GbI_$ii~CsmVL2YHy2yuRBIU1)-ENAoR&GZ2W$VbaX*fjx*vkLqIk8s3?nr!0!UVqcH z-AG6175X$Te{dcDGdSI_Psp~E*a?dWX_T=;XA-8h5&reS?V8N$UAK_X!R z0|RmV{Ynw}nF{e+9U*!xu6-JzG5TO|N@fts;+NHitg9AJv5=RLU~NoPeRhwC!c= z{~g>tfTszdx>{AiiU{%T>t4Ykyv|!geMk z_8+I=YwhS`H(JIJK(DIURcmOcmzL^Ovo)y*Fz9u#h|e6<>L#j^j&fPS{7gymi#|C< z@1vj=xUAQ z2ZO<_d!P7LBA+k3Rw|VaA0It>bmBjq{|^BM0N9HAraC;5s{jB107*qoM6N<$g76ee AO#lD@ diff --git a/toxygen/smileys/starwars/r2d2.png b/toxygen/smileys/starwars/r2d2.png index e114b3f92d01ba9b2efede3ec02215d5c6eda5a8..f0f0f9dfdc4f409bfd73c7ddc80171459bd60454 100644 GIT binary patch literal 1064 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 JPDHLkV1ka3?VA7q diff --git a/toxygen/smileys/starwars/samjackson.png b/toxygen/smileys/starwars/samjackson.png index d6f80246b73c6d406ff275513c6af21a2cb96610..3c109d98d32bafbc34ce11273aa6f64d467e18c5 100644 GIT binary patch delta 1538 zcmV+d2L1V<3%3lAB!2{FK}|sb0I`n?{9y$E001CkNK#Dz0D2|>0Dy!50Qvv`0D$NK z0Cg|`0P0`>06Lfe02gqax=}m;000JJOGiWi{{a60|De66lK=n(DM>^@R7l62mRn4d zXBfwS-`B6D2g;#PO7R4!=<4tQLlK7&r_PDYgv^n}9h}ZZ6Mv)G%#1TTUCf!7n9W`6 z!o)?hWOFl_%NCi5;yf%&QSg8wBDR1OTA&mPJ@@Ok3&d00f5}@I+uYu&J7fJTfIpurnxCIy zv7$2=NitiA$}-X5$o)xX3zAlcq}6NsUCzy|SL^2<`!%<+ywdLc52yrKfyMsj)-404 zYYP;gkK`9-J0x>j*QjzGaKSn`FTb_%GCA{?5E$@~sDBnHTDlTZr2^pExl^=Wzs9&Z z#W*jYCnYt7p}wAap!Bg95C9WU6n6K%*I#q4GSp%hk_%_Xk`~PUNiwAz0xpdz-#gJZ zE5EPXNyYB($t_s|!0B&3+fCorUC5FGGuc;hP4C*&0N-F18k zY$Fc7w03AyH?|#QOH?T5ay6Z-KKw4^ykt7L8 zk|^D{gSMt?c>BHFs=q+-+N~s8vXCSRW4eWsbsypn1OPUlt9fM_kO!=fbUA*p@7@1_ z|9W#`#)>6vSsT`D)#grm7x)e62ISG2tWTd}Q-Aezwvj!r3`u&>22zR%OI|*1XFL7f zolGiR@JK$lxQvr~c91)(RFkGH{UYY=ofvAnS<>};9j4;K;nWxB?N=G}hk+)*|9C@) zz;g6SL}D_E97B>Ms;jH1s;VLyjiM-V0--Q*ML~o|av}ob#xKB@zaFbxSmkf5Eg8B! z5P#3!_|B2^m&;bG4EnEtOHTriD$j|;6@&yGT{jw#BnerT357xka+IFiO=QejfZC9R ztLZYbkc1KgomPjxE5O{T+ZjIFjoKgxstj9$b>Y;nfzyBsi2dLE3A+l?6wR~(0DLW1 zd1KE3rY%|lK=tmAs6T!b!-Ofs0z;_%K7TeH`5RqQ3caTs-1+VbaaksDriBh?wLEe1 z$N7K*2#sO(5q1@rJlf1jCP5Sl5BnH$-Np7&1;f2=T=r&+mT`!pgjSo1Q9Yii6Dp8A zI{cme7;~-oOm^Du{)MagjC)x{W9C?(4?nv@RAMHTDgh}u8LeJNN8K6JYBf5QNPm2I zkQr-sVxC!w>PCpcpBqUWZzQ2egWYqKzLq9}Lp?;J{^q9vkHU{B;ZUJkrw2vB-{&TC zdOqgd=>WJ}uG4z@FoxnqP-MUu%%-RP1n#!W1Ot62ii{lc0~~)^xCk5!4tOFOl?c%= zqRzmuN{6Gp9Y=dRfdnH%ZOwEyHGkmsdU3e^z}5KsTfX~PHpFc6~n~CeiYm05UK# zGA%GSEip7yF)%taH99pmD=;uRFfj6w)Q|uG03~!qSaf7zbY(hiZ)9m^c>ppnGBPbN oH!U$VR536*Gc`IjHY+eNIxsMU%xo-?Atwp|07*qoM6N<$f&*Q~d;kCd delta 1389 zcmV-z1(N!=44?~;B$EjSB!2}$Nkl+8Z@qZk#fXF36#5Lj! z@!d=7tTs&kkQ1{B717IkNSqbfM{G0IujCh%l|xfl1ffWTL_7|!(*gUPUYIYOjZ>RL z#BLhI_!EeVi85mKSnJgt5p!q0wrTAq!F>&iiQk{9T3V^n=^>HHq0s7(NTrY%zt=x| zFAz%zsmSNFZ|}R_zJK`C&xUn00Q+M^+5YwH7Yujz?HE1Zx+oI}K>5r9n?S2^Ok^%0 zuIOi0RQ7dtploRaBBNg93VEnnu?7sE&-HZU(mC{X-9YieYS=q2LzSmO(Bp2WP~Agc zfJd+$SH)Z-@Az6St&8?rc}ml~q@s554+6F77{&gc$oxQwaescLXV{L$eMexZY2Y57 z|N0YLJ=+XpO+5l$H>^Fk@cOYckZB4qPJ=tXbuIFzmzg|wZJ$%~UH47h+)N_YE8`aOk~oS9fl~>W2QJEgN={ z@Y@vog&1aqC+1{xez}qF9_mNwl6tP=epn~RM@pjow`v@}wn1HO3gtb!PV+>7g@w8ePD>p-*GBt%eTWf;1N7JQS-#U^1Lj8IY z{*s;BM+r}4UKUShz{`=qansC+r>MfC(I|MS1l+eRC|Fnvp-cg%r2{FR0+}RLC{=yf z5y9eFyMGb7I81RKk6&Ok>zB>?iY*tyNhJSo{ZX}RGNRdwxSK%lb-cF!FlH}b&D}Te z`vC2yPeL}m9LY!!!f*iFPW%apLWPlYHr)C48qze-+<7_-+M81)Ge4}P*I4t3K8fS7 zOU)}ToT2713}T@Gg3h}zKHrGYhzm|@H{`lvFn^2yVsRei!fBW_y%7R0Wj*AB+@ME5 zZN#(9Wv^ z|7H|pKXxK_njAS*B3QjA;pw$tJm{tx2zO7`YgzGAnOM{$lt@9kjC52R!tEbbG6=w_hC>cgvL|Gyc10#_k z#FxM}Fu)PXkt5jOjbV!!e!m|!=WlQgT|_G8fryMx#PARu^Rr(6{Zuk%3*>Ob@7*TU zli!T`k?}bnEDTz1e)}L~J>;aezrc=@&wt!o?>-mn88}GxIye<^_Sh9RHNMKzm1>f= z%#hJjVUCy!M8hG3X*v{^76?)?3jAj>UY8R=pSO)JKbpe!dQW`pm5;S_(jc0IXu&GD zuAPQft0iNhjJ;lzl$JrpkRxWZO{ddvIqW3vVujaG*6}He(_A{U7HC8s2^kwQIby@t zc}gLJ3gcWzrBcq(Vzjje0%LI6?X1{A;?pPG&mX(axr?sqjY>Mb%+|oumyZr+|72EV v|2zt_j@6U>lQ~Lod-&dV?^Lz%j{pMzMW-5P=iNgO00000NkvXXu0mjfJ{hTo diff --git a/toxygen/smileys/starwars/shocked.png b/toxygen/smileys/starwars/shocked.png index fa3747d7f254f88196ff415de7b5022f19631778..2770ab364676e5605a68fbf7b887c45feba6148c 100644 GIT binary patch literal 1280 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 VgUoC!ks&7v002ovPDHLkV1gfGfT{oh delta 1323 zcmV+`1=RZR3*HKlB$L7gB!2|}Nkl*f+?jSLM@@C zh=uNpwmUn!J2Sg8&q{_SjtA;i^LIGRBYp_Cua4KmTfz9}1Dq3}3D`w&A_sT< ze7Iy5ax@f}8b1Feo_{~_Bsr|7ASV7rR3e&)$NYx6$*<*NmJMVj-wAp*b!+AUqQ9|Y zd0Y?$m~sm^E04@sfAZaGK-P3yr+KJYvbPHuu#{{-yJIY3XrR8p{GT@{-H>K}($)DU{m z$scU?tjsvu)_+F(x+$b9J&kJe8LnJnliu(^#v5Ko!W+qcjJV+lJDr@FzZh890$WXi z4R?TL*niS9*bZytAXq^F{7b+K?&&*P(+0LYjPRm%IeX>GJAA3oUZX1u8Oy?i6c|?| z`2+GgM2r&J-3j3+t?<^(p(hqRYa*A+p|i6SeSLith<_8HMd;*Mo@T484!xsp2$aJJ z|C(Prr#QJ#aLp6>UgyP~wOn|)H$LhFp`d7{z%2fm?pIC8&KGB5O&xD`Mu>xT|1rA_lhr* z^H`9yW1SeXLnPP@F4zkF>R|{zN?uE>#Ls!X!1XUsI(`VnT|c3moP_JS z#gNv_gP~=i{*@o6Bp97Z$#a%{)R>xz3tl%^w||6UdKk$QhhQ-hEl3V>Y%YY3%`n*p zxQ~AgZpY^kRz3j#b~^vE1=Gwzo5)c5+Cwuew-xcD`9wM%h}FV0N*F&gi0IPQ@Xc?o zIHdcJLm&DX!4)3?uXez*I02?ngJr8!_-<|~^>_Y`%v9XT_D|D8qo%FtP{xw*FNnj} zKz~O{yIdjCObIx+ADTJ|-Oypy+O#4nEKP&08z?C%vSUe=#_5^O%$=L+tISBETN9h$ zwGyy%v1K{vwWyd}S02?BB5wRiTUxW4ab>xTLL1!zFCjxz2kmk`Tbz|V% zy&$-cqkhq-R4O%3&LcxuPm$q8p1%EamEyVT-Ai3(_(FXE`t%EZ-{D0}RI^YE#rP%< zc@(XgtkO)?!u_22`wYY2KE3>^PN#$V5252paEHHcepIJ=4Y!gN_bA%Oe8XS_EH6Y5 zq_(y;EG$f|Rzv*i7Hp>Jv-2tsFmLwXtT7=Jt;XMD2Md@C>J9=T%^|@mNB*MBy=zcmIWI6 z{0##EsB8}+r9lG$OU*ut+uIC%w6vJ5H?m!qIw`ttYSpnrftt+2j?u}N5ufJLp%WG zyKE|5UEb(4J~UijRB%?hxZr%n)UG&JmDy{Fb>fb5LxXYa8@&I(0y7aOGQ@1&Y_o-% zwdZ0(7gpwRf?2U!pC%mxIQL}xc>j2NO17yfo6RmZH}jBmUA$9r=OT&fVI6!`5*-p3 z`I!ls+-%HY9cSddq<5TKF}-w&EpwOM-N0ZHS-x48T(0Lyr$Yg`T(eLQ7q)5k?i7cF zgaqmt>u4YR;^M%_C|>>N!ggRHX7#x+cagmg7qIZIq+N7(%I-iH;>Iwy+UhH$%QZDA zf@I->V*Z_jK$KEy#m*QHv9w9)>mFIL^LHuZq(SYUwJ>5TUbdVcuIlC$h;QY}XWh6G@1qTw zlPbTH&%KX~dE?;6mx`DVQlau+Uoo#Pmf+Vd)Ki6^(^w;&>F>=QJ7PZ7dh@8`C;1H0 z>%XkGF+^Hv{%Ot$BTigUU)NUIR_EnItHjX;L_=K>S?95JyNN>fiv)=xAw4NmhyfrG zNF;9p)tg9;B@*bqesn*ICxJjG5cEGz6#t8mmM%z5&iQY`H^ZmAFv6!FCd8yCi_Row k3IR)yeq4y>q$Q>Z`NBj&PR5WBdqMzkLZX82?>%<$AJS-Lq5uE@ delta 979 zcmV;^11$XR34jQY83+ad0044OHq4W~1Sfw3qDe$SRCwBqR9$FWWf*?G^OK~>S<;-O zO}igP#NkB4R%+`-tB!(FR1k*J7VXtQD0ndx1}Z9wunU>!%@A)?iWl=j@S>f6#fzx} z#o2DKb)_S^Y5#MQCOyf?IXUO}zQ;B;(}5p(Nb>$X@6Y$XpSb)Q{_EUL7$*!7juL+~ z!WLnc@C{*-kY~f(TYl3?xs8OKP7@~dzEL^4FNaVn3rUT@b1cm+A(bz=z>Pd7Q8R%V~`{8Xe ze~B~85}r@rdmlWcAkqU+6eWOn@C3ZVA~_b}Z(M=9|5?awoqF#`_U#*fN+AYIOVf1& zLh&KOVMtr^|LFL?IZyHkR84Ox+GXv$1PatVjfoIWo z6z*@67=8NjKo<>@IR6pJV_<(ex&q%fT(hjpAr*2og8HQwp=6H1x2fsYHOL8Suv4R$ zRFPbP_5CQ6jsds@8ZlvzERKd*W-^0EBkydMbSbeXm^zWvsM#0kw*-HQ!uw9WIj>TL z%JFJ&i|65~G88pLK`q$D605Is&!)DP*5KFc0bV|{fXS&<>WkBq?S@^maqjC4yz<_S zrp#PfXRY^b?B;*jL?%6>q0VFqk2FWb?(y~A;rO11 zl2JsZw8=zIGSTht89yV($lx?#A|8*2B9XSg_fVFDVQn-TbTL0Ql*?syexL9L5!LSW zII`j-;azeVCp^lNP2gs(@bz!Mv>$H=Z#jPnFaXc}kq+|*V>AE&002ovPDHLkV1hHT B<>CMU diff --git a/toxygen/smileys/starwars/stormtrooper.png b/toxygen/smileys/starwars/stormtrooper.png index 5014c1e48d8793dd98aa030dac767a8a2705c7c1..a4a4cfe58abde3e35e3611d51b98607e5a76b319 100644 GIT binary patch literal 1225 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 K5%u92MSla>6}klg delta 1056 zcmV+*1mF9z3d{(Q83+ad0044OHq4U&1t)(4?ny*JRCwBKRBdQmRTzHmXFl6}+N4cv zS6KaEhDB;uVV#0bSGONS9dj{ zZnSH365DPzU7Dn6a&vEUbMN&%hbDIFArB|_yzl$GALo6KaP@Wk-`Y($L3oUiCis5| zHNq_6JHmNFjwzr;(Z$f*Iq{8^c(0IdSoBjOZe&`{e%~y_wRv?2!!Z<$g<2~_nm}Yyg`O#IP2G7C7*}n>EbfU9f5uK33PvSbuzy% zQFQ%`>}2%85y{b3fnd5##Ni6il?;H%9PKv5(4BHIJOpS3gj4%e;u&LPqu%Z=R#_^b z#g(R}rZ6@(2GcZIvh=I-U6zD9WU@#y0q}LlSahR2O~orhP$i(cz{xDjf?*hN$rg3j z9}r{*g3AT5w)l^XeQS$!?uLKjR~Xs7#3F0$45R=R z*wo1*K zRP7>~+7dSG0Tew45C{YijYcs*y3J-2v$F@$QT~#VWf|F|QFR8r>h7!M`_)?4odijT7s$#l*AcR3rElwrn>vP2*l!1PpGs zyG_7~zrMcCA=R%VWc+`K@(&+2NHieUk7U^Nf4rG_7pv=as*QlYzCNVWX(W?LPQ=ZzE_EC}8jHnx7y(-><#M^qBdAm=yq<|f z0zs;2YpvmF)U>m|(%;wGvq{nE^2*oG6^q3gRxjE)k#A!Lot1wX#LOjF@yDqIqbQhr z;PH4MiX!yiCkl4$)**_j!JnW^Ce!vLn48B0eeG>I_jw|uU6DOhYd(i`DXdvpdRuB7 zX3rr@i9fU;b?$mGanI1Dw*5}B*78n1+`IkKKEXPh5GUl>^>4qlpHMdZ4I3UFZrlAW azyJ}+mPuN1L_v`xN&+#JL?IzYKx2v`RR~GY7AfdNBQDehTT!uA zqj9WC7YP;^Mk0$+RDu$5SezgfZCzLbX<{HG@7=sWq4fE)Kl-nC&iTGM%XjX~oin$1 zLo#oU*8(p9z#M*JypUkucRtINz=X59zX@{N!Aar(Q1k8VDX}{d2TKx#NdO!O1K>yz z0Fy-O$e#f0r2t^g1b}%Nfcg1X8#l55a1n13}$9#2>jozBj2?>&y5&s&}JV&AvG!iKJ}<{w_#@RUN83ib{*29LLtE1EZVA>HYa8jZnO|ZJD-9L& z@y9|(+j>l23?VEpFHcNNv{)=?%T`>UhBd}O(^F{q1G}@*`Y;LVMR->V_9$awV&cY) z8*DZk9XFx%3}^2asExy4m7^$%58Q?iSlClxcOl97Yy;NBm^3y|Pfz1Lr(nw`cqbpb zw{FTgHa&^LW+C3r!}ZCqZH=>whwI|dIvU=w$DtF$`!UW=E^KCEOdbX+Pa+TT2C3_RsA@7zJN`Qv+~ z=|nFnOTddK&A3FqCIU}6KPka;#M9er(OmU#6Q3w&@Z&k6fM=3SKnlzDPjyfAr|tS* zQ#rE*OLKCN^i>Bvc{z)B%n#vu>|4H>p-?D%Qlj#Lf=Ayz?(7P-1kDX_bJK?WC}u~n z77B|v!4b?b`tsEw)bLpM#4>f+Y1&DdG+Smgj115B^B*?&tCW|DS131&M5&){?(0eI z>1Bu*^yr5C6({TJ>lE^Rd-2*j=zuAgpVRqQIhl54_$GDo_p2o%Ri@j}RmOr33t6<6yt9Gnim68*e97PG zBpofPy7`h{urY0WevB(<9#^cL@XozRy#6A2!WOwyESE7Qdt?Lw8ihuSq%b0>D@9Za zb44^Wn!c1mVNxje?|RPtOOTr<{XDzizXiuTb}b_WAHBO_Q(m@QA>Jbc9BE#bESR4w b-XRmp#L|M@Ph`Yb1pq%GIlg*L=COYOn|o|@ delta 990 zcmV<410npB3X2Gk83+ad0044OHq4Xb1Sfw3tw}^dRCwBKR9$EsRTTd2{Ox9Tvztt^ zNt@P|fTFD~tt=bCgc<~EsDdCu5~NQBQSd=p@Wmfc1RunQz7$c!O8o)fd}vfk@IfU| zurwB8t4%g!W77TK&1SMUGdpuVce-77YcBh7ch1ju&pG#=!~S>&d#wivrwLCJjuU^P zgl)og!WF_LLPi=U-xqBs?LHFe^daFjD?Ou{2a+%bMxp3o_%4q+rOd=mc5$No`*&}V z&8LJn35|QstRu1mgdh5kJ(si|NzpM8u;{`6$L!kh>lsx4x`y)NPghCuB1QL)R4FZ! z9!WHuKAG$bZ@{VM;ci}sSi217ZGnIJb^x*YGn%))hFh;A7UdX8pBN**?{B=zdOFkm zxIg(+G7?w8_q_+N?};nkw@WU0Bmp^nx+Nt51)Zwx=oH1h~f=+2VaKTGZg65@BsV=PNRQ!C??N+ zouV6+WM>8+pHPLp3G^Lr69ovx=}I1g)CoHerN3LRSZTm6z)Fr8#4|0)rp*0Ava(b_ zFEE;$o5S?meEZGY3il|W~e}YDIoJa51ey^G>=}_ zGMFa7LLn&IH~-VgZ~G?6!_a@Dx+Hs;SeT_MO)#B~;G`@sV0LyE^Yio2*1v=jr3`#d zy9sag5@udH73jR)$_^&h2665en^u$=1WHB`DElF0EPb1^js+JP(4PKo#&`ZT$!2Ewg1KefwKLrIGekRI$i1-bnwFW_Q%W!>@lF2t_W29r)iq<1v&3Hp}1aNM??x5H6c z-Ec_!jneA=&r5%2;WvMrHZHtsB7R?6MhIV>jZ*KqZM)KPR;tcZf3DqYr*pMjag3N# z@q`r!6yM)-HYtMA<9Bt{dmt&rog;Cd zQlp5-u6mc~d5X&EB$G>yk;!_sc0RkDacadPj!re9QtXGQ_zFs0ay%pg;)_LWztC7!sm{N1&*!QxhP7EtD#>lu{AV+Ll61 zfht3-uTtV51IR-nQUOs41q8xNAv`1(gcu0Py*KwJq@~xN{n4NO=InlZ&Yo{}=IojC zT|^jfqxCjx0Ki6m2uDaT_4Qi!2C)bK(D64xYZ3zmfdJgSwB9UUOZX0PAwmHFU+x1S z=K=sGB9t=^z-Kf7)FJ?wB>>ocdbKGs004{7h|uU@#Tl@=x(YY%ArAkCLA6JhdcQ`d z%MG(tP)&@gGSr~@)d2lwm?}jSnNTHf_HG0=Q;K%S>*Xx%lVnZp0aXPHdr@F`{+0gz zK{YhGu&|(>9n(EH^s;h4KADY8oKedK%bl_4vkZJH2YnK*9lo}>xTt+5Lmu(xsseCj zn!Z;wlnsW?l4fqYp+g@bP5!#Mr#b>^CDKTTDj3Uu2=TFmrB?pvH`cG3f(*l=`C2Bb z_z-D&7hUL+$z&lRAsUSa)r=!@y0%?_4n*Ul@9R4FNN>2hj)AtZunAdNSs9znhG7_c zQHQn!AbmnbHVGe$z3O(xUI%8J=+HbS?NhidpO4JU%;@#{75zBUMu+7dFN!zA zjr%lR5~ST9zUMMsOjQw)dM91iF`Z@-!!RR(`9^)M(@YU{s=!89%Ugkzb`7X(|*SyHU`+Lq5Y z^NH(dJ|{5RelSi1NC{hF7@3Ti&naXwm&=u|bGB>Dy;WFSc+1+x&XbdW>BM`2og3)% zhBY`lw%I(!w7qdhR-Ne3xqdU%o1P2es8K#?>ko^$WXdOQA8-F)e^yqOCC}N}Inizx zg}mvAUxtlMxM!;0?h`B)Yc+=*Np8C_E$y#a{>mek+ zr&4oO8DMqMY+>)>{`Nbr9&XjsV%zGT&86e9b4_OVNXmGIM%j?nmq6OemD*FQ-TNlb ztc+831U!CgErqquR$5Fgl3uYbQpLqud$TVKBrm70*A mAwDeuf$^!a5(j>YI8h>$h~qO)bxDXx1OPucj8hdP%KZoAhk~O3 delta 1000 zcmVMG zUb<}RiXeuqbQ>S67FN0}%OWCT3bOhjD1vW-FIojJ_~Igc5mCg#q7Nd16szKvRS;Pg zZ?!CL_m)~UNvG*#l1b7@CNtytrcI1)ao|UilkfcJ+`c3HI*YxYBg7fvI59?qi5h?L z3-L8^j>z$Y=~vvYlk^`E?(_!nOl)FC3?4{BiKoD1KOD;px3+SVjX#zrwI4rzfzG^3 zJWp(QoQ0mq4iH~NkDgA)@5$0XY|oHk)!`U9=vRM&w*1v4vOG-D{l)+4cm%jm{MbY3 zMCL&V?luIcehY5l0@|w|!ZHj*!X|(APu!m&zt2`)5(2$qXQSzS zh}w~)f5mJy%X{ADRO@dz^$ub zsWC9Ar)RMV>gid;(iuftxs>IRoeb_91XUGnV}srt#L(n%j6V7VlxRQJ7v^C%HtCoP zNffZSxQJ{v3(K+~F&Ya22<;o<(kJB6#0BL=NxwK3O;jd+i)% z9yx`jr6mM?luYgevWMXLQ_>P#SF`KYn5f8b?dDFoMBgprMI|J=0rR^VNQnvR>_swA zbHpgx^(sH2@icQ*MUNriz;qKdV-Do@HT>UW=^J37LvX2Xj#)*k+~9xaRW9~W zb5JCqE5ZxSMLI&Ns&<9kMh@1EH{rGz%4_)+>3qaeQ=|O$zpt%sB2fDoQT;71m1c9N ze!8xsR4REw{3#R)$mf6aXf8iavsc5`^*lGv(fwTSOyF~sjZ%if93QYRK<3-#w%1W< zNJu7=-PDT3B8HU9uvTZ`RCMUSmr+%>_+@|5Ym#w()KklSdy{XCPx}V?GqF?*EKVGJ z8Z0qQH((fU^#`v;?X?9sH5cW)21BoKq0a>2NA%F{rk&=KO$-x7-u~T9J5ROtc3Aj<8(qJ@1sDKe W1dWU=(zj^<00000Dy!50Qvv`0D$NK z0Cg|`0P0`>06Lfe02gqax=}m;000JJOGiWi{{a60|De66lK=n)*hxe|R9M61n0stf z*B!?{*Vp&@jY~*~*Cdos>O3e*2}q$GEDDqYO$HqzFbb@dDu1+AWlXt?nSg7A{6s+)!Kjpe1lD;b&T ze+JrsJzIbNtA8Y*LfN|S`+;u&X{{}7tD=$U8bMW68(_=IdO!r$34)-LBze)Z zYo1e0CgWL2lBRZDY>nLwb8E|9zTw`Mt>t$GMC!d6)<`5$+v~YB>7BRU7NXI}c2!kH z;1!pv$qKjyRaLJew{rDTvG&uG7fdG8yX3$ zD(sn=CjjJd@2J73JL&vpBoz9a#bPED3^ku|Hr@}{ulyTe0F*4A|GFrOr6!|6F9^a> zpbGc^aNUl9jEpn@0%2i7V&G{&MOB^iLM#$%^Y-{|=yOl96|JnDRG6Q8rH>1!Fv@Bt zKx=FJ?SFEZ4$K4QD2g&Y5DfLlVzD=N@7%ZP3t=_hJ&)w42+Pe09b2EY9AP>)a z1dGM8&?u{p5x_`G_$^L&cLE21kAd2ujjs(=&3_2U9s^zj<^tKkwgl~iKm+g#U;$tl zrvI89?`@~%Qgj$M0E6&1IuiT#d` z0M-oid^4~J_%+Z3>`qMDTfijXF<{fpKn#_31Te0ZAJ~)l{SdGLbAZ*r?-R371voyf z`+v-T#?`W9T7LfQ&zoELdf{xc>~@<$L_&k&*o;)nR`XRel1SghcA?kn!=5#n z^z?X`)z#LcC`!rBUHiYQD9WxG`46Tg-dhMvPR#rspd9GA%`x3EjG^izzEZX&uCo%e z^y-wH$um5cd@(^lm34aIP}LC{UFSf6qJQ}dC|Nj%nP4Eu#7UWGi2XG^&GsAM@F;t}YcPhY1@P!(8Bb3SdYzt(3HRae>B;uGn;Dy9 z=V(n0f<|Lk6$PW&gumrHosI5^=8SZD-JcVUMUf?mNI2F3T)3NNM;k^G@mO9Ejm8#+ z!(r8I9hbUw^HwhR^<%TyI8k?kqJOVWnWzFmjewvL(CcJlXmF6eP#6S(mQ$y7O~+~w zH5z5gk_8c`e9%oz<>aj$?jjy00hI#g4Emw>&%S>PGsd&OensrLs7 zAdY*IZBLJ}SjfrArutY7O^r<)uc_ssnFV;1XwZ4;Gdvy--5uR{dU|o5ZonIhD)t%q zoI35KyQ>>#!&#V2xFox6e(x-2@_m>@Se_@|HIGX@achdP}Zcg^Q zMw6b7iyaO79e;oD1{lO+EBMFv-bn%G`Fy@v)rYGWNjeEhk_NKundUos`v~-(Ji@yg zTg-qr6bflYjh2~@6znjWO-BI7w^uwB2ExLNFMh8;QPejbj(-n#0~$dPwiyg25jZiF z-2b|+`M5JZ5b(!Ckg-nNkvXX Hu0mjf$XTJS delta 1786 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 From 9008bcdb7f29fb98564881f57c5f81d9d014fb49 Mon Sep 17 00:00:00 2001 From: emdee Date: Tue, 27 Sep 2022 13:17:48 +0000 Subject: [PATCH 152/217] update docs --- docs/contact.md | 5 +++-- docs/contributing.md | 7 +++++-- docs/install.md | 36 ++++-------------------------------- docs/plugin_api.md | 2 +- 4 files changed, 13 insertions(+), 37 deletions(-) diff --git a/docs/contact.md b/docs/contact.md index 9f80595..5eb2fa6 100644 --- a/docs/contact.md +++ b/docs/contact.md @@ -1,5 +1,6 @@ # Contact us: -1) Using GitHub - open issue +1) https://git.plastiras.org/emdee/toxygen/issues -2) Use Toxygen Tox Group (NGC) - ID: 59D68B2709E81A679CF91416CB0E3692851C6CFCABEFF98B7131E3805A6D75FA +2) Use Toxygen Tox Group (NGC) - +ID: 59D68B2709E81A679CF91416CB0E3692851C6CFCABEFF98B7131E3805A6D75FA diff --git a/docs/contributing.md b/docs/contributing.md index 93292aa..b2cebf4 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -7,12 +7,15 @@ Help us find all bugs in Toxygen! Please provide following info: - Toxygen executable info - python executable (.py), precompiled binary, from package etc. - Steps to reproduce the bug -Want to see new feature in Toxygen? [Ask for it!](https://github.com/toxygen-project/toxygen/issues) +Want to see new feature in Toxygen? +[Ask for it!](https://git.plastiras.org/emdee/toxygen/issues) # Pull requests Developer? Feel free to open pull request. Our dev team is small so we glad to get help. -Don't know what to do? Improve UI, fix [issues](https://github.com/toxygen-project/toxygen/issues) or implement features from our TODO list. +Don't know what to do? Improve UI, fix +[issues](https://git.plastiras.org/emdee/toxygen/issues) +or implement features from our TODO list. You can find our TODO's in code, issues list and [here](/README.md). Also you can implement [plugins](/docs/plugins.md) for Toxygen. Note that we have a lot of branches for different purposes. Master branch is for stable versions (releases) only, so I recommend to open PR's to develop branch. Development of next Toxygen version usually goes there. Other branches used for implementing different tasks such as file transfers improvements or audio calls implementation etc. diff --git a/docs/install.md b/docs/install.md index 00f8188..b3c5457 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,33 +1,15 @@ # How to install Toxygen -## Use precompiled binary (recommended for users): -[Check our releases page](https://github.com/toxygen-project/toxygen/releases) - -## Using pip3 - -### Windows - -``pip install toxygen`` - -Run app using ``toxygen`` command. - ### Linux -1. Install [toxcore](https://github.com/irungentoo/toxcore/blob/master/INSTALL.md) with toxav support in your system (install in /usr/lib/) +1. Install [c-toxcore](https://github.com/TokTok/c-toxcore/) 2. Install PortAudio: ``sudo apt-get install portaudio19-dev`` 3. For 32-bit Linux install PyQt5: ``sudo apt-get install python3-pyqt5`` 4. Install [OpenCV](http://docs.opencv.org/trunk/d7/d9f/tutorial_linux_install.html) or via ``sudo pip3 install opencv-python`` -5. Install toxygen: -``sudo pip3 install toxygen`` +5. Install [toxygen](https://git.plastiras.org/emdee/toxygen/) 6. Run toxygen using ``toxygen`` command. -## Packages - -Arch Linux: [AUR](https://aur.archlinux.org/packages/toxygen-git/) - -Debian/Ubuntu: [tox.chat](https://tox.chat/download.html#gnulinux) - ## From source code (recommended for developers) ### Windows @@ -44,27 +26,17 @@ Note: 32-bit Python isn't supported due to bug with videocalls. It is strictly r 8. Download latest libtox.dll build, download latest libsodium.a build, put it into \toxygen\libs\ 9. Run \toxygen\main.py. -Optional: install toxygen using setup.py: ``python setup.py install`` - -[libtox.dll for 32-bit Python](https://build.tox.chat/view/libtoxcore/job/libtoxcore_build_windows_x86_shared_release/lastSuccessfulBuild/artifact/libtoxcore_build_windows_x86_shared_release.zip) - -[libtox.dll for 64-bit Python](https://build.tox.chat/view/libtoxcore/job/libtoxcore_build_windows_x86-64_shared_release/lastSuccessfulBuild/artifact/libtoxcore_build_windows_x86-64_shared_release.zip) - -[libsodium.a for 32-bit Python](https://build.tox.chat/view/libsodium/job/libsodium_build_windows_x86_static_release/lastSuccessfulBuild/artifact/libsodium_build_windows_x86_static_release.zip) - -[libsodium.a for 64-bit Python](https://build.tox.chat/view/libsodium/job/libsodium_build_windows_x86-64_static_release/lastSuccessfulBuild/artifact/libsodium_build_windows_x86-64_static_release.zip) - ### Linux 1. Install latest Python3: ``sudo apt-get install python3`` 2. Install PyQt5: ``sudo apt-get install python3-pyqt5`` or ``sudo pip3 install pyqt5`` -3. Install [toxcore](https://github.com/irungentoo/toxcore/blob/master/INSTALL.md) with toxav support in your system (install in /usr/lib/) +3. Install [toxcore](https://github.com/TokTok/c-toxcore) with toxav support) 4. Install PyAudio: ``sudo apt-get install portaudio19-dev`` and ``sudo apt-get install python3-pyaudio`` (or ``sudo pip3 install pyaudio``) 5. Install NumPy: ``sudo pip3 install numpy`` 6. Install [OpenCV](http://docs.opencv.org/trunk/d7/d9f/tutorial_linux_install.html) or via ``sudo pip3 install opencv-python`` -7. [Download toxygen](https://github.com/toxygen-project/toxygen/archive/master.zip) +7. [Download toxygen](https://git.plastiras.org/emdee/toxygen/) 8. Unpack archive 9. Run app: ``python3 main.py`` diff --git a/docs/plugin_api.md b/docs/plugin_api.md index d549e68..9eb30a4 100644 --- a/docs/plugin_api.md +++ b/docs/plugin_api.md @@ -1,6 +1,6 @@ # Plugins API -In Toxygen plugin is single python (supported Python 3.4 - 3.6) module (.py file) and directory with data associated with it. +In Toxygen plugin is single python module (.py file) and directory with data associated with it. Every module must contain one class derived from PluginSuperClass defined in [plugin_super_class.py](/src/plugins/plugin_super_class.py). Instance of this class will be created by PluginLoader class (defined in [plugin_support.py](/src/plugin_support.py) ). This class can enable/disable plugins and send data to it. Every plugin has its own full name and unique short name (1-5 symbols). Main app can get it using special methods. From cab3b4d9af83cb06609d0bcd75b1bd0c521ebcd5 Mon Sep 17 00:00:00 2001 From: emdee Date: Tue, 27 Sep 2022 13:32:53 +0000 Subject: [PATCH 153/217] update docs --- .gitignore | 3 ++- .travis.yml | 17 +++++++++-------- toxygen/smileys/smileys.py | 9 ++------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 78c26f5..0a8182a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ *.pyc *.pyo -*.ui toxygen/toxcore tests/tests tests/libs @@ -25,3 +24,5 @@ html Toxygen.egg-info *.tox .cache +*.db + diff --git a/.travis.yml b/.travis.yml index 7fc55ca..a4011e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,13 +12,13 @@ before_install: - sudo apt-get install -y checkinstall build-essential - sudo apt-get install portaudio19-dev - sudo apt-get install libsecret-1-dev - - sudo apt-get install libconfig-dev libvpx-dev check -qq install: - - pip3 install sip - - pip3 install pyaudio - - pip3 install pyqt5==5.14 - - pip3 install opencv-python + - pip install sip + - pip install pyqt5 + - pip install pyaudio + - pip install opencv-python + - pip install pydenticon before_script: # Opus - wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz @@ -38,15 +38,16 @@ before_script: - sudo ldconfig - cd .. # Toxcore - - git clone https://github.com/irungentoo/toxcore.git + - git clone https://github.com/ingvar1995/toxcore.git --branch=ngc_rebase - cd toxcore - - autoreconf -if - - ./configure + - mkdir _build && cd _build + - cmake .. - make -j$(nproc) - sudo make install - echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf - sudo ldconfig - cd .. + - cd .. script: - py.test tests/travis.py - py.test tests/tests.py diff --git a/toxygen/smileys/smileys.py b/toxygen/smileys/smileys.py index 43ae3fd..0391856 100644 --- a/toxygen/smileys/smileys.py +++ b/toxygen/smileys/smileys.py @@ -4,11 +4,6 @@ import os from collections import OrderedDict from PyQt5 import QtCore -# LOG=util.log -global LOG -import logging -LOG = logging.getLogger('app.'+__name__) -log = lambda x: LOG.info(x) class SmileyLoader: """ @@ -36,7 +31,7 @@ class SmileyLoader: self._smileys = json.loads(fl.read()) fl.seek(0) tmp = json.loads(fl.read(), object_pairs_hook=OrderedDict) - LOG.info('Smiley pack {} loaded'.format(pack_name)) + print('Smiley pack {} loaded'.format(pack_name)) keys, values, self._list = [], [], [] for key, value in tmp.items(): value = util.join_path(self.get_smileys_path(), value) @@ -47,7 +42,7 @@ class SmileyLoader: except Exception as ex: self._smileys = {} self._list = [] - LOG.error('Smiley pack {} was not loaded. Error: {}'.format(pack_name, str(ex))) + print('Smiley pack {} was not loaded. Error: {}'.format(pack_name, ex)) def get_smileys_path(self): return util.join_path(util.get_smileys_directory(), self._curr_pack) if self._curr_pack is not None else None From 675bf1b2b92254cfed4310f44e5873ddbe51c120 Mon Sep 17 00:00:00 2001 From: emdee Date: Tue, 27 Sep 2022 13:51:16 +0000 Subject: [PATCH 154/217] update big NGC --- toxygen/styles/light_style.qss | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 toxygen/styles/light_style.qss diff --git a/toxygen/styles/light_style.qss b/toxygen/styles/light_style.qss new file mode 100644 index 0000000..7e31b39 --- /dev/null +++ b/toxygen/styles/light_style.qss @@ -0,0 +1,19 @@ +.QWidget {font-family Helvetica;} +.QCheckBox { font-family Helvetica;} +.QComboBox { font-family Helvetica;} +.QGroupBox { font-family Helvetica;} +.QLabel {font-family Helvetica;} +.QLineEdit { font-family Helvetica;} +.QListWidget { font-family Helvetica;} +.QListWidgetItem { font-family Helvetica;} +.QMainWindow {font-family Helvetica;} +.QMenu {font-family Helvetica;} +.QMenuBar {font-family Helvetica;} +.QPlainText {font-family Courier; weight: 75;} +.QPlainTextEdit {font-family Courier;} +.QPushButton {font-family Helvetica;} +.QRadioButton { font-family Helvetica; } +.QText {font-family Courier; weight: 75; } +.QTextBrowser {font-family Courier; weight: 75; } +.QTextSingleLine {font-family Courier; weight: 75; } +.QToolBar { font-weight: bold; } From 870e3125ad016a91683d2b6bcbecf43b7a46fb34 Mon Sep 17 00:00:00 2001 From: emdee Date: Tue, 27 Sep 2022 13:51:50 +0000 Subject: [PATCH 155/217] update big NGC --- docs/ubuntu.png | Bin 111928 -> 109748 bytes docs/windows.png | Bin 82721 -> 72830 bytes setup.py | 11 +- toxygen/app.py | 790 +++++++++++++++--- toxygen/av/calls.py | 320 +++++-- toxygen/av/calls_manager.py | 80 +- toxygen/av/screen_sharing.py | 2 +- toxygen/bootstrap/bootstrap.py | 110 +-- toxygen/contacts/basecontact.py | 1 + toxygen/contacts/contact.py | 11 +- toxygen/contacts/contact_menu.py | 56 +- toxygen/contacts/contact_provider.py | 25 +- toxygen/contacts/contacts_manager.py | 35 +- toxygen/contacts/group_chat.py | 65 +- toxygen/contacts/profile.py | 5 + .../file_transfers/file_transfers_handler.py | 63 +- toxygen/groups/groups_service.py | 16 +- toxygen/history/database.py | 77 +- toxygen/history/history.py | 6 +- toxygen/images/accept.png | Bin 116823 -> 118982 bytes toxygen/images/accept_audio.png | Bin 13514 -> 6436 bytes toxygen/images/accept_video.png | Bin 13416 -> 12155 bytes toxygen/images/avatar.png | Bin 5848 -> 2254 bytes toxygen/images/busy.png | Bin 329 -> 433 bytes toxygen/images/busy_notification.png | Bin 609 -> 556 bytes toxygen/images/call.png | Bin 3623 -> 816 bytes toxygen/images/call_video.png | Bin 3625 -> 1204 bytes toxygen/images/decline.png | Bin 120648 -> 121873 bytes toxygen/images/decline_call.png | Bin 12132 -> 10434 bytes toxygen/images/file.png | Bin 3521 -> 1234 bytes toxygen/images/finish_call.png | Bin 3622 -> 816 bytes toxygen/images/finish_call_video.png | Bin 3112 -> 461 bytes toxygen/images/group.png | Bin 4142 -> 4375 bytes toxygen/images/icon.png | Bin 2595 -> 10872 bytes toxygen/images/icon_new_messages.png | Bin 3937 -> 911 bytes toxygen/images/idle.png | Bin 231 -> 400 bytes toxygen/images/idle_notification.png | Bin 405 -> 474 bytes toxygen/images/incoming_call.png | Bin 3622 -> 816 bytes toxygen/images/incoming_call_video.png | Bin 3112 -> 461 bytes toxygen/images/menu.png | Bin 3540 -> 1100 bytes toxygen/images/offline.png | Bin 159 -> 325 bytes toxygen/images/offline_notification.png | Bin 445 -> 489 bytes toxygen/images/online.png | Bin 201 -> 376 bytes toxygen/images/online_notification.png | Bin 351 -> 454 bytes toxygen/images/pause.png | Bin 306 -> 427 bytes toxygen/images/resume.png | Bin 2691 -> 1413 bytes toxygen/images/screenshot.png | Bin 481 -> 656 bytes toxygen/images/search.png | Bin 3773 -> 865 bytes toxygen/images/send.png | Bin 1129 -> 1520 bytes toxygen/images/smiley.png | Bin 5704 -> 2381 bytes toxygen/images/sticker.png | Bin 96248 -> 96514 bytes toxygen/images/typing.png | Bin 5777 -> 2574 bytes toxygen/main.py | 417 ++++++++- toxygen/messenger/messages.py | 34 +- toxygen/messenger/messenger.py | 18 +- toxygen/middleware/callbacks.py | 233 +++++- toxygen/middleware/threads.py | 162 ++-- toxygen/middleware/tox_factory.py | 108 ++- toxygen/network/tox_dns.py | 49 +- toxygen/notifications/sound.py | 19 +- toxygen/plugin_support/plugin_support.py | 70 +- toxygen/smileys/smileys.py | 9 +- toxygen/stickers/tox/black.png | Bin 2155 -> 1901 bytes toxygen/stickers/tox/red.png | Bin 3581 -> 3665 bytes toxygen/stickers/tox/tox_logo.png | Bin 31469 -> 16161 bytes toxygen/stickers/tox/tox_logo_1.png | Bin 27669 -> 27523 bytes toxygen/stickers/tox/white.png | Bin 4446 -> 4501 bytes toxygen/styles/rc/Hmovetoolbar.png | Bin 220 -> 322 bytes toxygen/styles/rc/Hsepartoolbar.png | Bin 172 -> 331 bytes toxygen/styles/rc/Vmovetoolbar.png | Bin 228 -> 361 bytes toxygen/styles/rc/Vsepartoolbar.png | Bin 187 -> 348 bytes toxygen/styles/rc/branch_closed-on.png | Bin 147 -> 276 bytes toxygen/styles/rc/branch_closed.png | Bin 160 -> 305 bytes toxygen/styles/rc/branch_open-on.png | Bin 150 -> 273 bytes toxygen/styles/rc/branch_open.png | Bin 166 -> 311 bytes toxygen/styles/rc/checkbox_checked.png | Bin 2965 -> 411 bytes .../styles/rc/checkbox_checked_disabled.png | Bin 2965 -> 411 bytes toxygen/styles/rc/checkbox_checked_focus.png | Bin 2965 -> 411 bytes toxygen/styles/rc/checkbox_indeterminate.png | Bin 493 -> 580 bytes .../rc/checkbox_indeterminate_disabled.png | Bin 492 -> 580 bytes .../rc/checkbox_indeterminate_focus.png | Bin 514 -> 580 bytes toxygen/styles/rc/checkbox_unchecked.png | Bin 2894 -> 358 bytes .../styles/rc/checkbox_unchecked_disabled.png | Bin 2894 -> 358 bytes .../styles/rc/checkbox_unchecked_focus.png | Bin 2894 -> 358 bytes toxygen/styles/rc/close-hover.png | Bin 598 -> 542 bytes toxygen/styles/rc/close-pressed.png | Bin 598 -> 542 bytes toxygen/styles/rc/close.png | Bin 586 -> 680 bytes toxygen/styles/rc/down_arrow.png | Bin 165 -> 310 bytes toxygen/styles/rc/down_arrow_disabled.png | Bin 166 -> 311 bytes toxygen/styles/rc/left_arrow.png | Bin 166 -> 311 bytes toxygen/styles/rc/left_arrow_disabled.png | Bin 166 -> 311 bytes toxygen/styles/rc/radio_checked.png | Bin 933 -> 930 bytes toxygen/styles/rc/radio_checked_disabled.png | Bin 933 -> 930 bytes toxygen/styles/rc/radio_checked_focus.png | Bin 933 -> 930 bytes toxygen/styles/rc/radio_unchecked.png | Bin 724 -> 786 bytes .../styles/rc/radio_unchecked_disabled.png | Bin 724 -> 786 bytes toxygen/styles/rc/radio_unchecked_focus.png | Bin 724 -> 786 bytes toxygen/styles/rc/right_arrow.png | Bin 160 -> 305 bytes toxygen/styles/rc/right_arrow_disabled.png | Bin 160 -> 305 bytes toxygen/styles/rc/sizegrip.png | Bin 129 -> 285 bytes toxygen/styles/rc/stylesheet-branch-end.png | Bin 224 -> 341 bytes toxygen/styles/rc/stylesheet-branch-more.png | Bin 182 -> 319 bytes toxygen/styles/rc/stylesheet-vline.png | Bin 239 -> 337 bytes toxygen/styles/rc/transparent.png | Bin 195 -> 298 bytes toxygen/styles/rc/undock.png | Bin 578 -> 545 bytes toxygen/styles/rc/up_arrow.png | Bin 158 -> 303 bytes toxygen/styles/rc/up_arrow_disabled.png | Bin 159 -> 304 bytes toxygen/ui/av_widgets.py | 133 ++- toxygen/ui/group_bans_widgets.py | 5 +- toxygen/ui/group_peers_list.py | 9 +- toxygen/ui/main_screen.py | 157 +++- toxygen/ui/menu.py | 186 +++-- toxygen/ui/messages_widgets.py | 3 +- toxygen/ui/password_screen.py | 4 + toxygen/ui/peer_screen.py | 4 +- toxygen/ui/profile_settings_screen.py | 6 +- toxygen/ui/widgets.py | 12 +- toxygen/ui/widgets_factory.py | 7 +- toxygen/updater/updater.py | 17 +- toxygen/user_data/profile_manager.py | 28 +- toxygen/user_data/settings.py | 316 +++++-- toxygen/utils/util.py | 34 +- 122 files changed, 2945 insertions(+), 768 deletions(-) mode change 100755 => 100644 docs/ubuntu.png mode change 100755 => 100644 docs/windows.png mode change 100755 => 100644 toxygen/images/accept.png mode change 100755 => 100644 toxygen/images/accept_audio.png mode change 100755 => 100644 toxygen/images/accept_video.png mode change 100755 => 100644 toxygen/images/avatar.png mode change 100755 => 100644 toxygen/images/call.png mode change 100755 => 100644 toxygen/images/call_video.png mode change 100755 => 100644 toxygen/images/decline.png mode change 100755 => 100644 toxygen/images/decline_call.png mode change 100755 => 100644 toxygen/images/file.png mode change 100755 => 100644 toxygen/images/finish_call.png mode change 100755 => 100644 toxygen/images/finish_call_video.png mode change 100755 => 100644 toxygen/images/icon_new_messages.png mode change 100755 => 100644 toxygen/images/incoming_call.png mode change 100755 => 100644 toxygen/images/incoming_call_video.png mode change 100755 => 100644 toxygen/images/menu.png mode change 100755 => 100644 toxygen/images/pause.png mode change 100755 => 100644 toxygen/images/resume.png mode change 100755 => 100644 toxygen/images/screenshot.png mode change 100755 => 100644 toxygen/images/send.png mode change 100755 => 100644 toxygen/images/smiley.png mode change 100755 => 100644 toxygen/images/sticker.png mode change 100755 => 100644 toxygen/images/typing.png mode change 100755 => 100644 toxygen/stickers/tox/black.png mode change 100755 => 100644 toxygen/stickers/tox/red.png mode change 100755 => 100644 toxygen/stickers/tox/tox_logo.png mode change 100755 => 100644 toxygen/stickers/tox/tox_logo_1.png mode change 100755 => 100644 toxygen/stickers/tox/white.png mode change 100755 => 100644 toxygen/styles/rc/Hmovetoolbar.png mode change 100755 => 100644 toxygen/styles/rc/Hsepartoolbar.png mode change 100755 => 100644 toxygen/styles/rc/Vmovetoolbar.png mode change 100755 => 100644 toxygen/styles/rc/Vsepartoolbar.png mode change 100755 => 100644 toxygen/styles/rc/branch_closed-on.png mode change 100755 => 100644 toxygen/styles/rc/branch_closed.png mode change 100755 => 100644 toxygen/styles/rc/branch_open-on.png mode change 100755 => 100644 toxygen/styles/rc/branch_open.png mode change 100755 => 100644 toxygen/styles/rc/checkbox_checked.png mode change 100755 => 100644 toxygen/styles/rc/checkbox_checked_disabled.png mode change 100755 => 100644 toxygen/styles/rc/checkbox_checked_focus.png mode change 100755 => 100644 toxygen/styles/rc/checkbox_indeterminate.png mode change 100755 => 100644 toxygen/styles/rc/checkbox_indeterminate_disabled.png mode change 100755 => 100644 toxygen/styles/rc/checkbox_indeterminate_focus.png mode change 100755 => 100644 toxygen/styles/rc/checkbox_unchecked.png mode change 100755 => 100644 toxygen/styles/rc/checkbox_unchecked_disabled.png mode change 100755 => 100644 toxygen/styles/rc/checkbox_unchecked_focus.png mode change 100755 => 100644 toxygen/styles/rc/close-hover.png mode change 100755 => 100644 toxygen/styles/rc/close-pressed.png mode change 100755 => 100644 toxygen/styles/rc/close.png mode change 100755 => 100644 toxygen/styles/rc/down_arrow.png mode change 100755 => 100644 toxygen/styles/rc/down_arrow_disabled.png mode change 100755 => 100644 toxygen/styles/rc/left_arrow.png mode change 100755 => 100644 toxygen/styles/rc/left_arrow_disabled.png mode change 100755 => 100644 toxygen/styles/rc/radio_checked.png mode change 100755 => 100644 toxygen/styles/rc/radio_checked_disabled.png mode change 100755 => 100644 toxygen/styles/rc/radio_checked_focus.png mode change 100755 => 100644 toxygen/styles/rc/radio_unchecked.png mode change 100755 => 100644 toxygen/styles/rc/radio_unchecked_disabled.png mode change 100755 => 100644 toxygen/styles/rc/radio_unchecked_focus.png mode change 100755 => 100644 toxygen/styles/rc/right_arrow.png mode change 100755 => 100644 toxygen/styles/rc/right_arrow_disabled.png mode change 100755 => 100644 toxygen/styles/rc/sizegrip.png mode change 100755 => 100644 toxygen/styles/rc/stylesheet-branch-end.png mode change 100755 => 100644 toxygen/styles/rc/stylesheet-branch-more.png mode change 100755 => 100644 toxygen/styles/rc/stylesheet-vline.png mode change 100755 => 100644 toxygen/styles/rc/transparent.png mode change 100755 => 100644 toxygen/styles/rc/undock.png mode change 100755 => 100644 toxygen/styles/rc/up_arrow.png mode change 100755 => 100644 toxygen/styles/rc/up_arrow_disabled.png diff --git a/docs/ubuntu.png b/docs/ubuntu.png old mode 100755 new mode 100644 index cd80444eb977148bc5a5efc0b2b58f80d0d9d8fb..67951a55941b7601e48aefa833fd1a6687a098d3 GIT binary patch literal 109748 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 literal 111928 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
apFL1o4urBf$s8CgW_ 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 Kwn*CK?f(HdvqqT! diff --git a/toxygen/images/accept_audio.png b/toxygen/images/accept_audio.png old mode 100755 new mode 100644 index 2fd2818a2f4918636b9b6239fb7f5202e3c76e67..796997490cf93d2405aecacff6344b50fae25a2c GIT binary patch literal 6436 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 Gz4c!bbz-Oh literal 13514 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 SBMQNyQMz;QcCoD4)Bgi6u8V#E diff --git a/toxygen/images/accept_video.png b/toxygen/images/accept_video.png old mode 100755 new mode 100644 index 2fdebe746f0fd232b0707cf54e69aa089832b576..bac3af7ec4bf54efcd3524c973d74217d02b94c9 GIT binary patch literal 12155 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 literal 13416 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 Z0%TlxGnU%MR0rTcm0KD&3luC~{6BjfwOarH diff --git a/toxygen/images/avatar.png b/toxygen/images/avatar.png old mode 100755 new mode 100644 index 83ac75787a06d910f1180218eecfd2125ffa70d4..06255a15aba0525cd4aa45fbe42943db741709d6 GIT binary patch delta 2210 zcmV;T2wnHsEzS{;85jfr0001Bxm5rF00d`2O+f$vv5yPGVoOIv0RM-N z%)bBt010qNS#tmY3ljhU3ljkVnw%H_000McNliru<_8W6BsUuLIAZ_+2f;~1K~!ko z)meW~llK|_yl+DI6%fL2LWP79Yy@#QYMpgV3%Y5c?X|8>)1y^l>4MKL4EZhIKF4LN#^@TOdzn@Y!@pb(NU?u~+L5B%sHo1^9H*ee zGwYd-*-yv2^piTa3GHwRiZBPoW0&Z3VecWEHoyuXZQzituvc^%yAU6_aS_U>x>Y^z zIqi%Pw6)kR;T6KHgu~*Wmds#^BX_e!rFEy_elNiepF&xy^|iDJNBxS#M0a}G4?sMVOg|epEpgA z^gK-OEfjBG7=vevZ#sy9Gc%ieu$H78Yd80piLrllvIV`UnvZ~3^phF|@$y6KpSHqC zWHU}%wZvXXiszNolMn|TP$%U5jBFn^zt@UcBHOIjYU9z2{UR|*iClmN=xx;nq(%z> zR){3Yh$gZ{OOjXcsCr8JaRs&lG@_WIr_#s*KweZ_Dzb@e*>R}~F%NZkIum7xqLH4K zy*_`2k3cS!mxXeKS1v}mB6B5=cmiAF(jG!O4RuL|oM&_XqqNLz$O+3L&)im?&QDto zwI&m48jqEv@Dl)PVoQp#Kg>CZ!Ru>^YhwB8TbqK%XgsoR1z!Ov4(4S45PQKLVj6pM zHyz25@>Pe@M3ewGI*1i)A=P{0y6_H`UR!@D-ig2Rp`bRl%ZG1(5re?iugzCDfHT;Q z`$snh+weD4Ke-=ROydkjDVU}*lK8$50RSv4>>1awA%hk}7zCh#>LF!~YJbv7L3)5G zR%WJPn#_<16a*Mp*c)?0t}pGt_mfo8Qb{_+>+UTg8=GRLV4Tbp?m#dS_bDO;u(5wB zCSEyLM$a&gx!SK2SyC5 zE^{Rh2_H;s+f3{D^)dGh?FW|&6eEs}e^g$XQ4ya-@p0nY0H(#bZBmh4cA{bSkJ{TB zbfFW5km!#Xxw`U(jjM`(T~HH8?s0#Hy%+%AJ}^egSbFB=aWvu;@JsVjyn@D;EqY>x zJz&J!K;;vf@N8quE+bB3J$w_e8e-4kmS6{NdKwyst_DIgT%4U4g@uNX?r(n#^OR_J zbYDfgVDkWri{0Jrj?fIBm~kCG8elGbs?~=8S9Q!+Mzmup0tbL8bbM_Z4v{l-%oloG z45q<;r{S!Vn{C9eH=r*f^8lb3866FAWj}P-^)Lh-@CTY3^iEDs0}QMnzV$-Vgxlib zJOwVC)3N8_pDzTh{R4lU;Z%P?`ICi%jQ1#kN#-s!buRtw;K>Spb_AMF4(T{zkR6qs zj~_=^F=;%RrI2$J{BN0)gZ^eLT5?E$;)5B<1^gTmzEMT~iNe3k}f#V!ZLaTU_w0oBe;-TQPuoZ08vV z3AUrYV&G#h7lhdtDDA@=+=yusK6qw?8>Sl!9J1zg;9YDb6eKCO;@zB%L)Jk~yv@u! zZNTx+$GRZqpTC{r^nkUqopZJ3qDwe|Jy0WViEJFycnNmmfi-#AA zEk55ADrK*~y6K3N``CZmaX0XV>zeuc@W5S|po0x}0OUgkC9)n_`KY2iqbxp0#JxZk zwa(+0?SH_fh3(9PmPFVg+qdrrq0wRwCRrzA@@Z$?ZjFuc0(5k-EH<_vE-NaLP7R3| zv#52RxMDkjCWI+aK^Ll$KO@8SZq~_~7*oI$Ffq=si1|IZ{xg3*aDC8)a>?0h!?F%6 z43e{a2NuDN$;a_!9oZa5zR-O7KOR~Ix1x+Xwfb7^lrEy#jgG0ET78W=73tV6NK#aZ z>hrn}3Lab@wB>b+>QP1Nr1|Vi47N~Dr6@Nlt1`C4l1I1g?(^eax=9_|h<12L%KtBh z*Y|0Dd|wDP$M}A}AA`cj_x}O7;Kh#Dn}Znu001R)MObuXVRU6WV{&C-bY%cCFfuYN zFgGnRG*mG#Ix{soG&CzPFgh?Wsiegx0000bbVXQnWMOn=I&E)cX=Zrz>% delta 5818 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%0Dtv0D6Wo=k8h}}t8)b!?OR)0J=xjWw*l<) z6S77E*q5E1eY>@_)f4FMP+ME;6bgkGSj70`5A%3`ysFaD($PRY?{c}A;^N{#0EYm~ znL!Mi1K?0`aq*zbEOYG zwvdp2kWFGCEfb_U* z1ppyD9`Cm$B_-0RsHlJ~R;$&X@$vCd07D!&U}$`Nd{nJg2V4ab2n5)?d2<>;5U;Si z-D3wVii(O#ef{;mE5El&0zI zjT<+Lxk?{XQ&U6ve10r|Bo5Ri@%j8%sZ<)mm3^BwZAxGNKH*mWb^Vs3=aX2FO^CYQc_Y#uIyX4ZXI7N7S96^M*>)so0}Uikw`c#k-zlP zOX7rtg!KTD15hJLNJvvX!H7Wf()8{HWh8Fv6wvsgTQnkl~?z&~X&SvME;fG5d+@bEAV zV2}Wy9~&Es7Yc=3pA$Y7hGAed8ky?qYWtx>hkA87oti!T>|q~~0g%m`H!rQEq(oL) zS}IIQNeP-g05CQ-#)w2B0|7v9GMVBEvxS|Jp2d$8(uyD>RA3HHU)ccfA0{IfcSjtCgv&tenWq%;G$&rV<`MlSy|crKox4WT6)Kh9m4?X0Q{MSv|NdR z)mgV=#}1=bs|^^rmX(!%4FUKF0E*VGU8@R2+5P+X=`CBf3;{R^U_}sP*9riqwr$&H z7#SG}$O&uLu2lgj0sx6vEN&Pa9GtPjPd@o%oFIsQ0a!c>0X#1}Jp4cV_wS$aS{N7@ zpv7Ww0{{sLz&J88GSuGQ?wpA=*REajo6%#RnrJBZB~pBmg%_lHGN6bt5xT*3{HwZEI`0hKIbpSwYeOF1EF`wKX+0S!bf) z+i$-$0O$bVCfOwA`?F`ys-@A8@@z>_7 zSFdUTT%39?&~3AS*>tB*pSJmuYLiv^JZ~XODWlUK$UH%p*_10V58F!{~H6eU1ZmyB)Q)wHk(D&Vl6{ zPfY?Ey1TpYeD&2=6aGMIkH-^@Ss*C>ghbIa9qmu8v9hv%(yrBN|2H*WO?}zw^?K`% z9Xr;m(P(^{1xZOsp-D+e3jq9Pma|F{z$=M~iBgF~;!{e0_wHT#*s)_h9*?IUz_dTv zZ-T^;BS$7EiuxYFA6N(*Y@q;b69@zi$B!R>;0Hm%=nFvt09J{`;)}JlwN76sGNYrT z%;%qfZk0%XB$ohu2yTwUrk!*EK9oo#m%sSp3;WpE80Qq3M=A1q&pr3tZC@#|4u^v| zbLNbzu(0qBfGPmL<9Ieutj?;!!ooXe&YW@iO0HBYmEOF(JSBkl;0JS52;j)8ufE#v z4^6J8r^j1fUOt+foZJkc0uN7M14)Vmtd5H0hu z+uPfJ4ILdF_wL-eGXOvjKm))GppSig0RRzzGysVU7cNX$zI?efKR-Wy*|KGk$;rt) ze=1fC!$6@>*bW~)e8pn1{2jpM>54HwHzFQ@mqjAc&Ye4V{(2^SVW-mxlgY%`Y&Kf2 z*Spnf^#g~);ZUhmW}2p5Y)vvOCnrY~5fKr8k)EE;PfSbuJ4S?`1FAX0y$hHU1NH`R~9HU4E() z@+pcsEtku6&v3(pj$QMdHcU)iE(-u0eui6S4zo4}6_x&?o2I5NiDd*q9M8(iYF8+K z6k{5VX4ZOWG#U-9P$M=0(mvlEjfd=!ptsNeA9j)Y;>Nd&U>cv7h@-A}f%ukn9oU%fL{j#=y|f!octg zDAe$RfuYoZf#FpG1B2BJ1_tqhIlBUFfD(!UJ|V9E|NlP`5OBgT;H1C*34i~S{s9p3 zSZFAa4U&DmE*hwqvn0qbn89t^^LEh(vlelREGoAEiZUj7ySs2@T=)_;(NLnEy~NYk zmHinzAD4=`)EA3npaMHj7sn8e>&XcMMqw!l2{UJjy*!{0dGLbWM8~a-jmjMddm0-R zXC5qic4p>eNt4DWDr}w#mJ=IUJ**5GN*NjE^zgfHl&Jb7#dg^n_AhLm}naqSQ!|sTyj($MMrLaN@iLmrVf~f8$WAjPju7Z OWbkzLb6Mw<&;$Uzu6CjT delta 260 zcmV+f0sH>31IYrA7#Ro#0001uGS9sL000DYLP=Bz2nYy#2xN$ntQeDn0VRLNNklEsksh&`l;ozIm2h0@&ITsH>DRKmh1soXNBjg<)k_Nfa_|7vl^-n;3^O zEwJ48dryO=*#W#vr?)qsUkhB|(c(dyQCv$D`+o2KfDZe^Z#J(%a%r585x}5ncE(w9 zsct-e2i+Z`|MN$%wLOo13j_l7B}kwSRZ7`-e)0{^tCX^?4gLdpq+xayUD5*p0000< KMNUMnLSTY5-fw{b diff --git a/toxygen/images/busy_notification.png b/toxygen/images/busy_notification.png index a01eb3ff6fc36c848de4332e792e40cb5342655f..5f73464a0c238160c5f40e86ff7506292bab26b0 100644 GIT binary patch literal 556 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>0egba4!kxSZO5TC7EZ$5~lH-_c>h zzyI_5MADVZuRfcjqWfV3=gKJ}(rUZSu2sHYbJ6-ed#gp8#0!b8Wx|@%Ckgvq>c7tt z6B50;L~#c1p<^!P?7w_9qZnUK$d;Gfn`XiJH1O7%Y}UAq4*EJgr{fd2+xHc`Sd*Ie zw&+IHuTOWL|CRssgL5KN&BTd7$EcRLMwFx^mZVxG7o`Fz1|tI_BV7Y?T|<))Ljx;g zQ!5h_Z36=<1A~=Ij;f<*$jwj5OsmAL;l|I}*+31FARB`7(@M${i&7cN%ggmL^RkPR b6AM!H@{7`Ezq647Dq`?-^>bP0l+XkKS9-zr delta 560 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 LS)wbE&iDTVkX2Se diff --git a/toxygen/images/call_video.png b/toxygen/images/call_video.png old mode 100755 new mode 100644 index ef9fa86820f09d1bab5143d35797efa79f5b4ba9..ba153e9cef8c3ac087dc7cff6915bc5d8d1d4096 GIT binary patch literal 1204 zcmeAS@N?(olHy`uVBq!ia0vp^DIm8&EL#Y7+!>a@a2CEqi4C48d;*Yv9Ffe)r_=LFr|Noz12k$7N zFa&_XI4?S_8W;mPB|(0{3=A3#^UKu(Ya|4FA3y66vUz_!Mf=)KQ`=VgTTj%i-Z*g` z*~4W0c6XxPyG<({Uf;I-Ang3v#s1?3p^r=85p>Q zL70(Y)*K0-AbW|YuPggAc0Mi@bEz*D$v{1~JY5_^GVZ;-8Sa1BK!i0x`jn2yA+FtL z7+ASlFCDyB{oeQH>Hq&{E)j`RFO#1%TVY9c+@}JS`mDJwUHXa=JZzu&oqI(@e!W?? zV#cZ9N7}lV_1S{ntxxzCEV*KmUpp)3FSd zL@&*Lr~Iq^n#W7T;2ZeBXeV|T;q(324x9y zxiUp31UZUwBrmaSO6{04he;y+`L)8}Z!7mGm`Ijy_*MQaIb_29?N3z~emS*LX#cW9 zDgQRkj!vFYIek}o(h|89D*aWzpVTkApB!2G|7c!ZsMh)0zmG4k`}z8!FPy%NjCj=&*NBpo#FA92RLTJd%PsvQH#I51R&)V5Q4Gf;HelF{r5}E*`#j@c5 delta 3592 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

R{69#)=&S&pQ23+A1c6L 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 literal 120648 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

cB#^|A?E(Gz9LXd8+dP8z-f>$_4|NgI_6B8~ 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 Ji*KX+{vTI)K{5aU diff --git a/docs/windows.png b/docs/windows.png old mode 100755 new mode 100644 index d4ed3238dd039c0d6964413838bb9ceafb9592dc..f13f4c0f28e5dfdb5ec40b1a8d32a3f20e11d3e9 GIT binary patch literal 72830 zcmYIv1ymf(()QpMT!XuNaCe8`?venDgy8PM-Q8``;7)K4K{i-`1P!i>%g_7Xd%yqe z_L;NQ(>*&=Pd!!DQyrzQCXbFvj0yk%x}t)NCIG-y0st&AGVH6QSofyo^#K1-QdJTF zz9yhOS|GgsrnFMfR0RMZdH@It1AzNiQP5uiaOVVo19Jcn$^-zyPr02MBCj2Ymdf%n zz{@|M!k&thR|(1|1p`+Az!?0e!HharTE7kl6lEl}y;hF$e9iG7%P$44AFk7Vr5Z_+ zB>9NYwhm)yBTkc`|JBr z#QV+%-_D1wE?*&`1B_mj*ybQ^$8VJbzl?aGs&eFm&TC_00UlR_pRM;MEebeBlm4p9 zUAx3P93p~t83n-_whrY9K3@i*8m{1R>yXW=I)#K$3;hNe> zK4hb7C*3AgB-*w=B)K{rWzHNS?$%jzdY+bfgf*s3{*c+e?+YwYNPC?qg+w$xYa!Bt zt(tJO5o6KnWKAI=d}Sdrbh&vezbE-uSL?8#1gbvMVaNCnlBsZy2&$FgyGCBB84ySBL8J>A5>;fGF%B}^k} z{V;27w0EsLzPe*=FYaZC6Be(MwlovOy7EI)??=mZcy+g}`(X7gfp4}fLdy6-%AVl!1X(CsavxOvmPDMi_^PsDv+ak`{5L3-k) zvC~mwF2dFtZ}J6ZANpN@--(^kdLS)RVJ#vc$ehzXoDBOQ0ahJ=1%jY9s0xUHgp3J7 zYKRII=nK?H~6_0-+_|ele~_ab3cS{%Wv}r4c*@ zH%P>HJVxbkDqXAkszi5@7&A_ByE5xE99rnTMqKPk3V zB1JMrY3A?ndSvZC)1{$*`Fu18&-)ZI(sxMjH1NDCr~TL1 z12PP*XKfPm@0;xcwaX=t6{vx+=n+{;7>#BQm~Y6D<(^UN9~D;>Y9_VQ{0b{gmA;n8 z1}ikG06?d-rf20%)8el)8l^?Y+x-fT7oH7rTQ}i$P{uDJGpL-o$FY3^hl2YF^|1zu z9j2m!s19SlU_L8*{*>ff)Zhm5+tWm%MBk7}DLV3nt!|t#r)wZ7oq2*+D`r!Ra4uC! z^dTvn)OIagLuy3Nld1IHlX0sYLPN3yH9T;J?9P$-n?`W6|P7K|tIVHiWM6x#DC|OS`^C43TK(JyV71s|NlMW4~7YDP1bg#nvEj62!t@X^hCA~|w2)VLB4-zme~ z%0BA$82W<)%u3*k1zUr&r+(UWni|FEa1Ok!!O?Za*E$v)Wi3^`Fr2+9M{Kr|zZXBi z6@VbL&klashV%{WIw`GBf{fp};5lh8CrX7|*>8A#N0}d?A(D~r40my$P}z~3jG*<% z>y1}wEo=Yi+?>B?5kicL03hxNs@3>tIdG<3W`vBXK`&IU<;`uu!lG+9cL=TaW@~Mr zOHqFBS97dqc2Mm_l~xM*)2wsiMsNp8%E?B`qQ7`%)}h(1r}jvqrk$!YlU1$8uqu?m z*1W-<72pQB)EdWS{YVQMKdoM%4vS1CWh-+Sz(^LT<^E$WWuu5gw``0L;}CdK@7#Cp zk&7F+|B>CqZwno}E71mnHdrB<_KmqC8B&mT9|sTw7X*9)xYcX!{9%4$j(+df-6(gs z>X_g%%i|L3gy)&5BVQ^U76f1@wT+a60fl6ln#SHD|H9p_ji=#3@hya;_1m4Y7r#D~ z%@9_!>=HsMlUvI$9uAI8&S?D@tmvnC8~^>Jzf^Nq5$FE1qJ?41_X(OxC#B*VXgYaE|Hk$ptD)6jRJtRQ)qU4pb91XJ|XEoIWLp9GHd1D=Ky!K;rEi z80ZY>RN_2;NrUc;lVmbvI(=x4f97#&L|7&F-7{C;5V}c%TmUWg5LfiO>ekSbP>RHR zupj3IAr{wm$8N6XaP{-LG|6?zkb=xz0VGfTspspaV!3(xg8jS%os`V@ndL~k-LS6F z&+n;G(&;u*{lCfwR-L<__C5xZ?KKD7l{*_ZS-Z##a{sBK)YPE;0|SnrqXecOCSf80 zQUDk>|4ab;kG4+FC;9zD$L7R|JE!Q(ItpbD-I}`ep0v>10fR2JO4^BJ+tv<#R{uMe zmsX1{dUD`%Q!a|XgX%}M-mATe191T*CYA0_yS!!{wGIOa(d)9v7VrQvO&HezGN;^< zdJmWw5lB4~&JH<<(=WU8VJeeAkquLqVS)`~qD!T=aU(2Cr8e-+fx$yomq6CQDZ*+= zDT`4eS^kLHQTBmG#R+u~#c>68s?H#V52C{`E7MZ0RV8}?h9)te_W#(T-)JCss`kg{ zHF#q#;3}E1x4?W;D@rPV5u#Wv?@;>Wcr)hYOOSGLQGQQCabuI?IKSUsi@uu22wgq- zt*Xe{v-u_M4Ol+8QZA(`r!X(Ja=ky%=?-KpzPAnO$MWFj+4`agrC7GKzU#H#9TL^3$J1D#ADD*6^Xpe&ii}hOK;Nt+q!1r0`8FyXk%gjxV66>V3_= z=E1>5k zK>$BlCPOtj3PE_6Go5RU-J{0=Gl?#2(CpKe}Kb+xlCNwDOQX@;1< zdQ37S+)C5~p6ZN*6$c;b@HC`sl(;CRm?TrNjt?bauuEl(aCzkv=HoTKtHrqTHv(_r z<`T)vK<+!)>6VqICFqtbp}bRixL4+y-^Lni$6Xy}|9%i}rt+qFYV#?DFFm=);x%%8KqnSjCIIJn*AaWEqX6?#A=g z=CYjHGk5^Xu+jG-e(D2to>_ma z<&B=#)9A@9_y1b%pJv0hOSR5#g$&libeBlgRFOn1g@>cGBydBy}c6xetxmx}zRmVeq-T3fD5hd%Hl={TRbfEC6hTA0&yl^!h*u_k>RDOal zCjh3B7imXS#mTCa)-#=Mhro;7&ccXK%CX5k`Ey{H?@@n?@BJ-)I0qxO$v-8836sWJ zg!aH09VvQrX%Cqh$4PIQ+A%t@R8ds#)PAH_RBGH8w_#e!Q5(*wIj1S)J1A&ok+}-sAK}(QU4+cB|kIQ>?VkMbAQIKIuIiJ zd@u*Aff_UoGpQsJ%JpOHKwHnywSSLpO-c3CWTOfSRhXtm zdI|sO!GR`0Z_9_YpE!>fMR7hIwDR|T)>4(U8q|};<~)&-vjQ?3Js$Vet$nk5zOPtUGZMW zl+$K&8!e5dz^gwzEv4f2P<*b8?W30;3{9BEg90)#=5dSF|0%&)o8aPRC}#w@f{@ z{mJ9yb@qxsPT|W;5y{-5z><-(&)mYKzJLj}o7oj_nMkKe5MbacCT3M05sbQ5jr-X( zcn9)z3K-W~^s;N11pChn2Wvj$%wUPCDW0-tp;I1w^=as1IhW*8a@amC%&m^vlK*nl zq*^l1zYBejOR5n2bIxKB%hJ5Y-3r0B=nDF~;o(f{Y!G8DH;jAbcCA6TrN@FO@UX5w zauqXVY}Oe}tjZWDc}ZIRyrw`>vu-v>QtBND;Zk0?eJ2|K!_6UG#x@8*E?%` zgblv(ZOlhPSINje*Cfa`5?+5;a+Zm$6vBZ#S-qF%#M-;U^YJGPyuQTVlOxr`u|0M; zJ@S6hmcGg&AtF*INNquxgppw7*!Ov{omp=BxbBq)CxI*gU;uDCT%RPoDM?{tgW!J0 zh4q$?tnK_}ob`kO{_Kui&j(yGCG*;`z$oA$BfL#~qd-r&kNpi#-AVG}mO)DWj6ko~ zfL=X5ffO8@XfdMzDLbUSH$Vd`6$|l=98oC2o%+Ro0dIFO*8T7EnO}g>`pdnxzn{@j zkBoF2lxNmM+gHA*mq=nI=MC8AP8zSlBZ;Y=0&5n($>5pkMIXPxJpj!L$RaqFP$kiX zY*n{d%`SZL{Fxd|MQtZ7L#CYCuui7&-5kV?YLy0!7?Qh?bFevn9*d*a9TIfmVnJuJ z8^nry(I!n9`dN&pgkbtj1F<$K=iTUc7a=)oR*ocHWOG zvR_1dqc&>6;Y@+!^MU5lwp{5EDpr+dJ=z1cI5=C>?}zu}lk2nF-)-jkbd74w|K%k) zSE0G+EAd**`Khm6({n@EoI^2Z4f}U|uEunIEZBB;*3E~SUPp$+QZEhx@rS+s<-@_& zLFGxKZ233o>FJoMPC{!by6RCqu38!*1pxxpIZe{PWb~8T^kG=cinRAT!Hi&`aUn5)9UxdaQ8H4sm^I&E51@&akKU%4dWO8I=G_q{Cq%Jt!7xl)hsEXGZ+|f{8 z5Z)g^U7g6M zfyc>c?Ahz{Ymi@Fd<^x!x6f&0>DH6yzG|Oelc+hA78|F_#X6%p)6_06ih)G2GmRr*7ZG~I9qU}9}OorpJDCeY@Xd68%w<&)J}tk9j0%6a;1++ zEHbCp`0j58uf5X@Us`?504|?^M77=0ZD*l8FFSEIY26;XUZpP^Dfu4Bzry$LWA>-o zv=wHrEQIHl7L9*ZW6SE2$-Ud^3SkpRNaApl?DA%a4~&E!$j>ZLh~6RCo*1PxL5RtU z9w4y<5R%#=klRg-;7lkv1`OYwKW|jch(RWP_X$_|t^sFiYo)(~w7k4L&J)EG39&_= zOAq6L>z4G5j6+eiW)3d6O7snpontj7E;1ID1gTUI8wMuyx9J4&OZP=33K1egsg&aaHhjnGGV%h-I!5!My+ON~QBBIS~2j9nI6`>sm_ZlP~D>?%tWd z=J@yoKIkTa>={yy>Oqq7V1Y}ORGC9LO#ZLVRvxgV#WLu}gNaX%a zQ~sSI2ht$3y6A{c2wF*_Q>ZMMM3NBuy@~k>YHTkTo9r4|r6QENMXOdQtGv=@KJcTg zofFT2uWubbW6R^-uOJ6!L7q+NxNmtxa-*Wm#7)^P0xcNw3%4>Y1?^R-9(;e5`>z%I zZxP67v%G((tZXNh_|*Q8$pB5EG9LWZ;C@Q#Oyjby&MVV>hqIirS+6uur?mOoVWk0Y z_Ag123GUAVa%;yZ;&`f-U)rnfT!zUZhEC-^57?)6RaLz6obJbbjt;dksP=}{hw!!8 zy$Bo4vA+kEo>k;BcgB!8eWPbx_8Uqq>4!nVY zMm!Qyf~eYzwJ;tVY+u!jiRsYkyUuEg>GmOo4LH4>cj*KBr6|5n<2W*?gVspE(;zm&W0C9XMJu zi!kthFFp5sXrmfvzn{NcT{ZIc^;KC!_uh*Wbrs_k-wZD!=YuOe+(`G|=sl>|2;BJ* zI0Xb@<}>!}3#WKKZ5}}So;C;MO?$76DEy)Q4+T%RBwA|UX6gb@LKFJ9@Iy{t?wncr zphVpPdwoy0&*A}(aaCBqZjQkwoP=0X3<3Vf7af6*f4^3}!fNd7=6CnHbRUg8xAxv+eLYfO8CjkwN*~0J*}(KoZ-xlb z9J~B}k%sxU|5y7@N^U*r@=CMlRE-pDt3?U_PL=*i;oC~Y?<|iY65QpzTOXIP7 zP}0-2016ioORSkX8G=!PN^LSn#rEllrc+IEUdF28B69zgx-6;|+_KeVq1(~`4fUz% zlvajubk+|gf1%ft?GAO+4hdnW4zvj<$F`WUcDBcr7Si3R5#r zMg%4vsuwlZy33cjsJr{d+&B<}BEx!pLU_Y?9E)`3%Y|F--FW-h=U{2CC^6}*rCiu7 z;s;5c@84<%_D3rz>-pUT3y=wyyRnYs&Qc)*L4*3(9hqCx{^3~KD*27qNaAtxzuU*P zQfsHoF|_jKG4dzBYlw6arJ}GhBK{VzAtj&$)Lc6c^rq=bl{dEN+7HqE2(i|UVV4a? z;sIohEjla&6)t&gOosZIo83=V4*Ir2JzQb+jog;(U9x8P2YOCDxN!IL^L@*$&>No) z)@6p+$kMVCXW_ov7~hYKfvP5t4;&)5qqsMwqA#xE&pQRx1g4lQZAj(7H`&X+n7Wm~ zM-r`=!{^q^;GI6?-j}fr0;lJ5Z2&mk+3*35;ep%V(+`Y40(1Q8fBLwOzT)5$)H&cP z3ny`W>aZ@HtUsOOJsaYhr@fejz0XNZy-%&u?5A}5W1<9~mBIXjI^M@B7b}v=vgt7m z7kc!#ShpX$NSgi;W1R0SyYo601Qse&)D(x_qb)sRm%Ld{%|japJ*Dv&xjK0`u9Q6` zeA;A-7|gaXPpn;C=B1TR(d$vvQKFsMB3GC*;amtMhqv1>lOsP;#|g(4KTcFqG0BcC z3H1q4x=4<$;frJ#DRP&WYHPD&;EHF`fjjDZ=d&AL`6PFoexR25_ZG<{bm!9o|6xpo=sRl?+dC2vx>o7X3pM0TnMuVwy3A)mV{WR>03BwN$7K+! ziKCExiqw}#*;wyyL{{JX&#_^2-f`rKU(Vg_s>QqFK<7iSXD-!_+%7r<6-c^9tN?fs zKu&wMpA-q15fh%rB^Dr%^umT$t=(k}IdjM2ITnh59RoyyWVkRXHEf*35&!<2}ADUU(=?H#YRC}GfHLFdK*+?-*J!+eWYeR zN>BH_6ZDwvfrNF0Fp<78P4`AWJF-3qnvxQ~n9s9$B3wH1CE@Bs%pa_`N7_Dx)Dt7= zO*QYRsgkja75S%u=O;r4i|}f4AZncR`->lvRmau=EXnUU?kj+zN{Xv1Qu|+4idJ(m zm4l6^oWw4BQrJ4sTf-PE9s^P?i?CLcg{cuNrarkx_dTzkO_lfZ`nThewn8@W8_Sh5apG$S$I*10qf4dZ!IVTb|X0CToq&T{BIP*gP90oTmc>WP$d^AEs9vqvZ3IjBxcfY}LzgG`Mf12Q1qt6Sp^~U^V2C|z&$uHl;invdLOUQ z8e_$M)b-_jmcP8U+izymm+9AgMwO?9C);sI-;4I;G+`AYQBmDQzRt9Q`*{_@5V4p+ zv4b2BoxKpbv}ed4c3u{3&IQL*1&HSdd$q+4r4xsSILAoQ3DpcLoYcUE5*dyhGz+kI=bUg;c(7; z_@~gRzL|8S=pqUi>T2qZFX&$sYONM6G@Kc+mvRxoo?njVqiP?+kzeYlo3uGFrR4NO z_Txb)0l*ra!!`LKZg-Y)V!9Ws-MVSawa#^Dp|3=?kmE?535Utea6SBr?;!Qp?FIfF z!pMV1g@St1UAHrg^?sM!`Tgc*YghWK7xjm{*H_XJDSIXuN1ue6E&({i;wg(@F2?0) zFL)xxk)Wh(`*gSvYLe&(cp!)i0XA}C0?!Fqs}Ro%76+I)&t+lx_-p9KP^<{PCiam}%tdzjyraXt$8@<1W3(QQ6MO??Jmt>u@4d0{=Y_DRxJpQPuSL?+D zdLN#UJYK?J5!%lVXZ&7z>)Dx}vgVdn)|}ULi%sfB7dKniD|{k=iWm0_&Q#EAQ9(kKspY!BowDq#p^UI3_wdAH8<-^5vlRpRG+*TMmvbQW z_e+mktEjbGX)lM_&caPRdBA?&eWbru;mqUaC<8`M*`uJxI-_HtSF6c1>bGtKk~Q7ay-Y_W?AeqGPDDvFAtV z>Bh@_>fdqOGoL^MEQo6v5`L9 zOMW$+dn_%V-1t;g9EmgUjLV&tgNn{u`<|_E&kJ&CndSP4O8vt1?e%uo*)8>=tFUK5 zrEeSW4`hw5R^cHc4C7-=a=LTj)G4M}MzIU~2tFYdK_a^G%;djsIOPr`Ev>gHv%D0A^0tu~|)oPq%?azGY&wmg^PtgwwhHD9#}N<_u1b zwr`IgJv{2z{rV5A=?a-R-Y#Utv`9DR!dR}R;Q_ygTzNSjZ%*x%BgbPDm;m_qj~g4m z^G}V<^Y|7?4GUvTq;#-ZaV;6mm7>5NObEO#nM}8fwyK9=MAAQwNPAk77ynL<9d$=a zg<0~wR6RsV3h(nsUD$t+8#8f~uyvbR@$udguw@lT<%2*+H>fN(LU0J2&?|_jz=Ts~ zQk|kO|K$c5GO#ms&ou58b83ZycYKD@Ayal^rwl@Gkri`@yo(HYKn98acGBDv+Q5IWH0S zHD?=j1t?=Q2bbsXnKOiAu#|!|*<3lx6tW88G{Uf{C*~8FlvXXvP9X9@wrA6)-wp1l z{q_W3Xrs_%T*8Z-YEwZW@k zV;eH)C-1$bnS0ab*jzzihbD)MEoPz|@-Ah&Wz5na?l_hfdlKXzUE+h7$QJ?`>mayv zlBMZm)v&<88l41P9%6S%)yT1yX9f%)K0zYyidm^p0ujCx`lo334B7@7&lSC!tMP%x zSm?Ld6<@!*xJ^r>GT{BiMQ$fz^Ku>K}%8-uVGKGH}vu2@> zb_29hL;WA6Qy+Q9t;#fqL{-olTPDoxvtT8YIu4Ne?c)s^#?02>{YR|8@Nk(3E@2^v zfDE=QUJ&&+Jj1TRT2TutFu$nzw=*12msGiClh(4P>W%R#w6SX3cGP32O7W4Ky}Vnk z*>0}$SF7mip%J?K5>!o8M$ji`CjWkmW3i=;XLMOMz~cOv<+Qf48=(58pOq->;#gf3 z*FvsAwuUVTU;Heo(Z@Fa$f#v$9gJv?-L9?c-y;!a0M#r;_k$}8Ba)gUUBr6ZQ#h`y zjEKl4HHtS=IC87G%85A0PbxSuy{({26IYr4u*j(d;wayS1bErX1h>|a!hIOePAblEp zkR4oZ?592@=<-$ha}b>5nht$B$|52h0Akeo6}$Iq&@X ztX-!_@p|>}(tHOUy4mHz+FR+BZ`Vi*yGVk&__aboC8A;1=a}{%OlX>%WzOI-ySqCN zb#*K!dNz)pbV3o50x)$?@952V=OR|@ig2)!aP;l8lq zLD`792YmL&29GxjACSvUM7zk5@OmoqU&}i<-v46Z9l2{7$f*w+n?86pxb{B@|KtQm_MmS(Bp`Ya+pvSM8{Ew;<#4 ziwo%hmCPiXP;zi*P;Jdrf(D<73ujr7wvdTa>lYmqC)CL7O=XWKX3{>zx@w1tua`dj z`IQ-mvo;=K{bX*EZny01L!4ksw?FcQwMlbb=vtM=e}&&)uL^3v;QCxq>yMu!e42PI zIsZqnH9PeS04;t0sZp)H-bhESK@6mh%q$VHzGM{Xr@ccrs?(Fly9B{Y#6Wbe3u8}83|md5 z7#UrSh>{Ss@sH@o(K<``nI|({s=!=g$W<`S5~_PfdLuFv*K+8RH694jiQS!V@feE@ ziSeqpAu976Ly_c>?>yMzX=8N~mnK1-LdhR&JyDMjg)7rr3c@WoS>17t*lqTlSB7OK z(zANAr5mUXAUV%)WaMA*F4Z-EX?0V0<5f@mkg?C+qf+=q7rLBvr|c+@T=~?sRAj)f zf|Qr%5)TO2ud`c&Do0BJyf@y zd0S(}(5_b&PZk*!k3p-{&hwcl*1eRB2!#qs84^J_@L`&;VT@6@sj(eN~`FbiDP z(MK}btO!tMbsdfYBA89ik<2Zu5hB;jL1b5CtJuUwV$SFQCX3z=G(5~2XsHS0+Ch=Y zXR3Lv1L{4UKY3z#1btw5Aa%w~6KN{g1KK^USECHaNdT2hq(@fHuQu)ZwhxZACOsqC zplou!mks)cez2!1CZY!poxhJKXX$X2MR(&{?aA2-PcJh^%uUDNzwZmm&rJ&&4K!w+ zm`I%-qX+Mec?E`fE6-AkdQN~KNw3Q}EH3W9UDN7uDt0k{y3QhK@GJyOW(T3deDL*> zmcEE@wbM!I5ggpq9wV;CU_Ol~AB@E<7>QNB6lwHh7J7U4<82oT`BY)k+a>jtzfIvP zPB(~gx2cV?YFc{5(~Fuq=kY-5;Mz}UwPPWEcMqm#$BYX3-^Xcz5y%) z)ppN5+rf&!-sP0b+!Kr+CH?4!6~T#ELyy3x^3EoT6HUreaoT*$5?tFfrs~=r@->S& zFm8_xJRMWqiJN#$TejV(zFLurvm=UFrcmHybf-&H)W3U+I?YLwoALK~i*Xqws<-f2 zY)fIil;_Nx))`e?#_mD1yu7|JF30tg(}8xa^7k%5WDESoM}cIPQ1$|uF15!q3Xnf> zs?p|g1Z`~zfB8?|&{p@#KN39XdUt0@V7e-!d_)wY#Zj@b5e-dQY1_E+7Zf5{Hb6f& zZSXy@>Br@lH^XQLQORcXNDUnepBhFI`+Uyec%uwezhv*!Z#qFtdI}?0)m^3SMN>}7 z6>ipuzrTArDe0A!^XZJTHtxf=z{EN~v<%~tzoF*6(a1fuc&v=1iV*!VMqvS>Ti4XG z8oA|CL#pW0;xD730kq@v)JI~NlA4t^&7~G(rCOR1ecEf6X(uzZ;tqAbv|G@1!?2uV zMl&OpsVCgDxLm(I(OA>86fKSv0IHoh(NqR&iT7g@o{pP77;(x!%9ZW%T2is>C$pU-Mm)h29R&`Cv9_+Sp<3qgU zpY-?{Jyx#1P~1{9H*zg+WTluy258^cl)qan$P$l>#vincxHC#?eX_3ElFQsB`kojctJGpmLYyR5 zo;B6}8>uOU$4h_Sc2zJ->w`Wkh4DPiAKQ8C?UcJM31HFGeVBLA#Cf(CFvs0O`~CUT zs;N)Q<#v(eDJIKuo<{Vj8jQ7nr%fTCSuPykZ_Y{d1~HWDzzh5L{cudlq)Y7mAm32b zf93r|0mWK%$&UCS*>opyp>`Ne2g3KAvy|grx~9s``0SjEJGr-he#X6|d3hUBrtv#2q2~Fu^RlXtqu*3VZkVs-{)SN z`uBlZEon4O7&gwxg$Yrzb@GoBnY~<_xc7nucN7h=ka^HoXXBuESGy#wjvM0kGCI-z zUUf_bS_tkOz4}Nri!%RxNVZ2q=0mB&+F3blJ6>iy&fdpTm7!qp@AT|xqzS@$la_Bt zfc;Xx@y>Jr1iS)XBUyB$sJ?%kQu`iFw?fZ^$jnG8VBF@g%oc-LyZRHWWO}fPmxN&9 zxVAdGp3S*x`AxQ{YsMn`nKiyJpGPnq=Ia7(!iy15<5i^$f|9{-qaOq4%U78?sZCL7UmE zJ&2IMcGVb;9-W7;k&4Ip?jQB@9uhN7by*#6Uch~L%3e4>-Y}kT_BsZyAkaf>?3(OM zrU_EeE2guj780F zD;E@O@gsHni^Hs27kwd<#DDI%m>)yG4*tJN`4g(? zE+^8JwaN(S$hXeka`pBiRNE5*z=C7d@<|0A765;MaR&oTDEFwh9lmZt;c-~iX*0jp z54s(w>s~Kp`^P|bis^wq#dOW)z-mTLP9>FM3_zkDfF4G*t1ZqaDo7IuO_ zst;UOff!I2M#rj;4E(@DW;JT{P@M17M(_y{i(QqS{V>$#Hzo(}%+L(_BJJLAu+p)r zL*ZzCGr#(jO1wv?DZc+d{3EFHw8v?_)-%EaQ6jxnw5|&2lClCa^9{%QYdX~*X2Q!W z`PlN;icW0Sq8(qY!|b8KxuFPSa5ln@7iYT5BX|bsz61`K%8$VyZCI#cgko%A|3}B> zZxCdk{;tb9s9MNjW69w9_-$=EV@N@cIsicZtF?vB+8;PY$Hq_RSYO^x8#T7W!Z2D? z^-2^np*tS5-VqoJ6H1^cbJj0kr^I!^tLn2Z3TYwAFmx(u%CMb=8(|72hzZQ+3J`2L!Wj+J7nCz%&q>IjfB%Xs>eT@8xu-&?N+#~XJmx7vb#zQ5ErkCyxL0^U(JO6 zdhS>&TN5Y)@{>LE+sT@{Is#VGJApOlE4#s0OP(Mvs;gsX=FtLXJl;RvYr7~Bu&UbSCkM2tnB4v8*RX7m4{x>tCm3B!5^E{M| z+|B5}!{3ZBN)h%dykrRazx}VMgp@=vrgm6i=J4BpG}~{U``(t^3?2^)VeN9Vnd3jt z6Vb}8q|8}eBdnfJgNxJif6}dPsl{FqMtef4FaR5n2;xlX`@er0CnQh88&*rc6~j1; zXYpEB1$=eD{J&n5xtGw&Ki|vl;Rz0uRIn{!{(o0|T=tG?HC7oi)f;5E_^VrT|5JD} z^d=+9FwT*}DOsdKR&}N74=R(*c5mc_yt~@;XtyuAyH!a3h2}`5ByMjIBc-*BfDEdc zta@=$-f=&xhr1TGREbpMJ0d2kF_}`@dENi`+&QlBTUbPzXGzx^>NlA&ONdsnT;=o* zD{;!)?ub2Xnc5scrcV6{+y~C@B&MeaIFYPl*76{uaz~4Q##@z2=}6j+9~72xOXC4d zOk{G~zgus6jdqu9{61q@Q)@8eP=5vG|Lcvt^o#MQv%0z_dUG~1_={6l65kKl5#zmOfB*PI=j+?QK5Q3UoEZ~tPv#sDF>l(QJ417La z`WoI>>u(KNp?EDa^tt(#L;l_alFF@t$MLWr0tn3ic)k~3-~MHQvwN0ULX>r2llw6; zEHdmf@_1_W>rwWe`yZ@f!`YwT8Sa54d=jParOCoREp6?Zd=Gwk9I4e(_TK>Ct?wg> zf4&HlhD}>tky;ZS==G1de%kuh-?q_x_}wOkAOIs$f+-Hah(vVGDJ-GKiu23&)^uG- zdM6C!RHf7p!d}6N8@Ylxc3uA>o2cq9yL##LFGM`7Igx(GX>UKKvgl&dt8kX=gZF)3 zJq!8-mmu#F3rrR^*xGZw{r&yBrBi9E+WO3>xq+kQOC>ekYr?w6g?#_h2(J z6t(}`*xBy|*>E8$jb5fMH_R0Lqk>$|TCSAyKmRpdu8=|r$%1rM-0tocRLG@RDRu&G z;PpiZq3RWx;OA1p2HLAwl6U3Ze#TnLKc;syC2*vqDmS94;PsP{cf0~!--AHTR#pv` zH8(>@=m%%*?d4o%99xeg(vdi8EZD_ezBZ`bWrC!mO}YO6nqHnSB zV!)qKJ{Y|9Z7in64BP+KASI=($ z0#53+gT!Sfe|));C&!Yq{ZmnH>eo-?UAU^JudkcQrksj(X47$xh`7=v^qv>-sdlY` zcy(3C*Y{o)K%RuZRKubRSx}F#c4M3GjlIN7!ivI*Qnw`NO`S-k1{g+I=Q3U`Q%(#4 z0il?+jfd~U3Rc>{z(7by$j_fYFQ-1>vz3LiqtckYZZb)GkxwmQoXTJPTWExa<6&dv ztWgzO!Owb|n&NpTa_S-2K(m@=lBlO1f=u@Ju%cK$IcKXpF=OYV-K!!dg$aK!nK7KH zEK(iSa!BnsflE@;0@3Q~{5A_w()|W!OFELfeLC1hI&duNTzolg=9PzONGj%+muHjg zSl$Q;3FSr^ca+S2064en_IE2$&17icU4*D2RkAt7NHNwvP3xpiL=GBsc+=Tz#Y{|) zbqK~gcsgm2?^1DaAXyw-pOF@`lfxw0psEp`e){y)@Wb6CQ2~72{z*EMyE!ek{j)ki zPDM_I@!GbbsPh%BN6P>4Yn4nS3x6E5uOTA6$X7+qSi)HbtQ^j;3@p@?3XUd@R*fd2 z{-UzO;o(+FGN_vRzZvPI1UUFt_qW#!*ijR6Qt~p6T|N;}4P6ph8SCnnACn(;sda1N zX2v~O#D3`gwR}9Z6!mrYuneaRIcQYjPJnZh%U-~QBA*xZZ~)0_{YO1SvDmQOIKn^> zQ#G7Yp?3t3@X10qK>y47N$(@tNY|PZbHw1&6Hr>+6FLYG#gQ0!lygxk(k{)ZA!Ey8 z!!eCb4rW^0csss6QoU94c_|kKvD4gt71>E$>i=XTp<++Yn=V6Q*=50 z=&>&T+95;h^P9*DVwvCxN~&0_Q9}G!l-4%Y0-yd;H2R-p!YHpH3vp85wDp%|e6qJl zx$3vY-jkpVoh;OY)&+<4_mdf{Z@c^-U~F8qzrM!Z2x0*6Z?e{9##G=L(Q3E$Pfn6K z_MI_pF`lh&Zq!!*NR(;}eE;pmP#twVs}*$iSXnk4?4pp44~cPy#Aq}>%6hKF1>Ri6 zY&>7L^xevqqrZp!C&l4DJ$cRcc=abr`m~gy?tDl8@%(a@j6iXDH%7PMMMem=TA~~$ z)%*ulDib)f_KW0^cMQ_r!*q8$qN3CSf+&}Q-go{cCYGl`EDRf33L~(AoquZPx7>CJ zu+<25)0Go?euh!wYUKGx81KA_yVb@cyc8|BO5&UwUc1~Njhk=gy%}Dlf0HayA z2LxJRs=iQ`%x$T$Ya4HsNW4=J7+!ZXvD}`5$sc zT0EX$lF6+Yf!KDdQPK)Ew(m0=>yR|NOI$8b4}3s-nrY*tZEU&nRNK3X5csx7g7$l+ z8S)PW5|QbDpw>O*?zVfjL`Bvp{EBl&9X8d3#85EG&%$5fVC?PxfJ7$R^HJ>!>?RW3 zu*pjslp}>9_G#)PI}_W){>jPd5$cow>1u?Sy~MZo;L1eAG1ayWx8KK4QX5%Tqw#P6 zWpT{sRgvCZV_fV>t~efT=hs_xUnPmPMIDld*~JQkPrjQ*KI#k^{BYt@To^WNRxAQN@jT<3i`Z(0!t_7H*B z9h^}u8Lw}4e$wy6IBWRz1h2i%(WzG&Y&CKB1b{;Creo61UfcI+v-7)4C?I5+VH{}( zarDLSg3YgMp{+aVp1mCD2VV(Zmkh`^DC%3@lke!=Z`KgU4S3qfSELMzNFjf#adTlE zuSVNUVUU$#vFXU)`r8kDd(RUu_P@0 z&SV-*$U1@E3oG@+a>X-I9aSmb*W~`79}c-HiYyr|Ixg=I6>M0$nbYI*uG#Z_HYlUn zGxtx~^~e0R`yYdSosaJ_{P#yV0-&HaXw=`Sh|&A7=a`SGcTn6HEzZ`ly&NyTrT<6N zTLwh+eNn?h2q+Q)0!j-=mvonOgMf5{bjOfV(xB9UbazV*4AR2T-QC?C&*k@jpXa?F z<^wa#+I@Olsk&k}0oUtuSwpfHfg2P)Lg_vwnaH!!@Ce1Km) zP;W4H5dkZQ`cMDSDT^ih7Z#DT0L(0r{y%y-Z*zlrwwopi86*>q4G=}*Mp}A5z)$h| zS(+b&5!YmgnyajJRl2w#1Mdm9bscteS4CO`NsMXNUHC|v;SkO1L3EZypN`lq{W~dt z8lA+Ay1L1p$732&jL>Ra3T~S4AhJk|OGN|mK@>#GbChCZyv)O61WM-i`sM#2OC|cu zU0t=d3zA>G4!(Xz2~{V}XtV7i#IUip++f>e-ONEGGWPUlYZqXF#Qzh@7$*LS25zoB zh+bt&7Q*8nZk|zh4dEvHMz8l3fO?cUy_kumXW2`FNQ(@r@v$29UhP#mX@+(ZZId5FV7G_l?-hj~3|#BWBP#Ye|Ep zpvtRSZU^@E#@b`KHHyx7&(CWjAXe#CpRmj`J}7 zd$6Av>HE*$M~t?l1^}>2TANX}1aqV0scAPRgs%i-;~4Z_v%N5J(j}z|o}1-6JwvbR z@%c2oZvQG%f5cx96Ej2%2_s5@P>v#cXrOlhVO`A9$LPQwvsg}OX}W_Y?;liD2=Cb+U`Dt+Eo$;sEm8bKBF739^UIdEt`dM=vcX)Q zj1<^C<<=IRFv4u8GZ&$1frm@Y#=$b>%$hkwCJ9*Ofcw2*;kvjLAK7Sm{P|RCxO-rR zgVn8wnQ^ekEp_;wEYc&gfnk#Yot9s&xHO)gA@3#bxRP1k7p)!72TuQ=$v!^f^)v(h zEda;`7>Vsc2iDZ&F4B0c2JH4~MCbjxW+IEXKv58B)0BUeEbc|QoyIY&zmR50#1z`7 z>N(pBq5X8OvsDVa^+U(ee6L$6{e*?6OjFF7&!}NuFTK`jiwvU8BRm0{nSI)g>$LNC zdhki}(K`^xN;^-@66TvA{$_1wYi(=qZra!rkGEwg zC5;y1?|`|;IZfPAg?!>qFi~X;#F5!;i7!>x(bOAg$<)>Fn4rmT7XtILP-JQ88-ZBP zzlreIzejVt&}l+Pn-nS_6gangzmbM7bi^yVD7ouUa7r$+%%l1u9c$) z{qg$qA=D&+$lb}_vXKrUUTnWisA{}*`n+542nJhS-ebUoISxSqyNhC;KnE#Cwr%Zg zAGm&X;xxQ4hy+Kb5yq95BP2^FBp5#%8l7SixrRn~Lc(g$OAdyJii)khJ(|a>7TJHF z^J985y?d`9VE0y!zh6FIl_+Hbh;rhP!hF!>Tcl&A2D7T{1USUo??wRmkI@~ox`b3Y zfT%>OMDJ6G8F{9bQpT0P^RY-o31e?D#*+A^ZtIY;`7I0YoA%+GP*7LAD#&}M-Op!b zXy|-AqrJM5S5V-i&jU%<8vT->&XL>J%TN~=)PhKst9oQ`$X-XBg)XUk3>D~aFoAx2 z+EL491o!>Wk?&S)nA(S*i>wC2lHL8#95<~5GXZ%Z6?mI3y6N(?`8&VMTC+$!o^JjR zT|{ksmO6Zr*-pEDVX5*t`a!MQs%K}-tZc{j30-y4f9P0|O`e|6t5s$bZpdQR6sz}@ ztQ0pjfv&wl%C$B(%}6O&o%7*rvd{QgDhHjeYI{a`PgTK3{Fe!|Z(eTnM8$avnTe$6 zs($clX($LmM{Y>OXC_?N)=7~6JA|rZcbsT?yKH#VfyH1lq%py)%zh-ewye&t*5e1gf*3LU~p_JC5iuXG_In z{+|+kv&&az_~>Psf1GwtazYHi_4*@nc-#9ji}v1kP!gIYpn>)N0gKYyPL5QTuKRw7 z`@s}v3G}6Ju=n5)D+GDn!4cMe;`R9G=_l=BG_+?_P}17!bdz&XH}onWEX;Th zp-M7Fk^-bcgq=)G-mHbMmY&DLhTk`!$7AGQzjZf|6lAdW_4U0cBFce#(fkm)HSgaq z&V!4R>p6i@8# zK4d$HzA}f&CGi>wtr#e@`(+i8u_`NMPKGuQg!2p8Xt~Nj@OG6!9tty#$A2dj_gQAq{8N^>|A!tJA+C{7MHwAB^kT zT|Q!R#;^oBDxwXgGbyJ26THE*SU>Fg3SX8BkJwl>p4q|`x>o*x3_|`O7k_GIrfXfo z+bnuS3SGkL&o*-Tz6!Jpkm>JY?&M)sDsQrJk zc*=p6^Q-a#@PPCXgRB^Q!GuiV^Y0pD0CCCtk3M z6B-!YXwq%q*+pp3)iV$!TAuaLIV0k1VJ}SuCLc<2?JcqQ``fc1cdlpxZ~&2SqFKG_ zINWS57u=;H%_(ngvbh`-n3?g>e#vXrilB-81#UJ)0M<~eo)2t1q^ykJUcH^pDVNIe z5}1~A|7S$FBmMrWI_;oY*;Hm>*h%6FOuWUq{|&ZC8^f~AFd8@B)})CjBJ6pTlRVb* zLkux1`cOZMjc^;T;TfJkG*s>LIAD12)1)Rt>|s z5SPBPu0Uru7kD}!OBT}j4{gldHnjUJ1K-dS^G~vbN-}wh1ab+d2>A!co*;&@EPo$$wBk!)M&1|U+5TwXMjl_Iy z28Nb9Q{y_keFXh(ISzIb(A0&p>&O9LLw|Yss>Z^(tnB~Yv#GuQ=dgW>M;4%tFIRav zis(JSPQcJkK*7}P&V%w-Sq7ha2fCy~nb{t;=95Ql;>mZ1G*AY;+V^={V@F-&y;NK{ z!7aGA)$<7;Zy-^Gz}d+|kxxP~dW^ggryqyK2TgjaNj*ej4)Y`=%%v)L>{2=GQn#JZ zgF;p4Uf44;xEjwpZbmY~e?&lVW*n}pPOqZ*(|V#?stwyR`%Rx$SY-!>!-zkI9{Vjc zKc@L+OA3qj(>Q{PYg660t9g6lJQ^aVNoUTFx<$X1Js}z=1J{jt)rz~8D@7Og;rc~B zr{jMiQA{MZX)Va6vPI-*X$uP<8Y1N28ytg~{f3cWbfuzsVu)8>uCQ&++gZ-@oN?Fy z;XNE$zh+&J7Q{%V`DP&i*Mw%1%F5~Fw;=2{A|{E@KA%&k>$-G)%zvyN8{JGwPw)qE z!68OlUg0eUQvIK|!V4WG7Gg0Gs7W8H7}E9PrAErj(Oferm4 z^7R;{kD(UnwpKe>7nCBzs~*dGXn;T;VX!yC*yChXg2iU(*N(ngwCUpMtXXD1r)zYw zdkEN^;d>JkX9`wi5N-79%U*EjA(5Q}v0&fP(h%k6v(wKNn(sP4-g)1C=bG8;N=rke zs3Jx7ONHFUJ^Es1u~PSa<4RV|X2iB)-tm1P6Ox&i+rGbVj^CF@e^yjj`Li?$05P@6 z76aHAFAJSTup7WFB|kJE*xgUYs@0?P2Umx9pGW2Qwvxgza=l*7oxP)0VR<3)qqQT&$LV zn(p2+J1U>?L+Seoz{lbJdeV1$tST*k_psTnoV;u>D)gw7oZ;vD=X*c;tPfU+A4AyS zPu{L08RqqrmUp6QTNJQpDVf z#N3!~vU4|G4cGqy;CNhzVyQ1NA^#s?%Oa&DHgQSmp#=s8^!#O1F@N#xoI`#8p9f6u z-fd{OUyFR1@h8|bw0UJ~y5BAr0t`psFXFZOM347rZ-Nh;y#~Fh9s9+<*?8b}NszxB zt|TnM)D@Ra3jFAx9>ao&EPXQc(dYyAF#Dq_CB^SZ(3|4^lOG<@*w?pHt+1mgZWicj z&58rhTpVZq_?{!*UG*tu`m6Ixte`vx*NOA?fY^EBoz9Q~8u_I2OC8kIX7QiMkq(9S zG7=us?<#}Fz$95p>rAqF^Kt>>Soqau^1M91o!?GOK8w3PG zWJyqO+ntC#u0-Z^^hz?r(jo5g7Pe0>u1*_LsTXJ(hpWehDqr_%ZY;M?E zt^~A&)q5-Sj=)g^3!!uSod+~HzY4$j{YWk6P7n|3AwW7tb0jnhLjGXUxwE%aH$bK* zOH>#0V~QYW*5g@xrHn`_bSoLRU*+*IyLB1V*Y0RcI_RC3=VOOO(7xfvK<`O|q+(mN zYULhzW1+v6&DDq1QY9TfjHYbgvylJjoFde9HD-hA}6`BbM+kf8i?leZ| zcDr`!iH-afDP*pfzx{zCdacQ*_Q~O2ky=BjHZ{A({Tzt)w?>Y;{5t1qR_+BA4j*k~0LF9J}bCe!x? zBI`QNe!bI&+FGIMmJuZ)&}P5HnK=UnQ%RYg2C#t75NKom4A8y=WMlApE3JfT<9)!@ zEiMpc{Ls;0I;QioQ?;IT4OSq};T`{ERKS+)Q3I9AMAmZ(qX0)lBq*bTmWNW=K9WsR zu}qDMwHwVaduT@AZnMFV$7N!w_~=kx1A@`YuBAVv-klx{IV@RUA?1GMo+~8E_*^LX z+7)v_MkjTU5>G*?+C;^*+KLoooPIrhXsN7MdTrIrxvNI&cX75@065?hP$&mn9#NT( zxLLc`RcdWoU&BMJo!O|#jQHUE_^eQUy^E&NAe~Y^7Bw?o_)~HS+BM|Q$T5aKP7Autr1qluJQNeDFikMA z6Qa;Rd`Q)lVTz>1gG7eyV?I+v^h@QxT~iD^s>)VpgKMsHyo9%lzBaq5t1onu2{d1H zwUTXAT+dFGFYjIYPRDUygu`hrNWdq`YLTzr$~`U-1jIZpZ~1wy1oKIO4^hcDB;)tN zm-Vjq#Cg9Sj+#BjvzXTkF;u#?%Kv(~dk+KqWGtSNEwC2Z!Lc4a5JB(HSnf|5ueS`` ze1|c>1o}yLZRsSg_o;B2v{}^}yEFc%f0K2As81(iX=}0Yn!U$rXC(vh%`aEsrMP;J z1HFtv|5O=>lF-dImalK|{cJFLMT>^PBy<(u;}%tT>9##WibT`2q3jCq__VL_vCVM+)@&djX(w5V{IkN+nBBdqF|z0LZAuTL^MvZ+k?@y;$; z7$5?no<#tiGg`{VvxcyR9Dk>Zh=1h76`u!zPMMomoeYknTF6|GzTcY3?~WcMm*{zv zK}z*CUM#1)bzB(Kfq5D^$PF|N^wmAhUk6$&#i+KZ-1Qk~TTf}bYeyX&J{lgW_zrIP z3{}jn6o8_1yQtd={9pwZ}XKPn5uU)s@Hk|ami@x#p(WtCjsWvyaru6nj;>s zrgU=D_w426SuI(D_w8A?1O^Kb%`dALo^o<>B#PWe4)O7~iW3>c?Bg6$Ck|#0QJ(TJrU+H@XH>20G}`cJ9lSs!JHtuZeE4<)6C}`puO9 zV``%zj=nWm9w4$n;TP#r+3xa-y?Jt{eSihMn+DZ_Zl>H2fG_O zk8{2TDeLbBi)6k8{q-wkUOtOburgrNma90C4c2p2M<;+#B7a-7u?L>ub&4(QyFwuM zvm?_EU+g7%vT<|?Um-o z@P9v;8KEOM?vJRh+kT?_1H#l`*E@ak)|t6+9UYxOpQqgWE}8)5JH_pHH@B1+pU{+= z-f`5diS-T?oIlJdT&MEU1Q z5{|uBr3zZeyxkR{y^NPd$k>`2pRU-q?^4$}F+xo@lRD94UPpuW-YtcN$4`&H3`z%0u zk?H5d{A@z$tEii_!E_vCAdm>4iuDF4#JyjznvzVrh*Rx4V@wYFdQuztxlGYUGR?B$ zR~(`^-;605GlM$gdH;HIx&F`BvGt(^Vha> zbtLnxPcX9kOsm=RI2t8coW?tyB~l$#;>6DTc0_t{P0f&1QWOwP&&I|E3Zg$CU5Sm2 z1wI(fQ0Tx%9O~}sesy$LII%(cQ#8KQH+_q4uoup+kWWYl#xev=>vo-y0g3K277; zx4!1rx3R`W+weOz>D^0^la)52n40Z-fmpmaL2WOGUKee`9>fyH8>;ZGr`vLEnrE*^ z_Y)mW0WZh#*5+S`9J_$Xr!)vN!1R>ZwNtlP-*S z`M9Zpi{|PDP}pW5$9pWX%jt-jiRIg>tlaUi31b{;!9v9Izcf*MoxMSLTIJ}x`ylA} zEX@u0}8yDP%9xYR2gx!MiJV zLyms=Zv+(S870;Y5((o#`?i9;tHDMODD+F$@#X-P1|Q%&=i&*=$!RmxHpVWA^f3BG zfs+JSucAW;Jdt+yh5o*Ff5yiF82AtRY}W z89(ORm~YqYcUR75pss#g`Kyk4c5}V;`)ic7IR9*)vlufB*o#)@;Wr-4!g z{GV5~0io@y+-LCm7*QOMNaB)UF3IP-oo8wBlCax))YbdK#3Q%a1~z!>MaDBiJ%x^O zE*BIYADaCd*(f?Eo^ReRX6zkii2vwev|oH0aKt>) z3*yZ!EZp7P5+RVghsTpRn$}v2sXyo}e2*)raUYd-c6Ph*DAV_L4i5GX+@K5k_5fHC zKn@bKtDjqO2+gKK3mDtML-zj{i-+IT#FszRP*~dnyb~lw5z;{ccXD&9BL{GIY^qKc*=ER{emVEeG0MJgJ;j)+70%ev_$4 zmSI~|XDTyxdEhI1aSJFi^cg2+)rzyg5d_?}OPIgeURLuZjd6)&9*P@G;F)DW-Mt9D z$xug{nOu7(%y8v7KMP<6ratcD{vZL!11hmpKOcQ&=57*_%|eugwTQVn1r-eJsWoe4 z;qQqpqF6SeM<*Ms?S$^HUmv5f(DJ#nwDzlIzMBy3 z9N@1f7Td7oC}+J_$31-h&R!AGxhGXTWukjP`15Asz{aKvYgi<(s40`HtE;=ayR-fP zjKfj?XySHd$h8B7z(bZ}o%lS?Z&L#V(WyU+z!q2Gs6~ENp+F#p3z^Z_zWFn-_wG2j zrGkK`$>ubbx(1sx=^BeB-Q*vhYZefc^xY0Xpo zAqbn`2m}&M6g;${K@FgFBAyx>p;MvjiR*AAK2Ggec^%S08bkA3>#e)EK3`Gq)V}EJ zuek6n`8_=V_nYqjhpM+u1!621@Z*{->{Nj^;C@TbRTA%*&+;JaliBD1%{=z!Xx zHjia@C?u!d8~`Rh{$Tb-5OISKp!4f&x>L8x>>Rd#MSun3!QN5Jta$NFqZ2ZS&zo!F zE0#1Rp$_NUB7r!2D-=w>_pHU5_MJs%XJ-!%Dn>do56Q)T4qfl$VU($62TY>% z{OCvtX7AK+4zVbL1a2A$x7}|yD*Bn!S8rdif*~-imc}9PP^#K4#AxWesYVG#+=10x_&KN)vM2{6T z)>wp*{{xiR(tw&)8ktJW>~nk2CLd4S@r#;WYLjk2+oG#0Us96h_js1VJXO%KBJZyB zp4Pw{9#GsQKKE<)Yns}N+vDLk;y0_omDXvG8m3>Sflew(ym^o&%vQcv_;hybqE2@) zVfao5W*hm*ZXY+8i=oP_&seYoDfwd6Rh zaz>e0J!S|aTuVy}P^_mrH|k=dE{%=`u%!!tos^W6EG>%|;n}qEzd5Rkii+y%>xXsZ z(S9Y+F`3fCKP42-t*i?Im6XT{J^=y2bG3XG{8=m3tPu*G+i)^#6eR9>i;wT-{zoo$ z-NhofV`YDT|MAf`@(o20cD5t<9W5vQNQ5ioPQ_Gi75a>JoNaQme}rs{$=qG zJ-dPFtfXWg8ZdaL1xhJWl_zcFQ?suo9c_38Sfa>S6jEsc@>nsIR$W8Z%*@-TWo=zX z3V0o-gTY9-Iq7!1q6!VfB&d%y^FL4rD@p}HA>wHBcD;M*G;mtFUp_+M)z6NrpQrtV z#kWqSQ`#`>0;9-q;<0G`S3f%3r~Q z9Ui@A@}RF$NuuS6Qr=-__c7Z3(jxhJ;&=DUP_ZCRb#`GPEiNvQl?4s)X!%ZjWpi^g zY>nna5bgK50~AoFvtmI_YpbxZumN5>unW?GZ5cB}4(~-sAQ>SVfI~Je*l8s?J2`$(b2$w48)^5=>MoJWx-F;x#83sD}sG^ zh-EZLf%9uCExs?CmH;2$#oZlO%=Clr#!{l)%&AMhmoF=^hj$kOpe+Tuf7Fbgm7Q<* z^LgmO#aSv;ru#Wha88aSY8Hams-izj+n}rGJFC|x8B6t8i(ne_h{ndM$@|hQwg_cE zzi+B`b-c6wczFN1y22|oCpW{by^ck%gr6EsMzZ&gT7}2bI848FTxOI`r&}@@uPj~0 z{kF~N2Yd?KX|Ep(|5FRli+82?9xb87l1#wY;(XjLgE2nH%t|+q-oDnuDEAg@Ld_{I zaMatk;vtHSOg(_u;|OF=J#-RiCjwGvz-k8F-rey=69J}8i+AeTqM)F#D&jF)zP{HskJRn));-#l+Rmmn6GjpQ1(DKc3sg|mlS-w~3vv4giglHM>MXIH^B+idq^`)8}qz!CD zrbz_a&l3C%3(S~-J#hnP-Ufv!J<$Qp_YbIx+1cyo4Qb3u?p~;Q95lcQNZcW+vm+xE z;l>&o8b(G&?tV)rxHO(#p5FQu@C`|{i;K$-LBuo0Pa@Lsd8Mh%4t~wFpg=hUtn_(`h=>w`f8w~WF zW5Rmv#MJ`qvFQK3gD{4H!EYxI^6KnVPEM8&@zH^2eqn(j_(lEP05mm#Jk&S|iOOVt zWF|Q-E)J4L33u|<*O!tCfp1vZ+e1FH(a=c6QUz7b9pFo_@pD%IP;c^?7&ahKB2=S7 zL)>ggO(oV!L`6Yy5&*oVsf7jL58Yi|8%Y$|82?pAq6s?hp8v5N%S}x+WR*rm!R`(M z<$uTi@OiNBz4s_T3MH^}tn0=0Vj_NWXa59tx}Z)fk7KCUy*7B}=|zBG>^d`3LbJZA z>IW@B7!~)FfFp|EW$D^@k^B|zGnr4ggHWW>j7MRbTuo?M)q(~peAu@o?%CMfY|<@P zHh2QG;`J|aKd!HvLJ~YCw)5jKrdTXRwgJYC~@-wjY5`=;&UujNJkbk`QU+5@SS+0 z){MW%-yO#cL9fXJH^AXz8AfA|;@dQ?k1DCiVv=~)d*3$zii++Ui(m&r6Et)R%(Lm< zm$C6XgBIS|HwRk6F^(IY=#4*6ga|(`EU51DhTJUKzoBQazaNGCfEl26V@3uAAbzq- z-Q5P-7evtP`xx68vIqtS>@UuB@%df;;|3#DkPvgdMKZbvk{U6Lg0Ml+Y+xd>e=83A z^NnZQBTdOQ#re8CZz=xV<+UP-0S|M-+ZgmSM)>h7c2Gb7V*T900yw2jENdvRyxNjc~k6(be7eK5F^1?=B-#h3mW>h?V;iH@8m~sHsv} zMSrs*T%8xHb?)GHr3npW2t~b5X*d0Mb_|fXfb;yM%>Fz1(*=DNCU8`a$lHvPvYsi; z)rYyII(2pRi_1&5+PVb%x1=+zO9zg9Miex2f!GOV;MGo9QEb}0?{5$RyB14Y^N@*c z2fQ7zBnXe3MYy%TfT8C=Okm3260jB{BZ_0pW!CPmGVzjV2%_-_JcAVB*>i1I7Z?o- zilSlyF?!w_*3P&f9^&%m=78H<_H5emsVPl$^%m#j<)APCS4_r1yQMQdNj*F7jZ1a@ zcP7YA7`Sm2r6AC~)}y(c|Iu0+>9d#jf}hu-U!e-7Bv0Vs$%~+$rxmsuArO5}%gcQ&rhx$q z#WX7`0cVFNoks8sp!`^I(7oJ|+Z#$n{P|hs3kUPi83@?Zk|MXFVrcCIEg&;@{{?WQ zQJP}G*iJlEnCk4*)RqgMgvvTtKbA@+7L5W>;i2_7W}2yrqg`BGnIwCrmZu-u<$cCR z(bfs@CcoqF&@$9-184w|3WrLOb;fQ^j6pDHi}|J+Fk5M45{*kakVd})e;bAjY& zfCT{r3V0Ea*qpnZ2w)K>zNBNI13z`X`~ZLa@#DU^y~biFF+SdOYeOa6G&$S|e+~Zc zwckr*Fdzzqz8L9pti#1j5rmbS^jy2du;S(`5zr{C&dxQ8{1wn7CPSmewb(}J@rh-8 z^8x3!Cx6qANay#?AYccPXFms{qk+K($}V}L>zM-@2!`p63Us-K zdKDuhi$XU6VZelB+|6&jM?w(IZfOxz?A%yie-WqvB;XkqgQ;5X`#co6g@v??ud1nl zXY68`8yP_W0cNpwa(;0k6Pp{fi~0KXHWV|YLm&jtpdyxj8mQMhj6|^oNMlx+5~*bH zy7a|BS%>MQ>1*+qa;>47Fa)))CqLY|+1(vzH3umX6Ks zcV$$`=F56(&tYcp4v33{_*61hwzdO*-fp!HWESfOxkRCZBsEZjuwR7JBI<;&;Nh9q z*1Lk8s*(IfzJj9N`AF>EA08Z%{_E=ligtH*92p0w)2)|SIitIK;ndnm!vbj}IDLyv z5(pP9K0dLuKRPJ1Hp6InQ;E9?X*a>?Xo&ytK=CpQ9_bJ>EnwRW5xOoy2K0E<|y0~w?k!a?pFif zl2(J_Qal<(JO+le2!e=&gu=pqdIn2Ze`A8KmLO=ZB>X?LwG9bl>Hq0hoOQmWZX&q4 zIJvkOp2+LZU{qDVmJNC=8A{o~{LeX1>J@g(_Omq?_YhEiPIyW-cV&-D;Fo+Qhq;a~ zjTI|F6@i&fAcL8B2Xgk(U*flH_eckvMCcl^;%?vIpvI3{6?#5m%%E`)iVRLXcoq$a z8wW0Y;yy?Ly@Mx)opEY=HIgBtQ<0xfVbS>ubchQ{RB7*c#+b<$3`1?zHkDdRUw|3rL`y94`q-@EMIxHP~S z0EMc#`pFGATCHH+@OTUi?x@-$dVE-NUvR-a++8Iiy1Dg~iN&k(x{wZ^`LBnVGKzeR zkc(st86QtHo_TVI$2HgLw4sj+=2!6cg5*(O=RKd@ueAfNm&)r>+FBO@A2?iSo_c*@@vkd&n``>2(?agmiYaNq5V-Ymk;IBDz|KI-t z>fr$VwGvt#^#?1!iHgF|RNhnm`>&_*`5y@d3F9j2te`;H2m@frzQ)cS|p1YS|R>k$ubaWF{SdEEOH}(z(R{7JM)fM zBir=3V1ZDv)t(k?xSC!kD4X3bxal+Hx|S~zA#0E9=+Bqv;-)jdCdC>0jx6ISW2oVgv@(*dYTsQlz$iWzh4h* zV;yd8L9-Oa(vGa)GGqJh^U)xt=PVh{lELF>4AdeT8u&eq)r=uxS-o*I8ATOKP^q{<89P zE?aGV{lz3UlT<|uAoBqnqMf4RM$pS@Mm1TM=HvG0=;$c}F9qzNemCa-Qu5a*lr`QL zn<>D#c|!sw(90%?2+%U{gQyF>VN>Yw-DGVs95EGIv&Lp`5p%r)DZSf+gM({WU_2}= zEHpF)wRsyce%BpGeouG8whOoY>_hjDSFQFuUR=T~Ed2al0431X)s=i`@t;ux2Ky1X zeORU!IN^oF#cu+Xw%jmW8a)TmcB_%I>Jva&EuGw`kOBW{(cT#bWcR=#MMZIg9k)Y6 zFfT4HRAk?XVfgylD-E^IJ0Ru#DJgp<+Nw+SmrK-NK zZ|U#dNZWicBQ~f)X-Be1<0Ye=QGHJi?E=scz6gVbO5S;T>FDSryaKYo4VO!)354$@ z6|mb4h{55`4#IaZ@Zix(AS8MgC^BXX`UFJXuFL{+F;}bB)753A8-plz*28xo&V6v? zk>`B6toCk`MbFc%WYw^>d6Uk}>@3>rXhs_glpr^?tayICdjQii_5jiPeM_OKq zhaIMIT5E(&l`uj)T7VSjv1hMhPE&oAxYUxUvTE{EYw(-n%ot60-{2-|^tZUR1|ZjJ z`A9*Pm=(VJ;`@VrD~1MHIMV6&8^2S?@Tzlf>5>w-U>`XOKO9>c|=ChrXYisMw&CFC}TlDR_8XO6A z{+v!%nZV(2x;$n`No_4BG3MO+U3;76l^;a?u*K&|yxGd|d%W2)@V(iPV`;$yDW>vd z#qR?LsbyP^fylYJ>qm~B-%q%`yE@VY_TJ^(DY*pnoW?XbDqtuiMgp9&&HB{6CzsV&g#cQ%F=3C__XAD>y*mpB zjPa{2PJZg@qg?u<{NJ&r++lx-#4CZ&S*$lU83l%A$}cAR=M*5ZrJrtJyb9q z$ZmnPIlp#7%g$XQ$chucP9Yg<83e}L(8)Cd9f6mzp;9zdng*sZJbnF?AL+o|G!=Y&dWI z^4!U-IL7DF;hWTmXJ0KT49&8Kj`^(Pm=(MwYs80ff9kDNCus1n#i2i>c zkx=nj^yh)vTSaBX4O&uM{23_Ggn)@|Xh>S)k+ozc{5*1K%;4L)bab`k(1Ae{O?I@i z!&YFX{G|d1wUo;CpUYCZ@E3gzJ#B5zC$%XCzeIhF#)~rfIgVLr?ThzKb&YK0={BD# zn3;$OD7@cJlt@q9a4Ic;pFA7v2PQp}401jzm(FM#G@esj{IFANsJN^GE78HqZ_8r# z8`k~zfZh|z61s&V#grOAcxd^b$a{RWKxk>w~A;W`@k3Kxbdd} zt1GtKYs#i9u>#GK$rnGps};FQ&Nh_wxcw5PH+N+M$bg44@&=l z7t1aA$cJ$N&Z+0?pN?;tr`2+@ZN!)en`0WD4-b0~jY<2K9`C7orbr3dgrS!NOJHsR zW0=s^)I3JaqUWA%{UX$PKaFkCnn7%9at?5EFp|n`|MwTRT0SQ195*>w`(?KLDJu&- zb}b70R(pyH6~#3ZB7#cD-pR)#|eAAvuP;kNJs;U1+3;`#tOj0 zSJq!viHDFz@QD8M`g6FkNHXAj=zUt$cxPYBPwU*OWizegbrfy>Z4qpLAoT1#x1+^( zt7m)N^jkPiQDc|6QZIN=T(J5VX$Y{JS>R5Pug)2yszA5=l=Z*_dL-*7akWAC+( z-Jk5$`0!F;lMmoN@qK~Wc&T2|4h(c&uBVz$2SYu$|jb^VO9vh1*z%YXX zv1G~j&KBfxvjN!}NGTw=JfC*dG6qskkj!V%FXt`*_Cz^%dYX;3d{DnMxyKMVM=0a; za^FMoN^M{8*v|68tn}$eOiffDcI{~OdIn4OA{;2X4XNbHGPmH;#aMj2H#H8p$tqaHFp0pEL%ilMC&4**t^lZ$B0@>kW?(|brB z^3E@6Y|PGH-7Tr`y-3gmkO4ETNuAJio9u!Dsdu&cIh_b8Z1!(efGkO97(OG9f6{0% zpfP3uM4|%aZUvt0kntX04j`sdp$Vh}f{Ozz>uFu9e zPfc{J;6mr9k$}{5*`KDO-peH2rj{xOeRqj(`u=ZhUV>MBG_=ZQ&g5vZ(bRHdv7R|) z?poJ&y*qFw$K2MNsLsyoif-TsQf==a{!j%Q_q6jjGsM!e({&XEZz3mJdNZ_80<&`D z7NLi*yAdhUu&+S;BNMz{#_~H3{6gdo)ipCS<85qwZg&N^M-vmErSDbK;)JX1>lpj^ zHO<70!NEbmteYQi_xS7VpZ3m%?3e0nhe|3YCnkV2Xn#XfAN+7J)e}tuKl28IZ?-C$ ztX6Yn-uN1ztl_r9v@*wm;GZYcbt6;>s)_u(y5`K`@bTX&Xh!fn}A zp?yDD=oW!ewf6m5F<`>VJK01K=Crx%`NftytOVvJ#~m|=zxsB0u%7i>wg&5o4uAOSk^O&hLYcZV%U zu&nwux5j+=wBL}73gk)S$g{Jv0>!l5NH+iX;jh-ZZr_W1xA{QAh%E*XVso>zzf)3x z4_ASxmt zF=-Hx7%+Nt2+|=+NGnRm=o)Nvcf(+Gce9PSZ~T0Jk9+Sw_i^`-$=LRe*Xz8_>zwB~ z=XvzA7J~0gS|T<8Ls*GY+9gW(n^@z|zlCxD4cgc3B~2Qqj=ZO<2ACIB9)RbYexA$J z7TZGkML5JzsKQ}=jdBKJE%^Nl^|CwCvrGS6c}G-4dP>n*b>g{Qw+!S=?^ZJH}_B1FZw1JHtSy%bZ(fWspw9 z=ekKmn-X6tvDG?J|EsAa^_$&rxBcGYiZ?97uM%Nr8EZH`g)I$ksl)L)l1`{ziACh3 zUGqn8uUQ|5kvmNBT6GSVJ!a4mD>v3~Gj5={yv~WmWEY1WOyf(iT$D61?8zw7-J*A8 z#h24ee%QlvDDukiH@dy4>KG!G5%n#sJN357UA%3j_*phbXg=%oR^{jy{p&@gL&LIa ziUnB^3tL-W9-av2ms@?b{td4QO5rDd_#IYxGJtQZvoT-0H2nt`P&>5P9?j9|0OTAl zK33P`{Bf~+%2y-DZb3lF>6S{CsrkD&R$ax*wR>ie+!w@ko$g2cQ7n3U8!8o@Yd!Dk zZEa~elCM3Xt$meAwfZOO(^ozIK*oojedO;>pY?J)iRWcDUwK-o6dY&t1E6Gj52T(L z-`kQD8zFl*=dss9+m?HMfhN)k>;B6@bKG-s%45LN^iLjG?;uGk2)U@;8Ns zc8-c={%HUJ3dz4XUEQ3xzZu;imbMog_jzT}bn@nZU_-RUIt`bqV~v6HvWU$c6U`I$ zYrOFrn+bv@p<#kH;{F0JZwQI1q(0;PA0=Z$@b&Bb4<8Y0o&pUw7)t3WdErF=!$_{E zxmta)wE}w}37h{1iAiSVdfff422AGs3q<$0m?Gp6?|VL|i%V#LXGJ#GP)d{0t&la7 z020{|nYONn|M50*kk_K;aTtsMD@-DAIFw=amz5!=_U8Ri5O)J|Wi$I)LU2rS>9hb{ zQ?q}X6?Aps@+f5R|L{v<>-m7{gnsi$WQpTzQLVQ;xua4u0@qa^>{P4Q!7Ivd$kUka zS1#9!qaXVVFabqWsL~*E;(wRRA$^7dhppgxDv`(Y5`%1bMFVD&BOxC5>F$x*8!gTkvHi&ED@tzoANm;X^al$D%4{@m za2HdobQ4EY`2j3j|ND?kwGqId!fPxXK$RvhObk+0`s)vn#8fY0Ge7^o zh!+)AQahb4|A&5>`jSl*>c=a4ovYJ&XFSCOM|0miFu-+}`RIw5dB3pDRA%Y;Dd(A^ z%rfH`ZdBzFvOh=VFdshA^balckAA!_zHNT@?ahn5=#6s9(fz5=JP8)P9Ov z0&rTPZW-5*7D&E#>Xiwcl~^jkVxVrBGM3Bv({wvmqmUA8vSySa4eWe>1i6<;_pSQJ&{0Yb`h_)#2+j2qknrMVk(8?|MzO!rB6d{VSTZkwg^$b~ z0*hPMO}P54m3Hv1t(F%$FllyTM1^IT_Sv+`t=89H<@yI{d6%~yE>o|WvAe}B)O~rr zIaNdYi{ruI=I#5Q&K(}WmC0OAIr42z<(#@3j#u9WId$LxDAot?@{B73zZBFi@o0Y1 zc*}v3ant!#R=@U*OCW&n_cdR+V7ctyvO>aq=4o68+jNt|-M3z~3cMlrUC;%~+84PG zIQK|W8~;}N5hqP~>6FC^>ZC2e^~Q2qQnQ91q^_eyx^s2waBr4y)oaJ={whq@#7>*n z{g=X!mGhli`t~fImVKH-p72$|;mgIVziWA!w-%>Cr`AY7TqxU-P;PMk7bgSnTrSzv zHQP`8ub#1X4yx#_j;5xrvEZMQE8L0xbWL!W@=!Ku$yI?y(2MGr>pM*THuXAxat+Mb z94Kue;^2hAFMflhUP`@W0)E;zs54cfNLK+>OT9E!Fj;hwY1v=0vXTdeYuCB=taE5G zisYj#18dv|ax;b^q54;UG}nde?bRtPfo5m0-KlZ!7<=jTV?=j!&I8!Z&Ls`SDOxZN z_|TC`IDd|T?irs{Y1Z|(pSyX3gzR=ZMu6FM-R%gPQ0Osw@R{$6;quDxSG@dNh1bW9 zPo^$i3-NF!hWkHet|sU<*=Hxdooc(2)`#p-P}9Mn!~Kk7AY`Ocv+wne0K6!Ptl_WB4VDtD1!n-F^IY}gEsnZZr$S1jyDFEDMn5~vx zWg;K>J?7m5=>iZ)wQjfbj>QcjB9IV?&acHzJ`GA2!*CTLZ`52e&j~fFPfM@1uJ?2< zxu)WGQjuK}9YL?>KpZBnN#jD>U=%NZuZQE;C!e#DQOD%fZtMOlzUsx@lfwcCjS|cNjES@*dOZbkiw#K3{a!j`X-rmt?_< z=Ji#cD1^QG5YPmMBI!XFyWN5}AQ!6*Bm}cTvZpUzZ4OH#?{SU9>=fDgIF^zv@C%5$ z-=4ocI9(UoHs*IbN}y)Lc51W&@IQNv!7=(MqlcjSZtY?(XoTb_T(8>UJhIi%g4SgC z_uAy>b3JKGZT)%fh>rzR?IS=OlN0Q1K9cSL@?A$ClHOe4EK#0oc$7Lq$Q=tF5A<^3 z;eRCnth53{d$Ttku`?4?d!08kr_1I$?{FQrL>h2a?@IzpV5Z3UJdk{*y2N?I3IUqg zS1?Sme|KXNi%kVx4KAR$-WKZF3rdx`_ms97PbzPSu6BX-!3H*>4u!ywi#7@7jpZBE zEze2YErp_U&-tu9T~)}Ke6*soNH!uXfsqaIzl{)7_rl|-!EvB>h&MJ?8++fm!BYn0 z0*Rf6ImY}SmV}~r9Di$3w1wD_-aq{D)cabl!NJR?_eLNFbT0W9ddjD{eiShjQHaWl z-)bEL+-mIVo%fqf^4IlEJ;ivsYz$;`@lks?U z<7dd#t=bGJhBMV)HgdOR>1W6cYf`gH&PPqAR7d-(m0F59SV-_$i zJH063u!v${=v5wz0q*wlqIvrd2qZ{5PO_)7;MH?Isk^u#a}b>naPc4=gI}nbU{|9q ze31ixP!c&$EpxiKZXV^oo{dnhYqTaaiT+^LntgwCw%uRl!60&!#+tpCIzw_R^9Ya* zm9spWbKJ|O2PMEH7d#O3^XJ?0KMpY8&#yPzJiPfgDcax)s&A-Gvf*O|aX*OH$DI^6%qD;bE zPF}fsx`kH(gpqFwW0s6}7_yPA9;S*MOi~W_K=qQ2ktzE&Q@=?~zLso^<=1vHdYTP& zD#Ncim*>Z@czs^#jO9)fmpJ$=LbZK2*k>iLjiBDv)zjHo7z7-EiQUN_!avVjkYj$g zM(pbLMjWj0f?4ZZM_V~YnJG$(0x0r^1%rDMF@eBlP#aY+zES?>9I}YYx8E5FdWbV_0UjCgi(1OtlnNn5U&X;VzW{rvFT4Fgf(hX6=d1 z$w=i!+$jv{P-{9_Hp)Tr12Zke!(MXf{SO zQK)g!8K=f8y){i!v7mSzaQ2Yutfo)W6Brl>5Hw8^Ngyn#6I_YevUulqOII6Uk~LcH z$!x97Fauc&O+AY6U*80QF=gIO%5T-WQiH~%e&4)#*&Ctj#TaKhUNrp7ucZgjMbA58 zMfoI3>{rMbSpzk>V;B&=Q6C(J0*}Ctmqs+-cEoaz8s6x4I5X`idjkRi3c4xwmA}>d zz}KIr%H}$g%5F+3LD6<*H=cJWGuj?(Jw`rr#|F*#>uogVZX}8#i}ti<0+WWPnSHb_ zB|uD5<>p$myA5=RgFF-I2C@3M`MhDMmZ{G7_DrCUOe3?|SLz;paWEt9 zrBv(N+-G6k5#Jl7UJCK49J*goIc+TE=HY$zx|l3K{$b8l@=ww#94@m23=rGYg#7)8 z?9p+iYr8LZW0jvC5D}e8iWY`QoA)$4(qet5I&<0BI>IL}j9a`b;!*NA+|qZZ;+ctt z!)}N$iA@+-k($-h;mBy|m(Pj{Shw(jlKOH&!kzBHdtHI+Uj+#fhsu9kXwUU6yv!C< zwxU`m0jYZXbH(1f5K&nvc<;QmP9=^4fu;ZL=PBc0@JT@ap^(ee$8SeIDd!0?gdW>9 zcw3O`VYK2itLxW?YGDH`4yHI*_9iKDEIs$TXv8Ri&FyGP(hdXoN45K!8m|{x-z>B# zEwtVMsPsL#`QP!Gbrl+xf+!L+)*Dc$JJ&!vnucMFsd-@am-L;5sB_#i9*xHFicOrb zwJ*TEYSjGOb3k*AAuO&Hm=ecV6m2|=q%a(U^JN)5a#-bc97Pqg3PPW6#q8wm%VwRB z@2R7h4R;)OmGOzmL;owR>fT5mAAnUkZf$48Ed{5(wER%}1jxz?-|)aQjq?@}a8TTr z@{zf0Wn_B!EtWGj2FQ%EzNoAkKf*!IftE;rDrwV$sworDq6Nt}K-{7|8YyL=C|VGx z7F?i5q{5n#b0+TCa8#-ku~gzO>5}i$idb7tc&jG|Ox*^MbuoLFd+u0Sw6E4`DKihm zcG{VFw3%{sW8=Sj!|cx{gx}ySx@f(iUum=n;6ON!myH`x>zPDPx4SuDHwNE7vo>nUbVrn(fKWPesZCbR2<0!52*n`P*B4u&QsF zkjEJ*!51>ZGrWFqNJV?zGRLOBjKp@pW9RL z1)4R)6pE2NYHi-w5_6vP+X^f(4!G^W_Q2WsM!bYBtNi(?C5xNDx~I$XPwU4@g10_tTiEwt zLmJg1>}njaVc{;{@dzzD=0uF+IW9$t8qma3-;SsKmDb4bGVi4CIx z1i_DrGaU?6B}HsAi&BKJ`y4;@S-u54&-DK?CA?4q`c8O+|Ic&oVIstJeVMw;LYoou z0q1Tz>Oqw-_dDeK%0WqNh|4)|8yvt6R{DcA>o~q-Ba2&4Gx?fDr66%Y?#>D8MK-5n z@ZssF)E{ACP2MQ^Z2319EAQL9P98t*4sL4_6S?D z15`D5&qfl~w*esE=cXYakon;dnoQ1+ z;I|PRjf}6okrr6>qK`V`p~ z3{Z;d=mftr{edaBn86sh9a~>4c9b#sEJQwHxJzr6I`C=bQ9_g?v*Cnxq9yl#d4SCi z8W)rZV%(k*|M9uR&H6{0v`sOJt|xT8R#OXD1lBh@o8PxpCMH8h!@c;{%t%4=nN`n%w5hS05HsG+zF)(FOVi zn0<9{Yn`j_1D5#Qs-V^g_Ki zsMuQ=Sqi-=#Lq@`hda#LK4 z_(wO$S)~6q&-PkfTyC9vcrwAf_%2YGv;_T*{d3-~;@G5x2jn5p22PqcH|{YoI==p$ zPW3#KX^-2MEWKYsl|SMo>e2R+->)D$p}A|4OM@(g4i>?8C2s`i*>urr zBMnVAN)QKbmlvnq-Mxjpqa!8BV}hZzi76@A&CTs@4-7uF&%7%Rgh4OP45<#W=J6#0sKJ}ZebNluHq~RP$ zJOzGU`kZ(>Ewo12PPs-WexMt2S2ypzXjhckUnC>BMtB=E#+!WPb9r!?zOo7A#sJ|) z;?nL*%xGNK_2Zh2FQK7RjbC3VC~SvvJCM;XKY6m5ERG4e>#duFPbgoU)F$gRf1f5Y z)Gh>3lIAlDC(XBNoh)P%^TE9XLm<7abrNjgpBuuyUGyHWKD;W?LJ8Wew~M@KsZsX9 zM&|KjbJ3UzsiQ7tKG^K;wqa%*>VO%Auc)lsa#lo@S7SLf^3kq+$}4I3xdBcT9xXcU zgcc@)>${CCc7XLIOr0eIb0^uaq`)dzq`BE`yJwOLlMl+_Udl8Yyc5-qEB=({dh*S| z!PSr>=c|ejyW@UtAive4_bWmLBJTDiPfIJ`E=6Q<-j->(hiH~r{5z}X>8je!sr@DJ zJT8D9GWk>JM#5#;u2N=oo5G#xh=E`xSaUIdRD+jc2VDKSr8CxbJ%=k=x7cgx{2ItUL%DR^0l{m}_FXH#zA_vu&~KBqXa7lkODYLAm~_xG z@gp@^2(!h>UZe5Au`Uh2X&$z%yzGtw_8foh*>HNnsR*;KxFgZN?PH z`*G}v&)h|t4D7W$8+-lEOtI5QuIBX&irn1MTnd}^`Ec@Qei4#3?K}p_dHLP?ZW-b} zG9#0TlPKA%=Ssej$#q*-5=!FfX>mwXTC=mYxboqg>Uf@ijXXS<@B5uM)dJsIObL^U zU3LrV#GZ_L&GLjeeh*y|W%hE>YwA4Btxzg;laEhbV3obyO=={ttHRGAcCmMNVfrI` zy3%W=&ErV-Xi^sv`lHBDu|j2osfS;(8)5x^J5f>0ec6eWwX(F%qm12YP@vk0lKEz7 z?N@q3Czs{#IeK|ShRw^Z+#+8VSKA)iduYAE8JUk=PL}Ra!jb#wHb}C!29m^MJlkT| zWDDTH2I}Tc!mE|HVx=^r*a#LjP`Z|!aUH~e}EbN$`K-wJC0WY59RZdkF zGFi+Z-P8qvb$l&AZHH~;0egCYj2vcM;f7oJ>}MxGT?Y`EX6$bl^X0(w-6z}&6ij;Q zW0DSkf7Q;l-*!B!s#E<58PP|?r4iQig*!>#7JaHGC1BP}D|jkmcn_fd@pM4DiV~hv zZ_j)q zGe&`a){6~dJ?HYhJQ{&VYN|I9 zgj`p(U?uv<7Vb{%-+gK;>F~g$0Go$$U>3W!w1mk~zwjfvxH!Lh2L3hp=Ma&co#T6o zP5jFDNbh0Os%GzR{}EL?W*cy}Zjog-&^VqA(~~xPJNXmJDkY@F7-_-vBu|v)RUJr& z!OBVo)uXTT(fOptp{$bmnlF*f7MCIHByrF7pei>PVwIa$6HErr_x_c$-H4VNyNn8= zEwuDHSwy2sSBvd#-oCA%2Hk(3Dh!lzMq9`o7-+S6RR!ptmGiu&CcK40PL$g5PDW(Cgyc!REao^*tRIVojf?v6XF5;7wUdYVPP0KcFw4+|{rUv?}Bx@_nu?;pMy@hzaMlmZJ zOqm;A!Llt;vn%M>7eu-X2g@E80!{VoqCt(9Ug8Wj(*=_wf`h+h z+f^Cm$mz}T2WC)2n|f|QkVs>TXMR07M_I}T=*dw0Q^&DlXJ<~tEYFkOEy1;oSD9Qj zF+B*?2)@jB9X%;Y-dO6iH-B+uzt=CX$YXamFtcp!157 zi1`P$IV;Mkf>};Wp@%z|tue2u%Mbo;t22W$XUoGV%rktHh}KypOs{S;ZeyhzX^bn| z$Wp0uc&pv=_l@J*LG^PD9Vt@x{AI_w)_WtT>zfnREn$vfG;2GkM>g-mRwA&k9x&AV ziNqtH!n2tUO(xM4_uhZh7}#JJji7oc5p=I(Xsh!K|`r7E7AFeMDhqk zC%BNp5v{y|78$e7&cU9E=)O?z+f)p0F^Zl?c>O zRrfT!QQ{`Zbv}A)Ykc}+sj<2vw@zbdQ0KZXS_GO6h*xQ8`iX0}HXE7KEH3Mb;W`FY zW?GCn+Z_Df!son$y2yF!BrjOS%Fh0_3bHkc7%kk|!JHs>ymq{1@Vr{OH$P1py99nK z7BMM6LDOa?*LP;ptZa@>wj5KA5;+usa(h0*q)iW(z4>mQN>i=;_9=IW%h7uG{t^az z!0BPYE$t? zq8VL47H$?E9@x;4*w~Sc_*ggso?{77Kc94Xsv8^bshcGFm6NrgfCQ%p#n#|Ra=HED zh{2$gdxo8(ei^82&m0d~-}ssnzH~p@BC3vag5JY zr7UFY3*5U!(zRdKp_xf?nN@oIEwj*%4r+t8xVpX8&G{Cn+x!hFyfsQGS-ceGagllS zT>Dp$1fnoiLR@>x$h@0$x%wJHAuu|qxAfRI9m)2vkmP9?CH*)enf|@yiT@}(6dLg(PvL1}V zi3Pp3UIAA9f*%f7uz}NgUE*-U6n6n$YN|XtOzE47?A^mi$jjgDk=#Kt);Cc~I}MEf zyyYgm=ON|?jUv~G4#nGvxpYgq4<}2EARH>KdlFM2h1k};g(lmRG)-QsDfD{S=_fsB z_+G6MrdDj698Z}l_(;@8#uK%&oa7iA|LSI#0Rq^rbpG4(T_EZONyC5{5E#f`2&l>7 zj{_R$VGUh=-ZNB#>b;pCup79Sm%WsbB`n{{>|0pZ-8jzR%jrCH#)wHo+ zex%IpvSp>{CLRZe7f$WTrwJN5=%6V$q(n)}E6WL6#y}O7<>`%@e)47w*hog=YC!Qp z64d~+qykx3kU9HXAwJ)EHif4$>Ll&N_``RvX^1l{)K}yWB5;R_pDc zJU4V{{|MiFjie2_PlBXG*3f=|T$J@n+Obi1F6@oxTk+a6>n>*|m4Nk+`V7zJUo&jJ zdQ@ShB*psU(TZMZCI6x`k-6*E7G~hj;85wg9IFog2p(mcA9S!*fhh&tJ}$eytG z!vaD)B6Ol-At%QQSvUEFBDdIbTvmGD)jM5YUhb?Z$)KZJ?XG|=RBZfK=J^%T318nX zUqUeG_Um9ZJiKi5HI?MZp5%ecrG;X$z}otixRAhQtui4`@h3 z*u8zixe=QX2NIv`X>r%WPL~;E5ej3{Mu>(Ql1o=keXm>3y)EJn8zMmr5k)5>^2I@-k!|)(h_tp7$39&kVJ=tWbs@+O^ zXdd+4651PmwQSurdQ*FjWS5IBdwhB|w2*k|9eOu}dh6eC)6d;SVKiSuFkA~{kaMvU zfE#1yE#D-XRn~dE;zB7XWc7~Px3j`GF$`7g0iW#0KHuOWXVf7s-Zjv6DXS0?Ejx^l zPaBfLpYEiPW*V>^AN9IAA|*YNMcHo;WNgg`)!rAiDqchgG3w;VP14N-=WUl)>%A8) z@}jJ#$?i-RUt39Ijs1*re1VOMWR8zF=&Zx`@D3y;$pSzs+WfKsQ*0vUmaDjRYW<15 z=IJ|kh?ep7hrR+8uqNfl=m+hH!z`ICYOFO0cJEWlvljAuV+b;1ap%sLrK5mp>Qq}P zaXW%$>@gHOiBrlT6JcksF>N6#%gKErghTH@dXX6sZ|FH5snEoT9{$)ALy)B{svvYgX05C4O@VxWj;9%T* zNu8D^n_7$i6Q`mKik_g!srK9Ef)jW8HXQ5cOe3HjaPOt4W9rWdjL>cauX5P_U)bVn z`*xu3%$@6PD~>OEC-X<3EMl&G?B3dM-s$ZTws%x6zzC=rY=#%mAP;6VRxa5&^kvi*z5g#naUd+=fIM-F{@j`5Rt=`*= zTJ`FPp6N!$B7pGv9S|*g`GeLR)E{Z+AO0x zQFC(A9eHul9mcnuA9uD6cG#qA{iXgD(1C|!e5WK7u8L8K2P@~JTFK%+ibc$Y^NeSc zN^u@!+^rt|cW(o*#i{DoTpfr)^U?b36R)Ov@DJ@-vTB=2p=61#>RG#oL*=Yzv-)EO zN#4r|8jy{cB*PVzw75xxsF-`1)o6aPE5lB14)z3A*Oe^30GOTy1=V`J*AdVZNmy+q zKX8KE`gf)jZ2ZdE*^ObJLro*#U4&gmh(mH=?*+z)nbSaojl9nL)b8BnO*$I7ziB=) z<>r)==m}-MtM48y6VC82kb&lFq_}vU>E6haY8|m(`vbecje%OzBfkKX8q7;%+$u^c z|4zvJYc53%n1mg8q~Ew7y*Fapp$f&v|w5E;)?_ z#$Y;7-7{q3Ew8pGw^E;(bq;1If%Em;79vD@QzTCkA%%~DV(i2$1Oh(-Hk1bP#1Hyp zFAvpK>Q3jwR(ewycM24*Ci-p=nI{o9Ns;Q-A25jm z9X2v_<`fv>^AjX&da_(FNb(!{40|vJH$-`#}Az;E*Rb8q4iP?LO_>amUOyF88J^TKxZ zN#i#aroFEYAf%T4^0(IgV8p(HltWTr0%jK;Esp{~ipw9sL5NsSpG%~$9GTx|hkAGE zVgQbV%R`mR!xO`c{RBaCUJT8Hf78ct%VIq`(^nTIF-lessO8zj%~oxY+)RWI)J)94u(T=ZR@^=)7-j`!R@U;XEE zvEt*B^6To7ZWBD$YiHCHYNoAR`ZN7yV~Y20ZEbB%&RR`|QqWi`P(w~@==!@fkL>xx z-#0)Fl=u2@PSeNU!3{Xn$|^S}#}W!fiRpg-cVe|YPHQziMhc_b%RXdE9whW9kq_3- z2co>ow(v8T*ghZc!_@)T`C7LF%x<7)z6T!dgRk`Q$R?|`T^j@_6K8yOJXBJo+yq3Q z>X*=H@_amOz3y0;Mz11%(U?OdE9v=bY1dU0@B)Kdzaav6NWXYi*35ltu{VQyV_rC$ zFXJx^FMDQgajaj7G|(Ap7dmrpyMu^ik;c^let^de9s|@x>7QUccBZNwgUS{+;&{*V zP`JFP^NzGdhw89u`?U}1B3Xx{Q8UlH_U?hqkJGwbJ`?=y`~X~r_zErojI!DLVd$QD z;CkXJ`s3-~W;Y~CGHal^# zg_!Zetz{)8CIU5~Qh%3boX7Oju-)cBSyh`8WH2M&`u!U73H*I5w#`ti9I=Nr_0<*0 za?;+408a1LcAm$IpSm0F6$0oLC9Q&T5z_g0*;N|QsXYI$I@uAq_j#$Xm~xk`po>|` zz{%i>iV8~aU4Pne_Y|O?1MnNGH3Y)At|N0u;=>;voXpEu>3tTbk>CT2>cZv1nt~?T zHg27H$HM(UG7;lB2HyFoQ9j$jC++cT$u74Wb+k|!4pRb7Zt*U=JQqMamK>_t+ONOp zRjBmVxPE&~BH$cHVbq%}?t_i@5nrUq_Ff)Jvz@Ml?owJ#SMCU=T^x>8+A^wlJe zl1{RKcV<@%)yK-pz4hJj6@6pFfHym#s(!GTn^|D2A>pFKAOdB4|L9)q$cUy1x+DZL zz(@-^9T7Fu!S&{SSA$yP<{yJm&I8-6D+GGy6~CzmsQ^V7@XJbbgjk&MnqSWZn;j?l z!_7tG{y)?+t_u-6?qi6+8XOxb%ULHlaO{u-wI9ny8i>pG*sfk_WrQ+yC&!NHTE!EM zkNkYS4cNrkv!X9RC75=d{j>rD6O=H~@w{6Q-ck;`cdMr~DSEALH_C%!`LSX-djj7XpmH^my>(q?z#> zTeG7$E@GRRW2|yd!E@jRel!+%x2oEv4gmw3^vIDliNm%q<(?A`Z29+l;g-fnaRqZk z3u|6aeU$apw;Vtpwa{hdaj^|e@mt;GRP=z|F%Xc+a6Gng;Tw!d=h+;25l(lTqm_=H zO$n%plpfybPBx?votFF_6xL26v+Jie?#d$xBLc;Z6uOrHemmEb>5-3s3!iBZ!`9#{ zG{7_uLUfZ@C%&E@`xjb&5_cK3zCur)=1qUlr-vJ1Z%Ta>tJ_)Hu^6wdV+Og1j#?WH zqoXvmZ$7Umok1p-rc_&-_x1q(xO_akC`J_N-UITj52oX2tpZi;jKh}mwW0Ww8G4MB z$Fqd!GWT)Hs~xmEK``j}XXiReVq(Rz&-&=>b6rVL8Pvl0EQ8`b*^4JJGzMo{+Qq+4zISpz5#TBa;?*dY zDp^8hGM`QugHZN;&U`Ypm3>vm-4~7?-mXS~UIY=7MqX*8^bO|TR~Zz9(P9&5m~a#H zd=yIV(E`v4H0@eF00p-L3(#J=GWMH!P!>E%@NhB+_1Zm+mcdT(CNV?Hs++I+hu=veAPHnnCgbwh83aIbZsS1e)olK~W1>^NBM zLYEx{!Mwxu_%U$N0L@snX~%b!t{UlSd)Ebmn{Mj&UIAXGNB85K|w6wOU@{AhK8X$ zc4r&zOIyt~eq|J~rkl%!*6y{Y0e!Y;f98~9F^S8_9V^T6{=u=~#5g2PsioXbi$=tCkHCgH|CUUVNyuR#gI8Ja4W}(P{+QvR)dhX77|Ni{fmPpPLCV2@C{<5>Aii z0tG1u1MHz2CF^OV%##WymC37~Bd`7rrr96uCO8x6lyhPn@#%N!&$^j)q2 z=7y-xfv6%9qYy?D;d!4!Rl9B=f}*mvBFA<&zW>U|8?Xy-M(`#uVu**q*-yT(kp$HJ z%+VZ~uZNAtXHw^!w<{J_FDI(_;D!{VSeT6}02#KPC|Lr`YBvon_YqBS1n!tA6qws{ zO*LQ_yFHMtPJ0ykT=N@i{YwH(#zkwYYcF_73G*soIdaG+c4pY~DM0x-Ko+ZF=-y~EFdlmXh`m_FEtAy zYBtFok>1|K&0nbsXr#hHa`e(Rma($O!ZALz&cBcUb$ z_+Y=a{)A13hq*=wJY*_zttXh`mAzUbyyM8~&vHHf;~!Bj4ZCq4G{LAsPglw%%kkO*FIP$)E$upQ@!83tD*U|7Ujh3fn?+qt44X-r zC$R8qrUFLDI>u?A>n%XSZ!{UJk;?zg#{4cVb(}1-uhX(76Y`T9$n@bXg*%cdm}dG0vZ@a4~NT$n3P>b$H*n88k>xh z!7_E&7uN4=zr`7FOH8>KEkdcTZnnGX&Wzn+Xc41feUuWjMuFyYJg?JuCDhOjyNl|+ zs*JAK@$qR}h(aCChsn9mIxEE&R&P}}Lw)d9o^ zFLY*!jtNz2jR@Q1d(Q(wAm^p}Nt|YcurRqMV3>@7!4*Q4pOp2_Cz~P|MaqBW+Cpqf z-lt!M!-_nmMWExgfUS#*vUL>QHW->J`s0dCO8;0~?xt>{pw7cEU3sPn*b#u%(<>#o zQn7ma6H}9uGbTYbt+|LXX50LUGNSB!huCX%nHpMf!=kMN;hutj%2>xgjyXFwPrt~^ zQQ49E3PuL+wb$_)Z6n@XMH>|~^@o5EXxsJsElI`32aP;O)KNm4p#H)Bk7`~;^aFCC zBCp}pOMM!E+y%Q2Wp@#O&7};^yRJ$d5?HZnGrRYYxPN;3d<0;D&; za53p~pD5;a2Hn&tOxA`CPj7gz>}20J^*82hCv={b^h#?(dh-OWEy-kxb*%L%}~$1JD60q7Hv zb8Om`gBVaC>wR__k2=g~gKduQkNJ2VjeseS<^Vv!8g-(nHCEuA{(C~-*ZG&b(s_aA zxIL)d+-KU_OQs^7X{m`Ebu->afJOH#8`$Y3Re_;EIqSQE;g2<)f#?RXR8upU;xW@b71fRyeNoQhLu41yNFB3CsK#4Ux7c!yKdOq5H=b zM1{LBth=bQFYooMpb2x=C}o{&!3$oWd#vcTKe@Si-1;jwA`n2hWYgrW*%8hRYxOxR z*OmjRb7jqbmSggbTwIgif_R&bl}u*pq7z9ZLg>CyU+o4dBa z09M@u@IQI(FLr2`ekOPDny^0p0eDou<$_+FSTadCt@d4P_8~8h;8`DXUdSv0lz4oo zvwe^tV544ROsa?@s^4x+{5;mw_RQ2#=Sp;R(Phq> zCLDlcnLYi%%V_8~VtT7an1}pa%B3BW{}FK6m_b+iY*Z#oYQ(@0QJyneU^Ef8?w^!u zA-CnFyfI(CZJTdtVYzj=w#ZoFvJjLtdn_Yr!mT$*8>AD;^5MKWbJ^A3det0J4cT`Q zr8)drky)5S!)pZxV#BCoyB#zzpH9^`i3|f0ruj3t58zb}0P1FV*2?jMxBYrG8!6^! z6fJ|F=zA|L4ZqhKc6oXPN3AJC85Spz_~{+*^&e?Wf3o%~eNJFkJ|nUC!LbecVm0c* zB3x&GQoTC!rligtFT<&6mz@kMc`Yq4pOr0gmAv|2X3s6x&sXxq0r1h?f|*Q|n4R~O zdYst8769vfj91$sR~LXIl8|^J(5Mb%%g_f$2`?M@1x`R1bTkJS^q{B+o3`{ zfqeJyU>1yg=F)fB0Qcnl(Q(URc`Hgr!iC4JASWMFg=F7bS&!Y48`kAEo$oMLs5 zYUI=%WJCa7}v#sb>1HY0RN4ikW&0ImhfK0(=-H6pmq+VXnQ*9ANh2#}B2FV2M zcu@vX+$SaWt|EycY0#CFygY6WX`^7)@wqtrDEjl?Wm4R3zG2vRAk{}ZRtiT{T5S7u zxiTV8TmC3UQSdi~Qgh3kHc&=R|G3&7YCg4u?nbfIE6RL1L9r@E-hx^le3|WPi}IOU zd;{Ioc?XQRJZ+df(Z?#nne1T#pk?1~rPcq%)Kx%5)rH%k5v02W0qK;M)*+-3hHfOJ zyBm=jPzjM%kZ?$m?hc9hX=y3x7`l0Pz_;FGEnPapy7$}@U+ulWE9fvI^K}pKIlQ8x zU4cMr$kT;vwE~6HH{V7#kOB3fYI=JM1Q8h=b-Q;PL=+9cQ(pnBPlR0(cC$#kH+^i10n-43>WG(#x98{249d}JawY6o$!D5Oz%(zxi6?b0Li%PW5XnboT!l@<&8;g$5T-7 zt@Q00Jm}ioO<^&Svqd@^+EQ{UNa#~_B& zhu*Zb<`RY$w{cEd^qhq19#IsiB|pq*0l+3;4<&tv!Q^k>4IA&NwFx5p8Dx1**Md)M zc!64d>(BG%+n(U7o*DYUBVNE@&^Xna7sfRsx@m5gerC_ztmgt>25(;mwFLs0492FP za{~&<#FaaMb|Dy}7MGrCo#-&!%+Y2ITSV|~u9zT2u!(5~?Bc2kgVW=d_qCm}i5LXo zBlGhnT3VxHV;*xOd1!DkAfCRxS-ACARUK5P_b;~Ui7)Wlodv@6^Po)}7WJ$Ez)_YN zRc&%R`}RARn(UBuHT;OAf27S>R?pr z0Op~=d#_p+)ao-2yaf)>l6%d0P7VTC2LJ`u9ij?I+&qvlh-?U{rz=F;$C)T@`t!

nJ0(%K)F_=yBDa}_h`kzeXs!j>biijZ(NV{ZtkYC2?|+Z{P|f&w`O@&Lo{ zMV&j=!J-#KoJB>9LpAaS>*L9JcG^p;RTGXbJ*lfJ_IN zSF7T(Ubq+RdK$-NnYSYn z@CL?3ay72{rLoTVCgg^A#zC%y6rjdg#z%tsPxkz4R;Gk+d?pMC?@!71Zb`7P=ljia z6#+kApubWQJ)>8n^kJ=ETN_ed>w?pVm*NW8+hB6{Cmf>1JK!6eXhqKx#;xHF@tv)BJ|^ z`Li_JBu#Kb$J-)04xzWw7-!cqQuwM-_$GROj-7KP^4#Ci^~A9H1<`?S)LPR)Ng9{% z0B}8M@NnZL^IyF;X#2;K^f1sY|Fm&*`RFGQ3z!ecRvKKMwMAK$I>&??e@W<5`I<1r zt&~=noi(~&_2CIgYHf7_&j+)fqa9A6Q%w8uKRCkI8|=WzM>#z1s{M0+t4#F3=)ad4 z=>)klh(Ox6hF3lt)$z`VndW6QwC$b;^7^CTm=W*r>b_eN2pLXF0Dn7QnVmPGe+Z$x zcS%KAw;5jApJEpKwn63QU<&@IlpkfrsoUs=GKTW)5-x;{^Fu=_O>*>~+g3X^tn^<^ zutJ3Rqfc6}fHYH~^tf{TLIT0*#+!SbX1ouq<*e^;Df$iCy`vyRSv%2ZVW&)Jq0*#} zzs51*JyujD0334;ysd29#A-b>ZRl$3zq}Ml{4+i$589BZaW;?bMv(`EP0NKM z4!<#!*T*67F1F*TjMO5LEQB`9{LI_IO@)D|3j^J4KfBHQ->D&?&DuM}$L46arjJRC z|HY0=xG++8aGl>W|96%MB*LV+ufRc$%p%dJ~_d0&J&UK)w|Ga5Gsu#q_$Vh#;`|lDs3Zgi~I@}zEug^n~UzKdvBEER` zeKmVTDrqUK&6`V3@%f5nEdBa1@&1!(U81z_poEYpX$Q2(Y+6i}D7@YntJTM8w{8uZ zpE`jX-3i0MP*HqSuZaZzNLYnx2~Dy3?%jNb%Vj3{Ln06N#^I3w9U39|d?)NfpUx*! zzUATFhEBW|KlyLQ4O;G>*m$NzK)b1LJ;DcGa|oN6=N<`I`U?Tb1kgr)Ca-jN$}xrz z^CJs($z^36((*duR`hh54ET6@b9aGrx#A;R{4+A%O&`8|kV%$Y74OKSsOBUOqmm+VQiY$sjm?_xQxQz+qyFTz|D#qLk3(6e7aMa(dcS1&FYvVRQY^x})h|RU#OjV=3;L-#N*0NAW}3sPf8zBzj`|VB;UAkjKG&mX^v~9u z24NU99E!w}?dCu#G%i@veW#lsF0|UzLs}y*1fy3fs%E>uK8nzs1NwDi7l;H@a9=*F z-C;diG(s~_F{;KbAOIexOszv-&9AOtMfeD7@Y8^}hm>`&PX>fO;#nqOtJ{zIl9_k{ z0ypaINz|8(AOAhgDsdSIx3x6QPZn=%C(ct@RH|Kf8habKU8V(OrlNrET^Mp?Rh4WK zh^Q7Sw1c?4a6?&^p)!k@wRLojryS16GVSeUupi{qFw)D&N5#+HQvIzKq8VQ*9Rg*E z-W*U+`2Y6^b#K(~-}%>(-boIYynsGq%Q1jK@m}MBAr|C8G@FaVNo3su#*W-d!Ok%( zSlH)`Prr1`(P<~DrYuCIzA4$erZa&Ka7ID`8kJ*{ERJdBc0Ni`JDK|_sYGq;L%=rb z@?<`CDpwh?wRS;IW4MgkVz;j;LMMBq?(f!qVnefIYKOKfk=jm7k4%&M^w3VD?iV$a zdkF=2@7y;Rxn|!3gg3*1D*Gh5+CXWBUM+ySM+kHy=8$9RoQl99yk38l71MwYFctcSE><0gS zWTM*U+Tk=!SZ>v`3t9$@SZB6JV5o$wjA=EC2@p9txvoAJgjgIaNhacfdjUxos*?`! zd0v-iH9HG4w7a5?rT_!6BO8BPY3%H=6PIe=BME0uDQdSYmpN;X5VDY1dv8>>;p7R6Uwsf~=t>D+ zsBKswBJ@Kd9c;VP(=!>(2SNe)4GxNYdx!Ey^kRKVA-T zrsh3R%nAUyh`(H$Tbul%PL#4E_<&j{I0X}Z^p-AY`eFnGC`l$;)VgbUisqLdVoQ`X z04mL8ZmwlZ?5``0OInK`dNWEZp|PGvsVkCQ*ljkg;NK@%dI+&;wvGI%X;apYQfK1j z%e8u4!a8|WZAj*zYZwvSq4L>y)fqQJu4vLwDfiXmK~Hp(4sdL_NUvx56JRn?ob$x? zV>uCaJqHF$FK^JS;qWcbO*eNX6_-8xcv)z_5f0LMu621M2VXD9{;y^AmTv%|Ie*IGGmn`zucH>b@KicnWKA4t`U|)e)T!+r~&37+$}~&d~YF3rV2UmtAe8Gh{n%%K^xI-vn7cYqiXfjRr9l$ zyFaX0M)514zvv3=9i*Aivo_BShg4dvfa#sjhl=A?p0~aE;=QdoM zaMLU5>M|Q(J^_}#PsH3ye&^ucZ*pIJ#56S^vkt+40X^&0-wRM3&b?vbUZrJHc0603 z>wwUr%O;=p4=4{NrVkw~GA zABzHG>B=F;jP^$AsGCK5Ww{Y3FfMJQ2*Dn~BJ%Af?elJl_DiF^S|ZaPp@gBa zam1mrDczmGC*d=8LxJlMOs{{Bst4|pPUGl;CNO3iT97afb#CGjimT#COkeAZ0Vh%A zuqfD?qaa4R=5C;b&9nO9s@B%gdCL^R#Pu%WKgZMJ84FU>L|NSG$8ubJG!UUJy`9_S zUo6#-dpOQLo1xnK@E7~p+_KC-4l3flf{^rn1Dij$gEe2>8CP)pAXlj&p6medYItBz zZywreCO~FLCG*0!V50MsP<){LF=|q+PH>eXu=)|^tfg=Csbh+2vrM8{Jx4eyQx?Ep z@o*3dNP%5(zTJd>8#jRah#>-wOU+wbyrwd1$o=8~Qp6%0G=^F=5e%{$S_515kUN6zr6-bEro`H^g zoPMfAV*!}0?%$Eq+*WAWJlc=_$$=Rtivv5c;tsY&sS?$S@y_xzc{oC~mMrXmTDyOJD#)DtPzQp`z9) zp=!Qk-rr{CnqrC#5^2@fn|xgX$0o9ZTpc!%(HytVxIOP1zcnP_X29vWJ6D60JPGy)R;=`S0-M%VD=d0bpfq{s#B$k?GM zD<4n_4J(assSgvTO=p)Dj{s0<$vvUg2i!_Mz0lXi8zRPJbrd@6zg%`%Z9|ude9Vf9HqHeBJ74G<@|m zt*zbgvp?P=#-a8ZEfZ|U@$(RIunNzo<>@^Yj%9u&5k<08<1|7Bm}I<)F*$HtR{O_N z*arFa%R@4X=73v;GyMQe$49p44u))lc z7=} zo(&cKX=F8|{>_TcK1=f$c3e-90@Rmc~0(!tiNGv~vuIK|LYC`!v$P zY#;pC^Zy_F@?gZ>q;zyP59Qs3+4?SGfAN045Vb2Jcf$feFbJ%;G7*;@1HCcf-x|1D$6#NXS$5L8WHr5Bt$>3*xd*UZBYFr%U_tG zdnB#xc@0uZyV#x=hp<*Gq-G|^5X$t((i04K{K(Jhn##QV3%m2Zv4h<`xy(e`fpXP# z-JYWv$}Uk>|FkD)#05K%8z?z>?)O~;BvHri+tEnfJE!Ai+MDXi9XFt6!?{N9aSudC z;Fa1%bCvpy=uoLwh%u0K4Vem}$Wrg&&Ch$?k9?pagM#Hc{k z*-pcln+l|d<`h?1?G=6;M8kP=nz{94I0^sCuEF77 zpN;>gZWX9hHalAk+Hvt3GJY1K!z}DHYpF^q3C#=Z^7$cq7RjtJQ`~stanCY~iYj{J z=N>@x1A;9dW_q#2-hzc#i0r;U%wZ%{Y@esJc^rn1MDGp**Jhy`Tk2}|-PE9^2GJck zB6gQuD+WjRGNw#mTo%QE8QYwaDLgYkHRS6A_q$s6|L^gRRbuFqO5$tSjaNuQzN2};zNo6 z@*;rJK)!NFdKLj*ZBe0u8^0J*`|*-KHU{{Dyh&rP#2Y;ffDjH49BI8COal1+=fef} zj)QeVDIhRjdh_}@03(Vb;OBbcKlG69gS#}saMG$^teWE*2k=qc&qb$?oF2Oh2N`l? zw|l9GS{9_5LHo+9S|P!f_g;T~N;B;|dUx1!gILt+c+o^Jy2p5-pze}!K%sXUm zGQrvQ1uHBq6d<=YBKseBp?)>BGbJBn?$Sb(cG=)=tSTr>z>~c|lzEWz`?=UIQ1$6xGy8w!Y`uW#7!*@w@#o8K2Nj znZ-WP45r}r)!&6c(2|mC(qN6k>TtkS=*dk?ZRw*3(zD?+upQ>B`icEh)8w;5#Wb}l zw!Ai`4V07hEtbdraMs58BA3i<#ee%P2s(hda#ERI=kwM!763V=_I9m>Y0SBb#o~6B znQ@PsZZBnO%JtJR1h7`}C#%TjW*+UIVo^JOohVYj08Tia z#lujBWPj&JN*q!Sz=njMlwIGhJn|NWH4>nEO%De@Q&{AINwhG}m+9a?Io-5k+dA-t zlKC-cq04%hylB}3zLW6=f34A_*44w1b{Etp-dSw+NE(@Q$BMJQJu7cR?4=vgf;Z_R zp8WBgkICyLKmOE3*Hj%vc97k!5q4TVHs)dNM8 z$I3SQXZy=yQ(nSQSdzG#af+FJx zxpmblx4v8Py_`$**bBL=u9{=V#;f2~%)WiEo29Br!l|R$vYYFxKL_5kyEs^|1U4fI zIlT8r&9ou4w_`dS5DHrDFLhdJs?m5UcMr$XZxXz>FWUUq_K2S!xcpbkq1m%9ZLUT? ziCV-D>_AH*1H^WB^vd0`Z?4w&Av{+uAQ?Y0IV!)D`*RDRC1vCZkC7pwW*6rSy9=`V zR5hM4f=0IE8P=vSMF=kaZ?8mrA1e-f*(Y%dJHgdzRd$wr8p~p!d3i7ONnZ_dHlsk* zer)fx6Df81hpH`Fhv()rssDN%^S{3rJC9Nt$&mJUZ*vrxFii{z42dB{+;hI(C~B zMU%X1q$sp^-Z2)?UiR=gtw$i*2*i6Wh zyDMmIc=I0?h{J>8@9*6^lOn{SQaB_)dL343-7}zgL=op&BHxeU_aal~vsSW@pVE6OG;7YG>*LM*?6A z61@=_IF14_C{+X8y5{r2H_hHVHd#y3)Zl;h*ZxpV{Rp4O70ho~-Ur=e>c>RLW26Wa z-_iUlA>-j%RB!{01*1^F$mi75>6Q_YL>PY(kz^4L$Ef<{yN|^NCQSQr6rt6U5_n6m z>z5gNrC11ulY3>-Gtk;y?jn&*O^~~xwmXwedV)cjM{J=J&4%nY)uLf@c7}w1p9*9* zd2xeG!V#;@OTRps)kzfj3~cV!PztLsu2H6?@>h@BpRVG^%W5dE$yf+*-As9}%(b;w z&RKllz99VXf8PalPv9%}j{WobJIh#mQ0o~Zs}WaFZPV0TwM9yru(*J{Ovtf!7y&cG zI%i}}VsC!Cl`zZ@;+!(MhqgC2AwOml)u9s!M&N63c$o)<^#?++L=Y$nwdI6V>KFRD zyZdKZrru*$kN}(1-{08S_0q^jJvJ_YBE-b+%fm+^H1GbkUilx6mfRt`aC0k{fEC)B zgqV>Rl~MJ>@ms`EyCLIJn~b+F)Gvmj`pD=oOK^g)!JZ|Lh(CJ+MhYUKKTWn8@Lcog z;oQ{?(9t*X`Kc4y&dj&3n1((#Mvd+Vtg{{G{E*%EQy!IZ)wt@98VN8L z!M5N{B&7>gWxJ_0P2xi^KRn(Mcm%T-aWtLOf`#QOZ07j`;;Bo)gv>Xo-0iby-MGfB zZ6W!ufbnzGmtnjECEGUC0~2)7kK2$E-{AW+tHPFFCRbl;p8uAK+P_nZS-eoKDSa<$;C5Wwa)+?puKz~?>a^;yOkRx5jN@0S~l~) zxb0Zu5{m{bmN`;&5d_@SBM23ErOR7mq+R1C21IwP+95@Eq3m+1IUlQ9P=D>A%-~;l zn#-*!giQtX^L~nj`$hxOB!VmS=A-Dxe{SJ*VrzGyPkI-1^}fe;5iGeMr!S-^{^n-q zXrQ+aBxjtH171+W6eWe_J!~|aC%9yc^R)|8g3%2KfX-+$T z%@^&NUb)8dM+##T9KAhf^4Pobq00K>>N2!=HVT&u4D)FUV*MR0Yn=69XCLK6b9Xc9 zMFYgrr3~@}+gWm0C`xLCZ%zbSj~4loImc8ldbH}*F9Q#7{)O(3DqD!*bJuPSE*BaR zEIf4n)1$l<8Lqy2{*5fjqkdr*DHdSBXr0|=m#d7fwCf_u{-Z=w3w7sz=pJPiyDvDO zn(Zn_>6H91ADBV+-(2Nh1q7(@V(l0KW}wNfT?OCOTLeH)f2OjS^q|ORrr<|Y&QjZp z{pBlX+BFj;*r!i*W7SN=q*O63Es*WYy!01O*pG$}{W96xPAs1NU1a)aWfjdE;$uG^ zofN_iK`%NqHy|LCKm!K<4|5gA3^BJTA!`f!Hicekm4!KJJOcNbYda|449LO?C*8)u zh50)cH))_pe*N=_{@=3pt<1*hqzeXF&lad4BV1VFTf zY1>Ug7XV^cN;L5b$5ZsOHOmLLqHaM?b7AVwO7N`>s^bBiY2dTZ2fX*#0RYbH3BdM0 z?rRkf`I0&I^`8NnX7%j+js)I#beDsFzs{tH&LeXwNcFL_t+*v4D4E94Xw!Lpj$3Jp zaL;q_fFmV_a$!21Hvgw>;;i*;T*l$+_^Oe=-P2TT6ljKP9XbzKoxLc%ru&rEp_wK)*biN4;n~ptfwN)&PfZmwm}zqVP0P981IY4^3BmsoZwbk z2$%r8SPQMKczNF40y1k;$GBAwS_dq5{JFSTaBo@I#g3ozJ6}gWcBlStsgr0sLU(3$ zqh<)yw6HQD2yl+;g%Rsf#UbGx{ucutoC&{dqn&46E&7m!hu~ho=;}I1HJF@AvPboG zmgj9;A3T4)5I*&8uiCv?W-fmz^U88*_y7- zVF?Z2z^ro$SfE3$V{+^Srq8im3-XVu8@Cg(d?~U z?eYKBwpb;lOJNTBev$DsE02?6a1$cZOL>^5)bnw6kAdI9F&!i$aokk9RwzGTgvJE0 zDFA%Fu34o2p%3`>~i)SZqz zs`AFphUQR|klt{~UV7(k5?`2M`w6^lX`b3dv26}25fv~CD|_-KDiFk?y@?G|D{mWi z&iDI@^x81^*nUj-PbivOJK7ksjo}0H0yaC16ixx9soY}?IaQKj#ZeAb#bqBfy}^_7 z-#NgXtXDd{dK%KR2^AFbjg;+fTnY%i!u%l(htd>yEvP!S37yD@96M|AhX9?O3%@fcn}!$)F<&`LwjOM-A2*o5-G3k}o0IP40U|?y zxcjN~RAKSW9gau5iG;L18K#H0hK5$!1b09>J7-!;XOMtGf!<^Dl9z;GylU0&Dy-xv zl}yv_VpO#{L&3m6`I-YkNhZUMIh;(WUSSgT^77*BS&huMoJZskh?k-_lt(nH#t}}I zL!k0rc;`V@2a#khzNne!L;oc_y~c9=wiK_#;+`mwV%myT@sZ*lR=jJVN>d(A+?V3A z6!Fc^i@jrqHwG6&>B%?EoFbwBM%CDk0Bp$~)*4QgcRVUkp$$*0U7!+j-2<`Zt^{sd z+Q0sU&-jcdb_k#*;m}sKRCat^>_qd(%*R(&+S^3~l>4a@eYgZRfuVf3@Sx~GjPK5+ z>umY?A=C^o8d(W7HTVP!iqoJ_Q|wkkgeNXFzQFMP73THd*jo&;( zJ>jfrzGq-Gf`a?h)7s7^Po41lAJpAXg7}89rI6U2$F8%Y*6pSUnyi~VlwGD{mi_~z zU(9%DTN@Vq4#i92h)^+tpC0;%C2tFn1u_6-4M02dw;fxnjWwAz?O#leyewkUa zrD$;b^t9DY$5VkXu$CLPI(zs}P~JEt86tPDp#rdR$N>nBIbR2d&ZTR_DO_q zLG)-3UOXcD_Lk_qtjhxo*$G-gI*b<)PkU&Y!+*A;HK2WY@s^FW2?LwOHK1p7zrNP2 zQ88(kE$RDaiWd@mxt8t>_tH-Ju6@C|YxZ%;LCWpprq@gc@KeB(S9~;pxGW9ZmL)J05{r4~4}iN(?!x=b~`$HWbCUa#7W<0ZZ`@8qc8Q>CUg3 z9bzAIqBIVUflUo-r&9*2a}DMb)imQQ-sHkg9C}V6#ajD@S~)ItWQlcJ6nYk=byBKj z??2jrbdB}I`c=t;2$csh#5+3|nJWa;7}vLr8Z#J%YqGL4sF^mHpT^|yKS;M0Y{bMn zWA$AggQ&U5=k?$+k-EaLV&_W7hDpJ1=2c_&=R~ z9k}ClGK?4E8T{gKIL;ZLi8?lT%-6y;FPPoMkI+1)h%a^B0d>C6rn>ed?5352GQ+^s zld;_Gw>vTa8xH?Ud=pBjmZSE$C(9;I+rkUmU9y+E!ssLh+H!o?IiX<>h+d?~J1QIF zfe`&VnHNNt{7ldN(j8hawy2N!HVw12&$-=yR#bWR;0*~aYF~uG4qm^p$x=&Q=+Bk; ziXu{51AQ6Cma~&!l=9Yi!QV4D__(viQTfM^sI*_is=CfL_-LJ^58p5GqZ$XigGz9k z#h09WHun5{DptU2pQZF&*4Ihv8b-Yth^(Dejn44cNJ9OK+=27IPUP33F28L2VgRrY zG~yXcBDa!LS&>c9H?^87^}v>{%;z&I6FJBr)w$B(epjEMGxR)xavTCGn`^C*L`8kI&FmC)aZjtv|nsds$tVikI!?74vrB6Vxnb@PV>pNg$JUre58??qC`@C zZECqPcBXw{ZshH*(9()$YFe!H6i~C%aH68@968_P>{*{a5}lC5kC(&ztJBT zvURTN1w)+U+0585&YDtQPl4o<#yp1p8WwWht-h*7(^_-qIf@JfuzNymI(cBNFMmlJLzpy_~!B z^QH9P&PDPmoQCoO6r9||HMEY+Ei(dCnSqBmR)?p1I z1a=GqM2vdZfk|a3e{KEXYWl|XfQ7}96dt*7WqpfF0#}mQ3#;hI>ECntpLU_4`J>56 ziGNV5x%^rK>u8y>uAJ4E;t%HeREbycY_@1Hzw?DWU^4sr{f(A|Y9W%(HjE_Fi6$0! zhhLsGM%T{YeSDG-D$=yX!Lby%yNE!T2aH&IuL{|^X#H)64%)?EI3@6ij7BGCI+bV6 z8oZD*{8DP@bckW#iFm^j@!Wig8;@7K3wRN14bSuKW znsqU)US7eE5{MbQc}3NVEj%L?0=ye0T4jwJM_G=WYj<(JZ7N^Q=x$zyfnM!Ke%F`o z#39uclgyto`hj$(iFuL#nyO(qa9%e+5mU<}{`>7q%pR&q?sxoTXS>fu76=3Jvw44*<&C-9 zSchPd%3{-We;W*<4V8$Su6e75FL|II|MFP$Do40`D)bhuy!B*(f6}AUV*mvv@6GQp z2@rA{Yf2lkXlOZmAmW4n{TbS;@3h##5ztj$(2+NZOh`Bm&%1r1qsm>&SmcXnFoYA`U?3=PQGwPYq#`gMwyRi z_pIq)zS2AA$xxMe>w~|QFV~I5cVz80$%V2GDlHf1u zDp!sI?U!^^%*+9*p=rWMJDNR2VqG>aNSW=Org`AnOYxpZxh#FN^lQ(41f{rqbi)DD zKYOl-Ph<3SP<^YbN@CpHX#Yi)z#*P8#Fu^j10Yp|`WWkcxqQ<@UT zPOf&+IL@x@cGnaY-!I%sbWRY%Zojce(ED?8@f@})!TPu5!cX3IQr4%R!xSc-$Qa~P zzs%dB#DBW4aH^vz-1-NF>=RejSTGe^5FP!!)iAKdWk^Mn*UK#T)zdcS^y6Bg`8yHO zp*WwFxs0Ns0{O`IRpt(i3-nQalUxtglq;y5+C^M8CgO zb^Z`++Nuo#Ww-x2 zo{`4jmbo1-k8y~u%Owu=G{~%C+1}L%@xj4d>37R-4#;3 zQ*}J*e$woEX%{nKKH(fU&LN72PeR$Y$yR>PZMs7T7#iSO3FddHm-r<`%^U$WZ0l$E zjO&safVfY+_pne{UPw$LACfLCB2V80!FT*>OGd5;~mlMirIn9%R=_u=y`?5xPX$bN%c&Tr7oAK zI<@bP&C=yFAEJ$tpK{nAJS=#FgFwefD!e^S?V zq3rNC34aQAAFJHpI<#HpryX9P{WS6>rv#l~@-2lXwGEYiCm5JV?F@uCmz_anzp_aA zUYov9m7~H@lb^758%W3*cFPiXN|qW;=$DiwZQuM<(r$0ZSzxM|p-%fKhQx#`3Sqdd zmKt??!270!C!#aI=cSyQOv&W5kQhOz*=#J@mUccc61j)a>GOB&visUVM_@lX0(viE zN1c5fmrz(%q}@u=HM}rC$q9t(_6w?XB1@BgH8>5(gz(&L8YHH=gVKC;LKs?1w$V>} z<#v6TNr33-H084~a$R8BG8#k35xhd07s1TsZHWi%dX4)?B`I3`66EcElJbNHC>!Jp zrM}^}*qs;6fr!8f)s8MH91gB0_{^7v@m#}z_oxDC)UaH3c4y#yawwKt<>sxEYIPMJ zhIkaN9nQlL*#1fYQABKd^x_zil)Rh%h@)fVcnQq+_Q{jzMq2?>e%SMV?5u*lqNDA z+moL!<;_sW+J0KpnLpc48}qZKS34Z{-B{OW**3V$i)ne4R#Do^Q~r`a+!22gW^HKF zT4w!ne%3Z^i8XNe9Juirdl>sqV&PKtN}cjD?)dcHxbq5OS+L4|jmx#dmXfZFR`t~X z`T`fLIJhi=^8Ij>i+vla`YZsa&GHsu(`u?OR|sK_^GU|Fhx3e z865kKq0gL|(-wklrgC_4SpK8?VYs!IwCs6sOto*R!^}Iq;HZqPFxDdqDW#$FOpG>~ zg(I;H9IBI7BBNWEGOvBAWT$lC0dUzChBfuZ2R zCtc34IDUjiy42@LBS?m?2cuNbl9o}8rppPlJq}cMjIv8BT4hhBC_`_Op)c`s#{xIn zl2!DWeHPLO{+s|dmiQU!Do>MUwWaShe7=`g1XCV0yXnk7Tf@2gF1=cER8w1NKN?*b z_LLdUzqmW-gS!JawN1dU)07tyr5DHsNfMhr56=~Xqtd!tORjxU#`~iA>gn?` zgXNu48qRMDK!B@KGKdiPLyg(IpzGnUA`zF|Zj@b~E$0;lib^CZ zF^n9$Sfb!Vlanji7>IY1JDY|^$Y|QYBh`Ll(K!*hw%7GFE(u1ehIa20JJe5Ly4~5i zVq+bK?V&WjvJNZ4>}`(L(7Jr_K%Zy=(Q`B=Dv8Hgg0Ej9T+^yfm~Fd^W$UK0Giu}e zRdcl-L3mfjbrP7_y;^udRr3bH-R>(1ga=v%@HKQ=Ynzr;CN!Z8Z!z2GQSpNTtAm4C zOYfMDu_8`cIzlxxdhFQx%M5N4_GM`lk9mX?4_~3gnRLOS#-M{H$)o#S@HP2u!n>Q6 z6_NkG5A3Z)wNsRU^R~l8x#;6>5FHh^-joC#=;UyG{TK)Qk}`BN^VgCfhCf8~d&y{J ztoAl|m3(pFXZChMLUc%u(Q>I|V5K7`us>FkawjlIT2_}!8pxe!ENYsPeZC8>F6dVM zh@PlfUaiwCX@>NF23t+?ruc7 z1f=1nM4F*Nx?_;;7vSpS9Nhti2ehWBaO|Qp_@Kf@k#B z*7t;=e(yjtzY%qg4+!i!EAlp)leRHvgS!kOOV+0 zr*$`ptWWM3$nV;#7Qa!3buTESLWy3)gi?0}7S+tdjg4Q$5@#X3wWR!r?#T@J3X!QjR>b25&E^xPh9|W_1A&Kj{>ET2x{t5>5 z=>GwgFauQ5`_v}il)j7Vn)!e}HTgGvT7WyxYFv=1sqPjj#8iCNC``jCROrErU!a`7 z6?3ReGyNEbsiRkT+1O$tXrBE5w>W}4^u-IiSfR^9ZrvCP*WmSr&k2D(s6UYgN8)Hk zOr?9~9_43Bvc(3{Oz+pGcG=dwaZ~J1`wKSHmLJkygay)GG_nWZtXoz%n8&2}MLIvf z@$mvxoW{Wwfj0NCczmRCvX+p^<&x4MVBnUw#@47cOR2FfL=Mk?@f2%wu z=&13jVU0Mqlfn2~y(~aw3gYC)A`APk^p%73*yZiVVk}b>>Op&8e|(|pC_Kn-P=0v2 zvWb32K2Derf<~zJYj-uRw$CXK^%9eFud^bZP{d9f#LTRs05tGw@tZ)Hez` z3wgT?uX9!Fo0??lM+|3BsZja+{dD>+m zn#bn8CBlX7KRjK{#;^Ri72whcB_tHvS|YOQkOC*8M|6F^ZQaY+y4r(`z8NdnbNe?V zq`&^vW_=^j;<}^P;GLCh;+@|VQ=k6=#<1T%BN zdOtDSdvL?BmIr4iLm&8SBl9ftEVI-&?WXoU=^PCvEE1&Pdf#r9bFu8v&l&Ql8bVMR_-2>nt19p4PmN!biTr2!fTKY_Mj zle@R&u!h`BV((K&g}LtFQBfh4@ZFapybi{*<{Q};A^u53;yc%~h?}OANq-qbKT=F& zAn_3)r$rBlK#;3{5ohb3s5zuIfK`m_({P1AYl^zn&O)xaQiS?CoIS%kNA(#okxp_+ zot9APB#XxF4K?2^E04#^kAlAzRhX}1Io&iZGStQo`K)-@_-*`~XvewP zwrXa;R*Q%mvwIRV2kn+v9pFkFtZaHHAZm zid+$5;#|-^$i{0D`&Ekg(i@3tZ09-#fm)$H)4|#kwX?y4pzozvkeQe?HSGU=BYTDq zt-UtxBbN2WKCRYbPwOF2Unxa#5e)1tk9)??Mma?~TCs#Sc7;a!+^l|*n??t@7hcvP z|6_paBN1t-XYjGOV>$WSwntgX7e5>!sR4EDcGmR0SJ*VvAd@O=nf#z5oteUW!wxUE<&qaUl(`@n=i#vsD=M*j3YI1sf^5_OD^`IYLDD)%8NEcQeTJQNMa`dP$1TM_MCG}s1G zTlLPa&b5g-bSq9q_kF9H?H@O1dMgSwQX6YiAQ83vcRPrMZ)LR`J>!m>y;K_*v@6b@ zpV!$|&l8BysF{60V7ol!-_FU{uB<0{2LyAu zqd9v@ZTjmJw}^(#to}UU9if?7>H8otb5GPV3=S-c@tDK{LEqm z_9L(x!4zl1zl{}{E3;e_9)VLO!?D{10wJ0Q(tz~cL^9}9o+#Yd!K#ELgI~0hV)Lak zJ&J;8&J*TfEww&<=f*ka|3t3(-D8e=Ftv=Xs7cQx?C9Qby<%)cE+}=*^j@fKq5k1P z(!PfA-{9;Qr3UvO4nMNduOC5?sZwXi02iJqT4%E2i@rQw(6PU+v?sh=v=;Jc8BE9S z@w};(P-G56IiD-}NB#w0S8HS%>Fd^5`Y7e*R$&u58t^C1dyM;d66M*@4>Nx)-fuuS zo~1S;{YZq~|670#jehz{d^VAeMc(H=JX<@FRxZvPlAEG-Gyv53I$5!W%w_`Jgc~^l z;d^TScC+yxLy_8%j#w%EPhWo$kuJsw!}>O#a7e;v2;?}14U39AS;{rh-V@036QvkB z5MLwX4Ueq8qh=bxp|3MAJN#v?8$8C4*XHh6dVKA76!%yw%MyLowz+0GW~`&*>6C+g z-laXFkTNr|7KTm7f;#M&bVOLiCpFmM1%xBD5ifytp6AUpp@3VAMc(KfMtqE|Df9SK zwdI{12sFhgDDUMZ+II0QJlmLW4n}k4CH{!n^XmA}#8b!rBOovX9{R(MjGo#W*2BiYf zf$_WxnAmu4q;aA3B-HIHc&{c>@`6^VNg}wsY0ZH$tE$?i4R@XiwmzYl0Z$R^{jm^f zcjV@XaM=BM2WPkr;NwH&X*l`A0DYK7@`#%UtRSBlMt5{~lPOx%aQB}%%T&T^9Bx)+ zLJct}q3J!3|8>R}R{zNfU_`%0pfr8{@5oEMK(mud&#AUV>W2nb(6O}@dIT2b?tU+i zJoyxaL1%tbkeF7I%f3k4kThF?nHg=YRIgeyeaZI3VxKmJx@3FqO0voK_lGvhEpp#} zrrzSWg*%Lfou=^xL1_gkQ$vd$djKjNUo#jF(*a%ce$GvhY__gd@%~4uCjecdN0o~s zlT3-wH!|uY0XoXR`1Y~`1c3r3L7-Q1raaC2U^~s`NuJl16MsCwKhDU@nVRSDhL&5% zIp*x|XYxes^*@GlPorfN>Xr?KOz)zo9N>&5ZL4d}zmZ3Ama?TOwRKvdsO>YUSk1Ye z*$>rfPNam{uHzc1^g22-jXt3Da;P?!owD%wZU2DQqne`77dbAH(8>xaMcDc`!)X7V z5wpFF27^o9e?Rl~m_*1H`|_hT`^s&5*S_hl#bkb{MY^6j%P>yy*ZxzLIEs6+=!m$` zk=K#+U9-sDk$!>DrlX+Y-j|i{WEL9KIx@^b-N(iCU~oW4a$?2h0Xxe~aO4?zE%rp2 zYklNcoBXe(FYHZ!sL*LRW!t{WXHd(SdR%cT79?i*5wZdd8;eg}Hg|nM-$Bwiwu3(Mlz;q1U{{S2xDa5x&PT4LZbnC9&Y~ zh=EDQe)&?=?tO%faRRbZkd zUK~p)R-ZLn3+Ohc$K}!wcmq^QK3~ySJ%Xu z&4jKIm-yD;dTpwWad)0T!+#`NJhA97`Lb?get(skcW@VFvymzN%Dp|**}Fi=1!wb2 zfA!BTvpcR2NB+l+5@QnsT4VEbum1Os|Iyf^bF)!QS!cLPHPS&9Os=$c-uu>iWue$^ zc*rU@KZQ(I4~d>Qdh@&S8YgP4XPj%96G$9!+;HT47)L3zGWiQ)zUV^|U8|X3SY`p8 z!kc#Olf#c(+=^E$++1xbbi}5p&e>L;`;U4+$FH3_8u%pK^uq4ab(>Tls7%7^&1C#?Y2tM-PG7xE z7l5YXUk%213b|ypQH`r#onk~QkS<|Vz=S1jLti+|V;u*fDuMDZe!68pqihTr{*O2Lvqbyx28MnS z&!+@Zhp3){%=CjOu64Q!JcF;ei-sI0vBVMjc%0*Qv(odf9E45pH|?z|PYq7V<*Q0J z55AW9We!{~(Ve5rmT?+XN}bfD?RlclWtZLvS9s*ktU8@w1me<)ziw5`EJ4|#oh~*l zn6cMLF|QQL@i}x-&3){od-UMJ2E^f8?H78llSx3T7SA?UO`@{31j`I38+WNP1|j9| zzSR;QFEGS2y{cW0K#xeP*&%;rZ2n*Nu>Y6|_;nPYm?||J71{8cNl1k5HkK&7Gw1fC zLE5U}zn^in>AtH}H%+qk)x3Czs^3g$+Xhre!mK7eA%8>4-bnuzY;>#e_;6T2cfDBYe^R_Qw8ZhEG$uH=F8*&uE3f6& z-}noK*NPg;TC=p|`&p?(!rBS-;XxoevP1T-{?9@lJ0VyqQGH?Zzu=`EEYXO6rA@G; zs6Y3PEqU$hH4|r25QO5;8m<^ZUTLtrW$X*-Q%hXwdGFjPoNSHCO_W@rx4+BrpU5tw;p9Uw9m`D*GH$w- zXJ}&&6#dFTLgOpe7sr#dsTB%_BNG0IfZy81592*5BY^WO#Lm!zknuPD5uKFLHM*O|1t3v${`y_B^&iozS{Y_%1|A8(PyB5 zCuNl!X@;f!YbyIFi!gJqW4-mJ$~}4y|w!L zmQLriF=~~}CvlF&DW{g0Stf)~kZbVI(N&qUOdbOd-9_vo8KX|W$_KEeaXptfm-=6+yB7oCT{)nj9DP2&($qrc)P`FAnqQo`FWPU^62?IL z$bu>_^kKK6z6yugJmO12gSyAG;b_r>9Q>Qo^32VFF~rGTB{TYmi0}VHk?mtI`)Rqf zktKEPpzc)Z5%1g_t~ipw{R_LHC;aZshtHZCDVUBCg;lvbu~U$=q-Y?5P>W==`5$zQ zbo9-KPNkF1_|-mCm?oWqRL&k?+<)GrZ5Y%AmR}y1E#gLVX&OyuRLWCxf0zCu5POw( zeWf9JAj3t5X+Zj`W)TwUc7r`x3K7GMzB2rUtM_5)6_N7sgH@E@X!D2aAB6VbKdV>a zj+bIH`LmGSUcRMA04%(=2m`~D{K^8;)0nJ=fCpfV0O}iRIQt80`N1r}zj?;+FT-XJ2W=EG4}Tf_?EcC(vT^ePKIU0t$Kw3r50Ao2m|Kc8Wf_Vk zO7Gn1c9jr{6?MNb1tqw$BFb4nhLB#z`fhYe?d;kHGAR1*CcpkCtW%(Ucnn&>8h_*) zKHSPw0Mc@J)@xI1DBvMi-<(umkky5RRP&O|PjkHV&-tfoQwzN^)USxQS(K#f6d@hI zzrt~zdg#H8bPAkEjkUVPS)N+Th>|3~`s8x9^2`fi370bY*^j)@D|7P44&w2K_&r%w z=z~tEEp+MZA$);Bng-toxRpZ9tkg{y9Q=IL3O^ld7dw0G7^G1=W{R`Ld%DKj0)?LtWH(~kJ+^ZY8f<1G zjEa-Wn^{4SV%tJ#WF96?;f-+G7l*F9@SrZ@dJ<8*e?nA~+ujNN&B+d&t4OVVD;#~2 zx0rRr7w0$(VNYLQfBpBP0?w954eL$0fwch;l_bJ^>ZSh{HWBWl53cov^M80!$PI+MAwH71!kQ0A18u6--<4CS&HB#Xma&LS@kwQ{vZI6bVaJ`dM2 z%l5qB7yR$vOup2ceJJ4xH@hW8zl545e-tSib#wtAf(FbKcXhBEp$Hp z8oQ@^+GqCDpY!az`>(&>{%FZQ09+Z+ouCx}kx(3Nu1+u>>YGSVh;iAWXx&j$F z4dP|fp&bU?F8JrPDm{s&qF{!V9%pL z{`lGQQWk-Dw@mew(F?3ZKmo20|SIS-WTe53z9T6a8(<*tA zLFp>KY*oN0@Qc+m^|g*akqTve4LobsgIgS4K6&kKu30;|^2zso_cyJCewM@ybEy9l zn9`SB4YG+~8eZ3;MbDG;w&Joe%;{>vxBD`2$m*rHwzYal50n0r!^@33(8d!LUHmqu z_@MVLTE7y{5_{rq@qU_@@o9U^HW<(8+%e8Ldvkwtj~NM#8ZXk`BK9Rm@sVxhjxEWY@Z8f!*LwSfxXIu*k8FVOpCX^5Ch^*k7r_FMwta?T^ z?m8Ejx_|B`*%6aE=M_!Eaa3jKQfXeg(rnd197fj4`bK@T7R;G^P3{}|lZjZnfJml+ zaZvbu#$!d&N?bLnB@ zoONg*w;zoXpnyM%UXBL41E`5~7EVsFK(k<7M~5+-WA{${fC%Ol7m65`98$LpsGgoh ztWC9=6>+XmID>*_=_qgm16gH*)EQ3$7Qy*=xuv3HV@P23jkvGN?AV`muEB5z!j`W) z!_o+X+yYx@j#avq*IVTk6<@wueirN^1-zs{2*l#W&CKnFz*Brn<1qGlNp0R`Z_4=S z#CVT2SHdXzzZmW}o4xc3fNln^$FTo^lulHc<&K*;r=b2h27U!pUzs$M)Z=dkCer0I zMft9BNl3u-Aon_|Wa3;$MpNrtAO8)?*dHDtD*)0h+DM5qp~OzCe2_jk_AI%PmxB|q zf(kFpFof<9rAWXt7QId(3whPnTuo|;_+U(Nb5Gac=XC(RuOKyE?OJ8N+*%9!-p%td z&fjRRHt4-ojzuCv@6~ttEX~4AH5>fdqMr9la^2d(F#kQ`-F`LS#!4ye|XMd~+u zgle+jFv(I&L9)8V7rv~HQ1!m`LcH_FPZ+w|i+;Q1lzWXyFRY2Vjs=bw-!Pi5UIsed zcj26@E}$JutLPdOm)B@vLA$tanW}*}AP;}vveWasVJU;uq6{fTIoLB&->L};$cp$a z>>D>*(-*_#-I%j)gZ*|;m1a4l%q6I;TT7C|LKWIb_S_#b<3Z?6{nR~?%H88MG4hdv zzvWmQxwYvuJnDrx%-qh&$x*VXmpE2)%F0z6%E};fjpk<>n6#N~{&HBU~CsxN|A8 z@bMx3pm&&AQC7!e%Baz9!dUU_@9d13L1F1s6Vuf5-?$3JlH9tc_}wCsr56hsk|Ke{_xaIQ56Sp89s~vs@ox9N1(czK$Nz(EC zMhJ7~>_O3Imyx>#=PuF3}>&-xstP-fT}@p(V~n*bSVEMLAq>>qNt`rRSZ zm3IqQS92&6*vtQ`-B8IXEjBf;y`nYc&nW)WI<^4@6DnbnOJk5Nq8oU=;C|>N-M7cE z3$2SzmM3O>pcXJIi!1|EZBs>{q==k4Tw`0{Qqz!=f#vulfH!4h2n6)f^-HQMIfnT) z+pe%&hL%Ju6~-!KmG7T|9M<8QyfcX2PC^~40Z&kxg--U|wOrwQ zwpGD9)5yq-_=udo%+tf zOK8TdoQ) z^2v5N3Q>dkA2HdT9=uo3wzn#v_$~Z?o_cJuvmM;lUgp@a9{Izk8zdeysj;WcGxRuZ z@1r`$3OhVxk!9tP@bca+#XJ1wQHGkx{ga6$MRU@*8T9pabDOMz^jg?w&04vK@FUIn&RBT~fOquU_?`?`KclxCjWh3a$PhUCKGN_N3ErslaDfZ#_p`=76F zBP+UfIoDvGJQU%Ftlh$vV)4;Y2;dOY7v57+BM7r3GI(1Hn*2Km>tR{Dv3ro3(z*Hd zp?`|OtGrEk`@806{xpdvdNa;yodiMEUIc%X|Eh$6H?2+Wg_Bv#4?<4tNgjgP``=(5 z_pZ><*|L`2s%MUgBaT9Crg;uFg-NNX4%MHedhYJ*(qBTWRkEWUL*cR0SODk#{f4hG z!7rC{-@{3S4MIU2uKZtxpHvc(SG%Y4_0vTL{GVkw-|7EMC^km5;h~koEU1kkvxtq4j*bZzd8bycLP{Os{*wP@05XvPvEeO&BIX%49!Rtlb zrut<+csmqzR{MuZsd#7c0z>HA zdiFV|-?X8t^L=s*gF_QNpJ}k74+30ER)UwOJnE_#X4?mD|27QTEQZ6TD}0?FVqo9J zg|)c(r%Z#!Zhs6d=G?nGyr=t5TF+ubizEwU%bmm^$D8ugDR)COwp%b7i(65-6#et> zaUt!NXIoeTqtJjl4uu_w6foGsq}V3C4KnWr@dsZ}zWP z8%PCx`#T@zrItvC8=a<)mHdwhT-(qft%2u>_uJ6uLyK({V+Y@$jz!LA@C`eO-t238 zv!Yv*{{Yz#XdDf2lFkG;Ck{b5SI6RA5Z%gw{7(_;iZjoQTjPso!~hmvP5ds*OTyP{ zc5G_>zm3&%F?Q3AA3y+nDUx$gnF;rsFuD2Untg{^&Fm!n#(@J20*hl^9xTlNJ~Kyp z#ys?qaGrBjuXg(|(^8hDuY+%GFl!ayPq-x_^6QZQ+3Ybev*e}|zE$A@I&Pe;18O1; zrv7D|fylKe28BCs3=9`@MDFDL_yu&4`&?RdB`hd8#4DKffEwxP4w#36;x9{&QjD_Z z$F{YbSr?lKlT=gIvr+F@Z`LgI*+WmmucRId26>lq+`om!zNd|2U*%bN+ug>WPAlqn z&Pa5PH-0ar8A7Rk3{99{v^G+Vtfvp%q!4=whylXVyyGG+?Fjh(YTyf5PsX@MOw*@P zICY^L%=y%One92wZ|^YI1{i*HSIu-RPK z@0&f>c@iBeG>-hDB;DGeUPL2^Qr44MQ}JY_9SXGhS{tY;Baz7NO{|Y+i-?C)!H&v| z$yzpx9G4eNQ6$?}N~R8`O_e{A1N&ZJCFXy1Y&IOq0(Xw5w^n7E;nr1^s3pL@6@b-k!*%QX=5uFBcJeRqQ{UJQIAS(Y&9Q z*1Gf%_u|LIt?Sv(q-;;pYr}%F2$PcsxLT5DtC`20{kr^lHosBl>GXDKa|5(-Vg*YQc}3S3hg|C_v( zy8N_x8}T05Rf{0Qap2a`^ZA_GUYozSA!eXKT*}DgLigCERD3r%29|tdCRjhj2s&H1 zg=e`CoL$m{*tR(mB!9ZXQ!@v`xd&WeYR?4$TYGgZS* z#qIAY*EuWn5G17}{n-QOpil&e*t6cGat=?wr;+HP@EquR>qM~Zhf0kA6k=&_QoOr> zf>_!tSOwWF{P-+t}fN@RA zZqn7iW0W>zh$_{#SZMR;qh%-CKEr@ZEGq)O_*+MH>l3!uSkZCjyc1%4mAuz92ZoxK zDjs`we;s*JJIZa1-h)s&G+!_O=b;$$aOyqxHrO+@j)nMADR-E7bU56Lx(Y%WP$No< zr#T;v)`JdPMUyo^JoBD4M9Dy~*77Y5`8G)a8EH zesKes9)0KRZ!g4v^b?eGS&bM8Qu8YHZ48&F*Cy6^Z*4A}-SjHStGS4vg-)+)!ike> zX`tjWfRdVkyiE0vs<>M7(g2#7{BhXLCCF1Oc<_K)?4feLjzs2s1pvKWRyel9^p&&L zOy_RrnTbm=_=A$grToU`|6biT=wOVe+61lL-7#)qM(>)xR5Rw#yfE4Eg8?nF?KVTJG_`6;|AK83@p2bFZH zz1FlsQ=l!h7!so-8^uXcYxZ*#-T(j(5;22K=1O@{wP3(EsyM?@~Ks{ zis}Td{w&fI%c@(LM85xYfBoj>vf3hk z=d!2TArke(7IZ6?`;#;fS-}TL*c8;Q$Ce+2lhsX(F}s;ap@=%_ib=gIf5-3Sg_ZWq zvUvGNn`uKn5r-3Hjz%YM=_UQ5`5Bd_{XmuN5fiVPH*^vA2gOUMh}R8dZ*~1sovuuN zXIXGPk`toh81)eLwT$*xGCb|s4S6ZFWfm##DrPHmdU)Z07!HUgyVovU$}%kD-k371 zig9pCC95>o+hr!mxrgZZ%@$wV_a{)(jV*?LDLJG`9b>sdWfKEE22us@@nz@aygh>H z70H7N8k@^cL#R<%&PsY#(`>Y(`Ptps!alC^ASBL`!!CdLfNRt9RvE~O>cVSqJmP8- zBKtaA|6-k85k5=v@bjiKXD;mFhK;0gxNr0#0$&g7gIGDJj;>)9!MG46rTFMZUqpFD z6zhn6bvEmWx+362IC!S*>{tVIeWSTxX-Ll1GwIf$xyBZxXO+6?rbT$koYUONsY-W? zSkFUg>QcNqd_M_Lje%IJXWOKp`(Etv@V$^Q5P6|Ad-9%5TQf&0TV_1z-FXL81=j20 zUfR{`VDq0|kQZyOr1i^xy)2445O8bnF<19{`OoJ2rdMfC(yv24r-#4I{EP8>^|?#z zBmRr-kTn}1Yy)2j9VeHa#h*0a(B12vJ?NJjXYg| z(N{4%X?yl$(i$Hd<&3u-ul+(-dTuSbUmt1x0H0SlwuH5fWp8euQ5NrebvMati=W$e zPk%2SX8~)7BeK_CMl|bJJu@$^&|B>zf7)xtw_rc)Cz&-g6~1Hi**%3NoPWbz@q1%_ z%XTv{X14jr_~Oag-bkG?DUn7HKI4RwQ?TSqlICH5XT*`UQKMTBMyG&t0)8e?Tpfta zxq)*Tr2D5l|EI+s_{~V?*aA9X7caqfo#W=q323(&*O#T%9m@nFL^fF^Iw;HDFLt-4 zpT%h};6a0VA+>JQJUFBYG=)qUveAoK(}sk~wNt|Td|lX5u?TJz6$Mk8z`78&TaQ*j zGEE9w#V`%IVA37>`{v)IxpzDrSN4UE3+6$27XCTdSrS2A{KT=I6k8er%=AFCgiIn3 zKpQe#_~gf(OCxOnL)E2kXJ%)lCnc9lOV9J*v(p&Ar%z0guu#1G6uoa$!f!)$VuaJE$(zXG2CY*j~cfsFdYbPv0AxzWB__L7^Fg zNEn!JuDNR-^8(};_`3Am{>DFf=;&O{A3OLawQ*F_gZmx?e)Xb=^AxQ((}v~E*sI+3 z;mc@HGIF=OK%V$>gRU;>yYMkcrUBg_N|p)P3G_aw{tBoMIR`_#0nq+e-R%W>Y?@az zzym8U8?rA_T@#Rq|0-h8Dja-DTe#}XRpIQ+J+ zL}cSEcyb3#s-RNX?JGrZ)2xG6d2}ciWuwr`5eI!gEF!Ui5KE7bY3u!=p&$IJqMBB= z9J^LlJ200dat|zXoCiI{rko*jF|b*0*9Vt?0Jn#$r>=-5@owg@?tN@qs-F3%x*GKF zGi@J3IymOn33=S!Z{Oq=0rd#-S~|#b0pC6XG#@5r4en3-m_QV}S>nOQd!zh2P;>wR zWSw}SaT8(a@lvzGR72chzr6b2^*YwAKlz_N8}MsD-X77heo}!>PEJaDn8juMkJGTGVVy*(&#$#qkLJhZ%qIBv+^ix@S=SSu6T#{Z_akRx}GsGvkw)Ma`7Jf z*Om;>3v%`XdvNXFSjA?zuaXIk)ZH%_!ggasI^Ut7#Fw0MpH`hk zI=ecZo9*61x3At2SMl|2)SQ$G2t#fcytB*PO&2^L$>STvK&XmS$-FAeQ9zXbc37`# z5*Ud7DggtL6&e(4Wnc;NyS4Jg=l6h0K(^fJ8nem9RC>jppy&;ndliT8p~%4uZZxk7 zR|=|~D&i-1Q2SGmR8Wm#=68i3C)3c&L#O-;(^6sDkVPVU`YbcyBL?AhqRN!7TYn8k zui|p$Ai2XY9(MD>n64qJemUWnUck#`5VQBjNdE(;a8U;$)XB=9tJ34`@VW3?_qgHZ zPnteEU9P5abn851U?88 za+uPoObM@fby!7%@>*MkgtNyHLUFk$@JY~-mfOAJ10fPUNd0ZOXDAv#O}BS7&;JRY_FE)Hr^v8=bGpik7FFl zpvSW?2{kZb48+DjckZg+=Ckn(m$-wqfhL|z6+-hw5$mGqbCU9w0HpfEDes=0@!5vbqslA0OcDM#(d|utx1_2dnfYvnEb=~-7 zV#?gC(88bM({kl=iQmhinFWwEQC&SRryVS1O7UILnzjT@cm4*oy7(S0-p7rm533nx z#c!juDq8?mw~s6`8hKgq-NF@-Aqv49wzA-(j}Da)oC{nLkJlPb;GS`H9>EZWSsTq2 zXb_{MbgAD4F?k`L?7N~j4jqPOrJm`|*VbV_`#>`{dJY%`JGt*RR8F#jjT!X0iiiSx zySv^wqe8J?ncSh|H|YqD;Z%O=0w0}pL+gW`^ zT>J)(;Kz63><7`Bl^aT9ri;0 ztw>EZZ5_sm+~@>_-ZoI5awQfE`*dj@3F_46ZC~AEltnExBy%-8o_l~pV3Wfu24=pY z%x9hOfa*Yg^Zw2LMT$MLY*m|rUyS0EwXn#wXf&-_HDUq9u!rK{8>Jd&NES3u3}D+} z9>D|tLLQ#P0huT$+RF29yFkSZzSQ)^ch-?Zr&+%~=3;w}JgrjIvjL94S|F?|i2d(R zLXkPkX>U1qao;)gsbiI4iEiq0U7@MfO)TS2M~oYNib*R-PJ#b+q$p?#e3Q8M-NG{| zrAMN84ic^SDD_)=^_F$$dRC~jr!YJv(BjCLbo70W@HyMCMU-2ho5KXq>d;6my8-Tx z>X+M>Q7DH4E3=>TIHrc5x>?BNGd3%jf{z`3uRbqB#w@N1yKORq&tLVNNwk{zyhs%4 z1;!||bZ`D?yib}4sl#UVSfkToe6KKv{Hfni9rU! zZL5Sl_SKIEaA+?<z>WPQD$ow1t(;G4bs2sAV& zMn|YhfzPnJzCafn{{-Jho@ky)_|i&}(`tpH_Kobe3a$STI+=V&qM!B#mA$MhzXQQd z$scz}bbJurZaBbLapS`$5$y>C&4Cq#`&~T^RC&~1LFugJ0)Xoktop#28+5joH{#+_ z5dJ_G7rzWuf{bEuJVJq)l1qbonB#LLlh23T;%u=`nGLS|eF!@I)E}^slo0FGgl_Si z^!}7!6?|mj(dA*n4m0d+%gxZPPSl#UO=lBIVq3JrBF=U5xgCQ;2bY{Er+}Toqr!%{ zt&xTs7hhRc?)0BxP*NUo&jf|o;(#`UqHYpN9b%6iqGXw;n=`yg!~X!LSsY3ge5j+EJd;~qA1A!PtIO{sL^8rfplxJqa&9$SfL{FYnYBW{r47rQoQZpRG@ zK1(otHeDgF6Eg0Uo6eUloEC%3JKI|8f;eQUpwFy^X#joxs$llV{Y2Dz&w1gDqn`P? z_{9jdC10~bv;#EzrQc#nYk7%Vh2dfybSQ3J9}I2 z`ADkUW0iq2q|blFw|!J|kj*Ie7K(UPj(n*h|D8AHm6~}6yJS6|i8=KFI5{*x&F{{? zi#U0z49Zr6KSh;Rs^C%9*z)u%tbrPLjL1y9ap|#WWeh7@gb1e%w7)l|>O9;Sn6#_t zTmw$LtBi`9T*RJR`3TYj?ov>-(=8-ESde)7#u15$HBMJtPVPxi^E0iGcKIQ1WTd6X z#;aKVw-uY1pKdUFG2Tz+v95E&u;;sierXSH=9hY39lX%KJM{Y7cVHVD{Lu9<<{p$Y z^URkJx?a5nFO-Z3huhc|m V$%FEp5v_wl;=KEqq1{K;c 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%0A!ed`}wFqQ1bem@%!J7 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 literal 116823 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

A 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 diff --git a/setup.py b/setup.py index fb80363..62c7fa4 100644 --- a/setup.py +++ b/setup.py @@ -12,9 +12,9 @@ version = main.__version__ + '.0' if system() == 'Windows': - MODULES = ['PyQt5', 'PyAudio', 'numpy', 'opencv-python', 'pydenticon'] + MODULES = ['PyQt5', 'PyAudio', 'numpy', 'opencv-python', 'pydenticon', 'cv2'] else: - MODULES = [] + MODULES = ['pydenticon'] try: import pyaudio except ImportError: @@ -32,9 +32,9 @@ else: except ImportError: MODULES.append('opencv-python') try: - import pydenticon + import coloredlogs except ImportError: - MODULES.append('pydenticon') + MODULES.append('coloredlogs') def get_packages(): @@ -84,6 +84,9 @@ setup(name='Toxygen', 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', ], entry_points={ 'console_scripts': ['toxygen=toxygen.main:main'] diff --git a/toxygen/app.py b/toxygen/app.py index a23816d..43ae756 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -1,12 +1,91 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- +import os +import sys +import traceback +from random import shuffle +import threading +from time import sleep + +from gevent import monkey; monkey.patch_all(); del monkey # noqa +import gevent + +import tests.support_testing as ts +from user_data import settings + +IDLE_PERIOD = 0.10 + +try: + import coloredlogs + if 'COLOREDLOGS_LEVEL_STYLES' not in os.environ: + os.environ['COLOREDLOGS_LEVEL_STYLES'] = 'spam=22;debug=28;verbose=34;notice=220;warning=202;success=118,bold;error=124;critical=background=red' + # https://pypi.org/project/coloredlogs/ +except ImportError as e: + coloredlogs = False + +global LOG +import logging +LOG = logging.getLogger('app') + +def setup_logging(oArgs): + global LOG + if coloredlogs: + aKw = dict(level=oArgs.loglevel, + logger=LOG, + fmt='%(name)s %(levelname)s %(message)s') + if oArgs.logfile: + oFd = open(oArgs.logfile, 'wt') + setattr(oArgs, 'log_oFd', oFd) + aKw['stream'] = oFd + coloredlogs.install(**aKw) + + else: + aKw = dict(level=oArgs.loglevel, + format='%(name)s %(levelname)-4s %(message)s') + if oArgs.logfile: + aKw['filename'] = oArgs.logfile + logging.basicConfig(**aKw) + + if oArgs.logfile: + oHandler = logging.StreamHandler(stream=sys.stdout) + LOG.addHandler(oHandler) + + logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S') + logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S' + logging._defaultFormatter.default_msec_format = '' + + LOG.setLevel(oArgs.loglevel) + LOG.trace = lambda l: LOG.log(0, repr(l)) + LOG.info(f"Setting loglevel to {oArgs.loglevel!s}") + + if oArgs.loglevel < 20: + # opencv debug + sys.OpenCV_LOADER_DEBUG = True + +#? with ignoreStderr(): for png +# silence logging PyQt5.uic.uiparser +logging.getLogger('PyQt5.uic').setLevel(logging.ERROR) +logging.getLogger('PyQt5.uic.uiparser').setLevel(logging.ERROR) +logging.getLogger('PyQt5.uic.properties').setLevel(logging.ERROR) + +from PyQt5 import QtWidgets, QtGui, QtCore +from qtpy.QtCore import QTimer +from qtpy.QtWidgets import QApplication + +try: + import qdarkstylexxx +except ImportError: + qdarkstyle = None + from middleware import threads import middleware.callbacks as callbacks -from PyQt5 import QtWidgets, QtGui, QtCore import ui.password_screen as password_screen import updater.updater as updater -import os from middleware.tox_factory import tox_factory import wrapper.toxencryptsave as tox_encrypt_save import user_data.toxes +from user_data import settings +from user_data.settings import get_user_config_path, merge_args_into_settings + from user_data.settings import Settings from ui.login_screen import LoginScreen from user_data.profile_manager import ProfileManager @@ -37,26 +116,85 @@ from contacts.group_peer_factory import GroupPeerFactory from user_data.backup_service import BackupService import styles.style # TODO: dynamic loading +from tests.support_testing import lLOCAL, lGOOD, lNEW, lRELAYS, inodeinfo_test +from tests.bootstrap_node_info import iNodeInfo +from tests.tests_socks import main as oTOX_OPTIONS, iMain, ToxOptions +global iI +iI = 0 + +sSTYLE = """ +.QWidget {font-family Helvetica;} +.QCheckBox { font-family Helvetica;} +.QComboBox { font-family Helvetica;} +.QGroupBox { font-family Helvetica;} +.QLabel {font-family Helvetica;} +.QLineEdit { font-family Helvetica;} +.QListWidget { font-family Helvetica;} +.QListWidgetItem { font-family Helvetica;} +.QMainWindow {font-family Helvetica;} +.QMenu {font-family Helvetica;} +.QMenuBar {font-family Helvetica;} +.QPlainText {font-family Courier; weight: 75;} +.QPlainTextEdit {font-family Courier;} +.QPushButton {font-family Helvetica;} +.QRadioButton { font-family Helvetica; } +.QText {font-family Courier; weight: 75; } +.QTextBrowser {font-family Courier; weight: 75; } +.QTextSingleLine {font-family Courier; weight: 75; } +.QToolBar { font-weight: bold; } +""" +from copy import deepcopy class App: - def __init__(self, version, path_to_profile=None, uri=None): + def __init__(self, version, args): + global LOG + self._args = args + self._path = path_to_profile = args.profile + uri = args.uri + logfile = args.logfile + loglevel = args.loglevel + + setup_logging(args) + # sys.stderr.write( 'Command line args: ' +repr(oArgs) +'\n') + LOG.info("Command line: " +' '.join(sys.argv[1:])) + LOG.debug(f'oArgs = {args!r}') + LOG.info("Starting toxygen version " +version) + self._version = version - self._app = self._settings = self._profile_manager = self._plugin_loader = self._messenger = None + self._app = self._settings = self._profile_manager = None + self._plugin_loader = self._messenger = None self._tox = self._ms = self._init = self._main_loop = self._av_loop = None self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None - self._friend_factory = self._calls_manager = self._contacts_manager = self._smiley_loader = None + self._friend_factory = self._calls_manager = None + self._contacts_manager = self._smiley_loader = None self._group_peer_factory = self._tox_dns = self._backup_service = None self._group_factory = self._groups_service = self._profile = None if uri is not None and uri.startswith('tox:'): self._uri = uri[4:] - self._path = path_to_profile # ----------------------------------------------------------------------------------------------------------------- # Public methods # ----------------------------------------------------------------------------------------------------------------- - def main(self): + def set_trace(self): + LOG.debug('pdb.set_trace ') + sys.stdin = sys.__stdin__ + sys.stdout = sys.__stdout__ + import pdb; pdb.set_trace() + + def ten(self, i=0): + global iI + iI += 1 + if logging.getLogger('app').getEffectiveLevel() != 10: + sys.stderr.write('CHANGED '+str(logging.getLogger().level+'\n')) + LOG.setLevel(10) + LOG.root.setLevel(10) + logging.getLogger('app').setLevel(10) + #sys.stderr.write(f"ten '+str(iI)+' {i}"+' '+repr(LOG) +'\n') + #LOG.debug('ten '+str(iI)) + + def iMain(self): """ Main function of app. loads login screen if needed and starts main screen """ @@ -68,74 +206,154 @@ class App: self._load_base_style() - if not self._select_and_load_profile(): - return + encrypt_save = tox_encrypt_save.ToxEncryptSave() + self._toxes = user_data.toxes.ToxES(encrypt_save) + try: + # this throws everything as errors + if not self._select_and_load_profile(): + return 2 + if hasattr(self._args, 'update') and self._args.update: + if self._try_to_update(): return 3 - if self._try_to_update(): - return + self._load_app_styles() + if self._args.language != 'English': + # > /var/local/src/toxygen/toxygen/app.py(303)_load_app_translations()->None + # -> self._app.translator = translator + # (Pdb) Fatal Python error: Segmentation fault + self._load_app_translations() + self._create_dependencies() - self._load_app_styles() - self._load_app_translations() + self._start_threads(True) - self._create_dependencies() - self._start_threads() - - if self._uri is not None: - self._ms.add_contact(self._uri) + if self._uri is not None: + self._ms.add_contact(self._uri) + except Exception as e: + LOG.error(f"Error loading profile: {e!s}") + sys.stderr.write(' iMain(): ' +f"Error loading profile: {e!s}" \ + +'\n' + traceback.format_exc()+'\n') + util_ui.message_box(str(e), + util_ui.tr('Error loading profile')) + return 4 self._app.lastWindowClosed.connect(self._app.quit) + try: + self._execute_app() + self.quit() + retval = 0 + except KeyboardInterrupt: + retval = 0 + except Exception: + retval = 1 - self._execute_app() - - self._stop_app() + return retval # ----------------------------------------------------------------------------------------------------------------- # App executing # ----------------------------------------------------------------------------------------------------------------- def _execute_app(self): + LOG.debug("_execute_app") + while True: try: self._app.exec_() except Exception as ex: - util.log('Unhandled exception: ' + str(ex)) + LOG.error('Unhandled exception: ' + str(ex)) else: break + def quit(self, retval=0): + LOG.debug("quit") + oArgs = self._args + if hasattr(oArgs, 'log_oFd'): + oArgs.log_oFd.close() + delattr(oArgs, 'log_oFd') + + # failsafe: segfaults on exit + if hasattr(self, '_tox'): + if self._tox and hasattr(self._tox, 'kill'): + self._tox.kill() + del self._tox + + self._stop_app() + if hasattr(self, '_app'): + self._app.quit() + del self._app.quit + del self._app + raise SystemExit(retval) + def _stop_app(self): + LOG.debug("_stop_app") self._plugin_loader.stop() - self._stop_threads() - self._file_transfer_handler.stop() - self._tray.hide() + try: + self._stop_threads(is_app_closing=True) + except (Exception, RuntimeError): + # RuntimeError: cannot join current thread + pass + if hasattr(self, '_tray') and self._tray: + self._tray.hide() self._save_profile() self._settings.close() self._kill_toxav() self._kill_tox() + sys.stderr.write('_stop_app end' +'\n') # ----------------------------------------------------------------------------------------------------------------- # App loading # ----------------------------------------------------------------------------------------------------------------- def _load_base_style(self): - with open(util.join_path(util.get_styles_directory(), 'dark_style.qss')) as fl: - style = fl.read() + if self._args.theme in ['', 'default']: return + + if qdarkstyle: + LOG.debug("_load_base_style qdarkstyle " +self._args.theme) + # QDarkStyleSheet + if self._args.theme == 'light': + from qdarkstyle.light.palette import LightPalette + style = qdarkstyle.load_stylesheet(palette=LightPalette) + else: + from qdarkstyle.dark.palette import DarkPalette + style = qdarkstyle.load_stylesheet(palette=DarkPalette) + else: + LOG.debug("_load_base_style qss " +self._args.theme) + name = self._args.theme + '.qss' + with open(util.join_path(util.get_styles_directory(), name)) as fl: + style = fl.read() + style += '\n' +sSTYLE self._app.setStyleSheet(style) def _load_app_styles(self): + LOG.debug(f"_load_app_styles {list(settings.built_in_themes().keys())!r}") # application color scheme - if self._settings['theme'] == 'dark': - return - for theme in self._settings.built_in_themes().keys(): + if self._settings['theme'] in ['', 'default']: return + for theme in settings.built_in_themes().keys(): if self._settings['theme'] != theme: continue - theme_path = self._settings.built_in_themes()[theme] - file_path = util.join_path(util.get_styles_directory(), theme_path) - with open(file_path) as fl: - style = fl.read() + if qdarkstyle: + LOG.debug("_load_base_style qdarkstyle " +self._args.theme) + # QDarkStyleSheet + if self._args.theme == 'light': + from qdarkstyle.light.palette import LightPalette + style = qdarkstyle.load_stylesheet(palette=LightPalette) + else: + from qdarkstyle.dark.palette import DarkPalette + style = qdarkstyle.load_stylesheet(palette=DarkPalette) + else: + theme_path = settings.built_in_themes()[theme] + file_path = util.join_path(util.get_styles_directory(), theme_path) + if not os.path.isfile(file_path): + LOG.warn('_load_app_styles: no theme file ' + file_path) + continue + with open(file_path) as fl: + style = fl.read() + LOG.debug('_load_app_styles: loading theme file ' + file_path) + style += '\n' +sSTYLE self._app.setStyleSheet(style) + LOG.info('_load_app_styles: loaded theme ' +self._args.theme) break def _load_login_screen_translations(self): + LOG.debug("_load_login_screen_translations") current_language, supported_languages = self._get_languages() if current_language not in supported_languages: return @@ -146,47 +364,77 @@ class App: self._app.translator = translator def _load_icon(self): + LOG.debug("_load_icon") icon_file = os.path.join(util.get_images_directory(), 'icon.png') self._app.setWindowIcon(QtGui.QIcon(icon_file)) @staticmethod def _get_languages(): + LOG.debug("_get_languages") current_locale = QtCore.QLocale() curr_language = current_locale.languageToString(current_locale.language()) - supported_languages = Settings.supported_languages() + supported_languages = settings.supported_languages() return curr_language, supported_languages def _load_app_translations(self): - lang = Settings.supported_languages()[self._settings['language']] + LOG.debug("_load_app_translations") + lang = settings.supported_languages()[self._settings['language']] translator = QtCore.QTranslator() translator.load(os.path.join(util.get_translations_directory(), lang)) self._app.installTranslator(translator) self._app.translator = translator def _select_and_load_profile(self): - encrypt_save = tox_encrypt_save.ToxEncryptSave() - self._toxes = user_data.toxes.ToxES(encrypt_save) + LOG.debug("_select_and_load_profile: " +repr(self._path)) - if self._path is not None: # toxygen was started with path to profile - self._load_existing_profile(self._path) + if self._path is not None: + # toxygen was started with path to profile + try: + assert os.path.exists(self._path), self._path + self._load_existing_profile(self._path) + except Exception as e: + LOG.error('_load_existing_profile failed: ' + str(e)) + title = 'Loading the profile failed ' + if self._path: + title += os.path.basename(self._path) + text = 'Loading the profile failed - \n' +str(e) + if 'Dis' == 'Abled': + text += '\nLoading the profile failed - \n' \ + +str(e) +'\nContinue with a default profile?' + reply = util_ui.question(text, title) + if not reply: + LOG.debug('_load_existing_profile not continuing ') + raise + LOG.debug('_load_existing_profile continuing ') + # drop through + else: + util_ui.message_box(text, title) + raise else: auto_profile = Settings.get_auto_profile() if auto_profile is None: # no default profile + LOG.debug('_select_and_load_profile no default profile ') result = self._select_profile() if result is None: + LOG.debug('no selected profile ') return False if result.is_new_profile(): # create new profile if not self._create_new_profile(result.profile_path): + LOG.warn('no new profile ') return False + LOG.debug('created new profile ') else: # load existing profile self._load_existing_profile(result.profile_path) + # drop through self._path = result.profile_path else: # default profile + LOG.debug('loading default profile ') self._path = auto_profile self._load_existing_profile(auto_profile) - if Settings.is_active_profile(self._path): # profile is in use + if settings.is_active_profile(self._path): # profile is in use + LOG.warn(f"_select_and_load_profile active: {self._path}") profile_name = util.get_profile_name_from_path(self._path) title = util_ui.tr('Profile {}').format(profile_name) text = util_ui.tr( @@ -204,21 +452,31 @@ class App: # ----------------------------------------------------------------------------------------------------------------- def _start_threads(self, initial_start=True): + LOG.debug(f"_start_threads before: {threading.enumerate()!r}") # init thread - self._init = threads.InitThread(self._tox, self._plugin_loader, self._settings, initial_start) + self._init = threads.InitThread(self._tox, + self._plugin_loader, + self._settings, + self, + initial_start) self._init.start() + def te(): return [t.name for t in threading.enumerate()] + LOG.debug(f"_start_threads init: {te()!r}") # starting threads for tox iterate and toxav iterate self._main_loop = threads.ToxIterateThread(self._tox) self._main_loop.start() + self._av_loop = threads.ToxAVIterateThread(self._tox.AV) self._av_loop.start() if initial_start: threads.start_file_transfer_thread() + LOG.debug(f"_start_threads after: {[t.name for t in threading.enumerate()]!r}") def _stop_threads(self, is_app_closing=True): - self._init.stop_thread() + LOG.debug("_stop_threads") + self._init.stop_thread(1.0) self._av_loop.stop_thread() self._main_loop.stop_thread() @@ -226,34 +484,50 @@ class App: if is_app_closing: threads.stop_file_transfer_thread() + def iterate(self, n=100): + interval = self._tox.iteration_interval() + for i in range(n): + self._tox.iterate() + gevent.sleep(interval / 1000.0) + # ----------------------------------------------------------------------------------------------------------------- # Profiles # ----------------------------------------------------------------------------------------------------------------- def _select_profile(self): - self._load_login_screen_translations() + LOG.debug("_select_profile") + if self._args.language != 'English': + self._load_login_screen_translations() ls = LoginScreen() profiles = ProfileManager.find_profiles() ls.update_select(profiles) ls.show() self._app.exec_() - return ls.result def _load_existing_profile(self, profile_path): + LOG.info("_load_existing_profile " +repr(profile_path)) + assert os.path.exists(profile_path), profile_path self._profile_manager = ProfileManager(self._toxes, profile_path) data = self._profile_manager.open_profile() if self._toxes.is_data_encrypted(data): + LOG.debug("_entering password") data = self._enter_password(data) - self._settings = Settings(self._toxes, profile_path.replace('.tox', '.json')) - self._tox = self._create_tox(data) + LOG.debug("_entered password") + json_file = profile_path.replace('.tox', '.json') + assert os.path.exists(json_file), json_file + LOG.debug("creating _settings from: " +json_file) + self._settings = Settings(self._toxes, json_file, self) + self._tox = self._create_tox(data, self._settings) + LOG.debug("created _tox") def _create_new_profile(self, profile_name): + LOG.info("_create_new_profile " + profile_name) result = self._get_create_profile_screen_result() if result is None: return False if result.save_into_default_folder: - profile_path = util.join_path(Settings.get_default_path(), profile_name + '.tox') + profile_path = util.join_path(get_user_config_path(), profile_name + '.tox') else: profile_path = util.join_path(util.curr_directory(__file__), profile_name + '.tox') if os.path.isfile(profile_path): @@ -261,19 +535,24 @@ class App: util_ui.tr('Error')) return False name = profile_name or 'toxygen_user' - self._tox = tox_factory() - self._tox.self_set_name(name if name else 'Toxygen User') - self._tox.self_set_status_message('Toxing on Toxygen') + assert self._args self._path = profile_path if result.password: self._toxes.set_password(result.password) - self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) + self._settings = Settings(self._toxes, + self._path.replace('.tox', '.json'), + app=self) + self._tox = self._create_tox(None, + self._settings) + self._tox.self_set_name(name if name else 'Toxygen User') + self._tox.self_set_status_message('Toxing on Toxygen') + self._profile_manager = ProfileManager(self._toxes, profile_path) try: self._save_profile() except Exception as ex: - print(ex) - util.log('Profile creation exception: ' + str(ex)) + #? print(ex) + LOG.error('Profile creation exception: ' + str(ex)) text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') util_ui.message_box(text, util_ui.tr('Error')) @@ -286,6 +565,7 @@ class App: return True def _get_create_profile_screen_result(self): + LOG.debug("_get_create_profile_screen_result") cps = CreateProfileScreen() cps.show() self._app.exec_() @@ -293,6 +573,7 @@ class App: return cps.result def _save_profile(self, data=None): + LOG.debug("_save_profile") data = data or self._tox.get_savedata() self._profile_manager.save_profile(data) @@ -304,15 +585,18 @@ class App: """ Show password screen """ + LOG.debug("_enter_password") p = password_screen.PasswordScreen(self._toxes, data) p.show() self._app.lastWindowClosed.connect(self._app.quit) self._app.exec_() if p.result is not None: return p.result - self._force_exit() + self._force_exit(0) + return None def _reset(self): + LOG.debug("_reset") """ Create new tox instance (new network settings) :return: tox instance @@ -323,50 +607,99 @@ class App: self._save_profile(data) self._kill_toxav() self._kill_tox() - # create new tox instance - self._tox = self._create_tox(data) - self._start_threads(False) + try: + # create new tox instance + self._tox = self._create_tox(data, self._settings) + assert self._tox + self._start_threads(False) - tox_savers = [self._friend_factory, self._group_factory, self._plugin_loader, self._contacts_manager, - self._contacts_provider, self._messenger, self._file_transfer_handler, self._groups_service, - self._profile] - for tox_saver in tox_savers: - tox_saver.set_tox(self._tox) + tox_savers = [self._friend_factory, self._group_factory, + self._plugin_loader, self._contacts_manager, + self._contacts_provider, self._messenger, + self._file_transfer_handler, + self._groups_service, self._profile] + for tox_saver in tox_savers: + tox_saver.set_tox(self._tox) - self._calls_manager.set_toxav(self._tox.AV) - self._contacts_manager.update_friends_numbers() - self._contacts_manager.update_groups_lists() - self._contacts_manager.update_groups_numbers() + self._calls_manager.set_toxav(self._tox.AV) + self._contacts_manager.update_friends_numbers() + self._contacts_manager.update_groups_lists() + self._contacts_manager.update_groups_numbers() - self._init_callbacks() + self._init_callbacks() + except BaseException as e: + LOG.error(f"_reset : {e}") + LOG.debug('_reset: ' \ + +'\n' + traceback.format_exc()) + title = util_ui.tr('Reset Error') + text = util_ui.tr('Error:') + str(e) + util_ui.message_box(text, title) def _create_dependencies(self): - self._backup_service = BackupService(self._settings, self._profile_manager) + LOG.info(f"_create_dependencies toxygen version {self._version}") + if hasattr(self._args, 'update') and self._args.update: + self._backup_service = BackupService(self._settings, + self._profile_manager) self._smiley_loader = SmileyLoader(self._settings) self._tox_dns = ToxDns(self._settings) - self._ms = MainWindow(self._settings, self._tray) - db = Database(self._path.replace('.tox', '.db'), self._toxes) + self._ms = MainWindow(self._settings, self._tray, self) + + db_path = self._path.replace('.tox', '.db') + db = Database(db_path, self._toxes) + if os.path.exists(db_path) and hasattr(db, 'open'): + db.open() + + assert self._tox contact_items_factory = ContactItemsFactory(self._settings, self._ms) - self._friend_factory = FriendFactory(self._profile_manager, self._settings, - self._tox, db, contact_items_factory) - self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) - self._group_peer_factory = GroupPeerFactory(self._tox, self._profile_manager, db, contact_items_factory) - self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory, + self._friend_factory = FriendFactory(self._profile_manager, + self._settings, + self._tox, + db, + contact_items_factory) + self._group_factory = GroupFactory(self._profile_manager, + self._settings, + self._tox, + db, + contact_items_factory) + self._group_peer_factory = GroupPeerFactory(self._tox, + self._profile_manager, + db, + contact_items_factory) + self._contacts_provider = ContactProvider(self._tox, + self._friend_factory, + self._group_factory, self._group_peer_factory) - self._profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) + self._profile = Profile(self._profile_manager, + self._tox, + self._ms, + self._contacts_provider, + self._reset) self._init_profile() self._plugin_loader = PluginLoader(self._settings, self) history = None - messages_items_factory = MessagesItemsFactory(self._settings, self._plugin_loader, self._smiley_loader, - self._ms, lambda m: history.delete_message(m)) - history = History(self._contacts_provider, db, self._settings, self._ms, messages_items_factory) - self._contacts_manager = ContactsManager(self._tox, self._settings, self._ms, self._profile_manager, - self._contacts_provider, history, self._tox_dns, + messages_items_factory = MessagesItemsFactory(self._settings, + self._plugin_loader, + self._smiley_loader, + self._ms, + lambda m: history.delete_message(m)) + history = History(self._contacts_provider, db, + self._settings, self._ms, messages_items_factory) + self._contacts_manager = ContactsManager(self._tox, + self._settings, + self._ms, + self._profile_manager, + self._contacts_provider, + history, self._tox_dns, messages_items_factory) history.set_contacts_manager(self._contacts_manager) - self._calls_manager = CallsManager(self._tox.AV, self._settings, self._ms, self._contacts_manager) - self._messenger = Messenger(self._tox, self._plugin_loader, self._ms, self._contacts_manager, + self._calls_manager = CallsManager(self._tox.AV, + self._settings, + self._ms, + self._contacts_manager, + self) + self._messenger = Messenger(self._tox, + self._plugin_loader, self._ms, self._contacts_manager, self._contacts_provider, messages_items_factory, self._profile, self._calls_manager) file_transfers_message_service = FileTransfersMessagesService(self._contacts_manager, messages_items_factory, @@ -376,49 +709,304 @@ class App: messages_items_factory.set_file_transfers_handler(self._file_transfer_handler) widgets_factory = None widgets_factory_provider = Provider(lambda: widgets_factory) - self._groups_service = GroupsService(self._tox, self._contacts_manager, self._contacts_provider, self._ms, + self._groups_service = GroupsService(self._tox, + self._contacts_manager, + self._contacts_provider, + self._ms, widgets_factory_provider) - widgets_factory = WidgetsFactory(self._settings, self._profile, self._profile_manager, self._contacts_manager, - self._file_transfer_handler, self._smiley_loader, self._plugin_loader, - self._toxes, self._version, self._groups_service, history, + widgets_factory = WidgetsFactory(self._settings, + self._profile, + self._profile_manager, + self._contacts_manager, + self._file_transfer_handler, + self._smiley_loader, + self._plugin_loader, + self._toxes, + self._version, + self._groups_service, + history, self._contacts_provider) - self._tray = tray.init_tray(self._profile, self._settings, self._ms, self._toxes) - self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, self._profile, - self._plugin_loader, self._file_transfer_handler, history, self._calls_manager, - self._groups_service, self._toxes) + if False: + self._tray = tray.init_tray(self._profile, + self._settings, + self._ms, self._toxes) + self._ms.set_dependencies(widgets_factory, + self._tray, + self._contacts_manager, + self._messenger, + self._profile, + self._plugin_loader, + self._file_transfer_handler, + history, + self._calls_manager, + self._groups_service, self._toxes, self) - self._tray.show() + if False: + # the tray icon does not die with the app + self._tray.show() self._ms.show() + # FixMe: + self._log = lambda line: LOG.log(self._args.loglevel, + self._ms.status(line)) + self._ms._log = self._log # used in callbacks.py + self.LOG = self._log # backwards + + if False: + self.status_handler = logging.Handler() + self.status_handler.setLevel(logging.INFO) # self._args.loglevel + self.status_handler.handle = self._ms.status + self._init_callbacks() + LOG.info("_create_dependencies toxygen version " +self._version) def _try_to_update(self): + LOG.debug("_try_to_update") updating = updater.start_update_if_needed(self._version, self._settings) if updating: + LOG.info("Updating toxygen version " +self._version) self._save_profile() self._settings.close() self._kill_toxav() self._kill_tox() return updating - def _create_tox(self, data): - return tox_factory(data, self._settings) + def _create_tox(self, data, settings_): + LOG.info("_create_tox calling tox_factory") + assert self._args + retval = tox_factory(data=data, settings=settings_, + args=self._args, app=self) + LOG.debug("_create_tox succeeded") + return retval - def _force_exit(self): - raise SystemExit() + def _force_exit(self, retval=0): + LOG.debug("_force_exit") + sys.exit(0) - def _init_callbacks(self): - callbacks.init_callbacks(self._tox, self._profile, self._settings, self._plugin_loader, self._contacts_manager, - self._calls_manager, self._file_transfer_handler, self._ms, self._tray, - self._messenger, self._groups_service, self._contacts_provider) + def _init_callbacks(self, ms=None): + LOG.debug("_init_callbacks") + callbacks.init_callbacks(self._tox, self._profile, self._settings, + self._plugin_loader, self._contacts_manager, + self._calls_manager, + self._file_transfer_handler, self._ms, + self._tray, + self._messenger, self._groups_service, + self._contacts_provider, self._ms) def _init_profile(self): + LOG.debug("_init_profile") if not self._profile.has_avatar(): self._profile.reset_avatar(self._settings['identicons']) def _kill_toxav(self): + LOG.debug("_kill_toxav") self._calls_manager.set_toxav(None) self._tox.AV.kill() def _kill_tox(self): + LOG.debug("_kill_tox") self._tox.kill() + + def _test_relays(self, lElts=None): + env = self._test_env() + if lElts is None: + lElts = env['lElts'] + # shuffle(env['lElts']) + LOG.debug(f"_test_relays {len(env['lElts'])}") + for host,port,key in env['lElts'][:10]: + try: + oRet = self._tox.add_tcp_relay(host, port, key) + LOG.debug('add_tcp_relay to ' +host +':' +str(port) \ + +' : ' +str(oRet)) + except Exception as e: + LOG.warn('tox_add_tcp_relay ' +host +' : ' +str(e)) + # LOG.error(traceback.format_exc()) + # LOG.info("Connected status: " +repr(self._tox.self_get_connection_status())) + + def _test_tox(self): + self.test_net() + self._ms.log_console() + + def test_net(self, lElts=None, oThread=None, iMax=4): + + LOG.debug("test_net " +self._args.network) + # bootstrap + LOG.debug('Calling generate_nodes: ') + lNodes = ts.generate_nodes(oArgs=self._args) + if lNodes: + self._settings['current_nodes'] = lNodes + else: + LOG.warn('empty generate_nodes: ') + + # if oThread and oThread._stop_thread: return + LOG.debug("test_net network=" +self._args.network +' iMax=' +str(iMax)) + if self._args.network not in ['local', 'localnew', 'newlocal']: + b = ts.bAreWeConnected() + if b is None: + i = os.system('ip route|grep ^def') + if i > 0: + b = False + else: + b = True + if not b: + LOG.warn("No default route for network " +self._args.network) + text = 'You have no default route - are you connected?' + reply = util_ui.question(text, "Are you connected?") + if not reply: return + iMax = 1 + else: + LOG.debug("Have default route for network " +self._args.network) + + LOG.debug(f"test_net {self._args.network} iMax= {iMax}") + i = 0 + while i < iMax: + # if oThread and oThread._stop_thread: return + i = i + 1 + LOG.debug(f"bootstrapping status # {i}") + self._test_bootstrap() + if hasattr(self._args, 'proxy_type') and self._args.proxy_type > 0: + LOG.debug(f"relaying status # {i}") + self._test_relays() + status = self._tox.self_get_connection_status() + LOG.debug(f"connecting status # {i}" +' : ' +repr(status)) + if status > 0: + LOG.info(f"Connected # {i}" +' : ' +repr(status)) + break + QtCore.QThread.msleep(3000) + # NO QtCore.QCoreApplication.processEvents() + LOG.trace(f"Connected status #{i}: {status!r}") + sleep(1) + + def _test_env(self): + _settings = self._settings + if 'proxy_type' not in _settings or _settings['proxy_type'] == 0 or \ + not _settings['proxy_host'] or not _settings['proxy_port']: + env = dict( prot = 'ipv4') + elif _settings['proxy_type'] == 2: + env = dict(prot = 'socks5', + https_proxy='', \ + socks_proxy='socks5://' \ + +_settings['proxy_host'] +':' \ + +str(_settings['proxy_port'])) + elif _settings['proxy_type'] == 1: + env = dict(prot = 'https', + socks_proxy='', \ + https_proxy='http://' \ + +_settings['proxy_host'] +':' \ + +str(_settings['proxy_port'])) + if 'current_nodes' in _settings and _settings['current_nodes']: + LOG.debug("Using current nodes "+' : ' +str(len(_settings['current_nodes']))) + lElts = _settings['current_nodes'] + elif _settings['network'] in ['local', 'newlocal']: + lElts = lLOCAL + elif _settings['network'] == 'old': + lElts = lGOOD + elif 'proxy_type' not in _settings or _settings['proxy_type'] == 0 or \ + not _settings['proxy_host'] or not _settings['proxy_port']: + lElts = lNEW + else: + lElts = lRELAYS + env['lElts'] = lElts + LOG.debug(f"test_env {len(env['lElts'])}") + return env + + def _test_bootstrap(self, lElts=None): + env = self._test_env() + if lElts is None: + lElts = env['lElts'] + #shuffle(env['lElts']) + LOG.debug(f"_test_bootstrap #Elts={len(lElts)}") + LOG.trace(f"_test_bootstrap lElts={lElts[:10]}") + for host,port,key in lElts[:10]: + try: + assert len(key) == 64, key + assert len(host) <= 16, host + if type(port) == str: + port = int(port) + oRet = self._tox.bootstrap(host, port, key) + LOG.debug('bootstrap to ' +host +':' +str(port) \ + +' : ' +repr(oRet)) + except Exception as e: + LOG.warn('self._tox.bootstrap host=' +host \ + +' port=' +str(port) \ + +' key=' +key \ + +' : ' +str(e)) + # LOG.error(traceback.format_exc()) + + LOG.debug("Connected status: " +repr(self._tox.self_get_connection_status())) + + def _test_socks(self, lElts=None): + LOG.debug("_test_socks") + if not self._tox: return + title = 'Extended Test Suite' + text = 'Run the Extended Test Suite?\nThe program may freeze for 1-10 minutes.' + i = os.system('ip route|grep ^def >/dev/null') + if i > 0: + text += '\nYou have no default route - are you connected?' + reply = util_ui.question(text, title) + if not reply: return + + env = self._test_env() + if lElts is None: + lElts = env['lElts'] + # shuffle(env['lElts']) + try: + inodeinfo_test(env['lElts'], env) + except Exception as e: + # json.decoder.JSONDecodeError + LOG.error(f"test_tox ' +' : {e}") + LOG.error('_test_tox(): ' \ + +'\n' + traceback.format_exc()) + title = 'Extended Test Suite Error' + text = 'Error:' + str(e) + util_ui.message_box(text, title) + + # LOG.info("Connected status: " +repr(self._tox.self_get_connection_status())) + self._ms.log_console() + + def _test_main(self): + from tests.tests_socks import main as tests_main + LOG.debug("_test_socks") + if not self._tox: return + title = 'Extended Test Suite' + text = 'Run the Extended Test Suite?\nThe program may freeze for 20-60 minutes.' + reply = util_ui.question(text, title) + if reply: + if hasattr(self._args, 'proxy_type') and self._args.proxy_type: + lArgs = ['--proxy_host', self._args.proxy_host, + '--proxy_port', str(self._args.proxy_port), + '--proxy_type', str(self._args.proxy_type), ] + else: + lArgs = list() + try: + tests_main(lArgs) + except Exception as e: + LOG.error(f"_test_socks(): {e}") + LOG.error('_test_socks(): ' \ + +'\n' + traceback.format_exc()) + title = 'Extended Test Suite Error' + text = 'Error:' + str(e) + util_ui.message_box(text, title) + self._ms.log_console() + +class GEventProcessing: + """Interoperability class between Qt/gevent that allows processing gevent + tasks during Qt idle periods.""" + def __init__(self, idle_period=IDLE_PERIOD): + # Limit the IDLE handler's frequency while still allow for gevent + # to trigger a microthread anytime + self._idle_period = idle_period + # IDLE timer: on_idle is called whenever no Qt events left for + # processing + self._timer = QTimer() + self._timer.timeout.connect(self.process_events) + self._timer.start(0) + def __enter__(self): + pass + def __exit__(self, *exc_info): + self._timer.stop() + def process_events(self, idle_period=None): + if idle_period is None: + idle_period = self._idle_period + # Cooperative yield, allow gevent to monitor file handles via libevent + gevent.sleep(idle_period) diff --git a/toxygen/av/calls.py b/toxygen/av/calls.py index d5f2fe7..b290798 100644 --- a/toxygen/av/calls.py +++ b/toxygen/av/calls.py @@ -1,14 +1,26 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- import pyaudio import time import threading -from wrapper.toxav_enums import * -import cv2 import itertools -import numpy as np + +from wrapper.toxav_enums import * from av import screen_sharing from av.call import Call import common.tox_save +from utils import ui as util_ui +import tests.support_testing as ts +from middleware.threads import invoke_in_main_thread +from main import sleep +from middleware.threads import BaseThread + +global LOG +import logging +LOG = logging.getLogger('app.'+__name__) + +TIMER_TIMEOUT = 30.0 +bSTREAM_CALLBACK = False class AV(common.tox_save.ToxAvSave): @@ -16,6 +28,13 @@ class AV(common.tox_save.ToxAvSave): super().__init__(toxav) self._settings = settings self._running = True + s = settings + if 'video' not in s: + LOG.warn("AV.__init__ 'video' not in s" ) + LOG.debug(f"AV.__init__ {s!r}" ) + elif 'device' not in s['video']: + LOG.warn("AV.__init__ 'device' not in s.video" ) + LOG.debug(f"AV.__init__ {s['video']!r}" ) self._calls = {} # dict: key - friend number, value - Call instance @@ -25,17 +44,27 @@ class AV(common.tox_save.ToxAvSave): self._audio_running = False self._out_stream = None - self._audio_rate = 8000 self._audio_channels = 1 self._audio_duration = 60 - self._audio_sample_count = self._audio_rate * self._audio_channels * self._audio_duration // 1000 + self._audio_rate_pa = 48000 + self._audio_rate_tox = 48000 + self._audio_rate_pa = 48000 + self._audio_krate_tox_audio = self._audio_rate_tox // 1000 + self._audio_krate_tox_video = 5000 + self._audio_sample_count_pa = self._audio_rate_pa * self._audio_channels * self._audio_duration // 1000 + self._audio_sample_count_tox = self._audio_rate_tox * self._audio_channels * self._audio_duration // 1000 self._video = None self._video_thread = None self._video_running = False - self._video_width = 640 - self._video_height = 480 + self._video_width = 320 + self._video_height = 240 + + iOutput = self._settings._args.audio['output'] + self.lPaSampleratesO = ts.lSdSamplerates(iOutput) + global oPYA + oPYA = self._audio = pyaudio.PyAudio() def stop(self): self._running = False @@ -51,27 +80,70 @@ class AV(common.tox_save.ToxAvSave): def __call__(self, friend_number, audio, video): """Call friend with specified number""" - self._toxav.call(friend_number, 32 if audio else 0, 5000 if video else 0) + if friend_number in self._calls: + LOG.warn(f"__call__ already has {friend_number}") + return + if self._audio_krate_tox_audio not in ts.lToxSampleratesK: + LOG.warn(f"__call__ {self._audio_krate_tox_audio} not in {ts.lToxSampleratesK}") + + try: + self._toxav.call(friend_number, + self._audio_krate_tox_audio if audio else 0, + self._audio_krate_tox_video if video else 0) + except ArgumentError as e: + LOG.warn(f"_toxav.call already has {friend_number}") + return self._calls[friend_number] = Call(audio, video) - threading.Timer(30.0, lambda: self.finish_not_started_call(friend_number)).start() + threading.Timer(TIMER_TIMEOUT, + lambda: self.finish_not_started_call(friend_number)).start() def accept_call(self, friend_number, audio_enabled, video_enabled): + # obsolete + return call_accept_call(self, friend_number, audio_enabled, video_enabled) + + def call_accept_call(self, friend_number, audio_enabled, video_enabled): + LOG.debug(f"call_accept_call from {friend_number} {self._running}" + + f"{audio_enabled} {video_enabled}") + # import pdb; pdb.set_trace() - gets into q Qt exec_ problem + # ts.trepan_handler() + + if self._audio_krate_tox_audio not in ts.lToxSampleratesK: + LOG.warn(f"__call__ {self._audio_krate_tox_audio} not in {ts.lToxSampleratesK}") if self._running: self._calls[friend_number] = Call(audio_enabled, video_enabled) - self._toxav.answer(friend_number, 32 if audio_enabled else 0, 5000 if video_enabled else 0) + # audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending. + # video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending. + try: + self._toxav.answer(friend_number, + self._audio_krate_tox_audio if audio_enabled else 0, + self._audio_krate_tox_video if video_enabled else 0) + except ArgumentError as e: + LOG.debug(f"AV accept_call error from {friend_number} {self._running}" + + f"{e}") + raise if audio_enabled: + # may raise self.start_audio_thread() if video_enabled: + # may raise self.start_video_thread() def finish_call(self, friend_number, by_friend=False): + LOG.debug(f"finish_call {friend_number}") if not by_friend: self._toxav.call_control(friend_number, TOXAV_CALL_CONTROL['CANCEL']) if friend_number in self._calls: del self._calls[friend_number] - if not len(list(filter(lambda c: c.out_audio, self._calls))): + try: + # AttributeError: 'int' object has no attribute 'out_audio' + if not len(list(filter(lambda c: c.out_audio, self._calls))): + self.stop_audio_thread() + if not len(list(filter(lambda c: c.out_video, self._calls))): + self.stop_video_thread() + except Exception as e: + LOG.error(f"finish_call FixMe: {e}") + # dunno self.stop_audio_thread() - if not len(list(filter(lambda c: c.out_video, self._calls))): self.stop_video_thread() def finish_not_started_call(self, friend_number): @@ -84,6 +156,7 @@ class AV(common.tox_save.ToxAvSave): """ New call state """ + LOG.debug(f"toxav_call_state_cb {friend_number}") call = self._calls[friend_number] call.is_active = True @@ -107,31 +180,80 @@ class AV(common.tox_save.ToxAvSave): """ Start audio sending """ + global oPYA + iInput = self._settings._args.audio['input'] if self._audio_thread is not None: + LOG.warn(f"start_audio_thread device={iInput}") return + iInput = self._settings._args.audio['input'] + LOG.debug(f"start_audio_thread device={iInput}") + lPaSamplerates = ts.lSdSamplerates(iInput) + if not(len(lPaSamplerates)): + e = f"No supported sample rates for device: audio[input]={iInput!r}" + LOG.error(f"No supported sample rates {e}") + raise RuntimeError(e) + if not self._audio_rate_pa in lPaSamplerates: + LOG.warn(f"{self._audio_rate_pa} not in {lPaSamplerates!r}") + if False: + self._audio_rate_pa = oPYA.get_device_info_by_index(iInput)['defaultSampleRate'] + else: + LOG.warn(f"Setting audio_rate to: {lPaSamplerates[0]}") + self._audio_rate_pa = lPaSamplerates[0] - self._audio_running = True + try: + LOG.debug( f"start_audio_thread framerate: {self._audio_rate_pa}" \ + +f" device: {iInput}" + +f" supported: {lPaSamplerates!r}") + if self._audio_rate_pa not in lPaSamplerates: + LOG.warn(f"PAudio sampling rate was {self._audio_rate_pa} changed to {lPaSamplerates[0]}") + self._audio_rate_pa = lPaSamplerates[0] - self._audio = pyaudio.PyAudio() - self._audio_stream = self._audio.open(format=pyaudio.paInt16, - rate=self._audio_rate, - channels=self._audio_channels, - input=True, - input_device_index=self._settings.audio['input'], - frames_per_buffer=self._audio_sample_count * 10) + if bSTREAM_CALLBACK: + self._audio_stream = oPYA.open(format=pyaudio.paInt16, + rate=self._audio_rate_pa, + channels=self._audio_channels, + input=True, + input_device_index=iInput, + frames_per_buffer=self._audio_sample_count_pa * 10, + stream_callback=self.send_audio_data) + self._audio_running = True + self._audio_stream.start_stream() + while self._audio_stream.is_active(): + sleep(0.1) + self._audio_stream.stop_stream() + self._audio_stream.close() - self._audio_thread = threading.Thread(target=self.send_audio) - self._audio_thread.start() + else: + self._audio_stream = oPYA.open(format=pyaudio.paInt16, + rate=self._audio_rate_pa, + channels=self._audio_channels, + input=True, + input_device_index=iInput, + frames_per_buffer=self._audio_sample_count_pa * 10) + self._audio_running = True + self._audio_thread = BaseThread(target=self.send_audio, + name='_audio_thread') + self._audio_thread.start() + + except Exception as e: + LOG.error(f"Starting self._audio.open {e}") + LOG.debug(repr(dict(format=pyaudio.paInt16, + rate=self._audio_rate_pa, + channels=self._audio_channels, + input=True, + input_device_index=iInput, + frames_per_buffer=self._audio_sample_count_pa * 10))) + # catcher in place in calls_manager + raise RuntimeError(e) + else: + LOG.debug(f"start_audio_thread {self._audio_stream!r}") def stop_audio_thread(self): if self._audio_thread is None: return - self._audio_running = False - self._audio_thread.join() - self._audio_thread = None self._audio_stream = None self._audio = None @@ -144,21 +266,39 @@ class AV(common.tox_save.ToxAvSave): def start_video_thread(self): if self._video_thread is not None: return + s = self._settings + if 'video' not in s: + LOG.warn("AV.__init__ 'video' not in s" ) + LOG.debug(f"start_video_thread {s!r}" ) + raise RuntimeError("start_video_thread not 'video' in s)" ) + elif 'device' not in s['video']: + LOG.error("start_video_thread not 'device' in s['video']" ) + LOG.debug(f"start_video_thread {s['video']!r}" ) + raise RuntimeError("start_video_thread not 'device' ins s['video']" ) + self._video_width = s['video']['width'] + self._video_height = s['video']['height'] + + LOG.info("start_video_thread " \ + +f" device: {s['video']['device']}" \ + +f" supported: {s['video']['width']} {s['video']['height']}") + + s['video']['device'] = -1 + if s['video']['device'] == -1: + self._video = screen_sharing.DesktopGrabber(s['video']['x'], + s['video']['y'], + s['video']['width'], + s['video']['height']) + else: + with ts.ignoreStdout(): + import cv2 + self._video = cv2.VideoCapture(s['video']['device']) + self._video.set(cv2.CAP_PROP_FPS, 25) + self._video.set(cv2.CAP_PROP_FRAME_WIDTH, self._video_width) + self._video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._video_height) self._video_running = True - self._video_width = s.video['width'] - self._video_height = s.video['height'] - - if s.video['device'] == -1: - self._video = screen_sharing.DesktopGrabber(self._settings.video['x'], self._settings.video['y'], - self._settings.video['width'], self._settings.video['height']) - else: - self._video = cv2.VideoCapture(self._settings.video['device']) - self._video.set(cv2.CAP_PROP_FPS, 25) - self._video.set(cv2.CAP_PROP_FRAME_WIDTH, self._video_width) - self._video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._video_height) - - self._video_thread = threading.Thread(target=self.send_video) + self._video_thread = BaseThread(target=self.send_video, + name='_video_thread') self._video_thread.start() def stop_video_thread(self): @@ -166,7 +306,17 @@ class AV(common.tox_save.ToxAvSave): return self._video_running = False - self._video_thread.join() + i = 0 + while i < ts.iTHREAD_JOINS: + self._video_thread.join(ts.iTHREAD_TIMEOUT) + try: + if not self._video_thread.is_alive(): break + except: + # AttributeError: 'NoneType' object has no attribute 'join' + break + i = i + 1 + else: + LOG.warn("self._video_thread.is_alive BLOCKED") self._video_thread = None self._video = None @@ -180,58 +330,109 @@ class AV(common.tox_save.ToxAvSave): """ if self._out_stream is None: - self._out_stream = self._audio.open(format=pyaudio.paInt16, - channels=channels_count, - rate=rate, - output_device_index=self._settings.audio['output'], - output=True) + iOutput = self._settings._args.audio['output'] + if not rate in self.lPaSampleratesO: + LOG.warn(f"{rate} not in {self.lPaSampleratesO!r}") + if False: + rate = oPYA.get_device_info_by_index(iOutput)['defaultSampleRate'] + LOG.warn(f"Setting audio_rate to: {self.lPaSampleratesO[0]}") + rate = self.lPaSampleratesO[0] + try: + with ts.ignoreStderr(): + self._out_stream = oPYA.open(format=pyaudio.paInt16, + channels=channels_count, + rate=rate, + output_device_index=iOutput, + output=True) + except Exception as e: + LOG.error(f"Error playing audio_chunk creating self._out_stream {e}") + LOG.debug(f"audio_chunk output_device_index={self._settings._args.audio['input']} rate={rate} channels={channels_count}") + invoke_in_main_thread(util_ui.message_box, + str(e), + util_ui.tr("Error Chunking audio")) + # dunno + self.stop() + return + self._out_stream.write(samples) # ----------------------------------------------------------------------------------------------------------------- # AV sending # ----------------------------------------------------------------------------------------------------------------- + def send_audio_data(self, data, count, *largs, **kwargs): + pcm = data + # :param sampling_rate: Audio sampling rate used in this frame. + if self._toxav is None: + raise RuntimeError("_toxav not initialized") + if self._audio_rate_tox not in ts.lToxSamplerates: + LOG.warn(f"ToxAudio sampling rate was {self._audio_rate_tox} changed to {ts.lToxSamplerates[0]}") + self._audio_rate_tox = ts.lToxSamplerates[0] + + for friend_num in self._calls: + if self._calls[friend_num].out_audio: + try: + # app.av.calls ERROR Error send_audio: One of the frame parameters was invalid. E.g. the resolution may be too small or too large, or the audio sampling rate may be unsupported + self._toxav.audio_send_frame(friend_num, + pcm, + count, + self._audio_channels, + self._audio_rate_tox) + except Exception as e: + LOG.error(f"Error send_audio audio_send_frame: {e}") + LOG.debug(f"send_audio self._audio_rate_tox={self._audio_rate_tox} self._audio_channels={self._audio_channels}") + invoke_in_main_thread(util_ui.message_box, + str(e), + util_ui.tr("Error send_audio audio_send_frame")) + pass + def send_audio(self): """ This method sends audio to friends """ - + i=0 + count = self._audio_sample_count_tox + LOG.debug(f"send_audio stream={self._audio_stream}") while self._audio_running: try: - pcm = self._audio_stream.read(self._audio_sample_count) - if pcm: - for friend_num in self._calls: - if self._calls[friend_num].out_audio: - try: - self._toxav.audio_send_frame(friend_num, pcm, self._audio_sample_count, - self._audio_channels, self._audio_rate) - except: - pass + pcm = self._audio_stream.read(count, exception_on_overflow=False) + if not pcm: + sleep(0.1) + else: + self.send_audio_data(pcm, count) except: pass - - time.sleep(0.01) + i += 1 + LOG.debug(f"send_audio {i}") + sleep(0.01) def send_video(self): """ This method sends video to friends """ + LOG.debug(f"send_video thread={threading.current_thread()}" + +f" self._video_running={self._video_running}" + +f" device: {self._settings['video']['device']}" ) while self._video_running: try: result, frame = self._video.read() if result: + LOG.warn(f"send_video video_send_frame _video.read") + else: height, width, channels = frame.shape for friend_num in self._calls: if self._calls[friend_num].out_video: try: y, u, v = self.convert_bgr_to_yuv(frame) self._toxav.video_send_frame(friend_num, width, height, y, u, v) - except: + except Exception as e: + LOG.debug(f"send_video video_send_frame ERROR {e}") pass + except: pass - time.sleep(0.01) + sleep(0.1) def convert_bgr_to_yuv(self, frame): """ @@ -264,11 +465,14 @@ class AV(common.tox_save.ToxAvSave): Y, U, V can be extracted using slices and joined in one list using itertools.chain.from_iterable() Function returns bytes(y), bytes(u), bytes(v), because it is required for ctypes """ + with ts.ignoreStdout(): + import cv2 frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV_I420) y = frame[:self._video_height, :] y = list(itertools.chain.from_iterable(y)) + import numpy as np u = np.zeros((self._video_height // 2, self._video_width // 2), dtype=np.int) u[::2, :] = frame[self._video_height:self._video_height * 5 // 4, :self._video_width // 2] u[1::2, :] = frame[self._video_height:self._video_height * 5 // 4, self._video_width // 2:] diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py index 5a48672..7e44cf5 100644 --- a/toxygen/av/calls_manager.py +++ b/toxygen/av/calls_manager.py @@ -1,22 +1,30 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- + +import sys import threading -import cv2 + import av.calls from messenger.messages import * from ui import av_widgets import common.event as event +import utils.ui as util_ui +global LOG +import logging +LOG = logging.getLogger('app.'+__name__) class CallsManager: - def __init__(self, toxav, settings, screen, contacts_manager): + def __init__(self, toxav, settings, main_screen, contacts_manager, app=None): self._call = av.calls.AV(toxav, settings) # object with data about calls self._call_widgets = {} # dict of incoming call widgets self._incoming_calls = set() self._settings = settings - self._screen = screen + self._main_screen = main_screen self._contacts_manager = contacts_manager self._call_started_event = event.Event() # friend_number, audio, video, is_outgoing self._call_finished_event = event.Event() # friend_number, is_declined + self._app = app def set_toxav(self, toxav): self._call.set_toxav(toxav) @@ -45,10 +53,10 @@ class CallsManager: if not self._contacts_manager.is_active_a_friend(): return if num not in self._call and self._contacts_manager.is_active_online(): # start call - if not self._settings.audio['enabled']: + if not self._settings['audio']['enabled']: return self._call(num, audio, video) - self._screen.active_call() + self._main_screen.active_call() self._call_started_event(num, audio, video, True) elif num in self._call: # finish or cancel call if you call with active friend self.stop_call(num, False) @@ -57,13 +65,13 @@ class CallsManager: """ Incoming call from friend. """ - if not self._settings.audio['enabled']: - return + LOG.debug(__name__ +f" incoming_call {friend_number}") + # if not self._settings['audio']['enabled']: return friend = self._contacts_manager.get_friend_by_number(friend_number) self._call_started_event(friend_number, audio, video, False) self._incoming_calls.add(friend_number) if friend_number == self._contacts_manager.get_active_number(): - self._screen.incoming_call() + self._main_screen.incoming_call() else: friend.actions = True text = util_ui.tr("Incoming video call") if video else util_ui.tr("Incoming audio call") @@ -74,31 +82,73 @@ class CallsManager: def accept_call(self, friend_number, audio, video): """ Accept incoming call with audio or video + Called from a thread """ - self._call.accept_call(friend_number, audio, video) - self._screen.active_call() - if friend_number in self._incoming_calls: - self._incoming_calls.remove(friend_number) - del self._call_widgets[friend_number] + LOG.debug(f"CM accept_call from {friend_number} {audio} {video}") + sys.stdout.flush() + + try: + self._call.call_accept_call(friend_number, audio, video) + except Exception as e: + LOG.error(f"accept_call _call.accept_call ERROR for {friend_number} {e}") + self._main_screen.call_finished() + if hasattr(self._main_screen, '_settings') and \ + 'audio' in self._main_screen._settings and \ + 'input' in self._main_screen._settings['audio']: + iInput = self._settings['audio']['input'] + iOutput = self._settings['audio']['output'] + iVideo = self._settings['video']['device'] + LOG.debug(f"iInput={iInput} iOutput={iOutput} iVideo={iVideo}") + elif hasattr(self._main_screen, '_settings') and \ + hasattr(self._main_screen._settings, 'audio') and \ + 'input' not in self._main_screen._settings['audio']: + LOG.warn(f"'audio' not in {self._main_screen._settings!r}") + elif hasattr(self._main_screen, '_settings') and \ + hasattr(self._main_screen._settings, 'audio') and \ + 'input' not in self._main_screen._settings['audio']: + LOG.warn(f"'audio' not in {self._main_screen._settings!r}") + else: + LOG.warn(f"_settings not in self._main_screen") + util_ui.message_box(str(e), + util_ui.tr('ERROR Accepting call from {friend_number}')) + else: + self._main_screen.active_call() + + finally: + # does not terminate call - just the av_widget + if friend_number in self._incoming_calls: + self._incoming_calls.remove(friend_number) + try: + self._call_widgets[friend_number].close() + del self._call_widgets[friend_number] + except: + # RuntimeError: wrapped C/C++ object of type IncomingCallWidget has been deleted + + pass + LOG.debug(f" closed self._call_widgets[{friend_number}]") + def stop_call(self, friend_number, by_friend): """ Stop call with friend """ + LOG.debug(__name__+f" stop_call {friend_number}") if friend_number in self._incoming_calls: self._incoming_calls.remove(friend_number) is_declined = True else: is_declined = False - self._screen.call_finished() - is_video = self._call.is_video_call(friend_number) + self._main_screen.call_finished() self._call.finish_call(friend_number, by_friend) # finish or decline call if friend_number in self._call_widgets: self._call_widgets[friend_number].close() del self._call_widgets[friend_number] def destroy_window(): + #??? FixMed + is_video = self._call.is_video_call(friend_number) if is_video: + import cv2 cv2.destroyWindow(str(friend_number)) threading.Timer(2.0, destroy_window).start() diff --git a/toxygen/av/screen_sharing.py b/toxygen/av/screen_sharing.py index 265658c..3739f0c 100644 --- a/toxygen/av/screen_sharing.py +++ b/toxygen/av/screen_sharing.py @@ -1,4 +1,3 @@ -import numpy as np from PyQt5 import QtWidgets @@ -17,6 +16,7 @@ class DesktopGrabber: pixmap = self._screen.grabWindow(0, self._x, self._y, self._width, self._height) image = pixmap.toImage() s = image.bits().asstring(self._width * self._height * 4) + import numpy as np arr = np.fromstring(s, dtype=np.uint8).reshape((self._height, self._width, 4)) return True, arr diff --git a/toxygen/bootstrap/bootstrap.py b/toxygen/bootstrap/bootstrap.py index fad68c4..a8df3d4 100644 --- a/toxygen/bootstrap/bootstrap.py +++ b/toxygen/bootstrap/bootstrap.py @@ -1,83 +1,49 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- import random import urllib.request from utils.util import * -from PyQt5 import QtNetwork, QtCore -import json +from PyQt5 import QtNetwork +from PyQt5 import QtCore +try: + import requests +except ImportError: + requests = None +try: + import pycurl + import certifi + from io import BytesIO +except ImportError: + pycurl = None +from user_data.settings import get_user_config_path +from tests.support_testing import download_url, _get_nodes_path -DEFAULT_NODES_COUNT = 4 +global LOG +import logging +LOG = logging.getLogger('app.'+'bootstrap') - -class Node: - - def __init__(self, node): - self._ip, self._port, self._tox_key = node['ipv4'], node['port'], node['public_key'] - self._priority = random.randint(1, 1000000) if node['status_tcp'] and node['status_udp'] else 0 - - def get_priority(self): - return self._priority - - priority = property(get_priority) - - def get_data(self): - return self._ip, self._port, self._tox_key - - -def generate_nodes(nodes_count=DEFAULT_NODES_COUNT): - with open(_get_nodes_path(), 'rt') as fl: - json_nodes = json.loads(fl.read())['nodes'] - nodes = map(lambda json_node: Node(json_node), json_nodes) - nodes = filter(lambda n: n.priority > 0, nodes) - sorted_nodes = sorted(nodes, key=lambda x: x.priority) - if nodes_count is not None: - sorted_nodes = sorted_nodes[-DEFAULT_NODES_COUNT:] - for node in sorted_nodes: - yield node.get_data() - - -def download_nodes_list(settings): - url = 'https://nodes.tox.chat/json' +def download_nodes_list(settings, oArgs): if not settings['download_nodes_list']: - return + return '' + url = settings['download_nodes_url'] + path = _get_nodes_path(oArgs=oArgs) + # dont download blindly so we can edit the file and not block on startup + if os.path.isfile(path): + with open(path, 'rt') as fl: + result = fl.read() + return result + LOG.debug("downloading list of nodes") + result = download_url(url, settings._app) + if not result: + LOG.warn("failed downloading list of nodes") + return '' + LOG.info("downloaded list of nodes") + _save_nodes(result, settings._app) + return result - if not settings['proxy_type']: # no proxy - try: - req = urllib.request.Request(url) - req.add_header('Content-Type', 'application/json') - response = urllib.request.urlopen(req) - result = response.read() - _save_nodes(result) - except Exception as ex: - log('TOX nodes loading error: ' + str(ex)) - else: # proxy - netman = QtNetwork.QNetworkAccessManager() - proxy = QtNetwork.QNetworkProxy() - proxy.setType( - QtNetwork.QNetworkProxy.Socks5Proxy if settings['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy) - proxy.setHostName(settings['proxy_host']) - proxy.setPort(settings['proxy_port']) - netman.setProxy(proxy) - try: - request = QtNetwork.QNetworkRequest() - request.setUrl(QtCore.QUrl(url)) - reply = netman.get(request) - - while not reply.isFinished(): - QtCore.QThread.msleep(1) - QtCore.QCoreApplication.processEvents() - data = bytes(reply.readAll().data()) - _save_nodes(data) - except Exception as ex: - log('TOX nodes loading error: ' + str(ex)) - - -def _get_nodes_path(): - return join_path(curr_directory(__file__), 'nodes.json') - - -def _save_nodes(nodes): +def _save_nodes(nodes, app): if not nodes: return - print('Saving nodes...') - with open(_get_nodes_path(), 'wb') as fl: + with open(_get_nodes_path(oArgs=app._args), 'wb') as fl: + LOG.info("Saving nodes to " +_get_nodes_path()) fl.write(nodes) diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py index 2058890..f1ae6bf 100644 --- a/toxygen/contacts/basecontact.py +++ b/toxygen/contacts/basecontact.py @@ -1,3 +1,4 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- from user_data.settings import * from PyQt5 import QtCore, QtGui from wrapper.toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index e88acf2..b680c58 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -1,10 +1,17 @@ -from history.database import * +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- +from history.database import TIMEOUT, \ + SAVE_MESSAGES, MESSAGE_AUTHOR + from contacts import basecontact, common from messenger.messages import * from contacts.contact_menu import * from file_transfers import file_transfers as ft import re +# LOG=util.log +global LOG +import logging +LOG = logging.getLogger('app.'+__name__) class Contact(basecontact.BaseContact): """ @@ -139,7 +146,7 @@ class Contact(basecontact.BaseContact): and m.tox_message_id == tox_message_id, self._corr))[0] message.mark_as_sent() except Exception as ex: - util.log('Mark as sent ex: ' + str(ex)) + LOG.error(f"Mark as sent: {ex!s}") # ----------------------------------------------------------------------------------------------------------------- # Message deletion diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index 8178d31..0e4922e 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -1,6 +1,12 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- from PyQt5 import QtWidgets -import utils.ui as util_ui +import utils.ui as util_ui +from wrapper.toxcore_enums_and_consts import * + +global LOG +import logging +LOG = logging.getLogger('app') # ----------------------------------------------------------------------------------------------------------------- # Builder @@ -99,8 +105,8 @@ class BaseContactMenuGenerator: (copy_menu_builder .with_name(util_ui.tr('Copy')) .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name)) - .with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.status_message)) - .with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id)) + .with_action(util_ui.tr("Status message"), lambda: main_screen.copy_text(self._contact.status_message)) + .with_action(util_ui.tr("Public key"), lambda: main_screen.copy_text(self._contact.tox_id)) ) return copy_menu_builder @@ -108,11 +114,11 @@ class BaseContactMenuGenerator: def _generate_history_menu_builder(self, history_loader, main_screen): history_menu_builder = ContactMenuBuilder() (history_menu_builder - .with_name(util_ui.tr('Chat history')) - .with_action(util_ui.tr('Clear history'), lambda: history_loader.clear_history(self._contact) + .with_name(util_ui.tr("Chat history")) + .with_action(util_ui.tr("Clear history"), lambda: history_loader.clear_history(self._contact) or main_screen.messages.clear()) - .with_action(util_ui.tr('Export as text'), lambda: history_loader.export_history(self._contact)) - .with_action(util_ui.tr('Export as HTML'), lambda: history_loader.export_history(self._contact, False)) + .with_action(util_ui.tr("Export as text"), lambda: history_loader.export_history(self._contact)) + .with_action(util_ui.tr("Export as HTML"), lambda: history_loader.export_history(self._contact, False)) ) return history_menu_builder @@ -127,16 +133,16 @@ class FriendMenuGenerator(BaseContactMenuGenerator): groups_menu_builder = self._generate_groups_menu(contacts_manager, groups_service) allowed = self._contact.tox_id in settings['auto_accept_from_friends'] - auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') + auto = util_ui.tr("Disallow auto accept") if allowed else util_ui.tr('Allow auto accept') builder = ContactMenuBuilder() menu = (builder - .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_action(util_ui.tr("Set alias"), lambda: main_screen.set_alias(number)) .with_submenu(history_menu_builder) .with_submenu(copy_menu_builder) .with_action(auto, lambda: main_screen.auto_accept(number, not allowed)) - .with_action(util_ui.tr('Remove friend'), lambda: main_screen.remove_friend(number)) - .with_action(util_ui.tr('Block friend'), lambda: main_screen.block_friend(number)) + .with_action(util_ui.tr("Remove friend"), lambda: main_screen.remove_friend(number)) + .with_action(util_ui.tr("Block friend"), lambda: main_screen.block_friend(number)) .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) .with_optional_submenu(plugins_menu_builder) .with_optional_submenu(groups_menu_builder) @@ -165,11 +171,13 @@ class FriendMenuGenerator(BaseContactMenuGenerator): def _generate_groups_menu(self, contacts_manager, groups_service): chats = contacts_manager.get_group_chats() + LOG.debug(f"_generate_groups_menu len(chats)={len(chats)} or self._contact.status={self._contact.status}") if not len(chats) or self._contact.status is None: - return None + #? return None + pass groups_menu_builder = ContactMenuBuilder() (groups_menu_builder - .with_name(util_ui.tr('Invite to group')) + .with_name(util_ui.tr("Invite to group")) .with_actions([(g.name, lambda: groups_service.invite_friend(self._contact.number, g.number)) for g in chats]) ) @@ -184,26 +192,26 @@ class GroupMenuGenerator(BaseContactMenuGenerator): builder = ContactMenuBuilder() menu = (builder - .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_action(util_ui.tr("Set alias"), lambda: main_screen.set_alias(number)) .with_submenu(copy_menu_builder) .with_submenu(history_menu_builder) - .with_optional_action(util_ui.tr('Manage group'), + .with_optional_action(util_ui.tr("Manage group"), lambda: groups_service.show_group_management_screen(self._contact), self._contact.is_self_founder()) - .with_optional_action(util_ui.tr('Group settings'), + .with_optional_action(util_ui.tr("Group settings"), lambda: groups_service.show_group_settings_screen(self._contact), not self._contact.is_self_founder()) - .with_optional_action(util_ui.tr('Set topic'), + .with_optional_action(util_ui.tr("Set topic"), lambda: groups_service.set_group_topic(self._contact), self._contact.is_self_moderator_or_founder()) - .with_action(util_ui.tr('Bans list'), - lambda: groups_service.show_bans_list(self._contact)) - .with_action(util_ui.tr('Reconnect to group'), +# .with_action(util_ui.tr("Bans list"), +# lambda: groups_service.show_bans_list(self._contact)) + .with_action(util_ui.tr("Reconnect to group"), lambda: groups_service.reconnect_to_group(self._contact.number)) - .with_optional_action(util_ui.tr('Disconnect from group'), + .with_optional_action(util_ui.tr("Disconnect from group"), lambda: groups_service.disconnect_from_group(self._contact.number), self._contact.status is not None) - .with_action(util_ui.tr('Leave group'), lambda: groups_service.leave_group(self._contact.number)) + .with_action(util_ui.tr("Leave group"), lambda: groups_service.leave_group(self._contact.number)) .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) ).build() @@ -218,10 +226,10 @@ class GroupPeerMenuGenerator(BaseContactMenuGenerator): builder = ContactMenuBuilder() menu = (builder - .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_action(util_ui.tr("Set alias"), lambda: main_screen.set_alias(number)) .with_submenu(copy_menu_builder) .with_submenu(history_menu_builder) - .with_action(util_ui.tr('Quit chat'), + .with_action(util_ui.tr("Quit chat"), lambda: contacts_manager.remove_group_peer(self._contact)) .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) ).build() diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py index 76e8e79..8dda974 100644 --- a/toxygen/contacts/contact_provider.py +++ b/toxygen/contacts/contact_provider.py @@ -15,8 +15,10 @@ class ContactProvider(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def get_friend_by_number(self, friend_number): - public_key = self._tox.friend_get_public_key(friend_number) - + try: + public_key = self._tox.friend_get_public_key(friend_number) + except Exception as e: + return None return self.get_friend_by_public_key(public_key) def get_friend_by_public_key(self, public_key): @@ -29,7 +31,10 @@ class ContactProvider(tox_save.ToxSave): return friend def get_all_friends(self): - friend_numbers = self._tox.self_get_friend_list() + try: + friend_numbers = self._tox.self_get_friend_list() + except Exception as e: + return None friends = map(lambda n: self.get_friend_by_number(n), friend_numbers) return list(friends) @@ -39,13 +44,19 @@ class ContactProvider(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def get_all_groups(self): - group_numbers = range(self._tox.group_get_number_groups()) + try: + group_numbers = range(self._tox.group_get_number_groups()) + except Exception as e: + return None groups = map(lambda n: self.get_group_by_number(n), group_numbers) return list(groups) def get_group_by_number(self, group_number): - public_key = self._tox.group_get_chat_id(group_number) + try: + public_key = self._tox.group_get_chat_id(group_number) + except Exception as e: + return None return self.get_group_by_public_key(public_key) @@ -67,8 +78,8 @@ class ContactProvider(tox_save.ToxSave): def get_group_peer_by_id(self, group, peer_id): peer = group.get_peer_by_id(peer_id) - - return self._get_group_peer(group, peer) + if peer: + return self._get_group_peer(group, peer) def get_group_peer_by_public_key(self, group, public_key): peer = group.get_peer_by_public_key(public_key) diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 87a61ff..c0ec665 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,9 +1,15 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- from contacts.friend import Friend from contacts.group_chat import GroupChat from messenger.messages import * from common.tox_save import ToxSave from contacts.group_peer_contact import GroupPeerContact +# LOG=util.log +global LOG +import logging +LOG = logging.getLogger('app.'+__name__) +log = lambda x: LOG.info(x) class ContactsManager(ToxSave): """ @@ -129,8 +135,8 @@ class ContactsManager(ToxSave): self._set_current_contact_data(contact) self._active_contact_changed(contact) except Exception as ex: # no friend found. ignore - util.log('Friend value: ' + str(value)) - util.log('Error in set active: ' + str(ex)) + LOG.warn(f"no friend found. Friend value: {value!s}") + LOG.error('in set active: ' + str(ex)) raise active_contact = property(get_active, set_active) @@ -235,8 +241,9 @@ class ContactsManager(ToxSave): def get_or_create_group_peer_contact(self, group_number, peer_id): group = self.get_group_by_number(group_number) peer = group.get_peer_by_id(peer_id) - if not self.check_if_contact_exists(peer.public_key): - self.add_group_peer(group, peer) + if peer: # broken + if not self.check_if_contact_exists(peer.public_key): + self.add_group_peer(group, peer) return self.get_contact_by_tox_id(peer.public_key) @@ -375,16 +382,18 @@ class ContactsManager(ToxSave): def remove_group_peer_by_id(self, group, peer_id): peer = group.get_peer_by_id(peer_id) - if not self.check_if_contact_exists(peer.public_key): - return - contact = self.get_contact_by_tox_id(peer.public_key) - self.remove_group_peer(contact) + if peer: # broken + if not self.check_if_contact_exists(peer.public_key): + return + contact = self.get_contact_by_tox_id(peer.public_key) + self.remove_group_peer(contact) def remove_group_peer(self, group_peer_contact): contact = self.get_contact_by_tox_id(group_peer_contact.tox_id) - self._cleanup_contact_data(contact) - num = self._contacts.index(contact) - self._delete_contact(num) + if contact: + self._cleanup_contact_data(contact) + num = self._contacts.index(contact) + self._delete_contact(num) def get_gc_peer_name(self, name): group = self.get_curr_contact() @@ -432,7 +441,7 @@ class ContactsManager(ToxSave): self.save_profile() return True except Exception as ex: # wrong data - util.log('Friend request failed with ' + str(ex)) + LOG.error('Friend request failed with ' + str(ex)) return str(ex) def process_friend_request(self, tox_id, message): @@ -451,7 +460,7 @@ class ContactsManager(ToxSave): data = self._tox.get_savedata() self._profile_manager.save_profile(data) except Exception as ex: # something is wrong - util.log('Accept friend request failed! ' + str(ex)) + LOG.error('Accept friend request failed! ' + str(ex)) def can_send_typing_notification(self): return self._settings['typing_notifications'] and not self.is_active_a_group_chat_peer() diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index 19ebc8e..a1ed948 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -1,3 +1,5 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- + from contacts import contact from contacts.contact_menu import GroupMenuGenerator import utils.util as util @@ -6,6 +8,14 @@ from wrapper import toxcore_enums_and_consts as constants from common.tox_save import ToxSave from groups.group_ban import GroupBan +global LOG +import logging +LOG = logging.getLogger(__name__) +def LOG_ERROR(l): print('ERROR_: '+l) +def LOG_WARN(l): print('WARN_: '+l) +def LOG_INFO(l): print('INFO_: '+l) +def LOG_DEBUG(l): print('DEBUG_: '+l) +def LOG_TRACE(l): pass # print('TRACE+ '+l) class GroupChat(contact.Contact, ToxSave): @@ -73,6 +83,10 @@ class GroupChat(contact.Contact, ToxSave): return self.get_self_role() == constants.TOX_GROUP_ROLE['FOUNDER'] def add_peer(self, peer_id, is_current_user=False): + if peer_id > self._peers_limit: + LOG_WARN(f"add_peer id={peer_id} > {self._peers_limit}") + return + peer = GroupChatPeer(peer_id, self._tox.group_peer_get_name(self._number, peer_id), self._tox.group_peer_get_status(self._number, peer_id), @@ -86,25 +100,41 @@ class GroupChat(contact.Contact, ToxSave): self.remove_all_peers_except_self() else: peer = self.get_peer_by_id(peer_id) - self._peers.remove(peer) + if peer: # broken + self._peers.remove(peer) + else: + LOG_WARN(f"remove_peer empty peers for {peer_id}") def get_peer_by_id(self, peer_id): peers = list(filter(lambda p: p.id == peer_id, self._peers)) - - return peers[0] + if peers: + #? broken + return peers[0] + else: + LOG_WARN(f"get_peer_by_id empty peers for {peer_id}") + return [] def get_peer_by_public_key(self, public_key): peers = list(filter(lambda p: p.public_key == public_key, self._peers)) - - return peers[0] + # DEBUGc: group_moderation #0 mod_id=4294967295 event_type=3 + # WARN_: get_peer_by_id empty peers for 4294967295 + if peers: + return peers[0] + else: + LOG_WARN(f"get_peer_by_public_key empty peers for {public_key}") + return [] def remove_all_peers_except_self(self): self._peers = self._peers[:1] def get_peers_names(self): peers_names = map(lambda p: p.name, self._peers) - - return list(peers_names) + if peers_names: # broken + return list(peers_names) + else: + LOG_WARN(f"get_peers_names empty peers") + #? broken + return [] def get_peers(self): return self._peers[:] @@ -112,16 +142,17 @@ class GroupChat(contact.Contact, ToxSave): peers = property(get_peers) def get_bans(self): - ban_ids = self._tox.group_ban_get_list(self._number) - bans = [] - for ban_id in ban_ids: - ban = GroupBan(ban_id, - self._tox.group_ban_get_target(self._number, ban_id), - self._tox.group_ban_get_time_set(self._number, ban_id)) - bans.append(ban) - - return bans - + return [] +# ban_ids = self._tox.group_ban_get_list(self._number) +# bans = [] +# for ban_id in ban_ids: +# ban = GroupBan(ban_id, +# self._tox.group_ban_get_target(self._number, ban_id), +# self._tox.group_ban_get_time_set(self._number, ban_id)) +# bans.append(ban) +# +# return bans +# bans = property(get_bans) # ----------------------------------------------------------------------------------------------------------------- diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py index 81220af..097c1fa 100644 --- a/toxygen/contacts/profile.py +++ b/toxygen/contacts/profile.py @@ -1,9 +1,13 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- from contacts import basecontact import random import threading import common.tox_save as tox_save from middleware.threads import invoke_in_main_thread +global LOG +import logging +LOG = logging.getLogger('app.'+__name__) class Profile(basecontact.BaseContact, tox_save.ToxSave): """ @@ -14,6 +18,7 @@ class Profile(basecontact.BaseContact, tox_save.ToxSave): :param tox: tox instance :param screen: ref to main screen """ + assert tox basecontact.BaseContact.__init__(self, profile_manager, tox.self_get_name(), diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 114383b..0c137d9 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -1,11 +1,19 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- from messenger.messages import * from ui.contact_items import * import utils.util as util from common.tox_save import ToxSave +from tests.support_testing import assert_main_thread +from copy import deepcopy +# LOG=util.log +global LOG +import logging +LOG = logging.getLogger('app.'+__name__) +log = lambda x: LOG.info(x) class FileTransfersHandler(ToxSave): - + lBlockAvatars = [] def __init__(self, tox, settings, contact_provider, file_transfers_message_service, profile): super().__init__(tox) self._settings = settings @@ -19,7 +27,8 @@ class FileTransfersHandler(ToxSave): # key = (friend number, file number), value - message id profile.avatar_changed_event.add_callback(self._send_avatar_to_contacts) - + self. lBlockAvatars = [] + def stop(self): self._settings['paused_file_transfers'] = self._paused_file_transfers if self._settings['resend_files'] else {} self._settings.save() @@ -37,6 +46,7 @@ class FileTransfersHandler(ToxSave): :param file_name: file name without path """ friend = self._get_friend_by_number(friend_number) + if friend is None: return None auto = self._settings['allow_auto_accept'] and friend.tox_id in self._settings['auto_accept_from_friends'] inline = is_inline(file_name) and self._settings['allow_inline'] file_id = self._tox.file_get_file_id(friend_number, file_number) @@ -85,7 +95,9 @@ class FileTransfersHandler(ToxSave): self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) def cancel_not_started_transfer(self, friend_number, message_id): - self._get_friend_by_number(friend_number).delete_one_unsent_file(message_id) + friend = self._get_friend_by_number(friend_number) + if friend is None: return None + friend.delete_one_unsent_file(message_id) def pause_transfer(self, friend_number, file_number, by_friend=False): """ @@ -115,6 +127,7 @@ class FileTransfersHandler(ToxSave): """ path = self._generate_valid_path(path, from_position) friend = self._get_friend_by_number(friend_number) + if friend is None: return None if not inline: rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position) else: @@ -145,6 +158,7 @@ class FileTransfersHandler(ToxSave): def send_inline(self, data, file_name, friend_number, is_resend=False): friend = self._get_friend_by_number(friend_number) + if friend is None: return None if friend.status is None and not is_resend: self._file_transfers_message_service.add_unsent_file_message(friend, file_name, data) return @@ -162,11 +176,12 @@ class FileTransfersHandler(ToxSave): :param file_id: file id of transfer """ friend = self._get_friend_by_number(friend_number) + if friend is None: return None if friend.status is None and not is_resend: self._file_transfers_message_service.add_unsent_file_message(friend, path, None) return elif friend.status is None and is_resend: - print('Error in sending') + LOG.error('Error in sending') return st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id) file_name = os.path.basename(path) @@ -186,23 +201,27 @@ class FileTransfersHandler(ToxSave): def transfer_finished(self, friend_number, file_number): transfer = self._file_transfers[(friend_number, file_number)] + friend = self._get_friend_by_number(friend_number) + if friend is None: return None t = type(transfer) if t is ReceiveAvatar: - self._get_friend_by_number(friend_number).load_avatar() + friend.load_avatar() elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']): # inline image - print('inline') + LOG.debug('inline') inline = InlineImageMessage(transfer.data) message_id = self._insert_inline_before[(friend_number, file_number)] del self._insert_inline_before[(friend_number, file_number)] - index = self._get_friend_by_number(friend_number).insert_inline(message_id, inline) + if friend is None: return None + index = friend.insert_inline(message_id, inline) self._file_transfers_message_service.add_inline_message(transfer, index) del self._file_transfers[(friend_number, file_number)] def send_files(self, friend_number): - friend = self._get_friend_by_number(friend_number) - friend.remove_invalid_unsent_files() - files = friend.get_unsent_files() try: + friend = self._get_friend_by_number(friend_number) + if friend is None: return None + friend.remove_invalid_unsent_files() + files = friend.get_unsent_files() for fl in files: data, path = fl.data, fl.path if data is not None: @@ -211,6 +230,7 @@ class FileTransfersHandler(ToxSave): self.send_file(path, friend_number, True) friend.clear_unsent_files() for key in self._paused_file_transfers.keys(): + # RuntimeError: dictionary changed size during iteration (path, ft_friend_number, is_incoming, start_position) = self._paused_file_transfers[key] if not os.path.exists(path): del self._paused_file_transfers[key] @@ -218,12 +238,16 @@ class FileTransfersHandler(ToxSave): self.send_file(path, friend_number, True, key) del self._paused_file_transfers[key] except Exception as ex: - print('Exception in file sending: ' + str(ex)) + LOG.error('Exception in file sending: ' + str(ex)) def friend_exit(self, friend_number): - for friend_num, file_num in self._file_transfers.keys(): + # RuntimeError: dictionary changed size during iteration + lMayChangeDynamically = self._file_transfers.copy() + for friend_num, file_num in lMayChangeDynamically: if friend_num != friend_number: continue + if (friend_num, file_num) not in self._file_transfers: + continue ft = self._file_transfers[(friend_num, file_num)] if type(ft) is SendTransfer: self._paused_file_transfers[ft.file_id] = [ft.path, friend_num, False, -1] @@ -240,8 +264,16 @@ class FileTransfersHandler(ToxSave): :param friend_number: number of friend who should get new avatar :param avatar_path: path to avatar or None if reset """ - sa = SendAvatar(avatar_path, self._tox, friend_number) - self._file_transfers[(friend_number, sa.file_number)] = sa + if (avatar_path, friend_number,) in self.lBlockAvatars: + return + + try: + sa = SendAvatar(avatar_path, self._tox, friend_number) + self._file_transfers[(friend_number, sa.file_number)] = sa + except Exception as e: + # ArgumentError('This client is currently not connected to the friend.') + LOG.error(f"send_avatar {e}") + self.lBlockAvatars.append( (avatar_path, friend_number,) ) def incoming_avatar(self, friend_number, file_number, size): """ @@ -251,6 +283,7 @@ class FileTransfersHandler(ToxSave): :param size: size of avatar or 0 (default avatar) """ friend = self._get_friend_by_number(friend_number) + if friend is None: return None ra = ReceiveAvatar(friend.get_contact_avatar_path(), self._tox, friend_number, size, file_number) if ra.state != FILE_TRANSFER_STATE['CANCELLED']: self._file_transfers[(friend_number, file_number)] = ra @@ -259,6 +292,7 @@ class FileTransfersHandler(ToxSave): friend.reset_avatar(self._settings['identicons']) def _send_avatar_to_contacts(self, _): + # from a callback friends = self._get_all_friends() for friend in filter(self._is_friend_online, friends): self.send_avatar(friend.number) @@ -269,6 +303,7 @@ class FileTransfersHandler(ToxSave): def _is_friend_online(self, friend_number): friend = self._get_friend_by_number(friend_number) + if friend is None: return None return friend.status is not None diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index b8fc7cc..258ab80 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -1,8 +1,11 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- + import common.tox_save as tox_save import utils.ui as util_ui from groups.peers_list import PeersListGenerator from groups.group_invite import GroupInvite import wrapper.toxcore_enums_and_consts as constants +from wrapper.toxcore_enums_and_consts import * class GroupsService(tox_save.ToxSave): @@ -65,7 +68,17 @@ class GroupsService(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def invite_friend(self, friend_number, group_number): - self._tox.group_invite_friend(group_number, friend_number) + if self._tox.friend_get_connection_status(friend_number) == TOX_CONNECTION['NONE']: + title = f"Error in group_invite_friend {friend_number}" + e = f"Friend not connected friend_number={friend_number}" + util_ui.message_box(title +'\n' +str(e), title) + return + + try: + self._tox.group_invite_friend(group_number, friend_number) + except Exception as e: + title = f"Error in group_invite_friend {group_number} {friend_number}" + util_ui.message_box(title +'\n' +str(e), title) def process_group_invite(self, friend_number, group_name, invite_data): friend = self._get_friend_by_number(friend_number) @@ -188,6 +201,7 @@ class GroupsService(tox_save.ToxSave): # ----------------------------------------------------------------------------------------------------------------- def show_bans_list(self, group): + return widgets_factory = self._get_widgets_factory() self._screen = widgets_factory.create_groups_bans_screen(group) self._screen.show() diff --git a/toxygen/history/database.py b/toxygen/history/database.py index 751c74b..c17a968 100644 --- a/toxygen/history/database.py +++ b/toxygen/history/database.py @@ -1,7 +1,13 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- from sqlite3 import connect import os.path import utils.util as util +# LOG=util.log +global LOG +import logging +LOG = logging.getLogger('app.'+__name__) +log = lambda x: LOG.info(x) TIMEOUT = 11 @@ -24,19 +30,30 @@ CONTACT_TYPE = { class Database: def __init__(self, path, toxes): - self._path, self._toxes = path, toxes + self._path = path + self._toxes = toxes self._name = os.path.basename(path) - if os.path.exists(path): - try: - with open(path, 'rb') as fin: - data = fin.read() - if toxes.is_data_encrypted(data): - data = toxes.pass_decrypt(data) - with open(path, 'wb') as fout: - fout.write(data) - except Exception as ex: - util.log('Db reading error: ' + str(ex)) - os.remove(path) + + def open(self): + path = self._path + toxes = self._toxes + if not os.path.exists(path): + LOG.warn('Db not found: ' +path) + return + try: + with open(path, 'rb') as fin: + data = fin.read() + except Exception as ex: + LOG.error('Db reading error: ' +path +' ' +str(ex)) + raise + try: + if toxes.is_data_encrypted(data): + data = toxes.pass_decrypt(data) + with open(path, 'wb') as fout: + fout.write(data) + except Exception as ex: + LOG.error('Db writing error: ' +path +' ' + str(ex)) + os.remove(path) # ----------------------------------------------------------------------------------------------------------------- # Public methods @@ -72,9 +89,11 @@ class Database: ' message_type INTEGER' ')') db.commit() - except: - print('Database is locked!') + return True + except Exception as e: + LOG("ERROR: " +self._name +' Database exception! ' +str(e)) db.rollback() + return False finally: db.close() @@ -84,9 +103,11 @@ class Database: cursor = db.cursor() cursor.execute('DROP TABLE id' + tox_id + ';') db.commit() - except: - print('Database is locked!') + return True + except Exception as e: + LOG("ERROR: " +self._name +' Database exception! ' +str(e)) db.rollback() + return False finally: db.close() @@ -98,9 +119,11 @@ class Database: '(message, author_name, author_type, unix_time, message_type) ' + 'VALUES (?, ?, ?, ?, ?, ?);', messages_iter) db.commit() - except: - print('Database is locked!') + return True + except Exception as e: + LOG("ERROR: " +self._name +' Database exception! ' +str(e)) db.rollback() + return False finally: db.close() @@ -111,9 +134,11 @@ class Database: cursor.execute('UPDATE id' + tox_id + ' SET author = 0 ' 'WHERE id = ' + str(message_id) + ' AND author = 2;') db.commit() - except: - print('Database is locked!') + return True + except Exception as e: + LOG("ERROR: " +self._name +' Database exception! ' +str(e)) db.rollback() + return False finally: db.close() @@ -123,9 +148,11 @@ class Database: cursor = db.cursor() cursor.execute('DELETE FROM id' + tox_id + ' WHERE id = ' + str(unique_id) + ';') db.commit() - except: - print('Database is locked!') + return True + except Exception as e: + LOG("ERROR: " +self._name +' Database exception! ' +str(e)) db.rollback() + return False finally: db.close() @@ -135,9 +162,11 @@ class Database: cursor = db.cursor() cursor.execute('DELETE FROM id' + tox_id + ';') db.commit() - except: - print('Database is locked!') + return True + except Exception as e: + LOG("ERROR: " +self._name +' Database exception! ' +str(e)) db.rollback() + return False finally: db.close() diff --git a/toxygen/history/history.py b/toxygen/history/history.py index bd7e353..074322e 100644 --- a/toxygen/history/history.py +++ b/toxygen/history/history.py @@ -1,3 +1,4 @@ +# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- from history.history_logs_generators import * @@ -11,7 +12,7 @@ class History: self._messages_items_factory = messages_items_factory self._is_loading = False self._contacts_manager = None - + def __del__(self): del self._db @@ -26,7 +27,8 @@ class History: """ Save history to db """ - if self._settings['save_db']: + # me a mistake? was _db not _history + if self._settings['save_history'] or self._settings['save_db']: for friend in self._contact_provider.get_all_friends(): self._db.add_friend_to_db(friend.tox_id) if not self._settings['save_unsent_only']: diff --git a/toxygen/images/accept.png b/toxygen/images/accept.png old mode 100755 new mode 100644 index aaa1388847d126a0745e7f06339d01ab4f7907a8..eedb81873d39783be273cf4f390a98a7bee804b3 GIT binary patch literal 118982 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