├── Version ├── dist └── .gitkeep ├── assets ├── logo.jpg ├── logo48.ico ├── logo48.jpg └── logoHD.jpg ├── .gitignore ├── src ├── Config.json ├── ChatGPT-Executor.sln ├── WebSocketService.cs ├── ChatGPT-Executor.csproj ├── firstPrompt.md └── CommandManager.cs ├── ReleaseNotes.md ├── Improvements.md ├── scripts ├── CreateNewPack.sh ├── CreateNewPack.bat ├── InnoSetUp_Installer.bat └── CreateSetup.iss ├── LICENSE_NOTICE.md ├── CONTRIBUTING.md ├── README.md └── LICENSE-APACHE.txt /Version: -------------------------------------------------------------------------------- 1 | 1.2.0 -------------------------------------------------------------------------------- /dist/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/improveTheWorld/ChatGPT-Executor/HEAD/assets/logo.jpg -------------------------------------------------------------------------------- /assets/logo48.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/improveTheWorld/ChatGPT-Executor/HEAD/assets/logo48.ico -------------------------------------------------------------------------------- /assets/logo48.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/improveTheWorld/ChatGPT-Executor/HEAD/assets/logo48.jpg -------------------------------------------------------------------------------- /assets/logoHD.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/improveTheWorld/ChatGPT-Executor/HEAD/assets/logoHD.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | 3 | # Ignore everything in the dist folder 4 | /dist/* 5 | 6 | # But do not ignore the release notes file 7 | !/dist/RELEASE_NOTES.md 8 | -------------------------------------------------------------------------------- /src/Config.json: -------------------------------------------------------------------------------- 1 | { 2 | "FirstPromptFilePath": "firstPrompt.md", 3 | "Header": "CHATGPT<< \\r\\n", 4 | "Tail": "\r\n >>CHATGPT", 5 | "AskForNext": "<>", 6 | "WsPort": "8181", 7 | "MaxWords": "2400", 8 | "MaxMessageSize": "4000", 9 | "AcceptNewClients": "false", 10 | "AlreadyHadClient": "true" 11 | } -------------------------------------------------------------------------------- /ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | # V1.2.0 , what's new : 2 | 3 | - Implemented authentication token mechanisms to prevent server misuse. 4 | - Resolved an issue involving lost connections without recovery attempts. 5 | - Introduced log generation for better tracking and debugging. 6 | - Externalized modifiable variables to a dedicated configuration file for enhanced customization. 7 | - Converted the server application into a Windows service for seamless integration and execution. 8 | - Digitally signed the installation package to mitigate antivirus warnings. -------------------------------------------------------------------------------- /Improvements.md: -------------------------------------------------------------------------------- 1 | * Update the protocol to directly handle writing text to a file as generated for the user. 2 | 3 | * Enhance the protocol to support updating existing files with a patch generated from ChatGPT. This reduces the number of tokens required to modify an existing file (as ChatGPT has limited token memory). Add the ability to apply patches and save the result in a new file. 4 | 5 | * When ChatGPT is asked to read a file, start by providing the total number of lines. 6 | 7 | * Ensure compatibility with the Linux shell. 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ChatGPT-Executor.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33516.290 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatGPT-Executor", "ChatGPT-Executor.csproj", "{951901AD-D3E1-43B0-945C-0123138587C5}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {951901AD-D3E1-43B0-945C-0123138587C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {951901AD-D3E1-43B0-945C-0123138587C5}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {951901AD-D3E1-43B0-945C-0123138587C5}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {951901AD-D3E1-43B0-945C-0123138587C5}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {1FE0E9CD-EA11-4528-9B4F-CC8E201EDBBC} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /scripts/CreateNewPack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the version number from the file 4 | version=$(cat ../Version) 5 | 6 | # Check if version is not empty 7 | if [ ! -z "$version" ]; then 8 | # Build the project and redirect only the error output 9 | dotnet build ../src --configuration Release /p:Version=$version /p:Force=true 2> build_errors.txt 10 | 11 | # Check if the error output is empty 12 | # ... 13 | else 14 | echo "Error: Failed to retrieve the version number from the file." 15 | fi 16 | 17 | # Check if the error output is empty 18 | if [ ! -s build_errors.txt ]; then 19 | echo "Compilation succeeded, creating zip file..." 20 | 21 | # Delete existing zip file if it exists 22 | if [ -e "../dist/Executor_$version.zip" ]; then 23 | rm "../dist/Executor_$version.zip" 24 | fi 25 | 26 | # Zip the output files 27 | mkdir -p ../dist 28 | zip -r "../dist/Executor_$version.zip" "../src/bin/Release/net6.0" 29 | echo "Zip file created successfully." 30 | 31 | # Clean the bin and obj folders 32 | echo "Cleaning up the bin and obj folders..." 33 | rm -rf "../src/bin" 34 | rm -rf "../src/obj" 35 | echo "Clean-up completed." 36 | 37 | else 38 | echo "Compilation failed. Please check the build_errors.txt for more information." 39 | fi 40 | 41 | # Clean up 42 | rm build_errors.txt 43 | -------------------------------------------------------------------------------- /src/WebSocketService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Topshelf; 7 | using System.IO; 8 | 9 | namespace ChatGPTExecutor 10 | { 11 | 12 | internal class WebSocketService 13 | { 14 | private CommandManager cmdManager; 15 | 16 | public WebSocketService() 17 | { 18 | this.cmdManager = new CommandManager(); 19 | } 20 | 21 | public void Start() 22 | { 23 | this.cmdManager.Start(); 24 | } 25 | 26 | public void Stop() 27 | { 28 | this.cmdManager.Stop(); 29 | } 30 | 31 | } 32 | 33 | public class ChatGPTExecutorService 34 | { 35 | static void Main() 36 | { 37 | HostFactory.Run(x => 38 | { 39 | x.Service(s => 40 | { 41 | s.ConstructUsing(name => new WebSocketService()); 42 | s.WhenStarted(tc => tc.Start()); 43 | s.WhenStopped(tc => tc.Stop()); 44 | }); 45 | x.RunAsLocalSystem(); 46 | 47 | x.SetDescription("Ce service gère une connexion WebSocket pour exécuter des commandes windows"); 48 | x.SetDisplayName("ChatGPT-Executor"); 49 | x.SetServiceName("ChatGPT-Executor"); 50 | }); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE_NOTICE.md: -------------------------------------------------------------------------------- 1 | # Dual License Notice 2 | 3 | This software is licensed under two separate licenses: a permissive open-source license for free software projects, and a commercial license for non-free software projects. By using this software, you agree to comply with the terms of the appropriate license. 4 | 5 | ## For Free Software Projects 6 | 7 | If you are using this software in a free software project, you may use it under the terms of the [Apache License, Version 2.0](LICENSE-APACHE.txt). This license allows for free use, modification, and distribution, as long as you include the copyright notice, license text, and any modifications made to the software are clearly marked. 8 | 9 | ## For Non-Free Software Projects 10 | 11 | If you are using this software in a non-free software project, you are required to obtain a commercial license. Please contact the author at bilelgatri@gmail.com to discuss licensing terms and pricing. 12 | 13 | By using this software in a non-free software project without obtaining a commercial license, you agree that you are in violation of the terms of this notice and may be subject to legal action. 14 | 15 | ## Acknowledgment and Link for Both Commercial and Non-Commercial Use 16 | 17 | Regardless of whether your use is commercial or non-commercial, when using the compiled version of this software in a third-party project, we kindly ask that you acknowledge the author and provide a link to the software GitHub repository. 18 | 19 | Remember, this dual-license notice is provided as a reference and may not cover all legal aspects. Consult with a legal professional to ensure your licensing choices meet your specific requirements and expectations. 20 | -------------------------------------------------------------------------------- /scripts/CreateNewPack.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | rem Get the version number from the file 5 | set "version=" 6 | for /f "tokens=* delims=" %%F in (..\Version) do set "version=%%F" 7 | 8 | rem Check if version is not empty 9 | if not "!version!"=="" ( 10 | rem Build the project and redirect only the error output 11 | dotnet build ../src --configuration Release /p:Version=%version% /p:Force=true 2> build_errors.txt 12 | 13 | rem Check if the error output is empty 14 | rem ... 15 | ) else ( 16 | echo "Error: Failed to retrieve the version number from the file." 17 | ) 18 | 19 | 20 | rem Check if the error output is empty 21 | for %%I in (build_errors.txt) do set fileSize=%%~zI 22 | if !fileSize!==0 ( 23 | echo Compilation succeeded, creating zip file... 24 | 25 | rem Delete existing zip file if it exists 26 | if exist "..\dist\Executor_%version%.zip" ( 27 | del "..\dist\Executor_%version%.zip" 28 | ) 29 | 30 | ISCC CreateSetup.iss /DMyAppVersion=%version% 31 | rem Zip the output files 32 | powershell -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::CreateFromDirectory('..\src\bin\Release\net6.0', '..\dist\Executor_%version%.zip'); }" 33 | 34 | echo Zip file created successfully. 35 | 36 | rem Clean the bin and obj folders 37 | echo Cleaning up the bin and obj folders... 38 | rmdir /s /q "..\src\bin" 39 | rmdir /s /q "..\src\obj" 40 | echo Clean-up completed. 41 | 42 | ) else ( 43 | echo Compilation failed. Please check the build_errors.txt for more information. 44 | ) 45 | rem Clean up 46 | del build_errors.txt 47 | 48 | :end 49 | -------------------------------------------------------------------------------- /src/ChatGPT-Executor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | ..\assets\logo48.ico 9 | 2023 @Tec-Net 10 | README.md 11 | https://github.com/improveTheWorld/ChatGPT-Executor.git 12 | LICENSE_NOTICE.md 13 | True 14 | 1.0.0-rc1 15 | 16 | Tec-Net 17 | ChatGPT-Driver 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Always 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | True 43 | \ 44 | 45 | 46 | True 47 | \ 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /scripts/InnoSetUp_Installer.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | :: Install/Uninstall Inno Setup 5 | 6 | :: Set paths 7 | set "InnoVersion=6.2.2" 8 | set "InnoPath=%ProgramFiles(x86)%\Inno Setup 6" 9 | 10 | :: Check if Inno Setup is installed 11 | if exist "%InnoPath%" goto checkISCC 12 | goto installPrompt 13 | 14 | :checkISCC 15 | if exist "%InnoPath%\ISCC.exe" goto uninstallPrompt 16 | goto installPrompt 17 | 18 | :installPrompt 19 | set /p UserInput=Inno Setup is not installed. Do you want to install it (y/n)? 20 | if /i "%UserInput%"=="y" goto install 21 | goto end 22 | 23 | :install 24 | :: Install Inno Setup 25 | powershell -Command "Invoke-WebRequest -Uri 'http://files.jrsoftware.org/is/6/innosetup-%InnoVersion%.exe' -OutFile 'innosetup-%InnoVersion%.exe'" 26 | innosetup-%InnoVersion%.exe /VERYSILENT 27 | 28 | :addPath 29 | :: Add to system PATH 30 | for /F "tokens=2,* delims= " %%A in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PATH ^| findstr /i path') do set "SysPath=%%B" 31 | setx /M PATH "%SysPath%;%InnoPath%" 32 | 33 | goto end 34 | 35 | :uninstallPrompt 36 | set /p UserInput=Inno Setup is installed. Do you want to uninstall it (y/n)? 37 | if /i "%UserInput%"=="y" goto uninstall 38 | goto end 39 | 40 | :uninstall 41 | :: Uninstall Inno Setup 42 | "%InnoPath%\unins000.exe" /silent 43 | 44 | :: Remove from PATH 45 | for /F "tokens=2,* delims= " %%A in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PATH ^| findstr /i path') do set "SysPath=%%B" 46 | set "NewPath=" 47 | for %%A in ("%SysPath:;=" "%"") do ( 48 | if /I not "%%~A"=="%InnoPath%" ( 49 | if "!NewPath!"=="" ( 50 | set "NewPath=%%~A" 51 | ) else ( 52 | set "NewPath=!NewPath!;%%~A" 53 | ) 54 | ) 55 | ) 56 | 57 | :: Update system PATH 58 | setx /M PATH "%NewPath%" 59 | 60 | echo Uninstallation complete. 61 | goto end 62 | 63 | :end 64 | endlocal -------------------------------------------------------------------------------- /src/firstPrompt.md: -------------------------------------------------------------------------------- 1 | # Title: Collaborative Task Execution with MMI 2 | 3 | ## Background 4 | You are an Automated AI assistant designed to generate Windows commands for multiple purpose, working alongside a user (which is me) and an external program called MMI. The user assigns tasks to you, and you generate Windows commands to accomplish those tasks. MMI is a program that only understands Windows commands and can be thought of as a Windows command prompt you can communicate with. MMI executes your commands and provides you with the output, which you then analyze and adapt your commands based on the feedback. 5 | 6 | ## User Communication Guidelines 7 | 8 | - Communicate with user until you understand well your task, you'll have to use MMI to execute Windows commands needed to accomplish it. 9 | - Once the task is clear, avoid communicating with user unless it is essential and only to seek information or clafifications. 10 | - Begin each message to the user with "Sir:". 11 | 12 | ## MMI comunication protocol 13 | - Remeber you may use powershell commands, if so , always call them via cmd 14 | - Start each command batch to MMI with "MMI<< " and end it with " >>MMI", e.g. "MMI<< (command1 && command2) || command3 >>MMI". MMI will extract the message between these two tokens, execute the commands, and give you the feedback. 15 | - One command batch is allowed at a time. So, wait until you finish receiving a hole feedback from MMI before starting new comand batch. 16 | - MMI's feedback always begins with "CHATGPT<< " and ends with " >>CHATGPT". 17 | - MMI has a limited output size, so it may divide feedback into consecutive pieces. 18 | - When the feedback is too large, MMI will divide it into frames. The first frame will start with the exact token "CHATGPT<< " and end with "<>", while the last frame will end with the exact token " >>CHATGPT". Each intermediate frame will end with the "<>" token. 19 | - You should bufferise these pieces, respond each time with "MMI<< NEXT >>MMI" for each piece until the full feedback is received before proceeding. 20 | - Always wait until you receive the " >>CHATGPT" before considering that the feedback is complete. 21 | - Handle and process the frames, requesting the next piece with "MMI<< NEXT >>MMI" until the entire feedback is received and understood before moving on. 22 | - Always wait for the " >>CHATGPT" token before proceeding with the next steps. 23 | - Avoid communicating with the user unless it is necessary to request information or to inform them that the task has been completed. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Thank you for your interest in contributing to ChatGPT-Executor! We appreciate your effort and value your contributions. This document provides guidelines to help make the contribution process smooth and effective for everyone involved. 4 | 5 | ## Getting Started 6 | 7 | 1. **Fork the repository**: Start by forking the repository to your own GitHub account. This allows you to make changes to the project without affecting the original repository. 8 | 9 | 2. **Create a branch**: Create a new branch with a descriptive name based on the feature or bugfix you plan to work on. For example, `fix-issue-123` or `add-new-feature`. 10 | 11 | 3. **Clone the repository**: Clone the forked repository to your local machine and set the upstream remote to the original repository. 12 | 13 | >git clone https://github.com/improveTheWorld/ChatGPT-Executor.git 14 | 15 | >cd ChatGPT-Executor 16 | 17 | >git remote add upstream https://github.com/improveTheWorld/ChatGPT-Executor.git 18 | 19 | 20 | 21 | ## Contributing Code 22 | 23 | 1. **Sync your fork**: Before starting your work, ensure your fork is up-to-date with the original repository. 24 | 25 | git fetch upstream 26 | git merge upstream/main 27 | 28 | 29 | 2. **Write clean and well-documented code**: While working on your changes, make sure to follow the project's coding style and write well-documented code. 30 | 31 | 3. **Test your changes**: Test your changes thoroughly to ensure that you do not introduce new bugs. 32 | 33 | 4. **Commit your changes**: Break your work into small, logical commits with descriptive commit messages. This makes it easier for maintainers to review your changes. 34 | 35 | 5. **Push your changes**: Push your changes to your fork on GitHub. 36 | 37 | git push origin 38 | 39 | 40 | ## Submitting a Pull Request 41 | 42 | 1. **Create a Pull Request**: Go to the original repository on GitHub, and click the "New Pull Request" button. Choose the branch you worked on and submit the Pull Request. 43 | 44 | 2. **Describe your changes**: In the Pull Request description, provide a clear and concise explanation of the changes you made, referencing any related issues or discussions. 45 | 46 | 3. **Wait for a review**: The maintainers will review your Pull Request and may request changes or ask questions. Please be patient and address any feedback provided. 47 | 48 | 4. **Make any requested changes**: If the maintainers request changes, make the necessary updates and push the changes to your branch. 49 | 50 | 5. **Celebrate**: Once your Pull Request is merged, you've successfully contributed to the project! Thank you for your efforts! 51 | 52 | ## Reporting Issues 53 | 54 | If you find a bug or have a suggestion, please [open a new issue](https://github.com/improveTheWorld/ChatGPT-Executor/issues/new) in the repository. When reporting a bug, provide as much information as possible, including steps to reproduce the problem, your operating system, and any relevant error messages. 55 | 56 | ## Additional Resources 57 | 58 | - [GitHub Documentation](https://docs.github.com/en) 59 | - [Git Documentation](https://git-scm.com/doc) 60 | 61 | Thank you for contributing to [Your Project Name]! 62 | Remember to replace [Your Project Name], , , and with the appropriate information for your project. -------------------------------------------------------------------------------- /scripts/CreateSetup.iss: -------------------------------------------------------------------------------- 1 | 2 | ; Script generated by the Inno Setup Script Wizard. 3 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 4 | 5 | #define MyAppName "ChatGPT-Executor" 6 | #define MyAppPublisher "Tec-Net" 7 | #define MyAppURL "https://github.com/improveTheWorld/ChatGPT-Executor" 8 | #define MyAppExeName "ChatGPT-Executor.exe" 9 | #define buildFoler "..\src\bin\Release\net6.0" 10 | 11 | 12 | ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. 13 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 14 | [Setup] 15 | AppId={{0AC7A55E-A919-478B-9201-1E7470C8D167}} 16 | AppName={#MyAppName} 17 | AppVersion={#MyAppVersion} 18 | AppPublisher={#MyAppPublisher} 19 | AppPublisherURL={#MyAppURL} 20 | AppSupportURL={#MyAppURL} 21 | AppUpdatesURL={#MyAppURL} 22 | DefaultDirName={autopf}\{#MyAppName} 23 | OutputDir=../dist/ 24 | DisableProgramGroupPage=yes 25 | LicenseFile=..\LICENSE-APACHE.txt 26 | PrivilegesRequired=admin 27 | PrivilegesRequiredOverridesAllowed=commandline 28 | OutputBaseFilename=ChatGPT-Executor-{#MyAppVersion}-Setup 29 | Compression=lzma 30 | SolidCompression=yes 31 | WizardStyle=modern 32 | AppMutex=MyProgramMutex 33 | SignTool=signTool 34 | SetupIconFile=..\assets\logo48.ico 35 | 36 | 37 | [Languages] 38 | Name: "english"; MessagesFile: "compiler:Default.isl" 39 | Name: "armenian"; MessagesFile: "compiler:Languages\Armenian.isl" 40 | Name: "brazilianportuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl" 41 | Name: "bulgarian"; MessagesFile: "compiler:Languages\Bulgarian.isl" 42 | Name: "catalan"; MessagesFile: "compiler:Languages\Catalan.isl" 43 | Name: "corsican"; MessagesFile: "compiler:Languages\Corsican.isl" 44 | Name: "czech"; MessagesFile: "compiler:Languages\Czech.isl" 45 | Name: "danish"; MessagesFile: "compiler:Languages\Danish.isl" 46 | Name: "dutch"; MessagesFile: "compiler:Languages\Dutch.isl" 47 | Name: "finnish"; MessagesFile: "compiler:Languages\Finnish.isl" 48 | Name: "french"; MessagesFile: "compiler:Languages\French.isl" 49 | Name: "german"; MessagesFile: "compiler:Languages\German.isl" 50 | Name: "hebrew"; MessagesFile: "compiler:Languages\Hebrew.isl" 51 | Name: "hungarian"; MessagesFile: "compiler:Languages\Hungarian.isl" 52 | Name: "icelandic"; MessagesFile: "compiler:Languages\Icelandic.isl" 53 | Name: "italian"; MessagesFile: "compiler:Languages\Italian.isl" 54 | Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl" 55 | Name: "norwegian"; MessagesFile: "compiler:Languages\Norwegian.isl" 56 | Name: "polish"; MessagesFile: "compiler:Languages\Polish.isl" 57 | Name: "portuguese"; MessagesFile: "compiler:Languages\Portuguese.isl" 58 | Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl" 59 | Name: "slovak"; MessagesFile: "compiler:Languages\Slovak.isl" 60 | Name: "slovenian"; MessagesFile: "compiler:Languages\Slovenian.isl" 61 | Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl" 62 | Name: "turkish"; MessagesFile: "compiler:Languages\Turkish.isl" 63 | Name: "ukrainian"; MessagesFile: "compiler:Languages\Ukrainian.isl" 64 | 65 | 66 | [Files] 67 | Source: "{#buildFoler}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion 68 | Source: "{#buildFoler}\ChatGPT-Executor.deps.json"; DestDir: "{app}"; Flags: ignoreversion 69 | Source: "{#buildFoler}\ChatGPT-Executor.dll"; DestDir: "{app}"; Flags: ignoreversion 70 | Source: "{#buildFoler}\ChatGPT-Executor.exe"; DestDir: "{app}"; Flags: ignoreversion 71 | Source: "{#buildFoler}\ChatGPT-Executor.pdb"; DestDir: "{app}"; Flags: ignoreversion 72 | Source: "{#buildFoler}\ChatGPT-Executor.runtimeconfig.json"; DestDir: "{app}"; Flags: ignoreversion 73 | Source: "{#buildFoler}\Config.json"; DestDir: "{app}"; Flags: ignoreversion 74 | Source: "{#buildFoler}\firstPrompt.md"; DestDir: "{app}"; Flags: ignoreversion 75 | Source: "{#buildFoler}\Fleck.dll"; DestDir: "{app}"; Flags: ignoreversion 76 | Source: "{#buildFoler}\Microsoft.Win32.SystemEvents.dll"; DestDir: "{app}"; Flags: ignoreversion 77 | Source: "{#buildFoler}\System.Diagnostics.EventLog.dll"; DestDir: "{app}"; Flags: ignoreversion 78 | Source: "{#buildFoler}\System.ServiceProcess.ServiceController.dll"; DestDir: "{app}"; Flags: ignoreversion 79 | Source: "{#buildFoler}\Topshelf.dll"; DestDir: "{app}"; Flags: ignoreversion 80 | Source: "{#buildFoler}\TopShelf.ServiceInstaller.dll"; DestDir: "{app}"; Flags: ignoreversion 81 | Source: "{#buildFoler}\Serilog.dll"; DestDir: "{app}"; Flags: ignoreversion 82 | Source: "{#buildFoler}\Serilog.Formatting.Compact.dll"; DestDir: "{app}"; Flags: ignoreversion 83 | Source: "{#buildFoler}\Serilog.Sinks.Console.dll"; DestDir: "{app}"; Flags: ignoreversion 84 | Source: "{#buildFoler}\Serilog.Sinks.File.dll"; DestDir: "{app}"; Flags: ignoreversion 85 | Source: "{#buildFoler}\runtimes\*"; DestDir: "{app}\runtimes"; Flags: ignoreversion recursesubdirs createallsubdirs 86 | 87 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 88 | 89 | [Icons] 90 | Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" 91 | 92 | 93 | [Run] 94 | Filename: "{app}\{#MyAppExeName}"; Parameters: "install start --autostart"; Flags: runhidden 95 | Filename: "{cmd}"; Parameters: "/C sc config {#MyAppExeName} start= auto"; Flags: runhidden 96 | 97 | 98 | 99 | [UninstallRun] 100 | Filename: "{app}\{#MyAppExeName}"; Parameters: "uninstall"; Flags: runhidden; RunOnceId: "uninstallService" 101 | 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![ChatGPT-Executor Logo](./assets/logo.jpg) **ChatGPT-Executor** 2 | 3 | **ChatGPT-Executor is a server application that empowers ChatGPT to execute Windows commands, unlocking a wide range of applications and capabilities.** 4 | 5 | ChatGPT-Executor is part of the [ChatGPT-Driver](youtu.be/9fCtMJQxQ4c) system, designed to seamlessly integrate ChatGPT with third-party software and enable it to execute Windows commands. To use ChatGPT-Executor, you'll also need to install the [ChatGPT-Bridge](https://github.com/improveTheWorld/ChatGPT-Bridge) browser plugin. 6 | 7 | We are continuously striving to maintain and improve this project. If you love our work and find it useful, consider supporting us. Every small donation will go a long way in helping us to keep this project alive and free. **💌[Donate Here](https://www.paypal.com/donate/?hosted_button_id=SJTG7U2E6PC4W)💌** 8 | 9 | ## 🌟 Features 10 | 11 | * 💬 Real-time execution of Windows commands generated by ChatGPT 12 | * ⚡ Efficient WebSocket server 13 | * 🔄 Bidirectional communication with ChatGPT and the command execution environment 14 | * 📦 Easy integration with ChatGPT web plugin 15 | * 📄 Enable ChatGPT to read large files that exceed the size of a single prompt 16 | 17 | ## 🆕 Most recent Release Notes (Version 1.2.0) 18 | 19 | * Introduced an authentication token mechanism to safeguard against server misuse. 20 | * Fixed an issue relating to lost connections without recovery attempts. 21 | * Added logging capability for improved traceability and debugging. 22 | * Migrated modifiable variables to a dedicated configuration file to enhance customization. 23 | * Transformed the server application to run as a Windows service for seamless execution. 24 | * Digitally signed the setup to eliminate antivirus warnings. 25 | 26 | ## 🚀 Getting Started 27 | 28 | You may download and install Executor V1.2.0 from [Here](https://bit.ly/46rz4zE) 29 | 30 | If you want to genrate your own binaries , see the next paragraph. 31 | 32 | ### Prerequisites 33 | 34 | * Windows operating system 35 | 36 | ### Compilation 37 | 38 | * **Using command-line** 39 | 40 | Follow these instructions to compile the ChatGPT-Executor using command-line tools: 41 | 42 | 1. Ensure you have .NET 6.0 SDK installed on your system. If not, download it from the [.NET downloads page](https://dotnet.microsoft.com/download/dotnet/6.0). 43 | 2. Open a command prompt or terminal window. 44 | 3. If you have Git installed, you can use the following command: 45 | 46 | >`git clone https://github.com/improveTheWorld/ChatGPT-Executor.git` 47 | 48 | Alternatively, download the repository as a ZIP file to your local machine from [here](https://github.com/improveTheWorld/ChatGPT-Executor/archive/refs/heads/master.zip) and extract it to a folder on your local machine. 49 | 50 | 4. Change your working directory to the ChatGPT-Executor folder: 51 | 52 | >`cd ChatGPT-Executor` 53 | 54 | 5. Build the solution using the *"dotnet build"* command: 55 | 56 | >`dotnet build` 57 | 58 | 6. Once the build is successful, you can find the compiled executable in the "bin" folder of the project directory, under the "Release" folder. 59 | 60 | Congratulations! 🎉 ChatGPT-Executor is now up. Luanch it on your local machine. You need now to install the [ChatGPT-Bridge plugin](https://github.com/improveTheWorld/ChatGPT-Bridge) to see it in action. 61 | * **Using Microsoft visual Studio** 62 | 63 | 1. Clone the repository: 64 | 65 | git clone [https://github.com/improveTheWorld/ChatGPT-Executor.git](https://github.com/improveTheWorld/ChatGPT-Executor.git) 66 | 2. `To compile the ChatGPT-Executor, which is written in C# using Microsoft Visual Studio 2022 and targeting .NET 6.0, follow these steps: 67 | 68 | 1. Ensure you have Microsoft Visual Studio 2022 installed on your system. If not, download it from the [official Microsoft website](https://visualstudio.microsoft.com/vs/). 69 | 2. Install the .NET 6.0 SDK from the[.NET downloads page](https://dotnet.microsoft.com/download/dotnet/6.0). 70 | 3. download the repository as a ZIP file to your local machine from [here](https://github.com/improveTheWorld/ChatGPT-Executor/archive/refs/heads/master.zip) and extract it to a folder on your local machine. 71 | 4. Open the solution file (ChatGPT-Executor.sln) in Visual Studio 2022. 72 | 5. Build the solution by selecting "Build" from the menu, then "Build Solution" or use the shortcut "Ctrl+Shift+B". 73 | 6. Once the build is successful, you can find the compiled executable in the "bin" folder of the project directory, under the appropriate configuration folder (e.g., "Debug" or "Release"). 74 | 75 | Congratulations! 🎉 ChatGPT-Executor is now up. Luanch it on your local machine. You need now to install the [ChatGPT-Bridge plugin](https://github.com/improveTheWorld/ChatGPT-Bridge) to see it in action. 76 | 77 | ## 🛠️ Usage 78 | 79 | After running the ChatGPT-Executor server application, you need to use it in conjunction with ChatGPT-Bridge to enable it to execute Windows commands generated by CHATGPT. For detailed usage instructions, please refer to the [ChatGPT-Bridge GitHub Repository](https://github.com/improveTheWorld/ChatGPT-Bridge). 80 | 81 | `As an example of use, we asked ChatGPT to troubleshoot our WiFi card. We had an issue that the Windows network diagnostic tools were unable to detect. ChatGPT, after trying different approaches using our ChatGPT-Bridge system, was able to fix it for us.`[Video](https://youtu.be/9fCtMJQxQ4c) 82 | 83 | 87 | 📧 Contributing 88 | --------------- 89 | 90 | We welcome contributions! If you'd like to contribute, please follow the steps outlined in our [Contributing Guidelines](./CONTRIBUTING.md). 91 | 92 | ## ⚙️ Possible improvements 93 | 94 | **The first relase is already on the air, here are the next steps:** 95 | 96 | * Update the protocol to directly handle writing text to a file as generated for the user. 97 | 98 | * Enhance the protocol to support updating existing files with a patch generated from ChatGPT. This reduces the number of tokens required to modify an existing file (as ChatGPT has limited token memory). Add the ability to apply patches and save the result in a new file. 99 | 100 | * Introduce a configuration file to set the maximum word count for a prompt. 101 | 102 | * Optimize the maximum word usage by testing the real possible limit accepted in one prompt. 103 | 104 | * When ChatGPT is asked to read a file, start by providing the total number of lines. It seems helping him to handle the frames protocol. 105 | 106 | * When the Executor asks ChatGPT to continue, it should specify the last received line and request that ChatGPT not resend previously received content. 107 | 108 | * Ensure compatibility with the Linux system /linux shell. 109 | 110 | * Make the application run as a background service. 111 | 112 | * Move the management of the initial prompt to the server side to minimize dependency on the plugin protocol and ensure ongoing compatibility between the initial prompt version and the server-implemented version. To accomplish this, add a config parameter on the plugin side that requests an initial prompt when new communication is detected. The plugin should send a command to the server, which should be independent of the protocol, such as "Send\_First\_Prompt". 113 | 114 | 115 | ## 🔐 License 116 | 117 | * This project is licensed under the Apache V2.0 for free software use - see the [LICENSE](./LICENSE-APACHE.txt) file for details. 118 | * For commercial software use, see the [LICENSE\_NOTICE](./LICENSE_NOTICE.md) file. 119 | 120 | ## 📬 Contact 121 | 122 | If you have any questions or suggestions, please feel free to reach out to us: 123 | 124 | * [Tec-Net](mailto:tecnet.paris@gmail.com) 125 | 126 | 127 | 128 | **Together with ChatGPT-Bridge, ChatGPT-Executor unlocks the unlimited power and potential of ChatGPT. Join us on this exciting journey to revolutionize the way we interact with AI-powered language models!** 129 | -------------------------------------------------------------------------------- /LICENSE-APACHE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /src/CommandManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | using System.Threading; 7 | using Fleck; 8 | using System.Timers; 9 | using System.Security.Cryptography; 10 | using Serilog; 11 | using Serilog.Events; 12 | using Serilog.Sinks.File; 13 | using Serilog.Formatting.Compact; 14 | using System.Data; 15 | using Timer = System.Threading.Timer; 16 | using System.Text.Json; 17 | using System.Reflection.PortableExecutable; 18 | using System.Globalization; 19 | 20 | namespace ChatGPTExecutor 21 | { 22 | 23 | class CommandManager 24 | { 25 | private readonly struct Config 26 | { 27 | 28 | public readonly string FirstPromptFilePath; 29 | public readonly string Header; 30 | public readonly string Tail; 31 | public readonly string AskForNext; 32 | public readonly string WsPort; 33 | public readonly int MaxWords; 34 | public readonly int MaxMessageSize; 35 | public readonly bool AcceptNewClients; 36 | public readonly bool AlreadyHadClient; 37 | 38 | 39 | public Config(string ConfigFilePath) 40 | { 41 | 42 | 43 | string jsonContent = File.ReadAllText(ConfigFilePath); 44 | 45 | Dictionary dict = JsonSerializer.Deserialize>(jsonContent); 46 | 47 | Header = dict["Header"]; 48 | Tail = dict["Tail"]; 49 | AskForNext = dict["AskForNext"]; 50 | WsPort = dict["WsPort"]; 51 | MaxWords = Convert.ToInt32(dict["MaxWords"]); 52 | MaxMessageSize = Convert.ToInt32(dict["MaxMessageSize"]); 53 | AcceptNewClients = Convert.ToBoolean(dict["AcceptNewClients"]); 54 | AlreadyHadClient = Convert.ToBoolean(dict["AlreadyHadClient"]); 55 | FirstPromptFilePath = dict["FirstPromptFilePath"]; 56 | 57 | } 58 | } 59 | 60 | 61 | private static readonly Config config = new Config("Config.json"); 62 | 63 | private static List outputParts = new List(); 64 | private static int currentPartIndex = 0; 65 | private static string receptionBuffer = ""; 66 | 67 | private static bool firstOutputLine = true; 68 | private static List pendingCommands = new List(); 69 | 70 | private static Process cmdProcess; 71 | private static StreamWriter cmdStreamWriter; 72 | private static StringBuilder normalOutput = new StringBuilder(); 73 | private static StringBuilder errorOutput = new StringBuilder(); 74 | private static ManualResetEvent outputEndEvent = new ManualResetEvent(false); 75 | private static ManualResetEvent questionAskedEvent = new ManualResetEvent(false); 76 | private static ManualResetEvent answerProvidedEvent = new ManualResetEvent(false); 77 | private static System.Timers.Timer outputTimeoutTimer; 78 | private static string firstPrompt; 79 | 80 | private static WebSocketServer server; 81 | private static CancellationTokenSource cancellationTokenSource= new CancellationTokenSource(); 82 | private static Task serviceTask; 83 | private static Dictionary authSentences = new Dictionary(); 84 | private static string AuthKey; 85 | 86 | private static bool acceptNewClients = config.AcceptNewClients; 87 | private static bool alreadyHadClient = config.AlreadyHadClient; 88 | 89 | 90 | 91 | 92 | private void initfunKey(bool createIfNone = true) 93 | { 94 | AuthKey = loadFunKey(); 95 | if (createIfNone && AuthKey == string.Empty) 96 | { 97 | Log.Information("Creating new key ..."); 98 | AuthKey = GenerateRandomToken(); 99 | savefunKey(AuthKey); 100 | acceptNewClients = true; 101 | alreadyHadClient = false; 102 | new Timer( (_) => 103 | { 104 | acceptNewClients = false; 105 | }, 106 | null, 107 | 600000, 108 | Timeout.Infinite 109 | ); 110 | } 111 | } 112 | private void savefunKey(string funKey) 113 | { 114 | StreamWriter output = new StreamWriter("Stat"); 115 | 116 | string key = GenerateRandomToken(); 117 | output.Write(key + EncryptStringAES(Convert.ToBase64String(Encoding.UTF8.GetBytes(funKey)),key)); 118 | output.Flush(); 119 | output.Close(); 120 | } 121 | 122 | string loadFunKey() 123 | { 124 | string filePath = "Stat"; 125 | string text = ""; 126 | 127 | if (File.Exists(filePath)) 128 | { 129 | using (StreamReader input = new StreamReader(filePath)) 130 | { 131 | text = input.ReadToEnd(); 132 | } 133 | } 134 | else 135 | { 136 | acceptNewClients = true; 137 | alreadyHadClient = false; 138 | } 139 | 140 | if (text.Length > 44) 141 | { 142 | return DecryptStringAES(text.Substring(44), text.Substring(0, 44)); 143 | } 144 | else 145 | { 146 | return string.Empty; 147 | } 148 | 149 | } 150 | 151 | 152 | static void resetCommunicationBuffers() 153 | { 154 | outputParts.Clear(); 155 | currentPartIndex = 0; 156 | receptionBuffer = string.Empty; 157 | pendingCommands.Clear(); 158 | } 159 | 160 | 161 | public void Stop() 162 | { 163 | Log.Information("Shutting down..."); 164 | 165 | // Stop the cmd process 166 | if (!cmdProcess.HasExited) 167 | { 168 | cmdProcess.Kill(); 169 | } 170 | 171 | // Dispose the WebSocket server 172 | cancellationTokenSource.Cancel(); 173 | server.Dispose(); 174 | serviceTask.Wait(); 175 | 176 | Log.Information("WebSocket server stopped."); 177 | } 178 | 179 | public void Start() 180 | { 181 | serviceTask = Task.Run(() => StartServiceTask(), cancellationTokenSource.Token); 182 | } 183 | 184 | public static string GenerateRandomToken(int keySize = 256) 185 | { 186 | using (Aes aes = Aes.Create()) 187 | { 188 | aes.KeySize = keySize; 189 | aes.GenerateKey(); 190 | return Convert.ToBase64String(aes.Key); 191 | } 192 | } 193 | 194 | 195 | 196 | 197 | 198 | public static string EncryptStringAES(string b64Text, string b64Key) 199 | { 200 | byte[] keyBytes = Convert.FromBase64String(b64Key); 201 | byte[] plainBytes = Convert.FromBase64String(b64Text); 202 | 203 | using (Aes aes = Aes.Create()) 204 | { 205 | aes.Key = keyBytes; 206 | aes.GenerateIV(); // Generate a new IV for each encryption 207 | aes.Padding = PaddingMode.PKCS7; // Set the padding mode to PKCS7 208 | 209 | ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); 210 | 211 | using (MemoryStream ms = new MemoryStream()) 212 | { 213 | using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) 214 | { 215 | cs.Write(plainBytes, 0, plainBytes.Length); 216 | cs.FlushFinalBlock(); 217 | 218 | // Prepend the IV to the cipherText, so it's available for decryption 219 | byte[] cipherBytes = new byte[aes.IV.Length + ms.Length]; 220 | Array.Copy(aes.IV, 0, cipherBytes, 0, aes.IV.Length); 221 | Array.Copy(ms.ToArray(), 0, cipherBytes, aes.IV.Length, ms.Length); 222 | var b64encrypted = Convert.ToBase64String(cipherBytes); 223 | return b64encrypted; 224 | } 225 | } 226 | } 227 | } 228 | 229 | public static string DecryptStringAES(string b64CipherText, string b64Key) 230 | { 231 | try 232 | { 233 | byte[] keyBytes = Convert.FromBase64String(b64Key); 234 | byte[] encryptedBytes = Convert.FromBase64String(b64CipherText); 235 | byte[] iv = new byte[16]; 236 | Array.Copy(encryptedBytes, 0, iv, 0, iv.Length); 237 | byte[] cipherBytes = new byte[encryptedBytes.Length - iv.Length]; 238 | Array.Copy(encryptedBytes, iv.Length, cipherBytes, 0, cipherBytes.Length); 239 | 240 | using (Aes aes = Aes.Create()) 241 | { 242 | aes.Key = keyBytes; 243 | aes.IV = iv; 244 | aes.Padding = PaddingMode.PKCS7; // Set the padding mode to PKCS7 245 | 246 | ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); 247 | 248 | using (MemoryStream ms = new MemoryStream(cipherBytes)) 249 | using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) 250 | using (StreamReader reader = new StreamReader(cs)) 251 | { 252 | return reader.ReadToEnd(); 253 | } 254 | } 255 | }catch 256 | { 257 | Log.Warning("Answer could not be decrypted . Authetification failed"); 258 | return string.Empty; 259 | } 260 | 261 | } 262 | 263 | public static string ComputeSha256Hash(string rawData) 264 | { 265 | using (SHA256 sha256Hash = SHA256.Create()) 266 | { 267 | byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData)); 268 | 269 | StringBuilder builder = new StringBuilder(); 270 | for (int i = 0; i < bytes.Length; i++) 271 | { 272 | builder.Append(bytes[i].ToString("x2")); 273 | } 274 | return builder.ToString(); 275 | } 276 | } 277 | 278 | public void connect(WebSocketServer server) 279 | { 280 | server.Start(socket => 281 | { 282 | socket.OnOpen = () => 283 | { 284 | var cookies = socket.ConnectionInfo.Cookies; 285 | Log.Information($"Socket {socket.ConnectionInfo.Id}: Connection Attempt, Authentificate..."); 286 | 287 | if (!cookies.ContainsKey("authenticated")) 288 | { 289 | cookies["authenticated"] = "False"; 290 | } 291 | 292 | if(acceptNewClients || !alreadyHadClient) 293 | { 294 | Log.Information(" New client registred"); 295 | initfunKey(true); 296 | socket.Send("KEY:"+ AuthKey); 297 | alreadyHadClient = true; 298 | cookies["authenticated"] = "True"; 299 | } 300 | else if (cookies["authenticated"] == "False") 301 | { 302 | Log.Information($"Socket {socket.ConnectionInfo.Id} : Authetification question sent."); 303 | var token = GenerateRandomToken(); 304 | cookies["token"] = ComputeSha256Hash(token); 305 | socket.Send(token); 306 | } 307 | }; 308 | 309 | 310 | socket.OnClose = () => 311 | { 312 | Log.Information($"Socket {socket.ConnectionInfo.Id} :Connection closed."); 313 | 314 | // Reconnect when the connection is closed 315 | if (!cancellationTokenSource.IsCancellationRequested) 316 | { 317 | Task.Delay(TimeSpan.FromSeconds(5)).ContinueWith(_ => connect(server)); 318 | } 319 | }; 320 | socket.OnMessage = message => 321 | { 322 | var cookies = socket.ConnectionInfo.Cookies; 323 | 324 | if (cookies["authenticated"] == "False") 325 | { 326 | if (DecryptStringAES(message, AuthKey) == cookies["token"]) 327 | { 328 | cookies["authenticated"] = "True"; 329 | Log.Information($"Socket {socket.ConnectionInfo.Id} : Correct Authentification answer. authenticated."); 330 | } 331 | else 332 | { 333 | Log.Error($"Socket {socket.ConnectionInfo.Id} :Wrong Authentification answer, Failed."); 334 | } 335 | 336 | } 337 | 338 | if (cookies["authenticated"] == "True") 339 | { 340 | if (message == "_STOP_") 341 | { 342 | resetCommunicationBuffers(); 343 | return; 344 | } 345 | else if (message == "_START_NEW") 346 | { 347 | resetCommunicationBuffers(); 348 | socket.Send(firstPrompt); 349 | Log.Information("First Prompt sent"); 350 | return; 351 | } 352 | else 353 | { 354 | Log.Information("New message received"); 355 | ExtractWindowsCommands(message); 356 | 357 | 358 | if (receptionBuffer != string.Empty) 359 | { 360 | Log.Information($"Last Command Uncomplete. Continue"); 361 | socket.Send($"{config.Header} The previous command was incomplete. Please do not repeat any words that I have already received. Instead, continue from where you left off. The last line I received was '{GetLastLine(receptionBuffer)}'. {config.Tail}"); 362 | return; 363 | } 364 | 365 | if (pendingCommands.Count != 0) 366 | { 367 | bool multiCommands = false; 368 | if (pendingCommands.Count > 1) 369 | { 370 | multiCommands = true; 371 | } 372 | 373 | StringBuilder concatenatedOutput = new StringBuilder(); 374 | int commandIndex = 0; 375 | foreach (string command in pendingCommands) 376 | { 377 | if (command == "NEXT") 378 | { 379 | Console.Write("Will sent NEXT Part:"); 380 | } 381 | else 382 | { 383 | //reset output parts list 384 | outputParts.Clear(); 385 | currentPartIndex = 0; 386 | 387 | commandIndex++; 388 | string commandOutput = ExecuteCommand(command); 389 | if (multiCommands && commandOutput.Trim() != string.Empty) 390 | { 391 | concatenatedOutput.Append($"## Command {commandIndex} Feedback \r\n"); 392 | concatenatedOutput.Append(commandOutput); 393 | concatenatedOutput.Append($"## End command {commandIndex} Feedback \r\n"); 394 | } 395 | else 396 | { 397 | concatenatedOutput.Append(commandOutput); 398 | } 399 | } 400 | } 401 | pendingCommands.Clear(); 402 | 403 | 404 | processOutput(concatenatedOutput.ToString()); 405 | SendNextPart(socket); 406 | } 407 | 408 | } 409 | }; 410 | }; 411 | 412 | }); 413 | 414 | } 415 | public void StartServiceTask() 416 | { 417 | bool isNewInstance; 418 | initfunKey(false); 419 | using (Mutex mutex = new Mutex(true, "ChatGPTExecutor", out isNewInstance)) 420 | { 421 | if (isNewInstance) 422 | { 423 | 424 | Log.Logger = new LoggerConfiguration() 425 | .WriteTo.Console() 426 | .WriteTo.File( 427 | new CompactJsonFormatter(), 428 | "logs/log-.txt", 429 | rollingInterval: RollingInterval.Day, 430 | fileSizeLimitBytes: 1_000_000_000, 431 | rollOnFileSizeLimit: true, 432 | retainedFileCountLimit: null, 433 | shared: true, 434 | flushToDiskInterval: TimeSpan.FromSeconds(1)) 435 | .CreateLogger(); 436 | 437 | Log.Information("Logging initialized............."); 438 | 439 | 440 | 441 | 442 | // Initialize cmd process 443 | InitializeCmdProcess(); 444 | 445 | if(File.Exists(config.FirstPromptFilePath)) 446 | { 447 | firstPrompt = File.ReadAllText(config.FirstPromptFilePath); 448 | } 449 | 450 | server = new WebSocketServer($"ws://127.0.0.1:{config.WsPort}"); 451 | cancellationTokenSource = new CancellationTokenSource(); 452 | connect(server); 453 | 454 | 455 | Log.Information("WebSocket server started. Press CTRL+C to exit..."); 456 | Console.CancelKeyPress += (sender, e) => 457 | { 458 | Log.Information("Shutting down..."); 459 | e.Cancel = true; 460 | cancellationTokenSource.Cancel(); 461 | }; 462 | 463 | WaitHandle.WaitAny(new[] { cancellationTokenSource.Token.WaitHandle }); 464 | 465 | server.Dispose(); 466 | 467 | Log.CloseAndFlush(); 468 | } 469 | else 470 | { 471 | Log.Information("An instance of this program is already running."); 472 | } 473 | } 474 | } 475 | 476 | public static string GetLastLine(string text) 477 | { 478 | if (string.IsNullOrEmpty(text)) 479 | { 480 | return string.Empty; 481 | } 482 | 483 | int lastNewLineIndex = text.LastIndexOfAny(new char[] { '\r', '\n' }); 484 | if (lastNewLineIndex == -1) 485 | { 486 | return text; 487 | } 488 | 489 | if (lastNewLineIndex > 0 && text[lastNewLineIndex - 1] == '\r' && text[lastNewLineIndex] == '\n') 490 | { 491 | lastNewLineIndex--; 492 | } 493 | 494 | return text.Substring(lastNewLineIndex + 1); 495 | } 496 | 497 | static void processOutput(string output) 498 | { 499 | //Console.WriteLine($"Command Execution output: {output}"); 500 | output = config.Header + output + config.Tail; 501 | // Divide the output into parts and send the first part 502 | DivideOutput(output); 503 | } 504 | 505 | 506 | 507 | static void DivideOutputOnSize(string output) 508 | { 509 | 510 | int outputLength = output.Length; 511 | int position = 0; 512 | //bool firstPart = true; 513 | 514 | while (position < outputLength) 515 | { 516 | int length = Math.Min(config.MaxMessageSize, outputLength - position); 517 | string part; 518 | 519 | //if (firstPart) 520 | //{ 521 | // part = "BEGIN<" + output.Substring(position, length); 522 | // firstPart = false; 523 | //} 524 | //else 525 | if (position + length >= outputLength) 526 | { 527 | part = output.Substring(position, length); 528 | } 529 | else 530 | { 531 | part = output.Substring(position, length) + config.AskForNext; 532 | } 533 | 534 | outputParts.Add(part); 535 | position += length; 536 | } 537 | } 538 | 539 | //static void DivideOutput(string output) 540 | //{ 541 | // outputParts.Clear(); 542 | // currentPartIndex = 0; 543 | 544 | // int outputLength = output.Length; 545 | // int position = 0; 546 | 547 | 548 | // while (position < outputLength) 549 | // { 550 | // int wordsProcessed = 0; 551 | // StringBuilder partBuilder = new StringBuilder(); 552 | 553 | // while (wordsProcessed < maxWords && position < outputLength) 554 | // { 555 | // partBuilder.Append(output[position]); 556 | // if (char.IsWhiteSpace(output[position])) 557 | // { 558 | // wordsProcessed++; 559 | // } 560 | // position++; 561 | // } 562 | 563 | // if (position < outputLength) 564 | // { 565 | // partBuilder.Append(AskForNext); 566 | // } 567 | 568 | // outputParts.Add(partBuilder.ToString()); 569 | // } 570 | //} 571 | 572 | 573 | static void DivideOutput(string output) 574 | { 575 | int outputLength = output.Length; 576 | int position = 0; 577 | 578 | while (position < outputLength) 579 | { 580 | int wordsProcessed = 0; 581 | StringBuilder partBuilder = new StringBuilder(); 582 | 583 | while (wordsProcessed < config.MaxWords && position < outputLength) 584 | { 585 | partBuilder.Append(output[position]); 586 | if (char.IsWhiteSpace(output[position])) 587 | { 588 | wordsProcessed++; 589 | } 590 | position++; 591 | } 592 | 593 | // If the current position is not the end of the output, 594 | // search for the last newline character in the block 595 | if (position < outputLength) 596 | { 597 | int lastNewline = partBuilder.ToString().LastIndexOf('\n'); 598 | if (lastNewline != -1) 599 | { 600 | position -= (partBuilder.Length - lastNewline - 1); 601 | partBuilder.Length = lastNewline + 1; 602 | partBuilder.Append(config.AskForNext); 603 | } 604 | else 605 | { 606 | partBuilder.Append("\r\n"+ config.AskForNext); 607 | } 608 | } 609 | outputParts.Add(partBuilder.ToString()); 610 | } 611 | } 612 | 613 | 614 | static int GetIndexOfNthWord(int n,int startIndex, string text) 615 | { 616 | if (n < 1 || string.IsNullOrEmpty(text)) 617 | { 618 | return -1; 619 | } 620 | 621 | int wordCount = 0; 622 | bool isPreviousCharSpace = true; 623 | 624 | for (int i = startIndex; i < text.Length; i++) 625 | { 626 | if (char.IsWhiteSpace(text[i])) 627 | { 628 | isPreviousCharSpace = true; 629 | } 630 | else if (isPreviousCharSpace) 631 | { 632 | wordCount++; 633 | if (wordCount == n) 634 | { 635 | return i; 636 | } 637 | isPreviousCharSpace = false; 638 | } 639 | } 640 | 641 | return -1; 642 | } 643 | 644 | static void SendNextPart(IWebSocketConnection socket) 645 | { 646 | if (currentPartIndex < outputParts.Count) 647 | { 648 | Log.Information(outputParts[currentPartIndex]); 649 | int size = outputParts[currentPartIndex].Length; 650 | socket.Send( outputParts[currentPartIndex]); 651 | currentPartIndex++; 652 | } 653 | } 654 | 655 | static List ExtractWindowsCommands(string message) 656 | { 657 | string headerKeyword = "MMI<<"; 658 | string tailorKeyword = ">>MMI"; 659 | 660 | int startIndex = -1; 661 | int endIndex = -1; 662 | 663 | receptionBuffer += message; 664 | 665 | do 666 | { 667 | startIndex = receptionBuffer.IndexOf(headerKeyword, startIndex + 1); 668 | if (startIndex != -1) 669 | { 670 | endIndex = receptionBuffer.IndexOf(tailorKeyword, startIndex + headerKeyword.Length); 671 | if (endIndex != -1) 672 | { 673 | string command = receptionBuffer.Substring(startIndex + headerKeyword.Length, endIndex - startIndex - headerKeyword.Length).Trim(); 674 | pendingCommands.Add(command); 675 | } 676 | else 677 | { 678 | receptionBuffer = receptionBuffer.Substring(startIndex); 679 | break; 680 | } 681 | } 682 | else 683 | { 684 | receptionBuffer = ""; 685 | } 686 | } while (startIndex != -1 && endIndex != -1); 687 | 688 | return pendingCommands; 689 | } 690 | 691 | 692 | static void InitializeCmdProcess() 693 | { 694 | cmdProcess = new Process(); 695 | cmdProcess.StartInfo.FileName = "cmd.exe"; 696 | cmdProcess.StartInfo.UseShellExecute = false; 697 | cmdProcess.StartInfo.RedirectStandardOutput = true; 698 | cmdProcess.StartInfo.RedirectStandardInput = true; 699 | cmdProcess.StartInfo.RedirectStandardError = true; 700 | cmdProcess.StartInfo.CreateNoWindow = true; 701 | 702 | cmdProcess.OutputDataReceived += CmdProcess_OutputDataReceived; 703 | cmdProcess.ErrorDataReceived += CmdProcess_ErrorDataReceived; 704 | 705 | cmdProcess.Start(); 706 | 707 | cmdStreamWriter = cmdProcess.StandardInput; 708 | 709 | cmdProcess.BeginOutputReadLine(); 710 | cmdProcess.BeginErrorReadLine(); 711 | 712 | outputTimeoutTimer = new System.Timers.Timer(3000); // 3 second timeout 713 | outputTimeoutTimer.Elapsed += OutputTimeoutTimer_Elapsed; 714 | outputTimeoutTimer.AutoReset = false; 715 | } 716 | 717 | 718 | 719 | 720 | 721 | public static string ExecuteCommand(string command) 722 | { 723 | normalOutput.Clear(); 724 | errorOutput.Clear(); 725 | 726 | outputEndEvent.Reset(); 727 | questionAskedEvent.Reset(); 728 | 729 | cmdStreamWriter.WriteLine(command); 730 | 731 | cmdStreamWriter.Flush(); 732 | 733 | outputTimeoutTimer.Start(); 734 | 735 | WaitHandle.WaitAny(new[] { outputEndEvent, questionAskedEvent }); 736 | 737 | outputTimeoutTimer.Stop(); 738 | 739 | string output = normalOutput.ToString(); 740 | string error = errorOutput.ToString(); 741 | 742 | if (!string.IsNullOrEmpty(error)) 743 | { 744 | return $"Error: {error}"; 745 | } 746 | 747 | if (questionAskedEvent.WaitOne(0)) 748 | { 749 | return $"Question: {output}"; 750 | } 751 | 752 | return output; 753 | } 754 | 755 | 756 | public static void ProvideAnswer(string answer) 757 | { 758 | cmdStreamWriter.WriteLine(answer); 759 | cmdStreamWriter.Flush(); 760 | answerProvidedEvent.Set(); 761 | } 762 | 763 | static void CmdProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) 764 | { 765 | if (e.Data != null) 766 | { 767 | outputTimeoutTimer.Stop(); 768 | outputTimeoutTimer.Start(); 769 | 770 | if (e.Data == "end") 771 | { 772 | outputEndEvent.Set(); 773 | } 774 | else if (e.Data.EndsWith("?")) 775 | { 776 | normalOutput.AppendLine(e.Data); 777 | questionAskedEvent.Set(); 778 | } 779 | else if (e.Data.EndsWith("echo end")) 780 | { 781 | outputEndEvent.Set(); 782 | // Ignore the "echo end" line 783 | } 784 | else 785 | {//ignore the first line when creating a cmd window: 786 | // (c)Microsoft Corporation.Tous droits réservés. 787 | if (firstOutputLine) 788 | { 789 | firstOutputLine = false; 790 | } 791 | else 792 | { 793 | normalOutput.AppendLine(e.Data); 794 | 795 | } 796 | } 797 | } 798 | } 799 | 800 | private static void CmdProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e) 801 | { 802 | if (e.Data == null) return; 803 | 804 | errorOutput.AppendLine(e.Data); 805 | } 806 | 807 | private static void OutputTimeoutTimer_Elapsed(object sender, ElapsedEventArgs e) 808 | { 809 | outputEndEvent.Set(); 810 | } 811 | 812 | } 813 | } 814 | 815 | --------------------------------------------------------------------------------