def on_addButton_clicked(self): (res, name, url) = MultiInputDialog.getTexts(self._tr("Add repository"), self._tr("Name:"), self._tr("URL:"), "", "", self) if res: qurl = QUrl(url) if qurl.isValid() and not qurl.isLocalFile(): rep = dict() rep["name"] = name rep["url"] = url rep["origin"] = "local" rep["active"] = True self.replist[name] = rep item = QListWidgetItem(name) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(Qt.Checked) item.setData(Qt.UserRole, name) self.repositoryList.addItem(item) else: QMessageBox.critical(self._tr("Error"), self._tr("The URL {url} is not valid"). format(url=url))
def __init__(self): loadCfg(self.ini, self.cfg) url = self.cfg.get("general", "template") if url: self.nwmc_template = QNetworkAccessManager() self.nwmc_template.connect("finished(QNetworkReply*)", self.loadTemplates) self.nwmc_template.get(QNetworkRequest(QUrl(url))) url = self.cfg.get("general", "whitelist") if url: self.nwmc_whitelist = QNetworkAccessManager() self.nwmc_whitelist.connect("finished(QNetworkReply*)", self.loadWhitelist) self.nwmc_whitelist.get(QNetworkRequest(QUrl(url))) if PluginHost.cfg.getboolean("general", "verbose"): ts3lib.printMessageToCurrentTab("{0}[color=orange]{1}[/color] Plugin for pyTSon by [url=https://github.com/{2}]{2}[/url] loaded.".format(timestamp(),self.name,self.author))
def on_copyAction_triggered(self): cur = self.listmodel.fileByIndex(self.currentItem()) if not cur: return err, host, port, _ = ts3lib.getServerConnectInfo(self.schid) if err == ERROR_ok: url = ("[URL=ts3file://{address}?port={port}&channel={cid}&" "path={path}&filename={fname}&isDir={isdir}&" "size={size}&fileDateTime={date}]{fname}[/URL]").format( address=host, port=port, cid=self.cid, path=QUrl.toPercentEncoding(cur.path), fname=cur.name, isdir=1 if cur.isDirectory else 0, size=cur.size, date=int(cur.datetime.timestamp())) QApplication.clipboard().setText(url) else: self.showError(self._tr("Error getting server connection info"), err)
def onConnectionInfoEvent(self, serverConnectionHandlerID, clientID): try: if not self.requested == clientID: return (error, ip) = ts3.getConnectionVariableAsString( serverConnectionHandlerID, clientID, ts3defines.ConnectionProperties.CONNECTION_CLIENT_IP) if error == ts3defines.ERROR_ok: self.ip = ip self.nwm = QNetworkAccessManager() self.nwm.connect("finished(QNetworkReply*)", self.onMainReply) self.nwm.get( QNetworkRequest( QUrl(self.cfg['api']['main'].replace("{ip}", ip)))) if PluginHost.cfg.getboolean("general", "verbose"): ts3.printMessageToCurrentTab( self.cfg['api']['main'].replace("{ip}", ip)) else: (e, msg) = ts3.getErrorMessage(error) ts3.printMessageToCurrentTab( "[[color=orange]WARNING[/color]] [color=red]ISPValidator could not resolve the IP for '%s' (Reason: %s)" % (self.clientURL(serverConnectionHandlerID, clientID), msg)) except: ts3.printMessageToCurrentTab( "[[color=orange]WARNING[/color]] [color=red]ISPValidator could not resolve the IP for '%s' (Reason: %s)" % (self.clientURL(serverConnectionHandlerID, clientID), format_exc()))
def onClientMoveEvent(self, schid, clientID, oldChannelID, newChannelID, visibility, moveMessage): if not self.toggle: return try: (error, ownid) = ts3lib.getClientID(schid) if ownid == clientID: (error, ntp) = ts3lib.getChannelVariableAsInt( schid, newChannelID, ts3defines.ChannelPropertiesRare.CHANNEL_NEEDED_TALK_POWER) if self.debug: ts3lib.printMessageToCurrentTab( 'error: {0} | ntp: {1}'.format(error, ntp)) if ntp < 1: return (error, tp) = ts3lib.getClientVariableAsInt( schid, ownid, ts3defines.ClientPropertiesRare.CLIENT_IS_TALKER) if self.debug: ts3lib.printMessageToCurrentTab( 'error: {0} | tp: {1}'.format(error, tp)) if tp: return self.nwmc = QNetworkAccessManager() self.nwmc.connect("finished(QNetworkReply*)", self.jokeReply) self.schid = schid self.nwmc.get( QNetworkRequest( QUrl("http://tambal.azurewebsites.net/joke/random"))) except: from traceback import format_exc ts3lib.logMessage(format_exc(), ts3defines.LogLevel.LogLevel_ERROR, "pyTSon", 0)
def LoadStation(self, key): # Non-premium streams can just start loading straight away if not self.PLAYLISTS[self.audio_type]["premium"]: self.LoadPlaylist(key) return # Otherwise we have to get the user's hashKey request = QNetworkRequest(QUrl("http://www.sky.fm/configure_player.php")) postdata = "amember_login=%s&amember_pass=%s" % ( QUrl.toPercentEncoding(self.username), QUrl.toPercentEncoding(self.password)) self.load_station_reply = self.network.post(request, postdata) self.load_station_reply.connect("finished()", self.LoadHashKeyFinished) self.last_key = key
def install(self, addon): self.addon = addon req = QNetworkRequest(QUrl(addon["url"])) req.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True) self.nwm.get(req) self.consoleEdit.append("Downloading %s ..." % addon["url"])
def requestServers(self, url): if self.serverBrowser.config["GENERAL"]["debug"] == "True": self.requests += 1 ts3.printMessageToCurrentTab("Request: "+str(self.requests)) self.nwm = QNetworkAccessManager() self.nwm.connect("finished(QNetworkReply*)", self.serversReply) self.nwm.get(QNetworkRequest(QUrl(url)))
def startAuthenticationProcess(self): self.settingsDialog.group_account.widget_authorize.button_authenticate.setEnabled( False) self.flow = dropbox.client.DropboxOAuth2FlowNoRedirect( 'sfacmqvdb9dn66r', 'hx8meda636xgsox') authorize_url = QUrl(self.flow.start()) QDesktopServices.openUrl(authorize_url) code = raw_input( "Enter the authorization code from the dropbox website:") if code: try: self.access_token, self.user_id = self.flow.finish(code) self.client = dropbox.client.DropboxClient(self.access_token) self.display_name = self.client.account_info()['display_name'] except dropbox.rest.ErrorResponse: if "win" in sys.platform: #Workaround for crash on windows self.parentWidget.hide() self.settingsDialog.hide() QMessageBox.critical( self.settingsDialog, "Failed to authenticate", "Failed to authenticate with Dropbox. Wrong code?") if "win" in sys.platform: self.settingsDialog.show() self.parentWidget.show() self.saveSettings() self.updateUi()
def onConnectStatusChangeEvent(self, schid, newStatus, errorNumber): if not self.enabled: return if newStatus != ts3defines.ConnectStatus.STATUS_CONNECTING: return if self.proxied: self.proxied = False; return err, host, port, pw = ts3lib.getServerConnectInfo(schid) if host.lower() in self.whitelist: ts3lib.printMessageToCurrentTab("[color=green]%s is whitelisted, not using proxy!" % host); return ip = QHostAddress(host) if not ip.isNull(): if ip.isLoopback(): ts3lib.printMessageToCurrentTab("[color=green]%s is Loopback, not using proxy!" % host); return elif ip.isMulticast(): ts3lib.printMessageToCurrentTab("[color=green]%s is Multicast, not using proxy!" % host); return is_nickname = False if not "." in host: ts3lib.printMessageToCurrentTab("[color=orange]%s is a server nickname, resolving..." % host) self.backup["address"] = host is_nickname = True if not is_nickname: self.backup["address"] = "{}:{}".format(host,port) ts3lib.printMessageToCurrentTab("[color=red]Not proxied on %s, disconnecting!"%self.backup["address"]) ts3lib.stopConnection(schid, "switching to proxy") if pw: self.backup["pw"] = pw err, nickname = ts3lib.getClientSelfVariable(schid, ts3defines.ClientProperties.CLIENT_NICKNAME) if not err and nickname: self.backup["nickname"] = nickname err, nickname_phonetic = ts3lib.getClientSelfVariable(schid, ts3defines.ClientPropertiesRare.CLIENT_NICKNAME_PHONETIC) if not err and nickname_phonetic: self.backup["phonetic"] = nickname_phonetic err, c = ts3lib.getClientSelfVariable(schid, ts3defines.ClientProperties.CLIENT_DEFAULT_CHANNEL) if not err and c: self.backup["c"] = c err, cpw = ts3lib.getClientSelfVariable(schid, ts3defines.ClientProperties.CLIENT_DEFAULT_CHANNEL_PASSWORD) if not err and cpw: self.backup["cpw"] = cpw err, default_token = ts3lib.getClientSelfVariable(schid, ts3defines.ClientPropertiesRare.CLIENT_DEFAULT_TOKEN) if not err and default_token: self.backup["token"] = default_token if is_nickname: self.nwmc_resolver.get(QNetworkRequest(QUrl("https://named.myteamspeak.com/lookup?name=%s"%host))) return self.proxy(host, port)
def checkNotice(self): self.notice_nwmc.connect("finished(QNetworkReply*)", self.loadNotice) self.notice_nwmc.get( QNetworkRequest( QUrl( "https://raw.githubusercontent.com/R4P3-NET/CustomBadges/master/notice" )))
def startAuthenticationProcess(self): self.settingsDialog.group_account.widget_authorize.button_authenticate.setEnabled( False) authorize_url = QUrl( self.client.auth_provider.get_auth_url(REDIRECT_URI)) QDesktopServices.openUrl(authorize_url) try: code = raw_input( "Copy everything in the address bar after \"code=\", and paste it below:" ) except NameError: code = input( "Copy everything in the address bar after \"code=\", and paste it below:" ) if code: try: self.client.auth_provider.authenticate(code, REDIRECT_URI, None) except: if "win" in sys.platform: #Workaround for crash on windows self.parentWidget.hide() self.settingsDialog.hide() QMessageBox.critical( self.settingsDialog, "Failed to authenticate", "Failed to authenticate with OneDrive. Wrong code?") if "win" in sys.platform: self.settingsDialog.show() self.parentWidget.show() #Set display name self.displayName = self.client.drive.get().owner.user.display_name self.loggedIn = True self.saveSettings() self.updateUi()
def onTextMessageEvent(self, schid, targetMode, toID, fromID, fromName, fromUniqueIdentifier, message, ffIgnored): if ffIgnored: return (error, myid) = ts3lib.getClientID(schid) _message = message.lower() if myid == fromID and not "~check~" in _message: return if not any(substring in _message for substring in ["[url]", "[url="]): return msg = { "schid": schid, "returnCode": ts3lib.createReturnCode(), "invoker": fromID, "targetMode": targetMode, "urls": parseURLs(message, self.bbcode_url), "response": "[b]Link Checker:[/b]" } hosts = [] for url in msg["urls"]: if url[0] != url[1]: msg["response"] += "\n[color=orange]Suspicous Link [url]{}[/url] points to [url]{}[/url][/color]".format(url[0],url[1]) # answerMessage(schid, targetMode, fromID, "[color=orange]{}: {}".format(url[0], url[1])) host = QUrl(url[1]).host() if host and "." in host and not host in hosts: hosts.append(host) if len(hosts) > 0: self.messages.append(msg) self.getLinkInfo(hosts)
def getLinkInfo(self, urls): domains = "/".join(urls) url = "http://api.mywot.com/0.4/public_link_json2?hosts=%s/&key=%s" % (domains,self.wot_api_key) ts3lib.logMessage('Requesting %s'%url, ts3defines.LogLevel.LogLevel_ERROR, "PyTSon Linkinfo Script", 0) self.nwm = QNetworkAccessManager() self.nwm.connect("finished(QNetworkReply*)", self.onWOTReply) self.nwm.get(QNetworkRequest(QUrl(url)))
def requestBadgesExt(self): try: with open(self.badges_ext, encoding='utf-8-sig') as json_file: self.extbadges = load(json_file) except: self.nwmc_ext = QNetworkAccessManager() self.nwmc_ext.connect("finished(QNetworkReply*)", self.loadBadgesExt) self.nwmc_ext.get(QNetworkRequest(QUrl(self.badges_ext_remote)))
def downloadFile(self, url, path): """ :param url: :param path: """ self.nwmc.connect("finished(QNetworkReply*)", self._downloadFileReply) dlpath = path self.nwmc.get(QNetworkRequest(QUrl(url)))
def go_to_linedit_url(self): try: url = str(self.lineedit_address.currentText) except: self.lineedit_address.setEditText("invalid url") # should prolly support unicode but meh for now... return if url.startswith("http://") == False and url.startswith("https://") == False: url = "http://" + url self.load_url(QUrl(url))
def requestExtIcon(self, filename): self.nwmc_exti[filename] = QNetworkAccessManager() self.nwmc_exti[filename].connect("finished(QNetworkReply*)", self.loadExtIcon) self.tmpfile[filename] = QFile() self.tmpfile[filename].setFileName(path.join(self.icons, filename)) self.tmpfile[filename].open(QIODevice.WriteOnly) url = "https://raw.githubusercontent.com/R4P3-NET/CustomBadges/master/img/{}".format( filename) self.nwmc_exti[filename].get(QNetworkRequest(QUrl(url)))
def on_txt_ip_textChanged(self, text): try: if not hasattr(self, "nwmc_ip"): self.disableISP(); return if not text: self.disableISP(); return if len(text) < 7: self.disableISP(); return ip = QHostAddress(text) if ip.isNull() or ip.isLoopback() or ip.isMulticast(): self.disableISP(); return if text.strip() in ["127.0.0.1", "0.0.0.0", "255.255.255"]: self.disableISP(); return self.nwmc_ip.get(QNetworkRequest(QUrl("http://ip-api.com/json/{ip}".format(ip=text)))) except: ts3lib.logMessage(format_exc(), ts3defines.LogLevel.LogLevel_ERROR, "pyTSon", 0)
def getLinkInfo(self, urls): # https://www.mywot.com/wiki/API links = "/".join(urls) ts3lib.printMessageToCurrentTab("%s" % links) url = "http://api.mywot.com/0.4/public_link_json2?hosts=%s&key=%s" % ( links, self.wotapikey) ts3.logMessage('Requesting %s' % url, ts3defines.LogLevel.LogLevel_ERROR, "PyTSon Linkinfo Script", 0) self.nwm = QNetworkAccessManager() self.nwm.connect("finished(QNetworkReply*)", self.onNetworkReply) self.nwm.get(QNetworkRequest(QUrl(url)))
def updateReply(self, reply): version = loads(reply.readAll().data().decode('utf-8'))["version"] if version != self.version: x = QDialog() x.setAttribute(Qt.WA_DeleteOnClose) _x = QMessageBox.question( x, "{} v{} by {}".format(self.name, self.version, self.author), "Noua versiune v{} la linksteam a fost gasita, dai update acum?" .format(version), QMessageBox.Yes, QMessageBox.No) if _x == QMessageBox.Yes: QDesktopServices.openUrl(QUrl(self.repourl))
def requestCountries(self): self.nwmc = QNetworkAccessManager() self.nwmc.connect("finished(QNetworkReply*)", self.onCountryListReply) self.nwmc.get( QNetworkRequest( QUrl(self.serverBrowser.config['GENERAL']['api'] + "servercountries"))) if self.serverBrowser.config["GENERAL"]["debug"] == "True": ts3.printMessageToCurrentTab( "requestCountries: " + self.serverBrowser.config['GENERAL']['api'] + "servercountries")
def onMenuItemEvent(self, schid, atype, menuItemID, selectedItemID): try: url = "" if atype == ts3defines.PluginMenuType.PLUGIN_MENU_TYPE_CLIENT: if menuItemID == 1: # Nickname (TSViewer) url = "http://www.tsviewer.com/index.php?page=search&action=ausgabe_user&nickname=%%CLIENT_NAME_PERCENT_ENCODED%%" elif menuItemID == 2: # Nickname (GameTracker) url = "http://www.gametracker.com/search/?search_by=online_offline_player&query=%%CLIENT_NAME_PERCENT_ENCODED%%" elif menuItemID == 3: # Nickname (TS3Index) url = "http://ts3index.com/?page=searchclient&nickname=%%CLIENT_NAME_PERCENT_ENCODED%%" elif menuItemID == 4: # Nickname (Google) url = "https://www.google.com/search?q=%%CLIENT_NAME_PERCENT_ENCODED%%" elif menuItemID == 5: # Profil (GameTracker) url = "http://www.gametracker.com/search/?search_by=profile_username&query=%%CLIENT_NAME_PERCENT_ENCODED%%" elif menuItemID == 6: # UID (TS3Index) url = "http://ts3index.com/?page=searchclient&uid=%%CLIENT_UNIQUE_ID%%" elif menuItemID == 7: # UID (Google) url = "https://www.google.com/search?q=%%CLIENT_UNIQUE_ID%%" elif menuItemID == 8: # Besitzer (TSViewer) url = "http://www.tsviewer.com/index.php?page=search&action=ausgabe&suchbereich=ansprechpartner&suchinhalt=%%CLIENT_NAME_PERCENT_ENCODED%%" elif menuItemID == 9: # Badges (TS3Index) url = "https://ts3index.com/?page=searchclient&badges=%%CLIENT_BADGES%%" else: return # payload = {'username':'******', 'password':'******'} # nickname_encoded = urlencode(nickname, quote_via=quote_plus) # nickname_encoded = nickname_encoded.replace("+", "%2B").replace("/", "%2F").replace("=", "%3D") if "%%CLIENT_NAME_PERCENT_ENCODED%%" in url: (err, nickname) = ts3lib.getClientVariable( schid, selectedItemID, ts3defines.ClientProperties.CLIENT_NICKNAME) url = url.replace("%%CLIENT_NAME_PERCENT_ENCODED%%", quote_plus(nickname)) if "%%CLIENT_UNIQUE_ID%%" in url: (err, uid) = ts3lib.getClientVariable( schid, selectedItemID, ts3defines.ClientProperties.CLIENT_UNIQUE_IDENTIFIER) url = url.replace("%%CLIENT_UNIQUE_ID%%", quote_plus(uid)) if "%%CLIENT_BADGES%%" in url: (err, badges) = ts3lib.getClientVariable( schid, selectedItemID, ts3defines.ClientPropertiesRare.CLIENT_BADGES) url = url.replace("%%CLIENT_BADGES%%", badges) else: return if PluginHost.cfg.getboolean("general", "verbose"): ts3lib.printMessageToCurrentTab( "Opening URL: \"{}\"".format(url)) QDesktopServices.openUrl(QUrl(url)) except: ts3lib.logMessage(format_exc(), ts3defines.LogLevel.LogLevel_ERROR, "pyTSon", 0)
def __init__(self): if isfile(self.bin): self.yatqa = QProcess() self.yatqa.setProgram(self.bin) else: msgBox( "Cannot find YatQA!\nPlease make sure it's installed at\n\n\"{}\"" .format(self.bin)) QDesktopServices.openUrl(QUrl("http://yat.qa/")) if PluginHost.cfg.getboolean("general", "verbose"): ts3lib.printMessageToCurrentTab( "{0}[color=orange]{1}[/color] Plugin for pyTSon by [url=https://github.com/{2}]{2}[/url] loaded." .format(timestamp(), self.name, self.author))
def StartSearch(self, artist, album, id): query = self.PrepareAmazonRESTUrl(artist + " " + album) url = QUrl.fromEncoded(self.API_URL.format(query)) LOGGER.debug("ID %d: Sending request to '%s'" % (id, url)) reply = self.network.get(QNetworkRequest(url)) def QueryFinished(): LOGGER.debug("ID %d: Finished" % id) self.SearchFinished(id, self.ParseReply(reply)) reply.connect("finished()", QueryFinished) return True
def urlAvatar(self, schid): try: self.nwm = QNetworkAccessManager() self.nwm.connect("finished(QNetworkReply*)", self.onNetworkReply) self.schid = schid print("%s" % self.config.get('GENERAL', 'imgurl')) self.nwm.get( QNetworkRequest(QUrl(self.config.get('GENERAL', 'imgurl')))) except: from traceback import format_exc try: ts3lib.logMessage(format_exc(), ts3defines.LogLevel.LogLevel_ERROR, "PyTSon Script", 0) except: print(format_exc())
def updateRepositories(self): self.addons = {} self.pluginsList.clear() self.pending += sum(x["active"] for x in self.replist.values()) for rep in self.replist.values(): if all(x in rep for x in ['name', 'url', 'origin', 'active']): if rep["active"]: self.nwm.get(QNetworkRequest(QUrl(rep["url"]))) else: self.pending -= 1 ts3print(self._tr("Invalid repository in list, ignoring"), ts3defines.LogLevel.LogLevel_WARNING, "pyTSon.RepositoryDialog.updateRepositories", 0) self.updatePendingButtons()
def startAuthenticationProcess(self): self.saveSettings() self.loadSettings() auth_url = self.imgur.authorization_url('pin') QDesktopServices.openUrl(QUrl(auth_url)) pin = raw_input("Enter PIN from imgur website:") if pin: try: self.access_token, self.refresh_token = self.imgur.exchange_pin( pin) except KeyError as e: QMessageBox.critical(self.settingsDialog, "Imgur key error", "Failed to exchange pin. " + e.message) self.access_token, self.username = self.imgur.refresh_access_token() self.saveSettings() self.updateUi()
def __init__(self): loadCfg(self.ini, self.cfg) try: with open(self.badgesinfo, encoding='utf-8-sig') as json_file: self.badges = load(json_file) except: self.nwmc = QNetworkAccessManager() self.nwmc.connect("finished(QNetworkReply*)", self.loadBadges) self.nwmc.get( QNetworkRequest( QUrl( "https://gist.githubusercontent.com/Bluscream/29b838f11adc409feac9874267b43b1e/raw" ))) if self.cfg.getboolean("general", "debug"): ts3lib.printMessageToCurrentTab( "{0}[color=orange]{1}[/color] Plugin for pyTSon by [url=https://github.com/{2}]{2}[/url] loaded." .format(timestamp(), self.name, self.author))
def startAuthenticationProcess(self): self.settingsDialog.group_account.widget_authorize.button_authenticate.setEnabled( False) self.flow = client.OAuth2WebServerFlow( client_id=self.clientID, client_secret=self.clientSecret, scope=SCOPES, redirect_uri="urn:ietf:wg:oauth:2.0:oob") authorize_url = QUrl(self.flow.step1_get_authorize_url()) QDesktopServices.openUrl(authorize_url) try: code = raw_input( "Enter the authorization code from the Google Drive website:") except NameError: code = input( "Enter the authorization code from the Google Drive website:") if code: try: oauth2_result = self.flow.step2_exchange(code) self.accessToken = oauth2_result.access_token self.refreshToken = oauth2_result.refresh_token self.driveService = build('drive', 'v3', http=oauth2_result.authorize(Http())) account = self.driveService.about().get( fields="user").execute() self.displayName = account["user"]["displayName"] except client.Error: if "win" in sys.platform: #Workaround for crash on windows self.parentWidget.hide() self.settingsDialog.hide() QMessageBox.critical( self.settingsDialog, "Failed to authenticate", "Failed to authenticate with Google Drive. Wrong code?") if "win" in sys.platform: self.settingsDialog.show() self.parentWidget.show() self.saveSettings() self.updateUi()
def onClientMoveEvent(self, schid, clientID, oldChannelID, newChannelID, visibility, moveMessage): if not self.toggle or schid != self.schid: return (error, ownid) = ts3lib.getClientID(schid) if ownid != clientID: return if not self.talker(): if self.toggle == 1: if self.msg != "": self.nwmc = QNetworkAccessManager() self.nwmc.connect("finished(QNetworkReply*)", self.jokeReply) self.nwmc.get( QNetworkRequest( QUrl("http://tambal.azurewebsites.net/joke/random") )) else: ts3lib.requestIsTalker(schid, True, self.msg) elif self.toggle == 2: self.active = True else: if self.toggle == 2: self.active = False
def GetQueryURL(self, query): current_args = self.api_args.copy() current_args['q'] = query return QUrl.fromEncoded(self.API_URL.format(urllib.urlencode(current_args)))
import importlib import traceback import json from configparser import ConfigParser from pytsonui.console import PythonConsole from pytsonui.config import ConfigurationDialog from PythonQt.QtGui import QFont, QColor, QMessageBox from PythonQt.QtCore import QUrl, QTimer, QTranslator, QCoreApplication from PythonQt.QtNetwork import (QNetworkAccessManager, QNetworkRequest, QNetworkReply) from weakref import WeakValueDictionary REL_URL = QUrl("https://api.github.com/repos/pathmann/pyTSon/releases/latest") def logprint(msg, loglevel, channel): err = ts3lib.logMessage(msg, loglevel, channel, 0) if err != ts3defines.ERROR_ok: print(msg) class PluginHost(pytson.Translatable): defaultConfig = [("general", [("differentApi", "False"), ("uninstallQuestion", "True"), ("loadAllMenus", "True"), ("language", "inherited"), ("verbose", "False")]), ("plugins", []), ("console", [("backgroundColor", "#000000"),