Repository: OsuSync/Sync Branch: master Commit: b15f75d3e356 Files: 125 Total size: 264.5 KB Directory structure: gitextract_0_roh6il/ ├── .gitignore ├── .gitmodules ├── LICENSE ├── Language/ │ ├── de-DE/ │ │ ├── BanManagerPlugin.DefaultLanguage.lang │ │ ├── BeatmapSuggest.DefaultLanguage.lang │ │ ├── ConfigGUI.DefaultLanguage.lang │ │ ├── DefaultGUI.Language.lang │ │ ├── DefaultPlugin.Language.lang │ │ ├── NowPlaying.Languages.lang │ │ ├── OsuRTDataProvider.DefaultLanguage.lang │ │ ├── RealTimePPDisplayer.DefaultLanguage.lang │ │ ├── RecentlyUserQuery.DefaultLanguage.lang │ │ └── Sync.Tools.DefaultI18n.lang │ ├── en-US/ │ │ ├── BanManagerPlugin.DefaultLanguage.lang │ │ ├── BeatmapSuggest.DefaultLanguage.lang │ │ ├── ConfigGUI.DefaultLanguage.lang │ │ ├── DefaultGUI.Language.lang │ │ ├── DefaultPlugin.Language.lang │ │ ├── NowPlaying.Languages.lang │ │ ├── OsuRTDataProvider.DefaultLanguage.lang │ │ ├── RealTimePPDisplayer.DefaultLanguage.lang │ │ ├── RecentlyUserQuery.DefaultLanguage.lang │ │ └── Sync.Tools.DefaultI18n.lang │ ├── hu-HU/ │ │ ├── BanManagerPlugin.DefaultLanguage.lang │ │ ├── DefaultGUI.Language.lang │ │ ├── DefaultPlugin.Language.lang │ │ ├── NowPlaying.Languages.lang │ │ ├── OsuRTDataProvider.DefaultLanguage.lang │ │ ├── RealTimePPDisplayer.DefaultLanguage.lang │ │ ├── RecentlyUserQuery.DefaultLanguage.lang │ │ └── Sync.Tools.DefaultI18n.lang │ ├── ja-JP/ │ │ ├── BanManagerPlugin.DefaultLanguage.lang │ │ ├── BeatmapSuggest.DefaultLanguage.lang │ │ ├── ConfigGUI.DefaultLanguage.lang │ │ ├── DefaultGUI.Language.lang │ │ ├── DefaultPlugin.Language.lang │ │ ├── NowPlaying.Languages.lang │ │ ├── OsuRTDataProvider.DefaultLanguage.lang │ │ ├── RealTimePPDisplayer.DefaultLanguage.lang │ │ ├── RecentlyUserQuery.DefaultLanguage.lang │ │ └── Sync.Tools.DefaultI18n.lang │ ├── ru-RU/ │ │ ├── BanManagerPlugin.DefaultLanguage.lang │ │ ├── BeatmapSuggest.DefaultLanguage.lang │ │ ├── ConfigGUI.DefaultLanguage.lang │ │ ├── DefaultGUI.Language.lang │ │ ├── DefaultPlugin.Language.lang │ │ ├── NowPlaying.Languages.lang │ │ ├── OsuRTDataProvider.DefaultLanguage.lang │ │ ├── RealTimePPDisplayer.DefaultLanguage.lang │ │ ├── RecentlyUserQuery.DefaultLanguage.lang │ │ └── Sync.Tools.DefaultI18n.lang │ └── zh-CN/ │ ├── BanManagerPlugin.DefaultLanguage.lang │ ├── ConfigGUI.DefaultLanguage.lang │ ├── DefaultGUI.Language.lang │ ├── DefaultPlugin.Language.lang │ ├── NowPlaying.Languages.lang │ ├── RecentlyUserQuery.DefaultLanguage.lang │ └── Sync.Tools.DefaultI18n.lang ├── README.md ├── Sync/ │ ├── Client/ │ │ ├── ClientEvent.cs │ │ ├── ClientManager.cs │ │ ├── ClientWorkWrapper.cs │ │ └── DefaultReciveClient.cs │ ├── Command/ │ │ ├── CommandDispatch.cs │ │ └── CommandManager.cs │ ├── Dependence/ │ │ └── config.ini │ ├── Event/ │ │ └── EventDispatcher.cs │ ├── MessageFilter/ │ │ ├── FilterBase.cs │ │ ├── FilterManager.cs │ │ ├── FilterPriorityAttribute.cs │ │ ├── MessageDispatcher.cs │ │ └── MessageManager.cs │ ├── Plugins/ │ │ ├── Plugin.cs │ │ └── PluginManager.cs │ ├── Program.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── Resources/ │ │ ├── App.config │ │ └── icon.psd │ ├── Source/ │ │ ├── SourceBase.cs │ │ ├── SourceEvent.cs │ │ ├── SourceManager.cs │ │ └── SourceWorkWrapper.cs │ ├── Sync.csproj │ ├── SyncHost.cs │ ├── Tools/ │ │ ├── Builtin/ │ │ │ ├── CommonCommand.cs │ │ │ ├── InternalPlugin.cs │ │ │ └── PluginCommand.cs │ │ ├── CommandParser.cs │ │ ├── Configuration.cs │ │ ├── ConfigurationAttribute/ │ │ │ ├── BaseConfigurationAttribute.cs │ │ │ ├── BoolAttribute.cs │ │ │ ├── ClientAndSourceAttribute.cs │ │ │ ├── ColorAttribute.cs │ │ │ ├── ConfigurationHolderAttribute.cs │ │ │ ├── FloatAttribute.cs │ │ │ ├── FontAttribute.cs │ │ │ ├── GuiLanguageElement.cs │ │ │ ├── IntegerAttribute.cs │ │ │ ├── ListAttribute.cs │ │ │ ├── PathAttribute.cs │ │ │ └── StringAttribute.cs │ │ ├── ConfigurationIO.cs │ │ ├── I18n.cs │ │ ├── IConfigurable.cs │ │ ├── PluginConfiuration.cs │ │ ├── SentryHelper.cs │ │ ├── StartupArgument.cs │ │ ├── StartupHelper.cs │ │ ├── StringElement.cs │ │ ├── SyncIO/ │ │ │ ├── ConsoleWriter.cs │ │ │ ├── FileLoggerWriter.cs │ │ │ ├── IOWrapper.cs │ │ │ ├── Logger.cs │ │ │ └── NConsoleWriter.cs │ │ ├── Updater.cs │ │ └── Utils/ │ │ ├── ConfigurationHelper.cs │ │ └── PluginsHelper.cs │ ├── app.config │ ├── config.ini │ └── packages.config └── Sync.sln ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Rr]elease/ x64/ build/ [Bb]in/ [Oo]bj/ # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets !packages/*/build/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.log *.scc *.json # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* .*crunch*.local.xml # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.Publish.xml *.pubxml # NuGet Packages Directory ## TODO: If you have NuGet Package Restore enabled, uncomment the next line #packages/ # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.[Pp]ublish.xml *.pfx *.publishsettings # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files App_Data/*.mdf App_Data/*.ldf # ========================= # Windows detritus # ========================= # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Mac crap .DS_Store .vs/ .vscode/ ReadMe/ packages/ /output /Sync/bin* ================================================ FILE: .gitmodules ================================================ [submodule "OfficalPlugins"] path = OfficalPlugins url = https://github.com/Deliay/SyncPlugin.git ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017-2018 OsuSync Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Language/de-DE/BanManagerPlugin.DefaultLanguage.lang ================================================ [de-DE] LANG_HELP_BAN=Sperre Benutzer/ID/Regex ,Nachrichten werden über IRC versendet. LANG_HELP_UNBAN=Entsperre Benutzer/ID/Regex. LANG_HELP_WHITELIST=Füge Benutzer/ID/Regex zur Whitelist hinzu, um das versenden von Nachrichten über IRC zu ermöglichen. LANG_HELP_REMOVE_WHITELIST=Entferne Benutzer/ID/Regex aus der Whitelist. LANG_HELP_ACCESS=Setze Berechtigungen zum Versenden von Nachrichten über IRC. LANG_HELP_LIST=Liste die Whitelist oder die Sperrliste auf. LANG_ERR_COMMAND=Befehlsfehler. ================================================ FILE: Language/de-DE/BeatmapSuggest.DefaultLanguage.lang ================================================ [de-DE] LANG_GET_BEATMAP_FAILED=Erhalten von Beatmap Metadaten {0} schlug fehl,Fehlermeldung: {1}. LANG_SUGGEST_MEG={0} fordert dich auf, folgende Beatmap zu spielen [{1} {2}] || [{3} herunterladen] || [{4} Verweis] oder werwende "?dl"/"?dl all". LANG_NOT_FOUND_ERR=Es kann kein passender Inhalt gefunden werden oder die BeatmapSet ID ist ungültig. LANG_UNKNOWN_TITLE= LANG_GET_BEATMAP_TIME_OUT=Zeitüberschreitung beim erhalten des Beatmap Titel {0}, Aufgabenstatus{1}. LANG_INVAILD_ID=Ungültige ID {0}. LANG_UNKOWN_PARAM=Unbekannter Parameter {0}. LANG_ERROR_INFO_IMCOMPLETE=Unvollständige Informationen. LANG_NO_API_KEY_NOFITY=Der osu!api Schlüssel-eintrag fehlt in der Konfigurationsdatei config.ini. Bitte vervollständige diesen mit Hilfe von https://osu.ppy.sh/p/api LANG_START_DOWNLOAD=Der Download der Beatmap {0} hat begonnen. LANG_FINISH_DOWNLOAD=Der Download der Beatmap {0} wurde abgeschlossen. LANG_FAILED_DOWNLOAD=Der Downloaded der Beatmap {0} schlug fehl, Fehlermeldung: {1}. LANG_DOWNLOAD_TASK_COUNT=Der Download von {0} Beatmaps hat begonnen. ================================================ FILE: Language/de-DE/ConfigGUI.DefaultLanguage.lang ================================================ [de-DE] BUTTON_OPEN=Öffne BUTTON_BROWSE=Durchsuche BUTTON_FONT=Schriftart BUTTON_COLOR=Farbe WINDOW_TITLE=Konfiguration BUTTON_SAVE=Speichere LABEL_SAVED=Gespeichert! LABEL_SAVED_SAVING=Speichere... WINDOW_TITLE_REQUIRE_RESTART=Bitte starten sie neu um einige Einstellungen zu übernehmen. ================================================ FILE: Language/de-DE/DefaultGUI.Language.lang ================================================ [de-DE] UI_DISPLAY=Öffne die UI UI_TIPS_BOTIRC=BotIRC UI_TIPS_BOTIRC_PASS=IRC Passwort UI_TIPS_STATUS=Status UI_TIPS_DANMAKU=LiveID UI_TIPS_OSU_IRC=osu!Chat UI_TIPS_TARGET_IRC=ToIRC UI_BOTTON_START=Start UI_BOTTON_STOP=Stop UI_BOTTON_LOGIN_DANMAKU=Source Login UI_BOTTON_SWITCH_CON=Wechsel zur command-line-interface UI UI_BOTTON_EXIT=Beende UI_TIPS_LIVE_ID=LiveID UI_TIPS_LIVE_SOURCE=Quelle UI_INFO_RESTART_REQ=Um die Änderung des Livestreams zu übernehmen, muss Sync neugestartet werden. Starte jetzt neu? ================================================ FILE: Language/de-DE/DefaultPlugin.Language.lang ================================================ [de-DE] LANG_COMMANDS_LOGIN=login Benutzername> [Passwort] um Nachrichten zur Quelle zu versenden. LANG_COMMANDS_EXIT=Arbeiten einstellen und beenden. LANG_COMMANDS_CLEAR=Bildschirm löschen. LANG_COMMANDS_STATUS=Status der aktuellen Arbeit abrufen. LANG_COMMANDS_STOP=Laufende Arbeit stoppen. LANG_COMMANDS_START=Synchronisierung starten. LANG_COMMANDS_HELP=Hilfemitteilung drucken. LANG_COMMANDS_SOURCEMSG=sourcemsg Eine Nachricht zum Test an die Quelle senden. LANG_COMMANDS_CLIENTMSG=clientmsg Eine Nachricht zum Test an den Kunden senden. LANG_COMMANDS_SOURCES=Alle Quellen-Liste erhalten oder eine Quelle setzen. LANG_COMMANDS_MSGMGR=--help-argument ausführen, um Hilfe zu erhalten. LANG_COMMANDS_FILTERS=Alle verfügbaren Filter auflisten. LANG_COMMANDS_DISABLE= disable , um OnDisable() Funtion des Plugins aufzurufen. LANG_COMMANDS_SWITCH_CLIENT=switchclient [client], um uu einem bestimmten Client wechseln. LANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd], um sich an der Quelle anzumelden. LANG_COMMANDS_RESTART=Anwendung neu starten. LANG_COMMANDS_LANG=lang [cultureName] Sprache holen/einstellen. LANG_COMMANDS_LISTLANG=listlang [--all] Liste (unterstützte/alle) Sprachen. LANG_COMMANDS_BILIBILI=setbili Quelle der Bilibili RoomID setzen. LANG_COMMANDS_FILTERS_ITEM=Item LANG_COMMANDS_FILTERS_OBJ=Object LANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc), um das OSUIRCBot-Konten setzen. LANG_COMMANDS_BOTIRC_CURRENT=Aktuelles BotIRC: {0:S}. LANG_COMMANDS_BOTIRC_SET=Aktuelles BotIRC auf {0:S} setzen. LANG_COMMANDS_IRC_CURRENT=Aktuelles IRC Ziel: {0:S}. LANG_COMMANDS_IRC_SET=Aktuelles IRC Ziel auf {0:S} festlegen. LANG_COMMANDS_TARGET_CURRENT=Aktuelle LiveID: {0:S} LANG_COMMANDS_TARGET_SET=Aktuelle LiveID setzen auf LANG_COMMANDS_CLIENT_NAME=Client LANG_COMMANDS_CLIENT_AUTHOR=Autor LANG_COMMANDS_SOURCES_NAME=Quelle LANG_COMMANDS_SOURCES_AUTHOR=Autor LANG_COMMANDS_CURRENT=Auf {0:S} festlegen. LANG_COMMANDS_DANMAKU_NOT_SUPPORT=Die aktuelle Quelle ist keine Sendable-Quelle. LANG_COMMANDS_EXIT_DONE=Ausführung beendet, bitte schließen Sie das Fenster. LANG_COMMANDS_CHAT_IRC_NOTCONNECT= IRC nicht verbunden.Nachricht können nicht an den Client versendet werden. LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Anmeldung zur Quelle erforderlich. LANG_COMMANDS_START_ALREADY_RUN= Eine Instanz der Anwendung läuft bereits. LANG_COMMANDS_ARGUMENT_WRONG=Falsches Argument. LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit :Lower Level für weniger Nachrichten\n--option :auto/force_all/force_limit. LANG_COMMANDS_MSGMGR_LIMIT=Begrenzt... LANG_COMMANDS_MSGMGR_FREE=Unbegrenzt LANG_COMMANDS_MSGMGR_STATUS=MessageManager Modus:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}. LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Geschwindigkeitsbegrenzung auf {0} setzen. LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Grenzwerttyp auf {0} setzen. LANG_SEND_COOKIE_SAVED=Anmelde-Informationen erfolgreich gespeichert. LANG_SEND_DONE=Nachricht gesendet. LANG_DOUYU_FAIL=Überprüfung des Verbindungsstatus fehlgeschlagen! Außer {0}, Ergebnis {1} LANG_DOUYU_AUTH_SUCC=Authentifizierung zu Douyutv erfolgreich. LANG_DOUYU_DANMAKU=Erhaltene Nachricht: {0}:{1} LANG_DOUYU_GIFT=Geschenk LANG_BILIBILI_ONLINECHANGE=Online änderung: {0} LANG_COMMANDS_CLIENTUSERMSG=chatuser IRC-Nachricht an Live mit angegebenem Benutzernamen senden LANG_OSUIRC_LOGIN_SUCCESS=Anmeldung erfolgreich. LANG_OSUIRC_LOGIN_FAILED=Versuch, die Verbindung zu osu!irc wiederherzustellen, fehlgeschlagen, Grund:{0} LANG_OSUIRC_NETWORK_INTERRUPTED= Das Netzwerk ist unterbrochen, versuchen Sie nun, die Verbindung wieder herzustellen. LANG_OSUIRC_RECIVER_FINISHED=Der Empfänger-Thread ist beendet. ================================================ FILE: Language/de-DE/NowPlaying.Languages.lang ================================================ [de-DE] OSU_PATH_NOT_SET=osu! Pfad ist leer. Die Anwendung sucht nach einer laufenden osu! Instanz. FIND_OSU_PATH=Kann den osu! Pfad nicht finden: {0:S} OSU_PATH_FAIL=Kann den osu! Pfad nicht finden. Weitere Informationen versteckt. ERROR_WHILE_FIND_PATH=Fehler bei der Suche nach dem osu! Pfad {0:S} UNKNOWN_COMMAND=Unbekannter Befehl {0:S} STATUS_PLAYING=Spiele STATUS_EDITING=Bearbeite STATUS_OTHER=Höre STATUS_TIP_INFO=Ich bin {0:S}{1:S} STATUS_TIP_INFO_WRAP=Ich bin {0:S}{1:S} ERROR_WHILE_SEARCH_MAP=Fehler bei der Suche nach {0:S}-{1:S} [{2:S}] {3:S} CONSOLE_OUTPUT_RESULT=Datei:{0:S} ({1}ms) HP/CS/AR/OD: {2}{3}{4}{5} OUTPUT_RESULT=Aktuell {0}:{1} CURRENT_IDLE=Aktuell im Lehrlauf ================================================ FILE: Language/de-DE/OsuRTDataProvider.DefaultLanguage.lang ================================================ [de-DE] LANG_OSU_NOT_FOUND=[OsuRTDataProvider][ID:{0}]Kann den osu!.exe Prozess nicht finden. LANG_OSU_FOUND=[OsuRTDataProvider][ID:{0}]osu!.exe Prozess gefunden. LANG_INIT_PLAY_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Initialisierung von PlayFinder schlug fehl! Wiederhole in {1} Sekunden. LANG_INIT_PLAY_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Initialisierung von PlayFinder Success! LANG_INIT_STATUS_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Initialisierung von StatusFinder schlug fehl! Wiederhole in {1} Sekunden. LANG_INIT_STATUS_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Initialisierung von StatusFinder erfolgreich! LANG_INIT_BEATMAP_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Initialisierung von BeatmapFinder schlug fehl! Wiederhole in {1} Sekunden. LANG_INIT_BEATMAP_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Initialisierung von BeatmapFinder erfolgreich! LANG_INIT_MODE_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Initialisierung von ModeFinder Failed! schlug fehl! Wiederhole in {1} Sekunden. LANG_INIT_MODE_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Initialisierung von ModeFinder erfolgreich! LANG_BEATMAP_NOT_FOUND=Beatmap nicht gefunden. ================================================ FILE: Language/de-DE/RealTimePPDisplayer.DefaultLanguage.lang ================================================ [de-DE] UI_MENU_TOPMOST=Oberste TEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]PP Datei: {0} MMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]Speicherzuordnungsdatei: {0} ================================================ FILE: Language/de-DE/RecentlyUserQuery.DefaultLanguage.lang ================================================ [de-DE] LANG_HELP=\nDies ist eine Hilfe für IRC Befehle. Hänge dazu ein "?" am Anfang der Nachrichten ein.\nrecently --status | Rufe den Status des aktuellen Protokolls ab (osu! IRC nicht verfügbar)\n recently --u | Rufe Protokoll ab (Nicht empfohlen für osu! IRC Verwendung)\n recently --i | Rufe Protokoll ab (Nicht empfohlen für osu! IRC Verwendung)\n recently | Rufe den aktuellen Benutzernamen und die ID ab. Die ID kann verwendet für Aktionen wie "?ban --i" (osu! IRC unterstützt) verwendet werden\nrecently --disable | Nachrichtenprotokollierung deaktiviert. Alle Protokolle werden gelöscht (osu! IRC unterstützt)\recently --start | Protokollierung erneut starten (osu! IRC unterstützt)\recently --realloc | Kapazität der Protokolle neu zuweisen, um mehr Protokolle zu speichern (osu! IRC unterstützt)\recently --recently | Aktuelle Benutzer-ID ermitteln. LANG_MSG_STATUS=MessageRecord Status: {0} | recordCount/Kapazität: {1}/{2} LANG_RUNNING=Läuft LANG_STOP=Angehalten LANG_MSG_DISABLE=Nachrichtenprotokollierung deaktiviert, alle Protokolle wurden gelöscht. LANG_MSG_START=Die Nachrichtenprotokollierung wurde gestartet. LANG_MSG_REALLOC_ERR=MessageRecord: Falsche Anweisungen. LANG_MSG_REALLOC=Sie können jetzt den Nachrichtenverlauf für {0} protokollieren. LANG_MSG_NOTIMPLENT=Nicht implementiert. LANG_MSG_UNKNOWNCOMMAND=Unbekannter Befehl. ================================================ FILE: Language/de-DE/Sync.Tools.DefaultI18n.lang ================================================ [de-DE] LANG_Loading=Lade.... LANG_Plugins={0:D} Plugins geladen. LANG_Sources={0:D} Quellen geladen. LANG_Client={0:D} Clients geladen. LANG_Error=KÖNNEN WARPPER NICHT INITIALISIEREN! ÜBERPRÜFEN SIE IHRE PLUGINS. LANG_Commands={0:D} Befehle geladen. LANG_Filters={0:D} Filter geladen. LANG_Ready=Fertig. LANG_RqueireLogin=Bitte melden Sie sich an. LANG_AccountName=Benutzername: LANG_AccountPw=Passwort: LANG_AccountSave=Account erfolgreich gespeichert! LANG_Start=Starte... LANG_Stopping=Stoppe... LANG_Restarting=Starte neu... LANG_LoadingPlugin=Lade {0:S} ... LANG_LoadPluginErr=Kann {0:S} nicht laden ({1:S}) LANG_NotPluginErr={0:S} ist kein Standartplugin ({1:S}) LANG_NotConfig=Bitte nehmen sie Grundeinstellungen an der Konfiguration 'config.ini' vor. LANG_NoSource=Kann keine Quelle für Warpper finden. Bitte installieren Sie eine Quelle. LANG_MissSource=Ich kann die Quelle in den spezifischen Einstellungen nicht finden. LANG_SetSource={0:S} als aktuelle Live-Quelle festlegen LANG_SupportSend=Nach der Anmeldung können Sie eine Nachricht über die Quelle senden (Befehl: login [Benutzer] [Passwort]) LANG_CertLength=Länge des Zertifikats: {0:D} LANG_CertExist=Aktuelles Zertifikat vorhanden, überschreiben Sie es mit dem Anmelde-Befehl. LANG_SendNotReady=Die aktuelle Quelle ist nicht bereit, an Danmaku zu senden. LANG_UnknowCommand=Unbekannter Befehl, geben Sie 'help' für die Befehlsliste ein. LANG_CommandFail=Befehlsausführung fehlgeschlagen, geben Sie 'help' für die Befehlsliste ein. LANG_ConfigFile=Einstellungen LANG_UserCount=Änderung der Benutzeranzahl : {0:D} LANG_UserCount_Change=Zuschauer {0:S} to {1:D} LANG_UserCount_Change_Increase=erhöhen LANG_UserCount_Change_Decrease=reduzieren LANG_Source_Disconnecting=Trennen der Quellverbindung... LANG_Source_Disconnected=Die Verbindung zum Quellserver wurde getrennt. Wiederholung nach 3 Sekunden. LANG_Source_Disconnected_Succ=Erfolgreiche Trennung der Verbindung zum Quellserver. LANG_Source_Connect=Verbindung zum Quellserver herstellen... LANG_Source_Connected_Succ=Erfolg mit dem Quellenserver verbunden! LANG_Current_Online=Aktuell online: {0:D} LANG_Gift_Sent=Ich sende {O:D} (1 S) zu dir! LANG_Config=Einstellungen: LANG_Config_Status_OK=OK, LiveID:{0} LANG_Config_Status_Fail=Konfiguration fehlgeschlagen. LANG_Source=Quelle {0:S}: LANG_IRC=Client: LANG_Danmaku=Danmaku sendet: LANG_Status_Connected=Verbunden LANG_Status_NotConenct=Leerlauf LANG_Loading_Config=Laden von Einstellungen...\n LANG_Welcome=osu!Live Sync Version {0:S} LANG_Help=Geben Sie den Befehl 'help' für Hilfe ein LANG_Command=Befehl LANG_Command_Description=Beschreibung LANG_Plugin_Cycle_Reference=Das Plugin wird zyklisch referenziert. {0:S} wird ohne den abhängigen Baum nicht geladen. LANG_IRC_Connecting=[Client] verbindend... LANG_IRC_Disconnect=Trennen der Verbindung zum IRC-Server... LANG_IRC_Connect_Timeout=Client-Verbindung fehlgeschlagen, bitte überprüfen Sie Ihre Internetverbindung. LANG_IRC_Ready=[Client] ist bereit! LANG_MsgMgr_Limit=Der Nachrichtenbegrenzer ist eingeschaltet, das Danmaku nur mit dem Präfix ?send wird an irc gesendet. LANG_MsgMgr_Free=Der Nachrichtenbegrenzer ist ausgeschaltet. LANG_Instance_Exist=Die Anwendung ist bereits gestarter. LANG_COMMANDS_LOGIN=login Benutzername> [Passwort] um Nachrichten zur Quelle zu versenden. LANG_COMMANDS_LOGIN=login [pass] login to source to send message to source LANG_COMMANDS_EXIT=Arbeiten einstellen und beenden. LANG_COMMANDS_EXIT=Stop sync and Exit LANG_COMMANDS_CLEAR=Bildschirm löschen. LANG_COMMANDS_CLEAR=Clear output screen LANG_COMMANDS_STATUS=Status der aktuellen Arbeit abrufen. LANG_COMMANDS_STATUS=Get status of current Source and Client LANG_COMMANDS_STOP=Laufende Arbeit stoppen. LANG_COMMANDS_STOP=Stop sync LANG_COMMANDS_START=Synchronisierung starten. LANG_COMMANDS_START=Start sync LANG_COMMANDS_HELP=Hilfemitteilung drucken. LANG_COMMANDS_HELP=Print help message LANG_COMMANDS_SOURCEMSG=sourcemsg Eine Nachricht zum Test an die Quelle senden. LANG_COMMANDS_SOURCEMSG=sourcemsg Send a message to source for test LANG_COMMANDS_CLIENTMSG=clientmsg Eine Nachricht zum Test an den Kunden senden. LANG_COMMANDS_CLIENTMSG=clientmsg Send a message to client for test LANG_COMMANDS_SOURCES=Alle Quellen-Liste erhalten oder eine Quelle setzen. LANG_COMMANDS_SOURCES=get all source list or set a source LANG_COMMANDS_MSGMGR=--help-argument ausführen, um Hilfe zu erhalten. LANG_COMMANDS_MSGMGR=Execute with '--help' to get help LANG_COMMANDS_FILTERS=Alle verfügbaren Filter auflisten. LANG_COMMANDS_FILTERS=List all available filters. LANG_COMMANDS_DISABLE= disable , um OnDisable() Funtion des Plugins aufzurufen. LANG_COMMANDS_DISABLE=disable Invoke Plugin OnDisable() function LANG_COMMANDS_SWITCH_CLIENT=switchclient [client], um uu einem bestimmten Client wechseln. LANG_COMMANDS_SWITCH_CLIENT=switchclient [client] Switch to specific client LANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd], um sich an der Quelle anzumelden. LANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd] Login to source LANG_COMMANDS_RESTART=Anwendung neu starten. LANG_COMMANDS_RESTART=Restart Sync LANG_COMMANDS_LANG=lang [cultureName] Sprache holen/einstellen. LANG_COMMANDS_LANG=lang [cultureName] Get/Set language LANG_COMMANDS_LISTLANG=listlang [--all] Liste (unterstützte/alle) Sprachen. LANG_COMMANDS_FILTERS_ITEM=Item LANG_COMMANDS_FILTERS_ITEM=Item LANG_COMMANDS_FILTERS_OBJ=Object LANG_COMMANDS_CLIENT_NAME=Client LANG_COMMANDS_CLIENT_NAME=Client LANG_COMMANDS_CLIENT_AUTHOR=Autor LANG_COMMANDS_CLIENT_AUTHOR=Author LANG_COMMANDS_SOURCES_NAME=Quelle LANG_COMMANDS_SOURCES_NAME=Source LANG_COMMANDS_SOURCES_AUTHOR=Autor LANG_COMMANDS_SOURCES_AUTHOR=Author LANG_COMMANDS_CURRENT=Auf {0:S} festlegen. LANG_COMMANDS_CURRENT=Set to {0:S} LANG_COMMANDS_DANMAKU_NOT_SUPPORT=Die aktuelle Quelle ist keine Sendable-Quelle. LANG_COMMANDS_CHAT_IRC_NOTCONNECT= IRC nicht verbunden.Nachricht können nicht an den Client versendet werden. LANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!irc not connect properly, can't send message. LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Anmeldung zur Quelle erforderlich. LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Require Login to source! LANG_COMMANDS_START_ALREADY_RUN= Eine Instanz der Anwendung läuft bereits. LANG_COMMANDS_START_ALREADY_RUN=An instance is already running LANG_COMMANDS_ARGUMENT_WRONG=Falsches Argument. LANG_COMMANDS_ARGUMENT_WRONG=Wrong argument. LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit :Lower Level für weniger Nachrichten\n--option :auto/force_all/force_limit. LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit :Lower Level for less messaging\n--option :Auto/ForceAll/ForceLimit/DisableAll LANG_COMMANDS_MSGMGR_LIMIT=Begrenzt... LANG_COMMANDS_MSGMGR_LIMIT=MessageManager is limited... LANG_COMMANDS_MSGMGR_FREE=Unbegrenzt LANG_COMMANDS_MSGMGR_FREE=MessageManager is freed... LANG_COMMANDS_MSGMGR_STATUS=MessageManager Modus:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}. LANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3} LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Geschwindigkeitsbegrenzung auf {0} setzen. LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Set speed limit to {0} LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Grenzwerttyp auf {0} setzen. LANG_COMMANDS_START_NO_SOURCE=Es muss vor dem Start eine Quelle ausgewählt. LANG_COMMANDS_START_NO_CLIENT=Es müssen vor dem Start ein Client ausgewählt sein. LANG_COMMANDS_CURRENT_LANG=Aktuelle Sprache: {0:S} {1:S} LANG_COMMANDS_LANG_SWITCHED=sucSprache erfolgreich umstellen auf {1:S}({0:S}) LANG_COMMANDS_LANG_NOT_FOUND=Sprachumschaltung fehlgeschlagen! Benutze 'listlang', um alle verfügbaren Sprachen anzuzuzeigen. LANG_UPDATE_DONE=Update durchgeführt. Neustart, um die Aktualisierung abzuschließen. LANG_INSTALL_DONE=Installation abgeschlossen. Neustart, um die Installation abzuschließen. LANG_PLUGIN_NOT_FOUND=Plugin {0} ist nicht vorhanden. LANG_REMOVE_DONE=Entfernt. Neu starten, um Effekt anzuwenden. LANG_VERSION_LATEST_OR_CANEL={0} ist aktuell oder wurde vom Benutzer abgebrochen. LANG_UPDATE_CHECK_ERROR=Aktualisierung kann nicht geprüft werden für [{0}] : {1} : {2} LANG_UPDATE_ERROR=Update nicht möglich: {0} : {1} LANG_SOURCE_NOT_SUPPORT_SEND=Die Quelle {0} unterstützt das Senden von Nachrichten noch nicht. LANG_NO_PLUGIN_SELECT=Es muss einen Plugin-Namen angeben werden. LANG_PLUGIN_DISABLED=Deaktiviert LANG_COMMANDS_CLIENTUSERMSG=chatuser Testnachricht als zum IRC senden. LANG_COMMANDS_EXIT_DONE=Aktionsausführung beenden beendet, bitte schließen Sie das Fenster. LANG_NO_ANY_SOURCE=Es ist keine Quelle verfügbar, bitte überprüfen Sie den Plugin-Ordner oder geben Sie 'plugins install DefaultPlugin' ein, um das Plugin als Standardquellen zu installieren ================================================ FILE: Language/en-US/BanManagerPlugin.DefaultLanguage.lang ================================================ [en-US] LANG_HELP_BAN=ban user/id/regex ,message will be sent to irc LANG_HELP_UNBAN=unban user/id/regex LANG_HELP_WHITELIST=add user/id/regex to whitelist who can send message to irc LANG_HELP_REMOVE_WHITELIST=remove user/id/regex from whitelist LANG_HELP_ACCESS=set access level for sending messages to irc LANG_HELP_LIST=list whiltelist or banlist LANG_ERR_COMMAND=command error ================================================ FILE: Language/en-US/BeatmapSuggest.DefaultLanguage.lang ================================================ [en-US] LANG_GET_BEATMAP_FAILED=Get beatmap info {0} failed,message:{1} LANG_SUGGEST_MEG={0} want you to play the beatmap [{1} {2}] || [{3} dl] || [{4} mirror] or type"?dl"/"?dl all" LANG_NOT_FOUND_ERR=ҲƥݻidЧbeatmapSetId Can't match infomation or param ID is invaild beatmapSetId LANG_UNKNOWN_TITLE= LANG_GET_BEATMAP_TIME_OUT=Get beatmap info {0} time out,TaskStatus{1} LANG_INVAILD_ID=invaild id value {0} LANG_UNKOWN_PARAM=unknown param {0} LANG_ERROR_INFO_IMCOMPLETE=Incompletable infomation LANG_NO_API_KEY_NOFITY=there is no ApiKey in config.ini,please provide your osu!api_key for using.ApiKey address:https://osu.ppy.sh/p/api LANG_START_DOWNLOAD=Start to download beatmap:{0} LANG_FINISH_DOWNLOAD=Beatmap downloaded {0} LANG_FAILED_DOWNLOAD=Downloaded {0} failed,message {1} LANG_DOWNLOAD_TASK_COUNT=Start to download {0} beatmaps. ================================================ FILE: Language/en-US/ConfigGUI.DefaultLanguage.lang ================================================ [en-us] BUTTON_OPEN=Open BUTTON_BROWSE=Browse BUTTON_FONT=Font BUTTON_COLOR=Color WINDOW_TITLE=Config BUTTON_SAVE=Save LABEL_SAVED=Saved! LABEL_SAVED_SAVING=Saving... WINDOW_TITLE_REQUIRE_RESTART=Some settings restart to take effect ================================================ FILE: Language/en-US/DefaultGUI.Language.lang ================================================ [en-US] UI_DISPLAY=Show UI UI_TIPS_BOTIRC=BotIRC UI_TIPS_BOTIRC_PASS=IRC PW UI_TIPS_STATUS=Status UI_TIPS_DANMAKU=LiveID UI_TIPS_OSU_IRC=osu!Chat UI_TIPS_TARGET_IRC=ToIRC UI_BOTTON_START=Start UI_BOTTON_STOP=Stop UI_BOTTON_LOGIN_DANMAKU=Source Login UI_BOTTON_SWITCH_CON=Switch to CMD UI UI_BOTTON_EXIT=Exit UI_TIPS_LIVE_ID=LiveID UI_TIPS_LIVE_SOURCE=Source UI_INFO_RESTART_REQ=Changing live source will be applied after restarting Sync. Restart now? ================================================ FILE: Language/en-US/DefaultPlugin.Language.lang ================================================ [en-US] LANG_COMMANDS_LOGIN=login [pass] login to source to send message to source LANG_COMMANDS_EXIT=Stop works and Exit LANG_COMMANDS_CLEAR=Clear screen LANG_COMMANDS_STATUS=Get status of current work LANG_COMMANDS_STOP=Stop current work LANG_COMMANDS_START=Start sync LANG_COMMANDS_HELP=Print help message LANG_COMMANDS_SOURCEMSG=sourcemsg Send a message to source for test LANG_COMMANDS_CLIENTMSG=clientmsg Send a message to client for test LANG_COMMANDS_SOURCES=get all source list or set a source LANG_COMMANDS_MSGMGR=Execute with --help argument to get help LANG_COMMANDS_FILTERS=List all available filters. LANG_COMMANDS_DISABLE=disable Invoke Plugin OnDisable() function LANG_COMMANDS_SWITCH_CLIENT=switchclient [client] Switch to specific client LANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd] Login to source LANG_COMMANDS_RESTART=Restart application LANG_COMMANDS_LANG=lang [cultureName] Get/Set language LANG_COMMANDS_LISTLANG=listlang [--all] List (supported/all) languages LANG_COMMANDS_BILIBILI=setbili Set Source Bilibili RoomID LANG_COMMANDS_FILTERS_ITEM=Item LANG_COMMANDS_FILTERS_OBJ=Object LANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc) Set OSUIRCBot Accounts LANG_COMMANDS_BOTIRC_CURRENT=Current BotIRC: {0:S} LANG_COMMANDS_BOTIRC_SET=Set Current BotIRC to {0:S} LANG_COMMANDS_IRC_CURRENT=Current Target IRC: {0:S} LANG_COMMANDS_IRC_SET=Set Current Target IRC to {0:S} LANG_COMMANDS_TARGET_CURRENT=Current LiveID: {0:S} LANG_COMMANDS_TARGET_SET=Set Current LiveID to LANG_COMMANDS_CLIENT_NAME=Client LANG_COMMANDS_CLIENT_AUTHOR=Author LANG_COMMANDS_SOURCES_NAME=Source LANG_COMMANDS_SOURCES_AUTHOR=Author LANG_COMMANDS_CURRENT=Set to {0:S} LANG_COMMANDS_DANMAKU_NOT_SUPPORT=Current source is not a Sendable source. LANG_COMMANDS_EXIT_DONE=Exit action execution done, please close the window. LANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu! irc is not connected, cannot send message to client. LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Require Login to source! LANG_COMMANDS_START_ALREADY_RUN=An instance is already running LANG_COMMANDS_ARGUMENT_WRONG=Wrong argument. LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit :Lower Level for less messaging\n--option :auto/force_all/force_limit LANG_COMMANDS_MSGMGR_LIMIT=Limited... LANG_COMMANDS_MSGMGR_FREE=Unlimited LANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3} LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Set speed limit to {0} LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Set limit type to {0} LANG_SEND_COOKIE_SAVED=Login infomation succesfully saved LANG_SEND_DONE=Message Sent. LANG_DOUYU_FAIL=Checking connection status failed! Except {0}, Result {1} LANG_DOUYU_AUTH_SUCC=Auth to Douyutv success LANG_DOUYU_DANMAKU=Recive message: {0}:{1} LANG_DOUYU_GIFT=Gift LANG_BILIBILI_ONLINECHANGE=Online change: {0} LANG_COMMANDS_CLIENTUSERMSG=chatuser Send irc message to live with specified username LANG_OSUIRC_LOGIN_SUCCESS=Login succesfully. LANG_OSUIRC_LOGIN_FAILED=Try to reconnect to osu!irc failed,reason:{0} LANG_OSUIRC_NETWORK_INTERRUPTED=Network is interrupted,now try to reconnect. LANG_OSUIRC_RECIVER_FINISHED=Reciver thread is finished. ================================================ FILE: Language/en-US/NowPlaying.Languages.lang ================================================ [en-US] OSU_PATH_NOT_SET=osu! path is empty, program will search the running osu! instance. FIND_OSU_PATH=Can't find osu! path! {0:S} OSU_PATH_FAIL=Can't find osu! path, Detailed info is disabled. ERROR_WHILE_FIND_PATH=Error while searching osu! path {0:S} UNKNOWN_COMMAND=Unknown command {0:S} STATUS_PLAYING=Playing STATUS_EDITING=Editing STATUS_OTHER=Listening STATUS_TIP_INFO=I'm {0:S}{1:S} STATUS_TIP_INFO_WRAP=I'm {0:S}{1:S} ERROR_WHILE_SEARCH_MAP=Error while searching {0:S}-{1:S} [{2:S}] {3:S} CONSOLE_OUTPUT_RESULT=File:{0:S} ({1}ms) HP/CS/AR/OD: {2}{3}{4}{5} OUTPUT_RESULT=Current {0}:{1} CURRENT_IDLE=Currently idle ================================================ FILE: Language/en-US/OsuRTDataProvider.DefaultLanguage.lang ================================================ [en-US] LANG_OSU_NOT_FOUND=[OsuRTDataProvider][ID:{0}]Cannot find osu!.exe process LANG_OSU_FOUND=[OsuRTDataProvider][ID:{0}]Found osu!.exe process LANG_INIT_PLAY_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init PlayFinder Failed! Retry after {1} seconds LANG_INIT_PLAY_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init PlayFinder Success! LANG_INIT_STATUS_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init StatusFinder Failed! Retry after {1} seconds LANG_INIT_STATUS_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init StatusFinder Success! LANG_INIT_BEATMAP_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init BeatmapFinder Failed! Retry after {1} seconds LANG_INIT_BEATMAP_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init BeatmapFinder Success! LANG_INIT_MODE_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init ModeFinder Failed! Retry after {1} seconds LANG_INIT_MODE_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init ModeFinder Success! LANG_BEATMAP_NOT_FOUND=Beatmap not found ================================================ FILE: Language/en-US/RealTimePPDisplayer.DefaultLanguage.lang ================================================ [en-US] UI_MENU_TOPMOST=Topmost TEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]PP File: {0} MMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]Memory Mapping File: {0} ================================================ FILE: Language/en-US/RecentlyUserQuery.DefaultLanguage.lang ================================================ [en-US] LANG_HELP=\nThis is a help for osu!irc common commands(in osu!irc please add "?" at the beginning of commands)\nrecently --status | Get the status of the current log (osu!irc unavailable)\nrecently --u | Get the history (Not recommended for osu!irc use)\nrecently --i | Get the history (Not recommended for osu!irc use)\nrecently | Get recent username and ID, ID can be used to perform actions like "?ban --i" (osu!irc supported)\nrecently --disable | Message logging disabled, all logs have been cleared (osu!irc supported)\nrecently --start | Start logging again (osu!irc supported)\nrecently --realloc | Reallocate the capacity of the logs to store more logs (osu!irc supported)\nrecently --recently | Get recent user id\n LANG_MSG_STATUS=MessageRecord status: {0} | recordCount/Capacity : {1}/{2} LANG_RUNNING=running LANG_STOP=stopped LANG_MSG_DISABLE=Message logging disabled, all logs have been cleared LANG_MSG_START=Message logging has been started LANG_MSG_REALLOC_ERR=MessageRecord: Wrong instructions LANG_MSG_REALLOC=You can now log the message history for {0} LANG_MSG_NOTIMPLENT=Not implemented LANG_MSG_UNKNOWNCOMMAND=Unknown command. ================================================ FILE: Language/en-US/Sync.Tools.DefaultI18n.lang ================================================ [en-US] LANG_Loading=Loading.... LANG_Plugins=Loaded {0:D} Plugins LANG_Sources=Loaded {0:D} Sources LANG_Client=Loaded {0:D} Clients LANG_Error=CAN NOT INITIAL WARPPERS! CHECK YOUR PLUGINS. LANG_Commands=Loaded {0:D} Commands LANG_Filters=Loaded {0:D} Filters LANG_Ready=Done LANG_RqueireLogin=Please login to your account LANG_AccountName=Username: LANG_AccountPw=Password: LANG_AccountSave=Account succesfully saved! LANG_Start=Starting... LANG_Stopping=Stopping... LANG_Restarting=Restarting... LANG_LoadingPlugin=Load {0:S} ... LANG_LoadPluginErr=Can't load {0:S} ({1:S}) LANG_NotPluginErr={0:S} not a standard plugin ({1:S}) LANG_NotConfig=Please edit 'config.ini' for initial settings. LANG_NoSource=Can't find a source for warpper. Please install a source. LANG_MissSource=Can't find source from the specific settings. LANG_SetSource=Set {0:S} as current live source LANG_SupportSend=After login you can send a message via source (Command: login [user] [passwd]) LANG_CertLength=Certification Length: {0:D} LANG_CertExist=Current certification exist, override it with login command LANG_SendNotReady=Current source is not ready for send a danmaku. LANG_UnknowCommand=Unknown command, type 'help' for command list. LANG_CommandFail=Command execution failed, type 'help' for command list. LANG_ConfigFile=Settings LANG_UserCount=User Count change : {0:D} LANG_UserCount_Change=Spectators {0:S} to {1:D} LANG_UserCount_Change_Increase=increase LANG_UserCount_Change_Decrease=reduce LANG_Source_Disconnecting=Disconnecting the source connection... LANG_Source_Disconnected=Source server has been disconnected. Retry after 3 sec. LANG_Source_Disconnected_Succ=Successfully disconnected from the source server. LANG_Source_Connect=Connecting to source server... LANG_Source_Connected_Succ=Success connected to source server! LANG_Current_Online=Current online : {0:D} LANG_Gift_Sent=I send {O:D} {1:S} to you! LANG_Config=Settings: LANG_Config_Status_OK=OK, LiveID:{0} LANG_Config_Status_Fail=Configuration failed. LANG_Source=Source{0:S}: LANG_IRC=Client: LANG_Danmaku=Danmaku Send: LANG_Status_Connected=Connected LANG_Status_NotConenct=Idle LANG_Loading_Config=Loading settings...\n LANG_Welcome=osu!Live Sync ver {0:S} LANG_Help=Type 'help' command for help LANG_Command=Command LANG_Command_Description=Description LANG_Plugin_Cycle_Reference=The plugin is cycle referenced. {0:S} will not load without the dependent tree. LANG_IRC_Connecting=[Client] connecting... LANG_IRC_Disconnect=Disconnecting from IRC server... LANG_IRC_Connect_Timeout=Client connection failed,please check your internet connection. LANG_IRC_Ready=[Client] is ready! LANG_MsgMgr_Limit=Message limiter is toggled ON, the danmaku only with ?send prefix will be sent to irc LANG_MsgMgr_Free=Message limiter is toggled OFF. LANG_Instance_Exist=Sync is already started. LANG_COMMANDS_LOGIN=login [pass] login to source to send message to source LANG_COMMANDS_EXIT=Stop sync and Exit LANG_COMMANDS_CLEAR=Clear output screen LANG_COMMANDS_STATUS=Get status of current Source and Client LANG_COMMANDS_STOP=Stop sync LANG_COMMANDS_START=Start sync LANG_COMMANDS_HELP=Print help message LANG_COMMANDS_SOURCEMSG=sourcemsg Send a message to source for test LANG_COMMANDS_CLIENTMSG=clientmsg Send a message to client for test LANG_COMMANDS_SOURCES=get all source list or set a source LANG_COMMANDS_MSGMGR=Execute with '--help' to get help LANG_COMMANDS_FILTERS=List all available filters. LANG_COMMANDS_DISABLE=disable Invoke Plugin OnDisable() function LANG_COMMANDS_SWITCH_CLIENT=switchclient [client] Switch to specific client LANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd] Login to source LANG_COMMANDS_RESTART=Restart Sync LANG_COMMANDS_LANG=lang [cultureName] Get/Set language LANG_COMMANDS_LISTLANG=listlang [--all] List (supported/all) languages LANG_COMMANDS_FILTERS_ITEM=Item LANG_COMMANDS_FILTERS_OBJ=Object LANG_COMMANDS_CLIENT_NAME=Client LANG_COMMANDS_CLIENT_AUTHOR=Author LANG_COMMANDS_SOURCES_NAME=Source LANG_COMMANDS_SOURCES_AUTHOR=Author LANG_COMMANDS_CURRENT=Set to {0:S} LANG_COMMANDS_DANMAKU_NOT_SUPPORT=Current source is not a Sendable source. LANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!irc not connect properly, can't send message. LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Require Login to source! LANG_COMMANDS_START_ALREADY_RUN=An instance is already running LANG_COMMANDS_ARGUMENT_WRONG=Wrong argument. LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit :Lower Level for less messaging\n--option :Auto/ForceAll/ForceLimit/DisableAll LANG_COMMANDS_MSGMGR_LIMIT=MessageManager is limited... LANG_COMMANDS_MSGMGR_FREE=MessageManager is freed... LANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3} LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Set speed limit to {0} LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Set limit type to {0} LANG_COMMANDS_START_NO_SOURCE=need select a Source before start. LANG_COMMANDS_START_NO_CLIENT=need select a Client before start. LANG_COMMANDS_CURRENT_LANG=Current language: {0:S} {1:S} LANG_COMMANDS_LANG_SWITCHED=succesfully switch language to {1:S}({0:S}) LANG_COMMANDS_LANG_NOT_FOUND=switch language fail! use 'listlang' show all available language LANG_UPDATE_DONE=Update done. Restart to complete update. LANG_INSTALL_DONE=Install done. Restart to complete install. LANG_PLUGIN_NOT_FOUND=Plugin {0} is not exist LANG_REMOVE_DONE=Remove done. Restart to apply effect LANG_VERSION_LATEST_OR_CANEL={0} is up-to-date or user caneled update operation. LANG_UPDATE_CHECK_ERROR=Cannot check update for [{0}] : {1} : {2} LANG_UPDATE_ERROR=Cannot update : {0} : {1} LANG_SOURCE_NOT_SUPPORT_SEND=The source {0} not support send message yet. LANG_NO_PLUGIN_SELECT=Must specify a plugin name LANG_PLUGIN_DISABLED=Disabled LANG_COMMANDS_CLIENTUSERMSG=chatuser send message as username for irc test LANG_COMMANDS_EXIT_DONE=Exit action execution done, please close the window. LANG_NO_ANY_SOURCE="There is no source available, please check Plugin folder or input 'plugins install DefaultPlugin' to install plugin for default sources." ================================================ FILE: Language/hu-HU/BanManagerPlugin.DefaultLanguage.lang ================================================ [hu-HU] LANG_HELP_BAN=ban user/id/regex , felhasznl kitiltsa, zenet kldve lesz IRC-re is LANG_HELP_UNBAN=unban user/id/regex , felhasznl tiltssnak feloldsa LANG_HELP_WHITELIST=add user/id/regex felhasznl engedlylisthoz adsa, akik tudnak IRC-re zenetet kldeni LANG_HELP_REMOVE_WHITELIST=remove user/id/regex felhasznl engedlylistrl val levtele LANG_HELP_ACCESS=hozzfrsi szint belltsa az IRC-en val zenetkldshez LANG_HELP_LIST=list whitelist(engedlylista) vagy banlist(tiltslista) LANG_ERR_COMMAND=parancs hiba ================================================ FILE: Language/hu-HU/DefaultGUI.Language.lang ================================================ [hu-HU] UI_DISPLAY=Fellet mutatsa UI_TIPS_BOTIRC=BotIRC UI_TIPS_BOTIRC_PASS=IRC Jelsz UI_TIPS_STATUS=Sttusz UI_TIPS_DANMAKU=LiveID UI_TIPS_OSU_IRC=osu!Chat UI_TIPS_TARGET_IRC=IRC cm UI_BOTTON_START=Indts UI_BOTTON_STOP=Lellts UI_BOTTON_LOGIN_DANMAKU=Platform bejelentkezs UI_BOTTON_SWITCH_CON=Vlts CMD felletre UI_BOTTON_EXIT=Kilps UI_TIPS_LIVE_ID=LiveID UI_TIPS_LIVE_SOURCE=Platform UI_INFO_RESTART_REQ=Platform megvltoztatshoz jraindts szksges. jraindtod most? ================================================ FILE: Language/hu-HU/DefaultPlugin.Language.lang ================================================ [hu-HU] LANG_COMMANDS_LOGIN=login [pass] login to source to send message to source LANG_COMMANDS_EXIT=Folyamatok lelltsa s Kilps LANG_COMMANDS_CLEAR=Kperny tiszttsa LANG_COMMANDS_STATUS=Fut folyamatok sttusza LANG_COMMANDS_STOP=Jelenlegi folyamat lelltsa LANG_COMMANDS_START=Folyamat indtsa LANG_COMMANDS_HELP=Segtsg msolsa LANG_COMMANDS_SOURCEMSG=sourcemsg Teszt ezenet kldse a platformnak LANG_COMMANDS_CLIENTMSG=clientmsg Teszt ezenet kldse a kliensnek LANG_COMMANDS_SOURCES=sszes platform lekrdezse vagy belltsa LANG_COMMANDS_MSGMGR=Vgrehajts --help parancsal a sghoz LANG_COMMANDS_FILTERS=sszes szr listzsa. LANG_COMMANDS_DISABLE=disable Plugin funkcijnak kikapcsolsa LANG_COMMANDS_SWITCH_CLIENT=switchclient [client] Meghatrozott kliensre vlts LANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd] Bejelentkezs platformra LANG_COMMANDS_RESTART=Program jraindtsa LANG_COMMANDS_LANG=lang [cultureName] Nyelv lekrdezse/belltsa LANG_COMMANDS_LISTLANG=listlang [--all] Nyelvlista (tmogatott/sszes) LANG_COMMANDS_BILIBILI=setbili Bilibili platform RoomID belltsa LANG_COMMANDS_FILTERS_ITEM=Trgy LANG_COMMANDS_FILTERS_OBJ=Objektum LANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc) OSUIRCBot Felhasznl belltsa LANG_COMMANDS_BOTIRC_CURRENT=Jelenlegi BotIRC: {0:S} LANG_COMMANDS_BOTIRC_SET=BotIRC jelenlegi belltsa erre: {0:S} LANG_COMMANDS_IRC_CURRENT=Clpont IRC: {0:S} LANG_COMMANDS_IRC_SET=Clpont IRC belltsa ide {0:S} LANG_COMMANDS_TARGET_CURRENT=Jelenlegi LiveID: {0:S} LANG_COMMANDS_TARGET_SET=Jelenlegi LiveID belltsa ide LANG_COMMANDS_CLIENT_NAME=Kliens LANG_COMMANDS_CLIENT_AUTHOR=Szerz LANG_COMMANDS_SOURCES_NAME=Platform LANG_COMMANDS_SOURCES_AUTHOR=Szerz LANG_COMMANDS_CURRENT=Bellts erre: {0:S} LANG_COMMANDS_DANMAKU_NOT_SUPPORT=Jelenlegi platform nem egy zenetkld platform. LANG_COMMANDS_EXIT_DONE=Kilps parancs vgrehajtva, krlek zrd be az ablakot. LANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu! irc nincs csatlakozva, nem tudsz zenetet kldeni a kliensnek. LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Platformra val bejelentkezs szksges! LANG_COMMANDS_START_ALREADY_RUN=Egy folyamat mr fut LANG_COMMANDS_ARGUMENT_WRONG=Rosszul megadott argument. LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit :Kevesebb zenetkldshez cskkentsd a szintet\n--option :auto/force_all/force_limit LANG_COMMANDS_MSGMGR_LIMIT=Korltozott... LANG_COMMANDS_MSGMGR_FREE=Korltlan LANG_COMMANDS_MSGMGR_STATUS=MessageManager md:{4:S},sttusz:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3} LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Sebessg hatr belltsa erre: {0} LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Tpus hatr belltsa erre: {0} LANG_SEND_COOKIE_SAVED=Bejelentkezsi informci sikeresen mentve LANG_SEND_DONE=zenet elkldve. LANG_DOUYU_FAIL=Kapcsolat ellenrzse sikertelen! Except {0}, Result {1} LANG_DOUYU_AUTH_SUCC=Douyutv bejelentkezs sikeres LANG_DOUYU_DANMAKU=Fogadott zenet: {0}:{1} LANG_DOUYU_GIFT=Ajndk LANG_BILIBILI_ONLINECHANGE=Online vltozs: {0} LANG_COMMANDS_CLIENTUSERMSG=chatuser IRC zenet kldse lbe egy megadott felhasznlnak ================================================ FILE: Language/hu-HU/NowPlaying.Languages.lang ================================================ [hu-HU] OSU_PATH_NOT_SET=osu! megadott elrsi helye res, a program prblja megkeresni a fut osu! alkalmazst. FIND_OSU_PATH=osu! elrsi helye nem tallhat! {0:S} OSU_PATH_FAIL=osu! elrsi helye nem tallhat, Rszletes informcik letiltva. ERROR_WHILE_FIND_PATH=Hiba trtnt az osu! elrsi helynek keressekor {0:S} UNKNOWN_COMMAND=Ismeretlen parancs {0:S} STATUS_PLAYING=Jtkban STATUS_EDITING=Szerkeszt STATUS_OTHER=Hallgatja STATUS_TIP_INFO=n vagyok {0:S}{1:S} STATUS_TIP_INFO_WRAP=n vagyok {0:S}{1:S} ERROR_WHILE_SEARCH_MAP=Hiba keress kzben {0:S}-{1:S} [{2:S}] {3:S} CONSOLE_OUTPUT_RESULT=Fjl:{0:S} ({1}ms) HP/CS/AR/OD: {2}{3}{4}{5} OUTPUT_RESULT=Jelenlegi {0}:{1} CURRENT_IDLE=Jelenleg ttlen ================================================ FILE: Language/hu-HU/OsuRTDataProvider.DefaultLanguage.lang ================================================ [hu-HU] LANG_OSU_NOT_FOUND=[OsuRTDataProvider][ID:{0} osu!.exe folyamat nem tallhat LANG_OSU_FOUND=[OsuRTDataProvider][ID:{0}]osu!.exe folyamat szlelve LANG_INIT_PLAY_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init PlayFinder Hiba! jraprblkozs {1} msodperc mlva LANG_INIT_PLAY_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init PlayFinder Sikerlt! LANG_INIT_STATUS_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init StatusFinder Hiba! jraprblkozs {1} msodperc mlva LANG_INIT_STATUS_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init StatusFinder Sikerlt! LANG_INIT_BEATMAP_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init BeatmapFinder Hiba! jraprblkozs {1} msodperc mlva LANG_INIT_BEATMAP_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init BeatmapFinder Sikerlt! LANG_INIT_MODE_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init ModeFinder Hiba! jraprblkozs {1} msodperc mlva LANG_INIT_MODE_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init ModeFinder Sikerlt! LANG_BEATMAP_NOT_FOUND=Beatmap nem tallhat ================================================ FILE: Language/hu-HU/RealTimePPDisplayer.DefaultLanguage.lang ================================================ [hu-HU] UI_MENU_TOPMOST=Legnagyobb TEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]PP File: {0} MMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]Memory Mapping File: {0} ================================================ FILE: Language/hu-HU/RecentlyUserQuery.DefaultLanguage.lang ================================================ [hu-HU] LANG_HELP=\nEz egy sg az osu!irc ltalnos parancsaihoz(osu!irc-ben krlek hasznlj "?" eltagot a parancso keltt)\nrecently --status | Jelenlegi zenetelzmny sttusznak lekrdezse (osu!irc nem lehetsges)\nrecently --u | elzmnynek lekrdezse (Nem ajnlott osu!irc hasznlata)\nrecently --i | elzmnynek lekrdezse (Nem ajnlott osu!irc hasznlata)\nrecently | Felhasznlnv s ID lekrdezse, az ID felhasznlhat parancsok vgrehajtshoz, pldul "?ban --i" (osu!irc tmogatott)\nrecently --disable | zenetek mentsnek kikapcsols, sszes zenetelzmny trlve (osu!irc tmogatott)\nrecently --start | zenetek mentsnek elkezdse (osu!irc tmogatott)\nrecently --realloc | zenetelzmnyek trhelynek jraellenrzse tbb zenet trolshoz (osu!irc tmogatott)\nrecently --recently | Legutbbi felhasznl ID lekrdezse\n LANG_MSG_STATUS=MessageRecord sttusz: {0} | recordCount/Capacity : {1}/{2} LANG_RUNNING=fut LANG_STOP=lelltott LANG_MSG_DISABLE=zenetek mentse letiltva, az sszes trolt zenet elzmny trlve LANG_MSG_START=zenetek mentse elkezddtt LANG_MSG_REALLOC_ERR=MessageRecord: Rossz parancs LANG_MSG_REALLOC=zenetek lehetsges trolsi ideje: {0} LANG_MSG_NOTIMPLENT=Nincs beptve LANG_MSG_UNKNOWNCOMMAND=Ismeretlen parancs. ================================================ FILE: Language/hu-HU/Sync.Tools.DefaultI18n.lang ================================================ [hu-HU] LANG_Loading=Betlts... LANG_Plugins=Betltve {0:D} Plugin LANG_Sources=Betltve {0:D} Platform LANG_Client=Betltve {0:D} Kliens LANG_Error=Warpper nem tlthet be! ELLENRIZD A PLUGINOKAT. LANG_Commands=Betltve {0:D} Parancs LANG_Filters=Betltve {0:D} Szr LANG_Ready=Ksz LANG_RqueireLogin=Krlek lpj be a fikodba LANG_AccountName=Felhasznlnv: LANG_AccountPw=Jelsz: LANG_AccountSave=Fik sikeresen mentve! LANG_Start=Indts... LANG_Stopping=Lellts... LANG_Restarting=jraindts... LANG_LoadingPlugin={0:S} betltse... LANG_LoadPluginErr=Nem lehet betlteni {0:S} ({1:S}) LANG_NotPluginErr={0:S} nem egy ltalnos plugin ({1:S}) LANG_NotConfig=Krlek szerkeszd a 'config.ini'-t tovbbi belltsokrt. LANG_NoSource=Warpper platform nem tallhat. Krlek telepts egy platformot. LANG_MissSource=A megadott belltsokkal platform nem tallhat. LANG_SetSource={0:S} belltsa jelenlegi l platformnak LANG_SupportSend=Bejelentkezs utn a platformon keresztl tudsz zenetet kldeni (Parancs: login [user] [passwd]) LANG_CertLength=Bizonylat hosszsga: {0:D} LANG_CertExist=Jelenlegi bizonylat mr ltezik, bejelentkezsi paranccsal fellrhat LANG_SendNotReady=Jelenlegi paltform nem ll kszen zenet kldsre. LANG_UnknowCommand=Ismeretlen parancs, rj 'help' parancsot a sghoz. LANG_CommandFail=Parancs vgrehajtsi hiba, rj 'help' parancsot a sghoz. LANG_ConfigFile=Belltsok LANG_UserCount=Felhasznl szm vltozs : {0:D} LANG_UserCount_Change=Megfigyelk {0:S} to {1:D} LANG_UserCount_Change_Increase=nvels LANG_UserCount_Change_Decrease=cskkents LANG_Source_Disconnecting=Platform kapcsolat lecsatlakozs... LANG_Source_Disconnected=Lecsatlakozva a platform szerverrl. jracsatlakozs 3 msodperc mlva. LANG_Source_Disconnected_Succ=Sikeresen lecsatlakoztl a platform szerverrl. LANG_Source_Connect=Csatlakozs a platform szervehez... LANG_Source_Connected_Succ=Sikeres csatlakozs apaltform szerverhez LANG_Current_Online=Jelenleg elrhet : {0:D} LANG_Gift_Sent=Kldk {O:D} {1:S} neked! LANG_Config=Belltsok: LANG_Config_Status_OK=OK, LiveID:{0} LANG_Config_Status_Fail=Konfigurls sikertelen. LANG_Source=Platform{0:S}: LANG_IRC=Kliens: LANG_Danmaku=zenet kldse: LANG_Status_Connected=Csatlakozva LANG_Status_NotConenct=Ttlen LANG_Loading_Config=Belltsok betltse...\n LANG_Welcome=osu!Live Sync vezi {0:S} LANG_Help=rj 'help' parancsot a sgrt LANG_Command=Parancs LANG_Command_Description=Lers LANG_Plugin_Cycle_Reference=A plugin ciklushoz kttt. {0:S} megfelel gazata nlkl nem fog betlteni. LANG_IRC_Connecting=[Client] csatlakozs... LANG_IRC_Disconnect=Lecsatlakozs az IRC szerverrl... LANG_IRC_Connect_Timeout=Kliens csatlakozs sikertelen, krlek ellenrizd az internet kapcsolatodat. LANG_IRC_Ready=[Client] kszen ll! LANG_MsgMgr_Limit=zenet korltoz BEKAPCSOLVA, az zenet csak ?send eltaggal lesz elkldve IRC-re LANG_MsgMgr_Free=zenet korltoz KIKAPCSOLVA. ================================================ FILE: Language/ja-JP/BanManagerPlugin.DefaultLanguage.lang ================================================ [ja-JP] LANG_HELP_BAN=ban user/id/regex ,bZ[WIRCɑM܂ LANG_HELP_UNBAN=unban user/id/regex LANG_HELP_WHITELIST=add user/id/regex IRCɃbZ[W𑗐MłzCgXgɒlj܂ LANG_HELP_REMOVE_WHITELIST=remove user/id/regex ŃzCgXg폜 LANG_HELP_ACCESS=IRCɃbZ[W𑗐M邽߂̃ANZXxݒ肷 LANG_HELP_LIST=zCgXgEBANXg LANG_ERR_COMMAND=R}hG[ ================================================ FILE: Language/ja-JP/BeatmapSuggest.DefaultLanguage.lang ================================================ [ja-JP] LANG_GET_BEATMAP_FAILED={0} r[g}bv̏̎擾Ɏs܂BG[bZ[W: {1} LANG_SUGGEST_MEG={0} r[g}bvvCꍇ [{1} {2}] || [{3} dl] || [{4} mirror] ?dl ?dl all Ɠ͂Ă LANG_NOT_FOUND_ERR=beatmapSetID̏ƈvȂp[^[IDbeatmapSetIDł LANG_UNKNOWN_TITLE= LANG_GET_BEATMAP_TIME_OUT=r[g}bv擾܂ {0}b^CAEg ^XNXe[^X: {1} LANG_INVAILD_ID={0} ͖IDł LANG_UNKOWN_PARAM={0} p[^[͌‚܂ł LANG_ERROR_INFO_IMCOMPLETE=sSȏ LANG_NO_API_KEY_NOFITY=config.iniosu!APIL[܂Bgp邽߂osu!APIL[ݒ肵Ă (osu!APIL[͂擾ł܂: https://osu.ppy.sh/p/api ) LANG_START_DOWNLOAD=r[g}bṽ_E[hJn܂: {0} LANG_FINISH_DOWNLOAD={0} r[g}bv_E[h܂ LANG_FAILED_DOWNLOAD={0} ̃_E[hɎs܂BG[bZ[W: {1} LANG_DOWNLOAD_TASK_COUNT={0} ‚̃r[g}bṽ_E[hJn܂ ================================================ FILE: Language/ja-JP/ConfigGUI.DefaultLanguage.lang ================================================ [ja-JP] BUTTON_OPEN=J BUTTON_BROWSE=Q BUTTON_FONT=tHg BUTTON_COLOR=F WINDOW_TITLE=ݒ BUTTON_SAVE=ۑ LABEL_SAVED=ۑ܂I LABEL_SAVED_SAVING=ۑ... WINDOW_TITLE_REQUIRE_RESTART=ݒLɂɂ͍ċNĂ ================================================ FILE: Language/ja-JP/DefaultGUI.Language.lang ================================================ [ja-JP] UI_DISPLAY=UI\ UI_TIPS_BOTIRC=BotIRC UI_TIPS_BOTIRC_PASS=IRC pX[h UI_TIPS_STATUS=Xe[^X UI_TIPS_DANMAKU=LiveID UI_TIPS_OSU_IRC=osu!Chat UI_TIPS_TARGET_IRC=IRC UI_BOTTON_START=Jn UI_BOTTON_STOP=~ UI_BOTTON_LOGIN_DANMAKU=\[XɃOC UI_BOTTON_SWITCH_CON=R}hUIɐ؂ւ UI_BOTTON_EXIT=I UI_TIPS_LIVE_ID=LiveID UI_TIPS_LIVE_SOURCE=\[X UI_INFO_RESTART_REQ=SyncċNƃCu\[X̕ύXKp܂B ċN܂H ================================================ FILE: Language/ja-JP/DefaultPlugin.Language.lang ================================================ [ja-JP] LANG_COMMANDS_LOGIN=login <[U[> [pX[h] Ń\[XɃOCăbZ[W𑗐M܂ LANG_COMMANDS_EXIT=Sync~ďI܂ LANG_COMMANDS_CLEAR=\Ă郍OZbg܂ LANG_COMMANDS_STATUS=\[XƃNCAǧ݂̃Xe[^X擾܂ LANG_COMMANDS_STOP=Sync~܂ LANG_COMMANDS_START=SyncJn܂ LANG_COMMANDS_HELP=wv\܂ LANG_COMMANDS_SOURCEMSG=sourcemsg Ń\[XɃeXgbZ[W𑗐M܂ LANG_COMMANDS_CLIENTMSG=clientmsg ŃNCAgɃeXgbZ[W𑗐M܂ LANG_COMMANDS_SOURCES=ׂẴ\[X̃Xg\邩A\[X̐ݒ܂ LANG_COMMANDS_MSGMGR=--help s邱ƂŃwv\܂ LANG_COMMANDS_FILTERS=gp”\̃tB^[Xgɂĕ\܂ LANG_COMMANDS_DISABLE=disable ŃvOCOnDisable()֐Ăяo܂ LANG_COMMANDS_SWITCH_CLIENT=switchclient [NCAg] ŃNCAg؂ւ܂ LANG_COMMANDS_SOURCELOGIN=sourcelogin [[U[] [pX[h] Ń\[XɃOC܂ LANG_COMMANDS_RESTART=SyncċN܂ LANG_COMMANDS_LANG=lang [ꖼ (: ja-JP)] ύX܂ LANG_COMMANDS_LISTLANG=listlang [--all] Ή/ׂĂ̌̃Xg\܂ LANG_COMMANDS_BILIBILI=setbili BiliBili\[XRoomIDݒ肵܂ LANG_COMMANDS_FILTERS_ITEM=ACe LANG_COMMANDS_FILTERS_OBJ=IuWFNg LANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc) OSUIRC BotAJEgݒ肵܂ LANG_COMMANDS_BOTIRC_CURRENT=݂BotIRC: {0:S} LANG_COMMANDS_BOTIRC_SET=BotIRC {0:S} ɐݒ肵܂ LANG_COMMANDS_IRC_CURRENT=݂̃^[QbgIRC: {0:S} LANG_COMMANDS_IRC_SET=^[QbgIRC {0:S} ɕύX܂ LANG_COMMANDS_TARGET_CURRENT=݂LiveID: {0:S} LANG_COMMANDS_TARGET_SET=LiveIDύX܂ LANG_COMMANDS_CLIENT_NAME=NCAg LANG_COMMANDS_CLIENT_AUTHOR= LANG_COMMANDS_SOURCES_NAME=\[X LANG_COMMANDS_SOURCES_AUTHOR= LANG_COMMANDS_CURRENT={0:S} ɐݒ LANG_COMMANDS_DANMAKU_NOT_SUPPORT=݂̃\[Xł͑Mł܂ LANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!IRCɐڑłȂ߃bZ[W𑗐Mł܂ LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=\[XɃOCĂI LANG_COMMANDS_START_ALREADY_RUN=CX^X͂łɎsĂ܂ LANG_COMMANDS_ARGUMENT_WRONG=Ⴂ܂ LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit :ႢxɂƃbZ[W点܂ \n--option <^Cv> : Auto/ForceAll/ForceLimit/DisableAll LANG_COMMANDS_MSGMGR_LIMIT=bZ[W}l[W[͐Ă܂... LANG_COMMANDS_MSGMGR_FREE=bZ[W}l[W[͊JĂ܂... LANG_COMMANDS_MSGMGR_STATUS=bZ[W}l[W[ [h: {4:S}, Xe[^X: {0:D}, L[̐/̐/񕜎: {1} / {2} / {3} LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=x {0} ɕύX LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=̃^Cv {0} ɕύX LANG_COMMANDS_START_NO_SOURCE=JnOɃ\[XIKv܂ LANG_COMMANDS_START_NO_CLIENT=JnOɃNCAgIKv܂ LANG_COMMANDS_CURRENT_LANG=݂̌: {0:S} {1:S} LANG_COMMANDS_LANG_SWITCHED= {1:S}({0:S}) ɐ؂ւ܂ LANG_COMMANDS_LANG_NOT_FOUND=̐؂ւɎs܂I listlang R}hŌݗpł錾̃Xg\ł܂ LANG_SEND_COOKIE_SAVED=OC񂪕ۑ܂ LANG_SEND_DONE=bZ[WM܂ LANG_DOUYU_FAIL=ڑXe[^X̊mFɎs܂I O: {0} : {1} LANG_DOUYU_AUTH_SUCC=DouyutvF؂ɐ LANG_DOUYU_DANMAKU=bZ[WM: {0}:{1} LANG_DOUYU_GIFT=Mtg LANG_BILIBILI_ONLINECHANGE=IC̕ύX: {0} LANG_OSUIRC_LOGIN_SUCCESS=OCɐ LANG_OSUIRC_LOGIN_FAILED=osu!IRC̍ĐڑɎs܂B sR: {0} LANG_OSUIRC_NETWORK_INTERRUPTED=lbg[Nf܂BĐڑĂ LANG_OSUIRC_RECIVER_FINISHED=MXbhI܂ LANG_COMMANDS_CLIENTUSERMSG=chatuser <[U[> IRCeXg̃[U[ŃbZ[W𑗐M܂ LANG_COMMANDS_EXIT_DONE=I܂BEBhE‚Ă ================================================ FILE: Language/ja-JP/NowPlaying.Languages.lang ================================================ [ja-JP] OSU_PATH_NOT_SET=osu!̃pXݒ肳Ă܂Asosu!̃CX^X܂ FIND_OSU_PATH=osu!̃pX‚邱Ƃł܂I {0:S} OSU_PATH_FAIL=osu!̃pX‚邱Ƃł܂Aڍ׏̕\͖ɂȂĂ܂B ERROR_WHILE_FIND_PATH=osu!̃pXɃG[܂ {0:S} UNKNOWN_COMMAND=R}h‚܂ {0:S} STATUS_PLAYING=vC STATUS_EDITING=ҏW STATUS_OTHER=Đ STATUS_TIP_INFO= {0:S}{1:S} STATUS_TIP_INFO_WRAP= {0:S}{1:S} ERROR_WHILE_SEARCH_MAP={0:S}-{1:S} [{2:S}] {3:S} ̌ɃG[܂ CONSOLE_OUTPUT_RESULT=t@C:{0:S} ({1}ms) HP/CS/AR/OD: {2} / {3} / {4} / {5} OUTPUT_RESULT= {0}:{1} CURRENT_IDLE=ݑҋ@ ================================================ FILE: Language/ja-JP/OsuRTDataProvider.DefaultLanguage.lang ================================================ [ja-JP] LANG_OSU_NOT_FOUND=[OsuRTDataProvider][ID:{0}]osu!.exevZX‚܂ł LANG_OSU_FOUND=[OsuRTDataProvider][ID:{0}]osu!.exevZX‚܂ LANG_INIT_PLAY_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]PlayFindeȑɎs܂I {1} bɍĎs܂ LANG_INIT_PLAY_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]PlayFindeȑɐ܂I LANG_INIT_STATUS_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]StatusFindeȑɎs܂I {1} bɍĎs܂ LANG_INIT_STATUS_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]StatusFindeȑɐ܂I LANG_INIT_BEATMAP_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]BeatmapFindeȑɎs܂I {1} bɍĎs܂ LANG_INIT_BEATMAP_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]BeatmapFindeȑɐ܂I LANG_INIT_MODE_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]ModeFindeȑɎs܂I {1} bɍĎs܂ LANG_INIT_MODE_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]ModeFindeȑɐ܂I LANG_BEATMAP_NOT_FOUND=r[g}bv‚܂ ================================================ FILE: Language/ja-JP/RealTimePPDisplayer.DefaultLanguage.lang ================================================ [ja-JP] UI_MENU_TOPMOST=ŏ TEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]PP t@C: {0} MMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer][}bsOt@C: {0} ================================================ FILE: Language/ja-JP/RecentlyUserQuery.DefaultLanguage.lang ================================================ [ja-JP] LANG_HELP=\nosu!IRC̃R}hwvł(osu!IRCł̓R}h̐擪 ? ljĂ)\nrecently --status | ݂̃Xe[^XO擾܂(osu!IRCł͎gps)\nrecently --u <[U[> | <[U[>̗擾܂(osu!IRCł̎gp͔񐄏)\nrecently --i <[U[ID> | <[U[ID>̗擾܂(osu!IRCł̎gp͔񐄏)\nrecently | ŋ߂̃[U[ID擾AIDgp ?ban --i Ȃǂ̃R}hsł܂(osu!IRCɑΉ)\nrecently --disable | bZ[WȌo͂𖳌ɂAׂẴO폜܂(osu!IRCɑΉ)\nrecently --start | ēxȌo͂Jn܂(osu!IRCɑΉ)\nrecently --realloc | O̗eʂĊ蓖ĂăOۑeʂ𑽂܂(osu!IRCɑΉ)\nrecently --recently | ŋ߂̃[U[ID擾܂\n LANG_MSG_STATUS=bZ[WL^̃Xe[^X: {0} | L^/e : {1}/{2} LANG_RUNNING=ғ LANG_STOP=~ LANG_MSG_DISABLE=bZ[WȌo͂ɂȂ肷ׂẴO܂ LANG_MSG_START=bZ[WȌo͂Jn܂ LANG_MSG_REALLOC_ERR=bZ[WL^: ߂ɊԈႢ܂ LANG_MSG_REALLOC={0} ̃bZ[WOɏo͂ł悤ɂȂ܂ LANG_MSG_NOTIMPLENT=Ă܂ LANG_MSG_UNKNOWNCOMMAND=R}h‚܂ ================================================ FILE: Language/ja-JP/Sync.Tools.DefaultI18n.lang ================================================ [ja-JP] LANG_Loading=Ǎ... LANG_Plugins={0:D} ‚̃vOCǂݍ݂܂ LANG_Sources={0:D} ‚̃\[Xǂݍ݂܂ LANG_Client={0:D} ‚̃NCAgǂݍ݂܂ LANG_Error=bp[̏ł܂I vOCmFĂI LANG_Commands={0:D} ‚̃R}hǂݍ݂܂ LANG_Filters={0:D} ‚̃tB^[ǂݍ݂܂ LANG_Ready=I LANG_RqueireLogin=OCĂ LANG_AccountName=[U[: LANG_AccountPw=pX[h: LANG_AccountSave=AJEgۑ܂I LANG_Start=N... LANG_Stopping=~... LANG_Restarting=ċN... LANG_LoadingPlugin={0:S} Ǎ... LANG_LoadPluginErr={0:S} ǂݍ݂ł܂ł ({1:S}) LANG_NotPluginErr={0:S} ͒ʏ̃vOCł͂܂ ({1:S}) LANG_NotConfig=config.ini ҏWAݒĂ LANG_NoSource=\[XɃbp[‚܂łB\[XCXg[Ă LANG_MissSource=ݒ肩\[X‚邱Ƃł܂ł LANG_SetSource=Cu\[X {0:S} ɕύX܂ LANG_SupportSend=OC\[X烁bZ[W𑗂邱Ƃł܂ (R}h: login [[U[] [pX[h]) LANG_CertLength=F؊: {0:D} LANG_CertExist=F؂łɑ݂܂Blogin R}hŏ㏑ĂB LANG_SendNotReady=݂̃\[Xł͒e𑗐M鏀łĂ܂ LANG_UnknowCommand=R}h‚܂Bhelp Ɠ͂ƃR}hXg\邱Ƃł܂ LANG_CommandFail=R}hsɎs܂Bhelp Ɠ͂ƃR}hXg\邱Ƃł܂ LANG_ConfigFile=ݒ LANG_UserCount=[U[̕ύX : {0:D} LANG_UserCount_Change=XyN^[ {0:S} {1:D} ɂȂ܂ LANG_UserCount_Change_Increase= LANG_UserCount_Change_Decrease= LANG_Source_Disconnecting=\[X̐ڑؒfĂ܂... LANG_Source_Disconnected=\[XT[o[ؒf܂B 3bɍĐڑ܂ LANG_Source_Disconnected_Succ=\[XT[o[ؒf܂ LANG_Source_Connect=\[XT[o[ɐڑ... LANG_Source_Connected_Succ=\[XT[o[ɐڑ܂I LANG_Current_Online=݂̃IC : {0:D} LANG_Gift_Sent=Ȃ {O:D} {1:S} 𑗂܂I LANG_Config=ݒ: LANG_Config_Status_OK=OK, LiveID:{0} LANG_Config_Status_Fail=s܂ LANG_Source=\[X {0:S}: LANG_IRC=NCAg: LANG_Danmaku=eM: LANG_Status_Connected=ڑ LANG_Status_NotConenct=ҋ@ LANG_Loading_Config=ݒǍ...\n LANG_Welcome=osu!Live Sync o[W: {0:S} LANG_Help=help Ɠ͂ƃwv\邱Ƃł܂ LANG_Command=R}h LANG_Command_Description= LANG_Plugin_Cycle_Reference=vOC͏zŽQƂ܂B {0:S} ͈ˑc[Ȃƃ[h܂ LANG_IRC_Connecting=[NCAg] ڑ... LANG_IRC_Disconnect=IRCT[o[ؒf... LANG_IRC_Connect_Timeout=NCAg̐ڑɎs܂BC^[lbg̐ڑmFĂ LANG_IRC_Ready=[NCAg] I LANG_MsgMgr_Limit=bZ[WIɐ؂ւ܂B?send vtBbNX܂܂ꂽêIRCɑM܂ LANG_MsgMgr_Free=bZ[WItɐ؂ւ܂B LANG_Instance_Exist=Sync͂łɋNĂ܂ LANG_COMMANDS_LOGIN=login <[U[> [pX[h] Ń\[XɃOCăbZ[W𑗐M܂ LANG_COMMANDS_EXIT=Sync~ďI܂ LANG_COMMANDS_CLEAR=\Ă郍OZbg܂ LANG_COMMANDS_STATUS=\[XƃNCAǧ݂̃Xe[^X擾܂ LANG_COMMANDS_STOP=Sync~܂ LANG_COMMANDS_START=SyncJn܂ LANG_COMMANDS_HELP=wv\܂ LANG_COMMANDS_SOURCEMSG=sourcemsg Ń\[XɃeXgbZ[W𑗐M܂ LANG_COMMANDS_CLIENTMSG=clientmsg ŃNCAgɃeXgbZ[W𑗐M܂ LANG_COMMANDS_SOURCES=ׂẴ\[X̃Xg\邩A\[X̐ݒ܂ LANG_COMMANDS_MSGMGR=--help s邱ƂŃwv\܂ LANG_COMMANDS_FILTERS=gp”\̃tB^[Xgɂĕ\܂ LANG_COMMANDS_DISABLE=disable ŃvOCOnDisable()֐Ăяo܂ LANG_COMMANDS_SWITCH_CLIENT=switchclient [NCAg] ŃNCAg؂ւ܂ LANG_COMMANDS_SOURCELOGIN=sourcelogin [[U[] [pX[h] Ń\[XɃOC܂ LANG_COMMANDS_RESTART=SyncċN܂ LANG_COMMANDS_LANG=lang [ꖼ (: ja-JP)] ύX܂ LANG_COMMANDS_LISTLANG=listlang [--all] Ή/ׂĂ̌̃Xg\܂ LANG_COMMANDS_FILTERS_ITEM=ACe LANG_COMMANDS_FILTERS_OBJ=IuWFNg LANG_COMMANDS_CLIENT_NAME=NCAg LANG_COMMANDS_CLIENT_AUTHOR= LANG_COMMANDS_SOURCES_NAME=\[X LANG_COMMANDS_SOURCES_AUTHOR= LANG_COMMANDS_CURRENT={0:S} ɐݒ LANG_COMMANDS_DANMAKU_NOT_SUPPORT=݂̃\[Xł͑Mł܂ LANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!IRCɐڑłȂ߃bZ[W𑗐Mł܂ LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=\[XɃOCĂI LANG_COMMANDS_START_ALREADY_RUN=CX^X͂łɎsĂ܂ LANG_COMMANDS_ARGUMENT_WRONG=Ⴂ܂ LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit :ႢxɂƃbZ[W点܂ \n--option <^Cv> : Auto/ForceAll/ForceLimit/DisableAll LANG_COMMANDS_MSGMGR_LIMIT=bZ[W}l[W[͐Ă܂... LANG_COMMANDS_MSGMGR_FREE=bZ[W}l[W[͊JĂ܂... LANG_COMMANDS_MSGMGR_STATUS=bZ[W}l[W[ [h: {4:S}, Xe[^X: {0:D}, L[̐/̐/񕜎: {1} / {2} / {3} LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=x {0} ɕύX LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=̃^Cv {0} ɕύX LANG_COMMANDS_START_NO_SOURCE=JnOɃ\[XIKv܂ LANG_COMMANDS_START_NO_CLIENT=JnOɃNCAgIKv܂ LANG_COMMANDS_CURRENT_LANG=݂̌: {0:S} {1:S} LANG_COMMANDS_LANG_SWITCHED= {1:S}({0:S}) ɐ؂ւ܂ LANG_COMMANDS_LANG_NOT_FOUND=̐؂ւɎs܂I listlang R}hŌݗpł錾̃Xg\ł܂ LANG_UPDATE_DONE=Abvf[g܂BċNƃAbvf[gK܂ LANG_INSTALL_DONE=CXg[܂BċNƃCXg[K܂ LANG_PLUGIN_NOT_FOUND={0} vOC݂܂ LANG_REMOVE_DONE=폜܂BKɂ͍ċNĂ LANG_VERSION_LATEST={0} ͍ŐVł LANG_UPDATE_CHECK_ERROR=[{0} ̃o[W̍XVmFł܂] : {1} : {2} LANG_UPDATE_ERROR=XVł܂ : {0} : {1} LANG_SOURCE_NOT_SUPPORT_SEND={0} \[X͂܂bZ[WMɑΉĂ܂ LANG_NO_PLUGIN_SELECT=vOCw肷Kv܂ LANG_PLUGIN_DISABLED= LANG_COMMANDS_CLIENTUSERMSG=chatuser <[U[> IRCeXg̃[U[ŃbZ[W𑗐M܂ LANG_COMMANDS_EXIT_DONE=I܂BEBhE‚Ă LANG_NO_ANY_SOURCE=ugp”\ȃvOC܂BvOCtH_[mF邩Aplugins install DefaultPlugin Ɠ͂ăftHg̃vOCCXg[ĂBv ================================================ FILE: Language/ru-RU/BanManagerPlugin.DefaultLanguage.lang ================================================ [ru-RU] LANG_HELP_BAN= /id/regex , irc LANG_HELP_UNBAN= /id/regex LANG_HELP_WHITELIST= /id/regex , irc LANG_HELP_REMOVE_WHITELIST= /id/regex LANG_HELP_ACCESS= irc LANG_HELP_LIST= LANG_ERR_COMMAND= ================================================ FILE: Language/ru-RU/BeatmapSuggest.DefaultLanguage.lang ================================================ [ru-RU] LANG_GET_BEATMAP_FAILED= {0} ,: {1} LANG_SUGGEST_MEG={0} , [{1} {2}] || [{3} dl] || [{4} mirror] "?dl"/"?dl all" LANG_NOT_FOUND_ERR= ID - beatmapSetID LANG_UNKNOWN_TITLE= LANG_GET_BEATMAP_TIME_OUT= {0} ,TaskStatus{1} LANG_INVAILD_ID= id {0} LANG_UNKOWN_PARAM= {0} LANG_ERROR_INFO_IMCOMPLETE= LANG_NO_API_KEY_NOFITY= ApiKey config.ini, , osu!api_key . ApiKey:https://osu.ppy.sh/p/api LANG_START_DOWNLOAD= : {0} LANG_FINISH_DOWNLOAD= {0} LANG_FAILED_DOWNLOAD= {0} ,: {1} LANG_DOWNLOAD_TASK_COUNT= {0} . ================================================ FILE: Language/ru-RU/ConfigGUI.DefaultLanguage.lang ================================================ [ru-RU] BUTTON_OPEN= BUTTON_BROWSE= BUTTON_FONT= BUTTON_COLOR= WINDOW_TITLE= WINDOW_TITLE_REQUIRE_RESTART= BUTTON_SAVE= LABEL_SAVED=! LABEL_SAVED_SAVING=... COMMAND_LINE_HINT=[ConfigGUI] "config" . TRAY_HIDE_SHOW=/ TRAY_CONFIG= TRAY_OPEN_SYNC_FOLDER= Sync TRAY_EXIT= ================================================ FILE: Language/ru-RU/DefaultGUI.Language.lang ================================================ [ru-RU] UI_DISPLAY= UI UI_TIPS_BOTIRC=BotIRC UI_TIPS_BOTIRC_PASS= IRC UI_TIPS_STATUS= UI_TIPS_DANMAKU=LiveID UI_TIPS_OSU_IRC=osu!Chat UI_TIPS_TARGET_IRC=ToIRC UI_BOTTON_START= UI_BOTTON_STOP= UI_BOTTON_LOGIN_DANMAKU= UI_BOTTON_SWITCH_CON= CMD UI UI_BOTTON_EXIT= UI_TIPS_LIVE_ID=LiveID UI_TIPS_LIVE_SOURCE= UI_INFO_RESTART_REQ= Sync. ? ================================================ FILE: Language/ru-RU/DefaultPlugin.Language.lang ================================================ [ru-RU] LANG_COMMANDS_LOGIN=login <> [] LANG_COMMANDS_EXIT= LANG_COMMANDS_CLEAR= LANG_COMMANDS_STATUS= LANG_COMMANDS_STOP= LANG_COMMANDS_START= Sync LANG_COMMANDS_HELP= LANG_COMMANDS_SOURCEMSG=sourcemsg LANG_COMMANDS_CLIENTMSG=clientmsg LANG_COMMANDS_SOURCES= LANG_COMMANDS_MSGMGR= --help, LANG_COMMANDS_FILTERS= LANG_COMMANDS_DISABLE=disabple < > OnDisable() LANG_COMMANDS_SWITCH_CLIENT=switchclient [] LANG_COMMANDS_SOURCELOGIN=sourcelogin [] [] LANG_COMMANDS_RESTART= LANG_COMMANDS_LANG=lang [cultureName] / LANG_COMMANDS_LISTLANG=listlang [--all] (/) LANG_COMMANDS_BILIBILI=setbili Bilibili RoomID LANG_COMMANDS_FILTERS_ITEM= LANG_COMMANDS_FILTERS_OBJ= LANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc) OSUIRCBot LANG_COMMANDS_BOTIRC_CURRENT= BotIRC: {0:S} LANG_COMMANDS_BOTIRC_SET= BotIRC {0:S} LANG_COMMANDS_IRC_CURRENT= Target IRC: {0:S} LANG_COMMANDS_IRC_SET= Target IRC {0:S} LANG_COMMANDS_TARGET_CURRENT= LiveID: {0:S} LANG_COMMANDS_TARGET_SET= LiveID {0:S} LANG_COMMANDS_CLIENT_NAME= LANG_COMMANDS_CLIENT_AUTHOR= LANG_COMMANDS_SOURCES_NAME= LANG_COMMANDS_SOURCES_AUTHOR= LANG_COMMANDS_CURRENT= {0:S} LANG_COMMANDS_DANMAKU_NOT_SUPPORT= Sendable LANG_COMMANDS_EXIT_DONE= , , . LANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!irc , . LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN= ! LANG_COMMANDS_START_ALREADY_RUN= LANG_COMMANDS_ARGUMENT_WRONG= . LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit <> : - \n--option <> :auto/force_all/force_limit LANG_COMMANDS_MSGMGR_LIMIT=... LANG_COMMANDS_MSGMGR_FREE= LANG_COMMANDS_MSGMGR_STATUS= MessageManager:{4:S},:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3} LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET= {0} LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET= {0} LANG_SEND_COOKIE_SAVED= LANG_SEND_DONE= . LANG_DOUYU_FAIL= ! {0}, {1} LANG_DOUYU_AUTH_SUCC= Douyutv LANG_DOUYU_DANMAKU= : {0}: {1} LANG_DOUYU_GIFT= LANG_BILIBILI_ONLINECHANGE= : {0} LANG_COMMANDS_CLIENTUSERMSG=chatuser IRC LANG_OSUIRC_LOGIN_SUCCESS= . LANG_OSUIRC_LOGIN_FAILED= osu!irc , : {0} LANG_OSUIRC_NETWORK_INTERRUPTED= , . LANG_OSUIRC_RECIVER_FINISHED=Reciver thread is finished. ================================================ FILE: Language/ru-RU/NowPlaying.Languages.lang ================================================ [ru-RU] OSU_PATH_NOT_SET= osu! , osu!. FIND_OSU_PATH= osu! {0:S} OSU_PATH_FAIL= osu!, . ERROR_WHILE_FIND_PATH= osu! {0:S} UNKNOWN_COMMAND= {0:S} STATUS_PLAYING= STATUS_EDITING= STATUS_OTHER= STATUS_TIP_INFO= {0:S} {1:S} STATUS_TIP_INFO_WRAP= {0:S} {1:S} ERROR_WHILE_SEARCH_MAP= {0:S}-{1:S} [{2:S}] {3:S} CONSOLE_OUTPUT_RESULT=:{0:S} ({1}ms) HP/CS/AR/OD: {2}{3}{4}{5} OUTPUT_RESULT= {0}:{1} CURRENT_IDLE= ================================================ FILE: Language/ru-RU/OsuRTDataProvider.DefaultLanguage.lang ================================================ [ru-RU] LANG_OSU_NOT_FOUND=[ID:{0}] osu!.exe LANG_OSU_FOUND=[ID:{0}] osu!.exe LANG_INIT_PLAY_FINDER_FAILED=[ID:{0}] PlayerFinder ! {1} LANG_INIT_PLAY_FINDER_SUCCESS=[ID:{0}] PlayerFinder ! LANG_INIT_STATUS_FINDER_FAILED=[ID:{0}] StatusFinder ! {1} LANG_INIT_STATUS_FINDER_SUCCESS=[ID:{0}] StatusFinder ! LANG_INIT_BEATMAP_FINDER_FAILED=[ID:{0}] BeatmapFinder ! {1} LANG_INIT_BEATMAP_FINDER_SUCCESS=[ID:{0}] BeatmapFinder ! LANG_INIT_MODE_FINDER_FAILED=[ID:{0}] ModeFinder ! {1} LANG_INIT_MODE_FINDER_SUCCESS=[ID:{0}] ModeFinder ! LANG_BEATMAP_NOT_FOUND= ListenInterval= (ms) EnableTourneyMode= TeamSize= DebugMode= ForceOsuSongsDirectory= osu! LANG_TOURNEY_HINT= : {0} CHECK_GOTO_RELEASE_PAGE_HINT= "ortdp releases", LANG_CHECK_ORTDP_UPDATE= OsuRTDataProvider({0}) GameMode= DisableProcessNotFoundInformation= osu!, EnableModsChangedAtListening= , () ================================================ FILE: Language/ru-RU/RealTimePPDisplayer.DefaultLanguage.lang ================================================ [ru-RU] UI_MENU_TOPMOST= TEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer] pp: {0} MMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer] Memory Mapping: {0} FPS=FPS TextOutputPath= DisplayHitObject= FontName= PPFontSize= pp PPFontColor= pp HitCountFontSize= HitCountFontColor= BackgroundColor= WindowHeight= WindowWidth= SmoothTime=Smooth Time Topmost= WindowTextShadow=WindowTextShadow OutputMethods= DebugMode= PPFormat= pp HitCountFormat= RoundDigits= IgnoreTouchScreenDecrease= UseUnicodePerformanceInformation= CHECK_RTPPD_UPDATE=[RealTimePPDisplayer] RealTimePPDisplayer({0}) CHECK_OPPAI_UPDATE=[RealTimePPDisplayer] oppai-ng ({0}) CHECK_GOTO_RELEASE_PAGE_HINT=[RealTimePPDisplayer] "rtpp releases", . UI_UPDATE_CONFIGGUI_MESSAGEBOX=, ConfigGUI 0.2.1 . HINT_BEATMAP_NO_FOUND=[RealTimePPDisplayer] ! Songs. ( OsuRTDataProvider, ). HINT_CANNOT_WATCH_OTHER_PLAYER=[RealTimePPDisplayer] . ( , "By CuteSync Proxy" api ). HINT_POBT_VERSION_LOWER=[RealTimePPDisplayer] PublicOsuBotTransfer , 1.3.0 "By CuteSync Proxy". MBX_POBT_VERSION_NO_INSTALLED= PublicOsuBotTransfer . UI_OPENEDITOR_BUTTON_CONTENT= UI_MULTIOUTPUTEDITOR_BUTTON_CONTENT= UI_NAME_LABEL=: UI_FORMAT_LABEL=: UI_DELETETHIS_BUTTON_CONTENT= UI_SMOOTH_CHECKBOX_CONTENT= UI_EDITFORMAT_BUTTON_CONTENT= UI_SMMOTH_CHECKBOX_TOOLTIP= UI_TYPE_COMBOBOX_TOOLTIP= UI_FORMATTER_COMBOBOX_TOOLTIP= UI_MODES_MULTISELECTCOMBOBOX_TOOLTIP= RankingSendPerformanceToChat= pp ByCuteSyncProxy=By CuteSync Proxy ApiKey=Osu! Api Formatter= ================================================ FILE: Language/ru-RU/RecentlyUserQuery.DefaultLanguage.lang ================================================ [ru-RU] LANG_HELP=\n osu!irc ( osu!irc , , "?" )\nrecently --status | ( osu!irc)\nrecently --u | ( osu!irc)\nrecently --i | ( osu!irc)\nrecently | ID, ID , "?ban --i" ( osu!irc)\nrecently --disable | , ( osu!irc)\nrecently --start | ( osu!irc)\nrecently --realloc | , ( osu!irc)\nrecently --recently | id \n LANG_MSG_STATUS= MessageRecord: {0} | recordCount/ : {1}/{2} LANG_RUNNING= LANG_STOP= LANG_MSG_DISABLE= LANG_MSG_START= LANG_MSG_REALLOC_ERR=MessageRecord: LANG_MSG_REALLOC= {0} LANG_MSG_NOTIMPLENT= LANG_MSG_UNKNOWNCOMMAND= . ================================================ FILE: Language/ru-RU/Sync.Tools.DefaultI18n.lang ================================================ [ru-RU] LANG_Loading=... LANG_Plugins= {0:D} (-) LANG_Sources= {0:D} (-) LANG_Client= {0:D} (-) LANG_Error=CAN NOT INITIAL WARPPERS! . LANG_Commands= {0:D} (-) LANG_Filters= {0:D} (-) LANG_Ready= LANG_RqueireLogin=, LANG_AccountName= : LANG_AccountPw=: LANG_AccountSave= ! LANG_Start=... LANG_Stopping=... LANG_Restarting=... LANG_LoadingPlugin= {0:S} ... LANG_LoadPluginErr= {0:S} ({1:S}) LANG_NotPluginErr={0:S} ({1:S}) LANG_NotConfig=, 'config.ini' . LANG_NoSource= wrapper. , . LANG_MissSource= - . LANG_SetSource= {0:S} live- LANG_SupportSend= (: login [] []) LANG_CertLength= : {0:D} LANG_CertExist= , LANG_SendNotReady= danmaku. LANG_UnknowCommand= , 'help' . LANG_CommandFail= , 'help' . LANG_ConfigFile= LANG_UserCount= : {0:D} LANG_UserCount_Change= {0:S} {1:D} LANG_UserCount_Change_Increase= LANG_UserCount_Change_Decrease= LANG_Source_Disconnecting= ... LANG_Source_Disconnected= . 3 . LANG_Source_Disconnected_Succ= . LANG_Source_Connect= ... LANG_Source_Connected_Succ= ! LANG_Current_Online= : {0:D} LANG_Gift_Sent= {0:D} {1:S} LANG_Config=: LANG_Config_Status_OK=OK, LiveID:{0} LANG_Config_Status_Fail= . LANG_Source={0:S}: LANG_IRC=: LANG_Danmaku=Danmaku : LANG_Status_Connected= LANG_Status_NotConenct= LANG_Loading_Config= ...\n LANG_Welcome=osu!Live Sync {0:S} LANG_Help= 'help' LANG_Command= LANG_Command_Description= LANG_Plugin_Cycle_Reference= . {0:S} . LANG_IRC_Connecting=[] ... LANG_IRC_Disconnect= IRC... LANG_IRC_Connect_Timeout= , . LANG_IRC_Ready=[] ! LANG_MsgMgr_Limit= , irc danmaku ?send LANG_MsgMgr_Free= . LANG_Instance_Exist=Sync . LANG_COMMANDS_LOGIN=login <> [] LANG_COMMANDS_EXIT= Sync LANG_COMMANDS_CLEAR= LANG_COMMANDS_STATUS= LANG_COMMANDS_STOP= Sync LANG_COMMANDS_START= Sync LANG_COMMANDS_HELP= LANG_COMMANDS_SOURCEMSG=sourcemsg LANG_COMMANDS_CLIENTMSG=clientmsg LANG_COMMANDS_SOURCES= LANG_COMMANDS_MSGMGR= --help, LANG_COMMANDS_FILTERS= LANG_COMMANDS_DISABLE=disable < > OnDisable() LANG_COMMANDS_SWITCH_CLIENT=switchclient [] LANG_COMMANDS_SOURCELOGIN=sourcelogin [] [] LANG_COMMANDS_RESTART= LANG_COMMANDS_LANG=lang [cultureName] / LANG_COMMANDS_LISTLANG=listlang [--all] (/) LANG_COMMANDS_FILTERS_ITEM= LANG_COMMANDS_FILTERS_OBJ= LANG_COMMANDS_CLIENT_NAME= LANG_COMMANDS_CLIENT_AUTHOR= LANG_COMMANDS_SOURCES_NAME= LANG_COMMANDS_SOURCES_AUTHOR= LANG_COMMANDS_CURRENT= {0:S} LANG_COMMANDS_DANMAKU_NOT_SUPPORT= Sendable LANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!irc , . LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN= ! LANG_COMMANDS_START_ALREADY_RUN= LANG_COMMANDS_ARGUMENT_WRONG= . LANG_COMMANDS_MSGMGR_HELP=\n--status \n--limit <> : - \n--option <> :auto/force_all/force_limit LANG_COMMANDS_MSGMGR_LIMIT=MessageManager ... LANG_COMMANDS_MSGMGR_FREE=MessageManager ... LANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3} LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET= {0} LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET= {0} LANG_COMMANDS_START_NO_SOURCE= . LANG_COMMANDS_START_NO_CLIENT= . LANG_COMMANDS_CURRENT_LANG= : {0:S} {1:S} LANG_COMMANDS_LANG_SWITCHED= {1:S}({0:S}) LANG_COMMANDS_LANG_NOT_FOUND= ! 'listlang' LANG_UPDATE_DONE= . , . LANG_INSTALL_DONE= . , . LANG_PLUGIN_NOT_FOUND= {0} LANG_REMOVE_DONE= . . LANG_VERSION_LATEST={0} LANG_UPDATE_CHECK_ERROR= [{0}] : {1} : {2} LANG_UPDATE_ERROR= : {0} : {1} LANG_SOURCE_NOT_SUPPORT_SEND= {0} . LANG_NO_PLUGIN_SELECT= LANG_PLUGIN_DISABLED= LANG_COMMANDS_CLIENTUSERMSG=chatuser IRC LANG_COMMANDS_EXIT_DONE= , , . LANG_NO_ANY_SOURCE=" , 'plugins install DefaultPlugin', ". ================================================ FILE: Language/zh-CN/BanManagerPlugin.DefaultLanguage.lang ================================================ [zh-CN] LANG_HELP_BAN=ֹijuser/id/regexϢirc LANG_HELP_UNBAN=ֹijuser/id/regex LANG_HELP_WHITELIST=ijuser/id/regex˽һֱȨ޷Ϣirc LANG_HELP_REMOVE_WHITELIST=ijuser/id/regexӰƳ LANG_HELP_ACCESS=÷ϢircȨ LANG_HELP_LIST=ȡֹ߽û͹ LANG_ERR_COMMAND=ָ ================================================ FILE: Language/zh-CN/DefaultGUI.Language.lang ================================================ [zh-CN] UI_DISPLAY=ʾUI UI_TIPS_BOTIRC=BotIRC UI_TIPS_BOTIRC_PASS=IRC UI_TIPS_STATUS=״̬ UI_TIPS_DANMAKU=ֱĻ UI_TIPS_OSU_IRC=osu! UI_TIPS_TARGET_IRC=ĿIRC UI_BOTTON_START=ʼ UI_BOTTON_STOP=ֹͣ UI_BOTTON_LOGIN_DANMAKU=¼ֱ UI_BOTTON_SWITCH_CON=л̨ UI_BOTTON_EXIT=˳ UI_TIPS_LIVE_ID=ֱ UI_TIPS_LIVE_SOURCE=ֱԴ UI_INFO_RESTART_REQ=ıֱԴ֮Ч ================================================ FILE: Language/zh-CN/DefaultPlugin.Language.lang ================================================ [zh-CN] LANG_COMMANDS_LOGIN=login [pass] ¼Ŀ굯ĻվĻ͹ LANG_COMMANDS_EXIT=˳ LANG_COMMANDS_CLEAR=Ļ LANG_COMMANDS_STATUS=õǰ״̬ LANG_COMMANDS_STOP=ֹͣǰ LANG_COMMANDS_START=ʼͬ LANG_COMMANDS_HELP=ӡϢ LANG_COMMANDS_SOURCEMSG=danmaku ͵Ļ LANG_COMMANDS_CLIENTMSG=chat IRCϢ LANG_COMMANDS_SOURCES=õǰеĻԴб LANG_COMMANDS_MSGMGR=鿴Ϣ,--helpȡ LANG_COMMANDS_FILTERS=беǰϢ LANG_COMMANDS_DISABLE=ͽϢ disable () LANG_COMMANDS_SWITCH_CLIENT=лָClientʵΪȡClientб LANG_COMMANDS_SOURCELOGIN=¼ĻԴ sourcelogin [û] [] LANG_COMMANDS_RESTART=Ӧó LANG_COMMANDS_LANG=lang [cultureName] Get/Set language LANG_COMMANDS_LISTLANG=listlang [--all] List (supported/all) languages LANG_COMMANDS_BILIBILI=setbili roomID BilibiliֱԴķ LANG_COMMANDS_FILTERS_ITEM= LANG_COMMANDS_FILTERS_OBJ= LANG_COMMANDS_SET_OSU_BOT=osuĻ˺ setosubot (˺) (IRC) (ĿIRC) LANG_COMMANDS_BOTIRC_CURRENT=ǰBotIRC: {0:S} LANG_COMMANDS_BOTIRC_SET=ǰBotIRCΪ {0:S} LANG_COMMANDS_IRC_CURRENT=ǰĿIRC: {0:S} LANG_COMMANDS_IRC_SET=ǰĿIRCΪ LANG_COMMANDS_TARGET_CURRENT=ǰֱID: {0:S} LANG_COMMANDS_TARGET_SET=ǰֱIDΪ LANG_COMMANDS_CLIENT_NAME=Client LANG_COMMANDS_CLIENT_AUTHOR= LANG_COMMANDS_SOURCES_NAME=ĻԴ LANG_COMMANDS_SOURCES_AUTHOR= LANG_COMMANDS_CURRENT=ǰΪ {0:S} LANG_COMMANDS_DANMAKU_NOT_SUPPORT=ʾǰĻԴַ֧͵ĻĻԴ\n LANG_COMMANDS_EXIT_DONE=˳ɣڻδرգǿƹرա LANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu! irc δӣܷϢ LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=¼ܷ͵Ļ! LANG_COMMANDS_START_ALREADY_RUN=ͬʵѾС LANG_COMMANDS_ARGUMENT_WRONG=ȷ LANG_COMMANDS_MSGMGR_HELP=\n--status :鿴ǰϢϢ\n--limit <ֵ> :ƷϢĵȼԽ;Խ״ܿ\n--option <> :ùܿصķʽautoԶܿأforce_allǿȫ,force_limitǽʹ?sendϢ LANG_COMMANDS_MSGMGR_LIMIT=... LANG_COMMANDS_MSGMGR_FREE= LANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3} LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=ƷٶȵȼΪ{0} LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=ϢĹƷʽΪ{0} LANG_SEND_COOKIE_SAVED=½Ϣɹ LANG_SEND_DONE= LANG_DOUYU_FAIL=״̬ʧ! ڴ{0}, {1} LANG_DOUYU_AUTH_SUCC=֤ɹ LANG_DOUYU_DANMAKU=յĻ: {0}:{1} LANG_DOUYU_GIFT= LANG_BILIBILI_ONLINECHANGE=ֱ仯 {0} ================================================ FILE: Language/zh-CN/NowPlaying.Languages.lang ================================================ [zh-CN] OSU_PATH_NOT_SET=δosuļ·ѰǰеosuԶ FIND_OSU_PATH=ҵosu·! {0:S} OSU_PATH_FAIL=δosuļ·Ҳûеosu޷ʹô˲߼ܣú·osuSyncܼʹ ERROR_WHILE_FIND_PATH=ϸϢʧܣ {0:S} UNKNOWN_COMMAND=Ч{0:S} STATUS_PLAYING= STATUS_EDITING= STATUS_OTHER= STATUS_TIP_INFO={0:S}{1:S} STATUS_TIP_INFO_WRAP={0:S}{1:S} ERROR_WHILE_SEARCH_MAP=Լ{0:S}-{1:S} [{2:S}]ʱʧܣ{3:S} CONSOLE_OUTPUT_RESULT=ļ{0:S} (ʱ: {1}) HP/CS/AR/OD: {2}{3}{4}{5} OUTPUT_RESULT=ǰ{0}:{1} CURRENT_IDLE=ǰûڴͼ ================================================ FILE: Language/zh-CN/RecentlyUserQuery.DefaultLanguage.lang ================================================ [zh-CN] LANG_HELP=\nָcmd˺osu!irc˹ͨ(osu!ircڿͷ"?")\nrecently --status |ȡǰϢ¼״̬Ϣ(osu!irc)\nrecently --u |ȡûʷϢ(osu!irc)\nrecently --i |ȡûʷϢ(osu!irc)\nrecently |ȡûֺid,idִ"?ban --i"ָ(osu!irc)\nrecently --disable |رռ¼йܲ(osu!irc)\nrecently --start |¿ʼ¼(osu!irc)\nrecently --realloc |·¼¼(osu!irc)\nrecently --recently |ȡûid\n LANG_MSG_STATUS=MessageRecord status: {0} | recordCount/Capacity : {1}/{2} LANG_RUNNING=running LANG_STOP=stopped LANG_MSG_DISABLE=Ϣ¼ѽã LANG_MSG_START=Ϣ¼ LANG_MSG_REALLOC_ERR=MessageRecord: ָ LANG_MSG_REALLOC=Ϣ¼ڿɼ¼ {0} ʷ¼ LANG_MSG_NOTIMPLENT=~ LANG_MSG_UNKNOWNCOMMAND=δ֪ ================================================ FILE: Language/zh-CN/Sync.Tools.DefaultI18n.lang ================================================ [zh-CN] LANG_Loading=ȡ.... LANG_Plugins= {0:D} LANG_Sources= {0:D} ֱԴ LANG_Client= {0:D} Client LANG_Error=ܳʼȷǷѾװֱԴ. LANG_Commands= {0:D} LANG_Filters= {0:D} LANG_Ready=׼ LANG_RqueireLogin=¼RnW˺ LANG_AccountName=û: LANG_AccountPw=: LANG_AccountSave=˻ɹ! ʼӵ LANG_Start=ʼ.... LANG_Stopping=ֹͣ... LANG_Restarting=¿ʼ... LANG_LoadingPlugin= {0:S} ... LANG_LoadPluginErr= {0:S} ({1:S}) LANG_NotPluginErr={0:S} osuSync ({1:S}) LANG_NotConfig= 'config.ini' ٿʼͬ LANG_NoSource=޷ҵκֱԴ밲װһֱԴ LANG_MissSource=ҲĬƥֱԴֱʹõһ LANG_SetSource= {0:S} ΪֱĻԴ LANG_SupportSend=ʾ:ǰĻԴ֧Ϸڷ͵ĻԴĹܣlogin [û] [] ¼!(û߿ѡ) LANG_CertLength=Certification: {0:D} LANG_CertExist=ʾǰе¼Certification¼踲ǣlogin [û] []иǣûѡ룩 LANG_SendNotReady=ǰClientδ־ĻͿã볢ʹLogin¼ LANG_UnknowCommand=δ֪ help鿴б LANG_CommandFail=ִʧܣ help鿴б LANG_ConfigFile=ļ LANG_UserCount=û: {0:D} LANG_UserCount_Change=ֱΧ{0:S}{1:D} LANG_UserCount_Change_Increase= LANG_UserCount_Change_Decrease= LANG_Source_Disconnecting=ڶϿĻԴ.... LANG_Source_Disconnected=ӱϿ3 LANG_Source_Disconnected_Succ=ԴϿӳɹ LANG_Source_Connect=ӵĻԴ.... LANG_Source_Connected_Succ=Դӳɹ LANG_Current_Online=ǰ: {0:D} LANG_Gift_Sent=͸{O:D}{1:S}! LANG_Config=ļ: LANG_Config_Status_OK=OK, ID:{0} LANG_Config_Status_Fail=δóɹ LANG_Source=Դ{0:S}: LANG_IRC=Client: LANG_Danmaku=Ļ: LANG_Status_Connected= LANG_Status_NotConenct=δ LANG_Loading_Config=ڶȡļ....\n LANG_Welcome=ӭʹ osuֱĻͬ ver {0:S} LANG_Help= 'help' ðб\n\n LANG_Command= LANG_Command_Description= LANG_MsgMgr_Limit=ǰϢ ʼ ƣֻ?sendݲŻᷢ͵ircƵ LANG_MsgMgr_Free=ǰϢ ,ݿֱӷ͵ircƵ LANG_Plugin_Cycle_Reference=ֲ֮ѭùϵ {0:S} ᰴտָϵм ================================================ FILE: README.md ================================================ # Sync ![SyncIcon](Sync/Resources/SyncIcon.ico) ## [Download Releases](https://github.com/OsuSync/Sync/releases) , [UserGuide](https://github.com/Deliay/Sync/wiki/UserReadMe_en) , [Official Plugins](https://github.com/Deliay/SyncPlugin) [QQ群](https://jq.qq.com/?_wv=1027&k=5y2CVZ6) / [Discord](https://discord.gg/KG86sWS) ---------------------------------- Sync your live chat to osu!~ [MIT License](LICENSE) ================================================ FILE: Sync/Client/ClientEvent.cs ================================================ using Sync.MessageFilter; using Sync.Plugins; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Client { /// /// Singleton Client event dispathcher /// public class ClientEvents : BaseEventDispatcher { public readonly static ClientEvents Instance = new ClientEvents(); private ClientEvents() { EventDispatcher.Instance.RegisterNewDispatcher(GetType()); } } /// /// Base client event interface flag /// public interface IClientEvent : IBaseEvent { } /// /// Fire when client start work (fire time decide by client) /// public struct ClientStartWorkEvent : IClientEvent { public ClientWorkWrapper Client { get => SyncHost.Instance.ClientWrapper; } } /// /// Fire when client stop work /// public struct ClientStopWorkEvent : IClientEvent { } /// /// Fire when Client recive a IRCMessage /// public struct ClientOnMessageEvent : IClientEvent { public IRCMessage Message { get; } public ClientOnMessageEvent(IRCMessage message) { Message = message; } } } ================================================ FILE: Sync/Client/ClientManager.cs ================================================ using Sync.MessageFilter; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Client { /// /// The manager for Clients /// public class ClientManager { private LinkedList clients; public IReadOnlyList Clients { get => clients.ToList(); } public int Count { get => clients.Count; } public static readonly ClientManager Instance = new ClientManager(); private ClientManager() { clients = new LinkedList(); } public void AddAllClient(params DefaultClient[] clients) { foreach (var client in clients) { if (!this.clients.Contains(client)) { this.clients.AddLast(client); } } } public bool AddClient(DefaultClient client) { if(clients.Contains(client)) { return false; } else { clients.AddLast(client); return true; } } } } ================================================ FILE: Sync/Client/ClientWorkWrapper.cs ================================================ using Sync.Tools; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Client { /// /// A wrapper for Client instance switch and dispatch /// public class ClientWorkWrapper { public DefaultClient Client { get; internal set; } private ClientManager clients; public ClientWorkWrapper(ClientManager manager) { clients = manager; ResetClient(); } /// /// Call when reload client form confiuration file /// public void ResetClient() { DefaultClient client = clients.Clients.FirstOrDefault(p => p.ClientName == DefaultConfiguration.Instance.Client); if (Client != null && Client != client) { Client.SwitchOtherClient(); } if (client == null) { if (clients.Count == 0) return; client = clients.Clients.First(); } Client = client; Client.SwitchThisClient(); } } } ================================================ FILE: Sync/Client/DefaultReciveClient.cs ================================================ using Sync.MessageFilter; using Sync.Plugins; using Sync.Source; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Client { public abstract class DefaultClient { //Author and Client Name public string Author { get; } public string ClientName { get; } public BaseEventDispatcher EventBus { get => ClientEvents.Instance; } public string NickName { get; protected set; } /// /// Invoke while user switch to other client instance /// public abstract void SwitchOtherClient(); /// /// Invoke while user switch to this instance /// public abstract void SwitchThisClient(); public DefaultClient(string Author, string Name) { this.Author = Author; this.ClientName = Name; } /// /// Raise event and pass messages /// /// Bypass message protected void EnqueueMessage(IRCMessage msg) { EventBus.RaiseEvent(new ClientOnMessageEvent(msg)); } public abstract void StartWork(); public abstract void StopWork(); public abstract void Restart(); public SourceStatus CurrentStatus { get; protected set; } /// /// SendMessage to Client /// WARNING: SEND MESSAGE DIRECTLY WHIT THIS WILL NOT PASS BY FILTERS! /// /// public abstract void SendMessage(IMessageBase message); } } ================================================ FILE: Sync/Command/CommandDispatch.cs ================================================ using System; using System.Collections.Generic; namespace Sync.Command { /// /// Delegate for one Command /// /// Command args /// public delegate bool CommandDelegate(Arguments arg); /// /// A typedef for arguments list /// public class Arguments : List { public Arguments() { } public Arguments(params string[] args) { AddRange(args); } public static implicit operator Arguments(string[] args) { return new Arguments(args); } } /// /// A Hashmap command dispatcher /// public class CommandDispatch { private Dictionary cmdList = new Dictionary(); private Dictionary cmdDest = new Dictionary(); public int count { get { return cmdList.Count; } } /// /// Bind command to string /// /// Invoke name /// Functor /// Description /// success, fail public bool bind(string name, CommandDelegate func, string desc) { if (cmdList.ContainsKey(name)) return false; cmdList.Add(name, func); cmdDest.Add(name, desc); return true; } public CommandDelegate get(string name) { if (cmdList.ContainsKey(name)) return cmdList[name]; else return null; } public IDictionary getCommandsHelp() { return cmdDest; } public bool invoke(string name, Arguments args) { try { if (cmdList.ContainsKey(name)) return cmdList[name](args); else return false; } catch(Exception e) { Tools.IO.CurrentIO.Write(e.Message); Tools.IO.CurrentIO.Write(e.StackTrace); Tools.IO.CurrentIO.Write(e.Source); return false; } } } } ================================================ FILE: Sync/Command/CommandManager.cs ================================================ using Sync.Tools; using static Sync.Tools.DefaultI18n; using System; namespace Sync.Command { /// /// Command manager for plugins commands /// public class CommandManager { CommandDispatch dispatch; public CommandManager() { dispatch = new CommandDispatch(); } /// /// Register command via this dispatch /// public CommandDispatch Dispatch { get { return dispatch; } } /// /// Invoke command in string /// /// Command public void invokeCmdString(string cmd) { if (cmd == null || cmd.Length == 0) return; string[] args = cmd.Split(" ".ToCharArray(), 2); if(args.Length < 1 ) { IO.CurrentIO.Write(LANG_UnknowCommand); return; } string arg = string.Empty; if (args.Length > 1) arg = args[1]; if (!dispatch.invoke(args[0], (arg == string.Empty ? new Arguments() : arg.Split(' ')))) { IO.CurrentIO.Write(LANG_CommandFail); } } } } ================================================ FILE: Sync/Dependence/config.ini ================================================ [config] LiveRoomID=0 TargetIRC=noname BotIRC=noname BotIRCPassword=password Provider=BiliBili ================================================ FILE: Sync/Event/EventDispatcher.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Sync.Plugins { /// /// Base event class /// public interface IBaseEvent { } public class EventDispatcherTaskScheduler : TaskScheduler { private LinkedList tasks = new LinkedList(); protected override IEnumerable GetScheduledTasks() { return tasks; } protected override void QueueTask(Task task) { var thread = new Thread(() => { TryExecuteTask(task); }); thread.Start(); } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return TryExecuteTask(task); } } public abstract class BaseEventDispatcher where T : IBaseEvent { /// /// private event for call /// /// /// private void raiseEventAsync(Event insance) where Event : T { EventDispatcher.Instance.RaiseEventAsync(this.GetType(), insance); } private void raiseEvent(Event insance) where Event : T { EventDispatcher.Instance.RaiseEvent(this.GetType(), insance); } /// /// public virtual for sub-classes override /// /// /// public virtual void RaiseEventAsync(Event @event) where Event : T { raiseEventAsync(@event); } /// /// public virtual for sub-classes override /// /// /// public virtual void RaiseEvent(Event @event) where Event : T { raiseEvent(@event); } /// /// regist class for event bind /// /// /// public void BindEvent(EventHandlerFunc handler) where Event : T { EventDispatcher.Instance.RegisterEventHandler(GetType(), handler); } } /// /// the generic handler impl /// /// which typeof Event should handle /// the event fired you target handler /// public delegate void EventHandlerFunc(Event @event) where Event : IBaseEvent; public class HandlerList : LinkedList { } /// /// typedef for LinkedList /// public class Dispatcher : Dictionary { } /// /// [Singleton] Global event dispatcher /// public class EventDispatcher { private Dictionary dispatchers = new Dictionary(); private TaskScheduler tasks = new EventDispatcherTaskScheduler(); private EventDispatcher() { } public static readonly EventDispatcher Instance = new EventDispatcher(); /// /// Register new event type[event dispatcher] /// /// New dispatcher classes /// public void RegisterNewDispatcher() where EventDispatcher : BaseEventDispatcher where TEvent : IBaseEvent { RegisterNewDispatcher(typeof(EventDispatcher)); } /// /// Register new event type /// /// public void RegisterNewDispatcher(Type t) { if (dispatchers.ContainsKey(t)) return; else dispatchers.Add(t, new Dispatcher()); return; } /// /// Get all binder of this event /// /// Target event /// Event dispatcher /// public HandlerList GetHandlerList(Type eventType) { return GetHandlerList(eventType, typeof(Event)); } /// /// Get all binder of this event /// /// Event dispatcher /// Target event /// public HandlerList GetHandlerList(Type eventType, Type @event) { return GetDispatcher(eventType)[@event]; } /// /// Get dispathcer by type /// /// type /// public Dispatcher GetDispatcher(Type eventType) { return (dispatchers[eventType]); } /// /// Get dispathcer by T /// /// Type /// public Dispatcher GetDispatcher() { return (dispatchers[typeof(EventType)]); } /// /// Return a dispathcer is or not exist /// /// /// public bool ExistDispatcher() { return dispatchers.ContainsKey(typeof(EventType)); } /// /// Return a dispathcer is or not exit /// /// /// public bool ExistDispatcher(Type eventType) { return dispatchers.ContainsKey(eventType); } /// /// Fire event with async call /// /// Dispathcer /// Event /// Event instance internal void RaiseEventAsync(Event @event) where Event : IBaseEvent { RaiseEventAsync(typeof(EventType), @event); } /// /// Fire event with async call /// /// Event /// dispatcher /// event instance internal void RaiseEventAsync(Type eventType, Event @event) where Event : IBaseEvent { if (!GetDispatcher(eventType).ContainsKey(typeof(Event))) return; foreach (var item in GetDispatcher(eventType)[typeof(Event)]) { var p = ((EventHandlerFunc)(item)); Task.Run(() => p((@event))); } } /// /// Fire event with sync call /// /// Event dispathcer /// Event /// Event instance internal void RaiseEvent(Event @event) where Event : IBaseEvent { RaiseEvent(typeof(EventType), @event); } /// /// Fire event with sync call /// /// Event /// dispatcher /// event instance internal void RaiseEvent(Type eventType, Event @event) where Event : IBaseEvent { Type typo = typeof(Event); if (!GetDispatcher(eventType).ContainsKey(typo)) return; foreach (var item in GetDispatcher(eventType)[typo]) { ((EventHandlerFunc)(item))(@event); } } /// /// Register event handler /// /// Target event /// Dispatcher /// handler /// public bool RegisterEventHandler(Type eventType, EventHandlerFunc handler) where Event : IBaseEvent { Type typo = typeof(Event); Dispatcher dispatcher = null; if (ExistDispatcher(eventType)) { dispatcher = GetDispatcher(eventType); } else { throw new Exception("Dispatcher not register!"); } if(!dispatcher.ContainsKey(typo)) { dispatcher.Add(typo, new HandlerList()); } if (dispatcher[typo].Contains(handler)) return false; dispatcher[typo].AddLast(handler); return true; } /// /// Register event handle /// /// Event dispathcer /// Target Event /// Handler /// public bool RegisterEventHandler(EventHandlerFunc handler) where Event : IBaseEvent { return RegisterEventHandler(typeof(EventType), handler); } /// /// Remove event handle /// /// Event dispathcer /// Target Event /// Handler public void RemoveEventHandler(EventHandlerFunc handler) where Event : IBaseEvent { if (dispatchers.TryGetValue(typeof(EventType),out Dispatcher dispatcher)) { var list = dispatcher[typeof(Event)]; list.Remove(handler); } } } } ================================================ FILE: Sync/MessageFilter/FilterBase.cs ================================================ using System; using Sync.Source; using Sync.Tools; using static Sync.Tools.DefaultI18n; using Sync.Plugins; namespace Sync.MessageFilter { /// /// Base filter message /// public interface IMessageBase : IBaseEvent { StringElement User { get; set; } StringElement Message { get; set; } bool Cancel { get; set; } } /// /// Online change event will raise this message /// public struct OnlineChangeMessage : IMessageBase { public StringElement User { get; set; } public StringElement Message { get; set; } public StringElement Name { get; set; } public int Count { get; set; } public bool Cancel { get; set; } public OnlineChangeMessage(int count) { User = ""; Message = string.Format(LANG_Current_Online, count); Name = ""; Cancel = false; this.Count = count; } } /// /// GiftMessage event will raise this message /// public struct GiftMessage : IMessageBase { public StringElement User { get; set; } public StringElement Message { get; set; } public StringElement Name { get; set; } public IBaseGiftEvent Source { get; } public int Count { get; set; } /// /// cancel标志指示了这条来自弹幕的消息将不会同步到IRC。 /// public bool Cancel { get; set; } public GiftMessage(IBaseGiftEvent src) { this.User = src.SenderName; this.Name = src.GiftName; this.Count = src.GiftCount; this.Source = src; this.Message = string.Format(LANG_Gift_Sent, Count, Name); Cancel = false; } } /// /// Normal danmaku event will raise this message /// public struct DanmakuMessage : IMessageBase { public StringElement User { get; set; } public StringElement Message { get; set; } /// /// cancel标志指示了这条来自弹幕的消息将不会同步到IRC。 /// public bool Cancel { get; set; } public DanmakuMessage(IBaseDanmakuEvent src) { this.User = src.SenderName; this.Message = src.Danmuku; this.Cancel = false; } } /// /// Client Reccive event will raise this message /// public struct IRCMessage : IMessageBase { public StringElement User { get; set; } public StringElement Message { get; set; } /// /// cancel标志指示了这条来自IRC的消息,不会同步到弹幕 /// public bool Cancel { get; set; } public IRCMessage(StringElement user, StringElement rawMessage) { this.User = user; this.Message = rawMessage; this.Cancel = false; } } public interface ISourceOnlineChange { //just flag } public interface ISourceGift { //just flag } public interface ISourceDanmaku { //just flag } public interface ISourceClient { //just flag } public interface IFilter { void onMsg(ref IMessageBase msg); } } ================================================ FILE: Sync/MessageFilter/FilterManager.cs ================================================ using Sync.MessageFilter; using Sync.Source; using Sync.Tools; using System; using System.Text; using System.Collections.Generic; using System.Linq; namespace Sync.Plugins { /// /// Manager and filter message /// public class FilterManager : BaseEventDispatcher { Dictionary> filters; internal FilterManager() { filters = new Dictionary>(); AddSource(); AddSource(); AddSource(); AddSource(); EventDispatcher.Instance.RegisterNewDispatcher(GetType()); //Bind source event for Message Dispathcer SourceEvents.Instance.BindEvent(evt => SyncHost.Instance.Messages.RaiseMessage(new IRCMessage(evt.SenderName, evt.Danmuku))); SourceEvents.Instance.BindEvent(evt => { if (DefaultConfiguration.Instance.EnableViewersChangedNotify.ToBool()) SyncHost.Instance.Messages.RaiseMessage(new OnlineChangeMessage(evt.Count)); }); SourceEvents.Instance.BindEvent(evt => { if (DefaultConfiguration.Instance.EnableGiftChangedNotify.ToBool()) SyncHost.Instance.Messages.RaiseMessage(new GiftMessage(evt)); }); } private void AddSource() { filters.Add(typeof(T), new List()); } public IEnumerable> GetFiltersEnum() { foreach (var list in filters) { foreach(var item in list.Value) { yield return new KeyValuePair(list.Key, item); } } } public int Count { get { return filters.Sum(p => p.Value.Count); } } internal void PassFilterDanmaku(ref IMessageBase msg) { PassFilter(ref msg); } internal void PassFilterOSU(ref IMessageBase msg) { PassFilter(ref msg); } internal void PassFilterGift(ref IMessageBase msg) { PassFilter(ref msg); } internal void PassFilterOnlineChange(ref IMessageBase msg) { PassFilter(ref msg); } private void PassFilter(ref IMessageBase msg) { PassFilter(typeof(T), ref msg); } private void PassFilter(Type identify, ref IMessageBase msg) { foreach (var filter in filters[identify]) { filter.onMsg(ref msg); if (msg.Cancel) //已经处理的就不用继续给后面的处理了 break; } RaiseEventAsync(msg); } public void AddFilter(IFilter filter) { foreach (var i in filter.GetType().GetInterfaces()) { if(filters.ContainsKey(i)) { filters[i].Add(filter); filters[i].Sort((a,b)=> ((int)b.GetFilterPriority())- ((int)a.GetFilterPriority())); } } } public void deleteFilter(IFilter filter) { foreach (var i in filter.GetType().GetInterfaces()) { if (filters.ContainsKey(i)) { filters[i].Remove(filter); } } } public void AddFilters(params IFilter[] filters) { foreach (IFilter filter in filters) { AddFilter(filter); } } } } ================================================ FILE: Sync/MessageFilter/FilterPriorityAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.MessageFilter { public enum FilterPriority { Lowest=-2, Low=-1, Normal=0, High=1, Highest=2 } [AttributeUsage(AttributeTargets.Class)] public class FilterPriorityAttribute:Attribute { public FilterPriority Priority { get; set; } = FilterPriority.Normal; } public static class FilterPriorityAttributeExtension { public static FilterPriority GetFilterPriority(this IFilter filter) { var attr=filter.GetType().GetCustomAttributes(typeof(FilterPriorityAttribute),false).FirstOrDefault() as FilterPriorityAttribute; return attr?.Priority ?? FilterPriority.Normal; } } } ================================================ FILE: Sync/MessageFilter/MessageDispatcher.cs ================================================ using Sync.Client; using Sync.MessageFilter; using Sync.Source; using Sync.Tools; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Plugins { public class MessageDispatcher { FilterManager filters; internal MessageDispatcher(FilterManager f) { filters = f; MessageManager.LimitLevel = 3; MessageManager.Option = MessageManager.PeekOption.Auto; MessageManager.Init(filters); MessageManager.SetSendMessageAction(new MessageManager.SendMessageAction((target,message) =>{ SyncHost.Instance.ClientWrapper.Client.SendMessage(new IRCMessage(target, message)); })); } /// /// Send danmaku message to osu!IRC /// /// Danmaku public void onDanmaku(IBaseDanmakuEvent danmaku) { IMessageBase msg = new DanmakuMessage(danmaku); RaiseMessage(msg); } /// /// Send osu!irc message to damaku /// /// Sender /// Message public void onIRC(StringElement user, StringElement message) { IMessageBase msg = new IRCMessage(user, message); RaiseMessage(msg); } public void RaiseMessage(IMessageBase msg) { RaiseMessage(typeof(Source), msg); } /// /// Raise a message and pass to filter list /// private void RaiseMessage(Type msgType, IMessageBase msg) { IMessageBase newMsg = msg; //From danmaku if (msgType == typeof(ISourceDanmaku)) { filters.PassFilterDanmaku(ref newMsg); if (newMsg.Cancel) return; else { //Send this danmaku message to osu!irc MessageManager.PostIRCMessage(SyncHost.Instance.ClientWrapper.Client.NickName, newMsg); } return; } //From osu!orc else if (msgType == typeof(ISourceClient)) { filters.PassFilterOSU(ref newMsg); if (newMsg.Cancel) return; else { //Detect sender is osu!irc self if (newMsg.User.RawText == SyncHost.Instance.ClientWrapper.Client.NickName) { //Send message to danmaku source if (!SyncHost.Instance.SourceWrapper.Sendable) { IO.CurrentIO.WriteColor(DefaultI18n.LANG_SendNotReady, ConsoleColor.Red); } else { SyncHost.Instance.SourceWrapper.SendableSource.Send(newMsg); } } //Send message to irc if Not sender else { MessageManager.PostIRCMessage(SyncHost.Instance.ClientWrapper.Client.NickName, newMsg); } } return; } //From danmaku gift(subscribe or other gift) else if (msgType == typeof(ISourceGift)) { filters.PassFilterGift(ref newMsg); if (newMsg.Cancel) return; else { SyncHost.Instance.ClientWrapper.Client?.SendMessage(new IRCMessage(SyncHost.Instance.ClientWrapper.Client.NickName, newMsg.Message)); } } //The spectator count change else if (msgType == typeof(ISourceOnlineChange)) { filters.PassFilterOnlineChange(ref newMsg); if (newMsg.Cancel) return; else { SyncHost.Instance.ClientWrapper.Client?.SendMessage(new IRCMessage(SyncHost.Instance.ClientWrapper.Client.NickName, newMsg.Message)); } } } } } ================================================ FILE: Sync/MessageFilter/MessageManager.cs ================================================ using Sync.Plugins; using Sync.Tools; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace Sync.MessageFilter { /// /// Message limiter /// public class MessageManager { private static float sendLimit_pre = 0; private static int time_inv = 1000; private static float time = 0; private static float recoverTime = 60000; private static SendFilter sendFilter = new SendFilter(); public static PeekOption Option { set; get; } = PeekOption.Auto; private static Thread thread = null; public static float RecoverTime { get { return recoverTime; } } public static int CurrentQueueCount { get { return MessageQueue.Count; } } private static float currentRecoverTime = 0; private static Object lockObj = new object(); private static volatile int sendCount = 0; public int CurrentCount { get { return sendCount; } } private static int limitCount = 5; public static int LimitLevel { get { return limitCount; } set { limitCount = value; sendLimit_pre = (float)limitCount / (60000 / time_inv); } } private static volatile bool isLimit = false; public static bool IsLimit { get { return isLimit; } } private static SendMessageAction action; private static List MessageQueue = new List(); private struct MessageBaseWrapper { public DateTime time; public string target; public IMessageBase message; } private class SendFilter : IFilter, ISourceDanmaku { public void onMsg(ref IMessageBase msg) { switch (Option) { case PeekOption.ForceAll://还要过滤可能存在的?前缀 break; case PeekOption.DisableAll: msg.Cancel = true; return; case PeekOption.OnlySendCommand: if (!msg.Message.RawText.StartsWith("?send")) { msg.Cancel = true; return; } break; case PeekOption.Auto: if (IsLimit && (!msg.Message.RawText.StartsWith("?send"))) { msg.Cancel = true; return; } break; default: break; } if (msg.Message.RawText.StartsWith("?send ")) msg.Message = new StringElement(msg.Message.RawText.Remove(0, 5)); } } public enum PeekOption { ForceAll, DisableAll, OnlySendCommand, Auto } public static void Init(FilterManager manager) { thread = new Thread(runThread); SyncHost.Instance.Filters.AddFilter(sendFilter); thread.Start(); } public static void PostIRCMessage(string target, IMessageBase message) { lock (lockObj) { MessageQueue.Add(new MessageBaseWrapper() { time=DateTime.Now, target=target, message =message }); sendCount++; } } public static void SetOption(string optionName) { if (Enum.TryParse(optionName, true, out PeekOption peekOption)) Option = peekOption; else Option = PeekOption.Auto; } public delegate void SendMessageAction(string userName, string message); public static void SetSendMessageAction(SendMessageAction action) { MessageManager.action = action; } public static void SendMessage(string userName, string message) { action(userName, message); } private static void runThread(object state) { StringBuilder sb=new StringBuilder(); while (true) { Thread.Sleep(time_inv); sb.Clear(); time = time + time_inv; if (Option == PeekOption.Auto) { //Exceeded limit? if (!isLimit && (float)sendCount / (60000.0f / time_inv) >= sendLimit_pre) { isLimit = true; IO.CurrentIO.WriteColor("isLimit is true now", ConsoleColor.Yellow); currentRecoverTime = 0; } //unlimit time currentRecoverTime += isLimit ? time_inv : 0; if (currentRecoverTime >= recoverTime) { currentRecoverTime = 0; isLimit = false; IO.CurrentIO.WriteColor("isLimit is false now", ConsoleColor.Yellow); } } sendCount = 0; if (isLimit) { lock (lockObj) { while (MessageQueue.Count != 0) { var message = MessageQueue[0]; MessageQueue.RemoveAt(0); if (message.message == null) break; sb.AppendFormat("{0}:{1} || ", message.message.User, message.message.Message.RawText); } } if (sb.Length != 0) SendMessage("", sb.ToString()); } else { if (MessageQueue.Count != 0) { var message = MessageQueue[0]; MessageQueue.RemoveAt(0); SendMessage(message.target, message.message.User + message.message.Message.ToString()); } } } } } } ================================================ FILE: Sync/Plugins/Plugin.cs ================================================ using Sync.Command; using System; using System.Reflection; namespace Sync.Plugins { public abstract class Plugin { public readonly string Name; public readonly string Author; public BaseEventDispatcher EventBus { get => PluginEvents.Instance; } public Plugin(string Name, string Author) { this.Name = Name; this.Author = Author; } protected SyncHost getHoster() { return SyncHost.Instance; } public string getName() { return Name; } public string getAuthor() { return Author; } public string getGuid() { return this.GetType().GetCustomAttribute()?.GUID; } public override string ToString() { return Name; } public virtual void OnDisable() { } public virtual void OnEnable() { } public virtual void OnExit() { } } } ================================================ FILE: Sync/Plugins/PluginManager.cs ================================================ using Sync.Client; using Sync.Command; using Sync.Source; using Sync.Tools; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using static Sync.Tools.DefaultI18n; namespace Sync.Plugins { /// /// A flag for plugin event type /// public interface IPluginEvent : IBaseEvent { } /// /// Base plugin events /// public class PluginEvents : BaseEventDispatcher { /// /// Fire when init plugin /// public struct InitPluginEvent : IPluginEvent { public Plugin Plugin { get; private set; } public InitPluginEvent(Plugin plugin) { this.Plugin = plugin; } } /// /// Fire when init source /// public struct InitSourceEvent : IPluginEvent { public SourceManager Sources { get; private set; } public InitSourceEvent(SourceManager source) { Sources = source; } } /// /// Fire when init filter /// public struct InitFilterEvent : IPluginEvent { public FilterManager Filters { get; private set; } public InitFilterEvent(FilterManager filters) { Filters = filters; } } /// /// Fire when init command /// public struct InitCommandEvent : IPluginEvent { public CommandManager Commands { get; private set; } public InitCommandEvent(CommandManager commands) { Commands = commands; } } /// /// Fire when init clients /// public struct InitClientEvent : IPluginEvent { public ClientManager Clients { get; private set; } public InitClientEvent(ClientManager clients) { Clients = clients; } } /// /// Fire when init source warpper /// public struct InitSourceWarpperEvent : IPluginEvent { public SourceWorkWrapper SourceWrapper { get; private set; } public InitSourceWarpperEvent(SourceWorkWrapper wrapper) { SourceWrapper = wrapper; } } /// /// Fire when init client warpper /// public struct InitClientWarpperEvent : IPluginEvent { public ClientWorkWrapper ClientWrapper { get; private set; } public InitClientWarpperEvent(ClientWorkWrapper wrapper) { ClientWrapper = wrapper; } } /// /// Fire when load complete /// public struct LoadCompleteEvent : IPluginEvent { public SyncHost Host { get; private set; } public LoadCompleteEvent(SyncHost host) { Host = host; } } public struct ConfigurationChange : IPluginEvent { } /// /// Fire when ready /// public struct ProgramReadyEvent : IPluginEvent { //public SyncManager Manager { get; private set; } //public SyncManagerCompleteEvent() //{ // Manager = Program.host.SyncInstance; //} } public static readonly PluginEvents Instance = new PluginEvents(); private PluginEvents() { EventDispatcher.Instance.RegisterNewDispatcher(GetType()); } } /// /// Plugins Manager /// Load plugins from Plugins foldere and Initial plugin /// public class PluginManager { private List pluginList; private List asmList; private LinkedList loadedList; private List allList; internal PluginManager() { } /// /// Raise global to all plugin /// /// Return commands count internal int LoadCommnads() { PluginEvents.Instance.RaiseEvent(new PluginEvents.InitCommandEvent(SyncHost.Instance.Commands)); return SyncHost.Instance.Commands.Dispatch.count; } /// /// Raise global to all plugin /// /// Return source count internal int LoadSources() { PluginEvents.Instance.RaiseEvent(new PluginEvents.InitSourceEvent(SyncHost.Instance.Sources)); return SyncHost.Instance.Sources.SourceList.Count(); } /// /// Raise global to all plugin /// /// Return filter count internal int LoadFilters() { PluginEvents.Instance.RaiseEvent(new PluginEvents.InitFilterEvent(SyncHost.Instance.Filters)); return SyncHost.Instance.Filters.Count; } /// /// Raise global to all plugin /// /// internal int LoadClients() { PluginEvents.Instance.RaiseEvent(new PluginEvents.InitClientEvent(SyncHost.Instance.Clients)); return SyncHost.Instance.Clients.Count; } /// /// Raise global to all plugin /// internal void ReadySync() { PluginEvents.Instance.RaiseEvent(new PluginEvents.ProgramReadyEvent()); } /// /// Get a for plugin list /// /// public IEnumerable GetPlugins() { return pluginList; } /// /// Internal get plugin list /// /// internal List GetPluginList() { return pluginList; } /// /// Raise a to all plugin /// internal void ReadyProgram() { PluginEvents.Instance.RaiseEvent(new PluginEvents.LoadCompleteEvent(SyncHost.Instance)); } /// /// Initial and load all support Plugin in 'Plugins' folder /// /// Plugins count internal int LoadPlugins() { //Combine search path string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"); //Current plugins list pluginList = new List(); //Loaded assembly asmList = new List(); //Pre-add all assemblies in current AppDomain asmList.AddRange(AppDomain.CurrentDomain.GetAssemblies()); //create Plugins folder if not exist if (!Directory.Exists(path)) { try { Directory.CreateDirectory(path); IO.CurrentIO.WriteColor($"Created default Plugins folder : {path}", ConsoleColor.Green); } catch (Exception e) { IO.CurrentIO.WriteColor($"Create default Plugins folder ({path}) failed : {e.Message} ", ConsoleColor.Yellow); } } //Change directiory to Plugins Directory.SetCurrentDirectory(path); //create cache folder var rootCache = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache"); string cache = Path.Combine(rootCache, $"cache_{(new Random()).Next().ToString("x8")}"); try { Directory.Delete(rootCache, true); Directory.CreateDirectory(rootCache); } catch { } try { if (Directory.Exists(cache)) { Directory.Delete(cache, true); } Directory.CreateDirectory(cache); } catch (Exception e) { IO.CurrentIO.WriteColor($"Create temporary cache folder ({cache}) failed : {e.Message} ", ConsoleColor.Yellow); throw e; } new DirectoryInfo(rootCache) { Attributes = FileAttributes.Normal }; //error extra notify mark bool got_locked_error = false; //Search all .dll files in directory(include sub directory) foreach (string file in Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories)) { try { if (asmList.Any(a => a.Location == file)) continue; //Load assembly directly string temp = Path.Combine(cache, Path.GetFileName(file)); File.Copy(file, temp); Assembly asm = Assembly.LoadFrom(temp); asmList.Add(asm); } catch (BadImageFormatException) { /* * Do not output anything when the file is not a * .Net assembly. * This can reduces unnecessary output when * unmanaged assembly is needed. */ } catch (Exception e) { //Not a .NET Assembly DLL IO.CurrentIO.WriteColor(String.Format(LANG_LoadPluginErr, file, e.Message), ConsoleColor.Red); if (e.Message.Contains("0x80131515")) got_locked_error = true; } } if (got_locked_error) IO.CurrentIO.WriteColor(String.Format("Opps,It seems your plugin dll files were locked by System.\n" + "Please view https://osu.ppy.sh/forum/t/685031/start=37 and https://osu.ppy.sh/forum/t/685031/start=24 to solve problem."), ConsoleColor.Red); loadedList = new LinkedList(); //To slove plugin dependency, List lazylist = new List(); allList = new List(); //Load all plugins first foreach (Assembly asm in asmList) { try { foreach (Type item in asm.GetExportedTypes()) { Type it = asm.GetType(item.FullName); if (it == null || !it.IsClass || !it.IsPublic || !typeof(Plugin).IsAssignableFrom(it) || typeof(Plugin) == it) continue; allList.Add(it); } } catch (FileNotFoundException e) { CheckUnknownDependency(e.FileName.Substring(0, e.FileName.IndexOf(",") - 1)); } catch (Exception e) { //Not up to date IO.CurrentIO.WriteColor(String.Format(LANG_LoadPluginErr, asm.FullName, e.Message), ConsoleColor.Red); continue; } } lazylist = allList.ToList(); //looping add for resolve dependency do { lazylist = layerLoader(lazylist); } while (lazylist.Count != 0); return pluginList.Count; } /// /// Load plugins /// Load dependencies plugin first /// /// All : /// Not loaded plugins private List layerLoader(IList asmList) { List nextLoad = new List(); foreach (Type it in asmList) { try { var hardDeps = it.GetCustomAttributes(); foreach (var item in hardDeps) { var target = allList.Select(p => p.GetCustomAttribute())?.FirstOrDefault(p => p?.GUID == item.GUID); if (item.Require && target == null) CheckGUIDUpdate(item); if (item.Version == null) continue; if (!CompareVersion(item.Version, target.Version)) CheckGUIDUpdate(item); } var softDeps = it.GetCustomAttributes(); foreach (var item in softDeps) { foreach (var dep in item.RequirePluguins) { if (!allList.Any(p => p.Name.Contains(dep) || dep.Contains(p.Name))) CheckUnknownDependency(dep); } } if (LateLoad(it)) { #if (DEBUG) IO.CurrentIO.WriteColor($"Lazy load [{it.Name}]", ConsoleColor.Green); #endif nextLoad.Add(it); //Dependency load at this time //Lazy load this plugin at next time continue; } //no dependencies or dependencies all was loaded if (!it.IsSubclassOf(typeof(Plugin))) continue; else { LoadPluginFormType(it); loadedList.AddLast(it); } } catch (Exception e) { IO.CurrentIO.WriteColor(String.Format(LANG_NotPluginErr, it.Name, e.Message), ConsoleColor.Red); IO.CurrentIO.WriteColor(e.StackTrace, ConsoleColor.Red); continue; } } return nextLoad; } private bool LateLoad(Type a) { SyncRequirePlugin requireAttr = a.GetCustomAttribute(); SyncSoftRequirePlugin softRequirePlugin = a.GetCustomAttribute(); IEnumerable deps = a.GetCustomAttributes(); SyncPluginID pid = a.GetCustomAttribute(); if (deps != null) { foreach (var item in deps) { if (loadedList.Any(p => p.GetCustomAttribute()?.GUID == item.GUID)) continue; else { if (CheckIsReferenceTo(allList.FirstOrDefault(p => p.GetCustomAttribute()?.GUID == item.GUID), pid?.GUID)) return false; else return true; } } } if (requireAttr != null) { foreach (var item in requireAttr.RequirePluguins) { //Dependency was been loaded if (loadedList.Contains(item)) continue; else { //Check cycle reference if (CheckIsReferenceTo(item, a)) return false; else return true; } } } if (softRequirePlugin != null) { foreach (var item in softRequirePlugin.RequirePluguins) { Type s = allList.FirstOrDefault(p => p.Name == item); if (s == null) { continue; } else { if (CheckIsReferenceTo(s, a)) return false; if (!loadedList.Contains(s)) return true; } } } return false; } private bool CheckIsReferenceTo(Type a, string b) { var result = a.GetCustomAttributes()?.Any(p => p.GUID == b); if (result.HasValue) return result.Value; else return false; } private bool CheckIsReferenceTo(Type a, Type b) { return CheckIsHardReferenceTo(a, b) || CheckIsSoftReferenceTo(a, b.Name); } private bool CheckIsHardReferenceTo(Type a, Type b) { SyncRequirePlugin refRequireCheck = a.GetCustomAttribute(); if (refRequireCheck == null) return false; return refRequireCheck.RequirePluguins.Contains(b); } private bool CheckIsSoftReferenceTo(Type a, string b) { SyncSoftRequirePlugin refRequireCheck = a.GetCustomAttribute(); if (refRequireCheck == null) return false; return refRequireCheck.RequirePluguins.Contains(b); } /// /// True if is satisfy for the require of /// /// A version require /// Target version /// public static bool CompareVersion(string a, string b) { // v : less or equal // x : must // ^ : large or equal char start = a[0]; Func converter; if (start == 'v') converter = p => p == null || p.Value; else if (start == '^') converter = p => p == null || !p.Value; else converter = p => p == null; string ta = a.Substring(1); var va = ta.Split('.').Select(k => int.Parse(k)).ToArray(); var vb = b.Split('.').Select(k => int.Parse(k)).ToArray(); for (int i = 0; i < va.Length; i++) { bool val = va[i] > vb[i]; if (va[i] == vb[i]) continue; else return converter(val); } return converter(null); } private void CheckUnknownDependency(string name) { if (Updater.update.InstallByKeyword(name, false)) { SyncHost.Instance.ForceRestartSync(); throw new SyncPluginOutdateException($"Need restart application to update {name}"); } else { throw new SyncMissingPluginException($"Can't install dependency plugin: {name}"); } } private void CheckGUIDUpdate(SyncPluginDependency item) { if (Updater.update.InternalUpdate(item.GUID, true)) { SyncHost.Instance.ForceRestartSync(); throw new SyncPluginOutdateException($"Need restart application to update {item.GUID}"); } else { throw new SyncMissingPluginException($"Can't install dependency plugin: {item.GUID}, version {item.Version}"); } } private Plugin LoadPluginFormType(Type it) { object pluginTest = it.Assembly.CreateInstance(it.FullName); if (pluginTest == null) { throw new NullReferenceException("Create instance fail!"); } Plugin plugin = (Plugin)pluginTest; IO.CurrentIO.WriteColor(String.Format(LANG_LoadingPlugin, plugin.Name), ConsoleColor.White); pluginList.Add(plugin); plugin.OnEnable(); PluginEvents.Instance.RaiseEventAsync(new PluginEvents.InitPluginEvent(plugin)); return plugin; } } public class SyncMissingPluginException : Exception { public SyncMissingPluginException(string msg) : base(msg) { } } public class SyncPluginOutdateException : Exception { public SyncPluginOutdateException(string msg) : base(msg) { } } /// /// Using this attribute when you want load some plugin before your plugin. /// public class SyncRequirePlugin : Attribute { public IReadOnlyList RequirePluguins; public SyncRequirePlugin(params Type[] types) { RequirePluguins = new List(types); } } /// /// Using this attribute when you dependence some plugin without hard link. /// public class SyncSoftRequirePlugin : Attribute { public IReadOnlyList RequirePluguins; public SyncSoftRequirePlugin(params string[] types) { RequirePluguins = new List(types); } } public class SyncPluginID : Attribute { public string GUID { get; } /// /// Major.Minjor.Reversion /// e.g: 1.4.5 /// public string Version { get; } public SyncPluginID(string GUID, string Version) { this.Version = Version; this.GUID = GUID; } } public class SyncPluginDependency : Attribute { public string GUID { get; } public string Version { get; set; } public bool Require { get; set; } public SyncPluginDependency(string guid) => GUID = guid; } } ================================================ FILE: Sync/Program.cs ================================================ using SharpRaven; using Sync.Tools; using System; using System.Threading; namespace Sync { public static class Program { static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; (new StartupHelper(args)).Start(); } private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { SentryHelper.Instance.RepoterError(e.ExceptionObject as Exception); Thread.Sleep(2000); Environment.Exit(1); } } } ================================================ FILE: Sync/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows; // 有关程序集的一般信息由以下 // 控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("Sync!")] [assembly: AssemblyDescription("Sync Live danmaku and Games!")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Remilia")] [assembly: AssemblyProduct("Sync")] [assembly: AssemblyCopyright("MIT")] [assembly: AssemblyTrademark("RemiliaScarlet.com")] [assembly: AssemblyCulture("")] //将 ComVisible 设置为 false 将使此程序集中的类型 //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, //请将此类型的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] //若要开始生成可本地化的应用程序,请 // 中的 .csproj 文件中 //例如,如果您在源文件中使用的是美国英语, //使用的是美国英语,请将 设置为 en-US。 然后取消 //对以下 NeutralResourceLanguage 特性的注释。 更新 //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。 //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] [assembly: ThemeInfo( ResourceDictionaryLocation.None, //主题特定资源词典所处位置 //(当资源未在页面 //或应用程序资源字典中找到时使用) ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置 //(当资源未在页面 //、应用程序或任何主题专用资源字典中找到时使用) )] // 程序集的版本信息由下列四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, // 方法是按如下所示使用“*”: : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("2.18.5.0")] [assembly: AssemblyFileVersion("2.18.5.0")] ================================================ FILE: Sync/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // 此代码由工具生成。 // 运行时版本:4.0.30319.42000 // // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // //------------------------------------------------------------------------------ namespace Sync.Properties { using System; /// /// 一个强类型的资源类,用于查找本地化的字符串等。 /// // 此类是由 StronglyTypedResourceBuilder // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// 返回此类使用的缓存的 ResourceManager 实例。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Sync.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// 使用此强类型资源类,为所有资源查找 /// 重写当前线程的 CurrentUICulture 属性。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } } } ================================================ FILE: Sync/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Sync/Properties/Settings.Designer.cs ================================================ //------------------------------------------------------------------------------ // // 此代码由工具生成。 // 运行时版本:4.0.30319.42000 // // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // //------------------------------------------------------------------------------ namespace Sync.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { get { return defaultInstance; } } } } ================================================ FILE: Sync/Properties/Settings.settings ================================================  ================================================ FILE: Sync/Resources/App.config ================================================ ================================================ FILE: Sync/Source/SourceBase.cs ================================================ using Sync.MessageFilter; using Sync.Plugins; using Sync.Tools; using System; using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; namespace Sync.Source { /// /// Flag this source is support send /// public abstract class SendableSource : SourceBase { //if sendablesource is able send message now. public virtual bool SendStatus { get => Status == SourceStatus.CONNECTED_WORKING; } public SendableSource(string Name, string Author) : base(Name, Author) { } internal void login(string user, string password) { Login(user, password); } public abstract void Login(string user, string password); public abstract void Send(IMessageBase message); } /// /// base class help program manager the source impl in program dispatch /// public abstract class SourceBase { public string Name { get; private set; } public string Author { get; private set; } public string LiveID { get; set; } = ""; public BaseEventDispatcher EventBus { get => SourceEvents.Instance; } public SourceStatus Status { get; protected set; } public SourceBase(string Name, string Author) { this.Name = Name; this.Author = Author; this.Status = SourceStatus.IDLE; } /// /// Raise a event synchronized and dispatch it to handler asynchronized /// /// Target Event Type /// Type args protected void RaiseEvent(T args) where T : ISourceEvent { EventBus.RaiseEvent(args); } internal void connect() { this.Status = SourceStatus.USER_REQUEST_CONNECT; Connect(); } internal void disconnect() { this.Status = SourceStatus.USER_REQUEST_DISCONNECT; Disconnect(); } public abstract void Connect(); public abstract void Disconnect(); } ///// ///// fire when Source event raised. ///// ///// ////public delegate void SourceEventEvt(T args) where T : SourceEvent; ///// ///// Source event base arg class ///// Including event name and eventobject ///// //public class SourceEventArgs where T : SourceEvent //{ // private SourceEvent eventObj; // public string Name { get; private set; } // public T EventObject { get => (T)eventObj; } // public SourceEventArgs(T EventObject) // { // eventObj = EventObject; // } // public T CastTo() // { // return (T)EventObject; // } //} /// /// Source network impossible status /// public enum SourceStatus { /// /// Source working good, still working /// CONNECTED_WORKING, /// /// Source working good, but waiting for remote server /// CONNECTED_WAITING, /// /// Still establish connection to target server /// CONNECTING, /// /// disconnect by remote /// REMOTE_DISCONNECTED, /// /// disconnect by user or network drop /// USER_DISCONNECTED, /// /// no any connection action /// IDLE, /// /// user request connect and waiting for Source class response. /// USER_REQUEST_CONNECT, /// /// user request disconnect, and waiting for Source class response. /// USER_REQUEST_DISCONNECT, } } ================================================ FILE: Sync/Source/SourceEvent.cs ================================================ using Sync.Client; using Sync.MessageFilter; using Sync.Plugins; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Source { /// /// Singleton for source events /// public sealed class SourceEvents : BaseEventDispatcher { public readonly static SourceEvents Instance = new SourceEvents(); private SourceEvents() { EventDispatcher.Instance.RegisterNewDispatcher(GetType()); } } /// /// This message will fire when source start work /// public struct StartSourceEvent : ISourceEvent { public SourceWorkWrapper Source { get => SyncHost.Instance.SourceWrapper; } } /// /// This message will fire when source stop work /// public struct StopSyncEvent : ISourceEvent { } /// /// The message will fire when source recive a danmaku /// public struct BaseDanmakuEvent : IBaseDanmakuEvent { public string Danmuku { get; set; } public string SenderName { get; set; } public string SendTime { get; set; } public BaseDanmakuEvent(string danmaku, string sender, string time) { Danmuku = danmaku; SenderName = sender; SendTime = time; } } /// /// Base danmaku interface /// public interface IBaseDanmakuEvent : ISourceEvent { string Danmuku { get; set; } string SenderName { get; set; } string SendTime { get; set; } } /// /// Base gift interface /// public interface IBaseGiftEvent : ISourceEvent { string GiftName { get; set; } int GiftCount { get; set; } string SenderName { get; set; } string SendTime { get; set; } } /// /// This event will fire when source status change /// public struct BaseStatusEvent : ISourceEvent { public SourceStatus Status { get; private set; } public BaseStatusEvent(SourceStatus status) { Status = status; } } /// /// This event will fire when source check online change /// public struct BaseOnlineCountEvent : ISourceEvent { public int Count { get; set; } public BaseOnlineCountEvent(int Count) { this.Count = Count; } } /// /// Source event flag /// public interface ISourceEvent : IBaseEvent { } } ================================================ FILE: Sync/Source/SourceManager.cs ================================================ using Sync.Source; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Plugins { /// /// Source manager /// public class SourceManager { LinkedList listSources; public SourceManager() { listSources = new LinkedList(); } public IEnumerable SourceList { get { return listSources; } } public bool AddSource(SourceBase src) { if(listSources.Contains(src)) { return false; } else { listSources.AddLast(src); } return true; } } } ================================================ FILE: Sync/Source/SourceWorkWrapper.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sync.Plugins; using Sync.Tools; using static Sync.Tools.DefaultI18n; namespace Sync.Source { /// /// A wrapper for select source and check source senable /// public class SourceWorkWrapper { private SourceManager sources; public bool Sendable { get; } public SourceWorkWrapper(SourceManager sources) { this.sources = sources; Source = sources.SourceList.Where(p => p.Name == DefaultConfiguration.Instance.Source).FirstOrDefault()??sources.SourceList.FirstOrDefault();/*没有的话就默认第一个*/ if(Source == null) { IO.CurrentIO.WriteColor(LANG_NO_ANY_SOURCE, ConsoleColor.Red); } if (Source is SendableSource) { SendableSource = (SendableSource)Source; Sendable = true; } } public SourceBase Source { get; internal set; } public SendableSource SendableSource { get; internal set; } } } ================================================ FILE: Sync/Sync.csproj ================================================  Debug AnyCPU {FBD514C2-2830-479E-B050-D1C383028456} Exe Properties Sync Sync v4.5 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 false publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false true x86 true full false bin\Debug\ TRACE;DEBUG prompt 4 true x86 none true ..\Release\ TRACE;SyncRelease prompt 0 true false true Off Sync.Program Resources\SyncIcon.ico LocalIntranet false ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll ..\packages\SharpRaven.2.4.0\lib\net45\SharpRaven.dll ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll 4.0 Code True True Resources.resx True Settings.settings True ResXFileCodeGenerator Resources.Designer.cs SettingsSingleFileGenerator Settings.Designer.cs Designer False Microsoft .NET Framework 4.5 %28x86 和 x64%29 true False .NET Framework 3.5 SP1 false xcopy $(ProjectDir)\..\Language $(ProjectDir)\$(OutDir)\Language\ /s/e/y ================================================ FILE: Sync/SyncHost.cs ================================================ using Sync.Command; using Sync.Plugins; using Sync.Tools; using System; using System.Collections.Generic; using static Sync.Tools.IO; using static Sync.Tools.DefaultI18n; using Sync.Client; using Sync.Source; using System.Reflection; using System.Diagnostics; namespace Sync { /// /// A manager for global modules /// public class SyncHost { public static SyncHost Instance { get; internal set; } private SourceWorkWrapper sourceWrapper; private ClientWorkWrapper clientWrapper; private CommandManager commands; private PluginManager plugins; private SourceManager sources; private FilterManager filters; private MessageDispatcher messages; private ClientManager clients; /// /// Internal use only /// internal SyncHost() { } /// /// Invoke to load plugins /// internal void Load() { CurrentIO.Write(LANG_Loading); //Initial plugins manager plugins = new PluginManager(); CurrentIO.WriteColor(String.Format(LANG_Plugins, plugins.LoadPlugins()), ConsoleColor.Green); //Initial danmaku source sources = new SourceManager(); CurrentIO.WriteColor(String.Format(LANG_Sources, plugins.LoadSources()), ConsoleColor.Green); //select a danmaku source by config try { sourceWrapper = new SourceWorkWrapper(sources); PluginEvents.Instance.RaiseEvent(new PluginEvents.InitSourceWarpperEvent(sourceWrapper)); } catch { CurrentIO.Write(""); CurrentIO.WriteColor(LANG_Error, ConsoleColor.Red); CurrentIO.WriteColor("Press enter to continue", ConsoleColor.Red); CurrentIO.ReadCommand(); } //Get clients singleton clients = ClientManager.Instance; CurrentIO.WriteColor(String.Format(LANG_Client, plugins.LoadClients()), ConsoleColor.Green); clientWrapper = new ClientWorkWrapper(clients); PluginEvents.Instance.RaiseEvent(new PluginEvents.InitClientWarpperEvent(clientWrapper)); commands = new CommandManager(); CurrentIO.WriteColor(String.Format(LANG_Commands, plugins.LoadCommnads()), ConsoleColor.Green); filters = new FilterManager(); CurrentIO.WriteColor(String.Format(LANG_Filters, plugins.LoadFilters()), ConsoleColor.Green); messages = new MessageDispatcher(filters); plugins.ReadyProgram(); CurrentIO.WriteColor(LANG_Ready, ConsoleColor.Cyan); } /// /// The internal PluginManager instance property /// internal PluginManager Plugins { get { return plugins; } } /// /// Read only plugins list /// /// public IEnumerable EnumPluings() { return plugins.GetPlugins(); } public CommandManager Commands { get { return commands; } } public SourceManager Sources { get { return sources; } } public FilterManager Filters { get { return filters; } } public MessageDispatcher Messages { get { return messages; } } public ClientManager Clients { get => clients; } public SourceWorkWrapper SourceWrapper { get => sourceWrapper; } public ClientWorkWrapper ClientWrapper { get => clientWrapper; } public void ExitSync() { try { SaveSync(); } finally { Environment.Exit(0); } } public void SaveSync() { ClientWrapper?.Client?.StopWork(); SourceWrapper?.Source?.Disconnect(); foreach (var item in PluginConfigurationManager.ConfigurationSet) { item.SaveAll(); } plugins.GetPluginList().ForEach(p => p.OnExit()); } public void RestartSync() { try { SaveSync(); } finally { ForceRestartSync(); } } public void ForceRestartSync() { Process.Start(Assembly.GetEntryAssembly().Location); Environment.Exit(0); } } } ================================================ FILE: Sync/Tools/Builtin/CommonCommand.cs ================================================ using Sync.Command; using Sync.MessageFilter; using Sync.Plugins; using Sync.Source; using Sync.Tools; using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using static Sync.Tools.DefaultI18n; namespace Sync.Tools.Builtin { public sealed class CommonCommand { public void BindCommondCommand(CommandDispatch dispatch) { dispatch.bind("exit", exit, LANG_COMMANDS_EXIT); dispatch.bind("restart", restart, LANG_COMMANDS_RESTART); dispatch.bind("stop", stop, LANG_COMMANDS_STOP); dispatch.bind("start", start, LANG_COMMANDS_START); dispatch.bind("status", status, LANG_COMMANDS_STATUS); dispatch.bind("sourcemsg", sourcemsg, LANG_COMMANDS_SOURCEMSG); dispatch.bind("clientmsg", clientmsg, LANG_COMMANDS_CLIENTMSG); dispatch.bind("clientusermsg", chatuser, LANG_COMMANDS_CLIENTUSERMSG); dispatch.bind("disable", disable, LANG_COMMANDS_DISABLE); dispatch.bind("client", switchclient, LANG_COMMANDS_SWITCH_CLIENT); dispatch.bind("sourcelogin", sourcelogin, LANG_COMMANDS_SOURCELOGIN); dispatch.bind("clear", clear, LANG_COMMANDS_CLEAR); dispatch.bind("help", help, LANG_COMMANDS_HELP); dispatch.bind("listlang", languages, LANG_COMMANDS_LISTLANG); dispatch.bind("lang", language, LANG_COMMANDS_LANG); dispatch.bind("msgmgr", msgmgr, LANG_COMMANDS_MSGMGR); dispatch.bind("source", listsource, LANG_COMMANDS_SOURCES); dispatch.bind("filters", filters, LANG_COMMANDS_FILTERS); } private bool listsource(Arguments arg) { foreach (SourceBase src in SyncHost.Instance.Sources.SourceList) { IO.CurrentIO.WriteColor("", ConsoleColor.Gray, false); IO.CurrentIO.WriteColor(LANG_COMMANDS_SOURCES_NAME, ConsoleColor.Cyan, false, false); IO.CurrentIO.WriteColor(src.Name.PadRight(18), ConsoleColor.White, false, false); IO.CurrentIO.WriteColor(LANG_COMMANDS_SOURCES_AUTHOR, ConsoleColor.DarkCyan, false, false); IO.CurrentIO.WriteColor(src.Author, ConsoleColor.White, true, false); } IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_CURRENT, SyncHost.Instance.ClientWrapper?.Client?.ClientName ?? "还没指定接收源"), ConsoleColor.Green); return true; } private bool switchclient(Arguments arg) { if (arg.Count == 0) { foreach (var item in SyncHost.Instance.Clients.Clients) { IO.CurrentIO.WriteColor("", ConsoleColor.Gray, false); IO.CurrentIO.WriteColor(LANG_COMMANDS_CLIENT_NAME, ConsoleColor.Cyan, false, false); IO.CurrentIO.WriteColor(item.ClientName.PadRight(18), ConsoleColor.White, false, false); IO.CurrentIO.WriteColor(LANG_COMMANDS_CLIENT_AUTHOR, ConsoleColor.DarkCyan, false, false); IO.CurrentIO.WriteColor(item.Author, ConsoleColor.White, true, false); } IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_CURRENT, SyncHost.Instance.SourceWrapper?.Source?.Name ?? "还没指定发送源"), ConsoleColor.Green); } else { if (SyncHost.Instance.Clients.Clients.FirstOrDefault(p => p.ClientName == arg[0]) == null) return false; DefaultConfiguration.Instance.Client = arg[0]; SyncHost.Instance.ClientWrapper.ResetClient(); } return true; } private bool disable(Arguments arg) { if (arg.Count == 0) IO.CurrentIO.WriteColor(LANG_NO_PLUGIN_SELECT, ConsoleColor.Red); else foreach (var item in Sync.SyncHost.Instance.EnumPluings()) { if (item.Name == arg[0]) { item.OnDisable(); IO.CurrentIO.WriteColor(LANG_PLUGIN_DISABLED + arg[0], ConsoleColor.Red); } } return true; } private bool sourcelogin(Arguments arg) { if (SyncHost.Instance.SourceWrapper.Sendable) { switch (arg.Count) { case 0: SyncHost.Instance.SourceWrapper.SendableSource.Login("", ""); break; case 1: SyncHost.Instance.SourceWrapper.SendableSource.Login(arg[0], ""); break; case 2: SyncHost.Instance.SourceWrapper.SendableSource.Login(arg[0], arg[1]); break; default: break; } } else IO.CurrentIO.WriteColor(string.Format(LANG_SOURCE_NOT_SUPPORT_SEND, SyncHost.Instance.SourceWrapper.Source?.GetType().Name), ConsoleColor.Red); return true; } private bool clientmsg(Arguments arg) { if (arg.Count == 0 || (SyncHost.Instance.ClientWrapper.Client.CurrentStatus != SourceStatus.CONNECTED_WORKING)) { IO.CurrentIO.Write(LANG_COMMANDS_CHAT_IRC_NOTCONNECT); return true; } SyncHost.Instance.Messages.RaiseMessage(new DanmakuMessage() { User = "Console", Message = string.Join(" ", arg) }); return true; } private bool chatuser(Arguments arg) { if (arg.Count < 1 || (SyncHost.Instance.ClientWrapper.Client.CurrentStatus != SourceStatus.CONNECTED_WORKING)) { IO.CurrentIO.Write(LANG_COMMANDS_CHAT_IRC_NOTCONNECT); } var message = string.Join(" ",arg.Skip(1)); SyncHost.Instance.Messages.RaiseMessage(new DanmakuMessage() { User = arg[0].Trim(), Message = message }); return true; } private bool sourcemsg(Arguments arg) { if (SyncHost.Instance.SourceWrapper.Sendable) { if (SyncHost.Instance.SourceWrapper.SendableSource.SendStatus) { SyncHost.Instance.SourceWrapper.SendableSource.Send(new IRCMessage(string.Empty, string.Join("", arg))); return true; } else { IO.CurrentIO.Write(LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN); } } else { IO.CurrentIO.Write(LANG_COMMANDS_DANMAKU_NOT_SUPPORT); } return false; } private bool start(Arguments arg) { if (SyncHost.Instance.SourceWrapper.Source == null) { IO.CurrentIO.WriteColor(LANG_COMMANDS_START_NO_SOURCE, ConsoleColor.Red); return true; } if (SyncHost.Instance.ClientWrapper.Client == null) { IO.CurrentIO.WriteColor(LANG_COMMANDS_START_NO_CLIENT, ConsoleColor.Red); return true; } if (SyncHost.Instance.SourceWrapper.Source.Status == SourceStatus.CONNECTED_WORKING) { IO.CurrentIO.WriteColor(LANG_COMMANDS_START_ALREADY_RUN, ConsoleColor.Red); return true; } SyncHost.Instance.ClientWrapper?.Client?.StartWork(); SyncHost.Instance.SourceWrapper?.Source?.Connect(); return true; } private bool stop(Arguments arg) { SyncHost.Instance.ClientWrapper.Client?.StopWork(); SyncHost.Instance.SourceWrapper.Source?.Disconnect(); return true; } private bool status(Arguments arg) { IO.CurrentIO.WriteStatus(); return true; } private bool language(Arguments arg) { if (arg.Count == 0) { CultureInfo info = CultureInfo.GetCultureInfo(I18n.Instance.CurrentLanguage); IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_CURRENT_LANG, info.Name, info.NativeName), ConsoleColor.Yellow); return true; } else if (arg.Count == 1) { try { CultureInfo info = CultureInfo.GetCultureInfo(arg[0]); DefaultConfiguration.Instance.Language = arg[0]; IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_LANG_SWITCHED, arg[0], info.NativeName), ConsoleColor.Green); return true; } catch (CultureNotFoundException) { IO.CurrentIO.WriteColor(LANG_COMMANDS_LANG_NOT_FOUND, ConsoleColor.Red); return false; } } return false; } private bool clear(Arguments arg) { IO.CurrentIO.Clear(); IO.CurrentIO.WriteWelcome(); return true; } private bool help(Arguments arg) { IO.CurrentIO.WriteHelp(); return true; } private static Dictionary> MessageManagerCommandMap = new Dictionary> { {"--help",msgmgr_help}, {"--status",msgmgr_status}, {"--limit",msgmgr_limit}, {"--option",msgmgr_option} }; private static void msgmgr_help(Arguments arg) { IO.CurrentIO.WriteColor(LANG_COMMANDS_MSGMGR_HELP, ConsoleColor.Yellow); } private static void msgmgr_status(Arguments arg) { IO.CurrentIO.WriteColor(String.Format(LANG_COMMANDS_MSGMGR_STATUS, (string)(MessageManager.IsLimit ? LANG_COMMANDS_MSGMGR_LIMIT : LANG_COMMANDS_MSGMGR_FREE), MessageManager.CurrentQueueCount, MessageManager.LimitLevel, MessageManager.RecoverTime, MessageManager.Option.ToString()), ConsoleColor.Yellow); } private static void msgmgr_limit(Arguments arg) { if (arg.Count == 2 && Int32.TryParse(arg[1].Trim(), out int value)) { MessageManager.LimitLevel = value; IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET, MessageManager.LimitLevel), ConsoleColor.Yellow); } else IO.CurrentIO.WriteColor(LANG_COMMANDS_ARGUMENT_WRONG, ConsoleColor.Red); } private static void msgmgr_option(Arguments arg) { if (arg.Count == 2) { MessageManager.SetOption(arg[1].Trim()); IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET, MessageManager.Option.ToString()), ConsoleColor.Yellow); } else IO.CurrentIO.WriteColor(LANG_COMMANDS_ARGUMENT_WRONG, ConsoleColor.Red); } private bool msgmgr(Arguments arg) { if (arg.Count == 0) IO.CurrentIO.WriteColor(LANG_COMMANDS_ARGUMENT_WRONG, ConsoleColor.Red); else MessageManagerCommandMap[arg[0]](arg); return true; } private bool languages(Arguments arg) { if (arg.Count > 0 && arg[0] == "--all") { foreach (var item in CultureInfo.GetCultures(CultureTypes.NeutralCultures)) { IO.CurrentIO.WriteColor(string.Format("CultureName: {0:S}\t{1:S}", item.Name, item.NativeName), ConsoleColor.Yellow); } } else { foreach (var item in System.IO.Directory.EnumerateDirectories(I18n.Instance.LangFolder)) { string name = item.Substring(item.LastIndexOf('\\') + 1); IO.CurrentIO.WriteColor(string.Format("CultureName: {0:S}\t{1:S}", name, CultureInfo.GetCultureInfo(name).NativeName), ConsoleColor.Yellow); } } return true; } private bool exit(Arguments arg) { stop(arg); SyncHost.Instance.ExitSync(); IO.CurrentIO.Write(LANG_COMMANDS_EXIT_DONE); return true; } private bool restart(Arguments arg) { Process.Start(Assembly.GetEntryAssembly().Location, "-f"); Environment.Exit(0); return true; } private bool filters(Arguments arg) { foreach (var item in SyncHost.Instance.Filters.GetFiltersEnum()) { IO.CurrentIO.WriteColor("", ConsoleColor.Gray, false); IO.CurrentIO.WriteColor(LANG_COMMANDS_FILTERS_ITEM, ConsoleColor.Cyan, false, false); IO.CurrentIO.WriteColor(item.Key.Name.PadRight(22), ConsoleColor.White, false, false); IO.CurrentIO.WriteColor(LANG_COMMANDS_FILTERS_OBJ, ConsoleColor.DarkCyan, false, false); IO.CurrentIO.WriteColor(item.Value.GetType().Name, ConsoleColor.White, true, false); } return true; } } } ================================================ FILE: Sync/Tools/Builtin/InternalPlugin.cs ================================================ using Sync.Command; using Sync.Plugins; using System; namespace Sync.Tools.Builtin { public class InternalPlugin : Plugin { //private PluginConfigurationManager config; private CommonCommand commonCommand = new CommonCommand(); public InternalPlugin() : base("InternalPlugin", "OsuSync") { } public override void OnEnable() { EventBus.BindEvent(p => { Func addCmd = p.Commands.Dispatch.bind; addCmd("plugins", PluginCommand.Instance.Plugins, "Install & Update Plugins online, type 'plugins' to get help."); commonCommand.BindCommondCommand(p.Commands.Dispatch); }); } internal bool CheckUpdate(string guid) => PluginCommand.Instance.InternalUpdate(guid,true); } } ================================================ FILE: Sync/Tools/Builtin/PluginCommand.cs ================================================ using Sync.Command; using Sync.Plugins; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Security.Cryptography; using System.Text; using static Sync.Tools.DefaultI18n; namespace Sync.Tools.Builtin { internal sealed class PluginCommand { #region SingleInstance private static PluginCommand instance = null; internal static PluginCommand Instance => instance ?? (instance = new PluginCommand()); #endregion #region Updater Decleare [DataContract] public class UpdateData { [DataMember(Order = 0)] public int id { get; set; } [DataMember(Order = 1)] public string name { get; set; } [DataMember(Order = 2)] public string author { get; set; } [DataMember(Order = 3)] public string latestHash { get; set; } [DataMember(Order = 4)] public string downloadUrl { get; set; } [DataMember(Order = 5)] public string description { get; set; } [DataMember(Order = 6)] public string guid { get; set; } [DataMember(Order = 7)] public string fileName { get; set; } } [DataContract] public class SyncUpdate { [DataMember(Order = 0)] public string versionHash { get; set; } [DataMember(Order = 1)] public string downloadURL { get; set; } [DataMember(Order = 2)] public string versionId { get; set; } } #endregion Updater Decleare public bool Plugins(Arguments arg) { if (arg.Count == 0) return Help(); switch (arg[0]) { case "search": return Search(arg[1]); case "update": if (arg.Count > 1) { var part_plugin_name = arg[1]; return Update(part_plugin_name, arg.Any(a => a == "--no_ask")); } else return Update(arg.Any(a => a == "--no_ask")); case "install": return Install(arg[1]); case "list": return List(); case "remove": return Remove(arg[1]); case "latest": return SyncUpdateCheck(true); default: return Help(); } } private bool AskAgreeUpdate(UpdateData data) { IO.CurrentIO.WriteColor($"----------------\nplugin {data.name} have a new version!\nFile hash:{data.latestHash}\nDownload? (Y/N):", ConsoleColor.Green, false); var result = IO.CurrentIO.ReadCommand(); return result.ToLower() == "y"; } public bool ShouldDownloadUpdate(UpdateData update_data, string current_file_path, bool no_ask) { //todo : version compare if (!File.Exists(current_file_path)) { return true; } var current_file_hash = MD5HashFile(current_file_path).ToLower(); return (current_file_hash != update_data.latestHash) && AskAgreeUpdate(update_data); } internal bool InternalUpdate(string plugin_guid, bool no_ask) { try { var result = Serializer($"http://sync.mcbaka.com/api/Update/plugin/{plugin_guid}"); IO.CurrentIO.Write($"Fetched update: {result.name} by {result.author} [{plugin_guid}]"); var target = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins", result.fileName); if (ShouldDownloadUpdate(result, target, no_ask)) { IO.CurrentIO.Write($"Download: {result.downloadUrl}..."); if (!DownloadSingleFile(result.downloadUrl, target, result.fileName)) { throw new Exception("Download update files failed!"); } } else { IO.CurrentIO.Write(string.Format(LANG_VERSION_LATEST_OR_CANEL, result.name)); } return true; } catch (Exception e) { IO.CurrentIO.WriteColor(string.Format(LANG_UPDATE_ERROR, e.TargetSite.Name, e.Message), ConsoleColor.Yellow); } return false; } internal void InternalUpdate(IEnumerable update_plugins, bool no_ask) { foreach (var item in update_plugins) { InternalUpdate(item.getGuid(), no_ask); } if (update_plugins.Count() != 0) RequireRestart(LANG_UPDATE_DONE); } private bool Update(bool no_ask = false) { IEnumerable plugins = SyncHost.Instance.EnumPluings(); InternalUpdate(plugins, no_ask); return true; } private bool Update(string part_plugin_name, bool no_ask = false) { if (string.IsNullOrWhiteSpace(part_plugin_name)) return Update(no_ask); IEnumerable plugins = from plugin in SyncHost.Instance.EnumPluings() where plugin.Name.ToLower().Contains(part_plugin_name.ToLower()) select plugin; InternalUpdate(plugins, no_ask); return true; } private bool Search(string keyword) { try { var result = Serializer($"http://sync.mcbaka.com/api/Update/search/{keyword}"); foreach (var item in result) { IO.CurrentIO.WriteColor("Name", ConsoleColor.Cyan, false, false); IO.CurrentIO.WriteColor(item.name.PadRight(15), ConsoleColor.White, false, false); IO.CurrentIO.WriteColor("Author", ConsoleColor.DarkCyan, false, false); IO.CurrentIO.WriteColor(item.author.PadRight(15), ConsoleColor.White, false, false); IO.CurrentIO.WriteColor("Description", ConsoleColor.DarkCyan, false, false); IO.CurrentIO.WriteColor(item.description, ConsoleColor.White, true, false); IO.CurrentIO.WriteColor("GUID ", ConsoleColor.DarkCyan, false, false); IO.CurrentIO.WriteColor(item.guid.PadRight(32), ConsoleColor.White, true, false); IO.CurrentIO.WriteColor("===============", ConsoleColor.White, true, false); } return true; } catch (Exception e) { IO.CurrentIO.Write($"Error while {e.TargetSite.Name} : {e.Message}"); } return false; } internal bool InstallByKeyword(string keyword, bool requireRestart = true) { if (Serializer($"http://sync.mcbaka.com/api/Update/search/{keyword}") is UpdateData[] datas) { if (datas.Length == 0 || InternalUpdate(datas[0].guid, true)) { if (requireRestart) RequireRestart(LANG_INSTALL_DONE); return true; } else return false; } return false; } private bool Install(string guid) { if (InternalUpdate(guid, true)) { RequireRestart(LANG_INSTALL_DONE); return true; } else { return InstallByKeyword(guid); } } private bool Remove(string name) { var type = SyncHost.Instance.EnumPluings().FirstOrDefault(p => p.Name.ToLower().Contains(name.ToLower())); if (type == null) { IO.CurrentIO.WriteColor(string.Format(LANG_PLUGIN_NOT_FOUND, name), ConsoleColor.Red); return false; } else { var result = Serializer($"http://sync.mcbaka.com/api/Update/search/{name}")?[0]; var target = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins", Path.GetFileName(result.fileName)); if (File.Exists(target)) File.Delete(target); RequireRestart(LANG_REMOVE_DONE); return true; } } private bool List() { var list = SyncHost.Instance.EnumPluings(); foreach (var item in list) { IO.CurrentIO.WriteColor("Name", ConsoleColor.Cyan, false, false); IO.CurrentIO.WriteColor(item.Name.PadRight(25), ConsoleColor.White, false, false); IO.CurrentIO.WriteColor("Author", ConsoleColor.DarkCyan, false, false); IO.CurrentIO.WriteColor(item.Author.PadRight(20), ConsoleColor.White, false, false); var info = item.GetType().GetCustomAttribute(); IO.CurrentIO.WriteColor("Support Update:", ConsoleColor.DarkCyan, false, false); if (info != null) { IO.CurrentIO.WriteColor("Yes".PadRight(15), ConsoleColor.White, false, false); IO.CurrentIO.WriteColor("Ver:", ConsoleColor.DarkCyan, false, false); IO.CurrentIO.WriteColor(info.Version.PadRight(15), ConsoleColor.White, true, false); } else { IO.CurrentIO.WriteColor("No", ConsoleColor.White, true, false); } } return true; } internal bool SyncUpdateCheck(bool download = false) { try { IO.CurrentIO.WriteColor("Fetch Sync update..", ConsoleColor.Cyan); var result = Serializer($"http://sync.mcbaka.com/api/Update/latest"); if (!File.Exists(Updater.CurrentFullSourceEXEPath) || MD5HashFile(Updater.CurrentFullSourceEXEPath) != result.versionHash) { if (download) { IO.CurrentIO.Write($"Download: {result.downloadURL}..."); DownloadSingleFile(result.downloadURL, Updater.CurrentFullUpdateEXEPath, "Sync"); RequireRestart("Update downloaded. Restart to apply effect"); } else IO.CurrentIO.WriteColor($"There is a new version Sync! please visit https://github.com/OsuSync/Sync/releases/latest " + $", you can type \"plugins latest\" in Sync for automatically updating self.", ConsoleColor.Cyan); } else IO.CurrentIO.WriteColor("Sync update check done.Enjoy~", ConsoleColor.Cyan); } catch (Exception e) { IO.CurrentIO.WriteColor("Fetch Sync update info failed,please check your network if it can able to connect http://sync.mcbaka.com/", ConsoleColor.Red); return false; } return true; } private bool Help() { IO.CurrentIO.Write("Help for 'plugins' command:"); IO.CurrentIO.WriteHelp("install", "install [guid/name] Install plugin"); IO.CurrentIO.WriteHelp("remove", "remove [name] Remove plugin by name"); IO.CurrentIO.WriteHelp("search", "search [keyword] Search plugins"); IO.CurrentIO.WriteHelp("update", "Check update for Sync and Plugins"); IO.CurrentIO.WriteHelp("list", "List current installed plugins"); IO.CurrentIO.WriteHelp("latest", "Check latest Sync update"); return true; } private void RequireRestart(string msg) { IO.CurrentIO.WriteColor($"{msg}? (Y/N):", ConsoleColor.Green, false); var result = IO.CurrentIO.ReadCommand(); if (result.ToLower().StartsWith("y")) SyncHost.Instance.RestartSync(); } private T Serializer(string url) { WebClient web; web = new WebClient(); Random rd = new Random(); Stream data = web.OpenRead(url + "?rd=" + rd.Next()); DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T)); return (T)serializer.ReadObject(data); } private string MD5HashFile(string filePath) { FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); md5.ComputeHash(fs); byte[] b = md5.Hash; fs.Dispose(); md5.Clear(); StringBuilder sb = new StringBuilder(32); for (int i = 0; i < b.Length; i++) sb.Append(b[i].ToString("X2")); return sb.ToString(); } private bool DownloadSingleFile(string dlUrl, string path, string name) { try { //打开HTTP连接,获得文件长度 IO.CurrentIO.WriteColor($"Download {name} from {dlUrl}", ConsoleColor.Magenta); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3; HttpWebRequest Myrq = (HttpWebRequest)WebRequest.Create(dlUrl); HttpWebResponse myrp = (HttpWebResponse)Myrq.GetResponse(); long totalBytes = myrp.ContentLength; string convertdTotal = (totalBytes / 1024).ToString("0.00"); IO.CurrentIO.WriteHelp(name, $"Total: {convertdTotal} KB"); //判断是否存在已经下载的文件,如果存在,且长度相等,则直接解压 if (File.Exists(path + "_")) File.Delete(path + "_"); //如果存在目标缓存文件,就删掉它 Stream st = myrp.GetResponseStream(); //获得http流 Stream so = new FileStream(path + "_", FileMode.Create); //创建文件流 long totalDownloadedByte = 0; //已经下载的字节数量 long updateDownloadByte = 0; //已经计算下载速度的字节位置 long downloadspeed = 0; //当前下载速度 byte Stopwatch time = new Stopwatch(); //下载速度计时器 time.Reset(); time.Start(); byte[] buffer = new byte[1024]; //缓冲区 int osize = st.Read(buffer, 0, buffer.Length); //向缓冲区填入数据 while (osize > 0) //判断还剩没剩数据 { so.Write(buffer, 0, osize); //写入文件流 totalDownloadedByte += osize; //更新当前下载进度 osize = st.Read(buffer, 0, buffer.Length); //获得下一个缓冲区 if (time.ElapsedMilliseconds > 1000) //数据统计大于1000ms,则更新下载速度 { downloadspeed = totalDownloadedByte - updateDownloadByte; //这段时间已经下载的数据量 time.Restart(); //计时器重新开始计时 updateDownloadByte = totalDownloadedByte; //更新已经计算速度的数据位置 IO.CurrentIO.WriteHelp($"{(downloadspeed / 1024).ToString("0.0")}KB/s", $"{(totalDownloadedByte / 1024).ToString("0.0")}/{convertdTotal} KB");//UI更新 } } so.Close(); st.Close(); if (File.Exists(path)) { File.Delete(path); } File.Copy(path + "_", path); File.Delete(path + "_"); if (dlUrl.EndsWith("zip")) { var zip = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path) + ".zip"); if (File.Exists(zip)) File.Delete(zip); File.Move(path, zip); using (var archive = ZipFile.Open(zip, ZipArchiveMode.Update)) { foreach (ZipArchiveEntry entry in archive.Entries) { if (entry.Length == 0) continue; IO.CurrentIO.WriteHelp(entry.FullName, entry.Length.ToString()); try { string fileFullName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, entry.FullName); string directoryName = Path.GetDirectoryName(fileFullName); if (!Directory.Exists(directoryName)) Directory.CreateDirectory(directoryName); entry.ExtractToFile(fileFullName, true); } catch { } } } File.Delete(zip); } IO.CurrentIO.WriteColor($"[{name}] Done.", ConsoleColor.Green); return true; } catch (Exception e) { IO.CurrentIO.WriteColor($"Error while {e.TargetSite.Name} : {e.Message}", ConsoleColor.Red); return false; } } } } ================================================ FILE: Sync/Tools/CommandParser.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools { internal class CommandParser { //storage the commands parse to the command exector string[] args; List> parsedArgumentActions = new List>(); List> parsedExecuteActions = new List>(); Dictionary>> parseArgumentBinder; Dictionary>> parseActionBinder; int parseSharedIndex = 0; internal CommandParser(string[] args) : this(args, new Dictionary>>(), new Dictionary>>()) { } internal CommandParser(string[] args, Dictionary>> arguments, Dictionary>> actions) { this.args = args; this.parseArgumentBinder = arguments; this.parseActionBinder = actions; ParseCommands(); } //Parse commands form args to Actions public void ParseCommands() { try { foreach (var item in args) { ParseStringBlock($"{item} "); } } catch (Exception) { Console.WriteLine("Wrong argument"); } } private void ParseStringBlock(string value) { if (value.StartsWith("--")) { ParseArgumentFullArgs(value.TrimStart().TrimEnd()); } else if (value.StartsWith("-")) { ParseArgumentArgs(value.Substring(1)); } else { ParseExecuteArgs(value); } } private void ParseArgumentFullArgs(string value) { parsedArgumentActions.Add(parseArgumentBinder[value](value)); } private void ParseArgumentArgs(string value) { for (parseSharedIndex = 0; parseSharedIndex < value.Length - 1; parseSharedIndex++) { parsedArgumentActions.Add(parseArgumentBinder[value.Substring(parseSharedIndex, 1)](value)); } } private void ParseExecuteArgs(string value) { if (value.Length > 0) { parsedExecuteActions.Add(parseActionBinder[value.Substring(0, 1)](value)); } } #region ParseTools private string ReadLiteralString(string value) { string result = string.Empty; //is quote ", read until next quote " char current = value[++parseSharedIndex]; while (current != '"') { result += current; current = value[++parseSharedIndex]; } return result; } private string ReadNumberToString(string value) { string result = string.Empty; char current = value[parseSharedIndex++]; while (char.IsDigit(current)) { result += current; current = value[parseSharedIndex++]; } parseSharedIndex -= 2; return result; } private string GetSubArgument(string value) { char peek = value[++parseSharedIndex]; if (peek == '"') return ReadLiteralString(value); if (char.IsDigit(peek)) return ReadNumberToString(value); return string.Empty; } #endregion public void ExecuteActionOn(T instance) { foreach (var item in parsedArgumentActions) { item.Invoke(instance); } foreach (var item in parsedExecuteActions) { item.Invoke(instance); } } } } ================================================ FILE: Sync/Tools/Configuration.cs ================================================ using Sync.MessageFilter; using Sync.Tools.ConfigurationAttribute; namespace Sync.Tools { /// /// Default plugin confiuration /// public class DefaultConfiguration : IConfigurable { public const string DEFAULT_LANGUAGE = "LocalSettings"; [ClientList(NoCheck = true)] public ConfigurationElement Client { get; set; } = ""; [SourceList(NoCheck = true)] public ConfigurationElement Source { get; set; } = ""; public ConfigurationElement Language { get; set; } = ""; [Path(IsDirectory = true)] public ConfigurationElement LogDirectory { get; set; } = @"Logs\"; [String] public ConfigurationElement LogFilename { get; set; } = @"Log-{0}.txt"; [Bool] public ConfigurationElement EnableViewersChangedNotify { get; set; } = "False"; [Bool] public ConfigurationElement EnableGiftChangedNotify { get; set; } = "False"; [Bool] public ConfigurationElement CheckUpdateOnStartup { get; set; } = "True"; [List(ValueList = new[] { "Auto", "ForceAll", "OnlySendCommand", "DisableAll" }, IgnoreCase = true)] public ConfigurationElement MessageManagerDefaultOption { get; set; } = "Auto"; public void onConfigurationLoad() { MessageManager.SetOption(MessageManagerDefaultOption); } public void onConfigurationReload() { MessageManager.SetOption(MessageManagerDefaultOption); } public void onConfigurationSave() { MessageManagerDefaultOption = MessageManager.Option.ToString(); } public static readonly DefaultConfiguration Instance = new DefaultConfiguration(); private static readonly PluginConfigurationManager config = new PluginConfigurationManager("Sync"); static DefaultConfiguration() { config.AddItem(Instance); } } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/BaseConfigurationAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)] public abstract class BaseConfigurationAttribute : Attribute { public bool RequireRestart { get; set; } = false; public bool Hide { get; set; } public bool NoCheck { get; set; } public virtual string CheckFailedFormatMessage { get; set; } = "Parse error:{0}"; public abstract bool Check(string value); public void CheckFailedNotify(object obj) => IO.CurrentIO.WriteColor($"[Config]{string.Format(CheckFailedFormatMessage, obj.ToString())}", ConsoleColor.Red); } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/BoolAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { public class BoolAttribute : BaseConfigurationAttribute { public override bool Check(string value) => bool.TryParse(value,out _); } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/ClientAndSourceAttribute.cs ================================================ using Sync.Client; using Sync.Plugins; using Sync.Tools.ConfigurationAttribute; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { class ClientListAttribute : ListAttribute { public override string[] ValueList => ClientManager.Instance.Clients.Select(c=>c.ClientName).ToArray(); } class SourceListAttribute : ListAttribute { public override string[] ValueList => SyncHost.Instance.Sources.SourceList.Select(s => s.Name).ToArray(); } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/ColorAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { public class ColorAttribute : BaseConfigurationAttribute { //#RRGGBBAA public override bool Check(string rgba) { return rgba.Length == 9 && rgba[0] == '#' && byte.TryParse(rgba.Substring(1, 2), NumberStyles.HexNumber, null, out var _) && byte.TryParse(rgba.Substring(3, 2), NumberStyles.HexNumber, null, out var _) && byte.TryParse(rgba.Substring(5, 2), NumberStyles.HexNumber, null, out var _) && byte.TryParse(rgba.Substring(7, 2), NumberStyles.HexNumber, null, out var _); } } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/ConfigurationHolderAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false)] public class ConfigurationHolderAttribute:Attribute { public bool Hide { get; set; } = false; public bool NoCheck { get; set; } = false; } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/FloatAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { public class FloatAttribute : BaseConfigurationAttribute { public float MinValue { get; set; } = float.MinValue; public float MaxValue { get; set; } = float.MaxValue; public float Step { get; set; } = 1; public override bool Check(string o) { if (!float.TryParse(o, out float v)) return false; return (MinValue <= v && v <= MaxValue); } } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/FontAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { public class FontAttribute : BaseConfigurationAttribute { public override bool Check(string value) { return true; } } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/GuiLanguageElement.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { public struct GuiLanguageElement { private LanguageElement element; public GuiLanguageElement(string defaultVal) { element = new LanguageElement(defaultVal); } public static implicit operator GuiLanguageElement(string val) { return new GuiLanguageElement(val); } public static implicit operator string(GuiLanguageElement element) { return element.ToString(); } public override string ToString() { return element.ToString(); } } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/IntegerAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { public class IntegerAttribute : BaseConfigurationAttribute { public int MinValue { get; set; } = int.MinValue; public int MaxValue { get; set; } = int.MaxValue; public override bool Check(string i) { if (!int.TryParse(i, out int v)) return false; return (MinValue <= v && v <= MaxValue); } } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/ListAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { public class ListAttribute : BaseConfigurationAttribute { public virtual string[] ValueList { get; set; } = new string[] { }; public bool IgnoreCase { get; set; } = false; public bool AllowMultiSelect { get; set; } = false; public char SplitSeparator { get; set; } = ','; public override bool Check(string val) { var m_val = IgnoreCase ? val.ToLower() : val; if ((ValueList.Length != 0)) { if (AllowMultiSelect) { foreach (var str in m_val.Split(new[] { SplitSeparator }, StringSplitOptions.RemoveEmptyEntries)) { if (!ContainValue(str)) { CheckFailedNotify(val); return false; } } } else { if (!ContainValue(m_val)) { CheckFailedNotify(val); return false; } } } return true; } private bool ContainValue(string content) { return ValueList.Where((str) => (IgnoreCase ? str.ToLower() : str) == content).Count() != 0; } } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/PathAttribute.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { public class PathAttribute : BaseConfigurationAttribute { /// /// 是否钦定这路径是否必须存在,通常用于读取配置文件 /// public bool RequireExist { get; set; } = false; public bool IsDirectory { get; set; } = true; public override bool Check(string file_path) { if (RequireExist && (!(IsDirectory ? File.Exists(file_path) : Directory.Exists(file_path)))) { CheckFailedNotify(file_path); return false; } return true; } } } ================================================ FILE: Sync/Tools/ConfigurationAttribute/StringAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.ConfigurationAttribute { public class StringAttribute : BaseConfigurationAttribute { public override bool Check(string value) => true; } } ================================================ FILE: Sync/Tools/ConfigurationIO.cs ================================================ using System; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Sync.Tools { /// /// INI File reader /// static class ConfigurationIO { /// /// InI key /// public enum DefaultConfig { EnableViewersChangedNotify, EnableGiftChangedNotify, Client, Source, Language, LoggerFile, } private static FileSystemWatcher watcher; static ConfigurationIO() { watcher = new FileSystemWatcher(AppDomain.CurrentDomain.BaseDirectory); watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size; watcher.EnableRaisingEvents = true; watcher.Changed += (s, e) => { if (PluginConfigurationManager.InSaving) return; if (!e.Name.StartsWith("config.ini")) return; watcher.EnableRaisingEvents = false; Task.Run(()=> { Thread.Sleep(500); watcher.EnableRaisingEvents = true; }); Thread.Sleep(100); foreach (var item in PluginConfigurationManager.ConfigurationSet) { item.ReloadAll(); } Plugins.PluginEvents.Instance.RaiseEventAsync(new Plugins.PluginEvents.ConfigurationChange()); }; } [DllImport("kernel32")] private static extern int GetPrivateProfileString(string section, string key, string defVal, StringBuilder retVal, int size, string filePath); [DllImport("kernel32")] private static extern bool WritePrivateProfileString(string section, string key, string val, string filePath); /// /// Config path /// public readonly static string ConfigFile = AppDomain.CurrentDomain.BaseDirectory + "config.ini"; /// /// The ini read buffer(thread safe) /// private readonly static ThreadLocal iniReadBuffer = new ThreadLocal(()=>new StringBuilder(65535)); /// /// Read value /// /// Key /// Section /// Value internal static string IniReadValue(string FilePath, string key, string column = "config") { StringBuilder temp = iniReadBuffer.Value; GetPrivateProfileString(column, key, "", temp, 65535, FilePath); return temp.ToString(); } internal static bool IniWriteValue(string FilePath, string key, string value, string column = "config") { return WritePrivateProfileString(column, key, value, FilePath); } /// /// Read value /// /// Key /// Section /// Value public static string Read(string key, string column = "config") { return IniReadValue(ConfigFile, key, column); } /// /// Read via enum /// /// Key /// Value public static string ReadConfig(DefaultConfig key) { return IniReadValue(ConfigFile, Enum.GetName(typeof(DefaultConfig), key)); } /// /// Write to /// /// Key /// Value /// Section /// public static bool Write(string key, string value, string column = "config") { return IniWriteValue(ConfigFile, key, value, column); } /// /// Write value via enum /// /// Key /// value /// public static bool WriteConfig(DefaultConfig key, string value) { return Write(Enum.GetName(typeof(DefaultConfig), key), value); } } } ================================================ FILE: Sync/Tools/I18n.cs ================================================ using Sync.Tools.ConfigurationAttribute; using System; using System.Collections.Generic; using System.IO; using System.Reflection; namespace Sync.Tools { public class DefaultI18n : I18nProvider { public static LanguageElement LANG_Loading = "读取中...."; public static LanguageElement LANG_Plugins = "已载入 {0:D} 个 插件"; public static LanguageElement LANG_Sources = "已载入 {0:D} 个 直播源"; public static LanguageElement LANG_Client = "已载入 {0:D} 个 Client"; public static LanguageElement LANG_Error = "不能初始化连接器,请确认是否已经安装直播源."; public static LanguageElement LANG_Commands = "已载入 {0:D} 个 命令"; public static LanguageElement LANG_Filters = "已载入 {0:D} 个 过滤器"; public static LanguageElement LANG_Ready = "准备就绪。"; public static LanguageElement LANG_RqueireLogin = "请登录到RnW账号"; public static LanguageElement LANG_AccountName = "用户名:"; public static LanguageElement LANG_AccountPw = "密码:"; public static LanguageElement LANG_AccountSave = "账户保存成功! 将开始连接到服务器"; public static LanguageElement LANG_Start = "开始工作...."; public static LanguageElement LANG_Stopping = "停止工作..."; public static LanguageElement LANG_Restarting = "重新开始工作..."; public static LanguageElement LANG_LoadingPlugin = "载入 {0:S} 中..."; public static LanguageElement LANG_LoadPluginErr = "不能载入 {0:S} ({1:S})"; public static LanguageElement LANG_NotPluginErr = "插件{0:S} 非osuSync插件 ({1:S})"; public static LanguageElement LANG_NotConfig = "请配置 'config.ini' 后再开始进行同步操作。"; public static LanguageElement LANG_NoSource = "无法找到任何直播源!请安装一个直播源。"; public static LanguageElement LANG_MissSource = "找不到默认匹配的直播源,直接使用第一个。"; public static LanguageElement LANG_SetSource = "设置 {0:S} 为直播弹幕源"; public static LanguageElement LANG_SupportSend = "提示:当前弹幕源支持游戏内发送到弹幕源的功能,请输入login [用户名] [密码] 来登录!(用户名、密码二者可选输入)"; public static LanguageElement LANG_CertLength = "Certification长度: {0:D}"; public static LanguageElement LANG_CertExist = "提示:当前已有登录Certification记录,如需覆盖,请输入login [用户名] [密码]进行覆盖!(用户名密码可选输入)"; public static LanguageElement LANG_SendNotReady = "当前Client未标志弹幕发送可用,请尝试使用Login登录"; public static LanguageElement LANG_UnknowCommand = "未知命令! 请输入help查看命令列表。"; public static LanguageElement LANG_CommandFail = "命令执行失败! 请输入help查看命令列表。"; public static LanguageElement LANG_ConfigFile = "配置文件"; public static LanguageElement LANG_UserCount = "用户总数变更: {0:D}"; public static LanguageElement LANG_UserCount_Change = "直播间围观人数{0:S}到{1:D}人"; public static LanguageElement LANG_UserCount_Change_Increase = "增加"; public static LanguageElement LANG_UserCount_Change_Decrease = "减少"; public static LanguageElement LANG_Source_Disconnecting = "正在断开弹幕源服务器的连接...."; public static LanguageElement LANG_Source_Disconnected = "服务器连接被断开,3秒后重连!"; public static LanguageElement LANG_Source_Disconnected_Succ = "源服务器断开连接成功!"; public static LanguageElement LANG_Source_Connect = "正在连接弹幕源服务器...."; public static LanguageElement LANG_Source_Connected_Succ = "源服务器连接成功!"; public static LanguageElement LANG_Current_Online = "当前在线人数: {0:D}"; public static LanguageElement LANG_Gift_Sent = "我送给你{O:D}份{1:S}!"; public static LanguageElement LANG_Config = "配置文件: "; public static LanguageElement LANG_Config_Status_OK = "OK, 房间ID:{0}"; public static LanguageElement LANG_Config_Status_Fail = "尚未配置成功"; public static LanguageElement LANG_Source = "源{0:S}: "; public static LanguageElement LANG_IRC = "Client:"; public static LanguageElement LANG_Danmaku = "弹幕发送:"; public static LanguageElement LANG_Status_Connected = "已连接"; public static LanguageElement LANG_Status_NotConenct = "未连接"; public static LanguageElement LANG_Loading_Config = @"正在读取配置文件....\n"; public static LanguageElement LANG_Welcome = "欢迎使用 osu直播弹幕同步工具 ver {0:S} "; public static LanguageElement LANG_Help = @"输入 'help' 获得帮助列表\n\n"; public static LanguageElement LANG_Command = "命令"; public static LanguageElement LANG_Command_Description = "描述"; public static LanguageElement LANG_MsgMgr_Limit = "当前消息管理器 开始 管制,只有?send命令的内容才会发送到irc频道"; public static LanguageElement LANG_MsgMgr_Free = "当前消息管理器 解除 管制,内容可以直接发送到irc频道"; public static LanguageElement LANG_Plugin_Cycle_Reference = "发现插件之间的循环引用关系,插件 {0:S} 将不会按照开发者指定的依赖关系进行加载"; //from default plugin public static LanguageElement LANG_COMMANDS_LOGIN = "login [pass] 登录到目标弹幕网站,启动弹幕发送功能"; public static LanguageElement LANG_COMMANDS_EXIT = "退出软件"; public static LanguageElement LANG_COMMANDS_CLEAR = "清空屏幕"; public static LanguageElement LANG_COMMANDS_STATUS = "获得当前连接状态属性"; public static LanguageElement LANG_COMMANDS_STOP = "停止当前连接"; public static LanguageElement LANG_COMMANDS_START = "开始同步"; public static LanguageElement LANG_COMMANDS_HELP = "打印帮助信息"; public static LanguageElement LANG_COMMANDS_SOURCEMSG = "danmaku 发送弹幕测试"; public static LanguageElement LANG_COMMANDS_CLIENTMSG = "chat 发送IRC信息测试"; public static LanguageElement LANG_COMMANDS_CLIENTUSERMSG = "chatuser 按照username名字发送IRC信息测试"; public static LanguageElement LANG_COMMANDS_EXIT_DONE = "退出操作已完成,如果窗口还未关闭,您可以强制关闭。"; public static LanguageElement LANG_COMMANDS_SOURCES = "获得当前所有弹幕源列表"; public static LanguageElement LANG_COMMANDS_MSGMGR = "查看或者设置消息控制器相关内容,添加--help参数获取帮助"; public static LanguageElement LANG_COMMANDS_FILTERS = "列表所有当前可用消息过滤器"; public static LanguageElement LANG_COMMANDS_DISABLE = "向插件发送禁用消息 disable (插件名称)"; public static LanguageElement LANG_COMMANDS_SWITCH_CLIENT = "切换到指定Client实例,不带名称则为获取Client列表"; public static LanguageElement LANG_COMMANDS_SOURCELOGIN = "登录到弹幕源 sourcelogin [用户名] [密码]"; public static LanguageElement LANG_COMMANDS_RESTART = "重新启动应用程序"; public static LanguageElement LANG_COMMANDS_LANG = "lang [cultureName] Get/Set language"; public static LanguageElement LANG_COMMANDS_LISTLANG = "listlang [--all] List (supported/all) languages"; public static LanguageElement LANG_COMMANDS_FILTERS_ITEM = "过滤项"; public static LanguageElement LANG_COMMANDS_FILTERS_OBJ = "过滤器"; public static LanguageElement LANG_COMMANDS_CLIENT_NAME = "Client"; public static LanguageElement LANG_COMMANDS_CLIENT_AUTHOR = "作者"; public static LanguageElement LANG_COMMANDS_SOURCES_NAME = "弹幕源"; public static LanguageElement LANG_COMMANDS_SOURCES_AUTHOR = "作者"; public static LanguageElement LANG_COMMANDS_CURRENT = "当前设置为 {0:S}"; public static LanguageElement LANG_COMMANDS_DANMAKU_NOT_SUPPORT = @"提示:当前弹幕源不支持发送弹幕,请更换弹幕源!\n"; public static LanguageElement LANG_COMMANDS_CHAT_IRC_NOTCONNECT = "osu! irc 尚未连接,您还不能发送消息。"; public static LanguageElement LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN = "你必须登录才能发送弹幕!"; public static LanguageElement LANG_COMMANDS_START_ALREADY_RUN = "同步实例已经在运行。"; public static LanguageElement LANG_COMMANDS_ARGUMENT_WRONG = "参数不正确"; public static LanguageElement LANG_COMMANDS_MSGMGR_HELP = @"\n--status :查看当前消息管理器的信息\n--limit <数值> :是设置限制发送信息的等级,越低就越容易触发管控\n--option <名称> :是设置管控的方式,其中Auto是自动管控,ForceAll强行全都发送,ForceLimit是仅发送使用?send命令的消息,DisableAll是拦截任何管道内的信息"; public static LanguageElement LANG_COMMANDS_MSGMGR_LIMIT = "限制中..."; public static LanguageElement LANG_COMMANDS_MSGMGR_FREE = "无限制"; public static LanguageElement LANG_COMMANDS_MSGMGR_STATUS = "MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}"; public static LanguageElement LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET = "设置限制发送速度等级为{0}"; public static LanguageElement LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET = "设置消息管理器的管制方式为{0}"; public static LanguageElement LANG_COMMANDS_START_NO_SOURCE = "还未钦定任何一个接收源"; public static LanguageElement LANG_COMMANDS_START_NO_CLIENT = "还未钦定任何一个发送源"; public static LanguageElement LANG_COMMANDS_CURRENT_LANG = "当前语言: {0:S}\t{1:S}"; public static LanguageElement LANG_COMMANDS_LANG_SWITCHED = "成功切换语言至 {1:S}({0:S})"; public static LanguageElement LANG_COMMANDS_LANG_NOT_FOUND = "切换语言失败,请检查语言代码参数是否正确"; public static LanguageElement LANG_UPDATE_DONE = "更新完成,是否重启软件"; public static LanguageElement LANG_INSTALL_DONE = "下载完成,是否重启软件"; public static LanguageElement LANG_PLUGIN_NOT_FOUND = "插件 {0} 不存在"; public static LanguageElement LANG_REMOVE_DONE = "删除成功,是否重启软件"; public static LanguageElement LANG_VERSION_LATEST_OR_CANEL = "{0} 已是最新,或者已被用户取消更新操作"; public static LanguageElement LANG_UPDATE_CHECK_ERROR = "无法根据 [{0}] 检查更新 : {1} : {2}"; public static LanguageElement LANG_UPDATE_ERROR = "无法更新 : {0} : {1}"; public static LanguageElement LANG_SOURCE_NOT_SUPPORT_SEND = "接收源 {0} 并不支持发送功能"; public static LanguageElement LANG_NO_PLUGIN_SELECT = "还未钦定插件名称"; public static LanguageElement LANG_PLUGIN_DISABLED = "已禁用 "; public static LanguageElement LANG_NO_ANY_SOURCE = "没有任何弹幕接收源,请检查Plugins目录或使用\"plugins install DefaultPlugin\"来安装默认插件"; public static LanguageElement LANG_Instance_Exist = "只能存在一个Sync进程,等待上一个Sync结束"; } public interface I18nProvider { } public struct LanguageElement { private string value; public LanguageElement(string defaultVal) { value = defaultVal; } public static implicit operator LanguageElement(string val) { return new LanguageElement(val); } public static implicit operator string(LanguageElement element) { return element.value; } public override string ToString() { return value; } } /// /// I18n Manager /// public class I18n { public static string CurrentSystemLang { get => System.Globalization.CultureInfo.CurrentCulture.Name; } private string Base { get => AppDomain.CurrentDomain.BaseDirectory; } public string LangFolder { get => Path.Combine(Base, "Language"); } public string SelectLangFolder { get => Path.Combine(LangFolder, CurrentLanguage); } public string CurrentLanguage; private static I18n instance; private static List ApplyedProvider = new List(); public static I18n Instance { get { if (instance == null) { if (DefaultConfiguration.Instance.Language == DefaultConfiguration.DEFAULT_LANGUAGE || DefaultConfiguration.Instance.Language.ToString().Length == 0) { instance = new I18n(CurrentSystemLang); DefaultConfiguration.Instance.Language = CurrentSystemLang; } else { instance = new I18n(DefaultConfiguration.Instance.Language); } } return instance; } private set { instance = value; } } public static void SwitchToCulture(string CultureName) { Instance = new I18n(CultureName); foreach (var item in ApplyedProvider) { Instance.ApplyLanguage(item); } } private I18n() { } /// /// Constructor for initial one language /// /// Cultura name private I18n(string CultureName) { CurrentLanguage = CultureName; if (!Directory.Exists(LangFolder)) Directory.CreateDirectory(LangFolder); if (!Directory.Exists(SelectLangFolder)) Directory.CreateDirectory(SelectLangFolder); } public void ApplyLanguage(I18nProvider instance) { if (!ApplyedProvider.Exists(p => p == instance)) ApplyedProvider.Add(instance); string LangFile = Path.Combine(SelectLangFolder, instance.GetType().FullName) + ".lang"; foreach (FieldInfo item in instance.GetType().GetFields()) { if (item.FieldType.Equals(typeof(LanguageElement))) { string value = ConfigurationIO.IniReadValue(LangFile, item.Name, CurrentLanguage); if (value == "") { value = (LanguageElement)item.GetValue(instance); ConfigurationIO.IniWriteValue(LangFile, item.Name, value, CurrentLanguage); } item.SetValue(instance, new LanguageElement(value)); } else if (item.FieldType.Equals(typeof(GuiLanguageElement))) { string value = ConfigurationIO.IniReadValue(LangFile, item.Name, CurrentLanguage); if (value == "") { value = (GuiLanguageElement)item.GetValue(instance); ConfigurationIO.IniWriteValue(LangFile, item.Name, value, CurrentLanguage); } item.SetValue(instance, new GuiLanguageElement(value)); } } } public override string ToString() => $"CurrentLanguage={CurrentLanguage}"; } } ================================================ FILE: Sync/Tools/IConfigurable.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools { public interface IConfigurable { /// /// Invoke when load configuration /// void onConfigurationLoad(); /// /// Invoke when save configuration /// void onConfigurationSave(); /// /// Detected change form files and reload values /// void onConfigurationReload(); } } ================================================ FILE: Sync/Tools/PluginConfiuration.cs ================================================ using Sync.Plugins; using Sync.Tools; using Sync.Tools.ConfigurationAttribute; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Sync.Tools { public sealed class ConfigurationElement { private string _cfg = string.Empty; public ConfigurationElement() { } public ConfigurationElement(string def) { _cfg = def; } public static implicit operator string(ConfigurationElement e) { return e?._cfg??string.Empty; } public static implicit operator ConfigurationElement(string e) { return new ConfigurationElement(e); } public override string ToString() { return _cfg; } public bool ToBool() => _cfg.ToLower() == "true"; public int ToInt() => int.Parse(_cfg); public float ToFloat() => float.Parse(_cfg); } /// /// Plugins configuration service, create instance to get configuration service /// internal sealed class PluginConfiuration { internal string name; internal Plugin instance; internal IConfigurable config; public PluginConfiuration(Plugin instance, IConfigurable config):this(instance.Name, config) { this.instance = instance; } internal PluginConfiuration(string name, IConfigurable config) { this.name = name; this.config = config; ForceLoad(); } ~PluginConfiuration() { ForceSave(); } internal void Load() { var configType = config.GetType(); var holderAttribute = configType.GetCustomAttribute(); foreach (PropertyInfo item in configType.GetProperties()) { if (item.PropertyType == typeof(ConfigurationElement)) { ConfigurationElement element = ConfigurationIO.Read(item.Name, name + "." + config.GetType().Name/*,item.GetValue(config).ToString()*/); if (!string.IsNullOrWhiteSpace(element)) { if (CheckValueVaild(item, element, holderAttribute)) { item.SetValue(config, element); } } else { //if not exist,write to config.ini immediately ConfigurationIO.Write(item.Name, (ConfigurationElement)item.GetValue(config), name + "." + config.GetType().Name); } } } } internal void ForceLoad() { Load(); config.onConfigurationLoad(); } private bool CheckValueVaild(PropertyInfo info, ConfigurationElement element, ConfigurationHolderAttribute classHolder) { var configAttribute = info.GetCustomAttribute(); bool noCheck = configAttribute?.NoCheck ?? classHolder?.NoCheck ?? true; if (configAttribute == null) return true; if (!noCheck) if (!configAttribute.Check(element)) { configAttribute.CheckFailedNotify(element); return false; } return true; } internal void ForceReload() { Load(); config.onConfigurationReload(); } internal void ForceSave() { config.onConfigurationSave(); foreach (PropertyInfo item in config.GetType().GetProperties()) { if (item.PropertyType == typeof(ConfigurationElement)) { ConfigurationIO.Write(item.Name, (ConfigurationElement)item.GetValue(config), name + "." + config.GetType().Name); } } } } /// /// Configuration Manager /// public sealed class PluginConfigurationManager { internal static ConcurrentBag ConfigurationSet = new ConcurrentBag(); internal static bool InSaving = false; internal List items; internal Plugin instance; internal string name; public PluginConfigurationManager(Plugin plugin):this(plugin.Name) { instance = plugin; } internal PluginConfigurationManager(string name) { items = new List(); this.name = name; ConfigurationSet.Add(this); } public void AddItem(IConfigurable Config) { items.Add(new PluginConfiuration(name, Config)); } internal void ReloadAll() { foreach (var item in items) { item.ForceReload(); } } public void SaveAll() { InSaving = true; foreach (var item in items) { item.ForceSave(); } InSaving = false; } //public PluginConfiuration GetInstance(IConfigurable obj) //{ // foreach (var item in items) // { // if (item.GetType() == obj.GetType()) return item; // } // return null; //} ~PluginConfigurationManager () => SaveAll(); } } ================================================ FILE: Sync/Tools/SentryHelper.cs ================================================ using SharpRaven; using SharpRaven.Data; using Sync.Plugins; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Sync.Tools { /// /// A standard error reporter for plugins /// /// Plugin Error impl public abstract class ErrorRepoter where T : Plugin { private Action errorHandler; public ErrorRepoter() { errorHandler = SentryHelper.Instance.RegisterErrorReporter(typeof(T)); } public void ReportException(Exception e) => errorHandler(e); } /// /// Sentry helper for error reporter /// internal class SentryHelper { private RavenClient ravenClient = new RavenClient("https://955afb316e2c4a2eaa4070c18071b0c0@sentry.io/1276032"); private Dictionary registedErrorReporter = new Dictionary(); #if SyncRelease private const bool notInDebugger = true; #else private const bool notInDebugger = false; #endif private SentryHelper() { } internal Action RegisterErrorReporter(Type type) { if (type == null || !type.IsClass || !type.IsPublic || !typeof(Plugin).IsAssignableFrom(type) || typeof(Plugin) == type) { return (x) => Console.WriteLine("Type must inherit Plugin class"); } else { var logger = type.FullName; var version = type.Assembly.GetName().Version.ToString(); return (x) => this.Error(logger, version, x); } } internal void Error(string logger, string version, Exception e, bool silent = false) { ravenClient.Logger = logger; ravenClient.Release = version; var error = new SentryEvent(e); if (notInDebugger) { if (!silent) Console.WriteLine("Opps! You seem occur a error! We was captured this error and repoting to developers"); ravenClient.Capture(error); } else { Console.WriteLine("A error was raised but not repoted to Sync developer"); Console.WriteLine("Now a error report will print, you can report in github issue"); Console.WriteLine(error.Message.ToString()); Console.WriteLine($"Scope: {logger}, Version: {version}"); Console.WriteLine("- TRACE -"); Console.WriteLine(e.StackTrace); } } internal void RepoterError(Exception e, bool silent = false) => Error("Sync", Assembly.GetEntryAssembly().GetName().Version.ToString(), e, silent); internal static readonly SentryHelper Instance = new SentryHelper(); } } ================================================ FILE: Sync/Tools/StartupArgument.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools { internal static class StartupArgument { private static Action ForceStartArg(string arg) => (_) => StartupHelper.ForceStart = true; private static Action NeedUpdateSyncArg(string arg) => (_) => StartupHelper.NeedUpdateSync = true; internal static Dictionary>> Arguments = new Dictionary>>() { { "f", ForceStartArg }, { "--force-start", ForceStartArg }, { "u", NeedUpdateSyncArg }, { "--update", NeedUpdateSyncArg } }; internal static Dictionary>> Actions = new Dictionary>>(); } } ================================================ FILE: Sync/Tools/StartupHelper.cs ================================================ using Sync.Tools; using System; using System.Linq; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.MemoryMappedFiles; using System.Runtime.InteropServices; using System.Threading; using static Sync.Tools.IO; namespace Sync.Tools { public class StartupHelper { public const string SYNC_GUID = "{781d2da2-1b44-46d9-8b01-e1d59adc018b}"; internal delegate bool ControlCtrlDelegate(int CtrlType); [DllImport("kernel32.dll")] internal static extern bool SetConsoleCtrlHandler(ControlCtrlDelegate HandlerRoutine, bool Add); internal static ControlCtrlDelegate cancelHandler = new ControlCtrlDelegate(HandlerRoutine); private static bool HandlerRoutine(int CtrlType) { CurrentIO.Write("Exiting..."); SyncHost.Instance?.SaveSync(); CurrentIO.Write("Saved."); SetConsoleCtrlHandler(cancelHandler, false); return true; } private void SyncInstanceLocker(MemoryMappedFile syncMappedFile, bool forceExit) { using (var syncViewStream = syncMappedFile.CreateViewStream()) { int pid = 0; using (var reader = new BinaryReader(syncViewStream)) { pid = reader.ReadInt32(); if (pid != 0) { var oldSync = Process.GetProcesses().FirstOrDefault((proc) => proc.Id == pid); if (oldSync != null) { if (forceExit) { oldSync.Kill(); } else { CurrentIO.WriteColor(DefaultI18n.LANG_Instance_Exist, ConsoleColor.Red); int limit = 50, current = 0; while (!oldSync.WaitForExit(100)) { current++; if (current > limit) break; } if (!oldSync.WaitForExit(100)) { oldSync.Kill(); } } } } using (var writer = new BinaryWriter(syncViewStream)) { writer.Seek(0, SeekOrigin.Begin); writer.Write(Process.GetCurrentProcess().Id); } } } } static void PreInitSync() { //Initialize I18n I18n.Instance.ApplyLanguage(new DefaultI18n()); } static void InitSync() { //Apply update if (Updater.ApplyUpdate(NeedUpdateSync)) Environment.Exit(0); //Add Console close event handler SetConsoleCtrlHandler(cancelHandler, true); //Initialize Sync core SyncHost.Instance = new SyncHost(); SyncHost.Instance.Load(); //Sync ready message to all plugins SyncHost.Instance.Plugins.ReadySync(); //Check update if (DefaultConfiguration.Instance.CheckUpdateOnStartup.ToBool()) Updater.update.SyncUpdateCheck(); //Sync program update check if (Updater.IsUpdated) CurrentIO.WriteColor("Sync is already up to date!", ConsoleColor.Green); } public static bool ForceStart { get; internal set; } public static bool NeedUpdateSync { get; internal set; } internal StartupHelper(string[] args) { (new CommandParser(args, StartupArgument.Arguments, StartupArgument.Actions)).ExecuteActionOn(this); } internal void Start() { //Check sync.exe is run using (var syncMappedFile = MemoryMappedFile.CreateOrOpen(SYNC_GUID, 4)) { PreInitSync(); SyncInstanceLocker(syncMappedFile, ForceStart); InitSync(); CurrentIO.WriteWelcome(); while (true) { var cmd = CurrentIO.ReadCommand(); SyncHost.Instance.Commands.invokeCmdString(cmd); } } } } } ================================================ FILE: Sync/Tools/StringElement.cs ================================================ using System; namespace Sync.Tools { /// /// A helper for a string with a perfix and a suffix /// public struct StringElement { public string perfix; public string str; public string suffix; public StringElement(string perfix, string text, string suffix) { this.perfix = perfix; str = text; if (str == null) str = string.Empty; this.suffix = suffix; } public StringElement(string perfix, string text) { this.perfix = perfix; str = text; if (str == null) str = string.Empty; suffix = string.Empty; } public StringElement(string source) { str = source; if (str == null) str = string.Empty; perfix = string.Empty; suffix = string.Empty; } public string Result { get { return str.Length == 0 ? String.Empty : perfix + str + suffix; } } public string RawText { get { return str; } } public string Perfix { get { return perfix; } } public string Suffix { get { return suffix; } } public static StringElement operator +(StringElement op1, StringElement op2) { if (op1 == null) return op2; if (op2 == null) return op1; return new StringElement(op1.perfix + op2.perfix, op1.str + op2.str, op1.suffix + op2.suffix); } public static string operator +(StringElement op1, string op2) { return op1.Result + op2; } public static implicit operator StringElement(string e) { return new StringElement(e); } public static implicit operator string(StringElement e) { return e.Result; } public override string ToString() { return Result; } } } ================================================ FILE: Sync/Tools/SyncIO/ConsoleWriter.cs ================================================ using System; namespace Sync.Tools { [Obsolete] public interface ISyncIO : ISyncOutput, ISyncInput { } public interface ISyncConsoleWriter : ISyncOutput, ISyncInput { } public interface ISyncOutput { void Write(string msg, bool newline = true, bool time = true); void WriteColor(string text, ConsoleColor color, bool newline = true, bool time = true); void WriteHelp(string cmd, string desc); void WriteHelp(); void WriteStatus(); void WriteWelcome(); void Clear(); } public interface ISyncInput { string ReadCommand(); } public static class IO { public static readonly NConsoleWriter DefaultIO = new NConsoleWriter(); public static readonly FileLoggerWriter FileLogger; public static readonly IOWrapper CurrentIO = new IOWrapper(); [Obsolete("Obsoleted, instead with AddOutput and SetInput", true)] public static void SetIO(ISyncIO specIO) { CurrentIO.SetInput(specIO); CurrentIO.AddOutput(specIO); } public static void SetIO(ISyncConsoleWriter specIO) { CurrentIO.SetInput(specIO); CurrentIO.AddOutput(specIO); } static IO() { SetIO(DefaultIO); try { FileLogger = new FileLoggerWriter(); AddOutput(FileLogger); } catch { DefaultIO.Write("Initial File Logger failed!!"); } } public static void AddOutput(ISyncOutput output) => CurrentIO.AddOutput(output); public static void SetInput(ISyncInput input) => CurrentIO.SetInput(input); } } ================================================ FILE: Sync/Tools/SyncIO/FileLoggerWriter.cs ================================================ using System; using System.IO; using static Sync.Tools.DefaultI18n; namespace Sync.Tools { public class FileLoggerWriter : ISyncOutput { private StreamWriter logger; internal FileLoggerWriter() { if (DefaultConfiguration.Instance.LogDirectory == "") { DefaultConfiguration.Instance.LogDirectory = @"Logs\"; } if(DefaultConfiguration.Instance.LogFilename == "") { DefaultConfiguration.Instance.LogFilename = "Log-{0}.txt"; } string date = DateTime.Now.ToString("yyyy-MM-dd@hh.mm.ss"); string log = string.Format(Path.Combine(DefaultConfiguration.Instance.LogDirectory, DefaultConfiguration.Instance.LogFilename), date); if (!Path.IsPathRooted(log)) log = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,log); Directory.CreateDirectory(Path.GetDirectoryName(log)); logger = new StreamWriter(File.Open(log, FileMode.OpenOrCreate, FileAccess.Write)) { AutoFlush = true }; } public void Clear() { } public void Write(string msg, bool newline = true, bool time = true) { string ms = System.Text.RegularExpressions.Regex.Replace(msg, @"\\t|\\n", m => { switch (m.ToString()) { case @"\t": return "\t"; case @"\n": return "\n"; } return m.ToString(); }); logger.Write((time ? "[" + DateTime.Now.ToLongTimeString() + "] " : "") + ms + (newline ? "\n" : "")); } public void WriteColor(string text, ConsoleColor color, bool newline = true, bool time = true) { Write(text, newline, time); } public void WriteHelp(string cmd, string desc) { } public void WriteHelp() { } public void WriteStatus() { } public void WriteWelcome() { Write(string.Format(LANG_Welcome, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString())); Write(LANG_Help); } } } ================================================ FILE: Sync/Tools/SyncIO/IOWrapper.cs ================================================ using System; using System.Collections.Generic; namespace Sync.Tools { public sealed class IOWrapper : ISyncConsoleWriter { private ISyncInput currI; private List currOs = new List(); public void Clear() => currOs.ForEach(p => p.Clear()); public string ReadCommand() => currI.ReadCommand(); public void Write(string msg, bool newline = true, bool time = true) => currOs.ForEach(p => p.Write(msg, newline, time)); public void WriteColor(string text, ConsoleColor color, bool newline = true, bool time = true) => currOs.ForEach(p => p.WriteColor(text, color, newline, time)); public void WriteHelp(string cmd, string desc) => currOs.ForEach(p => p.WriteHelp(cmd, desc)); public void WriteHelp() => currOs.ForEach(p => p.WriteHelp()); public void WriteStatus() => currOs.ForEach(p => p.WriteStatus()); public void WriteWelcome() => currOs.ForEach(p => p.WriteWelcome()); internal void SetInput(ISyncInput input) { this.currI = input; } internal void AddOutput(ISyncOutput output) { if (currOs.Contains(output)) return; currOs.Add(output); } } } ================================================ FILE: Sync/Tools/SyncIO/Logger.cs ================================================ using System; namespace Sync.Tools { public enum LogType { Infomation, Warning, Error, } public class Logger { private static ConsoleColor[] LOGTYPE_COLORS = new[] { ConsoleColor.Green, ConsoleColor.Yellow, ConsoleColor.Red }; private readonly string prefix; private readonly ISyncOutput output; public Logger(string prefix, ISyncOutput output = null) { this.prefix = prefix; this.output = output ?? IO.CurrentIO; } public void Log(string message, LogType type) => output?.WriteColor($"[{prefix}]{message}", LOGTYPE_COLORS[(int)type]); public void LogInfomation(string message) => Log(message, LogType.Infomation); public void LogWarning(string message) => Log(message, LogType.Warning); public void LogError(string message) => Log(message, LogType.Error); } public class Logger : Logger { public Logger(ISyncOutput output = null) : base(typeof(T).Name, output) { } } } ================================================ FILE: Sync/Tools/SyncIO/NConsoleWriter.cs ================================================ using System; using static Sync.Tools.DefaultI18n; namespace Sync.Tools { public class NConsoleWriter : ISyncConsoleWriter, ISyncOutput, ISyncInput { private bool wait = false; /// /// Read Line /// /// Input chars public string ReadCommand() { WriteColor(">", ConsoleColor.Green, false, false); wait = true; return Console.ReadLine(); } /// /// Write a message to console /// /// Message /// Display in new line public void Write(string msg, bool newline = true, bool time = true) { if (wait) { wait = false; Console.SetCursorPosition(0, Console.CursorTop); } string ms = System.Text.RegularExpressions.Regex.Replace(msg, @"\\t|\\n", m => { switch (m.ToString()) { case @"\t": return "\t"; case @"\n": return "\n"; } return m.ToString(); }); Console.Write((time ? "[" + DateTime.Now.ToLongTimeString() + "] " : "") + ms + (newline ? "\n" : "")); } /// /// Write a message with color /// /// Message /// Color /// Display in new line public void WriteColor(string text, ConsoleColor color, bool newline = true, bool time = true) { Console.ForegroundColor = color; Write(text, newline, time); Console.ResetColor(); } /// /// Write a formated help message /// /// 命令 /// 命令描述 public void WriteHelp(string cmd, string desc) { WriteColor(cmd.PadRight(10), ConsoleColor.Cyan, false, false); WriteColor(desc, ConsoleColor.White, true, false); } /// /// Write current work status /// public void WriteStatus() { WriteColor("Source:" + SyncHost.Instance.SourceWrapper.Source?.Status.ToString(), ConsoleColor.Magenta); WriteColor("Client:" + SyncHost.Instance.ClientWrapper.Client?.CurrentStatus.ToString(), ConsoleColor.Magenta); } /// /// Write welcolme message /// public void WriteWelcome() { Write(string.Format(LANG_Welcome, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString())); Write(LANG_Help); } /// /// Write all commands /// public void WriteHelp() { WriteHelp(LANG_Command, LANG_Command_Description); WriteHelp("======", "======"); foreach (var item in SyncHost.Instance.Commands.Dispatch.getCommandsHelp()) { WriteHelp(item.Key, item.Value); } WriteHelp("======", "======"); Write("", true, false); } /// /// Clear screen /// public void Clear() { Console.Clear(); } } } ================================================ FILE: Sync/Tools/Updater.cs ================================================ using Sync.Tools.Builtin; using Sync.Tools; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools { static class Updater { public const string SourceEXEName = "Sync.exe"; public const string UpdateEXEName = "Sync_update.exe"; public const string UpdateArg = "--update"; public static readonly string CurrentEXEName = Path.GetFileName(Process.GetCurrentProcess().Modules[0].FileName); public static readonly string CurrentPath = AppDomain.CurrentDomain.BaseDirectory; public static readonly string CurrentFullEXEPath = Path.Combine(CurrentPath, CurrentEXEName); public static readonly string CurrentFullSourceEXEPath = Path.Combine(CurrentPath, SourceEXEName); public static readonly string CurrentFullUpdateEXEPath = Path.Combine(CurrentPath, UpdateEXEName); public static bool IsUpdated = false; internal static PluginCommand update => PluginCommand.Instance; public static bool ApplyUpdate(bool needUpdate) { if (CurrentEXEName == SourceEXEName) { if (needUpdate) { Process.GetProcessesByName(UpdateEXEName).FirstOrDefault(p => p.MainModule.FileName.Contains(CurrentFullUpdateEXEPath))?.Kill(); File.Delete(CurrentFullUpdateEXEPath); IsUpdated = true; } else if (File.Exists(CurrentFullUpdateEXEPath)) { try { Process.Start(CurrentFullUpdateEXEPath); return true; } catch (Exception e) { File.Delete(Path.Combine(CurrentPath, UpdateEXEName)); IO.CurrentIO.WriteColor($"Apply update fail! Can't launch ({e.Message})", ConsoleColor.Red); return false; } } } else if (CurrentEXEName == UpdateEXEName) { Process.GetProcessesByName(SourceEXEName).FirstOrDefault(p => p.MainModule.FileName.Contains(CurrentFullSourceEXEPath))?.Kill(); File.Delete(CurrentFullSourceEXEPath); File.Copy(CurrentFullUpdateEXEPath, CurrentFullSourceEXEPath); Process.Start(CurrentFullSourceEXEPath, UpdateArg); return true; } return false; } } } ================================================ FILE: Sync/Tools/Utils/ConfigurationHelper.cs ================================================ using Sync.Plugins; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.Utils { public static class ConfigurationHelper { /// /// dymatic to get config value from other plugin by reflection. /// /// default as plugin name /// default as IConfigurable name /// /// public static bool TryGetConfigurationElement(string main_section_name,string sub_section_name, string config_name,out ConfigurationElement result) { foreach(var manager in PluginConfigurationManager.ConfigurationSet) { if (main_section_name == manager.name) { foreach (var plugin_config in manager.items) { var config_instance = plugin_config.config; //sub section name var name = config_instance.GetType().Name; if (name==sub_section_name) { var config_type = config_instance.GetType(); foreach (var prop in config_type.GetProperties()) { if (prop.Name == config_name) { //copy result = prop.GetValue(config_instance).ToString(); return true; } } } } } } result = null; return false; } } } ================================================ FILE: Sync/Tools/Utils/PluginsHelper.cs ================================================ using Sync.Plugins; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Sync.Tools.Utils { public static class PluginsHelper { public static bool TryGetPlugin(out T plugin) where T : Plugin { plugin = SyncHost.Instance.Plugins.GetPlugins().FirstOrDefault(p => p is T) as T; return plugin != null; } } } ================================================ FILE: Sync/app.config ================================================ ================================================ FILE: Sync/config.ini ================================================ [config] LiveRoomID= TargetIRC= BotIRC= BotIRCPassword= Provider=Bilibili ================================================ FILE: Sync/packages.config ================================================  ================================================ FILE: Sync.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26403.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sync", "Sync\Sync.csproj", "{FBD514C2-2830-479E-B050-D1C383028456}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {FBD514C2-2830-479E-B050-D1C383028456}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FBD514C2-2830-479E-B050-D1C383028456}.Debug|Any CPU.Build.0 = Debug|Any CPU {FBD514C2-2830-479E-B050-D1C383028456}.Debug|x86.ActiveCfg = Debug|Any CPU {FBD514C2-2830-479E-B050-D1C383028456}.Debug|x86.Build.0 = Debug|Any CPU {FBD514C2-2830-479E-B050-D1C383028456}.Release|Any CPU.ActiveCfg = Release|Any CPU {FBD514C2-2830-479E-B050-D1C383028456}.Release|Any CPU.Build.0 = Release|Any CPU {FBD514C2-2830-479E-B050-D1C383028456}.Release|x86.ActiveCfg = Release|Any CPU {FBD514C2-2830-479E-B050-D1C383028456}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal