├── .gitattributes ├── .gitignore ├── AppControlPlugin ├── AppControlPlugin.cs ├── AppControlPlugin.csproj ├── AppControlPluginCommand.cs ├── AppControlPluginSettings.cs ├── AppControlPluginSettings_eng.json └── KeyCodes.txt ├── BrowserPlugin ├── BrowserPlugin.cs ├── BrowserPlugin.csproj ├── BrowserPluginCommand.cs ├── BrowserPluginSettings.cs └── BrowserPluginSettings_eng.json ├── CurrencyRatePlugin ├── CurrencyRatePlugin.cs ├── CurrencyRatePlugin.csproj ├── CurrencyRatePluginCommand.cs ├── CurrencyRatePluginSettings.cs └── CurrencyRatePluginSettings_eng.json ├── GoogleCalendarPlugin ├── CalendarEvents.cs ├── GoogleCalendarPlugin.cs ├── GoogleCalendarPlugin.csproj ├── GoogleCalendarPluginCommand.cs ├── GoogleCalendarPluginSettings.cs ├── GoogleCalendarPluginSettings_eng.json ├── birthdays_calendar.jpg └── calendarId.jpg ├── HelloPlugin ├── HelloPlugin.cs ├── HelloPlugin.csproj ├── HelloPluginCommand.cs ├── HelloPluginSettings.cs └── HelloPluginSettings_eng.json ├── LICENSE ├── MQTTInPlugin ├── MQTTInPlugin.cs ├── MQTTInPlugin.csproj ├── MQTTInPluginSettings.cs └── MQTTInTopic.cs ├── MQTTOutPlugin ├── MQTTOutPlugin.cs ├── MQTTOutPlugin.csproj ├── MQTTOutPluginCommand.cs ├── MQTTOutPluginSettings.cs └── MQTTPluginSettings_eng.json ├── OpenWeatherPlugin ├── CurrentWeatherData.cs ├── OpenWeatherPlugin.cs ├── OpenWeatherPlugin.csproj ├── OpenWeatherPluginCommand.cs ├── OpenWeatherPluginSettings.cs ├── OpenWeatherPluginSettings_eng.json ├── WeatherCurrent.cs ├── WeatherForecast.cs └── WeatherForecastData.cs ├── PluginInterface ├── Config.cs ├── GenericNumberSettings.cs ├── IAudioOutSingleton.cs ├── INumberToText.cs ├── ITextToNumber.cs ├── NumberToTextEmpty.cs ├── NumberToTextEng.cs ├── NumberToTextFactory.cs ├── NumberToTextGeneric.cs ├── NumberToTextRus.cs ├── PluginBase.cs ├── PluginCommand.cs ├── PluginInterface.csproj ├── PluginTools.cs ├── TextToNumberEmpty.cs ├── TextToNumberEng.cs ├── TextToNumberFactory.cs ├── TextToNumberGeneric.cs └── TextToNumberRus.cs ├── README.md ├── README_rus.md ├── RunProgramPlugin ├── RunProgramPlugin.cs ├── RunProgramPlugin.csproj ├── RunProgramPluginCommand.cs ├── RunProgramPluginSettings.cs └── RunProgramPluginSettings_eng.json ├── TimerPlugin ├── TimerPlugin.cs ├── TimerPlugin.csproj ├── TimerPluginCommand.cs ├── TimerPluginSettings.cs ├── TimerPluginSettings_eng.json └── timer.wav ├── VoiceAssistant.sln └── VoiceAssistant ├── AssistantStart.wav ├── AudioOutSingleton.cs ├── GenericPluginLoader.cs ├── Misrecognition.wav ├── ProcessingCommand.cs ├── Program.cs ├── VoiceAssistant.csproj ├── VoiceAssistantSettings.cs ├── VoskResult.cs ├── appsettings_eng.json └── desktop.ini /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /AppControlPlugin/AppControlPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | PreserveNewest 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /AppControlPlugin/AppControlPluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace AppControlPlugin 6 | { 7 | public class AppControlPluginCommand : PluginCommand 8 | { 9 | public string Response = ""; 10 | public string ApplicationId = ""; 11 | public string[] KeyNames = { "" }; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /AppControlPlugin/AppControlPluginSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace AppControlPlugin 6 | { 7 | public class AppControlPluginSettings 8 | { 9 | public string KeyNotFound = "Кнопка не найдена"; 10 | public string ProcessNotFound = "Процесс не найден"; 11 | 12 | //[JsonProperty(Required = Required.Always)] 13 | public AppControlPluginCommand[] Commands = 14 | { 15 | new AppControlPluginCommand 16 | { 17 | Name = "MPC next", 18 | Tokens = new[] 19 | { 20 | new Token 21 | { 22 | SuccessRate = 90, 23 | Type = TokenType.Command, 24 | Value = new[] {"следующий"} 25 | }, 26 | new Token 27 | { 28 | SuccessRate = 90, 29 | Type = TokenType.Command, 30 | Value = new[] {"фильм"} 31 | } 32 | }, 33 | ApplicationId = "mpc-hc64", 34 | KeyNames = new[] { "VK_NEXT" }, 35 | Response = "Перешел на следующий" 36 | }, 37 | new AppControlPluginCommand 38 | { 39 | Name = "MPC previous", 40 | Tokens = new[] 41 | { 42 | new Token 43 | { 44 | SuccessRate = 90, 45 | Type = TokenType.Command, 46 | Value = new[] {"предыдущий"} 47 | }, 48 | new Token 49 | { 50 | SuccessRate = 90, 51 | Type = TokenType.Command, 52 | Value = new[] {"фильм"} 53 | } 54 | }, 55 | ApplicationId = "mpc-hc64", 56 | KeyNames = new[] { "VK_PRIOR" }, 57 | Response = "Вернулся на предыдущий" 58 | }, 59 | new AppControlPluginCommand 60 | { 61 | Name = "MPC play", 62 | Tokens = new[] 63 | { 64 | new Token 65 | { 66 | SuccessRate = 90, 67 | Type = TokenType.Command, 68 | Value = new[] {"Запусти"} 69 | }, 70 | new Token 71 | { 72 | SuccessRate = 90, 73 | Type = TokenType.Command, 74 | Value = new[] { "фильм" } 75 | } 76 | }, 77 | ApplicationId = "mpc-hc64", 78 | KeyNames = new[] { "VK_SPACE" }, 79 | Response = "фильм запущен" 80 | }, 81 | new AppControlPluginCommand 82 | { 83 | Name = "MPC stop", 84 | Tokens = new[] 85 | { 86 | new Token 87 | { 88 | SuccessRate = 90, 89 | Type = TokenType.Command, 90 | Value = new[] {"Останови"} 91 | }, 92 | new Token 93 | { 94 | SuccessRate = 90, 95 | Type = TokenType.Command, 96 | Value = new[] { "фильм" } 97 | } 98 | }, 99 | ApplicationId = "mpc-hc64", 100 | KeyNames = new[] { "VK_DECIMAL" }, 101 | Response = "фильм остановлен" 102 | }, 103 | new AppControlPluginCommand 104 | { 105 | Name = "MPC play/pause", 106 | Tokens = new[] 107 | { 108 | new Token 109 | { 110 | SuccessRate = 90, 111 | Type = TokenType.Command, 112 | Value = new[] { "фильм" } 113 | }, 114 | new Token 115 | { 116 | SuccessRate = 90, 117 | Type = TokenType.Command, 118 | Value = new[] { "на" } 119 | }, 120 | new Token 121 | { 122 | SuccessRate = 90, 123 | Type = TokenType.Command, 124 | Value = new[] {"паузу"} 125 | } 126 | }, 127 | ApplicationId = "mpc-hc64", 128 | KeyNames = new[] { "VK_SPACE" }, 129 | Response = "Пауза" 130 | }, 131 | new AppControlPluginCommand 132 | { 133 | Name = "MPC volume up", 134 | Tokens = new[] 135 | { 136 | new Token 137 | { 138 | SuccessRate = 90, 139 | Type = TokenType.Command, 140 | Value = new[] { "сделай" } 141 | }, 142 | new Token 143 | { 144 | SuccessRate = 90, 145 | Type = TokenType.Command, 146 | Value = new[] { "громче" } 147 | } 148 | }, 149 | ApplicationId="mpc-hc64", 150 | KeyNames = new[] { "VK_UP","VK_UP","VK_UP","VK_UP" }, 151 | Response = "сделал громче" 152 | }, 153 | new AppControlPluginCommand 154 | { 155 | Name = "MPC volume down", 156 | Tokens = new[] 157 | { 158 | new Token 159 | { 160 | SuccessRate = 90, 161 | Type = TokenType.Command, 162 | Value = new[] { "сделай" } 163 | }, 164 | new Token 165 | { 166 | SuccessRate = 90, 167 | Type = TokenType.Command, 168 | Value = new[] { "тише" } 169 | } 170 | }, 171 | ApplicationId = "mpc-hc64", 172 | KeyNames = new[] { "VK_DOWN","VK_DOWN","VK_DOWN","VK_DOWN" }, 173 | Response = "сделал тише" 174 | } 175 | }; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /AppControlPlugin/AppControlPluginSettings_eng.json: -------------------------------------------------------------------------------- 1 | { 2 | "KeyNotFound": "key code not found", 3 | "ProcessNotFound": "process not found", 4 | "Commands": [ 5 | { 6 | "Response": "switched forth", 7 | "ApplicationId": "mpc-hc64", 8 | "KeyNames": [ 9 | "VK_NEXT" 10 | ], 11 | "Name": "MPC next", 12 | "Tokens": [ 13 | { 14 | "Value": [ 15 | "next" 16 | ], 17 | "Type": "Command", 18 | "SuccessRate": 90 19 | }, 20 | { 21 | "Value": [ 22 | "movie" 23 | ], 24 | "Type": "Command", 25 | "SuccessRate": 90 26 | } 27 | ] 28 | }, 29 | { 30 | "Response": "switched back", 31 | "ApplicationId": "mpc-hc64", 32 | "KeyNames": [ 33 | "VK_PRIOR" 34 | ], 35 | "Name": "MPC previous", 36 | "Tokens": [ 37 | { 38 | "Value": [ 39 | "previous" 40 | ], 41 | "Type": "Command", 42 | "SuccessRate": 90 43 | }, 44 | { 45 | "Value": [ 46 | "movie" 47 | ], 48 | "Type": "Command", 49 | "SuccessRate": 90 50 | } 51 | ] 52 | }, 53 | { 54 | "Response": "movie started", 55 | "ApplicationId": "mpc-hc64", 56 | "KeyNames": [ 57 | "VK_SPACE" 58 | ], 59 | "Name": "MPC play", 60 | "Tokens": [ 61 | { 62 | "Value": [ 63 | "start", 64 | "play" 65 | ], 66 | "Type": "Command", 67 | "SuccessRate": 90 68 | }, 69 | { 70 | "Value": [ 71 | "movie" 72 | ], 73 | "Type": "Command", 74 | "SuccessRate": 90 75 | } 76 | ] 77 | }, 78 | { 79 | "Response": "movie stopped", 80 | "ApplicationId": "mpc-hc64", 81 | "KeyNames": [ 82 | "VK_DECIMAL" 83 | ], 84 | "Name": "MPC stop", 85 | "Tokens": [ 86 | { 87 | "Value": [ 88 | "stop" 89 | ], 90 | "Type": "Command", 91 | "SuccessRate": 90 92 | }, 93 | { 94 | "Value": [ 95 | "movie" 96 | ], 97 | "Type": "Command", 98 | "SuccessRate": 90 99 | } 100 | ] 101 | }, 102 | { 103 | "Response": "movie paused", 104 | "ApplicationId": "mpc-hc64", 105 | "KeyNames": [ 106 | "VK_SPACE" 107 | ], 108 | "Name": "MPC play/pause", 109 | "Tokens": [ 110 | { 111 | "Value": [ 112 | "pause" 113 | ], 114 | "Type": "Command", 115 | "SuccessRate": 90 116 | }, 117 | { 118 | "Value": [ 119 | "movie" 120 | ], 121 | "Type": "Command", 122 | "SuccessRate": 90 123 | } 124 | ] 125 | }, 126 | { 127 | "Response": "volume increased", 128 | "ApplicationId": "mpc-hc64", 129 | "KeyNames": [ 130 | "VK_UP", 131 | "VK_UP", 132 | "VK_UP", 133 | "VK_UP" 134 | ], 135 | "Name": "MPC volume up", 136 | "Tokens": [ 137 | { 138 | "Value": [ 139 | "volume" 140 | ], 141 | "Type": "Command", 142 | "SuccessRate": 90 143 | }, 144 | { 145 | "Value": [ 146 | "up" 147 | ], 148 | "Type": "Command", 149 | "SuccessRate": 90 150 | } 151 | ] 152 | }, 153 | { 154 | "Response": "volume decreased", 155 | "ApplicationId": "mpc-hc64", 156 | "KeyNames": [ 157 | "VK_DOWN", 158 | "VK_DOWN", 159 | "VK_DOWN", 160 | "VK_DOWN" 161 | ], 162 | "Name": "MPC volume down", 163 | "Tokens": [ 164 | { 165 | "Value": [ 166 | "volume" 167 | ], 168 | "Type": "Command", 169 | "SuccessRate": 90 170 | }, 171 | { 172 | "Value": [ 173 | "down" 174 | ], 175 | "Type": "Command", 176 | "SuccessRate": 90 177 | } 178 | ] 179 | } 180 | ] 181 | } -------------------------------------------------------------------------------- /AppControlPlugin/KeyCodes.txt: -------------------------------------------------------------------------------- 1 | Constant Value Description 2 | 3 | VK_LBUTTON 0x01 Left mouse button 4 | VK_RBUTTON 0x02 Right mouse button 5 | VK_CANCEL 0x03 Control-break processing 6 | VK_MBUTTON 0x04 Middle mouse button (three-button mouse) 7 | VK_XBUTTON1 0x05 X1 mouse button 8 | VK_XBUTTON2 0x06 X2 mouse button 9 | - 0x07 Undefined 10 | VK_BACK 0x08 BACKSPACE key 11 | VK_TAB 0x09 TAB key 12 | - 0x0A-0B Reserved 13 | VK_CLEAR 0x0C CLEAR key 14 | VK_RETURN 0x0D ENTER key 15 | - 0x0E-0F Undefined 16 | VK_SHIFT 0x10 SHIFT key 17 | VK_CONTROL 0x11 CTRL key 18 | VK_MENU 0x12 ALT key 19 | VK_PAUSE 0x13 PAUSE key 20 | VK_CAPITAL 0x14 CAPS LOCK key 21 | VK_KANA 0x15 IME Kana mode 22 | VK_HANGUEL 0x15 IME Hanguel mode (maintained for compatibility; use VK_HANGUL) 23 | VK_HANGUL 0x15 IME Hangul mode 24 | VK_IME_ON 0x16 IME On 25 | VK_JUNJA 0x17 IME Junja mode 26 | VK_FINAL 0x18 IME final mode 27 | VK_HANJA 0x19 IME Hanja mode 28 | VK_KANJI 0x19 IME Kanji mode 29 | VK_IME_OFF 0x1A IME Off 30 | VK_ESCAPE 0x1B ESC key 31 | VK_CONVERT 0x1C IME convert 32 | VK_NONCONVERT 0x1D IME nonconvert 33 | VK_ACCEPT 0x1E IME accept 34 | VK_MODECHANGE 0x1F IME mode change request 35 | VK_SPACE 0x20 SPACEBAR 36 | VK_PRIOR 0x21 PAGE UP key 37 | VK_NEXT 0x22 PAGE DOWN key 38 | VK_END 0x23 END key 39 | VK_HOME 0x24 HOME key 40 | VK_LEFT 0x25 LEFT ARROW key 41 | VK_UP 0x26 UP ARROW key 42 | VK_RIGHT 0x27 RIGHT ARROW key 43 | VK_DOWN 0x28 DOWN ARROW key 44 | VK_SELECT 0x29 SELECT key 45 | VK_PRINT 0x2A PRINT key 46 | VK_EXECUTE 0x2B EXECUTE key 47 | VK_SNAPSHOT 0x2C PRINT SCREEN key 48 | VK_INSERT 0x2D INS key 49 | VK_DELETE 0x2E DEL key 50 | VK_HELP 0x2F HELP key 51 | - 0x30 0 key 52 | - 0x31 1 key 53 | - 0x32 2 key 54 | - 0x33 3 key 55 | - 0x34 4 key 56 | - 0x35 5 key 57 | - 0x36 6 key 58 | - 0x37 7 key 59 | - 0x38 8 key 60 | - 0x39 9 key 61 | - 0x3A-40 Undefined 62 | - 0x41 A key 63 | - 0x42 B key 64 | - 0x43 C key 65 | - 0x44 D key 66 | - 0x45 E key 67 | - 0x46 F key 68 | - 0x47 G key 69 | - 0x48 H key 70 | - 0x49 I key 71 | - 0x4A J key 72 | - 0x4B K key 73 | - 0x4C L key 74 | - 0x4D M key 75 | - 0x4E N key 76 | - 0x4F O key 77 | - 0x50 P key 78 | - 0x51 Q key 79 | - 0x52 R key 80 | - 0x53 S key 81 | - 0x54 T key 82 | - 0x55 U key 83 | - 0x56 V key 84 | - 0x57 W key 85 | - 0x58 X key 86 | - 0x59 Y key 87 | - 0x5A Z key 88 | VK_LWIN 0x5B Left Windows key (Natural keyboard) 89 | VK_RWIN 0x5C Right Windows key (Natural keyboard) 90 | VK_APPS 0x5D Applications key (Natural keyboard) 91 | - 0x5E Reserved 92 | VK_SLEEP 0x5F Computer Sleep key 93 | VK_NUMPAD0 0x60 Numeric keypad 0 key 94 | VK_NUMPAD1 0x61 Numeric keypad 1 key 95 | VK_NUMPAD2 0x62 Numeric keypad 2 key 96 | VK_NUMPAD3 0x63 Numeric keypad 3 key 97 | VK_NUMPAD4 0x64 Numeric keypad 4 key 98 | VK_NUMPAD5 0x65 Numeric keypad 5 key 99 | VK_NUMPAD6 0x66 Numeric keypad 6 key 100 | VK_NUMPAD7 0x67 Numeric keypad 7 key 101 | VK_NUMPAD8 0x68 Numeric keypad 8 key 102 | VK_NUMPAD9 0x69 Numeric keypad 9 key 103 | VK_MULTIPLY 0x6A Multiply key 104 | VK_ADD 0x6B Add key 105 | VK_SEPARATOR 0x6C Separator key 106 | VK_SUBTRACT 0x6D Subtract key 107 | VK_DECIMAL 0x6E Decimal key 108 | VK_DIVIDE 0x6F Divide key 109 | VK_F1 0x70 F1 key 110 | VK_F2 0x71 F2 key 111 | VK_F3 0x72 F3 key 112 | VK_F4 0x73 F4 key 113 | VK_F5 0x74 F5 key 114 | VK_F6 0x75 F6 key 115 | VK_F7 0x76 F7 key 116 | VK_F8 0x77 F8 key 117 | VK_F9 0x78 F9 key 118 | VK_F10 0x79 F10 key 119 | VK_F11 0x7A F11 key 120 | VK_F12 0x7B F12 key 121 | VK_F13 0x7C F13 key 122 | VK_F14 0x7D F14 key 123 | VK_F15 0x7E F15 key 124 | VK_F16 0x7F F16 key 125 | VK_F17 0x80 F17 key 126 | VK_F18 0x81 F18 key 127 | VK_F19 0x82 F19 key 128 | VK_F20 0x83 F20 key 129 | VK_F21 0x84 F21 key 130 | VK_F22 0x85 F22 key 131 | VK_F23 0x86 F23 key 132 | VK_F24 0x87 F24 key 133 | - 0x88-8F Unassigned 134 | VK_NUMLOCK 0x90 NUM LOCK key 135 | VK_SCROLL 0x91 SCROLL LOCK key 136 | - 0x92-96 OEM specific 137 | - 0x97-9F Unassigned 138 | VK_LSHIFT 0xA0 Left SHIFT key 139 | VK_RSHIFT 0xA1 Right SHIFT key 140 | VK_LCONTROL 0xA2 Left CONTROL key 141 | VK_RCONTROL 0xA3 Right CONTROL key 142 | VK_LMENU 0xA4 Left MENU key 143 | VK_RMENU 0xA5 Right MENU key 144 | 145 | 146 | VK_BROWSER_BACK 0xA6 Browser Back key 147 | VK_BROWSER_FORWARD 0xA7 Browser Forward key 148 | VK_BROWSER_REFRESH 0xA8 Browser Refresh key 149 | VK_BROWSER_STOP 0xA9 Browser Stop key 150 | VK_BROWSER_SEARCH 0xAA Browser Search key 151 | VK_BROWSER_FAVORITES 0xAB Browser Favorites key 152 | VK_BROWSER_HOME 0xAC Browser Start and Home key 153 | VK_VOLUME_MUTE 0xAD Volume Mute key 154 | VK_VOLUME_DOWN 0xAE Volume Down key 155 | VK_VOLUME_UP 0xAF Volume Up key 156 | VK_MEDIA_NEXT_TRACK 0xB0 Next Track key 157 | VK_MEDIA_PREV_TRACK 0xB1 Previous Track key 158 | VK_MEDIA_STOP 0xB2 Stop Media key 159 | VK_MEDIA_PLAY_PAUSE 0xB3 Play/Pause Media key 160 | VK_LAUNCH_MAIL 0xB4 Start Mail key 161 | VK_LAUNCH_MEDIA_SELECT 0xB5 Select Media key 162 | 163 | 164 | VK_LAUNCH_APP1 0xB6 Start Application 1 key 165 | VK_LAUNCH_APP2 0xB7 Start Application 2 key 166 | - 0xB8-B9 Reserved 167 | VK_OEM_1 0xBA Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key 168 | VK_OEM_PLUS 0xBB For any country/region, the '+' key 169 | VK_OEM_COMMA 0xBC For any country/region, the ',' key 170 | VK_OEM_MINUS 0xBD For any country/region, the '-' key 171 | VK_OEM_PERIOD 0xBE For any country/region, the '.' key 172 | VK_OEM_2 0xBF Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key 173 | VK_OEM_3 0xC0 Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key 174 | - 0xC1-D7 Reserved 175 | - 0xD8-DA Unassigned 176 | VK_OEM_4 0xDB Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key 177 | VK_OEM_5 0xDC Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\|' key 178 | VK_OEM_6 0xDD Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key 179 | VK_OEM_7 0xDE Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key 180 | VK_OEM_8 0xDF Used for miscellaneous characters; it can vary by keyboard. 181 | - 0xE0 Reserved 182 | - 0xE1 OEM specific 183 | VK_OEM_102 0xE2 The <> keys on the US standard keyboard, or the \\| key on the non-US 102-key keyboard 184 | - 0xE3-E4 OEM specific 185 | VK_PROCESSKEY 0xE5 IME PROCESS key 186 | - 0xE6 OEM specific 187 | VK_PACKET 0xE7 Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP 188 | - 0xE8 Unassigned 189 | - 0xE9-F5 OEM specific 190 | VK_ATTN 0xF6 Attn key 191 | VK_CRSEL 0xF7 CrSel key 192 | VK_EXSEL 0xF8 ExSel key 193 | VK_EREOF 0xF9 Erase EOF key 194 | VK_PLAY 0xFA Play key 195 | VK_ZOOM 0xFB Zoom key 196 | VK_NONAME 0xFC Reserved 197 | VK_PA1 0xFD PA1 key 198 | VK_OEM_CLEAR 0xFE Clear key 199 | -------------------------------------------------------------------------------- /BrowserPlugin/BrowserPlugin.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Runtime.InteropServices; 9 | 10 | using Microsoft.Win32; 11 | 12 | using PluginInterface; 13 | 14 | namespace BrowserPlugin 15 | { 16 | public class BrowserPlugin : PluginBase 17 | { 18 | private readonly BrowserPluginCommand[] BrowserCommands; 19 | private readonly Dictionary _processes = new Dictionary(); 20 | private readonly string _canNotClose; 21 | private readonly string _notRunning; 22 | private readonly string _canNotRun; 23 | private readonly string _alreadyRunning; 24 | 25 | public BrowserPlugin(IAudioOutSingleton audioOut, string currentCulture, string pluginPath) : base(audioOut, currentCulture, pluginPath) 26 | { 27 | var configBuilder = new Config($"{PluginPath}\\{PluginConfigFile}"); 28 | 29 | if (!File.Exists($"{PluginPath}\\{PluginConfigFile}")) 30 | { 31 | configBuilder.SaveConfig(); 32 | } 33 | 34 | BrowserCommands = configBuilder.ConfigStorage.Commands; 35 | 36 | if (BrowserCommands is PluginCommand[] newCmds) 37 | { 38 | _commands = newCmds; 39 | } 40 | 41 | _canNotClose = configBuilder.ConfigStorage.CanNotClose; 42 | _notRunning = configBuilder.ConfigStorage.NotRunning; 43 | _canNotRun = configBuilder.ConfigStorage.CanNotRun; 44 | _alreadyRunning = configBuilder.ConfigStorage.AlreadyRunning; 45 | } 46 | 47 | public override void Execute(string commandName, List commandTokens) 48 | { 49 | var command = BrowserCommands.FirstOrDefault(n => n.Name == commandName); 50 | 51 | if (command == null) 52 | { 53 | return; 54 | } 55 | 56 | var response = string.Empty; 57 | var processId = command.URL; 58 | 59 | if (command.isStopCommand) 60 | { 61 | if (_processes.TryGetValue(processId, out var proc)) 62 | { 63 | if (proc != null) 64 | { 65 | try 66 | { 67 | proc.Kill(); 68 | response = command.Response; 69 | } 70 | catch 71 | { 72 | response = _canNotClose; 73 | } 74 | finally 75 | { 76 | _processes.Remove(processId); 77 | } 78 | } 79 | else 80 | { 81 | response = _notRunning; 82 | } 83 | } 84 | else 85 | { 86 | response = _notRunning; 87 | } 88 | } 89 | else 90 | { 91 | if (!_processes.TryGetValue(processId, out _)) 92 | { 93 | var process = OpenUrl(command.URL, command.useStandAloneBrowser); 94 | 95 | if (process != null) 96 | { 97 | if (command.useStandAloneBrowser) 98 | { 99 | _processes.Add(processId, process); 100 | } 101 | 102 | response = command.Response; 103 | } 104 | else 105 | { 106 | response = _canNotRun; 107 | } 108 | } 109 | else 110 | { 111 | response = _alreadyRunning; 112 | } 113 | } 114 | AudioOut.Speak(response); 115 | } 116 | 117 | private Process OpenUrl(string url, bool standAloneBrowser = false) 118 | { 119 | Process proc; 120 | try 121 | { 122 | if (standAloneBrowser) 123 | { 124 | string browser = string.Empty; 125 | RegistryKey key = null; 126 | 127 | try 128 | { 129 | key = Registry.ClassesRoot.OpenSubKey(@"HTTP\shell\open\command"); 130 | 131 | if (key != null) 132 | { 133 | // Get default Browser 134 | browser = key.GetValue(null).ToString().ToLower().Trim(new[] { '"' }); 135 | } 136 | 137 | if (!browser.EndsWith("exe")) 138 | { 139 | //Remove all after the ".exe" 140 | browser = browser.Substring(0, browser.LastIndexOf(".exe", StringComparison.InvariantCultureIgnoreCase) + 4); 141 | } 142 | } 143 | finally 144 | { 145 | if (key != null) 146 | { 147 | key.Close(); 148 | } 149 | } 150 | 151 | proc = Process.Start(browser, url); 152 | } 153 | else 154 | { 155 | proc = Process.Start(url); 156 | } 157 | } 158 | catch 159 | { 160 | try 161 | { 162 | // hack because of this: https://github.com/dotnet/corefx/issues/10361 163 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 164 | { 165 | url = url.Replace("&", "^&"); 166 | proc = Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); 167 | 168 | /* 169 | Process myProcess = new Process(); 170 | try 171 | { 172 | // true is the default, but it is important not to set it to false 173 | myProcess.StartInfo.UseShellExecute = true; 174 | myProcess.StartInfo.FileName = "http://some.domain.tld/bla"; 175 | myProcess.Start(); 176 | } 177 | catch (Exception e) 178 | { 179 | Console.WriteLine(e.Message); 180 | } 181 | */ 182 | //Windows.System.Launcher.LaunchUriAsync(new Uri("http://google.com")); 183 | //Process.Start("explorer.exe", $"\"{uri}\""); 184 | 185 | } 186 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 187 | { 188 | proc = Process.Start("xdg-open", url); 189 | } 190 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 191 | { 192 | proc = Process.Start("open", url); 193 | } 194 | else 195 | { 196 | throw; 197 | } 198 | } 199 | catch 200 | { 201 | return null; 202 | } 203 | } 204 | 205 | return proc; 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /BrowserPlugin/BrowserPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /BrowserPlugin/BrowserPluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace BrowserPlugin 6 | { 7 | public class BrowserPluginCommand : PluginCommand 8 | { 9 | public string Response = ""; 10 | public string URL = ""; 11 | public bool isStopCommand = false; 12 | public bool useStandAloneBrowser = false; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BrowserPlugin/BrowserPluginSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace BrowserPlugin 6 | { 7 | public class BrowserPluginSettings 8 | { 9 | //[JsonProperty(Required = Required.Always)] 10 | public string CanNotClose = "не удалось закрыть"; 11 | public string NotRunning = "не запущен"; 12 | public string CanNotRun = "не удалось запустить"; 13 | public string AlreadyRunning = "уже запущен"; 14 | 15 | public BrowserPluginCommand[] Commands = 16 | { 17 | new BrowserPluginCommand 18 | { 19 | Name = "Run Yandex", 20 | Tokens = new[] 21 | { 22 | new Token 23 | { 24 | SuccessRate = 90, 25 | Type = TokenType.Command, 26 | Value = new[] {"запусти", "открой"} 27 | }, 28 | new Token 29 | { 30 | SuccessRate = 90, 31 | Type = TokenType.Command, 32 | Value = new[] {"яндекс"} 33 | } 34 | }, 35 | Response = "Яндекс запущен", 36 | URL = "www.yandex.ru", 37 | isStopCommand = false, 38 | useStandAloneBrowser = true 39 | }, 40 | new BrowserPluginCommand 41 | { 42 | Name = "Stop Yandex", 43 | Tokens = new[] 44 | { 45 | new Token 46 | { 47 | SuccessRate = 90, 48 | Type = TokenType.Command, 49 | Value = new[] {"останови", "закрой"} 50 | }, 51 | new Token 52 | { 53 | SuccessRate = 90, 54 | Type = TokenType.Command, 55 | Value = new[] {"яндекс"} 56 | } 57 | }, 58 | Response = "Яндекс закрыт", 59 | URL = "www.yandex.ru", 60 | isStopCommand = true, 61 | useStandAloneBrowser = true 62 | }, 63 | new BrowserPluginCommand 64 | { 65 | Name = "Run Google", 66 | Tokens = new[] 67 | { 68 | new Token 69 | { 70 | SuccessRate = 90, 71 | Type = TokenType.Command, 72 | Value = new[] {"запусти", "открой" } 73 | }, 74 | new Token 75 | { 76 | SuccessRate = 90, 77 | Type = TokenType.Command, 78 | Value = new[] {"гугл"} 79 | } 80 | }, 81 | Response = "Гугл запущен", 82 | URL = "www.google.com", 83 | isStopCommand = false, 84 | useStandAloneBrowser = false 85 | }, 86 | new BrowserPluginCommand 87 | { 88 | Name = "Stop Google", 89 | Tokens = new[] 90 | { 91 | new Token 92 | { 93 | SuccessRate = 90, 94 | Type = TokenType.Command, 95 | Value = new[] {"останови", "закрой"} 96 | }, 97 | new Token 98 | { 99 | SuccessRate = 90, 100 | Type = TokenType.Command, 101 | Value = new[] { "гугл" } 102 | } 103 | }, 104 | Response = "Гугл закрыт", 105 | URL = "www.google.com", 106 | isStopCommand = true, 107 | useStandAloneBrowser = false 108 | }, 109 | new BrowserPluginCommand 110 | { 111 | Name = "Run Kinopoisk", 112 | Tokens = new[] 113 | { 114 | new Token 115 | { 116 | SuccessRate = 90, 117 | Type = TokenType.Command, 118 | Value = new[] {"запусти", "открой" } 119 | }, 120 | new Token 121 | { 122 | SuccessRate = 90, 123 | Type = TokenType.Command, 124 | Value = new[] {"кинопоиск"} 125 | } 126 | }, 127 | Response = "Кинопоиск запущен", 128 | URL = "www.kinopoisk.ru", 129 | isStopCommand = false, 130 | useStandAloneBrowser = false 131 | }, 132 | new BrowserPluginCommand 133 | { 134 | Name = "Stop Kinopoisk", 135 | Tokens = new[] 136 | { 137 | new Token 138 | { 139 | SuccessRate = 90, 140 | Type = TokenType.Command, 141 | Value = new[] {"останови", "закрой"} 142 | }, 143 | new Token 144 | { 145 | SuccessRate = 90, 146 | Type = TokenType.Command, 147 | Value = new[] { "кинопоиск" } 148 | } 149 | }, 150 | Response = "Кинопоиск закрыт", 151 | URL = "www.kinopoisk.ru", 152 | isStopCommand = true, 153 | useStandAloneBrowser = false 154 | } 155 | }; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /BrowserPlugin/BrowserPluginSettings_eng.json: -------------------------------------------------------------------------------- 1 | { 2 | "CanNotClose": "can not close", 3 | "NotRunning": "not running", 4 | "CanNotRun": "can not run", 5 | "AlreadyRunning": "already running", 6 | "Commands": [ 7 | { 8 | "Response": "Yandex is running", 9 | "URL": "www.yandex.ru", 10 | "isStopCommand": false, 11 | "useStandAloneBrowser": true, 12 | "Name": "Run Yandex", 13 | "Tokens": [ 14 | { 15 | "Value": [ 16 | "Start", 17 | "Open" 18 | ], 19 | "Type": "Command", 20 | "SuccessRate": 90 21 | }, 22 | { 23 | "Value": [ 24 | "yandex" 25 | ], 26 | "Type": "Command", 27 | "SuccessRate": 90 28 | } 29 | ] 30 | }, 31 | { 32 | "Response": "Yandex is closed", 33 | "URL": "www.yandex.ru", 34 | "isStopCommand": true, 35 | "useStandAloneBrowser": true, 36 | "Name": "Stop Yandex", 37 | "Tokens": [ 38 | { 39 | "Value": [ 40 | "Stop", 41 | "Close" 42 | ], 43 | "Type": "Command", 44 | "SuccessRate": 90 45 | }, 46 | { 47 | "Value": [ 48 | "yandex" 49 | ], 50 | "Type": "Command", 51 | "SuccessRate": 90 52 | } 53 | ] 54 | }, 55 | { 56 | "Response": "Google is running", 57 | "URL": "www.google.com", 58 | "isStopCommand": false, 59 | "useStandAloneBrowser": false, 60 | "Name": "Run Google", 61 | "Tokens": [ 62 | { 63 | "Value": [ 64 | "Start", 65 | "Open" 66 | ], 67 | "Type": "Command", 68 | "SuccessRate": 90 69 | }, 70 | { 71 | "Value": [ 72 | "google" 73 | ], 74 | "Type": "Command", 75 | "SuccessRate": 90 76 | } 77 | ] 78 | }, 79 | { 80 | "Response": "Google is closed", 81 | "URL": "www.google.com", 82 | "isStopCommand": true, 83 | "useStandAloneBrowser": false, 84 | "Name": "Stop Google", 85 | "Tokens": [ 86 | { 87 | "Value": [ 88 | "Stop", 89 | "Close" 90 | ], 91 | "Type": "Command", 92 | "SuccessRate": 90 93 | }, 94 | { 95 | "Value": [ 96 | "google" 97 | ], 98 | "Type": "Command", 99 | "SuccessRate": 90 100 | } 101 | ] 102 | } 103 | ] 104 | } -------------------------------------------------------------------------------- /CurrencyRatePlugin/CurrencyRatePlugin.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Threading.Tasks; 9 | using System.Xml.Linq; 10 | 11 | using PluginInterface; 12 | 13 | namespace CurrencyRatePlugin 14 | { 15 | public class CurrencyRatePlugin : PluginBase 16 | { 17 | private readonly CurrencyRatePluginCommand[] CurrencyRateCommands; 18 | private readonly string CurrencyServiceUrl; 19 | private readonly string CurrencyDecimalSeparatorWord; 20 | 21 | public CurrencyRatePlugin(IAudioOutSingleton audioOut, string currentCulture, string pluginPath) : base(audioOut, currentCulture, pluginPath) 22 | { 23 | var configBuilder = new Config($"{PluginPath}\\{PluginConfigFile}"); 24 | if (!File.Exists($"{PluginPath}\\{PluginConfigFile}")) 25 | { 26 | configBuilder.SaveConfig(); 27 | } 28 | 29 | CurrencyRateCommands = configBuilder.ConfigStorage.Commands; 30 | 31 | if (CurrencyRateCommands is PluginCommand[] newCmds) 32 | { 33 | _commands = newCmds; 34 | } 35 | CurrencyServiceUrl = configBuilder.ConfigStorage.CurrencyServiceUrl; 36 | CurrencyDecimalSeparatorWord = configBuilder.ConfigStorage.CurrencyDecimalSeparatorWord; 37 | } 38 | 39 | public override void Execute(string commandName, List commandTokens) 40 | { 41 | var command = CurrencyRateCommands.FirstOrDefault(n => n.Name == commandName); 42 | 43 | if (command == null) 44 | { 45 | return; 46 | } 47 | 48 | var currencyRate = GetCurrencyRate(CurrencyServiceUrl, command.CurencyCode, command.RateDecimalRound).Result; 49 | if (currencyRate.Item1 > 0) 50 | { 51 | var message = string.Format(command.Response, "", currencyRate.Item1, currencyRate.Item2 52 | .ToString() 53 | .Replace(System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator, 54 | $" {CurrencyDecimalSeparatorWord} ")); 55 | AudioOut.Speak(message); 56 | } 57 | } 58 | 59 | private async Task<(int, float)> GetCurrencyRate(string currencyServiceUrl, string curencyCode, int decimalRound) 60 | { 61 | var currencyRates = await GetRate(currencyServiceUrl); 62 | if (currencyRates == null) 63 | return (-1, -1); 64 | 65 | var currencyRate = currencyRates.FirstOrDefault(n => n.CurrencyCode == curencyCode); 66 | if (currencyRate == null) 67 | return (-1, -1); 68 | 69 | if (!int.TryParse(currencyRate.Nominal, out var nominal)) 70 | return (-1, -1); 71 | var decimalSeparator = System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator; 72 | var v = currencyRate.Value.Replace(".", decimalSeparator); 73 | v = v.Replace(",", decimalSeparator); 74 | v = v.Substring(0, v.IndexOf(decimalSeparator) + decimalRound + 1); 75 | if (!float.TryParse(v, out var val)) 76 | return (-1, -1); 77 | 78 | return (nominal, val); 79 | } 80 | 81 | private async Task GetRate(string currencyServiceUrl) 82 | { 83 | try 84 | { 85 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(currencyServiceUrl); 86 | request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; 87 | 88 | using (var response = await request.GetResponseAsync()) 89 | { 90 | using (Stream stream = response.GetResponseStream()) 91 | { 92 | using (StreamReader reader = new StreamReader(stream)) 93 | { 94 | var xmlDoc = await reader.ReadToEndAsync(); 95 | XDocument xdoc = XDocument.Parse(xmlDoc); 96 | 97 | // получаем корневой узел 98 | XElement? rates = xdoc.Element("ValCurs"); 99 | var currencyRates = new List(); 100 | 101 | if (rates != null) 102 | { 103 | // проходим по всем элементам person 104 | foreach (XElement rate in rates.Elements("Valute")) 105 | { 106 | var code = rate.Element("CharCode"); 107 | var name = rate.Element("Name"); 108 | var nom = rate.Element("Nominal"); 109 | var val = rate.Element("Value"); 110 | 111 | var newRate = new CurrencyRate() 112 | { 113 | CurrencyCode = code?.Value, 114 | CurrencyName = name?.Value, 115 | Nominal = nom?.Value, 116 | Value = val?.Value 117 | }; 118 | 119 | currencyRates.Add(newRate); 120 | } 121 | } 122 | 123 | return currencyRates.ToArray(); 124 | } 125 | } 126 | } 127 | } 128 | catch (Exception ex) 129 | { 130 | Console.WriteLine($"Error reading from {currencyServiceUrl}: {ex.Message}"); 131 | 132 | return null; 133 | } 134 | } 135 | } 136 | 137 | public class CurrencyRate 138 | { 139 | public string CurrencyCode; 140 | public string CurrencyName; 141 | public string Nominal; 142 | public string Value; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /CurrencyRatePlugin/CurrencyRatePlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CurrencyRatePlugin/CurrencyRatePluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace CurrencyRatePlugin 6 | { 7 | public class CurrencyRatePluginCommand : PluginCommand 8 | { 9 | public string CurencyCode = ""; 10 | public int RateDecimalRound = 2; 11 | public string Response = ""; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CurrencyRatePlugin/CurrencyRatePluginSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace CurrencyRatePlugin 6 | { 7 | public class CurrencyRatePluginSettings 8 | { 9 | public string CurrencyServiceUrl = "http://www.cbr.ru/scripts/XML_daily.asp"; 10 | public string CurrencyDecimalSeparatorWord = "точка"; 11 | 12 | //[JsonProperty(Required = Required.Always)] 13 | public CurrencyRatePluginCommand[] Commands = 14 | { 15 | new CurrencyRatePluginCommand 16 | { 17 | Name = "Short USD request", 18 | Tokens = new[] 19 | { 20 | new Token 21 | { 22 | SuccessRate = 90, 23 | Type = TokenType.Command, 24 | Value = new[] {"курс"} 25 | }, 26 | new Token 27 | { 28 | SuccessRate = 90, 29 | Type = TokenType.Command, 30 | Value = new[] {"доллара"} 31 | } 32 | }, 33 | CurencyCode = "USD", 34 | RateDecimalRound=1, 35 | Response = "{1} доллар равен {2} рублей" 36 | }, 37 | new CurrencyRatePluginCommand 38 | { 39 | Name = "Long USD request", 40 | Tokens = new[] 41 | { 42 | new Token 43 | { 44 | SuccessRate = 90, 45 | Type = TokenType.Command, 46 | Value = new[] {"какой"} 47 | }, 48 | new Token 49 | { 50 | SuccessRate = 90, 51 | Type = TokenType.Command, 52 | Value = new[] {"курс"} 53 | }, 54 | new Token 55 | { 56 | SuccessRate = 90, 57 | Type = TokenType.Command, 58 | Value = new[] {"доллара"} 59 | } 60 | }, 61 | CurencyCode = "USD", 62 | RateDecimalRound=1, 63 | Response = "{1} доллар равен {2} рублей" 64 | }, 65 | new CurrencyRatePluginCommand 66 | { 67 | Name = "Short EUR request", 68 | Tokens = new[] 69 | { 70 | new Token 71 | { 72 | SuccessRate = 90, 73 | Type = TokenType.Command, 74 | Value = new[] {"курс"} 75 | }, 76 | new Token 77 | { 78 | SuccessRate = 90, 79 | Type = TokenType.Command, 80 | Value = new[] { "евро" } 81 | } 82 | }, 83 | CurencyCode = "EUR", 84 | RateDecimalRound=1, 85 | Response = "{1} евро равен {2} рублей" 86 | }, 87 | new CurrencyRatePluginCommand 88 | { 89 | Name = "Long EUR request", 90 | Tokens = new[] 91 | { 92 | new Token 93 | { 94 | SuccessRate = 90, 95 | Type = TokenType.Command, 96 | Value = new[] {"какой"} 97 | }, 98 | new Token 99 | { 100 | SuccessRate = 90, 101 | Type = TokenType.Command, 102 | Value = new[] {"курс"} 103 | }, 104 | new Token 105 | { 106 | SuccessRate = 90, 107 | Type = TokenType.Command, 108 | Value = new[] {"евро"} 109 | } 110 | }, 111 | CurencyCode = "EUR", 112 | RateDecimalRound=1, 113 | Response = "{1} евро равен {2} рублей" 114 | }, 115 | }; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /CurrencyRatePlugin/CurrencyRatePluginSettings_eng.json: -------------------------------------------------------------------------------- 1 | { 2 | "CurrencyServiceUrl": "http://www.cbr.ru/scripts/XML_daily.asp", 3 | "CurrencyDecimalSeparatorWord": "dot", 4 | "Commands": [ 5 | { 6 | "CurencyCode": "USD", 7 | "RateDecimalRound": 1, 8 | "Response": "{1} dollar is {2} rubles", 9 | "Name": "Short USD request", 10 | "Tokens": [ 11 | { 12 | "Value": [ 13 | "dollar" 14 | ], 15 | "Type": "Command", 16 | "SuccessRate": 90 17 | }, 18 | { 19 | "Value": [ 20 | "exchange" 21 | ], 22 | "Type": "Command", 23 | "SuccessRate": 90 24 | }, 25 | { 26 | "Value": [ 27 | "rate" 28 | ], 29 | "Type": "Command", 30 | "SuccessRate": 90 31 | } 32 | ] 33 | }, 34 | { 35 | "CurencyCode": "USD", 36 | "RateDecimalRound": 1, 37 | "Response": "{1} dollar is {2} rubles", 38 | "Name": "Long USD request", 39 | "Tokens": [ 40 | { 41 | "Value": [ 42 | "What" 43 | ], 44 | "Type": "Command", 45 | "SuccessRate": 90 46 | }, 47 | { 48 | "Value": [ 49 | "is " 50 | ], 51 | "Type": "Command", 52 | "SuccessRate": 90 53 | }, 54 | { 55 | "Value": [ 56 | "the" 57 | ], 58 | "Type": "Command", 59 | "SuccessRate": 90 60 | }, 61 | { 62 | "Value": [ 63 | "dollar" 64 | ], 65 | "Type": "Command", 66 | "SuccessRate": 90 67 | }, 68 | { 69 | "Value": [ 70 | "exchange" 71 | ], 72 | "Type": "Command", 73 | "SuccessRate": 90 74 | }, 75 | { 76 | "Value": [ 77 | "rate" 78 | ], 79 | "Type": "Command", 80 | "SuccessRate": 90 81 | } 82 | ] 83 | }, 84 | { 85 | "CurencyCode": "EUR", 86 | "RateDecimalRound": 1, 87 | "Response": "{1} euro is {2} rubles", 88 | "Name": "Short EUR request", 89 | "Tokens": [ 90 | { 91 | "Value": [ 92 | "euro" 93 | ], 94 | "Type": "Command", 95 | "SuccessRate": 90 96 | }, 97 | { 98 | "Value": [ 99 | "exchange" 100 | ], 101 | "Type": "Command", 102 | "SuccessRate": 90 103 | }, 104 | { 105 | "Value": [ 106 | "rate" 107 | ], 108 | "Type": "Command", 109 | "SuccessRate": 90 110 | } 111 | ] 112 | }, 113 | { 114 | "CurencyCode": "EUR", 115 | "RateDecimalRound": 1, 116 | "Response": "{1} euro is {2} rubles", 117 | "Name": "Long EUR request", 118 | "Tokens": [ 119 | { 120 | "Value": [ 121 | "What" 122 | ], 123 | "Type": "Command", 124 | "SuccessRate": 90 125 | }, 126 | { 127 | "Value": [ 128 | "is " 129 | ], 130 | "Type": "Command", 131 | "SuccessRate": 90 132 | }, 133 | { 134 | "Value": [ 135 | "the" 136 | ], 137 | "Type": "Command", 138 | "SuccessRate": 90 139 | }, 140 | { 141 | "Value": [ 142 | "euro" 143 | ], 144 | "Type": "Command", 145 | "SuccessRate": 90 146 | }, 147 | { 148 | "Value": [ 149 | "exchange" 150 | ], 151 | "Type": "Command", 152 | "SuccessRate": 90 153 | }, 154 | { 155 | "Value": [ 156 | "rate" 157 | ], 158 | "Type": "Command", 159 | "SuccessRate": 90 160 | } 161 | ] 162 | } 163 | ] 164 | } -------------------------------------------------------------------------------- /GoogleCalendarPlugin/CalendarEvents.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using Google.Apis.Calendar.v3.Data; 4 | 5 | using System; 6 | 7 | namespace GoogleCalendarPlugin 8 | { 9 | public partial class GoogleCalendarPlugin 10 | { 11 | private class CalendarEvents 12 | { 13 | public string Attendees = string.Empty; 14 | public string Description = string.Empty; 15 | public string Location = string.Empty; 16 | public string Summary = string.Empty; 17 | 18 | private readonly DateTime? Start; 19 | public string StartYear => Start?.Year.ToString() ?? string.Empty; 20 | public string StartMonth => Start?.Month.ToString() ?? string.Empty; 21 | public string StartDay => Start?.Day.ToString() ?? string.Empty; 22 | 23 | public string StartDate 24 | { 25 | get 26 | { 27 | return ((DateTime)Start).ToString("dd MMMM"); 28 | } 29 | } 30 | 31 | public string StartTime 32 | { 33 | get 34 | { 35 | string result = NumberToString(Start?.Hour ?? 0, oneHour, twoHours, fiveHours); 36 | result += NumberToString(Start?.Minute ?? 0, oneMinute, twoMinutes, fiveMinutes); 37 | 38 | return result; 39 | } 40 | } 41 | 42 | private readonly DateTime? End; 43 | public string EndYear => End?.Year.ToString() ?? string.Empty; 44 | public string EndMonth => End?.Month.ToString() ?? string.Empty; 45 | public string EndDay => End?.Day.ToString() ?? string.Empty; 46 | 47 | public string EndDate 48 | { 49 | get 50 | { 51 | return ((DateTime)End).ToString("dd MMMM"); 52 | } 53 | } 54 | 55 | public string EndTime 56 | { 57 | get 58 | { 59 | string result = NumberToString(End?.Hour ?? 0, oneHour, twoHours, fiveHours); 60 | result += NumberToString(End?.Minute ?? 0, oneMinute, twoMinutes, fiveMinutes); 61 | 62 | return result; 63 | } 64 | } 65 | 66 | private int _lengthHour = -1; 67 | private int LengthHour 68 | { 69 | get 70 | { 71 | if (_lengthHour < 0) 72 | { 73 | if (Start != null && End != null) 74 | { 75 | var diff = ((DateTime)End).Subtract((DateTime)Start); 76 | _lengthHour = (int)diff.TotalHours; 77 | } 78 | else 79 | { 80 | _lengthHour = 0; 81 | } 82 | } 83 | 84 | return _lengthHour; 85 | } 86 | } 87 | 88 | private int _lengthMinute = -1; 89 | private int LengthMinute 90 | { 91 | get 92 | { 93 | if (_lengthMinute < 0) 94 | { 95 | if (Start != null && End != null) 96 | { 97 | var diff = ((DateTime)End).Subtract((DateTime)Start); 98 | _lengthMinute = (int)diff.TotalMinutes; 99 | } 100 | else 101 | { 102 | _lengthMinute = 0; 103 | } 104 | } 105 | 106 | return _lengthMinute; 107 | } 108 | } 109 | 110 | public CalendarEvents(Event calendarEvent) 111 | { 112 | if (calendarEvent.Attendees != null) 113 | { 114 | foreach (var attendee in calendarEvent.Attendees) 115 | { 116 | Attendees = attendee + ","; 117 | } 118 | } 119 | else 120 | { 121 | Attendees = string.Empty; 122 | } 123 | 124 | Description = calendarEvent.Description ?? string.Empty; 125 | Location = calendarEvent.Location ?? string.Empty; 126 | Summary = calendarEvent.Summary ?? string.Empty; 127 | Start = calendarEvent.Start.DateTime ?? default; 128 | End = calendarEvent.End.DateTime ?? default; 129 | } 130 | 131 | public string Length 132 | { 133 | get 134 | { 135 | string result = NumberToString(LengthHour, oneHour, twoHours, fiveHours); 136 | result += NumberToString(LengthMinute, oneMinute, twoMinutes, fiveMinutes); 137 | 138 | return result; 139 | } 140 | } 141 | 142 | private string NumberToString(int number, string one, string two, string five) 143 | { 144 | string result = ""; 145 | 146 | if (number > 0) 147 | { 148 | int lastNumber = number % 10; 149 | result += number.ToString() + " "; 150 | if (lastNumber == 1) 151 | result += one + " "; 152 | else if (lastNumber > 1 && lastNumber < 5) 153 | result += two + " "; 154 | else if (lastNumber > 5) 155 | result += five + " "; 156 | } 157 | 158 | return result; 159 | 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /GoogleCalendarPlugin/GoogleCalendarPlugin.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using Google.Apis.Auth.OAuth2; 4 | using Google.Apis.Calendar.v3; 5 | using Google.Apis.Calendar.v3.Data; 6 | using Google.Apis.Services; 7 | using Google.Apis.Util.Store; 8 | 9 | using PluginInterface; 10 | 11 | using System; 12 | using System.Collections.Generic; 13 | using System.IO; 14 | using System.Linq; 15 | using System.Text; 16 | using System.Threading; 17 | 18 | namespace GoogleCalendarPlugin 19 | { 20 | public partial class GoogleCalendarPlugin : PluginBase 21 | { 22 | private readonly GoogleCalendarPluginCommand[] GoogleCalendarCommands; 23 | private readonly string[] Scopes = { CalendarService.Scope.Calendar }; 24 | private readonly string ApplicationName = "GoogleCalendarPlugin"; 25 | private readonly string GoogleApiCredentials = ""; 26 | private readonly string credPath = "token_json"; 27 | 28 | private static readonly string oneHour = "час"; 29 | private static readonly string twoHours = "часа"; 30 | private static readonly string fiveHours = "часов"; 31 | 32 | private static readonly string oneMinute = "минута"; 33 | private static readonly string twoMinutes = "минуты"; 34 | private static readonly string fiveMinutes = "минут"; 35 | 36 | private readonly string moreResultsAvailableMessage; 37 | private readonly string noEventsMessage; 38 | private readonly string NoDataMessage; 39 | 40 | public GoogleCalendarPlugin(IAudioOutSingleton audioOut, string currentCulture, string pluginPath) : base(audioOut, currentCulture, pluginPath) 41 | { 42 | var configBuilder = new Config($"{PluginPath}\\{PluginConfigFile}"); 43 | if (!File.Exists($"{PluginPath}\\{PluginConfigFile}")) 44 | { 45 | configBuilder.SaveConfig(); 46 | } 47 | 48 | GoogleCalendarCommands = configBuilder.ConfigStorage.Commands; 49 | 50 | if (GoogleCalendarCommands is PluginCommand[] newCmds) 51 | { 52 | _commands = newCmds; 53 | } 54 | moreResultsAvailableMessage = configBuilder.ConfigStorage.MoreResultsAvailableMessage; 55 | noEventsMessage = configBuilder.ConfigStorage.NoEventsMessage; 56 | NoDataMessage = configBuilder.ConfigStorage.NoDataMessage; 57 | } 58 | 59 | public override void Execute(string commandName, List commandTokens) 60 | { 61 | var command = GoogleCalendarCommands.FirstOrDefault(n => n.Name == commandName); 62 | if (command == null) 63 | { 64 | return; 65 | } 66 | 67 | var events = GetEvents(GoogleApiCredentials, command.CalendarId, command.DaysStart, command.DaysCount, command.MaxEvents, out var moreEvents); 68 | 69 | if (events?.Length <= 0) 70 | { 71 | AudioOut.Speak(NoDataMessage); 72 | return; 73 | } 74 | 75 | var eventsMessage = new StringBuilder(); 76 | foreach (var eventItem in events) 77 | { 78 | eventsMessage.Append(PluginTools.FormatStringWithClassFields(command.SingleEventMessage, eventItem)); 79 | } 80 | 81 | if (eventsMessage.Length <= 0) 82 | { 83 | eventsMessage.Append(noEventsMessage); 84 | } 85 | 86 | var message = string.Format(command.Response, null, eventsMessage); 87 | if (moreEvents) 88 | { 89 | message += moreResultsAvailableMessage; 90 | } 91 | 92 | AudioOut.Speak(message); 93 | } 94 | 95 | private CalendarEvents[] GetEvents(string apiCredentials, string calendarName, int daysStart, int daysCount, int maxEvents, out bool moreEvents) 96 | { 97 | moreEvents = false; 98 | UserCredential credential; 99 | var result = new List(); 100 | 101 | try 102 | { 103 | //new FileStream("credentials.json", FileMode.Open, FileAccess.Read)) 104 | using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(apiCredentials))) 105 | { 106 | // The file token.json stores the user's access and refresh tokens, and is created 107 | // automatically when the authorization flow completes for the first time. 108 | credential = GoogleWebAuthorizationBroker.AuthorizeAsync( 109 | GoogleClientSecrets.FromStream(stream).Secrets, 110 | Scopes, 111 | "user", 112 | CancellationToken.None, 113 | new FileDataStore($"{PluginPath}\\{credPath}", true)).Result; 114 | } 115 | 116 | // Create Google Calendar API service. 117 | using var service = new CalendarService(new BaseClientService.Initializer() 118 | { 119 | HttpClientInitializer = credential, 120 | ApplicationName = ApplicationName, 121 | }); 122 | 123 | if (string.IsNullOrEmpty(calendarName)) 124 | { 125 | var calendarsRequest = service.CalendarList.List(); 126 | var calendars = calendarsRequest.Execute(); 127 | 128 | if (calendars != null) 129 | { 130 | Console.WriteLine("Available calendars:"); 131 | 132 | foreach (var calendar in calendars.Items) 133 | { 134 | Console.WriteLine($"ID: {calendar.Id}\r\nDescription: {calendar.Description}\r\nSummary: {calendar.Summary}\r\n"); 135 | } 136 | } 137 | 138 | // set calendar name to default 139 | calendarName = "primary"; 140 | } 141 | 142 | // Define parameters of request 143 | EventsResource.ListRequest request = service.Events.List(calendarName); // "****@gmail.com", "primary", "addressbook#contacts@group.v.calendar.google.com" 144 | request.ShowDeleted = false; 145 | request.SingleEvents = true; 146 | request.MaxResults = maxEvents; 147 | request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime; 148 | request.TimeMax = DateTime.Today.AddDays(daysCount); 149 | if (daysStart == 0) 150 | request.TimeMin = DateTime.Today.AddDays(daysStart); 151 | else 152 | request.TimeMin = DateTime.Now; 153 | 154 | // List events 155 | Events events = request.Execute(); 156 | if (events.Items != null) 157 | { 158 | foreach (var eventItem in events.Items) 159 | { 160 | result.Add(new CalendarEvents(eventItem)); 161 | } 162 | } 163 | 164 | // do we have more events pages? No use to list too many events at once 165 | moreEvents = !string.IsNullOrEmpty(events.NextPageToken); 166 | 167 | } 168 | catch (Exception ex) 169 | { 170 | Console.WriteLine($"Can't get GoogleCalendar data: {ex.Message}"); 171 | } 172 | 173 | return result.ToArray(); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /GoogleCalendarPlugin/GoogleCalendarPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /GoogleCalendarPlugin/GoogleCalendarPluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace GoogleCalendarPlugin 6 | { 7 | public class GoogleCalendarPluginCommand : PluginCommand 8 | { 9 | public string SingleEventMessage = ""; 10 | public string Response = ""; 11 | public string CalendarId = ""; 12 | public int DaysStart = 0; 13 | public int DaysCount = 7; 14 | public int MaxEvents = 10; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /GoogleCalendarPlugin/GoogleCalendarPluginSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace GoogleCalendarPlugin 6 | { 7 | public class GoogleCalendarPluginSettings 8 | { 9 | public string[] ConfigurationNote = 10 | { 11 | "Interpolation macros:", 12 | "Only for \"SingleEventMessage\":", 13 | "{Attendees} - list of attendees", 14 | "{Description} - event description", 15 | "{Location} - event location", 16 | "{Summary} - event summary", 17 | "{Length} - event duration", 18 | "{StartYear} - event start year", 19 | "{StartMonth} - event start month", 20 | "{StartDay} - event start day", 21 | "{StarDate} - event start day and month", 22 | "{StarTime} - event start hour and minute", 23 | "{EndYear} - event end year", 24 | "{EndMonth} - event end month", 25 | "{EndDay} - event end day", 26 | "{EndDate} - event end day and month", 27 | "{EndTime} - event end hour and minute", 28 | "", 29 | "Only for \"Response\":", 30 | "{1} - use SingleEventMessage sample", 31 | }; 32 | 33 | //[JsonProperty(Required = Required.Always)] 34 | public GoogleCalendarPluginCommand[] Commands = 35 | { 36 | new GoogleCalendarPluginCommand 37 | { 38 | Name = "Today's events", 39 | Tokens = new[] 40 | { 41 | new Token 42 | { 43 | SuccessRate = 90, 44 | Type = TokenType.Command, 45 | Value = new[] {"Календарь", "События", "Расписание"} 46 | }, 47 | new Token 48 | { 49 | SuccessRate = 90, 50 | Type = TokenType.Command, 51 | Value = new[] {"на"} 52 | }, 53 | new Token 54 | { 55 | SuccessRate = 90, 56 | Type = TokenType.Command, 57 | Value = new[] {"сегодня"} 58 | } 59 | }, 60 | CalendarId = "", 61 | DaysStart = 0, 62 | DaysCount = 1, 63 | SingleEventMessage = "{StarTime} - {Summary}", 64 | Response = "Расписание на сегодня: {1}", 65 | MaxEvents = 10 66 | }, 67 | new GoogleCalendarPluginCommand 68 | { 69 | Name = "Tomorrow's events", 70 | Tokens = new[] 71 | { 72 | new Token 73 | { 74 | SuccessRate = 90, 75 | Type = TokenType.Command, 76 | Value = new[] {"Календарь", "События", "Расписание"} 77 | }, 78 | new Token 79 | { 80 | SuccessRate = 90, 81 | Type = TokenType.Command, 82 | Value = new[] {"на"} 83 | }, 84 | new Token 85 | { 86 | SuccessRate = 90, 87 | Type = TokenType.Command, 88 | Value = new[] {"завтра"} 89 | } 90 | }, 91 | CalendarId = "", 92 | DaysStart = 1, 93 | DaysCount = 1, 94 | SingleEventMessage = "{StarTime} - {Summary}", 95 | Response = "Расписание на завтра: {1}", 96 | MaxEvents = 10 97 | }, 98 | new GoogleCalendarPluginCommand 99 | { 100 | Name = "Today's birthdays", 101 | Tokens = new[] 102 | { 103 | new Token 104 | { 105 | SuccessRate = 90, 106 | Type = TokenType.Command, 107 | Value = new[] {"Дни"} 108 | }, 109 | new Token 110 | { 111 | SuccessRate = 90, 112 | Type = TokenType.Command, 113 | Value = new[] {"рождения"} 114 | }, 115 | new Token 116 | { 117 | SuccessRate = 90, 118 | Type = TokenType.Command, 119 | Value = new[] {"сегодня"} 120 | } 121 | }, 122 | CalendarId = "addressbook#contacts@group.v.calendar.google.com", 123 | DaysStart = 0, 124 | DaysCount = 1, 125 | SingleEventMessage = "{Summary}", 126 | Response = "Дни рождения сегодня: {1}", 127 | MaxEvents = 10 128 | }, 129 | new GoogleCalendarPluginCommand 130 | { 131 | Name = "Tomorrow's birthdays", 132 | Tokens = new[] 133 | { 134 | new Token 135 | { 136 | SuccessRate = 90, 137 | Type = TokenType.Command, 138 | Value = new[] {"Дни"} 139 | }, 140 | new Token 141 | { 142 | SuccessRate = 90, 143 | Type = TokenType.Command, 144 | Value = new[] {"рождения"} 145 | }, 146 | new Token 147 | { 148 | SuccessRate = 90, 149 | Type = TokenType.Command, 150 | Value = new[] { "завтра" } 151 | } 152 | }, 153 | CalendarId = "addressbook#contacts@group.v.calendar.google.com", 154 | DaysStart = 0, 155 | DaysCount = 1, 156 | SingleEventMessage = "{Summary}", 157 | Response = "Дни рождения завтра: {1}", 158 | MaxEvents = 10 159 | }, 160 | new GoogleCalendarPluginCommand 161 | { 162 | Name = "Upcoming birthdays", 163 | Tokens = new[] 164 | { 165 | new Token 166 | { 167 | SuccessRate = 90, 168 | Type = TokenType.Command, 169 | Value = new[] {"Ближайшие"} 170 | }, 171 | new Token 172 | { 173 | SuccessRate = 90, 174 | Type = TokenType.Command, 175 | Value = new[] {"дни"} 176 | }, 177 | new Token 178 | { 179 | SuccessRate = 90, 180 | Type = TokenType.Command, 181 | Value = new[] {"рождения"} 182 | }, 183 | }, 184 | CalendarId = "addressbook#contacts@group.v.calendar.google.com", 185 | DaysStart = 0, 186 | DaysCount = 7, 187 | SingleEventMessage = "{StarDate} - {Summary}", 188 | Response = "Приближающиеся дни рождения: {1}", 189 | MaxEvents = 10 190 | } 191 | }; 192 | 193 | public string MoreResultsAvailableMessage = "и дальнейшие события, не вошедшие в список"; 194 | public string NoEventsMessage = "пусто"; 195 | public string NoDataMessage = "не могу получить данные с сервера"; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /GoogleCalendarPlugin/GoogleCalendarPluginSettings_eng.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConfigurationNote": [ 3 | "Interpolation macros:", 4 | "Only for \"SingleEventMessage\":", 5 | "{Attendees} - list of attendees", 6 | "{Description} - event description", 7 | "{Location} - event location", 8 | "{Summary} - event summary", 9 | "{Length} - event duration", 10 | "{StartYear} - event start year", 11 | "{StartMonth} - event start month", 12 | "{StartDay} - event start day", 13 | "{StarDate} - event start day and month", 14 | "{StarTime} - event start hour and minute", 15 | "{EndYear} - event end year", 16 | "{EndMonth} - event end month", 17 | "{EndDay} - event end day", 18 | "{EndDate} - event end day and month", 19 | "{EndTime} - event end hour and minute", 20 | "", 21 | "Only for \"Response\":", 22 | "{1} - use SingleEventMessage sample" 23 | ], 24 | "Commands": [ 25 | { 26 | "SingleEventMessage": "{StarTime} - {Summary}", 27 | "Response": "Today's events: {1}", 28 | "CalendarId": "", 29 | "DaysStart": 0, 30 | "DaysCount": 1, 31 | "MaxEvents": 10, 32 | "Name": "Today's events", 33 | "Tokens": [ 34 | { 35 | "Value": [ 36 | "Schedule", 37 | "Events" 38 | ], 39 | "Type": "Command", 40 | "SuccessRate": 90 41 | }, 42 | { 43 | "Value": [ 44 | "for" 45 | ], 46 | "Type": "Command", 47 | "SuccessRate": 90 48 | }, 49 | { 50 | "Value": [ 51 | "today" 52 | ], 53 | "Type": "Command", 54 | "SuccessRate": 90 55 | } 56 | ] 57 | }, 58 | { 59 | "SingleEventMessage": "{StarTime} - {Summary}", 60 | "Response": "Tomorrow's events: {1}", 61 | "CalendarId": "", 62 | "DaysStart": 1, 63 | "DaysCount": 1, 64 | "MaxEvents": 10, 65 | "Name": "Tomorrow's events", 66 | "Tokens": [ 67 | { 68 | "Value": [ 69 | "Schedule", 70 | "Events" 71 | ], 72 | "Type": "Command", 73 | "SuccessRate": 90 74 | }, 75 | { 76 | "Value": [ 77 | "for" 78 | ], 79 | "Type": "Command", 80 | "SuccessRate": 90 81 | }, 82 | { 83 | "Value": [ 84 | "tomorrow" 85 | ], 86 | "Type": "Command", 87 | "SuccessRate": 90 88 | } 89 | ] 90 | }, 91 | { 92 | "SingleEventMessage": "{Summary}", 93 | "Response": "Today's birthdays: {1}", 94 | "CalendarId": "addressbook#contacts@group.v.calendar.google.com", 95 | "DaysStart": 0, 96 | "DaysCount": 1, 97 | "MaxEvents": 10, 98 | "Name": "Today's birthdays", 99 | "Tokens": [ 100 | { 101 | "Value": [ 102 | "birthdays" 103 | ], 104 | "Type": "Command", 105 | "SuccessRate": 90 106 | }, 107 | { 108 | "Value": [ 109 | "for" 110 | ], 111 | "Type": "Command", 112 | "SuccessRate": 90 113 | }, 114 | { 115 | "Value": [ 116 | "today" 117 | ], 118 | "Type": "Command", 119 | "SuccessRate": 90 120 | } 121 | ] 122 | }, 123 | { 124 | "SingleEventMessage": "{StarDate} - {Summary}", 125 | "Response": "Upcoming birthdays: {1}", 126 | "CalendarId": "addressbook#contacts@group.v.calendar.google.com", 127 | "DaysStart": 0, 128 | "DaysCount": 7, 129 | "MaxEvents": 10, 130 | "Name": "Upcoming birthdays", 131 | "Tokens": [ 132 | { 133 | "Value": [ 134 | "Upcoming" 135 | ], 136 | "Type": "Command", 137 | "SuccessRate": 90 138 | }, 139 | { 140 | "Value": [ 141 | "birthdays" 142 | ], 143 | "Type": "Command", 144 | "SuccessRate": 90 145 | } 146 | ] 147 | } 148 | ], 149 | "MoreResultsAvailableMessage": "and there are more events not fitted the list", 150 | "NoEventsMessage": "no events", 151 | "NoDataMessage": "can not get data from server" 152 | } -------------------------------------------------------------------------------- /GoogleCalendarPlugin/birthdays_calendar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jekyll2014/VoiceAssistant/bea10e8c38475eb44d8f67e4f7300f4625a33276/GoogleCalendarPlugin/birthdays_calendar.jpg -------------------------------------------------------------------------------- /GoogleCalendarPlugin/calendarId.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jekyll2014/VoiceAssistant/bea10e8c38475eb44d8f67e4f7300f4625a33276/GoogleCalendarPlugin/calendarId.jpg -------------------------------------------------------------------------------- /HelloPlugin/HelloPlugin.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | using PluginInterface; 8 | 9 | namespace HelloPlugin 10 | { 11 | public class HelloPlugin : PluginBase 12 | { 13 | private readonly HelloPluginCommand[] HelloCommands; 14 | 15 | public HelloPlugin(IAudioOutSingleton audioOut, string currentCulture, string pluginPath) : base(audioOut, currentCulture, pluginPath) 16 | { 17 | var configBuilder = new Config($"{PluginPath}\\{PluginConfigFile}"); 18 | if (!File.Exists($"{PluginPath}\\{PluginConfigFile}")) 19 | { 20 | configBuilder.SaveConfig(); 21 | } 22 | 23 | HelloCommands = configBuilder.ConfigStorage.Commands; 24 | 25 | if (HelloCommands is PluginCommand[] newCmds) 26 | { 27 | _commands = newCmds; 28 | } 29 | 30 | // example of listening to the audio/word stream from core module. 31 | //base.CanAcceptSound = true; 32 | //base.CanAcceptWords = true; 33 | 34 | // example of injecting command audio/text to execute by main module. 35 | //base.CanInjectSound = true; 36 | //base.CanInjectWords = true; 37 | } 38 | 39 | public override void Execute(string commandName, List commandTokens) 40 | { 41 | var command = HelloCommands.FirstOrDefault(n => n.Name == commandName); 42 | 43 | if (command == null) 44 | { 45 | return; 46 | } 47 | // example of listening to the audio/word stream from core module 48 | /* 49 | var sndData = GetSound(); 50 | AudioOut.PlayDataBuffer(sndData); 51 | 52 | var wordsData = ""; 53 | foreach (var w in GetWords()) 54 | { 55 | wordsData += " " + w; 56 | } 57 | Console.WriteLine($"ReceivedText: {wordsData}"); 58 | */ 59 | 60 | // example of injecting command audio/text to execute by main module 61 | /*InjectTextCommand("Вася привет"); 62 | InjectAudioCommand(new byte[1024], 44100, 16, 1);*/ 63 | 64 | AudioOut.Speak(command.Response); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /HelloPlugin/HelloPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /HelloPlugin/HelloPluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace HelloPlugin 6 | { 7 | public class HelloPluginCommand : PluginCommand 8 | { 9 | public string Response = ""; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /HelloPlugin/HelloPluginSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace HelloPlugin 6 | { 7 | public class HelloPluginSettings 8 | { 9 | //[JsonProperty(Required = Required.Always)] 10 | public HelloPluginCommand[] Commands = 11 | { 12 | new HelloPluginCommand 13 | { 14 | Name = "Greeting informal", 15 | Tokens = new[] 16 | { 17 | new Token 18 | { 19 | SuccessRate = 90, 20 | Type = TokenType.Command, 21 | Value = new[] {"привет"} 22 | } 23 | }, 24 | Response = "И тебе привет" 25 | }, 26 | new HelloPluginCommand 27 | { 28 | Name = "Greeting formal", 29 | Tokens = new[] 30 | { 31 | new Token 32 | { 33 | SuccessRate = 90, 34 | Type = TokenType.Command, 35 | Value = new[] {"Здравствуй"} 36 | } 37 | }, 38 | Response = "И тебе не болеть" 39 | }, 40 | new HelloPluginCommand 41 | { 42 | Name = "Greeting wishing", 43 | Tokens = new[] 44 | { 45 | new Token 46 | { 47 | SuccessRate = 90, 48 | Type = TokenType.Command, 49 | Value = new[] {"Доброго", "Добрый"} 50 | }, 51 | new Token 52 | { 53 | SuccessRate = 90, 54 | Type = TokenType.Command, 55 | Value = new[] {"дня", "день"} 56 | } 57 | }, 58 | Response = "И тебе доброго дня" 59 | }, 60 | new HelloPluginCommand 61 | { 62 | Name = "Greeting short informal", 63 | Tokens = new[] 64 | { 65 | new Token 66 | { 67 | SuccessRate = 90, 68 | Type = TokenType.Command, 69 | Value = new[] {"Хай"} 70 | } 71 | }, 72 | Response = "Хаюшки" 73 | } 74 | }; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /HelloPlugin/HelloPluginSettings_eng.json: -------------------------------------------------------------------------------- 1 | { 2 | "Commands": [ 3 | { 4 | "Response": "Nice to hear you", 5 | "Name": "Greeting informal", 6 | "Tokens": [ 7 | { 8 | "Value": [ 9 | "Greeting" 10 | ], 11 | "Type": "Command", 12 | "SuccessRate": 90 13 | } 14 | ] 15 | }, 16 | { 17 | "Response": "And you too", 18 | "Name": "Greeting formal", 19 | "Tokens": [ 20 | { 21 | "Value": [ 22 | "Hello" 23 | ], 24 | "Type": "Command", 25 | "SuccessRate": 90 26 | } 27 | ] 28 | }, 29 | { 30 | "Response": "Have a good day too", 31 | "Name": "Greeting wishing", 32 | "Tokens": [ 33 | { 34 | "Value": [ 35 | "Have" 36 | ], 37 | "Type": "Command", 38 | "SuccessRate": 90 39 | }, 40 | { 41 | "Value": [ 42 | "a" 43 | ], 44 | "Type": "Command", 45 | "SuccessRate": 90 46 | }, 47 | { 48 | "Value": [ 49 | "good" 50 | ], 51 | "Type": "Command", 52 | "SuccessRate": 90 53 | }, 54 | { 55 | "Value": [ 56 | "day" 57 | ], 58 | "Type": "Command", 59 | "SuccessRate": 90 60 | } 61 | ] 62 | }, 63 | { 64 | "Response": "Hi", 65 | "Name": "Greeting short informal", 66 | "Tokens": [ 67 | { 68 | "Value": [ 69 | "Hi" 70 | ], 71 | "Type": "Command", 72 | "SuccessRate": 90 73 | } 74 | ] 75 | } 76 | ] 77 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Andrey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MQTTInPlugin/MQTTInPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /MQTTInPlugin/MQTTInPluginSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace MQTTInPlugin 6 | { 7 | public class MQTTInPluginSettings 8 | { 9 | public string RequestServer = ""; 10 | public int RequestServerPort = 1883; 11 | public string RequestServerLogin = ""; 12 | public string RequestServerPassword = ""; 13 | public string ClientID = "MQTTInPlugin"; 14 | public bool AddTimeStampToClientID = true; 15 | public bool KeepConnectionAlive = true; 16 | public int ReconnectTimeOut = 60; 17 | 18 | //[JsonProperty(Required = Required.Always)] 19 | public MQTTInTopic[] Topics = 20 | { 21 | new MQTTInTopic 22 | { 23 | MQTTSubscribeTopic = "testTopic", 24 | DataIsAudio = false 25 | } 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /MQTTInPlugin/MQTTInTopic.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace MQTTInPlugin 4 | { 5 | public class MQTTInTopic 6 | { 7 | public string MQTTSubscribeTopic = ""; 8 | public bool DataIsAudio = false; 9 | public int samplingRate = 16000; 10 | public int bits = 16; 11 | public int channels = 1; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MQTTOutPlugin/MQTTOutPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /MQTTOutPlugin/MQTTOutPluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace MQTTOutPlugin 6 | { 7 | public class MQTTOutPluginCommand : PluginCommand 8 | { 9 | public string PluginResponse = ""; 10 | public string MQTTRequestTopic = ""; 11 | public string MQTTRequestText = ""; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MQTTOutPlugin/MQTTOutPluginSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace MQTTOutPlugin 6 | { 7 | public class MQTTOutPluginSettings 8 | { 9 | public string RequestServer = ""; 10 | public int RequestServerPort = 1883; 11 | public string RequestServerLogin = ""; 12 | public string RequestServerPassword = ""; 13 | public string ClientID = "MQTTOutPlugin"; 14 | public bool AddTimeStampToClientID = true; 15 | public bool KeepConnectionAlive = true; 16 | public int ReconnectTimeOut = 60; 17 | 18 | public string FailedConnectionResponse = "подключение не работает"; 19 | 20 | //[JsonProperty(Required = Required.Always)] 21 | public MQTTOutPluginCommand[] Commands = 22 | { 23 | new MQTTOutPluginCommand 24 | { 25 | Name = "Test", 26 | Tokens = new[] 27 | { 28 | new Token 29 | { 30 | SuccessRate = 90, 31 | Type = TokenType.Command, 32 | Value = new[] {"тест"} 33 | }, 34 | new Token 35 | { 36 | SuccessRate = 90, 37 | Type = TokenType.Command, 38 | Value = new[] {"соединения"} 39 | } 40 | }, 41 | PluginResponse = "Соединение установлено", 42 | MQTTRequestText = "вася привет", 43 | MQTTRequestTopic = "testTopic" 44 | } 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /MQTTOutPlugin/MQTTPluginSettings_eng.json: -------------------------------------------------------------------------------- 1 | { 2 | "Commands": [ 3 | { 4 | "Response": "Nice to hear you", 5 | "Name": "Greeting informal", 6 | "Tokens": [ 7 | { 8 | "Value": [ 9 | "Greeting" 10 | ], 11 | "Type": "Command", 12 | "SuccessRate": 90 13 | } 14 | ] 15 | }, 16 | { 17 | "Response": "And you too", 18 | "Name": "Greeting formal", 19 | "Tokens": [ 20 | { 21 | "Value": [ 22 | "Hello" 23 | ], 24 | "Type": "Command", 25 | "SuccessRate": 90 26 | } 27 | ] 28 | }, 29 | { 30 | "Response": "Have a good day too", 31 | "Name": "Greeting wishing", 32 | "Tokens": [ 33 | { 34 | "Value": [ 35 | "Have" 36 | ], 37 | "Type": "Command", 38 | "SuccessRate": 90 39 | }, 40 | { 41 | "Value": [ 42 | "a" 43 | ], 44 | "Type": "Command", 45 | "SuccessRate": 90 46 | }, 47 | { 48 | "Value": [ 49 | "good" 50 | ], 51 | "Type": "Command", 52 | "SuccessRate": 90 53 | }, 54 | { 55 | "Value": [ 56 | "day" 57 | ], 58 | "Type": "Command", 59 | "SuccessRate": 90 60 | } 61 | ] 62 | }, 63 | { 64 | "Response": "Hi", 65 | "Name": "Greeting short informal", 66 | "Tokens": [ 67 | { 68 | "Value": [ 69 | "Hi" 70 | ], 71 | "Type": "Command", 72 | "SuccessRate": 90 73 | } 74 | ] 75 | } 76 | ] 77 | } -------------------------------------------------------------------------------- /OpenWeatherPlugin/CurrentWeatherData.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System; 4 | 5 | namespace OpenWeatherPlugin 6 | { 7 | public partial class OpenWeatherPlugin 8 | { 9 | public class CurrentWeatherData 10 | { 11 | // weather.description - Weather condition within the group. You can get the output in your language. 12 | public string WeatherDescription; 13 | 14 | // main.temp - Temperature 15 | public int Temperature; 16 | 17 | // main.humidity - Humidity, % 18 | public int Humidity; 19 | 20 | // main.pressure - Atmospheric pressure (on the sea level, if there is no sea_level or grnd_level data), hPa 21 | public int Pressure; 22 | 23 | // wind.speed - Wind speed. Unit Default: meter/sec, Metric: meter/sec, Imperial: miles/hour 24 | public int WindSpeed; 25 | 26 | // wind.deg - Wind direction, degrees (meteorological) 27 | public int WindDirection; 28 | 29 | public string WindDirectionName; 30 | 31 | // clouds.all - Cloudiness, % 32 | public int Clouds; 33 | 34 | // dt - Time of data calculation, unix, UTC 35 | public DateTime RecordDateTime; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /OpenWeatherPlugin/OpenWeatherPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /OpenWeatherPlugin/OpenWeatherPluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace OpenWeatherPlugin 6 | { 7 | public class OpenWeatherPluginCommand : PluginCommand 8 | { 9 | public string DayTime = ""; 10 | public string CityId = ""; 11 | public string Response = ""; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /OpenWeatherPlugin/OpenWeatherPluginSettings_eng.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jekyll2014/VoiceAssistant/bea10e8c38475eb44d8f67e4f7300f4625a33276/OpenWeatherPlugin/OpenWeatherPluginSettings_eng.json -------------------------------------------------------------------------------- /OpenWeatherPlugin/WeatherCurrent.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System.Collections.Generic; 4 | 5 | namespace OpenWeatherPlugin 6 | { 7 | public class WeatherCurrent 8 | { 9 | public Coord coord { get; set; } 10 | public List weather { get; set; } 11 | public string @base { get; set; } 12 | public Main main { get; set; } 13 | public int visibility { get; set; } 14 | public Wind wind { get; set; } 15 | public Clouds clouds { get; set; } 16 | public Rain rain { get; set; } 17 | public Snow snow { get; set; } 18 | public int dt { get; set; } 19 | public Sys sys { get; set; } 20 | public int timezone { get; set; } 21 | public int id { get; set; } 22 | public string name { get; set; } 23 | public int cod { get; set; } 24 | 25 | public class Coord 26 | { 27 | public double lon { get; set; } 28 | public double lat { get; set; } 29 | } 30 | 31 | public class Weather 32 | { 33 | public int id { get; set; } 34 | public string main { get; set; } 35 | public string description { get; set; } 36 | public string icon { get; set; } 37 | } 38 | 39 | public class Main 40 | { 41 | public double temp { get; set; } 42 | public double feels_like { get; set; } 43 | public double temp_min { get; set; } 44 | public double temp_max { get; set; } 45 | public int pressure { get; set; } 46 | public int humidity { get; set; } 47 | } 48 | 49 | public class Wind 50 | { 51 | public double speed { get; set; } 52 | public int deg { get; set; } 53 | } 54 | 55 | public class Clouds 56 | { 57 | public int all { get; set; } 58 | } 59 | 60 | public class Rain 61 | { 62 | //[JsonProperty(PropertyName = "1h")] 63 | public int _1h { get; set; } 64 | //[JsonProperty(PropertyName = "3h")] 65 | public int _3h { get; set; } 66 | } 67 | 68 | public class Snow 69 | { 70 | public int _1h { get; set; } 71 | public int _3h { get; set; } 72 | } 73 | 74 | public class Sys 75 | { 76 | public int type { get; set; } 77 | public int id { get; set; } 78 | public string country { get; set; } 79 | public int sunrise { get; set; } 80 | public int sunset { get; set; } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /OpenWeatherPlugin/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System.Collections.Generic; 4 | 5 | namespace OpenWeatherPlugin 6 | { 7 | // Root myDeserializedClass = JsonConvert.DeserializeObject(myJsonResponse); 8 | public class WeatherForecast 9 | { 10 | public string cod { get; set; } 11 | public int message { get; set; } 12 | public int cnt { get; set; } 13 | public List list { get; set; } 14 | public City city { get; set; } 15 | 16 | public class Main 17 | { 18 | public double temp { get; set; } 19 | public double feels_like { get; set; } 20 | public double temp_min { get; set; } 21 | public double temp_max { get; set; } 22 | public int pressure { get; set; } 23 | public int sea_level { get; set; } 24 | public int grnd_level { get; set; } 25 | public int humidity { get; set; } 26 | public double temp_kf { get; set; } 27 | } 28 | 29 | public class Weather 30 | { 31 | public int id { get; set; } 32 | public string main { get; set; } 33 | public string description { get; set; } 34 | public string icon { get; set; } 35 | } 36 | 37 | public class Clouds 38 | { 39 | public int all { get; set; } 40 | } 41 | 42 | public class Wind 43 | { 44 | public double speed { get; set; } 45 | public int deg { get; set; } 46 | public double gust { get; set; } 47 | } 48 | 49 | public class Sys 50 | { 51 | public string pod { get; set; } 52 | } 53 | 54 | public class Rain 55 | { 56 | public double _3h { get; set; } 57 | } 58 | 59 | public class Snow 60 | { 61 | public double _3h { get; set; } 62 | } 63 | 64 | public class List 65 | { 66 | public int dt { get; set; } 67 | public Main main { get; set; } 68 | public List weather { get; set; } 69 | public Clouds clouds { get; set; } 70 | public Wind wind { get; set; } 71 | public int visibility { get; set; } 72 | public double pop { get; set; } 73 | public Sys sys { get; set; } 74 | public string dt_txt { get; set; } 75 | public Rain rain { get; set; } 76 | public Snow snow { get; set; } 77 | } 78 | 79 | public class Coord 80 | { 81 | public double lat { get; set; } 82 | public double lon { get; set; } 83 | } 84 | 85 | public class City 86 | { 87 | public int id { get; set; } 88 | public string name { get; set; } 89 | public Coord coord { get; set; } 90 | public string country { get; set; } 91 | public int population { get; set; } 92 | public int timezone { get; set; } 93 | public int sunrise { get; set; } 94 | public int sunset { get; set; } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /OpenWeatherPlugin/WeatherForecastData.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System; 4 | 5 | namespace OpenWeatherPlugin 6 | { 7 | public partial class OpenWeatherPlugin 8 | { 9 | public class WeatherForecastData 10 | { 11 | // weather.description - Weather condition within the group. You can get the output in your language. 12 | public string WeatherDescription; 13 | 14 | // main.temp - Temperature 15 | public int Temperature; 16 | public int MinTemperature; 17 | public int MaxTemperature; 18 | 19 | // main.humidity - Humidity, % 20 | public int Humidity; 21 | public int MinHumidity; 22 | public int MaxHumidity; 23 | 24 | // main.pressure - Atmospheric pressure (on the sea level, if there is no sea_level or grnd_level data), hPa 25 | public int Pressure; 26 | public int MinPressure; 27 | public int MaxPressure; 28 | 29 | // wind.speed - Wind speed. Unit Default: meter/sec, Metric: meter/sec, Imperial: miles/hour 30 | public int WindSpeed; 31 | public int MinWindSpeed; 32 | public int MaxWindSpeed; 33 | 34 | // wind.deg - Wind direction, degrees (meteorological) 35 | public int WindDirection; 36 | public int MinWindDirection; 37 | public int MaxWindDirection; 38 | 39 | public string WindDirectionName; 40 | public string MinWindDirectionName; 41 | public string MaxWindDirectionName; 42 | 43 | // clouds.all - Cloudiness, % 44 | public int Clouds; 45 | public int MinClouds; 46 | public int MaxClouds; 47 | 48 | // dt - Time of data calculation, unix, UTC 49 | public DateTime RecordDateTime; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /PluginInterface/Config.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | 10 | namespace PluginInterface 11 | { 12 | public class Config where T : class, new() 13 | { 14 | private readonly string _configFileName; 15 | 16 | public T ConfigStorage { get; set; } = new T(); 17 | 18 | public Config(string file) 19 | { 20 | _configFileName = file; 21 | LoadConfig(); 22 | } 23 | 24 | public bool LoadConfig() 25 | { 26 | if (string.IsNullOrEmpty(_configFileName)) 27 | return false; 28 | 29 | try 30 | { 31 | var json = JObject.Parse(File.ReadAllText(_configFileName)); 32 | ConfigStorage = GetSection(json, ""); 33 | } 34 | catch 35 | { 36 | return false; 37 | } 38 | 39 | return true; 40 | } 41 | 42 | public TK GetSection(JObject json, string sectionName = null) where TK : class, new() 43 | { 44 | if (string.IsNullOrEmpty(_configFileName)) 45 | return default; 46 | 47 | if (string.IsNullOrEmpty(sectionName)) 48 | return json?.ToObject() ?? 49 | throw new InvalidOperationException($"Cannot find section {sectionName}"); 50 | 51 | return json[sectionName]?.ToObject() ?? 52 | throw new InvalidOperationException($"Cannot find section {sectionName}"); 53 | } 54 | 55 | public bool SaveConfig() 56 | { 57 | if (string.IsNullOrEmpty(_configFileName)) 58 | return false; 59 | 60 | try 61 | { 62 | File.WriteAllText(_configFileName, 63 | JsonConvert.SerializeObject(ConfigStorage, Formatting.Indented, new Newtonsoft.Json.JsonSerializerSettings() 64 | { 65 | Converters = new List 66 | { 67 | new Newtonsoft.Json.Converters.StringEnumConverter() 68 | } 69 | })); 70 | } 71 | catch 72 | { 73 | return false; 74 | } 75 | 76 | return true; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /PluginInterface/GenericNumberSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace PluginInterface 4 | { 5 | public class GenericNumberSettings 6 | { 7 | public string Culture = "ru-RU"; 8 | 9 | public string Minus = "минус"; 10 | public string Plus = "плюс"; 11 | public string Zero = "ноль"; 12 | public string OneFemale = "одна"; 13 | public string TwoFemale = "две"; 14 | 15 | public string One = "один"; 16 | public string Two = "два"; 17 | public string Three = "три"; 18 | public string Four = "четыре"; 19 | public string Five = "пять"; 20 | public string Six = "шесть"; 21 | public string Seven = "семь"; 22 | public string Eight = "восемь"; 23 | public string Nine = "девять"; 24 | public string Elleven = "одиннадцать"; 25 | public string Twelve = "двенадцать"; 26 | public string Thirteen = "тринадцать"; 27 | public string Fourteen = "четырнадцать"; 28 | public string Fifteen = "пятнадцать"; 29 | public string Sixteen = "шестнадцать"; 30 | public string Seventeen = "семнадцать"; 31 | public string Eighteen = "восемнадцать"; 32 | public string Nineteen = "девятнадцать"; 33 | 34 | public string Ten = "десять"; 35 | public string Twenty = "двадцать"; 36 | public string Thirty = "тридцать"; 37 | public string Forty = "сорок"; 38 | public string Fifty = "пятьдесят"; 39 | public string Sixty = "шестьдесят"; 40 | public string Seventy = "семьдесят"; 41 | public string Eighty = "восемьдесят"; 42 | public string Ninety = "девяносто"; 43 | 44 | public string OneHundred = "сто"; 45 | public string TwoHundred = "двести"; 46 | public string ThreeHundred = "триста"; 47 | public string FourHundred = "четыреста"; 48 | public string FiveHundred = "пятьсот"; 49 | public string SixHundred = "шестьсот"; 50 | public string SevenHundred = "семьсот"; 51 | public string EightHundred = "восемьсот"; 52 | public string NineHundred = "девятьсот"; 53 | 54 | public string OneThousand = "тысяча"; 55 | public string TwoThousand = "тысячи"; 56 | public string FiveThousand = "тысяч"; 57 | 58 | public string OneMillion = "миллион"; 59 | public string TwoMillion = "миллиона"; 60 | public string FiveMillion = "миллионов"; 61 | 62 | public string OneBillion = "миллиард"; 63 | public string TwoBillion = "миллиарда"; 64 | public string FiveBillion = "миллиардов"; 65 | 66 | public string OneTrillion = "триллион"; 67 | public string TwoTrillion = "триллиона"; 68 | public string FiveTrillion = "триллионов"; 69 | 70 | public string OneQuadrillion = "квадриллион"; 71 | public string TwoQuadrillion = "квадриллиона"; 72 | public string FiveQuadrillion = "квадриллионов"; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /PluginInterface/IAudioOutSingleton.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace PluginInterface 4 | { 5 | public interface IAudioOutSingleton 6 | { 7 | void PlayFile(string audioFile, bool exclusive = true); 8 | void Speak(string text, bool exclusive = true); 9 | void PlayDataBuffer(byte[] data, bool exclusive = true); 10 | } 11 | } -------------------------------------------------------------------------------- /PluginInterface/INumberToText.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace PluginInterface 4 | { 5 | public interface INumberToText 6 | { 7 | public abstract string ConvertNumberToString(long val); 8 | } 9 | } -------------------------------------------------------------------------------- /PluginInterface/ITextToNumber.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace PluginInterface 4 | { 5 | public interface ITextToNumber 6 | { 7 | public abstract long ConvertStringToNumber(string numberString, int ratio = 100); 8 | } 9 | } -------------------------------------------------------------------------------- /PluginInterface/NumberToTextEmpty.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace PluginInterface 4 | { 5 | public class NumberToTextEmpty : INumberToText 6 | { 7 | public string ConvertNumberToString(long val) 8 | { 9 | return string.Empty; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /PluginInterface/NumberToTextEng.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System; 4 | 5 | namespace PluginInterface 6 | { 7 | public class NumberToTextEng : INumberToText 8 | { 9 | //converts any number between 0 & INT_MAX (2,147,483,647) 10 | public string ConvertNumberToString(long val) 11 | { 12 | var result = string.Empty; 13 | 14 | if (val < 0) 15 | { 16 | result = "minus "; 17 | val = -val; 18 | } 19 | 20 | if (val == 0) 21 | result = "zero"; 22 | else if (val < 10) 23 | result += ConvertDigitToString(val); 24 | else if (val < 20) 25 | result += ConvertTeensToString(val); 26 | else if (val < 100) 27 | result += ConvertHighTensToString(val); 28 | else if (val < 1000) 29 | result += ConvertBigNumberToString(val, (long)1e2, "hundred"); 30 | else if (val < 1e6) 31 | result += ConvertBigNumberToString(val, (long)1e3, "thousand"); 32 | else if (val < 1e9) 33 | result += ConvertBigNumberToString(val, (long)1e6, "million"); 34 | else if (val < 1e12) 35 | result += ConvertBigNumberToString(val, (long)1e9, "billion"); 36 | else if (val < 1e15) 37 | result += ConvertBigNumberToString(val, (long)1e12, "trillion"); 38 | else if (val < 1e18) 39 | result += ConvertBigNumberToString(val, (long)1e15, "quadrillion"); 40 | else if (val < 1e21) 41 | result += ConvertBigNumberToString(val, (long)1e18, "quintillion"); 42 | else 43 | return "more than quintillion"; 44 | 45 | return result; 46 | } 47 | 48 | private string ConvertDigitToString(long i) 49 | { 50 | switch (i) 51 | { 52 | case 0: return ""; 53 | case 1: return "one"; 54 | case 2: return "two"; 55 | case 3: return "three"; 56 | case 4: return "four"; 57 | case 5: return "five"; 58 | case 6: return "six"; 59 | case 7: return "seven"; 60 | case 8: return "eight"; 61 | case 9: return "nine"; 62 | default: 63 | throw new IndexOutOfRangeException(String.Format("{0} not a digit", i)); 64 | } 65 | } 66 | 67 | //assumes a number between 10 & 19 68 | private string ConvertTeensToString(long n) 69 | { 70 | switch (n) 71 | { 72 | case 10: return "ten"; 73 | case 11: return "eleven"; 74 | case 12: return "twelve"; 75 | case 13: return "thirteen"; 76 | case 14: return "fourteen"; 77 | case 15: return "fiveteen"; 78 | case 16: return "sixteen"; 79 | case 17: return "seventeen"; 80 | case 18: return "eighteen"; 81 | case 19: return "nineteen"; 82 | default: 83 | throw new IndexOutOfRangeException(String.Format("{0} not a teen", n)); 84 | } 85 | } 86 | 87 | //assumes a number between 20 and 99 88 | private string ConvertHighTensToString(long n) 89 | { 90 | long tensDigit = (long)(Math.Floor((double)n / 10.0)); 91 | 92 | string tensStr; 93 | switch (tensDigit) 94 | { 95 | case 2: tensStr = "twenty"; break; 96 | case 3: tensStr = "thirty"; break; 97 | case 4: tensStr = "forty"; break; 98 | case 5: tensStr = "fifty"; break; 99 | case 6: tensStr = "sixty"; break; 100 | case 7: tensStr = "seventy"; break; 101 | case 8: tensStr = "eighty"; break; 102 | case 9: tensStr = "ninety"; break; 103 | default: 104 | throw new IndexOutOfRangeException(String.Format("{0} not in range 20-99", n)); 105 | } 106 | if (n % 10 == 0) return tensStr; 107 | string onesStr = ConvertDigitToString(n - tensDigit * 10); 108 | return tensStr + "-" + onesStr; 109 | } 110 | 111 | // Use this to convert any integer bigger than 99 112 | private string ConvertBigNumberToString(long n, long baseNum, string baseNumStr) 113 | { 114 | // special case: use commas to separate portions of the number, unless we are in the hundreds 115 | string separator = (baseNumStr != "hundred") ? ", " : " "; 116 | 117 | // Strategy: translate the first portion of the number, then recursively translate the remaining sections. 118 | // Step 1: strip off first portion, and convert it to string: 119 | long bigPart = (long)(Math.Floor((double)n / baseNum)); 120 | string bigPartStr = ConvertNumberToString(bigPart) + " " + baseNumStr; 121 | // Step 2: check to see whether we're done: 122 | if (n % baseNum == 0) return bigPartStr; 123 | // Step 3: concatenate 1st part of string with recursively generated remainder: 124 | long restOfNumber = n - bigPart * baseNum; 125 | return bigPartStr + separator + ConvertNumberToString(restOfNumber); 126 | } 127 | } 128 | } 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /PluginInterface/NumberToTextFactory.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace PluginInterface 4 | { 5 | public static class NumberToTextFactory 6 | { 7 | public static INumberToText GetNumberToTextConvertor(string culture) 8 | { 9 | if (culture.StartsWith("ru-")) 10 | return new NumberToTextRus(); 11 | else if (culture.StartsWith("en-")) 12 | return new NumberToTextEng(); 13 | else 14 | return new NumberToTextGeneric(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /PluginInterface/NumberToTextRus.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System; 4 | using System.Text; 5 | 6 | namespace PluginInterface 7 | { 8 | public class NumberToTextRus : INumberToText 9 | { 10 | //Наименования сотен 11 | private readonly string[] _hundreds = 12 | { 13 | "", "сто ", "двести ", "триста ", "четыреста ", 14 | "пятьсот ", "шестьсот ", "семьсот ", "восемьсот ", "девятьсот " 15 | }; 16 | 17 | //Наименования десятков 18 | private readonly string[] _tens = 19 | { 20 | "", "десять ", "двадцать ", "тридцать ", "сорок ", "пятьдесят ", 21 | "шестьдесят ", "семьдесят ", "восемьдесят ", "девяносто " 22 | }; 23 | 24 | private readonly string[] _frac20 = 25 | { 26 | "", "один ", "два ", "три ", "четыре ", "пять ", "шесть ", 27 | "семь ", "восемь ", "девять ", "десять ", "одиннадцать ", 28 | "двенадцать ", "тринадцать ", "четырнадцать ", "пятнадцать ", 29 | "шестнадцать ", "семнадцать ", "восемнадцать ", "девятнадцать " 30 | }; 31 | 32 | private readonly string[] _OneTwoFiveThousand = { "тысяча", "тысячи", "тысяч" }; 33 | private readonly string[] _OneTwoFiveMillion = { "миллион", "миллиона", "миллионов" }; 34 | private readonly string[] _OneTwoFiveBillion = { "миллиард", "миллиарда", "миллиардов" }; 35 | private readonly string[] _OneTwoFiveTrillion = { "триллион", "триллиона", "триллионов" }; 36 | private readonly string[] _OneTwoFiveQuadrillion = { "квадриллион", "квадриллиона", "квадриллионов" }; 37 | 38 | private readonly string _zero = "ноль "; 39 | private readonly string _oneFemale = "одна "; 40 | private readonly string _twoFemale = "две "; 41 | private readonly string _minus = "минус "; 42 | 43 | /// 44 | /// Перевод целого числа в строку 45 | /// 46 | /// Число 47 | /// Возвращает строковую запись числа 48 | public string ConvertNumberToString(long val) 49 | { 50 | var minus = false; 51 | 52 | if (val < 0) 53 | { 54 | val = -val; 55 | minus = true; 56 | } 57 | 58 | var n = val; 59 | 60 | var r = new StringBuilder(); 61 | 62 | if (0 == n) 63 | r.Append(_zero); 64 | 65 | if (n % 1000 != 0) 66 | r.Append(Str(n, true, new[] { "", "", "" })); 67 | 68 | n /= 1000; 69 | 70 | r.Insert(0, Str(n, false, _OneTwoFiveThousand)); 71 | n /= 1000; 72 | 73 | r.Insert(0, Str(n, true, _OneTwoFiveMillion)); 74 | n /= 1000; 75 | 76 | r.Insert(0, Str(n, true, _OneTwoFiveBillion)); 77 | n /= 1000; 78 | 79 | r.Insert(0, Str(n, true, _OneTwoFiveTrillion)); 80 | n /= 1000; 81 | 82 | r.Insert(0, Str(n, true, _OneTwoFiveQuadrillion)); 83 | 84 | if (minus) 85 | r.Insert(0, _minus); 86 | 87 | return r.ToString(); 88 | } 89 | 90 | /// 91 | /// Перевод в строку числа с учётом падежного окончания относящегося к числу существительного 92 | /// 93 | /// Число 94 | /// Род существительного, которое относится к числу 95 | /// Форма существительного в единственном числе 96 | /// Форма существительного от двух до четырёх 97 | /// Форма существительного от пяти и больше 98 | /// 99 | private string Str(long val, bool male, string[] oneTwoFive) 100 | { 101 | var num = val % 1000; 102 | if (0 == num) return ""; 103 | 104 | if (num < 0) throw new ArgumentOutOfRangeException(nameof(val), "Parameter can't be less than zero"); 105 | 106 | if (!male) 107 | { 108 | _frac20[1] = _oneFemale + " "; 109 | _frac20[2] = _twoFemale + " "; 110 | } 111 | 112 | var r = new StringBuilder(_hundreds[num / 100] + " "); 113 | 114 | if (num % 100 < 20) 115 | { 116 | r.Append(_frac20[num % 100] + " "); 117 | } 118 | else 119 | { 120 | r.Append(_tens[num % 100 / 10] + " "); 121 | r.Append(_frac20[num % 10] + " "); 122 | } 123 | 124 | r.Append(Case(num, oneTwoFive[0], oneTwoFive[1], oneTwoFive[2])); 125 | 126 | if (r.Length != 0) 127 | r.Append(" "); 128 | 129 | return r.ToString(); 130 | } 131 | 132 | /// 133 | /// Выбор правильного падежного окончания сущесвительного 134 | /// 135 | /// Число 136 | /// Форма существительного в единственном числе 137 | /// Форма существительного от двух до четырёх 138 | /// Форма существительного от пяти и больше 139 | /// Возвращает существительное с падежным окончанием, которое соответсвует числу 140 | private string Case(long val, string one, string two, string five) 141 | { 142 | var t = val % 100 > 20 ? val % 10 : val % 20; 143 | 144 | switch (t) 145 | { 146 | case 1: return one + " "; 147 | case 2: 148 | case 3: 149 | case 4: return two + " "; 150 | default: return five + " "; 151 | } 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /PluginInterface/PluginBase.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | 7 | namespace PluginInterface 8 | { 9 | public abstract class PluginBase 10 | { 11 | public const string PluginInterfaceVersion = "1.1"; 12 | protected readonly string PluginPath; 13 | protected readonly string PluginConfigFile; 14 | protected readonly IAudioOutSingleton AudioOut; 15 | public volatile bool CanAcceptSound = false; 16 | public volatile bool CanAcceptWords = false; 17 | public volatile bool CanInjectSound = false; 18 | public volatile bool CanInjectWords = false; 19 | private static readonly object SyncRootAudio = new object(); 20 | private static readonly object SyncRootWords = new object(); 21 | private readonly List _recognizedWords = new List(); 22 | private readonly List _recordedAudio = new List(); 23 | private readonly int _recognizedWordsBufferLimit = 1024; 24 | private readonly int _recordedAudioBufferLimit = 1024 * 1024; 25 | private readonly bool _recognizedWordsAlwaysBuffer = true; 26 | private readonly bool _recordedAudioAlwaysBuffer = true; 27 | 28 | protected string _pluginName; 29 | public string PluginName => _pluginName; 30 | 31 | protected string _currentCulture; 32 | public string CurrentCulture => _currentCulture; 33 | 34 | protected PluginCommand[] _commands; 35 | public PluginCommand[] Commands => _commands; 36 | 37 | public delegate void ExternalAudioCommandHandler(byte[] buffer, int samplingRate, int bits, int channels); 38 | public event ExternalAudioCommandHandler ExternalAudioCommand; 39 | 40 | public delegate void ExternalTextCommandHandler(string command); 41 | public event ExternalTextCommandHandler ExternalTextCommand; 42 | 43 | public delegate void RecordedAudioCommandHandler(byte[] buffer, int samplingRate, int bits, int channels); 44 | public event RecordedAudioCommandHandler RecordedAudioCommand; 45 | 46 | public delegate void RecordedTextCommandHandler(string command); 47 | public event RecordedTextCommandHandler RecordedTextCommand; 48 | 49 | protected PluginBase(IAudioOutSingleton audioOut, string currentCulture, string pluginPath) 50 | { 51 | var file = new FileInfo(pluginPath); 52 | AudioOut = audioOut; 53 | PluginPath = file.DirectoryName; 54 | _pluginName = file.Name; 55 | PluginConfigFile = file.Name[..^file.Extension.Length] + "Settings.json"; 56 | _currentCulture = currentCulture; 57 | } 58 | 59 | public abstract void Execute(string commandName, List commandTokens); 60 | 61 | public void AddWords(string words) 62 | { 63 | var tmpWords = words; 64 | Task.Run(() => 65 | { 66 | lock (SyncRootWords) 67 | { 68 | if (RecordedTextCommand == null || _recognizedWordsAlwaysBuffer) 69 | { 70 | var newBuffer = tmpWords.Split(' '); 71 | var newBufferLength = _recognizedWords.Count + newBuffer.Length; 72 | 73 | if (newBufferLength > _recognizedWordsBufferLimit) 74 | { 75 | var removeWords = newBufferLength - _recognizedWordsBufferLimit; 76 | _recognizedWords.RemoveRange(0, removeWords); 77 | } 78 | 79 | _recognizedWords.AddRange(newBuffer); 80 | } 81 | 82 | RecordedTextCommand?.Invoke(words); 83 | } 84 | }); 85 | } 86 | 87 | protected string[] GetWords() 88 | { 89 | lock (SyncRootWords) 90 | { 91 | var result = _recognizedWords.ToArray(); 92 | _recognizedWords.Clear(); 93 | return result; 94 | } 95 | } 96 | 97 | public void AddSound(byte[] data, int samplingRate, int bits, int channels) 98 | { 99 | var tmpData = data; 100 | Task.Run(() => 101 | { 102 | lock (SyncRootAudio) 103 | { 104 | if (RecordedAudioCommand == null || _recordedAudioAlwaysBuffer) 105 | { 106 | var newBufferLength = _recordedAudio.Count + tmpData.Length; 107 | 108 | if (newBufferLength > _recordedAudioBufferLimit) 109 | { 110 | var removeBytes = newBufferLength - _recordedAudioBufferLimit; 111 | _recordedAudio.RemoveRange(0, removeBytes); 112 | } 113 | 114 | _recordedAudio.AddRange(tmpData); 115 | } 116 | 117 | RecordedAudioCommand?.Invoke(data, samplingRate, bits, channels); 118 | } 119 | }); 120 | } 121 | 122 | protected byte[] GetSound() 123 | { 124 | lock (SyncRootAudio) 125 | { 126 | var result = _recordedAudio.ToArray(); 127 | _recordedAudio.Clear(); 128 | return result; 129 | } 130 | } 131 | 132 | protected void InjectTextCommand(string command) 133 | { 134 | ExternalTextCommand?.Invoke(command); 135 | } 136 | 137 | protected void InjectAudioCommand(byte[] buffer, int samplingRate, int bits, int channels) 138 | { 139 | ExternalAudioCommand?.Invoke(buffer, samplingRate, bits, channels); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /PluginInterface/PluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace PluginInterface 8 | { 9 | public class PluginCommand 10 | { 11 | public string Name; 12 | public Token[] Tokens; 13 | 14 | public Token GetParameter(string paramName, IEnumerable tokens) 15 | { 16 | for (var i = 0; i < Tokens.Length; i++) 17 | if (Tokens[i].Value.Contains(paramName)) 18 | return tokens.ElementAt(i); 19 | 20 | return null; 21 | } 22 | 23 | public override string ToString() 24 | { 25 | var sb = new StringBuilder(); 26 | 27 | if (Tokens != null && Tokens.Length > 0) 28 | { 29 | foreach (var token in Tokens.Select(n => n.Value)) 30 | { 31 | if (sb.Length > 0) 32 | { 33 | sb.Append(" "); 34 | } 35 | 36 | if (token.Length == 1) 37 | { 38 | sb.Append($"{token[0]}"); 39 | } 40 | else 41 | { 42 | sb.Append("["); 43 | var nextElement = false; 44 | 45 | foreach (var word in token) 46 | { 47 | if (nextElement) 48 | { 49 | sb.Append(", "); 50 | } 51 | 52 | sb.Append($"{word}"); 53 | nextElement = true; 54 | } 55 | 56 | sb.Append("]"); 57 | } 58 | } 59 | } 60 | 61 | return sb.ToString().Trim(); 62 | } 63 | } 64 | 65 | public class Token 66 | { 67 | public string[] Value; 68 | public TokenType Type = TokenType.Unknown; 69 | public int SuccessRate = 100; // success ratio for fuzzy compare 70 | } 71 | 72 | public enum TokenType 73 | { 74 | Unknown, 75 | Command, 76 | Parameter 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /PluginInterface/PluginInterface.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /PluginInterface/PluginTools.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System.Text; 4 | 5 | namespace PluginInterface 6 | { 7 | public static class PluginTools 8 | { 9 | public static string FormatStringWithClassFields(string sample, object sourceClass) 10 | { 11 | var result = new StringBuilder(); 12 | var openBracketPosition = sample.IndexOf('{'); 13 | 14 | if (openBracketPosition < 0) 15 | { 16 | return sample; 17 | } 18 | 19 | if (openBracketPosition > 0) 20 | { 21 | result.Append(sample.Substring(0, openBracketPosition)); 22 | } 23 | 24 | while (openBracketPosition >= 0) 25 | { 26 | var closeBracketPosition = sample.IndexOf('}', openBracketPosition + 1); 27 | if (closeBracketPosition < 0) 28 | { 29 | result.Append(sample.Substring(openBracketPosition)); 30 | 31 | return result.ToString(); 32 | } 33 | 34 | var propertyName = sample.Substring(openBracketPosition + 1, closeBracketPosition - openBracketPosition - 1); 35 | 36 | object propertyValue = null; 37 | try 38 | { 39 | propertyValue = sourceClass.GetType()?.GetField(propertyName)?.GetValue(sourceClass); 40 | propertyValue ??= sourceClass.GetType()?.GetProperty(propertyName)?.GetValue(sourceClass); 41 | } 42 | catch 43 | { 44 | //no property or field found for the name 45 | } 46 | 47 | if (propertyValue != null) 48 | { 49 | result.Append(propertyValue.ToString()); 50 | } 51 | else 52 | { 53 | result.Append("{" + propertyName + "}"); 54 | } 55 | 56 | openBracketPosition = sample.IndexOf('{', closeBracketPosition + 1); 57 | 58 | if (openBracketPosition > 0) 59 | { 60 | result.Append(sample.Substring(closeBracketPosition + 1, openBracketPosition - closeBracketPosition - 1)); 61 | } 62 | else 63 | { 64 | result.Append(sample.Substring(closeBracketPosition + 1)); 65 | } 66 | } 67 | 68 | return result.ToString(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /PluginInterface/TextToNumberEmpty.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace PluginInterface 4 | { 5 | public class TextToNumberEmpty : ITextToNumber 6 | { 7 | public long ConvertStringToNumber(string numberString, int ratio = 100) 8 | { 9 | return 0; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /PluginInterface/TextToNumberEng.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace PluginInterface 9 | { 10 | class TextToNumberEng : ITextToNumber 11 | { 12 | private readonly Dictionary numberTable = new Dictionary 13 | { 14 | {"zero",0}, 15 | {"one",1}, 16 | {"two",2}, 17 | {"three",3}, 18 | {"four",4}, 19 | {"five",5}, 20 | {"six",6}, 21 | {"seven",7}, 22 | {"eight",8}, 23 | {"nine",9}, 24 | {"ten",10}, 25 | {"eleven",11}, 26 | {"twelve",12}, 27 | {"thirteen",13}, 28 | {"fourteen",14}, 29 | {"fifteen",15}, 30 | {"sixteen",16}, 31 | {"seventeen",17}, 32 | {"eighteen",18}, 33 | {"nineteen",19}, 34 | {"twenty",20}, 35 | {"thirty",30}, 36 | {"forty",40}, 37 | {"fifty",50}, 38 | {"sixty",60}, 39 | {"seventy",70}, 40 | {"eighty",80}, 41 | {"ninety",90}, 42 | {"hundred",100}, 43 | {"thousand",1000}, 44 | {"lakh",100000}, 45 | {"million",1000000}, 46 | {"billion",1000000000}, 47 | {"trillion",1000000000000}, 48 | {"quadrillion",1000000000000000}, 49 | {"quintillion",1000000000000000000} 50 | }; 51 | 52 | public long ConvertStringToNumber(string numberString, int ratio = 100) 53 | { 54 | var numbers = Regex.Matches(numberString, @"\w+").Cast() 55 | .Select(m => m.Value.ToLowerInvariant()) 56 | .Where(v => numberTable.ContainsKey(v)) 57 | .Select(v => numberTable[v]); 58 | 59 | long acc = 0, total = 0; 60 | 61 | foreach (var n in numbers) 62 | { 63 | if (n >= 1000) 64 | { 65 | total += acc * n; 66 | acc = 0; 67 | } 68 | else if (n >= 100) 69 | { 70 | acc *= n; 71 | } 72 | else acc += n; 73 | } 74 | 75 | return (total + acc) * (numberString.StartsWith("minus", 76 | StringComparison.InvariantCultureIgnoreCase) ? -1 : 1); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /PluginInterface/TextToNumberFactory.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace PluginInterface 4 | { 5 | public static class TextToNumberFactory 6 | { 7 | public static ITextToNumber GetTextToNumberConvertor(string culture) 8 | { 9 | if (culture.StartsWith("ru-")) 10 | return new TextToNumberRus(); 11 | else if (culture.StartsWith("en-")) 12 | return new TextToNumberEng(); 13 | else 14 | return new TextToNumberGeneric(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /PluginInterface/TextToNumberGeneric.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using FuzzySharp; 4 | 5 | using System.Collections.Generic; 6 | using System.IO; 7 | 8 | namespace PluginInterface 9 | { 10 | class TextToNumberGeneric : ITextToNumber 11 | { 12 | private const string _settingsConfigFile = "GenericNumberSettings.json"; 13 | 14 | private readonly Dictionary Signs; 15 | private readonly Dictionary Numbers; 16 | private readonly Dictionary Multipliers; 17 | 18 | public TextToNumberGeneric() 19 | { 20 | var configBuilder = new Config($"{_settingsConfigFile}"); 21 | if (!File.Exists($"{_settingsConfigFile}")) 22 | { 23 | configBuilder.SaveConfig(); 24 | } 25 | 26 | var configStorage = configBuilder.ConfigStorage; 27 | 28 | Signs = new Dictionary 29 | { 30 | {configStorage.Minus, false}, 31 | {configStorage.Plus, true} 32 | }; 33 | 34 | Numbers = new Dictionary 35 | { 36 | {configStorage.Zero, 0}, 37 | {configStorage.One, 1}, 38 | {configStorage.OneFemale, 1}, 39 | {configStorage.Two, 2}, 40 | {configStorage.TwoFemale, 2}, 41 | {configStorage.Three, 3}, 42 | {configStorage.Four, 4}, 43 | {configStorage.Five, 5}, 44 | {configStorage.Six, 6}, 45 | {configStorage.Seven, 7}, 46 | {configStorage.Eight, 8}, 47 | {configStorage.Nine, 9}, 48 | {configStorage.Ten, 10}, 49 | {configStorage.Elleven, 11}, 50 | {configStorage.Twelve, 12}, 51 | {configStorage.Thirteen, 13}, 52 | {configStorage.Fourteen, 14}, 53 | {configStorage.Fifteen, 15}, 54 | {configStorage.Sixteen, 16}, 55 | {configStorage.Seventeen, 17}, 56 | {configStorage.Eighteen, 18}, 57 | {configStorage.Nineteen, 19}, 58 | {configStorage.Twenty, 20}, 59 | {configStorage.Thirty, 30}, 60 | {configStorage.Forty, 40}, 61 | {configStorage.Fifty, 50}, 62 | {configStorage.Sixty, 60}, 63 | {configStorage.Seventy, 70}, 64 | {configStorage.Eighty, 80}, 65 | {configStorage.Ninety, 90}, 66 | {configStorage.OneHundred, 100}, 67 | {configStorage.TwoHundred, 200}, 68 | {configStorage.ThreeHundred, 300}, 69 | {configStorage.FourHundred, 400}, 70 | {configStorage.FiveHundred, 500}, 71 | {configStorage.SixHundred, 600}, 72 | {configStorage.SevenHundred, 700}, 73 | {configStorage.EightHundred, 800}, 74 | {configStorage.NineHundred, 900}, 75 | {configStorage.OneThousand, 1000}, 76 | {configStorage.OneMillion, 1000000}, 77 | {configStorage.OneBillion, 1000000000}, 78 | {configStorage.OneTrillion, 1000000000000}, 79 | {configStorage.OneQuadrillion, 1000000000000}, 80 | }; 81 | 82 | Multipliers = new Dictionary 83 | { 84 | {configStorage.OneThousand, 1000}, 85 | {configStorage.TwoThousand, 1000}, 86 | {configStorage.FiveThousand, 1000}, 87 | 88 | {configStorage.OneMillion, 1000000}, 89 | {configStorage.TwoMillion, 1000000}, 90 | {configStorage.FiveMillion, 1000000}, 91 | 92 | {configStorage.OneBillion, 1000000000}, 93 | {configStorage.TwoBillion, 1000000000}, 94 | {configStorage.FiveBillion, 1000000000}, 95 | 96 | {configStorage.OneTrillion, 1000000000000}, 97 | {configStorage.TwoTrillion, 1000000000000}, 98 | {configStorage.FiveTrillion, 1000000000000}, 99 | 100 | {configStorage.OneQuadrillion, 1000000000000000}, 101 | {configStorage.TwoQuadrillion, 1000000000000000}, 102 | {configStorage.FiveQuadrillion, 1000000000000000} 103 | }; 104 | } 105 | 106 | public long ConvertStringToNumber(string numberString, int ratio = 100) 107 | { 108 | long result = 0; 109 | var positive = true; 110 | var i = 0; 111 | var tokens = numberString.ToLower().Split(' '); 112 | 113 | if (TryGetValueFuzz(Signs, tokens[0], ratio, out var p)) 114 | { 115 | positive = p; 116 | i++; 117 | } 118 | 119 | for (; i < tokens.Length; i++) 120 | if (TryGetValueFuzz(Numbers, tokens[i], ratio, out var number)) 121 | { 122 | if (i + 1 < tokens.Length && 123 | TryGetValueFuzz(Multipliers, tokens[i + 1], ratio, out var multiplier)) 124 | { 125 | number *= multiplier; 126 | i++; 127 | } 128 | 129 | result += number; 130 | } 131 | else 132 | { 133 | i = tokens.Length; 134 | } 135 | 136 | if (!positive) 137 | result *= -1; 138 | 139 | return result; 140 | } 141 | 142 | private bool TryGetValueFuzz(Dictionary dict, string sample, int ratio, out T value) 143 | { 144 | value = default; 145 | 146 | foreach (var (key1, value1) in dict) 147 | { 148 | if (Fuzz.WeightedRatio(key1, sample) > ratio) 149 | { 150 | value = value1; 151 | return true; 152 | } 153 | } 154 | 155 | return false; 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /PluginInterface/TextToNumberRus.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using FuzzySharp; 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace PluginInterface 8 | { 9 | public class TextToNumberRus : ITextToNumber 10 | { 11 | private readonly Dictionary Signs = new Dictionary 12 | { 13 | {"минус", false}, 14 | {"плюс", true} 15 | }; 16 | 17 | private readonly Dictionary Numbers = new Dictionary 18 | { 19 | {"ноль", 0}, 20 | {"один", 1}, 21 | {"одна", 1}, 22 | {"одну", 1}, 23 | {"два", 2}, 24 | {"две", 2}, 25 | {"три", 3}, 26 | {"четыре", 4}, 27 | {"пять", 5}, 28 | {"шесть", 6}, 29 | {"семь", 7}, 30 | {"восемь", 8}, 31 | {"девять", 9}, 32 | {"десять", 10}, 33 | {"одиннадцать", 11}, 34 | {"двенадцать", 12}, 35 | {"тринадцать", 13}, 36 | {"четырнадцать", 14}, 37 | {"пятнадцать", 15}, 38 | {"шестнадцать", 16}, 39 | {"семнадцать", 17}, 40 | {"восемнадцать", 18}, 41 | {"девятнадцать", 19}, 42 | {"двадцать", 20}, 43 | {"тридцать", 30}, 44 | {"сорок", 40}, 45 | {"пятьдесят", 50}, 46 | {"шестьдесят", 60}, 47 | {"семьдесят", 70}, 48 | {"восемьдесят", 80}, 49 | {"девяносто", 90}, 50 | {"сто", 100}, 51 | {"двести", 200}, 52 | {"триста", 300}, 53 | {"четыреста", 400}, 54 | {"пятьсот", 500}, 55 | {"шестьсот", 600}, 56 | {"семьсот", 700}, 57 | {"восемьсот", 800}, 58 | {"девятьсот", 900}, 59 | {"тысяча", 1000}, 60 | {"миллион", 1000000}, 61 | {"миллиард", 1000000000}, 62 | {"триллион", 1000000000000}, 63 | {"квадриллион", 1000000000000}, 64 | }; 65 | 66 | private readonly Dictionary Multipliers = new Dictionary 67 | { 68 | {"тысяча", 1000}, 69 | {"тысячи", 1000}, 70 | {"тысяч", 1000}, 71 | 72 | {"миллион", 1000000}, 73 | {"миллиона", 1000000}, 74 | {"миллионов", 1000000}, 75 | 76 | {"миллиард", 1000000000}, 77 | {"миллиарда", 1000000000}, 78 | {"миллиардов", 1000000000}, 79 | 80 | {"триллион", 1000000000000}, 81 | {"триллиона", 1000000000000}, 82 | {"триллионов", 1000000000000}, 83 | 84 | {"квадриллион", 1000000000000000}, 85 | {"квадриллиона", 1000000000000000}, 86 | {"квадриллионов", 1000000000000000} 87 | }; 88 | 89 | public long ConvertStringToNumber(string numberString, int ratio = 100) 90 | { 91 | long result = 0; 92 | var positive = true; 93 | var i = 0; 94 | var tokens = numberString.ToLower().Split(' '); 95 | 96 | if (TryGetValueFuzz(Signs, tokens[0], ratio, out var p)) 97 | { 98 | positive = p; 99 | i++; 100 | } 101 | 102 | for (; i < tokens.Length; i++) 103 | if (TryGetValueFuzz(Numbers, tokens[i], ratio, out var number)) 104 | { 105 | if (i + 1 < tokens.Length && 106 | TryGetValueFuzz(Multipliers, tokens[i + 1], ratio, out var multiplier)) 107 | { 108 | number *= multiplier; 109 | i++; 110 | } 111 | 112 | result += number; 113 | } 114 | else 115 | { 116 | i = tokens.Length; 117 | } 118 | 119 | if (!positive) 120 | result *= -1; 121 | 122 | return result; 123 | } 124 | 125 | private bool TryGetValueFuzz(Dictionary dict, string sample, int ratio, out TK value) 126 | { 127 | value = default; 128 | 129 | foreach (var (key1, value1) in dict) 130 | { 131 | if (Fuzz.WeightedRatio(key1, sample) > ratio) 132 | { 133 | value = value1; 134 | return true; 135 | } 136 | } 137 | 138 | return false; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /RunProgramPlugin/RunProgramPlugin.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | 9 | using PluginInterface; 10 | 11 | namespace RunProgramPlugin 12 | { 13 | public class RunProgramPlugin : PluginBase 14 | { 15 | private readonly RunProgramPluginCommand[] RunProgramCommands; 16 | private readonly Dictionary _processes = new Dictionary(); 17 | private readonly string _canNotClose; 18 | private readonly string _notRunning; 19 | private readonly string _canNotRun; 20 | private readonly string _alreadyRunning; 21 | 22 | public RunProgramPlugin(IAudioOutSingleton audioOut, string currentCulture, string pluginPath) : base(audioOut, currentCulture, pluginPath) 23 | { 24 | var configBuilder = new Config($"{PluginPath}\\{PluginConfigFile}"); 25 | 26 | if (!File.Exists($"{PluginPath}\\{PluginConfigFile}")) 27 | { 28 | configBuilder.SaveConfig(); 29 | } 30 | 31 | RunProgramCommands = configBuilder.ConfigStorage.Commands; 32 | 33 | if (RunProgramCommands is PluginCommand[] newCmds) 34 | { 35 | _commands = newCmds; 36 | } 37 | 38 | _canNotClose = configBuilder.ConfigStorage.CanNotClose; 39 | _notRunning = configBuilder.ConfigStorage.NotRunning; 40 | _canNotRun = configBuilder.ConfigStorage.CanNotRun; 41 | _alreadyRunning = configBuilder.ConfigStorage.AlreadyRunning; 42 | } 43 | 44 | public override void Execute(string commandName, List commandTokens) 45 | { 46 | var command = RunProgramCommands.FirstOrDefault(n => n.Name == commandName); 47 | 48 | if (command == null) 49 | { 50 | return; 51 | } 52 | 53 | var response = string.Empty; 54 | var processId = command.CommandLine; 55 | 56 | if (command.IsStopCommand) 57 | { 58 | if (_processes.TryGetValue(processId, out var proc)) 59 | { 60 | if (proc != null) 61 | { 62 | try 63 | { 64 | proc.Kill(); 65 | response = command.Response; 66 | } 67 | catch 68 | { 69 | response = _canNotClose; 70 | } 71 | finally 72 | { 73 | _processes.Remove(processId); 74 | } 75 | } 76 | else 77 | { 78 | response = _notRunning; 79 | } 80 | } 81 | else 82 | { 83 | response = _notRunning; 84 | } 85 | } 86 | else 87 | { 88 | if (!_processes.TryGetValue(processId, out _)) 89 | { 90 | var process = RunCommand(command.CommandLine); 91 | 92 | if (process != null) 93 | { 94 | if (!command.AllowMultiple) 95 | { 96 | _processes.Add(processId, process); 97 | } 98 | 99 | response = command.Response; 100 | } 101 | else 102 | { 103 | response = _canNotRun; 104 | } 105 | } 106 | else 107 | { 108 | response = _alreadyRunning; 109 | } 110 | } 111 | 112 | AudioOut.Speak(response); 113 | } 114 | 115 | private Process RunCommand(string command) 116 | { 117 | Process proc; 118 | try 119 | { 120 | proc = Process.Start(command); 121 | } 122 | catch 123 | { 124 | try 125 | { 126 | // hack because of this: https://github.com/dotnet/corefx/issues/10361 127 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 128 | { 129 | command = command.Replace("&", "^&"); 130 | proc = Process.Start(new ProcessStartInfo(command) { UseShellExecute = true }); 131 | 132 | /* 133 | Process myProcess = new Process(); 134 | try 135 | { 136 | // true is the default, but it is important not to set it to false 137 | myProcess.StartInfo.UseShellExecute = true; 138 | myProcess.StartInfo.FileName = "http://some.domain.tld/bla"; 139 | myProcess.Start(); 140 | } 141 | catch (Exception e) 142 | { 143 | Console.WriteLine(e.Message); 144 | } 145 | */ 146 | //Windows.System.Launcher.LaunchUriAsync(new Uri("http://google.com")); 147 | //Process.Start("explorer.exe", $"\"{uri}\""); 148 | 149 | } 150 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 151 | { 152 | proc = Process.Start("xdg-open", command); 153 | } 154 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 155 | { 156 | proc = Process.Start("open", command); 157 | } 158 | else 159 | { 160 | throw; 161 | } 162 | } 163 | catch 164 | { 165 | return null; 166 | } 167 | } 168 | 169 | return proc; 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /RunProgramPlugin/RunProgramPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RunProgramPlugin/RunProgramPluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace RunProgramPlugin 6 | { 7 | public class RunProgramPluginCommand : PluginCommand 8 | { 9 | public string Response = ""; 10 | public string CommandLine = ""; 11 | public bool IsStopCommand = false; 12 | public bool AllowMultiple = false; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /RunProgramPlugin/RunProgramPluginSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace RunProgramPlugin 6 | { 7 | public class RunProgramPluginSettings 8 | { 9 | //[JsonProperty(Required = Required.Always)] 10 | public string CanNotClose = "не удалось закрыть"; 11 | public string NotRunning = "не запущен"; 12 | public string CanNotRun = "не удалось запустить"; 13 | public string AlreadyRunning = "уже запущен"; 14 | 15 | public RunProgramPluginCommand[] Commands = 16 | { 17 | new RunProgramPluginCommand 18 | { 19 | Name = "Run VSCode", 20 | Tokens = new[] 21 | { 22 | new Token 23 | { 24 | SuccessRate = 90, 25 | Type = TokenType.Command, 26 | Value = new[] {"запусти", "открой"} 27 | }, 28 | new Token 29 | { 30 | SuccessRate = 90, 31 | Type = TokenType.Command, 32 | Value = new[] {"вес"} 33 | }, 34 | new Token 35 | { 36 | SuccessRate = 90, 37 | Type = TokenType.Command, 38 | Value = new[] {"код"} 39 | } 40 | }, 41 | Response = "Вижуал студио код запущена", 42 | CommandLine = "code", 43 | IsStopCommand = false, 44 | AllowMultiple = true 45 | }, 46 | new RunProgramPluginCommand 47 | { 48 | Name = "Stop VSCode", 49 | Tokens = new[] 50 | { 51 | new Token 52 | { 53 | SuccessRate = 90, 54 | Type = TokenType.Command, 55 | Value = new[] {"останови", "закрой"} 56 | }, 57 | new Token 58 | { 59 | SuccessRate = 90, 60 | Type = TokenType.Command, 61 | Value = new[] {"вес"} 62 | }, 63 | new Token 64 | { 65 | SuccessRate = 90, 66 | Type = TokenType.Command, 67 | Value = new[] {"код"} 68 | } 69 | }, 70 | Response = "Вижуал студио код закрыт", 71 | CommandLine = "code", 72 | IsStopCommand = true, 73 | AllowMultiple = true 74 | }, 75 | new RunProgramPluginCommand 76 | { 77 | Name = "Run notepad++", 78 | Tokens = new[] 79 | { 80 | new Token 81 | { 82 | SuccessRate = 90, 83 | Type = TokenType.Command, 84 | Value = new[] {"запусти", "открой" } 85 | }, 86 | new Token 87 | { 88 | SuccessRate = 90, 89 | Type = TokenType.Command, 90 | Value = new[] {"блокнот"} 91 | } 92 | }, 93 | Response = "Блокнот запущен", 94 | CommandLine = "notepad++.exe", 95 | IsStopCommand = false, 96 | AllowMultiple = false 97 | }, 98 | new RunProgramPluginCommand 99 | { 100 | Name = "Stop notepad++", 101 | Tokens = new[] 102 | { 103 | new Token 104 | { 105 | SuccessRate = 90, 106 | Type = TokenType.Command, 107 | Value = new[] {"останови", "закрой"} 108 | }, 109 | new Token 110 | { 111 | SuccessRate = 90, 112 | Type = TokenType.Command, 113 | Value = new[] { "блокнот" } 114 | } 115 | }, 116 | Response = "Блокнот закрыт", 117 | CommandLine = "notepad++.exe", 118 | IsStopCommand = true, 119 | AllowMultiple = false 120 | } 121 | }; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /RunProgramPlugin/RunProgramPluginSettings_eng.json: -------------------------------------------------------------------------------- 1 | { 2 | "CanNotClose": "can not close", 3 | "NotRunning": "not running", 4 | "CanNotRun": "can not run", 5 | "AlreadyRunning": "already running", 6 | "Commands": [ 7 | { 8 | "Response": "Visual Studio Code is running", 9 | "CommandLine": "code", 10 | "IsStopCommand": false, 11 | "AllowMultiple": true, 12 | "Name": "Run VSCode", 13 | "Tokens": [ 14 | { 15 | "Value": [ 16 | "run", 17 | "open" 18 | ], 19 | "Type": "Command", 20 | "SuccessRate": 90 21 | }, 22 | { 23 | "Value": [ 24 | "vs" 25 | ], 26 | "Type": "Command", 27 | "SuccessRate": 90 28 | }, 29 | { 30 | "Value": [ 31 | "code" 32 | ], 33 | "Type": "Command", 34 | "SuccessRate": 90 35 | } 36 | ] 37 | }, 38 | { 39 | "Response": "Visual Studio Code closed", 40 | "CommandLine": "code", 41 | "IsStopCommand": true, 42 | "AllowMultiple": true, 43 | "Name": "Stop VSCode", 44 | "Tokens": [ 45 | { 46 | "Value": [ 47 | "stop", 48 | "close" 49 | ], 50 | "Type": "Command", 51 | "SuccessRate": 90 52 | }, 53 | { 54 | "Value": [ 55 | "vs" 56 | ], 57 | "Type": "Command", 58 | "SuccessRate": 90 59 | }, 60 | { 61 | "Value": [ 62 | "code" 63 | ], 64 | "Type": "Command", 65 | "SuccessRate": 90 66 | } 67 | ] 68 | }, 69 | { 70 | "Response": "notepad is running", 71 | "CommandLine": "notepad++.exe", 72 | "IsStopCommand": false, 73 | "AllowMultiple": false, 74 | "Name": "Run notepad++", 75 | "Tokens": [ 76 | { 77 | "Value": [ 78 | "run", 79 | "open" 80 | ], 81 | "Type": "Command", 82 | "SuccessRate": 90 83 | }, 84 | { 85 | "Value": [ 86 | "notepad" 87 | ], 88 | "Type": "Command", 89 | "SuccessRate": 90 90 | } 91 | ] 92 | }, 93 | { 94 | "Response": "notepad is closed", 95 | "CommandLine": "notepad++.exe", 96 | "IsStopCommand": true, 97 | "AllowMultiple": false, 98 | "Name": "Stop notepad++", 99 | "Tokens": [ 100 | { 101 | "Value": [ 102 | "stop", 103 | "close" 104 | ], 105 | "Type": "Command", 106 | "SuccessRate": 90 107 | }, 108 | { 109 | "Value": [ 110 | "notepad" 111 | ], 112 | "Type": "Command", 113 | "SuccessRate": 90 114 | } 115 | ] 116 | } 117 | ] 118 | } -------------------------------------------------------------------------------- /TimerPlugin/TimerPlugin.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Timers; 8 | 9 | using PluginInterface; 10 | 11 | namespace TimerPlugin 12 | { 13 | public class TimerPlugin : PluginBase 14 | { 15 | private readonly TimerPluginCommand[] TimerCommands; 16 | private readonly string _alarmSound; 17 | private readonly string _incorrectTime; 18 | private readonly string _timerNotFound; 19 | private readonly List<(string, Timer)> _timers = new List<(string, Timer)>(); 20 | 21 | public TimerPlugin(IAudioOutSingleton audioOut, string currentCulture, string pluginPath) : base(audioOut, currentCulture, pluginPath) 22 | { 23 | var configBuilder = new Config($"{PluginPath}\\{PluginConfigFile}"); 24 | if (!File.Exists($"{PluginPath}\\{PluginConfigFile}")) 25 | { 26 | configBuilder.SaveConfig(); 27 | } 28 | 29 | TimerCommands = configBuilder.ConfigStorage.Commands; 30 | 31 | if (TimerCommands is PluginCommand[] newCmds) 32 | { 33 | _commands = newCmds; 34 | } 35 | 36 | _alarmSound = configBuilder.ConfigStorage.AlarmSound; 37 | _incorrectTime = configBuilder.ConfigStorage.IncorrectTime; 38 | _timerNotFound = configBuilder.ConfigStorage.TimerNotFound; 39 | } 40 | 41 | public override void Execute(string commandName, List commandTokens) 42 | { 43 | var command = TimerCommands.FirstOrDefault(n => n.Name == commandName); 44 | 45 | 46 | if (command == null) 47 | { 48 | return; 49 | } 50 | 51 | var minToken = command.GetParameter("%minutes%", commandTokens); 52 | var secToken = command.GetParameter("%seconds%", commandTokens); 53 | var minCount = 0; 54 | 55 | var stringToNumberConvertor = TextToNumberFactory.GetTextToNumberConvertor(CurrentCulture); 56 | 57 | if (minToken != null) 58 | { 59 | minCount = (int)stringToNumberConvertor.ConvertStringToNumber(minToken.Value[0], minToken.SuccessRate); 60 | } 61 | 62 | var secCount = 0; 63 | if (secToken != null) 64 | { 65 | secCount = (int)stringToNumberConvertor.ConvertStringToNumber(secToken.Value[0], secToken.SuccessRate); 66 | } 67 | 68 | var response = string.Empty; 69 | if (!command.isStopCommand && minCount + secCount == 0) 70 | { 71 | response = _incorrectTime; 72 | } 73 | else 74 | { 75 | var delay = $"{minCount}+{secCount}"; 76 | 77 | var NumberToStringConvertor = NumberToTextFactory.GetNumberToTextConvertor(CurrentCulture); 78 | 79 | 80 | if (command.isStopCommand) 81 | { 82 | var timer = _timers.FirstOrDefault(n => n.Item2.Enabled && n.Item1 == delay); 83 | 84 | if (timer.Item2 != null) 85 | { 86 | timer.Item2.Stop(); 87 | _timers.Remove(timer); 88 | // string.Empty is used to avoid using {0} int templates 89 | response = string.Format(command.Response, string.Empty, 90 | NumberToStringConvertor.ConvertNumberToString(minCount), 91 | NumberToStringConvertor.ConvertNumberToString(secCount)); 92 | } 93 | else 94 | { 95 | response = _timerNotFound; 96 | } 97 | } 98 | else 99 | { 100 | var t = new Timer 101 | { 102 | AutoReset = false, 103 | Interval = new TimeSpan(0, minCount, secCount).TotalMilliseconds 104 | }; 105 | 106 | t.Elapsed += (obj, args) => 107 | { 108 | var timer = _timers.FirstOrDefault(n => n.Item2 == obj); 109 | 110 | if (timer.Item2 != null) 111 | { 112 | _timers.Remove(timer); 113 | } 114 | 115 | AudioOut.PlayFile($"{PluginPath}\\{_alarmSound}"); 116 | }; 117 | 118 | _timers.Add((delay, t)); 119 | t.Start(); 120 | 121 | // string.Empty is used to avoid using {0} int templates 122 | response = string.Format(command.Response, string.Empty, 123 | NumberToStringConvertor.ConvertNumberToString(minCount), 124 | NumberToStringConvertor.ConvertNumberToString(secCount)); 125 | } 126 | } 127 | 128 | AudioOut.Speak(response); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /TimerPlugin/TimerPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | PreserveNewest 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /TimerPlugin/TimerPluginCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using PluginInterface; 4 | 5 | namespace TimerPlugin 6 | { 7 | public class TimerPluginCommand : PluginCommand 8 | { 9 | public string Response = ""; 10 | public bool isStopCommand = false; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TimerPlugin/TimerPluginSettings_eng.json: -------------------------------------------------------------------------------- 1 | { 2 | "AlarmSound": "timer.wav", 3 | "IncorrectTime": "incorrect time", 4 | "TimerNotFound": "timer not found", 5 | "Commands": [ 6 | { 7 | "Response": "Timer set to {1} minutes", 8 | "isStopCommand": false, 9 | "Name": "Run timer minutes", 10 | "Tokens": [ 11 | { 12 | "Value": [ 13 | "set", 14 | "start", 15 | "run" 16 | ], 17 | "Type": "Command", 18 | "SuccessRate": 90 19 | }, 20 | { 21 | "Value": [ 22 | "timer" 23 | ], 24 | "Type": "Command", 25 | "SuccessRate": 90 26 | }, 27 | { 28 | "Value": [ 29 | "to" 30 | ], 31 | "Type": "Command", 32 | "SuccessRate": 90 33 | }, 34 | { 35 | "Value": [ 36 | "%minutes%" 37 | ], 38 | "Type": "Parameter", 39 | "SuccessRate": 90 40 | }, 41 | { 42 | "Value": [ 43 | "minute", 44 | "minutes" 45 | ], 46 | "Type": "Command", 47 | "SuccessRate": 90 48 | } 49 | ] 50 | }, 51 | { 52 | "Response": "{1} minutes timer stopped", 53 | "isStopCommand": true, 54 | "Name": "Stop timer minutes", 55 | "Tokens": [ 56 | { 57 | "Value": [ 58 | "stop", 59 | "delete" 60 | ], 61 | "Type": "Command", 62 | "SuccessRate": 90 63 | }, 64 | { 65 | "Value": [ 66 | "%minutes%" 67 | ], 68 | "Type": "Parameter", 69 | "SuccessRate": 90 70 | }, 71 | { 72 | "Value": [ 73 | "minute", 74 | "minutes" 75 | ], 76 | "Type": "Command", 77 | "SuccessRate": 90 78 | }, 79 | { 80 | "Value": [ 81 | "timer" 82 | ], 83 | "Type": "Command", 84 | "SuccessRate": 90 85 | }, 86 | ] 87 | }, 88 | { 89 | "Response": "Timer set to {2} seconds", 90 | "isStopCommand": false, 91 | "Name": "Run timer seconds", 92 | "Tokens": [ 93 | { 94 | "Value": [ 95 | "set", 96 | "start", 97 | "run" 98 | ], 99 | "Type": "Command", 100 | "SuccessRate": 90 101 | }, 102 | { 103 | "Value": [ 104 | "timer" 105 | ], 106 | "Type": "Command", 107 | "SuccessRate": 90 108 | }, 109 | { 110 | "Value": [ 111 | "to" 112 | ], 113 | "Type": "Command", 114 | "SuccessRate": 90 115 | }, 116 | { 117 | "Value": [ 118 | "%seconds%" 119 | ], 120 | "Type": "Parameter", 121 | "SuccessRate": 90 122 | }, 123 | { 124 | "Value": [ 125 | "second", 126 | "seconds" 127 | ], 128 | "Type": "Command", 129 | "SuccessRate": 90 130 | } 131 | ] 132 | }, 133 | { 134 | "Response": "Таймер на {2} секунд остановлен", 135 | "isStopCommand": true, 136 | "Name": "Stop timer seconds", 137 | "Tokens": [ 138 | { 139 | "Value": [ 140 | "stop", 141 | "delete" 142 | ], 143 | "Type": "Command", 144 | "SuccessRate": 90 145 | }, 146 | { 147 | "Value": [ 148 | "%seconds%" 149 | ], 150 | "Type": "Parameter", 151 | "SuccessRate": 90 152 | }, 153 | { 154 | "Value": [ 155 | "second", 156 | "seconds" 157 | ], 158 | "Type": "Command", 159 | "SuccessRate": 90 160 | }, 161 | { 162 | "Value": [ 163 | "timer" 164 | ], 165 | "Type": "Command", 166 | "SuccessRate": 90 167 | } 168 | ] 169 | }, 170 | { 171 | "Response": "Timer set to {1} minutes {2} seconds", 172 | "isStopCommand": false, 173 | "Name": "Run timer minutes, seconds", 174 | "Tokens": [ 175 | { 176 | "Value": [ 177 | "set", 178 | "start", 179 | "run" 180 | ], 181 | "Type": "Command", 182 | "SuccessRate": 90 183 | }, 184 | { 185 | "Value": [ 186 | "timer" 187 | ], 188 | "Type": "Command", 189 | "SuccessRate": 90 190 | }, 191 | { 192 | "Value": [ 193 | "to" 194 | ], 195 | "Type": "Command", 196 | "SuccessRate": 90 197 | }, 198 | { 199 | "Value": [ 200 | "%minutes%" 201 | ], 202 | "Type": "Parameter", 203 | "SuccessRate": 90 204 | }, 205 | { 206 | "Value": [ 207 | "minute", 208 | "minutes" 209 | ], 210 | "Type": "Command", 211 | "SuccessRate": 90 212 | }, 213 | { 214 | "Value": [ 215 | "%seconds%" 216 | ], 217 | "Type": "Parameter", 218 | "SuccessRate": 90 219 | }, 220 | { 221 | "Value": [ 222 | "second", 223 | "seconds" 224 | ], 225 | "Type": "Command", 226 | "SuccessRate": 90 227 | } 228 | ] 229 | }, 230 | { 231 | "Response": "{1} minutes {2} seconds timer stopped", 232 | "isStopCommand": true, 233 | "Name": "Stop timer minutes, seconds", 234 | "Tokens": [ 235 | { 236 | "Value": [ 237 | "stop", 238 | "delete" 239 | ], 240 | "Type": "Command", 241 | "SuccessRate": 90 242 | }, 243 | { 244 | "Value": [ 245 | "%minutes%" 246 | ], 247 | "Type": "Parameter", 248 | "SuccessRate": 90 249 | }, 250 | { 251 | "Value": [ 252 | "minute", 253 | "minutes" 254 | ], 255 | "Type": "Command", 256 | "SuccessRate": 90 257 | }, 258 | { 259 | "Value": [ 260 | "%seconds%" 261 | ], 262 | "Type": "Parameter", 263 | "SuccessRate": 90 264 | }, 265 | { 266 | "Value": [ 267 | "second", 268 | "seconds" 269 | ], 270 | "Type": "Command", 271 | "SuccessRate": 90 272 | }, 273 | { 274 | "Value": [ 275 | "timer" 276 | ], 277 | "Type": "Command", 278 | "SuccessRate": 90 279 | } 280 | ] 281 | }, 282 | { 283 | "Response": "Timers stopped", 284 | "isStopCommand": true, 285 | "Name": "Stop timers", 286 | "Tokens": [ 287 | { 288 | "Value": [ 289 | "stop", 290 | "delete" 291 | ], 292 | "Type": "Command", 293 | "SuccessRate": 90 294 | }, 295 | { 296 | "Value": [ 297 | "timers" 298 | ], 299 | "Type": "Command", 300 | "SuccessRate": 90 301 | } 302 | ] 303 | }, 304 | { 305 | "Response": "all timers stopped", 306 | "isStopCommand": true, 307 | "Name": "Stop all timers", 308 | "Tokens": [ 309 | { 310 | "Value": [ 311 | "stop", 312 | "delete" 313 | ], 314 | "Type": "Command", 315 | "SuccessRate": 90 316 | }, 317 | { 318 | "Value": [ 319 | "all" 320 | ], 321 | "Type": "Command", 322 | "SuccessRate": 90 323 | }, 324 | { 325 | "Value": [ 326 | "timers" 327 | ], 328 | "Type": "Command", 329 | "SuccessRate": 90 330 | } 331 | ] 332 | } 333 | ] 334 | } -------------------------------------------------------------------------------- /TimerPlugin/timer.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jekyll2014/VoiceAssistant/bea10e8c38475eb44d8f67e4f7300f4625a33276/TimerPlugin/timer.wav -------------------------------------------------------------------------------- /VoiceAssistant.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32616.157 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VoiceAssistant", "VoiceAssistant\VoiceAssistant.csproj", "{298A14AF-23B7-4520-AC88-F34499FA784A}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {D8E672A3-242D-436B-9C6C-F92B8F864F4C} = {D8E672A3-242D-436B-9C6C-F92B8F864F4C} 9 | EndProjectSection 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimerPlugin", "TimerPlugin\TimerPlugin.csproj", "{FBC63F53-EE4B-4E6E-81D6-0AF31019F517}" 12 | ProjectSection(ProjectDependencies) = postProject 13 | {D8E672A3-242D-436B-9C6C-F92B8F864F4C} = {D8E672A3-242D-436B-9C6C-F92B8F864F4C} 14 | EndProjectSection 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloPlugin", "HelloPlugin\HelloPlugin.csproj", "{D4F6D69B-7655-44A6-8E82-38698B3C19B6}" 17 | ProjectSection(ProjectDependencies) = postProject 18 | {D8E672A3-242D-436B-9C6C-F92B8F864F4C} = {D8E672A3-242D-436B-9C6C-F92B8F864F4C} 19 | EndProjectSection 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PluginInterface", "PluginInterface\PluginInterface.csproj", "{D8E672A3-242D-436B-9C6C-F92B8F864F4C}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BrowserPlugin", "BrowserPlugin\BrowserPlugin.csproj", "{B8B95E2A-AB96-42B6-B9CD-3C402F5FDD6A}" 24 | EndProject 25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RunProgramPlugin", "RunProgramPlugin\RunProgramPlugin.csproj", "{167C8B0A-E613-4B9B-AE8A-C09C8B167166}" 26 | EndProject 27 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CurrencyRatePlugin", "CurrencyRatePlugin\CurrencyRatePlugin.csproj", "{CA81441C-2635-4847-A86D-0F8C57F7243D}" 28 | EndProject 29 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppControlPlugin", "AppControlPlugin\AppControlPlugin.csproj", "{EC559348-A4A1-4A34-BC79-9D42EC85956A}" 30 | EndProject 31 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenWeatherPlugin", "OpenWeatherPlugin\OpenWeatherPlugin.csproj", "{66B0ADBA-EE01-4674-9CA1-018ABA79058C}" 32 | EndProject 33 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GoogleCalendarPlugin", "GoogleCalendarPlugin\GoogleCalendarPlugin.csproj", "{53B5123F-6468-4F45-A8B5-209384312E42}" 34 | EndProject 35 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTOutPlugin", "MQTTOutPlugin\MQTTOutPlugin.csproj", "{BDF6B74D-D98F-4B78-95BB-39C63BE8BB77}" 36 | ProjectSection(ProjectDependencies) = postProject 37 | {D8E672A3-242D-436B-9C6C-F92B8F864F4C} = {D8E672A3-242D-436B-9C6C-F92B8F864F4C} 38 | EndProjectSection 39 | EndProject 40 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTInPlugin", "MQTTInPlugin\MQTTInPlugin.csproj", "{107E156F-D939-43AD-BE00-70EBBB3333DA}" 41 | EndProject 42 | Global 43 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 44 | Debug|Any CPU = Debug|Any CPU 45 | Release|Any CPU = Release|Any CPU 46 | EndGlobalSection 47 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 48 | {298A14AF-23B7-4520-AC88-F34499FA784A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {298A14AF-23B7-4520-AC88-F34499FA784A}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {298A14AF-23B7-4520-AC88-F34499FA784A}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {298A14AF-23B7-4520-AC88-F34499FA784A}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {FBC63F53-EE4B-4E6E-81D6-0AF31019F517}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {FBC63F53-EE4B-4E6E-81D6-0AF31019F517}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {FBC63F53-EE4B-4E6E-81D6-0AF31019F517}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {FBC63F53-EE4B-4E6E-81D6-0AF31019F517}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {D4F6D69B-7655-44A6-8E82-38698B3C19B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {D4F6D69B-7655-44A6-8E82-38698B3C19B6}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {D4F6D69B-7655-44A6-8E82-38698B3C19B6}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {D4F6D69B-7655-44A6-8E82-38698B3C19B6}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {D8E672A3-242D-436B-9C6C-F92B8F864F4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {D8E672A3-242D-436B-9C6C-F92B8F864F4C}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {D8E672A3-242D-436B-9C6C-F92B8F864F4C}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {D8E672A3-242D-436B-9C6C-F92B8F864F4C}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {B8B95E2A-AB96-42B6-B9CD-3C402F5FDD6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {B8B95E2A-AB96-42B6-B9CD-3C402F5FDD6A}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {B8B95E2A-AB96-42B6-B9CD-3C402F5FDD6A}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {B8B95E2A-AB96-42B6-B9CD-3C402F5FDD6A}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {167C8B0A-E613-4B9B-AE8A-C09C8B167166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {167C8B0A-E613-4B9B-AE8A-C09C8B167166}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {167C8B0A-E613-4B9B-AE8A-C09C8B167166}.Release|Any CPU.ActiveCfg = Release|Any CPU 71 | {167C8B0A-E613-4B9B-AE8A-C09C8B167166}.Release|Any CPU.Build.0 = Release|Any CPU 72 | {CA81441C-2635-4847-A86D-0F8C57F7243D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 73 | {CA81441C-2635-4847-A86D-0F8C57F7243D}.Debug|Any CPU.Build.0 = Debug|Any CPU 74 | {CA81441C-2635-4847-A86D-0F8C57F7243D}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {CA81441C-2635-4847-A86D-0F8C57F7243D}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {EC559348-A4A1-4A34-BC79-9D42EC85956A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 77 | {EC559348-A4A1-4A34-BC79-9D42EC85956A}.Debug|Any CPU.Build.0 = Debug|Any CPU 78 | {EC559348-A4A1-4A34-BC79-9D42EC85956A}.Release|Any CPU.ActiveCfg = Release|Any CPU 79 | {EC559348-A4A1-4A34-BC79-9D42EC85956A}.Release|Any CPU.Build.0 = Release|Any CPU 80 | {66B0ADBA-EE01-4674-9CA1-018ABA79058C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 81 | {66B0ADBA-EE01-4674-9CA1-018ABA79058C}.Debug|Any CPU.Build.0 = Debug|Any CPU 82 | {66B0ADBA-EE01-4674-9CA1-018ABA79058C}.Release|Any CPU.ActiveCfg = Release|Any CPU 83 | {66B0ADBA-EE01-4674-9CA1-018ABA79058C}.Release|Any CPU.Build.0 = Release|Any CPU 84 | {53B5123F-6468-4F45-A8B5-209384312E42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 85 | {53B5123F-6468-4F45-A8B5-209384312E42}.Debug|Any CPU.Build.0 = Debug|Any CPU 86 | {53B5123F-6468-4F45-A8B5-209384312E42}.Release|Any CPU.ActiveCfg = Release|Any CPU 87 | {53B5123F-6468-4F45-A8B5-209384312E42}.Release|Any CPU.Build.0 = Release|Any CPU 88 | {BDF6B74D-D98F-4B78-95BB-39C63BE8BB77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 89 | {BDF6B74D-D98F-4B78-95BB-39C63BE8BB77}.Debug|Any CPU.Build.0 = Debug|Any CPU 90 | {BDF6B74D-D98F-4B78-95BB-39C63BE8BB77}.Release|Any CPU.ActiveCfg = Release|Any CPU 91 | {BDF6B74D-D98F-4B78-95BB-39C63BE8BB77}.Release|Any CPU.Build.0 = Release|Any CPU 92 | {107E156F-D939-43AD-BE00-70EBBB3333DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 93 | {107E156F-D939-43AD-BE00-70EBBB3333DA}.Debug|Any CPU.Build.0 = Debug|Any CPU 94 | {107E156F-D939-43AD-BE00-70EBBB3333DA}.Release|Any CPU.ActiveCfg = Release|Any CPU 95 | {107E156F-D939-43AD-BE00-70EBBB3333DA}.Release|Any CPU.Build.0 = Release|Any CPU 96 | EndGlobalSection 97 | GlobalSection(SolutionProperties) = preSolution 98 | HideSolutionNode = FALSE 99 | EndGlobalSection 100 | GlobalSection(ExtensibilityGlobals) = postSolution 101 | SolutionGuid = {E98BAD0E-2957-4758-9F3E-6094D374516D} 102 | EndGlobalSection 103 | EndGlobal 104 | -------------------------------------------------------------------------------- /VoiceAssistant/AssistantStart.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jekyll2014/VoiceAssistant/bea10e8c38475eb44d8f67e4f7300f4625a33276/VoiceAssistant/AssistantStart.wav -------------------------------------------------------------------------------- /VoiceAssistant/AudioOutSingleton.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using NAudio.Wave; 4 | 5 | using PluginInterface; 6 | 7 | using System.Globalization; 8 | using System.IO; 9 | using System.Speech.AudioFormat; 10 | using System.Speech.Synthesis; 11 | using System.Threading; 12 | 13 | namespace VoiceAssistant 14 | { 15 | public class AudioOutSingleton : IAudioOutSingleton 16 | { 17 | private static AudioOutSingleton _instance; 18 | private static readonly object SyncRoot = new(); 19 | private readonly SpeechSynthesizer _synthesizer; 20 | private readonly PromptBuilder _promptBuilder; 21 | private readonly WaveOut _waveOut; 22 | private readonly int _sampleRate; 23 | private readonly string _speakerLanguage; 24 | 25 | protected AudioOutSingleton(string speakerLanguage, SpeechSynthesizer synthesizer, PromptBuilder promptBuilder, 26 | WaveOut waveOut, int sampleRate) 27 | { 28 | _speakerLanguage = speakerLanguage; 29 | _synthesizer = synthesizer; 30 | _promptBuilder = promptBuilder; 31 | _waveOut = waveOut; 32 | _sampleRate = sampleRate; 33 | } 34 | 35 | public static AudioOutSingleton GetInstance(string speakerLanguage, SpeechSynthesizer synthesizer, 36 | PromptBuilder promptBuilder, WaveOut waveOut, int sampleRate) 37 | { 38 | if (_instance != null) 39 | return _instance; 40 | 41 | lock (SyncRoot) 42 | { 43 | _instance ??= new AudioOutSingleton(speakerLanguage, synthesizer, promptBuilder, waveOut, 44 | sampleRate); 45 | } 46 | 47 | return _instance; 48 | } 49 | 50 | public void Speak(string text, bool exclusive = true) 51 | { 52 | if (_instance == null) 53 | return; 54 | 55 | lock (SyncRoot) 56 | { 57 | var speechStream = new MemoryStream(); 58 | var rs = new RawSourceWaveStream(speechStream, new WaveFormat(_sampleRate, 2)); 59 | _synthesizer.SetOutputToAudioStream(speechStream, 60 | new SpeechAudioFormatInfo(_sampleRate, AudioBitsPerSample.Sixteen, AudioChannel.Stereo)); 61 | 62 | _promptBuilder.StartVoice(new CultureInfo(_speakerLanguage)); 63 | _promptBuilder.AppendText(text); 64 | _promptBuilder.EndVoice(); 65 | _synthesizer.Speak(_promptBuilder); 66 | _promptBuilder.ClearContent(); 67 | 68 | rs.Position = 0; 69 | _waveOut.Init(rs); 70 | _waveOut.Play(); 71 | 72 | if (!exclusive) 73 | return; 74 | 75 | while (_waveOut.PlaybackState == PlaybackState.Playing) 76 | { 77 | Thread.Sleep(100); 78 | } 79 | } 80 | } 81 | 82 | public void PlayFile(string audioFile, bool exclusive = true) 83 | { 84 | if (_instance == null) 85 | return; 86 | 87 | lock (SyncRoot) 88 | { 89 | using (var file = new AudioFileReader(audioFile)) 90 | { 91 | _waveOut.Init(file); 92 | _waveOut.Play(); 93 | 94 | if (!exclusive) 95 | return; 96 | 97 | while (_waveOut.PlaybackState == PlaybackState.Playing) 98 | { 99 | Thread.Sleep(100); 100 | } 101 | } 102 | } 103 | } 104 | 105 | public void PlayDataBuffer(byte[] data, bool exclusive = true) 106 | { 107 | if (_instance == null) 108 | return; 109 | 110 | lock (SyncRoot) 111 | { 112 | using (var provider = new RawSourceWaveStream(new MemoryStream(data), new WaveFormat(16000, 1))) 113 | { 114 | provider.Position = 0; 115 | _waveOut.Init(provider); 116 | _waveOut.Play(); 117 | 118 | if (!exclusive) 119 | return; 120 | 121 | while (_waveOut.PlaybackState == PlaybackState.Playing) 122 | { 123 | Thread.Sleep(100); 124 | } 125 | } 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /VoiceAssistant/GenericPluginLoader.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.Loader; 9 | 10 | using PluginInterface; 11 | 12 | namespace VoiceAssistant 13 | { 14 | public class GenericPluginLoader where T : PluginBase 15 | { 16 | private readonly List> _loadContexts = new(); 17 | 18 | public List LoadAll(string pluginPath, string filter, params object[] constructorArgs) 19 | { 20 | var plugins = new List(); 21 | 22 | foreach (var filePath in Directory.EnumerateFiles(pluginPath, filter, SearchOption.AllDirectories)) 23 | { 24 | try 25 | { 26 | var plugin = Load(filePath, constructorArgs); 27 | 28 | if (plugin != null) 29 | { 30 | plugins.Add(plugin); 31 | } 32 | } 33 | catch (Exception ex) 34 | { 35 | Console.WriteLine($"Error loading plugin {filePath}: {ex}"); 36 | } 37 | } 38 | 39 | return plugins; 40 | } 41 | 42 | private T Load(string pluginPath, params object[] constructorArgs) 43 | { 44 | var loadContext = new GenericAssemblyLoadContext(pluginPath); 45 | 46 | _loadContexts.Add(loadContext); 47 | 48 | var assembly = loadContext.LoadFromAssemblyPath(pluginPath); 49 | 50 | var type = assembly.GetTypes().FirstOrDefault(t => typeof(T).IsAssignableFrom(t)); 51 | if (type == null) return null; 52 | 53 | var newArgs = new List(); 54 | newArgs.AddRange(constructorArgs); 55 | newArgs.Add(pluginPath); 56 | 57 | return (T)Activator.CreateInstance(type, newArgs.ToArray()); 58 | } 59 | 60 | public void UnloadAll() 61 | { 62 | foreach (var loadContext in _loadContexts) loadContext.Unload(); 63 | } 64 | } 65 | 66 | public class GenericAssemblyLoadContext : AssemblyLoadContext where T : class 67 | { 68 | private readonly AssemblyDependencyResolver _resolver; 69 | private readonly HashSet _assembliesToNotLoadIntoContext; 70 | 71 | public GenericAssemblyLoadContext(string pluginPath) : base(true) 72 | { 73 | var pluginInterfaceAssembly = typeof(T).Assembly.FullName; 74 | _assembliesToNotLoadIntoContext = GetReferencedAssemblyFullNames(pluginInterfaceAssembly); 75 | _assembliesToNotLoadIntoContext?.Add(pluginInterfaceAssembly); 76 | _resolver = new AssemblyDependencyResolver(pluginPath); 77 | } 78 | 79 | private static HashSet GetReferencedAssemblyFullNames(string referencedBy) 80 | { 81 | return AppDomain.CurrentDomain 82 | .GetAssemblies() 83 | .FirstOrDefault(t => t.FullName == referencedBy)? 84 | .GetReferencedAssemblies()? 85 | .Select(t => t.FullName)? 86 | .ToHashSet(); 87 | } 88 | 89 | protected override Assembly Load(AssemblyName assemblyName) 90 | { 91 | //Do not load the Plugin Interface DLL into the adapter's context 92 | //otherwise IsAssignableFrom is false. 93 | if (_assembliesToNotLoadIntoContext?.Contains(assemblyName.FullName) ?? true) 94 | return null; 95 | 96 | var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); 97 | 98 | return assemblyPath != null ? LoadFromAssemblyPath(assemblyPath) : null; 99 | } 100 | 101 | protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) 102 | { 103 | var libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); 104 | 105 | return libraryPath != null ? LoadUnmanagedDllFromPath(libraryPath) : IntPtr.Zero; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /VoiceAssistant/Misrecognition.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jekyll2014/VoiceAssistant/bea10e8c38475eb44d8f67e4f7300f4625a33276/VoiceAssistant/Misrecognition.wav -------------------------------------------------------------------------------- /VoiceAssistant/ProcessingCommand.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | using PluginInterface; 8 | 9 | namespace VoiceAssistant 10 | { 11 | public class ProcessingCommand 12 | { 13 | public string PluginName; 14 | public PluginCommand ExpectedCommand; 15 | public List CommandTokens = new(); 16 | 17 | public override string ToString() 18 | { 19 | var sb = new StringBuilder(); 20 | sb.AppendLine($"Plugin: {PluginName}"); 21 | sb.AppendLine($" Command name: {ExpectedCommand.Name}"); 22 | sb.AppendLine($" Expected phrase: {ExpectedCommand.ToString()}"); 23 | sb.AppendLine($" Assembled phrase: {CommandTokensToString()}"); 24 | 25 | return sb.ToString(); 26 | } 27 | 28 | public string CommandTokensToString() 29 | { 30 | var sb = new StringBuilder(); 31 | 32 | if (CommandTokens != null && CommandTokens.Count > 0) 33 | { 34 | foreach (var token in CommandTokens.Select(n => n.Value)) 35 | { 36 | if (sb.Length > 0) 37 | { 38 | sb.Append(' '); 39 | } 40 | 41 | if (token.Length == 1) 42 | { 43 | sb.Append($"{token[0]}"); 44 | } 45 | else 46 | { 47 | sb.Append('['); 48 | var nextElement = false; 49 | 50 | foreach (var word in token) 51 | { 52 | if (nextElement) 53 | { 54 | sb.Append(", "); 55 | } 56 | 57 | sb.Append($"{word}"); 58 | nextElement = true; 59 | } 60 | 61 | sb.Append(']'); 62 | } 63 | } 64 | } 65 | 66 | return sb.ToString(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /VoiceAssistant/VoiceAssistant.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0-windows 6 | 1.0.2 7 | LICENSE 8 | https://github.com/jekyll2014/VoiceAssistant 9 | Jekyll 10 | VoiceAssistant.Program 11 | 12 | 13 | 14 | 15 | tlbimp 16 | 4 17 | 5 18 | c866ca3a-32f7-11d2-9602-00c04f8ee628 19 | 0 20 | false 21 | true 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | PreserveNewest 39 | 40 | 41 | PreserveNewest 42 | 43 | 44 | True 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /VoiceAssistant/VoiceAssistantSettings.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | namespace VoiceAssistant 4 | { 5 | public class VoiceAssistantSettings 6 | { 7 | //[JsonProperty(Required = Required.Always)] 8 | public string ModelFolder = "model"; 9 | public string SelectedAudioInDevice = ""; 10 | public string SelectedAudioOutDevice = ""; 11 | public int AudioInSampleRate = 16000; 12 | public int AudioOutSampleRate = 44100; 13 | public string[] CallSign = { "Вася" }; 14 | public int DefaultSuccessRate = 90; 15 | public string VoiceName = ""; 16 | public string SpeakerCulture = "ru-RU"; 17 | public string PluginsFolder = "plugins"; 18 | public string PluginFileMask = "*Plugin.dll"; 19 | public string StartSound = "AssistantStart.wav"; 20 | public string MisrecognitionSound = "Misrecognition.wav"; 21 | public int CommandAwaitTime = 10; 22 | public int NextWordAwaitTime = 3; 23 | public int VoskLogLevel = -1; 24 | public string CommandNotRecognizedMessage = "Команда не распознана"; 25 | public string CommandNotFoundMessage = "Команда не найдена"; 26 | public bool AllowPluginsToListenToSound = false; 27 | public bool AllowPluginsToListenToWords = false; 28 | public bool AllowPluginsToInjectSound = false; 29 | public bool AllowPluginsToInjectWords = false; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /VoiceAssistant/VoskResult.cs: -------------------------------------------------------------------------------- 1 | // This is an independent project of an individual developer. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | using System.Collections.Generic; 4 | 5 | namespace VoiceAssistant 6 | { 7 | public class VoskResult 8 | { 9 | public List result { get; set; } 10 | public string text { get; set; } 11 | } 12 | 13 | public class Result 14 | { 15 | public double conf { get; set; } 16 | public double end { get; set; } 17 | public double start { get; set; } 18 | public string word { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /VoiceAssistant/appsettings_eng.json: -------------------------------------------------------------------------------- 1 | { 2 | "ModelFolder": "model", 3 | "SelectedAudioInDevice": "", 4 | "SelectedAudioOutDevice": "", 5 | "AudioInSampleRate": 16000, 6 | "AudioOutSampleRate": 44100, 7 | "CallSign": [ 8 | "Baby" 9 | ], 10 | "DefaultSuccessRate": 90, 11 | "VoiceName": "", 12 | "SpeakerCulture": "en-US", 13 | "PluginsFolder": "plugins", 14 | "PluginFileMask": "*Plugin.dll", 15 | "StartSound": "AssistantStart.wav", 16 | "MisrecognitionSound": "Misrecognition.wav", 17 | "CommandAwaitTime": 10, 18 | "NextWordAwaitTime": 3, 19 | "VoskLogLevel": -1, 20 | "CommandNotRecognizedMessage": "Command not recognized", 21 | "CommandNotFoundMessage": "Command not found", 22 | "AllowPluginsToListenToSound": false, 23 | "AllowPluginsToListenToWords": false 24 | } -------------------------------------------------------------------------------- /VoiceAssistant/desktop.ini: -------------------------------------------------------------------------------- 1 | [LocalizedFileNames] 2 | Speech Misrecognition.wav=@%windir%\system32\Speech\SpeechUX\sapi.cpl,-5580 3 | --------------------------------------------------------------------------------